Erstellt: 28. Juli 2013 (zuletzt geändert: 1. Januar 2018)

Raster-IRQ: Timing-Probleme

Zeit ist ALLES!

C64 Studio, AMCE & TASM

Wer schon etwas mit dem Rasterzeileninterrupt experimentiert hat, dem ist bestimmt aufgefallen, dass nicht immer alles so läuft, wie man es sich gedacht hat. Beim Raster-IRQ ist das Timing meistens immens wichtig, wenn das nicht stimmt, erreicht man häufig nicht den gewünschten Effekt.

Um dem Beitrag korrekt folgen zu können ist es wichtig, dass die Beispiele ganz genau übernommen werden, ein Taktzyklus mehr oder weniger und schon kommt evtl. etwas vollkommen anderes heraus. Außerdem setze ich wieder ein PAL-System voraus, wie es bei den in Deutschland verkauften C64-Modellen verwendet wurde.
Da ich auch vergleichen wollte, wie sich die Beispiele auf unterschiedlichen Systemen & Emulatoren verhalten, habe ich sie mit WinVICE 3.1 x64 & x64sc, CCS64 3.9 und Emu64 Beta 5.0.15 (alle unter Windows 10 64Bit), sowie auf dem Turbo Chameleon mit Beta 9g, dem C64 Reloaded MK1 & 2 und mit einem „echten“ C64 getestet. An den entsprechenden Stellen weise ich auf evtl. Unterschiede hin.

Da WinVICE so schön zum C64 Studio passt und sich damit einiges besser debuggen lässt, beziehe ich mich in diesem Beitrag hauptsächlich auf diese Kombination.
Außerdem gehe ich davon aus, dass ihr im Emulator den Rahmen nicht abgeschaltet habt. Den brauchen wir jetzt nämlich.

Die 1 Pixel-Linie im Rahmen

Um uns die Timing-Probleme vor Augen zu führen, stellen wir uns doch einfach mal die simple Aufgabe, eine 1 Pixel hohe Line durch Veränderung der Farbe im oberen Rahmen zu erzeugen. Also flugs ein Programm zusammengeschustert, ist ja „Babykram“:

LINESTART      = $20                ;hier soll die Linie erscheinen
LINEEND        = LINESTART+1        ;und ab hier keine Linie mehr

;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2062
 !word main-2, 2018 
 !byte $9e
 !text " 2062"
 !byte $00,$00,$00
 
!zone main
main
 sei                                ;IRQs sperren

 lda #<myIRQ                        ;Adresse unserer IRQ-Routine
 sta $0314                          ;in den RAM-Vector
 lda #>myIRQ
 sta $0315

 lda #%00000001                     ;Raster-IRQ vom VIC-II freigeben
 sta $d01a

 lda #LINESTART                     ;erste Zeile für Raster-IRQ
 sta $d012                          ;festlegen

 lda $d011                          ;höchstes BIT der Rasterzeile
 and #%01111111                     ;zur Sicherheit löschen
 sta $d011

 cli                                ;IRQs freigeben

 jmp *                              ;Endlosschleife



!zone myIRQ
myIRQ                               ;Eigene Interrupt-Routine
 lda $d019                          ;Auslöser: Raster oder Timer?
 bmi .raster                        ;falls Raster -> .raster
 lda $dc0d                          ;sonst
 jmp $ea31                          ;Timer-IRQ verarbeiten

.raster
 lda $d012                          ;aktuelle Rasterzeile lesen
 cmp #LINESTART                     ;prüfe ob Rasterzeile = LINESTART
 bne doBlack                        ;falls nicht -> doBlack
 lda #$01                           ;sonst weiß für die Linie
 sta $d020                          ;als Rahmenfarbe setzen
 lda #LINEEND                       ;Zeile LINEEND nächsten IRQ
 sta $d012                          ;festlegen
 jmp .exit                          ;und zum IRQ-Ende

doBlack
 cmp #LINEEND                       ;prüfe ob Rasterzeile = LINEEND
 bne .exit                          ;falls nicht zum Ende springen
 lda #$00                           ;sonst, Rahmenfarbe auf schwarz
 sta $d020                          ;setzen
 lda #LINESTART                     ;nächster IRQ wieder bei LINESTART
 sta $d012                          ;festlegen

