搜索
bottom↓
回复: 55

发布一个简单的C语言编译器(含虚拟机)

  [复制链接]

出0入0汤圆

发表于 2013-6-1 11:12:33 | 显示全部楼层 |阅读模式
本帖最后由 lcw_swust 于 2013-6-1 11:13 编辑

本程序由一个解释器修改而来,由于需要较大内存,目前只是在KEIL下软件仿真运行
上程序:

/*--------------------------------------------------
TC编译器说明

目标:
                单片机中运行编译器,能编译简单的C语言程序,生成汇编
        代码,再将汇编代码转为虚拟机的机器码,再让虚拟机执行机器码。
        关于简单的C语言程序:
                实现if,while,一维数组,API函数调用。       
                数据类型 (signed/unsigned)char\int\long (*)
--------------------------------------------------
虚拟机结构:
        寄存器(全32位):r0~rf(共16个)
        内存: 自定义
寄存器使用规则:
        函数入口用r0、r1、r2、r3传递
        函数返回用r4,计算结果也用r4
        计算使用寄存器r4、r5、r6、r7
        Rc用于进入函数时保存Re,便于计算局部变量的偏移量
        Rd用于指针查表(相当于x指针,用于ld、st指令)
        Re用于申请变量空间(相当于y指针)
        Rf用于堆栈(相当于sp,用于push、pop、call、ret指令)
        其余寄存器备用

虚拟机指令集:
        算术指令:
                (全是寄存器作为操作数)
                neg(~),not(!),add(+),sub(-),mul(*),div(/),mod(%)
                and(&),or(|),xor(^),grt(>),les(<),sl(<<),sr(>>)
                land(&&),lor(||),nless(>=),ngrt(<=),neq(!=),equ(==)
       
        跳转指令:
                ret,jmp,call,
                je        (r4=0则跳转)
                jne        (r4!=0则跳转)
        数据传送:
                mov         r,r        (寄存器复制)
                ldi                r,i        (加载立即数)
                //ld系列
                ldu8        r         (r=*(U8*)rd,从RAM或ROM加载无符号8位数)
                lds8        r         (r=*(S8*)rd,从RAM或ROM加载有符号8位数)
                ldu16        r         (r=*(U16*)rd,从RAM或ROM加载无符号16位数)
                lds16        r         (r=*(S16*)rd,从RAM或ROM加载有符号16位数)
                ldu32        r         (r=*(U32*)rd,从RAM或ROM加载无符号32位数)
                lds32        r         (r=*(S32*)rd,从RAM或ROM加载有符号32位数)
                //st系列
                stu8        r         (*(U8*)rd=r,向内存写入无符号8位数)
                sts8        r         (*(S8*)rd=r,向内存写入有符号8位数)
                stu16        r         (*(U16*)rd=r,向内存写入无符号16位数)
                sts16        r         (*(S16*)rd=r,向内存写入有符号16位数)
                stu32        r         (*(U32*)rd=r,向内存写入无符号32位数)
                sts32        r         (*(S32*)rd=r,向内存写入有符号32位数)
                push        r        (sp-=4,r=*sp,sp即rf)
                pop                r        (r=*sp,sp+=4,)
--------------------------------------------------
C程序转为汇编代码的过程:
(参考TCCMP.C文件)
1.编译中使用的缓冲区
  缓冲区先是被分为独立的3段,普通的代码放入第1段,定义数组时,
  数组首地址(标号)赋给第1段中的变量,数组元素的值放入第2段,
  如果数组元素是双引号字符串,则将双引号字符串首地址(标号)
  放入第2段,字符串放入第3段。
  这些参考AddAsmCode1、AddAsmCode2、AddAsmCode3
  当然,在写入时有可能出现第1段装不下,写到第2段去的情况,
  这时就需要,加大第1段,将第2、3段往右移动(参考moveasm2、moveasm3)。
  当然,如果编译器运行环境的缓冲区足够,则可以不用这么麻烦,
  直接定义三个缓冲区就是了。
  最后,将3段连接起来变一个字符串,用于Link

2.变量定义
  (参考addvar函数)
  假如一个变量占两字节,则只需让re增加2:

  ldi r4,2
  add re,r4        //re=re+2

3.读取变量
  (参考getvar_id函数)
  mov rd,rc        //rc=rd=进入函数时保存的局部变量的RAM区首地址
  ldi r4,2        //r4=该局部变量在局部变量缓冲区中的偏移地址
  add rd,r4 //rd=该变量的绝对地址
  ldu8 r4        //r4=*(U8*)rd,读到该变量的值,根据变量的类型,ldu8可能为lds8\ldu16\lds16等

4.写变量
  (参考setvar函数)
  mov rd,rc
  ldi r5,2
  add rd,r5 //rd=该变量的绝对地址
  stu8 r4   //*(U8*)rd=r4

5.数组
  (参考vardef1内state==10的处理)
  定义数组赋初值时,则生成一个标号,标号的值赋给该数组变量,然后将数组元素放入另一code段
  char s[]="123";
  可能被编译成
          code第1段----------
          ......
          ldi r4,lab001  //取数组地址
          stu32 r4                //变量赋值
          code第2段----------
          lab001:
          arrs8 049,050,051,
  long s[]={"123","456"};

  可能被编译成
          code第1段----------
          ......
          ldi r4,lab001  //取数组地址
          sts32 r4                //变量赋值
          code第2段----------
          lab001:
          arru32 lab002,lab003,
          code第3段----------
          lab002:
          arrs8 049,050,051,
          lab003:
          arrs8 052,053,054,

6.算式
  (参考getexp及getexp1)
        getexp是将整个算式的值求出,x结果放入r4,
                例如 a+b*c
        getexp1只是求出算式中的一个参数,并放入r4,就像上面的a\b\c

        举个例子,要计算 1+2
        调用getexp(),
        getexp()内先调用getexp1,
        得到代码:
                ldi r4,1    //r4=1
        getexp()再调用GetToken,取得+号
        得到代码:
                push r4                //保存r4

                调用getexp1得到:
                ldi r4,2   //r4=加号右方的2

                mov r5,r4  //r5=r4=2
                pop r4           //r4=加号左方的1

                调用cacu得到:
                add r4,r5  //r4=r4+r5=1+2

简单总结:
    本编译器将C转成汇编代码基本上是按照固定规律来的,
        所以可能会有些冗余,目前未作优化
......
--------------------------------------------------
汇编转为机器码的过程:
(参考Link.C文件)

调用 Link_GetToken,
如果得到的不是以冒号结尾的标号,则当成是指令,
然后查表将Token字符串转为指令码,再根据指令码的
类型获取指令之后的参数,如果参数中有标号,则查找
标号(Link_getcst)。
--------------------------------------------------
机器码的执行过程:
(参考VM.C文件)
这个相对简单,读取一字节的机器码之后,根据指令类
型进行处理,对寄存器、RAM等进行读写,以及调用某些
API函数。
--------------------------------------------------
一些注意事项及BUG:
  对于运算式的处理,是按从左到右来的,没有优先级之分
  有括号的地方会作为单独的运算式处理
  例如
          1+2*3        等价于(1+2)*3
          1+(2*3)        等价于1+(2*3)

  不要在循环内定义参数
  函数传递参数的个数以及返回参数没有严格检查
  定义变量赋初值时不能取变量值,可以取变量地址
  比如int a=&b;是可以的,int a=b;就不行


//--------------------------------------------------*/


