BIT instruction?
-
Meterman58761
- Posts: 19
- Joined: 05 Jun 2021
BIT instruction?
So, I've never really been clear on the BIT instruction, as I so seldom encountered this opcode when poking around in assembly language back in my C64 days.
Are there any real-world code examples that would illustrate how this works?
I have at hand some code that could stand a slight rewrite, and I am wondering if a BIT instruction would be useful for that scenario.
Right now, in several places, a number is tested bit by bit, by doing an ASL or LSR followed by a BCC or BCS, based on whether a 0 or 1 is desired, then repeating the process for the next bit until all the bits have been shifted through the carry.
Seeing as the carry bit doesn't translate so well into modern high-level languages, I want to see if there are other usable approaches that don't take up so many bytes in assembly language, but readily translate into newer languages.
Now, I could just use AND to test the individual bits, but that means I have to reload the accumulator each time before testing a given bit, and that just adds to code length.
So, is BIT just a non-destructive AND?
In other words, if I do BIT #$08, and comes back 0, and I want to check for 0, I use BEQ? Then I can follow with a BIT #$04 and continue evaluating that way?
Are there any real-world code examples that would illustrate how this works?
I have at hand some code that could stand a slight rewrite, and I am wondering if a BIT instruction would be useful for that scenario.
Right now, in several places, a number is tested bit by bit, by doing an ASL or LSR followed by a BCC or BCS, based on whether a 0 or 1 is desired, then repeating the process for the next bit until all the bits have been shifted through the carry.
Seeing as the carry bit doesn't translate so well into modern high-level languages, I want to see if there are other usable approaches that don't take up so many bytes in assembly language, but readily translate into newer languages.
Now, I could just use AND to test the individual bits, but that means I have to reload the accumulator each time before testing a given bit, and that just adds to code length.
So, is BIT just a non-destructive AND?
In other words, if I do BIT #$08, and comes back 0, and I want to check for 0, I use BEQ? Then I can follow with a BIT #$04 and continue evaluating that way?
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: BIT instruction?
The BIT instruction performs a logical AND between the accumulator and memory, setting or clearing status register flags, but not storing the result of the logical AND.
is logically equivalent to:
the difference being the second example doesn't modify the accumulator the way the first example does. The branch to is_zero will only be taken if ACCUMULATOR & MEMORY would result in $00.
BIT on the NMOS 6502 has only two addressing modes. On the 65C02 and 65C816, new addressing modes are <zp>,X and <abs>,X, plus immediate mode:
which may be more convenient at times.
When BIT is used on memory, as in the second example above, bits 6 and 7 of the memory location being tested are copied to the status register's V and N flags, respectively. The Z flag will always reflect the result of the logical AND operation.
Code: Select all
LDA #%01010000
AND uart_isr
BEQ is_zerois logically equivalent to:
Code: Select all
LDA #%01010000
BIT uart_isr
BEQ is_zerothe difference being the second example doesn't modify the accumulator the way the first example does. The branch to is_zero will only be taken if ACCUMULATOR & MEMORY would result in $00.
BIT on the NMOS 6502 has only two addressing modes. On the 65C02 and 65C816, new addressing modes are <zp>,X and <abs>,X, plus immediate mode:
Code: Select all
LDA uart_isr
BIT #%01010000
BEQ is_zerowhich may be more convenient at times.
When BIT is used on memory, as in the second example above, bits 6 and 7 of the memory location being tested are copied to the status register's V and N flags, respectively. The Z flag will always reflect the result of the logical AND operation.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: BIT instruction?
Meterman58761 wrote:
So, is BIT just a non-destructive AND?
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: BIT instruction?
BIT is really nice for testing port bits. If you will need to quickly test an I/O line like for the data line when you're bit-banging a synchronous-serial connection, put it on bit 6 or bit 7 of a parallel port, so you can just do:
without regard for what's in the accumulator, nor affecting the accumulator.
Code: Select all
BIT PORT_A
BMI ____ ; (or BPL or BVC or BVS, as appropriate)without regard for what's in the accumulator, nor affecting the accumulator.
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?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Re: BIT instruction?
This is, surely, the reason why the peripheral chips we use often present a useful overall status bit in bit 7.
-
Meterman58761
- Posts: 19
- Joined: 05 Jun 2021
Re: BIT instruction?
Here's a snippet showing what I mean about using the carry bit to evaluate a number and if I understand BIT based on what you guys say:
Code: Select all
Relay status nybble: 0 = contacts closed, 1 = contacts open
Existing code:
LB_ECC0 LDA A $C3 // fetch status nybble
LSR A // shift Relay A bit into carry
BCS LB_ECCA // contact opened like it was supposed to
JMP LB_EE31 // jump to test fail: Relay A
LB_ECCA LDA B #$07 // Write pass value to Relay A register
STA B $0CEA
LSR A // shift Relay B bit into carry
BCS LB_ECD5 // contact opened like it was supposed to
JMP LB_EE67 // jump to test fail: Relay B
LB_ECD5 LDA B #$07 // Write pass value to Relay B register
STA B $0CEB
LSR A // shift Relay C bit into carry
BCS LB_ECE0 // contact opened like it was supposed to
JMP LB_EE6F // jump to test fail: Relay C
LB_ECE0 LDA B #$07 // Write pass value to Relay C register
STA B $0CEC
LSR A // shift Relay D bit into carry
BCS LB_ECEB // contact opened like it was supposed to
JMP LB_EE77 // jump to test fail: Relay D
LB_ECEB LDA B #$07 // Write pass value to Relay D register
STA B $0CED
If I understand BIT:
LB_ECC0 LDA A $C3 // fetch status nybble
BIT A #$01 // Test bit 0 - Relay A
BNE LB_ECCA // contact opened like it was supposed to
JMP LB_EE31 // jump to test fail: Relay A
LB_ECCA LDA B #$07 // Write pass value to Relay A register
STA B $0CEA
BIT A #$02 // Test bit 1 - Relay B
BNE LB_ECD5 // contact opened like it was supposed to
JMP LB_EE67 // jump to test fail: Relay B
LB_ECD5 LDA B #$07 // Write pass value to Relay B register
STA B $0CEB
BIT A #$04 // Test bit 2 - Relay C
BNE LB_ECE0 // contact opened like it was supposed to
JMP LB_EE6F // jump to test fail: Relay C
LB_ECE0 LDA B #$07 // Write pass value to Relay C register
STA B $0CEC
BIT A #$08 // Test bit 3 - Relay D
BNE LB_ECEB // contact opened like it was supposed to
JMP LB_EE77 // jump to test fail: Relay D
LB_ECEB LDA B #$07 // Write pass value to Relay D register
STA B $0CED
Re: BIT instruction?
Umm, do you have two accumulators? Is this 6800, or 6809 family, perhaps, rather than 6502? I'm not very familiar but it's possible those micros have different behaviour.
On the 6502, if you needed to update some other address without disturbing A, you would typically use X or Y, which are both single-byte registers.
On the 6502, if you needed to update some other address without disturbing A, you would typically use X or Y, which are both single-byte registers.
-
Meterman58761
- Posts: 19
- Joined: 05 Jun 2021
Re: BIT instruction?
Currently working in 6801 assembly, yes, so if / when this gets translated over to 6502, the B accumulator in this example would end up being substituted with the Y index.
Re: BIT instruction?
I think you have the right idea, anyway.
There's a choice, whether to load a mask into A and BIT against a location, or load A from the location and then BIT against a constant. Sometimes it will be right to read the location just once.
If you have just 2 bits of interest and they are at the top of the byte, BIT makes more sense, I think. If your bits of interest are at the bottom of the byte, shifting and checking C makes good sense. It's not the case that BIT is always the best choice, but sometimes it is. So it's worth understanding it so you can use it as appropriate.
Sometimes a mix of tactics will be best, for example if bits 7 6 and 0 are in use.
There's a choice, whether to load a mask into A and BIT against a location, or load A from the location and then BIT against a constant. Sometimes it will be right to read the location just once.
If you have just 2 bits of interest and they are at the top of the byte, BIT makes more sense, I think. If your bits of interest are at the bottom of the byte, shifting and checking C makes good sense. It's not the case that BIT is always the best choice, but sometimes it is. So it's worth understanding it so you can use it as appropriate.
Sometimes a mix of tactics will be best, for example if bits 7 6 and 0 are in use.
Re: BIT instruction?
If you want to check all the bits of a location, one by one, you could do something like:
I think that maybe you're describing a byte of flags (where each bit is a flag). You may prefer to use a whole byte as a flag and just focus on the bit 7 of the byte and check it with the BIT instruction and the BPL/BMI branches.
Similarly another useful opportunity is to check if a signed number in memory is positive or negative, without compromising the accumulator:
Code: Select all
LDA #$01
BIT mem
BNE bit0set
ASL
BIT mem
BNE bit1set
ASL
BIT mem
BNE bit2set
...
Code: Select all
true = $FF
false = $00
LDA #false
;LDA #true
STA memflag
[...]
BIT memflag
BPL flagisfalse
Code: Select all
BIT num
BMI isneg
-
Meterman58761
- Posts: 19
- Joined: 05 Jun 2021
Re: BIT instruction?
This successive bit-shifting is done in several places in the code.
The most common use is to interpret the relay status nybble to determine whether the four relays opened or closed as directed, and to set the pass / fail conditions as appropriate.
It is also used within the main loop; at various points in the code, a 'configuration byte' is set and program flow is then directed back to the main loop as a 'do this step now but not that' (this section uses ASL and BCC to run through bits 7-3, 1, and 0; when the carry bit is 0, the program drops down to the next bit to be evaluated).
My goal is to end up with something that works in assembly, whether 6801 or 6502, yet intuitively translates to higher level languages (i.e. C++).
This code was riddled with shortcuts and odd programming quirks and I've pried out as many as I could find.
Perhaps I should post the initial configuration check code just to show you what I'm working with...
The most common use is to interpret the relay status nybble to determine whether the four relays opened or closed as directed, and to set the pass / fail conditions as appropriate.
It is also used within the main loop; at various points in the code, a 'configuration byte' is set and program flow is then directed back to the main loop as a 'do this step now but not that' (this section uses ASL and BCC to run through bits 7-3, 1, and 0; when the carry bit is 0, the program drops down to the next bit to be evaluated).
My goal is to end up with something that works in assembly, whether 6801 or 6502, yet intuitively translates to higher level languages (i.e. C++).
This code was riddled with shortcuts and odd programming quirks and I've pried out as many as I could find.
Perhaps I should post the initial configuration check code just to show you what I'm working with...
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: BIT instruction?
I probably wouldn't use BIT here. Instead, I might do something like:
using the BCC instead of BCS around a JMP, and put them all within branch range. It looks like the various places to jump to are all short enough to put them together within branch range.
Code: Select all
LDY #7
LDA $C3 ; fetch status nybble
LSR ; shift Relay A bit into carry
BCC REL_A_TEST_FAIL ; branch to test fail: Relay A
STY $0CEA ; Write pass value to Relay A register
LSR ; shift Relay B bit into carry
BCC REL_B_TEST_FAIL ; Branch to test fail: Relay B
STY $0CEB ; Write pass value to Relay B register
LSR ; shift Relay C bit into carry
BCC REL_C_TEST_FAIL ; Branch to test fail: Relay C
STY $0CEC ; Write pass value to Relay C register
LSR ; shift Relay D bit into carry
BCC REL_D_TEST_FAIL ; jump to test fail: Relay D
STY $0CED ; Write pass value to Relay D registerusing the BCC instead of BCS around a JMP, and put them all within branch range. It looks like the various places to jump to are all short enough to put them together within branch range.
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?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
-
teamtempest
- Posts: 443
- Joined: 08 Nov 2009
- Location: Minnesota
- Contact:
Re: BIT instruction?
BIT is quite handy for waiting until a status line goes to a desired state:
You might see something like this in bit-banged serial i/o, for instance.
Code: Select all
lda #$10 ; status bit is bit #4 in this example
@1 bit ioport
beq @1 ; wait until set (or bne if waiting until clear)
-
Meterman58761
- Posts: 19
- Joined: 05 Jun 2021
Re: BIT instruction?
As for using BIT to wait for one line to change state, that's actually not a bad idea. Will have to see if that would work better than the existing approach.
Anyway, coming back to the original part of my post, for the post-test relay check routine I did end up using BIT rather than LSR paired with BCC / BCS, as the routine which it replaced was several layers deep and a mass of shifts, carries, branches, and even jumps that was scattered out into several sections. 500+ bytes (in 5-6 places) replaced by a ~100-byte subroutine and two JSRs.
Anyway, coming back to the original part of my post, for the post-test relay check routine I did end up using BIT rather than LSR paired with BCC / BCS, as the routine which it replaced was several layers deep and a mass of shifts, carries, branches, and even jumps that was scattered out into several sections. 500+ bytes (in 5-6 places) replaced by a ~100-byte subroutine and two JSRs.