diff options
Diffstat (limited to 'compiler/ppu.pas')
-rw-r--r-- | compiler/ppu.pas | 1068 |
1 files changed, 1068 insertions, 0 deletions
diff --git a/compiler/ppu.pas b/compiler/ppu.pas new file mode 100644 index 0000000000..3f06f7a7b8 --- /dev/null +++ b/compiler/ppu.pas @@ -0,0 +1,1068 @@ +{ + 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 + globtype; + +{ 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 longint; + pcrc_array = ^tcrc_array; +{$endif Test_Double_checksum} + +const + CurrentPPUVersion=50; + +{ buffer sizes } + maxentrysize = 1024; + ppubufsize = 16384; + +{ppu entries} + mainentryid = 1; + subentryid = 2; + {special} + iberror = 0; + ibstartdefs = 248; + ibenddefs = 249; + ibstartsyms = 250; + ibendsyms = 251; + ibendinterface = 252; + ibendimplementation = 253; + ibendbrowser = 254; + ibend = 255; + {general} + ibmodulename = 1; + ibsourcefiles = 2; + ibloadunit = 3; + ibinitunit = 4; + iblinkunitofiles = 5; + iblinkunitstaticlibs = 6; + iblinkunitsharedlibs = 7; + iblinkotherofiles = 8; + iblinkotherstaticlibs = 9; + iblinkothersharedlibs = 10; + ibsymref = 12; + ibdefref = 13; + ibendsymtablebrowser = 14; + ibbeginsymtablebrowser = 15; +{$IFDEF MACRO_DIFF_HINT} + ibusedmacros = 16; +{$ENDIF} + ibderefdata = 17; + ibexportedmacros = 18; + ibderefmap = 19; + {syms} + ibtypesym = 20; + ibprocsym = 21; + ibglobalvarsym = 22; + ibconstsym = 23; + ibenumsym = 24; + ibtypedconstsym = 25; + ibabsolutevarsym = 26; + ibpropertysym = 27; + ibfieldvarsym = 28; + ibunitsym = 29; { needed for browser } + iblabelsym = 30; + ibsyssym = 31; + ibrttisym = 32; + iblocalvarsym = 33; + ibparavarsym = 34; + ibmacrosym = 35; + {definitions} + iborddef = 40; + ibpointerdef = 41; + ibarraydef = 42; + ibprocdef = 43; + ibshortstringdef = 44; + ibrecorddef = 45; + ibfiledef = 46; + ibformaldef = 47; + ibobjectdef = 48; + ibenumdef = 49; + ibsetdef = 50; + ibprocvardef = 51; + ibfloatdef = 52; + ibclassrefdef = 53; + iblongstringdef = 54; +{$ifdef ansistring_bits} + ibansistring16def = 58; + ibansistring32def = 55; + ibansistring64def = 59; +{$else} + ibansistringdef = 55; +{$endif} + ibwidestringdef = 56; + ibvariantdef = 57; + {implementation/objectdata} + ibnodetree = 80; + ibasmsymbols = 81; + +{ unit flags } + uf_init = $1; + uf_finalize = $2; + uf_big_endian = $4; + uf_has_browser = $10; + uf_in_library = $20; { is the file in another file than <ppufile>.* ? } + uf_smart_linked = $40; { the ppu can be smartlinked } + uf_static_linked = $80; { the ppu can be linked static } + uf_shared_linked = $100; { the ppu can be linked shared } + uf_local_browser = $200; + uf_no_link = $400; { unit has no .o generated, but can still have + external linking! } + uf_has_resources = $800; { unit has resource string section } + uf_little_endian = $1000; + uf_release = $2000; { unit was compiled with -Ur option } + uf_threadvars = $4000; { unit has threadvars } + uf_fpu_emulation = $8000; { this unit was compiled with fpu emulation on } + uf_has_debuginfo = $10000; { this unit has debuginfo generated } + uf_local_symtable = $20000; { this unit has a local symtable stored } + uf_uses_variants = $40000; { this unit uses variants } + uf_has_resourcefiles = $80000; { this unit has external resources (using $R directive)} + + +type + ppureal=extended; + + tppuerror=(ppuentrytoobig,ppuentryerror); + + tppuheader=record + id : array[1..3] of char; { = 'PPU' } + ver : array[1..3] of char; + compiler : word; + cpu : word; + target : word; + flags : longint; + size : longint; { size of the ppufile without header } + checksum : cardinal; { checksum for this ppufile } + interface_checksum : cardinal; + future : array[0..2] of longint; + end; + + tppuentry=packed record + size : longint; + id : byte; + nr : byte; + end; + + tppufile=class + private + f : file; + mode : byte; {0 - Closed, 1 - Reading, 2 - Writing} + fname : string; + fsize : integer; +{$ifdef Test_Double_checksum} + public + crcindex, + crc_index, + crcindex2, + crc_index2 : cardinal; + crc_test, + crc_test2 : pcrc_array; + private +{$endif def Test_Double_checksum} + change_endian : boolean; + buf : pchar; + bufstart, + bufsize, + bufidx : integer; + entrybufstart, + entrystart, + entryidx : integer; + entry : tppuentry; + closed, + tempclosed : boolean; + closepos : integer; + public + entrytyp : byte; + header : tppuheader; + size : integer; + crc, + interface_crc : cardinal; + error, + do_crc, + do_interface_crc : boolean; + crc_only : boolean; { used to calculate interface_crc before implementation } + constructor Create(const fn:string); + destructor Destroy;override; + procedure flush; + procedure closefile; + function CheckPPUId:boolean; + function GetPPUVersion:integer; + procedure NewHeader; + procedure NewEntry; + {read} + function openfile:boolean; + procedure reloadbuf; + procedure readdata(var b;len:integer); + procedure skipdata(len:integer); + function readentry:byte; + function EndOfEntry:boolean; + function entrysize:longint; + procedure getdatabuf(var b;len:integer;var res:integer); + procedure getdata(var b;len:integer); + function getbyte:byte; + function getword:word; + function getlongint:longint; + function getint64:int64; + function getaint:aint; + function getreal:ppureal; + function getstring:string; + procedure getnormalset(var b); + procedure getsmallset(var b); + function skipuntilentry(untilb:byte):boolean; + {write} + function createfile:boolean; + procedure writeheader; + procedure writebuf; + procedure writedata(const b;len:integer); + procedure writeentry(ibnr:byte); + procedure putdata(const b;len:integer); + procedure putbyte(b:byte); + procedure putword(w:word); + procedure putlongint(l:longint); + procedure putint64(i:int64); + procedure putaint(i:aint); + procedure putreal(d:ppureal); + procedure putstring(s:string); + procedure putnormalset(const b); + procedure putsmallset(const b); + procedure tempclose; + function tempopen:boolean; + end; + +implementation + + uses +{$ifdef Test_Double_checksum} + comphook, +{$endif def Test_Double_checksum} + crc, + cutils; + +{***************************************************************************** + Endian Handling +*****************************************************************************} + +Function SwapLong(x : longint): longint; +var + y : word; + z : word; +Begin + y := x shr 16; + y := word(longint(y) shl 8) or (y shr 8); + z := x and $FFFF; + z := word(longint(z) shl 8) or (z shr 8); + SwapLong := (longint(z) shl 16) or longint(y); +End; + + +Function SwapWord(x : word): word; +var + z : byte; +Begin + z := x shr 8; + x := x and $ff; + x := word(x shl 8); + SwapWord := x or z; +End; + + +{***************************************************************************** + TPPUFile +*****************************************************************************} + +constructor tppufile.Create(const fn:string); +begin + fname:=fn; + change_endian:=false; + crc_only:=false; + Mode:=0; + NewHeader; + Error:=false; + closed:=true; + tempclosed:=false; + getmem(buf,ppubufsize); +end; + + +destructor tppufile.destroy; +begin + closefile; + if assigned(buf) then + freemem(buf,ppubufsize); +end; + + +procedure tppufile.flush; +begin + if Mode=2 then + writebuf; +end; + + +procedure tppufile.closefile; +begin +{$ifdef Test_Double_checksum} + if mode=2 then + begin + if assigned(crc_test) then + dispose(crc_test); + if assigned(crc_test2) then + dispose(crc_test2); + end; +{$endif Test_Double_checksum} + if Mode<>0 then + begin + Flush; + {$I-} + system.close(f); + {$I+} + if ioresult<>0 then; + Mode:=0; + closed:=true; + end; +end; + + +function tppufile.CheckPPUId:boolean; +begin + CheckPPUId:=((Header.Id[1]='P') and (Header.Id[2]='P') and (Header.Id[3]='U')); +end; + + +function tppufile.GetPPUVersion:integer; +var + l : integer; + code : integer; +begin + Val(header.ver[1]+header.ver[2]+header.ver[3],l,code); + if code=0 then + GetPPUVersion:=l + else + GetPPUVersion:=0; +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 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; + + +{***************************************************************************** + TPPUFile Reading +*****************************************************************************} + +function tppufile.openfile:boolean; +var + ofmode : byte; + i : integer; +begin + openfile:=false; + assign(f,fname); + ofmode:=filemode; + filemode:=$0; + {$I-} + reset(f,1); + {$I+} + filemode:=ofmode; + if ioresult<>0 then + exit; + closed:=false; +{read ppuheader} + fsize:=filesize(f); + if fsize<sizeof(tppuheader) then + exit; + blockread(f,header,sizeof(tppuheader),i); + { The header is always stored in little endian order } + { therefore swap if on a big endian machine } +{$IFDEF ENDIAN_BIG} + header.compiler := SwapWord(header.compiler); + header.cpu := SwapWord(header.cpu); + header.target := SwapWord(header.target); + header.flags := SwapLong(header.flags); + header.size := SwapLong(header.size); + header.checksum := cardinal(SwapLong(longint(header.checksum))); + header.interface_checksum := cardinal(SwapLong(longint(header.interface_checksum))); +{$ENDIF} + { the PPU DATA is stored in native order } + if (header.flags and uf_big_endian) = uf_big_endian then + Begin +{$IFDEF ENDIAN_LITTLE} + change_endian := TRUE; +{$ELSE} + change_endian := FALSE; +{$ENDIF} + End + else if (header.flags and uf_little_endian) = uf_little_endian then + Begin +{$IFDEF ENDIAN_BIG} + change_endian := TRUE; +{$ELSE} + change_endian := FALSE; +{$ENDIF} + End; +{reset buffer} + bufstart:=i; + bufsize:=0; + bufidx:=0; + Mode:=1; + FillChar(entry,sizeof(tppuentry),0); + entryidx:=0; + entrystart:=0; + entrybufstart:=0; + Error:=false; + openfile:=true; +end; + + +procedure tppufile.reloadbuf; +begin + inc(bufstart,bufsize); + blockread(f,buf^,ppubufsize,bufsize); + bufidx:=0; +end; + + +procedure tppufile.readdata(var b;len:integer); +var + p : pchar; + left, + idx : integer; +begin + p:=pchar(@b); + idx:=0; + while len>0 do + begin + left:=bufsize-bufidx; + if len>left then + begin + move(buf[bufidx],p[idx],left); + dec(len,left); + inc(idx,left); + reloadbuf; + if bufsize=0 then + exit; + end + else + begin + move(buf[bufidx],p[idx],len); + inc(bufidx,len); + exit; + end; + end; +end; + + +procedure tppufile.skipdata(len:integer); +var + left : integer; +begin + while len>0 do + begin + left:=bufsize-bufidx; + if len>left then + begin + dec(len,left); + reloadbuf; + if bufsize=0 then + exit; + end + else + begin + inc(bufidx,len); + exit; + end; + end; +end; + + +function tppufile.readentry:byte; +begin + if entryidx<entry.size then + skipdata(entry.size-entryidx); + readdata(entry,sizeof(tppuentry)); + if change_endian then + entry.size:=swaplong(entry.size); + entrystart:=bufstart+bufidx; + entryidx:=0; + if not(entry.id in [mainentryid,subentryid]) then + begin + readentry:=iberror; + error:=true; + exit; + end; + readentry:=entry.nr; +end; + + +function tppufile.endofentry:boolean; +begin + endofentry:=(entryidx>=entry.size); +end; + + +function tppufile.entrysize:longint; +begin + entrysize:=entry.size; +end; + + +procedure tppufile.getdatabuf(var b;len:integer;var res:integer); +begin + if entryidx+len>entry.size then + res:=entry.size-entryidx + else + res:=len; + readdata(b,res); + inc(entryidx,res); +end; + + +procedure tppufile.getdata(var b;len:integer); +begin + if entryidx+len>entry.size then + begin + error:=true; + exit; + end; + readdata(b,len); + inc(entryidx,len); +end; + + +function tppufile.getbyte:byte; +var + b : byte; +begin + if entryidx+1>entry.size then + begin + error:=true; + getbyte:=0; + exit; + end; + readdata(b,1); + getbyte:=b; + inc(entryidx); +end; + + +function tppufile.getword:word; +var + w : word; +begin + if entryidx+2>entry.size then + begin + error:=true; + getword:=0; + exit; + end; + readdata(w,2); + if change_endian then + getword:=swapword(w) + else + getword:=w; + inc(entryidx,2); +end; + + +function tppufile.getlongint:longint; +var + l : longint; +begin + if entryidx+4>entry.size then + begin + error:=true; + getlongint:=0; + exit; + end; + readdata(l,4); + if change_endian then + getlongint:=swaplong(l) + else + getlongint:=l; + inc(entryidx,4); +end; + + +function tppufile.getint64:int64; +var + i : int64; +begin + if entryidx+8>entry.size then + begin + error:=true; + result:=0; + exit; + end; + readdata(i,8); + if change_endian then + result:=swapint64(i) + else + result:=i; + inc(entryidx,8); +end; + + +function tppufile.getaint:aint; +begin +{$ifdef cpu64bit} + result:=getint64; +{$else cpu64bit} + result:=getlongint; +{$endif cpu64bit} +end; + + +function tppufile.getreal:ppureal; +var + d : ppureal; +begin + if entryidx+sizeof(ppureal)>entry.size then + begin + error:=true; + getreal:=0; + exit; + end; + readdata(d,sizeof(ppureal)); + getreal:=d; + inc(entryidx,sizeof(ppureal)); +end; + + +function tppufile.getstring:string; +var + s : string; +begin + s[0]:=chr(getbyte); + if entryidx+length(s)>entry.size then + begin + error:=true; + exit; + end; + ReadData(s[1],length(s)); + getstring:=s; + inc(entryidx,length(s)); +end; + + +procedure tppufile.getsmallset(var b); +var + l : longint; +begin + l:=getlongint; + longint(b):=l; +end; + + +procedure tppufile.getnormalset(var b); +type + SetLongintArray = Array [0..7] of longint; +var + i : longint; +begin + if change_endian then + begin + for i:=0 to 7 do + SetLongintArray(b)[i]:=getlongint; + end + else + getdata(b,32); +end; + + +function tppufile.skipuntilentry(untilb:byte):boolean; +var + b : byte; +begin + repeat + b:=readentry; + until (b in [ibend,iberror]) or ((b=untilb) and (entry.id=mainentryid)); + skipuntilentry:=(b=untilb); +end; + + +{***************************************************************************** + TPPUFile Writing +*****************************************************************************} + +function tppufile.createfile:boolean; +begin + createfile:=false; +{$ifdef INTFPPU} + if crc_only then + begin + fname:=fname+'.intf'; + crc_only:=false; + end; +{$endif} + if not crc_only then + begin + assign(f,fname); + {$ifdef MACOS} + {FPas is FreePascal's creator code on MacOS. See systems/mac_crea.txt} + SetDefaultMacOSCreator('FPas'); + SetDefaultMacOSFiletype('FPPU'); + {$endif} + {$I-} + rewrite(f,1); + {$I+} + {$ifdef MACOS} + SetDefaultMacOSCreator('MPS '); + SetDefaultMacOSFiletype('TEXT'); + {$endif} + if ioresult<>0 then + exit; + Mode:=2; + {write header for sure} + blockwrite(f,header,sizeof(tppuheader)); + end; + bufsize:=ppubufsize; + bufstart:=sizeof(tppuheader); + bufidx:=0; +{reset} + crc:=cardinal($ffffffff); + interface_crc:=cardinal($ffffffff); + do_interface_crc:=true; + Error:=false; + do_crc:=true; + size:=0; + entrytyp:=mainentryid; +{start} + NewEntry; + createfile:=true; +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.size:=bufstart-sizeof(tppuheader); + { set the endian flag } +{$ifndef FPC_BIG_ENDIAN} + header.flags := header.flags or uf_little_endian; +{$else not FPC_BIG_ENDIAN} + header.flags := header.flags or uf_big_endian; + { Now swap the header in the correct endian (always little endian) } + header.compiler := SwapWord(header.compiler); + header.cpu := SwapWord(header.cpu); + header.target := SwapWord(header.target); + header.flags := SwapLong(header.flags); + header.size := SwapLong(header.size); + header.checksum := cardinal(SwapLong(longint(header.checksum))); + header.interface_checksum := cardinal(SwapLong(longint(header.interface_checksum))); +{$endif not FPC_BIG_ENDIAN} +{ write header and restore filepos after it } + opos:=filepos(f); + seek(f,0); + blockwrite(f,header,sizeof(tppuheader)); + seek(f,opos); +end; + + +procedure tppufile.writebuf; +begin + if not crc_only then + blockwrite(f,buf^,bufidx); + inc(bufstart,bufidx); + bufidx:=0; +end; + + +procedure tppufile.writedata(const b;len:integer); +var + p : pchar; + left, + idx : integer; +begin + if crc_only then + exit; + p:=pchar(@b); + idx:=0; + while len>0 do + begin + left:=bufsize-bufidx; + if len>left then + begin + move(p[idx],buf[bufidx],left); + dec(len,left); + inc(idx,left); + inc(bufidx,left); + writebuf; + end + else + begin + move(p[idx],buf[bufidx],len); + inc(bufidx,len); + exit; + end; + end; +end; + + +procedure tppufile.NewEntry; +begin + with entry do + begin + id:=entrytyp; + nr:=ibend; + size:=0; + end; +{Reset Entry State} + entryidx:=0; + entrybufstart:=bufstart; + entrystart:=bufstart+bufidx; +{Alloc in buffer} + writedata(entry,sizeof(tppuentry)); +end; + + +procedure tppufile.writeentry(ibnr:byte); +var + opos : integer; +begin +{create entry} + entry.id:=entrytyp; + entry.nr:=ibnr; + entry.size:=entryidx; +{it's already been sent to disk ?} + if entrybufstart<>bufstart then + begin + if not crc_only then + begin + {flush to be sure} + WriteBuf; + {write entry} + opos:=filepos(f); + seek(f,entrystart); + blockwrite(f,entry,sizeof(tppuentry)); + seek(f,opos); + end; + entrybufstart:=bufstart; + end + else + move(entry,buf[entrystart-bufstart],sizeof(entry)); +{Add New Entry, which is ibend by default} + entrystart:=bufstart+bufidx; {next entry position} + NewEntry; +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} + end; + end; + if not crc_only then + writedata(b,len); + inc(entryidx,len); +end; + + +procedure tppufile.putbyte(b:byte); +begin + putdata(b,1); +end; + + +procedure tppufile.putword(w:word); +begin + putdata(w,2); +end; + + +procedure tppufile.putlongint(l:longint); +begin + putdata(l,4); +end; + + +procedure tppufile.putint64(i:int64); +begin + putdata(i,8); +end; + + +procedure tppufile.putaint(i:aint); +begin + putdata(i,sizeof(aint)); +end; + + +procedure tppufile.putreal(d:ppureal); +begin + putdata(d,sizeof(ppureal)); +end; + + + procedure tppufile.putstring(s:string); + begin + putdata(s,length(s)+1); + end; + + + procedure tppufile.putsmallset(const b); + var + l : longint; + begin + l:=longint(b); + putlongint(l); + end; + + + procedure tppufile.putnormalset(const b); + type + SetLongintArray = Array [0..7] of longint; + var + i : longint; + tempb : setlongintarray; + begin + if change_endian then + begin + for i:=0 to 7 do + tempb[i]:=SwapLong(SetLongintArray(b)[i]); + putdata(tempb,32); + end + else + putdata(b,32); + end; + + + procedure tppufile.tempclose; + begin + if not closed then + begin + closepos:=filepos(f); + {$I-} + system.close(f); + {$I+} + if ioresult<>0 then; + closed:=true; + tempclosed:=true; + end; + end; + + + function tppufile.tempopen:boolean; + var + ofm : byte; + begin + tempopen:=false; + if not closed or not tempclosed then + exit; + ofm:=filemode; + filemode:=0; + {$I-} + reset(f,1); + {$I+} + filemode:=ofm; + if ioresult<>0 then + exit; + closed:=false; + tempclosed:=false; + + { restore state } + seek(f,closepos); + tempopen:=true; + end; + +end. |