bits in bytes und umgekehrt...

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Code: Alles auswählen

class Foo(object):
    def status_from_byte1(self, status):
        self.flag_C = [0, 1][0 != status & 1]
        self.flag_V = [0, 1][0 != status & 2]
        self.flag_Z = [0, 1][0 != status & 4]
        self.flag_N = [0, 1][0 != status & 8]
        self.flag_I = [0, 1][0 != status & 16]
        self.flag_H = [0, 1][0 != status & 32]
        self.flag_F = [0, 1][0 != status & 64]
        self.flag_E = [0, 1][0 != status & 128]

    def status_from_byte2(self, status):
        self.flag_C = 0 if status & 1 == 0 else 1
        self.flag_V = 0 if status & 2 == 0 else 1
        self.flag_Z = 0 if status & 4 == 0 else 1
        self.flag_N = 0 if status & 8 == 0 else 1
        self.flag_I = 0 if status & 16 == 0 else 1
        self.flag_H = 0 if status & 32 == 0 else 1
        self.flag_F = 0 if status & 64 == 0 else 1
        self.flag_E = 0 if status & 128 == 0 else 1

    def status_from_byte3(self, status):
        data = tuple([int(x) for x in '{0:08b}'.format(status)])
        self.flag_E, \
        self.flag_F, \
        self.flag_H, \
        self.flag_I, \
        self.flag_N, \
        self.flag_Z, \
        self.flag_V, \
        self.flag_C = data

    def status_from_byte4(self, status):
        data = [0 if status & x == 0 else 1 for x in (128, 64, 32, 16, 8, 4, 2, 1)]
        self.flag_E, \
        self.flag_F, \
        self.flag_H, \
        self.flag_I, \
        self.flag_N, \
        self.flag_Z, \
        self.flag_V, \
        self.flag_C = data

    def status_as_byte(self):
        return self.flag_C | \
            self.flag_V << 1 | \
            self.flag_Z << 2 | \
            self.flag_N << 3 | \
            self.flag_I << 4 | \
            self.flag_H << 5 | \
            self.flag_F << 6 | \
            self.flag_E << 7

f = Foo()

for i in xrange(255):
    f.status_from_byte4(i)
    i2 = f.status_as_byte()
    assert i == i2
Hier vier varianten wie man ein status byte in einzelne 0/1 bit flags bringen kann.
Welches ist die beste? Noch weitere Vorschläge?

Ich denke status_from_byte2() oder status_from_byte4() sind meine Favoriten.
Was könnte man bei status_as_byte() machen?

(Natürlich ist & nur ein &)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Ungetestet:

Code: Alles auswählen

class Foo(object):
    FLAGS = [(n, 2**i) for i, n in enumerate('CVZNIFE')]
    FLAG_PREFIX = 'flag_'

    def __init__(self):
        for name, _ in self.FLAGS:
            setattr(self, self.FLAG_PREFIX + name, False)

    @property
    def status_byte(self):
        return sum(
            m for n, m in self.FLAGS if getattr(self, self.FLAG_PREFIX + n)
        )

    @status_byte.setter
    def status_byte(self, status):
        for name, mask in self.FLAGS:
            setattr(self, self.FLAG_PREFIX + name, bool(status & mask))
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ich würde es auch über Properties lösen, aber wenn du es in kurz willst:

Code: Alles auswählen

C, V, Z, N, I, H, F, E = (bool(status & (1 << i)) for i in xrange(8))
bzw.

Code: Alles auswählen

status = sum(value << i for (i, value) in enumerate((C, V, Z, N, I, H, F, E)))
Das Leben ist wie ein Tennisball.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

ich würde es ja umgekehrt über properties machen:

Code: Alles auswählen

def _property_bit(digit):
    def setter(self, value):
        if value:
            self.flags |= digit
        else: 
            self.flags &= ~digit 
    return property(lambda self: int(self.flags&digit!=0), setter)

class Foo(object):
    def __init__(self, flags):
        self.flags = flags
    
    flag_C = _property_bit(1)
    flag_V = _property_bit(2)
    flag_Z = _property_bit(4)
    flag_N = _property_bit(8)
    flag_I = _property_bit(16)
    flag_H = _property_bit(32)
    flag_F = _property_bit(64)
    flag_E = _property_bit(128)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Viele Wege führen ... :