.exit
 lda #%00000001                     ;Raster-IRQ bestätigen
 sta $d019

 pla                                ;Register vom Stack holen
 tay
 pla
 tax
 pla

 rti                                ;und IRQ verlassen

Das Programm richtet zunächst unseren Interrupt myIRQ ein (Adresse im RAM-Vector hinterlegen, Raster-IRQs erlauben und erste Zeile eintragen). Nach Freigabe der Interrupts, lassen wir das Programm in einer Endlosschleife ‚hängen‚.

Im Interrupt myIRQ wird dann wieder geprüft, ob dieser ein Rasterzeilen- oder Timer-Interrupt ist. Timer-Interrupts werden über $ea31 verarbeitet. Beim Raster-IRQ wird geprüft, ob dieser in unserer gewünschten Zeile LINESTART stattgefunden hat, falls ja, wird die Rahmenfarbe auf weiß gesetzt und die direkt folgende Zeile LINEEND als nächster Raster-IRQ festgelegt. Danach wird zum Ende gesprungen. Wurde der aktuelle Raster-IRQ nicht bei LINESTART ausgelöst, geht es bei doBlack weiter. Dort wird geprüft, ob es sich um die Rasterzeile LINEEND handelt, falls nicht geht es zum Ende, sonst wird die Rahmenfarbe auf schwarz gesetzt und als nächste Rasterzeile für den IRQ wieder LINESTART eingetragen. Die Routine läuft anschließen zum Ende weiter, dort wird der IRQ bestätigt, die Register werden vom Stack geholt und die Routine wird verlassen.

Nach dem Start sehen wir zwar den Ansatz einer Linie, aber gleichzeitig flackert auch eine ganze Menge.

Das läuft nicht so, wie geplant!

Dieses auffällige Flackern tritt ausnahmslos in allen von mir getesteten Emulationen auf. Auch das Turbo Chameleon 64 zeigt diesen Effekt. Die drei verschiedenen, von mir verwendeten C64 Modelle, verhalten sich aber anders! Sie zeigen größtenteils ein relativ ruhiges Streifenmuster, das nur ab und zu von einem ‚weißblitzer‘ überlagert wird. Da haben wir es wieder, nichts kann die ‚echte‚ Maschine ersetzen. Aber auch das ‚Bild‘ der echten C64-Modelle haben wir so nicht gewollt!

Sobald ihr LINEEND = LINESTART+1 in +2 ändert, hört das nervige Flackern endlich auf, aber das Ergebnis ist trotzdem nicht wie gewünscht.

Kein Flackern mehr, aber die Linie passt immer noch nicht
Kein Flackern mehr, aber die Linie passt immer noch nicht

Die Linie ist zwei Pixel hoch und rechts zuckt sie noch immer, außerdem ist die Linie hier sogar drei Pixel hoch. Wer genau hinsieht, dem fällt auch auf, dass die Linie teilweise als Ganzes flackert und ab und zu auch der gesamte BS weiß aufblitzt. Bevor wir das in Angriff nehmen, aktiviert bitte zunächst die Funktion „Rahmen debuggen“ von WinVICE. Wählt dazu den Menüpunkt „Einstellungen -> VIC-II Einstellungen…“ aus und markiert im Dialog die entsprechende Funktion.

Rahmen debuggen aktivieren.
Rahmen debuggen aktivieren.

Bestätigt den Dialog mit OK, speichert eure WinVICE Einstellungen und startet das Programm erneut.

WinVICE mit aktiviertem 'Rahmen debuggen'
WinVICE mit aktiviertem ‚Rahmen debuggen‘

Das Aussehen von WinVICE hat sich merklich geändert. Wir sehen nun den gesamten Rasterzeilen-Bereich und müssen feststellen, dass es nicht nur rechts, sondern auch links flackert und sich unsere Linie (zumindest teilweise) in vier Rasterzeilen befindet. Links wäre das Flackern evtl. kein Problem, dies findet im nicht sichtbaren Bereich statt. Wir sehen es ja nur, da wir den Rahmen „debuggen“ und so den kompletten Überblick haben. Diese Einstellung ermöglicht euch also einen Blick auf den normalerweise unsichtbaren Bereich (früher war das nur mit Monitoren möglich, bei denen ihr die Bildlage frei einstellen konntet). Der Rasterstrahl legt also erstmal ein gutes Stück zurück, bevor er in den für uns sichtbaren Bereich vordringt.

