Busy-Wait-Schleife

Aus C64-Wiki
Zur Navigation springenZur Suche springen

Unter einer Busy-Wait-Schleife (von engl. beschäftigtes Warten) versteht man eine Warteschleife in einem Programm, die unablässig eine Bedingung überprüft, bis diese eingetreten ist. Dadurch ist der Computer die ganze Zeit über mit dieser Abfrage beschäftigt und kann nichts anderes mehr machen, weshalb Alternativen, wenn sie denn existieren, die bessere Wahl darstellen.

Wenn man beispielsweise darauf wartet, dass es 18 Uhr ist, könnte man alle 5 Sekunden auf die Uhr schauen, bis es so weit ist. Das wäre dann Busy-Wait, weil man ja beim Warten die ganze Zeit über mit dem Schauen auf die Uhr beschäftigt ist. Die Alternative, sich einen Wecker zu stellen der um 18 Uhr piept, hat diesen Nachteil nicht.

BASIC[Bearbeiten | Quelltext bearbeiten]

In BASIC gibt es im Wesentlichen zwei Möglichkeiten, eine Busy-Wait-Schleife zu implementieren: Mit dem WAIT-Befehl kann auf gewisse Bit-Pattern in einer Speicherzelle gewartet werden. Das ist vor allem für zeitkritische Anwendungen (für ein exaktes Timing) nützlich, wie beispielsweise einem Sprite-Multiplexer in BASIC.

Alternativ dazu kann eine Bedingung in einer Schleife überprüft werden. Um beispielsweise auf eine bestimmte Uhrzeit zu warten kann man wie folgt vorgehen:

10 TI$ = "175930"
20 IF LEFT$(TI$,2) <> "18" GOTO20
30 PRINT "ALARM ES IST "TI$" UHR"

In Zeile 10 wird die aktuelle Zeit auf 17:59:30 gesetzt. Zeile 20 wartet in einer Busy-Wait-Schleife darauf, dass die ersten beiden Ziffern zu einer 18 werden. Das Programm wartet somit 30 Sekunden lang.

In BASIC 7.0 gibt es als dritte Möglichkeit noch den SLEEP-Befehl, mit dem eine gewisse Dauer gewartet wird. Allerdings ist dieser, anders als der gleichlautende Unix-Befehl ebenfalls als Busy-Wait implementiert, weshalb der Computer in der Wartezeit auch hier nicht anderweitig genutzt werden kann.

Alternativen[Bearbeiten | Quelltext bearbeiten]

Im C64-BASIC gibt es keine alternative Möglichkeit. Das einzige, was man hier machen kann, ist die Wartezeit sinnvoll zu nutzen, indem man in der Warteschleife noch andere Befehle ausführt. Dadurch wird allerdings das Timing möglicherweise etwas ungenauer. Im Falle der Tastaturabfrage wird das durch den Tastaturpuffer allerdings abgefedert (der auch dann gefüllt wird, wenn noch nicht aktiv auf eine Taste abgefragt wird). Das folgende Programm füllt während des Wartens den Bildschirm mit Sternchen:

10 TI$ = "175930"
20 PRINT "*"; : REM WARTEZEIT SINNVOLL NUTZEN
30 IF LEFT$(TI$,2) <> "18" GOTO20
40 PRINT "ALARM ES IST "TI$" UHR"


Unter BASIC 7.0 kann ein Busy-Wait mithilfe einer speziellen Konstruktion umgangen werden, indem sich ein (unsichtbar) automatisch bewegtes Sprite eine Kollision auslöst und dadurch eine BASIC-Routine aufruft (via COLLISION). Damit sind zeitgesteuerte BASIC-Handler vergleichbar mit einer IRQ bei Maschinenprogrammen möglich. So sind auch feine Zeitintervalle in einer Auflösung von 1/50 s behandelbar. Siehe COLLISION-Beispiel.


In der BASIC-Erweiterung TSB gibt es mit dem Schlüsselwort ON KEY eine Möglichkeit, ohne Busy-Wait-Schleife auf einen Tastendruck zu warten.

Assembler[Bearbeiten | Quelltext bearbeiten]

Das nachfolgende Assemblerprogramm in ACME-Syntax wartet in einer Busy-Wait-Schleife darauf, dass es auf der Echtzeituhr des CIA1-Bausteins 18:00 Uhr ist:

!to "busywait.prg", cbm

*=$c000
                lda $DC0F        ; Uhrzeit setzen
                and #$7F
                sta $DC0F
                lda $DC0E        ; 50 Hz
                ora #$80
                sta $DC0E

                lda #$85         ; 5 Uhr PM = 17 Uhr
                sta $DC0B
                lda #$59         ; 59 Minuten
                sta $DC0A
                lda #$30         ; 30 Sekunden
                sta $DC09
                lda #$00         ; 0 Zehntelsekunden
                sta $DC08

-               lda $DC0B        ; Busy-Wait-Schleife
                cmp #$86         ; Vergleicht die Stunden mit 18 Uhr
                beq +
                lda $DC08        ; Zehntelsekunden müssen gelesen werden
                jmp -            ; damit die nächste Abfrage der Stunden
                                 ; richtig funktioniert

+               ldx #$00         ; Alarm-Meldung ausgeben
                beq +

-               jsr $FFD2        ; Zeichen ausgeben
                inx
