Computer Systems Fundamentals

#Use & to check whether a bit is set
#include <stdio.h>
int main(void){                                                 //X 
    unsigned int x = 5;        //00000000 00000000 00000000 00000101
    unsigned int mask = 2;     //00000000 00000000 00000000 00000010
                               //-------------------------------------
                               //00000000 00000000 00000000 00000000              
   
    unsigned int result = x & mask;
    if(result != 0){            
        printf("Bit is set\n");     //bit set: bit is equal to 1
    } else {
        printf("Bit is not set\n"); //bit is equal to 0
    }    
                          //MSB                               LSB
    unsigned int y = 7;   //00000000 00000000 00000000 00000111
                          //00000000 00000000 00000000 00000010
                          //00000000 00000000 00000000 00000010 
    result = y & mask;
    if(result != 0){
        printf("Bit is set\n");
    } else {
        printf("Bit is not set\n");
    }    

    return 0;
}

/*
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

*/

Use | to set a specific bit
#include  <stdio.h>


#define BYTE_SIZE 8

int main(void){                  //       X     
    unsigned int x = 5;          //0000 0101     |
    unsigned int mask = 2;       //0000 0010
                                 //---------
                                 //0000 0111  7
                                
    unsigned int result = x | mask;
    printf("%u\n",result);
    
    
    return 0;
}

/*
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

*/
 

Use a negation to create a useful mask to unset a bit
#include <stdio.h>
 

int main(void){                  //       X           
    unsigned char x = 7;         //0000 0111
                                 //1111 1101  &
                                 //----------
                                 //0000 0101                               
    
    printf("Number is %u\n",x);
    
    //First way
    unsigned int mask1 = 0xFD;
    unsigned int result = x & mask1;
    printf("%u\n",result);        
     
       
    //Second way                 //0000 0111    
    unsigned int mask2 = 2;                                                                     
    result = x & ~mask2;
    printf("%u\n",result);    
    return 0;
}

 

Use xor to toggle a bit
#include <stdio.h>
 

int main(void){                             
    unsigned char x = 7;          //0000 0111
    
                                  //       X          
                                  //0000 0111   //7
    unsigned int mask = 2;        //0000 0010   //^
                                  //----------
                                  //0000 0101 
                                  
                                  //0000 0101   //5
                                  //0000 0010   // ^
                                  //----------
                                  //0000 0111
                                
    printf("Number is %u\n",x);
    unsigned int result = x ^ mask;
    printf("%u\n",result);
    result = result ^ mask;
    printf("%u\n",result);
    result = result ^ mask;
    printf("%u\n",result);
    
    return 0;
}

 
01010101 & 
10101010
--------
00000000 
 

01010101 | 
10101010
---------
11111111

 x & 
~x
-----
00000000 

x | 
~x
----
11111111 
 
M      L 
     *
XXXXXXXX    |
00000100    
---------
XXXXX1XX
 

     *
XXXXXXXX    &
11111011
---------
XXXXX0XX
#include <stdio.h>
#include <stdint.h>

int main(void){                 //X
   uint8_t x = 0xC0;      //1100 0000  
   uint8_t mask = 4;      //0000 0100
                          //---------
                          //1100 0100 &
                          //1111 1011
                          //---------
                          //1100 0000
   
   printf("0x%X\n",x);

   //set the bit - make bit equal to 1
   x = x | mask;
    
   printf("0x%X\n",x);
   
  
   //unset the bit - make bit equal to 0        
   x = x & ~mask;       
   
   printf("0x%X\n",x);
   
 
   return 0;
}


