AT89S52 memory mapping in C?

Thread Starter

Embededd

Joined Jun 4, 2025
131
I've AT89S52 microcontroller (8051 family), and before writing my own code, I want to clearly understand its memory layout. I am familiar with the typical C language memory model, where memory is divided into sections like text, data, bss, heap, and stack. However, I find this model doesn’t directly map to the architecture of the AT89S52. I referred to the AT89S52 datasheet (specifically section 6.2), but it doesn't provide a detailed explanation of how the RAM is internally divided (e.g., where the stack grows from, where global/static variables are stored, or how heap is managed if at all).

Datasheet https://ww1.microchip.com/downloads/en/DeviceDoc/doc1919.pdf

Here’s what I am trying to understand: How is the 256 bytes of internal RAM used — what part is typically for stack, data, or heap? Is there any fixed memory region for global/static variables? How do compilers (like Keil or SDCC) map the typical C memory sections (data, stack, etc.) onto the 8051 memory?

Any pointers, clarifications, would be greatly appreciated
 

MrChips

Joined Oct 2, 2009
34,628
The 8051 MCU itself has only one SRAM memory area.
The allocation of memory space is done in the compiler in the setup menu of the IDE.
Look at how the memory space is allocated by examining the .map file.
 

Thread Starter

Embededd

Joined Jun 4, 2025
131
The 8051 MCU itself has only one SRAM memory area.
The allocation of memory space in done in the compiler in the setup menu of the IDE.
Look at how the memory space is allocated by examining the .map file.
Thank you for pointing me to the .map file for understanding the memory allocation in the AT89S52.

I have attached my source code and the generated .map file for reference. After reviewing them, I'm still unclear about some memory layout fundamentals in the AT89S52:

SOURCE CODE
C:
#include<reg51.h>

void delay(int a)
{
    int i,j;
    for(i=0;i<a;i++)
    for(j=0;j<a;j++);
}
sbit led=P2^0;

void main()
{
    while (1)
  {
        led = 1;
       delay(5000);
      led=0;
      delay(5000);
    }
}
MAP File
C:
BL51 BANKED LINKER/LOCATER V6.22.4.0                                                  


BL51 BANKED LINKER/LOCATER V6.22.4.0, INVOKED BY:
C:\KEIL_V5\C51\BIN\BL51.EXE .\Objects\STARTUP.obj, .\Objects\LED_2.obj TO .\Objects\LED_2 PRINT (.\Listings\LED_2.m51) R
>> AMSIZE (256)


MEMORY MODEL: SMALL


INPUT MODULES INCLUDED:
  .\Objects\STARTUP.obj (?C_STARTUP)
  .\Objects\LED_2.obj (LED_2)


LINK MAP OF MODULE:  .\Objects\LED_2 (?C_STARTUP)


            TYPE    BASE      LENGTH    RELOCATION   SEGMENT NAME
            -----------------------------------------------------

            * * * * * * *   D A T A   M E M O R Y   * * * * * * *
            REG     0000H     0008H     ABSOLUTE     "REG BANK 0"
            IDATA   0008H     0001H     UNIT         ?STACK

            * * * * * * *   C O D E   M E M O R Y   * * * * * * *
            CODE    0000H     0003H     ABSOLUTE   
                    0003H     07FDH                  *** GAP ***
            CODE    0800H     002FH     UNIT         ?PR?_DELAY?LED_2
            CODE    082FH     0010H     UNIT         ?PR?MAIN?LED_2
            CODE    083FH     000CH     UNIT         ?C_C51STARTUP



OVERLAY MAP OF MODULE:   .\Objects\LED_2 (?C_STARTUP)


SEGMENT
  +--> CALLED SEGMENT
---------------------
?C_C51STARTUP
  +--> ?PR?MAIN?LED_2

?PR?MAIN?LED_2
  +--> ?PR?_DELAY?LED_2



SYMBOL TABLE OF MODULE:  .\Objects\LED_2 (?C_STARTUP)

  VALUE           TYPE          NAME
  ----------------------------------

  -------         MODULE        ?C_STARTUP
  C:083FH         SEGMENT       ?C_C51STARTUP
  I:0008H         SEGMENT       ?STACK
  C:0000H         PUBLIC        ?C_STARTUP
  D:00E0H         SYMBOL        ACC
  D:00F0H         SYMBOL        B
  D:0083H         SYMBOL        DPH
