Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Assembler / P15

.pdf
Скачиваний:
63
Добавлен:
02.06.2015
Размер:
470.78 Кб
Скачать

11

COMMENT &

0. Текст для шифрования вводится в командной строке. В тексте — только прописные латинские буквы и пробелы. Удалить пробелы и зашифровать текст. Каждый символ преобразуется по формуле 3*код+5 (mod 26). При этом считается, что код буквы 'A' равен 0, код буквы 'B' равен 1 и т.д.

&

.MODEL small

.STACK 100h

.DATA

text DB 83 DUP(?)

msg1 DB "В командной строке нет текста", 0Dh, 0Ah, '$' msg2 DB "В тексте недопустимые символы", 0Dh, 0Ah, '$'

.CODE ;---------------------------------------------------

;Подпрограмма шифровки символа

;Вход: AL - код символа

;Выход: AL - код зашифрованного символа

;----------------------------------------------------

encode PROC

cmp al,'A' ; Если код символа не ниже кода 'A' jnae cf1

cmp al,'Z' ; и не выше кода 'Z', jnbe cf1

sub al,'A' ; Получить код символа, начиная с 0

;Вычисление нового кода по формуле 3*код+5 (mod 26) mov bl,3

mul bl add ax,5 mov bl,26 idiv bl

add ah,'A'

; Вернуться к коду ASCII

mov al,ah

 

clc

;

нормальное завершение

ret

 

 

cf1: stc

; на входе символ, отличный от прописной латинской буквы

ret

 

 

encode ENDP

 

start: mov ax, @data mov ds, ax

; Копирование цифр из хвоста командной строки в строку для обработки mov cx, 0

mov cl, es:[80h] ; Длина хвоста командной строки в CX

jcxz empty

; Если хвост пустой – на empty

dec cx

; Не нужно учитывать начальный пробел

mov si, 82h

; Смещение хвоста – в SI

mov di, OFFSET text ; Указатель для новой строки – в DI

12

; Цикл обработки элементов строки

n: mov al, es:[si] ; Очередной символ хвоста – в AL cmp al, ' ' ; Если встретился пробел,

je cont ; то пропустить его,

call encode ; а символ - зашифровать

jc no_A_Z ; Переход, если встретился недопустимый символ

mov [di],al ; Поместить зашифрованный символ в строку-приемник inc di ; Переместить указатель в строке-приемнике

cont: inc si ; Переместить указатель в исходной строке loop n

;

;Поместить в строку-приемник символы ВК и ПС, а также терминатор mov word ptr [di], 0A0Dh

inc di inc di

mov byte ptr [di], '$'

;Вывод итоговой строки на экран

mov dx, OFFSET text mov ah, 9h

int 21h

jmp short fin

; Вывод сообщений об ошибках

empty: mov dx, OFFSET msg1 ; строка пуста mov ah, 9h

int 21h

jmp short fin

no_A_Z: mov dx, OFFSET msg2 ; в строке недопустимые символы mov ah, 9h

int 21h

; Завершение работы программы fin: mov ax, 4C00h

int 21h END start

Распечатка этого файла составляет содержание отчета. Текст программы обязательно должен включать подробные комментарии.

Отметим некоторые особенности в тексте программы.

1.Программа открывается блоком комментариев, начинающихся с директивы COMMENT. Сами комментарии должны ограничиваться парой одинаковых символов. В нашем случае выбран &, так как этот символ не встречается внутри самих комментариев.

2.Директивы Ассемблера (PROC, DB и т.д.) и операции времени трансляции (OFFSET) выделяем прописными буквами для лучшей читабельности текста программы.

3.Вместо числовых кодов букв (например, 41h) используем их символьные обозначения ('A').

13

15.12.3. Трансляция, компоновка и выполнение программы. Трансляция программы

tasm/zi/l d1v0

(ключ /zi — включение отладочной информации, /l — получение файла листинга). На выходе файлы a6v0.obj и a6v0.lst.

Компоновка tlink /v d1v0

(ключ /v — включение отладочной информации). На выходе файлы a6v0.exe и a6v0.map.

Запуск на выполнение

D:\>d1v0

Вкомандной строке нет текста

