Калашников.ru - Ассемблер? Это просто!.. (Выпуск № 026)

InterReklama Advertising

Ассемблер? Это просто! Учимся программировать

______________________________________

Выпуск N 026 (Оболочка)

Доброе время суток, дорогие подписчики!

Сегодня в номере:

Информация для новых подписчиков

Ваши письма.

История развития IBM-совместимых компьютеров (кратко)

Оболочка

!!! Сюрприз !!!

Информация для новых подписчиков Благодарю Вас, что подписались на рассылку "Ассемблер? Это просто! Учимся программировать". Надеюсь, что Вы не останетесь равнодушны к ней и почерпнете море полезной информации, а также повысите свой уровень в "общении" с IBM-совместимыми компьютерами.

Прежде, чем приступать к изучению материала в данном выпуске, Вам необходимо внимательно ознакомиться с предыдущими. Я уверен, что Вы очень быстро и без труда догоните остальных подписчиков, не смотря на то, что у нас уже 26 выпуск.

Все, что нужно для изучения Ассемблера (предыдущие выпуски, адреса экспертов, необходимые программы, документацию, а также многое другое), можно найти на сайте http://www.Kalashnikoff.ru. Рекомендую Вам сперва ознакомиться с разделом "Информация для новых подписчиков".

Если у Вас нет выхода в Сеть, то предыдущие выпуски рассылки, информацию для новеньких и адреса экспертов можно получить по почте, направив пустое письмо по адресу AssmIssues@Kalashnikoff.ru. Информация (350 Кб) будет выслана Вам в течение двух рабочих дней с момента получения Вашего письма. Однако, пожалуйста, не злоупотребляйте этим, т. к. высылка писем подобного объема несет существенную нагрузку на почтовые сервера.

Ваши письма. В предыдущих выпусках рассылки я писал про язык С и, видимо, допустил ошибку. Заострю на этом внимание, т.к. мне кажется, что многим подписчикам данная информация будет полезна (учитывая то, что вскоре приступим к Windows + С). Ко мне пришло письмо от Алексея следующего содержания (привожу выдержки):

Здравствуйте, уважаемый Олег!

Вы действительно ошиблись по поводу языка C. Файл stdio.h (и другие *.h) не содержит самих функций, они находятся в различных (в зависимости от модели памяти) библиотечных файлах. А файлы *.h (они называются заголовочными) содержат прототипы (заголовки) функций и представляют собой обычные текстовые файлы.

Прототип показывает компилятору, какого типа, в каком порядке и каким образом (через стек или регистры) функции передаются аргументы, а так же какого типа функция возвращает значения. Можно в библиотеку включить свои функции, написанные на C или ассемблере, и пользоваться этими функциями как стандартными, создав свой заголовочный файл.

Практически каждый стандартный заголовочный файл содержит несколько десятков прототипов, но в код программы включаются ТОЛЬКО ИСПОЛЬЗУЕМЫЕ В ДАННОЙ ПРОГРАММЕ ФУНКЦИИ.

Причины большего объёма программы, написанной на C в другом.

Во-первых, в каждую C-программу включается так называемый загрузчик C (находящийся в файлах c0?.obj, различных в зависимости от модели памяти). С него начинается работа программы, затем вызывается функция main С-программы, завершает программу опять же загрузчик C. А в нём содержатся обработчики некоторых прерываний, переменные, определение версии операционной системы и много ещё чего (в комплект поставки C/C++ входит файл c0.asm, так что его можно посмотреть).

Во-вторых, ... Впрочем, я начинаю увлекаться. Ведь это не рассылка по C/C++, а я не автор. Хочется только добавить, что С-компилятор генерирует довольно эффективный код, в этом можно убедиться, задав при компиляции ключ -S и посмотрев листинг на ассемблере.

____________

Некотрые уточнения по поводу 386/486 SX/DX машин. В принципе, пришло несколько подобных писем (от: Serge I.Driantsov, Дмитрий Сердюк). Однако, письмо Максима Белова более-менее точно описывало ситуацию с SX/DX машинами:

...С процессорами ситуация несколько иная была.

1. 486 начались с 16 или с 20 или с 25 МГц (у меня на рабочем ящике стоял такой уродец - 486SX-25).

2. 486 DX-33 (домашний первый ящик) - сопроцессор был уже там.

3. 386 серия - совершенно отдельная песня. Если в 486 сопроцессор был встроен и отключен (процент выхода годных чипов), то в 386 индекс DX/SX говорит о ширине внешней шины данных процессора - 32/16 бит. (как 8086/8088 - 16 разрядный внутри, а вот снаружи 16/8).

4. Что-то я еще слышал про 486SX/387DX связку, но плохо себе это представляю, как это возможно.

5. Говоря о выпайке, может быть, имелось в виду выпаивание распаянного на материнской плате процессора?

___________

Мой комментарий.

1. Читайте следующий раздел: "История развития IBM-совместимых компьютеров".

3. Полностью согласен, что индекс SX в 386 машинах свидетельствовал о разрядности шины.

4. Дело в том, что 487 сопроцессор ничем не отличается от 387. Поэтому структура и принцип работы, а также команды 387 сопроцессора аналогичны 487. Иначе говоря, в "четверках" стоял обычный "трешный" сопроцессор, только располагался он на одном кристалле с основным процессором.

5. Лично я купил в свое время 386DX-40, рассчитывая, что в нем присутствует сопроцессор (теперь понятно, что я ошибался). При вскрытии системного блока, оказалось, что сопроцессор действительно присутствовал когда-то, но его выпаяли. Это было явно видно на разъеме...

____________

Granit прислал алгоритм вывода десятичных чисел на экран. Спасибо!

Но! Дело в том, что максимальное число, которое может вывести данная процедура, гораздо меньше, чем то, которое выводим мы в нашем примере. Тем не менее, я опубликовываю данный алгоритм (без проверки с моей стороны):

DEC.ASM

p486 ideal model small dataseg dest db 5 dup (0) source dd 0ffffffffh codeseg start: mov ax,@data mov ds,ax mov es,ax mov eax,[source] mov ecx,32 ;-------------------------------------- next: push cx mov cx,5 mov bx,offset dest ;---------------------------- j0: mov dl,[bx] and dl,0fh cmp dl,5 jc short j1 add [byte bx],3 j1: mov dl,[bx] and dl,0f0h cmp dl,80 jc short j2 add [byte bx],48 j2: inc bx loop j0 ;---------------------------- shl eax,1 mov cx,5 mov bx,offset dest +4 j3: rcl [byte bx],1 dec bx loop j3 ;-------------------------------------- pop cx loop next ;==================================================================== mov ecx,5 mov bx,offset dest next2: push cx mov dl,[bx] and dl,0f0h ror dl,4 add dl,48 mov al,0 mov ah,2 int 21h mov dl,[bx] and dl,0fh add dl,48 mov al,0 mov ah,2 int 21h inc bx pop cx loop next2 mov ax,4c00h int 21h stack 200h end start

ВНИМАНИЕ! Это - типичный формат *.exe-файла. Ассемблировать его нужно следующим образом:

ML.EXE DEC.ASM

Или:

TASM DEC.ASM

TLINK DEC.OBJ

И все! Никаких параметров! В результате должен получиться DEC.EXE. Судя по структуре файла, видно, что Granit использует TASM...

___________

Добрый день или другое какое время суток!

Я решил узнать, что же такое Ассемблер и подписался на рассылку, не забыв и заказать предыдущие выпуски. Но вот одно немного смущает еще до начала всех проб и ошибок. Как-то кто-то говорил мне, что для программирования на Ассемблере надо знать архитектуру именного того процессора, для которого пишется код на Ассемблере: Pentium ли это, Pentium Pro, Pentium-II или Pentium-III. А у меня дома машина на AMD-K6 бегает. Действительно ли этот процессор имеет свою систему команд, и правда ли то, что книги или рассылки по программированию на Ассеблере для Пентиума или 486 ничем не помогут мне, так как я хочу работать на AMD-K6? Спасибо заранее за разъяснения.

