C语言模拟最简单的计算机

C语言模拟最简单的计算机

以下内容参考南大"计算机系统基础"实验:不停计算的机器

概述

如下面的伪代码所示,计算机运行程序的过程为取指令-->运行指令-->更新PC的值。

c 复制代码
while (1) {
  从PC指示的存储器位置取出指令;
  执行指令;
  更新PC;
}

取指(instruction fetch, IF)

执行一条指令前, 首先要拿到这条指令。指令究竟在哪里呢? 还记得冯诺依曼体系结构的核心思想吗? 那就是"存储程序, 程序控制".

这两句话告诉我们, 指令被放在存储器中, 由PC指出当前指令的位置. 事实上, PC就是一个指针!!!

取指令要做的事情自然就是将PC指向的指令从内存读入到CPU中。

译码(instruction decode, ID)

在取指阶段, 计算机拿到了将要执行的指令. 让我们来看看这个指令的·面貌, 睁大眼睛一看, 竟然是个0和1组成的比特串!

c 复制代码
10111001 00110100 00010010 00000000 00000000

计算机也只是个巨大的数字电路, 它只能理解0和1。

拿到指令后,我们需要对指令进行翻译。指令通常包含操作码和操作数两个部分的信息。根据操作码的含义去执行对应的操作,操作数一般作为该操作需要的数据。

执行(execute, EX)

经过译码之后, CPU就知道当前指令具体要做什么了, 执行阶段就是真正完成指令的工作. 现在TRM只有加法器这一个执行部件, 必要的时候,

只需要往加法器输入两个源操作数, 就能得到执行的结果了. 之后还要把结果写回到目的操作数中, 可能是寄存器, 也可能是内存.

更新PC

执行完一条指令之后, CPU就要执行下一条指令. 在这之前, CPU需要更新PC的值, 让PC加上刚才执行完的指令的长度,

即可指向下一条指令的位置.

代码实操

这里直接从南京大学"计算机系统基础"实验搬过来:不停计算的机器

为南大打call !!!

模拟的计算机有4个8位的寄存器, 一个4位PC, 以及一段16字节的内存. 它支持R型和M型两种指令格式, 4条指令. 其指令手册如下:

c 复制代码
                                                     4  2  0
            |                        |        | +----+--+--+
mov   rt,rs | R[rt] <- R[rs]         | R-type | |0000|rt|rs|
            |                        |        | +----+--+--+
            |                        |        | +----+--+--+
add   rt,rs | R[rt] <- R[rs] + R[rt] | R-type | |0001|rt|rs|
            |                        |        | +----+--+--+
            |                        |        | +----+--+--+
load  addr  | R[0] <- M[addr]        | M-type | |1110| addr|
            |                        |        | +----+--+--+
            |                        |        | +----+--+--+
store addr  | M[addr] <- R[0]        | M-type | |1111| addr|
            |                        |        | +----+--+--+
c 复制代码
#include <stdint.h>
#include <stdio.h>

#define NREG 4
#define NMEM 16

// 定义指令格式
typedef union {
  struct { uint8_t rs : 2, rt : 2, op : 4; } rtype;
  struct { uint8_t addr : 4      , op : 4; } mtype;
  uint8_t inst;
} inst_t;

#define DECODE_R(inst) uint8_t rt = (inst).rtype.rt, rs = (inst).rtype.rs
#define DECODE_M(inst) uint8_t addr = (inst).mtype.addr

uint8_t pc = 0;       // PC, C语言中没有4位的数据类型, 我们采用8位类型来表示
uint8_t R[NREG] = {}; // 寄存器
uint8_t M[NMEM] = {   // 内存, 其中包含一个计算z = x + y的程序
  0b11100110,  // load  6#     | R[0] <- M[y]
  0b00000100,  // mov   r1, r0 | R[1] <- R[0]
  0b11100101,  // load  5#     | R[0] <- M[x]
  0b00010001,  // add   r0, r1 | R[0] <- R[0] + R[1]
  0b11110111,  // store 7#     | M[z] <- R[0]
  0b00010000,  // x = 16
  0b00100001,  // y = 33
  0b00000000,  // z = 0
};

int halt = 0; // 结束标志

// 执行一条指令
void exec_once() {
  inst_t this;
  this.inst = M[pc]; // 取指
  switch (this.rtype.op) {
  //  操作码译码       操作数译码           执行
    case 0b0000: { DECODE_R(this); R[rt]   = R[rs];   break; }
    case 0b0001: { DECODE_R(this); R[rt]  += R[rs];   break; }
    case 0b1110: { DECODE_M(this); R[0]    = M[addr]; break; }
    case 0b1111: { DECODE_M(this); M[addr] = R[0];    break; }
    default:
      printf("Invalid instruction with opcode = %x, halting...\n", this.rtype.op);
      halt = 1;
      break;
  }
  pc ++; // 更新PC
}

int main() {
  while (1) {
    exec_once();
    if (halt) break;
  }
  printf("The result of 16 + 33 is %d\n", M[7]);
  return 0;
}
相关推荐
Herbert_hwt33 分钟前
C语言零基础入门:循环控制与数据类型详解
c语言·数据结构·算法
Tyler_TXZ2 小时前
C++C语言之——二叉树
c语言·开发语言·数据结构·c++·二叉树
小羊先生car2 小时前
F429-HAL-RS485(回环/双机实验)(2026/8/16)
c语言·单片机·嵌入式硬件·软件构建
白狐_7985 小时前
408 数据结构|线索二叉树两题详解:先序线索化后的空链域 + 中序前驱/后继判断
c语言·数据结构·链表
GeekZHR5 小时前
C语言指针进阶补充6:动态内存管理、mem系列内存函数、复杂指针声明,一次补齐指针的“三大盲区“
java·c语言·算法·指针
caimouse5 小时前
ReactOS 图形系统分析(43):API 裁剪 — cliprgn.c
c语言·开发语言
caimouse7 小时前
ReactOS 图形系统分析(36):位块传输 — bitblt.c
c语言·reactos
Tyler_TXZ7 小时前
C++C语言之——树
c语言·c++·算法·
小羊先生car7 小时前
F429-HAL-Can总线(回环及双机实验)(2026/8/15)
c语言·单片机·嵌入式硬件·软件构建
caimouse8 小时前
ReactOS 图形系统分析(38):区域子系统 — region.c
c语言