程序运行截图








本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出10入23汤圆

发表于 2013-6-1 11:35:50 | 显示全部楼层
标记一下,貌似很强大

出0入4汤圆

发表于 2013-6-1 11:42:15 | 显示全部楼层
哪位能科普一下,虚拟机是如何做出来的?原理是什么?

出0入0汤圆

发表于 2013-6-1 11:58:35 | 显示全部楼层
先mark,貌似很不错

出0入0汤圆

发表于 2013-6-1 12:05:07 | 显示全部楼层
真不是一般的强大

出0入34汤圆

发表于 2013-6-1 13:08:38 | 显示全部楼层
不错,测试后感觉十分强大! ...

出0入0汤圆

发表于 2013-6-1 13:24:39 来自手机 | 显示全部楼层
表示相当的喜欢这东西,看看先!

出0入0汤圆

发表于 2013-6-1 13:43:20 | 显示全部楼层
好强大啊

出0入0汤圆

发表于 2013-6-1 14:09:17 | 显示全部楼层
虽然不懂你们在说什么,但听起来很强大的样子

出0入0汤圆

发表于 2013-6-1 14:25:08 | 显示全部楼层
强大,喜欢,要下

出0入0汤圆

发表于 2013-6-1 15:33:20 | 显示全部楼层
收藏,谢谢。

