В этом посте:

  • Список опкодов в порядке их бинарного кода
  • Таблица опкодов
  • Объяснение по категориям инструкций

Аттрибуция

При составлении этой страницы использованы материалы:

Категории инструкций

Все инструкции можно разделить на 6 категорий:

  • 🔧 Управление процессором
  • ⤵ Переходы и вызовы
  • 📩 Загрузка, сохранение и перемещение, 8-бит
  • 📥 Загрузка, сохранение и перемещение, 16-бит
  • 🧮 Арифметические и логические операции, 8-бит
  • 📐 Арифметические и логические операции, 16-бит

Некоторые инструкции не следует использовать. Они помечены: 🤬.

Список опкодов

Опкод (hex) Категория Мнемоника Краткое описание Затронутые флаги Ссылка на описание
00 🔧 NOP Ничего не делает NOP
01 {2} {3} 📥 LXI B, d16 B ← {2}, C ← {3} LXI
02 📩 STAX B (BC) ← A LDAX/STAX
03 📐 INX B BC ← BC + 1 CY INX/DCX
04 🧮 INR B B ← B + 1 Z, S, P, AC INR/DCR
05 🧮 DCR B B ← B - 1 Z, S, P, AC INR/DCR
06 {2} 📩 MVI B, d8 B ← {2} MVI
07 🧮 RLC A ← A « 1 (CY = пред. бит 7; бит 0 = пред. бит 7) CY RLC/RRC/RAL/RAR
08 🤬 {NOP} !!! Неопределенные инструкции
09 📐 DAD B HL ← HL + BC DAD
0A 📩 LDAX B A ← (BC) LDAX/STAX
0B 📐 DCX B BC ← BC - 1 CY INX/DCX
0C 🧮 INR C C ← C + 1 Z, S, P, AC INR/DCR
0D 🧮 DCR C C ← C - 1 Z, S, P, AC INR/DCR
0E {2} 📩 MVI C, d8 C ← {2} MVI
0F 🧮 RRC A ← A » 1 (CY = пред. бит 0; бит 7 = пред. бит 0) CY RLC/RRC/RAL/RAR
10 🤬 {NOP} !!! Неопределенные инструкции
11 {2} {3} 📥 LXI D, d16 D ← {2}, E ← {3} LXI
12 📩 STAX D (DE) ← A LDAX/STAX
13 📐 INX D DE ← DE + 1 CY INX/DCX
14 🧮 INR D D ← D + 1 Z, S, P, AC INR/DCR
15 🧮 DCR D D ← D - 1 Z, S, P, AC INR/DCR
16 {2} 📩 MVI D, d8 D ← {2} MVI
17 🧮 RAL A ← A ≪ 1 (бит 0 = пред. CY, CY = пред. бит 7) CY RLC/RRC/RAL/RAR
18 🤬 {NOP} !!! Неопределенные инструкции
19 📐 DAD D HL ← HL + DE DAD
1A 📩 LDAX D A ← (DE) LDAX/STAX
1B 📐 DCX D DE ← DE - 1 CY INX/DCX
1C 🧮 INR E E ← E + 1 Z, S, P, AC INR/DCR
1D 🧮 DCR E E ← E - 1 Z, S, P, AC INR/DCR
1E {2} 📩 MVI E, d8 E ← {2} MVI
1F 🧮 RAR A ← A ≫ 1 (бит 7 = пред. CY, CY = пред. бит 0) CY RLC/RRC/RAL/RAR
20 🤬 {NOP} !!! Неопределенные инструкции
21 {2} {3} 📥 LXI H, d16 H ← {2}, L ← {3} LXI
22 {2} {3} 📥 SHLD a16 (a16) ← L, (a16+1) ← H SHLD/LHLD
23 📐 INX H HL ← HL + 1 CY INX/DCX
24 🧮 INR H H ← H + 1 Z, S, P, AC INR/DCR
25 🧮 DCR H H ← H - 1 Z, S, P, AC INR/DCR
26 {2} 📩 MVI H, d8 H ← {2} MVI
27 🧮 DAA Преобразовать A в формат BCD CY, AC DAA
28 🤬 {NOP} !!! Неопределенные инструкции
29 📐 DAD H HL ← HL + HL DAD
2A {2} {3} 📥 LHLD a16 L ← (a16), H ← (a16+1) LHLD/SHLD
2B 📐 DCX H HL ← HL - 1 CY INX/DCX
2C 🧮 INR L L ← L + 1 Z, S, P, AC INR/DCR
2D 🧮 DCR L L ← L - 1 Z, S, P, AC INR/DCR
2E {2} 📩 MVI L, d8 L ← {2} MVI
2F 🧮 CMA A ← ~A CMA
30 🤬 {NOP} !!! Неопределенные инструкции
31 {2} {3} 📥 LXI SP, d16 SP ← {2}, SP+1 ← {3} LXI
32 {2} {3} 📩 STA a16 (a16) ← A STA
33 📐 INX SP SP ← SP + 1 CY INX/DCX
34 🧮 INR M (HL) ← (HL) + 1 Z, S, P, AC INR/DCR
35 🧮 DCR M (HL) ← (HL) - 1 Z, S, P, AC INR/DCR
36 {2} 📩 MVI M, d8 (HL) ← {2} MVI
37 🧮 STC CY ← 1 STC/CMC
38 🤬 {NOP} !!! Неопределенные инструкции
39 📐 DAD SP HL ← HL + SP DAD
3A {2} {3} 📩 LDA a16 A ← (a16) LDA
3B 📐 DCX SP SP ← SP - 1 CY INX/DCX
3C 🧮 INR A A ← A + 1 Z, S, P, AC INR/DCR
3D 🧮 DCR A A ← A - 1 Z, S, P, AC INR/DCR
3E {2} 📩 MVI A, d8 A ← {2} MVI
3F 🧮 CMC CY ← !CY STC/CMC
40 📩 MOV B, B {NOP} B ← B MOV
41 📩 MOV B, C B ← C MOV
42 📩 MOV B, D B ← D MOV
43 📩 MOV B, E B ← E MOV
44 📩 MOV B, H B ← H MOV
45 📩 MOV B, L B ← L MOV
46 📩 MOV B, M B ← (HL) MOV
47 📩 MOV B, A B ← A MOV
48 📩 MOV C, B C ← B MOV
49 📩 MOV C, C {NOP} C ← C MOV
4A 📩 MOV C, D C ← D MOV
4B 📩 MOV C, E C ← E MOV
4C 📩 MOV C, H C ← H MOV
4D 📩 MOV C, L C ← L MOV
4E 📩 MOV C, M C ← (HL) MOV
4F 📩 MOV C, A C ← A MOV
50 📩 MOV D, B D ← B MOV
51 📩 MOV D, C D ← C MOV
52 📩 MOV D, D {NOP} D ← D MOV
53 📩 MOV D, E D ← E MOV
54 📩 MOV D, H D ← H MOV
55 📩 MOV D, L D ← L MOV
56 📩 MOV D, M D ← (HL) MOV
57 📩 MOV D, A D ← A MOV
58 📩 MOV E, B E ← B MOV
59 📩 MOV E, C E ← C MOV
5A 📩 MOV E, D E ← D MOV
5B 📩 MOV E, E {NOP} E ← E MOV
5C 📩 MOV E, H E ← H MOV
5D 📩 MOV E, L E ← L MOV
5E 📩 MOV E, M E ← (HL) MOV
5F 📩 MOV E, A E ← A MOV
60 📩 MOV H, B H ← B MOV
61 📩 MOV H, C H ← C MOV
62 📩 MOV H, D H ← D MOV
63 📩 MOV H, E H ← E MOV
64 📩 MOV H, H {NOP} H ← H MOV
65 📩 MOV H, L H ← L MOV
66 📩 MOV H, M H ← (HL) MOV
67 📩 MOV H, A H ← A MOV
68 📩 MOV L, B L ← B MOV
69 📩 MOV L, C L ← C MOV
6A 📩 MOV L, D L ← D MOV
6B 📩 MOV L, E L ← E MOV
6C 📩 MOV L, H L ← H MOV
6D 📩 MOV L, L {NOP} L ← L MOV
6E 📩 MOV L, M L ← (HL) MOV
6F 📩 MOV L, A L ← A MOV
70 📩 MOV M, B (HL) ← B MOV
71 📩 MOV M, C (HL) ← C MOV
72 📩 MOV M, D (HL) ← D MOV
73 📩 MOV M, E (HL) ← E MOV
74 📩 MOV M, H (HL) ← H MOV
75 📩 MOV M, L (HL) ← L MOV
76 🔧 HLT Остановить процессор HLT
77 📩 MOV M, A (HL) ← A MOV
78 📩 MOV A, B A ← B MOV
79 📩 MOV A, C A ← C MOV
7A 📩 MOV A, D A ← D MOV
7B 📩 MOV A, E A ← E MOV
7C 📩 MOV A, H A ← H MOV
7D 📩 MOV A, L A ← L MOV
7E 📩 MOV A, M A ← (HL) MOV
7F 📩 MOV A, A {NOP} A ← A MOV
80 🧮 ADD B A ← A + B Z, S, P, CY, AC ADD
81 🧮 ADD C A ← A + C Z, S, P, CY, AC ADD
82 🧮 ADD D A ← A + D Z, S, P, CY, AC ADD
83 🧮 ADD E A ← A + E Z, S, P, CY, AC ADD
84 🧮 ADD H A ← A + H Z, S, P, CY, AC ADD
85 🧮 ADD L A ← A + L Z, S, P, CY, AC ADD
86 🧮 ADD M A ← A + (HL) Z, S, P, CY, AC ADD
87 🧮 ADD A A ← A + A Z, S, P, CY, AC ADD
88 🧮 ADC B A ← A + B + CY Z, S, P, CY, AC ADC
89 🧮 ADC C A ← A + C + CY Z, S, P, CY, AC ADC
8A 🧮 ADC D A ← A + D + CY Z, S, P, CY, AC ADC
8B 🧮 ADC E A ← A + E + CY Z, S, P, CY, AC ADC
8C 🧮 ADC H A ← A + H + CY Z, S, P, CY, AC ADC
8D 🧮 ADC L A ← A + L + CY Z, S, P, CY, AC ADC
8E 🧮 ADC M A ← A + (HL) + CY Z, S, P, CY, AC ADC
8F 🧮 ADC A A ← A + A + CY Z, S, P, CY, AC ADC
90 🧮 SUB B A ← A - B Z, S, P, CY, AC SUB
91 🧮 SUB C A ← A - C Z, S, P, CY, AC SUB
92 🧮 SUB D A ← A - D Z, S, P, CY, AC SUB
93 🧮 SUB E A ← A - E Z, S, P, CY, AC SUB
94 🧮 SUB H A ← A - H Z, S, P, CY, AC SUB
95 🧮 SUB L A ← A - L Z, S, P, CY, AC SUB
96 🧮 SUB M A ← A - (HL) Z, S, P, CY, AC SUB
97 🧮 SUB A A ← A - A {A ← 0} Z, S, P, CY, AC SUB
98 🧮 SBB B A ← A - B - CY Z, S, P, CY, AC SBB
99 🧮 SBB C A ← A - C - CY Z, S, P, CY, AC SBB
9A 🧮 SBB D A ← A - D - CY Z, S, P, CY, AC SBB
9B 🧮 SBB E A ← A - E - CY Z, S, P, CY, AC SBB
9C 🧮 SBB H A ← A - H - CY Z, S, P, CY, AC SBB
9D 🧮 SBB L A ← A - L - CY Z, S, P, CY, AC SBB
9E 🧮 SBB M A ← A - (HL) - CY Z, S, P, CY, AC SBB
9F 🧮 SBB A A ← A - A - CY Z, S, P, CY, AC SBB
A0 🧮 ANA B A ← A & B Z, S, P, CY ANA
A1 🧮 ANA C A ← A & C Z, S, P, CY ANA
A2 🧮 ANA D A ← A & D Z, S, P, CY ANA
A3 🧮 ANA E A ← A & E Z, S, P, CY ANA
A4 🧮 ANA H A ← A & H Z, S, P, CY ANA
A5 🧮 ANA L A ← A & L Z, S, P, CY ANA
A6 🧮 ANA M A ← A & (HL) Z, S, P, CY ANA
A7 🧮 ANA A A ← A & A {NOP} Z, S, P, CY ANA
A8 🧮 XRA B A ← A ^ B Z, S, P, CY, AC XRA
A9 🧮 XRA C A ← A ^ C Z, S, P, CY, AC XRA
AA 🧮 XRA D A ← A ^ D Z, S, P, CY, AC XRA
AB 🧮 XRA E A ← A ^ E Z, S, P, CY, AC XRA
AC 🧮 XRA H A ← A ^ H Z, S, P, CY, AC XRA
AD 🧮 XRA L A ← A ^ L Z, S, P, CY, AC XRA
AE 🧮 XRA M A ← A ^ (HL) Z, S, P, CY, AC XRA
AF 🧮 XRA A A ← A ^ A {A ← 0} Z, S, P, CY, AC XRA
B0 🧮 ORA B A ← A | B Z, S, P, CY ORA
B1 🧮 ORA C A ← A | C Z, S, P, CY ORA
B2 🧮 ORA D A ← A | D Z, S, P, CY ORA
B3 🧮 ORA E A ← A | E Z, S, P, CY ORA
B4 🧮 ORA H A ← A | H Z, S, P, CY ORA
B5 🧮 ORA L A ← A | L Z, S, P, CY ORA
B6 🧮 ORA M A ← A | (HL) Z, S, P, CY ORA
B7 🧮 ORA A A ← A | A {NOP} Z, S, P, CY ORA
B8 🧮 CMP B ∅ ← A - B Z, S, P, CY, AC CMP
B9 🧮 CMP C ∅ ← A - C Z, S, P, CY, AC CMP
BA 🧮 CMP D ∅ ← A - D Z, S, P, CY, AC CMP
BB 🧮 CMP E ∅ ← A - E Z, S, P, CY, AC CMP
BC 🧮 CMP H ∅ ← A - H Z, S, P, CY, AC CMP
BD 🧮 CMP L ∅ ← A - L Z, S, P, CY, AC CMP
BE 🧮 CMP M ∅ ← A - (HL) Z, S, P, CY, AC CMP
BF 🧮 CMP A ∅ ← A - A Z, S, P, CY, AC CMP
C0 RNZ если не Z, PC ← POP() RET
C1 📥 POP B BC ← POP() PUSH/POP
C2 {2} {3} JNZ a16 если не Z, PC ← a16 JMP
C3 {2} {3} JMP a16 PC ← a16 JMP
C4 {2} {3} CNZ a16 если не Z, PUSH(PC) и PC ← a16 CALL
C5 📥 PUSH B PUSH(BC) PUSH/POP
C6 {2} 🧮 ADI d8 A ← A + {2} Z, S, P, CY, AC ADD
C7 RST 0 PUSH(PC) и PC ← 0x0000 RST
C8 RZ если Z, PC ← POP() RET
C9 RET PC ← POP() RET
CA {2} {3} JZ a16 если Z, PC ← a16 JMP
CB {2} {3} 🤬 {JMP a16} !!! Неопределенные инструкции
CC {2} {3} CZ a16 если Z, PUSH(PC) и PC ← a16 CALL
CD {2} {3} CALL a16 PUSH(PC) и PC ← a16 CALL
CE {2} 🧮 ACI d8 A ← A + {2} + CY Z, S, P, CY, AC Immediate-арифметика
CF RST 1 PUSH(PC) и PC ← 0x0008 RST
D0 RNC если не CY, PC ← POP() RET
D1 📥 POP D DE ← POP() PUSH/POP
D2 {2} {3} JNC a16 если не CY, PC ← a16 JMP
D3 {2} 🔧 OUT d8 если не CY, выводим A в порт {2} IN/OUT
D4 {2} {3} CNC a16 если не CY, PUSH(PC) и PC ← a16 CALL
D5 📥 PUSH D PUSH(DE) PUSH/POP
D6 {2} 🧮 SUI d8 A ← A - {2} Z, S, P, CY, AC Immmediate-арифметика
D7 RST 2 PUSH(PC) и PC ← 0x0010 RST
D8 RC если не CY, PC ← POP() RET
D9 🤬 {RET} !!! Неопределенные инструкции
DA {2} {3} JC a16 если CY, PC ← a16 JMP
DB {2} {3} 🔧 IN d8 считываем значение в A из порта {2} IN/OUT
DC {2} {3} CC a16 если CY, PUSH(PC) и PC ← a16 CALL
DD {2} {3} 🤬 {CALL d16} !!! Неопределенные инструкции
DE {2} 🧮 SBI d8 A ← A - {2} - CY Z, S, P, CY, AC Immediate-арифметика
DF RST 3 PUSH(PC) и PC ← 0x0018 RST
E0 {2} {3} RPO если не P, PC ← POP() RET
E1 📥 POP H HL ← POP() PUSH/POP
E2 {2} {3} JPO a16 если не P, PC ← a16 JMP
E3 {2} {3} 📥 XTHL tmp1 ← (SP); tmp2 ← (SP+1); (SP) ← L; (SP+1) ← H; H ← tmp1; L ← tmp2 XTHL
E4 {2} {3} CPO a16 если не P, PUSH(PC) и PC ← a16 CALL
E5 📥 PUSH H PUSH(HL) PUSH/POP
E6 {2} 🧮 ANI d8 A ← A & {2} Z, S, P, CY, AC Immediate-арифметика
E7 RST 4 PUSH(PC) и PC ← 0x0020 RST
E8 RPE если P, PC ← POP() RET
E9 PCHL PC ← HL {JMP HL} JMP
EA {2} {3} JPE a16 если P, PC ← a16 JMP
EB {2} {3} 📥 XCHG tmp ← DE; DE ← HL; HL ← tmp XCHG
EC {2} {3} CPE a16 если P, PUSH(PC) и PC ← a16 CALL
ED {2} {3} 🤬 {CALL d16} !!! Неопределенные инструкции
EE {2} 🧮 XRI d8 A ← A ^ {2} Z, S, P, CY, AC Immediate-арифметика
EF RST 5 PUSH(PC) и PC ← 0x0028 RST
F0 {2} {3} RP если не S, PC ← POP() RET
F1 📥 POP PSW AF ← POP() Z, S, P, CY, AC PUSH/POP
F2 {2} {3} JP a16 если не S, PC ← a16 JMP
F3 {2} {3} 🔧 DI выключаем систему прирываний (INTE ← 0) DI/EI
F4 {2} {3} CP a16 если не S, PUSH(PC) и PC ← a16 CALL
F5 📥 PUSH PSW PUSH(AF) PUSH/POP
F6 {2} 🧮 ORI d8 A ← A | {2} Z, S, P, CY, AC Immediate-арифметика
F7 RST 6 PUSH(PC) и PC ← 0x0030 RST
F8 {2} {3} RM если не S, PC ← POP() RET
F9 📥 SPHL SP ← HL SPHL
FA {2} {3} JM a16 если S, PC ← a16 JMP
FB {2} {3} 🔧 EI включаем систему прирываний (INTE ← 1) DI/EI
FC {2} {3} CM a16 если S, PUSH(PC) и PC ← a16 CALL
FD {2} {3} 🤬 {CALL d16} !!! Неопределенные инструкции
FE {2} 🧮 CPI d8 ∅ ← A - {2} Z, S, P, CY, AC Immediate-арифметика
FF RST 7 PUSH(PC) и PC ← 0x0038 RST
Опкод (hex) Категория Мнемоника Краткое описание Затронутые флаги Ссылка на описание

