← DOS/51 emulatorIMANIA / TECHNICAL FIELD MANUALSASCII edition ↓

VOLUME 01 / MCS-51 / FROM THE FIRST BYTE TO THE BUS

The Ultimate
8051
Assembly Guide.

Small machine. Complete instruction set.
A practical field manual for the ROMless 80C31.

44 mnemonics111 operand forms255 defined opcodes

FIELD NOTES / START51.TXT

01 / Think like an 8051

An 8051 executes small instructions on bytes and individual bits. The accumulator A is the main arithmetic register; B assists multiplication and division. R0-R7 are eight working registers selected from one of four banks in internal RAM. DPTR is a 16-bit address register, split into DPH and DPL. PC is the program counter; SP points to the last occupied byte of the internal-RAM stack.

A byte holds 0-255 unsigned or -128 to +127 in two's-complement notation. The bit pattern is the same: your program chooses the interpretation. Hexadecimal 0x2A is decimal 42. In these examples # means an immediate constant, @ means indirect addressing and a semicolon begins a comment. DOS/51 accepts 0x-prefixed numbers; many desktop assemblers also accept 2AH. Directives such as ORG, DB, EQU and END are assembler commands, not CPU instructions, and their spelling depends on the assembler.

The classic CPU has 44 instruction mnemonics, conventionally grouped into 111 operand forms, represented by 255 defined opcode bytes. Opcode A5 is reserved. Register-number and page-address bits explain why one form can have several opcodes. This guide covers the original instruction set, not 8051-compatible vendors' later extra opcodes.

One classic machine cycle is 12 oscillator clocks. At the DOS/51 board's 11.0592 MHz that is 1.085 microseconds. Most instructions take one or two machine cycles; MUL and DIV take four. A modern 1T 8051 derivative is not timing-compatible. The reference gives classic hardware timing, not a benchmark of JavaScript execution.

TRY IT IN DOS/51
Open nano first.asm, enter the program below, save with Ctrl-O and leave with Ctrl-X. Run asm -r first.asm. DOS/51 assembles at 0x8000; RET returns to the shell. This example prints an asterisk through the board's existing UART configuration. It does not reinitialize hardware owned by the operating system.
    clr 0x99         ; clear stale TI before transmitting
    mov a,#42
    mov 0x99,a        ; SBUF: transmit '*'
wait_tx:
    jnb 0x99,wait_tx  ; bit address 0x99 is TI, not SBUF
    clr 0x99         ; acknowledge transmit completion
    ret

That apparent address collision is deliberate: direct byte 0x99 is SBUF, whereas bit address 0x99 is SCON.1 (TI). The CPU instruction determines which address space is used.

Bare-metal programs are different: they start at reset address 0x0000, provide vectors, initialize SP, ports and peripherals, then run forever. Do not copy a reset-vector program into the DOS/51 transient area and expect it to own the machine. Save work before experiments that alter the stack, interrupt state or UART.

↓ START51.TXT · ASCII

FIELD NOTES / MEM51.TXT

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.
↓ MEM51.TXT · ASCII

03 / THE COMPLETE DESK REFERENCE

Every standard instruction.

All 111 operand forms, all 255 defined opcode bytes. Rn = R0–R7; @Ri = @R0 or @R1; dir = direct byte address; #imm = immediate byte; rel = signed displacement from the next instruction; addr11 = same-2-KB-page target; addr16 = full CODE address. Commas separate opcodes within a form, not bytes of one encoding. Multi-byte operands follow the opcode; MOV dir,dir encodes source then destination. Full addresses/immediate words are high byte then low byte; stack PC pushes are low then high.

MC means classic 12-clock machine cycles. Flag columns list arithmetic/Boolean effects, not every possible destination side effect. Parity P follows A automatically; writes to PSW or its bits can explicitly alter flags. All conditional branches cost two MC whether taken or not. Reserved A5 has no portable behavior.

44 mnemonics · 111 operand forms

ACALL

flow

Push the address after this instruction and call a target in the same 2-KB page as that return address. Uses two internal stack bytes; no automatic register saves.

Operand formOpcode(s), hexBytesMCFlags
ACALL addr1111, 31, 51, 71, 91, B1, D1, F122—
acall worker

Watch: Use LCALL if placement is not guaranteed. Page bits are checked against PC after the two-byte call.

ADD

arithmetic

A = A + source; CY, AC and OV describe the unsigned, nibble and signed results. Incoming CY is ignored.

Operand formOpcode(s), hexBytesMCFlags
ADD A,#imm2421CY AC OV
ADD A,dir2521CY AC OV
ADD A,@Ri26, 2711CY AC OV
ADD A,Rn28, 29, 2A, 2B, 2C, 2D, 2E, 2F11CY AC OV
add a,#1

Watch: For multi-byte addition use ADD on the low byte and ADDC on subsequent bytes.

ADDC

arithmetic

A = A + source + incoming CY. Updates CY, AC and OV.

Operand formOpcode(s), hexBytesMCFlags
ADDC A,#imm3421CY AC OV
ADDC A,dir3521CY AC OV
ADDC A,@Ri36, 3711CY AC OV
ADDC A,Rn38, 39, 3A, 3B, 3C, 3D, 3E, 3F11CY AC OV
addc a,r2

Watch: Initialize carry deliberately if this is the first byte of a calculation.

AJMP

flow

Jump within the 2-KB page containing the first byte after this instruction. Does not push a return address.

Operand formOpcode(s), hexBytesMCFlags
AJMP addr1101, 21, 41, 61, 81, A1, C1, E122—
ajmp destination

Watch: Crossing a page boundary during layout can invalidate a previously legal target.

ANL

logic

Bitwise AND for byte operands. Boolean forms compute C AND bit or C AND NOT bit; the / complements the source only.

Operand formOpcode(s), hexBytesMCFlags
ANL dir,A5221—
ANL dir,#imm5332—
ANL A,#imm5421—
ANL A,dir5521—
ANL A,@Ri56, 5711—
ANL A,Rn58, 59, 5A, 5B, 5C, 5D, 5E, 5F11—
ANL C,bit8222CY
ANL C,/bitB022CY
anl 0x90,#0xFE