+               lda .alarm,x
                bne -
                rts

.alarm         !pet 13, "alarm!", 13, 0

Alternativen[Bearbeiten | Quelltext bearbeiten]

Beim C64 kann man mit Assembler in etlichen Fällen die Nutzung einer Busy-Wait-Schleife umgehen. (Wenn das nicht geht, kann man zumindest, wie bei BASIC auch, die Wartezeit für zusätzliche Befehle nutzen.) Möglich wird dies durch die beiden Interrupts (IRQ und NMI). Die zu überprüfende Bedingung löst in diesen Fällen einen der beiden Interrupts aus und in der Interrupt-Routine kann dann darauf reagiert werden.


Die folgenden Bedingungen können einen IRQ auslösen:

  • Bei einem der beiden Timer der CIA1 findet ein Unterlauf statt.
  • Einer der beiden Timer der CIA1 erreicht die Alarmzeit.
  • Das Schieberegister der CIA1 ist abgearbeitet.
  • Der FLAG-Pin der CIA1 geht auf Low.
  • Der Rasterstrahl erreicht eine neue Zeile.
  • Zwei Sprites kollidieren.
  • Ein Sprite kollidiert mit dem Hintergrund.
  • Der Lightpen wird benutzt.
  • Am Expansionsport wird PIN 4 auf Low gesetzt. Hierdurch haben Steckmodule die Möglichkeit, eigene Bedingungen zu generieren.


Die folgenden Bedingungen können einen NMI auslösen:

  • Bei einem der beiden Timer der CIA2 findet ein Unterlauf statt.
  • Einer der beiden Timer der CIA2 erreicht die Alarmzeit.
  • Das Schieberegister der CIA2 ist abgearbeitet.
  • Der FLAG-Pin der CIA2 geht auf Low.
  • Der Benutzer drückt die RESTORE-Taste.
  • Am Expansionsport wird Pin D auf Low gesetzt. Hierdurch haben Steckmodule die Möglichkeit, eigene Bedingungen zu generieren.
  • Am Userport wird Pin FLAG 22 auf Low gesetzt. Hierdurch kann über die Serielle Schnittstelle ein Interrupt ausgelöst werden.

Beispiel[Bearbeiten | Quelltext bearbeiten]

Das nachfolgende Beispiel in ACME-Syntax nutzt die Alarmzeit der Echtzeituhr des CIA1 um ein Busy-Wait zu umgehen. Nach dem Aufruf mit SYS 49152 kehrt das Programm fast augenblicklich zur Eingabe zurück. Man kann jetzt zum Beispiel ein BASIC-Programm eingeben. Nach 30 Sekunden wird dann allerdings durch die IRQ-Routine das Wort "ALARM" auf dem Bildschirm ausgegeben.

!to "irqwait.prg", cbm

*=$c000
                lda $DC0F        ; Uhrzeit setzen
                and #$7F
                sta $DC0F
                lda $DC0E        ; 50 Hz
                ora #$80
                sta $DC0E

                lda #$85         ; 5 Uhr PM = 17 Uhr
                sta $DC0B
                lda #$59         ; 59 Minuten
                sta $DC0A
                lda #$30         ; 30 Sekunden
                sta $DC09
                lda #$00         ; 0 Zehntelsekunden
                sta $DC08

                lda $DC0F        ; Alarmzeit setzen
                ora #$80
                sta $DC0F

                lda #$86         ; 6 Uhr PM = 18 Uhr
                sta $DC0B
                lda #$00         ; 0 Minuten, Sekunden und Zehntelsekunden
                sta $DC0A
                sta $DC09
                sta $DC08

                sei              ; Interrupt verbiegen
                lda #<.irq
                sta $0314
                lda #>.irq
                sta $0315
                lda #$84         ; Alarmzeit-IRQ anschalten
                sta $DC0D
                cli
                rts

.irq            lda $DC0D
                bne +
                cli              ; kein CIA-Interrupt, deshalb
-               jmp $ea31        ; System-Interrupt aufrufen

+               and #$84
                cmp #$84
                bne -            ; kein Alarmzeit-Interrupt

                ldx #$00         ; Alarm-Meldung ausgeben
                beq +

-               jsr $ffd2        ; Zeichen ausgeben
                inx
+               lda .alarm,x
                bne -

                pla              ; Aufräumen und Interrupt
                tay              ; beenden.
                pla
                tax
                pla
                rti

.alarm         !pet 13, "alarm!", 13, 0

Moderne Betriebssysteme und moderne Prozessoren[Bearbeiten | Quelltext bearbeiten]

Bei anderen Betriebssystemen gibt es weitere Möglichkeiten eine Busy-Wait-Schleife zu vermeiden. Allen voran sind multitaskingfähige Betriebssysteme in der Lage das Programm (als eigenständigen "Task") so lange anzuhalten, bis eine bestimmte Bedingung eintrifft, während andere Tasks weiter abgearbeitet werden.

Bei modernen Prozessoren ist es möglich, diese in einen Ruhezustand zu versetzen, sodass während des Wartens der Stromverbrauch (und damit auch die Wärmeentwicklung) reduziert wird.

Weblinks[Bearbeiten | Quelltext bearbeiten]

WP-W11.png Wikipedia: Aktives Warten
WP-W11.png Wikipedia: Busy waiting Sprache:english