2005年6月12日星期日

申请免费人数上限为100的qq群

打开你的QQ,版本要高一点的。点QQ面版上面那“个人帐户”,进去后点右键-属性,地址(URL):
http://pay-client.qq.com/......8C46EFCF1844A1328167018

clientkey=”后面的那一传数字把他记住了。是QQ验证码

http://im.qq.com/cgi-bin/......F3C5C9C64650B6405D39B8F

点上面地址,在“uin=”后面跟你要申请群的QQ号码,也就是群的最高管理员的QQ号。“signature=”后面加上前面在个人帐户里叫你记住的QQ验证码。

改完了之后登陆地址,你可以看见“注册在线企业”这个叶子。

里面的信息随便写,主要是不能重复。企业帐号也是一样~申请第一群用你自己的QQ号码~申请第二的群的时候就不能在用同一个QQ号了,可以随便写个QQ号,因为不需要验证,而申请的群也是属于你(“uin=”QQ)的。

Friends主题曲歌词

I'll Be There For You 为你,我会在那里

(Friends主题曲)  

So no one told you 没有人曾告诉过你,

  Life was gonna be this way 生活就是这样的。

  Your job's a joke, 你的事业不成功,

  You're broke 一文不名,

  Your love life's D.O.A, 爱情没有结果。

  It's like you're always stuck in second gear 就像开车的时候卡在二档,无法迅速前进。

  Oh when it hasn't been your day 当这一天、这一周、这一个月,

  Your week, your month, or even your year 甚至这一年你都很倒霉。

  But I'll be there for you 但是我会在那里支持你,

  When the rain starts to pour 即使大雨倾盆。

  I'll be there for you 我会在那里支持你,

  Like I've been there before 就像我以前一样。

  I'll be there for you 我会在那里支持你,

  'Cause you're there for me, too 因为你也会是我坚强的后盾。

  You're still in bed at ten 早上10点你还赖在床上,

  And work began at eight 即使8点就应该去上班。

      You've burned your breakfast 你把早餐弄煳了,

  So far things are going great 但直到现在一切还不错。

  Your mother told you 你妈妈告诉你,

  There'd be days like these 生活有时候会像这样不如意。

  But she didn't tell you when the world 但是她没有告诉你,

  Has brought you down to your knees 你也会有心灰意冷的时候。

  But I'll be there for you 但是我会在那里支持你,

  When the rain starts to pour 即使大雨倾盆。

  I'll be there for you 我会在那里支持你,

  Like I've been there before 就像我以前一样。

  I'll be there for you 我会在那里支持你,

  'Cause you're there for me, too 因为你也会是我坚强的后盾。

  No one could ever know me 没有人能够了解我,

  No one could ever see me 没有人可以看透我,

  Seems you're the only one who knows 似乎只有你知道

  What it's like to be me 我该是什么样的人。

  Someone to face the day with 我会和你一起面对明天,

  Make it through all the rest with 一起坚持到底,

  Someone I'll always laugh with 一起纵情欢笑。

  Even at my worst I'm best with you 无论情况有多糟,我们都是最合拍的一对。

  It's like you're always stuck in second gear 就像开车的时候卡在二档,无法迅速前进。

  Oh when it hasn't been your day 当这一天、这一周、这一个月,

  Your week, your month, or even your year 甚至这一年你都很倒霉。   

  But I'll be there for you 但是我会在那里支持你,

  When the rain starts to pour 即使大雨倾盆。

  I'll be there for you 我会在那里支持你,

  Like I've been there before 就像我以前一样。

  I'll be there for you 我会在那里支持你,

  'Cause you're there for me, too 因为你也会是我坚强的后盾。

申请msn.com等其他hotmail油箱的地址

@MSN.com: https://accountservices.passport.net/reg.srf?id=963&sl=1&lc=2052
Hotmail.it: https://accountservices.passport.net/reg.srf?id=2&vv=30&sl=1&lc=1040
@Hotmail.de: https://accountservices.passport.net/reg.srf?id=2&vv=30&sl=1&lc=1031
@Hotmail.fr: https://accountservices.passport.net/reg.srf?id=2&vv=30&sl=1&lc=1036
@Hotmail.co.uk: https://accountservices.passport.net/reg.srf?id=2&sl=1&lc=2057
@Hotmail.co.jp: https://accountservices.passport.net/reg.srf?id=2&vv=30&sl=1&lc=1041

2005年6月10日星期五

大碗

一定要选最好的袭击目标,雇塔里班恐怖设计大师,什么东京呀横滨呀京都呀,能炸的全都给他炸了,干就干惊天动地的事业,用中国最新型的轰炸机,个头比航母还大三倍,导弹呀,核弹头呀,能挂的全给他挂上,原子弹最少也得挂百八十个,你要是挂普通炸弹,你都不好意思跟人家打招呼,轰炸机机长用阿富汗人,头上蒙一头巾,上面加一头圈,特野蛮的那种,到了日本上空,甭管有事没事,他都得跟人家日本人说:"May I help you sir"一口地道的阿拉伯腔,倍有面子,你说这样的轰炸机一次能炸死多少日本人?我觉的怎么也得十来万吧,十来万?那还得是当场死亡,到医院死亡的还不算,你还别嫌多,还不打折,就一字:
狠!你得揣摸日本人的心理,经受过原子弹打击的日本人,根本就不在乎再死个十万八万的,什么叫日本人你知道吗,真正的日本人就是不挨炸难受,炸了倒痛快!所以我们对日恐怖主义的口号就是"不求最准,但求最狠!"

今天

今天把网站首页改了改,还添加了一个ally页面.越来越觉得自己浮躁了,n多天没有看过内核了,网站美工上倒是花了些时间。但是心里那个遥远的梦依然依稀的存在--写一个属于自己的内核,不知道从何开始,也不清楚最后自己要做个什么东西出来,仅仅是一个目标树立在前方,真的有些害怕自己会被这个目标给荒废了,太遥远了,遥不可及。看见别人用汇编都能写出一个内核来,看看自己,什么也不会,所以最想做的事情就是静下心来,好好做点自己喜欢的事情。

论文再过几天就要交了,不管怎样过了这段时间可以稍微放松一下了。昨天和堂弟视频聊天的时候,老爹刚好也在他家,他变得那样的瘦,心突然一阵揪痛,然后眼泪已经在眼眶里打转了,晚上妹妹发端信说是因为发愁我工作的事情。我能理解父亲的想法,把我安排到银行去之后,父母也就都该放心了,我现在真的很后悔,唯一能做的就是等到过年,我立马把现在的工作辞掉,然后回家。我的父母是在太爱我了,我应该回去,并且我也愿意回去。

2005年6月8日星期三

奥斯卡最佳影片,你看过多少

1届《翼》
2届《百老汇的旋律》
3届《西线无战事》
4届《壮志千秋》
5届《大饭店》
6届《乱世春秋》
7届《一夜风流》
8届《叛舰喋血记》
9届《歌舞大王齐格菲》
10届《左拉传》
11届《浮生如梦》
12届《乱世佳人》又名《飘》
13届《蝴蝶梦》
14届《青山翠谷》
15届《米尼弗夫人》又名《忠勇之家》
16届《卡萨布兰卡》又名《北非谍影》
17届《与我同行》
18届《失去的周末》
19届《黄金时代》
20届《君子协定》
21届《哈姆雷特》
22届《当代奸雄》
23届《彗星美人》
24届《一个美国人在巴黎》
25届《戏中之王》
26届《永垂不朽》
27届《在江边》
28届《马蒂》
29届《环球旅行八十天》
30届《桂河大桥》
31届《琪琪》
32届《宾虚》
33届《公寓》
34届《西区故事》
35届《阿拉伯的劳伦斯》
36届《汤姆.琼斯》
37届《窈窕淑女》
38届《音乐之声》
39届《永远走红的人》
40届《炎热的夜晚》
41届《奥利弗!》
42届《午夜牛郎》
43届《巴顿将军》
44届《法国贩毒网》
45届《教父》
46届《骗》
47届《教父(续集)》
48届《飞越疯人院》
49届《洛奇》
50届《安妮.霍尔》
51届《猎鹿人》
52届《克莱默夫妇》
53届《普通人》
54届《火的战车》
55届《甘地》
56届《母女情深》
57届《莫扎特》
58届《走出非洲》
59届《野战排》
60届《末代皇帝》
61届《雨人》
62届《为戴茜小姐开车》
63届《与狼共舞》
64届《沉默的羔羊》
65届《杀无赦》
66届《辛德勒的名单》
67届《阿甘正传》又名《福雷斯特.冈普》
68届《勇敢的心》又名《惊世未了缘》
69届《英国病人》又名《英伦情人》
70届 《泰坦尼克号》又名《铁达尼号》
71届 《沙翁情史》
72届 《美国丽人》
73届 《角斗士》
74届 《美丽心灵》
75届 《芝加哥》
76届 《指环王3》
77届 《百万宝贝》

2005年6月7日星期二

纪念normandy登陆胜利61周年

61年前的昨天晚上到今天白天,一场有史以来最大规模的海上登陆战爆发了,同时,这一场战争也是二战的转折点,在盟军总司令艾森豪威尔的指挥下数以百万计的步兵,伞兵,装甲部队横跨英吉利海峡,在法国北部的normandy登陆,这一天也就是所谓的D-Day。当然指挥整个登陆行动的除了艾克之外还有很多著名的将军,蒙哥马利,巴顿,朱可夫等等,不管盟军内部的分歧有多大,最终的胜利还是他们的。想知道战争的惨烈就去看《band of brothers》吧。

所有的这些著名的将军当中我最欣赏的就是艾克了。最近在看他的传记~~

2005年6月6日星期一

后天就回去了

后天晚上回去学校,出来几个月了,这几个月我变化实在太大了,从一开始的一腔热血,到现在的心灰意冷,一切转变的是那么得快,出来才知道我是一个什么样子 的人,我是一个很不愿意守规矩的人,公司的规矩可能都被我破过了,包括在这里写blog,现在我就打算回去当一个银行的小职员,真的有些后悔当初没有听父 母的劝告,在外面一个人寂寞倒无所谓,苦和累也无所谓,关键是我在这里工作的很不开心,不知道为什么,觉得自己还没有长大,应该再返回到校园中去,对社会 中的一些做法和一些不成文的规矩很是不能理解。这个估计是我最应该适应的东西,也是我最不想适应的东西,慢慢的我会被磨平的。

还有二十多天的校园生活,呵呵,可能从此我十多年的学生生涯就要结束了,真有些怀念,每天可以无忧无虑的,什么也不用操心,到了这边什么都要自己打理,发 现生活真的好累啊。昨天邻居发短信说家里又惦记我了,看得我真的不是各滋味。现在周围所发生的一切都在促使我做出回家的决定。渐渐的我也发现还是回去好, 最起码可以在父母身边,工作的应该会比这里顺心一些。

高中同学又有结婚的了,好快呀,想想也应该了,时时会想起如果有一天接到她的电话,说她要结婚了之类的,我都不知道应该怎么应付。2年半了,她也应该有男 朋友了吧,女孩到了这个年龄都应该考虑个人问题了。到时候,我会微笑着参加她的婚礼的。

2005年6月3日星期五

qq群人数上限减少?

在水木上看到说是国家****部为了避免不良信息散播,规定qq一个群的人数上限在20,也就是新建立的qq群只能是这个数字,以前建立的现在还是100 个,不知道过些日子会不会也降为20,如果那样,我的那几个qq群就惨了。不敢多说了,现在什么都是敏感话题,让我想起了秦始皇 ~~~~~~~~~~~~~~

央视非常6+1录制现场看到的丑陋一幕

5.27号,也就是4天前,不少来自天津大学的同学,还有南开的同学,一起有幸参加了中央电视台非常6+1的录制。录制中却看到这样一幕丑陋的场景,不吐不快!
当时到了现场连线砸金蛋环节。因为前几次连线,观众都当即在电话那边听出了李咏的声音,于是李咏决定改变一下打电话的方式。这一次,对方是一名警察,对话大致如下:
警察:喂
李咏:喂,您好!是*警官吗?
警察:呃,你是?
李咏:您好!我们这里发生了一起交通事故,有人被摩托车撞了。
警察:这我不管。
李咏:您好,您能过来处理一下吗?
警察:...我不在上班
李咏:您看能怎么给解决一下吗?
警察:......
李咏:喂?喂?
(电话那边已经挂断...)
全场观众哗然。李咏很无奈看了看导播席。导播再一次拨通该警官的电话。
警察:...
李咏:喂,您好!是***警官吗?(这次说了全名)
警察:呃,我不是。
李咏:您是机主吗?
警察:不是。
李咏:您好!我是中央电视台非常6+1节目主持人李咏。您...(被对方打断)
警察:哦?李咏啊!您好您好您好!(态度绝对540度大转弯)
李咏:您是***警官吗?
警察:我是我是我是!
(全场观众,包括场内央视工作人员,再次全部哗然!)
李咏:刚才不好意思跟您开了个玩笑!非常感谢您发短信参与我们的节目!
...........

中国某些警察的丑恶嘴脸完全暴露出来了!不知道这一期节目播放的时候,上述场景会不会完整展现给全国观众?!大家可以关注一下,这期的四个嘉宾都是小朋友(8岁到13岁),很可爱!另外,这期的观众很多是天大和南开的同学,还有我们的同学同李咏的对话!!
只是上面丑陋的一幕,实在是让人恶心,值得让整个社会反思!

2005年5月31日星期二

神州为什么不好?

1。神舟为什么不好?因为其它品牌的厂家都说不好,不信,你去问他们。

2。神舟为什么不好?因为其它品牌的经销商都说不好,不信,你去问他们。

3。神舟为什么不好?因为其它用其它品牌的人都说不好,买同样配置的一万多的IBM的人,能承认同样配置的神舟好吗?不信,你去问他们。

4。神舟为什么不好?因为那么多人跳出来骂神舟不好,IT媒体也不敢说好。毕竟本本就便宜,广告费当然更少。不信,你去看看他们的文章。

5。神舟为什么不好?因为想买神舟笔记本的人一打听,听说神舟笔记本不好。其它他们除了觉得LOGO难看做工一般之外,也不知道还有什么不好。不信,你去问他们。

6。神舟为什么不好?因为神舟用户说不好!为什么?因为大家都说神舟不好,神舟的用户觉得特没面子!不信,你去问他们!

7。总之,神舟为什么不好?虽然价格便宜、性能强劲、保修期长,但仍然不好,就是不好,不好的还能好?大家都说不好,它就是不好!不好,就不好!!! ----奇怪的国民心态

2005年5月28日星期六

哈哈,考试完了

nnd第一次靠用英语写的技术类的试卷,题目比较简单,但是要求抬高了,居然要对90%才可以及格,估计没戏,下次再靠把,重修!!!!!!!!!!!!!

2005年5月27日星期五

新搬家

今天把blog放到了自己的地盘上面,嗬嗬,还不错,速度快了很多,不过发布速度狂慢,离我得那个免费的空间查多了,不过那个空间是国外的,访问速度在国内慢了一些,谁知道,兄弟说是比以前的免费空间访问还慢,一个地方一个样,不管了,我访问快就可以了,忙着毕业设计,烦死了~~~

2005年5月26日星期四

历史上最恐怖的12个英文单词 (老外也受不了!)

历史上最恐怖的12个英文单词 (老外也受不了!)

1,honorificabilitudinitatibus
这个字是由27个字母组成的。出现在大文豪莎士比亚的剧本「空爱一场」love's labou
's lost里,意思是「不胜光荣」。


2. antidisestablishmentarianism
这个字是由28个字母组成的。根据范克和华格若尔斯编的「英语新标准辞」里面的解释
,这个字的意思是「反对教会与国家分开学说」。它曾被英国首相格来斯顿william ew
art gladstone,1809-1898引述过一次。

3. floccinaucinihilipipification
这个字是由29个字母组成的。「牛津英文辞典」里就有这个字,意思是「把某事的价值
加以抹杀的行为或习惯」。

4. supercalifragilisticexpiadocious
这个字是由34个字母组成的。出现在一部名叫mary poppins的电影里,意思是「好」。

5. hepaticocholecystostcholecystntenterostomy
这个字是由40个字母组成的。出现在高德编的「医学辞典」里,为一个外科术语,亦即
在胆囊与胆管之间或肠子与胆囊之间接人工管子的手术。

6. pneumonoultramicyoscpicailicovolcanoconiosis
这个字是由45个字母组成的。出现在韦氏辞典第八版的版本中意思是「吸入硅酸盐细末
或石英岩灰而形成的肺尘埃沉着病」。矿工特别容易得这种病。

