Pluvio Phile
Pluvio Phile

Reputation: 1

How to manipulate array in emu8086

I am currently creating a simple game program in emu8086. Using arrow keys I am able to print asterisk in every direction I want. The problem is there is a requirement that if I already printed 5 asterisk the tail of the asterisk should be remove so that the body of my asterisk will remain 5 every print.

from my research, mostly used array but I have no idea yet how to manipulate this in array, since I don't know how to track the array's tail and position for me to be able to print space to delete the tail.

like:

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

after clicking a direction it should look like.. in this case it's just for the down direction.. how about combination of other directions?

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

here's my printing code

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

Upvotes: 0

Views: 1277

Answers (1)

Ped7g
Ped7g

Reputation: 16626

You can use so called "ring buffer", if you know the maximum length of snake.

The "arrays" in assembly is just continuous area of memory, with the structure defined by your code.

Let's say you will have snake 5 asterisks long at most, then I would use buffer of 6 [x, y] positions to define that, 5 positions are asterisks from head to tail, 6th position is the previous tail position, i.e. where the space is printed to "erase" tail.

I would actually re-arrange that in the code a bit, having such buffer with positions long some power of two (next power of two after 6 is 8, if your snake will grow, to fill screen, then you need 80*25=2000 positions at most, and next power of two is 2048). And I would keep around index to the head element (then just pick direction toward next body parts as you wish, I would probably put head last during init, so buffer[0] would contain the "space" tail, buffer[1] last asterisk "tail", ... buffer[5] would be "head" asterisk, and I would keep around that headIndex=5 (or rather directly memory offset as headOffset = 10, as each element would be 2 bytes ([x,y]) and 5*2 = 10).

So you have some "ring buffer" for body parts positions in data section:

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 (?)

And some code, which will initialize snake length to 5, then first 6 pairs of bytes in positions as where the snake starts (including the "space" tail at first position!), and the head offset to 10 (5th position byte-pair in buffer).

Now to print snake, you will load the headOffset and snakeLength, and do "snakeLength" many times:

  • fetch two bytes from positions + offset, as [x,y]
  • print asterisk there
  • decrement offset by two
  • and <register with offset>,(BUFFER_SIZE-2), which will make it to point to the last element of memory in case offset was 0 already (that's the reason, why power of two, to allow me to wrap around the "ring buffer" by single and instruction and bit masking operation).

And finally one more iteration, but this time printing space, not asterisk (for the final element).

That's about how to draw the snake defined in such array.

How to "move" it in some direction, let's say (-1, 0) (to the left):

  • read headOffset value
  • read current position of head from [positions + headOffset]
  • increment offset by 2
  • and <register with offset>,(BUFFER_SIZE-2) makes it point to next element in buffer
  • add (-1, 0) to current head position
  • store that updated position to [positions + offset]
  • store offset as new headOffset (which will make the last tail asterisk position now the new "space" tail, and the previous "space" tail is out of reach with the same snakeLength).

After you will draw this new state of data, the snake will "move" to the left by one step.

How to grow snake: same as move, but you also update snakeLength by +1, so the last two elements of buffer are still used as previous asterisk+space tail, keeping the tail at the same position, while head moved ahead. (you can grow the snake only by +1 in each step with this method, as you don't add new body parts to the tail ... but that's exactly how old snake games work, when you eat some bigger food which makes your snake grow by N parts, it is done over N next steps, not instantly).

So you have array of 8 positions, from which only 6 are used. Which 6 are used is defined by headOffset, which goes around and wraps around (from offset 0 going "down" is back to offset 14 (8*2-2), and from offset 14 going up means landing at offset 0, so the "ring buffer" is warping around, as "ring", without any beginning or end).


Alternative is to have array of 6 positions, and when you move the snake, you literally move data in the array from position i to position i-1, and then writing the new head position to the last array element. This probably simpler to understand at first try, but the "ring buffer" solution is superior because you don't have to move block of memory every time the snake moves, you just add new head and adjust offset/length data, instead of moving positions in memory, you move the offset by which you access the data. (with 1500 parts long snake that means saving almost 3000 bytes memmove just by setting up one new element and updating offset). The price for this is introduction of the offset warping logic (to go from offset 14 to 0 and from 0 to 14) every time you move from one element to next, but that is done by single and instruction, so it will be still much faster than copying memory of buffer.


emu8086 is inferior, and your lector should update the tools and course (how about NASM + dosbox + some neat debugger, most of universities have very likely still some turbo debugger student-licenses around from 199x era).

Upvotes: 1

Related Questions