
Assembler / P15
.pdf1
15. Язык Ассемблера
15.1. Что такое Ассемблер.
Ассемблер — программа для перевода мнемоники команд в машинный код. Дословно "Assembler" переводится как "сборщик", т.е. Ассемблер собирает коды. Язык, на котором записываются инструкции для программы Ассемблер, называется языком Ассемблера. Поэтому не совсем корректно говорить: "программа на Ассемблере". Правильно: "программа на языке Ассемблера".
С мини-Ассемблером нам пришлось работать, когда мы создавали программы в отладчике Turbo Debugger. Напомним, какие при этом возникали основные проблемы:
Нам приходилось самостоятельно распределять память для данных и кода. При этом в памяти возникали "дыры", т.к. мы опасались перекрытия данных и кода при внесении изменений в код программы;
Приходилось корректировать команды перехода "вперед", т.к. мы не знали числового значения метки в момент ввода команды;
Весьма трудоемким оказывался процесс внесения изменений в программу, особенно когда в "середину" программы приходилось добавлять новые команды.
Теперь мы возложим все эти задачи на программы, которые входят в состав пакетов для разработки программ на языке Ассемблера. Эти пакеты включают не только Ассемблеры, но и компоновщики, отладчики и прочие утилиты.
Таких пакетов в настоящее время существует несколько. Наибольшей популярностью пользуются MASM (Macro Assembler) фирмы Microsoft и TASM (Turbo Assembler) фирмы Borland. Мы будем составлять программы так, чтобы они проходили трансляцию и компоновку средствами обоих пакетов. На различия будем указывать особо. Основное внимание будет уделено TASM.
В чем заключается основное отличие Ассемблера от мини-Ассемблера, встроенного в отладчик? Ассемблер обрабатывает текстовый файл, содержащий программу. Эта программа содержит не только мнемоники команд, но и директивы, т.е. указания Ассемблеру для генерации кода и распределения данных.
15.2. Программа на языке Ассемблера.
В любом текстовом редакторе наберем текст программы, которая выводит на экран строку "Hello!". Если мы набираем эту программу в Microsoft Word, то ее нужно сохранять как текст, а не как документ.
файл first.asm |
|
||
|
.MODEL small |
; Малая модель памяти |
|
|
.STACK 100h |
; Для стека выделяется 100h байтов |
|
|
.DATA |
|
; Начало секции (сегмента) данных |
msg |
DB "Hello!",0Dh,0Ah,'$' ; Строка для вывода |
||
|
.CODE |
; Начало секции (сегмента) кода |
|
start: mov |
ax,@data |
; Загрузить регистр DS адресом DGROUP, |
|
|
mov |
ds,ax |
; в который входят сегменты данных и стека. |
|
mov |
ah,9h |
; Вывод на экран |
|
mov |
dx, OFFSET msg ; строки msg |
|
|
int |
21h |
; с помощью 9-й функции 21-го прерывания. |
|
mov |
ax,4C00h |
; Завершение работы программы |
|
int |
21h |
; с помощью функции 4Ch. |
2
END start ; Завершение текста программы
Прежде всего обратим внимание, что в программе имеются директивы, т.е. указания для Ассемблера, которые он будет использовать на этапе компиляции программы. Директивы рекомендуется набирать прописными буквами, чтобы читателю было легче отличить их от команд, которые будут исполняться при запуске программы на выполнение.
Директива .MODEL small указывает, что программная секция данных (она начинается с директивы .DATA) и программная секция кода (начинается с директивы .CODE) будут занимать не более одного сегмента ОЗУ. Напомним, что размер сегмента — 64 Кбайт. Для стекового сегмента явно указан его размер: 256 байт.
Впрограмме имеются метки: в секции данных метка msg, отделяемая пробелом от уже знакомой нам директивы определения данных DB. В секции кода первая команда снабжена меткой start. Метка отделена от команды двоеточием. Метка могла располагаться и в отдельной строке. Текст программы заканчивается директивой END, после которой указывается метка команды, с которой должно начаться выполнение программы.
Встроке msg байты 0Dh и 0Ah — это коды возврата каретки и перевода строки. За меткой start следуют две команды, которые обеспечивают загрузку сегментного регистра DS адресом секции данных (более точно, адресом группы
сименем DGROUP, в которую входят секции данных и стека). Этот адрес хранится во встроенной переменной @data. Команда mov ds,@data недопустима, поэтому приходится использовать промежуточный регистр AX. Операция OFFSET msg вычисляет смещение msg относительно начала секции данных. Так как это операция времени ассемблирования, набираем ее прописными буквами.
15.3. Трансляция и компоновка Трансляция программы происходит по команде
с:\tasm\bin\tasm first.asm
На диске появляется файл first.obj. Объектный файл имеет весьма сложную структуру. В частности, там хранятся машинные коды команд. Компоновка происходит по команде
с:\tasm\bin\tlink first.obj
На диске появляется файл first.exe, который можно запустить на выполнение. Одновременно создается карта загрузки first.map.
Запустим файл first.exe на выполнение. first.exe
Hello!
(Поучительно проследить, как изменяется на разных этапах подготовки программы число, обозначенное как @data.)
Транслятор tasm и компоновщик tlink имеют ключи, которые задают режимы создания объектного и исполнительного файлов. Чтобы получить листинг при трансляции введите команду: с:\tasm\bin\tasm/l first. Обратите внимание, что расширение файла можно и не указывать.
Если вы хотите включить в загрузочный файл отладочную информацию, а затем изучать программу в отладчике, то нужно ввести команды:
с:\tasm\bin\tasm/zi first с:\tasm\bin\tlink/v first с:\tasm\bin\td first
3
Ключи можно комбинировать, например:
с:\tasm\bin\tasm/zi/l first
15.4. Анализ листинга программы
Листинг программы first.lst состоит из трех частей. Проанализируем каждую часть отдельно. Для наглядности из текста программы перед трансляцией были удалены комментарии.
Turbo Assembler Version 4.1 |
19/04/02 00:33:35 |
Page 1 |
|||
first.asm |
|
|
|
|
|
1 |
0000 |
|
.MODEL small |
|
|
2 |
0000 |
|
.STACK 100h |
|
|
3 |
0000 |
|
.DATA |
|
|
4 |
0000 |
48 65 6C 6C 6F 21 0D+ msg |
DB "Hello!",0Dh,0Ah,'$' |
||
5 |
|
0A 24 |
|
|
|
6 |
0009 |
|
.CODE |
|
|
7 |
0000 |
B8 0000s start: |
mov |
ax,@data |
|
8 |
0003 |
8E D8 |
mov |
ds,ax |
|
9 |
0005 |
B4 09 |
mov |
ah,9h |
|
10 |
0007 |
BA 0000r |
mov |
dx, OFFSET msg |
|
11 |
000A CD 21 |
int 21h |
|
|
|
12 |
000C B8 4C00 |
mov |
ax,4C00h |
|
|
13 |
000F CD 21 |
int 21h |
|
|
|
14 |
|
|
END |
start |
|
Впервой части листинга информация расположена в четырех колонках. В первой колонке — порядковые номера строк листинга. Вторая колонка — текущее значение счетчика адреса (не путать с регистром IP — счетчиком команд). В начале секции данных (.DATA) и секции кода (.CODE) счетчик адреса сбрасывается, а затем возрастает с появлением в листинге новых данных
икоманд. В нашем случае, когда программа размещается только в одном файле, значение счетчика адреса станет в дальнейшем значением смещения (offset). В третьей колонке показаны сгенерированные коды. В секции данных мы видим ASCII коды для строки "Неllo!" и управляющих символов. Коды не уместились на одной строке (символ переноса — знак '+'). По значению счетчика адреса 0009 можно заключить, что строка занимает 9 байтов.
Всекции кода в третьей колонке размещены машинные коды команд. Обращает на себя внимание строка листинга
7 0000 B8 0000s start: mov ax,@data
Суффикс s (s — segment) в коде команды указывает, что непосредственный операнд связан с сегментным адресом. Когда мы будем изучать код программы в отладчике, то первый байт команды останется тем же — B8, а второй и третий байты изменятся, т.е. не будут соответствовать листингу. Об этом и
предупреждает суффикс s. |
|
В строке |
|
10 0007 BA 0000r |
mov dx, OFFSET msg |
суффикс r (r — relocatable — перемещаемый) сообщает, что смещение msg может измениться. В отладчике мы увидим код команды неизменным. Но если бы на этапе компоновки к нашему файлу был пристыкован другой файл с
4
секцией данных, то смещение msg от начала сегмента изменилось бы. На такую возможность и указывает r.
Вторая часть листинга содержит таблицу символов. Приведем фрагмент
этой таблицы. |
|
|
|
Turbo Assembler |
Version 4.1 |
19/04/02 00:33:35 Page 2 |
|
Symbol Table |
|
|
|
Symbol Name |
|
Type |
Value |
??DATE |
|
Text |
"19/04/02" |
??FILENAME |
|
Text |
"first " |
??TIME |
|
Text |
"00:33:35" |
… |
|
|
|
@DATA |
|
Text |
DGROUP |
… |
|
|
|
MSG |
|
Byte |
DGROUP:0000 |
START |
|
Near |
_TEXT:0000 |
Впервой колонке перечислены символические имена (Symbol Name), во второй колонке их тип (Type): текст, число. Для метки msg указано, что ее тип
—байтовый, для метки start — что она "близкая" (это определяется выбранной моделью памяти small). В третьей колонке приведено значение (Value) символического имени. Все имена записаны прописными буквами. Нами в программе определены только два имени: msg и start (Ассемблер перевел их в верхний регистр, т.е. заменил все буквы прописными). Остальные имена являются встроенными.
Втретьей, заключительной части листинга перечислены программные сегменты (секции) и группы, в которые они объединены.
Groups & Segments |
Bit Size Align |
Combine Class |
|
DGROUP |
Group |
|
|
STACK |
16 |
0100 Para |
Stack STACK |
_DATA |
16 |
0009 Word |
Public DATA |
_TEXT |
16 |
0011 Word |
Public CODE |
Сейчас мы не будем подробно анализировать эту часть листинга. Уместно сделать это позже, когда мы будем размещать текст программы в нескольких файлах. Сейчас отметим следующее. Секция кода (CODE) получила имя _TEXT, ее размер (Size) составляет 11h байтов. Секции стека с именем STACK и секция данных с именем _DATA объединены в группу DGROUP (группа размещается в одном сегменте оперативной памяти). Теперь ясно, почему в таблице символов введенные нами символические имена msg и start имеют сегментную часть адреса DGROUP и _TEXT. Конкретные числовые значения станут известны только на этапе загрузки .exe-файла в оперативную память.
15.5. Сообщения TASM об ошибках
При вызове tasm для трансляции first.asm выдаются следующие сообщения.
D:\ >tasm first.asm
Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International Assembling file: first.asm
5
Error messages: None
Warning messages: None
Passes: 1
Remaining memory: 388k
Сначала выводится номер версии Ассемблера. В пакет TASM 5.0 входит ассемблер tasm.exe версии 4.1. Далее сообщается, какой файл ассемблируется (позднее мы увидим, что за один вызов tasm можно ассемблировать несколько файлов, поэтому вовсе не лишней является информация, какой именно файл обрабатывается). Для этого файла в следующих двух строках выдается информация, что ошибок и предупреждений нет. Далее сообщается, что по тексту программы сделан один проход. Последнее сообщение информирует, сколько оперативной памяти осталось свободной при ассемблировании.
Перечислим возможные типы ошибок и предупреждений. Для этого намеренно будем вносить в файл ошибки.
1.Удалим директиву END start (проще всего закомментировать ее, поставив перед ней точку с запятой).
Assembling file: first.asm
**Fatal** first.asm(15) Unexpected end of file encountered Error messages: 1
Выдается тип ошибки (Fatal — грубая), в скобках указан номер строки файла, где встретилась ошибка (15), далее — диагностическое сообщение: "Неожиданно встречен конец файла". При этом не создается объектный файл и не создается файл с листингом (при наличии ключа /l).
Восстановим директиву END start.
2.Удалим запятую, разделяющую операнды, в команде mov ah,9h
**Error** first.asm(8) Undefined symbol: AH9H *Warning* first.asm(8) Argument needs type override Error messages: 1
Warning messages: 1
На этот раз файл листинга есть, но объектный файл по-прежнему отсутствует. Сообщение об ошибке: "Неопределенный символ AH9H". Как видите, сообщение не проясняет происхождение ошибки. К этой же строке делается предупреждение: аргумент требует переопределения типа. Это даже комментировать не будем. Восстановите запятую.
3. Добавим перед директивой END start строку mov ax,[200h]. *Warning* first.asm(13) [Constant] assumed to mean immediate constant Error messages: None
Warning messages: 1
На этот раз создается и файл с листингом и объектный файл. Но к предупреждениям нужно относиться внимательно и настороженно. В отладчике
мы увидим строку: |
|
|
1AB7:0011 B80002 |
mov |
ax,0200 |
Это вовсе не соответствует нашему замыслу: поместить в регистр AX содержимое ячейки со смещением 200. Предупреждение на этот раз было точным: Константа, заключенная в квадратные скобки, предполагается константой — непосредственным операндом. Как исправить эту ошибку? Нужно использовать префикс замены сегмента: mov ax, ds:[200h]. Тогда мы
увидим в отладчике: |
|
1AB7:0011 A10002 |
MOV AX,[0200] |
6
Префикс для DS: в коде, конечно, отсутствует.
15.6. Карта памяти Файл с картой памяти возникает при вызове компоновщика.
Start |
Stop |
Length |
Name |
Class |
00000H |
00010H |
00011H |
_TEXT |
CODE |
00020H |
00028H |
00009H |
_DATA |
DATA |
00030H |
0012FH 00100H STACK |
STACK |
Program entry point at 0000:0000
Для каждой программной секции определен начальный адрес (Start), конечный адрес (Stop), длина в байтах (Length), имя (Name) и класс (Class). Указан стартовый адрес (Program entry point). Адреса, указанные в Start, будут приплюсованы к адресу загрузки программы. Этот адрес задает загрузчик — компонента операционной системы программа command.com
15.7. Сообщения TLINK об ошибках
Так же как и TASM компоновщик выдает сообщения об ошибках трех типов: Fatal, Error, Warning. Приведем два примера.
Изменим в программе first.asm последнюю строку:
END ; start
Трансляция пройдет без замечаний. А компоновщик выдаст сообщение об ошибке:
Turbo Link Version 7.1.30.1. Copyright (c) 1987, 1996 Borland International Fatal: No program entry point (в программе нет стартового адреса) Восстановим последнюю строку (END start) и закомментируем строку
.STACK 100h. На этот раз получим предупреждение:
Warning: No stack
15.8. Запуск программы на выполнение
При запуске exe-файла на выполнение в ОЗУ образуется два блока памяти. В первом блоке расположены переменные окружения (enviroment). Например, там находятся строки PATH из autoexec.bat.
Второй блок (в котором и находится программа) состоит из двух частей. Сначала в памяти расположен так называемый префикс программного сегмента (PSP — program segment prefix). Его размер составляет ровно 256 = 100h байт. Далее располагается код и данные самой программы.
PSP в свою очередь состоит из двух частей. В его первой половине система размещает некоторую служебную информацию, к которой можно обращаться из самой программы. Например, по определенному смещению внутри PSP находится адрес блока окружения. Вторая половина PSP начинается со смещения 80h (именно половина: 80h + 80h = 100h). Она содержит хвост командной строки. Речь об этом пойдет ниже.
После загрузки программы в ОЗУ сегментные регистры содержат фиксированные адреса. В CS находится адрес сегмента кода программы, в SS — адрес сегмента стека программы. Они уже настроены нужным образом. А вот DS и ES содержат сегментный адрес PSP. Именно поэтому первые две выполняемые команды в exe-программе такие:
7
start: mov ax, @data mov ds, ax
После этого DS содержит сегментный адрес данных (более точно: сегментный адрес группы, включающей сегмент данных и сегмент стека). ES продолжает показывать на PSP.
15.9. Директивы определения данных.
Определение байтов, слов и двойных слов. Для начала мы изучим три директивы: DB (define byte), DW (define word), DD (define doubleword). Первые две нам уже знакомы по мини-Ассемблеру. Например,
DB 15, –3, 7 (в отличие от debug можно вводить и отрицательные числа) Метки. Метка — это символическое имя адреса с данными или командой.
Метка может состоять из симолов: A-Z, a-z, _, @, $, ?, 0-9. Метка не должна начинаться символами 0-9. Длина метки может составлять 255 символов, но Ассемблер различает только первые 32 символа. От директивы объявления данных метка отделяется пробелами, от команды — двоеточием. Метка с двоеточием может стоять в отдельной строке.
Использование меток. Рассмотрим пример. num DW 12
……
mov ax, num ; поместить в регистр AX число 12
Проанализируем синтаксис команды mov ax, num. Числовое значение num
— это адрес. Допустим, адрес (смещение) равен 4. Но в AX загружается не адрес num, а содержимое слова по этому адресу! В отладчике мы увидим команду mov ax, [0004]. Поэтому хотелось бы записать на языке Ассемблера эту команду иначе: mov ax, [num]. И MASM и TASM транслируют такую команду правильно. Более того, в TASM есть режим IDEAL, который требует именно такую форму записи команды: mov ax, [num]. Встретив команду mov ax, num в режиме IDEAL ассемблер TASM выдает предупреждение: Pointer expression needs brackets (Ссылка на адрес в памяти не заключена в квадратные скобки). Но сложилась традиция опускать квадратные скобки. Будем ей следовать.
А как загрузить адрес? Это можно сделать двумя способами. Один вам уже знаком: lea si, num. Второй способ — новый: mov si, OFFSET num. OFFSET —
это функция времени ассемблирования. На этапе ассемблирования вычисляется смещение num и формируется код команды. В отладчике (на этапе выполнения) мы увидим: mov si, 0004. Директивы и функции Ассемблера рекомендуется набирать прописными буквами. Ассемблеру это безразлично, а читателю делает текст программы это позволяет сразу видеть, что делается на стадии ассемблирования, а что — на стадии выполнения.
Цепочка символов. Определить цепочку символов можно двояко:
DB 'Hello'
DB "Hello"
т.е. в качестве ограничителей строки могут быть либо одинарные, либо двойные кавычки. Но недопустимо эти кавычки смешивать: DB 'Hello".
К цепочке изображаемых символов можно добавить управляющие символы. Добавим в конце строки символы "возврат каретки" и "перевод строки":
DB "Hello", 0Dh, 0Ah, '$'
Если вместо 0Dh поместить Dh, то Ассемблер выдал бы сообщение об ошибке: Illegal use of register (неверное использование регистра). Dh было бы
8
воспринято как имя регистра DH. Число должно предваряться десятичной цифрой.
Резервирование памяти. Если начальное значение выделяемой области памяти несущественно, то можно вместо конкретных чисел поместить вопросительные знаки:
DW ?, 6, ?, ? ; Зарезервировано четыре слова, определено второе.
В программе, работающей под управлением отладчика, эти вопросительные знаки заменяются нулями. Но если программа запускается автономно, то содержимое этих ячеек памяти может оказаться любым.
Оператор дублирования. Если некоторая последовательность данных многократно повторяется, то для сокращения записи можно воспользоваться оператором дублирования DUP (duplicate — дублировать). Примеры:
DB 6 DUP (1) ; эквивалентно DB 1,1,1,1,1,1
DW 2 DUP (1,2,?) ; эквивалентно DW 1,2,?,1,2,?
Итак, перед оператором DUP ставится коэффициент повторения, отделенный от DUP не менее, чем одним пробелом. В скобках указывается последовательность, подлежащая дублированию. Возможны вложенные DUP:
DB 2 DUP( 5, 3 DUP(1,0),2) ; эквивалентно DB 5,1,0,1,0,1,0,2,5,1,0,1,0,1,0,2
Атрибутные операторы. Как вы помните, отладчик "толковал по своем усмотрению" команду mov [300],5. Неясно, число 5 записывается в байт по адресу 300 или в слово по адресу 300? Разница существенная: байт с адресом 301 сохранит свое значение или будет обнулен? Поэтому для уточнения действия команды нужно было использовать атрибутный оператор: mov word ptr [300],5. Теперь рассмотрим пример на языке Ассемблера.
num DW 0FFFFh
……
mov num,5
Здесь атрибутный оператор не нужен. Ассемблер знает, что num — адрес слова, поэтому генерирует правильный код команды. После выполнения команды в байте с адресом num будет храниться число 5, а в байте с адресом num + 1 — нуль. Но как поступить, если нужно обратиться к байту слова? Вот здесь понадобится атрибутный оператор: mov byte ptr num+1,5. Адрес num + 1 вычисляется на этапе ассемблирования. В результате: num DW 5FFh.
В этом примере можно было обойтись без атрибутного оператора, но тогда слову num надо было дать альтернативное имя:
numb LABEL BYTE ; LABEL — метка num DW 0FFFFh
……
mov numb+1,5
Еще об атрибутном операторе. В отладчике была допустимой команда mov ah, [400]. Операнд-приемник — регистр, имеющий размер байта. Поэтому по адресу 400 выбирается байт. Теперь введем команду: mov ah, num. Последует сообщение об ошибке: Operand types do not match (Несоответствие типов операндов). Попытаемся исправить ошибку:
mov byte ptr ah, num.
Вновь последует то же самое сообщение об ошибке. Правильно так: mov ah, byte ptr num.
15.10. Подпрограммы.
Подпрограммы в программах на языке Ассемблера имеют формат
9
имя PROC
<тело подпрограммы> ret
имя ENDP
Вызов подпрограммы осуществляется командой call имя.
В зависимости от используемой модели памяти генерируются дальние или ближние вызовы и возвраты. Пока мы используем только малую модель памяти. Так как для этой модели программный код заключен в одном сегменте, то генерируются ближние вызовы и возвраты.
Пример. Строка заканчивается символом $. Подпрограмма преобразует прописные буквы строки в строчные. Входной параметр подпрограммы — адрес строки.
.MODEL small
.STACK 100h
.DATA
msg DB "Dogs and Cats are Friends", 0Dh, 0Ah, '$'
.CODE ;-----------------------------------------------------------------------------------------------
;Подпрограмма преобразует прописные буквы строки в строчные.
;Строка должна завершаться символом $.
;Вход: SI — адрес строки;
;Выход: SI — адрес терминатора строки;
;Используемые регистры: AL.
;------------------------------------------------------------------------------------------------ |
|
|
|
ToLower PROC |
|
|
|
next: |
mov al, [si] |
; |
Поместить очередной символ строки в AL |
|
cmp al, '$' |
; |
Если это терминатор строки, |
|
je fin |
; |
то завершить обработку. |
|
cmp al, 'A' |
; Если символ лежит в диапазоне 'A' - 'Z' |
|
|
jb cont |
|
|
|
cmp al, 'Z' |
|
|
|
ja cont |
|
|
|
add al, 'a' - 'A' ; то превратить его в соответствующую |
||
|
|
|
; строчную букву. |
|
mov [si], al |
|
; и поместить символ обратно в строку |
cont: |
inc si |
|
; Переместить указатель на следующий символ |
|
jmp next |
|
|
fin: |
ret |
|
|
ToLower ENDP
start:mov ax,@data mov ds,ax
;Вывод исходной строки mov ah,9
mov dx,OFFSET msg int 21h
;Преобразование строки mov si, OFFSET msg
10
call ToLower
;Вывод новой строки mov ah,9
mov dx,OFFSET msg int 21h
;Завершение программы mov ax, 4C00h
int 21h END start
Вместо je fin можно было сразу поместить команду ret.
Задача. Написать подпрограмму, которая удаляет из строки все цифры.
15.11. Командная строка.
Обычно мы запускаем программу на выполнение, набирая ее имя в командной строке и заканчивая ввод нажатием клавиши Enter. Но в командной строке можно вводить и дополнительную информацию, которая составляет так называемый "хвост командной строки". Пример: tasm.exe prim.asm. Здесь в хвосте командной строки мы указываем, какую программу нужно ассемблировать.
Хвост командной строки располагается в PSP, начиная со смещения 80h. Пример. Пусть в командной строке введено:
D:\>prim.exe ab c
Посмотрим в отладчике, как выглядит хвост командной строки в PSP Для этого вызовем отладчик так:
td prim.exe ab c
Перейдем в окно CPU (F10 → View → CPU). Перейдем в панель данных (Shift+Tab). Отобразим данные, начиная со смещения 80h (Alt+F10 → Goto → 80). Мы увидим:
ds:0080 05 20 61 62 20 63 0D 00
Сегментная часть адреса находится в DS, т.к. выполнение программы еще не началось. 05 — количество символов в командной строке: пробел, 'a', 'b', пробел, 'c'. Строка заканчивается управляющим символом 0D (в количество символов он не входит). Теперь из программы можно обратиться к этой области памяти и извлечь из нее необходимую информацию.
15.12. Пример выполнения задания D1.
15.12.1. Формулировка задания.
Вариант 0. Текст для шифрования вводится в командной строке. В тексте — только прописные латинские буквы и пробелы. Удалить пробелы и зашифровать текст. Каждый символ преобразуется по формуле 3*код+5 (mod 26). При этом считается, что код буквы 'A' равен 0, код буквы 'B' равен 1 и т.д. В программе должны быть одна или две подпрограммы для шифрования символа (или группы символов).
15.12.2. Текст программы.
файл D1v0.asm