gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > consoleInit question

#114687 - Vich - Wed Jan 10, 2007 1:54 am

I have code like asserts that might get called before consoleInit is called. When this happens, the assert might call std::cout or iprintf to output a message. When this output is generated before consoleInit is done, the program breaks and consoleInit that is executed after that seems to fail.
Can I detect - for example for the assert code - if consoleInit is called in an easy way?

(the less nice solution would be to create my own consoleInitProxy that stores a boolean to see if he has been called or not, but that would be a hack that I'd rather not do)

#114709 - simonjhall - Wed Jan 10, 2007 8:44 am

On other platforms I've used, assert calls abort which eventually calls exit. You could always register a function with atexit and put some console init/printf stuff in there...

Dumb question, but why don't you move the console init into somewhere earlier in your code? Or is the thing failing in a global constructor or something?
_________________
Big thanks to everyone who donated for Quake2

#114715 - Vich - Wed Jan 10, 2007 10:32 am

simonjhall wrote:
On other platforms I've used, assert calls abort which eventually calls exit. You could always register a function with atexit and put some console init/printf stuff in there...

Dumb question, but why don't you move the console init into somewhere earlier in your code? Or is the thing failing in a global constructor or something?


I'll write down some extra info:
I have 2 libraries:
- COW: this is a system library with streaming, RTTI, serialization, containers and much more. It contains an cow::Initialize(params) call that can initialize one or more functions, like "INIT_PLATFORM". When the platform gets initialized, the Nintendo DS console initialized.
- AlterNova: this library uses COW and initializes it. It doesn't initialize INIT_PLATFORM, because it's a graphics engine and has it's own platform initializer. an::Initialize takes care of the platform init itself because it doesn't only need a console init, but also graphics init and a screen flip.

The problem is that between cow::Initialize and an::Initialize, there might be an assert that goes off. Since AlterNova calls cow::Initialize without the INIT_PLATFORM parameter(and handles that himself), any asserts inbetween the 2 init calls will make my console break indefinitely.

So, by default the console init happens at cow::initialize, which is early enough in the code to make everything work in all cases, but the problem is that any library/program must have the possibility to just ignore this call.

I hope this makes sense, since I'm still getting awake here :+

[edit]
COW initialize:
Code:

void Initialize()
{
   powerON(POWER_ALL);

   // Set timer data
   TIMER0_DATA   = 0;
   TIMER1_DATA = 0;
   TIMER0_CR   = TIMER_DIV_1024;
   TIMER1_CR   = TIMER_CASCADE;

   // Second screen for 2D
   // Sub BG0 will be used to print text
   videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
   vramSetBankC(VRAM_C_SUB_BG);
   SUB_BG0_CR = BG_MAP_BASE(31);
   // Default font color is white
   BG_PALETTE_SUB[255] = RGB15(31,31,31);

   irqInit();
   irqSet(IRQ_VBLANK, 0);
   irqEnable(IRQ_VBLANK);

   // We want the console on the lower screen
   lcdSwap();

   consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);
}


AlterNova init:
Code:

   // Main NDS init
   powerON(POWER_ALL);

   // Set timer data
   TIMER0_DATA   = 0;
   TIMER1_DATA   = 0;
   TIMER0_CR   = TIMER_DIV_1024;
   TIMER1_CR   = TIMER_CASCADE;

   // Main screen for 3D
   videoSetMode(MODE_0_3D);
   // Second screen for 2D
   videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);   //sub bg 0 will be used to print text
   vramSetBankC(VRAM_C_SUB_BG);
   SUB_BG0_CR = BG_MAP_BASE(31);
   BG_PALETTE_SUB[255] = RGB15(31,31,31);   //by default font will be rendered with color 255
   // Init IRQ
   irqInit();
   irqSet(IRQ_VBLANK, 0);
   irqEnable(IRQ_VBLANK);
   // Swap screens
   lcdSwap();
   // Init console
   consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);


[edit2] I think I know how to fix it, maybe this might work:


Code:

void Initialize(int param)
{
   // ALWAYS CALL THIS CODE --->

   powerON(POWER_ALL);

   // Set timer data
   TIMER0_DATA   = 0;
   TIMER1_DATA = 0;
   TIMER0_CR   = TIMER_DIV_1024;
   TIMER1_CR   = TIMER_CASCADE;

   consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);

   //<---

   if (param & INIT_PLATFORM)
   {
      // Second screen for 2D
      // Sub BG0 will be used to print text
      videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
      vramSetBankC(VRAM_C_SUB_BG);
      SUB_BG0_CR = BG_MAP_BASE(31);
      // Default font color is white
      BG_PALETTE_SUB[255] = RGB15(31,31,31);

      irqInit();
      irqSet(IRQ_VBLANK, 0);
      irqEnable(IRQ_VBLANK);
   }

   // We want the console on the lower screen
   lcdSwap();
}


AlterNova init:
Code:

   // Init COW without INIT_PLATFORM
   cow::Initialize(0);

   // Main screen for 3D
   videoSetMode(MODE_0_3D);
   // Second screen for 2D
   videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);   //sub bg 0 will be used to print text
   vramSetBankC(VRAM_C_SUB_BG);
   SUB_BG0_CR = BG_MAP_BASE(31);
   BG_PALETTE_SUB[255] = RGB15(31,31,31);   //by default font will be rendered with color 255
   // Init IRQ
   irqInit();
   irqSet(IRQ_VBLANK, 0);
   irqEnable(IRQ_VBLANK);
   // Swap screens


I always presumed that consoleInit had to be after the videoSetModeStub, maybe it's not necessary? ... I'll test tonight :)

#114719 - kusma - Wed Jan 10, 2007 12:20 pm

Use your own ASSERT-macro, with your own assert implementation. That way you'll be able to make sure you're in text-mode with the console inited properly before printing the error. Or if you want to keep it gcc-specific, you can implement __assert() yourself. Here's the signature gcc uses:
Code:
void __assert(const char *file, const int line, const char *msg)

Just make sure it uses C linkage, and not C++.

#114721 - Vich - Wed Jan 10, 2007 1:21 pm

kusma wrote:
Use your own ASSERT-macro, with your own assert implementation. That way you'll be able to make sure you're in text-mode with the console inited properly before printing the error. Or if you want to keep it gcc-specific, you can implement __assert() yourself. Here's the signature gcc uses:
Code:
void __assert(const char *file, const int line, const char *msg)

Just make sure it uses C linkage, and not C++.

I'm already using my own assert macro. It's that macro that calls iprintf, but iprintf/cout doesn't know that iprintf doesn't work yet when consoleInit is not executed yet.

#114737 - kusma - Wed Jan 10, 2007 3:32 pm

Vich wrote:
I'm already using my own assert macro. It's that macro that calls iprintf, but iprintf/cout doesn't know that iprintf doesn't work yet when consoleInit is not executed yet.

Well, make your ASSERT-macro call a function that sets it all up, just in case then?

#114742 - wintermute - Wed Jan 10, 2007 4:22 pm

Vich wrote:

I always presumed that consoleInit had to be after the videoSetModeStub, maybe it's not necessary? ... I'll test tonight :)


No, it's not actually necessary to set the video mode before consoleInit.

It's probably perfectly reasonable to have your assert set the video mode and call consoleInit anyway, assuming you want to halt on asserts.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#114743 - Vich - Wed Jan 10, 2007 4:30 pm

wintermute wrote:
Vich wrote:

I always presumed that consoleInit had to be after the videoSetModeStub, maybe it's not necessary? ... I'll test tonight :)


No, it's not actually necessary to set the video mode before consoleInit.

Yeay ^^

Quote:

It's probably perfectly reasonable to have your assert set the video mode and call consoleInit anyway, assuming you want to halt on asserts.


Why would I want my assert to set the video mode and consoleInit? That doesn't make sense to me since that's not assert's job. Plus, Assert is put in my COW library, while I want to override the video mode and console init through the AlterNova library that inherits/uses the COW library.
[edit] Or was it a typo and you meant "COW library"(the library that holds the assert code) instead of "assert"? :)

#114751 - wintermute - Wed Jan 10, 2007 5:06 pm

assert's job is to display an error message.

If you need to set a video mode for that to happen then it kind of becomes assert's job really.

There are situations where all those programming rules you learn in academia go out the window - hint: the simplest approach to the problem is often the best.

When I use asserts I don't see anything particularly wrong with setting the video mode, initialising the console, displaying the error and halting the system.

The default exception handler in libnds does just that actually. I really wouldn't expect the user to have to initialise a console and have a specific video mode just for the sake of being able to see an error screen.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#114756 - Vich - Wed Jan 10, 2007 5:22 pm

wintermute wrote:
assert's job is to display an error message.

If you need to set a video mode for that to happen then it kind of becomes assert's job really.

If it's job is to display something, why should it also initialize the system that allows to display things? Displaying and initializing it are two separate things. Of course they are related, but it's not assert's job to initialize some system.

[edit] Plus, if I'd make output-related code do other stuff like initializing things, it could severely damage the cross-platform compatibility, because the program's flow wouldn't be the same on all platforms.
Those 2 libraries currently have the exact same workflow on Windows/Linux/NintendoDS I'd like to keep it that way.

You do mean something like this, do you? Or is there a misunderstanding here?
Code:

void assert(...)
{
    if (!is_console_initted)
        initConsole()
    // ... do assert stuff
}


Quote:
There are situations where all those programming rules you learn in academia go out the window - hint: the simplest approach to the problem is often the best.

That doesn't really count for me :P
(I have no college/univ degree whatsoever)
If a simple approach is a bad design, I don't code it. (unless there's no other way around it)
If I'd do such things at work, my lead would fire me :)


Quote:
When I use asserts I don't see anything particularly wrong with setting the video mode, initialising the console, displaying the error and halting the system.

The library that controls the asserts normally initializes some systems when that library's init funcion is called, but this might be overwritten by the main application (that's a design decision).

library A -> library B -> main program
The program uses library B. The program calls B::init(), B::Init() then calls A::init().
A::init normally initializes the console(since it's a simple library) and the screen buffer for 2D graphics because it doesn't need more than that. However, library B needs more than 3D graphics, thus tells library A when initializing that he shouldn't initialize the graphics system of the Nintendo DS.

Quote:
The default exception handler in libnds does just that actually. I really wouldn't expect the user to have to initialise a console and have a specific video mode just for the sake of being able to see an error screen.

I'm not using exception handlers at the moment. I thought I'd have to initialize a 2D video mode to have a console visible(because that's what the tutorial app showed me).

[edit] The real solution is to always call console init from the platform library(A) and then let the other init calls be done depending on what graphics mode the user wants from the DS (through library A::init or overwritten by library B::init).

eg. Some console app that uses platform library A will do something like this:
A::init(STREAMING | PLATFORM);
Some graphics app that uses graphics library B(and thus also A) will do something like this:
A::init(STREAMING);
B::init(PLATFORM);

#114764 - wintermute - Wed Jan 10, 2007 6:28 pm

Vich wrote:
wintermute wrote:
assert's job is to display an error message.

If you need to set a video mode for that to happen then it kind of becomes assert's job really.

If it's job is to display something, why should it also initialize the system that allows to display things? Displaying and initializing it are two separate things. Of course they are related, but it's not assert's job to initialize some system.


This is an embedded system with no OS, if you want a display you need to initialise it. Sometimes you just need to accept that there are situations where you can't separate things as much as you'd like.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#114766 - Vich - Wed Jan 10, 2007 6:44 pm

wintermute wrote:
Vich wrote:
wintermute wrote:
assert's job is to display an error message.

If you need to set a video mode for that to happen then it kind of becomes assert's job really.

If it's job is to display something, why should it also initialize the system that allows to display things? Displaying and initializing it are two separate things. Of course they are related, but it's not assert's job to initialize some system.


This is an embedded system with no OS, if you want a display you need to initialise it. Sometimes you just need to accept that there are situations where you can't separate things as much as you'd like.


Of course I have to do it, but I'd rather do it in the Init of my platform library when possible, because that's where it belongs. (and I now know that this is possible, because I can call consoleInit before any other 2D/3D graphics-related init calls)
So I can perfectly separate it.

#114829 - josath - Thu Jan 11, 2007 12:06 am

Vich wrote:
wintermute wrote:
Vich wrote:
wintermute wrote:
assert's job is to display an error message.

If you need to set a video mode for that to happen then it kind of becomes assert's job really.

If it's job is to display something, why should it also initialize the system that allows to display things? Displaying and initializing it are two separate things. Of course they are related, but it's not assert's job to initialize some system.


This is an embedded system with no OS, if you want a display you need to initialise it. Sometimes you just need to accept that there are situations where you can't separate things as much as you'd like.


Of course I have to do it, but I'd rather do it in the Init of my platform library when possible, because that's where it belongs. (and I now know that this is possible, because I can call consoleInit before any other 2D/3D graphics-related init calls)
So I can perfectly separate it.


Go ahead and separate it, but then put the call to InitMyConsoleGFX() inside the Assert() function. Problem solved.

#114832 - Vich - Thu Jan 11, 2007 12:15 am

josath wrote:
Go ahead and separate it, but then put the call to InitMyConsoleGFX() inside the Assert() function. Problem solved.

I fixed it by initting the console before any assert was called :)
(initting the console couldn't be done before setting the video mode, I tried)
_________________
[project website] [personal website]