С уважением, Michael.

___________

Мой комментарий.

Уважаемый Михаил!

Дело в том, что в 1981 году появились первые компьютеры на базе процессора Intel. Назывались они IBM PC. Со временем появились IBM PC/XT, IBM PC/Jr, IBM PC/AT, IBM PS/2 и т.д. Главное не это. Главное то, что программа, написанная для процессора Intel 8086 должна работать и на Pentium-III, т.к. Pentium - это процессор, совместимый с 8086. Иначе говоря, совместимость программ идет снизу вверх. Например, многие любят старые XT-шные игры: Pirates, Civilization, King's Bounty, Arcanoid и пр. Естественно, они были написаны для 8086 процессора, но работают также на 386-Pentium. AMD - это IBM-совместимый компьютер. Это означает, что программы, написанные для IBM PC/XT, будут работать на Pentium, на AMD и на других IBM-совместимых компьютерах. Отсюда правило - если Вы пишите программу, используя инструкции 8086 процессора, то я даю гарантию (точнее не я, а фирма-производитель процессоров), что они будут корректно работать на последующих процессорах (80286, 80386, AMD, Compaq и пр.).

___________

Хочу выразить благодарность Сергею Юрьевичу за проделанную работу по переводу *.txt-файлов в формат DOC. Спасибо!

История развития IBM-совместимых компьютеров Если я не ошибаюсь, то ситуация такая:

Процессор Тип (SX/DX) ОЗУ Винчестер (Мб) Мегагерцы (Mhz) Монитор Отечественный вариант 8086/8088 --- 256-640 Кб 10-20 4,77 - 8 Mono, CGA ЕС-1840, 1841, 1851 и пр. 80286 --- до 4 Мб 20-60 6 - 16 CGA - VGA EC-1849 80386 SX/DX 4-8 Мб 40-120 16-40 EGA - VGA EC-1863 80486 SX/DX 4-12 Мб 80-370 25-120 EGA - VGA Не помню Pentium --- 16 Мб 300-800 60 VGA - sVGA --- Некоторые комментарии. Приведенную в таблице информацию не следует считать обязательной для того или иного компьютера. Например, при желании можно было укомплектовать такую машину: IBM 486 SX-25, 10Мб - винчестер, 32Мб - ОЗУ, MDA - монитор...

Что же касается отечественных вариантов, то могу сказать, что лучшим конструкторским решением (не беру во внимание форму системного блока и шум вентилятора) можно считать ЕC-1849 (12Mhz, 640 Кб основной + 384 КБ расширенной (XMS) памяти, 40Мб винчестер, EGA - изначальная конфигурация завода МЗВТ). Самыми неудачными оказались: ЕС-1840 (не имел возможности подключать винчестер вообще, содержал ошибки в ПЗУ, а также  (как и в ЕС-1841 - 1843) два здоровых ящика (системный блок), которые нужно было включать (дисководы и сам компьютер)), ЕС-1863 ("мутант" 386SX - 2 Мб памяти, EGA-монитор, 16 Mhz, высокая цена).

Чем же отличались эти компьютеры друг от друга? Я считаю, что каждый ассемблерщик должен знать историю развития ПК. Ну, хотя бы вкратце. Иначе можете попасть впросак...

