Emulator in Python...

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
snafu
User
Beiträge: 6830
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Bei den ganzen Settern könnte man auch mit einer Hilfsmethode arbeiten:

Code: Alles auswählen

def set_bit_for(self, attrname, condition):
    setattr(self, attrname, 1 if condition else 0)
Und dann entsprechend:

Code: Alles auswählen

def set_Z8(self, r):
    self.set_bit_for('Z', r & 0xff)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@jens:
Du kannst die Macros doch relativ einfach übernehmen, aufpassen musst Du halt bei der festen Typenbreite und dem Vorzeichen, ungetestet:

Code: Alles auswählen

class Stub(object):
    CC_E = 0x80
    CC_F = 0x40
    CC_H = 0x20
    CC_I = 0x10
    CC_N = 0x08
    CC_Z = 0x04
    CC_V = 0x02
    CC_C = 0x01
    def __init__(self):
        self.cc = 0
    def set_z(self, r):
        self.cc |= 0 if r else self.CC_Z
    def set_n8(self, r):
        self.cc |= (r & 0x80) >> 4
    def set_nz8(self, r):
        self.set_n8(r)
        self.set_z(r & 0xff)
    def clr_nzv(self):
        self.cc &= ~(self.CC_N | self.CC_Z | self.CC_V)

    def op_dec(self, _in):
        out = ((_in & 255) - 1) & 255 # correction for 8 bit unsignedness
        self.clr_nzv()
        self.set_nz8(out)
        if out == 0x7f:
            self.cc |= self.CC_V
        return out
Edit: Der '&'-Bug macht es ziemlich unleserlich.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Naja, in Python gibt es ja kein signed / unsigned ... Wobei ich für signed das habe:

Code: Alles auswählen

def signed8(x):
    """ convert to signed 8-bit """
    if x > 0x7f: # 0x7f == 2**7-1 == 127
        x = x - 0x100 # 0x100 == 2**8 == 256
    return x


def signed16(x):
    """ convert to signed 16-bit """
    if x > 0x7fff: # 0x7fff == 2**15-1 == 32767
        x = x - 0x10000 # 0x100 == 2**16 == 65536
    return x
Nur beim C code ist dieses:

Code: Alles auswählen

unsigned out = in - 1
Was macht das unsigned hier?
Macht es nicht die Umsetzung (bei 8bit) von signed -128 bis 127 zu unsigned: 0 bis 255 ?

Was ich auch nicht weiß, sind beim 6809 grundsätzlich alle Arithmetik Instruktionen signed?

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

Das 'unsigned' ist ein Shortcut für 'unsigned int'. Da der Parameter als auch der Rückgabewert zu uint8_t werden, habe ich die 8Bit-Korrektur in der Funktion drin, für den Parameter könnte man das aber auch ausserhalb sicherstellen.

Code: Alles auswählen

unsigned out = in - 1
Hier wird 0 - 1 zu 255 und nicht -1.

Wie die CPU mit Vorzeichen umgeht - kA. Die CPU setzt doch sicherlich auf das 2er Komplement, dann sollten die Grundrechenarten sehr einfach sein. Das MSB kannst Du ja später für die signed-Konvertierung auslesen.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@jens:
Was mir noch aufgefallen ist - in Deiner Makroübertragung von `set_z` und `set_n8` fehlt das ODER mit dem derzeitigen Flagwert, heisst, ist eines der Flags auf 1, bleibts 1. Keine Ahnung, obs 'ne Rolle spielt.
In 'DEC' sehe ich nicht, wo Du N,Z und V auf 0 setzt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

jerch hat geschrieben:Was mir noch aufgefallen ist - in Deiner Makroübertragung von `set_z` und `set_n8` fehlt das ODER mit dem derzeitigen Flagwert, heisst, ist eines der Flags auf 1, bleibts 1.
Danke für den Hinweis. Das könnte man doch auch so lösen:

Code: Alles auswählen

    def set_V8(self, a, b, r):
        if self.V == 0:
            self.V = 1 if (a ^ b ^ r ^ (r >> 1)) & 0x80 else 0
Oder besser:

Code: Alles auswählen

    def set_V8(self, a, b, r):
        if self.V == 0 and (a ^ b ^ r ^ (r >> 1)) & 0x80:
            self.V = 1
Aber ich weiß auch nicht, ob Overflow/Negativ usw. solange auf 1 bleiben, bis man es explizit zurück setzt.

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:

