1. 程式人生 > >VM技術(二)從CHIP8入手CPU的模擬(三)

VM技術(二)從CHIP8入手CPU的模擬(三)

顯示器的實現

前面提到CHIP8的顯示器是64x32黑白畫素的顯示器,所以我們在QT中定義一個OpenGL的Wedgit顯示到視窗中,同時把keymap和遊戲迴圈設定在主窗體的timmer迴圈中。

Monitor64x32.h

//
// Created by Pulsar on 2019/7/18.
//

#ifndef EASYMVM_MONITOR16X16_H
#define EASYMVM_MONITOR16X16_H

#include <sys/time.h>
#include <iostream>
#include <QtWidgets>
#include <QMainWindow>
#include <QtCore>
#include <QTimer>
#include <CHIP8.h>
#include "Monitor64x32GL.h"
#include "ui_Monitor64x32.h"

namespace Ui {
    class Monitor64x32;
}
class Monitor64x32 : public QMainWindow {
Q_OBJECT
public:
    struct timeval clock_prev;

    Monitor64x32(QWidget *parent = 0);

    void link_cpu(CHIP8 &chip8);
    int keymap(unsigned char k);

    void keyPressEvent(QKeyEvent *event);


    int timediff_ms(struct timeval *end, struct timeval *start);


    void keyReleaseEvent(QKeyEvent *event);


    ~Monitor64x32();

private  slots:

    void paint_screen();


    void timeout_sc();

signals:
    void draw_pixel(int row, int col, float color);
    void repaint_screen();

private:
    CHIP8 cpu;
    Monitor64x32GL *glwindow;
    void paint_pixel(int row, int col, unsigned char color);
    void paint_cell(int row, int col, unsigned char color);

};


Monitor64x32.cpp

//
// Created by Pulsar on 2019/7/18.
//

#include <Monitor64x32.h>

void Monitor64x32::link_cpu(CHIP8 &cip8) {
    this->cpu = cip8;
}

Monitor64x32::Monitor64x32(QWidget *parent) : QMainWindow(parent) {
    QTimer *timer = new QTimer(this); //定時器
    timer->start(10);


    glwindow = new Monitor64x32GL(this);//設定繪製介面
    setCentralWidget(glwindow);//設定OpenGL介面到主窗體
    glwindow->setMinimumSize(640, 320);//設定螢幕最大值
    this->setMaximumSize(640, 320);////設定主窗體大小最小值

    connect(timer, SIGNAL(timeout()), this, SLOT(timeout_sc()));
    connect(glwindow, SIGNAL(paintGL()), this, SLOT(paint_screen()));
    connect(this, SIGNAL(repaint_screen()), glwindow, SLOT(repaint()));
    connect(this, SIGNAL(draw_pixel(int, int, float)), glwindow,
            SLOT(paint_pixel(int, int, float))); // 連結繪製函式和OpenGL介面的繪製函式
}
//繪製畫素
void Monitor64x32::paint_cell(int row, int col, unsigned char color) {
    int pixel_row = row * PIXEL_SIZE;
    int pixel_col = col * PIXEL_SIZE;
    int drow, dcol;

    for (drow = 0; drow < PIXEL_SIZE; drow++) {
        for (dcol = 0; dcol < PIXEL_SIZE; dcol++) {
            paint_pixel(pixel_row + drow, pixel_col + dcol, color);
        }
    }
}
//繪製畫素到螢幕
void Monitor64x32::paint_pixel(int row, int col, unsigned char color) {
    row = cpu.screen_rows - 1 - row;
    cpu.screen[row][col][0] = cpu.screen[row][col][1] = cpu.screen[row][col][2] = color;
}
//繪圖函式
void Monitor64x32::paint_screen() {
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    for (int row = 0; row < GFX_ROWS; row++) {
        for (int col = 0; col < GFX_COLS; col++) {
            paint_cell(row, col, cpu.gfx[row][col] ? WHITE : BLACK);
        }
    }

    glDrawPixels(SCREEN_COLS, SCREEN_ROWS, GL_RGB, GL_UNSIGNED_BYTE,
                 (void *) cpu.screen);
    glPixelZoom(2, 2);
}
//按鍵按下事件
void Monitor64x32::keyPressEvent(QKeyEvent *event) {
    unsigned char k = event->key();
    int index = keymap(k);
    if (index >= 0) cpu.setkeys(index, 1);
}

