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
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich hab wieder mit --display_cycle nachgesehen, was dabei rum kommt.

Enttäuschend :(

Die aktuelle multiprocess Lösung:

CPython liegt bei ~115.000 CPU cycles/sec. mit Tk GUI und ~170.000 bei einfacher console.
PyPy legt bei Tk GUI mit nur ~120.000 los und kommt auch nur bis ~150.000 CPU cycles/sec.
Bei einfacher Console fängt PyPy bei ~225.000 an und kommt auf ~310.000 CPU cycles/sec.

Also immer noch weit weg von den 895.000 der echten CPU...

Wenn ich "console_6809_test.py" teste, die ohne multiprocess arbeitet, also all Threads in einem Prozess, sieht es so aus:
CPython ~450.000 und PyPy fängt bei ~5.400.000 an und kommt bis ~7.500.000 CPU cycles/sec.

Offensichtlich bremst die Interprocess Kommunikation um einiges aus.

Ich hatte multiprocessing.JoinableQueue() genutzt und mit .task_done() und .join() gearbeitet.
Da es aber eh nur zwei Processe sind, habe ich auf multiprocessing.Queue() umgestellt: https://github.com/jedie/DragonPy/commi ... 7bdeedff05

CPython mit Tk GUI: ~180.000 (statt ~115.000)
PyPy mit Tk GUI: ~215.000 und geht auf ~250.000 CPU cycles/sec.


Frage mir allerdings, ob multiprocess sich überhaupt lohnt, oder ich besser nur auf Threads setzten sollte.

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

Ob sich Multiprocessing lohnt, das hängt stark vom Problem ab. Um sinnvoll eingesetzt zu werden, müssen die Prozesse gut getrennt sein und genügend Aufgaben zum Abarbeiten haben. Wenn du ständig Nachrichten zwischen den Prozessen austauscht, synchronisierst und wartest, dann ist der Overhead schnell so groß, dass alles ausgebremst wird.
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So... Bin gerade mächtig stolz... Denn mit https://github.com/jedie/DragonPy/commi ... b54296fc15 sehe ich nun:
````hCi`qyxr`DRAGON`DATA`LTD````````qvK`BASIC`INTERPRETER`qnp```````hCi`qyxr`BY`MICROSOFT```````````````````````````````````````````OK`
Ist nicht alles richtig, weil es quasi nur ein char = chr(byte) ist und die Kiste halt nicht wirklich ASCII liefert. Aber die Informationen, wie die ASCII-Tabelle ist, habe ich aber schon vor langer Zeit eingesammelt: http://archive.worldofdragon.org/index. ... le=CharMap bzw. https://gist.github.com/jedie/6975555 :lol:

Von den drei wichtigen Chips: SAM, PIA und VDG habe ich im Prinzip nur Dummy Funktionien Implementiert sie nichts machen. Hoffe mehr brauche ich auch nicht um im Text Modus mit dem BASIC Interpreter zu spielen. Das war ja eigentlich mein Ursprüngliches Ziel.

Nun muß ich sehen, wie ist das Display realisiere. Möchte gern genau die Umsetzung, wie das Original machen. Also ein Tkinter text-Widget zu füllen wäre doof. Ich brauche ein Starres Raster bei der ich die Zeichen gezielt setzten kann.
Evtl. also wirklich pygame nutzten?!?

EDIT: So, noch ein wenig gehackt und ta ta taaaa:
(C) 1982 DRAGON DATA LTD
16K BASIC INTERPRETER 1.0
(C) 1982 BY MICROSOFT
OK

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:

Nachdem die Eingabe von Zeichen funktioniert (wesentlich komplizierter als eine simple Serielle Schnittstelle) kann man das Dragon 32 / 64 ROM nun im Text-Modus nutzten. Sieht dann so aus:

Bild



Ist mit PyGame gemacht. Man sieht auch den Cursor blinken und Farben gehen auch:

Bild



Was noch nicht geht, ist aktuelle "Kleinbuchstaben" (Der Dragon kennt zwar eigentlich keine richtige Kleinschreibung, aber die Buchstaben werden invertiert dargestellt...)

Auch das Scrollen macht z.Z. Probleme. Anscheinend wird vom ROM nur jede zweite Zeile bewegt.... Wobei mir gerade beim schreiben spontan einfällt: Vielleicht wird dazu statt 1 Byte ein 16-bit Word genutzt, ich habe z.Z. allerdings nur 1-Byte lesen/schreiben implementiert!

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:Auch das Scrollen macht z.Z. Probleme. Anscheinend wird vom ROM nur jede zweite Zeile bewegt.... Wobei mir gerade beim schreiben spontan einfällt: Vielleicht wird dazu statt 1 Byte ein 16-bit Word genutzt, ich habe z.Z. allerdings nur 1-Byte lesen/schreiben implementiert!
An der nicht richtigen Trennung von byte/word anfragen lag ist. Ist behoben...

Nun überlege ich, ob ich das ganze multiprocessing in die Tonne hauen soll oder nicht. :K

Zumindest die jetzige Aufteilung macht keinen Sinn:

Code: Alles auswählen

 process_main.py                                     process_sub.py          
+-------------------------+                         +-----------------------+
|                         |                         |                       |
| BusCommunicationThread<--->multiprocessing.Queue<--------->Memory         |
|        +    ^           |                         |        +    ^         |
|        |    |           |                         |        |    |         |
|        |    |           |                         |        |    |         |
|        |    |           |                         |        |    |         |
|        v    +           |                         |        v    +         |
|       periphery         |                         |    6809 CPU Thread    |
|         +  ^            |                         |       +      ^        |
|         |  |            |                         |       |      |        |
|         |  |            |                         |       |      |        |
|         |  |            |                         |       v      +        |
|         v  +            |                         |  http control server  |
|     GUI mainloop        |                         |                       |
|                         |                         |                       |
+-------------------------+                         +-----------------------+
Über die verbindenden multiprocessing.Queue gehen einfach viel zu viele Daten.

Ich überlege nun, multiprocessing durch threads zu ersetzten und/oder anders zu Trennen.
So oder so, muß ich die GUI vom Rest trennen, oder? Ansonsten weiß ich nicht, wie ich den Tkinter mainloop und den CPU loop gleichzeitig aufrufen kann.

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:

Geht ganz gut vorran, ohne PyGame, nur mit Tk. Dabei gibt es nun die Original Schrift :D

Bild

und dieses Listing läuft auch so wie es soll:

Bild

Die cycles/sec Werte im Screenshot sind nicht ganz richtig. CPython kommt so auf ~400.000 und PyPy rennt recht unterschiedlich.

EDIT: Bilder ausgetauscht...

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

Nur eine Kleinigkeit: In den Titeln der Fenster sind die Anzahl der Zeilen und Spalten vertauscht.
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ups :oops:

Danke fürs melden ;)

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:

Mit dem multiprocessing bin ich noch nicht ganz durch... Ich über lege ob ich bei der alten Implementierung Fehler gemacht habe, bzw. Ob es nicht besser geht...

Vielleicht könnte ich die GUI im hauptprosess asynchron mit dem CPU unterprozess koppeln.
D.h. die GUI soll nicht die CPU 'stoppen' und sagen hier ist eine neue Tastatureingabe... Und die CPU sollte nicht warten, wenn ein neues Zeichen auf dem Bildschirm erscheinen soll...

Evtl. Sollte die interprocess Kommunikation jeweils in einem seperaten thread laufen?!?

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, hab das ganze nochmal reichlich umgebaut und aufgeräumt.

Im Prinzip sieht es grob nun so aus:

Code: Alles auswählen

    Main Thread                                     Sub Thread      
+------------------+                         +---------------------+
|                  |                         |                     |
| +-------------+  |  CPU cycles/sec queue   |                     |
| |            <------------------------------------+6809 CPU      |
| |             |  |                         |       +     ^       |
| |     GUI     |  |                         |       |     |       |
| |             |  | Display RAM write queue |    +--v-----+--+    |
| |  .--------------------------------------------+   Memory  |    |
| |  |          |  |                         |    +--+-----^--+    |
| |  |          |  |                         |       |     |       |
| |  |          |  |                         | +-----v-----+-----+ |
| |  |          |  |                         | |    Periphery    | |
| |  |          |  |     Keyboard queue      | |   MC6883 SAM    | |
| |  |          +--------------------------------->MC6821 PIA    | |
| |  |          |  |                         | |                 | |
| +--+-----^----+  |                         | |                 | |
|    |     |       |                         | +-----------------+ |
|    |     |       |                         |                     |
| +--v-----+----+  |                         |                     |
| |             |  |                         |                     |
| |   Display   |  |                         |                     |
| |             |  |                         |                     |
| +-------------+  |                         |                     |
+------------------+                         +---------------------+
[/size]

Ich habe also im prinzip zwei Threads. Die Daten zwischen den Thread wird mit Queue.Queue() ausgetauscht, wobei neue Daten immer mit .put_nowait() abgesetzt werden. Denn ein neues Zeichen auf dem Bildschirm muß ja nicht "in Echtzeit" erscheinen. Die CPU kann ja schon mal weiter machen ;)

Das ganze sollte sich relativ schnell auch mit multiprocessing betreiben lassen. Eigentlich dürfte das aber nur was bringen, wenn die GUI viel Rechenzeit konsumiert, die dann dem CPU Thread fehlt. Aber ich glaube das ist nicht der Fall.

EDIT: btw. das Emulieren eines TRS-80 Color Computer kurz CoCo funktioniert nun auch ;) Man kann nun auch eingaben machen...

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

``Queue.put_nowait`` macht nicht das was du vermutest! Wenn ein anderer Thread gerade auf die Warteschlange zugreift und du ein put_nowait machst, dann wird das Element *nicht* der Warteschlange hinzugefügt. Das nowait bedeutet *nicht*, dass das Element ohne Warten der Warteschlange hinzugefügt wird, sondern dass bei blockierter Warteschlange das Element nicht eingefügt wird und dir stattdessen eine Exception um die Ohren fliegt.

Wenn du nicht-blockierendes Warten haben willst, bzw. höchstens so lange warten willst wie das Einfügen oder Rausholen eines Elements dauert, dann musst du eine weitere Indirektstufe über einen Thread einführen:

Code: Alles auswählen

while True:
    outqueue.put(inqueue.get())
Der Thread, welcher outqueue-Elemente entgegennimmt, kann dann beliebig lange auf den Elementen rumarbeiten.
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Oh! Wichtiger Hinweis, das wußte ich wirklich nicht :oops:

In meinem Fall sind das allerdings auch nur zwei Threads. Der Haupt-Thread und der Neben-Thread...

Ich Frage mich, ob ich in dieser "nur zwei Threads" Konstellation, überhaupt mit "warten" rechnen muß...
Auf die schnelle werde ich erstmal alle put_nowait durch ein put() ersetzten.

Ob das ganze mit dem PyPy Problem in Relation ist? -> http://www.python-forum.de/viewtopic.php?f=1&t=34472

EDIT: In der Doku: https://docs.python.org/2.7/library/que ... .Queue.put steht dazu eigentlich nichts. Also das "eingaben" verloren gehen können.
btw. Frage mich ob ich nicht auch besser Queue.LifoQueue() nutzten sollte. Die Reihenfolge sollte natürlich beibehalten werden. Bei allen Tests bisher war das aber auch der Fall bei mir. Vielleicht wird das erst relevant, wenn mehrere Threads gleichzeitig drauf zugreifen?

EDIT2: Ich hab nun .put() für Display und Eingaben verwendet: https://github.com/jedie/DragonPy/commi ... 41401065c8

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

So, ich habe mir den Code noch einmal angeschaut, ein no_wait-Aufruf sollte beim Einfügen tatsächlich funktionieren, so lange noch genügend Slots verfügbar sind. In wie weit blockiert wird, das hängt dann von der deque-Implementierung ab.

Code: Alles auswählen

    def put(self, item, block=True, timeout=None):
        '''Put an item into the queue.

        If optional args 'block' is true and 'timeout' is None (the default),
        block if necessary until a free slot is available. If 'timeout' is
        a non-negative number, it blocks at most 'timeout' seconds and raises
        the Full exception if no free slot was available within that time.
        Otherwise ('block' is false), put an item on the queue if a free slot
        is immediately available, else raise the Full exception ('timeout'
        is ignored in that case).
        '''
        with self.not_full:
            if self.maxsize > 0:
                if not block:
                    if self._qsize() >= self.maxsize:
                        raise Full
                elif timeout is None:
                    while self._qsize() >= self.maxsize:
                        self.not_full.wait()
                elif timeout < 0:
                    raise ValueError("'timeout' must be a non-negative number")
                else:
                    endtime = time() + timeout
                    while self._qsize() >= self.maxsize:
                        remaining = endtime - time()
                        if remaining <= 0.0:
                            raise Full
                        self.not_full.wait(remaining)
            self._put(item)
            self.unfinished_tasks += 1
            self.not_empty.notify()

Code: Alles auswählen

    # Put a new item in the queue
    def _put(self, item):
        self.queue.append(item)
Ich finde es ein etwas unerwartetes Verhalten, aber OK.
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

OK, also man kann sagen, das nie Eingaben verlohren gehen. Es ist nur ein Unterschied ob blockiert wird oder ein raise Full gemacht wird, wenn maxsize erreicht ist. oder?
jens hat geschrieben:Frage mich ob ich nicht auch besser Queue.LifoQueue() nutzten sollte.
Das war natürlich blödsinn: Habe Fifo gelesen und nicht Lifo... :oops:

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

Wenn ich nichts übersehen habe, was mir natürlich nie passiert :oops:, dann sollte das so sein. Ja.
jens hat geschrieben:Das war natürlich blödsinn: Habe Fifo gelesen und nicht Lifo... :oops:
Hehe, ein Fifo gäbe aber bestimmt sehr interessante Effekte. Aus den Effekten auf die Fehlerursache zu schließen wäre bestimmt eine "spannende" Sache :shock:
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wobei Lifo hab ich nun auch im Einsatz: der CPU status, dabei interessiert ja eigentlich nur der neuste Eintrag... :-)

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:

Also ich glaube ich komme so nicht weiter...

Habe immer wieder einen Crash, ob nun CPython oder PyPy. Das Grundproblem sind wohl threads und TKinter (Wobei das wohl auch mit anderen Toolkits so wäre, vermute ich mal...)

Dann ist generell die Frage, ob Threads überhaupt Sinn machen. Denn durch den Global Interpreter Lock ("GIL") kann ja eh nur wirklich ein Thread laufen. Von daher kann ein Thread dennoch die GUI lähmen...

Vielleicht macht wegen des GIL multiprocessing doch Sinn?!?

Also doch fast so wie es jetzt ist, blos das "Main Thread" zum "Main Process" und "Sub Thread" zu "Sub Process" wird:
jens hat geschrieben:

Code: Alles auswählen

    Main Thread                                     Sub Thread      
+------------------+                         +---------------------+
|                  |                         |                     |
| +-------------+  |  CPU cycles/sec queue   |                     |
| |            <------------------------------------+6809 CPU      |
| |             |  |                         |       +     ^       |
| |     GUI     |  |                         |       |     |       |
| |             |  | Display RAM write queue |    +--v-----+--+    |
| |  .--------------------------------------------+   Memory  |    |
| |  |          |  |                         |    +--+-----^--+    |
| |  |          |  |                         |       |     |       |
| |  |          |  |                         | +-----v-----+-----+ |
| |  |          |  |                         | |    Periphery    | |
| |  |          |  |     Keyboard queue      | |   MC6883 SAM    | |
| |  |          +--------------------------------->MC6821 PIA    | |
| |  |          |  |                         | |                 | |
| +--+-----^----+  |                         | |                 | |
|    |     |       |                         | +-----------------+ |
|    |     |       |                         |                     |
| +--v-----+----+  |                         |                     |
| |             |  |                         |                     |
| |   Display   |  |                         |                     |
| |             |  |                         |                     |
| +-------------+  |                         |                     |
+------------------+                         +---------------------+
[/size]
Doch dann bremst wieder die Interprocess-Kommunikation aus?!?
Wie kann man das umgehen?

Wird es was bringen alle multiprocess.queue "zwischen zu puffern" ?
Also evtl.:

Code: Alles auswählen

   Main Process                                      Sub Process       
+------------------+                              +-------------------+
|                  |                              |                   |
|   Queue.Queue <-----> multiprocessing.Queue <-------> Queue.Queue   |
|                  |                              |                   |
+------------------+                              +-------------------+
Der Austausch multiprocessing.Queue <--> Queue.Queue könnte dann evtl. jeweils in separaten Threads erfolgen?

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:Habe immer wieder einen Crash, ob nun CPython oder PyPy. Das Grundproblem sind wohl threads und TKinter (Wobei das wohl auch mit anderen Toolkits so wäre, vermute ich mal...)
Das legt die Vermutung nahe, dass du aus dem Thread auf die GUI zugreifst.
jens hat geschrieben:Dann ist generell die Frage, ob Threads überhaupt Sinn machen. Denn durch den Global Interpreter Lock ("GIL") kann ja eh nur wirklich ein Thread laufen.
Jein. Wenn die parallelisierten Threads nur Berechnungen durchführen, dann macht dir der GIL einen Strich durch die Rechnung. Threads lohnen sich aber dann, wenn ein Thread Berechnungen durchführt und ein anderer irgendwo IO-Aufgaben durchführt. Daten von der Festplatte lesen, Daten über das Netzwer verschicken, etc. In deinem Fall kommen die letzten Aufgaben wohl eher selten vor. Wenn du allerdings pseude-parallele Berechnungen brauchst, dann wirst du um irgendeinen Threadingmechanismus nicht herumkommen. Egal ob nun Threads, Prozesse oder Pseude-Threads mit Generatoren.
jens hat geschrieben:Von daher kann ein Thread dennoch die GUI lähmen...
Nein, das kann nicht passieren. In CPython werden 100 Instruktionen durchgeführt, das ist zumindest der letzte Wert an den ich mich erinnern kann, und dann ein Thread-Wechsel durchgeführt. Blockiert ein Aufruf, eine Queue oder ein Lock, so wird immer gewechselt.
jens hat geschrieben:Vielleicht macht wegen des GIL multiprocessing doch Sinn?!?
Das hatte ich in einem der vorherigen Beiträge ja schon geschrieben. Das macht nur Sinn, wenn die Berechnungen auch wirklich parallel sind. Wenn du ständig Synchronisieren musst, dann macht dir der Overhead den ganzen Gewinn kaputt.
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

EyDu hat geschrieben:
jens hat geschrieben:Vielleicht macht wegen des GIL multiprocessing doch Sinn?!?
Das hatte ich in einem der vorherigen Beiträge ja schon geschrieben. Das macht nur Sinn, wenn die Berechnungen auch wirklich parallel sind. Wenn du ständig Synchronisieren musst, dann macht dir der Overhead den ganzen Gewinn kaputt.
Das ist nach wie vor die Frage.

Fazit wäre dann: Entweder multiprocessing oder keine Threads...

Ich glaube ich habe einen "Flaschenhals" bzw. Fehler gefunden: http://www.python-forum.de/viewtopic.ph ... 42#p262542

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:

Kleines Status Update:

Arbeite aktuell daran, das BASIC Listing aus dem Emulator in einen Text Editor zu bekommen und das ganze wieder zurück.
Aus dem Emulator herraus geht es schon. Anderen Weg ist in Arbeit.

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