Таблица опкодов

x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
0x 🔧 NOP 📥 LXI B,d16 📩 STAX B 📐 INX B 🧮 INR B 🧮 DCR B 📩 MVI B,d8 🧮 RLC 🤬 📐 DAD B 📥 LDAX B 📐 DCX B 🧮 INR C 🧮 DCR C 📩 MVI C,d8 🧮 RRC
1x 🤬 📥 LXI D,d16 📩 STAX D 📐 INX D 🧮 INR D 🧮 DCR D 📩 MVI D,d8 🧮 RAL 🤬 📐 DAD D 📥 LDAX D 📐 DCX D 🧮 INR E 🧮 DCR E 📩 MVI E,d8 🧮 RAR
2x 🤬 📥 LXI H,d16 📥 SHLD a16 📐 INX H 🧮 INR H 🧮 DCR H 📩 MVI H,d8 🧮 DAA 🤬 📐 DAD H 📥 LHLD a16 📐 DCX H 🧮 INR L 🧮 DCR L 📩 MVI L,d8 🧮 CMA
3x 🤬 📥 LXI SP,d16 📩 STA a16 📐 INX M 🧮 INR M 🧮 DCR M 📩 MVI M,d8 🧮 STC 🤬 📐 DAD SP 📥 LDA a16 📐 DCX SP 🧮 INR A 🧮 DCR A 📩 MVI A,d8 🧮 CMC
4x 📩 MOV B,B 📩 MOV B,C 📩 MOV B,D 📩 MOV B,E 📩 MOV B,H 📩 MOV B,L 📩 MOV B,M 📩 MOV B,A 📩 MOV B,B 📩 MOV C,C 📩 MOV C,D 📩 MOV C,E 📩 MOV C,H 📩 MOV C,L 📩 MOV C,M 📩 MOV C,A
5x 📩 MOV D,B 📩 MOV D,C 📩 MOV D,D 📩 MOV D,E 📩 MOV D,H 📩 MOV D,L 📩 MOV D,M 📩 MOV D,A 📩 MOV D,B 📩 MOV E,C 📩 MOV E,D 📩 MOV E,E 📩 MOV E,H 📩 MOV E,L 📩 MOV E,M 📩 MOV E,A
6x 📩 MOV H,B 📩 MOV H,C 📩 MOV H,D 📩 MOV H,E 📩 MOV H,H 📩 MOV H,L 📩 MOV H,M 📩 MOV H,A 📩 MOV H,B 📩 MOV L,C 📩 MOV L,D 📩 MOV L,E 📩 MOV L,H 📩 MOV L,L 📩 MOV L,M 📩 MOV L,A
7x 📩 MOV M,B 📩 MOV M,C 📩 MOV M,D 📩 MOV M,E 📩 MOV M,H 📩 MOV M,L 🔧 HLT 📩 MOV M,A 📩 MOV M,B 📩 MOV A,C 📩 MOV A,D 📩 MOV A,E 📩 MOV A,H 📩 MOV A,L 📩 MOV A,M 📩 MOV A,A
8x 🧮 ADD B 🧮 ADD C 🧮 ADD D 🧮 ADD E 🧮 ADD H 🧮 ADD L 🧮 ADD M 🧮 ADD A 🧮 ADC B 🧮 ADC C 🧮 ADC D 🧮 ADC E 🧮 ADC H 🧮 ADC L 🧮 ADC M 🧮 ADC A
9x 🧮 SUB B 🧮 SUB C 🧮 SUB D 🧮 SUB E 🧮 SUB H 🧮 SUB L 🧮 SUB M 🧮 SUB A 🧮 SBB B 🧮 SBB C 🧮 SBB D 🧮 SBB E 🧮 SBB H 🧮 SBB L 🧮 SBB M 🧮 SBB A
Ax 🧮 ANA B 🧮 ANA C 🧮 ANA D 🧮 ANA E 🧮 ANA H 🧮 ANA L 🧮 ANA M 🧮 ANA A 🧮 XRA B 🧮 XRA C 🧮 XRA D 🧮 XRA E 🧮 XRA H 🧮 XRA L 🧮 XRA M 🧮 XRA A
Bx 🧮 ORA B 🧮 ORA C 🧮 ORA D 🧮 ORA E 🧮 ORA H 🧮 ORA L 🧮 ORA M 🧮 ORA A 🧮 CMP B 🧮 CMP C 🧮 CMP D 🧮 CMP E 🧮 CMP H 🧮 CMP L 🧮 CMP M 🧮 CMP A
Cx RNZ 📥 POP B JNZ a16 JMP a16 CNZ a16 📥 PUSH B 🧮 ADI d8 RST 0 RZ RET JZ a16 🤬 CZ a16 CALL a16 🧮 ACI d8 RST 1
Dx RNC 📥 POP D JNC a16 🔧 OUT d8 CNC a16 📥 PUSH D 🧮 SUI d8 RST 2 RC 🤬 JC a16 🔧 IN d8 CC a16 🤬 🧮 SBI d8 RST 3
Ex RPO 📥 POP H JPO a16 📥 XTHL CPO a16 📥 PUSH H 🧮 ANI d8 RST 4 RPE PCHL JPE a16 📥 XCHG CPE a16 🤬 🧮 XRI d8 RST 5
Fx RP 📥 POP PSW JP a16 🔧 DI CP a16 📥 PUSH PSW 🧮 ORI d8 RST 6 RM 📥 SPHL JM a16 🔧 EI CM a16 🤬 🧮 CPI d8 RST 7