Watch: Byte forms preserve CY/AC/OV unless explicitly writing PSW. Boolean forms change C. Port destinations use their latch.

CJNE

flow

Compare without modifying either operand; branch if unequal. CY becomes 1 when the first operand is unsigned-less-than the second, otherwise 0.

Operand formOpcode(s), hexBytesMCFlags
CJNE A,#imm,relB432CY
CJNE A,dir,relB532CY
CJNE @Ri,#imm,relB6, B732CY
CJNE Rn,#imm,relB8, B9, BA, BB, BC, BD, BE, BF32CY
cjne a,#10,different

Watch: This is unsigned comparison, not a signed less-than instruction. CY changes even if the branch is not taken.

CLR

bits

Clear A, C or one directly addressed bit. CLR A does not clear carry.

Operand formOpcode(s), hexBytesMCFlags
CLR bitC221—
CLR CC311CY
CLR AE411—
clr c

Watch: Use CLR C before the first SUBB when no incoming borrow is intended.

CPL

bits

Complement all bits in A, carry alone or a single directly addressed bit.

Operand formOpcode(s), hexBytesMCFlags
CPL bitB221—
CPL CB311CY
CPL AF411—
cpl 0x90

Watch: CPL A leaves CY alone; CPL C toggles CY. A port-bit complement reads the latch.

DA

arithmetic

Decimal-adjust A after packed-BCD ADD/ADDC, using the low nibble, AC, high digit and CY. Can set carry for a decimal carry out; does not clear an existing carry.

Operand formOpcode(s), hexBytesMCFlags
DA AD411CY
da a

Watch: Only for valid packed-BCD addition. Not binary-to-BCD conversion or decimal subtraction.

DEC

arithmetic

Subtract one modulo 256 from A, an Rn register, a direct byte or internal RAM through @Ri. Does not update CY, AC or OV.

Operand formOpcode(s), hexBytesMCFlags
DEC A1411—
DEC dir1521—
DEC @Ri16, 1711—
DEC Rn18, 19, 1A, 1B, 1C, 1D, 1E, 1F11—
dec r7

Watch: There is no DEC DPTR instruction. A becomes FF when decrementing zero.

DIV

arithmetic

Unsigned divide A by B: quotient in A, remainder in B. CY is cleared; OV is cleared for a nonzero divisor and set for zero.

Operand formOpcode(s), hexBytesMCFlags
DIV AB8414CY=0 OV
div ab

Watch: When B is zero A and B are undefined. Test the divisor first.

DJNZ

flow

Decrement an Rn register or direct byte modulo 256, then branch if that result is nonzero. Arithmetic flags are unchanged.

Operand formOpcode(s), hexBytesMCFlags
DJNZ dir,relD532—
DJNZ Rn,relD8, D9, DA, DB, DC, DD, DE, DF22—
djnz r7,loop

Watch: An initial zero count wraps to FF and yields 256 iterations in a bottom-tested loop.

INC

arithmetic

Add one modulo 256 to a byte, or modulo 65536 to DPTR. Does not update CY, AC or OV.

Operand formOpcode(s), hexBytesMCFlags
INC A0411—
INC dir0521—
INC @Ri06, 0711—
INC Rn08, 09, 0A, 0B, 0C, 0D, 0E, 0F11—
INC DPTRA312—
inc dptr

Watch: DPTR is the only 16-bit INC destination; it does not provide a carry flag on rollover.

JB

flow

Branch when the addressed bit is one, leaving it unchanged.

Operand formOpcode(s), hexBytesMCFlags
JB bit,rel2032—
jb 0x90,high

Watch: A port test reads the pin; bit address 90 is P1.0, not the full P1 byte.

JBC

flow

If the addressed bit is one, clear it and branch. If zero, neither branch nor clear has an effect.

Operand formOpcode(s), hexBytesMCFlags
JBC bit,rel1032—
jbc 0x00,event

Watch: Useful for consuming a one-bit event flag. A port operation of this kind uses the latch.

JC

flow

Branch when CY is one.

Operand formOpcode(s), hexBytesMCFlags
JC rel4022—
jc carry_out

Watch: After SUBB a set CY means borrow; after CJNE it means unsigned less-than.

JMP

flow

Set PC to DPTR plus unsigned A. This is the one hardware computed JMP form.

Operand formOpcode(s), hexBytesMCFlags
JMP @A+DPTR7312—
jmp @a+dptr

Watch: A generic JMP label accepted by some assemblers is a pseudo-instruction, not an additional CPU opcode. A jump table must contain executable jump entries or code at the computed address.

JNB

flow

Branch when the addressed bit is zero without modifying it.

Operand formOpcode(s), hexBytesMCFlags
JNB bit,rel3032—
jnb 0x99,wait_tx

Watch: TI is bit address 99; direct byte address 99 is the different SBUF register.

JNC

flow

Branch when CY is zero.

Operand formOpcode(s), hexBytesMCFlags
JNC rel5022—
jnc no_borrow

Watch: No-borrow is unsigned greater-or-equal after a properly initialized SUBB.

JNZ

flow

Branch when A is not zero; no separate zero flag is consulted.

Operand formOpcode(s), hexBytesMCFlags
JNZ rel7022—
jnz nonzero

Watch: An INC R7 followed by JNZ still tests A, not R7.

JZ

flow

Branch when A is zero.

Operand formOpcode(s), hexBytesMCFlags
JZ rel6022—
jz empty

Watch: The branch neither clears A nor modifies carry.

LCALL

flow

Push the return PC and call any address in the 64-KB CODE space.

Operand formOpcode(s), hexBytesMCFlags
LCALL addr161232—
lcall worker

Watch: The stack is internal RAM. Budget two return-address bytes plus software saves and nesting.

LJMP

flow

Replace PC with a full 16-bit CODE address.

Operand formOpcode(s), hexBytesMCFlags
LJMP addr160232—
ljmp destination

Watch: No bank switching or external memory decoding is created by the instruction.

MOV

data

Copy a byte, bit or 16-bit immediate pointer. Destination comes first. Source is unchanged; MOV does not implicitly go through A.

