Wednesday, September 23, 2015

Variable attributes: Visibility - Scope - Lifetime

Ø  Variable Attributes:
Ø  Visibility:
Ø  void func()
       {
        int x;
        {
         // x is visible here
        }
       }

Ø  void func()
       {
        int x;
        {
         int x;
         // outer x is not visible here
        }
       }

Ø  Scope:
è Global scope – file scope – function scope – block scope
è In the previous example:
Ø  outer x scope is its block (outer block)
Ø  but it is not visible in the inner block (due to inner block variable with the same name)

Ø  Life Time:
è For local variables:
Ø  static: lifetime = program time
Ø  automatic: life time = function time
Ø  dynamic: malloc() and free()
è For global variables (global and file local)

Ø  lifetime = program time

if-elseif Statement: Most common conditions shall be handled first

·         Discuss the overhead of branching and why the most common case should be tested first in the (if – else) if construct to speed up execution.
o   Modern processors use pipelining.
From Wikipedia
o   This means that multiple commands are served in parallel

o   Ex:  in clock cycle 4
§  Cmd1: Fetch from memory
§  Cmd 2: Decode to the ALU
§  Cmd 3: Execute
§  Cmd 4: Write the result to the register or the memory
o   This requires that the CPU know the next commands to execute
o   If we have conditional branches, then, the CPU will not be able to know the next commands, and then it will not be able to utilize the pipelining.

o   Hence, it is recommended that the most common condition be the first condition in if-else statement, so we do not have a lot of conditional branching.

Create/Build C Project in VS C++

How To Create C Project in VS C++?
To edit your C program:
  1. From the main menu select File -> New -> Project
  2. In the New Project window:
    Under Project types, General > Empty Project
    Name your project, and specify a location for your project directory
    Click 'OK', then 'next'
  3. In the Application Wizard:
    Select Console application
    Select Empty project
    Deselect Precompiled header
  4. Once the project has been created, in the window on the left hand side you should see three folders:
    Header Files
    Resource Files
    Source Files
  5. Right-click on Source Files and Select Add-> New Item
    Select Code, and give the file a name
    The default here will be a file with a *.cpp extension (for a C++ file). After creating the file, rename it as a *.c file.
To compile and run:

  1. Press the green play button.
  2. By default, you will be running in debug mode and it will run your code and bring up the command window.
    To prevent the command window from closing as soon as the program finishes execution, add the following line to the end of your main function:
    getchar(); 
    This library function waits for any input key, and will therefore keep your console window open until a key is pressed.
  3. Another alternative is to use: 
    Or debug > Start without debuging
Precompiled Headers
Precompiled headers are a mechanism to speed up compilation by creating a partially processed version of some header files, and then using that version during compilations rather than repeatedly parsing the original headers.

Resource Files in VSC++
There are 3 folders in the “Solution Explorer Window” in VSC++:
·         Header Files: (.h)
·         Source Files: (.c)
·         Resource Files: (exe-images-...etc)

Win32 Vs Win64
·         Win32 has 32-bit address line à can support up to 4 GBytes RAM
·         Win64 has 64-bit address line à can support larger size RAM
·         Win32 Application shall run on both Win32 and Win64 system
·         Win64 Application shall run only on Win64 system

Assuming that we use Win64 machine for development, and we want the program to run on Win32 machines:
Host Platform: Win64 – Intel Processor
Target Platform: Win32/64 – Intel Processor

Notes:

  • Some compiler fires a warning in case of using undefined function. And assumes it is externed.
  • If the linker did not find the function externed, then it will fire an error
  • The manifest is not important for c, you can run the exe directly
  • Generated exe file is not pure machine code? it is OS dependent!

Sunday, September 20, 2015

Memory

Memory Types:


  • RAM
    • Data memory
    • Volatile
    • Fast
  • EEPROM / Flash
    • Program memory
    • Nonvolatile
    • From Wikipedia
    • Slow
  • Virtual memory
    • The OS combines regions of the RAM + regions of the Hard disk, and provide virtual addressing for them as if they are contiguous, to be used as a RAM
    • Have larger RAM, but slower
  • Cache memory
    • Faster than rams
    • Data cache, contains the data that is accessed frequently
    • Program cache, contains instructions that is being executed


Memory Management
  • The OS is responsible for allocating/deallocating/moving the heap/stack of the different processes

Friday, September 4, 2015

#define macro with a return value!

Can be done in gcc:

#define IS_DIGIT(x) ({\
int l;\
if (x == 1){\
l = 1;\
}else{\
l = 0;\
}\
l;\
})

The above macro returns an integer l. Can be used as follows:

int x = IS_DIGIT(1);

switch vs if

if the cases of switch are consecutive numbers, then switch may be more optimized than if

Example:

If worst case--> 4 comparison

If (x == 1){
    body 1;
else if (x == 2){
    body 2;
else if(x == 3){
    body 3;

else if(x == 4){
    body 4;
}

Assembly:

beq x,1,L1
beq x,2,L2
beq x,3,L3
beq x,4,L4
...
...
...
L1:
Body 1

L2:
Body 2

L3:
Body 3

L4:
Body 4




switch worst case --> 1 comparison

switch (x){
    case 1: 
        body1;
        break;
    case 2: 
        body2;
        break;    case 3: 
        body3;
        break;    case 4: 
        body4;
        break;}

Assembly:

jumpIfLessThan x,4,switch+x-1
...
...
...
switch:
    L1
    L2
    L3
    L4

...
...
...
L1:
Body 1

L2:
Body 2

L3:
Body 3

L4:
Body 4


Modern compilers output the same assembly for both switch and if

Wednesday, August 12, 2015

Structures

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


Structures, Unions and Enumerations are declared/defined similar to each other

  • struct tag{   type member_name;
       type member_name;   type member_name;
    } variable_list;
  • union tag{   type member_name;
       type member_name;   type member_name;
    } variable_list;
  • Enumeration tag{   enumeration list
    } variable_list;
Where, tag is optional, variable list is optional, but at least one of them must exist

  • For bit fields, the bits runs from left to right or from right to left dependent on the machine
  • typedef syntax:
    typedef type_name new_name;

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