Инструкции

У процессора есть несколко категорий команд:

🔧 Управление процессором

⬆ Вернуться к категориям инструкций

Эти инструкции отвечают за операции, которые нельзя отнести к другим категориям. Они редко используются при написании прикладных программ, и больше относятся к разработке системы.

NOP

⬆ Вернуться к инструкциям управления процессором

Выполнение инструкции NOP не изменяет ничего.

Опкод инструкции NOP равен 0x00, что значит, что по умолчанию память эмулятора СМ-1800 полностью заполнена NOP-инструкциями. Это значит, что если выполнять код в незаданной памяти, то процессор будет выполнять инструкции NOP, пока не дойдет до какого-то другого кода.

Пример использования
; Не делать ничего
NOP

HLT

⬆ Вернуться к инструкциям управления процессором

Инструкция HLT останавливает выполнение процессора.

После выполнения этой инструкции процессор входит в состояние, в котором выполнение кода прекращается до получения внешнего прерывания. Если внешнее прерывание получено, то процессор считывает с линий данных один байт инструкции (обычно это одна из RST-инструкций) и выполняет её.

Пример использования
; Остановить выполнение навсегда
DI
HLT

IN/OUT

⬆ Вернуться к инструкциям управления процессором

Инструкции IN d8 и OUT d8 используются для того, чтобы передавать данные во внешний порт или принимать данные из внешнего порта. Какой именно смысл имеют эти порты, зависит от конкретного оборудования вокруг процессора. Для систем, в которых есть системные вызовы (вроде СМ-1800), чаще всего следует не использовать эти инструкции самому, а вместо этого вызывать системные подпрограммы для выполнения таких задач.

