I came across some articles and books that saying when, I write in high-level language like C, startup code is used to prepare for my main code but when I write my program in pure Assembly there is no startup code why is that?!

  • 5
    \$\begingroup\$ Because you didn't write any. With assembly it's up to you to decide how to do things. You could create and use the same mechanisms are higher level languages (in fact C startup code is often written in assembly...), or you could chose a different way. \$\endgroup\$ Aug 20, 2019 at 2:21
  • 4
    \$\begingroup\$ Posting this question so quickly after your last electronics.stackexchange.com/questions/453661/… and while that does not have an accepted answer suggests you haven't really taken time to fully understand the responses to your highly related existing question. Some could probably successfully argue that this should be closed as a duplicate of that, as a true understanding of your other question would encompass this as well. \$\endgroup\$ Aug 20, 2019 at 2:25
  • 1
    \$\begingroup\$ Technically, the only thing you can't do in C is to set the stack pointer or perform critical tasks that need to be done before MMU and memory initializations have been carried out. The rest of the start-up code such as writing to misc registers, initializing .data and .bss etc can be carried out in C. Looking at mainstream Cortex M that load the SP from flash automatically, you can in theory write 100% of the program for it in C. \$\endgroup\$
    – Lundin
    Aug 21, 2019 at 6:29

3 Answers 3


C programs need some pre requisites for stack, global variables and so on.... Have a look here for more details. In assembler you had to do these things on your own, because you have the full control of the controller.


One important reason to write something in assembly is to have control over those kinds of low level details. Nevertheless you do still have to declare regions for stack, heap, constants and other types of non-program data when you create an assembly program. But it’s up to you as the assembly programmer to manage them yourself.


Here's a quick diagram of an executable unit:

enter image description here

There are six important areas used during program execution. (The seventh UNUSED area sits between the HEAP and STACK and provides room for each of them to grow and shrink during execution.)

The first four areas are compile-time computable. For compilers supporting multiple compilation units, it will the linker that computes the size required by the first four areas. For compilers that do not support multiple compilation units, it's likely the compiler itself that computes this size. (A compiler only supporting one compilation unit may still use a separate linker. So this isn't a hard rule.) In addition to these first four areas, the heap space (not always supported by every compiler and library) and the stack space represent dynamically changing areas during execution and cannot readily be computed prior to execution.

Usually, some code must be added to the code you write in order to set up the necessary circumstances for the above diagram. The first three areas labeled **PERSIST* on the left side must reside in non-volatile, persistent memory. This means that these first three sections must be stored into some kind of memory that will persist even after power is removed. It doesn't mean that they have to next to each other or even in the same persistent memory system. But they do have to persist.

In a large operating system, these first three areas will be stored on a secondary storage system such as a disk drive. And it's often true that they are in a different format than needed in the primary memory system once the program is allowed to start. In an embedded system, these first three areas are usually kept in flash. But this could also be EEPROM or FeRAM/FRAM (some MSP430 devices.)

The last remaining sections labeled on the left side as VOLATILE do not have to be stored in persistent memory. But they still may be required and must be set up, correctly, before the program can start.

During execution, the memory can be either read-only (never needs to be written) or else read-write. I've included RO and RW on the right side to indicate which of these two is appropriate for the indicated section. (The UNUSED area must also be read-write.) In some cases, the CODE section might not even be readable as data but instead only executable (this is often the case with the x86/x64 family from Intel.)

I've included some color bands to help me in describing something else. There are two basic families of CPU achitectures: von Neumann and Harvard. The von Neumann architecture is usually associated with systems that use a single address space for both code and data. The Harvard architecture is usually associated with systems that use two different address spaces; one for code and one for data. (There are more complex arrangements, of course.)

The right column of colors has only two colors, designated for Harvard architectures. The red color is for the code address space and the blue color is for the data address space. For Harvard, it is possible to simply use some kind of ROM for the red region. However, the blue region must be implemented in the data address space and some of it must be persistent. So one of two things must occur: (1) the data address space must include some data address space ROM that is then used to persist the CONST and INITIALIZED DATA areas with RAM filling out the rest of the data address space, but where now there is needed a step to copy the persistent INITIALIZED DATA to a newly allocated area in data address space RAM portion prior to operation; or else, (2) only data address space RAM is used, but now both the CONST and INITIALIZED DATA areas must be placed either into the code address space ROM and copied from there to the data address space RAM or else held in a third address space ROM and transferred into the data address space RAM by some other means. Either way, there is usually a step required to transfer the persistent portions of the program data into the data memory area. The only case I can imagine where a step isn't required is where the data address space for the Harvard architecture is implemented using FeRAM which is both persistent as well as being fully capable of supporting full-speed read-write operations.

The column of colors just to the left of that is more for discussing von Neumann architectures. Here, the only step necessarily required before the program begins execution is the transfer of the INITIALIZED DATA from ROM to RAM (unless, once again, FeRAM is being used.) The dark blue color indicates that CODE and CONST can be placed directly in ROM and left there without added support during the program initialization process. But the purple color for INITIALIZED DATA indicates that this area must be transferred from persistent storage to RAM. (The only exception, again, would be FeRAM [or core memory if you want to go back to the 1960's and 1970's.])

In short, there is usually some need for code to scope out the memory system and identify the available RAM, transfer some or all of the persistent memory into RAM, and set up the heap and stack pointers. In the case of C, there is also a need for all static lifetime variables (which technically may be located in the UNITIALIZED DATA area, though that area will be initialized in the case of C) to be initialized with a semantic zero. Also, in C, there may be a requirement of passing some parameters into main(), which means placing these on the stack (or whatever mechanism is used to pass in parameters to a function.) So there is added code in the C start-up code (which you don't usually write) to perform this step, as well.

Once all these areas are set up, the first line of the code you wrote can start. But not beforehand. So there is almost always some hidden code that is linked into your program during the linking phase that is used to do some checks, transfer and initialize various library systems (in C, the heap is managed the the malloc family of methods in the standard library), etc. Once all this is completed, the assumptions guaranteed by your language and compiler tools is hopefully well-met and the code you write can finally begin, safely.


Not the answer you're looking for? Browse other questions tagged or ask your own question.