Robotrontechnik-Forum

Registrieren || Einloggen || Hilfe/FAQ || Suche || Mitglieder || Home || Statistik || Kalender || Admins Willkommen Gast! RSS

Robotrontechnik-Forum » Sonstiges » JKCEMU 0.9.8.4 » Themenansicht

Autor Thread - Seiten: -1-
000
11.05.2024, 15:01 Uhr
kaiOr

Avatar von kaiOr

Hallo,

offensichtlich nimmt der emulierte U880 Interrupt-Anforderungen etwas vorschnell entgegen.
Das fällt auf, wenn bei anstehenden Interrupt auf EI direkt ein DI folgt.
EI
-----> INT-Acknowledge -> Bldg. INT-Vektor -> autom. DI -> CALL INT-Routine etc.....RETI
DI

Auf meinem KC85/4 muss ich da mind. noch einen NOP einbauen damit Interrupt-Anforderungen angenommen werden.
EI
NOP
-----> INT-Acknowledge
DI

Warum das auch so sein muss sieht man an einer typischen Interrupt-Routine;
PUSH AF
...
POP AF
EI
RETI
-----> INT-Acknowledge

Es soll sichergestellt sein, dass der RETI-Befehl noch passiert bevor die CPU neue Interrupt-Anforderungen entgegennimmt.
Würde der U880 schon vor RETI wieder auf /INT reagieren bekäme er keinen oder den falschen INT-Vektor bei INT-Acknowledge (/M1 + /IORQ) zugespielt da die IEI-IEO-Kette noch vom vorherigen Interrupt-Geber belegt ist. Nur die IEI-IEO-Kette bestimmt darüber welcher Peripheriebaustein auf Acknowledge & RETI reagieren darf.

So, jetzt wäre natürlich die Frage, ob das im JKCEMU überhaupt zum Problem werden kann. Können dort gleichzeitig Interrupte anstehen? Und/oder wird der Fall EI+RETI vielleicht sogar geprüft und andere Fälle EI+!(RETI) dagegen ignoriert?

Zurück zu Lück, warum macht man so kurz nach EI schon wieder DI? Wenn man mit Registern jongliert, die für gewöhnlich vom OS reserviert sind (IX,IY,SP..), will man natürlich möglichst wenig Prozessorzeit an das OS verschwenden. Besser gesagt, der stete Wechsel aller Register von OS- auf Programm-Status und umgekehrt wird ziemlich schnell ziemlich zeitaufwändig, nur um zu erfahren das mal wieder keine Taste gedrückt wurde.

Gruß,
Kai

Dieser Beitrag wurde am 11.05.2024 um 15:21 Uhr von kaiOr editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
001
12.05.2024, 09:04 Uhr
maleuma



Mit dieser Erkenntnis könntest du quasi ein Testprogramm schreiben, welches anzeigt, ob man auf dem Emulator oder auf einem echten KC ist?
--
Mario.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
002
12.05.2024, 21:39 Uhr
Early8Bitz

Avatar von Early8Bitz

Richtig, der EI Befehl erlaubt die Annahme eines anstehenden Interrupts erst nach dem Befehl, der auf EI folgt.
Das kann man in Zilogs Dokumentationen oder auch in der U880 technischen Beschreibung nachlesen.

Nicht so klar aus den Papieren geht hervor, ob dieses um einen Befehl versetzte Freigeben der Interruptannahme nur gilt, wenn die CPU vorher im DI war.
Prinzipiell könnte man ja irgendwo im Programm ein EI platzieren, auch wenn die CPU dort definitiv schon im Interrupt-enabled ist.
Würde dann eine zufällig während der EI-Ausführung eintreffende Interruptanforderung auch erst nach dem auf EI folgenden Befehl angenommen, oder direkt nach dem EI?
D.h. würde ein EI im Programmablauf in einem Programmteil, wo die Interruptannahme bereits enabled ist, implizit wie ein kurzes, einen Befehl langes DI wirken?
Bevor jemand meckert, dieses Konstrukt hat wahrscheinlich in der Praxis keine Bedeutung, aber für das Bauen eines Emulators ist das schon ein wichtiger Fakt, um die Hardware exakt nachzubilden.

Zitat:
kaiOr schrieb
Es soll sichergestellt sein, dass der RETI-Befehl noch passiert bevor die CPU neue Interrupt-Anforderungen entgegennimmt. Würde der U880 schon vor RETI wieder auf /INT reagieren...


Es ist nicht so, dass in einer ISR das EI unmittelbar vor dem RETI stehen muss. Warum soll die CPU keinen Interrupt annehmen dürfen, während sie noch die ISR einer anderen (niedriger priorisierten) Anforderung bearbeitet?
Wenn ich mit verschachtelten Interrupts arbeite (nested interrupts) ist es sogar notwendig, dass das EI möglichst nah am Beginn der ISR einer niedrig priorisierten Interrupquelle steht, damit ein höherpriorisierter Baustein diese ISR unterbrechen kann.

Also z.B. CTC am Anfang der Prioritätenkette erzeugt periodisch einen Interrupt für eine zeitkritische Funktion (Echtzeituhr, Bedienung von Schrittmotoren etc.).
Eine SIO weiter hinten in der Prioritätenkette löst bei eintreffenden Zeichen ebenfalls einen Interrupt aus.
Dann würde ich die ISR der SIO in etwa so aufbauen, um das vom CTC generierte Timing möglichst wenig zu beeinflussen:

Quellcode:
isrsio: ei
        push af
                  ; <- ab jetzt kann der CTC wieder interruptmäßig bedient werden
        push rr   ; weitere benötigte Register retten
        in a,(siodata)
        ; evtl. Test auf Empfangsfehler
        ; Berechnen der nächsten freien Position im Empfangspuffer
        ld (puffer+x),a
        pop rr
        pop af
        reti


--
Gruß
Ralf

Ist ein alter Schaltkreis ein Schaltgreis?

Dieser Beitrag wurde am 12.05.2024 um 21:41 Uhr von Early8Bitz editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
003
13.05.2024, 00:57 Uhr
kaiOr

Avatar von kaiOr

Hallo,

erstmal Asche auf mein Haupt und ich bitte um Entschuldigung. Mein Programm nahm einen ungeahnten Weg und ich jage ein Phantom.

Zitat:
maleuma schrieb
Mit dieser Erkenntnis könntest du quasi ein Testprogramm schreiben, welches anzeigt, ob man auf dem Emulator oder auf einem echten KC ist?

Das hätte ich vorher tun sollen. EMU und KC agieren an diese Stelle exakt gleich.
Die Abweichng hat wohl mehr mit dem zeitl. Eintreffen des Tastentelegramms zu tun.
http://robotron.webhop.net/files/kc/emutest.asm


Zitat:
Early8Bitz schrieb
Prinzipiell könnte man ja irgendwo im Programm ein EI platzieren, auch wenn die CPU dort definitiv schon im Interrupt-enabled ist.
Würde dann eine zufällig während der EI-Ausführung eintreffende Interruptanforderung auch erst nach dem auf EI folgenden Befehl angenommen, oder direkt nach dem EI?

Zumindest verzögert eine Serie EI-Befehle die INT-Annahme. KC85/4 und EMU sind sich da offenbar auch einig.
http://robotron.webhop.net/files/kc/eieitest.asm
Rueckkehr-Adresse ist immer der DI hinter NOP. Man könnte noch versuchen die Zeitkonstante der CTC so aufzuziehen, dass wirklich erst nach erlaubtem Interrupt ein einsames EI getroffen wird.


Zitat:
Early8Bitz schrieb
Es ist nicht so, dass in einer ISR das EI unmittelbar vor dem RETI stehen muss. Warum soll die CPU keinen Interrupt annehmen dürfen, während sie noch die ISR einer anderen (niedriger priorisierten) Anforderung bearbeitet?

