简体   繁体   English

用Java / C / C ++创建MIPS机器

[英]Creating a MIPS machine in Java/C/C++

Hey everyone I'm in an Assembly Language and Computer Organization class. 嘿大家我在汇编语言和计算机组织课上。 Recently I got an assignment that requires that I create a program that emulates a MIPS machine in Java, C, or C++. 最近我得到了一项任务,要求我创建一个emulates a MIPS machine in Java, C, or C++.

The program reads hex from a ASM file and stores the lines in an array. 该程序从ASM文件中读取十六进制并将这些行存储在一个数组中。 Then it is supposed to emulate the MIPS machine. 然后它应该模仿MIPS机器。

I've been searching all around but have no idea how to even start. 我一直在寻找,但不知道如何开始。 Does anyone have some ideas or even pseudo-code to get me on the right path? 有没有人有一些想法甚至伪代码让我走上正确的道路?

An example of the file to be read: 要读取的文件的示例:

24080019
2409001e
240a0023
01094020
010a4020
00082021

If anyone can just get me started down the right path that would be fantastic, thanks! 如果有人能让我开始走上正确的道路,那将是非常棒的,谢谢!

The first thing you need to do is to learn to decypher those instructions. 你需要做的第一件事就是学会解释这些指示。

It appears they're 8 hex digits. 它们看起来是8个十六进制数字。 This is nice, because MIPS instructions are 32 bits long. 这很好,因为MIPS指令长32位。 (a hex digit is 4 bits, of course.) (当然,十六进制数字是4位。)

So each line in the file cooresponds to one instruction. 因此文件中的每一行都与一条指令相对应。

Look at the format of instructions here: http://www.d.umn.edu/~gshute/spimsal/talref.html 请查看说明格式: http//www.d.umn.edu/~gshute/spimsal/talref.html

You need to keep track of a few registers inside the machine. 您需要跟踪机器内的一些寄存器。 (You know what the MIPS registers are, right?) (你知道MIPS寄存器是什么,对吧?)

Then you need to determine what the operations do, and what registers they effect. 然后,您需要确定操作的作用以及它们影响的寄存器。

SPIM might be a good place to start looking at how such a program might be constructed. SPIM可能是一个开始研究如何构建这样一个程序的好地方。 It's a MIPS32 simulator. 这是一个MIPS32模拟器。

So basically your program will be a loop that reads one instruction (one line in the input file)*, and alters its variables in the same fashion the instruction would alter its registers. 所以基本上你的程序将是一个读取一条指令(输入文件中的一行)*的循环,并以与指令改变其寄存器相同的方式改变其变量。

This would be a big "switch case" on the opcode, and for each case you handle the particular instruction. 这将是操作码上的一个很大的“切换案例”,并且对于每种情况,您都可以处理特定的指令。

The variables of your program would basically be your registers, and you will also have to simulate a memory somehow (you should be able to allocate your whole memory once and for all as a big chunk of memory that you will handle) and to handle the translation from the MIPS addresses to addresses in your chunk of memory. 你的程序的变量基本上是你的寄存器,你还必须以某种方式模拟一个内存(你应该能够一次性地分配你的整个内存作为你将处理的一大块内存)并处理从MIPS地址转换到您的内存块中的地址。

Then dealing with the opcodes is basically a question of altering your registers and your memory. 然后处理操作码基本上是改变寄存器和内存的问题。 You might be able to benefit from your language to do some operations (like sum, product, ...) but you'll most certainly have to handle a bit more than this: set the flags in your status register for instance. 你可能可以从你的语言中受益来做一些操作(比如总和,产品......)但你肯定要处理的不止这些:例如在状态寄存器中设置标志。

I'm not familiar with MIPS instruction set, but you might also have to do some address translation depending on the available addressing modes. 我不熟悉MIPS指令集,但您可能还需要根据可用的寻址模式进行一些地址转换。

*: Actually, it should be more clever than just reading the input file line by line: you should load your program in that "memory" array first, and handle a program counter that will start at the first instruction and be incremented after handling the current instruction. *:实际上,它应该比直接读取输入文件更聪明:你应该首先在“内存”数组中加载你的程序,并处理一个程序计数器,它将从第一条指令开始,并在处理完第一条指令后递增现行指示。 Sometimes, the flow might move the PC back. 有时,流程可能会将PC移回。 Ideally you'd also want to make this part of the "memory" unmodifiable, but it's not what you want to focus on first. 理想情况下,你也想让这部分“记忆”不可修改,但这不是你想要首先关注的。

I hope I didn't say anything irrelevant to MIPS. 我希望我没有说任何与MIPS无关的内容。

So in terms of structure, this pseudo-code gives an idea: 所以在结构方面,这个伪代码给出了一个想法:

set all your register variables to their default value
allocate memory for the "memory"
load your program in the "memory"
for (initialize PC ; ??? ; PC"++")
{
    read the "memory" at the address in PC -> opcode
    switch (opcode)
    {
        case op1:
            handle_op1(); // modify registers and/or "memory", set status register
        break;
        [...]
    }
}

I did something like this many years ago as a coursework. 很多年前我做了类似这样的课程作业。 Unfortunately, sources didn't survive, so I'll try to put together some general ideas which I remember. 不幸的是,消息来源没有生存,所以我会尝试将我记得的一些一般性想法放在一起。 I hope it will help you with your project. 我希望它能帮助你完成你的项目。