BL51 BANKED LINKER/LOCATER V6.22.4.0                                                  11/15/2024  17:50:56  PAGE 2


  D:0082H         SYMBOL        DPL
  N:0000H         SYMBOL        IBPSTACK
  N:0100H         SYMBOL        IBPSTACKTOP
  N:0080H         SYMBOL        IDATALEN
  C:0842H         SYMBOL        IDATALOOP
  N:0000H         SYMBOL        PBPSTACK
  N:0100H         SYMBOL        PBPSTACKTOP
  N:0000H         SYMBOL        PDATALEN
  N:0000H         SYMBOL        PDATASTART
  N:0000H         SYMBOL        PPAGE
  N:0000H         SYMBOL        PPAGEENABLE
  D:00A0H         SYMBOL        PPAGE_SFR
  D:0081H         SYMBOL        SP
  C:083FH         SYMBOL        STARTUP1
  N:0000H         SYMBOL        XBPSTACK
  N:0000H         SYMBOL        XBPSTACKTOP
  N:0000H         SYMBOL        XDATALEN
  N:0000H         SYMBOL        XDATASTART
  C:0000H         LINE#         126
  C:083FH         LINE#         133
  C:0841H         LINE#         134
  C:0842H         LINE#         135
  C:0843H         LINE#         136
  C:0845H         LINE#         185
  C:0848H         LINE#         196
  -------         ENDMOD        ?C_STARTUP

  -------         MODULE        LED_2
  C:0000H         SYMBOL        _ICE_DUMMY_
  D:00A0H         PUBLIC        P2
  C:0800H         PUBLIC        _delay
  C:082FH         PUBLIC        main
  B:00A0H.0       PUBLIC        led
  -------         PROC          _DELAY
  D:0006H         SYMBOL        a
  -------         DO         
  D:0004H         SYMBOL        i
  D:0002H         SYMBOL        j
  -------         ENDDO       
  C:0800H         LINE#         3
  C:0800H         LINE#         4
  C:0800H         LINE#         6
  C:0810H         LINE#         7
  C:082EH         LINE#         8
  -------         ENDPROC       _DELAY
  -------         PROC          MAIN
  C:082FH         LINE#         11
  C:082FH         LINE#         12
  C:082FH         LINE#         13
  C:082FH         LINE#         14
  C:082FH         LINE#         15
  C:0831H         LINE#         16
  C:0838H         LINE#         17
  C:083AH         LINE#         18
  C:083DH         LINE#         19
  -------         ENDPROC       MAIN
  -------         ENDMOD        LED_2
BL51 BANKED LINKER/LOCATER V6.22.4.0                                                  11/15/2024  17:50:56  PAGE 3



******************************************************************************
* RESTRICTED VERSION WITH 0800H BYTE CODE SIZE LIMIT; USED: 004EH BYTE ( 3%) *
******************************************************************************

Program Size: data=9.0 xdata=0 code=78
LINK/LOCATE RUN COMPLETE.  0 WARNING(S),  0 ERROR(S)
  1. Code Memory
    • Where exactly does the executable code begin in memory? (0000H, 0800H, etc.)
  2. Data Memory (Variables)
    • The map indicates "data=9.0 bytes" are used
    • At what address does this data section start?
  3. Stack Memory
    • Does this mean the stack always starts immediately after Register Bank 0?

I want to ensure I properly understand how the memory is allocated before expanding project. Thank you for your help!
 

MrChips

Joined Oct 2, 2009
34,628
Code memory begins at 0000H (ROM space)
Stack memory begins at 0008H (RAM space)

Your program has not requested any global memory. Hence there is no Data Memory allocated.
All local variables are allocated from the Stack Memory.
 

Futurist

Joined Apr 8, 2025
721
Code memory begins at 0000H (ROM space)
Stack memory begins at 0008H (RAM space)

Your program has not requested any global memory. Hence there is no Data Memory allocated.
All local variables are allocated from the Stack Memory.
Just for my sake, why does that look as only bytes in 0000H thru 0007H are available for code?
 

Papabravo

Joined Feb 24, 2006
22,058
Just for my sake, why does that look as only bytes in 0000H thru 0007H are available for code?
Because there are two different memory spaces, each of which spans 64K bytes. The read only code space is a combination of internal (on chip) and external memory. External memory is accessed with a control signal named PSEN* (Program Store Enable - Active Low). Programs can read from the internal or external code space with a MOVC instruction using the Data Pointer register.

