理解计算机系统组成原理 - 运用Java编程的工具rPeANUt

时间:2024-10-13 07:01:41

本文原创所有,未经允许不得转载,请尊重作者权利。

-----------------------------------------------------------------------------------------------

前段时间在知乎看到一个问题,国外的计算机专业 是如何教 《计算机组成原理》 这门课的?/question/38838088

就如题主反应的一样,国内对计算机组成原理(国外多称计算机系统Computer System)的教学大多刻板,艰深晦涩。

本文通过整理澳大利亚国立大学对Computer System的教学过程,借鉴先进技术,利用学习工具,也再一次对计算机组成原理的知识进行了一次复习。

学习的核心是依靠一款Java编程的工具rPeANUt,可以在bitbucket上找到,但是网站提供的版本并非最新。

文末将会附贴最新的rPeANUt下载链接和英文原版使用说明。

------------------------------------------------------------------------------------------------------------------------

rPeANUt是一个汇编机微处理器,可以在其中进行基本的代码开发和理解真实的微处理器内部是如何进行处理的(即硬件内部的执行步骤)。

下图是rPeANUt机的内部构成。


介绍

       rPeANUt机是一个用于在澳大利亚国立大学中教学计算机系统所制作的一个简单微处理器。这是一个Java编程实现的汇编模拟器。这份文件旨在描述一个汇编机微处理器,为了学生可以进行代码编程和理解真实的微处理器内部是如何进行编程开发的。

 

rPeANUt机 – 概述

       rPeANUt是一个32位微处理器,拥有16位地址、32位寄存器以及可寻址的字长32位内存存储。最大的地址存储数目是216=65536字长即262144字节(256K)。只有0x0000到0x7FFF地址被用于实际存储,0x8000到0XFFFF地址用于内存的IO映射(实际上只有3个地址单元被使用)。当微处理器重置指令寄存器(IP)为0x0200,因此通常一个程序会被装载到这个位置执行。0x0000到0x00FF地址被保留用于中断向量和其他操作系统代码,而且实存最后的960个单位被用作缓冲区。

微处理器包括以下的32位寄存器:

l  8个通用寄存器,可以用作存储数据或地址。记作R0,R1,…R7。

l  1个指令寄存器(IR),用于保存当前正在被执行的指令。

l  1个状态寄存器(SR),包含CPU状态信息。0位被用作整形数溢出(OF),位1被用作中断屏蔽(IM),位2被用作启动中断计时器(TI)。

l  3个常量寄存器,记作ONE,ZERO和MONE。它们分别包含常数1,0和-1。

微处理器包括以下的16位寄存器:

l  1个栈指针(SP),指向栈顶,用于方法的调用和返回,当微处理器被重置时SP被设定到0x7000。

l  1个程序计数器(PC),包含下一个要执行指令的地址。

注意指令寄存器不直接通过指令集访问,尽管指令的执行显然会影响到这个寄存器。

 

微处理器也包含一个控制单元,使CPU的数据按照一定顺序运行。微处理器有以下所示的执行周期:

             

 do {
IR = mem[PC];
PC = PC + 1;
execute_instruction in IR;
check for interrupts;
} while(!halt);

 

rPeANUt指令集

       指令均为单字长(32位)。寄存器被标记为R0,R1,…R7,SP,SR,PC,ONE,ZERO,MONE并拥有半字节(4位)的机器码。这种半字节编码方式为:R0记为0x0,R1记为0x1,…,R7记为0x7,SP记为0x8,SR记为0x9,PC记为0xA,ONE记为0xB,ZERO记为0xC,MONE记为0xD。如果寄存器编码是0xE那么在指令最后16位的立即值会被使用。地址和值占用32位机器编码指令中的16位,值从16位扩展到32位。下表的指令集描述假定指令字已经被加载到指令寄存器,并且程序计数器已经指向下一个指令字。

指令名称

汇编指令

机器码

描述

add <RS1> <RS2> <RD>

0x1<RS1><RS2><RD><value>

RD <- RS1 + RS2

sub <RS1> <RS2> <RD>

0x2<RS1><RS2><RD><value>

RD <- RS1 - RS2

mult <RS1> <RS2> <RD>

0x3<RS1><RS2><RD><value>

RD <- RS1 * RS2

div <RS1> <RS2> <RD>

0x4<RS1><RS2><RD><value>

RD <- RS1 / RS2

取模

mod <RS1> <RS2> <RD>

0x5<RS1><RS2><RD><value>

RD <- RS1 % RS2

 

按位与

and <RS1> <RS2> <RD>

0x6<RS1><RS2><RD><value>

RD <- RS1 & RS2

 

按位或

or <RS1> <RS2> <RD>

0x7<RS1><RS2><RD><value>

RD <- RS1 | RS2

按位亦或

xor <RS1> <RS2> <RD>

0x8<RS1><RS2><RD><value>

RD <- RS1 ^ RS2

按位左旋

rotate <RA> <RS> <RD>

0xE<RA><RS><RD><value>

RD <- RS << RA |

RS >>> (32 - RA)

neg <RS> <RD>

0xA0<RS><RD><value>

RD <- - RS

按位否

not <RS> <RD>

0xA1<RS><RD><value>

RD <- ~ RS

寄存器复制

move <RS> <RD>

0xA2<RS><RD><value>

RD <- RS

立即调用

call <address>

0xA300<address>

SP <- SP +1

mem[SP] <- PC

PC <- address

返回调用

return

0xA3010000

PC <- mem[SP]

SP <- SP-1

陷入

trap

0xA3020000

SP <- SP +1

mem[SP] <- PC

PC <- 0x0002

SR <- SR | (1<<1)

跳转

jump <address>

0xA400<address>

PC <- address

零跳转

jumpz <R> <address>

0xA41<R><address>

if (R == 0x00000000) {

PC <- address

}

否定跳转

jumpn <R> <address>

0xA42<R><address>

if ((R&0x80000000) !=

0x00000000) {

PC <- address

}

非零跳转

jumpnz <R> <address>

0xA43<R><address>

if (R != 0x00000000) {

PC <- address

}

重置状态字

reset <BIT>

0xA50<BIT>0000

SR <- SR & ~(1<<BIT)

设置状态字

set <BIT>

0xA51<BIT>0000

SR <- SR | (1<<BIT)

入栈

push <RS>

0xA60<RS><value>

SP <- SP + 1

mem[SP] <- RS

出栈

pop <RD>

0xA61<RD>0000

RD <- mem[SP]

SP <- SP - 1

立即寻址

load #<label or value> <RD>

0xC00<RD><value>

RD <- ext(value)

绝对寻址加载

load <address> <RD>

0xC10<RD><address>

RD <- mem[address]

间接寻址加载

load <RSA> <RD>

0xC2<RSA><RD>0000

RD <- mem[RSA]

基址移位寻址加载

load <RSA> #<value> <RD>

0xC3<RSA><RD><value>

RD <- mem[RSA +

ext(value)]

绝对寻址存储

store <RS> <address>

0xD1<RS>0<address>

mem[address] <- RS

间接寻址存储

store <RS> <RDA>

0xD2<RS><RDA>0000

mem[RDA] <- RS

基址移位寻址存储

store <RS> #<value> <RDA>

0xD3<RS><RDA><value>

mem[RDA+ext(value)]

<- RS

中止

halt

0x00000000

fetch execution stops

 

rPeANUt硬件与中断

       通过内存IO映射提供一个简单终端。设备的交互是通过这三个地址:0xFFF0数据IO,0xFFF1状态,0xFFF2控制。状态寄存器(0位)最低有效位的数据为1是表示数据可用,为0时表示数据不可用。在1位的状态寄存器位0时表示可以接收数据,为1时表示不可以接收数据。对设备写操作是在数据IO位置写内存地址,对设备读操作只需从数据IO地址读取。注意状态寄存器应当检查之前对设备的读写操作(尽管在写操作前检查状态字是良好的做法,但是在该模拟器中它总是可以进行写操作的,因此你可以直接在数据IO位置进行写操作)。控制地址被用作设置设备中断(控制地址的0位为0表示不可中断,为1表示可以中断)。当关键字触发中断被生成时,中断字会在之后被设定。注意默认情况下中断是不可行的。

       在状态字寄存器(若为1表示可用,为0表示不可用)的3位(TI)这里有一个中断计时器可用。当启用时且<从起始的时钟周期>%1000=0,中断计时器将会关闭。

       该模拟计算机拥有一个192像素宽、160像素高(或0xC0宽和0xA0高)的黑色和白色屏幕。屏幕的内容决定于地址起始0x7C40到地址结束0x7FFF的缓冲区。假定(0,0)作为屏幕的左上角,那么像素(x,y)是在地址0x7C40+6*y+x/32字长的x%32位。如果这一位为1那么像素点是白色,如果这一位为0那么像素点是黑色。

       若当前程序计数器发生中断入栈,那么程序计数器地址被设定和中断相联系。任何寄存器用于中断设备的程序中都必须被保存和恢复。中断事件也会设置高的中断屏蔽,并在中断程序完成前被清除。标准‘返回’指令被用于返回中断。设备中断和它们的地址如下表所示:

中断

地址

描述

内存错误

0x0000

访问内存不可寻址时发生

IO设备

0x0001

终端设备上的中断触发并命中关键字时发生

陷入

0x0002

陷入指令执行时中断发生

计时

0x0003

计时器触发且每1000个时钟周期

 

