gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > DMA fill/clear

#7201 - hnager - Thu Jun 12, 2003 3:22 am

So far I've only used DMA_Copy (thanks to the pern project) to copy one hunk of memory to another...that works really well. But what if I wanted to do something along the lines of a fill (pattern or solid color) in MODE4 (or any other for that matter). If I try to copy a single rgb color to a big block of memory (VRAM) it obviously doesn't fill with that color.

on the flipside - is there a clear technique?

#7207 - funkeejeffou - Thu Jun 12, 2003 7:56 am

If you have a better look at the DMA documentation, you' ll see that the bits 23-24 of the REG_DMAxCNT register(as it is called in the pernproject tutorial 5)sets the source increment. So when want to copy a fixed value to an entire block of memory, just write 01 in these bits.

unsigned short int *value_to_copy; //DMA 16 transfers
or
unsigned long int *value_to_copy; //DMA 32 transfers
*value_to_copy = 0 //set here the palette indice wich will be copied

e.g. for filling a screen in DMA 32 :
DMA_SRC = (unsigned long int)value_to_copy
DMA_DEST = (unsigned long int)video_buffer //0x6000000 or 600A000
DMAxCNT = 0x5002580
DMAxCNT |= 0x80000000
// 0x5002580 is for setting a 9600(240*160/4) wordcount in DMA32(bit 26 set) with a fixed source adress(bit 24 set)
//0x80000000 is for setting bit 31, wich will enable the transfer

Note that for filling the VRAM, even if the bus is 16 bit wide, use 32 bits transfers, copy will be made faster.

Hope this helps

#7217 - hzx - Thu Jun 12, 2003 11:58 am

hnager, could you specify your experiences a bit more clearly? I cannot decide if it is a DMA setup problem or a "Mighty MODE4" trouble. As I can understand, you want to fill a specified memory area with a given (color) value, but it doestn work. Have you tried setting DMA registers manually, not by the DMA_Copy subroutine?
_________________
.hzx

#7220 - hnager - Thu Jun 12, 2003 1:48 pm

Up until now I've always used a DMA_Copy() function to fill a memory block by specifying a source, destination and amouunt to copy - example:

DMA_Copy(3,(void*)spriteData,(void*)OAMData,128,DMA_16NOW);

So it's only a matter of setting bits 23-24 to 'tile' a block in to memory?

Any examples of this? what about the opposite, clearing the memoery.

#7224 - funkeejeffou - Thu Jun 12, 2003 2:23 pm

You're not very clear in your question...
Mode 4, wich is the mode you are interested in, is a bitmapped mode and not a tile one. Each pixel is represented in VRAM by a palette indice wich value is between 0 and 255(one byte lenght ie unsigned char).
Clearing Memory and filling it with a color is the same thing:
For clearing it, just copy 0 in each byte, as 0 will ALWAYS be the invisible color in the palette.
For filling it with a color, copy the palette's indice that contains the color you need into each VRAM byte.

When you copy a block of data to another one, you give the DMA your source adress and your destination adress in memory. Then, what happens in this case is that at each copy of halfword or world(respectively DMA 16bit and 32 bits), your adresseS will be incremented (or decremented, depending on the DMA configuration) (respectively by 2bytes and 4 bytes).
bits 22-21 control the type of increment of your destination adress :
- 00 is increment adress at each copy
- 01 is decrement
- 10 is fixed adress
Same thing for bits 24-23 wich will determine the increment type of your source adress.

So when you copy a block to another, increment or decrement source and destination adress.
When you copy a fixed value to a block, fix the source address and increment or decrement your destination address.

I know that my english isn't perfect, but if it is still not clear to you this DMA stuff, well I can't do anymore...

#7227 - niltsair - Thu Jun 12, 2003 2:29 pm

No, not exactly (to hnager).

Dma is only a way to quickly transfer data from one location to memory.