Die Probleme

Wer sich die Mühe macht und die Anzahl der Pixel vom höchsten Punkt unserer Linie bis zum oberen Rand zählt, der wird auf 33 Zeilen kommen. Wir möchten die Linie in #$20 = 32 beginnen lassen, da die Zählung mit 0 beginnt ist dies also schon mal die korrekte Zeile. Aber die Linie beginnt viel zuweit rechts! Dies ist auch der Grund, weshalb unser erstes Beispiel so stark geflackert hat. Bis wir die folgende Zeile als neuen Interrupt festgelegt haben, befindet sich der Rasterstrahl schon längst in dieser. Daher wird erst beim nächsten Bildaufbau unser gewünschter IRQ erzeugt und es kommt zum unschönen Flackern des gesamten Rahmens. Durch die Verdopplung der Zeilenhöhe LINEEND = LINESTART+2 haben wir uns mehr Zeit verschafft und das Problem umschifft. Aber wie erwähnt, blitzt hin und wieder immer noch der Rahmen weiß auf und die Linie flackert.

Um der Lösung unserer Aufgabe (eine 1 Pixel hohe Linie) wieder etwas näher zukommen, verzichten wir jetzt erstmal auf den zweiten IRQ. Bei Raster-IRQ: PAL oder NTSC wurde von mir behauptet, auf einem PAL-System stehen uns 63 Taktzyklen je Zeile zur Verfügung. Also ändern wir unser Programm so, dass nach dem Setzen von weiß 63 Zyklen gewartet wird, bevor wir zurück auf schwarz schalten. Versuchen wir also eine durchgängige Linie zu erzeugen.

;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2062
 !word main-2, 2018 
 !byte $9e
 !text " 2062"
 !byte $00,$00,$00

!zone main
main
 sei                                ;Raster-IRQ einrichten

 lda #<myIRQ
 sta $0314
 lda #>myIRQ
 sta $0315

 lda #%00000001
 sta $d01a

 lda #$20                           ;Da wir nur noch einen Raster-IRQ verwenden
 sta $d012                          ;setzen wir hier direkt die gewünschte Zeile

 lda $d011
 and #%01111111
 sta $d011

 cli

 jmp *


!zone myIRQ
myIRQ
 lda $d019                          ;Raster- oder Timer-IRQ?
 bmi .next
 lda $dc0d
 cli
 jmp $ea31

.next
 lda #$01                          
 sta $d020                          ;Rahmen = weiß
 ldx #$0a                           ;            2TZ
.loop
 dex                                ;10 * 2TZ = 20TZ
 bne .loop                          ; 9 * 3TZ = 27TZ
                                    ; 1 * 2TZ =  2TZ
 nop                                ;            2TZ
 nop                                ;            2TZ
 nop                                ;            2TZ
 lda #$00                           ;            2TZ
 sta $d020                          ;            4TZ Rahmen = schwarz
                                    ;===============
                                    ;           63TZ

 lda #%00000001                     ;IRQ bestätigen
 sta $d019

 pla                                ;Register vom Stack
 tay
 pla
 tax
 pla

 rti                                ;IRQ verlassen

Die einzige (nenneswerte) Änderung hat hinter .next stattgefunden. Da wir nur noch einen IRQ verwenden, prüfen wir nicht mehr in welcher Zeile er auftritt. Wir setzen direkt die Farbe auf weiß und warten solange, bis eine vollständige Zeile vergeht. Anschließend schalten wir zurück auf schwarz. Jetzt haben wir eine vollständige Linie, auch wenn sie noch einen Umbruch hat. Hier kommt es also auf die korrekte Anzahl Taktzyklen an, daher habe ich die als Kommentar ins Listing aufgenommen. Gewöhnt euch besser daran, für wirklich spektakuläre Effekte werdet ihr häufig die präzise Anzahl an Zyklen beachten müssen. Daher müsst ihr obige Routine auch anpassen, wenn ihr diese z. B. auf einem NTSC-System testen wollt. Werft ggf. nochmal einen Blick auf die Tabelle am Ende von Raster-IRQ: PAL oder NTSC.

Eine komplette Linie, allerdings noch nicht stabil und mit Umbruch.
Eine komplette Linie, allerdings noch nicht stabil und mit Umbruch.

