BIT Instruction

Programming the 6502 microprocessor and its relatives in assembly and other languages.
archie456
Posts: 5
Joined: 04 Jun 2021
Location: Chelmsford, Essex

BIT Instruction

Post by archie456 »

Hi,

I'm hoping to get some advise on the 6502 BIT instruction, as I can't seem to get it to do what I need.

So I understand that the instruction can be used to check if some bits are set (or not set) and it sets the zero flag accordingly.

I would like to check if bits 6 and 7 of a value stored in the Accumulator are set, I'm not interested in if the other bits are set or not.

So I've tried:

LDA #255 (Example value with bits 6 and 7 set)
BIT $C0 ($C0 = %11000000 - this indicates the two bits I want to check).
BEQ bitsareset
<some code to indicate bits 6 and 7 are not set>
.bitsareset
<some code to indicate bits 6 and 7 are set>

So the above code would indicate that 6 and 7 are set, is this correct? (Obviously not as I can't get it to work!).

I'd appreciate some advice - Thanks!
User avatar
BB8
Posts: 57
Joined: 01 Nov 2020
Location: Tatooine

Re: BIT Instruction

Post by BB8 »

The BIT instruction performs an AND between the Accumulator and the content of a location in memory. So, in your example, it will take the content of the location $C0 and will perform an AND with the accumulator. It will transfer bits 7 and 6 of the content of the location $C0 to the flags Z and V. It will not transfer the bits of the value $C0, nor will AND the $C0 value with the accumulator: the argument of BIT is a location in memory, and will fetch the content of that location to do the work.
Last edited by BB8 on Wed Jan 05, 2022 11:26 pm, edited 2 times in total.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: BIT Instruction

Post by GARTHWILSON »

I see BB8 posted while I was writing. I'll finish anyway.

The Z flag reflects the result of an AND operation, without affecting anything but the status register. $FF ANDed with $C0 will still have a couple of bits set, which is not zero, so Z will be clear, not set.

Note also that without regard to what's in the accumulator, you can do BIT on a memory location (or I/O) (not BIT-immediate) to get its bit 7 into the N flag and bit 6 into the V flag. Related instructions are TSB and TRB.

I cannot pass up the opportunity to recommend the excellent programming manual, "Programming the 65816 including the 6502, 65C02, and 65802" by David Eyes and Ron Lichty. This is a .pdf file of a rather large book that is well laid out and is much better than the description there lets on. Note: There were many problems with the earlier .pdf version that were not in the original paper manual; but in late March 2015, WDC scanned and OCR'ed the paper manual and posted the new, repaired .pdf. Chapter 18 gives at least a page for every instruction, telling exactly what it does, and covering all the addressing modes, cycle counts, etc..
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
BB8
Posts: 57
Joined: 01 Nov 2020
Location: Tatooine

Re: BIT Instruction

Post by BB8 »

BB8 wrote:
It will transfer bits 7 and 6 of the content of the location $C0 to the flags Z and V
sorry: it will transfer bit 7 and 6 to N and V.
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: BIT Instruction

Post by barrym95838 »

archie456 wrote:
I would like to check if bits 6 and 7 of a value stored in the Accumulator are set, I'm not interested in if the other bits are set or not.
For that particular example I would use CMP #$C0 ... if carry is set then bits 6 and 7 are both set, if carry is clear then one or both bits are clear. Woz used the negative flag after a CMP #$C0 in his 1976 floating point package's normalization routine to check if bits 6 and 7 were the same or not, a tidy hack that wouldn't have occurred in the minds of most mortals.

So, we have:

Code: Select all

    cmp #$c0
    bcs both_set   ; 11xxxxxx
    bpl both_clr   ; 00xxxxxx
    ora #0
    bmi only_7     ; 10xxxxxx
only_6:            ; 01xxxxxx
    ...
only_7:
    ...
both_set:
    ...
both_clr:
    ...
This untested example doesn't disturb any registers or memory, only the flags ... if that's not a concern then smaller versions are available.
Last edited by barrym95838 on Thu Jan 06, 2022 4:34 am, edited 3 times in total.
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)
IamRob
Posts: 357
Joined: 26 Apr 2020

Re: BIT Instruction

Post by IamRob »

I think I will put my 2 cents in here as well. Depending on you want to do after you have checked the bits in the Accumulator, but your branching instructions already have the hi-bit covered. If bit #7 is set, then you can branch using BMI/BPL without checking what is specifically in the hi-bit. I usually like to use bits 0 & 1, and 6 & 7 for flag indicators. They can then easily be checked like so.

LDA BYTE
BMI - branch if bit#7 set
ASL
BMI - branch if bit#6 set

or

LDA BYTE
LSR
BCS - branch if bit #0 set
ROR - preserve bit #0
BCS - branch if bit #1 set

some advantages of this method is that the you retain the original value in BYTE, if it is an address and not an immediate value. But also the Accumulator can be restored to its original value with a simple ROR for the first method and a ROL ROL for the second.

Also instead of using BIT on an immediate value, you can use BIT on a zero-page location, then it is just a matter of doing this:
Note: This method does not affect what is in the Accumulator.