rPeANUt汇编机

       rPeANUt汇编机提供一个将汇编程序转换成rPeANUt机器码的简单方式。它有两个工作阶段。第一个是按行逐行转换为机器码。这种转换的发生需要创建字符表和处理地址列表。第二个阶段包括处理所有那些丢失的地址。注意汇编机直接对硬件进行写操作是模拟主存操作(只是为了简单起见)。

       每一个指令必须置于单一一行。没有指令和标识的行通常会忽略。任何在第一个“;”分号后的字符被认为是注释且被忽略。

       作为汇编指令和数据的编码被置于下一个可以得到的地址。处理过程从地址0x0000开始且只能向前执行。如果你想向前跳步到一个新的地址那么你可以在“:”冒号前置入地址。注意不能向后执行。

       地址标识可以包含数字字母字符但是不能以数字开头且不能使关键词(如指令、寄存器名称、汇编指令等均为关键词)。一个单地址可以有多个名称,但是必须使用单独的行实现。

       指令应当单独置于一行或在一个冒号“:”之后。指令有指令名并拥有其参数,他们均使用空格隔开。指令名在该文件的说明部分的指令集表中给出。请注意指令参数的顺序。

       寄存器被记作:R0, R1, R2,…, R7, SP, SR, PC, ONE, ZERO, MONE。地址可使用整型数(10进制数或16进制数,16进制前缀为0x)或由标签给出。立即地址或值得前缀是#symbol。读写操作的地址形式由参数列表决定。

       关键词“block”被用作申请块内存。如果有块关键词,那么给出正整型数k并会申请k个字(均初始化为0)。如果块后有一个立即整型数或字符那么就新开辟一个字长,这个字初始化位给出的值。如果给出一个字符串使用#"<string>" (. #"Hello World")那么开辟一个块字并为其初始化成这个字符串。字符存储在每个字的最低有效位,并附加一个空的终止子。

       常数值可以是十进制、十六进制或一个字符(字符编码被用作值)。对于十六进制数前缀应当为“0x”。字符应当置于两个单引号之间,如#'c'等同于#99或#0x63。转义字符同样适用:#'\n'。

       关键词“ #include ”可以用于从其他文件插入资源。有且仅有一个字符串(没有#)包含指定文件。注意文件路径是运行rPeANUt的相对路径,如#include ""。

       “#define”用于让字符串代替一个标识,这是一种基本的文本替换,可以对常量重命名或简化重复代码。如:

#define start 0x0100
#define setTen load #10
start : setTen R5


       汇编代码可以定义简单的宏。这是一个使用几行完成的代码:

l  第一行是关键词“ macro ”

l  接下来是宏名称及其参数变量(参数变量以符号'&'开头)

l  接下来几行的代码是宏的实现(可能包含变量)

l  最后,宏使用单独的一行“ mend ”结束

宏可以通过宏的声明并后接参数来使用。宏必须在使用前定义,它们是文本替换的规则。以下是一个使用宏的hello world程序示例:

macro
putc &c
     load #&c R1
     store R1 0xfff0
mend
0x100 :  putc 'h'
         putc 'e'
         putc 'l'
         putc 'l'
         putc 'o'

------------------------------------------------------------------------------------------------------------------------------------

首先,运行rPeANUt需要java jdk环境,安装配置环境与使用不再冗述。

打开,运行界面如图所示。


---------------------------------------------------------------------------------------------------------------------------

几点说明:

左侧白色区域用于输入代码编程,边框处数字用于标示行号,右下角区域白色、黑色屏幕用于显示输入结果,右侧灰色区域在点击Assemble后显示硬件内部执行顺序,边框处各寄存器名称后显示内部存储的数据。Count用于记步,Step为按步执行,Run顺序执行,fast、slow表示执行时的速度,Stop用于暂停执行,Poke用于设置地址与值。菜单栏可以完成文本保存、打开,字体大小以及其他详细设置。

---------------------------------------------------------------------------------------------------------------------------

以下以简单的hello world程序理解计算机系统的硬件内部执行过程是怎样的。(通用寄存器、指令寄存器、状态寄存器、程序计数器、栈指针、控制单元等的各硬件执行过程)

程序代码如下所示。

macro
putc &c
         load #&c R1
         store R1 0xfff0
mend
0x100 :  putc 'h'
         putc 'e'
         putc 'l'
         putc 'l'
         putc 'o'
输入程序,Assemble运行。


右侧灰色区域显示了微程序内部的运行步骤,以及各个寄存器的初始状态。

按步运行STEP。观察寄存器值与下方屏幕显示。


--------------------------------------------------------------------------------------------------------------------------

由上可观硬件的每一步的执行情况,各寄存器的值显而易见。

另外,我们还可以定义更多的宏来实现更复杂的功能,并通过rPeANUt来分析计算机系统内部的运行状态。

此处再给出一个简单的代码示例1+2+3+4+5,帮助理解计算机系统的组成原理。

依据上文给出的指令集也可以自己编写一些程序观察(需要一些汇编基础)。

macro
putc &a &b &c
     load #&c R0
     load #&a R1
     load #&b R2
     add R1 R2 R1
     add R2 R0 R2
     add R1 R2 R1
     add R2 R0 R2
     add R1 R2 R1
     add R2 R0 R2
     add R1 R2 R1
     store R1 0xfff0
mend
0x100 : putc 1 2 1

--------------------------------------------------------------------------------------------------------------------------

rPeANUt 英文说明原文、中文翻译、文件、rPeANUt源程序下载可以进入

本文原创所有,未经允许不得转载,请尊重作者权利。

--------------------------------------------------------------------------------------------------------------------------

END

2016/08/14