CASM的全称是Catium Self Made language.
并且部分指令类似汇编语言, 因此有ASM成分(大雾).
参考build.bat编译, 编译出来的可执行程序casm.exe(windows下).
CASM目前没有使用任何平台相关代码, 因此她是跨平台的.
运行可执行文件时, 您可以在标准输入流中输入您的CASM代码, 或者您也可以在命令提示符中用管道符将文件重定向至标准输入流.
标准输入流结束后, CASM将尝试解释您的代码.
如果解释过程中发现语法错误, 将会报错.
CASM代码文件后缀应为.casm
一般的操作语法为 操作符 [操作字节数] 操作数 [操作数 ...]
注释要求单独成行或位于行尾, 主要是不能出现在其他语句的操作数位置, 以#开始
我们并未采用函数, 而是引入了"代码块"的概念.
作为循环的替代品, 代码块默认是循环执行的.
如果代码块内没有显式跳出/返回指令(如ret,rez等), 则会循环执行代码块主体内容.
see at block.cpp
代码块由block关键字声明, 以endblock关键字结束, 不得相交或嵌套.
举个例子:
block loop
    sub     4       cnt     1
    rez     cnt
endblock
block main
    var     4       cnt     0xff
    call    loop
    ret
endblock
上述代码会在main块中声明一个4字节变量cnt, 然后调用loop块, 每次循环令cnt减一, 直到cnt为零时返回main块, 最后退出main块.
整个程序由名为main的代码块开始执行, 如果不存在则不会有代码被执行
在任何情况下, 如果有某个代码块的名字被声明了多于1次, 则后面的同名代码块会覆盖前面的同名代码块.
CASM允许块的内容在运行时改变, 因此她是动态语言.
CASM采用堆栈模式管理内存:
- 变量的内存由堆维护, 堆自底向上生长vmem_heap.cpp
 - 栈空间由栈维护, 栈自顶向下生长vmem_stack.cpp
 
总的可用于解释代码的内存在编译解释器时定义(默认为128MB). 参考build.bat
- 接受1个操作数
 - 不需要指定数据长度
push a将a入栈, 即在栈顶按字节复制a a必须为变量
注意, 不需要显式的指定数据长度, 是因为数据长度根据变量名判断.
- 接受1个操作数
 - 不需要指定数据长度
pop a出栈到a, 即从栈顶按字节复制数据到a a必须为变量- 栈大小小于变量大小时忽略本次操作(
a中值不变)
注意, 不需要显式的指定数据长度, 是因为数据长度根据变量名判断. 
- 格式为10进制整数或16进制整数
 - 十进制整数可以为负数
 - 16进制整数格式类似
0x0123456789abcdef, 且不允许负数 - 超出目标操作数容量的, 高位将被舍去(在小端字节序下).
 
see at var.cpp
CASM不强制区分变量内容的类型, 因此她是一种弱类型语言.
- 变量的生存周期是从它被声明到他被销毁.
 - 在生存周期内, 变量的作用域是全局.
 - 只能使用
a~z,A~Z,0~9,_作为标识符(经测试 unicode字符或许也能用) - 区分大小写
 - 不允许
0~9作为标识符首字母 
- 接受2个操作数
 - 需要指定数据长度
 
var a b c 创建一个a字节的变量, 变量名为b, 初始值为c
a的大小不限制, 当堆空间不足以创建时, 产生错误.b的要求见变量名格式c只能为立即数,或其他变量.- 当
c为变量时, 初始化操作将按字节复制c的内容到b, 多退少补 - 当
c为立即数时, 如果a小于等于4则按值复制, 否则按字节填充c的最低字节. - 特别地, 0字节变量允许被声明, 但可能无法被正常使用
 - 长度为负的变量可能会导致未定义行为
 
小于等于4字节的变量在其他运算中会被优先视为整数.
3字节整数变量不区分正负.
注意, 在循环块中创建变量而不删除变量是不明智的, 因为循环执行至第二次的时候就会产生重名变量的错误.
- 接受1个操作数
 - 不需要指定数据长度
del a销毁一个名为a的变量.
销毁后内存会被释放进堆中(详见vmem_heap.cpp).
目前回收机制比较简陋, 对长度是二的幂的内存进行整块回收, 否则拆分后回收. 
格式为[addr]
其中addr可以是变量名,立即数,但不可以是另一个内存地址
表示内存中addr位置的内容, 元素字节数另外指定.
如果addr是一个长度大于4的变量, 将产生错误.
CASM是不进行内存保护的, 您可以用[]操作符访问任意地址的数据(限制在解释器提供的虚拟内存范围里).
指令分为块内指令和块外指令.
用于跳转到某个代码块, 在目标代码块显式返回时回到call所在代码块继续执行后续代码.
用于无条件跳出代码块.
- 接受一个操作数
 - 操作数必须为变量