Natürlich muss nicht jede Interrupt-Routine so ausschauen. Steht der rufende Interruptgeber in der IEI-IEO-Kette weiter vorne wird sogar der richtige Interruptvektor übergeben.
Das wirft dann aber neue Fragen auf:
Werden SIO + CTC gleichzeitig beim ersten RETI den IEO wegnehmen und der zweite RETI überflüssig?
Oder reagiert die SIO nach ihrem INT-Acknowledge tatsächlich noch auf Veränderungen in der IEI-IEO-Kette und stellt sich beim RETI hinten an, obwohl sie als Erstes dran war?

Gruß,
Kai

Dieser Beitrag wurde am 13.05.2024 um 01:20 Uhr von kaiOr editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
004
13.05.2024, 11:14 Uhr
Early8Bitz

Avatar von Early8Bitz


Zitat:
kaiOr schrieb
Steht der rufende Interruptgeber in der IEI-IEO-Kette weiter vorne wird sogar der richtige Interruptvektor übergeben.


Und ein Interuptgeber weiter hinten in der Kette kann seine Anforderung (/INT=Low) erst stellen, wenn sein IEI Eingang wieder High geworden ist. Da stellt sich die Frage nach dem Interruptvektor erst garnicht.

Zitat:
kaiOr schrieb
Das wirft dann aber neue Fragen auf:
Werden SIO + CTC gleichzeitig beim ersten RETI den IEO wegnehmen und der zweite RETI überflüssig?
Oder reagiert die SIO nach ihrem INT-Acknowledge tatsächlich noch auf Veränderungen in der IEI-IEO-Kette und stellt sich beim RETI hinten an, obwohl sie als Erstes dran war?


Das RETI auf dem Datenbus wird nur von dem Device beachtet, dessen gerade bearbeiteter Interrupt die höchste Priorität hat. Wo also IEI=High ist.
In meinem Beispiel würde also das RETI der CTC ISR nur auf den CTC wirken, da die SIO mit ihrer unterbrochenen ISR zu dem Zeitpunkt IEI=Low hat.
--
Gruß
Ralf

Ist ein alter Schaltkreis ein Schaltgreis?

Dieser Beitrag wurde am 13.05.2024 um 11:15 Uhr von Early8Bitz editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
005
13.05.2024, 11:29 Uhr
kaiOr

Avatar von kaiOr

Warum zeigst Du mir dann die Interruptroutine der SIO, wenn diese garnicht aufgerufen werden kann?

Doch, mehrere Peripheriebausteine können gleichzeitig /INT auslösen.
Schau Dir mal das Taktdiagram einer PIO an:

/INT geht auf Low noch bevor IEO und /INT wird nicht zurückgenommen. Sei es dummer Zufall oder ein Übersprecheffekt auf den externen Ports, zwei PIOs können taktsynchron Interrupt anmelden. Nur die während /M1 einschwingende IEI-IEO-Kette bestimmt, wer auf Interrupt-Acknowledge(/M1 + /IORQ) reagieren darf bzw. den Vektor liefert.

Dieser Beitrag wurde am 13.05.2024 um 11:39 Uhr von kaiOr editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
006
13.05.2024, 17:08 Uhr
Early8Bitz

Avatar von Early8Bitz


Zitat:
kaiOr schrieb
Warum zeigst Du mir dann die Interruptroutine der SIO, wenn diese garnicht aufgerufen werden kann?


Du hast mich falsch verstanden. Ausgangspunkt meines Beispiels war die EI/RETI Konstruktion in <001>.

Das Verhalten bei sich überschneidenden INTs ist ja davon abhängig, welcher der beiden Interruptquellen zuerst seine Ansprüche bei der CPU anmeldet.

Wenn der CTC einen Interrupt in Bearbeitung hat, muss die SIO wegen IEOctc=IEIsio=Low mit der Anmeldung ihres Interrupt warten, bis die CTC ISR mit RETI verlassen wird. Egal an welcher Stelle in der CTC ISR das EI platziert wird.

