Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 

Repository files navigation

⚙️ ASMLings — подробный гайд на русском

Полный русскоязычный гайд по asmlings — интерактивной песочнице для изучения ассемблера Intel 8086, в которой 16-битный x86-эмулятор написан на Rust. Внутри: что это, как устроено под капотом, как установить, как читать и решать упражнения, разборы реальных задач из репозитория, готовые примеры в examples/ и шпаргалки.

ASMLings — это «rustlings, но для ассемблера 8086». Ты редактируешь .asm-файл, сохраняешь — эмулятор моментально проверяет, что регистры, флаги и память пришли в нужное состояние, и подсвечивает зелёным/красным.

📢 t.me/rust_code — основной Telegram-канал с уроками и разбором кода для Rust-разработчиков.

🤖 t.me/ai_machinelearning_big_data — полезный Telegram-канал про ИИ, машинное обучение и Big Data: статьи, разборы моделей, новости и практические материалы.


📑 Оглавление

  1. Что такое ASMLings и зачем оно нужно
  2. Как это устроено под капотом
  3. Установка и зависимости
  4. Первый запуск
  5. Анатомия упражнения
  6. Мини-курс по 8086
  7. Карта памяти 8086
  8. Синтаксис NASM
  9. Базовые инструкции с примерами
  10. Подпрограммы и соглашения о сохранении регистров
  11. Разборы реальных упражнений из репозитория
  12. Примеры в репозитории
  13. Подводные камни 8086
  14. Как читать дизассемблированный код
  15. Частые ошибки и отладка
  16. Шпаргалка инструкций 8086
  17. Глоссарий
  18. FAQ
  19. Куда расти дальше

1. Что такое ASMLings и зачем оно нужно

ASMLings — это набор из ~32 коротких упражнений на ассемблере Intel 8086, выстроенных по возрастанию сложности: от mov ax, 0x1337 до 32-битного сложения через carry flag, циклов, подпрограмм, работы с памятью и стеком.

Что ты получаешь:

  • Мгновенный фидбек. Сохраняешь файл — через долю секунды видишь результат: прошёл ассерт или нет.
  • Изолированную среду. Не нужно поднимать MS-DOS, DOSBox или возиться с линковщиком — всё крутится во встроенном 16-битном эмуляторе.
  • Понятную прогрессию. Каждое упражнение строится на знаниях из предыдущего, а в комментариях прямо в .asm-файле уже лежит мини-объяснение темы.
  • Низкий порог входа в системное программирование. Понимание 8086 — это лучший «букварь» к x86-64, ARM64, ядрам ОС, реверсу, эксплойтам, embedded и тому, что генерит rustc/gcc.

Для кого: Rust-разработчики, бэкендеры и системщики, которые хотят перестать смотреть на ассемблер из godbolt как на магию.


2. Как это устроено под капотом

Когда ты сохраняешь .asm-файл, ASMLings выполняет следующий конвейер:

   твой .asm файл
        │
        ▼
   ┌──────────┐   плоский 16-битный бинарь
   │   NASM   │ ───────────────────────────►
   └──────────┘
        │
        ▼
   ┌────────────────────────────────────┐
   │  Rust-эмулятор 8086 (cpu crate)    │
   │                                    │
   │  - загружает бинарь в виртуальную  │
   │    память по адресу _start         │
   │  - декодирует инструкции           │
   │  - обновляет AX/BX/CX/DX/FLAGS/IP  │
   │  - крутится до hlt                 │
   └────────────────────────────────────┘
        │
        ▼
   ┌────────────────────────────────────┐
   │  Раннер: парсит ASSERT_*           │
   │  и сравнивает с финальным          │
   │  состоянием эмулятора              │
   └────────────────────────────────────┘
        │
        ▼
   ✅ или ❌
  1. NASM компилирует твой файл в плоский 16-битный бинарь (flat формат, без ELF/PE-обвязки).
  2. Rust-эмулятор 8086 загружает этот бинарь в виртуальную память по адресу _start.
  3. Эмулятор пошагово декодирует и исполняет инструкции, обновляя виртуальные регистры (AX, BX, CX, DX, SI, DI, BP, SP, IP), сегментные регистры (CS, DS, ES, SS) и FLAGS. Исполнение длится до встречи с hlt.
  4. Раннер парсит специальные комментарии ; ASSERT_REG, ; ASSERT_MEM, ; ASSERT_FLAG и сравнивает финальное состояние эмулятора с ожидаемым.
  5. Если все ассерты прошли и ты убрал строку ; I AM NOT DONE — раннер переключается на следующее упражнение.