出0入0汤圆

发表于 2013-6-1 15:49:21 | 显示全部楼层
强大呀,这东西

出0入0汤圆

发表于 2013-6-1 16:58:43 | 显示全部楼层
mark!!!!!!!!!!!!!!!!!!!!1111

出0入0汤圆

发表于 2013-6-1 17:17:56 | 显示全部楼层
强人啊。论坛上高手太多了
头像被屏蔽

出0入0汤圆

发表于 2013-6-2 09:08:13 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2013-6-2 09:43:20 | 显示全部楼层
mark!!!!!!!!!!!!

出0入50汤圆

发表于 2013-6-2 09:47:06 | 显示全部楼层
很厉害了,只是看到那18KB多的XRAM需求,心里一下哇凉瓦良的了,这样的51不好找啊,外扩SRAM就更麻烦了。

出0入0汤圆

 楼主| 发表于 2013-6-2 11:09:05 | 显示全部楼层
ilikemcu 发表于 2013-6-2 09:47
很厉害了,只是看到那18KB多的XRAM需求,心里一下哇凉瓦良的了,这样的51不好找啊,外扩SRAM就更麻烦了。 ...

的确,编译器需要较大的RAM,可以将编译器程序移植到VC或VB下,只让单片机运行虚拟机。

要在小型单片机上运行编译器,我暂时想到两个办法:
(1)如果程序空间足够装下编译器代码,但内存不够,可以利用SD卡,将asmcode[]、mcode[]等存入SD卡(或其它存储设备)。
(2)如果程序空间只能装下虚拟机的代码,则可以将这个编译器的C代码用“简单C” 的语法重新写一下,(在电脑上)编译成机器码存入SD卡中,让单片机运行虚拟机,运行SD卡中的代码,就是说在虚拟机中运行编译器;
综上所述,只要单片机能运行虚拟机,配合外部存储设备(如SD卡),就能运行大型的程序(比如运行这个编译器)。(当然,速度上会受限制)

出0入0汤圆

发表于 2013-6-2 11:19:35 | 显示全部楼层
这个是好东西

出0入0汤圆

发表于 2013-6-2 11:32:30 | 显示全部楼层
还没看懂怎样使用,先标记一下,然后慢慢来学习.

出0入0汤圆

发表于 2013-6-2 12:38:42 来自手机 | 显示全部楼层
Mark…
来自:amoBBS 阿莫电子论坛 Windows Phone 7 客户端

出0入0汤圆

发表于 2013-6-2 12:52:52 | 显示全部楼层
关注大师的新作品

出0入0汤圆

发表于 2013-7-29 09:36:39 | 显示全部楼层
绝对要顶啊!!!!!!!!!!!!!!!

出0入0汤圆

发表于 2013-7-29 09:50:55 | 显示全部楼层
在安桌手机上跑一个WIN XP。

出0入0汤圆

发表于 2013-7-29 14:59:39 | 显示全部楼层
我这段时间也在搞这个东东   递归啊 四元式啊  整的晕乎乎的 呵呵