Wie ihr im Vergleich zu oben seht, ist unsere Problemstelle schon weiter nach links gewandert, aber sie beginnt immer noch viel zu spät und ihr Anfang ist noch nicht ganz stabil. In den Windows-Emulatoren ist die Linie relativ stabil, wohingegen auf den C64-Modellen und beim TC64 Beginn & Ende der Linie schnell flackern. Dies ist die zweite Auffälligkeit zwischen den Software-Emulatoren und echter Hardware bzw. dem Turbo Chameleon 64.
Wer ganz genau hinsieht, wird außerdem bemerken, dass die Linie ca. alle 8-9 Sekunden etwas mehr flackert. Um dies genauer zu sehen, ändert im obigen Programm die Warteschleife doch einfach mal von ldx #$0a auf ldx #$01. Jetzt ist die Linie zwar nicht mehr vollständig, aber das Problem sollte etwas besser zu sehen sein, die Linie spring alle paar Sekunden. Sobald ihr das Problem wahrgenommen habt, ändert die Schleife bitte wieder auf #$0a. Als zweites solltet ihr bemerken, dass der Rahmen nicht mehr komplett weiß aufblitzt. Dies wurde durch den Verzicht auf den zweiten Raster-IRQ und die feste Wartezeit für weiß erreicht.

Warum beginnt die Linie so spät und weshalb springt sie?

Wie ihr euch spätestens durch die vorangegangenen Beispiele denken könnt, läuft der Rasterstrahl unabhängig von unserem Programm über den Bildschirm. Er bewegt sich immer mit exakt der selben Geschwindigkeit. Wenn jetzt der IRQ für unsere Zeile ausgelöst wird und wir würden direkt die Farbe auf weiß ändern, dann müsste die Linie doch eigentlich links mit weiß beginnen. Wie wir gesehen haben, ist dem aber nicht so! Bevor unser Befehl ausgeführt wird, geschieht noch eine ganze Menge. Eigentlich solltet ihr einiges schon wissen, aber wir begeben uns nochmal zurück an den Anfang zu Interrupts, um die einzelnen Punkte aufzulisten.

  1. Tritt direkt vor dem Raster-IRQ ein anderer Interrupt (z. B. der Timer-Interrupt) auf, dann beginnt die entsprechende Routine (beim Timer wird nach $ea31 gesprungen). Sobald ein IRQ auftritt, werden bekanntlich weitere Interrupts gesperrt und erst durch ein RTI oder CLI wieder erlaubt. Tritt nun ein IRQ unmittelbar vor erreichen unserer Rasterzeile auf, dann vergeht natürlich einiges an Zeit, bis die Interrupts wieder erlaubt werden und der Raster-IRQ endlich verarbeitet werden kann. Dies ist der Grund, weshalb unsere Linie alle 8 bis 9 Sekunden springt.
  2. Sobald der Rasterstrahl unsere gewünschte Zeile erreicht, wird ein IRQ ausgelöst. Allerdings verarbeitet der Prozessor erstmal den aktuellen Befehl, bevor er sich tatsächlich um den Interrupt kümmert. Da Befehle bis zu acht (bei Verwendung von illigalen Op-Codes) Taktzyklen benötigen, haben wir hier schon wieder eine Quelle für einen Versatz. Dieses Verhalten ist außerdem für das häufige Flackern des Linienanfangs verantwortlich. Da es ja bei jedem Raster-IRQ ein anderer Befehl sein kann, der gerade verarbeitet wird. Bei uns im Emulator ist die Linie relativ stabil, da wir einfach in einer Endlosschleife hängen. Fügt ihr aber auch nur ein NOP am Ende der IRQ-Routine direkt vor dem RTI ein, dann flackert auch unserer Anfang wieder recht heftig.

  3. Dann springt der Prozessor zu der an $fffe / $ffff hinterlegten Adresse (normalerweise $ff48). Dort werden die Register auf dem Stack gesichert, kontrolliert ob es sich um ein BRK oder IRQ handelt und dann beim IRQ per indirektem Sprung zu der Adresse von $0314/15 gesprungen. Das alles kostet auch Zeit.
  4. Jetzt sind wir in unserer Interrupt-Routine, aber bevor wir die Farbe ändern, prüfen wir ob es sich überhaupt um einen Rasterzeileninterrupt handelt, wenn nein geht es zur System-Routine und erst dann wird die Farbe in den Akku geladen und endlich für den Rahmen gesetzt.

