mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 05:58:02 +02:00
396 lines
10 KiB
ObjectPascal
396 lines
10 KiB
ObjectPascal
{
|
|
Copyright (c) 2024 by Kirill Kranz
|
|
|
|
This unit implements support link routines
|
|
for the (MIPSEL) PS1 target
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
****************************************************************************
|
|
}
|
|
unit t_ps1;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
uses link;
|
|
|
|
type
|
|
TLinkerPS1=class(TExternalLinker)
|
|
private
|
|
|
|
procedure WriteScriptFile;
|
|
function CreatePSXEXE(name: string): boolean;
|
|
|
|
public
|
|
|
|
constructor Create; override;
|
|
procedure InitSysInitUnitName; override;
|
|
procedure SetDefaultInfo; override;
|
|
function MakeExecutable: boolean; override;
|
|
|
|
end;
|
|
|
|
|
|
implementation
|
|
uses
|
|
sysutils, classes,
|
|
cutils, cfileutl, cclasses,
|
|
globtype, globals, systems, verbose, comphook, cscript, fmodule,
|
|
i_ps1;
|
|
|
|
|
|
constructor TLinkerPS1.Create;
|
|
begin
|
|
Inherited Create;
|
|
end;
|
|
|
|
Procedure TLinkerPS1.InitSysInitUnitName;
|
|
begin
|
|
sysinitunit:='si_prc';
|
|
end;
|
|
|
|
procedure TLinkerPS1.SetDefaultInfo;
|
|
begin
|
|
info.ExeCmd[1]:= 'ld $OPT $MAP $RES';
|
|
end;
|
|
|
|
|
|
procedure TLinkerPS1.WriteScriptFile;
|
|
Var
|
|
i : integer;
|
|
linkres : TLinkRes;
|
|
s : TCmdStr;
|
|
megastr : ansistring;
|
|
newname : ansistring;
|
|
HPath : TCmdStrListItem;
|
|
|
|
begin
|
|
|
|
{ Open link.res file }
|
|
LinkRes:= TLinkRes.Create(outputexedir + Info.ResName, true);
|
|
|
|
|
|
LinkRes.Add('MEMORY');
|
|
LinkRes.Add('{');
|
|
LinkRes.Add('ram (rwx) : ORIGIN = 0x80010000, LENGTH = 2M - 64K');
|
|
LinkRes.Add('}');
|
|
LinkRes.Add('ENTRY(_start)');
|
|
|
|
|
|
HPath:= TCmdStrListItem(LibrarySearchPath.First);
|
|
while assigned(HPath) do begin
|
|
s:= HPath.Str;
|
|
|
|
if s <> '' then LinkRes.Add('SEARCH_DIR("' + s + '")');
|
|
|
|
HPath:= TCmdStrListItem(HPath.Next);
|
|
end;
|
|
|
|
|
|
while not ObjectFiles.Empty do begin
|
|
LinkRes.Add('INPUT(' + ExtractFileName(ObjectFiles.GetFirst) + ')');
|
|
end;
|
|
|
|
|
|
LinkRes.Add('INPUT(libcard.a libpress.a libgpu.a libgs.a libgte.a)');
|
|
LinkRes.Add('INPUT(libcd.a libetc.a libsn.a libsnd.a libspu.a)');
|
|
LinkRes.Add('INPUT(libmath.a libcomb.a libtap.a libsio.a)');
|
|
LinkRes.Add('INPUT(libpad.a libc2.a libapi.a)');
|
|
|
|
LinkRes.Add('SECTIONS');
|
|
LinkRes.Add('{');
|
|
LinkRes.Add(' .text : ALIGN(8) {');
|
|
LinkRes.Add(' __exe_start__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __text_start__ = .;');
|
|
LinkRes.Add(' KEEP(*(.text.n_start));');
|
|
LinkRes.Add(' *(.text .text.*)');
|
|
LinkRes.Add(' __text_end__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __text_size__ = __text_start__ - __text_end__;');
|
|
LinkRes.Add(' } > ram');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' .rodata : ALIGN(8) {');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __rdata_start__ = .;');
|
|
LinkRes.Add(' *(.rdata .rdata.* .rodata .rodata.*)');
|
|
LinkRes.Add(' __rdata_end__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __rdata_size__ = __rdata_end__ - __rdata_start__;');
|
|
LinkRes.Add(' } > ram');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' .ctors : ALIGN(8) {');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __ctors_start__ = .;');
|
|
LinkRes.Add(' KEEP(*(.ctors))');
|
|
LinkRes.Add(' KEEP(*(SORT(.ctors.*)))');
|
|
LinkRes.Add(' __ctors_end__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __ctors_size__ = __ctors_end__ - __ctors_start__;');
|
|
LinkRes.Add(' } > ram');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' .dtors : ALIGN(8) {');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __dtors_start__ = .;');
|
|
LinkRes.Add(' KEEP(*(.dtors))');
|
|
LinkRes.Add(' KEEP(*(SORT(.dtors.*)))');
|
|
LinkRes.Add(' __dtors_end__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __dtors_size__ = __dtors_end__ - __dtors_start__;');
|
|
LinkRes.Add(' } > ram');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' .data : ALIGN(8) {');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __sdata_start__ = .;');
|
|
LinkRes.Add(' *(.sdata .sdata.*)');
|
|
LinkRes.Add(' __sdata_end__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __sdata_size__ = __sdata_end__ - __sdata_start__;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __data_start__ = .;');
|
|
LinkRes.Add(' *(.data .data.*)');
|
|
LinkRes.Add(' SORT(CONSTRUCTORS)');
|
|
LinkRes.Add(' . = ALIGN(2048);');
|
|
LinkRes.Add(' __data_end__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __exe_end__ = .;');
|
|
LinkRes.Add(' __data_size__ = __data_end__ - __data_start__;');
|
|
LinkRes.Add(' } > ram');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __exe_size__ = __exe_end__ - __exe_start__;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' .sbss : ALIGN(64) {');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __sbss_start__ = .;');
|
|
LinkRes.Add(' *(.sbss .sbss.*)');
|
|
LinkRes.Add(' __sbss_end__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __sbss_size__ = __sbss_end__ - __sbss_start__;');
|
|
LinkRes.Add(' } > ram');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' .bss : ALIGN(64) {');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __bss_start__ = .;');
|
|
LinkRes.Add(' *(.bss .bss.*)');
|
|
LinkRes.Add(' *(COMMON)');
|
|
LinkRes.Add(' . = ALIGN(64);');
|
|
LinkRes.Add(' __bss_end__ = .;');
|
|
LinkRes.Add('');
|
|
LinkRes.Add(' __bss_size__ = __bss_end__ - __bss_start__;');
|
|
LinkRes.Add(' } > ram');
|
|
LinkRes.Add('}');
|
|
|
|
linkres.writetodisk;
|
|
linkres.free;
|
|
|
|
end;
|
|
|
|
|
|
function FileCopy(source, target: string; startTargetOffset: dword): boolean;
|
|
var
|
|
fin, fout : file;
|
|
NumRead : dWord;
|
|
Buf : Array[1..2048] of byte;
|
|
|
|
begin
|
|
|
|
result:= false;
|
|
|
|
AssignFile(fin, source);
|
|
{$I-} reset(fin, 1); {$I+}
|
|
if ioResult <> 0 then exit;
|
|
|
|
AssignFile(fout, target);
|
|
if startTargetOffset = 0 then rewrite(fout, 1) else begin
|
|
{$I-} reset(fout, 1); {$I+}
|
|
if ioResult <> 0 then exit;
|
|
seek(fout, startTargetOffset);
|
|
end;
|
|
|
|
Repeat
|
|
BlockRead(fin,buf,Sizeof(buf),NumRead);
|
|
if NumRead = 0 then break;
|
|
BlockWrite(fout,Buf,NumRead);
|
|
Until false;
|
|
|
|
close(fin);
|
|
close(fout);
|
|
|
|
result:= true;
|
|
|
|
end;
|
|
|
|
|
|
procedure write0till(var f: file; k: dWord);
|
|
var
|
|
b : byte;
|
|
|
|
begin
|
|
|
|
b:= 0;
|
|
repeat
|
|
BlockWrite(f, b, 1);
|
|
until filesize(f) >= k;
|
|
|
|
end;
|
|
|
|
|
|
function TLinkerPS1.CreatePSXEXE(name: string): boolean;
|
|
const
|
|
header = 'PS-X EXE';
|
|
|
|
var
|
|
f : file;
|
|
fsize : dword;
|
|
d : dword;
|
|
|
|
begin
|
|
|
|
result:= false;
|
|
|
|
AssignFile(f, name + '.bin');
|
|
{$I-} reset(f, 1); {$I+}
|
|
if ioResult <> 0 then exit;
|
|
|
|
if filesize(f) >= $200000 then begin
|
|
Message3(link_f_executable_too_big_exceeds_X_by_Y_bytes,'PS1','2097152',tostr(filesize(f)-$200000));
|
|
exit;
|
|
end;
|
|
|
|
close(f);
|
|
|
|
AssignFile(f, name + '.psx-exe');
|
|
{$I-} rewrite(f, 1); {$I+}
|
|
if ioResult <> 0 then exit;
|
|
|
|
BlockWrite(f, header, sizeof(header));
|
|
write0till(f, $10);
|
|
|
|
d:= NtoLE($80010000);
|
|
BlockWrite(f, d, 4);
|
|
d:= NtoLE($FFFFFFFF);
|
|
BlockWrite(f, d, 4);
|
|
d:= NtoLE($80010000);
|
|
BlockWrite(f, d, 4);
|
|
|
|
write0till(f, $30);
|
|
write0till(f, $800);
|
|
|
|
fsize:= filesize(f);
|
|
close(f);
|
|
|
|
if not FileCopy(name + '.bin', name + '.psx-exe', fsize) then exit;
|
|
|
|
{$I-} reset(f, 1); {$I+}
|
|
if ioResult <> 0 then exit;
|
|
fsize:= filesize(f);
|
|
seek(f, fsize);
|
|
|
|
if (fsize mod $800) <> 0 then begin
|
|
fsize:= fsize + ($800 - (fsize mod $800));
|
|
write0till(f, fsize);
|
|
end;
|
|
|
|
seek(f, $1C);
|
|
|
|
d:= fsize - $800;
|
|
BlockWrite(f, d, 4);
|
|
|
|
close(f);
|
|
|
|
result:= true;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TLinkerPS1.MakeExecutable: boolean;
|
|
var
|
|
binstr, cmdstr, mapstr : TCmdStr;
|
|
success : boolean;
|
|
begin
|
|
if not (cs_link_nolink in current_settings.globalswitches) then
|
|
Message1(exec_i_linking,current_module.exefilename);
|
|
|
|
if (cs_link_map in current_settings.globalswitches) then
|
|
mapstr:='-Map '+maybequoted(ChangeFileExt(current_module.exefilename,'.map'))
|
|
else
|
|
mapstr:='';
|
|
|
|
WriteScriptFile;
|
|
|
|
if cs_link_nolink in current_settings.globalswitches then begin
|
|
result:= true;
|
|
exit;
|
|
end;
|
|
|
|
{ Call linker }
|
|
SplitBinCmd(Info.ExeCmd[1], binstr, cmdstr);
|
|
|
|
Replace(cmdstr, '$OPT', Info.ExtraOptions);
|
|
Replace(cmdstr, '$RES', '-T ' + (maybequoted(ScriptFixFileName(outputexedir + Info.ResName))));
|
|
Replace(cmdstr, '$MAP', mapstr);
|
|
|
|
if cs_link_smart in current_settings.globalswitches then cmdstr:= cmdstr + ' --gc-sections';
|
|
|
|
success:= DoExec(FindUtil(utilsprefix + BinStr), cmdstr + ' -o ' + current_module.exefilename + '.elf', true, false);
|
|
if not success then begin
|
|
result:= false;
|
|
exit;
|
|
end;
|
|
|
|
DeleteFile(outputexedir + Info.ResName);
|
|
|
|
|
|
if not FileCopy(current_module.exefilename + '.elf', current_module.exefilename + '.bin', 0) then begin
|
|
writeln('Cant Write ' + current_module.exefilename + '.bin File!');
|
|
result:= false;
|
|
exit;
|
|
end;
|
|
|
|
DeleteFile(current_module.exefilename + '.elf');
|
|
|
|
|
|
success:= DoExec(FindUtil(utilsprefix + 'objcopy'), ' -O binary ' + current_module.exefilename +'.bin', true, false);
|
|
if not success then begin
|
|
writeln('OBJDUMP failed!');
|
|
result:= false;
|
|
exit;
|
|
end;
|
|
|
|
|
|
success:= CreatePSXEXE(current_module.exefilename);
|
|
if not success then begin
|
|
writeln('Create PSX-EXE failed!');
|
|
result:= false;
|
|
exit;
|
|
end;
|
|
|
|
DeleteFile(current_module.exefilename + '.bin');
|
|
|
|
result:= true;
|
|
end;
|
|
|
|
|
|
|
|
initialization
|
|
|
|
RegisterLinker(ld_ps1, TLinkerPS1);
|
|
RegisterTarget(system_mipsel_ps1_info);
|
|
|
|
end.
|
|
|