[March , 25, 2008] Watcom C 的笔记13 Watcom C (13)

Watcom C 的笔记13 Watcom C (13)

               生成 .COM 文件的 BUG?

	如下程序:

#include <stdint.h>
main ()
{
 uint32_t wsize=64*1024,length=0x12345678;
 return length/wsize;
}

	编译命令:

SET WATCOM=\watcomc
set include=%watcom%\h
path %watcom%\binw
wcc bbb.c /ms
wlink system com file bbb.obj

        运行期会出现除零错误 “Divide overflow”。

        查看代码发现它使用下面的代码处理这个乘法

seg000:030F                 mov     ax, 5678h
seg000:0312                 mov     dx, 1234h
seg000:0315                 xor     bx, bx
seg000:0317                 xor     cx, cx
seg000:0319                 call    sub_104DB
seg000:031C                 pop     dx
seg000:031D                 pop     cx
seg000:031E                 pop     bx
seg000:031F                 retn
seg000:031F sub_10306       endp

        子程sub_104DB是使用bx作为除数的除法过程。计算这个除法的

过程很有意思,是分开,先计算 1234h / bx ,再计算 (余数+5678h)/ bx

的。也许这样处理应该是防止结果过大溢出的吧。

         如果让代码生成 DOS4GW的 EXE文件就不会有这样的问题。

暂时的猜测是 Watcom C 生成 COM 是默认在16位代码方式下的。它在处理

32位的数据上是有问题的,应该避免这样的情况。

							Zoologist
							2008-3-25

[March , 24, 2008] Watcom C 的笔记12 Watcom C (12)

Watcom C 的笔记12 Watcom C (12)

		函数指针的简单试验

    函数指针是:指向一个函数的指针。

#include <stdio.h>
void (*ptr)();

void func1()
{
	printf( "This is function 1 \n" );
}

void func2()
{
	printf( "This is function 2 \n" );
}

int main()
{
	ptr=func2;
	(*ptr)();
	ptr=func1;
	(*ptr)();
   return( 0 );
}

				Zoologist
				2008-3-20

参考:

【1】:来自 http://www.yourblog.org/Data/20041/2519.html
       作者:在黑暗中举起探索的火炬
指针函数和函数指针有什么区别

1,这两个概念都是简称,指针函数是指带指针的函数,即本质是一个函数。我们知道函数都又返回类型(如果不返回值,则为无值型),只不过指针函数返回类型是某一类型的指针。其定义格式如下所示:

返回类型标识符 *返回名称(形式参数表)
{ 函数体 }

返回类型可以是任何基本类型和复合类型。返回指针的函数的用途十分广泛。事实上,每一个函数,即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个“变量”。例如下面一个返回指针函数的例子:

#include

float *find();
main()
{
    static float score[][4]={{60,70,80,90},{56,89,34,45},{34,23,56,45}};
    float *p;
    int i,m;
    printf("Enter the number to be found:");
    scanf("%d",&m);
    printf("the score of NO.%d are:\n",m);
    p=find(score,m);
    for(i=0;i<4;i++)
        printf("%5.2f\t",*(p+i));
}

float *find(float(*pionter)[4],int n)/*定义指针函数*/
{
    float *pt;
    pt=*(pionter+n);
    return(pt);
}

学生学号从0号算起,函数find()被定义为指针函数,起形参pointer是指针指向包含4个元素的一维数组的指针变量。pointer+1指向 score的第一行。*(pointer+1)指向第一行的第0个元素。pt是一个指针变量,它指向浮点型变量。main()函数中调用find()函数,将score数组的首地址传给pointer.

2,“函数指针”是指向函数的指针变量,因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上一致的。函数指针有两个用途:调用函数和做函数的参数。函数指针的说明方法为:
数据类型标志符 (*指针变量名)(参数);注:函数括号中的参数可有可无,视情况而定。
下面的程序说明了函数指针调用函数的方法:

#include

int max(int x,int y){ return(x>y?x:y); }

void main()
{
    int (*ptr)();
    int a,b,c;
    ptr=max;
    scanf("%d,%d",&a,&b);
    c=(*ptr)(a,b);
    printf("a=%d,b=%d,max=%d",a,b,c);
}