Demonstrate C bitwise operations of 16 bit values
```bash
$ dcc bitwise.c print_bits.c -o bitwise
$ ./bitwise

Enter a: 23032
Enter b: 12345
Enter c: 3 a = 0101100111111000 = 0x59f8 = 23032 b = 0011000000111001 = 0x3039 = 12345 ~a = 1010011000000111 = 0xa607 = 42503 a & b = 0001000000111000 = 0x1038 = 4152 a | b = 0111100111111001 = 0x79f9 = 31225 a ^ b = 0110100111000001 = 0x69c1 = 27073 a >> c = 0000101100111111 = 0x0b3f = 2879 a << c = 1100111111000000 = 0xcfc0 = 53184 ```

#include <stdio.h>
#include <stdint.h>
#include "print_bits.h"

void print_bits_hex(char *description, uint16_t n);

int main(void) {

    uint16_t a = 0;
    printf("Enter a: ");
    scanf("%hd", &a);

    uint16_t b = 0;
    printf("Enter b: ");
    scanf("%hd", &b);

    printf("Enter c: ");
    int c = 0;
    scanf("%d", &c);

    print_bits_hex("     a = ", a);
    print_bits_hex("     b = ", b);
    print_bits_hex("    ~a = ", ~a);
    print_bits_hex(" a & b = ", a & b);
    print_bits_hex(" a | b = ", a | b);
    print_bits_hex(" a ^ b = ", a ^ b);
    print_bits_hex("a >> c = ", a >> c);
    print_bits_hex("a << c = ", a << c);

    return 0;
}

// print description then print
// binary, hexadecimal and decimal representation of a value
void print_bits_hex(char *description, uint16_t value) {
    printf("%s", description);
    print_bits(value, 8 * sizeof value);
    printf(" = 0x%04x = %d\n", value & 0xFFFF, value);
}
uint8_t x = 19; 
x =  00010011
     00000100
x >> 2    19/4  = 4


x << 1   00100110  = 19*2 = 38
#include <stdio.h>
#include <stdint.h>

#define BITS_IN_BYTE 8

// extract the nth bit from a value , assuming lsb is bit 0

//  76543210
//       X 
//  00001101
//  00000011
//  00000001&
//----------
//  00000001

       
uint8_t get_nth_bit(uint8_t value, int n) {
    uint8_t result = value >> n;
    result = result & 1U;
    return result;
}

// print the bottom how_many_bits bits of value
void printBits(uint8_t value) {
    int i = BITS_IN_BYTE  - 1;
    while (i >= 0){
        printf("%u",get_nth_bit(value,i));
        i--;
    }
    printf("\n");  
}

//Combining all code into one function and for an unsigned int.
void printBits2(uint8_t a) {
    int j;

    j = BITS_IN_BYTE * (sizeof a);
    while (j > 0) {
        j = j - 1;
        printf("%d", (a >> j) & 1);
    }
    printf("\n");
}



int main(void){
    uint8_t x = 3;     
                              
    printf("%u\n",x);
    //Mult by 4
    x = x << 2;
   
    printf("%u\n",x);   //12
    
    //Divide by 8
    x = x >> 3;         //1
       
    printf("%u\n",x);
   
    //Print Powers of 2
    printf("Powers of 2\n");
    for(int n = 1; n < 7; n++){
        printf("%u: %u\n",n,  1U << n ); //TODO
    }
   
    printf("\nPrinting numbers in binary\n");
    for (int i = 0; i < 25; i++){
        printBits(i);
    }
    
    //printBits(43);
    
    return 0;
}


Demonstrate that shifting the bits of a positive int left 1 position is equivalent to multiplying by 2
```
$ dcc shift_as_multiply.c print_bits.c -o shift_as_multiply
$ ./shift_as_multiply 4
2 to the power of 4 is 16

In binary it is: 00000000000000000000000000010000 $ ./shift_as_multiply 20 2 to the power of 20 is 1048576
In binary it is: 00000000000100000000000000000000 $ ./shift_as_multiply 31 2 to the power of 31 is 2147483648
In binary it is: 10000000000000000000000000000000 $ ```

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "print_bits.h"

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <exponent>\n", argv[0]);
        return 1;
    }

    int n = strtol(argv[1], NULL, 0);

    uint32_t power_of_two;

    int n_bits = 8 * sizeof power_of_two;

    if (n >= n_bits) {
        fprintf(stderr, "n is too large\n");
        return 1;
    }

    power_of_two = 1;
    power_of_two = power_of_two << n;

    printf("2 to the power of %d is %u\n", n, power_of_two);

    printf("In binary it is: ");
    print_bits(power_of_two, n_bits);
    printf("\n");

    return 0;
}
// header file so we use print_bits in several examples
#ifndef PRINT_BITS_H
#define PRINT_BITS_H

