Язык ассемблера
Язык ассемблера | |
---|---|
Класс языка | императивный |
Тип исполнения | ассемблируемый |
Появился в | 1949 |
Расширение файлов | .asm или .s [1] |
Медиафайлы на Викискладе |
Язы́к ассе́мблера (англ. assembly language, разг. ассе́мблер) — представление команд процессора в виде, доступном для чтения человеком. Язык ассемблера считается языком программирования низкого уровня в противовес высокоуровневым языкам, не привязанным к конкретной реализации вычислительной системы. Программы, написанные на языке ассемблера, однозначным образом переводятся в инструкции конкретного процессора и в большинстве случаев не могут быть перенесены без значительных изменений для запуска на машине с другой системой команд. Ассемблером также называется программа-компилятор, преобразующая код на языке ассемблера в машинный код. Программа, выполняющая обратную задачу, называется дизассемблером.
История
Первые ассемблеры были спроектированы Кэтлин Бут в 1947 под ARC2[2] и Дэвидом Уилером[англ.] в 1948 под EDSAC[3], при этом термин «ассемблер» не использовали, просто называя язык «множеством базовых команд» (англ. basic order set) и «начальными командами» (англ. initial orders) соответственно. Впервые термин «ассемблер» для процесса объединения полей в командное слово начали использовать более поздние отчёты по EDSAC.
Также на ранних этапах развития программирования было введено понятие автокод — язык программирования, предложения которого по своей структуре в основном подобны командам и обрабатываемым данным конкретного машинного языка[4][]. В настоящее время термин фактически не используется.
Исторически, если первым поколением языков программирования считать машинные коды, то язык ассемблера можно рассматривать как второе поколение языков программирования[]. Недостатки языка ассемблера, например, сложность разработки на нём больших программных комплексов, позже привели к появлению языков третьего поколения — языков программирования высокого уровня (таких как Фортран, Лисп, Кобол, Паскаль, Си и другие).
Синтаксис
Не существует общеупотребительного синтаксиса языка ассемблера. Так как системы команд различных процессоров значительно отличаются, также и отличаются языки ассемблера для этих процессоров. Кроме того, каждая программа-ассемблер может использовать отличающийся от других синтаксис. В ассемблерах для процессоров x86 наибольшее распространение получил так называемый Intel-синтаксис, в меньшей степени — синтаксис AT&T.
Мнемоники
Базовой конструкцией языка ассемблера является мнемоника, или мнемокод — краткое символьное представление команды процессора. Как правило, она состоит из нескольких символов, обозначающих производимое действие (например, mov
— для пересылки из одного регистра в другой, add
— для сложения значений и т.д.). Также в мнемонику может входить объект, над которым производится операция (регистр, память, стек), либо другие особенности (влияние на регистр флагов, условия исполнения и т.д.), однако в других диалектах те же особенности могут задаваться в операндах.
Как правило, ассемблер каждого процессора имеет свой традиционный набор мнемоник, но встречаются ассемблеры с кроссплатформенным синтаксисом (такие, как синтаксис AT&T), однако кроссплатформенными в них остаются только обозначения, код одного процессора не может быть напрямую перенесён на другой.
Операнды
В качестве операндов могут указываться регистры, константные значения, адреса ячеек памяти и портов ввода-вывода, константы, метки и т.д. Разные ассемблеры могут требовать разного порядка операндов: в одних реализациях первым идёт оператор, в который записывается значение, в других он идёт последним. Как правило, операнды отделяются от мнемоник команд пробелами.
Литералы
Наиболее распространённым типом данных, с которыми умеют работать большинство процессоров, является целое число, упакованное в машинное слово, либо один или несколько байтов, реже число с плавающей запятой. В программах на языке ассемблера значительно чаще используются значения, заданные в разных системах счисления. Прежде всего в компьютерах с восьмибитным байтом часто используется шестнадцатеричная запись числа, так как в один байт помещаются две шестнадцатеричные цифры. Некоторые значения могут записываться двоичными кодами. В ранних компьютерах с шестибитным байтом встречалась также восьмеричная система счисления. Способы записи могут различаться в различных ассемблерах, например:
- Для записи числа в десятичной системе счисления в одних трансляторах требуется представление только в виде цифр (
255
,65535
), тогда как в других для этого требуется начать число с точки (.255
,.65535
). - Для записи числа в шестнадцатеричной системе требуется начать число с префикса «0x» (
0xFF
,0x2000
), в других — добавить в конце числа «h» (0FFh
,2000h
), в третьих — записывать только цифры (0FF
,2000
), при этом в последних двух случаях у чисел, начинающиеся с A…F, для отличия их от символьных имён спереди добавляют ноль. - Признаком восьмеричной системы в некоторых трансляторах является ведущий ноль (
0377
,0177777
), в других требуется добавить префикс в виде буквы «O», а число заключить в апострофы (O’377’
,O’177777’
). - Для записи констант в двоичной системе распространённым является формат вида
b'10010111'
.
Кроме того, иногда требуется задавать блоки данных, загружаемые вместе с программным кодом, для чего ассемблер может содержать специализированные директивы. Современные ассемблеры могут поддерживать также организацию данных в виде различных структур.
Элементы выразительности
Ассемблеры могут поддерживать различные конструкции для облегчения чтения ассемблерного кода, снятия с программиста необходимости следить за адресами инструкций, а также для реализации элементов, характерных для языков высокого уровня.
- Метки — указывают на места в программе, на которые может производиться условный или безусловный переход, вызов процедуры, а также хранения данных и т.д. При ассемблировании метка преобразуется в адрес;
- Именованные константы — позволяют давать осмысленное имя числовому значению, а также централизованно его менять. В процессе ассемблирования константа заменяется соответствующим ей значением;
- Комментарии в языке ассемблера имеют большее значение, чем в языках высокого уровня, так как он не может обеспечить самодокументируемость кода.
- Макросы — позволяют упаковать часто встречающиеся последовательности команд, давая им осмысленное имя;
- Директивы ассемблера, позволяющие задавать режимы ассемблирования, осуществлять условную трансляцию и т.д.
Стандарты оформления кода
В ассемблерном коде как правило не применяются характерные для языков высокого уровня отступы и операторные скобки. Ассемблерный код обычно записывается в несколько колонок, которые включают:
- Адрес инструкции (необязательная колонка);
- Метки;
- Мнемонику самой инструкции;
- Операнды;
- Комментарии.
Такой способ записи отражает особенность исполнения программ на процессорах общего назначения: на уровне машинных кодов программы как правило линейны, не имеют никакой структуры и из одного места программы может быть осуществлён переход на другое безотносительно того, где находится начало программного кода и программа продолжит исполнение с того места, куда был осуществлён переход. Пример программы на языке ассемблера для архитектуры PIC16:
Again: movf 0x40,W ;Скопировать ячейку с адресом 0x40 (десятичное 64) в регистр W
addlw 0x05 ;Добавить константу 5 к регистру W
movwf PORTC ;Записать регистр W в выходной порт PORTC микроконтроллера
clrw ;Очистить регистр W (у данной инструкции нет операндов)
goto Again ;Перейти на метку Again
Достоинства и недостатки
Так как ассемблерный код однозначно переводится в машинный код для заданного процессора, это позволяет более полно использовать все возможности процессора, сокращать количество ненужных «холостых» операций и использовать прочие приёмы оптимизации программного кода, недоступные при использовании компиляторов, однако развитие оптимизирующих компиляторов приводит к тому, что качество генерируемого ими кода может быть выше, чем может написать программист на ассемблере средней квалификации[5]. При этом чем больше объём программы, тем меньше выигрыш от использования языка ассемблера.
Программы на ассемблере не допускают неопределённого поведения, однако в целом написание и отладка кода на ассемблере требует больше усилий. На ассемблере недоступен контроль типов, из-за чего смысл того или иного значения и допустимые действия над ним должен контролировать сам программист. При написании программ на языке ассемблера требуется постоянно пользоваться стеком и ограниченным количеством регистров общего назначения, а также указателями, что требует от программиста внимательности и хорошей памяти.
Программы на языке ассемблера практически невозможно перенести на машину с другой архитектурой или системой команд без переписывания программы, даже если при написании использовался «кроссплатформенный» диалект ассемблера: разные архитектуры процессоров имеют разные наборы регистров, флагов, разные размеры машинного слова, а также могут иметь узкоспециализированные команды, отсутствующие на других платформах.
Программа на ассемблере имеет больше возможностей для взаимодействия с оборудованием и ядром ОС. К примеру, в ранних домашних компьютерах и игровых приставках мог отсутствовать встроенный таймер достаточно высокого разрешения, но при этом тактовая частота процессора была стандартной для всех устройств одного типа, что позволяло использовать процессор в качестве таймера, рассчитывая количество тактов на выполнение тех или иных команд и вставляя в нужных местах пустые операции. В современных процессорах, использующих встроенные схемы оптимизации исполнения, динамическое изменение тактовой частоты и сложные системы прерываний, а тем более под управлением многозадачных ОС такие приёмы стали невозможны, однако продолжают использоваться на некоторых микроконтроллерах.
Применение
Появление ассемблеров значительно облегчило задачу программирования ранних компьютеров, но достаточно быстро сложность прикладных задач потребовала использования языков высокого уровня. Однако эти языки исполнялись достаточно медленно, к тому же им не всегда были доступны все аппаратные возможности компьютера. По мере роста производительности мейнфреймов и мини-компьютеров, а также с появлением таких языков, как Си, актуальность ассемблера начала снижаться, однако вновь возросла с появлением микрокомпьютеров. Как правило, ранние микропроцессоры имели невысокую производительность и небольшой объём доступной оперативной памяти, к тому же для них не сразу появились качественные компиляторы языков высокого уровня. Часто программы для домашних компьютеров, в том числе игры, писались целиком на ассемблере. Однако к началу XXI века к растущей производительности компьютеров добавились оптимизирующие компиляторы, которые генерировали машинный код более оптимальный, чем мог бы написать программист средней квалификации. Кроме того, важное значение стал приобретать вопрос переносимости между разными платформами.
Язык ассемблера также используется в отладке и обратной разработке, с использованием программ-дизассемблеров. Пользуясь дизассемблером можно контролировать исполнение программы на уровне машинных команд, что бывает полезно, например, при поиске мест с неопределённым поведением, или ошибок, возникающих при работе с указателями.
Ассемблерные вставки
Для облегчения разработки применялся следующий подход: большая часть кода пишется на языке высокого уровня, и только участки, для которых критична производительность, либо требующие обращения непосредственно к аппаратным ресурсам компьютера, пишутся на ассемблере.
Примеры
Hello, world!
.MODEL TINY
CODE SEGMENT
ASSUME CS:CODE, DS:CODE
ORG 100h
START:
mov ah,9
mov dx,OFFSET Msg
int 21h
int 20h
Msg DB 'Hello World',13,10,'$'
CODE ENDS
END START
.MODEL SMALL
.DATA
msg DB 'Hello World',13,10,'$'
.CODE
START:
mov ax, @DATA
mov ds, ax
mov ax, 0900h
lea dx, msg
int 21h
mov ax, 4C00h
int 21h
END START
SECTION .data
msg: db "Hello, world",10
len: equ $-msg
SECTION .text
global _start
_start: mov edx, len
mov ecx, msg
mov ebx, 1 ; stdout
mov eax, 4 ; write(2)
int 0x80
mov ebx, 0
mov eax, 1 ; exit(2)
int 0x80
SECTION .data
msg: db "Hello, world",10
len: equ $-msg
SECTION .text
global _start
syscall: int 0x80
ret
_start: push len
push msg
push 1 ; stdout
mov eax, 4 ; write(2)
call syscall
add esp, 3*4
push 0
mov eax, 1 ; exit(2)
call syscall
Программа для Microsoft Windows на диалекте MASM
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
msg db "Hello, world", 13, 10
len equ $-msg
.data?
written dd ?
.code
start:
push -11
call GetStdHandle
push 0
push OFFSET written
push len
push OFFSET msg
push eax
call WriteFile
push 0
call ExitProcess
end start
Консольная программа для Windows на диалекте FASM
format PE console
entry start
include 'include\win32a.inc'
section '.data' data readable writeable
message db 'Hello, world!',0
section '.code' code readable executable
start:
; CINVOKE макрос в составе FASM.
; Позволяет вызывать CDECL-функции.
cinvoke printf,message
cinvoke getch
; INVOKE аналогичный макрос для STDCALL-функций.
invoke ExitProcess,0
section '.idata' import data readable
library kernel,'kernel32.dll',\
msvcrt,'msvcrt.dll'
import kernel,\
ExitProcess,'ExitProcess'
import msvcrt,\
printf,'printf',\
getch,'_getch'
64-битная программа для Windows на диалекте YASM (c использованием линковщика от Microsoft)
;yasm-1.0.0-win32.exe -f win64 HelloWorld_Yasm.asm
;setenv /Release /x64 /xp
;link HelloWorld_Yasm.obj Kernel32.lib User32.lib /entry:main /subsystem:windows /LARGEADDRESSAWARE:NO
bits 64
global main
extern MessageBoxA
extern ExitProcess
section .data
mytit db 'The 64-bit world of Windows & assembler...', 0
mymsg db 'Hello World!', 0
section .text
main:
mov r9d, 0 ; uType = MB_OK
mov r8, mytit ; LPCSTR lpCaption
mov rdx, mymsg ; LPCSTR lpText
mov rcx, 0 ; hWnd = HWND_DESKTOP
call MessageBoxA
mov ecx, eax ; uExitCode = MessageBox(...)
call ExitProcess
ret
.section ".data"
hello: .asciz "Hello World!\n"
.section ".text"
.align 4
.global main
main:
save %sp, -96, %sp ! выделяем память
mov 4, %g1 ! 4 = WRITE (системный вызов)
mov 1, %o0 ! 1 = STDOUT
set hello, %o1
mov 14, %o2 ! количество символов
ta 8 ! вызов системы
! выход из программы
mov 1, %g1 ! move 1(exit() syscall) into %g1
mov 0, %o0 ! move 0(return address) into %o0
ta 8 ! вызов системы
Примеры программ для различных микроконтроллеров
Программа на языке ASM-51 для микроконтроллера AT89S52 (семейство MCS-51)
Данная программа отправляет обратно символ, полученный через последовательный порт UART ("Эхо"):
mov SCON, #50h
mov TH1, #0FDh
orl TMOD, #20h
setb TR1
again:
clr RI
jnb RI, $
mov A, SBUF
jnb RI, $
clr TI
mov SBUF, A
jnb TI, $
sjmp again
Битовые операции:
Си:
z = (a << 2) | (b & 15);
Ассемблер:
ADR r4,a ; get address for a
LDR r0,[r4] ; get value of a
MOV r0,r0,LSL#2 ; perform shift
ADR r4,b ; get address for b
LDR r1,[r4] ; get value of b
AND r1,r1,#15 ; perform AND
ORR r1,r0,r1 ; perform OR
ADR r4,z ; get address for z
STR r1,[r4] ; store value for z
Ветвления:
Си:
if (i == 0)
{
i = i + 10;
}
Ассемблер:
@(переменная i находится в регистре R1)
SUBS R1, R1, #0
ADDEQ R1, R1, #10
Циклы:
Си:
for ( i = 0 ; i < 15 ; i++)
{
j = j + j;
}
Ассемблер:
SUB R0, R0, R0 ; i -> R0 and i = 0
start CMP R0, #15 ; is i < 15?
ADDLT R1, R1, R1 ; j = j + j
ADDLT R0, R0, #1 ; i++
BLT start
Программа для микроконтроллера PIC16F628A (архитектура PIC)
В случае, когда к порту PORTB микроконтроллера подключены 8 светодиодов, программа включит их через один:
LIST p=16F628A
__CONFIG 0309H
STATUS equ 0x003
RP0 equ 5
TRISB equ 0x086
PORTB equ 0x006
ORG 0x0000 ;Вектор запуска
goto start ;Переход на начало основного кода
start:
bsf STATUS,RP0 ;Выбор банка 1
clrf TRISB ;Все биты порта PORTB - выходы
bcf STATUS,RP0 ;Выбор банка 0
led:
movlw .170 ;Запись двоичного значения "10101010" в PORTB
movwf PORTB
goto led ;Зацикливание
END
Программа для микроконтроллера MSP430G2231 (архитектура MSP430) в среде Code Composer Studio
.cdecls C,LIST, "msp430g2231.h"
;------------------------------------------------------------------------------
.text ; Program Start
;------------------------------------------------------------------------------
RESET mov.w #0280h,SP ; Initialize stackpointer
StopWDT mov.w #WDTPW+WDTHOLD,&WDTCTL ; Stop WDT
SetupP1 bis.b #001h,&P1DIR ; P1.0 output
;
Mainloop bit.b #010h,&P1IN ; P1.4 hi/low?
jc ON ; jmp--> P1.4 is set
;
OFF bic.b #001h,&P1OUT ; P1.0 = 0 / LED OFF
jmp Mainloop ;
ON bis.b #001h,&P1OUT ; P1.0 = 1 / LED ON
jmp Mainloop ;
;
;------------------------------------------------------------------------------
; Interrupt Vectors
;------------------------------------------------------------------------------
.sect ".reset" ; MSP430 RESET Vector
.short RESET ;
.end
Примечания
- ↑ https://cs.lmu.edu/~ray/notes/x86assembly/
- ↑ General Considerations in the Design of an All Purpose Electronic Digital Computer Архивная копия от 24 марта 2020 на Wayback Machine by Andrew D. Booth and Kathleen H. V. Britten. 2nd. Edition. August 1947.
- ↑ 1985 Computer Pioneer Award «For assembly language programming.» Дата обращения: 5 августа 2020. Архивировано 31 июля 2020 года.
- ↑ ГОСТ 19781-83 // Вычислительная техника. Терминология: Справочное пособие. Выпуск 1 / Рецензент канд. техн. наук Ю. П. Селиванов. — М.: Издательство стандартов, 1989. — 168 с. — 55 000 экз. — ISBN 5-7050-0155-X.
- ↑ Крис Касперски. Война миров: Ассемблер против Си . Дата обращения: 1 июня 2010. Архивировано из оригинала 29 июля 2010 года.
Литература
- Галисеев Г. В. Ассемблер для Win 32. Самоучитель. — М.: Диалектика, 2007. — 368 с. — ISBN 978-5-8459-1197-1.
- Зубков С. В. Ассемблер для DOS, Windows и UNIX. — М. ДМК Пресс; СПб. Питер, 2006. — 608 с. — ISBN 5-94074-259-9.
- Кип Ирвин. Язык ассемблера для процессоров Intel = Assembly Language for Intel-Based Computers. — М.: Вильямс, 2005. — 912 с. — ISBN 0-13-091013-9.
- Калашников О. А. Ассемблер? Это просто! Учимся программировать. — СПб.: БХВ-Петербург, 2007. — 384 с. — ISBN 978-5-94157-709-5.
- Крис Касперски. Искусство дизассемблирования. — СПб.: БХВ-Петербург, 2008. — 896 с. — ISBN 978-5-9775-0082-1.
- Владислав Пирогов. Ассемблер для Windows. — СПб.: БХВ-Петербург, 2007. — 896 с. — ISBN 978-5-9775-0084-5.
- Владислав Пирогов. Ассемблер и дизассемблирование. — СПб.: БХВ-Петербург, 2006. — 464 с. — ISBN 5-94157-677-3.
- Ричард Саймон. Microsoft Windows API Справочник системного программиста.
- Фрунзе А. В. Микроконтроллеры? Это же просто! — Т. 1.
- Юров В., Хорошенко С. Assembler: учебный курс. — СПб.: Питер, 1999. — С. 672. — ISBN 5-314-00047-4.
- Аблязов Р. З. Программирование на ассемблере на платформе х86-64. — М.: ДМК Пресс, 2011. — С. 304. — ISBN 978-5-94074-676-8.
- Юричев Д., Понимание языка ассемблера https://yurichev.com/writings/UAL-RU.pdf
- Практическое программирование микроконтроллеров Atmel AVR на языке ассемблера.. — 2-е. — БХВ-Петербург, 2014. — 368 с. — (Электроника). — ISBN 9785977533119.
Ссылки
- Assembler & Win64 (англ.) — введение в ассемблер под х86-64