46

The 6502 has a bit instruction which

  • copies two of the bits into the N and V flags,
  • pretends to and the byte with the accumulator, but discards the result and only affects Z.

I'm having a hard time picturing a use for this. And curiously, it's missing any indexed or indirect addressing modes, and so can only be used absolutely or in the zero page.

(Incidentally, it's convenient also for skipping two bytes in the instruction stream in cases where you don't care what happens to the flags, but I don't imagine that's the use MOS had in mind). What kinds of things was it meant for?

6
  • The nature of this question is similar to that of Why does the Z80 include the RLD and RRD instructions? May 25, 2019 at 16:00
  • 2
    Looking at the ProDOS 8 source code, BIT is used to check for a flag in bits 6 or 7, check if a number in memory is negative (again, bit 7), to read i/o memory (without destroying registers). It's also used twice as a clever hack to set the V flag (since there's a CLV instruction but no SEV instruction), and once used to hide other instructions. May 25, 2019 at 17:16
  • 1
    It looks to me like a straightforward bit-test instruction "Z := (dest & mask) == 0" with some weirdness bolted on the side - but then I've never programmed a 6502. May 25, 2019 at 21:33
  • 1
    @fadden Is it an undocumented instruction? What is the opcode? May 27, 2019 at 15:26
  • 2
    @berendi BIT imm is not a 6502 but a 65C02 instruction.Opcode is $89.
    – Raffzahn
    May 27, 2019 at 17:17

6 Answers 6

54

Early MOS documentation (KIM-1 Programming Manual, Synertek SY6500/MCS6500 Microcomputer Programming manual, etc) states:

The BIT instruction actually combines two instructions from the PDP-11 and MC6800, that of TST (Test Memory) and (BIT Test).

...

In addition to the nondestructive feature of the BIT which allows us to isolate an individual bit by use of the branch equal or branch no equal test, two modifications to the PDP-11 version of that instruction have been made in the MCS650X microprocessor. These are to allow a test of bit 7 and bit 6 of the field examined with the BIT test. This feature is particularly useful in serving polled interrupts and particularly in dealing with the MCS6520 (Peripheral Interface Device). This device has an interrupt sense bit in bit 6 and bit 7 of the status words. It is a standard of the M6800 bus that whenever possible, bit 7 reflects the interrupt status of an I/O device. This means that under normal circumstances, an analysis of the N flag after a load or BIT instruction should indicate the status of the bit 7 on the I/O device being sampled. To facilitate this test using the BIT instruction, bit 7 from the memory being tested is set into the N flag irrespective of the value in the accumulator. This is different from the bit instruction in the M6800 which requires that bit 7 also be set on the accumulator to set N. The advantage to the user is that if he decides to test bit 7 in the memory, it is done directly by sampling the N bit with a BIT followed by branch minus or branch plus instruction. This means that I/O sampling can be accomplished at any time during the operation of instructions irrespective of the value preloaded in the accumulator.

Another feature of the BIT test is the setting of bit 6 into the V flag. As indicated previously, the V flag is normally reserved for overflow into the sign position during an add and subtract instruction. In other words, the V flag is not disturbed by normal instructions. When the BIT instruction is used, it is assumed that the user is trying to examine the memory that he is testing with the BIT instruction. In order to receive maximum value from a BIT instruction, bit 6 from the memory being tested is set into the V flag. In the case of a normal memory operation, this just means that the user should organize his memory such that both of his flags to be tested are in either bit 6 or bit 7, in which case an appropriate mask does not have to be loaded into the accumulator prior to implementing the BIT instruction. In the case of the MCS6520, the BIT instruction can be used for sampling interrupt, irrespective of the mask. This allows the programmer to totally interrogate both bit 6 and bit 7 of the MCS6520 without disturbing the accumulator. In the case of the concurrent interrupts, i.e., bit 6 and bit 7 both on, the fact that the V flag is automatically set by the BIT instruction allows the user to postpone testing for the "6th bit on" until after he has totally handled the interrupt "for bit 7 on" unless he performs an arithmetic operation subsequent to the BIT operation.

1
  • 1
    In the kinds of embedded applications for which the CPU was expected to be used, I wonder how the cost and usefulness of making BIT affect the V and N flags directly compared with the cost and usefulness of having all instructions where the two bottom bits of the opcode are 11 behave as a "branch on special input" instruction, and then having systems use external hardware to add up to 64 system-specific input/output instructions (instructions that should perform output would refrain from asserting the special input).
    – supercat
    Dec 22, 2021 at 21:33
40

I'm having a hard time picturing a use for this [BIT]

It's mainly an I/O issue.

