#22606 - mymateo - Fri Jun 25, 2004 4:14 am
Hi everybody, me again. Lovely day, isn't it?
I've progressed in my programming from plotting one pixel on the screen without messing up other pixels. Now, I have a very small demo. I have 2 text backgrounds showing a room from Crono Trigger (SNES), and a sprite of Marle running around on the screen. I have 60 individual frames for the sprite, the ability to run or walk and have the sprites reflect that change, including different sprites for when she tries to run off the screen. I'm proud of my accomplishments, but I could not have done it withou a lot of help. I would like to thank everyone who has helped me out on this forum before, and for the creators of CowBite Virtual Spec, The PERN Project, and TONC.
Sorry to be long winded, but I want all you smart people out there to know that you have transformed me from a clumsy, know-nothing in C++ but programming for the GBA kinda guy to someone who actually knows a little bit. I come crawling for a little bit of help.
I am trying to implement a MOD player by Apex called AAS, and I have been successful. However, when I use it, my demo slows down to a crawl. The music plays fine, but my poor sprite moves about one tenth her usual speed, so I thought it's because it's how I reload new frames. I used to load a new frame directly into the sprite's memory each time it changed, which is fine for one sprite with no music and only 2 backgrounds. Now, I'm trying to use DMA transfers to accomplish my task.
I have too many sprites to store in the sprite memory (Each sprite is 1024 bytes, 60 sprites means 60 KB of memory needed, but only 32 KB available. OH NO!). This would have been best, to simply tell the GBA to change which frame is showing, but alas I cannot unless I drastically redo all the work I done to do dem sprites (redneck moment). SO long story short, I want to store my 60 KB of sprites into RAM somewhere, and use a DMA copy to load new frames. If someone know how this can be done, please post a reply and I can provide more details.
Just so everyone knows, I have tried the external work ram (256 KB), but I don't know how I can use that area properly, and I have not worked on DMA before so I don't even know if I'm doing it right.
Thank you everybody!
#22608 - DiscoStew - Fri Jun 25, 2004 4:29 am
Just a quick thought even if it might not be related to you 60K sprite stuff, since you are using AAS, have you changed all DMA3 calls to AAS_DoDMA3? As said in the documents shown here...
Notes of AAS_DoDMA3 wrote: |
The mixer uses DMA3 internally, which means it is no longer safe to use DMA3 in your code in the usual way since the mixer might interrupt it halfway through and corrupt the parameters. To fix this you should either use AAS_DoDMA3 or the stmia assembler instruction to write all parameters with a single instruction.
|
Perhaps by changing your DMA3 calls to AAS_DoDMA3, maybe your problem with slow sprites and such might be fixed.
_________________
DS - It's all about DiscoStew
#22611 - mymateo - Fri Jun 25, 2004 5:21 am
Hi DiscoStew
I am aware that I cannot use DMA3 due to AAS, but since I am not doing anything in my demo aside from moving my sprite, that leaves me DMAs 0 1 and 2. That's besides th point, sadly. I cannot get my sprite to work using DMA's at all, even without AAS. When I had sound and a sprite, I wasn't using any DMA's for my own code, I was using the CPU to copy the sprite data.
All that aside, if you know much about the AAS, then maybe you would know if using DMAs 0 - 2 is a good idea, or if I should use DMA3 using AAS' routines? That's not an issue for me right now, but it would be a good point of interest. :-)
Thanks for replying!
#22613 - sgeos - Fri Jun 25, 2004 5:45 am
Is your goal to copy data using DMA? If so, I can paste some code that gets the job done. First, I recommend that you isolate your sprite copying code in a single function. copy_sprite(), copy_marle() or something like that.
-Brendan
#22614 - mymateo - Fri Jun 25, 2004 5:53 am
Hi Brendan/sgeos
Part of it is copy data using DMA. I may have that figured out, but I may not. Mainly, I'm looking for:
(1) Where I can store 60 KB of data safely
(2) How to copy that data.
So any help on copying using DMA would be a great help. Thanks!
#22616 - sgeos - Fri Jun 25, 2004 8:16 am
mymateo wrote: |
(1) Where I can store 60 KB of data safely |
There is certainly 60 KB to spare in ROM. I'm not sure how fast you need to get at the data.
Quote: |
(2) How to copy that data.
So any help on copying using DMA would be a great help. Thanks! |
This is the DMA function I use:
Code: |
void dma_copy
(
int channel,
const void * source,
void * destination,
u16 count,
int is32bit
)
{
REG_DMAxSAD(channel) = (u32)source;
REG_DMAxDAD(channel) = (u32)destination;
REG_DMAxCNT_L(channel) = count;
REG_DMAxCNT_H(channel) = DMA_SRC_INC | DMA_DST_INC;
if (is32bit)
REG_DMAxCNT_H(channel) |= DMA_32 | DMA_ON;
else /* 16 bit */
REG_DMAxCNT_H(channel) |= DMA_16 | DMA_ON;
} |
Here is the relevant header information:
Code: |
#define BIT(x) (1 << (x))
// DMA Channel X - Pseudo Registers
#define REG_DMAxSAD(a) (*(vu32*)(0x40000B0 + 0xC * (a))) // DMA Source Address
#define REG_DMAxDAD(a) (*(vu32*)(0x40000B4 + 0xC * (a))) // DMA Destination Address
#define REG_DMAxCNT_L(a) (*(vu16*)(0x40000B8 + 0xC * (a))) // DMA Word Count
#define REG_DMAxCNT_H(a) (*(vu16*)(0x40000BA + 0xC * (a))) // DMA Control
#define DMA_DST_INC 0x0
#define DMA_DST_DEC BIT(5)
#define DMA_DST_FIX BIT(6)
#define DMA_DST_REL (BIT(5) | BIT(6))
#define DMA_SRC_INC 0x0
#define DMA_SRC_DEC BIT(7)
#define DMA_SRC_FIX BIT(8)
#define DMA_REPEAT BIT(9)
#define DMA_16 0x0
#define DMA_32 BIT(10)
#define DMA_DRQ BIT(11)
#define DMA_VBLANK BIT(12)
#define DMA_HBLANK BIT(13)
#define DMA_SPECIAL (BIT(12) | BIT(13)
#define DMA_FIFO DMA_SPECIAL
#define DMA_VIDEO DMA_SPECIAL
#define DMA_IRQ BIT(14)
#define DMA_OFF 0x0
#define DMA_ON BIT(15) |
Use it like this:
Code: |
type_t mydata[SIZE];
/* 16-bit copy using DMA channel 3 */
dma_copy(3, mydata, destination, sizeof(mydata) / sizeof(type_t) / 2, DMA_16);
/* 32-bit copy using DMA channel 3 */
dma_copy(3, mydata, destination, sizeof(mydata) / sizeof(type_t) / 4, DMA_32); |
If you don't understand something, ask.
-Brendan
#22617 - mymateo - Fri Jun 25, 2004 10:26 am
Hi Brendan (And everyone else, too...)
I tried to implement your code, and all worked well. The only thing is, is that I can't see my sprite, so I have to assume I'm still not doing something right. So I have two questions to post:
(1)
Code: |
/* 16-bit copy using DMA channel 3 */
dma_copy(3, mydata, destination, sizeof(mydata) / sizeof(type_t) / 2, DMA_16); |
Each frame of my sprite is 1 KB, 1024 bytes. Everything about them is 16-bit, so they are 512... um, pieces... each. (512 x 16 bits = 8192 bits, 8 bits to a byte = 1024 bytes -- For those who weren't following along).
TO THE POINT - That is the amount of data I want to copy. The "sizeof(mydata) / sizeof(type_t) / 2" part is a little confusing, as I don't know exactly why I have to divide by two. Long story shor (I ramble), in order to transfer 1024 bytes of data, what value do I enter?
(2)
My second question is this. I have a gut feeling that when I pass my variables to your routine, I'm not giving it the data I want. I am referring to the source and destination of the transfer. Here's a little tidbit of code that should tell you what I'm giving it.
Code: |
void DMASpriteCopy(u8 DMADirection,u8 DMAFrame)
{
u32 SourceAddress, DestinationAddress;
DestinationAddress = 0x06012000;
if (DMADirection == 1)
{
if (DMAFrame == 1) SourceAddress = 0x02000000;
if (DMAFrame == 2) SourceAddress = 0x02000200;
etc...
}
etc..
dma_copy(0, SourceAddress, DestinationAddress, 512, DMA_16);
} |
As you can see, I am attempting to store a 32 bit value in a variable, and send that variable to your routine. My gut feeling is that I should be sending something else, not a 32-bit number, but an address. However, I do not know how I can do that when my source changes. I'm pretty sure I can do it for the destination because that never changes. So, TO THE POINT.
How do I use a variable for the source and destination addresses?
I can do this manually no problem, so I know the sprite data is in the External Work Ram (I don't know how I can figure out where the sprite data is in ROM, and being Read Only Memory, I can't exactly dictate where it goes :-) ). The problem lies in trying to DMA it into the right spot.
Thanks for the help, I look forward to your reply!
#22620 - poslundc - Fri Jun 25, 2004 2:09 pm
mymateo wrote: |
TO THE POINT - That is the amount of data I want to copy. The "sizeof(mydata) / sizeof(type_t) / 2" part is a little confusing, as I don't know exactly why I have to divide by two. Long story shor (I ramble), in order to transfer 1024 bytes of data, what value do I enter? |
It's all a question of units of measurement. The sizeof() function returns the number of bytes used by a data type. The DMA register wants to know how many copies you want to make. So if you are using DMA_16, which copies 16 bits at a time (or 2 bytes), you want to divide the number of bytes by 2 in order to obtain the number of copies the DMA should do. Similarly, if you use DMA_32 then you would divide by four.
So to copy 1024 bytes using DMA_16, you would enter 512. Using DMA_32, you would enter 256.
FYI, C expresses memory addresses in terms of bytes. So if I have a char * (a char is 1 byte long on just about every platform) that addresses 0x08000000 and add 1 to it, I'll get 0x08000001. If I have a short * (a short is 2 bytes on the ARM, and most other 32-bit platforms) that addresses 0x08000000 and add 1 to it, I'll get 0x08000002. If I have an int * (an int is 4 bytes on the ARM, and most other 32-bit platforms) that addresses 0x08000000 and add 1 to it, I'll get 0x08000004.
Quote: |
As you can see, I am attempting to store a 32 bit value in a variable, and send that variable to your routine. My gut feeling is that I should be sending something else, not a 32-bit number, but an address. However, I do not know how I can do that when my source changes. I'm pretty sure I can do it for the destination because that never changes. So, TO THE POINT.
How do I use a variable for the source and destination addresses? |
An address is a 32-bit number on the ARM. You are passing it the memory addresses you want to copy from and to. You can create a char * or int * or void * or whatever as a variable to hold the address if you want, but in the end what the DMA really requires is two numbers, and it doesn't care if you're using C language features like pointers or not.
Quote: |
I am trying to implement a MOD player by Apex called AAS, and I have been successful. However, when I use it, my demo slows down to a crawl. |
This could just be an emulator thing; did you try it on hardware?
Quote: |
I have too many sprites to store in the sprite memory (Each sprite is 1024 bytes, 60 sprites means 60 KB of memory needed, but only 32 KB available. OH NO!). This would have been best, to simply tell the GBA to change which frame is showing, but alas I cannot unless I drastically redo all the work I done to do dem sprites (redneck moment). SO long story short, I want to store my 60 KB of sprites into RAM somewhere, and use a DMA copy to load new frames. If someone know how this can be done, please post a reply and I can provide more details.
Just so everyone knows, I have tried the external work ram (256 KB), but I don't know how I can use that area properly, and I have not worked on DMA before so I don't even know if I'm doing it right. |
FIrst of all, be sure to read tepples' excellent document on managing sprite VRAM.
Second of all, unless you are using multiboot (which requires the use of EWRAM) or malloc (which may, depending on your devkit's implementation, use EWRAM), EWRAM should remain empty and free to use however you see fit, so you are fine grabbing a pointer at 0x02000000 and going from there.
Third of all, if you aren't compressing your sprites - which I imagine you aren't - you should consider loading them in directly from ROM instead of EWRAM. DMA is much faster from ROM than it is from EWRAM.
Good luck,
Dan.
#22624 - dagamer34 - Fri Jun 25, 2004 4:21 pm
mymateo wrote: |
Hi Brendan (And everyone else, too...)
I tried to implement your code, and all worked well. The only thing is, is that I can't see my sprite, so I have to assume I'm still not doing something right. So I have two questions to post:
(1)
Code: | /* 16-bit copy using DMA channel 3 */
dma_copy(3, mydata, destination, sizeof(mydata) / sizeof(type_t) / 2, DMA_16); |
Each frame of my sprite is 1 KB, 1024 bytes. Everything about them is 16-bit, so they are 512... um, pieces... each. (512 x 16 bits = 8192 bits, 8 bits to a byte = 1024 bytes -- For those who weren't following along).
TO THE POINT - That is the amount of data I want to copy. The "sizeof(mydata) / sizeof(type_t) / 2" part is a little confusing, as I don't know exactly why I have to divide by two. Long story shor (I ramble), in order to transfer 1024 bytes of data, what value do I enter?
|
Sprites can only use 4 bits(16 colors) or 8 bits (256 colors). Your counting is off by half, meaning you should have just enough space to store all that data with 2KB left over.
_________________
Little kids and Playstation 2's don't mix. :(
#22626 - tepples - Fri Jun 25, 2004 4:59 pm
mymateo wrote: |
(Each sprite is 1024 bytes, 60 sprites means 60 KB of memory needed, but only 32 KB available. OH NO!) |
Assuming 16 colors, or 4 bits per pixel, a single sprite cel covering 1024 bytes has 2048 pixels. Assuming a 1:2 ratio as is used to draw most humanoid video game characters, that's 32x64 pixels, or a 2x2 grid of Super Marios. I haven't played CT (I was into fighting games, puzzle games, and puzzle fighting games at the time), but I don't remember any Super NES RPG having 32x64 pixel characters.
sgeos wrote: |
There is certainly 60 KB to spare in ROM. |
Neither MBV2 nor Xinga Super Memory Stick can load sprite cel data into ROM. Not everybody can afford a flash cart, and not everybody will be able to buy one should Nintendo manage to shut down sales of new flash carts on traditional online stores and used flash carts on eBay.
mymateo wrote: |
I can't see my sprite |
Are you running your code in VisualBoyAdvance? If so, check the Tile Viewer.
poslundc wrote: |
The DMA register wants to know how many copies you want to make. So if you are using DMA_16, which copies 16 bits at a time (or 2 bytes), you want to divide the number of bytes by 2 in order to obtain the number of copies the DMA should do. Similarly, if you use DMA_32 then you would divide by four. |
For ease of understanding, you may want to implement this conversion within your dma_memcpy() function rather than at the caller.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#22630 - sgeos - Fri Jun 25, 2004 7:35 pm
mymateo wrote: |
The "sizeof(mydata) / sizeof(type_t) / 2" part is a little confusing, |
It also contains an error. That should have read "sizeof(mydata) / 2". (sizeof(mydata) / sizeof(type_t) gives us the number of members in the array, which we do not care about.)
mymateo wrote: |
as I don't know exactly why I have to divide by two. |
sizeof(mydata) gives us the number of eight bit units:
64 bits / 8 bits = 8 units.
We are going to transfer in 16 bit units:
64 bits / 16 bit transfer = 4 transfer units.
The number of 16 bit transfer units is always going to be one half the number of eight bit units because we are moving two eight bit units at a time.
mymateo wrote: |
As you can see, I am attempting to store a 32 bit value in a variable, and send that variable to your routine. My gut feeling is that I should be sending something else, not a 32-bit number, but an address. |
The routine accepts void pointers. Just send it the source and destination addresses and it will do the rest. (Internally it converts those to 32-bit numbers, but you don't need to worry about that.)
The problem is that the version of gba.h that I developed my header from treated all registers as either 16 or 32-bit numbers. The DMA source and destination registers are 32-bit numbers, but would be better represented as pointers. If that made no sense, forget you read this paragraph and reread the previous paragraph one more time.
mymateo wrote: |
How do I use a variable for the source and destination addresses? |
Use a pointer.
tepples wrote: |
Neither MBV2 nor Xinga Super Memory Stick can load sprite cel data into ROM. Not everybody can afford a flash cart. |
Everyone can afford VBA, unless the price just increased. =P
mymateo wrote: |
Where I can store 60 KB of data safely |
ROM was not the best answer. Use const storage for your sprite data. It will end up in the "safest" spot. That may or may not be ROM.
tepples wrote: |
For ease of understanding, you may want to implement this conversion within your dma_memcpy() function rather than at the caller. |
This version of dma_copy should be easier to use in most cases:
Code: |
void dma_copy
(
int channel,
const void * source,
void * destination,
u32 count,
int is32bit
)
{
REG_DMAxSAD(channel) = (u32)source;
REG_DMAxDAD(channel) = (u32)destination;
REG_DMAxCNT_H(channel) = DMA_SRC_INC | DMA_DST_INC;
if (is32bit) {
REG_DMAxCNT_L(channel) = count / 4;
REG_DMAxCNT_H(channel) |= DMA_32 | DMA_ON;
}
else /* 16 bit */ {
REG_DMAxCNT_L(channel) = count / 2;
REG_DMAxCNT_H(channel) |= DMA_16 | DMA_ON;
}
} |
Use the same header information. Call like so:
Code: |
const type_t my_data[REALLY_BIG_NUMBER];
/*** 32-bit copy on DMA channel 3
*/
dma_copy(3, my_data, destination, sizeof(my_data), DMA_32);
/*** 16-bit copy on DMA channel 3
*/
dma_copy(3, my_data, destination, sizeof(my_data), DMA_16);
|
As far as your demo slow down, if you are testing using VBA, is it running at 100% (speed)?
-Brendan
#22632 - mymateo - Fri Jun 25, 2004 8:42 pm
You guys (or... girls? You can never tell with nicknames...) are all too smart for me, so unfortunately we run into a problem where I either won't understand you, you won't understand me, or you assume I know something that, in fact, I don't. So thank you all very much for the help, but I think we need to clear some things up...
sgeos - I don't understand.
I asked "How do I use a variable for the source and destination addresses?" and you said "Use a pointer." How do you mean? Should I use pointers to my variables, use my variables AS pointers, or create new variables that point to the memory addresses? Then, whatever the answer is, how exactly would that look in the code? (I've seen and used pointers for years, mostly in Pascal, but I've never been the one to make them as I have never really understood how exactly they work. I know WHAT they do, I just don't know HOW to use them.)
sgeos - You didn't understand me.
I asked "Where I can store 60 KB of data safely." and you said "Use const storage for your sprite data." which is what I was doing before. BUT the problem is that I do not know how to copy it from a CONST any other way than to set up a loop to copy it byte-by-byte (well, 2 bytes by two bytes really) to the OBJ Mem. This is too slow when I am playing music (It's full speed and then some without music), and is what causes my slow down and why I want to try DMA. The problem lies here: I do not know what the memory address of the CONST will be. And, as you stated yourself,
Quote: |
ROM was not the best answer. Use const storage for your sprite data. It will end up in the "safest" spot. That may or may not be ROM. |
which means I don't even know if it'll end up in ROM, so it's very hard to know where to find it! Perhaps, there is a way to use pointers here?
tepples - Jusy FYI
Quote: |
Assuming 16 colors, or 4 bits per pixel, a single sprite cel covering 1024 bytes has 2048 pixels. Assuming a 1:2 ratio as is used to draw most humanoid video game characters, that's 32x64 pixels, or a 2x2 grid of Super Marios. I haven't played CT (I was into fighting games, puzzle games, and puzzle fighting games at the time), but I don't remember any Super NES RPG having 32x64 pixel characters. |
My sprites are 32x32, 256 colors. The CT sprites are just around 32 pixels high, but annoyingly 18 to 24 pixels wide, which means my sprites take up a lot of space with zeros. But that's not the point. Anyways, just for your curiosity, that's what my sprites look like.
tepples - You didn't understand
Or rather, I did a poor job of explaining. When I said "I can't see my sprite", I knew it was because the sprite data was never making it from storage to usage. The Tile Viewer is blank, except for my BG data.
dagamer34 - You didn't understand
Quote: |
Sprites can only use 4 bits(16 colors) or 8 bits (256 colors). Your counting is off by half, meaning you should have just enough space to store all that data with 2KB left over. |
I said each of my sprites took 1024 bytes per frame, and I've calculated that out. What I said before was ... confusing. I wrote it way past my bedtime.
Quote: |
Each frame of my sprite is 1 KB, 1024 bytes. Everything about them is 16-bit, so they are 512... um, pieces... each. (512 x 16 bits = 8192 bits, 8 bits to a byte = 1024 bytes -- For those who weren't following along). |
Let me rephrase that.
Each sprite takes up 1024 bytes. (32 pixels times 32 pixels equals 1024 pixels. 16 color takes half a byte per pixel, 256 takes a full byte per pixel, so 1024 pixels 256 colors apeice is 1024 bytes). This is stored in 16-bit values. There are 2 bytes in each 16-bit value, so we can divide 1024 by 2 to get how many 16-bit values we need. 1024 / 2 = 512.
Here's where I think you went wrong. You simply calculated 512 by the number of sprites, 60, and came up with 30720 bytes. But it's 30720 16-bit values, and each 16-bit value takes 2 bytes, so we have to double it to 61440 bytes. Someone correct me if I'm wrong, I would love to find out that I have enough storage in OBJ ram to store everything!
poslundc - I don't understand
Quote: |
FYI, C expresses memory addresses in terms of bytes. So if I have a char * (a char is 1 byte long on just about every platform) that addresses 0x08000000 and add 1 to it, I'll get 0x08000001. If I have a short * (a short is 2 bytes on the ARM, and most other 32-bit platforms) that addresses 0x08000000 and add 1 to it, I'll get 0x08000002. If I have an int * (an int is 4 bytes on the ARM, and most other 32-bit platforms) that addresses 0x08000000 and add 1 to it, I'll get 0x08000004. |
... and I don't think I'm going to try to. I don't know why I would need to add chars to memory addresses, but I'll trust you on this one. The counting makes sense, I just don't know how that's going to help me with my sprites.
poslundc - Just answering a question
I wrote "I am trying to implement a MOD player by Apex called AAS, and I have been successful. However, when I use it, my demo slows down to a crawl." You asked "This could just be an emulator thing; did you try it on hardware? "
The answer is yes, I tried it both on software (VisualboyAdvance 1.7.1) and hardware (F2A cart, 64 Mb).
poslundc - Just responding...
... to your "First of all..."
I did read tepples' document on managing sprite VRAM, but sadly I simply have too much data to store in sprite VRAM. You only get 32 KB, and i need 60 KB. (Though for some reason, I can only access half of the sprite VRAM, which means I get 16 KB).
... to your "Second of all..."
Thank you. I have figured out EWRAM should be okay, but it's nice to know what uses it so that I don't have to live in fear of corrupted sprites. (Sadly, I think now that I'll have to trash the AAS sound system because I fear it uses the EWRAM too... have to look into that...)
... to your "Third of all..."
No, I'm not compressing sprites. I'm not quite that far yet. If you can tell me what method I could use to find where in ROM the sprites are stored, then I could look into using DMA, but until then I don't know how to find them. Since I can't write to ROM except for when I compile the binary, I can't control what part of memory they get loaded into.
But it's still good advice! I didn't know that the DMA works faster from ROM than EWRAM. I always thout RAM was faster than ROM no matter what.
---------
Well, I think I might have cleared some of the smoke... Hopefully nobody posted while I was typing all of this.
Again, I really appreciate all the help everybody is so willing to give. I learn a lot from this forum. I'll keep chugging along, and I'll let everyone know when I've got it working. Someday, I might even post a small demo.
Thanks!
#22634 - Miked0801 - Fri Jun 25, 2004 9:23 pm
For copy speed, you can do better than byte by byte copying. Either use the built Swi routines (CpuCopy, CpuFastCopy, CpuSet, etc.), or use DMA and specify u32s at a time. You can also typecast your data to a struct and assign one to another. This tells the compiler to use efficient ldm/stm opcodes to do your copies (which move data 4 bytes at a time.)
#22636 - mymateo - Fri Jun 25, 2004 9:28 pm
Miked0801
I would be honored if you could show me what
Quote: |
You can also typecast your data to a struct and assign one to another |
looks like in code, because I don't know which "one" to assign to "another". It sounds like a really good idea, but unfortunately I don't know how to do any of what you suggested, so a little example would be great!
Thanks
#22637 - poslundc - Fri Jun 25, 2004 9:43 pm
Miked0801 wrote: |
You can also typecast your data to a struct and assign one to another. This tells the compiler to use efficient ldm/stm opcodes to do your copies (which move data 4 bytes at a time.) |
I am still very iffy on this one, Mike... what compilers have you observed this in, and with what optimization setting? Because my version of gcc with third-level optimizations turned on still seems to use memcpy when I do struct-struct copies.
Mymateo - I will attempt to answer some of your questions when I get home from work later tonight. Suffice to say for now, though, that none of the different methods of copying data being discussed here will make even the tiniest observable difference in the speed of your application. If you game is running slow, it is because of some fundamental flaw with either the way you have implemented AAS (which I cannot help you with, having never used it) or with the way your program is structured (but in such a way that the problem doesn't manifest until you add an additional system like AAS - believe me, it's possible).
Dan.
#22638 - Miked0801 - Sat Jun 26, 2004 12:20 am
Your structures must be multiples of 4 in size else it will call memcpy. Also, make sure they are quad byte aligned (which they will be by default.) I've done this trick in a number of places and it always has worked - especially powerful in ARM code where it has more registers to work with.
Does someone else have the link to where I discussed this technique before?
#22640 - tepples - Sat Jun 26, 2004 12:50 am
mymateo wrote: |
I asked "How do I use a variable for the source and destination addresses?" and you said "Use a pointer." How do you mean? Should I use pointers to my variables, use my variables AS pointers, or create new variables that point to the memory addresses? |
Store the source and destination address in pointer variables.
Quote: |
Then, whatever the answer is, how exactly would that look in the code? |
u32 *dst = (u32 *)0x06010000;
u32 *src = (u32 *)crono_cel1;
Quote: |
BUT the problem is that I do not know how to copy it from a CONST any other way than to set up a loop to copy it byte-by-byte (well, 2 bytes by two bytes really) to the OBJ Mem. |
One question for you, rather than an assumption: Do you know how to call BIOS routines?
Quote: |
I do not know what the memory address of the CONST will be. |
If you assign a pointer variable = start of your array, you get the memory address you seek.
Quote: |
My sprites are 32x32, 256 colors. |
Are the CT sprites also 256 colors? Or do you not have tools to work with 16-color images?
Quote: |
When I said "I can't see my sprite", I knew it was because the sprite data was never making it from storage to usage. The Tile Viewer is blank |
"I can't see my sprite cel in Tile Viewer" would have been clearer.
Quote: |
Someone correct me if I'm wrong, I would love to find out that I have enough storage in OBJ ram to store everything! |
Have you ever opened up CT in a tile editor? Did it use 16-color or 256-color sprite cels?
Quote: |
I don't know why I would need to add chars to memory addresses, but I'll trust you on this one. The counting makes sense, I just don't know how that's going to help me with my sprites. |
The types "char *", "short *", "int *", and "long *" denote types of memory addresses, by what the compiler thinks they point to. A variable of type "char *" will denote pointers to 8-bit data.
Quote: |
(Though for some reason, I can only access half of the sprite VRAM, which means I get 16 KB). |
If you can access only the second half (0x06014000-0x06017fff), then are you using a bitmap background rather than a tile background?
Quote: |
I have figured out EWRAM should be okay, but it's nice to know what uses it |
Multiboot programs use EWRAM for code and read-only data. Arrays that have been marked to go in EWRAM use EWRAM. Some malloc() implementations use EWRAM. I don't think anything else does.
Quote: |
No, I'm not compressing sprites. I'm not quite that far yet. If you can tell me what method I could use to find where in ROM the sprites are stored |
To get a memory address, just give the name of the array containing the data. For example:
Code: |
/* if your array looks like this: */
const u16 crono_cel1[512] = /* 1024 bytes, 32x32 pixels */
{
/* mucha data omitted */
};
/* then the memory address is this: */
crono_cel1
|
Quote: |
I didn't know that the DMA works faster from ROM than EWRAM. |
ROM is clocked at 4n/2s (4 wait cycles for the first read and 2 for each sequential read) by default. EWRAM is clocked at 2n/2s. DMA reads ROM and EWRAM at roughly the same speed unless you've overclocked the ROM to 3n/1s, which most commercial games do. Both the official Game Paks and all currently sold flash carts are stable at 3n/1s.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#22641 - mymateo - Sat Jun 26, 2004 1:19 am
Like I said, you guys are terrific. Thanks for the info!
In response tepples:
No, I do not know how to call BIOS routines. If I've used any, then I didn't know they were BIOS routines.
Quote: |
If you assign a pointer variable = start of your array, you get the memory address you seek. |
Again, a HUGE help! I should have known that, I read it in my "Teach Yourself C++" book (but made no sense at the time). This just goes to show people are better than books!
I have the tools to work in 1-bit, 2-bit, 4-bit, 8-bit, 16-bit and 32-bit. If I don't have a tool, I can usually program what I need in Pascal. But usually I seek out Windows based tools.
The Ct sprites are in 256 color.
Quote: |
"I can't see my sprite cel in Tile Viewer" would have been clearer. |
True, but when I wrote "I can't see my sprite" I hadn't checked the Tile Viewer. I'll try to be more direct in the future.
I have looked for a tile viewer to read CT's sprites, but when I found a tool that actually found them, that tool had no way of saving them, so it was all a futile attempt.
Quote: |
The types "char *", "short *", "int *", and "long *" denote types of memory addresses, by what the compiler thinks they point to. A variable of type "char *" will denote pointers to 8-bit data. |
I know about data types, but why have the * after them? (Again, this is me just being ignorant. It's all about pointers, but I still don't quite know which variables I would make pointers.)
Next question.
No, I'm not using a bitmapped background, I'm using video mode 0, with 4 non-rotational backgrounds, and I'm using BG's 0 and 2. I myself spent a few minutes scratching my head over this one, and just gave up, committing myself to use the second half until I'm smarter and can figure it out.
Skip down a bit to your last statement.
I'll just say "Huh?!" as I have no idea about overclocking the ROM, and I think I'll save that until later, when I actually have a game that needs more power.
--------------------------
As for my progress, I now have the sound playing AND the sprites being copied by DMA. However, I've got a few bugs to sort out. My sprites aren't being displayed correctly (i.e. the head and feet are switched around, she looks left when she runs up, etc.). So I've got to play around with my numbers, find out where it's getting snagged, and perhaps scrap the DMA and try some of this pointer stuff. In any case, the AAS audio seems to be hogging too much anyhow, probably better to leave it alone for now.
So thanks again to everybody! I've got more than enough on my plate to digest, you guys are fountains of information!
#22651 - jd - Sat Jun 26, 2004 5:29 am
mymateo wrote: |
In any case, the AAS audio seems to be hogging too much anyhow, probably better to leave it alone for now. |
AAS's highest CPU usage is around 8% and usually it will be much less than that. This shouldn't really be noticeable so I'm not sure why you're getting such a drop in performance. The only thing I can think of is that, if you're syncing to vblank, maybe the extra overhead is enough to make your code take 2 frames rather than 1. Still, even this wouldn't explain the ten-fold performance drop you mentioned so it definitely sounds like something else is going wrong.
You also asked whether it is safe to use DMA 0-2 earlier. You can't use DMA 1 or 2 with AAS as they're used to play the audio. You also can't use Timers 0 and 1 for the same reason. DMA 3 can be used with the restrictions mentioned earlier. You can use DMA 0 as you see fit, though.
#22667 - dagamer34 - Sat Jun 26, 2004 4:46 pm
jd wrote: |
mymateo wrote: | In any case, the AAS audio seems to be hogging too much anyhow, probably better to leave it alone for now. |
AAS's highest CPU usage is around 8% and usually it will be much less than that. This shouldn't really be noticeable so I'm not sure why you're getting such a drop in performance. The only thing I can think of is that, if you're syncing to vblank, maybe the extra overhead is enough to make your code take 2 frames rather than 1. Still, even this wouldn't explain the ten-fold performance drop you mentioned so it definitely sounds like something else is going wrong.
You also asked whether it is safe to use DMA 0-2 earlier. You can't use DMA 1 or 2 with AAS as they're used to play the audio. You also can't use Timers 0 and 1 for the same reason. DMA 3 can be used with the restrictions mentioned earlier. You can use DMA 0 as you see fit, though. |
Yeah, I was thinking the same thing.
mymateo, how exactly are you doing the timing of your sprites? Are you using a timer, counting vblanks, or what? Can you try posting your vblank code, if possible?
_________________
Little kids and Playstation 2's don't mix. :(
#22692 - mymateo - Sun Jun 27, 2004 4:04 am
If you give me your e-mail address, I'll e-mail you my source and all included files. If you don't want to post your e-mail, just send it to me at mcnicol1@hotmail.com
And I know I'll invite spamming by asking people not to spam, so I won't. But please, I beg the lowlife scum who crawl from forum to forum, slithering from post to post, searching out innocent victim's e-mail addresses, don't spam me. Okay, I lied. I did ask. :-)
Aren't I good-natured and trusting??
#22695 - sgeos - Sun Jun 27, 2004 6:32 am
You would post on the forum to make it easy for everyone to comment on your code.
-Brendan
#22717 - dagamer34 - Sun Jun 27, 2004 9:39 pm
Yeah, post it for everyone to see.
And some people have their e-mail address publically available. Take a look below someone's post to see if they want to show their's (I do).
|
|
|
V
_________________
Little kids and Playstation 2's don't mix. :(
#22724 - mymateo - Sun Jun 27, 2004 11:58 pm
You see, that's why I don't claim to be smart. For some reason, it never occurred to me to try the e-mail button. :-)
Okay, if you just want to see my main code and don't care about actually trying it, I can post it. I have 2 versions in a ZIP file that include everything you need to compile (except for a copy of the Dev Kit Advance, R5 Beta 3) so if anyone wants the full kit'n'kabootle, just ask.
Code: |
#include "Gba.h"
#include "Screenmode.h"
#include "Background.h" // A header I made for backgrounds
#include "Keypad.h"
#include "sprite.h"
#include "MarleSpriteData.h" //Self-explanitory
#include "CronoMap.h" //Self-explanitory
#include "AAS.h"
#include "AAS_Data.h"
u16* BGTileMem00 = (u16*)0x06000000;
u16* BGTileMem02 = (u16*)0x06008000;
u16* BGMapsMem00 = (u16*)0x06004000;
u16* BGMapsMem02 = (u16*)0x06006000;
u16* CharMem = (u16*)0x06010000;
u16* EWRAM = (u16*)0x02010000;
#define SpriteArea 0x02010000
#define DirUp 1
#define DirDown 2
#define DirLeft 3
#define DirRight 4
#define Run1 1
#define Run2 2
#define Run3 3
#define Run4 4
#define Run5 5
#define Run6 6
#define Walk1 7
#define Walk2 8
#define Walk3 9
#define Walk4 10
#define Walk5 11
#define Walk6 12
#define StandStill 13
#define LookLeft 14
#define LookRight 15
u16 loop;
void vsync()
{
while (*(volatile unsigned short *)0x04000006 != 160);
};
void InitializeSprites()
{
for (loop = 0; loop < 128; loop++)
{
sprites[loop].attribute0 = 160;
sprites[loop].attribute1 = 240;
}
};
void CopyOAM()
{
u16* temp;
temp = (u16*)sprites;
for(loop = 0; loop < 128*4; loop++)
{
OAM[loop] = temp[loop];
}
};
void MoveSprite(OAMEntry* sp, int x, int y)
{
if(x < 0) x = 512 + x;
if(y < 0) y = 256 + y;
sp->attribute1 = sp->attribute1 & 0xFE00;
sp->attribute1 = sp->attribute1 | x;
sp->attribute0 = sp->attribute0 & 0xFF00;
sp->attribute0 = sp->attribute0 | y;
};
void VBlankInterruptHandler()
{
AAS_DoWork();
}
void UnusedInterruptHandler()
{
};
void (*AAS_IntrTable[13])(void) =
{
VBlankInterruptHandler, // VBlank Interrupt
UnusedInterruptHandler, // HBlank Interrupt
UnusedInterruptHandler, // V Counter Interrupt
UnusedInterruptHandler, // Timer 0 Interrupt
UnusedInterruptHandler, // Timer 2 Interrupt
UnusedInterruptHandler, // Timer 3 Interrupt
UnusedInterruptHandler, // Serial Communication Interrupt
UnusedInterruptHandler, // DMA0 Interrupt
UnusedInterruptHandler, // DMA1 Interrupt
UnusedInterruptHandler, // DMA2 Interrupt
UnusedInterruptHandler, // DMA3 Interrupt
UnusedInterruptHandler, // Key Interrupt
UnusedInterruptHandler // Cart Interrupt
};
void LoadMarleIntoExternalWorkRAM()
{
int Index;
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 0)] = Marle_Left_Walk_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 1)] = Marle_Left_Walk_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 2)] = Marle_Left_Walk_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 3)] = Marle_Left_Walk_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 4)] = Marle_Left_Walk_5Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 5)] = Marle_Left_Walk_6Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 6)] = Marle_Left_Run_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 7)] = Marle_Left_Run_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 8)] = Marle_Left_Run_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 9)] = Marle_Left_Run_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 10)] = Marle_Left_Run_5Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 11)] = Marle_Left_Run_6Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 12)] = Marle_Left_StandingData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 13)] = Marle_Left_Look_LeftData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 14)] = Marle_Left_Look_RightData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 15)] = Marle_Right_Walk_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 16)] = Marle_Right_Walk_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 17)] = Marle_Right_Walk_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 18)] = Marle_Right_Walk_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 19)] = Marle_Right_Walk_5Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 20)] = Marle_Right_Walk_6Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 21)] = Marle_Right_Run_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 22)] = Marle_Right_Run_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 23)] = Marle_Right_Run_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 24)] = Marle_Right_Run_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 25)] = Marle_Right_Run_5Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 26)] = Marle_Right_Run_6Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 27)] = Marle_Right_StandingData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 28)] = Marle_Right_Look_LeftData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 29)] = Marle_Right_Look_RightData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 30)] = Marle_Up_Walk_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 31)] = Marle_Up_Walk_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 32)] = Marle_Up_Walk_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 33)] = Marle_Up_Walk_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 34)] = Marle_Up_Walk_5Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 35)] = Marle_Up_Walk_6Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 36)] = Marle_Up_Run_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 37)] = Marle_Up_Run_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 38)] = Marle_Up_Run_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 39)] = Marle_Up_Run_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 40)] = Marle_Up_Run_5Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 41)] = Marle_Up_Run_6Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 42)] = Marle_Up_StandingData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 43)] = Marle_Up_Look_LeftData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 44)] = Marle_Up_Look_RightData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 45)] = Marle_Down_Walk_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 46)] = Marle_Down_Walk_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 47)] = Marle_Down_Walk_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 48)] = Marle_Down_Walk_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 49)] = Marle_Down_Walk_5Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 50)] = Marle_Down_Walk_6Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 51)] = Marle_Down_Run_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 52)] = Marle_Down_Run_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 53)] = Marle_Down_Run_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 54)] = Marle_Down_Run_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 55)] = Marle_Down_Run_5Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 56)] = Marle_Down_Run_6Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 57)] = Marle_Down_StandingData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 58)] = Marle_Down_Look_LeftData[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 59)] = Marle_Down_Look_RightData[Index];
};
void DMASpriteCopy(u8 DMADirection,u8 DMAFrame)
{
u32 SourceAddress, DestinationAddress;
DestinationAddress = 0x06014000;
if (DMADirection == DirLeft)
{
switch(DMAFrame)
{
case Walk1: SourceAddress = SpriteArea + (512 * 0); break;
case Walk2: SourceAddress = SpriteArea + (512 * 1); break;
case Walk3: SourceAddress = SpriteArea + (512 * 2); break;
case Walk4: SourceAddress = SpriteArea + (512 * 3); break;
case Walk5: SourceAddress = SpriteArea + (512 * 4); break;
case Walk6: SourceAddress = SpriteArea + (512 * 5); break;
case Run1: SourceAddress = SpriteArea + (512 * 6); break;
case Run2: SourceAddress = SpriteArea + (512 * 7); break;
case Run3: SourceAddress = SpriteArea + (512 * 8); break;
case Run4: SourceAddress = SpriteArea + (512 * 9); break;
case Run5: SourceAddress = SpriteArea + (512 * 10); break;
case Run6: SourceAddress = SpriteArea + (512 * 11); break;
case StandStill: SourceAddress = SpriteArea + (512 * 12); break;
case LookLeft: SourceAddress = SpriteArea + (512 * 13); break;
case LookRight: SourceAddress = SpriteArea + (512 * 14); break;
}
}
if (DMADirection == DirRight)
{
switch(DMAFrame)
{
case Walk1: SourceAddress = SpriteArea + (512 * 15); break;
case Walk2: SourceAddress = SpriteArea + (512 * 16); break;
case Walk3: SourceAddress = SpriteArea + (512 * 17); break;
case Walk4: SourceAddress = SpriteArea + (512 * 18); break;
case Walk5: SourceAddress = SpriteArea + (512 * 19); break;
case Walk6: SourceAddress = SpriteArea + (512 * 20); break;
case Run1: SourceAddress = SpriteArea + (512 * 21); break;
case Run2: SourceAddress = SpriteArea + (512 * 22); break;
case Run3: SourceAddress = SpriteArea + (512 * 23); break;
case Run4: SourceAddress = SpriteArea + (512 * 24); break;
case Run5: SourceAddress = SpriteArea + (512 * 25); break;
case Run6: SourceAddress = SpriteArea + (512 * 26); break;
case StandStill: SourceAddress = SpriteArea + (512 * 27); break;
case LookLeft: SourceAddress = SpriteArea + (512 * 28); break;
case LookRight: SourceAddress = SpriteArea + (512 * 29); break;
}
}
if (DMADirection == DirUp)
{
switch(DMAFrame)
{
case Walk1: SourceAddress = SpriteArea + (512 * 30); break;
case Walk2: SourceAddress = SpriteArea + (512 * 31); break;
case Walk3: SourceAddress = SpriteArea + (512 * 32); break;
case Walk4: SourceAddress = SpriteArea + (512 * 33); break;
case Walk5: SourceAddress = SpriteArea + (512 * 34); break;
case Walk6: SourceAddress = SpriteArea + (512 * 35); break;
case Run1: SourceAddress = SpriteArea + (512 * 36); break;
case Run2: SourceAddress = SpriteArea + (512 * 37); break;
case Run3: SourceAddress = SpriteArea + (512 * 38); break;
case Run4: SourceAddress = SpriteArea + (512 * 39); break;
case Run5: SourceAddress = SpriteArea + (512 * 40); break;
case Run6: SourceAddress = SpriteArea + (512 * 41); break;
case StandStill: SourceAddress = SpriteArea + (512 * 42); break;
case LookLeft: SourceAddress = SpriteArea + (512 * 43); break;
case LookRight: SourceAddress = SpriteArea + (512 * 44); break;
}
}
if (DMADirection == DirDown)
{
switch(DMAFrame)
{
case Walk1: SourceAddress = SpriteArea + (512 * 45); break;
case Walk2: SourceAddress = SpriteArea + (512 * 46); break;
case Walk3: SourceAddress = SpriteArea + (512 * 47); break;
case Walk4: SourceAddress = SpriteArea + (512 * 48); break;
case Walk5: SourceAddress = SpriteArea + (512 * 49); break;
case Walk6: SourceAddress = SpriteArea + (512 * 50); break;
case Run1: SourceAddress = SpriteArea + (512 * 51); break;
case Run2: SourceAddress = SpriteArea + (512 * 52); break;
case Run3: SourceAddress = SpriteArea + (512 * 53); break;
case Run4: SourceAddress = SpriteArea + (512 * 54); break;
case Run5: SourceAddress = SpriteArea + (512 * 55); break;
case Run6: SourceAddress = SpriteArea + (512 * 56); break;
case StandStill: SourceAddress = SpriteArea + (512 * 57); break;
case LookLeft: SourceAddress = SpriteArea + (512 * 58); break;
case LookRight: SourceAddress = SpriteArea + (512 * 59); break;
}
}
REG_DMA0SAD = SourceAddress;
REG_DMA0DAD = DestinationAddress;
REG_DMA0CNT = 512 | ENABLE_DMA;
};
void AgbMain()
{
AAS_SetConfig( AAS_CONFIG_MIX_32KHZ, AAS_CONFIG_CHANS_8, AAS_CONFIG_SPATIAL_STEREO, AAS_CONFIG_DYNAMIC_ON );
AAS_ShowLogo();
REG_DISPSTAT |= 0x8;
REG_IE |= 0x1;
AAS_MOD_Play( AAS_DATA_MOD_CreamOfTheEarth );
int x,y,X,Y,SpriteOffset;
int x_loop = 0, y_loop = 0, index = 0;
s16 Marlex = 50, Marley = 50;
u16 CurrentFrame, LastFrame, PrimaryDirection;
u8 MoveLeft, MoveRight, MoveUp, MoveDown;
s8 maxx,maxy;
REG_BG0CNT = BG_PRIORITY_2 | BG_TILE_MEM_0x06000000 | BG_1x256_PALETTE | BG_MAP_MEM_0x06004000 | BG_TEXT_32x32T;
REG_BG2CNT = BG_PRIORITY_1 | BG_TILE_MEM_0x06008000 | BG_1x256_PALETTE | BG_MAP_MEM_0x06006000 | BG_TEXT_32x32T;
SetMode(MODE_0 | BG0_ENABLE | BG2_ENABLE | OBJ_ENABLE | OBJ_MAP_1D);
LoadMarleIntoExternalWorkRAM();
InitializeSprites();
sprites[0].attribute0 = COLOR_256 | SQUARE | 64;
sprites[0].attribute1 = SIZE_32 | 35;
sprites[0].attribute2 = 512 | PRIORITY(0);
DMASpriteCopy(DirLeft, StandStill);
for (x = 0;x < 4*8*8;x++){OBJPaletteMem[x] = Palette[x];}
for (x = 0;x < 4*8*8;x++){ BGPaletteMem[x] = Palette[x];}
for (x = 0;x < 8*8*180;x++){ BGTileMem00[x] = bg0_Tiles[x*2] | bg0_Tiles[x*2+1] << 8;}
for (x = 0;x < 8*8*114;x++){ BGTileMem02[x] = bg1_Tiles[x*2] | bg1_Tiles[x*2+1] << 8;}
for (y = 0;y < 32*32;y++){ BGMapsMem00[y] = bg0_Map[y];}
for (y = 0;y < 32*32;y++){ BGMapsMem02[y] = bg1_Map[y];}
x=0;
y=0;
maxx = 5;
maxy = 1;
CurrentFrame = 0;
LastFrame = 0;
MoveLeft = 1;
MoveRight = 1;
MoveUp = 1;
MoveDown = 1;
PrimaryDirection = 0;
do
{
if (!(*KEYS & KEY_LEFT))
{
switch(CurrentFrame)
{
case 1: DMASpriteCopy(DirLeft, Walk1); break;
case 8: DMASpriteCopy(DirLeft, Walk2); break;
case 16: DMASpriteCopy(DirLeft, Walk3); break;
case 24: DMASpriteCopy(DirLeft, Walk4); break;
case 32: DMASpriteCopy(DirLeft, Walk5); break;
case 40: DMASpriteCopy(DirLeft, Walk6); break;
case 48: CurrentFrame = 0; break;
case 51: DMASpriteCopy(DirLeft, Run1); break;
case 58: DMASpriteCopy(DirLeft, Run2); break;
case 66: DMASpriteCopy(DirLeft, Run3); break;
case 74: DMASpriteCopy(DirLeft, Run4); break;
case 82: DMASpriteCopy(DirLeft, Run5); break;
case 90: DMASpriteCopy(DirLeft, Run6); break;
case 98: CurrentFrame = 50; break;
case 100: DMASpriteCopy(DirLeft, StandStill); break;
case 110: DMASpriteCopy(DirLeft, LookLeft); break;
case 120: DMASpriteCopy(DirLeft, StandStill); break;
case 130: DMASpriteCopy(DirLeft, LookRight); break;
case 140: CurrentFrame = 100; break;
}
}
if (!(*KEYS & KEY_RIGHT))
{
switch(CurrentFrame)
{
case 151: DMASpriteCopy(DirRight, Walk1); break;
case 158: DMASpriteCopy(DirRight, Walk2); break;
case 166: DMASpriteCopy(DirRight, Walk3); break;
case 174: DMASpriteCopy(DirRight, Walk4); break;
case 182: DMASpriteCopy(DirRight, Walk5); break;
case 190: DMASpriteCopy(DirRight, Walk6); break;
case 198: CurrentFrame = 150; break;
case 201: DMASpriteCopy(DirRight, Run1); break;
case 208: DMASpriteCopy(DirRight, Run2); break;
case 216: DMASpriteCopy(DirRight, Run3); break;
case 224: DMASpriteCopy(DirRight, Run4); break;
case 232: DMASpriteCopy(DirRight, Run5); break;
case 240: DMASpriteCopy(DirRight, Run6); break;
case 248: CurrentFrame = 200; break;
case 251: DMASpriteCopy(DirRight, StandStill); break;
case 260: DMASpriteCopy(DirRight, LookRight); break;
case 270: DMASpriteCopy(DirRight, StandStill); break;
case 280: DMASpriteCopy(DirRight, LookLeft); break;
case 290: CurrentFrame = 250; break;
}
}
if (!(*KEYS & KEY_UP))
{
switch(CurrentFrame)
{
case 301: DMASpriteCopy(DirUp, Walk1); break;
case 308: DMASpriteCopy(DirUp, Walk2); break;
case 316: DMASpriteCopy(DirUp, Walk3); break;
case 324: DMASpriteCopy(DirUp, Walk4); break;
case 332: DMASpriteCopy(DirUp, Walk5); break;
case 340: DMASpriteCopy(DirUp, Walk6); break;
case 348: CurrentFrame = 300; break;
case 351: DMASpriteCopy(DirUp, Run1); break;
case 358: DMASpriteCopy(DirUp, Run2); break;
case 366: DMASpriteCopy(DirUp, Run3); break;
case 374: DMASpriteCopy(DirUp, Run4); break;
case 382: DMASpriteCopy(DirUp, Run5); break;
case 390: DMASpriteCopy(DirUp, Run6); break;
case 398: CurrentFrame = 350; break;
case 401: DMASpriteCopy(DirUp, StandStill); break;
case 410: DMASpriteCopy(DirUp, LookRight); break;
case 420: DMASpriteCopy(DirUp, StandStill); break;
case 430: DMASpriteCopy(DirUp, LookLeft); break;
case 440: CurrentFrame = 400; break;
}
}
if (!(*KEYS & KEY_DOWN))
{
switch(CurrentFrame)
{
case 451: DMASpriteCopy(DirDown, Walk1); break;
case 458: DMASpriteCopy(DirDown, Walk2); break;
case 466: DMASpriteCopy(DirDown, Walk3); break;
case 474: DMASpriteCopy(DirDown, Walk4); break;
case 482: DMASpriteCopy(DirDown, Walk5); break;
case 490: DMASpriteCopy(DirDown, Walk6); break;
case 498: CurrentFrame = 450; break;
case 501: DMASpriteCopy(DirDown, Run1); break;
case 508: DMASpriteCopy(DirDown, Run2); break;
case 516: DMASpriteCopy(DirDown, Run3); break;
case 524: DMASpriteCopy(DirDown, Run4); break;
case 532: DMASpriteCopy(DirDown, Run5); break;
case 540: DMASpriteCopy(DirDown, Run6); break;
case 548: CurrentFrame = 500; break;
case 551: DMASpriteCopy(DirDown, StandStill); break;
case 560: DMASpriteCopy(DirDown, LookRight); break;
case 570: DMASpriteCopy(DirDown, StandStill); break;
case 580: DMASpriteCopy(DirDown, LookLeft); break;
case 590: CurrentFrame = 550; break;
}
}
x++;
y++;
if (!(*KEYS & KEY_LEFT))
{
Marlex--;
if (!(*KEYS & KEY_B)) Marlex--;
if (PrimaryDirection == 0) PrimaryDirection = 3;
if (PrimaryDirection == 4) PrimaryDirection = 3;
if ((PrimaryDirection == 1) && (*KEYS & KEY_UP)) PrimaryDirection = 3;
if ((PrimaryDirection == 2) && (*KEYS & KEY_DOWN)) PrimaryDirection = 3;
if (Marlex < 1)
{
Marlex = 1;
MoveLeft = 0;
MoveRight = 1;
}
}
if (!(*KEYS & KEY_RIGHT))
{
Marlex++;
if (!(*KEYS & KEY_B)) Marlex++;
if (PrimaryDirection == 0) PrimaryDirection = 4;
if (PrimaryDirection == 3) PrimaryDirection = 4;
if ((PrimaryDirection == 1) && (*KEYS & KEY_UP)) PrimaryDirection = 4;
if ((PrimaryDirection == 2) && (*KEYS & KEY_DOWN)) PrimaryDirection = 4;
if (Marlex > 210)
{
Marlex = 210;
MoveRight = 0;
MoveLeft = 1;
}
}
if (!(*KEYS & KEY_UP))
{
Marley--;
if (!(*KEYS & KEY_B)) Marley--;
if (PrimaryDirection == 0) PrimaryDirection = 1;
if (PrimaryDirection == 2) PrimaryDirection = 1;
if ((PrimaryDirection == 3) && (*KEYS & KEY_LEFT)) PrimaryDirection = 1;
if ((PrimaryDirection == 4) && (*KEYS & KEY_RIGHT)) PrimaryDirection = 1;
if (Marley < 5)
{
Marley = 5;
MoveUp = 0;
MoveDown = 1;
}
}
if (!(*KEYS & KEY_DOWN))
{
Marley++;
if (!(*KEYS & KEY_B)) Marley++;
if (PrimaryDirection == 0) PrimaryDirection = 2;
if (PrimaryDirection == 1) PrimaryDirection = 2;
if ((PrimaryDirection == 3) && (*KEYS & KEY_LEFT)) PrimaryDirection = 2;
if ((PrimaryDirection == 4) && (*KEYS & KEY_RIGHT)) PrimaryDirection = 2;
if (Marley > 125)
{
Marley = 125;
MoveDown = 0;
MoveUp = 1;
}
}
if ((MoveLeft == 0) && (MoveUp == 1) && (!(*KEYS & KEY_UP))) MoveLeft = 1;
if ((MoveLeft == 0) && (MoveDown == 1) && (!(*KEYS & KEY_DOWN))) MoveLeft = 1;
if ((MoveRight == 0) && (MoveUp == 1) && (!(*KEYS & KEY_UP))) MoveRight = 1;
if ((MoveRight == 0) && (MoveDown == 1) && (!(*KEYS & KEY_DOWN))) MoveRight = 1;
if ((MoveUp == 0) && (MoveLeft == 1) && (!(*KEYS & KEY_LEFT))) MoveUp = 1;
if ((MoveUp == 0) && (MoveRight == 1) && (!(*KEYS & KEY_RIGHT))) MoveUp = 1;
if ((MoveDown == 0) && (MoveLeft == 1) && (!(*KEYS & KEY_LEFT))) MoveDown = 1;
if ((MoveDown == 0) && (MoveRight == 1) && (!(*KEYS & KEY_RIGHT))) MoveDown = 1;
if ((!(*KEYS & KEY_LEFT)) && (PrimaryDirection == 3))
{
if (!(*KEYS & KEY_B))
{
if ((MoveLeft == 0) && ((CurrentFrame < 100) || (CurrentFrame > 140))) CurrentFrame = 100;
if ((MoveLeft == 1) && ((CurrentFrame < 50) || (CurrentFrame > 99))) CurrentFrame = 50;
}else if ((CurrentFrame < 1) || (CurrentFrame > 48)) CurrentFrame = 0;
}
if ((!(*KEYS & KEY_RIGHT)) && (PrimaryDirection == 4))
{
if (!(*KEYS & KEY_B))
{
if ((MoveRight == 0) && ((CurrentFrame < 250) || (CurrentFrame > 290))) CurrentFrame = 250;
if ((MoveRight == 1) && ((CurrentFrame < 200) || (CurrentFrame > 249))) CurrentFrame = 200;
}else if ((CurrentFrame < 151) || (CurrentFrame > 198)) CurrentFrame = 150;
}
if ((!(*KEYS & KEY_UP)) && (PrimaryDirection == 1))
{
if (!(*KEYS & KEY_B))
{
if ((MoveUp == 0) && ((CurrentFrame < 400) || (CurrentFrame > 440))) CurrentFrame = 400;
if ((MoveUp == 1) && ((CurrentFrame < 350) || (CurrentFrame > 399))) CurrentFrame = 350;
}else if ((CurrentFrame < 301) || (CurrentFrame > 348)) CurrentFrame = 300;
}
if ((!(*KEYS & KEY_DOWN)) && (PrimaryDirection == 2))
{
if (!(*KEYS & KEY_B))
{
if ((MoveDown == 0) && ((CurrentFrame < 550) || (CurrentFrame > 590))) CurrentFrame = 550;
if ((MoveDown == 1) && ((CurrentFrame < 500) || (CurrentFrame > 549))) CurrentFrame = 500;
}else if ((CurrentFrame < 451) || (CurrentFrame > 498)) CurrentFrame = 450;
}
if ((!(*KEYS & KEY_LEFT)) || (!(*KEYS & KEY_RIGHT)) || (!(*KEYS & KEY_UP)) || (!(*KEYS & KEY_DOWN))) CurrentFrame++;
if (CurrentFrame == LastFrame)
{
switch (PrimaryDirection)
{
case 1 : DMASpriteCopy(DirUp, StandStill); break;
case 2 : DMASpriteCopy(DirDown, StandStill); break;
case 3 : DMASpriteCopy(DirLeft, StandStill); break;
case 4 : DMASpriteCopy(DirRight, StandStill); break;
}
PrimaryDirection = 0;
CurrentFrame = 1000;
LastFrame = 0;
}
vsync();
MoveSprite(&sprites[0],Marlex,Marley);
CopyOAM();
if (CurrentFrame < 1000) LastFrame = CurrentFrame;
}
while(1);
}
|
Keep in mind, I haven't optimized the source a whole lot yet, and this version doesn't display the sprite properly (if at all, I can't remember) because I'm still working on the DMA, but the same thing happens when the sprite does show up - the music plays, but the graphics are slow and choppy. I can post a copy that looks worse than this that DOESN'T use DMA, and the sprite shows up properly, but I'm afraid proper programmers would start egging my house...
#22725 - dagamer34 - Mon Jun 28, 2004 12:15 am
Looking at your code, I seriously recommend creating a library for working code so that bugs that occur can easily be found. If you don't, mistakes will happen too easily. For example, do you really want to keep typing (!(*KEYS & KEY_LEFT)) every time you need to detect a keypress? It could just as easily be something as simple as KeyDown(KEY_LEFT). There's not chance of you forgetting to put the '!' operator, which would lead to some unexpected results.
These are just examples to make a point, not the absolute solution to the problem at hand.
_________________
Little kids and Playstation 2's don't mix. :(
#22731 - sgeos - Mon Jun 28, 2004 5:38 am
Would not it just be simpler to use one spot in VRAM for all of your sprite's frames?
That would make it much easier to add multiple animated sprites to your game:
Code: |
/* Character 0 looks right
*/
DMASpriteCopy(CHARACTER_FRAME(0), LookRight);
/* Character 1 is running
*/
DMASpriteCopy(CHARACTER_FRAME(1), Run1); |
-Brendan
#22732 - mymateo - Mon Jun 28, 2004 7:03 am
sgeos - I would like to use only one spot to store my sprites. But here's the problem:
VRAM - I only have 32 KB to store my sprites, and so far I have 60 KB. There are more frames to add to Marle - then, if I take it further, I could have Crono, Lucca, Frog, Ayla, Robo and Magus. You have to figure I have only 60 frames of about 100 or so, and multiply that by all the characters, that's 700 frames. Even doing some tricks like reducing the colors down from a standard 256 color palette to individual cutom-palettes of 16 colors, using smaller sprites, and mirroring the actions (facing left mirrors facing right, and facing up re-uses frames, just mirrored -- but I have each move depicted by its own frame) - And so on, we're still looking at easily more than 32 KB. If I'm going to learn how to do this stuff, I want to learn in a way that allows me to expand.
ROM - Sure, I could leave the sprites' data in a CONST and copy it from there, but that removes the option of copying via DMA for the time being. I'm still having troubles copying the data from a known location, let alone trying to figure out where the #ell the compiler is stuffing them into the ROM. Good idea, but not for me, not for now.
Which leaves me...
EWRAM - The only spot where I not only know where it is, but also has the storage space I need for the sprites, and I can use DMA. Sure, I don't have it working properly, but I at least have it working... kinda...
It's all a learning process for me, and in the future, I will find a way to be more efficient.
But as far as your sample code goes, aren't I doing that already?
-----------------------
dagamer34 - You've given me a thought.
You've pointed out that, in my code, I constantly use (!(*KEYS & KEY_LEFT)) when I need to figure out what key has been presed. Do you think it's possible that the slow down comes from one or both of:
(1) GBA constantly reading the key pressed, instead of just a variable that stores the relevant data until the next time the code should expect the user to press a different key.
Or in english...
In each loop, the user is expected to have either some or no buttons presed. So I should just get the keys pressed at the beginning of the loop, store it in a variable, and use it until the loop repeats, thus reducing the work the GBA has to do because it's not constantly testing for which keys are pressed. Hmmm... am I making sense? I hope so. I have a habit of not getting my point across...
or (2) That since my code expects the KEYS to stay the same from the beginning of the loop through to the end, that perhaps the keys being pressed changes whilst in the loop effects how the rest of the loop interprets what the sprite should be doing, thus causing it to have a hiccup?
In any case, you've given me just one more thing to optimize. The more, the better!
And as soon as I get some sleep, I'm going to fix up my code a bit. Thanks, guys!
#22755 - dagamer34 - Mon Jun 28, 2004 8:12 pm
No, that wasn't what I was talking about. What I am concerned with is that you are making "spaghetti" code. The problem could simply be that you added something incorrectly to your code to make it slow down. The problem I see is that you can't be certain your basic code to control the GBA isn't the problem.
Let me show you one more time what I mean:
Code: |
int Index;
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 0)] = Marle_Left_Walk_1Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 1)] = Marle_Left_Walk_2Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 2)] = Marle_Left_Walk_3Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 3)] = Marle_Left_Walk_4Data[Index];
for (Index = 0; Index < 512; Index++) EWRAM[Index + (512 * 4)] = Marle_Left_Walk_5Data[Index];
some more... |
It is too easy to mess this up every time you need copy copy data to EWRAM or VRAM. I strongly suggest you throw code like this into a library. It makes the main file much easier to read, takes less space, is well tested, and becomes MUCH easier to find the problem.
By the way, you are messing with VRAM before a VSync; that could led to problems. Go and find a profiler and see how long it takes to process the key handling part of your code.
_________________
Little kids and Playstation 2's don't mix. :(
#22758 - Miked0801 - Mon Jun 28, 2004 9:08 pm
Code: |
int Index, j;
for (Index = 0; Index < 512; Index++)
{
for(j=0; j<5; j++)
{
EWRAM[Index + (512 * j)] = Marle_Left_Walk_Data[j][Index];
}
}
|
A bit easier to read and maintain right? Just need to check your data to a 2-dim array - or if you can't figure out how to do that:
Code: |
int Index;
for (Index = 0; Index < 512; Index++)
{
EWRAM[Index + 512 * 0] = Marle_Left_Walk_1Data[Index];
EWRAM[Index + 512 * 1] = Marle_Left_Walk_2Data[Index];
EWRAM[Index + 512 * 2] = Marle_Left_Walk_3Data[Index];
EWRAM[Index + 512 * 3] = Marle_Left_Walk_4Data[Index];
EWRAM[Index + 512 * 4] = Marle_Left_Walk_5Data[Index];
}
|
Which is still cleaner than 5 seperate loops!
Or perhaps (assuming walk data is a u8 and u32 aligned), this will be (much) faster yet and looks pretty clean as well:
Code: |
typedef struct _WALK_DATA
{
u32 walkData[512/4];
} WALK_DATA;
void code_somewhere(void)
{
WALK_DATA *destPtr;
destPtr = (WALK_DATA *) EWRAM;
*destPtr++ = *((WALK_DATA *)Marle_Left_Walk_1data);
*destPtr++ = *((WALK_DATA *)Marle_Left_Walk_2data);
*destPtr++ = *((WALK_DATA *)Marle_Left_Walk_3data);
*destPtr++ = *((WALK_DATA *)Marle_Left_Walk_4data);
*destPtr++ = *((WALK_DATA *)Marle_Left_Walk_5data);
}
|
#22770 - mymateo - Tue Jun 29, 2004 4:27 am
Just a tiny update on what I've done...
Some of that "spaghetti" code mentioned... well, I think I just beat the living tar out of my DMASpriteCopy function. Take a peek at what I've done, and tell me if this is what you were referring to. I mean, is the old function considered to be "spaghetti code" compared to this?
Here, I've changed my constants. As you've noticed, everything gets loaded in the same way... Left, Right, Up then Down... Walk, then Run, then standing, looking left then right. So...
Code: |
#define DirLeft 0
#define DirRight 1
#define DirUp 2
#define DirDown 3
#define Walk1 0
#define Walk2 1
#define Walk3 2
#define Walk4 3
#define Walk5 4
#define Walk6 5
#define Run1 6
#define Run2 7
#define Run3 8
#define Run4 9
#define Run5 10
#define Run6 11
#define StandStill 12
#define LookLeft 13
#define LookRight 14 |
And so this allows me to change my DMASpriteCopy function to...
Code: |
void DMASpriteCopy(u8 DMADirection,u8 DMAFrame)
{
u32 SourceAddress, DestinationAddress;
DestinationAddress = 0x06014000;
SourceAddress = SpriteArea + (512 * ((15 * DMADirection) + DMAFrame));
REG_DMA0SAD = SourceAddress;
REG_DMA0DAD = DestinationAddress;
REG_DMA0CNT = 512 | ENABLE_DMA;
}; |
Anyway, just an update... Back to the code, gonna try and beat the source down a bit.
#22773 - sgeos - Tue Jun 29, 2004 6:16 am
Copying from const storage is just like copying from anywhere else:
Code: |
const u16 my_big_array[MY_BIG_ARRAY_SIZE];
u16 my_other_array[MY_OTHER_ARRAY_SIZE]; |
Where is my_big_array located? It is located at my_big_array or &my_big_array[0]. Where is that in memory? I don't care. If I ever do care, I'll set a pointer to my_big_array and look at the value of the pointer. (const)
Where is my_other_array located? It is located at my_other_array or &my_other_array[0]. Where is that in memory? I don't care. If I ever do care, I'll set a pointer to my_other_array and look at the value of the pointer. (not const)
Code: |
/*** Big pointer action
*/
u16 *big_pointer;
u16 *other_pointer;
big_pointer = my_big_array;
other_pointer = my_other_array; |
If you use DMA channel 3, you should be able to copy from any location on the GBA to any other location of the GBA. (Within reason. You also can not DMA to/from SRAM).
You might want to consider picking up a book specifically on C programming. I learned from "Practical C Programming", by Steve Oualline. ISBN 1-56592-306-5.
It seems to me that you would have an easier time dealing with some of the GBA's hardware specific details if you had a slightly stronger command of C.
-Brendan
#22776 - mymateo - Tue Jun 29, 2004 7:42 am
Hi sgeos
Two questions for you:
(1) How well does a pointer that contains a 16-bit (0x????) value fare when trying to store a 32-bit (0x????????) address?
(2) You specifically mention DMA3 as the channel to use as it can copy from almost anywhere to almost anywhere. Must it be DMA3, or is DMA0 good enough? Is there any difference?
Thanks for the feedback.
I have a book on programming for C. The problem is that it's based on programming for the computer, so all the examples rely on the PC - how it works, memory sizes, user input, etc. so it's very difficult to use the information to program on GBA. I have attempted to use it to actually learn how to use pointers, but it doesn't give any examples that would be of specific use to the GBA.
And yes, I would have an easier time programming if I knew C better. But the truth of the fact is that I'm not going to learn C to program games, and I'm not trying to program a game to learn C, I'm trying to do both. Learn C to make games, but use making the games a way to push myself to properly understand and learn more about C. I appreciate the help and patience everyone is showing.
#22778 - sgeos - Tue Jun 29, 2004 8:30 am
mymateo wrote: |
(1) How well does a pointer that contains a 16-bit (0x????) value fare when trying to store a 32-bit (0x????????) address? |
Pointers contian memory addresses, not values. You use a pointer to change the value in the memory address that the pointer contains. A pointer to u32 and a pointer to s16 can contain the same address. A u32 pointer will try to interpert the data at its address as u32 information. A s16 pointer will try to interpert that information as s16 data.
On the GBA all pointers are 32 bit.
mymateo wrote: |
(2) You specifically mention DMA3 as the channel to use as it can copy from almost anywhere to almost anywhere. Must it be DMA3, or is DMA0 good enough? Is there any difference? |
gbatek covers this. I suspect cowbite does as well.
DMA 0 copies from internal memory to internal memory.
DMA 1 copies from any memory to internal memory.
DMA 2 copies from any memory to internal memory.
DMA 3 copies from any memory to any memory.
mymateo wrote: |
I have a book on programming for C. The problem is that it's based on programming for the computer, so all the examples rely on the PC |
The GBA is a computer- it just doesn't have an OS. If you want to do any text output on the GBA, chances are that you are going to use siprintf. siprintf works more or less like printf. If you are going to construct look up tables, the best place to do that is your PC. The PC is the best place to learn C.
Pointers are basic. They work the same on all computer architectures that I am aware of. Once you know how they work on the PC, you'll know how they work on the GBA.
-Brendan
#22782 - mymateo - Tue Jun 29, 2004 9:20 am
sgeos
That's all I needed to finally get the DMA to work properly. Now my function for copying sprites via DMA is nice and small, and my sprite displays properly.
If I could kiss your feet, I would!
For anyone reading this post wanting to gleam some info, this is the final product of my DMASpriteCopy function.
Code: |
// Added this at the top
u16* CharMem2 = (u16*)0x06014000;
void DMASpriteCopy(u8 DMADirection,u8 DMAFrame)
{
//Added 'pointer' to the names for sanity's sake
u16 *SourceAddressPointer;
u16 *DestinationAddressPointer;
DestinationAddressPointer = CharMem2;
SourceAddressPointer = &EWRAM[512 * ((15 * DMADirection) + DMAFrame)];
AAS_DoDMA3( SourceAddressPointer, DestinationAddressPointer, 512 | ENABLE_DMA );
}; |
It works so beautifully!
Now I just need to speed up the code. I'm still getting quite a bit of lag.
jd, I'm sorry about the comments I made about the AAS. It just seemed fishy to me at first that as soon as I started to use the EWRAM and sound at the same time, nothing would work. Alas, I have been proven wrong.
#22785 - mymateo - Tue Jun 29, 2004 9:59 am
Hip-hip-hooray!
Instead of going to bed on time, I tinkered a little. I'm happy to say, I tracked down the problem. Something about the nature of the code didn't like what I was doing for vsync.
Code: |
while (*(volatile unsigned short *)0x04000006 != 160); |
When I didn't call vsync (you can hit me for not trying that sooner), my sprite zipped around like there was no tomorrow. So I tracked down a different method to vsync.
Code: |
while (*((volatile unsigned short *)0x04000004) & 1);
while (!(*((volatile unsigned short *)0x04000004) & 1)); |
And I am happy to report, I am running at 60 FPS with music, and no skipping or jumping or jerking anywhere!
THANK YOU EVERYONE!
#22790 - poslundc - Tue Jun 29, 2004 1:48 pm
You can understand what was happening if you think about the actual meaning of your loop. You are saying to loop infinitely while the vertical scan counter is not equal to 160. But you currently are running very little code. So little, in fact, that you can complete an entire loop before a scanline is completed.
So when the scanline counter reaches 160, your VBlank routine begins, finishes, and then your VDraw routine begins and finishes, and you reach the while () statement again. Except the scanline counter is still at 160, so your code runs a second time, a third time, etc. until finally the scanline counter reaches 161.
What you should be doing is just on the line before you wait for VBlank, wait to make sure that if you are not still on the same scanline, ie.
while (*(volatile unsigned short *)0x04000006 == 160);
The form you posted also works, in essentially the same way.
Dan.
#22798 - jd - Tue Jun 29, 2004 6:52 pm
mymateo wrote: |
jd, I'm sorry about the comments I made about the AAS. It just seemed fishy to me at first that as soon as I started to use the EWRAM and sound at the same time, nothing would work. Alas, I have been proven wrong. |
No problem. I'm glad you've got everything working.
mymateo wrote: |
Code: |
// Added this at the top
u16* CharMem2? ? = (u16*)0x06014000;
void DMASpriteCopy(u8 DMADirection,u8 DMAFrame)
{
? //Added 'pointer' to the names for sanity's sake
? u16 *SourceAddressPointer;
? u16 *DestinationAddressPointer;
? DestinationAddressPointer = CharMem2;
SourceAddressPointer = &EWRAM[512 * ((15 * DMADirection) + DMAFrame)];
AAS_DoDMA3( SourceAddressPointer, DestinationAddressPointer, 512 | ENABLE_DMA );
};
|
|
I strongly recommend that you avoid using global variables unless absolutely necessary. They're slower than local variables and also consume memory even when the function isn't being used. It would be better if you got rid of CharMem2 and just did:
Code: |
DestinationAddressPointer = (u16*)0x06014000;
|
An even better (more readable and future-proof) solution would be to make CharMem2 a #define. However, if you're planning to change its value whilst the code is running then it need to be a function parameter instead.
#22809 - dagamer34 - Tue Jun 29, 2004 10:00 pm
sgeos wrote: |
DMA 0 copies from internal memory to internal memory.
DMA 1 copies from any memory to internal memory.
DMA 2 copies from any memory to internal memory.
DMA 3 copies from any memory to any memory.
-Brendan |
DMA channels 1 & 2 I think are hardwired to the sound FIFOs so they can't be used in general purpose operations.
Also, you can't have more than 1 DMA running at a time. The higher level DMAs will interrupt the lower level ones.
_________________
Little kids and Playstation 2's don't mix. :(
#22828 - tepples - Wed Jun 30, 2004 12:49 am
dagamer34 wrote: |
DMA channels 1 & 2 I think are hardwired to the sound FIFOs so they can't be used in general purpose operations. |
Where did you read this? GBATEK's description of the DMA channels states that channels 1 and 2 can have any destination address in 0x02000000-0x07FFFFFE. You're probably thinking of the "special trigger" associated with channels 1 and 2.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#22829 - dagamer34 - Wed Jun 30, 2004 12:51 am
tepples wrote: |
dagamer34 wrote: | DMA channels 1 & 2 I think are hardwired to the sound FIFOs so they can't be used in general purpose operations. |
Where did you read this? GBATEK's description of the DMA channels states that channels 1 and 2 can have any destination address in 0x02000000-0x07FFFFFE. You're probably thinking of the "special trigger" associated with channels 1 and 2. |
Probably.
_________________
Little kids and Playstation 2's don't mix. :(