前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VM技术(二)从CHIP8入手CPU的模拟(四)

VM技术(二)从CHIP8入手CPU的模拟(四)

作者头像
Pulsar-V
发布2019-08-02 09:34:34
6780
发布2019-08-02 09:34:34
举报
文章被收录于专栏:Pulsar-V

完整的CHIP8类

CHIP8.h

代码语言:javascript
复制
//
// Created by Pulsar on 2019/7/18.
//

#ifndef EASYMVM_CHIP8_H
#define EASYMVM_CHIP8_H

#include <QtWidgets>
#include <QtCore/QtCore>
#include <cstdint>
#include <cstdlib>
#include <stdio.h>
#include <cstring>
#include <ctime>
#include <modules/VMCore/include/base_vm_env/base_vm_env.hpp>
//储存大小
#define MEM_SIZE 4096 
//屏幕高度
#define GFX_ROWS 32
//屏幕宽度
#define GFX_COLS 64
//像素个数
#define GFX_SIZE (GFX_ROWS * GFX_COLS)
//栈大小
#define STACK_SIZE 16
//按键大小
#define KEY_SIZE 16
//像素大小
#define PIXEL_SIZE 5
//CPU频率
#define CLOCK_HZ 60
//CPU周期
#define CLOCK_RATE_MS ((int) ((1.0 / CLOCK_HZ) * 1000 + 0.5))
//颜色
#define BLACK 0
#define WHITE 255

//屏幕行数
#define SCREEN_ROWS (GFX_ROWS * PIXEL_SIZE)
//屏幕列数
#define SCREEN_COLS (GFX_COLS * PIXEL_SIZE)

//屏幕索引
#define GFX_INDEX(row, col) ((row)*GFX_COLS + (col))

//最大游戏文件大小
#define MAX_GAME_SIZE (0x1000 - 0x200)

class CHIP8 : public base_vm_env {

public:
    unsigned char screen[SCREEN_ROWS][SCREEN_COLS][3];
    unsigned short opcode;
    //TODO:0x000-0x1FF - Chip 8解释器(包含用于显示的字体)
    // 0x050-0x0A0 - 用于生成 4x5 像素的字体集合 (从’0’到’F’)
    // 0x200-0xFFF - 游戏ROM 与工作RAM
    uint8_t memory[MEM_SIZE];

    //TODO:CPU 寄存器:Chip 8 有16个单字节(1 byte)寄存器,
    // 名字为V0,V1...到VF. 前15个寄存器为通用寄存器,
    // 最后一个寄存器(VF)是个进位标志(carry flag)
    uint8_t V[16];
    //TODO: 索引寄存器I(Index register,暂译为“索引寄存器”)
    //  程序计数器PC(program counter),值域为0x000 到 0xFFF:
    uint16_t I;
    uint16_t PC;
    uint8_t gfx[GFX_ROWS][GFX_COLS];
    //TODO:计数器
    uint8_t delay_timer;
    uint8_t sound_timer;

    //TODO:堆栈
    uint16_t stack[STACK_SIZE];
    uint16_t SP;
    //TODO:按键
    uint8_t key[KEY_SIZE];


    int screen_rows = SCREEN_ROWS;
    int screen_cols = SCREEN_COLS;
    int clock_rate_ms = CLOCK_RATE_MS;
    int clock_hz = CLOCK_HZ;
    int pixel_size = PIXEL_SIZE;

    char *name = "CHIP8";
    //绘图标志位
    bool draw_flag;
    //周期函数
    void runCycle();


    void loadGame(char *file_path);

    void debug_screen();

    void setkeys(int index,int state);
    //计时函数
    void tick();
    //绘制精灵
    void draw_sprite(uint8_t x, uint8_t y, uint8_t n);

    //打印当前状态
    void printState();

    //初始化CPU
    void initialize();
    //打印CPU信息
    void print_cpu_info() ;
};


#endif //EASYMVM_CHIP8_H

CHIP8.cpp

代码语言:javascript
复制
//
// Created by Pulsar on 2019/7/18.
//

#include <iostream>
#include "modules/CHIP8/include/CHIP8.h"

#ifdef __linux__
void Beep(int val1,int val2){};
#endif

#define unknown_opcode(op) \
    do { \
        fprintf(stderr, "Unknown opcode: 0x%x\n", op); \
        fprintf(stderr, "kk: 0x%02x\n", kk); \
        fprintf(stderr, "System Shutdown!!!\n"); \
        exit(42); \
    } while (0)

//#define DEBUG
#ifdef DEBUG
#define p(...) printf(__VA_ARGS__);
#else
#define p(...)
#endif

