Flags in Boolean Record packen - geht das?

Rund um die LCL und andere Komponenten
Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Flags in Boolean Record packen - geht das?

Beitrag von Timm Thaler »

Wenn man in Pascal eine Variable Boolean anlegt, belegt die mindestens ein Byte, obwohl sie nur zwei Zustände hat.

Ich speichere üblicherweise Statusflags auf dem AVR platzsparend in einer Variable vom Typ uint8, so dass jedes Flag ein Bit belegt, bekomme also 8 Booleans in ein Byte. Das sieht dann etwa so aus:

Code: Alles auswählen

const
  E_rtc       = 1 shl 0// Flag Fehler RTC
  E_twi       = 1 shl 1// Flag Fehler TWI
  E_eeprom    = 1 shl 2// Flag Fehler EEPROM
  E_sens      = 1 shl 7// Flag Fehler Sensor
var
  error : uint8;
 
  error := error or E_sens;  // Fehler Sensor setzen
  error := error and not E_sens;  // Fehler Sensor löschen
  if (error and E_rtc) = 0 then... // kein Fehler RTC
  if (error and E_rtc) <> 0 then... // Fehler RTC
  error := 0// Fehler löschen
 

Alternativ kann man auch statt der Bitmaske die Bitstelle nehmen, man darf es halt nicht mischen:

Code: Alles auswählen

const
  e_rtc       = 0// Flag Fehler RTC
  e_twi       = 1// Flag Fehler TWI
  e_eeprom    = 2// Flag Fehler EEPROM
  e_sens      = 7// Flag Fehler Sensor
var
  error : uint8;
 
  error := error or (1 << e_sens)// Fehler Sensor setzen
  error := error and not (1 << e_sens)// Fehler Sensor löschen
  if (error and (1 << e_rtc)) = 0 then... // kein Fehler RTC
  if (error and (1 << e_rtc)) <> 0 then... // Fehler RTC
  error := 0// Fehler löschen
 

Ist recht viel Schreibkram, da habe ich mir gedacht, es wäre doch schön, wenn es sowas gäbe:

Code: Alles auswählen

errtype = record
  rtc  : boolean;  // Flag Fehler RTC
  twi  : boolean;  // Flag Fehler TWI
  eeprom  : boolean;  // Flag Fehler EEPROM
  sens  : boolean;  // Flag Fehler Sensor
var
  error : errtype;
 

Wobei jedes Element in error nur ein Bit belegt.

Dann könnte man das so ansprechen:

Code: Alles auswählen

  error.sens := true// Fehler Sensor setzen
  error.sens := false// Fehler Sensor löschen
  if not error.rtc then... // kein Fehler RTC
  if error.rtc then... // Fehler RTC
  error := 0// alle Fehler löschen => naja, das wäre ein Bonus
 

Ich weiss, dass es solche gepackten Booleans unter Ada gibt, geht das in Pascal vielleicht auch? Und wird das für die Embedded AVR umgesetzt?

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Flags in Boolean Record packen - geht das?

Beitrag von m.fuchs »

Eigentlich kann kannst du das per Set eines Enums machen.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Flags in Boolean Record packen - geht das?

Beitrag von Mathias »

Hast du dies mal angeguckt ?

Code: Alles auswählen

var
  LEDPort: bitpacked array [0..7] of boolean absolute PORTD;
 
begin
  LEDPort[0] := True;
  LEDPort[1] := True;
  LEDPort[2] := False;
  LEDPort[3] := False;
Zuletzt geändert von Mathias am Mo 5. Mär 2018, 18:13, insgesamt 2-mal geändert.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

marcov
Beiträge: 1100
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Flags in Boolean Record packen - geht das?

Beitrag von marcov »

Siehe auch http://wiki.freepascal.org/Bit_manipulation zb

Code: Alles auswählen

TByteBits = bitpacked record
  Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7: Boolean;
end;
 
TByteEx = packed record
  case Integer of
    0: (ByteAccess: Byte);
    1: (BitAccess: TByteBits);
end;
 
TSomeBitLevelStructure = bitpacked record
  OneBit: 0..1;
  TwoBits: 0..3;
  FourBits: 0..15;
  EightBits: 0..255
end;

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Flags in Boolean Record packen - geht das?

Beitrag von Mathias »

bitpacked ist eine praktische Funktion, siehe Mini-Code.

Code: Alles auswählen

function ByteToBin(b:Byte):String;
var
  ba: bitpacked array[0..7] of Boolean absolute b;
  i:Integer;
begin
  SetLength(Result, 8);
  for i := 0 to 7 do begin
    Result[i + 1] := char(48 + byte(ba[i]));
  end;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  Caption:=ByteToBin(%11110011);
end;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Flags in Boolean Record packen - geht das?

Beitrag von Timm Thaler »

Das sieht doch ganz gut aus:

Code: Alles auswählen

type
  terror = bitpacked record  // Errorflags
    case byte of
      0 : (rtc,  // Flag Fehler RTC
           twi,  // Flag Fehler RTC
           eeprom,  // Flag Fehler EEPROM
           bit3,
           bit4,
           bit5,
           bit6,
           sens : boolean)// Flag Fehler Sensor
      1 : (all : uint8);
  end;
 
var
  error : terror;
 
  error.all := 0;
  error.rtc := true;
  error.rtc := false;
  if error.rtc then ...
  if not error.rtc then ...

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Flags in Boolean Record packen - geht das?

Beitrag von Timm Thaler »

Ich nehms zurück: Bei globaler Deklaration der Flag-Variablen wird das recht optimal umgesetzt. Aber bei Zuweisung über eine Prozedur

Code: Alles auswählen

