#18100 - ProblemBaby - Sat Mar 20, 2004 1:44 pm
is: while (REG_VCOUNT < 160);
important to do every loop?
it slows down my program by many 100%
I dont understand really what it do!
cuz the frame is drawn exact the same without it!
plz tell me what it do, and how I can make it faster or if I can skip it!
#18102 - yaustar - Sat Mar 20, 2004 2:34 pm
It is very important. It basically says "Wait until the frame has stopped being drawn". If you start moving stuff such as sprites in mid frame, you are going to end with misaligned graphics.
_________________
[Blog] [Portfolio]
#18104 - Paul Shirley - Sat Mar 20, 2004 3:23 pm
removed
Last edited by Paul Shirley on Sun Mar 28, 2004 8:55 pm; edited 1 time in total
#18111 - sajiimori - Sat Mar 20, 2004 6:16 pm
If you are not ready to use interrupts just yet, at least change it to REG_VCOUNT != 160. It will still eat up battery life, but it solves the other problem Paul mentioned.
Feel free to leave vsync out of your program for a while. You'll eventually find that you need it, and maybe it'll be informative to come to that conclusion on your own. ^_^
#18113 - abilyk - Sat Mar 20, 2004 7:11 pm
sajiimori wrote: |
If you are not ready to use interrupts just yet, at least change it to REG_VCOUNT != 160. It will still eat up battery life, but it solves the other problem Paul mentioned. |
Even this method could allow the loop to run more than once per frame. If your main loop finishes in less than one scanline (not likely, but possible), REG_VCOUNT will still == 160 and the loop will run again. I've seen this used:
Code: |
while(REG_VCOUNT != 160);
while(REG_VCOUNT != 161); |
VCOUNT values must be 160, then 161, in sequence, to get through both these loops. You lose a scanline's worth of Vblank, but it guarantees that the loop will only run once per frame.
#18124 - Paul Shirley - Sun Mar 21, 2004 2:59 am
removed
Last edited by Paul Shirley on Sun Mar 28, 2004 8:55 pm; edited 1 time in total
#18126 - abilyk - Sun Mar 21, 2004 3:56 am
Good points, I hadn't thought about those cases. I agree, using interrupts is a much more efficient and dependable way to do it. However, some newbies may want to hold off on using interrupts til more comfortable with the system. That was the case with me. If so, I'd suggest they do some simple profiling, and use what they learn about the behavior of their code (does your non-drawing code eat into vblank? does your vblank code run short?) to work out a polling check that works for their specific needs.
#18128 - poslundc - Sun Mar 21, 2004 4:40 am
I don't see what's so hard about this.
Code: |
while (1)
{
// VDraw code
while ((volatile u16)REG_VCOUNT < 160);
// VBlank code
while ((volatile u16)REG_VCOUNT >= 160);
} |
Break that.
Dan.
#18133 - tepples - Sun Mar 21, 2004 5:20 am
If a GBA game is programmed anything like Super Mario Bros. for NES was, that'll break it. SMB1 (and many other games by the same Nintendo programmers) set the CPU into the 6502's equivalent of a Halt state and just had the NES's vblank interrupt call the game's main loop. The main loop spun until draw time and then set a scroll value after scanline 31.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#18137 - poslundc - Sun Mar 21, 2004 2:40 pm
Tepples, my point was that if you don't want to use the halt/interrupt method it's very easy to set up a loop/hang-based system. Earlier on in the thread people were having trouble doing that.
Dan.
#18189 - delbogun - Mon Mar 22, 2004 3:01 pm
How do I do it with interrupts?
#18241 - johnny_north - Tue Mar 23, 2004 12:31 am
I'll give you the basics of how I do it, and then I'll fill you in on the details if you need.
Download Jeff Frohwein's crt0 and lnkscript http://www.devrs.com/gba/files/crtls.zip
Browse the crt0.s file and uncomment
.equ __InterruptSupport, 1
.equ __MultipleInterrupts, 1
When you build your project, assemble the crt0.s to crt0.o and link uning the included lnkscript. Here's what my linker flags look like:
LDFLAGS = -L $(LIBDIR) -L $(LIBDIR2) -L $(PRJDIR) -T lnkscript -lswi -lAAS -lm -lg -lstdc++ -lgcc -nostartfiles -Wl,-Map,bin.map
I think the -nosartfiles option forces (devkitadv anyway) to exclude the default crt0.o and lnkscript
Define the intrupt table as a global in your code:
void (*IntrTable[])() = {
vbl, // v-blank
0, // h-blank
0, // vcount
0, // timer0
0, // timer1
0, // timer2
0, // timer3
0, // serial
0, // dma0
0,// dma1
0, // dma2
0, // dma3
0, // key
0 // cart
};
Define the vbl function
void vbl(){
//do whatever
}
start the interupt for the vbl in you code somewhere (using defines from gba.h + some bit defines):
REG_IME = 0x00;// Disable interrupts
REG_IE |= BIT0;// Enable V-Blank IRQ.
REG_DISPSTAT |= BIT3;// Enable Display V-Blank IRQ also.
REG_IME = BIT0;// Enable interrupts
Let me know if you have troubles.
#18394 - Cearn - Thu Mar 25, 2004 2:34 pm
johnny_north wrote: |
I think the -nostartfiles option forces (devkitadv anyway) to exclude the default crt0.o and lnkscript |
I think -nostartfiles excludes the default crt0.o (and crtbegin.o and crtend.o for C++), but not the linkscript. That's what -Tlnkscript is for.
Also, it was my understanding that you should set the proper bit in REG_IF to indicate the interrupt was handled.
Code: |
void vbl()
{
// blah
REG_IF |= BIT0;
}
|
And if you want to use the BIOS call for this (VBlankIntrWait (swi 0x05)) you also need to set that bit in the bios check flag at 0x03007ff8 (I think it's called REG_IFCHECKBUFF or something)
Code: |
void vbl()
{
// blah
REG_IFCHECKBUFF |= BIT0;
REG_IF |= BIT0;
}
|
that last one had me going for a while when I tried to get VBlankIntrWait to work.
#18397 - Lupin - Thu Mar 25, 2004 3:08 pm
What is the actual difference between the bios call and normal vblank?
I once saw an example code that used this way of clearing the IF register:
REG_IF |= ~INT_KEYBOARD;
i think i will take a look at jeffs interrupt handler and try to implement it myself in iwram...
_________________
Team Pokeme
My blog and PM ASM tutorials
#18402 - Cearn - Thu Mar 25, 2004 3:35 pm
Using a normal VBlank you still need to check whether you're already in a VBlank or not (unless you want to put all your code in the VBlank handler of course). Something like
Code: |
// global
int in_vblank=0;
void vbl()
{
in_vblank=1;
REG_IF |= BIT0;
}
int main()
{
// setup VBlank interrupt
while(1)
{
while(in_vblank == 0) ; // do nothing
// do stuff
in_vblank=0;
}
}
|
With a the VBlankIntrWait BIOS call the CPU is actually switched off until the interrupt occurs (saving battery power), and you don't have to check for the interrupt yourself. To do it like this it's
Code: |
void vbl()
{
// other vbl stuff
// set bit in 0x03007ff8, like GbaTek told us to.
REG_IFCHECKBUFF |= BIT0;
REG_IF |= BIT0;
}
int main()
{
// set up VBlank interrupt
while(1)
{
// put CPU on hold until a VBlank occurs
// the part after the three colons is the "clobber list";
// swi 5 uses r0 and r1, so you don't want that used in
// other places. See GCC docs, point 6.34
// Also, if you're in ARM mode, use 0x050000, not 0x05.
asm volatile("swi 0x05" ::: "r0", "r1");
// do stuff
}
}
|
At least, that's what I think people use the VBlank interrupt
and swi 5 for. Plz correct me if I'm wrong.
Lupin wrote: |
I once saw an example code that used this way of clearing the IF register:
REG_IF |= ~INT_KEYBOARD;
|
Hmmm, maybe INT_KEYBOARD was already inverted? (i.e., the one bit 0 and the rest 1), otherwise using |= would make little sense.
#18470 - LOst? - Fri Mar 26, 2004 9:28 pm
If I hadn't so hard probelms whith the speed of the GBA, I would do any drawing/scrolling/sprite copying/palette copying during the VBlank interrupt, and then force the main loop to only show a frame when I want, by turning on and off the VBlank.