Mir ist aufgefallen, das ich wohl grundlegenden Fehler gemacht habe: Bei mir ist z.Z. ea nicht immer die "effective Address" sondern auch mal direkt der Speicherinhalt. Aber je nach OpCode braucht mal mal das eine oder mal das andere oder beide.

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:

jens hat geschrieben:Mir ist aufgefallen, das ich wohl grundlegenden Fehler gemacht habe: Bei mir ist z.Z. ea nicht immer die "effective Address" sondern auch mal direkt der Speicherinhalt. Aber je nach OpCode braucht mal mal das eine oder mal das andere oder beide.
Das habe ich mittlerweile behoben. Jede Instruction-Methode bekomme nun mit ea die effektive Adresse und je nach oppcode mit m den Speicherinhalt.

Um bei den CC Registern weiter zu kommen, muss ich erst mal verstehen, wie beim 6809 das genau mit den Flags wie Overflow und Carry funktioniert. Solange ich da herrum rätsele komm ich nicht weiter. Ich brauche halt passende unittests. Dann kann man mal weiter sehen, ob man das evtl. besser implementieren kann.

Damit ich ein besseres Verständnis habe, suche ich nach wegen, auf dem 8-bit-Rechner direkt Test-Programme laufen zu lassen.
Einen weg um ein kleines Assemblerprogramm auf dem Rechner auszuführen habe ich gefunden:

Code: Alles auswählen

    10 ' MACHINE CODE LOADER
    20 READ LA  ' LA = LOAD ADDRESS (START OF PROGRAM)
    30 READ EA  ' EA = ADDRESS OF FIRST INSTRUCTION
    40 PA = EA  'TO BE EXECUTED
    50 READ HB$ ' HEX CONSTANTS
    60 IF HB$="END" THEN 100
    70 POKE PA,VAL("&H"+HB$) ' POKE VALUE INTO MEMORY
    80 PA = PA + 1  ' INCREMENT ADDRESS
    90 GOTO 50
    100 PRINT "AUTORUN WITH EXEC";EA;":"
    105 PRINT "-------------------------------"
    110 EXEC EA ' AUTORUN
    114 PRINT
    115 PRINT "-------------------------------"
    120 PRINT "END ADDRESS:"; PA-1
    130 PRINT "RUN AGAIN WITH EXEC";EA
    149 ' LOAD ADDRESS:
    150 DATA 20001
    159 ' EXECUTE ADDRESS:
    160 DATA 20001
    165 ' MACHINE CODE IN HEX
    170 DATA 34,12      ' PSHS A,X       ; Save registers A, X
    180 DATA CC,58,58   ' LDD $5858      ; $5858 == 22616
    190 DATA BD,95,7A   ' JSR 38266      ; outputs the decimal value of D register
    260 DATA 35,92      ' PULS A,X,PC    ; Restore and return
    270 DATA END
In diesem Fall wird das D Register mit dem Wert 22616 gefüllt und diese Zahl erscheint dann auf dem Bildschirm.

Den Assembler Code habe ich handgeschrieben. Was viel zu mühsam ist.

Denke mal ich sollte mir mal einen richtigen 6809 Assembler ansehen und einen Debugger auf dem Rechner.

Schön wäre es, wenn man einen 6809 Simulator hätte. Es gibt auch welche, aber hab noch keinen Funktionierenden gefunden.

EDIT: Von der FernUniversität in Hagen gibt es einen 6809-Emulator für Windows zum download hier: http://www.fernuni-hagen.de/pwti/lehre/ ... rial.shtml
Läßt sich auch starten. Muß ich mir mal genauer ansehen.

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

@jens:
Ist denn hier --> http://koti.mbnet.fi/~atjs/mc6809/ nichts Brauchbares dabei?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

jerch hat geschrieben:@jens:
Ist denn hier --> http://koti.mbnet.fi/~atjs/mc6809/ nichts Brauchbares dabei?
Kenne ich. Hab mir schon einiges angesehen. Aber richtig brauchbares habe ich noch nicht gefunden. Die meisten Sachen sind zimlich alt. Teilweise noch für DOS.
Wirklich benutztbares habe ich bisher nicht gefunden.

Von der UNI-Kassel gibt es bei http://cms.uni-kassel.de/unicms/index.php?id=39261 einen Download Link Programming Tools (Compiler, Editor und Simulator)
Der 6809 Simulator ist ein DOS Programm. Funktioniert auch in DosBOX.
Aber wirklich benutzbar im Sinne von Benutzerfreundlich finde ich das jetzt nicht ;)

Interessant ist der 6809-Emulator für Windows von der FernUNI Hagen, download hier: http://www.fernuni-hagen.de/pwti/lehre/ ... rial.shtml

