Best way to do Nemesis-like scroll in screen 2

Page 1/2
| 2

By wimpie3

Champion (352)

wimpie3's picture

21-01-2021, 09:21

Looking for some ideas... I'd like to implement a Nemesis style scroll in screen 2. So basically the whole screen moves 1 row to the left and a new row of characters is drawn on the far right.

I had the idea to use a fast vram copy and move the first 39 rows to the left, but that doesn't seem to be possible. There is a vram to ram and a ram to vram function, so that could be a solution, but is this fast enough?

Another way seems to be to peek the entire screen and poke the characters back with a negative offset, but that means you're reconstructing the entire screen byte per byte.

What would be the best solution?

Login or register to post comments

By theNestruo

Champion (275)

theNestruo's picture

21-01-2021, 09:43

wimpie3 wrote:

Looking for some ideas... I'd like to implement a Nemesis style scroll in screen 2. So basically the whole screen moves 1 row to the left and a new row of characters is drawn on the far right.

I had the idea to use a fast vram copy and move the first 39 rows to the left, but that doesn't seem to be possible. There is a vram to ram and a ram to vram function, so that could be a solution, but is this fast enough?

Another way seems to be to peek the entire screen and poke the characters back with a negative offset, but that means you're reconstructing the entire screen byte per byte.

What would be the best solution?

The best solution is to use a "circular" NAMTBL buffer.
When blitting to VRAM, instead of doing a single 768b LDIRVM, let's think in 24x 32b LDIRVM (one for each row).
If you keep an "scroll offset", ranging from 0 to 31, that 32b LDIRVM should: LDIRVM 32-offset bytes starting from the offset (in RAM) to the starting of the row (in VRAM), move to the beginning of the row (in RAM), then LDIRVM the remaining offset bytes.
Then you only need to draw the "new" 24b in RAM each frame.

For a single row: in RAM you have "ABCDEF" and offset 0, then you blit "ABCDEF" to VRAM.
The next frame, you have offset 1, so "BCDEFA" will be blit to VRAM. If you have previously replaced the A with a G... you'll have "BCDEFG" in VRAM Smile

If you are a MAME user, check the VRAM (F4) in some games with scroll... most of them use the same principle (but hardware-based).

I did it this way in my (unfinished) World Rally clone, in case you want to check the code (https://github.com/theNestruo/msx-wrally/blob/master/src/ral...). This is slightly more complex (the scroll worked in two axis, so the offset ranges from 0 to 767) and the comments are in Spanish... but I hope the code is readable enough.

By wimpie3

Champion (352)

wimpie3's picture

21-01-2021, 09:49

Wow that's more complex than I thought it would be... is this also how Konami does it?

By theNestruo

Champion (275)

theNestruo's picture

21-01-2021, 10:08

wimpie3 wrote:

Wow that's more complex than I thought it would be... is this also how Konami does it?

It is actually less complex that it sounds Smile The tricky part is collision detection and to actually decode the new row/column from the data map. Ideally, the algorithm will be adjusted and optimized for your map structure (for example: if you use metatiles of 4x4 chars it may be convenient to make the NAMTBL buffer 36 bytes wide instead of 32)

I don't actually know how Konami did it...

By thegeps

Paladin (736)

thegeps's picture

21-01-2021, 11:01

I use a different approach:
I have a 768 buffer in RAM as nametable.
Let's name it nametable

Ld hl,nametable+1
Ld de,nametable
Ld bc,767
Ldir

Doing this all chars will move a byte to the left.
First char of all the rows becomes last char of previous row, but you have to overwrite all of them with rightmost column new data, so it doesn't matter

So you fill that column

Ld de,map_data ;point to your map data where you have groups of 24 bytes for each new column
Ld hl,nametable+31 ;point to last char of first row in our buffer
Ld b,24 ;nr of data
Draw_column:
Ld a,(de)
Ld (hl),a
Inc de
Push bc
Ld bc,32
Add hl,bc
Pop bc
Djnz draw_column

Then you'll copy whole buffer on vram

Xor a ;a=0 low byte of screen2 nametable
Ld b,a ;we need b=0 so b=256
Ld c,98h ;vdp data port
Di
Out (99h),a
Ld a,18h+64 ;hi byte of sc2 nametbl. +64= bit6 set for writing
Ei
Out (99h),a
Ld a,3
Ld hl,nametable ;origin of data (our buffer)
To_vram:
Outi
Jp nz,to_vram ;b=0=256 so loop will be performed 256 times
Dec a
Jp nz,to vram

And it's done

By theNestruo

Champion (275)

theNestruo's picture

21-01-2021, 11:10

thegeps wrote:

I use a different approach:
(...)
Let's name it nametable

Ld hl,nametable+1
Ld de,nametable
Ld bc,767
Ldir

Doing this all chars will move a byte to the left.
(...)

My approach tries to avoid this ldir because it consumes... 23 * 766 + 18 = 17.636 t-states :-S

By thegeps

Paladin (736)

thegeps's picture

21-01-2021, 11:36

It can be unrolled. And even lowered (probably first two rows don't have to be scrolled due to scoreboard). So buffer would be lowered too and copy to vram too. And even considering its t-state cost it is pretty fast. I used this on farty pig and before doing that (during 4th frame, because first 3 are for preshifted tiles exchange) It has to restore original tiles' vslues too (scrolling start at minute 03:30, before it only play music):

https://youtu.be/0oiy5OKE7-U

By Uninteresting

Master (182)

Uninteresting's picture

21-01-2021, 11:47

So NAMTBL is in RAM, and the same amount of data is moved from RAM to VRAM every frame in both cases? (I'm verifying because I intend to try doing something horizontally scrolling maybe next year.)

(When I was young, I tried to create in MSX-BASIC a snake game where the head was at array's first index and moving the snake meant moving all the body piece coordinates one step down in the array and assigning a new coordinate for the head (basically, the equivalent of that ldir operation). Obviously, I was dismayed when the snake slowed down before getting to any reasonable length...)

By wimpie3

Champion (352)

wimpie3's picture

21-01-2021, 11:58

So to get things straight: there is no way to copy from vram to vram, right, like you could do on screen 5? You always have to redraw the entire screen by copying the nametable from ram to vram?
(edit: basically the same question as above I see)

By thegeps

Paladin (736)

thegeps's picture

21-01-2021, 12:22

Yep, my code works this way. It is even easier (and faster) for vertical scrolling: in Freedom Fighter I had the whole level map in RAM about 8KB (decompressed from ROM before starting the level) starting from last map row till the first: to scroll within the map vertically I simply change the nametable start address of the 704 bytes buffer (first 2 rows are for scoreboard) decressing it by 32 bytes (a row) and copy to vram 704 bytes. On msx1 working in RAM is faster than copying from vram to ram, apply changes and copy back to vram

By thegeps

Paladin (736)

thegeps's picture

21-01-2021, 12:24

And no, copying from vram to vram isn't supported from msx1 vdp

Page 1/2
| 2