日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

汇编器源码剖析

發布時間:2025/6/15 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 汇编器源码剖析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?本文我們對一匯編器源代碼進行剖析,了解匯編器實現原理,進而我們根據樣例,自己實現一個匯編器。實現自己版本的匯編器放在另一篇中,本文主要是對別人的源碼進行剖析。

???????? 本文源代碼是來自Kevin Lynx的《基于棧的虛擬機的實現》中關于實現一個堆棧虛擬機中附帶了匯編器的實現,源碼下載地址如下:source code。由于本人對匯編器比較感興趣,所以對其進行如下剖析。

???????? 匯編器主要是一個sasm.c源文件。

???????? 其中,一開始定義了一個const char* op_desc[],op_desc是一個數組,其元素類型是const char*,即op_desc是一個字符串數組,其用于存儲匯編操作符。

/* map to op_type */const char *op_desc[] = {"HALT", "IN", "OUT", "ADD", "SUB", "MUL", "DIV","DUP","LD", "ST", "LDC", "JLT", "JLE", "JGT", "JGE", "JEQ", "JNE", "JMP", 0};

? ? ? ? ?下面我們對各個操作符進行逐一介紹:

?

操作符

操作數個數

說明

HALT

0,HALT

終止

IN

0,IN

從標準輸入中讀入整型值并壓棧

OUT

0,OUT

從棧中彈出,從標準輸出

ADD

0,ADD

從棧中彈出a,彈出b,計算b+a,并將結果壓入棧中

SUB

0,SUB

從棧中彈出a,彈出b,計算b-a,并將結果壓入棧中

MUL

0,MUL

從棧中彈出a,彈出b,計算b*a,并將結果壓入棧中

DIV

0,DIV

從棧中彈出a,彈出b,計算b/a,并將結果壓入棧中

DUP

0,DUP

壓入棧頂值的拷貝

LD

0,LD

從棧中彈出地址,并壓入改地址里的整數值

ST

0,ST

從棧中彈出值,再彈出地址,并將該值存儲到該地址中

LDC

1,LDC value

壓入value

JLT

1,JLT loc

彈出value,檢測value是否小于0,如果小于,則pc=loc

JLE

1,JLE loc

彈出value,檢測value是否小于等于0,如果小于等于,則pc=loc

JGT

1,JGT loc

彈出value,檢測value是否大于0,如果大于,則pc=loc

JGE

1,JGE loc

彈出value,檢測value是否大于等于0,如果大于等于,則pc=loc

JEQ

1,JEQ loc

彈出value,檢測value是否等于0,如果等于,則pc=loc

JNE

1,JNE loc

彈出value,檢測value是否不等于0,如果不等于,則pc=loc

JMP

0,JMP

不用彈出value,pc=loc

?

???????? sasm.c文件中包含了sm.h頭文件,sm.h文件中定義了實際的指令op_type,該指令是枚舉類型,op_type與op_desc的對應關系如下:

op_type

op_desc

opHalt

HALT

opIn

IN

opOut

OUT

opAdd

ADD

opSub

SUB

opMul

MUL

opDiv

DIV

opDup

DUP

opLd

LD

opSt

ST

opLdc

LDC

opJlt

JLT

opJle

JLE

opJgt

JGT

opJge

JGE

opJeq

JEQ

opJne

JNE

opJmp

JMP

opInvalid

無效參數

?? ? ? ? ?指令為一個結構體,其定義如下:

typedef struct Instruction{int op;int arg;} Instruction;

? ? ? ? ?op為操作符,arg為op對應的操作數,op可能是無參數操作碼,所以arg可能無用。

???????? sasm.c中get_op函數用于更具入參const char* s返回對應的實際的op_type,即是從op_desc到op_type的映射。

/* get op from its string desc */int get_op( const char *s ){int i = 0;for( ; op_desc[i] != 0; ++i ){if( strcmp( op_desc[i], s ) == 0 ){return i;}}return opInvalid;}

? ? ? ? ?get_op函數根據const char* s的值,依次檢測與op_desc中的字符串是否匹配,如果匹配則返回對應的索引值,如果不匹配則返回opInvalid,索引值i即是對應于op_type枚舉值。?

/* get the op code arg(operand) count */int get_operand_count( int op ){int ret;switch( op ){case opLdc:case opJlt:case opJle:case opJgt:case opJge:case opJeq:case opJne:case opJmp:ret = 1;break;default:ret = 0;}return ret;}

? ? ? ? ?get_operand_count函數返回op需要的操作數個數。操作符的操作數個數可以參考上表列舉的。這里,op對應的操作數個數只有兩種情況:0和1。