Code: Alles auswählen

class Bar(object):
	# sorted flags in byte
	FLAGS = 'CVZNIHFE'
	# prototype for flag reading
	FLAG_PROTO = lambda cls, x: lambda self: (self._status >> x) & 1
	
	def __new__(cls, *arg, **kwarg):
		inst = object.__new__(cls, arg, kwarg)
		for i, c in enumerate(cls.FLAGS):
			setattr(cls, 'flag_' + c, property(inst.FLAG_PROTO(i)))
		return inst
		
	def __init__(self):
		self._status = 0
		
	@property
	def status(self):
		'''return status as byte'''
		return chr(self._status)
		
	@status.setter
	def status(self, value):
		'''set status from byte'''
		self._status = ord(value)
Edit:
Nochmal geändert - ohne lästige Referenz auf ein Exemplar, einmaliger Methodenerweiterung der Klasse und mit Bitsetter, falls Du den brauchen solltest:

Code: Alles auswählen

class Foo(object):
    FLAGS = 'CVZNIHFE'
    FLAGS_SET = False
    
    @classmethod
    def _get_flag(cls, x):
        return lambda self: (self._status >> x) & 1
        
    @classmethod
    def _set_flag(cls, x):
        def set_flag(self, value):
            if value:
                self._status |= 1 << x
            else:
                self._status &= ~(1 << x)
        return set_flag
    
    def __new__(cls, *args, **kwargs):
        if not cls.FLAGS_SET:
            for i, c in enumerate(cls.FLAGS):
                setattr(cls, 'flag_' + c,
                        property(cls._get_flag(i), cls._set_flag(i)))
            cls.FLAGS_SET = True
        return object.__new__(cls, args, kwargs)
        
    def __init__(self):
        self._status = 0
        
    @property
    def status(self):
        return self._status
        
    @status.setter
    def status(self, value):
        self._status = value & 255

if __name__ == '__main__':		
        f = Foo()
        print bin(f.status)[2:].zfill(8), f.flag_C, f.flag_E
        f.flag_C = 1
        print bin(f.status)[2:].zfill(8), f.flag_C, f.flag_E
        f.flag_E = True
        f.flag_C = False
        print bin(f.status)[2:].zfill(8), f.flag_C, f.flag_E
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Danke euch allen, muß ich mir noch genauer überlegen. Die Aktuelle Lösung ist allerdings weniger nützlich.
Denn ich muß mir den attribut namen merken um ein += 1 zu machen: https://github.com/jedie/DragonPy/blob/ ... #L714-L746

Also sowas:

Code: Alles auswählen

        if foo == bar:
            register_value = self.stack_pointer
            reg_attr_name = "stack_pointer"
...
        if bar==foo:
            ea = register_value
            setattr(self, reg_attr_name, register_value + 1)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Die vielen ``elif``-Zweige die auf den selben Wert dispatchen sehen unschön aus. Und kann es sein, dass Zeile 785 einen Fehler enthält? Ein Word als Offset lesen, dass dann aber als `signed8()` addieren sieht komisch aus.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Jepp, ein Großteil von dem `elif`-Gedöns liesse sich auch als Container-Abfrage (Tupel oder Liste) via Indexzugriff realisieren.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Einen if..elseif teil habe ich umgeformt: https://github.com/jedie/DragonPy/commi ... 95d13d07f7
Sieht in der tat viel aufgeräumter auf...

