1.概述
~~~~~~~~
GIF(Graphics Interchange Format,图形交换格式)文件是由 CompuServe公司开发的图形文件格式,版权所有,任何商业目的使用均须 CompuServe公司授权。
GIF图象是基于颜色列表的(存储的数据是该点的颜色对应于颜色列表的索引值),最多只支持8位(256色)。GIF文件内部分成许多存储块,用来存储多幅图象或者是决定图象表现行为的控制块,用以实现动画和交互式应用。GIF文件还通过LZW压缩算法压缩图象数据来减少图象尺寸(关于LZW算法和GIF数据压缩>>...)。
2.GIF文件存储结构
~~~~~~~~~~~~~~~~~~~
GIF文件内部是按块划分的,包括控制块( Control Block )和数据块(Data Sub-blocks)两种。控制块是控制数据块行为的,根据不同的控制块包含一些不同的控制参数;数据块只包含一些8-bit的字符流,由它前面的控制块来决定它的功能,每个数据块大小从0到255个字节,数据块的第一个字节指出这个数据块大小(字节数),计算数据块的大小时不包括这个字节,所以一个空的数据块有一个字节,那就是数据块的大小0x00。下表是一个数据块的结构:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
0 | 块大小 | Block Size - 块大小,不包括这个这个字节(不计算块大小自身) | |||||||
1 | Data Values - 块数据,8-bit的字符串 | ||||||||
2 | |||||||||
... | |||||||||
254 | |||||||||
255 |
一个GIF文件的结构可分为文件头(File Header)、GIF数据流(GIF Data Stream)和文件终结器(Trailer)三个部分。文件头包含GIF文件署名(Signature)和版本号(Version);GIF数据流由控制标识符、图象块(Image Block)和其他的一些扩展块组成;文件终结器只有一个值为0x3B的字符(';')表示文件结束。下表显示了一个GIF文件的组成结构:
GIF署名 | 文件头 | |||
版本号 | ||||
逻辑屏幕标识符 | GIF数据流 | |||
全局颜色列表 | ||||
... | ||||
图象标识符 | 图象块 | |||
图象局部颜色列表图 | ||||
基于颜色列表的图象数据 | ||||
... | ||||
GIF结尾 | 文件结尾 |
下面就具体介绍各个部分:
文件头部分(Header)
~~~~~~~~~~~~~~~~~
GIF署名(Signature)和版本号(Version)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
GIF署名用来确认一个文件是否是GIF格式的文件,这一部分由三个字符组成:"GIF";文件版本号也是由三个字节组成,可以为"87a"或"89a".具体描述见下表:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
1 | 'G' | GIF文件标识 | |||||||
2 | 'I' | ||||||||
3 | 'F' | ||||||||
4 | '8' | GIF文件版本号:87a - 1987年5月 89a - 1989年7月 | |||||||
5 | '7'或'9' | ||||||||
6 | 'a' |
GIF数据流部分(GIF Data Stream)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
逻辑屏幕标识符(Logical Screen Descriptor)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这一部分由7个字节组成,定义了GIF图象的大小(Logical Screen Width & Height)、颜色深度(Color Bits)、背景色(Blackground Color Index)以及有无全局颜色列表(Global Color Table)和颜色列表的索引数(Index Count),具体描述见下表:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT | |
1 | 逻辑屏幕宽度 | 像素数,定义GIF图象的宽度 | ||||||||
2 | ||||||||||
3 | 逻辑屏幕高度 | 像素数,定义GIF图象的高度 | ||||||||
4 | ||||||||||
5 | m | cr | s | pixel | 具体描述见下... | |||||
6 | 背景色 | 背景颜色(在全局颜色列表中的索引,如果没有全局颜色列表,该值没有意义) | ||||||||
7 | 像素宽高比 | 像素宽高比(Pixel Aspect Radio) |
m - 全局颜色列表标志(Global Color Table Flag),当置位时表示有全局颜色列表,pixel值有意义.
cr - 颜色深度(Color ResoluTion),cr+1确定图象的颜色深度.
s - 分类标志(Sort Flag),如果置位表示全局颜色列表分类排列.
pixel - 全局颜色列表大小,pixel+1确定颜色列表的索引数(2的pixel+1次方).
全局颜色列表(Global Color Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
全局颜色列表必须紧跟在逻辑屏幕标识符后面,每个颜色列表索引条目由三个字节组成,按R、G、B的顺序排列。
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
1 | 索引1的红色值 | ||||||||
2 | 索引1的绿色值 | ||||||||
3 | 索引1的蓝色值 | ||||||||
4 | 索引2的红色值 | ||||||||
5 | 索引2的绿色值 | ||||||||
6 | 索引2的蓝色值 | ||||||||
7 | ... |
图象标识符(Image Descriptor)
~~~~~~~~~~~~~~~~~~~~~~~~~
一个GIF文件内可以包含多幅图象,一幅图象结束之后紧接着下是一幅图象的标识符,图象标识符以0x2C(',')字符开始,定义紧接着它的图象的性质,包括图象相对于逻辑屏幕边界的偏移量、图象大小以及有无局部颜色列表和颜色列表大小,由10个字节组成:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT | |
1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 图象标识符开始,固定值为',' | |
2 | X方向偏移量 | 必须限定在逻辑屏幕尺寸范围内 | ||||||||
3 | ||||||||||
4 | Y方向偏移量 | |||||||||
5 | ||||||||||
6 | 图象宽度 | |||||||||
7 | ||||||||||
8 | 图象高度 | |||||||||
9 | ||||||||||
10 | m | i | s | r | pixel | m - 局部颜色列表标志(Local Color Table Flag) | ||||
置位时标识紧接在图象标识符之后有一个局部颜色列表,供紧跟在它之后的一幅图象使用;值否时使用全局颜色列表,忽略pixel值。 i - 交织标志(Interlace Flag),置位时图象数据使用交织方式排列(详细描述...),否则使用顺序排列。 s - 分类标志(Sort Flag),如果置位表示紧跟着的局部颜色列表分类排列. r - 保留,必须初始化为0. pixel - 局部颜色列表大小(Size of Local Color Table),pixel+1就为颜色列表的位数 |
局部颜色列表(Local Color Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~
如果上面的局部颜色列表标志置位的话,则需要在这里(紧跟在图象标识符之后)定义一个局部颜色列表以供紧接着它的图象使用,注意使用前应线保存原来的颜色列表,使用结束之后回复原来保存的全局颜色列表。如果一个GIF文件即没有提供全局颜色列表,也没有提供局部颜色列表,可以自己创建一个颜色列表,或使用系统的颜色列表。局部颜色列表的排列方式和全局颜色列表一样:RGBRGB......
基于颜色列表的图象数据(Table-Based Image Data)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
由两部分组成:LZW编码长度(LZW Minimum Code Size)和图象数据(Image Data)。
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
1 | LZW编码长度 | LZW编码初始码表大小的位数,详细描述见LZW编码... | |||||||
... | 图象数据,由一个或几个数据块(Data Sub-blocks)组成 | ||||||||
数据块 | |||||||||
... |
GIF图象数据使用了LZW压缩算法(详细介绍请看后面的『LZW算法和GIF数据压缩』),大大减小了图象数据的大小。图象数据在压缩前有两种排列格式:连续的和交织的(由图象标识符的交织标志控制)。连续方式按从左到右、从上到下的顺序排列图象的光栅数据;交织图象按下面的方法处理光栅数据:
创建四个通道(pass)保存数据,每个通道提取不同行的数据:
第一通道(Pass 1)提取从第0行开始每隔8行的数据;
第二通道(Pass 2)提取从第4行开始每隔8行的数据;
第三通道(Pass 3)提取从第2行开始每隔4行的数据;
第四通道(Pass 4)提取从第1行开始每隔2行的数据;
下面的例子演示了提取交织图象数据的顺序:
行 | 通道1 | 通道2 | 通道3 | 通道4 | |
0 -------------------------------------------------------- | 1 | ||||
1 -------------------------------------------------------- | 4 | ||||
2 -------------------------------------------------------- | 3 | ||||
3 -------------------------------------------------------- | 4 | ||||
4 -------------------------------------------------------- | 2 | ||||
5 -------------------------------------------------------- | 4 | ||||
6 -------------------------------------------------------- | 3 | ||||
7 -------------------------------------------------------- | 4 | ||||
8 -------------------------------------------------------- | 1 | ||||
9 -------------------------------------------------------- | 4 | ||||
10 -------------------------------------------------------- | 3 | ||||
11 -------------------------------------------------------- | 4 | ||||
12 -------------------------------------------------------- | 2 | ||||
13 -------------------------------------------------------- | 4 | ||||
14 -------------------------------------------------------- | 3 | ||||
15 -------------------------------------------------------- | 4 | ||||
16 -------------------------------------------------------- | 1 | ||||
17 -------------------------------------------------------- | 4 | ||||
18 -------------------------------------------------------- | 3 | ||||
19 -------------------------------------------------------- | 4 | ||||
20 -------------------------------------------------------- | 2 |
图形控制扩展(Graphic Control Extension)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这一部分是可选的(需要89a版本),可以放在一个图象块(图象标识符)或文本扩展块的前面,用来控制跟在它后面的第一个图象(或文本)的渲染(Render)形式,组成结构如下:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
1 | 扩展块标识 | Extension Introducer - 标识这是一个扩展块,固定值0x21 | |||||||
2 | 图形控制扩展标签 | Graphic Control Label - 标识这是一个图形控制扩展块,固定值0xF9 | |||||||
3 | 块大小 | Block Size - 不包括块终结器,固定值4 | |||||||
4 | 保留 | 处置方法 | i | t | i - 用户输入标志;t - 透明色标志。详细描述见下... | ||||
5 | 延迟时间 | Delay Time - 单位1/100秒,如果值不为1,表示暂停规定的时间后再继续往下处理数据流 | |||||||
6 | |||||||||
7 | 透明色索引 | Transparent Color Index - 透明色索引值 | |||||||
8 | 块终结器 | Block Terminator - 标识块终结,固定值0 |
处置方法(Disposal Method):指出处置图形的方法,当值为:
0 - 不使用处置方法
1 - 不处置图形,把图形从当前位置移去
2 - 回复到背景色
3 - 回复到先前状态
4-7 - 自定义
用户输入标志(Use Input Flag):指出是否期待用户有输入之后才继续进行下去,置位表示期待,值否表示不期待。用户输入可以是按回车键、鼠标点击等,可以和延迟时间一起使用,在设置的延迟时间内用户有输入则马上继续进行,或者没有输入直到延迟时间到达而继续
透明颜色标志(Transparent Color Flag):置位表示使用透明颜色
注释扩展(Comment Extension)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
这一部分是可选的(需要89a版本),可以用来记录图形、版权、描述等任何的非图形和控制的纯文本数据(7-bit ASCII字符),注释扩展并不影响对图象数据流的处理,解码器完全可以忽略它。存放位置可以是数据流的任何地方,最好不要妨碍控制和数据块,推荐放在数据流的开始或结尾。具体组成:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
1 | 扩展块标识 | Extension Introducer - 标识这是一个扩展块,固定值0x21 | |||||||
2 | 注释块标签 | Comment Label - 标识这是一个注释块,固定值0xFE | |||||||
... | Comment Data - 一个或多个数据块(Data Sub-Blocks)组成 | ||||||||
注释块 | |||||||||
... | |||||||||
块终结器 | Block Terminator - 标识注释块结束,固定值0 |
图形文本扩展(Plain Text Extension)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这一部分是可选的(需要89a版本),用来绘制一个简单的文本图象,这一部分由用来绘制的纯文本数据(7-bit ASCII字符)和控制绘制的参数等组成。绘制文本借助于一个文本框(Text Grid)来定义边界,在文本框中划分多个单元格,每个字符占用一个单元,绘制时按从左到右、从上到下的顺序依次进行,直到最后一个字符或者占满整个文本框(之后的字符将被忽略,因此定义文本框的大小时应该注意到是否可以容纳整个文本),绘制文本的颜色索引使用全局颜色列表,没有则可以使用一个已经保存的前一个颜色列表。另外,图形文本扩展块也属于图形块(Graphic Rendering Block),可以在它前面定义图形控制扩展对它的表现形式进一步修改。图形文本扩展的组成:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
1 | 扩展块标识 | Extension Introducer - 标识这是一个扩展块,固定值0x21 | |||||||
2 | 图形控制扩展标签 | Plain Text Label - 标识这是一个图形文本扩展块,固定值0x01 | |||||||
3 | 块大小 | Block Size - 块大小,固定值12 | |||||||
4 | 文本框左边界位置 | Text Glid Left Posotion - 像素值,文本框离逻辑屏幕的左边界距离 | |||||||
5 | |||||||||
6 | 文本框上边界位置 | Text Glid Top Posotion - 像素值,文本框离逻辑屏幕的上边界距离 | |||||||
7 | |||||||||
8 | 文本框高度 | Text Glid Width -像素值 | |||||||
9 | |||||||||
10 | 文本框高度 | Text Glid Height - 像素值 | |||||||
11 | |||||||||
12 | 字符单元格宽度 | Character Cell Width - 像素值,单个单元格宽度 | |||||||
13 | 字符单元格高度 | Character Cell Height- 像素值,单个单元格高度 | |||||||
14 | 文本前景色索引 | Text Foreground Color Index - 前景色在全局颜色列表中的索引 | |||||||
15 | 文本背景色索引 | Text Blackground Color Index - 背景色在全局颜色列表中的索引 | |||||||
N | ... | Plain Text Data - 一个或多个数据块(Data Sub-Blocks)组成,保存要在显示的字符串。 | |||||||
文本数据块 | |||||||||
... | |||||||||
N+1 | 块终结 | Block Terminator - 标识注释块结束,固定值0 |
推荐:1.由于文本的字体(Font)和尺寸(Size)没有定义,解码器应该根据情况选择最合适的;
2.如果一个字符的值小于0x20或大于0xF7,则这个字符被推荐显示为一个空格(0x20);
3.为了兼容性,最好定义字符单元格的大小为8x8或8x16(宽度x高度)。
应用程序扩展(Application Extension)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这是提供给应用程序自己使用的(需要89a版本),应用程序可以在这里定义自己的标识、信息等,组成:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
1 | 扩展块标识 | Extension Introducer - 标识这是一个扩展块,固定值0x21 | |||||||
2 | 图形控制扩展标签 | Application Extension Label - 标识这是一个应用程序扩展块,固定值0xFF | |||||||
3 | 块大小 | Block Size - 块大小,固定值11 | |||||||
4 | 应用程序标识符 | Application Identifier - 用来鉴别应用程序自身的标识(8个连续ASCII字符) | |||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 | |||||||||
10 | |||||||||
11 | |||||||||
12 | 应用程序鉴别码 | Application Authentication Code - 应用程序定义的特殊标识码(3个连续ASCII字符) | |||||||
13 | |||||||||
14 | |||||||||
N | ... | 应用程序自定义数据块 - 一个或多个数据块(Data Sub-Blocks)组成,保存应用程序自己定义的数据 | |||||||
应用程序数据 | |||||||||
... | |||||||||
N+1 | 块终结器 | lock Terminator - 标识注释块结束,固定值0 |
文件结尾部分
~~~~~~~~~~~
文件终结器(Trailer)
~~~~~~~~~~~~~~~~
这一部分只有一个值为0的字节,标识一个GIF文件结束.
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
1 | 文件终结 | GIF Trailer - 标识GIF文件结束,固定值0x3B |
2.LZW算法和GIF数据压缩
~~~~~~~~~~~~~~~~~~~~~~~~~~~
GIF文件的图象数据使用了可变长度编码的LZW压缩算法(Variable-Length_Code LZW Compression),这是从LZW(Lempel Ziv Compression)压缩算法演变过来的,通过压缩原始数据的重复部分来达到减少文件大小的目的。
标准的LZW压缩原理:
~~~~~~~~~~~~~~~~~~
先来解释一下几个基本概念:
LZW压缩有三个重要的对象:数据流(CharStream)、编码流(CodeStream)和编译表(String Table)。在编码时,数据流是输入对象(图象的光栅数据序列),编码流就是输出对象(经过压缩运算的编码数据);在解码时,编码流则是输入对象,数据流是输出对象;而编译表是在编码和解码时都须要用借助的对象。
字符(Character):最基础的数据元素,在文本文件中就是一个字节,在光栅数据中就是一个像素的颜色在指定的颜色列表中的索引值;
字符串(String):由几个连续的字符组成;
前缀(Prefix):也是一个字符串,不过通常用在另一个字符的前面,而且它的长度可以为0;
根(Root):单个长度的字符串;
编码(Code):一个数字,按照固定长度(编码长度)从编码流中取出,编译表的映射值;
图案:一个字符串,按不定长度从数据流中读出,映射到编译表条目.
LZW压缩的原理:提取原始图象数据中的不同图案,基于这些图案创建一个编译表,然后用编译表中的图案索引来替代原始光栅数据中的相应图案,减少原始数据大小。看起来和调色板图象的实现原理差不多,但是应该注意到的是,我们这里的编译表不是事先创建好的,而是根据原始图象数据动态创建的,解码时还要从已编码的数据中还原出原来的编译表(GIF文件中是不携带编译表信息的),为了更好理解编解码原理,我们来看看具体的处理过程:
编码器(Compressor)
~~~~~~~~~~~~~~~~
编码数据,第一步,初始化一个编译表,假设这个编译表的大小是12位的,也就是最多有4096个单位,另外假设我们有32个不同的字符(也可以认为图象的每个像素最多有32种颜色),表示为a,b,c,d,e...,初始化编译表:第0项为a,第1项为b,第2项为c...一直到第31项,我们把这32项就称为根。
开始编译,先定义一个前缀对象Current Prefix,记为[.c.],现在它是空的,然后定义一个当前字符串Current String,标记为[.c.]k,[.c.]就为Current Prefix,k就为当前读取字符。现在来读取数据流的第一个字符,假如为p,那么Current String就等于[.c.]p(由于[.c.]为空,实际上值就等于p),现在在编译表中查找有没有Current String的值,由于p就是一个根字符,我们已经初始了32个根索引,当然可以找到,把p设为Current Prefix的值,不做任何事继续读取下一个字符,假设为q,Current String就等于[.c.]q(也就是pq),看看在编译表中有没有该值,当然。没有,这时我们要做下面的事情:将Current String的值(也就是pq)添加到编译表的第32项,把Current Prefix的值(也就是p)在编译表中的索引输出到编码流,修改Current Prefix为当前读取的字符(也就是q)。继续往下读,如果在编译表中可以查找到Current String的值([.c.]k),则把Current String的值([.c.]k)赋予Current Prefix;如果查找不到,则添加Current String的值([.c.]k)到编译表,把Current Prefix的值([.c.])在编译表中所对应的索引输出到编码流,同时修改Current Prefix为k ,这样一直循环下去直到数据流结束。伪代码看起来就像下面这样:
|
来看一个具体的例子,我们有一个字母表a,b,c,d.有一个输入的字符流abacaba。现在来初始化编译表:#0=a,#1=b,#2=c,#3=d.现在开始读取第一个字符a,[.c.]a=a,可以在在编译表中找到,修改[.c.]=a;不做任何事继续读取第二个字符b,[.c.]b=ab,在编译表中不能找,那么添加[.c.]b到编译表:#4=ab,同时输出[.c.](也就是a)的索引#0到编码流,修改[.c.]=b;读下一个字符a,[.c.]a=ba,在编译表中不能找到:添加编译表#5=ba,输出[.c.]的索引#1到编码流,修改[.c.]=a;读下一个字符c,[.c.]c=ac,在编译表中不能找到:添加编译表#6=ac,输出[.c.]的索引#0到编码流,修改[.c.]=c;读下一个字符a,[.c.]c=ca,在编译表中不能找到:添加编译表#7=ca,输出[.c.]的索引#2到编码流,修改[.c.]=a;读下一个字符b,[.c.]b=ab,编译表的#4=ab,修改[.c.]=ab;读取最后一个字符a,[.c.]a=aba,在编译表中不能找到:添加编译表#8=aba,输出[.c.]的索引#4到编码流,修改[.c.]=a;好了,现在没有数据了,输出[.c.]的值a的索引#0到编码流,这样最后的输出结果就是:#0#1#0#2#4#0.
解码器(Decompressor)
~~~~~~~~~~~~~~~~~~
好了,现在来看看解码数据。数据的解码,其实就是数据编码的逆向过程,要从已经编译的数据(编码流)中找出编译表,然后对照编译表还原图象的光栅数据。
首先,还是要初始化编译表。GIF文件的图象数据的第一个字节存储的就是LZW编码的编码大小(一般等于图象的位数),根据编码大小,初始化编译表的根条目(从0到2的编码大小次方),然后定义一个当前编码Current Code,记作[code],定义一个Old Code,记作[old]。读取第一个编码到[code],这是一个根编码,在编译表中可以找到,把该编码所对应的字符输出到数据流,[old]=[code];读取下一个编码到[code],这就有两种情况:在编译表中有或没有该编码,我们先来看第一种情况:先输出当前编码[code]所对应的字符串到数据流,然后把[old]所对应的字符(串)当成前缀prefix [...],当前编码[code]所对应的字符串的第一个字符当成k,组合起来当前字符串Current String就为[...]k,把[...]k添加到编译表,修改[old]=[code],读下一个编码;我们来看看在编译表中找不到该编码的情况,回想一下编码情况:如果数据流中有一个p[...]p[...]pq这样的字符串,p[...]在编译表中而p[...]p不在,编译器将输出p[...]的索引而添加p[...]p到编译表,下一个字符串p[...]p就可以在编译表中找到了,而p[...]pq不在编译表中,同样将输出p[...]p的索引值而添加p[...]pq到编译表,这样看来,解码器总比编码器『慢一步』,当我们遇到p[...]p所对应的索引时,我们不知到该索引对应的字符串(在解码器的编译表中还没有该索引,事实上,这个索引将在下一步添加),这时需要用猜测法:现在假设上面的p[...]所对应的索引值是#58,那么上面的字符串经过编译之后是#58#59,我们在解码器中读到#59时,编译表的最大索引只有#58,#59所对应的字符串就等于#58所对应的字符串(也就是p[...])加上这个字符串的第一个字符(也就是p),也就是p[...]p。事实上,这种猜测法是很准确(有点不好理解,仔细想一想吧)。上面的解码过程用伪代码表示就像下面这样:
|
GIF数据压缩
~~~~~~~~~~~
下面是GIF文件的图象数据结构:
BYTE | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | BIT |
1 | 编码长度 | LZW Code Size - LZW压缩的编码长度,也就是要压缩的数据的位数 | |||||||
... | 数据块 | ||||||||
块大小 | 数据块,如果需要可重复多次 | ||||||||
编码数据 | |||||||||
... | 数据块 | ||||||||
块终结器 | 一个图象的数据编码结束,固定值0 |
把光栅数据序列(数据流)压缩成GIF文件的图象数据(字符流)可以按下面的步骤进行:
1.定义编码长度
GIF图象数据的第一个字节就是编码长度(Code Size),这个值是指要表现一个像素所需要的最小位数,通常就等于图象的色深;
2.压缩数据
通过LZW压缩算法将图象的光栅数据流压缩成GIF的编码数据流。这里使用的LZW压缩算法是从标准的LZW压缩算法演变过来的,它们之间有如下的差别:
[1]GIF文件定义了一个编码大小(Clear Code),这个值等于2的『编码长度』次方,在从新开始一个编译表(编译表溢出)时均须输出该值,解码器遇到该值时意味着要从新初始化一个编译表;
[2]在一个图象的编码数据结束之前(也就是在块终结器的前面),需要输出一个Clear Code+1的值,解码器在遇到该值时就意味着GIF文件的一个图象数据流的结束;
[3]第一个可用到的编译表索引值是Clear Code+2(从0到Clear Code-1是根索引,再上去两个不可使用,新的索引从Clare Code+2开始添加);
[4]GIF输出的编码流是不定长的,每个编码的大小从Code Size + 1位到12位,编码的最大值就是4095(编译表需要定义的索引数就是4096),当编码所须的位数超过当前的位数时就把当前位数加1,这就需要在编码或解码时注意到编码长度的改变。
3.编译成字节序列
因为GIF输出的编码流是不定长的,这就需要把它们编译成固定的8-bit长度的字符流,编译顺序是从右往左。下面是一个具体例子:编译5位长度编码到8位字符
0 | b | b | b | a | a | a | a | a |
1 | d | c | c | c | c | c | b | b |
2 | e | e | e | e | d | d | d | d |
3 | g | g | f | f | f | f | f | e |
4 | h | h | h | h | h | g | g | g |
... | ||||||||
N |
4.打包
前面讲过,一个GIF的数据块的大小从0到255个字节,第一个字节是这个数据块的大小(字节数),这就需要将编译编后的码数据打包成一个或几个大小不大于255个字节的数据包。然后写入图象数据块中。
Keil C51开发系统基本知识
1. 第一节 系统概述
Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。用过汇编语言后再使用C来开发,体会更加深刻。
Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。
下面详细介绍Keil C51开发系统各部分功能和使用。
2. 第二节 Keil C51单片机软件开发系统的整体结构
C51工具包的整体结构,如图(1)所示,其中uVision与Ishell分别是C51 for Windows和for Dos的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。然后分别由C51及A51编译器编译生成目标文件(.OBJ)。目标文件可由LIB51创建生成库文件,也可以与库文件一起经L51连接定位生成绝对目标文件(.ABS)。ABS文件由OH51转换成标准的Hex文件,以供调试器dScope51或tScope51使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。
3. 第三节 存储模式
存储模式决定了没有明确指定存储类型的变量,函数参数等的缺省存储区域,共三种:
1. 1. Small模式
所有缺省变量参数均装入内部RAM,优点是访问速度快,缺点是空间有限,只适用于小程序。
2. 2. Compact模式
所有缺省变量均位于外部RAM区的一页(256Bytes),具体哪一页可由P2口指定,在STARTUP.A51文件中说明,也可用pdata指定,优点是空间较Small为宽裕速度较Small慢,较large要快,是一种中间状态。
3. 3. large模式
所有缺省变量可放在多达64KB的外部RAM区,优点是空间大,可存变量多,缺点是速度较慢。
提示:存储模式在C51编译器选项中选择。
4. 第四节 存储类型声明
变量或参数的存储类型可由存储模式指定缺省类型,也可由关键字直接声明指定。各类型分别用:code,data,idata,xdata,pdata说明,例:
data uar1
char code array[ ]=“hello!”;
unsigned char xdata arr[10][4][4];
5. 第五节 变量或数据类型
C51提供以下几种扩展数据类型:
bit 位变量值为0或1
sbit 从字节中定义的位变量 0或1
sfr sfr字节地址 0~255
sfr16 sfr字地址 0~65535
其余数据类型如:char,enum,short,int,long,float等与ANSI C相同。
6. 第六节 位变量与声明
1. 1. bit型变量
bit型变量可用变量类型,函数声明、函数返回值等,存贮于内部RAM20H~2FH。
注意:
(1) 用#pragma disable说明函数和用“usign”指定的函数,不能返回bit值。
(2) 一个bit变量不能声明为指针,如bit *ptr;是错误的
(3) 不能有bit数组如:bit arr[5];错误。
2. 2. 可位寻址区说明20H-2FH
可作如下定义:
int bdata i;
char bdata arr[3],
然后:
sbit bito=in0;sbit bit15=I^15;
sbit arr07=arr[0]^7;sbit arr15=arr[i]^7;
7. 第七节 Keil C51指针
C51支持一般指针(Generic Pointer)和存储器指针(Memory_Specific Pointer).
1. 1. 一般指针
一般指针的声明和使用均与标准C相同,不过同时还可以说明指针的存储类型,例如:
long * state;为一个指向long型整数的指针,而state本身则依存储模式存放。
char * xdata ptr;ptr为一个指向char数据的指针,而ptr本身放于外部RAM区,以上的long,char等指针指向的数据可存放于任何存储器中。
一般指针本身用3个字节存放,分别为存储器类型,高位偏移,低位偏移量。
2. 2. 存储器指针
基于存储器的指针说明时即指定了存贮类型,例如:
char data * str;str指向data区中char型数据
int xdata * pow; pow指向外部RAM的int型整数。
这种指针存放时,只需一个字节或2个字节就够了,因为只需存放偏移量。
3. 3. 指针转换
即指针在上两种类型之间转化:
l 当基于存储器的指针作为一个实参传递给需要一般指针的函数时,指针自动转化。
l 如果不说明外部函数原形,基于存储器的指针自动转化为一般指针,导致错误,因而请用“#include”说明所有函数原形。
l 可以强行改变指针类型。
8. 第八节 Keil C51函数
C51函数声明对ANSI C作了扩展,具体包括:
1. 1. 中断函数声明:
中断声明方法如下:
void serial_ISR () interrupt 4 [using 1]
{
/* ISR */
}
为提高代码的容错能力,在没用到的中断入口处生成iret语句,定义没用到的中断。
/* define not used interrupt, so generate "IRET" in their entrance */
void extern0_ISR() interrupt 0{} /* not used */
void timer0_ISR () interrupt 1{} /* not used */
void extern1_ISR() interrupt 2{} /* not used */
void timer1_ISR () interrupt 3{} /* not used */
void serial_ISR () interrupt 4{} /* not used */
2. 2. 通用存储工作区
3. 3. 选通用存储工作区由using x声明,见上例。
4. 4. 指定存储模式
由small compact 及large说明,例如:
void fun1(void) small { }
提示:small说明的函数内部变量全部使用内部RAM。关键的经常性的耗时的地方可以这样声明,以提高运行速度。
5. 5. #pragma disable
在函数前声明,只对一个函数有效。该函数调用过程中将不可被中断。
6. 6. 递归或可重入函数指定
在主程序和中断中都可调用的函数,容易产生问题。因为51和PC不同,PC使用堆栈传递参数,且静态变量以外的内部变量都在堆栈中;而51一般使用寄存器传递参数,内部变量一般在RAM中,函数重入时会破坏上次调用的数据。可以用以下两种方法解决函数重入:
a、在相应的函数前使用前述“#pragma disable”声明,即只允许主程序或中断之一调用该函数;
b、将该函数说明为可重入的。如下:
void func(param...) reentrant;
KeilC51编译后将生成一个可重入变量堆栈,然后就可以模拟通过堆栈传递变量的方法。
由于一般可重入函数由主程序和中断调用,所以通常中断使用与主程序不同的R寄存器组。
另外,对可重入函数,在相应的函数前面加上开关“#pragma noaregs”,以禁止编译器使用绝对寄存器寻址,可生成不依赖于寄存器组的代码。
7. 7. 指定PL/M-51函数
由alien指定。
4. 第四章 Keil C51高级编程
本章讨论以下内容:
l 绝对地址访问
l C与汇编的接口
l C51软件包中的通用文件
l 段名转换与程序优化
1. 第一节 绝对地址访问
C51提供了三种访问绝对地址的方法:
1. 1. 绝对宏:
在程序中,用“#include<absacc.h>”即可使用其中定义的宏来访问绝对地址,包括:
CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWORD
具体使用可看一看absacc.h便知
例如:
rval=CBYTE[0x0002];指向程序存贮器的0002h地址
rval=XWORD [0x0002];指向外RAM的0004h地址
2. 2. _at_关键字
直接在数据定义后加上_at_ const即可,但是注意:
(1)绝对变量不能被初使化;
(2)bit型函数及变量不能用_at_指定。
例如:
idata struct link list _at_ 0x40;指定list结构从40h开始。
xdata char text[25b] _at_0xE000;指定text数组从0E000H开始
提示:如果外部绝对变量是I/O端口等可自行变化数据,需要使用volatile关键字进行描述,请参考absacc.h。
3. 3. 连接定位控制
此法是利用连接控制指令code xdata pdata \data bdata对“段”地址进行,如要指定某具体变量地址,则很有局限性,不作详细讨论。
2. 第二节 Keil C51与汇编的接口
1. 1. 模块内接口
方法是用#pragma语句具体结构是:
#pragma asm
汇编行
#pragma endasm
这种方法实质是通过asm与ndasm告诉C51编译器中间行不用编译为汇编行,因而在编译控制指令中有SRC以控制将这些不用编译的行存入其中。
2. 2. 模块间接口
C模块与汇编模块的接口较简单,分别用C51与A51对源文件进行编译,然后用L51将obj文件连接即可,关键问题在于C函数与汇编函数之间的参数传递问题,C51中有两种参数传递方法。
(1) 通过寄存器传递函数参数
最多只能有3个参数通过寄存器传递,规律如下表:
参数数目 char int long,float 一般指针
123 R7R5R3 R6 & R7R4 & R5R2 & R3 R4~R7R4~R7 R1~R3R1~R3R1~R3
(2) 通过固定存储区传递(fixed memory)
这种方法将bit型参数传给一个存储段中:
?function_name?BIT
将其它类型参数均传给下面的段:?function_name?BYTE,且按照预选顺序存放。
至于这个固定存储区本身在何处,则由存储模式默认。
(3) 函数的返回值
函数返回值一律放于寄存器中,有如下规律:
return type Registev 说明
bit 标志位 由具体标志位返回
char/unsigned char 1_byte指针 R7 单字节由R7返回
int/unsigned int 2_byte指针 R6 & R7 双字节由R6和R7返回,MSB在R6
long&unsigned long R4~R7 MSB在R4, LSB在R7
float R4~R7 32Bit IEEE格式
一般指针 R1~R3 存储类型在R3 高位R2 低R1
(4) SRC控制
该控制指令将C文件编译生成汇编文件(.SRC),该汇编文件可改名后,生成汇编.ASM文件,再用A51进行编译。
3. 第三节 Keil C51软件包中的通用文件
在C51\LiB目录下有几个C源文件,这几个C源文件有非常重要的作用,对它们稍事修改,就可以用在自己的专用系统中。
1. 1. 动态内存分配
init_mem.C:此文件是初始化动态内存区的程序源代码。它可以指定动态内存的位置及大小,只有使用了init_mem( )才可以调回其它函数,诸如malloc calloc,realloc等。
calloc.c:此文件是给数组分配内存的源代码,它可以指定单位数据类型及该单元数目。
malloc.c:此文件是malloc的源代码,分配一段固定大小的内存。
realloc.c:此文件是realloc.c源代码,其功能是调整当前分配动态内存的大小。
2. 2. C51启动文件STARTUP.A51
启动文件STARTUP.A51中包含目标板启动代码,可在每个project中加入这个文件,只要复位,则该文件立即执行,其功能包括:
l 定义内部RAM大小、外部RAM大小、可重入堆栈位置
l 清除内部、外部或者以此页为单元的外部存储器
l 按存储模式初使化重入堆栈及堆栈指针
l 初始化8051硬件堆栈指针
l 向main( )函数交权
开发人员可修改以下数据从而对系统初始化
常数名 意义
IDATALEN 待清内部RAM长度
XDATA START 指定待清外部RAM起始地址
XDATALEN 待清外部RAM长度
IBPSTACK 是否小模式重入堆栈指针需初始化标志,1为需要。缺省为0
IBPSTACKTOP 指定小模式重入堆栈顶部地址
XBPSTACK 是否大模式重入堆栈指针需初始化标志,缺省为0
XBPSTACKTOP 指定大模式重入堆栈顶部地址
PBPSTACK 是否Compact重入堆栈指针,需初始化标志,缺省为0
PBPSTACKTOP 指定Compact模式重入堆栈顶部地址
PPAGEENABLE P2初始化允许开关
PPAGE 指定P2值
PDATASTART 待清外部RAM页首址
PDATALEN 待清外部RAM页长度
提示:如果要初始化P2作为紧凑模式高端地址,必须:PPAGEENAGLE=1,PPAGE为P2值,例如指定某页1000H-10FFH,则PPAGE=10H,而且连接时必须如下:
L51<input modules> PDATA(1080H),其中1080H是1000H-10FFH中的任一个值。
以下是STARTUP.A51代码片断,红色是经常可能需要修改的地方:
;------------------------------------------------------------------------------
; This file is part of the C51 Compiler package
; Copyright KEIL ELEKTRONIK GmbH 1990
;------------------------------------------------------------------------------
; STARTUP.A51: This code is executed after processor reset.
;
; To translate this file use A51 with the following invocation:
;
; A51 STARTUP.A51
;
; To link the modified STARTUP.OBJ file to your application use the following
; L51 invocation:
;
; L51 <your object file list>, STARTUP.OBJ <controls>
;
;------------------------------------------------------------------------------
;
; User-defined Power-On Initialization of Memory
;
; With the following EQU statements the initialization of memory
; at processor reset can be defined:
;
; ; the absolute start-address of IDATA memory is always 0
IDATALEN EQU 80H ; the length of IDATA memory in bytes.
;
XDATASTART EQU 0H ; the absolute start-address of XDATA memory
XDATALEN EQU 0H ; the length of XDATA memory in bytes.
;
PDATASTART EQU 0H ; the absolute start-address of PDATA memory
PDATALEN EQU 0H ; the length of PDATA memory in bytes.
;
; Notes: The IDATA space overlaps physically the DATA and BIT areas of the
; 8051 CPU. At minimum the memory space occupied from the C51
; run-time routines must be set to zero.
;------------------------------------------------------------------------------
;
; Reentrant Stack Initilization
;
; The following EQU statements define the stack pointer for reentrant
; functions and initialized it:
;
; Stack Space for reentrant functions in the SMALL model.
IBPSTACK EQU 0 ; set to 1 if small reentrant is used.
IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1.
;
; Stack Space for reentrant functions in the LARGE model.
XBPSTACK EQU 0 ; set to 1 if large reentrant is used.
XBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1.
;
; Stack Space for reentrant functions in the COMPACT model.
PBPSTACK EQU 0 ; set to 1 if compact reentrant is used.
PBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1.
;
;------------------------------------------------------------------------------
;
; Page Definition for Using the Compact Model with 64 KByte xdata RAM
;
; The following EQU statements define the xdata page used for pdata
; variables. The EQU PPAGE must conform with the PPAGE control used
; in the linker invocation.
;
PPAGEENABLE EQU 0 ; set to 1 if pdata object are used.
PPAGE EQU 0 ; define PPAGE number.
;
;------------------------------------------------------------------------------
3. 3. 标准输入输出文件
putchar.c
putchar.c是一个低级字符输出子程,开发人员可修改后应用到自己的硬件系统上,例如向CLD或LEN输出字符。
缺省:putchar.c是向串口输出一个字符XON&line;XOFF是流控标志,换行符“\*n”自动转化为回车/换行“\r\n”。
getkey.c
getkey函数是一个低级字符输入子程,该程序可用到自己硬件系统,如矩阵键盘输入中,缺省时通过串口输入字符。
4. 4. 其它文件
还包括对Watch-Dog有独特功能的INIT.A51函数以及对8×C751适用的函数,可参考源代码。
4. 第四节 段名协定与程序优化
1. 1. 段名协定(Segment Naming Conventions)
C51编译器生成的目标文件存放于许多段中,这些段是代码空间或数据空间的一些单元,一个段可以是可重定位的,也可以是绝对段,每一个可重定位的段都有一个类型和名字,C51段名有以下规定:
每个段名包括前缀与模块名两部分,前缀表示存储类型,模块名则是被编译的模块的名字,例如:
?CO?main1 :表示main1模块中的代码段中的常数部分
?PR?function1?module 表module模块中函数function1的可执行段,具体规定参阅手册。
2. 2. 程序优化
C51编译器是一个具有优化功能的编译器,它共提供六级优化功能。确保生成目标代码的最高效率(代码最少,运行速度最快)。具体六级优化的内容可参考帮助。
在C51中提供以下编译控制指令控制代码优化:
OPTIMIZE(SJXE):尽量采用子程序,使程序代码减少。
NOAREGS:不使用绝对寄存器访问,程序代码与寄存器段独立。
NOREGPARMS:参数传递总是在局部数据段实现,程序代码与低版本C51兼容。
OPTIMIZE(SIZE)AK OPTIMIZE(speed)提供6级优化功能,缺省为: OPTIMIZE(6,SPEED)。
5. 第五章 Keil C51库函数参考
C51强大功能及其高效率的重要体现之一在于其丰富的可直接调用的库函数,多使用库函数使程序代码简单,结构清晰,易于调试和维护,下面介绍C51的库函数系统。
1. 第一节 本征库函数(intrinsic routines)和非本征证库函数
C51提供的本征函数是指编译时直接将固定的代码插入当前行,而不是用ACALL和LCALL语句来实现,这样就大大提供了函数访问的效率,而非本征函数则必须由ACALL及LCALL调用。
C51的本征库函数只有9个,数目虽少,但都非常有用,列如下:
_crol_,_cror_:将char型变量循环向左(右)移动指定位数后返回
_iror_,_irol_:将int型变量循环向左(右)移动指定位数后返回
_lrol_,_lror_:将long型变量循环向左(右)移动指定位数后返回
_nop_: 相当于插入NOP
_testbit_: 相当于JBC bitvar测试该位变量并跳转同时清除。
_chkfloat_: 测试并返回源点数状态。
使用时,必须包含#inclucle <intrins.h>一行。
如不说明,下面谈到的库函数均指非本征库函数。
图(1) C51工具包整体结构图
3. 第三节 Keil C51工具包的安装
1. 1. C51 for Dos
在Windows下直接运行软件包中DOS\C51DOS.exe然后选择安装目录即可。完毕后欲使系统正常工作须进行以下操作(设C:\C51为安装目录):
修改Autoexec.bat,加入
path=C:\C51\Bin
Set C51LIB=C:\C51\LIB
Set C51INC=C:\C51\INC
然后运行Autoexec.bat
2. 2. C51 for Windows的安装及注意事项:
在Windows下运行软件包中WIN\Setup.exe,最好选择安装目录与C51 for Dos相同,这样设置最简单(设安装于C:\C51目录下)。然后将软件包中crack目录中的文件拷入C:\C51\Bin目录下。
4. 第四节 Keil C51工具包各部分功能及使用简介
1. 1. C51与A51
1. (1) C51
C51是C语言编译器,其使用方法为:
C51 sourcefile[编译控制指令]
或者
C51 @ commandfile
其中sourcefile为C源文件(.C)。大量的编译控制指令完成C51编译器的全部功能。包控C51输出文件C.LST,.OBJ,.I和.SRC文件的控制。源文件(.C)的控制等,详见第五部分的具体介绍。
而Commandfile为一个连接控制文件其内容包括:.C源文件及各编译控制指令,它没有固定的名字,开发人员可根据自己的习惯指定,它适于用控制指令较多的场合。
2. (2) A51
A51是汇编语言编译器,使用方法为:
A51 sourcefile[编译控制指令]
或A51 @ commandfile
其中sourcefile为汇编源文件(.asm或.a51),而编译控制指令的使用与其它汇编如ASM语言类似,可参考其他汇编语言材料。
Commandfile同C51中的Commandfile类似,它使A51使用和修改方便。
2. 2. L51和BL51
1. (1) L51
L51是Keil C51软件包提供的连接/定位器,其功能是将编译生成的OBJ文件与库文件连接定位生成绝对目标文件(.ABS),其使用方法为:
L51 目标文件列表[库文件列表] [to outputfile] [连接控制指令]
或 L51 @Commandfile
源程序的多个模块分别经C51与A51编译后生成多个OBJ文件,连接时,这些文件全列于目标文件列表中,作为输入文件,如果还需与库文件(.LiB)相连接,则库文件也必须列在其后。outputfile为输文件名,缺少时为第一模块名,后缀为.ABS。连接控制指令提供了连接定位时的所有控制功能。Commandfile为连接控制文件,其具体内容是包括了目标文件列表,库文件列表及输出文件、连接控制命令,以取代第一种繁琐的格式,由于目标模块库文件大多不止1个,因而第2种方法较多见,这个文件名字也可由使用者随意指定。
2. (2) Bl51
BL51也是C51软件包的连接/定位器,其具有L51的所有功能,此外它还具有以下3点特别之处:
a. 可以连接定位大于64kBytes的程序。
b. 具有代码域及域切换功能(CodeBanking & Bank Switching)
c. 可用于RTX51操作系统
RTX51是一个实时多任务操作系统,它改变了传统的编程模式,甚至不必用main( )函数,单片机系统软件向RTOS发展是一种趋势,这种趋势对于186和386及68K系列CPU更为明显和必须,对8051因CPU较为简单,程序结构等都不太复杂,RTX51作用显得不太突出,其专业版软件PK51软件包甚至不包括RTX51Full,而只有一个RTX51TINY版本的RTOS。RTX51 TINY适用于无外部RAM的单片机系统,因而可用面很窄,在本文中不作介绍。Bank switching技术因使用很少也不作介绍。
3. 3. DScope51,Tscope51及Monitor51
1. (1) dScope51
dScope51是一个源级调试器和模拟器,它可以调试由C51编译器、A51汇编器、PL/M-51编译器及ASM-51汇编器产生的程序。它不需目标板(for windows也可通过mon51接目标板),只能进行软件模拟,但其功能强大,可模拟CPU及其外围器件,如内部串口,外部I/O及定时器等,能对嵌入式软件功能进行有效测试。
其使用方法为:
DS51[debugfile][INIT(initfile)]
其中debugfile是一个Hex格式的8051文件,即待调试的文件其为可选的,可在进入dScope51后用load命令装入。
Initfile为一个初使化文件,它在启动dScope51后,在debugfile装入前装入,装有一些dScope的初使化参数及常用调试函数等。下面是一个dScope.ini文件(for dos)的内容:
Load ..\..\ds51\8051.iof
Map 0,0xffff
dScope51 for Windows则直接用鼠标进入,然后用load装入待调文件。
2. (2) tScope51
与dScope51不同的是Scope51必须带目标板,目前它可以通过两种方式访问目标板。(1) 通过EMul51在线仿真器,tScope51为该仿真器准备了一个动态连接文件EMUL51.IOT,但该方法必须配合该仿真器。(2) 通过Monitov51监控程序,这种方法是可行的,tScope51为访问Monitor51专门带有MON51.IOT连接程序,使用时可通过串口及监控程序来调试目标板。
其使用方法为:
TS51[INIT(file_name.ini)]
其中file_name.ini为一个初使化文件。
进入TS51后,必须装入IOT文件,可用的有MON51.IOT及EMUL51.IOT两种,如装入MON51.IOT:
Load.C:\C51\TS51\MON51.IOT CPUTYPE(80517)
可惜的是tScope51只有for Dos的版本。
3. (3) Monitor 51
Monitor51是一个监控程序通过PC机的串口与目标板进行通信,Monitor操作需要MON51或dScope51 for Windows,后面部分将对Monitor51做较为详细的介绍。
4. 4. Ishell及uVision
1. (1) Ishell for Dos
这是一个for Dos的IDE,直接在命令行键入Ishell,则进入该环境,它使用简单方便。其命令行与DOS命令行具有同样的功能,对单模块的Project直接由菜单进行编译连接,对多模块的project。则通过批处理,BAT文件进行编译连接,然后通过菜单控制由dScope51或tScope51对程序进行调试,因为是for dos的,不做太详细介绍。
2. (2) uVision for Windows
uVision for Windows是一个标准的Windows应用程序,它是C51的一个集成软件开发平台,具有源代码编辑、project管理、集成的make等功能,它的人机界面友好,操作方便,是开发者的首选,具体配置及使用见第五部分。
2. 第二章 Keil C51软件使用详解
1. 第一节 Keil C51编译器的控制指令
C51编译器的控制指令分为三类:源文件控制类,目标文件控制类及列表控制类。
1. 1. 源文件控制类
NOEXTEND:C51源文件不允许使用ANSI C扩展功能。
DEFINE(DF):定义预处理(在C51命令行)。
2. 2. 目标文件(Object)控制类:
COMPACT LARGE SMALL 选编译模式
DEBUG(DB) 包含调试信息,以供仿真器或dSCope51使用。
NOAMAKE(NOAM) 禁止AutoMake信息记录
NOREGPARMS 禁止用寄存器传递参数
OBJECTEXTEND(OE) Object文件包含附加变量类型信息
OPTIMIZE(OT) 指定优化级别
REGFILE(RF) 指定一个寄存器使用的文件以供整体优化用
REGISTERBANK(RB) 指定一个供绝对寄存器访问的寄存器区名
SRC 不生成目标文件只生成汇编源文件
其它控件不常用。
3. 3. 列表文件(listing)控制类:
CODE(CD):向列表文件加入汇编列表
LISTINCLUDE(LC):显示indude文件
SYMBOLS(SB):列表文件包括模块内所有符号的列表
WARNINGLEVEL(WL):选择“警告”级别
2. 第二节 dScope51的使用
1. 1. dScope51 for Dos
总的来说dScope51具有以下特性:
l 高级语言显示模式
l 集成硬件环境模拟
l 单步或“GO”执行模式
l 存储器、寄存器及变量访问
l Watch表达式之值
l 函数与信号功能
下面,具体说明在进入dScope51 for Dos之后,如何实现上述功能,dScope51采用下拉菜单格式和窗口显示控制,共有language、serial、exe、register四个窗口,其中exe为命令行窗口,language为程序窗口,serial为串口窗,register为寄存器窗。
1. (1) 高级语言显示模式
单击主菜单中的“View”,第一栏中的三条命令“Highlevel”、“Mixed”、“Assembly”分别对所装入的程序按照“高级”、“混合级”及“汇编级”三种方式显示,以方便调试使用。
2. (2) 集成硬件环境模拟显示
主菜单中“Peripheral”各条能显示模拟硬件环境的状态,其中:
i/o Port:显示各I/O口之值,对8031而言SFR中的P1、P2、P3、P0与引脚之值分别列出:
Interrupt:显示5个中断源的入口模式是否允许,优先级等中断状态。
Timer:显示各定时/计数器的模式,初始值状态等。
int Message:中断信息允许,如为允许(“>>”出现),则当中断申请时,显示中断源信息。比如当中断发生时会显示:
“interrupt Timer 0 occured”等
A/D converter:
显示A/D转换器状态无时,则提示“无”。
Serial:串口信息显示,包括串口模式、波特产等
Other:其它器件,如为8031则显示“ 无”
3. (3) 单步或“Go”执行
“F8”单步执行,“F5”全速执行到断点。或选主菜单中Trace单步执行CPU中的Go全速执行。
4. (4) 存储器寄存器及变量访问
外部存储器管理MAP菜单:设置(set)、取消(reset)、显示(Display)处理可用存储空间。
修改Code代码:ASM命令
存储器显示命令:D 类别为(X、D、I、B、C)
修改存储器命令:E 有以下几种命令EB、EC、EI、EL、EF、EP
复杂数据类型显示:Object命令;用以显示结构或数组的内容。欲使此命令有效,C51编译器必须有DB及OBJECTEXTEND两条。
反汇编命令:U
5. (5) “Watch”表达式之值
在View菜单的“Watch”一栏中有四项:其中包括定义Watch Point(Define)、删除Watch Point(remove,kill all),及自动更新选项。
也可用WS、WK等命令代替,下面具体看“表达式”类型:
dScope51一次最多可设16个WtchPoint表达式,显示于Watch Window之中,表达式可以是简单变量,也可是复杂数据类型如结构、数组和指向结构的指针等,例如:
>WS *ptime
>WS ptime→hour
>WS some_record[o],analog等等
6. (6) 关于.IOF文件
启动DS51后必须装入.IOF文件才能使CPU及Peripheral各项起作用,这个函数的使用是依据8051系列CPU的不同特点,装入8051各CPU硬件设备模拟驱动文件,比如8031CPU就必须load DS51目录下的8051.IOF。
2. 2. dScope for Windows
dScope for windows具有dScope for dos的全部功能,此外,它还具有以下明显的优点:
(1) 标准的Windows界面,操作更容易更简单;
(2) 常用操作多用对话框,而非Dos的行命令方式;
(3) 窗口资源更加丰富:存储器窗口、覆盖率分析、运行状态分析窗口,加强了调试功能;
因为dScope for Windows功能强大,具体操作在第八章详细介绍。
3. 第三节 Monitor51及其使用
1. 1. Monitor51对硬件的要求
(1) 硬件系统为51系列CPU;
(2) 带5K外部程序存储器(从O地址开始),存放Monitor51程序;
(3) 256Bytes的外部数据存储器以及5K的跟踪缓冲区,此外,外部数据存储器必须足够容纳所有应用程序代码及数据,且所有外部数据存储器必须为冯·诺伊曼存储器,即能一致访问XDATA与Code空间。
(4) 一个定时器作为波特率发生器供串口使用;
(5) 6 Bytes的空余堆栈。
2. 2. Mon51的使用
Mon51的使用途径有三种方式:
(1) Dos行命令方式
即先用install对MON51进行配置,然后用MON51进入Monitor状态,启用各种命令对Monitor51进行调试。
(2) tScope51方式
启动tScope51装入TS51目录下的MON51.IOT驱动文件,与目标板通信。
(3) dScope51 for Windows方式
在选CPU驱动文件时,选“MON51.dll”,则检查目标板并进入MON51状态。
3. 3. MON51的配置
(1) MON51 for Dos的配置
运行install文件(在MON51目录下),不同的参数可以配置不同的硬件环境。INSTALL Serialtype [xdstastart[codestart[bank][PROMCHECK]]],具体说明见MON51帮助文件或使用手册。
(2) MON51 for Windows的配置
在启用MON51.dll时,会使得系统自动检查目标板连接,如配置不对,则弹出“Configuration”对话框,设置PC串口,波特率等,完毕单击“apply”有效。
4. 4. 串口连接图:
收发交叉互连,RTS、CTS直连,DSR、DTR直连,具体引脚排列参考串口资料。
5. 5. MON51命令及使用
详细的MON51命令可参阅帮助。
4. 第四节 集成开发环境(IDE)的使用
1. 1. Ishell for Dos的使用
进入Ishell之后看到两个窗口:一个是文件窗口,一个是Dos命令行窗口,窗口上方是下拉式的命令菜单,其中的Files控制文件窗口的显隐。
使用Ishell,第一步就是配置系统,即要学习两个文件的修改与创建:
1. (1) Ishell.CFG文件
每一个project都有一个Ishell.CFG,其中存放有“Option菜单和Setup菜单下的部分信息;Bell enabled、Monochrome enabled、Editor Selected、CRT Lines、target enviroment、name of user edit、Automatic load for configuration enabled、file window enabled、file specification for file window、translate command line controls、project name等。
对每个project都必须设置以上信息,然后存盘“setup”的的“save”,这样才可正式开始下面工作。
2. (2) IShell.col文件
对IDE颜色设置,如不改动,可以缺省为主。
3. (3) CDF文件
该文件位于BIN目录下,每一文件定义一组外部函数工具包,即定义外部环境如8051.CDF,USER.CDF等,开发者可修改CDF文件,供自己使用,至于CDF文件内容可查看一下8051.CDF即可知道。注意.CDF文件是Ishell系统的核心所在,不同的CDF文件可使本IDE适用于不同的编译、连接系统,即本IDE并不仅适于C51。
下面谈一谈Automake工具:
C51的Automake是一个project管理器,在8051工具包中以OBJECT文件形式保留了一个project的信息,AutoMake用这些信息来进行project管理,一旦手工建立一个project,Automake可生成一个新的OBJECT,AutoMake利用此文件来编译那些修改过的文件。
Automake支持C51、A51、L51/BL51、C166、A166、L166等编译连接器。点中主菜单中的Automake即运行本工具。
Ishell for Dos使用比较繁琐,推荐使用uVision for windows。
2. 2. uVision for windows的使用
uVision是一个标准的windows应用程序,其编译功能、文件处理功能、project处理功能、窗口功能以及工具引用功能(如A51、C51、PL/M41、BL51 dScope等)等都较Ishell for Dos要强得多。
uVision采用BL51作连接器,因为BL51兼容L51,所以一切能在Dos下工作的project都可以到uVision中进行连接调试。
uVision采用dScope for windows作调试器,该调试器支持MON51及系统模拟两种方式,功能较for DOS要强大好用,调试功能强大。
注意:
(1) Option菜单下的各项要会使用,其中A51、C51、PL/M51、BL51定义各文件所使用的编译、连接控制指令,dScope定义一个dScope初始化文件。Make则是定义一个make文件。
(2) 进入调试是在RUN菜单下运行dScope。
(3) project中包括新建、打开、修改、更新、编译、连接等poject处理,具体使用可参考后面的例子。
3. 第三章 Keil C51 vs 标准C
深入理解并应用C51对标准ANSIC的扩展是学习C51的关键之一。因为大多数扩展功能都是直接针对8051系列CPU硬件的。大致有以下8类:
l 8051存储类型及存储区域
l 存储模式
l 存储器类型声明
l 变量类型声明
l 位变量与位寻址
l 特殊功能寄存器(SFR)
l C51指针
l 函数属性
具体说明如下(8031为缺省CPU)。
1. 第一节 Keil C51扩展关键字
C51 V4.0版本有以下扩展关键字(共19个):
_at_ idata sfr16 alien interrupt small
bdata large _task_ Code bit pdata
using reentrant xdata compact sbit data sfr
2. 第二节 内存区域(Memory Areas):
1. 1. Pragram Area:
由Code说明可有多达64kBytes的程序存储器
2. 2. Internal Data Memory:
内部数据存储器可用以下关键字说明:
data:直接寻址区,为内部RAM的低128字节 00H~7FH
idata:间接寻址区,包括整个内部RAM区 00H~FFH
bdata:可位寻址区, 20H~2FH
3. 3. External Data Memory
外部RAM视使用情况可由以下关键字标识:
xdata:可指定多达64KB的外部直接寻址区,地址范围0000H~0FFFFH
pdata:能访问1页(25bBytes)的外部RAM,主要用于紧凑模式(Compact Model)。
4. 4. Speciac Function Register Memory
8051提供128Bytes的SFR寻址区,这区域可位寻址、字节寻址或字寻址,用以控制定时器、计数器、串口、I/O及其它部件,可由以下几种关键字说明:
sfr:字节寻址 比如 sfr P0=0x80;为PO口地址为80H,“=”后H~FFH之间的常数。
sfr16:字寻址,如sfr16 T2=0xcc;指定Timer2口地址T2L=0xcc T2H=0xCD
sbit:位寻址,如sbit EA=0xAF;指定第0xAF位为EA,即中断允许
还可以有如下定义方法:
sbit 0V=PSW^2;(定义0V为PSW的第2位)
sbit 0V=0XDO^2;(同上)
或bit 0V-=0xD2(同上)。
2. 第二节 几类重要库函数
1. 1. 专用寄存器include文件
例如8031、8051均为REG51.h其中包括了所有8051的SFR及其位定义,一般系统都必须包括本文件。
2. 2. 绝对地址include文件absacc.h
该文件中实际只定义了几个宏,以确定各存储空间的绝对地址。
3. 3. 动态内存分配函数,位于stdlib.h中
4. 4. 缓冲区处理函数位于“string.h”中
其中包括拷贝比较移动等函数如:
memccpy memchr memcmp memcpy memmove memset
这样很方便地对缓冲区进行处理。
5. 5. 输入输出流函数,位于“stdio.h”中
流函数通8051的串口或用户定义的I/O口读写数据,缺省为8051串口,如要修改,比如改为LCD显示,可修改lib目录中的getkey.c及putchar.c源文件,然后在库中替换它们即可。
3. 第三节 Keil C51库函数原型列表
1. 1. CTYPE.H
bit isalnum(char c);
bit isalpha(char c);
bit iscntrl(char c);
bit isdigit(char c);
bit isgraph(char c);
bit islower(char c);
bit isprint(char c);
bit ispunct(char c);
bit isspace(char c);
bit isupper(char c);
bit isxdigit(char c);
bit toascii(char c);
bit toint(char c);
char tolower(char c);
char __tolower(char c);
char toupper(char c);
char __toupper(char c);
2. 2. INTRINS.H
unsigned char _crol_(unsigned char c,unsigned char b);
unsigned char _cror_(unsigned char c,unsigned char b);
unsigned char _chkfloat_(float ual);
unsigned int _irol_(unsigned int i,unsigned char b);
unsigned int _iror_(unsigned int i,unsigned char b);
unsigned long _irol_(unsigned long l,unsigned char b);
unsigned long _iror_(unsigned long L,unsigned char b);
void _nop_(void);
bit _testbit_(bit b);
3. 3. STDIO.H
char getchar(void);
char _getkey(void);
char *gets(char * string,int len);
int printf(const char * fmtstr[,argument]…);
char putchar(char c);
int puts (const char * string);
int scanf(const char * fmtstr.[,argument]…);
int sprintf(char * buffer,const char *fmtstr[;argument]);
int sscanf(char *buffer,const char * fmtstr[,argument]);
char ungetchar(char c);
void vprintf (const char *fmtstr,char * argptr);
void vsprintf(char *buffer,const char * fmtstr,char * argptr);
4. 4. STDLIB.H
float atof(void * string);
int atoi(void * string);
long atol(void * string);
void * calloc(unsigned int num,unsigned int len);
void free(void xdata *p);
void init_mempool(void *data *p,unsigned int size);
void *malloc (unsigned int size);
int rand(void);
void *realloc (void xdata *p,unsigned int size);
void srand (int seed);
5. 5. STRING.H
void *memccpy (void *dest,void *src,char c,int len);
void *memchr (void *buf,char c,int len);
char memcmp(void *buf1,void *buf2,int len);
void *memcopy (void *dest,void *SRC,int len);
void *memmove (void *dest,void *src,int len);
void *memset (void *buf,char c,int len);
char *strcat (char *dest,char *src);
char *strchr (const char *string,char c);
char strcmp (char *string1,char *string2);
char *strcpy (char *dest,char *src);
int strcspn(char *src,char * set);
int strlen (char *src);
char *strncat (char 8dest,char *src,int len);
char strncmp(char *string1,char *string2,int len);
char strncpy (char *dest,char *src,int len);
char *strpbrk (char *string,char *set);
int strpos (const char *string,char c);
char *strrchr (const char *string,char c);
char *strrpbrk (char *string,char *set);
int strrpos (const char *string,char c);
int strspn(char *string,char *set);
6. 第六章 Keil C51例子:Hello.c
Hello位于\C51\excmples\Hello\目录,其功能是向串口输出“Hello,world”整个程序如下:
#pragma DB OE CD
#indule <reg51.h>
#include<stdio.h>
void main(void)
{
SCOn=0x50;
TMOD=0x20
TH1=0xf3;
Tri=1;
TI=1;
printf(“Hello,world \n”);
while(1) { }
}
1. 第一节 uVision for Windows的使用步骤
(1) file_new新建一个hello.c文件,输入如上内容或直接用目录下源文件。
(2) file_save或工具栏将文件存盘。
(3) project_new project创建一个project名为hello,并在其中加入hello.c。
这时该project已是打开状态,或用open project打开已存在的project。
(4) option_C51 compiler中选出至少包括两项DB OE。
(5) option_dscope Debugger选中hello\DS51.INI
查看DS51.INI看其是否为:
“load…\…\BIN\8051.DLL
map 0, 0xffff”
否则修改。
(6) 在option_make选make文件顺序。
(7) project选Build project,看是否有语法错误,若无则生成HEX文件,若有则修改源文件后重复以上部分步骤。
(8) run_dScope debugger进入dScope51后装入hello则可用go直接运行看serial窗口有无输出,正常每系统运行一次,serial窗口均出现一个“Hello,world”表明运行无误。
2. 第二节 Ishell for Dos使用步骤
(1) 进入Ishell 用Setup editer选择编辑器。
然后单击Edit或用Edit命令编辑hello.c源文件,存盘,也可以在files窗口中直接选中hello.c。
(2) 用cd改换project目录至hello目录。
(3) 在setup_target一项目选8051。
(4) 在setup_C51中输出DB OE。
(5) 在setup_project输入project名hello。
(6) 在setup_save保存Ishell.CFG文件。
(7) 编辑一个Link文件hello.lin中有“hell.obj”一行。
(8) 由光标落在files菜单中的Hello.c上,单击“translate”,如无语法错,再击“link”,则Hex文件生成。
(9) 单击Simulate如在8051.CDF中选Simulate为dScope则进入dScope调试直接“Go”,看serial窗口输出为“Hello.world”。
(10) 如程序有误修改源代码后不必再translate或link了,只要一步Amake即可。
若project中包括不止一个文件,在DOS的Ishell中不能用Translate编译,而应建立bat文件,直接在命令窗编译,然后link连接。
如还需用Translate则只能多个文件分别编译,然后连接。
7. 第七章 Keil C51的代码效率
C51程序编译生成汇编代码的效率,是由许多因素共同决定的,对于Keil C51,主要受以下两种因素影响:
1. 第一节 存储模式的影响
存储模式决定了缺省变量的存储空间,而访问各空间变量的汇编代码的繁简程度决定了代码率的高低。
例如:一个整形变量i,如放于内存18H、19H空间,则++i的操作编译成四条语句:
INC 0x19
MOV A,0x19
JNZ 0x272D
INC 0x18
0x272D:
而如果放于外存空间0000H、0001H则++i的操作编译成九条语句:
MOV DPTR,0001
MOVX A,@ DPTR
INC A
MOVX @ DPTR,A
JNz #5
MOV OPTR,#0000
MOVX A,@DPTR
INC A
MOVX @ DPTR,A
就汇编之后的语句而言,对外部存储器的操作较内部存储器操作代码率要低得多,生成的语句为内存的两倍以上,而程序中有大量的这种操作,可见存储模式对代码率的响了。
因此程序设计的原则是
1、存储模式从small-Compact-large依次选择,实在是变量太多,才选large模式。
2、即使选择了large模式,对一些常用的局部的或者可放于内存中的变量,最好放于内存中,以尽量提高程序的代码率。
2. 第二节 程序结构的影响
程序的结构单元包括模块、函数等等。同样的功能,如果结构越复杂,其所涉及的操作、变量、功能模块函数等就越多,较之结构性好,代码简单的程序其代码率自然就低得多。
此外程序的运行控制语句,也是影响代码率的关键因素,例如:switch -case语句,许多编译器都把它们译得非常复杂,Keil C51也不例外,相对较为简易的Switch-case语句,编译成跳转指令形式,代码率较高,但对较为复杂的Switch-Case,则要调用一个系统库函数?C?ICASE进行处理,非常复杂。
再如if( ),while( ),等语句也是代码相对较低的语句,但编译以后比switch-case要高得多。
因此建议设计者尽量少用switch-case之类语句来控制程序结构,以提高代码率。
除以上两点外,其它因素也会对代码率产生影响,例如:
是否用寄存器传递参数 即NOAREGS选项是否有
是否包括调试信息:即DEBUG选项
是否包括扩展的调试信息:即BJECTEXTEND
8. 第八章 dScope for Windows使用详解
1. 第一节 概述
1. 1. 主窗口(Mainframe Window)
可设置其它各种调试窗口,设置断点、观察点,修改地址空间,加载文件等等;
2. 2. 调试窗口(DEBUG Window)
支持用户程序的各种显示方式,可连续运行,单步运行用户程序,并可在线 汇编;
3. 3. 命令窗口(Command Window)
支持命令行的输入;
4. 4. 观察窗口(Watch Window)
可设置所要观察的变量、表达式等;
5. 5. 寄存器窗口(Registe Window)
显示内部寄存器的内容,程序运行次数等;
6. 6. 串口窗口(Serical Windows)
显示串口接收和发送的数据;
7. 7. 性能分析窗口
显示所要观察的各程序段占用CPU的空间;
8. 8. 内存窗口(Memory Window)
显示所选择的内存中的数据;
9. 9. 符号浏览窗口(Symbol Browser Window)
显示各种符号名称,包括专有符号,用户自定义符号(函数名、变量、标号)等;
10. 10. 调用线窗口(Call-Stack Window)
动态显示当前执行的程序段的函数调用关系;
11. 11. 代码覆盖窗口
提供当前模块内各程序段中被执行代码的比率;。
12. 12. 外围设备窗口(peripherals)
可显示I/O口,定时器,中断,串口等外围设备状态;
2. 第二节 dScope for Windows基本操作
1. 1. 指定初始化文件
在uVision的Option菜单dScope Debugger中指定dScope的初始化文件,用uVision的RUN启动dScope将自动加载此初始化文件,自动执行其中命令;
下面是一个例子,可以看出调入一个调试代码的过程。Ds51.ini:
load 8051.dll
load test
slog>>test.log
xtal=11.0592
define button "go to main","g,main"
ws RevCounter
ws rm.r
g,main
PA RESET
PA serial
PA timer0
2. 2. 观察变量
方法1:命令行
WS expression [, numberbase ] [ LINE ]
其中numberbase为显示数制,10对应10进制,16对应16进制,缺省为16进制。LINE为单行显示,缺省为多行显示。
方法2:setup->Watchpoints,在对话框中输入变量
3. 3. 显示RAM的值
d i(x,d):起始地址,终止地址
d 变量名
4. 4. 观察堆栈
View->Call-stack->Show invocation,可以跟踪调用过程;
5. 5. 中断处理程序调试
在装入8051.dll后,在dScope的主菜单中将增加Peripherial,其有4个字菜单:
I/0 port:Pi端口状态
Interrupt:中断设置
Timer:定时器中断状态
Serial:串口中断状态
设置相应的中断请求标志位即可产生中断。
6. 6. 性能分析(Performance Analyzer:PA)
PA用来分析一段代码执行占用CPU的百分比。定义:
命令行 PA func_name
3. 第三节 dScope for Windows命令文件的编制
dScope除了用命令行的方式进行调试以外,还可将各种调试命令汇集于一个调试文件中,然后调用该文件,就可达到自动测试用户源代码的目的。dScope的命令文件支持C/PL/M的格式,因而编制调试命令文件与编制C语言程序有些类似。
1. 1. 地址空间及地址空间类型
1. (1) 地址空间分段
dScope提供的最大可用空间为16M,实际上我们只用以下三段:
① 内部数据空间段(0X00段或D段)
0X00:0X0000~0X00:0XFFFF(对MSC51而言为0X00:0X00FF)
② 外部数据空间段(0X01段式或X段)
0X01:0X0000~0X01~0XFFFF
③ 程序空间段(0XFF段或C段)
0XFF:0X0000~0XFF:0XFFFF
2. (2) 地址空间类型
C:代码空间
D:内部直接寻址空间
I: 内部间接寻址空间
X:外部数据空间
B:位寻址空间
P:I/O口
EB:扩展的位寻址空间(MCS251专有)
ED:扩展的数据空间(MCS251专有)
CO:常数空间(MCS251专有)
HC:正常数空间(MCS251专有)
2. 2. 常量
dScope支持十六进制、八进制、十进制、二进制常数,其后缀分别为H、Q(O)、T(或无)、Y;
dScope不区分常量的大、小写。
1. (1) 整型常量
分为整型(int),无符号整型(uint,00rd),长整型(long),无符号长整型(Wlong、Word)。
2. (2) 浮点型常量
与ANSI C相同。
3. (3) 字符串常量
与ANSI C相同
4. (4) 字符常量
分为字符型(Char)和无符号字符型(Uchar)一种。
5. (5) 行号常数
指用户程序中的行号,实际上是个地址
6. (6) 位常量(Bit):
0和1
7. (7) 地址常数
地址常数的种类很多,地址常数不同于行号常数,行号常数就是一个地址,而地址数被引用时,实际上是取该地址中的数据。
C:代码地址常数,如C:0X0012或0XFF:0X0012
D:内部直接寻址地址常数,如D:0X0068或0X00:0X0068
I:内部间按寻址地址常数,如I:0X0010或0X00:0X0010
X:外部数据空间地址常数,如X:0X0028或0X01:0X0028
B:位地址常数,如B:0X20或B:0X24.0
EB:扩展的位地址常数(MCS251专有),
ED:扩展的数据空间地址常数(MCS251专有)
CO:常数空间地址常数(MCS251专有)
HC:正常数空间地址常数(MCS251专有)
8. (8) 标识符常量
即用户源程序中的标号、函数名等,实际上代表某一地址。
9. (9) 用户源程序中定义的常数
3. 3. 变量
dScope所支持的变量名或标识符最多可由31个字符组成,第一个字母为A~Z,a~z,下划线或问号,后续字符可为字母、数字、下划线和问号。除CPU变量和系统变量外,dScope不支持全局变量,但可视“define”命令定义的变量为全局变量。
Dscope所支持的变量分为以下几种(变量名称不区分大、小写),支持类型转换:
1. (1) 整型变量
分为整型变量(int)、无符号整型变量(uint/word),长整型(Long) 、无符号长整型(Ulong/dword)。
2. (2) 浮点型变量(float)
与ANSI C相同。
3. (3) 字符型变量L
分为字符型(char)变量和无符号字符型(Uchar)
4. (4) 位变量(Bit)
5. (5) 系统变量
dScope自己定义了一系列内部变量,用户可对这些变量进行读或读/写操作, 可被用户自定义数所引用。
a. Cycles (Read Only)
32位变量(Ulong),指示当前程序执行已花费的指令周期(cycle)。
b. Ramsize(R/W)
16位变量(Uint),指示内部可直接寻址的数据空间大小。
c. Radix(R/N)
8位变量(Uchar),决定输出的数制
Radix=0X0A (10进制),Radix=0X10 (16进制)
d. -IIP-(R/W)
8位变量(Uchar),指示当前的中断嵌套数目。
e. $ (R/W)
32位变量(Ulong),指出PC值,通过对其进行写操作,可改变程序执行的流程。
f. Itrace (R/W)
8位变量(Uchar),决定是否对程序运行情况进行记录
Itrace=1,使能记录操作
Itrace=0,根本上记录操作
g. __Break__(R/W)
8位变量(Uchar) __Break__=1,中止程序的运行
h. __Mode__和__Frame size__是MCS 251专有的变量。
6. (6) CPU变量
即R0~R7、A、C(位变量)、B、DPTR及特殊功能寄存器变量,对这些变量均可进行读、写操作。
7. (7) 用户源程序中定义的变量、数组、结构等
4. 4. 运算符
dScope支持ANSI C的运算符,包括算术运算符,逻辑运算符,关系运算符。
5. 5. 表达式
以运算符将dScope所支持的常量、变量、函数等连接在一起,就构成了dScope的表达式。
6. 6. 数组
dScope不支持在命令文件中定义数组,但可引用用户程序中的数组,引用方式如同C。
7. 7. 结构和联合
dScope不支持在命令文件中定义结构和联合,但可引用用户程序中的结构和联合,引用方式如同C,但如要输出整个结构或联合的结果,就要用命令“OBJ”。
8. 8. 指针:
不可自定义指针,但支持用户源程序中的指针变量。
9. 9. dScope命令语句
dScope提供了一系列调试命令。在命令文件中,dScope只支持这些语句及前述定义的表达式,C语言的语句均不被支持,但在命令文件所包含的用户自定义函数(非用户源程序中的函数)中支持C语句,但用户自定义函数中同样不支持数组、结构、联合和指针。
1. (1) ASM
在线汇编命令,格式如下:
ASM C:0Xnnnn (或标号);设定插入汇编指令的地址
ASM 汇编指令
ASM 汇编指令
插入完毕后,在debug窗口内选择“Assemble->Assemble”完成编译。
2. (2) Assign
串行口分配指令,格式如下:
Assign channel<unreg>outreg
对MCS51为:Assign Win<SOIN> Soot
但目前的dScope版本并未提供完整串口窗口功能。
3. (3) Define
用户自定义变量指令,格式如下:
Define <类型> <变量名>
类型一为如前所述的变量类型,Define指令定义的变量可能为全局变量,可为用户自定义函数所引用。
4. (4) Display
内存显示命令,格式如下二:
D 起始地址,结束地址
地址如前所述的地址常数,标识符常量。
5. (5) Enter
内存修改指令,格式如下:
E 类型地址=表达式 [表达式2],[……]
类型如前所述,地址如前所述的地址常数。表达式如前所述,但如果是函数名称(含标号、指针变量),则关键字E→EP
6. (6) Map/Reset map
Map为内存段修改指令,Reset map将内存段复位或缺省值。
7. (7) Object
用以引用用户源程序中的结构(联合)、数组、格式如下:
Obj表达式 [n,],[Line]
表达式为用户源程序中的数组,结构(联合)名称。当Line缺省时,数目、结构(联合)的内容按n行输出;如有Line,则单行输出。
8. (8) U
反汇编命令,格式如下:
U [地址]
地址包括地址常 数及标识符常量,指明反汇编的起始地址。
9. (9) WK
观察点删除命令,格式如下:
WK n1[n2 ],[……] ;删除指定的观察点,n为字符型,整型
常数
WK * ;删除所有的观察点
10. (10) WS
观察点设置命令,格式如下:
WS 表达式[,n][LINE]
关键字LINE存在时,观察点表达式单行输出
LINE缺省时,观察点表达式n行输出。
11. (11) G
连续运行命令,格式如下:
G [起始地址],[终止地址]
地址为标识符常量或地址常数,地址缺省时,为连续运行。
12. (12) T/P
单步运行指令,格式如下:
T/P n ;n指至单行运行的步数,P指给用户当调用某函数时,把它作为一步处理,并不进入该函数运行。
13. (13) PA
性能分析操作指令,其分以下几种:
PA
显示当前所设置的性能分析程度段
PA Kill<SPAN style="mso-s
1005:创建表失败
1006:创建数据库失败
1007:数据库已存在,创建数据库失败
1008:数据库不存在,删除数据库失败
1009:不能删除数据库文件导致删除数据库失败
1010:不能删除数据目录导致删除数据库失败
1011:删除数据库文件失败
1012:不能读取系统表中的记录
1020:记录已被其他用户修改
1021:硬盘剩余空间不足,请加大硬盘可用空间
1022:关键字重复,更改记录失败
1023:关闭时发生错误
1024:读文件错误
1025:更改名字时发生错误
1026:写文件错误
1032:记录不存在
1036:数据表是只读的,不能对它进行修改
1037:系统内存不足,请重启数据库或重启服务器
1038:用于排序的内存不足,请增大排序缓冲区
1040:已到达数据库的最大连接数,请加大数据库可用连接数
1041:系统内存不足
1042:无效的主机名
1043:无效连接
1044:当前用户没有访问数据库的权限
1045:不能连接数据库,用户名或密码错误
1048:字段不能为空
1049:数据库不存在
1050:数据表已存在
1051:数据表不存在
1054:字段不存在
1065:无效的SQL语句,SQL语句为空
1081:不能建立Socket连接
1114:数据表已满,不能容纳任何记录
1116:打开的数据表太多
1129:数据库出现异常,请重启数据库
1130:连接数据库失败,没有连接数据库的权限
1133:数据库用户不存在
1141:当前用户无权访问数据库
1142:当前用户无权访问数据表
1143:当前用户无权访问数据表中的字段
1146:数据表不存在
1147:未定义用户对数据表的访问权限
1149:SQL语句语法错误
1158:网络错误,出现读错误,请检查网络连接状况
1159:网络错误,读超时,请检查网络连接状况
1160:网络错误,出现写错误,请检查网络连接状况
1161:网络错误,写超时,请检查网络连接状况
1062:字段值重复,入库失败
1169:字段值重复,更新记录失败
1177:打开数据表失败
1180:提交事务失败
1181:回滚事务失败
1203:当前用户和数据库建立的连接已到达数据库的最大连接数,请增大可用的数据库连接数或重启数据库
1205:加锁超时
1211:当前用户没有创建用户的权限
1216:外键约束检查失败,更新子表记录失败
1217:外键约束检查失败,删除或修改主表记录失败
1226:当前用户使用的资源已超过所允许的资源,请重启数据库或重启服务器
1227:权限不足,您无权进行此操作
1235:MySQL版本过低,不具有本功能
2001年秋季网络泡沫的破灭标志着互联网的一个转折点。很多人得出结论说,互联网被过分夸大了,实际上,泡沫和随之而来的衰退看上去是所有科技革
命的共同特点。衰退是正处于上升期的科技准备占据中央舞台的特色。伪装者被逐出门外,真正的成名故事显示出他们的实力,开始理解一个事物与其他分开的原
因。
Web 2.0的概念开始于O’Reilly与 MediaLive
International的一个献计献策会。网络先锋人物和O’Reilly公司副总裁Dale
Dougherty指出,网络非但没有破灭,而且随着许多令人激动的新程序和网站让人惊讶的突然规律性出现,网络比以往的作用更重要。更重要的是,在互联
网灾难中幸存下来的公司看上去有一些共同的特点。.com公司的垮掉能在某种程度上标志互联网的转折点吗, web
2.0这样的称呼有意义吗?我们同意它的确是,web 2.0大会就这样诞生了。
在那以后的一年半时间内,web 2.0这个词就无疑已经生根,在Google上有超过950万个引用结果。但仍有大量对web 2.0含义的不同观点,一些人谴责这只不过是一个毫无意义的市场时髦吵作词汇(buzzword),其他人则认为它是一个新的传统概念。
本文旨在阐明我们通过web2.0想要表达的含义。
在最初的集体讨论上,我们用例子阐明了我们对web2.0的理解。
Web 1.0—> Web 2.0
DoubleClick—>Google AdSense
Ofoto(网上照片
贮存和共享服务的提供商)—>Flickr
Akamai(阿卡迈技术公司)—>BitTorrent
mp3.com—>Napster
不列颠百科全书在线—>Wikipedia
个人网站—>网络日志(博客、部落格)
evite—>upcoming.org and EVDB
域名投机买卖—>搜索引擎优化(SEO)
网页浏览—>每次点击成本
屏幕抓取—>网络服务
发表/出版—>参与
内容管理系统(CMS)—>维基(wiki)
目录(分类)—>标签(“folksonomy”)
粘性—>聚合
这个表格还可以不断往前进。但是,究竟什么可以让我们确定一种程序或道路是web 1.0,而另外一种则是web
2.0呢?(这个问题非常迫切,因为web 2.0
概念已经非常流行,许多公司把它当作市场时髦词汇了,却并不真正理解它的含义。问题特别困难,因为许多对时髦词汇有癖好的创业型公司绝对不是web
2.0。其他有些程序如napster和BT被认为是web2.0,但它们甚至不是适当的网络应用程序!译者注:应该是由于版权问题)
web 1.0有一些被成功故事以及最有趣的新应用程序证明的原则,下面是列表。
1,作为平台的网络
和其他许多重要的概念一样,web2.0没有坚实的分界线,但有重力的核心。可以通过一系列原则和实践把web 2.0形象化,并证明部分或所有原则都在离核心或远或近的地方。
上图显示了在FOO Camp的一次献计献策会上的web 2.0“meme map”。这是一项正在进行的工作,但显示了许多来自web 2.0核心的思想。
例
如,在2004年10月第一届web 2.0大会上,John
Battelle和我在公开讨论中列出了初步的一系列原则。第一个原则就是“作为平台的互联网”。虽然这也是web
1.0时代骄子网景公司的口号。更重要的是,web
1.0时代最初的2个标兵――DoubleClick和Akamai在对待“作为平台的互联网”方面都是先驱。
人们并不认为它是网络服务,但实际上,广告服务是第一个在互联网上普遍开展的服务,第一个广泛展开的组合(mashup、用另外一个词,就是获得了
currency of
late?不明白啥意思),每个广告横幅都是两家网站之间的无缝合作,向另外一台电脑用户传递整合的页面。Akamai也把网络看成平台,在堆栈的较深层
次,建立了透明的缓冲存储器和内容传递网络(CDN),减轻了带宽堵塞的压力。
即便如此,这些先驱提供有用的对比,因为后加入者在相同问题上,可以有更深入的解决方案、对新平台的特性理解更深刻。DoubleClick 和Akamai都是web 2.0先驱,然而我们也能看出,通过包含额外的web 2.0设计模式,认识更多可能性有多大可能。
让我们对3个案例演练片刻,了解一些关键因素的差别。
Netscape vs. Google
如果说Netscape是web 1.0时代标准的搬运工,google则当然是web 2.0时代标准的搬运工,因为他们的IPO(首次公开募股)界定了各自的时代。因此,让我们以对这两家公司及其各自定位的对比开始。
Netscape根据旧的软件范例构筑了“作为平台的互联网”,他们的旗舰产品是网页浏览器,一种桌面应用程序,他们的战略是利用在浏览器领域的统
治地位,来建立一个高价服务器产品市场。理论上说,控制了浏览器显示内容和应用程序的标准,这给了netscape一种微软在PC市场上的支配力。作为熟
悉的外延,和无马马车(horseless
carriage,常指老式汽车。第一辆汽车问世时,当时称之为‘无马马车’)构成的汽车相似,网景意图用(网面)webtop代替桌面
(desktop),并计划通过信息升级和向webtop推送applets程序来组装webtop,信息和程序则来自愿意购买网景服务器的信息提供商。
最终,浏览器和网络服务器都成了最普通不过的东西,价值转移到了通过互联网平台传递的服务上。
与此相反,google从一开始开始就是网络应用程序,从来不卖也不打包,但作为服务传递,顾客也为这种服务直接或间接付帐。没有旧软件工业的任何
困扰。没有预定好的软件发布,只有不断的改进。没有许可或出售,只有使用。没有通向其他不同平台、以使顾客在自己的设备上运行软件的端口,只有大量可升
级、运行开源操作系统以及自制程序的电脑,公司以外人士从来没有机会目睹。
实际上,google需要的能力是网景从未面对的:数据库管理。Google不仅仅是收集软件工具,它是专门的数据库。没有数据,这些工具都没有用
处,没有软件,数据则无法管理。软件许可和控制API――此前时代的力量杠杆――现在却不相干了,因为软件从来不需要被分发,而只是运作。同时由于没有收
集和管理数据的能力,软件的用处不大。实际上,软件的价值与它帮助管理的数据的范围和力度成比例。
Google的服务不是一台服务器――尽管它通过大量互联网服务器传输,也不是浏览器,尽管它通过用户使用浏览器来体验。它旗舰的搜索服务也不控制
内容。与电话很类似,不仅仅是在电话两端,而是在中间的网络。google就在浏览器、搜索引擎与目标内容服务器之间,就像一个在用户和在线体验之间的ENABLER或中间人。
Netscape和google都可以被描述为软件公司,前者很明显属于诸如lotus微软,甲骨文SAP,之类的软件世界,而google则是和另外一些互联网应用程序很类似,例如eBay Amazon, Napster,DoubleClick和 Akamai.
和google类似,DoubleClick是互联网时代真正的孩子。它把软件作为一种服务,在数据管理中有核心的能力。正如上面所说的,它是互联网服务
的先驱,甚至早在还没有互联网服务这个名称之前。但是,DoubleClick最终受限于其自身的商业模式。它引入的是1990年代的概念,即互联网是关
于出版,而非参与;广告商应该作最后决定,而非顾客;大小很重要,互联网正日益变得由MEDIAMETRIX和其他互联网广告评比公司界定的大型网站操
纵。
结果,DoubleClick骄傲的在网站上宣布:其软件已经成功执行超过2000次。
与此相比较,Yahoo搜索行销(前Overture)和google adsense早已为无数广告客户分别刊登了广告。
Overture和google的成功来自对Chris Anderson提到的所谓“长尾”的理解,也就是组成互联网内容大部分的小网站的集体力量。DoubleClick需要正式的销售合同,这使得它们的市场限制在少数几个大
型网站。Overture和
Google则知道如何在每个网页上放置广告。更重要的是,他们避开了出版商/广告代理机构的广告模式,诸如横幅广告和弹出广告,而采用最低限度打扰用
户、上下文相关、对消费者友好的文本广告。
Web 2.0训诫:通过杠杆作用,使消费者自助服务和算法数据管理延伸到整个互联网,到达边缘、而不仅仅是中心,到达长尾、而不仅仅是头部。
没有什么可惊讶的,其他web2.0成功故事也证明了同样的行为。eBay使得个人之间只有数美元的偶然交易变得可能,作为自动的中间媒介。
Napster(尽管因为法律原因关闭了)不是通过集中的歌曲数据库建立其网络,而是通过构建一个系统,让每个下载者也变成服务端,这样发展起了网络。
Akamai vs. BitTorrentt
类似DoubleClick,Akamai也是经过优化从头部开始做生意,而不是尾部;从中央,而不是边缘。它通过在中央铺平前往高要求网站的道路,对每个在互联网边缘的个人也有益处,它从这些中央网站得到利润。
和其他P2P运动先驱相似,BT(BitTorrent)为互联网去中心化采取了彻底的方法。每个客户端也是服务端,文件被分割,从多个方位传输,利用下载者的带宽向其他用户提供带宽和数据。实际上,文件越流行,服务的越快,因为有更多用户提供带宽和完整文件的部分。
BT
(BitTorrent)就这样证明了web
2.0的关键原则:越多人使用,服务就自动变得越好。而Akamai必须增加服务器方能改善服务,每个BT使用者把自己的资源带给其他人。有一个隐含的
“参与体系”把每个人的边缘连接起来,利用用户自身的力量。“参与体系”则是内在的企业道德,服务主要作为智能经纪人。
2,利用集体智慧
看上去,在Web 1.0时代诞生、并引领web 2.0时代潮流的巨人成功背后的重要原则就是这个,他们把互联网力量和利用集体智慧组合了在一起。
YAHOO!,第一个互联网伟大的成功故事,诞生初期是目录或链接目录,一个数千人辛勤工作的结果,后来是百万互联网用户。随着yahoo转入创造内容类型事务,它作为网络用户集体创作门户的核心地位保持了下来。
Amazon和竞争对手Barnesandnoble.com出售相同产品,他们收到相同的产品介绍,封面图,卖主的编辑内
容。但是Amazon指定了一套科学的用户参与方案。他们按照用户浏览页面的次数,在每个页面上发出不同方式从参与邀请。更重要的,他们利用用户活动来创
造更好的搜索结果。Barnesandnoble.com搜索只是指向公司自己的产品或赞助商产品。Amazon总是以“最流行”、不仅基于销售,而是其
他因素的实时计算,AMAZON内部称之为产品周围的“流动flow”。有了按照用户参与次数的订单,因此它超越竞争者也不奇怪了。
现在,熟悉这种洞察力的创新公司可能延伸的更远,正在互联网上写下自己的标志。
维基百科,任何人均可编辑的在线百科全书。基于信任的试验,适用了Eric Raymond的格言,“有了足够的眼球,所有的bug都很肤浅”。维基百科早已是100强网站,很多人认为它不久进入前十。这是动态内容创作方面的深刻改变。
Flickr之类的网站,这两家公司最近收到广泛关注,是folksonomy概念的先驱,一种任何人均可自由选择关键词(标签)的分众分类。Tag考虑
到大脑自身使用的多重选择和重叠的联系,而不是硬梆梆的分类。在规范的例子中,一幅flickr小狗的图片可能被标签为“小狗”(puppy)和可爱
“cute”,考虑到了沿自然轴的检索产生了用户的活动。
或Python等涉及大多数网络服务器――依靠开源的个人生产(peer-production)方式,但他们自身是集体、net-enabled智慧的
例子。SourceForge.net有超过10万个开源软件项目。任何人都可以增加项目,任何人也可以下载和使用代码。作为用户行为的结果,新项目从边
缘迁移到中央,一个有机的软件采用过程几乎完全依靠病毒营销
训诫:来自用户贡献的网络影响是占据web 2.0时代市场主导地位的关键。
Blogging与大众智慧
Web 2.0时代被高度吹捧的一个特点是blogging的兴起。个人主页在互联网初期就开始活跃,个人日记和每日评论专栏比这个时间还要长,那么这一切有什么值得大惊小怪的呢?
最基本的,blog只是日记形式的个人主页。但是正如Rich Skrenta指出的,blog按时间顺序的组织形式“看上去好像是微不足道的差异,但它推动了一个完全不同的传输、广告和价值链。
造成这些区别的其中之一就是被称为RSS的技术。RSS是自早期黑客认识到CGI可以被用来创造数据库支持的网站以来,互联网基础体系中最有意义的
进步。RSS允许人们不仅是链接到一个页面,而是订阅它,当每次有页面更改时,都会有通知。Rich
Skrenta称之为“增加的(incremental)互联网”,其他人则称之为live web(活的互联网)。
当然,现在“动态网站”(也就是数据库支持的网站,有动态产生的内容)在过去10年里代替了静态网页。Live
web所说的动态不仅仅是页面,而是链接。指向一个weblog的链接可能指向一个不停在变化的页面,每一篇单独的条目则有一个永久链接
(permalinks),每个更改都有通知。就这样,RSS feed是比书签或单独页面链接要强大的多的链接。
RSS也意味着浏览器不再是浏览网页的唯一工具。一些RSS聚合器,例如bloglines是基于网络的,其他是桌面客户端,还有一些则允许用户通过便携式设备订阅不断更新的内容。
RSS现在不仅被用来发布blog新条目通知,也被用做各种数据更新,包括股票报价、天气数
据和相片的有效性。这种应用实际上是RSS本源的一种回归:RSS诞生于1997年,来自Dave Winer提出的Really Simple
Syndication技术,一般用来推送blog更新。网景公司的”Rich Site
Summary则允许用户用不断更新的数据流创建自定义的Netscape主页。
Netscape后来失去兴趣,这种技术由blog先驱Winer 的公司Userland继续发展。在目前的应用程序中,我们看到,尽管这是父母双方的遗产。
但是,RSS只是blog区别于普通网页的特点之一。Tom Coates有关于permalink意义的陈述。
看起来,它不过是微不足道的功能,但却是使得blog从一个易于出版现象
转变为重叠群落的对话混乱(a conversational mess of overlapping
communities)。它使得直接表示他人网站上非常明确的帖子第一次变得相对地容易,以及讨论。讨论形成、聊天形成了。结果,友谊形成或变得更加确
立。Permalink是第一次,也是最成功的在blog之间搭建桥梁的尝试。
在许多方面,RSS和permalink的联合给NNTP增加了许多特
性,在HTTP网络协议上。Blog圈可以被理解为早期互联网某些事物新的、对等的等价物,例如Usenet、BBS和会话水坑
(conversational watering
holes)等。人们不仅可以订阅彼此的网站、轻易在一个页面链接个人评论,同时通过trackbacks机制,当其他人链接到此页面时,他们可以看到并
进行回应,不管是通过相互链接还是增加评论。
有趣的是,双向链接是早期超文本系统的目标,例如Xanadu。超文本纯化论者(purists)庆祝trackback,认为这是通往双向链接的
一步。但是注意,trackback不是完全的双向,他们实际上(潜在的)是对称的单向链接,创造了双向链接的效果。这其中的区别看起来似乎很小,但在实
践中却很大。社会性网络系统――例如Friendster, Orkut和
LinkedIn,他们要求得到接受者的承认以建立链接――他们和互联网都缺乏相同的规模可伸缩性。正如flickr合创者Caterina
Fake指出的,注意力只是巧合的相互作用。(flickr就这样允许用户设立监视表――任何用户都可以通过RSS订阅其他用户的照片。注意力的目的是得
到通知,但不必链接)
如果web
2.0的核心之一是利用集体智慧、将互联网变成某种意义上的全球大脑,那么blog圈就是前脑中不断呓语的等价物,我们在大脑中听到的声音。它可能不反映
大脑深层次经常的无意识的结构,但却是有意识思想的等价物。作为有意识思想和注意力的反映,blog圈开始拥有强大的影响。
首先,因为搜索引擎利用链接结构,以帮助预测有用的页面。和大多数多产、及时的linker相似,blogger在修正搜索引擎结果方面有不对称的
角色。第二,因为blog社区太过依赖参考自己,blogger关注夸大可见度和能力的其他人。批评家谴责的回响室(echo
chamber)也是一个放大器(amplifier )。
如果这仅仅是放大器,Blog将变得无趣。但就像维基百科,blog也利用集体智慧作为某种过滤器。James Suriowecki 所说的“大众智慧”开始产生效果了。PageRank产生的结果比其他任何个人文档分析都要好,blog圈的集体关注决定价值取向。
主流媒体可能认为个人blog是竞争者,其实真正让他们失去勇气的是:他们的竞争对手是整个blog圈。这不仅仅是网站之间的竞争,而是商业模式之间的竞争。Web 2.0世界也是Dan Gillmor /所称的“自媒体(we, the media)”。在这里,此前的听众决定什么才是重要的,而不是后台的少数人。
,数据是下一个Intel Inside
每一个重要的互联网应用程序,到目前为止,都由专门的数据库支持。Google的网络爬虫,Yahoo!的目录(和网络爬虫).Amazon的产品
数据库,eBay的产品和商家数据库,MapQuest的地图数据库,Napster的歌曲库。正如Hal
Varian去年在一次个人谈话中谈及的,sql是新的html。数据库管理是web 2.0公司的核心能力,到这样的程度以至我们有时候用”infoware提到这些公司,而不是software(软件)。
这个事实引发了一个关键问题:谁拥有数据?
在互联网时代,人们可以发现许多例子,控制数据库从而控制市场并获得巨额利润。Network
Solutions公司(后来卖给Verisign),最初由政府批准他们在域名注册上的垄断,成为互联网最早挣大钱的公司之一。我们已经讨论通过控制软
件API的商业优势,这在因特网时代更加重要,控制数据源则不是,尤其是如果那些数据源创造起来非常昂贵或有责任通过网络影响增加回报。
看看MapQuest, maps.yahoo.com,
maps.msn.com,或者maps.google.com,等地图网站每张地图底部的版权通知,你会看到这么一行:“地图版权归NavTeq,
TeleAtlas”或者是新的卫星图片服务商,“图片版权归Digital
Globe”。这些公司在数据库商进行了大量投资(据报道,仅NavTeq公司就投资7500万美元建立简历街道地址和方向数据库。Digital
Globe花费了5亿美元发射自己的卫星,以改进政府提供的卫星图片。)NavTeq甚至于模仿英特尔的intel inside
的logo:装有导航系统的汽车,留下”NavTeq Onboard的烙印。数据的确是这些应用程序的Intel
Inside,是那些软件基本来自开源代码或经过改良的系统的唯一来源。
目前地图市场激烈的竞争证明,失败者如何理解拥有应用程序核心数据重要性程度,最终减低其竞争地位。MapQuest从1995年开始地图服务,是
此行业的先驱。而当yahoo和微软google决定进入此行业时,他们只需要获得相同数据的许可,就可以提供具有竞争性的服务。
但是,与此相反的是amazon的地位。和Barnesandnoble.com等竞争者类似,它最初的数据库来自ISBN注册提供商R.R.
Bowker。但与MapQuest不同的是,amazon增加了数据,增加了诸如封面图片、目录、索引和样品材料等出版商提供的数据。更重要的,他们利
用用户注释这些数据。10年后,amazon,而不是Bowker
,成为书籍目录资料的主要来源,也是许多读者、学者和图书管理员的参考资源。Amazon還引入了他们自己与ISBN相对应的产权标志-ASIN。
Amazon就这样有效地包含和扩展了他们的数据提供商。
设想一下,如果mapquest做了同样的事情,让用户注解地图和方向,增加价值。只通过颁发基础数据许可,就会让竞争者进入这一市场更加困难。
Google
maps为应用程序卖家和数据提供商进行竞争提供了一个鲜活的实验室。Google的计划模式导致许多增值服务的产生,以组合形式出现,用其他可接入因特
网的数据源链接google地图。Paul
Rademacher的housingmaps.com,就把google地图和Craigslist公寓出租、家庭购买数据联合了起来,创造了一个互动
的住宅搜索工具,这是类似mashup(译注:似乎可解释为组合)的优秀例证。
目前,这些mashup大多数是创新的试验,由黑客们完成。但是,企业家的活动紧跟其后。人们发现,google至少已经为一个种类的开发者占据了
数据源的地位,并把他们自身作为受欢迎的中介插入。随着数据提供商和应用程序卖主都认识到,某些种类的数据将变成web
2.0应用程序的基础,我们将在接下来几年内看到他们发生战斗。
竞赛在我们自己某些类型的核心数据:位置、身份、公共事物日历,产品标志符和自己的命名空间。很多花费巨资创造数据的案例中,可能有机会使得
Intel
Inside模式运转,在数据只有一个来源的情况下。在其他方面,第一家通过用户聚合、到达临界容量的公司将是获胜者,并将聚合的数据转为系统服务。
例如,在身份领域,PayPal,
Amazon’的1-click以及其他数百万用户的交流系统,都将是建立网络范围身份数据库合法的竞争者。(在这点上,Google最近尝试用手机号码
作为Gmail身份标志符可能是其踏入电话系统的一步。)同时,Sxip等公司正在探索联邦身份的潜在可能,试图找到某种可以提供无缝的web
2.0身份子系统的分布式1-click。在日历领域,EVDB通过wiki式的参与机构,尝试建立世界最大的共享日历。
在关于数据方面,必须指
出另外一点,这就是用户关心的隐私和数据权利问题。在很多早期网络应用程序中,版权很宽松。例如,Amazon认为(但没有强迫),任何提交给站点的评
论,人们可以在其他地方再次张贴相同的内容。然而,随着公司开始认识到控制数据可能是他们竞争优势的主要来源时,尝试控制将有所增加。
和专有软件
(proprietary
software)的增加导致自由软件运动类似,我们希望专有数据库也能在接下来10年内引发一场自由数据运动。我们已经可以在诸如维基百科、
Creative Commons等开源数据项目以及Greasemonkey等软件项目中,发现了这一趋势的早期征兆。
正如在上面关于google和netscape的讨论中所指出的,互联网时代定义特性之一就是,它是以服务方式传递,而不是产品。这导致这样一个公司在商业模式方面,出现很多根本性的改变。
运
转(Operations)必须成为核心竞争能力。Google或yahoo的产品开发专家必须配有一个每日运转专家。因此,基本原则就是把软件从一种产
品转为服务,除非每天都要维护,否则软件不会停止执行任务。Google必须不停的搜索网络、更新其索引,不断过滤垃圾链接以及影响其结果的其他尝试,不
断和动态的对数百万人异步查询进行反映,同时给他们匹配的文本广告。
Google的系统管理、网络和负载平衡技术可能比搜索运算法更加机密,这也就不是偶然了。Google在这些程序自动化方面的成功是面对其他竞争者的优势所在。
诸如Perl, Python, PHP和
现在的 Ruby等脚本语言在web2、0公司扮演巨大角色也不是偶然。Sun公司第一个网络管理员Hassan Schroeder
Perl描述为“因特网的输送带(the duct tape of the
internet)”。动态语言(经常称作脚本语言,被软件工件(software
artifact)时代的软件工程师藐视)是系统和网络管理员的、也是建立需要不停变化的动态系统的应用程序开发者上乘工具。
用
户必须被视作共同开发者,在开放源码发展实践的反思中(即使软件不大可能在开源协议下发行)。开源格言:“早发行,常发行”,这实际上已经变成更加激进的
“永远beta版”,产品开放式发展,每个月、每周甚至每天都有新特性产生。因此,Gmail、google
maps、Flickr、del.icio.us以及其他类似产品可能好几年都带有beta的标志,也就不是意外了。
实时监视用户行为,只是发现使用了哪些新特性,怎么使用,从而成为另外一个必须的核心能力。一个大型在线服务网络开发人员这么评论到:“我们每天在网站的某些部分增加2到3个新特性,如果用户不接受,那么我们就取消。如果他们喜欢,我们将扩大到整个网站。”
Flickr
开发人员Cal
Henderson最近披露,他们每半个小时就配置一些新的特性。这明显是一个完全不同的发展模式!并非所有网络程序都像flickr这样的极端,大多数
网站程序有一个与PC或客户服务器时代完全不同的发展周期。因为这个原因,最近的ZDnet社论总结出,微软不会击败google:微软的商业模式是依靠
人们每2年或3年更新其计算环境,而google则依靠人们每天在计算环境中发现新东西。
微软善于学习其竞争对手,但这次就没有这样的问题了,竞争将要求微软(以此延伸到现在其他的软件公司)变成一个完全不同的公司。诞生之初就是web 2.0的公司有其先天优势,他们没有旧模式(以及相应的商业模式和收入来源)的束缚。
5,轻量级规划模式
一旦网络服务的思想变得为人熟知,有着复杂网络服务、为分布式程序创造高度可信赖规划环境的大公司将进入竞争。
但是,和网络抛弃其大部分超文本理论而成功相似,用简单的实用主义取代理想主义设计,RSS因为其简单性,可能是最为普遍的网络服务。而复杂的合作网络服务还没有得到广泛配置。
与此相似,Amazon用两种方式提供网络服务:一个附着于SOAP形式网络服务堆栈,另外一个只通过HTTP提供XML数据,在轻量级方式中有时候被称为REST. 高端的B2B链接都使用SOAP堆栈,而95%都使用轻量级的REST服务。
在其他“有机的”网络服务中,也有相同的问题。Google最近的地图服务就是恰当的例子。Google 地图简单的AJAX界面迅速被黑客所解释明白,然后他们混合这些数据,形成新的服务。
与
地图相关的网络服务出现有一段时间了,从诸如ESRI 、MapQuest以及微软MapPoint
等地理信息系统卖家出现。但是google地图由于其简单性,在全世界燃起了一场大火。体验任何正规卖家支持的网络服务都需要双方正规的合同,
google地图贯彻的途径是让数据为人们所利用,黑客不久就发现方法重新使用这些数据。
这里有几个很有意义的训诫:
支持允许松散连接系
统的轻量级程序模式。企业赞助的网络服务堆栈的复杂性,就是为了使紧密的链接变得可能。在很多情况下这是必要的,但大多数有趣的应用程序实际上可以保持松
散、甚至脆弱的连接。Web
2.0的精神与传统的IT精神大相径庭!想象一下聚合,而不是调和。例如RSS和基于REST的网络服务等简单的网络服务,当它到达连接的另外一端时,是
关于向外的聚合数据,而不是控制发生了什么。这种思想是互联网本身的基本原理,是端到端(end-to-end)的反映。
为hackability和remixability设计
诸如原始网络、RSS
、AJAX等系统都有一些共同点:重新使用的门槛极其低。很多有用的软件实际上是开放源代码的,即使它不是,在知识产权保护方面障碍也不多。网页浏览器的
“查看代码”使得任何用户都有可能复制他人的网页,RSS是被设计用来使得用户可能观看他/她想要的内容,当需要时,不是由于信息提供商的命令;最成功的
网络服务是那些最容易吸收新的方向,甚至是创造者自己也没想到的。与更加典型的“版权所有”相比,通过“创作共用”流行起来的“保留部分版权”就是有用的
路牌,
集体创新
轻量级商业模式是轻量级程序和链接的自然衍生物。Web
2.0的精神是易于重新使用。例如ousingmaps.com这样的新服务就是通过简单组合两个已经存在的服务。Housingmaps.com还没有
商业模式,但是对很多小型公司来说,google adsense(也可能是Amazon的associates
fees,或者是两者)提供了同等的利润模式。
这些例子给另外一个web 2.0原则提供了洞察力,我们称之为
集体创新。当必需成分很丰富时,你可以很简单的以新奇或有效的方法将他们组合在一起,创造新的价值。和PC革命为组装必需硬件提供了很多创新机会类似,像
DELL这样的公司就从这样的组装中产生了学问,从而击败那些商业模式需要产品发展创新的公司。我们相信web
2.0将给很多公司提供机会,在利用和整合其他人提供的服务方面做的更好,击败竞争对手。
6,超越单一设备的软件
Web
2.0另外一个应该引起关注的特性是,它不再局限于PC平台。微软资深开发者Dave
Stutz在给微软的离别建议中指出,“在单一设备之上编写的有用软件,将在未来很长时间内,掌握高利润。Useful software
written above the level of the single device will command high margins
for a long time to come.”
当然,任何网络应用程序都可以被视作超越单个设备的软件。毕竟,即使最简单的网络程序也涉及到了两台电脑:主机服务器和用来浏览的电脑。正如我们讨论过的,作为平台的网络的发展将这种思想延伸到了由多台电脑组成的服务合成程序。
但是,由于在很多web 2.0领域里,2.0-ness并非新东西,而是网络平台真实潜力的实现,这个短语在观察如何为新平台设计应用程序和服务方面,给了我们关键的洞察力。
到
目前为止,iTunes是这个原则最好的例证。iTunes无缝地从手持设备到达后端的网络,PC扮演本地CACHE和控制台的角色。此前有很多将网络内
容带到便携式设备上的尝试,但是iPod/iTunes组合是完全跨越多设备的应用程序之一。TiVo是另外一个很好的例子。
iTunes 和 TiVo也证明了其他很多web
2.0的核心原则。他们本质上不是网络应用程序,但是他们利用了网络平台的力量,使之无缝、并几乎它们基础构造的一部分。数据管理明显是它们的心脏。他们
是服务,而不是应用程序包(尽管在Itunes的情况下,它可以被用作打包程序,只管理用户的本地数据)。更重要的是,TiVo 和
iTunes都显示出了利用集体智慧的萌芽,尽管在各自的例子中,他们的试验还在与IP通道(IP
lobby)进行战争。iTunes只有一个受限制的参与构建,尽管最近podcsting的加入充分改变了这种因素。
这就是web
2.0其中的一个领域,随着越来越多的设备被连接到新平台,我们将看到一些伟大的变革。当我们的电话和汽车不是消费数据而是报告数据时,还将出现什么样的
应用程序?实时的交通情况监视、快闪暴走族(flash mobs)、公民媒体,这些只是新平台能力的少数几个早期信号而已。
Web 2.0投资论文
风险投资商Paul Kedrosky写道:关键是在你与大众意见不一致的地方,发现可行的投资。
观察web 2.0的每个侧面是如何包括了与多数人不同的意见是很有趣的事情:每个人都强调保持数据隐私, Flickr/Napster等却使它公开化。这不仅仅是为不同意而不同意,这是让从分歧中找出有用东西。Flickr建立社区,napster建立连接宽度。
观
察这个的另外一条道路是,成功企业都放弃了一些昂贵,但被认为在免费获得曾经昂贵的有价值的东西方面很重要。例如,Wikipedia放弃了中央编辑控
制,换来了速度和规模。Napster放弃了“目录”的思想,得到规模。Amazon放弃了物理店面的思想,换来的是服务全世界。Google(最初)放
弃了大客户思想,得到了80%
需求得不到满足的人。在下面这句话里,颇有一些合气道(以敌力制敌)的意思,“你知道,你是对的,世界上任何人绝对都能更新这篇文章。猜什么呢,这是你的
坏消息?”Nat Torkington
7,富用户体验
早在Pei Wei1992年发明的
Viola浏览器中,网络就被用来在浏览器中传输applets以及其他活动内容。1995年,java的引入给这样的applet传输加了外框。
JavaScript和DHTML随后被以轻量级方式引入,以提供客户可编程性和富用户体验。几年前,Macromedia发明了富因特网应用程序
(Rich Internet Applications)这个词汇(开源Flash竞争者Laszlo
Systems也使用这个词汇),以突出Flash的能力:传输的不仅是多媒体内容,而是还有图形用户界面方式的应用程序体验。
但是,网络传输全
尺寸应用程序的潜力并没有对主流形成打击,直到Google推出了Gmail,接着就是Google地图,基于web的应用程序,有富用户界面和与PC等
同的相互作用。Google使用的技术被称作AJAX,在网络设计公司Adaptive Path的Jesse James
Garrett写的一篇文章中,他这样写道:
Ajax不是一项技术。它其实是好几项技术,每一项都自己繁荣,然后以新的强大方式走到一起了。Ajax包含了:
使用XHTML CSS的基于标准的陈述
使用Document Object Model的动态显示和互动
使用XML 和 XSL数据交换和处理
使用XMLHttpRequest的异步资料检索
JavaScript把他们所有绑定在了一起。
Ajax也是诸如Flickr等web 2.0应用程序的关键组成部分,37signals应用程序的大本营和基础。也是Google的Gmail和Orkut的基础。我们进入了一个用户界面创新的空前时期,网站开发者最终能够建立和PC应用程序一样丰富的网络应用程序。
有
趣的是,很多现在被探索的能力已经有不少年历史,在90年代末期,微软和网景都想象到了现在才实现的一些能力,但是他们对标准的战争使得跨浏览器程序变得
困难。只有到微软最终打败其他浏览器,才有事实上的单一浏览器标准可以写入,这种应用程序才变得可能。Firefox再次打起浏览器市场大战的时候,至少
目前我们还没有看到导致阻止90年代发展的、浏览器标准的毁灭性竞争。
我们将在接下来几年里看到许多新的网络应用程序,包括真正新奇的和PC程序在网络上的重新导入。每个平台的改变也都创造了一个机会,即改变在此前平台占主导地位的程序。
Gmail
早已给电子邮件带来了有趣的创新,把web的力量(易于到达、深刻的数据库能力和可搜索性)和在可用性上接近PC的用户界面接合在一起。同时,其他PC平
台上的邮件客户端正在从另外一端一点点解决问题,增加了IM和呈现能力(presence capabilities)。我们离整合了EMAIL,IM
手机优点的通信客户端还有多远?使用VOIP技术,给网络程序的富能力增加语音能力。竞赛还在继续。
看web
2.0怎么重制地址本很容易。Web
2.0样式的地址本将把本地电脑或手机的地址本仅仅视作你明确让系统记住的联系人仓库。同时,基于web、Gmail式的同步代理将记住每一个发出或收到
的信息、每个电子邮件地址和使用过的电话号码。并且,具有社会性网络启发,以决定当本地找不到答案时,用哪个来代替。如果没有答案,系统将在更广阔的社会
性网络进行查询。
Web 2.0文字处理将支持wiki式的集体编辑,而不仅仅是单独的文档。但是,它也将支持我们在PC文字处理上的富格式。Writely是这样程序的一个好例子,尽管它还没有引起广泛注意。
Web 2.0的革命也将不会局限在PC应用程序上,Salesforce.com证明网络可以如何用来作为服务传输软件,在企业级应用程序例如CRM.方面。
新入者的竞争机会是全力发掘web 2.0的潜力。胜出的公司将学习用户,创造应用程序,使用参与机构,不仅在软件界面,而且在共享数据的丰富程度上建立优势。
Web 2.0公司核心竞争能力
在探索上述的7个原则的过程中,我们突出强调了web 2.0的一些原则性特点。我们所举的每一个例子都证明了一个或多个这些关键原则,但是也可能丢失了其他的。以下总结web 2.0公司的核心竞争能力,作为结尾。
•服务,而非软件包,具有高效低成本的可扩展性
•控制独特、难以再造的数据源,越多人使用,就越丰富
•把用户当成合作开发者一般信任
•借力于集体智慧
•从消费者自身服务中产生长尾优势
•超越单一设备层次的软件
•轻量级的用户界面、开发模式和商业模式
加有相同水印的话,使用上述方法就未免太繁琐了。其实巧妙应用“页眉页脚”命令,可以轻松完成为每页添加水印的操作。方法如下:
1. 制作好文档后,通过“视图→页眉页脚”命令,调出“页眉页脚”工具栏,单击其中的“显示→隐藏文档正文文字”按钮,隐藏正文部分的文字内容。
2. 选择“插入”菜单中的“文本框”命令,在页眉的下方插入一个空文本框。
3. 在文本框内加入作为水印的文字、图形等内容,右击图片,选择快捷菜单中的“设置图片格式”命令,在对话框中“图片”选项卡下,通过“图像控制”改变图像的颜色,对比度和亮度,并手动调整图片的大小。
4. 通过“设置文本框格式”命令,把文本框的线条色改为无线条色。
5. 单击“页眉页脚”工具栏的“关闭”按钮,退出“页眉页脚”编辑。
完成上述步骤的操作,水印制作得以完成,这样就为每一页都添加了相同的水印。