BIT ZP_BYTE
BMI *+2 - branch on bit #7 set
BVS - branch on bit #6 set
BVS - branch if both bits #6 and #7 are set
... - code falls through if neither bit #6 or #7 are set
archie456
Posts: 5
Joined: 04 Jun 2021
Location: Chelmsford, Essex

Re: BIT Instruction

Post by archie456 »

Brilliant - thanks for the responses and help - lots to consider.
BruceRMcF
Posts: 388
Joined: 21 Aug 2019

Re: BIT Instruction

Post by BruceRMcF »

Note that while CMP is often best if you need ALL target bits set, "BIT addr" does let individually testing bits 5-7 with a single test and then a sequence of branches based on the sign, overflow and zero flags.

Code: Select all

    LDA #$20
    BIT SOURCEBYTE
    BMI ++
    BVS +
    BEQ Act0
    BNE Act1
+   BEQ Act2
    BNE Act3

++  BVS +
    BEQ Act4
    BNE Act5
+   BEQ Act6
    BNE Act7
I tried something along those lines in a recent effort to "crunch" an implementation of a Sweet16 VM ... https://github.com/BruceMcF/Sweeter16 ... but the space saved wasn't worth the trouble. Still, maybe someday it'll come in handy.
IamRob
Posts: 357
Joined: 26 Apr 2020

Re: BIT Instruction

Post by IamRob »

I think only the 65c02 and 65816 support BEQ/BNE with BIT that way. The 6502 does not, or at least one of the 6502's chips do not.
BruceRMcF
Posts: 388
Joined: 21 Aug 2019

Re: BIT Instruction

Post by BruceRMcF »

IamRob wrote:
I think only the 65c02 and 65816 support BEQ/BNE with BIT that way. The 6502 does not, or at least one of the 6502's chips do not.
AFAIU, that's the 6502 BIT I knew on the C64: https://www.masswerk.at/6502/6502_instr ... t.html#BIT
Quote:
bits 7 and 6 of operand are transfered to bit 7 and 6 of SR (N,V);
the zero-flag is set to the result of operand AND accumulator.
The BIT address modes that the 65C02 adds are "BIT zp,X", "BIT addr,X" and "BIT #n" ... "BIT #n" is especially handy. However loading bit 7 and bit 6 of the operand doesn't do much good with "BIT #n", because it comes from the "n", so you already know whether it has bits 6 and 7 set.

IOW, in the original opcodes, the mask is normally loaded into the accumulator before the BIT instruction, and the target is the operand, so pulling bit 7 and bit 6 into the sign and overflow flag is extra information "for free". But with "BIT #n" addressing, the mask is normally the operand and the target is normally in the accumulator, so reading in bits 7 and 6 of the constant "n" is not as useful.
User avatar
BigDumbDinosaur
Posts: 9426
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: BIT Instruction

Post by BigDumbDinosaur »

IamRob wrote:
I think only the 65c02 and 65816 support BEQ/BNE with BIT that way. The 6502 does not, or at least one of the 6502's chips do not.

His example works with all members of the 6502 family. The improvement offered by the 65C02 and 65C816 is BIT immediate, which is handy for testing a value already loaded into the accumulator, e.g.:

Code: Select all

          lda some_value
          bit #%00000100

The above tests bit 2 of whatever is in the accumulator.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: BIT Instruction

Post by GARTHWILSON »

The NMOS 6502 didn't have BIT# at all, nor BIT abs,X nor BIT ZP,X.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
IamRob
Posts: 357
Joined: 26 Apr 2020

Re: BIT Instruction

Post by IamRob »

Boy! do I look silly.

I have had this bit of code fail on me many times as a test, and assumed it wasn't supported.
It should have crashed and it didn't.

Code: Select all

   lda #0
   sta $6
   bit $6
   beq *+1
   rts
   brk
   brk
this is how I test If I want to know something specific about an instruction.
BruceRMcF
Posts: 388
Joined: 21 Aug 2019

Re: BIT Instruction

Post by BruceRMcF »

IamRob wrote:
Boy! do I look silly.

I have had this bit of code fail on me many times as a test, and assumed it wasn't supported.
It should have crashed and it didn't.

Code: Select all

   lda #0
   sta $6
   bit $6
   beq *+1
   rts
   brk
   brk
this is how I test If I want to know something specific about an instruction.
I don't know your assembler, but wouldn't *+1 as an operand be the first address after the address of the operand? That would be the address of the RTS instruction. So it could be taking the branch and returning, where you are concluding it failed to take the branch and returned.

Try *+2.
User avatar
BigDumbDinosaur
Posts: 9426
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: BIT Instruction

Post by BigDumbDinosaur »

BruceRMcF wrote:
I don't know your assembler, but wouldn't *+1 as an operand be the first address after the address of the operand?

Good catch. I never use absolute references to the program counter in that way, as they can easily introduce pernicious bugs. There's a reason labels exist... :D
x86?  We ain't got no x86.  We don't NEED no stinking x86!
Post Reply