Es werden also eine ganze Menge Befehle ausgeführt, bevor wir schließlich zum Setzen der Farbe kommen. Außerdem können uns auch noch externe Einflüsse (wie der Timer-IRQ) dazwischen funken. Es gilt jetzt diese Probleme zu minimieren. Für den Moment versuchen wir die Zeit bis zu unserem Interrupt so kurz wie möglich zu halten.

Da wir den Befehl, den die CPU gerade am Wickel hat, nicht direkt beeinflussen können, kümmern wir uns erstmal um die restlichen Probleme.

Bye-bye, ROM!

Ein Haufen Zeit geht für die ROM-Routine an $ff48 drauf. Es wäre doch viel schöner, wenn wir direkt zu unserer Routine kämen. Da es sich hier aber um das ROM handelt, können wir dort keine anderen Werte eintragen. Aber zum Glück hat der C64 ja den 6510 und der bietet an der Zero-Page-Adresse $01 die Möglichkeit das ROM zu deaktivieren.

Adresse: $01 | Standardwert = %00110111 ($37)

BIT | Funktion
-----------------
7-6 | <unbenutzt> 
 5  | Datasette: Motor (0=an | 1=aus)
 4  | Datasette: Tasten gedrückt (0=eine (oder mehrere) | 1=keine)
 3  | Datasette: Ausgangssignal (Daten die aufs Band geschrieben werden)
 2  | 1 = I/O-Register; 0 = CHAR-ROM
 1  | 1 = KERNAL-ROM  ; 0 = RAM an $a000-$bfff & $e000-$ffff (HIRAM)
 0  | 1 = BASIC-ROM   ; 0 = RAM an $a000-$bfff               (LORAM)

Achtung  I: Setzt ihr BIT-1 auf 0, dann wird auch immer das BASIC-ROM
            ausgeblendet, unabhängig von BIT-0. Was nicht weiter
            verwundert, ohne Kernal würde das BASIC eh nicht mehr
            richtig funktionieren.

Achtung II: Setzt ihr BIT-0 und BIT-1 auf 0, dann werden auch die I/O-
            Register abgeschaltet, BIT-2 hat dann keine Auswirkung mehr!
            Man bekommt so volle 64KB RAM, da nun auch an $d000-$dfff
            das RAM verfügbar ist. Ob man dann noch viel mit dem C64
            anfangen kann, lasse ich mal dahingestellt.

Sobald das ROM ausgeschaltet ist, können wir etwas in $fffe / $ffff ablegen. Bei einem Interrupt ist es der CPU nämlich egal, ob sich die Adresse im ROM oder RAM befindet. Es wird einfach zu der an $fffe/ff hinterlegten Adresse gesprungen.

Ändern wir unser Programm nun dementsprechend:

 lda #%01111111                    ;Ohne KERNAL-ROM gibt es auch 
 sta $DC0D                         ;kein $EA31 mehr, also Timer-IRQ aus!

 lda $01                           ;Inhalt von $01 in den Akku
 and #%11111101                    ;KERNAL- & BASIC-ROM 
 sta $01                           ;ausblenden

 lda #<MyIRQ:                      ;IRQ-Adresse jetzt direkt
 sta $FFFE                         ;an die 'ROM'-Position.
 lda #>MyIRQ:                      ;Das ist durchs KERNAL abschalten
 sta $FFFF                         ;jetzt natürlich RAM!!

Als erstes müssen wir noch daran denken, auch den Timer-IRQ zu deaktivieren. Ohne KERNAL-ROM gibt es natürlich auch keine Systemfunktion bei $ea31 mehr und wir können mit dem Timer im Moment nichts anfangen. Außerdem lösen wir damit auch unser 1. Problem. Ohne Timer-IRQ springt auch unsere Line alle 8 bis 9 Sekunden nicht mehr. Dann deaktivieren wir das KERNAL-ROM und damit auch automatisch das BASIC-ROM! Zum Schluß legen wir die Adresse unserer IRQ-Routine an $fffe/ff, statt wie bisher bei $0314/15, ab.