На аппаратном уровне можно считать, что у Intel 8080 на самом деле 17-битная адресная шина, где верхняя линия – это IORQ, которая равна 0 при обычной работе с памятью и 1 при выполнении IN и OUT.

При выполнении OUT, нижние 8 бит адресной шины устанавливаются в значение, переданное как аргумент инструкции. После этого процессор выдает значение A на линию данных, и внешнее оборудование может прочитать его оттуда.

При выполнении IN, нижние 8 бит устанавливаются в значение аргумента инструкции. После этого внешнее оборудование должно передать значение в процессор на линию данных, которое процессор принимает и записывает в регистр A.

Пример использования
; Передать значение 0x55 в порт 0xC3
MVI A, 0x55
OUT 0xC3

; Читать значение из порта 0xA2
IN 0xA2

DI/EI

⬆ Вернуться к инструкциям управления процессором

Пара инструкций DI и EI используется для управления прерываниями. DI (disable interrupts) выключает прерывания, а EI (enable interrupts) включает их.

Прерывания – это способ позволить оборудованию вокруг процессора останавливать выполнение основной программы и вместо этого делать некое действие в ответ на внешнее событие.

Прерывания работают следующим образом: если внешнее оборудование хочет вызвать прерывание, то подается сигнал на линию прерываний. Когда процессор завершает текущую инструкцию, он подтверждает, что получил прерывание. После этого внешнее оборудование должно подать на линию данных инструкцию из одного байта – чаще всего это одна из RST-инструкций.

После того, как прерывание произошло и было обработано, прерывания выключаются, поэтому если нужно обрабатывать последующие прерывания, то нужно включить их еще раз.

Пример использования
; Подождем до получения следующего прерывания
EI
HLT ; zzz...

; Если мы здесь, то мы уже получили и обработали прерывание
; Снова включим получение прерываний 
EI

⤵ Переходы и вызовы

Эти инструкции служат для изменения потока выполнения программы. Используя эти инструкции, можно реализовать циклы, процедуры и функции, условные блоки (if/else) и другие похожие конструкции высокоуровневых языков программирования.

Условные инструкции

⬆ Вернуться к переходам и вызовам

У инструкций перехода есть три вида: JMP, CALL и RET. Каждый из этих видов имеет 9 различных вариантов: один выполняет свое действие всегда, а остальные – в зависимости от текущего состояния флагов процессора. Все инструкции одного и того же вида имеют одинаковое поведение, если они выполняют переход, поэтому их действие описано один раз для каждого вида.

JMP CALL RET переходит безусловно
JZ CZ RZ переходит, если установлен флаг Z (результат равен 0)
JNZ CNZ RNZ переходит, если не установлен флаг Z (результат не равен 0)
JC CC RC переходит, если установлен флаг CY (есть переполнение)
JNC CNC RNC переходит, если не установлен флаг CY (нет переполнения)
JPE CPE RPE переходит, если установлен флаг P (количество единиц в результате – четное)
JPO CPO RPO переходит, если не установлен флаг P (количество единиц в результате – нечетное)
JM CM RM переходит, если установлен флаг S (результат меньше 0)
JP CP RP переходит, если не установлен флаг S (результат больше или равен 0)

JMP

⬆ Вернуться к переходам и вызовам

Эта инструкция имеет условные варианты.

