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

InterReklama Advertising

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

______________________________________

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

Здравствуйте, уважаемые подписчики!

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

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

Новости

Ваши письма.

Оболочка

Наши партнеры

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

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

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

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

Новости Новостей как всегда много. Есть хорошие, есть и плохая. Ну, начну с плохой.

1. К сожалению, не получается у меня выпускать рассылку раз в неделю. Даже раз в две недели. Вся проблема в том, что как всегда не хватает времени. И еще рук. Эх, мне бы две головы, да 4 руки, плюс 40 часов в сутки... Поверьте, работы очень много. Я параллельно над книгой работаю. Уже готова вторая часть и половина третей. И страниц - не поверите! - порядка 300, хотя рассылок обработано всего 14! Вот такие вот дела...

Я надеюсь, что рассылка дойдет до конца с периодичностью 1-2 раза в месяц. Тем более, что осталось-то совсем немного выпусков.

Кстати, в последнем выпуске я напишу несколько полезных и очень интересных советов по программированию на Ассемблере под DOS.

"Как же так", - спросите вы. "А под Windows мы не будем программировать?". Не волнуйтесь! Все будет. Дело в том, что рассылка "Ассемблер? Это просто! Учимся программировать" будет закрыта, а вместо нее станет выходить под другим названием. Но об этом позже...

Хорошие новости.

2. Начала потихоньку набирать обороты новая рассылка FAQ, в которой публикуются вопросы подписчиков и ответы экспертов на них. Не буду долго расписывать, скажу только, что теперь любой желающий может стать экспертом, а также выйти из состава экспертов. Все эксперты получают баллы за отправленные ответы, за которые можно получить... А, впрочем, если вы заинтересовались, то заходите сюда: http://www.kalashnikoff.ru/Experts/Rules.html. Вскоре будут также поощряться подписчики подающие самые интересные вопросы.

В рассылке FAQ планируется также публиковать ваши письма, мысли, предложения по программированию, алгоритмы, выдержки и многое другое, что относится к программированию. Все сообщения вы сможете отправлять самостоятельно. Выпуски, как уже отмечалось, выходят автоматически. В них будет скоро установлена форма голосования на ответы экспертов. От этого будет также зависеть начисление баллов тому или иному эксперту. Более того, скоро каждый желающий сможет самостоятельно создать свою экспертную группу (например, по программированию на Паскале, Си и т.п.). На эту группу смогут подписываться эксперты.

Весь сервис осуществляется теперь через сервер Kalashnikoff.ru. Хостинг предоставляется компанией www.100mb.ru.

Надеюсь, что вам понравится новая рассылка, а также все то, что я пытаюсь делать.

Просьба ко всем экспертам: зарегистрируйтесь, пожалуйста, по указанной выше ссылке в качестве эксперта, если вы до сих пор получаете вопросы от eGroups.

Благодарю всех, кто уже зарегистрировался в качестве экспертов, а также всех подписчиков, отправляющих вопросы.

3. В который уж раз существенно изменился внешний вид сайта. Теперь навигация стала гораздо удобней, да и сайт мало смахивает на типичную "домашнюю страничку". Оценивать, конечно, вам, уважаемые читатели.

4. Количество подписчиков на нашу рассылку перевалило за 10.000. Спасибо вам, что читаете, подписываетесь, участвуйте в обсуждениях. Хотя я должен извиниться за задержку с ответами на ваши письма, т.к. времени очень мало.

Ваши письма Доброго времени суток, Олег!

Я хотел бы предложить один интересный вариант вызова процедур с передачей параметров. Я сам это использую очень часто в своих ассемблерных программах. Это предложение появилось после прочтения письма Макса (Макс Черничкин (ака Semax) (semax@aport.ru)). Выглядит это так:

.model tiny, pascal

call MyProcedure, word ptr Param1, ax, ds, 12

MyProcedure proc Param1:word, Param2:word, Param3:word, Param4:word

mov ax, Param1

mov bx, Param2

add ax, Param3

sub bx, Param4

add ax, bx

ret

MyProcedure endp

Модель памяти может использоваться любая, после оператора возвращения из процедуры не надо указывать число для освобождения стека. Если в качестве параметров передавать адрес переменной, то из процедуры можно будет менять содержимое переменной. Косая черта позволяет переносить продолжение длиной строки на новую строчку. Думаю, это будет интересно и полезно многим программистам на ассемблере. Про себя могу сказать, что я просто ОБОЖАЮ этот язык за его ВСЕДОЗВОЛЕННОСТЬ и ПРОСТОТУ.