int Monitor64x32::keymap(unsigned char k) {
    switch (k) {
        case '1':
            return 0x1;
        case '2':
            return 0x2;
        case '3':
            return 0x3;
        case '4':
            return 0xc;

        case 'Q':
            return 0x4;
        case 'W':
            return 0x5;
        case 'E':
            return 0x6;
        case 'R':
            return 0xd;

        case 'A':
            return 0x7;
        case 'S':
            return 0x8;
        case 'D':
            return 0x9;
        case 'F':
            return 0xe;

        case 'Z':
            return 0xa;
        case 'X':
            return 0x0;
        case 'C':
            return 0xb;
        case 'V':
            return 0xf;

        default:
            return -1;
    }
}
//按鍵釋放事件
void Monitor64x32::keyReleaseEvent(QKeyEvent *event) {
    unsigned char k = event->key();

    int index = keymap(k);
    if (index >= 0) cpu.setkeys(index, 0);
}


Monitor64x32::~Monitor64x32() {
    delete glwindow;
}
//定時器超時事件
void Monitor64x32::timeout_sc() {
    struct timeval clock_now;
    gettimeofday(&clock_now, NULL);

    cpu.runCycle();

    if (cpu.draw_flag) {
        repaint_screen();
        cpu.draw_flag = false;
    }
    if (timediff_ms(&clock_now, &clock_prev) >= cpu.clock_rate_ms) {
        cpu.tick();
        clock_prev = clock_now;
    }
};

//分頻器
int Monitor64x32::timediff_ms(struct timeval *end, struct timeval *start) {
    int diff = (end->tv_sec - start->tv_sec) * 1000 +
               (end->tv_usec - start->tv_usec) / 1000;
    //printf("timediff = %d\n", diff);
    return diff;
}

接下來是OpenGL顯示器

Monitor64x32GL.h

//
// Created by Pulsar on 2019/7/18.
//

#ifndef EASYMVM_MONITOR64X32GL_H
#define EASYMVM_MONITOR64X32GL_H

#include <GL/gl.h>
#include <GL/glu.h>
#include<QOpenGLWidget>
#include <QWidget>
#include <QtCore/qglobal.h>


class   Monitor64x32GL : public QOpenGLWidget {
    Q_OBJECT
public:
    Monitor64x32GL(QWidget *parent = nullptr);

    void initializeGL();



    void paint_cell(int row, int col, unsigned char color);


    void resizeGL(int width, int height);

    virtual ~Monitor64x32GL();

signals:
    void paintGL();


public slots:
    void paint_pixel(int row, int col, float color);


private:
    int PIXEL_SIZE_SC;//畫素大小
    int SCREEN_ROWS_SC;//螢幕行數
    int SCREEN_COLS_SC;//螢幕列數
};
#endif //EASYMVM_MONITOR64X32_GL_H

Monitor64x32GL.cpp

//
// Created by Pulsar on 2019/7/18.
//

#ifndef EASYMVM_MONITOR64X32_GL_H
#define EASYMVM_MONITOR64X32_GL_H

#include <GL/gl.h>
#include <GL/glu.h>
#include<QOpenGLWidget>
#include <iostream>
#include <Monitor64x32GL.h>

#endif //EASYMVM_MONITOR64X32_GL_H

Monitor64x32GL::Monitor64x32GL(QWidget *parent) : QOpenGLWidget(parent) {

}
//清屏
void Monitor64x32GL::initializeGL() {
    glClearColor(0.0, 0.0, 0.0, 0.0);
}



void Monitor64x32GL::resizeGL(int width, int height) {
    gluPerspective(45.0, (GLfloat) width / (GLfloat) height, 0.1, 100);
    glLoadIdentity();
}

//繪製畫素點
void Monitor64x32GL::paint_pixel(int row, int col, float color) {
    float screen_row_width = 1.0 / 64.0 * 2;
    float screen_col_width = 1.0 / 32.0 * 2;
    glColor3f(color / 255.0, color / 255.0, color / 255.0);
    glBegin(GL_QUADS);
    glVertex2f(screen_row_width * col, -screen_col_width * row);
    glVertex2f(screen_row_width * col, -screen_col_width * (row + 1.0));
    glVertex2f(screen_row_width * (col + 1.0), -screen_col_width * (row + 1.0));
    glVertex2f(screen_row_width * (col + 1.0), -screen_col_width * row);
    glEnd();
}

void Monitor64x32GL::paint_cell(int row, int col, unsigned char color) {
    int pixel_row = row * PIXEL_SIZE_SC;
    int pixel_col = col * PIXEL_SIZE_SC;
    int drow, dcol;
//下面這段註釋的過程用來觀察對畫素的繪製是否正常
//    for (drow = 0; drow < PIXEL_SIZE_SC; drow++) {
//        for (dcol = 0; dcol < PIXEL_SIZE_SC; dcol++) {
//            paint_pixel(pixel_row + drow, pixel_col + dcol, color);
//        }
//    }
}

Monitor64x32GL::~Monitor64x3