Operand formOpcode(s), hexBytesMCFlags
MOV A,#imm7421—
MOV dir,#imm7532—
MOV @Ri,#imm76, 7721—
MOV Rn,#imm78, 79, 7A, 7B, 7C, 7D, 7E, 7F21—
MOV dir,dir8532—
MOV dir,@Ri86, 8722—
MOV dir,Rn88, 89, 8A, 8B, 8C, 8D, 8E, 8F22—
MOV DPTR,#imm169032—
MOV bit,C9222—
MOV C,bitA221CY
MOV @Ri,dirA6, A722—
MOV Rn,dirA8, A9, AA, AB, AC, AD, AE, AF22—
MOV A,dirE521—
MOV A,@RiE6, E711—
MOV A,RnE8, E9, EA, EB, EC, ED, EE, EF11—
MOV dir,AF521—
MOV @Ri,AF6, F711—
MOV Rn,AF8, F9, FA, FB, FC, FD, FE, FF11—
mov 0x30,#0x2A

Watch: MOV direct,direct encodes the SOURCE address before the destination, even though assembly syntax writes destination first. MOV C,bit and MOV bit,C explicitly read/write carry; other forms do not set arithmetic flags unless targeting PSW.

MOVC

data

Read a CODE byte at A+DPTR or A+PC into A. The PC-relative base is the address after the one-byte opcode. The index addition wraps in 16 bits.

Operand formOpcode(s), hexBytesMCFlags
MOVC A,@A+PC8312—
MOVC A,@A+DPTR9312—
movc a,@a+dptr

Watch: A is both the input index and the returned byte. DPTR is not incremented.

MOVX

data

Transfer between A and external DATA using @DPTR or @R0/@R1 addressing. Does not access SFRs or internal RAM.

Operand formOpcode(s), hexBytesMCFlags
MOVX A,@DPTRE012—
MOVX A,@RiE2, E312—
MOVX @DPTR,AF012—
MOVX @Ri,AF2, F312—
movx @dptr,a

Watch: Only A transfers data. @Ri provides an 8-bit low address; P2 supplies the board's high-address context on classic hardware.

MUL

arithmetic

Unsigned multiply A by B; the low product byte is A and the high byte B. CY is cleared; OV is one if B's result is nonzero.

Operand formOpcode(s), hexBytesMCFlags
MUL ABA414CY=0 OV
mul ab

Watch: B:A is a 16-bit result; neither operand is signed automatically.

NOP

flow

Consume one machine cycle without changing program-visible state apart from PC and normal peripheral progress.

Operand formOpcode(s), hexBytesMCFlags
NOP0011—
nop

Watch: This is a timing instruction, not a sleep mode; timers and interrupts still matter.

ORL

logic

Bitwise OR for byte forms; Boolean forms compute C OR bit or C OR NOT bit.

Operand formOpcode(s), hexBytesMCFlags
ORL dir,A4221—
ORL dir,#imm4332—
ORL A,#imm4421—
ORL A,dir4521—
ORL A,@Ri46, 4711—
ORL A,Rn48, 49, 4A, 4B, 4C, 4D, 4E, 4F11—
ORL C,bit7222CY
ORL C,/bitA022CY
orl 0x90,#1

Watch: Only the Boolean forms inherently change CY. A byte destination that is PSW can of course change flags.

POP

data

Read the byte at SP into the direct destination, then decrement SP.

Operand formOpcode(s), hexBytesMCFlags
POP dirD022—
pop 0xE0

Watch: Pop in reverse order of pushes. No POP Rn hardware form exists; use a direct byte address with the intended register bank.

PUSH

data

Increment SP, then copy the direct source byte onto the internal-RAM stack.

Operand formOpcode(s), hexBytesMCFlags
PUSH dirC022—
push 0xE0

Watch: No PUSH #immediate instruction. Stack overflow above 7F is not valid extra RAM on this 80C31.

RET

flow

Pop the two-byte return PC for a subroutine.

Operand formOpcode(s), hexBytesMCFlags
RET2212—
ret

Watch: Does not restore scratch registers or release interrupt priority in-service state.

RETI

flow

Restore the return PC and notify the interrupt controller that the interrupt service has finished.

Operand formOpcode(s), hexBytesMCFlags
RETI3212—
reti

Watch: Save/restore the working state yourself. Use for an ISR, not as an interchangeable spelling of RET.

RL

logic

Rotate A left: old bit 7 becomes bit 0. Carry is unchanged.

Operand formOpcode(s), hexBytesMCFlags
RL A2311—
rl a

Watch: A circular rotate is not a zero-filling shift.

RLC

logic

Rotate CY:A left through carry: old A.7 becomes CY and old CY becomes A.0.

Operand formOpcode(s), hexBytesMCFlags
RLC A3311CY
rlc a

Watch: Clear carry first for the low byte of a logical left shift.

RR

logic

Rotate A right: old bit 0 becomes bit 7. Carry is unchanged.

Operand formOpcode(s), hexBytesMCFlags
RR A0311—
rr a

Watch: Not an arithmetic right shift.

RRC

logic

Rotate CY:A right through carry: old A.0 becomes CY and old CY becomes A.7.

Operand formOpcode(s), hexBytesMCFlags
RRC A1311CY
rrc a

Watch: Clear carry for a zero-filled shift; for signed arithmetic shifting supply the original sign bit as carry.

SETB

bits

Set carry or a directly addressed bit to one.

Operand formOpcode(s), hexBytesMCFlags
SETB bitD221—
SETB CD311CY
setb 0x97

Watch: Setting a quasi-bidirectional port latch releases it for input; here bit 97 is P1.7.

SJMP

flow

Jump by a signed 8-bit displacement relative to PC after the two-byte instruction.

Operand formOpcode(s), hexBytesMCFlags
SJMP rel8022—
sjmp loop

Watch: Range -128..+127 bytes, not instructions. SJMP to itself is an infinite loop, not a return.

SUBB

arithmetic

A = A - source - incoming CY. Sets CY for unsigned borrow and updates AC and OV.

Operand formOpcode(s), hexBytesMCFlags
SUBB A,#imm9421CY AC OV
SUBB A,dir9521CY AC OV
SUBB A,@Ri96, 9711CY AC OV
SUBB A,Rn98, 99, 9A, 9B, 9C, 9D, 9E, 9F11CY AC OV
subb a,#1

