Как работать с массивом в emu8086

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

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

как:

1. *
2. *
3. *
4. *
5. *

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

1. 
2.*
3.*
4.*
5.*
6.*

вот мой код печати

continue:

mov ah, 2   ;print asterisk
mov dl, "*"
int 21h 

continue2:
mov ch, 32  ;hide blinking cursor
mov ah, 1
int 10h 

mov ah, 00h ;input direction
int 16h  

cmp ah, up
je  up        
cmp ah, left
je  left
cmp ah, down
je  down 
cmp ah, right
je  right

person Pluvio Phile    schedule 18.08.2017    source источник
comment
Как вы устанавливаете положение следующей звездочки? int 21h,2 печатает его в следующей позиции по умолчанию (в STDOUT), а не вверх/вниз /вправо/влево нажатием какой-либо клавиши. Кроме того, поскольку это DOS, почему бы вам не писать напрямую в VRAM, поскольку вы работаете над игрой, в эпоху 8086 PC XT, если вы хотели написать игру, вам приходилось отрезать всех посредников, включая очень медленные вызовы BIOS/DOS. Кстати, вы хорошо понимаете C/C++, поэтому я могу использовать примеры C в ответе?   -  person Ped7g    schedule 18.08.2017
comment
Я думал об ответе на этот вопрос, но на самом деле я пытался использовать emu8086, и он молча компилировал mov [di+bx*2],ax как mov [di+bx],ax без каких-либо предупреждений или ошибок. Я не хочу поддерживать такой плохой инструмент (у меня не в первый раз возникают какие-то странные проблемы с emu8086).   -  person Ped7g    schedule 18.08.2017
comment
мы фактически обязаны использовать emu8086. это похоже на игру про змейку, но она печатает тело (звездочки) по одной пользователем   -  person Pluvio Phile    schedule 18.08.2017
comment
они дали нам подсказку, что мы можем использовать массивы, но мне трудно понять, как это будет работать.   -  person Pluvio Phile    schedule 18.08.2017
comment
Кроме того, когда я проверял emu8086, я обнаружил, что самый первый пример Hello world работает, записывая данные непосредственно в видеопамять, пропуская медленные службы BIOS/DOS int 10h/int 21h. Возможно, вам следует прочитать об организации видеопамяти VGA в текстовом режиме под DOS и проверить этот пример, чтобы увидеть, как это работает. Хотя для чтения состояния клавиатуры BIOS int 16h подходит для простой игры в змейку (я просто надеюсь, что правильно помню эти прерывания), чтение клавиатуры непосредственно из порта 60h (или 61h?) приведет к конфликту с обработчиком BIOS, и вам это не нужно. для змеиной игры.   -  person Ped7g    schedule 18.08.2017


Ответы (1)


Вы можете использовать так называемый "кольцевой буфер", если знаете максимальную длину змейки.

«Массивы» в ассемблере — это просто непрерывная область памяти, структура которой определяется вашим кодом.

Допустим, у вас будет змейка длиной не более 5 звездочек, тогда я бы использовал буфер из 6 позиций [x, y], чтобы определить, что 5 позиций — это звездочки от головы до хвоста, 6-я позиция — предыдущая позиция хвоста, т. е. где пробел печатается, чтобы «стереть» хвост.

