Archive for May, 2009

0

ARM在汇编代码中调用C函数


对于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(-Thumb Procedure Call Standard),ATPCS主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容可以查看ADS1.2 Online Books ——Developer Guide的2.1节。这篇文档要讲的是 汇编代码中对C函数调用时如何进行参数的传递以及如何从C函数正确返回

不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递。

我们先讨论一下形参个数为4的情况.
实例1:
test_asm_args.asm
//——————————————————————————–
        IMPORT test_c_args ;声明test_c_args函数
        AREA TEST_ASM, CODE, READONLY
        EXPORT test_asm_args
test_asm_args
       STR lr, [sp, #-4]! 
;保存当前lr
        ldr r0,=0×10       
;参数 1
        ldr r1,=0×20        
;参数 2
        ldr r2,=0×30        
;参数 3
        ldr r3,=0×40       ;参数 4
        bl test_c_args      
;调用C函数
        LDR pc, [sp], #4  
;将lr装进pc(返回main函数) 
        END
test_c_args.c
//——————————————————————————–
void test_c_args(int a,int b,int c,int d)
{
        printk(“test_c_args:\n”);
        printk(“%0x %0x %0x %0x\n”,a,b,c,d);
}
main.c
//——————————————————————————–
int main()
{
     test_asm_args();
     for(;;);
}

程序从main函数开始执行,main调用了test_asm_args,test_asm_args调用了test_c_args,最后从test_asm_args返回main.
代码分别使用了汇编和C定义了两个函数,test_asm_args 和 test_c_args,test_asm_args调用了test_c_args,其参数的传递方式就是向R0~R3分别写入参数值,之后使用bl语句对test_c_args进行调用。其中值得注意的地方是用红色标记的语句,test_asm_args在调用test_c_args之前必须把当前的lr入栈,调用完test_c_args之后再把刚才保存在栈中的lr写回pc,这样才能返回到main函数中。


如果test_c_args的参数是8个呢?这种情况test_asm_args应该怎样传递参数呢?
实例2:
test_asm_args.asm
//——————————————————————————–
        IMPORT test_c_args ;声明test_c_args函数
        AREA TEST_ASM, CODE, READONLY
        EXPORT test_asm_args
test_asm_args
       STR lr, [sp, #-4]! 
;保存当前lr
       ldr r0,=0×1 ;参数 1
       ldr r1,=0×2 ;参数 2
       ldr r2,=0×3 ;参数 3
       ldr r3,=0×4 ;参数 4
      
 ldr r4,=0×8
       str r4,[sp,#-4]! ;参数 8 入栈
       ldr r4,=0×7
       str r4,[sp,#-4]! ;参数 7 入栈
       ldr r4,=0×6
       str r4,[sp,#-4]! ;参数 6 入栈
       ldr r4,=0×5
       str r4,[sp,#-4]! ;参数 5 入栈
       bl test_c_args_lots
       ADD sp, sp, #4     ;清除栈中参数 5,本语句执行完后sp指向 参数6 
       ADD sp, sp, #4     ;清除栈中参数 6,本语句执行完后sp指向 参数7
       ADD sp, sp, #4     ;清除栈中参数 7,本语句执行完后sp指向 参数8
       ADD sp, sp, #4     ;清除栈中参数 8,本语句执行完后sp指向 lr
       LDR pc, [sp],#4    
;将lr装进pc(返回main函数) 
        END
test_c_args.c
//——————————————————————————–
void test_c_args(int a,int b,int c,int d,int e,int f,int g,int h)
{
       printk(“test_c_args_lots:\n”);
       printk(“%0x %0x %0x %0x %0x %0x %0x %0x\n”,
              a,b,c,d,e,f,g,h);
}
main.c
//——————————————————————————–
int main()
{
     test_asm_args();
     for(;;);
}

这部分的代码和实例1的代码大部分是相同的,不同的地方是test_c_args的参数个数和test_asm_args的参数传递方式。
在test_asm_args中,参数1~参数4还是通过R0~R3进行传递,而参数5~参数8则是通过把其压入堆栈的方式进行传递,不过要注意这四个入栈参数的入栈顺序,是以参数8->参数7->参数6->参数5的顺序入栈的。
直到调用test_c_args之前,堆栈内容如下:
sp->+———-+
        |  参数5  |
       +———-+
        |  参数6  |
       +———-+
        |  参数7  |
       +———-+
        |  参数8  |
       +———-+
        |     lr      |
       +———-+
test_c_args执行返回后,则设置sp,对之前入栈的参数进行清除,最后将lr装入pc返回main函数,在执行 LDR pc, [sp],#4 指令之前堆栈内容如下:
       +———-+
        |  参数5  |
       +———-+
        |  参数6  |
       +———-+
        |  参数7  |
       +———-+
        |  参数8  |
sp->+———-+
        |     lr      |
       +———-+

0

Ophone媲美苹果 联想TD智能机O1独家初评


目前手机操作系统众多,特别在08年,传统的热门手机操作系统Symbian S60,Windows Mobile竞争激烈,像苹果MAC OS X、Google Android这样的新生代也不断涌现,智能手机系统迅猛的发展势头预示着手机市场的激烈争夺已经从单一的硬件之争逐渐转向开放性的操作系统之争。

  今天我们的主角联想O1就是采用了移动OMS全新智能操作系统的智能手机,也就是之前呼声很高的OPhone,型号01的意思,就是第一部采用OMS的OPhone手机。形象地说,O1是一部“混血”手机,它采用了中国移动的OMS系统,而联想则是O1的硬件生产厂商,“混血”最大的特点在于运营商中国移动可以进行深度定制,将服务更好融入手机系统之中。

联想O1
联想 O1(Ophone)  图 库  评 测  论 坛  报 价

科技感十足

  联想O1的外观图片在网上已经流传了一段时间,现在我们终于可以真实地感受这部强机,联想O1的外形科技感十足,宽大的机身与薄薄的厚度形成鲜明的对比,巨大的显示屏占据了机身大部分的面积。联想O1采用黑色搭配银色,让这部手机十分“百搭”,时尚风、商务风,都能在O1身上感受到。

联想ophone

  机身的尺寸O1显得比较夸张,116mm的机身长度,85mm的宽度,联想O1拥有“大饼脸”,但是机身的厚度只有12mm,联想O1机身尺寸与苹果iPhone 3G十分接近。联想O1的外形十分上镜,不管是机身造型还是颜色搭配,感觉很有档次,但是实际上手感觉质感有所欠缺,毕竟O1机身基本上都是塑料材质,包括机身两侧的银色包边采用的是电镀工艺,不像iPhone那样采用金属材质。

联想ophone

  联想O1的外形设计应该说非常成功,但是细节方面有待提高,一方面烤漆的质感有所不足,另外机身模块之间的结合不够紧密。使用过程中,背盖会发出吱吱的响声,令使用的舒适度降低,希望联想O1正式上市这些细节部分会有所改进。

联想ophone
比较庞大的机身

 

细节设计

  下面我们看看联想O1的细节设计,O1的整体风格还是比较简洁的,特别是正面,不过经过测试,发现O1的听筒位置内有乾坤,细细一条缝,除了听筒的功能,联想还为O1安置了一颗指示灯以及光线感应器,不注意的话很难看出来。

联想ophone

  机身顶部,联想O1安置了一颗电源键,除了充当开关机的功能,还兼顾了锁定、开锁功能,这个按键的功能和大部分Windows Mobile机型一样,O1这个按键的手感还算不错,没有生硬的感觉。需要一提的是,联想O1支持CMMB电视功能,所以侧边还有拉出来的天线,这样的设计在一些支持CMMB电视功能的手机上也比较常见。

联想O1

  我们转到底部,联想O1安置了标准的miniUSB接口,旁边的小孔是话筒,O1的这里还隐藏了手写笔,不仔细看还真不能发觉到。

img2.pconline.com.cn/pcon

联想ophone
拉出的手写笔

  我们接着看机身两侧,联想O1左则安置了音量键,右侧则安置了独立的快门键,由于按键的颜色与机身一样,所以看起来并没有复杂的感觉。

联想ophone

联想ophone

  看到背面,之前曝光的O1图片有OMS的字样,不过这次送测的O1却印上了心机的标志。可以看到O1的摄像头旁边安置了闪光灯,另一边则是独立扬声器。

联想ophone
背面的设计

 

按键设计

  之前不少网友都在猜测第一部OPhone手机O1会安置几个按键,网上也有各式的图片流出,现在正式为大家揭晓!联想O1安置了一个物理按键,不过在屏幕下方的两边,还有2个感应按键,不过联想为了增加神秘感,这2个感应按键平时不会点亮,只有按压过后才会点亮一段时间,所以如果没经过摸索,基本上很难察觉联想O1的感应按键。

联想ophone

联想ophone
按键灯光

扩展性

  联想O1的数据接口并不多,机身就只有一个miniUSB接口,就是说不管耳机、数据线、充电都是通过这个接口来实现。之前盛传联想O1会推出8G以及16G两个版本,不过这次送测的O1工程机只内置了3G内存,而且并不支持储存卡扩展。

  既然联想O1采用了大容量内存,自然要测试传输的速度如何,我们用miniUSB接口数据线将联想O1与电脑连接,并且用专业的软件检测O1的传输速度,测试结果令人有点失望,联想O1的平均值只有0.8MB/秒的传输速度,目前主流的大容量手机基本上能够达到4MB/秒-7MB/秒的速度。

联想ophone

  另外除了蓝牙、数据线这样基本的传输方式,这次在联想O1身上,居然出现了WIFI模块,O1正式上市时这个功能会否被阉割目前还是未知数。

联想ophone
联想ophone
联想O1具备WiFi无线模块

屏幕效果

  联想O1采用了3.5英寸的大屏幕,由于屏幕的尺寸较大,单手操作O1还是比较困难的,一手拿一手点比较合适。O1这块大屏幕的参数方面还是比较突出的,分辨率高达320×480像素,虽然尺寸高达3.5英寸,但是颗粒感并不太明显,整体的视觉效果很不错。

  就实际显示效果来看,联想O1的显示风格偏于“暖色调”,颜色十分浓郁,不过O1显示屏的可视角度一般,基本上从侧面超过45度就会出现明显的偏色。

联想ophone
超大屏幕

  对触屏手机比较了解的网友都知道,目前主流的触屏手机一般分为两种,一种类似iPhone的电容式屏幕,需要用手指头去操控,指甲与手写笔是行不通的,联想O1则是采用比较传统的电阻式屏幕感应模式,用指甲、手写笔触控比用手指头灵敏很多。

联想ophone
屏幕切换界面

解锁方式

  自从苹果iPhone采用了滑动式的解锁方式,全触控的手机在这方面都下了功夫,联想O1这次也带给我们新的解锁模式,在待机界面,有一个蓝色的圈圈不断的浮动,有点像Vista系统的泡泡屏保,我们需要做的,就是将这个浮动的蓝色泡泡拖动到左下角。

联想ophone

  虽然这种解锁方式算比较创新,但是弊端也是有的,当泡泡浮动到屏幕的上方时,要“捉住”蓝色圈圈不是很顺手,单手更是难以操作,如果联想O1能够提供多种解锁方式供用户选择,就更人性化了。

联想ophone
把光圈拖动至左下角解锁

PConline评测室总结

联想ophone
联想O1

  对于联想O1的初步测试我们就进行到这里,这部“混血”手机最大的亮点,还是在于中国移动的OMS操作系统。联想O1一直在网上有那么多的相关报道,直到真机在手我们才对它有真实的体会。联想O1无论是外形、做工、手感、操作都比我们预想的要好一些,特别是大屏幕的效果和触摸的流畅度。究竟联想O1的OMS系统会带给我们怎样的体验?后续的系列独家评测文章将会陆续为各位网友献上,大家敬请期待!

0

野外求生实用教学之怎样在野外安全的大便


室外、野外大便俗称“拉野屎”,小时候经常拉野屎,距今已有十数年没有拉过野屎了,非常怀念拉野屎的那种惬意,远非坐在狭小的卫生间拉屎的那种感觉,边拉野屎还可以边欣赏四周的风光,呼吸野外新鲜的空气,那拉屎质量叫一个高啊,如果有机会一定要再拉几次野屎!

以前教过大家拉屎时如何压水花,那是在室内拉屎,下面告诉大家怎样在野外安全的拉屎,以保证拉野屎的舒适度和安全系数。

拉野屎首先最重要的是地点的选择:

选择排水良好的地方,尽量避免会积水的地方,以免弄湿鞋子。
冬天选择可以避风和有阳光的地方,因为皮肤大面积暴露会散失大量人体热量。  但在狩猎区必须注意,因为反光可能会使猎人误击。
在山涧河谷大便的时候注意防洪,上游降雨可以导致山洪突然爆发。请选择地势较高的地方,因为据国内最神秘的部门——相关部门统计:人大便时的移动能力会下降72.53%。
尽量避开山崖或可能发生崩塌的坡地,尽管有人觉得朝悬崖下大便很酷。据某省旅游数据统计,半数以上的坠崖事故原因如此。
远离水源和去水源的必经之地,因为这也是野兽经常出没的地方,野兽受到刺激可能会发动攻击。雷雨天气不要在地势突出的地方和大树下大便,以免遭雷击。

在拉野屎进行中要注意一下三个方面:

可用木棍或树枝敲击附近草丛,将潜伏的动物驱走
多蚊虫的地方可将暴露的皮肤涂满驱虫药剂
可间歇的咳嗽以显示你的存在位置,避免野兽和他人的无意闯入,并能告知同伴你的状况良好

拉野屎常识问答集锦:

1、如果遇到一条野狗,它是先咬我还是先咬便便?答:看运气。
2、没纸怎么办?答:  世界上有种东西叫树叶。从外型上就有大有小,有长有短.大的肯定就不容易搞到手上。从材质上说,有光滑的,毛毛的,硬的,软的,很多种不同的混杂在一起。实践告诉我们,光滑的容易打滑。毛的扎PP。
3、天热时在野外大便会招来大批绿头苍蝇,在屁股下面嗡嗡作响,非常讨厌,不知有经验的朋友怎样处理?答:拉出第一泡屎后,立即稍稍抬起屁股,然后向前迈

(挪动)左腿一大步,右腿跟上向前一大步,这样就和第一泡屎有了一步的距离,苍蝇就不会干扰了.如果仍觉不够,可以仿上面的动作再做一便,那么可以扩大与
第一泡屎的距离,然后继续大便。
4、问:拉完野屎如何处理?答:无须特殊处理,可以自然分解。  一旦你遇险,救援人员也可以作为前进路标。

01

How to run mod file that is loaded into HEAP


05-15-2008, 11:20 PM
Brewin
Registered User Join Date: Mar 2006
Posts: 152
Rep Power: 4

How to run file that is loaded in the

——————————————————————————–

Hi everyone,
How can I run a MOD file that is loaded into the HEAP???? Any idea’s…

__________________
Thanks everyone for the info they share here..
Ramki@TTSL
VALUE HAS A VALUE ONLY IF ITS VALUE IS VALUED

——————————————————————————–
Last edited by Brewin : 05-15-2008 at 11:27 PM.

Brewin
View Public Profile
Send a private message to Brewin
Send email to Brewin
Find all posts by Brewin
Add Brewin to Your Buddy List

#2 06-04-2008, 07:36 PM
ArdyFalls
Registered User Join Date: Apr 2007
Posts: 23
Rep Power: 0

Steps for loading a mod into the heap

——————————————————————————–

1) malloc memory on the heap that is the size of the mode file + 4 in bytes.
2) Set the initial 4 btyes to point to the AEE struct thing that has all the AEEStdlib funcitons.
3) Set the processors program counter (PC register) to address 0
Please note, I could be kinda off, look at the code for AEE_ModLoad, it is always at address zero. It will tell you the arguments that you need to pass to it before setting the pc register to it. Remember, the first four arguments of any function are passed in registers r0 -r4.

I hope this helps

__________________
Ardavon Falls
Senior Software Engineer
MobiTV Inc

ArdyFalls
View Public Profile
Send a private message to ArdyFalls
Find all posts by ArdyFalls
Add ArdyFalls to Your Buddy List

#3 07-28-2008, 10:45 PM
Brewin
Registered User Join Date: Mar 2006
Posts: 152
Rep Power: 4

Loading and running a compressed mod file…

——————————————————————————–

Hi All,
To run a compressed mod file in you need to create a mod loader(Approxmately 700+ bytes, atleast mine).

For 2.x brew devices, your mod loader file name should be same as of your app name.
Ex: myapp.mif, myapp.mod(your mod loader), filename.gz(your compressed myapp.mod file, you can name this file as filename.bar so that you don’t have any problems when user disables your app).

For 3.x brew devices on which we can read a mod file by setting the required permissions in MIF can add this mod loader file content at the start of your compressed mod file or you can use the same as that used for 2.x which don’t required any permissions or settings in MIF.
Ex: myapp.mif, myapp.mod(your mod loader + filename.gz).

To create a mod loader, you have every information you need in 2 threads.

First read only the first post by user “ajiva” in the following thread. Then return to this thread for additional info you need.

Quote:

http://brewforums.qualcomm.com/showthread.php?t=11637

A call to AEEMod_Load that you have seen in the first thread will set the params in the registers.

Quote:
Note 1: Don’t release the buffer that contains the uncompressed content of your actual mod file in your mod loader. Once the actual app is closed, it will released by BREW system.
Note2: Release all other allocated buffers and interfaces you created in mod loader before making a call to AEEMod_Load.

Thanks for ajiva and Ardy Falls for the valuable info they provided.

__________________
Thanks everyone for the info they share here..
Ramki@TTSL
VALUE HAS A VALUE ONLY IF ITS VALUE IS VALUED

——————————————————————————–
Last edited by Brewin : 08-06-2008 at 12:59 AM.

Brewin
View Public Profile
Send a private message to Brewin
Send email to Brewin
Find all posts by Brewin
Add Brewin to Your Buddy List

#4 07-29-2008, 12:36 AM
Rajni
Registered User Join Date: Apr 2005
Posts: 32
Rep Power: 0

Really usefull

——————————————————————————–

Hi Ramki,
Really very useful information.

Rajni
View Public Profile
Send a private message to Rajni
Find all posts by Rajni
Add Rajni to Your Buddy List

#5 08-06-2008, 12:57 AM
Brewin
Registered User Join Date: Mar 2006
Posts: 152
Rep Power: 4

Loading and running a compressed extension mod file…

——————————————————————————–

Hi all,
The above procedure is not working in case if the compressed mod file is an extension. Applet compressed mod file is running fine. The decompressed file of the extension mod is Identical with the original extension mod file, but device is getting crashed when AEEMod_Load of the extension mod is executed.

if both an applet and extensions loads the same way then
why is this difference..

If I use normal extension mod everything goes well.

Do anyone has any idea about this.

Quote:
The extension I used is xmlparser, I will write my own extension and check does it make any difference using debug info.

__________________
Thanks everyone for the info they share here..
Ramki@TTSL
VALUE HAS A VALUE ONLY IF ITS VALUE IS VALUED

——————————————————————————–
Last edited by Brewin : 08-06-2008 at 01:02 AM.

Brewin
View Public Profile
Send a private message to Brewin
Send email to Brewin
Find all posts by Brewin
Add Brewin to Your Buddy List

#6 08-06-2008, 06:15 AM
Brewin
Registered User Join Date: Mar 2006
Posts: 152
Rep Power: 4

Loading a compressed module(Application/Extension)

——————————————————————————–

I hope I got the resolution,
Quote:
Originally Posted by Brewin
Hi all,
The above procedure is not working in case if the compressed mod file is an extension. Applet compressed mod file is running fine. The decompressed file of the extension mod is Identical with the original extension mod file, but device is getting crashed when AEEMod_Load of the extension mod is executed.

if both an applet and extensions loads the same way then
why is this difference..

If I use normal extension mod everything goes well.

Do anyone has any idea about this.

If the module irrespective of Application or Extension, is complied with standard AEEModGen.C then the MOD compression will work else the behavior is unknown.

Hope this helps, Please correct me if I am wrong.
But since all module will have the same entry point why this happens…

__________________
Thanks everyone for the info they share here..
Ramki@TTSL
VALUE HAS A VALUE ONLY IF ITS VALUE IS VALUED

——————————————————————————–
Last edited by Brewin : 08-06-2008 at 06:17 AM.

Brewin
View Public Profile
Send a private message to Brewin
Send email to Brewin
Find all posts by Brewin
Add Brewin to Your Buddy List

#7 08-07-2008, 12:32 PM
TG1e
Registered User Join Date: May 2008
Posts: 3
Rep Power: 0

Reference code please [if possible]

——————————————————————————–

Addressed to all who are working (or have worked) on this topic of loading a mod from heap mem…
Will it be possible for you to post some reference code?

Also, for clarity…

Do we only have to call AEEMod_Load?
Or do we have to call the other functions as well….How does the AEE come to know that we have loaded the compressed (and now decompressed) mod? How will the AEE send events to this newly loaded mod?

Awaiting your response(s)…

——————————————————————————–
Last edited by TG1e : 08-07-2008 at 12:34 PM.

TG1e
View Public Profile
Send a private message to TG1e
Find all posts by TG1e
Add TG1e to Your Buddy List

#8 08-23-2008, 06:51 AM
Brewin
Registered User Join Date: Mar 2006
Posts: 152
Rep Power: 4

Loading compressed mod file..Step by Step

——————————————————————————–

Quote:
Originally Posted by TG1e
Addressed to all who are working (or have worked) on this topic of loading a mod from heap mem…
Will it be possible for you to post some reference code?

What code you need, first compress you MOD file with gzip(if 7zip is used then you need to supply you own decompressor code in the mod loader or bootloader)
Example: You have an application with myapp.mod, myapp.bar, myapp.mif
After compressing MOD, your files are myapp.zip, myapp.bar,myapp.mif.
Now write a bootloader which will load myapp.zip into memory and uncompress..

1. Create a project with name ex: .

2. copy AEEModGen.C and AEEModGen.H into your project and add the following header files
AEE.h, AEEShell.h, AEEUnzipStream.h, AEEFile.h, AEEHeap.h, AEEStdLib.h.

3. Delete everything not related to AEEMod_Load and AEEStaticMod_New in both files and add the below line

Quote:
For AEEMod_Load typecast…..
typedef int (*RunLoadMod)(IShell *ps, void * ph, IModule ** pMod);

2. Delete every thing inside the AEEStaticMod_New function.

3. Now inside the AEEStaticMod_New function declare pointers to IUnzipAStream, IFileMgr and IFile interfaces.

4. Create an Instance of IFileMgr and IUnzipStream(if compressed with gzip). else
provide your own decompress function here.

5.Now allocate some memory and copy the “ph” variable which is the pointer to AEEHelper Functions into the first four bytes of allocated memory.

6. Open myapp.zip and Set it for uncompressing using
” IUNZIPASTREAM_SetStream “.

7. Now read the uncompressed stream using ” IUNZIPASTREAM_Read ” and copy the contents from 5th byte onwards into the same buffer using MEMCPY, REALLOC as per requirements.
( Now your buffer contains pointer to AEEHelper functions + uncompressed myapp.zip, now buffer size will be a minimum of 4+sizeof(myapp.mod) ).

8. Once your buffer is ready with the above you are done. Release the IFileMgr, Ifile and IUnzipStream interface pointers.

9. Now type cast the 5th byte of the buffer to AEEMod_Load with a return statement like

Quote:
return (RunLoadMod)(buffer+4)(pIShell,ph,ppMod);
to execute the actual mod file….

Note: Don’t release the buffer, once the app exits this memory will be released by AEE.

You modloader is ready now.
Compile and create the mod file and rename the mod file as myapp.mod.

Now you app contains, myapp.mif, myapp.mod, myapp.zip, myapp.bar.

Quote:
Also, for clarity…
Do we only have to call AEEMod_Load?
Or do we have to call the other functions as well….How does the AEE come to know that we have loaded the compressed (and now decompressed) mod? How will the AEE send events to this newly loaded mod?

When you click on your app, AEE loads the mod file associated with the app name in this case it is myapp.mod(mod loader) and executes the first address i.e AEEMod_load which will feed the actual mod to the AEE, it’s not the AEE that load your actual MOD file.

There is no applet related to modloader, when AEE encounter the last statement in the AEEStaticMod_New of the modloader AEE just jumps to the address where we have kept the actual MOD file and exectues from there in the process whatever is created and registered(IModuleVtbl, Applet, Applet_HandleEvent) are all belongs to the actual application mod file and AEE will send events to the registered handleEvent of the top visible applet which is nothing but your application handle Event.

__________________
Thanks everyone for the info they share here..
Ramki@TTSL
VALUE HAS A VALUE ONLY IF ITS VALUE IS VALUED

——————————————————————————–
Last edited by Brewin : 08-23-2008 at 06:59 AM.

Brewin
View Public Profile
Send a private message to Brewin
Send email to Brewin
Find all posts by Brewin
Add Brewin to Your Buddy List

#9 01-13-2009, 07:55 AM
ed_est
Registered User Join Date: Jan 2009
Posts: 3
Rep Power: 0

Hi Brewin,
I am interested in the functionality that you have described in this topic and I have tried to create a modloader by myself according the steps you have provided, but unfortunately my device just reboots when I am trying to execute the loaded into memory .mod file. I have tried my code on different devices with different BREW version and on all of them I had device reboot.
Here is my code in AEEStaticMod_New function ( I have simplified it because I don’t use compression for .MOD file, also I have removed all checks):

ISHELL_CreateInstance(pIShell, AEECLSID_FILEMGR, (void**) &piFileMgr);
piFile = IFILEMGR_OpenFile(piFileMgr, “realmod.bin”, _OFM_READ);
IFILE_GetInfo(piFile, &iFileInfo);
filesize = iFileInfo.dwSize;
pBuf = (byte*) MALLOC (filesize + 4);
MEMCPY(pBuf, ph, 4);
pBuf2 = pBuf + 4;
pFunc = (RunLoadMod)pBuf2;
bytesread = IFILE_Read(piFile, pBuf2, filesize);
IFILE_Release(piFile);
IFILEMGR_Release(piFileMgr);
return pFunc(pIShell,ph,ppMod);

Can you help me to understand why I can’t start .MOD file I have loaded by modloader? I am doing all needed steps that you provided in your post and everything seems correct for me, but maybe you will find that I am doing something wrong.

Thanks in advance.

ed_est
View Public Profile
Send a private message to ed_est
Send email to ed_est
Find all posts by ed_est
Add ed_est to Your Buddy List

#10 01-13-2009, 08:44 PM
Brewin
Registered User Join Date: Mar 2006
Posts: 152
Rep Power: 4

Helper functions Entry point problem…..

——————————————————————————–

Quote:
Originally Posted by ed_est
MEMCPY(pBuf, ph, 4); *********
pBuf2 = pBuf + 4;
((RunLoadMod)pBuf2)(pIShell,ph,ppMod);

You problem is here. It not properly copying the ph pointer(Helper functions Entry point).
MEMCPY(pBuf, ph, 4);

__________________
Thanks everyone for the info they share here..
Ramki@TTSL
VALUE HAS A VALUE ONLY IF ITS VALUE IS VALUED

Brewin
View Public Profile
Send a private message to Brewin
Send email to Brewin
Find all posts by Brewin
Add Brewin to Your Buddy List

#11 01-14-2009, 06:17 AM
ed_est
Registered User Join Date: Jan 2009
Posts: 3
Rep Power: 0

Brewin,
Thank you very match for your help.
I have fixed the this part of code and now it works.

0

在BREW程序中调用另一个mod的分析


近日读到这样一段程序。可以在程序运行时调用另一个mod文件并执行。这样可以做到应用自升级,压缩程序文件等实用的功能。

 

 要想理解这点,先看一下正常的BREW程序加载过程。

 BREW程序的入口,即相当于c程序的main,是位于AEEModGen.c中的AEEMod_Load函数。这可以从mak文件的连接选项中看出来。

 

LINK_ORDER = -first AEEMod_Load

 

 这个选项使得链接程序将该函数放在程序文件的0地址处。函数的定义如下:

 

int AEEMod_Load(IShell *pIShell, void *ph, IModule **ppMod)

{

   // Invoke helper function to do the actual loading.

   return AEEStaticMod_New(sizeof(AEEMod),pIShell,ph,ppMod,NULL,NULL);

}

  

  BREW底层调用这个函数,需要提供三个参数。pIShell是ISHELL接口的指针,有了它就可以创建和访问其它接口。ph是helper function类函数的函数列表指针,有了它就可以调用MALLOC等函数。ppMod是用于返回给BREW底层的地址,存储生成的module的信息。

  AEEMod_Load函数调用了AEEStaticMod_New函数。在这个函数中初始化了module数据结构,ppMod就是指向它的。函数结束后返回BREW底层。

  BREW底层稍后通过存储在module数据结构中的函数指针,调用同样位于AEEModGen.c中的AEEMod_CreateInstance函数,生成该Module的某一个Applet实例。在这个函数中会调用到我们所编写的AEEClsCreateInstance函数。在我们这个函数中注册了HandleEvent函数和FreeAppDate函数。这些信息同样通过一个applet数据结构指针返回给BREW底层。这之后,BREW会通过给HandleEvent发各种Event,来驱动程序运行。

 

  通过这整个过程,可以总结出BREW程序运行的必要条件。

  程序必须得到IShell指针,Helper Function指针,这样程序中对BREW底层各种函数的调用才能进行。BREW底层必须知道两个地址,通过它们,可以将Event传递给程序,调用FreeAppDate函数。

  因此,想在程序中调用另外一个mod,只需要想办法把程序里的IShell指针,Helper Function指针传递给mod,同时得到mod中的HandleEvent函数,FreeAppDate函数的地址就可以了。

  剩下的问题就是,程序怎样和mod文件交互呢,mod文件的格式是怎样的?

 

  生成mod的mak文件最后一般有这样两句话,

 

ld *.o a.elf

fromelf –bin a.elf a.

 

  arm链接器ld生成ELF格式,之后用格式转换工具fromelf将ELF文件转换成mod文件。

  ELF文件是带格式的可执行文件,对它的执行要靠操作系统的解析来进行。而通过–bin选项生成的mod文件,格式却是plain binary,即赤裸裸的二进制机器指令,其实是无格式的。只要将mod文件载入内存,跳转到它的0地址处,就可以一条条指令的执行下来。和mod文件交互,也就是要安排好mod文件0地址处的内容,知道mod文件的调用者会传入什么东西,mod文件会返回什么东西。

 

  

  下面来看一种实际的调用方法吧。代码如下:

 

typedef int (*RunLoadMod)(IShell *pIShell, void *ph, IModule **ppMod);

 

pData = MyLoadZip(pIShell);

if( !pData )

return EFAILED;

DBGPRINTF(“to RunLoadMod”);

if( SUCCESS != ((RunLoadMod)pData)(pIShell,ph,&pOrgMod) )

{

DBGPRINTF(“RunLoadMod_Err”);

goto Crt_Err;

}

  

  这段程序将mod文件读入内存,放在pData缓冲区内,用((RunLoadMod)pData)(pIShell,ph,&pOrgMod)一句执行之。RunLoadMod只不过是用typedef定义的一种函数指针类型。这句话的意思相当于是为待调用的mod文件准备好pIShell,ph,pOrgMod三个数据后,直接跳到内存中的mod文件的第一个字节处,将它当做普通机器指令一样执行。这种方法调用的mod文件,应是一个用完整的BREW框架编译后生成的。0地址处放下的是AEEMod_Load函数。Mod文件直接将Applet信息传递给BREW底层,注册HandleEvent函数。实际上是Mod文件取代了调用者的位置。

  

  但是这段程序很可能会运行失败。因为BREW底层除了传参外,还做了其它事情。这在AEEStdlib.h中可以看出。

 

#define GET_HELPER()      (*(((AEEHelperFuncs **)AEEMod_Load) – 1))

#define GET_HELPER_VER()  (*((uint32 *)(((byte *)AEEMod_Load) – sizeof(AEEHelperFuncs *) – sizeof(uint32))))

 

  这两个宏定义可以看成是两个全局常量。第一个是BREW Helper Function的函数指针表,第二个是函数指针表的版本号。上面提到的通过参数传递Helper Function函数指针,是只在模拟器上成立的。手机上其实是通过全局常量形式传递的。为保证mod可以正常运行,也应在缓冲区前保存这两个常量。

 

  这种“冒名顶替”的方式,对于只想压缩一下mod的应用来说是完全可以的。但有时,我们希望新调用的mod只是整个应用的一小部分,调用者继续存在。这就需要调用者充当BREW底层的一部分角色,记住mod返回的各种信息,在适当的时候调用mod中的相应函数。新调用的mod如果想要取得主mod的信息,类似于上面提到的HelperFuncs全局常量指针的方法是个可行的选择。

 

  其实知道了BREW程序的加载过程,就可以比较灵活的达到调用mod的目的,甚至BREW程序架构都是可以改动的。但是改动之后,如何在模拟器上进行调试就又成了一个问题。

 

  程序中调用mod的技巧,在国外几年前就已得到了应用。甚至有家公司专门开发了利用这个技巧压缩mod的工具。

 

http://www.s-cradle.com/english/products/sophiacompress_/index.html

 

参考文章:

 

1.How to build A mod to load B mod?

 

http://brewforums.qualcomm.com/showthread.php?t=11637

 

2.How to run mod file that is loaded in the

 

http://brewforums.qualcomm.com/showthread.php?t=18413&highlight=

 

3.深入BREW模块加载机制

 

http://nicefuture.ycool.com/post.722858.html

 

4.深度剖析BREW实现原理收藏

 

http://blog.csdn.net/Gemsea/archive/2006/09/07/1190206.aspx

01

MTK通过TRACE的栈信息寻找BUG原因与解决方法


前几天去一个公司帮他们解决BUG。BUG的描述是这样的,在使用在线QQ时,如果来电话,就会重启。没有发现ASSERT信息,只有stack dump信息。起初听他们描述,感觉像是QQ或者通话的问题。抓了TRACE之后,发现是MED模块的问题,由于MED主要是一些媒体文件的解码。由于观察现象时发现,通话时,还没有来得及响铃,就开始重启,因此可以大概推知是来电振铃出了问题,具体出在什么地方,需要查找TRACE信息。从别人那里获取的TRACE信息如下:

1745424 150706 _NIL TRACE_ERROR [1] fatal error (4): Data_abort – MED
Trace 1745424 150706 MOD_NIL TRACE_ERROR Exception type: data abort
Trace 1745424 150706 MOD_NIL TRACE_ERROR software version:
E500_A.1.4
Trace 1745424 150706 MOD_NIL TRACE_ERROR boot mode: normal mode
Trace 1745424 150706 MOD_NIL TRACE_ERROR rtc sec = 16, rtc min = 33, rtc hour = 1
Trace 1745424 150706 MOD_NIL TRACE_ERROR rtc day = 1, rtc mon = 1, rtc wday = 1, rtc year = 9
Trace 1745424 150706 MOD_NIL TRACE_ERROR execution unit: MEDMED
Trace 1745424 150706 MOD_NIL TRACE_ERROR status: 0×00000000
Trace 1745424 150706 MOD_NIL TRACE_ERROR stack pointer: 0×00169380
Trace 1745424 150706 MOD_NIL TRACE_ERROR stack dump:
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0xA0001BED
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x08480FD5
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x0847CE59
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x084DE0EF
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x0866D4D3
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x0845E407
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x0866D4A1
Trace 1745424 150706 MOD_NIL TRACE_ERROR number of messages in the external queue: 0
Trace 1745424 150706 MOD_NIL TRACE_ERROR messages in the external queue:
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE

观察TRACE,结合BUG描述,可以大致推知是MED的振铃问题,具体位置需要看catcher打印的栈信息。有些人遇到这个信息,就蒙了,感觉无从下手,我最早学习MTK时,也是无从下手,总觉得这样的信息没有ASSERT直观,一看就知道哪个文件哪一行出错。但ASSERT并不总是金牌,有一次遇到一个MEMCPY的ASSERT,调用的地方太多,照样需要查栈信息来寻找问题。当然写的代码,尽量在一些关键的地方加上ASSET,这会让人查找问题更轻松一些。当然有了这个信息,我们也可以大概推测出BUG的地方和原因的。要查找这个error的信息,必须要找到该软件版本对应的sym文件。该 sym和BIN文件同步生成,都位于BUILD文件下。该文件储存了ARM编绎器为软件中所有函数生成的地址。而出错时,MTK会保存当时栈中最近执行的 10条指令的地址,我们通过对这些地址的研究,就可以找到出错的地方。由于sym文件只保存了函数的地址,函数体里执行的代码地址没有列出,而一般打出的错误地址不会正好对应某个函数的地址,一般会会偏移N个单位。当然也有例外,我曾遇到一次出错,有一个地址正好和某函数地址对应。对于出错信息的地址与函数地址不对应的情况,我推测应该是函数体里的指令出错,所以应查找比该出错指令小且最近的函数。比如上例行 “Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x085569C9”,我们在sym文件中不应该查找0x085569C9对应的指令,因为一般会一个也找不到,我们要查找x085569C或者 x085569对就的函数,这时大约可以查到许多函数,找到与该出错地址最近且比该地址小的地址对应的函数,十个地址大约可以找到十个左右的函数,通过仔细排查,确认以下函数:

med_int_left_size
med_int_sfreeaud_main
aud_tts_play_req_hdlr
med_int_smalloc
med_set_ext_memory_pool
TCC_Task_Shell

如此可以大致确定问题出处和原因了,请教项目负责人,知道aud_tts_play_req_hdlr
是语音王的播放函数,在呼入时,有来电报号,该功能和QQ都是从MED栈上动态分配内存,由于QQ占用了一部内存,有呼入电话时,语音王分配内存失败,导到语音解码故障。问题一下解决。

0

MTK基于基站和小区的定位技术研究


我们的移动网络是通过一个个基站连接起来的,而一个基站又被划分成若干小区以方便查找。因为基站的位置相对比较固定,所以我们可以通过基站的编号cell_id和小区的编号LAC来定位地理位置。
具体的理论原理大概是这样的。根据GSM协议通信管理过程,我们大致可以知道呼叫建立的过程首先是用户定位,查找被呼叫人的位置信息。在GSM协议中,我们知道,用户的位置信息由MSC/VLR管理,MSC/VLR被划分成若干个较小的区域。假设定义被叫人的区域为LA,则每一个LA由一个位置区标识(LAI)识别,它们结构如下:
      LAI=MCC+MNC+LAC
·MCC=移动国家代码(被访问国家)
·MNC=移动网代码(服务的PLMN)
·LAC=位置区代码
比如中国的MCC:460(注意不是+86,+86是中国区号),移动的MNC:00,联通的MNC:01。LA的数据可以从VLR,下面让我们看看VLR中的数据。
VLR IMSI:        460  00  1234567890  
LAC:        262  15  0987
Data:          abc..      
MCRN:        358 50 456456
IMSI是全世界唯一的,可以唯一标志一张SIM卡,通俗一些说,就是SIM的身份证,所以IMSI号可以用来设计防盗追踪或者检查SIM是否被更换。其结构如下:
IMSI=MCC+MNC+MSIN
·MCC=移动网国家代码(三位)
·MNC=移动网代码(两位)
·MSIN=移动用户识别码(十位)
不过与本文无关,做不做过多介绍。
既然通信网络可以通过LAI 查找到被叫者,我们应该也可以使用LAI来确定被叫者的位置,既然LAI可以确定被叫者位置,当然也可以确定我们自己的位置,只不过这个位置信息不是特别的精确,与当地基站分布的密度有关。当然在这个结构中,国家代码和网络代码只能确定国家和使用的网络,我们能够使用的应该是LAC,LAC大概由基站信息和小区编号组成。
其具体算法就是首先获取本机所处位置的LAI数据,然后建立一张查找表,表中用基站编号和小区编号对应一个地理位置。通过查表就可以知道自己的地理位置。
读MTK代码时发现一个形如下的结构:
typedef struct {
    kal_uint8 mcc1; /* MCC DIGIT1*/
    kal_uint8 mcc2; /* MCC DIGIT2*/
    kal_uint8 mcc3; /* MCC DIGIT3*/
    kal_uint8 mnc1; /* MNC DIGIT1*/
    kal_uint8 mnc2; /* MNC DIGIT2*/
    kal_uint8 mnc3; /* MNC DIGIT3 Always 1111*/
    kal_uint8 la_code[2];
    kal_uint8 ra_code;  /* RA CODE*/
    kal_uint16 cell_id; /* CELL ID*/
} cell_info_struct;
该结构描述了基站的信息,恰好与GSM协议相对应,该结构可以通过发消息获。网上也能找来基站与地理位置的数据信息,看来实现起来应该无啥难度。该功能在没有GPS的情况下实现粗略定位,相较GPS可以几乎无成本的使用,对于喜欢猎奇又不愿意花钱的朋友,无异是一个不错的选择。但由于网上流行的基站数据并不完整,对于基站和地理位置的对应,如果能建立一个共享的WAP网站,发动大众的力量实现动态更新升级,那可就是利国利民,造福世人的好事。不过刚刚在网上看贴子听说,基站与地理位置对应的信息属于国家机密,不知是真是假,要是那样的话,我们也就只能自己做了玩玩,却是不能推广的。把自己推广到监狱里了,那可就得不偿失了,玩的朋友们注意了。

0

据说是最健康的作息时间表


有意思吧

 

7:30:起床。英国威斯敏斯特大学的研究人员发现,那些在早上5:22―7:21 分起床的人,其血液中有一种能引起心脏病的物质含量较高,因此,在7:21之后起床对身体健康更加有益。
打开台灯。“一醒来,就将灯打开,这样将会重新调整体内的生物钟,调整睡眠和醒来模式。”拉夫堡大学睡眠研究中心教授吉姆·霍恩说。
喝一杯水。水是身体内成千上万化学反应得以进行的必需物质。早上喝一杯清水,可以补充晚上的缺水状态。 
 
7:30―8:00:在早饭之前刷牙。“在早饭之前刷牙可以防止牙齿的腐蚀,因为刷牙之后,可以在牙齿外面涂上一层含氟的保护层。要么,就等早饭之后半小时再刷牙。”英国牙齿协会健康和安全研究人员戈登·沃特金斯说。 
 
8:00―8:30:吃早饭。“早饭必须吃,因为它可以帮助你维持血糖水平的稳定。”伦敦大学国王学院营养师凯文·威尔伦说。早饭可以吃燕麦粥等,这类食物具有较低的血糖指数。 
 
8:30―9:00:避免运动。来自布鲁奈尔大学的研究人员发现,在早晨进行锻炼的运动员更容易感染疾病,因为免疫系统在这个时间的功能最弱。步行上班。马萨诸塞州大学医学院的研究人员发现,每天走路的人,比那些久坐不运动的人患感冒病的几率低25%。 
 
9:30:开始一天中最困难的工作。纽约睡眠中心的研究人员发现,大部分人在每天醒来的一两个小时内头脑最清醒。 
 
10:30:让眼睛离开屏幕休息一下。如果你使用电脑工作,那么每工作一小时,就让眼睛休息3分钟。 
 
11:00:吃点水果。这是一种解决身体血糖下降的好方法。吃一个橙子或一些红色水果,这样做能同时补充体内的铁含量和维生素C含量。 
 
13:00:在面包上加一些豆类蔬菜。你需要一顿可口的午餐,并且能够缓慢地释放能量。“烘烤的豆类食品富含纤维素,番茄酱可以当作是蔬菜的一部分。”维伦博士说。 
 
14:30―15:30:午休一小会儿。雅典的一所大学研究发现,那些每天中午午休30分钟或更长时间,每周至少午休3次的人,因心脏病死亡的几率会下降37%。 
 
16:00:喝杯酸奶。这样做可以稳定血糖水平。在每天三餐之间喝些酸牛奶,有利于心脏健康。 
 
17:00―19:00:锻炼身体。根据体内的生物钟,这个时间是运动的最佳时间,舍菲尔德大学运动学医生瑞沃·尼克说。 
 
19:30:晚餐少吃点。晚饭吃太多,会引起血糖升高,并增加消化系统的负担,影响睡眠。晚饭应该多吃蔬菜,少吃富含卡路里和蛋白质的食物。吃饭时要细嚼慢咽。 
 
21:45:看会电视。这个时间看会儿电视放松一下,有助于睡眠,但要注意,尽量不要躺在床上看电视,这会影响睡眠质量。 
 
23:00:洗个热水澡。“体温的适当降低有助于放松和睡眠。”拉夫堡大学睡眠研究中心吉姆·霍恩教授说。 
 
23:30:上床睡觉。如果你早上7点30起床,现在入睡可以保证你享受8小时充足的睡眠。

0

Blizzard的MPQ文件格式搜索算法


真TMD神了。

作者:ghost2006
最近学习了一下Blizzard的MPQ文件格式,颇有一些心得,其中一条就是对HastTable的理解,很想写出来给大家共享,感谢Justin Olbrantz的文章《Inside MoPaQ》,大多认识来源于此。
先提一个简单的问题,如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?有一个方法最简单,老老实实从头查到尾,一个一个比较,直到找到为止,我想只要学过程序设计的人都能把这样一个程序作出来,但要是有程序员把这样的程序交给用户,我只
能用无语来评价,或许它真的能工作,但…也只能如此了。最合适的算法自然是使用HashTable(哈希表),先介绍介绍其中的基本知识,所谓Hash,一般是一个整数,通过某种算法,可以把一个字符串”压缩” 成一个整数,这个数称为Hash,当然,无论如何,一个32位整数是无法对应回一个字符串的,但在程序中,两个字符
串计算出的Hash值相等的可能非常 小,下面看看在MPQ中的Hash算法
unsigned long HashString(char *lpszFileName, unsigned long dwHashType)
{
unsigned char *key = (unsigned char *)lpszFileName;
unsigned long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE;
int ch;
while(*key != 0)
{
ch = toupper(*key++);
seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
}
return seed1;
}
Blizzard的这个算法是非常高效的,被称为”One-Way Hash”,举个例子,字符串”unitneutralacritter.grp”通过这个算法得到的结果是0xA26067F3。
是 不是把第一个算法改进一下,改成逐个比较字符串的Hash值就可以了呢,答案是,远远不够,要想得到最快的算法,就不能进行逐个的比较,通常是构造一个哈 希表(Hash Table)来解决问题,哈希表是一个大数组,这个数组的容量根据程序的要求来定义,例如1024,每一个Hash值通过取模运算 ()对应到数组中的一个位置,这样,只要比较这个字符串的哈希值对应的位置又没有被占用,就可以得到最后的结果了,想想这是什么速度?是的,是最快 的O(1),现在仔细看看这个算法吧
int GetHashTablePos(char *lpszString, SOMESTRUCTURE *lpTable, int nTableSize)
{
int nHash = HashString(lpszString), nHashPos = nHash % nTableSize;
if (lpTable[nHashPos].bExists && !strcmp(lpTable[nHashPos].pString, lpszString
))
return nHashPos;
else
return -1; //Error value
}
看 到此,我想大家都在想一个很严重的问题:”如果两个字符串在哈希表中对应的位置相同怎么办?”,毕竟一个数组容量是有限的,这种可能性很大。解决该问题的 方法很多,我首先想到的就是用”链表”,大学里学的数据结构教会了这个百试百灵的法宝,我遇到的很多算法都可以转化成链表来解决,只要在哈希表的每个 入口挂一个链表,保存所有对应的字符串就OK了。事情到此似乎有了完美的结局,如果是把问题独自交给我解决,此时我可能就要开始定义数据结构然后写代码了。然而Blizzard的程序员使用的方法则是更精妙的方法。基本原理就是:他们在哈希表中不是用一个哈希值而是用三个哈希值来校验字符串。中 国有句古话”再一再二不能再三再四”,看来Blizzard也深得此话的精髓,如果说两个不同的字符串经过一个哈希算法得到的入口点一致有可能,但用三个 不同的哈希算法算出的入口点都一致,那几乎可以肯定是不可能的事了,这个几率是:1888946593147858085478
4,大概是10的 22.3次方分之一,对一个游戏程序来说足够安全了。现在再回到数据结构上,Blizzard使用的哈希表没有使用链表,而采用”顺延”的方式来解决问题,看看这个算法:
int GetHashTablePos(char *lpszString, MPQHASHTABLE *lpTable, int nTableSize)
{
const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;
int nHash = HashString(lpszString, HASH_OFFSET);
int nHashA = HashString(lpszString, HASH_A);
int nHashB = HashString(lpszString, HASH_B);
int nHashStart = nHash % nTableSize, nHashPos = nHashStart;
while (lpTable[nHashPos].bExists)
{
if (lpTable[nHashPos].nHashA == nHashA && lpTable[nHashPos].nHashB == nHashB
)
return nHashPos;
else
nHashPos = (nHashPos + 1) % nTableSize;
if (nHashPos == nHashStart)
break;
}
return -1; //Error value
}
1. 计算出字符串的三个哈希值(一个用来确定位置,另外两个用来校验)
2. 察看哈希表中的这个位置
3. 哈希表中这个位置为空吗?如果为空,则肯定该字符串不存在,返回
4. 如果存在,则检查其他两个哈希值是否也匹配,如果匹配,则表示找到了该字符串,返

5. 移到下一个位置,如果已经越界,则表示没有找到,返回
6. 看看是不是又回到了原来的位置,如果是,则返回没找到
7. 回到3
怎么样,很简单的算法吧,但确实是天才的idea, 其实最优秀的算法往往是简单有效的算法.

3

穷人为什么穷?富人为什么富?


富人爱创业;穷人爱打工。
富人有投资意识;穷人则无。
富人看趋势做事;穷人看结果做事。
富人做事雷厉风行;穷人优柔寡断。
富人有博大的心胸;穷人心胸狭窄。
富人知道只有付出才有收获;穷人期待不劳而获。
富人做事前先看积极和光明;穷人光看消极和失败的一面。
富人不安分,有赚钱的野心;穷人吃饱、喝足、安逸”OK”。
富人字典里没有”不可能”;穷人字典里常常是”不可能”。
富人热爱工作,工作并快乐着;穷人则热爱休息,工作并痛苦着。
富人喜欢与人合作,为了壮大力量;穷人怕合作,怕吃眼前亏。
富人眼光远大,因此不会计较眼前一点得失;穷人目光短浅,斤斤计较眼前得失。
富人有感染力、有激情、精神抖擞;穷人大部分连自己都感染不了–世界末日。
富人做事珍惜时间,总觉时间不够用–创造财富;穷人总觉得时间富余,无所事事。
富人做事有永不服输的精神;穷人做事遇到挫败就放弃,很多穷人还没有做事就失败了。
富人从不觉得自己富,因为看到比自己更富的人,所以很谦虚;穷人稍一有钱就觉得是富人,于是趾高气昂。

Random Posts Recent Comments

  • 女友糖尿病害我蛀牙 Says:

    汗一个…...

  • Htj06 Says:

    zhenyouchuangyi...

  • 电商圈 Says:

    试图该怎么建立啊,,怎在程序中是吸纳...

  • edward Says:

    看得人心旷神怡,好文,情不自禁的顶一下...

  • Daniel Says:

    我也在处理这个问题,没有找到好的方法。我用了楼上兄弟的方法,还是可以的。不知道您找到好的方法了吗、我暂时楼上兄弟的方法。...

  • 卡,卡 Says:

    弱弱问一句:博主,你博客的模板这样设计pv高吗?...

  • 站长工具 Says:

    博主,兔年快乐!...

  • health Says:

    great post!!I hope I can read more in your website....

  • pdu Says:

    好博文,支持分享...

  • 站长工具 Says:

    博主的文章很不错,我是站长工具-站长精灵的作者,一款专业的SEO工具软件(可以帮您提高博客的流量),想跟您交换个链接,不知可否...

Tag Cloud

arm audio blog brew cache class debug flash google html j2me java javascript Joke linux lua mobile mtk php python ror ruby server shell stream unix web windows 优化 动态加载 女人 女生 平台 开发 手机 技术 流媒体 测试 漫画 生活 男人 男生 缓存 芯片