VM技術(二)從CHIP8入手CPU的模擬(三)
阿新 • • 發佈:2019-08-01
顯示器的實現
前面提到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