数码之家

 找回密码
 立即注册
搜索
查看: 864|回复: 10

[C51] 2k行代码实现的51上的BASIC

[复制链接]
发表于 2024-8-16 11:21:31 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

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

x
https://github.com/PJSDDL/PJ_BASIC
使用<2000行C代码,开发了一个简洁的BASIC解释器。该解释器具有如下优点:只使用C标准库、能产生报错信息、适合移植嵌入式设备、不用写行号。在STM32上该解释器占用rom<20K(包含CUBEMX标准库占用空间),使用ram<20K,绝大多数STM32芯片都可以轻松运行。下面详细介绍该语言。

(该代码存在不同版本,请以示例代码及注释为准!)

一、语法
PJ_BASIC语句有两种:赋值语句和关键字语句。
1.1,赋值语句
格式如下:
变量  =  表达式
例如:
i = i + 1
需要注意的是,每个符号之间都要由空格分开,表达式不能以运算符开头:
i = -1-1 (错误,没有空格)
i = -  1  -  1  (错误,表达式以运算符开头)
i = 0  -  1  -  1  (正确)
变量被赋值前,要使用var语句声明,否则会报错。
PJ_BASIC支持以下几种运算符:+  -  *  /  >  <  ==,其中比较运算符的运算优先级最低,值为0或1,例如如下表达式
2 > 1
1 > 2
前者等于1,后者等于0

1.2,关键字语句
关键字语句由关键字开头,有以下几种:
1.2.1,pri
该关键字作用是打印变量、常量或字符串(不能打印表达式的值)。被打印变量需要用空格隔开:
例子:pri  i  0  “1200”
1.2.2,var   
定义变量,变量默认为32位int型
例子:var  i
1.2.3,if   endif   
条件分支语句,使用方法如下:
if  表达式
语句
endif
例如,以下语句判断i的值,i>2则打印i的值
if  i  >  2
pri  i
endif
1.2.4,while endwh  循环语句
使用方法如下:
while  表达式
语句
endwh  
当表达式的值为1,则会进行循环,否则跳出。例如,您可以使用如下指令进入死循环
while  1
endwh
1.2.5,func call ret语句
您可以组合使用这三条语句定义子函数。call指令执行时,会自动将当前指令位置压栈,然后跳转到func位置,执行语句后遇到ret跳转回cal语句后的一条语句。子函数名必须为数字,函数内不能定义局部变量,您可以使用全局变量在主函数和子函数之间传递参数。
例如,您可以使用如下指令定义一个自加函数
var  i
call  1
pri   i
func  1
i  =  i  +  1
ret
程序执行时如果遇到func,则会自动跳转到ret后的语句,例如
var  i
i = 2
func  1
i = 1
ret
pri  i
执行结果是2,因为子函数只能使用call调用,不能顺序执行。因此,您可以使用func ret语句将代码注释掉。
1.2.6,read  write语句
PJ_BASIC中定义了一个大数组mem,您可以利用read  write读取这个数组。read命令格式如下:
read  常数1(或变量1)  常数2(或变量2)
意为将常数2(或变量2)视为数组索引,读取数组mem,结果保存在常数1(或变量1)中
write 命令格式如下:
write  常数1(或变量1)  常数2(或变量2)
意为将常数2(或变量2)的值,保存在常数1(或变量1)对应的地址中
下面的示例代码展示了两种指令的用法:
var i
i  =  100
var  addr
addr  =  4
write  3  3
read  i  3
pri i  
write  addr  321
read  i  addr
pri  i  

1.3,关于代码书写
若代码以字符串的形式保存在char basic_prog[]中,字符串中的换行应使用\n表示,双引号"使用转义字符\"表示,字符串换行时用\连接两行代码。一个示例字符串数组basic_prog形式如下:
char basic_prog[] = "\
var flag \n\
flag = 11 \n\
while flag > 1 \n\
    pri flag \n\
endwh \n\
";
每行代码后都要留一个空格,然后以\n\结尾。
若您使用STM32串口输入代码,代码结尾不应有任何符号,代码最后应加一个\0,以提示代码结尾。
var flag
flag = 11
while flag > 1
    pri flag