The read write data memory goes from 0x0000 to 0xFFFF and is accessed with control signals RD* (Memory Read - Active Low) and WR* (Memory Write - Active Low). The external data memory is always accessed with specific indirect instructions using the Data Pointer Register so there is no conflict with the internal data memory and SFR (Special Function Register) space. The mnemonic for this instruction is MOVX.

Most implementations do not have actual memory of 128K for code and data. Many implementations do not have external code or data memory and use only on chip resources.
 

Thread Starter

Embededd

Joined Jun 4, 2025
131
I see in datasheet , The internal program memory size is 8KB (0x1FFF). This mean only programs smaller than 8KB can be flashed onto the MCU. If I try to flash a larger program, what happens? Will it corrupt the MCU?

How to test this scenario—could you explain?

1749558583231.png
 
Last edited:

Thread Starter

Embededd

Joined Jun 4, 2025
131
Why bother to test it?
You know you cannot fit 2 litres of water into a 1-litre bottle
My curiosity was about observing the actual behaviour. Will the compiler throw an error? Could forcibly flashing excess code corrupt the MCU? I think the compiler will throw error , but wanted to confirm if there’s any risk of hardware damage
 

MrChips

Joined Oct 2, 2009
34,628
The IDE (Integrated Development Environment) requires you to specify the MCU part number.
Hence the compiler/linker will not allow a program to exceed the specified memory size.

Assuming that you can override the memory size, the programmer will read the signature bytes on the MCU and will discover a conflict, thus preventing you from programming the device.

Assuming that you manually override all settings, there are two possible outcomes when you attempt to program the device.
PROMS are erased before programming. In most cases, all bits are erased to 1. Programming will write 0 into the bit. Thus, programming will wrap around the memory space and 1s will be programmed to 0s. This is the equivalent of a boolean AND result.

In the reverse case, if the PROM is erased to 0, programming will write 1s. This is equivalent to boolean OR.

In both cases, your program will fail to execute correctly because the code has been corrupted.
 

Papabravo

Joined Feb 24, 2006
22,058
I see in datasheet , The internal program memory size is 8KB (0x1FFF). This mean only programs smaller than 8KB can be flashed onto the MCU. If I try to flash a larger program, what happens? Will it corrupt the MCU?

How to test this scenario—could you explain?

View attachment 350786
What happens will depend on the compiler and the PROM programmer. The compiler will likely tell you that you have exceeded the allowable program memory size. In the case of such an error it might refuse to generate the .hex file required by the programmer. If it does generate the .hex file you will have to examine it to determine what the compiler did.

In case you have to use an external memory, you have two options:
  1. Put all the code in the external memory and ground the EA* pin, so all code fetches will be done to external memory using PSEN* as the READ strobe.
  2. Divide the code into two parts, an internal part and an external part. Program the MCU with the internal part and the external memory with the external part. Connect EA* to Vcc and instruction fetches will go to the correct place.
 

Thread Starter

Embededd

Joined Jun 4, 2025
131
I’ve been going through the entire AT89S52 datasheet trying to understand where exactly the stack starts, how it grows, and where it ends. However, I couldn’t find a direct explanation in the datasheet. I did find the SFR address table, which lists the hardware register addresses, but datasheet doesn’t clearly mention stack behaviour.

I’m struggling to understand how the AT89S52’s data memory is divided. The datasheet mentions 256B of internal RAM (lower 128B + upper 128B) For example: Local variables are stored on the stack but what is the address range for the stack? If I declare a global variable in code, What would be address range where global variables would store

Could you please clarify me to the right section

1749568061096.png
SFR address table
1749567992806.png
 

MrChips

Joined Oct 2, 2009
34,628
In most simple MCUs, SRAM resides in one block of memory, for example, a 256-byte SRAM might reside from address 0000H to 00FFH.

Variables are allocated from address 0000H.

The Stack Pointer (SP) is initialized to 00FFH, i.e. the stack grows from the highest memory location towards the lowest.
A PUSH instruction writes data at the address contained in SP and SP is decremented. This is called post-decrement operation.

Conversely, a POP instruction pre-increments SP and extracts the data at the address held in SP.

The AT89S52 is different.
The SP begins at 0008H and grows towards higher memory addresses.

