#50578 - justinGBA - Wed Aug 10, 2005 8:59 pm
So i was over on TONC's web site, and was reading up on the windowing... Well i'm not the best when it comes to understanding how to set bits. So this is more or less a request for some working fragments of windowing code. I even fired up his source code, and that is just a big unorganized mess.
Or... Can someone help me get it working with my current code...
For my video i do this...
Code: |
#define InitVideo(mode) *(unsigned long*)0x4000000 = (mode)
//Tile based Modes
#define Mode0 0x0
#define Mode1 0x1
#define Mode2 0x2
//Bitmap based Modes
#define Mode3 0x3
#define Mode4 0x4
#define Mode5 0x5
/* --Backgrounds To Enable-- */
#define Bg0 0x100
#define Bg1 0x200
//Bg2: default for Mode3
#define Bg2 0x400
#define Bg3 0x800
//USE OF FUNCTION
InitVideo( Mode0 | OBJ_ENABLE | OBJ_MAP_1D | Bg0 | Bg1 );
|
So what value do i or into this to make windowing turned on...?
his site says set REG_DISPCNT{d,e,f} respectively.
then to set window bounds i do this?
Code: |
// win0: l= 36, r= 76, t= 20, b= 60 (40x40 window)
// win1: l= 12, r= 228, t= 12, b= 148 (12 pixel margin)
// Obj at win0, bg0 at win1, bg1 at winOut
REG_WIN0H= (36<<8) | 76;
REG_WIN1H= (12<<8) | 228;
REG_WIN0V= (20<<8) | 60;
REG_WIN1V= (12<<8) | 148;
REG_WININ = (WIN_BG0<<8) | (WIN_OBJ);
REG_WINOUT= WIN_BG1;
|
So i guess i just need to know how to or the DEF of my REG_DISPCNT.
#50590 - headspin - Wed Aug 10, 2005 11:31 pm
Here is some of my windowing code...
Code: |
#define WIN1_ENABLE 0x2000
#define WIN2_ENABLE 0x4000
void set_window()
{
REG_WIN0H = (8 << 8) | 230;
REG_WIN0V = (120 << 8) | 152;
REG_WININ = 4095;
REG_WINOUT = 4094;
REG_DISPCNT |= WIN1_ENABLE;
}
void off_window()
{
REG_DISPCNT ^= WIN1_ENABLE;
} |
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game
#50636 - Cearn - Thu Aug 11, 2005 8:23 am
justinGBA wrote: |
... REG_DISPCNT{d,e,f} ... |
Means bits 0xD, 0xE and 0xF of REG_DISPCNT (i.o.w., bits 13, 14, 15). I could swear I mentioned it somewhere ...
#51587 - jarobi - Mon Aug 22, 2005 4:25 am
I just got started on windowing and interrupts. I was wondering if there was a way to make neat window shapes using hblank interrupts. If I run the following code during hblank, I would think that it gives me a diagonal line across the screen, but the window shifts and twists in every imaginable way instead:
Code: |
#define setWin0Bounds(x1, x2, y1, y2) REG_WIN0H=((int)(x1)<<8)|(int)(x2);REG_WIN0V=((int)(y1)<<8)|(int)(y2);
int x = 0;
void hblank_isr()
{
setWin0Bounds(x, x+16, REG_VCOUNT, REG_VCOUNT+1);
}
void vblank_isr()
{
x = 0;
}
|
_________________
Nihongo o hanasemasen!
#51608 - Cearn - Mon Aug 22, 2005 8:47 am
HBlank interrupts happen after a given REG_VCOUNT, not before. You shouldn't be getting anythgin at all then. If you're updating every hblank, just use the whole vertical range (0, 160). For a diagonal line, x should be updated in the hblank too.
Code: |
void hblank_isr()
{
setWin0Bounds(x, x+16, 0, 160)
x++;
}
|
This one seems to work ... with the exception that the top line starts with x=68 because there are hbl interrupts inside the VBlank too, but I'm sure you'll find a way around those.
#51688 - jarobi - Mon Aug 22, 2005 11:59 pm
Your example actually worked! Thank you very much Cearn. I see what you mean about x starting at 68 though, but that shouldn't be too much of an issue for me. It's a lot better than that television static effect I was getting before.
_________________
Nihongo o hanasemasen!
#51879 - jarobi - Wed Aug 24, 2005 7:38 am
I managed to solve the issue of my window starting 68 px more to the right than expected by initializing my x values to 67 minus their original value. I then proceeded to create a spotlight effect with my window by calling the following function every hblank:
Code: |
void adjustWindow()
{
setWin0Bounds(x0/3, x1, 0, 160);
x0++;
x1++;
}
|
I would get the predictable result using an emulator but when I tried it on hardware, all hell broke loose; by that I mean that the window was doing that same thing I mentioned earlier. Almost by coincidence, I was simultaneously researching division on the GBA. I found out that using the / operator or even using the bios division function takes hundreds of cycles. I'm guessing that is a little bit too much to be in a hblank routine. However, when I divide by 2 in the above function, everything works out OK for the obvious reason that the operation is optimized by the compiler. So, to create neat window effects that involve "complex" math, do I have to store my x values in a table instead or is there another way?
_________________
Nihongo o hanasemasen!
#51906 - tepples - Wed Aug 24, 2005 1:58 pm
Sixty-eight? That number sounds familiar. That's how many scanlines tall the vblank is.
The window isn't 68 pixels to the right; it's 68 pixels up. You're incrementing x even on hblank interrupts that occur during vblank. (Hblank interrupts occur during vblank; hblank DMA doesn't.) You're supposed to set the X position based on the current value of the VCOUNT register.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#51978 - jarobi - Thu Aug 25, 2005 6:47 am
I was actually aware of incrementing x in the vblank. The reason why I did not set the x position with respect to the value in the vcount reg is because the hardware messes up my window if I do; this works in an emulator though. It seems to me that the hardware doesn't like it when I read from the vcount register during hblank. I have even tried incrementing some value every hblank and I get the same crappy results. Any clues as to what my problem is?
Edit: I wonder if my accessing of the vcount register is somehow taking more than the ~250 cycles allowed in hblank?
_________________
Nihongo o hanasemasen!
#52000 - tepples - Thu Aug 25, 2005 11:53 am
The most reliable way to set a window is with hblank DMA, not hblank interrupts. Or are you already using all four DMA channels for other purposes?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#52042 - jarobi - Thu Aug 25, 2005 9:19 pm
So, what you're saying is that I should competely scrap hblank interrupts in favour of hblank DMA? If I were to do that, then would it be valid for me to do the following in code:
Code: |
//main game loop (pseudo-code)
while (1)
{
waitForVBlank();
int windowValV, windowValH;
windowValV = windowSettings;
windowValH = otherSettings;
dmaCopy(&windowValV, REG_WIN0V, 32BITCPY, START_HBLANK);
dmaCopy(&windowValH, REG_WIN0H, 32BITCPY, START_HBLANK);
otherStuffToDo();
}
|
Is there something that I am missing here? Oh yeah, I am not using DMA for anything inside of my main game loop.
_________________
Nihongo o hanasemasen!
#52067 - tepples - Fri Aug 26, 2005 2:29 am
Yes, scrap interrupts for now.
Source: The DMA happens after scanline rendering, not before. You'll need to set up an array, manually copy element 0 of the array into the register during vblank, and then start the source address at element 1.
Destination: You only need to DMA to the WIN0H register, which is 16 bits wide.
Size: You need to use start on hblank, repeating, 16-bit transfers, and a transfer size of 1 word.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#52077 - jarobi - Fri Aug 26, 2005 5:48 am
I got "the white screen of death"! I think I did exactly what you told me to do, but it just won't work... either that or my brain won't work; more likely the latter than the former :). Anyhow, this is what I have in my main loop.
Code: |
int main()
{
Background demon_bk;
u16 win_values[160];
int x, i, my0, mx0, my1, mx1;
/*REG_DISPSTAT |= IRQ_VB|IRQ_HB;
irqEnable(INT_VBLANK, doSomething);
irqEnable(INT_HBLANK, adjustWindow);
interruptEnable();*/
my0 = 2;
mx0 = 1;
my1 = 1;
mx1 = 1;
for (i = 0, x = 0; i < 160; i++, x++)
{
win_values[i] = ((u16)Div(x*my0, mx0))<<8|((u16)Div((x+1)*my1, mx1));
}
demon_bk.bgNumber = 2;
demon_bk.attributes = BG_COLOR_256
|ROTBG_SIZE_1
|BG_PRIORITY(3)
|CHAR_BASE_BLOCK(0)
|SCREEN_BASE_BLOCK(24)
|WRAP_AROUND;
loadBGPalette((u16*)demonPal);
loadBGTiles((u16*)demonData, 0, 256, 0);
loadBGMap((u16*)demon_map, 24, 16, 16);
REG_BG2CNT = demon_bk.attributes;
setWin0Content(W0_BG2);
setMode(MODE_2|BG2_ENABLE|WIN0_ENABLE);
while (1)
{
waitForVBlank();
REG_WIN0H = win_values[0];
while (REG_DISPSTAT & DISPSTAT_VB); //make sure DMA happens after vblank
REG_DMA2SAD = (u32)(&win_values[1]); //28-bit address
REG_DMA2DAD = (u32)(®_WIN0H); //27-bit addresses only
REG_DMA2CNT_L = 1;
REG_DMA2CNT_H = SRC_CONTROL(0)|DES_CONTROL(NO_INC_DEC)|START_MODE(XFER_HBLK)|BLOCK_SIZE_16|REPEAT|DMA_ENABLE;
}
return 0;
}
|
Unrelated Thought: I think I totally hijacked this thread :)
_________________
Nihongo o hanasemasen!
#52078 - DekuTree64 - Fri Aug 26, 2005 5:59 am
Hmm, what does your waitForVBlank look like? If it's using the BIOS wait, you do need to have your VBlank interrupt enabled or it'll never break out.
Also, be sure to set the DMA control to 0 before you set it back up again. Otherwise it won't pay any attention, and will keep blasting whatever memory happened to be after your win_values table into the register.
I don't think that would cause a white screen, but might look a little crazy :)
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#52081 - jarobi - Fri Aug 26, 2005 6:21 am
No dice. Still not working. Here is what my crappy waitForVBlank function looks like:
Code: |
void waitForVBlank()
{
while (!(REG_DISPSTAT & DISPSTAT_VB) && REG_VCOUNT != 160);
}
|
Can you suggest anything better than the above?
_________________
Nihongo o hanasemasen!
#52141 - jarobi - Sat Aug 27, 2005 6:02 am
I almost have my whole windowing issue figured out. I figured out that my call to Div was causing the white screen of death for some reason. In fact, my whole program halted at that point because my background wasn't even in memory. I guess I should stay away from bios functions for a while.
I researched hblank dma a bit and learned that it starts automatically after vblank but never during. I know that was probably mentioned in a previous post, but I guess I needed to hear it in different words. Now that I think about it, that line where I'm waiting on the condition REG_DISPSTAT & DISPSTAT_VB seems really stupid to me now because it was never needed at all. My main issue now is trying to get something other than a square window displaying properly (or at all), but I think I have bothered everyone here enough :).
Thanks everyone for your help! I really learned a lot in the past few days! Maybe I will submit a windowing demo when I master them...
_________________
Nihongo o hanasemasen!
#52194 - jarobi - Sun Aug 28, 2005 7:07 am
I know I said I'd shut up now, but I finally got my stuff working and thought I should share this info for anyone wanting to do neat windowing effects. Remember the code I had in my main loop:
Code: |
//
// EVIL NON_WORKING CODE >:D~
//
waitForVBlank();
REG_WIN0H = win_values[0];
while (REG_DISPSTAT & DISPSTAT_VB); //make sure DMA happens after vblank
REG_DMA2SAD = (u32)(&win_values[1]); //28-bit address
REG_DMA2DAD = (u32)(®_WIN0H); //27-bit addresses only
REG_DMA2CNT_L = 1;
REG_DMA2CNT_H = SRC_CONTROL(0)|DES_CONTROL(NO_INC_DEC)|START_MODE(XFER_HBLK)|BLOCK_SIZE_16|REPEAT|DMA_ENABLE;
|
It seems that things are in the wrong order here! Here's how it should go down.
Code: |
//
// GOOD WORKING CODE O:)
//
REG_DMA3SAD = (u32)(&win_values[1]); //28-bit address
REG_DMA3DAD = (u32)(®_WIN0H); //27-bit addresses only
REG_DMA3CNT_L = 1;
REG_DMA3CNT_H = SRC_CONTROL(0)|DES_CONTROL(NO_INC_DEC)|START_MODE(XFER_HBLK)|BLOCK_SIZE_16|REPEAT|DMA_ENABLE;
waitForVBlank();
REG_DMA3CNT_H = 0;
REG_WIN0H = win_values[0];
|
Since DMA takes two cycles to actually begin (which I recently learned off TONC), I was effectively turning it off before it even had a chance to begin. I hardly think setting something to zero will take more than one or two cycles. So, the hblank DMA needs time to complete, which makes it convenient to put a waitForVBlank function right after it. And to make sure nothing gets screwed up, turn that s--t off upon entering vblank, set the first window value, and then proceed to do whatever.
Thanks again everybody! I'll shut up for good now... at least in this thread... :)
_________________
Nihongo o hanasemasen!