ЭЛЕМЕНТЫ ГРАФИКИ В ПЛАНЕ МИНИМИЗАЦИИ ПРОГРАММ

	1. Минимизация программ - ключ к алгоритмам
	2. Рисование отрезка
	3. Рисование окружностиБЫСТРОЕ РИСОВАНИЕ ОКРУЖНОСТИ
	4. Рисование круга
	5. Фрактал Мандельбротта - минимальная программа
	6. Разные картинки

	     1. Минимизация программ - ключ к алгоритмам
		      и одна из основ надежности

	Данное сочинение - не руководство по применению шикарных
пакетов. В нем - речь о самых элементарных геометрических объектах,
и то не составляющих сколько-нибудь полного набора. Материал
адресован главным образом системным программистам, всем желающим
покопаться в машинных кодах и желающим посоревноваться в написании
кратчайших программ. Некоторые примеры уже были предметом соревнований
(в том числе общероссийских) и поэтому вряд ли могут быть улучшены.
Хотя кто знает ?..
	Казалось бы, зачем возвращаться к хорошо изученным основам,
к рисованию точек, линий, окружности, когда центр тяжести исследований
давно переместился на гораздо более высокие уровни? Конечно, сокращение
нескольких байтов в мегабайтных программах не может иметь эпохального
значения.
	Но как показывает опыт, начав с микроскопического эффекта от
подчистки элементарных вещей, можно схожими методами нередко добиться 
удивительного эффекта на средних программах. Здесь приведены примеры
программ на 1-2 порядка меньше аналогов. При десятках тысяч операторов
различие доходит до 1000-кратного. Конечно, знания особенностей ЭВМ
для этого недостаточно. Нужны мощные математические алгоритмы. Но
минимизация машинного кода - это вполне реальный путь, который ведет
усовершенствованию известных алгоритмов и к открытию новых.
	На первый взгляд кажется, что программа - лишь неуклюжая
интерпретация лаконичных математических формул, и все находки
программиста легко нашел бы и математик лишь с листом бумаги и
ручкой, если бы счел это нужным. На самом деле это далеко не так.
Понятия программирования предоставляют нам уровень и элементы -
кирпичики этого уровня, совершенно чуждые чистой математике и даже
таким ее прикладным областям как методы приближенных вычислений. 
С этого, более глубокого уровня, в ином свете предстают даже самые
элементарные математические объекты, обнаруживаются неожиданные связи,
из ничего, казалось бы, появляются самые настоящие математические
формулы. Возможно, когда-то они озарили бы и математика-мыслителя.
Однако есть и другой, не быстрый, но широкий и надежный путь к новым
математическим знаниям.
	Когда мощности ЭВМ растут не по дням, а по часам, тогда борьба
за экономию памяти, места на диске и т.д. представляется мелочной, а
то и совершенно излишней. Но представьте, что все файлы на вашей ЭВМ
удалось сжать в 1000 раз, и соответственно во столько же раз быстрее
с ними оперировать, во столько же раз больше вы теперь можете разместить
информации. Представьте, что вы поставляете заказчику огромную систему
в виде одного COM-файла вместо прежних 1000 файлов с неистребимым
стремлением к исчезанию и перепутыванию между собой.
	Сокращение загрузочного модуля даже в два раза - это не
просто соответствующее сокращение занятого места на диске и ускорение
запуска. Это значит, что на любом этапе (запуск, выполнение, хранение,
копирование, архивирование) модуль гораздо менее подвержен всяким
напастям, будь то атаки вирусов, падение напряжения, несанкционированный
доступ, ошибки операционной системы, некорректность других программ и
прочие сбои, причины которых остаются неизвестны. В результате
надежность программы повышается чуть ли не на три порядка.
	Конечно, если ваша программа и так относительно невелика и
дает, скажем, 1 сбой на миллион запусков, и вы запускаете ее не более
10 раз в сутки, то вы никогда не заметите даже 100-кратного отличия в
надежности, так как оно смажется гораздо более частыми неприятностями,
обычно преследующими вычислительную технику.
	Но в промышленной эксплуатации надежность непременно дает
о себе знать. Кроме того, есть немало случаев, когда сбои недопустимы,
поскольку ведут к катастрофическим последствиям.
	Особые требования, по мнению автора, должны предъявляться
