Два очень разных решения: сделанное вручную и сгенерированное с помощью специального языка. Интересно, чему все-таки равен критический минимум профессионализма?

Где же эти 5 байт?

На заданный в вашей статье вопрос "Можно ли сделать меньше?" я ответил: "Безусловно, можно", справедливо рассудив, что меньше можно сделать любую программу.

Прежде всего я обратил внимание на две опечатки в тексте программы:

  • инструкция сопроцессора FLDL2T напечатана как FLD2T;
  • комментарий ";clear and increase stp(Twice)" перенесен на следующую строку и может сбить с толку не знающего ассемблер человека.
  • Кроме того, программа Виталия Ланского начинает рисовать фрактал ЗА ПРЕДЕЛАМИ ЭКРАНА, что легко обнаруживается визуально, впрочем, и исправляется эта неточность также легко. Необходимо просто поменять местами инструкции 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-м процессоре).

    Выход из программы - по . Оператор mov cl,46 отвечает за цветовое оформление. Если оно не понравится, замените 46 на 80 или 100.

    В дополнение к теме сообщаю, что на основании математических методов оптимизации мною создан язык программирования высокого уровня 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
    Николай Васильевич Невесенко,
    к.ф.-м.н., с.н.с. Уральского госуниверситета
    (адрес в редакции).