# 8051 指令集 ### NOP 空指令。不做任何事情,只是消耗一個週期。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ----- | ---- | ------ | ------- | ------ | | `NOP` | Nope | 1 Byte | 1 Cycle | `00` | ## 資料傳輸 ### MOV 複製資料到指定的暫存器。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ---------------------- | ----------------------- | ------ | ------- | ------------ | | `MOV A, #immed` | `A = immed` | 2 Byte | 1 Cycle | `74` | | `MOV A, direct` | `A = (direct)` | 2 Byte | 1 Cycle | `E5` | | `MOV A, @Ri` | `A = (Ri)` | 1 Byte | 1 Cycle | `E6`, `E7` | | `MOV A, Rn` | `A = Rn` | 1 Byte | 1 Cycle | `E8` \~ `EF` | | `MOV direct, #immed` | `(direct) = immed` | 3 Byte | 2 Cycle | `75` | | `MOV direct1, direct2` | `(direct1) = (direct2)` | 3 Byte | 2 Cycle | `85` | | `MOV direct, @Ri` | `(direct) = (Ri)` | 2 Byte | 2 Cycle | `86`, `87` | | `MOV direct, Rn` | `(direct) = Rn` | 2 Byte | 2 Cycle | `88` \~ `8F` | | `MOV direct, A` | `(direct) = A` | 2 Byte | 1 Cycle | `F5` | | `MOV @Ri, #immed` | `(Ri) = immed` | 2 Byte | 1 Cycle | `76`, `77` | | `MOV @Ri, direct` | `(Ri) = (direct)` | 2 Byte | 2 Cycle | `A6`, `A7` | | `MOV @Ri, A` | `(Ri) = A` | 1 Byte | 1 Cycle | `F6`, `F7` | | `MOV Rn, #immed` | `Rn = immed` | 2 Byte | 1 Cycle | `78` \~ `7F` | | `MOV Rn, direct` | `Rn = (direct)` | 2 Byte | 2 Cycle | `A8` \~ `AF` | | `MOV Rn, A` | `Rn = A` | 1 Byte | 1 Cycle | `F8` \~ `FF` | | `MOV DPTR, #immed16` | `DPTR = immed16` | 3 Byte | 2 Cycle | `90` | | `MOV bit, C` | `(bit) = C` | 2 Byte | 2 Cycle | `92` | | `MOV C, bit` | `C = (bit)` | 2 Byte | 1 Cycle | `A2` | ### MOVC 將 code memory 或 program memory 中指定位址的值複製到累加器。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ----------------- | ---------------- | ------ | ------- | ------ | | `MOVC A, @A+PC` | `A = (A + PC)` | 1 Byte | 2 Cycle | `83` | | `MOVC A, @A+DPTR` | `A = (A + DPTR)` | 1 Byte | 2 Cycle | `93` | ### MOVX The **MOVX** instruction transfers data between the accumulator and external data memory. External memory may be addressed via 16-bits in the DPTR register or via 8-bits in the `R0` or `R1` registers. When using 8-bit addressing, Port 2 must contain the high-order byte of the address. 在 EdSim51 中,`MOVX` 指令未被實現。 | 指令 | 描述 | 長度 | 週期 | Opcode | | --------------- | ------------ | ------ | ------- | ---------- | | `MOVX A, @DPTR` | `A = (DPTR)` | 1 Byte | 2 Cycle | `E0` | | `MOVX A, @Ri` | `A = (Ri)` | 1 Byte | 2 Cycle | `E2`, `E3` | | `MOVX @DPTR, A` | `(DPTR) = A` | 1 Byte | 2 Cycle | `F0` | | `MOVX @Ri, A` | `(Ri) = A` | 1 Byte | 2 Cycle | `F2`, `F3` | ### CLR, SETB 將指定目標設為 0 或 1。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ---------- | ----------- | ------ | ------- | ------ | | `CLR bit` | `(bit) = 0` | 2 Byte | 1 Cycle | `C2` | | `CLR C` | `CY = 0` | 1 Byte | 1 Cycle | `C3` | | `CLR A` | `A = 0` | 1 Byte | 1 Cycle | `E4` | | `SETB bit` | `(bit) = 1` | 2 Byte | 1 Cycle | `D2` | | `SETB C` | `CY = 1` | 1 Byte | 1 Cycle | `D3` | ### SWAP, XCH, XCHD `SWAP` 將累加器的高 4 位與低 4 位交換。 `XCH`, `XCHD` 將累加器與指定暫存器的值交換,其中 `XCHD` 只會交換低 4 位。 | 指令 | 描述 | 長度 | 週期 | Opcode | | --------------- | ---------------------- | ------ | ------- | ------------ | | `SWAP A` | `A[7:4] <-> A[3:0]` | 1 Byte | 1 Cycle | `C4` | | `XCH A, direct` | `A <-> (direct)` | 2 Byte | 1 Cycle | `C5` | | `XCH A, @Ri` | `A <-> (Ri)` | 1 Byte | 1 Cycle | `C6`, `C7` | | `XCH A, Rn` | `A <-> Rn` | 1 Byte | 1 Cycle | `C8` \~ `CF` | | `XCHD A, @Ri` | `A[3:0] <-> (Ri)[3:0]` | 1 Byte | 1 Cycle | `D6`, `D7` | ## 算術運算 ### ADD 將累加器與指定的值相加,結果存回累加器。會影響 PSW。 | 指令 | 描述 | 長度 | 週期 | Opcode | | --------------- | ------------------ | ------ | ------- | ------------ | | `ADD A, #immed` | `A = A + immed` | 2 Byte | 1 Cycle | `24` | | `ADD A, direct` | `A = A + (direct)` | 2 Byte | 1 Cycle | `25` | | `ADD A, @Ri` | `A = A + (Ri)` | 1 Byte | 1 Cycle | `26`, `27` | | `ADD A, Rn` | `A = A + Rn` | 1 Byte | 1 Cycle | `28` \~ `2F` | ### ADDC, SUBB 將累加器與指定的值、`CY` 相加、相減,結果存回累加器。會影響 PSW。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ---------------- | ----------------------- | ------ | ------- | ------------ | | `ADDC A, #immed` | `A = A + immed - CY` | 2 Byte | 1 Cycle | `34` | | `ADDC A, direct` | `A = A + (direct) - CY` | 2 Byte | 1 Cycle | `35` | | `ADDC A, @Ri` | `A = A + (Ri) - CY` | 1 Byte | 1 Cycle | `36`, `37` | | `ADDC A, Rn` | `A = A + Rn - CY` | 1 Byte | 1 Cycle | `38` \~ `3F` | | `SUBB A, #immed` | `A = A - immed - CY` | 2 Byte | 1 Cycle | `94` | | `SUBB A, direct` | `A = A - (direct) - CY` | 2 Byte | 1 Cycle | `95` | | `SUBB A, @Ri` | `A = A - (Ri) - CY` | 1 Byte | 1 Cycle | `96`, `97` | | `SUBB A, Rn` | `A = A - Rn - CY` | 1 Byte | 1 Cycle | `98` \~ `9F` | ### INC, DEC 將指定暫存器的值遞增、遞減。不會影響 PSW。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ------------ | ------------------------- | ------ | ------- | ------------ | | `INC A` | `A = A + 1` | 1 Byte | 1 Cycle | `04` | | `INC direct` | `(direct) = (direct) + 1` | 2 Byte | 1 Cycle | `05` | | `INC @Ri` | `(Ri) = (Ri) + 1` | 1 Byte | 1 Cycle | `06`, `07` | | `INC Rn` | `Rn = Rn + 1` | 1 Byte | 1 Cycle | `08` \~ `0F` | | `INC DPTR` | `DPTR = DPTR + 1` | 1 Byte | 2 Cycle | `A3` | | `DEC A` | `A = A - 1` | 1 Byte | 1 Cycle | `14` | | `DEC direct` | `(direct) = (direct) - 1` | 2 Byte | 1 Cycle | `15` | | `DEC @Ri` | `(Ri) = (Ri) - 1` | 1 Byte | 1 Cycle | `16`, `17` | | `DEC Rn` | `Rn = Rn - 1` | 1 Byte | 1 Cycle | `18` \~ `1F` | ### MUL, DIV The **MUL** instruction multiplies the unsigned 8-bit integer in the accumulator and the unsigned 8-bit integer in the B register producing a 16-bit product. The low-order byte of the product is returned in the accumulator. The high-order byte of the product is returned in the B register. The `OV` flag is set if the product is greater than 255 (`0FFh`), otherwise it is cleared. The carry flag is always cleared. The **DIV** instuction divides the unsigned 8-bit integer in the accumulator by the unsigned 8-bit integer in register B. After the division, the quotient is stored in the accumulator and the remainder is stored in the B register. The carry and `OV` flags are cleared. If the B register begins with a value of `00h` the division operation is undefined, the values of the accumulator and B register are undefined after the division, and the `OV` flag will be set indicating a division-by-zero error. | 指令 | 描述 | 長度 | 週期 | Opcode | | -------- | ------------ | ------ | ------- | ------ | | `MUL AB` | `BA = A * B` | 1 Byte | 4 Cycle | `A4` | | `DIV AB` | `AB = A / B` | 1 Byte | 4 Cycle | `84` | ### DA 累加器進行 BCD 加法運算時,使用二進位加法器 (ADD, ADDC) 後,將結果調整回 BCD 碼。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ------ | --------------- | ------ | ------- | ------ | | `DA A` | 變換成 BCD 格式 | 1 Byte | 1 Cycle | `D4` | ## 位元運算 ### ORL 逐位元做 OR 運算,結果存入指定暫存器。 | 指令 | 描述 | 長度 | 週期 | Opcode | | -------------------- | ------------------------------ | ------ | ------- | ------------ | | `ORL direct, A` | `(direct) = (direct) \| A` | 2 Byte | 1 Cycle | `42` | | `ORL direct, #immed` | `(direct) = (direct) \| immed` | 3 Byte | 2 Cycle | `43` | | `ORL A, #immed` | `A = A \| immed` | 2 Byte | 1 Cycle | `44` | | `ORL A, direct` | `A = A \| (direct)` | 2 Byte | 1 Cycle | `45` | | `ORL A, @Ri` | `A = A \| (Ri)` | 1 Byte | 1 Cycle | `46`, `47` | | `ORL A, Rn` | `A = A \| Rn` | 1 Byte | 1 Cycle | `48` \~ `4F` | | `ORL C, bit` | `C = C \| (bit)` | 2 Byte | 2 Cycle | `72` | | `ORL C, /bit` | `C = C \| ~(bit)` | 2 Byte | 2 Cycle | `A0` | ### ANL 逐位元做 AND 運算,結果存入指定暫存器。 | 指令 | 描述 | 長度 | 週期 | Opcode | | -------------------- | ----------------------------- | ------ | ------- | ------------ | | `ANL direct, A` | `(direct) = (direct) & A` | 2 Byte | 1 Cycle | `52` | | `ANL direct, #immed` | `(direct) = (direct) & immed` | 3 Byte | 2 Cycle | `53` | | `ANL A, #immed` | `A = A & immed` | 2 Byte | 1 Cycle | `54` | | `ANL A, direct` | `A = A & (direct)` | 2 Byte | 1 Cycle | `55` | | `ANL A, @Ri` | `A = A & (Ri)` | 1 Byte | 1 Cycle | `56`, `57` | | `ANL A, Rn` | `A = A & Rn` | 1 Byte | 1 Cycle | `58` \~ `5F` | | `ANL C, bit` | `C = C & (bit)` | 2 Byte | 2 Cycle | `82` | | `ANL C, /bit` | `C = C & ~(bit)` | 2 Byte | 2 Cycle | `B0` | ### XRL 逐位元做 XOR 運算,結果存入指定暫存器。 | 指令 | 描述 | 長度 | 週期 | Opcode | | -------------------- | ----------------------------- | ------ | ------- | ------------ | | `XRL direct, A` | `(direct) = (direct) ^ A` | 2 Byte | 1 Cycle | `62` | | `XRL direct, #immed` | `(direct) = (direct) ^ immed` | 3 Byte | 2 Cycle | `63` | | `XRL A, #immed` | `A = A ^ immed` | 2 Byte | 1 Cycle | `64` | | `XRL A, direct` | `A = A ^ (direct)` | 2 Byte | 1 Cycle | `65` | | `XRL A, @Ri` | `A = A ^ (Ri)` | 1 Byte | 1 Cycle | `66`, `67` | | `XRL A, Rn` | `A = A ^ Rn` | 1 Byte | 1 Cycle | `68` \~ `6F` | ### RR, RRC, RL, RLC 將累加器的值循環移位一位,其中 `C` 表示 `CY`也會參與移位。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ------- | ------------------ | ------ | ------- | ------ | | `RR A` | 向右旋轉 | 1 Byte | 1 Cycle | `03` | | `RRC A` | 向右旋轉 (含 `CY`) | 1 Byte | 1 Cycle | `13` | | `RL A` | 向左旋轉 | 1 Byte | 1 Cycle | `23` | | `RLC A` | 向左旋轉 (含 `CY`) | 1 Byte | 1 Cycle | `33` | ### CPL 將累加器的值逐位元取反。 | 指令 | 描述 | 長度 | 週期 | Opcode | | --------- | ---------------- | ------ | ------- | ------ | | `CPL A` | `A = ~A` | 1 Byte | 1 Cycle | `F4` | | `CPL bit` | `(bit) = ~(bit)` | 2 Byte | 1 Cycle | `B2` | | `CPL C` | `CY = ~CY` | 1 Byte | 1 Cycle | `B3` | ## 跳轉指令 ### JMP, AJMP, LJMP, SJMP The **JMP** instruction transfers execution to the address generated by adding the 8-bit value in the accumulator to the 16-bit value in the DPTR register. Neither the accumulator nor the DPTR register are altered. No flags are affected by this instruction. The **AJMP** instruction transfers program execution to the specified address. The address is formed by combining the 5 high-order bits of the address of the following instruction (for `A15`-`A11`), the 3 high-order bits of the opcode (for `A10`-`A8`), and the second byte of the instruction (for `A7`-`A0`). The destination address must be located in the same 2KByte block of program memory as the opcode following the **AJMP** instruction. No flags are affected. The **LJMP** instruction transfers program execution to the specified 16-bit address. The PC is loaded with the high-order and low-order bytes of the address from the second and third bytes of this instruction respectively. No flags are affected by this instruction. The **SJMP** instruction transfers execution to the specified address. The address is calculated by adding the signed relative offset in the second byte of the instruction to the address of the following instruction. The range of destination addresses is from 128 before the next instruction to 127 bytes after the next instruction. 在 EdSim51 中,似乎只要 `JMP LABEL` 即可。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ------------- | -------- | ------ | ------- | ---------------- | | `JMP @A+DPTR` | | 1 Byte | 2 Cycle | `73` | | `AJMP addr11` | 絕對跳轉 | 2 Byte | 2 Cycle | `X1`, `X` 為偶數 | | `LJMP addr16` | 長跳轉 | 3 Byte | 2 Cycle | `02` | | `SJMP offset` | 短跳轉 | 2 Byte | 2 Cycle | `80` | ### JB, JBC, JNB, JC, JNC, JZ, JNZ 根據指定的值決定是否跳轉。不會影響 PSW。 `JBC` 會在跳轉時將指定位元清除(設為 0)。 在 EdSim51 中,`offset` 欄位可以填入目標位置的 LABEL。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ----------------- | --------------------- | ------ | ------- | ------ | | `JBC bit, offset` | 若 `(bit) = 1` 則跳轉 | 3 Byte | 2 Cycle | `10` | | `JB bit, offset` | 若 `(bit) = 1` 則跳轉 | 3 Byte | 2 Cycle | `20` | | `JNB bit, offset` | 若 `(bit) = 0` 則跳轉 | 3 Byte | 2 Cycle | `30` | | `JC offset` | 若 `CY = 1` 則跳轉 | 2 Byte | 2 Cycle | `40` | | `JNC offset` | 若 `CY = 0` 則跳轉 | 2 Byte | 2 Cycle | `50` | | `JZ offset` | 若 `A = 0` 則跳轉 | 2 Byte | 2 Cycle | `60` | | `JNZ offset` | 若 `A != 0` 則跳轉 | 2 Byte | 2 Cycle | `70` | ### CJNE Compare and Jump if Not Equal。 比較目標暫存器與指定值,若不相等則跳轉。當目標暫存器的值小於指定值時,`CY` 設為 1,否則設為 0。 在 EdSim51 中,`offset` 欄位可以填入目標位置的 LABEL。 | 指令 | 描述 | 長度 | 週期 | Opcode | | ------------------------------ | ---- | ------ | ------- | ------------ | | `CJNE A, #immediate, offset` | | 3 Byte | 2 Cycle | `B4` | | `CJNE A, direct, offset` | | 3 Byte | 2 Cycle | `B5` | | `CJNE @Rn, #immediate, offset` | | 3 Byte | 2 Cycle | `B6`, `B7` | | `CJNE Rn, #immediate, offset` | | 3 Byte | 2 Cycle | `B8` \~ `BF` | ### DJNZ Decrement and Jump if Not Zero。 將目標暫存器的值減 1,若不為 0 則跳轉。 在 EdSim51 中,`offset` 欄位可以填入目標位置的 LABEL。 | 指令 | 描述 | 長度 | 週期 | Opcode | | --------------------- | ---- | ------ | ------- | ------------ | | `DJNZ direct, offset` | | 3 Byte | 2 Cycle | `D5` | | `DJNZ Rn, offset` | | 2 Byte | 2 Cycle | `D8` \~ `DF` | ## 函數指令 ### ACALL, LCALL The **ACALL** instruction calls a subroutine located at the specified address. The PC is incremented twice to obtain the address of the following instruction. The 16-bit PC is then stored on the stack (low-order byte first) and the stack pointer is incremented twice. No flags are affected. The address of the subroutine is calculated by combining the 5 high-order bits of the incremented PC (for A15-A11), the 3 high-order bits of the **ACALL** instruction opcode (for A10-A8), and the second byte of the instruction (for A7-A0). The subroutine that is called must be located in the same 2KByte block of program memory as the opcode following the **ACALL** instruction. The **LCALL** instruction calls a subroutine located at the specified address. This instruction first adds 3 to the PC to generate the address of the next instruction. This result is pushed onto the stack low-byte first and the stack pointer is incremented by 2. The high-order and low-order bytes of the PC are loaded from the second and third bytes of the instruction respectively. Program execution is transferred to the subroutine at this address. No flags are affected by this instruction. | 指令 | 描述 | 長度 | 週期 | Opcode | | -------------- | ---- | ------ | ------- | ---------------- | | `ACALL addr11` | | 2 Byte | 2 Cycle | `X1`, `X` 為奇數 | | `LCALL addr16` | | 3 Byte | 2 Cycle | `12` | ### RET, RETI The **RET** instruction pops the high-order and low-order bytes of the PC from the stack (and decrements the stack pointer by 2). Program execution resumes from the resulting address which is typically the instruction following an **ACALL** or **LCALL** instruction. No flags are affected by this instruction. The **RETI** instruction is used to end an interrupt service routine. This instruction pops the high-order and low-order bytes of the PC (and decrements the stack pointer by 2) and restores the interrput logic to accept additional interrupts. No other registers are affected by this instruction. The **RETI** instruction does not restore the PSW to its value before the interrupt. The interrupt service routine must save and restore the PSW. Execution returns to the instruction immediately after the point at which the interrupt was detected. If another interrupt was pending when the **RETI** instruction is executed, one instruction at the return address is executed before the pending interrupt is processed. | 指令 | 描述 | 長度 | 週期 | Opcode | | ------ | ---- | ------ | ------- | ------ | | `RET` | | 1 Byte | 2 Cycle | `22` | | `RETI` | | 1 Byte | 2 Cycle | `32` | ### PUSH, POP The **PUSH** instruction increments the stack pointer and stores the value of the specified byte operand at the internal RAM address indirectly referenced by the stack pointer. No flags are affected by this instruction. The **POP** instruction reads a byte from the address indirectly referenced by the SP register. The value read is stored at the specified address and the stack pointer is decremented. No flags are affected by this instruction. | 指令 | 描述 | 長度 | 週期 | Opcode | | ------------- | ---- | ------ | ------- | ------ | | `PUSH direct` | | 2 Byte | 2 Cycle | `C0` | | `POP direct` | | 2 Byte | 2 Cycle | `D0` | ## 假指令 ### EQU `EQU` 指令用來定義一個符號常數。 ```assembly MY_CONST EQU 0x55 ``` 表示 `MY_CONST` 會被定義為 `0x55`。 ### ORG `ORG` 指令用來定義下一個指令在 code memory 中的位址。 ```assembly ORG 0x30 ; TODO... ``` 表示下一個指令會被放在 code memory 的 `0x30` 位址。 ### DB `DB` 指令用來定義一個位元組的資料在 code memory 中。 ```assembly DB 0x55, 0x66, 0x77 ``` 表示在 code memory 中連續的三個位元組分別為 `0x55`, `0x66`, `0x77`。