Various improvements.
Rudimentary keyboard input. Resize/Zoom video. Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
parent
c9fb116464
commit
fe0730c4d3
8 changed files with 295 additions and 26 deletions
|
@ -1,5 +1,9 @@
|
|||
#include "emulator.h"
|
||||
|
||||
#include <QMdiSubWindow>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
Emulator::Emulator(QWidget *parent) :
|
||||
QFrame(parent)
|
||||
{
|
||||
|
@ -10,3 +14,32 @@ void Emulator::redrawScreen()
|
|||
{
|
||||
video->update();
|
||||
}
|
||||
|
||||
void Emulator::setVideoSize(QMdiSubWindow * window, const QSize & size)
|
||||
{
|
||||
window->showNormal();
|
||||
|
||||
// assume the extra space between widget and border is not affected by a resize
|
||||
const QSize gap = window->size() - video->size();
|
||||
window->resize(size + gap);
|
||||
}
|
||||
|
||||
void Emulator::setZoom(QMdiSubWindow * window, int x)
|
||||
{
|
||||
const QSize target = video->minimumSize() * x;
|
||||
setVideoSize(window, target);
|
||||
}
|
||||
|
||||
void Emulator::set43AspectRatio(QMdiSubWindow * window)
|
||||
{
|
||||
// keep the same surface with 4:3 aspect ratio
|
||||
const QSize & size = video->size();
|
||||
const double area = size.height() * size.width();
|
||||
|
||||
const int numerator = 35;
|
||||
const int denominator = 24;
|
||||
|
||||
const int x = sqrt(area / (numerator * denominator));
|
||||
const QSize target(numerator * x, denominator * x);
|
||||
setVideoSize(window, target);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "ui_emulator.h"
|
||||
|
||||
class QMdiSubWindow;
|
||||
|
||||
class Emulator : public QFrame, private Ui::Emulator
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -11,6 +13,13 @@ public:
|
|||
explicit Emulator(QWidget *parent = 0);
|
||||
|
||||
void redrawScreen();
|
||||
|
||||
void setZoom(QMdiSubWindow * window, int x);
|
||||
void set43AspectRatio(QMdiSubWindow * window);
|
||||
|
||||
private:
|
||||
void setVideoSize(QMdiSubWindow * window, const QSize & size);
|
||||
|
||||
};
|
||||
|
||||
#endif // EMULATOR_H
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
<height>384</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -58,25 +58,63 @@ namespace
|
|||
|
||||
}
|
||||
|
||||
void FrameDrawDiskLEDS(HDC x) {}
|
||||
void FrameDrawDiskStatus(HDC x) {}
|
||||
void FrameRefreshStatus(int x, bool) {}
|
||||
void FrameDrawDiskLEDS(HDC)
|
||||
{
|
||||
}
|
||||
|
||||
// Keyboard
|
||||
void FrameDrawDiskStatus(HDC)
|
||||
{
|
||||
|
||||
BYTE KeybGetKeycode () {}
|
||||
BYTE __stdcall KeybReadData (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft) {}
|
||||
BYTE __stdcall KeybReadFlag (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft) {}
|
||||
}
|
||||
|
||||
void FrameRefreshStatus(int, bool)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Joystick
|
||||
|
||||
BYTE __stdcall JoyReadButton(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft) {}
|
||||
BYTE __stdcall JoyReadPosition(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft) {}
|
||||
BYTE __stdcall JoyResetPosition(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft) {}
|
||||
BYTE __stdcall JoyReadButton(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
||||
{
|
||||
Q_UNUSED(pc)
|
||||
Q_UNUSED(addr)
|
||||
Q_UNUSED(bWrite)
|
||||
Q_UNUSED(d)
|
||||
Q_UNUSED(nCyclesLeft)
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTE __stdcall JoyReadPosition(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
||||
{
|
||||
Q_UNUSED(pc)
|
||||
Q_UNUSED(addr)
|
||||
Q_UNUSED(bWrite)
|
||||
Q_UNUSED(d)
|
||||
Q_UNUSED(nCyclesLeft)
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTE __stdcall JoyResetPosition(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
||||
{
|
||||
Q_UNUSED(pc)
|
||||
Q_UNUSED(addr)
|
||||
Q_UNUSED(bWrite)
|
||||
Q_UNUSED(d)
|
||||
Q_UNUSED(nCyclesLeft)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Speaker
|
||||
|
||||
BYTE __stdcall SpkrToggle (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft) {}
|
||||
BYTE __stdcall SpkrToggle (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
||||
{
|
||||
Q_UNUSED(pc)
|
||||
Q_UNUSED(addr)
|
||||
Q_UNUSED(bWrite)
|
||||
Q_UNUSED(d)
|
||||
Q_UNUSED(nCyclesLeft)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VideoInitialize() {}
|
||||
|
||||
|
@ -86,7 +124,7 @@ QApple::QApple(QWidget *parent) :
|
|||
setupUi(this);
|
||||
|
||||
myEmulator = new Emulator(mdiArea);
|
||||
mdiArea->addSubWindow(myEmulator, Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint);
|
||||
myEmulatorWindow = mdiArea->addSubWindow(myEmulator, Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint);
|
||||
|
||||
myMSGap = 5;
|
||||
|
||||
|
@ -94,7 +132,7 @@ QApple::QApple(QWidget *parent) :
|
|||
startEmulator();
|
||||
}
|
||||
|
||||
void QApple::timerEvent(QTimerEvent *event)
|
||||
void QApple::timerEvent(QTimerEvent *)
|
||||
{
|
||||
const double fUsecPerSec = 1.e6;
|
||||
const UINT nExecutionPeriodUsec = 1000 * myMSGap;
|
||||
|
@ -115,7 +153,7 @@ void QApple::timerEvent(QTimerEvent *event)
|
|||
|
||||
void QApple::on_actionStart_triggered()
|
||||
{
|
||||
myTimerID = startTimer(myMSGap);
|
||||
myTimerID = startTimer(0);
|
||||
actionPause->setEnabled(true);
|
||||
actionStart->setEnabled(false);
|
||||
}
|
||||
|
@ -126,3 +164,18 @@ void QApple::on_actionPause_triggered()
|
|||
actionPause->setEnabled(false);
|
||||
actionStart->setEnabled(true);
|
||||
}
|
||||
|
||||
void QApple::on_actionX1_triggered()
|
||||
{
|
||||
myEmulator->setZoom(myEmulatorWindow, 1);
|
||||
}
|
||||
|
||||
void QApple::on_actionX2_triggered()
|
||||
{
|
||||
myEmulator->setZoom(myEmulatorWindow, 2);
|
||||
}
|
||||
|
||||
void QApple::on_action4_3_triggered()
|
||||
{
|
||||
myEmulator->set43AspectRatio(myEmulatorWindow);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,14 @@ private slots:
|
|||
|
||||
void on_actionPause_triggered();
|
||||
|
||||
void on_actionX1_triggered();
|
||||
|
||||
void on_actionX2_triggered();
|
||||
|
||||
void on_action4_3_triggered();
|
||||
|
||||
private:
|
||||
QMdiSubWindow * myEmulatorWindow;
|
||||
Emulator * myEmulator;
|
||||
|
||||
int myMSGap;
|
||||
|
|
|
@ -45,6 +45,10 @@
|
|||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="action4_3"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionX1"/>
|
||||
<addaction name="actionX2"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<action name="actionStart">
|
||||
|
@ -57,6 +61,21 @@
|
|||
<string>Pause</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action4_3">
|
||||
<property name="text">
|
||||
<string>4:3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionX1">
|
||||
<property name="text">
|
||||
<string>x1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionX2">
|
||||
<property name="text">
|
||||
<string>x2</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "video.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "Video.h"
|
||||
|
@ -21,12 +22,34 @@ namespace
|
|||
#define SW_PAGE2 (g_uVideoMode & VF_PAGE2)
|
||||
#define SW_TEXT (g_uVideoMode & VF_TEXT)
|
||||
|
||||
bool g_bTextFlashState = false;
|
||||
// bool g_bTextFlashState = false;
|
||||
|
||||
void halfScanLines(QPixmap & charset)
|
||||
{
|
||||
const int height = charset.height();
|
||||
const int width = charset.width();
|
||||
|
||||
const QColor background = charset.toImage().pixelColor(0, height - 1);
|
||||
|
||||
QPainter paint(&charset);
|
||||
paint.setPen(background);
|
||||
|
||||
for (int i = 0; i < height; i += 2)
|
||||
{
|
||||
paint.drawLine(0, i, width - 1, i);
|
||||
}
|
||||
}
|
||||
|
||||
BYTE nextKey = 0;
|
||||
bool keyReady = false;
|
||||
|
||||
}
|
||||
|
||||
bool Video::Update40ColCell (QPainter & painter, int x, int y, int xpixel, int ypixel, int offset)
|
||||
{
|
||||
Q_UNUSED(xpixel)
|
||||
Q_UNUSED(ypixel)
|
||||
|
||||
BYTE ch = *(g_pTextBank0+offset);
|
||||
|
||||
const int row = ch / 16;
|
||||
|
@ -34,49 +57,105 @@ bool Video::Update40ColCell (QPainter & painter, int x, int y, int xpixel, int y
|
|||
|
||||
const int sx = 16 * column;
|
||||
const int sy = 16 * row;
|
||||
painter.drawPixmap(x * 14, y * 16, *myCharset, sx, sy, 14, 16);
|
||||
painter.drawPixmap(x * 14, y * 16, myCharset40, sx, sy, 14, 16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Video::Update80ColCell(QPainter & painter, int x, int y, int xpixel, int ypixel, int offset)
|
||||
{
|
||||
BYTE ch1 = *(g_pTextBank1+offset);
|
||||
BYTE ch2 = *(g_pTextBank0+offset);
|
||||
Q_UNUSED(xpixel)
|
||||
Q_UNUSED(ypixel)
|
||||
|
||||
{
|
||||
BYTE ch1 = *(g_pTextBank1+offset);
|
||||
|
||||
const int row = ch1 / 16;
|
||||
const int column = ch1 % 16;
|
||||
|
||||
const int sx = 8 * column;
|
||||
const int sy = 16 * row;
|
||||
painter.drawPixmap(x * 14, y * 16, myCharset80, sx, sy, 7, 16);
|
||||
}
|
||||
|
||||
{
|
||||
BYTE ch2 = *(g_pTextBank0+offset);
|
||||
|
||||
const int row = ch2 / 16;
|
||||
const int column = ch2 % 16;
|
||||
|
||||
const int sx = 8 * column;
|
||||
const int sy = 16 * row;
|
||||
painter.drawPixmap(x * 14 + 7, y * 16, myCharset80, sx, sy, 7, 16);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Video::UpdateLoResCell(QPainter & painter, int x, int y, int xpixel, int ypixel, int offset)
|
||||
{
|
||||
Q_UNUSED(painter)
|
||||
Q_UNUSED(x)
|
||||
Q_UNUSED(y)
|
||||
Q_UNUSED(xpixel)
|
||||
Q_UNUSED(ypixel)
|
||||
|
||||
BYTE val = *(g_pTextBank0+offset);
|
||||
|
||||
Q_UNUSED(val)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Video::UpdateDLoResCell(QPainter & painter, int x, int y, int xpixel, int ypixel, int offset)
|
||||
{
|
||||
Q_UNUSED(painter)
|
||||
Q_UNUSED(x)
|
||||
Q_UNUSED(y)
|
||||
Q_UNUSED(xpixel)
|
||||
Q_UNUSED(ypixel)
|
||||
Q_UNUSED(offset)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Video::UpdateHiResCell(QPainter & painter, int x, int y, int xpixel, int ypixel, int offset)
|
||||
{
|
||||
Q_UNUSED(painter)
|
||||
Q_UNUSED(x)
|
||||
Q_UNUSED(y)
|
||||
Q_UNUSED(xpixel)
|
||||
Q_UNUSED(ypixel)
|
||||
|
||||
const BYTE * base = g_pHiresBank0 + offset;
|
||||
|
||||
Q_UNUSED(base)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Video::UpdateDHiResCell(QPainter & painter, int x, int y, int xpixel, int ypixel, int offset)
|
||||
{
|
||||
Q_UNUSED(painter)
|
||||
Q_UNUSED(x)
|
||||
Q_UNUSED(y)
|
||||
Q_UNUSED(xpixel)
|
||||
Q_UNUSED(ypixel)
|
||||
Q_UNUSED(offset)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Video::Video(QWidget *parent) : QWidget(parent)
|
||||
Video::Video(QWidget *parent) : VIDEO_BASECLASS(parent)
|
||||
{
|
||||
myCharset.reset(new QPixmap(":/resources/CHARSET4.BMP"));
|
||||
myCharset40.load(":/resources/CHARSET4.BMP");
|
||||
halfScanLines(myCharset40);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
myCharset80 = myCharset40.scaled(myCharset40.width() / 2, myCharset40.height());
|
||||
}
|
||||
|
||||
void Video::paintEvent(QPaintEvent *event)
|
||||
void Video::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
|
@ -143,3 +222,65 @@ void Video::paintEvent(QPaintEvent *event)
|
|||
ypixel += 16;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
const int key = event->key();
|
||||
|
||||
BYTE ch = 0;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
ch = 0x0d;
|
||||
break;
|
||||
default:
|
||||
if (key < 0x80)
|
||||
{
|
||||
ch = key;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch)
|
||||
{
|
||||
nextKey = ch | 0x80;
|
||||
keyReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::keyReleaseEvent(QKeyEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
}
|
||||
|
||||
// Keyboard
|
||||
|
||||
BYTE KeybGetKeycode ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTE __stdcall KeybReadData (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
||||
{
|
||||
Q_UNUSED(pc)
|
||||
Q_UNUSED(addr)
|
||||
Q_UNUSED(bWrite)
|
||||
Q_UNUSED(d)
|
||||
Q_UNUSED(nCyclesLeft)
|
||||
|
||||
return nextKey;
|
||||
}
|
||||
|
||||
BYTE __stdcall KeybReadFlag (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
||||
{
|
||||
Q_UNUSED(pc)
|
||||
Q_UNUSED(addr)
|
||||
Q_UNUSED(bWrite)
|
||||
Q_UNUSED(d)
|
||||
Q_UNUSED(nCyclesLeft)
|
||||
|
||||
BYTE result = keyReady ? nextKey : 0;
|
||||
nextKey = 0;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
#ifndef VIDEO_H
|
||||
#define VIDEO_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QOpenGLWidget>
|
||||
|
||||
#include <memory>
|
||||
//#define VIDEO_BASECLASS QOpenGLWidget
|
||||
#define VIDEO_BASECLASS QWidget
|
||||
|
||||
class Video : public QWidget
|
||||
class Video : public VIDEO_BASECLASS
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -16,7 +17,9 @@ signals:
|
|||
public slots:
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event);
|
||||
virtual void paintEvent(QPaintEvent *event);
|
||||
virtual void keyPressEvent(QKeyEvent *event);
|
||||
virtual void keyReleaseEvent(QKeyEvent *event);
|
||||
|
||||
private:
|
||||
bool Update40ColCell(QPainter & painter, int x, int y, int xpixel, int ypixel, int offset);
|
||||
|
@ -26,7 +29,8 @@ private:
|
|||
bool UpdateHiResCell(QPainter & painter, int x, int y, int xpixel, int ypixel, int offset);
|
||||
bool UpdateDHiResCell(QPainter & painter, int x, int y, int xpixel, int ypixel, int offset);
|
||||
|
||||
std::shared_ptr<QPixmap> myCharset;
|
||||
QPixmap myCharset40;
|
||||
QPixmap myCharset80;
|
||||
};
|
||||
|
||||
#endif // VIDEO_H
|
||||
|
|
Loading…
Add table
Reference in a new issue