Разбор демки на 128 байт из архива 1997 года

    Очень приятно осуществлять свои желания, особенно из далёкого прошлого, такого далёкого что уже и забыл что этого когда-то хотел. Я мало знаю о демосцене и уж точно никогда не следил ни за авторами ни за их работами, мне просто нравилось смотреть то что получалось. Иногда мне хотелось в этом разобраться, но тогда мне не хватало знаний и опыта, позже усидчивости, а потом и вовсе у меня пропал к этому интерес. Но недавно мой друг, с кем мы учились в то время и который поставлял нам все новинки, включая демки, с BBS и Fidonet, потому что у него чуть ли ни у единственного был и телефон и модем и компьютер одновременно, посетил CAFePARTY со своими работами, что заставило меня открыть архив моего первого компьютера, выбрать демку и разобраться.

    pentagra.com

    Объективно оценивая свои силы я взял 128 байтовое интро которое мне понравилось визуально. Файл pentagra.com за подписью Mcm, 128 байт, последнее изменение 24.09.1996 18:10:14, шестнадцатеричный дамп:

    000000: b0 13 cd 10 68 00 a0 07 06 1f ac ba c8 03 ee 42
    000010: b1 40 ee 40 6e 6e e2 fa b8 3f 3f bb 40 01 bf 40
    000020: 05 57 b1 78 ab 03 fb e2 fb 5f b1 60 88 01 aa 03
    000030: fb 03 fb e2 f7 b1 61 88 01 aa 2b fb 2b fb e2 f7
    000040: bf d1 99 57 b1 78 ab 2b fb e2 fb 5f b1 8f f3 ab
    000050: 81 fe 00 fa 73 12 ac 0a c0 74 0d 48 88 44 fe 88
    000060: 04 88 40 ff 88 84 bf fe 03 f2 42 75 e3 e4 60 3c
    000070: 01 75 a5 b8 03 00 cd 10 c3 00 00 00 00 4d 63 6d

    Из того же архива я вытащил:

    • Hiew 6.11 (на сайте можно найти 6.50) — я его использовал в качестве дизассемблера
    • Пакет TASM — которым я собрал обратно полученный код, чтобы убедиться что ничего не напутал
    • Flambeaux Software's TECH Help! 6.0 — в меру подробный и исчерпывающий интерактивный справочник по DOS API, функциям BIOS, железу и ассемблеру
    • Майко Г.В. Ассемблер для IBM PC — почти карманного формата справочник по всем основным командам Intel 8086 и правилами оформления текста программы. Без архитектурных подробностей и с элементарными примерами, только самые базовые вещи. Здесь есть почти всё необходимое, но писать на ассемблере в отрыве от среды не получается
    • Поэтому вторая книга Зубков С.В. Assembler. Для DOS, Windows и Unix — путеводитель по аппаратным закоулкам и DOS

    От экстремально-минимальной реализации стоило ожидать использования трюков и нестандартных подходов, но кроме некоторых допущений в начальных условиях я не увидел никаких технических уловок, но увидел уловку алгоритмическую. И тут пару слов следует сказать об опыте. В чём может заключаться сложность? Или в реализации или в алгоритме. Например, в команде mov di, 099d1h, можно испугаться магической константы. Но если находиться в контексте использования, то становится ясно, что это адрес для доступа по экранным координатам X и Y, где X=17, Y=123, 320 это разрешение экрана в пикселях по горизонтали. Вместе это даёт нам 17+123*320, преобразование двумерных координат к одномерным.

    Смотря сейчас на то что происходит на экране, я достаточно легко представляю как смог бы это реализовать, пусть не в с 128 байтах, пусть не 100% похоже, но смог бы. А 20 лет назад не мог, хотя все используемые мною инструменты я вытащил с пыльных полок и мне не надо было заглядывать в Интернет, чтобы понять как это работает. Поэтому в первую очередь это контекст, понимание ЧТО происходит, а уж вопрос трюков и КАК это сделать на втором месте.

    Что мы видим:

    1. 5 линий пентаграммы. Это не обязательно прямые неразрывные линии по всем канонам. Мы видим только общую фигуру, без деталей
    2. Эффект пламени, который состоит из двух важных частей: правильно подобранной палитры и алгоритма постоянного изменения цвета точек на экране с элементами неопределённости, но сохранением непрерывной последовательности палитры для соседних точек. Например, можно рассчитать весь текущий экран усреднив значения соседних пикселей с предыдущего экрана, а в случайных местах добавить более «яркие» точки, или не в случайных местах, но случайные по значению, или вовсе не случайно, достаточно отойти от линейного порядка. Один из вариантов как это сделано в DOOM. Результат должен получиться в виде перетекающих друг в друга цветов, от постоянно возникающих ярких областей к затухающим

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

    DOS и загрузка .COM программ


    Программа в .com файле это чистый код, никаких заголовков, надо просто поместить его в нужное место. Этим занимается DOS, точнее системный вызов 4Bh. Происходит достаточно много действий, остановимся на результате:

    • Все сегментные регистры CS, DS, ES, SS загружены одним значением
    • Для всей программы зарезервировано 65536 байт, ровно один сегмент на который и указывают все сегментные регистры. Первые 256 байт занимает системный заголовок — PSP (Префикс Программного Сегмента). По адресу CS:0, первое поле PSP, располагается команда INT 20h — завершить текущую программу и передать управление родительскому процессу. Сама программа начинается с адреса CS:100h и занимает следующие 128 байт
    • В стек помещено слово 0000h, регистр SP равен FFFEh. Это значит, что последние два байта в этом сегменте по адресу SS:FFFEh обнулены. Фактически это ближний адрес возврата из процедуры, который приведёт нас к команде завершения по адресу CS:0
    • Регистры AL и AH содержат признак ошибки определения букв дисков из первого и второго аргумента при вызове программы. Если ошибок нет то они равны 0, если есть то FFh

    Я искренне считал, что в общем случае состояние регистров не определено. Но в разбираемом коде делается, на мой взгляд, весьма смелое предположение об их начальном состоянии, в частности о регистрах CX, SI и флаге направления DF. Я не нашёл этому подтверждения в том списке источников что приводил выше, поэтому отправился полистать исходники MS-DOS 2.0:

    • Про DF можно предположить, что он сброшен командой cld, потому что в последней перед передачей управления пересылке строк используется прямое направление, следовательно DF сброшен. Хотя нет явного использования cld именно в этом месте, команда сброса флага направления встречается достаточно часто перед многими другими пересылками
    • SI содержит 100h, потому что используется для определения смещения которое будет загружено в регистр счётчик команд IP
    • CX равен FFh, потому что используется как счётчик с начальным значением 80h для пересылки содержимого всей командной строки и соответственно после пересылки равен 0. А после этого в CL, как временную переменную, загружается FFh и используется для выставления признака ошибки буквы диска в AL и AH

    Исходников более новых версий нет, но есть исходники DOSBox:

    reg_ax=reg_bx=0;reg_cx=0xff;
    reg_dx=pspseg;
    reg_si=RealOff(csip);
    reg_di=RealOff(sssp);
    

    То есть совпадает с тем что я увидел в исходниках MS-DOS (2-й версии!), видно и начальные значения других регистров, здесь это явная, специальная инициализация. Для MS-DOS значения регистров кроме AX, сегментных и стека, это рудименты их использования по другому назначению это не догма и не стандарт, поэтому про них нигде и не упоминается. Но зато становится немного понятным образовавшаяся экосистема и вся боль Microsoft по поддержке совместимости со старыми версиями, вынуждающая тащить за собой все случайно образовавшиеся значения, потому что к ним так привыкли программисты.

    Наконец, для нас этих знаний достаточно, начинаем восстанавливать программу с заголовков:

    .186
    .model tiny
    .code
    .startup
    

    Определяем тип процессора 80186, потому что используем команду outsb, которая появилась только в этой модели. Один сегмент кода и точка входа в программу, которая вместе с определением модели памяти tiny позволит компилятору посчитать правильно все смещения переменных и переходов. При сборке tlink используется ключ /t, на выходе это даст .com файл.

    Графика и палитра


    Для переключения в графический режим необходимо обратиться к функции BIOS, для чего вызывается прерывание 10h, AH=0, в AL помещаем идентификатор нужного режима — 13h:

    mov al, 13h ;b0 13
    int 10h     ;cd 10
    

    Обратите внимание, что AH мы не трогаем, предполагая что там ноль, согласно условиям загрузки программы. Выбранный режим соответствует графическому разрешению 320 на 200 точек с 256 цветной палитрой. Для отображение точки на экране нужно записать в область памяти, которая начинается с адреса A000h:0, байт соответствующий цвету. Сегментные регистры данных заполняем этим значением:

    push 0a000h ;68 00 a0
    pop es      ;07
    push es     ;06
    pop ds      ;1f
    

    Логически память организована в виде двумерного массива, в который отображаются экранные координаты, 0:0 соответствует левому верхнему углу. После переключения режима она заполнена нулями — чёрный цвет в палитре по умолчанию. Формула перевода в линейное смещение X+Y*L, где L — разрешение по горизонтали, в нашем случае 320. В этом виде я и буду писать в тех местах где используются константы, при трансляции текста программы они вычислятся автоматически.

    Для смены палитры мы напрямую обращаемся к оборудованию используя порты ввода вывода:

    lodsb         ;ac
    mov dx, 03c8h ;ba c8 03
    out dx, al    ;ee
    

    Первая команда загружает в AL байт данных расположенный по адресу DS:SI. В DS у нас загружен сегментный адрес видеопамяти и мы знаем что она заполнена нулями, в SI — в общем случае неизвестно что, как минимум не 0. Нам это почти не важно, куда бы не указывал SI мы почти наверняка попадём в видеопамять которая занимает в этом разрешении 320*200=64000 байт, практически весь сегмент целиком. Таким образом мы ожидаем, что после этой команды AL=0. К SI прибавляется или вычитается единица, это зависит от установки флага направления DF. Пока нам это тоже не особо важно, куда бы не сдвинулся SI мы всё ещё остаёмся в области видеопамяти заполненной нулями.

    Далее загружаем DX номером порта 03C8h, вывод в который определяет какой цвет из 256 мы собираемся переопределить. В нашем случае это 0 из AL.

    Цвет кодируется в палитре RGB и для этого следует писать в порт 03C9h (на один больше чем 3C8h) три раза подряд, по разу для каждой из компонент. Максимальная яркость компоненты — 63, минимальная 0.

    inc dx         ;42
    mov cl, 64     ;b1 40
    
    PALETTE:
        out dx, al ;ee
        inc ax     ;40
    
        outsb      ;6e
        outsb      ;6e
    loop PALETTE   ;e2 fa(-6), короткий переход на 6 байт назад
    

    Увеличим DX на единицу, чтобы в нём был нужный номер порта. CL это наш счётчик цикла равный 64, при этом мы полагаем что CH=0, как описано ранее исходя из начальных условий загрузки. Далее мы выводим в порт первую компоненту — красную, яркость которой будет храниться в AL, именно она у нас будет изменяться, на первом шаге 0. После чего увеличиваем её яркость на единицу, чтобы вывести в следующей итерации. Далее выполняем две команды outsb записывающие в порт, номер которого содержится в DX, байт из области памяти DS:SI, помним что у нас там нули. SI каждый раз изменяется на единицу.

    Как только мы вывели три компоненты, то к номеру цвета автоматически прибавляется единица. Таким образом не надо повторно определять цвет выводом в порт 3C8h если цвета идут подряд, что и требуется. Команда loop уменьшит CX на единицу, если получится значение отличное от нуля то перейдёт к началу цикла, если 0 то к следующей за циклом команде.

    Всего 64 повтора. На каждом повторе определяем для цвета, начиная с 0 до 63, красную компоненту яркостью совпадающую с текущим номером цвета. Зелёную и синюю составляющие сбрасываем, чтобы получить вот такую палитру от минимальной до максимальной яркости красного:

    palette


    Линии


    Настраиваем начальные значения цвета и координат:

    LINES:
    mov ax, 03f3fh   ;b8 3f 3f
    
    mov bx, 0+1*320  ;bb 40 01
    mov di, 64+4*320 ;bf 40 05
    push di          ;57
    

    В AL и AH загружаем максимальный возможный (самый яркий) цвет 63(3Fh), соответственно AX определяет сразу две точки. BX — максимальное разрешение по горизонтали. В дальнейшем это будет использоваться, чтобы прибавить или отнять одну строку от текущих координат. DI — координаты 64:4, сохраняем их в стеке.

    Рисуем первую линию из верхнего левого угла к правому крайнему:

    mov cl, 120    ;b1 78
    LINE1:
        stosw      ;ab
        add di, bx ;03 fb
    loop LINE1     ;e2 fb(-5)
    

    Настраиваем счётчик — это будет количество строк. Далее сохраняем слово (два байта) из AX по адресу ES:DI. Это действие отобразит две точки на экране с максимальным цветом из нашей палитры, потому что ES настроен на видеопамять, а в DI установлены конкретные координаты. После этого действия к DI прибавится 2, так как были записаны два байта. Мы явно не устанавливаем флаг направления DF и полагаемся на то что он сброшен, опять вспоминаем наши начальные условия загрузки программы. В противном случае двойка бы отнималась, что не позволило бы нарисовать желаемую линию.

    Далее DI=DI+BX, что эквивалентно увеличению координаты Y на единицу. Таким образом, в теле цикла рисуются две точки в одной строке, координата X увеличивается на 2, а координата Y на 1 и повторяется это действие 120 раз, картинка с результатом чуть ниже.

    Вторая линия — из верхнего левого в вершину:

    pop di               ;5f
    mov cl, 96           ;b1 60
    LINE2:
        mov [bx+di], al ;88 01
        stosb            ;aa
        add di, bx       ;03 fb
        add di, bx       ;03 fb
    loop LINE2           ;e2 f7(-9)
    

    Восстанавливаем начальные координаты 64:4 и настраиваем счётчик на 96 повторений. Выводим одну точку, но на одну строку ниже текущих координат. Как и раньше это достигается прибавление значения из BX, только не сохраняя новые координаты. Конструкция [bx+di] или [bx][di] называется адресация по базе с индексированием и работает на уровне процессора, а не транслятора. В качестве сегментного регистра по умолчанию с BX используется DS. После чего выводим вторую точку, но уже в текущие координаты. DI, а следовательно X увеличивается на единицу, так как использована только одна команда пересылки байта — stosb. Последние две команды тела цикла — это увеличение Y на 2, для чего опять используем BX.

    После отрисовки двух линий, получается следующее изображение около верхнего левого угла:

    line 1,2


    Слева и сверху координаты, справа адреса смещения строки в видеопамяти. Точка 64:4 будет нарисована дважды.

    Третья линия — из вершины к верхнему правому углу:

    mov cl, 97          ;b1 61
    LINE3:
        mov [bx+di], al ;88 01
        stosb           ;aa
        sub di, bx      ;2b fb
        sub di, bx      ;2b fb
    loop LINE3          ;e2 f7(-9)
    

    DI уже содержит нужное значение координат 160:196, нам надо нарисовать линию из вершины, где закончилась предыдущая линия, двигаясь вверх экрана сохраняя тот же угол наклона. Соответственно цикл почти идентичный. CX увеличен на 1, потому что текущая координата Y на 2 больше (ниже) того где закончилась предыдущая линия, она посчиталась уже для следующей итерации. Поэтому, чтобы попасть в верхний угол, надо сделать лишний шаг. Движение по X продолжается в том же направлении — плюс один после каждой итерации, а по Y вместо прибавления мы отнимаем двойку. Точки выводятся в том же порядке, сначала нижняя потом верхняя.

    line 3


    Четвёртая линия — из левого крайнего к верхнему правому углу:

    mov di, 17+123*320 ;bf d1 99
    push di            ;57
    
    mov cl, 120        ;b1 78
    LINE4:
        stosw          ;ab
        sub di, bx     ;2b fb(-5)
    loop LINE4
    

    Мы опять находимся в нужных координатах, но это не используется, видимо для того чтобы не менять флаг направления DF. Поэтому в DI помещаются новые координаты и сохраняются в стеке.

    Далее всё идентично первой линии, только координата Y не растёт, а уменьшается, мы поднимаемся вверх.

    Пятая линия — горизонтальная:

    pop di      ;5f
    mov cl, 143 ;b1 8f
    rep stosw   ;f3 ab
    

    Тут всё просто, используется механизм повтора пересылки микропроцессора, так как горизонтальная линия соответствует простому увеличению адреса каждой следующей точки. В DI восстанавливается адрес соответствующий координате левого крайнего угла, запомненный на предыдущем шаге. Задаётся количество повторений в CX и применяется префикс повторения c командой пересылки слов.

    После этого действия мы имеем полностью нарисованную пентаграмму самым ярким цветом. 80 использованных байт и 48 в запасе.

    Магия огня


    Задаём граничные условия для вычислений:

    FLAME:
    cmp si, 320*200 ;81 fe 00 fa
    jae NEXT_PIXEL  ;73 12
    
    lodsb           ;ac
    or al,al        ;0a c0
    jz NEXT_PIXEL   ;74 0d
    

    В SI будет координата текущей точки для расчётов, если мы выходим за границы экрана то никаких расчётов с этой точкой не производим, переходим к вычислению следующей.

    lodsb загружает байт из области DS:SI в AL, то есть цвет точки в текущих координатах. Если он равен 0, то тоже ничего не предпринимаем и переходим к следующей точке.

    Вычисление нового цвета

    Это основной алгоритм изменения значений цвета на экране, это ещё не пламя, это база для него. Рассчитываем соседние точки и добиваемся непрерывности цвета:

    dec ax               ;48
    mov [si-2], al       ;88 44 fe
    mov [si], al         ;88 04
    mov [bx+si-1], al    ;88 40 ff
    mov [si-1-1*320], al ;88 84 bf fe
    

    Отнимаем от AX, фактически от AL, единицу, в котором содержится не нулевое значение цвета полученное из текущих координат. Далее полученное значение мы запишем во все соседние точки, относительно текущей координаты, то есть немного их притушим, исходя из нашей палитры.

    Так как после lodsb значение SI увеличилось на единицу и уже не соответствует той точке цвет которой мы прочитали в AL, то это приходится корректировать. Обратите внимание, что уже не используются команды пересылки байт stosb, вместо этого применяется mov, чтобы точно определить адрес куда будет помещено значение. Если принять что текущие координаты X:Y, для них SI-1, то:

    • mov [si-2], al — запись нового цвета в точку X-1:Y, слева от текущей. От SI отнимается 2 по причине описанной чуть выше, так как к нему уже прибавлена лишняя единица
    • mov [si], al — запись нового цвета в точку X+1:Y, справа от текущей. В SI уже X+1
    • mov [bx+si-1], al — запись нового цвета в точку X:Y+1, снизу от текущей. Опять используем BX для Y+1
    • mov [si-1-1*320], al — запись нового цвета в точку X:Y-1, сверху от текущей. BX мы не сможем использовать, так как нам надо отнимать координату, архитектура процессора не позволяет это сделать в таком виде, поэтому используется константа в соответствии с формулой приведения координат

    В качестве сегментного регистра выступает DS, который используется по умолчанию вместе с SI и BX.

    Нигде не проверяется ситуация когда точка попадает на край экрана. Это не может привести к сбою, так как мы всегда будем в границах видеосегмента. Соседняя точка может попасть либо в не отображаемую область с адресами больше 64000 либо на соседнюю строку, что нам никак не вредит и даже чуть-чуть помогает, как будет видно из дальнейшего описания.

    Та самая магия, вычисление координат следующей точки

    NEXT_PIXEL:
    add si, dx ;03 f2
    inc dx     ;42
    jnz FLAME  ;75 e3(-29)
    

    Вернёмся чуть назад, мы нигде специально не устанавливали начальное значение SI, а в DX у нас остался номер порта ввода вывода который мы использовали для палитры. Выполняем всего три простых действия SI=SI+DX, очевидно это задаст новые координаты, какие? DX=DX+1 и если DX не равен 0, то возвращаемся обратно к базовому алгоритму получения и вычисления соседних точек, то есть DX это какой-то счётчик?

    Мы знаем что надо обойти все точки и рассчитать изменения яркости их соседей. Если сделать это подряд, то вероятно мы получим статичный градиент, может не совсем ровный, но неизменный вокруг наших линий. Мы знаем размерность нашего экрана и сколько точек мы должны обойти, но здесь мы этим почти пренебрегаем, точнее выбираем близкое значение 65536 вместо точных 64000. DX это в самом деле счётчик, как раз 65536. Но почему не важно его начальное значение и почему мы берём конечное значение больше чем всего точек на экране?

    Потому что мы обходим точки не подряд и не все. Каждая следующая линейная координата больше предыдущей на величину DX. То есть, в SI сумма из DX элементов простой арифметической прогрессии: 0,1,2,3,4,5,6,...,362,363,...,65535. Это уже даёт нам нелинейность, если начать с SI=0 и DX=0, то в SI получим: 0,1,3,4,6,10,15,21,...,65341,65703,...,2147450880.

    Но это ещё не всё, так как размерность SI 16 бит, то значение больше 65535 мы получить не можем, происходит переполнение и в SI остаётся остаток по модулю 65536. Формула вычисления линейной координаты принимает такой вид SI=(SI+DX) MOD 65536, что совершенно ломает непрерывный порядок: 0,1,3,4,6,10,15,21,...,65341,167,530,894,…

    Теперь вспомним что SI никак не инициализируется, то есть когда в следующий раз мы вернёмся к этому циклу то начнём с той координаты где мы остановились, а не с 0 или какой-то заданной. Это добавит хаоса в нашу последовательность — удлинит количество не повторяющихся элементов. В противном случае, обход точек был бы всегда одинаковым, пусть и нелинейным. Эффект пламени присутствовал бы, но не так явно. Если и говорить о трюке, то это как раз он и есть. DX, всегда, кроме первого использования, неявно начинается с 0 как результат переполнения inc dx.

    И ещё немного хаоса добавляется нашими граничными значениями, так как для SI>=64000 никаких точек не будет нарисовано и последовательность вывода слегка сбивается. А пропуск всех точек с нулевым значением приводит к эффекту воспламенения на первых нескольких секундах работы программы. Это происходит потому что полный цикл заканчивается быстрее, так как большинство точек не обрабатывается. Но главное, потому что яркость для большинства точек будет только нарастать, их не смогут затенить соседние более тусклые участки — их просто ещё нет, а нулевые значения не рассчитываются. После того как полностью чёрные области исчезнут установится баланс, какие-то области будут увеличивать яркость, а какие-то и уменьшать.

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

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

    in al, 60h  ;e4 60
    cmp al, 01h ;3c 01
    
    jne LINES   ;75 a5(-91)
    

    Но перед этим считываем из порта 60h, это клавиатура, скан-код последней нажатой клавиши. Для ESC он равен 1. Если это так — была нажата клавиша ESC, двигаемся в сторону выхода.

    Завершение


    Стоит обратить внимание что во время обновления текущего экрана, что занимает какое-то время, выйти из программы нельзя, то есть реакция на ESC будет отложенная. Если во время ожидания и после ESC будет нажата ещё какая-то клавиша, то мы всё равно останемся в программе, из порта можно считать только последний скан-код. Ещё один момент, мы не подменяем и не используем для этого системные функции DOS и BIOS, не зависимо от того что мы считали из порта нажатая клавиша помещается в циклический буфер и будет, вероятно, прочитана оттуда следующей программой после завершения нашей интро, скорее всего файловым менеджером или command.com. Это приведёт к её обработке, например, Volkov Commander по ESC спрячет свои панели.

    Осталось вернуться к текстовому режиму 3:

    mov ax, 03h ;b8 03 00
    int 10h     ;cd 10
    

    Предполагается, что мы были именно в этом режиме до запуска программы, но в общем случае это может быть не так. Здесь мы обновляем AX целиком, потому что точно знаем, что AH не содержит 0.

    Теперь можно выходить:

    retn ;c3
    

    Это команда ближнего выхода из процедуры, которая возьмёт из стека значение помещённого там слова (два байта) и загрузит в счётчик команд IP. По начальным условиям в стеке у нас нули, это приведёт нас по адресу CS:0, где как мы знаем находится код команды int 20h — завершение работы.

    И 7 байт для копирайта:

    dd 0h    ;00 00 00 00
    db 'Mcm' ;4d 63 6d
    end
    

    Можно сказать что ещё осталось место, которое я бы потратил на более строгую начальную инициализацию, но так как всё работает и в современном DOSBox, наверное, автор всё сделал правильно.

    Пройдёмся ещё раз по порядку:

    1. Переключаемся в графический режим, сохраняя области видеопамяти в сегментных регистрах данных
    2. Рисуем 4 линии под одинаковыми углами, но в разных направлениях манипулируя только скоростью изменения координат каждой точки: или X+1 и Y+2, или X+2 и Y+1. Элемент линии состоит у нас не из одной точки, а из двух расположенных горизонтально или вертикально. Пятая линия параллельна горизонтали, рисуем её в одну команду встроенными в процессор механизмами
    3. Выбираем следующую точку экрана согласно формуле SI=(SI+DX) MOD 65536, каждый раз прибавляя к DX единицу, что даёт нелинейную последовательность значений, которую мы продолжаем в следующем цикле, так как не обнуляем SI. Вокруг полученной координаты мы уменьшаем яркость точек на 1. Сделав так 65536 раз, возвращаемся к предыдущему шагу, чтобы опять нарисовать яркие линии. Весь эффект и вся соль, по сути вот в этих двух простых командах — add si, dx и inc dx, две суммы ничего не значащих сами по себе и ничего не объясняющих, но имеющих колоссальное значение в результате
    4. Проверяем если была нажата ESC то выходим, восстановив текстовый режим экрана и дописав в конце свой копирайт

    Код программы целиком.
    .186
    .model tiny
    .code
    .startup
    
            mov al, 13h
            int 10h
    
            push 0a000h
            pop es
            push es
            pop ds
    
            lodsb
            mov dx, 03c8h
            out dx, al
    
            inc dx
            mov cl, 040h
    
    PALETTE:
            out dx, al
            inc ax
    
            outsb
            outsb
            loop PALETTE
    
    LINES:
            mov ax, 03f3fh
    
            mov bx, 0+1*320
            mov di, 64+4*320
            push di
    
            mov cl, 120
    LINE1:
            stosw
            add di, bx
            loop LINE1
    
            pop di
            mov cl, 96
    LINE2:
            mov [bx+di], al
            stosb
            add di, bx
            add di, bx
            loop LINE2
    
            mov cl, 97
    LINE3:
            mov [bx+di], al
            stosb
            sub di, bx
            sub di, bx
            loop LINE3
    
            mov di, 17+123*320
            push di
    
            mov cl, 120
    LINE4:
            stosw
            sub di, bx
            loop LINE4
    
            pop di
            mov cl, 143
            rep stosw
    
    FLAME:
            cmp si, 320*200
            jae NEXT_PIXEL
    
            lodsb
            or al,al
            jz NEXT_PIXEL
    
            dec ax
            mov [si-2], al
            mov [si], al
            mov [bx+si-1], al
            mov [si-1-1*320], al
    
    NEXT_PIXEL:
            add si, dx
            inc dx
            jnz FLAME
    
            in al, 60h
            cmp al, 01h
            jne LINES
    
            mov ax, 03h
            int 10h
    
            retn
    
            dd 0h
            db 'Mcm'
    end

    Для компиляции надо выполнить: tasm pentagra.asm и tlink /t pentagra.obj.

    Не знаю, стало ли понятным для вас ЧТО и КАК было реализовано, но мне кажется был использован красивый и необычный подход при создании эффекта пламени. Хотя мне и не с чем сравнивать, может так делали все, а теперь и вы так же сможете.

    Комментарии 81

      +8
      Хорошая статья, очень подробно всё описано (хотя я читал немного по-диагонали, т.к. лично для меня тут всё довольно очевидно, тем не менее 1-2 момента для себя всё же отметил). При этом интра не шибко сложная, но симпатичная. Для начинающего демомейкера в категории PC tiny intro, ИМХО, довольно хороший туториал получился.

      Я бы с удовольствием почитал разбор навороченных интр типа тех, что делает Digimind, к примеру. Или Rrrola, Red Sector Inc и другие ребята такого уровня. Hellmood/DESiRE, кстати, в своих микро-интрах (32/64 байта) обычно оставляет исходники с подробными комментами, за что ему большой респект. Вот, кстати, полезный ресурс в его соавторстве: sizecoding.org.

      p.s. Жоре привет! ;)
        +1

        Круто. В наше время, полагаю, потребовалось бы писать целый шейдер для подобного эффекта.

          +3
          Вы не правы.
          Способ реализации напрямую диктуется задачами.
          Ну а так… Здесь и написан по сути шейдер.
            0
            Если говорить про современный 3D-движок, который работает только с текстурами, мешами, выводя графику через DX/OGL, конечно же.
            UPD: а я не говорю про демки, скорее про реализацию подобного эффекта в какой-нибудь игре.
              0
              Демки на готовых движках никто не делает.
                0
                Есть всякие там Tooll2, fxEngine и т.п. штуки. Другое дело, что это не всегда приветствуется и порой вызывает споры.
                Ну и к слову сказать (хотя это уже несколько другое), группы нередко делают кастомные движки, но в основном для себя. И это уже не критикуется, ибо работа получается полностью авторская.
                  0
                  Эти штуки явно не попадают под определение «современный 3D-движок», не?
                    0
                    Unity…
                      +3
                      Кто-то делает демки под минимальный размер на Unity?
                        0
                        Демы пишут не только под определённый размер.
                        Довольно много компо не имеют требований по размеру.
                          0
                          Ну в данном то месте вроде как именно демки под мини размер обсуждаются. Это даже в названии статьи описывается.
                            0
                            В статье вообще 128-байтовая интра под DOS обсуждается :)
                            Тогда и обсуждение 3D-движков/OGL/DX не в тему.
                            Но мы же уже давно отклонились от темы в этой ветке обсуждения…
                              +1
                              тут речь как про про демосцену, если там и делают движок, то свой.
                              Ну или покажите из мира демосцены, что-то на юните или Unreal
                                0
                                Мне кажется тут как в спидранах: есть набор правил под любую ситуацию.
                                Более чем уверен, что есть какой нибудь набор правил и для «демо на UE»
                                  0
                                  Это нетипично, но бывает. Искать такие демы я не буду, т.к. таких критериев поиска на pouet, к примеру, нет. Но я думаю, что нагуглить при должном старании можно. Собственно, а почему нет, если это не запрещено правилами, а размер неограничен?

                                  Я был как раз на обозначенном автором CAFePARTY, и там была лекция про Tooll2 и даже дема на нём же (которая заняла призовые места и вызвала много дискуссий). Да, это не 3D-движок, но не все демы 3D. Кроме того, есть конкурсы типа One Scene Compo, где прямо в правилах написано, что можно использовать Unity, Unreal и пр.: cafeparty.org.ru/2019/compo-rules/#demointro
                0
                Работа шейдера и подобной интры принципиально разная.
                В шейдере каждый кадр вычисляется цвет каждого пикселя.
                А в подобной же интре меняется (вычисляется) координата точки и там определяется цвет. Грубо говоря, рисуется линия по-точечно (а не вычисляется цвет на основе принадлежности координаты линии для всего экрана). И изображение соответственно меняется отдельными точками, а не кадрами (тут даже нет ожидания начала кадра – ретрейса).

                Пример интр, работающих по принципу шейдеров – Puls by Rrrola (на основе рей-марчинга) или фракталы (Мандельброт/Жюлиа). Т.е. где идёт проход по всему экрану и вычисляется цвет каждой точки. Правда, без многопоточности :))
                  0
                  Возможно у нас разное понимание значения слова «по сути»?
                    0
                    Всё возможно :)
                +1
                При этом шейдер скорее всего будет занимать меньше места чем попытка реализовать что-то подобное на CPU

                В общем-то многие из современных демок и являются небольшими программами, которые инициализируют OpenGL/DirectX, и почти весь их объем — один или несколько сложных шейдеров.

                К примеру демка из 2010 «elevated» 4кб
                вот тут можно посмотреть на ютубе
                www.youtube.com/watch?v=jB0vBmiTr6o
                вот тут портированный на ShaderToy шейдер
                www.shadertoy.com/view/MdX3Rr
                Сам 4кб бинарник можно найти тоже (Windows), только под win10 (и может даже win7) нужно искать пофикшенную версию
                  0
                  Да, 4k обычно – это шейдер с кодом его запуска + музыка, созданная в 4klang (с плеером из той же коробки).

                  А по поводу места (меньше CPU-код или шейдер), то есть 32- и 64-байтовые демки, которые далеко не всегда можно уложить в такой шейдерный код. Да и многие 256-батовые тоже. Тут всё зависит от сложности эффекта, пожалуй ;)
                    0
                    Помнится, Сысоев с товарищами тетрис в 4К уложил, может в загашниках у меня где и лежит ещё.

                +1
                Спасибо! Это моя любимая демка с детства. Тогда воспринималась как нечто совершенно магическое. Вдохновившись, даже пытался что-то столь же миниатюрное на асме сделать, но у меня даже банальный летающий по экрану кирпич не получилось в 256 байтов вместить )
                  0
                  В свое время я начал изучать ассемблер, будучи впечатлен именно такими демками.

                  Ах какой сложной задачей казалось нарисовать окружность! Нет, через тригонометрию — элементарно, но нет же тригонометрии у ассемблера! Впрочем, черт с ней с окружностью, точку, точку как нарисовать на экране.

                  Волшебный режим 10h…

                  P.S. Вдохновила меня, на самом деле, демка, вращавшая трехмерные фигуры из точек, морфившиеся в процессе. Занимала она порядка 4 килобайт и был это экзешник.
                    0
                    P.S. Вдохновила меня, на самом деле, демка, вращавшая трехмерные фигуры из точек, морфившиеся в процессе. Занимала она порядка 4 килобайт и был это экзешник.

                    Я тоже помню такую. Интересно, кто нибудь знает где её сейчас найти?

                      0
                      тут: morph3d.
                        0

                        Именно оно, спасибо! И забыл же про неё совсем, а такие первые впечатления были… Пошёл впадать в детство.

                        0
                        А вот и её исходный код Morph3d на ГитХабе. Плюсом COM & EXE
                          0
                          Like! Спасибо.
                            0

                            Про открытый код не знал, спасибо. Ассемблер там разобрать будет нелегко.

                              0
                              Ну ассемблер там не такой ужасный, код даже вполне прокомментированный. Можно даже статью написать, с его подробным разбором.
                          0
                          если мне не изменяет память, то я гдето видел её исходники на ассемблере.
                          0
                          А помимо цели минимизации размера, ещё есть и другая цель, например, когда нужно реализовать сложный алгоритм одной функцией без условных переходов, без тернарный оператора и тд.
                          Это может пригодится например для FPGA во время оптимизации алгоритмов на HDL.

                          Вот для примера алгоритм на Си летающего по диагоналям кирпича и отскакивающего от стенок. Он в виде всего одной формулы (см строки 16 и 17). Очень не оптимальный т.к. например, в Си нет возможности взять и раскопировать бит влево или в право и поэтому использую вычитание из нуля. На верилоге это выглядело бы в разы проще и понятнее.
                          Этот пример иллюстрирует так работали игровые приставки первого и второго поколения типа понга (которые без проца: транзисторы и/или рассыпная логика).

                          gist.github.com/Mirn/26174833464eb5403d2cd4c7bf26ccf5#file-opencv_demo3-cpp-L16
                            0
                            Этот пример иллюстрирует так работали игровые приставки первого и второго поколения типа понга (которые без проца: транзисторы и/или рассыпная логика).

                            Приставки на транзисторах работали иначе. Они вообще, по сути, аналоговыми были, там размеры и положение элементов определялись генераторами с регулируемой частотой, а меняя частоту генераторов, производилось движение. Потенциометры, подключенные к генераторам ракеток, были в качестве джойстиков, разряжающиеся/заряжающиеся конденсаторы двигали мяч в одну-другую сторону.
                          +2
                          Моя любимая демка называется mars
                          Тоже сделана довольно интересно, хотя и размер намного больше.
                            +1
                            Да, это было круто.
                            Mars на ютубе

                            Но 5 килобайт — довольно много.
                              0
                              2.7 если ужать exepack
                                –1
                                Это уже читерство :)
                                Хотя… 4k обычно Crinkler'ом сжимают вообще и ничего :)
                              0
                              Да, mars тоже крышу сносил. В моём личном рейтинге вторым номером идёт. Но там примерно понятно, как сделано, и размер бинарника всё-же не экстремально маленький.
                              +1
                              У неё две версии было — первая летала сама, во второй можно было рулить.
                              +1
                              Сейчас бы под такую задачу взяли бы несколько фреймворков, nodejs, пакеты,…
                                +7
                                Вы не поверите, но и ТОГДА в продакшене задачи решались с помощью готовых фреймворков.
                                Это — спортивное программирование, оно перпендикулярно промышленным задачам.
                                  0
                                  Да, демосцена – это просто энтузиазм, творчество, хобби.
                                  Некоторые (а может и многие) прогают графику чисто для души, на работе же занимаются совершенно другими задачами.

                                  Вообще, конечно, сцена под DOS и под Windows очень сильно отличается как минимум наличием функций и библиотек, которые доступны на уровне ОС.
                                    0
                                    но и ТОГДА в продакшене задачи решались с помощью готовых фреймворков

                                    Например каких?
                                      +2

                                      Clipper, FoxPro для БД, Turbo Vision для интерфейса и BGI для графики?
                                      Никто не мешал написать свою графику и почти гарантированно быстрее BGI, но так ли оно надо, если нужно просто пайчарт вывести? :-)

                                        +1
                                        Из этого списка только Turbo Vision можно назвать фреймворком и то с оговорками. Clipper — компилятор, далее язык, FoxPro — язык + среда. BGI библиотека.
                                        Меня интересовало, если уж речь шла за графику, было ли что-то высокоуровневое для создание игр тогда.
                                          0
                                          Девкиты для игровых консолей разве что шли со всеми готовыми библиотеками.
                                          На PC под DOS все старались написать свой крутой движок. Иногда потом лицензировали его другим.
                                          Кстати, на PC-демосцене в 90-ые некоторые использовали свои самописные движки и фреймворки. Например, у группы Doomsday была своя система со скриптовым языком.
                                          Это если мы говорим про графику. Более распространены были библиотеки для звука — тот же Midas, Cubic, Fmod.
                                      0

                                      Вот лишь бы что ответить. Приведите пример. Я писал под турбопаскаль 5.5, clipper, foxpro2 — все только руками на языке. Не было ничего.

                                        0
                                        Ну демка-то от 1996 года. Тогда уже вовсю фреймворки юзались. В том же турбопаскале уже в версии 6.0 появился и быстро стал мегапопулярным фреймворк TurboVision, какой это год, 1990-й? А в 1996 мы уже писали на Delphi под VCL, на сях под MFC и OWL и так далее. Да и Clipper/FoxPro, учитывая, что там прям в языке можно манипулировать базой данных, сами вполне себе фреймворки.
                                      0
                                      Ага… С гигабайтами разных библиотек и IDE. Блин, у меня на ЕС-1840 был винт 20 мегабайт и это было круто! На Spectrum какое-то время вообще дисковода не было, только магнитофон. И я с помощью этого зарабатывал деньги.
                                        0
                                        логично предположить сколько сейчас зарабатываешь если объёмы увеличились многократно
                                        здравствуй билли
                                      +2
                                      Что-то подобное (код демки) было напечатано на футболках Enlight'96 на спине. Но по моему, там был перевернутый крест, а не пентаграмма. Горящий. Свою футболку давно сносил, так что не найду. А на фото в отчетах у cr качество фиговое. :(

                                      Вот тут можно посмотреть, если кому интересно. :)
                                      www.enlight.ru/enlight/96/report2.html

                                      Эх, молодость, молодость…
                                      +1
                                      Эх блин, ностальгия! Тоже в своё время этим увлекался… Первый на что-то годный комп у меня появился в 98-м (как раз за месяц до дефолта). Год на него копил с зарплаты 200 долларов. До того был Spectrum и ЕС-1840. Комп продавался с предустановленной DOS и набором демок. Меня они буквально поразили. Начал в них копаться, изучать алгоритмы, через месяц сам уже начал что-то подобное писать… Нда… Комп по тем временам считался если не немерянно, то во всяком случае достаточно крутым. Пентиум 120 мегагерц, и 32 мегабайта памяти. А сейчас те же 32 мегабайта на моей отладочной платке с FPGA начального уровня — de0-nano. А на телефоне гигабайты… А всего-то 20 лет прошло… Вот так…
                                        +1

                                        Немного поностальгирую.


                                        mov al, 13h ;b0 13
                                        int 10h     ;cd 10```

                                        Ах, эти b0 13 cd 10…
                                        Помню, как по рукам ходило тайное знание, как вывести из строя windows XP в 7 байт.
                                        Вот все содержимое .com файла, которое намертво и надолго делает компьютер под управлением windows xp неработоспособным до перезагрузки:
                                        b0 13 cd 10 c4 c4 00


                                        Эксплуатирует костыль в виндовом эмуляторе ДОСа. Первые 4 байта переводят программу в графический режим, 2 следующих эквивалентны невозможной инструкции, которая используется для передачи исключений в этом эмуляторе, или чего-то вроде того. Последний 0 — код ошибки, который не обрабатывается в эмуляторе. В результате эмулятор (а он в полноэкранном режиме уже) виснет. При этом все программы и сама винда в фоне отлично работают, но клавиатура и мышь до нее не доходят. Экран намертво черный.


                                        Вот и получается: "а я могу повесить ваш компьютер с 7-ми байт".

                                          0
                                          а я могу повесить ваш компьютер с 7-ми байт
                                          А я могу повесить ваш компьютер с 4 байт )))
                                          ru.wikipedia.org/wiki/F0_0F_C7_C8
                                            0

                                            Ну, вы только пентиум повесите, а я любой компьютер, под управлением windows XP, возможно еще и 2000.

                                            +1
                                            9х в 3 байта вешали. «ълю» писалось в блокноте и сохранялось как .exe.
                                              0
                                              В 9X намертво повесит систему:
                                              cli
                                              jmp short $ ; hlt (в Win98, по крайней мере) не сработает


                                              А перезагрузит:
                                              mov al,1
                                              out 92h,al
                                            +1
                                            Спасибо за статью! Для меня все что связано с демосценой — это как еще раз вернуться в студенчество… Как там говорят «если пришел к ассемблеру через демосцену — ставь лайк» :))
                                            p.s. в русской википедии в ссылках при статье «Демосцена» — одна из моих первых проб пера, и именно про демосцену… И что прикольно, рядом статья Олега Чебенеева, он же mindw0rk, рассказы которого я взахлеб почитывал в то время…
                                              +3

                                              Кстати, чтобы использовать FASM (под Линукс например), нужно просто удалить директивы в начале и в конце и добавить org 100h:


                                              -.186
                                              -.model tiny
                                              -.code
                                              -.startup
                                              +        org  100h
                                               ; ..... инструкции здесь
                                              -end

                                              Потом:


                                              fasm demo.asm
                                              dosbox demo.com
                                                +1
                                                Классная статья и реверс с разбором «как это работает» доходчиво и понятно. Читается на одном дыхании. Надеюсь увидеть еще такие же разборы демок. Пишите!
                                                  0
                                                  Когда декомпильнули fire.com с его «шейдерами», а точнее, фильтрами, тогда и понеслись подобного рода демки на всех платформах.

                                                    –3
                                                    128 байт говорите? А сколько теперь один лишь калькулятор в Windows вести?
                                                      0
                                                      Отличная статья. Рекомендую тем, кто интересуется демосценой посмотреть ранние демки farbrausch… в те годы для меня это была полная магия запихать в 64к столько всего…
                                                        0

                                                        Где0то в тех же годах видел демку cross.com, рисовавшую такой же эффект, только вместо пентаграммы был крест. Видать, авторы этой демки взяли ту, переделали блок линий (он тут малех посложнее, ещё может код эффекта ужать смогли) и за своё выдали. Или наоборот, хм. Все равно всем респект.

                                                          0
                                                          В той деме был другой алгоритм пламени, и он был намного красивее.
                                                          0
                                                          Прекрасная статья! Демки — это отдельный вид искусства: компактность кода, зрелищность и производительность. Причем не понятно порой, что в этом списке первостепенно. Мне как программисту — компактность кода, разумеется. Когда впервые их увидел в 1998 году — был просто поражен.
                                                            +1
                                                            .kkrieger ru.wikipedia.org/wiki/.kkrieger



                                                            За из же авторством невероятные демки www.theproduct.de
                                                              0
                                                              Запустил под виртуалкой, работает…
                                                              Правда, пару байтов пришлось добавить — два раза команду hlt.
                                                              Ведь все равно в конце там есть несколько байтов незаполненных, если я правильно понял, так что в 128 все равно можно остаться.
                                                                0
                                                                Я в студенчестве экспериментально выяснил, что минимальня программа которая запускается, состоит из одного байта. Это символ 'b' в ASCII. Т.е берём блокнот, пишем в нём одну букву b, и сохраняем как b.exe. Сейчас в Windows 10 не работает, но в XP ещё работало. Запускалась консоль и курсор хаотически помаргивал в начале экрана. Может кто объяснить, за счёт чего эт работало?
                                                                  0
                                                                  минимальная прога, которая запускается и корректно работает — однобайтовая команда retn, и это .com файл, а никак не .exe
                                                                  +6
                                                                  Охтыж. Прям ностальгия. Помню эту демку. Я в те времена как-раз 128-байтниками развлекался. Она меня вдохновила на повторить.

                                                                  Полез в архивы. Нашёл ту самую дискетку! :-)
                                                                  Вот моя версия, анархия:
                                                                  0000000000: B0 13 CD 10 B7 A0 53 53 │ 1F 07 BD 40 01 33 F6 B1
                                                                  0000000010: C8 51 8B F9 8B CD 8B C1 │ 2D A0 00 F7 E0 50 8B C7
                                                                  0000000020: 2D 64 00 F7 E0 5B 03 C3 │ B0 0C 80 FC 20 75 02 88
                                                                  0000000030: 04 46 E2 E2 59 E2 DA BF │ 9E 7A FE C9 F3 AA BF C6
                                                                  0000000040: ED B1 5F 51 AA 2B FD AA │ 2B FD 4F E2 F7 59 83 EF
                                                                  0000000050: 0F AA 03 FD AA 03 FD 4F │ E2 F7 33 C0 49 03 D9 81
                                                                  0000000060: FB 7A F8 77 0D 38 07 74 │ 09 C6 87 40 01 04 02 D9
                                                                  0000000070: 89 07 E2 E9 B4 01 CD 16 │ 74 93 B8 03 00 CD 10 C3

                                                                  gif-ка эмулятора
                                                                  image


                                                                  Помню тогда собирался послать пачку работ на демо-пати, но то-ли опоздал, то-ли сессия, то-ли что-то пошло не так.

                                                                  Вот еще пару работ которыми тогда гордился:
                                                                  множество Мондельброта (ох, как я тогда радовался когда вместо 1 лишнего байта, после двух дней мучений придумал как сэкономить целых три байта, даже подпись влезла в конце файла):
                                                                  0000000000: B0 13 CD 10 68 00 A0 1F │ BA C8 03 EE 42 32 C9 8A
                                                                  0000000010: C1 EE 6E 6E E2 F9 BD 9C │ FF B9 C8 00 51 BA 50 FF
                                                                  0000000020: B9 40 01 51 57 33 DB 33 │ FF 33 C9 52 8B C7 F7 E8
                                                                  0000000030: 8B F0 8B C3 F7 E8 2B F0 │ C1 FE 06 5A 03 F2 52 8B
                                                                  0000000040: C6 F7 E8 C1 F8 06 50 8B │ C7 F7 EB C1 F8 05 03 C5
                                                                  0000000050: 8B D8 8B FE F7 E8 C1 F8 │ 06 5A 03 C2 5A 0A E4 75
                                                                  0000000060: 06 49 80 F9 E0 75 C4 5F │ 88 0D 47 42 59 E2 B4 45
                                                                  0000000070: 59 E2 A9 FE CC CD 16 B8 │ 03 00 CD 10 C3 00 41 73

                                                                  image

                                                                  кольца ньютона:
                                                                  0000000000: B0 13 CD 10 68 00 A0 07 │ B1 C8 BF 80 01 51 83 E9
                                                                  0000000010: 64 8B E9 B9 40 01 51 81 │ E9 A0 00 BE 70 01 51 E8
                                                                  0000000020: 3A 00 86 D8 59 E8 34 00 │ 02 C3 24 03 8A D8 C0 E0
                                                                  0000000030: 02 0A C3 0C 10 88 05 47 │ 59 E2 DB 59 E2 CF B1 04
                                                                  0000000040: AD 01 44 F6 7D 05 FF 44 │ FE EB 03 FF 4C FE E2 F0
                                                                  0000000050: 33 FF B5 7D F3 A5 E4 60 │ 48 75 AD C3 AD 2B C8 AD
                                                                  0000000060: 03 C5 F7 E0 50 8B C1 F7 │ E1 5A 03 C2 C1 E8 0B C3
                                                                  0000000070: 00 00 32 00 32 00 00 00 │ 09 00 00 00 00 00 F5 FF

                                                                  gif
                                                                  image


                                                                  3d фонтан:
                                                                  0000000000: B0 13 CD 10 B7 A0 53 07 │ BE 7E 01 B9 8C 00 C7 04
                                                                  0000000010: 00 00 AD 89 3C AD AB AB │ 89 0C AD C7 04 60 04 AD
                                                                  0000000020: E2 EC BE 80 01 B9 8C 00 │ 51 AD C1 F8 03 8B F8 AD
                                                                  0000000030: AD C1 F8 05 05 64 00 81 │ C7 80 06 B9 40 01 F7 E1
                                                                  0000000040: 03 F8 AD C0 E8 04 0C 01 │ 24 09 88 05 59 E2 D9 33
                                                                  0000000050: FF B5 7D A5 89 54 FE E2 │ FA BE 7E 01 B9 18 01 AD
                                                                  0000000060: C1 F8 04 01 04 7D 05 FF │ 44 FE EB 03 FF 4C FE AD
                                                                  0000000070: E2 ED B4 01 CD 16 74 AA │ B8 03 00 CD 10 C3 41 73

                                                                  gif
                                                                  image
                                                                    0
                                                                    Вам неплохо было бы написать статью про свои интры.
                                                                    Довольно годные :)
                                                                      0

                                                                      Поддерживаю просьбу сделать разбор. И ps: давненько не сохранял программы на диск из текста в HEXe, вспрмнил про UUE.

                                                                        +5
                                                                        jin_x Cenzo
                                                                        Увы, но статью не смогу. Сейчас нашел исходники — тихий ужас. Я был молод и горяч. Исходники такие же. :-) Проще реверсинжинерить как это делал Loiqig :-) А учитывая что прошло без малого 22 года (исходники середины 97-го), и отсутствие опыта ассемблера приблизительно с тех же времен — у меня уйдет не меньше суток-двух на вспомнить, и еще сутки на статью. Я просто не готов к такому подвигу (слишком много детей/домашних дел для этого).

                                                                        Вот пример исходника колец ньютона
                                                                        .286
                                                                        .MODEL TINY
                                                                        .CODE

                                                                        org 100h

                                                                        Start:
                                                                        ; MOV DX,3C8H
                                                                        ; OUT DX,AL
                                                                        ; INC DX

                                                                        MOV AL,13h
                                                                        INT 10h

                                                                        ; XOR CL,CL
                                                                        ;PAL:
                                                                        ; MOV AL,CL
                                                                        ;; AND AL,0111b
                                                                        ; SHL AL,4
                                                                        ; OUT DX,AL
                                                                        ; OUT DX,AL
                                                                        ; OUT DX,AL
                                                                        ; LOOP PAL

                                                                        PUSH 0A000h
                                                                        POP ES

                                                                        REPEAT:
                                                                        ; MOV CX,200
                                                                        MOV CL,0C8h
                                                                        ; XOR SI,SI
                                                                        MOV dI,OFFSET ACTIVE
                                                                        LOOP_Y:
                                                                        PUSH CX
                                                                        SUB CX,100
                                                                        ; MOV Y,CX
                                                                        MOV bp,CX
                                                                        MOV CX,320
                                                                        LOOP_X:
                                                                        PUSH CX
                                                                        SUB CX,160
                                                                        mov si,offset x_x1
                                                                        ; MOV X,CX
                                                                        PUSH CX
                                                                        CALL CIRCLE
                                                                        ; MOV DS:[dI],AL
                                                                        xchg bl,al

                                                                        ; MOV CX,X
                                                                        POP CX
                                                                        CALL CIRCLE
                                                                        ; and al,00001111b
                                                                        ; or al,00000111b
                                                                        ; add al,15
                                                                        ; add DS:[dI],AL
                                                                        ; add al,DS:[dI]
                                                                        add al,bl
                                                                        and al,00000011b
                                                                        mov bl,al
                                                                        shl al,2
                                                                        or al,bl
                                                                        or al,00010000b
                                                                        MOV DS:[dI],AL

                                                                        INC dI

                                                                        POP CX
                                                                        LOOP LOOP_X
                                                                        POP CX
                                                                        LOOP LOOP_Y

                                                                        ; MOV SI,OFFSET D_X1
                                                                        MOV Cl,4
                                                                        MOVE:
                                                                        ; MOV AX,[SI] ;D_X1
                                                                        lodsw
                                                                        ADD [SI]-10,AX
                                                                        ; CMP WORD PTR [SI]-4,0
                                                                        JGE DEC_X1
                                                                        INC WORD PTR [SI]-2 ;D_X1
                                                                        JMP SHORT END_D_X1
                                                                        DEC_X1:
                                                                        DEC WORD PTR [SI]-2 ;D_X1
                                                                        END_D_X1:
                                                                        ; ADD SI,4
                                                                        LOOP MOVE

                                                                        ; MOV SI,OFFSET ACTIVE
                                                                        XOR DI,DI
                                                                        ; MOV CX,32000
                                                                        MOV CH,7Dh
                                                                        REP MOVSW

                                                                        ; MOV AH,01H
                                                                        ; INT 16H
                                                                        ; JZ REPEAT ;UNTIL KEYPRESSED
                                                                        in al,60h
                                                                        dec ax
                                                                        jnz repeat

                                                                        ; MOV AX,03h
                                                                        ; INT 10h
                                                                        RET

                                                                        CIRCLE PROC NEAR
                                                                        ; SUB CX,X_X1
                                                                        ; MOV BX,Y_Y1
                                                                        lodsw
                                                                        sub cx,ax
                                                                        lodsw
                                                                        ; mov bx,ax

                                                                        add AX,bp ;Y
                                                                        MUL AX
                                                                        push ax
                                                                        MOV AX,CX
                                                                        MUL CX
                                                                        POP DX
                                                                        ADD AX,DX
                                                                        SHR AX,11
                                                                        RET
                                                                        CIRCLE ENDP

                                                                        X_X1 DW (0)
                                                                        Y_Y1 DW (50)
                                                                        X_X2 DW (50)
                                                                        Y_Y2 DW (?)
                                                                        D_X1 DW (09)
                                                                        D_Y1 DW (0)
                                                                        D_X2 DW (0)
                                                                        D_Y2 DW (-11)
                                                                        ;X DW (?)
                                                                        ;Y DW (?)
                                                                        ACTIVE DB 64000 DUP (?)
                                                                        END Start



                                                                        Ежели кто захочет покопаться, прикладываю архивчик работ:


                                                                        Вот некоторые превьюшки из архива:
                                                                        Огоньки. По-моему только ленивый не делал их. К сожалению на современном железе даже в эмуляторе все работает слишком уж быстро. Пришлось дополнительно замедлять внутри virtual box'а. Но даже это не помогает — смотрится хуже чем было на старых ламповых компах. Плюс рандом уже не тот (в качестве рандома используется мусор из памяти — теперь нет того мусора. Возможно то был мусор от qemm386, или еще какой).
                                                                        fire2.com


                                                                        fire3.com


                                                                        fire4.com



                                                                        Жизнь Конвея, куда ж без неё? :-)
                                                                        life.com



                                                                        Просто залипательный геометрический мусор.
                                                                        shiza.com



                                                                        Братья-близнецы с ротацией палитры.
                                                                        hyper.com


                                                                        water.com



                                                                        Дыра — пытался повторить эффект из какой-то 4k-intro.
                                                                        hole.com



                                                                          +1

                                                                          fire4 красиво пылает.

                                                                        +1
                                                                        Ностальжи! Я участвовал в 128 байтниках всего раз, вот результаты (мое — 6-е место):
                                                                        demozoo.org/parties/804
                                                                          0
                                                                          Забавно. Я пробовал протащить 3д-графику в движок для текстовых игр. который умеет в пиксели. Получилось примерно так:
                                                                          image
                                                                          instead-games.ru/game.php?ID=305

                                                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                          Самое читаемое