btw. Ach, was Grundlegendes zum Thema Wie man einen Emulator programmiert hab ich bei http://fms.komkon.org/EMUL8/HOWTO.html gefunden. Interessant, wenn man den ganzen "wir in C optimieren" Teil weg läßt.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 6830
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

jens hat geschrieben:was Grundlegendes zum Thema Wie man einen Emulator programmiert hab ich bei http://fms.komkon.org/EMUL8/HOWTO.html gefunden. Interessant, wenn man den ganzen "wir in C optimieren" Teil weg läßt.
Der Absatz "How do I optimize C code?" ist nicht völlig auf systemnahe Sprachen wie C beschränkt, falls das so gemeint war. Wenn man zum Beispiel oft Berechnungen mit Vielfachen von 2 hat, dann kann man die durchaus zu Bitoperationen umschreiben. Das spart auch in CPython ein bißchen Ausführungszeit ein. Und der erste dort genannte Punkt, der die Nutzung eines Profilers zwecks Codeanalyse vorschlägt, ist ebenfalls in Python umsetzbar. Klar, die Verwendung spezieller C-Schlüsselworte trifft natürlich nur auf C zu.
BlackJack

@snafu: Das mit den Schiebeoperationen ist in C in zweifacher Hinsicht fragwürdig. 1. gibt es den Geschwindigkeitsunterschied auf modernen CPU nicht mehr generell und 2. wenn es ihn gibt, dann macht das schon der Compiler für uns. Da muss ich gerade an diesen Tweet denken:
@jonshiring hat geschrieben:In C++, everytime someone writes ">> 3" instead of "/ 8", I bet the compiler is like, "OH DAMN! I would have never thought of that!"
Wenn man einen Emulator programmiert, beziehungsweise wenn jemand den Quelltext für einen Emulator liest, kann man davon ausgehen, dass die Bitoperationen sitzen und verstanden werden, also kann man da auch schieben statt Punktrechnungen zu machen, aber in „normalen” Programmen würde ich mir so etwas verkneifen. Es geht auf Kosten der einfache(re)n Verständlichkeit und bringt bei modernen Maschinen/Compilern keinen Vorteil.

Wenn man mich dabei erwischt das zu machen, dann entweder aus Gewohnheit, weil man das früher so gemacht hat, als die Compiler noch dumm waren und der Unterschied in der Laufzeit dramatisch, oder weil der Code auch für den C64 kompiliert werden soll, wo der Compiler nicht ganz so schlau ist und der Unterschied in der Laufzeit echt dramatisch ist. :-)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

BlackJack hat geschrieben:Wenn man einen Emulator programmiert, beziehungsweise wenn jemand den Quelltext für einen Emulator liest, kann man davon ausgehen, dass die Bitoperationen sitzen und verstanden werden, also kann man da auch schieben statt Punktrechnungen zu machen, aber in „normalen” Programmen würde ich mir so etwas verkneifen.
Das ist bei mir nicht wirklich der Fall. Deswegen möchte ich das ehr vermeiden :oops:

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

jens hat geschrieben:
BlackJack hat geschrieben:Wenn man einen Emulator programmiert, beziehungsweise wenn jemand den Quelltext für einen Emulator liest, kann man davon ausgehen, dass die Bitoperationen sitzen und verstanden werden, also kann man da auch schieben statt Punktrechnungen zu machen, aber in „normalen” Programmen würde ich mir so etwas verkneifen.
Das ist bei mir nicht wirklich der Fall. Deswegen möchte ich das ehr vermeiden :oops:
Das ist ja fast wie der Wunsch Romane zu verfassen, aber bitte ohne Wörter zu benutzen. Ich hab dir schon vor einigen Wochen nahegelegt, dass du dich damit beschäftigen solltest. Das wären vielleicht ein oder zwei Abende Arbeit gewesen und hätte dir wahrscheinlich schon mehr Zeit gespart. Manchmal braucht es eben ein wenig Wissen und Bit-Operationen sind nun alles andere als Magie.
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6830
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@BlackJack: Ich sprach bewusst von CPython, dessen Compiler ja nicht allzu bekannt für Optimierungen am Quelltext ist. Nach meiner Erfahrung ist ein Shift in CPython circa 20% schneller als eine äquivalente Division. In PyPy sind beide annähernd gleich schnell, sodass man durch's Shiften natürlich auch nichts verlieren würde. Jython verhält sich in diesem Fall von der Performance her ähnlich wie CPython.

Ich habe das mit diesem Code geprüft:

Code: Alles auswählen

import time