The 6502 is in many ways designed especially for control/embedded applications and BIT is a part of this. 6500 specific IO-devices are designed to report any service conditions on bit 7 (and 6). For example the 6522 will set bit 7 of the Interrupt Flag Register when an interrupt condition has been reported. So a 6522 can be polled in a system without interrupts without eating up many instructions. Even an active wait in polling would be just two instructions. As long as it's about bit 6 and 7 BIT operates non destructive - unlike a sequence of LDA/AND - so no registers need to be saved, only flags changed, enabling the insertion of frequent checks.

Same reason why the Apple II's keyboard port puts the key-pressed flag into bit 7. A quick BIT can now be used to check for key press. And only the flags are affected.

As a side-effect BIT is versatile for fast test of flags/semaphores. Bit 6/7 of a flag byte can be tested right away. Likewise (counting) semaphores/locks. In addition bit can be used to test for a numbers sign, again without touching anything but the flags.

And curiously, it's missing any indexed or indirect addressing modes, and so can only be used absolutely or in the zero page.

Not a concern for I/O, as these addresses are usually in fixed locations, especially when thinking embedded systems.


Bottom Line: BIT is an instruction meant to speed up I/O handling.


The design side reasoning is to have an instruction to enable branching on as many bits as possible with minimal impact. Of the four testable bits (N/V/Z/C) one, carry, is as well usable as user flag (SEC/CLC), so keeping that function leaves 3. Using the AND function already implemented sets N and Z, so BIT just needs to suppress copying the result of AND back to A and copying Bit 6 into V.

Sounds like minimal effort to add a quite handy instruction.


For a somewhat more in-depth look at BIT, V and SO see this answer about why there is no SEV.

2
  • 1
    See also the 'Chuck Peddle Special' SO pin, which is a hardware line to set the overflow (V) flag. Have a BIT and/or BVS instruction in a tight loop waiting for the SO pin to be triggered by some hardware device and you've got a REALLY fast polling mechanism. forum.6502.org/viewtopic.php?f=3&t=3342 Jun 28 at 11:58
  • @Eight-BitGuru For the V vs SO issue have a look at this answer. I guess I should link it?
    – Raffzahn
    Jun 28 at 12:03
29

A good example would be reading from the floppy drive on the C64. The incoming clock and data signals of the serial bus are (perhaps not accidentally) wired to bits 6 and 7 of port A on CIA#2, appearing at $DD00.

BIT $DD00

samples both inputs and stores them in the N and V flags. Jumping back conditionally with

BVC $-3

waits while the clock is low, as data must be sampled on the rising clock edge. When it falls through the BVC, the N flag contains the data bit. It can be transferred to the C flag this way

BMI $+4
CLC
BIT $38

just to showcase the secondary use of the BIT instruction, skipping the SEC (opcode $38), where BMI is jumping to.

Now the data bit is in the carry flag, without altering any of the three data registers. It can be shifted into the accumulator, which would hold the data byte being assembled. Meanwhile X can count the number of bits received, and Y can serve as offset when storing the received data with STA (zp),Y

2
  • 2
    That BMI, CLC, BIT$38 snippet is indeed a part of many I/O routines.
    – Janka
    May 26, 2019 at 0:02
  • 2
    Sampling both inputs at a time can be handy. An even cuter exploitation of BIT can be found on the Atari 2600's video chip. It has 15 sprite-collision input flags and 6 controller input flags, but rather than e.g. using two 8-bit registers for the sprite flags and one for the inputs, it puts the collision flags on bits 6-7 of eight addresses, and the controller inputs on bit 7 of six more. What makes this especially cute is that the chip only includes drive circuitry for the top two bits of the data bus; since code won't care what's on the bottom six bits, there's no need to drive them.
    – supercat
    May 28, 2019 at 15:06
15

Simply it gives the developer the ability to set status flags without actually moving any memory or destroying any registers. With a register starved system like the 6502, and the fact that (pretty much) all data movement is through the registers, there can certainly be use cases where this capability can come in handy.

12

I spent a lot of time in the 80s going through 6502 ROMs for home computers. I can honestly tell you what the BIT instruction is used for most of the time in practice.

Mostly it's not used for any calculation of interest. It's used as a filler instruction to allow multiple entry points to a routine, each with different common parameter values. I don't think I ever saw it used for the intended purpose. (Maybe once or twice.)

So for example if you had a routine which prints an ASCII character, you might commonly want to print space, or carriage return or things like '?' on the Commodore machines etc. and to save space in the ROM they just used the BIT instruction to isolate LDA instructions, or other loads. For example:

label_print_CR:
   LDA #$0D   - carriage return
   .byte $2C  (BIT instruction op code)