用于有条件跳出代码块, 操作数值为0时跳出代码块, 否则继续执行块内其后的语句. 
举个例子:
block loop
    sub     4       cnt     1       # 1
    rez     cnt                     # 2
endblock
block main
    var     4       cnt     0xff
    call    loop
    ret                             # 3
endblock
上述代码会循环执行loop块的内容(#1 & #2), 直到cnt值为0时跳出, 继续执行#3.
用于无条件结束整个程序(强制令解释器退出).
mov a b c 从c复制a字节的数据到b位置
- 接受2个操作数
 - 需要指定数据长度
 a必须是一个正立即数.b,c可以是内存地址, 可以是变量名c可以为立即数b不得为立即数
注: 3字节运算视为4字节运算并自然溢出为3字节
add a b c 进行一次a字节加法, 把c加在b上
- 接受2个操作数
 - 需要指定数据长度(不超过4)
 b必须为变量c可以是立即数,变量
sub a b c 进行一次a字节减法, 把b减去c
- 接受2个操作数
 - 需要指定数据长度(不超过4)
 b必须为变量c可以是立即数,变量
mul a b c 进行一次a字节乘法, 把c乘在b上
- 接受2个操作数
 - 需要指定数据长度(不超过4)
 b必须为变量c可以是立即数,变量
div a b c 进行一次a字节除法, 把c除在b上.
- 接受2个操作数
 - 需要指定数据长度(不超过4)
 b必须为变量c可以是立即数,变量- 下取整.
 
取地址运算.
addr a b 获取b的内存地址并写到a
- 接受2个操作数
 - 不需要指定数据长度
 - 要求
a,b均为变量 a最好为4字节变量, 少于字节的将会截取低位, 多于4字节的只填充低4字节.
取长度运算.
size a b 获取b的内存大小并写到a
- 接受2个操作数
 - 不需要指定数据长度
 - 要求
a,b均为变量. a最好为4字节变量, 少于字节的将会截取低位, 多于4字节的只填充低4字节.
pchar a 把a当做一个ASCII字符输出到标准输出流(stdout)
- 接受1个操作数
 - 不需要指定数据长度
 a可以为变量, 内存地址, 立即数- 如果
a是变量, 则取其最低字节 - 如果
a是内存地址, 则取该位置1字节 - 如果
a是立即数, 则取其最低字节 
pint a 把a当做一个有符号整数输出到标准输出流(stdout)
- 接受1个操作数
 - 不需要指定数据长度
 a可以为变量, 立即数- 如果
a是变量, 则最多取其低4字节 - 如果
a是立即数, 则直接输出 - 特别地, 如果
a是3字节变量则被视为一个仅有低3位的4字节有符号整数. 
用于包含另外一个文件.
include a.casm 把a.casm文件的内容加入当前程序, 要求a.casm必须是一个可以读取的合法casm文件, 其内容格式依照本文其他要求.
比如说,1.casm中有一个block, 想要在2.casm执行时被调用, 就可以在2.casm中加入一句include 1.casm.
由include连接的若干代码文件共享变量,内存,堆栈.
一般情况下, 由于CASM对块的处理是顺序无关的, include可以位于任何块外部的位置, 文件头部, 尾部, 中间均不影响被包含代码的执行.
特别地, 如果重定义了同名代码块, 那么include的位置会影响到代码块的内容, 参见block.
include会吧目标文件当做一个新的流交给流解释器处理.
用于标记某个代码块开始.
用于标记某个代码块结束.
宏定义是对CASM代码内容的文本替换, 流解释器不会进行语法检查.
宏定义的生存周期是从被定义到被取消定义(如果取消的话, 否则就是到程序结束).
宏定义不可以嵌套, 虽然我完全有能力写一个解释嵌套宏定义的, 而且我以前也在考场上写过, 但是我不想再写了x
宏定义不可以递归,理由同上.
不会被宏定义替换的:
- 其他块外指令
 - 其他宏定义
 
define a b 定义a为b, 在该宏定义的生存周期内, 代码中所有的a字符串将被替换为b.
b只能为一个不断字的目标.- 如果出现重名的宏定义, 则按后出现的会替换先出现的.
 
undef a 取消a的已有宏定义.