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 > Crazy DMA problem!

#8597 - regularkid - Wed Jul 16, 2003 4:07 am

Ok. I'm trying to simply clear the video buffer (back buffered) each frame (mode 4) before drawing to it. However, I am using DMA to fill the video buffer to a specific color. When I use DMA by setting the registers up at the beginning of the game loop, everything works fine and the rest of the drawing gets done on top of it. But when I call a function to setup the DMA registers, the rest of the drawing gets drawn below the screen clear. Very strange. My only guess is that for some reason when you return from a function that does DMA stuff, it waits to do the transfer? Here is my game loop code:

Code:

while(1)
{
    u16 color = (0 | (0 << 8));

#if 0
    DMA_Fill(19200, (u32*)&color, (u32*)g_VideoBuffer);
#else
    REG_DMA3SAD = (u32)&
    REG_DMA3DAD = (u32)g_VideoBuffer;
    REG_DMA3CNT = 19200 | WORD_SIZE_16 | TIMING_IMMEDIATE | SOURCE_FIXED | DESTINATION_INCREMENT | ENABLE;
#endif

    LEVEL_Draw();

    VIDEO_WaitVSync();
    VIDEO_Flip();
}


The DMA_Fill call has the exact same code as the #else part of that block. When I use the function call (#if 1), I only see the screen fill. But when I use the DMA code without the function call (#if 0), then everything works fine and everything in LEVEL_Draw() gets drawn perfectly. Very strange! Any help at all would be greatly appreciated!

NOTE: When I change the number of words to copy to 9600 (half of the screen), I can see my LEVEL_Draw() stuff behind the half-cleared screen when I use the DMA_Fill call.
_________________
- RegularKid

#8599 - night_ - Wed Jul 16, 2003 6:41 am

On the first, quick look it appears a bit strange to me that you cast the address of color, which is a 16 bit number,
to a pointer to a 32 bit number in order to pass it to your function. It should be enough to just pass the address of color
(u32) &color and write this to the src register: REG_DMA3SAD = passedValue; Also check this for g_Videobuffer.
Maybe I can have a closer look at this after having some sleep..

#8600 - regularkid - Wed Jul 16, 2003 7:27 am

The reason that I cast it to a 32bit pointer is because that is what the dma source and destination register's types are. However, the g_VideoBuffer is a 16bit pointer and the color is also 16bit. That is the reason for the cast.
_________________
- RegularKid

#8606 - Lupin - Wed Jul 16, 2003 12:41 pm

Sorry, but I couldn't really come up with an real solution, but if you want to solve your problem, why don't you use an simple #define for your DMA copy routine? It's faster (branching costs a bit of time) and it would solve your problem. You could also use an inline function, but I prefer #defines, since the compiler doesn't generate code for the define (it generates an function label for inlined functions....)

#8612 - night_ - Wed Jul 16, 2003 3:45 pm

I think you got me wrong. What you need is a address which is 32 bits and not an address that points to
a 32 bit value. What you do is casting the pointer. (u32) &color is just casting the address value to a 32 bit value.

Do the following: make color and videobuffer global. Use the exact same code in your function as you do in your endless loop.
That also means: Do not access the passed values but the globals. If that works, consider reading my first post again.

#8628 - Archeious - Wed Jul 16, 2003 9:37 pm

Lupin wrote:
Sorry, but I couldn't really come up with an real solution, but if you want to solve your problem, why don't you use an simple #define for your DMA copy routine? It's faster (branching costs a bit of time) and it would solve your problem. You could also use an inline function, but I prefer #defines, since the compiler doesn't generate code for the define (it generates an function label for inlined functions....)


Should read the article in Game Programmings Gems 2 by Peter Dalton. He explains why inline functions are 90% of the time better then macros.

#8630 - jd - Wed Jul 16, 2003 11:28 pm

Code:

    REG_DMA3SAD = (u32)&


Maybe the above is just a typo in your post, but that code should read:

Code:

    REG_DMA3SAD = (u32)& color;


(Actually, I'm pretty sure that's a bug in the forum because it just removed the "color;" from my version as well. I had to add a space between & and "color;" to get it to work.)

Also, if DMA_Fill() has exactly the same code as the #else part of the block then it might be setting DMA3SAD to point to the address of the address of color. Posting the code to DMA_Fill() would help to clarify things.

#8663 - Lupin - Thu Jul 17, 2003 2:37 pm

Archeious, why are inline functions faster then macros? Sorry, but I don't want to buy an whole book just to be enlightened about this small topic :)

#8666 - night_ - Thu Jul 17, 2003 3:48 pm

offtopic:

- inline functions are c++
- with inline functions you have type checking (compile time errors!). To a macro you can pass any data type.
- inline functions evaluate every argument only once, whereas macros just put the arguments into the macro code.

A classic example for a common error is:
Code:
 #define abs_macro(i) ( (i) >= 0 ? (i) : -(i) ) //absolute value macro

user code:
Code:
abs_macro(i++);


produces the following code:
Code:
 (i++) >= 0 ? (i++) : -(i++)

so if you want the absolute value of 3 you get 5. Which is obviously wrong.

It is also imprtant to put paranthesis around the arguments.

Code:
 #define multiply_macro(i,j) (i*j) //absolute value macro


user code:

Code:
multiply_macro(a+b,c+d);


results in

Code:
a+b*c+d


b and c are multiplied,which is obviously not what the user intended.

this solves the problem:
#define multiply_macro(i,j) ((i)*(j)) //absolute value macro

If I code in C on projects that need high performance, I use functions in the debug and corresponding macros in the release version, because macro code can be a nightmare to debug.

Please also note that macros and inline functions can blow up your code. There are other issues, but as this is off-topic and I have already written more than I wanted...

Oh.. your question. If you still want to know: inline functions may be a little bit slower than macros, but if you use c++, use inline functions because they are,for the above reasons, BETTER not FASTER.


Last edited by night_ on Thu Jul 17, 2003 9:46 pm; edited 1 time in total

#8674 - Cyberman - Thu Jul 17, 2003 8:17 pm

night_ wrote:
offtopic:

- inline functions are c++
- with inline functions you have type checking (compile time errors!). To a macro you can pass any data type.
- inline functions evaluate every argument only once, whereas macros just put the arguments into the macro code.

1 : Yes this seems sad.. but that really isn't a problem unless you are not using the GCC compilor :)

2: This is GOOD type checking makes for fewer mistakess. Macros can create problems because of type checking in any case, debugging the output of the pre processor can be a nightmare, trust me I've spent several hours trying to find a single macro mistake. Bottom line is be sure you are doing what you think you are doing, Macros don't magically make this happen.

3: Inline functions are quite similiar to macros however there are subtle if not important differences. Inline means the compilor generates the code inline with the code the 'function' call appears in. Thus no function call ocurs. However you do not expose anything of your class, or what the function is doing you might also protect passed variables from being modified etc. The advantage here is inline only generates code for what is necessary. Another important difference is you can't have temporary variables in macros (you can in inline functions). Inlines are perfectly good to use and can lead to simplification (it mostly depends on how well organized the programer is). Macros are good to use as well. So what does one do? Use the right feature for the right thing. Macros are excelent for handling complex function calls and creating complicated structures without making the code a nightmare, at the same time, one has to know what they are doing.

A good example of a useful macro definition:

Code:

#define AREA_TAG(NAME)\
{\
TAG_AREA_ID,\
#NAME,\
NAME##_Data\
};


An example of using this kind of macro
Code:

TAG_AREA Sandusky_Region_List[] =
{
 MK_AREA_TAG(Sandusky_Desert),
 MK_AREA_TAG(Sandusky_OceanSide),
 MK_AREA_TAG(Sandusky_Mountain),
 MK_AREA_TAG(Sandusky_City),
 MK_AREA_TAG(Sandusky)
};


One would use other macros to create the structures used by the region list (in the above array) this code saves one a LOT of typing :) This is a good use for a macro.

Inlines uses are more obvious.. so I don't see the need for examples.

Cyb

#8680 - Archeious - Thu Jul 17, 2003 10:25 pm

Lupin wrote:
Archeious, why are inline functions faster then macros? Sorry, but I don't want to buy an whole book just to be enlightened about this small topic :)


Sorry if I led you to think inline function are faster. I won't go over the reason to use inline vs #defines, it looks like that has been taken care of.

I use inline function when they are well just that functions (well psuedo functions) like DMA copies because of the compile time typecasting and arguments are only evaluated once.

Once again sorry if I led you to any confusion.

#8696 - peebrain - Fri Jul 18, 2003 4:27 am

Cyberman wrote:
Another important difference is you can't have temporary variables in macros (you can in inline functions).


Yeah you can.

Code:

#define LOOP_MACRO(v)  \
{\
for (int i = 0; i < 10; i++)\
v = v + i * v;\
}


Voila! Just put braces around the entire thing to generate a new scope.

~Sean
_________________
http://www.pbwhere.com

#8707 - niltsair - Fri Jul 18, 2003 2:38 pm

in
Quote:
#define AREA_TAG(NAME)\
{\
TAG_AREA_ID,\
#NAME,\
NAME##_Data\
};
What are the # in #NAME and NAME##_Data for?

#8710 - Nessie - Fri Jul 18, 2003 3:03 pm

I think # is the macro stringize feature if I remember correctly?

Basically you could do something like this:

Code:
//Helper macro
#define ENUM2STR(_enum)  _enum,#_enum

enum ECityEnum
{
    Madison = 0,
    Chicago,
    London,
    Paris,
};

// Simple hypothetical structure
typedef struct SCityElement
{
    ECityEnum mCity;
    const char *mCityString;
} TCityElement;

TCityElement CityList[]=
{
    ENUM2STR(Madison),
    ENUM2STR(Chicago),
    ENUM2STR(London),
    ENUM2STR(Paris),
};

/* Is the same as:
TCityElement CityList[]=
{
    Madison,"Madison",
    Chicago,"Chicago",
    London,"London",
    Paris,"Paris"
};
*/


Of course, I should probably test this, but hey if I'm wrong, I'm sure someone will kindly correct me.

Cheers

#8734 - tepples - Fri Jul 18, 2003 6:36 pm

peebrain wrote:
Code:

#define LOOP_MACRO(v)  \
{\
for (int i = 0; i < 10; i++)\
v = v + i * v;\
}


Voila! Just put braces around the entire thing to generate a new scope.

Actually, this will cause problems in an if-then construction because you want the caller to supply the final semicolon; otherwise, there will be problems in 'if' and 'do' constructions. The comp.lang.c FAQ suggests something like this:
Code:

#define LOOP_MACRO(v)  \
do {\
for (int i = 0; i < 10; i++)\
v = v + i * v;\
} while(0)

Notice the lack of semicolon at the end.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.