⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 从c语言到cpu的指令设计.txt

📁 从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 + -