English Readme <- Click Me
ONICPU 是游戏缺氧的一个 CPU 模组,支持你建造可编程的代码执行控制单元,用它来实现复杂的自动化功能。
目前原版游戏只有简单的逻辑门自动化,这在实现一些很大型的电路时,会非常复杂,因此设计了这个模组,使用代码来控制自动化。
主要功能:
- CPU 运算单元,支持 4 输入 4 输出 IO,8 输入 8 输出 IO
- CPU 支持使用汇编代码或者 JavaScript 代码进行控制
- 拓展线组读取器、线组写入器使其支持传输 32 位数值
- 拓展一系列传感器,使其可以直接读取数值,传给 CPU 处理
- 数码管显示
你可以在自动化菜单中找到 CPU(需要先解锁相应科技树),目前有三种大小的 CPU:
- 2 输入 2 输出 IO
- 4 输入 4 输出 IO
- 8 输入 8 输出 IO
CPU 左侧是输入端口,程序可以读取它的值,右侧是输出端口,可以输出值,控制下游连接的设备。 CPU 使用一个控制端口(中下位置)来进行控制,只有在控制端口接收到绿色信号时,单元才会启动。
启动状态下,你的程序默认会以 5Hz 的频率执行,在每个执行周期中,你可以读取输入参数,进行自定义计算,然后修改输出端口的值。
因为游戏代码限制,所有 CPU 端口最大支持 Int32(-2,147,483,648 ~ 2,147,483,647)
每种 CPU 都有两个版本,有(JavaScript)后缀的表示这个 CPU 使用 JavaScript 代码进行控制。JavaScript 简单易上手,汇编比较复杂但有趣,你可以选择自己喜欢的。
注意: 在代码中请不要使用 while 循环来做延迟,这会导致游戏卡住,应该使用变量计数器来实现延时。
有些时候总是会遇到一些奇奇怪怪的问题,这时候你可以按照以下顺序尝试
- 检查是否有接线错误,尤其是Reset位置是否存在误接线
- 保存并重新加载(SL)游戏,这通常来说会解决大多数的问题
- 尝试开一个新存档并禁用其它mod(根据个人经验,这一步几乎没有作用)
- 提交一个Issue
你可以在选中 CPU 单元后,在右侧菜单中点击“编写程序”,来打开编辑器,在编辑器中:
- 顶部显示的是程序当前的状态信息
- 在左侧输入框中编写代码
- 右上角按钮控制程序运行、暂停。
- 右侧显示的是实时的程序 IO 数值
- 在汇编模式中,右侧还会显示寄存器、内存数据
- 在 JavaScript 模式中,右侧还会显示程序输出日志信息
建议你可以在 Visual Studio Code 或者其他代码编辑器软件中编写好代码再复制到游戏中。
点击 立即编译 可以立即编译程序,编译会临时暂停运行程序,可以再点击上方运行按钮开始运行,点击 保存并关闭 可以编译程序然后关闭。
提示:CPU 是否运行是受控制端口是否有绿色信号控制的,但你在代码编辑器中手动点击运行时运行不受信号限制。
本模组拓展了一系列传感器,使其可以直接读取数值,你可以将其连接至 CPU,然后就可以读取数值来进行相关处理。
本模组添加了数值显示器,你可以用它来显示网络中的数值。分为 3 档,分别可以显示 8 位、16 位、32 位整数。
当你选择有(JavaScript)后缀的 CPU 后,你就可以开始用 JavaScript 写代码了。你的程序会以下方 3 个主要函数来组织:
- start:程序启动时执行
- stop:程序停止时执行
- tick:程序在运行中每逻辑帧执行
tick 函数必须存在,否则 CPU 单元会停止执行,start 和 stop 可选。
function start() {
//代码,启动时执行
}
function stop() {
//代码,停止时执行
}
function tick() {
//代码,每逻辑帧执行
}
你的主要程序通常应该写在 tick 函数中,tick 函数的推荐逻辑应该是,读取数据->处理数据->输出数据。
你可以在 Demo 目录 来查看几个示例
你还可以根据更多条件来实现更多复杂功能,下方是 CPU 单元一些支持的功能说明:
输入和输出: 你可以使用 io.PX 读取或设置引脚值。例如:
- 读 P0 输入
let pin0value = io.P0;
- 设置 P4 输出
io.P3 = 1;
注意:游戏会在名称 start、stop、tick 执行完毕后将修改后的输出值同步到信号网络。
存储数据: 你的代码中其他的位置声明变量,在每次编译/上电/游戏重新加载会丢失,需要手动加载。 模组内置了 storage 对象,在这个对象下的数据会被游戏保存,可以在下次存档加载后读取
- 储存数据
storage.example = 31;
- 读取数据
io.P3 = storage.example;
辅助函数
- **getbit: 你可以调用 **getbit(io.P0, 1) 得到输入引脚的 1 位值。
- **changebit: 你可以调用 io.P3 = **changebit(io.P3, 1, 0) 设置输出引脚的 1 位值=0。
- **log: 你可以调用' **log('message') '来记录编辑器中的一些消息。
- **reset: 调用 **reset() 来重置所有输出 IO
- **shutdown: 你可以调用 **shutdown('error message') 来关闭这个 CPU 单元。
CPU 支持使用汇编代码。但汇编比较难,推荐还是使用 JavaScript,也可以用汇编,因为挺好玩的,但在写代码之前,你需要学习汇编相关知识。
本 CPU 支持的是类 C51 汇编(部分子集,和标准不一样),所有计算指令、寄存器大小均为 32 位。本文只说明 CPU 指令相关支持情况和特殊说明,汇编相关知识你需要到互联网上搜索相关资料。
指令格式
[操作码] [操作数] 例如: MOV A, P0 :label 标签
支持的寻址方式
- 立即数寻址 #20H 例如:MOV A,#20H
- 寄存器寻址 PSW / A / R1 例如:MOV A,R1
- 寄存器间接寻址 @R1 / @REG1 例如:MOV A,@R1
- 跳转寻址 :label 例如:jne :loop
支持的寄存器
- R0-R7 内置通用寄存器
- PSW 程序状态字寄存器 (与 8086 定义一致)
- A
- B
- C
- ACC 累加寄存器
- SP 堆栈指针寄存器
- PC 程序计数器
支持的指令表
无操作数指令
- NOP
- RST
- HLT
- POP
1 操作数指令
- SWAP
- PUSH
- NEG
- JMP
- JE
- JNE
- JZ
- JO
- JNO
- JS
- JNS
- JP
- JNP
- JC
- JNC
- JB
- JNB
- JA
- JNA
- JAE
- JNAE
- JBE
- JNBE
2 操作数指令
- MOV
- XCHG
- XCHD
- ADD
- SUB
- INC
- DEC
- MUL
- MOD
- AND
- OR
- XOR
- NOR
- MOD
- SHL
- SHR
- CMP
读取写入 IO
- 读取写入 IO 简化了,无特殊指令,需要内置寄存器一样,直接读取/写入:MOV A, P0 或者 MOV P0, #11
示例程序
mov r0,#10 mov r1,#0 mov r2,#0 :loop inc r2 dec r0 cmp r0,r1 jne :loop mov p3,r2 hlt