显示器的实现
前面提到CHIP8的显示器是64x32黑白像素的显示器,所以我们在QT中定义一个OpenGL的Wedgit显示到窗口中,同时把keymap和游戏循环设置在主窗体的timmer循环中。
//
// 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);
};
//
// 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显示器
//
// 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
//
// 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::~Monitor64x32GL() {
}