Wednesday, August 5, 2015

Expressions

While reading in the book "The complete C Reference". I got some notes!

Expressions

  • DataTypes:
    • char: 1 byte
    • int
    • float
    • double
    • void
  • Modifiers:
    • signed
    • unsigned
    • long 
    • short
  • DataTypes with modifiers (some notes)
    • the default is "signed"
      Ex:
      • char = signed char
    • Sizes:
      • char: 8 bits
      • short int: 16 bits
      • long int: 32 bits
      • long long int: 64 bits (added by C99)
      • float: 32 bits
      • double: 64 bits 
      • long double: 80 bits (added by C99)
  • Identifiers (variable and functions names, labels, ...etc) :
    • first character: "_" or a letter
    • other characters: "_" or letter or number
    • significant charachters:
      • internal identifiers (used only in the same file)
      • external identifiers (used in more than one file, like global variables and functions)
      • C89:
        • for internal identifiers: 31 characters are significant
        • for external identifiers: 6 characters are significant ("studentName" and "studentNameCon" will be treated as the same identifier!)
      • C99:
        • for internal identifiers: 63 characters are significant
        • for external identifiers: 31 characters are significant
  • Variables
    • C89: all variables shall be declared in the beginning of the block
    • C99: can be defined anyway
  • 4 C Scopes:
    • File Scope (variables defined in file scope are global)
    • Block Scope (variables defined in a block scope, are local to there block, also variables defined in the function definition (formal parameters) are local to the function block scope )
    • Prototype Scope (variables declared in function prototype, are local to the prototype)
    • Function Scope: applies only to the labels. (so block scope does not apply to labels, function scope is applied instead!)
      • the following code is not valid
        void func1(){
         
        fun1: goto fun2;
        }
        void fun2(){
         
        fun2: goto fun1;
        }
      • the following code is valid
        void fun1(){
         printf("fun1");
            {
               
        block1: printf("block1");
            }
            goto
        block1;
        }
  • Type Qualifiers:
    • const
      • Saved in ROM
      • The program can not change this variable
      • Other component other than the program can change the variable (ex: hardware device)
    • volatile
      • To highlight that this variable may be changed, without explicit assignment in the program
        ex:
        may be changed by HW device, by operating system, ...etc
      • This prevents compiler optimization like the following
        • y = x*3/(5x+2);    =======>     y = x*3/(5x+2);
          z = x*3/(5x+2);                 z = y;
  • Storage Class Specifiers:
    • extern  ==> the variabale is defined in other file
      • In some compilers, it is not mandatory to use "extern" key word
    • static
      • in file scope ==> the variable is file global (internal)
      • in block scope ==> the variable shall be initialized only once at the program startup
    • register
      • This variable shall be stored in a register if applicable (faster access)
      • If no registers are available, define them some how to be faster in operations.
      • Allowed for local variables.
      • Theoretically, can be ignored by the compiler if not possible, but this seldom happens.
    • auto
      • The variable is local to the function (no need to write this! all function variables are auto by default)
  • Constants
    • The compiler fits numeric constants to data types as follows:
      • Default fit
        • Integer constant: to the smallest data type fits
          • 16 >> short
          • 100000 >> long
        • Floating point constant: to double
          • 16.3 >> double
      • postfix fit
        • 12.9F or 12.9f >> float
        • 12.9L or 12.9l >> long double
        • 12U or 12u >> unsiged short
        • 12L or 12l >> long int
    • Hexadecimal and Octal
      • 0x10 ==> Hexadecimal
      • 010 ==> Octal
  • Operators:
    • Assignment operator "="
      • Multiple Assignment
        • x = z = 1;
      • Compound assignment
        • x += 1;
    • Arithmatic operators
      • ++ , --
      • - (unary operator)
      • + , / , %
      • + , - 
    • Relational and logical operators
      • !
      • > , >= , < , <=
      • == , !=
      • &&
      • ||
    • Bitwise Operators
      • & , | , ^ , ~
    • Other Operators
      • ? (ternary operator)
      • pointers: * , &
        • pointer operators and unary "-" operator have the highest precedence
    • sizeof()
      • unary operator
      • run time operator
      • returns the size of the operand (in bytes)
      • the return value is of type size_t, where:
        "size_t" is defined to be "unsigned int", using typedef
    • Comma operator
      • separates expressions
      • the result is the last expression result
      • x = (y = 5, y + 1);
        this means:
        • put y = 5
        • then put x = y + 1
    • dot (.) and  arrow (->)
      • dot (.)
        • for struct/union member reference using the struct
      • arrow (->)
        • for struct/union member reference using the struct pointer
    • () and []
      • ()
        • for precedence
      • []
        • for array indexing
    • Precedence
      • Unary operators and ?
        • associate from right to left
      • Other operators
        • associate from left to right
      • Precedence:
        • ()    []    ->    . 
        • !    ~    ++    --    -    (type)    *:at address    &:address of    sizeof
        • *    /    %
        • +    -    
        • <<    >>
        • <    <=    >    >=
        • ==    !=
        • &
        • ^
        • |
        • &&
        • ||
        • ?    :
        • =    +=    -=    *=    /=    etc
        • ,
    • Type promotion in expressions:
      • char,  short ==> int
      • if one operand is long double ==> the other is promoted to long double
      • else if one operand is double ==> the other is promoted to double
      • else if one operand is float ==> the other is promoted to float
      • else if one operand is unsigned long ==> the other is promoted to unsigned long
      • else if one operand is long ==> the other is promoted to long
      • else if one operand is unsigned int ==> the other is promoted to unsigned int
    • Example: 
      • unsigned char x = 0xFF
      • if (~x == 0x00) // evaluates to false
        • because x will be promoted to unsigned int -> 0x000000FF
        • ~x = 0xFFFFFF00