endwh \0

二、解释器宏定义介绍
在BASIC.h中,有一些宏定义,您可以修改这些定义以节约硬件资源
LIST_LEN:预编译生成代码列表的最大长度
MAX_EXPR_LEN:表达式最大长度
MAX_VAR_NUM:最多可以定义的变量数量
MAX_VAR_LEN:变量最大长度(字符串长度)
MAX_FUN_STACK:函数嵌套最大数量
MEM_SIZE:mem数组尺寸

三、解释器运行原理简介
代码首先经过分词器parser分词,形成预编译代码,然后利用索引计算器index_match计算关键字跳转位置,例如计算if对应的endif的位置,最后使用运行器basic_run执行代码,代码中的表达式由表达式计算器expr计算。
该代码没有优化,如果您对代码进行修改,可以节约更多的空间,代价是牺牲一定的速度。

四、性能测试
运行以下质数运算代码:
pri \"prim\"\n\
var b \n\
var div \n\
var flag \n\
b = 2 \n\
div = 2 \n\
flag = 0 \n\
while b < 10000 \n\
    flag = 1 \n\
    div = 2 \n\
    while div < b / 2 + 1 \n\
        if ( b / div ) * div == b \n\
        flag = 0 \n\
        endif \n\
        div = div + 1\n\
    endwh \n\
    if flag \n\
    pri b \n\
    endif \n\
    b = b + 1 \n\
endwh \n\
pri \"prim_end\"\n\
笔记本电脑上,C语言耗时0.3秒,PJ_BASIC耗时15.6秒,耗时相差40倍左右,STM32上,C语言耗时6.5秒,PJ_BASIC耗时2397秒,耗时相差400倍左右

打赏

参与人数 1家元 +15 收起 理由
不长叶子的树 + 15

查看全部打赏

 楼主| 发表于 2024-8-16 11:23:34 | 显示全部楼层
该程序几乎能在所有常见的STM32F1及以上型号运行,在STC上运行时,最好使用ST C8A8K64D,否则需要外扩rom或ram
回复 支持 反对

使用道具 举报

发表于 2024-8-16 12:06:38 | 显示全部楼层
看样子像是楼主自己开发的?
回复 支持 反对

使用道具 举报

发表于 2024-8-16 12:49:53 | 显示全部楼层
感谢楼主的分享。学习了
回复 支持 反对

使用道具 举报

发表于 2024-8-16 13:47:05 | 显示全部楼层
这个厉害了
回复 支持 反对

使用道具 举报

发表于 2024-8-16 14:31:12 | 显示全部楼层
8080  Z80 cpu 时代的产物    嫁接到单片    牛  
回复 支持 反对

使用道具 举报

发表于 2024-8-17 10:50:26 | 显示全部楼层
本帖最后由 网络孤客 于 2024-8-17 11:02 编辑

这个厉害了,可以在STM32用,就是慢了些。
回复 支持 反对

使用道具 举报

发表于 2024-8-17 13:04:27 | 显示全部楼层
太厉害了,单片机也可以用上BASIC?
回复 支持 反对

使用道具 举报

发表于 2024-8-18 07:33:13 | 显示全部楼层
八十年代末期上学时学过BASIC语言,但对其一直不理解,九十年代末期自学51单片机的汇编语言,后来自学C51,通过汇编语言才逐渐理解C51。如果现在再看BASIC语言,应该能很好的理解了。
回复 支持 反对

使用道具 举报

发表于 2024-8-21 19:18:48 来自手机浏览器 | 显示全部楼层
m这个好厉害
回复 支持 反对

使用道具 举报

发表于 2024-8-22 08:15:36 来自手机浏览器 | 显示全部楼层
这个厉害了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-2 11:37 , Processed in 0.249600 second(s), 12 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

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