также к продуктам, рассчитанным на мировое распространение. Одно дело,
когда ваша личная домашняя программа мучает вашу персоналку, которая и
так большую часть суток стоит без дела. И совсем другое дело, когда
даже один лишний оператор, стоящий, например, в какой-нибудь развесистой
операционке у миллионов пользователей, крадет их время и пускает на
ветер энергетические и прочие ресурсы. Избыточность системы в 10, 100,
1000 раз - это уже откровенное надувательство покупателя, граничащее с
преступлением.

			2. Рисование отрезка

	Серьезных математиков вряд ли может заинтересовать рисование
отрезка. Однако задача эта очень нетривиальна. Ведь пространство на
экране дискретное, и совсем не прост выбор точек для закрашивания,
чтобы они максимально создавали иллюзию непрерывной линии. Задача
осложняется тем, что точки экрана не имеют удобной адресации. Если
математик ограничится выдачей координат точек, то для программиста
координаты - это только начало работы.
	В следующем алгоритме вы, конечно, найдете знакомые
классические мотивы. Уж слишком прост отрезок, чтобы открывать
совершенно новые способы его рисования. Тем не менее, классика не
выведет вас на минимальный объем подпрограммы, а изложенное ниже
и начиненное массой нюансов - выведет.
	Исходными являются: целые числа x1, y1, x2, y2 - координаты
отрезка и целое число, задающее цвет отрезка.
	1. Если x1 > x2 , то начальная и конечная точка меняются
местами, и добиваемся, чтобы x1 не превосходил x2. С точки зрения
математики такая перестановка малозначительна, если вообще не
излишня. Но в задаче минимизации она прямо влияет на результат.
	2. Засылаем x2-x1 в x2. Таким образом, x2+1 - это количество
точек в проекции будущего отрезка на ось абсцисс. Формируется адрес
для помещения на экране первой точки отрезка (после этого координату
x1 уже знать не обязательно).
	3. Засылаем в y2 модуль разности y2-y1 и запоминаем знак этой
разности.
	4. Фиксируется что больше: x2 или y2. Т.е. от чего отрезок
больше отклоняется: от вертикали или от горизонтали. Если x2 > y2 ,
то эти значения переставляются.
	Если, например, отрезок располагается полого, то каждая абсцисса
рисуемых точек встретится по одному разу, и значит, в процессе рисования
достаточно последовательно увеличивать соответствующую переменную.
Ординаты в этом случае будут меняться рывками, моменты которых вычислить
непросто. Заметим, что в алгоритме не используются вещественные числа
хотя бы по причине слишком медленной их обработки. Но если вещественные
числа использовать, то вопросы наращивания и округления координат проще
не станут.
	5. Значение y2 засылается в счетчик и еще запасается в памяти.
Заметим, что y2+1 равно количеству точек, которые будут изображать из
себя отрезок. Поэтому не случайно начальное значение счетчика. Запасти
это значение нужно, поскольку оно далее понадобится, а y2 предполагается
менять.
	6. Значение y2 уменьшаем вдвое. Это нужно для более или менее
равномерного распределения неизбежных ступенек на том, что будет
считаться отрезком. Если не сделать указанной операции, то ступеньки
выпадают и на концы отрезка, что неприятно бросается в глаза.
	7. На экране рисуется точка в соответствии с
имеющимся ее адресом. В y2 засылается значение y2-x2.
	8. Если y2 < 0 , то переход на пункт 10.
	9. Если признак, вычисленный в п.4 говорит, что отрезок
составляет с вертикалью угол не более 45 градусов, то переход на п.12.
	Иначе делается следующее. Адрес на экране корректируется так,
чтобы ордината очередной точки изменилась, но не в ту сторону, куда
направляется отрезок, а в противоположную. И переход на п.11.
	10. К y2 добавляется запасенное в памяти число.
	11. Корректируется адрес на экране так, чтобы изменилась
абсцисса очередной точки.
	12. Корректируется адрес на экране с целью изменения
ординаты в нужную сторону.
	13. Значение счетчика уменьшается на 1. Если оно неотрицательно,
то переход на п.7.

	В следующей реализации для x1, y1, x2, y2 используются
регистры CX, BP, SI, DI соответственно, выбор которых в принципе
достаточно произволен. Цвет отрезка поступает в AH. Корректность
исходных данных не проверяется в подпрограмме, так как их ошибочность
не ведет к катастрофическим последствиям и легко обнаруживается
визуально по результату.
	Адрес для помещения на экране формируется в BX и AH. Для
