ZX-Ревю 1996 №4-5 1996 г.

Читатель - читателю - multicolor - эффекты на бордюре и кое-что еще.


ЧИТАТЕЛЬ - ЧИТАТЕЛЮ

© Сергей Кучкин, г. Москва

MULTICOLOR - ЭФФЕКТЫ НА БОРДЮРЕ И КОЕ-ЧТО ЕЩЕ.

Как ни странно, эта тема на страницах журнала почти всегда обходилась стороной. Между тем, в последнее время MULTICOLOR встречается все чаще, а уж без бордюрных эффектов не обходится ни одна музыкальная демонстрация. Определимся с сутью вопроса:

Multicolor - расположение в одном знакоместе (8x8) более чем двух цветов (статическое либо динамическое);

Эффекты на бордюре - статические либо динамические изображения на бордюре.

Принцип, положенный в основу этих эффектов, довольно прост. Дело в том, что изменения в экранной памяти отображаются не мгновенно, а только при очередной прорисовке кадра (каждые 20 мс). Поэтому, быстро варьируя значениями атрибутов, можно добиться желаемого результата. Но тут начинаются проблемы.

1. Аппаратная несовместимость и проблемы синхронизации.

Чтобы хоть как-то компенсировать свою некомпетентность в аппаратных вопросах, я проштудировал все публикации ZX-РЕВЮ на тему ВДТа, порта #FF и тому подобных вещей. Оставшиеся вопросы были проверены программно, но с одним ограничением: под рукой у меня есть ТОЛЬКО ПЕНТАГОН-128, и тестировал я ТОЛЬКО его.

1.1. Сплошные и раздельные поля памяти: конфликт Z80 и ULA. (Историческая справка).

При работе процессора, в блоке экранной памяти может возникнуть ситуация, когда запрос Z80 произойдет одновременно с запросом ULA (которая автоматически обновляет экран). В самых первых моделях Спектрума этот вопрос разрешался в пользу ULA. При этом на процессор переставали подаваться тактовые импульсы (другой метод состоял в подаче сигнала WAIT). Чем лучше была синхронизирована схема компьютера, тем реже возникали конфликты, и тем быстрее работал процессор.

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

Если память компьютера организовывалась на одной микросхеме в 64К (речь пока идет о 48-ых Спек-трумах), то конфликт возникал во всем адресном пространстве ("Москва - 48"). Если же память организовывалась на трех микросхемах по 16К, то конфликт возникал только в экранном блоке #4000-#8000 ("Пентагон-48?").

Известно, также, что в фирменных 128-ых моделях Спектрума страницы памяти 0-3 были бесконфликтными (не состязательными), а 4-7 конфликтными (состязательными).

Позднее появились машины с раздельными полями памяти ("Пентагон-128"). В них для ULA отвели отдельное Видео-ОЗУ, и она перестала тормозить процессор. Программы одинаково быстро заработали во всем адресном пространстве, что гораздо упростило синхронизацию при работе с экраном.

1.2. Экран: 312 против 320. Синхронизация по INT'y.

Сигнал на перерисовку экрана - кадровый синхроимпульс вырабатывается каждые 20 миллисекунд. По его заднему фронту идет INT (сигнал прерывания) (тут я сошлюсь на Кирилла Громова, ZX-РЕВЮ 95/3).

Длительность INT'a по классическим меркам должна составлять 8-9 мкс, то есть 28-32 такта. Но это теория. На практике же начало INT'a может и не совпадать с концом синхроимпульса, а длительность кое-где достигает 15 мкс (52-53 такта). Главное для нас сейчас только чтобы эти величины были постоянными.

кс

КС

INT

Прорисовка экрана начинается с самого верха бордюра и заканчивается в самом его низу. В моем Пентагоне экран состоит из 320 строк по 224 такта на каждую строку. Таким образом, всего получается 320x224=71680 тактов/экран (очевидно, столько же тактов/INT). Однако, существует другой вариант, считающийся более правильным (?). Он реализован, например, в "ПРОФИ". Это 312 строк в экране. Если сделать логичное предположение, что одна строка прорисовывается также за 224 такта, то получим 312x224=69888 тактов/INT. Если Вы захотите проверить число строк на экране своего компьютера, то можете воспользоваться предложенной ниже программой LINETEST. После вызова из Бейсика PRINT USR ... она вернет количество строк в паре BC (исходя из 224 тактов в строке). У меня этот тест дал, как и ожидалось, 320. Проверить его на "Профи", к сожалению, не было возможности.

; LINETEST 312/320/...?

ORG

#8000

ENT

$

DI

;

3апрещаем прерывания.

LD

HL,INT

Останавливаем адрес программы обработки

LD

(#80FF),HL

прерываний на процедуру INT.

LD

A, #80

LD

I, A

IM

2

LD

DE,0 ;

Обнуляем счетчик строк.

EI

Разрешаем прерывания.

HALT

Ждем очередного синхроимпульса.

LD

HL,INT2

Переопределяем обработку прерываний

LD

(#80FF),HL

на процедуру INT2, которая остановит выполнение после прихода следующего синхроимпульса.

INC

DE ;

Увеличиваем счетчик строк.

LD

в,15 ;

Пропускаем необходимые такты.

DJNZ

Ml

LD

A, R

!!! команда для пропуска еще 9 тактов!

JR

М0 ;

Цикл замкнулся.

EI

RET

POP

HL ;

Снимаем со стека адрес возврата.

LD

С, E

Полученное число строк

LD

B,D ;

пересылаем в вС.

IM

0

возвращаемся

EI

в

RET

Бейсик.

М0

Ml

INT

INT2

Обратите внимание! Здесь и далее комментарием «!!!» у меня обозначены команды, выполняемые вхолостую (только ради тактов).

Стоит заметить, что при выполнении процедуры INT, прерывание может захватываться повторно (даже при нормальной длине сигнала INT), но это не страшно, так как лишние 30-40 тактов не способны повлиять на результат теста. (Потенциально возможны ситуации, когда в экране умещается нецелое число строк, например, 312.5; в подобных случаях тест ошибется, если, конечно, не произвести в нем соответствующих изменений).

Теперь я приведу тактовый "портрет" экрана моего Пентагона.

224 такта

80 строк

Экран

69 тактов1

Бордюр

192 строки

48 строк

I 128 I

тактов ■ '

Отсюда видно, например, что каждая строка "маленького" экрана (256x192) прорисовывается отдельно. А вот пиксели в строке рисуются по два за такт (256 пикселов за 128 тактов).

Можно заметить, что верхнему левому углу "малого" экрана соответствует 69-й такт 80 строки. Эти величины как раз и характеризуют положение INT^ относительно кадрового синхроимпульса. Не исключено, что они колеблются даже у компьютеров одного типа (однако это не факт). Кстати, эти 69 тактов долго не давали мне покоя (число ведь не круглое!). Я перемерял так и сяк, но результат был один и тот же. В конце концов, я решил, что это связано с длиной кадрового синхроимпульса (ведь INT идет позже начала перерисовки экрана как раз на этот интервал времени, а я отсчитывал такты, разумеется, от INT'a).

Как же синхронизировать программу с перерисовкой экрана? Например, просто HALT'ом:

LD HL,INT

27 1 тактов

; INT - адрес программы обработки

LD LD LD IM EI

HALT INT ...

Вот и все, программа придет на INT почти сразу после начала перерисовки экрана. Теперь выводите, например, в порт #FE каждые 71680 (69888) тактов что-нибудь, и у Вас готова картинка на бордюре. Вставив небольшую первоначальную задержку легко разместить бордюрную картинку там, где нужно. При желании, можно даже заставить ее двигаться (варьируя число тактов между перерисовками). В «Приложении Б» приведен пример программы CURSOR, которая делает нечто подобное плюс еще кое-что.

1.3. Синхронизация с помощью порта #FF: пара цитат.

Порт #FF! По своей популярности он давно уже побил все рекорды. Корреспонденты приходили и уходили, а порт #FF успешно просуществовал на страницах ZX-РЕВЮ аж с 1991 года и до наших дней! Причем редкий номер не обходился без нового "окончательного и бесповоротного" решения проблемы. Так в чем же суть?

В фирменном Спектруме при чтении из порта #FF (как впрочем, и из любого другого несуществующего порта) считываются атрибуты, выводимые в данный момент на экран, попеременно с кодом #FF. Почему это происходит, объяснили уже давно (ZX-РЕВЮ за 1991 год).

Многие отечественные компьютеры (если не сказать, подавляющее большинство) не имеют порта #FF (то есть, из него ВСЕГДА читается код #FF). И не было бы много печали, если бы некоторые программы не использовали метод синхронизации с помощью этого порта. Его суть вот в чем: WAIT IN A,(#FF) ; Этот цикл выполняется до тех пор,

INC А ; пока в порту не появится байт атрибутов

JR Z,WAIT ; (то есть началась перерисовка экрана).

Очевидно, что при отсутствии порта #FF программа просто повиснет.

Существуют тесты, позволяющие проверить наличие этого порта у Вас (у меня, например, порта нет, что и заставляет меня прекратить рассказ, не подтвержденный экспериментами).

1.4. INT и его повадки: важные мелочи.

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

Запустив мою программу CURSOR, Вы должны были заметить, что "бордюрная" часть курсора постоянно либо "убегает", либо отстает от "экранной" (поэтому и была введена клавиша '0'). Такое поведение курсора объясняется не просчетом в программе, а свойством системы прерываний.

Дело в том, что когда приходит сигнал INT, процессор может быть занят выполнением команды. В таком случае он сначала завершит команду, а только потом перейдет по прерыванию. Задержка может составлять от 1 до 20-26 (?) тактов в зависимости от выполняемой команды. Между тем, прорисовка экрана уже идет полным ходом, то есть переход по INT запаздывает. В случае с HALT'ом (который кто-то назвал NOP'ом без увеличения PC), задержка может составлять 0, 1, 2 или 3 такта. Если вспомнить, что за один такт прорисовываются два пикселя экрана, то за 3 такта: 2x3=6 - почти целое знакоместо!

Возникает вопрос, можно ли избавиться от этого эффекта программно? Ответ: можно. Идея реализована в приведенной ниже программе FILTER. Она использует ячейки #81FF, #8200, изменяет I и некоторые другие регистры. После вызова CALL FILTER сигнал INT соответствует началу следующей команды (но прерывания запрещены).

;INT FILTER ONLY FOR PENTAGON-12 8

(#XXFF),HL ; прерываний. A, #XX I,A 2

ORG

#8000

ENT

$

DI

LD

HL,INT

; Останавливаем процедуру обработки

LD

(#81FF),HL

; прерываний INT.

LD

A,0

; Очищаем дно буфера.

LD

(BUFF+4),A

LD

E, l

; Счетчик установлен в 1.

LD

HL,BUFF

; HL - указатель на первый свободный

LD

A, #81

; байт в буфере.

LD

I,A

IM

2

Ждем прерывание. Теперь у нас задержка INTa 0, 1, 2 или 3 такта, а в (HL)=1 в любом случае. 3адержкана71677тактов. Таким образом перед прерыванием успеет начать выполняться INC E для задержек 0, 1 и 2, а для 3 уже нет. В результате: (HL)=2 для случаев 0, 1 и 2 и (HL)=1 для случая 3. 2

Аналогичный "отсев" по одному.

3

4

EI

HALT CALL

WAIT

INC

CALL

INC

CALL

INC

DI

E

WAIT E

WAIT E

3апрещаем прерывания. Теперь расклад такой (см. далее таблицу):

Случай

BUFF+0

BUFF+1

BUFF+2

BUFF+3

0

1

2

3

4

1

1

2

3

3

2

1

2

2

3

3

1

1

2

3

Осталось проверить ячейки и сделать

соответствующие задержки.

LD

BC,2749

В BC - число повторений в цикле задержки ((ВС-1)х26+21 тактов).

LD

HL,(BUFF+1)

EX

DE, HL

LD

HL,(BUFF+3)

LD

A, H

OR

A

JR

NZ, F2

Если в (BUFF+4) не ноль, то произошел гипотетический случай: прерывание" вклинилось" между EI и HALT. (См. начало программы). Это приведет лишь только к сдвигу данных на один байт, что и устраняем).

F0

LD CP

A, L 4

JR

Z,DISP3 ;

Начальное смещение 0!

LD

A, D

CP

L

JR

Z,DISP2 ;

Начальное смещение 1!

LD

А, E

CP

D

JR

Z,DISP1 ;

Начальное смещение 2!

DISP0

NOP

LD

LD

R, A R, A

!!! Обработка смещения 3. ! ! ! ! ! !

Fl

DEC

BC

Ждем рассчитанное число тактов до

LD

А, В ;

следующего INT'a

OR

C

JR

NZ, F1

RET

Возврат.

F2

LD

E,D

B случае, если прерывание "вклинилось" между

LD

D, L

EI и HALT (в начале),

LD

L,H

надо сдвинуть все на 1 байт.

DEC

BC

Уменьшаем счетчик задержки, компенсируя время,

DEC

BC

потраченное на перезагрузки

LD

A, (HL)

! ! !

NOP

! ! !

JR

F0

DISP1

DEC

BC

Обработка смещения 2.

NOP

! ! !

JR

DISP0

DISP2

JR

DISP0 ;

Обработка смещения 1.

DISP3

LD

R, A

;

!!! Обработка смещения 0.

LD

A, (HL)

;

! ! !

JR

DISP0

WAIT

LD

BC,2753

;

Задержка на 71617 тактов

Wl

DEC

BC

;

(включая время на вызов CALL WAIT)

LD

A, B

OR

C

JR

NZ,W1

LD

B, (HL)

;

!!!

RET

INT

LD

(HL),E

INC

HL

LD

D,(HL)

;

!!!

LD

D,(HL)

;

!!!

EI

RET

BUFF

DEFS

5

;

Буфер для хранения результатов.

Процедура довольно "сырая" (как, впрочем, и все программки в этой статье).

В принципе, ее несложно переработать и для "Профи" (69888 тактов/INT), но я не стал этого делать из-за невозможности отладки на "Пентагоне".

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

Программу CURSOR, модернизированную с помощью "фильтра", Вы найдете в «Приложении Б» под названием SCURS. Теперь ничто ни от чего не отстаёт, все в порядке (за исключением мелочи: бордюрное изображение у меня всегда отстает от экранного примерно на 1 пиксель, то есть 0.5 такта, что, очевидно, программно исправить - невозможно).

Правда, у FILTER'a есть недостаток - он долго работает (несколько прерываний). Таким образом, с его помощью возможна только первоначальная синхронизация, а когда кадры пропускать нельзя, Вам придется выкручиваться без него.

2. MULTICOLOR.

Может сложиться впечатление, что multicolor - вещь редкостная, и, ввиду своей сложности, является уделом музыкальных демонстраций и заставок. Я попытаюсь доказать, что это не так.

Что же нужно для multicolor'a?

a) Синхронизироваться с началом перерисовки экрана;

b) Дождаться перерисовки атрибутов (то есть пропустить, как минимум, первые 80 строк);

c) Дать компьютеру прорисовать первую "полоску" атрибутов, но прежде, чем он доберется до второй, заменить атрибуты на другие.

Таким образом, в одном знакоместе можно разместить до 2x8=16 цветов, то есть, все цвета Спектрума! Цвет задается отдельно для каждого байта знакоместа. Осталось только повторять подобный трюк в каждом кадре, и у Вас на экране полноценный multicolor!

Кстати, для обладателей "старого доброго" RESET'a могу порекомендовать надежный тест. Если Вы сомневаетесь: multicolor перед Вами или обман, то жмите RESET, но не отпускайте его некоторое время. При этом multicolor "рассыплется", а обычная картинка будет преспокойно висеть на экране.

Возможно, у Вас возникнет желание перерисовывать атрибуты настолько быстро, чтобы получился multicolor в пределах одного байта знакоместа. Но хочу Вас огорчить - этот трюк не удастся. Горизонтальный multicolor невозможен, и вот почему.

Байт из экранной памяти вместе со своим атрибутом перед выводом на экран попадает в специальный сдвиговый регистр, недоступный программно. Между считыванием байта из памяти и засылкой его на экран -интервал в 3 такта. Таким образом, первый байт экрана считывается на 66-м такте 80-й строки (нумеруя с 0). Причем любые изменения памяти в промежутке между считыванием и изображением станут заметны только в следующем кадре.

Но вернемся к multicolor'y вертикальному. Тут не все так мрачно, хотя есть и свои проблемы. Небольшой расчет: прорисовка строки экрана занимает 224 такта; переброска 1 байта с помощью LDI (LDD) занимает 16 тактов; 224/16=14, то есть, максимум 14 знакомест с multicolor'oM в строке (это даже без учета установок для LDI, а с ними и того меньше). Что же делать?

Как всегда, выручает стек. Придется, также, отбросить очень медленные команды перехода, то есть развернуть все циклы. У нас получится "рыхлый" код размером в несколько килобайт, но игра стоит свеч.

Есть две проблемы: каким способом брать атрибуты из памяти и каким класть их на экран (трудно нагрузить и то и другое на один стек). Однозначного ответа быть не может. Если Вас устраивает жесткая фиксация области multicolor'a на экране, то Вы немного выигрываете в скорости:

LD SP,#XXXX ; #XXXX - адрес в памяти

POP HL

LD (#YYYY),HL

POP HL

LD (#ZZZZ),HL ; #YYYY, #ZZZZ адреса на экране

и так далее для всей области. Замечу, что POP быстрее, чем PUSH на 1 такт; а LD (#ADDR),HL быстрее LD (#ADDR),DE на 4 такта. При такой процедуре вывода достаточно изменить атрибут в памяти, и при следующей прорисовке он изобразится на экране. Теперь подсчет: "POP HL" + "LD (#YYYY),HL" = 26 тактов; 224=8x26+16. To есть, получаем 2x8=16 знакомест (каждый "POP HL" даёт два), плюс еще 16 холостых тактов. Следующий метод позволяет более гибко менять положение области тикисо1ог'а на экране: LD SP,#ADDR ; #ADDR - адрес на экране

LD HL,(#XXXX)

PUSH HL

LD HL,(#YYYY) ; #XXXX, #YYYY адреса в памяти

PUSH HL

и так далее для всей области. Обратите внимание, что в отличие от предыдущего случая, где могла быть только одна команда загрузки SP, здесь такая команда нужна при выводе каждой строчки (из-за причудливости спек-трумовского экрана). Зато для изменения местоположения multicolor'a достаточно изменить "только" все #ADDR. Это немало, но сравните с предыдущим случаем. Теперь расчет: "LD HL,(#XXXX)" + "PUSH HL" = 27тактов и еще 10 тактов выполняется "LD SP,#ADDR", итого: 224-10=214=7x27+25. То есть, получаем 2x7=14 знакомест в строке и 25 холостых тактов (право, обидно!).

Может возникнуть резонный вопрос: "А нельзя ли косвенно адресовать экран, то есть, вместо LD SP,#ADDR написать LD SP,(#ADDR)?" Ответ: «Нельзя». То есть, конечно, можно, но придется пожертвовать еще парой знакомест. Дело в том, что направление прорисовки экрана и направление заполнения стека противоположны. Вследствие этого, во избежание простоя, мы должны перегружать SP не один, а два раза за строку.

Подводя итог вышесказанному, осталось заметить, что алгоритмы, аналогичные вышерассмотренным реализованы в программах MAXSCM и 16Х16МС. Первая представляет собой реализацию multicolor'a для любого рисунка размером 18x14 знакомест. С моей точки, зрения это максимально широкий по X multicolor, реализуемый для произвольных атрибутов (в специфических случаях, безусловно, его можно расширить). По Y размер картинки некритичен, и может, в принципе, достигать' всего экрана. 16Х16МС реализует multicolor в квадрате 16x16 по первому из рассмотренных алгоритмов. Обе программы сначала создают сам работающий блок, а потом его запускают. При этом на экране может быть любое изображение, а файл атрибутов multicolor'a должен находиться по адресу 49152. Чуть подробнее программы описаны в «Приложении Б».

После прочтения предыдущих абзацев у Вас, должно быть, сложилось впечатление, что все свое время процессор должен посвятить multicolor'y, забросив остальные дела. Но это не так! Рассмотрим, например, multicolor 16x16 (по площади 1/3 экрана). На каждый кадр приходится 42956 тактов "безделья", что ни много, ни мало, а почти 3/5 всего времени процессора. Его можно занять, например, полным вертикальным scro11'ом этого самого квадрата 16x16 с точностью до точки, пустить текст нестандартным шрифтом со скоростью 5 строк в секунду (больше, чем достаточно), и еще время останется!

Как видно из всего вышесказанного, multicolor открывает поистине необозримые возможности для создания мелкой цветной графики. Остается только удивляться, что они не использованы до сих пор. Кстати, второй экран 128 компьютера мне так и не удалось задействовать для улучшения работы multicolor'a. Вот если бы разрешалось фиксировать экран на любой из 8 страниц... Тогда ПОЛНОЭКРАННЫЙ multicolor сводился бы к простой цепочке OUT^ в порт #FD (несмотря на то, что этот OUT делает очень многое, он выполняется не дольше обычного).

Быть может, широкому распространению multicolor'a мешает аппаратная несовместимость? Но этот момент преодолим. Сейчас я разрабатываю систему multicolor'a, автоматически распознающую компьютер, и подстраивающуюся под него. Но тут катастрофически не хватает информации по распространенным синхросисте-мам. Если кто-нибудь заинтересуется подробным вопросом, то мой адрес и телефон даны ниже. Пишите, звоните!

3. Вместо послесловия.

Кучкин Сергей

121096, г. Москва, ул. Минская, д.14, кв.102; тел. 144-26-00

Приложение А.

Программируя multicolor, я столкнулся с одной проблемой: все имеющиеся у меня готовые процедуры теперь не годились, поскольку время их выполнения зависело от начальных условий. Так, например, при написании scroller'a под тиШсо1ог'ом мне пришлось заново переписывать вывод нестандартным шрифтом, опрос клавиатуры и все подобные мелочи.

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

Приходится минимизировать число условных переходов и вызовов, которые сильно осложняют ситуацию.

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

Способ 1: прямая коррекция времени.

Каждый условный переход должен превратиться в подобную конструкцию:

! ! ! Тут NOP и ... обозначают команды, выполняемые для выравнивания

времени в случае соблюдения условия !!! и в случае его несоблюдения.

MC,M1

JP NOP

JP NOP

M2

Ml

Этот способ хорош при небольшом числе необходимых переходов. Но вот с САЬЬ'ами он справиться уже не может. Тут применим способ 2.

Способ 2: косвенная коррекция времени.

Один из регистров (регистровых пар) или ячейка памяти отводится под счетчик задержки в конце программы. Вначале в этот счетчик заносится время задержки, характерное для самой быстрой работы (не происходило условных переходов и заходов в CALL'bi). При вызове каждая из подпрограмм корректирует этот счетчик в зависимости от времени, необходимого для ее выполнения. Поскольку единица счетчика, скорее всего, соответствует нескольким десяткам тактов, то тут не обойтись, также, без прямой коррекции. Но хватит слов. Вот пример:

LD

BC,#300

; Инициализируем счетчик.

CALL

NC,M1

CALL

Z,M2

DEC

BC

; Задержка в соответствии со счётчиком

LD

A, B

; (единица счетчика равна 2 6 тактам).

OR

C

JR

NZ,M3

RET

DEC

BC

; "Занимаем" у счетчика 52 такта,

DEC

BC

; необходимые для работы.

NOP

; !!! "Возвращаем" 4 лишних.

........................; Продолжаем

Однако, бывают случаи (особенно при адаптации готовых процедур), когда вносить изменения в структуру программы крайне нежелательно. Тогда применим самый трудоемкий способ:

Способ3: способ "супервизора". Когда нам необходимо вызвать процедуру, которая не заботится о времени своего выполнения, мы вызываем не ее, а специально приготовленную программу-супервизор. Эта программа анализирует переданные параметры, делает соответствующие задержки, и только потом вызывает процедуру.

Метод плох тем, что необходимо для каждого случая рассчитать число тактов выполнения, заложить эту информацию в "супервизор". И не удивительно, если он после этого получится больше самой программы. Что поделаешь, издержки неизбежны!

А теперь небольшой списочек команд, которыми можно забивать время в несколько тактов, если в этом возникнет необходимость:_

4 такта

NOP; LD reg8,reg8

5 тактов

RET <условие> (если условие заведомо ложно!)

6 тактов

INC reg16; DEC reg 16

7 тактов

LD reg8,(HL); CP #00; ADD A,#00

8 тактов

NEG; IM 0/1/2

9 тактов

LD R,A

10 тактов

LD reg16,#NNNN; INC IX; DEC IX

11 тактов

INC (HL); ADD HL,reg16; OUT (#NN),A

12 тактов

IN reg8,(C); OUT (C),reg8

13 тактов

LD A,(#NNNN)

14 тактов

LD IX,#NNNN

15 тактов

PUSH IX; ADD IX,reg16; ADC HL,reg16

16 тактов

CPI; CPD; LD HL,(#NNNN)

17 тактов

CALL #NNNN

18 тактов

RLD; RRD

19 тактов

LD reg8,(IX+d); CP (IX+d)

20 тактов

LD reg16,(#NNNN)

21 такт

Некоторые команды при специфических условиях

23 такта

RLC (IX+d); SET 0,(IX+d); EX (SP),IX

Обозначения:

reg8 - A,B,C,D,E,H,L (для IN, F);

regie - BC, DE, HL и AF либо SP (что допустимо);

#NN -байт;

#NNNN - слово;

d - смещение в командах с индексными регистрами. IX везде свободно заменяется на IY.

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

Отдельно следует заметить, что переход по маскируемому прерыванию занимает 19 тактов (без учета возможной задержки, то есть в случае, когда прерывание пришло перед началом выполнения команды). Приложение Б.

Первая программа LINETEST - возвращает в регистровой паре ВС число строк в экранном кадре компьютера (если в строке по 224 такта). Возможен вызов из Бейсика по PRINT USR

;LINETEST

M0

Ml

INT

INT2

312/320/...?

ORG

#8000

ENT

$

DI

LD

HL,INT

LD

(#80FF),HL

LD

A, #80

LD

I,A

IM

2

LD

DE,0

EI

HALT

LD

HL,INT2

LD

(#80FF),HL

INC

DE

LD

B, 15

DJNZ

M1

LD

A, R

JR

M0

EI

RET

POP

HL

LD

C, E

LD

B, D

IM

0

EI

RET

ВСЕ ОСТАЛЬНЫЕ ПРОГРАММЫ СИНХРОНИЗИРОВАНЫ ТОЛЬКО ПОД "ПЕТАГОН"!

Программа CURSOR позволяет перемещать курсор 3x3 знакоместа, как по экрану, так и по бордюру. В силу изложенных ранее причин курсор на бордюре может отставать от экранного. Ситуацию можно исправить, нажимая клавишу '0'. Управление синклер-джостиком, выход SPACE.

;CURSOR

ORG

#8000

ENT

$

DI

EXX

PUSH

HL

LD

HL,INT

LD

(#81FF),HL

LD

HL,#0509

LD

(X),HL

LD

BC,#2FF

LD

DE,#5801

LD

HL,#5800

LD

(HL),#0F

LDIR

LD

A, #81

LD

I,A

IM

2

EI

HALT

DI

LD

BC,473

M0

DEC

BC

LD

A, B

OR

C

JR

NZ,M0

INC

BC

INC

BC

LD

R, A

Ml

LD

C, #FE

LD

HL,#0107

CALL

DRAW

LD

BC,2334

CALL

INKEY

CALL

WAIT

LD

DE,0

LD

DE,0

LD

D,0

LD

BC,#7FFE

IN

D, (C)

RR

D

JR

C,M1

LD

BC,#2FF

LD

DE,#5801

LD

HL,#5800

LD

(HL),#0F

LDIR

POP

HL

EXX

IM

0

EI

RET

INT

EI RET

X

DEFB

#09

Y

DEFB

#05

DRAW

LD

E,#18

D1

LD

B, 14

D2

DJNZ

D2

OUT

(C),L

OUT

(C),H

DEC

E

JR

NZ, Dl

RET

DEC

BC

LD

A, B

OR

С

JR

NZ,WAITRET

LD

A, #0F

CALL

DRAW2

LD

A, #EF

IN

A,(#FE)

RRA

CALL

NC,FIRE

RRA

CALL

NC, UP

RRA

CALL

NC,DOWN

RRA

CALL

N,RIGHT

RRA

CALL

N,LEFT

LD

A, #38

CALL

DRAW2

RET

LD

HL, -2

ADD

HL, BC

LD

C, L

LD

B, H

LD

D,0

RET

LD

HL,-74

ADD

HL, BC

LD

C, L

LD

B, H

LD

E,A

LD

A, (Y)

DEC

A

CP

l

LD

(Y) , A

LD

A, E

CALL

Z,DOWN

LD

DE,0

LD

DE,0

LD

D,0

NOP

RET

LD

HL, 64

ADD

HL, BC

LD

C, L

LD

B, H

LD

E,A

LD

A, (Y)

INC

A

CP

#24

LD

(Y) , A

LD

A, E

CALL

Z,UP

LD

R, A

LD

R, A

LD

R, A

RET

LD

HL,#FFFC

ADD

HL, BC

LD

C, L

LD

B, H

LD

E,A

LD

A, (X)

INC

A

CP

#2F

LD

(X) , A

LD

A, E

CALL

Z,LEFT

LD

E, 0

RET

LD

HL,#FFFB

ADD

HL, BC

LD

C, L

LD

B, H

LD

E,A

LD

A, (X)

DEC

A

CP

6

LD

(X) , A

LD

A, E

CALL

Z,RIGHT

LD

R, A

LD

R, A

LD

D,0

RET

EX

AF,AF

PUSH

BC

LD

DE,(X)

LD

C, 3

LD

B, 3

LD

h, 0

LD

A, E

CP

12

RL

H

CP

44

CCF

RL

H

LD

A, D

LD

1,0

CP

8

RL

L

CP

32

CCF

RL

L

EXX

LD

C,A

EX

AF,AF

LD

B,A

LD

A, C

SUB

8

LD

L,A

LD

H, 0

ADD

HL, HL

ADD

HL, HL

ADD

HL, HL

ADD

HL, HL

ADD

HL, HL

EXX

LD

A, E

EXX

SUB

12

LD

E,A

LD

D,0

WAIT

INKEY

FIRE

UP

DOWN

RIGHT

LEFT

DRAW2

Rl

R2

ADD

HL, DE

LD

DE,#58

ADD

HL, DE

EXX

LD

A, H

OR

L

EXX

JR

NZ, R4

LD

DE,0

LD

(HL),B

LD

A, B

EX

AF,AF'

EXX

INC A

DJNZ

R2

INC

E

DEC

С

JR

NZ, R1

POP

BC

RET

JR

R3

R4

Программа - FILTER. Компенсирует непроизвольную задержку INT'a при ожидании исполнения команды. После ее вызова прерывания запрещены, а сигнал INT соответствует началу следующей команды. Портится содержимое I, и других регистров.

;INT FILTER

R3

ORG

#8000

ENT

$

DI

LD

HL,INT

LD

(#81FF),HL

LD

A,0

LD

(BUFF+4),A

LD

E, l

LD

HL,BUFF

LD

A, #81

LD

I,A

IM

2

EI

HALT

CALL

WAIT

INC

E

;2

CALL

WAIT

INC

E

;3

CALL

WAIT

INC

E

;4

DI

LD

BC,2749

LD

Hl,(BUFF+1)

EX

DE, HL

LD

HL,(BUFF+3)

LD

A, H

OR

A

JR

NZ, F2

LD

A, L

CP

4

JR

Z,DISP3

LD

A, D

CP

L

JR

Z,DISP2

LD

A, E

CP

D

JR

Z,DISP1

NOP

;

FILTER

F0

DISP0

LD

R, A

• ! ! ! ! ...

LD

R, A

• ! ! ! ! ...

F1

DEC

BC

LD

A, B

OR

С

JR

NZ,F1

RET

F2

LD

E, D

LD

D, L

LD

L, H

DEC

BC

DEC

BC

LD

A, (HL)

• ! ! ! ! ...

NOP

• ! ! ! ! ...

JR

F0

DISP1

DEC

BC

NOP

• ! ! ! ! ...

JR

DISP0

DISP2

JR

DISP0

DISP3

LD

R, A

• ! ! ! ! ...

LD

A, (HL)

• ! ! ! ! ...

JR

DISP0

WAIT

LD

BC,2753

W1

DEC

BC

LD

A, B

OR

С

JR

NZ,W1

LD

B, (HL)

• ! ! ! ! ...

RET

INT

LD

(HL),E

INC

HL

LD

D,(HL)

• ! ! ! ! ...

LD

D,(HL)

• ! ! ! ! ...

EI

RET

BUFF

DEFS

5

Программа

CURSOR, оснащенная фильтром INT'a. Управление синклер-джостиком,

Возможно отставание бордюрного курсора на один пиксель (что неустранимо программно).

;SCURS

ORG

#8000

ENT

$

JP

CURSOR

FILTER

DI

LD

HL,INT

LD

(#81FF),HL

LD

A,0

LD

(BUFF+4),A

LD

E, l

LD

HL,BUFF

LD

A, #81

LD

I,A

IM

2

EI

HALT

CALL

WAIT

INC

E

;2

CALL

WAIT

INC

E

;3

CALL

WAIT

INC

E

;4

DI

LD

B, 2749

LD

HL,(BUFF+1)

EX

DE, HL

LD

HL,(BUFF+3)

LD

A, H

OR

A

JR

NZ, F2

F0

LD

A, L

CP

4

JR

Z,DISP3

LD

A, D

CP

L

JR

Z,DISP2

LD

A, E

CP

D

JR

Z,DISP1

DISP0

NOP

LD

R, A

LD

R, A

F1

DEC

BC

LD

A, B

OR

С

JR

NZ, F1

RET

F2

LD

E, D

LD

D, L

LD

L, H

DEC

BC

DEC

BC

LD

A, (HL)

NOP

JR

FO

DISP1

DEC

BC

NOP

JR

DISP0

DISP2

JR

DISP0

DISP3

LD

R, A

LD

A, (HL)

JR

DISP0

WAIT

LD

BC,2753

W1

DEC

BC

LD

A, B

OR

С

JR

NZ, W1

LD

B, (HL)

RET

INT

LD

(HL),E

INC

HL

LD

D,(HL)

LD

D,(HL)

EI

RET

BUFF

DEFS

5

CURSOR

DI

EXX

PUSH

HL

LD

HL,#0509

LD

(X),HL

LD

BC,#2FF

LD

DE,#5801

LD

HL,#5800

LD

(HL),#0F

LDIR

CALL

FILTER

LD

BC,475

mo

DEC

BC

LD

A, B

OR

С

JR

NZ,M0

LD

R, A

Ml

LD

C, #FE

LD

HL,#0107

CALL

DRAW

LD

BC,2334

CALL

INKEY

CALL

WAIT2

LD

DE,0

LD

DE,0

LD

D,0

LD

BC,#7FFE

IN

D, (C)

RR

D

JR

C,M1

LD

BC,#2FF

LD

DE,#5801

LD

HL,#5800

LD

(HL),#0F

LDIR

POP

HL

EXX

IM

0

EI

RET

X

DEFB

#09

Y

DEFB

#05

DRAW

LD

E,#18

D1

LD

B, 14

D2

DJNZ

D2

OUT

(C),L

OUT

(C),H

DEC

E

JR

NZ,D1

RET

WAIT2

DEC

BC

LD

A, B

OR

С

JR

NZ,WAIT2

RET

INKEY

LD

A, #0F

CALL

DRAW2

LD

A, #EF

IN

A,(#FE)

RRA

CALL

NC,FIRE

RRA

CALL

NC, UP

RRA

CALL

NC,DOWN

RRA

CALL

NC,RIGHT

RRA

CALL

NC,LEFT

LD

A, #38

CALL

DRAW2

RET

FIRE

LD

HL, -2

ADD

HL, BC

LD

C, L

LD

B, H

LD

D,0

RET

LD

HL,-74

ADD

HL, BC

LD

C, L

LD

B, H

LD

E,A

LD

A, (Y)

DEC

A

CP

1

LD

(Y) , A

LD

A, E

CALL

Z,DOWN

LD

DE, 0

LD

DE, 0

LD

D,0

NOP

RET

LD

HL, 64

ADD

HL, BC

LD

C, L

LD

B, H

LD

E,A

LD

A, (Y)

INC

A

CP

#24

LD

(Y) , A

LD

A, E

CALL

Z,UP

LD

R, A

LD

R, A

LD

R, A

RET

LD

HL,#FFFC

ADD

HL, BC

LD

C, L

LD

B, H

LD

E,A

LD

A, (X)

INC

A

CP

#2F

LD

(X) , A

LD

A, E

CALL

Z,LEFT

LD

E, 0

RET

LD

HL,#FFFB

ADD

HL, BC

LD

C, L

LD

B, H

LD

E,A

LD

A, (X)

DEC

A

CP

6

LD

(X) , A

LD

A, E

CALL

Z,RIGHT

LD

R, A

LD

R, A

LD

D,0

RET

EX

AF,AF

PUSH

BC

LD

DE,(X)

LD

C, 3

LD

B, 3

UP

DOWN

RIGHT

LEFT

DRAW2

R1

LD

H, 0

LD

A, E

CP

12

RL

H

CP

44

CCF

RL

H

LD

A, D

LD

L,0

CP

8

RL

L

CP

32

CCF

RL

L

EXX

LD

C,A

EX

AF,AF'

LD

B,A

LD

A, C

SUB

8

LD

L,A

LD

H, 0

ADD

HL, HL

ADD

HL, HL

ADD

HL, HL

ADD

HL, HL

ADD

HL, HL

EXX

LD

A, E

EXX

SUB

12

LD

E,A

LD

D,0

ADD

HL, DE

LD

DE,#5800

ADD

HL, DE

EXX

LD

A, H

OR

L

EXX

JR

NZ, R4

LD

DE,0

LD

(HL),B

LD

A, B

EX

AF,AF

EXX

INC

A

DJNZ

R2

INC

E

DEC

С

JR

NZ, R1

POP

BC

RET

JR

R3

Программа для создания статического multicolor'a в отцентрованном прямоугольнике 18x14 знакомест. Изображение должно уже находиться на экране, а атрибуты - с адреса SCR=#C000 в формате 18 байт на каждую строку х 112 строк (по порядку).

;MAXIMUM STATIC MULTICOLOR (X)

ORG #8000 SCR EQU #C000

ENT $

LD DE,L4

LD IX,SCR

EXX

PUSH

HL

LD

HL,#58B1

LD

C,14

LD

(L1+1),HL

LD

DE,#0008

ADD

HL, DE

LD

(L2+1),HL

LD

DE,#0018

ADD

HL, DE

LD

B, 8

EXX

LD

HL,L2-3

LD

B, 5

LD

A, (IX+0)

INC

IX

LD

(HL),A

INC

HL

LD

A, (IX+0)

INC

IX

LD

(HL),A

DEC

HL

DEC

HL

DEC

HL

DEC

HL

DEC

HL

DJNZ

K1

LD

BC, L2-L1

LD

HL, L1

LDIR

LD

B, 4

LD

HL,L3-5

LD

A, (IX+0)

INC

IX

LD

(HL),A

INC

HL

LD

A, (IX+0)

INC

IX

LD

(HL),A

DEC

HL

DEC

HL

DEC

HL

DEC

HL

DEC

HL

DJNZ

K2

LD

BC, L3-L2

LD

HL, L2

LDIR

EXX

DJNZ

к0

DEC

С

JR

NZ,K_1

EXX

LD

B,L4-L3

LD

HL,L3LDIR

LD

HL,INT

LD

(#94FF),HL

LD

(FOR_SP),SP

LD

A, #94

LD

I,A

IM

2

EI

HALT

LD

BC,1026

K 1

ко

K1

K2

END

угольник multicolor'a отцентрирован и занимает 16x16 знакомест. Атрибуты (2048 байт) должны располагаться с адреса SCR по 16 байт на строку.

;16X16 MULTICOLOR

ORG #8000 SCR EQU #C000

ENT $

FOR_SP L1

DEC

BC

LD

A, B

OR

C

JR

NZ, K3

LD

bc,(0)

NOP

NOP

CALL

L4

POP

HL

EXX

IM

0

EI

RET

LD

bc,(0)

LD

bc,(0)

RET

DEFW

0

LD

SP,#0000

LD

DE,#0000

PUSH

DE

LD

DE,#0000

PUSH

DE

LD

DE,#0000

PUSH

DE

LD

DE,#0000

PUSH

DE

LD

DE,#0000

PUSH

DE

LD

SP,#0000

LD

DE,#0000

PUSH

DE

LD

DE,#0000

PUSH

DE

LD

DE,#0000

PUSH

DE

LD

DE,#0000

PUSH

DE

ADD

HL, HL

NOP

LD

B, 1790

DEC

BC

LD

A, B

OR

C

JR

NZ,L31

LD

R, A

INC

DE

LD

A, #7F

IN

A,(#FE)

RRCA

JP

C, L4

LD

SP,(FOR_SP)

DEC

SP

DEC

SP

RET

DEFS

4954

NOP

Программа аналогична пре

КЗ

INT

L2

L3 L31

EXX

PUSH

HL

LD

HL,#5888

LD

IX, L4

LD

A,16

LD

C,8

LD

DE,#0004

LD

B, 8

PUSH

HL

LD

(IX+0),#E1

LD

(IX+1),#22

LD

(IX+2),L

LD

(IX+3),H

ADD

IX, DE

INC

HL

INC

HL

DJNZ

K2

LD

B, E

LD

(IX+0),D

INC

IX

DJNZ

K3

POP

HL

DEC

С

JR

NZ, K1

LD

BC,#0020

ADD

HL, BC

DEC

A

JR

NZ, KO

PUSH

IX

POP

DE

LD

BQL3-L1

LD

HL, L1

LDIR

LD

HL,INT

LD

(#92FF),HL

LD

(FOR_SP),SP

LD

A, #92

LD

I,A

IM

2

EI

HALT

LD

BC,956

DEC

BC

LD

A, B

OR

С

JR

NZ, K4

LD

BC,(0)

NOP

CALL

L3

POP

HL

EXX

IM

1

EI

RET

RET

DEFW

0

LD

BC, 1652

DEC

BC

LD

A, B

OR

С

JR

NZ, L2

LD

R, A

LD

A, #7F

IN

A,(#FE)

ко

K1 K2

K3

K4

INT

FOR_SP

L1

L2

RRCA

JP

C, L3

LD

SP,(FOR_SP)

DEC

SP

DEC

SP

RET

L3

LD

SP,SCR

L4

DEFS

4633

END

NOP

P.S. Буквально на днях посмотрел "INSULT MEGADEMO" Code Busters. Полноэкранный скроллер в "секретной" части megademo не явился для меня откровением.

Ввиду всего вышеизложенного, теоретически вопрос ясен (хотя от теории до практики всегда далеко). Следует только заметить, что размеры scroИer'а по горизонтали нельзя существенно улучшить, поскольку быстрейшая команда вывода на бордюр OUT (#FE),A длится 11 тактов, а реально применимая OUT (C),reg8 -12 тактов, что представляет собой 12x24 точки экрана, то есть 3 знакоместа (что, похоже, и реализовано в megademo).




СОДЕРЖАНИЕ:


  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



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

Похожие статьи:
Обзор - В этом разделе, как обычно, обзор программ, поступивших в город за последнюю неделю.
Авто - Как я учился водить машину.
Поиск - поиск игр, программ.
Железо - Рассказ "Пофигисты".
ЖЗЛ - Joker.

В этот день...   26 апреля