D:\>d1v0 Aa1

Втексте недопустимые символы

D:\>d1v0 NEW LINE SRTMDSR

15.12.4. Отладка

D:\>td d1v0.exe NEW LINE

(расширение .exe нужно обязательно указывать).

Закроем окна Module (Alt+F3) и Watch (Alt+F3). Создадим окно CPU (F10 → View → CPU). «Распахнем» его (F5). Последовательными нажатиями Ctrl+M добьемся, чтобы в панели кода отображались и строки исходного текста программы и сгенерированный код:

#d1v0#start: start: mov ax, @data cs:001E B82A57 mov ax,572A

Мы видим, что встроенная переменная Ассемблера @data на этапе выполнения получила значение 572Ah.

Перейдем в панель данных (Shift+Tab). Отобразим хвост командной строки

(Alt+F10 → Goto → 80).

Уменьшим размер окна CPU и отобразим окно данных (F10 → View → Dump), расположив его ниже окна CPU. В окне Dump перейдем к адресу text (Alt+F10 → Goto → text). Обратите внимание, теперь мы набираем не конкретный адрес, а его символическое обозначение.

Переходим в окно CPU (F6). Выполняем программу по шагам, чтобы проследить правильность ее выполнения. (Никогда не запускайте программу на языке Ассемблера сразу на выполнение! Наверняка в ней есть ошибки.) Для этого нажимаем F7, если нужно войти в подпрограмму, или F8, если подпрограмму нужно выполнить как одну команду.

Проследите, как изменяется содержимое стека при вызове подпрограммы и возврате в главную программу.

Посмотреть вывод на экран зашифрованной строки можно, нажав комбинацию Alt+F5. "Вернуться обратно" — также Alt+F5.

Если мы хотим испытать работу программы с другими исходными данными, нет необходимости выходить из отладчика и заново вызывать его. В главном меню: F10 → Run → Arguments. Появляется диалоговое окно Enter command line arguments. Введем, например, WEB (начальный пробел вводить не

14

нужно, он появится автоматически). Отладчик задает вопрос: Reload program so arguments take effect? (Заново загрузить программу, чтобы аргументы возымели эффект?). Разумеется, отвечаем Yes (нажимаем Enter). Чтобы в панели кода перейти к первой команде нашей программы, набираем Ctrl+G → start (опятьтаки обратите внимание: мы набираем не адрес, а его символическое имя).

Выход из отладчика — Alt+X.

Для проверки правильности самих вычислений нам поможет отладчик. Вычислим с помощью отладчика, в какой символ перейдет буква 'N'. Для этого откроем окно Watches (F10 → View → Watches) и введем в него выражение:

(('N'–'A')*3+5) mod 26d+'A',c

Здесь после запятой указан символ форматирования c, являющийся сокращением от слова character — символ. В окне появится выражение и результат его вычисления:

(('T'–'A')*3+5) mod 26d+'A',c 'S'

Обратите внимание на суффикс d у числа 26. При отладке программы на языке Ассемблера числа по умолчанию рассматриваются как 16-ричные.

Для выполнения таких же вычислений с буквой 'E' выделите уже введенное выражение и в контекстном меню выберите пункт Edit (редактирование).

15.13. Командный файл для трансляции, компоновки и отладки.

Для того чтобы не набирать каждый раз похожие команды, полезно создать командный файл. Его текст может быть, например таким.

1.bat

c:\tasm\bin\tasm /l/zi %1 IF ERRORLEVEL 1 GOTO exit c:\tasm\bin\tlink /v %1 pause

c:\tasm\bin\td %1.exe :exit

Для программы d1v0.asm вызов командного файла имеет вид

1 d1v0

Расширение файла (asm) не указывается. Если вызвать командный файл так:

1 d1v0.asm

то будет сгенерирована команда c:\tasm\bin\tlink /v d1v0.asm

вместо правильной c:\tasm\bin\tlink /v d1v0.obj

Но расширение файла при использовании tasm и tlink можно не указывать.

Авот для Turbo Debugger это расширение (exe) нужно указать обязательно.

Всистемную переменную ERRORLEVEL записывается код возврата программы tasm. Вторую строку командного файла следует читать так:

