Список и таблица опкодов
В этом посте:
- Список опкодов в порядке их бинарного кода
- Таблица опкодов
- Объяснение по категориям инструкций
Аттрибуция
При составлении этой страницы использованы материалы:
- Таблица опкодов на сайте Pastaraiser, из которой я узнал про существование всех инструкций и использовал ее постоянно при написании программ
- Intel 8080 Programmer’s Manual – официальное руководство к программированию для процессора I8080
- исходный код для эмулятора Intel 8080
Категории инструкций
Все инструкции можно разделить на 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) | Категория | Мнемоника | Краткое описание | Затронутые флаги | Ссылка на описание |
Таблица опкодов
Инструкции
У процессора есть несколко категорий команд:
- 🔧 Управление процессором
- ⤵ Переходы и вызовы
- 📩 Загрузка, сохранение и перемещение, 8-бит
- 📥 Загрузка, сохранение и перемещение, 16-бит
- 🧮 Арифметические и логические операции, 8-бит
- 📐 Арифметические и логические операции, 16-бит
🔧 Управление процессором
⬆ Вернуться к категориям инструкций
Эти инструкции отвечают за операции, которые нельзя отнести к другим категориям. Они редко используются при написании прикладных программ, и больше относятся к разработке системы.
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 1 … RST 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 делает следующее:
- Записывает старшие 8 битов значения пары регистров в адрес
SP - 1. - Записывает младшие 8 битов значения пары регистров в адрес
SP - 2. - Уменьшает значение
SPна 2.
Инструкция POP делает следующее:
- Извлекает младшие 8 битов значения пары регистров из адреса
SP. - Извлекает старшие 8 битов значения пары регистров из адреса
SP + 1. - Увеличивает значение
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) осуществляет эту проверку и изменение по следующим правилам:
- Если младшие 4 бита имеют значение больше 9, или если флаг
ACустановлен, то значение аккумулятора увеличивается на 6, иначе увеличение не происходит. Если произошел перенос из младших 4 битов, устанавливается флагAC. - Если после шага 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, используют эти опкоды для каких-то других инструкций, поэтому если попробовать выполнить инструкцию с этим опкодом, процессор может выполнить неожиданное действие. Учитывая то что с помощью этих опкодов нельзя сделать ничего нового, рекомендуется просто расценивать их как запрещенные к использованию.
Если вам помогла эта статья, вы можете поддержать автора: