libnds应用研究(一)——文字



掌叔
2009-02-13 09:14:04

摘自:[url]http://blog.sina.com.cn/niubods[/url]
作者:niubo

无论C语言还是LavaX,首先要解决的就是文字输出的问题,最典型的就是“Hello World”了。事实上我们学了这么久C语言程序设计,绝大多数都是这种“控制台程序”。
DS有两个屏,用上屏还是下屏显示文字就成了问题。这里不打算说太多,简单提一下DS有两个图像引擎,主引擎和副引擎,和上下屏没有确定的对应关系,可以互换。两个引擎的主要差别就是主引擎比副引擎多两个图像模式。一般示例程序习惯用上屏显示图像,下屏显示文字。所以这里也以下屏为例看一下如何输出文字。
方法1:
这是一个最省事最直接的方法,代码如下:

#include
#include
int main(void)
{
//初始化中断和启动垂直中断
irqInit();
irqEnable(IRQ_VBLANK);
//初始化控制台
consoleDemoInit();

printf("Hello World!
");
//无限循环
while (1)
{
swiWaitForVBlank();
}

return 0;
}
这应该是比较简洁的“Hello World”程序,除了在下屏显示一行文字之外没有其他东西。
初始化中断部分就不多说了,只说初始化控制台这一句:consoleDemoInit();
它以一个默认设定(下屏,MODE_0_2D模式VRAM_C和0号背景层)初始化控制台。通常情况下我们没有什么其他要求,只是想显示文字的话用这个就行了。

方法2:
可以用另一个复杂点的函数初始化控制台,还可以用printf函数的“扩展版”iprintf在不同的位置显示文字。在上个示例代码中稍作修改就可以了。
初始化中断后,把consoleDemoInit();替换成下面的代码:

videoSetMode(0); //不使用主屏幕
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE); //副屏的0号背景层用来显示文字
vramSetBankC(VRAM_C_SUB_BG);//把显存映射到一块内存空间

SUB_BG0_CR = BG_MAP_BASE(31);//设定对背景0的控制寄存器赋值

BG_PALETTE_SUB[255] = RGB15(31,31,31); //默认状态下文字会以调色板中的255号颜色进行填充

//consoleInit() 更加灵活,但是这个已经够你用了,而且运行也很快。
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);

这一段看上去很复杂,那么就来逐句分析一下:
1.把显示模式设成0就是不适用该屏幕。
2.把副屏显示模式设成MODE_0_2D,开启0号背景层来显示文字。
3.我的理解是把显存的C区用于副屏背景的显示。这里值得注意的是从显存分为从A到I共9个区,其中常用的为A到D区,而能用于副屏背景的只有C区。显存的分区、大小和应用范围可以参考以下资料:[url]http://www.dev-scene.com/NDS/DOCgraphicmodes#VRAM[/url]
4.对背景0的控制寄存器进行赋值,使背景0映射到显示缓存的31号块。这里有必要多说一些,只有显存控制器才能管理显存,我们只能对内存进行操作,所以要把显存映射到内存才能间接控制显存。我的理解是被映射的这部分内存就叫做显示缓存。
5.BG_PALETTE_SUB顾名思义就是副屏背景的调色板,字体的颜色默认就是第255号颜色,这里把它用RGB15这个宏设成白色(R,G,B颜色值的范围都是0~31)
6.这个初始化函数的原型是 void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth);
参数map是要绘制文字的缓存地址,这个应该和在寄存器里设置的缓存位置相同。
参数charBase是要读取字体的显存地址,原样抄过来用就行了。
参数bitDepth是颜色深度,这里是16bit。
注释中提到consoleInit()这个函数,参数更多,使用更灵活,可以自定义字体……不过只能显示英文字母的话对自定义字体暂时没兴趣,就不多说了,感兴趣可以去看libndsinclude
dsarm9console.h这个文件里有详细说明。
接下来是演示的iprintf()这个函数的几种用法:

//转义符。清屏,然后重置光标
//x1b[2J
iprintf("x1b[2J");

// 转义符。用来设置打印位置
// /x1b[line;columnH
iprintf("x1b[10;10HHello World!");

// 转义符。向上移动光标
// /x1b[linesA
iprintf("x1b[10ALine 0");

// a转义符。向左移动光标
// /x1b[columnsD
iprintf("x1b[28DColumn 0");

// 转义符。向下移动光标
// /x1b[linesB
iprintf("x1b[19BLine 19");

// 转义符。向右移动光标
// /x1b[columnsC
iprintf("x1b[5CColumn 20");

这段程序摘自示例程序中的ansi_console, 注释很清楚就不多说了。想在上屏显示也很简单思路有两个:交换主副引擎和上下屏的对应关系,或者直接把初始化这一套改到主引擎上。纯粹研究一下,实际使用中用处应该不大。