Главная страница

Ассемблер для блондинок (и блондинов). И блондинов


Скачать 0.75 Mb.
НазваниеИ блондинов
АнкорАссемблер для блондинок (и блондинов).pdf
Дата14.12.2021
Размер0.75 Mb.
Формат файлаpdf
Имя файлаАссемблер для блондинок (и блондинов).pdf
ТипПрактическая работа
#303672
страница6 из 6
1   2   3   4   5   6
jb @B; меньше начала диапазона, переход на ввод
cmp bl,dh
ja @B; больше конца диапазона, переход на ввод
sub bl,dl; - номер буквы (начиная сна метку "начало ввода"
@@: ; обработка содержимого стека
mov ecx,[ebp-4]; восстановили длину диапазона
outstrln 'вот сколько раз встретился каждый символ из заданного диапазона'
@@:
outchar dl
pop eax
outintln eax,3
inc dl
dec ecx; это замена LOOP - он здесь не проходит
jnz @B
newline

@kon: ; выходные действия процедуры, эпилог
pop edi
pop edx
pop ecx
pop ebx
pop eax
add esp,4; можно mov esp,ebp
pop ebp
ret 4*1; удаляем из стека параметр
Letter endp
; ==================
Start:
ClrScr
push 1234; для последующей проверки правильности очистки стека
mov ebp,223344; для последующей проверки правильности восстановления регистров
mov ecx,665577
ConsoleTitle "Процедура с локальным массивом"
xor eax,eax
mov al,'a'
mov ah,'m'
push eax; 'am#0#0'
call Letter
outstr 'введите еще один диапазон - два символа'
inchar bl
inchar bh
push ebx
call Letter
newline
pop ebx
outword ebp,10
outword ecx,10
outwordln ebx,10
exit
end Start В написанной процедуре мы со стеком работаем в основном "обычным" образом записываем в него нули командой push (и заодно отводим память под локальный массива в конце читаем информацию командой pop (и заодно освобождаем стек от локального массива. Чаще память в стеке отводят ивы- свобождают с помощью изменения значения регистра ESP. Задача. Напишем похожую процедуру. Процедура вводит текст сточкой на конце ив качестве результата выдает в регистре AL букву, которая в этом тексте чаще всего встречается (будем считать, что хотя бы одна буква в тексте есть, если несколько букв встречаются одинаково часто, выведем любую. Локальные данные такой процедуры – массив количеств букв – будем хранить в стеке. Никаких параметров у процедуры нет, результат она передает через регистр AL.
include console.inc
.code
MAXLET proc
push ebp
mov ebp,esp
sub esp,26*4; порождение локального массива из 26 двойных слов
comment *
[ebp-26*4]; начало массива это адрес первого элемента, адрес второго [ebp- 26*4 +4] и т.п.
можно было не заводить массив изменением ESP, так как в него надо положить начальные значения, можно было "запушить" 26 нулей.
*
push ecx
push ebx
push edx
outstrln "Вводите текст сточкой в конце"
mov ecx,26
xor eax,eax
Obnul: ; обнуление 26 позиций в стеке
mov dword ptr [ebp-26*4+4*eax],0
inc eax
loop Obnul vvod: ; ввод строки символов и обработка
inchar al
cmp al,'.'; строка заканчивается точкой
je kon
cmp al,'a'
jb vvod
cmp al,'z'
ja vvod
; сюда попали только если была введена маленькая буква
sub al,'a'; получили номер буквы
inc dword ptr [ebp-26*4+4*eax]; двойные слова, поэтому умножаем на 4
jmp vvod kon:
outstrln "печать"
mov ebx,0
mov dl,'a'
mov ecx,0 print:
outchar dl; очередная буква
outstr ' - '
outword dword ptr [ebp-26*4+4*ebx]
cmp dword ptr [ebp-26*4+4*ebx],ecx; сравниваем текущее значение и миксимальное - cl
jbe @F
mov ecx,dword ptr [ebp-26*4+4*ebx]; меняем максимум
mov al,dl
@@: outstr' '
inc ebx
inc dl; переходим к следующей букве
cmp dl,'z'
jbe print
newline
pop edx; Эпилог работаем со стеком в обратном порядкен
pop ebx
pop ecx
add esp,26*4; освобождаем стек от локального массива
pop ebp
ret
MAXLET endp
Start:

call MAXLET
outchar al
exit
end Start
15. Многомодульные программы Чтобы хорошо понять данный материал, надо прочитать соответствующие главы в учебнике [1]. Здесь только приводятся примеры работающих программ. Как происходит работа многомодульной программы
1. Имеем несколько модулей, написанных на одном или нескольких языках программирования, между модулями имеются некоторых связи (попеременным, пометкам. Эти связи должны быть явно обозначены. Если из модуля совершается переход по некоторой метке, описанной в другом модуле или используется переменная, описанная в другом модуле, без указания, что эти объекты являются внешними, модуль компилироваться не будет (куда идти, если метки нет. Компилятору дается указание extern (и описываются внешние объекты, тогда так компилятор поймет, что искать данные объекты в данном модуле не надо, их отсутствие ошибкой не является. Если переменная (процедура, метка) описана в данном модуле, но ею будет пользоваться другой модуль (например, переходить по этой метке, данный факт тоже надо обозначить. Такой модуль без специальных указаний компилятору компилироваться будет (если есть процедура, которая не вызывается, это не ошибка, но при компиляции теряются все имена, данные пользователем, восстановить их уже будет нельзя. Модули компилируются по отдельности (каждый – с помощью "своего" компилятора, в зависимости оттого, на каком языке написан. В результате компиляции получается так называемый объектный файл. В объектном файле не содержится сведений, на каком языке был написан исходный модуль. Выполняться по отдельности модули обычно не могут. В нашем случае модули, написанные на Ассемблере, надо откомпилировать с помощью компилятора языка Ассемблер. Сделать это удобно в той папке, в которой вы выполняли программы на ассемблере, так как из нее удобным образом прописаны пути к нужным файлам. Модуль, написанный на Паскале надо откомпилировать компилятором Паскаль. Это удобно делать также в той папке, в которую вы помещали программы, которые запускали для работы на Паскале, так как именно для этой папке прописаны пути к нужным файлам для компилятора Паскаль. Чтобы программа из нескольких модулей заработала, надо все модули собрать в одну папку, в туже папку поместить программу-сборщик (линковщик). Понятно, что, если все модули на ассемблере, делать это надо в папке с ассемблерными программами. Если один из модулей на Паскале, удобнее переписать объектные файлы всех модулей в папку с Паскаль-программой, тогда сборку можно поручить Паскаля обычный запуск из Паскаль-среды кнопкой RUN). Отметим, что описанный здесь способ не является единственным, можно помещать модули в разные папки, аккуратно прописывая пути к нужным файлам. Можно компилировать Паскаль-программу не из
Паскаль-среды, а из командной строки и т.д. Примеры.
1. Головной и вспомогательный модуль на ассемблере Головной модуль (вызывает процедуру, которая описана в другом модуле.
include console.inc
comment * головной модуль, вызывает процедуру на ассемблере в другом модуле
*
.data
X dd ?
.code
Start:

; в головном модуле (и только в нем) обязательно должна
; быть метка начала работы (повторяется после END)
extrn PROST@0:near
; внешнее имя. К названию процедуры добавлено @0. Эта
; процедура описана во вспомогательном модуле, из
; головного она вызывается.
outstrln 'Это работает головной модуль'
inint Введите число'
outintln Введено '
push offset X
; параметр-адрес передается в процедуру через стек
call PROST@0; К имени процедуры добавлено @0
; Процедура выполняет действие X:=X+111
outintln Получено'
; можно видеть, что процедура проработала, изменила значение X
exit
end Start; уголовного модуля после end пишется метка,
; с которой начинается работа программы Вспомогательный модуль. В нем описана процедура, которая вызывается из головного модуля.
include console.inc
; вспомогательный модуль
.code
public PROST; внешнее имя _prost@0
; Эта процедура будет вызываться из других модулей
PROST proc
; здесь всѐ обычным образом, имя процедуры без всяких добавок
push ebp
mov ebp,esp; стандартные действия входа в процедуру
push ebx; сохраняем регистр, он понадобится
mov ebx,[ebp+8]; это адрес переменной из стека
add dword ptr [ebx],111; увеличиваем переменную на 111
pop ebx
pop ebp
ret 4; в стеке был один параметру вспомогательного модуля в конце стоит END без метки Компиляция модулей В файле, которым мы пользуемся для выполнения программ на Ассемблере, содержатся команды для компиляции, сборки и выполнения программы. Модуль надо только откомпилировать (получить объектный файл. Это можно сделать с помощью такого файла. В первую строку надо записать имя модуля (без расширения. Если компиляция прошла успешно, на экране появится сообщение о создании объектного файла, а в папке появится файл стем же именем и расширением OBJ
@echo off
set Name=prost
set path=..\bin;..\..\bin
set include=..\include;..\..\include
set lib=..\lib;..\..\lib ml /c /coff /Fl %Name%.asm
if errorlevel 1 goto errasm
echo ______ Now you have a object file _____
goto TheEnd
:errasm

echo Assembler Error !!!!!!!!!!!!
goto TheEnd
:TheEnd
pause Когда все модули откомпилированы (имеются объектные файлы, их надо собрать (LINK), получить выполняемый файл (EXE) и выполнить его. Это можно сделать с помощью такого файла. В первую и вторую строки надо записать имена модулей (без расширения, в третью строку – имя, которые Вы хотите дать получившемуся выполняемому файлу (может совпадать с именем одного из модулей. Если все пройдет успешно, в папке появится файл с заданным Вами именем и расширением EXE, а сформированная программа будет выполнена, результаты ее работы Вы увидите на экране.
@echo off
set Name1=mod_gl
set Name2=mmm
set NameRez=mod_mmm
set path=..\bin;..\..\bin
set include=..\include;..\..\include
set lib=..\lib;..\..\lib link /subsystem:console /out:%NameRez%.exe %Name1%.obj %Name2%.obj
if errorlevel 1 goto errlink
echo Link is sucseccful
%NameRez%.exe goto TheEnd
:errlink
echo Link Error !!!!!!!!!!!!!!!!!
goto TheEnd
:TheEnd
pause
2. Головной модуль на Паскале, вспомогательный - на ассемблере. Во вспомогательном модуле процедура, которая вызывается из программы на Паскале. Если при подготовке вспомогательного модуля выполнены все соглашения о связях, описанную в нем процедуру можно вызвать из Паскаля. Вспомогательный модуль транслируется точно также, подготавливается объектный файл, который помещается в папку с программой на Паскале. Вот программа на Паскале, делающая тоже самое, что головной модуль на ассемблере в предыдущем примере (вызывает процедуру из описанного выше модуля.
program Connect;
procedure PROST(var a:longint); В процедуре 1 параметр, передается по ссылке - то есть в стеке должен быть адрес
stdcall; такое соглашение о связях
external name '_PROST@0'; Внешнее имя у этой процедуры будет таким
{$L PROST.obj}
здесь имя файла, в котором находится скомпилированный модуль
var ss:longint;
begin
Введите целое число
Readln(ss);
PROST(ss); процедура вызывается также, как если бы была описана в этой же программе на Паскале
Результат работы процедуры ', ss) ; readln
end.
Программы из примера 1 и из примера 2 работают одинаково, пользуясь одними тем же модулем с процедурой.
3. Головной модуль на Паскале, вспомогательный на Ассемблере. Вызывается процедура с двумя параметрами, которые передаются один по ссылке, другой по значению. Параметры закладываются в стек справа налево. Таким образом, первый (самый левый параметр) будет в стеке по адресу [ebp+8], второй – [ebp+12] и т.д. Головной модуль на Паскале
program Connect;
procedure SUMPR(p:longint; var s:longint);
stdcall; external name '_SUMPR@0';
{$L SUMPR.obj}
{ Процедура SUMPR в модуле SUMPR.OBJ, внешнее имя _Sumpr@0 }
var ss:longint;
begin
Программа ss:=23; SUMPR(345,ss);
Результат
Writeln(ss); readln
end. Вспомогательный модуль с процедурой на ассемблере
include console.inc
comment * модуль для работы с паскаль-программой
Два параметра sumpr(a,b) Выполняется действие b:=a+b
*
.code
public SUMPR; внешнее имя _SUMPR@0
SUMPR proc
push ebp
mov ebp,esp
push eax
push ebx
mov eax,[ebp+8]; первый параметр (слева)
mov ebx,[ebp+12]; второй параметр
add [ebx],eax
pop ebx
pop eax
pop ebp
ret 8
SUMPR endp
end.
4. Головной модуль на Паскале, вспомогательный – на ассемблере. Вызывается функция, описанная во вспомогательном модуле. Все точно также. Значение функции передается через регистр AL, AX или EAX в зависимости от типа результата (byte, integer, longint). Файл на Паскале
program Connect;
function Fun(a,b:longint):longint;
stdcall; external name '_FUN@0';

{$L confu.obj} Функция в модуле, внешнее имя _FUN@0}
var ss:longint;
begin
Программа ss:=23; ss:=Fun(111,222);
Результат
Writeln (ss); readln
end. Файл на ассемблере
include console.inc comment * функция для работы с Паскаль-программой
Вычитает из первого параметра второй
*
.code
public FUN
FUN proc
push ebp
mov ebp,esp
mov eax,[ebp+8]; й параметр-значение
sub eax,[ebp+12]; й параметр параметр
pop ebp
ret 8
FUN endp
end
5. Оба модуля на ассемблере. Передают друг другу управление пометкам. Во вспомогательном модуле описана переменная, в нем она вводится, а в головном – печатается. Головной модуль
include console.inc
comment * головной модуль - передает управление вспомогательному, там вводится значение, которое выводится в головном модуле
*
.code
extern VVOD:near;
extern A:dword; это имя описано в другом модуле
public METKA
; это имя описано здесь, будет использоваться в другом модуле
Beg:
outstrln 'Работает головной модуль'
outstrln 'Управление передается на вспомогательный модуль'
jmp VVOD
METKA:
outstrln 'Работает снова головной модуль'
outstr 'Было введено значение '
outintln A
exit
end Beg Вспомогательный

include console.inc
; модуль
public A; внешнее имя _A
public VVOD
extern METKA: near;
.data
A dd ?
.code
VVOD:
outstrln 'Работает вспомогательный модуль'
outstr 'Введите значение переменной'
inint A
outstrln 'Значение введено. Управление передается на головной модуль'
jmp METKA
end
1   2   3   4   5   6


написать администратору сайта