Watch-режим (asmlings start) сидит на файловой системе через inotify/FSEvents и пере-прогоняет упражнение при каждом сохранении.

🧠 Почему именно 8086, а не x86-64? Потому что 8086 ровно настолько маленький, чтобы за вечер выучить набор инструкций целиком, но при этом достаточно реальный — вся идеология (регистры общего назначения, флаги, стек, сегменты) дошла до современных CPU практически без изменений.


3. Установка и зависимости

3.1 Что должно быть в системе

Зависимость Зачем нужна Проверка
Rust toolchain поставить и запустить asmlings rustc --version
NASM компилятор, который превращает .asm в бинарь nasm -v
(Linux) libatomic нужна Rust-эмулятору для 128-битных атомиков

Если Rust ещё не стоит:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

3.2 Установка NASM

# macOS
brew install nasm

# Ubuntu / Debian
sudo apt install nasm

# Arch Linux
sudo pacman -S nasm

# Fedora / RHEL
sudo dnf install nasm

# Windows (PowerShell)
winget install NASM

3.3 Установка asmlings

Вариант A — быстро, без компиляции (рекомендую):

cargo install cargo-binstall
cargo binstall asmlings

Вариант B — через cargo (с компиляцией всего эмулятора, ~3–5 минут):

cargo install asmlings

Вариант C — готовый бинарник: Releases.

3.4 Troubleshooting (Linux)

Если cargo install asmlings падает с ошибкой линковщика про __atomic_compare_exchange_16 или undefined symbol — твоему линковщику не хватает libatomic:

RUSTFLAGS="-Clink-arg=-latomic" cargo install asmlings

Если линкер ругается, что -latomic не найден — поставь библиотеку:

# Ubuntu / Debian
sudo apt install libatomic1

# Arch
sudo pacman -S gcc-libs

# Fedora / RHEL
sudo dnf install libatomic

3.5 Рекомендуемые редакторы и расширения

Редактор Расширения для .asm
VS Code / Cursor 13xforever.language-x86-64-assembly, maziac.asm-code-lens
Neovim asm_x86.vim, treesitter parser asm
Helix встроенная подсветка asm из коробки
JetBrains плагин «NASM Language»

4. Первый запуск

# 1. Создай рабочую папку
mkdir asm-course && cd asm-course

# 2. Развернуть упражнения и трекер прогресса
asmlings init

# 3. Запустить watch-режим — оставь этот терминал открытым!
asmlings start

После asmlings init в текущей папке появится:

asm-course/
├── exercises/          ← задания (.asm файлы)
│   ├── 01_bare_metal.asm
│   ├── 02_halves.asm
│   ├── 03_addition.asm
│   └── ...
└── .asmlings/          ← трекер прогресса

Команды:

asmlings start    # watch-режим: пересобирает текущее упражнение при сохранении
asmlings run      # одноразово прогнать текущее упражнение
asmlings list     # статус всех упражнений (done / todo)
asmlings hint     # подсказка по текущему упражнению (если есть)

4.1 Рекомендуемый workflow

  1. Открой два окна терминала: в первом — asmlings start, во втором — обычный shell.
  2. Открой exercises/ в редакторе. Файл со статусом pending в asmlings list — текущий.
  3. Прочитай комментарии в файле, реши, сохрани.
  4. Если ассерт упал — посмотри expected vs got в выводе раннера.
  5. Зелёный — удали ; I AM NOT DONE, сохрани ещё раз — переход к следующему.

5. Анатомия упражнения

Открой любой файл из exercises/ — у всех одинаковый каркас. Вот 01_bare_metal.asm целиком, как он лежит в репозитории:

; EXERCISE 1: The Bare Metal
; The 8086 CPU has four main general-purpose 16-bit registers: AX, BX, CX, DX.
; Move the value 0x1337 into the AX register.
;
; ASSERT_REG: AX == 0x1337
global _start
section .text
_start:
; Write your code here:
; I AM NOT DONE

hlt

Разбираем построчно:

Строка Что это
; EXERCISE 1: ... Описание задачи человеческим языком.
; ASSERT_REG: AX == 0x1337 Машиночитаемая проверка. Раннер прочитает это и сравнит с финальным состоянием регистра AX.
global _start Делает метку _start видимой для линковщика.
section .text Секция исполняемого кода.
_start: Точка входа — эмулятор начнёт исполнение отсюда.
; Write your code here: Сюда ты вписываешь решение.
; I AM NOT DONE Сентинель. Пока эта строка есть — упражнение не зачитывается.
hlt Останавливает CPU. Без него эмулятор будет крутить мусор в памяти.

5.1 Три типа ассертов

; ASSERT_REG:  AX == 0x1337           ← проверка регистра
; ASSERT_MEM:  result == 0x000F       ← проверка слова в памяти по метке
; ASSERT_FLAG: ZF == 1                ← проверка флага

5.2 Решение упражнения 1

; EXERCISE 1: The Bare Metal
; ASSERT_REG: AX == 0x1337
global _start
section .text
_start:
    mov ax, 0x1337    ; ← наш код
hlt

6. Мини-курс по 8086

6.1 Регистры общего назначения (16 бит)

Регистр Назначение Половинки (8 бит)
AX Accumulator: арифметика, ввод/вывод AH (high) / AL (low)
BX Base: адресация ([bx], [bx+si]) BH / BL
CX Counter: счётчик циклов и сдвигов CH / CL
DX Data: умножение/деление, порты I/O DH / DL

Особенность 8086: каждый из этих регистров можно резать пополам:

mov ax, 0x0000
mov ah, 0xAB      ; AX = 0xAB00
mov al, 0xCD      ; AX = 0xABCD

6.2 Индексные и указательные регистры

Регистр Что хранит
SI Source Index — указатель источника для строковых операций
DI Destination Index — указатель приёмника
BP Base Pointer — база стекового фрейма
SP Stack Pointer — вершина стека
IP Instruction Pointer — адрес текущей инструкции

6.3 Сегментные регистры

8086 адресует память по схеме сегмент:смещение. Физический адрес = сегмент * 16 + смещение — 20-битное адресное пространство, 1 МБ.

Регистр Назначение
CS Code Segment
DS Data Segment
ES Extra Segment
SS Stack Segment

6.4 Регистр FLAGS

Флаг Имя Когда взводится
ZF Zero результат = 0
CF Carry беззнаковое переполнение / перенос
SF Sign старший бит = 1 (отрицательное число)
OF Overflow знаковое переполнение
PF Parity чётное число единичных битов
AF Auxiliary перенос между нибблами
DF Direction направление строковых операций
IF Interrupt разрешены ли прерывания

В упражнениях чаще всего встречаются ZF, CF, SF.


7. Карта памяти 8086

Хотя ASMLings всё абстрагирует, полезно знать, как физически выглядел 1 МБ адресного пространства на реальном PC:

Адрес Что лежит
0x00000–0x003FF IVT — Interrupt Vector Table, 256 векторов по 4 байта
0x00400–0x004FF BIOS Data Area
0x00500–0x07BFF свободная conventional memory
0x07C00–0x07DFF boot sector (BIOS грузит сюда первый сектор диска)
0x07E00–0x9FFFF conventional RAM (DOS-программы)
0xA0000–0xAFFFF VGA video memory (графический режим)
0xB8000–0xBFFFF CGA/EGA text mode — сюда писали Hello, World! в DOS
0xC0000–0xEFFFF ROM-расширения (видеокарта, контроллеры)
0xF0000–0xFFFFF BIOS ROM

💡 Адрес 0xB8000 — легендарный. Запишешь туда два байта (символ + атрибут) — на экране появится цветная буква.

mov ax, 0xB800
mov es, ax           ; ES = сегмент видеопамяти
mov di, 0            ; смещение
mov al, 'H'
mov ah, 0x07         ; атрибут: светло-серый на чёрном
mov [es:di], ax      ; пишем символ в верхний-левый угол экрана

8. Синтаксис NASM

8.1 Скелет файла

; Это комментарий — всё после ';' до конца строки
global _start             ; экспортируем точку входа
section .text             ; секция кода
_start:
    mov ax, 0x1337
    hlt
section .data
counter: dw 0x0000        ; объявляем 16-битное слово
message: db "Hi", 0       ; объявляем байты со строкой

8.2 Правила синтаксиса (Intel)

  • Порядок операндов: mov dest, src — приёмник слева, источник справа.
  • Числа: 42 (dec), 0x2A или 2Ah (hex), 0b101010 (bin), 'A' (символ → 0x41).
  • Память — в квадратных скобках: mov ax, [0x0200], mov [bx], cx.
  • Размер задаётся регистром или директивой: mov word [bx], 1.

8.3 Директивы данных

Директива Размер Пример
db 1 байт db 0x42, 'A', 10
dw 2 байта dw 0x1337, 0xCAFE
dd 4 байта dd 0xDEADBEEF
resb N резерв N байт buffer: resb 64

8.4 Разрешённые режимы адресации

Форма Пример
[disp] mov ax, [0x0200]
[bx] или [bp] mov ax, [bx]
[si] или [di] mov ax, [si]
[bx+si], [bx+di], [bp+si], [bp+di] mov ax, [bx+si]
любое выше + disp mov ax, [bx+si+4]

Что нельзя: [ax], [cx], [dx], [bx+bp], [si+di].


9. Базовые инструкции с примерами

9.1 mov — пересылка

mov ax, 0x1234        ; немедленное → регистр
mov bx, ax            ; регистр → регистр
mov [0x0200], ax      ; регистр → память
mov cx, [0x0200]      ; память → регистр
mov [bx], dx          ; косвенная адресация

Что нельзя: mov [bx], [si] — память→память за раз запрещено.

9.2 Арифметика

mov ax, 10
add ax, 5       ; ax = 15
sub ax, 3       ; ax = 12
inc ax          ; ax = 13
dec ax          ; ax = 12
neg ax          ; ax = -12 = 0xFFF4

9.3 mul и div

mov ax, 6
mov bx, 7
mul bx              ; DX:AX = 42

xor dx, dx          ; ОБЯЗАТЕЛЬНО перед div маленьких чисел
mov ax, 100
mov bx, 7
div bx              ; AX = 14, DX = 2

9.4 Логика

and ax, 0x00FF     ; маска: оставить младший байт
or  ax, 0x0F00     ; установить биты
xor ax, ax         ; ax = 0  ← идиома обнуления
not ax             ; инвертировать все биты
test ax, 0x0001    ; как and, но результат не пишет

9.5 Сдвиги

shl ax, 1          ; *2
shr ax, 1          ; /2 (беззнаково)
sar ax, 1          ; /2 (знаково, сохраняет знак)
rol ax, 4          ; циклический сдвиг влево

9.6 cmp и переходы

cmp ax, 5
je  equal           ; ZF=1
jne not_equal
jl  less            ; знаковое <
jg  greater
jb  below           ; беззнаковое <
ja  above

jl/jg — знаковые сравнения, jb/ja — беззнаковые. Перепутать — получить баг на отрицательных.

9.7 loop

mov cx, 5
xor ax, ax
.next:
    add ax, 1
    loop .next     ; cx--, прыгает пока cx != 0

⚠️ При CX=0 на входе loop сделает 65536 итераций. Защита — jcxz перед циклом.

9.8 Стек

push ax            ; SP -= 2; [SS:SP] = AX
pop  bx            ; BX = [SS:SP]; SP += 2

10. Подпрограммы и соглашения о сохранении регистров

10.1 call и ret

call label = push IP_next; jmp label. ret = pop IP.

_start:
    mov ax, 9
    call double      ; вызов
    hlt              ; ax = 18

double:
    shl ax, 1
    ret

10.2 Что портится при call

В 8086 нет жёсткой ABI, как в System V x86-64. Подпрограмма может портить любые регистры. Две стратегии:

A — caller-saved. Вызывающий сохраняет:

push ax
push bx
call my_func
pop  bx
pop  ax

B — callee-saved. Подпрограмма сама сохраняет и восстанавливает:

my_func:
    push bx
    push cx
    ; ...
    pop  cx          ; восстанавливать в ОБРАТНОМ порядке
    pop  bx
    ret

10.3 pusha / popa (80186+)

my_func:
    pusha            ; push AX, CX, DX, BX, SP, BP, SI, DI
    ; ...
    popa
    ret

10.4 Передача аргументов через регистры

; memset(BX=addr, CX=count, AL=byte)
memset:
    push di
    mov di, bx
.loop:
    mov [di], al
    inc di
    loop .loop
    pop di
    ret

10.5 Стековый фрейм через BP

my_func:
    push bp
    mov  bp, sp
    mov  ax, [bp+4]      ; первый аргумент со стека
    add  ax, [bp+6]      ; второй
    pop  bp
    ret

11. Разборы реальных упражнений из репозитория

Упражнение 2 — Register Halves

_start:
    mov ah, 0xAB
    mov al, 0xCD
    hlt

Упражнение 3 — Addition

_start:
    mov bx, 0x0010
    add bx, 0x0005
    hlt

Упражнение 5 — Register to Register

_start:
    mov ax, 0xBEEF
    mov dx, ax
    hlt

Упражнение 8 — XOR-обнуление

_start:
    xor cx, cx
    xor dx, dx
    hlt

Упражнение 10 — Стек

_start:
    mov ax, 0xCAFE
    push ax
    pop bx
    hlt

Упражнение 14 — Умножение

_start:
    mov ax, 0x0005
    mov bx, 0x0006
    mul bx              ; DX:AX = 30
    hlt

Упражнение 17 — cmp и условный переход

_start:
    mov ax, 0x000A
    cmp ax, 0x000A
    je  is_ten
    mov bx, 0x0002
    jmp end
is_ten:
    mov bx, 0x0001
end:
    hlt

Упражнение 18 — Цикл

_start:
    xor ax, ax
    mov cx, 4
.next:
    add ax, 3
    loop .next
    hlt

Упражнение 20 — Запись в память

_start:
    mov ax, 0x0007
    add ax, 0x0008
    mov [result], ax
    hlt

section .data
result: dw 0x0000

Упражнение 23 — Подпрограммы

_start:
    mov ax, 0x0009
    call double
    hlt

double:
    shl ax, 1
    ret

Упражнение 25 — 32-битное сложение через CF

_start:
    mov dx, 0x0001
    mov ax, 0xFFFF
    mov cx, 0x0000
    mov bx, 0x0001

    add ax, bx          ; CF=1
    adc dx, cx          ; DX = 1 + 0 + 1 = 2
    hlt

adc (Add with Carry) добавляет ещё и текущий CF. Базовый приём multi-precision arithmetic.


12. Примеры в репозитории

В папке examples/ лежат готовые .asm-файлы с подробными комментариями:

Файл Тема
01_hello_register.asm загрузка значения в регистр
02_register_halves.asm работа с AH/AL
03_arithmetic.asm сложение, вычитание, neg
04_xor_idiom.asm идиома обнуления через XOR
05_stack.asm push/pop, обмен через стек
06_mul_div.asm умножение и деление с DX:AX
07_loop_sum.asm сумма 1+2+...+10 через loop
08_memory.asm чтение и запись в память
09_subroutine.asm вызов и возврат, передача через регистры
10_carry_32bit.asm 32-битное сложение через CF и ADC

Подробное описание — в examples/README.md.


13. Подводные камни 8086

13.1 loop при CX = 0

Цикл сначала декрементит CX — при входе с CX = 0 получишь 0xFFFF итераций. Защита:

    jcxz skip            ; если CX=0 — сразу прыжок
.next:
    ; ...тело цикла...
    loop .next
skip:

13.2 div без обнуления DX

Делимое — пара DX:AX. Если DX содержит мусор, результат будет неожиданным.

xor dx, dx            ; ОБЯЗАТЕЛЬНО
mov ax, 100
div bx

13.3 Знаковое vs беззнаковое сравнение

mov al, 0xFF          ; беззнаково = 255, знаково = -1
cmp al, 1
jb less_unsigned      ; не прыгнет (255 > 1)
jl less_signed        ; прыгнет (-1 < 1)

13.4 Размер операнда

mov [bx], 1            ; ОШИБКА: byte или word?
mov byte [bx], 1       ; ок: 1 байт
mov word [bx], 1       ; ок: 2 байта

13.5 Segment wrap

Смещение — 16 бит. 0xFFFF + 1 оборачивается в 0x0000 внутри сегмента, а не переходит в следующий.

13.6 push csmov cs, ...

В CS нельзя положить значение через mov — только через far jmp/call/retf.

13.7 Strings и DF

Строковые инструкции (movsb, stosb, lodsb) идут по памяти в направлении DF:

  • cldDF=0, идём вперёд (SI/DI инкрементируются).
  • stdDF=1, идём назад.

Хорошая привычка — ставить cld в начале процедуры.


14. Как читать дизассемблированный код

godbolt.org — пишешь Rust/C, видишь сгенерированный ассемблер.

14.1 Простая функция на Rust

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

rustc на x86-64 c -O:

add:
    lea  eax, [rdi + rsi]    ; одной инструкцией: eax = rdi + rsi
    ret

14.2 Цикл

pub fn sum_to(n: i32) -> i32 {
    let mut s = 0;
    for i in 1..=n { s += i; }
    s
}
sum_to:
    xor  eax, eax            ; s = 0
    test edi, edi
    jle  .done
    mov  ecx, 1
.loop:
    add  eax, ecx
    inc  ecx
    cmp  ecx, edi
    jle  .loop
.done:
    ret

Узнаваемо: xor eax, eax для обнуления, cmp + переход, inc для счётчика, ret.


15. Частые ошибки и отладка

Симптом Причина Решение
nasm: command not found NASM не установлен См. раздел 3.2
Упражнение не зачитывается, ассерты прошли Не удалена ; I AM NOT DONE Удали её
ASSERT_REG: AX == ... (got 0x0000) Не та инструкция или регистр Проверь, что пишешь в AX, а не BX/AL
Бесконечный цикл Забыл hlt или условие выхода в loop Добавь hlt, перепроверь CX
Странные значения после mul/div Результат / делимое в DX:AX Учитывай старшее слово, перед divxor dx, dx
Условные переходы «наоборот» на знаковых jb/ja вместо jl/jg Знаковые — jl/jg, беззнаковые — jb/ja
mov [bx], [si] не компилируется Память→память запрещено Через регистр
Linker error __atomic_compare_exchange_16 На Linux нет libatomic RUSTFLAGS="-Clink-arg=-latomic" cargo install asmlings

15.1 Отладка упражнения

  1. Перечитай комментарии в .asm — там обычно есть подсказка.
  2. Запусти asmlings run — увидишь, какой ассерт упал.
  3. Закомментируй последние строки, проверь промежуточное состояние.
  4. Эмулятор молчит — почти всегда нет hlt.

16. Шпаргалка инструкций 8086

Пересылка:   mov, xchg, lea, push, pop, pushf, popf
Арифметика:  add, adc, sub, sbb, inc, dec, neg, mul, imul, div, idiv, cmp
Логика:      and, or, xor, not, test
Сдвиги:      shl, shr, sal, sar, rol, ror, rcl, rcr
Переходы:    jmp, je/jne, jl/jg, jb/ja, jz/jnz, jc/jnc, jo/jno, js/jns, loop, jcxz
Стек/вызовы: call, ret, push, pop, pusha, popa, enter, leave
Строки:      movsb, movsw, lodsb, stosb, cmpsb, scasb, rep, repe, repne
Прочее:      nop, hlt, int, cld, std, clc, stc, cli, sti

16.1 Условия переходов

Инструкция Условие Использование
je / jz ZF=1 равно / ноль
jne / jnz ZF=0 не равно
jb / jc CF=1 беззнаковое <
ja CF=0 ∧ ZF=0 беззнаковое >
jl SF≠OF знаковое <
jg ZF=0 ∧ SF=OF знаковое >
js SF=1 отрицательное
jo OF=1 знаковое переполнение
jcxz CX=0 CX = 0 (не смотрит флаги!)

16.2 Идиомы

Идиома Эквивалент Зачем
xor reg, reg mov reg, 0 короче и быстрее
test reg, reg cmp reg, 0 короче
shl reg, 1 reg * 2 быстрее mul
shr reg, 1 reg / 2 быстрее div
inc reg add reg, 1 1 байт vs 3
lea ax, [bx+4] mov ax, bx; add ax, 4 одной инструкцией

