画面の拡大、キーボード入力、CMT機能などを実装してみました。
emu.h
/* Skelton for retropc emulator Qt Version : tanam Date : 2013.05.18 - [ Qt4.8 emulation i/f ] */ #ifndef _EMU_H_ #define _EMU_H_ #include <QApplication> #include <QtGui> #include <QKeyEvent> #include <QPainter> #include <QTimer> #include <windows.h> #include "common.h" #include "vm/vm.h" #include "res/resource.h" #include "config.h" #ifndef Q_OS_ANDROID #include <dsound.h> #endif #ifndef SCREEN_WIDTH_ASPECT #define SCREEN_WIDTH_ASPECT SCREEN_WIDTH #endif #ifndef SCREEN_HEIGHT_ASPECT #define SCREEN_HEIGHT_ASPECT SCREEN_HEIGHT #endif #ifndef WINDOW_WIDTH #define WINDOW_WIDTH SCREEN_WIDTH_ASPECT #endif #ifndef WINDOW_HEIGHT #define WINDOW_HEIGHT SCREEN_HEIGHT_ASPECT #endif class EMU : public QWidget { Q_OBJECT protected: VM* vm; void paintEvent(QPaintEvent *); void keyPressEvent(QKeyEvent *); void keyReleaseEvent(QKeyEvent *); void mousePressEvent(QMouseEvent *); protected slots: void onTimer(); private: QTimer *m_timer; int skip_frames; void ShowPopup(); // ---------------------------------------- // input // ---------------------------------------- void initialize_input(); void release_input(); void update_input(); uint8 key_status[256]; // windows key code mapping uint32 joy_status[2]; // joystick #1, #2 (b0 = up, b1 = down, b2 = left, b3 = right, b4- = buttons int joy_num; uint32 joy_mask[2]; // ---------------------------------------- // screen // ---------------------------------------- void initialize_screen(); void release_screen(); // screen settings int screen_width, screen_height; int screen_width_aspect, screen_height_aspect; int window_width, window_height; // screen buffer unsigned char *scrbuf; // ---------------------------------------- // sound // ---------------------------------------- void initialize_sound(); void release_sound(); void update_sound(int* extra_frames); int sound_rate, sound_samples; bool sound_ok, sound_started, now_mute; #ifndef Q_OS_ANDROID // direct sound LPDIRECTSOUND lpds; LPDIRECTSOUNDBUFFER lpdsb, lpdsp; bool first_half; #endif // ---------------------------------------- // media // ---------------------------------------- typedef struct { _TCHAR path[_MAX_PATH]; bool play; int offset; int wait_count; } media_status_t; #ifdef USE_CART media_status_t cart_status; #endif #ifdef USE_FD1 media_status_t disk_status[8]; #endif #ifdef USE_TAPE media_status_t tape_status; #endif void initialize_media(); void update_media(); void restore_media(); void clear_media_status(media_status_t *status) { status->path[0] = _T('\0'); status->wait_count = 0; } public: // ---------------------------------------- // initialize // ---------------------------------------- EMU(QWidget *parent = 0); ~EMU(); _TCHAR app_path[_MAX_PATH]; _TCHAR* bios_path(_TCHAR* file_name); // drive virtual machine int run(); void reset(); // user interface #ifdef USE_CART void open_cart(_TCHAR* file_path); void close_cart(); bool cart_inserted(); #endif #ifdef USE_FD1 void open_disk(int drv, _TCHAR* file_path, int offset); void close_disk(int drv); bool disk_inserted(int drv); #endif #ifdef USE_TAPE void play_tape(_TCHAR* file_path); void rec_tape(_TCHAR* file_path); void close_tape(); bool tape_inserted(); #endif // screen int get_window_width(int mode); int get_window_height(int mode); void draw_screen(); // sound void mute_sound(); // ---------------------------------------- // for virtual machine // ---------------------------------------- // input device uint8* key_buffer() { return key_status; } uint32* joy_buffer() { return joy_status; } // screen scrntype* screen_buffer(int y); // debug log void out_message(const _TCHAR* format); }; #endif
emu.cpp
/* Skelton for retropc emulator Qt Version : tanam Date : 2013.05.18 - [ Qt4.8 emulation i/f ] */ #include "emu.h" // ---------------------------------------------------------------------------- // initialize // ---------------------------------------------------------------------------- EMU::EMU(QWidget *) { // timer event skip_frames=0; m_timer = new QTimer(); connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimer())); m_timer->start(1000/FPS); // load sound config static int freq_table[8] = {2000, 4000, 8000, 11025, 22050, 44100, 48000, 96000}; static double late_table[5] = {0.05, 0.1, 0.2, 0.3, 0.4}; if(!(0 <= config.sound_frequency && config.sound_frequency < 8)) { config.sound_frequency = 6; // default: 48KHz } if(!(0 <= config.sound_latency && config.sound_latency < 5)) { config.sound_latency = 1; // default: 100msec } sound_rate = freq_table[config.sound_frequency]; sound_samples = (int)(sound_rate * late_table[config.sound_latency] + 0.5); // initialize vm = new VM(this); initialize_input(); initialize_screen(); initialize_sound(); initialize_media(); vm->initialize_sound(sound_rate, sound_samples); vm->reset(); config.window_mode = 1; open_cart((_TCHAR *)"/sdcard/rom/SC3000.ROM"); } EMU::~EMU() { release_input(); release_screen(); release_sound(); delete vm; } // ---------------------------------------------------------------------------- // drive machine // ---------------------------------------------------------------------------- int EMU::run() { update_input(); update_media(); // virtual machine may be driven to fill sound buffer int extra_frames = 0; update_sound(&extra_frames); // drive virtual machine if(extra_frames == 0) { vm->run(); extra_frames = 1; } return extra_frames; } void EMU::reset() { // reset virtual machine vm->reset(); } // ---------------------------------------------------------------------------- // user interface // ---------------------------------------------------------------------------- void EMU::initialize_media() { #ifdef USE_CART memset(&cart_status, 0, sizeof(cart_status)); #endif #ifdef USE_FD1 memset(disk_status, 0, sizeof(disk_status)); #endif #ifdef USE_TAPE memset(&tape_status, 0, sizeof(tape_status)); #endif } void EMU::update_media() { #ifdef USE_FD1 for(int drv = 0; drv < 8; drv++) { if(disk_status[drv].wait_count != 0 && --disk_status[drv].wait_count == 0) { vm->open_disk(drv, disk_status[drv].path, disk_status[drv].offset); } } #endif #ifdef USE_TAPE if(tape_status.wait_count != 0 && --tape_status.wait_count == 0) { if(tape_status.play) { vm->play_tape(tape_status.path); } else { vm->rec_tape(tape_status.path); } } #endif } void EMU::restore_media() { #ifdef USE_CART if(cart_status.path[0] != _T('\0')) { vm->open_cart(cart_status.path); } #endif #ifdef USE_FD1 for(int drv = 0; drv < 8; drv++) { if(disk_status[drv].path[0] != _T('\0')) { vm->open_disk(drv, disk_status[drv].path, disk_status[drv].offset); } } #endif #ifdef USE_TAPE if(tape_status.path[0] != _T('\0')) { if(tape_status.play) { vm->play_tape(tape_status.path); } else { tape_status.path[0] = _T('\0'); } } #endif } #ifdef USE_CART void EMU::open_cart(_TCHAR* file_path) { vm->open_cart(file_path); _tcscpy(cart_status.path, file_path); } void EMU::close_cart() { vm->close_cart(); clear_media_status(&cart_status); } bool EMU::cart_inserted() { return vm->cart_inserted(); } #endif #ifdef USE_FD1 void EMU::open_disk(int drv, _TCHAR* file_path, int offset) { if(vm->disk_inserted(drv)) { vm->close_disk(drv); // wait 0.5sec disk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2); } else if(disk_status[drv].wait_count == 0) { vm->open_disk(drv, file_path, offset); } _tcscpy(disk_status[drv].path, file_path); disk_status[drv].offset = offset; } void EMU::close_disk(int drv) { vm->close_disk(drv); clear_media_status(&disk_status[drv]); } bool EMU::disk_inserted(int drv) { return vm->disk_inserted(drv); } #endif #ifdef USE_TAPE void EMU::play_tape(_TCHAR* file_path) { if(vm->tape_inserted()) { vm->close_tape(); // wait 0.5sec tape_status.wait_count = (int)(FRAMES_PER_SEC / 2); } else if(tape_status.wait_count == 0) { vm->play_tape(file_path); } _tcscpy(tape_status.path, file_path); tape_status.play = true; } void EMU::rec_tape(_TCHAR* file_path) { if(vm->tape_inserted()) { vm->close_tape(); // wait 0.5sec tape_status.wait_count = (int)(FRAMES_PER_SEC / 2); } else if(tape_status.wait_count == 0) { vm->rec_tape(file_path); } _tcscpy(tape_status.path, file_path); tape_status.play = false; } void EMU::close_tape() { vm->close_tape(); clear_media_status(&tape_status); } bool EMU::tape_inserted() { return vm->tape_inserted(); } #endif void EMU::onTimer() { run(); if (++skip_frames >MAX_SKIP_FRAMES) { draw_screen(); skip_frames=0; } update(); return; } _TCHAR* EMU::bios_path(_TCHAR* file_name) { static _TCHAR file_path[_MAX_PATH]; _stprintf(file_path, _T("%s%s"), app_path, file_name); return file_path; } void EMU::out_message(const _TCHAR* format) { qDebug(format); return; }