IF ERRORLEVEL >= 1 GOTO exit

но знак >= опускается. Если tasm не выдал сообщения об ошибках, то его код возврата равен нулю и происходит вызов компоновщика. Если же в процессе трансляции обнаружены ошибки, то код возврата равен 1. Происходит досрочный выход из командного файла (переход на метку exit).

Зачем в файле 1.bat размещена команда pause? Если при трансляции будут предупреждения (warnings), то прочитать их мы не успеем, так как Turbo

m1
m2
m3
m4

15

Debugger ―заслонит‖ экран с сообщениями. Сначала мы изучаем сообщения tasm и tlink и нажимаем на любую клавишу, чтобы вызвать Turbo Debugger.

15.14. Средства языка Ассемблера.

15.14.1. Переменные и константы времени ассемблирования.

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

Вот простой пример. Нам нужно задать четыре массива слов, заполненных нулями. Размер первого массива равен N, размер второго — N + 2, размер третьего — 2N – 1, четвертого — остаток от деления N на 3. Решение при N = 5:

.DATA

DW 5 DUP(0)

DW 7 DUP(0)

DW 9 DUP(0)

DW 2 DUP(0)

Если возникнет необходимость изменить число N, то размеры массивов придется заново вычислять вручную. Хотелось бы в программе задавать число N, а размеры массивов вычислять автоматически. Решение:

 

.DATA

N = 5

 

m1

DW

N DUP(0)

m2

DW

N+2 DUP(0)

m3

DW

2*N-1 DUP(0)

m4

DW

N MOD 3 DUP(0)

 

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

дать свои имена:

 

.DATA

 

N1

= 5

 

 

N2

= N1+2

 

N3

= 2*N1-1

 

N4

= N1 MOD 3

m1

DW

N1

DUP(0)

m2

DW

N2

DUP(0)

m3

DW

N3

DUP(0)

m4

DW

N4

DUP(0)

 

 

 

Переменные времени ассемблирования являются 16-разрядными. Такие переменные могут участвовать в арифметических выражениях. Возможны следующие операции: + (сложение), – (вычитание), * (умножение), / (деление нацело), MOD (взятие остатка по модулю).

Константы задаются с помощью директивы EQU. Например, часто используемому сочетанию символов 0Dh,0Ah,'$' можно дать имя, которое Ассемблер будет заменять его определением.

CRLFT EQU 0Dh,0Ah,'$'

Далее в программе изменять это определение нельзя.

16

15.14.2. Счетчик адреса.

Когда начинается новая программная секция, специальная встроенная переменная Ассемблера — счетчик адреса — сбрасывается в нуль. По мере выделения новых участков памяти для переменных или команд счетчик адреса увеличивается. Посмотрим это на примере. Вот часть листинга небольшой программы.

1

0000

 

 

 

.MODEL small

2

0000

 

 

 

.STACK 100h

3

0000

 

 

 

.DATA

4

0000

01

02

a

DB

1,2

5

0002

0003 0004 0005

b

DW

3,4,5

6

0008

48

65 6C 6C 6F 24

c

DB

'Hello$'

7

000E

0000000C

d

DD

12

8

0012

 

 

 

.CODE

9

0000

B8

0000s

s:

mov ax,@data

10

0003

8E

D8

 

mov ds,ax

11

0005

8B

1E 0006r

 

mov bx,b+4

12

0009

B8

4C00

 

mov ax,4C00h

13

000C

CD 21

 

int 21h

14

 

 

 

 

END s

Во второй колонке — значение счетчика адреса. Проследите его изменение в зависимости от количества и типа данных (в секции .DATA) и в зависимости от размера команд (секция .CODE). Величина b+4 вычисляется на этапе трансляции (мы видим слева 0006r).

А вот что мы увидим в отладчике.

 

-CPU Pentium-----------------------

 

ds:0006 = 0005

cs:0000 B83919

mov

ax,1939

cs:0003 8ED8

mov

ds,ax

cs:0005►8B1E0600

mov

bx,[0006]

cs:0009 B8004C

mov

ax,4C00

cs:000C CD21 int

21

 

Справа вверху в панели кода отображается адрес и содержимое ячейки, на которую ссылается текущая команда (команда, адрес которой в IP).

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