#define IS_BIT_SET(byte, bit) (((0x80 >> (bit)) & (byte)) != 0x0)

#define FONTSET_ADDRESS 0x00
#define FONTSET_BYTES_PER_CHAR 5
unsigned char chip8_fontset[80] = {
        0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
        0x20, 0x60, 0x20, 0x20, 0x70, // 1
        0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
        0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
        0x90, 0x90, 0xF0, 0x10, 0x10, // 4
        0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
        0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
        0xF0, 0x10, 0x20, 0x40, 0x40, // 7
        0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
        0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
        0xF0, 0x90, 0xF0, 0x90, 0x90, // A
        0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
        0xF0, 0x80, 0x80, 0x80, 0xF0, // C
        0xE0, 0x90, 0x90, 0x90, 0xE0, // D
        0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
        0xF0, 0x80, 0xF0, 0x80, 0x80  // F
};

static inline uint8_t randbyte() { return (rand() % 256); }

void CHIP8::initialize() {
    //TODO:初始化内存与寄存器(注意这个操作只需执行一次)
    int i;

    PC = 0x200;
    opcode = 0;
    I = 0;
    SP = 0;

    memset(memory, 0, sizeof(uint8_t) * MEM_SIZE);
    memset(V, 0, sizeof(uint8_t) * 16);
    memset(gfx, 0, sizeof(uint8_t) * GFX_SIZE);
    memset(stack, 0, sizeof(uint16_t) * STACK_SIZE);
    memset(key, 0, sizeof(uint8_t) * KEY_SIZE);

    for (i = 0; i < 80; i++) {
        memory[FONTSET_ADDRESS + i] = chip8_fontset[i];
    }

    draw_flag = true;
    delay_timer = 0;
    sound_timer = 0;
    srand(time(NULL));
}

void CHIP8::runCycle() {
    int i;
    uint8_t x, y, n;
    uint8_t kk;
    uint16_t nnn;

    //TODO:获取opcode
    opcode = memory[PC] << 8 | memory[PC + 1];
    //获取高四位和低四位
    x = (opcode >> 8) & 0x000F; // 取低4位数据
    y = (opcode >> 4) & 0x000F; // 取高4位数据
    //获取操作码
    n = opcode & 0x000F; // 取四位汇编操作码
    kk = opcode & 0x00FF; // 取四位汇编寄存器码
    nnn = opcode & 0x0FFF; // 取数据码

#ifdef DEBUG
//    std::cout<<"PC:"<<PC<<"Op:"<<opcode<<std::endl;
//    printf("PC: 0x%04x Op: 0x%04x\n", PC, opcode);
#endif

    
    switch (opcode & 0xF000) {
        case 0x0000:
            switch (kk) {
                case 0x00E0:
                    p("Clear the screen\n");
                    memset(gfx, 0, sizeof(uint8_t) * GFX_SIZE);
                    draw_flag = true;
                    PC += 2;
                    break;
                case 0x00EE: // ret
                    p("ret\n");
                    PC = stack[--SP];
                    break;
                default:
                    unknown_opcode(opcode);
            }
            break;
        case 0x1000: 
            p("Jump to address 0x%x\n", nnn);
            PC = nnn;
            break;
        case 0x2000:
            p("Call address 0x%x\n", nnn);
            stack[SP++] = PC + 2;
            PC = nnn;
            break;
        case 0x3000: 
            p("Skip next instruction if 0x%x == 0x%x\n", V[x], kk);
            PC += (V[x] == kk) ? 4 : 2;
            break;
        case 0x4000: 
            p("Skip next instruction if 0x%x != 0x%x\n", V[x], kk);
            PC += (V[x] != kk) ? 4 : 2;
            break;
        case 0x5000: 
            p("Skip next instruction if 0x%x == 0x%x\n", V[x], V[y]);
            PC += (V[x] == V[y]) ? 4 : 2;
            break;
        case 0x6000: 
            p("Set V[0x%x] to 0x%x\n", x, kk);
            V[x] = kk;
            PC += 2;
            break;
        case 0x7000: 
            p("Set V[0x%d] to V[0x%d] + 0x%x\n", x, x, kk);
            V[x] += kk;
            PC += 2;
            break;
        case 0x8000: 
            switch (n) {
                case 0x0:
                    p("V[0x%x] = V[0x%x] = 0x%x\n", x, y, V[y]);
                    V[x] = V[y];
                    break;
                case 0x1:
                    p("V[0x%x] |= V[0x%x] = 0x%x\n", x, y, V[y]);
                    V[x] = V[x] | V[y];
                    break;
                case 0x2:
                    p("V[0x%x] &= V[0x%x] = 0x%x\n", x, y, V[y]);
                    V[x] = V[x] & V[y];
                    break;
                case 0x3:
                    p("V[0x%x] ^= V[0x%x] = 0x%x\n", x, y, V[y]);
                    V[x] = V[x] ^ V[y];
                    break;
                case 0x4:
                    p("V[0x%x] = V[0x%x] + V[0x%x] = 0x%x + 0x%x\n", x, x, y, V[x], V[y]);
                    V[0xF] = ((int) V[x] + (int) V[y]) > 255 ? 1 : 0;
                    V[x] = V[x] + V[y];
                    break;
                case 0x5:
                    p("V[0x%x] = V[0x%x] - V[0x%x] = 0x%x - 0x%x\n", x, x, y, V[x], V[y]);
                    V[0xF] = (V[x] > V[y]) ? 1 : 0;
                    V[x] = V[x] - V[y];
                    break;
                case 0x6:
                    p("V[0x%x] = V[0x%x] >> 1 = 0x%x >> 1\n", x, x, V[x]);
                    V[0xF] = V[x] & 0x1;
                    V[x] = (V[x] >> 1);
                    break;
                case 0x7:
                    p("V[0x%x] = V[0x%x] - V[0x%x] = 0x%x - 0x%x\n", x, y, x, V[y], V[x]);
                    V[0xF] = (V[y] > V[x]) ? 1 : 0;
                    V[x] = V[y] - V[x];
                    break;
                case 0xE:
                    p("V[0x%x] = V[0x%x] << 1 = 0x%x << 1\n", x, x, V[x]);
                    V[0xF] = (V[x] >> 7) & 0x1;
                    V[x] = (V[x] << 1);
                    break;
                default:
                    unknown_opcode(opcode);
            }
            PC += 2;
            break;
        case 0x9000: 
            switch (n) {
                case 0x0:
                    p("Skip next instruction if 0x%x != 0x%x\n", V[x], V[y]);
                    PC += (V[x] != V[y]) ? 4 : 2;
                    break;
                default:
                    unknown_opcode(opcode);
            }
            break;
        case 0xA000:
            p("Set I to 0x%x\n", nnn);
            I = nnn;
            PC += 2;
            break;
        case 0xB000: 
            p("Jump to 0x%x + V[0] (0x%x)\n", nnn, V[0]);
            PC = nnn + V[0];
            break;
        case 0xC000: 
            p("V[0x%x] = random byte\n", x);
            V[x] = randbyte() & kk;
            PC += 2;
            break;
        case 0xD000: 
            p("Draw sprite at (V[0x%x], V[0x%x]) = (0x%x, 0x%x) of height %d",
              x, y, V[x], V[y], n);
            draw_sprite(V[x], V[y], n);
            PC += 2;
            draw_flag = true;
            break;
        case 0xE000: // 按键事件处理
            switch (kk) {
                case 0x9E: 
                    p("Skip next instruction if key[%d] is pressed\n", x);
                    PC += (key[V[x]]) ? 4 : 2;
                    break;
                case 0xA1: 
                    p("Skip next instruction if key[%d] is NOT pressed\n", x);
                    PC += (!key[V[x]]) ? 4 : 2;
                    break;
                default:
                    unknown_opcode(opcode);
            }
            break;
        case 0xF000: // misc
            switch (kk) {
                case 0x07:
                    p("V[0x%x] = delay timer = %d\n", x, delay_timer);
                    V[x] = delay_timer;
                    PC += 2;
                    break;
                case 0x0A:
                    i = 0;
                    printf("Wait for key instruction\n");
                    while (true) {
                        for (i = 0; i < KEY_SIZE; i++) {
                            if (key[i]) {
                                V[x] = i;
                                goto got_key_press;
                            }
                        }
                    }
                got_key_press:
                    PC += 2;
                    break;
                case 0x15:
                    p("delay timer = V[0x%x] = %d\n", x, V[x]);
                    delay_timer = V[x];
                    PC += 2;
                    break;
                case 0x18:
                    p("sound timer = V[0x%x] = %d\n", x, V[x]);
                    sound_timer = V[x];
                    PC += 2;
                    break;
                case 0x1E:
                    p("I = I + V[0x%x] = 0x%x + 0x%x\n", x, I, V[x]);
                    V[0xF] = (I + V[x] > 0xfff) ? 1 : 0;
                    I = I + V[x];
                    PC += 2;
                    break;
                case 0x29:
                    p("I = location of font for character V[0x%x] = 0x%x\n", x, V[x]);
                    I = FONTSET_BYTES_PER_CHAR * V[x];
                    PC += 2;
                    break;
                case 0x33:
                    p("Store BCD for %d starting at address 0x%x\n", V[x], I);
                    memory[I] = (V[x] % 1000) / 100; // hundred's digit
                    memory[I + 1] = (V[x] % 100) / 10;   // ten's digit
                    memory[I + 2] = (V[x] % 10);         // one's digit
                    PC += 2;
                    break;
                case 0x55:
                    p("Copy sprite from registers 0 to 0x%x into memory at address 0x%x\n", x, I);
                    for (i = 0; i <= x; i++) { memory[I + i] = V[i]; }
                    I += x + 1;
                    PC += 2;
                    break;
                case 0x65:
                    p("Copy sprite from memory at address 0x%x into registers 0 to 0x%x\n", x, I);
                    for (i = 0; i <= x; i++) { V[i] = memory[I + i]; }
                    I += x + 1;
                    PC += 2;
                    break;
                default:
                    unknown_opcode(opcode);
            }
            break;
        default:
            unknown_opcode(opcode);
    }

#ifdef DEBUG
//    this->printState();
#endif
}