7. antipericatametaanaparcircumvolutiorectumgustpoops of the coprofied
这个字是由50个字母组成的。有个图书馆的书架上,陈列着法国作家拉伯雷着的「葛甘
塔和潘特古」故事系列。其中有一本,书名就是这个长长的英文字。

8. osseocaynisanguineoviscericartilagininervomedullary
这个字是由51个字母组成的。它是人体构造一术语,曾出现在英国作家皮考克thomas l
ovepeacock,1785-1866那本名叫headlong hall的小说中。

9. aequeosalinocalcalinoceraceoaluminosocupreovitriolie
这个字是由52个字母组成的。它是英国医学作者爱德华.史特罗哲dr edward strother,

1675-1737创造的字,专用来形容英格兰格洛斯特夏布瑞斯陀这个地方的矿泉水成分。

10.bababadalgharaghtakamminarronnkonnbronntonnerronntuonnthunntrovarrhounawnsk
awntoohoohoordenenthurnuk
这个字是由100个字母组成的。就出现在爱尔兰作家乔埃斯james joyce,1882-1942作品
finneganswake的扉页,象征,代表亚当和夏娃的堕落。

11.lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitoka
takechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolag
oiosiraiosiraiobaphetraganopterygon
这个字是由182个字母组成的。它是从希腊字英译过来的英文字,源出自希腊喜剧作家亚

里斯多芬尼斯aristophanes,448-385的剧本the ecclesiazusae中。它是指由剩余的菜和

牛肉函煮而成的辣味食物。

12.
1913个字母,“色氨酸合成酶a蛋白质”(一种含有267种氨基酸酶)的化学名:
methionylglutaminylarginyltyrosylglutamylserylleucylphenylalanylalanylglutamin
ylleucyllysylglutamylarginyllysylglutamylglycylalanylphenylalanylvalylprolyphe
nylalanylvalythreonylleucylglycylaspartylprolylglycylisoleucylglutamylglutamin
ylserylleucyllysylisoleucylaspartylthreonylleucylisoleucylglutamylalanylglycyl
alanylasparthlalanylleucylglutamylleucylglycylisoleucylprolylphenylalanylseryl
aspartylprolylleucylalanylaspartylglycylprolylthreonylisoleucylglutaminylaspfr
aginylalanylthreonylleucylarfinylalanylphenylalanylalanylalanylglycylvalythreo
nylprolylalanylglutaminylcysteinylphenylalanylglutamylmethionylleucylalanylleu
oylisoleucylarginylglutaminyllysyhistidylprolylthreonylisoleucylprolylisoleucy
lglycylleucylmethionyltyrosylalanylasparaginylleucylvalylphenylalanylasparagin
yllysyglycylisoleucylaspartylglutamylphenylalanylthrosylalanylglutaminylcystei
nylglutamyllysylvalylglycylvalylaspartylserylvalylleucylvalylalnylaspartylvaly
lprolylvalylglutaminylglutamylserylalanylprolylphenylalanylarginylglutaminylal
anylalanylleucylarginylhistidylasparaginyvalylalanylprolylisoleucylprolylisoleucylpheny
lalanylisoleucylphenylalanylisoleucylcysteinylprolylprolylaspartylalanylaspart
ylaspartylaspartylleucylleucylarginylglutaminylisoleucylalanylseryltyrosylglyc
ylarginylglycyltyrosylthreonyltyrosylleucylleucylserylarginylalanylglycylvalyl
threonylglycylalanylglutamylasparainylarginylalanylalanylleucylprolylleucylasp
araginylhistidylleucylvalylalanyllysylleucyllysylglutamyltyrosylasparaginylala
nylalanylprolylprolylleucylglutaminylglgycylphenylalanylglycylisoleucylserylal
anylprolylaspartylglutaminylvalyllysylalanylalanylisoleucylaspartylalanylglycy
lalanylalanylglycylalanylisoleucylserylglycylserylalanylisoleucylvalyllysyliso
ieucylisoleucylglutamylglutaminylhistidylasparaginylisoleucylglutamylprolylglu
tamyllysylmethionylleucylalanylalanylleucyllysylvalylphenylalanylcalylglutamin
ylprolylmethionlysylalanylalanylthreonylarginylserine

2005年5月25日星期三

星球大战编年史

星球大战编年史

  帝国历前250000-52000年

  旧共和国形成,杰迪武士首次出现,一些沉迷原力黑暗面的杰迪武士,被共和国驱逐,在流浪过程中,他们奴役了一个酷似人类的种族——西斯(Sith),并成了西斯族的首领,统治者称自己为西斯大君(Lordsof the Sith),自此数千年间,西斯帝国茁壮成长,这些黑暗杰迪武士把西斯族训练成了致命的战士。

  帝国历前52000-5000年

  玛卡·瑞格诺斯(Marka Ragnos)打败塞蒙斯(Simus)成为西斯帝国的统治者。

  帝国历前5000年

  藉由杰迪武士之助,黛塔女皇(Empress Teta)统一了康诺斯星系(Koros System)的七个世界,日后康诺斯星系更名为黛塔星系,而此一事件后称为统一战争(Vnification Wars)。

  帝国历前5000-4990年

  玛卡·瑞格斯死亡,那嘉·沙度(Naga Sadow)继位成为西斯大君。超空间探险家盖夫(Gav)及丘丽·达拉冈(Jori Daragon)兄妹失陷在强大的西斯帝国。这件事导致旧共和国与西斯帝国间的大战。旧共和国与黛塔女皇联手,凭借着杰迪武士与星际舰队,击败了那嘉·沙度,这个西斯大君败退到宇宙的远端,最后落脚于雅汶四号卫星(Yavin 4),而此一事件后称为超空间大战(Great Hyperspace Wars)。

  帝国历前4400年

  精通黑暗原力的博里顿·纳德(Freedon Nadd),控制了昂德朗星球(Onderon)的易希斯城(Iziz),并将反对者逐出野外被野兽吞噬,这些反对者后来驯服了这些野兽。组织起来对易希斯城展开长达四百年的反击,此事件后称为野兽战争(Beast Wars)。

  帝国历前4398年

  博里顿·纳德死亡,但野兽战争持续中。

  帝国历前4002年

  旧共和国发现了昂德朗星,并指派杰迪大师阿卡(Jedi Master Arca)监控。

  帝国历前4002-4000年

  阿卡指派他的杰迪学徒们,以乌立克·奎卓马(Ulic Qel-Droma)为首去平定野兽战争,但受到博里顿·纳德信徒—阿玛诺拉皇后(Queen Amanoa)的反抗,杰迪大师阿卡亲自出马,阿玛诺拉皇后败亡,继位的葛利亚公主(Prince Galia)与野兽之王欧朗·奇拉(Beast Lord Oron Kira)结为连理,野兽战争终告落幕。

  帝国历前4000-3998年

  博里顿·纳德的信徒偷走了纳德的尸体,并由受纳德阴魂控的欧敏国王(King Ommin)领军,他是葛利亚公主之父,绑架阿卡师傅,并进攻希斯城。奎卓马和诺米·阳骑者(Nomi Sunrider),率领一些杰迪武士,杀死了欧敏国王,并确保博里顿·纳德、阿玛诺拉皇后与欧敏的尸体不再受到利用。但一对黑暗原力的信仰者—阿力玛(Aleema)与萨多·凯多(Sstal Keto)已从欧敏国王那里得到了西斯的护身符,并从纳德的阴魂那里得到了西斯之剑(Sith Swords),后来藉此创立了凯瑞斯教派(Sect Krath)。

  帝国历前3997-3996年

  阿力玛与萨朵·凯多藉着西斯遗物这力控制了黛塔星系,一个堕落的杰迪武士艾克萨·亢(Exar Kun)受了西斯阴魂那嘉·沙度的引诱,向黑暗面靠拢,并诱使包括乌立克·奎卓马在内的许多杰迪武士加入了黑暗面。凯瑞斯教派企图恢复古西斯帝国,因此和几百名杰迪武士爆发了大战,后成为西斯战争(Sith War),许多著名杰迪,包含阿卡大师都死于此役。战争结束后,阿力玛与萨朵·凯多反而被其同盟所杀。诺米·阳骑者将乌立克·奎卓马拉回了光明面,并再组成杰迪大军,亢在杰迪大军攻击他在雅汶四号卫星的基地时被消灭了,但是他的灵魂仍困在雅汶四号上几千年。

  帝国历前896年

  绝地大师尤达(Yoda)出生。

  帝国历前200年

  武技族乔巴卡(Chewbacca)在卡须克行星(Kashyyyk)上诞生。

  帝国历前92年

  绝地大师魁刚·金(Qui-Gon Jinn)出生。

  帝国历前60年

  欧比旺·克诺比(Obi-Wan Kenobi)出生。

  帝国历前48年

  蒙·茉斯玛(Mon Mothma)在千瑞拉星球(Chandrila)出生,后来成为参议员与反抗军之领袖。

  帝国历前46年

  阿米达拉女王(Queen Amidala)出生。

  帝国历前41年

  阿纳金·斯凯沃克(Anakin Skywalker)出生。

  EPISODE I

  帝国历前32年

  纳布星(Naboo)发生达斯·西底亚斯(Darth Sidious)幕后主谋的贸易联盟(Trade Federation)暴力封锁事件,年轻阿米达拉女王与冈根族(Gungans)合力抵抗。纳布星帕尔普廷(Palpatine)议员当选最高议长。绝地大师魁刚·金丧生于达斯·摩尔(Darth Maul)双刃光剑之下,欧比旺为其师复仇,将达斯·摩尔拦腰斩断。阿纳金·斯凯沃克师从欧比旺,开始其绝地学徒之路。

  帝国历前29年

  韩·索罗(Han Solo)出生于科瑞利安星系(Corellian)。

  EPISODE II

  帝国历前22年

  贸易联盟和旧共和之间矛盾日益激化,共和国一改往日和平主张,使用克隆军队开始建立新次序。史称克隆人战争。同年,年轻的阿纳金·斯凯沃克与原纳布星女王阿米达拉结合。

  EPISODE III

  帝国历前22-20年

  阿纳金·斯凯沃克被引诱入原力的黑暗面,成为达斯·维德(Darth Vader)。

  帝国历前18-4年

  反抗军与帝国的对抗(Rebellion Against the Empire)。

  帝国历前18年

  帝国暴虐,一些参议员开始同盟对抗帝国,即为后来之反抗军同盟(Rebel Alliance)。而绝地武士在达斯·维德的追杀下,逐渐雕零,欧比旺隐居塔图因星球(Tatooine),尤达避居达格巴星(Dagobah)。

  卢克·斯凯沃克(Luke Skywalker)和他的双胞胎姐妹莱阿(Leia)出生。

  帝国历前10年

  年轻的韩·索罗成为一个顶尖的飞行员和走私者,并从兰多·卡雷西亚(Lando Calrissian)手中赢得千年猎鹰号(Millennium Falcon)。

  帝国历前10-5年

  机械人伙伴C-3PO和R2-D2在不同的主人手下牵扯入一连串惊险的冒险。

  帝国历前5-2年

  韩·索罗和他的大副乔巴卡经历企业星区(Corporate Sector)中的一连串探险。

  帝国历前1-元年

  反抗军人员凯·卡特龙(Kyle Katarn)偷取帝国的死星计划。

  EPISODE IV

  帝国元年

  皇帝帕尔普廷(Emperor Palpatine)解散议会,并宣布『新秩序』(New Order)。卢克·斯凯沃克、本·克诺比、韩·索罗及乔巴卡救出莱阿公主,面对达斯·维德,并在雅汶战役中协助摧毁死星。但在这之前死星已经摧毁莉亚的家乡行星奥德兰。克诺比让维德将他击倒,并成为原力的一部分。

  帝国元年后期

  卢克和莱阿出发寻找内藏有强烈原力的凯伯水晶(Kaiburr crystal),并遭遇达斯·维德。反抗军撤离雅汶四号上的基地,并开始寻找适合的地点,最后将新基地设在行星霍斯(Hoth)上。反抗军英雄在欧德曼特尔(Ord Mantell)遭遇奖金猎人(Bounty Hunter)。

  EPISODE V

  帝国3年

  帝国大反击,在霍斯战役中击溃反抗军。卢克·斯凯沃克到达格巴行星接受尤达大师的训练。莱阿、韩、乔巴卡和C-3PO在贝斯平(Bespin)的贝斯平云城(Cloud City)被达斯·维德捉
住当饵,引诱年轻的斯凯沃克来救他们。韩被碳化冷冻,并交由奖金猎人波巴·菲特(Boba Fett)运送给犯罪头子甲巴(Jabba the Hutt)。卢克面对维德,维德宣称自己是卢克的父亲。卢克损失一只手和他的光剑,但被莱阿和兰多救了出来。

  帝国3年 中期

  年轻的辛黛儿·托瓦尼(Cindel Towani)和她的家人在恩朵卫星(Endor)上坠机。他们得到一个部落的土生艾沃克族(Ewok)的帮助。

  帝国3年 后期

  黑阳犯罪组织(Black Sun)的领袖西瑟王子(Prince Xizor)计划要消灭达斯·维德,并成为皇帝的副手。他计划藉由暗杀卢克·斯凯沃克达成他的目标,但最后却是他自己被杀。卢克建造了他自己的光剑。

  EPISODE VI

  帝国4年

  卢克·斯凯沃克摧毁甲巴并救出韩·索罗。回到达格巴星,他后发现尤达的生命力量渐渐衰竭。在那里卢克得知维德真的是他的父亲,而莱阿则是他的姐妹。在恩多战役中,韩及莱阿在恩朵卫星上藉由艾沃克族的协助,摧毁用以保护死星 II的护盾产生器。而兰多和驾驶X翼战斗机的魏吉·安地列斯(Wedge Antilles)则合力摧毁这颗更新更强的死星 II。维德从皇帝的手中救出他的儿子卢克,但却死于摧毁帕尔普廷皇帝的过程。

  黑暗帝国历4年

  在摧毁死星 II杀死黑暗皇帝后几天,反抗军必须在巴库拉(Bakula)的停战协议中和帝国武力联手击退虚鲁克族(Ssi-Ruuk)。波巴·菲特从沙虫口中逃生,与丹卡(Dengar)合伙继续赏金猎人生涯,并对韩之家族,持续展开报复。

  新人物凯·卡特龙成为一位杰迪武士。

  兰多·卡雷西亚重新掌管贝斯平云城。

  黑暗帝国历4年5月

  反叛军浪子中队(Rogue Squadron)的X翼战斗机飞行员开始有计划的以行星为单位夺回整个银河。

  黑暗帝国历5年

  包括自称是皇帝之子的区勒斯(Trioculus)在内,许多人尝试夺取受创的帝国领导地位。年少的杰迪王子肯(Jedi Prince Ken)从与世隔绝的地下杰迪失落之城(Lost City of the Jedi)现身与黑暗皇帝真正的儿子—采可洛帕斯(Triclops)阻挠了区勒斯的阴谋。

  甲巴的父亲—赫特族·洛霸(Zorba the Hutt),从兰多手上窃得贝斯平云城,并多次计划要消灭杀子仇人—莱阿公主。

  黑暗帝国历6年5月-7年5月

  在魏吉·安德列斯的率领下,浪子中队解放帝都行星克鲁斯根(Coruscant),并为了夺取能够为全银河人治病的巴克他(Bacta)星球和前帝国情报局长作战,后称为巴克他战争。

  黑暗帝国历8年

  韩·索罗和莱阿·奥嘎纳公主结婚,但这是在韩“绑架”莱阿到行星戴斯摩(Dathomir)之后的事情。

  黑暗帝国历9年

  帝国元帅史隆(Imperial Grand Admiral Thrawn )从银河的边疆回来,并籍由一个疯狂的黑暗杰迪武士复制人杰洛司(Joruus C'Baoth)的帮助,重整帝国武力,并成为新共和国的主要威胁。新共和国最后击退了史隆元帅,史隆元帅最后被自己的保镖鲁克(Rukh)杀害。

  莱阿·奥嘎纳·索罗产下一对双胞胎,婕娜(Jaina)和杰森(Jacen)。

  黑暗帝国历10年

  皇帝的复制人(Emperor's Clone)使用银河之枪(Galaxy Gun )和灭世者战舰(World Devastators)等超级武器将混乱带给新共和国,而且卢克·斯凯沃克差点堕入黑暗面。帝国短暂的夺回首都。后来韩·索罗杀掉了皇帝的复制人,但其阴魂附入一个杰迪武士恩帕多佳由斯·布蓝德(Empatojayos Brand)的身上,直到布蓝德死后,这一代暴君才真正的消失于这个世界。

  莱阿生下第三个孩子,以他外公的名字命名为阿纳金·索罗(Anakin Solo)。

  黑暗帝国历11年

  卢克在雅汶四号卫星上建立杰迪训练学校(Jedi Training Academy ),但差点被苏醒的黑暗大君艾克萨·亢的灵魂杀死。魏吉·安地列斯升任大将军。

  黑暗帝国历12-13年

  在卢克被困在老旧的帝国战斗要塞、对抗一个赫特族的犯罪头子、以及处理一种致命传染病神秘蔓延之时,他和一度是杰迪武士的卡莉丝塔(Callista)有一段史诗般但没有结果的恋情。

  黑暗帝国历14年

  莱阿和韩的三个孩子被一个曾是帝国官员,创设“帝国重生”运动(Empire Reborn)的海瑟(Hethrir)绑架。

  黑暗帝国历15年

  卢克·斯凯沃克学得隐形物体(cloak objects)的能力。

  黑暗帝国历16-17年

  新共和国受到残忍恐怖的种族夜维森族(Yevethan)的威胁。韩·索罗也落入夜维森族之手,幸好乔巴卡组成一支武技族救援队,救回了韩。

  卢克寻找有关他母亲的线索。

  黑暗帝国历17年

  曾是杰迪学院学生的库勒(Kueller)受到黑暗面的诱惑,放置炸弹威胁新共和国政府。

  黑暗帝国历18年

  韩的堂兄,和他长的很像的施瑞肯·撒苏洛(Thracken Sal-Solo),领导一支残酷的革命军意图夺取科瑞利安星系的领导权。索罗的孩子们在千钧一发之时停止了撒苏洛炸毁星球的武器,阻止了他的阴谋。

  黑暗帝国历19年

  新共和国赫然发现史隆元帅竟然还活着,虽然后来再度击败了史隆,但史隆的存在仍是整个宇宙和平的最大威胁。

  黑暗帝国历22-23年

  阿纳金·索罗开始在杰迪学院学习。

  黑暗帝国历23-24年

  玛拉·婕黛(Mara Jade)原本是效忠于黑暗皇帝的杀手——人称皇帝之手(Hand of the Emperor)。在皇帝死后,顿失生活目标,因此以卢克为敌,认为卢克是破坏她生活的凶手,经过一番流浪,她栖身于塔龙·卡帝的走私组织(Talon Karrde's smuggling organization),在史隆元帅与共和国的对抗期间,玛拉再次与卢克相遇,而在逃离米克星(Myrkr)的事件中,两人冰释前嫌,后来玛拉加入了 反抗军,并曾短期的加入杰迪学院,并随着向光明原力的方向靠拢,她与卢克产生爱情,在第二帝国完全灭亡之后,两人终于结为连理。

  婕黛和杰森·索罗姐弟在杰迪学院接受训练,这群杰迪学院的年轻学徒,帮助平定了邪恶的第二帝国(Second Emperium),并与纠缠不清的波巴·菲特一再周旋,他们的杰迪之路充满着艰辛,但他们也是新杰迪武士传说的开始。

本文参考资料:
《Tale of the Jedi Comics》
《Star Wars Encyclopedia》
《The Essential Chronology》
《BTM CDROM》

昨天是我的生日

长这么大第一次记得自己的生日,呵呵,老早就惦记了,可是和平常一样,累倒半死才回去,然后洗澡睡觉,就这么过了~~~~~~~~












(^oo^)

我心目中最适合演唱北京奥运主题歌的明星

本次大型网上调查将以公众推荐的形式推选出100名候选人,再由公众投票共同评出20名“我心目中最适合演唱北京奥运主题歌的明星”。
您可以通过北青网、千龙网、新浪网 、搜狐网、网易、TOM、腾讯网参与公开投票。

一、评选标准
要求被推选的歌手须具备时代特征,有良好的公众形象,符合奥运精神。
他们共同的特质是:具备震撼心灵的演艺才华。

二、评选规则
1、请从100名海选产生的推荐歌手中选择1至2名(演唱组合将按1个单位计票)你认为适合演唱2008奥运主题歌的明星;
2、请点击明星头像旁边的“投票”按钮投票;
3、查看投票结果请点击“查看”按钮;
4. 投票截止日期:2005年6月20日24:00。

http://event.ynet.com/2005aoyun-vote/index.jsp

2005年5月24日星期二

测试一下你的真实的心理年龄

早发过了,今天在贴出来,看看,偶是35岁,咋jie老呢

2005年5月22日星期日

分析ELF的加载过程

ELF的可执行文件与共享库在结构上非常类似,它们具有一张程序段表,用来描述这些段如何映射到�
炭占�.
对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来�
�,段的加载位置是浮动的,位置无关的,程序段表反映的是以0作为基准地址的相对加载地址.尽管共
享库的连接是不充分的,为了便于测试动态链接器,Linux允许直接加载共享库运行.如果应用程序具
有动态链接器的描述段,内核在完成程序段加载后,紧接着加载动态链接器,并且启动动态链接器的�
肟�.

typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type; /* ET_EXEC ET_DYN 等 */
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry; /* Entry point */
Elf32_Off e_phoff; 程序段描述表的位置
Elf32_Off e_shoff; 一般段描述表的位置
Elf32_Word e_flags;
Elf32_Half e_ehsize; ELF头的大小
Elf32_Half e_phentsize; 程序段描述表单元的大小
Elf32_Half e_phnum; 程序段描述表单元的个数
Elf32_Half e_shentsize; 一般段描述表的单元大小
Elf32_Half e_shnum; 一般段描述表单元的个数
Elf32_Half e_shstrndx;
} Elf32_Ehdr;

typedef struct elf32_phdr{
Elf32_Word p_type; PT_INTERP,PT_LOAD,PT_DYNAMIC等
Elf32_Off p_offset; 该程序段在ELF文件中的位置
Elf32_Addr p_vaddr; 该程序段被映射到进程的虚拟地址
Elf32_Addr p_paddr;
Elf32_Word p_filesz; 该程序段的文件尺寸
Elf32_Word p_memsz; 该程序段的内存尺寸
Elf32_Word p_flags; 该程序段的映射属性
Elf32_Word p_align;
} Elf32_Phdr;

struct linux_binprm{
char buf[BINPRM_BUF_SIZE]; 预先读入的ELF文件头
struct page *page[MAX_ARG_PAGES];
unsigned long p; /* current top of mem */
int sh_bang;
struct file * file;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
int argc, envc;
char * filename; /* Name of binary */
unsigned long loader, exec;
};
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
struct file *interpreter = NULL; /* to shut gcc up */
unsigned long load_addr = 0, load_bias;
int load_addr_set = 0;
char * elf_interpreter = NULL;
unsigned int interpreter_type = INTERPRETER_NONE;
unsigned char ibcs2_interpreter = 0;
mm_segment_t old_fs;
unsigned long error;
struct elf_phdr * elf_ppnt, *elf_phdata;
unsigned long elf_bss, k, elf_brk;
int elf_exec_fileno;
int retval, size, i;
unsigned long elf_entry, interp_load_addr = 0;
unsigned long start_code, end_code, start_data, end_data;
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
struct exec interp_ex;
char passed_fileno[6];

/* Get the exec-header */
elf_ex = *((struct elfhdr *) bprm->buf);

retval = -ENOEXEC;
/* First of all, some simple consistency checks */
if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
goto out; 文件头标记是否匹配

if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN)
goto out; 文件类型是否为可执行文件或共享库
if (!elf_check_arch(&elf_ex))
goto out;
if (!bprm->file->f_op||!bprm->file->f_op->mmap)
goto out; 所在的文件系统是否具有文件映射功能

/* Now read in all of the header information */

retval = -ENOMEM;
size = elf_ex.e_phentsize * elf_ex.e_phnum; 求程序段表总长度
if (size > 65536)
goto out;
elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL); 分配程序段表空间
if (!elf_phdata)
goto out;

; 读入程序段表
retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *) elf_phdata, size);
if (retval < 0)
goto out_free_ph;

retval = get_unused_fd(); 取可用进程文件表的自由槽位
if (retval < 0)
goto out_free_ph;
get_file(bprm->file);
fd_install(elf_exec_fileno = retval, bprm->file); 将打开的文件安装到进程文件表

elf_ppnt = elf_phdata; 指向程序段表
elf_bss = 0; bss段的起始地址
elf_brk = 0; bss段的终止地址

start_code = ~0UL; 代码段的开始
end_code = 0; 代码段的终止
start_data = 0; 数据段的开始
end_data = 0; 数据段的终止
; 扫描ELF程序段表,搜寻动态链接器定义
for (i = 0; i < elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) {
retval = -EINVAL;
if (elf_interpreter)
goto out_free_dentry; 如果包含多个动态链接器描述项

/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/

retval = -ENOMEM; 为动态链接器名称字符串分配空间
elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
GFP_KERNEL);
if (!elf_interpreter)
goto out_free_file;
; 将动态链接器的文件名读入内存
retval = kernel_read(bprm->file, elf_ppnt->p_offset,
elf_interpreter,
elf_ppnt->p_filesz);
if (retval < 0)
goto out_free_interp;
/* If the program interpreter is one of these two,
* then assume an iBCS2 image. Otherwise assume
* a native linux image.
*/
if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0)
ibcs2_interpreter = 1; 说明应用程序是IBCS2仿真代码

interpreter = open_exec(elf_interpreter); 打开动态链接器文件
retval = PTR_ERR(interpreter);
if (IS_ERR(interpreter))
goto out_free_interp;
retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);
; 读入动态链接器文件头
if (retval < 0)
goto out_free_dentry;

/* Get the exec headers */
interp_ex = *((struct exec *) bprm->buf); 假定为aout格式的文件头结构
interp_elf_ex = *((struct elfhdr *) bprm->buf); 假定为ELF文件头结构
}
elf_ppnt++; 下一片段目录项
}

/* Some simple consistency checks for the interpreter */
if (elf_interpreter) {
; 如果定义了动态链接器,分析其格式类型
interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;


/* Now figure out which format our binary is */
if ((N_MAGIC(interp_ex) != OMAGIC) &&
(N_MAGIC(interp_ex) != ZMAGIC) &&
(N_MAGIC(interp_ex) != QMAGIC))
interpreter_type = INTERPRETER_ELF; 如果不是AOUT标识

if (memcmp(interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
interpreter_type &= ~INTERPRETER_ELF; 如果没有ELF标识

retval = -ELIBBAD;
if (!interpreter_type) 不能识别动态链接器类型
goto out_free_dentry;

/* Make sure only one type was selected */
if ((interpreter_type & INTERPRETER_ELF) &&
interpreter_type != INTERPRETER_ELF) {
printk(KERN_WARNING "ELF: Ambiguous type, using ELF\n");
interpreter_type = INTERPRETER_ELF;
}
}

/* OK, we are done with that, now set up the arg stuff,
and then start this sucker up */

if (!bprm->sh_bang) {
char * passed_p;

if (interpreter_type == INTERPRETER_AOUT) {
sprintf(passed_fileno, "%d", elf_exec_fileno);
passed_p = passed_fileno;

if (elf_interpreter) {
retval = copy_strings_kernel(1
,&passed_p,bprm);
; 将程序的文件描述符压入参数堆栈,准备传递给aout格式的动态链接器
if (retval)
goto out_free_dentry;
bprm->argc++; bprm->page[]中参数的数目
}
}
}

/* Flush all traces of the currently running executable */
retval = flush_old_exec(bprm);
if (retval)
goto out_free_dentry;

/* OK, This is the point of no return */
current->mm->start_data = 0;
current->mm->end_data = 0;
current->mm->end_code = 0;
current->mm->mmap = NULL;
current->flags &= ~PF_FORKNOEXEC;
elf_entry = (unsigned long) elf_ex.e_entry; 应用程序的入口地址

/* Do this immediately, since STACK_TOP as used in setup_arg_pages
may depend on the personality. */
SET_PERSONALITY(elf_ex, ibcs2_interpreter);
; 如果是ibcs2_interpreter非0,置PER_SVR4代码个性,否则置PER_LINUX代码个性

/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
current->mm->rss = 0;
setup_arg_pages(bprm); /* XXX: check error */
; 建立描述堆栈参数页的初始虚存范围结构
current->mm->start_stack = bprm->p;

/* Try and get dynamic programs out of the way of the default mmap
base, as well as whatever program they might try to exec. This
is because the brk will follow the loader, and is not movable. */

load_bias = ELF_PAGESTART(elf_ex.e_type==ET_DYN ? ELF_ET_DYN_BASE : 0);
; 如果需要加载的是共享库,则设置共享库的加载偏置为ELF_ET_DYN_BASE
/* Now we do a little grungy work by mmaping the ELF image into
the correct location in memory. At this point, we assume that
the image should be loaded at fixed address, not at a variable
address. */

old_fs = get_fs();
set_fs(get_ds());
; 再次扫描程序段描述表,映射其中的程序段到进程空间
for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
int elf_prot = 0, elf_flags;
unsigned long vaddr;

if (elf_ppnt->p_type != PT_LOAD)
continue; 如果程序段不可加载

if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;

elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;
; 根据程序段描述设置相应的mmap参数

vaddr = elf_ppnt->p_vaddr; 段起始地址
if (elf_ex.e_type == ET_EXEC || load_addr_set) {
; 对于可执行程序,使用固定映射
elf_flags |= MAP_FIXED;
}

error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
; 将该程序段[eppnt->p_offset,eppnt->p_filesz]映射到虚存(load_bias+vaddr)开始的区域

if (!load_addr_set) {
load_addr_set = 1;
load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
; 求出该ELF文件在用户虚存中的起始地址
if (elf_ex.e_type == ET_DYN) {
load_bias += error -
ELF_PAGESTART(load_bias + vaddr);
load_addr += error;
}
}

k = elf_ppnt->p_vaddr;
if (k < start_code) start_code = k; 取最小的段地址作为代码段起始
if (start_data < k) start_data = k; 取最大的段地址作为数据段起始

k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; 这时k指向文件段尾

if (k > elf_bss)
elf_bss = k; 取最大文件段尾作为BSS段起始
if ((elf_ppnt->p_flags & PF_X) && end_code < k)
end_code = k; 取最大可执行的文件段尾作为可执行段的终止
if (end_data < k)
end_data = k; 取最大的文件段尾作为数据段的终止
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; 这时k指向内存段尾
if (k > elf_brk)
elf_brk = k; 取最大的内存段尾作为BSS段的终止
}
set_fs(old_fs);
elf_entry += load_bias;
elf_bss += load_bias;
elf_brk += load_bias;
start_code += load_bias;
end_code += load_bias;
start_data += load_bias;
end_data += load_bias;

if (elf_interpreter) {
if (interpreter_type == INTERPRETER_AOUT)
elf_entry = load_aout_interp(&interp_ex,
interpreter);
else
elf_entry = load_elf_interp(&interp_elf_ex, 动态链接器的文件头
interpreter, 动态链接器打开的文件结构
&interp_load_addr); 输出链接器的加载地址
; 可执行程序的入口变为动态链接器的入口
allow_write_access(interpreter);
fput(interpreter);
kfree(elf_interpreter);

if (elf_entry == ~0UL) {
printk(KERN_ERR "Unable to load interpreter\n");
kfree(elf_phdata);
send_sig(SIGSEGV, current, 0);
return 0;
}
}

kfree(elf_phdata); 释放程序段表

if (interpreter_type != INTERPRETER_AOUT)
sys_close(elf_exec_fileno);

set_binfmt(&elf_format); 增加ELF内核模块的引用计数

compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
bprm->p = (unsigned long) 建立入口函数参数表
create_elf_tables((char *)bprm->p,
bprm->argc,
bprm->envc,
(interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),
load_addr, load_bias,
interp_load_addr,
(interpreter_type == INTERPRETER_AOUT ? 0 : 1));
/* N.B. passed_fileno might not be initialized? */
if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start += strlen(passed_fileno) + 1;
current->mm->start_brk = current->mm->brk = elf_brk;
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->start_data = start_data;
current->mm->end_data = end_data;
current->mm->start_stack = bprm->p;

/* Calling set_brk effectively mmaps the pages that we need
* for the bss and break sections
*/
set_brk(elf_bss, elf_brk); 建立bss的虚存映射,elf_bss是bss的开始,elf_brk是bss的结束

padzero(elf_bss); 如果bss不起始于页连界上,说明与data段有重叠,则将该页bss区域清0

#if 0
printk("(start_brk) %lx\n" , (long) current->mm->start_brk);
printk("(end_code) %lx\n" , (long) current->mm->end_code);
printk("(start_code) %lx\n" , (long) current->mm->start_code);
printk("(start_data) %lx\n" , (long) current->mm->start_data);
printk("(end_data) %lx\n" , (long) current->mm->end_data);
printk("(start_stack) %lx\n" , (long) current->mm->start_stack);
printk("(brk) %lx\n" , (long) current->mm->brk);
#endif

if ( current->personality == PER_SVR4 )
{
/* Why this, you ask??? Well SVr4 maps page 0 as read-only,
and some applications "depend" upon this behavior.
Since we do not have the power to recompile these, we
emulate the SVr4 behavior. Sigh. */
/* N.B. Shouldn't the size here be PAGE_SIZE?? */
down(&current->mm->mmap_sem);
error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
up(&current->mm->mmap_sem);
}

#ifdef ELF_PLAT_INIT
/*
* The ABI may specify that certain registers be set up in special
* ways (on i386 %edx is the address of a T_FINI function, for
* example. This macro performs whatever initialization to
* the regs structure is required.
*/
ELF_PLAT_INIT(regs);
#endif
r /> start_thread(regs, elf_entry, bprm->p); 将返回的eip设为elf_entry,esp设为bprm->p
if (current->ptrace & PT_PTRACED)
send_sig(SIGTRAP, current, 0); 如果进程处于跟踪状态,则生成SIGTRAP信号
retval = 0;
out:
return retval;

/* error cleanup */
out_free_dentry:
allow_write_access(interpreter);
fput(interpreter);
out_free_interp:
if (elf_interpreter)
kfree(elf_interpreter);
out_free_file:
sys_close(elf_exec_fileno);
out_free_ph:
kfree(elf_phdata);
goto out;
}
static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
struct file * interpreter,
unsigned long *interp_load_addr)
{
struct elf_phdr *elf_phdata;
struct elf_phdr *eppnt;
unsigned long load_addr = 0;
int load_addr_set = 0;
unsigned long last_bss = 0, elf_bss = 0;
unsigned long error = ~0UL;
int retval, i, size;

/* First of all, some simple consistency checks */
if (interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN)
goto out;
if (!elf_check_arch(interp_elf_ex))
goto out;
if (!interpreter->f_op || !interpreter->f_op->mmap)
goto out;

/*
* If the size of this structure has changed, then punt, since
* we will be doing the wrong thing.
*/
if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
goto out;

/* Now read in all of the header information */

size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;
if (size > ELF_MIN_ALIGN)
goto out; 如果动态链接器的程序段表的尺寸大于1个页面
elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
if (!elf_phdata)
goto out;

retval = kernel_read(interpreter,interp_elf_ex->e_phoff,(char *)elf_phdata,size);
error = retval;
if (retval < 0)
goto out_close;

eppnt = elf_phdata;
for (i=0; ie_phnum; i++, eppnt++) {
if (eppnt->p_type == PT_LOAD) {
int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
int elf_prot = 0;
unsigned long vaddr = 0;
unsigned long k, map_addr;

if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
vaddr = eppnt->p_vaddr;
if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
elf_type |= MAP_FIXED;

map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type);

if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
load_addr = map_addr - ELF_PAGESTART(vaddr);
load_addr_set = 1;
}

/*
* Find the end of the file mapping for this phdr, and keep
* track of the largest address we see for this.
*/
k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
if (k > elf_bss)
elf_bss = k;

/*
* Do the same thing for the memory mapping - between
* elf_bss and last_bss is the bss section.
*/
k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
if (k > last_bss)
last_bss = k;
}
}