Jetzt können wir noch die Interrupt-Routine bereinigen. Da wir keinen Timer-IRQ mehr haben, können wir die Prüfung rausschmeißen. Eigentlich müssten wir jetzt selbst fürs Retten der Register sorgen, aber da unser Programm in einer Endlosschleife hängt und kein anderer IRQ mehr auftreten kann, verzichten wir aufs Retten und Zurückholen der Register. Dass geht nur hier, normalerweise bringt ihr den C64 damit zum Absturz!

Hier nochmal das komplette Programm…

;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2062
 !word main-2, 2018 
 !byte $9e
 !text " 2062"
 !byte $00,$00,$00

!zone main
main
 sei                                ;Raster-IRQ einrichten

 lda #%01111111                     ;Ohne KERNAL-ROM gibt es auch 
 sta $dc0d                          ;kein $ea31 mehr, also Timer-IRQ aus!

 lda $01                            ;Inhalt von $01 in den Akku
 and #%11111101                     ;KERNAL- & BASIC-ROM 
 sta $01                            ;ausblenden

 lda #<myIRQ                        ;IRQ-Adresse jetzt direkt
 sta $fffe                          ;an die 'ROM'-Position.
 lda #>myIRQ                        ;Das ist durchs KERNAL abschalten
 sta $ffff                          ;jetzt natürlich RAM!!
 
 lda #%00000001
 sta $d01a

 lda #$20                           ;Da wir nur noch einen Raster-IRQ verwenden
 sta $d012                          ;setzen wir hier direkt die gewünschte Zeile

 lda $d011
 and #%01111111
 sta $d011

 cli

 jmp *


!zone myIRQ
myIRQ
 lda #$01                          
 sta $d020                          ;Rahmen = weiß
 ldx #$0a                           ;            2TZ
.loop
 dex                                ;10 * 2TZ = 20TZ
 bne .loop                          ; 9 * 3TZ = 27TZ
                                    ; 1 * 2TZ =  2TZ
 nop                                ;            2TZ
 nop                                ;            2TZ
 nop                                ;            2TZ
 lda #$00                           ;            2TZ
 sta $d020                          ;            4TZ Rahmen = schwarz
                                    ;===============
                                    ;           63TZ

 lda #%00000001                     ;IRQ bestätigen
 sta $d019

 rti                                ;IRQ verlassen

Wir nähern uns endlich dem linken Rand. Der Linienbeginn (und damit auch deren Ende) flackern zwar, aber wir haben es fast geschafft.

Wie man mit „vollem Rahmen“ sieht, sind wir fast am Ziel!
Wie man mit „vollem Rahmen“ sieht, sind wir fast am Ziel!

Ich habe hier WinVICE zurück auf „Voller Rahmen“ gestellt, um zu zeigen, dass wir es fast geschafft haben. Sobald der Versatz unter dem Rahmen verschwindet, sind wir am Ziel!


Was können wir jetzt tun, um endlich ganz nach links zu gelangen?

Die Antwort ist ganz einfach: NICHTS! 😥

Wir haben das ROM und den Timer-IRQ ausgeschaltet!!
Wir verzichten aufs Retten und Zurückholen der Register, obwohl das normalerweise nicht möglich ist!!!
Wir setzen direkt zu Beginn des Interrupts die Farbe auf weiß!!!!
Es gibt nichts mehr, was wir einsparen könnten!!!!!

Unser bisheriges Vorgehen führt einfach nicht zum Erfolg, wir müssen nach einer anderen Lösung Ausschau halten.

Seid jetzt bitte nicht enttäuscht, hier habt ihr viel Grundlagen-Wissen erhalten, das ihr unbedingt für den nächsten Schritt zum Rasterzeilen-Profi braucht!
OK, ich nerve euch teilweise damit, aber ich kann einfach nicht anders: Programmieren lernt man nur in der Praxis. Es gehört einfach dazu Fehlschläge zu erleiden und Enttäuschungen zu überwinden. Nur wer dass übersteht, wird zu einem wirklich guten und fähigen Programmierer werden.

Jetzt halte ich es wie Kill Bill: Vol. 1 und lasse euch etwas ratlos und unzufrieden zurück.

Fortsetzung folgt… 😉


Schrott!!Naja...Geht so...Ganz gut...SUPER! (9 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


ZurückWeiter

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Protected by WP Anti Spam