Watch: Clear carry for ordinary subtraction; keep the borrow chain for multi-byte subtraction.

SWAP

logic

Exchange the high and low nibbles of A. Arithmetic flags remain unchanged.

Operand formOpcode(s), hexBytesMCFlags
SWAP AC411—
swap a

Watch: AB becomes BA in hexadecimal; this is not byte-order swapping of a 16-bit value.

XCH

data

Exchange A with an Rn register, direct byte or internal RAM through @Ri.

Operand formOpcode(s), hexBytesMCFlags
XCH A,dirC521—
XCH A,@RiC6, C711—
XCH A,RnC8, C9, CA, CB, CC, CD, CE, CF11—
xch a,r3

Watch: There is no XCH between two arbitrary direct bytes or with XDATA.

XCHD

data

Exchange the low four bits of A with the low four bits of internal RAM at @R0/@R1. High nibbles are unchanged.

Operand formOpcode(s), hexBytesMCFlags
XCHD A,@RiD6, D711—
xchd a,@r0

Watch: Only internal indirect RAM addressing is legal; it is not an external nibble-memory operation.

XRL

logic

Bitwise exclusive-OR using A or a direct byte destination. There is no Boolean XRL C,bit form.

Operand formOpcode(s), hexBytesMCFlags
XRL dir,A6221—
XRL dir,#imm6332—
XRL A,#imm6421—
XRL A,dir6521—
XRL A,@Ri66, 6711—
XRL A,Rn68, 69, 6A, 6B, 6C, 6D, 6E, 6F11—
xrl a,#0xFF

Watch: For Boolean XOR implement the operation with available carry/bit instructions instead.

FIELD NOTES / CODE51.TXT

04 / Practical assembly patterns

BIT TESTS AND COUNTED LOOPS
    mov r7,#10
again:
    ; body executes ten times
    djnz r7,again
    ret

Starting R7 at zero executes 256 iterations, not zero. DJNZ changes no arithmetic flags. Conditional branches and SJMP have a signed -128..+127 byte displacement measured from the instruction AFTER the branch. For a far condition, invert the test and use LJMP. AJMP/ACALL require the target to share the 2-KB page of the NEXT instruction, so code near a page boundary needs special care.

UNSIGNED COMPARE
    cjne a,#10,different
    ; A == 10; CY is now 0
    sjmp done
different:
    jc below         ; unsigned A < 10
    ; otherwise A > 10
below:
done:
    ret
PACKED BCD
    mov a,#0x45
    add a,#0x38
    da a             ; A = 83 (hex encoding of decimal 83)
    ret

DA is for the result of packed-BCD ADD/ADDC with valid BCD operands. It is not binary-to-decimal conversion and is not the correction for subtraction. DA may set CY; it does not clear an existing carry. AC/OV are not a decimal result test.

LOOKUP TABLE (DOS/51 PROGRAM)
    mov dptr,#table
    mov a,#2
    movc a,@a+dptr    ; A = ASCII '2'
    ret
table:
    db 48,49,50,51

CODE and XDATA overlap physically only where a BOARD wires them that way. DOS/51's upper RAM is deliberately visible in both spaces, allowing its assembler to write instructions with MOVX and then execute them. A generic 80C31 does not create executable RAM by itself.

MULTIPLY / DIVIDE
    mov a,#200
    mov 0xF0,#3      ; B
    mul ab           ; B:A = 0x0258 (600), CY=0, OV=1
    mov a,#200
    mov 0xF0,#7
    div ab           ; A=28 quotient, B=4 remainder, CY=0
    ret

DIV by zero sets OV and leaves A/B results undefined. Check B before dividing. MUL and DIV are unsigned; signed multiplication/division need additional sign handling.

ROTATE / SHIFT
RL/RR rotate within A and leave CY alone. RLC/RRC rotate the nine-bit CY:A chain. For an unsigned right shift use CLR C followed by RRC A. A multi-byte right shift starts at the high byte; a left shift starts at the low byte. SWAP A exchanges nibbles; XCH exchanges whole bytes; XCHD exchanges only the low nibbles of A and an indirectly addressed RAM byte.
MASKS
    anl 0x90,#0xFE   ; clear P1.0, preserve other latches
    orl 0x90,#0x01   ; set P1.0, preserve other latches
    cpl 0x90         ; BIT address 90: toggle P1.0

These examples use numeric SFR/bit addresses for DOS/51 portability. In a desktop assembler, prefer named EQU/bit declarations from the exact chip header. Do not assume another assembler's names, macros or directives exist in the small on-board assembler.

SUBROUTINE CONTRACTS
Document inputs, outputs, scratch registers and flags. Save only what your calling convention requires, but do not silently change the register bank. RET restores PC, not A, B, PSW or DPTR. Interrupt entry likewise does not preserve your working registers. For a callable DOS/51 program, keep the shell's stack valid and restore any state you borrow. An endless loop, disabled interrupts or a changed UART can make the shell appear dead; Reset recovers execution but does not undo files overwritten on the RAM disk.
↓ CODE51.TXT · ASCII

FIELD NOTES / CHIP51.TXT

05 / The stocked Philips P80C31SFPN