/* Now use mmap to map the library into memory. */

/*
* Now fill out the bss section. First pad the last page up
* to the page boundary, and then perform a mmap to make sure
* that there are zero-mapped pages up to and including the
* last bss page.
*/
padzero(elf_bss);
elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1); /* What we have mapped so far
*/

/* Map the last of the bss segment */
if (last_bss > elf_bss)
do_brk(elf_bss, last_bss - elf_bss);

*interp_load_addr = load_addr;
error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;

out_close:
kfree(elf_phdata);
out:
return error;
}
static inline unsigned long
elf_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int
type)
{
unsigned long map_addr;

down(&current->mm->mmap_sem);
map_addr = do_mmap(filep, ELF_PAGESTART(addr),
eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type,
eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));

up(&current->mm->mmap_sem);
return(map_addr);
}
void set_binfmt(struct linux_binfmt *new)
{
struct linux_binfmt *old = current->binfmt;
if (new && new->module)
__MOD_INC_USE_COUNT(new->module);
current->binfmt = new;
if (old && old->module)
__MOD_DEC_USE_COUNT(old->module);
}
static void set_brk(unsigned long start, unsigned long end)
{
start = ELF_PAGEALIGN(start);
end = ELF_PAGEALIGN(end);
if (end <= start)
return;
do_brk(start, end - start);
}
static void padzero(unsigned long elf_bss)
{
unsigned long nbyte;

nbyte = ELF_PAGEOFFSET(elf_bss);
if (nbyte) {
nbyte = ELF_MIN_ALIGN - nbyte;
clear_user((void *) elf_bss, nbyte);
}
}

2005年5月17日星期二

我的工作,共勉之


水木回来了

偶尔在山西大学bbs看到了,结果到www.newsmth.org, 果然水木又重新建立起来了,不过估计是地下自己搞得,要不然不知道为什么不用以前的那个域名了,还好,以前的账户还可以登陆,不过需要重新激活,哎,又能上水木就应该很庆幸了,不管喽,灌水去!

今天新建立了一个blog

嘿嘿,今天弄了个英文版本的,其实一开始就想把这个现在这个blog弄成英文的,还是慢慢的变成母语了,去年弄了个wallop邀请,今天突然想起来了,所以把以前的垃圾删除,开张!练练我那该死的英语

2005年5月16日星期一

最想听却又不敢听的一首歌曲

http://www.flash77.com/flashplay/flash77_4897.htm

张学友的《心如刀割

张学友:心如刀割

我的天是灰色
我的心是蓝色
触摸着你的心
竟是透明的

你的悠然自得
我却束手无策
我的心痛竟是你的快乐

其实我不想对你恋恋不舍
但什么让我辗转反侧
不觉我说着说着天就亮了
我的唇角尝到一种苦涩

我是真的为你哭了
你是真的随他走了
就在这一刻
全世界伤心角色又多了我一个
我是真的为你爱了
你是真的跟他走了
能给的我全都给了我都舍得
除了让你知道我心如刀割

2005年5月7日星期六

这句名言出自谁口中?

看了,还不错,转过来


在一所著名大学里的历史课上,教授正在向来自各国的同学提问:“要生存还是要灭亡。这句名言出自谁的口中?”
  
     沉寂了半天之后,古田站起来说:“威廉·莎士比亚。”
  
    “很好,被誉为‘欧洲的良心’是指谁?”
  
    “罗曼·罗兰。”
  
    “完全正确,同学们,刚才回答问题的是位日本学生,可是作为欧洲国家的学生却答不出来,太遗憾了!”教授不无感慨的说道。
  
    “干死小日本!”突然有人发出一声喊叫。
  
    “谁!谁说的!”教授气得语音都颤抖了。
 ?br>    “杜鲁门说的。”约翰站了起来。
  
    “你以为自己在干什么?”教授生气的说道。
  
    “麦当娜说的。”杰克也站了起来。
  
    “这真叫人恶心,简直无法无天了。”教授浑身气得发抖。
  
    “乔治·布什说的。”斯蒂芬也坐不住了。
  
    课堂立刻陷入了混乱之中,所有的学生都开始议论纷纷,一些学生开始起哄:“耶!真***够劲。”
  
    “克林顿对莱温斯基说的。”玛丽毫无表情的接话道。
  
    教授愤怒得说不出话来,隔了一会,他大踏步的向门外走去,到门口时,他冷冷的看了所有人一眼:“我会回来的。”
  
    “阿诺德·施瓦辛格说的。”鲍勃终于插上话了。
  
    古田委屈的一摊手:“我没做什么坏事,为什么会这样?”
  
    “张国荣说的。”李小丽一脸崇拜的神情回答
  
    所有的学生都围成一个圈,汤姆有些垂头丧气:“该死,我们完了。”
  
    “希特勒说的。”伊汉诺娃立刻回答。
  
    赖特叹了口气:“今天将是一个很有意义的日子。”
  
    “本·拉登说的。”克瑞斯终于为自己能说出一个名字而得意。
  
    “这决非是我最得意的一天。”古田惭愧的说着。
  
    “托尼·布莱尔说的。”已经不知道谁在回答。
  
    这时校长和教授一起进来了,他脸色铁青,几乎是一字一顿的说道:“你们要为此付出代价!”
  
    “斯大林说的。”全班同学异口同声的回答

魅力城市视频

我的家乡,山西省长治市,被评为了全国10大魅力城市之一,这里有几个视频,请大家欣赏

My hometown, Chang Zhi ,Shan Xi province ,was appraised tobe one of the 10 most attractive cities in China ,here are some videos about my hometown:

中文版本 In Chinese
http://218.26.240.48/spdb/ZT/zgczhy.rm

中国魅力城市-长治(德语版) In German
http://218.26.240.48/spdb/ZT/sx_041130115528_sxpd.rm

中国魅力城市-长治(英语版) In English
http://218.26.240.48/spdb/ZT/sx_041130115342_sxpd.rm

2005年4月28日星期四

head.s 分析[原创]

2005-04-28 BY:LinuxRen
head.s就运行在32位保护模式下面了,这里是名副其实的内核了

GNU 的汇编直接数,比如 0x10,如果前面有$,即$0x10,则表示立即数,在16位实模式下,内存地址由段基地址左移四位加上段内偏移量组成,而在保护模式下,由段选择子 (逻辑地址的高16位 )(由段选择子选择的描述符所决定的段基地址不需要左移,直接加偏移地址就可以形成线性地址)和段内偏移量(逻辑地址的低16位)组成,这里是内存管理中 的段式管理,还有页式管理(以后再说),这里有张图片可以很好地说明段式管理:

如果仅仅是0x10,则表示内存的地址。

这张图片好像有个小问题,右上角的偏移量应该是32位的,这里很容易给人错觉好像偏移量就是逻辑地址的低16位,段选择子时逻辑地址的高16位,可以从intel的手册得到证实 其中的图5-2,呵呵,intel应该不会犯错吧

为什么既有段式内存管理,也有页式内存管理,我想大概是cpu设计的原因,如果cpu加电直接就可以进入32位,那么段式管理就应该报废了。也不知道现在的64位的cpu是怎么处理的,现在64微处理器的资料实在太少了。

head.s 第18行,movl $0x10, %eax 之后eax就相当于段选择子,与gdt表的地址进行运算,就可以定为一个全局描述符,在全局表述符中会给出对应段的段基址,再加上已知的段内 偏移量,就可以将一个逻辑地址转为一个线性地址,这里的0x10仅仅是段选择子。

然后call setup_idt, setup_idt代码在79行,就是将idt中的每一项都设置为指向同一个中断门ignore_int,即打印一段信息“Unknown interrupt”

在这里说一下idt,idt就是中断描述符表,和全剧描述符表是一个等级的,相应的中断描述符表项和全局描述符表项是对应的,中断门ignore_int是和全局描述符表项所表述的段一个等级的,只不过这里的不是一个段,而可以是一段代码,这样就应该容易理解了吧。

接 着call setup_gdt,gdt在前面已经临时设置过了,在这里要重新设置.这里将gdt还是设置成为含有256个全局描述符表项,第0个不用,第一个系统代 码段,第二个系统数据段,值分别是 0x00c09a0000000fff 0x00c09020000000fff,可以对照着张表来看看具体的意思:


这里将系统代码段和系统数据段的段长都设置成为了16M.

后面的252项都填充0,用于以后用户程序使用。

接下来就是检查数学协处理器,还有什么287/387,没看,跳过。

然后就是开启分页管理

这里的分页机制是专门为内核使用的,也就是说以后的应用程序并不适用这里的页表,而是在程序执行的时候自己自动加载自己的页表。

这里的分页比较简单,经过分页之后的物理地址是和分页前的线性地址相同的,这一点是非常重要的。因为在分页启动之前有一句话: pushl $_main
这里是将main函数的地址压栈,当分页启动之后要讲这个地址pop出来,并且跳到main去执行,系统就会跳到以前压入栈的main的地址经过分页映射后的地址去运行,如果物理地址和线性地址不一样的话,那么就会跳到错误的地方,但是这里不会。

[原创]setup.s分析

setup.s会将被bootsect.s读取到0x10000处的system模块移动到0x00000处,这样好象会把中断表给覆盖掉,文件执行到后来会加载中断描述符表,但是idt表却是这个样子的:

idt_48:
.word 0
.word 0,0

idt表的基地址居然还是0x00000,这里就搞不懂了,因为这个时候0x00000的地方已经是system模块了,所以在真正的重新设置idt表以前,如果出现异常,我不知道系统将会进行什么样的处理,这里是问题一

setup.s会读取大量的参数然后存到0x90000处,也就是说会覆盖bootsect.s,具体的参数分布书上44页有,这里有个问题是,系统会将 第一个硬盘的参数表读到0x90080处,然后再读取第二个硬盘的参数表到0x90090处,然后再去检测到底有没有第二个硬盘,如果没有再将 0x90090处的参数表清0,怪了,为什么不先检测有没有第二个硬盘,在去决定是否读取参数表到0x90090呢?非要反着来不成?

下面是我对保护模式的一点理解,不知是否正确,有错误欢迎提出:

在16位的实模式下的程序在内存中的布局和操作系统的是混在一起的,也就是说,cpu没有提供对操作系统的保护,这会出很多问题,32为保护模式下, cpu提供的很强大的功能来保护操作系统的代码不被侵犯,我们所要做的就是提供相应的数据,将相应的寄存器初始化,打开A20地址线,然后执行一个跳转指 令,cpu就会进入保护模式,对我们的程序进行保护,具体的各位可以在网上找到资料,其中一个方法就是设置gdt以及ldt来保护操作系统以及应用程序, gdt(大小限制在64k以内)也即全局描述符表里包含有全局描述符,每个全局描述符得大小为8字节,所以理论上,一个gdt一共可以有64K/8=8K 个全局描述符.其中三个是描述全局性的操作系统的代码段,数据段,其他的一个程序占用一个全局描述符.

系统怎么来确定要使用哪一个全局描述符呢?使用段选择子!这里是他的结构:

其中3-15字节是用来索引gdt来去定某一个全局描述符得,共13位,所以gdt最大长度限制在2^13=64K.但是不知道为什么在这里Linus只将gdt的长度设置为了2K,也就是为什么在
gdt_48:
.word 0x800 !这里为什么是0x800,而不是0xFFFF,难道就是因为现在的gdt是临时的,以后还有设置,所以只要够现在用就可以了??

.word 512+gdt,0x9 ! 这里由于setup.s是在0x90200处,所以要加上0x200(512)的偏移量。

然后就是为系统进入32为保护模式作准备了,这里就要先初始化gdt,由于现在还没有程序运行,所以只是用了三项(其实是两项),其中,第零项没有使用,第一项描述系统的代码段,第二项描述系统的数据段(堆栈段)。

全局表述符表:

gdt:
.word 0x0000 !第0个弃用
.word 0x0000
.word 0x0000
.word 0x0000

.word 0x07FF !第1个,第0个用了4个word,所以这里偏移量是0x08
.word 0x0000 !用来描述系统代码段
.word 0x9A00
.word 0x00C0

.word 0x07FF !第2个,偏移量是0x10,也就是16
.word 0x0000 !用来描述系统数据段
.word 0x9200
.word 0x00C0

这里是gdt的结构



这是,进入保护模式所要准备的数据已经够了,接下来就是加载gdt的基地址以及大小限制到专用寄存器gdtr,idt基地址以及大小限制到idtr,然后打开A20数据线,关于A20数据线,哈工大纯c板块上有很好的文章介绍,然后重新对8259进行编程(看见头大,没怎么看),设置状态字,然后执行
jmpi 0,8 !这里的8就是上面的段选择子的值,化为二进制为 0000,0000,0000,1000 这里的1就是第1个全局描述符,即系统代码段,就会去执行head.s

最好对这个时候内核内存布局有个印象,便于以后的理解

-------------------
数据段表述符
-------------------
代码段描述符
-------------------
-------------------0X90200
硬件参数
-------------------0X90000



head.s程序
--------------------0X00000

[原创]内核引导启动程序分析

主要参考用书:《linux内核完全分析》
由于比较简单,所以先把bootsect.s看了一下,其实整个内核引导程序有三个,分别是bootsect.s,setup.s,head.s ,前两个都不属于严格意义上的kernel,只不过是对kernel的启动做一些准备,真正的内核是从head.s启动的,现在先分析 bootsect.s:

当你按机箱上的power on的时候,cup会自动去到bios中地址0xFFFF0处开始执行bios中的代码,除了会进行一些初始化工作,这段代码最主要的是将可启动设备的第 一个扇区(bootsect.s)(引导扇区,512字节)读到内存0x07c00处。

当bootsect.s执行时,它已经被读到了0x07c00处,首先它会将自己读到0x90000处,为什么要先读到0x7c00,再读到 0x90000,是由于bios被设置成了先读到0x7c00的地方,再度到0x90000是为了防止后来读入到0x10000处的system模块覆盖 掉bootsect.s。将自己读到0x90000处后先会设置一下堆栈(0x9000:0xff00),然后将setup.s读入到紧接着 bootsect的地方(0x90200),默认的是从软盘读取的(硬盘在当时估计是奢侈品),然后就是取相应磁盘(被当作软盘,写成了hard code)的参数,然后就是利用bios的0x10中断,ah=0x03,将一段话"Loading system..."打印到屏幕上。然后检测要使用哪个根文件系统设备(软盘或者硬盘),具体的方法是判断508,509字节处的根设备号是否被置为1.
问题一.508,509是什么时候设置的?

如果有硬盘的话,就将其保存,否则读取每磁道的扇区数来判断是什么类型的软盘(1.2M还是1.4M),也保存。
问题二.这里硬盘参数ROOT_DEV=0x306,意思是第二个硬盘的第一个分区,为什么是第二个硬盘?

问题三.在这之前的代码都是软盘启动的,是不是就是说,不管你有没有硬盘,都必须从软盘启动,如果不修改bootsect的代码的话

最后执行 jmpi 0,SETUPSEG,跳转到setup.s程序的开始处执行。


2005年4月21日星期四

今天新买了个域名

LinuxRen.Org,58块钱,现在就有两个域名了,我对.net的注册商彻底失望了,打算快到期的时候转注册商,LinuxRen.Org的解析到首页上,LinuxRen.Net直接解析到论坛上面,这样也方便一些,与空间在一个地方买的,兄弟给推荐的人,哈哈,没有这个贱人我都不知道该怎么弄了,网站就打算先这样办下去,慢慢人估计会多起来的,关键要有自己的特点,由于自己水平不高,所以还是在网上招聘版主,嗯,大家一起努力~~~~~~~~~

2005年4月19日星期二

BIOS和DOS建立的中断向量表

绝对地址 16进制 10进制 有关内容
00H 0H 0 0做除数时处理器发出的中断 溢出条件是:

除法类型 结果
---------------------------------
有符号字 +127
有符号双字 +32767
无符号字 | >255
无符号双字 | >65535
---------------------------------
DOS设有INT 00H处理程序,系统将显示"Divide Overflo",结束当前程序的执行。

04H 1H 1 单步调试时处理器发出的中断
08H 2H 2 非屏蔽中断
0CH 3H 3 调试程序设置断点时处理器发出的中断
10H 4H 4 发生算术溢出时处理器发出的中断
14H 5H 5 调用BIOS的屏幕拷贝操作
18-1FH 6-7H 6-7 保留单元
20H 8H 8 每1/18.2秒定时器发出的中断
24H 9H 9 按压或释放键时产生的中断
28H 0AH 10 保留单元
2CH 0BH 11 通讯设备使用的硬件中断
30H 0CH 12 通讯设备使用的硬件中断
34H 0DH 13 交替打印时硬件产生的中断
38H 0EH 14 软驱操作结束时产生的硬件中断
3CH 0FH 15 打印机发出警告信号时产生的硬件中断
40H 10H 16 BIOS的显示I/O功能调用
44H 11H 17 BIOS设备确认调用
48H 12H 18 BIOS确认内存空间大小的功能调用
4CH 13H 19 BOIS的磁盘I/O功能调用
50H 14H 20 BIOS的RS-232串行I/O功能调用
54H 15H 21 在PC和XT机上是BIOS磁带I/O功能调用。在AT机上是AT扩充服务功能调用。
58H 16H 22 BIOS的键盘I/O功能调用
5CH 17H 23 BIOS的打印机I/O功能调用
60H 18H 24 ROM的BASIC解释和程序功能调用
64H 19H 25 BIOS的装载引导服务调用
68H 1AH 26 BIOS的日期时钟功能调用
6CH 1BH 27 Ctrl+Break处理程序功能调用。当键入Ctrl+Break键时指向可执行的程序入口初 始化BIOS使该向量指向一条TRET指令。用户可修改该向量,使它指向自己的程序。
70H 1CH 28 指向每1/18.2秒时可执行的服务程序的入口。初始化时该向量指向一条IRET指令。用户可修 改该向量,使它指向自己的Ctrl+Break 处理程序。
74H 1DH 29 指向显示控制器初始化参数。BIOS使这个向量指向ROM驻留表。
78H 1EH 30 指向软盘参数表。BIOS使这个向量指向ROM 驻留表,但是DOS把它改为指向DOS的RAM 驻留表。
7CH 1FH 31 指向一点阵表。在这个表中,BIOS可以找到字符集后128个字符的点阵
80H 20H 32 终止程序的DOS功能调用
84H 21H 33 任何种DOS功能调用
88H 22H 34 指向DOS的结束地址
8CH 23H 35 指向D
OS的Ctrl+Break处理程序
90H 24H 36 指向DOS的严重错误处理程序
94H 25H 37 DOS绝对磁盘读调用
98H 26H 38 DOS绝对磁盘写调用
9CH 27H 39 程序终止,但仍驻留内存的DOS功能调用
9DH 28H 40 DOS空闲
9EH 29H 41 支持驱动器程序输出
9FH-A1H 2AH-2CH 42-44 保留单元
A2H 2DH 45 DOS构件接口
A3H 2EH 46 COMMAND.COM退回入口
A4H 2FH 47 多路中断(空闲信号)
A5-FFH 30-3FH 48-63 为DOS保留的单元
100H 40H 64 保留单元
104H 41H 65 指向硬盘0的参数表,BOIS使这个向量指向ROM驻留的表。
108-10FH 42-43H 66-67 保留单元
110H 44H 68 PC机使用,用于指向低分辩率图形字符参数表
114H 45H 69 保留单元
118H 46H 70 指向硬盘1的参数表,BIOS使这个向量指向 ROM驻留的表。
11CH 47H 71 保留单元
120H 48H 72 PC机使用,用于把PC机的键盘代码变换为标准的键盘代码。
124H 49H 73 指向键盘增强服务变换表
128-17FH 4A-5FH 74-95 保留单元
180-19FH 60-67H 96-103 为用户程序保留的单元
1A0-1BFH 68-6FH 104-111 未使用
1C0H 70H 112 硬件中断(IRQ--interrupt request) 8--实时时钟中断
1C4H 71H 113 硬件中断9
1C8H 72H 114 硬件中断10
1CCH 73H 115 硬件中断11
1D0H 74H 116 硬件中断12
1D4H 75H 117 硬件中断13--BIOS把这个中断向量重定向为非屏蔽中断(NMI)
1D8H 76H 118 硬件中断14
1DCH 77H 119 硬件中断15
1E0-1FFH 78-7FH 120-127 未使用
200-217H 80-85H 128-133 为BASIC保留
218-3C3H 86-F0H 134-240 BASIC程序运行时提供给BASIC解释程序作用
3C4-#FFH F1-FFH 241-255 未作用

2005年4月15日星期五

有一种爱叫痛 有一种爱叫放手

有一种爱很凄迷,有一种爱只能远望,有一种爱注定成传奇.......
 有一种爱叫做痛,痛得心脏起了褶子,痛得头脑空洞无物,痛得意志麻木萧瑟......
 有一种爱叫放弃,明知道许多事情是没有答案的,却想寻找一个答案,真的好累......
 有一种爱叫忍让,忍让也是一种爱,以爱的方式善待对方的缺陷,用包容的胸怀宽恕自己的爱人,给他一个悔悟的机会留一个自省的空间于平平淡淡中演绎经典,在无声无语中融洽恩爱.这样即使是不传奇的爱情也将变得永恒,再平淡的婚姻,依然一如既往令人留连.
有一种爱,叫放手.曾经天真的以为不管时间和空间的距离有多长多远,感情一定会恒久不变,因为爱是没有理由的......
 爱不能成为牵绊,所以要选择放手,从容的让彼此走彼此的世界,凡事到极至,伤也会痛.其实爱过就会懂,彼此个性的太过坚强终究会是一起生活的阴影。
 昨日的幸福以成为一种痕迹,两人能携手走完整人生固然很好,可陪上了一段也应心存感激了.
 爱一个人不是要成为所爱人的牵绊,只要心中有爱,生活总是那么美好!
 相距是一种缘,相识,相恋更是一种缘分,缘起而聚,缘尽而散,放手才是真爱.有一种爱叫放手!
 有一种爱,叫离开,曾经以为自己的爱情能够长久,曾经以为真心的付出就能换来幸福,其实错了......
 爱情给的唯一的东西就是背叛,无情的背叛!曾经是那么相爱的两个人,转眼陌路,留下的是残缺不全的记忆和心痛.
 没有想到结局会是这样,曾经的山盟海誓,曾经的天长地久,转眼都成了飞灰。
 经常惊醒于午夜梦回的黑暗中,我的心都好痛,我思念一个人的疼痛,看着空中的星星,想着远方属于你的夜,你还好吗?一直都快乐吗?没有我在你身边是不是有另外一个人去关心你,爱你呢?
 我现在唯一的愿望只是希望再见你一面,但我又怕见你,怕见到你,我的心又会再一次的被捏碎.我只有对自己说不要再去想他,不要再想了,虽然他的影子从未离开过。
生活还是要过的,其实有种爱叫离开,再见了,我的爱人......
 有一种爱,我们不能称之为爱情,虽然有同样的心动,同样的怀想,同样乍然相见的喜悦,依依不舍的眷恋,但世间总有一种约束,让心思沉静,让感情不再漂泊,依然可以在午夜梦回事心生柔情,依然可以相信自己的完美与可爱,在这些温柔的情愫里。
  有一种爱,可以默默的爱,默默的理解,默默的在心里装满祝福,挥一挥手,让春草缠绵,落江成阵,就是有这样的感情啊!飘荡成缠绵而温暖的空气,就是在这样 无心的眷恋里,我们认识自己也认识世间,就是有这样无缘而有情的瞬间,让我们轻轻的叹息,深深的爱,虽然我们相爱,但不能称之为爱情,多想让爱发出声响 啊!可那是一种毁灭,善良绝对不允许这样,多想让爱明白啊!可怎么人心看着爱有为难,感情也不能饶恕爱的胡来。
 就这样默默的去爱,永远放在心头来爱;当风吹来的时候,就让湖水激动地涌出堤岸,一点点就已经足够了,就象喝第一口茶水,才能品出味道.只要轻盈的湖水永远不下沉,只要坚固的堤岸永远拦得住湖水,相信湖水总是会泛出激情,堤岸也总是能感到坚强。
 有一种爱,永远难以启齿,可这种爱情来得持久,来得绝美......

第一次见老头

昨晚第一次见公司的CEO,给我们这些新进来的人开了个会,美籍华人,好像生于湖南,挺牛气的,自我介绍就说SQL框架是由他带队设计开发的,厉害!当年的IBM全球副总裁,普林斯顿大学博士毕业, 是美国电脑界声望最高、职务最高的华人 。想当初公司刚刚成立的时候就是靠的他的个人魅力吸引来了一大帮的牛人,短短1年半已经很有名气了,真得不容易。然后就是一些慷慨激昂的话,归根结底就是让我们这些人工作再苦一些,再累一些...听到最后都不耐烦了,和小学时候听校长报告一样,没啥区别,一个字:累!

在网上找的一些资料:

*刘英武
1941年出生于湖南长沙
1965年获台湾大学电机学士学位。
1969年获美国普林斯顿大学电机学和计算机学博士学位。
职业:
1955年至今:威科公司(Walket Interactive Systems)董事长、总裁和首席执行官
1993——1995年:凯登斯(Cadence Design Systems)首席执行官
1989——1992年:宏基关系企业决经理与宏基北美洲和欧洲总公司董事长和首席执行官
1969——1989年:IBM公司(以下摘要)
应用软件事业部总经理
办公室系统副总裁
公司管理委员会秘书长
通讯软件部主任电脑研究部经理
兼任:加州影片委员会委员
cadence、Triden、ASE三家公司董事

空间开通了

今天网站正式开通,放了个discuz的论坛,还不错,速度比较块,不过 刚才死活访问不到,估计又是域名解析速度慢,没办法,当时贪图小便宜找了一家小的网站注册,没想到速度 chaooooooooooooooooooooooooooooooooooooooooooooooo级慢,有时候还会报说找不到域名, kao~~~~~~~~~~~~~~~

想看的一些书

没有时间看,不过还是列出来,现在最希望的就是有那么一大段时间,用来看看书。按优先级列出来,不过是并行处理:

LINUX内核完全注释》这个赵郡是在太牛了,给我们这些浮躁的小青年们树立的一个极好的榜样,想学知识,首先心态要端正,http://www.oldlinux.org/ 作者的网站,很不错,有什么问题可以亲自询问赵老师,保证教到你完全明白为止。这本书也是我毕业设计要啃的书。目前这本书正在翻译成英文的,估计今年中旬出版。看这本书最好伴着《Unix操作系统设计》或者《操作系统概念》(英文的第七版已经出来了,国内还没得卖)

深入理解Linux内核(第二版)》不多说了(因为还没看-_-#),圣经级的。翻译的还可以(据说)

Linux内核源代码情景分析 上下》又是中国人的书,并且是难得的好书!看了内存管理一章,爽!不过还不够底层,所以转而看完全分析了,呵呵

UNIX环境高级编程》如果你用过linux,并且在linux上写过C程序,这本书你就一定听说过,大牛!尤其是作者W.Richard Stevens[已故],他的每一本书我都打算好好读一下。

UNIX网络编程卷2:进程间通信(第2版)(英文影印版)》Stevens系列

UNIX 网络编程(第2版)第1卷:套接口API和X/Open 传输接口API》Stevens系列

TCP/IP详解三卷》Stevens系列

深入理解计算机系统(修订版)》 底层的一些东西,“所有想写出更快、更可靠程序的开发人员必读之书!”

Linkers & Loaders》好像没有纸版本的,我把地址贴到了LinuxRen上,看看吧,对理解程序的执行很有帮助的。

中断没有被屏蔽,J2EE类的书籍可能要"陷入"进来

2005年4月11日星期一

80386 ASM程序设计基础–80386实模式下编程III

主要介绍段描述符,段选择子
在保护模式下,段是实现虚拟地址到线性地址转换的基础。在保护方下,每个段有三个参数:段基址,段界 限,段属性。段基址规定了线性地址空间中段的开始地址,段基址长度为32位,所以任何一个段都可以从32位线性地址空间中的任何一个字节开始,这一点和实 式方式不同,实式方式下要求段的边界必须被16整除。段界限规定段的大小,段界限用20位表示,而且段界限可以是字节或4K为单位,这个称为段的粒度。当 段界限以字节为单位时,那么段的范围是1字节至1M字节;当段界限是以4K字节为单位时,那么段的范围是4K至4G。段的界限同时也是用来校验偏移地址的 合法性,比如说段A的基址为00123456H,段界限为1000H,如果段界限以字节为单位,那么段的范围是00123456H-00124456H; 如果段界限以4K字节为单位,那么段的范围是00123456H-00223456H。事实上,段的界限也可以用来校验偏移地址的合法性,上面的例子中界 限为1000H,那么偏移地址的范围就是0-1000H,如果偏移地址不在这个范围内那就会引起异常。需要说明的是,数据段有点特殊,因为数据段的偏移范 围不仅仅是由段界限来决定,还要由段的扩展方向(Extension Direction)来决定,因为要照顾到堆栈段(堆栈段是一种特殊的数据段,它是向低端地址扩展的),如果段界限为Limit,段的扩展方向为向高端地 址扩展的话,那么我们可以断定它是一普通的数据段,0-Limit是有效的偏移范围,而Limit以上属于无效的偏移范围;如果段界限为Limit,段的 扩展方向为向低端地址扩展的话,那么可以断定它是一堆栈段,此时0-Limit是无效的偏移范围,Limit以上则属于有效的偏移范围,正好和向高端地址 扩展的普通数据段相反。除了堆栈段以外,其它的段均是自然向高端扩展。
段基址,段界限及段属性这三个参数在保护模式下用描述符来描述,每个描述符的长度为8个字节,每个段都有一个对应的描述符。在保护模式下有三种描述符:存储段描述符,系统段描述符,门描述符。
A.存储段描述符:存储段是指程序直接执行的代码段和数据段,存储段描述符是用来描述存储段的,也可以说是用来描述代码和数据段的,它的长度为8个字节,该描述符结构示意图:

第7字节 第6字节 第5字节 第4字节 第3字节 第2字节 第1字节 第0字节
|--------|------------------|-----------------------------|-----------------|
|段基址的| | | |
|高8位 |Segment Attributes| 段基址的低24位 | 段界限的低16位 |
| 24~31 | 段属性,占用两 | 0~23 | 0~15 |
| | 个字节 | | |
|--------|------------------|-----------------------------|-----------------|
| |
| |
_________| |_____________________________
| 15 14 13 12 11 8 7 6 5 3 0|
|---|---|---|---|-------------|---|--- -|---|------------|
| G | D |0 |AVL|段界限的高4位| P | DPL |DT | TYPE |
|---|---|---|---|--- ---------|---|-----|---|------------|

段基址和段界限都被安排在描述符的两个域中,主要是来看段的属性:
a.G(第15位),这是段界限粒度,即是说段界限到底是以字节为单还是以4K字节为单位。G=0表示段界限是字节,G=1表示段界限为4K字节。
b.D(第14位),D是一个很特殊的位,在描述可执行段,向低扩展数据段或者由SS寄存器寻址的段。 在描述可执行段的描述符中,D位决定了指令使用的地址及操作数据默认的大小,D=1表示默认情况下使用32位地址及32位或8位操作数,这样的代码段称为 32位代码段;D=0表示默认情况下使用16位地址及16位操作数或8位操作数,这样的代码段称为16位代码段;在向低扩展的数据段中,D=1表示段的上 部界限为4G,D=0表示段的上部界限为64K;在描述由SS寄存器寻址的段中,该位决定使用隐式的堆栈访问指令使用何种堆栈指针寄存器。D=1表示使用 32位堆栈指针寄存器ESP,D=0表示使用16位堆栈指针寄存器SP,隐式的堆栈访问指令指的是那些指令中没有明显对SP或ESP进行操作的指令,比如 说PUSH,POP,PUSHA,POPA,PUSHAD,POPAD都属于隐式的堆栈访问指令。
c.0(第13位),这一位恒为0,为80386以后的处理器保留的。
d.AVL(第12位),软件可利用位,主要是为了保持和以后的处理兼容。
e.第11位到第8位是段界限的高4位。
f.P(第7位),存在位,P=1表示描述符对转换地址有效。P=0表示描述符对转换地址无效,如果使用该描述符将会引起异常。
g.DPL(Descriptor Privelege Level)描述符特权级,共2位,它规定了所述段的特权级别,用于特权检查,以决定是否能对该段进行访问。
h.DT(Descriptor Type)描述符的类型,DT=0表示存储段描述符,DT=0表示系统段描述符和门描述符。
i.TYPE,共4位,说明存储段的具体属性:
TYPE0:指示描述符是否被访问,用A标记,A=0表示描述符未被访问,A=1表示描述符已被访问。
TYPE1:根据TYPE3来确定。
TYPE2:根据TYPE3来确定。
TYPE3:指示描述符所描述的段是数据段还是代码段,用E标记。E=0表示是不可执行段,是数据段,对应的描述符也就是数据段描述符。E=1表示是可执行段,也就是代码段,对就的描述符也就是代码段描述符。
如果TYPE3=0,也就是说描述符是数据段描述符,那么TYPE1指示该数据段是否可写,用W标记。 W=0表示对应的数据段不可写,只读。W=1表示对应的数据段可写。TYPE2则指示数据段的扩展方向,用ED标记。ED=0表示向高端扩展,ED=1表 示向低端扩展。
如果TYPE3=1,也就是说描述符是代码段描述符,那么TYPE1指示该代码段是否可读,用符号R标 记。R=0表示对应的代码段不可读,只能执行,R=1表示对应的代码可读可执行。TYPE2则指示所描述的代码段是否是一致代码段,用C表示。C=0表示 代码段不是一致代码段,C=1表示是一致代码段。
TYPE3-TYPE0这四位可以列成一个表:
___________________________________________________________________________________
|0000 |只读 |
|_____|____________________________________________________________________________|
|0001 |只读,已访问 |
|_____|____________________________________________________________________________|
|0010 |可读,可写 |
|_____|____________________________________________________________________________|
|0011 |读写,已访问 |
|_____|____________________________________________________________________________|
|0100 |只读,向低扩展 |
|_____|____________________________________________________________________________|
|0101 |只读,向低扩展 |
|_____|____________________________________________________________________________|
|0110 |读/写,向低扩展 |
|_____|____________________________________________________________________________|
|0111 |读/写,向低扩展,已访问 |
|_____|____________________________________________________________________________|
|1000 |只执行 |
|_____|____________________________________________________________________________|
|1001 |只执行,已访问 |
|_____|____________________________________________________________________________|
|1010 |可执行,可读 |
|_____|____________________
________________________________________________________|
|1011 |可执行,可读,已访问 |
|_____|____________________________________________________________________________|
|1100 |只执行,一致代码段 |
|_____|____________________________________________________________________________|
|1101 |只执行,一致代码段,已访问 |
|_____|____________________________________________________________________________|
|1110 |可执行,可读,一致代码段 |
|_____|____________________________________________________________________________|
|1111 |可执行,可读,一致代码段,已访问 |
|_____|____________________________________________________________________________|
存储段描述符的结构可以这样定义:
DESCRIPTOR STRUCT
Segment_LimitL16 DW 0;段界限的低16位
Segment_BaseL16 DW 0;段基址的低16位
Segment_BaseM8 DB 0;段基址的中间8位
Segment_BaseH8 DB 0;段基址的高8位
Segment_Attributes DW 0;段属性
DESCRIPTOR ENDS
一个任务有多个段,每个段都有一个描述符。因此在80386下,为了方便管理这些段描述符,将描述符组 成一个线性表,称之为描述符表。在80386下有三种描述符表:GDT(Global Descriptor Table),LDT(Local Descriptor Table),IDT(Interrupt Descriptor Table)。在整个系统中全局描述符表GDT和中断描述符表只有一张,局部描述符表可以由若干张。每个描述符表都形成一个特殊的16位数据段,这样的特 殊数据段最多可以有8192个描述符,具体使用哪一个段描述符,由段的选择子来确定。每个任务都有自已的局部描述符表LDT,它包含自已的代码段,数据 段,堆栈段,也包含该任务使用的一些门描述符。随着任务的切换,LDT也跟着切换。GDT包含每一个任务都可能或可以访问的段的描述符,通常包含描述操作 系统所用的代码段,数据段以及堆栈段的描述符,也包含描述任务LDT的描述符。在任务切换时,并不切换GDT。一个任务的整个虚拟地址空间可以分为相等的 两半,一半空间的描述符在全局描述符表GDT中,一半空的描述符在局部描述符表LDT中。由于全局描述符表和局部描述符表都可以包含最多为8192个描述 符,而每个描述符所描述的段的最大长度为4G,因此最大的虚拟地址空间为:8192*4G*2=64TB。
段选择子用来确定使用描述符表中的哪一个描述符。实式模式下逻辑地址由段地址*16再加上段内偏移地 址;保护模式下虚拟地址空间由段选择子和段内偏移来确定,和实式模式比较,段选择子代替了段值,实际上通过段选择子就可以确定了段基址。选择子的高13位 是描述符表中的索引号,用来确定描述符,因为是13位,所以说最多可以有2的13次方8192个描述符,索引号:0-8191。标记TI指示是从全局描述 符中读取描述符还是从局部描述符表中读取描述符。TI=0指示是从全局描述符表中读取描述符,TI=1指示从局部描述符表读取描述符。RPL表示请求特权 级,用于特权检查。假设段选择子为88H,则表示请求的特权级别是0,从全局描述表中读取描述表,描述符的索引号为11H。有一个特殊的选择子称为空选择 子,它的Index=0(即高13位为0),TI=0,RPL则可以为任意值。当用空选择子对存储器进行访问,会出现异常。空选择子对应于全局描述表中的 第0个描述符,因此全局描述符表中的第0个描述符总是不会被访问。如果TI=1,那么就不是空选择子,它指定的是当前局部描述符表中的第0个描述符。为了 更快地从段选择子中获得段的基本信息(段基址,段界限,段属性),从80386开始为每个段寄存器在硬件上配备了段描述符高速缓冲存储器,对我们写程序的 人来讲,它是不可编程的。有了这种高速缓冲寄存器后,每当将选择子装入段寄存器后,处理器将自动装入描述符表中相应的描述符,并将描述表的信息装入到高速 缓冲寄存器,这样可以加快访问速度,以下是段选择子的结构示意图:

15________________________________________________________________3__2__1_____0
| |TI | RPL |
|________________________________________________________________|___|________|

80386 ASM程序设计基础–80386实模式下编程II

虽然80386处理器要较以前的处理器的功能大大增强,但这些功能只能在保护模式下才能全部得到发挥。在实模式下最大寻址空间只有1M,但在保护模式最大 寻址空间可达4G,可以访问到所有的物理内存。同时由于引入虚拟内存的概念,在程序设计中可使用的地址空间为64TB。80386处理器采用了可扩充的分 段管理和可选的分页管理机制,这两个存储管理机制由MMU(Memory Management Unit)部件来实现。因此,如果在80386下进行实模式编程,这时的80386处理器相当于一功能更强大,运行速度更快的8086处理器。80386 提供对虚拟存储器的支持,虚拟存储器的理论基础就是:速度非常快的内存储器和海量的外存储器,所以它是一种软硬件结合的技术,它能够提供比物理内存大得多 的存储空间。
80386下的段具有三个属性:段基址,段界限,段属性,通常描述段的称作段描述符(Segment Descriptor),而描述符通常放在一个线性表中,这种线性表又分为:GDT(Global Descriptor Table),LDT(Local Descriptor Table),IDT(Interrupt Descriptor Table),通常用一个叫做选择子的东西去确定使用上述三个线性表中哪一个描述符。程序中使用的地址空间就是虚拟地址空间,上面已经说过80386下虚 拟地址空间可达到64TB(后面将解释为什么可以达到64TB),虚拟地址空间由一个选择子和段内偏移组成,这是因为通过段的选择子我们可以得到该段的描 述符,而在描述符中又说明了段的基址,段的界限及段的属性,再加上段的偏移就可以得到虚拟地址空间。不过请注意,这里并没有将段基址乘以16再加上偏移地 址,这是保护模式与实式模式的区别之一。很明显,任何数据都必须装入到物理内存才能够被存储器处理,所以二维的虚拟地址空间必须转换成一维的物理地址。同 时,由于每个任务都有自已的虚拟地址空间,为了防止多个并行任务将虚拟地址空间映射同一物理地址空间采用线性地址空间隔离虚拟地址和物理地址,线性地址空 间由一维的线性地址构成,线性地址空间与物理地址空间对等,线性地址为32位,可寻址空间为4GB(物理地址空间最大也可以达到4GB,址址为32位,所 以说线性地址空间与物理地址空间对等)。下面是80386虚拟地址空间与物理址空间的转换示意图:

|----------| |------------| |--------| |------------------| |--------|
| 虚拟地址 |------>|分段管理部件|------>|线性地址|---|--->|可选的分页管理部件|---|-->|物理地址|
|----|-----| |------------| |--------| | |------------------| | |--------|
|------|-------| | |
| | |---------------------------|
|----------| |---------|
| 选择子 | | 段内偏移|
|----------| |---------|

地址映射过程中,通过分段管理部件将虚拟地址空间转换成线性地址,这一步是必然存在的。如果在程序中启 用了分页管理机制,那么线性地址还要经过分页管理部件的处理才得到最后的物理地址。如果没有采用分页管理机制,那么得到的线性地址就是物理地址。分页管理 部件的主要的工作机制在于将线性地址和物理地址划分成大小相同的块,通过在建立两者之间的页表来建立对应关系。分段管理机制使用大小可变的存储块,使用分 段管理机制适合处理复杂系统的逻辑分段。分页管理机制使用固定大小的块,所以它适合管理物理存储器,分页管理机制能够更有效地使用虚拟地址空间。
80386支持多任务,因此对各个任务进行保护是非常必要的,对任务的保护可分为:同一任务内的保护,不同任务之间的保护。
a.同一任务内的保护,在同一任务内定义有四种特权级别(Previlege Level),将这些特权级别分配给段中的代码和数据,把最高的特权级别分配给最重要的数据和最可信任的代码,将较低级别的特权分给一般的代码和不重要的 数据。特权级别用0~3来表示,用数字0表示最高特权级别,用数字3表示最低特权级别,在比较特权级别时不使用大于或小于,而是使用外层或里层来比较,很 明显特权级别为0表示最里层,特别级别为3表示最外层。任何一个存储段(程序直接进行访问的代码段和数据段)都有一个特权级别,在一个程序试图访问这个存 储时,就会进行特权级别的比较,如果小于或等于(如果等于表明同级,小于则表明是内层)处该存储段的特权级别就可以对该存储段进行访问。任务在特定时刻下 的特权级别称为CPL(Current Previlege Level),看一简单的结构示意图:

|---------|-------|
| CodeA | DataA | 特权级别为0
|---------|-------|
|---------|-------|
| CodeB | DataB | 特权级别为1
|---------|-------|
|---------|-------|
| CodeC | DataC | 特权级别为2
|---------|-------|
|---------|-------|
| CodeD | DataD | 特权级别为3
|---------|-------|

CodeA可以访问DataA,CodeB,DataB,CodeC,DataC,CodeD,DataD
CodeB可以访问Datab,CodeC,DataC,CodeD,DataD,但不可以访问CodeA,DataA
CodeC可以访问DataC,CodeD,DataD,但不可以访问CodeA,DataA,CodeB,DataB
CodeD处在最外层,只能访问同级的DataD,不可以访问CodeA,DataA,CodeB,DataB,CodeC,DataC
通常应用程序放在最外层,但由于每个应用程序的虚拟地址空间不同,因此它们被隔离保护。这种特权级别的 典型用法就是:将操作系统的核心放在0层,操作系统的其余部分放在1级,2级留给中间软件使用,3级放应用程序,这样的安排的好处在于:操作系统的核心因 为放在0层,因此它可以访问任务中所有的存储段,而1级的部分操作系统可以访问除0级以外的所有存储段,应用程序只能访问自身的存储段。
b.不同任务间的保护,通过把每个任务放在不同的虚拟地址空间来实现隔离保护,虚拟地址到物理地址之间 的映射由每个任务中的映射函数来决定,随着任务切换,映射函数也跟着切换,这样可以保证任务A映射到物理内存中的区域与任务B映射到内存中的区域是不同 的,尽管有可能它们的虚拟地址空间相同,但它们最终在物理内存中的位置是不同的,从而起到了保护作用

80386 ASM程序设计基础–80386实模式下编程

80386实模式下编程
80386在实模式下是一个更快的8086,它不但可以进行32位操作,而且还可以进32位寻址,并且 还可以使用80386的扩展指令。不过,由于是在实模下,寻址的最大空间为1M。在一个段内,段的最大长度不超过64K,否则就会发生异常。
在8086下定义一个段的完整格式是:
段名 [定位类型] [组合类型] [‘类别’]
80386下定义一个段的完整格式是:
段名 [定位类型] [组合类型] [‘类别’] [属性类型]
说明:属性类型有两种:USE32和USE16,USE32表示32位段,USE16表示16位段。如 果你在程序中用到伪指令.386,那么默认的属性类型就是USE32(32位段),如果没有用伪指令指定CPU的类型,那么默认的属性类型就是 USE16,在实方式下只能使用16位段,即用USE16。
eg:
CSEG PARA PUBLIC USE32;定义一个32位的段
AA DW ?

BB DD ?
CC DB ?
DD DW ?
EE DW 0,0,0.....
CSEG ENDS
由于在80386中用到了66H操作前缀和67H地址前缀,因此尽管在实式模式下,只要设定的CPU类 型是80386,仍然可以进行32位操作,可以进行32位寻址,66H,67H这两个前缀无需程序员在程序中书写,汇编程序会自动加上的。只要在程序中对 32位操作数进行访问,或进行32位寻址,那么就会加上操作数前缀66H和地址前缀67H。相反,如果在32位段中对16位或8位的访问,汇编程序中也会 加上这两个前缀。
下面将给出一个例子程序,演示一下在80386的实模式下编程的方法与技巧(这是从网上down的一个程序,不是我写的,但我会作详细的解剖,并与8086下的程序设计作出比较):
用十进制,十六进制,二进制三种形式显示双字存储单元F000:1234中的内容
|------------------MAIN PROC------------|
| .386 |
| code segment para public 'code' use16 |
| assume cs:code |
| begin: |
| mov ax,0f000h |
| mov fs,ax |
| mov eax,fs:[1234H] |
| call todec |
| call newline |
| call tohex |
| mov al,'H' |
| call echo |
| call newline |
| call tobin |
| mov al,'B' |
| call echo |
| call newline |
| mov ah,4ch |
| int 21h |
|---------------------------------------|
;sub-function todec
todec proc near
pushad
mov ebx,10
xor cx,cx
dec1:
xor edx,edx
div ebx
push dx
inc cx
or eax,eax
jnz dec1
dec2:
pop ax
call toasc
call echo
loop dec2
popad
ret
todec endp

;sub-function tobin
tobin proc near
push eax
push ecx
push edx
bsr edx,eax
jnz bin1
xor dx,dx
bin1:
mov cl,31
sub cl,dl
shl eax,cl
mov cx,dx
inc cx
mov edx,eax
bin2:
rol edx,1
mov al,'0'
adc al,0
call echo
loop bin2
pop edx
pop ecx
pop eax
ret
tobin endp

;sub-function tohex
tohex proc near
countb=8
enter countb,0
movzx ebp,bp
mov ecx,countb
mov edx,eax
hex1:
mov al,dl
and al,0fh
mov [ebp-countb+ecx-1],al
ror edx,4
loop hex1
mov cx,countb
xor ebx,ebx
hex2:
cmp byte ptr [ebp-countb+ebx],0
jnz hex3
inc ebx
loop hex2
dec ebx
mov cx,1
hex3:
mov al,[ebp-countb+ebx]
inc ebx
call toasc
call echo
loop hex3
leave
ret
tohex endp

;sub-function toasc
toasc proc near
and al,0fh
cmp al,'0'
cmp al,'9'
seta dl
movzx dx,dl
imul dx,7
add al,dl
toasc1:ret
toasc endp

;sub-function newline
newline proc near
push dx
push ax
mov dl,0dh
mov ah,2
int 21
mov dl,0ah
int 21
pop ax
pop dx
ret
newline endp

echo proc near
push ax
push dx
mov dl,al
mov ah,2
int 21h
pop dx
pop ax
echo endp
剖析:
先来看主程序框架,下面就是MAIN PROC:
|------------------MAIN PROC-------------------------------|
|.386;定义处理器的类型为386表示可以使用所有80386指令 |
| code segment para public 'code' use16 |
| assume cs:code |
| begin: |
| mov ax,0f000h |
| mov fs,ax;将f000h装入段寄存器fs |
| mov eax,fs:[1234H];将1234H内存单元中的双字送给寄存器EAX|
| call todec;调用子过程todec |
| call newline;调用子过程newline进行回车换行 |
| mov eax,fs:[1234h]; |
| call tohex;调用子过程tohex |
| mov al,'H' |
| call echo;显示字符H |
| call newline; |
| mov eax,fs:[1234H] |
| call tobin;调用子过程tobin |
| mov al,'B' |
| call echo


| call newline |
| mov ah,4ch |
| int 21h |
|----------------------------------------------------------|
主程序中的内容一目了然,很简单。和8086下唯一不同的是就是要用伪指令定义CPU的类型,并且段寄存器的定义多了一个属性类型USE16,再就是32位操作,使用80386的指令,其它的和8086下没有什么区别。
重点是要分析几个过程,从网上down下来时,过程newline和toasc没有实现代码,因为这很 简单,所以上述toasc,newline,echo的过程体是由我写进去的,这两个过程体代码不多而且非常简单,就不作介绍了。重点介绍todec, tobin,tohex。
a.子过程todec,这个子过程的主要功能是将f000:1234双字单元的内容用十进制显示,下面就来看每一行代码:
|-----------------------------------------------------------|
|todec proc near |
| pushad |
| mov ebx,10 |
| xor cx,cx |
| dec1: |
| xor edx,edx |
| div ebx |
| push dx |
| inc cx |
| or eax,eax |
| jnz dec1 |
| dec2: |
| pop ax |
| call toasc |
| call echo |
| loop dec2 |
| popad |
| ret |
|todec endp |
|-----------------------------------------------------------|
分析:将一个数用十进制数来表示,要它对它进行除以10的运算,得到商和余数。再将商除以10,如此循 环直到商为0为止,在这个过程中得到的一系列的模(余数)就是十进制数系列。在主程序中,已经将f000:1234双字单元的内容放到EAX寄存器中,由 于后来要用十六进制数,二进制数显示,所以EAX寄存器的内容不允许改变,因此在子过程的一开始,要将EAX的内容先入栈,所以子过程的一开始就用 PUSHAD将8个32位通用寄存器的内容全部入栈。在标号dec1不断地进行除以10运算,将所得到的余数全部入栈,同时用cx进行计数。在标号 dec2中,逐个弹出在标号dec1中得到的余数,然后分别将它们显示出来,这样就可以将该存储单元中的内容用十进数表示,下面解释每一条指令的功能:
a1.pushad;将8个32位通用寄存器全部入栈
a2.xor cx,cx;cx清0
a3.mov ebx,10;10=>ebx<
br /> a4.xor edx,edx;edx清0
a5.div ebx;edx存放高32位,不过是0,EAX中存放低32位,即ffff:[1234]双字的内容;除法得到的商放在EAX,余数放在EDX
a6.push dx;将edx的低16位dx入栈
a7.inc cx;cx+1=>cx
a8.or eax,eax;对eax进行或操作,主要是用来判断eax是否为0,即判断商是否为0,从而判断是否应该结束标号为dec1的循环。
a9.jnz dec1
a10.pop ax;将放在堆栈中的余数逐个弹出到ax中
a11.call toasc;显示ax的内容
a12.call echo
a13.loop dec2;将所有的余数显示完毕
a14.popad;8个32位通用寄存器全部出栈
a15.ret

b.子过程tohex
PUSH BP
SP=>BP
SP<=SP-CNT1
|------------------------------------------------------------|
|tohex proc near |
| countb=8 |
| enter countb,0 |
| movzx ebp,bp |
| mov ecx,countb |
| mov edx,eax |
|hex1: |
| mov al,dl |
| and al,0fh |
| mov [ebp-countb+ecx-1],al |
| ror edx,4 |
| loop hex1 |
| mov cx,countb |
| xor ebx,ebx |
|hex2: |
| cmp byte ptr [ebp-countb+ebx],0 |
| jnz hex3 |
| inc ebx |
| loop hex2 |
| dec ebx |
| mov cx,1 |
|hex3: |
| mov al,[ebp-countb+ebx] |
| inc ebx |
| call toasc |
| call echo |
| loop hex3 |
| leave |
| ret |
|tohex endp |
|------------------------------------------------------------|
分析:该子过程的功能是将f000:1234双字单元的内容以16进制数显示出来,首先来考虑一下将一 个数以16进制数表示出来的算法,事实上在汇编语言中操作数一直都是以十六进制表示的。因此,在这个子过程中不可以像上一个子过程一样,通过不断的除法取 模得到结果。事实上,我们只需要将32位操作,以每半个字节(四位)的内容显示出来就可以了,有了这一编程思想,就很容易看懂上面的子过程。当然你们会 问,为什么要每次只显示半个字节而不显示一个字节呢?呵呵,十六进制的十六个数是从0000-1111,不就是半个字节了。所以要循环8次才可以显示出 32位的EAX,所以这里用ror指令来不断循环移位,每次右移4位放到dl的低4位中。这8个半字节分别放在[ebp-1]至[ebp-8]的存储单元 中。不过,存储的顺序是由低位到高位,如果就这样显示结果肯定显示反了。标号hex2,hex3的主要功能是用来判断f000:1234双字单元的内容是 否为0,如果为0,只需要将最后结果显示一个0即可,否则就显示出8位内容。下面是每条指令的功能:
b1.countb=8;伪指令定义一局部变量countb,其值为8
b2.enter countb,0;建立堆栈框架指令
b3.movzx ebp,bp;对bp进行零扩展
b4.mov ecx,countb;8=>ecx
b5.mov edx,eax;将eax=>edx
b6.mov al,dl
b7.and al,0fh;取低4位
b8.mov [ebp-countb+ecx-1],al;将8个半字节的内容逐一送到[ebp-1]至[ebp-8]的内存单元中
b9.ror edx,4;对edx进行循环右移,每次移动4位
b10.loop hex1
b11.mov cx,countb
b12.xor ebx,ebx;ebx清0
b13.cmp byte ptr [ebp-countb+ebx],0;下面的语句主要用来判断源操作数f000:1234的内容是否为0,如果是0,就在屏幕上只显示一个0
b14.jnz hex3
b15.inc ebx
b16.loop hex2
b17.dec ebx
b18.mov cx,1
b19.mov al,[ebp-countb+ebx];逐一显示[ebp-8]到[ebp-1]的内容。
b20.inc ebx
b21.call toasc
b22.call echo
b23.loop hex3
b24.leave;释放堆栈框架
b25.ret

c.子过程tobin
|---------------------------------------|
|tobin proc near |
| push eax |
| push ecx |
| push edx |
| bsr edx,eax |
| jnz bin1 |
| xor dx,dx |
|bin1: |
| mov cl,31 |
| sub cl,dl |
| shl eax,cl |

| mov cx,dx |
| inc cx
| mov edx,eax |
|bin2:

| rol edx,1
| mov al,'0'
| adc al,0
| call echo
| loop bin2
| pop edx |
| pop ecx
| pop eax
| ret
|tobin endp
|---------------------------------------|
分析:将一个数用二进制数显示出来,只需要用ROL指令就可以了。这里作者写的程序就是这个思路,在标 号bin1中主要判断f000:1234单元的内容是否为0,如果为0,那么只需要在屏幕上显示一个0就可以了。否则的话,就用ROL指令对源操作数移位 32位,从最高位31位到最低位逐一显示出来,程序设计思路很简单,没有什么复杂的算法,下面看每一条指令的含义:
c1.push eax;eax入栈
c2.push ecx;ecx入栈
c3.push edx;edx入栈
c4.bsr edx,eax;对eax进行扫描,并把第一个为1的位号送给edx
c5.jnz bin1;如果eax不为0,就跳到c7去执行
c6.xor dx,dx;如果eax为0,就将dx清0
c7.mov cl,31;从c7到c12主要用来设置计数器cx,如果eax=0,那么就设置cx=1,如果eax不等于0,那么就设置ecx=32
c8.sub cl,dl
c9.shl eax,cl
c10.mov cx,dx
c11.inc cx
c12.mov edx,eax
c13.rol edx,1;从c13到c15主要用来显示二进制数据,顺序是从最高位31位到最低位0位
c14.mov al,'0'
c15.adc al,0
c16.call echo
c17.loop bin2
c18.pop edx;edx出栈
c19.pop ecx;ecx出栈
c20.pop eax;eax出栈
c21.ret
在后续的篇幅里将主要介绍保护式下的段页管理机制及及如何在保护模下编程。

80386 ASM程序设计基础–高级语言支持,条件字节设置指令

高级语言支持,条件字节设置指令
AA.高级语言支持指令,开始于80186,主要是用来简化高级语言的某些特征,总共有3条指令:ENTER,LEAVE,BOUND
a.ENTER,LEAVE,建立与释放堆栈框架命令。在C语言中,栈不仅用来向函数传递入口参数,而 且在函数内部的局部变量也存放在栈中。为了准确地存取这些这些局变量和准确地获得入口参数,就需要建立堆栈框架,先看一个小程序:
//C Programming-Language
int sum(int x,int y)
{
int sum;
sum=x+y;
return sum;
}
//The corresponding ASM codes lists below
_sum proc near;注意C语言中函数参数的入栈方式是从右向左,即先是参数y入栈,再是x入栈,再是函数的返回地址入栈
push bp
mov bp,sp;建立堆栈框架
sub sp,2

mov ax,word ptr [bp+4];取参数x
add ax,word ptr [bp+6];加参数y
mov word ptr [bp-2],ax
mov ax,word ptr [bp-2]
mov sp,bp;释放栈框架
pop bp
ret
_sum endp
此时栈顶的示意图是:
|----------------------|
| BP |<====SP
|----------------------|
| 函数返回地址 |<====BP+2
|----------------------|
| 参数x |<====BP+4
|----------------------|
| 参数y |<====BP+6
|----------------------|
| ...... |<====BP+8
|----------------------|
| ........ |<====BP+n,n是一能被2整除的数
|----------------------|
如果用建立和释放堆栈框架指令,那么对应的汇编程序应该是:
_sum proc near
enter 2,0;建立栈框架
mov ax,word ptr [bp+4];取参数x
add ax,word ptr [bp+6];加参数y
mov word ptr [bp-2],ax
mov ax,word ptr [bp-2]
leave;释放栈框架
ret
_sum endp
b.建立栈框架指令ENTER,格式如下:ENTER CNT1,CNT2。其中CNT1表示框架的大小,即子程序中需要放在栈中局部变量的字节数;CNT2是立即数,表示子程序嵌套级别,即从调用框架复制到当前框架的指针数。在立即数CNT2为0时,ENTER指令的实过程是:
PUSH BP
SP=>BP
SP<=SP-CNT1
c.释放栈框架指令LEAVE,其具体实现过程:
8086:
BP=>SP
POP BP
80386:
EBP=>ESP
POP EBP
d.ENTER和LEAVE指令均不影响标志寄存器中的各标志位,同时LEAVE指令只负责释放栈框架,并不负责函数返回。因此,要在LEAVE指令后安排一条返回指令。

BB.条件字节设置指令
这是80386新增的一组指令集,将会在后面全部列表出来。条件字节设置指令的格式:
SETxx OPRD
xx是助记符的一部分,OPRD只能是8位的寄存器或存储单元。
eg:
SETO AL;表示当溢出标志位为1时,即OF=1,将AL置1,否则AL清0
SETNC CH;表示当CF=0时,将CH置1,否则将CH清0
SETNA BYTE PTR [100];表示当AF=0,将DS:[100]这一个字置1,否则将它清0
a.SETZ OPRD;等于0时(ZF=1),置OPRD为1,否则清0
b.SETE OPRD;同a
c.SETNZ OPRD;不等于0时(ZF=0),置OPRD为1,否则清0
d.SETNE OPRD;同c
e.SETS OPRD;为负数时(SF=1)置OPRD为1,否则清0
f.SETNS OPRD;同e正好相反(SF=0)
g.SETO OPRD;OF=1,置OPRD为1,否则清0
h.SETNO OPRD;同g正好相反
i.SETP OPRD;偶(PF=1)置1
j.SETPE OPRD;同i
k.SETNP OPRD;奇(PF=0)置1
l.SETPO OPRD;同k
m.SETB OPRD;低于置OPRD为1,否则清0,这是针对无符号数的
n.SETNAE OPRD;不高于即低于或等于时置OPRD为1,否则清0,这是针对无符号数的
o.SETC OPRD;CF=1,置OPRD为1,否则清0
p.SETNB OPRD;高于或等于时,置OPRD为1,否则清0,这是针对无符号数的
q.SETAE OPRD;高于时置OPRD为1,否则清0,这是针对无符号数的
r.SETNC OPRD;CF=0时,置OPRD为1,否则清0,这是针对无符号数的
s.SETBE OPRD;低于或等于时,置OPRD为1,否则清0,这是针对无符号数的,CF|ZF=1
t.SETNA OPRD;同s,这是针对无符号数的,CF|ZF=1
u.SETNBE OPRD;高于时置OPRD为1,否则清0,这是针对无符号数的,CF OR ZF=0
v.SETA OPRD;同u,这是针对无符号数的,CF OR ZF=0
w.SETL OPRD;小于时,置OPRD为1,否则清0,这是针对有符号数的
x.SETNGE OPRD;同w,这是针对有符号数的
y.SETNL OPRD;大于或等于时,置OPR为1,否则清0,这是针对有符号数的
z.SETGE OPRD;同y,这是针对有符号数的
a1.SETLE OPRD;小于或等于时,置OPRD为1,否则清0,这是针对有符号数的
a2.SETNG OPRD;同a1,这是针对有符号数的
a3.SETNLE;大于时,置OPRD为1,否则清0,这是针对有符号数的
a4.SETG;同a3,这是针对有符号数的

80386 ASM程序设计基础–控制转移指令,串操作指令

控制转移指令,串操作指令
80386控制转移指令包括:转移指令,循环指令,过程调用和返回指令。
A.转移指令包括无条件转移指令JMP和条件转移指令,无条件转移指令分为段内直接转移,段内间接转 移,段间直接转移,段间间接转移。由于80386有保护模式和实模式,在实模式下,段内转移的范围在-128~127,段间转移最大范围为64K。在保护 模式需要用48位指针,即CS:EIP(16位+32位)。条件转移指令有很多包括JCXZ,JECXZ,JBE,JAE,JA,JB等,其用法和 8086相似。

B.循环指令LOOP,LOOPZ,LOO0PE,LOOPNZ,LOOPNE,TASM支持助记符 LOOP,LOOPWE,LOOPWZ,LOOPWNZ,LOOPWNE,LOOPD,LOOPWD,LOOPDE,LOOPDNE,LOOPDNZ。以 CX作为计数器时,就可用LOOP,LOOPWE,LOOPWZ,LOOPWNZ,LOOPWNE;在以ECX作为计数器时,以LOOPD, LOOPDE,LOOPDZ,LOOPDNZ,LOOPDNE,下面的一段例子可以说明问题:
ABC PROC
MOV CX,100H
AA:
;ADD YOUR CODES HERE
LOOP AA
ABC END

C.过程调用和返回调用CALL,RET
这两个指令与8086的用法相同,但由于80386下有实模式和保护模式下。在实模式下,无论是段内调 用还是段间调用均采用32位指针,即CS:IP,它们的用法与8086下相同。在保护模式下,段间调用和段内调用均用48位指针,即ECS:IP。RET 用于返回,具体实现过程会比较复杂,在介绍完80386的地址的管理机制后会作介绍,先介绍一下以下CALL指令在8086中的用法:
a.段内直接转移,具体格式:CALL 过程名。此时CS不入栈,IP的内栈入栈,入栈后再将加上目的地址与CALL指令的下一条指令的偏移地址之差值就可以转移到目的地址,详细过程:
SP-2=>SP;将堆栈指针SP减2
(SP)<=IP;将IP进栈
IP+偏移地址之差;转到目的地址
b.段内间接转移,具体格式:CALL OPRD,那么在这里OPRD可以寄存器或内存单元,它的具体实现过程:
SP-2=>SP;将堆栈指针SP减2
(SP)<=IP;将IP进栈
IP<=(OPRD);转到目的地址
同a一样,CS不入栈
c.段间直接转移,具体格式:CALL 过程名 [FAR],此时CS,IP均要入栈,详细的实现过程:
SP-2=>SP;将堆栈指针减2
(SP)<=CS;将CS入栈
SP-2=>SP;将堆栈指针再减2
(SP)<=IP;将IP入栈
;装入新的CS,IP
IP<=过程入口的偏移地址
CS<=过程入口的段地址
d.段间间接转移,具体格式:CALL OPRD [FAR],此时CS,IP均要入栈,OPRD是32位,你知道在8086中没有32位寄存器。因此,这里的OPRD一定是存储单元,高16位是CS的值,低16位是IP值,详细的实现过程:
SP-2=>SP;将堆栈指针减2
(SP)<=CS;将CS入栈
SP-2=>SP;将堆栈指针再减2
(SP)<=IP;将IP入栈
;装入新的CS,IP
IP<=(OPRD+2,OPRD+3)
CS<=(OPRD,OPRD1)
e.段内返回
格式:RET。实际上它的实现过程:
(SP)=>IP;从当前栈顶弹出一个字,将它送给IP指令计数器
SP+2=>SP;SP
f.段间返回
格式:RET,实际上它的实现过程:
(SP)=>IP;IP出栈
SP+2=>SP;
(SP)=>CS;CS出栈
SP+2=>SP;

D.中断返回指令IRET
功能和用法与8086相同,这里顺便介绍一下8086的中断返回指令
IRET,具体的实现过程:
IP<=(SP);IP出栈
SP+2=>SP;
CS<=(SP);CS出栈
SP+2=>SP;
FLAGS<=(SP);标志寄存器出栈
SP+2=>SP;

E.串操作指令
80386在串操作指令方面增加了双字操作,在8086五条指令的基础上增加了INS,OUTS。
a.LOADSD,和8086的用法和功能相同,不过是对32位操作数操作。
b.STOSD,和8086的用法和功能相同,不过是对32位操作数操作。
c.CMPSD,和8086的用法和功能相同,不过是对32位操作数操作。
d.SCANSD,和8086的用法和功能相同,不过是对32位操作数操作。
e.MOVSD,和8086的用法和功能相同,不过是对32位操作数操作。
f.重复前缀REP,和8086的功能与用法相同,仍以CX为计数器,看下面的一小程序:
ROR ECX,2
REP MOVSD;以CX为计数器,每次传送双字
ROL ECX,1
REP MOVSW;以CX为计数器,每次传送一字
ROL ECX,1
REP MOVSB;以CX为计数器,每个传送一个字节
g.INSB,INSW,INSD,OUTSB,OUTSW,OUTSD
g1.INSB,串输入指令,以字节单位,该指令的功能是从DX指定的端口读入一个字节到ES:DI指定的内存单元中。
g2.INSW,串输入指令,以字单位,该指令的功能是从DX指定的端口读入一个字节到ES:DI指定的内存单元中。
g3.INSD,串输入指令,以双字单位,该指令的功能是从DX指定的端口读入一个字节到ES:DI指定的内存单元中。
g4.OUTSB, 串输出指令,以字节为单位,将DS:SI内存单元的内容送往DX指定的端口。
g5.OUTSW, 串输出指令,以字为单位,将DS:SI内存单元的内容送往DX指定的端口。
g6.OUTSD, 串输出指令,以双字为单位,将DS:SI内存单元的内容送往DX指定的端口。
g7.串输入和串输出指令不影响标志寄存器中的各标志位,串操作指令可以与REP一起使用

80386 ASM程序设计基础–算术运算指令,逻辑运算指令,移位指令

算术运算指令,逻辑运算指令,移位指令
AA.算术运算指令
A.加减法运算ADD,ADC,INC,SUB,SBB,DEC,CMP,NEG
a.ADD,和8086功能,用法相同,不过支持32位操作,下面的语句都是合法的。
ADD ESI,EDI
ADD EAX,DWORD PTR [1000H]
b.ADC,带进位的加法指令,即OPRDS+OPRDD+CF,其中OPRDS代表源操作数,OPRDD代表目的操作,CF代表进位标志位,功能和用法与8086相同,支持32位操作。
c.SUB,和8086相同,支持32位操作。
d.SBB,带进位的减法指令,即OPRDD-OPRDS-CF,其中OPRDS代表源操作数,OPRDD代表目的操作数,CF代表进位标志位,功能和用法与8086相同,支持32位操作。
e.DEC,减1操作,功能和用法与8086相同,支持32位操作。
f.CMP,比较操作,功能和用法与8086相同,支持32位操作。
g.NEG,求补操作,功能和用法与8086相同,支持32位操作。
h.INC 加1操作,功能和用法与8086相同,支持32位操作。

B.乘除法指令MUL,DIV,IMUL,IDIV
a.MUL,无符号数乘法指令,和8086功能用法一样,即指令中只给出一个操作,被乘数已默认,如果 指令给出的操作数是32位的话,被乘数默认为EAX,那么乘积将存放在EDX:EAX中,其中EDX存放高32位,EAX存放低32位,如果此时EDX= 0,即高32位为0的话,那么OF=0,CF=0,否则被置1。如果指令给出的操数作是16位的话,被乘数默认为AX那么乘积将放在DX:AX中,其中 DX中将存放高16位,AX中存放低16位。如果指令给出的操作数是8位的话,被乘数默认为AL,那么乘积将放在AX,AH中存放高8位,AL中存放低8 位。
b.DIV,无符号数的除法指令,和8086一样,指令给出一个操作数,被除数已默认。如果指令中给出的操作数为32,那么被除数将是EDX:EAX, 最终的商将存放在EAX, 余数将存放在EDX中。如果指令给出操作数为16位,那么被除数为EAX,最终得到的商放在AX,余数放在EAX的高16位。如果指令中给出的操作数为8位,那么被除数是16位,最终得到的商将放在AL中,余数放在AH中。
c.IMUL,有符号数的乘法指令,除了具有8086的用法外,有新的形式:
c1.IMUL DST,SRC;将源操作数SRC与目的操作DST相乘,并将结果送往DST。
c2.IMUL DST,SRC1,SRC2;将源操作数SRC1与源操作数SRC2相乘,并将结果送往DST。
使用这种形式必须遵守的规则,形式c1指令中目的操作数必须是16位或32位通寄存器,源操作数的长度 必须与目的操作的长度一样(8位立即数除外,即00H-FFH或80H-7FH),源操作数可以是通用寄存器,也可以是存储单元或立即数。形式c2指令中 的源操作数SRC1可以是通用寄存器也可以是存储单元,源操作数SRC2必须是立即数,DST必须是16位或32位通用寄存器。呵呵,对于这些规则无需去 问为什么,这是硬件的特性决定的,如果一定要问为什么,那只能问INTEL公司的硬件工程师了:)。同时,有一点要注意的是:这两种形式的指令,目的寄存 器的长度与源操作数长度一样(8位立即数除外),这样的话,该指令事实上对有符号数和无符号数是一样的,因为乘积的低位部分均存储在目的寄存器中,而高位 部分在这两种形式的指令中不予以存储。
d.IDIV,有符号数的除法指令,用法和8086相同,不过支持32位操作。

C.符号扩展指令CBW,CWD,CWDE,CDQ
a.CBW,前面已介绍,在第三篇。
b.CWD,前面已介绍,在第三篇。
c.CWDE,是80386新增的指令。格式:CWDE。功能:将AX的符号位扩展到EAX的高16位中。
d.CDQ,是80386新增的指令。格式:CDQ。功能,将EAX的符号位扩展到EDX中。
e.以上四条指令均不影响标志位。
f.举例说明:
;If AX=1234H,EAX=99991234H
CBW;After processing the instruction,AX=1234,DX=0000H
CDQ;After processing the instruction,EAX=99991234H,EDX=FFFFFFFFH

BB.逻辑运算指令和移位指令NOT,AND,OR,XOR,TEST,SAL,SAR,SHL,SHR,ROL,ROR,RCL,RCR,SHLD,SHRD
a.NOT,AND,OR,XOR,TEST这些指令的功能和用法与8086完全相同,不过它们支持32位操作。
b.TEST,测试指令,该指令测试的结果并不回送到目的操作数和源操数。之所以要使用这条的指令,主要是因为根据TEST指令得到的结果,进行程序的条件转移。
c.SAL,算术左移,功能和8086一样,但在8086中,如果在移位的位数超过1位,那么一定要移 位的位数放在CX寄存器中。在80386中,可以不用这样做,其它的移位指令也一样。除了这一点以外,用法和8086一样,当然也支持32位操作。以下的 语句均是合法的。
SHL AL,5;这在8086中是非法,但在80386中是合法的
SHL WORD PTR [SI],3
d.SAR,算术右移,将操作数右移指定的位数,但左边的符号位保持不变,移出的最低位进入CF标志位。
e.SHL,逻辑左移,用法和功能与SAL一样。
f.SHR,逻辑右移,将操作右移指定的位数,同时每移一位,左边用0补充,移出的最低位进入CF标志位。
g.说明:在80386中,实际移位的位数是指令中移位位数的低5位,也就是说移位位数的范围在0- 31或0-1FH,CF标志位总是保留着目的操作数最后被移出位的值。当移位位数大于操作数的长度时,CF被置0。如果移位位数为1,移位前后的结果的符 号位都是一样,那么很明显的是该操作数经移位后没有移出,此时OF=0。这四条指令的移位示意图(我画的是16位操作数的移位示意图,8位和32依此类 推),SAL,SHL相当于乘法;SAR,SHR相当于除法。
SAL:
|-------------------------------------------------------------------------------------------|
|CF|<-|bit15|bit14|bit13|bit12|bit11|bit10|bit9|bit8|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|
|-- ----------------------------------------------------------------------------------------|
SHL:
|-------------------------------------------------------------------------------------------|
|CF|<-|bit15|bit14|bit13|bit12|bit11|bit10|bit9|bit8|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|
|--- ---------------------------------------------------------------------------------------|
SAR:
|--------------------------------------------------------------------------------------------|
|-|bit15|bit14|bit13|bit12|bit11|bit10|bit9|bit8|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|->|CF||
| |---|----------------------------------------------------------------------------------------|
| ^
|-----|最高位保持不变
SHR:
|--------------------------------------------------------------------------------------------|
0->|bit15|bit14|bit13|bit12|bit11|bit10|bit9|bit8|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|->|CF||
|--------------------------------------------------------------------------------------------|
h.ROL,循环左移,支持32位操作数,用法和8086一样。
i.ROR,循环右移,支持32位操作数,用法和8086一样。
j.RCL,带进位的循环左移,支持32位操作数,用法和8086一样。
k.RCR,带进位的循环右移,支持32位操作数,用法和8086一样。
l.ROL,ROR,RCL,RCR的移位示意图(仍然以16位操作数来画,8位/32位依次类推):
ROL:
|--------------------------------------------------------------------------------------------------|
|<-|bit15|bit14|bit13|bit12|bit11|bit10|bit9|bit
8|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|<--------|
|--------------------------------------------------------------------------------------------------|
|--------------------------------------------------------------------------------------------------|
ROR:
|-------------------------------------------------------------------------------------------|
|->|bit15|bit14|bit13|bit12|bit11|bit10|bit9|bit8|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|->|
|-------------------------------------------------------------------------------------------|
|-------------------------------------------------------------------------------------------|
RCL:
|-------------------------------------------------------------------------------------------------|
|<-|CF|<-|bit15|bit14|bit13|bit12|bit11|bit10|bit9|bit8|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|<-|
|-------------------------------------------------------------------------------------------------|
|-------------------------------------------------------------------------------------------------|
RCR:
|-------------------------------------------------------------------------------------------------|
|->|CF||
|---- --------------------------------------------------------------------------------------------|
|-------------------------------------------------------------------------------------------------|
m.SHLD,80386新增的双精度左位指令,指令格式:SHLD OPRD1,OPRD2,M
n.SHRD,80386新增的双精度右移指令,指令格式:SHRD,OPRD1,OPRD2,M
o.m,n这两条指令的使用规则是:源操作数OPRD1可以是16位或32位通用寄存器或者16位存储 单元或者32位存储单元,源操作数OPRD2必须是16位或32位通寄存器,M表示移位次数,可以是CL寄存器,也可以是8位立即数。功能:SHLD是将 源操作数OPRD1移M位,空出的位用OPRD2高端的M位来填补,源操作数OPRD2的内容不变,最后移出的位放在CF中;SHRD将源操作数 OPRD1移M位,空出的位用OPRD2低端M位来填补,源操作数OPRD2保持不变,最后移出的位放在CF中,对于这两条指令,当移位位数仅为1的话, 移出和移后的符号位不变的话,那么OF=0,如果符号位不一样的话,那OF=1。
p.这两条指令是80386新增的指令,举两个简单的例子加以说明:
p1.SHLD:
MOV AX,8321H
MOV DX,5678H
SHLD AX,DX,1
SHLD AX,DX,2
分析一下该指令的详细执行过程(用示意图, 第一个图画的就是AX的内容):
AX=8321h
|-------------------------------|
|1|0|0|0|0|0|1|1|0|0|1|0|0|0|0|1|
|-------------------------------|
根据指令SHLD AX,DX,1,先左移一位,得到AX=0642H:
|-------------------------------|
|0|0|0|0|0|1|1|0|0|1|0|0|0|0|1|0| CF=1
|-------------------------------|
经过上一步的移位后,AX的最后一位(即bit0)空出来,其值为0;根据指令的用法将用DX的第15位填充,填充后AX的内容为:
|-------------------------------|
|0|0|0|0|0|1|1|0|0|1|0|0|0|0|1|0|
|-------------------------------|
同时由于移位后AX的符号位与移位前AX的符号位不同,所以在移位过程中产生了溢出,OF=1,最后结果AX=0642H。
同理,SHLD AX,DX,2,执行完这条指令后,最后结果为AX=0644H
p2.SHRD:
MOV EAX,12345678H
MOV EDX,99994599H
SHRD AX,DX,1
SHRD AX,DX,2
分析一下该指令的详细执行过程(用示意图,第一个图画的是EAX的内容):
EAX=12345678H
|---------------------------------------------------------------|
|0|0|0|1|0|0|1|0|0|0|1|1|0|1|0|0|0|1|0|1|0|1|1|0|0|1|1|1|1|0|0|0|
|---------------------------------------------------------------|
根据指令SHRD AX,DX,1,将AX右移一位得到EAX=091A2B3EH:
|---------------------------------------------------------------|
|0|0|0|0|1|0|0|1|0|0|0|1|1|0|1|0|0|0|1|0|1|0|1|1|0|0|1|1|1|1|1|0|
|---------------------------------------------------------------|
经过上一步的移位后,EAX的最高位(第31位)空出来用0填充,根据指令的用法,最EDX的第0位来填充,填充后EAX的内容为:
|---------------------------------------------------------------|
|1|0|0|0|1|0|0|1|0|0|0|1|1|0|1|0|0|0|1|0|0|1|1|0|0|1|1|1|1|0|0|0|
|---------------------------------------------------------------|
即EAX=891A2B3EH,CF=0,OF=0
同理,指令SHRD AX,DX,2,执行完这条件指令后,最后结果为EAX=048D159C,CF=0,OF=0