The standard library includes a start-up module that prepares the environment for running applications written in C. Several versions of the start-up script are available because each processor has different set-up requirements. The msp430-gcc compiler selects the appropriate module based on the processor specified in the command line options.
The start-up module is responsible for the following tasks
Providing a default vector table.
Providing default interrupt handlers.
Initializing the watchdog timer.
Initializing the .data segment.
Zeroing the .bss segment.
Jumping to main(). (A jump is used, rather than a call, to save space on the stack. main() is not expected to return.)
The start-up module contains a default interrupt vector table. The contents of the table are filled with predefined function names which can be overridden by the programmer. The last entry in the table, however, is the address of the reset vector. The “_reset_vector__” is defined as a weak symbol. This means that if the application doesn't define it, the linker will use the version in the library (or module). However, a user defined version will take precedence.
Look at the disassembled code produced by
$ msp430-objdump -DS a.out
a.out: file format elf32-msp430
Disassembly of section .text:
0000fc00 <_reset_vector__>:
fc00: b2 40 80 5a mov #23168, &0x0120 ; #0x5a80
fc04: 20 01
fc06: 3f 40 50 fc mov #-944, r15 ; #0xfc50
fc0a: 3e 40 00 02 mov #512, r14 ; #0x0200
fc0e: 3d 40 00 02 mov #512, r13 ; #0x0200
fc12: 0d 9e cmp r14, r13
fc14: 06 24 jz $+14 ; abs dst addr 0xfc22
fc16: 1d 53 inc r13
fc18: fe 4f 00 00 mov.b @r15+, 0(r14)
fc1c: 1e 53 inc r14
fc1e: 0f 9d cmp r13, r15
fc20: fb 2b jnc $-8 ; abs dst addr 0xfc18
fc22: 3f 40 00 02 mov #512, r15 ; #0x0200
fc26: 3d 40 00 02 mov #512, r13 ; #0x0200
fc2a: 0d 9f cmp r15, r13
fc2c: 06 24 jz $+14 ; abs dst addr 0xfc3a
fc2e: 1d 53 inc r13
fc30: cf 43 00 00 mov.b #0, 0(r15) ; subst r3 with As==00
fc34: 1f 53 inc r15
fc36: 0f 9d cmp r13, r15
fc38: fb 2b jnc $-8 ; abs dst addr 0xfc30
fc3a: 30 40 44 fc br #0xfc44
0000fc3e <_unexpected_1_>:
fc3e: 30 40 42 fc br #0xfc42
0000fc42 <_unexpected_>:
fc42: 00 13 reti
0000fc44 <main>:
fc44: 31 40 80 02 mov #640, SP ; #0x0280
fc48: 30 40 4c fc br #0xfc4c
0000fc4c <__stop_progExec__>:
fc4c: 02 43 clr SR
fc4e: fe 3f jmp $-2 ; abs dst addr 0xfc4c
Disassembly of section .data:
Disassembly of section .vectors:
0000ffe0 <InterruptVectors>:
ffe0: 3e fc interrupt service routine at 0xfc3e
ffe2: 3e fc interrupt service routine at 0xfc3e
ffe4: 3e fc interrupt service routine at 0xfc3e
ffe6: 3e fc interrupt service routine at 0xfc3e
ffe8: 3e fc interrupt service routine at 0xfc3e
ffea: 3e fc interrupt service routine at 0xfc3e
ffec: 3e fc interrupt service routine at 0xfc3e
ffee: 3e fc interrupt service routine at 0xfc3e
fff0: 3e fc interrupt service routine at 0xfc3e
fff2: 3e fc interrupt service routine at 0xfc3e
fff4: 3e fc interrupt service routine at 0xfc3e
fff6: 3e fc interrupt service routine at 0xfc3e
fff8: 3e fc interrupt service routine at 0xfc3e
fffa: 3e fc interrupt service routine at 0xfc3e
fffc: 3e fc interrupt service routine at 0xfc3e
fffe: 00 fc interrupt service routine at 0xfc00
OK. Lets start from the end. Every MSP430 device has interrupt vectors table located at 0xffe0. So, here we can see, that as execution begins, the PC is loaded with the address 0xfc00. “_reset_vector__” is located at this address.
The first thing that happens is the watchdog timer is initialized. Then the program copies the initialized global variables to RAM (0xfc0a - 0xfc20). After this, the uninitialized globals are cleared (0xfc22 - 0xfc3a). After this, we jump to 'main', which is located at 0xfc44.
In main, we copy the value 0x0280 to r1. This initializes the stack pointer, taking into account the space required for local variables.
Next, as long as main does nothing, it jumps to “__stop_progExec__”. At this point the SR is zeroed. The next instruction is jump to “__stop_progExec__”. This is the end of program execution.
In this module, the application uses:
“_etext” - end of the “.text” section. The place where the initial values of global variables are stored.
“__data_start” - the start of RAM.
“_edata” - the end of data RAM (“_edata - __data_start” is the size of the “.data” segment).
“__bss_start” - the place where uninitialized variables resides in RAM.
“__bss_end” - the end of this segment.
“__stack” - the stack.
All of these variables can be overridden with -Wl,--defsym=[symname]=(value) For example, to set the initial stack point to0x280, use -Wl,--defsym=__stack=0x280.
In most cases it is not necessary to redefine these values. They can be obtained from the user application as follows
... extern int __stack; int m; (int *) m = &__stack; /* now m contains the address of the stack */ ...
Please note that these values do not change once they have been initialized.
The startup code adds a litter overhead to the application. The size of the startup code is 80 bytes without interrupt vector table. If you do not like this approach, you can define your own startup code.