Best regards, TimsoN

e-mail: timson@katehgr.almaty.kz

Оболочка  

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

; Sshell28.ASM - программа к рассылке № 028 ; (С) Авторские права на файлы-приложения принадлежат подписчикам рассылки ; "Ассемблер? Это просто! Учимся программировать" ; Автор рассылки: ; Калашников Олег Александрович (e-mail: Assembler@Kalashnikoff.ru) ; http://www.Kalashnikoff.ru ; --- Ассемблирование (получение *.com файла) --- ;При использовании MASM 6.11 - 6.13: ;ML.EXE Sshell28.asm /AT ;При использовании TASM: ;TASM.EXE Sshell28.asm ;TLINK.EXE Sshell28.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 $ ;Область хранения текущего каталога Temp_files equ Current_dir+300 ;Временное хранение найденного файла Finish equ Temp_files+320 ;Это финиш! 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,'+' ;низ рамки... 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 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 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 ;Данная процедура выводит всего лишь одно число на экран. Представляете, ;сколько работы проделывает процессор за считанные доли миллисекунд, выводя ;на экран несколько файлов? ;А ведь кроме вывода чисел мы еще проверяем текущий режим монитора и текущую ;видеостраницу, копируем пользовательский экран, читаем и сохраняем ;текущую позицию курсора, рисуем рамку, производя необходимые расчеты, ;читаем каталог в память и выводим первые файлы на экран по одному символу... ;Имейте также в виду, что наши алгоритмы пока не совсем оптимальны. 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 - процедуры работы с файлами ==== ; === Читаем файлы в текущем каталоге === Get_files proc push cs ;Инициализируем ES pop es mov fs,Seg_offset ;FS - сегмент смещений файлов xor di,di mov Current_offset,di mov fs:[di],di ;Получаем смещение файла в банке данных смещений mov bp,2 ;Следующий файл будет помещаться по адресу 2 call Get_first ;Получаем первый файл jc No_morefiles ;Если нет файлов вообще - на выход cmp word ptr es:[Temp_files+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 word ptr fs:[bp-2],0FFFFh ;Признак конца списка файлов mov es,Seg_files ;Первый файл сразу помеченный. mov byte ptr es:[0],1 push cs pop es mov Current_file,0 ;Текущий файл - 0 (т.е. первый) ret All_files db '*.*',0 Get_files endp ; --- Перенос имени найденного файла в сегмент файлов нашей программы --- Move_file proc push es mov es,Seg_files mov si,offset Temp_files ;Имя найденного файла mov di,Current_offset xor al,al ;Заносим статус файла stosb ;Заносим размер файла mov eax,[si+1Ch] stosd mov eax,[si+20h] stosd add si,2Ch ;SI - указывает на смещение найденного файла (DS:SI) ;Заносим имя найденного файла в буфер нашей программы Next_byte: lodsb stosb or al,al jnz Next_byte Quit_move: ;Заносим смещение файла в банк данных смещений mov Current_offset,di mov fs:[bp],di ;FS:[BP] - указывает на следующий файл inc bp inc bp pop es ret Move_file endp ; === Вывод ОДНОГО найденного файла на экран === Out_files proc mov bl,Number_files ;BL указывает на количество выводимых файлов push ds push es push 0B800h pop es mov di,Start_file ;SI = начальную позицию при выводе на экран mov ah,1Bh mov dx,0302h ;Начало для вывода файлов mov fs,Seg_offset ;FS - банк данных смещений mov ds,Seg_files ;DS - банк данных файлов Next_fileout: mov si,fs:[di] ;DS:SI - смещение следующего файла cmp si,0FFFFh ;Последний файл? je Exit_files push si push di cmp cs:Out_fstat,0 ;Чистить ли строку перед выводом файла jz No_clear ;Чистим строку перед выводом файла mov al,32 mov cx,77 call Get_linear mov cx,77 rep stosw No_clear: add si,9 call Print_string ;Выводим очередной файл... pop di ;Выводим размер файла pop si call Print_fsize ;С какими атрибутами выводить (отмеченный, текущий...). mov bh,[si] call Get_status add dx,0100h ;Иначе увеличиваем DH на 1 для вывода следующего имени... inc di inc di dec bl ;Уменьшаем количество выводимых файлов на 1 jnz short Next_fileout ;Уже 0? Тогда больше выводить не нужно... Exit_files: pop es pop ds ret Number_files db 21 ;Количество выводимых файлов на экран. Start_file dw 0 Out_fstat db 0 Out_files endp ; === Проверяем статус файла === Get_status proc pusha mov ah,1Bh ;Обычный файл... or bh,bh ;Есть какие-нибудь метки? jz Quit_sts ;Нет - на выход  mov ah,30h cmp bh,1 ;Это текущий файл? jz Quit_sts ;Да - на выход  mov ah,1Eh cmp bh,2 ;Это отмеченный файл? jz Quit_sts ;Да - на выход  mov ah,3Eh ;Получается, что текущий + отмеченный... Quit_sts: mov dl,1 ;Разрисовываем его... mov cx,78 call Color_line popa ret Get_status endp ; === Выводим размер файла === Print_fsize proc pusha push ds mov eax,ds:[si+1] ;Получим первую часть размера файла mov ebx,ds:[si+5] ;Получим вторую часть push cs pop ds mov si,offset Number_dec ;Занесем размер файла в переменную Number_dec mov dword ptr [si],ebx ;Процедура Out_dec будет выводить число, mov dword ptr [si+4],eax ;которое находится именно в этой переменной. mov dl,50 ;DL указывает на колонку mov ah,1Bh ;AH содержит атрибуты для выводимого числа call Out_dec ;Выводим число pop ds popa ret Print_fsize endp ; === Поиск первого файла в текущем каталоге === Get_first proc mov ax,714Eh ;Функция поиска первого файла mov di,offset Temp_files ;DI должен указывать на буфер, куда будут xor si,si ;записываться данные о найденном файле (типа DTA). mov cx,0FFh ;Ищем все возможные файлы. Это что-то вроде атрибутов файла mov dx,offset All_files ;Маска поиска int 21h mov Handle,ax ;Сохраним номер процесса поиска файлов для 714Fh ret Get_first endp ; === Поиск следующих файлов === Get_other proc mov bx,Handle ;Указываем номер процесса. mov ax,714Fh ;Функция поиска следующих файлов xor si,si mov di,offset Temp_files ;DI должен указывать на буфер, куда будут int 21h ;записываться данные о найденном файле (типа DTA). ret Get_other endp

Keyboard.asm

; ==== KEYBOARD.ASM - процедуры работы с клавиатурой ==== ; === Ждем нажатия клавиши === Pause proc xor ah,ah int 16h ret Pause endp ; === Клавиша Insert ASCII 52h === Insert proc push es mov es,Seg_files ;База файлов mov fs,Seg_offset ;База смещений mov di,Current_file mov di,fs:[di] xor byte ptr es:[di],2 ;Инвертируем байт (отмечено / неотмечено) call Down_pressed ;Имитируем нажатие клавиши "стрелка вниз". mov Out_fstat,0 ;Выводить без чистки строк call Out_files No_insert: pop es ret Insert endp ; === Клавиша PgDown ASCII 51h === PgDown proc ;Имитировать будем нажатие клавиши "стрелка вниз" столько раз, сколько выводим ;файлов на экран. xor cx,cx mov cl,Number_files Next_PgDn: push cx call Down_pressed ;Имитируем нажатие "клавиши вниз" pop cx dec cx or cx,cx jnz Next_PgDn ret PgDown endp ; === Клавиша PgUp ASCII 49h === PgUp proc xor cx,cx mov cl,Number_files Next_PgUp: push cx call Up_pressed pop cx loop Next_PgUp ret PgUp endp ; === Клавиша Home ASCII 49h === K_Home proc ;Выводить будем до тех пор, пока процедура Up_pressed не вернет утановленный ;флаг переноса (Carry Flag) Next_khome: call Up_pressed jnc Next_khome ret K_Home endp ; === Клавиша End ASCII 49h === K_end proc Next_kend: call Down_pressed jnc Next_kend ret K_End endp ; === Клавиша вниз === ;Пользователь нажал клавишу вниз! Переводим курсор на следующий файл... Down_pressed proc push es mov es,Seg_files ;База файлов mov fs,Seg_offset ;База смещений mov di,Current_file add di,2 mov di,fs:[di] cmp di,0FFFFh ;Это последний файл? je No_down ;Нет. Тогда устанавливаем метку "текущий файл" для следующего файла or byte ptr es:[di],1 ;Нет. Тогда устанавливаем метку "текущий файл" mov Out_fstat,0 mov di,Current_file mov di,fs:[di] ;Текущий файл - сбрасываем метку текущего файла and byte ptr es:[di],11111110b add Current_file,2 ;Вычисляем, нужно ли прокручивать файлы? mov ax,Current_file mov bx,Start_file sub ax,bx shr al,1 cmp al,Number_files jb No_moved ;Нужно! Т.е. пользователь дошел до самого нижнего файла внизу экрана. add Start_file,2 mov Out_fstat,1 ;Установим метку того, что нужно чистить строки. No_moved: call Out_files ;Выводим файлы на экран clc pop es ret No_down: pop es stc ret Down_pressed endp ; === Клавиша вверх === ;Пользователь нажал клавишу вверх! Переводим курсор на предыдущий файл... Up_pressed proc push es mov es,Seg_files ;База файлов mov fs,Seg_offset ;База смещений mov Out_fstat,0 mov di,Current_file mov di,fs:[di] or di,di ;Это самый верхний файл? jz No_up ;Нет. Тогда сбрасываем метку "текущий файл" для текущего файла and byte ptr es:[di],11111110b sub Current_file,2 mov di,Current_file mov di,fs:[di] ;Устанавливаем метку "текущий файл" для предыдущего файла or byte ptr es:[di],1 ;Проверка на прокрутку экрана mov ax,Current_file add ax,2 cmp ax,Start_file jne No_moveu ;Будем прокручивать, т.к. пользователь добрался курсором до верхней части ;экрана. mov Out_fstat,1 sub Start_file,2 No_moveu: call Out_files ;Выводим файлы на экран clc pop es ret No_up: pop es stc ret Up_pressed endp ; === Клавиша Enter === Enter_pressed proc ;Пользователь нажал клавишу Enter! Меняем текущий каталог... ;!!! Обратите внимание, что мы пока не проверяем, нажал ли пользователь Enter ;на каталоге или файле !!! push es push ds mov di,Current_dir mov cx,0FFFFh xor al,al repne scasb dec di mov si,Current_file mov fs,Seg_offset mov si,fs:[si] mov ds,Seg_files add si,9 ;Переходим в каталог верхнего уровня? cmp word ptr ds:[si],'..' jne Next_cdir ;Если так, то ищем в строке с текущим каталогом первый СЛЕВА символ "\" ;Пример: Допустим, текущий каталог такой: ;C:\ASSM\FILES\'ASCII 0' ;Будем искать символ '\' после M std dec di dec di mov al,'\' mov cx,0FFFFh repne scasb cld ;Добавляем нуль после первого левого найденного символа в текущем пути. add di,2 xor al,al stosb ;Получили: ;C:\ASSM\'ASCII 0' jmp short Got_dp ;Если переходим в подкаталог, то добавляем к текущему пути имя каталога, ;на котором пользователь нажал "Enter". ;Например: ;C:\ASSM\FILES\'ASCII 0' ;После добавления будет так: ;C:\ASSM\FILES\MYFILES\'ASCII 0' Next_cdir: lodsb or al,al jz Got_dir stosb jmp short Next_cdir Got_dir: mov ax,005Ch ;'\' stosw Got_dp: pop es pop ds ;Инициализируем переменные... mov Current_file,0 mov Start_file,0 mov Out_fstat,1 ;Перечитываем диск. call Draw_main_frame ;Рисуем головную рамку call Change_dir call Get_files ;Читаем каталог в память call Out_files ;Выводим файлы на экран ret Enter_pressed endp

Main.asm

; === MAIN.ASM - Головная процедура === Main_proc proc call Hide_cursor ;прячем курсор call Prepare_all call Save_mainscr ;сохраним содержимое экрана... Re_Read_dir: ;Инициализируем переменные. mov Current_file,0 mov Start_file,0 mov Out_fstat,1 call Draw_main_frame ;Рисуем головную рамку call Get_curdir ;Получаем текущий каталог call Change_dir ;Делаем его текущим call Get_files ;Читаем каталог в память call Out_files ;Выводим файлы на экран Next_key: call Pause or al,al jz Ext_code ;Это расширенный ASCII-код клавиши? Да - проверяем его.  cmp al,27 ;Нажали ESC? je Quit_prg ;Да! На метку  cmp al,13 ;Нажали Enter? je Enter_prsd jmp short Next_key Quit_prg: call Quit_prog ;Подтвердим намерения пользователя выйти из программы. jnc Next_key ;Пользователь подтвердил выход? НЕТ? Тогда на Next_key  ;Да, подтвердил (а жаль!). Тогда выходим. call Restore_mainscr ;восстановим содержимое экрана call Restore_cursor ;восстановим позицию курсор ret ;Выходим из процедуры MAIN.ASM, а затем сразу в DOS! Enter_prsd: call Enter_pressed jmp short Next_key Ext_code: cmp ah,60h ;Нажали Ctrl+F3? je Re_read_dir ;Да - перечитываем каталог . cmp ah,62h ;Нажали Ctrl+F5? je User_screenl ;Да - показываем экран пользователю . cmp ah,50h ;Стрелка вниз? je Key_down cmp ah,48h ;Стрелка вверх? je Key_up cmp ah,51h ;Клавиша PageDown? je Key_pgd cmp ah,49h ;Клавиша PageUp? je Key_pgu cmp ah,52h ;Клавиша Insert? je Key_ins cmp ah,47h ;Клавиша Home? je Key_home cmp ah,4Fh je Key_end ;Клавиша End? jmp short Next_key ;Нет - ждем другую клавишу  ;Пошли процедуры обработки нажатых клавиш... Key_down: call Down_pressed jmp Next_key Key_up: call Up_pressed jmp Next_key Key_pgd: call PgDown jmp Next_key Key_pgu: call PgUp jmp Next_key Key_home: call K_home jmp Next_key Key_end: call K_end jmp Next_key Key_Ins: call Insert jmp Next_key User_screenl: mov ax,0501h ;Показываем пользователю 1 видеостраницу. int 10h call Pause ;Ждем до тех пор, пока пользователь не налюбуется... mov ax,0500h ;Опять на нулевую устанавливаем. int 10h jmp Next_key ;Ждем дальше...  Main_proc endp ; === Готовим все необходимое для работы === Prepare_all proc call Prepare_memory ;Отводим память ret Prepare_all endp ;--- Ужимаем память --- Prepare_memory proc mov bx,offset Finish shr bx,4 inc bx mov ah,4Ah int 21h ;Ужимаем размер отведенной памяти до метки Finish ; -- Отводим память для считывания каталога ;Здесь будут смещения на файлы mov ah,48h mov bx,250 ; 250 * 16 = 4000 байт = 2000 ссылок на файлы int 21h mov Seg_offset,ax ;Сохраним сегмент ;Здесь будут сами файлы mov ah,48h mov bx,4096 ;4096 * 16 = 65536 байт int 21h mov Seg_files,ax ;Сохраним сегмент ret Seg_offset dw ? Seg_files dw ? Prepare_memory endp ; === Получаем текущий диск+каталог === Get_curdir proc mov dword ptr [Current_dir],005C3A43h ;Заносим ASCIZ C:\ mov ah,19h int 21h ;Получаем текущий диск add al,'A' mov byte ptr [Current_dir],al ret Get_curdir endp ; === Change_dir === Change_dir proc mov ax,713Bh mov dx,offset Current_dir int 21h push 0B800h pop es mov ah,1Fh mov dx,0102h mov si,offset Current_dir call Print_string ret Change_dir endp ; === Выходим из программы? === Quit_prog proc push 1 push offset Mess_quitl push 4F00h push offset Mess_qup push 0 push offset Mess_quit push 01b call Draw_frame call Pause call Restore_scr ;Восстановим сохранунную часть экрана. cmp al,'Y' ;Нажали 'Y' / 'y' / Enter (13)? je Yes_quit ;Да!  cmp al,'y' je Yes_quit cmp al,13 je Yes_quit clc ret Yes_quit: stc ;Установим флаг переноса (нажали 'Y', значит выходим)... ret Quit_prog endp ; === Рисуем головную рамку === Draw_main_frame proc push 23 push 78 push Main_color push offset Mess_head push offset Mess_down push 0 push 10b call Draw_frame ;Рисуем рамку на весь экран ret Draw_main_frame endp

Messages.asm

; === MESSAGES.ASM - Конфигурационный файл === Mess_about db 0Ah, 0Dh, 'SuperShell - оболочка для DOS, написанная на Ассемблере.',0Ah, 0Dh db 'Рассылка "Ассемблер? Это просто! Учимся программировать", Выпуск № 028',0Ah,0Dh db 'http://www.Kalashnikoff.ru. E-mail: Assembler@Kalashnikoff.ru',0Ah,0Dh,0Ah db '(C) Авторские права на файлы-приложения принадлежат всем подписчикам рассылки.',0Ah, 0Dh, 0Ah db 9,9,'=== Россия, Москва, 2001 год ===',0Ah,0Dh,'$' Main_color dw 1F00h Mess_head db 1Eh, ' Super Shell, Версия 1.0 ',0 Mess_down db 1Dh, ' Россия, Москва, 2001 ',0 Mess_qup db 4Eh, ' Выход ',0 Mess_quit db 4Bh, ' Вы действительно хотите выйти в DOS (Y/N)?',0 Mess_quitl equ $-Mess_quit-1 ;Один байт занимает код цвета (4Bh)

Data.asm

; === DATA.ASM - данные оболочки === ; ==== ПЕРЕМЕННЫЕ ==== Num_attr db ? ;переменная для атрибутов Num_DX dw ? Video_page db 0 ;текущая видеостраница Pos_cursor dw ? ;главная позиция курсора Handle dw ? ;Номер файла Current_file dw ? ;Текущий файл Current_offset dw 0 ;Текущее смещение ; --- Данные для процедуры Draw_frame --- Height_X equ [bp+12] ;высота рамки Width_Y equ [bp+10] ;ширина рамки Attr equ [bp+8] ;атрибуты рамки Mess_up equ [bp+6] ;сообщение вверху рамки Mess_dn equ [bp+4] ;сообщение внизу рамки Mess_ins equ [bp+2] ;сообщение внутри рамки Other equ [bp] ;иная конфигурация окна

