It makes perfect sense. ROM and RAM are not contiguous space. Rather, they are spaces that are physically separate from each other. It's all in HOW we access the memory space and which instructions we use that determines whether we are accessing the internal ROM, internal RAM, external RAM, and the SFR space.
On both 8051 and 8052, directly addressing addresses in the range of 0x80-0xFF always accesses the SFR space. On the 8052, we also have on chip RAM that resides in the 0x80-0xFF range, but we use indirect addressing to access this space.
Example, if we execute -
Code:
mov 0x90, #0x55 ;load value 0x55 to P1 register
This writes the value 0x55 to the P1 SFR. However, if we execute -
Code:
mov R0,#0x90 ;load RAM address to R0
mov @R0,#0x55 ;store value 0x55 to RAM address in R0
This writes the value 0x55 to address 0x90 in internal RAM space as we have indirectly addressed RAM location 0x90 using the R0 register as an address pointer.
Another little trick that you only use if you really know what you're doing is to use the stack pointer along with the push and pop instructions to read/write the memory space from 0x80-0xFF. This works because the stack indirectly addresses the RAM space. On most 8052 applications, I actually dedicate the 0x80-0xFF RAM space to the stack by moving the stack pointer to address 0x7F in my initialization code.
The program counter ONLY accesses internal ROM as this is where the code is stored. We can also read data in code memory by using the MOVC instruction.
When reading external RAM, we use the MOVX instruction.
Through the use of different instructions and addressing methods to access the different memory spaces, this allows each separate memory space to use the same address ranges.
To summarize -
MOVX is dedicated to external RAM space only
MOV is dedicated to internal RAM space and SFR space only
MOVC is dedicated to internal and external program ROM only
The program counter is dedicated to internal/external ROM space only