Bei den anderen ( https://github.com/jedie/DragonPy/blob/ ... #L727-L793 ) passiert aber mehr. Mit würde nur einfallen, das ganz auszulagern und methodenaufrufe per dict zu verwalten. Aber einfacher wird es dadurch auch nicht wirklich...
BlackJack hat geschrieben:kann es sein, dass Zeile 785 einen Fehler enthält? Ein Word als Offset lesen, dass dann aber als `signed8()` addieren sieht komisch aus.
Ja stimmt, das macht eigentlich keinen Sinn.

EDIT: Der code basiert auf dem von XRoar, hier: https://github.com/jedie/XRoar/blob/mas ... #L592-L652
Leider verstehe ich aber nicht alles dort...

EDIT: Ach bin ich blöd... :oops: Ich hab code vom 6309 abgeschaut, statt in 6809.c rein zu sehen :roll:

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@jens:
Ich finde den Zugriff über die Namen resp. getattr/setattr nicht gelungen. Die Register würde ich in einen Container packen. Das erleichtert den Zugriff über die Adressen und würde den Großteil der elif-Kaskaden vereinfachen, die sprechenden Namen kannst Du ja mit properties vorhalten.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Noch habe ich es nicht optimiert. Allerdings mal in einer separaten Klasse zusammengefasst und mit update Methoden versehen.
Dazu noch ein paar unittests.

Leider bekomme ich 'Half-Carry' und 'Overflow' nicht hin.

Code, weil hier & kaputt -> https://gist.github.com/jedie/6595991 (gern auch dort editieren)

Der Code basiert auf: https://github.com/jedie/XRoar/blob/mas ... .c#L60-L93

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, habe das ganze nochmal geändert. Nun sind die Flags als properties. Allerdings in einer neueren Variante. Ich hab zusätzlich noch code aus meiner cpu6809.py dort eingebaut: https://gist.github.com/jedie/6595991

diffs: https://gist.github.com/jedie/6595991/revisions

Stellen bei denen ich mir nicht sicher bin, sind mit XXX gekenntzeichnet. Bei FIXME im unittest, funktioniert anscheinend das nicht richtig :(

EDIT: habs eingebaut: https://github.com/jedie/DragonPy/commi ... 370635a110

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Zum setzten der CC flags:

Ich habe nochmal den original C code aus XRoar genommen und ihn quasi 1:1 in Python übernommen, aber ob das so richtig ist:
original:

Code: Alles auswählen

#define SET_Z(r)          ( REG_CC |= ((r) ? 0 : CC_Z) )
#define SET_N8(r)         ( REG_CC |= (r&0x80)>>4 )
#define SET_N16(r)        ( REG_CC |= (r&0x8000)>>12 )
#define SET_H(a,b,r)      ( REG_CC |= ((a^b^r)&0x10)<<1 )
#define SET_C8(r)         ( REG_CC |= (r&0x100)>>8 )
#define SET_C16(r)        ( REG_CC |= (r&0x10000)>>16 )
#define SET_V8(a,b,r)     ( REG_CC |= ((a^b^r^(r>>1))&0x80)>>6 )
#define SET_V16(a,b,r)    ( REG_CC |= ((a^b^r^(r>>1))&0x8000)>>14 )
Meins:

Code: Alles auswählen

    def set_H(self, a, b, r):
        r2 = (a ^ b ^ r) & 0x10
        self.H = 0 if r2 == 0 else 1
        log.debug("\tset_H(): set half-carry flag to %i: ($%x ^ $%x ^ $%x) & 0x10 = $%x",
            self.H, a, b, r, r2
        )

    def set_Z8(self, r):
        r2 = r & 0xff
        self.Z = 1 if r2 == 0 else 0
        log.debug("\tset_Z8(): set zero flag to %i: $%02x & 0xff = $%02x",
            self.Z, r, r2
        )

    def set_Z16(self, r):
        r2 = r & 0xffff
        self.Z = 1 if r2 == 0 else 0
        log.debug("\tset_Z16(): set zero flag to %i: $%04x & 0xffff = $%04x",
            self.Z, r, r2
        )

    def set_N8(self, r):
        r2 = r & 0x80
        self.N = 0 if r2 == 0 else 1
        log.debug("\tset_N8(): set negative flag to %i: ($%02x & 0x80) = $%02x",
            self.N, r, r2
        )

    def set_N16(self, r):
        r2 = r & 0x8000
        self.N = 0 if r2 == 0 else 1
        log.debug("\tset_N16(): set negative flag to %i: ($%04x & 0x8000) = $%04x",
            self.N, r, r2
        )

    def set_C8(self, r):
        r2 = r & 0x100
        self.C = 0 if r2 == 0 else 1
        log.debug("\tset_C8(): carry flag to %i: ($%02x & 0x100) = $%02x",
            self.C, r, r2
        )

    def set_C16(self, r):
        r2 = r & 0x10000
        self.C = 0 if r2 == 0 else 1
        log.debug("\tset_C16(): carry flag to %i: ($%04x & 0x10000) = $%04x",
            self.C, r, r2
        )

    def set_V8(self, a, b, r):
        r2 = (a ^ b ^ r ^ (r >> 1)) & 0x80
        self.V = 0 if r2 == 0 else 1
        log.debug("\tset_V8(): overflow flag to %i: (($%02x ^ $%02x ^ $%02x ^ ($%02x >> 1)) & 0x80) = $%02x",
            self.V, a, b, r, r, r2
        )

    def set_V16(self, a, b, r):
        r2 = (a ^ b ^ r ^ (r >> 1)) & 0x8000
        self.V = 0 if r2 == 0 else 1
        log.debug("\tset_V16(): overflow flag to %i: (($%04x ^ $%04x ^ $%04x ^ ($%04x >> 1)) & 0x8000) = $%04x",
            self.V, a, b, r, r, r2
        )

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ich würde die ganzen ``if``s noch vereinfachen:

Code: Alles auswählen

self.H = 0 if r2 == 0 else 1
self.H = bool(r2)

self.Z = 1 if r2 == 0 else 0
self.Z = not r2
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das hatte ich mir auch schon mal überlegt. Die Flags mir True/False zu setzten, statt 0 / 1...

Aber bevor ich "bessere" Lösungen angehe, muß erstmal das richtige rauskommen und das ganze dann mit Unittests abgesichert. Doch noch funktioniert das alles nicht so richtig :(

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@jens:
Du hast die Veroderung nicht drin, Bsp:

Code: Alles auswählen

#define SET_N8(r)         ( REG_CC |= (r&0x80)>>4 )
heisst doch, dass in REG_CC eventuell das 4. Bit auf 1 geht, aber nicht auf 0 gehen kann, wenn es vorher schon 1 war, da (nur das 4. Bit betrachtend):

Code: Alles auswählen

  REG_CC(zuvor)  |  rechts   -->  REG_CC(danach)
1     0               0                 0
2     0               1                 1
3     1               0                 1
4     1               1                 1
Dein

Code: Alles auswählen

self.N = 0 if r2 == 0 else 1
hingegen überträgt einfach das Bit der Auswertung der rechten Seite auf N. Unter der Annahme, das N das 4.Bit in REG_CC ist, sollte es heissen:

Code: Alles auswählen

if r & 0x80:
    self.N = 1
# else uninteressant, da N auf vorherigen Wert bleibt
Hab auf Deine anderen Funktionen nicht draufgeschaut.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Danke für den Hinweis!

Das hatten wir zwar schon mal, aber ich habe es zwischenzeitlich wieder geändert. :oops:

Ich hab es nun wieder mit https://github.com/jedie/DragonPy/commi ... e5a6bcac9d eingebaut. Dazu einige Instruktionen angepasst. Doch nun heißt es Fleißarbeit und alle Restlichen anpassen...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@jens:
Da Du eine ziemlich low level Emulation der CPU bis auf Registerebene machst, würde ich versuchen, möglichst viele Pythoninstruktionen an dieser Stelle zu sparen. Die Flagauswertung passiert mit jedem op-Code gleich mehrfach, da kann umständlicher Code sich zu einer schlechten Laufzeit aufsummieren.
Für N z.B. spart dies den Lookup von N, die Auswertung von N==0, das setzen von r2 und die Auswertung von r2==0:

Code: Alles auswählen

def set_N8(self, r):
    if r & 0x80:
        self.N = 1
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Danke!

Optimierungen stehen hinten an, wenn ich eine lauffähige Version inkl. Unittests habe. Dann kommen auch ganz viele log-Ausgaben weg.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, um mal weiter zu kommen, habe ich nun einen ersten richtigen Unitest für die CC Register der auch funktioniert: https://github.com/jedie/DragonPy/commi ... a332390766

Das ganze gab ich mit einem BASIC Skript gegen getestet hier: https://github.com/jedie/PyDragon32/blo ... C_ADDA.bas

Sieht im XRoar Emulator dann so aus:
Bild
Bild
Bild

Ich denke es wäre nicht schlecht, das selbe dann für SUB, INC, DEC und vielleicht noch für andere Ops zu haben.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten