Рассылка закрыта
При закрытии подписчики были переданы в рассылку "RFpro.ru: Ассемблер? Это просто! Учимся программировать" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
← Октябрь 2014 → | ||||||
1
|
2
|
3
|
4
|
5
|
||
---|---|---|---|---|---|---|
7
|
8
|
9
|
10
|
11
|
12
|
|
14
|
15
|
16
|
17
|
18
|
19
|
|
21
|
22
|
23
|
24
|
25
|
26
|
|
28
|
29
|
30
|
31
|
Статистика
0 за неделю
Уроки ассемблеру. Быстро и просто. Урок 15
Вот что подумал, друзья. А давайте изменим нашу программу из 14 урока так, чтобы наш файл был загружен в память и там изменён. Помните, мы это проделывали в 11-м уроке? Для самых любопытных опубликую листинг программы ниже. Листинг программы, читающей файлы в память: .286 ;Разрешает ассемблирование непривилегированных инструкций ;процессора 80286 (реальный режим) и инструкций арифметического ;сопроцессора 80287. CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный ;сегмент в памяти программ по определённому адресу. ;Имя нашего сегмента будет CSEG. assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для ;вычисления действующего адреса для всех меток и переменных, опре- ;делённых для сегмента или группы сегментов с указанным именем. ;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш ;единственный сегмент (мы его назвали CSEG). org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот- ;ветствии с адресом, задаваемым "выражением". ;Сейчас этот счётчик равен 100h - используется для всех программ ;типа .com begin: ;Метка начала программы. mov ah,1Ah ;Установим PSP в конец файла. mov dx,offset Finish int 21h call Find_first ;Ищем первый файл. jc Error_file_metka ;Нет txt-файлов - на выход. goto_cikl: ;Начало цикла. mov ax,3D02h ;Загружаем в регистр ah число 3Dh (функция открытия ;файла с записью), а в al число 02h (пишем в конец). ;Можно было записать и так - mov ah,3Dh ;mov al,02h mov dx,offset Finish ;Указываем адрес файла в DTA (по умолчанию он 80h от начала PSP, add dx,1Eh ;+1Eh - наше имя файла. int 21h ;Выполняем функцию. mov Handle,ax ;При открытии файлу будет присвоен номер, его и ;сохраняем для дальнейших действий, mov bx,ax ;а заодно и сохраняем его в bx. mov ah,3Fh ;Читаем файл mov cx,1024 ;с длиной 1Кб. mov dx,offset Finish add dx,100h ;DX устанавливаем за PSP. int 21h ;Выполняем функцию. push ax ;Запомним длину файла в стек. mov dx,offset Finish add dx,100h add dx,ax ;В ax-число действительно прочитанных байт. mov si,offset one ;Устанавливаем si на символ, который мы будем добавлять. mov di,dx ;Устанавливаем di туда, куда мы будем осуществлять перенос. mov cx,1 ;Переносим 1 байт. rep movsb ;Переносим! mov ax,4200h ;Используем функцию установки указателя. ;al=00h - устанавливаем в начало. mov cx,0 ;Нам надо записать прямо в начало файла, поэтому mov dx,0 ;обнулим cx и dx (иначе будет писать далее на ;значение (CX * 65536) + DX int 21h ;Выполняем функцию. mov ah,40h ;Используем функцию записи в файл. mov dx,offset Finish add dx,100h ;В dx - адрес начала считанного файла. pop cx ;Число записываемых байт. inc cx ;На один больше. int 21h ;Выполняем функцию. mov ah,3Eh ;Используем функцию закрытия файла. mov bx,Handle ;Для закрытия обязательно "вспоминаем" его номер, ;номер у нас был сохранён в Handle. int 21h ;Выполняем функцию. call Find_next ;Ищем следующий файл. jnc goto_cikl ;Нашли ещё один файл; прыгаем на метку. mov ah,9 ;Загружаем в регистр ah число 9 (указываем функцию). mov dx,offset Vse_ok ;Указываем, что за фразу мы будем выводить. int 21h ;Выводим фразу. mov ah,4Ch ;Используем для выхода из программы. int 21h Error_file_metka: ;Вывод сообщения об ошибке. mov ah,9 ;Загружаем в регистр ah число 9 (указываем функцию). mov dx,offset Error_file ;Указываем, что за фразу мы будем выводить. int 21h ;Выводим фразу. mov ah,4Ch ;Используем для выхода из программы. int 21h Find_first proc ;Подпрограмма поиска первого файла. mov ah,4Eh ;Ищем первый файл по маске (функция 4Eh). xor cx,cx ;Атрибуты обычные. Смотрим, что в CX. mov dx,offset File_name ;Адрес маски в DS:DX int 21h ;В DTA заносится имя найденного файла. ret Find_first endp ;Подпрограмма поиска следующего файла. Find_next proc mov dx,offset Finish ;DS:DX указывают на DTA. xor cx,cx ;CX=0. mov ah,4Fh ;4Fh - поиск следующего файла. int 21h ;В DTA заносится имя найденного файла. ret Find_next endp File_name db '*.txt',0 ;Маска файла. Handle dw 0FFFFh ;Определяем переменную Handle, которую используем для ;хранения номера файла. По умолчанию она равна 0FFFFh. one db '1' ;Определяем переменную one, содержащую символ "1", ;которую мы будем приписывать в конец файла. Vse_ok db 'Файлы найдены и обновлены. Всем спасибо.$' Error_file db 'Файлы не найдены. Поместите файлы *.txt в каталог с программой.$' Finish equ $ ;Метка конца программы. CSEG ends end begin Пожалуйста, помните, что эта программа будет корректно работать с файлами длиной не более 1Кб. А вообще, максимальная длина программы типа .СОМ составляет 65536 байт минус длина префикса (256 байт) и обязательное слово стека (2 байта). Когда управление передается программе типа .СОМ, все регистры указывают на префикс. В указатель стека SР, если позволяет память, помещается число 0FFFFН, в противном случае - максимальный адрес памяти минус 2 байта. (DOS при входе в программу помещает в стек нулевое слово). Друзья, чтобы продолжить изучение нами ассемблера, нам нужно разобраться
в форматах хранения данных и организации массивов. С массивами на самом
деле всё проще, чем в языках высокого уровня. mas dd 1,2,3,4,5 2) Используя оператор повторения dup. К примеру: ;Размер каждого элемента 2 байта: mas dw 5 dup (0) Такой способ определения используется для резервирования памяти с целью размещения и инициализации элементов массива. Также существуют способы с использованием директив label и rept, а также цикла для инициализации значениями области памяти, которую можно будет впоследствии трактовать как массив. Доступ к элементам массива При работе с массивами необходимо чётко представлять себе, что все элементы массива располагаются в памяти компьютера последовательно. Само по себе такое расположение ничего не говорит о назначении и порядке использования этих элементов. И только лишь программист с помощью составленного им алгоритма обработки определяет, как нужно трактовать эту последовательность байт, составляющих массив. Так, одну и ту же область памяти можно трактовать как одномерный массив, и одновременно те же самые данные можно трактовать как двумерный массив. Всё зависит только от алгоритма обработки этих данных в конкретной программе. Сами по себе данные не несут никакой информации о своём “смысловом”, или логическом, типе. Помните об этом принципиальном моменте. Эти же соображения можно распространить и на индексы элементов массива. Ассемблер не подозревает об их существовании и ему абсолютно всё равно, каковы их численные смысловые значения. Для того чтобы локализовать определённый элемент массива, к его имени нужно добавить индекс. Так как мы моделируем массив, то должны позаботиться и о моделировании индекса. В языке ассемблера индексы массивов — это обычные адреса, но с ними работают особым образом. Другими словами, когда при программировании на ассемблере мы говорим об индексе, то скорее подразумеваем под этим не номер элемента в массиве, а некоторый адрес. Давайте ещё раз обратимся к описанию массива. К примеру, в программе статически определена последовательность данных: Пусть эта последовательность чисел трактуется как одномерный массив. Размерность каждого элемента определяется директивой dw, то есть она равна 2 байта. Чтобы получить доступ к числу 6677h, нужно к адресу массива прибавить 6. Нумерация элементов массива в ассемблере начинается с нуля. В общем случае для получения адреса элемента в массиве необходимо начальный (базовый) адрес массива сложить с произведением индекса этого элемента на размер элемента массива: база + (индекс*размер элемента) Архитектура микропроцессора предоставляет достаточно удобные программно-аппаратные средства для работы с массивами. К ним относятся базовые и индексные регистры, позволяющие реализовать несколько режимов адресации данных. Используя данные режимы адресации, можно организовать эффективную работу с массивами в памяти. Выше мы рассматривали программу по изменению файла в памяти. А почему бы для этих целей нам не использовать массив? Вот как будет выглядеть программа после такого нововведения: .286 ;Разрешает ассемблирование непривилегированных инструкций ;процессора 80286 (реальный режим) и инструкций арифметического ;сопроцессора 80287. CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный ;сегмент в памяти программ по определённому адресу. ;Имя нашего сегмента будет CSEG. assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для ;вычисления действующего адреса для всех меток и переменных, опре- ;делённых для сегмента или группы сегментов с указанным именем. ;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш ;единственный сегмент (мы его назвали CSEG). org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот- ;ветствии с адресом, задаваемым "выражением". ;Сейчас этот счётчик равен 100h - используется для всех программ ;типа .com begin: ;Метка начала программы. mov ah,1Ah ;Установим PSP в конец файла. mov dx,offset Finish int 21h call Find_first ;Ищем первый файл. jc Error_file_metka ;Нет txt-файлов - на выход. goto_cikl: ;Начало цикла. mov ax,3D02h ;Загружаем в регистр ah число 3Dh (функция открытия ;файла с записью), а в al число 02h (пишем в конец). ;Можно было записать и так - mov ah,3Dh ;mov al,02h mov dx,offset Finish ;Указываем адрес файла в DTA (по умолчанию он 80h от начала PSP, add dx,1Eh ;+1Eh - наше имя файла. int 21h ;Выполняем функцию. mov Handle,ax ;При открытии файлу будет присвоен номер, его и ;сохраняем для дальнейших действий, mov bx,ax ;а заодно и сохраняем его в bx. mov ah,3Fh ;Читаем файл mov cx,1024 ;с длиной 1Кб. mov dx,offset massive int 21h ;Выполняем функцию. push ax ;Запомним длину файла в стек. mov dx,offset massive ;В ax-число действительно прочитанных байт. add dx,ax ;В dx - конец прочитанного файла. mov si,offset one ;Устанавливаем si на символ, который мы будем добавлять. mov di,dx ;Устанавливаем di туда, куда мы будем осуществлять перенос. mov cx,1 ;Переносим 1 байт. rep movsb ;Переносим! mov ax,4200h ;Используем функцию установки указателя. ;al=00h - устанавливаем в начало. mov cx,0 ;Нам надо записать прямо в начало файла, поэтому mov dx,0 ;обнулим cx и dx (иначе будет писать далее на ;значение (CX * 65536) + DX int 21h ;Выполняем функцию. mov ah,40h ;Используем функцию записи в файл. mov dx,offset massive ;В dx - адрес начала считанного файла. pop cx ;Число записываемых байт. inc cx ;На один больше. int 21h ;Выполняем функцию. mov ah,3Eh ;Используем функцию закрытия файла. mov bx,Handle ;Для закрытия обязательно "вспоминаем" его номер, ;номер у нас был сохранён в Handle. int 21h ;Выполняем функцию. call Find_next ;Ищем следующий файл. jnc goto_cikl ;Нашли ещё один файл; прыгаем на метку. mov ah,9 ;Загружаем в регистр ah число 9 (указываем функцию). mov dx,offset Vse_ok ;Указываем, что за фразу мы будем выводить. int 21h ;Выводим фразу. mov ah,4Ch ;Используем для выхода из программы. int 21h Error_file_metka: ;Вывод сообщения об ошибке. mov ah,9 ;Загружаем в регистр ah число 9 (указываем функцию). mov dx,offset Error_file ;Указываем, что за фразу мы будем выводить. int 21h ;Выводим фразу. mov ah,4Ch ;Используем для выхода из программы. int 21h Find_first proc ;Подпрограмма поиска первого файла. mov ah,4Eh ;Ищем первый файл по маске (функция 4Eh). xor cx,cx ;Атрибуты обычные. Смотрим, что в CX. mov dx,offset File_name ;Адрес маски в DS:DX int 21h ;В DTA заносится имя найденного файла. ret Find_first endp ;Подпрограмма поиска следующего файла. Find_next proc mov dx,offset Finish ;DS:DX указывают на DTA. xor cx,cx ;CX=0. mov ah,4Fh ;4Fh - поиск следующего файла. int 21h ;В DTA заносится имя найденного файла. ret Find_next endp File_name db '*.txt',0 ;Маска файла. Handle dw 0FFFFh ;Определяем переменную Handle, которую используем для ;хранения номера файла. По умолчанию она равна 0FFFFh. one db '1' ;Определяем переменную one, содержащую символ "1", ;которую мы будем приписывать в конец файла. massive db 1024 dup(0) ;Массив massive длиной 1 Кб. Vse_ok db 'Файлы найдены и обновлены. Всем спасибо.$' Error_file db 'Файлы не найдены. Поместите файлы *.txt в каталог с программой.$' Finish equ $ ;Метка конца программы. CSEG ends end begin
|
В избранное | ||