Я бы на самом деле немного изменил это в коде, имея такой буфер с позициями длиной в степени двойки (следующая степень двойки после 6 равна 8, если ваша змея будет расти, чтобы заполнить экран, тогда вам нужно 80 * 25 = не более 2000 позиций, а следующая степень двойки — 2048). И я бы оставил индекс для элемента head (затем просто выбираю направление к следующим частям тела, как вы хотите, я, вероятно, поместил бы head последним во время инициализации, поэтому буфер [0] будет содержать хвост "пробел", буфер [1] последний звездочка "хвост", ... буфер[5] будет звездочкой "голова", и я бы придерживался этого headIndex=5 (или, скорее, непосредственно смещение памяти как headOffset = 10, так как каждый элемент будет состоять из 2 байтов ([x,y]) и 5* 2 = 10).

Итак, у вас есть некоторый «кольцевой буфер» для позиций частей тела в разделе данных:

BUFFER_SIZE  EQU  2*8  ; or 2*2048 for growing snake
    ; everything will be "2*", because each position is 2 bytes big

snakeLength:
    dw      ?
headOffset:
    dw      ?
positions:
    db      BUFFER_SIZE DUP (?)

И некоторый код, который инициализирует длину змеи до 5, затем первые 6 пар байтов в positions, где начинается змейка (включая хвост «пробел» в первой позиции!), и смещение головы до 10 (5-я позиция байт-пара в буфере).

Теперь, чтобы напечатать змею, вы загрузите headOffset и snakeLength и много раз выполните "snakeLength":

  • получить два байта из positions + offset, как [x,y]
  • напечатать звездочку там
  • уменьшить смещение на два
  • and <register with offset>,(BUFFER_SIZE-2), что заставит его указывать на последний элемент памяти в случае, если смещение уже равно 0 (вот причина, почему степень двойки, чтобы позволить мне обернуть «кольцевой буфер» одной инструкцией and и операцией маскирования битов) .

И, наконец, еще одна итерация, но на этот раз печать пробела, а не звездочки (для последнего элемента).

Вот о том, как нарисовать змею, определенную в таком массиве.

Как "переместить" его в каком-то направлении, скажем (-1, 0) (влево):

  • прочитать headOffset значение
  • прочитать текущее положение головы из [positions + headOffset]
  • приращение смещения на 2
  • and <register with offset>,(BUFFER_SIZE-2) указывает на следующий элемент в буфере
  • добавить (-1, 0) к текущему положению головы
  • сохранить эту обновленную позицию в [positions + offset]
  • сохранить смещение как новое headOffset (что сделает последнее положение звездочки хвоста теперь новым хвостом «пробел», а предыдущий хвост «пробел» недосягаем с той же змеиной длиной).

После того, как вы нарисуете это новое состояние данных, змейка «переместится» влево на один шаг.

Как вырастить змейку: то же самое, что и перемещение, но вы также обновляете snakeLength на +1, поэтому последние два элемента буфера по-прежнему используются как предыдущий хвост звездочки + пробел, сохраняя хвост в том же положении, в то время как голова перемещается вперед. (с помощью этого метода вы можете вырастить змею только на +1 на каждом этапе, так как вы не добавляете новые части тела к хвосту... но именно так работают старые игры со змеями, когда вы едите более крупную пищу, которая делает вашу змейка растет на N частей, это делается за N следующих шагов, а не мгновенно).

Итак, у вас есть массив из 8 позиций, из которых используются только 6. Какие 6 используются, определяется headOffset, который идет по кругу (от смещения 0 движение «вниз» возвращается к смещению 14 (8 * 2-2), а от смещения 14 движение вверх означает посадку на смещение 0, поэтому "кольцевой буфер" крутится, как "кольцо", без начала и конца).


Альтернативой является наличие массива из 6 позиций, и когда вы перемещаете змейку, вы буквально перемещаете данные в массиве из позиции i в позицию i-1, а затем записываете новая позиция головы к последнему элементу массива. Это, вероятно, проще понять с первого раза, но решение с «кольцевым буфером» лучше, потому что вам не нужно перемещать блок памяти каждый раз, когда змея движется, вы просто добавляете новую голову и корректируете данные смещения/длины вместо перемещения позиции в памяти, вы перемещаете смещение, по которому вы получаете доступ к данным. (со змейкой длиной 1500 частей это означает экономию почти 3000 байтов memmove только за счет настройки одного нового элемента и обновления смещения). Платой за это является введение логики деформации смещения (переход от смещения 14 к 0 и от 0 к 14) каждый раз, когда вы переходите от одного элемента к другому, но это делается с помощью одной инструкции and, так что это все равно будет намного сложнее. быстрее, чем копирование памяти буфера.


emu8086 уступает, и ваш лектор должен обновить инструменты и курс (как насчет NASM + dosbox + какой-нибудь аккуратный отладчик, в большинстве университетов, скорее всего, все еще есть студенческие лицензии на турбо-отладчик примерно с эпохи 199x).

person Ped7g    schedule 18.08.2017
comment
Кстати, таким образом вы получите мигающую часть тела, когда будете проходить позицию, где нарисован последний пробел (особенно если змея растет, то есть проходит несколько частей змеи). Чтобы избежать этого, вы должны вместо этого рисовать от конца к голове, рисуя сначала пространство, а затем тело. На самом деле, если вы знаете, что содержимое экрана не повреждено, вы можете нарисовать только пространство, чтобы стереть последнюю часть тела, когда змея движется (не растет), и рисовать только голову. Остальная часть тела отмечена звездочками, поэтому можно повторно использовать то, что уже есть на экране, без перерисовки их каждый раз. Но это про трюки с логикой рисования - person Ped7g; 18.08.2017
comment
спасибо за ваши усилия, сэр. Я постараюсь понять ваш метод. - person Pluvio Phile; 18.08.2017