Wenn die SIO einen Interrupt in Bearbeitung hat, kann der CTC wegen IEIctc=High natürlich einen Interrupt anmelden. Seine höhere Priorität in der Hardware-Kette würde ihm aber nichts nutzen, wenn in der SIO ISR das EI erst am Ende unmittelbar vor dem RETI käme. Daher meine Aussage, dass ich das EI in einer ISR immer möglichst an den Anfang setzen würde. Es sei denn, ich will den höher priorisierten Baustein bewusst daran hindern, meine niedriger priorisierten ISR zu unterbrechen.

Hier gibt es eine gute Darstellung des Interruptverhaltens des Z80 Systems.
http://www.z80.info/zip/z80-interrupts.pdf
--
Gruß
Ralf

Ist ein alter Schaltkreis ein Schaltgreis?

Dieser Beitrag wurde am 13.05.2024 um 17:09 Uhr von Early8Bitz editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
007
13.05.2024, 21:52 Uhr
Early8Bitz

Avatar von Early8Bitz

Hallo Kai,

im Prinzip stimmt das bisher beschriebene. Eine Feinheit habe ich aus dem verlinkten Dokument noch heraus gelesen, was in den normalen technischen Manuals des Z80-Systems gar nicht beschrieben ist.

Nehmen wir mal an, die SIO (der niedriger priorisierte Baustein) hat ein Zeichen empfangen und den Empfangsinterrupt ausgelöst wird, der gerade von der CPU bedient wird. Dann haben wir den Zustand in der Prioritätenkette

High-[IEI CTC IEO]-High-[IEI SIO IOE]-Low

Nehmen wir weiter an, dass in der ISR der SIO kein EI am Anfang steht, erst am Ende unmittelbar vor dem RETI. Jetzt hat der CTC (der höher priorisierte Bausten) das Bedürfnis, einen Interrupt bedient zu bekommen. Er zieht /INT und sein IEO auf Low. Zustand der Prioritätenkette jetzt

High-[IEI CTC IEO]-Low-[IEI SIO IOE]-Low

Die CPU ignoriert jedoch die Interruptanforderung des CTC, da sie noch von der Interruptbestätigung der SIO im disable Zustand (IFF1=0) ist.
D.h. die ISR der SIO läuft unbeeindruckt ihrem Ende entgegen. Jetzt kommt das EI als vorletzter Befehl in der SIO-ISR, aber da der EI-Befehl die Interruptannahme um einen weiteren Befehl verzögert, ist als nächstes das RETI der SIO-ISR dran (und der CTC wartet immer noch, dass sein Interrupt bedient wird).

Nun der Knackpunkt: Ein RETI bei IEIsio=Low und IEOsio=Low würde von der SIO ignoriert, da diese dann 'denkt', das RETI gehöre zur ISR eines höher priorisierten Bausteins.
Die SIO-Hardware kann ja nicht 'wissen', dass der höher priorisierte Baustein gar nicht bedient wird, sie geht aber auf Grund von IEOctc=IEIsio=Low davon aus.

Aus diesem Grund wirkt noch folgender zusätzlicher Mechanismus:
Der CTC, der den Interrupt angemeldet hat aber nicht bedient wird, nimmt nach Erkennen eines ED-Bytes im M1-Zyklus (Instruction fetch, wir erinnern uns, der Opcode von RETI ist ED 4D) sein IEO für den nächsten M1-Zyklus zurück auf High. Damit wird auch IEI an der SIO für den nächstem M1-Zyklus High.

High-[IEI CTC IEO]-High-[IEI SIO IOE]-Low

Jetzt folgt der zweite M1-Zyklus der RETI, wo das 4D auf dem Bus liegt. Jetzt erkennt die SIO korrekt, dass das RETI für sie bestimmt ist und beendet korrekt ihren Interruptzustand.
Nach dem M1-Zyklus geht das IEO des CTC (und damit der Rest der Prioritätenkette) wieder auf Low und signalisiert somit, dass er jetzt den nächsten zu bedienenden Interrupt angemeldet hat.

High-[IEI CTC IEO]-Low-[IEI SIO IOE]-Low

Jetzt wird der Interrupt des CTC angenommen.

High-[IEI CTC IEO]-Low-[IEI SIO IOE]-Low

Nach dem RETI in der CTC-ISR (sofern die SIO nicht schon wieder quengelt):

