Bitmanipulering i C

3.2.1 Visa en funktion int pack( unsigned short year, unsigned short month, unsigned short day) som packar tre heltal representerande år, månad och dag, i ett heltal som returneras. Det returnerade heltalet ska packas enligt enligt följande figur

int pack( unsigned short year, unsigned short month, unsigned short day) 
{ 
  unsigned int rval = year << 9;
  rval |= month << 5;
  rval |= day;
  return rval;
}

3.2.2 Konstruera en C-funktion int countones( unsigned int val) som räknar antalet 1-ställda bitar hos parametern och returnerar antalet.
int	countones( unsigned int val)
{
  int	retval = 0;	
  while(val)
  {
    if( val & 1 ) 
      retval++;
    val >>= 1;
  }
  return retval;
}

3.2.3 Konstruera en C-funktion int bitcheck( unsigned int val) som undersöker en parameter med avseende på antalet 1-ställda bitar. Funktionen ska returnera 1 om antalet ettor hos parametern är udda, annars ska funktionsvärdet vara 0.
int	bitcheck( unsigned int val)
{
  int	retval = 0;	
  while(val)
  {
    if( val & 1 ) 
      retval++;
    val >>= 1;
  }
  return retval & 1;
}

3.2.4 Konstruera en C-funktion int countzeroes( unsigned int val) som räknar antalet 0-ställda bitar hos parametern och returnerar antalet. Funktionen ska vara portabel, dvs. du får inte göra antaganden om den exakta (maskinberoende) storleken av en int.
int	countones( unsigned int val)
{
  /* Vi räknar antalet ettor och subtraherar dessa från ordlängden */
  int	retval = 0;	
  while(val)
  {
    if( val & 1 ) 
      retval++;
    val >>= 1;
  }
  return (sizeof(int)*8) - retval;
}

3.2.5 En funktion för manipulering av bitar i en operand ska konstrueras. Funktionen deklareras: unsigned int bitconvert(int method, unsigned int value); Returvärdet bestäms av parametern value och de två minst signifikanta bitarna i parametern method (oberoende av parametern i övrigt) enligt:
method (b1b0)=00: returnera sanningsvärdet av value.
method (b1b0)=01: returnera bitvis komplementet av value.
method (b1b0)=10: returnera de 16 mest signifikanta bitarna av value.
method (b1b0)=11: returnera det 32-bitars tal som bildas från det 16 bitars teckenutvidgade extraherade tal från b24-b8 hos value. För denna uppgift gäller att sizeof(int) är 4.
unsigned int	bitconvert(int method, unsigned int value)
{
  unsigned int rv;
  switch( method & 3 )
  {
    case 0: rv = value; break;
    case 1: rv = ( value ^ 0xFFFFFFFF ); break;  // or rv = ~value; 
    case 2: rv = ( value >> 16 ); break;
    case 3: rv = ( (value >>8) & 0xFFFF);
      if (rv & 0x8000 )
        rv = (rv | 0xFFFF0000 );
      break;
  }
  return rv;
}

3.2.6 Bitwise OR används typiskt för att ett-ställa bitar. Bitwise AND används typiskt för att nollställa bitar. Bitwise XOR används typiskt för att invertera bitar. Du måste vara bekväm med att översätta fram och tillbaks mellan decimala, hexadecimala och binära talsystemet. (Oktala talsystemet används sällan nu för tiden.) Du måste behärska AND, OR, XOR, <<, >> och ~ (not).

  1. Skapa en funktion void printBinary(unsigned char byte) som tar en char som parameter och skriver ut denna på binär form, dvs. 1:or och 0:or. T ex 00001001 för byte = 9.
    #include <stdio.h>
    #include <string.h>
    
    void printBinary(unsigned char byte)
    {
        for(int i=7; i>=0; i--) {
            int bit = (byte >> i) & 0x01;
            printf("%d", bit);
        }
    }
    int main()
    {
        unsigned char byte = 128;
        printBinary(byte);
    	return 0;
    }
    

  2. Gör en funktion som tar en 8 tecken lång sträng av 1:or och 0:or och printar ut den som ett decimaltal. Ex: char str[] = "00001001" ska ge utskriften 9 om funktionen anropas med strängen.
    
    #include <stdio.h>
    #include <string.h>
    
    void printDecimal(char str[])
    {
        unsigned char byte = 0;
        for(int i=0; i<8; i++) {
            if(str[i] == '1')
                byte |= (1<<(7-i));
        }
        printf("%d", byte);
    }
    int main()
    {
        char str[] = "00001001";
        printDecimal(str);
    	return 0;
    }
    