15.14.3. Примеры программ.

Перепишем программы a3 и a4, которые ранее мы создавали с помощью отладчика.

файл a3v0.asm

.MODEL small

.STACK 100h

.DATA

d DB "68",0Dh,0Ah,'$'

.CODE start: mov ax,@data

mov ds,ax

mov dx,OFFSET d mov ah, 9h

 

 

17

 

int 21h

 

 

mov al,d[0] ; Первая цифра — в AL.

 

mov ah,d[1] ; Вторая цифра — в AH.

 

add al,ah

; Сумма кодов цифр — в AL.

 

cmp al,2*'0'+ 10 ; Сравниваем сумму кодов с числом 10

 

jng m

; Если сумма больше 10,

 

xchg ah,d[0]

; то меняем местами

 

mov d[1],ah

; цифры,

 

jmp fin

 

m:

sub al,ah

; иначе восстанавливаем код первой цифры.

 

cmp al,'3'

; Будет ли в AL код цифры после вычитания 3?

 

jnge fin

; Если ДА,

 

sub al,3

; то вычитаем из кода цифры тройку

 

mov d[0],al

; и возвращаем цифру в память

fin:

mov dx,OFFSET d

 

mov ah, 9h

 

 

int 21h

 

 

mov ax,4C00h

 

 

int 21h

 

 

END start

 

Здесь

команду mov al,d[0] можно заменить на mov al,d, но в

приведенном варианте выборка первой цифры выглядит как обращение к элементу массива. Команду mov ah,d[1] можно было заменить на mov al,d+1. Обратите внимание, что текст программы приобрел наглядность за

счет использования средств

Ассемблера:

например, команда cmp al,33h

заменена на cmp al,'3'.

 

 

В отладчике мы увидим строки:

 

#a3v0#18: jmp fin

 

 

193C:0021 EB09

jmp

#a3v0#fin (002C)

193C:0023 90

nop

 

Команда nop появилась как следствие "технологии обратных поправок". Ассемблер во время трансляции команды jmp fin "не знает" числового значения метки fin, поэтому резервирует для команды перехода три байта, на случай, если метка fin расположена дальше, чем 127 байтов. Далее оказывается, что достаточно двухбайтовой команды перехода и лишний байт заполняется командой nop.

Избавиться от "паразитной" команды nop можно двумя способами. Вопервых, можно в программе указать команду короткого перехода: jmp short fin. Во-вторых, можно дать транслятору возможность совершить несколько проходов по тексту программы. Для этого указывается ключ /m. Тогда все лишние команды nop будут вычищены.

Перепишем программу для задания A4. Код, который не претерпел изменений, обозначим многоточием.

файл a4v0.asm

.MODEL small

.STACK 100h

.DATA

A DB 2,1,2, 13 DUP(1)

18

DimA = $ - A

BDB DimA DUP(0)

CDW DimA DUP(-1)

.CODE

start: mov ax, @data mov ds,ax

mov si,OFFSET A mov di,OFFSET B mov bx,OFFSET C sub dx,dx

mov cx,DimA

n:mov al,[si]

. . .

loop n

mov ax,4C00h int 21h

END start

Поясним некоторые решения. В директиве A DB 2,1,2, 13 DUP(1) использована конструкция повторения DUP (DUPlicate — дублировать): тринадцать раз в память будет записана единица. В следующей строке введена переменная времени ассемблирования DimA (в исполняемом файле мы с помощью отладчика увидим только ее конкретное значение в команде mov cx,DimA). значение DimA вычисляется как разность текущего значения счетчика адреса (встроенная переменная $) и адреса массива A. Эта разность в точности равна количеству элементов массива A. В двух нижеследующих директивах переменная DimA используется как коэффициент повторения в DUP. Если мы захотим изменить в программе количество элементов в массиве A, то размер двух других массивов будет изменен автоматически. Также автоматически изменится непосредственный операнд в команде mov cx,DimA. В строке mov bx,OFFSET C на этапе ассемблирования вычисляется начальный адрес массива C, который ранее мы скрупулезно вычисляли вручную.

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

Соседние файлы в папке Assembler