#include <stdint.h>
void print_bits(uint64_t value, int how_many_bits);

#endif
two useful functions that we will use in a number of following programs
#include <stdio.h>
#include <stdint.h>

#include "print_bits.h"

// extract the nth bit from a value
int get_nth_bit(uint64_t value, int n) {
    // shift the bit right n bits
    // this leaves the n-th bit as the least significant bit
    uint64_t shifted_value = value >> n;

    // zero all bits except the the least significant bit
    int bit = shifted_value & 1;

    return bit;
}

// print the bottom how_many_bits bits of value
void print_bits(uint64_t value, int how_many_bits) {
    // print bits from most significant to least significant

    for (int i = how_many_bits - 1; i >= 0; i--) {
        int bit = get_nth_bit(value, i);
        printf("%d", bit);
    }
}


Examples of illegal bit-shift operations
#include <stdio.h>
#include <stdint.h>

int main(void) {
    // int16_t is a signed type (-32768..32767)
    // below operations are undefined for a signed type
    int16_t i;

    i = -1;
    i = i >> 1; // undefined -  shift of a negative value
    printf("%d\n", i);

    i = -1;
    i = i << 1; // undefined -  shift of a negative value
    printf("%d\n", i);

    i = 32767;
    i = i << 1; // undefined -  left shift produces a negative value
    printf("%d\n", i);
 
    uint64_t j;
    j = 1 << 33; // undefined - constant 1 is an int 
    j = ((uint64_t)1) << 33; // ok
    printf("%lu\n",j);   

    return 0;
}


Demonstrate use shift operators and subtraction to obtain a bit pattern of n 1s
```
$ dcc set_low_bits.c print_bits.c -o n_ones
$ ./set_low_bits 3

The bottom 3 bits of 7 are ones: 00000000000000000000000000000111 $ ./set_low_bits 19
The bottom 19 bits of 524287 are ones: 00000000000001111111111111111111 $ ./set_low_bits 29
The bottom 29 bits of 536870911 are ones: 00011111111111111111111111111111 ```

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "assert.h"

#include "print_bits.h"

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <exponent>\n", argv[0]);
        return 1;
    }

    int n = strtol(argv[1], NULL, 0);

    uint32_t mask;

    int n_bits = 8 * sizeof mask;

    assert(n >= 0 && n < n_bits);

    mask = 1;
    mask = mask << n;
    mask = mask - 1;

    printf("The bottom %d bits of %u are ones:\n", n, mask);
    print_bits(mask, n_bits);
    printf("\n");

    return 0;
}


Demonstrate use shift operators and subtraction to obtain a bit pattern with a range of bits set.
```
$ dcc set_bit_range.c print_bits.c -o set_bit_range
$ ./set_bit_range 0 7

Bits 0 to 7 of 255 are ones: 00000000000000000000000011111111 $ ./set_bit_range 8 15
Bits 8 to 15 of 65280 are ones: 00000000000000001111111100000000 $ ./set_bit_range 8 23
Bits 8 to 23 of 16776960 are ones: 00000000111111111111111100000000 $ ./set_bit_range 1 30
Bits 1 to 30 of 2147483646 are ones: 01111111111111111111111111111110 ```

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include "print_bits.h"

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <low-bit> <high-bit>\n", argv[0]);
        return 1;
    }

    int low_bit = strtol(argv[1], NULL, 0);
    int high_bit = strtol(argv[2], NULL, 0);

    uint32_t mask;

    int n_bits = 8 * sizeof mask;

    assert(low_bit >= 0);
    assert(high_bit >= low_bit);
    assert(high_bit < n_bits);

    int mask_size = high_bit - low_bit + 1;

    mask = 1;
    mask = mask << mask_size;
    mask = mask - 1;
    mask = mask << low_bit;

    printf("Bits %d to %d of %u are ones:\n", low_bit, high_bit, mask);
    print_bits(mask, n_bits);
    printf("\n");

    return 0;
}


