Toys - Scrolling in Evo SDK.

Info Guide #12
Scrolling in Evo SDK
Hippiman/Conscience

   So guys, today we will talk again
about how to get the most out of the Evo SDK and 
go beyond its limitations. I think those of 
you who tried to make any game in 
this environment, were faced with a lack of 
scrolling functions. It's big enough 
problem, because scrolling is done by re─ 
drawing a bunch of tiles every frame is a job 
ungrateful and very slow. 
   You can, of course, do it the way I do in mine.
game "Nomad": cut a large background into several pieces─ 
just small stripes and move them very quickly─ 
radii, simulating the parallax effect. But this one 
the method is more like “dancing with a tambourine”, 
gives a lot of headaches when drawing 
backgrounds, it still eats up a lot of the processor─ 
long time and not very pleasing to the eye─ 
rock. 
   There are still two options left: to score on
scrolling and make the game screen-by-screen, or 
write it in assembly language, directly changing 
video memory. That's what we'll do today 
using vertical scrolling as an example. And in 
at the end of the article I will tell you about one very 
an unpleasant pitfall, and if it is not 
to bypass, then all the work on working with video memory─ 
they will be in vain. 

            Memory organization

   Evo SDK produces images in resolution
320*200 in16 colors out of 64 possible. This 
standard ATM video mode with everything─
flowing from us.
   Each of the two screens takes up 2 pages─
bits of RAM, with each page divided into
two areas:

Page #05 (#07):"odd"
   #2000...#ЗФЗF -
pixel pairs3,7,11...155,159(8000 bytes) 
   #0000...#1FЗF -
pixel pairs1,5,9...153,157(8000 bytes) 

Page #01 (#03):"even"
   #2000...#ЗФЗF -
pixel pairs2,6,10...154,158(8000 bytes) 
   #0000...#1FЗF -
pairs of pixels0,4,8...152,156(8000 bytes) 

   Each byte contains the color of two pixels─
lei, and the bits in it are mixed:

6,2,1,0- left pixel color,
7,5,4,3- the color of the right pixel.

            Scrolling example

   For example, let's look at the simplest function─
tion. This function simply shifts the entire screen
down a few lines (indicated in pa─
metershift ). First he determines
which "screen" needs to be shifted, then
calculates the shift start address and the address whereyou need to write down the data and then use
commandlddr sends data to
background screen.

void do_scrolldown(u8 shift) __naked
{
__asm
 push ix
 ld ix,#0
 add ix,sp
 ld a,(_SCREENACTIVE)
//looking for the required page 
 bit 1,a
 jr z,secpaged
frstpaged:
 ld a,#0x1
 jpdopaged
secpaged:
 ld a,#0x3
dopaged:
 ld (_gl_page),a

//In the SCREENACTIVE SDK variable stores 
//active screen number. The code above reads 
//this variable and sets the desired one 
//page number in_gl_page, which was 
//pre-declared in C code. 

begd:
//page 
 ld a,(_gl_page)
 xor #0x7f
 ld bc,#Oxbff7
 ld (_MEMSLOT2),a
 out (c),a

//This code includes the previously selected 
//page in the 2nd window. _MEMSLOT2 - 
//the only window we can 
//more or less free to use. 
//The rest are constantly busy. 

beg2d:
//--------------------------- 
//writeaddr 
//Calculate the address of the place where we will be 
//move part of the screen. 
 ld c,#0x0
 ld b,4 (ix)
 dec b
//Get the number of lines for which 
//need to move the screen. 
//4 (ix) is the 1st parameter, 
//passed to the C function. 

 ld hl,#0x28
 ld de,#0x28
 jr z,zerd
muld:
 add hl,de
 djnz muld
zerd:
//Multiply the number of lines by 40 bytes 
//(by adding in a loop), we get a shift from 
//top of page. 

 ld b,h
 ld c,l
 ld de,(_gl_addr)
 add hl,de
ld de,#0x1f40
 add hl,de
 push hl
//Get the real address where it should go 
//copy data: start of memory sector 
//+ size of copied area 
//+ screen size. 

//Each byte stores information about 
//two pixels 
//- we get not 320 bytes in a row, but 160. 
//Each row is divided into 2 pages 
//- we get 80 bytes per page 
//each page is divided in half again 
//- we get 40 bytes per area. 

//readaddr 
ld hl,(_gl_addr)
 add hl,de
 push hl
//Calculate the address fromwe will 
//copy memory: 
//beginning of the memory sector + screen size. 

//read and write 
ld hl,#0x1f40
 sbc hl,bc
 ld b,h
 ld c,l
 pop hl
 pop de
lddr
//Use the lddr command to 
//moves a large memory area. 
//This command forwards blocks from bottom to top 
//(HL must contain the start address 
//sent data, 
//in DE - forwarding destination address, 
//in BC - the size of the forwarded area). 

//Followed by several checks for 
//page number and address of the beginning of the area. 
//Unremarkable code, used for 
//in order to process all 4 screens 
//segment of memory. 
ld hl,(_gl_addr)//check the address
 ld bc,#0x8000
 sbc hl,bc
 jr z,addrifd
 jp nxtd
addrifd:
  ld hl,#OxaOO0
  ld (_gl_addr),hl
  jp beg2d
nxtd:
  ld hl,#0x8000
  ld (_gl_addr),hl
  ld a,(_gl_page)
  ld b,#0x1
  sub b
  jr z,frst_chngd
  jp next_page_ifd
frst_chngd:
  ld a,#0x5
  ld (_gl_page),a
  jp begd
next_page_ifd:
  ld a,(_gl_page)
  ld b,#0x3
  sub b
  jr z,sec_chngd
  jp endd
sec_chngd:
  ld a,#0x7
  ld (_gl_page),a
  jp begd
endd:
  ld bc,#Oxbff7
  ld a,#0x71
  out (c),a
  pop ix
  ret
__endasm;
}

   Можно  заметить, что между двумя облас─
тями памяти #0000...#1FЗF и #2000...#ЗFЗF
есть немного свободного места(192 байта).
Этого места в аккурат хватает для хранения
4 строк. В данном примере это не нужно, но
для  зацикленного  скроллинга, в  качестве
буфера придётся очень кстати.

         Обман спрайтового движка

   Теперь, казалось  бы, ничего  не  может
омрачить радость от честного вертикальногоscrolling, but if you turn on the spray─
you, then you will encounter a peculiarity of work
sprite engine. He doesn't let me change the parameters─
Screen wrinkles when moving sprites
It just spoils the picture.

   It should be like this:



   And it turns out like this:



   Sprites restore screen area,
over which they move.
   But this can also be solved.

   How does a sprite engine actually work?
In addition to the main two screens, each of
which takes2 pages, the SDK has
another special background buffer for sprites,
which occupies4 pages.
 This buffer is needed so that sprites
when moving, the background was restored under
its previous location.
   Every time the screen is refreshed, it calls
functionupdateTilesToBuffer , which is new
struggling with the tile and copy update map─
transfers the necessary tiles to the recovery buffer
sprites.
   And the tile update map, in my opinion─
It changes every time you redraw it
any tile.
   On one side this diagram looks like
confusing, but on the other hand, it allows
achieve maximum performance in
expense of excess memory (which is
ATM/ZX Evo is plenty). 
   The first thing that comes to mind: you need
somehow let the SDK know that the tiles are scrolling─
this areaupdated and need to be copied─
put into buffer. After thinking a little, you realize that
It is not necessary to update the entire area. After all
only those tiles in from─ need to be updated
changed areas over which there is a spray─
you, this will improve performance
programs.
   So we have a task: somehow
force the engine to update the tiles in the buffer.
The SDK source code has a wonderful function─
tion -setTileUpdateMapF , which is installed─
shows the tile update flag, and the coordinates
takes from register pairBC . This is what
that's what we need. However this feature is not available
from C code. But no one bothers us
call her at the address, because in the end
program, the engine code is always in
in the same place.
   The address of entry into a function can be determined─
pour, tritely debugging the final code, but
We're not perverts, especially since it's like this
will need to be done every time you deposit
changes to the SDK code. We will go differently, bo─
in a simpler way.

   Open the directory with the SDK sources.
Find the file lib_startup.asm. Scrolling to 
the very end and find the code that answers 
for exporting functions. Let’s add it there but─ 
on the first line is this code: 
 export setTileUpdateMapF
 Nowwe do
toolssjasmplus
 sjasmplus.exe lib_startup.asm
The output is startup.bin and
lib_startup.exp . The second one is what we need. From─
cover it in a text editor and find─ 
dim 
 setTileUpdateMapF: EQU 0x0000E644
(your address may be different). 
 0x0000E644 there is an entry address into our
function. 
   Next, ASMA experts can write their
binding of calling this function from C code, for 
I will give the rest an example of such a binding. 

void setTileUpdateMap(u8 x,u8 y) __naked
{
__asm
 push ix
 ld ix,#0
 add ix,sp
 ld a,#0x1

ld c,4 (ix)
 ld b,5 (ix)
  call (#OxeбЧ4)
  inc b
  call (#OxeбЧ4)
  inc b
  call (#OxeбЧ4)
  inc c
  call (#OxeбЧ4)
  inc c
  call (#OxeбЧ4)
  dec b
  call (#OxeбЧ4)
  dec b
  call (#OxeбЧ4)
  dec c
  call (#OxeбЧ4)
  inc b
  call (#OxeбЧ4)
  pop ix
  ret
__endasm;
}

Replace #OxebCh4 with your address.

   The sprite in the Evo SDK has a size16*16 px,
i.e. at best it will need to be updated
4 tiles. But almost always update
you will have 9 tiles around the center of the sprite.
This function does just that: at the input
gets the sprite coordinates (top left
corner) and updates 9 tiles down and to the right
from it.
   Now every second frame needs a challenge─
implement this function, and you will be happy -
sprites will not spoil the background. "The third co─
smic speed" when shifting the entire screen─
You still won't succeed, sprites
rendering slows down more than twice, but
room for creativity and a foundation for the future
you now have.

 P.S.:in the appendix to the magazine you will find
the mem_test project, and it contains several functions 
vertical scrolling, including looping─ 
scrolling the entire screen up and down, and 
also looped scrolling of a certain 
screen sectors. 

Share your thoughts about the article