我想将表示寄存器的压缩数组传递给接受char *
的C函数(在DLL中)。这是我到目前为止的代码:
from ctypes import *
class BitField():
def __init__(self,position,size):
self.position = position
self.size = size
class Reg1(BitField):
self.dll = "win32.dll"
self.B6 = BitField(self,58,6)
self.B5 = BitField(self,54,4)
self.B4 = BitField(self,48,6)
self.B3 = BitField(self,36,12)
self.B2 = BitField(self,24,12)
self.B1 = BitField(self,16,8)
self.B0 = BitField(self,0,16)
pack_register(Reg1,(expects a tuple)):
pass # created a stub , need help on this
#I want to pack the to 64 bit data, by padding all other zero's,
#if none of bit #fields specified by user.
obj = Reg1()
obj.B5 = 4
obj.B4 = 12
obj.B0 = 70
charBuffer = create_string_buffer(64)
packed_value =pack_register(Reg1,(B5,B4,B0))
charBuffer = packed_value
obj.dll.createBuff(charBuffer) # accepting a character pointer
这是win32.dll函数(在C中)
int createBuff (char * charBuffer){
print charBuffer
}
从代码中的注释可以看出,我不知道如何将寄存器打包成64位。我也有32位和128位寄存器。
如何打包寄存器并以合适的格式输出以传递给我需要char *
的C函数?
答案 0 :(得分:5)
此代码创建一个类,让您阅读&使用字段名称写入位域。它没有记在ctypes
,但你仍然觉得它很有用。
#!/usr/bin/env python
""" A bit field class
See http://stackoverflow.com/q/31960327/4014959
Written by PM 2Ring 2015.08.12
"""
class BitFields(object):
""" A bit field class
fieldwidth is a tuple or list containing the
bit width of each field, from least significant
to most significant.
"""
def __init__(self, totalwidth, fieldwidths):
if sum(fieldwidths) != totalwidth:
raise ValueError, "Field width error"
self.fieldwidths = fieldwidths
self.num_fields = len(fieldwidths)
#Calculate field offsets
self.offsets = []
pos = 0
for w in fieldwidths:
self.offsets.append(pos)
pos += w
#Set up bitfield attribute names
self.field_names = ['b' + str(i) for i in range(self.num_fields)]
self.clear()
#Set all fields to zero
def clear(self):
for f in self.field_names:
setattr(self, f, 0)
#A generator expression of all the field values
def _all_fields(self):
return (getattr(self, f) for f in self.field_names)
def __str__(self):
return ', '.join(['%s: 0x%x' % (f, v)
for f, v in zip(self.field_names, self._all_fields())])
#Get the register value as an int
@property
def value(self):
return sum(v<<p for v, p in zip(self._all_fields(), self.offsets))
#Set field values
def regset(self, **kwargs):
for f, v in kwargs.items():
setattr(self, f, v)
#Test
fields = (16, 8, 12, 12, 6, 4, 6)
reg = BitFields(64, fields)
#Set some fields by attribute
reg.b0 = 10
reg.b1 = 1
reg.b2 = 3
#Print the register using its __str__ method
print reg
#Print a single field
print reg.b1
#Print the current value of the register in decimal and as a hex string
v = reg.value
print v, hex(v)
#Reset all fields to zero
reg.clear()
print reg
#Set some fields by keyword
reg.regset(b0=7, b1=3, b2=1)
print reg
#Set some fields using a dict
field_dict = {'b0':5, 'b3':0xa, 'b4':0xf}
reg.regset(**field_dict)
print reg
<强>输出强>
b0: 0xa, b1: 0x1, b2: 0x3, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
1
50397194 0x301000a
b0: 0x0, b1: 0x0, b2: 0x0, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x7, b1: 0x3, b2: 0x1, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x5, b1: 0x3, b2: 0x1, b3: 0xa, b4: 0xf, b5: 0x0, b6: 0x0
这是一个简单的Python 2 to_bytes()
函数。
def to_bytes(n, width):
b = bytearray(width)
for i in range(width-1, -1, -1):
b[i] = n & 0xff
n >>= 8
if n == 0:
break
return bytes(b)
n = 0x8182838485868788
print repr(to_bytes(n, 8))
<强>输出强>
'\x81\x82\x83\x84\x85\x86\x87\x88'
这是一个稍微修改过的版本,带有一个新方法.setvalue()
,它允许您从整数设置寄存器的值。在构造函数中调用此方法,因此您现在可以传递一个可选的整数来初始化寄存器。如果没有初始值传递给构造函数,则寄存器初始化为零,如前所述。
class BitFields(object):
""" A bit field class
fieldwidth is a tuple or list containing the
bit width of each field, from least significant
to most significant.
"""
def __init__(self, totalwidth, fieldwidths, value=0):
if sum(fieldwidths) != totalwidth:
raise ValueError, "Field width error"
self.fieldwidths = fieldwidths
self.num_fields = len(fieldwidths)
#Calculate field offsets
self.offsets = []
pos = 0
for w in fieldwidths:
self.offsets.append(pos)
pos += w
#Set up bitfield attribute names
self.field_names = ['b' + str(i) for i in range(self.num_fields)]
#Initialize
self.setvalue(value)
#Set all fields to zero
def clear(self):
for f in self.field_names:
setattr(self, f, 0)
#A generator expression of all the field values
def _all_fields(self):
return (getattr(self, f) for f in self.field_names)
def __str__(self):
return ', '.join(['%s: 0x%x' % (f, v)
for f, v in zip(self.field_names, self._all_fields())])
#Get the register value as an int
@property
def value(self):
return sum(v<<p for v, p in zip(self._all_fields(), self.offsets))
#Set field values
def regset(self, **kwargs):
for f, v in kwargs.items():
setattr(self, f, v)
#Set the register from an int
def setvalue(self, value):
for f, w in zip(self.field_names, self.fieldwidths):
#print f, w
mask = (1<<w) - 1
v = value & mask
value >>= w
setattr(self, f, v)
#Test
fields = (16, 8, 12, 12, 6, 4, 6)
reg = BitFields(64, fields)
#Set some fields by attribute
reg.b0 = 10
reg.b1 = 1
reg.b2 = 3
#Print the register using its __str__ method
print reg
#Print a single field
print reg.b1
#Print the current value of the register in decimal and as a hex string
v = reg.value
print v, hex(v)
#Reset all fields to zero
reg.clear()
print reg
#Set some fields by keyword
reg.regset(b0=7, b1=3, b2=1)
print reg
#Set some fields using a dict
field_dict = {'b0':5, 'b3':0xa, 'b4':0xf}
reg.regset(**field_dict)
print reg
#Set the register from an int or long
n = 0x111133337777ffff
reg = BitFields(64, fields, n)
print reg
v = reg.value
print v, hex(v), n == v
n = 0x123456789abcdef0
reg.setvalue(n)
print reg
v = reg.value
print v, hex(v), n == v
import random
print 'Testing .setvalue()...'
for _ in xrange(50000):
n = random.randint(0, (1<<64) - 1)
reg.setvalue(n)
v = reg.value
assert v == n, (n, v)
print 'OK'
<强>输出强>
b0: 0xa, b1: 0x1, b2: 0x3, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
1
50397194 0x301000a
b0: 0x0, b1: 0x0, b2: 0x0, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x7, b1: 0x3, b2: 0x1, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x5, b1: 0x3, b2: 0x1, b3: 0xa, b4: 0xf, b5: 0x0, b6: 0x0
b0: 0xffff, b1: 0x77, b2: 0x377, b3: 0x333, b4: 0x11, b5: 0x4, b6: 0x4
1229820469389557759 0x111133337777ffffL True
b0: 0xdef0, b1: 0xbc, b2: 0x89a, b3: 0x567, b4: 0x34, b5: 0x8, b6: 0x4
1311768467463790320 0x123456789abcdef0L True
Testing .setvalue()...
OK
答案 1 :(得分:4)
您可以创建一个能够将多个字段转换为单个int的寄存器(然后可以使用int.to_bytes
将其转换为字节字符串)。例如:
class Register:
def __init__ (self, *sizes):
self.sizes = sizes
def pack (self, *fields):
offset = 0
result = 0
for value, size in zip(fields, self.sizes):
result |= self.trimSize(value, size) << offset
offset += size
return result
def unpack (self, value):
offset = 0
fields = []
for size in self.sizes:
fields.append(self.trimSize(value, size))
value >>= size
return fields
def trimSize (self, value, size):
return value & ((1 << size) - 1)
你可以像这样使用它:
>>> r = Register(16, 8, 12, 12, 6, 4, 6)
>>> r.pack(70, 0, 0, 12, 4, 0, 0)
1126724540563526
>>> _.to_bytes(8, 'big')
b'\x00\x04\x00\xc0\x00\x00\x00F'
您还可以再次解压缩值:
>>> r.unpack(1126724540563526)
[70, 0, 0, 12, 4, 0, 0]
您现在可以创建一个新的类型,它具有您的注册字段的多个属性,并在内部使用Register
:
class Reg1:
def __init__ (self):
self.r = Register(16, 8, 12, 12, 6, 4, 6)
# default values
self.B0 = self.B1 = self.B2 = self.B3 = self.B4 = self.B5 = self.B6 = 0
def pack (self):
return self.r.pack(self.B0, self.B1, self.B2, self.B3, self.B4, self.B5, self.B6)
def unpack (self, value):
self.B0, self.B1, self.B2, self.B3, self.B4, self.B5, self.B6 = self.r.unpack(value)
像这样使用:
>>> obj = Reg1()
>>> obj.B5 = 4
>>> obj.B4 = 12
>>> obj.B0 = 70
>>> obj.pack()
75435293758455878