Thinking in Silicon 通向伟大之路

hiworld

My Links

Blog Stats

News

档案

随笔分类

相册分类

Community

Download

Learning

News

Referrence

Regional Site


2006年12月22日 #

欢迎广大无聊的大学生来调戏之

http://www.etone.edu.cn

破解有奖

posted @ 15:20 | Feedback (4)

2006年8月25日 #

经典解说fiba

过他,好的,3分!3分!3分!王仕鹏立功了,王仕鹏立功了!不要给斯洛文尼亚队任何的机会。
伟大的中国队后卫!他继承了中国队的光荣传统。胡卫东、孙军在这一刻灵魂附体,王仕鹏他一个人代表了中国篮球悠久的历史和传统,在这一刻他不是一个人在战斗,他不是一个人.....!
王仕鹏,面对这个3分。他面对的是全世界中国球迷的目光和期待。
斯落文尼亚曾经在世界杯预选赛的附加赛中多次防住绝杀球,王仕鹏应该深知这一点,他还能够微笑着面对他面前的这个人吗?10秒钟以后他会是怎样的表情?
球进了!比赛结束了!中国队获得了胜利,淘汰了斯落文尼亚队。他们没有再一次倒在欧洲的球队面前,伟大的中国队后卫!中国万岁!
伟大的中国,中国人的期望,这个3分是一个绝对理论上的绝杀。绝对的空心,中国队进入了16强!
这个胜利属于中国,属于王仕鹏,属于YAO,属于王ZZ,属于刘炜,属于朱芳雨,属于所有热爱中国篮球的人!
斯落文尼亚队也许会后悔的,内斯他在下半场他打得太保守、太沉稳了,他失去了自己的勇气,面对中国篮球悠久的历史和传统,他没有再拿出小组赛中那样猛扑猛打的作风,他终于自食其果。他们该回家了,他们不用回遥远的斯洛文尼亚,他们大多数都在美国生活,再见!

posted @ 13:07 | Feedback (2)

2006年8月23日 #

瞎扯FIBA

本来早就想说,但是blog似乎除了点问题。(anan, 你要负责。。。)
在沃尔马和民工兄弟众,(不排除中间有个别伪白领)一起席地而坐,看中国队vs美国队
球赛看完了,结果也比较符合我的预期,现摘录现场后面几个伪白领的高见如下:
 
中国队落后30多分,有一阵突然中国队三分球人品爆发,那几个伪白领于是开始高呼:“靠,早这样打早就赢了”...
有了那一阵的三分球爆发,后面只要中国队外线一拿球,他们也不管美国队员早就贴了上来,高呼:“sb,还不出手,投三分阿”
 
后卫每次运球到前场,开始执行教练的战术,在外线进行进攻准备,他们高呼:“这个sb怎么每次都运那么久球阿”,
是的,中国的控卫的确不是很优秀,失误也多,但是你也不能看到人家一个人运球过半场就骂人家运球多阿,难道每次
都要10秒内出手,打游戏机阿?
 
有一次比较搞笑:控卫运球到外线被包夹,后面有位球迷高呼“sb,不会运球就把球给控卫阿”,我晕...
 
最受不了的是这些人完全不看场上形势,中国队一拿球就高呼什么sb,还不投。。靠,你试试...
 
居然看到Yao从中线运球上篮,忍不住笑了出来...

posted @ 12:59 | Feedback (4)

2006年7月25日 #

你的最佳才能到底是什么?

各位,我建议你们自己问自己这个问题。
不要很快的回答,我的最佳才能当然是什么什么,没错,你现在也许手头正在做或者你现在很擅长做某件事情,但是,他是不是真正的你的最佳才能呢?
在你的最佳才能区奋斗,事半功倍,反之亦然。当然,我们有时候也要考虑兴趣什么的。不过问自己这个问题,还是有好处。
没事的时候,想想罢,总比50岁的时候再来考虑有益,对吧?

posted @ 18:31 | Feedback (6)

2006年7月21日 #

转载-现代京剧《齐祖恨》

[第一场]动员

齐达内(点绛唇牌)中场无人比,盘带美如奇。

(齐达内并法国队众同上)

齐达内(唱)齐某头顶虽无毛,进攻组织非某不行。

法国队众人(同白)正是啊。

齐达内(念)正青春歇顶头似闪电,廿四载征战脚有机关,身在意国心不在,效力卫国夺金盏。

(白)某,齐达内,法兰西马赛人氏,自幼练得金顶一个、钢腿一双,助攻、进球易如反掌,可叹二十四载,我绿茵场上异国征战,到如今意甲猫腻,叫我好不心寒,纵有十三个冠军在身,罢罢罢,功成名就,正是退隐之时。只缘世界大战重燃战火,我便整顿人马再杀一程。

法国队众人(同白)遵命。

齐达内(白)我等一拦巴西。

法国队众人(同白)法拦西。

齐达内(白)又退二牙。

法国队众人(同白)前杀西班牙,后吃葡萄还吐牙。

齐达内(白)夺取金杯只待此战,众将听令。

(法国队众人跪倒)