Saturday, June 27, 2015

C Overview


  • History
    • Dennis Richie, invented it, to write the Unix with it -> 1970
    • Previously Unix was written by assembly -> Bad portability :(
      • Rewrite Unix again for each new processor
    • ANSI (American National Standardization Institute), released the first C standard in 1989 ( C89 )
    • Amendment 1 was added to C89 in 1995
    • C89 with Amendment 1 is the base of C++
    • C89 with Amendment 1 is called the C Subset of C++
    • C99 was released by ANSI in 1999
      • Nearly the same as C89  + some added features (Such as: Variable Length Arrays and strict pointer qualifier)

  • Middle Level Language
    • High level features:
      • C is like the High Level Language in the following features:
        • Portable (easy to adapt the program written for a platform to another platform)
        • Supports DataTypes
          • A datatype defines
            • A set of values that the variable can store
            • A set of operations that can be performed on that variable
      • C is unlike the High Level Languages in the following features:
        • C does not support Run-Time Error Checking, like "array index out of bound"
        • C is a Weakly typed language
          • Implicit casting is allowed
          • Implicit casting happens for arguments that does not match the parameter type.
        • C has few keywords:
          • 32 Keyword in C89
          • 5 more Keywords in C99
          • BASIC (a high level language) defined 100 keywords
    • Low Level Features:
      • Manipulation of Bits, Bytes and Addresses
C key words,  from "The Complete C Reference - 4th Edition"
  • C is a Structured Language
    • The code consists of component blocks (functions - while - if - ...etc )
    • goto usage is either forbidden or discouraged
    • Assembly is not a structured language, cause the code contains jumps and branches -> Spaghetti code!

  • C is a programmer language
    • Unlike BASIC language, which is developed for non-programmers to solve simple problems.

  • Compiler Vs Interpreter
    • Interpreter parses one line of code at a time, execute it.
    • Compiler parses the whole program, generates machine code, that is executed.
    • Interpreter parses the code line each time it is being executed
    • Compiler parses the code lines only once.
    • Java is designed for interpretation.
    • C is designed for compilation (However we can make C interpreters, however will not best utilize C)

Friday, June 26, 2015

ET-STM32F103 ARM Cortex board get started

  • Board ET-STM32F103
  • Based on ARM CORTEX-M3
  • Keil uVision
    • can be used to build the project
    • can be used for debugging, using HW "ULINK".
  • Code Worior
    • I was able to build the project using code worrior
    • I opened the debugger, and tried to debug offline (need more investigation)
  • STMicroelectronics Flash loader: used to flash the board with the hex file
    • Connect the usb-to-serial to UART1
    • Switch To Bootloader (from the boot push button on the board)
    • Press reset (from the reset push button on the board)
    • Open the "STMicroelectronics Flash loader"
      • COM3 (check the port from device manager)
      • Baud 115200
      • 8 Bits
      • Parity: even
      • Echo: disabled
      • Timeout: 5

Friday, June 5, 2015

Compiler

Compilation Steps
Very good tutorial
Very good Video tutorial

  • Pre-processing
    • Process the # Directives
  • Lexical Analysis
    • Strips out white spaces and comments
    • Divide the source code into lexemes, Outputs stream of tokens
    • Generates error on illegal lexemes: example:
      • 1_x = 1 + 2;    --> error: identifier the start with number
  • Syntax Analysis
    • Check the code against grammar, generate errors for wrong grammar.
    • Outputs a parse tree
    • Example of errors:
      • x = 1 + ;       --> can not generate parse tree
      • struct mystruct g,x;
        i = g * x;       --> i is not defined, g and x can not be multiplied, however no error is generated from syntax analyzer.
  • Semantic Analysis
    • Program symbol table is created, debug info is inserted
    • Checks the code for logical 
    • Error Examples:
      • struct mystruct g,x;
        i = g * x;    --> the multiplication operation is illegal for structs, i is not defined
      • warning: variable used before initialization
      • int i = "hi";  --> warning: implicit conversion from pointer to integer
  • Intermediate Code Generation
    • IR, Intermediate code Representation, is a machine code independent representation.
    • This helps to keep the parsing part of the compiler unchanged for different targets, while only the synthesis part is changed from target to target.
    • Output: AST (Abstract Syntax Tree) or Pseudo Code.
  • Machine Independent Code Optimization
    • Loop Unrolling
    • Expanding inline functions
    • Dead code removal
  • Code Generation
    • Conver IR code to machine opcodes
  • Machine Dependent Code Optimization
    • Register Allocation

More Details

  • Lexical Analysis (Tokenizing)
    • Input:
      • Is the c code
      • It consists of lexemes
    • Output
      • Tokens
        • a token is a <Token Class, "lexeme"> pair
    • Ex:
      • if (i == j)
            z = 0;
      • This is read by the parser as follows:
        • if (i == j)\n\tz = 0;
      • Step 1: Devide into lexemes:
        • |if| |(|i| |==| |j|)|\n\t|z| |=| |0|;|
      • Step 2: Identify the "Token Class" of each "lexeme"
        Hence, generate the tokens: <class,"lexeme"> pairs
        • <keyword,"if">
        • <whitespace," ">
        • <PAREN_OPEN,"(">
        • <identifier,"i">
        • <whitespace," ">
        • <operator,"==">
        • <whitespace," ">
        • <identifier,"j">
        • <PAREN_CLOSE,")">
        • <whitespace,"\n\t">
        • <identifier,"z">
        • <whitespace," ">
        • <EQ,"=">
        • <whitespace," ">
        • <integer,"0">
        • <SEMI_COLON,";">
      • Token Classes:
        • Whitespace
          • nonempty sequency of blanks, new lines or tabs
        • Integers
        • Keywords
        • Identifiers
        • Operators
        • Normally each "punctuation" lexemes, has its own class:
          • PAREN_O:           (
          • PAREN_C:           )
          • SEMI_COLON:    ;
          • EQ:                       =
  • Parsing (Syntax Analysis)
    • Input:
      • Sequence of tokens from the lexer
    • Output:
      • Parse Tree
    • Parser does the following:
      • Ensure that the the tokens follow C rules, to avoid syntax errors
      • Generates Parse tree:
      • Intermediate representation:
        • AST: Abstract Syntax Tree 
          • Also a pseudo code can replace AST
          • If the compiler supports different languages on different targets, then this AST or Pseudo code is machine independent
          • AST is like the Parse tree, but removing some unneeded info
    • Example:
      Parse Tree:

      The pic is from Online Courses Compiler Course

      Parse Tree Has some unneeded info:

      The pic is from Online Courses Compiler Course
  • AST (Abstract Syntax Tree):
    Generated as a sort of Intermediate Representation

    The pic is from Online Courses Compiler Course

  • Debug info: (mapping source code to machine code) maps functions, instructions in the mapped binary program, to the source code

        binary instruction => Item name, Item type, file name, line number, ...etc)
  • Symbol table: All identifiers in the source code is related to their memory segments and addresses