void read_asm(){char line[256];char op_str[32];unsigned short op;int arg_c;int arg;unsigned short loc;while( !feof( fp_in ) ){fgets( line, sizeof( line ) - 1, fp_in );sscanf( line, "%d%s", (int*)&loc, op_str );op = (unsigned short) get_op( op_str );arg_c = get_operand_count( op );if( arg_c > 0 ){char *s = strstr( line, op_str );s = &s[strcspn( s, " \t" )+1];arg = atoi( s );}else{arg = 0;}i_mem[loc].op = op;i_mem[loc].arg = arg;}}

? ? ? ? ?read_asm函數用于讀取匯編代碼。在讀取匯編代碼的過程中,根據當前行的op_str值和get_op函數得到操作符op,并由get_operand_count函數來決定是否讀取對應的操作數,將讀取的指令復制給的指令結構體。

???????? read_asm包含了兩個字符串處理函數:strstr和strcspn:

函數

原型

功能

參考

strstr

char* strstr(char* str1, char* str2);

從str1中查找是否有字符串str2,如果有則返回str1中str2起始位置的指針;如果沒有,則返回0

百度百科

CPLUSPLUS

ZHWEN

strcspn

size_t strcspn(const char* s1, const char* s2);

順序在s1中搜尋與s2中字符的第一個相同字符,包括結束符0,如果存在,則返回該字符在s1中出現的位置;如果不存在,則返回s1的長度

百度百科

CPLUSPLUS

ZHWEN

? ? ? ? ?關于字符串的函數,還有:strpbrk、strspn、strncmp等。

void output(){int loc = 0;int arg_c;for( ; i_mem[loc].op != opHalt; ++ loc ){char op = (char) i_mem[loc].op;fwrite( &op, sizeof( char ), 1, fp_out ); /* op */arg_c = get_operand_count( op );if( arg_c > 0 ){int arg = i_mem[loc].arg;fwrite( &arg, sizeof( arg ), 1, fp_out ); /* arg */} }}

? ? ? ? ?output函數將指令結構體數組中的指令逐個掃描,取操作碼op將其輸出,并根據op由get_operand_count函數得到op對應的操作數個數,如果有操作數,則將對應的操作數輸出。

?

???????? 測試函數:

int main( int argc, char **argv ){if( argc < 2 ){fprintf( stderr, "Usage:%s <filename>\n", argv[0] );exit( -1 );}fp_in = fopen( argv[1], "r" );if( fp_in == 0 ){fprintf( stderr, "Open %s failed\n", argv[1] );exit( -1 );}{char output[256] = { 0 };int l = strcspn( argv[1], "." );strncpy( output, argv[1], l );strcat( output, ".sm" );fp_out = fopen( output, "wb" );if( fp_out == 0 ){fprintf( stderr, "Open %s failed\n", output );exit( -1 );}}read_asm();output();fclose( fp_in );fclose( fp_out );return 0;}

? ? ? ? ?main函數先檢測argc,截取argv[1]的文件名,并補上.sm后綴名,.sm文件是二進制文件,用于虛擬機的執行。輸入輸出文件都沒問題后,調用read_asm和output函數將匯編代碼輸入,在輸入的過程中利用get_op函數將匯編代碼轉換為二進制代碼,然后利用output函數將二進制代碼輸出到文件中。最后,將輸入輸出文件都關閉。

?

?????????總結

???????? 本文是對一匯編器源碼進行了剖析,代碼中的數據結構主要是:

???????? op_desc:字符串數組,定義了匯編代碼的指令字符串

???????? op_type:枚舉類型,標識實際的指令,由于是枚舉類型,其值為0、1、2、……,指令本身的值即為0、1、2、……

???????? Instruction:指令結構體,用來定義一個指令結構體數組。其包含兩個元素:op和arg,op表示操作符,arg表示操作數,由于op最多只有一個操作數,所以arg滿足要求。

???????? 代碼中的一些函數:

???????? get_op:根據字符串形式的匯編代碼得到實際的指令值int型,實際也對應于枚舉類型的op_type。

???????? get_operand_count:根據實際的指令碼op得到其對應的操作數個數。

???????? read_asm:讀取匯編代碼,在讀取匯編代碼的過程中將其轉換為對應的二進制代碼,一個指令碼占1個字節,如果有參數,其參數占4個字節大小。將指令碼和操作數保存到Instruction結構體數組i_mem。

???????? out_put:將i_mem中的指令,如果指令有操作數,則將操作數一并輸出。

???????? main:檢測參數是否合法,如果合法且輸入輸出文件無誤,則將匯編代碼讀入并轉化,并將二進制代碼輸出到文件中。

?

???????? 以上是對該匯編器的源碼剖析,接下來我們按照匯編器的實現原理,編寫一個自己的匯編器,另外對反匯編器的源碼進行學習,并實現一個自己版本的反匯編器。

總結

以上是生活随笔為你收集整理的汇编器源码剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。