Is there a way to address a single bit in C?
Asked Answered
L

6

16

I've done some researching on bit addressable microcontrollers. The only one that came across in my path is the Intel MCS-51(wiki page) which is still used very commonly today... I was wondering if you can directly address a bit in C, for example on the SFR region wiki 8051 memory architecture.

The bits that I address in the SFR, are they bit addressed directly, or is it a bitwise operation in a bitfield that is byte addressed or is it something else completely?

Specifically, from here: Check if a single bit is set, it looks like the bit is directly manipulated with a MOV, I'm wondering if this is possible in C (with extensions) or does it only look like bitwise operation, but in the background there are some compiler stuff that uses bytes only?

As a follow up question, are there any bit addressable modern processors?

Locris answered 10/4, 2012 at 2:5 Comment(0)
A
8

This is not uncommon. For example, SDCC (Small Device C Compiler) is a popular compiler for MCS-51. You'll find the manual here. The most relevant section is 3.4.1, it describes the language extensions for MCS-51:

3.4.1.6 __bit

This is a data-type and a storage class specifier. When a variable is declared as a bit, it is allocated into the bit addressable memory of 8051, e.g.:

 __bit test_bit;

Writing 1 to this variable generates the assembly code:

 D2*00 setb _test_bit

The bit addressable memory consists of 128 bits which are located from 0x20 to 0x2f in data memory. Apart from this 8051 specific storage class most architectures support ANSI-C bitfields4. In accordance with ISO/IEC 9899 bits and bitfields without an explicit signed modifier are implemented as unsigned.

Autarchy answered 10/4, 2012 at 2:44 Comment(0)
R
10

In plain C (without any extensions, whatever they may be), you can declare a variable as a bit field. It can save a lot of typing and is less error prone.

Here is an example program. It declares a bit field with an union with a regular type of the same size.

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct 
    {
        union
        {
            struct {
                int bit0:1;
                int bit1:1;
                int bit2:1;
                int bit3:1;
                int bit4:1;
                int bit5:1;
                int bit6:1;
                int bit7:1;
            };
            unsigned char byte;
        };
    } EigthBits;

    EigthBits b;

    b.byte = 0;
    printf("Will be 0 ==> %d\n", b.byte);

    b.bit4 = 1;
    printf("Will be 16 ==> %d\n", b.byte);
}

Will print this output :

    Will be 0 ==> 0
    Will be 16 ==> 16

It is usefull to set values to individual bit on a control register, for example. You can set more bits (like int two_bits:2;) to suit your needs.

Rentschler answered 10/4, 2012 at 2:42 Comment(5)
Likely not runtime efficient, but explains it, +1Facetious
The code posted here is not portable. printf("Will be 16 ==> %d\n", b.byte); is not necessarily true. The layout and ordering of bit fields is entirely implementation-defined:Mcmullin
"An implementation may allocate any addressable storage unit large enough to hold a bit- field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified."Mcmullin
You are allocating (4 bytes for 1 bit) * 8. So for your theoretical "8 bit" struct you are using 32 bytes.Mabel
Not necessarily @Countour-Integral. As mentionned by AndrewHenie, the behavior is defined by your compiler. Carefull inspection of the generated assembly code, compared to the code you need to write to address bits with bit shifts and masks would tell you what approach is best in your context. This answer favors readability.Rentschler
A
8

This is not uncommon. For example, SDCC (Small Device C Compiler) is a popular compiler for MCS-51. You'll find the manual here. The most relevant section is 3.4.1, it describes the language extensions for MCS-51:

3.4.1.6 __bit

This is a data-type and a storage class specifier. When a variable is declared as a bit, it is allocated into the bit addressable memory of 8051, e.g.:

 __bit test_bit;

Writing 1 to this variable generates the assembly code:

 D2*00 setb _test_bit

The bit addressable memory consists of 128 bits which are located from 0x20 to 0x2f in data memory. Apart from this 8051 specific storage class most architectures support ANSI-C bitfields4. In accordance with ISO/IEC 9899 bits and bitfields without an explicit signed modifier are implemented as unsigned.

Autarchy answered 10/4, 2012 at 2:44 Comment(0)
P
7

In C, you typically read one byte, and then mask the bit you want, but some processor-specific compilers pre-define registers or even individual bits for you. For example, the Keil Cx51 User's Guide defines bit and sfr data types.

You'd use the sfr type like this:

sfr P0 = 0x80;    // Port 0 is accessed at address 80h.
P0 = 0x20;        // Write 20h to Port 0.