type
  tSadc = bitpacked record  // Statusflags ADC
    case byte of
      0 : (bit0, bit1, bit2, bit3, gnd, open, noack, err : boolean);
      1 : (all : uint8);
  end;
 
var
  Sstat : array [1..Clen] of tSadc;  // Status Flags
 
write_status(Sstat[nr]);
 
procedure write_status(stat : tSadc);
begin
    if stat.noack then begin
      serial_string(@text_noack);
    end else if stat.open then begin
      serial_string(@text_opn)...
 


macht der Compiler leider sowas draus:

Code: Alles auswählen

# [363] if stat.noack then begin
   mov   r18,r2
   lsr   r18
   lsr   r18
   lsr   r18
   lsr   r18
   lsr   r18
   lsr   r18
   sbrs   r18,0
   rjmp   .Lj152
.Lj151:


Häh? Warum wird hier nicht gleich mit sbrs an der richtigen Bitstelle geprüft?

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Flags in Boolean Record packen - geht das?

Beitrag von siro »

Das gefällt mir....
er schiebt das abzufragende Bit Stück für Stück ins LSB um dann mit dem gleichen Befehl das Bit abzufragen,
mit dem er es auch ohne zu schieben abfragen kann... :mrgreen:

Compiler erzeugen nunmal nicht immer optimalen Code.
Die wenigsten schauen sich aber heutzutage den erzeugten Assemblercode noch an,
da sind wir wohl eher eine aussterbende Rasse....

Hast Du mal einen anderen Optimierungsslevel versucht:
https://www.freepascal.org/docs-html/prog/progse49.html

Siro
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Flags in Boolean Record packen - geht das?

Beitrag von Timm Thaler »

siro hat geschrieben:Compiler erzeugen nunmal nicht immer optimalen Code.


Es ist nur schade, dass die umständliche Schreibweise (Sstat[k] and (1 shl 6) = 0 wunderbar zu sbrc r18,6 kompiliert wird, die elegantere Schreibweise mit bitpacked record das aber nicht nutzt.

siro hat geschrieben:Die wenigsten schauen sich aber heutzutage den erzeugten Assemblercode noch an


Plötzlich 300 Byte Flashbedarf mehr "aus dem Nichts", ohne zusätzliche Funktionen implementiert zu haben fällt halt auf. Und da habe ich nur einen Teil meiner Flag-Abfragen (Test auf Error-Flags) umgestellt, der Rest arbeitet noch mit Bitmasken und Vergleich. Wenn ich die alle umsetze wird es wahrscheinlich 1k mehr Speicherbedarf durch die lsr ohne Zugewinn an Funktionalität.

siro hat geschrieben:Hast Du mal einen anderen Optimierungsslevel versucht:


Ich optimiere für AVR immer auf -O3, -O4 möche ich für ein Projekt bei dem es auf Zuverlässigkeit ankommt nicht machen müssen.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Flags in Boolean Record packen - geht das?

Beitrag von Mathias »

Es ist nur schade, dass die umständliche Schreibweise (Sstat[k] and (1 shl 6) = 0 wunderbar zu sbrc r18,6 kompiliert wird, die elegantere Schreibweise mit bitpacked record das aber nicht nutzt.
Als ich dazumal das Tutorial erstellt, hatten alle Pin-Zugriffe den gleichen Speicherbedarf.

http://wiki.freepascal.org/AVR_Embedded_Tutorial_-_Simple_GPIO_on_and_off_output/de#Pin_direkt_ansprechen

Oder hat der Compiler Mühe mit dem case im record ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Flags in Boolean Record packen - geht das?

Beitrag von Timm Thaler »

Mathias hat geschrieben:Als ich dazumal das Tutorial erstellt, hatten alle Pin-Zugriffe den gleichen Speicherbedarf.


Das sind auch Portzugriffe mit sbi und cbi. Da kann der Compiler nichts sinnvoll shiften.

Mathias hat geschrieben:Oder hat der Compiler Mühe mit dem case im record ?


Nope, keine Änderung, wenn bitpacked record ohne case angelegt wird.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Flags in Boolean Record packen - geht das?

Beitrag von Timm Thaler »

Ist optimiert:

Code: Alles auswählen

# [81] if ftest.bit6 then begin
   lds   r18,(U_sHVS_DEFINE_ss_FTEST)
   sbrs   r18,6
   rjmp   .Lj19
.Lj18:
# [82] if not ftest.bit3 then begin
   lds   r18,(U_sHVS_DEFINE_ss_FTEST)
   sbrc   r18,3
   rjmp   .Lj21
.Lj22:
 


Dieser Florian ist echt fit. https://bugs.freepascal.org/view.php?id=33417

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Flags in Boolean Record packen - geht das?

Beitrag von Socke »

Timm Thaler hat geschrieben:Dieser Florian ist echt fit.

Dieser Florian entwickelt auch schon seit 1993 am Free Pascal Compiler :D
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Flags in Boolean Record packen - geht das?

Beitrag von Timm Thaler »

Ach, der Florian ist das.

Ich glaub es ist noch ein Bug im if not Vergleich auf einzelne Bits, ich bin gerade dabei das einzugrenzen.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Flags in Boolean Record packen - geht das?

Beitrag von Timm Thaler »

Also da ist momentan noch ein Bug, wenn man mit if not ... then auf false prüft und der record by value an eine Prozedur übergeben wurde. Dann werden die Anweisungen nach then ignoriert. Bei Prüfung auf true oder bei Übergabe by reference funktioniert es.

(https://bugs.freepascal.org/view.php?id=33423)

Boah, auf so komische Fehler muss man erstmal kommen. Und ich wunder mich, warum meine Heizung nicht geht... ;-)

Antworten