To use it you need to :
1. Set the source address (Data that will get copied)
2. Set the destination addess (Where to put the data in memory)
3. Set the control register (How to do the transfer, and how much data to transfer)

For step 1, you declare a variable that will contains the color you want to use to fill whatever tile you want to. You use this variable address as source address.
For step 2, you set the destination address as the meory location of the tile you want to mofify.
So in step 3, instead of telling Dma to increase the source address after every 1 data transfer, you tell it to keep using the same address again and again. To do this set the rigths bits or use the #define for it.

Hope this help. Look at the code of 'DMA_Copy' and understand how it works. To 'clear' a tile, is simply a matter of setting your source variable as having the value 0, or any color you want the tile to be.

#7228 - hnager - Thu Jun 12, 2003 2:32 pm

sorry about that - poor example of DMA on my part - that's how I've used DMA in mode1 to work with sprites, I haven't done too much with it in mode4 because of the problems I was having with filling VRAM with a single color.

The reason I ask is - I'm working through an old DOS game programming book which uses vga mode 13h (mode4 is the closest to that on gba) - so a lot of the examples use a memfill to make the screen a color (for example). I'll try what you suggest, although I still am a little confused ;)

#7229 - Quirky - Thu Jun 12, 2003 2:34 pm

One point, if you fill the mode 4 screen with 0, then the colour of the BD layer shows through (which is the colour set in palette[0]). Remember to set this colour to 0 (black) at the start of your game if you want to clear to black, otherwise using the boot menu will make a screen that clears to blue :-)

This also applies for "fading out" in any of the other modes.

#7230 - hnager - Thu Jun 12, 2003 2:34 pm

thanks! (to everyone with suggestions)

#7240 - hnager - Fri Jun 13, 2003 1:02 am

ah!

I've tried to implement the suggestions but I'm having no luck - here is a piece of the source uisng a lot of pern project #defines, I'm color cycling the palette so that I can be sure to see the filled screen (if it actually filled that is) - what am I missing?>

int main(){
int index, y=0, curr_color=1;
short unsigned int color, color_1;
unsigned long int *color_2;

SetMode(MODE_4 | BG2_ENABLE);

createPalette();

*color_2 = bgPalette[1];

REG_DMA3SAD = (u32)color_2;
REG_DMA3DAD = (u32)VideoBuffer;
REG_DMA3CNT = 9600 | DMA_ENABLE | DMA_TIMEING_IMMEDIATE | DMA_32 | DMA_SOURCE_FIXED | DMA_DEST_INCREMENT;

PlotPixel(10,10,1);
while(1){
color_1 = bgPalette[1];
for(index=1;index<255;index++){
color = bgPalette[index+1];
bgPalette[index] = color;
BGPaletteMem[index] = bgPalette[index];
}

bgPalette[255] = color_1;
WaitForVsync();
}
}

#7250 - gb_feedback - Fri Jun 13, 2003 9:05 am

Quote:
REG_DMA3SAD = (u32)color_2;

Shouldn't that be
REG_DMA3SAD = (u32)&color_2; ?
_________________
http://www.bookreader.co.uk/

#7251 - Quirky - Fri Jun 13, 2003 9:18 am

No, as it's a pointer to a unsigned long int. A short would suffice though.

#7258 - gb_feedback - Fri Jun 13, 2003 11:38 am

Well in that case what is
Quote:
*color_2 = bgPalette[1];
doing.

Maybe I'm having a bad day but...
_________________
http://www.bookreader.co.uk/

#7259 - hzx - Fri Jun 13, 2003 11:47 am

*color_2 = bgPalette[1];

in this case color_2 will contain a color value, which is invalid for your pointert.

Try

*color_2 = &bgPalette[1]

instead.
_________________
.hzx

#7260 - gb_feedback - Fri Jun 13, 2003 11:52 am

But what is color_2 pointing to?
_________________
http://www.bookreader.co.uk/

#7261 - Quirky - Fri Jun 13, 2003 12:20 pm

That's true, it points nowhere!

The current code won't work as expected, as it sets the value of color_2 (which points nowhere) to the value of the bg pallete. And hzxs code won't work either, as it sets the value of a null pointer to be the address of the bg palette - in fact it doesn't even compile without warning you you've cocked up.

The correct code would be:

unsigned short * color_2;
color_2 = &bgPalette[1];

Which makes color_2 point to the second value in the bgPalette (if you make it a long int, you get a warning due to incompatible pointer types, as the palette is an array of shorts)

then

REG_DMA3SAD = (u32)color_2;

And I've tested this works, so it isn't idle speculation this time ;-)

#7262 - hnager - Fri Jun 13, 2003 12:50 pm

bgPalette[] just contains 256 colors, I just use it for MODE4...I'll try

*color_2 = &bgPalette[1]

and see where it gets me. Hopefully to a color cycling screen...

#7263 - Quirky - Fri Jun 13, 2003 12:57 pm

hnager wrote:


*color_2 = &bgPalette[1]

and see where it gets me. Hopefully to a color cycling screen...


That will get you "warning: assignment makes integer from pointer without a cast".

#7266 - hnager - Fri Jun 13, 2003 2:05 pm

hmmm, that's not it. What would get it to do what i'm hoping?

#7268 - gb_feedback - Fri Jun 13, 2003 2:12 pm

As quirky said:
Quote:
unsigned short * color_2;
color_2 = &bgPalette[1];


_________________
http://www.bookreader.co.uk/

#7269 - hnager - Fri Jun 13, 2003 2:20 pm

that's what i meant - sorry ;)

#7270 - niltsair - Fri Jun 13, 2003 2:42 pm

Actually, you don't need a variable, just do this :
REG_DMA3SAD = (u32)&bgPalette[1];

But using a variable like you did would be faster, because it has a highter access speed than video memory. I think it's cleaner the way i wrote but probably (really really slightly) slower.


And about your problem, one thing often encountered : Make sure your DMA #defines use a volatile. It's one of the most common problem. Without it, the compiler optimize it to nothing (it can't see why you assiging value there and then do nothing with them)

Such as :
#define REG_DMA3SAD ((volatile u32) 0xMemoryAddress) or
#define REG_DMA3SAD ((vu32) 0xMemoryAddress)

#7272 - hzx - Fri Jun 13, 2003 4:12 pm

Quirky wrote:

And hzxs code won't work either, as it sets the value of a null pointer to be the address of the bg palette - in fact it doesn't even compile without warning you you've cocked up.

The correct code would be:

unsigned short * color_2;
color_2 = &bgPalette[1];



True, sorry, in a hurry, a missed the * in front of the pointer.
_________________
.hzx

#7273 - hzx - Fri Jun 13, 2003 4:16 pm

niltsair wrote:
Actually, you don't need a variable, just do this :
REG_DMA3SAD = (u32)&bgPalette[1];



And since you already know the exact location of bgPalette[1], you can make your code even faster with writing the numeric value into the code :)
_________________
.hzx

#7274 - hnager - Fri Jun 13, 2003 4:20 pm

sounds as if i have a few things to try out - thanks again.

#7275 - niltsair - Fri Jun 13, 2003 4:57 pm

hzx wrote:
niltsair wrote:
Actually, you don't need a variable, just do this :
REG_DMA3SAD = (u32)&bgPalette[1];


And since you already know the exact location of bgPalette[1], you can make your code even faster with writing the numeric value into the code :)

To do that you have to write: (u32)(bgPaletteAdr + 2). (Where bgPaletteAdr is the address used by the bgPalette define)

But i don't think it would be faster. His bgPalette is stored in Videomemory while a local variable is stored in IWRAM. There will be a small overhead to store the new value in a variable, but each DMA access to it afterward will be faster since IWRam has a faster access. Of course, this might be a little pointless for the quantity of data transfered here.