📄 从c语言到cpu的指令设计.txt
字号:
最近在设计一个虚拟机的指令集,在CISC和RISC两类中我更倾向于RISC.但是在具体的指令集细节上我却犯愁了.
我知道你肯定要建议我去学习一下其他CPU的指令集.但是你光看别人的指令集能知道别人在这么设计时的用意吗?
而且在多种CPU指令集中游离,不一会就晕了.有时候你会觉得CPU A的某些指令集设计的特别好.而有时候你有会
感觉CPU B的指令集设计的特别好.我知道你肯定想的是把两个CPU好的都保留下来.可是一旦你保留了某些指令后
你会发现这些指令和整个CPU好像有些不搭调.在优美方面似乎有降低了一个档次.后来我换了一个思路.既然从现有的
CPU的指令集去设计一类新的CPU指令这么麻烦.那么为什么不从一种高级语言去推出哪些指令集是必须的呢?于是乎
就有了下面这些内容.
在设计CPU的指令的时候我们可以借鉴现有的高级语言.例如C语言.本文就是从C语言的基本运算来推算出
基本的CPU指令集.
一:基本变量
CPU的基本变量有:全局非静态变量,全局静态变量,局部静态变量,局部自动变量
所有的全局变量和静态变量其实都是通过静态数据段来实现的.那么剩下的就只有两类了:自动变量和静态变量.
CPU肯定是不能直接支持变量的定义的.但是我们可以通过给变量分类来考虑内存分类.通过刚才的分析我们
可以知道CPU至少需要3类内存:代码段,数据段,堆栈段.注意这里的术语都是沿用x86的术语.只是为了让你熟悉.
可能在新的CPU中并不这么称呼他.关于内存分类暂时放在这里.我们先来考虑一下变量的基本运算.
算术运算:
加 例如 y = x + z;
减 y = x - z;
乘 y = x * z;
除 y = x / z;
求模 y = x % z;
位运算:
位与 y = x & z;
位或 y = x | z;
位非 y = ~x; //这里非是位非,并不是逻辑非.所以用的是~
位异或 y = x ^ z;
左移 y = x << 8;
右移 y = z >> 8;
比较运算:
大于 x > y ?
等于 x == y ?
小于 x < y ?
大于等于 x >= y ?
小于等于 x <= y ?
这些比较运算要分带符号数比较和不带符号数的比较.
这里指令中只有大于,小于,等于是必须的.有过汇编经验的同志们肯定知道.如果不提供大于等于或小于等于的话.就要通过两次跳转来实现
逻辑运算:
逻辑与 y = x && z;
逻辑或 y = x || z;
逻辑异或 y = x ^ z;
逻辑非 y = !x;
二:流程控制
无条件跳转 goto LABEL;
大于则跳转 if (x > y) {do someting;} else {do other thing;}
小于则跳转 if (x < y) {do someting;} else {do other thing;}
等于则跳转 if (x == y) {do someting;} else {do other thing;}
大于等于可以通过等于和大于来实现,小于等于类似.
三:函数调用
调用函数 x(5);
函数返回 return;
四:内存操作
这部分设计到内存寻址.现在暂时不讨论.
五:其他元素
C语言中您好可以定义结构体,共用体,enum等等.这些都会设计到内存的细节.暂时不讨论.
六:内存访问.
C语言中常见的内存访问有.
x = 5; //内存操作数和立即数
x = y; //两个内存操作数
*x = 5; //指针和立即数
*x = y; //指针和内存操作数
总结上面的运算我们可以等到基本的指令集了.如果在增加一些控制指令那么我们得到一个简单的CPU指令集了.
我们先定义一下基本的寄存器吧!
ip : 指令指针
sp : 堆栈指针
flag : 标志寄存器
rmul : 乘法寄存器
idt : 中断描述符
reg0 - reg12 13个通用寄存器.
然后定义一下基本的指令结构.
┏━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃操作码 8位┃第一操作数 8位┃第二操作数 8位┃第三操作数 8位┃
┗━━━━━┻━━━━━━━┻━━━━━━━┻━━━━━━━┛
8为操作码:可以支持256条指令.我想不管是现在还是将来都够了.
后面的3个8位表示操作数的寻址方式. 0 - 15表示寄存器号,占4位.高4位表示寻址方式.
寻址方式:
我想只支持三种寻址方式.
立即数 : 指令 SetReg支持 , SetReg reg1, imm32;
内存操作数: 指令 Load和Save支持, Load reg1, [reg2], imm8
寄存器 : 除了上面列出的指令,其他指令都只支持寄存器操作数.
下面说明每条指令的助记符和作用还有指令格式.
add 加 add reg1, reg2, reg3; reg3 = reg1 + reg2
sub 减 sub reg1, reg2, reg3; reg3 = reg1 - reg2;
mul 乘 mul reg1, reg2, reg3; reg3 = reg1 * reg2; 高32位在rmul中.
div 除 div reg1, reg2, reg3; reg3 = reg1 / reg2;
mod 模 mod reg1, reg2, reg3; reg3 = reg1 % reg2;
bitand 位与 bitand reg1, reg2, reg3; reg3 = reg1 & reg2;
bitor 位或 bitor reg1, reg2, reg3; reg3 = reg1 | reg2;
bitxor 为异或 bitxor reg1, reg2, reg3; reg3 = reg1 ^ reg2;
上面的指令格式都和标准格式一样
bitnot 位非 bitnot reg1, reg2 ; reg1 = ~reg2 ; 这里是~表示位非
┏━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃操作码 8位┃第一操作数 8位┃第二操作数 8位┃ 不用 ┃
┗━━━━━┻━━━━━━━┻━━━━━━━┻━━━━━━━┛
bitrs 右移 bitrs reg1, reg2, reg3; reg3 = reg1 << reg2;
bitrs reg1, imm8, reg3; reg3 = reg1 << imm8; 这里的寻址方式请注意
bitls 左移 bitls reg1, reg2, reg3; reg3 = reg1 >> reg2;
bitrs reg1, imm8, reg3; reg3 = reg1 >> imm8; 这里的寻址方式请注意
┏━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃操作码 8位┃第一操作数 8位┃第二操作数 8位┃第三操作数 8位┃
┗━━━━━┻━━━━━━━┻━━━━━━━┻━━━━━━━┛
注意imm8的寻址方式在第一操作数中
cmps 算数比较 cmpa reg1, reg2 ; flag低两位表示比较结果
cmpl 逻辑比较 cmpl reg1, reg2 ; flag低两位表示比较结果
┏━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃操作码 8位┃第一操作数 8位┃第二操作数 8位┃ 不用 ┃
┗━━━━━┻━━━━━━━┻━━━━━━━┻━━━━━━━┛
je 相等跳转 jz +/-imm24 ; ip +/- imm24 ; 这里的寻址方式请注意
jnz 不等跳转 jnz +/-imm24 ; ip +/- imm24 ; 这里的寻址方式请注意
ja 大于则跳转 ja +/-imm24 ; ip +/- imm24 ; 这里的寻址方式请注意
jl 小于则跳转 jl +/-imm24 ; ip +/- imm24 ; 这里的寻址方式请注意
jmps 短跳转 jmps +/-imm24 ; ip +/- imm24 ; 这里的寻址方式请注意
┏━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃操作码 8位┃25位地址,每条指令都是32位,所以24位可以表示26位地址,但是这里只用了25位 ┃
┗━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
jmpl 长跳转 jmpl reg1 ; ip = reg1
┏━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃操作码 8位┃第一操作数 8位┃ 不用 ┃ 不用 ┃
┗━━━━━┻━━━━━━━┻━━━━━━━┻━━━━━━━┛
call 函数调用 call reg1 ; push ip, ip = reg1
┏━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃操作码 8位┃第一操作数 8位┃ 不用 ┃ 不用 ┃
┗━━━━━┻━━━━━━━┻━━━━━━━┻━━━━━━━┛
ret 函数返回 ret +/-imm24 ; pop ip, sp +/- imm24
┏━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃操作码 8位┃25位地址,每条指令都是32位,所以24位可以表示26位地址,但是这里只用了25位 ┃
┗━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
int 中断调用 int imm8 ; push ip, ip = idt[imm8]
int reg1 ; push ip, ip = idt[reg1]
reti 中断返回 reti ; pop ip
cli 关中断
sti 开中断
in IO输入 in imm8, reg2 ; reg2 = iospace[imm8]
in reg1, reg2 ; reg2 = iospace[reg1]
out IO输出 out imm8, reg2 ; iospace[imm8] = reg2
out reg1, reg2 ; ipspace[reg1] = reg2
halt 关机
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -