Перевод: Поляков А.В. http://www.avprog.narod.ru

Полный набор команд процессора 8086

Быстрые ссылки:

AAA
AAD
AAM
AAS
ADC
ADD
AND
CALL
CBW
CLC
CLD
CLI
CMC
CMP
CMPSB
CMPSW
CWD
DAA
DAS
DEC
DIV
HLT
IDIV
IMUL
IN
INC
INT
INTO
IRET
JA
JAE
JB
JBE
JC
JCXZ
JE
JG
JGE
JL
JLE
JMP
JNA
JNAE
JNB
JNBE
JNC
JNE
JNG
JNGE
JNL
JNLE
JNO
JNP
JNS
JNZ
JO
JP
JPE
JPO
JS
JZ
LAHF
LDS
LEA
LES
LODSB
LODSW
LOOP
LOOPE
LOOPNE
LOOPNZ
LOOPZ
MOV
MOVSB
MOVSW
MUL
NEG
NOP
NOT
OR
OUT
POP
POPA
POPF
PUSH
PUSHA
PUSHF
RCL
RCR
REP
REPE
REPNE
REPNZ
REPZ
RET
RETF
ROL
ROR
SAHF
SAL
SAR
SBB
SCASB
SCASW
SHL
SHR
STC
STD
STI
STOSB
STOSW
SUB
TEST
XCHG
XLATB
XOR

Типы операндов:

REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

SREG: DS, ES, SS, и только как второй операнд: CS.

memory: [BX], [BX+SI+7], переменная и т.п...(см. Доступ к памяти).

immediate: 5, -24, 3Fh, 10001101b, и т.п...

Примечания:

  • Если для команды требуются два операнда, то они разделяются запятой. Например:

    REG, memory

  • Если это два операнда, то они должны иметь одинаковый размер (кроме команд сдвига и циклического сдвига). Например:

    AL, DL
    DX, AX
    m1 DB ?
     AL, m1
    m2 DW ?
     AX, m2

  • Некоторые команды допускают несколько комбинаций операндов. Например:

    memory, immediate
    REG, immediate

    memory, REG
    REG, SREG

  • Некоторые примеры содержат макросы, поэтому желательно использовать "горячую клавишу" Shift + F8, чтобы Пропустить шаг (чтобы выполнить код макроса с максимальной скоростью, установите step delay - задержку между выполнением команд на ноль), иначе эмулятор будет выполнять каждую команду макроса. Ниже приведен пример, который использует макрос PRINTN:

   #make_COM#
   include 'emu8086.inc'
   ORG 100h
   MOV AL, 1
   MOV BL, 2
   PRINTN 'Hello World!'   ; макрос.
   MOV CL, 3
   PRINTN 'Welcome!'       ; макрос.
   RET

Эти знаки используются для отображения состояния флагов:

1 - команда устанавливает этот флаг в 1.
0 - команда устанавливает этот флаг в 0.
r - значение флага зависит от результата выполнения команды.
? - значение флага не определено (может быть 1 или 0).

Некоторые команды генерируются в абсолютно одинаковый машинный код, поэтому дизассемблер может иметь проблемы при декодировании вашего оригинального кода. Это особенно важно в командах условного перехода (см. "Управление ходом программы").

Команды в алфавитном порядке:

Команда Операнды Описание
AAA Нет операндов Коррекция ASCII-формата после сложения.
Корректирует результат в регистрах AH и AL после сложения при работе с BCD-значениями.

Она работает согласно следующему алгоритму

Если младшие (правые) четыре бита
регистра AL > 9 или флаг AF = 1, то:
  • AL = AL + 6
  • AH = AH + 1
  • AF = 1
  • CF = 1
иначе
  • AF = 0
  • CF = 0
в любом случае:
очистить старшие четыре бита регистра AL.

Пример:
MOV AX, 15   ; AH = 00, AL = 0Fh
AAA          ; AH = 01, AL = 05
RET
C | Z | S | O | P | A
r | ? | ? | ? | ? | r
AAD Нет операндов Коррекция ASCII-формата перед делением.
Подготавливает два BCD-значения для деления.

Алгоритм:

  • AL = (AH * 10) + AL
  • AH = 0
Пример:
MOV AX, 0105h   ; AH = 01, AL = 05
AAD             ; AH = 00, AL = 0Fh (15)
RET
C | Z | S | O | P | A
? | r | r | ? | r | ?
AAM Нет операндов Коррекция ASCII-формата после умножения.
Корректирует результат умножения двух BCD-значений.

Алгоритм:

  • AH = AL / 10
  • AL = остаток
Пример:
MOV AL, 15   ; AL = 0Fh
AAM          ; AH = 01, AL = 05
RET
C | Z | S | O | P | A
? | r | r | ? | r | ?
AAS Нет операндов Коррекция ASCII-формата после вычитания.
Корректирует результат в регистрах AH и AL после вычитания при работе с BCD-значениями.

Алгоритм:

если младшие четыре бита
регистра AL > 9 или AF = 1, то:
  • AL = AL - 6
  • AH = AH - 1
  • AF = 1
  • CF = 1
иначе
  • AF = 0
  • CF = 0
в любом случае:
очистить старшие четыре бита регистра AL.

Пример:
MOV AX, 02FFh  ; AH = 02, AL = 0FFh
AAS            ; AH = 01, AL = 09
RET
C | Z | S | O | P | A
r | ? | ? | ? | ? | r
ADC REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Сложение с переносом.

Алгоритм:

operand1 = operand1 + operand2 + CF

Пример:
STC        ; установить CF = 1
MOV AL, 5  ; AL = 5
ADC AL, 1  ; AL = 7
RET
C | Z | S | O | P | A
r | r | r | r | r | r
ADD REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Сложение.

Алгоритм:

operand1 = operand1 + operand2

Пример:
MOV AL, 5   ; AL = 5
ADD AL, -3  ; AL = 2
RET
C | Z | S | O | P | A
r | r | r | r | r | r
AND REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Логическое И между всеми битами двух операндов. Результат записывается в 1-й операнд.

Действуют следующие правила:

1 AND 1 = 1
1 AND 0 = 0
0 AND 1 = 0
0 AND 0 = 0

Пример:
MOV AL, 'a'        ; AL = 01100001b
AND AL, 11011111b  ; AL = 01000001b  ('A')
RET
C | Z | S | O | P
0 | r | r | 0 | r
CALL имя процедуры
метк
4-х байтовый адрес
         Передает управление процедуре, заносит в стек адрес следующей команды (из IP). 4-х байтовый адрес может быть введен в следующей форме: 1234h:5678h, первое значение - сегмент, второе значение - смещение (в случае дальнего вызова регистр CS также заносится в стек).

Пример:
#make_COM#
ORG 100h  ; для СОМ-файла.

CALL p1

ADD AX, 1

RET         ; вернуться в операционную систему.

p1 PROC     ; объявление процедуры.
    MOV AX, 1234h
    RET     ; возвращение в программу.
p1 ENDP
C Z S O P A
не изменяются
CBW Нет операндов Преобразует байт в слово.

Алгоритм:

Если старший бит AL = 1, то:
  • AH = 255 (0FFh)

иначе
  • AH = 0
Пример:
MOV AX, 0   ; AH = 0, AL = 0
MOV AL, -5  ; AX = 000FBh (251)
CBW         ; AX = 0FFFBh (-5)
RET
C Z S O P A
не изменяются
CLC Нет операндов Очищает флаг переноса (CF).

Алгоритм:

CF = 0

C
0
CLD Нет операндов Очищает флаг направления (DF). Clear Direction flag. Значения регистров SI и DI будут увеличиваться командами: CMPSB, CMPSW, LODSB, LODSW, MOVSB, MOVSW, STOSB, STOSW.

Алгоритм:

DF = 0

D
0
CLI Нет операндов Очищает флаг прерывания (IF). Это отключает аппаратные прерывания.

Алгоритм:

IF = 0

I
0
CMC Нет операндов Переключает флаг переноса, т.е. инвертирует значение CF.

Алгоритм:

если CF = 1 то CF = 0
если CF = 0 то CF = 1

C
r
CMP REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Сравнение.

Алгоритм:

operand1 - operand2

результат никуда не записывается, флаги устанавливаются (OF, SF, ZF, AF, PF, CF) в соответствии с результатом.

Пример:
MOV AL, 5
MOV BL, 5
CMP AL, BL  ; AL = 5, ZF = 1 (значит равно!)
RET
C | Z | S | O | P | A
r | r | r | r | r | r
CMPSB Нет операндов Сравнивает байты: ES:[DI] из DS:[SI].

Алгоритм:

  • DS:[SI] - ES:[DI]
  • установить флаги в соответствии с результатом:
    OF, SF, ZF, AF, PF, CF
  • если DF = 0 то
    • SI = SI + 1
    • DI = DI + 1
    иначе
    • SI = SI - 1
    • DI = DI - 1
 Пример:
см. cmpsb.asm в каталоге Samples.

C | Z | S | O | P | A
r | r | r | r | r | r
CMPSW Нет операндов Сравнивает слова: ES:[DI] из DS:[SI].

Алгоритм:

  • DS:[SI] - ES:[DI]
  • установить флаги в соответствии с результатом:
    OF, SF, ZF, AF, PF, CF
  • если DF = 0 то
    • SI = SI + 2
    • DI = DI + 2
    иначе
    • SI = SI - 2
    • DI = DI - 2
 Пример:
см. cmpsw.asm в каталоге Samples.

C | Z | S | O | P | A
r | r | r | r | r | r
CWD Нет операндов Преобразует слово в двойное слово.

Алгоритм:

если старший бит AX = 1, то:
  • DX = 65535 (0FFFFh)

иначе
  • DX = 0
Пример:
MOV DX, 0   ; DX = 0
MOV AX, 0   ; AX = 0
MOV AX, -5  ; DX AX = 00000h:0FFFBh
CWD         ; DX AX = 0FFFFh:0FFFBh
RET
C Z S O P A
не изменяются
DAA Нет операндов Десятичная коррекция после сложения.
Корректирует результат сложения двух упакованных BCD-значений.

Алгоритм:

если младшие четыре бита AL > 9 или AF = 1, то:
  • AL = AL + 6
  • AF = 1
если AL > 9Fh или CF = 1, то:
  • AL = AL + 60h
  • CF = 1
Пример:
MOV AL, 0Fh  ; AL = 0Fh (15)
DAA          ; AL = 15h
RET
C | Z | S | O | P | A
r | r | r | r | r | r
DAS Нет операндов Десятичная коррекция после вычитания.
Корректирует результат вычитания двух упакованных BCD-значений.

Алгоритм:

если младшие четыре бита AL > 9 или AF = 1, то:
  • AL = AL - 6
  • AF = 1
если AL > 9Fh или CF = 1, то:
  • AL = AL - 60h
  • CF = 1
Пример:
MOV AL, 0FFh  ; AL = 0FFh (-1)
DAS           ; AL = 99h, CF = 1
RET
C | Z | S | O | P | A
r | r | r | r | r | r
DEC REG
memory
         Декремент.

Алгоритм:

operand = operand - 1

Пример:
MOV AL, 255  ; AL = 0FFh (255 или -1)
DEC AL       ; AL = 0FEh (254 или -2)
RET
Z | S | O | P | A
r | r | r | r | r
CF - не изменяется!
DIV REG
memory
         Беззнаковое деление.

Алгоритм:

если операнд - это байт:
AL = AX / операнд
AH = остаток (модуль)
если операнд - это слово:
AX = (DX AX) / операнд
DX = остаток (модуль)
Пример:
MOV AX, 203   ; AX = 00CBh
MOV BL, 4
DIV BL        ; AL = 50 (32h), AH = 3
RET
C | Z | S | O | P | A
? | ? | ? | ? | ? | ?
HLT Нет операндов Останов системы.

Пример:
MOV AX, 5
HLT
C Z S O P A
unchanged
IDIV REG
memory
         Знаковое деление.

Алгоритм:

если операнд - это байт:
AL = AX / операнд
AH = остаток (модуль)
если операнд - это слово:
AX = (DX AX) / операнд
DX = остаток (модуль)
Пример:
MOV AX, -203 ; AX = 0FF35h
MOV BL, 4
IDIV BL      ; AL = -50 (0CEh), AH = -3 (0FDh)
RET
C | Z | S | O | P | A
? | ? | ? | ? | ? | ?
IMUL REG
memory
         Знаковое умножение.

Алгоритм:

если операнд - это байт:
AX = AL * операнд.
если операнд - это слово:
(DX AX) = AX * операнд.
Пример:
MOV AL, -2
MOV BL, -4
IMUL BL      ; AX = 8
RET
C | Z | S | O | P | A
r | ? | ? | r | ? | ?
CF=OF=0 если результат вписывается в операнд IMUL.
IN AL, im.byte
AL, DX
AX, im.byte
AX, DX 		Помещает данные из порта в AL или AX.
Второй операнд - это номер порта. Если требуется доступ к порту с номером более 255, то нужно использовать регистр DX.
Пример:
IN AX, 4  ; получить состояние светофора.
IN AL, 7  ; получить состояние шагового дигателя.
C Z S O P A
не изменяются
INC REG
memory
         Инкремент.

Алгоритм:

operand = operand + 1

Пример:
MOV AL, 4
INC AL       ; AL = 5
RET
Z | S | O | P | A
r | r | r | r | r
CF - не изменяется!
INT immediate byte Выполняет прерывание программы и передает управление функции, указанной в immediate byte (0..255).

Алгоритм:

    Поместить в стек:
    • флаговый регистр
    • CS
    • IP
  • IF = 0
  • Передать управление процедуре прерывания
Пример:
MOV AH, 0Eh  ; телетайп.
MOV AL, 'A'
INT 10h      ; Прерывание BIOS.
RET
C | Z | S | O | P | A | I
не изменяются |  |  |  |  |  | 0
INTO Нет операндов Приводит к прерыванию при возникновении переполнения (флаг OF = 1) и выполняет команду IRET 4.

Алгоритм:

если OF = 1 то INT 4

Пример:
; -5 - 127 = -132 (за пределами диапазона -128..127)
; результат вычитания (SUB) неправильный (124),
; поэтому OF = 1:
MOV AL, -5
SUB AL, 127   ; AL = 7Ch (124)
INTO          ; ошибка процесса.
RET
IRET Нет операндов Возврат из обработки прерывания.

Алгоритм:

    Выгрузить из стека:
    • IP
    • CS
    • регистр флагов
C Z S O P A
выгруженные
JA метка Короткий переход по "выше" или "не ниже или равно". Используется после проверки беззнаковых данных для передачи управления по другому адресу.

Алгоритм:

    если (CF = 0) и (ZF = 0) то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 250
   CMP AL, 5
   JA label1
   PRINT 'AL не больше 5'
   JMP exit
label1:
   PRINT 'AL больше 5'
exit:
   RET
C Z S O P A
не изменяются
JAE метка Короткий переход, если первый операнд "больше или равен" второму операнду. (в результате выполнения команды CMP). Беззнаковый.

Алгоритм:

    если CF = 0 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 5
   CMP AL, 5
   JAE label1
   PRINT 'AL не больше 5'
   JMP exit
label1:
   PRINT 'AL больше или равен 5'
exit:
   RET
C Z S O P A
не изменяются
JB метка Короткий переход, если первый операнд "меньше" второго операнда (в результате выполнения команды CMP). Беззнаковый.

Algorithm:

    если CF = 1 то выполниь переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 1
   CMP AL, 5
   JB  label1
   PRINT 'AL не меньше 5'
   JMP exit
label1:
   PRINT 'AL меньше 5'
exit:
   RET
C Z S O P A
не изменяются
JBE метка Короткий переход, если первый операнд "меньше" или "равен" второму операнду (в результате выполнения команды CMP). Беззнаковый.

Алгоритм:

    если CF = 1 или ZF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 5
   CMP AL, 5
   JBE  label1
   PRINT 'AL не меньше 5'
   JMP exit
label1:
   PRINT 'AL меньше или равен 5'
exit:
   RET
C Z S O P A
не изменяются
JC метка Короткий переход если флаг переноса установлен в 1.

Алгоритм:

    если CF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 255
   ADD AL, 1
   JC  label1
   PRINT 'нет переноса.'
   JMP exit
label1:
   PRINT 'имеем перенос.'
exit:
   RET
C Z S O P A
не изменяются
JCXZ метка Короткий переход, если регистр СХ равен 0.

Algorithm:

    если CX = 0 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV CX, 0
   JCXZ label1
   PRINT 'CX не равен нулю.'
   JMP exit
label1:
   PRINT 'CX равен нулю.'
exit:
   RET
C Z S O P A
не изменяются
JE метка Короткий переход, если первый операнд равен второму операнду (в результате выполнения команды CMP). Знаковый/Беззнаковый.

Алгоритм:

    если ZF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 5
   CMP AL, 5
   JE  label1
   PRINT 'AL не равен 5.'
   JMP exit
label1:
   PRINT 'AL равен 5.'
exit:
   RET
C Z S O P A
не изменяются
JG метка Короткий переход, если первый операнд больше второго операнда (в результате выполнения команды CMP). Знаковый.

Алгоритм:

    если (ZF = 0) и (SF = OF) то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 5
   CMP AL, -5
   JG  label1
   PRINT 'AL не больше -5.'
   JMP exit
label1:
   PRINT 'AL больше -5.'
exit:
   RET
C Z S O P A
не изменяются
JGE метка Короткий переход, если первый операнд больше или равен второму операнду (в результате выполнения команды CMP). Знаковый.

Алгоритм:

    если SF = OF то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   CMP AL, -5
   JGE  label1
   PRINT 'AL < -5'
   JMP exit
label1:
   PRINT 'AL >= -5'
exit:
   RET
C Z S O P A
не изменяются
JL метка Короткий переход, если первый операнд меньше второго операнда (в результате выполнения команды CMP). Знаковый.

Алгоритм:

    если SF <> OF то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, -2
   CMP AL, 5
   JL  label1
   PRINT 'AL >= 5.'
   JMP exit
label1:
   PRINT 'AL < 5.'
exit:
   RET
C Z S O P A
не изменяются
JLE метка Короткий переход, если первый операнд меньше или равен второму операнду (в результате выполнения команды CMP). Знаковый.

Алгоритм:

    если SF <> OF или ZF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, -2
   CMP AL, 5
   JLE label1
   PRINT 'AL > 5.'
   JMP exit
label1:
   PRINT 'AL <= 5.'
exit:
   RET
C Z S O P A
не изменяются
JMP метка
4-х байтовый адрес
         Безусловный переход. Передает управление другому участку программы. 4-х байтовый адрес может быть введен в такой форме: 1234h:5678h, первое значение - сегмент, второе значение - смещение.

Алгоритм:

    выполнить переход в любом случае
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 5
   JMP label1    ; "перешагнуть" через две строки!
   PRINT 'Нет перехода!'
   MOV AL, 0
label1:
   PRINT 'Добрались сюда!'
   RET
C Z S O P A
не изменяются
JNA метка Короткий переход, если первый операнд не больше второго операнда (в результате выполнения команды CMP). Беззнаковый.

Алгоритм:

    если CF = 1 или ZF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   CMP AL, 5
   JNA label1
   PRINT 'AL больше 5.'
   JMP exit
label1:
   PRINT 'AL не больше 5.'
exit:
   RET
C Z S O P A
не изменяются
JNAE метка Короткий переход, если первый операнд не больше и не равен второму операнду (в результате выполнения команды CMP). Беззнаковый.

Алгоритм:

    если CF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   CMP AL, 5
   JNAE label1
   PRINT 'AL >= 5.'
   JMP exit
label1:
   PRINT 'AL < 5.'
exit:
   RET
C Z S O P A
не изменяются
JNB метка Короткий переход, если первый операнд не меньше второго операнда (в результате выполнения команды CMP). Беззнаковый.

Алгоритм:

    если CF = 0 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 7
   CMP AL, 5
   JNB label1
   PRINT 'AL < 5.'
   JMP exit
label1:
   PRINT 'AL >= 5.'
exit:
   RET
C Z S O P A
не изменяются
JNBE метка Короткий переход, если первый операнд не меньше и не равен второму операнду (в результате выполнения команды CMP). Беззнаковый.

Алгоритм:

    если (CF = 0) и (ZF = 0) то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 7
   CMP AL, 5
   JNBE label1
   PRINT 'AL <= 5.'
   JMP exit
label1:
   PRINT 'AL > 5.'
exit:
   RET
C Z S O P A
не изменяются
JNC метка Короткий переход, если флаг переноса установлен в ноль.

Алгоритм:

    если CF = 0 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   ADD AL, 3
   JNC  label1
   PRINT 'имеем перенос.'
   JMP exit
label1:
   PRINT 'нет переноса.'
exit:
   RET
C Z S O P A
не изменяются
JNE метка Короткий переход, если первый операнд не равен второму операнду (в результате выполнения команды CMP). Знаковый/Беззнаковый.

Алгоритм:

    если ZF = 0 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   CMP AL, 3
   JNE  label1
   PRINT 'AL = 3.'
   JMP exit
label1:
   PRINT 'Al <> 3.'
exit:
   RET
C Z S O P A
не изменяются
JNG метка Короткий переход, если первый операнд не больше второго операнда (в результате выполнения команды CMP). Знаковый.

Алгоритм:

    если (ZF = 1) и (SF <> OF) то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   CMP AL, 3
   JNG  label1
   PRINT 'AL > 3.'
   JMP exit
label1:
   PRINT 'Al <= 3.'
exit:
   RET
C Z S O P A
не изменяются
JNGE метка Короткий переход, если первый операнд не больше и не равен второму операнду (в результате выполнения команды CMP). Знаковый.

Алгоритм:

    если SF <> OF то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   CMP AL, 3
   JNGE  label1
   PRINT 'AL >= 3.'
   JMP exit
label1:
   PRINT 'Al < 3.'
exit:
   RET
C Z S O P A
не изменяются
JNL метка Короткий переход, если первый операнд не меньше второго операнда (в результате выполнения команды CMP). Знаковый.

Алгоритм:

    если SF = OF то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   CMP AL, -3
   JNL label1
   PRINT 'AL < -3.'
   JMP exit
label1:
   PRINT 'Al >= -3.'
exit:
   RET
C Z S O P A
не изменяются
JNLE метка Короткий переход, если первый операнд не меньше и не равен второму операнду (в результате выполнения команды CMP). Знаковый.

Алгоритм:

    если (SF = OF) и (ZF = 0) то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 2
   CMP AL, -3
   JNLE label1
   PRINT 'AL <= -3.'
   JMP exit
label1:
   PRINT 'Al > -3.'
exit:
   RET
C Z S O P A
не изменяются
JNO метка Короткий переход, если нет переполнения.

Алгоритм:

    если OF = 0 то выполнить переход
Пример:
; -5 - 2 = -7 (в пределах -128..127)
; результат команды SUB (вычитание) правильный,
; поэтому OF = 0:

include 'emu8086.inc'
#make_COM#
ORG 100h
  MOV AL, -5
  SUB AL, 2   ; AL = 0F9h (-7)
JNO  label1
  PRINT 'переполнение!'
JMP exit
label1:
  PRINT 'нет переполнения.'
exit:
  RET
C Z S O P A
не изменяется
JNP метка Короткий переход, если нет паритета, или паритет нечетный. Проверяются только младшие 8 битов результата. Устанавливается командами CMP, SUB, ADD, TEST, AND, OR, XOR.

Алгоритм:

    если PF = 0 то выполнить переход
Example:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 00000111b   ; AL = 7
   OR  AL, 0           ; только установка флагов.
   JNP label1
   PRINT 'паритет четный.'
   JMP exit
label1:
   PRINT 'паритет нечетный.'
exit:
   RET
C Z S O P A
не изменяются
JNS метка Короткий переход, если нет знака (если положительный). Устанавливается командами CMP, SUB, ADD, TEST, AND, OR, XOR.

Алгоритм:

    если SF = 0 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 00000111b   ; AL = 7
   OR  AL, 0           ; только установка флагов.
   JNS label1
   PRINT 'есть знак.'
   JMP exit
label1:
   PRINT 'нет знака.'
exit:
   RET
C Z S O P A
не изменяются
JNZ метка Короткий переход, если "не ноль". Устанавливается командами CMP, SUB, ADD, TEST, AND, OR, XOR.

Алгоритм:

    если ZF = 0 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 00000111b   ; AL = 7
   OR  AL, 0           ; только установка флагов
   JNZ label1
   PRINT 'ноль.'
   JMP exit
label1:
   PRINT 'не ноль.'
exit:
   RET
C Z S O P A
не изменяются
JO метка Короткий переход по переполнению.

Алгоритм:

    если OF = 1 то выполнить переход
Пример:
; -5 - 127 = -132 (вне диапазона -128..127)
; результат вычитания неправильный (124),
; поэтому OF = 1:

include 'emu8086.inc'
#make_COM#
org 100h
  MOV AL, -5
  SUB AL, 127   ; AL = 7Ch (124)
JO  label1
  PRINT 'нет переполнения.'
JMP exit
label1:
  PRINT 'переполнение!'
exit:
  RET
C Z S O P A
не изменяются
JP метка Короткий переход, если есть паритет или паритет четный. Проверяются только младшие 8 битов результата. Устанавливается командами CMP, SUB, ADD, TEST, AND, OR, XOR.

Алгоритм:

    если PF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 00000101b   ; AL = 5
   OR  AL, 0           ; только установка флагов.
   JP label1
   PRINT 'паритет нечетный.'
   JMP exit
label1:
   PRINT 'паритет четный.'
exit:
   RET
C Z S O P A
не изменяются
JPE метка Короткий переход, если есть паритет или паритет четный. Проверяются только младшие 8 битов результата. Устанавливается командами CMP, SUB, ADD, TEST, AND, OR, XOR.

Алгоритм:

    если PF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 00000101b   ; AL = 5
   OR  AL, 0           ; только установка флагов.
   JPE label1
   PRINT 'паритет нечетный.'
   JMP exit
label1:
   PRINT 'паритет четный.'
exit:
   RET
C Z S O P A
не изменяются
JPO метка Короткий переход, если нет паритета, или паритет нечетный. Проверяются только младшие 8 битов результата. Устанавливается командами CMP, SUB, ADD, TEST, AND, OR, XOR.

Алгоритм:

    если PF = 0 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 00000111b   ; AL = 7
   OR  AL, 0           ; только установка флагов.
   JPO label1
   PRINT 'паритет четный.'
   JMP exit
label1:
   PRINT 'паритет нечетный.'
exit:
   RET
C Z S O P A
не изменяются
JS метка Переход по знаку (если отрицательный). Устанавливается командами CMP, SUB, ADD, TEST, AND, OR, XOR.

Алгоритм:

    если SF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 10000000b   ; AL = -128
   OR  AL, 0           ; только установка флагов.
   JS label1
   PRINT 'нет знака.'
   JMP exit
label1:
   PRINT 'есть знак.'
exit:
   RET
C Z S O P A
не изменяются
JZ метка Короткий переход по "нулю". Устанавливается командами CMP, SUB, ADD, TEST, AND, OR, XOR.

Алгоритм:

    если ZF = 1 то выполнить переход
Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AL, 5
   CMP AL, 5
   JZ  label1
   PRINT 'AL не равно 5.'
   JMP exit
label1:
   PRINT 'AL равно 5.'
exit:
   RET
C Z S O P A
не изменяются
LAHF Нет операндов Загрузка младших 8 байтов регистра флагов в регистр АН.

Алгоритм:

    AH = флаговому регистру

Биты AH:  7    6   5    4   3    2   1    0
        [SF] [ZF] [0] [AF] [0] [PF] [1] [CF]
биты 1, 3, 5 зарезервированы.

C Z S O P A
не изменяются
LDS REG, memory Загружает в необходимые регистры четыре байта памяти (двойное слово), содержащей относительный адрес и сегментный адрес. Сегментный адрес помещается в регистр DS, а относительный адрес (смещение) - в любой из общих или индексных регистров или в регистровый указатель.

Алгоритм:

  • REG = первое слово
  • DS = второе слово
Пример:

#make_COM#
ORG 100h

LDS AX, m

RET

m  DW  1234h
   DW  5678h

END
В AX записано значение 1234h, в DS записано значение 5678h.

C Z S O P A
не изменяются
LEA REG, memory Загрузка исполнительного адреса.
Команда LEA загружает в регистр, указанный в команде в качестве первого операнда, относительный адрес второго операнда (не значение операнда!). В качестве первого операнда следует указывать регистр общего назначения (не сегментный), в качестве второго - ячейку памяти. Команда

LEA reg, mem

эквивалентна команде

MOV reg,offset mem

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

Алгоритм:

  • REG = адрес памяти (смещение)
Обычно эту команду заменяют командой MOV, если это возможно.

Пример:

#make_COM#
ORG 100h

LEA AX, m

RET

m  DW  1234h

END
В AX записано значение: 0104h.
Команда LEA занимает 3 байта, команда RET занимает 1 байт, мы начинаем с адреса 100h, поэтому адрес 'm' - это 104h.

C Z S O P A
не изменяются
LES REG, memory Загрузка указателя с использованием регистра ES.
Считывает из памяти по указанному адресу двойное слово (32 бит), содержащее указатель (полный адрес некоторой ячейки), и загружает младшую половину указателя (т.е. относительный адрес) в указанный в команде регистр, а старшую половину указателя (т.е. сегментный адрес) в регистр ES. Таким образом, команда

LES reg, mem

эквивалентна следующей группе команд:

MOV reg, word ptr mem
MOV ES, word ptr mem+2

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

Алгоритм:

  • REG = первое слово
  • ES = второе слово
Пример:

#make_COM#
ORG 100h

LES AX, m

RET

m  DW  1234h
   DW  5678h

END
В AX записано значение 1234h, В ES записано значение 5678h.

C Z S O P A
не изменяются
LODSB Нет операндов Загружает байт из DS:[SI] в регистр AL. Изменяет SI.

Алгоритм:

  • AL = DS:[SI]
  • если DF = 0 то
    • SI = SI + 1
    иначе
    • SI = SI - 1
 Пример:
#make_COM#
ORG 100h

LEA SI, a1
MOV CX, 5
MOV AH, 0Eh

m: LODSB
INT 10h
LOOP m

RET

a1 DB 'H', 'e', 'l', 'l', 'o'
C Z S O P A
не изменяются
LODSW Нет операндов Загрузить слово из DS:[SI] в регистр AX. Изменяет SI.

Алгоритм:

  • AX = DS:[SI]
  • если DF = 0 то
    • SI = SI + 2
    иначе
    • SI = SI - 2
 Пример:
#make_COM#
ORG 100h

LEA SI, a1
MOV CX, 5

REP LODSW   ; в итоге в АХ будет значение 555h.

RET

a1 dw 111h, 222h, 333h, 444h, 555h
C Z S O P A
не изменяются
LOOP метка Уменьшает СХ, переходит на метку, если СХ не равен нулю.

Алгоритм:

  • CX = CX - 1
  • если CX <> 0 то
    • выполнить переход
    иначе
    • не выполнять переход, продолжить цикл
 Пример:
   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV CX, 5
label1:
   PRINTN 'цикл!'
   LOOP label1
   RET
C Z S O P A
не изменяются
LOOPE метка Уменьшить СХ, выполнить переход, если СХ "не ноль" или "равно". (ZF = 1).

Алгоритм:

  • CX = CX - 1
  • если (CX <> 0) и (ZF = 1) то
    • выполнить переход
    иначе
    • не выполнять переход, продолжить цикл
 Пример:
; Цикл выполняется 5 раз или до тех пор, пока
; результат в регистре AL лежит в пределах байта.
; Результат превысит значение 255 на третьем "заходе" (100+100+100),
; поэтому цикл будет завершен.

   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AX, 0
   MOV CX, 5
label1:
   PUTC '*'
   ADD AX, 100
   CMP AH, 0
   LOOPE label1
   RET
C Z S O P A
не изменяются
LOOPNE метка Уменьшает СХ, выполняет переход к метке если СХ не равен нулю и выполняется условие "не равно" (ZF = 0).

Алгоритм:

  • CX = CX - 1
  • если (CX <> 0) и (ZF = 0) то
    • выполнить переход
    иначе
    • не выполнять переход, продолжить цикл
 Пример:
; Цикл выполняется 5 раз, или до тех пор,
; пока не будет найдено число '7'.

   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV SI, 0
   MOV CX, 5
label1:
   PUTC '*'
   MOV AL, v1[SI]
   INC SI         ; следующий байт (SI=SI+1).
   CMP AL, 7
   LOOPNE label1
   RET
   v1 db 9, 8, 7, 6, 5
C Z S O P A
не изменяются
LOOPNZ метка Уменьшает СХ, выполняет переход к метке, если СХ не равен нулю и ZF = 0.

Алгоритм:

  • CX = CX - 1
  • если (CX <> 0) и (ZF = 0) то
    • выполнить переход
    иначе
    • не выполнять переход, продолжить цикл
 Пример:
; Цикл выполняется 5 раз, или до тех пор,
; пока не будет найдено число '7'.

   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV SI, 0
   MOV CX, 5
label1:
   PUTC '*'
   MOV AL, v1[SI]
   INC SI         ; следующий байт (SI=SI+1).
   CMP AL, 7
   LOOPNZ label1
   RET
   v1 db 9, 8, 7, 6, 5
C Z S O P A
не изменяются
LOOPZ метка Уменьшает СХ, выполняет переход к метке, если СХ не равен нулю и ZF = 1.

Алгоритм:

  • CX = CX - 1
  • если (CX <> 0) и (ZF = 1) то
    • выполнить переход
    иначе
    • не выполнять переход, продолжить цикл
 Пример:
; Цикл выполняется 5 раз или до тех пор,
; пока значение в регистре AL не выходит за пределы байта.
; Результат превысит значение 255 на третьем цикле (100+100+100),
; поэтому цикл будет завершен после третьего прохода.

   include 'emu8086.inc'
   #make_COM#
   ORG 100h
   MOV AX, 0
   MOV CX, 5
label1:
   PUTC '*'
   ADD AX, 100
   CMP AH, 0
   LOOPZ label1
   RET
C Z S O P A
не изменяются
MOV REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate

SREG, memory
memory, SREG
REG, SREG
SREG, REG 		Копирует operand2 в operand1.

Команда MOV не может:
  • записывать данные в регистры CS и IP.
  • копировать данные из одного сегментного регистра в другой сегментный регистр (сначала нужно скопировать данные в регистр общего назначения).
  • копировать непосредственное значение в сегментный регистр (сначала нужно скопировать данные в регистр общего назначения).

Алгоритм:

operand1 = operand2
Пример:
#make_COM#
ORG 100h
MOV AX, 0B800h    ; установить AX = B800h (память VGA).
MOV DS, AX        ; копировать значение из AX в DS.
MOV CL, 'A'       ; CL = 41h (ASCII-код).
MOV CH, 01011111b ; CL = атрибуты цвета.
MOV BX, 15Eh      ; BX = позиция на экране.
MOV [BX], CX      ; w.[0B800h:015Eh] = CX.
RET               ; вернуться в операционную систему.
C Z S O P A
не изменяются
MOVSB Нет операндов Копирует байт из DS:[SI] в ES:[DI]. Изменяет регистры SI и DI.

Алгоритм:

  • ES:[DI] = DS:[SI]
  • если DF = 0 то
    • SI = SI + 1
    • DI = DI + 1
    иначе
    • SI = SI - 1
    • DI = DI - 1
 Пример:
#make_COM#
ORG 100h

LEA SI, a1
LEA DI, a2
MOV CX, 5
REP MOVSB

RET

a1 DB 1,2,3,4,5
a2 DB 5 DUP(0)
C Z S O P A
не изменяются
MOVSW Нет операндов Копирует слово из DS:[SI] в ES:[DI]. Изменяет регистры SI и DI.

Алгоритм:

  • ES:[DI] = DS:[SI]
  • если DF = 0 то
    • SI = SI + 2
    • DI = DI + 2
    иначе
    • SI = SI - 2
    • DI = DI - 2
 Пример:
#make_COM#
ORG 100h

LEA SI, a1
LEA DI, a2
MOV CX, 5
REP MOVSW

RET

a1 DW 1,2,3,4,5
a2 DW 5 DUP(0)
C Z S O P A
не изменяются
MUL REG
memory
         Беззнаковое умножение.

Алгоритм:

если операнд - byte:
AX = AL * операнд.
если операнд - word:
(DX AX) = AX * операнд.
Пример:
MOV AL, 200   ; AL = 0C8h
MOV BL, 4
MUL BL        ; AX = 0320h (800)
RET
C | Z | S | O | P | A
r | ? | ? | r | ? | ?
CF=OF=0 если старшая секция результата - ноль.
NEG REG
memory
         Отрицание. Делает операнд отрицательным (дополнение до двух).

Алгоритм:

  • Инвертировать все биты операнда
  • Прибавить единицу к инвертированному операнду
 Пример:
MOV AL, 5   ; AL = 05h
NEG AL      ; AL = 0FBh (-5)
NEG AL      ; AL = 05h (5)
RET
C | Z | S | O | P | A
r | r | r | r | r | r
NOP Нет операндов Нет операции. Обычно используется для небольшой задержки программы.

Алгоритм:

  • Ничего не делать
 Пример:
; ничего не делать три раза:
NOP
NOP
NOP
RET
C Z S O P A
не изменяются
NOT REG
memory
         Инвертирует каждый бит операнда.

Алгоритм:

  • если бит равен 1, переключить его в 0.
  • если бит равен 0, переключить его в 1.
 Пример:
MOV AL, 00011011b
NOT AL   ; AL = 11100100b
RET
C Z S O P A
не изменяются
OR REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Логическое ИЛИ между всеми битами двух операндов. Результат записывается в первый операнд.

Выполняются следующие правила:

1 OR 1 = 1
1 OR 0 = 1
0 OR 1 = 1
0 OR 0 = 0

Пример:
MOV AL, 'A'       ; AL = 01000001b
OR AL, 00100000b  ; AL = 01100001b  ('a')
RET
C | Z | S | O | P | A
0 | r | r | 0 | r | ?
OUT im.byte, AL
im.byte, AX
DX, AL
DX, AX 		Выводит данные из регистра AL или AX в порт.
Первый операнд - номер порта. Если требуется доступ к порту, номер которого превышает 255, то должен быть использован регистр DX.

Пример:
MOV AX, 0FFFh ; Включить все
OUT 4, AX     ; светофоры.

MOV AL, 100b  ; Включть третий магнит
OUT 7, AL     ; шагового двигателя.
C Z S O P A
не изменяются
POP REG
SREG
memory 		Получает 16-битовое значение из стека.

Алгоритм:

  • операнд = SS:[SP] (вершина стека)
  • SP = SP + 2
Пример:
MOV AX, 1234h
PUSH AX
POP  DX     ; DX = 1234h
RET
C Z S O P A
не изменяются
POPA Нет операндов Выгружает все регистры общего назначения DI, SI, BP, SP, BX, DX, CX, AX из стека.
(Значение SP игнорируется, оно выгружается, но не записывается в регистр SP).

Примечание: эта команда работает только на процессорах 80186 и выше!

Алгоритм:

  • POP DI
  • POP SI
  • POP BP
  • POP xx (значение SP игнорируется)
  • POP BX
  • POP DX
  • POP CX
  • POP AX
C Z S O P A
не изменяются
POPF Нет операндов Получает регистр флагов из стека.

Алгоритм:

  • флаги = SS:[SP] (вершина стека)
  • SP = SP + 2
C Z S O P A
выгружаются из стека
PUSH REG
SREG
memory
immediate 		Записывает 16-битовое значение в стек.

Примечание: PUSH immediate работает только на процессорах 80186 и выше!

Алгоритм:

  • SP = SP - 2
  • SS:[SP] (вершина стека) = операнд
Пример:
MOV AX, 1234h
PUSH AX
POP  DX     ; DX = 1234h
RET
C Z S O P A
не изменяются
PUSHA Нет операндов Помещает в стек все регистры общего назначения: AX, CX, DX, BX, SP, BP, SI, DI.
Использует оригинальное значение регистра SP (перед выполнением PUSHA).

Примечание: эта команда работает только на процессорах 80186 и выше!

Алгоритм:

  • PUSH AX
  • PUSH CX
  • PUSH DX
  • PUSH BX
  • PUSH SP
  • PUSH BP
  • PUSH SI
  • PUSH DI
C Z S O P A
не изменяются
PUSHF Нет операндов Записывает регистр флагов в стек.

Алгоритм:

  • SP = SP - 2
  • SS:[SP] (вершина стека) = флаги
C Z S O P A
не изменяются
RCL memory, immediate
REG, immediate

memory, CL
REG, CL 		Циклический сдвиг (ротация) влево через перенос. Количество ротаций устанавливается во втором операнде.
Если immediate больше единицы, ассемблер генерирует несколько команд RCL xx, 1, потому что 8086 имеет машинный код только для этой команды (тот же принцип работы используют все команды сдвига/ротации).

Алгоритм:

    самый левый бит записать во флаг CF, сдвинуть все биты влево, значение флага CF записать в самый правый бит (бит 0).

Пример:
STC               ; установить перенос (CF=1).
MOV AL, 1Ch       ; AL = 00011100b
RCL AL, 1         ; AL = 00111001b,  CF=0.
RET
C | O
r | r
OF=0 если первый операнд сохраняет первоначальный знак (+ или -).
RCR memory, immediate
REG, immediate

memory, CL
REG, CL 		Циклический сдвиг (ротация) вправо через перенос. Количество ротаций устанавливается во втором операнде.

Алгоритм:

    самый правый бит (бит 0) записать во флаг CF, сдвинуть все биты вправо, значение флага CF записать в самый левый бит.

Пример:
STC               ; установить перенос (CF=1).
MOV AL, 1Ch       ; AL = 00011100b
RCR AL, 1         ; AL = 10001110b,  CF=0.
RET
C | O
r | r
OF=0 если первый операнд сохраняет первоначальный знак (+ или -).
REP нужная команда
         Повторяет следующие команды MOVSB, MOVSW, LODSB, LODSW, STOSB, STOSW. Количество повторов указано в СХ.

Алгоритм:

check_cx:

если CX <> 0 то
  • выполнить нужную команду
  • CX = CX - 1
  • вернуться к метке check_cx
иначе
  • выйти из REP-цикла
Z
r
REPE нужная команда
         Повторяет следующие команды CMPSB, CMPSW, SCASB, SCASW, пока ZF = 1, но не более CX раз.

Алгоритм:

check_cx:

если CX <> 0 то
  • выполнить нужную команду
  • CX = CX - 1
  • если ZF = 1 то:
    • вернуться к метке check_cx
    иначе
    • выйти из REPE-цикла
иначе
  • выйти из REPE-цикла
 Пример:
см. cmpsb.asm в каталоге Samples.

Z
r
REPNE нужная команда
         Повторяет следующие команды CMPSB, CMPSW, SCASB, SCASW, пока ZF = 0, но не более CX раз.

Алгоритм:

check_cx:

если CX <> 0 то
  • выполнить нужную команду
  • CX = CX - 1
  • если ZF = 0 то:
    • вернуться к метке check_cx
    иначе
    • выйти из REPNE-цикла
иначе
  • выйти из REPNE-цикла
Z
r
REPNZ нужная команда
         Повторяет следующие команды CMPSB, CMPSW, SCASB, SCASW, пока ZF = 0, но не более CX раз.

Алгоритм:

check_cx:

если CX <> 0 то
  • выполнить нужную команду
  • CX = CX - 1
  • если ZF = 0 то:
    • вернуться к метке check_cx
    иначе
    • выйти из REPNZ-цикла
иначе
  • выйти из REPNZ-цикла
Z
r
REPZ нужная команда
         Повторяет следующие команды CMPSB, CMPSW, SCASB, SCASW, пока ZF = 1, но не более CX раз.

Алгоритм:

check_cx:

если CX <> 0 то
  • выполнить нужную команду
  • CX = CX - 1
  • если ZF = 1 то:
    • вернуться к метке check_cx
    иначе
    • выйти из REPZ-цикла
иначе
  • выйти из REPZ-цикла
Z
r
RET Нет операндов
или четное immediate 		Возврат из ближней процедуры.

Алгоритм:

  • Получить из стека:
    • IP
  • если имеется операнд immediate: SP = SP + операнд
Пример:
#make_COM#
ORG 100h  ; для COM-файла.

CALL p1

ADD AX, 1

RET         ; вернуться в операционную систему.

p1 PROC     ; объявление процедуры.
    MOV AX, 1234h
    RET     ; вернуться в программу.
p1 ENDP
C Z S O P A
не изменяются
RETF Нет операндов
или четное immediate 		Возврат из дальней процедуры.

Алгоритм:

  • Получить из стека:
    • IP
    • CS
  • если имеется операнд immediate: SP = SP + операнд
C Z S O P A
не изменяются
ROL memory, immediate
REG, immediate

memory, CL
REG, CL 		Циклический сдвиг (ротация) влево. Количество ротаций устанавливается во втором операнде.

Алгоритм:

    самый левый бит записать во флаг CF, сдвинуть все биты влево, в самый правый бит записать флаг CF.
Пример:
MOV AL, 1Ch       ; AL = 00011100b
ROL AL, 1         ; AL = 00111000b,  CF=0.
RET
C | O
r | r
OF=0 если первый операнд сохраняет первоначальный знак (+ или -).
ROR memory, immediate
REG, immediate

memory, CL
REG, CL 		Циклический сдвиг (ротация) вправо. Количество ротаций устанавливается во втором операнде.

Алгоритм:

    самый правый бит записать во флаг CF, сдвинуть все биты вправо, в самый левый бит записать флаг CF.
Пример:
MOV AL, 1Ch       ; AL = 00011100b
ROR AL, 1         ; AL = 00001110b,  CF=0.
RET
C | O
r | r
OF=0 если первый операнд сохраняет первоначальный знак (+ или -).
SAHF Нет операндов Записать данные из регистра АН в младшие 8 битов регистра флагов.

Алгоритм:

    регистр флагов = AH

Биты АН:   7    6   5    4   3    2   1    0
         [SF] [ZF] [0] [AF] [0] [PF] [1] [CF]
биты 1, 3, 5 зарезервированы.

C | Z | S | O | P | A
r | r | r | r | r | r
SAL memory, immediate
REG, immediate

memory, CL
REG, CL 		Арифметический сдвиг влево. Количество сдвигов записывается во второй операнд.

Алгоритм:

  • самый левый бит записать в CF, сдвинуть все биты влево,
  • в самый правый бит записать ноль.
 Пример:
MOV AL, 0E0h      ; AL = 11100000b
SAL AL, 1         ; AL = 11000000b,  CF=1.
RET
C | O
r | r
OF=0 если первый операнд сохраняет первоначальный знак (+ или -).
SAR memory, immediate
REG, immediate

memory, CL
REG, CL 		Арифметический сдвиг вправо. Количество сдвигов записывается во второй операнд.

Алгоритм:

  • самый правый бит записать в CF, сдвинуть все биты вправо,
  • в самый правый бит записать ноль.
  • Бит знака, который вставляется в самую левую позицию, имеет то же значение, что и перед сдвигом.
 Пример:
MOV AL, 0E0h      ; AL = 11100000b
SAR AL, 1         ; AL = 11110000b,  CF=0.

MOV BL, 4Ch       ; BL = 01001100b
SAR BL, 1         ; BL = 00100110b,  CF=0.

RET
C | O
r | r
OF=0 если первый операнд сохраняет первоначальный знак (+ или -).
SBB REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Вычитание с заемом.

Алгоритм:

operand1 = operand1 - operand2 - CF

Пример:
STC
MOV AL, 5
SBB AL, 3    ; AL = 5 - 3 - 1 = 1

RET
C | Z | S | O | P | A
r | r | r | r | r | r
SCASB Нет операндов Сравнивает байты (ищет байт в строке): AL из ES:[DI].

Алгоритм:

  • ES:[DI] - AL
  • установить флаги в зависимости от результата:
    OF, SF, ZF, AF, PF, CF
  • если DF = 0 то
    • DI = DI + 1
    иначе
    • DI = DI - 1

C | Z | S | O | P | A
r | r | r | r | r | r
SCASW Нет операндов Сравнивает слова: AX из ES:[DI].

Алгоритм:

  • ES:[DI] - AX
  • установить флаги в зависимости от результата:
    OF, SF, ZF, AF, PF, CF
  • если DF = 0 то
    • DI = DI + 2
    иначе
    • DI = DI - 2

C | Z | S | O | P | A
r | r | r | r | r | r
SHL memory, immediate
REG, immediate

memory, CL
REG, CL 		Сдвиг влево. Количество сдвигов указывается во втором операнде. Знаковый бит рассматривается как обычный бит данных.

Algorithm:

  • Записать самый левый бит в CF, сдвинуть все биты влево.
  • В самый правый бит записать ноль.
 Пример:
MOV AL, 11100000b
SHL AL, 1         ; AL = 11000000b,  CF=1.

RET
C | O
r | r
OF=0 если первый операнд сохраняет первоначальный знак (+ или -).
SHR memory, immediate
REG, immediate

memory, CL
REG, CL 		Сдвиг вправо. Количество сдвигов указывается во втором операнде. Знаковый бит рассматривается как обычный бит данных.

Algorithm:

  • Записать самый правый бит в CF, сдвинуть все биты вправо.
  • В самый левый бит записать ноль.
 Пример:
MOV AL, 00000111b
SHR AL, 1         ; AL = 00000011b,  CF=1.

RET
C | O
r | r
OF=0 если первый операнд сохраняет первоначальный знак (+ или -).
STC Нет операндов Устанавливает флаг переноса (CF).

Алгоритм:

CF = 1

C
1
STD Нет операндов Устанавливает флаг направления (DF). Значения регистров SI и DI уменьшаются командами: CMPSB, CMPSW, LODSB, LODSW, MOVSB, MOVSW, STOSB, STOSW.

Алгоритм:

DF = 1

D
1
STI Нет операндов Устанавливает флаг прерываний. Это включает аппаратные прерывания.

Алгоритм:

IF = 1

I
1
STOSB Нет операндов Записывает байт из AL в ES:[DI]. Изменяет SI.

Алгоритм:

  • ES:[DI] = AL
  • если DF = 0 то
    • DI = DI + 1
    иначе
    • DI = DI - 1
 Пример:
#make_COM#
ORG 100h

LEA DI, a1
MOV AL, 12h
MOV CX, 5

REP STOSB

RET

a1 DB 5 dup(0)
C Z S O P A
не изменяются
STOSW Нет операндов Записывает слово из AX в ES:[DI]. Изменяет SI.

Алгоритм:

  • ES:[DI] = AX
  • если DF = 0 то
    • DI = DI + 2
    иначе
    • DI = DI - 2
 Пример:
#make_COM#
ORG 100h

LEA DI, a1
MOV AX, 1234h
MOV CX, 5

REP STOSW

RET

a1 DW 5 dup(0)
C Z S O P A
не изменяются
SUB REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Вычитание.

Алгоритм:

operand1 = operand1 - operand2

Пример:
MOV AL, 5
SUB AL, 1         ; AL = 4

RET
C | Z | S | O | P | A
r | r | r | r | r | r
TEST REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Логическое И между всеми битами двух операндов. Не изменяет результирующий операнд, а влияет только на флаги. Задействованы следующие флаги: ZF, SF, PF.

Выполняются следующие правила:

1 AND 1 = 1
1 AND 0 = 0
0 AND 1 = 0
0 AND 0 = 0

Пример:
MOV AL, 00000101b
TEST AL, 1         ; ZF = 0.
TEST AL, 10b       ; ZF = 1.
RET
C | Z | S | O | P
0 | r | r | 0 | r
XCHG REG, memory
memory, REG
REG, REG 		Перестановка двух операндов.

Алгоритм:

operand1 < - > operand2

Пример:
MOV AL, 5
MOV AH, 2
XCHG AL, AH   ; AL = 2, AH = 5
XCHG AL, AH   ; AL = 5, AH = 2
RET
C Z S O P A
не изменяются
XLATB Нет операндов Транслирует байт из таблицы.
Копирует байт из памяти по адресу DS:[BX + беззнаковый AL] в регистр AL.

Алгоритм:

AL = DS:[BX + беззнаковый AL]

Пример:
#make_COM#
ORG 100h
LEA BX, dat
MOV AL, 2
XLATB     ; AL = 33h

RET

dat DB 11h, 22h, 33h, 44h, 55h
C Z S O P A
не изменяются
XOR REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate 		Логическое XOR (Исключающее ИЛИ)между всеми битами двух операндов. Результат записывается в первый операнд.

Выполняются следующие правила:

1 XOR 1 = 0
1 XOR 0 = 1
0 XOR 1 = 1
0 XOR 0 = 0

Пример:
MOV AL, 00000111b
XOR AL, 00000010b    ; AL = 00000101b
RET
C | Z | S | O | P | A
0 | r | r | 0 | r | ?

Copyright © 2003 Emu8086, Inc.
Все права защищены.
http://www.emu8086.com


Уроки Ассемблера 8086 для начинающих (Часть 1)

Перевод: Поляков А.В. http://www.avprog.narod.ru

Эти уроки предназначены для тех, кто вообще не знаком с Ассемблером, или имеет весьма отдаленное представление о нем. Конечно, если вы имеете опыт программирования на других языках (Basic, C/C++, Pascal...), то это вам очень поможет.

Но даже если вы хорошо знаете Ассемблер, то просмотр этого документа вам не помешает, так как здесь вы можете изучить синтаксис Emu8086.

Подразумевается, что вы имеете некоторое представление о системах счисления (HEX/BIN). Ну а если нет, то рекомендуем вам изучить раздел Системы счисления перед тем, как вы продолжите ознакомление с этим документом.

Что такое язык Ассемблера?

Язык Ассемблера - это язык программирования низкого уровня. Для начала вы должны ознакомиться с общей структурой компьютера, чтобы в дальнейшем понимать, о чем идет речь. Упрощенная модель компьютера:

System bus - системная шина (окрашена желтым цветом) соединяет различные компоненты компьютера.

CPU - центральный процессор - это сердце компьютера. Большинство вычислений происходит в CPU.

RAM - оперативная память (ОЗУ). В оперативную память загружаются программы для выполнения.

Строение CPU

РЕГИСТРЫ ОБЩЕГО НАЗНАЧЕНИЯ

Процессор 8086 имеет 8 регистров общего назначения, каждый регистр имеет имя:
  • AX - регистр-аккумулятор (разделен на два регистра: AH и AL).
  • BX - регистр базового адреса (разделяется на BH / BL).
  • CX - регистр-счетчик (разделяется на CH / CL).
  • DX - регистр данных (разделяется на DH / DL).
  • SI - регистр - индекс источника.
  • DI - регистр - индекс назначения.
  • BP - указатель базы.
  • SP - указатель стека.

Несмотря на имя регистра, программист сам определяет, для каких целей использовать регистры общего назначения. Основное назначение регистра - хранение числа (переменной). Разрядность вышеописанных регистров 16 бит, т.е., например, 0011000000111001b (в двоичной системе) или 12345 в десятичной (человеческой) системе.

4 регистра общего назначения (AX, BX, CX, DX) разделены на две части. К каждой части можно обращаться как к отдельному регистру. Например, если AX=0011000000111001b, то AH=00110000b, а AL=00111001b. Старший байт обозначается буквой "H", а младший байт - буквой "L".

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

СЕГМЕНТНЫЕ РЕГИСТРЫ

  • CS - указывает на сегмент, содержащий начальный адрес текущей программы.
  • DS - обычно указывает на начальный адрес сегмента данных (переменных).
  • ES - дополнительный сегментный регистр.
  • SS - содержит начальный адрес сегмента стека.
Хотя в сегментных регистрах можно хранить любые данные, делать это неразумно. Сегментные регистры имеют строго определенное назначение - обеспечение доступа к блокам памяти.

Сегментные регистры работают совместно с регистрами общего назначения для доступа к памяти. Например, если мы хотим получить доступ к памяти с физическим адресом 12345h (в шестнадцатиричном исчислении), мы должны установить DS = 1230h и SI = 0045h. И это правильно, потому что таким образом мы можем получить доступ к памяти, физический адрес которой больше, чем значение, которое может поместиться в одиночном регистре.

Процессор вычисляет физический адрес, умножая значение сегментного регистра на 10h и прибавляя к полученному результату значение регистра общего назначения (1230h * 10h + 45h = 12345h):

Адрес, сформированный с помощью двух регистров, называется реальным адресом.

По умолчанию регистры BX, SI и DI работают с сегментным регистром DS; регистры BP и SP работают с SS.

Другие регистры общего назначения не могут формировать реальный адрес!

Также, хотя BX может формировать реальный адрес, BH и BL не могут!

РЕГИСТРЫ СПЕЦИАЛЬНОГО НАЗНАЧЕНИЯ

  • IP - командный указатель.
  • Флаговый регистр - определяет текущее состояние процессора.

Регистр IP всегда работает совместно с сегментным регистром CS и указывает на выполняемую в данный момент команду.

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

Вообще вы не можете напрямую обращаться к этим регистрам.

>>> Следующая часть >>>


Уроки Ассемблера 8086 для начинающих (Часть 2)

Перевод: Поляков А.В. http://www.avprog.narod.ru

Доступ к памяти

Для доступа к памяти можно использовать следующие четыре регистра: BX, SI, DI, BP.

Комбинируя эти регистры внутри квадратных скобок [ ], мы можем получить доступ к различным местоположениям в памяти. Возможны следующие комбинации (режимы адресации):

[BX + SI]
[BX + DI]
[BP + SI]
[BP + DI]
 [SI]
[DI]
d16 (только переменная-смещение)
[BX]
 [BX + SI] + d8
[BX + DI] + d8
[BP + SI] + d8
[BP + DI] + d8
[SI] + d8
[DI] + d8
[BP] + d8
[BX] + d8
 [BX + SI] + d16
[BX + DI] + d16
[BP + SI] + d16
[BP + DI] + d16
 [SI] + d16
[DI] + d16
[BP] + d16
[BX] + d16

d8 - обозначение для 8-ми битовой подстановки.

d16 - обозначение для 16-ми битовой подстановки.

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

Подстановка может быть как внутри так и вне квадратных скобок ([ ]), компилятор генерирует одинаковый машинный код в обоих случаях.

Подстановка - это величина со знаком, поэтому она может быть как положительной, так и отрицательной.

Обычно компилятор различает d8 и d16 и генерирует требуемый машинный код.

Например, представим что DS = 100, BX = 30, SI = 70.
Следующий способ адресации: [BX + SI] + 25
вычисляется процессором для этого физического адреса: 100 * 16 + 30 + 70 + 25 = 1725.

По умолчанию сегментный регистр DS используется для всех способов адресации, кроме способа, который используется с регистром BP. В последнем случае используют сегментный регистр SS.

Легко запомнить все возможные комбинации с помощью таблицы:

Вы можете формировать все имеющие силу комбинации, взяв по одному пункту из каждого столбца, либо пропустить какой-либо столбец и ничего из него не взять. Как вы можете видеть, BX и BP никогда не идут вместе. SI и DI также не могут быть вместе. Здесь приведен пример имеющего силу способа адресации: [BX+5].

Значение в сегментном регистре (CS, DS, SS, ES) называется "segment (сегмент)",
а значение в регистре общего назначения (BX, SI, DI, BP) называется "offset (смещение)".
Если DS содержит значение 1234h, а SI содержит значение 7890h, то это можно записать как 1234:7890. Физический адрес будет таким: 1234h * 10h + 7890h = 19BD0h.

Чтобы указать компилятору тип данных,
должны использоваться эти префиксы:

BYTE PTR - для байта.
WORD PTR - для слова (два байта).

Например:
BYTE PTR [BX]     ; доступ к байту.
    или
WORD PTR [BX]     ; доступ к слову.
Emu8086 поддерживает короткие префиксы:

b. - для BYTE PTR
w. - для WORD PTR

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

Команда MOV

  • Копирует второй операнд (источник) в первый операнд (приемник).

  • Операнд-источник может быть непосредственным значением, регистром общего назначения или местоположением памяти.

  • Регистр-приемник может быть регистром общего назначения или местоположением памяти.

  • Оба операнда должны иметь одинаковый размер байта или слова.
Эти типы операндов поддерживаются:

MOV регистр, память
MOV память, регистр
MOV регистр, регистр
MOV память, непосредственное значение (число)
MOV регистр, непосредственное значение
регистр: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

память: [BX], [BX+SI+7], переменная, и т.п...

непосредственное значение: 5, -24, 3Fh, 10001101b, и т.п...

Для сегментных регистров поддерживаются только эти типы MOV:

MOV сегментный регистр, память
MOV память, сегментный регистр
MOV регистр, сегментный регистр
MOV сегментный регистр, регистр
сегментный регистр: DS, ES, SS, и только как второй операнд: CS.

регистр: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

память: [BX], [BX+SI+7], переменная и т.п...

Команда MOV не может использоваться для установки значений регистров CS и IP.

Здесь короткая программа, которая демонстрирует использование команды MOV:

#MAKE_COM#        ; команда компилятору для создания СОМ-файла.
ORG 100h          ; директива, необходимая для COM-программы.
MOV AX, 0B800h    ; установить AX в шестнадцатиричное значение B800h.
MOV DS, AX        ; копировать значение из AX в DS.
MOV CL, 'A'       ; установить в CL ASCII-код символа 'A', т.е. 41h.
MOV CH, 01011111b ; установить CH в двоичное значение.
MOV BX, 15Eh      ; установить BX в 15Eh.
MOV [BX], CX      ; копировать содержимое из CX в память с адресом B800:015E
RET               ; вернуться в операционную систему.

Вы можете скопировать и вставить вышеописанную программу в редактор кода Emu8086, и нажать кнопку [Compile and Emulate] (или нажать клавишу F5 на клавиатуре).

Окно эмулятора должно открыться с загруженной программой. Щелкните кнопку [Single Step] (пошаговый режим) и наблюдайте за содержимым регистров.

Как скопировать и вставить:
  1. Выделите техт программы с помощью мыши: щелкните перед текстом и, не отпуская кнопки, продвигайте мышь, пока не будет выделен нужный текст.

  2. Нажмите комбинацию клавиш Ctrl + C для копирования.

  3. Перейдите в редактор кода Emu8086 и нажмите комбинацию клавиш Ctrl + V, чтобы вставить текст.

Как вы могли догадаться, точка с запятой (";") используется для комментариев. Все символы, которые следуют за ";", игнорируются компилятором.

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

Фактически, вышеописанная программа записывает данные непосредственно в видеопамять, так что MOV - это очень мощная инструкция.


Уроки Ассемблера 8086 для начинающих (Часть 3)

Переменные

Перевод: Поляков А.В. http://www.avprog.narod.ru

Переменные хранятся в памяти по определенным адресам. Программисту проще иметь дело именами переменных, чем с адресами в памяти. Например, переменная с именем "var1" будет более понятна в коде программы, чем адрес 5A73:235B, особенно когда количество переменных велико.

Наш компилятор поддерживает два типа переменных: BYTE и WORD.

Синтаксис для объявления переменных:

имя DB значение

имя DW значение

DB - Define Byte - определяет байт.
DW - Define Word - определяет слово.

имя - может быть любой комбинацией букв или цифр, но должно начинаться с буквы. Можно объявлять безымянные переменные, которые имеют адрес, но не имеют имени.

значение - может быть любой числовой величиной, которая представлена в какой-либо из поддерживаемых систем счисления (шестнадцатиричной, двоичной или десятичной). Значение может также быть симолом "?" для не инициализированных переменных.

Как вы уже знаете из части 2 этих уроков, команда MOV используется для копирования значения из источника в приемник.

Давайте посмотрим другой пример с командой MOV:

#MAKE_COM#
ORG 100h

MOV AL, var1
MOV BX, var2

RET    ; завершение программы.

VAR1 DB 7
var2 DW 1234h

Скопируйте вышеприведенный код в редактор кода Emu8086 и нажмите клавишу F5, чтобы откомпилировать и загрузить этот код в эмулятор. Вы увидите примерно такую картину:

На рисунке вы можете заметить команды, похожие на те, что используются в нашем примере. Только переменные заменены фактическими местоположениями в памяти. Когда компилятор создает машинный код, он автоматически заменяет имена всех переменных их смещениями. По умолчанию сегмент загружен в регистр DS (в COM-файлах значение регистра DS устанавливается таким же, что и значение в регистре CS - сегменте кода).

В таблице памяти (memory) первый столбец - это смещение, второй столбец - это шестнадцатиричное значение, третий столбец - десятичное значение, а последний столбец - это символ ASCII, соответствующий данному числу.

Компилятор не чувствителен к регистру, поэтому "VAR1" и "var1" - это одно и то же.

Смещение переменной VAR1 - это 0108h, а полный адрес - 0B56:0108.

Смещение переменной var2 - это 0109h, а полный адрес - 0B56:0109. Эта переменная имеет тип WORD, поэтому занимает 2 БАЙТА. Принято младший байт записывать по меньшему адресу, поэтому 34h размещается перед 12h.

Вы можете увидеть некоторые другие инструкции после команды RET. Это случается потому, что дизассемблер не знает, где начинаются данные. Он только обрабатывает значения в памяти и понимает их как имеющие силу инструкции процессора 8086 (мы изучим их позже).

Вы можете даже написать программу, используя только директиву DB:

#MAKE_COM#
ORG 100h

DB 0A0h
DB 08h
DB 01h

DB 8Bh
DB 1Eh
DB 09h
DB 01h

DB 0C3h

DB 7

DB 34h
DB 12h

Скопируйте вышеприведенный код в редактор кода Emu8086 и нажмите клавишу F5, чтобы откомпилировать и загрузить этот код в эмулятор. Вы получите тот же самый дизассемблированный код и тот же самый результат работы программы!

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

ORG 100h - это директива компилятора (она указывает компилятору как обрабатывать исходный код). Эта директива очень важна при работе с переменными. Она указывает компилятору, какой исполняемый файл будет загружаться в смещение (offset) 100h (256 байтов), так что компилятор должен вычислить правильный адрес для всех переменных, когда он размещает имена переменных с их смещениями. Директивы никогда не преобразуются в какой-либо реальный машинный код. Почему исполняемый файл загружается по смещению 100h? Операционная система хранит некоторые данные о программе в первых 256 байтах, начиная от CS (сегмента кода), такие как параметры командной строки и т.д. Все это справедливо только для COM-файлов, файлы EXE загружаются по смещению 0000, и обычно используют специальный сегмент для переменных. Может быть, мы поговорим об EXE-файлах позже.

Массивы

Массив можно рассматривать как цепочку переменных. Текстовая строка - это пример массива байтов, в котором каждый символ представлен значением ASCII-кода (0..255).

Вот некоторые примеры определения массивов:

a DB 48h, 65h, 6Ch, 6Ch, 6Fh, 00h
b DB 'Hello', 0

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

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

MOV AL, a[3]

Вы можете также использовать какой-либо из регистров BX, SI, DI, BP, например:

MOV SI, 3
MOV AL, a[SI]

Если необходимо объявить большой массив, вы можете использовать оператор DUP.

Синтаксис для DUP:

количество DUP ( значение(я) )

количество - количество дубликатов (любая константа).
значение - выражение, которое будет дублироваться оператором DUP.

например:

c DB 5 DUP(9)

а это альтернативный способ объявления:

c DB 9, 9, 9, 9, 9

еще один пример:

d DB 5 DUP(1, 2)

а это альтернативный способ объявления:

d DB 1, 2, 1, 2, 1, 2, 1, 2, 1, 2

Конечно, вы можете использовать DW вместо DB, если требуется хранить числа более 255, или менее -128. DW не может быть использован для объявления строк!

Оператор DUP не может содержать более 1020 знаков в качестве операнда! (в последнем примере 13 знаков). Если вам необходимо объявить очень большой массив, разделите его на две строки (вы получите один большой массив в памяти).

Получение адреса переменной

Есть такая команда LEA (Load Effective Address) и альтернативный оператор OFFSET. Как OFFSET так и LEA могут быть использованы для получения смещения адреса переменной.

LEA более мощная, т.к. она также позволяет вам получить адрес индексированных переменных. Получение адреса переменной может быть очень полезно в различных ситуациях, например, если вам необходимо поместить параметр в процедуру.

Напоминание:
Чтобы указать компилятору тип данных, вы должны использовать следующие префиксы:

BYTE PTR - для байта.
WORD PTR - для слова (два байта).

Например:
BYTE PTR [BX]     ; доступ к байту.
    или
WORD PTR [BX]     ; доступ к слову.
Emu8086 поддерживает короткие префиксы:

b. - для BYTE PTR
w. - для WORD PTR

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

Здесь первый пример:

ORG 100h

MOV    AL, VAR1              ; проверить значение VAR1, поместив ее в AL.

LEA    BX, VAR1              ; записать адрес переменной VAR1 в BX.

MOV    BYTE PTR [BX], 44h    ; изменить содержимое переменной VAR1.

MOV    AL, VAR1              ; проверить значение VAR1, поместив ее в AL.

RET

VAR1   DB  22h

END

Здесь другой пример, который использует OFFSET вместо LEA:

ORG 100h

MOV    AL, VAR1              ; проверить значение VAR1, поместив ее в AL.

MOV    BX, OFFSET VAR1       ; записать адрес переменной VAR1 в BX.

MOV    BYTE PTR [BX], 44h    ; изменить содержимое переменной VAR1.

MOV    AL, VAR1              ; проверить значение VAR1, поместив ее в AL.

RET

VAR1   DB  22h

END

Оба примера функционально идентичны.

Эти строки:

LEA BX, VAR1
MOV BX, OFFSET VAR1

даже компилируются в одинаковый машинный код: MOV BX, num
num - это 16-битовое значение смещения переменной.

Пожалуйста учтите, что только эти регистры могут использоваться внутри квадратных скобок (как указатели памяти):

BX, SI, DI, BP!

(См. предыдущую часть уроков).

Константы

Константы подобны переменным, но они существуют до того, как ваша программа откомпилирована (ассемблирована). После определения константы ее значение не может быть изменено. Для определения константы используется директива EQU:

имя EQU < любое выражение >

Например:

k EQU 5

MOV AX, k

Этот пример функционально идентичен коду:

MOV AX, 5

Вы можете наблюдать переменные во время выполнения программы, если выберите пункт "Variables" в меню "View" эмулятора.

Чтобы наблюдать массивы, вы должны щелкнуть по переменной и установить свойство Elements - размер массива. В Ассемблере нет строгих типов данных, поэтому любые переменные могут быть представлены как массив.

Переменная может быть просмотрена в любой числовой системе:
  • HEX - шестнадцатиричная (основа 16).
  • BIN - двоичная (основа 2).
  • OCT - восмеричная (основа 8).
  • SIGNED - десятичная со знаком (основа 10).
  • UNSIGNED - десятичная без знака (основа 10).
  • CHAR - коды ASCII-символов (всего 256 символов, некоторые символы невидимы).

Вы можете редактировать переменные, когда ваша программа выполняется, просто щелкнув дважды по переменной или выбрать ее и щелкнуть кнопку Edit.

Можно вводить числа в любой системе, шестнадцатиричные цифры должны иметь суффикс "h", двоичные - суффикс "b", восмеричные - суффикс "o", десятичные цифры не требуют суффикса. Строка может быть введена следующим способом:

'hello world', 0

(эта строка заканчивается нулем).

Массив может быть введен следующим способом:

1, 2, 3, 4, 5

(массив может быть массивом байтов или слов, это зависит от того, выбран ли BYTE или WORD для введенной переменной).

Выражения преобразуются автоматически, например:

если введено это выражение:
5 + 2

оно будет преобразовано в 7 и т.п...


Уроки Ассемблера 8086 для начинающих (Часть 4)

Прерывания

Перевод: Поляков А.В. http://www.avprog.narod.ru

Прерывания можно рассматривать как номер функции. Эти функции делают программирование более легким - вместо написания кода путем печатания символов вы можете просто вызвать прерывание и оно все сделает за вас. Существуют также функции прерываний, которые работают с дисками и другим "железом". Мы называем такие функции программными прерываниями.

Прерывания могут быть также вызваны различными устройствами. Такие прерывания называются аппаратными прерываниями. Но сейчас нас интересуют только программные прерывания.

Чтобы выполнить программное прерывание, используют команду INT, которая имеет очень простой синтаксис:
INT значение
Где значение может быть числом в диапазоне от 0 до 255 (или от 0 до 0FFh), обычно мы будем использовать шестнадцатиричные числа. Вы можете подумать, что имеются только 256 функций, но это не так. Каждое прерывание может иметь подфункции. Чтобы определить подфункцию, в регистр AH нужно записать ее номер перед вызовом прерывания. Каждое прерывание может иметь до 256 подфункций (таким образом мы получаем 256 * 256 = 65536 функций). В основном используется регистр AH, но иногда могут использоваться и другие регистры. Обычно другие регистры используются для записи параметров и данных подфункции.

Следующий пример использует прерывание INT 10h и подфункцию 0Eh, чтобы напечатать сообщение "Hello!". Эта функция выводит символ на экран, перемещая курсор и прокручивая экран по необходимости.

#MAKE_COM#        ; инструкция компилятора для создания СОМ-файла.
ORG    100h

; Подфункция, которую мы используем
; не изменяет регистр АН после завершения,
; так что мы можем использовать его только один раз

MOV    AH, 0Eh    ; выбор подфункции.

; Подфункция INT 10h / 0Eh принимает
; в качестве параметра ASCII-код символа,
; который нужно записывать в регистр AL.

MOV    AL, 'H'    ; ASCII-код: 72
INT    10h        ; напечатать его!

MOV    AL, 'e'    ; ASCII-код: 101
INT    10h        ; напечатать его!

MOV    AL, 'l'    ; ASCII-код: 108
INT    10h        ; напечатать его!

MOV    AL, 'l'    ; ASCII-код: 108
INT    10h        ; напечатать его!

MOV    AL, 'o'    ; ASCII-код: 111
INT    10h        ; напечатать его!

MOV    AL, '!'    ; ASCII-код: 33
INT    10h        ; напечатать его!

RET               ; вернуться в операционную систему.

Скопируйте и вставьте эту программу в редактор кода Emu8086 и нажмите кнопку [Compile and Emulate]. Запустите ее!

См. список поддерживаемых прерываний для более подробной информации о прерываниях.


Уроки Ассемблера 8086 для начинающих (Часть 5)

Библиотека общих функций - emu8086.inc

Перевод: Поляков А.В. http://www.avprog.narod.ru

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

Возможно, что на сегодняшний момент вы не сможете полностью понять содержимое emu8086.inc (расположенной в каталоге Inc), но это не страшно, т.к. вам достаточно понять, для чего все это нужно.

Чтобы использовать какие-либо функции в emu8086.inc, вы должны вписать следующую строку в вашем исходном файле:

include 'emu8086.inc'

emu8086.inc определяет следующие макросы:

  • PUTC char - макрос с одним параметром, печатает ASCII-символ в текущей позиции курсора.

  • GOTOXY col, row - макрос с двумя параметрами, устанавливает позицию курсора.

  • PRINT string - макрос с одним параметром, печатает строку.

  • PRINTN string - макрос с одним параметром, печатает строку. Это тоже самое, что и PRINT, но автоматически добавляется "перевод каретки" в конце строки (аналогично процедуре Writeln в Паскале).

  • CURSOROFF - скрывает текстовый курсор.

  • CURSORON - показывает текстовый курсор.

Чтобы использовать какой-либо из вышеописанных макросов, просто напечатайте его имя в нужном месте вашего кода и, если необходимо, параметры. Например:

include emu8086.inc

ORG    100h

PRINT 'Hello World!'

GOTOXY 10, 5

PUTC 65           ; 65 - это ASCII-код для буквы 'A'
PUTC 'B'

RET               ; вернуться в операционную систему.
END               ; директива для окончания компиляции.

Когда компилятор обрабатывает ваш исходный код, он ищет файл emu8086.inc для объявленных макросов и заменяет имя макроса реальным кодом. Вообще макросы - это относительно небольшие участки кода, частое использование макросов может сделать вашу программу (исполнимый файл) слишком большой (для оптимизации размера лучше использовать процедуры).

emu8086.inc также определяет следующие процедуры:

  • PRINT_STRING - процедура для печати строки с нулевым окончанием с текущей позиции курсора. Получает адрес строки в регистре DS:SI. Чтобы использовать эту процедуру, следует объявить: DEFINE_PRINT_STRING перед директивой END.

  • PTHIS - процедура для печати строки с нулевым окончанием с текущей позиции курсора (как и PRINT_STRING), но получает адрес из стека. Строка с нулевым окончанием должна быть определена только после команды CALL. Например:

    CALL PTHIS
    db 'Hello World!', 0

     Чтобы использовать эту процедуру, следует объявить: DEFINE_PTHIS перед директивой END.

  • GET_STRING - процедура для получения строки с нулевым окончанием от пользователя. Принятая строка записана в буфер, адрес которого указан в DS:DI, размер буфера должен быть в DX. Процедура завершает ввод, если нажата клавиша 'Enter'. Чтобы использовать эту процедуру, следует объявить: DEFINE_GET_STRING перед директивой END.

  • CLEAR_SCREEN - процедура для очистки экрана (выполняет полную прокрутку экрана и устанавливает курсор в его верхней части) Чтобы использовать эту процедуру, следует объявить: DEFINE_CLEAR_SCREEN перед директивой END.

  • SCAN_NUM - процедура, которая получает многозначное число СО ЗНАКОМ с клавиатуры, и записывает результат в регистр CX. Чтобы использовать эту процедуру, следует объявить: DEFINE_SCAN_NUM перед директивой END.

  • PRINT_NUM - процедура, которая печатает число со знаком, которое находится в регистре AX. Чтобы использовать эту процедуру, следует объявить: DEFINE_PRINT_NUM перед директивой END.

  • PRINT_NUM_UNS - процедура, которая печатает число без знака из регистра AX. Чтобы использовать эту процедуру, следует объявить: DEFINE_PRINT_NUM_UNS перед директивой END.

Чтобы использовать какую-либо из вышеописанных процедур, вы должны сначала объявить функцию в нижней части вашего файла (но перед END!!), а затем использовать команду CALL, за которой следует имя процедуры. Пример:

include 'emu8086.inc'

ORG    100h

LEA    SI, msg1       ; попросить ввести число
CALL   print_string   ;
CALL   scan_num       ; получить число в CX.

MOV    AX, CX         ; копировать число в AX.

; напечатать следующие строки:
CALL   pthis
DB  13, 10, 'You have entered: ', 0

CALL   print_num      ; напечатать число из AX.

RET                   ; вернуться в операционную систему.

msg1   DB  'Enter the number: ', 0

DEFINE_SCAN_NUM
DEFINE_PRINT_STRING
DEFINE_PRINT_NUM
DEFINE_PRINT_NUM_UNS  ; требуется для print_num.
DEFINE_PTHIS

END                   ; директива окончания компиляции.

Сначала компилятор обрабатывает объявления (это просто обычные макросы, которые рассматриваются до процедур). Когда компилятор получает команду CALL, он заменяет имя процедуры адресом кода, где объявлена процедура. Когда выполнится команда CALL, то управление будет передано процедуре. Это очень удобно: даже если вы вызовите в вашем коде одну и ту же процедуру 100 раз, то вы все-равно будете иметь относительно небольшой размер исполнимого файла. Кажется сложным, не так ли? Но это не страшно. Со временем вы все это изучите, а пока достаточно понимания только основных принципов.


Уроки Ассемблера 8086 для начинающих (Часть 6)

Арифметические и логические команды

Перевод: Поляков А.В. http://www.avprog.narod.ru

Большинство арифметических и логических команд влияют на регистр состояния процессора (или Флаги)

Как вы можете видеть, в этом регистре 16 бит. Каждый бит называется флагом и может принимать значение 1 или 0.

  • Carry Flag (CF) - перенос - этот флаг устанавливается в 1, когдаслучается беззнаковое переполнение. Например, если вы увеличили байт 255 + 1 (результат не помещается в диапазоне 0...255). Если переполнение не происходит, этот флаг установлен в 0.

  • Zero Flag (ZF) - ноль - устанавливается в 1, если результат равен нулю. Если результат не нулевой, то этот флаг устанавливается в 0.

  • Sign Flag (SF) - знак - установлен в 1, если результат - отрицательное число. Если результат положительный, то этот флаг устанавливается в 0. Обычно этот флаг принимает значение старшего значащего бита.

  • Overflow Flag (OF) - переполнение - устанавливается в 1, если случается переполнение при арифметических операциях со знаком. Например, если вы увеличили байт 100 + 50 (результат не помещается в диапазоне -128...127).

  • Parity Flag (PF) - контроль четности - этот флаг устанавливается в 1, если в младших 8-битовых данных четное число. Если число нечетное, то этот бит установлен в 0. Даже если результат - это слово, то анализируются только 8 младших бит!

  • Auxiliary Flag (AF) - внешний перенос - установлен в 1, если случилось переполнение без знака младших 4-х битов (т.е. перенос из 3-го бита).

  • Interrupt enable Flag (IF) - прерывание - если этот флаг установлен в 1, то процессор реагирует на прерывание от внешних устройств.

  • Direction Flag (DF) - направление - этот флаг используется некоторыми командами для обработки цепочки данных. Если флаг установлен в 0 - обработка происходит в прямом направлении, если 1 - в обратном.

Имеются три группы команд.

Первая группа: ADD, SUB,CMP, AND, TEST, OR, XOR

Эти типы операндов поддерживаются:

REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

memory: [BX], [BX+SI+7], переменная, и т.п...

immediate: 5, -24, 3Fh, 10001101b, и т.п...

После операции между операндами результат всегда записывается в первый операнд. Команды CMP и TEST воздействуют только на флаги и не записывают результат (эта команда используется для принятия решения во время выполнения программы).

Эти команды влияют только на флаги:
CF, ZF, SF, OF, PF, AF.

  • ADD - Прибавить второй операнд к первому.

  • SUB - Вычесть второй операнд из первого.

  • CMP - Вычесть второй операнд из первого только для флагов.

  • AND - Логическое И между всеми битами двух операндов. При этом соблюдаются правила:
    1 AND 1 = 1
    1 AND 0 = 0
    0 AND 1 = 0
    0 AND 0 = 0
    Как видите, мы получаем 1 только в том случае, если оба бита равны 1.

  • TEST - То же самое, что AND, но только для флагов.

  • OR - Логическое ИЛИ между всеми битами двух операндов. При этом соблюдаются правила:
    1 OR 1 = 1
    1 OR 0 = 1
    0 OR 1 = 1
    0 OR 0 = 0
    Как видите, мы получаем 1 каждый раз, когда хотя бы один бит равен 1.

  • XOR - Логическое XOR (исключающее ИЛИ) между всеми битами двух операндов. При этом соблюдаются правила:
    1 XOR 1 = 0
    1 XOR 0 = 1
    0 XOR 1 = 1
    0 XOR 0 = 0
    Как видите, мы получаем 1 каждый раз, когда биты имеют различное значение.

Вторая группа: MUL, IMUL, DIV, IDIV

Эти типы операндов поддерживаются:

REG
memory
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

memory: [BX], [BX+SI+7], переменная, и т.п...

Команды MUL и IMUL влияют только на эти флаги:
CF, OF
Если результат превышает размер операнда, то эти флаги установлены в 1, если результат умещается в размер операнда, то эти флаги установлены в 0.

Для команд DIV и IDIV флаги не определены.

  • MUL - беззнаковое умножение:
    если операнд - это байт:
    AX = AL * операнд.
    если операнд - это слово:
    (DX AX) = AX * операнд.
  • IMUL - умножение со знаком:
    если операнд - это байт:
    AX = AL * операнд.
    если операнд - это слово:
    (DX AX) = AX * операнд.
  • DIV - беззнаковое деление:
    если операнд - это байт:
    AL = AX / операнд
    AH = остаток (модуль).     .
    если операнд - это слово:
    AX = (DX AX) / операнд
    DX = остаток (модуль).     .
  • IDIV - деление со знаком:
    если операнд - это байт:
    AL = AX / операнд
    AH = остаток (модуль).     .
    если операнд - это слово:
    AX = (DX AX) / операнд
    DX = остаток (модуль).     .

Третья группа: INC, DEC, NOT, NEG

Эти типы операндов поддерживаются:

REG
memory
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

memory: [BX], [BX+SI+7], переменная, и т.п...

Команды INC и DEC влияют только на эти флаги:
ZF, SF, OF, PF, AF.

Команда NOT не влияет ни на какие флаги!

Команда NEG влияет только на эти флаги:
CF, ZF, SF, OF, PF, AF.

  • NOT - инвертирование каждого бита операнда.

  • NEG - Меняет знак операнда (дополнение до двух). Обычно она инвертирует каждый бит операнда, а затем прибавляет к нему единицу. Например, 5 преобразуется в -5, а -2 преобразуется в 2.


Уроки Ассемблера 8086 для начинающих (Часть 7)

Управление ходом программы

Перевод: Поляков А.В. http://www.avprog.narod.ru

Управление ходом программы - очень важная вещь. Это то, что заставляет программу принимать решения, в зависимости от некоторых условий.

  • Безусловные переходы

    Основная команда, которая передает управление в другую точку программы - это JMP.

    Основной синтаксис команды JMP:
    JMP метка
    Чтобы объявить метку в вашей программе, просто напечатайте ее имя и в конце добавьте двоеточие ":". Метка может быть любой комбинацией символов, но не должна начинаться с цифры. Например, ниже представлены три правильных объявления меток:
    label1:
    label2:
    a:
    Метка может быть объявлена на отдельной строке или перед любой другой командой, например:
    x1:
    MOV AX, 1

    x2: MOV AX, 2
    Здесь представлен пример команды JMP:

ORG    100h

MOV    AX, 5          ; записать в AX число 5.
MOV    BX, 2          ; записать в ВX число 5.

JMP    calc           ; перейти к 'calc'.

back:  JMP stop       ; перейти к 'stop'.

calc:
ADD    AX, BX         ; прибавить BX к AX.
JMP    back           ; перейти к 'back'.

stop:

RET                   ; вернуться в операционную систему.

END                   ; директива для прекращения компиляции.

    Конечно, есть более простой путь для вычисления результата с двумя числами, но это хороший пример применения команды JMP.
    Как вы можете видеть из этого примера, команда JMP может передавать управление другому участку программы, который может находиться как после этой команды, так и перед ней. Этот переход может быть осуществлен в пределах текущего сегмента кода (65,535 байтов).

  • Короткие условные переходы

    Подобно команде JMP, которая выполняет безусловный переход, существуют команды, которые осуществляют условный переход (переход, который осуществляется только в том случае, если выполняется определенное условие). Эти команды разделяются на три группы. Первая группа только проверяет отдельный флаг, вторая - сравнивает числа со знаком, третья - сравнивает числа без знака.

    Команды перехода, проверяющие одиночный флаг

    Команда | Описание | Условие | Обратная команда
    JZ , JE | Переход, если "равно" ("нуль"). Т.е. если сравниваемые значения равны, то ZF = 1 и переход выполняется |  ZF = 1 | JNZ, JNE
    JC , JB, JNAE | Переход, если есть перенос ("ниже", "не выше или равно"). |  CF = 1 | JNC, JNB, JAE
    JS | Переход по знаку. |  SF = 1 | JNS
    JO | Переход по переполнению. |  OF = 1 | JNO
    JPE, JP | Переход, если есть паритет или паритет четный. |  PF = 1 | JPO
    JNZ , JNE | Переход по "не равно" или по "не нуль" |  ZF = 0 | JZ, JE
    JNC , JNB, JAE | Переход, если нет переноса ("выше или равно" или "не ниже"). |  CF = 0 | JC, JB, JNAE
    JNS | Переход, если нет знака. |  SF = 0 | JS
    JNO | Переход, если нет переполнения. |  OF = 0 | JO
    JPO, JNP | Переход, если нет паритета или паритет нечетный. |  PF = 0 | JPE, JP

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

    Команды перехода для чисел со знаками

    Команда Описание Условие Обратная команда
    JE , JZ Переход, если "равно" (=).
    переход, если "ноль".     		ZF = 1 JNE, JNZ
    JNE , JNZ Переход, если "не равно" (<>).
    Переход, если "не ноль".     		ZF = 0 JE, JZ
    JG , JNLE Переход, если "больше" (>).
    Переход, если "не меньше или равно" (not <=).     		ZF = 0
    and
    SF = OF     		JNG, JLE
    JL , JNGE Переход, если "меньше" (<).
    Переход, если "не больше или равно" (not >=).     		SF <> OF JNL, JGE
    JGE , JNL Переход, если "больше или равно" (>=).
    Переход, если "не меньше" (not <).     		SF = OF JNGE, JL
    JLE , JNG Переход, если "меньше или равно" (<=).
    Переход, если "не больше" (not >).     		ZF = 1
    or
    SF <> OF     		JNLE, JG

    <> - этот знак означает "не равно".

    Команды перехода для чисел без знаков

    Команда Описание Условие Обратная команда
    JE , JZ Переход, если "равно" (=).
    Переход, если "ноль".     		ZF = 1 JNE, JNZ
    JNE , JNZ Переход, если "не равно" (<>).
    Переход, если "не ноль".     		ZF = 0 JE, JZ
    JA , JNBE Переход, если "выше" (>).
    Переход, если "не ниже или равно" (not <=).     		CF = 0
    and
    ZF = 0     		JNA, JBE
    JB , JNAE, JC Переход, если "ниже" (<).
    Переход, если "не выше или равно" (not >=).
    Переход по переносу.     		CF = 1 JNB, JAE, JNC
    JAE , JNB, JNC Переход, если "выше или равно" (>=).
    Переход, если "не ниже" (not <).
    Переход, если "нет переноса".     		CF = 0 JNAE, JB
    JBE , JNA Переход, если "ниже или равно" (<=).
    Переход, если "не выше" (not >).     		CF = 1
    or
    ZF = 1     		JNBE, JA

    Обычно, если требуется сравнить два числовых значения, то используют команду CMP (она делает то же самое, что и команда SUB (вычитание), но не сохраняет результат, а влияет только на флаги.

    Логика очень простая, например:

    требуется сравнить числа 5 и 2,

    5 - 2 = 3

    результат - НЕ НОЛЬ (Флаг Нуля - Zero Flag (ZF) установлен в 0).

    Другой пример:

    требуется сравнить 7 и 7,

    7 - 7 = 0

    результат - НОЛЬ! (Флаг Нуля - Zero Flag (ZF) установлен в 1 и команды JZ или JE выполнят переход).

    Ниже приведен пример команды CMP и условного перехода:

include emu8086.inc

ORG    100h

MOV    AL, 25     ; записать в AL число 25.
MOV    BL, 10     ; записать в BL число 10.

CMP    AL, BL     ; сравнить AL с BL.

JE     equal      ; если AL = BL (ZF = 1), то перейти к метке equal.

PUTC   'N'        ; иначе, если AL <> BL, то продолжить выполнение
JMP    stop       ; программы - напечатать 'N' и перейти к метке stop.

equal:            ; если программа на этой метке,
PUTC   'Y'        ; то AL = BL, поэтому выводим на экран 'Y'.

stop:

RET               ; сюда приходим в любом случае

END

    Попробуйте вышеописанный пример с различными числами в AL и BL, откройте флаги, щелкнув по кнопке [FLAGS]. Используйте [Single Step - пошаговый режим] и наблюдайте за происходящим. Не забывайте перекомпилировать и перезагружать вашу программу после кажого сделанного вами в ней изменения (используйте клавишу F5).

    Все условные переходы имеют одно серьезное ограничение - в отличие от команды JMP, они могут выполнять переход только на 127 байтов вперед или на 128 байтов назад (учтите, что большие команды ассемблируются в 3 и более байтов).

    Мы можем легко преодолеть это ограничение, используя следующий метод:

    • Взять обратную команду из приведенной выше таблицы и выполнить переход к метке label_x.

    • Использовать команду JMP для перехода к нужному участку программы.

    • Определить метку label_x: только после команды JMP.

    label_x: - может быть любым именем.

    Пример:

include emu8086.inc

ORG    100h

MOV    AL, 25     ; записать в AL число 25.
MOV    BL, 10     ; записать в BL число 10.

CMP    AL, BL     ; сравнить AL с BL.

JNE    not_equal  ; переход, если AL <> BL (ZF = 0).
JMP    equal
not_equal:

; представим, что здесь у нас
; размещается код, который ассмеблируется
; более чем в 127 байтов...

PUTC   'N'        ; если мы оказались здесь, то AL <> BL,
JMP    stop       ; тогда печатаем 'N', и переходим к метке stop.

equal:            ; если мы оказались здесь,
PUTC   'Y'        ; то AL = BL, тогда печатаем 'Y'.

stop:

RET               ; сюда приходим в любом случае.

END

Другой, реже используемый метод, представляет собой применение непосредственного значения (адреса) вместо метки. Если непосредственное значение начинается с символа '$', то выполняется относительный переход - переход относительно текущего адреса. Компилятор вычисляет команду, которая находится по заданному смещению, и выполняет переход непосредственно к ней. Например:

ORG    100h

; безусловный переход вперед:
; пропускаем следующие два байта,
JMP $2
a DB 3    ; 1 байт.
b DB 4    ; 1 байт.

; переход назад на 7 байтов, если BL <> 0:
; (Команда JMP занимает 2 байта)
MOV BL,9
DEC BL      ; 2 байта.
CMP BL, 0   ; 3 байта.
JNE $-7

RET

END


Уроки Ассемблера 8086 для начинающих (Часть 8)

Процедуры

Перевод: Поляков А.В. http://www.avprog.narod.ru

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

Синтаксис для объявления процедуры:
имя PROC

      ; здесь находится
      ; код процедуры ...

RET
имя ENDP

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

Возможно, вы уже знаете, что команда RET используется для возвращения в операционную систему. Эта же команда используется для возвращения из процедуры (фактически операционная система воспринимает вашу программу, как специальную процедуру).

PROC и ENDP - это директивы компилятора, поэтому они не ассемблируются в какой-либо реальный машинный код. Компилятор только запоминает адрес процедуры.

Команда CALL используется для вызова процедуры.

Пример:

ORG    100h

CALL   m1

MOV    AX, 2

RET                   ; вернуться в операционную систему.

m1     PROC
MOV    BX, 5
RET                   ; вернуться в программу, из которой была вызвана.
m1     ENDP

END

Вышеописанный пример вызывает процедуру m1, которая выполняет команду MOV BX, 5. После окончания процедуры, программа выполняет команду, следующую после команды CALL, т.е. команду: MOV AX, 2.

Есть несколько способов передачи параметров процедуре. Самый простой из них - использование регистров. Здесь представлен пример процедуры, которая принимает два параметра в регистрах AL и BL, умножает их и возвращает результат в регистр AX:

ORG    100h

MOV    AL, 1
MOV    BL, 2

CALL   m2
CALL   m2
CALL   m2
CALL   m2

RET                   ; вернуться в операционную систему.

m2     PROC
MUL    BL             ; AX = AL * BL.
RET                   ; вернуться в вызвавшую программу.
m2     ENDP

END

В этом примере значение регистра AL изменяется каждый раз при вызове процедуры, а состояние регистра BL не изменяется. Таким образом мы получили алгоритм вычисления числа 2 в 4-ой степени.
В результате в регистре AX будет число 16 (или 10h).

Здесь дан другой пример, в котором используется процедура для вывода на экран сообщения Hello World!:

ORG    100h

LEA    SI, msg        ; загрузить адрес msg в регистр SI.

CALL   print_me

RET                   ; вернуться в ОС.

; ==========================================================
; эта процедура печатает строку, строка должна оканчиваться
; нулем (иметь ноль в конце), адрес строки дожен быть в
; регистреhe SI:
print_me     PROC

next_char:
    CMP  b.[SI], 0    ; проверить регистр SI, если он
    JE   stop         ; равен 0, перейти к метке stop

    MOV  AL, [SI]     ; иначе получить ASCII-символ.

    MOV  AH, 0Eh      ; номер функции для печати символа.
    INT  10h          ; используем прерывание для печати
                      ; символа из AL.

    ADD  SI, 1        ; увеличить индекс строкового массива

    JMP  next_char    ; вернуться и напечатать другой символ

stop:
RET                   ; вернуться в программу.
print_me     ENDP
; ==========================================================

msg    DB  'Hello World!', 0   ; строка с нулевым окончанием

END

"b." - префикс перед [SI] означает, что нам необходимо сравнивать байты, а не слова. Если вы хотите сравнить слова, добавьте префикс "w." вместо "b.". Если один из сравниваемых операторов - регистр, то вставлять префиксы не требуется, так как компилятор знает размер каждого регистра.


Уроки Ассемблера 8086 для начинающих (Часть 9)

Стек

Перевод: Поляков А.В. http://www.avprog.narod.ru

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

Вы можете использовать стек для хранения любых данных. Для работы со стеком имеются две команды:

PUSH - записывает 16-ти битовое значение в стек.

POP - получает 16-ти битовое значение из стека.

Синтаксис для команды PUSH:

PUSH REG
PUSH SREG
PUSH memory
PUSH immediate
REG: AX, BX, CX, DX, DI, SI, BP, SP.

SREG: DS, ES, SS, CS.

memory: [BX], [BX+SI+7], 16-ти битовая переменная и т.п...

immediate: 5, -24, 3Fh, 10001101b, и т.п...

Синтаксис для команды POP:

POP REG
POP SREG
POP memory
REG: AX, BX, CX, DX, DI, SI, BP, SP.

SREG: DS, ES, SS, (кроме CS).

memory: [BX], [BX+SI+7], 16-ти битовая переменная и т.п...

Примечания:
  • Команды PUSH и POP работают только с 16-ти битовыми значениями!

  • Примечание: PUSH immediate работает только на процессорах 80186 и выше!

Стек использует алгоритм LIFO (Last In First Out - Последним пришел - первым ушел), это значит, что если мы поместим эти значения одно за другим в стек:

1, 2, 3, 4, 5

то первым значением, которое мы можем получить из стека, будет 5, затем 4, 3, 2 и только потом 1.

Очень важно применять равное количество команд PUSH и POP, иначе стек может быть нарушен и невозможно будет вернуться в операционную систему. Как вы уже знаете, мы используем команду RET для возвращения в операционную систему. Когда программа запускается, ее адрес записывается в стек (обычно это 0000h).

Команды PUSH и POP чрезвычайно полезны, т.к. для хранения данных обычно недостаточно только регистров. Вот выход из ситуации:
  • Записать значение регистра в стек (используя PUSH).

  • Использовать этот регистр в своих целях.

  • Восстановить предыдущее значение регистра из стека (используя POP).

Пример:

ORG    100h

MOV    AX, 1234h
PUSH   AX          ; записать значение из AX в стек.

MOV    AX, 5678h   ; изменить значение регистра AX.

POP    AX          ; восстановить первоначальное значение AX.

RET

END

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

ORG    100h

MOV    AX, 1212h   ; записать в АХ число 1212h.
MOV    BX, 3434h   ; записать в ВХ число 3434h.

PUSH   AX          ; записать значение AX в стек.
PUSH   BX          ; записать значение ВX в стек.

POP    AX          ; установить в AX значение BX.
POP    BX          ; установить в ВX значение АX.

RET

END

Обмен данными происходит потому, что стек использует алгоритм LIFO (Последним пришел - первым вышел), поэтому когда мы помещаем в стек число 1212h, а затем - 3434h, то при обращении к стеку мы сначала получим число 3434h, и только потом - 1212h.

Область памяти стека устанавливается при помощи регистров SS (Stack Segment - сегмент стека) и SP (Stack Pointer - указатель стека). Обычно операционная система устанавливает значения этих регистров на начало программы.

Команда "PUSH источник" делает следующее:
  • Вычитает 2 из регистра SP.

  • Записывает значение источника по адресу SS:SP.

Команда "POP приемник" делает следующее:
  • Записывает данные, размещенные по адресу SS:SP в приемник.

  • Увеличивает на 2 значение регистра SP.

Текущий адрес указателя в SS:SP называется вершиной стека.

Для COM-файлов сегмент стека - это обычно и сегмент кода, а указатель стека установлен в значение 0FFFEh. По адресу SS:0FFFEh записывается адрес возврата для команды RET, которая выполняется в конце программы.

Вы можете наблюдать за работой стека, щелкнув по кнопке [Stack] в окне эмулятора. Вершина стека отмечена знаком "<".


Уроки Ассемблера 8086 для начинающих (Часть 10)

Макросы

Перевод: Поляков А.В. http://www.avprog.narod.ru

Макросы - это те же процедуры, только виртуальные. Макросы подобны процедурам, но они выполняются только во время компиляции. После компиляции все макросы заменяются реальными командами. Если вы объявите макрос и никогда не будете использовать его в вашем коде, компилятор просто проигнорирует его. emu8086.inc содержит хорошие примеры использования макросов. Этот файл содержит несколько макросов, которые делают создание кода более легким.

Определение макроса:

имя    MACRO  [параметры,...]

             <команды>

ENDM

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

MyMacro    MACRO  p1, p2, p3

     MOV AX, p1
     MOV BX, p2
     MOV CX, p3

ENDM

ORG 100h

MyMacro 1, 2, 3

MyMacro 4, 5, DX

RET

Вышеописанный код эквивалентен следующему набору команд:

MOV AX, 00001h
MOV BX, 00002h
MOV CX, 00003h
MOV AX, 00004h
MOV BX, 00005h
MOV CX, DX

Некоторые важные замечания о макросах и процедурах:
  • Если вы хотите использовать процедуру, вы должны применить команду CALL, например:
    CALL MyProc
  • Если вы хотите использовать макрос, вы можете просто напечатать его имя. Пример:
    MyMacro
  • Процедура располагается по определенному адресу в памяти. Если вы воспользуетесь процедурой 100 раз, процессор будет передавать управление в эту часть памяти. Возвращение из процедуры выполняется командой RET. Для хранения адреса возврата используется стек. Команда CALL занимает 3 байта, так что размер исполнимого файла увеличивается очень незначительно, независимо от того, сколько раз вызывается процедура.

  • Макрос выполняется непосредственно в коде программы. Поэтому если вы используете один и тот же макрос 100 раз, то компилятор будет распаковывать этот макрос также 100 раз, делая исполнимый файл все больше и больше, каждый раз вставляя в программу команды макроса.

  • Вы должны использовать стек или регистры общего назначения для передачи параметров процедуре.

  • Для передачи параметров макросу, вам достаточно просто напечатать их после имени макроса. Например:
    MyMacro 1, 2, 3
  • Чтобы обозначить конец макроса, достаточно директивы ENDM.

  • Чтобы обозначить конец процедуры, вы должны напечатать имя процедуры перед директивой ENDP.

Макрос распаковывается непосредственно в коде, поэтому, если имеются метки внутри макроопределения, вы можете получить ошибку "Duplicate declaration" (Двойное объявление), если макрос используется два или более раз. Чтобы избежать такой проблемы, используйте директиву LOCAL, сопровождающую имена переменных, меток или имен процедур. Например:

MyMacro2    MACRO
    LOCAL label1, label2

    CMP  AX, 2
    JE label1
    CMP  AX, 3
    JE label2
    label1:
         INC  AX
    label2:
         ADD  AX, 2
ENDM

ORG 100h

MyMacro2

MyMacro2

RET

Если вы планируете использовать ваши макросы в нескольких программах, то лучше разместить все макросы в отдельном файле. Поместите этот файл в каталог Inc и примените директиву INCLUDE имя файла для использования макросов. Пример такого файла можно найти в разделе Библиотека общих функций - emu8086.inc.


Уроки Ассемблера 8086 для начинающих (Часть 11)

Создание вашей собственной операционной системы

Перевод: Поляков А.В. http://www.avprog.narod.ru

Обычно, когда компьютер стартует, он пытается загрузиться с первого 512-байтового сектора (это Цилиндр 0, Головка 0, Сектор 1) дискеты в дисководе A: в память по адресу 0000h:7C00h и передать ей управление. Если это не удается, то BIOS пытается использовать MBR первого жесткого диска.

Этот урок посвящен загрузке с дискеты. Те же принципы используются при загрузке с жесткого диска. Но использование дискеты имеет несколько преимуществ:
  • Вы можете сохранить существующую операционную систему неповрежденной (Windows, DOS...).

  • Загрузочную запись дискеты легко модифицировать.

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

; директива для создания BOOT-файла:
#MAKE_BOOT#

; Загрузочная запись помещается в 0000:7C00.
; Эту информацию нужно сообщить компилятору:
ORG 7C00h

; загружаем адрес сообщения в регистр SI:
LEA SI, msg

; функция телетайпа:
MOV AH, 0Eh

print:   MOV AL, [SI]
         CMP AL, 0
         JZ done
         INT 10h   ; печать в режиме телетайпа.
         INC SI
         JMP print

; ожидание нажатия любой клавиши:
done:      MOV AH, 0
           INT 16h

; записать волшебное значение в 0040h:0072h:
;   0000h - холодная загрузка.
;   1234h - горячая загрузка.
MOV     AX, 0040h
MOV     DS, AX
MOV     w.[0072h], 0000h ; холодная загрузка.

JMP	0FFFFh:0000h	 ; перезагрузка!

new_line EQU 13, 10

msg DB  'Hello This is My First Boot Program!'
    DB  new_line, 'Press any key to reboot', 0

Скопируйте описанный выше пример в редактор исходного кода Emu8086 и нажмите кнопку [Compile and Emulate]. Эмулятор автоматически загрузит ".boot" файл по адресу 0000h:7C00h.

Вы можете управлять им как обычной программой или использовать меню Virtual Drive -> Write 512 bytes at 7C00h to -> Boot Sector виртуального дисковода (файл FLOPPY_0 в каталоге, где установлен эмулятор). После записи вашей программы в Виртуальный Дисковод, вы можете выбрать Boot from Floppy из меню Virtual Drive.

Из любопытства вы можете записать виртуальную дискету (FLOPPY_0) или ".boot"-файл на реальную дискету и загрузить с нее ваш компьютер. Я рекомендую использовать "RawWrite for Windows" из: http://uranus.it.swin.edu.au/~jn/linux/rawwrite.htm
(с недавнего времени работает под всеми версиями Windows!)

Примечание: Этот .boot-файл не совместим с загрузочным сектором MS-DOS (будет невозможно прочитать или записать данные на вашу дискету, пока вы ее снова не отформатируете). Фактичеки, если вы используете какие-либо "сырые" программы, такие как описанная выше, они так или иначе будут стирать все данные. Так что убедитесь, что ваша дискета не содержит важных данных.

".boot"-файлы имеют ограничение 512 байтов (размер сектора). Если ваша операционная система имеет размер, превышающий это ограничение, то вам придется использовать программу для загрузки из других секторов. Хороший пример маленькой операционной системы можно найти в каталоге "Samples":
micro-os_loader.asm
micro-os_kernel.asm

Для создания расширенной операционной системы (более 512 байтов), вы можете использовать файлы ".bin" (выберите "BIN Template" из меню "File" -> "New").

Чтобы записать файл ".bin" на виртуальный дисковод, выберите "Write .bin file to floppy..." из меню эмулятора "Virtual Drive":

Вы можете также использовать для записи ".boot"-файлов.

Сектор:
Цилиндр: 0
Головка:0
Сектор: 1
это загрузочный сектор!

Упрощенный дисковод и структура дискеты:

Для дискеты на 1440 КБ:

  • Дисковод имеет 2 стороны и 2 головки - по одной на каждую сторону (0..1). Головки дисковода перемещаются над поверхностью диска на каждой стороне.

  • Каждая сторона имеет 80 цилиндров (нумерация 0..79).

  • Каждый цилиндр имеет 18 секторов (1..18).

  • В каждом секторе 512 байтов.

  • Общий размер дискеты: 2 x 80 x 18 x 512 = 1474560 байтов.

Чтобы прочитать секторы с дискеты, используйте INT 13h / AH = 02h.


Уроки Ассемблера 8086 для начинающих (Часть 12)

Управление внешними устройствами

Перевод: Поляков А.В. http://www.avprog.narod.ru

Эмулятор имеет три встроенных виртуальных устройства: светофор, шаговый двигатель и робот. Вы можете посмотреть эти устройства, используя меню эмулятора "Virtual Devices".

Техническую информацию ищите в разделе I/O ports.

Как правило, можно использовать любые процессоры семейства х86 для управления всеми видами устройств, различающихся в основном номерами портов ввода-вывода, которые могут быть изменены с помощью хитрого электронного оборудования. Обычно ".bin"-файл записывается микросхему ПЗУ (Read Only Memory (ROM) - постоянное запоминающее устройство - ПЗУ), система читает программу из этой микросхемы, загружает программу в ОЗУ и выполняет программу. Этот принцип используется для многих современных устройств, таких как микроволновые печи и т.п.

Светофор

Обычно для управления светофором используется массив (таблица) значений. В определенный период времени значение читается из массива и отправляется в порт. Например:

; директива для создания BIN-файла:
#MAKE_BIN#
#CS=500#
#DS=500#
#SS=500#
#SP=FFFF#
#IP=0#

; пропустить таблицу данных:
JMP start

table DW 100001100001b
      DW 110011110011b
      DW 001100001100b
      DW 011110011110b

start:

MOV SI, 0

; установить счетчик на количество
; элементов в таблице:
MOV CX, 4

next_value:

; получить значение из таблицы:
MOV AX, table[SI]

; установить значение в порт ввода-вывода светофора:
OUT 4, AX

; следующее слово:
ADD SI, 2

CALL PAUSE

LOOP next_value

; начать с другого первого значения
JMP start

; ==========================
PAUSE PROC
; сохранить регистры:
PUSH CX
PUSH DX
PUSH AX

; установить интервал (1 миллион микросекунд - 1 секунда):
MOV     CX, 0Fh
MOV     DX, 4240h
MOV     AH, 86h
INT     15h

; восстановить регистры:
POP AX
POP DX
POP CX
RET
PAUSE ENDP
; ==========================

Шаговый двигатель

Двигатель может выполнить "полушаг" при помощи пары магнитов, которые поворачивают ротор двигателя на определенный угол. Затем включается следующая пара магнитов и поворачивает ротор двигателя на следующий "шаг" и т.д.

Двигатель может выполниь полный шаг, если его повернуть парой магнитов, затем другой парой магнитов и в конце - одиночным магнитом и т.п. Лучший способ осуществить полный шаг - это выполнить его как два полушага.

Полушаг - это 11.25 градусов.
Полный шаг - это 22.5 градуса.

Двигатель может вращаться как по часовой стрелке, так и против часовой стрелки.

См. stepper_motor.asm в каталоге Samples.

См. также раздел ссылок Emu8086 I/O ports.

Робот

Полный список набора команд для робота приведен в разделе I/O ports.

Для управления роботом должен быть использован набор алгоритмов для достижения максимальной эффективности. Самым простым, но очень неэффективным является алгоритм случайных перемещений (см. robot.asm в каталоге Samples).

Можно также использовать таблицу данных (как и в светофоре). Это может быть хорошо, если робот всегда работает в одной и той же среде.


Поздравляю! Вы изучили все уроки!

Сейчас вы должны быть готовы для:
  • Полный набор инструкций 8086

Если вы имеете какие-либо идеи, предложения, сообщите нам:
e-mail: info@emu8086.com
Или поищите новую версию здесь:
http://www.emu8086.com

<<< Предыдущая часть <<<


Совместимость с MASM / TASM

Синтаксис Emu8086 полностью совместим со всеми основными ассмеблерами, включая MASM и TASM; кроме нескольких директив, которые имеются только в Emu8086. Если требуется откомпилировать программу с помощью другого ассемблера, то вам нужно будет "закомментировать" или удалить нижеуказанные директивы и любые другие директивы, которые начинаются со знака '#':
#MAKE_COM#
#MAKE_EXE#
#MAKE_BIN#
#MAKE_BOOT#

Emu8086 не поддерживает директиву ASSUME. Практически все программисты считают, что эта директива только усложняет ваш код. Предпочтительнее вручную указывать, какой из сегментных регистров CS:, DS:, ES: или SS:, можно использовать для адресации конкретной ячейки, если сегмент данных хранится не DS. Например:
MOV AX, [BX]        ; то же самое, что и MOV AX, DS:[BX]
MOV AX, ES:[BX]

Emu8086 не требует определять сегмент, если вы компилируете COM-файл, но MASM и TASM могут потребовать это, например:

CSEG    SEGMENT     ; сегмент кода начинается здесь.

; #MAKE_COM#        ; "закомментировано" для Emu8086.

ORG 100h

start:  MOV AL, 5   ; какой-то простой код...
        MOV BL, 2
        XOR AL, BL
        XOR BL, AL
        XOR AL, BL

        RET

CSEG    ENDS        ; сегмент кода заканчивается здесь.

END     start       ; остановить компилятор и установить точку входа.

Точка входа для COM-файла всегда должна быть 0100h (первая команда после директивы ORG 100h). В MASM и TASM вам может потребоваться вручную установить точку входа, используя директиву END. Emu8086 одинаково работает как с этой директивой, так и без нее.

Чтобы проверить вышеописанный код, сохраните его в файл test.asm (или любой другой) и запустите следующие команды из командной строки:

Для MASM 6.0:
  MASM test.asm
  LINK test.obj, test.com,,, /TINY
Для TASM 4.1:
  TASM test.asm
  TLINK test.obj /t
Вы должны получить файл test.com (11 байтов). Щелкните по нему правой кнопкой мыши и выберите Отправить в и emu8086. Вы можете видеть, что дизассемблированный код не содержит каких-либо директив и идентичен коду, сгенерированному с помощью Emu8086 без каких-либо хитрых директив.

Шаблон, используемый Emu8086 для создания EXE-файлов, полностью совместим с MASM и TASM, только следует "закомментировать" директиву #MAKE_EXE#, чтобы избежать сообщения об ошибке Unknown character (неизвестный символ) в строке 11.

EXE-файлы генерируются MASM точно также, как и emu8086. TASM не подсчитывает контрольные суммы, и имеет несколько отличную структуру ЕХЕ-файла, но генерирует точно такой же машинный код.

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

Ассемблер Emu8086 поддерживает укороченные варианты BYTE PTR and WORD PTR - это: B. и W.

Для MASM и TASM вам придется заменить B. и W. на BYTE PTR и WORD PTR соответственно.

Например:
LEA BX, var1
MOV WORD PTR [BX], 1234h ; работает всегда.
MOV w.[BX], 1234h        ; та же команда, но работает только в Emu8086.
HLT

var1  DB  0
var2  DB  0


Компиляция кода Ассемблера

Напечатайте ваш код внутри текстовой области и щелкните кнопку [Compile]. Вас спросят, где сохранить откомпилированный файл. После завершения компиляции вы можете щелкнуть кнопку [Emulate] для загрузки откомпилированного файла в эмулятор.

Директивы, определяющие тип исполнимого файла:

         #MAKE_COM#
         #MAKE_BIN#
         #MAKE_BOOT#
         #MAKE_EXE#
Вы можете вставить эти директивы в исходный код для определения нужного вам типа исполнимого файла. В том случае, если компилятор не найдет ни одной из этих директив, он спросит у вас тип файла перед его созданием.

Описание типов исполнимых файлов:

  • #MAKE_COM# - самый старый и самый простой формат исполнимого файла. Такие файлы загружаются с префиксом 100h (256 байтов). Выберите СОМ Template из меню New, если вы планируете компилировать COM-файл. Директива компилятора ORG 100h должна быть добавлена перед кодом. Выполнение всегда начинается с первого байта файла. Поддерживается командной строкой DOS и Windows.

  • #MAKE_EXE# - более "продвинутый" формат исполнимого файла. Не ограничены размер и количество сегментов. Сегмент стека должен быть определен в программе. Вы можете выбрать EXE Template из меню New для создания простой ЕХЕ-программы с определенными сегментмами Данных, Стека и Кода. Точка входа (где начинается выполнение) определяется программистом. Поддерживается командной строкой DOS и Windows.

  • #MAKE_BIN# - простой исполнимый файл. Вы можете определить значения всех регистров, сегмент и смещение для области памяти, куда этот файл будет загружен. Если загрузить файл "MY.BIN" в эмулятор, он будет виден для файла "MY.BINF" и загрузится файл "MY.BIN" в местоположение, определенное в файле "MY.BINF". Регистры также установятся с учетом информации из этого файла (откройте этот файл в редакторе для изменения или изучения). В том случае, если эмулятор не найдет файл "MY.BINF", будет использоваться текущие значения регистров и файл "MY.BIN" загрузится в текущий CS:IP.
    Выполнение начинается со значения в CS:IP.
    Этот тип файла уникален для Emu8086.

    Файл ".BINF создается автоматически компилятором, если он находит директиву #MAKE_BIN#.
    ПРЕДУПРЕЖДЕНИЕ! если файл ".binf" существует, то он будет перезаписан!

    #LOAD_SEGMENT=1234#
    #LOAD_OFFSET=0000#
    #AL=12#
    #AH=34#
    #BH=00#
    #BL=00#
    #CH=00#
    #CL=00#
    #DH=00#
    #DL=00#
    #DS=0000#
    #ES=0000#
    #SI=0000#
    #DI=0000#
    #BP=0000#
    #CS=1234#
    #IP=0000#
    #SS=0000#
    #SP=0000#

    Значения должны быть шестнадцатиричными!

    Если эти значения не определены, то они устанавливаются по умолчанию:
    LOAD_SEGMENT = 0100
    LOAD_OFFSET = 0000
    CS = ES = SS = DS = 0100
    IP = 0000
    Если LOAD_SEGMENT и LOAD_OFFSET не определены, то используются значения CS и IP, и наоборот.

    Если значение Load to offset не равно нулю (0000), то ORG ????h должна быть добавлена в файл .BIN, где ????h - это смещение для загрузки. Это должно быть сделано для того, чтобы компилятор мог вычислить правильные адреса.

  • #MAKE_BOOT# - эта директива копирует первую дорожку дискеты (загрузочный сектор).
    Вы можете записать загрузочный сектор виртуального дисковода (FLOPPY_0) через меню эмулятора:
    [Virtual Drive] -> [Write 512 bytes at 7C00 to Boot Sector]
    Сначала вы должны откомпилировать ".boot"-файл, а затем загрузить его в эмулятор (см. "micro-os_loader.asm" и "micro-os_kernel.asm" в разделе "Samples").

    Затем выберите в меню [Virtual Drive] -> [Boot from Floppy], чтобы загрузить эмулятор с виртуального дисковода.

    Затем, если вам любопытно, вы можете записать виртуальную дискету на реальную дискету и загрузить с нее ваш компьютер. Я рекомендую использовать "RawWrite for Windows" с: http://uranus.it.swin.edu.au/~jn/linux/rawwrite.htm
    (учтите, что "micro-os_loader.asm" не использует MS-DOS-совместимый загрузочный сектор, так что лучше использовать чистую дискету, хотя она должна быть отформатирована IBM (MS-DOS)).
    Директива компилятора ORG 7C00h должна быть добавлена перед кодом, если компьютер начинает загружаться с первой дорожки дискеты в адрес 0000:7C00.
    Размер .BOOT-файла должен быть менее 512 байтов (ограничен размером сектора дискеты).
    Выполнение всегда начинается с первого байта файла. Этот тип файла уникален для эмулятора Emu8086.

Обработка ошибок

Компилятор выводит отчет об ошибках в отдельном окне:

MOV DS, 100 - это недопустимая команда, потому что в сегментный регистр нельзя устанавливать непосредственное значение - должны использоваться регистры общего назначения:
MOV AX, 100
MOV DS, AX

MOV AL, 300 - это недопустимая команда, т.к. регистр AL имеет только 8 битов, и его максимальное значение 255 (или 11111111b), а минимальное -128.

Компилятор делает несколько проходов перед генерацией правильного машинного кода. Если он находит ошибку и не выполняет требуемое количество проходов, он может выдать неправильное сообщение об ошибке. Например:
#make_COM#
ORG 100h

MOV AX, 0
MOV CX, 5
m1: INC AX
LOOP m1                ; это не настоящая ошибка!

MOV AL, 0FFFFh         ; ошибка здесь.

RET
Список генерируемых ошибок:
(7) Condition Jump out of range (Условие перехода за пределами диапазона)!: LOOP m1
(9) Wrong parameters (Неправильнве параметры): MOV AL, 0FFFFh
(9) Operands do not match (Операнды не соответствуют): Second operand is over 8 bits (Второй операнд более 8 битов)!

Первое сообщение (7) - неправильное. Компилятор не закончил вычисление смещений для меток, поэтому он думает, что смещение метки m1 - это 0000. Этот адрес лежит за пределами диапазона, т.к. мы начинаем со смещения 100h.

Внесите исправления в эту строку: MOV AL, 0FFFFh (AL не может содержать значение 0FFFFh). Это устранит обе ошибки! Например:

#make_COM#
ORG 100h

MOV AX, 0
MOV CX, 5
m1: INC AX
LOOP m1                ; тот же самый код без ошибки!

MOV AL, 0FFh           ; все!

RET

При сохранении компилируемого файла, компилятор также сохраняет 2 других файла, которые используются эмулятором для отображения фактического исходного кода при его исполнении и выборе соответствующей строки.
  • *.~asm - этот файл содержит оригинальный исходный код, который был использован для создания исполнимого файла.

  • *.debug - этот файл содержит информацию, которая позволяет эмулятору выбирать строки оригинального исходного кода во время выполнения машинного кода.

  • *.symbol - Таблица символов. Она содержит информацию, которая позволяет отображать окно "Variables" (Переменные). Это текстовый файл, так что вы можете посмотреть его из текстового редактора.

  • *.binf - этот файл содержит информацию, которая используется эмулятором для загрузки BIN-файла в указанное местоположение, и установки значений регистров предшествующих выполнению (создается только в том случае, если исполнимый файл - это BIN-файл).


Пользовательская таблица памяти

Вы можете определить свою собственную таблицу памяти (отличающуюся от IBM-PC). Для этого требуется создать файл "CUSTOM_MEMORY_MAP.inf" в том же каталоге, где установлена Emu8086.exe. Использование следующего формата добавляет информацию в этот конфигурационный файл:

Адрес - ИмяФайла
...

Например:

0000:0000 - System.bin
F000:0000 - Rom.bin
12AC - Data.dat

Адрес может быть как физическим (без ":"), так и логическим. Значение должно быть в шестнадцатиричной форме. Эмулятор будет искать файл, имя которого указано после символа "-", и загрузит его в память по указанному адресу.

Эмулятор не будет изменять Область системной информации (паямть от 00400h до 00500h), если ваш конфигурационный файл имеет директиву "NO_SYS_INFO" (в отдельной строке). Пример:

NO_SYS_INFO
0000:0000 - System.bin
F000:0000 - Rom.bin
12AC - Data.dat

Эмулятор позволит вам загрузить ".bin"-файлы по любому адресу памяти (будьте осторожны - не загружайте их в вашу системную область или область данных).

Предупреждение! стандартные прерывания не будут работать, если вы измените таблицу памяти и не предусмотрите для них замену. Отменить изменения можно просто удвлив или переименовав файл "CUSTOM_MEMORY_MAP.inf", и перезагрузив программу.

См. также: Глобальная таблица памяти


Редактор исходного кода

Использование мыши

Редактор поддерживает следующие действия мыши:

Действие мыши | Результат
Щелчок левой кнопкой над текстом | Изменение позиции курсора
Щелчок правой кнопкой | Отображение контекстного меню
Нажатие левой кнопки над выделенным участком текста и перетаскивание | Перемещение текста
Ctrl + Нажатие левой кнопки над выделенным участком текста и перетаскивание | Копирование текста
Щелчок левой кнопкой по левому краю | Выделение строки
Нажатие левой кнопки на левом краю и перетаскивание | Выделение нескольких строк
Alt + Нажатие левой кнопки и перетаскивание | Выделение колонки текста
Двойной щелчок левой кнопки над текстом | Выбор слова под курсором
Вращение колеса мыши | Вертикальная прокрутка окна
Щелчок по колесу | Выбор слова под курсором
Двойной щелчок по колесу | Выделение строки под курсором

Горячие клавиши редактора:

Команда                  Сочетание клавиш
=======================================================================
Вставить закладку        Control + F2
Следующая закладка       F2
Предыдущая закладка      Shift + F2

Копирование              Control + C, Control + Insert
Вырезание                Control + X, Shift + Delete, Control + Alt + W
Вырезание строки         Control + Y
Вырезание предложения    Control + Alt + K
Вставка                  Control + V, Shift + Insert

Отмена                   Control + Z, Alt + Backspace

Конец документа          Control + End
Выделение текста от
курсора до конца
документа                Control + Shift + End
Начало документа         Control + Home
Выделение текста от
курсора до начала
документа                Control + Shift + Home

Поиск                    Control + F, Alt + F3
Продолжить поиск         F3
Найти следующее слово    Control + F3
Найти предыдущее         Shift + F3
Найти предыдущее слово   Control + Shift + F3
Найти и заменить         Control + H, Control + Alt + F3
Перейти к строке         Control + G

Выделить все             Control + A
Выделить строку          Control + Alt + F8

Вставить новую строку
выше                     Control + Shift + N

Табуляция                Tab
Отмена табуляции         Shift + Tab

Снятие выделения         Control + Shift + T
Снятие выделения         Control + Shift + Space

Перевод выделения в
верхний регистр          Control + L
Перевод выделения в
нижний регистр           Control + U, Control + Shift + U

Левое слово              Control + Left
Правое слово             Control + Right
Левое предложение        Control + Alt + Left
Правое предложение       Control + Alt + Right

Перепечатывание          Insert
Отобразить пробелы       Control + Alt + T

Прокрутка окна вверх     Control + Down
Прокрутка окна вниз      Control + Up
Прокрутка окна влево     Control + PageUp
Прокрутка окна вправо    Control + PageDown

Удаление слова справа    Control + Delete
Удаление слова слева     Control + Backspace

Выделение символа слева  Shift + Left
Выделение символа справа Shift + Right
Выделение слова слева    Control + Shift + Left
Выделение слова справа   Control + Shift + Right
Выделение строки слева   Shift + Home
Выделение строки справа  Shift + End
Выделение верхней строки Shift + Up
Выделение нижней строки  Shift + Down
Выделение страницы вверх Shift + PgUp
Выделение страницы вниз  Shift + PgDown

Записать макрос          Control + Shift + R
Установить счетчик
повтора                  Control + R

Допустимые правила синтаксиса выражений для поиска и замены

Шаблоны:
        ? (для любых символов),
        + (для одного или более),
        * (для нуля или более).

Наборы символов:
        Символы, заключенные в квадратные скобки,
        трактуются как набор опций.

        Диапазон символов может быть определен
        с помощью дефиса (например, [a-c]).

Логическое ИЛИ:
        Подвыражения могут содержать логическое ИЛИ,
        которое обозначается символом |.

Если появятся проблемы с редактором исходного кода, вы можете вручную скопировать файл "cmax20.ocx" из каталога, где установлена программа, в папку Windows\System или Windows\System32, заменив существующую версию этого файла (после этого может потребоваться перезагрузка компьютера).

Авторское право: 1997-2002 Barry Allyn. Все права защищены.


 Using the emulator

If you want to load your code into the emulator, just click "Emulate" button [image].
But you can also use emulator to load executables even if you don't have the original source code. Select Show emulator from the Emulator menu.

Try loading files from "MyBuild" folder. If there are no files in "MyBuild" folder return to source editor, select Examples from File menu, load any sample, compile it and then load into the emulator:

[Single Step] button executes instructions one by one stopping after each instruction.

[Run] button executes instructions one by one with delay set by step delay between instructions.

Double click on register text-boxes opens "Extended Viewer" window with value of that register converted to all possible forms. You can modify the value of the register directly in this window.

Double click on memory list item opens "Extended Viewer" with WORD value loaded from memory list at selected location. Less significant byte is at lower address: LOW BYTE is loaded from selected position and HIGH BYTE from next memory address. You can modify the value of the memory word directly in the "Extended Viewer" window,

You can modify the values of registers on runtime by typing over the existing values.

[Flags] button allows you to view and modify flags on runtime.

Virtual drives

Emulator supports up to 4 virtual floppy drives. By default there is a FLOPPY_0 file that is an image of a real floppy disk (the size of that file is exactly 1,474,560 bytes).

To add more floppy drives select [Create new floppy drive] from [Virtual drive] menu. Each time you add a floppy drive emulator creates a FLOPPY_1, FLOPPY_2, and FLOPPY_3 files.
Created floppy disks are images of empty IBM/MS-DOS formatted disk images. Only 4 floppy drives are supported (0..3)!
To delete a floppy drive you should close the emulator, delete the required file manually and restart the emulator.

You can determine the number of attached floppy drives using INT 11h this function returns AX register with BIOS equipment list. Bits 7 and 6 define the number of floppy disk drives (minus 1):
Bits 7-6 of AX:
          00 single floppy disk.
          01 two floppy disks.
          10 three floppy disks.
          11 four floppy disks.
Emulator starts counting attached floppy drives from starting from the first, in case file FLOPPY_1 does not exist it stops the check and ignores FLOPPY_2 and FLOPPY_3 files.

To write and read from floppy drive you can use INT 13h function, see list of supported interrupts for more information.

emulator can emulate tiny operating system, check out operating system tutorial.


Документация для Emu8086
  • Как начать?

  • Уроки

  • Ссылки Emu8086

  • Полный набор команд процессора 8086


Порты ввода/вывода

Эмулятор не воспроизводит каие-либо оригинальные порты ввода/вывода. Вместо этого он имеет виртуальные устройства, которые могут быть доступны через команды IN/OUT.

Пользовательские устройства ввода/вывода.

"Emu8086" поддерживает дополнительные устройства, которые могут быть созданы сторонними организациями. Устройство должно быть написано на каком-либо языке программирования, например: Visual Basic, VC++, Delphi. Более подробную информацию и примеры исходного кода ищите в папке DEVICES.

Для пользовательских устройств зарезервированы адреса: от 00000Fh до 0FFFFh (от 15 до 65535). Порт 100 соответствует байту 100 в файле "EmuPort.io", порт 101 - байту 101 и т.п...
(отсчет ведется от нуля).
Файл "EmuPort.io" размещен в каталоге Windows "Temp" (может быть получен с помощью API функции GetTempPath()).

Я буду рад включить разработанные вами устройства в следующий выпуск "Emu8086". Если вы решите поделится своим устройством с другими разработчиками - пожалуйста, вышлите его исходный код нам!

Устройства, которые доступны через меню эмулятора "Virtual Devices".

  • Светофор - Порт 4 (слово)

    Светофор управляется пересылкой данных через порт ввода/вывода 4.
    Имеются 12 ламп: 4 зеленых, 4 желтых и 4 красных.

    Вы можете управлять состоянием каждой лампы, устанавливая эти биты:

    1 - лампа включена.
    0 - лампа выключена.

    Используются только 12 младших битов слова (от 0 до 11), последние биты (от 12 до 15) не используются Пример:

    MOV AX, 0000001011110100b
    OUT 4, AX

    Мы используем в подписях к рисунку шестнадцатиричные цифры желтого цвета (для компактности). Ниже приведена таблица преобразования шестнадцатиричных цифр в десятичные:
    Шест. - Десят.

A   -   10
B   -   11
C   -   12    (не используется)
D   -   13    (не используется)
E   -   14    (не используется)
F   -   15    (не используется)

    Первый операнд для команды OUT - это номер порта (4), второй операнд - это слово (AX), которое записывается в порт. Первый операнд должен быть непосредственным значением в диапазоне (0..255) или регистром DX. Второй операнд должен быть только регистром AX или AL.

    См. также "traffic_lights.asm" в Samples.

    Если нужно, вы можете прочитать данные из порта, используя команду IN, например:

    IN AX, 4

     Первый операнд в команде IN (AX) получает значение из порта, второй операнд (4) - это номер порта. Первый операнд должен быть только регистром AX или AL. Второй операнд должен быть непосредственным значением от 0 до 255 или регистром DX.

  • Шаговый двигатель - Порт 7 (байт)

    Шаговый двигатель управляется посылкой данных в порт ввода-вывода 7.

    Шаговый двигатель - это электромотор, который может точно управляться сигналами от компьютера.

    Двигатель поворачивается на определенный угол каждый раз, когда он принимает сигнал. Изменяя соотношение между продолжительностью импульсов и пауз, можно заставить двигатель вращаться с разной скоростью или повернуть ротор на заданный угол и сотановить двигатель.

    В основе 3-х фазного шагового двигателя лежит конструкция из 3-х магнитов, управляемых битами 0, 1 и 2. Остальные биты (2..7) не используются.

    Когда магнит работает, он становится красным. Стрелка в левом верхнем углу показывает направление последнего движения мотора. Зеленая линия здесь только для того, чтобы видеть реальное вращение.

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

    MOV AL, 001b ; инициализация.
    OUT 7, AL

    MOV AL, 011b ; полушаг 1.
    OUT 7, AL

    MOV AL, 010b ; полушаг 2.
    OUT 7, AL

    MOV AL, 110b ; полушаг 3.
    OUT 7, AL

     Если вы когда-либо играли с магнитами, вы поймете, как это работает. Попробуйте поэксперементировать, или см. "stepper_motor.asm" в Samples.

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

    IN AL, 7

  • Робот - Порт 9 (3 байта)

    Робот управляется посылкой данных в порт ввода-вывода 9.

    Первый байт (Порт 9) - это Командный регистр. Запись данных в этот порт заставляет робота выполнять определенные действия. Поддерживаются следующие значения:

    Десятичное значение | Двоичное значение | Действие
    0 | 00000000 | Бездействие.
    1 | 00000001 | Движение вперед.
    2 | 00000010 | Поворот налево.
    3 | 00000011 | Поворот направо.
    4 | 00000100 | Исследование. Исследует объект перед собой, используя датчик. Если робот завершает задачу, результат устанавливается в Регистр данных и Бит #0 Регистра состояния установлен в 1.
    5 | 00000101 | Включение лампы.
    6 | 00000110 | Выключение лампы.

    Второй байт (порт 10) - это Регистр данных. Этот регистр устанавливается после завершения задачи командой Исследование:

    Десятичное значение | Двоичное значение | Значение
    255 | 11111111 | Стена
    0 | 00000000 | Ничего
    7 | 00000111 | Лампа включена
    8 | 00001000 | Лампа выключена

    Третий байт (порт 11) - это Регистр состояния. Чтение данных из этого порта позволяет определить состояние робота. Каждый бит имеет определенное свойство:

    Номер бита | Описание
    Бит #0 | ноль, если в Регистре данных нет новых данных, единица, если в Регистре данных новые (не прочитанные) данные.
    Бит #1 | ноль, если робот готов принять следующую команду, единица, если робот занят выполнением текущей задачи.
    Бит #2 | ноль, если нет ошибок при выполнении последней команды, единица, если случилась ошибка при выполнении команды (когда робот не может завершить задачу: движение, поворот, исследование, включение или выключение лампы).

    Пример:

MOV AL, 1  ; движение вперед.
OUT 9, AL  ;

MOV AL, 3  ; поворот вправо.
OUT 9, AL  ;

MOV AL, 1  ; движение вперед.
OUT 9, AL  ;

MOV AL, 2  ; поворот влево.
OUT 9, AL  ;

MOV AL, 1  ; движение вперед.
OUT 9, AL  ;

    Примите во внимание, что робот - это механическое создание, поэтому для выполнения команд ему требуется какое-то время. Вы должны всегда проверять бит#1 Регистра состояния, прежде чем записать данные в порт 9, иначе робот откажется выполнять вашу команду и вы увидите надпись "BUSY!". См. "robot.asm" в Samples.

    Создание пользовательсокой Карты мира робота

    Вы можете создать любую карту для робота, используя панель инструментов (см. рис.).

    Если вы выберите робота и разместите его над существующим роботом, он повернется на 90 градусов по часовой стрелке. Чтобы передвинуть робота на другое место, щелкните по нему, затем щелкните в нужном месте карты.

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

    Если разместить стену над существующей стеной, то стена будет удалена.

    Данная версия работает только с одним роботом. Если вы забудете разместить робота на карте, то он будет помещен туда автоматически в случайном месте.

    Карта автоматически сохраняется после выхода.

    Щелчок правой кнопкой по карте вызовет меню, которое позволяет включать/выключать все лампы.


Глобальная таблица памяти

Процессор 8086 может иметь доступ до 1 MB оперативной паямти (ОЗУ - RAM). Это ограничение связано с сегментной организацией памяти. Так как сегментные регистры (CS, SS, ES, DS) могут хранить максимальные значения 0FFFFh и регистры смещения (IP, BX, SI, DI, BP, SP) также могут хранить максимальное значение 0FFFFh, то наибольший адрес памяти может быть FFFF:FFFF или физический адрес: 0FFFFh * 10h + 0FFFFh = 10FFEFh = 65535 * 16 + 65535 = 1114095 байтов
Современные процессоры имеют регистры большего размера, поэтому они могут иметь доступ к горазо большему объему памяти, но принцип доступа остается тем же самым.

Таблица памяти эмулятора (и типичного IBM PC):

Физический адрес в области памяти (шестнадцатиричный) | Краткое описание
00000 - 00400 | Векторы прерываний. Эмулятор загружает файл "INT_VECT" в физический адрес 00000h.
00400 - 00500 | Область системной информации. Мы используем маленькую хитрость для установки некоторых параметров, загружая маленькую последнюю часть (21 байт) файла "INT_VECT" в эту область (размер этого файла 1045 или 415h байтов, поэтому загруженный файл занимает память от 00000 to 00415h). Этот блок памяти модернизируется эмулятором, если конфигурация изменяется (см. Область системной информации).
00500 - A0000 | Свободная область памяти. Блок размером 654080 байтов. Сюда вы можете загрузить свою программу.
A0000 - B1000 | Видеопамять для VGA, Monochrome и других адаптеров. Не используется эмулятором!
B1000 - B8000 | Зарезервировано. Не используется эмулятором!
B8000 - C0000 | 32 KB видеопамяти для Color Graphics Adapter (CGA). Эмулятор использует эту область памяти для хранения 8-ми страниц видеопамяти. Размер экрана эмулятора может быть изменен, тогда для каждой страницы будет требоваться меньше памяти. Хотя эмулятор всегда использует 1000h (4096 байтов) для каждой страницы (см. INT 10h / AH=05h в списке поддерживаемых прерываний).
C0000 - F4000 | Зарезервировано.
F4000 - 10FFEF | ROM BIOS и расширения. Эмулятор загружает файл "BIOS_ROM" в физический адрес 0F4000h. Таблица прерываний указана в этой области памяти для получения эмуляции функций прерываний.

Векторы прерываний (память от 00000h до 00400h)

Номер            Адрес в таблице         Адрес подпрограммы
прерывания       векторов прерываний     BIOS
(hex)

00               00x4 = 00               F400:0170 - генерируется процессором,
                                                     ошибка деления.

04               04x4 = 10               F400:0180 - генерируется процессором,
                                                     обнаружение
                                                     переполнения INTO.

10               10x4 = 40               F400:0190 - Видеофункции.

11               11x4 = 44               F400:01D0 - Получение списка
                                                     оборудования BIOS.

12               12x4 = 48               F400:01A0 - Получение размера памяти.

13               13x4 = 4C               F400:01B0 - Дисковые функции.

15               15x4 = 54               F400:01E0 - Функции BIOS.

16               16x4 = 58               F400:01C0 - Функции клавиатуры.

19               19x4 = 64               FFFF:0000 - Перезагрузка.

1A               1Ax4 = 68               F400:0160 - Функции времени.

1E               1Ex4 = 78               F400:AFC7 - Вектор параметров дискеты.

20               20x4 = 80               F400:0150 - Функция DOS:
                                                     завершение программы.

21               21x4 = 84               F400:0200 - Функция DOS.

все другие       ??x4 = ??               F400:0100 - Перехватчик прерывания по умолчанию.
                                                     Печатает сообщение
                                                     "Interupt not supported yet"
                                                     ("Прерывание не поддерживается").
Вызов подсистемы BIOS дизассемблируется при "BIOS DI" (он не использует регистр DI каким-либо способом, это только способ декодирования: мы используем "FF /7" для такого декодирования, "FFFFCD10" используется, чтобы эмулятор мог эмулировать прерывание 10h).

F400:0100 имеет этот код FFFFCDFF (декодированный как INT 255, и генерируется сообщение об ошибке).

Область системной информации (память от 00400h до 00500h)

Адрес (hex) Размер Описание
0040h:0010 слово Список оборудования BIOS.

Состояние битов зависит от обнаруженного BIOS оборудования:
Бит(ы)	Описание
 15-14  количество параллельных портов.
 13     не поддерживается.
 12     игровой порт установлен.
 11-9   количество последовательных портов.
 8      зарезервировано.
 7-6    количество дисководов (минус 1):
          00 один дисковод;
          01 два дисковода;
          10 три дисковода;
          11 четыре дисковода.
 5-4    видеорежим:
          00 EGA,VGA,PGA, или другой встроенный видео BIOS;
          01 40x25 CGA цветной;
          10 80x25 CGA цветной (по умолчанию в эмуляторе);
          11 80x25 монохромный текстовый.
 3    не поддерживается.
 2    не поддерживается.
 1    математический сопроцессор установлен.
 0    установлен, если загрузка происходит с дискеты
      (всегда установлен в эмуляторе).
Это слово также возвращается в AX прерыванием INT 11h.
Значение по умолчанию: 0021h или 0000 0000 0010 0001b
0040h:0013 слово Общая память (в килобайтах) начиная от абсолютного адреса 00000h.
Это слово также возвращается в AX прерыванием INT 12h.
Это значение установлено: 0280h (640KB).
0040h:004A слово Количество столбцов на экране.
Значение по умолчанию: 0032h (50 столбцов).
0040h:004E слово Начальный адрес текущей видеостраницы в видеопамяти (после 0B800:0000).
Значени по умолчанию: 0000h.
0040h:0050 8 слов Содержит позицию курсора (горизонтальную и вертикальную координаты) на каждой из 8-ми страниц.
Значение по умолчанию: 0000h (для всех 8-ми слов).
0040h:0062 байт Номер текущей видеостраницы.
Значение по умолчанию: 00h (1-я страница).
0040h:0084 байт Количество строк на экране минус 1.
Значение по умолчанию: 13h (19+1=20 строк).

См. также: Пользовательская таблица памяти


 | Разделы помощи | Общие сведения | Уроки | Ссылки Emu8086 |


Системы счисления

Что это такое?

Есть много способов для представления одного и того же числового значения. В давние времена люди использовали палочки для подсчета. Позднее научились рисовать палочки на земле и в конечном счете на бумаге. Так что число 5 вначале представлялось как: | | | | | (пять палочек).

Еще позднее римляне начали использовать различные символы для большого количества палочек: | | | означало три палочки, а V означало пять палочек, и наконец X использовалось для представления 10-ти палочек!

Использование палочек для счета было хорошей идеей для того времени. А использование символов вместо реальных палочек было еще лучше. Одним из наиболее удобных современных способов представления чисел является десятичная система. Почему? Потому что она использует основное свое достижение - идею использования символа для подсчета ничего. Около 1500 лет назад в Индии ноль (0) был впервые использован как число! Позднее он был использован на Ближнем Востоке как арабский sifr. И наконец, представлен на западе как латинский zephiro. Вскоре вы увидите, какое большое значение имеет эта идея во всех современных системах счисления.

Десятичная система

Большинство людей сегодня используют десятичное представление числа. В десятичной системе 10 цифр:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Этими цифрами можно представить любое значение, например:

754.

Значение формируется путем суммирования всех цифр, умноженных на основание (в нашем случае основание равно 10, т.к. в десятичной системе 10 цифр) в степени, равной позиции цифры (отсчет ведется с нуля):

Позиция каждой цифры - очень важный фактор! Например, если вы поместите "7" в конец:

547

то это будет уже другое значение:

Важное замечание: любое число в нулевой степени равно единице, даже ноль в нулевой степени равен 1:

Двоичная система

Компьютеры не такие умные, как люди (во всяком случае пока). Легко сделать электронную машину с двумя состояниями: включено и выключено, или 1 и 0.
Компьютеры используют двоичную систему, которая использует всего две цифры:

0, 1

И поэтому основание в двоичной системе равно 2.

Каждая цифра в двоичном числе называется БИТ, 4 бита - это ПОЛУБАЙТ, 8 битов это БАЙТ, два байта - это СЛОВО, два слова - это ДВОЙНОЕ СЛОВО (используется редко):

В конец двоичного числа принято добавлять букву "b". Таким образом мы можем определить, что 101b - это двоичное число, которое соответствует десятичному значению 5.

Двоичное число 10100101b эквивалентно десятичному значению 165:

Шестнадцатиричная система

Шестнадцатиричная система использует 16 цифр:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F

И поэтому основание в шестнадцатиричной системе равно 16.

Шестнадцатиричные числа являются компактными и легкими для чтения.
Их легко преобразовать в двоичную систему и наборот. Каждый полубайт (4 бита) можно преобразовать в шестнадцатиричную цифру, пользуясь этой таблицей:

Десятичное (основание 10) | Двоичное (основание 2) | Шестнадцатиричное (основание 16)
0 | 0000 | 0
1 | 0001 | 1
2 | 0010 | 2
3 | 0011 | 3
4 | 0100 | 4
5 | 0101 | 5
6 | 0110 | 6
7 | 0111 | 7
8 | 1000 | 8
9 | 1001 | 9
10 | 1010 | A
11 | 1011 | B
12 | 1100 | C
13 | 1101 | D
14 | 1110 | E
15 | 1111 | F

Принято в конец шестнадцатиричного числа добавлять букву "h", таким образом мы можем определить, что 5Fh - это шестнадцатиричное число, которому соответствует десятичное значение 95.
Мы также добавляем "0" (ноль) в начало шестнадцатиричного числа, если оно начинается с буквы (A..F), например 0E120h.

Шестнадцатиричное число 1234h эквивалентно десятичному 4660:

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

Чтобы преобразовать число из десятичной системы в какую-либо другую, необходимо выполнить целое деление десятичного значения на основание нужной системы счисления. Получим результат и остаток. Затем будем делить результат на основание системы, пока результат не будет равен нулю.

Остатки используются для представления значения в новой системе счисления.

Давайте преобразуем число 39 (основание 10) в шестнадцатиричную систему (основание 16):

Вы видите, что мы получили шестнадцатиричное число 27h.
Все остатки в этом примере меньше 10, поэтому мы не используем буквы.

Здесь приведен пример перевода числа с большим количеством цифр:
Давайте преобразуем десятичное число 43868 в шестнадцатиричную форму:

Результат получаем такой: 0AB5Ch, используя описанную выше таблицу для преобразования остатков больше 9 в соответствующую букву.

Используя тот же принцип мы можем преобразовать число в двоичную форму (используя 2 как делитель), или преобразовать шестнадцатиричное число в двоичное число, используя описанную выше таблицу:

Вы видите, что мы получили следующее двоичное число: 1010101101011100b

Числа со знаком

Нет никакой уверенности в том, каким является шестнадцатиричное число 0FFh - отрицательным или положительным. Оно может быть представлено двумя десятичными значениями: "255" или "- 1".

8 бит можно использовать для создания 256 комбинаций (включая ноль), поэтому мы просто предполагаем, что первые 128 комбинаций (0..127) будут представлять положительные числа, а следующие 128 комбинаций (128..256) будут представлять отрицательные числа.

Чтобы получить число "- 5", мы должны вычесть 5 из максимально возможного числа комбинаций (256). Так мы получим: 256 - 5 = 251.

Использование этого способа для представления отрицательных чисел имеет определенный смысл. В математике, если вы прибавите "- 5" к "5", вы должны получить ноль.
То же самое случается, когда процессор складывает два байта: 5 и 251. Результат превышает значение 255, поэтому из-за переполнения процессор получает ноль!

Когда используется комбинация 128..256, то старший бит всегда равен 1. Это используется для определения знака.

Тот же самый принцип используется для слов (16-ти битовых значений). С шестнадцатью битами можно создать 65536 комбинаций, первые 32768 комбинаций (0..32767) используются для представления положительных чисел, а следующие 32768 комбинаций (32767..65535) представляют отрицательные числа.

В Emu8086 имеются некоторые удобные инструменты для преобразования чисел и вычисления любых числовых выражений. Все их можно увидеть, выбрав пункт меню Math:

Number Convertor - преобразователь чисел позволяет вам преобразовывать числа из любой системы в любую систему. Просто напечатайте значение в любом текстовом поле и это значение будет автоматически преобразовано во все другие системы. Вы можете работать как с 8-ми битовыми, так и с 16-ти битовыми значениями.

Expression Evaluator - вычисление выражений может быть использовано для вычислений выражений, в котороых имеются числа, представленные в различных системах счисления, и преобразования чисел из одной системы в другую. Напечатайте выражение и нажмите ENTER. Результат появится в выбранной системе счисления. Вы можете работать со значениями до 32 битов. Если установлен флажок Signed, то программа будет считать все значения знаковыми (+ или -), кроме десятичных чисел и двойных слов. Двойное слово всегда расценивается как знаковое значение, поэтому 0FFFFFFFFh преобразуется в -1.
Например, вы хотите вычислить: 0FFFFh * 10h + 0FFFFh (максимальное местоположение памяти, доступное процессору 8086). Если вы установите флажки Signed и Word, вы получите -17 (потому что выражение будет вычислено как (-1) * 16 + (-1) . Чтобы выполнить вычисления без знака, уберите флажок Signed, тогда выражение будет вычислено как 65535 * 16 + 65535 и вы должны получить 1114095.
Вы можете также использовать Number Convertor для преобразования не десятичных чисел, знаковых десятичных значений, и выполнять вычисления с десятичными значениями (если это для вас проще).

Эти операции поддерживаются:

~       НЕ (инвертирует все биты).
*       умножение.
/       деление.
%       модуль.
+       сумма.
-       вычитание (и унарный минус).
<<      сдвиг влево.
>>      сдвиг вправо.
&       логическое И.
^       исключающее ИЛИ.
|       логическое ИЛИ.

Двоичные числа должны иметь суффикс "b", например:
00011011b

Шестнадцатиричные числа должны иметь суффикс "h", и начинаться с нуля
если первая цифра - это буква (A..F), например:
0ABCDh

Восмеричные (основа 8) числа должны иметь суффикс "o", например:
77o

>>> Следующий урок >>>


Ссылки Emu8086

  • Редактор исходного кода

  • Компиляция ассемблерного кода

  • Использование эмулятора

  • Полный набор команд процессора 8086

  • Список поддерживаемых прерываний

  • Глобальная таблица памяти

  • Пользовательская таблица памяти

  • Совместимость MASM / TASM

  • Порты ввода-вывода


Emu8086. Общие сведения.

Перевод: Поляков А.В. http://www.avprog.narod.ru

Все для изучения ассемблера в одном пакете! Emu8086 сочетает в себе мощный редактор исходного кода, ассемблер, дизассемблер, программный эмулятор (виртуальный ПК) с отладчиком и поэтапное обучение.

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

Визуальный интерфейс очень прост в работе. Вы можете наблюдать регистры, флаги и память во время выполнения вашей программы.

Арифметико-логическое устройство (АЛУ) показывает внутреннюю работу центрального процессора (CPU).

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

Машинный код 8086 полностью совместим со всеми последовавшими за ним поколениями микропроцессоров Intel, включая Pentium II и Pentium 4, и я уверен, что Pentium 5 будет также поддерживать команды 8086. Это делает код 8086 очень привлекательным, так как он выполняется как на старых, так и на современных компьютерных системах. Другим преимуществом является то, что набор команд 8086 сравнительно невелик, и поэтому изучить его будет легче.

Emu8086 имеет более легкий синтаксис, чем любые другие ассемблеры, но будет генерировать программу, которая сможет быть выполнена на любом компьютере, поддерживающем машинный код 8086. Это большой плюс для начинающих!

Примечание: Если вы не используете Emu8086 для компиляции кода, вы не сможете выполнить ваш исходный код в пошаговом режиме.

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

Как начать?

  1. Запустите Emu8086, выбрав ее значок в меню "Пуск", или непосредственно запустите приложение Emu8086.exe.

  2. Выберите "Samples (примеры)" из меню "File".

  3. Щелкните кнопку [Compile and Emulate] (или нажмите клавишу F5).

  4. Щелкните кнопку [Single Step] (пошаговый режим) (или нажмите клавишу F8), и наблюдайте за выполнением кода.

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

  6. А теперь самое время для просмотра уроков.


Прерывания, поддерживаемые в настоящее время эмулятором

Быстрые ссылки:

INT 10h/00h
INT 10h/01h
INT 10h/02h
INT 10h/03h
INT 10h/05h
INT 10h/06h
INT 10h/07h
 INT 10h/08h
INT 10h/09h
INT 10h/0Ah
INT 10h/0Eh
INT 10h/13h
INT 10h/1003h
INT 11h
 INT 12h
INT 13h/00h
INT 13h/02h
INT 13h/03h
INT 15h/86h
INT 16h/00h
INT 16h/01h
 INT 19h
INT 1Ah/00h
INT 21h

Список поддерживаемых прерываний с описаниями:

INT 10h / AH = 00h - установить видеорежим.
вход:
AL = нужный видеорежим.

Следующие видеорежимы поддерживаются:

00h - Текстовый режим 40x25, 16 цветов, 8 страниц.

03h - Текстовый режим 80x25, 16 цветов, 8 страниц.
INT 10h / AH = 01h - установить форму курсора в текстовом режиме.
вход:
CH = вершина курсора (биты 0-4) и опции (биты 5-7).
CL = основание курсора (биты 0-4).

Если биты 6-5 регистра CH установлены в 00, то курсор виден. Чтобы скрыть курсор, установите биты в 01 (следующее значение в CH будет скрывать курсор: 28h - 00101000b). Бит 7 должен быть всегда нулем.

Примечание переводчика. Высота курсора - это разность между вершиной и основанием курсора. Т.е. если установить биты 0-4 регистра СН в 00000, а регистра CL - в 11111, то курсор будет высотой в 32 строки (не текстовых строки - а строки экранных). Если установить биты 0-3 регистра СН в 1111, а регистра CL - в 1111, то курсор будет высотой в одну строку и располагаться под буквами. Ну и так далее...
INT 10h / AH = 02h - установить позицию курсора.
вход:
DH = строка.
DL = столбец.
BH = номер страницы (0..7).
INT 10h / AH = 03h - получить позицию и размер курсора.
вход:
BH = номер страницы.
выход:
DH = строка.
DL = столбец.
CH = вершина курсора.
CL = основание курсора.
INT 10h / AH = 05h - выбрать активную видео страницу.
вход:
AL = номер новой страницы (0..7).
активированная страница отображается.
INT 10h / AH = 06h - прокрутка окна вверх.
INT 10h / AH = 07h - прокрутка окна вниз.
вход:
AL = количество строк, которые должны быть прокручены (00h = полная очистка окна).
BH = атрибут, используемый для записи строки пробелов в нижней части окна.
CH, CL = строка, столбец - координаты верхнего левого угла окна.
DH, DL = строка, столбец - координаты нижнего правого угла окна.
INT 10h / AH = 08h - прочитать символ и атрибут с позиции курсора.

вход:
BH = номер страницы.
выход:
AH = атрибут.
AL = символ.
INT 10h / AH = 09h - записать символ и атрибут в позиции курсора.

вход:
AL = символ для вывода на экран.
BH = номер страницы.
BL = атрибут.
CX = сколько раз символ будет выведен на экран.
INT 10h / AH = 0Ah - записать символ только в позицию курсора.

вход:
AL = символ для вывода на экран.
BH = номер страницы.
CX = сколько раз символ будет выведен на экран.
INT 10h / AH = 0Eh - вывод в режиме телетайпа.

вход:
AL = символ для записи.
Эта функция отображает символ на экране, передвигает курсор и прокручивает экран, если это необходимо. Печать всегда выполняется на текущей видеостранице.

INT 10h / AH = 13h - записать строку.

вход:
AL = режим записи:
bit 0: не изменять курсор после записи;
bit 1: строка содержит атрибуты.
BH = номер страницы.
BL = атрибут, если строка содержит только символы (бит 1 регистра AL - ноль).
CX = количество символов в строке (атрибуты не считаются).
DL,DH = столбец, строка - координаты, с которых начинается запись строки.
ES:BP указатель на строку, которая будет напечатана.

INT 10h / AX = 1003h - выбрать интенсивность/мерцание.

вход:
BL = режим записи:
0: включить интенсивность (16 возможных цветов фона).
1: включить мерцание (не поддерживается эмулятором!).
BH = 0 (чтобы избежать проблем на некоторых адаптерах).

Таблица цветов:
Атрибут символа - это 8-ми битовое значение. Младшие четыре бита представляют набор цветов переднего плана, а старшие 4 бита - цвета фона. Мерцание цветов фона не поддерживается.

HEX    BIN       ЦВЕТ

0      0000      черный
1      0001      синий
2      0010      зеленый
3      0011      бирюзовый
4      0100      красный
5      0101      фиолетовый
6      0110      коричневый
7      0111      светло-серый
8      1000      темно-серый
9      1001      светло-синий
A      1010      светло-зеленый
B      1011      голубой (светло-бирюзовый)
C      1100      ярко-красный
D      1101      малиновый
E      1110      желтый
F      1111      белый

INT 11h - получить список оборудования BIOS.
выход:
AX = слово списка оборудования BIOS. Фактически эта функция возвращает содержимое слова в 0040h:0010h.

В настоящее время эта функция может использоваться для определения количества дисководов.

Описание битов для обнаруженного BIOS установленного оборудования:
Бит(ы)	Описание
 15-14  количество параллельных портов.
 13     не поддерживается.
 12     игровой прот установлен.
 11-9   количество последовательных портов.
 8      зарезервировано.
 7-6    количество дисководов (минус 1):
          00 один дисковод;
          01 два дисковода;
          10 три дисковода;
          11 четыре дисковода.
 5-4    видеорежимы:
          00 EGA,VGA,PGA, или другой со встроенной видео BIOS;
          01 40x25 CGA цвета;
          10 80x25 CGA цвета (по умолчанию в эмуляторе);
          11 80x25 монохромный текст.
 3    не поддерживается.
 2    не поддерживается.
 1    математический сопроцессор установлен.
 0    установлен, если дисковод может быть загрузочным (всегда установлен в эмуляторе).

INT 12h - получить размер памяти.
выход:
AX = килобайты смежной памяти, начинающейся с абсолютного адреса 00000h. Возвращает содержимое слова по адресу 0040h:0013h.

Дисководы эмулируются с помощью файлов FLOPPY_0(..3).

INT 13h / AH = 00h - сброс дисковода (в настоящее время эта функция не работает).

INT 13h / AH = 02h - прочитать сектор диска в память.
INT 13h / AH = 03h - записать сектор на диск.
вход:
AL = количество секторов для чтения/записи (не должно быть нулем)
CH = номер цилиндра (0..79).
CL = номер сектора (1..18).
DH = номер головки (0..1).
DL = номер дисковода (0..3 , зависит от количества файлов FLOPPY_?).
ES:BX указатель на буфер данных.
выход:
CF устанавливается в случае ошибки.
CF очищен, если ошибок нет.
AH = состояние (0 - если все выполнено успешно).
AL = количество переданных секторов.
Примечание: каждый сектор имеет 512 байтов.

INT 15h / AH = 86h - функция ожидания BIOS.
вход:
CX:DX = интервал в микросекундах
выход:
CF сброшен, если операция выполнена успешно (ждет окончания интервала),
CF установлен, если произошла ошибка или функция ожидания уже в работе.

Примечание:
на многих системах разрешение периода ожидания равно 977 микросекундам. Emu8086 использует 1000-микросекундный период.

INT 16h / AH = 00h - прочитать следующую нажатую клавишу (без эха).
выход:
AH = скан-код BIOS.
AL = ASCII-символ.
(если клавиша прочитана, она удаляется из буфера клавиатуры).

INT 16h / AH = 01h - проверить готовность символа в буфере клавиатуры.
выход:
ZF = 1 если символ не готов.
ZF = 0 если символ готов.
AH = скан-код BIOS.
AL = ASCII-символ.
(если клавиша прочитана, она удаляется из буфера клавиатуры).

INT 19h - перезагрузка системы.
Обычно BIOS пытается прочитать сектор 1, головку 0, дорожку 0 с дисковода А: в 0000h:7C00h. Эмулятор просто останавливает выполнение. Для загрузки с дисковода выберите из меню: 'Virtual Drive' -> 'Boot from Floppy'

INT 1Ah / AH = 00h - получить системное время.
выход:
CX:DX = количество тиков, начиная с полночи.
AL = 0, если таймер не переполнялся за 24 часа с момента сброса.
Примечания:
Часы обновляются приблизительно 18.20648 раз в секунду,
и 1800B0h за 24 часа.
AL еще не устанавливается эмулятором!

MS-DOS все-же не может полностью быть загружен в эмулятор, поэтому я сделал эмуляцию для некоторых основных прерываний DOS:

 INT 20h - выход в операционную систему.

INT 21h / AH=09h - вывод строки из DS:DX.

INT 21h / AH=0Ah - ввод строки в DS:DX, первый байт - размер буфера, второй байт - количество фактически прочитанных символов.

INT 21h / AH=4Ch - выход в операционную систему.

INT 21h / AH=01h - чтение символа из стандартного устройства ввода с эхом, результат записывается в AL.

INT 21h / AH=02h - запись символа в стандартное устройство вывода, DL = символ для записи. После выполнения AL = DL.


Уроки

Уроки Ассемблера 8086
  • Системы счисления

  • Часть 1: Что такое язык Ассемблера?

  • Часть 2: Доступ к памяти

  • Часть 3: Переменные

  • Часть 4: Прерывания

  • Часть 5: Библиотека общих функций - emu8086.inc

  • Часть 6: Арифметические и логические команды

  • Часть 7: Управление ходом программы

  • Часть 8: Процедуры

  • Часть 9: Стек

  • Часть 10: Макросы

  • Часть 11: Создание вашей собственной операционной системы

  • Часть 12: Управление внешними устройствами (робот, шаговый двигатель...)