Инструкция JMP a16 принимает аргумент в виде адреса. Если выполняется переход, то следующая инструкция, которую процессор начнет выполнять, будет находиться по этому адресу.

Эта инструкция может быть использована для того, чтобы:

  • делать циклы
  • реализовывать условные операторы
  • пропускать куски памяти, которые не являются кодом
Пример использования
; Перейти к адресу 0x1234
JMP 0x1234
; (или, если написать байты раздельно)
JMP 0x34 0x12

Выполним какое-то действие 10 раз:

; Сначала инициализируем счетчик: пусть в регистре A будет значение 10
MVI A, 10
; Метка начала цикла
loop:
    ; Выполним какое-то действие
    NOP ; ...
    NOP ; ...
    ; Сделали действие!
    ; Теперь вычтем единицу из счетчика
    DCR A
    ; Если счетчик еще не равен нулю, перейдем к началу цикла
    JNZ loop
; Если мы здесь, то мы не сделали переход в начало, значит цикл завершился
NOP ; ...

Получим значение из порта, и если оно имеет четное количество единиц, то выведем другое значение:

; Сначала считываем значение
IN 0xD0
; Проверяем его (устанавливаем флаги)
CPI 0x00
; Если оно не четное, то нужно пропустить следующий кусок кода
JPO not_even_parity
    ; Если мы здесь, то значение имеет четное количество единиц, и мы делаем вывод
    MVI A, 0x20
    OUT 0xD0
not_even_parity:
; Если мы здесь, значит мы либо выполнили переход и пропустили вывод, либо только что сделали вывод
NOP ; ...

Добавим строку внутри кода:

NOP ; ...
NOP ; ...
; Сейчас я хочу вывести какую-то строку на экран,
; но я хочу записать эту строку прямо здесь в памяти.
; Чтобы процессор не начал выполнять эту строку как код, мы должны пропустить ее.
JMP skip_string  ; перейдем на метку, которая после конца строки
string:  ; эта метка будет обозначать начало строки
  DB "HELLO WORLD!\0"  ; запишем строку в память
skip_string:  ; эта метка указывает на инструкции после конца строки
LXI BC, string  ; передадим адрес строки в регистр BC
CALL 0x0043     ; вызовем функцию вывода строки

CALL

⬆ Вернуться к переходам и вызовам

Эта инструкция имеет условные варианты.

Инструкция CALL a16, как и инструкция JMP a16, принимает в качестве аргумента адрес, к которому нужно перейти. Однако при вызове CALL, помимо перехода, дополнительно значение регистра PC записывается в стек. Это нужно, чтобы будущая инструкция RET могла вернуться в этот код.

Эта инструкция используется для того, чтобы вызывать подпрограммы – куски кода, которые нужно использовать несколько раз. Когда выполняется CALL, процессор запоминает на стек, как вернуться к основной программе, и затем начинает выполнять подпрограмму. Когда подпрограмма завершается, она использует инструкцию RET, и процессор достает этот запомненный адрес из стека и переходит к нему, возвращаясь в основную программу.

Инструкция CALL только сохраняет адрес возврата в стеке – она не изменяет регистры или флаги. Это означает, что текущие значения регистров доступны подпрограмме, и являются основным способом передачи данных в подпрограмму.

Пример использования
; Подпрограмма, которая умножает значение в регистре A на 10
times_10:
    ; мы будем использовать регистр B как временное хранилище
    ; запишем его текущее значение на стек
    PUSH BC
    ; A = x
    ADD A    ; A = x + x = 2*x
    MOV B,A  ; B = A = 2*x
    ADD A    ; A = 2*x + 2*x = 4*x
    ADD A    ; A = 4*x + 4*x = 8*x
    ADD B    ; A = 8*x + 2*x = 10*x
    ; вернем значение из стека в регистр B
    POP BC
    ; возвращаемся в основную программу
    RET

; Установим значение регистра A
MVI A, 3
; Вызовем подпрограмму
CALL times_10
; Теперь значение регистра A будет равно 30

RET

⬆ Вернуться к переходам и вызовам

Эта инструкция имеет условные варианты.

Инструкция RET считывает значение из стека и переходит к адресу, записанному в этом значении.

Используя эту инструкцию, можно создавать подпрограммы – куски кода, которые можно использовать несколько раз из разных мест основной программы.

Если подпрограмма использует инструкции управления стеком, то следует быть аккуратным при написании кода, чтобы к моменту вызова RET стек был на том же уровне, что и в начале подпрограммы. Это из-за того, что при вызове RET, процессор достает значение из стека и переходит к адресу, записанному в этом значении, и если программа выполнила PUSH, который не был взят обратно с POP, то при попытке вернуться в основную программу, процессор вместо этого не вернется к ожидаемому адресу.

Пример использования
; Подпрограмма, которая умножает значение в регистре A на 10
times_10:
    ; мы будем использовать регистр B как временное хранилище
    ; запишем его текущее значение на стек
    PUSH BC
    ; A = x
    ADD A    ; A = x + x = 2*x
    MOV B,A  ; B = A = 2*x
    ADD A    ; A = 2*x + 2*x = 4*x
    ADD A    ; A = 4*x + 4*x = 8*x
    ADD B    ; A = 8*x + 2*x = 10*x
    ; вернем значение из стека в регистр B
    POP BC
    ; возвращаемся в основную программу
    RET

; Установим значение регистра A
MVI A, 3
; Вызовем подпрограмму
CALL times_10
; Теперь значение регистра A будет равно 30

Пример проблемы при вызове RET с использованием стека:

; Подпрограмма, которая делает что-то полезное
subprogram:
  NOP  ; ...
  NOP  ; ...
  ; ... Во время работы подпрограммы мы положили значение на стек
  MVI H, 0x40
  MVI L, 0x00
  PUSH HL
  NOP  ; ...
  NOP  ; ...
  ; !!! Мы забыли сделать POP !!!
  RET  ; Этот RET считает значение от PUSH и перейдет к адресу 0x4000 !!


; Если попробовать использовать эту подпрограмму в основной программе...
NOP   ; ...
NOP   ; ...
CALL subprogram
NOP   ; <-- Этот код не будет выполнен -- мы не вернемся сюда

RST

⬆ Вернуться к переходам и вызовам

Инструкции RST 0, RST 1RST 7 выполняют то же самое, что CALL, к адресу 0x0000 + n*8, где n от 0 до 7. Это означает, что при вызове RST 0, процессор перейдет к адресу 0x0000, при вызове RST 1 процессор перейдет к адресу 0x0008, при вызове RST 2 процессор перейдет к адресу 0x0010 и т.д.

Эти инструкции полезны больше всего для работы с внешними прерываниями. Когда происходит прерывание, процессор считывает и выполняет одну инструкцию с шины данных, прежде чем продолжить работать как обычно. Каждая из инструкций RST занимает один байт, поэтому если в этот момент на шине данных выставить один из восьми опкодов RST, то процессор запомнит свое последнее положение в коде на стек. и выполнит код для обработки прерывания. Процедура прерывания затем может использовать RET, чтобы вернуться к основному коду и продолжить работу как обычно.

Эти опкоды также являются обычными инструкциями, поэтому можно использовать их в своем коде. Например, если вызвать инструкцию RST 0, то процессор перейдет к адресу 0x0000, как будто он был толькол что перезагружен с помощью линии RESET.

Пример использования
; Бесконечно принимаем значение из порта
; Если это значение стало равно 0, перезагружаем процессор
loop:
  IN 0x80   ; считываем значение
  CPI 0x00  ; сравниваем с 0
  JNZ loop  ; если не равно, продолжаем цикл
