В 8-м уроке
мы узнали, как можно записывать информацию в файлы. Давайте попробуем
создать патч для обновления нашей программы. Допустим, у нас есть программа из второго урока,
выводящая на экран строчку "Hello, world!". Нам нужно написать
обновление для неё. Вместо строки "Hello, world!", после выполнения
нашего патча, будем выводить строку "Goodbye, world!".
Из второго урока возьмём исходный код, сохраним в файле hello.asm в кодировке 866 и скомпилируем его в файл hello.com: ml hello.asm /AT.
Текст программы из урока 2: ;Всё, что следует за значком ";" - это комментарий.
CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный ;сегмент
в памяти программ по определённому адресу. ;Имя нашего сегмента будет CSEG.
assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для ;вычисления действующего адреса для всех меток и переменных, опре- ;делённых для сегмента или группы сегментов с указанным именем. ;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш ;единственный сегмент (мы его назвали CSEG).
org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот- ;ветствии с адресом, задаваемым "выражением". ;Сейчас этот счётчик равен 100h - используется для всех программ ;типа .com
begin: ;Метка начала программы.
;Выводим фразу, используя функцию 9 прерывания 21h. mov ah,9 ;Загружаем в регистр ah число 9 (указываем функцию). mov dx,offset helloworld ;Указываем, что за фразу мы будем
выводить. int 21h ;Выводим фразу.
int 20h ;Выходим в DOS.
helloworld db 'Hello, world!$' ;Определяем переменную helloworld, доступную побайтно, с фразой ;"Hello, world!". В одинарных кавычках, после знака "!" ставим ;знак "$".
CSEG ends ;Указываем на завершение сегмента CSEG. end begin ;Конец программы.
Теперь создаём программу
test.com, позволяющую исправить наш файл, не считывая его в память:
;Всё, что следует за значком ";" - это комментарий.
CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный ;сегмент в памяти
программ по определённому адресу. ;Имя нашего сегмента будет CSEG.
assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для ;вычисления действующего адреса для всех меток и переменных, опре- ;делённых для сегмента или группы сегментов с указанным именем. ;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш ;единственный сегмент (мы его назвали CSEG).
org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот- ;ветствии с адресом, задаваемым "выражением". ;Сейчас этот счётчик равен 100h - используется для всех программ ;типа .com
begin: ;Метка начала программы.
;Данная программа будет обновлять файл ;hello.com.
mov ax,3D02h ;Первоначально откроем файл для чтения-записи. mov dx,offset File_name int 21h jc
Message_bad
mov Handle,ax ;Если файл существует, сохраняем номер файла. mov bx,ax
mov dx,9h ;Устанавливаем указатель с начала файла +9h. mov cx,0h ;+9h мы указали потому, что просмотрели программу mov ax,4200h ;hello.com в отладчике afdpro и увидели, что int 21h ;начало строки "Hello, world!" находит по этому адресу.
mov
ah,40h ;Записываем 16 символов (в т.ч.$) из смещения New_phrase mov dx,offset New_phrase ;по адресу, указанному выше. mov cx,16 int 21h
Message_ok: ;Подпрограмма успешного вывода строки и выхода. mov bx,Handle ;Сначала сохраним файл, восстановив bx. mov ah,3Eh ;Закроем файл. int 21h
mov ah,9 ;Выводим строку. mov dx,offset Mess_ok int
21h int 20h
Message_bad: ;Подпрограмма вывода сообщения об ошибке и выхода. mov ah,9 mov dx,offset Mess_bad int 21h int 20h
File_name db 'hello.com',0,'!$' ;Имя файла.
Mess_ok db 'Файл обновлён. Всем спасибо.$' ;Успешное сообщение и сообщение об ошибке (ниже).
Mess_bad db 'Файл не найден. Поместите hello.com в каталог с программой.$'
Handle dw 0FFFFh ;Определяем
переменную (для идентификатора файла).
New_phrase db 'Goodbye, world!$' ;Новая фраза, которая заменяет "Hello, world!".
CSEG ends end begin Комментариев мы уже используем чуть меньше, но думаю, если вы разобрались с 8-м
уроком, то здесь всё понятно. Теперь усложним задачу.
Задача
будет аналогичная, но файл должен быть загружен в память, и там
изменён. Жирным шрифтом, по традиции, указываем отличия от предыдущего
листинга.
;Всё, что следует за значком ";" - это комментарий.
CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный ;сегмент в памяти программ по определённому адресу. ;Имя нашего сегмента будет CSEG.
assume
cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для ;вычисления действующего адреса для всех меток и переменных, опре- ;делённых для сегмента или группы сегментов с указанным именем. ;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш ;единственный сегмент (мы его назвали CSEG).
org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот-
;ветствии с адресом, задаваемым "выражением". ;Сейчас этот счётчик равен 100h - используется для всех программ ;типа .com
begin: ;Метка начала программы.
;Данная программа будет обновлять файл ;hello.com.
mov ax,3D02h ;Первоначально откроем файл для чтения-записи. mov dx,offset File_name int 21h jc Message_bad
mov Handle,ax ;Если файл существует, сохраняем номер файла. mov bx,ax
mov ah,3Fh ;Чтение в память. mov cx,19h ;Определяем количество читаемых байт (напр.25 - будущая длина hello.com). mov dx,offset Finish ;по адресу памяти за нашей программой. int 21h
;Файл в нашей памяти. Строка Hello, world находится в db ;в смещении 109h от начала программы hello, т.е. через 9 байт ;У нас она располагается
тоже через 9 байт, но после конца (Finish equ $) ;Определим её настоящий адрес:
add dx,9h
mov si,offset New_phrase ;Переносим 16 букв c адреса смещения переменной New_phrase mov di,dx ;на новый адрес dx (конец нашей програмы + 9 байт). mov cx,16 rep movsb
mov dx,0h ;Устанавливаем указатель с начала файла +0h. mov cx,0h ;+0h мы указали
потому, что будем mov ax,4200h ;перезаписывать файл hello.com с первого байта. int 21h ;
mov ah,40h ;Записываем 25 символов (в т.ч.$) из смещения Finish mov dx,offset Finish ;по адресу, указанному выше. mov cx,25 int 21h ;Смотрим отладчик - файл прочтён и строка в памяти
заменилась!
Message_ok: ;Подпрограмма успешного вывода строки и выхода. mov bx,Handle ;Сначала сохраним файл, восстановив bx. mov ah,3Eh ;Закроем файл. int 21h
mov ah,9 ;Выводим строку. mov dx,offset Mess_ok int 21h int 20h
Message_bad: ;Подпрограмма вывода сообщения об ошибке и выхода. mov ah,9 mov dx,offset Mess_bad int 21h int 20h
File_name
db 'hello.com',0,'!$' ;Имя файла.
Mess_ok db 'Файл обновлён. Всем спасибо.$' ;Успешное сообщение и сообщение об ошибке (ниже).
Mess_bad db 'Файл не найден. Поместите hello.com в каталог с программой.$'
Handle dw 0FFFFh ;Определяем переменную (для идентификатора файла).
New_phrase db 'Goodbye, world!$' ;Новая фраза, которая заменяет "Hello, world!".
Finish equ $ ;Метка конца нашей
программы!
CSEG ends end begin Новое в программе: mov ah,3Fh — Изучаем функцию 3fh прерывания 21h: HELP.EXE -> Указатель функций DOS/BIOS -> Функции DOS -> 3fH Read File. rep movsb и предыдущие три строки — программка переноса данных из одного места в другое. Разберёмся с ней чуть позже. Finish equ $ — Новая для нас инструкция. Является меткой конца файла. При использовании mov dx,offset
Finish в dx записывается адрес смещения данной метки — т.е. конца программы. Рекомендуем проверить это в отладчике AFDPro.