Demonstrate use shift operators and subtraction to extract a bit pattern with a range of bits set.
```
$ dcc extract_bit_range.c print_bits.c -o extract_bit_range
$ ./extract_bit_range 4 7 42

Value 42 in binary is: 00000000000000000000000000101010
Bits 4 to 7 of 42 are: 0010 $ ./extract_bit_range 10 20 123456789
Value 123456789 in binary is: 00000111010110111100110100010101
Bits 10 to 20 of 123456789 are: 11011110011 ```

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include "print_bits.h"

int main(int argc, char *argv[]) {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s <low-bit> <high-bit> <value>\n", argv[0]);
        return 1;
    }

    int low_bit = strtol(argv[1], NULL, 0);
    int high_bit = strtol(argv[2], NULL, 0);
    uint32_t value = strtol(argv[3], NULL, 0);

    uint32_t mask;

    int n_bits = 8 * sizeof mask;

    assert(low_bit >= 0);
    assert(high_bit >= low_bit);
    assert(high_bit < n_bits);

    int mask_size = high_bit - low_bit + 1;

    mask = 1;
    mask = mask << mask_size;
    mask = mask - 1;
    mask = mask << low_bit;

    // get a value with the bits outside the range low_bit..high_bit set to zero
    uint32_t extracted_bits = value & mask;

    // right shift the extracted_bits so low_bit becomes bit 0
    extracted_bits = extracted_bits >> low_bit;

    printf("Value %u in binary is:\n", value);
    print_bits(value, n_bits);
    printf("\n");

    printf("Bits %d to %d of %u are:\n", low_bit, high_bit, value);
    print_bits(extracted_bits, mask_size);
    printf("\n");

    return 0;
}


Print an integer in hexadecimal without using printf to demonstrate using bitwise operators to extract digits
```
$ dcc print_int_in_hex.c -o print_int_in_hex
$ ./print_int_in_hex

Enter a positive int: 42 42 = 0x0000002A $ ./print_int_in_hex
Enter a positive int: 65535 65535 = 0x0000FFFF $ ./print_int_in_hex
Enter a positive int: 3735928559 3735928559 = 0xDEADBEEF $ ```

#include <stdio.h>
#include <stdint.h>

void print_hex(uint32_t n);

int main(void) {

    uint32_t a = 0;
    printf("Enter a positive int: ");
    scanf("%u", &a);

    printf("%u = 0x", a);
    print_hex(a);
    printf("\n");

    return 0;
}

// print n in hexadecimal

void print_hex(uint32_t n) {

    // sizeof returns number of bytes in n's representation
    // each byte is 2 hexadecimal digits

    int n_hex_digits = 2 * (sizeof n);

    // print hex digits from most significant to least significant

    for (int which_digit = n_hex_digits - 1; which_digit >= 0; which_digit--) {

        // shift value across so hex digit we want
        // is in bottom 4 bits

        int bit_shift = 4 * which_digit;
        uint32_t shifted_value = n >> bit_shift;

        // mask off (zero) all bits but the bottom 4 bites

        int hex_digit = shifted_value & 0xF;

        // hex digit will be a value 0..15
        // obtain the corresponding ASCII value
        // "0123456789ABCDEF" is a char array
        // containing the appropriate ASCII values (+ a '\0')

        int hex_digit_ascii = "0123456789ABCDEF"[hex_digit];

        putchar(hex_digit_ascii);
    }
}


