What Happens When main() Returns on a Bare‑Metal Microcontroller?
In bare‑metal embedded systems without an RTOS, the fate of a program after the C main() function returns varies by compiler, and understanding the startup code and post‑main behavior explains why LEDs may flicker or why an infinite loop is often required.
Problem
On an 8051‑based development board a simple C program that turns on two LEDs leaves the other six LEDs faintly lit after main() returns. Adding an infinite loop ( while(1);) in main() eliminates the faint LEDs.
Observed Source Code
#include <REGX51.H>
void test(num)
{
switch(num)
{
case 1: P2_0 = 0; P2_1 = 0; break;
}
}
void main(void)
{
test(1);
}Version with an explicit infinite loop:
#include <REGX51.H>
void test(num)
{
switch(num)
{
case 1: P2_0 = 0; P2_1 = 0; break;
}
}
void main(void)
{
test(1);
while(1);
}Where Execution Continues After main() Returns
After reset the 8051 runs a small startup routine (often STARTUP.A51) that initializes data sections, sets the stack pointer, and then jumps to the entry point ?C_START. The jump to main() is a long jump, so main() never returns to the startup code. When main() finishes, the CPU simply executes whatever bytes follow the last instruction of main(). The exact tail code is generated by the compiler.
Typical Startup Code (excerpt)
NAME ?C_STARTUP
?C_C51STARTUP SEGMENT CODE
?STACK SEGMENT IDATA
RSEG ?STACK
DS 1
EXTRN CODE (?C_START)
PUBLIC ?C_STARTUP
CSEG AT 0
?C_STARTUP: LJMP STARTUP1
RSEG ?C_C51STARTUP
STARTUP1:
IF IDATALEN <> 0
MOV R0,#IDATALEN - 1
CLR A
IDATALOOP: MOV @R0,A
DJNZ R0,IDATALOOP
ENDIF
; (initialization of XDATA, PDATA, stack, etc.)
MOV SP,#?STACK-1
LJMP ?C_START
ENDCompiler‑Specific Post‑ main() Behaviour
Keil C51 Compiler
MOV R0, #0x7F
CLR A
MOV @R0, A
DJNZ R0, (3)
MOV SP, #0x0C
LJMP mainThe first four instructions clear the first 128 bytes of internal RAM, the fifth sets the stack pointer, and the sixth jumps back to the start of main(), effectively creating an infinite loop after the user code finishes.
MPLAB (PIC) Compiler
For PIC devices the compiler inserts a reset instruction at the end of main(), causing a hardware reset when the function returns.
Conclusion
In bare‑metal embedded development without an operating system, the main() function must never exit. If it does, the processor will execute whatever code the compiler placed after main(), which may clear memory, reset the device, or jump back to main(). Because this behaviour is compiler‑dependent, adding an explicit infinite loop such as while(1); is the safest way to keep the program running as intended.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
