+------------------------------------------------------------------------+
|                   02 / Memory, addressing and flags                    |
+------------------------------------------------------------------------+

THREE SPACES, ONE NUMBER
CODE is up to 64 KB of program memory, fetched by PC and read as data with
MOVC. XDATA is a separate, up-to-64-KB external data space accessed with
MOVX. Internal DATA contains the CPU's small RAM and its directly
addressed special function registers. Thus MOV A,0x30, MOVC A,@A+DPTR and
MOVX A,@DPTR can read three different physical places even if the
numerical address is the same.

INTERNAL MAP (128-byte 80C31)
00-07  register bank 0 (reset default)
08-0F  register bank 1
10-17  register bank 2
18-1F  register bank 3
20-2F  16 bytes / 128 individually addressable RAM bits
30-7F  general RAM and stack, allocated by your program
80-FF  directly addressed SFRs, NOT another 128 RAM bytes

WHAT IS A REGISTER BANK?
The working registers R0-R7 are eight names for eight bytes in internal
RAM. There are four sets of those bytes, called register banks. Only one
bank is selected at a time. Selecting another bank changes which RAM bytes
the names refer to; it does not copy or erase their contents.

For example, with bank 0 selected, MOV A,R3 reads internal RAM address
0x03. With bank 1 selected, the same instruction reads address 0x0B. R3 is
register number 3 (the fourth register, because numbering starts at zero).
By contrast, MOV A,0x03 always reads address 0x03, regardless of the
selected bank.

HOW THE CPU SELECTS A BANK
PSW stands for Program Status Word. It is a special function register: a
byte used by the CPU for status flags and control settings, rather than
ordinary data storage. Two of its bits select the register bank: RS1 is
bit 4 and RS0 is bit 3. Written as RS1:RS0, they form a two-bit number:

RS1 RS0  Selected bank  R0-R7 use RAM addresses  R3 uses
 0   0       0                00-07                03
 0   1       1                08-0F                0B
 1   0       2                10-17                13
 1   1       3                18-1F                1B

Reset selects bank 0. Other banks can give interrupt handlers their own
working registers, but software must manage the selection and reserve the
RAM. In DOS/51, do not casually switch banks or overwrite these bytes: the
running firmware also uses internal RAM and the stack.

WHAT DOES INDIRECT ADDRESSING MEAN?
A direct operand names the address in the instruction itself: MOV A,0x30
reads RAM address 0x30. An indirect operand instead uses an address held
in a register. After MOV R0,#0x30, MOV A,@R0 reads that same RAM byte. The
@ means 'use the address stored in this register'; ordinary indirect MOV
supports R0 or R1, not any arbitrary register.

Addressing examples:
    mov a,#0x30      ; A = literal 48
    mov a,0x30       ; A = RAM byte at 30
    mov r0,#0x30
    mov a,@r0        ; same RAM byte, indirect
    mov dptr,#0x1234
    movx a,@dptr     ; external DATA byte at 1234
    clr a
    movc a,@a+dptr   ; CODE byte at 1234

ORDINARY MOV: RAM OR A HARDWARE REGISTER?
The 80C31 has only 128 bytes of internal RAM, at 00-7F. Addresses 80-FF
select special function registers (SFRs) only when used as DIRECT
operands. These SFRs control hardware such as ports and timers. For
instance, MOV A,0x90 reads the Port 1 register, P1.

Ordinary indirect MOV does not use that SFR address space. Loading R0 with
0x90 and executing MOV A,@R0 therefore does NOT read P1: it attempts to
read an internal RAM byte that does not exist on this 80C31. The 80C32 is
different: it adds another 128 internal RAM bytes at indirect addresses
80-FF. That extra RAM still does not turn indirect accesses into SFR
accesses. Choose both the address AND the addressing mode deliberately.

MOVX through @R0/@R1 uses an 8-bit low address; classic external hardware
uses P2's latch for the high address. MOVX @DPTR uses all 16 address bits.
Neither is the same as ordinary MOV @Ri. MOVC uses A as an unsigned index;
the @A+PC base is PC AFTER the MOVC opcode. JMP @A+DPTR also uses an
unsigned byte offset, not a table of pointers automatically dereferenced
by hardware.

BIT MEMORY
Bit addresses 00-7F map to bytes 20-2F: byte = 20 + (bit_address / 8), bit
= bit_address modulo 8. Bit 00 is RAM 20.0; bit 7F is RAM 2F.7. Bit
addresses 80-FF select implemented bit-addressable SFRs whose BYTE address
is divisible by eight. Not every SFR supports bit operations: TMOD at 89
does not. Bit address 89 is TCON.1, not TMOD. Use ANL/ORL on a whole byte
for non-bit-addressable registers.

FLAGS: PSW = CY AC F0 RS1 RS0 OV user P
CY is carry for addition and borrow for SUBB. AC is carry/borrow across
bit 3. OV signals signed arithmetic overflow. P is maintained from A: 1
for an odd number of set bits. F0 is a software flag. The classic CPU has
NO zero or negative flag; JZ/JNZ inspect A itself. INC and DEC do not
update CY or OV. They still affect P if they change A.

For unsigned comparison, CJNE sets CY when its first unsigned operand is
less than the second. For signed comparison you must account for
sign/overflow yourself. Example: 0x7F + 1 becomes 0x80 with OV=1 but CY=0.
Conversely 0xFF + 1 becomes 0 with CY=1 but OV=0.

16-BIT ADD / SUBTRACT
    mov a,0x30       ; low byte of left operand
    add a,0x32       ; low byte of right operand
    mov 0x34,a
    mov a,0x31
    addc a,0x33
    mov 0x35,a       ; final CY is the 16-bit carry

For subtraction start with CLR C and use SUBB on both low and high bytes.
A plain SUB instruction does not exist.

STACK
Reset sets SP=07; a PUSH first increments SP, then writes. CALL pushes the
return address low byte first, then high; RET reverses that sequence. A
bare-metal MOV SP,#0x5F makes the first pushed byte land at 60, leaving 32
bytes through 7F. Reserve that area and check worst-case calls plus
interrupt nesting. The stack does not automatically grow into external
RAM. PUSH/POP accept direct byte addresses, not immediate data or an Rn
operand. PUSH 0x00 saves bank-0 R0, not necessarily the current bank's R0.

PORT READS
MOV A,P1 reads pins. Read-modify-write instructions with a port
destination (ANL P1,#mask, ORL P1,#mask, CPL P1.0 and friends) use the
output latch. That matters when external circuitry pulls a released input
low. Use masked or bit writes so unrelated outputs survive.

[ DOS/51 FIELD MANUAL | IMANIA.DK | 2026-11-14 ]