I have not examined what the compiler does. One can assume that global variables are allocated from address 00FFH and backwards towards lower memory. Global variable allocation is static and is performed at compile time. Stack usage is dynamic and space is allocated at run time.

MEMORY CORRUPTION!

In both cases, one can recognize a fatal problem. If stack usage grows and collides with data space, the stack can destroy data, and vice versa, data can destroy what is held in the stack. This is a fatal error and will cause the program to crash.
 

Papabravo

Joined Feb 24, 2006
22,058
The stack pointer register is at address 081H and lies between the Port 0 data register (P0) and the low byte of the Data Pointer Register. In the lower portion of the internal memory are up to four register banks in addresses 00H through 01FH. Your C compiler apparently supports the use of only a single register bank and places the initial stack pointer at address 07H. It is incremented before placing data on the stack and is decremented after taking an item off of the stack. The stack grows upwards in memory.

The upper half of the internal memory is the SFR space for instructions using direct memory access and RAM for instructions using indirect memory access through two of the eight scratch registers R0 or R1. How the compiler allocates memory above the stack and below the address of the Port 0 data register is up to the compiler.
 
Last edited:

Thread Starter

Embededd

Joined Jun 4, 2025
131
I've been reading up on the AT89S52 and I understand that the stack uses internal RAM and grows upwards starting from around 0x08.

But I also think that if the stack grows beyond 0x7F, it can overwrite the (SFRs) from 0x80 to 0xFF. That sounds pretty dangerous, especially if we have recursive functions or just poor stack management.

So I was wondering: How can we detect a stack overflow at runtime on the AT89S52?

Can we rely on the .map file to understand stack usage?

I believe it only shows static allocations like global variables , not the actual stack growth during runtime, right?
 
Last edited:

MrChips

Joined Oct 2, 2009
34,628
Stack memory corruption can occur on all MCUs unless you have memory protection schemes built-in in the HW and SW.
On a small embedded MCU system, you have to rely on good programming habits and techniques to avoid crashes.

The .map file is generated by the linker. Hence it has no knowledge of stack usage at run time. You would have to run a code simulator or debugger to see how the stack grows under all possible scenarios and particularly worst case scenarios.

To get an idea and understanding of how the stack can grow, examine all your subroutine calls, particularly nested calls, local variables, and interrupt calls.
 

Papabravo

Joined Feb 24, 2006
22,058
I've been reading up on the AT89S52 and I understand that the stack uses internal RAM and grows upwards starting from around 0x08.

But I also think that if the stack grows beyond 0x7F, it can overwrite the (SFRs) from 0x80 to 0xFF. That sounds pretty dangerous, especially if we have recursive functions or just poor stack management.

So I was wondering: How can we detect a stack overflow at runtime on the AT89S52?

Can we rely on the .map file to understand stack usage?

I believe it only shows static allocations like global variables , not the actual stack growth during runtime, right?
You missed the main point in an earlier post of mine:
  1. A direct access to an address in the range [080H, 0FFH] goes to an SFR or nothing. It goes to nothing if no SFR is mapped to that location.
  2. An indirect access to an address in the range[080H, 0FFH] via the stack pointer goes to INTERNAL RAM.
Also note that you cannot directly address the internal RAM locations from 080H thru 0FFH for either a READ or a WRITE operation.
Also note that the Stack Pointer register (SP) is an 8-bit register and can only point to 256 unique locations using an indirect access.

You need to up your game when it comes to reading datasheets, but I will agree that the 8051 is a tough nut to crack.

As for stack overflow it cannot be detected in hardware. The only way you could do it is with code to check the SP register against the boundary prior to every access. In an embedded application we never did this because we were never willing to pay the penalty in space or time. We also NEVER did dynamic memory allocation and never used recursive subroutines.
 
Last edited:

Thread Starter

Embededd

Joined Jun 4, 2025
131
The .map file is generated by the linker.
So, if I understand correctly the linker script decides which memory section gets assigned to variables. And I believe we can modify the memory section size ( stack ) by updating this linker script?
 

Papabravo

Joined Feb 24, 2006
22,058
So, if I understand correctly the linker script decides which memory section gets assigned to variables. And I believe we can modify the memory section size ( stack ) by updating this linker script?
That supposition seems entirely reasonable. If you have not already done so I would also check the C-startup file for details on how the actual environment is set up.
 
Top