label_print_space:
   LDA #$20   - space
   .byte $2C
label_print_question_mark:
   LDA #$3F   - '?'
label_print_character:
   main code to print character in A

That would assemble to be:

A9 0D 2C A9 20 2C A9 3F etc..

If you disassemble it you get:

LDA #$0D
BIT $20A9
BIT $3FA9
etc

so the BIT is doing nothing, but it's giving you these cunning specific extra entry points to the routine for printing.

Here's the entry points for the Commodore PET V4.0 BASIC (I don't have the code unfortunately.)

bb1d    strout  Output String
bb3a    outspc  Output Format Character
bb41        -Print '<cursor right>'
bb44        -Print '?'
bb46    -   Output Character in A

You can clearly see the entry points are just 2-3 bytes apart for 41, 44, 46 so there's no way they're using branches or jumps to load and jump into the general routine.

This application of the BIT instruction is just totally ubiquitous across 6502 machines of that era. I used it in my own code all the time. As you can tell from the disassembly it can be rather confusing as to what they're doing.

This is from the 1976 Microsoft 6502 BASIC listing which you can check out here https://www.pagetable.com/docs/M6502.MAC.txt

DEFINE  SKIP1,  <XWD ^O1000,^O044>  ;BIT ZERO PAGE TRICK.
DEFINE  SKIP2,  <XWD ^O1000,^O054>  ;BIT ABS TRICK.

Note these values are in octal so that's $24 and $2c for the two byte and three byte BIT instructions.

Here's an example:

PARCHK: JSR CHKOPN      ;ONLY POSSIBILITY LEFT IS
    JSR FRMEVL      ;A FORMULA IN PARENTHESIS.
                ;RECURSIVELY EVALUATE THE FORMULA.
CHKCLS: LDAI    41      ;CHECK FOR A RIGHT PARENTHESE
    SKIP2
CHKOPN: LDAI    40
    SKIP2
CHKCOM: LDAI    44
;
; "SYNCHK" LOOKS AT THE CURRENT CHARACTER TO MAKE SURE IT
; IS THE SPECIFIC THING LOADED INTO ACCA JUST BEFORE THE CALL TO
; "SYNCHK". IF NOT, IT CALLS THE "SYNTAX ERROR" ROUTINE.
; OTHERWISE IT GOBBLES THE NEXT CHAR AND RETURNS,
;
; [A]=NEW CHAR AND TXTPTR IS ADVANCED BY "CHRGET".
;
SYNCHR: LDYI    0
    CMPDY   TXTPTR      ;CHARACTERS EQUAL?
    BNE SNERR
CHRGO5: JMP CHRGET
2
  • 5
    From the OP: "(Incidentally, it's convenient also for skipping two bytes in the instruction stream in cases where you don't care what happens to the flags, but I don't imagine that's the use MOS had in mind). What kinds of things was it meant for?)" May 28, 2019 at 10:50
  • @OmarL - To be somewhat fair to Robotbugs, I didn't actually know what you meant when you referred to this usage briefly in your question, so even though this wasn't properly answering your question, it's quite a useful bit of info for someone who was unaware of it. Embarrassingly, I once wrote an entire commercial game in 6502 while being unaware of this trick. (-_-)
    – Aiken Drum
    Oct 22, 2021 at 8:49
3

I'm looking at a 1983 Apple][+ source code listing using four consecutive BIT instructions to put the machine into full screen hires graphics mode:

BIT $C057    Set Hi-Res graphics
BIT $C054    Set graphics page one
BIT $C052    Set full screen graphics
BIT $C050    Set graphics mode

IIRC, on the Apple ][ just reading addresses flipped graphics modes around, so it would appear this simply avoided trashing e.g. the A register in practice.

3
  • Not just the Apple][ did this, Acorn machines too. Where there are CRTC or other controller chips whose registers must be set, this was, as you say, a trick to avoid changing CPU registers unnecessarily.
    – Chenmunka
    Dec 22, 2021 at 18:23
  • Interestingly, for applications that wanted to be able to modify any bit without affecting any other, having behavior controlled entirely by addresses, and ignoring any distinction between reads and writes, was cheaper and easier than trying to latch written data. On the 6502, all of the address lines will be driven well before the start of the phi2 clock, but when performing a store, the data to be written won't be valid until some time after the start of phi2 (but before the end of phi2). Using a latch would require generating a signal that became active after data became valid...
    – supercat
    Jun 28 at 14:59
  • ...but before the next time the address could change. Since all address pins will be valid for the entire time phi2 is high, ignoring written data is cheaper than trying to use it, though I'll admit I'm curious why common practice was to use loads rather than data-ignored stores.
    – supercat
    Jun 28 at 15:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .