Программирование nes (dendy) вычисление коллизий

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Начнём с разрешения nes (dendy) в PAL и NTSC они разные но возьмем разрешение в PAL 256/240 пикселя. В денди есть структура NameTable которая заполняется спрайтами background'а 2 банка памяти chr. NameTable заполняется sprit'ами размером 8x8 пикселей. Выходит что 256 пикселей это 32 тайла в ширину. И 240 пикселей это 30 тайлов. И так теперь у нас есть разрешение экрана 32 плитки на 30 плиток. Эту плитку можем представить как матрицу, в которой вписан каждый тайл и если значение $00 то данный спрайт не вызывает коллизии, а если $FF то коллизия произошла. Но если 32*30 то мы получим 960 байт, в этом случае будет сложно "вытащить" нужный байт, так как 8 бит это 255 в пиковом случае, то есть 11111111 или $FF. Значит как мы можем это оптимизировать, мы можем представить матрицу из 1 и 0. Итого в 1 байте = 8 бит. Если мы разделим 32/8 = 4 то получим 4 байта. С 30 строками по Y мы получаем 30*4 = 120 байт. В итоге получаем структуру:

Структура карты коллизий
collisionMap:
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %11111111, %11111111, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %11111111, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000011, %11111111, %11111111, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 
  .byt %00000000, %00000000, %00000000, %00000000 

Структуру определили теперь надо сделать несколько шагов:

  • Вычислить позицию X (в переводе на тайлы) которая будет указывать на разряд в байте, разряд имею ввиду определённую цифру на конкретной позиции, к примеру X = 10 значит надо взять 2 байт, и посмотреть 2-ю цифру в этом байте.

  • Вычислить позицию Y который укажет на ряд в котором находиться байт

  • Вычислить порядковый номер байта Y*X по сути.

В последнем пункте всплывает проблема что сделать умножение на ассемблере 6502 довольно сложно, можно сделать цикл и складывать числа, но это не оптимально. Но мы можем умножать и делить на 2-ва. Просто смещая биты числа вправо или влево соответственно. К примеру число 100 в бинарном виде будет выглядеть так 01100100 а 50 будет такой 00110010 как мы видим при смещение в право мы делим число на 2, а вот к примеру, если взять число 200 то в битах оно будет 11001000 то есть если бинарные обозначение числа 100 сдвинуть на 1 бит влево то мы получим умножение на 2.

Как вычислить позицию байта в таблице. Нам необходимо привести некое 10-чное значение координаты, пусть она будет 151 пиксель (далее будем использовать это значение в качестве примера) нам необходимо разделить это значение на 8 количество пикселей в 1-м спрайте и потом ещё на 8 количество байт, то есть необходимо координату x разделить на 64. Таких чисел может быть всего 3-ри (0, 1, 2, 3). В результате, если 6 раз сдвинуть биты в числе мы это число разделим на 2^6 (два в степени 6 то соответствуют 64)

Код деления на 64
lda xCoordinate ; xCoordinate - переменная в zeropage
lsr ; сдвиг в право на один бит делим на 2
lsr ; делим на (2*2=4)
lsr ; делим на 8
lsr ; делим на 16
lsr ; 32
lsr ; сдвинули 6 раз, в результате разделили на 64
sta tmpX ; tmpx = 2

Следующий шаг самый сложный, необходимо вычислить X*Y где Y координата тайла в таблице то есть это Y = yCoordinate/8, 8 это 2 в степени 3, по этому смещаем на 3 бита координату Y. К примеру yCoordinate = 121.

Делим Y на 8
lda yCoordinate
lsr
lsr
lsr ; здесь в акумуляторе число 15

Теперь необходимо X*Y что бы получить позицию байта в таблице (условной конечно же, они не хранятся таблицей), и тут решение довольно простое, мы можем умножить строку которая получилась на 4 а потом прибавить просто позицию X. 4 в данном случае количество байт в строке 4 (количество байт) * 8 (количество бит) * 8 (количество пикселей в тайле) = 256 это как раз разрешение nes. 4 это 2^2 а значит нам необходимо получившееся выше число сдвинуть 2 раза в лево и прибавить X который мы получили выше.

