Seite 1 von 2

bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 13:15
von jens

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 &)

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 13:33
von 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))

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 13:38
von EyDu
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)))

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 13:45
von Sirius3
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)

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 15:05
von jerch
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

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 17:02
von jens
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)

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 17:20
von 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.

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 19:15
von snafu
Jepp, ein Großteil von dem `elif`-Gedöns liesse sich auch als Container-Abfrage (Tupel oder Liste) via Indexzugriff realisieren.

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 21:17
von jens
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:

Re: bits in bytes und umgekehrt...

Verfasst: Montag 16. September 2013, 21:36
von jerch
@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.

Re: bits in bytes und umgekehrt...

Verfasst: Dienstag 17. September 2013, 16:36
von jens
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

Re: bits in bytes und umgekehrt...

Verfasst: Mittwoch 18. September 2013, 22:09
von jens
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

Re: bits in bytes und umgekehrt...

Verfasst: Dienstag 22. Oktober 2013, 09:48
von jens
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
        )

Re: bits in bytes und umgekehrt...

Verfasst: Dienstag 22. Oktober 2013, 09:55
von EyDu
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

Re: bits in bytes und umgekehrt...

Verfasst: Dienstag 22. Oktober 2013, 10:29
von jens
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 :(

Re: bits in bytes und umgekehrt...

Verfasst: Dienstag 22. Oktober 2013, 21:41
von jerch
@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.

Re: bits in bytes und umgekehrt...

Verfasst: Mittwoch 23. Oktober 2013, 08:37
von jens
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...

Re: bits in bytes und umgekehrt...

Verfasst: Mittwoch 23. Oktober 2013, 09:10
von jerch
@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

Re: bits in bytes und umgekehrt...

Verfasst: Donnerstag 24. Oktober 2013, 08:27
von jens
Danke!

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

Re: bits in bytes und umgekehrt...

Verfasst: Donnerstag 24. Oktober 2013, 10:32
von jens
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.