High-[IEI CTC IEO]-High-[IEI SIO IOE]-High

EDIT:
Legende:
Kein Interrupt
Interrupt angemeldet, aber nicht bestätigt
Interrupt in Bearbeitung
--
Gruß
Ralf

Ist ein alter Schaltkreis ein Schaltgreis?

Dieser Beitrag wurde am 13.05.2024 um 22:12 Uhr von Early8Bitz editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
008
14.05.2024, 11:25 Uhr
kaiOr

Avatar von kaiOr

Danke Ralf,

damit ist der Fall "IE vozeitig wieder aktiviert" durch die Entwickler bei Zilog besser berücksichtigt als gedacht.

Letztlich hängt es wohl von der Hauptprogramm-Schleife ab, ob man das auch so umsetzen muss.
Macht die CPU gerade große Spaziergänge (Diskettenarbeit, Bildschirmaufbau etc.) kommt man vermutlich nicht um diese Verschachtelung herum um den Datenfluss aufrecht zu erhalten.
Grast die CPU dagegen nur in ewiger Warteschleife ihre ToDo-Liste ab könnte man die ISR der SIO auch aufs Nötigste reduzieren.
isrsio:
PUSH AF
LD A,(TODO)
SET 0,A ;CASE 1
LD (TODO),A
POP AF
EI
RETI

Der Zeitverlust einer CALL-RET(I)-Verschachtelung fällt weg. Man muss nicht endlos viele Register sichern/rücksichern. Die CTC kommt trotzdem noch rechtzeitig zum Zug. Das Hauptprogramm holt die Daten der SIO.

Gruß,
Kai

Dieser Beitrag wurde am 14.05.2024 um 11:27 Uhr von kaiOr editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
009
14.05.2024, 21:19 Uhr
ambrosius



Nicht zu vergessen auch die beiden Flags IFF1 und IFF2, welche sich angemeldete Interrupts merken und je nach Bearbeitung auch gesetzt werden:

siehe Ludwig Claßen, Programmierung des Mikroprozessorsystems U880-K1520, Reihe Automatisierungstechnik, Verlag Technik, Seite 16 ff:

...
MASK!ERBARER INTERRUPT (INT)
Der maskierbare Interrupteingang kann über zwei dem Programmierer zur Verfügung
stehende Befehle "Einschalten Interruptsystem" (EI) und "Ausschalten Interruptsystem"
(DI) in einzelnen Programm abschnitten aktiviert bzw. ausgeblendet werden. Ist das
Interruptsystem nicht freigegeben, werden INT-Anforderungen vom U 880 ignoriert.
Nach Einschalten der ZVE und nach RESET ist das Interruptsystem immer ausge-
schaltet (DI).
Das Einschalten des Interruptsystems (EI) setzt die beiden Flipflops IFF1 und IFF2
auf 1. Ein dann anstehendes INT-Signal wird erkannt. Umgekehrt bewirkt das
Sperren des Interruptsystems (DI), daij die bei den Interruptmerker IFF1 und IFF2 auf
o gesetzt werden. Nachdem ein Programmunterbrechungssignal erkannt und akzep-
tiert worden ist, wird hardwaremäijig eine DI-Operation ausgeführt. Sie bewirkt, ge-
nauso wie das Software-DI, ein Rücksetzen von IFF1 und IFF2. Erneute Unter-
brechungen müssen dann erst wieder vom Programmierer zugelassen (EI) werden.
Das "Enable Interrupt" wird immer erst nach der Ausführung der auf den EI-Befehl
folgenden Instruktion wirksam. Nur dadurch ist ein ordnungsgemäijer Abschluij der
Interruptbedienroutinen möglich (EI + RETI) ...
--
viele Grüße
Holger
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
010
15.05.2024, 21:56 Uhr
Early8Bitz

Avatar von Early8Bitz


Zitat:
kaiOr schrieb
isrsio:
....
RETI


Hi Kai,

