CSCI 2122任务4
截止日期:2024年3月22日星期五晚上11:59,通过git提交
目标
本课业的目的是练习用C进行编码,并强化中讨论的概念关于程序表示的类。在这个任务1中,您将实现一个二进制翻译器2,如Rosetta3。您的程序将从翻译一个简单的指令集(比x86简单得多)到x86并生成x86汇编代码。代码将然后通过组装和运行来进行测试。为了使任务更简单,将其分为两部分。在第一部分中,您将实现加载器和一个简单的翻译器,该翻译器翻译更简单的指令。在第二部分中,您将扩展翻译器以翻译更复杂的指令。
准备
1.完成工作分配0,或确保已安装完成工作分配所需的工具。
2.克隆您的课业存储库:
https://git.cs.dal.ca/courses/2024-winter/csci-2122/assignment-4/????.git哪里是您的CSID。如果需要,请参阅课业0中的说明和Brightspace教程你不知道怎么做。在存储库中有一个目录:xtra,用于编写代码。目录中有一个tests目录,其中包含每次提交代码时将执行的测试。请不要修改测试目录或根目录中的.gitlab-ci.yml文件。正在修改这些文件可能会破坏测试。当分配完成时,这些文件将替换为原始文件分级。您将获得可用于构建程序的示例Makefile文件。如果你如果正在使用CLion,则将从CLion生成的CMakeLists.txt文件生成Makefile。
背景
对于这项任务,您将把简化的基于RISC的16位指令集中的二进制转换为x86-64装配具体地说X指令集包括少量(大约30条)指令,
其中大部分在大小上是两个字节(一个字)。X体系结构具有16位字大小和16个通用16位寄存器(r0…r15)。几乎所有
指令对16位数据块进行操作。因此,所有值和地址的大小都是16位。代 写CSCI 2122实现二进制翻译器全部16位值也以big-endian格式编码,这意味着最高有效字节位于第一位。
除了16个通用寄存器外,该体系结构还有两个特殊的16位寄存器:一个程序计数器(PC),其存储将被执行的下一指令的地址和状态(F),其存储表示CPU状态的位标志。状态寄存器(F)的最低有效位是条件标志,表示最后一次逻辑测试操作的真值。如果这个条件是真的,否则就是假的。
1这项任务的想法间接来自于凯尔•史密斯。
2.https://en.wikipedia.org/wiki/Binary_translation
3.https://en.wikipedia.org/wiki/Rosetta_软件
此外,CPU使用最后一个通用寄存器r15来存储指向程序堆栈的指针。
当一个项从堆栈中弹出时,此寄存器将增加2,当
一个项目被推到堆栈上。程序堆栈用于存储临时值、
函数以及函数调用的返回地址。
X指令集
指令集包括大约30条指令,用于执行算术和逻辑、数据移动、堆栈操作和流控制。大多数指令以寄存器作为操作数并存储
寄存器中运算的结果。但是,有些指令也将立即值作为操作数。因此,有四类指令:0操作数指令、1操作数指令,2操作数指令和扩展指令,它们采用两个字(4字节)而不是一个字。
除了扩展指令之外的所有指令都被编码为单个字(16位)。扩展说明如下
也是一个字,但后面跟着一个额外的一个字操作数。因此,如果指令是扩展的
指令时,PC在指令执行期间需要2的额外增量。如前所述
以前,大多数指令都被编码为一个单词。单词的最高有效两位
指示指令是0操作数指令(00)、1操作数指令“01”还是2操作数
指令(10)或扩展指令(11)。对于0操作数指令编码,两个最高有效位为00,接下来的六位表示指令标识符。这个
指令的第二个字节是0。
对于1操作数指令编码来说两个最高有效位是01,
下一位指示操作数是立即数还是寄存器,
并且接下来的五个比特表示指令标识符。如果第三多
有效位为0,则第二个字节的四个最高有效位
对要操作的寄存器进行编码(0…15)。否则,如果第三个最高有效位是1,那么
第二个字节对立即值进行编码。
对于2操作数指令编码,两个最高有效位为10,并且
接下来的六个比特表示指令标识符。第二个字节编码
两个四位块中的两个寄存器操作数。每个4位块标识一个寄存器(r0…r15)。
对于扩展指令编码,两个最高有效位是
11,下一位指示是否使用第二寄存器操作数,以及
接下来的五个比特表示指令标识符。如果第三多
有效位为0,则指令仅使用指令后面的一个字立即数操作数。否则,如果第三个最高有效位是1,那么四个
第二字节的最高有效位对作为第二操作数的寄存器(1…15)进行编码。
指令集如表1、表2、表3和表4所示。每个描述都包括助记符(以及
语法)、指令的编码、指令的描述和功能。例如,
loadi和push指令具有以下描述:
记忆编码描述函数
添加rS,rD 10000001 S D将寄存器rS添加到寄存器rD.rD← rD+rS
loadi V,rD 11100001 D 0将立即值或地址V加载到
寄存器rD。
rD← 内存[PC]
个人计算机← PC+2
将rS 01000011 S 0将寄存器rS推到程序堆栈上。r15← r15-2
存储器[r15]← rS
首先,观察加法指令取两个寄存器操作数,并将第一个寄存器与第二个寄存器相加。所有2操作数指令仅在寄存器上操作,第二个寄存器既是源,又是
目的地,而第一个是源。它是一条2操作数指令;因此前两位是10,其
指令标识符是000001,因此指令的第一个字节是0x81。
其次,loadi指令是一条扩展指令,它采用16位立即数并将其存储在
登记因此,前两位为11,寄存器位设置为1,指令标识符为00001。
因此,第一个字节被编码为0xE1。
第三,push指令是一条1操作数指令,取单个寄存器操作数。因此,第一
两个位为01,立即数位为0,指令标识符为00011。因此,第一个字节是
编码为0x43。
注意,S和D是表示S和D的4位矢量。
表1:0-操作数指令
记忆编码描述函数
ret 0000000 1 0从过程调用返回。P C← 存储器[r15]
r15← r15+2
cld 00000010 0停止调试模式请参阅下面的调试模式。
std 00000011 S 0启动调试模式请参阅下面的调试模式。
表1:1-操作数指令
记忆编码描述函数
否定rD 01000001 D 0否定寄存器rD。rD← −rD
而不是rD 01000010 D 0逻辑否定寄存器rD。rD←!rD
inc rD 01001000 D 0增量rD。rD← rD+1
dec rD 01001001 D 0递减rD。rD← rD–1
将rS 01000011 S 0将寄存器rS推到程序堆栈上。r15← r15–2
存储器[r15]← rS
pop rD 01000100 D 0将堆栈中的值放入寄存器rD.rD← 存储器[r15]
r15← r15+2
out rS 01000111 S 0将rS中的字符输出到std-out。输出← rS(见下文)
br L 01100001 L如果条件位为,则相对于标签L的分支
是的。
如果F&0x00010x001:
个人计算机← PC+L–2
jr L 01100010 L相对于标签L.PC的跳跃← PC+L–2
表3:2-操作数指令
记忆编码描述函数
将寄存器rS加到寄存器rD。rD← rD+rS
sub rS,rD 10000010 S D从寄存器rD.rD减去寄存器rS← rD-rS
mul rS,rD 10000011 S D将寄存器rD乘以寄存器rS.rD← rD*rS
以及rS、rD 10000101 S D和寄存器rS与寄存器rD。rD← rD和rS
或rS、rD 10000110 S D或寄存器rS与寄存器rD。rD← rD|rS
xor rS、rD 10000111 S D X或寄存器rS与寄存器rD。rD← rD^rS
test rS1,rS2 10001010 S D将条件标志设置为true当且仅当
rS1∧rS2不是0。
如果rS1&rS2!=0:
F← F|0x0001
其他的
F← &0xFFFE
cmp rS1,rS2 10001011 S D将条件标志设置为true当且仅当
rS1<rS2。
如果rS1<rS2:
F← F|0x0001
其他的
F← &0xFFFE
equ rS1,rS2 10001100 S D将条件标志设置为true当且仅当
rS1rS2。
如果rS1==rS2:
F← F|0x0001
其他的
F← &0xFFFE
mov rS、rD 10001101 S D将寄存器rS复制到寄存器rD。rD← rS
加载rS,rD 10001110 SD将字从存储器加载到寄存器rD
由寄存器rS指向。
rD← 内存[rS]
stor rS,rD 10001111 SD将字从寄存器rS存储到存储器
寄存器r中的地址。
内存[rD]← rS
loadb rS,rD 10010000 S D将字节从存储器加载到寄存器rD
由寄存器rS指向。
rD← (字节)内存[rS]
storb rS,rD 10010001 S D将字节从寄存器rS存储到存储器
寄存器r中的地址。
(字节)内存[rD]← rS
表3:扩展说明
其中标识符由任意长度的字母(A-Za-z)、数字(0-9)或下划线()序列组成
第一个字符不是数字。冒号(:)必须终止标签。标签表示程序中的相应位置,并可用于跳转到代码中的那个位置。
5.指令由助记符组成,如add、loadi、push等,后面跟零或更多操作数。操作数必须与助记符用一个或多个空格分隔。多个操作数用逗号分隔。
6.如果操作数是寄存器,则其形式必须为r#,其中#的范围介于0和15之间(包括0和15)。例如,r13。
7.如果指令是立即数,那么自变量可以是标签,也可以是整数。笔记标签区分大小写。如果指定了标签,则标签后面不应该有冒号。
8.指令指示汇编程序执行特定的功能或以特定的方式表现。所有指令都以句点开头,后面跟一个关键字。有三个指令:.literal、.words和.glob,每个指令都接受一个操作数。
(a) .literal指令当前编码以null结尾的字符串或整数
程序中的位置。例如。,
mystring:
.字面意思是“你好,世界!”
myvalue:
.文字42
对后面跟有表示十进制值42的16位(1个字)值的以零结尾的字符串进行编码。在本例中,每个编码之前都有标签,因此很容易访问这些文字。也就是说,标签mystring表示字符串编码的地址(相对于程序的开始),标签myvalue表示值的地址(相对于程序的开始)。此用于
hello.xas示例。
(b) .words指令当前留出指定数量的内存单词
程序中的位置。例如。,
myspace:
.单词6
在程序的当前点分配6个字的内存。此空间未初始化为任何特定值。和以前一样,指令前面的标签表示
相对于程序开始的第一个单词的地址。这在xrt0.xas中用于
为程序堆栈留出空间。
(c) .glob指令导出指定的符号(标签)(如果在文件中定义),并且
如果指定的符号(标签)已使用但未在文件中定义,则导入该符号。例如。,
.glob foo
.glob条
...
loadi bar,r0
...
foo:
.字面意思是“你好,世界!”
声明了两个符号(标签)foo和bar。符号foo是在这个文件中声明的,所以它将
在其他文件中导出(可以访问)。使用后一个符号bar,但不使用
定义。链接此文件时,链接器在中查找符号(标签)定义
其他文件使对符号的所有引用都指向定义符号的位置。
注意:建议您将文字和所有空间分配放在程序的末尾,因此
它们不会干扰程序本身。如果你确实把文字放在程序的中间,你
将需要确保您的代码绕过这些分配。
提供了几个示例程序集文件(以.xas结尾)。您可以通过调用来组装它们
汇编程序,例如:
./xas example.xas example.xo
此调用将导致汇编程序读入example.xas文件并生成输出
文件example.xo。请随意查看汇编程序的代码。