Два очень разных решения: сделанное вручную и сгенерированное с помощью специального языка. Интересно, чему все-таки равен критический минимум профессионализма?
Где же эти 5 байт?
На заданный в вашей статье вопрос "Можно ли сделать меньше?" я ответил: "Безусловно, можно", справедливо рассудив, что меньше можно сделать любую программу.
Прежде всего я обратил внимание на две опечатки в тексте программы:
Кроме того, программа Виталия Ланского начинает рисовать фрактал ЗА ПРЕДЕЛАМИ ЭКРАНА, что легко обнаруживается визуально, впрочем, и исправляется эта неточность также легко. Необходимо просто поменять местами инструкции MOV ES: [BX],CL и DEC BX.
Но это так, к слову, а по делу - я два дня изучал программу и искал, где же скрыты резервы для уменьшения ее длины, ...и нашел!
На бумаге программа была сокращена до 90 байт, но, как часто бывает, в жизни все оказалось не так просто и, выиграв 7 байт на бумаге, я проиграл 2 байта на компьютере, увы :-(. Так где же скрывались эти 5 байт?
Во-первых, это сегментный регистр ES для обращения к видеопамяти (MOV ES: [BX], CL), используя DS (сегментный регистр по умолчанию), мы экономим первый байт.
Во-вторых, два раза в стек математического сопроцессора загружается значение x=1 (один раз перед циклом TUDY, второй раз внутри цикла в конце). Мы загрузим х один раз в начале цикла и выиграем 4 байта, убрав инструкции FLD1; FSTP ST(3), но проиграем 2 байта, так как в конце цикла необходимо очистить стек сопроцессора от использованного уже x (см. FCOMP ST(1)). Итог: минус 2 байта.
Ну и наконец, команда JZ VSE в середине цикла PAINT. Организуем цикл CIKL по DX, а цикл PAINT по CX с помощью инструкции LOOP, не меняющей, кстати, флаги процессора, - это позволит избавиться от строки JZ VSE, а для того, чтобы выйти все-таки из цикла TUDY, мы вместо JMP SHORT TUDY напишем JNZ TUDY. Так мы сэкономим еще 2 байта (обратите внимание на DEC DX, хотя сначала находящийся в DH "мусор" не влияет на работу программы). Итог: еще минус 2 байта.
В результате программа сократилась с 97 до 92 байт, не утратив при этом своей функциональной полноты и законченности, присущей истинному произведению искусства :-).
Компилировать программу желательно старым TASM'ом (я пользовался версией 2.0), так как он не добавляет перед всеми инструкциями сопроцессора инструкцию WAIT.
В заключение позвольте выразить огромную благодарность Виталию Ланскому и надежду, что найдется человек, который сделает эту программу еще, еще и еще короче :-).
; (f)97 Орехов Михаил Mihal ; Soft( г. Нижнекамск .286 .287 MANDEL SEGMENT BYTE PUBLIC 'CODE' ASSUME CS: MANDEL, DS: MANDEL ORG 100h START^ mov al, 13h ; Установим видеорежим 13р int 10h ; 320x200 256 цветов DB 0dbh, 0E3h ; инструкция FINIT fld1 ; Верхнее значение Y=1 fldl2t ; Число для проверки сходимости fldPI ; fidiv WORD PTR TUDY+1 ; DelX=DelY=0. 0098 push 0A000h ; Сегмент видеопамяти=А000 pop ds mov bh, 0fah ; Рисуем с ВХ=64000 TUDY: mov cx, 320 ; Точек в строке экрана fsub ST(2), ST ; Y=Y-DelY fld1 ; Правое значение Х=1 PAINT: ;******** Вычисляем сумму ряда ******** fld ST ; Re=X fld ST(4) ; Im=Y mov dl, 100 ;Обсчет 100 элементов ряда CIKL: fld ST(1) ; Загрузим Re fmul ST, ST ; ST=Re*Re fld ST(1) ; Загрузим Im fmul ST, ST : ST=Im*Im fsubp ST(1), ST ; ST=Re*Re-Im*Im fadd ST, ST(3) ;Новое Re=Re*Re-Im*Im+X fld ST(2) ; Загрузим Re fmul ST, ST(2) ; ST=Re*Im fadd ST, ST ; ST=2*Re*Im fadd ST, ST(7) ; Новое Im=2*Re*Im+Y fst ST(2) ;Сохраним новое значение Re fabs ; ST=ABS(Im) fcomp st(6) ;ST меньше, чем 3. 3219 ? DB 0dfh, 0E0h ; FSTSW ax fstp ST(2) ;Сохраним новое значение Re sahf jnc End_Calc ; Если ST>3. 3219 тогда на End_Calc dec dx ; !В DH сначала мусор,затем 0! jnz Cikl ;*** Закончили вычислять сумму ряда*** End_Calc: fcompp st(1) ;Чистим стек сопроцессора от Re и Im dec bx mov [bx], dl ; Рисуем точку Snova: fsub ST, ST(1) ; X=X-DelX loop Paint NewString: fcomp st(1) ;Чистим стек от Х jnz TUDY VSE: int 21h ; Ждем нажатия на клавиши mov ax, 3 ; Включаем текстовый режим int 10h ret ; Возврат в OS MANDEL ENDS END Start
инженер-программист отдела
САПР (адрес в редакции).
Не только спортивный интерес
К вопросу о рисовании фрактала Мандельбротта. Объем программы 63 байта. Причем в ней не используется сопроцессор и ускорено рисование: 0,33 с против прежних 2,36 с (от установки графики до запроса клавиши; на 486-м процессоре).
Выход из программы - по
В дополнение к теме сообщаю, что на основании математических методов оптимизации мною создан язык программирования высокого уровня AZ, его объем 5 (пять!) Кбайт. Причем это не игрушка, а основной инструмент работы, на нем выпущен ряд сложных программ и мощных СУБД. В языке есть команды, управляющие графикой, звуком, мышью, вызовом других программ, обработкой данных, а также специализированные команды и непременные атрибуты языков общего назначения. Его применение позволило полностью отказаться от других языков, в том числе от ассемблера. (Фрактал написан на AZ, а затем по СОМ-файлу воссоздан ассемблеровский текст.)
Выпускаемые на AZ программы в 10-100 раз меньше, чем на других языках высокого уровня. Время трансляции программы из 10 тысяч операторов (из исходного текста непосредственно в загрузочный модуль) около 1 с, при этом получается модуль примерно в 60 Кбайт. Программы на AZ отличаются высокой скоростью, экономичностью и надежностью, не достижимой иными средствами.
Не предлагаю читателям соревноваться в создании языков, но буду благодарен, если кому-то удастся сократить подпрограммы, которые AZ добавляет к выпускаемым модулям. Например, рисование произвольного отрезка в графическом режиме 12h (640х480). Подпрограмма должна быть быстрой, без умножения, деления и операций сопроцессора, хотя любопытны и медленные варианты, если они отличаются компактностью. Она должна давать плавную линию, насколько позволяет точечная графика. Координаты концов и цвет поступают в регистрах, в каких - выбирайте сами (у нас это: СХ, ВР, SI, DI, AH). Сохранение регистров общего назначения излишне, однако DS и ES надо вернуть в исходном виде. Графический режим считается заданным, выходить из него в подпрограмме не следует. Но маски и все нужное, что не появляется само при вызове режима 12h, надо задать в подпрограмме. DS и ES даны, можно занимать соответствующие сегменты, хотя это и не приветствуется. Желательн работать на 386-м процессоре. Вот решение из 92 байтов (в машинных кодах):
59, 206, 126, 4, 135, 253, 43, 241, 214, 186, 206, 3, 239, 180, 160, 142, 232, 184, 1, 15, 239, 184, 8, 128, 210, 204, 193, 233, 3, 107, 221, 80, 3, 217, 177, 80, 43, 253, 125, 4, 247, 217, 247, 223, 59, 254, 125, 4, 4, 128, 135, 254, 139, 239, 137, 62, -2, -1, 209, 255, 239, 101, 8, 7, 81, 43, 254, 124, 8, 60, 0, 125, 10, 43, 201, 235, 4, 3, 62, -2, -1, 208, 204, 19, 217, 89, 77, 125, -29, 195
Уверен, что накопление оптимальных реализаций различных функций имеет не только спортивный интерес. Оно помогло бы при разработке новых пакетов, языков, операционных систем и процессоров. Опыт создания AZ выявил огромные резервы оптимизации, о которых, по-видимому, мало кто догадывается. Причем резервы почти даровые - для их использования не нужны ни дорогостоящее оборудование, ни новые технологии. Зато из 286-го процессора они зачастую позволяют выжимать то, что не по силам модным программам даже на Pentium.
TEXT SEGMENT BYTE PUBLIC 'CODE' ORG 100h assume cs:TEXT, ds:TEXT START: mov al,19 int 10h db 104, 0, 160 ; push 0A000h pop ds shr si, 1 ; si=Y=128 Rou: mov di, -321 ; di=X=-321 dec si ; Y=di=X=-321 dec si ; Y=Y-1 Col: inc di ; X=X+1 je Rou sub bp, bp ; Re=0 mov cl, 46 ; Color Calc: lea bp, [bp+di+127] ; Re=Re+X+127 add ax, si ; Im=Im+Y push ax ; push Im imul bp ; Im=Im*Re db 193, 248, 5 ; sar ax, 5 Im=Im/32 inc ax ; Im=Im+1 pop dx ; Old Im db 15, 175, 237 ; imul bp, bp Re=Re*Re db 15, 175, 210 ;imuldx, dx Im=Im*Im jb Paint sub bp, dx ; Re=(Re*Re-Im*Im) db 193, 253, 6 ; sar bp, 6 Re=Re/64 loop Calc Paint: mov [bx], cl sub ax, ax ; Im=0 inc bx jne Col int 16h ; Key (Escape: al=27, ah=1) aad 232 ; al = 27+1*232 = 3, ah=0 int 10h ; Mode 3 ret TEXT ENDS END START
к.ф.-м.н., с.н.с. Уральского госуниверситета
(адрес в редакции).