BACKENDS: ATARI: QoL improvements

- fixed aspect ratio correction (overlay destroyed
  its state, keyboard shortcut didn't always work),
  as well as significantly sped it up on VGA and
  SuperVidel

- smoother transitions between video modes

- reduce the number of video mode changes

- fixed a few extreme cases when triple buffering
  could lose an update

- lighter ST RAM usage in the lite build

- removed hardware accessing init/deinit routines
This commit is contained in:
Miro Kropacek 2024-07-14 10:46:06 +02:00
parent 0c77d67845
commit dd49c9e3ce
10 changed files with 348 additions and 439 deletions

View File

@ -21,172 +21,11 @@
#include "../../platform/atari/symbols.h"
.global SYM(asm_screen_tt_save)
.global SYM(asm_screen_falcon_save)
.global SYM(asm_screen_tt_restore)
.global SYM(asm_screen_falcon_restore)
.global SYM(asm_draw_4bpl_sprite)
.global SYM(asm_draw_8bpl_sprite)
.text
| extern void asm_screen_tt_save(void);
|
SYM(asm_screen_tt_save):
bsr wait_vbl | avoid flickering
lea 0xffff8400.w,a0
lea save_pal,a1
moveq #256/2-1,d0
tt_save_loop:
move.l (a0)+,(a1)+
dbra d0,tt_save_loop
lea save_video,a1
move.l 0xffff8200.w,(a1)+ | vidhm
move.w 0xffff820c.w,(a1)+ | vidl
move.w 0xffff8262.w,(a1)+ | tt shifter
rts
| extern void asm_screen_falcon_save(void);
|
SYM(asm_screen_falcon_save):
movem.l d2-d7/a2,-(sp)
bsr wait_vbl | avoid flickering
lea 0xffff9800.w,a0 | save falcon palette
lea save_pal,a1 |
moveq #256/2-1,d7 |
|
falcon_save_loop:
move.l (a0)+,(a1)+ |
move.l (a0)+,(a1)+ |
dbra d7,falcon_save_loop |
movem.l 0xffff8240.w,d0-d7 | save st palette
movem.l d0-d7,(a1) |
lea save_video,a0
move.l 0xffff8200.w,(a0)+ | vidhm
move.w 0xffff820c.w,(a0)+ | vidl
move.l 0xffff8282.w,(a0)+ | h-regs
move.l 0xffff8286.w,(a0)+ |
move.l 0xffff828a.w,(a0)+ |
move.l 0xffff82a2.w,(a0)+ | v-regs
move.l 0xffff82a6.w,(a0)+ |
move.l 0xffff82aa.w,(a0)+ |
move.w 0xffff82c0.w,(a0)+ | vco
move.w 0xffff82c2.w,(a0)+ | c_s
move.l 0xffff820e.w,(a0)+ | offset+width
move.w 0xffff820a.w,(a0)+ | sync
move.b 0xffff8265.w,(a0)+ | p_o
cmpi.w #0xb0,0xffff8282.w | st(e) / falcon test
sle (a0)+ | it's a falcon resolution
move.w 0xffff8266.w,(a0)+ | f_s
move.w 0xffff8260.w,(a0)+ | st_s
movem.l (sp)+,d2-d7/a2
rts
| extern void asm_screen_tt_restore(void);
|
SYM(asm_screen_tt_restore):
bsr wait_vbl | avoid flickering
lea save_video,a1
move.l (a1)+,0xffff8200.w | vidhm
move.w (a1)+,0xffff820c.w | vidl
move.w (a1)+,0xffff8262.w | tt shifter
lea save_pal,a0
lea 0xffff8400.w,a1
moveq #256/2-1,d0
.loop: move.l (a0)+,(a1)+
dbra d0,.loop
rts
| extern void asm_screen_falcon_restore(void);
|
SYM(asm_screen_falcon_restore):
movem.l d2-d7/a2,-(sp)
bsr wait_vbl | avoid flickering
lea save_video,a0
move.l (a0)+,0xffff8200.w | videobase_address:h&m
move.w (a0)+,0xffff820c.w | l
move.l (a0)+,0xffff8282.w | h-regs
move.l (a0)+,0xffff8286.w |
move.l (a0)+,0xffff828a.w |
move.l (a0)+,0xffff82a2.w | v-regs
move.l (a0)+,0xffff82a6.w |
move.l (a0)+,0xffff82aa.w |
move.w (a0)+,0xffff82c0.w | vco
move.w (a0)+,0xffff82c2.w | c_s
move.l (a0)+,0xffff820e.w | offset+width
move.w (a0)+,0xffff820a.w | sync
move.b (a0)+,0xffff8265.w | p_o
tst.b (a0)+ | st(e) compatible mode?
bne falcon_restore_st_comp | yes
falcon_restore_falcon:
move.l a0,-(sp)
bsr wait_vbl | Patch to avoid
clr.w 0xffff8266.w | monochrome sync errors
bsr wait_vbl | (ripped from
move.l (sp)+,a0 | FreeMiNT kernel,
move.w (a0),0xffff8266.w | by Draco/Yescrew)
bra falcon_restore_restored
falcon_restore_st_comp:
move.w (a0)+,0xffff8266.w | falcon-shift
move.w (a0),0xffff8260.w | st-shift
lea save_video,a0
move.w 32(a0),0xffff82c2.w | c_s
move.l 34(a0),0xffff820e.w | offset+width
falcon_restore_restored:
lea save_pal,a0 | restore falcon palette
lea 0xffff9800.w,a1 |
moveq #128-1,d7 |
|
falcon_restore_loop:
move.l (a0)+,(a1)+ |
move.l (a0)+,(a1)+ |
dbra d7,falcon_restore_loop |
movem.l (a0),d0-d7 | restore st palette
movem.l d0-d7,0xffff8240.w |
movem.l (sp)+,d2-d7/a2
rts
wait_vbl:
move.w #0x25,-(sp) | Vsync()
trap #14 |
addq.l #2,sp |
rts
| extern void asm_draw_4bpl_sprite(uint16 *dstBuffer, const uint16 *srcBuffer, const uint16 *srcMask,
| uint destX, uint destY, uint dstPitch, uint w, uint h);
|
@ -206,7 +45,7 @@ SYM(asm_draw_4bpl_sprite):
| Draws a 4 bitplane sprite at any position on screen.
| (c) 1999 Pieter van der Meer (EarX)
|
| INPUT: d0.w: x position of sprite on screen (left side)
| d1.w: y position of sprite on screen (top side)
| d6.w: number of 16pixel X blocks to do

View File

@ -26,24 +26,6 @@
extern "C" {
/**
* Save Atari TT video registers.
*/
void asm_screen_tt_save(void);
/**
* Save Atari Falcon video registers.
*/
void asm_screen_falcon_save(void);
/**
* Restore Atari TT video registers.
*/
void asm_screen_tt_restore(void);
/**
* Restore Atari Falcon video registers.
*/
void asm_screen_falcon_restore(void);
/**
* Copy 4bpl sprite into 4bpl buffer. Sprite's width must be multiply of 16.
*

View File

@ -24,6 +24,7 @@
#include "atari-graphics.h"
#include <mint/cookie.h>
#include <mint/falcon.h>
#include <mint/osbind.h>
#include <mint/sysvars.h>
@ -49,18 +50,49 @@ static const Graphics::PixelFormat PIXELFORMAT_CLUT8 = Graphics::PixelFormat::cr
static const Graphics::PixelFormat PIXELFORMAT_RGB332 = Graphics::PixelFormat(1, 3, 3, 2, 0, 5, 2, 0, 0);
static const Graphics::PixelFormat PIXELFORMAT_RGB121 = Graphics::PixelFormat(1, 1, 2, 1, 0, 3, 1, 0, 0);
static bool s_shrinkVidelVisibleArea;
static void shrinkVidelVisibleArea() {
// Active VGA screen area consists of 960 half-lines, i.e. 480 raster lines.
// In case of 320x240, the number is still 480 but data is fetched
// only for 240 lines so it doesn't make a difference to us.
if (hasSuperVidel()) {
const int vOffset = ((480 - 400) / 2) * 2; // *2 because of half-lines
// VDB = VBE = VDB + paddding/2
*((volatile uint16*)0xFFFF82A8) = *((volatile uint16*)0xFFFF82A6) = *((volatile uint16*)0xFFFF82A8) + vOffset;
// VDE = VBB = VDE - padding/2
*((volatile uint16*)0xFFFF82AA) = *((volatile uint16*)0xFFFF82A4) = *((volatile uint16*)0xFFFF82AA) - vOffset;
} else {
// 31500/60.1 = 524 raster lines
// vft = 524 * 2 + 1 = 1049 half-lines
// 480 visible lines = 960 half-lines
// 1049 - 960 = 89 half-lines reserved for borders
// we want 400 visible lines = 800 half-lines
// vft = 800 + 89 = 889 half-lines in total ~ 70.1 Hz vertical frequency
int16 vft = *((volatile int16*)0xFFFF82A2);
int16 vss = *((volatile int16*)0xFFFF82AC); // vss = vft - vss_sync
vss -= vft; // -vss_sync
*((volatile int16*)0xFFFF82A2) = 889;
*((volatile int16*)0xFFFF82AC) = 889 + vss;
}
}
static bool s_tt;
static int s_shakeXOffset;
static int s_shakeYOffset;
static int s_aspectRatioCorrectionYOffset;
static Graphics::Surface *s_screenSurf;
static void VblHandler() {
if (s_screenSurf) {
#ifdef SCREEN_ACTIVE
const int bitsPerPixel = (s_screenSurf->format == PIXELFORMAT_RGB121 ? 4 : 8);
uintptr p = (uintptr)s_screenSurf->getBasePtr(0, MAX_V_SHAKE + s_shakeYOffset);
uintptr p = (uintptr)s_screenSurf->getBasePtr(0, MAX_V_SHAKE + s_shakeYOffset + s_aspectRatioCorrectionYOffset);
if (!s_tt) {
const int bitsPerPixel = (s_screenSurf->format == PIXELFORMAT_RGB121 ? 4 : 8);
s_shakeXOffset = -s_shakeXOffset;
if (s_shakeXOffset >= 0) {
@ -85,6 +117,11 @@ static void VblHandler() {
#endif
s_screenSurf = nullptr;
}
if (s_shrinkVidelVisibleArea) {
shrinkVidelVisibleArea();
s_shrinkVidelVisibleArea = false;
}
}
static uint32 InstallVblHandler() {
@ -119,50 +156,36 @@ static uint32 UninstallVblHandler() {
return uninstalled;
}
static void shrinkVidelVisibleArea() {
// Active VGA screen area consists of 960 half-lines, i.e. 480 raster lines.
// In case of 320x240, the number is still 480 but data is fetched
// only for 240 lines so it doesn't make a difference to us.
Vsync();
if (hasSuperVidel()) {
const int vOffset = ((480 - 400) / 2) * 2; // *2 because of half-lines
// VDB = VBE = VDB + paddding/2
*((volatile uint16*)0xFFFF82A8) = *((volatile uint16*)0xFFFF82A6) = *((volatile uint16*)0xFFFF82A8) + vOffset;
// VDE = VBB = VDE - padding/2
*((volatile uint16*)0xFFFF82AA) = *((volatile uint16*)0xFFFF82A4) = *((volatile uint16*)0xFFFF82AA) - vOffset;
} else {
// 31500/60.1 = 524 raster lines
// vft = 524 * 2 + 1 = 1049 half-lines
// 480 visible lines = 960 half-lines
// 1049 - 960 = 89 half-lines reserved for borders
// we want 400 visible lines = 800 half-lines
// vft = 800 + 89 = 889 half-lines in total ~ 70.1 Hz vertical frequency
int16 vft = *((volatile int16*)0xFFFF82A2);
int16 vss = *((volatile int16*)0xFFFF82AC); // vss = vft - vss_sync
vss -= vft; // -vss_sync
*((volatile int16*)0xFFFF82A2) = 889;
*((volatile int16*)0xFFFF82AC) = 889 + vss;
}
}
static int s_oldRez = -1;
static int s_oldMode = -1;
static void *s_oldPhysbase = nullptr;
static Palette s_oldPalette;
void AtariGraphicsShutdown() {
Supexec(UninstallVblHandler);
if (s_oldRez != -1) {
Setscreen(SCR_NOCHANGE, s_oldPhysbase, s_oldRez);
EsetPalette(0, s_oldPalette.entries, s_oldPalette.tt);
} else if (s_oldMode != -1) {
// prevent setting video base address just on the VDB line
Vsync();
if (hasSuperVidel())
VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480
VsetMode(s_oldMode);
static _RGB black[256];
VsetRGB(0, 256, black);
VsetScreen(SCR_NOCHANGE, s_oldPhysbase, SCR_NOCHANGE, SCR_NOCHANGE);
if (hasSuperVidel()) {
// SuperVidel XBIOS does not restore those (unlike TOS/EmuTOS)
long ssp = Super(SUP_SET);
//*((volatile char *)0xFFFF8265) = 0;
*((volatile short *)0xFFFF820E) = 0;
Super(ssp);
VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480
}
VsetMode(s_oldMode);
VsetRGB(0, s_oldPalette.entries, s_oldPalette.falcon);
}
}
@ -226,12 +249,31 @@ AtariGraphicsManager::AtariGraphicsManager() {
}
}
// although we store/restore video hardware in OSystem_Atari,
// make sure that internal OS structures are updated correctly, too
if (_tt) {
s_oldRez = Getrez();
// EgetPalette / EsetPalette doesn't care about current resolution's number of colors
s_oldPalette.entries = 256;
EgetPalette(0, 256, s_oldPalette.tt);
} else {
s_oldMode = VsetMode(VM_INQUIRE);
switch (s_oldMode & NUMCOLS) {
case BPS1:
s_oldPalette.entries = 2;
break;
case BPS2:
s_oldPalette.entries = 4;
break;
case BPS4:
s_oldPalette.entries = 16;
break;
case BPS8:
case BPS8C:
s_oldPalette.entries = 256;
break;
default:
s_oldPalette.entries = 0;
}
VgetRGB(0, s_oldPalette.entries, s_oldPalette.falcon);
}
s_oldPhysbase = Physbase();
@ -253,7 +295,7 @@ AtariGraphicsManager::~AtariGraphicsManager() {
bool AtariGraphicsManager::hasFeature(OSystem::Feature f) const {
switch (f) {
case OSystem::Feature::kFeatureAspectRatioCorrection:
//debug("hasFeature(kFeatureAspectRatioCorrection): %d", !_vgaMonitor);
//debug("hasFeature(kFeatureAspectRatioCorrection): %d", !_tt);
return !_tt;
case OSystem::Feature::kFeatureCursorPalette:
// FIXME: pretend to have cursor palette at all times, this function
@ -266,14 +308,21 @@ bool AtariGraphicsManager::hasFeature(OSystem::Feature f) const {
default:
return false;
}
// TODO: kFeatureDisplayLogFile?, kFeatureClipboardSupport, kFeatureSystemBrowserDialog
}
void AtariGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
if (!hasFeature(f))
return;
switch (f) {
case OSystem::Feature::kFeatureAspectRatioCorrection:
//debug("setFeatureState(kFeatureAspectRatioCorrection): %d", enable);
_oldAspectRatioCorrection = _aspectRatioCorrection;
_aspectRatioCorrection = enable;
_pendingState.aspectRatioCorrection = enable;
if (_currentState.aspectRatioCorrection != _pendingState.aspectRatioCorrection)
_pendingState.change |= GraphicsState::kAspectRatioCorrection;
break;
default:
break;
@ -284,7 +333,7 @@ bool AtariGraphicsManager::getFeatureState(OSystem::Feature f) const {
switch (f) {
case OSystem::Feature::kFeatureAspectRatioCorrection:
//debug("getFeatureState(kFeatureAspectRatioCorrection): %d", _aspectRatioCorrection);
return _aspectRatioCorrection;
return _currentState.aspectRatioCorrection;
case OSystem::Feature::kFeatureCursorPalette:
//debug("getFeatureState(kFeatureCursorPalette): %d", isOverlayVisible());
//return isOverlayVisible();
@ -297,14 +346,13 @@ bool AtariGraphicsManager::getFeatureState(OSystem::Feature f) const {
bool AtariGraphicsManager::setGraphicsMode(int mode, uint flags) {
debug("setGraphicsMode: %d, %d", mode, flags);
GraphicsMode graphicsMode = (GraphicsMode)mode;
_pendingState.mode = (GraphicsMode)mode;
if (graphicsMode >= GraphicsMode::DirectRendering && graphicsMode <= GraphicsMode::TripleBuffering) {
_pendingState.mode = graphicsMode;
return true;
}
if (_currentState.mode != _pendingState.mode)
_pendingState.change |= GraphicsState::kScreenAddress;
return false;
// this doesn't seem to be checked anywhere
return true;
}
void AtariGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
@ -313,10 +361,20 @@ void AtariGraphicsManager::initSize(uint width, uint height, const Graphics::Pix
_pendingState.width = width;
_pendingState.height = height;
_pendingState.format = format ? *format : PIXELFORMAT_CLUT8;
if ((_pendingState.width > 0 && _pendingState.height > 0)
&& (_currentState.width != _pendingState.width || _currentState.height != _pendingState.height)) {
_pendingState.change |= GraphicsState::kVideoMode;
}
}
void AtariGraphicsManager::beginGFXTransaction() {
debug("beginGFXTransaction");
// these serve as a flag whether we are launching a game; if not, they will be always zeroed
_pendingState.width = 0;
_pendingState.height = 0;
_pendingState.change &= ~GraphicsState::kVideoMode;
}
OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
@ -324,24 +382,42 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
int error = OSystem::TransactionError::kTransactionSuccess;
if (_pendingState.mode < GraphicsMode::DirectRendering || _pendingState.mode > GraphicsMode::TripleBuffering)
error |= OSystem::TransactionError::kTransactionModeSwitchFailed;
if (_pendingState.format != PIXELFORMAT_CLUT8)
error |= OSystem::TransactionError::kTransactionFormatNotSupported;
if (_pendingState.width > getMaximumScreenWidth() || _pendingState.height > getMaximumScreenHeight())
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
if (_pendingState.width > 0 && _pendingState.height > 0) {
if (_pendingState.width > getMaximumScreenWidth() || _pendingState.height > getMaximumScreenHeight())
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
if (_pendingState.width % 16 != 0 && !hasSuperVidel()) {
warning("Requested width not divisible by 16, please report");
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
if (_pendingState.width % 16 != 0 && !hasSuperVidel()) {
warning("Requested width not divisible by 16, please report");
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
}
}
if (error != OSystem::TransactionError::kTransactionSuccess) {
warning("endGFXTransaction failed: %02x", (int)error);
// all our errors are fatal but engine.cpp takes only this one seriously
// all our errors are fatal as we don't support rollback so make sure that
// initGraphicsAny() fails (note: setupGraphics() doesn't check errors at all)
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
return static_cast<OSystem::TransactionError>(error);
}
// don't exit overlay unless there is real video mode to be set
if (_pendingState.width == 0 || _pendingState.height == 0) {
_ignoreHideOverlay = true;
return OSystem::kTransactionSuccess;
} else if (_overlayVisible) {
// that's it, really. updateScreen() will take care of everything.
_ignoreHideOverlay = false;
_overlayVisible = false;
// if being in the overlay, reset everything (same as hideOverlay() does)
_pendingState.change |= GraphicsState::kAll;
}
_chunkySurface.init(_pendingState.width, _pendingState.height, _pendingState.width,
_chunkySurface.getPixels(), _pendingState.format);
@ -350,18 +426,16 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
_screen[BACK_BUFFER2]->reset(_pendingState.width, _pendingState.height, 8, true);
_workScreen = _screen[_pendingState.mode <= GraphicsMode::SingleBuffering ? FRONT_BUFFER : BACK_BUFFER1];
s_screenSurf = nullptr;
s_shakeXOffset = 0;
s_shakeYOffset = 0;
// in case of resolution change from GUI
if (_oldWorkScreen)
_oldWorkScreen = _workScreen;
_palette.clear();
_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
_pendingState.change |= GraphicsState::kPalette;
// no point of setting this in updateScreen(), it would only complicate code
_currentState = _pendingState;
// currently there is no use for this
_currentState.change = GraphicsState::kNone;
// apply new screen changes
updateScreen();
return OSystem::kTransactionSuccess;
}
@ -387,7 +461,7 @@ void AtariGraphicsManager::setPalette(const byte *colors, uint start, uint num)
}
}
_pendingScreenChange |= kPendingScreenChangePalette;
_pendingState.change |= GraphicsState::kPalette;
}
void AtariGraphicsManager::grabPalette(byte *colors, uint start, uint num) const {
@ -464,6 +538,8 @@ void AtariGraphicsManager::fillScreen(uint32 col) {
}
void AtariGraphicsManager::fillScreen(const Common::Rect &r, uint32 col) {
debug("fillScreen: %dx%d %d", r.width(), r.height(), col);
Graphics::Surface *screen = lockScreen();
if (screen)
screen->fillRect(r, col);
@ -528,26 +604,20 @@ void AtariGraphicsManager::updateScreen() {
assert(_workScreen == _screen[BACK_BUFFER1]);
screenUpdated = updateScreenInternal(_chunkySurface);
break;
default:
warning("Unknown graphics mode %d", (int)_currentState.mode);
}
}
_workScreen->clearDirtyRects();
#ifdef SCREEN_ACTIVE
// first change video mode so we can modify video regs later
if (_pendingScreenChange & kPendingScreenChangeMode) {
if (_workScreen->rez != -1) {
// unfortunately this reinitializes VDI, too
Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _workScreen->rez);
} else if (_workScreen->mode != -1) {
VsetMode(_workScreen->mode);
}
if (!_overlayPending && (_pendingState.width == 0 || _pendingState.height == 0)) {
return;
}
if (_pendingScreenChange & kPendingScreenChangeScreen) {
// calling (V)SetScreen without Vsync() is dangerous (at least on Falcon)
s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf;
} else if (screenUpdated && !isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
if (screenUpdated
&& !isOverlayVisible()
&& _currentState.mode == GraphicsMode::TripleBuffering) {
// Triple buffer:
// - alternate BACK_BUFFER1 and BACK_BUFFER2
// - check if FRONT_BUFFER has been displayed for at least one frame
@ -585,111 +655,125 @@ void AtariGraphicsManager::updateScreen() {
// FRONT_BUFFER is displayed and still contains previously finished frame
}
if (_pendingScreenChange & kPendingScreenChangePalette) {
if (_tt)
EsetPalette(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->tt);
else
VsetRGB(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->falcon);
const GraphicsState oldPendingState = _pendingState;
if (_overlayPending) {
debug("Forcing overlay pending state");
_pendingState.change = GraphicsState::kAll;
}
_pendingScreenChange = kPendingScreenChangeNone;
bool doShrinkVidelVisibleArea = false;
bool doSuperVidelReset = false;
if (_pendingState.change & GraphicsState::kAspectRatioCorrection) {
assert(_workScreen->mode != -1);
if (_oldAspectRatioCorrection != _aspectRatioCorrection) {
if (!isOverlayVisible() && _currentState.height == 200) {
if (_pendingState.aspectRatioCorrection && _currentState.height == 200 && !isOverlayVisible()) {
// apply machine-specific aspect ratio correction
if (!_vgaMonitor) {
short mode = VsetMode(VM_INQUIRE);
if (_aspectRatioCorrection) {
// 60 Hz
mode &= ~PAL;
mode |= NTSC;
} else {
// 50 Hz
mode &= ~NTSC;
mode |= PAL;
}
VsetMode(mode);
} else if (hasSuperVidel() || !_tt) {
if (_aspectRatioCorrection) {
for (int screenId : { FRONT_BUFFER, BACK_BUFFER1, BACK_BUFFER2 }) {
Screen *screen = _screen[screenId];
Graphics::Surface *offsettedSurf = screen->offsettedSurf;
// erase old screen
offsettedSurf->fillRect(Common::Rect(offsettedSurf->w, offsettedSurf->h), 0);
// setup new screen
screen->oldScreenSurfaceWidth = screen->surf.w;
screen->oldScreenSurfaceHeight = screen->surf.h;
screen->oldScreenSurfacePitch = screen->surf.pitch;
screen->oldOffsettedSurfaceWidth = offsettedSurf->w;
screen->oldOffsettedSurfaceHeight = offsettedSurf->h;
screen->surf.w = 320 + 2 * MAX_HZ_SHAKE;
screen->surf.h = 200 + 2 * MAX_V_SHAKE;
screen->surf.pitch = screen->surf.w;
offsettedSurf->init(
320, 200, screen->surf.pitch,
screen->surf.getBasePtr((screen->surf.w - 320) / 2, (screen->surf.h - 200) / 2),
screen->surf.format);
screen->addDirtyRect(*lockScreen(), Common::Rect(offsettedSurf->w, offsettedSurf->h), _currentState.mode == GraphicsMode::DirectRendering);
}
Supexec(shrinkVidelVisibleArea);
} else {
for (int screenId : { FRONT_BUFFER, BACK_BUFFER1, BACK_BUFFER2 }) {
Screen *screen = _screen[screenId];
Graphics::Surface *offsettedSurf = screen->offsettedSurf;
assert(screen->oldScreenSurfaceWidth != -1);
assert(screen->oldScreenSurfaceHeight != -1);
assert(screen->oldScreenSurfacePitch != -1);
assert(screen->oldOffsettedSurfaceWidth != -1);
assert(screen->oldOffsettedSurfaceHeight != -1);
// erase old screen
offsettedSurf->fillRect(Common::Rect(offsettedSurf->w, offsettedSurf->h), 0);
// setup new screen
screen->surf.w = screen->oldScreenSurfaceWidth;
screen->surf.h = screen->oldScreenSurfaceHeight;
screen->surf.pitch = screen->oldScreenSurfacePitch;
offsettedSurf->init(
screen->oldOffsettedSurfaceWidth, screen->oldOffsettedSurfaceHeight, screen->surf.pitch,
screen->surf.getBasePtr(
(screen->surf.w - screen->oldOffsettedSurfaceWidth) / 2,
(screen->surf.h - screen->oldOffsettedSurfaceHeight) / 2),
screen->surf.format);
screen->oldScreenSurfaceWidth = -1;
screen->oldScreenSurfaceHeight = -1;
screen->oldScreenSurfacePitch = -1;
screen->oldOffsettedSurfaceWidth = -1;
screen->oldOffsettedSurfaceHeight = -1;
screen->addDirtyRect(*lockScreen(), Common::Rect(offsettedSurf->w, offsettedSurf->h), _currentState.mode == GraphicsMode::DirectRendering);
}
if (hasSuperVidel())
VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480
VsetMode(_workScreen->mode);
}
_workScreen->mode &= ~PAL;
// 60 Hz
_workScreen->mode |= NTSC;
_pendingState.change |= GraphicsState::kVideoMode;
} else {
// TODO: some tricks with TT's 480 lines?
Screen *screen = _screen[FRONT_BUFFER];
s_aspectRatioCorrectionYOffset = (screen->surf.h - 2*MAX_V_SHAKE - screen->offsettedSurf->h) / 2;
_pendingState.change |= GraphicsState::kShakeScreen;
if (_pendingState.change & GraphicsState::kVideoMode)
doShrinkVidelVisibleArea = true;
else
s_shrinkVidelVisibleArea = true;
}
} else {
// reset back to default mode
if (!_vgaMonitor) {
_workScreen->mode &= ~NTSC;
// 50 Hz
_workScreen->mode |= PAL;
_pendingState.change |= GraphicsState::kVideoMode;
} else {
s_aspectRatioCorrectionYOffset = 0;
s_shrinkVidelVisibleArea = false;
if (hasSuperVidel())
doSuperVidelReset = true;
_pendingState.change |= GraphicsState::kVideoMode;
}
}
_pendingState.change &= ~GraphicsState::kAspectRatioCorrection;
}
#ifdef SCREEN_ACTIVE
if (_pendingState.change & GraphicsState::kVideoMode) {
if (_workScreen->rez != -1) {
// unfortunately this reinitializes VDI, too
Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _workScreen->rez);
// strictly speaking, this is necessary only if kScreenAddress is set but makes code easier
static uint16 black[256];
// Vsync(); // done by Setscreen() above
EsetPalette(0, isOverlayVisible() ? 16 : 256, black);
} else if (_workScreen->mode != -1) {
// VsetMode() must be called first: it resets all hz/v, scrolling and line width registers
// so even if kScreenAddress wasn't scheduled, we have to set new s_screenSurf to refresh them
static _RGB black[256];
VsetRGB(0, 256, black);
// Vsync(); // done by (either) VsetMode() below
if (doSuperVidelReset) {
VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480
doSuperVidelReset = false;
}
_oldAspectRatioCorrection = _aspectRatioCorrection;
_pendingScreenChange |= kPendingScreenChangeScreen;
updateScreen();
} else {
// ignore new value in overlay
_aspectRatioCorrection = _oldAspectRatioCorrection;
debug("VsetMode: %04x", _workScreen->mode);
VsetMode(_workScreen->mode);
}
// due to implied Vsync() above
assert(s_screenSurf == nullptr);
// refresh Videl register settings
s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf;
s_shrinkVidelVisibleArea = doShrinkVidelVisibleArea;
// keep kVideoMode for resetting the palette later
_pendingState.change &= ~(GraphicsState::kScreenAddress | GraphicsState::kShakeScreen);
}
if (_pendingState.change & GraphicsState::kScreenAddress) {
// takes effect in the nearest VBL interrupt but we always wait for Vsync() in this case
Vsync();
assert(s_screenSurf == nullptr);
s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf;
_pendingState.change &= ~GraphicsState::kScreenAddress;
}
if (_pendingState.change & GraphicsState::kShakeScreen) {
// takes effect in the nearest VBL interrupt
if (!s_screenSurf)
s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf;
_pendingState.change &= ~GraphicsState::kShakeScreen;
}
if (_pendingState.change & (GraphicsState::kVideoMode | GraphicsState::kPalette)) {
if (!_tt) {
// takes effect in the nearest VBL interrupt
VsetRGB(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->falcon);
} else {
// takes effect immediatelly (it's possible that Vsync() hasn't been called: that's expected,
// don't cripple framerate only for a palette change)
EsetPalette(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->tt);
}
_pendingState.change &= ~(GraphicsState::kVideoMode | GraphicsState::kPalette);
}
#endif
if (_overlayPending) {
_pendingState = oldPendingState;
_overlayPending = false;
}
//debug("end of updateScreen");
}
@ -704,12 +788,11 @@ void AtariGraphicsManager::setShakePos(int shakeXOffset, int shakeYOffset) {
s_shakeYOffset = shakeYOffset;
}
_pendingScreenChange |= kPendingScreenChangeScreen;
updateScreen();
_pendingState.change |= GraphicsState::kShakeScreen;
}
void AtariGraphicsManager::showOverlay(bool inGUI) {
debug("showOverlay");
debug("showOverlay (visible: %d)", _overlayVisible);
if (_overlayVisible)
return;
@ -722,34 +805,38 @@ void AtariGraphicsManager::showOverlay(bool inGUI) {
_workScreen = _screen[OVERLAY_BUFFER];
// do not cache dirtyRects and oldCursorRect
const int bitsPerPixel = getBitsPerPixel(getOverlayFormat());
static bool resetCursorPosition = true;
_workScreen->reset(getOverlayWidth(), getOverlayHeight(), bitsPerPixel, resetCursorPosition);
resetCursorPosition = false;
_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
_workScreen->reset(getOverlayWidth(), getOverlayHeight(), getBitsPerPixel(getOverlayFormat()), false);
_overlayVisible = true;
assert(_pendingState.change == GraphicsState::kNone);
_overlayPending = true;
updateScreen();
}
void AtariGraphicsManager::hideOverlay() {
debug("hideOverlay");
debug("hideOverlay (ignore: %d, visible: %d)", _ignoreHideOverlay, _overlayVisible);
if (!_overlayVisible)
return;
if (_ignoreHideOverlay) {
// faster than _workScreen->reset()
_workScreen->clearDirtyRects();
_workScreen->cursor.reset();
return;
}
_workScreen = _oldWorkScreen;
_oldWorkScreen = nullptr;
// FIXME: perhaps there's a better way but this will do for now
_checkUnalignedPitch = true;
_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
_overlayVisible = false;
assert(_pendingState.change == GraphicsState::kNone);
_pendingState.change = GraphicsState::kAll;
updateScreen();
}
@ -935,14 +1022,36 @@ void AtariGraphicsManager::updateMousePosition(int deltaX, int deltaY) {
}
bool AtariGraphicsManager::notifyEvent(const Common::Event &event) {
if (event.type != Common::EVENT_CUSTOM_BACKEND_ACTION_START) {
return false;
}
switch (event.type) {
case Common::EVENT_RETURN_TO_LAUNCHER:
case Common::EVENT_QUIT:
if (isOverlayVisible()) {
_ignoreHideOverlay = true;
return false;
}
break;
switch ((CustomEventAction) event.customType) {
case kActionToggleAspectRatioCorrection:
_aspectRatioCorrection = !_aspectRatioCorrection;
return true;
case Common::EVENT_CUSTOM_BACKEND_ACTION_START:
switch ((CustomEventAction) event.customType) {
case kActionToggleAspectRatioCorrection:
if (hasFeature(OSystem::Feature::kFeatureAspectRatioCorrection)) {
_pendingState.aspectRatioCorrection = !_pendingState.aspectRatioCorrection;
if (_currentState.aspectRatioCorrection != _pendingState.aspectRatioCorrection) {
_pendingState.change |= GraphicsState::kAspectRatioCorrection;
// would be updated in updateScreen() anyway
_currentState.aspectRatioCorrection = _pendingState.aspectRatioCorrection;
updateScreen();
}
return true;
}
break;
}
break;
default:
return false;
}
return false;
@ -967,7 +1076,9 @@ void AtariGraphicsManager::allocateSurfaces() {
_screen[i] = new Screen(this, getMaximumScreenWidth(), getMaximumScreenHeight(), PIXELFORMAT_CLUT8, &_palette);
}
_screen[OVERLAY_BUFFER] = new Screen(this, getOverlayWidth(), getOverlayHeight(), getOverlayFormat(), &_overlayPalette);
// overlay is the default screen upon start
_workScreen = _screen[OVERLAY_BUFFER] = new Screen(this, getOverlayWidth(), getOverlayHeight(), getOverlayFormat(), &_overlayPalette);
_workScreen->reset(getOverlayWidth(), getOverlayHeight(), getBitsPerPixel(getOverlayFormat()), true);
_chunkySurface.create(getMaximumScreenWidth(), getMaximumScreenHeight(), PIXELFORMAT_CLUT8);
_overlaySurface.create(getOverlayWidth(), getOverlayHeight(), getOverlayFormat());

View File

@ -112,12 +112,24 @@ protected:
void freeSurfaces();
private:
enum class GraphicsMode : int {
Unknown = -1,
DirectRendering = 0,
SingleBuffering = 1,
TripleBuffering = 3
};
enum CustomEventAction {
kActionToggleAspectRatioCorrection = 100,
};
#ifndef DISABLE_FANCY_THEMES
int16 getMaximumScreenHeight() const { return 480; }
int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 640 : 640*1.2); }
#else
int16 getMaximumScreenHeight() const { return _tt ? 480 : 240; }
int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 320 : 320*1.2); }
#endif
bool updateScreenInternal(const Graphics::Surface &srcSurface);
@ -162,38 +174,28 @@ private:
bool _vgaMonitor = true;
bool _tt = false;
bool _aspectRatioCorrection = false;
bool _oldAspectRatioCorrection = false;
bool _checkUnalignedPitch = false;
enum class GraphicsMode : int {
DirectRendering = 0,
SingleBuffering = 1,
TripleBuffering = 3
};
struct GraphicsState {
GraphicsState(GraphicsMode mode_)
: mode(mode_)
, width(0)
, height(0) {
}
GraphicsMode mode;
int width;
int height;
GraphicsMode mode = GraphicsMode::Unknown;
int width = 0;
int height = 0;
Graphics::PixelFormat format;
};
GraphicsState _pendingState{ (GraphicsMode)getDefaultGraphicsMode() };
GraphicsState _currentState{ (GraphicsMode)getDefaultGraphicsMode() };
bool aspectRatioCorrection = false;
enum PendingScreenChange {
kPendingScreenChangeNone = 0,
kPendingScreenChangeMode = 1<<0,
kPendingScreenChangeScreen = 1<<1,
kPendingScreenChangePalette = 1<<2
enum PendingScreenChange {
kNone = 0,
kVideoMode = 1<<0,
kScreenAddress = 1<<1,
kPalette = 1<<2,
kAspectRatioCorrection = 1<<3,
kShakeScreen = 1<<4,
kAll = kVideoMode | kScreenAddress | kPalette | kAspectRatioCorrection | kShakeScreen,
};
int change = kNone;
};
int _pendingScreenChange = kPendingScreenChangeNone;
GraphicsState _pendingState;
GraphicsState _currentState;
enum {
FRONT_BUFFER,
@ -208,7 +210,9 @@ private:
Graphics::Surface _chunkySurface;
bool _overlayVisible = false;
bool _overlayVisible = true;
bool _overlayPending = true;
bool _ignoreHideOverlay = true;
Graphics::Surface _overlaySurface;
Palette _palette;

View File

@ -45,11 +45,14 @@ class Palette {
public:
void clear() {
memset(data, 0, sizeof(data));
entries = 0;
}
uint16 *const tt = reinterpret_cast<uint16*>(data);
_RGB *const falcon = reinterpret_cast<_RGB*>(data);
int entries = 0;
private:
byte data[256*4] = {};
};
@ -80,12 +83,6 @@ struct Screen {
int mode = -1;
Graphics::Surface *const offsettedSurf = &_offsettedSurf;
int oldScreenSurfaceWidth = -1;
int oldScreenSurfaceHeight = -1;
int oldScreenSurfacePitch = -1;
int oldOffsettedSurfaceWidth = -1;
int oldOffsettedSurfaceHeight = -1;
private:
static constexpr size_t ALIGN = 16; // 16 bytes

View File

@ -25,13 +25,11 @@
#include <mint/falcon.h>
#include <mint/osbind.h>
#include <mint/ostruct.h>
#include <usound.h> // https://github.com/mikrosk/usound
#include "common/config-manager.h"
#include "common/debug.h"
// see https://github.com/mikrosk/atari_sound_setup
#include "../../../../atari_sound_setup.git/atari_sound_setup.h"
#define DEFAULT_OUTPUT_RATE 24585
#define DEFAULT_OUTPUT_CHANNELS 2
#define DEFAULT_SAMPLES 2048 // 83ms

View File

@ -59,6 +59,8 @@
#include "base/main.h"
#include "gui/debugger.h"
#define INPUT_ACTIVE
/*
* Include header files needed for the getFilesystemFactory() method.
*/
@ -77,7 +79,6 @@ extern "C" volatile uint32 counter_200hz;
extern void nf_init(void);
extern void nf_print(const char* msg);
static bool s_tt = false;
static int s_app_id = -1;
static bool exit_already_called = false;
@ -89,12 +90,9 @@ static void critical_restore() {
AtariAudioShutdown();
AtariGraphicsShutdown();
if (s_tt)
Supexec(asm_screen_tt_restore);
else
Supexec(asm_screen_falcon_restore);
Supexec(atari_200hz_shutdown);
#ifdef INPUT_ACTIVE
if (atari_old_kbdvec && atari_old_mousevec) {
_KBDVECS *kbdvecs = Kbdvbase();
((uintptr *)kbdvecs)[-1] = (uintptr)atari_old_kbdvec;
@ -110,6 +108,7 @@ static void critical_restore() {
// ok, restore mouse cursor at least
graf_mouse(M_ON, NULL);
}
#endif
}
// called on normal program termination (via exit() or returning from main())
@ -141,8 +140,6 @@ OSystem_Atari::OSystem_Atari() {
exit(EXIT_FAILURE);
}
s_tt = (vdo == VDO_TT);
enum {
MCH_ST = 0,
MCH_STE,
@ -161,23 +158,18 @@ OSystem_Atari::OSystem_Atari() {
exit(EXIT_FAILURE);
}
#ifdef INPUT_ACTIVE
_KBDVECS *kbdvecs = Kbdvbase();
atari_old_kbdvec = (KBDVEC)(((uintptr *)kbdvecs)[-1]);
atari_old_mousevec = kbdvecs->mousevec;
((uintptr *)kbdvecs)[-1] = (uintptr)atari_kbdvec;
kbdvecs->mousevec = atari_mousevec;
#endif
Supexec(atari_200hz_init);
_timerInitialized = true;
if (s_tt)
Supexec(asm_screen_tt_save);
else
Supexec(asm_screen_falcon_save);
_videoInitialized = true;
// protect against sudden exit()
atexit(exit_restore);
// protect against sudden crash
@ -210,16 +202,6 @@ OSystem_Atari::~OSystem_Atari() {
delete _fsFactory;
_fsFactory = nullptr;
if (_videoInitialized) {
if (s_tt)
Supexec(asm_screen_tt_restore);
else {
Supexec(asm_screen_falcon_restore);
}
_videoInitialized = false;
}
if (_timerInitialized) {
Supexec(atari_200hz_shutdown);
_timerInitialized = false;
@ -273,9 +255,11 @@ void OSystem_Atari::initBackend() {
_vdi_width = work_out[0] + 1;
_vdi_height = work_out[1] + 1;
#ifdef INPUT_ACTIVE
graf_mouse(M_OFF, NULL);
// see https://github.com/freemint/freemint/issues/312
//wind_update(BEG_UPDATE);
#endif
}
_timerManager = new DefaultTimerManager();

View File

@ -51,7 +51,6 @@ public:
private:
long _startTime;
bool _videoInitialized = false;
bool _timerInitialized = false;
int16 _vdi_handle;

View File

@ -305,11 +305,6 @@ Yes, it's a hack. :) Owners of a CRT monitor can achieve the same effect by the
analog knobs -- stretch and move the 320x200 picture unless black borders are no
longer visible. This hack provides a more elegant and per-game functionality.
Realtime aspect ratio correction (CTRL+ALT+a) should be used with caution in
Direct rendering mode because there's no way to refresh the screen. So if you
change the setting and there isn't any game screen update coming, screen will
stay black.
Audio mixing
------------

View File

@ -347,7 +347,7 @@ static void setupGraphics(OSystem &system) {
system.setScaler(ConfMan.get("scaler").c_str(), ConfMan.getInt("scale_factor"));
system.setShader(ConfMan.getPath("shader"));
#if defined(OPENDINGUX) || defined(MIYOO) || defined(MIYOOMINI)
#if defined(OPENDINGUX) || defined(MIYOO) || defined(MIYOOMINI) || defined(ATARI)
// 0, 0 means "autodetect" but currently only SDL supports
// it and really useful only on Opendingux. When more platforms
// support it we will switch to it.