; теперь значение равно 0
RST 0     ; перезагружаем процессор

📩 Загрузка, сохранение и перемещение, 8-бит

⬆ Вернуться к списку инструкций

Эти инструкции используются для перемещения информации, один байт за раз.

MOV

⬆ Вернуться к инструкциям перемещения, 8-бит

Эта инструкция копирует значение из источника в назначение. Она является одной из самых часто используемых во всем наборе инструкций.

Она принимает два аргумента: назначение и источник. Эти два аргумента являются частью одного байта, и поэтому одна эта инструкция занимает почти четверть всего пространства инструкций.

Первый аргумент – это назначение, а второй аргумент – это источник. Можно читать это как операцию присваивания: MOV X,Y значит X = Y.

Аргументы – это один из следующих символов: A, B, C, D, E, H, L, M. Первые семь из этих – это названия регистров процессора, и операции между ними осуществляются полностью внутри процессора. Аргумент M – особенный: он обозначает ячейку памяти, адрес которой записан в паре регистров HL.

Операции вида MOV X,X копируют значение из какого-то регистра в сам этот регистр, тем самым не меняя его значение. Таким образом, они являются альтернативой NOP. В определенных задачах, когда важно, сколько времени занимает выполнение инструкций, операции вида MOV X,X действительно использовались как слегка более длительный NOP.

Среди набора инструкций отсутствует инструкция MOV M,M – ее место занимает инструкция HLT.

Пример использования
; Скопировать значение из регистра B в регистр A
MOV A,B

; Скопировать значение из памяти по адресу `0x1234` в регистр C
LXI H, 0x1234
MOV C, M

; Скопировать значение из памяти `0x1234` в `0x5678`, через регистр D
LXI H, 0x1234
MOV D, M  ; теперь D содержит значение из памяти 0x1234
LXI H, 0x5678
MOV M, D  ; теперь память 0x5678 содержит значение в D, которое было скопировано из памяти 0x1234

MVI

⬆ Вернуться к инструкциям перемещения, 8-бит

Инструкция MVI загружает в регистр значение, которое передается как аргумент.

Регистр задается таким же образом, как и в операции MOV – один из A, B, C, D, E, H, L, M.

Пример использования
; Загрузить значение 0x12 в регистр A
MVI A, 0x12

; Загрузить значение 0x34 в память по адресу 0xABCD
LXI H, 0xABCD
MVI M, 0x34

LDAX/STAX

⬆ Вернуться к инструкциям перемещения, 8-бит

Эта инструкция перемещает значение между регистром A и памятью по адресу в паре регистров.

Принимается аргумент – один символ, B или D – который обозначает, что нужно использовать адрес в регистрах BC или DE, соответственно. Инструкция LDAX считывает значение из памяти в регистр A, а STAX – наоборот, из регистра в память.

Пример использования
; Считать значение из памяти по адресу 0x1234 в регистр A
LXI B, 0x1234
LDAX B

; Переместить значение из памяти по адресу 0x1234 в память по адресу 0x5678 через регистр A
LXI B, 0x1234
LXI D, 0x5678
LDAX B
STAX D

LDA/STA

⬆ Вернуться к инструкциям перемещения, 8-бит

Эта инструкция делает то же самое, что и LDAX/STAX – загружает или сохраняет значение между памятью и регистром A. В отличии от LDAX/STAX, эта инструкция принимает аргумент – адрес, относительно которого следует произвести операцию.

Пример использования
; Считать значение из памяти по адресу 0x1234 в регистр A
LDA 0x1234

; Переместить значение из памяти по адресу 0x1234 в память по адресу 0x5678 через регистр A
LDA 0x1234
STA 0x5678

📥 Загрузка, сохранение и перемещение, 16-бит

⬆ Вернуться к списку инструкций

Эти инструкции используются для перемещения пар байтов.

LXI

⬆ Вернуться к инструкциям перемещения, 16-бит

Эта инструкция загружает значение аргумента в пару регистров.

Принимает два аргумента: название пары регистров (B для BC, D для DE, H для HL или SP для регистра-указателя на стек), и 16-битное значение. Загружает значение в указанную пару регистров.

Пример использования
; Загрузить значение 0x1234 в пару регистров BC
LXI B, 0x1234

; Инициализировать стек по адресу 0xff00
LXI SP, 0xff00

LHLD/SHLD

⬆ Вернуться к инструкциям перемещения, 16-бит

Эти две инструкции используются для перемещения значения между парой регистров HL и памятью.

Обе инструкции принимают аргумент – адрес a16. Инструкция LHLD считывает значение по адресу a16 в регистр L, а значение по адресу a16 + 1 в регистр H. Инструкция SHLD, наоборот, записывает значение L в a16, а H – в a16 + 1.

Пример использования

; Загрузить значение из адреса 0x4000 в HL
LHLD 0x4000

; Сохранить значение из HL в адрес 0x4000
SHLD 0x4000

; Сохранить значение 0xBEEF в адрес 0x1234 через HL
LXI H, 0xBEEF
SHLD 0x1234

PUSH/POP

⬆ Вернуться к инструкциям перемещения, 16-бит

Эти две инструкции используются для того, чтобы манипулировать стеком.

Стек – это область памяти, которую можно использовать для хранения временных значений. Например, там хранятся адреса возврата для подпрограмм, вызываемых с помощью CALL.

Обе инструкции принимают аргумент – один из B, D, H или PSW. Это – название пары регистров, которые будут записаны в стек или извлечены из него.

Поскольку адреса для возвращения из подпрограммы также находятся в стеке, следует быть осторожным при использовании стека в своих программах. К тому моменту, как вызывается RET, следует убедиться, что все операции PUSH имеют соответствующие им операции POP (можно представлять PUSH и POP как скобки вокруг блоков кода – тогда это правило значит, что последовательность скобок должна быть правильной). Это нужно потому, что, если на верху стека лежит что-то кроме адреса возврата, то при выполнении RET процессор перейдет не обратно в основную программу, а в неизвестное место в памяти.

Инструкция PUSH делает следующее:

  1. Записывает старшие 8 битов значения пары регистров в адрес SP - 1.
  2. Записывает младшие 8 битов значения пары регистров в адрес SP - 2.
  3. Уменьшает значение SP на 2.

Инструкция POP делает следующее:

  1. Извлекает младшие 8 битов значения пары регистров из адреса SP.
  2. Извлекает старшие 8 битов значения пары регистров из адреса SP + 1.
  3. Увеличивает значение SP на 2.

Аргумент B значит, что старшим байтом считается регистр B, а младшим – C. Аргумент D значит, что старшим байтом считается регистр D, а младшим – E. Аргумент H значит, что старшим байтом считается регистр H, а младшим – L.

Аргумент PSW (Program Status Word) особенный – для него старшим байтом считается значение регистра A, а младшим – особый байт, содержащий все флаги процессора:

Бит 7 6 5 4 3 2 1 0
Значение S Z всегда 0 AC всегда 0 P всегда 1 CY

При PUSH PSW биты 5, 3 и 1 младшего байта, который находится на стеке, имеют значения 0, 0 и 1 соответственно. При POP PSW биты 5, 3 и 1 игнорируются.

Пример использования
; Записать в стек значение всех регистров
PUSH B
PUSH D
PUSH H
PUSH PSW

; Выполнить что-то...
NOP
NOP
; ...сохраняем результаты работы в память...
NOP

; Теперь возвращаем процессор в исходное состояние всех регистров
; (результаты работы были записаны в память)

POP PSW  ; инструкции POP идут в обратном порядке от PUSH
POP H
POP D
POP B