ptr是指向函数的指针变量,所以可把函数max()赋给ptr作为ptr的值,即把max()的入口地址赋给ptr,以后就可以用ptr来调用该函数,实际上ptr和max都指向同一个入口地址,不同就是ptr是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你像怎么做了。在程序中把哪个函数的地址赋给它,它就指向哪个函数。而后用指针变量调用它,因此可以先后指向不同的函数,不过注意,指向函数的指针变量没有++和--运算,用时要小心。

junglesong 转载www.chinagpa.com

[March , 20, 2008] Watcom C 的笔记11 Watcom C (11)

Watcom C 的笔记11 Watcom C (11)

				结构体对齐

    我是不主张使用汇编语言学习算法的,最主要的原因是汇编语言并不适合

构造复杂的数据结构。当然,在内存特别紧张的情况下,必须使用汇编语言做算法,

将会是非常痛苦的事情。我曾经研究过一段压缩代码,从上面的部分特征来看,

似乎是先用C语言实现算法,再尝试逐步转化为汇编语言的。额外的话题:BIOS

中的代码大部分是压缩后再存放的。AMI的压缩比大约高出Award 20-30%(个人

估计:)

    下面是我写程序中使用的结构体,编写中需要确定结构体中每个成员是否处在内存

中正确的位置,诸如freg0要在 50h 偏移处......

    struct spistruct{  			
      		uint32_t bfpr;	//00-03 BIOS Flash Primary Region
      		uint16_t hsfsts;//04-05 Hardware Squuencing Flash Status
      		uint16_t hsfctl;//06-07 Hardware Sequencing Flash Control
      		uint32_t faddr; //08-0B Flash Address
      		uint32_t rsrvd1;//0C-0F Reserved    		      		
      		uint8_t  fdata[0x40];//10-13 Flash Data 0
      				     //14-4F Flash Data N
      		uint32_t fracc; //50-57 Flash Region Access Permissions     		
      		uint32_t freg0; //54-57 Flash Region0
      		uint32_t freg1; //58-5B Flash Region1     		      		
      		uint32_t freg2; //5C-5F Flash Region2
      		uint32_t freg3; //60-63 Flash Region3     		      		      		
      		uint32_t freg4; //64-67 Flash Region4      		
      		uint8_t  rsrvd2[12];//68-73 Reserved    		      		      		
      		uint32_t fpr0;  //74-77 Flash Protected Range 0
      		uint32_t fpr1;	//78-7B Flash Protected Range 1
      		uint32_t fpr2;  //7C-7F Flash Protected Range 2
      		uint32_t fpr3;  //80-83 Flash Protected Range 3
      		uint32_t fpr4;  //84-87 Flash Protected Range 4
      		uint32_t rsrvd3[2];//88-8F Reserved
      		uint8_t  ssfsts;//90    Software Squency Flash Status
      		uint16_t ssfctl1;//91-92        		      		      		
      		uint8_t  ssfctl2;//93	 //91-93 Software Squency Flash Control
      		uint16_t preop;	//94-95 Prefix Opcode Configuration
      		uint16_t optype;//96-97 Opcode Type Configuration
      		uint8_t opmenu[8];//98-9F Opcode Menu Configuration      		
      		uint32_t bbar;	//A0-A3    BIOS Base Address Configuration
       		uint32_t rsrvd4[3];//A4-AF Reserved
      		uint32_t fdoc;	//B0-B3 Flash Descriptor Observerability Control
      		uint32_t fdod;	//B4-B7 Flash Descriptor Observerability Data      		
       		uint32_t rsrvd5[2];//B8-BF Reserved4
       		uint32_t afc;		//C0-C3 Additional Flash Control Register     
       		uint32_t lvscc; //C4-C7 Host Lower Vendor Specific Component Capabilities Register      		 		
       		uint32_t uvscc; //C8-CB Host Upper Vendor Specific Component Capabilities Register
       		uint32_t rsrvd6;//CC-CF Reserved
       		uint32_t fpb;		//D0-D3     		
} ;

    最初,我采用的是笨办法:给成员赋值,然后查看是否出现在应该的内存位置。

