BIT $hhll
Mnemonic: | BIT $hhll |
Opcode: | $2C |
Operand(en): | $ll $hh |
Anzahl der Bytes: | 3 |
Befehlsgruppe: | Arithmetik- und Logikbefehl |
Adressierung: | absolut |
Beeinflusste Flags im Statusregister: | |
Negative-Flag | |
Overflow-Flag | |
Zero-Flag | |
Taktzyklen: | 4 |
Der Assembler-Befehl BIT $hhll macht eine bitweise UND-Verknüpfung des Akkumulators mit dem Inhalt der Speicherzelle $hhll und setzt die Flaggen (Zero-Flag, Negative-Flag und Overflow-Flag) entsprechend.
Das Ergebnis wird nicht wie bei dem Befehl AND $hhll in den Akkumulator geschrieben.
Dieser Befehl ist ideal zum Abfragen einzelner Bits (siehe Beispiel 1) und im Vergleich zu einer Kombination aus LDA $hhll und AND #$nn hinsichtlich Speicherbedarf und Ausführungszeit effizienter, wenn auf das Eintreten eines Ereignisses in einer Warteschleife gewartet wird.
Nachdem die Variante BIT #$nn nicht existiert, kann dies mit der BIT $hhll für passende Speicherstellen im ROM, die dann das Immediate-Argument ersetzen, realisiert werden. Siehe Beispiel 3.
Eine weitere trickreiche Einsatzmöglichkeit des BIT-Befehls ist das Überspringen von Befehlen, indem zwei Byte lange Befehle als Parameter versteckt werden und dabei gewährleistet ist, dass dabei der BIT-Befehl sich neutral verhält, also sonst keine Register (abgesehen vom Statusregister) verändert, wie im Beispiel 2 gezeigt wird.
Der BIT-Befehl besteht aus 2 Teilen:
- Zunächst werden die Bits 7 und 6 der Speicherzelle $hhll ins Negative-Flag und das Overflow-Flag übertragen,
- dann wird eine bitweise UND-Verknüpfung zwischen dem Akkumulator und dem Inhalt der Speicherzelle $hhll durchgeführt, wodurch nun das Zero-Flag beeinflusst wird, also gesetzt ist, wenn es das Ergebnis 0 ergab.
Es ist somit möglich, dass sich nach der Ausführung des Befehls alle drei genannten Flaggen verändert haben und sogar alle drei gesetzt sind. Im Gegensatz dazu kann beim Assemblerbefehl AND $hhll das Negative-Flag und das Zero-Flag niemals gleichzeitig gesetzt sein, da ein negatives Ergebnis nicht gleichzeitig 0 sein kann.
Funktionsprinzip
Bedeutung der Buchstaben im Befehl
BIT | BIT test |
Teste Bits |
Beispiele
Beispiel 1
Abfragen einzelner Bits:
; Diese Programm ist eine einfache Joystick-Abfrage des Controlports 2. ; Joystick auf/ab = Rahmenfarbe +/- ; Joystick links/rechts = Hintergrundfarbe +/- ; Feuerknopf = Ende ; Programmstart mit SYS 49152 *=$c000 ; Startadresse des Programms start lda $02 cmp $dc00 ; Port auf CIA1, Port A beq start ; bleibe solange in Schleife bis sich Joystick-Register ändert. abfrage lda $dc00 ; neuen Wert in Speicherzelle 2 zwischenspeichern. sta $02 lda #%00000001 ; Joystick-Bewegung "auf" maskieren bit $dc00 ; bitweise UND-Verknüpfung der Adresse 56320, aktiv wenn Bit = 0 bne weiter1 ; wenn Bewegung nicht nach oben --> keine Erhöhung der Rahmenfarbe inc $d020 ; Rahmenfarbe + 1 weiter1 lda #%00000010 ; Joystick-Bewegung "ab" maskieren bit $dc00 ; bitweise UND-Verknüpfung der Adresse 56320, aktiv wenn Bit = 0 bne weiter2 ; wenn Bewegung nicht nach unten --> keine Verminderung der Rahmenfarbe dec $d020 ; Rahmenfarbe - 1 weiter2 lda #%00000100 ; Joystick-Bewegung "links" maskieren bit $dc00 ; bitweise UND-Verknüpfung der Adresse 56320, aktiv wenn Bit = 0 bne weiter3 ; wenn Bewegung nicht nach links --> keine Erhöhung der Hintergrundfarbe inc $d021 ; Hintergrundfarbe + 1 weiter3 lda #%00001000 ; Joystick-Bewegung "rechts" maskieren bit $dc00 ; bitweise UND-Verknüpfung der Adresse 56320, aktiv wenn Bit = 0 bne weiter4 ; wenn Bewegung nicht nach rechts --> keine Verminderung der Hintergrundfarbe dec $d021 ; Hintergrundfarbe - 1 weiter4 lda #%00010000 ; Feuerknopf-Bit maskieren bit $dc00 ; bitweise UND-Verknüpfung der Adresse 56320, aktiv wenn Bit = 0 bne start ; wenn Feuerknopf nicht gedrückt --> verzweige zum Start rts ; Rücksprung ins BASIC
Speicherauszug:
.c000 a5 02 lda $02 .c002 cd 00 dc cmp $dc00 .c005 f0 f9 beq $c000 .c007 ad 00 dc lda $dc00 .c00a 85 02 sta $02 .c00c a9 01 lda #$01 .c00e 2c 00 dc bit $dc00 .c011 d0 03 bne $c016 .c013 ee 20 d0 inc $d020 .c016 a9 02 lda #$02 .c018 2c 00 dc bit $dc00 .c01b d0 03 bne $c020 .c01d ce 20 d0 dec $d020 .c020 a9 04 lda #$04 .c022 2c 00 dc bit $dc00 .c025 d0 03 bne $c02a .c027 ee 21 d0 inc $d021 .c02a a9 08 lda #$08 .c02c 2c 00 dc bit $dc00 .c02f d0 03 bne $c034 .c031 ce 21 d0 dec $d021 .c034 a9 10 lda #$10 .c036 2c 00 dc bit $dc00 .c039 d0 c5 bne $c000 .c03b 60 rts
Vergleich des 1. Beispielprogramms mit BASIC V2:
100 IF A=PEEK(56320) THEN 100 110 A=PEEK(56320) 200 IF (A AND 1) <> 0 THEN 300 210 POKE 53280, (PEEK(53280)+1) AND 255 300 IF (A AND 2) <> 0 THEN 400 310 POKE 53280, (PEEK(53280)-1) AND 255 400 IF (A AND 4) <> 0 THEN 500 410 POKE 53281, (PEEK(53281)+1) AND 255 500 IF (A AND 8) <> 0 THEN 600 510 POKE 53281, (PEEK(53281)-1) AND 255 600 IF (A AND 16) <> 0 THEN 100
Beispiel 2
Zweckentfremdung von BIT: Platzsparendes Überspringen von nachfolgenden Befehlen mit zwei Byte Länge. Hierbei wird die eigentliche Funktionalität des Befehls gar nicht genutzt, vielmehr wird er als eine Art Drei-Byte-NOP benutzt. Prinzipiell lässt sich hierfür auch z.B. der illegale Opcode $0c benutzen (der in der Tat ein NOP $hhll ist und anders als BIT $hhll keine Nebeneffekte auf Flaggen hat). In der Praxis findet sich jedoch meist BIT, nicht zuletzt, um die Portabilität zu anderen 6502-CPU-Varianten (z.B. 65C02 oder 65C816) zu gewährleisten, die die illegalen Opcodes nicht kennen.
; Dieses Programm zeigt das Überspringen von Befehlen mit Hilfe des BIT-Befehls. ; Statt der $2Cs ließe sich auch z.B. ein JMP Out oder BNE Out einfügen, beides braucht jedoch mehr Platz. ; Nach dem Start mit SYS 49152 wird der Bildschirm gelöscht und die Zeichenfolge !***! ausgegeben. *=$c000 ; Startadresse des Programms BSOUT = $ffd2 start jsr Clrscr ; Bildschirm löschen jsr Rufzeichen ; ! ausgeben jsr Stern ; * ausgeben jsr Stern ; * ausgeben jsr Stern ; * ausgeben jsr Rufzeichen ; ! ausgeben rts ; Rücksprung ins BASIC ; ----- Unterprogramm ----- Stern lda #'*' ; Stern ausgeben (Befehlsfolge: lda #'*' bit $21a9 bit $93a9 jsr $ffd2 rts) .byte $2c ; Opcode für BIT $hhll Rufzeichen lda #'!' ; Rufzeichen ausgeben (Befehlsfolge: lda #'!' bit $93a9 jsr $ffd2 rts) .byte $2c ; Opcode für BIT $hhll Clrscr lda #147 ; Bildschirm löschen Out jsr BSOUT ; Zeichen am Bildschirm ausgeben rts ; Rücksprung
Speicherauszug:
.c000 20 19 c0 jsr $c019 .c003 20 16 c0 jsr $c016 .c006 20 13 c0 jsr $c013 .c009 20 13 c0 jsr $c013 .c00c 20 13 c0 jsr $c013 .c00f 20 16 c0 jsr $c016 .c012 60 rts .c013 a9 2a lda #$2a >c015 2c .byte $2c .c016 a9 21 lda #$21 >c018 2c .byte $2c .c019 a9 93 lda #$93 .c01b 20 d2 ff jsr $ffd2 .c01e 60 rts
Aus CPU-Sicht werden die folgenden Befehle ausgeführt:
; ...nach einem jsr $c013: .c013 a9 2a lda #$2a .c015 2c a9 21 bit $21a9 ; BIT "verdeckt" a9 21 = lda #$21 .c018 2c a9 93 bit $93a9 ; BIT "verdeckt" a9 93 = lda #$93 .c01b 20 d2 ff jsr $ffd2 .c01e 60 rts ; ...nach einem jsr $c016: .c016 a9 21 lda #$21 .c018 2c a9 93 bit $93a9 ; BIT "verdeckt" a9 93 = lda #$93 .c01b 20 d2 ff jsr $ffd2 .c01e 60 rts
Beispiel 3
BIT #$nn
kann durch Verwendung von festen Werten im ROM abgebildet werden, was den Vorteil hat, dass die Register hierfür nicht geändert werden müssen. Vergleichbar mit entsprechender Instruktion beim Prozessor 65C02, allerdings mit dem Unterschied, dass bei dieser Nachbildung die Flags N und V entsprechend geändert werden und dabei ein Byte und ein Taktzyklus mehr beansprucht wird, als beim 65C02.
Hier sind häufig genutzte Bit-Masken angeführt, um einzelne Bits zu testen, das Zero-Flag explizt zu setzen (kann auf die Art aber nicht gelöscht werden) oder gemäß Inhalt von A zu setzen (wobei aber die Flags N und V gleichzeitig gesetzt werden).
C64 KERNAL-ROM:
bit $E01A ; #$01 (Bit 0 bis 7 testen -> Z-Flag ...) bit $E069 ; #$02 bit $E0A6 ; #$04 bit $E0B1 ; #$08 bit $E027 ; #$10 bit $E002 ; #$20 bit $E653 ; #$40 (setzt V-Flag, löscht N-Flag) bit $E0EC ; #$80 (setzt N-Flag, löscht V-Flag) bit $E03B ; #$00 (Z-Flag setzen, N- und V-Flag löschen) bit $E0A0 ; #$FF (Z-Flag gemäß Akku-Wert, N- und V-Flag aber immer gesetzt)
C64 BASIC-ROM:
bit $A390 ; #$01 (Bit 0 bis 7 testen -> Z-Flag ...) bit $A39B ; #$02 bit $A3E7 ; #$04 bit $A3B9 ; #$08 bit $A058 ; #$10 bit $A1A1 ; #$20 bit $AB5A ; #$40 (setzt V-Flag, löscht N-Flag) bit $A018 ; #$80 (setzt N-Flag, löscht V-Flag) bit $A07A ; #$00 (Z-Flag setzen, N- und V-Flag löschen) bit $A336 ; #$FF (Z-Flag gemäß Akku-Wert, N- und V-Flag aber immer gesetzt)