齐达内(唱)今日将会亚平宁,意大利佬儿不足惧,莫论健翔呼万岁,我等上前主正义,进攻有亨利,断后看萨尼,老巢有我巴特斯,里贝、马卢与我同捣敌营,有球进帐必封赏,若进乌龙身首分离,莫怪齐某心似铁,此恨绵绵无绝期。

法国队众人(同白)得令。

(众人同下)

[第二场]开战

(急急风牌,齐达内、马特拉奇、裁判并两队众球员同上)

齐达内(唱)裁判大人一声哨,双队人马腿似弓,郭靖挽弓射大雕,法兰西挽弓射大力神。

(马卢达切入禁区,与马特拉齐抬起的左腿接触后倒地)

忽见马卢被人绊,倒地禁区实在惨,齐某远处用目看,谁家小儿在此孽顽。

马特拉奇(唱)见得敌兵入禁区,管它三七与二一,防住一时立功勋,若道犯规不认承。

裁判(唱)马特拉奇犯规甚,不判点球人将我审。

(裁判指向点球点)

马特拉奇(白)呀,可真是点球?

裁判(白)真是。

马特拉奇(白)可确是点球?

裁判(白)确是。

马特拉奇(白)不改?

裁判(白)不改。

马特拉奇(念)亏呀!

齐达内(念)蓝衣小儿原是马特,绊我大将还生狡辩,看我齐某冲上前阵,点球必进机会难得。

(裁判哨响,齐达内点球罚进)

齐达内(唱)皮球入帐不由我心花怒放,廿四载到今日收关绿茵场,再夺金杯保齐某晚节,从此归隐作采菊陶郎。

马特拉奇(唱)齐秃点球应声入,怒发冲冠我凭栏处,此时落后都怨咱。心想那齐秃我国训栽,到今日他旧情不念,如食狼心狗胆。想他远祖本居阿尔及利亚,如今道貌岸然妄称法藩,看我如何将他灭,冲入敌营我要与你了断。

(马特拉奇头球攻门,球进了)

马特拉奇(大笑,白)齐秃小儿,与我加时再战一程。

齐达内(白)休得无理,齐某加时杀你鼠辈,速唤你家人收尸莫待,哼。

(裁判哨响,齐达内、马特拉奇并两队众球员同下)

[第三场]加时

(急急风牌,齐达内、马特拉奇、裁判并两队众球员同上)

裁判(唱)四年一度世界杯,越是决战越揪心,一个时辰转眼过,一比一平还看加时。

(裁判哨响)

裁判(白)加时开战。

(两队人马开战)

马特拉奇(唱)加时战正酣,我队损失惨,加图索鼻翼遭马卢暗算,血流成河映红杜鹃山。最恨齐秃不自知,门前三丈攻我门,头顶一甩我心一紧,好在布冯眼如猎鹰手似钢铁。眼下颠倒乾坤换边再战,我定要将那齐秃羞辱几番。

(加时下半场)

齐达内(念)马特小儿盯某甚紧,前后左右腿脚难伸,更有贼手将衣扯,不干不净球怎踢?

(白)马氏可将贼手收去?

马特拉奇(白)为何?

齐达内(唱)若要球衣场下讲,送与你作镇宅的幡。

马特拉奇(唱)小小齐秃不自知,你的球衣送葬的幡。

齐达内(唱)马特贼子口吐狂,言语辱骂我自不搭。

马特拉奇(唱)齐秃不搭有缘由,家母胞姐住青楼,血缘阿尔巴尼亚,妄称法籍实卖国。

齐达内(唱)听他言,不由得怒气发,几欲忍过这时辰,怎舍得白头娘亲养育恩,金杯且不论,头顶恶贼难解我恨。

齐达内(白)呔(齐达内头顶马特拉奇)

裁判(唱)那边战正酣,这边起波澜,马氏倒地栽,我看是谁坏。

法国队众(白)马贼骂人。

马特拉奇(白)齐秃顶我,好惨呐。

裁判(白)骂人我不知,顶他有事实,吃我一红牌,管你是否就此离开。

齐达内(念)双目见红牌,心头无限哀。

(白)罢!

齐达内(唱)金杯身边错过,某怎愿回身将它细瞅,叹一世英雄气盖世,到死竟遇小人迷惑,时不利兮骓不逝,追不逝兮可奈何,达内,达内,奈若何?

(齐达内下)

[第四场]尾声

齐达内(唱)在黄罗宝帐看转播,气煞了老将军齐某。

(齐达内上)

齐达内(唱)特雷泽盖将点球错过,马特拉奇却消遥快活,廿四载某雄心勃勃,恨一时冲动身死沙场,今日哭别绿茵场,明日返乡做他行,某乃法兰西好儿郎,防小人,莫轻狂,虽有遗憾,某美名也得传扬。

posted @ 13:01 | Feedback (3)

2006年6月29日 #

这年头,混血才是王道

亲上加亲过时鸟,混上加混才是解决矛盾的根本办法。
热烈祝贺我1/4的血统散布海外...还有3/4,继续努力...

posted @ 15:40 | Feedback (6)

2006年6月14日 #

DSL.参上

一个简单的编程示例
ARM7代码:
我们首先需要一个ARM7代码。ARM7是唯一一个可以控制触摸屏的CPU。 'libnds' 库的例子用一些“样板文件”('boilerplate')代码来实现一个“垂直空白中断”('Vertical Blank Interrupt')。这个中断可以为画屏幕产生一个规则基础。(保证各硬件功能顺序进行)

在一个中断处理中,“样板文件”( 'boilerplate' )代码获取触摸屏的参数值并储存以便于ARM9代码使用。记住只有ARM7可以访问触摸屏,所以需要读取(硬件设计如此)。但将它储存到一个内部ARM9可访问数据结构中就可以使得CPU使用这个数据了。

这是中断代码:
void InterruptHandler(void) {
  static int heartbeat = 0;
 
  if (REG_IF & IRQ_VBLANK) {
    uint16 but=0, x=0, y=0, xpx=0, ypx=0, z1=0, z2=0, batt=0, aux=0;
    int t1=0, t2=0;
    uint32 temp=0;
    uint8 ct[sizeof(IPC->curtime)];

   
    // Update the heartbeat
    heartbeat++;
 
    // Read the X/Y buttons and the /PENIRQ line
    but = REG_KEYXY;;
    if (!(but & 0x40)) {
      // Read the touch screen
      x = touchRead(TSC_MEASURE_X);
      y = touchRead(TSC_MEASURE_Y);
      xpx = ( ((SCREEN_WIDTH -60) * x) / TOUCH_WIDTH  ) - TOUCH_OFFSET_X;
      ypx = ( ((SCREEN_HEIGHT-60) * y) / TOUCH_HEIGHT ) - TOUCH_OFFSET_Y;
      z1 = touchRead(TSC_MEASURE_Z1);
      z2 = touchRead(TSC_MEASURE_Z2);
    }

    batt = touchRead(TSC_MEASURE_BATTERY);
    aux  = touchRead(TSC_MEASURE_AUX);

    // Read the time
    rtcGetTime((uint8 *)ct);
    BCDToInteger((uint8 *)&(ct[1]), 7);
 
    // Read the temperature
    temp = touchReadTemperature(&t1, &t2);
 
    // Update the IPC struct
    IPC->heartbeat = heartbeat;
    IPC->buttons   = but;
    IPC->touchX    = x;
    IPC->touchY    = y;
    IPC->touchXpx  = xpx;
    IPC->touchYpx  = ypx;
    IPC->touchZ1   = z1;
    IPC->touchZ2   = z2;
    IPC->battery   = batt;
    IPC->aux       = aux;

    for(u32 i=0; i<sizeof(ct); i++) {
      IPC->curtime[i] = ct[i];
    }

    IPC->temperature = temp;
    IPC->tdiode1 = t1;
    IPC->tdiode2 = t2;
  }
 
  // Acknowledge interrupts
  REG_IF = REG_IF;
}

ARM7代码中有一个类似于C编程的“MAIN”函数。它设置一个中断处理所以上面的代码会在“垂直空白中断”(Vertical Blank Interrupt )时被执行并进入一个无限循环等待它。

int main(int argc, char ** argv) {
  // Reset the clock if needed
  rtcReset();

  //enable sound
  SOUND_CR = SCHANNEL_ENABLE | SOUND_VOL(0x7F);
  IPC->soundData = 0;
 
  // Set up the interrupt handler
  REG_IME = 0;
  IRQ_HANDLER = &InterruptHandler;
  REG_IE = IRQ_VBLANK;
  REG_IF = ~0;
  DISP_SR = DISP_VBLANK_IRQ;
  REG_IME = 1;

  // Keep the ARM7 out of main RAM
  while (1) swiWaitForVBlank();
  return 0;
}


我把这段代码写入一个 'arm7_main.cpp' 文件。ARM7 G++编译器会将其编译成一个包含ARM7代码的 'arm7.o' 文件。G++需要很多的选项来编译ARM7代码。

arm-elf-g++ -g -Wall -O2 -mcpu=arm7tdmi -mtune=arm7tdmi \
   -fomit-frame-pointer -ffast-math -mthumb-interwork   \
   -Id:\devkitpro\ndslib\include -DARM7 -c arm7_main.cpp -oarm7_main.o

编译完之后我们便有了 'arm7_main.o' 文件,它需要用LIBNDS提供的DS ARM7标准库文件连接。这和用C标准库连接一个普通C程序一样。这步用 'objcopy'可产生一个DS ARM7可执行的二进制的'elf'格式文件:

arm-elf-g++ -g -mthumb-interwork -mno-fpu  \
  -specs=ds_arm7.specs arm7_main.o -Ld:\devkitpro\ndslib\lib -lnds7 -oarm7.elf

arm-elf-objcopy -O binary arm7.elf arm7.bin

ARM9代码:
在这个例子中ARM9代码是担任主要工作的。它会显示一个简单的文本消息和输出从触摸屏获得的X,Y坐标。记住是ARM7代码获取这个信息并储存在一个我们在这可以访问的数据结构中。
ARM9代码是一个单一的 'main' 函数。首先设置显示模式以让我们使用NDS的一个显示屏。在这种情况下是主屏。我们还需要设置显示文本、字体颜色等等:

powerON(POWER_ALL);
 
  // Use the main screen for output
  videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE);
  vramSetBankA(VRAM_A_MAIN_BG);
  BG0_CR = BG_MAP_BASE(31);

  // Set the colour of the font to White.
  BG_PALETTE[255] = RGB15(31,31,31);

  consoleInitDefault((u16*)SCREEN_BASE_BLOCK(31), (u16*)CHAR_BASE_BLOCK(0), 16);

文本在这里输出并进入一个显示触摸屏数据的循环。标准C函数 'printf' 用来输出文本到NDS的显示屏,当我显示触摸屏的X、Y坐标时我用ASCII码转换以避免光标的连续位置:

printf("\n\n\tHello World!\n");
  while(1) {
    touchPosition touchXY = touchReadXY();

    printf("\x1b[10;0H");
    printf("Touch x = %d   \n", touchXY.px);
    printf("Touch y = %d   \n", touchXY.py);   
  }

这段代码在'arm9_main.cpp' 中会被像ARM7一样被连接,只须改变一下连接程序的ARM9选项:

arm-elf-g++ -g -Wall -O2 -mcpu=arm9tdmi -mtune=arm9tdmi \
   -fomit-frame-pointer -ffast-math -mthumb-interwork   \
   -Id:\devkitpro\ndslib\include -DARM9 -c arm9_main.cpp -oarm9_main.o

arm-elf-g++ -g -mthumb-interwork -mno-fpu  \
  -specs=ds_arm9.specs arm9_main.o -Ld:\devkitpro\ndslib\lib -lnds9 -o arm9.elf

arm-elf-objcopy -O binary arm9.elf arm9.bin

创建DS可执行文件:

现在我们有了ARM7和ARM9的.bin 格式代码,我们现在需要创建一个包含这些文件,以及位图、图标、资源等的可执行“小文件系统”('mini-filesystem' )。使用的工具是'ndstool':


ndstool -c demo1.nds -9 arm9.bin -7 arm7.bin


以上的'ndstool'命令会创建一个'demo1.nds'文件,可以真正在硬件上运行。它包含一个ARM9 CPU 运行的代码和一个ARM7 CPU 运行的代码。

这一部分包括如何利用NDS的“帧缓冲”(framebuffer)模式在NDS的一个显示屏上绘图。NDS的每一个显示屏都可以被设置成很多种模式。每一种模式有其优缺点,但我们这里只使用“帧缓冲”模式来实现一个最简单的绘图功能。

“帧缓冲”(framebuffer):
“帧缓冲”是一种显示屏映射到一部分内存的模式。向内存中写数据会导致数据显示在显示屏上。在这个模式中,显示屏上的一个象素由2字节数据表示。这相当于C语言中的16位无符号整型数据类型(16 bit unsigned integer)。我们写入这部分内存的数据是以555格式显现的象素颜色。

所以我们不需要人为转换555格式,这有一个方便的宏'RGB15'让我们确定每一个象素的红、绿、蓝色的数量。每一个红、绿、蓝元素是一个从0-31的值。0表示无颜色,31表示最大色。(通过不同组合来显现不同颜色)。这是一个例子:

  RGB15          Color
RGB15(31,0,0)     Red
RGB15(0,31,0)    Green
RGB15(0,0,31)     Blue
RGB15(0,0,0)     Black
RGB15(31,31,31)  White

下面的代码片是示范如何通过给定一个指向“帧缓冲”区内存开始部分的指针来用蓝色填充屏幕的:

   uint16* framebuffer = ...;
  for(int i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; ++i)
    *framebuffer++ = RGB15(0,0,31);

向“帧缓冲”写的数据会立刻被画在相应象素上。“帧缓冲”模式的好处是你可以在屏幕任意地方画任何你想画的东西,它直接使用NDS的2D硬件加速。
“帧缓冲”的缺点是你必须自己做所有事。没有“精灵”('sprites')、贴图、卷动等等,除非你自己写代码实现。NDS的其他模式也许会适合做这些事,它们会在以后的教程中介绍。同时,“帧缓冲”模式有很多灵活性,并让我们更加接近硬件。

屏幕:
从硬件角度来看NDS上有两个屏幕。一个在上一个在下。下屏是唯一的触摸屏。

从编程角度来看这也有两个屏。一个主屏和一个副屏。每一个程序设计中的屏是和硬件对应的。在这个例子中我们只使用一个屏,主屏,它所对应的是硬件的上屏幕。
我们用一个叫'videoSetMode'的函数来设置屏的模式。有很多“帧缓冲”内存可以被映射到一个屏幕。这就允许我们实现双缓冲、特殊页之类的功能。我们现在只使用一个“帧缓冲”,所以我们使用“MODE_FB0”;

 videoSetMode(MODE_FB0);

“帧缓冲”的内存区域是一部分带有一个字母“A”的名叫'VRAM'的内存。对每一部分,我们需要告诉显示系统那一部分'VRAM' 内存我们用来做 “帧缓冲”。我们可以像这样使用第一个,VRAM_A:

vramSetBankA(VRAM_A_LCD);

画一个图形:
我们要在显示屏上画的是一个单色矩形。我们写了一个函数来实现它,函数参数包括矩形在屏幕上的位置的X、Y坐标、颜色、以及一个指向“帧缓冲”区的指针:

void draw_shape(int x, int y, uint16* buffer, uint16 color)
{
  buffer += y * SCREEN_WIDTH + x;
  for(int i = 0; i < shape_height; ++i) {
    uint16* line = buffer + (SCREEN_WIDTH * i);
    for(int j = 0; j < shape_width; ++j) {
      *line++ = color;
    }
  }
}


“帧缓冲”在内存中以行排列,所以如果屏幕有200象素宽,那麽“帧缓冲”开始的200个uint16(一个象素由一个uint16数据表示)数据单元就代表显示屏的第一行。第二个200个uint16数据单元就表示第二行,等等。
“draw_shape”函数首先通过对X、Y坐标的计算来确定第一个象素在“帧缓冲”中的位置。注意SCREEN_WIDTH 和 SCREEN_HEIGHT 是“LIBNDS”提供的返回屏幕宽、高的宏。

函数接下来通过向“帧缓冲”中的正确位置写入颜色数据来绘制图形的每一行。

shape_height 和 shape_width 是静态变量,做测试时可改变其值:

static int shape_width = 10;
static int shape_height = 10;

移动图形:
让图形能在屏幕上移动我们需要擦除现在位置的图形并在新位置重画。这可以通过保存运动前后X、Y坐标的轨迹坐标来实现:

static int old_x = 0;
static int old_y = 0;
static int shape_x = 0;
static int shape_y = 0;

接下来绘图变的简单了,我们只需通过调用'draw_shape' 函数和old_x、old_y坐标将其变为背景色来擦除它,再调用一次用新 'x'、'y'坐标来绘制新位置的图形:
 
  draw_shape(old_x, old_y, VRAM_A, RGB15(0, 0, 0));
  draw_shape(shape_x, shape_y, VRAM_A, RGB15(31, 0, 0));

注意在'draw_shape'函数中的“帧缓冲”参数位置是'VRAM_A',这是我们之前映射到主屏的“帧缓冲”。

并不太正确:

一个简单的画图和计算位置的 'main' 函数可以是这样:

int main(void)
{
  powerON(POWER_ALL);
  videoSetMode(MODE_FB0);
  vramSetBankA(VRAM_A_LCD);

  while(1) {
    old_x = shape_x;
    old_y = shape_y;
    shape_x++;
    if(shape_x + shape_width >= SCREEN_WIDTH) {
      shape_x = 0;
      shape_y += shape_height;
      if(shape_y + shape_height >= SCREEN_HEIGHT) {
 shape_y = 0;
      }
    }     
    draw_shape(old_x, old_y, VRAM_A, RGB15(0, 0, 0));
    draw_shape(shape_x, shape_y, VRAM_A, RGB15(31, 0, 0));
  }


如果你运行的话你会看见一个“倾斜”的图形在屏幕上运动。(不是矩形)。

“垂直间隔中断”(Vertical Blank Interrupt):

出现一个倾斜矩形的原因是屏幕显示的工作方法。硬件设备刷新屏幕的频率是1/60秒。它通过访问每一个象素来实现,一行接一行,复制“帧缓冲”的内容到显示屏。

同时,在 main函数中,我们在改变帧缓冲中的内容。所以如果硬件在我们擦除之前绘制图形,它不会被马上擦除。如果我们在硬件绘制之前改变数据,它会绘制一部分新数据和一部分旧数据。

幸好硬件有一种方法告诉我们它绘制完成。这就叫“垂直间隔中断”('Vertical Blank Interrupt')我们可以注册一个函数让它在这种情况发生时被调用。

中断是一个硬件机制,它中断我们当前正在做的事并调用一些函数做一些其他的事。当中断函数返回时,之前的活动就像没有中断一样继续。

为了防止我们之前看到的画图问题,我们希望在向帧缓冲写入数据时硬件不要刷新屏幕。做这的最好时候就是在垂直间隔中断时。

设置中断:

首先我们要告诉NDS在中断时我们要调用什麽函数:

void InitInterruptHandler()
{
  REG_IME = 0;
  IRQ_HANDLER = on_irq;
  REG_IE = IRQ_VBLANK;
  REG_IF = ~0;
  DISP_SR = DISP_VBLANK_IRQ;
  REG_IME = 1;
}

在这个代码片中我们只是让垂直间隔中断发生并在发生的同时调用'on_irq' 函数。

'on_irq' 函数会像我们之前在 'main'函数中做的一样向帧缓冲中写入数据:

void on_irq()

  if(REG_IF & IRQ_VBLANK) {
    draw_shape(old_x, old_y, VRAM_A, RGB15(0, 0, 0));
    draw_shape(shape_x, shape_y, VRAM_A, RGB15(31, 0, 0));

    // Tell the DS we handled the VBLANK interrupt
    VBLANK_INTR_WAIT_FLAGS |= IRQ_VBLANK;
    REG_IF |= IRQ_VBLANK;
  }
  else {
    // Ignore all other interrupts
    REG_IF = REG_IF;
  }
}

这个函数主要是向缓冲中写入数据,其余部分是做一些中断操作。

我们也需要告诉NDS我们处理了垂直同步中断。这是我们以后要调用的 'swiWaitForVBlank' 需要的,我会在那时解释。代码是这样的:

// Tell the DS we handled the VBLANK interrupt
    VBLANK_INTR_WAIT_FLAGS |= IRQ_VBLANK;
    REG_IF |= IRQ_VBLANK;

依然不太对:

我们可以将写入代码从'main' 函数中移出:
int main(void)
{
  powerON(POWER_ALL);
  videoSetMode(MODE_FB0);
  vramSetBankA(VRAM_A_LCD);
  InitInterruptHandler();
  while(1) {
    old_x = shape_x;
    old_y = shape_y;
    shape_x++;
    if(shape_x + shape_width >= SCREEN_WIDTH) {
      shape_x = 0;
      shape_y += shape_height;
      if(shape_y + shape_height >= SCREEN_HEIGHT) {
 shape_y = 0;
      }
    }     
  }


不幸的是运行结果依然有问题。矩形在屏幕上多次出现并最终形成一个棋盘样子。

幸亏问题很清楚,当垂直间隔中断发生时硬件每1/60秒调用一次on_irq 函数,这时图形正在被擦除和重画。

不幸的是 'main' 函数中的'while'循环和帧率并不同步。它快速的运行使得图形的坐标不断增加,这可能在'on_irq' 程序调用时运行了50次。结果绘图程序擦除了已经被更新了50次的图形坐标。所以错误的区域被擦除了。

纠正:

我们可以通过让'while'循环等待直到中断发生。这可以通过让循环慢下来的侧面效果来实现。ARM9处理器可以有效的减慢直到中断发生。它也可以使我们的帧率维持在60帧每秒。实现这个功能的函数是'swiWaitForVBlank'。

你可能记得之前在'on_irq' 函数中我们设置了一些寄存器,说是用来处理垂直间隔中断的。这是'swiWaitForVBlank'工作所必须的。如果不设置的话'swiWaitForVBlank'会挂起硬件并等待一个永不会发生的中断消息。

加入这些行使得我们的程序正确运行:

int main(void)
{
  powerON(POWER_ALL);
  videoSetMode(MODE_FB0);
  vramSetBankA(VRAM_A_LCD);
  InitInterruptHandler();
  while(1) {
    old_x = shape_x;
    old_y = shape_y;
    shape_x++;
    if(shape_x + shape_width >= SCREEN_WIDTH) {
      shape_x = 0;
      shape_y += shape_height;
      if(shape_y + shape_height >= SCREEN_HEIGHT) {
 shape_y = 0;
      }
    }     
    swiWaitForVBlank();
  }

  return 0;
}

交换屏幕:

我之前提到主屏是可以映射到硬件的上屏或下屏的。当前程序的主屏是映射到上屏的。我们可以通过使用'lcdSwap'函数来改变它。它可以在任何时候调用来交换屏幕。下面的函数会使得程序运行在触摸屏上:

int main(void)
{
  powerON(POWER_ALL);
  videoSetMode(MODE_FB0);
  vramSetBankA(VRAM_A_LCD);
  InitInterruptHandler();
  lcdSwap();
  while(1) {
    old_x = shape_x;
    old_y = shape_y;
    shape_x++;
    if(shape_x + shape_width >= SCREEN_WIDTH) {
      shape_x = 0;
      shape_y += shape_height;
      if(shape_y + shape_height >= SCREEN_HEIGHT) {
 shape_y = 0;
      }
    }     
    swiWaitForVBlank();
  }

  return 0;
}

 

这一节,我们介绍如何处理用户的按键操作。

REG_KEYINPUT 寄存器:

NDS有一个硬件寄存器,当按键被按下时,寄存器的值会改变。寄存器REG_KEYINPUT 位于内存地址的0x4000130处。它是只读寄存器。当按键时,值会改变:

KEYS Bit Key                'ndslib' define   Down if    Up if
   0     A                     KEY_A          Cleared     Set
   1     B                     KEY_B          Cleared     Set
   2   Select                  KEY_SELECT     Cleared     Set
   3   Start                   KEY_START      Cleared     Set
   4  Directional Right        KEY_RIGHT      Cleared     Set
   5  Directional Left         KEY_LEFT       Cleared     Set
   6  Directional Up           KEY_UP         Cleared     Set
   7  Directional Down         KEY_DOWN       Cleared     Set
   8  Right Alternate Button   KEY_R          Cleared     Set
   9  Left Alternate  Button   KEY_L          Cleared     Set

REG_KEYXY 寄存器:
你会注意到这里漏掉了两个键, 'X' 和 'Y'。
这两个键是从不同的寄存器读取的,REG_KEYXY寄存器,在内存地址0x04000136处。不幸的是这个寄存器只能被ARM7读取。

为了让它能被ARM9读取,在LIBNDS的ARM7样板代码中,ARM7在垂直间隔中断时读取寄存器值并储存在IPC数据结构中,IPC可被ARM9读取。这是相关代码片:

void InterruptHandler(void) {
   [...]

   but = REG_KEYXY;

   [...]

   IPC->heartbeat = heartbeat;
   IPC->buttons   = but;
   IPC->touchX    = x;

   [...]
}

REG_KEYXY 寄存器不仅包含X、Y键状态位,它还包括触控笔和NDS屏幕和上或打开的状态位:

XKEYS Bit Key       'ndslib' define   Down if... Up if ...
    0      X            (1 << 0)        Cleared Set
    1      Y            (1 << 1)        Cleared Set
    2     Pen Down      (1 << 6)        Cleared Set
    3     Hinge         (1 << 7)            Set Cleared


读键:

使读取 REG_KEYINPUT 寄存器变的简单一点,我取它值的补码。这允许使用'&' 操作符来判断键是否被按下。我们可以这样写代码:

    uint16 keysPressed = ~(REG_KEYINPUT);
    if(keys_pressed & KEY_UP)
      --shape_y;
来取代这个不直观的
    if(!(REG_KEYINPUT & KEY_UP))
      --shape_y;

同样的事ARM7可以这样做:
 uint16 specialKeysPressed = ~IPC->buttons;

    // Y Key
    if(specialKeysPressed & (1 << 1))
      shape_color = RGB15(7, 7, 7);

    // X Key
    if(specialKeysPressed & (1 << 0))
      shape_color = RGB15(0, 15, 15);

    // Pen Down
    if(specialKeysPressed & (1 << 6))
      shape_color = RGB15(0, 31, 31);

    // Hinge closed
    if(!(specialKeysPressed & (1 << 7)))
      shape_color = RGB15(0, 0, 0);


通过键来控制图形移动:
我们对上一篇教程中的示例做一下轻微改动,以通过键来控制。

我们用方向键控制来代替图形的自动移动。其他的键控制图形的颜色。合上屏幕会使它消失,打开再按一个键又会让它重现。

我们使用一个公共变量来储存颜色值:

static uint16 shape_color = RGB15(31, 0, 0);

绘图代码也须改变:
void on_irq()

  if(REG_IF & IRQ_VBLANK) {
    draw_shape(old_x, old_y, VRAM_A, RGB15(0, 0, 0));
    draw_shape(shape_x, shape_y, VRAM_A, shape_color);

    // Tell the DS we handled the VBLANK interrupt
    VBLANK_INTR_WAIT_FLAGS |= IRQ_VBLANK;
    REG_IF |= IRQ_VBLANK;
  }
  else {
    // Ignore all other interrupts
    REG_IF = REG_IF;
  }
}

图形通过方向键移动:

    uint16 keysPressed = ~(REG_KEYINPUT);

    // Based on the key pressed, move the shape.
    if(keysPressed & KEY_UP)
      --shape_y;
    if(keysPressed & KEY_DOWN)
      ++shape_y;
    if(keysPressed & KEY_LEFT)
      --shape_x;
    if(keysPressed & KEY_RIGHT)
      ++shape_x;

颜色通过测试IPC->buttons的值来改变,但首先取它的补码使其更加直观:

   uint16 specialKeysPressed = ~IPC->buttons;

    // Change the color of the shape if the relevant key was pressed.
    if(keysPressed & KEY_A)
      shape_color = RGB15(31, 0, 0);

    if(keysPressed & KEY_B)
      shape_color = RGB15(0, 31, 0);

    if(keysPressed & KEY_SELECT)
      shape_color = RGB15(0, 0, 31);

    if(keysPressed & KEY_START)
      shape_color = RGB15(31, 31, 31);

    if(keysPressed & KEY_R)
      shape_color = RGB15(15, 0, 15);

    if(keysPressed & KEY_L)
      shape_color = RGB15(7, 15, 7);

    // Y Key
    if(specialKeysPressed & (1 << 1))
      shape_color = RGB15(7, 7, 7);

    // X Key
    if(specialKeysPressed & (1 << 0))
      shape_color = RGB15(0, 15, 15);

    // Pen Down
    if(specialKeysPressed & (1 << 6))
      shape_color = RGB15(0, 31, 31);

    // Hinge closed
    if(!(specialKeysPressed & (1 << 7)))
      shape_color = RGB15(0, 0, 0);

 

 

posted @ 18:02 | Feedback (1)

2006年6月13日 #

思想这个东西...

如果你对以下见解有不同看法,我完全理解:
 
马克思主义教材否定唯物机械论,并且说了一堆其实等于没说的理由,比如人的主观能动性。可是我不以为然,我始终觉得,唯物机械论是有道理的。人无非也是由微粒构成,不管这些微粒是我们认识的电子中子,还是还没有认识的其他子,他都一定服从全部的宇宙物理定律,包括我们认识的和尚未认识的。假设A代表宇宙的全部物理定律,B是宇宙中的全部微粒在某个时刻的全部状态,那么A作用于B,一定能推出宇宙的在无限小的时间距离中的下一个状态,也就是说,全宇宙的发展在宇宙诞生的一刻就注定了。你会说,不可能,我明天会去做什么?我明天可能9点起床,可能9点10分起床,难道也是注定的?没错,注定的。你明天无论9点起床还是9点10分起床,都一定有原因,而这些原因也一定还有原因,推广开来,你什么时候起床也是注定的。只是由于这些原因我们可能一时难以辨别,就以一个“人的主观能动性”概括之。当然从这个角度来说,也不错,如果你把“决定一个事件的全部原因以及原因的原因”理解为偶然和人的主观能动性的话,我们的观点其实一样了。
 

posted @ 18:06 | Feedback (5)

2006年5月29日 #

以身试劲-内家功夫亲身体验记

我见到了A师兄,出于A师兄的要求,故用A代替。
体验1-透劲
我伸出胳膊,鼓足劲,做好一切防护心理及生理准备,A师兄右手抬于我胳膊上方20-30公分,不发劲,仅仅向下挂了我胳膊一下,当时只觉得如同一块几十斤重的铁棒敲在我的胳膊上,身体立刻失去平衡,几乎被甩翻在地。打完后,皮肤不红不同,似乎没有感觉,但是一握拳,顿觉手臂筋肉断裂般疼痛。10分钟过去后,觉得胳膊骨头疼痛,不能触摸,一摸则疼痛难忍,没有皮外伤,被打处得肌肉不同,可以捏,但决不能按。30分钟过去后,被打手臂完全丧失运动能力。 这一下我回去后买了40块膏药来贴,现在可以活动了。好在A师兄只是轻挂了我一下,要是真正发劲。。。。。。
 
体验2-推
我用尽全力推A师兄,感觉除了在推一堵墙以外也就没什么了。。。寒一个
 
体验3-长劲发人
我以最稳的姿势预备,A师兄双掌贴在我胸口,然后一个长劲,我腾空而起,向后飞出2米。。。。不痛,因为没有发打击力
 
体验4-短劲发人
我同上,A师兄单掌贴我胸,然后发一个短劲,只听“啪”的一声,我向后退了数步,胸口痛。。。短劲还是具有打击力的,好在师兄只用了皮毛劲
 
体验5-撞
A师兄后肩轻靠我一下,我腾空而起大概20厘米
 
体验6-弹
我抓住A师兄的胳膊,用力压他,然后他一弹,我被弹得腾空飞出2米开外
 
虽然飞来飞去被体验的很惨,不过终于亲身体验了传说中的内家功夫的利害指出,这还只是出于表演目的用的皮毛劲而已。
PS.现在的虎皮膏药真贵...

posted @ 12:46 | Feedback (8)

2006年5月26日 #

自配曲, 十三不亲,欢迎传唱

一个空格为1拍,没有空格的是半拍,加^为低8度,~为延长一拍
 
 
 
C = 5
3  3  7  65 2 32 1, 2 2 3 2 1 21 7^
朋友们哪要记真听我唱段十三亲
6^  4 3 3 2  32 1 , 6^  6^  2 2 3~~
句句说的是大实话我的朋友啊
2  4  3  3 2 12  3 2 1~ 23 2 1
听在耳里记在心哪嗳嗨吆
 
repeat

父母亲也不一定亲父母给我们养育恩
满堂的儿女都留不住我的朋友啊
年年都得添新份哪嗳嗨吆
儿子亲也不一定亲长大以后结了婚
结婚后都是老婆好我的朋友啊
忘了父母养育恩哪嗳嗨吆
姑娘亲也不一定亲命中注定是别家的人
长大以后嫁出去我的哥们呀
一年能回几次娘家门
丈母娘亲也不一定亲姑爷常常来登门
吃完了饭菜一抹嘴儿我的哥们呀
他两脚一抬走了人哪嗳嗨吆
丈夫亲也不一定亲看见了野花起外心
他和女人去跳舞我的哥们呀
回到家里闹离婚哪嗳嗨吆
老婆亲也不一定亲背着丈夫跟了别人
整天到晚不回家我的哥们呀
忘了夫妻结发恩哪嗳嗨吆
亲戚亲也不一定亲亲戚都有穷富分
穷也不向那富来奔我的哥们呀
富了不登穷家门哪嗳嗨吆
金钱亲也不一定亲人活一世情最真
虽说你有千百万我的哥们呀
死后你也带不走半分文
哥们亲也不一定亲平时喝酒最认真
一旦兄弟遇了难我的哥们呀
一年半载见不着人哪嗳嗨吆
兄弟亲呀那才叫亲一奶同胞心连心
别看平时来往少我的哥们呀
患难之处见亲人哪嗳嗨吆
姐妹亲呀那才叫亲你来我往串家门
虽说有点私房钱我的哥们呀
花钱的都是自家人哪嗳嗨吆
五谷杂粮才叫亲颗颗粒粒养咱身
一年四季离不了我的哥们呀
吃粮别忘种田人哪嗳嗨吆
毛主席最最亲领导咱们闹翻身
建立人民共和国我的朋友啊
幸福生活到如今哪嗳嗨吆

posted @ 15:54 | Feedback (3)