出0入0汤圆

发表于 2013-7-29 16:07:56 | 显示全部楼层
好东西哦

出0入0汤圆

发表于 2013-7-30 12:27:18 | 显示全部楼层
虽然没看太懂,感觉很厉害的样子,先记下了

出0入0汤圆

发表于 2014-2-9 12:09:05 | 显示全部楼层
mark,强大   ,

出0入0汤圆

发表于 2014-2-9 12:28:03 | 显示全部楼层
不明觉厉啊!

出0入0汤圆

发表于 2014-2-9 13:46:22 | 显示全部楼层
主要在哪些方面用的?

出0入0汤圆

发表于 2014-2-9 13:52:18 | 显示全部楼层
mark。。。

出0入0汤圆

发表于 2014-2-9 14:40:00 | 显示全部楼层
技术上挺好玩的,但是真没想到有什么实用价值

出0入0汤圆

发表于 2014-2-10 15:09:21 | 显示全部楼层
感谢楼主分享

出0入0汤圆

发表于 2014-2-10 19:23:56 | 显示全部楼层
感觉魂魄瞬间被你勾走了……好强大……

出0入0汤圆

发表于 2014-2-12 00:47:47 | 显示全部楼层
我勒个擦,这个真的很强大啊!!!!佩服至极!!!一直想学习这方面的知识,苦于木有资料,嗯嗯,已经下载并收藏,待到山花烂漫时。。。拿出来欣赏欣赏,研究研究。。。。。。。

出0入0汤圆

发表于 2014-2-12 10:30:46 | 显示全部楼层
不明觉厉!

出0入0汤圆

发表于 2014-2-12 14:12:31 | 显示全部楼层
标记一下,貌似很强大

出0入0汤圆

发表于 2014-11-19 15:28:52 | 显示全部楼层
非常佩服楼主。

出0入0汤圆

发表于 2014-11-19 15:51:46 | 显示全部楼层
没看懂。。不明觉厉

出0入0汤圆

发表于 2017-4-25 22:00:55 | 显示全部楼层
很好很强大!!谢谢楼主分享研究成果!!

出0入0汤圆

发表于 2017-5-17 18:56:33 来自手机 | 显示全部楼层
真的很配服楼主的水平。

出0入0汤圆

发表于 2017-5-18 06:15:29 来自手机 | 显示全部楼层
楼主大牛,不要虚拟机,直接对51机怎么实现一个?

出0入0汤圆

 楼主| 发表于 2017-5-18 08:48:08 | 显示全部楼层
本帖最后由 lcw_swust 于 2017-5-18 08:58 编辑
guoj 发表于 2017-5-18 06:15
楼主大牛,不要虚拟机,直接对51机怎么实现一个?


这个编译器是运行于单片机上的,但对RAM要求较多,估计只有STM32才能胜任,
虚拟机对RAM和ROM的要求很低,普通51单片机也能运行,所以,可以在STM32上运行编译器,编译后的机器码传给51单片机,让51去执行。

如果只想让单片机运行C语言程序,对执行速度要求不高,可以看看我的另一个帖子里的解释器,可以在一些RAM相对较大的51单片机上运行:
https://www.amobbs.com/thread-5492296-1-1.html

这里有个关于编译器与解释器的对比,很形象:
http://blog.csdn.net/touzani/article/details/1625760

我好像误会你的意思了,你是说直接编译成51单片机的机器码?
估计这工作量有点大,我没考虑过,只是想做个像JAVA那样能跨平台运行的东东。
曾经看到有坛友做了51、AVR的编译器,暂时没找到帖子。
----------
找到了,就是这位大神:
https://www.amobbs.com/forum.php?mod=viewthread&tid=5482772

出0入0汤圆

发表于 2017-5-18 09:23:00 | 显示全部楼层
lcw_swust 发表于 2017-5-18 08:48
这个编译器是运行于单片机上的,但对RAM要求较多,估计只有STM32才能胜任,
虚拟机对RAM和ROM的要求很低 ...