Använd dina två funktioner i de föregående uppgifterna för att verifiera dina lösningar nedan.
  1. Skapa en unsigned char c = 128;. Ettställ bit 1, 3 och 5. Antag bitarna numreras 0-7 (1-8 är också vanligt). Verifiera och skriv ut decimaltalet.
    
    #include <stdio.h>
    int main()
    {
        unsigned char byte = 128;
        printBinary(byte); printf("\n");
        byte |= (1<<1 | 1<<3 | 1<<5);
        printBinary(byte); printf("\n");
        printf("%d\n", byte);
    	return 0;
    }
    

  2. Skapa en variabel signed char x = 27;. Invertera alla bitarna och addera 1. Testkör. Vad hände? Varför?
       x ^= 0xff;
       x++;
    
    
  3. Skapa ett program som ger samma utskrift som videon.
    Gör det genom att skifta bitar. Skapa en variabel unsigned short a = 0x8000; där du skiftar ner bitarna varje iteration. Skapa på motsvarande vis en variabel där du skiftar upp bitarna varje iteration. OR:a ihop de två variablerna inför varje utskrift.
          För att vid varje iteration få en blank terminal med texten längst ned, skapa en funktion som varje iteration rensar terminalfönstret alldeles innan du skriver ut texten. Detta kan göras genom att t ex skriva ut c:a 80 st "\n".
          En sak till... För att dina iterationer skall komma i ett lagom tempo så kan du använda operativsystemets sleep-funktion efter varje iteration. Har du skapat en yttre evig loop så gör anropet till sleep (som återför kontrollen till operativsystemet) även att programmet kan vara enklare att avbryta när du stänger console-fönstret.

    Windows: Använd Sleep(millisekunder) tillsammans med #include <windows.h>


    Mac OS: Använd usleep(mikrosekunder) tillsammans med #include <unistd.h>
    	
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h> //(eller <windows.h>)
    
    void printBinary(unsigned short tal)
    {
        for(int i=15; i>=0; i--) {
            int bit = (tal >> i) & 0x01;
            printf("%d", bit);
        }
    }
    void clearscreen()
    {
        for(int i=0; i<80; i++)
            printf("\n");
    }
    int main()
    {
        while (1)
        {
            unsigned short a = 0x8000, b = 0x0001;        
            for(int i=0; i<15; i++)
            {
                clearscreen();
                a >>= 1;
                b <<= 1;
                printBinary(a | b);
                printf("\n");
                // without the sleep, the eternal loop may
                // also cause the program to be harder to 
                // terminate.
                usleep(250000); // för Windows: Sleep(250);
            }
        }
    	return 0;
    }
    
  4. Gör nu ditt eget animerade favoritbitmönster
  5. Lägg till så att texten scrollar omväxlande till höger/vänster så att den ser ut att studsa mot terminalkanterna. (Om du kan, lägg även till så att bitmönstret animeras hälften så fort som texten scrollar). Se videon nedan.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>  // för abs()
#include <unistd.h> //(eller <windows.h>)

...
void printSpaces(int n)
{
    for(int i=0; i<n; i++)
        printf(" ");
}
int main()
{
    unsigned short a = 1, b = 0x8000;
    int scrollWidth = 50; 
    int loopIter = 0;
    while (1)
    {
        clearscreen();
        
        // Hantera horisontell bouncing scroll
        // låt scrolliter loopa runt från 0 t.om. 99; 
        int scrollIter = loopIter % (2*scrollWidth); 
        int pos = 35 + scrollWidth - abs(scrollWidth - scrollIter); 
        printSpaces(pos);
        
        // hantera bit-scroll
        if((loopIter % 2) != 0) { // uppdatera bitmönstret endast varannan iteration.
            a = (a <= 1) ? 0x8000 : a >> 1; 
            b = (b == 0x8000) ? 0x0001 : b << 1;
        }
        printBinary(a | b); 
        
        printf("\n");    
        usleep(33333);
        loopIter++;
    }
    return 0;
}
  1. Om du tittar på lösningen ovan så ser du principen för att lägga till sinsemellan oberoende händelseförlopp. Gör nu så att texten även studsar i y-led. Se videon nedan:
#include <math.h> //för fabs(), sin()
...
        // Hantera horisontell bouncing scroll
        ...
        // hantera bit-scroll
        ...
        // Hantera vertikal studs
        float t = (loopIter % 41) / 40.0f; // t: 0 -> 40
        float y = 10.3f*( fabs(2*(3*t*t - 2*t*t*t) -1)); // ser bättre ut än 
                                                         // en ren t^2-kurva
        for(int i=0; i<y; i++)
            printf("\n");