знака разности y2-y1 используется регистр CX, причем запоминается
не +1 и не -1, а такое значение, которое позволяет короче
корректировать BX. Что больше: x2 или y2 - фиксируется в AL.
Счетчиком служит регистр BP.

	cmp cx,si	; Сравнение x1 и x2
	jle M1		; Если x1 левее, то перестановка не нужна
	xchg cx,si	; Перестановка x1 и x2
	xchg bp,di	; Перестановка y1 и y2
M1:	sub si,cx	; x2-x1
	db 214		; setalc, al=0
	mov dx,974	; Номер порта
	out dx,ax	; Установка цвета
	mov ah,160	; Адрес видеобуфера 160*256*16
	mov gs,ax	; Установка gs на видеобуфер
	mov ax,32776	; al=8 ah=128
	ror ah,cl	; Формирование адреса в буфере
	shr cx,3	; cx/8
	imul bx,bp,80	; Формирование начального адреса bx=bp*80
	add bx,cx	; Доформирование адреса согласно x1
	mov cl,80	; Формирование сдвига адреса
	sub di,bp	; y2-y1
	jge M2		; Если вторая ордината больше, то остается cl=80
	neg cx		; Теперь cl=-80
	neg di		; Смена знака y2
M2:	cmp di,si	; Определение угла наклона отрезка
	jge M3		; Если > 45 град., то остается al=0
	add al,128	; Если y2 < y1 , то al=128
	xchg di,si	; Перестановка y1 и y2
M3:	mov bp,di	; Количество точек - 1 , изображающих отрезок
	mov [254],di	; Его же запасаем в памяти
	sar di,1	; y2 / 2 , чтобы не было лишних ступенек
CCC:	out dx,ax	; Установка маски
	or gs:[bx],al	; Вывод очередной точки
	sub di,si	; Вычитаем длину меньшей проекции
	jl M5		; Если < , то надо менять и x, и y
	cmp al,0	; Вспоминаем наклон отрезка
	jge M7		; Если > 45 град., то меняем только ординату
	sub bx,cx	; Корректировка адреса в обратную сторону
	jmp M6
M5:	add di,[254]	; y2 + запасенное число
M6:	ror ah,1	; Корректировка разряда, т.е. абсциссы
M7:	adc bx,cx	; Корректировка адреса с изменением ординаты
	dec bp		; Уменьшение счетчика
	jge CCC		; Возврат на начало цикла
	ret

	Для контроля то же самое в кодах (всего 86 байтов):
59,206, 126,4, 135,206, 135,253, 43,241, 214, 186,206,3,
239, 180,160, 142,232, 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,254,0,
209,255, 239, 101,8,7, 43,254, 124,8, 60,0, 125,10,
43,217, 235,4, 3,62,254,0, 208,204, 19,217, 77, 125,229, 195.
	Можете посмотреть коды в шестнадцатиричной системе.

	Чтобы запустить эту подпрограмму, нужна управляющая часть, где
устанавливается графический режим и задаются координаты отрезка. Поэтому
сначала надо набить, например,
	mov al,18	; Номер графического режима 18
	int 16		; Прерывание 16 с функцией 0: установка режима
	mov dx,974	; Номер порта
	mov ax,3841	; al=1 ah=15
	out dx,ax	; Разрешение доступа ко всем 4 плоскостям
	xor cx,cx	; cx=0 = x1
	xor bp,bp	; bp=0 = y1
	mov si,639	; si = x2
	mov di,479	; di = y2
	mov ah,14	; Цвет желтый
	call LINE	; Вызов п/п рисования отрезка
	mov ah,0
	int 22		; Ожидание нажатия клавиши
	mov ax,3
	int 16		; Возврат в текстовый режим
	ret
LINE:

и сразу непосредственно указанные выше 86 байтов. Все вместе это
находится в прилагаемой программе LIN.COM. Понятно, что саму LIN.COM
минимизировать не надо, один отрезок всегда можно нарисовать более
простыми средствами, во всяком случае без создания подпрограммы.
Суть - в 86 байтах подпрограммы. Свой вклад в достижение этого
количества внес также В.В.Уленгов из Хабаровска.
	В принципе подпрограмма рисования отрезка может обойтись
только регистрами, но тогда она не будет кратчайшей (всего лишь на
1 байт больше).
	Здесь используются два байта оперативной памяти по адресу
254 (относительно DS=CS), выбор которых достаточно произволен.
	Пример приведен в графическом режиме 18 (640*480). Для
большего количества точек по ширине, скажем, для 800, нетрудно
догадаться, что число 80, дважды фигурирующее в листинге, надо
заменить на 100. В 19-м режиме (320*200) подпрограмма рисования
отрезка будет, конечно, короче из-за более простой адресации и
ненужности ряда установок, но вряд ли она представляет большой
интерес ввиду слишком крупных "точек" в этом режиме.
	К подпрограмме можно сделать добавку, которая (с помощью
прерывания 16, функция 15) выясняет текущий видеорежим и соответственно
далее организует свою работу. Можно подавать параметры на вход, а
выяснять их ранее, чтобы не повторять при выполнении операции,
которые можно сделать один раз до вывода (возможно, большого)
рисунка. В приведенном варианте установку регистра gs на видеобуфер
тоже можно было бы вынести за рамки подпрограммы (а может быть, и
установку цвета тоже). Вариации и надстройки - по вкусу и по
ситуации. Здесь же задача - выявить основное, непреходящее.
	В подпрограмме не используется сопроцессор по двум причинам:
во-первых рисование должно быть по возможности быстрым, во-вторых,
более короткого кода просто не получается.
	Сократить подпрограмму можно использованием прерывания,
выводящего точку в графике. Приведем для сведения только коды (69 байтов),
так как значительная часть их повторяет уже описанный вариант:
57,241, 126,4, 135,206, 135,253, 41,206, 214, 139,213, 41,239,
125,3, 64, 247,223, 57,247, 125,4, 4,128, 135,254, 139,223,
139,239, 209,255, 96, 183,0, 138,196, 180,12, 205,16, 97, 41,247,
124,7, 60,0, 125,6, 65, 235,10, 1,239, 65, 168,1, 116,2, 74,74,
66,75, 125,222, 195.
	Можете посмотреть коды в шестнадцатиричной системе.
Упростится и управляющая часть: после установки графического
режима можно сразу переходить к координатам. Однако это решение
- из другой весовой категории. Оно слишком медленное, и для
практики может сгодиться в весьма редких случаях. Так прерывание,
рассчитанное на все случаи жизни, оказалось не пригодно практически
ни к одному из них.
	Поэтому в качестве конкурирующих принимаются быстрые
решения. Если вы будете злоупотреблять делением, умножением или
сопроцессором, то полезного варианта наверняка не получится.

		      3. Рисование окружности

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

	Как правило, где есть окружность, там нужен и отрезок.
Поэтому раз подпрограмма рисования отрезка все равно занимает
место, то грех ею не воспользоваться. Ведь окружность большей
частью требует тех же операций. Окружность можно заменить цепочкой,
например, из 60 отрезков. Для подсчета координат узлов не обязательно
отрекаться от сопроцессора, потому что рисование каждого отрезка
уже потребует немало операций. При этом получается подпрограмма
примерно в 70 байтов, которая не отличается ни скоростью, ни
компактностью.
	Для сведения приводим еще более медленную, но и более короткую
подпрограмму, которая обходится без отрезков, а просто выводит через
прерывание точки, меняя угол с достаточно мелким шагом. 
Исходные данные: al=цвет, cx=x, dx=y, bx=радиус.
189,0,6, 96, 104,0,3, 217,235, 139,220, 222,79,6, 222,55,
217,251, 222,79,10, 223,31, 3,15, 222,79,10, 223,31, 3,23,
183,0, 180,12, 205,16, 95,97, 77, 117,216, 195 (всего 44 байта).
Описания нет, так как п/п вряд ли представляет теоретический интерес.
	Но зато на ее основе сделана и прилагается ROUND.COM (объем
103 байта) - развлекательная безделушка для заполнения экрана не самым
тривиальным мусором.

	И все же решения задачи рисования окружности не может быть без
быстрого варианта. Если внешняя программа выполняет много операций в
графике, то становится нецелесообразно присоединять к ней одновременно
и быстрый (некомпактный), и медленный (компактный) варианты рисования
окружности. Меньше программа в целом получится тогда, когда обходится
одним вариантом, наиболее универсальным. А таковым является именно
быстрая подпрограмма.
	В следующем варианте не используются сопроцессор и деление,
а в основном цикле нет и умножения. Во всей подпрограмме только один
раз попадается целочисленное умножение (при вычислении адреса в
видеобуфере для начальной точки), и только один раз это умножение
срабатывает, поэтому борьба с ним не актуальна даже при первоочередном
требовании к скорости.
	Для получения начальной точки к абсциссе центра добавляется
радиус. Для координат x,y текущей точки используются регистры si и
di, но хранятся не абсолютные координаты, а относительно центра.
По x,y подпрограмма строит ортогональный вектор (y,-x) и получает
два возможных направления (y,0) и (0,-x) для движения к следующей
точке на экране. Эти векторы нормализуются и получаются два кандидата
на очередную точку. Берется тот, который ближе к идеальной окружности.
Разумеется, при этом не вычисляются ни квадраты, ни квадратные корни.
Как меняется расстояние до окружности (для чего используется bp) -
легко определить по его предыдущему значению и по нормализованному
вектору смещения.
	Завершение процесса - по приходу в начальную точку.
	Подпрограмма не делает проверок на попадание рисуемых точек в
экран, поскольку промах не катастрофичен. Но такие проверки нетрудно
вставить перед очередным выводом в видеобуфер. При этом можно проверять
посчитанный адрес bx в видеобуфере (это менее строгая проверка) или
координаты очередной точки в si и di (это более строгая проверка).
	В случае нулевого радиуса выводится точка. При отрицательном
радиусе он берется по модулю.
	Объем подпрограммы: 137 байтов. Вместе с управляющей частью
она прилагается в модуле ROU1.COM.

	БЫСТРОЕ РИСОВАНИЕ ОКРУЖНОСТИ
	Цвет окружности поступает в подпрограмму ROU через регистр
ah, радиус в cx, координаты центра в si и di.

	mov al,18	; Режим 18
	int 16		; Установка графического режима
	mov ah,13	; Цвет
	mov cx,200	; Радиус
	mov si,300	; Абсцисса центра
	mov di,200	; Ордината центра
	call ROU
	mov ah,0
	int 22		; Ожидание нажатия клавиши
	mov ax,3
	int 16		; Возврат в текстовый режим
	ret
; Конец управляющей части

; Начало п/п рисования окружности
ROU:	mov dx,974	; Номер порта
	mov al,0
	out dx,ax	; Установка цвета
	mov ah,160
	mov gs,ax	; Установка gs на видеобуфер
	mov ax,3841	; al=1, ah=15
	out dx,ax	; Настройка графического режима
	push cx		; Сохранение радиуса
	add cx,si	; cx = абсцисса начальной точки
	mov ah,128	; Заполнение крайнего левого бита в ah
	ror ah,cl	; Сдвиг бита согласно абсциссе
	shr cx,3	; Абсцисса в байтах
	imul bx,di,80	; bx=di*80 адрес в видеобуфере согласно ординате
	add bx,cx	; Корректировка адреса согласно абсциссе
	pop cx		; Восстановление радиуса в cx
	xor bp,bp	; bp=0 = начальное знание показателя отклонений
	mov si,cx	; si=cx = начальное значение x
	xor di,di	; di=0 = начальное значение y

M1:	push cx		; Сохранение радиуса
	mov al,8
	out dx,ax	; Установка маски вывода согласно ah
	or gs:[bx],al	; Вывод в видеобуфер
	push ax		; Сохранение маски вывода
	mov ax,si	; x
	or di,di	; Направление по x ортогонального вектора
	call PP
	push cx		; Сохранение кандидата показателя отклонений
	push ax		; Сохранение модуля кандидата
	mov ax,si	; x
	neg ax		; Направление по y ортогонального вектора
	mov ax,di	; y
	call PP
	pop bp		; Восстановление модуля 1-го кандидата
	cmp ax,bp	; Сравнение модулей кандидатов
	pop bp		; Восстановление значения 1-го кандидата
	pop ax		; Восстановление маски вывода
	jb Y
	or di,di	; Проверка абсциссы ортогонального вектора
	jl M2
	inc si		; x+1
	ror ah,1	; Точка вправо
	adc bx,0	; Корректировка адреса в буфере
	jmp M6