真的强大,这样让我想起,我接触过一个产品,上面运行ARM A系列 跑RT-linux,linux运行虚拟机,不知道什么虚拟机,然后用vs平台编译vb,编译过之后放在linux的虚拟机上运行,这个非常类型你的设计技术,可以多多交流。

出0入0汤圆

 楼主| 发表于 2017-5-18 09:38:04 | 显示全部楼层
ffbiao 发表于 2017-5-18 09:23
真的强大,这样让我想起,我接触过一个产品,上面运行ARM A系列 跑RT-linux,linux运行虚拟机,不知道什 ...

linux真是神奇。
linux上运行用VB写的程序?真牛,不知是不是这个叫Mono的东东。
http://blog.csdn.net/qinyuanpei/article/details/51304362

出0入0汤圆

发表于 2017-5-18 11:03:25 | 显示全部楼层
lcw_swust 发表于 2017-5-18 09:38
linux真是神奇。
linux上运行用VB写的程序?真牛,不知是不是这个叫Mono的东东。
http://blog.csdn.net/q ...

这个mono可以在ARM ubuntu linux上运行,但是我说不是这个,那个vb语言准确说是basic语言

出0入0汤圆

发表于 2017-5-18 18:44:09 | 显示全部楼层
本帖最后由 guoj 于 2017-5-18 18:49 编辑
lcw_swust 发表于 2017-5-18 08:48
这个编译器是运行于单片机上的,但对RAM要求较多,估计只有STM32才能胜任,
虚拟机对RAM和ROM的要求很低 ...


[是的,意思是写个相当于C51编译器]
剩下的我就想知道,如此高深功力,到底多少年能够炼成啊!

就服你了!

出0入0汤圆

发表于 2018-10-3 21:54:50 | 显示全部楼层
牛逼得不行 MARK 学习

出0入0汤圆

发表于 2018-12-3 23:57:11 | 显示全部楼层
我读了一遍代码,楼主牛逼的不行。
这是我第一次 读到编译器的代码,立马感觉好多问题都得到了解释。谢谢楼主。

出0入0汤圆

发表于 2018-12-4 19:07:12 | 显示全部楼层
好强大啊

出0入0汤圆

发表于 2018-12-6 16:35:58 | 显示全部楼层
没看懂,感觉牛逼!

出0入0汤圆

 楼主| 发表于 2018-12-6 18:57:01 | 显示全部楼层
jiang887786 发表于 2018-12-6 16:35
没看懂,感觉牛逼!

就是在单片机上运行一个编译器,编译C语言。

出0入0汤圆

发表于 2018-12-6 19:16:12 | 显示全部楼层
mark标记一下,虚拟机C语言,多谢

出0入0汤圆

发表于 2018-12-6 21:57:48 | 显示全部楼层

标记一下,

出0入0汤圆

发表于 2018-12-7 09:17:47 | 显示全部楼层
lcw_swust 发表于 2018-12-6 18:57
就是在单片机上运行一个编译器,编译C语言。

这样会对单片机资源耗费很多吧,具体有哪些实用价值呢?类似于某些PLC的解释语言吗?你熟悉3S公司的CODESYS软件吗?它似乎是运行的一个什么操作系统,貌似也是解释类型的吧?

出0入0汤圆

 楼主| 发表于 2018-12-7 11:40:50 | 显示全部楼层
jiang887786 发表于 2018-12-7 09:17
这样会对单片机资源耗费很多吧,具体有哪些实用价值呢?类似于某些PLC的解释语言吗?你熟悉3S公司的CODESYS ...

也许可以用于某些玩具机器人之类吧,用于练习编程。
也可以像JAVA那样做一些跨平台的东西。
CODESYS不熟。

解释器我也写过,坛里也有开源PLC,不知是不是一样。
https://www.amobbs.com/thread-5492296-1-1.html
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-24 09:25

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表