在STC论坛上看到有人用C++语言实现8051汇编编译器(源码),好奇下,试着用FB写了一下。
基本原理就是通过分析汇编文件然后转换为机器码。以下是51汇编与机器码对应的表格(数据来自网络,如果发现有误请联系QQ1493446087修正。)。
| 助记符 | 说明 | 字节 | 周期 | 机器码 | 二进制机器码 | 参数1 | 参数2 | 说明 | 
| NOP | 空操作 | 1 | 1 | 00 | 0000 0000 | / | / | |
| ACALL addr 11 | 绝对子程序调用 | 2 | 2 | ***0 0001 | a10a9a80 0001 | a7a6a5a4 a3a2a1a0 | / | |
| LJMP addr 16 | 长转移 | 3 | 2 | 02 | 0000 0010 | a15a14a13a12 a11a10a9a8 | a7a6a5a4 a3a2a1a0 | |
| RR A | A右移一位 | 1 | 1 | 03 | 0000 0011 | / | / | |
| INC A | A加1 | 1 | 1 | 04 | 0000 0100 | / | / | |
| INC data | 直接字节加1 | 2 | 1 | 05 | 0000 0101 | 直接地址 | / | |
| INC @Ri | 间接RAM加1 | 1 | 1 | 06 - 07 | 0000 011i | / | / | i = 0,1 | 
| INC Rn | 寄存器加1 | 1 | 1 | 08 - 0F | 0000 1rrr | / | / | r = 0 - 7 | 
| JBC bit,rel | 若直接位==1则转移且清除 | 3 | 2 | 10 | 0001 0000 | 位地址 | 相对地址 rel | rel = 标签地址 - 当前地址的下一个地址 | 
| AJMP addr 11 | 绝对转移 | 2 | 2 | ***1 0001 | a10a9a81 0001 | a7a6a5a4 a3a2a1a0 | / | |
| LCALL addr 16 | 子程序调用 | 3 | 2 | 12 | 0001 0010 | a15a14a13a12 a11a10a9a8 | a7a6a5a4 a3a2a1a0 | |
| RRC A | A带进位右移一位 | 1 | 1 | 13 | 0001 0011 | / | / | |
| DEC A | A减1 | 1 | 1 | 14 | 0001 0100 | / | / | |
| DEC data | 直接字节减1 | 2 | 1 | 15 | 0001 0101 | 直接地址 | / | |
| DEC @Ri | 间接RAM减1 | 1 | 1 | 16 - 17 | 0001 011i | / | / | i = 0,1 | 
| DEC Rn | 寄存器减1 | 1 | 1 | 18 - 1F | 0001 1rrr | / | / | r = 0 - 7 | 
| JB bit,rel | 若直接位==1则转移 | 3 | 2 | 20 | 0010 0000 | 位地址 | 相对地址 rel | |
| RET | 子程序调用返回 | 1 | 2 | 22 | 0010 0010 | / | / | |
| RL A | A循环左移一位 | 1 | 1 | 23 | 0010 0011 | / | / | |
| ADD A,#data | 立即数加到A | 2 | 1 | 24 | 0010 0100 | 立即数 | / | |
| ADD A,data | 直接字节加到A | 2 | 1 | 25 | 0010 0101 | 直接地址 | / | |
| ADD A,@Ri | 间接RAM加到A | 1 | 1 | 26 - 27 | 0010 011i | / | / | i = 0,1 | 
| ADD A,Rn | 寄存器加到A | 1 | 1 | 28 - 2F | 0010 1rrr | / | / | r = 0 - 7 | 
| JNB bit,rel | 若直接位==0则转移 | 3 | 2 | 30 | 0011 0000 | 位地址 | 相对地址 rel | |
| RETI | 中断程序调用返回 | 1 | 2 | 32 | 0011 0010 | / | / | |
| RLC A | A带进位左移一位 | 1 | 1 | 33 | 0011 0011 | / | / | |
| ADDC A,#data | 立即数带进位加到A | 2 | 1 | 34 | 0011 0100 | 立即数 | / | |
| ADDC A,data | 直接字节带进位加到A | 2 | 1 | 35 | 0011 0101 | 直接地址 | / | |
| ADDC A,@Ri | 间接RAM带进位加到A | 1 | 1 | 36 - 37 | 0011 011i | / | / | i = 0,1 | 
| ADDC A,Rn | 寄存器带进位加到A | 1 | 1 | 38 - 3F | 0011 1rrr | / | / | r = 0 - 7 | 
| JC rel | 若C==1则转移 | 2 | 2 | 40 | 0100 0000 | 相对地址 rel | / | |
| ORL data,A | A或到直接字节 | 2 | 1 | 42 | 0100 0010 | 直接地址 | ||
| ORL data,#data | 立即数或到直接字节 | 3 | 2 | 43 | 0100 0011 | 直接地址 | 立即数 | |
| ORL A,#data | 立即数或到A | 2 | 1 | 44 | 0100 0100 | 立即数 | / | |
| ORL A,data | 直接字节或到A | 2 | 1 | 45 | 0100 0101 | 直接地址 | / | |
| ORL A,@Ri | 间接RAM或到A | 1 | 1 | 46 - 47 | 0100 011i | / | / | i = 0,1 | 
| ORL A,Rn | 寄存器或到A | 1 | 1 | 48 - 4F | 0100 1rrr | / | / | r = 0 - 7 | 
| JNC rel | 若C≠1则转移 | 2 | 2 | 50 | 0101 0000 | 相对地址 rel | / | |
| ANL data,A | A与到直接字节 | 2 | 1 | 52 | 0101 0010 | 直接地址 | / | |
| ANL data,#data | 立即数与到直接字节 | 3 | 2 | 53 | 0101 0011 | 直接地址 | 立即数 | |
| ANL A,#data | 立即数与到A | 2 | 1 | 54 | 0101 0100 | 立即数 | / | |
| ANL A,data | 直接字节与到A | 2 | 1 | 55 | 0101 0101 | 直接地址 | / | |
| ANL A,@Ri | 间接RAM与到A | 1 | 1 | 56 - 57 | 0101 011i | / | / | i = 0,1 | 
| ANL A,Rn | 寄存器与到A | 1 | 1 | 58 - 5F | 0101 1rrr | / | / | r = 0 - 7 | 
| JZ rel | 若A==0则转移 | 2 | 2 | 60 | 0110 0000 | 相对地址 rel | / | |
| XRL data,A | A异或到直接字节 | 2 | 1 | 62 | 0110 0010 | 直接地址 | / | |
| XRL data,#data | 立即数异或到直接字节 | 3 | 2 | 63 | 0110 0011 | 直接地址 | 立即数 | |
| XRL A,#data | 立即数异或到A | 2 | 1 | 64 | 0110 0100 | 立即数 | / | |
| XRL A,data | 直接字节异或到A | 2 | 1 | 65 | 0110 0101 | 直接地址 | / | |
| XRL A,@Ri | 间接RAM异或到A | 1 | 1 | 66 - 67 | 0110 011i | / | / | i = 0,1 | 
| XRL A,Rn | 寄存器异或到A | 1 | 1 | 68 - 6F | 0110 1rrr | / | / | r = 0 - 7 | 
| JNZ rel | 若A≠0则转移 | 2 | 2 | 70 | 0111 0000 | 相对地址 rel | / | |
| ORL C,bit | 直接位或到进位位 | 2 | 2 | 72 | 0111 0010 | 位地址 | / | |
| JMP @A+DPTR | 相对于DPTR间接转移 | 1 | 2 | 73 | 0111 0011 | / | / | |
| MOV A,#data | 立即数送A | 2 | 1 | 74 | 0111 0100 | 立即数 | / | |
| MOV data,#data | 立即数送直接字节 | 3 | 2 | 75 | 0111 0101 | 直接地址 | 立即数 | |
| MOV @Ri,#data | 立即数送间接Rn | 2 | 2 | 76 - 77 | 0111 011i | 立即数 | / | i = 0,1 | 
| MOV Rn,#data | 立即数送寄存器 | 2 | 1 | 78 - 7F | 0111 1rrr | 立即数 | / | r = 0 - 7 | 
| SJMP rel / JMP rel | 短转移 | 2 | 2 | 80 | 1000 0000 | 相对地址 rel | / | |
| ANL C,bit | 直接位与到进位位 | 2 | 2 | 82 | 1000 0010 | 位地址 | / | |
| MOVC A,@A+PC | A+PC寻址程序存贮字节送A | 1 | 2 | 83 | 1000 0011 | / | / | |
| DIV AB | A除B | 1 | 4 | 84 | 1000 0100 | / | / | |
| MOV data,data | 直接字节送直接字节 | 3 | 2 | 85 | 1000 0101 | 直接地址 | / | |
| MOV data,@Ri | 间接Rn送直接字节 | 2 | 2 | 86 - 87 | 1000 011i | 直接地址 | / | i = 0,1 | 
| MOV data,Rn | 寄存器送直接字节 | 2 | 1 | 88 - 8F | 1000 1rrr | 直接地址 | / | r = 0 - 7 | 
| MOV DPTR,#data16 | 16位常数送数据指针 | 3 | 1 | 90 | 1001 0000 | 高立即数 | 底立即数 | |
| MOV bit,C | 进位位送直接位 | 2 | 2 | 92 | 1001 0010 | 位地址 | / | |
| MOVC A,@A+DPTR | A+DPTR寻址程序存贮字节送A | 1 | 2 | 93 | 1001 0011 | / | / | |
| SUBB A,#data | 从A中减去立即数和进位 | 2 | 1 | 94 | 1001 0100 | 立即数 | / | |
| SUBB A,data | 从A中减去直接字节和进位 | 2 | 1 | 95 | 1001 0101 | 直接地址 | / | |
| SUBB A,@Ri | 从A中减去间接RAM和进位 | 1 | 1 | 96 - 97 | 1001 011i | / | / | i = 0,1 | 
| SUBB A,Rn | 从A中减去寄存器和进位 | 1 | 1 | 98 - 9F | 1001 1rrr | / | / | r = 0 - 7 | 
| ORL C,/bit | 直接位的反码或到进位位 | 2 | 2 | A0 | 1010 0000 | 位地址 | / | |
| MOV C,bit | 直接位送进位位 | 2 | 1 | A2 | 1010 0010 | 位地址 | / | |
| INC DPTR | 数据指针加1 | 1 | 2 | A3 | 1010 0011 | / | / | |
| MUL AB | A乘B | 1 | 4 | A4 | 1010 0100 | / | / | |
| MOV @Ri,data | 直接字节送间接Rn | 1 | 1 | A6 - A7 | 1010 011i | / | / | i = 0,1 | 
| MOV Rn,data | 直接数送寄存器 | 2 | 2 | A8 - AF | 1010 1rrr | 直接地址 | / | r = 0 - 7 | 
| ANL C,/bit | 直接位的反码与到进位位 | 2 | 2 | B0 | 1011 0000 | 位地址 | / | |
| CPL bit | 直接位取反 | 2 | 1 | B2 | 1011 0010 | 位地址 | / | |
| CPL C | 进位位取反 | 1 | 1 | B3 | 1011 0011 | / | / | |
| CJNE A,#data,rel | 立即数与A比较,不等转移 | 3 | 2 | B4 | 1011 0100 | 立即数 | 相对地址 rel | |
| CJNE A,data,rel | 直接数与A比较,不等转移 | 3 | 2 | B5 | 1011 0101 | 直接地址 | 相对地址 rel | |
| CJNE @Ri,#data,rel | 立即数与间接RAM比较,不等转移 | 3 | 2 | B6 - B7 | 1011 011i | 立即数 | 相对地址 rel | i = 0,1 | 
| CJNE Rn,#data,rel | 立即数与寄存器比较不等转移 | 3 | 2 | B8 - BF | 1011 1rrr | 立即数 | 相对地址 rel | r = 0 - 7 | 
| PUSH data | 直接字节入栈,SP加1 | 2 | 2 | C0 | 1100 0000 | 直接地址 | / | |
| CLR bit | 直接位清0 | 2 | 1 | C2 | 1100 0010 | 位地址 | / | |
| CLR C | 进位位清0 | 1 | 1 | C3 | 1100 0011 | / | / | |
| SWAP A | A半字节交换 | 1 | 1 | C4 | 1100 0100 | / | / | |
| XCH A,data | 直接字节与A交换 | 2 | 1 | C5 | 1100 0101 | 直接地址 | / | |
| XCH A,@Ri | 间接Rn与A交换 | 1 | 1 | C6 - C7 | 1100 011i | / | / | i = 0,1 | 
| XCH A,Rn | 寄存器与A交换 | 1 | 1 | C8 - CF | 1100 1rrr | / | / | r = 0 - 7 | 
| POP data | 直接字节出栈,SP减1 | 2 | 2 | D0 | 1101 0000 | 直接地址 | / | |
| SETB bit | 直接位置位 1 | 2 | 1 | D2 | 1101 0010 | 位地址 | / | |
| SETB C | 进位位置位 1 | 1 | 1 | D3 | 1101 0011 | / | / | |
| DA A | A十进制调整 | 1 | 1 | D4 | 1101 0100 | / | / | |
| DJNE data,rel | 直接字节减1不为0转移 | 3 | 2 | D5 | 1101 0101 | 直接地址 | 相对地址 rel | |
| XCHD A,@Ri | 间接Rn与A低半字节交换 | 1 | 1 | D6 - D7 | 1101 011i | / | / | i = 0,1 | 
| DJNE Rn,rel | 寄存器减1不为0转移 | 2 | 2 | D8 - DF | 1101 1rrr | 相对地址 rel | / | r = 0 - 7 | 
| MOVX A,@DPTR | 外部数据送A(16位地址) | 1 | 2 | E0 | 1110 0000 | / | / | |
| MOVX A,@Ri | 外部数据送A(8位地址) | 1 | 2 | E2 - E3 | 1110 0010 | / | / | |
| CLR A | A清0 | 1 | 1 | E4 | 1110 0011 | / | / | |
| MOV A,data | 直接字节送A | 2 | 1 | E5 | 1110 0100 | 直接地址 | / | |
| MOV A,@Ri | 间接RAM送A | 1 | 1 | E6 - E7 | 1110 011i | / | / | i = 0,1 | 
| MOV A,Rn | 寄存器送A | 1 | 1 | E8 - EF | 1110 1rrr | / | / | r = 0 - 7 | 
| MOVX @DPTR,A | A送外部数据(16位地址) | 1 | 2 | F0 | 1111 0000 | / | / | |
| MOVX @Ri,A | A送外部数据(8位地址) | 1 | 2 | F2 - F3 | 1111 001i | / | / | |
| CPL A | A求反码 | 1 | 1 | F4 | 1111 0100 | / | / | |
| MOV data,A | A送直接字节 | 2 | 1 | F5 | 1111 0101 | 直接地址 | / | |
| MOV @Ri,A | A送间接Rn | 1 | 2 | F6 - F7 | 1111 0101 | / | / | i = 0,1 | 
| MOV Rn,A | A送寄存器 | 1 | 1 | F8 - FF | 1111 1rrr | / | / | r = 0 - 7 | 
所以就可以开始分析汇编文件,然后根据以上表格转换对应的机器码。
1、使用main函数来接收控制台的命令参数
FB里使用Main函数需要自己定义入口点,并使用__FB_ARGC__和__FB_ARGV__来获取参数。ParseAsmFile来解析ASM文件到机器码。CompileToHex将机器码转换为Hex文件。(具体实现看源码)
Private Function main(ByVal argc As Integer,ByVal argv As ZString Ptr Ptr) As IntegerPrint argc,*argv[1],*argv[2]'argc 是参数个数'argv 是命令行的参数指针'*argv[0] 表示第1个参数,是exe本身的名字'*argv[1] 表示第2个参数,这里固定为ASM文件名'*argv[2] 表示第3个参数,这里固定为Hex文件名If argc > 1 Then '有参数'1、解析命令(未完成)'根据命令添加8051默认寄存器'2、解析汇编文件ParseAsmFile(*argv[1])'3、输出Hex文件CompileToHex(*argv[2])End IfReturn 0
End FunctionEnd main(__FB_ARGC__, __FB_ARGV__)2、另外建立一个调用编译器的窗体工程
窗体如下:

测试打印信息:

源码工程