项目概述
跟据CSAPP中对Y86-64指令集的描述和讲解,以任意技术栈自行实现一个CPU模拟器。
下载链接:PJ-handout.tar
项目要求
-
项目完成人数1~2人。其中单人完成的同学可以适当降低一些工作量的要求。多人组队的同学需要把参与项目的每个人的工作量和贡献度都写在报告中。
-
支持CSAPP中Y86-64的基本指令
-
程序输入输出满足基本格式,输出与标准答案一致
-
需要提交项目报告和项目文件
-
对技术栈不做限制,可以自由选择编程语言(python,c++,SystemVerilog等)、操作系统
-
在满足项目要求的条件下,有能力的同学可以做额外的完善,例如更好的架构设计、更丰富的指令、所学知识的综合等,即添加自己的亮点
-
进行课堂汇报(安排在期末前),包括项目设计的亮点等
分值分配
- 基础功能(80%)
- 项目展示与报告(15%)
- 项目亮点(5%)
项目内容
在项目中,你需要设计实际一个cpu模拟器,你的程序将从stdio
读取机器码.yo
文件,然后在stdout
按要求输出初始状态和每条指令执行后的CPU状态的日志。(回想bomblab的重定向) 最后的运行方式类似如下:
1 | $ ./cpu < example.yo > example.json |
模拟器输入
请以文件包给出的包含了机器码和汇编码的.yo
文件作为模拟器输入,自行编写代码处理 样例如下:
1 | 0x00a: 30f23f00000000000000 | irmovq $63, %rdx # src and dst have 63 elements |
模拟器输出
-
输出一份
json
格式文件或yaml
格式文件,可使用库或自行编写代码 -
要求在每条指令执行完毕后输出完整的寄存器信息和内存非零值(八字节对齐,按小端法解释为十进制有符号整数)。内存非零值指{(内存地址,内存值)|内存值内存地址} ,即所有非零内存值的内存地址-值键值对。
-
所有输出(含内存地址、寄存器值、内存值)均以十进制输出
-
最终完整输出样例如下,无需担心每次log内key-value的排列顺序,但要确保列表内log的顺序与程序执行顺序一致
-
json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22[
{
"PC": 0,
"REG": {
"rax": 1,
"rcx": -2,
"rdx": 3,
},
"CC": {
"ZF": 0,
"SF": 0,
"OF": 0
},
"STAT": 1,
"MEM": {
"64": 4294901760,
"72": 65535,
...
}
},
...
] -
yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16- PC: 0
REG:
rax: 1
rcx: -2
rdx: 3
...
CC:
ZF: 0
SF: 0
OF: 0
MEM:
64: 4294901760
72: 65535
...
STAT: 1
- ...
-
实现前端界面(可选)
为了方便期末pre和行CPU运行期间相关信息的展示,同学们可以选择性完成前端界面。前端页面不要求强制完成,完成不会获得额外分数,你甚至可以用命令行作为前端页面。前端界面的时间方法非常多,这里不做限制,助教可以推荐几个常用的技术思路:
桌面应用
思路是不输出此时处理器的状态,直接显示到前端
- QT库:一个非常成熟的跨系统GUI库,有C++, Python等语言的API,网上也能找到很多资料
- GTK库:另一个非常成熟且很有开源精神的GUI库,提供了C, JS, Python等语言的API The GTK Project - A free and open-source cross-platform widget toolkit
- Electron:一个用写网页的方法写GUI的库,如果你有web基础,可以考虑用这个Electron | Buildcross-platform desktop apps with JavaScript, HTML, and CSS. (electronjs.org)
- UWP, SwiftUI,Unity…
web应用
思路是后端提供API的Web接口,前端读取Web接口的数据显示
P.S. 你可以直接传上一阶段生成的json数据
- Django&Flask+HTML/JS:非常成熟的Python Web框架,熟练的话半天时间就能把整个PJ写完
- NodeJS+HTML:非常成熟的JS后端,性能据说也不错
- Go/Java/…+HTML
不知道从哪里开始?
- 阅读CSAPP,了解Y86-64指令集
- 先试着读取文件内容和解析文件内容为一条条对应指令
- 画图设计你的每条指令的执行过程,回想课上讲的cpu设计
- 先从简单的一条指令开始,逐步增加指令
- 在每条指令结束后记得保存寄存器和内存的状态(变量状态)
什么是项目亮点
项目亮点是指你在完成基本功能的基础上,自行添加的功能或者设计,我们可以给出如下几个方向的建议:
- 任何你认为你所作的巧妙独特的地方
- 更好的cpu架构设计
- 更丰富的指令集
- 更好的展示效果
- 如何结合我们之前学过的实验内容或课程内容来丰富功能
P.S. 前端页面不会算作亮点,因为这并不是主要考察目标,大家可以放心。
项目展示
项目展示将在课程最后一到两周进行,具体时间待定。希望同学们能尽早完成pj方便课上展示。展示形式为每组5分钟左右的汇报,需要同学准备相应的ppt,汇报内容可以包括但不限于:
- cpu架构设计
- cpu实机运行效果
- 项目亮点
我们的pre的分数全部由投票打分得到
提交方式
请将项目报告、项目文件打包提交,命名格式为: 姓名_学号_PJ
- 项目报告内需包含你的CPU的设计、前端的设计(如果有)、代码运行的方法。在讲清楚的情况下越短越好,以及你的意见+建议+感受(可选)。
- 项目文件内需包含你所有的代码和静态文件(如前端的图片,设计文档等)
- 请不要将.git或者二进制文件附在压缩包里,除非为项目配置编译环境十分复杂。
注意事项
- 本次项目旨在考查同学们对Y86指令集和基本处理器设计思想的掌握程度,满足项目要求即可获得大部分分数,请自行平衡好时间
- 禁止抄袭代码,鼓励自行学习相关知识丰富模拟器功能
- 小组如果中途发生变动,需要向助教说明情况
Y86-64实验报告
我的亮点就是采用了更加丰富的指令集,以及利用了LRU实现的cache来优化cpu的运行;在接下来的报告中我会进行讲解。
前端
采用了html
加上JavaScript
写了我的前端,在运行过程中,统计了我缓存的hit以及miss、eviction来让查找自己是哪一步导致了eviction,在前端可以通过按键,可以进行上一个PC以及下一个PC之间的切换,以及memory的查找。
memory
初始化
最开始进行memory的初始化,把yo文件中所有的指令全部按照地址信息读取进内存,定义了一个大小为一千的map<int ,string>
进行索引;对于每一个字节,即两个十六进制数字进行储存。
void CPUSimulator::initMemory()
指令读取
void CPUSimulator::run()
最开始从0位置根据PC的值进行读取指令,根据指令的instuction_code
来进行判断读取几个byte,然后将指令读取进我的缓存中,此时地址将会被解析为三部分,来进行缓存中的定位以及hit、miss、eviction的判定,也就是cache_lab
中学的知识。
对于每一条指令,将会从小端法十六进制补码被解读为大端法十进制long long格式被存储在我的缓存里,同时寄存器里也是这种形式,而内存中为了更加方便地进行instuction_Code
的解析,我选择使用小端法十六进制补码进行储存以及处理。
数据存储
// 将数据写入缓存,如果缓存未命中,先从内存中读取数据
void write(long long address, long long data);
//从内存中读取信息
std::string read_mem(long long b);
// 从缓存中读取数据,如果缓存中没有,从内存中读取
long long read(long long address);
//信息写入内存
void write_mem(std::string a,long long b);
当数据被读取进来的时候,数据存储在我的缓存中,并且当数据进行更改的时候,也是在缓存中寻找对应位置进行更改;当需要在进行内存以及寄存器之间的信息交换的时候,信息从缓存读取到我的寄存器之中。当且仅当,缓存发生了eviction
,需要移除一个或多个缓存项以腾出空间给新的数据的时候,缓存中最少次数被使用的一项会被更新到memory
里面。
同理,在读取一个信息的时候,先看缓存中有没有这项数据,没有的话,先将数据从memory读取进我的缓存中,再对数据进行操作。因为cache和内存之间是异步操作,内存不会被及时更新;所以这里我每次更新缓存的同时把内存也更新了测试样例就过不了了,所以每次cache更新的时候我都把内存更新掉了。
指令的处理
void CPUSimulator::executeInstruction(const std::string& instruction)
当指令通过instuction_code
被解析之后,我们通过调用各种函数来进行相关操作。其中再调用函数后需要更新CPU的PC值来进行判断下一步读取什么指令。
其中又三个点需要注意
opq
操作时注意更新状态码,方便需要条件判断的函数比如jl
、jne
等等,来判断是否执行。pushq
需要 如果注意调用rsp
寄存器,需要先把寄存器信息存起来- 在进行
pushq
的时候防止地址越界,如果越界需要更新状态码,终止程序的运行
对于新的指令集,然后我想起了自己在写boom_lab
的时候,经常rax
,eax
到处倒腾,非常难受,我就写了一套直接对rax
,rdi
之类地址进行高低位操作的指令;还有一些直接对寄存器的值与某个值进行比较,避免了寄存器之间数值的相互倒腾;最后增添了乘除指令。
//两个寄存器的高位相加,存放在第三个寄存器里
void highaddq(Register source1,Register source2);
//两个寄存器的低位相加,存放在第三个寄存器里
void lowaddq(Register source1,Register source2);
//两个寄存器的高位相减,存放在第三个寄存器里
void highsubq(Register source1,Register source2);
//两个寄存器的低位相减,存放在第三个寄存器里
void lowsub(Register source1,Register source2);
//如果寄存器中的值大于某个值,就跳转
void highjump(Register source,long long V, long long D);
//如果寄存器中的值不大于某个值,就跳转
void nothighjump(Register source,long long V, long long D);
//如果寄存器中的值小于某个值,就跳转
void lowjump(Register source,long long V, long long D);
//如果寄存器中的值不小于某个值,就跳转
void notlowjump(Register source,long long V, long long D);
//两个寄存器的值相乘,结果存在第二个寄存器里
void mulq(Register source1,Register source2);
//两个寄存器的值相除,结果存放在第二个寄存器里
void divq(Register source1,Register source2);
代码引用了nlohmann::json,运行需要的json.hpp文件我一并放入文件夹了
cpu.h
#include <iostream>
#include <string>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include <json.hpp>
#include <bitset>
#include <vector>
#include <sstream>
#include <iomanip>
#include <list>
using json = nlohmann::json;
//十六进制转十进制
std::string hexToBinary(const std::string& hexString) ;
// 将小端法表示的十六进制string转换为大端法表示的十进制long long
long long convertLittleEndianHexToDecimal(const std::string &hexString) ;
// 将大端法表示的十进制long long转换为小端法表示的十六进制字符串
std::string convertDecimalToLittleEndianHex(long long decimalValue) ;
int hexCharToInt(char hexChar) ;
//得到一个地址的高地址部分
std::string getLowAddress(const std::string& input) ;
// 定义缓存行的大小和缓存的大小
const int CACHE_LINE_SIZE = 2;
const long long CACHE_SIZE = 16;
// 缓存行的数据结构
struct CacheLine {
bool valid;
long long tag;
std::vector<long long> data;
CacheLine() : valid(false), tag(-1), data(CACHE_LINE_SIZE, 0) {}
};
// LRU缓存替换策略
class LRUCache {
private:
std::unordered_map<long long, std::list<long long>::iterator> indexMap;
std::list<long long> lruList;
long long capacity;
public:
LRUCache(long long capacity) : capacity(capacity) {}
long long index_will_delet();
// 添加一个索引到LRU列表中
void addIndex(long long index) ;
// 获取最近最少使用的索引
long long getLRUIndex() const ;
};
enum class Register {
RAX, RCX, RDX, RBX, RSP, RBP,RSI,RDI,
R8, R9, R10, R11, R12, R13, R14,F,
REG_COUNT
};
//利用index索引寄存器
Register getRegisterAtIndex( int index) ;
// 辅助函数,将Register转换为字符串
std::string getRegisterName(Register reg) ;
class CacheSimulator;
// CPU状态结构体
struct CPUState {
int64_t registers[static_cast<int>(Register::REG_COUNT)] = {0};
long long PC = 0;
std::map<int, std::string> memory={};
int ZF = 1;
int SF = 0;
int OF = 0;
int STATE = 1;
int hits = 0;
int misses = 0;
int eviction = 0;
// 将CPU状态转换为JSON对象
json toJSON() const;
json toJSON1() const;
};
// 模拟器类
class CPUSimulator {
public:
CPUSimulator(long long capacity): lruCache(capacity) {
for (int i = 0; i < static_cast<int>(Register::REG_COUNT); ++i) {
CP.registers[i] = 0;
}
}
void initMemory();
//信息写入内存
void write_mem(std::string a,long long b);
//从内存中读取信息
std::string read_mem(long long b);
void run();
// 从缓存中读取数据,如果缓存中没有,从内存中读取
long long read(long long address);
// 将数据写入缓存,如果缓存未命中,先从内存中读取数据
void write(long long address, long long data);
// 获取缓存命中率
double getHitRate() const ;
private:
CPUState CP;
//缓存
std::unordered_map<long long, CacheLine> cache;
//缓存的LRU逻辑实现
LRUCache lruCache;
// 更新缓存行
void updateCache(long long index, long long tag, long long data,long long address);
// 执行一条指令
void executeInstruction(const std::string& instruction) ;
void rrmovq(Register source, Register destination);
void comvle(Register source, Register destination);
void cmovl(Register source, Register destination);
void cmove(Register source, Register destination);
void cmovne(Register source, Register destination);
void cmovge(Register source, Register destination);
void cmovg(Register source, Register destination);
void irmovq(Register destination, long long V);
void rmmovq(Register source, Register destination, long long D);
void mrmovq(Register destination, Register source, long long D);
void addq(Register source, Register destination);
void subq(Register source, Register destination);
void andq(Register source, Register destination);
void xorq(Register source, Register destination);
void jle(long long Dest);
void jl(long long Dest);
void je(long long Dest);
void jne(long long Dest);
void jge(long long Dest);
void jg(long long Dest);
void call(long long Dest, int n);
void ret();
void pushq(Register source);
void popq(Register destination);
//两个寄存器的高位相加,存放在第三个寄存器里
void highaddq(Register source1,Register source2);
//两个寄存器的低位相加,存放在第三个寄存器里
void lowaddq(Register source1,Register source2);
//两个寄存器的高位相减,存放在第三个寄存器里
void highsubq(Register source1,Register source2);
//两个寄存器的低位相减,存放在第三个寄存器里
void lowsub(Register source1,Register source2);
//如果寄存器中的值大于某个值,就跳转
void highjump(Register source,long long V, long long D);
//如果寄存器中的值不大于某个值,就跳转
void nothighjump(Register source,long long V, long long D);
//如果寄存器中的值小于某个值,就跳转
void lowjump(Register source,long long V, long long D);
//如果寄存器中的值不小于某个值,就跳转
void notlowjump(Register source,long long V, long long D);
//两个寄存器的值相乘,结果存在第二个寄存器里
void mulq(Register source1,Register source2);
//两个寄存器的值相除,结果存放在第二个寄存器里
void divq(Register source1,Register source2);
};
CPU.cpp
#include "CPU.h"
using json = nlohmann::json;
//十六进制转十进制
std::string hexToBinary(const std::string& hexString) {
std::string binaryString;
for (char c : hexString) {
int value;
if (isdigit(c)) {
value = c - '0';
} else {
value = toupper(c) - 'A' + 10;
}
// 将每个十六进制数字映射为四位二进制
for (int i = 3; i >= 0; --i) {
int bit = (value >> i) & 1;
binaryString += std::to_string(bit);
}
}
return binaryString;
}
// 将小端法表示的十六进制string转换为大端法表示的十进制long long
long long convertLittleEndianHexToDecimal(const std::string &hexString) {
// 检查字符串长度是否为偶数
if (hexString.length() % 2 != 0) {
std::cerr << "错误:十六进制字符串长度必须为偶数" << std::endl;
return 0;
}
// 反转每两个字符的顺序
std::string reversedHexString;
for (int i = hexString.length() - 2; i >= 0; i -= 2) {
reversedHexString += hexString.substr(i, 2);
}
reversedHexString = hexToBinary(reversedHexString);
// 使用 std::bitset 构造函数将二进制字符串转换为 bitset
std::bitset<64> bits(reversedHexString);
long long result =0;
for(int i = 0;i < 64;i++){
if(i == 0){
result = -(reversedHexString[i]-'0')*(1ULL << 63);
}else{
result += (reversedHexString[i] - '0')*(1ULL <<(63 - i));
}
}
return result;
}
// 将大端法表示的十进制long long转换为小端法表示的十六进制字符串
std::string convertDecimalToLittleEndianHex(long long decimalValue) {
// 使用字符串流进行十六进制转换
std::ostringstream oss;
oss << std::hex << decimalValue;
std::string hexString = oss.str();
// 如果字符串长度为奇数,前面补零
if (hexString.length() % 2 != 0) {
hexString = "0" + hexString;
}
// 反转每两个字符的顺序
std::string littleEndianHexString;
for (int i = hexString.length() - 2; i >= 0; i -= 2) {
littleEndianHexString += hexString.substr(i, 2);
}
return littleEndianHexString;
}
//得到一个地址的低地址部分
std::string getLowAddress(const std::string& input) {
// 确保输入字符串长度为八位
if (input.length() != 8) {
std::cerr << "Error: 输入字符串长度必须为八位" << std::endl;
return "";
}
// 取前四位
std::string output = input.substr(0, 4);
// 后四位变成'0000'
output += "0000";
return output;
}
//得到一个地址的高地址部分
std::string getHighAddress(const std::string& input) {
// 确保输入字符串长度为八位
if (input.length() != 8) {
std::cerr << "Error: 输入字符串长度必须为八位" << std::endl;
return "";
}
// 取后四位
std::string output = input.substr(4, 4);
// 前四位变成'0000'
output += "0000" ;
return output;
}
//得到要驱逐的cache索引
long long LRUCache::index_will_delet(){
long long removedIndex = -1;
if(lruList.size() >= capacity) {//满了
removedIndex = lruList.back();}
return removedIndex;
}
//更新LRU
void LRUCache::addIndex(long long index) {
if (indexMap.find(index) != indexMap.end()) {//更新位置
lruList.erase(indexMap[index]);
} else if (lruList.size() >= capacity) {//满了
long long removedIndex = lruList.back();
lruList.pop_back();
indexMap.erase(removedIndex);
}
lruList.push_front(index);
indexMap[index] = lruList.begin();
}
// 获取最近最少使用的索引
long long LRUCache::getLRUIndex() const {
return lruList.back();
}
std::string getRegisterName(Register reg) {
switch (reg) {
case Register::RAX: return "rax";
case Register::RBX: return "rbx";
case Register::RCX: return "rcx";
case Register::RDX: return "rdx";
case Register::RSI: return "rsi";
case Register::RDI: return "rdi";
case Register::RSP: return "rsp";
case Register::RBP: return "rbp";
case Register::R8: return "r8";
case Register::R9: return "r9";
case Register::R10: return "r10";
case Register::R11: return "r11";
case Register::R12: return "r12";
case Register::R13: return "r13";
case Register::R14: return "r14";
default: return "UNKNOWN";
}
}
//十六进制字符转十进制数字
int hexCharToInt(char hexChar) {
if (std::isxdigit(hexChar)) {
if (std::isdigit(hexChar)) {
return hexChar - '0';
} else {
return std::toupper(hexChar) - 'A' + 10;
}
} else {
// 处理非法字符的情况,这里简单返回 -1,你可以根据需求改变这部分的逻辑
return -1;
}
}
//通过数字索引访问寄存器
Register getRegisterAtIndex( int index) {
if (index >= 0 && index < static_cast<int>(Register::REG_COUNT)) {
return static_cast<Register>(index);
}else{
return static_cast<Register>(0);;
}
}
json CPUState::toJSON() const {
json j;
j["PC"] = PC;
// 寄存器状态
j["REG"] = {};
for (int i = 0; i < static_cast<int>(Register::REG_COUNT)-1; ++i) {
j["REG"][getRegisterName(static_cast<Register>(i))] = registers[i];
}
j["STAT"] = STATE;
// CC状态
j["CC"] = {
{"ZF", ZF},
{"SF", SF},
{"OF", OF}
};
j["MEM"] = nlohmann::json::object();
int location = 0;
std::string s ={};
long long out = 0;
int count = 0;
for (const auto& entry : memory) {
count++;
long long address = entry.first;
const std::string& value = entry.second;
s += value;
if(count == 8){
count =0;
out = convertLittleEndianHexToDecimal(s);
if(out != 0){
std::stringstream ss;
ss << std::dec << entry.first -7;
j["MEM"][ss.str()] = out;
s={};
}
}
}
return j;
}
json CPUState::toJSON1() const {
json j;
j["PC"] = PC;
// 寄存器状态
j["REG"] = {};
for (int i = 0; i < static_cast<int>(Register::REG_COUNT)-1; ++i) {
j["REG"][getRegisterName(static_cast<Register>(i))] = registers[i];
}
j["STAT"] = STATE;
// CC状态
j["CC"] = {
{"ZF", ZF},
{"SF", SF},
{"OF", OF}
};
// Cache状态
j["Cache"] = {
{"hit",hits},
{"miss",misses},
{"eviction", eviction}
};
j["MEM"] = nlohmann::json::object();
int location = 0;
std::string s ={};
long long out = 0;
int count = 0;
for (const auto& entry : memory) {
count++;
long long address = entry.first;
const std::string& value = entry.second;
s += value;
if(count == 8){
count =0;
out = convertLittleEndianHexToDecimal(s);
if(out != 0){
std::stringstream ss;
ss << std::dec << entry.first -7;
j["MEM"][ss.str()] = out;
s={};
}
}
}
return j;
}
void CPUSimulator::initMemory() {
const int largeSize = 1000;
for (int i = 0; i < largeSize; i++) {
CP.memory[i] = "00";
}
std::string str;
while (getline(std::cin, str)) {
// while (getline(infile, str)) {
if (str[5] == ':' && str.find_first_of(' ') == 6 && str[7] != ' ') {
const std::string temp = str.substr(0, str.find_first_of(':'));
if (temp.empty())
break;
long long index = std::stoll(temp, nullptr, 16);
std::string st;
int firstSpacePos = str.find_first_of(' '); // 找第一个空格的位置
int secondSpacePos = str.find(' ', firstSpacePos + 1); // 从第一个空格后开始找第二个空格的位置
st=str.substr(7,secondSpacePos-7);
for(int i = 0;i < (secondSpacePos-7)/2;i++){
CP.memory[index +i] = st.substr((i)*2,2);
}
}
}
}
void CPUSimulator::updateCache(long long index, long long tag, long long data,long long address) {
CacheLine& line = cache[index];
line.valid = true;
line.tag = tag;
line.data[address % CACHE_LINE_SIZE] = data;
lruCache.addIndex(index);
}
// 从缓存中读取数据,如果缓存中没有,从内存中读取
long long CPUSimulator::read(long long address) {
long long index = address % CACHE_SIZE;
long long tag = address / CACHE_SIZE;
if (cache.find(index) != cache.end() && cache[index].valid && cache[index].tag == tag) {
// 命中
CP.hits++;
lruCache.addIndex(index);
return cache[index].data[address % CACHE_LINE_SIZE];
} else {
// 未命中,从内存读取数据到缓存
CP.misses++;
if(lruCache.index_will_delet() != -1){
// long long data = convertLittleEndianHexToDecimal(cpu->read_mem(address));//写回策略中才会写这个操作,但是为了通过测试点更新一下内存
CP.eviction ++;
}
long long data = convertLittleEndianHexToDecimal(read_mem(address));
updateCache(index, tag, data,address);
return data;
}
}
void CPUSimulator::write(long long address, long long data) {
long long index = address % CACHE_SIZE;
long long tag = address / CACHE_SIZE;
if (cache.find(index) != cache.end() && cache[index].valid && cache[index].tag == tag) {
// 命中,直接写入缓存
CP.hits++;
lruCache.addIndex(index);
cache[index].data[address % CACHE_LINE_SIZE] = data;
write_mem(convertDecimalToLittleEndianHex(data),address);//写回策略中不会写这个操作,但是为了通过测试点更新一下内存
} else {
// 未命中,从内存读取数据到缓存再写入
CP.misses++;
write_mem(convertDecimalToLittleEndianHex(data),address);
updateCache(index, tag, data,address);
}
}
double CPUSimulator::getHitRate() const {
return static_cast<double>(CP.hits) / (CP.hits + CP.misses);
}
void CPUSimulator::write_mem(std::string a,long long b){
for(int i = 0;i < a.length()/2;i++)
CP.memory[b+i] = a.substr(i*2,2);
for(int i = 0;i < 8 -a.length()/2;i++){
CP.memory[b+i+a.length()/2] ="00";
}
}
std::string CPUSimulator::read_mem(long long b){
std::string s ={};
for(int i = 0;i < 8;i++){
s += CP.memory[b+i];
}
return s;
}
// void CPUSimulator::run(const std::string& filePath) {
void CPUSimulator::run(){
initMemory();
// initMemory(filePath);
std::string line = {};
int byte = 0;
json log;
json log1;
while (CP.STATE == 1){
line = {};
switch(CP.memory[CP.PC][0]){
case '0':
case '1':
case '9':
byte = 1;
break;
case '2':
case '6':
case 'a':
case 'b':
byte = 2;
break;
case '7':
case '8':
byte = 9;
break;
case '3':
case '4':
case '5':
byte = 10;
break;
case 'c':
byte = 18;
break;
}
for(int i = 0;i < byte;i++){
line += CP.memory[CP.PC +i];
}
// line = CP.memory[CP.PC];
executeInstruction(line);
log.push_back(CP.toJSON());
log1.push_back(CP.toJSON1());
}
std::ofstream outputFile("result.json");
if (outputFile.is_open()) {
// Write the JSON content to the file with indentation of 4 spaces
outputFile << log1.dump(4) << std::endl;
}
// Close the file
outputFile.close();
// 输出JSON日志到标准输出
std::cout << log.dump(4) << std::endl;
}
void CPUSimulator::executeInstruction(const std::string& instruction) {
char Instruction_Code = instruction[0];
char Function_Code = instruction[1];
Register Register_Code_1;
Register Register_Code_2;
std::string Offset;
long long V;
long long D;
long long Dest;
Register_Code_1 = getRegisterAtIndex(hexCharToInt(instruction[2]));
Register_Code_2 = getRegisterAtIndex(hexCharToInt(instruction[3]));
switch(Instruction_Code){
case('0'):
CP.STATE = 2;
return;
case('1'):
break;
case('2'):
switch (Function_Code) {
case '0':
rrmovq(Register_Code_1,Register_Code_2);
break;
case '1':
comvle(Register_Code_1, Register_Code_2);
break;
case '2':
cmovl(Register_Code_1, Register_Code_2);
break;
case '3':
cmove(Register_Code_1, Register_Code_2);
break;
case '4':
cmovne(Register_Code_1, Register_Code_2);
break;
case '5':
cmovge(Register_Code_1, Register_Code_2);
break;
case '6':
cmovg(Register_Code_1, Register_Code_2);
break;
}
break;
case('3'):
V = convertLittleEndianHexToDecimal(instruction.substr(4));
irmovq(Register_Code_2,V);
break;
case('4'):
D = convertLittleEndianHexToDecimal(instruction.substr(4));
rmmovq(Register_Code_1,Register_Code_2,D);
break;
case('5'):
D = convertLittleEndianHexToDecimal(instruction.substr(4));
mrmovq(Register_Code_1,Register_Code_2,D);
break;
case('6'):
switch(Function_Code){
case('0'):
addq(Register_Code_1,Register_Code_2);
break;
case('1'):
subq(Register_Code_1,Register_Code_2);
break;
case('2'):
andq(Register_Code_1,Register_Code_2);
break;
case('3'):
xorq(Register_Code_1,Register_Code_2);
break;
}
break;
case('7'):
Dest = convertLittleEndianHexToDecimal(instruction.substr(2));
switch(Function_Code){
case('0'):
CP.PC = Dest;
return;
case('1'):
if ((CP.SF^CP.OF)|CP.ZF) {
CP.PC = Dest;
}
else{
CP.PC += instruction.length()/2;
}
return;
case('2'):
if ((CP.SF^CP.OF)) {
CP.PC = Dest;
}
else{
CP.PC += instruction.length()/2;
}
return;
case('3'):
if ((CP.ZF)) {
CP.PC = Dest;
}
else{
CP.PC += instruction.length()/2;
}
return;
case('4'):
if ((!CP.ZF)) {
CP.PC = Dest;
}
else{
CP.PC += instruction.length()/2;
}
return;
case('5'):
if ((!(CP.SF^CP.OF))) {
CP.PC = Dest;
}
else{
CP.PC += instruction.length()/2;
}
return;
case('6'):
if ((!(CP.SF^CP.OF))&(!CP.ZF)) {
CP.PC = Dest;
}
else{
CP.PC += instruction.length()/2;
}
return;
}
case('8'):
Dest = convertLittleEndianHexToDecimal(instruction.substr(2));
call(Dest,instruction.length()/2);
return;
case('9'):
ret();
return;
case 'a':
pushq(Register_Code_1);
if(CP.STATE == 1){
CP.PC += instruction.length()/2;
}else{
return;
}
return;
case 'b':
popq(Register_Code_1);
break;
}
CP.PC += instruction.length()/2;
}
void CPUSimulator::rrmovq(Register source, Register destination) {
CP.registers[static_cast<int>(destination)] = CP.registers[static_cast<int>(source)];
}
void CPUSimulator::comvle(Register source, Register destination) {
if ((CP.SF^CP.OF)|CP.ZF) {
CP.registers[static_cast<int>(destination)] = CP.registers[static_cast<int>(source)];
}
}
void CPUSimulator::cmovl(Register source, Register destination) {
if ((CP.SF^CP.OF)) {
CP.registers[static_cast<int>(destination)] = CP.registers[static_cast<int>(source)];
}
}
void CPUSimulator::cmove(Register source, Register destination) {
if ((CP.ZF)) {
CP.registers[static_cast<int>(destination)] = CP.registers[static_cast<int>(source)];
}
}
void CPUSimulator::cmovne(Register source, Register destination) {
if ((!CP.ZF)) {
CP.registers[static_cast<int>(destination)] = CP.registers[static_cast<int>(source)];
}
}
void CPUSimulator::cmovge(Register source, Register destination) {
if ((!(CP.SF^CP.OF))) {
CP.registers[static_cast<int>(destination)] = CP.registers[static_cast<int>(source)];
}
}
void CPUSimulator::cmovg(Register source, Register destination) {
if ((!(CP.SF^CP.OF))&(!CP.ZF)) {
CP.registers[static_cast<int>(destination)] = CP.registers[static_cast<int>(source)];
}
}
void CPUSimulator::irmovq(Register destination, long long V) {
CP.registers[static_cast<int>(destination)] = V;
}
void CPUSimulator::rmmovq(Register source, Register destination, long long D) {
long long temp;
temp = CP.registers[static_cast<int>(source)];
write(CP.registers[static_cast<int>(destination)] + D,CP.registers[static_cast<int>(source)]);
}
void CPUSimulator::mrmovq(Register destination, Register source, long long D) {
CP.registers[static_cast<int>(destination)] = read((CP.registers[static_cast<int>(source)] + D));
}
void CPUSimulator::addq(Register source, Register destination){
long long x = CP.registers[static_cast<int>(source)];
long long y = CP.registers[static_cast<int>(destination)];
long long t = x + y;
CP.OF = (x <0 == y<0)&& (t < 0 != x< 0);
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.registers[static_cast<int>(destination)] += CP.registers[static_cast<int>(source)];
}
void CPUSimulator::subq(Register source, Register destination){
long long x = CP.registers[static_cast<int>(source)];
long long y = CP.registers[static_cast<int>(destination)];
long long t = y -x;
CP.OF = (t <y != x>0);
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.registers[static_cast<int>(destination)] -= CP.registers[static_cast<int>(source)];
}
void CPUSimulator::andq(Register source, Register destination){
long long x = CP.registers[static_cast<int>(source)];
long long y = CP.registers[static_cast<int>(destination)];
long long t = x&y;
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.OF = 0;
CP.registers[static_cast<int>(destination)] =CP.registers[static_cast<int>(destination)] & CP.registers[static_cast<int>(source)];
}
void CPUSimulator::xorq(Register source, Register destination){
long long x = CP.registers[static_cast<int>(source)];
long long y = CP.registers[static_cast<int>(destination)];
long long t = x^y;
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.OF = 0;
CP.registers[static_cast<int>(destination)] =CP.registers[static_cast<int>(destination)] ^ CP.registers[static_cast<int>(source)];
}
void CPUSimulator::jle(long long Dest) {
if ((CP.SF^CP.OF)|CP.ZF) {
CP.PC = Dest;
}
else{
}
}
void CPUSimulator::jl(long long Dest) {
if ((CP.SF^CP.OF)) {
CP.PC = Dest;
}
}
void CPUSimulator::je(long long Dest) {
if ((CP.ZF)) {
CP.PC = Dest;
}
}
void CPUSimulator::jne(long long Dest) {
if ((!CP.ZF)) {
CP.PC = Dest;
}
}
void CPUSimulator::jge(long long Dest) {
if ((!(CP.SF^CP.OF))) {
CP.PC = Dest;
}
}
void CPUSimulator::jg(long long Dest) {
if ((!(CP.SF^CP.OF))&(!CP.ZF)) {
CP.PC = Dest;
}
}
void CPUSimulator::call(long long Dest,int n){
long long temp;
CP.registers[static_cast<int>(getRegisterAtIndex(4))] -= 8;
write(CP.registers[static_cast<int>(getRegisterAtIndex(4))],CP.PC+n);
CP.PC = Dest;
}
void CPUSimulator::ret(){
CP.registers[static_cast<int>(getRegisterAtIndex(4))] += 8;
CP.PC = read(CP.registers[static_cast<int>(getRegisterAtIndex(4))]-8);
}
void CPUSimulator::pushq(Register source){
long long temp;
temp = CP.registers[static_cast<int>(source)];
CP.registers[static_cast<int>(getRegisterAtIndex(4))] -= 8;
if(CP.registers[static_cast<int>(getRegisterAtIndex(4))]<8){
CP.STATE = 3;
return;
}
write(CP.registers[static_cast<int>(getRegisterAtIndex(4))],temp);
}
void CPUSimulator::popq(Register destination){
CP.registers[static_cast<int>(getRegisterAtIndex(4))] += 8;
CP.registers[static_cast<int>(destination)] = read(CP.registers[static_cast<int>(getRegisterAtIndex(4))] -8);
}
void CPUSimulator::highaddq(Register source1,Register source2){
std::string temp1 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source1)]);
std::string temp2 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source2)]);
long long x = convertLittleEndianHexToDecimal(getHighAddress(temp1));
long long y = convertLittleEndianHexToDecimal(getHighAddress(temp2));
long long t = x + y;
CP.OF = (x <0 == y<0)&& (t < 0 != x< 0);
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.registers[static_cast<int>(source2)] = t;
}
void CPUSimulator::lowaddq(Register source1,Register source2){
std::string temp1 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source1)]);
std::string temp2 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source2)]);
long long x = convertLittleEndianHexToDecimal(getLowAddress(temp1));
long long y = convertLittleEndianHexToDecimal(getLowAddress(temp2));
long long t = x + y;
CP.OF = (x <0 == y<0)&& (t < 0 != x< 0);
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.registers[static_cast<int>(source2)] = t;
}
void CPUSimulator::highsubq(Register source1,Register source2){
std::string temp1 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source1)]);
std::string temp2 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source2)]);
long long x = convertLittleEndianHexToDecimal(getHighAddress(temp1));
long long y = convertLittleEndianHexToDecimal(getHighAddress(temp2));
long long t = y -x;
CP.OF = (t <y != x>0);
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.registers[static_cast<int>(source2)] = t;
}
void CPUSimulator::lowsub(Register source1,Register source2){
std::string temp1 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source1)]);
std::string temp2 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source2)]);
long long x = convertLittleEndianHexToDecimal(getLowAddress(temp1));
long long y = convertLittleEndianHexToDecimal(getLowAddress(temp2));
long long t = y -x;
CP.OF = (t <y != x>0);
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.registers[static_cast<int>(source2)] = t;
}
//如果寄存器中的值大于某个值,就跳转
void CPUSimulator::highjump(Register source,long long V, long long D){
if(CP.registers[static_cast<int>(source)] > V){
CP.PC = D;
}
}
//如果寄存器中的值小于某个值,就跳转
void CPUSimulator::lowjump(Register source,long long V, long long D){
if(CP.registers[static_cast<int>(source)] < V){
CP.PC = D;
}
}
//如果寄存器中的值不大于某个值,就跳转
void CPUSimulator::nothighjump(Register source,long long V, long long D){
if(!CP.registers[static_cast<int>(source)] > V){
CP.PC = D;
}
}
//如果寄存器中的值不小于某个值,就跳转
void CPUSimulator::notlowjump(Register source,long long V, long long D){
if(!CP.registers[static_cast<int>(source)] < V){
CP.PC = D;
}
}
//两个寄存器的值相乘,结果存在第二个寄存器里
void CPUSimulator::mulq(Register source1,Register source2){
std::string temp1 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source1)]);
std::string temp2 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source2)]);
long long x = convertLittleEndianHexToDecimal(getLowAddress(temp1));
long long y = convertLittleEndianHexToDecimal(getLowAddress(temp2));
long long t = y*x;
CP.OF = (t < y && x > 0);
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.registers[static_cast<int>(source2)] = t;
}
//两个寄存器的值相除,结果存放在第二个寄存器里
void CPUSimulator::divq(Register source1,Register source2){
std::string temp1 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source1)]);
std::string temp2 = convertDecimalToLittleEndianHex( CP.registers[static_cast<int>(source2)]);
long long x = convertLittleEndianHexToDecimal(getLowAddress(temp1));
long long y = convertLittleEndianHexToDecimal(getLowAddress(temp2));
long long t = 0;
if(x != 0){
t = y/x;
}else{
CP.STATE = 3;
}
CP.OF = (t <y != x>0);
CP.ZF = (t == 0);
CP.SF = (t < 0);
CP.registers[static_cast<int>(source2)] = t;
}
int main() {
CPUSimulator simulator(1);
simulator.run();
return 0;
}