M2:	dec si		; x-1
	rol ah,1	; Точка влево
	sbb bx,0	; Корректировка адреса в буфере
	jmp M6
Y:	mov bp,cx	; Показатель отклонений равен 2-му кандидату
	or si,si	; Направление смещения по y
	jg M5
	inc di		; y+1
	add bx,80	; На строку ниже на экране
	jmp M6
M5:	dec di		; y-1
	sub bx,80	; На строку выше на экране
M6:	pop cx		; Восстановление радиуса в cx
	jcxz M9		; Выход при нулевом радиусе
	cmp si,cx	; Сравнение x с абсциссой начальной точки
	jne M1
	or di,di	; Сравнение y с ординатой начальной точки
	jne M1

PP:	jg M8
	jl M7
	mov ah,-1	; Очень большое значение модуля отклонений
	ret
M7:	neg ax
M8:	sal ax,1	; Расчет значения кандидата
	inc ax
	add ax,bp
	mov cx,ax	; cx = копия значения кандидата
	jge M9
	neg ax		; ax = модуль значения кандидата
M9:	ret

	В отличие от рисования отрезка здесь больше установок
видеорежима перенесено в подпрограмму (и соответственно меньше
в управляющей части). Как отмечалось, выбор зависит от ситуации и
от вкуса. На первый взгляд предпочтительней подпрограмма, которая
не требует для себя большой подготовительной работы, а все, что
возможно, делает сама. (Однако при ряде последовательных вызовов
она делает явно лишние операции.) Поскольку для окружности объем
и так оказался не мал, то нет смысла слишком "чистить" подпрограмму.
Для отрезка в погоне за показателями мы поступили иначе.

			 4. Рисование круга

	Здесь ситуация еще более расплывчатая, чем для окружности.
Более или менее однозначным представляется только то, что круг
надо заполнять горизонтальными отрезками, концы которых лежат на
соответствующей окружности. С точки зрения чистой математики
вертикальные (и даже наклонные) отрезки имеют ничуть не меньше
прав на заполнение круга. Но в программировании при распространенном
строении видеобуфера положение совсем иное. Легко выводятся группы
из 8 (и менее) последовательных горизонтальных точек, лежащих в
одном байте. И если этот факт не использовать, то программа заведомо
будет расточительной по времени.
	Рисование каждого отрезка (из составляющих круг) становится
довольно трудоемким, поэтому представляются сомнительными усилия по
ускорению расчета концов отрезка. И мы не приводим такой расчет. Во
всяком случае его можно скроить по аналогии с описанным выше
алгоритмом рисованием окружности. Медленный вариант расчета тоже не
не приводится, так как он слишком тривиален. Достаточно воспользоваться
такой функцией сопроцессора как извлечение квадратного корня.
	Из всех составляющих в рисовании круга неочевидным является
быстрое рисование горизонтального отрезка. Конечно, можно применить
подпрограмму рисования произвольного отрезка, но если она даже самая
быстрая, то будет сильно уступать специальной подпрограмме,
использующей всю специфику момента.
	Следующая подпрограмма рисования горизонтального отрезка
достаточно обща и годится не только для круга. (Установки графического
режима, кроме самого режима, находятся внутри п/п.) Наоборот, при
рисовании круга из нее надо вынести наружу установки графического
режима, чтобы они не срабатывали заново для каждого отрезка и не
тратили бы впустую время. (Не забудьте в начале п/п оставить ah=-1.)
Исходные данные: si=x1, di=x2, bx=y, ah=цвет. Предполагается, что
x1 не превосходит x2. Если нужна п/п без этого ограничения, то в
начале надо добавить сравнение si и di, а также их перестановку
при si > di (на это уйдет 6 байтов).
	mov al,0
	mov dx,974
	out dx,ax	; Установка цвета
	mov ah,160
	mov gs,ax	; gs на видеобуфер
	mov ax,-255	; al=1 ah=-1
	out dx,ax	; Дооступ к 4 плоскостям
	mov al,7
	mov cx,si
	and cl,al
	shr ah,cl	; Расчет левого неполного байта
	shr si,3	; К адресу левого края отрезка
	mov cx,di
	and cl,al
	inc ax		; al=8
	mov ch,127
	shr ch,cl
	shr di,3	; К адресу правого края отрезка
	imul bx,bx,80
	add bx,si	; Адрес левого края отрезка
	sub di,si	; Длина
CC:	jne MM
	sub ah,ch
MM:	out dx,ax	; Установка маски вывода
	or gs:[bx],al	; Вывод в видеобуфер
	mov ah,-1
	inc bx		; Корректировка адреса в видеобуфере
	dec di		; Проверка на конец цикла
	jge CC
	ret
	Для длинных отрезков подпрограмму можно ускорить, если вывод
полных байтов в видеобуфер организовать с помощью команды rep stosb.

	5. Фрактал Мандельбротта - минимальная программа

	Программка FRACTAL.COM не предлагает всего сервиса по
рисованию фракталов. Но показывает достаточно четкую и выразительную
картинку, в которой не узнать фрактал Мандельбротта просто невозможно.
Над минимизацией этой программы многие спецы ломали голову.
	mov	al,13h
	int	10h		; Set video mode
	push	0A000h		; Video Buf Segment
	pop	ds
	mov	si,128		; Y = 128
Row:	mov	di,-321		; X = -321
	dec	si		; Y = Y - 1
Col:	inc	di		; X = X + 1
	je	Row
	mov	cl,46		; Цвет
Calc:	mov	bp,dx		; Re
	add	bp,ax		; Re + Im
	sub	dx,ax		; Re - Im
	imul	bp,dx		; Re = (Re + Im) (Re - Im)
	jo	Paint
	add	dx,ax		; Re
	imul	dx		; Im = Re * Im
	sar	ax,5		; Im = Im/32
	sar	bp,6		; Re = Re/64
	adc	ax,si		; Im = Im + Y
	lea	dx,[bp+di+127]	; Re = Re + X - 127
	loop	Calc
Paint:	xchg	[bx],cl		; Put Pixel & clear cx
	imul	cx  		; (dx, ax) = 0
	inc	bx		; Next Pixel
	jne	Col
	int	16h		; Wait
	aad	232 		; ax = 3 after ESC
	int	10h		; Set video mode
	ret

	То же самое для контроля в кодах (всего 62 байта):
176,19, 205,16, 104,0,160,31, 190,128,0, 191,191,254, 78, 71,
116,249, 177,46, 139,234, 3,232, 43,208, 15,175,234, 114,17,
3,208, 247,234, 193,248,5, 193,253,6, 19,198, 141,83,127, 226,228,
134,15, 247,233, 67, 117,216, 205,22, 213,232, 205,16, 195.
	Можете посмотреть коды в шестнадцатиричной системе.
	Свой вклад в достижение указанного объема внес также
Р.Н.Шакиров из Екатеринбурга.
	По всей видимости, данная программа вообще может претендовать
на самую маленькую в мире содержательную программу, выполняющую работу
по конкретной математической задаче. Можно придумать и меньшие программы,
рисующие какой-нибудь замысловатый орнамент, но не решающие никакой
заранее поставленной проблемы. Мандельбротта же никак к тривиалам не
отнесешь, расчет с комплексными числами далеко не очевиден, его даже
нелегко понять, когда программа налицо и все операторы как на блюдечке.

	Несколько слов вообще о маленьких программах. COM-файл из одного
байта (195) - это полноценная программа, по крайней мере с технической
точки зрения. Однако она ничего не делает. Нетрудно добавить к этому
байту еще, например, операцию сложения в регистрах, но и она будет
бесполезна, если не заданы исходные значения, а главное, если невозможно
посмотреть результат (без отладчика, средствами самой программы).
	Главная трудность как раз в выводе, который обычно громоздок и
для которого нет хороших прерываний. Напомним что подпрограмма
рисования отрезка (это без управляющей части и без задания исходных
данных) уже намного больше 62 байтов. Более короткие способы вывода
можно поискать для выдачи численных данных в текстовом режиме.
Но, скажем, чтобы развернуть число из al в текстовое представление,
автору этой работы понадобится подпрограмма в 16 байтов плюс установка
адреса вывода и cx для количества знаков (как минимум 5 байтов). Далее
понадобится вызвать прерывание по выводу строки на экран, которое может
занять до десятка байтов, не считая установки курсора. Разворачивание
вещественных чисел гораздо более трудоемко. (Автор обходится
подпрограммой в 98 байтов, расставляющей пробелы, минус, точку, т.е.
все необходимые атрибуты по исходному числу поступающему в ax или
st0. Если у вас меньше, сигнализируйте!)
	Поэтому чтобы переплюнуть фрактал Мандельбротта, надо либо
поискать фрактал попроще, либо придумать полезный арифметический
расчет, умещающийся в десятке простейших команд.

			6. Разные картинки

	Нижеследующие программы можно скачать из каталога автора
на файловом сервере ftp.suncloud.ru.

	MARSN.COM - построен на мотивы известной заставки MARS,
но меньше по объему в 13.5 раз, хотя и MARS при объеме в 3 КБ на
первый взгляд представляется рекордным достижением. Правда, от
прототипа в MARSN не осталось ничего, кроме общего эмоционального
настроя. При этом надо учесть, что MARS заархивирован и в первозданном
виде почти вдвое больше, а MARSN представлен чисто (ни один
архиватор его уже не берет).
	Если всмотреться в пейзаж, то можно обнаружить три типа
рельефа: горы, поля, барханы. Как и в MARS, сымитировано подскакивание
летательного аппарата на ухабах, но несколько упрощено за ненадобностью
формирование неба. От запуска к запуску местность меняется.

	GEXIS.COM - аналог игрушки PENTIX и еще более ранней TETRIS.
В отличие от предшественников GEXIS имеет в несколько раз больше багаж
фигур, включая шестиклеточные. Тем не менее, GEXIS, наоборот в 40 раз
меньше, чем PENTIX. Правда, последний словоохотлив, а GEXIS
воздерживается от комментариев. С другой стороны, в GEXIS понадобился
расчет, позволяющий не заваливать игрока сложными фигурами. Во всяком
случае, если в вашей покупке 97.5 % ненужных комментариев, то вы явно
переплатили.

	BIGBL.COM - дальнейшее развитие идей TETRIS и BLOCK OUT.
Здесь предлагается заполнить даже не слой в стакане, а трехмерную
пространственную поверхность. Причем стоимость каждого кубика
возрастает с удалением поверхности от центра. Так что выгоднее сразу
начинать со строительства удаленных поверхностей (не в пример стакану
в пространстве это возможно).
	В отличие от PENTIX и даже от GEXIS здесь нет списка игроков и
итоговых результатов, поскольку, как нетрудно убедиться играющему, в
данном случае более примечателен процесс, нежели результат (варианты
которого не столь разнообразны). Судя по объему GEXIS (всего-то 749
байтов), учет результатов не требует значительных объемов программы.
	При всем этом BIGBL вписался в 1 КБ, а вовсе не растащился
на мегабайты, как можно было бы ожидать, если продолжить тенденции
развития PENTIX и BLOCK OUT, этих старых и в общем-то достаточно
экономных по нынешним меркам игрушек.
	Вращение падающей фигуры - по клавишам: Q, W, E.
	Вращение тела - клавишами: 1, 2, 3.

	SUP0.COM - упрощенный вариант одного из уровней известной
игрушки SUPAPLEX. Здесь в 2 КБ заложена большая часть инструментария
этой игрушки. Каждый новый уровень требует лишь задания очередного
варианта игрового поля, а на это даже в полномасштабной версии нужно
от 0.5 до 1 КБ.

	FISH.COM. В отличие от других иллюстраций, "Рыба" собрана не
с нуля, а получена формальными преобразованиями известной картинки,
содержащей массу графических деталей. Оттого - и весьма заметный
объем FISH. Все же этот объем на 2 порядка меньше, чем у оригинала.
Кроме того, в отличие от источника здесь рыба движется, меняет
размеры и цвет, да еще и машет хвостом.

	K.COM - коврик, калейдоскоп, на первый взгляд элементарная
картинка. Однако всмотритесь в тончайший узор. Кроме того, картинки
никогда не повторяются.

	DOM.COM - вращение домика в пространстве. Конечно, мощные
пакеты делают больше. Но прикиньте, сколько в этих пакетах лишнего,
если один из сложнейших расчетов можно уместить в 2 КБ.

	D12.COM - это иллюстрация к любопытной математической задаче:
разместить на плоскости 12 точек в 19 рядов по три точки в каждом.

	KOR.COM, S21.COM, S22.COM - это просто очень малые по объему
картинки.

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

                                                      Невесенко Н.В.
                                                    5 ноября 2002 г.

                         © 2002 Suncloud.Ru