Homebrew Drawing issues with my GBA Program

Kirbedev

Member
OP
Newcomer
Joined
Nov 13, 2022
Messages
5
Trophies
0
Age
16
XP
77
Country
United States
Not sure if this belongs here but I am having an issues with devkitpro and my gba program. I created a function in my `.h` file, which looks like this:
C:
void draw_rect_f(int x, int y, int w, int h, int color)
{
    // Draw a filled rectangle
    for (int i = 0; i < w; i++) {
        for (int j = 0; j < h; j++) {
            ((unsigned short*)0x06000000)[(y + j) * 240 + x + i] = color;
        }
    }
}

I know DMA is probably a better method, but I have no flippin' clue how to get that working. But here's the issue:
Calling
Code:
VBlankIntrWait()
at the end of my main loop causes my shapes to draw at the exact same position every frame, and when I remove it they do move, but with extreme screen tearing, which leads me to believe the rectangles are not drawn/updated fast enough.
C++:
    // ...

    // Main loop
    while (1) {
        // Clear background
        draw_rect_f(0, 0, 240, 160, rgb(200, 75, 75));

        // Update snake
        snake.update();

        // Wait for vblank
        VBlankIntrWait();
    }

What should I do?
 

Adam0x48

New Member
Newbie
Joined
Mar 31, 2023
Messages
1
Trophies
0
Age
20
XP
16
Country
Canada
For a quick and dirty method:
C:
#define REG_VCOUNT (*(volatile unsigned short*)(0x04000006))
void vsync()
{
    while(REG_VCOUNT >= SCREEN_H);
    while (REG_VCOUNT < SCREEN_H);
}
This is considered a bad method because it uses CPU instructions in order to wait, consuming battery.
VBlankIntrWait() is better because it powers down the CPU while it's waiting.
You need to initialize some stuff to get interrupt calls to work. I can't post links yet apparently but look up "tonc GBA" and search for the "Hardware interrupts" page.
The main things you're gonna need from there areirq_init() and irq_add(). Then, go to the "Bios Calls" page, and find the section on VBlankIntrWait to put it all together in a simple demo program. This stuff goes way further than drawing a little rectangle so if you have any questions, don't hesitate to ask.
 
Last edited by Adam0x48,
  • Like
Reactions: Kirbedev

Kirbedev

Member
OP
Newcomer
Joined
Nov 13, 2022
Messages
5
Trophies
0
Age
16
XP
77
Country
United States
For a quick and dirty method:
C:
#define REG_VCOUNT (*(volatile unsigned short*)(0x04000006))
void vsync()
{
    while(REG_VCOUNT >= SCREEN_H);
    while (REG_VCOUNT < SCREEN_H);
}
This is considered a bad method because it uses CPU instructions in order to wait, consuming battery.
VBlankIntrWait() is better because it powers down the CPU while it's waiting.
You need to initialize some stuff to get interrupt calls to work. I can't post links yet apparently but look up "tonc GBA" and search for the "Hardware interrupts" page.
The main things you're gonna need from there areirq_init() and irq_add(). Then, go to the "Bios Calls" page, and find the section on VBlankIntrWait to put it all together in a simple demo program. This stuff goes way further than drawing a little rectangle so if you have any questions, don't hesitate to ask.
Hello!

I really appreciate the response, and I apologize for taking so long to reply.

The part I am struggling with is what I need to define and use to set a specific pixel to a specific color.
While Tonc goes into great detail about how this stuff works, it doesn't seem to specify how a pixel is set. The DMA page it provides looks into displaying images, and doesn't seem to cover this particular use case.

I would greatly appreciate it if you could help point me in the right direction. Thanks in advance.
 

Coto

-
Member
Joined
Jun 4, 2010
Messages
2,979
Trophies
2
XP
2,565
Country
Chile
#include "nds/ndstypes.h"
C++:
    // ...

    // Main loop
    while (1) {
        // Clear background
        dmaFillHalfWords (rgb(200, 75, 75), (void *)0x06000000, (240 * 160 * sizeof(u16)));

        // Update snake
        snake.update();

        // Wait for vblank
        VBlankIntrWait();
    }

This should work. sizeof(u16) is 2 bytes, or a halfword. Because GBA pixel format is 15bit RGB value

If the RGB conversion doesn't work, try using
Code:
#define        RGB(r,g,b)    ( ((r)&31) | (((g)&31)<<5) | (((b)&31)<<10) )
 
  • Like
Reactions: Kirbedev

Kirbedev

Member
OP
Newcomer
Joined
Nov 13, 2022
Messages
5
Trophies
0
Age
16
XP
77
Country
United States
This should work. sizeof(u16) is 2 bytes, or a halfword. Because GBA pixel format is 15bit RGB value

If the RGB conversion doesn't work, try using
Code:
#define        RGB(r,g,b)    ( ((r)&31) | (((g)&31)<<5) | (((b)&31)<<10) )
Thanks for the response. I included ndstypes (installed libnds from the graphic devkitpro installer) but it says dmaFillHalfWords is undefined. When I highlight it in visual studio, it says it's an int, rather than an errorType. What am I missing?
 

Kirbedev

Member
OP
Newcomer
Joined
Nov 13, 2022
Messages
5
Trophies
0
Age
16
XP
77
Country
United States
try adding dma.h

Code:
#include "dma.h"
Strange... it's valid now, but compiling the program raises a number of errors:

Code:
C:\devkitPro\libnds\include\nds\dma.h: In function 'dmaFillWords':
C:\devkitPro\libnds\include\nds\dma.h:216:9: warning: implicit declaration of function 'DMA_FILL'; did you mean 'DMA_FIFO'? [-Wimplicit-function-declaration]
  216 |         DMA_FILL(3) = value;
      |         ^~~~~~~~
      |         DMA_FIFO
C:\devkitPro\libnds\include\nds\dma.h:216:21: error: lvalue required as left operand of assignment
  216 |         DMA_FILL(3) = value;
      |                     ^
C:\devkitPro\libnds\include\nds\dma.h:217:30: error: lvalue required as unary '&' operand
  217 |         DMA_SRC(3) = (uint32)&DMA_FILL(3);
      |                              ^
C:\devkitPro\libnds\include\nds\dma.h: In function 'dmaFillHalfWords':
C:\devkitPro\libnds\include\nds\dma.h:238:21: error: lvalue required as left operand of assignment
  238 |         DMA_FILL(3) = (uint32)value;
      |                     ^
C:\devkitPro\libnds\include\nds\dma.h:239:30: error: lvalue required as unary '&' operand
  239 |         DMA_SRC(3) = (uint32)&DMA_FILL(3);
      |                              ^
 

Coto

-
Member
Joined
Jun 4, 2010
Messages
2,979
Trophies
2
XP
2,565
Country
Chile
you're either passing an instance of variable "0x06000000" (like a pointer), or your devkit environment is broken. Try reinstalling the environment or ask in their forums.

edit: NVM, you've used a NDS header of DMA, which of course cause errors as your target is GBA. Try to replace "dma.h" with whatever header DMA bits for your GBA environment has.

Or you could give it a go to this (first example):
https://austinjadams.com/blog/autograding-gba-dma/
(in the 'u16 color = RED;' would go your 'rgb(200, 75, 75);' as you're using that RGB15 color value repeated on the DMA controller, to overwrite the whole 0x06000000 VRAM, pixel by pixel)


or this:
https://www.coranac.com/tonc/text/dma.htm
 
Last edited by Coto,
  • Like
Reactions: Kirbedev

Kirbedev

Member
OP
Newcomer
Joined
Nov 13, 2022
Messages
5
Trophies
0
Age
16
XP
77
Country
United States
In the end I just used Butano with a custom .h file that defined some addresses. Everything works now. Thanks for the help though!

P.S. You have no idea how good it feels to use C++ again after hours of fumbling away in C
 
Last edited by Kirbedev,
  • Like
Reactions: Coto

Coto

-
Member
Joined
Jun 4, 2010
Messages
2,979
Trophies
2
XP
2,565
Country
Chile
In the end I just used Butano with a custom .h file that defined some addresses. Everything works now. Thanks for the help though!

P.S. You have no idea how good it feels to use C++ again after hours of fumbling away in C
C++ is not as portable as C, besides, it's about 50% slower when functions due to vtables. But it's way easier to get things working at first, due to STD (the library)

edit: lol
 

ghjfdtg

Well-Known Member
Member
Joined
Jul 13, 2014
Messages
1,362
Trophies
1
XP
3,297
Country
C++ can be just as fast as C. It's all down to how you write your code. You can avoid vtables in most cases if you know how.
 

Coto

-
Member
Joined
Jun 4, 2010
Messages
2,979
Trophies
2
XP
2,565
Country
Chile
C++ can be just as fast as C. It's all down to how you write your code. You can avoid vtables in most cases if you know how.
That's rather vague, and false. Do not confuse devs.

C++ is slower on NDS and I can prove it.
I've ported pool / snooker games to NDS (it's just not public due to engine barely fitting in EWRAM) and they cause a 50% decrease in speed the moment name mangling and usage of STD libraries take place. Let alone virtual / friend classes.

on C it's fast, stable and reduces about 400K ~ of memory, libraries at the expense of reimplementing most of the platform specifics yourself.

You could also do C in C++ to speed up things, but it'll never be as fast as pure C code. And i've done that as well


This game needs at least 100mhz to run, but i've made it run at 66mhz through heavy usage of standard OpenGL displaylists and C. On C++ speed would have been unacceptable, like I said, again, 50% slower.


This NDS SnakeGL port is in C++ and CPU is near 100% just because it's in C++ (and if you run a profiler through Visual Studio 2012 you'll see most of the time spent is on STD libraries traversing across game instances, and looping waiting for timer in milliseconds to reach current framerate)

References:
Source 1
 
Last edited by Coto,

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
  • K3Nv2 @ K3Nv2:
    I'll reformat and have a 3tb raid0 m. 2 at least
    +1
  • K3Nv2 @ K3Nv2:
    Lmao that sold out fast
    +1
  • Veho @ Veho:
    Yeet the cat.
    +1
  • K3Nv2 @ K3Nv2:
    Good idea
    +1
  • The Real Jdbye @ The Real Jdbye:
    i thought everybody knew cocktails are like 75% ice
  • Veho @ Veho:
    Yeah but not like this.
  • Veho @ Veho:
    It's not like they're complaining that their Slurpee is 99% ice or something, but if the cocktail calls for "shot of vodka, shot of vermouth, shot of gin, shot of Campari, three shots of juice, squirt of lemon" and ends up being a thimbleful of booze, that's a problem.
  • The Real Jdbye @ The Real Jdbye:
    the funny thing is cocktails in norway are only allowed to have 1 20ml shot of booze
  • The Real Jdbye @ The Real Jdbye:
    so..... yeah
  • The Real Jdbye @ The Real Jdbye:
    we're used to only having a thimbleful of booze
  • Veho @ Veho:
    Booo.
  • The Real Jdbye @ The Real Jdbye:
    same thing if you want whisky on the rocks or something, you can't get a double
  • The Real Jdbye @ The Real Jdbye:
    but you could buy as many shots of whisky (or anything else) as you want and ask for a glass of ice and pour them in
  • The Real Jdbye @ The Real Jdbye:
    it's dumb
  • Veho @ Veho:
    Maybe.
  • Veho @ Veho:
    There was a comparison of the number of Ibuprofen poisonings before and after they limited the maximum dosage per box or per pill (i'll look that up). No limit on the number of boxes you can still buy as many as you want, so people argued it was pointless.
  • Veho @ Veho:
    But the number of (accidental) poisonings dropped because drinking an entire package of ibuprofen pills went from "I need a new liver" to "I need a new box of Ibuprofen".
  • Veho @ Veho:
    Here we have ketoprofen that used to be prescription-only because of the risk of toxic dosages, but then they halved the dose per pill and sell them in bottles of six pills apiece instead of twenty and it doesn't need a prescription any more. Yes you can buy more than one bottle but people simply don't.
  • Psionic Roshambo @ Psionic Roshambo:
    Usually accidentally overdose of ibuprofen here is from people taking like cold medicine then ibuprofen for a headache and the combination is over what they need
    Veho @ Veho: https://imgur.com/gallery/QQkYnQu