def measure_division(number, divisor, num_loops=10**7):
    before = time.time()
    for _ in xrange(num_loops):
        number // divisor
    after = time.time()
    return after - before

def measure_right_shift(number, num_bits, num_loops=10**7):
    before = time.time()
    for _ in xrange(num_loops):
        number >> num_bits
    after = time.time()
    return after - before
Und dann in der CPython-Shell:

Code: Alles auswählen

>>> import shiftperf
>>> 0x150 // 8
42
>>> 0x150 >> 3
42
>>> shiftperf.measure_right_shift(0x150, 3) / shiftperf.measure_division(0x150, 8)
0.7201051550896614
>>> shiftperf.measure_right_shift(0x150, 3) / shiftperf.measure_division(0x150, 8)
0.7211107141874019
>>> shiftperf.measure_right_shift(0x150, 3) / shiftperf.measure_division(0x150, 8)
0.7240534444336117
Jetzt bei der Demo-Session war der Unterschied, wie man sieht, sogar noch etwas deutlicher.

Ich will ja keinesfalls sagen, dass jede infrage kommende Operation mit einer Zweierpotenz durch eine Shift-Operation ersetzt werden soll. Wenn die Berechnung allerdings der Flaschenhals des Programms ist, dann kann Shiften schon viel helfen. Ich wollt's auch eigentlich nur mal erwähnt haben. Ist nichts, was jetzt durch eine ellenlange Offtopic-Diskussion den Thread kaputt machen soll...
BlackJack

@snafu: Das Ergebnis kann ich nicht nachvollziehen, bei mir sind die beiden Operationen annähernd gleich schnell unter CPython 2.7:

Code: Alles auswählen

In [6]: %timeit 0x150 // 8
10000000 loops, best of 3: 35 ns per loop

In [7]: %timeit 0x150 >> 3
10000000 loops, best of 3: 35.1 ns per loop
Unter welchem Betriebssystem hast Du das denn gemacht? Eventuell die falsche Funktion zum Messen verwendet? Einer der Gründe warum man das `timeit`-Modul verwenden sollte, insbesondere wenn es um so kleine Laufzeiten geht.
Benutzeravatar
snafu
User
Beiträge: 6830
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@BlackJack: Du hast Recht. Wenn ich es, so wie du, mittels `%timeit` in IPython laufen lasse, dann sind die Ergebnisse annähernd gleich. Danke für die Richtigstellung.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Optimierungen stehen bei mir eh erstmal hinten an. Wobei ich aus Perfomance Gründen auf die ganzen Bit Operationen, die z.B. XRoar macht verzichte und auf ein dict zurück greife.
Ist ein wenig das was bei http://fms.komkon.org/EMUL8/HOWTO.html#LABH auch erwähnt wird.

Ich bin auch wieder ein Stückchen weiter. Nun habe ich Zugriffe auf andere Teile des Rechners, wie PIA/SAM, die ich irgendwie implementieren muß. Angefangen habe ich mit einigen "Dummy-Rückmeldungen", siehe: https://github.com/jedie/DragonPy/blob/ ... #L346-L402

Zum Grundlegenden Aufbau gibt es einen guten Überblick hier: http://www.6809.org.uk/dragon/hardware.shtml

Ich habe auch gemerkt das der Test-Driven-Ansatz auch in diesem Projekt eine gute Idee ist. Mit ein paar Unittests ( https://github.com/jedie/DragonPy/blob/ ... cpu6809.py ) konnte ich einige Fehler eleminieren.
Ich bräuchte mehr davon ;)

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:

Wieder einiges implementiert: https://github.com/jedie/DragonPy/compa ... ...21a9544

Neben einigen neuen Ops, habe ich nun auch den Stack implementiert.

Dabei Frage ich mich allerdings ob es nicht "harte" Grenzen beim PULL/PUSH gibt?

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

@jens: Nach einem Blick in die Befehlssatzübersicht sind die beiden Stapelzeiger 16-Bit-Register. Also kann man damit die vollen 64 KiB adressieren. Bei einem Überlauf gibt es bei Prozessoren in der Regel entweder einen Interrupt oder es ”wrappt” einfach, also wenn man über die 64 KiB hinaus geht, fängt es wieder bei 0 an und wenn man unter 0 geht, kommt man wieder ganz oben im Speicher an. Bei so einem verhältnismässig einfachen Prozessor würde ich auf das letztere tippen.

Komisch das sich die Geräte nicht so gut verkauft haben, denn man sagt doch immer „SEX sells” und der Maschinenbefehlssatz hat einen SEX-Befehl. ;-)
Antworten