32

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?

  • The nature of this question is similar to that of Why does the Z80 include the RLD and RRD instructions? – Wilson May 25 at 16:00
  • 1
    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. – Kelvin Sherlock May 25 at 17:16
  • 4
    You are mistaken, the AND affects the Z flag, not the C flag. – Janka May 25 at 18:28
  • 1
    @fadden Is it an undocumented instruction? What is the opcode? – berendi May 27 at 15:26
  • 2
    @berendi BIT imm is not a 6502 but a 65C02 instruction.Opcode is $89. – Raffzahn May 27 at 17:17
40

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.

29

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. so a quick BIT can be used to check if a key has been pressed. 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.

22

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

  • That BMI, CLC, BIT$38 snippet is indeed a part of many I/O routines. – Janka May 26 at 0:02
  • 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 at 15:06
13

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.

1

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
  • 3
    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?)" – Wilson May 28 at 10:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.