О шорах на глазах программистов Побудила написать эту статью меня программа А.Аброськина для выдачи списка текущих каталогов всех дисков, опубликованная в "Мониторе" 5.93. Если до этого я еще терпел неэффективные и просто неправильные решения, опубликованные в журналах или распростораняемые, как FreeWare, то тут я не выдержал и запустил текстовый редактор (эквивалент "взялся за перо") дабы предостеречь читателей от копирования чужих и собственных ошибочных представлений о принципах работы компьютерных программ, которые повторяются столь систематически, что кажется, что многие пользователи и программисты одеты в шоры и не замечают элементарных вещей. Чем же меня так возмутила бедная утилита Аброськина? Ну, во-первых Assume cs:code,ds:code .... mov cs:[records] .... Зачем здесь префикс CS: ? Он абсолютно не нужен, так как это не резидент, а обычная COM программа, в которой, если не принять специальных мер CS и DS равны, что честно указал автор своей директивой ASSUME. А ведь префикс честно транслируется в соответствующий байт кода. И на исполнение его тратится время каждый раз, когда сия программа, претендующая на рекорд по скорости и быстодействию обращается к локальной переменной. Во-вторых, исключительно хитрый способ загрузки адреса в регистры ES:DI. На это потребовались аж четыре команды: MOV DI,ES:[BX+16H] MOV AX,ES:[BX+18H] PUSH AX POP ES хотя последние три без малейшего ущерба для эффективности заменяются на одну: MOV ES,ES:[BX+18H] а лично я написал бы: LES DI,ES:[BX+16H] вместо всех четырех. В-третьих, если вы используете в своих программах функции, которые не поддерхиваются XT, типа функции 13H прерывания 10H (хотя какой в этом смысл - использование функции 0EH экономит 8 байт, хотя и замедляет вывод, так как при этом не нужно опрашивать положение курсора и пересылать данные из DI в BP, а нужно всего лишь организовать простейший цикл.), отмечайте это в описании. В-четвертых, думайте, куда вы выводите! Это касается в первую очередь не программистов на ассемблере, а тех кто использует Turbo Pascal и автоматически пишет в начале программы uses crt; В данном случае гораздо удобнее воспользоваться стандартным выводом DOS. Это увеличит программу на 5 байт (после того как мы ее сократили на 19), но сделает ее более универсальной, так как ее вывод можно теперь перенаправить в файл или пропустив через фильтр, например программу FIND, заставить ваш bat-файл сделать какой-нибудь глубокомысленный вывод. Вот таблица размеров трех вариантов программы вывода списка каталогов. Обратите внимание, что первый вариант возник всего лишь при попытке адаптировать программу А.Аброськина на XT, а он уже на 8 байт короче.  ·············║········║······································▄ ▀Имя файла ┐Размер ┐ Примечание ▀ ≤∙∙∙∙∙∙∙∙∙∙∙∙∙√∙∙∙∙∙∙∙∙√∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙├ ▀DRIVES1 COM ┐ 82 ┐Вывод на экран через функцию 0EH BIOS ▀ ▀DRIVES2 COM ┐ 71 ┐То же, но исправлены мелкие ошибки ▀ ▀DRIVES3 COM ┐ 76 ┐Вывод на стандартный вывод DOS ▀ ≥·············═········═······································█ Что же касается модуля crt, то претензий к нему горадо больше. Во-первых, он громоздок - кто читал тексты Run-Time Library тот знает - в отличие, например, от модуля DOS, CRT всегда линкуется к программе целиком. Во-вторых, он сильно осложняет жизнь неопытным любителям писать графические программы, так как далеко не все знают что для работы CRT в графике нужно поместить в программу оператор присваивания DirectVideo:=False; и вместо этого пишут собственные процедуры для замены Readln и Writeln, как правило, хуже оригинала. (Сам этим грешил в свое время). Если вам нужны из этого модуля только функции работы с клавиатурой и звуком, попробуйте придумать что-нибудь другое. (cm. например статью в "Мониторе" 6.93 "Звуковые устройства") Рекором, который я видел в этом отношении была программа на Turbo Vision, которая в случае аварийного завершения использовала процедуру ClrScr из модуля Crt, вместо того, чтобы честно сделать TApplication.Done. Вообще, я встречал весьма квалифицированных программистов, убежденных, что Turbo Vision использует Crt. :-) И еще одно замечание. На этот раз не А.Аброськину, а А.Тихонову, автору статьи "Показать картинку" в том же номере "Монитора". Кто вам сказал, что палитра EGA/VGA насчитывает только 64 цвета? То есть для EGA это действительно так, а у VGA цветов, извините, 262144. И средства BGI позволяют любые 16 из них на экране получить. Для этого есть процедура SetRGBPalette(ColorNum,Red,Green,Blue:Integer); Да, с налету она у вас не заработала. Признаюсь, сам долго бил себя кулаком по лбу, когда мне объяснили, почему не работает такой фрагмент программы: Var Pal:Array[0..15]of record R,G,B:byte end; i:integer; . . . . . for i:=0 to 15 do With Pal[i] do SetRGBPalette(i,R,G,B); Дело в том, что первый параметр процедуры SetRGBPalette - не номер цвета, а номер индекса палитры, т.е. то, что еще предстоит связать с цветом процедурой SetPalette (или воспользоваться уже установленными связями. (Увы, они отнюдь не тривиальны, поэтому лучше сделать так: for i:=0 to 15 do begin SetPalette(i,i); With Pal[i] do SetRGBPalette(i,R,G,B); end; Рекомендую всем читателям статьи "Показать картинку", имеющим VGA, сделать нечто аналогичное с палитрой, хранящейся в PCX или GIF - файле. Насколько выиграет изображение от того, что значения каждой из цветных составляющих будут не от 0 до 3, а от 0 до 63! И последнее: не стоит запоминать и восстанавливать палитру, если единственная графическая операция после ее восстановления это CloseGraph. Я бы даже в случае необходимости продолжения работы с графикой для очистки экрана и восстановления палитры по умолчанию воспользовался бы SetGraphMode. На последок, чтобы авторы раскритикованных программ не обижались, приведу текст последней из своих модификаций программы выдачи списка текущих каталогов (TASM 2.0)- можете покритиковать и меня - зачем, например, мне здесь понадбилась директива LOCALS? MODEL TINY LOCALS .CODE ORG 100H ASSUME DS:@CODE Start:Mov AH,30H ; Определение версии DOS INT 21H CMP AL,4 JAE @@1 MOV Recs,51H ;Меняем только байт, а не слово @@1: MOV AH,52H INT 21H MOV AL,ES:[BX+20H] MOV [Drives],AL ;Зачем здесь квадратные скобки?Не пойму... LES DI,ES:[BX+16H] @@2: Push ES Push DI MOV AX,1212H ;А за это А.Аброськину - спасибо! INT 2FH ;Я бы писал через ScaSB PUSH DS ;Сохраним DS PUSH ES ;и зашлем ES:DI в DS:DX MOV DX,DI POP DS MOV BX,1 ;File Handle 1 = StdOut MOV AH,40H INT 21H ;Ошибки, по-моему, быть не могло - CF не ;проверяем POP DS ;Восстановим DS MOV CX,2 MOV DX,OFFSET CrLF ;И выведем еще два байта- конец строки MOV AH,40H INT 21H POP DI POP ES ADD DI,Records ;Здесь с размером записи работаем как со ;словом DEC Byte PTR Drives ;Byte Ptr здесь не обязательно LOOPNZ @@2 RET CrLf DW 0A0DH ;Увы, файловые функции требуют жертв. CR LF ;приходится хранить как данные Records LABEL WORD ;А эта конструкция позволит - ; интерпретировать одно и то же место как слово и как байт Recs DB 58H DB 0 ;*********** на этом COM-Файлу - конец *********** Drives DB ? ;Неинициализированные переменные - в конец ;программы - в файле им не место END Start