С чего все начиналось (речь идет о IBM'ках!).

В 1981 году на свет появляется первый персональный компьютер IBM PC. Затем, как уже писалось, XT, AT и т.д.

286 процессор мало чем отличался от своих старших "братьев". Имел несколько новых команд (например, PUSH число), а также позволял переводить компьютер в т.н. "защищенный режим" (protected mode). Сомневаюсь, что многие программы им пользовались, т.к. этот режим сильно отличался от "трешного". Немного увеличилась скорость работы, да памяти чуть-чуть нарастили. Вот, собственно, и все.

"Тройки" (т.е. 386 процессоры) существенно отличались от всех предшествующих поколений компьютеров. Прежде всего - полноценный защищенный режим, много дополнительных команд для работы с ним, возможность управлять до 4 Гб ОЗУ и многое другое. По сути дела, современный Пентиум - это не что иное, как обычная "тройка", только работающая в несколько раз быстрее, да имеющая несколько дополнительных операторов (MMX, SSE).

"Четверки" далеко не ушли от 386. Единственное, что их отличало от "трешек", так это расположение сопроцессора на одном кристалле с основным процессором. Существенно увеличилась скорость работы, добавилось несколько новых команд и др. по мелочам. Все остальное было также, как у "тройки".

Ну, а "Пентиум" - он, как говорится, и в Африке "Пентиум"...

Вот это вкратце. Надеюсь, о компьютерах мы еще поговорим подробней позже...

Оболочка

Sshell26.asm (головной файл)

; Sshell26.ASM - программа к рассылке № 026 ; (С) Авторские права на файлы-приложения принадлежат подписчикам рассылки ; "Ассемблер? Это просто! Учимся программировать" ; Автор рассылки: ; Калашников Олег Александрович (e-mail: Assembler@Kalashnikoff.ru) ; http://www.Kalashnikoff.ru ; --- Ассемблирование (получение *.com файла) --- ;При использовании MASM 6.11 - 6.13: ;ML.EXE Sshell26.asm /AT ;При использовании TASM: ;TASM.EXE Sshell26.asm ;TLINK.EXE Sshell26.obj /t/x ;______________________________________________________ .386 ;Будем использовать регистры и команды 80386 процессора, .287 ;А также команды 80287 сопроцессора CSEG segment use16 assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG org 100h Start: jmp Begin ; ======= Процедуры ========= ; Головная include main.asm ; Работа с дисплеем include display.asm ; Работа с файлами include files.asm ; Работа с клавиатурой include keyboard.asm ; Сообщения include messages.asm ; Переменные include data.asm ; Начало программы Begin: call Check_video ;Проверим видеорежим и текущую страницу mov ah,9 mov dx,offset Mess_about int 21h ;выводим сообщение с приветствием call Main_proc ; === Головная процедура === ; Выходим в DOS int 20h Current_dir equ $ ;Область хранения текущего каталога Finish equ Current_dir+300 ;Это финиш! CSEG ends end Start

Display.asm

; ===== DISPLAY.ASM - процедуры работы с экраном ====== ; === Рисуем рамку заданного размера в центре экрана === Draw_frame proc mov bp,sp add bp,2 push es push 0B800h pop es ;Производим вычисления для того, чтобы разместить ЛЮБУЮ рамку в цетре экрана. mov ax,Height_X shr al,1 mov dh,11 sub dh,al mov ax,Width_Y shr al,1 mov dl,39 sub dl,al ;Теперь DH содержит центрированный ряд (строку), ;а DL - колонку относительно размеров рамки (окошка)... ;Сохраним полученный адрес, с которого начинается вывод рамки mov Num_DX,dx mov ax,Other test al,1 ;Нулевой бит равен 0? jz No_copyscr ;Если так, то копировать экран не нужно. mov ax,Height_X ;Иначе копируем в область 2 видеостраницы add ax,2 call Copy_scr No_copyscr: call Get_linear push di mov ax,Attr mov al,'г' stosw mov al,'=' mov cx,Width_Y rep stosw mov al,'¬' ;завершаем верхний ряд stosw pop di ;восстановим DI + 160 (следующий ряд) add di,160 inc dh mov cx,Height_X ;CX - кол-во повторов (высота) Next_lined: push cx push di mov al,'¦' stosw mov al,32 mov cx,Width_Y rep stosw mov al,'¦' stosw pop di add di,160 inc dh pop cx loop Next_lined ;следующий ряд... mov al,'L' ;низ рамки... stosw mov al,'=' mov cx,Width_Y rep stosw mov al,'-' stosw ;Выводим сообщение внизу рамки mov si,Mess_dn call Draw_messfr ;Выводим сообщение вверху рамки mov dx,Num_DX push dx mov si,Mess_up call Draw_messfr pop dx add dx,0101h mov si,Mess_ins ;Адрес сообщения, которое будет внутри рамки or si,si ;Если там 0, то не выводим... jz No_draw mov ah,[si] inc si call Print_string ;теперь у нас примерно такая ситуация на экране: ;г==== Сообщение вверху =====¬ ;¦Сообщение внутри ¦ ;¦ ¦ ;¦ ¦ ;L==== Сообщение внизу ======- No_draw: mov ax,Other ;Получим дополнительную информацию test ax,10b jz No_upline mov dx,Num_dx add dh,2 call Get_linear mov ax,Attr mov al,'¦' mov cx,1 stosw mov cx,Width_Y mov al,'-' rep stosw mov al,'¦' stosw ;теперь у нас примерно такая ситуация на экране: ;г==== Сообщение вверху =====¬ ;¦Сообщение внутри ¦ ;¦--------- линия -----------¦ ;¦ ¦ ;¦ ¦ ;L==== Сообщение внизу ======- No_upline: pop es ret 14 Draw_frame endp ; --- Вывод сообщениий вверху и внизу рамки --- ;Вспомогательна процедура. Draw_messfr proc or si,si ;SI = 0?.. jz No_drawup ;тогда ничего выводить не надо, выходим mov ah,[si] inc si call Count_strmid ;Вычисляем середину строки call Print_string ;Выводим строку на экран No_drawup: ret Draw_messfr endp ; === Вычисляем середину строки === ;Вход: CS:SI - адрес строки ;Выход: DL - середина адреса для вывода строки Count_strmid proc push es push di push ax push cs pop es mov di,si xor al,al mov cx,0FFFFh ;сколько символов перебирать (возьмем максимум)... repne scasb ;Ищем 0 в строке ;SI=начало строки ;DI=конец строки+1 sub di,si ;DI=DI-SI-1 = длина строки dec di shr di,1 ;Делим длину на 2 mov ax,40 ;Делим кол-во символов в строке на 2 = 40 sub ax,di ;AX=40-половина длины строки = нужная колонка mov dl,al ;DL=колонка, с которой следует выводить строку! pop ax pop di pop es ret Count_strmid endp ; === Вывод стоки на экран === ;Вход: DS:SI - адрес строки для вывода ; DX - координаты для вывода ; AH - атрибуты строки ;Выход: ничего Print_string proc call Get_linear Next_symstr: lodsb ;Получаем очередной символ строки or al,al ;Это 0 (конец строки?) jz Stop_outstr stosw jmp short Next_Symstr ;Следующий символ Stop_outstr: ret Print_string endp ; === Вывод символа на экран === ;Вход: AL - символ для вывода ; AH - атрибуты символа ; DX - координаты для вывода ;Выход: ничего Print_sym proc call Get_linear ;Получаем линейный адрес строки stosw ;Заносим в видеобуфер атрибут (AH) и символ (AL) ret Print_sym endp ; === Преобразование DH:DL в линейный массив === Get_linear proc push ax ;сохраним все используемые регистры push bx push dx shl dl,1 ;умножаем DL на 2. mov al,dh ;в AL - ряд, mov bl,160 ;который нужно умножить на 160 mul bl ;умножаем: AL(ряд)*160; результат --- в AX mov di,ax ;результат умножения - в DI xor dh,dh ;аннулируем DH add di,dx ;теперь в DI линейный адрес в видеобуфере. pop dx pop bx pop ax ret Get_linear endp ; === Проверяем видеорежим монитора и текущую видеостраницу === Check_video proc mov ah,0Fh int 10h cmp al,3 ;Текстовый режим? je Ok_video mov ax,3 int 10h Ok_video: or bh,bh ;Нулевая страница? jz Ok_page mov ax,0500h int 10h Ok_page: ret Check_video endp ; === Сохраним экран === Save_mainscr proc pusha push es push ds push 0B800h ;с нулевой страницы pop ds xor si,si push 0B900h ;в первую страницу... pop es xor di,di mov cx,2000 ;4000 байт rep movsw pop ds ;восстановим регистры. pop es popa ret Save_mainscr endp ; === Восстановим экран === Restore_mainscr proc pusha push es push ds push 0B900h ;с первой видеостраницы pop ds xor si,si push 0B800h ;в нулевую... pop es xor di,di mov cx,2000 rep movsw pop ds pop es popa ret Restore_mainscr endp ; === Копируем часть экрана === ;Вход: DH - ряд, с которого необходимо начать копирование ; AL - количество рядов для копирования ;Выход: ничего Copy_scr proc pusha push es push ds xor dl,dl call Get_linear mov bl,160 ;Получим количество байт, котрые нужно копировать mul bl mov cx,ax ;Их - в CX (будем использовать CX как счетчик) mov si,di ;DS:SI - откуда копируем xor di,di ;ES:SI - куда копируем mov Num_copySI,si ;Сохраним полученные значения для восстановления mov Num_copyDI,di mov Num_copyCX,cx push 0B800h pop ds push 0BA00h pop es rep movsb pop ds pop es popa ret Num_copySI dw ? Num_copyDI dw ? Num_copyCX dw ? Copy_scr endp ; === Восстанавливаем часть экрана === ;Вход: ничего (все уже сохранено в переменных ) ;Выход: ничего Restore_scr proc pusha push es push ds mov di,Num_copySI ;Получим сохраненные процедурой Copy_scr значения mov si,Num_copyDI mov cx,Num_copyCX push 0BA00h pop ds push 0B800h pop es rep movsb ;Копируем со 2-ой страницы в 0-ую... pop ds pop es popa ret Restore_scr endp ; === Прячем курсор, сохранив предварительно его текущую позицию === Hide_cursor proc mov ah,3 ;получаем текущую позицию курсора mov bh,Video_page int 10h mov Pos_cursor,dx mov ah,2 ;Установим курсор на первую страницу как на нулевой mov bh,1 int 10h mov bh,Video_page ;прячем курсор на 0-ой видеостранице mov dx,1900h int 10h ret Hide_cursor endp ; === Восстановим курсор === Restore_cursor proc mov ah,2 mov bh,Video_page ;видеостраница mov dx,Pos_cursor ;сохраненная позиция int 10h ;установим (позиционируем) курсор ret Restore_cursor endp ; === Вывод десятичных чисел на экран (сопроцессор) === ;Вход: Number_dec - 64-х битное число для вывода ; DX - адрес для вывода ; AH - атрибуты для вывода ;Выход: ничего Out_dec proc pusha push es push 0B800h pop es xor cx,cx ;CX - счетчик для точек (см. ниже ) finit ;Инициализация сопроцессора ;Проведем необходимую настройку сопроцессора для округления чисел... fstcw Dat or Dat,0C00h fldcw Dat fldz fild Divider fild Number_dec fst st(7) ;ST(7)=ST(0) Next_sym: fprem ;Делим и получаем остаток от деления в ST(0) fistp Dat ;Dat=ST(0) ;Выводим остаток от деления, который загружен в Dat. mov al,byte ptr Dat add al,'0' call Print_sym dec dx ;Вывели. DX=DX-1 - следующий символ ;Подготовимся к выводу следующего символа... fld st(6) ;ST(0)=ST(6) fdiv st,st(1) ;Теперь делим ST(0) на ST(1) frndint ;Округлим полученное число fst st(7) ;ST(7)=ST(0). ;Теперь проверим, равно ли делимое нулю. push ax fcom st(2) ;ST(0)=ST(2)? fstsw ax ;AX=состояние сопроцессора and ax,100010100000000b ;Аннулируем ненужные нам биты cmp ax,100000000000000b ;ST(0)=ST(2)? pop ax je Finish_dec ;Если равно, то на выход  ;Точкой будем отделять три цифры для удобства. Пример: 1.568 inc cx cmp cx,3 ;Три цифры выведено? jne Next_sym ;Пока нет. Следующая цифра  mov al,'.' ;Выводим разделитель разрядов (точку) call Print_sym dec dx xor cx,cx ;Обнулим счетчик jmp short Next_sym ;Следующая цифра  Finish_dec: pop es popa ret Number_dec dq ? ;64-х разрядная переменная для выводимого числа Divider dw 10 ;Делитель всегда 10 Dat dw ? ;Временная переменная для хранения остатка от деления на 10 ;Данная процедура выводит всего лишь одно число на экран. Представляете, ;сколько работы проделывает процессор за считанные доли миллисекунд, выводя ;на экран несколько файлов? ;А ведь кроме вывода чисел мы еще проверяем текущий режим монитора и текущую ;видеостраницу, копируем пользовательский экран, читаем и сохраняем ;текущую позицию курсора, рисуем рамку, производя необходимые расчеты, ;читаем каталог в память и выводим первые файлы на экран по одному символу... ;Имейте также в виду, что наши алгоритмы пока не совсем оптимальны. ;Уверяю вас, что даже на PC/XT вывод будет осуществлен мгновенно. Т.е. ;на глаз совсем не заметно... Впечатляет скорость работы процессора? ;Надеюсь, что так... Out_dec endp ; === Выделяем строку определенным цветом === ;На входе: DX - координаты ; CX - длина строки ; AH - атрибут Color_line proc pusha push es push 0B800h pop es call Get_linear inc di ;Указываем не на символ, а на атрибут Next_symbc: mov es:[di],ah ;Заносим атрибут... inc di ;Следующий... inc di loop Next_symbc pop es popa ret Color_line endp

Files.asm

; ==== FILES.ASM - процедуры работы с файлами ==== ; !!! Здесь придется повозиться не меньше, чем с Keyboard.asm !!! ; === Читаем файлы в текущем каталоге === Get_files proc push es mov es,Seg_files mov Current_offset,500 mov Prev_file,0 mov di,Current_offset xor eax,eax stosd call Get_first ;Получаем первый файл jc No_morefiles ;Если нет файлов вообще - на выход cmp word ptr es:[2Ch],002Eh ;Первый файл - '.'? je Next_file_ ;Если так, то ищем следующий файл... call Move_file ;Если нет, то переносим файл в память. Next_file_: call Get_other ;Получаем следующий файл... jc No_morefiles ;Файлы закончились - на выход. call Move_file ;Если нет, то переносим файл в память. jmp short Next_file_ ;Ищем следующий файл... No_morefiles: mov di,Next_file xor ax,ax stosw mov di,500 mov Current_file,di ;Будет указывать на то, что первый файл - текущий mov di,Current_file or byte ptr es:[di+4],1 pop es ret All_files db '*.*',0 Seg_files dw ? ;Сегментный адрес буфера файлов Current_offset dw 500 Next_file dw ? Prev_file dw 0 Get_files endp ; === Поиск первого файла в текущем каталоге === Get_first proc ;Здесь можно поставить инструкцию int 3 и в процессе отладки позволить ;отладчику выполнить программу до конца (Run). Дойдя до int 3 отладчик ;обязательно остановится, что позволит вам проанализировать работу ;приведенных ниже строк. Попробуйте! ; int 3 ;

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