Reciting the Changelog

The problem with development blogs that try to keep up to date is that a lot of the posts tend to be nothing more than the changelog with comments. That’s the problem with this blog, anyway, so I apologise for regurgitating the changelog. It’s useful for me, though, plus it lets me add emphasis to changes that will break all of your code, such as the latest font changes.

Back to the latest SVN changes. The Font class now accepts const u16* bitmap data instead of plain old u16*, so the const-ness of PAGfx data no longer has to be cast away.

The SuperBitmap class has two new functions - “newBitmapGraphicsPort()” creates a GraphicsPort object that can write to the SuperBitmap’s buffer. Might be handy for something, but I’m not going to delete the existing SuperBitmap drawing routines as they’ll be much faster (they don’t run through the clipping routines). It’s got a “drawBitmap()” function too, which will blit a bitmap (or portion thereof) to the buffer.

At the moment, the SuperBitmap can either be passed an existing bitmap in its constructor to use as its buffer, or it can create one itself. This is mainly to enable the use of PAGfx bitmaps as images. However, the big problem with this is that the SuperBitmap can’t write to the PAGfx bitmaps - they are arrays of constants. The drawing functions don’t check to see if they’re trying to draw over consts, which will probably cause unpredictable things to happen.

There are two solutions to this - either make the drawing functions check if the bitmap is internal (u16) or external (const u16) before trying to draw anything. The other alternative, now that the drawBitmap() function is done, is to strip out the whole external bitmap concept and make the SuperBitmap always create a new image. Uses more memory (96K for a full-screen bitmap), but makes more sense.


And while I think about it, I should have renamed the “Gradient” gadget to “CopperList”. Heh.

Also, I can update the TextWriter class to output in a specific colour by doing something like this:

u16 oldColour = Font->getColour();

And another idea. I already scan each glyph in a font when it is constructed to see whether or not that glyph contains any non-transparent pixels. It would be trivial to extend that function so that it spots empty columns in a glyph, and stores the number of populated columns. If I know the number of populated columns in a glyph, I can have non-fixed width fonts. I could even scrap the current method of storing the populated/unpopulated information, and rely solely on the width - if the width is 0, I can just ignore that glyph (and assume its width is the nominal width of the font for text size calculations).


Jeff on 2007-12-09 at 05:27 said:

You could avoid your problem with SuperBitmap and const bitmaps by having two classes. ReadonlySuperBitmap, which has a constructor that takes a ‘const bitmap’ and SuperBitmap, which rejects a ‘const bitmap

Define your graphic methods in the appropriate classes and it should all be fine. Of course, why you need a SuperBitmap thats constant, I’m not too sure - I guess its useful to be able to blit out of a bitmap.

Jeff on 2007-12-09 at 05:27 said:

Win32 defines a number of its ‘set’ API’s to return the old value. For example, SelectPen() sets a new pen in the port, and returns the old pen. This sort of thing makes your code a little more symmetric, which can help in getting it right first time. ie, your snippet above would be:

u16 saveColour = font->setColour(newColour); drawText(text); font->setColour(saveColour);

Some people would even put that into a {} block so that saveColour went out of scope immediately, thus ensuring it wasn’t used elsewhere in your code.

It can get a bit tricky when doing it with things like setFont() since you need to balance your delete operations correctly - you can’t leak u16 but you can easily leak Font* and its anyones guess as to how char* would pass in/out (ie, as the original or a copy, or a const copy, etc.

Steven on 2007-12-09 at 14:21 said:

Hi, I’ve been reading this blog for a while (well since I stumbled upon your post on gbadev.org’s forums, and sugested that you drop the C++ approach) and I’ve started to code my own GUI in C (based on the System 6.0 GUI style). When it comes to fonts I found that implementing non-fixed width fonts is easy, and as you sugested storing the width of the font as a variable in the system is the right way to do it. I think I sent you the code I’m using for ByteMap Fonts which I’m using on my GUI and that system allows me to handle multiple fonts of different heights very easy.

A lot of what Jeff has been sugesting is very good (and some of it’s been noted down for when I get to the point with my GUI to implement it). Shame I don’t use STL, PALib or any C++ features :D but that’s just me I like to have a clean no nonsense base library that I can overload easily.

Oh just about to post and remembered something else about fonts - they are not all *x8 in size, if you store width, height, x and y offset you could reduce the abount your coping to the screen at the expense of an extra 4 or 8 bytes for this information, this way if the width is 0 skip that character, any other size use the width and height as well as the offsets to reduce the amount of non plotted pixels that your reading. This might not sound a lot of savings, but it will show in your text rendering code, espially if you were to use a 3x5 font…

I’d post a link to a screen shot of my current GUI’s progress but that would be wrong in my opinion as this is about your GUI (which is very good and looks a lot like the Amiga WB from the days of good quality games).

ant on 2007-12-09 at 15:40 said:

Hey, welcome to the blog! Yep, still got the BMFont code. I’ll get around to integrating it at some point, probably around the same time as I look at the non-fixed width font stuff. I think Amiga bitmap fonts and BMFonts took more or less the same approach (I suppose there aren’t really many efficient ways of storing font data), so it might be possible to support both with little extra effort (mmm, object orientation at its best!).

The thing that really put me off trying to create Woopsi in C (apart from the fact that I’ve been an OO coder for years now, and can get things done faster and cleaner that way) was the Cybiko’s GUI. It’s a classic example of a UI done incredibly badly - despite having (at one time) a fairly vibrant homebrew scene, I don’t think anyone ever managed to used the GUI API. It’s a godawful mess of badly-written C designed to emulate C++ data structures. I think I’d probably have written something like that if I’d tried it in C. Unless you’re a great C coder, I imagine that something like a GUI system is very easy to get wrong.

I’d actually love to have a look at your GUI - do you have any screenshots or a demo?

Steven on 2007-12-09 at 18:43 said:

Yeah I’ve got a screen shot, I think the current version of the GUI is about the same as Woopsie v0.0001 Pre-Alpha - or the stage wher you put squares on the screen and it looked sort of right :D

Anyway the screen shot is at - http://sharriso.gotadsl.co.uk/images/NDowS_Mock_1.jpg - It’s currently stored on my personal web server that is prone to disapear every now and then due to power cuts thanks to some dodgy power grid issues around my house :( makes coding fun during wet weather (thank god I got a laptop for coding and watching films while at work :)). I’m in the process of documenting everything that’s in my head on how the system works, and I’ve not looked at the Cybiko’s GUI code so if it ends up like that then I will seriously wonder where I went wrong. One thing I’ve done in my GUI is set up the system so that the user can easily add their own features from day 1, or if they want to they can totally change the GUI layout just by changing the callback procedures that’s used for the various bits on the screen - so the system SHOULD be able to get over ridden quite easily to allow a developer to change the way the GUI is drawn (so instead of the System 6.0 interface they could have a GEOS or a totally custom interface) .

ant on 2007-12-09 at 18:56 said:

That’s looking pretty good already!

Jeff on 2007-12-09 at 22:35 said:

Whilst I applaud the desire to avoid the dependency on PALib (I have this horrible feeling that it has pathological dependencies on specific versions of libnds), and quite frankly, I think the STL sucks big time, its important to not get distracted by the technical challenge of ‘framework’ vs ‘application’.

(For what its worth, I suspect that Woopsi needs about 2% of what PALib does - but by building on top of PALib, he makes it possible for other PALib functions to be used (sound, etc) by other developers)

I came across Woopsi for exactly the same reasons; I wanted to write an app for my wife, but got distracted onto writing a simple windowing system, primarily because PALib still seemed way to hard to understand, with its magic “screen modes”, and odd-ball “manual video memory management” (no offense to PALib, as time goes by, I understand more and more why its so hard, and how good a job they have actually done). Then I came across Woopsi and realised that someone else had already taken on the hardest task. The fact that Woopsi handles clipping for you is the killer blow as far as I’m concerned - reimplementing that would be way more effort than its worth.

The kicker with Woopsi was that it has solved the major tricky bits for me already, and its a very clean architecture to sit on. Despite the fact that ant keeps changing things under my feet, I haven’t yet seen a change that invalidated more than about 20 minutes worth of work.

Given ant’s recent changes to WindowBase, I wouldn’t mind betting that you could reimplement your window look&feel on top of his with minimal effort.

As to the whole “I hate C++, I’d rather do it with my own structures”, I won’t get into that argument. If the problem looks suitable for solving with ‘inheritance-oid’ data structures, its foolish not to use a language that directly addresses them. In a past life, I coded up OO designs in Honeywell Assembler and whilst it worked just fine, I’d have killed for a compiler that did the boring stuff (like vtables) for me.

Remember, you reach the sky by standing on the shoulders of giants, rather than spending your time building yet another ladder.

Steven on 2007-12-10 at 01:10 said:

I never said I hated C++, I’m just giving myself a challenge :D a little like a mis-quoted IRC chat I had a few weeks back when I said I was looking at OS Dev stuff and someone thought I was writing a full OS for the DS!!!

I’m actually looking at Woopsie as “Something I want my system to be when it grows up”, but at the moment I think the genetic code is not bonding together (for example I’ve just found a bug that I can’t find! If I re-draw the screen, the screen object thinks it’s a window and attempts to draw a close box…)

One thing I will more than likely do is what MS did for Windows - they made a class library to help coders that use C++ do windows coding faster. I used to code in MFC and found it caused more trouble than it’s worth for most of the projects I was doing at the time, now I’m a Win32 coder that also uses C++ for the application code - a nice hybrid approach as I can control the GUI connections faster in Win32 than I could in MFC. I also found coding in MFC made me lazy and consintrait more on the WOW factor, than the actualy funtionality - but hey that’s just me :D

Jeff on 2007-12-10 at 01:35 said:

If your idea of fun is searching for bugs in your code that others have already fixed in theirs then by all means keep on keeping on. I know I’ve done exactly the same in the past; the impetus to use Woopsi has been my wife saying “you know, you should try to finish something for a change”.

As to hating C++, I wasn’t trying to imply you said that, only that thats the nub of most arguments in that space. I really don’t care too much either way what other people do, except when they waste my time telling me how much better their way is.

The biggest issue I’ve had with non-C++ OO frameworks has been that you still need to buy into all the other assumptions that person has made about how to do pseudo-inheritance and memory-management, and there’s nearly always a dealbreaker. From ‘you have to use my string class, no really, its way better than yours’ to ‘I can’t do multiple inheritance, but why would you want that anyway?’

As to using MFC, in my day job I’m hamstrung by the fact that some other apps I am forced to deal with still require MFC, but Microsoft were not capable of keeping their class library compilable across the various incarnations of the C++ compilers, and so I need to have sources that #ifdef based on compiler versions, with tricky makefiles to link the appropriate libraries, and then there’s the ludicrous nonsense that is Vista assemblies. Not to mention incompatibility with the other frameworks that Microsoft pushes (ATL,etc) to the point where you can’t build a DLL with MFC7 that can also build with MFC8 because of mandatory static objects.

And when push comes to shove, MFC still doesn’t protect you from needing to know about the underlying Win32isms.

Oops, ranting again.

Now, if you wanted a real challenge, write a real C++ wrapper over PALib that exposed its backgrounds, sprites, etc in a rational OO fashion; even if all it did was check that “you’re in mode 5, you can’t write to background 2 any more”, etc, it’d be a win.

Jeff on 2007-12-11 at 00:29 said:

With regard to when I said this:

“Whilst I applaud the desire to avoid the dependency on PALib (I have this horrible feeling that it has pathological dependencies on specific versions of libnds)…”

do you have any idea why this:


currently says:

“Woopsi 0.26 - A BSD-licenced windowing system loosely based on the Commodore Amiga’s “Intuition” windowing system. (Sadly requires PALib)”

notable the ‘sadly’ part? Whilst I’m not crazy about PALib, its mostly an unfamiliarity rather than an rational dislike. I can see where it might be a technical challenge to have an #ifdef that lets Woopsi sit on top of raw libnds instead of PALib, since there really isn’t that much you use PALib for other than screen setup and its vbl loop. But fundamentally you’d just be inlining PALib stuff and removing options (since you have one hard-wired graphics mode, etc)

ant on 2007-12-11 at 20:49 said:

PALib has its problems. Like you said, it has dependencies on particular versions of libnds (currently r20; r21 doesn’t work). It has problems with Vista, and new releases are sporadic.

However, I think the real reason for this is the same reason people object to C++ over C - the same kind of snobbery you encounter if you tell a VB programmer that you code VBScript (“Oh no! A scripting language!“), tell a C programmer that you code VB (“Oh no! BASIC!”), or tell an assembly programmer you’re a C coder (“Oh no! A high-level language!“). PALib is libnds-lite. Just one calorie; not low-level enough.

Personally, I’ll happily program in about two dozen languages, from high-level scripting languages down to assembly, so I just prefer to pick the language or toolkit that fits the task.

ant on 2007-12-11 at 23:17 said:

Thinking about it some more, you could argue that PALib limits your options - you can’t create a custom ARM7 binary without a lot of fiddling, it’s more difficult to integrate other libraries, etc.

On the other hand, how many DS homebrew projects are there that need a custom ARM7 binary? And how many projects are there that just need a decent GUI?

Jeff on 2007-12-12 at 00:40 said:

Its a reasonable question to ask. As it stands, I would like to be able to have rebootlib available, which requires the custom arm7. Sadly, the default libnds one doesn’t cut it either so its still a problem.

I just took a quick look at the woopsi sources again, and I’m of the opinion that you could very quickly minimise your dependence on PAlib, though you’ll need to adjust your coding style.

For example, pretty much every woopsi header file includes when all it really need is (this gets you the s16, etc) and you could get away with putting this in gadget.h, font.h and debug.h and everyone else would get it for free. You should also go through and remove all redundant #includes - just use class XXX when you need a forward reference. This will speed up compiles quite a bit, and remove pathological interdependencies, AND ensure that you include headers in the right source files deliberately, rather than by accident.

As it stands, you need in debug.cpp, gadget.cpp, graphicsport.cpp, screen.cpp, superbitmap.cpp, textviewer.cpp textwriter.cpp and woopsi.cpp - all other cpp files are fine without it.

When you look at, say, gadget.cpp, it needs two PA constructs, the macro PA_RGB() which is trivial to reproduce, and PA_DrawBg[] which is the array of two screen base pointers. These are pretty much hard-wired constants, given Woopsi’s insistance on PA_Init calls

graphicsport.cpp wants the DMA_Copy() and DMA_Force() macros - if you actually looked at what those things expanded to, you’d find you could be even more efficient. There’s no magic in them, they just copy some values into IO Port registers.

screen.cpp wants DMA stuff and PA_DrawBg[] superbitmap.cpp, ditto textviewer.cpp ditto textwriter.cpp PA_DrawBg[] and NULL, which indicates that its missing

woopsi.cpp is the only interesting one in that it initialises the graphics modes, and relies on the PA vbl to keep the Stylus structure up to date. Again, there is no rocket science in that particular function, it relies on libnds to do the hard work, and just repackages the results into its own structure, which is what Woopsi then does as well.

I would be tempted to redirect all the PA references in the other source files to static inline methods in Woopsi - ie, leave Woopsi dependent on PALib but change everything else to be independent. Once thats done, it should be relatively straight-forward to have a #ifdef that changes Woopsi to do its own thing, or use PALib.

I would not lose the ability to be built on top of PALib because it does provide some value in its ability to play sounds, etc. But for those people who don’t want that, perhaps a libnds-only solution is attractive.

Jeff on 2007-12-12 at 00:41 said:

Please check the spam filter

Jeff on 2007-12-12 at 11:52 said:

Oh, bugger, html was swallowed <PA9.h> and <nds.h>

ant on 2007-12-12 at 13:26 said:

I’ve considered stripping PALib and making Woopsi standalone - I think there’s a post somewhere about it. It’s something I’d probably do once the whole thing is finished; I’d rather concentrate on the functionality side now, then start polishing it up once I’ve got a stable codebase to work with.

Jeff on 2007-12-12 at 19:59 said:

Well, in theory I agree - as I say, I’d push all the PAisms out of as much code as possible, so that when the day comes its not a daunting task.

Even now, the number of source files seems to be doubling every week which is a good thing (because it means more functionality) but may also be a curse (because cut/paste means you propagate unnecessary stuff forward which you later need to clean up.

Jeff on 2007-12-12 at 20:06 said:

I mean, while looking through the PA sources, I found the following:

PA_Stuff.c: u32 Blank[130000>>2];

PA_Inits.c: void PA_Init(void) { s32 i; for (i = 0; i <130000>>2; i++) Blank[i] = 0;

Thats a 260,000 byte array of zeroes thats taken out of your available memory. 260K!!! And what is it used for? So that it can do things like this:

PA_ResetSpriteSys() DMA_Copy((void)Blank, (void)used_mem, 256, DMA_32NOW);

void PA_Default16bitInit(u8 screen, u8 bg_priority){ DMA_Copy(Blank, (void*)PA_DrawBg[screen], 256*192, DMA_16NOW);

etc. Rather than use the DMA_Force() function to blit the same zero over an array, they have a carefully prepared array of zeroes.

Thats an insane waste.

Steven on 2007-12-12 at 21:56 said:

Jeff that’s not a 260K of memory, it’s only 130K (130000 >> 2 = 32500 Double words (s32’s))..

And that’s WAY over a single screen size of 49152 words or 98304 bytes…

Why does PAlib need a 130K block of 0’s???

I actually agree with Jeff, remove the dependance on PAlib sooner rather than later, you will not regret it…

Jeff on 2007-12-12 at 23:50 said:

Ok, I read >>2 as / 2

I really wish people wouldn’t waste their time doing that sort of nonsense - gcc is perfectly capable of recognising integer divisions and turning it into shifts if possible.

Steven on 2007-12-13 at 05:26 said:

Even so 130000 / 2 still does not equal 260K

Neer mind I worked out how you got to that value :D

God it’s too damm early for me :( stupid work.

Steven on 2007-12-13 at 15:21 said:

Gah, I’ve just started the de-PAing, and already I’ve hit a lovely wall…

What the hell was the PALib Developers on when they did the PA_vblFunc() function????

It starts off fine, ish…

it flushes everything, then updates the pad, stylus, moves the prites, updates the onscreen objects, and updates the DS motion info. Next comes the Real Time Clock update, as well as something to do with the mic (does Woopsie use any of these features???).

Ok that’s ok… then it gets strange…

set PA_NewFrame = 1 - this is the code to indicate that we are on a new frame…..

PRE-increase PA_nVBLs and PA_TestVBLs… WHY 2 frame counters?????

then run a function to deal with counters.

OOOO finally call the CustomVBL function.

Oh and we nearly forgot, update the sprite animations, cos it’s not important….

Dear god this is not a helpful library, it’s a god damm nightmare for games… If your not using features they should not be getting updated…

I’ll probably rant a little more later… so far all I’ve done is replace the #include with #include and copy my code in from my library to setup the screens…

ant on 2007-12-13 at 15:58 said:

Woopsi needs the pad, stylus, framebuffer for both screens, VBL and the DMA hardware. Other than that, I don’t think it uses anything. No sprites, no sounds, no microphone, no clock.

Steven on 2007-12-13 at 16:07 said:

Your not using the VBL by the looks of it… or at least I can’t find the VBL irq handler in woopsie anywhere.

The only time VBL is encountered is the wait for VBL code in the main loop.

Sofar I’ve added 1 function, 2 if I replicate the VBL code of PALib (which monitors the lid, so I think it might be a good idea to do that) and that’s about it… everything else is done a little different in libNDS then PALib, but it’s minor changes….

ant on 2007-12-13 at 16:43 said:

Yeah, that’s the only time I need a VBL. I need the lid stuff, too - I forgot that.

Steven on 2007-12-13 at 16:58 said:

Ok, so to recap:

I re-created the PA Lib’s VBL handler (after looking at what it does I sort of thought about it for all of 3 nanoseconds and put in a woospieWaitVBL function).

I’ve created an initWoopsieGrfxMode() function that basically does everything PA lib did on it’s initialisation stage, apart from allocating 130k of ram, setting that ram to zero and then setting the screen to red or green depending on what screen it was (no kidding the comments are still in there from the test code that they had!!!).

I’m looking at the Pad and Stylus stuff now (why did PA Lib seperate those two bits?) and also noticed that the Stylus has a DblClick option (you’ve got to be bloody fast for a DblClick < 45 scan lines). So I’ll get to that soon, and since the pad was using 3 structs that stored 12 bytes each I think I can get that down to 6 bytes with some nice anonymous unions…

After killing Visual Studio 2007 on my Laptop with the whole Find / Replace PA9.h with nds.h in all files in solution I think I’m going to have fun with the other files (debug is the next file that errors out…)

Steven on 2007-12-13 at 17:30 said:

Here’s the nifty little struct I’m adding:

typedef struct PAD { union { struct { u16 A:1; u16 B:1; u16 Select:1; u16 Start:1; u16 Right:1; u16 Left:1; u16 Up:1; u16 Down:1; u16 R:1; u16 L:1; u16 X:1; u16 Y:1; u16 Touch:1; u16 Lid:1; }; u16 AllKeys; }; } _pad;

(names and style will be changed to your style when I get everything working)

Basically you will have a variable to store the held status of the keys (all keys, including the stylus and lid) and it’s values are setup with 1 bit of code:

held.AllKeys = keysHeld();

Then you just check the key your interested in like so:

if( held.A ) // 1 = held, 0 = not held

Nice and clean :D

ant on 2007-12-13 at 18:38 said:

Looking good!

Steven on 2007-12-13 at 18:44 said:

I’m now bald btw….

ant on 2007-12-13 at 18:53 said:

Hahaha! :D

Steven on 2007-12-13 at 19:13 said:

Just been speaking to WinterMute on IRC and he posted this link:


Freetype on the DS, oh and he had a bloody good laugh about the PA stuff Jeff found :D..

Jeff on 2007-12-13 at 20:27 said:

Hmmm, I hope we don’t get into a slagging match over PALib.

Its quite clearly the result of incremental development, getting stuff working then not messing with it and going forward to the next feature, rather than going back and cleaning up. Which is typical of something thats being done for the love of it. Actually, its also typical of something done for cash - you don’t get paid after the first delivery so there’s little incentive to go back and tidy, just hack around the problems and hope they don’t bite too hard.

I think the real pinch will come in working out how to build an extensible mechanism for connecting functionality in the arm7 to the arm9, which is essentially a ‘development methodology’ problem. You can see that there is “custom support for sound transfer” in which is deprecated and may go away, libwifi has its own magic vector that people have to set to wire it into themselves, rather than it being able to wire itself in, etc.

The key, however, is that it needs to be OPTIONAL. Its no use whatsoever if HelloWorld.nds is 4MB.

Steven on 2007-12-13 at 21:15 said:

No slagging match, but if someone new to coding was to go though the PA LIb source and take what they saw as gospel, then that could be a bad thing. Also the people on he IRC channel #dsdev on blitzed irc sort of hate PAlib with a passion…

Jeff on 2007-12-13 at 23:50 said:

Well, I don’t want to defend it too strongly, because I agree, the sources are currently a pigs ear rather than a silk purse. You cannot possibly learn from them.

On the other hand, most of the other libnds-based source that I’ve found around the traps has been no better. Everyone seems to think that you need to work out “the hard way” about screen modes, and the implications on the number of types of backgrounds they allow, etc. And the whole ‘arm7/arm9’ IPC mechanism is positively opaque.

Jeff on 2007-12-13 at 23:55 said:


I mean, just recently, I saw the following slab of code go past.

  • videoSetMode( MODE_5_2D | DISPLAY_BG3_ACTIVE );
  • videoSetModeSub( MODE_5_2D | DISPLAY_BG3_ACTIVE ); //sub bg 0 will be used to print text +
  • vramSetBankA( VRAM_A_MAIN_BG );
  • vramSetBankC( VRAM_C_SUB_BG ); +
  • BG3_CR = BG_BMP16_256x256 | BG_BMP_BASE(0); +
  • BG3_XDY = 0;
  • BG3_XDX = 1 << 8;
  • BG3_YDX = 0;
  • BG3_YDY = 1 << 8; +
  • SUB_BG3_CR = BG_BMP16_256x256 | BG_BMP_BASE(0); +
  • SUB_BG3_XDY = 0;
  • SUB_BG3_XDX = 1 << 8;
  • SUB_BG3_YDX = 0;
  • SUB_BG3_YDY = 1 << 8; +
  • DrawBg[1] = (u16*)BG_BMP_RAM(0);
  • DrawBg[0] = (u16*)BG_BMP_RAM_SUB(0);

and I thought to myself “what the hell does that do”


ant on 2007-12-14 at 00:01 said:

Heh, I learnt everything I know about DS development from PALib.

Steven on 2007-12-14 at 00:07 said:

OK so I can’t document code for beans…

The code was knocked together in a couple of minutes to replace the PA code…

Jeff on 2007-12-14 at 00:29 said:

No offense intended. Just an observation that we all do it, write code that looks fine to us, but is actually inpenetrable to others reading it. Its a function of what you “know to be self-evident”.

Jeff on 2007-12-14 at 00:31 said:

Is that comment on the videoSetModeSub() line correct, anyway? Its actually setting up the top-screen to be exactly the same as the top screen, isn’t it?

(I hate the fact that the “top” screen is the “sub” screen, in nds speak)

Jeff on 2007-12-14 at 00:32 said:

And by top, of course I mean “bottom”. The second top, not the first or the third.

Yah, go back to work Jeff…

Steven on 2007-12-14 at 00:41 said:

No I think I got that originally from one of the examples, and forgot to change the comment.