Умножаем Y на 4 и складываем с X
asl ; сдвиг в лево
asl
adc tmpX ; сложение
tay ; содержимое A в Y переносим

Остаётся ещё один шаг вычисление позиции бита в найденном байте, мы уже знаем что в одном байте 8 бит, размер спрайта то же 8х8 пикселей, и ранее мы делили X на 8 что бы получить позицию тайла (по сути бита), потом еще раз на 8 что бы получить позицию байта. При этом неизбежно была округлена дробная часть числа которая и содержит указание на тот бит который необходимо сравнить. Возвращаюсь к примеру с числом 151 на 64 оно целым не делиться, в результате деление будет число 2.35.... то есть мы понимаем что мы взяли 2 байта, буквально 00000000 00000000 ...и тут ещё некое количество битов... Вычислить это количество бит довольно легко, необходимо лишь взять число 8 в бинарном виде (а точнее 7 потому как координаты с 0 считаются) а это 00000111 и число 151 в бинарном виде это 0010010111 отсеять лишний "хвост" с помощью логического "И', в результате 0 и 0 = 0, 0 и 1 = 0, 1 и 1 = 1, мы получаем 00000111 7 пиксель. Далее представим что число стало 150 то в бинарном виде оно будет 0010010110 то есть 00000110 что соответствует 6.

Остаётся ещё один момент, что бы вычислить бит нам надо создать маску битов из 8 байт

Создаем такую маску с 8 байтами

bitMask:
  .byt %10000000 ; 1
  .byt %01000000 ; 2
  .byt %00100000 ; 3
  .byt %00010000 ; 4
  .byt %00001000 ; 5
  .byt %00000100 ; 6
  .byt %00000010 ; 7
  .byt %00000001 ; 8

Далее берем индекс байта который мы вычислили Y*4+X. И индекс бита когда мы применяли маску 00000111 с помощью логического "И"

Вычисляем позицию бита и смотрим 1 или 0?

  lda collisionMap, y ; берём байт
  and bitMask, x ; берем байт с позицией бита X
  beq gravityIncrement ; нет колизии
  bne gravityStopIncrement ; колизия была вычеслена

Как это работает при операции AND в асемблере, она влияет на флаг Z, Я пока точно не уверен в том как команды AND, EOR влияют на Z, то есть в какой момент он 1 а в какой он 0, пока предполагаю что когда после AND 0 то Z=1 если после AND результат > 0 то Z=0, но могу ошибаться. Буду благодарен если кто то в комментариях разъяснит этот момент.

В качестве вывода, программирование на NES (Asembler 6502) с каждым днём открывает для меня новый мир. Сегодня мы научились определять столкновение с платформой, в следующих статьях Я хотел бы затронуть тему флагов, врагов, столкновение с врагами.

Источник: https://habr.com/ru/post/577620/


Интересные статьи

Интересные статьи

Василий Прокофьев — разработчик Usetech в Рязани. На Java Meeting Point он расскажет о своем опыте использования реактивного программирования.В этом интервью Василий поде...
Предлагаем вашему вниманию подборку материалов от python.org о том, с чего начать первые шаги в программировании. Если Вы никогда не занимались программированием раньше, эти ...
В этой статье я хочу продемонстрировать использование DispmanX API одноплатных компьютеров Raspberry. DispmanX API предоставляет возможность создавать на десктопе Raspberry новые отображаемые с...
Привет! Калькулятор у нас почему-то ассоциируется с чем-то, что должен написать каждый новичок. Возможно потому, что исторически компьютеры с той целью и создавались, чтобы считать. Но мы буде...
Адепты функционального программирования любят завлекать новичков обещаниями идеальной выразительности кода, 100% корректностью, лёгкостью поддержки и простотой рефакторинга, а иногда даже пророча...