EXACT DEVICE, NOT AN IMAGINARY SUPER-8051
The shop part is Philips P80C31SFPN in PDIP-40. The Philips ordering table identifies S as the 16 MHz grade, F as -40 to +85 C and PN as plastic DIP. It is ROMless: an external program-memory circuit is required. Its 128 internal RAM bytes are not the DOS/51 board's external SRAM. No ADC, DAC, native SPI, I2C, USB, internal program flash, PCA or watchdog is supplied by the basic part.
IMPORTANT DATASHEET REVISION DISTINCTION
The classic 8031/80C31 baseline has Timer 0, Timer 1, five interrupt sources and one DPTR. Philips' 7 August 2000 80C31/80C32 specification explicitly lists additional functions even in its Table 1 headed 8XC51/80C31: Timer 2, dual DPTR, enhanced UART and four-level interrupt priority. Its prose and tables must not be replaced by a generic 8051 feature list. This guide covers that documented Philips register set separately below. The current emulator implements the classic baseline, NOT those extensions. No silicon-revision or bench test of the stocked chips has been performed here; consult the exact production revision before depending on an extension. The shared document explicitly excludes WUPD from the 80C31.
PDIP-40 PIN MAP - TOP VIEW, NOTCH AT TOP
             +------U------+
 P1.0/T2   1 |             | 40 VCC
 P1.1/T2EX 2 |             | 39 P0.0/AD0
 P1.2      3 |             | 38 P0.1/AD1
 P1.3      4 |             | 37 P0.2/AD2
 P1.4      5 |             | 36 P0.3/AD3
 P1.5      6 |             | 35 P0.4/AD4
 P1.6      7 |             | 34 P0.5/AD5
 P1.7      8 |             | 33 P0.6/AD6
 RST       9 |             | 32 P0.7/AD7
 P3.0/RXD 10 |             | 31 /EA
 P3.1/TXD 11 |             | 30 ALE
 P3.2/INT0 12 |             | 29 /PSEN
 P3.3/INT1 13 |             | 28 P2.7/A15
 P3.4/T0  14 |             | 27 P2.6/A14
 P3.5/T1  15 |             | 26 P2.5/A13
 P3.6/WR  16 |             | 25 P2.4/A12
 P3.7/RD  17 |             | 24 P2.3/A11
 XTAL2    18 |             | 23 P2.2/A10
 XTAL1    19 |             | 22 P2.1/A9
 GND      20 |_____________| 21 P2.0/A8

T2/T2EX labels refer to the documented Philips extension, not the emulator. INT0/INT1, WR and RD are active-low alternate signals. RST is active HIGH. /EA is the external-access PIN, not the interrupt-enable bit called EA. Tie /EA low for this ROMless CPU. Hold RST high for at least two machine cycles after the oscillator is running; allow oscillator startup at power-on.

EXTERNAL BUS
P0 multiplexes A0-A7 and D0-D7. ALE allows an external latch to retain the low address; P2 supplies A8-A15. /PSEN reads program memory, /RD and /WR access external data. A fetch begins with an address on the bus, ALE latches its low byte, then P0 changes to data and the selected read strobe enables memory output. Address decoding decides which physical chip responds. Use correctly timed glue logic and avoid two devices driving P0 together.
PORTS
P0 is open-drain as ordinary GPIO and needs external pull-ups for a high output; its bus function has active pull-ups. P1/P2/P3 have quasi-bidirectional pull-ups: write a one to release a pin for input, then read it. Writing zero drives low. There is no separate DDR register. The advertised 32 port lines are not 32 spare GPIOs on a ROMless system: external code uses P0/P2 continuously, and UART, interrupts, timers and memory strobes consume P3 pins.
CLOCK / POWER
The oscillator amplifier uses XTAL1/XTAL2 with a crystal circuit; for an external clock drive XTAL1 and leave XTAL2 open as specified. The part is static and supports a stopped clock. IDL=PCON.0 stops the CPU but keeps relevant peripherals running; an enabled interrupt or reset leaves idle. PD=PCON.1 enters power-down, stopping the oscillator while retaining RAM under the specified supply conditions. Do not assume an external interrupt wakes an 80C31: the shared datasheet's WUPD feature is explicitly unavailable on it. Reset is the conservative recovery path. GPIO/reset behavior and supply limits must be checked for the chosen mode.

The ordering table lists 2.7-5.5 V and up to 16 MHz for this S grade; use the electrical and AC tables for the actual supply, clock and loads. DOS/51 describes a 5 V board. An SD card needs 3.3 V power and suitable signal-level conversion; it is not a 5 V peripheral. Use a TTL-compatible serial adapter or a proper RS-232 transceiver, never raw RS-232 voltage on RXD.

↓ CHIP51.TXT · ASCII

FIELD NOTES / IO51.TXT

06 / Timers, UART and interrupts

BASELINE SFR DESK REFERENCE (BYTE ADDRESSES)
80 P0    81 SP    82 DPL   83 DPH   87 PCON
88 TCON  89 TMOD  8A TL0   8B TL1   8C TH0   8D TH1
90 P1    98 SCON  99 SBUF  A0 P2    A8 IE    B0 P3
B8 IP    D0 PSW   E0 ACC   F0 B
Bit-addressable: P0, TCON, P1, SCON, P2, IE, P3, IP, PSW, ACC, B. SP resets to 07 and port latches to FF. Reset is not an instruction to clear all RAM.
TIMER 0 AND TIMER 1
TMOD has one four-bit field per timer: Timer 1 in bits 7-4, Timer 0 in bits 3-0. Each field is GATE, C/T, M1, M0. GATE=1 requires the corresponding INT pin high AND TRn set. C/T=0 counts internal machine cycles; C/T=1 counts sampled falling edges on T0/T1. Mode 0 is a 13-bit counter (THn plus the low five bits of TLn); mode 1 is 16-bit; mode 2 is 8-bit auto-reload from THn into TLn. Mode 3 splits Timer 0 into two 8-bit timers: TL0 keeps Timer 0 controls, TH0 borrows TR1/TF1. Timer 1's normal interrupt/control use is consequently restricted; do not treat mode 3 as two independent ordinary 16-bit timers. Timer 1 mode 3 stops its counting.

TCON bits: TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0. TRn starts a timer; TFn records overflow. Timer overflow flags are cleared on hardware interrupt vectoring; polling code must clear them itself. Stop the timer before making a coherent multi-byte configuration change.

BARE-METAL POLLED TIMER (NOT DOS/51 INITIALIZATION)
    anl 0x89,#0xF0   ; preserve Timer 1 mode
    orl 0x89,#0x01   ; Timer 0 mode 1, internal clock
    clr 0x8C         ; TR0 (bit address)
    mov 0x8C,#0xFC   ; TH0 (byte address)
    mov 0x8A,#0x66   ; TL0: 65536 - 922
    clr 0x8D         ; TF0 (bit address)
    setb 0x8C
wait0:
    jnb 0x8D,wait0
    clr 0x8C
    clr 0x8D