Convert an integer to a string of hexadecimal digits without using snprintf to demonstrate using bitwise operators to extract digits
```
$ dcc int_to_hex_string.c -o int_to_hex_string
$ ./int_to_hex_string
$ ./int_to_hex_string

Enter a positive int: 42 42 = 0x0000002A $ ./int_to_hex_string
Enter a positive int: 65535 65535 = 0x0000FFFF $ ./int_to_hex_string
Enter a positive int: 3735928559 3735928559 = 0xDEADBEEF $ ```

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

char *int_to_hex_string(uint32_t n);

int main(void) {
    uint32_t a = 0;
    printf("Enter a positive int: ");
    scanf("%u", &a);

    char *hex_string = int_to_hex_string(a);

    // print the returned string
    printf("%u = 0x%s\n", a, hex_string);

    free(hex_string);

    return 0;
}

// return a malloced string containing the hexadecimal digits of n

char *int_to_hex_string(uint32_t n) {
    // sizeof returns number of bytes in n's representation
    // each byte is 2 hexadecimal digits

    int n_hex_digits = 2 * (sizeof n);

    // allocate memory to hold the hex digits + a terminating 0
    char *string = malloc(n_hex_digits + 1);

    // print hex digits from most significant to least significant

    for (int which_digit = 0; which_digit < n_hex_digits; which_digit++) {
        // shift value across so hex digit we want
        // is in bottom 4 bits

        int bit_shift = 4 * which_digit;
        uint32_t shifted_value = n >> bit_shift;

        // mask off (zero) all bits but the bottom 4 bites

        int hex_digit = shifted_value & 0xF;

        // hex digit will be a value 0..15
        // obtain the corresponding ASCII value
        // "0123456789ABCDEF" is a char array
        // containing the appropriate ASCII values

        int hex_digit_ascii = "0123456789ABCDEF"[hex_digit];

        int string_position = n_hex_digits - which_digit - 1;
        string[string_position] = hex_digit_ascii;
    }

    // 0 terminate the array
    string[n_hex_digits] = 0;

    return string;
}


Convert a hexadecimal string to an integer
```
$ dcc hex_string_to_int.c -o hex_string_to_int
$ dcc hex_string_to_int.c -o hex_string_to_int
$ ./hex_string_to_int  2A
2A hexadecimal is 42 base 10
$ ./hex_string_to_int FFFF

FFFF hexadecimal is 65535 base 10 $ ./hex_string_to_int DEADBEEF
DEADBEEF hexadecimal is 3735928559 base 10 $ ```

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

uint32_t hex_string_to_int(char *hex_string);
int hex_digit_to_int(int ascii_digit);

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <hexadecimal-number>\n", argv[0]);
        return 1;
    }

    char *hex_string = argv[1];

    uint32_t u = hex_string_to_int(hex_string);

    printf("%s hexadecimal is %u base 10\n", hex_string, u);

    return 0;
}

uint32_t hex_string_to_int(char *hex_string) {
    uint32_t value = 0;

    for (int i = 0; hex_string[i] != 0; i++) {
        int ascii_hex_digit = hex_string[i];
        int digit_as_int = hex_digit_to_int(ascii_hex_digit);

        value = value << 4;
        value = value | digit_as_int;
    }

    return value;
}

// given the ascii value of a hexadecimal digit
// return the corresponding integer