SPHL

⬆ Вернуться к инструкциям перемещения, 16-бит

Эта инструкция загружает в регистр SP значение из пары регистров H и L. Значение в HL не изменяется.

Пример использования
; Инициализировать стек по адресу `0xfff0`
LXI H, 0xff
LXI L, 0xf0
SPHL  ; теперь SP = HL = 0xfff0

XCHG

⬆ Вернуться к инструкциям перемещения, 16-бит

Эта инструкция меняет местами значения в паре регистров DE и HL.

Пример использования
MVI D, 0xDE
MVI E, 0xAD
MVI H, 0xBE
MVI L, 0xEF
; Теперь DE = 0xDEAD, HL = 0xBEEF
XCHG
; Теперь DE = 0xBEEF, HL = 0xDEAD

XTHL

⬆ Вернуться к инструкциям перемещения, 16-бит

Эта инструкция меняет местами значение в паре регистров HL и значение на верхушке стека.

Более точно, сначала меняется местами значение регистра L и ячейки памяти по адресу SP, а затем меняется местами значение регистра H и ячейки памяти по адресу SP+1.

Пример использования
LXI B, 0xABCD
PUSH B
LXI H, 0x1234
; Теперь на верхушке стека лежит значение 0xABCD, а HL=0x1234
XTHL
; Теперь на верхушке стека лежит значение 0x1234, а HL=0xABCD

🧮 Арифметические и логические операции, 8-бит

⬆ Вернуться к списку инструкций

Эти инструкции выполняют преобразования над 8-битными значениями. Каждая из этих инструкций принимает значение из регистра A, преобразует его и записывает в регистр A.

Информация про флаги

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

У процессора есть 5 флагов, которые обозначают результаты выполнения арифметических и логических операций. Эти флаги называются S, Z, AC, P, CY.

Для флагов, слово “установить” значит “сделать равным 1”, а “сбросить” – “сделать равным 0”.

  • Z (Zero) – устанавливается, если результатом операции является 0.
  • S (Sign) – устанавливается, если результат операции отрицательный (используя дополнительный код, то есть если старший бит результата равен 1).
  • P (Parity) – устанавливается, если двоичное представление результата операции имеет нечетное число единиц.
  • CY (Carry) – устанавливается, если при выполнении операции произошло переполнение.
  • AC (Auxiliary Carry) – устанавливается, если при выполнении операции произошло переполнение из бита 3 (используется только для инструкции DAA).

Флаг P всегда обозначает четность результата. Флаг Z всегда устанавливается, если результат операции равен 0. Флаг S всегда обозначает значение старшего бита. Значение флагов CY и AC определяется в зависимости от инструкции.

Инструкции с immediate-значением

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Некоторые инструкции, которые принимают как аргумент название регистра со значением, имеют альтернативный вариант. В этом альтернативном варианте они принимают как аргумент само это значение. Значение, написанное непосредственно в коде, называется immediate.

Поведение этих инструкций идентично поведению обычных вариантов, в том числе работа с флагами. Для информации по этим вариантам смотри документацию обычного варианта инструкции.

Обычная инструкция Immediate-вариант
ADD ADI
ADC ACI
SUB SUI
SBB SBI
CMP CUI
ANA ANI
XRA XRI
ORA ORI
CMP CPI
Пример использования
; Пример инструкции ADD
MVI A, 0x01
MVI B, 0x02
ADD B
; Теперь A = 0x03

; Пример замены этой инструкции на immediate-вариант
MVI A, 0x01
ADI 0x02
; Теперь A = 0x03

ADD

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция имеет immediate-вариант.

Эта инструкция добавляет к значению в аккумуляторе значение из аргумента.

Если результат оказывается больше 255, то происходит переполнение. Из итогового результата вычитается 256 (то есть, если результат был 257, то в A будет записано 1, и т. д.), а флаг CY будет установлен.

Если при сложении двух значений в столбик получается перенос в бит 4 из бита 3, то устанавливается флаг AC. Пример:

Номер бита 7 6 5 4 3 2 1 0
0x2E 0 0 1 0 1 1 1 0
+ 0x74 0 1 1 1 0 1 0 0
= 0xA2 1 0 1 0 0 0 1 0
             
  CY=0     AC=1        

Флаги Z, S и P обновляются как обычно.

ADC

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция имеет immediate-вариант.

Эта инструкция выполняет то же самое, что и ADD, только к результату ещё добавляется значение флага CY. Эта инструкция полезна, когда производится сложение над числом, занимающим несколько байтов.

SUB

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция имеет immediate-вариант.

Эта инструкция вычитает из значения в аккумуляторе значение аргумента, согласно правилам дополнительного кода

Если из старшего бита нет переноса – то есть если было заимствование – то флаг CY устанавливается, иначе сбрасывается. Это противоположное поведение от операции сложения, где CY сбрасывается при отсутствии переполнения.

Флаги Z, S и P обновляются как обычно.

SBB

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция имеет immediate-вариант.

Сначала, значение флага CY внутренне добавляется к значению аргумента. Затем результат этого вычитается из аккумулятора, как с SUB.

ANA

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция имеет immediate-вариант.

Выполняет побитовое И между значением аккумулятора и значением аргумента. Для каждого из 8 битов, если значение бита в аккумуляторе и значение бита в аргументе равно 1, то в ответе бит устанавливается в 1, иначе 0.

Флаг CY сбрасывается. Флаги Z, S и P обновляются как обычно.

XRA

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция имеет immediate-вариант.

Выполняет побитовое исключительное-ИЛИ между значением аккумулятора и значением аргумента. Для каждого из 8 битов, если значение бита в аккумуляторе и значение бита в аргументе различается, то в ответе бит устанавливается в 1, иначе 0.

Флаги CY и AC сбрасывается. Флаги Z, S и P обновляются как обычно.

ORA

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция имеет immediate-вариант.

Выполняет побитовое ИЛИ между значением аккумулятора и значением аргумента. Для каждого из 8 битов, если либо значение бита в аккумуляторе, либо значение бита в аргументе равно 1, то в ответе бит устанавливается в 1, иначе 0.

Флаги CY и AC сбрасывается. Флаги Z, S и P обновляются как обычно.

CMP

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция имеет immediate-вариант.

Эта инструкция выполняет сравнение значения аккумулятора с значением аргумента.

Внутренне, из значения регистра A вычитается значение аргумента, результат игнорируется (оба регистра остаются неизменными), а флаги устанавливаются в соответствии с результатом вычитания.

В частности, Z будет установлен, если два значения равны, и сброшен, если они не равны. Поскольку выполняется вычитание, флаг CY будет установлен, если значение аргумента больше значения аккумулятора, и сброшен иначе.

Флаги Z, S и P обновляются как обычно (относительно результата внутреннего вычитания).

INR/DCR

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эти две инструкции изменяют значение указываемого регистра на 1. Операция INR X эквивалентна ADI X, 1, а DCR X эквивалентна SUI X, 1. Поэтому, она также изменяет флаги Z, S, P и AC таким же образом, как и соответствующие инструкции сложения и вычитания.

RLC/RRC/RAL/RAR

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эти четыре инструкции служат для побитового сдвига аккумулятора. RLC вращает регистр влево ←, RRC – вправо →, а RAL и RAR вращают регистр через флаг переноса CY. Важно заметить, это противоположно интуиции – инструкции с буквой C в названии не используют флаг CY для осуществления переноса, а RLC и RRC, наоборот, используют.

