или
mov ax,[MemLoc]
Здесь значение 1234h получается как прямой, а не как непосредственный операнд: инструкция mov использует встроенное в нее смещение 5002h и загружает в ax значение по смещению 5002h, которое в данном случае равно 1234h.
Часто встречающейся ошибкой является то, что часто забывают использовать операцию offset, например:
mov si,MemLoc,
где нужно использовать смещение MemLoc. На первый взгляд, данная строка не выглядит неправильной, и так как MemLoc это переменная размером в слово, то эта строка не приведет к ошибке ассемблирования. Однако при выполнении в si будут загружены содержащиеся в переменной MemLoc данные (1234h), а не ее смещение (5002h) и результаты будут непредсказуемы.
Надежного способа избежать этой проблемы нет, но можно принять за правило заключать все ссылки на память в квадратные скобки. Когда перед ссылками на адресные константы будет указываться префикс OFFSET, а ссылки на память заключаться в квадратные скобки, это устранит двусмысленность и неопределенность при использовании имен переменных памяти. При таком соглашении работа инструкций
mov si,offset MemLoc
и
mov si,[MemLoc]
становится совершенно понятной, в то время как инструкция mov si,MemLoc
будет настораживать.
Библиографический список
1.Зубков С.В. Assembler для DOS, Windows и UNIX. / С.В. Зубков 11-е изд.,
стер. М.: ДМК Пресс; СПб.: Питер, 2010. 640 с.
2.Бурдаев О.В. Ассемблер в задачах защиты информации / О.В. Бурдаев, М.А. Иванов, И.И. Тетерин Под ред. И.Ю. Жукова. – 3-е изд., стер. – М.: КУДИЦ-
ОБРАЗ, 2008. – 544 с.
59
3. Абель Питер. Язык и программирование для IBM PC. / Питер Абель. Издво КОРОНА-Век, 2009.
60
Приложение 1
Примеры (тексты) программ
Пример П.1.1
;lab3pr1.asm |
|
; Примеры |
|
mov al,b1 |
|
sar al,1 |
; арифметический сдвиг байта вправо |
mov byte ptr rezult,al |
|
; ---------------------------------------- |
|
mov al,b1 |
|
rol al,1 |
; циклический сдвиг байта влево |
mov byte ptr rezult,al |
|
; ---------------------------------------- |
|
mov al,b1
ror al,1 ; циклический сдвиг байта вправо mov byte ptr rezult,al
=================================================================
Пример П.1.2
; lab3pr2.asm COM-программа создания файла на диске и вывода в файл
codesg segmentPARA 'Code' |
|
|||
|
assume cs:codesg,ds:codesg,ss:codesg,es:codesg |
|||
|
org |
100h |
|
; обход PSP |
begin: |
|
jmp |
main |
;обход через данные |
; --------------------------------------------------- |
|
|
|
|
flda |
dw |
250 |
|
;определение данных |
fldb |
dw |
125 |
|
|
fldc |
dw |
? |
|
|
soob1 |
db |
'Привет!','$',0ah,0dh |
||
soob2 |
db |
'Error |
|
','$',0ah,0dh |
fcbrec label byte |
|
|
||
fcbdriv db |
03 |
|
;диск С |
|
fcbname db |
'file1 |
' |
|
|
fcbext |
db |
'dat' |
|
|
fcbblk dw |
0000 |
|
|
|
fcbrcsz dw |
? |
|
|
|
fcbflsz dd |
? |
|
|
|
|
dw |
? |
|
|
|
dt |
? |
|
|
fcbsqrc db |
00 |
|
|
|
; |
dd |
? |
|
|
|
|
|
|
|
Main |
proc |
near |
|
|
|
mov ah,09 |
|
|
|
|
lea dx, soob1 |
|
|
int 21h ---------------------------------------------------
mov ah,16h lea dx,fcbrec
int 21h ---------------------------------------------------
61
cmp al,00 jnz error mov fcbrcsz,1 mov ah,1ah lea dx,soob2
int 21h ---------------------------------------------------
mov ah,15h lea dx,fcbrec
int 21h ---------------------------------------------------
jmp exit
error:
mov ah,09 lea dx, soob2
int 21h ---------------------------------------------------
exit: |
|
ret |
;возврат в DOS |
main endp |
|
codesg |
ends |
end |
begin |
=================================================================
Пример П.1.3
formula3 proc
;lab3pr3.asm, АБП(1), ЦСЛ(10), СДП(11)П, СДЛ(12)
;Примеры:
cmp x1,3 jz fabp_1 cmp x1,4 jz fcsl_10 cmp x1,5 jz fsdr_11 cmp x1,6 jz fsdl_12
;Вычислительная часть!
fabp_1: |
;АБП(1) |
mov al,b1 |
|
sar al,1 |
|
mov byte ptr rezult,al |
|
ret |
|
fcsl_10: |
;ЦСЛ(10) |
mov ax,w1 |
|
mov cx,10 |
|
csl_10: |
|
rol ax,1 |
|
loop csl_10 |
|
mov word ptr rezult,ax |
|
ret |
|
fsdr_11: |
;СДП(11)П |
mov ax,word ptr d1 |
|
mov dx,word ptr d1[2] |
|
mov cx,11 |
|
|
62 |
|
cmp cf,0 |
|
|
jnz cf1 |
|
cf0: |
clc |
|
|
jmp sdr_11 |
|
cf1: |
stc |
|
sdr_11: |
|
|
|
rcr dx,1 |
|
|
rcr ax,1 |
|
|
loop sdr_11 |
|
|
jc rcf1 |
|
|
jnc rcf0 |
|
rcf1: |
mov cf,1 |
|
|
jmp next |
|
rcf0: |
mov cf,0 |
|
next: |
mov word ptr rezult,ax |
|
|
mov word ptr rezult[2],dx |
|
|
ret |
|
fsdl_12: |
;СДЛ(12) |
|
|
mov ax,word ptr d1 |
|
|
mov dx,word ptr d1[2] |
|
|
mov cx,12 |
|
sdl_12: clc |
|
|
|
rcl ax,1 |
|
|
rcl dx,1 |
|
|
clc |
|
|
loop sdl_12 |
|
|
mov word ptr rezult,ax |
|
|
mov word ptr rezult[2],dx |
|
|
ret |
|
formula3 |
endp |
=================================================================
Пример П.1.4
; lab3pr4.asm использование блока управления файлом FCB для создания файла
;----------------------------------------------------------
codesg segment para 'Code'
assume cs:codesg,ds:codesg,ss:codesg,es:codesg
org |
100h |
|
; обход PSP |
begin: jmp |
main |
|
;обход через данные |
; --------------------------------------------------- |
|
||
reclen equ |
32 |
|
|
namepar |
label |
byte |
;список параметров: |
maxlen |
db |
reclen |
; макс.длина имени |
namelen |
db |
? |
; число введенных символов |
namedta |
db |
reclen dup(' ') |
; область передачи (DTA) |
fcbrec |
label |
byte |
;FCB для дискового файла |
fcbdriv |
db |
04 |
; дисковод D |
fcbname |
db |
'namefile' |
; имя файла |
fcbext |
db |
'dat' |
; тип файла |
fcbblk |
dw |
0000 |
; номер текущего блока |
|
|
|
63 |
fcbrcsz |
dw |
? |
|
; размер логической записи |
|
fcbflsz |
|
dd |
? |
|
; размер файла (DOS) |
|
dw |
? |
|
|
; дата (DOS) |
|
dt |
? |
|
|
; зарезервировано (DOS) |
fcbsqrc |
db |
00 |
|
; номер текущей записи |
|
|
dd |
? |
|
|
; относительный номер |
crlf |
db |
13,10,'$' |
|
|
|
errcde |
|
db |
00 |
|
|
prompt |
db |
'Name? ','$' |
|
|
|
row |
db |
01 |
|
|
|
opnmsg |
db |
'*** Open error ***', '$' |
|||
wrtmsg |
db |
'*** Write error ***', '$' |
|||
; --------------------------------------------------------- |
|||||
main |
proc |
far |
|
|
|
|
mov |
ax,0600h |
|
|
|
|
call |
q10scr |
|
|
;очистить экран |
|
call |
q20curs |
|
;установить курсор |
|
|
call |
c10open |
|
;открыть, установить DTA |
|
|
cmp |
errcde,00 |
|
;есть место на диске? |
|
|
jz |
a20loop |
; |
да - продолжить, |
|
|
ret |
|
|
; |
нет - вернуться в DOS |
a20loop: |
|
|
|
|
|
|
call |
d10proc |
|
|
|
|
cmp |
namelen,00 |
|
;конец ввода? |
|
|
jne |
a20loop |
; |
нет продолжить, |
|
|
call |
g10clse |
; |
да закрыть файл |
|
|
ret |
|
|
; |
и вернуться в DOS |
main |
endp |
|
|
|
|
;Открытие дискового файла:
c10open |
proc near |
|
mov |
ah,16h |
;функция создания файла |
lea |
dx,fcbrec |
|
int |
21h |
|
cmp |
al,00 |
;есть место на диске? |
jnz |
c20 |
; нет ошибка |
mov |
fcbrcsz,reclen |
;размер записи (EQU) |
lea |
dx,namedta |
;загрузить адрес DTA |
mov |
ah,1ah |
|
int |
21ah |
|
ret |
|
|
c20: |
|
|
lea |
dx,opnmsg |
;сообщение об ошибке |
call |
x10err |
|
ret |
|
|
c10open |
endp |
|
;Ввод с клавиатуры:
d10proc |
proc |
near |
mov |
ah,09 |
;функция вывода на экран |
|
|
64 |
lea |
dx,prompt |
|
;выдать запрос |
int |
21h |
|
|
mov |
ah,0ah |
|
;функция ввода |
lea |
dx,namepar |
|
;ввести имя файла |
int |
21h |
|
|
call |
e10disp |
|
;прокрутка на экране |
cmp |
namelen,00 |
|
;имя введено? |
jne |
d20 |
; |
да продолжить, |
ret |
|
; |
нет выйти |
d20: |
|
|
|
mov |
bh,00 |
|
;заменить символ Return |
mov |
bl,namelen |
|
|
mov |
namedta[bx],' ' |
|
;записать пробел |
call |
f10writ |
|
;вызвать подпрограмму записи |
; |
|
|
|
cld |
|
|
|
lea |
di,namedta |
|
;очистить |
mov |
cx,reclen/2 |
|
; поле |
mov |
ax,2020h |
|
; имени |
rep |
stosw |
|
|
ret |
|
|
;выйти |
d10proc |
endp |
|
|
;Прокрутка и установка курсора:
e10disp |
proc near |
|
|
mov |
ah,09 |
|
;функция вывода на экран |
lea |
dx,crlf |
|
;CR/LF |
int |
21h |
|
;вызов DOS |
cmp |
row,18 |
|
;последняя строка экрана? |
jae |
e20 |
; |
да обойти, |
inc |
row |
; |
нет увеличить строку |
ret |
|
|
|
e20: |
|
|
|
mov |
ax,0601h |
|
;прокрутка на 1 строку |
call |
q10scr |
|
|
call |
q20curs |
|
;установить курсор |
ret |
|
|
|
e10disp |
endp |
|
|
;Запись на диск:
f10writ |
proc near |
|
|
mov |
ah,15h |
;функция записи |
|
lea |
dx,fcbrec |
|
|
int |
21h |
|
|
cmp |
al,00 |
;запись без ошибок? |
|
jz |
f20 |
; |
да |
lea |
dx,wrtmsg |
; |
нет |
call |
x10err |
; выдать сообщение |
|
mov |
namelen,00 |
|
|
65
f20: ret f10writ endp
;Закрытие дискового файла:
g10clse |
proc near |
|
mov |
namedta,1ah |
;установить конец файла EOF |
call |
f10writ |
|
mov |
ah,10h |
;функция закрытия |
lea |
dx,fcbrec |
|
int |
21h |
|
ret |
|
|
g10clse |
endp |
|
;Прокрутка экрана:
q10scr |
proc near |
;ax уже установлен |
mov |
bh,1eh |
;цвет желтый на синем |
mov |
cx,0000 |
|
mov |
dx,184fh |
|
int |
10h |
;прокрутка |
ret |
|
|
q10scr |
endp |
|
;Установка курсора:
q20curs |
proc near |
|
mov |
ah,02 |
|
mov |
bh,00 |
|
mov |
dl,00 |
|
mov |
dh,row |
;установить курсор |
int |
10h |
|
ret |
|
|
q20curs |
endp |
|
;Вывод сообщения об ошибке на диске:
x10err |
proc near |
|
mov |
ah,09 |
;dx содержит |
int |
21h |
; адрес сообщения |
mov |
errcde,01 |
;установить код ошибки |
ret |
|
|
x10err |
endp |
|
codesg |
ends |
|
end |
begin |
|
__________________________________________________________________________
Программа, приведенная в данном примере, создает дисковый файл по имени, которое вводится пользователем с клавиатуры. Блок FCB (fcbrec) в данной программе содержит следующие поля:
fcbdriv программа должна создать файл на диске 4 (или D). fcbname имя файла namefile.
fcbext тип файла dat.
fcbblk Начальное значение номера текущего блока 0.
fcbrcsz Размер записей не определен, так как операция открытия устанавливает в данном поле значение 128.
fcbsqrc -начальное значение номера текущей записи 0.
66
В программе организованы следующие процедуры:
begin вызывает c10open для создания файла и установки адреса DTA для DOS, вызывает d10proc для ввода имени файла. Если ввод пустой, то происходит вызов g10clse для завершения программы;
c10open создает для файла элемент в директории, устанавливает размер записей 32 (шест.20) и инициализирует адрес буфера DTA для DOS. d10proc выдает запрос на ввод имен, вводит имена с клавиатуры и
вызывает процедуру f10writ для записи вводимых имен на диск. e10dispуправляет прокруткой и установкой курсора. f10writ записывает имена в дисковой файл.
g10clse записывает маркер конца файла и закрывает файл.
x10err выдает на экран сообщение об ошибке в случае некорректной операции создания файла или записи данных.
Каждая операция записи автоматически добавляет 1 к fcbsqrc (номер текущей записи) и шест. 20 (размер записи) к fcbflsz (размер файла). Так как каждая запись имеет длину 32 байта, то операция заносит в буфер 16 записей и затем записывает весь буфер в сектор диска. Ниже показано содержимое DTA и буфера:
DTA: |текущая запись|
Буфер: |запись 00|запись 01|запись 02|...|запись 15| Если пользователь ввел 25 имен, то счетчик записей увеличится от 1 до 25
(шест.19). Размер файла составит:
25 * 32 байта = 800 байтов или шест. 320 Операция закрытия заносит во второй сектор оставшиеся в буфере девять
записей и изменяет в оглавлении дату и размер файла. Размер записывается байтами в переставленном порядке: 20030000. Последний буфер имеет следующий вид:
Буфер: |запись 16|запись 17|...|запись 24|шест.1A|...|...|
Для простоты в приведенной программе создаются записи файла, содержащие только одно поле. Записи большинства других файлов, однако, содержат различные символьные и двоичные поля и требуют описания записи в DTA. Если записи содержат двоичные числа, то не следует использовать маркер конца файла (EOF), так как двоичное число может совпасть с шест. кодом 1A.
Для того, чтобы сделать программу более гибкой, можно разрешить пользователю указать дисковод, на котором находится или будет находиться файл. В начале выполнения программа может выдать на экран сообщение, чтобы пользователь ввел номер дисковода, а затем изменить первый байт блока
FCB.
=================================================================
Пример П.1.5
; lab3pr5.asm программа, использующая FCB для чтения дискового файла
;Чтение записей созданных в CREATDSK
;-------------------------------------------------------
codesg segmentPARA 'Code'
assume cs:codesg,ds:codesg,ss:codesg,es:codesg
org |
100h |
; обход PSP |
begin: jmp |
main |
;обход через данные |
|
|
67 |
; --------------------------------------------------- |
|
|||
fcbrec label |
byte |
|
;FCB для файла |
|
fcbdriv |
db |
04 |
; дисковод D |
|
fcbname |
db |
'namefile' |
; имя файла |
|
fcbext |
|
db |
'dat' |
; тип файла |
fcbblk |
|
dw |
0000 |
; номер текущего блока |
fcbrcsz |
dw |
0000 |
; длина логической записи |
|
fcbflsz |
|
dd |
? |
; размер файла (DOS) |
|
|
dw |
? |
; дата (DOS) |
|
|
dt |
? |
; зарезервировано (DOS) |
fcbsqrc |
db |
00 |
; текущий номер записи |
|
|
|
dd |
? |
; относительный номер |
reclen |
|
equ |
32 |
;длина записи |
namefld |
db |
reclen dup(' '), 13, 10, '$' |
||
endcde |
db |
00 |
|
|
openmsg |
db |
'*** Open error ***', '$' |
||
readmsg |
db |
'*** Read error ***', '$' |
||
row |
db |
00 |
|
|
main |
proc |
far |
|
|
|
mov |
ax,0600h |
|
|
|
call |
q10scr |
|
;очистить экран |
|
call |
q20curs |
;установить курсор |
|
|
call |
e10open |
;открыть файл и установить DTA |
|
|
cmp |
endcde,00 |
;открытие без ошибок? |
|
|
jnz |
a90 |
; |
нет завершить |
a20loop: |
|
|
|
|
|
call |
f10read |
;прочитать запись |
|
|
cmp |
endcde,00 |
;чтение без ошибок? |
|
|
jnz |
a90 |
; |
нет выйти |
|
call |
g10disp |
;выдать имя на экран |
|
|
jmp |
a20loop |
;продолжить |
|
a90: |
ret |
|
|
;завершить |
begin |
endp |
|
|
|
;Открытие файла на диске:
e10open |
proc near |
|
lea |
dx,fcbrec |
|
mov |
ah,0fh |
;функция открытия |
int |
21h |
|
cmp |
al,00 |
;файл найден? |
jnz |
e20 |
; нет ошибка |
mov |
fcbrcsz,reclen |
;длина записи (EQU) |
mov |
ah,1ah |
|
lea |
dx,namefld |
;адрес DTA |
int |
21h |
|
ret |
|
|
e20: |
|
|
mov |
endcde,01 |
;сообщение об ошибке |
lea |
dx,openmsg |
|
call |
x10error |
|
|
|
68 |
ret e10open endp
;Чтение дисковой записи:
f10read |
proc near |
|
|
mov |
ah,14h |
;функция чтения |
|
lea |
dx,fcbrec |
|
|
int |
21h |
|
|
cmp |
namefld,1ah |
;считан маркер EOF? |
|
jne |
f20 |
; |
нет |
mov |
endcde,01 |
; |
да |
jmp |
f90 |
|
|
f20: |
|
|
|
cmp |
al,00 |
;чтение без ошибок? |
|
jz |
f90 |
; |
да выйти |
mov |
endcde,01 |
; |
нет: |
cmp |
al,01 |
;конец файла? |
|
jz |
f90 |
; |
да выйти, |
lea |
dx,readmsg |
; |
нет значит |
call |
x10err |
; ошибка чтения |
|
f90: |
|
|
|
ret |
|
|
|
f10read |
endp |
|
|
;Вывод записи на экран:
g10disp |
proc near |
|
|
mov |
ah,09 |
;функция вывода на экран |
|
lea |
dx,namefld |
|
|
int |
21h |
|
|
cmp |
row,20 |
;последняя строка экрана? |
|
jae |
g30 |
; |
нет... |
inc |
row |
; |
да увеличить строку |
jmp |
g90 |
|
|
g30: |
|
|
|
mov |
ax,0601h |
|
|
call |
q10scr |
; прокрутить |
|
call |
q20curs |
; установить курсор |
|
g90: ret |
|
|
|
g10disp |
endp |
|
|
;Прокрутка (скроллинг) экрана:
q10scr |
proc near |
;ax уже установлен |
mov |
bh,1eh |
;установить цвет |
mov |
cx,0000 |
|
mov |
dx,184fh |
;функция прокрутки |
int |
10h |
|
ret |
|
|
q10scr |
endp |
|
;Установка курсора:
q20curs |
proc near |
mov |
ah,02 |
mov |
bh,00 |
69
mov dh,row mov dl,00 int 10h ret
q20curs endp
;Вывод сообщения об ошибке на диске:
x10err |
proc |
near |
mov |
ah,09 |
;dx содержит адрес |
int |
21h |
; сообщения |
ret |
|
|
x10err |
endp |
|
codesg |
ends |
|
end |
begin |
|
В данном примере приведена программа, которая выполняет чтение файла, созданного предыдущей программой, и вывод на экран имен из записей файла. Обе программы содержат идентичные блоки FCB, хотя имена полей FCB могут быть различны. Содержимое полей имени и типа файла должны быть одинаковы. Программа содержит следующие процедуры:
begin вызывает процедуру e10open для открытия файла и установки DTA и вызывает f10read для чтения записей. Если считан маркер конца файла, то программа завершается, если нет, то вызывается процедура g10disp.
e10open открывает файл, устанавливает значение размера и записей, равное 32 (шест. 20), и инициализирует адрес DTA.
f10read выполняет последовательное чтение записей. Операция чтения автоматически увеличивает номер текущей записи в блоке FCB.
g10disp выводит на экран содержимое прочитанной записи.
x10err выводит на экран сообщение об ошибке в случае некорректной операции открытия или чтения.
Операция открытия выполняет поиск имени и типа файла в оглавлении. Если необходимый элемент оглавления найден, то автоматически в блок FCB заносятся размер файла, дата и длина записей. Первая операция чтения записи с номером 00 получает доступ к диску и считывает весь сектор (16 записей) в буфер. После этого первая запись заносится в DTA, а номер текущей записи в FCB увеличивается с 00 до 01:
Буфер: |запись 00|запись 01|запись 02|... |запись 15| DTA : |запись 00|
Второй операции чтения нет необходимости обращаться к диску. Так как требуемая запись уже находится в буфере, то операция просто пересылает запись 01 из буфера в DTA и увеличивает номер текущей записи на единицу.
Таким же образом выполняются следующие операции чтения, пока все 16 записей из буфера не будут обработаны.
Операция чтения 16-й записи приводит к физическому чтению следующего сектора в буфер и пересылке первой записи сектора в DTA. Последующие операции чтения переносят остальные записи из буфера в DTA. Попытка прочитать после последней записи вызовет состояние конца файла и в регистр al будет записан код возврата шест. 01.
70