void CHIP8::tick() {
    // 更新计时器
    if (delay_timer > 0) {
        --delay_timer;
    }
    if (sound_timer > 0) {
        --sound_timer;
        if (sound_timer == 0) {
            Beep(400,400);//注意Beep函数只有Windows才有,Linux另外安装libbeep
#ifdef DEBUG
            printf("BEEP!\n");
#endif
        }
    }
}

void CHIP8::draw_sprite(uint8_t x, uint8_t y, uint8_t n) {
    unsigned row = y, col = x;
    unsigned byte_index;
    unsigned bit_index;

    // 中断寄存器置 0
    V[0xF] = 0;
    for (byte_index = 0; byte_index < n; byte_index++) {
        uint8_t byte = memory[I + byte_index];

        for (bit_index = 0; bit_index < 8; bit_index++) {
            uint8_t bit = (byte >> bit_index) & 0x1;
            uint8_t *pixelp = &gfx[(row + byte_index) % GFX_ROWS]
            [(col + (7 - bit_index)) % GFX_COLS];

            if (bit == 1 && *pixelp == 1) V[0xF] = 1;

            *pixelp = *pixelp ^ bit;
        }
    }
}


void CHIP8::loadGame(char *file_path) {
    FILE *fgame;

    fgame = fopen(file_path, "rb");

    if (NULL == fgame) {
        fprintf(stderr, "Unable to open game: %s\n", file_path);
        exit(42);
    }

    fread(&memory[0x200], 1, MAX_GAME_SIZE, fgame);

    fclose(fgame);
}


void CHIP8::printState() {
    printf("------------------------------------------------------------------\n");
    printf("\n");

    printf("V0: 0x%02x  V4: 0x%02x  V8: 0x%02x  VC: 0x%02x\n",
           V[0], V[4], V[8], V[12]);
    printf("V1: 0x%02x  V5: 0x%02x  V9: 0x%02x  VD: 0x%02x\n",
           V[1], V[5], V[9], V[13]);
    printf("V2: 0x%02x  V6: 0x%02x  VA: 0x%02x  VE: 0x%02x\n",
           V[2], V[6], V[10], V[14]);
    printf("V3: 0x%02x  V7: 0x%02x  VB: 0x%02x  VF: 0x%02x\n",
           V[3], V[7], V[11], V[15]);

    printf("\n");
    printf("PC: 0x%04x\n", PC);
    printf("\n");
    printf("\n");
}

void CHIP8::debug_screen() {
    int x, y;

    for (y = 0; y < GFX_ROWS; y++) {
        for (x = 0; x < GFX_COLS; x++) {
            if (gfx[y][x] == 0) printf("0");
            else printf(" ");
        }
        printf("\n");
    }
    printf("\n");

}

void CHIP8::print_cpu_info() {
    std::cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << std::endl;
    std::cout << "CPU name      |    " << this->name << std::endl;
    std::cout << "Screen rows   |    " << this->screen_rows << std::endl;
    std::cout << "Screen cols   |    " << this->screen_cols << std::endl;
    std::cout << "Clock rate_ms |    " << this->clock_rate_ms << std::endl;
    std::cout << "Clock hz      |    " << this->clock_hz << std::endl;
    std::cout << "Pixel size    |    " << this->pixel_size << std::endl;
    std::cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" << std::endl;
}

void CHIP8::setkeys(int index,int state) {
    this->key[index]=state;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 完整的CHIP8类
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档