В микроконтроллерах, современных Intel 8080, по умолчанию осуществлялся N+1-перенос, и по умолчанию сдвиги производились через бит переноса. Можно запомнить операции RLC и RRC как Rotate Left/Right and Copy. Более детальное обсуждение можно увидеть в вопросе на Retrocomputing Stack Exchange.

Побитовый сдвиг – это операция, при которой биты регистра перемещаются между позициями:

Состояние регистра 7 6 5 4 3 2 1 0
Начальное значение 1 1 1 1 0 0 0 0
После сдвига вправо → 0 1 1 1 1 0 0 0
После сдвига влево ← 1 1 1 0 0 0 0 1

В этой таблице приведен пример сдвига, при котором тот бит, который оказался выдвинут из байта, оказывается на противоположном конце байта – например, при сдвиге вправо → бит 0 перешел в бит 7, а при сдвиге влево ← бит 7 перешел в бит 0. Так работают операции RLC и RRC. При выполнении этих операций бит, который оказался выдвинут, также записывается в флаг переноса CY.

При операции RAL/RAR также осуществляется сдвиг, но на этот раз по 9-битному значению, составленному из 8-битного аккумулятора и бита переноса CY. При RAL сдвиг происходит влево ←, и флаг CY дописывается слева ← от аккумулятора.

Состояние регистров CY 7 6 5 4 3 2 1 0
До RAL 0 1 1 1 1 0 0 0 0
После RAL 1 1 1 1 0 0 0 0 0
После двух RAL 1 1 1 0 0 0 0 0 1
После трех RAL 1 1 0 0 0 0 0 1 1
После четырех RAL 1 0 0 0 0 0 1 1 1
После пяти RAL 0 0 0 0 0 1 1 1 1
После шести RAL 0 0 0 0 1 1 1 1 0

При RAR сдвиг происходит вправо →, и флаг CY дописывается справа → от аккумулятора.

Состояние регистров 7 6 5 4 3 2 1 0 CY
До RAR 1 1 0 0 0 0 1 1 0
После RAR 0 1 1 0 0 0 0 1 1
После двух RAR 1 0 1 1 0 0 0 0 1
После трех RAR 1 1 0 1 1 0 0 0 0
После четырех RAR 0 1 1 0 1 1 0 0 0
После пяти RAR 0 0 1 1 0 1 1 0 0
После шести RAR 0 0 0 1 1 0 1 1 0

DAA

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эта инструкция изменяет значение в аккумуляторе, используя значения в флагах CY и AC, чтобы преобразовать результат операции ADD или SUB в значение в двоично-десятичном формате.

Одно 8-битное значение может представлять две десятичных цифры, если старшие и младшие 4 бита каждый не превышают 9. Между несколькими байтами, содержащими так закодированные значения, можно выполнять арифметику, используя обычные 8-битные арифметические операции – нужно лишь проверять, что после каждой операции сохраняется свойство, что каждые из 4-битных значений не превышают 9. Операция DAA (Decimal Adjust Accumulator) осуществляет эту проверку и изменение по следующим правилам:

  1. Если младшие 4 бита имеют значение больше 9, или если флаг AC установлен, то значение аккумулятора увеличивается на 6, иначе увеличение не происходит. Если произошел перенос из младших 4 битов, устанавливается флаг AC.
  2. Если после шага 1 старшие 4 бита аккумулятора имеют значение больше 9, или если установлен флаг CY, то значение старших 4 битов увеличивается на 6, иначе увеличение не происходит. Если произошел перенос из старших 4 битов, устанавливается флаг CY.

Флаги Z, S и P обновляются как обычно.

Псевдокод для операции DAA (списано из кода эмулятора Intel 8080):

procedure DAA is
  NCY := RegF.CY
  correction := 0x00
  LSB := RegA & 0x0F
  MSB := RegA >> 4

  if RegF.AC or LSB > 9 then
    correction := 0x06
  end if

  if RegF.CY or MSB > 9 or (MSB >= 9 and LSB >= 9) then
    correction := (correction | 0x60)
    NCY := 1
  end if

  call ADI(correction)
  RegF.CY := NCY
end procedure

STC/CMC

⬆ Вернуться к арифметико-логическим инструкциям, 8-бит

Эти две инструкции используются для управления флагом CY. Инструкция STC устанавливает флаг CY в состояние 1, а инструкция CMC изменяет его значение на противоположное.

Пример использования
STC
; Теперь `CY=1`
JNC 0x4000  ; переход не будет осуществлен, потому что `CY ≠ 0`

CMC
; Теперь `CY=0`
JNC 0x4000  ; переход будет осуществлен, потому что `CY = 0`

📐 Арифметико-логические инструкции, 16-бит

⬆ Вернуться к списку инструкций

Эти инструкции используются для выполнения арифметических операций над 16-битными значениями.

INX/DCX

⬆ Вернуться к арифметико-логическим инструкциям, 16-бит

Эти две инструкции позволяют инкрементировать или декрементировать (то есть, изменять на единицу в обе стороны) значение пары регистров BC (через аргумент B), DE (D), HL (H) или регистра – указателя на верхушку стека SP.

Эти операции не изменяют значение флагов и тихо переполняются через 16-битные значения. Например, если в H есть значение 0x7f, а в L есть значение 0xff, то после выполнения INX H, в H будет значение 0x80, а в L будет значение 0x00. Аналогично, если сначала B равен 0xff и C равен 0xff, то после выполнения INX B, в B будет 0x00, а в C будет 0x00.

Эти инструкции больше всего полезны для манипуляции адресами памяти. Например, комбинированный регистр HL используется как указатель в память для записи или чтения, и используя операции INX и DCX, можно перемещать этот указатель между близкими адресами.

Пример использования
; В бесконечном цикле, двигать указатель вперед по памяти,
; и для каждой ячейки увеличить их значение на 5.
; (Эта программа перезапишет всю память, в том числе
; свой собственный код, поэтому цикл не будет выполняться бесконечно.)

LXI H, 0x0000 ; перейти к началу памяти
loop:     ; метка на начало цикла
MOV A, M  ; загрузить значение из памяти
ADI 5     ; прибавить 5 к аккумулятору
MOV M, A  ; записать значение в память
INX H     ; перейти к следующей ячейке памяти
JMP loop  ; перейти к началу цикла

DAD

⬆ Вернуться к арифметико-логическим инструкциям, 16-бит

Эта инструкция выполняет сложение между 16-битными значениями. К значению в регистрах HL добавляется значение из одного из регистров BC, DE, HL, SP. Если результат оказывается больше максимального 16-битного значения, то устанавливается флаг CY.

Пример использования
LXI H, 0x7f00
LXI B, 0x0100
; Теперь HL=0x7f00, BC=0x0123
DAD B
; Теперь HL=0x8023, CY=0

LXI H, 0xf000
LXI D, 0x1100
; Теперь HL=0xf000, DE=0x1100
DAD D
; Теперь HL=0x0100, CY=1

🤬 Неопределенные инструкции

⬆ Вернуться к списку инструкций

Некоторые из опкодов являются незадокументированными в спецификации к процессору Intel 8080.

В стандартном Intel 8080 эти опкоды вызывают такое же поведение, как и какие-то другие. В списке инструкций написано, какую инструкцию копирует эта.

Однако в коде использовать эти инструкции не следует. Другие процессоры, например Zilog Z80, используют эти опкоды для каких-то других инструкций, поэтому если попробовать выполнить инструкцию с этим опкодом, процессор может выполнить неожиданное действие. Учитывая то что с помощью этих опкодов нельзя сделать ничего нового, рекомендуется просто расценивать их как запрещенные к использованию.