First and most simple - register block. 首先也是最简单的 - 寄存器块。 I did it as a plain structure. 我把它做成一个简单的结构。 For flag register(s) I created few set/clear functions for the sake of convenience. 对于标志寄存器,为方便起见,我创建了一些设置/清除功能。

Second and probably requiring most effort - instruction decoding. 第二,可能需要付出最多努力 - 指令解码。 I had a handbook on my target CPU instruction set which explained meaning of different bits in the binary code. 我在目标CPU指令集上有一本手册,它解释了二进制代码中不同位的含义。 There are normally few basic classes of the instructions: arithmetic/boolean operations, control flow instructions, memory/register copying/exchange and probably a couple of others. 通常很少有基本类的指令:算术/布尔操作,控制流指令,存储器/寄存器复制/交换以及可能的其他几个。 Another aspect is how the operands are addressed by an instruction. 另一个方面是如何通过指令处理操作数。 In general there are 2 operands and addressing of each of them is encoded in the binary command. 通常,存在2个操作数,并且每个操作数的寻址在二进制命令中被编码。 So to interpret them you need two things: 所以要解释它们你需要两件事:

  • a set of handler functions covering all variations of all instruction classes which will do the actual job (ie - modify your machine state); 一组处理函数,涵盖了将执行实际工作的所有指令类的所有变体(即 - 修改机器状态);
  • and some instruction selector function which will take next binary command at Program Counter (PC) or Instruction Pointer (IP), prepare it for execution on your "machine" (for example, decide that it's an addition operation, extract argument value(s) from memory), call a corresponding handler function and adjust PC/IP after the call. 以及一些指令选择器功能,它将在程序计数器(PC)或指令指针(IP)上执行下一个二进制命令,准备在“机器”上执行(例如,确定它是一个加法运算,提取参数值)从内存中),调用相应的处理函数并在调用后调整PC / IP。 Here you can also print a nice human-readable assembly instruction along with command's bytecode and its address. 在这里,您还可以打印一个很好的人类可读汇编指令以及命令的字节码及其地址。

Third - memory. 第三 - 记忆。 This depends on the target architecture. 这取决于目标架构。 In my case there were no segments/selectors and the max memory amount was small, so I've just allocated a corresponding block. 在我的情况下,没有段/选择器,最大内存量很小,所以我刚刚分配了一个相应的块。 To access concrete memory cells I added a set/get functions. 为了访问具体的存储单元,我添加了一个set / get函数。 Some areas of the memory were supposed to be taken by ROM, so this thin layer was helpful to implement it. 内存的某些区域应该由ROM占用,因此这个薄层有助于实现它。

Forth - I/O and interrupts. 第四 - I / O和中断。 And this is where it can get really complicated, depending on the requirements and the platform. 根据需求和平台,这可能会变得非常复杂。 The the simplest terminal output can be achieved by dedicating a memory block for some sort of screen buffer. 最简单的终端输出可以通过为某种屏幕缓冲区专用存储块来实现。 When your mem-setter function sees a write to that block, it updates your console emulation (you need a GUI in your app, right?). 当mem-setter函数看到对该块的写入时,它会更新您的控制台仿真(您的应用程序中需要一个GUI,对吗?)。 A simplest console input will also resemble a 8086 architecture - when user presses a key you emulate an interrupt, pass key code into it etc. If you need something more complex that that, ie real BIOS support with loadable interrupt handlers and/or emulation of some I/O controllers then it will take the same amount of time that you have spent on the previous parts of the machine. 最简单的控制台输入也类似于8086架构 - 当用户按下键时,您可以模拟中断,将密钥代码传递给它等。如果您需要更复杂的东西,即带有可加载中断处理程序的真实BIOS支持和/或模拟一些I / O控制器将花费您在机器的先前部分上花费的相同时间。 So don't delay it to the last week. 所以不要把它推迟到上周。

You need to think about two issues: mapping the internal state of the emulated machine to variables in your emulator, and executing the code. 您需要考虑两个问题:将模拟机器的内部状态映射到模拟器中的变量,以及执行代码。 The first is normally handled by an array for the registers, probably in a struct with various other information: a program counter, a bit map with condition codes (supposing the processor has these---I don't know the MIPS architecture), etc. The second will be either a switch or a table of pointers to functions or functional objects. 第一个通常由寄存器的数组处理,可能在具有各种其他信息的结构中:程序计数器,带有条件代码的位图(假设处理器具有这些 - 我不知道MIPS架构),第二个将是一个开关或一个指向函数或功能对象的指针表。 If the architecture uses different machine instruction formats, this could be a multilevel table. 如果体系结构使用不同的机器指令格式,则可以是多级表。 The emulator then reads the "instruction" at the current instruction pointer, then "executes" it by switching on it or using it to index into the table. 然后,仿真器在当前指令指针处读取“指令”,然后通过打开它或使用它来索引到表中来“执行”它。 Over and over, in an endless loop. 一遍又一遍,无休止的循环。

This should get you started: 这应该让你开始:

http://www.oberle.org/procsimu-index.html http://www.oberle.org/procsimu-index.html

You would have to customize it for MIPS instruction set. 您必须为MIPS指令集自定义它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM