Inferno #03
22 ноября 2002

Gamedev - WORM-255F. Код игры Питон размером в 255 байт с комментариями.

; WORM-255F tinygame
; by Shiru Otaku/ANGEL2 18.o9.2oo2
; mailto: shiru@mail.ru

; Данная программка являет собой игру
; "Питон" размером в 255 байт.
; Уверен что можно сделать ещё короче,
; но в 128 байт при всех примочках
; она всё равно не поместится:).

; Эта версия использует некоторые
; подпрограммы 48basic rom (опрос BREAK).
; Эта версия использует очень упрощённое
; RND - по значению регистра R.
; Эта версия имеет звуковые эффекты через
; хрипер (целых 2;).
; В этой версии квадраты-кролики мигают
; (не FLASHем).
; В этой версии голова змия выделена
; отдельным цветом.
; Игре всё равно что было на экране до её
; запуска (все значения атрибутов
; используют одинаковые INK и PAPER).

; Управление - SINCLAIR JOY.
; Выход из игры - BREAK KEY (IY и стек
; не портит).

; максимальная длина змия - 255.


WORMBUF  EQU #C16F ;адрес буфера змия
                   ;(такой хитрый потому
                   ;что младший байт
                   ;адреса также
                   ;используется в
                   ;инициализации новой
                   ;игры.

WORMCOL  EQU #2D   ;цвет змия, только
                   ;нечётное число (bit 0
                   ;is set).
RAMKCOL  EQU #09   ;цвет края поля, только
                   ;нечётное число.


;поехали...

 ORG #C000

;начинаем новую игру - сюда управление
;передаётся и при запуске, и при
;рестарте вследствие гамовера.

START

;очищаем игровое поле и одновременно
;рисуем рамку поля - это выгоднее чем
;раздельно очистить экран и нарисовать
;рамку (меньше занимает).
;Рамка поля нам нужна чтобы проще
;определять касание змием края поля, по
;цвету. Это будет короче, нежели проверять
;значение адреса головы змия в области
;атрибутов (почти вдвое).

;в HL - начало области атрибутов.

 LD HL,#5800
 
;закрасили верхнюю линию цветом края
;игрового поля.

 LD B,#20
 LD A,RAMKCOL
 LD (HL),A
 INC HL
 DJNZ $-2
 
;теперь красим 22 строки - первый байт
;как край, 30 байт в 0, последний байт
;строки тоже как край.

 LD B,22
SUDA
 LD (HL),A
 INC HL
 
;Причем если внешний цикл идёт по DJNZ,
;то внутренний (30 байт) - по регистру С,
;мы декрементим его пока не установится
;флаг переноса.

 LD C,#1E
 LD (HL),0
 INC HL
 DEC C
 JR NZ,$-4
 LD (HL),A
 INC HL
 DJNZ SUDA
 
;и красим нижнюю линию тоже в цвет края
;игрового поля.

 LD B,#20
 LD (HL),A
 INC HL
 DJNZ $-2

;Инициализируем переменные игры. Задаём
;длину змия равную 4-ём. Реально это будет
;3 знакоместа, 4-же потому-что последний
;элемент змия - нулевым цветом выводится,
;позволяет обойтись без очистки экрана
;каждый цикл.

 LD A,4
 LD (WORMLEN), A
 
;Заносим в переменную цвета бордюра двойку
;красный (типа гамовер)
;Экономим байт за счёт использования RRA.
;Переменная нужна чтобы цвет бордюра
;менялся с задержкой, а не сразу-же.

 RRA
 LD (BORDCOL), A
 
;Инициализируем буфер змия. Здесь
;используется младший байт адреса
;буфера (хранится в HL) для того чтобы
;и заносить его в память как младший байт
;адреса атрибутов, и для значения цикла
;одновременно.
;цикл при этом идёт дольше чем требуется
;(#71 раз), но это несущественно - зато
;экономим два байта на цикле без DJNZ.

         LD HL,WORMBUF
 ADD A,L 

 LD (HL),A    
 INC HL       
 LD (HL),#59   
 INC HL        
 DEC A         
 JR NZ,$-6
 
;Заносим в переменную направления движения
;змия код команды DEC HL (#2B) Это значит
;что при старте игры змий двигается влево.
 
 LD A,#2B      
 LD (WORMDIR), A 

;Вызываем подпрограмму вывода нового
;зайца-кролика. Подпрограмма использована
;потому-что генерация нового зайца нужна
;два раза - при старте и при съедении
;старого кролика.

 CALL GENKROL


;Теперь начинается основной цикл игры.

MAINLOOP

;В качестве адреса переменной BORDCOL
;используется прямое указание на байт
;операнда команды LD A,n - позволяет
;сэкономить лишний байт кода
;В данном месте у нас в регистре A реально
;хранится не 0, а цвет бордюра.

BORDCOL=$+1
 LD A,0       

;Красим бордюр в этот цвет.

 OUT (#FE), A   

;И заносим в переменную BORDCOL единицу
;- в следующий раз бордюр станет синим
;(т.к. будут HALTы - глаз успеет увидеть
;предыдущий цвет).

 LD A,1         
 LD (BORDCOL), A

;Переменную KROLADR - адрес текущего
;кролика в области атрибутов - также
;храним в команде (LD HL,nn).

KROLADR=$+1
 LD HL,0    

;Переменную цвета кролика тоже храним в
;команде. Она нужна для мигания кролика
;- мы не можем использовать FLASH
;потому-что мы не очищали собственно
;экранную область, и кто знает что там за
;мусор (мы ведь не будем надеяться что все
;нужные цвета установлены из
;BASIC-загрузчика:)
;Изначально эта переменная равна #36
;(зелёные INK и PAPER)

KROLCOL=$+1
 LD A,#36
 LD (HL),A

;Нарисуем змия. Его длина хранится в
;команде LD B,n. Надо помнить что длина
;змия реально на 1 меньше, чем хранится в
;этой переменной - после хвоста змия
;рисуем ещё одно знакоместо чёрным цветом.

WORMLEN=$+1
 LD B,0        
 LD HL,WORMBUF
 
;Цвет текущего знакоместа змия мы храним в
;регистре A. Изначально он равен нулю,
;т.к. мы будем выводить змия от конца к
;началу (это позволит сэкономить несколько
;байт на затиралке хвоста).
  
 XOR A
 
;Копируем через стек адрес буфера змия в
;IX - это короче чем загружать его туда
;как LD IX,nn (на байт).
    
 PUSH HL       
 POP IX    
      
;Цикл вывода змия.

PRWORM0

;Берём адрес из буфера змия в DE (адрес в
;области атрибутов, никаких координат - из
;них долго пересчитывать).

 LD E,(HL)      
 INC HL         
 LD D,(HL)      
 INC HL 

;Красим знакоместо по этому адресу цветом
;из регистра A.

 LD (DE),A   
 
;Первое знакоместо (хвост) покрасился
;нулём, а теперь мы грузим в A цвет
;собственно тушки змия (потери скорости от
;лишней команды в цикле не смертельны -
;нам важен размер:).

 LD A,WORMCOL
 
;Одновременно с выводом мы сдвигаем адреса
;в буфере змия к его хвосту - если сделать
;это раздельно с выводом, то программа
;станет длинее.
;Т.к. в IX у нас хранится тоже что и в
;HL - используем относительное смещение,
;всё равно, даже при нулевом смещении байт
;на него тратится.

 LD (IX-2),E    
 LD (IX-1),D    
 INC IX         
 INC IX         
 DJNZ PRWORM0   

;После цикла в DE остался адрес головы
;змия - если мы хотим выделить её
;отдельным цветом, то делаем это
;(но выглядит не очень красиво, так что
;можно и не выделять, а сэкономить три
;байта). Я выделил включённой яркостью

 XOR 64
 LD (DE),A

;На случай если длина змия увеличится мы
;копируем последний адрес головы оного в
;следующую ячейку буфера (на неё указывает
;HL после цикла вывода).

 LD (HL),E      
 INC HL        
 LD (HL),D      

;Обменяем HL и DE, т.к. сейчас нам надо
;выяснить новый адрес головы в области
;атрибутов, а это удобнее делать в
;регистре HL.

 EX DE,HL       

;В BC мы загружаем #20 - чтобы сложением
;HL и BC можно было получить новый адрес
;при движении вниз. Старшую часть пары
;(B) мы не загружаем, т.к. она после
;последнего DJNZ равна нулю.

 LD C,#20     
   
;Логично предположить что для движения
;вверх нам нужно вычесть из HL BC.
;Но мы так не сделаем, т.к. команда
;SUB HL,BC занимает в памяти два байта
;вместо одного байта команды ADD HL,BC.
;Проблема не в лишнем байте, а в том что
;новый адрес мы вычисляем
;самомодифицирующимся кодом, и для
;движения вверх нам пришлось-бы
;модифицировать два байта (для остальных
;ставить ;NOP), в то время как для прочих
;направлений надо модифицировать всего
;один. Поэтому мы тратим три байта на
;загрузку в пару DE значения #FFE0.
;Это значение равно отрицательному числу
;#20, и если мы прибавим его к HL, то
;реально мы сделаем тоже самое, как
;если-бы отнимали #20.

 LD DE,#FFE0

;Переменная WORMDIR указывает на адрес в
;памяти, в который мы в блоке управления
;змием будем писать команды INC HL/DEC HL
;/ADD HL,BC/ADD HL,DE в зависимости от
;выбранного направления движения.

WORMDIR=$
 NOP
 
;теперь вернём найденный адрес обратно в
;DE, т.к. он нам ещё понадобится для
;определения факта смерти змия по
;различным обстоятельствам и для занесения
;в последнюю ячейку буфера если змий
;выживет.
         
 EX DE,HL       

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

 LD B,7         
 HALT          
 DJNZ $-1       

;Поменяем цвет кролика на новый (имитируем
;FLASH) XOR`ом. Для этого адрес переменной
;цвета кролика взять в HL, т.к. нам его
;обратно записывать ещё.
;Старый цвет кролика остаётся в регистре C
;для дальнейших проверок

 LD HL,KROLCOL
 LD A,(HL)
 LD C,A
 XOR 9
 LD (HL),A

;Теперь покопаемся в желудке змия чтобы
;узнать, не съели-ли мы кого-нибудь (себя
;например:). Для начала узнаем, не был-ли
;съеден кролик.

 LD A,(DE)      

;Так кролик или нет?

 CP C
 JR NZ,CHKDEAD;Вроде нет...

;Точно кролик!
;Ну тогда надо-бы удлинить змия и
;сгенерировать нового кролика.
;Удлиняем. Но есть одна тонкость - длина
;змия задаётся у нас одним байтом, и на
;случай если игрок - гигант большого
;джойстика, то нужно предусмотреть
;случай когда длина змия стала равна 255.
;Чтобы не стал он после очередного кролика
;змием-невидимкой:) Потому после INC (HL)
;проверяем флаг переполнения, и если чего
;- восстанавливаем статус-кво. То-есть
;если змий был длиной в 255, то он таким и
;останется.

 LD HL,WORMLEN  
 INC (HL)       
 JR NZ,$+3     
 DEC (HL)
 
;Продолжаем удлинять змия - увеличим
;указатель на голову змия в IX на два
;(то-есть на один знак). Надо заметить,
;что без этих команд обойтись нельзя
;(как может показаться) - ведь тогда IX
;будет указывать не на голову змия, а на
;предыдущую ячейку буфера, и тогда змию -
;хана.

 INC IX         
 INC IX
 
;Позовём на сцену нового кролика:).
       
 CALL GENKROL
 
;После GENKROL в регистре A всегда нуль -
;его мы и заносим в то место, где схавали
;кролика - чтобы при проверке на смерть
;змия не принять останки кролика за
;что-нибудь иное
 
 LD (DE),A

;На радостях по поводу съедения зайца
;весело подмигнём зелёным бордюром.

 LD A,4         
 LD (BORDCOL), A 

;А заодно и пикнем:) Т.к. пикать по другим
;поводам мы не намерены - не станем
;выносить пикалку в подпрограмму. При
;работе пикалки важно значение регистра A7
;- в нем нужно хранить цвет бордюра (чтобы
;пока идёт пикание он не менялся). Как
;хорошо что именно оно там сейчас и есть..
;В регистре C у нас длительность пикания
;(внешний цикл).

 LD C,70
;Чтобы был звук - надо менять значение
;4-ого бита в порте #FE с 0 на 1
 XOR 16
 OUT (#FE), A
;сделаем задержку между сменой значений
;бита, причём длиной в текущее значение
;внешнего цикла, это чтобы был не просто
;пик, а со сменой частоты.
 LD B,C
 DJNZ $
 DEC C
 JR NZ,$-8

;А теперь проверим, не сдохли-ли мы
;невзначай (надкусили себя/край экрана).
;Проверить будет удобнее всего с помощью
;команды RRA (один байт), ведь цвета всего
;невкусного - стенок, змиев всяких - у нас
;задано нечётным числом. Соответственно,
;от этих предметов после RRA установится
;флаг переноса. Если мы съели кролика -
;флаг не установится, т.к. в пикалке мы
;занесли в регистр A чётное число
;(со сброшенным младшим битом).

CHKDEAD

;Сдохли?..

 RRA       
 JR NC,NODEAD;Нет!:)..

;Упс... Мои жубы...
;По этому поводу нужно вывести звук
;ломающихся зубов;). Он аналогичен
;предыдущей пикалке, только чтобы звук был
;не чистым тоном, а шумным - мы будем
;менять значение 4-ого бита в зависимости
;от того, что найдём в регистре R. И не
;забудем в младших трёх битах регистра A
;удерживать число 2 - красный бордюр.

 LD C,250
 LD A,R
         AND 18
 OR 2
 OUT (#FE), A
 LD B,C
 DJNZ $
 DEC C
 JR NZ,$-12
 
;...И под звук крошащихся челюстей
;запускаем игру заново...

 JP START
 
;А если мы не подохли до этого момента -
;значит надо записать новый адрес головы
;змия в конец буфера. Только надо помнить
;что после цикла вывода указатель на адрес
;(тот, что в IX - HL мы давно запортили)
;на 2 больше чем надо (INC'и-то
;выполнились); а если мы удлинились от
;съедения зайца - тогда значение IX как
;раз то что надо, но раздельные
;записывалки адреса сожрут лишние байты.
;Вобщем, заносим DE не по IX, а в адрес
;на 2 меньше (указываем это относительным
;смещением).

NODEAD
 LD (IX-1),D    
 LD (IX-2),E    

;Теперь надо поспрошать клавиатуру, а не
;тыкает-ли игрок в неё твёрдыми тупыми
;предметами?.. Особенно в районе клавиш
;67890 (Sinclair joy)?..

 LD BC,#EFFE
 
;В HL взяли адрес WORMDIR - байта, который
;будем модифицировать в зависимости от
;тыканий игрока (либо оставим прежним,
;если юзер устал заниматься ерундой).

 LD HL,WORMDIR
 IN A,(C)       

;Проверять биты полуряда 67890 будем не
;с помощью BIT n,r и не с помощью CP n -
;оба этих способа жрут по два байта, а нам
;нужно проверить весь полуряд. Потому
;выгоднее использовать однобайтовую
;команду RRA (проверять придётся в порядке
;следования битов клавиш, и один раз бит
;пропустить - клавишу 0, она у нас
;не используется).

 RRA           
 RRA           
 JR C,$+4       
;Нажата клавиша вверх (9) - заносим в
;WORMDIR код операции ADD HL,DE
 LD (HL),#19    
 RRA           
 JR C,$+4       
;Нажата клавиша вниз (8) - заносим в
;WORMDIR код операции ADD HL,BC
 LD (HL),#09    
 RRA           
 JR C,$+4       
;Нажата клавиша вправо (7) - заносим в
;WORMDIR код операции INC HL
 LD (HL),#23    
 RRA           
 JR C,$+4       
;Нажата клавиша влево (6) - заносим в
;WORMDIR код операции DEC HL
 LD (HL),#2B    

;Напоследок проверим, не нажал-ли игрок
;BREAK (бывают и такие огорчения:).
;Проверяем с помощью известной
;подпрограммы ROM BASIC48. Если не нажал -
;уходим на цикл, если нажал...
 CALL #1F54
 JP C,MAINLOOP
 
;...Ну раз нажал, так и досвидания...
 RET


;Это - подпрограмма генерации адресов
;кроликов, сама заносит оные в KROLADR,
;сама проверяет - нет-ли чего нехорошего
;там где кролик собрался появиться, итд.
;RND использовано очень хреновое, просто
;по регистру R, но для данной игры это
;оправданно (другие способы сожрут много
;байт).
;Алгоритм генерации адреса в нужных
;пределах также очень туп (но пашет).

GENKROL

;Сначала сгенерим старший байт адреса - он
;должен быть в пределах #58..#5A.
;Этого можно достичь, сгенерировав число
;0..2 (AND`ом, и если вышло 3 - то
;генерировать заново). Потом добавляем к
;этому числу #58, и получаем старший байт
;адреса.

 LD A,R
 LD H,3         
 AND H          
 CP H           
 JR Z,GENKROL   
 ADD A,#58      
 LD H,A 
 
;Теперь генерируем младший байт адреса,
;его значение может быть в пределах
;#00..#FF, так что никаких дополнительных
;извращений не понадобится.
 LD A,R
 LD L,A
 
;Проверяем, что за атрибут находится в
;сгенерированном адресе, и если это змий
;или край поля - генерируем адрес заново.
       
 LD A,(HL)      
 CP 0           
 JR NZ,GENKROL
 
;Заносим сгенерированный адрес в
;переменную KROLADR.
  
 LD (KROLADR), HL

;Как можно заметить, на выходе из этой
;подпрограммы регистр A всегда равен нулю
;- это свойство подпрограммы использовано
;выше.

 RET           

;Вот и всё.



Другие статьи номера:

Sofтинка - Новая версия триколорного редактора 8 color editor v 0.04. Улучшения, результаты тестирования и список изменений.

Inferno - Об оболочке.

Диплом - Диплом Alone Coder-а. Разработка программного обеспечения для специализированного логического анализатора. Комментарии к тексту диплома.

Диплом - Диплом Alone Coder-а. Разработка программного обеспечения для специализированного логического анализатора. Часть 1.

Диплом - Диплом Alone Coder-а. Разработка программного обеспечения для специализированного логического анализатора. Часть 2.

Диплом - Диплом Alone Coder-а. Разработка программного обеспечения для специализированного логического анализатора. Часть 3.

Диплом - Диплом Alone Coder-а. Разработка программного обеспечения для специализированного логического анализатора. Часть 4.

Inferno - Вступление от редакторов.

Inferno - Авторы журнала.

Размышления - В гостях у Кристобаля ХУНТЫ. Методика оценки фантастических произведений и способы придумывания новых фантастических идей.

Размышления - Как стать писателем. Руководство.

Inferno - Письма в редакцию.

Поэзия - Стих "Кто я?"

Возможности Спектрума - Pseudo-Color: миф или реальность? Алгоритм перевод чёрно-белого изображения в цветное.

Gamedev - WORM-255F. Код игры Питон размером в 255 байт с комментариями.

Юмор - Литературные анекдоты.


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

Похожие статьи:
Игры - OPERATIONS GUNSHIP
Разное - "Не для кого не является секретом то, что Spectrum "отстал" от IBM лет на 10-20"
Сглаживание картинок - Алгоритм сглаживания статических изображений.

В этот день...   17 июля