Итак, теперь оболочка работает без сбоев, т.е. мы можем читать каталог, заходить в подкаталоги и возвращаться в корневой. Работают также клавиши PageUp, PageDown, Home, End, Insert (отметка файлов) Ctr+F3.

Более того, алгоритм считывания файлов и вывод их на экран полностью изменился. Я позаимствовал алгоритм считывания каталога у Макса Черничкина. Спасибо Вам, уважаемый читатель Макс, за присланный алгоритм. Надеюсь, что Вы не будете против его применения в наших файлах-приложениях. Более того, полагаю, что многие подписчики с удовольствием с ним ознакомятся.

Что же это за алгоритм и как он работает?

Об этом вкратце писалось в письме Макса в прошлом выпуске. Теперь рассмотрим его на практике.

Считывание каталога происходит следующим образом:

Готовим сегментные регистры:

mov fs,Seg_offset ;FS - сегмент смещений файлов

xor di,di

mov Current_offset,di

mov fs:[di],di ;Получаем смещение файла в банке данных смещений

mov bp,2 ;Следующий файл будет помещаться по адресу 2

Обратите внимание на новый сегментный регистр FS, который начал использоваться в 286 процессоре. Ничего особенного в нем нет. Поэтому считайте его дополнительным сегментом.

Итак, файлы будут размещаться таким образом: отводим память под смещения для файлов:

;Здесь будут смещения на файлы

mov ah,48h

mov bx,250 ; 250 * 16 = 4000 байт = 2000 ссылок на файлы

int 21h

mov Seg_offset,ax ;Сохраним сегмент

Отводим память для хранения файлов:

;Здесь будут сами файлы

mov ah,48h

mov bx,4096 ;4096 * 16 = 65536 байт

int 21h

mov Seg_files,ax ;Сохраним сегмент

Сейчас все будет понятно.

Представим, что в читаемом каталоге есть два файла: main.asm и other.asm.

Таким образом, файлы в памяти будут размещены так:

  main.asm other.asm <Конец файлов> Seg_offset 0000h 0009h 0FFFFh Seg_files main.asm + ASCII 0 other.asm+ASCII 0   В таблице вы видите два сегмента, которые мы отвели (см. выше): Seg_offset и Seg_files. В первом сегменте будут сохраняться смещения на файлы, которые находятся во втором сегменте. Сигналом окончания файлов служит число 0FFFFh вместо смещения в первом сегменте.

Теперь процедура вывода на экран:

Получаем число (2 байта), расположенное в сегменте Seg_offset по смещению 0000h. В первом случае это будет 0000h. Затем читаем и выводим файл, который расположен по полученному смещению из сегмента Seg_offset.

Увеличиваем указатель смещения в первом сегменте на 2 (т.е. перейдем на следующий файл). Получаем число 0009h. По этому смещению находится в памяти следующий файл (т.е. other.asm). Его и выводим.

Увеличиваем указатель на 2. Получаем очередное смещение. Это 0FFFFh? Значит предыдущий файл был последним. Все! Вывод закончен.

Все просто!

Более того, обратите внимание, что и перемещаться по каталогу в памяти будет очень удобно. Хотим получить имя пятого файла в цепочке? Производим нехитрые вычисления: 5*2=10. Почему на два? Так ведь смещение занимает два байта. Т.о. получаем смещение пятого файла в сегменте Seg_files. Остается его только вывести.

Реально наша оболочка заносит не только имя файла + ASCII 0, но и его статус (текущий, отмеченный) и размер. В следующем выпуске добавим атрибуты и дату создания / изменения.

Вот, собственно, и весь алгоритм. Только на словах он звучит очень просто, а вот на практике не очень. Ну, я полагаю, вы разберетесь.

Я, конечно, понимаю, что чем больше программа, тем сложнее в ней разобраться. Помните: терпение, упорство и желание изучить Ассемблер вам воздастся полностью.

_____________

Рассмотрим еще несколько новых переменных:

1. mov Current_file,0

2. mov Start_file,0

3. mov Out_fstat,1

Первая переменная хранит в себе смещение текущего файла (т.е. файла, на котором установлен курсор). Так нам удобней и быстрее будет искать текущий файл.

Вторая переменная содержит смещение файла, с которого следует выводить файлы на экран. Наша оболочка может отображать на экране 21 файл. Если же в каталоге больше файлов, то, дойдя до самого нижнего внизу или вверху экрана, необходимо "прокрутить" их. Т.е. самый верхний / самый нижний на экране убирается, а на месте его появляется следующий / предыдущий. Вот переменная Start_file как раз и будет хранить смещение файла, с которого нужно выводить на экран все последующие файлы.

Обратите внимание, что под словом "прокрутка" понимается на т.н. "скроллинг", а запись файлов поверх себя. Дело в том, что скроллинг осуществляется с помощью прерывания 10h, что существенно тормозит работу. Для этого мы будем лучше пользоваться нашим способом, который не требует вызова никакого прерывания.

Третья переменная (Out_fstat) хранит состояние вывода одного файла, а именно: чистить ли полностью строку перед выводом или нет. Зачем чистить строку перед выводом файла? Представьте, что файлы прокручиваются на экране. Тогда получаем так:

M A I N . A S M   O T H E R . A S M Таким образом, прокрутив файлы вниз (main.asm "ляжет" на место other.asm) мы получим такое на экране:

main.asmm

т.е. символ "m" от предыдущего файла останется. Для этой цели перед выводом очередного файла необходимо изначально очистить строку.

Почему же тогда в одних случаях мы чистим строку, а в некоторых - нет (это регулирует переменная Out_fstat)? Если чистить строку каждый раз, то файлы будут мерцать, что не очень удобно для просмотра. Если вы не поняли о чем идет речь (сумбурно я объяснил как-то), то поэкспериментируйте. Надеюсь, станет все понятно...

Пример: пользователь на самом верхнем файле нажимает клавишу "Insert", что приводит к отметке файла (т.е. он на экране выделяется светло-желтым цветом). Делается это очень просто.

У нас существует процедура вывода файлов (Out_files, FILES.ASM), которая выводит файл, начиная с позиции Start_file и до конца (либо 21). Как уже отмечалось, в буфер заносится не только имя найденного файла, но и его статус (изначально все файлы имеют нулевой статус, т.е. не отмечен, не текущий). После перечитывания каталога мы автоматически делаем первый файл текущим (статус - 1), т.е. на нем будет стоять курсор.

Вернемся. Пользователь нажал "Insert". Мы даем текущему файлу статус 2 (т.е. отмеченный). Для этого первый байт в буфере для файла (т.е. его статус) отмечается как 2. Для следующего файла ставим статус 1 (текущий) и вызываем процедуру Out_files. Out_files берет первый файл, "смотрит" его статус. Если это 1 - то выводит с обычными атрибутами (т.е. как будто на него указывает курсор), если 2, то как отмеченный (т.е. светло-желтый), если иной другой, то как отмеченный + текущий. И так с каждым файлом.

Т.о. просто установив нужный статус тому или иному файлу, мы вызываем процедуру Out_files, которая выделит все, что нужно.

Если при этом не было "прокрутки" файлов, то мы вызываем Out_files, при этом занеся в Out_fstat число 0, что укажет процедуре на то, что чистить строки не надо (все равно ведь все символы будут ложиться поверх уже существующих).

Если же произошла прокрутка файлов, то вызываем процедуру Out_files, при этом занеся в Out_fstat число 1, что укажет процедуре чистить строку перед выводом каждого файла.

Поэкспериментируйте с приложением...

____________

Что происходит, если пользователь нажимает клавишу PageUp или PageDown. Очень просто! Мы имитируем нажатие на клавишу "Стрелка вверх" или "Стрелка вниз" столько раз, сколько файлов показано на экране.

Что происходит, если пользователь нажимает на клавиши "Home" или "End"?

Процедуры Up_pressed и Down_pressed (нажатие на стрелку вверх или вниз) устанавливают флаг переноса, если достигнут конец списка файлов (или начало). Сами же процедуры работают так:

; === Клавиша Home ASCII 49h ===

K_Home proc

;Выводить будем до тех пор, пока процедура Up_pressed не вернет установленный флаг переноса (Carry Flag)

Next_khome:

call Up_pressed

jnc Next_khome

ret

K_Home endp

; === Клавиша End ASCII 49h ===

K_end proc

Next_kend:

call Down_pressed

jnc Next_kend

ret

K_End endp

_______________

На сегодня все! Вам осталось только разобрать файлы-приложения в отладчике (лучше TD), а также прочитать описания в них!

Удачи!

Наши партнеры Ну и на последок хотелось бы сказать пару слов о наших партнерах.

1. Рассылка "Уроки для начинающих программистов".

Одна из лучших рассылок по программированию для начинающих. Проект Эдуарда Дмитриева.

Рассылка для тех, кто не знаком или почти не знаком с программированием, но хочет ему научиться... Я буду учить Вас писать программы начиная от работы с простейшими алгоритмами, кончая графикой и созданием полноценных видеоигр и системных программ... Вы научитесь писать гостевые книги, счетчики, рассылки и другие полезные вещи. Освоите HTML, JAVA и JAVA script...

http://prog.agava.ru - Библиотека программиста.

________________

2. Hi-tech - Рассылка "Низкоуровневое программирование для дZенствующих".

В данной рассылке рассматривается программирование на Ассемблере. Как уверяют сами авторы данного проекта: "Наш авторский курс по низкоуровневому программированию. С его помощью вы наконец-таки разберетесь с тем, как все-таки работают эти чертовы компьютеры, научитесь программировать их "узкие места" и обходить многочисленные "подводные камни"...". На мой взгляд это довольно-таки интересный проект, причем, авторы постарались подойти к изучению Ассемблера с тонким компьютерным юмором, что придало рассылке "Низкоуровневое программирование..." некую уникальность (код рассылки: comp.soft.prog.hitech).

________________

3. Рассылка Сергея Никифорова "Программирование на Visual Basic и ASP" на Subscribe.ru (код рассылки: comp.soft.prog.vbs).

Название и сайт автора: Visual Basic Streets, http://www.vbstreets.ru.

С уважением,

Калашников Олег: Assembler@Kalashnikoff.ru

Мой ICQ No.: 68951340

URL сайта подписчиков: http://www.Kalashnikoff.ru

Форма для подачи вопроса: http://www.Kalashnikoff.ru/Experts/Question.html

______________

По вопросам сотрудничества, рекламы и спонсорства обращайтесь:

Публичное размещение материала из рассылки: Cooperation@Kalashnikoff.ru Реклама на сайте, в книге и рассылках: http://www.Kalashnikoff.ru/Reklama.html, Reklama@Kalashnikoff.ru Издание книги по материалам рассылки (спонсорство): Sponsor@Kalashnikoff.ru

(C) Москва, 2001. Авторское право принадлежит Калашникову О.А. Публичное размещение материала из рассылки, а также его использование полностью или частично в коммерческих или иных подобных целях без письменного согласия автора влечет ответственность за нарушение авторских прав. [Следующий выпуск] [На главную страницу]

u="u496.71.spylog.com";d=document;nv=navigator;na=nv.appName;p=1; bv=Math.round(parseFloat(nv.appVersion)*100); n=(na.substring(0,2)=="Mi")?0:1;rn=Math.random();z="p="+p+"&rn="+rn;y=""; y+=""; y+=""; y+=""; d.write(y);if(!n) { d.write("

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