At 11.0592 MHz, 922 ticks are approximately 1 ms. Polling/setup overhead makes this NOT an exact 1-ms software delay. For periodic precision use a suitable reload strategy and account for interrupt latency. External event counting is sampled, so input edge rate and pulse widths must meet the chip timing requirements.

UART: SCON = SM0 SM1 SM2 REN TB8 RB8 TI RI
Mode 0: synchronous shift register, normally fosc/12.
Mode 1: 8-bit UART with start/stop bits, variable baud.
Mode 2: 9-bit UART, fixed fosc/64 or fosc/32 via SMOD.
Mode 3: 9-bit UART, variable baud.
REN enables reception. Write SBUF to transmit; read SBUF to retrieve the separately buffered received byte. RI and TI share one interrupt vector and are cleared by SOFTWARE, not by reading SBUF or entering the ISR. Mode 1's RB8 reflects the received stop bit when appropriate; modes 2/3 use TB8/RB8 for the ninth data bit and SM2 for multiprocessor filtering.
With Timer 1 mode 2, UART modes 1/3 use:
baud = (2^SMOD * fosc) / (384 * (256 - TH1)).
At 11.0592 MHz: TH1=FD and SMOD=0 gives 9600; TH1=FD and SMOD=1 gives 19200; TH1=FF and SMOD=1 gives 57600. Arbitrary baud rates are not all representable.
BARE-METAL 9600-BAUD SETUP (11.0592 MHz)
    anl 0x87,#0x7F   ; clear SMOD, preserve other PCON bits
    anl 0x89,#0x0F
    orl 0x89,#0x20   ; Timer 1 mode 2
    mov 0x8D,#0xFD   ; TH1 reload
    mov 0x8B,#0xFD   ; TL1 initial count
    mov 0x98,#0x50   ; mode 1, REN=1, RI/TI=0
    setb 0x8E        ; TR1

Do not run that setup inside DOS/51: its console is already 57600 baud, and Timer 1 belongs to it. To poll reception, wait for RI, read SBUF and clear RI. A polled transmitter writes SBUF, waits for TI and clears TI; ensure TI was not left set by an earlier transmission. At higher throughput handle overrun in software: there is no general-purpose deep hardware FIFO.

INTERRUPTS
Reset jumps to 0000. Baseline vectors: INT0=0003, Timer0=000B, INT1=0013, Timer1=001B, serial=0023. Each vector is normally a short LJMP stub because adjacent entries are only eight bytes apart. IE.7 (EA) globally enables requests; IE bits 0-4 enable EX0, ET0, EX1, ET1 and ES. IP bits select low/high priority in the baseline core. A high-priority handler can preempt a low-priority handler, not vice versa; equal-priority service is not reentrant. Fixed polling order resolves simultaneous equal-priority requests in the vector order above.

IT0/IT1=1 selects falling-edge external interrupts; zero selects active-low level requests. Release a level-triggered request before returning or it can immediately retrigger. Switch bounce requires debouncing. Hardware pushes PC only. Save every clobbered register including PSW as necessary; restore in reverse order and finish with RETI so the interrupt controller releases its in-service state. RET alone is not an interrupt return. Serial ISRs test BOTH RI and TI. A register-bank switch can reduce saves, but each nested priority level needs a nonconflicting bank and RAM allocation.

On DOS/51 the lower CODE vectors are in ROM. An application at 8000 cannot patch them just by writing that numerical XDATA address. Use documented firmware facilities or a different ROM build rather than assuming vector ownership.

↓ IO51.TXT · ASCII

FIELD NOTES / PHIL51.TXT

07 / Philips-specific extensions

SCOPE
This chapter follows Philips' 2000-08-07 80C31/80C32 specification, including its Table 1 explicitly headed 8XC51/80C31. These functions are not part of the standard instruction set and are not implemented by the present DOS/51 emulator. The shop's actual silicon revision has not been bench-identified; the shared datasheet must be reconciled with the particular chip before writing extension-dependent production code. Core instructions do not change: SFR configuration exposes the extra hardware.
TIMER 2
Documented registers: T2CON=C8, T2MOD=C9, RCAP2L=CA, RCAP2H=CB, TL2=CC, TH2=CD. T2CON is bit-addressable; T2MOD is not. Timer 2 adds 16-bit capture, auto-reload up/down counting and an independent UART baud source. P1.0 is T2/clock-out and P1.1 is T2EX.
T2CON bits: TF2 EXF2 RCLK TCLK EXEN2 TR2 C/T2 CP/RL2.
TR2 starts the counter; C/T2 chooses internal timer or external falling-edge count. CP/RL2=1 selects capture; zero selects reload when not overridden by the UART baud configuration. EXEN2 enables external capture/reload events. Software must clear TF2 and EXF2 in the shared Timer 2 handler; unlike Timer 0/1 overflow flags they are not automatically cleared on vector entry.

Capture copies TL2/TH2 into RCAP2L/H on a qualifying T2EX falling edge without stopping the running counter. Auto-reload reloads the capture/reload value on overflow, and optionally on a T2EX edge. T2MOD.DCEN enables up/down selection by T2EX; EXF2 behavior then changes and is not the same interrupt source as ordinary capture. Consult the mode diagram rather than reusing a capture ISR unaltered.

Setting TCLK and/or RCLK routes Timer 2 to transmit/receive baud respectively; the other direction can remain on Timer 1. Internally clocked UART baud is fosc / [32 * (65536 - RCAP2)]. At 11.0592 MHz, RCAP2=FFDC gives 9600 baud. In this baud mode the internal counter advances at fosc/2, not fosc/12, and rollover does not set TF2. Stop TR2 before changing reload/counter configuration.

For clock output on P1.0, clear C/T2, set T2MOD.T2OE and TR2. Frequency = fosc / [4 * (65536 - RCAP2)]. The pin is no longer ordinary GPIO. Clock-out and baud generation do not provide an ordinary overflow interrupt in the same way as timer mode. These formulas do not describe the emulator's bit-banged SD clock.

