Fundamentals 6 min read

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.

Liangxu Linux
Liangxu Linux
Liangxu Linux
What Happens When main() Returns on a Bare‑Metal Microcontroller?

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

                END

Compiler‑Specific Post‑ main() Behaviour

Keil C51 Compiler

MOV R0, #0x7F
CLR A
MOV @R0, A
DJNZ R0, (3)
MOV SP, #0x0C
LJMP main

The 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

C programmingembedded systemsMicrocontrollermain functionstartup code
Liangxu Linux
Written by

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.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.