zoukankan      html  css  js  c++  java
  • 从零开始打造我的计算机系统【处理器设计】

    从零开始打造我的计算机系统

    处理器设计

    某种意义上而言如今的CPU模型的设计是为了更方便的设计操作系统,比如操作系统的安全是内核空间和用户空间的概念出现,进程模型使TSS模型出现,虚拟内存使MMU出现等等,如今我们要设计一个CPU的时候,实际上是考虑两点:

    一:它是否能很好的实现C语言。

    二:它是否能很好的实现一个操作系统。

    只要这两点满足,它已经成为一个可以用的CPU模型。

    在我想从零开始设计我的计算机以来,我就设计了几种CPU模型,当然,最开始选择的是支持虚拟内存的CPU,并且支持一些系统指令,然后我发现高估了自己的编程能力,所以决定从最简单实模式CPU做起。仍然保持它的可扩充性,以便以后升级。

    然而仍然保留原始的几个要求:

    1.不考虑兼容性。

    2.不考虑性能。

    3.不考虑移植。

    4.仅仅逻辑上可行。

    仍按照前面的想法,我将阐释一下几个关键点:分支,调用,中断和异常。

    现在我们按照一个简单的计划进行:

    单核实模式CPU

    单核保护模式CPU

    多核保护模式CPU

    2.1关键点

    2.1.1 分支

    分支分为条件分支和无条件分支,在条件分支中,共支持一下几种条件,或许用C语言的方式更好表达些,那就是等于跳转,不等于跳转,小于跳转,大于跳转,小于等于跳转,大于等于跳转。这些跳转模式,除了==!=之外,都支持无符号数比较和有符号数比较。(注意:不是无符号数和有符号数比较。)

    条件分支都是相对PC偏移的。

    无条件分支采用简单的寄存器跳转指令,抛弃了立即数跳转,只是因为我们用软件模拟,不必要考虑性能,而这样以来,编程人员的压力大大减轻。所以一个无条件分支指令看起来是这个样子。

    LOAD $S0, 32位立即数。

    JUMP $S0

    LOAD指令看起来已经超过了4字节也就是32位大小,怎么实现它曾经在我的脑海里思索了好久,目前而言,有两种方法。

    最重要的是,我们知道,当程序分支的时候,我们仅仅修改的是PC指,不进行堆栈保存。

    附录:怎么设计一条加载32位立即数的指令?

    最开始实现的时候,是把LOAD当作伪指令,由两条指令ORI以及LUI指令组成,ORI指令是或立即数,LUI是把一个十六位立即数加载到寄存器的高16位。

    所以一条LOAD $R0,#HELLO会被汇编成以下两条指令。

    ORI $R0,#HELLO_16位。

    LUI $R0,#HELLO_16位。

    这样看起来是解决了问题,但是怎么对这两条指令重载一直是我心中的疑问?

    有两个问题:第一,LOAD指令不是仅仅能加载地址。

    LOAD $R0,10000可能就是指10000而已,可以代表绝对地址,也可以不代表。

    在苦苦思索好久之后,我认为汇编器是无法决定这样的一条指令是否引用了内存,除非根据上下文,但是那样又太复杂了。

    于是我加上了一条汇编器规则:

    规则 $0_0 引用内存地址必须使用标号,否则会产生一个运行时错误。

    这样一来,LOAD $R0,#10000LOAD $R0,label是两条截然不同意义的指令,汇编器知道第一个10000不需要重载,而label可能值也是10000,但是需要重载。

    第一个问题解决之后,第二个问题迎面而来,怎么对一个32位的地址进行重载而不会引起错误。在这里假设HELLO的基地址变了,怎么跟着改变呢?

    ORI $R0,#HELLO_16位。

    LUI $R0,#HELLO_16位。

    上下文信息已经丢失了,假如ORILUI都加上重定位常量的话,就出错了。【在设计过程中我一度抛弃了LOAD指令,决定直接使用JMP LABEL指令,这样的话整个LABEL26位,又由于PC总是四字节对齐,所以其实寻址可以达到2^28次方,也就是256M的程序,我认为我永远也写不了那么大的程序,所以似乎是可以接受的,但是我后来又放弃了这个想法。】

    解决方法如下,上面两条指令已经被标记为可以重载,而ORI会和重定位常量的低16位相加,而LUI会和重定位常量的高16位相加。

    如上看来这是一个完美的解决方案。

    还有另外一种方法,就是在整个32位的指令集里,设计一条64位的指令,无须细说你也想到了它的样子,LOAD $R0,#32位立即数里,LOAD $R0占用低内存地址的32位,而32位立即数占用高32位,看起来是这个样子:

     

    目前采用第一种解决方案,也就是分散成两条指令。

    2.1.2 调用

    程序调用有两条指令,一条是call,一条是ret,现在我们来想一想当程序调用时保存了什么,仅仅保存返回地址到栈顶。对了,就是这样。

    无论任何时候我们不考虑一个调用是否是叶调用,因为我们不考虑性能。调用call的指令形式和JUMP类似,比如call $0,我在设计的时候就决定了这是一个全局的跳转,所以我们不再关心局部跳转和远程跳转,那是Intel的事。

    C语言调用时堆栈如图所示,为了更方便的实现C语言,我们添加SP寄存器和FP寄存器。

     

    2.1.3 中断和异常

    不管怎么说,以前我对中断和异常有误解,中断结束后,程序的流程应该回到原来的地方接着执行,然而异常却不是这样,比如说一个除以0的异常,很明显,程序不应该继续下去。但是从另外一个角度来看,缺页异常,当异常结束后,很明显应该回到原来的地方继续执行。

    这么说起来,中断其实是一种异常,而异常是一个更广泛的概念。

    中断向量号支持0-255,其中0-31留着系统使用,但是并不是每个都被使用了。

    中断向量号

    中断功能

    解释

    0

    除以0异常

    默认停止程序

    1

    单步调试

    Flag置位

    2

    不可屏蔽中断

     

    3

    设置断点

     

    中断可以嵌套吗?

    答:当一个设备发生中断的时候,设备会发起一个我发生中断了的标志,在CPU指令执行结束后,CPU检测所有发生中断的设备,检测到优先级最高的那个,假如检测得到的优先级比正在执行中断子程序的中断优先级还高,或者没有中断,则执行中断过程,即保存状态寄存器和PC地址,复写中断向量到中断寄存器,假如检测到的中断优先级没有正在执行的中断程序的优先级高,则CPU简单的抛弃这些中断请求。

    当设备发现中断没有被请求的时候,它们间隔一段时间再次发送中断。

    当中断完成后,CPU把中断发生标志清零,代表可以发生中断了。

    无论如何,一个32位的PSW足以保存这些东西了。

    软中断总是可以嵌套的,假如一个中断子程序使用了一个低优先级的软中断,我们必须完成它,所以软中断可以嵌套。

    2.2 寄存器布局

    我们讨论一下寄存器布局吧,首先从数量来说,我们会设置3232位的寄存器,编号从$0$31,另外4个浮点型寄存器,编号从$32$35(在我想不到更好的符号之前暂定)。

    然后我们首先讨论那些重要的寄存器并进行分配编号。

    重要的寄存器:PC,BP,SP,IDTR,PSW

    编号

    描述

    0

    zero寄存器,任何时候读取都是0,任何时候写它都无效,任何时候用它间接寻址产生错误。

    1

    中断表基址寄存器,允许你挪动表地址,不过暂时设为0

    2

    程序状态寄存器,目前只放置三个状态,TFtrace flag),中断许可,和中断发生,高八位存放中断发生时中断向量,再八位存放优先级数。

    3

    Sp栈顶寄存器

    4

    bp 帧寄存器

    5

    PC

    6-31

    通用寄存器(随着以后的扩展,通用寄存器会越来越少。)

     

    2.3 指令集

    应当无疑问,所有指令遵守opcode rs,rt,rd指令的格式,这是rs = rt (opcode) rd,我们遵守这一规定,就是目的寄存器在前。(请忽略rs,rd,rt原来的含义吧。)

    一共有

    l 7条整数运算指令

    l 4条浮点运算指令

    l 9条逻辑运算指令

    l 11条分支指令

    l 10条数据传输指令(只有他们和内存关联吧。)

    l 7条系统指令

    从我设置指令的格式来说,无论如何也不会超过64条吧?假如超过的话,又要改架构了。

    opcode(6)

    5

    5

    5

    11

     

     

    CPU指令集

    算术运算指令

    整数运算指令(7

    0

    rs

    rt

    rd

    保留

    add rs,rt,rd

    1

    rs

    rt

    imm

    addi,rs,rt,imm

    10

    rs

    rt

    rd

    保留

    sub rs,rt,rd

    100

    rs

    rt

    rd

    保留

    mul rs,rt,rd

    101

    rs

    rt

    rd

    保留

    div rs,rt,rd

    110

    rs

    rt

    rd

    保留

    mod rs,rt,rd

    浮点数运算指令(4

    111

    rs

    rt

    rd

    保留

    fadd rs,rt,rd

    1000

    rs

    rt

    rd

    保留

    fsub rs,rt,rd

    1001

    rs

    rt

    rd

    保留

    fmul rs,rt,rd

    1010

    rs

    rt

    rd

    保留

    fdiv rs,rt,rd

    逻辑运算指令(9

    1011

    rs

    rt

    rd

    保留

    and rs,rt,rd

    1100

    rs

    rt

    rd

    保留

    or rs,rt,rd

    1101

    rs

    rt

    保留

    保留

    not rs,rt

    1110

    rs

    rt

    rd

    保留

    xor rs,rt,rd

    1111

    rs

    rt

    imm

    andi rs,rt,imm

    10000

    rs

    rt

    imm

    ori rs,rt,imm

    10001

    rs

    rt

    imm

    xori rs,rt,imm

    10010

    rs

    rt

    shamt

    sll rs,rd,shamt

    10011

    rs

    rt

    shamt

    slr rs,rd,shamt

    比较转移指令(11)

    10100

    rs

    rt

    lable

    le rs,rt,lable

    10101

    rs

    rt

    lable

    ga rs,rt,lable

    10110

    rs

    rt

    lable

    nle rs,rt,lable

    10111

    rs

    rt

    lable

    nga rs,rt,lable

    11000

    rs

    rt

    lable

    leu rs,rt,lable

    11001

    rs

    rt

    lable

    gau rs,rt,lable

    11010

    rs

    rt

    lable

    nleu rs,rt,lable

    11011

    rs

    rt

    lable

    ngau rs,rt,lable

    11100

    rs

    rt

    lable

    eq rs,rt,lable

    11101

    rs

    rt

    lable

    ueq rs,rt,lable

    11110

    rs

    保留

    jmp rs

     

    数据传输指令(10

    11111

    rs

    rt

     

     

    mov rs,rt

    100000

    rs

    rt

    rd

    保留

    lword rs,rt,rd

    100001

    rs

    rt

    rd

    保留

    sword rs,rt,rd

    100110

    rs

    保留

    imm

    lui rs,imm

    100111

    f1

    rt

    rd

    保留

    ldoub $f1,$r2,$r3

    101000

    f1

    rt

    rd

    保留

    sdoub $f1,$r2,$r3

    系统指令(7)

    101001

    rs

    保留

    call rs

     

    101010

    保留

    ret

     

    101011

    rs

    保留

    push rs

     

    101100

    rs

    保留

    pop rs

     

    101101

    保留

    halt

     

    101110

    imm

    int imm

     

    101111

    保留

    IRET

     

    2.4 系统架构

    本系统,采用一下参数:

    l 一个屏幕,用800x600的窗口,采用EGE娘的EGE库。

    l 一块内存,内存暂定32MB

    l 一块硬盘,用文件作为模拟,64MB

    l 一个键盘

    l 一个交叉编译器

    l 一个程序加载器

    屏幕的缓冲区映射到内存的0xb8000处(为什么?)采用80X25模型,每行80个,共25行。所以需要80字节X25=2000字节。最终为b8d70-1

    键盘的缓冲区映射到内存的0xb8d70处的16字节。接外部中断16

    那么,开始吧。

     

     

     

  • 相关阅读:
    Matlab脚本和函数
    C# vb .net图像合成-合成富文本
    C# vb .net图像合成-合成星形
    C# vb .net图像合成-多图片叠加合成
    C# vb .net图像合成-合成艺术字 照片合成艺术字
    C# vb .net图像合成-合成文字
    C# vb .net实现锐化效果滤镜
    C# vb .net实现像素化效果滤镜打马赛克
    C# vb .net实现过度曝光效果滤镜
    C# vb .net实现淡色效果滤镜
  • 原文地址:https://www.cnblogs.com/likeyiyy/p/3551553.html
Copyright © 2011-2022 走看看