DUAL DATA POINTER
AUXR1 at A2, bit 0 (DPS), selects DPTR0 or DPTR1. The selected pointer appears through DPL/DPH and is used by MOV DPTR,#imm16, INC DPTR, MOVC A,@A+DPTR, MOVX through DPTR and JMP @A+DPTR. Save selection as well as pointer values across calls/interrupts. A2 is NOT a bit-addressable SFR; do not write SETB 0xA2 expecting to select DPTR1 (that bit address belongs to P2). Mask the byte with ORL 0xA2,#1 / ANL 0xA2,#0xFE. Reserve undefined bits. Two pointers let a copy loop alternate source/destination addresses without reloading both address bytes every iteration.
FOUR INTERRUPT PRIORITIES
The documented IPH register at B7 supplies the high priority bit; IP at B8 supplies the low bit. IPH:IP combinations 00,01,10,11 select levels 0-3 for each source. IPH is not bit-addressable. Timer 2 adds ET2 at IE.5, PT2 at IP.5 and PT2H at IPH.5, with vector 002B. The serial source still shares RI/TI. Preserve unused bits and arrange a real stack/register-save strategy for nested handlers.
ENHANCED UART
SADDR=A9 holds a slave address and SADEN=B9 masks it for automatic address recognition. These byte SFRs are not bit-addressable. SM2 and the ninth-bit multiprocessor protocol allow address filtering rather than interrupting for every data byte. Review given/broadcast-address matching in the Philips diagrams before choosing a mask.

PCON.6 (SMOD0) chooses whether SCON.7 is the mode bit SM0 or framing-error status FE. A missing stop bit sets FE when this feature is selected; software clears it. Configure UART mode with the correct SMOD0 setting first, so a status bit is not mistaken for the mode-selection bit. PCON.7 remains baud doubler SMOD1. This is not a new CPU opcode or a second UART.

POWER / ALE
AUXR=8E, bit 0 (AO), controls the reduced-EMI ALE feature described by Philips. It is not bit-addressable. A ROMless board relies on the external fetch bus, so do not casually suppress the address-latch mechanism. Verify the documented behavior for external program/data cycles and the actual board's latch.

PCON also documents a power-off flag POF and software flags GF1/GF0; reset source affects reset values. The shared datasheet describes asynchronous port reset and power-down behavior. Its AUXR1.WUPD external-interrupt wake option is specifically marked unavailable on 80C31; do not promise 80C32 wake behavior for this stocked part. Full electrical limits, reset timing and mode transitions belong to the original manufacturer specification linked in the web edition.

↓ PHIL51.TXT · ASCII

FIELD NOTES / DOS51.TXT

08 / DOS/51 lab and troubleshooting

WHAT THE EMULATOR ACTUALLY MODELS
Classic 80C31 CPU, 128-byte internal RAM, Timer 0/1, baseline interrupt controller, UART and power-control states. The board adds an external 32-KB ROM and two 32-KB SRAMs, then implements SD-card SPI in software. Timer 2, dual DPTR, enhanced address-recognition UART, IPH and analog electrical behavior are not modeled. Reading the Philips extensions chapter is not proof that those features run in this emulator.
BOARD MAP
CODE  0000-7FFF : DOS/51 ROM
CODE  8000-FFFF : upper SRAM (executable)
XDATA 0000-7FFF : lower SRAM (data)
XDATA 8000-FFFF : same upper SRAM as CODE
The RAM disk uses XDATA 2000-7FFF (24 KB). Application/assembler/editor storage uses the upper SRAM. Firmware state also occupies internal/external RAM: those bytes are not all free application scratch space.
SD CARD / FIELD MANUAL
A first-use browser card includes this ASCII field manual and DropBlocks. Existing browser cards are not replaced. Use the Install field manual button to add missing manual files to a FAT16 card; existing files with the same names are left unchanged. FAT32 cards can be inserted for firmware access, but the browser-side installer is FAT16-only.
    sdls
    sdget guide51.txt
    cat guide51.txt
    rm guide51.txt
    sdget mem51.txt
    cat mem51.txt

Remove an imported chapter from the RAM disk when done; the SD copy remains. The full manual is split into small files because the RAM disk is only 24 KB and may already contain your work. If sdget reports insufficient space, save/delete unneeded RAM files first; do not blank the card to solve a RAM-disk capacity problem. Use the online edition for search and the complete opcode table. Download card saves the actual FAT16 image with the text files in its root.

BOARD GPIO
P1.0-P1.3 are the example LED lines (active low). P1.4=/CS, P1.5=SCK, P1.6=MOSI, P1.7=MISO for software SPI. Leave the MISO latch high for input. Use bit instructions on LED bits; do not replace all of P1 with an arbitrary byte while the SD card is attached. P0/P2 serve the external memory bus; P3.0/1 serve the serial console; P3.6/7 serve external RAM strobes.

The SPI protocol and FAT filesystem are firmware/board features, not CPU instructions or on-chip 80C31 peripherals. A real SD card needs a 3.3-V supply and suitable logic translation. The browser models digital transactions, not regulator current, wire capacitance, analogue signal edges or component damage.

GETTING UNSTUCK
- No console after a test: did you change Timer 1, SCON, PCON, SP or IE? Reset restarts the ROM while preserving SRAM. Power cycle clears RAM and RAM-disk files; the SD card has separate persistence.
- A conditional branch behaves strangely: check A rather than an imaginary zero flag; check signed versus unsigned comparisons and whether an instruction changed CY.
- Wrong RAM byte: distinguish #literal from direct address and MOV from MOVX/MOVC. Check the selected register bank.
- A bit write hits the wrong peripheral: byte and bit addresses are different namespaces. Only selected SFR bytes are bit-addressable.
- Table lookup reads nonsense: initialize A; MOVC adds it to DPTR or post-opcode PC. It does not increment DPTR afterwards.
- RET jumps into nowhere: inspect unmatched PUSH/POP, stack overflow above 7F, corrupted return addresses or an unintended jump into a subroutine.
- A port will not go high: an output latch zero drives low; release it with one for input. P0 GPIO needs pull-ups. On the actual ROMless board many pins are already the memory bus.
- Timer counts at the wrong rate: classic timer mode is oscillator/12; UART Timer 2 baud mode is a distinct Philips extension. A cycle table is not an oscillator-clock table.
- New SD files are missing: an old saved card is intentionally preserved. Click Install field manual; then sdls. Do not erase a visitor's saved work just to seed a new example.
NEXT EXPERIMENTS
1. Change the printed character in first.asm and predict the ASCII code.
2. Add 0x7F+1 and 0xFF+1; inspect results and PSW with the debugger/memory facilities available to your setup.
3. Build a lookup table and prove the difference between MOVC and MOVX.
4. Toggle only P1.0 while preserving all SD lines.
5. On a separate bare-metal ROM, build a Timer 0 ISR with an explicit register-save contract; keep DOS/51's ROM untouched.
↓ DOS51.TXT · ASCII