was Du da vorschlägst, ist ja nichts anderes, als kompliziert gestaltetes Polling.
Ich nehme mal an, in deinem Hauptprogramm würdest Du periodisch die TODO Variable abfragen (ich habs mal als Unterprogramm geschrieben) und das empfangene Zeichen aus der SIO abholen, wenn TODO sagt, es ist eines da.

Quellcode:

; SIO im Interrupbetrieb, ISR siehe Kai<008>
; UP im Hauptprogramm periodisch aufgerufen
getsio: push   hl
        ld     hl,TODO
        bit    0,(hl)
        jr     z,gets1      ; Zero, wenn kein Zeichen verfuegbar
        res    0,(hl)       ; No Zero, wenn Zeichen empfangen
        in     a,(siodata)  ; Zeichen aus SIO lesen
gets1:  pop    hl
        ret


Dann kannst Du aber gleich ganz auf den Interruptbetrieb der SIO verzichten und periodisch das Empfangsbit (Bit 0 im Read Register 0 der SIO) abfragen.

Quellcode:

; SIO im Polling Betrieb
; UP im Hauptprogramm periodisch aufgerufen
getsio: in     a,(sioctrl)  ; SIO RR0 lesen
        bit    0,a          ; Zeichen im Empfangspuffer?
        ret    z            ; Nein, RET Zero
        in     a,(siodata)  ; Zeichen aus SIO lesen
        ret                 ; No Zero, wenn Zeichen empfangen


--
Gruß
Ralf

Ist ein alter Schaltkreis ein Schaltgreis?
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
011
15.05.2024, 22:24 Uhr
Early8Bitz

Avatar von Early8Bitz


Zitat:
ambrosius schrieb
Nicht zu vergessen auch die beiden Flags IFF1 und IFF2, welche sich angemeldete Interrupts merken und je nach Bearbeitung auch gesetzt werden:


Hallo Holger,

Die Flags haben wir nicht vergessen, die Flags waren für unsere Betrachtung der Interruptprioritätenkette und der verschachtelten Interrupts nur nicht relevant.

Für den maskierbaren Interrupt spielt nur der Zustand des IFF1 eine Rolle. Ist es gesetzt (EI-Befehl), akzeptiert die CPU maskierbare Interrruptanforderungen, ist es nicht gesetzt (DI-Befehl) ignoriert die CPU maskierbare Interruptanforderungen.
Im EI-Zustand wird das Flag IFF1 rückgesetzt (DI durch Hardware) sobald die CPU den Interrupt eines Peripheriebaustens annimmt und kann nur durch einen expliziten EI-Befehl wieder gesetzt werden.
IFF2 hat für den maskierbaren Interrupt keine funktionelle Bedeutung, auch wenn es bei EI, DI und Interruptannahme immer synchron zum IFF1 gesetzt bzw. rückgesetzt wird.

Die Aufgabe des IFF2 ist vielmehr, den Zustand des IFF1 während der Behandlung einer NMI-Routine aufzubewahren.
Ein NMI wird bei der Annahme IFF1 nach IFF2 kopieren und dann IFF1 auf Null setzen (rücksetzen). IFF2 ist quasi das 'Backup' des IFF1. Damit sind in der NMI Serviceroutine maskierbare Interrupts erstmal disabled, da IFF1=0.
Um das 'Restore' von IFF1 kümmert sich dann der RETN Befehl am Ende der NMI Serviceroutine. Der kopiert dann IFF2 zurück nach IFF1, so dass nach Rückkehr ins unterbrochene Programm der ursprüngliche Zustand des maskierbaren Interruptsystems wieder hergestellt ist.

Die Formulierung ".. Flags IFF1 und IFF2, welche sich angemeldete Interrupts merken...." war vielleicht nur etwas unglücklich gewählt und Du hast das Richtige gemeint.
--
Gruß
Ralf

Ist ein alter Schaltkreis ein Schaltgreis?

Dieser Beitrag wurde am 15.05.2024 um 22:25 Uhr von Early8Bitz editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
Seiten: -1-     [ Sonstiges ]  



Robotrontechnik-Forum

powered by ThWboard 3 Beta 2.84-php5
© by Paul Baecher & Felix Gonschorek