后来发现了一个很好用的宏,它是标准库的扩充,可以直接使用:

	#define offsetof(type,memb) ((size_t) & ((type *) 0)->memb 参考[2]

    使用的例子: printf("[%lx]  [%x]\n",spi->opmenu[0],offsetof(struct spistruct,opmenu) );

    随后调试中又发现 ssfctl1 成员总是不能处在正确的位置上。经过研究发现,

结构体中有一个"对齐"的问题。因为内存如果对齐的话,会节省访问时间。我们希望

成员处于91h的位置,而由于对齐,编译器会将其放在92h处对齐。

    Watcom C 中上述问题可以用下面的方法解决:

#pragma pack(1)		//指定按照 1 byte 对齐
struct spistruct{  			
      		uint32_t bfpr;	//00-03 BIOS Flash Primary Region
      		uint16_t hsfsts;//0
          ... ... ... ...
          ... ... ... ...
          ... ... ... ...

参考:

[1] 败中求胜——Qdieyou个人工作室
http://blog.csdn.net/Qdieyou/archive/2007/04/26/1585715.aspx

[2]《C语言参考手册》 P325

						Zoologist
						2008-3-20

[March , 17, 2008] DOS下的C语言编辑器 我之前之前一直使用Boxer,感觉很糟糕。这个编辑器似乎是专门为Watcom C 设计的,不过最好不要配置在IDE中编译,我试验发现编译经常会死机。也许以后有时间我会考虑写一个类似Turbo C的编辑器吧。

DOS下的C语言编辑器 我之前之前一直使用Boxer,感觉很糟糕。这个编辑器似乎是专门为Watcom C 设计的,不过最好不要配置在IDE中编译,我试验发现编译经常会死机。也许以后有时间我会考虑写一个类似Turbo C的编辑器吧。

wc

[March , 15, 2008] Watcom C 的笔记10 Watcom C (10)

Watcom C 的笔记10 Watcom C (10)

		局部变量在内存的位置

    在编写一个com文件的时候,遇到奇怪的问题:编译正常,运行后显示

“Stack Overflow!”。程序片段如下:

#include <stdio.h>

int main(int argc,char *argv[])
{
 int i,n;
 FILE *f;
 long int sum=0;
 char buffer[2048]=; //1K 就在这里指定一个做buffer的变量

 if (argc!=2) {printf("usage: checksum file

     试验表明,如果在1024之上就会出现这个错误。在编译过程中,还发现

这个地方的大小不会影响生成com文件的大小。查阅资料【参考1】,上面说

局部变量存放在堆栈中... ...因此如果太大了自然会出现堆栈溢出的问题。

    设置为全局变量就可以了,这样并不会影响生成文件的大小,看起来仍然是

在运行过程中动态分配的。分配在代码和堆栈之间的空间。具体如何确定,还不

清楚。

    编译批处理文件 mkchk.bat
     wcc checksum /ms 
     wlink system com file checksum
    源程序:
#include <stdlib.h>
#include <stdio.h>

char buffer[0xC000]; //48K

int main(int argc,char *argv[])
{
 int i,n;
 FILE *f;
 unsigned long int sum=0;

 if (argc!=2) {printf("usage: checksum filename\n"); exit(1);};

 //argv[0] is executable file name
 f=fopen(argv[1],"r+b");
 if (f==NULL) {printf("File open error!");}

 n=fread(buffer,sizeof(char),sizeof(buffer),f);
 while (n!=0)
   {
   	for (i=0;i<n;i++)
   	  {
   	  	sum=sum+buffer[i];
   	  }
 n=fread(buffer,sizeof(char),sizeof(buffer),f);   	  
   }

 printf("Checksum in BYTE:  %12hX\n",sum & 0xFF);
 printf("Checksum in WORD:  %12X\n", sum & 0xFFFF);
 printf("Checksum in DWORD: %12lX\n",sum);  
 return 0;
}

    运行结果:

G:\watcomc>checksum dos4gw.exe
Checksum in BYTE:            40
Checksum in WORD:          3E40
Checksum in DWORD:     F0153E40    

参考:

1.http://topic.csdn.net/t/20040316/12/2848204.html

				2008-3-15
				Zoologist

[March , 10, 2008]风俗问题

            		风俗问题

    有一段时间,我认为只有有钱人家风俗多,规矩多,后来发现根本不是

那么回事。比如,我在家吃饭的时候,俺爹总是教育我吃米饭不要趴着吃,

要端起碗吃。又说,这要是在老家你奶奶肯定一筷子打过来。等到我上学了,

工作了,用不锈钢盘子吃饭觉得很别扭,总想端起来吃,而端起来之后又过于

与众不同,大多数人都会认为你要舔盘子。另外,在老家,吃咸鸭蛋的时候,

也有要求必须从什么大头打开,而我总是敲到实心的那一端。

    现在我家保留着最有特点的风俗就是烧纸。通常是在过年前几天,买好

黄纸。在另外一张纸上,如同信封一般,写好“收件人”,不过都是“匿名信”,

决不能落款否则不吉利。给不同人的要分开写,分开烧。也从来没见过“XXX

同志转XXX同志收”,想必下面的世界邮政事业很发达。

    一般都在十字路口烧,莫非是因为交通方便?烧之前,先要检几个树枝

在地上画一个圈,在圈中烧。点燃之后,用树枝翻动,这样才能烧得干净。

在烧的过程中,还要再点燃几张放在圈外,意思是给路过的“大鬼”“小鬼”。

一边烧,还要一边念去世亲属的名称,说来收钱吧。

    我第一次正式执行这个风俗大约是在大二的寒假,在这之前通常只有回老家

才会烧纸。下楼,画圈,烧纸。然后按照妈妈教导念道“爷爷、姥爷来收纸吧”。

俺娘笑,我急忙改口说来收钱吧。心里想着大家都烧,多影响人家的金融秩序啊,

不知道会不会通货膨胀啊?

    今年回去也烧了。和父亲一起去的。路口有清洁工一直守候在那。看见

我们来,直接递给我们两个树枝。随后聊了几句得知,他们每天都会守在这里,

直到夜里12点,负责打扫灰烬。前几天还有城管带着大铁槽,要求人们直接在

里面烧。地上是大片烧过留下的黑色。

    妈妈说,很多很多年以前,烧纸是作为封建迷信活动严加禁止的。只是

后来又慢慢开始了。按道理说我是一个无神论者,不过俺娘说,这是风俗,

让你不要忘记先人。并且,我还是很喜欢烧东西这样事情的。

    以前有一个笑话是说,中国人摆上水果供在墓碑前,洋人是摆放着鲜花的。

洋人就问道:你们的先人什么时候出来吃这些东西啊?中国人回答,当你们的

先人出来闻鲜花的时候,我们的先人就出来吃东西了。

							Zoologist
							2008-3-10

[March , 7, 2008] Watcom C 的笔记9 Watcom C (9)

Watcom C 的笔记9 Watcom C (9)

          运算优先级的问题

    C语言中,“+ -”的优先级高于“<< >>”因此,下面这段程序无法得到期望值:

#include <stdio.h>
#include <stdint.h>
void main ()
{
	uint8_t		command_write=1;
	uint8_t		command_read=2;
	uint8_t		command_erase=3;
	uint8_t		command_read_status=4;

	uint64_t rst=0xFFFFFFFF;

	rst =       command_write+
			command_read<<8+
			command_erase<<16+
			command_read_status<<24;

	printf("[%08lX]\n",rst);
}	

     最简单的方法是将表达式改为下面的形式:

	rst =       command_write+
			(command_read<<8)+
			(command_erase<<16)+
			(command_read_status<<24);

					9:23 2008-1-21

[March , 3, 2008] Watcom C 的笔记8 Watcom C (8)

           随机函数

    stdlib.h 中的 int rand(void) 产生随机数。需要注意的是,生成的

范围是 0-32767。 直接使用这个参数生成的是伪随机数。

#include <stdio.h>
#include <stdlib.h>
int main()
{
 int i;

  for (i=0;i<0x200;i++)
   {
   	if ((i % 0x10)==0) {printf("\n");}
    printf("%5d ",rand());
   }
 return 0;
}

    上面这个程序将生成256个随机数,因为产生的是伪随机数,因此,两次

运行产生的结果是相同的。

    使用 void srand( unsigned int seed ) 会指定随即种子,但是对于

相同的随机种子,产生的序列还是一样的 ... ... 看来生成随机数真的有必要

使用时钟生成种子。

    clock_t clock(void) 是同 rdstc 类似的函数,

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <limits.h>
void compute( void )
{
int i, j;
double x;
x = 0.0;
for( i = 1; i <= 10000; i++ )
for( j = 1; j <= 10000; j++ )
x= sqrt( (double) i * j );
printf( "%16.7f\n", x );
}
void main()
{
  clock_t starttime,endtime;
	starttime=clock(); //开始计时
	compute();
	endtime=clock();   //停止计时
	printf( "Execution time was %lu \n",(endtime-starttime)/CLOCKS_PER_SEC); //显示经过了多少秒
 }

     使用time函数获取当前时间,结果是32位的unsigned long,想必可以

用来做随机种子。

#include <stdio.h>
#include <time.h>
void main()
{
  time_t time_of_day;
  time_of_day = time (NULL);
	printf( "%lu \n",time_of_day);
 }

						Z.t
						2008-1-22

[February , 27, 2008] 回乡偶记

		     回乡偶记

(一)回来之后,听亲戚讲述我奶奶家的历史,让我颇感惊讶。第一件事情是,

我爷爷是48年县城的第一批秘密党员。那时候还没有解放,秘密入的;第二件

事情是,我奶奶家是地主。汗,我一直以为我是“根正苗红”的“三代贫农”

----不过要从我爷爷出身算,是。

(二)飞回来上班的。印象深刻的是坐火车次次都有人广播找医生;坐飞机似乎

次次都有带小孩子的,哭声不断。

(三)飞机上打起来了。一个男人,带着一个女的,女的怀抱很小的孩子,还有

个大约11岁的男孩,不知道什么关系。飞机上的空间挺小的,前面的一个人将

座椅放下来了,大约是阻挡了他的活动吧,双方发生了争执。只见这个人,

向前一探,一把将前排那个乘客的衬衣撕开了。声音很响,刹那间“锦帛相见”

这个词语竟然跳入脑中... ...稍后,飞机上的工作人员过来了,请后面的这个

人到后舱。随后,传出那个男人的声音。他说,这要是在地上,早把他的脑袋

拧下来了... ...又说,我们东北人就是这样,这也不是什么歧视的问题... ...

听了然人觉得好笑。这么大的脾气至少是个“处长”什么的吧。随后,抱着孩子的

女子,急忙掏出200块,求前面的男的私了。再后来,飞机准备降落了,机上工作

人员拿了4个装呕吐物的袋子(没用过的),让旁边的乘客帮着写下事情的经过。

衬衣被撕坏的乘客始终坚持必须道歉,而施暴者坚持不肯。另外也只肯出200块。

再后来,飞机降落了,其余的乘客鱼贯而出,飞机上的这5个人准备到地面解决问题。

坐我前面的女乘客走到“受害者”面前说,最看不惯你这种人,欺负孤儿寡母的,

还说衬衣500块,这不是讹人吗(一时间我都闹不清楚逻辑关系了)。那个人,

讪讪无语。东北女人豪爽,汗~

(四)飞机准备降落了,空中小姐敲厕所门,让里面的人抓紧出来... ...和火车差

不多啊~ 火车到站,就要关闭厕所,通常是乘务员狂敲一阵让里面的人出来。我也

多次见过乘客面目狰狞的恳求乘务员通融一下,行个方便。

(五)在家掰辣椒。晾干的红辣椒准备做辣椒油吃。我用手揉碎之。摸了一下鼻子,

立马火烧火燎。求助于老娘,老娘说,你要用剪子,或者套上塑料带,直接用手会很辣,

严重的话手指都会觉得辣。哎,你真没有生活常识啊。辣了吧,用醋试试。我急忙找了

个棉签,沾上醋,擦之。无效,还仿佛扩散了。黄豆大的汗珠不断落下。忽然问,

这个好用吗?老娘曰,不知道,不过你看凉菜咸了或者辣了都是加醋啊!后面我又偷着

试验了油性的化妆品,也不好用。最后打了一盆凉水,扎在里面了......

    晚上,睡觉的时候舔了一下手指,还是很辣的,不敢乱摸了。

					Zoologist
					2008-2-27