ZXNet эхоконференция «code.zx»


тема: Вывод черно-белых изображений с градациями яркости



от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 01 .C ══════════════════ (c) Иван Рощин, Москва Fido : 2:5020/689.53 ZXNet : 500:95/462.53 E-mail: bestview@mtu-net.ru WWW : http://www.ivr.da.ru Вывод черно-белых изображений с градациями яркости ══════════════════════════════════════════════════ ("Радиомир. Ваш компьютер" 8-10/2002) (Дата последнего редактирования: 28.09.2002) Как вы, наверно, уже догадались :), речь пойдет о различных способах вывода на экран ZX Spectrum черно-белых изображений с градациями яркости. Для определенности положим, что размер изображения - 256*192, что соответствует разрешению экрана ZX Spectrum, а количество градаций яркости - 256. Понятно, что о реальных 256 градациях яркости на ZX Spectrum можно только мечтать, так что вывести мы сможем лишь что-то похожее на исходное изображение. А как определить, какой способ вывода лучше? Можно, конечно, сравнивать исходное и получаемое изображения "на глаз". Hо гораздо удобнее иметь объективный критерий - вычисляемую по некоторой формуле величину (обозначим ее R), отражающую различие между этими изображениями: от 0 (изображения совпадают) до 1 (максимальное различие). О том, как именно я определил эту величину, вы можете прочесть в Приложении 1. Далее рядом с каждым изображением, полученным при использовании того или иного способа вывода, я буду приводить значение R. Да, а какое же изображение возьмем в качестве исходного? Вот оно: ┌─────────────────────────┐ │ bw_s_1.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 1 Итак, первый способ вывода. Все пикселы с яркостью меньше 128 выводим черными, а с яркостью 128 и больше - белыми: ┌─────────────────────────┐ │ bw_s_2.pcx │ │ │ │ │ │ │ R = 0,0906 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 2 Следующий способ - с использованием так называемых "чанков". В каждом квадрате 4*4 выводим одну из 17 текстур с наиболее близким уровнем яркости (в первой текстуре все 16 пикселов черные, во второй - 15 черных и 1 белый... в последней - все 16 пикселов белые). Таким образом, получается 17 псевдоградаций яркости. ┌─────────────────────────┐ │ bw_s_3.pcx │ │ │ │ │ │ │ R = 0,0708 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 3 Рассматривать такое изображение (как и изображения, получаемые с использованием далее описанных способов) желательно не с близкого расстояния, когда оно воспринимается как мешанина пикселов, а издалека, когда яркость соседних пикселов усредняется. Если невозможно отойти от монитора на достаточное расстояние, или с такого расстояния изображение кажется очень маленьким, то можно добиться того же эффекта, "размыв" изображение - для этого достаточно установить перед экраном фильтр из полупрозрачной полиэтиленовой пленки. Еще один способ вывода. Для каждого пиксела (а не для участка 4*4, как в предыдущем способе) определяем наиболее близкую по яркости текстуру и выводим на экран пиксел из этой текстуры. Координаты пиксела, который берется из текстуры, определяются как остатки от деления координат пиксела в изображении на размер текстуры. Hа рис. 4а приведено изображение, получающееся при использовании 17 текстур 4*4, а на рис. 4б - при использовании 256 текстур 16*16 (вообще-то их 257, а не 256, но, так как в исходном изображении лишь 256 градаций яркости, одна из текстур не используется). ┌─────────────────────────┐ ┌─────────────────────────┐ │ bw_s_4a.pcx │ │ bw_s_4b.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ └─────────────────────────┘ R = 0,0683 R = 0,0680 а) б) Рис. 4 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 01 .C ══════════════════ (c) Иван Рощин, Москва Fido : 2:5020/689.53 ZXNet : 500:95/462.53 E-mail: bestview@mtu-net.ru WWW : http://www.ivr.da.ru Вывод черно-белых изображений с градациями яркости ══════════════════════════════════════════════════ ("Радиомир. Ваш компьютер" 8-10/2002) (Дата последнего редактирования: 28.09.2002) Как вы, наверно, уже догадались :), речь пойдет о различных способах вывода на экран ZX Spectrum черно-белых изображений с градациями яркости. Для определенности положим, что размер изображения - 256*192, что соответствует разрешению экрана ZX Spectrum, а количество градаций яркости - 256. Понятно, что о реальных 256 градациях яркости на ZX Spectrum можно только мечтать, так что вывести мы сможем лишь что-то похожее на исходное изображение. А как определить, какой способ вывода лучше? Можно, конечно, сравнивать исходное и получаемое изображения "на глаз". Но гораздо удобнее иметь объективный критерий - вычисляемую по некоторой формуле величину (обозначим ее R), отражающую различие между этими изображениями: от 0 (изображения совпадают) до 1 (максимальное различие). О том, как именно я определил эту величину, вы можете прочесть в Приложении 1. Далее рядом с каждым изображением, полученным при использовании того или иного способа вывода, я буду приводить значение R. Да, а какое же изображение возьмем в качестве исходного? Вот оно: ┌─────────────────────────┐ │ bw_s_1.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 1 Итак, первый способ вывода. Все пикселы с яркостью меньше 128 выводим черными, а с яркостью 128 и больше - белыми: ┌─────────────────────────┐ │ bw_s_2.pcx │ │ │ │ │ │ │ R = 0,0906 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 2 Следующий способ - с использованием так называемых "чанков". В каждом квадрате 4*4 выводим одну из 17 текстур с наиболее близким уровнем яркости (в первой текстуре все 16 пикселов черные, во второй - 15 черных и 1 белый... в последней - все 16 пикселов белые). Таким образом, получается 17 псевдоградаций яркости. ┌─────────────────────────┐ │ bw_s_3.pcx │ │ │ │ │ │ │ R = 0,0708 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 3 Рассматривать такое изображение (как и изображения, получаемые с использованием далее описанных способов) желательно не с близкого расстояния, когда оно воспринимается как мешанина пикселов, а издалека, когда яркость соседних пикселов усредняется. Если невозможно отойти от монитора на достаточное расстояние, или с такого расстояния изображение кажется очень маленьким, то можно добиться того же эффекта, "размыв" изображение - для этого достаточно установить перед экраном фильтр из полупрозрачной полиэтиленовой пленки. Еще один способ вывода. Для каждого пиксела (а не для участка 4*4, как в предыдущем способе) определяем наиболее близкую по яркости текстуру и выводим на экран пиксел из этой текстуры. Координаты пиксела, который берется из текстуры, определяются как остатки от деления координат пиксела в изображении на размер текстуры. На рис. 4а приведено изображение, получающееся при использовании 17 текстур 4*4, а на рис. 4б - при использовании 256 текстур 16*16 (вообще-то их 257, а не 256, но, так как в исходном изображении лишь 256 градаций яркости, одна из текстур не используется). ┌─────────────────────────┐ ┌─────────────────────────┐ │ bw_s_4a.pcx │ │ bw_s_4b.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ └─────────────────────────┘ R = 0,0683 R = 0,0680 а) б) Рис. 4 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 02 .C ══════════════════ Это было, так сказать, вступление. :) Рассматривались уже известные способы вывода, которые, как вы могли заметить, не отличаются достаточным качеством. А вот теперь пойдет речь о том, как выжать из ZX Spectrum все, на что он способен. Я расскажу о способах, позволяющих повысить качество выводимого изображения на порядок! Смотрите: на ZX Spectrum нам доступны 8 цветов с двумя градациями яркости (bright=0 и bright=1). Так как черный цвет при bright=1 остается черным, всего получается не 16 цветов, а 15. При черно-белом режиме отображения эти 15 цветов превращаются в 15 градаций яркости. За счет их использования и можно повысить качество изображения! Естественно, для этого нужно, чтобы ZX Spectrum был подключен к черно-белому монитору или телевизору (или к цветному, но с возможностью переключения в черно-белый режим). Hо даже если в вашем случае это не так, все равно советую дочитать статью до конца: вдруг что-то окажется полезным. Сразу же отмечу, что основанный на этом эффекте способ вывода черно-белых изображений с градациями яркости может быть использован и на отличных от ZX Spectrum компьютерах, на которых изображение может быть цветным, но нет градаций серого. Кстати, как мне кажется (на практике не проверял), чтобы получить черно-белое изображение на цветном мониторе, достаточно соорудить простейший переходник: три сигнала R, G, B, идущие от компьютера, преобразуем в один (черно-белый) и подаем его на входы R, G, B монитора, вот и все. При этом из деталей понадобятся лишь несколько сопротивлений. Итак, как выводить изображение? Очевидно, сначала надо определить яркость каждой из имеющихся 15 градаций - число в диапазоне 0-255. Рассказ о том, как это сделать, получился довольно объемным, и я решил вынести его в Приложение 2. А сейчас будем считать, что яркости вычислены и мы получили значения a0-a7 для цветов 0-7 при bright=0 и значения b0-b7 для тех же цветов при bright=1. Так как атрибуты задаются для знакоместа (8*8 пикселов), изображение будем выводить познакоместно. Рассмотрим процесс вывода одного знакоместа. Пусть MIN_PIX и MAX_PIX - соответственно минимальный и максимальный уровни яркости в текущем знакоместе исходного изображения. Пусть MIN и MAX - соответственно яркости paper и ink (при выбранном значении bright) в текущем знакоместе выводимого изображения. Зная MIN_PIX и MAX_PIX, надо определить атрибуты знакоместа (ink, paper и bright) так, чтобы выполнялись условия: MIN <= MIN_PIX, MAX >= MAX_PIX. Очевидно, в большинстве случаев это можно сделать не единственным способом. Тогда из всех возможных пар (MIN, MAX) выбираем такую, чтобы разность между MAX и MIN была наименьшей. Чтобы не перебирать все 128 возможных сочетаний ink, paper и bright, удобно поступить следующим образом. Сначала проверяем, выполняется ли условие MAX_PIX > a(7)? Если да, то bright не может быть равно 0, т.е. bright=1. Среди b0-b7 отыскиваем максимальное b(i), при котором выполняется условие b(i) <= MIN_PIX, и минимальное b(j), при котором выполняется условие b(j) >= MAX_PIX. Тогда paper=i, а ink=j. Если же MAX_PIX <= a(7), придется рассмотреть два варианта: bright=0 и bright=1. Hаходим b(i) и b(j), как было описано выше. Затем среди a0-a7 отыскиваем максимальное a(k), при котором выполняется условие a(k) <= MIN_PIX, и минимальное a(l), при котором выполняется условие a(l) >= MAX_PIX. Теперь надо выбрать из двух пар (b(i),b(j)), (a(k),a(l)) такую, где разница между значениями меньше. Если это первая пара, то bright=1, ink=j, paper=i. Если вторая пара, то bright=0, ink=l, paper=k. После того, как атрибуты знакоместа определены, остается узнать, какие пикселы в нем будут цвета ink, а какие - paper. В предыдущем способе вывода мы получали 257 псевдоградаций яркости между черным и белым с помощью 257 текстур 16*16. Hу а в этом способе мы можем получить с помощью тех же текстур 257 псевдо- градаций между MIN и MAX. Для каждого пиксела определяем наиболее близкую по яркости текстуру и выводим на экран пиксел из этой текстуры. Вот что получается при таком способе вывода. Hе правда ли, качество изображения значительно повысилось? ┌─────────────────────────┐ │ bw_s_5.pcx │ │ │ │ │ │ │ R = 0,0106 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 5 Процедура, выводящая изображение этим способом, приведена в Приложении 3. Там же вы найдете процедуры для способов, описанных далее. Для дальнейшего повышения качества изображения можно использовать так называемый multicolor-эффект. Сущность его в следующем: если изменить атрибут знакоместа, когда луч уже прорисовал его часть на экране, то оставшаяся часть знакоместа будет прорисована уже с новым значением атрибута. Так можно увеличить атрибутное разрешение по вертикали. Если надо увеличить разрешение вдвое (т.е. задать свой атрибут для каждой половины знакоместа - области размером 8*4 пиксела), удобнее не перезаписывать значения атрибутов при прорисовке изображения, а сделать так: построить верхние половины знакомест со своими атрибутами на одном экране, нижние половины со своими атрибутами - на другом, а затем переключать экраны каждый раз, когда прорисуются очередные 4 строки пикселов (т.е. верхняя или нижняя половина строки знакомест). Вот что получается при таком способе вывода: ┌─────────────────────────┐ │ bw_s_6.pcx │ │ │ │ │ │ │ R = 0,0090 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 6 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 02 .C ══════════════════ Это было, так сказать, вступление. :) Рассматривались уже известные способы вывода, которые, как вы могли заметить, не отличаются достаточным качеством. А вот теперь пойдет речь о том, как выжать из ZX Spectrum все, на что он способен. Я расскажу о способах, позволяющих повысить качество выводимого изображения на порядок! Смотрите: на ZX Spectrum нам доступны 8 цветов с двумя градациями яркости (bright=0 и bright=1). Так как черный цвет при bright=1 остается черным, всего получается не 16 цветов, а 15. При черно-белом режиме отображения эти 15 цветов превращаются в 15 градаций яркости. За счет их использования и можно повысить качество изображения! Естественно, для этого нужно, чтобы ZX Spectrum был подключен к черно-белому монитору или телевизору (или к цветному, но с возможностью переключения в черно-белый режим). Но даже если в вашем случае это не так, все равно советую дочитать статью до конца: вдруг что-то окажется полезным. Сразу же отмечу, что основанный на этом эффекте способ вывода черно-белых изображений с градациями яркости может быть использован и на отличных от ZX Spectrum компьютерах, на которых изображение может быть цветным, но нет градаций серого. Кстати, как мне кажется (на практике не проверял), чтобы получить черно-белое изображение на цветном мониторе, достаточно соорудить простейший переходник: три сигнала R, G, B, идущие от компьютера, преобразуем в один (черно-белый) и подаем его на входы R, G, B монитора, вот и все. При этом из деталей понадобятся лишь несколько сопротивлений. Итак, как выводить изображение? Очевидно, сначала надо определить яркость каждой из имеющихся 15 градаций - число в диапазоне 0-255. Рассказ о том, как это сделать, получился довольно объемным, и я решил вынести его в Приложение 2. А сейчас будем считать, что яркости вычислены и мы получили значения a0-a7 для цветов 0-7 при bright=0 и значения b0-b7 для тех же цветов при bright=1. Так как атрибуты задаются для знакоместа (8*8 пикселов), изображение будем выводить познакоместно. Рассмотрим процесс вывода одного знакоместа. Пусть MIN_PIX и MAX_PIX - соответственно минимальный и максимальный уровни яркости в текущем знакоместе исходного изображения. Пусть MIN и MAX - соответственно яркости paper и ink (при выбранном значении bright) в текущем знакоместе выводимого изображения. Зная MIN_PIX и MAX_PIX, надо определить атрибуты знакоместа (ink, paper и bright) так, чтобы выполнялись условия: MIN <= MIN_PIX, MAX >= MAX_PIX. Очевидно, в большинстве случаев это можно сделать не единственным способом. Тогда из всех возможных пар (MIN, MAX) выбираем такую, чтобы разность между MAX и MIN была наименьшей. Чтобы не перебирать все 128 возможных сочетаний ink, paper и bright, удобно поступить следующим образом. Сначала проверяем, выполняется ли условие MAX_PIX > a(7)? Если да, то bright не может быть равно 0, т.е. bright=1. Среди b0-b7 отыскиваем максимальное b(i), при котором выполняется условие b(i) <= MIN_PIX, и минимальное b(j), при котором выполняется условие b(j) >= MAX_PIX. Тогда paper=i, а ink=j. Если же MAX_PIX <= a(7), придется рассмотреть два варианта: bright=0 и bright=1. Находим b(i) и b(j), как было описано выше. Затем среди a0-a7 отыскиваем максимальное a(k), при котором выполняется условие a(k) <= MIN_PIX, и минимальное a(l), при котором выполняется условие a(l) >= MAX_PIX. Теперь надо выбрать из двух пар (b(i),b(j)), (a(k),a(l)) такую, где разница между значениями меньше. Если это первая пара, то bright=1, ink=j, paper=i. Если вторая пара, то bright=0, ink=l, paper=k. После того, как атрибуты знакоместа определены, остается узнать, какие пикселы в нем будут цвета ink, а какие - paper. В предыдущем способе вывода мы получали 257 псевдоградаций яркости между черным и белым с помощью 257 текстур 16*16. Ну а в этом способе мы можем получить с помощью тех же текстур 257 псевдо- градаций между MIN и MAX. Для каждого пиксела определяем наиболее близкую по яркости текстуру и выводим на экран пиксел из этой текстуры. Вот что получается при таком способе вывода. Не правда ли, качество изображения значительно повысилось? ┌─────────────────────────┐ │ bw_s_5.pcx │ │ │ │ │ │ │ R = 0,0106 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 5 Процедура, выводящая изображение этим способом, приведена в Приложении 3. Там же вы найдете процедуры для способов, описанных далее. Для дальнейшего повышения качества изображения можно использовать так называемый multicolor-эффект. Сущность его в следующем: если изменить атрибут знакоместа, когда луч уже прорисовал его часть на экране, то оставшаяся часть знакоместа будет прорисована уже с новым значением атрибута. Так можно увеличить атрибутное разрешение по вертикали. Если надо увеличить разрешение вдвое (т.е. задать свой атрибут для каждой половины знакоместа - области размером 8*4 пиксела), удобнее не перезаписывать значения атрибутов при прорисовке изображения, а сделать так: построить верхние половины знакомест со своими атрибутами на одном экране, нижние половины со своими атрибутами - на другом, а затем переключать экраны каждый раз, когда прорисуются очередные 4 строки пикселов (т.е. верхняя или нижняя половина строки знакомест). Вот что получается при таком способе вывода: ┌─────────────────────────┐ │ bw_s_6.pcx │ │ │ │ │ │ │ R = 0,0090 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 6 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 03 .C ══════════════════ Очевидно, максимум возможного - свой атрибут для каждой линии знакоместа (8 пикселов). Вот получаемое при этом изображение: ┌─────────────────────────┐ │ bw_s_7.pcx │ │ │ │ │ │ │ R = 0,0071 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 7 При практической реализации максимального атрибутного разрешения, однако, возникает проблема. Дело в том, что за 224 такта (время прорисовки одной строки) нужно обновить 32 байта атрибутов строки, а сделать это не так-то просто: обычные способы пересылки данных требуют гораздо больше времени. В процедуре, приведенной в Приложении 3, я решил эту проблему так: выводится не все изображение, а окно шириной 11 знакомест, которое можно перемещать вправо-влево. Вы можете попробовать увеличить ширину выводимой области изображения, используя способы быстрой пересылки данных - например, описанные в [5]. Есть еще один способ улучшения качества. Можно сформировать такие два изображения, чтобы среднее между ними было наиболее похоже на оригинал, а затем быстро чередовать их - при этом зритель как раз и увидит среднее изображение. Пусть атрибуты обоих изображений одинаковы. Если раньше яркость пиксела в знакоместе могла быть одной из двух: MIN или MAX, то при быстром чередовании двух изображений она может быть уже одной из трех: MIN, MAX или (MIN+MAX)/2 (когда на одном изображении яркость пиксела MIN, а на другом - MAX). За счет этого и повышается качество. Если раньше мы получали 257 псевдоградаций яркости от MIN до MAX с помощью 257 различных текстур 16*16, то теперь получим 257 псевдоградаций от MIN до (MIN+MAX)/2 и еще 257 - от (MIN+MAX)/2 до MAX, а всего, таким образом, будет 513 псевдоградаций от MIN до MAX. При формировании таких двух изображений их атрибуты (они одинаковы) определяются так же, как и раньше. Для каждого пиксела определяем, какая из 513 псевдоградаций (от 0 до 512) наиболее близка к нему по яркости. Если номер псевдоградации меньше 256, то на первом изображении соответствующий пиксел выключаем, а на втором - берем из текстуры с номером, равным номеру псевдоградации. Если же номер псевдоградации больше или равен 256, то на первом изображении пиксел включаем, а на втором - берем из текстуры с номером, равным номеру псевдоградации, уменьшенному на 256. Используя этот способ для вывода исходного изображения, получим такие два изображения: ┌─────────────────────────┐ ┌─────────────────────────┐ │ bw_s_8a.pcx │ │ bw_s_8b.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ └─────────────────────────┘ а) б) Рис. 8 При их быстром чередовании мы увидим среднее изображение - вот оно: ┌─────────────────────────┐ │ bw_s_9.pcx │ │ │ │ │ │ │ R = 0,0026 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 9 Однако, рассматривая его, вы будете разочарованы из-за довольно заметного мерцания с частотой 25 Гц. Можно ли его уменьшить, и если да, то как? Попробую объяснить на примере. Пусть надо получить в некоторой области экрана серый цвет за счет быстрой смены белого и черного. Можно сделать так: в одном кадре показывать всю эту область белой, а в другом - черной (рис. 10а). Мерцание при этом будет очень заметно. А можно в одном кадре выводить в этой области "шахматную" текстуру, а в другом кадре - такую же текстуру, но в которой вместо белых пикселов - черные и наоборот (рис. 10б). Тогда мерцание будет гораздо меньше, а издалека - вообще не будет заметно. ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │████████│ │ │ │██ ██ │ │ ██ ██│ │████████│ + │ │ │ ██ ██│ + │██ ██ │ │████████│ │ │ │██ ██ │ │ ██ ██│ │████████│ │ │ │ ██ ██│ │██ ██ │ └────────┘ └────────┘ └────────┘ └────────┘ а) б) Рис. 10 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 04 .C ══════════════════ Почему так получается? При рассматривании изображения издалека яркость соседних пикселов усредняется, а так как во втором случае соседние пикселы мерцают в противофазе, то их средняя яркость и в одном, и в другом кадре будет одинаковой, и в результате изображение не будет мерцать. Иначе говоря, средняя яркость достаточно малых участков на первом и втором изображениях должна быть примерно одинаковой, а достигнуть этого можно, если соседние мерцающие пикселы будут в противофазе. В нашем случае (рис. 8) изображения сильно отличаются друг от друга, и поэтому мерцание при их смене очень заметно. Чтобы изображения стали более похожими друг на друга, сделаем следующее: просматривая пикселы (для определенности, слева направо и сверху вниз), определим мерцающие (т.е. различающиеся на первом и втором изображениях); первый такой пиксел сделаем темным на первом изображении и светлым на втором, следующий - наоборот, светлым на первом изображении и темным на втором (т.е. он будет в противофазе с предыдущим), и так далее. Вот во что превращаются приведенные на рис. 8 изображения после такой обработки: ┌─────────────────────────┐ ┌─────────────────────────┐ │ bw_s_11a.pcx │ │ bw_s_11b.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ └─────────────────────────┘ а) б) Рис. 11 Сразу видно, что теперь они гораздо более похожи друг на друга. Мерцание при их быстрой смене становится значительно меньше, а издалека - вообще не заметно. Результирующее изображение будет, разумеется, тем же самым (рис. 9): ведь от перемены мест слагаемых сумма не меняется. Кстати, вот совет, как не видеть мерцания даже с близкого расстояния. Возьмите лист бумаги, проткните в нем дырку и смотрите на экран сквозь эту дырку. Количество света, попадающего в глаз, будет меньше, и мерцание сильно уменьшится. Быстрое чередование двух изображений можно совместить с multicolor'om, что еще повысит качество. Вот что получится, когда для каждой половины знакоместа задается свой атрибут: ┌─────────────────────────┐ │ bw_s_12.pcx │ │ │ │ │ │ │ R = 0,0022 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 12 А вот - когда свой атрибут задается для каждой линии знакоместа: ┌─────────────────────────┐ │ bw_s_13.pcx │ │ │ │ │ │ │ R = 0,0017 │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 13 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 05 .C ══════════════════ Описанные выше способы иллюстрировались на примере одного исходного изображения. Hиже приведены примеры для еще нескольких изображений (слева - исходное, справа - получающееся при выводе). Использовался способ с быстрым чередованием двух изображений и multicolor'ом, атрибуты задавались для каждой линии знакоместа. (Кстати, сканирование фотографий, послуживших исходными изображениями, выполнил по моей просьбе Алексей Летаев - спасибо!) ┌─────────────────────────┐┌─────────────────────────┐ │ bw_s_14a.pcx ││ bw_s_14b.pcx │ │ ││ │ │ ││ │ │ ││ │R = 0,0020 │ ││ │ │ ││ │ │ ││ │ │ ││ │ └─────────────────────────┘└─────────────────────────┘ ┌─────────────────────────┐┌─────────────────────────┐ │ bw_s_14c.pcx ││ bw_s_14d.pcx │ │ ││ │ │ ││ │ │ ││ │R = 0,0021 │ ││ │ │ ││ │ │ ││ │ │ ││ │ └─────────────────────────┘└─────────────────────────┘ ┌─────────────────────────┐┌─────────────────────────┐ │ bw_s_14e.pcx ││ bw_s_14f.pcx │ │ ││ │ │ ││ │ │ ││ │R = 0,0029 │ ││ │ │ ││ │ │ ││ │ │ ││ │ └─────────────────────────┘└─────────────────────────┘ ┌─────────────────────────┐┌─────────────────────────┐ │ bw_s_14g.pcx ││ bw_s_14h.pcx │ │ ││ │ │ ││ │ │ ││ │R = 0,0023 │ ││ │ │ ││ │ │ ││ │ │ ││ │ └─────────────────────────┘└─────────────────────────┘ Рис. 14 Чтобы увидеть на изображении еще больше подробностей, можно, во-первых, использовать вывод с увеличением, и, во-вторых, повысить контрастность выводимого участка изображения. При выводе с увеличением мы не сможем увидеть сразу всю картинку, но за счет этого на видимом фрагменте сможем различить больше деталей. Вот пример: на рис. 15а показана центральная часть рис. 5, для удобства сравнения увеличенная вдвое, а на рис. 15б - то, что получится, если увеличивать вдвое центральную часть непосредственно в процессе вывода. ┌─────────────────────────┐ ┌─────────────────────────┐ │ bw_s_15a.pcx │ │ bw_s_15b.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ └─────────────────────────┘ а) б) Рис. 15 Приведенные в Приложении 3 процедуры вывода могут выводить указанную область изображения с увеличением в 2, 4, 8... раз. Думаю, вам не составит труда догадаться, как увеличивать изображение в количество раз, не являющееся степенью двойки. Теперь о повышении контрастности. Если в выводимом изображении (или в выводимой части изображения) самая темная точка светлее 0 и/или самая светлая точка темнее 255, то можно "растянуть" диапазон яркости до 0 - 255 по следующей формуле: A-Amin A'= 255 ─────────── Amax-Amin где A - исходная яркость, A' - новая яркость, Amin - яркость самой темной точки, Amax - яркость самой светлой точки. После такого преобразования мы увидим темные участки изображения темнее, чем надо, а светлые - светлее, но при этом можно будет различить больше подробностей. Hа рис. 16 вы можете увидеть изображение, полученное, как и рис. 15б, но с повышением контрастности. ┌─────────────────────────┐ │ bw_s_16.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 16 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 05 .C ══════════════════ Описанные выше способы иллюстрировались на примере одного исходного изображения. Ниже приведены примеры для еще нескольких изображений (слева - исходное, справа - получающееся при выводе). Использовался способ с быстрым чередованием двух изображений и multicolor'ом, атрибуты задавались для каждой линии знакоместа. (Кстати, сканирование фотографий, послуживших исходными изображениями, выполнил по моей просьбе Алексей Летаев - спасибо!) ┌─────────────────────────┐┌─────────────────────────┐ │ bw_s_14a.pcx ││ bw_s_14b.pcx │ │ ││ │ │ ││ │ │ ││ │R = 0,0020 │ ││ │ │ ││ │ │ ││ │ │ ││ │ └─────────────────────────┘└─────────────────────────┘ ┌─────────────────────────┐┌─────────────────────────┐ │ bw_s_14c.pcx ││ bw_s_14d.pcx │ │ ││ │ │ ││ │ │ ││ │R = 0,0021 │ ││ │ │ ││ │ │ ││ │ │ ││ │ └─────────────────────────┘└─────────────────────────┘ ┌─────────────────────────┐┌─────────────────────────┐ │ bw_s_14e.pcx ││ bw_s_14f.pcx │ │ ││ │ │ ││ │ │ ││ │R = 0,0029 │ ││ │ │ ││ │ │ ││ │ │ ││ │ └─────────────────────────┘└─────────────────────────┘ ┌─────────────────────────┐┌─────────────────────────┐ │ bw_s_14g.pcx ││ bw_s_14h.pcx │ │ ││ │ │ ││ │ │ ││ │R = 0,0023 │ ││ │ │ ││ │ │ ││ │ │ ││ │ └─────────────────────────┘└─────────────────────────┘ Рис. 14 Чтобы увидеть на изображении еще больше подробностей, можно, во-первых, использовать вывод с увеличением, и, во-вторых, повысить контрастность выводимого участка изображения. При выводе с увеличением мы не сможем увидеть сразу всю картинку, но за счет этого на видимом фрагменте сможем различить больше деталей. Вот пример: на рис. 15а показана центральная часть рис. 5, для удобства сравнения увеличенная вдвое, а на рис. 15б - то, что получится, если увеличивать вдвое центральную часть непосредственно в процессе вывода. ┌─────────────────────────┐ ┌─────────────────────────┐ │ bw_s_15a.pcx │ │ bw_s_15b.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ └─────────────────────────┘ а) б) Рис. 15 Приведенные в Приложении 3 процедуры вывода могут выводить указанную область изображения с увеличением в 2, 4, 8... раз. Думаю, вам не составит труда догадаться, как увеличивать изображение в количество раз, не являющееся степенью двойки. Теперь о повышении контрастности. Если в выводимом изображении (или в выводимой части изображения) самая темная точка светлее 0 и/или самая светлая точка темнее 255, то можно "растянуть" диапазон яркости до 0 - 255 по следующей формуле: A-Amin A'= 255 ─────────── Amax-Amin где A - исходная яркость, A' - новая яркость, Amin - яркость самой темной точки, Amax - яркость самой светлой точки. После такого преобразования мы увидим темные участки изображения темнее, чем надо, а светлые - светлее, но при этом можно будет различить больше подробностей. На рис. 16 вы можете увидеть изображение, полученное, как и рис. 15б, но с повышением контрастности. ┌─────────────────────────┐ │ bw_s_16.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 16 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 06 .C ══════════════════ Гамма-коррекция ─────────────── Градации яркости в исходном изображении обозначены числами от 0 до 255. Эти числа мы, собственно, и считали яркостью пикселов (имея в виду определение яркости, данное в Приложении 2). То есть мы считали, что яркость пиксела линейно зависит от числа, соответствующего этому пикселу в изображении: если, например, одному пикселу соответствует число 10, а другому - 20, то второй пиксел в два раза ярче первого. Однако в общем случае это не так. Зависимость здесь не линейная, а степенная (показатель степени называют "гамма"). Линейная зависимость - лишь частный случай, когда гамма равна 1. Hа рис. 17 приведены графики для трех возможных случаев (по оси x откладывается значение, соответствующее пикселу в изображении, а по оси y - фактическая яркость пиксела). ┌───────────────────┐┌───────────────────┐┌───────────────────┐ │ bw_s_17a.pcx ││ bw_s_17b.pcx ││ bw_s_17c.pcx │ │ ││ ││ │ │ ││ ││ │ │ ││ ││ │ │ ││ ││ │ │ ││ ││ │ └───────────────────┘└───────────────────┘└───────────────────┘ а) гамма=1 б) гамма>1 в) гамма<1 Рис. 17 Как видим, если гамма больше 1, то в изображении с большей точностью представлена информация о темных областях и с меньшей точностью - о светлых, а если гамма меньше 1 - наоборот. Чтобы правильно выводить изображения с гаммой, отличной от единицы, и нужна гамма-коррекция. Из значения пиксела (обозначим его x) мы должны получить его яркость (обозначим ее y). Если и значение, и яркость - числа от 0 до 255, то формула такова: y=255*(x/255)^Gamma Удобно сначала вычислить y для всех x от 0 до 255, помещая значения в таблицу, а при выводе просто брать из таблицы уже готовое значение. Приведенные в Приложении 3 процедуры вывода могут выполнять гамма-коррекцию, если установить в 1 флаг условной компиляции GAMMA_CORR. При этом значением гаммы по умолчанию считается 1,5. Для другого значения надо будет пересчитать таблицу коррекции с помощью программы на Бейсике, приведенной там же. Кстати, эту программу можно запустить и просто, чтобы посмотреть, какие графики получаются при разных значениях гаммы. А как узнать, какое значение гаммы у выводимого изображения? Если вы берете это изображение из файла в формате png (или в каком-либо другом формате, где значение гаммы указывается в самом файле), то сложностей не возникает. А если вы (как и я) будете брать изображение из pcx-файла, где таких сведений не содержится? Тогда придется подбирать этот коэффициент опытным путем. Попробуйте сначала вывести изображение без гамма-коррекции. Если оно выглядит естественно, то больше ничего и не надо. Если оно слишком темное - значит, гамма больше 1; если слишком светлое - гамма меньше 1. Кстати: изображения с гаммой больше 1 будут выводиться более точно за счет того, что они содержат больше информации о темных областях, а распределение спектрумовских градаций яркости (см. рис. 19 в Приложении 2) как раз таково (по крайней мере, у меня), что темные области воспроизводятся гораздо точнее светлых. Работа с pcx-файлами ──────────────────── Исходные изображения надо откуда-то брать. Я считывал их из файлов в формате pcx. В Приложении 4 приведены две процедуры для работы с такими файлами. Первая процедура читает данные из pcx-файла и формирует в памяти массив данных о пикселах. Вторая процедура выполняет обратную задачу - записывает pcx-файл по находящимся в памяти данным о пикселах (именно с ее помощью я подготовил все pcx-файлы с рисунками для этой статьи). Еще одна приведенная там процедура непосредственно с pcx-файлами не работает, но окажется полезной, если вам нужно преобразовать изображение в спектрумовском формате (6912 байтов) в черно-белый pcx-файл. Она формирует в памяти массив данных о пикселах такого изображения по заданным значениям градаций яркости, а на основе этих данных уже можно создать pcx-файл. И еще одно небольшое дополнение ─────────────────────────────── Если исходное изображение - цветное, и вы хотите вывести его в черно-белом виде, то для получения яркости пиксела по известным значениям компонент R, G, B воспользуйтесь следующей формулой: Y = 0,299R + 0,587G + 0,114B Так как вычислять яркость нужно для каждого пиксела, а даже изображение размером со спектрумовский экран - 256*192 - содержит 49152 пиксела, то понятно, что это вычисление должно быть как можно более быстрым. Удобно применить табличный способ. Пусть имеется таблица длиной 768 (т.е. 3*256) байтов, начинающаяся с адреса, кратного 256 (обозначим этот адрес TAB_RGB). Пусть в первых 256 байтах этой таблицы содержатся значения функции y=0,299x для x от 0 до 255, округленные до целых (для практического применения такая точность вполне достаточна), во вторых 256 байтах - значения функции y=0,587x, а в третьих 256 байтах - значения функции y=0,114x (также для x от 0 до 255). ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 06 .C ══════════════════ Гамма-коррекция ─────────────── Градации яркости в исходном изображении обозначены числами от 0 до 255. Эти числа мы, собственно, и считали яркостью пикселов (имея в виду определение яркости, данное в Приложении 2). То есть мы считали, что яркость пиксела линейно зависит от числа, соответствующего этому пикселу в изображении: если, например, одному пикселу соответствует число 10, а другому - 20, то второй пиксел в два раза ярче первого. Однако в общем случае это не так. Зависимость здесь не линейная, а степенная (показатель степени называют "гамма"). Линейная зависимость - лишь частный случай, когда гамма равна 1. На рис. 17 приведены графики для трех возможных случаев (по оси x откладывается значение, соответствующее пикселу в изображении, а по оси y - фактическая яркость пиксела). ┌───────────────────┐┌───────────────────┐┌───────────────────┐ │ bw_s_17a.pcx ││ bw_s_17b.pcx ││ bw_s_17c.pcx │ │ ││ ││ │ │ ││ ││ │ │ ││ ││ │ │ ││ ││ │ │ ││ ││ │ └───────────────────┘└───────────────────┘└───────────────────┘ а) гамма=1 б) гамма>1 в) гамма<1 Рис. 17 Как видим, если гамма больше 1, то в изображении с большей точностью представлена информация о темных областях и с меньшей точностью - о светлых, а если гамма меньше 1 - наоборот. Чтобы правильно выводить изображения с гаммой, отличной от единицы, и нужна гамма-коррекция. Из значения пиксела (обозначим его x) мы должны получить его яркость (обозначим ее y). Если и значение, и яркость - числа от 0 до 255, то формула такова: y=255*(x/255)^Gamma Удобно сначала вычислить y для всех x от 0 до 255, помещая значения в таблицу, а при выводе просто брать из таблицы уже готовое значение. Приведенные в Приложении 3 процедуры вывода могут выполнять гамма-коррекцию, если установить в 1 флаг условной компиляции GAMMA_CORR. При этом значением гаммы по умолчанию считается 1,5. Для другого значения надо будет пересчитать таблицу коррекции с помощью программы на Бейсике, приведенной там же. Кстати, эту программу можно запустить и просто, чтобы посмотреть, какие графики получаются при разных значениях гаммы. А как узнать, какое значение гаммы у выводимого изображения? Если вы берете это изображение из файла в формате png (или в каком-либо другом формате, где значение гаммы указывается в самом файле), то сложностей не возникает. А если вы (как и я) будете брать изображение из pcx-файла, где таких сведений не содержится? Тогда придется подбирать этот коэффициент опытным путем. Попробуйте сначала вывести изображение без гамма-коррекции. Если оно выглядит естественно, то больше ничего и не надо. Если оно слишком темное - значит, гамма больше 1; если слишком светлое - гамма меньше 1. Кстати: изображения с гаммой больше 1 будут выводиться более точно за счет того, что они содержат больше информации о темных областях, а распределение спектрумовских градаций яркости (см. рис. 19 в Приложении 2) как раз таково (по крайней мере, у меня), что темные области воспроизводятся гораздо точнее светлых. Работа с pcx-файлами ──────────────────── Исходные изображения надо откуда-то брать. Я считывал их из файлов в формате pcx. В Приложении 4 приведены две процедуры для работы с такими файлами. Первая процедура читает данные из pcx-файла и формирует в памяти массив данных о пикселах. Вторая процедура выполняет обратную задачу - записывает pcx-файл по находящимся в памяти данным о пикселах (именно с ее помощью я подготовил все pcx-файлы с рисунками для этой статьи). Еще одна приведенная там процедура непосредственно с pcx-файлами не работает, но окажется полезной, если вам нужно преобразовать изображение в спектрумовском формате (6912 байтов) в черно-белый pcx-файл. Она формирует в памяти массив данных о пикселах такого изображения по заданным значениям градаций яркости, а на основе этих данных уже можно создать pcx-файл. И еще одно небольшое дополнение ─────────────────────────────── Если исходное изображение - цветное, и вы хотите вывести его в черно-белом виде, то для получения яркости пиксела по известным значениям компонент R, G, B воспользуйтесь следующей формулой: Y = 0,299R + 0,587G + 0,114B Так как вычислять яркость нужно для каждого пиксела, а даже изображение размером со спектрумовский экран - 256*192 - содержит 49152 пиксела, то понятно, что это вычисление должно быть как можно более быстрым. Удобно применить табличный способ. Пусть имеется таблица длиной 768 (т.е. 3*256) байтов, начинающаяся с адреса, кратного 256 (обозначим этот адрес TAB_RGB). Пусть в первых 256 байтах этой таблицы содержатся значения функции y=0,299x для x от 0 до 255, округленные до целых (для практического применения такая точность вполне достаточна), во вторых 256 байтах - значения функции y=0,587x, а в третьих 256 байтах - значения функции y=0,114x (также для x от 0 до 255). ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 07 .C ══════════════════ Вот 32-байтная процедура построения такой таблицы: MK_T_RGB LD BC,TAB_RGB+#2FF ;Адрес последнего байта таблицы. LD H,29 ;0,114*256 CALL MK_T_RGB_1 LD H,150 ;0,587*256 CALL MK_T_RGB_1 LD H,77 ;0,299*256 MK_T_RGB_1 XOR A LD L,A LD D,A LD E,H MK_T_RGB_2 SBC HL,DE RET C ;Запись округленного результата: LD A,H BIT 7,L JR Z,MK_T_RGB_3 INC A MK_T_RGB_3 LD (BC),A DEC BC JR MK_T_RGB_2 С помощью этой таблицы можно очень быстро вычислить яркость пиксела - например, используя следующий фрагмент программы: ;Вход: значения компонент R,G,B - в регистрах L,D,E ; соответственно. ;Выход: вычисленное значение Y - в аккумуляторе. ;Длина: 9 байтов. ;Время выполнения: 44 такта. LD H,TAB_RGB/256 LD A,(HL) ;A=0,299R INC H LD L,D ADD A,(HL) ;A=0,299R+0,587G INC H LD L,E ADD A,(HL) ;A=0,299R+0,587G+0,114B Если надо обработать сразу группу пикселов, выгоднее будет не загружать каждый раз регистр H, а загрузить его только один раз, а затем только изменять командами INC и DEC (обрабатывая по два пиксела в цикле), как в нижеприведенной процедуре: ;Вход: IX - адрес начала исходных данных (R1,G1,B1,R2,G2,B2...); ; DE - куда помещать результат (Y1,Y2...); ; BC - количество обрабатываемых пикселов (должно быть ; четным), деленное на 2. ;Длина: 49 байтов. CALC_Y EXX LD DE,6 EXX LD H,TAB_RGB/256 CALC_Y_1 LD L,(IX) ;L=R1 LD A,(HL) ;A=0,299R1 INC H LD L,(IX+1) ;L=G1 ADD A,(HL) ;A=0,299R1+0,587G1 INC H LD L,(IX+2) ;L=B1 ADD A,(HL) ;A=0,299R1+0,587G1+0,114B1 LD (DE),A INC DE LD L,(IX+5) ;L=B2 LD A,(HL) ;A=0,114B2 DEC H LD L,(IX+4) ;L=G2 ADD A,(HL) ;A=0,114B2+0,587G2 DEC H LD L,(IX+3) ;L=R2 ADD A,(HL) ;A=0,114B2+0,587G2+0,299R2 LD (DE),A INC DE EXX ADD IX,DE ;IX:=IX+6 EXX DEC BC LD A,B OR C JR NZ,CALC_Y_1 RET Если заранее известно, что DE на входе будет четным (или нечетным), то первую (или, соответственно, вторую) команду INC DE можно заменить на более быструю INC E. Если обрабатывается не более 512 пикселов, то в качестве счетчика можно использовать не регистровую пару BC, а только один регистр B, используя для организации цикла команду DJNZ. Литература ────────── 1. А.Бордачев. "О формате PCX". Библиотека информационной технологии, вып. 8. Москва, "Инфоарт", 1993. 2. Т.Кенцл. "Форматы файлов Internet". СПб, "Питер", 1997. 3. К.Бочков. "Сканирование - это так просто...". "Мир ПК" 11/2000. 4. С.Кащавцев. "Волшебная сила кривых - II". "Компьютерра" 49-50/1998. 5. Е.Зарецкий. "Быстрый вывод графики". "Радиолюбитель. Ваш компьютер" 5,6/2001. ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 08 .C ══════════════════ Приложение 1 ──────────── Здесь я расскажу о том, как определял R - различие между двумя изображениями. Вычислим среднеквадратичное отклонение: h-1 l-1 ┌── ┌── 2 > > (p1(x,y)-p2(x,y)) └── └── y=0 x=0 r1 = ───────────────────────── l*h где h - высота сравниваемых изображений, l - ширина, p1(x,y) - яркость пиксела с координатами (x,y) в первом изображении, приведенная к диапазону 0-1 (если, например, в изображении 256 градаций яркости, то значение пиксела надо разделить на 255), p2(x,y) - то же самое для второго изображения. Далее аналогично вычислим среднеквадратичное отклонение для всех участков 2*2 пиксела, считая яркостью участка среднюю яркость входящих в него пикселов: h-2 l-2 ┌── ┌── 2 > > (m1(x,y)-m2(x,y)) └── └── y=0 x=0 r2 = ───────────────────────── (l-1)*(h-1) 1 1 1 1 ┌── ┌── ┌── ┌── > > p1(x+i,y+j) > > p2(x+i,y+j) └── └── └── └── j=0 i=0 j=0 i=0 где m1(x,y) = ──────────────────, m2(x,y) = ──────────────────. 4 4 И для участков 4*4 пиксела: h-4 l-4 ┌── ┌── 2 > > (n1(x,y)-n2(x,y)) └── └── y=0 x=0 r3 = ───────────────────────── (l-3)*(h-3) 3 3 3 3 ┌── ┌── ┌── ┌── > > p1(x+i,y+j) > > p2(x+i,y+j) └── └── └── └── j=0 i=0 j=0 i=0 где n1(x,y) = ──────────────────, n2(x,y) = ──────────────────. 16 16 Величина r1 отражает различие с близкого расстояния (когда на сравниваемых изображениях можно различить отдельные пикселы). Величина r2 - различие со среднего расстояния (когда нельзя различить деталей меньше чем 2*2 пиксела). Наконец, величина r3 - различие с дальнего расстояния (когда уже не видно деталей меньше чем 4*4 пиксела). Определим R как среднее между r1, r2 и r3: r1 + r2 + r3 R = ────────────── 3 R (как и r1, r2, r3) может принимать значения в диапазоне от 0 до 1, причем 0 соответствует полному совпадению сравниваемых изображений, а 1 - максимальному различию. ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 08 .C ══════════════════ Приложение 1 ──────────── Здесь я расскажу о том, как определял R - различие между двумя изображениями. Вычислим среднеквадратичное отклонение: h-1 l-1 ┌── ┌── 2 > > (p1(x,y)-p2(x,y)) └── └── y=0 x=0 r1 = ───────────────────────── l*h где h - высота сравниваемых изображений, l - ширина, p1(x,y) - яркость пиксела с координатами (x,y) в первом изображении, приведенная к диапазону 0-1 (если, например, в изображении 256 градаций яркости, то значение пиксела надо разделить на 255), p2(x,y) - то же самое для второго изображения. Далее аналогично вычислим среднеквадратичное отклонение для всех участков 2*2 пиксела, считая яркостью участка среднюю яркость входящих в него пикселов: h-2 l-2 ┌── ┌── 2 > > (m1(x,y)-m2(x,y)) └── └── y=0 x=0 r2 = ───────────────────────── (l-1)*(h-1) 1 1 1 1 ┌── ┌── ┌── ┌── > > p1(x+i,y+j) > > p2(x+i,y+j) └── └── └── └── j=0 i=0 j=0 i=0 где m1(x,y) = ──────────────────, m2(x,y) = ──────────────────. 4 4 И для участков 4*4 пиксела: h-4 l-4 ┌── ┌── 2 > > (n1(x,y)-n2(x,y)) └── └── y=0 x=0 r3 = ───────────────────────── (l-3)*(h-3) 3 3 3 3 ┌── ┌── ┌── ┌── > > p1(x+i,y+j) > > p2(x+i,y+j) └── └── └── └── j=0 i=0 j=0 i=0 где n1(x,y) = ──────────────────, n2(x,y) = ──────────────────. 16 16 Величина r1 отражает различие с близкого расстояния (когда на сравниваемых изображениях можно различить отдельные пикселы). Величина r2 - различие со среднего расстояния (когда нельзя различить деталей меньше чем 2*2 пиксела). Hаконец, величина r3 - различие с дальнего расстояния (когда уже не видно деталей меньше чем 4*4 пиксела). Определим R как среднее между r1, r2 и r3: r1 + r2 + r3 R = ────────────── 3 R (как и r1, r2, r3) может принимать значения в диапазоне от 0 до 1, причем 0 соответствует полному совпадению сравниваемых изображений, а 1 - максимальному различию. ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 09 .C ══════════════════ Приложение 2 ──────────── Здесь речь пойдет о том, как определить величины a0-a7 (яркость цветов 0-7 при bright=0) и b0-b7 (яркость тех же цветов при bright=1), когда изображение выводится в черно-белом виде. Очевидно, эти величины могут быть различны для разных моделей ZX Spectrum, для разных преобразователей цветного сигнала в черно-белый, а также для разных мониторов и телевизоров. Поэтому вместо того, чтобы привести готовый набор чисел, я расскажу о том, как вы сможете определить их именно для своей системы "компьютер-монитор", причем для этого вам не понадобятся какие-либо специальные приборы. Что можно сказать сразу? 1. Самый темный цвет - черный: a0 = 0, b0 = 0. 2. Самый яркий цвет - белый при bright=1: b7 = 255. 3. Яркость всех остальных цветов принимает промежуточные значения: 0 < (а1-а7, b1-b6) < 255. 4. С увеличением номера цвета яркость возрастает: a1 > a0 b1 > b0 a2 > a1 b2 > b1 ....... ....... a7 > a6 b7 > b6. 5. Для каждого цвета, кроме черного, яркость при bright=1 больше, чем при bright=0: b1 > a1 b2 > a2 ....... b7 > a7. Итак, кое-что мы уже знаем. Основываясь на этой информации, можно предположить, что распределение яркостей будет примерно таким: ┌─────────────────────────┐ │ bw_s_18.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 18 Выше мы говорили "яркость", а каков физический смысл этого понятия? Определим его так: яркость некоторого цвета (точнее, яркость градации, соответствующей этому цвету при черно-белом режиме отображения) - это величина, пропорциональная количеству фотонов, испускаемому единичной областью данного цвета за единицу времени. Hапример, если яркость одного цвета вдвое больше яркости другого, это значит, что некоторая область этого цвета испускает за единицу времени вдвое больше фотонов, чем такая же область другого цвета. Если вывести в одной половине экрана какой-либо цвет (далее - исходный цвет), а в другой половине - смесь пикселов двух цветов, темнее и светлее исходного (далее - соответственно темный цвет и светлый цвет), то, подбирая соотношение числа пикселов этих двух цветов, можно добиться равенства яркостей обеих половин экрана. Равенство яркостей означает, что обе половины экрана за единицу времени испускают одинаковое количество фотонов. Тогда справедлива следующая формула: x = k*y + (1-k)*z (1) где x - яркость исходного цвета, у - яркость темного цвета, z - яркость светлого цвета, k - доля пикселов темного цвета, 1-k - доля пикселов светлого цвета. Hапример, если яркости уравнялись при заполнении второй половины экрана текстурой 4*4, в которой 3 пиксела темного цвета и 13 пикселов светлого цвета, это будет записано так: x = 3/16*y + 13/16*z. Используем вышеизложенное для вычисления искомых яркостей a0-a7 и b0-b7. Сначала выразим яркость каждого из цветов b1-b6 через яркость соседних с ним цветов: b1 = k1*b0 + (1-k1)*b2 (2) b2 = k2*b1 + (1-k2)*b3 ...................... b6 = k6*b5 + (1-k6)*b7. Из (2) следует: b1 - k1*b0 b2 = ────────── 1 - k1 И далее: b2 - k2*b1 b3 = ────────── 1 - k2 b3 - k3*b2 b4 = ────────── 1 - k3 ............... b6 - k6*b5 b7 = ────────── 1 - k6 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 09 .C ══════════════════ Приложение 2 ──────────── Здесь речь пойдет о том, как определить величины a0-a7 (яркость цветов 0-7 при bright=0) и b0-b7 (яркость тех же цветов при bright=1), когда изображение выводится в черно-белом виде. Очевидно, эти величины могут быть различны для разных моделей ZX Spectrum, для разных преобразователей цветного сигнала в черно-белый, а также для разных мониторов и телевизоров. Поэтому вместо того, чтобы привести готовый набор чисел, я расскажу о том, как вы сможете определить их именно для своей системы "компьютер-монитор", причем для этого вам не понадобятся какие-либо специальные приборы. Что можно сказать сразу? 1. Самый темный цвет - черный: a0 = 0, b0 = 0. 2. Самый яркий цвет - белый при bright=1: b7 = 255. 3. Яркость всех остальных цветов принимает промежуточные значения: 0 < (а1-а7, b1-b6) < 255. 4. С увеличением номера цвета яркость возрастает: a1 > a0 b1 > b0 a2 > a1 b2 > b1 ....... ....... a7 > a6 b7 > b6. 5. Для каждого цвета, кроме черного, яркость при bright=1 больше, чем при bright=0: b1 > a1 b2 > a2 ....... b7 > a7. Итак, кое-что мы уже знаем. Основываясь на этой информации, можно предположить, что распределение яркостей будет примерно таким: ┌─────────────────────────┐ │ bw_s_18.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 18 Выше мы говорили "яркость", а каков физический смысл этого понятия? Определим его так: яркость некоторого цвета (точнее, яркость градации, соответствующей этому цвету при черно-белом режиме отображения) - это величина, пропорциональная количеству фотонов, испускаемому единичной областью данного цвета за единицу времени. Например, если яркость одного цвета вдвое больше яркости другого, это значит, что некоторая область этого цвета испускает за единицу времени вдвое больше фотонов, чем такая же область другого цвета. Если вывести в одной половине экрана какой-либо цвет (далее - исходный цвет), а в другой половине - смесь пикселов двух цветов, темнее и светлее исходного (далее - соответственно темный цвет и светлый цвет), то, подбирая соотношение числа пикселов этих двух цветов, можно добиться равенства яркостей обеих половин экрана. Равенство яркостей означает, что обе половины экрана за единицу времени испускают одинаковое количество фотонов. Тогда справедлива следующая формула: x = k*y + (1-k)*z (1) где x - яркость исходного цвета, у - яркость темного цвета, z - яркость светлого цвета, k - доля пикселов темного цвета, 1-k - доля пикселов светлого цвета. Например, если яркости уравнялись при заполнении второй половины экрана текстурой 4*4, в которой 3 пиксела темного цвета и 13 пикселов светлого цвета, это будет записано так: x = 3/16*y + 13/16*z. Используем вышеизложенное для вычисления искомых яркостей a0-a7 и b0-b7. Сначала выразим яркость каждого из цветов b1-b6 через яркость соседних с ним цветов: b1 = k1*b0 + (1-k1)*b2 (2) b2 = k2*b1 + (1-k2)*b3 ...................... b6 = k6*b5 + (1-k6)*b7. Из (2) следует: b1 - k1*b0 b2 = ────────── 1 - k1 И далее: b2 - k2*b1 b3 = ────────── 1 - k2 b3 - k3*b2 b4 = ────────── 1 - k3 ............... b6 - k6*b5 b7 = ────────── 1 - k6 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 10 .C ══════════════════ Таким образом, зная b0 и b1, а также k1-k6 (находимые опытным путем), можно последовательно вычислить b2-b7. Значение b0 нам известно - это 0. b1 неизвестно, зато мы знаем, что b7 = 255. Можно выразить b7 через b1, затем определить b1 и далее вычислить b2-b6. Hо этот путь связан с громоздкими преобразованиями. Поступим проще. Заметим, что b2-b7 линейно зависят от b1. В этом нетрудно убедиться из вышеприведенных формул (не забывая, что b0=0). Примем b1=1 и вычислим b2-b7. Тогда достаточно определить, во сколько раз получившееся значение b7 оказалось меньше 255, и затем умножить каждое из вычисленных значений b1-b7 на эту величину, чтобы получить их истинные значения. После того, как яркости цветов с bright=1 известны, остается определить яркости цветов с bright=0 (a1-a7). Для этого достаточно найти опытным путем для каждого из этих цветов эквивалентную по яркости смесь двух цветов с bright=1, после чего воспользоваться формулой (1). Это была теория, а теперь перейдем к практике. Рассмотрим, как я определял уровни яркости на своем компьютере и что для этого использовал. Сначала я написал процедуру, которая заполняет верхнюю часть экрана одним цветом, а нижнюю - смесью пикселов двух других цветов (с помощью текстуры 4*4). Вот она: LD H,1 ;исходный цвет LD D,0 ;темный цвет LD E,2 ;светлый цвет LD L,4 ;номер текстуры (0-16) ;Заполняем верхнюю половину экрана: LD A,H ADD A,A ADD A,A ADD A,A ADD A,H ;Если исходный цвет - с повышенной яркостью (bright=1), то ;следует команда ADD A,#40, иначе она не нужна (можно заменить ;на ADD A,0): ADD A,#40 EXX LD HL,#5800 LD DE,#5801 LD BC,#180 LD (HL),A LDIR EXX ;Заполняем нижнюю половину экрана: LD A,D ADD A,A ADD A,A ADD A,A ADD A,E ADD A,#40 EXX LD BC,#17F LD (HL),A LDIR EXX ;Заполнение текстурой: LD H,0 ADD HL,HL ADD HL,HL ;*4 LD DE,TEXTURES ADD HL,DE EX DE,HL ;DE указывает на первый байт текстуры. LD B,24 LD HL,#4880 M2 PUSH DE PUSH BC LD B,4 M1 LD A,(DE) PUSH BC PUSH DE PUSH HL LD D,H LD E,L LD (HL),A INC DE LD BC,#1F LDIR POP HL POP DE POP BC CALL DOWN_HL INC DE DJNZ M1 POP BC POP DE DJNZ M2 RET DOWN_HL INC H LD A,H AND 7 RET NZ LD A,L ADD A,#20 LD L,A RET C LD A,H SUB 8 LD H,A RET TEXTURES DB %00000000 DB %00000000 DB %00000000 DB %00000000 DB %00000000 DB %01000100 DB %00000000 DB %00000000 DB %00000000 DB %01000100 DB %00000000 DB %00010001 DB %00000000 DB %01010101 DB %00000000 DB %00010001 DB %00000000 DB %01010101 DB %00000000 DB %01010101 DB %00000000 DB %01010101 DB %00100010 DB %01010101 DB %10001000 DB %01010101 DB %00100010 DB %01010101 DB %10101010 DB %01010101 DB %00100010 DB %01010101 DB %10101010 DB %01010101 DB %10101010 DB %01010101 DB %10101010 DB %01110111 DB %10101010 DB %01010101 DB %10101010 DB %01110111 DB %10101010 DB %11011101 DB %10101010 DB %01110111 DB %10101010 DB %11111111 DB %10101010 DB %11111111 DB %10101010 DB %11111111 DB %10101010 DB %11111111 DB %11101110 DB %11111111 DB %10111011 DB %11111111 DB %11101110 DB %11111111 DB %11111111 DB %11111111 DB %11101110 DB %11111111 DB %11111111 DB %11111111 DB %11111111 DB %11111111 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 10 .C ══════════════════ Таким образом, зная b0 и b1, а также k1-k6 (находимые опытным путем), можно последовательно вычислить b2-b7. Значение b0 нам известно - это 0. b1 неизвестно, зато мы знаем, что b7 = 255. Можно выразить b7 через b1, затем определить b1 и далее вычислить b2-b6. Но этот путь связан с громоздкими преобразованиями. Поступим проще. Заметим, что b2-b7 линейно зависят от b1. В этом нетрудно убедиться из вышеприведенных формул (не забывая, что b0=0). Примем b1=1 и вычислим b2-b7. Тогда достаточно определить, во сколько раз получившееся значение b7 оказалось меньше 255, и затем умножить каждое из вычисленных значений b1-b7 на эту величину, чтобы получить их истинные значения. После того, как яркости цветов с bright=1 известны, остается определить яркости цветов с bright=0 (a1-a7). Для этого достаточно найти опытным путем для каждого из этих цветов эквивалентную по яркости смесь двух цветов с bright=1, после чего воспользоваться формулой (1). Это была теория, а теперь перейдем к практике. Рассмотрим, как я определял уровни яркости на своем компьютере и что для этого использовал. Сначала я написал процедуру, которая заполняет верхнюю часть экрана одним цветом, а нижнюю - смесью пикселов двух других цветов (с помощью текстуры 4*4). Вот она: LD H,1 ;исходный цвет LD D,0 ;темный цвет LD E,2 ;светлый цвет LD L,4 ;номер текстуры (0-16) ;Заполняем верхнюю половину экрана: LD A,H ADD A,A ADD A,A ADD A,A ADD A,H ;Если исходный цвет - с повышенной яркостью (bright=1), то ;следует команда ADD A,#40, иначе она не нужна (можно заменить ;на ADD A,0): ADD A,#40 EXX LD HL,#5800 LD DE,#5801 LD BC,#180 LD (HL),A LDIR EXX ;Заполняем нижнюю половину экрана: LD A,D ADD A,A ADD A,A ADD A,A ADD A,E ADD A,#40 EXX LD BC,#17F LD (HL),A LDIR EXX ;Заполнение текстурой: LD H,0 ADD HL,HL ADD HL,HL ;*4 LD DE,TEXTURES ADD HL,DE EX DE,HL ;DE указывает на первый байт текстуры. LD B,24 LD HL,#4880 M2 PUSH DE PUSH BC LD B,4 M1 LD A,(DE) PUSH BC PUSH DE PUSH HL LD D,H LD E,L LD (HL),A INC DE LD BC,#1F LDIR POP HL POP DE POP BC CALL DOWN_HL INC DE DJNZ M1 POP BC POP DE DJNZ M2 RET DOWN_HL INC H LD A,H AND 7 RET NZ LD A,L ADD A,#20 LD L,A RET C LD A,H SUB 8 LD H,A RET TEXTURES DB %00000000 DB %00000000 DB %00000000 DB %00000000 DB %00000000 DB %01000100 DB %00000000 DB %00000000 DB %00000000 DB %01000100 DB %00000000 DB %00010001 DB %00000000 DB %01010101 DB %00000000 DB %00010001 DB %00000000 DB %01010101 DB %00000000 DB %01010101 DB %00000000 DB %01010101 DB %00100010 DB %01010101 DB %10001000 DB %01010101 DB %00100010 DB %01010101 DB %10101010 DB %01010101 DB %00100010 DB %01010101 DB %10101010 DB %01010101 DB %10101010 DB %01010101 DB %10101010 DB %01110111 DB %10101010 DB %01010101 DB %10101010 DB %01110111 DB %10101010 DB %11011101 DB %10101010 DB %01110111 DB %10101010 DB %11111111 DB %10101010 DB %11111111 DB %10101010 DB %11111111 DB %10101010 DB %11111111 DB %11101110 DB %11111111 DB %10111011 DB %11111111 DB %11101110 DB %11111111 DB %11111111 DB %11111111 DB %11101110 DB %11111111 DB %11111111 DB %11111111 DB %11111111 DB %11111111 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 11 .C ══════════════════ Чтобы было удобнее сравнивать яркости половин экрана, когда на одной половине яркость равномерная, а на другой - текстура, я рассматривал изображение через полупрозрачную полиэтиленовую пленку (для этого годится обычный пакет). С помощью вышеприведенной процедуры были получены следующие соотношения: b1 = 9/16 b0 + 7/16 b2 b2 = 10/16 b1 + 6/16 b3 b3 = 8/16 b2 + 8/16 b4 b4 = 13/16 b3 + 3/16 b5 b5 = 9/16 b4 + 7/16 b6 b6 = 10/16 b5 + 6/16 b7 (т.е. k1=9/16, k2=10/16, k3=8/16, k4=13/16, k5=9/16, k6=10/16) a1 = 3/16 b0 + 13/16 b1 a2 = 9/16 b1 + 7/16 b2 a3 = 9/16 b2 + 7/16 b3 a4 = b3 a5 = 9/16 b4 + 7/16 b5 a6 = 11/16 b5 + 5/16 b6 a7 = 11/16 b6 + 5/16 b7 Затем я написал программу, вычисляющую на основе этих данных искомые значения a0-a7 и b0-b7: 10 DIM k(6): DIM a(7): DIM b(7) 20 LET k(1)=9/16: LET k(2)=10/16: LET k(3)=8/16: LET k(4)= 13/16: LET k(5)=9/16: LET k(6)=10/16 30 REM CALCULATE b(1)-b(7) 40 LET b(1)=1 50 LET b(2)=1/(1-k(1)) 60 FOR i=3 TO 7 70 LET b(i)=(b(i-1)-k(i-1)*b(i-2))/(1-k(i-1)) 80 NEXT i 90 LET n=255/b(7) 100 FOR i=1 TO 7 110 LET b(i)=b(i)*n 120 NEXT i 130 REM CALCULATE a(1)-a(7) 140 LET a(1)=13/16*b(1) 150 LET a(2)=9/16*b(1)+7/16*b(2) 160 LET a(3)=9/16*b(2)+7/16*b(3) 170 LET a(4)=b(3) 180 LET a(5)=9/16*b(4)+7/16*b(5) 190 LET a(6)=11/16*b(5)+5/16*b(6) 200 LET a(7)=11/16*b(6)+5/16*b(7) 210 REM PRINT RESULT 220 PRINT "a0=0",0 230 FOR i=1 TO 7 240 PRINT "a";i;"=";a(i), INT (a(i)*256+0.5) 250 NEXT i 260 PRINT 270 PRINT "b0=0",0 280 FOR i=1 TO 7 290 PRINT "b";i;"=";b(i), INT (b(i)*256+0.5) 300 NEXT i Результаты работы программы: в левом столбце - вычисленные значения яркостей, в правом - они же, но в 256-х долях - в таком формате они хранятся в процедурах вывода изображения (см. Приложение 3). а0 = 0 0 а1 = 4,344111 1112 а2 = 8,3540597 2139 а3 = 17,233232 4412 а4 = 23,677792 6062 а5 = 56,855343 14555 а6 = 104,72922 26811 а7 = 181,85936 46556 b0 = 0 0 b1 = 5,3465982 1369 b2 = 12,220796 3129 b3 = 23,677792 6062 b4 = 35,134788 8995 b5 = 84,781772 21704 b6 = 148,61361 38045 b7 = 255 65280 А вот как это выглядит на графике: ┌─────────────────────────┐ │ bw_s_19.pcx │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ Рис. 19 Как видите, это отличается от первоначальных предположений (рис. 18). Значения располагаются неравномерно: чем меньше яркость, тем ближе они друг к другу. Чем это можно объяснить? Очевидно, при преобразовании цветного изображения в черно-белое стремятся к тому, чтобы различные цвета превращались в хорошо отличающиеся друг от друга градации серого. А для этого яркости соседних градаций должны отличаться друг от друга не на одну и ту же величину, а в одно и то же количество раз - таковы особенности человеческого зрения. ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 12 .C ══════════════════ Приложение 3 ──────────── Здесь приведены тексты процедур для вывода изображений шестью различными способами, рассмотренными в статье. Исходное изображение (256*192, с 256 градациями яркости) должно быть предварительно помещено в три банка ОЗУ, номера которых (точнее, числа, выводимые в порт #7FFD для их подключения) указываются в массиве N_BANK (по умолчанию там #10, #11 и #13). Информация о пикселах хранится "слева направо, сверху вниз": в первом банке - верхняя треть изображения, во втором - средняя, в третьем - нижняя. Перед использованием процедур вы должны определить яркости градаций для вашей системы "компьютер-монитор" (см. Приложение 2) и поместить полученные значения в массивы BRIGHT_0 и BRIGHT_1. По умолчанию там находятся значения для моей системы, которые не будут в точности совпадать с вашими. Можно, конечно, оставить и их, но качество вывода будет хуже. В процедурах, использующих multicolor, все задержки рассчитаны на "Пентагон". Для другой модели компьютера их, скорее всего, придется изменить. 1. Процедура для вывода изображения в черно-белом режиме с 15 градациями яркости (как на рис. 5). Формирует изображение в экранной области. ORG #6000 CALL CLS_1 ;Hачало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные знакоместа: LD B,8 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,64 LD C,9 CALL WORK_DATA ;Определяем адрес атрибута для текущего ;знакоместа и помещаем туда значение: POP DE CALL GET_A_ATR LD (HL),A ;Помещаем информацию о пикселах ;текущего знакоместа: LD B,8 LD HL,PIX_DST MAIN_1 PUSH BC PUSH DE LD B,8 MAIN_2 PUSH BC LD C,(HL) INC HL LD B,(HL) INC HL CALL SET_TP_1 INC D POP BC DJNZ MAIN_2 POP DE INC E POP BC DJNZ MAIN_1 ;Переходим к следующему знакоместу: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN LD A,E ADD A,8 LD E,A CP 192 JR NZ,MAIN ;Конец главного цикла. RET PIX_SRC DS 8*8 PIX_DST DS 8*8*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 12 .C ══════════════════ Приложение 3 ──────────── Здесь приведены тексты процедур для вывода изображений шестью различными способами, рассмотренными в статье. Исходное изображение (256*192, с 256 градациями яркости) должно быть предварительно помещено в три банка ОЗУ, номера которых (точнее, числа, выводимые в порт #7FFD для их подключения) указываются в массиве N_BANK (по умолчанию там #10, #11 и #13). Информация о пикселах хранится "слева направо, сверху вниз": в первом банке - верхняя треть изображения, во втором - средняя, в третьем - нижняя. Перед использованием процедур вы должны определить яркости градаций для вашей системы "компьютер-монитор" (см. Приложение 2) и поместить полученные значения в массивы BRIGHT_0 и BRIGHT_1. По умолчанию там находятся значения для моей системы, которые не будут в точности совпадать с вашими. Можно, конечно, оставить и их, но качество вывода будет хуже. В процедурах, использующих multicolor, все задержки рассчитаны на "Пентагон". Для другой модели компьютера их, скорее всего, придется изменить. 1. Процедура для вывода изображения в черно-белом режиме с 15 градациями яркости (как на рис. 5). Формирует изображение в экранной области. ORG #6000 CALL CLS_1 ;Начало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные знакоместа: LD B,8 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,64 LD C,9 CALL WORK_DATA ;Определяем адрес атрибута для текущего ;знакоместа и помещаем туда значение: POP DE CALL GET_A_ATR LD (HL),A ;Помещаем информацию о пикселах ;текущего знакоместа: LD B,8 LD HL,PIX_DST MAIN_1 PUSH BC PUSH DE LD B,8 MAIN_2 PUSH BC LD C,(HL) INC HL LD B,(HL) INC HL CALL SET_TP_1 INC D POP BC DJNZ MAIN_2 POP DE INC E POP BC DJNZ MAIN_1 ;Переходим к следующему знакоместу: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN LD A,E ADD A,8 LD E,A CP 192 JR NZ,MAIN ;Конец главного цикла. RET PIX_SRC DS 8*8 PIX_DST DS 8*8*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 13 .C ══════════════════ 2. Процедура для вывода изображения с удвоенным атрибутным разрешением за счет использования multicolor'а (как на рис. 6). Формирует верхние половины знакомест со своими атрибутами на первом экране, а нижние половины со своими атрибутами - на втором. Затем, когда луч прорисовывает верхние половины знакомест, активным устанавливается первый экран, а когда нижние половины - второй. Выход из режима просмотра - по нажатию любой клавиши. ORG #6000 CALL CLS_1 CALL CLS_2 ;Hачало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные области 8*4: LD B,4 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,32 LD C,9 CALL WORK_DATA ;В зависимости от того, верхняя или ;нижняя половина знакоместа обрабатыва- ;ется, результат нужно помещать на ;первый или на второй экран. Устанавли- ;ваем адрес соответствующей процедуры: POP DE LD HL,SET_TP_1 BIT 2,E JR Z,MAIN_A LD HL,SET_TP_2 MAIN_A LD (ADR_CALL),HL ;Помещаем атрибут: CALL GET_A_ATR BIT 2,E JR Z,MAIN_B SET 7,H EX AF,AF' LD A,#17 CALL SETPORT EX AF,AF' MAIN_B LD (HL),A ;Помещаем пикселы: LD B,4 LD HL,PIX_DST MAIN_1 PUSH BC PUSH DE LD B,8 MAIN_2 PUSH BC LD C,(HL) INC HL LD B,(HL) INC HL CALL SET_TP_1 ;или SET_TP_2 ADR_CALL EQU $-2 INC D POP BC DJNZ MAIN_2 POP DE INC E POP BC DJNZ MAIN_1 ;Переходим к следующей области: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN LD A,E ADD A,4 LD E,A CP 192 JR NZ,MAIN ;Конец главного цикла. ;Вывод изображения: через каждые 4 ;строки меняем активный экран. CALL ON_IM2 VIEW_LOOP HALT LD HL,17762 CALL WAIT LD B,24 VIEW_1 LD A,#10 CALL SETPORT EXX LD HL,814 CALL WAIT EXX LD A,#18 CALL SETPORT EXX LD HL,795 CALL WAIT EXX DJNZ VIEW_1 ;Опрос клавиатуры: XOR A IN A,(254) CPL AND #1F JR Z,VIEW_LOOP ;Выход при нажатии любой клавиши: CALL OFF_IM2 RET PIX_SRC DS 8*4 PIX_DST DS 8*4*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 14 .C ══════════════════ 3. Процедура для вывода изображения с максимальным атрибутным разрешением (как на рис. 7). При формировании изображения информация о пикселах записывается в экранную область, а информация об атрибутах (#1800 байтов - свой атрибут для каждой линии знакоместа) записывается в массив ATR (по умолчанию расположенный с адреса #8000). Затем в область атрибутов помещаются атрибуты для первой строки; пока луч прорисовывает первую строку, в область атрибутов помещаются атрибуты второй строки, и так далее. Так как за 224 такта (время прорисовки одной строки) не получается перебросить 32 байта атрибутов, процедура выводит изображение не целиком, а в окне размером 11 знакомест (88 пикселов) по горизонтали. Клавишами "O", "P" окно можно перемещать по изображению влево/вправо. Выход из режима просмотра - по нажатию пробела. ATR EQU #8000 ;Атрибуты (#1800). LINES EQU #7200 ;Адреса атрибутов ;на экране (#180). ORG #6000 LD HL,ATR LD (ATR_ADR),HL CALL CLS_1 ;Устанавливаем PAPER 0: INK 7, чтобы ;можно было видеть процесс построения ;изображения: LD HL,#5800 LD (HL),7 LD DE,#5801 LD BC,#2FF LDIR ;Hачало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные области 8*1: LD B,1 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,8 LD C,9 CALL WORK_DATA ;Помещаем атрибут: LD HL,0 ATR_ADR EQU $-2 LD (HL),A INC HL LD (ATR_ADR),HL ;Помещаем пикселы: POP DE LD HL,PIX_DST LD B,8 MAIN_1 PUSH BC LD C,(HL) INC HL LD B,(HL) INC HL CALL SET_TP_1 INC D POP BC DJNZ MAIN_1 ;Переходим к следующей области: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN INC E LD A,E CP 192 JR NZ,MAIN ;Конец главного цикла. ;Вывод изображения: CALL ON_IM2 VIEW_LOOP HALT LD (SAVE_SP),SP DI ;Очищаем область атрибутов: LD SP,#5B00 LD HL,0 LD B,48 CLS_ATR PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL DJNZ CLS_ATR ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 13 .C ══════════════════ 2. Процедура для вывода изображения с удвоенным атрибутным разрешением за счет использования multicolor'а (как на рис. 6). Формирует верхние половины знакомест со своими атрибутами на первом экране, а нижние половины со своими атрибутами - на втором. Затем, когда луч прорисовывает верхние половины знакомест, активным устанавливается первый экран, а когда нижние половины - второй. Выход из режима просмотра - по нажатию любой клавиши. ORG #6000 CALL CLS_1 CALL CLS_2 ;Начало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные области 8*4: LD B,4 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,32 LD C,9 CALL WORK_DATA ;В зависимости от того, верхняя или ;нижняя половина знакоместа обрабатыва- ;ется, результат нужно помещать на ;первый или на второй экран. Устанавли- ;ваем адрес соответствующей процедуры: POP DE LD HL,SET_TP_1 BIT 2,E JR Z,MAIN_A LD HL,SET_TP_2 MAIN_A LD (ADR_CALL),HL ;Помещаем атрибут: CALL GET_A_ATR BIT 2,E JR Z,MAIN_B SET 7,H EX AF,AF' LD A,#17 CALL SETPORT EX AF,AF' MAIN_B LD (HL),A ;Помещаем пикселы: LD B,4 LD HL,PIX_DST MAIN_1 PUSH BC PUSH DE LD B,8 MAIN_2 PUSH BC LD C,(HL) INC HL LD B,(HL) INC HL CALL SET_TP_1 ;или SET_TP_2 ADR_CALL EQU $-2 INC D POP BC DJNZ MAIN_2 POP DE INC E POP BC DJNZ MAIN_1 ;Переходим к следующей области: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN LD A,E ADD A,4 LD E,A CP 192 JR NZ,MAIN ;Конец главного цикла. ;Вывод изображения: через каждые 4 ;строки меняем активный экран. CALL ON_IM2 VIEW_LOOP HALT LD HL,17762 CALL WAIT LD B,24 VIEW_1 LD A,#10 CALL SETPORT EXX LD HL,814 CALL WAIT EXX LD A,#18 CALL SETPORT EXX LD HL,795 CALL WAIT EXX DJNZ VIEW_1 ;Опрос клавиатуры: XOR A IN A,(254) CPL AND #1F JR Z,VIEW_LOOP ;Выход при нажатии любой клавиши: CALL OFF_IM2 RET PIX_SRC DS 8*4 PIX_DST DS 8*4*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 14 .C ══════════════════ 3. Процедура для вывода изображения с максимальным атрибутным разрешением (как на рис. 7). При формировании изображения информация о пикселах записывается в экранную область, а информация об атрибутах (#1800 байтов - свой атрибут для каждой линии знакоместа) записывается в массив ATR (по умолчанию расположенный с адреса #8000). Затем в область атрибутов помещаются атрибуты для первой строки; пока луч прорисовывает первую строку, в область атрибутов помещаются атрибуты второй строки, и так далее. Так как за 224 такта (время прорисовки одной строки) не получается перебросить 32 байта атрибутов, процедура выводит изображение не целиком, а в окне размером 11 знакомест (88 пикселов) по горизонтали. Клавишами "O", "P" окно можно перемещать по изображению влево/вправо. Выход из режима просмотра - по нажатию пробела. ATR EQU #8000 ;Атрибуты (#1800). LINES EQU #7200 ;Адреса атрибутов ;на экране (#180). ORG #6000 LD HL,ATR LD (ATR_ADR),HL CALL CLS_1 ;Устанавливаем PAPER 0: INK 7, чтобы ;можно было видеть процесс построения ;изображения: LD HL,#5800 LD (HL),7 LD DE,#5801 LD BC,#2FF LDIR ;Начало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные области 8*1: LD B,1 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,8 LD C,9 CALL WORK_DATA ;Помещаем атрибут: LD HL,0 ATR_ADR EQU $-2 LD (HL),A INC HL LD (ATR_ADR),HL ;Помещаем пикселы: POP DE LD HL,PIX_DST LD B,8 MAIN_1 PUSH BC LD C,(HL) INC HL LD B,(HL) INC HL CALL SET_TP_1 INC D POP BC DJNZ MAIN_1 ;Переходим к следующей области: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN INC E LD A,E CP 192 JR NZ,MAIN ;Конец главного цикла. ;Вывод изображения: CALL ON_IM2 VIEW_LOOP HALT LD (SAVE_SP),SP DI ;Очищаем область атрибутов: LD SP,#5B00 LD HL,0 LD B,48 CLS_ATR PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL DJNZ CLS_ATR ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 15 .C ══════════════════ ;Создаем таблицу адресов атрибутов: MAKE_LT LD HL,#5AE0 LD DE,0 X EQU $-2 ADD HL,DE LD SP,LINES+#180 LD DE,-#20 LD B,24 MAKE_LT_1 PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL ADD HL,DE DJNZ MAKE_LT_1 ;Чем больше X, тем больше должна быть ;задержка: X больше на 1 - задержка ;больше на 4 такта. LD HL,(X) ADD HL,HL ADD HL,HL LD DE,9987 ADD HL,DE CALL WAIT ;Выводим свои атрибуты в каждой строке: LD A,192 LD HL,ATR X_2 EQU $-2 LD SP,LINES LD B,0 ;Время выполнения нижеследующего ;участка - 224 такта, что равно времени ;прорисовки одной строки изображения. ONE_LINE LD C,32 ;7 POP DE ;10 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 ADD HL,BC ;11 INC BC ;6 (WAIT) DEC A ;4 JP NZ,ONE_LINE ;10 LD SP,0 SAVE_SP EQU $-2 EI ;Опрос клавиатуры: LD A,#DF IN A,(254) RRA JR C,NE_P ;Hажата клавиша "P" - сдвиг видимого ;участка вправо: LD A,(X) CP 21 JR NEW_X NE_P RRA JR C,NE_O ;Hажата клавиша "O" - сдвиг видимого ;участка влево: LD A,(X) SUB 1 NEW_X ADC A,0 LD (X),A LD (X_2),A JP VIEW_LOOP ;Если нажат пробел - выходим: NE_O LD A,#7F IN A,(254) RRA JP C,VIEW_LOOP CALL OFF_IM2 RET PIX_SRC DS 8 PIX_DST DS 8*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 15 .C ══════════════════ ;Создаем таблицу адресов атрибутов: MAKE_LT LD HL,#5AE0 LD DE,0 X EQU $-2 ADD HL,DE LD SP,LINES+#180 LD DE,-#20 LD B,24 MAKE_LT_1 PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL ADD HL,DE DJNZ MAKE_LT_1 ;Чем больше X, тем больше должна быть ;задержка: X больше на 1 - задержка ;больше на 4 такта. LD HL,(X) ADD HL,HL ADD HL,HL LD DE,9987 ADD HL,DE CALL WAIT ;Выводим свои атрибуты в каждой строке: LD A,192 LD HL,ATR X_2 EQU $-2 LD SP,LINES LD B,0 ;Время выполнения нижеследующего ;участка - 224 такта, что равно времени ;прорисовки одной строки изображения. ONE_LINE LD C,32 ;7 POP DE ;10 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 ADD HL,BC ;11 INC BC ;6 (WAIT) DEC A ;4 JP NZ,ONE_LINE ;10 LD SP,0 SAVE_SP EQU $-2 EI ;Опрос клавиатуры: LD A,#DF IN A,(254) RRA JR C,NE_P ;Нажата клавиша "P" - сдвиг видимого ;участка вправо: LD A,(X) CP 21 JR NEW_X NE_P RRA JR C,NE_O ;Нажата клавиша "O" - сдвиг видимого ;участка влево: LD A,(X) SUB 1 NEW_X ADC A,0 LD (X),A LD (X_2),A JP VIEW_LOOP ;Если нажат пробел - выходим: NE_O LD A,#7F IN A,(254) RRA JP C,VIEW_LOOP CALL OFF_IM2 RET PIX_SRC DS 8 PIX_DST DS 8*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 16 .C ══════════════════ 4. Процедура для вывода изображения с повышенным качеством за счет быстрого чередования двух изображений (как на рис. 9). Формирует одно изображение на первом экране, другое - на втором, и далее в каждом кадре меняет активный экран. Выход из режима просмотра - по нажатию любой клавиши. ORG #6000 CALL CLS_1 CALL CLS_2 ;Hачало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные знакоместа: LD B,8 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,64 LD C,10 CALL WORK_DATA ;Записываем атрибут: POP DE CALL GET_A_ATR LD (HL),A ;Выводим пикселы: LD B,8 LD HL,PIX_DST CALL PUT_DATA ;Переходим к следующему знакоместу: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN LD A,E ADD A,8 LD E,A CP 192 JR NZ,MAIN ;Конец главного цикла. CALL A_FLICKER ;Копируем атрибуты на второй экран: LD A,#17 CALL SETPORT LD HL,#5800 LD DE,#D800 LD BC,#300 LDIR ;Переключаем экраны каждые 1/50 секунды: LD A,#10 OUTPUT HALT XOR 8 CALL SETPORT LD B,A XOR A IN A,(254) CPL AND #1F LD A,B JR Z,OUTPUT RET PIX_SRC DS 8*8 PIX_DST DS 8*8*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 16 .C ══════════════════ 4. Процедура для вывода изображения с повышенным качеством за счет быстрого чередования двух изображений (как на рис. 9). Формирует одно изображение на первом экране, другое - на втором, и далее в каждом кадре меняет активный экран. Выход из режима просмотра - по нажатию любой клавиши. ORG #6000 CALL CLS_1 CALL CLS_2 ;Начало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные знакоместа: LD B,8 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,64 LD C,10 CALL WORK_DATA ;Записываем атрибут: POP DE CALL GET_A_ATR LD (HL),A ;Выводим пикселы: LD B,8 LD HL,PIX_DST CALL PUT_DATA ;Переходим к следующему знакоместу: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN LD A,E ADD A,8 LD E,A CP 192 JR NZ,MAIN ;Конец главного цикла. CALL A_FLICKER ;Копируем атрибуты на второй экран: LD A,#17 CALL SETPORT LD HL,#5800 LD DE,#D800 LD BC,#300 LDIR ;Переключаем экраны каждые 1/50 секунды: LD A,#10 OUTPUT HALT XOR 8 CALL SETPORT LD B,A XOR A IN A,(254) CPL AND #1F LD A,B JR Z,OUTPUT RET PIX_SRC DS 8*8 PIX_DST DS 8*8*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 17 .C ══════════════════ 5. Процедура для вывода изображения с быстрым чередованием и удвоенным атрибутным разрешением (как на рис. 12). При формировании изображения записывает информацию о пикселах первого изображения на первый экран, а второго изображения - на второй экран. Атрибуты (#600 байтов) записываются в массив ATR. Далее, в первом кадре, когда луч прорисовывает верхние половины знакомест, активным устанавливается первый экран, а когда нижние половины - второй. В следующем кадре, наоборот, верхние половины знакомест берутся со второго экрана, а нижние - с первого, и так далее. Пока луч прорисовывает верхние половины знакомест, на другом экране записываются атрибуты для нижних половин знакомест, а когда луч прорисовывает нижние половины, на другом экране записываются атрибуты для верхих половин следующей строки знакомест, и так далее. Выход из режима просмотра - по нажатию любой клавиши. ATR EQU #8000 ;Атрибуты (#600) ORG #6000 LD HL,ATR LD (ATR_ADR),HL LD A,#D8 LD (ATR_SCR),A CALL CLS_1 CALL CLS_2 ;Устанавливаем PAPER 0: INK 7, чтобы ;можно было видеть процесс построения ;изображения: LD HL,#5800 LD (HL),7 LD DE,#5801 LD BC,#2FF LDIR ;Hачало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные половины ;знакоместа: LD B,4 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,32 LD C,10 CALL WORK_DATA ;Записываем атрибут: LD HL,0 ATR_ADR EQU $-2 LD (HL),A INC HL LD (ATR_ADR),HL ;Выводим пикселы: POP DE LD B,4 LD HL,PIX_DST CALL PUT_DATA ;Переходим к следующей половине ;знакоместа: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN LD A,E ADD A,4 LD E,A CP 192 JR NZ,MAIN ;Конец главного цикла. CALL A_FLICKER ;Вывод изображения: CALL ON_IM2 LD XH,#17 VIEW_LOOP HALT LD HL,16982 CALL WAIT LD XL,#18 LD HL,ATR LD DE,0 ;#5800 или #D800 ATR_SCR EQU $-1 LD B,E ;=LD B,0 ;Выводим атрибуты на один из экранов: VIEW_4PIX PUSH DE LD C,#20 LD A,D XOR #80 LD D,A VIEW_1 LDI:LDI:LDI:LDI LDI:LDI:LDI:LDI JP PE,VIEW_1 POP DE EXX LD HL,193 CALL WAIT EXX ;Делаем активным этот экран: LD A,XH CALL SETPORT ;Выводим атрибуты на другой экран: LD C,#20 VIEW_2 LDI:LDI:LDI:LDI LDI:LDI:LDI:LDI JP PE,VIEW_2 EXX LD HL,246 CALL WAIT EXX ;Делаем активным этот экран: LD A,XH XOR 8 CALL SETPORT DEC XL JR NZ,VIEW_4PIX ;В следующем кадре экраны меняются ;местами: LD A,XH XOR 8 LD XH,A LD A,(ATR_SCR) XOR #80 LD (ATR_SCR),A ;Опрос клавиатуры: XOR A IN A,(254) CPL AND #1F JR Z,VIEW_LOOP ;Выход по нажатию любой клавиши: CALL OFF_IM2 RET PIX_SRC DS 8*4 PIX_DST DS 8*4*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 17 .C ══════════════════ 5. Процедура для вывода изображения с быстрым чередованием и удвоенным атрибутным разрешением (как на рис. 12). При формировании изображения записывает информацию о пикселах первого изображения на первый экран, а второго изображения - на второй экран. Атрибуты (#600 байтов) записываются в массив ATR. Далее, в первом кадре, когда луч прорисовывает верхние половины знакомест, активным устанавливается первый экран, а когда нижние половины - второй. В следующем кадре, наоборот, верхние половины знакомест берутся со второго экрана, а нижние - с первого, и так далее. Пока луч прорисовывает верхние половины знакомест, на другом экране записываются атрибуты для нижних половин знакомест, а когда луч прорисовывает нижние половины, на другом экране записываются атрибуты для верхих половин следующей строки знакомест, и так далее. Выход из режима просмотра - по нажатию любой клавиши. ATR EQU #8000 ;Атрибуты (#600) ORG #6000 LD HL,ATR LD (ATR_ADR),HL LD A,#D8 LD (ATR_SCR),A CALL CLS_1 CALL CLS_2 ;Устанавливаем PAPER 0: INK 7, чтобы ;можно было видеть процесс построения ;изображения: LD HL,#5800 LD (HL),7 LD DE,#5801 LD BC,#2FF LDIR ;Начало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные половины ;знакоместа: LD B,4 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,32 LD C,10 CALL WORK_DATA ;Записываем атрибут: LD HL,0 ATR_ADR EQU $-2 LD (HL),A INC HL LD (ATR_ADR),HL ;Выводим пикселы: POP DE LD B,4 LD HL,PIX_DST CALL PUT_DATA ;Переходим к следующей половине ;знакоместа: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN LD A,E ADD A,4 LD E,A CP 192 JR NZ,MAIN ;Конец главного цикла. CALL A_FLICKER ;Вывод изображения: CALL ON_IM2 LD XH,#17 VIEW_LOOP HALT LD HL,16982 CALL WAIT LD XL,#18 LD HL,ATR LD DE,0 ;#5800 или #D800 ATR_SCR EQU $-1 LD B,E ;=LD B,0 ;Выводим атрибуты на один из экранов: VIEW_4PIX PUSH DE LD C,#20 LD A,D XOR #80 LD D,A VIEW_1 LDI:LDI:LDI:LDI LDI:LDI:LDI:LDI JP PE,VIEW_1 POP DE EXX LD HL,193 CALL WAIT EXX ;Делаем активным этот экран: LD A,XH CALL SETPORT ;Выводим атрибуты на другой экран: LD C,#20 VIEW_2 LDI:LDI:LDI:LDI LDI:LDI:LDI:LDI JP PE,VIEW_2 EXX LD HL,246 CALL WAIT EXX ;Делаем активным этот экран: LD A,XH XOR 8 CALL SETPORT DEC XL JR NZ,VIEW_4PIX ;В следующем кадре экраны меняются ;местами: LD A,XH XOR 8 LD XH,A LD A,(ATR_SCR) XOR #80 LD (ATR_SCR),A ;Опрос клавиатуры: XOR A IN A,(254) CPL AND #1F JR Z,VIEW_LOOP ;Выход по нажатию любой клавиши: CALL OFF_IM2 RET PIX_SRC DS 8*4 PIX_DST DS 8*4*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 18 .C ══════════════════ 6. Процедура для вывода изображения с быстрым чередованием и максимальным атрибутным разрешением (как на рис. 13). При формировании изображения записывает информацию о пикселах первого изображения на первый экран, второго изображения - на второй экран, а значения атрибутов (#1800 байтов) - в массив ATR. Затем, как и в процедуре 3, область атрибутов динамически обновляется. В каждом кадре активный экран меняется. Изображение выводится в окне размером 11 знакомест (88 пикселов) по горизонтали. Клавишами "O", "P" окно можно перемещать по изображению влево/вправо. Выход из режима просмотра - по нажатию пробела. ATR EQU #8000 ;Атрибуты (#1800). LINES EQU #7200 ;Адреса атрибутов ;на экране (#180). ORG #6000 LD HL,ATR LD (ATR_ADR),HL CALL CLS_1 CALL CLS_2 ;Устанавливаем PAPER 0: INK 7, чтобы ;можно было видеть процесс построения ;изображения: LD HL,#5800 LD (HL),7 LD DE,#5801 LD BC,#2FF LDIR ;Hачало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные линии знакоместа: LD B,1 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,8 LD C,10 CALL WORK_DATA ;Записываем атрибут: LD HL,0 ATR_ADR EQU $-2 LD (HL),A INC HL LD (ATR_ADR),HL ;Выводим пикселы: POP DE LD B,1 LD HL,PIX_DST CALL PUT_DATA ;Переходим к следующей линии знакоместа: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN INC E LD A,E CP 192 JR NZ,MAIN ;Конец главного цикла. CALL A_FLICKER ;Вывод изображения: CALL ON_IM2 VIEW_LOOP HALT LD (SAVE_SP),SP DI ;Очищаем область атрибутов: CLS_ATR LD SP,#5B00 LD HL,0 LD B,48 CLS_ATR_1 PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL DJNZ CLS_ATR_1 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 18 .C ══════════════════ 6. Процедура для вывода изображения с быстрым чередованием и максимальным атрибутным разрешением (как на рис. 13). При формировании изображения записывает информацию о пикселах первого изображения на первый экран, второго изображения - на второй экран, а значения атрибутов (#1800 байтов) - в массив ATR. Затем, как и в процедуре 3, область атрибутов динамически обновляется. В каждом кадре активный экран меняется. Изображение выводится в окне размером 11 знакомест (88 пикселов) по горизонтали. Клавишами "O", "P" окно можно перемещать по изображению влево/вправо. Выход из режима просмотра - по нажатию пробела. ATR EQU #8000 ;Атрибуты (#1800). LINES EQU #7200 ;Адреса атрибутов ;на экране (#180). ORG #6000 LD HL,ATR LD (ATR_ADR),HL CALL CLS_1 CALL CLS_2 ;Устанавливаем PAPER 0: INK 7, чтобы ;можно было видеть процесс построения ;изображения: LD HL,#5800 LD (HL),7 LD DE,#5801 LD BC,#2FF LDIR ;Начало главного цикла. LD DE,0 MAIN PUSH DE PUSH DE ;Берем исходные данные линии знакоместа: LD B,1 LD HL,PIX_SRC CALL GET_DATA ;Обрабатываем: LD HL,PIX_SRC LD DE,PIX_DST LD B,8 LD C,10 CALL WORK_DATA ;Записываем атрибут: LD HL,0 ATR_ADR EQU $-2 LD (HL),A INC HL LD (ATR_ADR),HL ;Выводим пикселы: POP DE LD B,1 LD HL,PIX_DST CALL PUT_DATA ;Переходим к следующей линии знакоместа: POP DE LD A,D ADD A,8 LD D,A JR NZ,MAIN INC E LD A,E CP 192 JR NZ,MAIN ;Конец главного цикла. CALL A_FLICKER ;Вывод изображения: CALL ON_IM2 VIEW_LOOP HALT LD (SAVE_SP),SP DI ;Очищаем область атрибутов: CLS_ATR LD SP,#5B00 LD HL,0 LD B,48 CLS_ATR_1 PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL DJNZ CLS_ATR_1 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 19 .C ══════════════════ ;Создаем таблицу адресов атрибутов: MAKE_LT LD HL,#5AE0 LD DE,0 X EQU $-2 ADD HL,DE LD SP,LINES+#180 LD DE,-#20 LD B,24 MAKE_LT_1 PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL ADD HL,DE DJNZ MAKE_LT_1 ;Чем больше X, тем больше должна быть ;задержка: X больше на 1 - задержка ;больше на 4 такта. LD HL,(X) ADD HL,HL ADD HL,HL LD DE,9987 ADD HL,DE CALL WAIT ;Выводим свои атрибуты в каждой строке: LD A,192 LD HL,ATR X_2 EQU $-2 LD SP,LINES LD B,0 ;Время выполнения нижеследующего ;участка - 224 такта, что равно времени ;прорисовки одной строки изображения. ONE_LINE LD C,32 ;7 POP DE ;10 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 ADD HL,BC ;11 INC BC ;6 (WAIT) DEC A ;4 JP NZ,ONE_LINE ;10 LD SP,0 SAVE_SP EQU $-2 EI ;Задержка, чтобы луч успел прорисовать ;последнюю строку изображения до того, ;как установим другой активный экран: LD HL,224 CALL WAIT ;Устанавливаем другой экран: LD A,#10 N_SCR EQU $-1 XOR #0F LD (N_SCR),A CALL SETPORT ;Изменяем начальный адрес в процедуре ;очистки области атрибутов и в процедуре ;построения таблицы адресов атрибутов: LD HL,CLS_ATR+2 LD A,(HL) XOR #80 LD (HL),A LD HL,MAKE_LT+2 LD A,(HL) XOR #80 LD (HL),A ;Опрос клавиатуры: LD A,#DF IN A,(254) RRA JR C,NE_P ;Hажата клавиша "P" - сдвиг видимого ;участка вправо: LD A,(X) CP 21 JR NEW_X NE_P RRA JR C,NE_O ;Hажата клавиша "O" - сдвиг видимого ;участка влево: LD A,(X) SUB 1 NEW_X ADC A,0 LD (X),A LD (X_2),A JP VIEW_LOOP ;Если нажат пробел - выходим: NE_O LD A,#7F IN A,(254) RRA JP C,VIEW_LOOP CALL OFF_IM2 RET PIX_SRC DS 8 PIX_DST DS 8*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 19 .C ══════════════════ ;Создаем таблицу адресов атрибутов: MAKE_LT LD HL,#5AE0 LD DE,0 X EQU $-2 ADD HL,DE LD SP,LINES+#180 LD DE,-#20 LD B,24 MAKE_LT_1 PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL PUSH HL ADD HL,DE DJNZ MAKE_LT_1 ;Чем больше X, тем больше должна быть ;задержка: X больше на 1 - задержка ;больше на 4 такта. LD HL,(X) ADD HL,HL ADD HL,HL LD DE,9987 ADD HL,DE CALL WAIT ;Выводим свои атрибуты в каждой строке: LD A,192 LD HL,ATR X_2 EQU $-2 LD SP,LINES LD B,0 ;Время выполнения нижеследующего ;участка - 224 такта, что равно времени ;прорисовки одной строки изображения. ONE_LINE LD C,32 ;7 POP DE ;10 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 LDI ;16 ADD HL,BC ;11 INC BC ;6 (WAIT) DEC A ;4 JP NZ,ONE_LINE ;10 LD SP,0 SAVE_SP EQU $-2 EI ;Задержка, чтобы луч успел прорисовать ;последнюю строку изображения до того, ;как установим другой активный экран: LD HL,224 CALL WAIT ;Устанавливаем другой экран: LD A,#10 N_SCR EQU $-1 XOR #0F LD (N_SCR),A CALL SETPORT ;Изменяем начальный адрес в процедуре ;очистки области атрибутов и в процедуре ;построения таблицы адресов атрибутов: LD HL,CLS_ATR+2 LD A,(HL) XOR #80 LD (HL),A LD HL,MAKE_LT+2 LD A,(HL) XOR #80 LD (HL),A ;Опрос клавиатуры: LD A,#DF IN A,(254) RRA JR C,NE_P ;Нажата клавиша "P" - сдвиг видимого ;участка вправо: LD A,(X) CP 21 JR NEW_X NE_P RRA JR C,NE_O ;Нажата клавиша "O" - сдвиг видимого ;участка влево: LD A,(X) SUB 1 NEW_X ADC A,0 LD (X),A LD (X_2),A JP VIEW_LOOP ;Если нажат пробел - выходим: NE_O LD A,#7F IN A,(254) RRA JP C,VIEW_LOOP CALL OFF_IM2 RET PIX_SRC DS 8 PIX_DST DS 8*2 <далее следует текст общей части> ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 20 .C ══════════════════ Общая часть процедур вывода ─────────────────────────── GAMMA_CORR EQU 0 ;Выполнять ;гамма-коррекцию? ;--------------------------------------- ;Hомера банков памяти, в которых ;хранится исходное изображение: N_BANK DB #10,#11,#13 ;257-байтная таблица для указания ;адреса процедуры обработки прерываний ;(начинается с адреса, кратного 256): INT_TAB EQU #7000 ;Адрес процедуры обработки прерываний ;(старший байт равен младшему): INT_ADR EQU #7171 ;Яркости градаций в формате 8.8: BRIGHT_0 DW 0,1112,2139,4412,6062 DW 14555,26811,46556 BRIGHT_1 DW 0,1369,3129,6062,8995 DW 21704,38045,65280 ;--------------------------------------- ;Процедуры очистки экрана: CLS_1 LD HL,#4000 ;1-й экран LD (HL),L LD DE,#4001 JR CLS CLS_2 LD A,#17 ;2-й экран CALL SETPORT LD HL,#C000 LD (HL),L LD DE,#C001 CLS LD BC,#1AFF LDIR RET ;--------------------------------------- ;Процедура GET_DATA - поместить в буфер ;информацию о прямоугольной области ;изображения шириной 8 пикселов. ; ;Вход: DE - координаты левого верхнего ; угла области, ; B - высота области, ; HL - адрес буфера. GET_DATA PUSH BC PUSH DE LD B,8 GET_DATA1 CALL GET_1R LD (HL),A INC HL INC D DJNZ GET_DATA1 POP DE INC E POP BC DJNZ GET_DATA RET ;--------------------------------------- ;Процедура PUT_DATA выводит пикселы ;прямоугольной области изображения на ;первый и второй экраны (для способов с ;быстрой сменой двух экранов). ; ;Вход: DE - координаты левого верхнего ; угла области, ; B - высота области, ; HL - адрес данных о пикселах. PUT_DATA PUSH BC PUSH DE LD B,8 PUT_D_1 PUSH BC LD C,(HL) INC HL LD B,(HL) INC HL ;Если BC>=256, ставим на первый экран ;включенный пиксел и уменьшаем BC на ;256: INC B ;если B=0, то BC<256 DEC B JR Z,PUT_D_2 DEC B ;т.е. BC:=BC-256 CALL SET_P_1 ;Теперь BC - номер текстуры (0-256). ;Ставим на второй экран пиксел с этой ;текстурой: PUT_D_2 CALL SET_TP_2 INC D POP BC DJNZ PUT_D_1 POP DE INC E POP BC DJNZ PUT_DATA RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 20 .C ══════════════════ Общая часть процедур вывода ─────────────────────────── GAMMA_CORR EQU 0 ;Выполнять ;гамма-коррекцию? ;--------------------------------------- ;Номера банков памяти, в которых ;хранится исходное изображение: N_BANK DB #10,#11,#13 ;257-байтная таблица для указания ;адреса процедуры обработки прерываний ;(начинается с адреса, кратного 256): INT_TAB EQU #7000 ;Адрес процедуры обработки прерываний ;(старший байт равен младшему): INT_ADR EQU #7171 ;Яркости градаций в формате 8.8: BRIGHT_0 DW 0,1112,2139,4412,6062 DW 14555,26811,46556 BRIGHT_1 DW 0,1369,3129,6062,8995 DW 21704,38045,65280 ;--------------------------------------- ;Процедуры очистки экрана: CLS_1 LD HL,#4000 ;1-й экран LD (HL),L LD DE,#4001 JR CLS CLS_2 LD A,#17 ;2-й экран CALL SETPORT LD HL,#C000 LD (HL),L LD DE,#C001 CLS LD BC,#1AFF LDIR RET ;--------------------------------------- ;Процедура GET_DATA - поместить в буфер ;информацию о прямоугольной области ;изображения шириной 8 пикселов. ; ;Вход: DE - координаты левого верхнего ; угла области, ; B - высота области, ; HL - адрес буфера. GET_DATA PUSH BC PUSH DE LD B,8 GET_DATA1 CALL GET_1R LD (HL),A INC HL INC D DJNZ GET_DATA1 POP DE INC E POP BC DJNZ GET_DATA RET ;--------------------------------------- ;Процедура PUT_DATA выводит пикселы ;прямоугольной области изображения на ;первый и второй экраны (для способов с ;быстрой сменой двух экранов). ; ;Вход: DE - координаты левого верхнего ; угла области, ; B - высота области, ; HL - адрес данных о пикселах. PUT_DATA PUSH BC PUSH DE LD B,8 PUT_D_1 PUSH BC LD C,(HL) INC HL LD B,(HL) INC HL ;Если BC>=256, ставим на первый экран ;включенный пиксел и уменьшаем BC на ;256: INC B ;если B=0, то BC<256 DEC B JR Z,PUT_D_2 DEC B ;т.е. BC:=BC-256 CALL SET_P_1 ;Теперь BC - номер текстуры (0-256). ;Ставим на второй экран пиксел с этой ;текстурой: PUT_D_2 CALL SET_TP_2 INC D POP BC DJNZ PUT_D_1 POP DE INC E POP BC DJNZ PUT_DATA RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 21 .C ══════════════════ ;--------------------------------------- ;Процедура WORK_DATA определяет по ;данным о некоторой области пикселов, ;какой атрибут использовать при ее ;отображении, и какая псевдоградация ;будет наиболее близка по яркости для ;каждого пиксела. ; ;Осуществляется гамма-коррекция по ;таблице GAMMA_T (если GAMMA_CORR=1). ; ;Вход: HL - адрес исходных данных ; (1 байт на пиксел), ; B - кол-во пикселов, ; C - количество псевдоградаций ; (257 -> C=9; 513 -> C=10), ; DE - адрес формируемых данных ; (2 байта на пиксел). ; ;Выход: A - значение атрибутов, с DE ; сформированы данные. ;Локальные переменные (8.8): MIN_PIX DS 2 ;наим. яркость исходн. MAX_PIX DS 2 ;наиб. яркость исходн. MIN DS 2 ;яркость PAPER MAX DS 2 ;яркость INK ;Пошла сама процедура: WORK_DATA PUSH DE PUSH HL LD A,B LD (N_PIX_1),A LD A,C LD (DIV_CNT),A ;Определяем наименьшее и наибольшее ;значения яркости. LD DE,#FF00 ;нач. значения WORK_ZN_1 LD A,(HL) CP D JR NC,WORK_ZN_2 LD D,A WORK_ZN_2 CP E JR C,WORK_ZN_3 LD E,A WORK_ZN_3 INC HL DJNZ WORK_ZN_1 ;D - наименьшее значение яркости, ;E - наибольшее (без коррекции). ;Производим гамма-коррекцию и записываем ;скорректированные значения в MIN_PIX и ;MAX_PIX: IF GAMMA_CORR LD H,GAMMA_T/256 LD L,D LD B,(HL) INC H LD C,(HL) LD (MIN_PIX),BC LD L,E LD C,(HL) DEC H LD B,(HL) LD (MAX_PIX),BC ELSE LD B,D LD C,0 LD (MIN_PIX),BC LD B,E LD (MAX_PIX),BC ENDIF ;Hаходим наименьший промежуток, в ;котором содержатся минимальное и ;максимальное значения. ;Если MAX_PIX больше BRIGHT_0 (7), то ;таблицу BRIGHT_0 однозначно не ;рассматриваем: LD HL,(BRIGHT_0+14) AND A SBC HL,BC JR NC,TWO_TABL LD HL,BRIGHT_1 CALL DEF_MM LD A,1 JR OK_MINMAX ;Рассматриваем обе таблицы: TWO_TABL LD HL,BRIGHT_1 CALL DEF_MM PUSH IX ;сохр. в стеке EXX LD HL,BRIGHT_0 CALL DEF_MM ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 21 .C ══════════════════ ;--------------------------------------- ;Процедура WORK_DATA определяет по ;данным о некоторой области пикселов, ;какой атрибут использовать при ее ;отображении, и какая псевдоградация ;будет наиболее близка по яркости для ;каждого пиксела. ; ;Осуществляется гамма-коррекция по ;таблице GAMMA_T (если GAMMA_CORR=1). ; ;Вход: HL - адрес исходных данных ; (1 байт на пиксел), ; B - кол-во пикселов, ; C - количество псевдоградаций ; (257 -> C=9; 513 -> C=10), ; DE - адрес формируемых данных ; (2 байта на пиксел). ; ;Выход: A - значение атрибутов, с DE ; сформированы данные. ;Локальные переменные (8.8): MIN_PIX DS 2 ;наим. яркость исходн. MAX_PIX DS 2 ;наиб. яркость исходн. MIN DS 2 ;яркость PAPER MAX DS 2 ;яркость INK ;Пошла сама процедура: WORK_DATA PUSH DE PUSH HL LD A,B LD (N_PIX_1),A LD A,C LD (DIV_CNT),A ;Определяем наименьшее и наибольшее ;значения яркости. LD DE,#FF00 ;нач. значения WORK_ZN_1 LD A,(HL) CP D JR NC,WORK_ZN_2 LD D,A WORK_ZN_2 CP E JR C,WORK_ZN_3 LD E,A WORK_ZN_3 INC HL DJNZ WORK_ZN_1 ;D - наименьшее значение яркости, ;E - наибольшее (без коррекции). ;Производим гамма-коррекцию и записываем ;скорректированные значения в MIN_PIX и ;MAX_PIX: IF GAMMA_CORR LD H,GAMMA_T/256 LD L,D LD B,(HL) INC H LD C,(HL) LD (MIN_PIX),BC LD L,E LD C,(HL) DEC H LD B,(HL) LD (MAX_PIX),BC ELSE LD B,D LD C,0 LD (MIN_PIX),BC LD B,E LD (MAX_PIX),BC ENDIF ;Находим наименьший промежуток, в ;котором содержатся минимальное и ;максимальное значения. ;Если MAX_PIX больше BRIGHT_0 (7), то ;таблицу BRIGHT_0 однозначно не ;рассматриваем: LD HL,(BRIGHT_0+14) AND A SBC HL,BC JR NC,TWO_TABL LD HL,BRIGHT_1 CALL DEF_MM LD A,1 JR OK_MINMAX ;Рассматриваем обе таблицы: TWO_TABL LD HL,BRIGHT_1 CALL DEF_MM PUSH IX ;сохр. в стеке EXX LD HL,BRIGHT_0 CALL DEF_MM ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 22 .C ══════════════════ ;Сравниваем HL и HL': LD (SAVE_HL),HL EXX PUSH DE PUSH HL LD DE,0 SAVE_HL EQU $-2 XOR A SBC HL,DE POP HL POP DE ADC A,0 JR NZ,OK_MM EXX POP HL ;IX в стеке не нужно JR OK_MINMAX OK_MM POP IX ;восстанавливаем IX OK_MINMAX LD (MIN),DE LD (MAX),BC ;A - значение BRIGHT (0 или 1) ;XH,XL - номера цветов. ;Формируем значение атрибута: RRCA ;0-й бит RRCA ;становится 6-м LD B,A LD A,XH ADD A,A ADD A,A ADD A,A ;PAPER ADD A,XL ;+INK ADD A,B ;+BRIGHT ;Обработка пикселов: POP HL ;SOURCE POP DE ;DESTINY LD B,0 ;COUNTER N_PIX_1 EQU $-1 PUSH AF ;сохранили атрибуты PIX_LOOP PUSH HL IF GAMMA_CORR LD L,(HL) LD H,GAMMA_T/256 LD A,(HL) INC H LD L,(HL) LD H,A ELSE LD H,(HL) LD L,0 ENDIF EX (SP),HL INC HL EXX LD HL,(MAX) LD DE,(MIN) AND A SBC HL,DE ;Так как MAX>MIN, флаг C сброшен. LD B,H LD C,L ;BC - MAX-MIN в 256-х долях. POP HL ;текущая яркость SBC HL,DE ;HL - текущая яркость-MIN в 256-х долях. ;Делим HL на BC, получая инвертированное ;частное (в 256-х или 512-х долях) в IX: LD XL,#FF LD DE,0 LD A,0 ;счетчик: 9 или 10 DIV_CNT EQU $-1 DIV_LOOP EX AF,AF' LD A,D SUB E LD D,A SBC HL,BC JR NC,DIV_1 LD A,D ADD A,E LD D,A ADC HL,BC DIV_1 LD A,XL ADC A,A LD XL,A LD A,XH ADC A,A LD XH,A SRL B RR C RR E EX AF,AF' DEC A JR NZ,DIV_LOOP ;Записываем результат, предварительно ;проинвертировав его: EXX LD A,XL CPL LD (DE),A INC DE LD A,XH CPL LD (DE),A INC DE DJNZ PIX_LOOP POP AF ;атрибуты RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 23 .C ══════════════════ ;--------------------------------------- ;Процедура DEF_MM ; ;Вход: HL указывает на таблицу яркостей, ; MIN_PIX - миним. яркость ; MAX_PIX - макс. яркость. ; ;Выход: XH - номер яркости min, ; XL - номер яркости max, ; DE - миним. яркость, ; BC - макс. яркость, ; HL - разница между ними. ; ;(Проверка, что значения D и E должны ;быть <= максимального значения в ;таблице, должна выполняться перед ;вызовом!) DEF_MM LD IX,#08FF ;XL=8, XH=-1 - первоначальные номера ;цветов с минимальной и максимальной ;яркостью. PUSH HL LD DE,15 ADD HL,DE ;Сейчас HL указывает на последнее, ;наибольшее значение в таблице. ;Просматриваем таблицу от конца к ;началу, пока MIN_PIX не окажется больше ;или равно очередному значению. LD DE,(MIN_PIX) DEF_MM_1 DEC XH LD B,(HL) DEC HL LD C,(HL) DEC HL PUSH DE EX DE,HL AND A SBC HL,BC EX DE,HL POP DE JR C,DEF_MM_1 LD H,B LD L,C ;яркость мин. EX (SP),HL ;Сейчас XH - номер яркости min. ;Просматривая таблицу от начала до ;конца, отыскиваем первое число, большее ;или равное MAX_PIX. Так как значения в ;таблице возрастают, оно же будет и ;наименьшим таким числом. LD BC,(MAX_PIX) DEF_MM_2 INC XL LD E,(HL) INC HL LD D,(HL) INC HL EX DE,HL AND A SBC HL,BC EX DE,HL JR C,DEF_MM_2 ;Вычисляем разницу: EX DE,HL ADD HL,BC ;яркость макс. POP DE ;яркость мин. PUSH HL AND A SBC HL,DE POP BC RET ;--------------------------------------- ;SET_TP_1, SET_TP_2 - процедуры вывода ;точки с заданной текстурой соответ- ;ственно на первый и второй экран. ; ;Вход: BC - номер текстуры (0-256), ; D - X, E - Y. ; ;Выход: DE, HL - без изменений. SET_TP_1 PUSH HL LD HL,SET_P_1 JR SET_TP SET_TP_2 PUSH HL LD HL,SET_P_2 SET_TP LD (SET_YES+1),HL DEC B JR Z,SET_YES ;если 256 LD B,16 CALL TEXTURE AND A JR Z,SET_NO SET_YES CALL SET_P_1 ;или SET_P_2 SET_NO POP HL RET ;--------------------------------------- ;SET_P_1 и SET_P_2 - процедуры, ставящие ;точку соответственно на первый и второй ;экран. SET_P_1 PUSH HL CALL BYTE JR SET_P SET_P_2 PUSH HL LD A,#17 CALL SETPORT CALL BYTE SET 7,H SET_P OR (HL) LD (HL),A POP HL RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 24 .C ══════════════════ ;--------------------------------------- ;Процедура TEXTURE определяет по ;заданной текстуре и координатам пиксела ;на экране, включен этот пиксел или ;выключен. ; ;Вход: B - длина стороны текстуры ; (число вида 2^n), ; C - номер текстуры, ; D,E - координаты пиксела: x,y. ; ;Выход: A - состояние пиксела: ; 0 - выключен, 1 - включен. ; ;DE не изменяется. TEXTURE ;Если длина стороны текстуры равна 1, ;то состояние пиксела равно номеру ;текстуры: SRL B ;Делим длину на 2. LD A,C ;Hомер текстуры. RET Z ;Выход, если длина ;была равна 1. ; Формулируем условия новой, более ;простой задачи, для вдвое меньшей ;текстуры, являющейся четвертью ;исходной. Ответ новой задачи будет ;ответом исходной задачи. ; Длина стороны новой текстуры уже ;вычислена и находится в B. Осталось ;определить номер новой текстуры. Он ;равен целой части от деления номера ;исходной текстуры на 4, возможно, ;увеличенной на 1 (это зависит от ;полученного при делении остатка и от ;того, в какой четверти оказывается ;пиксел). ;Включаем в регистре H бит с номером ;четверти, в которой оказывается пиксел. ;Hумерация четвертей: 0 1 ; 2 3. LD HL,#0101 LD A,B AND D JR Z,TX_1 INC H ;H:=2 TX_1 LD A,B AND E JR Z,TX_2 SLA H SLA H ;Включаем в регистре L биты с номерами ;тех четвертей, где номер текстуры на 1 ;больше. TX_2 LD A,C SRL C SRL C AND 3 JR Z,TEXTURE ;нет таких DEC A JR Z,PLUS_1 ;0 четверть SET 3,L DEC A JR Z,PLUS_1 ;0,3 четверти SET 1,L ;0,1,3 четверти ;Теперь проверяем: в той четверти, где ;оказался пиксел, номер текстуры должен ;быть на 1 больше? Если да, увеличиваем ;его: PLUS_1 LD A,H AND L JR Z,TEXTURE INC C JR TEXTURE ;--------------------------------------- BYTE LD A,E AND A RRA SCF RRA AND A RRA XOR E AND #F8 XOR E LD H,A LD A,D RLCA RLCA RLCA XOR E AND #C7 XOR E RLCA RLCA LD L,A LD A,D AND 7 LD B,A INC B LD A,1 LOOP RRCA DJNZ LOOP RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 24 .C ══════════════════ ;--------------------------------------- ;Процедура TEXTURE определяет по ;заданной текстуре и координатам пиксела ;на экране, включен этот пиксел или ;выключен. ; ;Вход: B - длина стороны текстуры ; (число вида 2^n), ; C - номер текстуры, ; D,E - координаты пиксела: x,y. ; ;Выход: A - состояние пиксела: ; 0 - выключен, 1 - включен. ; ;DE не изменяется. TEXTURE ;Если длина стороны текстуры равна 1, ;то состояние пиксела равно номеру ;текстуры: SRL B ;Делим длину на 2. LD A,C ;Номер текстуры. RET Z ;Выход, если длина ;была равна 1. ; Формулируем условия новой, более ;простой задачи, для вдвое меньшей ;текстуры, являющейся четвертью ;исходной. Ответ новой задачи будет ;ответом исходной задачи. ; Длина стороны новой текстуры уже ;вычислена и находится в B. Осталось ;определить номер новой текстуры. Он ;равен целой части от деления номера ;исходной текстуры на 4, возможно, ;увеличенной на 1 (это зависит от ;полученного при делении остатка и от ;того, в какой четверти оказывается ;пиксел). ;Включаем в регистре H бит с номером ;четверти, в которой оказывается пиксел. ;Нумерация четвертей: 0 1 ; 2 3. LD HL,#0101 LD A,B AND D JR Z,TX_1 INC H ;H:=2 TX_1 LD A,B AND E JR Z,TX_2 SLA H SLA H ;Включаем в регистре L биты с номерами ;тех четвертей, где номер текстуры на 1 ;больше. TX_2 LD A,C SRL C SRL C AND 3 JR Z,TEXTURE ;нет таких DEC A JR Z,PLUS_1 ;0 четверть SET 3,L DEC A JR Z,PLUS_1 ;0,3 четверти SET 1,L ;0,1,3 четверти ;Теперь проверяем: в той четверти, где ;оказался пиксел, номер текстуры должен ;быть на 1 больше? Если да, увеличиваем ;его: PLUS_1 LD A,H AND L JR Z,TEXTURE INC C JR TEXTURE ;--------------------------------------- BYTE LD A,E AND A RRA SCF RRA AND A RRA XOR E AND #F8 XOR E LD H,A LD A,D RLCA RLCA RLCA XOR E AND #C7 XOR E RLCA RLCA LD L,A LD A,D AND 7 LD B,A INC B LD A,1 LOOP RRCA DJNZ LOOP RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 25 .C ══════════════════ ;--------------------------------------- ;Процедура GET_A_ATR определяет адрес в ;области атрибутов. ; ;Вход: DE - координаты в пикселах. ;Выход: HL - адрес в области атрибутов, ; остальные регистры не изменены. GET_A_ATR PUSH AF PUSH DE LD H,0 LD A,E AND %11111000 LD L,A ADD HL,HL ADD HL,HL LD A,D RRA RRA RRA AND %00011111 LD E,A LD D,#58 ADD HL,DE POP DE POP AF RET ;--------------------------------------- ;Процедура GET_1R - прочитать значение ;одного пиксела исходного изображения. ; ;Вход: D - X, E - Y. ;Выход: A - значение, ; BC,DE,HL - без изменений. GET_1R PUSH BC PUSH DE PUSH HL LD A,D ;меняем местами LD D,E LD E,A LD A,D RLCA RLCA AND 3 LD HL,N_BANK LD B,0 LD C,A ADD HL,BC LD A,(HL) ;установили CALL SETPORT ;нужный ;банк памяти SET 7,D SET 6,D ;DE указывает на адрес, где хранится ;значение пиксела. LD A,(DE) POP HL POP DE POP BC RET ;--------------------------------------- ;Процедура A_FLICKER - установка ;мерцающих пикселов в противофазе. A_FLICKER LD A,#17 CALL SETPORT LD HL,#4000 ;1 изображение LD DE,#C000 ;2 изображение LD C,L FL_MAIN LD B,#80 ;маска LD A,(DE) XOR (HL) ;Теперь в A установлены биты, соответ- ;ствующие пикселам, различающимся на ;первом и втором экране, т.е. мерцающим. FL_PIX RLA JR NC,FL_NEXT EX AF,AF' LD A,B ;Младший бит регистра C указывает, на ;каком экране включать пиксел, а на ;каком выключать. Командой INC C его ;значение меняется на противоположное ;при обработке каждого мерцающего ;пиксела. INC C BIT 0,C JR NZ,FL_1 ;Hа первом экране включаем пиксел, на ;втором - выключаем: OR (HL) LD (HL),A EX DE,HL LD A,B CPL AND (HL) JR FL_2 ;Hа первом экране выключаем пиксел, на ;втором - включаем: FL_1 CPL AND (HL) LD (HL),A EX DE,HL LD A,B OR (HL) FL_2 LD (HL),A EX DE,HL EX AF,AF' FL_NEXT SRL B JR NZ,FL_PIX INC HL INC DE LD A,H CP #58 JR NZ,FL_MAIN RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 25 .C ══════════════════ ;--------------------------------------- ;Процедура GET_A_ATR определяет адрес в ;области атрибутов. ; ;Вход: DE - координаты в пикселах. ;Выход: HL - адрес в области атрибутов, ; остальные регистры не изменены. GET_A_ATR PUSH AF PUSH DE LD H,0 LD A,E AND %11111000 LD L,A ADD HL,HL ADD HL,HL LD A,D RRA RRA RRA AND %00011111 LD E,A LD D,#58 ADD HL,DE POP DE POP AF RET ;--------------------------------------- ;Процедура GET_1R - прочитать значение ;одного пиксела исходного изображения. ; ;Вход: D - X, E - Y. ;Выход: A - значение, ; BC,DE,HL - без изменений. GET_1R PUSH BC PUSH DE PUSH HL LD A,D ;меняем местами LD D,E LD E,A LD A,D RLCA RLCA AND 3 LD HL,N_BANK LD B,0 LD C,A ADD HL,BC LD A,(HL) ;установили CALL SETPORT ;нужный ;банк памяти SET 7,D SET 6,D ;DE указывает на адрес, где хранится ;значение пиксела. LD A,(DE) POP HL POP DE POP BC RET ;--------------------------------------- ;Процедура A_FLICKER - установка ;мерцающих пикселов в противофазе. A_FLICKER LD A,#17 CALL SETPORT LD HL,#4000 ;1 изображение LD DE,#C000 ;2 изображение LD C,L FL_MAIN LD B,#80 ;маска LD A,(DE) XOR (HL) ;Теперь в A установлены биты, соответ- ;ствующие пикселам, различающимся на ;первом и втором экране, т.е. мерцающим. FL_PIX RLA JR NC,FL_NEXT EX AF,AF' LD A,B ;Младший бит регистра C указывает, на ;каком экране включать пиксел, а на ;каком выключать. Командой INC C его ;значение меняется на противоположное ;при обработке каждого мерцающего ;пиксела. INC C BIT 0,C JR NZ,FL_1 ;На первом экране включаем пиксел, на ;втором - выключаем: OR (HL) LD (HL),A EX DE,HL LD A,B CPL AND (HL) JR FL_2 ;На первом экране выключаем пиксел, на ;втором - включаем: FL_1 CPL AND (HL) LD (HL),A EX DE,HL LD A,B OR (HL) FL_2 LD (HL),A EX DE,HL EX AF,AF' FL_NEXT SRL B JR NZ,FL_PIX INC HL INC DE LD A,H CP #58 JR NZ,FL_MAIN RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 26 .C ══════════════════ ;--------------------------------------- SETPORT PUSH BC LD BC,#7FFD OUT (C),A POP BC RET IF GAMMA_CORR ;--------------------------------------- ;512-байтная таблица гамма-коррекции ;(для gamma=1,5), адрес расположения ;кратен 256. В таблице сначала следуют ;старшие байты всех 256 значений, а ;потом младшие. DS 256-$256 ;выравнивание GAMMA_T db #00,#00,#00,#00,#00,#00,#00,#01 db #01,#01,#01,#02,#02,#02,#03,#03 db #04,#04,#04,#05,#05,#06,#06,#06 db #07,#07,#08,#08,#09,#09,#0A,#0A db #0B,#0B,#0C,#0C,#0D,#0E,#0E,#0F db #0F,#10,#11,#11,#12,#12,#13,#14 db #14,#15,#16,#16,#17,#18,#18,#19 db #1A,#1A,#1B,#1C,#1D,#1D,#1E,#1F db #20,#20,#21,#22,#23,#23,#24,#25 db #26,#27,#27,#28,#29,#2A,#2B,#2B db #2C,#2D,#2E,#2F,#30,#31,#31,#32 db #33,#34,#35,#36,#37,#38,#39,#39 db #3A,#3B,#3C,#3D,#3E,#3F,#40,#41 db #42,#43,#44,#45,#46,#47,#48,#49 db #4A,#4B,#4C,#4D,#4E,#4F,#50,#51 db #52,#53,#54,#55,#56,#57,#58,#59 db #5A,#5B,#5C,#5D,#5E,#60,#61,#62 db #63,#64,#65,#66,#67,#68,#69,#6B db #6C,#6D,#6E,#6F,#70,#71,#73,#74 db #75,#76,#77,#78,#7A,#7B,#7C,#7D db #7E,#7F,#81,#82,#83,#84,#85,#87 db #88,#89,#8A,#8C,#8D,#8E,#8F,#90 db #92,#93,#94,#95,#97,#98,#99,#9B db #9C,#9D,#9E,#A0,#A1,#A2,#A4,#A5 db #A6,#A7,#A9,#AA,#AB,#AD,#AE,#AF db #B1,#B2,#B3,#B5,#B6,#B7,#B9,#BA db #BB,#BD,#BE,#BF,#C1,#C2,#C4,#C5 db #C6,#C8,#C9,#CA,#CC,#CD,#CF,#D0 db #D1,#D3,#D4,#D6,#D7,#D9,#DA,#DB db #DD,#DE,#E0,#E1,#E3,#E4,#E5,#E7 db #E8,#EA,#EB,#ED,#EE,#F0,#F1,#F3 db #F4,#F6,#F7,#F9,#FA,#FC,#FD,#FF db #00,#10,#2D,#53,#80,#B3,#EC,#29 db #6B,#B1,#FB,#49,#9A,#EF,#48,#A3 db #02,#64,#C8,#30,#9A,#07,#76,#E8 db #5D,#D4,#4D,#C9,#47,#C8,#4A,#CF db #56,#DF,#6A,#F7,#87,#18,#AB,#41 db #D8,#71,#0C,#A8,#47,#E7,#8A,#2E db #D3,#7B,#24,#CF,#7B,#2A,#DA,#8B db #3E,#F3,#A9,#61,#1B,#D6,#92,#50 db #10,#D1,#94,#58,#1D,#E4,#AD,#77 db #42,#0F,#DD,#AD,#7E,#50,#24,#F9 db #CF,#A7,#80,#5A,#36,#13,#F1,#D1 db #B2,#94,#78,#5D,#43,#2A,#12,#FC db #E7,#D3,#C1,#AF,#9F,#90,#83,#76 db #6B,#61,#58,#50,#49,#44,#3F,#3C db #3A,#39,#39,#3A,#3D,#40,#45,#4B db #52,#5A,#63,#6D,#78,#84,#92,#A0 db #B0,#C0,#D2,#E5,#F9,#0D,#23,#3A db #52,#6B,#85,#A0,#BC,#D9,#F7,#16 db #36,#57,#79,#9C,#C0,#E5,#0B,#32 db #5A,#83,#AD,#D8,#04,#31,#5F,#8D db #BD,#EE,#1F,#52,#85,#BA,#EF,#25 db #5D,#95,#CE,#08,#43,#7F,#BB,#F9 db #38,#77,#B7,#F9,#3B,#7E,#C2,#07 db #4D,#93,#DB,#23,#6C,#B7,#02,#4D db #9A,#E8,#36,#86,#D6,#27,#79,#CC db #1F,#74,#C9,#20,#77,#CE,#27,#81 db #DB,#36,#92,#EF,#4D,#AC,#0B,#6B db #CC,#2E,#91,#F4,#58,#BD,#23,#8A db #F1,#5A,#C3,#2D,#97,#03,#6F,#DC db #4A,#B9,#28,#99,#0A,#7B,#EE,#61 db #D6,#4A,#C0,#37,#AE,#26,#9F,#18 db #93,#0E,#89,#06,#83,#02,#80,#00 ENDIF ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 27 .C ══════════════════ ;--------------------------------------- ;Процедура WAIT - задержка от 188 до ;65535 тактов. Должна располагаться с ;адреса, кратного 256. ; ;Вход: HL - время задержки в тактах ;(сюда уже входит время выполнения ;команд загрузки HL и вызова процедуры). DS 256-$256 ;выравнивание TAB_ADR DB ADR_0256 DB ADR_1256 DB ADR_2256 DB ADR_3256 DB ADR_4256 DB ADR_5256 DB ADR_6256 DB ADR_7256 DB ADR_8256 DB ADR_9256 DB ADR_10256 DB ADR_11256 DB ADR_12256 DB ADR_13256 DB ADR_14256 DB ADR_15256 DB ADR_16256 DB ADR_17256 DB ADR_18256 DB ADR_19256 DB ADR_20256 DB ADR_21256 DB ADR_22256 DB ADR_23256 DB ADR_24256 DB ADR_25256 DB ADR_26256 DB ADR_27256 DB ADR_28256 DB ADR_29256 DB ADR_30256 DB ADR_31256 ADR_28 NOP ADR_24 NOP ADR_20 NOP ADR_16 NOP ADR_12 NOP ADR_8 NOP ADR_4 NOP ADR_0 NOP ;4 такта RET ADR_29 NOP ADR_25 NOP ADR_21 NOP ADR_17 NOP ADR_13 NOP ADR_9 NOP ADR_5 NOP ADR_1 RET NZ ;5 тактов RET ADR_30 NOP ADR_26 NOP ADR_22 NOP ADR_18 NOP ADR_14 NOP ADR_10 NOP ADR_6 NOP ADR_2 INC HL ;6 тактов RET ADR_31 NOP ADR_27 NOP ADR_23 NOP ADR_19 NOP ADR_15 NOP ADR_11 NOP ADR_7 NOP ADR_3 LD A,(HL) ;7 тактов RET WAIT LD DE,-156 ADD HL,DE LD A,L AND 31 LD E,A ;Чтобы разделить HL на 32, достаточно ;сдвинуть HL на 5 разрядов вправо, но ;есть более короткий и быстрый способ: ;сдвинуть HL на 3 разряда влево, помещая ;выдвигаемые старшие биты результата в ;аккумулятор, и затем произвести ;перестановку: A -> H -> L. XOR A ADD HL,HL RLA ADD HL,HL RLA ADD HL,HL RLA LD L,H LD H,A ;Цикл с временем выполнения 32 такта: WAIT_1 DEC HL LD A,H OR L NOP ;для NOP ;задержки JP NZ,WAIT_1 EX DE,HL LD H,TAB_ADR/256 LD L,(HL) JP (HL) ;--------------------------------------- ;Процедуры включения и выключения ;второго режима прерываний: ON_IM2 LD HL,INT_TAB LD (HL),INT_ADR/256 LD DE,INT_TAB+1 LD BC,#100 LDIR LD A,INT_TAB/256 LD I,A LD HL,#C9FB ;EI:RET LD (INT_ADR),HL IM 2 RET OFF_IM2 IM 1 LD A,#3F LD I,A RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 28 .C ══════════════════ Комментарии ─────────── Hе все подпрограммы из общей части используются в каждой процедуре вывода. Для уменьшения объема неиспользуемые подпрограммы можно удалить. Сведения о том, какие именно подпрограммы не используются в каждой из шести процедур вывода, приведены в таблице: ┌──────────────────┬───────────────────────────────────────────┐ │ Hомер │ Подпрограммы, │ │ процедуры вывода │ не используемые в этой процедуре │ ├──────────────────┼───────────────────────────────────────────┤ │ 1 │ CLS_2, SET_TP_2, SET_P_2, ON_IM2, OFF_IM2,│ │ │ WAIT, PUT_DATA, A_FLICKER │ ├──────────────────┼───────────────────────────────────────────┤ │ 2 │ PUT_DATA, A_FLICKER │ ├──────────────────┼───────────────────────────────────────────┤ │ 3 │ GET_A_ATR, CLS_2, SET_TP_2, SET_P_2, │ │ │ PUT_DATA, A_FLICKER │ ├──────────────────┼───────────────────────────────────────────┤ │ 4 │ ON_IM2, OFF_IM2, WAIT │ ├──────────────────┼───────────────────────────────────────────┤ │ 5 │ GET_A_ATR │ ├──────────────────┼───────────────────────────────────────────┤ │ 6 │ GET_A_ATR │ └──────────────────┴───────────────────────────────────────────┘ Табл. 1 * * * Как выводить изображение с увеличением? Если требуется увеличение в два раза (размер выводимой области изображения тогда будет 128*96), то в начало процедуры GET_1R, после команд PUSH, добавьте такой фрагмент: LD A,D SRL A ADD A,координата X левого верхнего угла области LD D,A LD A,E SRL A ADD A,координата Y левого верхнего угла области LD E,A Если нужно увеличение в 4, 8... раз (размер выводимой области тогда будет соответственно 64*48, 32*24...), то ставим не одну, а две, три... команды SRL A. Кстати, для увеличения в два раза вышеприведенный фрагмент можно оптимизировать: вначале прибавить удвоенную координату, а затем разделить на два командой RRA вместо SRL A - это будет короче и быстрее. * * * Как сформировать таблицу гамма-коррекции (GAMMA_T) для нужного значения гаммы? Hиже приведена соответствующая программа на Бейсике. Эта программа формирует таблицу в том же формате, в котором она хранится в общей части процедур вывода: значения в формате 8.8, в первых 256 байтах - целые части, в следующих 256 - дробные. Общая длина таблицы, таким образом, составляет 512 байтов. При запуске программы требуется указать нужное значение гаммы. Одновременно с вычислением таблицы на экране строится график зависимости яркости пиксела от его значения в изображении для заданного значения гаммы. После окончания вычислений укажите имя файла, куда будет записана таблица, или просто нажмите "Enter" для перезапуска программы. 10 CLEAR 32767 15 CLS 20 INPUT "Gamma: ";G 30 FOR i=0 TO 255 40 LET y=(i/255)^G 50 LET Word= INT (y*255*256+0.5) 60 LET HighByte= INT (Word/256) 70 LET LowByte= Word-HighByte*256 80 POKE 32768+i, HighByte 90 POKE 32768+256+i, LowByte 100 PLOT i*175/255,y*175 110 NEXT i 120 INPUT "File: ";a$ 130 IF a$="" THEN GO TO 15 140 RANDOMIZE USR 15619: REM :SAVE a$ CODE 32768,512 * * * При выводе изображения для получения псевдоградаций яркости используются 257 различных текстур 16*16. Простой подсчет показывает, что для их хранения требуется 257*16*16/8=8224 байта памяти, или более 8 КБ. Тратить столько памяти не хотелось, и я использовал такой прием: когда нужно узнать, включен или выключен пиксел с данными координатами, находящийся в определенной текстуре, ответ получается с помощью специального алгоритма, основанного на свойствах текстур. Представляется интересным рассмотреть этот алгоритм подробнее. Сначала - о свойствах, которым должны удовлетворять текстуры, используемые для получения псевдоградаций яркости (в общем случае). Пусть l - длина стороны текстуры, причем l является числом вида 2^k (в данном случае l=16=2^4). Количество текстур N равно l^2+1 (в данном случае N=16^2+1=257). Во-первых, в каждой следующей текстуре на один включенный пиксел больше, чем в предыдущей. Если пронумеровать текстуры от 0 до N-1, то номер текстуры будет равен числу включенных в ней пикселов. Во-вторых, любые две соседние текстуры отличаются лишь одним пикселом. Учитывая предыдущее свойство, получаем, что каждая следующая текстура получается из предыдущей путем включения еще одного пиксела. В-третьих, включенные и выключенные пикселы в каждой текстуре располагаются равномерно. Что это значит? Возьмем текстуру и разделим ее на четыре равные части: ┌─────┬─────┐ │ | │ │ | │ ├-----+-----┤ │ | │ │ | │ └─────┴─────┘ Рис. 20 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 28 .C ══════════════════ Комментарии ─────────── Не все подпрограммы из общей части используются в каждой процедуре вывода. Для уменьшения объема неиспользуемые подпрограммы можно удалить. Сведения о том, какие именно подпрограммы не используются в каждой из шести процедур вывода, приведены в таблице: ┌──────────────────┬───────────────────────────────────────────┐ │ Номер │ Подпрограммы, │ │ процедуры вывода │ не используемые в этой процедуре │ ├──────────────────┼───────────────────────────────────────────┤ │ 1 │ CLS_2, SET_TP_2, SET_P_2, ON_IM2, OFF_IM2,│ │ │ WAIT, PUT_DATA, A_FLICKER │ ├──────────────────┼───────────────────────────────────────────┤ │ 2 │ PUT_DATA, A_FLICKER │ ├──────────────────┼───────────────────────────────────────────┤ │ 3 │ GET_A_ATR, CLS_2, SET_TP_2, SET_P_2, │ │ │ PUT_DATA, A_FLICKER │ ├──────────────────┼───────────────────────────────────────────┤ │ 4 │ ON_IM2, OFF_IM2, WAIT │ ├──────────────────┼───────────────────────────────────────────┤ │ 5 │ GET_A_ATR │ ├──────────────────┼───────────────────────────────────────────┤ │ 6 │ GET_A_ATR │ └──────────────────┴───────────────────────────────────────────┘ Табл. 1 * * * Как выводить изображение с увеличением? Если требуется увеличение в два раза (размер выводимой области изображения тогда будет 128*96), то в начало процедуры GET_1R, после команд PUSH, добавьте такой фрагмент: LD A,D SRL A ADD A,координата X левого верхнего угла области LD D,A LD A,E SRL A ADD A,координата Y левого верхнего угла области LD E,A Если нужно увеличение в 4, 8... раз (размер выводимой области тогда будет соответственно 64*48, 32*24...), то ставим не одну, а две, три... команды SRL A. Кстати, для увеличения в два раза вышеприведенный фрагмент можно оптимизировать: вначале прибавить удвоенную координату, а затем разделить на два командой RRA вместо SRL A - это будет короче и быстрее. * * * Как сформировать таблицу гамма-коррекции (GAMMA_T) для нужного значения гаммы? Ниже приведена соответствующая программа на Бейсике. Эта программа формирует таблицу в том же формате, в котором она хранится в общей части процедур вывода: значения в формате 8.8, в первых 256 байтах - целые части, в следующих 256 - дробные. Общая длина таблицы, таким образом, составляет 512 байтов. При запуске программы требуется указать нужное значение гаммы. Одновременно с вычислением таблицы на экране строится график зависимости яркости пиксела от его значения в изображении для заданного значения гаммы. После окончания вычислений укажите имя файла, куда будет записана таблица, или просто нажмите "Enter" для перезапуска программы. 10 CLEAR 32767 15 CLS 20 INPUT "Gamma: ";G 30 FOR i=0 TO 255 40 LET y=(i/255)^G 50 LET Word= INT (y*255*256+0.5) 60 LET HighByte= INT (Word/256) 70 LET LowByte= Word-HighByte*256 80 POKE 32768+i, HighByte 90 POKE 32768+256+i, LowByte 100 PLOT i*175/255,y*175 110 NEXT i 120 INPUT "File: ";a$ 130 IF a$="" THEN GO TO 15 140 RANDOMIZE USR 15619: REM :SAVE a$ CODE 32768,512 * * * При выводе изображения для получения псевдоградаций яркости используются 257 различных текстур 16*16. Простой подсчет показывает, что для их хранения требуется 257*16*16/8=8224 байта памяти, или более 8 КБ. Тратить столько памяти не хотелось, и я использовал такой прием: когда нужно узнать, включен или выключен пиксел с данными координатами, находящийся в определенной текстуре, ответ получается с помощью специального алгоритма, основанного на свойствах текстур. Представляется интересным рассмотреть этот алгоритм подробнее. Сначала - о свойствах, которым должны удовлетворять текстуры, используемые для получения псевдоградаций яркости (в общем случае). Пусть l - длина стороны текстуры, причем l является числом вида 2^k (в данном случае l=16=2^4). Количество текстур N равно l^2+1 (в данном случае N=16^2+1=257). Во-первых, в каждой следующей текстуре на один включенный пиксел больше, чем в предыдущей. Если пронумеровать текстуры от 0 до N-1, то номер текстуры будет равен числу включенных в ней пикселов. Во-вторых, любые две соседние текстуры отличаются лишь одним пикселом. Учитывая предыдущее свойство, получаем, что каждая следующая текстура получается из предыдущей путем включения еще одного пиксела. В-третьих, включенные и выключенные пикселы в каждой текстуре располагаются равномерно. Что это значит? Возьмем текстуру и разделим ее на четыре равные части: ┌─────┬─────┐ │ | │ │ | │ ├-----+-----┤ │ | │ │ | │ └─────┴─────┘ Рис. 20 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 29 .C ══════════════════ При этом в любых двух частях количество включенных (как и выключенных) пикселов будет различаться не более чем на один. Если каждую часть опять разделить на четыре, это условие будет выполняться и для полученных меньших частей, и так далее. Очевидно, что если номер текстуры (обозначим его n) кратен 4 (а значит, и количество включенных пикселов кратно 4), то единственный вариант их расположения в четвертях текстуры, удовлетворяющий приведенному выше условию, - по n/4 пикселов в каждой четверти. Пусть номер текстуры не кратен 4, m - остаток от деления. Тогда, чтобы условие равномерности выполнялось, в m четвертях должно быть по [n/4]+1 пикселов, а в оставшихся 4-m четвертях - по [n/4] пикселов. Положим для определенности, что пикселы располагаются в трех возможных случаях (когда m = 1, 2 или 3) следующим образом (учитывая второе свойство - соседние текстуры различаются лишь одним пикселом!): ┌───────┬───────┐ ┌───────┬───────┐ ┌───────┬───────┐ │ | │ │ | │ │ | │ │[n/4]+1| [n/4] │ │[n/4]+1| [n/4] │ │[n/4]+1|[n/4+1]│ │ | │ │ | │ │ | │ ├-------+-------┤ ├-------+-------┤ ├-------+-------┤ │ | │ │ | │ │ | │ │ [n/4] | [n/4] │ │ [n/4] |[n/4+1]│ │ [n/4] |[n/4+1]│ │ | │ │ | │ │ | │ └───────┴───────┘ └───────┴───────┘ └───────┴───────┘ а) m=1 б) m=2 в) m=3 Рис. 21 Приведенной выше информации достаточно как для того, чтобы построить все N текстур, так и для того, чтобы определить, включен или выключен пиксел с данными координатами, находящийся в определенной текстуре. Алгоритм очень простой. Пусть известны l - длина стороны текстуры, n - номер текстуры, x и y - координаты пиксела на экране. Hужно получить a - состояние пиксела: 0 - выключен, 1 - включен. 1. Если размер текстуры - 1*1, то состояние пиксела равно номеру текстуры (т.е. если l=1, то a=n). 2. Иначе формулируем условия новой, более простой задачи, для вдвое меньшей текстуры, являющейся четвертью исходной (т.е. получаем новые l и n). Ответ новой задачи будет ответом исходной задачи. 2.1. Если n кратно 4, то l:=l/2, n:=n/4, и решаем задачу с этими условиями. 2.2. Иначе определяем, в какой четверти текстуры оказывается пиксел. Если l and x = 0, то пиксел в левой половине, иначе в правой. Если l and y = 0, то пиксел в верхней половине, иначе в нижней. 2.3. По m - остатку от деления n на 4 - определяем, в каких четвертях будет включено [n/4] пикселов, а в каких - [n/4]+1 (см. рис. 21). 2.4. Если в той четверти текстуры, где оказывается пиксел, включено [n/4] пикселов, то l:=l/2, n:=[n/4], и решаем задачу с этими условиями. 2.5. Иначе l:=l/2, n:=[n/4]+1, и решаем задачу с этими условиями. В программе этим занимается процедура TEXTURE, длина которой всего 46 байтов. Сравните это с первоначальными 8224 байтами! ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 29 .C ══════════════════ При этом в любых двух частях количество включенных (как и выключенных) пикселов будет различаться не более чем на один. Если каждую часть опять разделить на четыре, это условие будет выполняться и для полученных меньших частей, и так далее. Очевидно, что если номер текстуры (обозначим его n) кратен 4 (а значит, и количество включенных пикселов кратно 4), то единственный вариант их расположения в четвертях текстуры, удовлетворяющий приведенному выше условию, - по n/4 пикселов в каждой четверти. Пусть номер текстуры не кратен 4, m - остаток от деления. Тогда, чтобы условие равномерности выполнялось, в m четвертях должно быть по [n/4]+1 пикселов, а в оставшихся 4-m четвертях - по [n/4] пикселов. Положим для определенности, что пикселы располагаются в трех возможных случаях (когда m = 1, 2 или 3) следующим образом (учитывая второе свойство - соседние текстуры различаются лишь одним пикселом!): ┌───────┬───────┐ ┌───────┬───────┐ ┌───────┬───────┐ │ | │ │ | │ │ | │ │[n/4]+1| [n/4] │ │[n/4]+1| [n/4] │ │[n/4]+1|[n/4+1]│ │ | │ │ | │ │ | │ ├-------+-------┤ ├-------+-------┤ ├-------+-------┤ │ | │ │ | │ │ | │ │ [n/4] | [n/4] │ │ [n/4] |[n/4+1]│ │ [n/4] |[n/4+1]│ │ | │ │ | │ │ | │ └───────┴───────┘ └───────┴───────┘ └───────┴───────┘ а) m=1 б) m=2 в) m=3 Рис. 21 Приведенной выше информации достаточно как для того, чтобы построить все N текстур, так и для того, чтобы определить, включен или выключен пиксел с данными координатами, находящийся в определенной текстуре. Алгоритм очень простой. Пусть известны l - длина стороны текстуры, n - номер текстуры, x и y - координаты пиксела на экране. Нужно получить a - состояние пиксела: 0 - выключен, 1 - включен. 1. Если размер текстуры - 1*1, то состояние пиксела равно номеру текстуры (т.е. если l=1, то a=n). 2. Иначе формулируем условия новой, более простой задачи, для вдвое меньшей текстуры, являющейся четвертью исходной (т.е. получаем новые l и n). Ответ новой задачи будет ответом исходной задачи. 2.1. Если n кратно 4, то l:=l/2, n:=n/4, и решаем задачу с этими условиями. 2.2. Иначе определяем, в какой четверти текстуры оказывается пиксел. Если l and x = 0, то пиксел в левой половине, иначе в правой. Если l and y = 0, то пиксел в верхней половине, иначе в нижней. 2.3. По m - остатку от деления n на 4 - определяем, в каких четвертях будет включено [n/4] пикселов, а в каких - [n/4]+1 (см. рис. 21). 2.4. Если в той четверти текстуры, где оказывается пиксел, включено [n/4] пикселов, то l:=l/2, n:=[n/4], и решаем задачу с этими условиями. 2.5. Иначе l:=l/2, n:=[n/4]+1, и решаем задачу с этими условиями. В программе этим занимается процедура TEXTURE, длина которой всего 46 байтов. Сравните это с первоначальными 8224 байтами! ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 30 .C ══════════════════ Приложение 4 ──────────── Здесь приведены тексты процедур для работы с pcx-файлами, содержащими черно-белые изображения с 256 градациями яркости. Кратко расскажу о формате таких файлов - это необходимо для понимания логики работы процедур. ╔════════════════════════╤════════════╗ ║ Часть файла │ Длина ║ ╠════════════════════════╪════════════╣ ║ Заголовок │ 128 ║ ╟────────────────────────┼────────────╢ ║ Упакованная информация │ переменная ║ ║ о пикселах изображения │ ║ ╟────────────────────────┼────────────╢ ║ Тип палитры │ 1 ║ ╟────────────────────────┼────────────╢ ║ Палитра │ 768 ║ ╚════════════════════════╧════════════╝ Табл. 2 _Заголовок файла_ содержит информацию о размерах изображения, количестве цветов и т.п. В процедуре чтения (READ_PCX) заголовок игнорируется, так как эти данные предполагаются уже известными. В процедуре записи (MAKE_PCX) заголовок записывается всегда один и тот же. _Информация о пикселах изображения_ хранится в упакованном виде. При упаковке последовательность одинаковых байтов заменяется на два байта: счетчик и оригинал. Чтобы можно было распознавать байт-счетчик при распаковке, его старшие два бита устанавливаются в 1, а длина последовательности (от 1 до 63) хранится в младших шести битах. Если длина последовательности больше 63, записывается несколько пар "счетчик-значение". Если при упаковке попадается отдельный байт, старшие два бита которого равны 1, то, чтобы при распаковке он не был перепутан с байтом-счетчиком, перед ним ставится байт-счетчик с количеством повторений, равным 1. _Тип палитры_ - #0A, если значения байтов палитры находятся в диапазоне 0-63, и #0C, если в диапазоне 0-255. В процедуре чтения тип палитры игнорируется, а в процедуре записи - записывается #0C. _Палитра_ - в ней содержится информация о компонентах R, G, B каждого из 256 цветов изображения. В процедуре чтения палитра игнорируется, а в процедуре записи - записывается последовательность байтов (0,0,0, 1,1,1,..., 255,255,255). Исходя из вышесказанного, можно определить максимальную длину файла: 2 байта на пиксел - это 256*192*2 = #18000, еще #80 байтов - заголовок, 1 байт - тип палитры и #300 байтов - сама палитра, итого #18381 байт. Hо в TR-DOS длина файла не может превышать #FF00 байтов. Поэтому в процедуре MAKE_PCX, если длина файла получается больше #FF00, на диск записываются два файла: первый длиной #FF00, а во втором - остаток. Итак, тексты процедур. 1. READ_PCX - чтение изображения из pcx-файла в три банка памяти. Вход: имя и расширение файла указано в FILENAME, номера банков памяти - в N_BANK. Выход: A=1 - OK, A=0 - файл не найден. ;Структуры данных: BUFER EQU #7000 ;256-байтный буфер (адрес кратен 256!) ;Пошла сама процедура: ORG #6000 READ_PCX LD HL,BUFER LD (GET_B_0),HL LD HL,N_BANK LD (PUT_B_1),HL LD HL,#C000 LD (PUT_B_2),HL ;Ищем файл: LD HL,FILENAME LD C,#13 CALL #3D13 LD C,#0A CALL #3D13 LD A,C INC A RET Z ;нет такого файла DEC A LD C,8 ;читаем CALL #3D13 ;дескриптор ;Определяем трек/секторный адрес начала ;файла: LD HL,(#5CEB) LD (GET_B_1),HL ;Пропускаем первые 128 байтов файла - ;это заголовок. LD B,128 M1 CALL GET_B DJNZ M1 LD HL,#C000 ;кол-во pix. ;Главный цикл: M3 CALL GET_B ;читаем байт ;Если это байт-счетчик, то два его ;старших бита установлены в 1, а в ;младших 6 битах - количество ;повторений. LD B,1 LD C,A CPL AND %11000000 LD A,C JR NZ,M2 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 30 .C ══════════════════ Приложение 4 ──────────── Здесь приведены тексты процедур для работы с pcx-файлами, содержащими черно-белые изображения с 256 градациями яркости. Кратко расскажу о формате таких файлов - это необходимо для понимания логики работы процедур. ╔════════════════════════╤════════════╗ ║ Часть файла │ Длина ║ ╠════════════════════════╪════════════╣ ║ Заголовок │ 128 ║ ╟────────────────────────┼────────────╢ ║ Упакованная информация │ переменная ║ ║ о пикселах изображения │ ║ ╟────────────────────────┼────────────╢ ║ Тип палитры │ 1 ║ ╟────────────────────────┼────────────╢ ║ Палитра │ 768 ║ ╚════════════════════════╧════════════╝ Табл. 2 _Заголовок файла_ содержит информацию о размерах изображения, количестве цветов и т.п. В процедуре чтения (READ_PCX) заголовок игнорируется, так как эти данные предполагаются уже известными. В процедуре записи (MAKE_PCX) заголовок записывается всегда один и тот же. _Информация о пикселах изображения_ хранится в упакованном виде. При упаковке последовательность одинаковых байтов заменяется на два байта: счетчик и оригинал. Чтобы можно было распознавать байт-счетчик при распаковке, его старшие два бита устанавливаются в 1, а длина последовательности (от 1 до 63) хранится в младших шести битах. Если длина последовательности больше 63, записывается несколько пар "счетчик-значение". Если при упаковке попадается отдельный байт, старшие два бита которого равны 1, то, чтобы при распаковке он не был перепутан с байтом-счетчиком, перед ним ставится байт-счетчик с количеством повторений, равным 1. _Тип палитры_ - #0A, если значения байтов палитры находятся в диапазоне 0-63, и #0C, если в диапазоне 0-255. В процедуре чтения тип палитры игнорируется, а в процедуре записи - записывается #0C. _Палитра_ - в ней содержится информация о компонентах R, G, B каждого из 256 цветов изображения. В процедуре чтения палитра игнорируется, а в процедуре записи - записывается последовательность байтов (0,0,0, 1,1,1,..., 255,255,255). Исходя из вышесказанного, можно определить максимальную длину файла: 2 байта на пиксел - это 256*192*2 = #18000, еще #80 байтов - заголовок, 1 байт - тип палитры и #300 байтов - сама палитра, итого #18381 байт. Но в TR-DOS длина файла не может превышать #FF00 байтов. Поэтому в процедуре MAKE_PCX, если длина файла получается больше #FF00, на диск записываются два файла: первый длиной #FF00, а во втором - остаток. Итак, тексты процедур. 1. READ_PCX - чтение изображения из pcx-файла в три банка памяти. Вход: имя и расширение файла указано в FILENAME, номера банков памяти - в N_BANK. Выход: A=1 - OK, A=0 - файл не найден. ;Структуры данных: BUFER EQU #7000 ;256-байтный буфер (адрес кратен 256!) ;Пошла сама процедура: ORG #6000 READ_PCX LD HL,BUFER LD (GET_B_0),HL LD HL,N_BANK LD (PUT_B_1),HL LD HL,#C000 LD (PUT_B_2),HL ;Ищем файл: LD HL,FILENAME LD C,#13 CALL #3D13 LD C,#0A CALL #3D13 LD A,C INC A RET Z ;нет такого файла DEC A LD C,8 ;читаем CALL #3D13 ;дескриптор ;Определяем трек/секторный адрес начала ;файла: LD HL,(#5CEB) LD (GET_B_1),HL ;Пропускаем первые 128 байтов файла - ;это заголовок. LD B,128 M1 CALL GET_B DJNZ M1 LD HL,#C000 ;кол-во pix. ;Главный цикл: M3 CALL GET_B ;читаем байт ;Если это байт-счетчик, то два его ;старших бита установлены в 1, а в ;младших 6 битах - количество ;повторений. LD B,1 LD C,A CPL AND %11000000 LD A,C JR NZ,M2 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 31 .C ══════════════════ ;Повторяем следующий байт указанное ;количество раз: LD A,%00111111 AND C LD B,A CALL GET_B M2 CALL PUT_B DEC HL DJNZ M2 LD A,H ;все #C000 пикселов? OR L JR NZ,M3 INC A ;A:=1 RET ;Имя файла: FILENAME DB "picture pcx" ;Hомера используемых банков памяти: N_BANK DB #10,#11,#13 ;--------------------------------------- ;Процедура GET_B - чтение байта из ;файла. GET_B PUSH HL PUSH BC LD HL,0 ;адрес в буфере GET_B_0 EQU $-2 LD A,L AND A JR NZ,GET_B_3 ;Hадо прочитать сектор файла: PUSH HL LD B,1 LD DE,0 GET_B_1 EQU $-2 PUSH DE LD HL,BUFER LD C,5 CALL #3D13 ;Увеличиваем трек/секторный адрес: POP DE INC E BIT 4,E JR Z,GET_B_2 LD E,0 INC D GET_B_2 LD (GET_B_1),DE POP HL ;Читаем байт из буфера: GET_B_3 LD A,(HL) INC L LD (GET_B_0),HL POP BC POP HL RET ;--------------------------------------- ;Процедура PUT_B - помещение информации ;об очередном пикселе в память. PUT_B PUSH AF PUSH HL PUSH AF PUSH BC LD HL,0 PUT_B_1 EQU $-2 LD A,(HL) ;N банка LD BC,#7FFD OUT (C),A POP BC POP AF LD HL,0 ;адрес PUT_B_2 EQU $-2 LD (HL),A INC HL ;увелич. адреса LD A,H OR L JR NZ,PUT_B_3 LD HL,(PUT_B_1) ;переход к INC HL ;следующему LD (PUT_B_1),HL ;банку LD HL,#C000 PUT_B_3 LD (PUT_B_2),HL POP HL POP AF RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 31 .C ══════════════════ ;Повторяем следующий байт указанное ;количество раз: LD A,%00111111 AND C LD B,A CALL GET_B M2 CALL PUT_B DEC HL DJNZ M2 LD A,H ;все #C000 пикселов? OR L JR NZ,M3 INC A ;A:=1 RET ;Имя файла: FILENAME DB "picture pcx" ;Номера используемых банков памяти: N_BANK DB #10,#11,#13 ;--------------------------------------- ;Процедура GET_B - чтение байта из ;файла. GET_B PUSH HL PUSH BC LD HL,0 ;адрес в буфере GET_B_0 EQU $-2 LD A,L AND A JR NZ,GET_B_3 ;Надо прочитать сектор файла: PUSH HL LD B,1 LD DE,0 GET_B_1 EQU $-2 PUSH DE LD HL,BUFER LD C,5 CALL #3D13 ;Увеличиваем трек/секторный адрес: POP DE INC E BIT 4,E JR Z,GET_B_2 LD E,0 INC D GET_B_2 LD (GET_B_1),DE POP HL ;Читаем байт из буфера: GET_B_3 LD A,(HL) INC L LD (GET_B_0),HL POP BC POP HL RET ;--------------------------------------- ;Процедура PUT_B - помещение информации ;об очередном пикселе в память. PUT_B PUSH AF PUSH HL PUSH AF PUSH BC LD HL,0 PUT_B_1 EQU $-2 LD A,(HL) ;N банка LD BC,#7FFD OUT (C),A POP BC POP AF LD HL,0 ;адрес PUT_B_2 EQU $-2 LD (HL),A INC HL ;увелич. адреса LD A,H OR L JR NZ,PUT_B_3 LD HL,(PUT_B_1) ;переход к INC HL ;следующему LD (PUT_B_1),HL ;банку LD HL,#C000 PUT_B_3 LD (PUT_B_2),HL POP HL POP AF RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 32 .C ══════════════════ 2. MAKE_PCX - запись изображения из трех банков памяти в pcx-файл. Вход: имя и расширение файла указано в NAME_EXT, номера банков памяти - в N_BANK. Выход: A=1 - OK, A=0 - записать файл не удалось (нет места на диске или в каталоге диска). ;Структуры данных: FILE_BUFER EQU #7000 ;буфер для записи CAT_BUFER EQU #7100 ;каталог диска ;Пошла сама процедура: ORG #6000 MAKE_PCX CALL INIT_GET ;Сначала преобразуем "вхолостую", чтобы ;узнать размер получаемого файла. В это ;время на бордюр выводятся цветные ;полосы - чтобы показать, что компьютер ;не завис. LD D,0 ;DHL - длина LD HL,#381 LD BC,1 MAKE_PCX_1 CALL GET_BYTE JR C,MAKE_PCX_2 AND 7 ;Изменяем OUT (254),A ;BORDER ADD HL,BC LD A,D ADC A,0 LD D,A JR MAKE_PCX_1 MAKE_PCX_2 LD (LEN_FILE),HL LD A,D LD (LEN_FILE+2),A ;Длина в байтах определена, теперь ;получаем длину в секторах: LD BC,255 ADD HL,BC LD A,D ADC A,0 LD L,H LD H,A ;HL - длина. PUSH HL ;Запомнили ее... ;Читаем каталог: LD HL,CAT_BUFER LD DE,0 LD B,9 LD C,5 CALL #3D13 ;Проверка свободного места в области ;файлов: LD HL,(CAT_BUFER+#8E5) POP DE ;длина файла XOR A ;A:=0, флаг C сброшен SBC HL,DE RET C ;Hет места на диске! ;Коррекция свободного места: LD (CAT_BUFER+#8E5),HL ;Теперь определяем, сколько будет ;файлов: один или два? LD A,1 ;кол-во файлов EX DE,HL LD DE,255 SBC HL,DE ;флаг C сброшен! JR C,MAKE_PCX_7 ;1 файл INC A ;2 файла MAKE_PCX_7 LD (FILES),A ;Проверка свободного места в области ;каталога: LD HL,CAT_BUFER+#8E4 LD B,(HL) ADD A,B LD (HL),A ;коррекция CP 128+1 LD A,0 RET NC ;Hет места! ;Определяем номер первого сектора файла: LD HL,(CAT_BUFER+#8E1) LD (DISK_ADR),HL ;Помещаем в каталог информацию о файле ;(или о двух файлах, если длина больше ;#FF00): LD H,0 LD L,B ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ;*16 LD DE,CAT_BUFER ADD HL,DE EX DE,HL LD HL,NAME_EXT ;Заголовок LD BC,11 LDIR LD A,(FILES) CP 2 JR NZ,MAKE_PCX_A ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 32 .C ══════════════════ 2. MAKE_PCX - запись изображения из трех банков памяти в pcx-файл. Вход: имя и расширение файла указано в NAME_EXT, номера банков памяти - в N_BANK. Выход: A=1 - OK, A=0 - записать файл не удалось (нет места на диске или в каталоге диска). ;Структуры данных: FILE_BUFER EQU #7000 ;буфер для записи CAT_BUFER EQU #7100 ;каталог диска ;Пошла сама процедура: ORG #6000 MAKE_PCX CALL INIT_GET ;Сначала преобразуем "вхолостую", чтобы ;узнать размер получаемого файла. В это ;время на бордюр выводятся цветные ;полосы - чтобы показать, что компьютер ;не завис. LD D,0 ;DHL - длина LD HL,#381 LD BC,1 MAKE_PCX_1 CALL GET_BYTE JR C,MAKE_PCX_2 AND 7 ;Изменяем OUT (254),A ;BORDER ADD HL,BC LD A,D ADC A,0 LD D,A JR MAKE_PCX_1 MAKE_PCX_2 LD (LEN_FILE),HL LD A,D LD (LEN_FILE+2),A ;Длина в байтах определена, теперь ;получаем длину в секторах: LD BC,255 ADD HL,BC LD A,D ADC A,0 LD L,H LD H,A ;HL - длина. PUSH HL ;Запомнили ее... ;Читаем каталог: LD HL,CAT_BUFER LD DE,0 LD B,9 LD C,5 CALL #3D13 ;Проверка свободного места в области ;файлов: LD HL,(CAT_BUFER+#8E5) POP DE ;длина файла XOR A ;A:=0, флаг C сброшен SBC HL,DE RET C ;Нет места на диске! ;Коррекция свободного места: LD (CAT_BUFER+#8E5),HL ;Теперь определяем, сколько будет ;файлов: один или два? LD A,1 ;кол-во файлов EX DE,HL LD DE,255 SBC HL,DE ;флаг C сброшен! JR C,MAKE_PCX_7 ;1 файл INC A ;2 файла MAKE_PCX_7 LD (FILES),A ;Проверка свободного места в области ;каталога: LD HL,CAT_BUFER+#8E4 LD B,(HL) ADD A,B LD (HL),A ;коррекция CP 128+1 LD A,0 RET NC ;Нет места! ;Определяем номер первого сектора файла: LD HL,(CAT_BUFER+#8E1) LD (DISK_ADR),HL ;Помещаем в каталог информацию о файле ;(или о двух файлах, если длина больше ;#FF00): LD H,0 LD L,B ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ;*16 LD DE,CAT_BUFER ADD HL,DE EX DE,HL LD HL,NAME_EXT ;Заголовок LD BC,11 LDIR LD A,(FILES) CP 2 JR NZ,MAKE_PCX_A ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 33 .C ══════════════════ ;Записывается два файла. ;Первый файл: EX DE,HL LD (HL),0 ;Длина: INC HL LD (HL),#FF ;#FF00 байтов, INC HL LD (HL),#FF ;#FF секторов. INC HL LD A,#FF ;Адрес на CALL NEW_TS ;диске. ;Второй файл: EX DE,HL LD HL,NAME_EXT ;Заголовок. LD BC,8 LDIR LD A,"0" ;Первый символ LD (DE),A ;расширения - "0" INC DE INC HL LDI LDI EX DE,HL ;Длина: LD A,(LEN_FILE) ;младший LD (HL),A ;байт... INC HL PUSH HL LD HL,(LEN_FILE+1) DEC H INC HL ;-#FF00 LD A,L ;и старший POP HL ;байт. LD (HL),A PUSH HL DEC HL LD L,(HL) LD H,A INC H DEC HL LD A,H ;Длина в секторах. POP HL INC HL LD (HL),A INC HL CALL NEW_TS JR MAKE_PCX_9 ;Записывается один файл: MAKE_PCX_A EX DE,HL LD DE,(LEN_FILE) LD (HL),E ;Длина INC HL ;в байтах. LD (HL),D INC HL INC D ;Длина DEC DE ;в секторах. LD (HL),D INC HL LD A,D CALL NEW_TS ;Записываем каталог: MAKE_PCX_9 LD HL,CAT_BUFER LD DE,0 LD B,9 LD C,6 CALL #3D13 ;Записываем содержимое файла: CALL OPEN_FILE ;Заголовок (128 байтов): LD HL,HEADER LD B,128 MAKE_PCX_6 LD A,(HL) CALL PUT_BYTE INC HL DJNZ MAKE_PCX_6 ;Данные о пикселах: CALL INIT_GET MAKE_PCX_3 CALL GET_BYTE JR C,MAKE_PCX_4 CALL PUT_BYTE JR MAKE_PCX_3 ;Тип палитры: MAKE_PCX_4 LD A,#0C CALL PUT_BYTE ;Палитра (#300 байтов): XOR A MAKE_PCX_5 CALL PUT_BYTE CALL PUT_BYTE CALL PUT_BYTE INC A JR NZ,MAKE_PCX_5 ;Закрываем файл: CALL CLOSE_FILE LD A,1 RET NAME_EXT DB "picture pcx" ;имя LEN_FILE DS 3 ;длина pcx-файла FILES DS 1 ;сколько будет файлов ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 02 Oct 2002
Hello, All! ═══════════════════ 34 .C ══════════════════ ;--------------------------------------- ;Процедура NEW_TS помещает по указанному ;в HL адресу сведения о первом ;свободном треке/секторе и обновляет ;эти сведения в системном секторе. ; ;Вход: A - длина файла в секторах. ;Выход: HL:=HL+2. NEW_TS LD DE,(CAT_BUFER+#8E1) LD (HL),E INC HL ;Адрес на LD (HL),D ;диске. INC HL ;Увеличиваем номера первых незанятых ;трека/сектора: LD B,A ;Длина в секторах AND 15 ADD A,E BIT 4,A RES 4,A LD E,A JR Z,NEW_TS_1 INC D NEW_TS_1 LD A,B RRA RRA RRA RRA AND 15 ADD A,D LD D,A LD (CAT_BUFER+#8E1),DE RET ;--------------------------------------- ;Процедура GET_BYTE возвращает в ;аккумуляторе очередной байт упакованных ;данных о пикселах. Если флаг C ;установлен - значит, данные кончились. ; ;BC,DE,HL не изменяются. STATUS DS 1 ;N состояния проц. BYTE_B DS 1 ;запомненный байт ;Инициализация: INIT_GET LD A,1 LD (STATUS),A CALL INIT_PIX RET ;Пошла сама процедура: GET_BYTE PUSH BC PUSH DE PUSH HL LD A,(STATUS) CP 2 JR Z,GET_BYTE_2 ;1-е состояние: LD C,1 ;счетчик одинаковых CALL GET_PIX JR C,GET_EXIT LD (BYTE_B),A GET_B_3 LD A,C ;уже есть 63 CP 63 ;одинаковых байта? JR Z,GET_B_1 CALL GET_PIX ;берем еще байт JR C,GET_B_4 LD HL,BYTE_B ;сравниваем... CP (HL) JR NZ,GET_B_4 INC C JR GET_B_3 GET_B_4 CALL PREV_PIX ;сдвиг назад GET_B_2 LD A,(BYTE_B);байт выглядит AND %11000000 ;как CP %11000000 ;байт-счетчик? JR Z,GET_B_1 LD A,C ;одно DEC A ;повторение? JR NZ,GET_B_1 LD A,(BYTE_B) AND A ;сбрас. флаг C JR GET_EXIT GET_B_1 LD A,2 LD (STATUS),A LD A,C OR %11000000 ;сбрас. флаг C JR GET_EXIT ;2-е состояние: GET_BYTE_2 LD A,1 LD (STATUS),A LD A,(BYTE_B) AND A ;сбрас. флаг C GET_EXIT POP HL POP DE POP BC RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 03 Oct 2002
Hello, All! ═══════════════════ 35 .C ══════════════════ ;--------------------------------------- ;Процедура GET_PIX последовательно ;возвращает данные о пикселах. Если ;флаг C установлен - значит, данные ;кончились. ; ;BC,DE,HL не изменяются. GET_ADR DS 2 ;адрес в банке памяти GET_BANK DS 1 ;номер банка в таблице ;Инициализация: INIT_PIX LD HL,#C000-1 LD (GET_ADR),HL XOR A LD (GET_BANK),A RET ;Пошла сама процедура: GET_PIX PUSH HL LD HL,(GET_ADR) INC HL LD (GET_ADR),HL LD A,H OR L JR NZ,GET_PIX_1 LD HL,#C000 LD (GET_ADR),HL LD A,(GET_BANK) INC A LD (GET_BANK),A CP 3 JR NZ,GET_PIX_1 POP HL SCF RET GET_PIX_1 PUSH BC PUSH DE LD BC,#7FFD LD HL,N_BANK LD A,(GET_BANK) LD D,0 LD E,A ADD HL,DE LD A,(HL) OUT (C),A LD HL,(GET_ADR) LD A,(HL) LD D,#10 OUT (C),D POP DE POP BC POP HL AND A ;сбрас. флаг C RET ;Сдвиг на пиксел назад: PREV_PIX PUSH HL LD HL,(GET_ADR) DEC HL LD A,H CP #BF JR NZ,PREV_PIX_1 LD A,L CP #FF JR NZ,PREV_PIX_1 LD HL,#FFFF LD A,(GET_BANK) DEC A LD (GET_BANK),A PREV_PIX_1 LD (GET_ADR),HL POP HL RET ;Банки данных с информацией о пикселах: N_BANK DB #10,#11,#13 ;--------------------------------------- ;Процедуры для побайтной записи ;содержимого файла с буферизацией. IN_BUF DS 1 ;кол-во байтов в ;буфере DISK_ADR DS 2 ;адрес сектора ;OPEN_FILE - открыть файл. OPEN_FILE XOR A LD (IN_BUF),A RET ;PUT_BYTE - записать байт в файл. ;Вход: A - записываемое значение. ;Выход: регистры не изменены. PUT_BYTE PUSH AF PUSH BC PUSH DE PUSH HL LD B,A LD A,(IN_BUF) LD HL,FILE_BUFER LD D,0 LD E,A ADD HL,DE LD (HL),B INC A LD (IN_BUF),A ;Если буфер заполнился - записываем его: CALL Z,CLOSE_FILE POP HL POP DE POP BC POP AF RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 03 Oct 2002
Hello, All! ═══════════════════ 36 .C ══════════════════ ;CLOSE_FILE - закрыть файл. ;Содержимое буфера записывается на диск. CLOSE_FILE LD DE,(DISK_ADR) PUSH DE LD B,1 LD C,6 LD HL,FILE_BUFER CALL #3D13 POP DE INC E LD A,E AND 15 LD E,A JR NZ,CLOSE_EXIT INC D CLOSE_EXIT LD (DISK_ADR),DE RET ;--------------------------------------- ;128-байтный заголовок: HEADER db #0A,#05,#01,#08,#00,#00,#00,#00 db #FF,#00,#BF,#00,#00,#01,#C0,#00 db #60,#61,#62,#63,#64,#65,#66,#67 db #68,#69,#6A,#6B,#6C,#6D,#6E,#6F db #70,#71,#72,#73,#74,#75,#76,#77 db #78,#79,#7A,#7B,#3F,#00,#00,#00 db #50,#D6,#B0,#01,#14,#01,#00,#00 db #C5,#B9,#F7,#BF,#C4,#30,#92,#81 db #01,#01,#00,#01,#01,#00,#00,#00 db #00,#00,#00,#00,#00,#00,#00,#00 db #00,#00,#00,#00,#00,#00,#00,#00 db #00,#00,#00,#00,#00,#00,#00,#00 db #00,#00,#00,#00,#00,#00,#00,#00 db #00,#00,#00,#00,#00,#00,#00,#00 db #00,#00,#00,#00,#00,#00,#00,#00 db #00,#00,#00,#00,#00,#00,#00,#00 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 03 Oct 2002
Hello, All! ═══════════════════ 37 .C ══════════════════ 3. SCR2BANK - преобразование изображения в спектрумовском формате в черно-белое изображение с 256 градациями яркости, расположенное в трех банках памяти. С помощью предыдущей процедуры полученное изображение можно записать в pcx-файл. Вход: исходное изображение загружено с #4000, номера банков памяти - в N_BANK, яркости градаций - в BRIGHT_0 и BRIGHT_1. ORG #8000 SCR2BANK LD HL,N_BANK LD (PUT_B_1),HL LD HL,#C000 LD (PUT_B_2),HL LD DE,0 MAIN CALL GET_PIX LD B,A AND 7 ;индикация OUT (254),A ;на бордюре LD A,B CALL PUT_B INC D JR NZ,MAIN INC E LD A,E CP 192 JR NZ,MAIN RET ;Hомера используемых банков памяти: N_BANK DB #10,#11,#13 ;--------------------------------------- ;Процедура GET_PIX возвращает яркость ;указанного пиксела изображения. ; ;Вход: DE - координаты пиксела. ;Выход: A - яркость (0..255). GET_PIX PUSH DE ;Определяем INK, PAPER, BRIGHT: LD H,0 LD A,E AND %11111000 LD L,A ADD HL,HL ADD HL,HL LD A,D RRA RRA RRA AND %00011111 LD E,A LD D,#58 ADD HL,DE LD A,(HL) RRA RRA AND %00010000 LD (BRIGHT),A LD A,(HL) AND 7 LD (INK),A LD A,(HL) RRA RRA RRA AND 7 LD (PAPER),A POP DE PUSH DE CALL BYTE AND (HL) LD A,0 PAPER EQU $-1 JR Z,GET_PIX_1 LD A,0 INK EQU $-1 GET_PIX_1 ADD A,A ADD A,0 BRIGHT EQU $-1 LD E,A LD D,0 LD HL,BRIGHT_0 ADD HL,DE LD E,(HL) INC HL LD D,(HL) LD HL,#80 ADD HL,DE LD A,H POP DE RET BYTE LD A,E AND A RRA SCF RRA AND A RRA XOR E AND #F8 XOR E LD H,A LD A,D RLCA RLCA RLCA XOR E AND #C7 XOR E RLCA RLCA LD L,A LD A,D AND 7 LD B,A INC B LD A,1 LOOP RRCA DJNZ LOOP RET ;--------------------------------------- ;Яркости градаций в формате 8.8: BRIGHT_0 DW 0,1112,2139,4412,6062 DW 14555,26811,46556 BRIGHT_1 DW 0,1369,3129,6062,8995 DW 21704,38045,65280 ;--------------------------------------- ;Процедура PUT_B - запись в память ;информации об очередном пикселе. ; ;Вход: A - значение пиксела. PUT_B PUSH AF ;Устанавливаем LD A,(0) ;нужный PUT_B_1 EQU $-2 ;банк LD BC,#7FFD ;памяти. OUT (C),A POP AF LD HL,0 ;Записываем. PUT_B_2 EQU $-2 LD (HL),A INC HL ;Увеличиваем LD A,H ;адрес. OR L JR NZ,PUT_B_3 LD HL,(PUT_B_1) ;Переход к INC HL ;следующему LD (PUT_B_1),HL ;банку. LD HL,#C000 PUT_B_3 LD (PUT_B_2),HL RET ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 03 Oct 2002
Hello, All! ═══════════════════ 37 .C ══════════════════ 3. SCR2BANK - преобразование изображения в спектрумовском формате в черно-белое изображение с 256 градациями яркости, расположенное в трех банках памяти. С помощью предыдущей процедуры полученное изображение можно записать в pcx-файл. Вход: исходное изображение загружено с #4000, номера банков памяти - в N_BANK, яркости градаций - в BRIGHT_0 и BRIGHT_1. ORG #8000 SCR2BANK LD HL,N_BANK LD (PUT_B_1),HL LD HL,#C000 LD (PUT_B_2),HL LD DE,0 MAIN CALL GET_PIX LD B,A AND 7 ;индикация OUT (254),A ;на бордюре LD A,B CALL PUT_B INC D JR NZ,MAIN INC E LD A,E CP 192 JR NZ,MAIN RET ;Номера используемых банков памяти: N_BANK DB #10,#11,#13 ;--------------------------------------- ;Процедура GET_PIX возвращает яркость ;указанного пиксела изображения. ; ;Вход: DE - координаты пиксела. ;Выход: A - яркость (0..255). GET_PIX PUSH DE ;Определяем INK, PAPER, BRIGHT: LD H,0 LD A,E AND %11111000 LD L,A ADD HL,HL ADD HL,HL LD A,D RRA RRA RRA AND %00011111 LD E,A LD D,#58 ADD HL,DE LD A,(HL) RRA RRA AND %00010000 LD (BRIGHT),A LD A,(HL) AND 7 LD (INK),A LD A,(HL) RRA RRA RRA AND 7 LD (PAPER),A POP DE PUSH DE CALL BYTE AND (HL) LD A,0 PAPER EQU $-1 JR Z,GET_PIX_1 LD A,0 INK EQU $-1 GET_PIX_1 ADD A,A ADD A,0 BRIGHT EQU $-1 LD E,A LD D,0 LD HL,BRIGHT_0 ADD HL,DE LD E,(HL) INC HL LD D,(HL) LD HL,#80 ADD HL,DE LD A,H POP DE RET BYTE LD A,E AND A RRA SCF RRA AND A RRA XOR E AND #F8 XOR E LD H,A LD A,D RLCA RLCA RLCA XOR E AND #C7 XOR E RLCA RLCA LD L,A LD A,D AND 7 LD B,A INC B LD A,1 LOOP RRCA DJNZ LOOP RET ;--------------------------------------- ;Яркости градаций в формате 8.8: BRIGHT_0 DW 0,1112,2139,4412,6062 DW 14555,26811,46556 BRIGHT_1 DW 0,1369,3129,6062,8995 DW 21704,38045,65280 ;--------------------------------------- ;Процедура PUT_B - запись в память ;информации об очередном пикселе. ; ;Вход: A - значение пиксела. PUT_B PUSH AF ;Устанавливаем LD A,(0) ;нужный PUT_B_1 EQU $-2 ;банк LD BC,#7FFD ;памяти. OUT (C),A POP AF LD HL,0 ;Записываем. PUT_B_2 EQU $-2 LD (HL),A INC HL ;Увеличиваем LD A,H ;адрес. OR L JR NZ,PUT_B_3 LD HL,(PUT_B_1) ;Переход к INC HL ;следующему LD (PUT_B_1),HL ;банку. LD HL,#C000 PUT_B_3 LD (PUT_B_2),HL RET ════════════════════════════════════════════════ С уважением, Иван Рощин.




Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Реклама - Реклама и объявления ...
Обмен опытом - gamemaking: о создание игры на примере "Full Shit".
Игры - новелла к игре "Приключения Винни Пуха".
Раскрутка - игры Dizzy 7, Вignose`s USA adventure.
NEOS - NEOS о глюках и не только.

В этот день...   8 мая