17. Глоссарий

  • Word — 16-битное значение (2 байта). В 8086 это «родной» размер.
  • DWord — 32-битное (double word).
  • IVT (Interrupt Vector Table) — таблица обработчиков прерываний в начале памяти.
  • MBR (Master Boot Record) — первый сектор диска, который BIOS грузит по 0x7C00.
  • POST — Power-On Self-Test, что делает BIOS до загрузки ОС.
  • REAL mode — режим 8086, 20 бит адресации, без защиты памяти.
  • PROTECTED mode — режим 80286+, виртуальная память, привилегии.
  • Endianness — порядок байт. x86 — little-endian: 0x1234 в памяти как 34 12.
  • NOP — «no operation», 1 байт 0x90, ничего не делает (для выравнивания).
  • Prefetch queue — очередь предварительной выборки инструкций. У 8086 — 6 байт.
  • BCD (Binary Coded Decimal) — каждая десятичная цифра в 4 битах.
  • Sentinel (в ASMLings) — строка ; I AM NOT DONE.

18. FAQ

Q: Можно ли запускать настоящие 8086-программы (.COM)? A: ASMLings — учебная песочница, не полный DOS-эмулятор. Для .COM/.EXEDOSBox-X, 86Box, v86.

Q: Чем 8086 отличается от 8088? A: Только шириной шины данных (16 vs 8 бит). Программисту они идентичны. IBM PC изначально использовал 8088.

Q: Почему задачи только до 32-го? A: Это покрывает почти весь базовый ISA 8086. Дальше — расширения (80186, 80286, FPU), их удобнее изучать на x86-64.

Q: Есть ли смысл учить 8086 в 2026? A: Да. x86-64 — прямой наследник 8086. И 8086 — самый простой способ понять концепции (регистры, флаги, стек, сегменты), которые есть везде, включая ARM64.

Q: Можно использовать инструкции 80286+? A: Часто да (pusha/popa, shl reg, imm > 1). Но цель упражнений — освоить именно 8086.

Q: Где исходники эмулятора? A: В asmlings, зависимость cpu crate. Хороший материал для изучения «как написать эмулятор» на Rust.


19. Куда расти дальше

  • 📖 PC Assembly Language — Paul A. Carter (бесплатный PDF).
  • 📖 The Art of Assembly Language — Randall Hyde.
  • 📖 Modern X86 Assembly Language Programming — Daniel Kusswurm.
  • 🔗 felixcloutier.com/x86 — справочник по x86-инструкциям.
  • 🔗 godbolt.org — Compiler Explorer.
  • 🔗 OSDev wiki — если хочешь написать свою мини-ОС.
  • 🦀 Rust + asm: макрос asm! стабилен с 1.59.
  • 🔍 Следующие архитектуры: x86-64 → ARM64 → RISC-V.
  • 🛠 Реверс-инжиниринг: Ghidra, Binary Ninja, radare2.
  • 🎯 Boot sector кодинг: напиши свой 512-байтный «hello, world», который грузится BIOS без ОС.

19.1 Ассемблер прямо в Rust

use std::arch::asm;

fn add_via_asm(a: u64, b: u64) -> u64 {
    let mut result: u64;
    unsafe {
        asm!(
            "mov {0}, {1}",
            "add {0}, {2}",
            out(reg) result,
            in(reg)  a,
            in(reg)  b,
        );
    }
    result
}

19.2 Мини-проект-челлендж

Когда пройдёшь все упражнения, попробуй написать на 8086:

  1. Strlen — длина ASCIIZ-строки.
  2. Memcpy — копирование CX байт из [DS:SI] в [ES:DI].
  3. Atoi"12345" → число в AX.
  4. Fibonacci — N-е число Фибоначчи в 16 битах.
  5. Bubble sort — отсортировать массив из CX слов по адресу BX.

🤝 Вклад

PR и issue приветствуются.

📜 Лицензия

Гайд — MIT. Проект asmlings@giacomo-folli.


📢 t.me/rust_code — основной Telegram-канал с уроками и разбором кода для Rust-разработчиков.

About

ASMLings гайд на русском

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors