編寫程序如下:
編譯連接並用debug加載,觀察main函數的內容:
Showchar函數的內容:
觀察發現,main函數要傳遞兩個參數‘a’和2,在匯編代碼中是先將2賦給ax,再將ax入棧,然後將a賦給ax,將ax入棧。在showchar函數中,程序將sp賦給bp,再從bp+4處取出形參a賦給al,再將al中的值賦給b800:690h,然後再從bp+6處取出形參b賦給al,再將al中的值賦給b800:691h。可見main函數給showchar傳遞參數是把要傳遞的值賦給ax,再將ax入棧,且如果有多個要傳遞的值,是由後往前將參數入棧。Showchar函數接收參數是將sp賦給bp,然後由bp+4找到棧中存儲的參數a,由bp+6找到棧中存儲的的參數b,為什麼是bp+4和bp+6呢?因為程序在將兩個參數入棧後,call指令將showchar的地址入棧占2個字節,在showchar中將bp入棧又占2個字節,所以要由bp+4找到第一個參數的地址。那麼我對此提出三個問題:
(1) main函數將char型數據和int型數據入棧是占2個字節,那麼如果是float型或者long int型、double型、long double型等超過2字節的變量類型怎麼辦?
(2) Showchar函數將棧中取出的參數賦給al,為什麼2是int型也只賦給一個字節的al?如果是更大的參數怎麼辦?
(3) 我們注意到這一個指令
是把al賦給es:[bx],是不是所有非ds的段寄存器都要像這樣寫?
對於第一個問題,我們把程序中的char和int改成float和double看看:
編譯連接,用debug查看,main函數為:
Showchar函數為:
發現main函數的入棧值為:4008、0000、0000、0000、4006、6666.
再看showchar函數的內容,查資料發現int 35h的作用是讀取中斷向量,但是不知道它的具體功能,inc si和les ax,[06fb];int 39的作用是什麼呢?
這裡我對於c語言的一些語句在匯編裡的實現還是有的不理解,但是這不是我們研究的重點,既然暫時弄不懂,就先跳過這個問題。
再看第三個問題,發現所有es作段地址的指令都是如上格式,ds作段寄存器的指令都把ds省略了。
再來看下一個程序:
編譯連接,用debug加載查看,main函數為:
Showchar函數為:
觀察C語言的showchar函數可以發現:第一個參數n是要顯示的參數數量,第二個參數color是要顯示的參數顏色,之後的就是要顯示的參數。Showchar函數通過參數n來知道要顯示多少個字符。然後通過循環來調用寄存器從棧中提取參數。
但是printf函數的參數是要直接輸出的,沒有一個參數是告訴它下面有多少個參數。但是printf裡面是要輸入%c或者%d,那麼函數是通過統計%c和%d的數量來判斷要輸出多少參數的嗎?我們寫一個printf函數來看看:
編譯連接並用debug加載有:
這裡是將參數1和2入棧,再入棧194,然後執行printf函數,那麼194有什麼作用呢?查閱資料知,程序將%c和%d等符號放在偏移地址0194處,結尾加0,通過統計該地址處的%個數來確定要輸出的字符數量。所以peintf函數和showchar函數的區別就是showchar函數參數個數已給出而printf函數是要根據%c或%d個數來確定參數個數而已。那麼我們要實現簡單的printf函數,可以在showchar函數的基礎上來改動。
下面是網上找的代碼:
void printf(char *,...);
int pow(int, int);
main()
{
/*printf("I think this is interesting :%c and %c and %c",0x61,0x62,0x63);*/
printf("No.%d,%d,%d,this is me %c ing yuan",45,123,8958,'Q');
}
void printf(char *des, ...)
{
/*first sure the length of string des*/
int len=0;
int i=0;
int showp=0; /*define the point of showing chars*/
int parap=0; /*define of parameter position in stack*/
int intValueLength=1;
int signalNum=0;
/*calculate length of stirng des */
while(des[i]!='/0')
{
len++;
i++;
}
i=0;
while(des[i]!='/0')
{
if(des[i]=='%')
{
/*check type of value user want to show.*/
if(des[i+1]=='d')
{
/*here show integer value*/
int showIntValue=*(int *)(_BP+6+parap+parap); /*here, we show understand that define one char point value, we just push the point value into stack, but not the string value*/
int reValue=showIntValue;
/* *(int far *)(0xb8000000+160*10+80+showp+showp)=showIntValue;
*(int far *)(0xb8000000+160*10+81+showp+showp)=2;*/
i+=2;
parap++;
intValueLength=1;
/*here we calculate the length of integer value we want to show ,and then we can sure the next value show position*/
while(reValue/10!=0)
{
intValueLength++;
reValue/=10;
}
/*first calculate the length of unmber and show every sigal positon number of Integer */
signalNum = showIntValue/pow(10,--intValueLength);
*(char far *)(0xb8000000+160*10+80+showp+showp)=signalNum+48; /*show the highest signal number*/
*(char far *)(0xb8000000+160*10+81+showp+showp)=2;
showp++;
while(intValueLength!=0)
{
showIntValue=showIntValue-signalNum*pow(10,intValueLength);
signalNum= showIntValue/pow(10,--intValueLength);
*(char far *)(0xb8000000+160*10+80+showp+showp)=signalNum+48; /*show the highest signal number*/
*(char far *)(0xb8000000+160*10+81+showp+showp)=2;
showp++;
}
/*showp+=intValueLength;*/
}
else if (des[i+1]=='c')
{
/*here show charactor value*/
*(char far*)(0xb8000000+160*10+80+showp+showp)=*(int *)(_BP+6+parap+parap); /*value of _BP and distance address of CALL order*/
*(char far*)(0xb8000000+160*10+81+showp+showp)=2;
parap++;
showp++;
i+=2;
}
else /*direct show char value in string des*/
{
*(char far *)(0xb8000000+160*10+80+showp+showp)=*(int *)(*(int *)(_BP+4)+i);
i++;
showp++;
}
}
else /*also direct to show char in des*/
{
*(char far *)(0xb8000000+160*10+80+showp+showp)=*(int *)(*(int *)(_BP+4)+i);
i++;
showp++;
}
}
}
int pow(int index,int power)
{
int finalValue=1;
if(power==0);
else
{
while(power!=0)
{
finalValue=finalValue*index;
power--;
}
}
return finalValue;
}
二、 解決的問題
(1) 使用es+偏移地址時,查看指令,段寄存器會獨自占一條指令。
(2) Main函數是如何給showchar傳遞參數的?showchar是如何接受參數的?
答:main函數將參數入棧,showchar用bp寄存器在棧中提取參數。
(3) showchar函數是如何知道要顯示多少個字符的?printf是如何知道有多少個參數的?
答:showchar函數是通過第一個參數n知道要顯示字符數量的,printf是通過第一個字符串中%c和%d的數量來知道要顯示字符數量的。
三、 未解決的問題
(1) main函數將char型數據和int型數據入棧是占2個字節,那麼如果是float型或者long int型、double型、long double型等超過2字節的變量類型怎麼辦?
(2) Showchar函數將棧中取出的參數賦給al,為什麼2是int型也只賦給一個字節的al?如果是更大的參數怎麼辦?