fpc/compiler/ppu.pas
pierre f2b200e4f0 Fix recordtoken writing into ppu files to allow correct
handling in cross-configuration with different endianess.

  The code has been modified to use the same scheme as the writing of
  the other parts of the ppu, i.e. change_endian filed has been
  added also to tscannerfile class of scanner unit.
  This field is then used to swap values that required endianess
  conversion.

  * scanner.pas: change_endian filed added to tscannerfile class.
    The value of this field is set as the same field in tentryfile class of entfile unit.
    Token read and write methods converted to use change_endian field.

  * ppu.pas: Increase CurrentPPILongVersion

  * utils/ppuutils/ppudump.pp: Remove unneeded FPC_BIG_ENDIAN code
    which was needed because tokens were previously written using a
    different rule.

git-svn-id: trunk@42527 -
2019-07-29 05:33:00 +00:00

410 lines
12 KiB
ObjectPascal

{
Copyright (c) 1998-2002 by Florian Klaempfl
Routines to read/write ppu files
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 ppu;
{$i fpcdefs.inc}
interface
uses
constexp,entfile;
{ Also write the ppu if only crc if done, this can be used with ppudump to
see the differences between the intf and implementation }
{ define INTFPPU}
{$ifdef Test_Double_checksum}
var
CRCFile : text;
const
CRC_array_Size = 200000;
type
tcrc_array = array[0..crc_array_size] of dword;
pcrc_array = ^tcrc_array;
{$endif Test_Double_checksum}
const
{ only update this version if something change in the tppuheader:
* the unit flags listed below
* the format of the header itslf
This number cannot become bigger than 255 (it's stored in a byte) }
CurrentPPUVersion = 207;
{ for any other changes to the ppu format, increase this version number
(it's a cardinal) }
CurrentPPULongVersion = 5;
{ unit flags }
uf_big_endian = $000004;
uf_in_library = $000020; { is the file in another file than <ppufile>.* ? }
uf_smart_linked = $000040; { the ppu can be smartlinked }
uf_static_linked = $000080; { the ppu can be linked static }
uf_shared_linked = $000100; { the ppu can be linked shared }
uf_lto_linked = $000200; { the ppu can be used with LTO }
uf_no_link = $000400; { unit has no .o generated, but can still have external linking! }
uf_little_endian = $001000;
uf_fpu_emulation = $008000; { this unit was compiled with fpu emulation on }
type
{ bestreal is defined based on the target architecture }
ppureal=bestreal;
tppuerror=(ppuentrytoobig,ppuentryerror);
tppuheader=record
common : tentryheader;
checksum : cardinal; { checksum for this ppufile }
interface_checksum : cardinal;
deflistsize,
symlistsize : longint;
indirect_checksum: cardinal;
end;
tppuentry=tentry;
tunitasmlisttype=(ualt_public,ualt_extern);
{ tppufile }
tppufile=class(tentryfile)
{$ifdef Test_Double_checksum}
public
crcindex,
crc_index,
crcindex2,
crc_index2 : cardinal;
crc_test,
crc_test2 : pcrc_array;
private
{$endif def Test_Double_checksum}
protected
procedure newheader;override;
function readheader: longint;override;
function outputallowed: boolean;override;
//procedure doputdata(const b;len:integer);override;
function getheadersize:longint;override;
function getheaderaddr:pentryheader;override;
procedure resetfile;override;
{$ifdef DEBUG_PPU}
procedure ppu_log(st :string);override;
{$endif}
public
header : tppuheader;
{ crc for the entire unit }
crc,
{ crc for the interface definitions in this unit }
interface_crc,
{ crc of all object/class definitions in the interface of this unit, xor'ed
by the crc's of all object/class definitions in the interfaces of units
used by this unit. Reason: see mantis #13840 }
indirect_crc : cardinal;
do_crc,
do_interface_crc,
do_indirect_crc : boolean;
crc_only : boolean; { used to calculate interface_crc before implementation }
constructor Create(const fn:string);
destructor destroy;override;
function CheckPPUId:boolean;
{read}
{ nothing special currently }
{write}
function createfile:boolean;override;
procedure writeheader;override;
procedure putdata(const b;len:integer);override;
end;
implementation
uses
{$ifdef Test_Double_checksum}
comphook,
{$endif def Test_Double_checksum}
fpccrc;
function swapendian_ppureal(d:ppureal):ppureal;
type ppureal_bytes=array[0..sizeof(d)-1] of byte;
var i:0..sizeof(d)-1;
begin
for i:=low(ppureal_bytes) to high(ppureal_bytes) do
ppureal_bytes(swapendian_ppureal)[i]:=ppureal_bytes(d)[high(ppureal_bytes)-i];
end;
{*****************************************************************************
TPPUFile
*****************************************************************************}
constructor tppufile.Create(const fn:string);
begin
inherited Create(fn);
crc_only:=false;
{$ifdef Test_Double_checksum}
if not assigned(crc_test) then
new(crc_test);
if not assigned(crc_test2) then
new(crc_test2);
{$endif Test_Double_checksum}
end;
destructor tppufile.destroy;
begin
{$ifdef Test_Double_checksum}
if assigned(crc_test) then
dispose(crc_test);
crc_test:=nil;
if assigned(crc_test2) then
dispose(crc_test2);
crc_test2:=nil;
{$endif Test_Double_checksum}
inherited destroy;
end;
function tppufile.CheckPPUId:boolean;
begin
CheckPPUId:=((Header.common.Id[1]='P') and
(Header.common.Id[2]='P') and
(Header.common.Id[3]='U'));
end;
procedure tppufile.newheader;
var
s : string;
begin
fillchar(header,sizeof(tppuheader),0);
str(currentppuversion,s);
while length(s)<3 do
s:='0'+s;
with header.common do
begin
Id[1]:='P';
Id[2]:='P';
Id[3]:='U';
Ver[1]:=s[1];
Ver[2]:=s[2];
Ver[3]:=s[3];
end;
end;
function tppufile.readheader: longint;
begin
if fsize<sizeof(tppuheader) then
exit(0);
result:=f.Read(header,sizeof(tppuheader));
{ The header is always stored in little endian order }
{ therefore swap if on a big endian machine }
{$IFDEF ENDIAN_BIG}
header.common.compiler := swapendian(header.common.compiler);
header.common.cpu := swapendian(header.common.cpu);
header.common.target := swapendian(header.common.target);
header.common.flags := swapendian(header.common.flags);
header.common.size := swapendian(header.common.size);
header.checksum := swapendian(header.checksum);
header.interface_checksum := swapendian(header.interface_checksum);
header.indirect_checksum := swapendian(header.indirect_checksum);
header.deflistsize:=swapendian(header.deflistsize);
header.symlistsize:=swapendian(header.symlistsize);
{ the PPU DATA is stored in native order }
change_endian := (header.common.flags and uf_little_endian) = uf_little_endian;
{$ELSE not ENDIAN_BIG}
change_endian := (header.common.flags and uf_big_endian) = uf_big_endian;
{$ENDIF}
end;
function tppufile.outputallowed: boolean;
begin
result:=not crc_only;
end;
{$ifdef DEBUG_PPU}
procedure tppufile.ppu_log(st :string);
begin
inherited ppu_log(st);
if flog_open then
begin
if do_crc and (ppu_log_idx < bufstart+bufidx) then
begin
writeln(flog,'New crc : ',hexstr(dword(crc),8));
writeln(flog,'New interface crc : ',hexstr(dword(interface_crc),8));
writeln(flog,'New indirect crc : ',hexstr(dword(indirect_crc),8));
ppu_log_idx:=bufstart+bufidx;
end;
end;
{$ifdef IN_PPUDUMP}
if update_crc then
begin
writeln('New crc : ',hexstr(dword(crc),8));
writeln('New interface crc : ',hexstr(dword(interface_crc),8));
writeln('New indirect crc : ',hexstr(dword(indirect_crc),8));
end;
{$endif}
end;
{$endif}
{*****************************************************************************
TPPUFile Reading
*****************************************************************************}
{ nothing special currently }
{*****************************************************************************
TPPUFile Writing
*****************************************************************************}
function tppufile.createfile:boolean;
begin
{$ifdef INTFPPU}
if crc_only then
begin
fname:=fname+'.intf';
crc_only:=false;
end;
{$endif}
result:=inherited createfile;
end;
procedure tppufile.writeheader;
var
opos : integer;
begin
if crc_only then
exit;
{ flush buffer }
writebuf;
{ update size (w/o header!) in the header }
header.common.size:=bufstart-sizeof(tppuheader);
{ set the endian flag }
{$ifndef FPC_BIG_ENDIAN}
header.common.flags := header.common.flags or uf_little_endian;
{$else not FPC_BIG_ENDIAN}
header.common.flags := header.common.flags or uf_big_endian;
{ Now swap the header.common in the correct endian (always little endian) }
header.common.compiler := swapendian(header.common.compiler);
header.common.cpu := swapendian(header.common.cpu);
header.common.target := swapendian(header.common.target);
header.common.flags := swapendian(header.common.flags);
header.common.size := swapendian(header.common.size);
header.checksum := swapendian(header.checksum);
header.interface_checksum := swapendian(header.interface_checksum);
header.indirect_checksum := swapendian(header.indirect_checksum);
header.deflistsize:=swapendian(header.deflistsize);
header.symlistsize:=swapendian(header.symlistsize);
{$endif not FPC_BIG_ENDIAN}
{ write header and restore filepos after it }
opos:=f.Position;
f.Position:=0;
f.Write(header,sizeof(tppuheader));
f.Position:=opos;
end;
procedure tppufile.putdata(const b;len:integer);
begin
if do_crc then
begin
crc:=UpdateCrc32(crc,b,len);
{$ifdef Test_Double_checksum}
if crc_only then
begin
crc_test2^[crc_index2]:=crc;
{$ifdef Test_Double_checksum_write}
Writeln(CRCFile,crc);
{$endif Test_Double_checksum_write}
if crc_index2<crc_array_size then
inc(crc_index2);
end
else
begin
if (crcindex2<crc_array_size) and (crcindex2<crc_index2) and
(crc_test2^[crcindex2]<>crc) then
Do_comment(V_Note,'impl CRC changed');
{$ifdef Test_Double_checksum_write}
Writeln(CRCFile,crc);
{$endif Test_Double_checksum_write}
inc(crcindex2);
end;
{$endif def Test_Double_checksum}
if do_interface_crc then
begin
interface_crc:=UpdateCrc32(interface_crc,b,len);
{$ifdef Test_Double_checksum}
if crc_only then
begin
crc_test^[crc_index]:=interface_crc;
{$ifdef Test_Double_checksum_write}
Writeln(CRCFile,interface_crc);
{$endif Test_Double_checksum_write}
if crc_index<crc_array_size then
inc(crc_index);
end
else
begin
if (crcindex<crc_array_size) and (crcindex<crc_index) and
(crc_test^[crcindex]<>interface_crc) then
Do_comment(V_Warning,'CRC changed');
{$ifdef Test_Double_checksum_write}
Writeln(CRCFile,interface_crc);
{$endif Test_Double_checksum_write}
inc(crcindex);
end;
{$endif def Test_Double_checksum}
{ indirect crc must only be calculated for the interface; changes
to a class in the implementation cannot require another unit to
be recompiled }
if do_indirect_crc then
indirect_crc:=UpdateCrc32(indirect_crc,b,len);
end;
end;
inherited putdata(b,len);
(*if not crc_only then
writedata(b,len);
inc(entryidx,len);*)
end;
function tppufile.getheadersize: longint;
begin
result:=sizeof(header);
end;
function tppufile.getheaderaddr: pentryheader;
begin
result:=@header;
end;
procedure tppufile.resetfile;
begin
crc:=0;
interface_crc:=0;
indirect_crc:=0;
do_interface_crc:=true;
do_indirect_crc:=false;
do_crc:=true;
end;
end.