int hex_digit_to_int(int ascii_digit) {
    if (ascii_digit >= '0' && ascii_digit <= '9') {
        // the ASCII characters '0' .. '9' are contiguous
        // in other words they have consecutive values
        // so subtract the ASCII value for '0' yields the corresponding integer

        return ascii_digit - '0';
    }

    if (ascii_digit >= 'A' && ascii_digit <= 'F') {
        // for characters 'A' .. 'F' obtain the
        // corresponding integer for a hexadecimal digit

        return 10 + (ascii_digit - 'A');
    }

    fprintf(stderr, "Bad digit '%c'\n", ascii_digit);
    exit(1);
}
copy stdin to stdout xor'ing each byte with value given as argument
```
$ echo Hello Andrew|xor 42
bOFFE
kDNXO] $ echo Hello Andrew|xor 42|cat -A
bOFFE$
kDNXO] $
$  echo Hello |xor 42
bOFFE $ echo -n 'bOFFE '|xor 42

Hello $ echo Hello|xor 123|xor 123
Hello $ ```

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <xor-value>\n", argv[0]);
        return 1;
    }

    int xor_value = strtol(argv[1], NULL, 0);

    if (xor_value < 0 || xor_value > 255) {
        fprintf(stderr, "Usage: %s <xor-value>\n", argv[0]);
        return 1;
    }

    int c;
    while ((c = getchar()) != EOF) {
        //    exclusive-or
        //    ^  | 0  1
        //   ----|-----
        //    0  | 0  1
        //    1  | 1  0

        int xor_c = c ^ xor_value;

        putchar(xor_c);
    }

    return 0;
}


Represent a small set of possible values using bits
```
$ dcc pokemon.c print_bits.c -o pokemon
$ ./pokemon
0000010000000000 BUG_TYPE
0000000000010000 POISON_TYPE
1000000000000000 FAIRY_TYPE
1000010000010000 our_pokemon type (1)

Poisonous 1001010000000000 our_pokemon type (2)
Scary ```

#include <stdio.h>
#include <stdint.h>
#include "print_bits.h"


#define POKEMON_TYPE_BITS 16

#define FIRE_TYPE      0x0001
#define FIGHTING_TYPE  0x0002
#define WATER_TYPE     0x0004
#define FLYING_TYPE    0x0008
#define POISON_TYPE    0x0010
#define ELECTRIC_TYPE  0x0020
#define GROUND_TYPE    0x0040
#define PSYCHIC_TYPE   0x0080
#define ROCK_TYPE      0x0100
#define ICE_TYPE       0x0200
#define BUG_TYPE       0x0400
#define DRAGON_TYPE    0x0800
#define GHOST_TYPE     0x1000
#define DARK_TYPE      0x2000
#define STEEL_TYPE     0x4000
#define FAIRY_TYPE     0x8000

int main(void) {

    // example code to create a pokemon with 3 types

    uint16_t our_pokemon = BUG_TYPE | POISON_TYPE | FAIRY_TYPE;

    print_bits(BUG_TYPE, POKEMON_TYPE_BITS);
    printf(" BUG_TYPE\n");
    print_bits(POISON_TYPE, POKEMON_TYPE_BITS);
    printf(" POISON_TYPE\n");
    print_bits(FAIRY_TYPE, POKEMON_TYPE_BITS);
    printf(" FAIRY_TYPE\n");

    print_bits(our_pokemon, POKEMON_TYPE_BITS);
    printf(" our_pokemon type (1)\n");

    // example code to check if a pokemon is of a type:

    if (our_pokemon & POISON_TYPE) {
        printf("Poisonous\n"); // prints
    }

    if (our_pokemon & GHOST_TYPE) {
        printf("Scary\n"); // does not print
    }

    // example code to add a type to a pokemon
    our_pokemon |= GHOST_TYPE;

    // example code to remove a type from a pokemon
    our_pokemon &= ~ POISON_TYPE;

    print_bits(our_pokemon, POKEMON_TYPE_BITS);
    printf(" our_pokemon type (2)\n");

    if (our_pokemon & POISON_TYPE) {
        printf("Poisonous\n"); // does not print
    }

    if (our_pokemon & GHOST_TYPE) {
        printf("Scary\n"); // prints
    }
    return 0;
}