Structure bitweise füllen / abfragen

Für allgemeine Fragen zur Programmierung mit PureBasic.
andi256
Beiträge: 100
Registriert: 06.11.2004 11:23
Computerausstattung: PB 5.30 (x64) Win7
Wohnort: Österreich

Structure bitweise füllen / abfragen

Beitrag von andi256 »

Hallo

Hat wer eine Idee wie man eine Structure "schön" bitweise füllen kann ?

Meine struct setzt sich aus lauter 1-bit flags zusammen die ich einzeln ansprechen will
ala

Code: Alles auswählen

error.COM_ERROR
error\f_cts_time = 1
ohne mit Masken und Shifts da rumzuspielen

Code: Alles auswählen

error.COM_ERROR
error\error_flags = %0000010000000000
f_cts_time = ((error\error_flags & %0000010000000000) >> 10)

Code: Alles auswählen

Structure COM_ERROR
 error_flags.u               0000 0000   0000 0000
;  f_Data         ; 1 bit    x
;  f_net          ; 2 bit     xx
;  f_com          ; 1 bit       x 
;  f_break        ; 1 bit         x 
;  f_cts_time     ; 1 bit          x
;  f_dsr_time     ; 1 bit           x
;  f_rlsd_time    ; 1 bit            x

;  f_overrun      ; 1 bit
;  f_parity       ; 1 bit
;  f_frame        ; 1 bit
;  f_status       ; 1 bit
;  f_no_use_1     ; 1 bit
;  f_no_use_2     ; 1 bit
;  f_rx_over      ; 1 bit
;  f_nouse_3      ; 1 bit  Summe 16 bit 
EndStructure 

error.COM_ERROR
error\error_flags = 0
Danke Andi
Benutzeravatar
Pelagio
Beiträge: 424
Registriert: 11.11.2004 17:52
Computerausstattung: Intel Core i3-4170 CPU 3,70 GHz
8,00 GB Arbeitsspeicher
WIN 10 Pro 64 Bit Betriebssystem
Wohnort: Bremen

Re: Structure bitweise füllen / abfragen

Beitrag von Pelagio »

Hallo andi256,

Ich bin mir zwar nicht sicher ob mein Beispiel dem entspricht was du dir vorgestellt hast aber es wäre jedenfalls eine weiter zu entwickelnde Idee.

Code: Alles auswählen

Structure BIT_Error
  f_Data.s{1}        
  f_net.s{2}         
  f_com.s{1}         
  Leer1.s{1}
  f_break.s{1}       
  f_cts_time.s{1}    
  f_dsr_time.s{1}    
  f_rlsd_time.s{1}   
  Leer2.s{2}
  f_overrun.s{1}     
  f_parity.s{1}      
  f_frame.s{1}       
  f_status.s{1}      
  Leer3.s{1}
  f_no_use_1.s{1}    
  f_no_use_2.s{1}    
  f_rx_over.s{1}     
  f_nouse_3.s{1}     
EndStructure

Structure COM_ERROR
   StructureUnion
      error_flags.s{20}   ;0000 0000  0000 0000
      Bit.Bit_Error
   EndStructureUnion
EndStructure 

error.COM_ERROR
error\error_flags = "1000 0010  0000 0000"
Debug error\Bit\f_Data
Debug error\Bit\f_dsr_Time
Ohne Zeit kein Fleiß
Auf neustem Stand zu sein ist eine Kunst die nicht jeder perfektioniert [Win10Pro(64); PB6.10 LTS]. :allright:
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Structure bitweise füllen / abfragen

Beitrag von NicTheQuick »

Meine Idee wäre zwar Anfangs mit etwas Arbeit verbunden, aber funktioniert dann recht einfach. Ich habe dir die Arbeit auch schon mal abgenommen. :wink:

Code: Alles auswählen

Structure COM_ERROR
	error_flags.u    ;          0000 0000   0000 0000
	; f_data         ; 1 bit    x
	; f_net          ; 2 bit     xx
	; f_com          ; 1 bit       x
	; f_break        ; 1 bit         x
	; f_cts_time     ; 1 bit          x
	; f_dsr_time     ; 1 bit           x
	; f_rlsd_time    ; 1 bit            x
	
	; f_overrun      ; 1 bit
	; f_parity       ; 1 bit
	; f_frame        ; 1 bit
	; f_status       ; 1 bit
	; f_no_use_1     ; 1 bit
	; f_no_use_2     ; 1 bit
	; f_rx_over      ; 1 bit
	; f_nouse_3      ; 1 bit  Summe 16 bit