APPENDIX / EVERY BYTE AT A GLANCE

The 16 × 16 opcode map.

High nibble down the left, low nibble across the top. Scroll the map horizontally on a small screen; select a cell for the instruction explanation. A5 is deliberately reserved.

HEX_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0_NOPAJMP addr11LJMP addr16RR AINC AINC dirINC @R0INC @R1INC R0INC R1INC R2INC R3INC R4INC R5INC R6INC R7
1_JBC bit,relACALL addr11LCALL addr16RRC ADEC ADEC dirDEC @R0DEC @R1DEC R0DEC R1DEC R2DEC R3DEC R4DEC R5DEC R6DEC R7
2_JB bit,relAJMP addr11RETRL AADD A,#immADD A,dirADD A,@R0ADD A,@R1ADD A,R0ADD A,R1ADD A,R2ADD A,R3ADD A,R4ADD A,R5ADD A,R6ADD A,R7
3_JNB bit,relACALL addr11RETIRLC AADDC A,#immADDC A,dirADDC A,@R0ADDC A,@R1ADDC A,R0ADDC A,R1ADDC A,R2ADDC A,R3ADDC A,R4ADDC A,R5ADDC A,R6ADDC A,R7
4_JC relAJMP addr11ORL dir,AORL dir,#immORL A,#immORL A,dirORL A,@R0ORL A,@R1ORL A,R0ORL A,R1ORL A,R2ORL A,R3ORL A,R4ORL A,R5ORL A,R6ORL A,R7
5_JNC relACALL addr11ANL dir,AANL dir,#immANL A,#immANL A,dirANL A,@R0ANL A,@R1ANL A,R0ANL A,R1ANL A,R2ANL A,R3ANL A,R4ANL A,R5ANL A,R6ANL A,R7
6_JZ relAJMP addr11XRL dir,AXRL dir,#immXRL A,#immXRL A,dirXRL A,@R0XRL A,@R1XRL A,R0XRL A,R1XRL A,R2XRL A,R3XRL A,R4XRL A,R5XRL A,R6XRL A,R7
7_JNZ relACALL addr11ORL C,bitJMP @A+DPTRMOV A,#immMOV dir,#immMOV @R0,#immMOV @R1,#immMOV R0,#immMOV R1,#immMOV R2,#immMOV R3,#immMOV R4,#immMOV R5,#immMOV R6,#immMOV R7,#imm
8_SJMP relAJMP addr11ANL C,bitMOVC A,@A+PCDIV ABMOV dir,dirMOV dir,@R0MOV dir,@R1MOV dir,R0MOV dir,R1MOV dir,R2MOV dir,R3MOV dir,R4MOV dir,R5MOV dir,R6MOV dir,R7
9_MOV DPTR,#imm16ACALL addr11MOV bit,CMOVC A,@A+DPTRSUBB A,#immSUBB A,dirSUBB A,@R0SUBB A,@R1SUBB A,R0SUBB A,R1SUBB A,R2SUBB A,R3SUBB A,R4SUBB A,R5SUBB A,R6SUBB A,R7
A_ORL C,/bitAJMP addr11MOV C,bitINC DPTRMUL ABRESERVEDMOV @R0,dirMOV @R1,dirMOV R0,dirMOV R1,dirMOV R2,dirMOV R3,dirMOV R4,dirMOV R5,dirMOV R6,dirMOV R7,dir
B_ANL C,/bitACALL addr11CPL bitCPL CCJNE A,#imm,relCJNE A,dir,relCJNE @R0,#imm,relCJNE @R1,#imm,relCJNE R0,#imm,relCJNE R1,#imm,relCJNE R2,#imm,relCJNE R3,#imm,relCJNE R4,#imm,relCJNE R5,#imm,relCJNE R6,#imm,relCJNE R7,#imm,rel
C_PUSH dirAJMP addr11CLR bitCLR CSWAP AXCH A,dirXCH A,@R0XCH A,@R1XCH A,R0XCH A,R1XCH A,R2XCH A,R3XCH A,R4XCH A,R5XCH A,R6XCH A,R7
D_POP dirACALL addr11SETB bitSETB CDA ADJNZ dir,relXCHD A,@R0XCHD A,@R1DJNZ R0,relDJNZ R1,relDJNZ R2,relDJNZ R3,relDJNZ R4,relDJNZ R5,relDJNZ R6,relDJNZ R7,rel
E_MOVX A,@DPTRAJMP addr11MOVX A,@R0MOVX A,@R1CLR AMOV A,dirMOV A,@R0MOV A,@R1MOV A,R0MOV A,R1MOV A,R2MOV A,R3MOV A,R4MOV A,R5MOV A,R6MOV A,R7
F_MOVX @DPTR,AACALL addr11MOVX @R0,AMOVX @R1,ACPL AMOV dir,AMOV @R0,AMOV @R1,AMOV R0,AMOV R1,AMOV R2,AMOV R3,AMOV R4,AMOV R5,AMOV R6,AMOV R7,A

10 / PRIMARY SOURCES & SCOPE

Know which machine you mean.

Written for the Philips P80C31SFPN stocked at iMania, with standard instruction semantics checked against Intel’s original instruction descriptions. This is an independently written teaching guide, not a reproduction of a manufacturer manual. Hardware extensions are attributed to the dated Philips specification and separated from emulator support; no physical chip bench-validation is claimed.

Edition 2026-11-14. Classic CPU timings are hardware reference values; browser speed and modeled peripheral coverage are separate matters. The Philips shared-family datasheet contains revision-sensitive extensions: see chapter 07 instead of assuming every generic 8051, emulator or old 80C31 has them.

Back to the machine →