To use the byte-at-a-time method, you'd do something like this:

#define SFR (* (unsigned char *) 0x80)  // Address of SFR is 0x80.

#define BIT0 0x01  // LSB of any byte
#define BIT1 0x02
#define BIT2 0x04
. . .
#define BIT7 0x80  // MSB of any byte

// Read BIT1 of SFR. sfrBit1 is 1 if BIT1 is set, 0 if not.
unsigned char sfrBit1 = SFR & BIT1  ?  1 : 0;

// Set BIT0 of SFR.
SFR |= BIT0;

// Clear BIT2 of SFR.
SFR &= ~BIT2;

For convenience, you can define utility macros to set and clear individual bits:

#define SET(reg, bit) reg |=  (1 << bit)  // Sets a bit in reg.
#define CLR(reg, bit) reg &= ~(1 << bit)  // Clears a bit in reg.

SET(SFR, 1); // Set BIT1 
CLR(SFR, 2); // Clear BIT2
Plat answered 10/4, 2012 at 2:18 Comment(0)
O
2

Anything is possible in "C (with extensions)" because C with extensions could be any dialect, bringing you the programming equivalent of a pie in the sky.

In ISO standard C, the smallest individually addressable unit of storage is the byte, represented by the character types.

Access to bits (even bit field members of structures) is performed by accessing an entire cell of surrounding bits.

If a processor can address bits, maybe one way to get to that is through the C compiler's inline assembly language, if it has such thing. Or externally linked assembly language routines.

Oblation answered 10/4, 2012 at 2:19 Comment(3)
You could have explained how that is done, though (which operators are needed and the general principle, or pointers for the asker).Facetious
What's the point: those methods do not actually address one bit!Oblation
Neither does mov which the question references. But effectively you are manipulating one bit out of many, even if in reality the operation happens at byte/word-level.Facetious
P
2

The processor 8051 defines a few instructions to address a single bit to clear to 0, set to 1 or test the value followed by a conditional branch to bypass the following code if the bit was set or clear.

These instructions are very efficient, using only 2 bytes. The fist byte define the instruction and the second is the operand which identify which bit to use, out of 256 bit total.

The first 128 bits are referencing 16 SFR bytes (bits 0 to 7 at address 0x80 for P0, bit 8 to 15 for SFR at addreess 0x88 for P1, etc up to bit 120 to 127 for SFR 0xF8).

The next 128 bits, as described above by Hans Pasant, are referencing the 16 bytes of internal RAM between 0x20 and 0x2F.

The 8051 actually read the entire byte, test, set or clear the specified bit and write all 8 bit back to the same location (except for test). Beside being efficient, these instruction also do not modify any register such as R0 to R7, the accumulator, etc.

The C compiler could easily accept more than 128 "__bit" variables and replace the efficient assembler code mentioned above by the classical binary masking operations described by most people. However, the easiest solution as implemented by Keil compiler is to declare an error when using more than 128 bit variables.

There are some DSP and graphic processors which can address a specified number of bit. This can be very useful when writing efficient compression algorithm, some drawing routine, etc. For example, the TMS34010 from Texas Instruments could define two group of registers using different number of bits for each memory access. A loop could read for example 3 bit at a time using a register from the first group and write 11 bits using a register in the second group. Internally, the memory controller was still reading 16 bit, and writing back 16 bits and was modifying only the specified number of bits inside each 16 bit words. All memory operations are referencing a bit, not a word. For example, the Program Counter is incremented by 16 on execution of each instruction. The hardware would create an exception if the for lower bits would be non zero, but for performing some type of obfuscation, it would have been easy for Texas Instrument to make a chip that would accept to read the opcode with some bit shifted.

The inventors of the C language predicted that one day there could be some processor which could be bit addressable. They explained that the current restriction of reading/writing an entire word when using the bit structures (as described by Ixe013) could be lifted in the future. Unfortunately, the bit addressable processors did not gain much attention in the software world.

Parthenos answered 4/2, 2013 at 12:41 Comment(0)
P
1

That depends on what you mean, by "a way to address". There is no problem in writing some set_bit(address, bit, value), get_bit(address, bit, value) functions which will perform such actions. However get_bit will return you char at least which is a byte.

The byte adressing limitation comes naturally from a hardware, which most modern computer uses. This is very similar to 512 byte sector of a hard drive, which is a minimal IO operation for read or write. This however doesn't stops you from writing single bytes and bits to it.

Pohl answered 10/4, 2012 at 2:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.