mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 10:18:22 +02:00

to 0 or field_type(-1) (bug noted by Russell Davies on fpc-devel + his test programs) * fixed bit offset calculations for nested bitpacked record regvars git-svn-id: trunk@11242 -
163 lines
5.5 KiB
ObjectPascal
163 lines
5.5 KiB
ObjectPascal
// http://lists.freepascal.org/lists/fpc-devel/2008-June/013919.html
|
|
|
|
// I was interested to see if bit packing works when a record member spans
|
|
// byte boundaries, and in general it appears to work. However on my system
|
|
// I discovered a bug that this program illustrates.
|
|
//
|
|
// This program demonstrates a bug using a bitpacked record where a member
|
|
// crosses a byte boundary.
|
|
// The record structure is (on little endian systems -- Jonas):
|
|
// Member: | bit15_9 | bit8_1 | bit0 |
|
|
// Bits: | 15 .. 9 | 8 .. 1 | 0 |
|
|
// Value: | 0..127 | 0..255 | 0..1 |
|
|
//
|
|
// The structure is mapped to a word via a variant record for convenience.
|
|
//
|
|
// The limited amount of testing done indicates that the record member bit8_1
|
|
// only causes a problem with a value of $FF, but the interesting thing is
|
|
// that the result varies depending on other (unrelated) program structure.
|
|
// For example the expected word result with bit 0 = 1, bits 1..9 = $FF and
|
|
// the rest 0, should be $01FF but I have seen the correct value as well as
|
|
// results of $0001, $0003, $0121, $012. Adding code before the tests seems
|
|
// to change the result, possibly/ indicating that some variable or register
|
|
// used in the bitpacking routine is not being cleared/initialized.
|
|
//
|
|
// Different compiler modes, optimisations, range checking were tried, but
|
|
// the results were the same.
|
|
//
|
|
// Note that using a variant record to show the value is only a convenience
|
|
// here and the error can be seen without a variant record by examining
|
|
// the struct directly, or by overlaying the word using the absolute keyword.
|
|
//
|
|
// Tested on Intel Core 2 Duo running Windows XP Pro SP2, Compiler version
|
|
// 2.2.0 [2007/09/09] and 2.3.1 [2008/02/03]
|
|
|
|
|
|
|
|
uses SysUtils;
|
|
|
|
|
|
type
|
|
bit = 0..1;
|
|
t7bit = 0..127;
|
|
|
|
// A record to test behaviour over byte boundaries.
|
|
BitStruct = bitpacked record
|
|
bit0 : bit;
|
|
bit8_1 : byte; // This set to $FF causes problems...
|
|
bit15_9 : t7bit;
|
|
end;
|
|
|
|
// Map the record to a word for convenience - but overlaying
|
|
// a word using absolute instead a variant record produces
|
|
// the same result.
|
|
|
|
MappedStruct = packed record
|
|
case boolean of
|
|
false : (AsWord : word);
|
|
true : (AsBits : BitStruct);
|
|
end;
|
|
|
|
|
|
procedure TestBits;
|
|
var
|
|
TestLocal : MappedStruct;
|
|
begin
|
|
TestLocal.AsBits.bit0 := 1;
|
|
TestLocal.AsBits.bit8_1 := $FF;
|
|
TestLocal.AsBits.bit15_9 := $0;
|
|
if (TestLocal.AsBits.bit0<>1) or
|
|
(TestLocal.AsBits.bit8_1<>$ff) or
|
|
(TestLocal.AsBits.bit15_9<>0) then
|
|
halt(1);
|
|
// writeln(' Expected : $01FF, Got : $',IntToHex(TestLocal.AsWord,4),' (I get $0121 V2.2.0, $0109 V2.3.1)');
|
|
end;
|
|
|
|
|
|
var
|
|
TestGlobal : MappedStruct;
|
|
begin
|
|
//Do test in main routine - on my system results in $0001.
|
|
// Also interesting - using 'with TestGlobal, AsBits do begin ...' instead of
|
|
// fully qualified names returns different values in some cases.
|
|
|
|
Writeln('Testing in main: | $00 | $FF | 1 |');
|
|
TestGlobal.AsBits.bit0 := 1;
|
|
TestGlobal.AsBits.bit8_1 := $FF;
|
|
TestGlobal.AsBits.bit15_9 := $0;
|
|
if (TestGlobal.AsBits.bit0<>1) or
|
|
(TestGlobal.AsBits.bit8_1<>$ff) or
|
|
(TestGlobal.AsBits.bit15_9<>0) then
|
|
halt(2);
|
|
// writeln(' Expected : $01FF, Got : $',IntToHex(TestGlobal.AsWord,4), ' (I get $0001 V2.2.0, $01F9 V2.3.1)');
|
|
|
|
// Test it in a procedure - results in $0121 on V2.2.0
|
|
writeln;
|
|
Writeln('Testing in procedure: | $01 | $FF | 1 |');
|
|
TestBits;
|
|
|
|
// Test this in main
|
|
Writeln;
|
|
Writeln('Back in main: | $3F | $FF | 1 |');
|
|
TestGlobal.AsBits.bit0 := 1;
|
|
TestGlobal.AsBits.bit8_1 := $FF;
|
|
TestGlobal.AsBits.bit15_9 := $3F;
|
|
if (TestGlobal.AsBits.bit0<>1) or
|
|
(TestGlobal.AsBits.bit8_1<>$ff) or
|
|
(TestGlobal.AsBits.bit15_9<>$3f) then
|
|
halt(3);
|
|
// writeln(' Expected : $7FFF, Got : $',IntToHex(TestGlobal.AsWord,4),' ($7E01 V2.2.0, $7FF9 V2.3.1)');
|
|
|
|
// and again in main.
|
|
Writeln;
|
|
Writeln('In main, | $7F | $FF | 1 |');
|
|
TestGlobal.AsBits.bit0 := 1;
|
|
TestGlobal.AsBits.bit8_1 := $FF;
|
|
TestGlobal.AsBits.bit15_9 := $7F;
|
|
if (TestGlobal.AsBits.bit0<>1) or
|
|
(TestGlobal.AsBits.bit8_1<>$ff) or
|
|
(TestGlobal.AsBits.bit15_9<>$7f) then
|
|
halt(4);
|
|
// writeln(' Expected : $FFFF, Got : $',IntToHex(TestGlobal.AsWord,4), ' ($FE01 V.2.2.0, $FFF9 V2.3.1)');
|
|
|
|
|
|
// Now set bits 8..1 to $FE
|
|
Writeln;
|
|
Writeln('Above tests, but with bits 8..1 set to $FE - all work on my system');
|
|
|
|
Writeln(' | $00 | $FE | 1 |');
|
|
TestGlobal.AsBits.bit0 := 1;
|
|
TestGlobal.AsBits.bit8_1 := $FE;
|
|
TestGlobal.AsBits.bit15_9 := $0;
|
|
if (TestGlobal.AsBits.bit0<>1) or
|
|
(TestGlobal.AsBits.bit8_1<>$fe) or
|
|
(TestGlobal.AsBits.bit15_9<>0) then
|
|
halt(5);
|
|
// writeln(' Expected : $01FD, Got : $',IntToHex(TestGlobal.AsWord,4));
|
|
|
|
Writeln;
|
|
Writeln(' | $3F | $FE | 1 |');
|
|
TestGlobal.AsBits.bit0 := 1;
|
|
TestGlobal.AsBits.bit8_1 := $FE;
|
|
TestGlobal.AsBits.bit15_9 := $3F;
|
|
if (TestGlobal.AsBits.bit0<>1) or
|
|
(TestGlobal.AsBits.bit8_1<>$fe) or
|
|
(TestGlobal.AsBits.bit15_9<>$3f) then
|
|
halt(6);
|
|
// writeln(' Expected : $7FFD, Got : $',IntToHex(TestGlobal.AsWord,4));
|
|
|
|
// and again in main.
|
|
Writeln;
|
|
Writeln(' | $7F | $FE | 1 |');
|
|
TestGlobal.AsBits.bit0 := 1;
|
|
TestGlobal.AsBits.bit8_1 := $FE;
|
|
TestGlobal.AsBits.bit15_9 := $7F;
|
|
if (TestGlobal.AsBits.bit0<>1) or
|
|
(TestGlobal.AsBits.bit8_1<>$fe) or
|
|
(TestGlobal.AsBits.bit15_9<>$7f) then
|
|
halt(7);
|
|
// writeln(' Expected : $FFFD, Got : $',IntToHex(TestGlobal.AsWord,4));
|
|
|
|
end.
|
|
|