Various improvements.

Rudimentary keyboard input.
Resize/Zoom video.

Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
Andrea Odetti 2017-07-04 12:14:04 +01:00
parent c9fb116464
commit fe0730c4d3
8 changed files with 295 additions and 26 deletions

View file

@ -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);
}

View file

@ -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

View file

@ -28,6 +28,9 @@
<height>384</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
</widget>
</item>
</layout>

View file

@ -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);
}

View file

@ -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;

View file

@ -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/>

View file

@ -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;
}

View file

@ -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