Compare signed to unsigned

If you have this code:

main(){
    unsigned int x = 5;
    int y = -3;
    if (y>x)
        printf("y is bigger\n\r");
}

What is the output of this?


  • int = signed int
  • when comparing unsigned to signed, the signed is casted into unsigned
  • in our case
    • x = 00000005
    • y = FFFFFFFD   (MSB = sign bit = 1, the other bits shall be the 2's complement of 3)
    • casting u into unsigned, then comparing, y > x --> true
    • the output: "y is bigger"

What about normal operation?
main(){
int x;
int y = -10;
unsigned int z = 4;
x = y + z;

printf("%d\n\r",x);
}

// -6?
// Also y will be casted to unsigned, then added to z,  the result will be correct, thanks to the 2's complement representation
// y + z = FFFFFFF6 + 00000004 = FFFFFFFA = - 6


Ex:

main(){
short x;
 long x2;
short y = -10;
unsigned short z = 4;
x = y + z;
 x2 = y + z;
 x3 = y + (short)z;

printf("%d, %d, %d\n\r",x, x2, x3);
}

in 16 bit machine:

  • y + z = 0xFFF6 + 0x0004 = 0xFFFA
    • x = 0xFFFA
      • Since x is signed, then x = - 6
    • x2 = 0x0000FFFA
      • Since x2 is signed, then x = + 65530
      • No sign extension happened. because (y + z) is unsigned
    • x3 = 0xFFFFFFFA
      • Since x3 is signed, then x = - 6
      • sign extension happened because (y + z) is signed
in 32 bit machine:
  • y + z = 0xFFFFFFF6 + 0x00000004 = 0xFFFFFFFA
    • x = x2 = x3 = - 6

Wednesday, May 27, 2015

Functions and Stack



                       ###########                                                           ######## 
                       # Registers #                                                           #  Stack #
                       ###########                                                           ########
###################################        ####################################
####            Stack Pointer (sp)                ###       ###                local variable n                    ###
###################################        ####################################
####              Fame Counter (fc)             ###       ###                .........................                    ###
###################################        ####################################
####            Link Register (lr)                ###       ###                 local variable 1                  ###
###################################        ####################################
####           Program Counter (pc)          ###       ###             Passed Parameter n                ###
###################################        ####################################
                                                                              ###             ...............................                ###
                                                                              ####################################
                                                                              ###             Passed Parameter 1                ###
                                                                              ####################################
                                                                              ###    caller function lr (return address)   ###
                                                                              ####################################
                                                                              ###  caller function fp (frame pointer)     ###
                                                                              ####################################
                                                                              ###              same block repeats              ###
                                                                              ###              for caller functions              ###
                                                                              ###              ................................              ###
  • Stack pointer (sp) register:
    • Is a HW register, which points to the top of the stack
    • As local variables are defined, they are added to the stack, and the stack pointer value changes.
    • Since the stack pointer is changing, we can not reference the function parameters and local variables as offset from the sp value
    • Instead, we reference the function parameters and variables by adding offset to the frame pointer (fp)
  • Frame Pointer (fp) register
    • Frame pointer register, points to the bottom of the stack part that is related to the current function
    • By other words, fp = sp, before adding the function parameters or local variables.
    • fp value is constant for a function, hence it is used to reference the function parameters and local variable.
  • Program Counter (pc) register:
    • A register that points to the next instruction to be executed.
  • Link Register (lr):
    • When calling a function, a branch happens from the normal sequence to the address of this function.
    • The return address is saved in the link register.
    • After the function finishes, pc = lr
  • When a function call happens:
    • Current lr is saved to stack --> Update sp
    • lr = pc + 1 (return address is saved to lr)
    • Current fp is saved to stack --> Update sp
    • Current sp is saved to fp
    • Parameters are moved to stack --> Update sp
    • Local variables are created on stack --> Update sp
    • Parameters and local variables are refered to as [fp + offset]
    • After the function is finished:
      • It returns the return value in a special register
      • sp = fp
      • pc = lr
      • fp = saved fp
      • lr = saved lr



Sunday, May 24, 2015

Embedded C Build Process


  • Pre-Processor:
    • .c + .h --> .i
    • .c + .h --> .i
    • .c + .h --> .i
    • Stripes out the comments
    • Substitute the #include
    • Substitute the # macros
  • Compiler:
    • .i --> .s --> .o
    • .i --> .s --> .o
    • .i --> .s --> .o
    • from c to assembly
    • the assembly is written in machine code
    • each .o file contains:
      • symbol table
        • symbol name | address in memory | symbol size | symbol section (bss - data - rodata - text - ...etc)
        • all addresses are relative to the file
        • for external variables, the addresses are not determined. (to be determined after linking)
      • sections
        • bss: uninitialized global variables
        • data: initialized global variables
        • rodata: constants
        • text: code
  • Linker:
    • .o + .o + .o + startup.o + standard_libs.o --> app.o
    • standard_libs.o
      • to resolve functions like printf(), ...etc
    • startup.o
      • disable all interrupts
      • copy initialized variables from ROM to RAM
      • initialize uninitialized data
      • allocate space for stack and initialize it
      • initializes the processor stack pointer
      • create and initialize the heap
      • enable interrupts
      • call main
      • startup is written by the developer as assembly or c, then compiled and linked with the application
      • When the reset line is disabled (once the processor start working), the program counter points to the start up
      • "Start up" can be called "boot loader", usually it is called a boot loader if it comes more complicated than just doing the above points.
        A "boot loader" can load OS, initialize HW, ...etc.
        If a boorloader is interupted "by a push button, or an escape method, ...etc", it stops loading the main program, and wait for a new main program to be flashed ...etc.
    • app.o:
      • all text, bss, data, rodata sections of all the input object files are merged together, hence:
        • symbol table contains zero unresolved symbols
        • bss contains all uninitialized variables
        • data contains all initialized variables
        • rodata contains all constants
        • text contains all code
      • In case of embedded systems: 
        • the addresses needs to be mapped to the system memory
        • this is done using a "linker script" in the "locating" process
  • Locator:
    • app.o + linker script --> targetFile
    • To be continued



You can use the files in the this link, a long with the following commands to see the above info in action:
// to run preprocessor-compiler-linker-locator, saving all temp file
gcc main.c add.c -o adder -save-temps
// you can view the preprocessor output
*.i
// you can view the assembly
*.s
// to view the object files, and target file:
objdump -d -t -s adder > adderObjectDump
objdump -d -t -s main.o > mainObjectDump
objdump -d -t -s add.o > addObjectDump

    where:  -d // display the executable sections in assembly form (disassemble)
                 -t // print symbol table
                 -s // display the contents of the sections