EndStructure

#f_data_bm      = %1000000000000000 : #f_data_bp      = 15
#f_net_bm       = %0110000000000000 : #f_net_bp       = 13
#f_com_bm       = %0001000000000000 : #f_com_bp       = 12
#f_break_bm     = %0000100000000000 : #f_break_bp     = 11
#f_cts_time_bm  = %0000010000000000 : #f_cts_time_bp  = 10
#f_dsr_time_bm  = %0000001000000000 : #f_dsr_time_bp  =  9
#f_rlsd_time_bm = %0000000100000000 : #f_rlsd_time_bp =  8
#f_overrun_bm   = %0000000010000000 : #f_overrun_bp   =  7
#f_parity_bm    = %0000000001000000 : #f_parity_bp    =  6
#f_frame_bm     = %0000000000100000 : #f_frame_bp     =  5
#f_status_bm    = %0000000000010000 : #f_status_bp    =  4
#f_rx_over_bm   = %0000000000000010 : #f_rx_over_bp   =  1
#f_nouse_3_bm   = %0000000000000001 : #f_nouse_3_bp   =  0

error.COM_ERROR
error\error_flags = 0

Macro SetFlag(var, flag, value = $ffff)
	var = (var & ~flag#_bm) | ((value << flag#_bp) & flag#_bm)
EndMacro

Macro ClearFlag(var, flag)
	var & ~flag#_bm
EndMacro

SetFlag(error\error_flags, #f_net, 3)
SetFlag(error\error_flags, #f_cts_time)
SetFlag(error\error_flags, #f_nouse_3)

Debug Bin(error\error_flags)

ClearFlag(error\error_flags, #f_cts_time)
SetFlag(error\error_flags, #f_dsr_time)
ClearFlag(error\error_flags, #f_nouse_3)

Debug Bin(error\error_flags)
Bild
andi256
Beiträge: 100
Registriert: 06.11.2004 11:23
Computerausstattung: PB 5.30 (x64) Win7
Wohnort: Österreich

Re: Structure bitweise füllen / abfragen

Beitrag von andi256 »

@Pelagio

Danke für deinen Ansatz ... da ich aber die structur empfangen -> ändern -> senden muß .. ist das mit der deklaration als "string" so für mich nicht nutzbar ....

@Nick
Dein Ansatz ist schon mal super nur bedeutet das doch noch einige Arbeit für mich ...in meinen Auszug von oben sind 2 vom 30 Bytes angeführt :-( ... aber ich brauche nur einen kleinen Teil der flags ... und die kann ich mir mit deiner Methode schön zusammenbasteln!


Anleitung hier (Seite 188 - 192)

Code: Alles auswählen

typedef struct _com_error
{ union
{ word error_flags;
struct _err_flags
{ word f_data : 1; //not used/reserved
word f_net : 2; //not used/reserved
word f_com : 1; //Set when COM port error detected
word f_break : 1; //Reflect the break flag
word f_cts_time : 1; //Time out while waiting on CTS
word f_dsr_time : 1; //Time out while waiting on DSR
word f_rlsd_time : 1; //Time out while waiting on RLSD (CD)
word f_overrun : 1; //Overrun error
word f_parity : 1; //Parity error
word f_frame : 1; //Framing error
word f_status : 1; //not used/reserved
word no_use_1 : 1; //not used
word no_use_2 : 1; //not used
word f_rx_over : 1; //Ring buffer overrun after handshake
word no_use_3 : 1; //not used
};
};
} COM_ERROR;
Codesniplet laut Anleitung in einer anderen Sprache ... schaut da für mich doch wesentlich einfacher aus :-) !

Danke Andi
Zuletzt geändert von andi256 am 23.08.2013 13:15, insgesamt 1-mal geändert.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Structure bitweise füllen / abfragen

Beitrag von STARGÅTE »

Also Bit-Strukturfelder gibt es (leider) nicht in PureBasic, sodass du erst mal nur ein 2-Byte-Feld nehmen kannst.
Pelagio Idee ist zwar nett, erfüllt aber nicht die bedingung, dass es wirklich nur einzelne Bit sind, denn ein Zeichen halt 1 bis 2 Byte.
@NicTheQuick: Die "Doppeldefinition" ist eigentlich unnötig, weil du ja jederzeit aus der Position, die echte Zahl bekommen kannst, mit einem Shift.

Ich verwende ganz gerne diese Beiden Prozeduren (Set/GetBit):

Code: Alles auswählen

Structure AllTypes
	StructureUnion
		b.b
		w.w
		l.l
		q.q
	EndStructureUnion
EndStructure

Procedure SetBit(*Buffer.AllTypes, Position.i, Value.i)
	Select Position
		Case 0 To 7
			*Buffer\b = *Buffer\b & ~(1<<Position) | (Value<<Position)
		Case 8 To 15
			*Buffer\w = *Buffer\w & ~(1<<Position) | (Value<<Position)
		Case 16 To 31
			*Buffer\l = *Buffer\l & ~(1<<Position) | (Value<<Position)
		Case 32 To 63
			*Buffer\q = *Buffer\q & ~(1<<Position) | (Value<<Position)
		Default
			SetBit(*Buffer + Position/8, Position%8, Value)
	EndSelect
EndProcedure

Procedure GetBit(*Buffer.AllTypes, Position.i)
	Select Position
		Case 0 To 7
			ProcedureReturn (*Buffer\b>>Position)&1
		Case 8 To 15
			ProcedureReturn (*Buffer\w>>Position)&1
		Case 16 To 31
			ProcedureReturn (*Buffer\l>>Position)&1
		Case 32 To 63
			ProcedureReturn (*Buffer\q>>Position)&1
		Default
			ProcedureReturn GetBit(*Buffer + Position/8, Position%8)
	EndSelect	
EndProcedure

Define Long.l = %100100
SetBit(@Long, 2, 0)
SetBit(@Long, 3, 1)
Debug Bin(Long)
Debug GetBit(@Long, 5)
Sie arbeiten mit beliebig langen Speichern bzw. Positionsangaben, also auch mit deiner 30 Byte-Struktur also 240 Bits
Und du müsstest nur Konstanten für deine Felderpositionen definieren
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Structure bitweise füllen / abfragen

Beitrag von NicTheQuick »

Ja, C bzw. C++ bietet solche Bit-Sachen von Haus aus an, weil das gerade bei der Programmierung von Treibern etc. vieles vereinfacht. Deswegen ist dieses Konstrukt auch so in die Sprache eingeflossen. Aber bei Purebasic wird sowas wohl nicht kommen, weil das immer noch selten ist einzelne Bits zu manipulieren.
STARGÅTE hat geschrieben:@NicTheQuick: Die "Doppeldefinition" ist eigentlich unnötig, weil du ja jederzeit aus der Position, die echte Zahl bekommen kannst, mit einem Shift.
Nicht ganz. Er hat nämlich auch ein Feld, wo 2 Bits manipuliert werden sollen. Deswegen hat die Bitmaske an dieser Stelle auch 2 Bits gesetzt. Mit einem normalen Shift würde es dann nicht mehr sauber funktionieren, weil das Macro dann beim löschen eines 2-Bit-Feldes nicht wüsste, dass dieses 2 Bits hat. ;)
Bild
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Structure bitweise füllen / abfragen

Beitrag von STARGÅTE »

Ah das habe ich nicht gesehen, in dem Fall, könnte man aber mein SetBit um einen Zusatzparameter (Länge) erweitern, welcher die Maske entsprechend anpasst. Der Wert kann ja dann trotzdem normal übergeben werden:
Bitte schön:

Code: Alles auswählen

Structure AllTypes
	StructureUnion
		b.b
		w.w
		l.l
		q.q
	EndStructureUnion
EndStructure

Procedure SetBit(*Buffer.AllTypes, Position.i, Value.i, Length.i=1)
	Select Position
		Case 0 To 7
			*Buffer\b = *Buffer\b & ~(($FF>>(8-Length))<<Position) | (Value<<Position)
		Case 8 To 15
			*Buffer\w = *Buffer\w & ~(($FFFF>>(16-Length))<<Position) | (Value<<Position)
		Case 16 To 31
			*Buffer\l = *Buffer\l & ~(($FFFFFFFF>>(32-Length))<<Position) | (Value<<Position)
		Case 32 To 63
			*Buffer\q = *Buffer\q & ~(($FFFFFFFFFFFFFFFF>>(64-Length))<<Position) | (Value<<Position)
		Default
			SetBit(*Buffer + Position/8, Position%8, Value, Length)
	EndSelect
EndProcedure

Procedure GetBit(*Buffer.AllTypes, Position.i, Length.i=1)
	Select Position
		Case 0 To 7
			ProcedureReturn (*Buffer\b>>Position)&($FF>>(8-Length))
		Case 8 To 15
			ProcedureReturn (*Buffer\w>>Position)&($FFFF>>(16-Length))
		Case 16 To 31
			ProcedureReturn (*Buffer\l>>Position)&($FFFFFFFF>>(32-Length))
		Case 32 To 63
			ProcedureReturn (*Buffer\q>>Position)&($FFFFFFFFFFFFFFFF>>(64-Length))
		Default
			ProcedureReturn GetBit(*Buffer + Position/8, Position%8, Length)
	EndSelect	
EndProcedure

Define Long.l = %100100
SetBit(@Long, 2, %0)
SetBit(@Long, 3, %11, 2)
Debug Bin(Long)
Debug GetBit(@Long, 5)
Debug Bin(GetBit(@Long, 2, 2))
Edit: Bugfix
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
alter Mann
Beiträge: 201
Registriert: 29.08.2008 09:13
Wohnort: hinterm Mond

Re: Structure bitweise füllen / abfragen

Beitrag von alter Mann »

@Stargate:
Ich weiß, dass das jetzt gemein ist...

Code: Alles auswählen

Structure AllTypes
   StructureUnion
      b.b
      w.w
      l.l
      q.q
   EndStructureUnion
EndStructure

Procedure SetBit(*Buffer.AllTypes, Position.i, Value.i, Length.i=1)
   Select Position
      Case 0 To 7
         *Buffer\b = *Buffer\b & ~(($FF>>(8-Length))<<Position) | (Value<<Position)
      Case 8 To 15
         *Buffer\w = *Buffer\w & ~(($FFFF>>(16-Length))<<Position) | (Value<<Position)
      Case 16 To 31
         *Buffer\l = *Buffer\l & ~(($FFFFFFFF>>(32-Length))<<Position) | (Value<<Position)
      Case 32 To 63
         *Buffer\q = *Buffer\q & ~(($FFFFFFFFFFFFFFFF>>(64-Length))<<Position) | (Value<<Position)
      Default
         SetBit(*Buffer + Position/8, Position%8, Value, Length)
   EndSelect
EndProcedure

Procedure GetBit(*Buffer.AllTypes, Position.i, Length.i=1)
   Select Position
      Case 0 To 7
         ProcedureReturn (*Buffer\b>>Position)&($FF>>(8-Length))
      Case 8 To 15
         ProcedureReturn (*Buffer\w>>Position)&($FFFF>>(16-Length))
      Case 16 To 31
         ProcedureReturn (*Buffer\l>>Position)&($FFFFFFFF>>(32-Length))
      Case 32 To 63
         ProcedureReturn (*Buffer\q>>Position)&($FFFFFFFFFFFFFFFF>>(64-Length))
      Default
         ProcedureReturn GetBit(*Buffer + Position/8, Position%8, Length)
   EndSelect   
EndProcedure

Define Long.l = 0
SetBit(@Long, 7, %111111111, 9)
Debug Bin(GetBit(@Long, 7, 9))
Win11 64Bit / PB 6.0
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Structure bitweise füllen / abfragen

Beitrag von NicTheQuick »

alter Mann hat geschrieben:@Stargate:
Ich weiß, dass das jetzt gemein ist...
Das hab ich auch schon gesehen, war aber grad zu beschäftigt um es zu bemängeln. :P
Am besten wäre eigentlich man geht so viele Bytes vor wie möglich und nutzt dann immer ein Quad für die Manipulation selbst.

Code: Alles auswählen

Procedure SetBit(*Buffer.Quad, Position.i, Value.i, Length.i=1)
	If (Position > 7)
		*Buffer + Position / 8
		Position & $ff
	EndIf
	*Buffer\q = *Buffer\q & ~(($FFFFFFFFFFFFFFFF >> (64 - Length)) << Position) | (Value << Position)
EndProcedure

Procedure.q GetBit(*Buffer.Quad, Position.i, Length.i=1)
	If (Position > 7)
		*Buffer + Position / 8
		Position & $ff
	EndIf
	ProcedureReturn (*Buffer\q >> Position) & ($FFFFFFFFFFFFFFFF >> (64 - Length))
EndProcedure

Procedure.s getBitString(*Buffer.Ascii, Length.i = 4)
	Protected result.s
	*Buffer + Length - 1
	While Length
		result + RSet(Bin(*Buffer\a, #PB_Ascii), 8, "0")
		*Buffer - 1
		Length - 1
	Wend
	ProcedureReturn result
EndProcedure

Define Long.l = 0
SetBit(@Long, 7, %111111111, 9)
Debug Bin(GetBit(@Long, 7, 9))
Debug getBitString(@Long, 4)
Allerdings gibt es dann immer noch eine Limitierung auf 64 - 7 = 57 Bits im Worst case.
Bild
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Structure bitweise füllen / abfragen

Beitrag von STARGÅTE »

Danke fürs Testen alter Mann.

Bei der Abfrage des Types muss ich natürlich die Position und Länge berücksichtigen, um den Typ zu bestimmen.
Hier die neue abgeänderte Version:

Code: Alles auswählen

Structure AllTypes
	StructureUnion
		b.b : w.w : l.l : q.q
	EndStructureUnion
EndStructure

Procedure SetBit(*Buffer.AllTypes, Position.i, Value.i, Length.i=1)
	If Position > 7
		SetBit(*Buffer + Position/8, Position%8, Value, Length)
	EndIf
	Select Position+Length
		Case 1 To 8
			*Buffer\b = *Buffer\b & ~(($FF>>(8-Length))<<Position) | (Value<<Position)
		Case 9 To 16
			*Buffer\w = *Buffer\w & ~(($FFFF>>(16-Length))<<Position) | (Value<<Position)
		Case 17 To 32
			*Buffer\l = *Buffer\l & ~(($FFFFFFFF>>(32-Length))<<Position) | (Value<<Position)
		Case 33 To 64
			*Buffer\q = *Buffer\q & ~(($FFFFFFFFFFFFFFFF>>(64-Length))<<Position) | (Value<<Position)
	EndSelect
EndProcedure

Procedure GetBit(*Buffer.AllTypes, Position.i, Length.i=1)
	If Position > 7
		ProcedureReturn GetBit(*Buffer + Position/8, Position%8, Length)
	EndIf
	Select Position+Length
		Case 1 To 8
			ProcedureReturn (*Buffer\b>>Position)&($FF>>(8-Length))
		Case 9 To 16
			ProcedureReturn (*Buffer\w>>Position)&($FFFF>>(16-Length))
		Case 17 To 32
			ProcedureReturn (*Buffer\l>>Position)&($FFFFFFFF>>(32-Length))
		Case 33 To 64
			ProcedureReturn (*Buffer\q>>Position)&($FFFFFFFFFFFFFFFF>>(64-Length))
		Default
	EndSelect   
EndProcedure

Define Long.l = 0
SetBit(@Long, 7, %111111111, 9)
Debug RSet(Bin(Long), 32, "0")
Debug Bin(GetBit(@Long, 7, 9))
@Nic: Immer eine Quad zu schreiben ist schlecht, da ein Feld/Buffer ja u.U. auch kleiner sein kann, und dann kommt ein IMA.
Mit der neuen Version sollte es jetzt funktionieren, mit der Einschränkung, das ich auf einem 8-Teilerfremden Bit keine komplette Quad schreiben kann. Könnte man aber durchaus noch hinzufügen, als Case 65 To 72, und dort eine Quad und ein Byte manipulieren ...
Leider fällt mir gerade auf, dass das BitShift von PB probleme mit Sign-Quads hat:

Code: Alles auswählen

q.q = $FFFFFFFFFFFFFFFF
q >> 32
Debug q
Das Vorzeichen bleibt bestehen.

Ich werde heute Abend zuhause mal n ASM-Code dafür schreiben.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Antworten