[Scummvm-git-logs] scummvm master -> 30670e0a46b234808d8e44f4978f4afd0e2bea20

mikrosk noreply at scummvm.org
Mon Jul 15 17:43:42 UTC 2024


This automated email contains information about 13 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
c6e0584ed8 BACKENDS: ATARI: Enable HE games (using bink)
ee3421b283 BACKENDS: SDL: Fix a crash
08bd51f38a BACKENDS: ATARI: Fix a buffer overflow
3482685a07 BACKENDS: ATARI: Complete Eiffel support
b34af00210 BACKENDS: ATARI: Silence some of dlmalloc warnings
e9c7c8e03c JANITORIAL: atari_c2p-asm -> atari-c2p-asm
957aa15e7e BACKENDS: ATARI: Move Cursor outside AtariGraphicsManager
c7bc03d4f4 BACKENDS: ATARI: Move Screen outside AtariGraphicsManager
0c77d67845 BACKENDS: ATARI: Make Cursor Screen's property
dd49c9e3ce BACKENDS: ATARI: QoL improvements
4c98561eec BACKENDS: ATARI: Move timer interrupt to ossystem_atari.cpp
7dabb755dd BACKENDS: ATARI: Fixes to build scripts
30670e0a46 BACKENDS: ATARI: Add support for -mfastcall


Commit: c6e0584ed85391f2ceda77b3d9e141a29a24a4e6
    https://github.com/scummvm/scummvm/commit/c6e0584ed85391f2ceda77b3d9e141a29a24a4e6
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:50+02:00

Commit Message:
BACKENDS: ATARI: Enable HE games (using bink)

It's doesn't cost that much (400k) and it's reasonably fast on CT60.

Changed paths:
    backends/platform/atari/build-release.sh


diff --git a/backends/platform/atari/build-release.sh b/backends/platform/atari/build-release.sh
index 856169a0fe1..dbbd47ea15c 100755
--- a/backends/platform/atari/build-release.sh
+++ b/backends/platform/atari/build-release.sh
@@ -30,7 +30,6 @@ then
 	--disable-translation \
 	--disable-eventrecorder \
 	--disable-tts \
-	--disable-bink \
 	--opengl-mode=none \
 	--enable-verbose-build \
 	--enable-text-console \


Commit: ee3421b283eee9b9ed292b9695b3edf6e347be50
    https://github.com/scummvm/scummvm/commit/ee3421b283eee9b9ed292b9695b3edf6e347be50
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:50+02:00

Commit Message:
BACKENDS: SDL: Fix a crash

Returning Common::Path(0) in case that neither $HOME or $XDG_CONFIG_HOME
is found leads to a crash in Common::FSNode as it is not prepared to
handle such an object.

Changed paths:
    backends/platform/sdl/posix/posix.cpp


diff --git a/backends/platform/sdl/posix/posix.cpp b/backends/platform/sdl/posix/posix.cpp
index d406eb7f77d..f2330f7fc4c 100644
--- a/backends/platform/sdl/posix/posix.cpp
+++ b/backends/platform/sdl/posix/posix.cpp
@@ -140,13 +140,11 @@ Common::Path OSystem_POSIX::getDefaultConfigFileName() {
 	envVar = getenv("XDG_CONFIG_HOME");
 	if (!envVar || !*envVar) {
 		envVar = getenv("HOME");
-		if (!envVar) {
-			return 0;
-		}
-
-		if (Posix::assureDirectoryExists(".config", envVar)) {
-			prefix = envVar;
-			prefix += "/.config";
+		if (envVar && *envVar) {
+			if (Posix::assureDirectoryExists(".config", envVar)) {
+				prefix = envVar;
+				prefix += "/.config";
+			}
 		}
 	} else {
 		prefix = envVar;


Commit: 08bd51f38a84b799bcb4cfde31937abb59942ebb
    https://github.com/scummvm/scummvm/commit/08bd51f38a84b799bcb4cfde31937abb59942ebb
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:50+02:00

Commit Message:
BACKENDS: ATARI: Fix a buffer overflow

Apparently 1024 characters is not enough for everyone.

Changed paths:
    backends/platform/atari/osystem_atari.cpp


diff --git a/backends/platform/atari/osystem_atari.cpp b/backends/platform/atari/osystem_atari.cpp
index 8547b00db03..5b3a9cd7ec0 100644
--- a/backends/platform/atari/osystem_atari.cpp
+++ b/backends/platform/atari/osystem_atari.cpp
@@ -381,7 +381,7 @@ void OSystem_Atari::logMessage(LogMessageType::Type type, const char *message) {
 		output = stderr;
 
 	static char str[1024+1];
-	sprintf(str, "[%08d] %s", getMillis(), message);
+	snprintf(str, sizeof(str), "[%08d] %s", getMillis(), message);
 
 	fputs(str, output);
 	fflush(output);


Commit: 3482685a075b0982520fb1e66d8c295540d94448
    https://github.com/scummvm/scummvm/commit/3482685a075b0982520fb1e66d8c295540d94448
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:50+02:00

Commit Message:
BACKENDS: ATARI: Complete Eiffel support

Also, pass proper keycode of the modifier keys instead of
KEYCODE_INVALID.

Changed paths:
    backends/events/atari/atari-events.cpp
    backends/events/atari/atari-events.h


diff --git a/backends/events/atari/atari-events.cpp b/backends/events/atari/atari-events.cpp
index ae6d1d46b93..ce848acbebe 100644
--- a/backends/events/atari/atari-events.cpp
+++ b/backends/events/atari/atari-events.cpp
@@ -60,7 +60,12 @@ AtariEventSource::AtariEventSource() {
 	_scancodeToKeycode[0x0e] = Common::KEYCODE_BACKSPACE;
 	_scancodeToKeycode[0x0f] = Common::KEYCODE_TAB;
 	_scancodeToKeycode[0x1c] = Common::KEYCODE_RETURN;
+	_scancodeToKeycode[0x1d] = Common::KEYCODE_LCTRL;	// Eiffel doesn't recognise RCTRL
+	_scancodeToKeycode[0x2a] = Common::KEYCODE_LSHIFT;
+	_scancodeToKeycode[0x36] = Common::KEYCODE_RSHIFT;
+	_scancodeToKeycode[0x38] = Common::KEYCODE_LALT;	// Eiffel doesn't recognise RALT
 	_scancodeToKeycode[0x39] = Common::KEYCODE_SPACE;
+	_scancodeToKeycode[0x3a] = Common::KEYCODE_CAPSLOCK;
 	_scancodeToKeycode[0x3b] = Common::KEYCODE_F1;
 	_scancodeToKeycode[0x3c] = Common::KEYCODE_F2;
 	_scancodeToKeycode[0x3d] = Common::KEYCODE_F3;
@@ -75,19 +80,24 @@ AtariEventSource::AtariEventSource() {
 	_scancodeToKeycode[0x46] = Common::KEYCODE_PAGEDOWN;	// Eiffel only
 	_scancodeToKeycode[0x47] = Common::KEYCODE_HOME;
 	_scancodeToKeycode[0x48] = Common::KEYCODE_UP;
+	_scancodeToKeycode[0x49] = Common::KEYCODE_PRINT;	// Eiffel only
 	_scancodeToKeycode[0x4a] = Common::KEYCODE_KP_MINUS;
 	_scancodeToKeycode[0x4b] = Common::KEYCODE_LEFT;
-	_scancodeToKeycode[0x4c] = Common::KEYCODE_LMETA;
+	_scancodeToKeycode[0x4c] = Common::KEYCODE_SCROLLOCK;	// Eiffel only (on Milan: AltGr!)
 	_scancodeToKeycode[0x4d] = Common::KEYCODE_RIGHT;
 	_scancodeToKeycode[0x4e] = Common::KEYCODE_KP_PLUS;
 	_scancodeToKeycode[0x4f] = Common::KEYCODE_PAUSE;	// Eiffel only
 	_scancodeToKeycode[0x50] = Common::KEYCODE_DOWN;
 	_scancodeToKeycode[0x52] = Common::KEYCODE_INSERT;
 	_scancodeToKeycode[0x53] = Common::KEYCODE_DELETE;
+	_scancodeToKeycode[0x54] = Common::KEYCODE_NUMLOCK;	// Eiffel only
 	_scancodeToKeycode[0x55] = Common::KEYCODE_END;	// Eiffel only
+	_scancodeToKeycode[0x56] = Common::KEYCODE_LMETA;	// Eiffel only
+	_scancodeToKeycode[0x57] = Common::KEYCODE_RMETA;	// Eiffel only
+	_scancodeToKeycode[0x58] = Common::KEYCODE_MENU;	// Eiffel only
 	_scancodeToKeycode[0x5b] = Common::KEYCODE_TILDE;	// Eiffel only
-	_scancodeToKeycode[0x61] = Common::KEYCODE_F12;	// UNDO
-	_scancodeToKeycode[0x62] = Common::KEYCODE_F11;	// HELP
+	_scancodeToKeycode[0x61] = Common::KEYCODE_F12;	// UNDO (there's also Common::KEYCODE_UNDO available...)
+	_scancodeToKeycode[0x62] = Common::KEYCODE_F11;	// HELP (there's also Common::KEYCODE_HELP available...)
 	_scancodeToKeycode[0x63] = Common::KEYCODE_LEFTPAREN;	// KEYPAD (
 	_scancodeToKeycode[0x64] = Common::KEYCODE_RIGHTPAREN;	// KEYPAD )
 	_scancodeToKeycode[0x65] = Common::KEYCODE_KP_DIVIDE;	// KEYPAD /
@@ -177,6 +187,27 @@ bool AtariEventSource::pollEvent(Common::Event &event) {
 		if (scancode == 0x3a && pressed)
 			_capslockActive = !_capslockActive;
 
+		if (scancode == 0x4c && pressed)
+			_scrolllockActive = !_scrolllockActive;
+
+		if (scancode == 0x54 && pressed)
+			_numlockActive = !_numlockActive;
+
+		// Eiffel only
+		if (scancode == 0x37) {
+			if (pressed) {
+				_mmbDown = true;
+				event.type = Common::EVENT_MBUTTONDOWN;
+				event.mouse = _graphicsManager->getMousePosition();
+				return true;
+			} else if (_mmbDown) {
+				_mmbDown = false;
+				event.type = Common::EVENT_MBUTTONUP;
+				event.mouse = _graphicsManager->getMousePosition();
+				return true;
+			}
+		}
+
 		// Eiffel only
 		if (scancode == 0x59) {
 			event.type = Common::EVENT_WHEELUP;
@@ -269,6 +300,8 @@ bool AtariEventSource::pollEvent(Common::Event &event) {
 		event.kbd.flags |= _altActive ? Common::KBD_ALT : 0;
 		event.kbd.flags |= (_lshiftActive || _rshiftActive) ? Common::KBD_SHIFT : 0;
 		event.kbd.flags |= _capslockActive ? Common::KBD_CAPS : 0;
+		event.kbd.flags |= _scrolllockActive ? Common::KBD_SCRL : 0;
+		event.kbd.flags |= _numlockActive ? Common::KBD_NUM : 0;
 
 		return true;
 	}
diff --git a/backends/events/atari/atari-events.h b/backends/events/atari/atari-events.h
index 38c29019090..cb12fa258dc 100644
--- a/backends/events/atari/atari-events.h
+++ b/backends/events/atari/atari-events.h
@@ -45,6 +45,7 @@ private:
 	AtariGraphicsManager *_graphicsManager = nullptr;
 
 	bool _lmbDown = false;
+	bool _mmbDown = false;
 	bool _rmbDown = false;
 
 	bool _lshiftActive = false;
@@ -52,6 +53,8 @@ private:
 	bool _ctrlActive = false;
 	bool _altActive = false;
 	bool _capslockActive = false;
+	bool _scrolllockActive = false;
+	bool _numlockActive = false;
 
 	byte _unshiftToAscii[128];
 	byte _shiftToAscii[128];


Commit: b34af00210be49daf32f7862d149e7e8571f3086
    https://github.com/scummvm/scummvm/commit/b34af00210be49daf32f7862d149e7e8571f3086
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:50+02:00

Commit Message:
BACKENDS: ATARI: Silence some of dlmalloc warnings

They were printed every time something has been changed in
atari-graphics.cpp.

Changed paths:
    backends/platform/atari/dlmalloc.h


diff --git a/backends/platform/atari/dlmalloc.h b/backends/platform/atari/dlmalloc.h
index eb8232c946d..0ac3fbf360f 100644
--- a/backends/platform/atari/dlmalloc.h
+++ b/backends/platform/atari/dlmalloc.h
@@ -31,6 +31,12 @@
 #define MSPACES 1
 #define MALLOC_ALIGNMENT ((size_t)16U) /* 16B cache line */
 
+#pragma GCC diagnostic push
+/* warning: 'mallinfo mallinfo()' hides constructor for 'struct mallinfo' [-Wshadow] */
+#pragma GCC diagnostic ignored "-Wshadow"
+/* warning: this use of "defined" may not be portable [-Wexpansion-to-defined] */
+#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
+
 /*
 Copyright 2023 Doug Lea
 
@@ -1461,4 +1467,6 @@ DLMALLOC_EXPORT int mspace_mallopt(int, int);
 }  /* end of extern "C" */
 #endif /* __cplusplus */
 
+#pragma GCC diagnostic pop
+
 #endif /* PLATFORM_ATARI_DLMALLOC_H */


Commit: e9c7c8e03c9270b2bd33cd7761a416a24f5fe96e
    https://github.com/scummvm/scummvm/commit/e9c7c8e03c9270b2bd33cd7761a416a24f5fe96e
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:50+02:00

Commit Message:
JANITORIAL: atari_c2p-asm -> atari-c2p-asm

Changed paths:
  A backends/graphics/atari/atari-c2p-asm.S
  A backends/graphics/atari/atari-c2p-asm.h
  R backends/graphics/atari/atari_c2p-asm.S
  R backends/graphics/atari/atari_c2p-asm.h
    backends/graphics/atari/atari-graphics-videl.h
    backends/module.mk


diff --git a/backends/graphics/atari/atari_c2p-asm.S b/backends/graphics/atari/atari-c2p-asm.S
similarity index 100%
rename from backends/graphics/atari/atari_c2p-asm.S
rename to backends/graphics/atari/atari-c2p-asm.S
diff --git a/backends/graphics/atari/atari_c2p-asm.h b/backends/graphics/atari/atari-c2p-asm.h
similarity index 100%
rename from backends/graphics/atari/atari_c2p-asm.h
rename to backends/graphics/atari/atari-c2p-asm.h
diff --git a/backends/graphics/atari/atari-graphics-videl.h b/backends/graphics/atari/atari-graphics-videl.h
index 584b699c01f..862409b91f1 100644
--- a/backends/graphics/atari/atari-graphics-videl.h
+++ b/backends/graphics/atari/atari-graphics-videl.h
@@ -24,8 +24,8 @@
 
 #include "backends/graphics/atari/atari-graphics.h"
 
+#include "backends/graphics/atari/atari-c2p-asm.h"
 #include "backends/graphics/atari/atari-graphics-asm.h"
-#include "backends/graphics/atari/atari_c2p-asm.h"
 #include "common/system.h"
 
 class AtariVidelManager : public AtariGraphicsManager {
diff --git a/backends/module.mk b/backends/module.mk
index 5d885c96ca0..cdbe853f735 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -367,7 +367,7 @@ endif
 ifeq ($(BACKEND),atari)
 MODULE_OBJS += \
 	events/atari/atari-events.o \
-	graphics/atari/atari_c2p-asm.o \
+	graphics/atari/atari-c2p-asm.o \
 	graphics/atari/atari-graphics.o \
 	graphics/atari/atari-graphics-asm.o \
 	mixer/atari/atari-mixer.o


Commit: 957aa15e7e7b520ebf9aca8e73785f7a8cd42670
    https://github.com/scummvm/scummvm/commit/957aa15e7e7b520ebf9aca8e73785f7a8cd42670
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:51+02:00

Commit Message:
BACKENDS: ATARI: Move Cursor outside AtariGraphicsManager

Changed paths:
  A backends/graphics/atari/atari-cursor.cpp
  A backends/graphics/atari/atari-cursor.h
    backends/graphics/atari/atari-graphics.cpp
    backends/graphics/atari/atari-graphics.h
    backends/module.mk


diff --git a/backends/graphics/atari/atari-cursor.cpp b/backends/graphics/atari/atari-cursor.cpp
new file mode 100644
index 00000000000..dab54c911b9
--- /dev/null
+++ b/backends/graphics/atari/atari-cursor.cpp
@@ -0,0 +1,150 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "atari-cursor.h"
+
+#include <cassert>
+
+extern bool g_unalignedPitch;
+
+void Cursor::update(const Graphics::Surface &screen, bool isModified) {
+	if (!_buf) {
+		outOfScreen = true;
+		return;
+	}
+
+	if (!visible || !isModified)
+		return;
+
+	srcRect = Common::Rect(_width, _height);
+
+	dstRect = Common::Rect(
+		_x - _hotspotX,	// left
+		_y - _hotspotY,	// top
+		_x - _hotspotX + _width,	// right
+		_y - _hotspotY + _height);	// bottom
+
+	outOfScreen = !screen.clip(srcRect, dstRect);
+
+	assert(srcRect.width() == dstRect.width());
+	assert(srcRect.height() == dstRect.height());
+}
+
+void Cursor::updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen) {
+	_x += deltaX;
+	_y += deltaY;
+
+	if (_x < 0)
+		_x = 0;
+	else if (_x >= screen.w)
+		_x = screen.w - 1;
+
+	if (_y < 0)
+		_y = 0;
+	else if (_y >= screen.h)
+		_y = screen.h - 1;
+}
+
+void Cursor::setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor) {
+	if (w == 0 || h == 0 || buf == nullptr) {
+		_buf = nullptr;
+		return;
+	}
+
+	_buf = (const byte *)buf;
+	_width = w;
+	_height = h;
+	_hotspotX = hotspotX;
+	_hotspotY = hotspotY;
+	_keycolor = keycolor;
+}
+
+void Cursor::convertTo(const Graphics::PixelFormat &format) {
+	const int cursorWidth = (srcRect.width() + 15) & (-16);
+	const int cursorHeight = _height;
+	const bool isCLUT8 = format.isCLUT8();
+
+	if (surface.w != cursorWidth || surface.h != cursorHeight || surface.format != format) {
+		if (!isCLUT8 && surface.format != format) {
+			_rShift = format.rLoss - format.rShift;
+			_gShift = format.gLoss - format.gShift;
+			_bShift = format.bLoss - format.bShift;
+
+			_rMask = format.rMax() << format.rShift;
+			_gMask = format.gMax() << format.gShift;
+			_bMask = format.bMax() << format.bShift;
+		}
+
+		surface.create(cursorWidth, cursorHeight, format);
+
+		const bool old_unalignedPitch = g_unalignedPitch;
+		g_unalignedPitch = true;
+		surfaceMask.create(surface.w / 8, surface.h, format);	// 1 bpl
+		g_unalignedPitch = old_unalignedPitch;
+	}
+
+	const int srcRectWidth = srcRect.width();
+
+	const byte *src = _buf + srcRect.left;
+	byte *dst = (byte *)surface.getPixels();
+	uint16 *dstMask = (uint16 *)surfaceMask.getPixels();
+	const int srcPadding = _width - srcRectWidth;
+	const int dstPadding = surface.w - srcRectWidth;
+
+	for (int j = 0; j < cursorHeight; ++j) {
+		for (int i = 0; i < srcRectWidth; ++i) {
+			const uint32 color = *src++;
+			const uint16 bit = 1 << (15 - (i % 16));
+
+			if (color != _keycolor) {
+				if (!isCLUT8) {
+					// Convert CLUT8 to RGB332/RGB121 palette
+					*dst++ = ((palette[color*3 + 0] >> _rShift) & _rMask)
+						   | ((palette[color*3 + 1] >> _gShift) & _gMask)
+						   | ((palette[color*3 + 2] >> _bShift) & _bMask);
+				} else {
+					*dst++ = color;
+				}
+
+				// clear bit
+				*dstMask &= ~bit;
+			} else {
+				*dst++ = 0x00;
+
+				// set bit
+				*dstMask |= bit;
+			}
+
+			if (bit == 0x0001)
+				dstMask++;
+		}
+
+		src += srcPadding;
+
+		if (dstPadding) {
+			memset(dst, 0x00, dstPadding);
+			dst += dstPadding;
+
+			*dstMask |= ((1 << dstPadding) - 1);
+			dstMask++;
+		}
+	}
+}
diff --git a/backends/graphics/atari/atari-cursor.h b/backends/graphics/atari/atari-cursor.h
new file mode 100644
index 00000000000..7f1795ab0ce
--- /dev/null
+++ b/backends/graphics/atari/atari-cursor.h
@@ -0,0 +1,87 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_ATARI_CURSOR_H
+#define BACKENDS_GRAPHICS_ATARI_CURSOR_H
+
+#include "common/rect.h"
+#include "common/scummsys.h"
+#include "graphics/surface.h"
+
+struct Cursor {
+	void update(const Graphics::Surface &screen, bool isModified);
+
+	bool visible = false;
+
+	// position
+	Common::Point getPosition() const {
+		return Common::Point(_x, _y);
+	}
+	void setPosition(int x, int y) {
+		_x = x;
+		_y = y;
+	}
+	void updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen);
+	void swap() {
+		const int tmpX = _oldX;
+		const int tmpY = _oldY;
+
+		_oldX = _x;
+		_oldY = _y;
+
+		_x = tmpX;
+		_y = tmpY;
+	}
+
+	// surface
+	void setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor);
+	void convertTo(const Graphics::PixelFormat &format);
+	Graphics::Surface surface;
+	Graphics::Surface surfaceMask;
+
+	// rects (valid only if !outOfScreen)
+	bool isClipped() const {
+		return outOfScreen ? false : _width != srcRect.width();
+	}
+	bool outOfScreen = true;
+	Common::Rect srcRect;
+	Common::Rect dstRect;
+
+	// palette (only used for the overlay)
+	byte palette[256*3] = {};
+
+private:
+	int _x = -1, _y = -1;
+	int _oldX = -1, _oldY = -1;
+
+	// related to 'surface'
+	const byte *_buf = nullptr;
+	int _width;
+	int _height;
+	int _hotspotX;
+	int _hotspotY;
+	uint32 _keycolor;
+
+	int _rShift, _gShift, _bShift;
+	int _rMask, _gMask, _bMask;
+};
+
+#endif // BACKENDS_GRAPHICS_ATARI_CURSOR_H
diff --git a/backends/graphics/atari/atari-graphics.cpp b/backends/graphics/atari/atari-graphics.cpp
index 08ac79d8a69..8ce12b34a9b 100644
--- a/backends/graphics/atari/atari-graphics.cpp
+++ b/backends/graphics/atari/atari-graphics.cpp
@@ -1050,10 +1050,7 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
 		//debug("Redraw cursor: %d %d %d %d", _cursor.dstRect.left, _cursor.dstRect.top, _cursor.dstRect.width(), _cursor.dstRect.height());
 
 		if (cursorSurfaceChanged || _cursor.isClipped()) {
-			if (dstSurface->format.isCLUT8())
-				_cursor.convertTo<true>(dstSurface->format);
-			else
-				_cursor.convertTo<false>(dstSurface->format);
+			_cursor.convertTo(dstSurface->format);
 			{
 				// copy in-place (will do nothing on regular Surface::copyRectToSurface)
 				Graphics::Surface surf;
@@ -1311,128 +1308,3 @@ void AtariGraphicsManager::Screen::restoreBackground(const Common::Rect &rect) {
 		rect.width() * bitsPerPixel / 8, rect.height(),	// fake 4bpp by 8bpp's width/2
 		offsettedSurf->format.bytesPerPixel);
 }
-
-
-void AtariGraphicsManager::Cursor::update(const Graphics::Surface &screen, bool isModified) {
-	if (!_buf) {
-		outOfScreen = true;
-		return;
-	}
-
-	if (!visible || !isModified)
-		return;
-
-	srcRect = Common::Rect(_width, _height);
-
-	dstRect = Common::Rect(
-		_x - _hotspotX,	// left
-		_y - _hotspotY,	// top
-		_x - _hotspotX + _width,	// right
-		_y - _hotspotY + _height);	// bottom
-
-	outOfScreen = !screen.clip(srcRect, dstRect);
-
-	assert(srcRect.width() == dstRect.width());
-	assert(srcRect.height() == dstRect.height());
-}
-
-void AtariGraphicsManager::Cursor::updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen) {
-	_x += deltaX;
-	_y += deltaY;
-
-	if (_x < 0)
-		_x = 0;
-	else if (_x >= screen.w)
-		_x = screen.w - 1;
-
-	if (_y < 0)
-		_y = 0;
-	else if (_y >= screen.h)
-		_y = screen.h - 1;
-}
-
-void AtariGraphicsManager::Cursor::setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor) {
-	if (w == 0 || h == 0 || buf == nullptr) {
-		_buf = nullptr;
-		return;
-	}
-
-	_buf = (const byte *)buf;
-	_width = w;
-	_height = h;
-	_hotspotX = hotspotX;
-	_hotspotY = hotspotY;
-	_keycolor = keycolor;
-}
-
-template <bool isClut8>	// hopefully compiler optimizes all the branching out
-void AtariGraphicsManager::Cursor::convertTo(const Graphics::PixelFormat &format) {
-	const int cursorWidth = (srcRect.width() + 15) & (-16);
-	const int cursorHeight = _height;
-
-	if (surface.w != cursorWidth || surface.h != cursorHeight || surface.format != format) {
-		if (!isClut8 && surface.format != format) {
-			_rShift = format.rLoss - format.rShift;
-			_gShift = format.gLoss - format.gShift;
-			_bShift = format.bLoss - format.bShift;
-
-			_rMask = format.rMax() << format.rShift;
-			_gMask = format.gMax() << format.gShift;
-			_bMask = format.bMax() << format.bShift;
-		}
-
-		surface.create(cursorWidth, cursorHeight, format);
-
-		const bool old_unalignedPitch = g_unalignedPitch;
-		g_unalignedPitch = true;
-		surfaceMask.create(surface.w / 8, surface.h, format);	// 1 bpl
-		g_unalignedPitch = old_unalignedPitch;
-	}
-
-	const int srcRectWidth = srcRect.width();
-
-	const byte *src = _buf + srcRect.left;
-	byte *dst = (byte *)surface.getPixels();
-	uint16 *dstMask = (uint16 *)surfaceMask.getPixels();
-	const int srcPadding = _width - srcRectWidth;
-	const int dstPadding = surface.w - srcRectWidth;
-
-	for (int j = 0; j < cursorHeight; ++j) {
-		for (int i = 0; i < srcRectWidth; ++i) {
-			const uint32 color = *src++;
-			const uint16 bit = 1 << (15 - (i % 16));
-
-			if (color != _keycolor) {
-				if (!isClut8) {
-					// Convert CLUT8 to RGB332/RGB121 palette
-					*dst++ = ((palette[color*3 + 0] >> _rShift) & _rMask)
-						   | ((palette[color*3 + 1] >> _gShift) & _gMask)
-						   | ((palette[color*3 + 2] >> _bShift) & _bMask);
-				} else {
-					*dst++ = color;
-				}
-
-				// clear bit
-				*dstMask &= ~bit;
-			} else {
-				*dst++ = 0x00;
-
-				// set bit
-				*dstMask |= bit;
-			}
-
-			if (bit == 0x0001)
-				dstMask++;
-		}
-
-		src += srcPadding;
-
-		if (dstPadding) {
-			memset(dst, 0x00, dstPadding);
-			dst += dstPadding;
-
-			*dstMask |= ((1 << dstPadding) - 1);
-			dstMask++;
-		}
-	}
-}
diff --git a/backends/graphics/atari/atari-graphics.h b/backends/graphics/atari/atari-graphics.h
index 0ca1a5e2ce1..c057d4a1389 100644
--- a/backends/graphics/atari/atari-graphics.h
+++ b/backends/graphics/atari/atari-graphics.h
@@ -29,6 +29,7 @@
 #include <mint/ostruct.h>
 #include <unordered_set>
 
+#include "atari-cursor.h"
 #include "common/rect.h"
 #include "graphics/surface.h"
 
@@ -318,64 +319,6 @@ private:
 	bool _overlayVisible = false;
 	Graphics::Surface _overlaySurface;
 
-	struct Cursor {
-		void update(const Graphics::Surface &screen, bool isModified);
-
-		bool visible = false;
-
-		// position
-		Common::Point getPosition() const {
-			return Common::Point(_x, _y);
-		}
-		void setPosition(int x, int y) {
-			_x = x;
-			_y = y;
-		}
-		void updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen);
-		void swap() {
-			const int tmpX = _oldX;
-			const int tmpY = _oldY;
-
-			_oldX = _x;
-			_oldY = _y;
-
-			_x = tmpX;
-			_y = tmpY;
-		}
-
-		// surface
-		void setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor);
-		template <bool isClut8>
-		void convertTo(const Graphics::PixelFormat &format);
-		Graphics::Surface surface;
-		Graphics::Surface surfaceMask;
-
-		// rects (valid only if !outOfScreen)
-		bool isClipped() const {
-			return outOfScreen ? false : _width != srcRect.width();
-		}
-		bool outOfScreen = true;
-		Common::Rect srcRect;
-		Common::Rect dstRect;
-
-		// palette (only used for the overlay)
-		byte palette[256*3] = {};
-
-	private:
-		int _x = -1, _y = -1;
-		int _oldX = -1, _oldY = -1;
-
-		// related to 'surface'
-		const byte *_buf = nullptr;
-		int _width;
-		int _height;
-		int _hotspotX;
-		int _hotspotY;
-		uint32 _keycolor;
-
-		int _rShift, _gShift, _bShift;
-		int _rMask, _gMask, _bMask;
-	};
 	Cursor _cursor;
 
 	Palette _palette;
diff --git a/backends/module.mk b/backends/module.mk
index cdbe853f735..7595b86dfd1 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -368,6 +368,7 @@ ifeq ($(BACKEND),atari)
 MODULE_OBJS += \
 	events/atari/atari-events.o \
 	graphics/atari/atari-c2p-asm.o \
+	graphics/atari/atari-cursor.o \
 	graphics/atari/atari-graphics.o \
 	graphics/atari/atari-graphics-asm.o \
 	mixer/atari/atari-mixer.o


Commit: c7bc03d4f48e873fc4e1bab80d2653b5f0ced405
    https://github.com/scummvm/scummvm/commit/c7bc03d4f48e873fc4e1bab80d2653b5f0ced405
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:51+02:00

Commit Message:
BACKENDS: ATARI: Move Screen outside AtariGraphicsManager

Changed paths:
  A backends/graphics/atari/atari-screen.cpp
  A backends/graphics/atari/atari-screen.h
    backends/graphics/atari/atari-graphics.cpp
    backends/graphics/atari/atari-graphics.h
    backends/module.mk


diff --git a/backends/graphics/atari/atari-graphics.cpp b/backends/graphics/atari/atari-graphics.cpp
index 8ce12b34a9b..818be679cee 100644
--- a/backends/graphics/atari/atari-graphics.cpp
+++ b/backends/graphics/atari/atari-graphics.cpp
@@ -21,18 +21,15 @@
 
 #define FORBIDDEN_SYMBOL_EXCEPTION_FILE // atari-graphics.h's unordered_set
 
-#include "backends/graphics/atari/atari-graphics.h"
+#include "atari-graphics.h"
 
 #include <mint/cookie.h>
-#include <mint/falcon.h>
 #include <mint/osbind.h>
 #include <mint/sysvars.h>
 
-#include "backends/graphics/atari/atari-graphics-superblitter.h"
+#include "backends/platform/atari/dlmalloc.h"
 #include "backends/keymapper/action.h"
 #include "backends/keymapper/keymap.h"
-#include "backends/platform/atari/dlmalloc.h"
-
 #include "common/config-manager.h"
 #include "common/str.h"
 #include "common/textconsole.h"	// for warning() & error()
@@ -41,10 +38,9 @@
 #include "graphics/blit.h"
 #include "gui/ThemeEngine.h"
 
-#define SCREEN_ACTIVE
+#include "atari-graphics-superblitter.h"
 
-#define MAX_HZ_SHAKE 16 // Falcon only
-#define MAX_V_SHAKE  16
+#define SCREEN_ACTIVE
 
 bool g_unalignedPitch = false;
 mspace g_mspace = nullptr;
@@ -991,15 +987,15 @@ template <bool directRendering>	// hopefully compiler optimizes all the branchin
 bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurface) {
 	//debug("updateScreenInternal");
 
-	const DirtyRects &dirtyRects  = _workScreen->dirtyRects;
-	Graphics::Surface *dstSurface = _workScreen->offsettedSurf;
-	bool &cursorPositionChanged   = _workScreen->cursorPositionChanged;
-	bool &cursorSurfaceChanged    = _workScreen->cursorSurfaceChanged;
-	bool &cursorVisibilityChanged = _workScreen->cursorVisibilityChanged;
-	Common::Rect &oldCursorRect   = _workScreen->oldCursorRect;
-	const bool &fullRedraw        = _workScreen->fullRedraw;
+	const Screen::DirtyRects &dirtyRects = _workScreen->dirtyRects;
+	Graphics::Surface *dstSurface        = _workScreen->offsettedSurf;
+	bool &cursorPositionChanged          = _workScreen->cursorPositionChanged;
+	bool &cursorSurfaceChanged           = _workScreen->cursorSurfaceChanged;
+	bool &cursorVisibilityChanged        = _workScreen->cursorVisibilityChanged;
+	Common::Rect &oldCursorRect          = _workScreen->oldCursorRect;
+	const bool &fullRedraw               = _workScreen->fullRedraw;
 
-	const int dstBitsPerPixel     = getBitsPerPixel(dstSurface->format);
+	const int dstBitsPerPixel            = getBitsPerPixel(dstSurface->format);
 
 	bool updated = false;
 
@@ -1129,182 +1125,3 @@ bool AtariGraphicsManager::isOverlayDirectRendering() const {
 #endif
 		;
 }
-
-AtariGraphicsManager::Screen::Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette_)
-	: _manager(manager)
-	, palette(palette_) {
-	const AtariMemAlloc &allocFunc = _manager->getStRamAllocFunc();
-
-	surf.init(
-		width + (_manager->_tt ? 0 : 2 * MAX_HZ_SHAKE),
-		height + 2 * MAX_V_SHAKE,
-		(width + (_manager->_tt ? 0 : 2 * MAX_HZ_SHAKE)) * _manager->getBitsPerPixel(format) / 8,
-		nullptr,
-		format);
-
-	void *pixelsUnaligned = allocFunc(sizeof(uintptr) + (surf.h * surf.pitch) + ALIGN - 1);
-	if (!pixelsUnaligned) {
-		error("Failed to allocate memory in ST RAM");
-	}
-
-	surf.setPixels((void *)(((uintptr)pixelsUnaligned + sizeof(uintptr) + ALIGN - 1) & (-ALIGN)));
-
-	// store the unaligned pointer for later release
-	*((uintptr *)surf.getPixels() - 1) = (uintptr)pixelsUnaligned;
-
-	memset(surf.getPixels(), 0, surf.h * surf.pitch);
-
-	_offsettedSurf.init(
-		width, height, surf.pitch,
-		surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2),
-		surf.format);
-}
-
-AtariGraphicsManager::Screen::~Screen() {
-	const AtariMemFree &freeFunc = _manager->getStRamFreeFunc();
-
-	freeFunc((void *)*((uintptr *)surf.getPixels() - 1));
-}
-
-void AtariGraphicsManager::Screen::reset(int width, int height, int bitsPerPixel) {
-	cursorPositionChanged = true;
-	cursorSurfaceChanged = true;
-	cursorVisibilityChanged = false;
-	clearDirtyRects();
-	oldCursorRect = Common::Rect();
-	rez = -1;
-	mode = -1;
-
-	// erase old screen
-	_offsettedSurf.fillRect(Common::Rect(_offsettedSurf.w, _offsettedSurf.h), 0);
-
-	if (_manager->_tt) {
-		if (width <= 320 && height <= 240) {
-			surf.w = 320;
-			surf.h = 240 + 2 * MAX_V_SHAKE;
-			surf.pitch = 2 * surf.w * bitsPerPixel / 8;
-			rez = kRezValueTTLow;
-		} else {
-			surf.w = 640;
-			surf.h = 480 + 2 * MAX_V_SHAKE;
-			surf.pitch = surf.w * bitsPerPixel / 8;
-			rez = kRezValueTTMid;
-		}
-	} else {
-		mode = VsetMode(VM_INQUIRE) & PAL;
-
-		if (_manager->_vgaMonitor) {
-			mode |= VGA | (bitsPerPixel == 4 ? BPS4 : (hasSuperVidel() ? BPS8C : BPS8));
-
-			if (width <= 320 && height <= 240) {
-				surf.w = 320;
-				surf.h = 240;
-				mode |= VERTFLAG | COL40;
-			} else {
-				surf.w = 640;
-				surf.h = 480;
-				mode |= COL80;
-			}
-		} else {
-			mode |= TV | (bitsPerPixel == 4 ? BPS4 : BPS8);
-
-			if (width <= 320 && height <= 200) {
-				surf.w = 320;
-				surf.h = 200;
-				mode |= COL40;
-			} else if (width <= 320*1.2 && height <= 200*1.2) {
-				surf.w = 320*1.2;
-				surf.h = 200*1.2;
-				mode |= OVERSCAN | COL40;
-			} else if (width <= 640 && height <= 400) {
-				surf.w = 640;
-				surf.h = 400;
-				mode |= VERTFLAG | COL80;
-			} else {
-				surf.w = 640*1.2;
-				surf.h = 400*1.2;
-				mode |= VERTFLAG | OVERSCAN | COL80;
-			}
-		}
-
-		surf.w += 2 * MAX_HZ_SHAKE;
-		surf.h += 2 * MAX_V_SHAKE;
-		surf.pitch = surf.w * bitsPerPixel / 8;
-	}
-
-	_offsettedSurf.init(
-		width, height, surf.pitch,
-		surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2),
-		surf.format);
-}
-
-void AtariGraphicsManager::Screen::addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect, bool directRendering) {
-	if (fullRedraw)
-		return;
-
-	if ((rect.width() == srcSurface.w && rect.height() == srcSurface.h)
-		|| dirtyRects.size() == 128) {	// 320x200 can hold at most 250 16x16 rectangles
-		//debug("addDirtyRect[%d]: purge %d x %d", (int)dirtyRects.size(), srcSurface.w, srcSurface.h);
-
-		dirtyRects.clear();
-		dirtyRects.emplace(srcSurface.w, srcSurface.h);
-
-		oldCursorRect = Common::Rect();
-
-		fullRedraw = true;
-		return;
-	}
-
-	dirtyRects.insert(rect);
-
-	if (!oldCursorRect.isEmpty()) {
-		const Common::Rect alignedOldCursorRect = _manager->alignRect(oldCursorRect);
-
-		// we have to check *aligned* oldCursorRect because it is background which gets copied,
-		// i.e. it has to be up to date even outside the cursor rectangle.
-		// do it now to avoid complex checking in updateScreenInternal()
-		if (rect.contains(alignedOldCursorRect)) {
-			oldCursorRect = Common::Rect();
-		} else if (rect.intersects(alignedOldCursorRect)) {
-			if (!directRendering) {
-				_manager->copyRectToSurface(
-					*offsettedSurf, _manager->getBitsPerPixel(offsettedSurf->format), srcSurface,
-					alignedOldCursorRect.left, alignedOldCursorRect.top,
-					alignedOldCursorRect);
-			} else {
-				restoreBackground(alignedOldCursorRect);
-			}
-
-			oldCursorRect = Common::Rect();
-		}
-	}
-}
-
-void AtariGraphicsManager::Screen::storeBackground(const Common::Rect &rect) {
-	const int bitsPerPixel = _manager->getBitsPerPixel(offsettedSurf->format);
-
-	if (_cursorBackgroundSurf.w != rect.width()
-		|| _cursorBackgroundSurf.h != rect.height()
-		|| _cursorBackgroundSurf.format != offsettedSurf->format) {
-		_cursorBackgroundSurf.create(rect.width(), rect.height(), offsettedSurf->format);
-		_cursorBackgroundSurf.pitch = _cursorBackgroundSurf.pitch * bitsPerPixel / 8;
-	}
-
-	Graphics::copyBlit(
-		(byte *)_cursorBackgroundSurf.getPixels(),
-		(const byte *)offsettedSurf->getPixels() + rect.top * offsettedSurf->pitch + rect.left * bitsPerPixel / 8,
-		_cursorBackgroundSurf.pitch, offsettedSurf->pitch,
-		rect.width() * bitsPerPixel / 8, rect.height(),	// fake 4bpp by 8bpp's width/2
-		offsettedSurf->format.bytesPerPixel);
-}
-
-void AtariGraphicsManager::Screen::restoreBackground(const Common::Rect &rect) {
-	const int bitsPerPixel = _manager->getBitsPerPixel(offsettedSurf->format);
-
-	Graphics::copyBlit(
-		(byte *)offsettedSurf->getPixels() + rect.top * offsettedSurf->pitch + rect.left * bitsPerPixel / 8,
-		(const byte *)_cursorBackgroundSurf.getPixels(),
-		offsettedSurf->pitch, _cursorBackgroundSurf.pitch,
-		rect.width() * bitsPerPixel / 8, rect.height(),	// fake 4bpp by 8bpp's width/2
-		offsettedSurf->format.bytesPerPixel);
-}
diff --git a/backends/graphics/atari/atari-graphics.h b/backends/graphics/atari/atari-graphics.h
index c057d4a1389..164d32232b3 100644
--- a/backends/graphics/atari/atari-graphics.h
+++ b/backends/graphics/atari/atari-graphics.h
@@ -26,25 +26,19 @@
 #include "common/events.h"
 
 #include <mint/osbind.h>
-#include <mint/ostruct.h>
-#include <unordered_set>
 
-#include "atari-cursor.h"
 #include "common/rect.h"
 #include "graphics/surface.h"
 
-template<>
-struct std::hash<Common::Rect>
-{
-	std::size_t operator()(Common::Rect const& rect) const noexcept
-	{
-		return 31 * (31 * (31 * rect.left + rect.top) + rect.right) + rect.bottom;
-	}
-};
+#include "atari-cursor.h"
+#include "atari-screen.h"
 
-///////////////////////////////////////////////////////////////////////////////
+#define MAX_HZ_SHAKE 16 // Falcon only
+#define MAX_V_SHAKE  16
 
 class AtariGraphicsManager : public GraphicsManager, Common::EventObserver {
+	friend class Screen;
+
 public:
 	AtariGraphicsManager();
 	virtual ~AtariGraphicsManager();
@@ -116,42 +110,11 @@ protected:
 	void allocateSurfaces();
 	void freeSurfaces();
 
-	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;
-		Graphics::PixelFormat format;
-	};
-	GraphicsState _pendingState{ (GraphicsMode)getDefaultGraphicsMode() };
-
 private:
-	using DirtyRects = std::unordered_set<Common::Rect>;
-
 	enum CustomEventAction {
 		kActionToggleAspectRatioCorrection = 100,
 	};
 
-	enum SteTtRezValue {
-		kRezValueSTLow  = 0,	// 320x200 at 4bpp, ST palette
-		kRezValueSTMid  = 1,	// 640x200 at 2bpp, ST palette
-		kRezValueSTHigh = 2,	// 640x400 at 1bpp, ST palette
-		kRezValueTTLow  = 7,	// 320x480 at 8bpp, TT palette
-		kRezValueTTMid  = 4,	// 640x480 at 4bpp, TT palette
-		kRezValueTTHigh = 6		// 1280x960 at 1bpp, TT palette
-	};
-
 	int16 getMaximumScreenHeight() const { return 480; }
 	int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 640 : 640*1.2); }
 
@@ -236,6 +199,25 @@ private:
 	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;
+		Graphics::PixelFormat format;
+	};
+	GraphicsState _pendingState{ (GraphicsMode)getDefaultGraphicsMode() };
 	GraphicsState _currentState{ (GraphicsMode)getDefaultGraphicsMode() };
 
 	enum PendingScreenChange {
@@ -253,63 +235,6 @@ private:
 		OVERLAY_BUFFER,
 		BUFFER_COUNT
 	};
-
-	class Palette {
-	public:
-		void clear() {
-			memset(data, 0, sizeof(data));
-		}
-
-		uint16 *const tt = reinterpret_cast<uint16*>(data);
-		_RGB *const falcon = reinterpret_cast<_RGB*>(data);
-
-	private:
-		byte data[256*4] = {};
-	};
-
-	struct Screen {
-		Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette);
-		~Screen();
-
-		void reset(int width, int height, int bitsPerPixel);
-		// must be called before any rectangle drawing
-		void addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect, bool directRendering);
-
-		void clearDirtyRects() {
-			dirtyRects.clear();
-			fullRedraw = false;
-		}
-
-		void storeBackground(const Common::Rect &rect);
-		void restoreBackground(const Common::Rect &rect);
-
-		Graphics::Surface surf;
-		const Palette *palette;
-		bool cursorPositionChanged = true;
-		bool cursorSurfaceChanged = true;
-		bool cursorVisibilityChanged = false;
-		DirtyRects dirtyRects;
-		bool fullRedraw = false;
-		Common::Rect oldCursorRect;
-		int rez = -1;
-		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
-
-		const AtariGraphicsManager *_manager;
-
-		Graphics::Surface _offsettedSurf;
-		// used by direct rendering
-		Graphics::Surface _cursorBackgroundSurf;
-	};
 	Screen *_screen[BUFFER_COUNT] = {};
 	Screen *_workScreen = nullptr;
 	Screen *_oldWorkScreen = nullptr;	// used in hideOverlay()
diff --git a/backends/graphics/atari/atari-screen.cpp b/backends/graphics/atari/atari-screen.cpp
new file mode 100644
index 00000000000..d1f30dd66aa
--- /dev/null
+++ b/backends/graphics/atari/atari-screen.cpp
@@ -0,0 +1,208 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "atari-screen.h"
+
+#include <mint/falcon.h>
+
+#include "graphics/blit.h"
+
+#include "atari-graphics.h"
+#include "atari-graphics-superblitter.h"
+
+Screen::Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette_)
+	: _manager(manager)
+	, palette(palette_) {
+	const AtariGraphicsManager::AtariMemAlloc &allocFunc = _manager->getStRamAllocFunc();
+
+	surf.init(
+		width + (_manager->_tt ? 0 : 2 * MAX_HZ_SHAKE),
+		height + 2 * MAX_V_SHAKE,
+		(width + (_manager->_tt ? 0 : 2 * MAX_HZ_SHAKE)) * _manager->getBitsPerPixel(format) / 8,
+		nullptr,
+		format);
+
+	void *pixelsUnaligned = allocFunc(sizeof(uintptr) + (surf.h * surf.pitch) + ALIGN - 1);
+	if (!pixelsUnaligned) {
+		error("Failed to allocate memory in ST RAM");
+	}
+
+	surf.setPixels((void *)(((uintptr)pixelsUnaligned + sizeof(uintptr) + ALIGN - 1) & (-ALIGN)));
+
+	// store the unaligned pointer for later release
+	*((uintptr *)surf.getPixels() - 1) = (uintptr)pixelsUnaligned;
+
+	memset(surf.getPixels(), 0, surf.h * surf.pitch);
+
+	_offsettedSurf.init(
+		width, height, surf.pitch,
+		surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2),
+		surf.format);
+}
+
+Screen::~Screen() {
+	const AtariGraphicsManager::AtariMemFree &freeFunc = _manager->getStRamFreeFunc();
+
+	freeFunc((void *)*((uintptr *)surf.getPixels() - 1));
+}
+
+void Screen::reset(int width, int height, int bitsPerPixel) {
+	cursorPositionChanged = true;
+	cursorSurfaceChanged = true;
+	cursorVisibilityChanged = false;
+	clearDirtyRects();
+	oldCursorRect = Common::Rect();
+	rez = -1;
+	mode = -1;
+
+	// erase old screen
+	_offsettedSurf.fillRect(Common::Rect(_offsettedSurf.w, _offsettedSurf.h), 0);
+
+	if (_manager->_tt) {
+		if (width <= 320 && height <= 240) {
+			surf.w = 320;
+			surf.h = 240 + 2 * MAX_V_SHAKE;
+			surf.pitch = 2 * surf.w * bitsPerPixel / 8;
+			rez = kRezValueTTLow;
+		} else {
+			surf.w = 640;
+			surf.h = 480 + 2 * MAX_V_SHAKE;
+			surf.pitch = surf.w * bitsPerPixel / 8;
+			rez = kRezValueTTMid;
+		}
+	} else {
+		mode = VsetMode(VM_INQUIRE) & PAL;
+
+		if (_manager->_vgaMonitor) {
+			mode |= VGA | (bitsPerPixel == 4 ? BPS4 : (hasSuperVidel() ? BPS8C : BPS8));
+
+			if (width <= 320 && height <= 240) {
+				surf.w = 320;
+				surf.h = 240;
+				mode |= VERTFLAG | COL40;
+			} else {
+				surf.w = 640;
+				surf.h = 480;
+				mode |= COL80;
+			}
+		} else {
+			mode |= TV | (bitsPerPixel == 4 ? BPS4 : BPS8);
+
+			if (width <= 320 && height <= 200) {
+				surf.w = 320;
+				surf.h = 200;
+				mode |= COL40;
+			} else if (width <= 320*1.2 && height <= 200*1.2) {
+				surf.w = 320*1.2;
+				surf.h = 200*1.2;
+				mode |= OVERSCAN | COL40;
+			} else if (width <= 640 && height <= 400) {
+				surf.w = 640;
+				surf.h = 400;
+				mode |= VERTFLAG | COL80;
+			} else {
+				surf.w = 640*1.2;
+				surf.h = 400*1.2;
+				mode |= VERTFLAG | OVERSCAN | COL80;
+			}
+		}
+
+		surf.w += 2 * MAX_HZ_SHAKE;
+		surf.h += 2 * MAX_V_SHAKE;
+		surf.pitch = surf.w * bitsPerPixel / 8;
+	}
+
+	_offsettedSurf.init(
+		width, height, surf.pitch,
+		surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2),
+		surf.format);
+}
+
+void Screen::addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect, bool directRendering) {
+	if (fullRedraw)
+		return;
+
+	if ((rect.width() == srcSurface.w && rect.height() == srcSurface.h)
+		|| dirtyRects.size() == 128) {	// 320x200 can hold at most 250 16x16 rectangles
+		//debug("addDirtyRect[%d]: purge %d x %d", (int)dirtyRects.size(), srcSurface.w, srcSurface.h);
+
+		dirtyRects.clear();
+		dirtyRects.emplace(srcSurface.w, srcSurface.h);
+
+		oldCursorRect = Common::Rect();
+
+		fullRedraw = true;
+		return;
+	}
+
+	dirtyRects.insert(rect);
+
+	if (!oldCursorRect.isEmpty()) {
+		const Common::Rect alignedOldCursorRect = _manager->alignRect(oldCursorRect);
+
+		// we have to check *aligned* oldCursorRect because it is background which gets copied,
+		// i.e. it has to be up to date even outside the cursor rectangle.
+		// do it now to avoid complex checking in updateScreenInternal()
+		if (rect.contains(alignedOldCursorRect)) {
+			oldCursorRect = Common::Rect();
+		} else if (rect.intersects(alignedOldCursorRect)) {
+			if (!directRendering) {
+				_manager->copyRectToSurface(
+					*offsettedSurf, _manager->getBitsPerPixel(offsettedSurf->format), srcSurface,
+					alignedOldCursorRect.left, alignedOldCursorRect.top,
+					alignedOldCursorRect);
+			} else {
+				restoreBackground(alignedOldCursorRect);
+			}
+
+			oldCursorRect = Common::Rect();
+		}
+	}
+}
+
+void Screen::storeBackground(const Common::Rect &rect) {
+	const int bitsPerPixel = _manager->getBitsPerPixel(offsettedSurf->format);
+
+	if (_cursorBackgroundSurf.w != rect.width()
+		|| _cursorBackgroundSurf.h != rect.height()
+		|| _cursorBackgroundSurf.format != offsettedSurf->format) {
+		_cursorBackgroundSurf.create(rect.width(), rect.height(), offsettedSurf->format);
+		_cursorBackgroundSurf.pitch = _cursorBackgroundSurf.pitch * bitsPerPixel / 8;
+	}
+
+	Graphics::copyBlit(
+		(byte *)_cursorBackgroundSurf.getPixels(),
+		(const byte *)offsettedSurf->getPixels() + rect.top * offsettedSurf->pitch + rect.left * bitsPerPixel / 8,
+		_cursorBackgroundSurf.pitch, offsettedSurf->pitch,
+		rect.width() * bitsPerPixel / 8, rect.height(),	// fake 4bpp by 8bpp's width/2
+		offsettedSurf->format.bytesPerPixel);
+}
+
+void Screen::restoreBackground(const Common::Rect &rect) {
+	const int bitsPerPixel = _manager->getBitsPerPixel(offsettedSurf->format);
+
+	Graphics::copyBlit(
+		(byte *)offsettedSurf->getPixels() + rect.top * offsettedSurf->pitch + rect.left * bitsPerPixel / 8,
+		(const byte *)_cursorBackgroundSurf.getPixels(),
+		offsettedSurf->pitch, _cursorBackgroundSurf.pitch,
+		rect.width() * bitsPerPixel / 8, rect.height(),	// fake 4bpp by 8bpp's width/2
+		offsettedSurf->format.bytesPerPixel);
+}
diff --git a/backends/graphics/atari/atari-screen.h b/backends/graphics/atari/atari-screen.h
new file mode 100644
index 00000000000..7ffd188a5e7
--- /dev/null
+++ b/backends/graphics/atari/atari-screen.h
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_ATARI_SCREEN_H
+#define BACKENDS_GRAPHICS_ATARI_SCREEN_H
+
+#include <unordered_set>
+#include <mint/ostruct.h>
+
+#include "common/rect.h"
+#include "graphics/surface.h"
+
+template<>
+struct std::hash<Common::Rect>
+{
+	std::size_t operator()(Common::Rect const& rect) const noexcept
+	{
+		return 31 * (31 * (31 * rect.left + rect.top) + rect.right) + rect.bottom;
+	}
+};
+
+class AtariGraphicsManager;
+
+class Palette {
+public:
+	void clear() {
+		memset(data, 0, sizeof(data));
+	}
+
+	uint16 *const tt = reinterpret_cast<uint16*>(data);
+	_RGB *const falcon = reinterpret_cast<_RGB*>(data);
+
+private:
+	byte data[256*4] = {};
+};
+
+struct Screen {
+	using DirtyRects = std::unordered_set<Common::Rect>;
+
+	Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette);
+	~Screen();
+
+	void reset(int width, int height, int bitsPerPixel);
+	// must be called before any rectangle drawing
+	void addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect, bool directRendering);
+
+	void clearDirtyRects() {
+		dirtyRects.clear();
+		fullRedraw = false;
+	}
+
+	void storeBackground(const Common::Rect &rect);
+	void restoreBackground(const Common::Rect &rect);
+
+	Graphics::Surface surf;
+	const Palette *palette;
+	bool cursorPositionChanged = true;
+	bool cursorSurfaceChanged = true;
+	bool cursorVisibilityChanged = false;
+	DirtyRects dirtyRects;
+	bool fullRedraw = false;
+	Common::Rect oldCursorRect;
+	int rez = -1;
+	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
+
+	enum SteTtRezValue {
+		kRezValueSTLow  = 0,	// 320x200 at 4bpp, ST palette
+		kRezValueSTMid  = 1,	// 640x200 at 2bpp, ST palette
+		kRezValueSTHigh = 2,	// 640x400 at 1bpp, ST palette
+		kRezValueTTLow  = 7,	// 320x480 at 8bpp, TT palette
+		kRezValueTTMid  = 4,	// 640x480 at 4bpp, TT palette
+		kRezValueTTHigh = 6		// 1280x960 at 1bpp, TT palette
+	};
+
+	const AtariGraphicsManager *_manager;
+
+	Graphics::Surface _offsettedSurf;
+	// used by direct rendering
+	Graphics::Surface _cursorBackgroundSurf;
+};
+
+#endif // BACKENDS_GRAPHICS_ATARI_SCREEN_H
diff --git a/backends/module.mk b/backends/module.mk
index 7595b86dfd1..8cb79e3601e 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -371,6 +371,7 @@ MODULE_OBJS += \
 	graphics/atari/atari-cursor.o \
 	graphics/atari/atari-graphics.o \
 	graphics/atari/atari-graphics-asm.o \
+	graphics/atari/atari-screen.o \
 	mixer/atari/atari-mixer.o
 endif
 


Commit: 0c77d678452d599f52da6fbb5162b21dc87d0718
    https://github.com/scummvm/scummvm/commit/0c77d678452d599f52da6fbb5162b21dc87d0718
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:51+02:00

Commit Message:
BACKENDS: ATARI: Make Cursor Screen's property

Instead of having one globally in AtariGraphicsManager. This simplifies
code and cleans up a lot of dark corners in AtariGraphicsManager.

A slight disadvantage is that now the cursor surface has be to converted
for each buffer.

Changed paths:
    backends/graphics/atari/atari-cursor.cpp
    backends/graphics/atari/atari-cursor.h
    backends/graphics/atari/atari-graphics.cpp
    backends/graphics/atari/atari-graphics.h
    backends/graphics/atari/atari-screen.cpp
    backends/graphics/atari/atari-screen.h


diff --git a/backends/graphics/atari/atari-cursor.cpp b/backends/graphics/atari/atari-cursor.cpp
index dab54c911b9..ec178b19a47 100644
--- a/backends/graphics/atari/atari-cursor.cpp
+++ b/backends/graphics/atari/atari-cursor.cpp
@@ -23,44 +23,56 @@
 
 #include <cassert>
 
+#include "graphics/blit.h"
+
+#include "atari-graphics.h"
+#include "atari-screen.h"
+
 extern bool g_unalignedPitch;
 
-void Cursor::update(const Graphics::Surface &screen, bool isModified) {
+byte Cursor::_palette[256*3] = {};
+
+void Cursor::update() {
 	if (!_buf) {
-		outOfScreen = true;
+		_outOfScreen = true;
 		return;
 	}
 
-	if (!visible || !isModified)
+	if (!_visible || (!_positionChanged && !_surfaceChanged))
 		return;
 
-	srcRect = Common::Rect(_width, _height);
+	_srcRect = Common::Rect(_width, _height);
 
-	dstRect = Common::Rect(
+	_dstRect = Common::Rect(
 		_x - _hotspotX,	// left
 		_y - _hotspotY,	// top
 		_x - _hotspotX + _width,	// right
 		_y - _hotspotY + _height);	// bottom
 
-	outOfScreen = !screen.clip(srcRect, dstRect);
+	_outOfScreen = !_parentScreen->offsettedSurf->clip(_srcRect, _dstRect);
 
-	assert(srcRect.width() == dstRect.width());
-	assert(srcRect.height() == dstRect.height());
+	assert(_srcRect.width() == _dstRect.width());
+	assert(_srcRect.height() == _dstRect.height());
 }
 
-void Cursor::updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen) {
+void Cursor::updatePosition(int deltaX, int deltaY) {
+	if (deltaX == 0 && deltaY == 0)
+		return;
+
 	_x += deltaX;
 	_y += deltaY;
 
 	if (_x < 0)
 		_x = 0;
-	else if (_x >= screen.w)
-		_x = screen.w - 1;
+	else if (_x >= _parentScreen->offsettedSurf->w)
+		_x = _parentScreen->offsettedSurf->w - 1;
 
 	if (_y < 0)
 		_y = 0;
-	else if (_y >= screen.h)
-		_y = screen.h - 1;
+	else if (_y >= _parentScreen->offsettedSurf->h)
+		_y = _parentScreen->offsettedSurf->h - 1;
+
+	_positionChanged = true;
 }
 
 void Cursor::setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor) {
@@ -75,15 +87,24 @@ void Cursor::setSurface(const void *buf, int w, int h, int hotspotX, int hotspot
 	_hotspotX = hotspotX;
 	_hotspotY = hotspotY;
 	_keycolor = keycolor;
+
+	_surfaceChanged = true;
+}
+
+void Cursor::setPalette(const byte *colors, uint start, uint num) {
+	if (colors)
+		memcpy(&_palette[start * 3], colors, num * 3);
+
+	_surfaceChanged = true;
 }
 
 void Cursor::convertTo(const Graphics::PixelFormat &format) {
-	const int cursorWidth = (srcRect.width() + 15) & (-16);
+	const int cursorWidth = (_srcRect.width() + 15) & (-16);
 	const int cursorHeight = _height;
 	const bool isCLUT8 = format.isCLUT8();
 
-	if (surface.w != cursorWidth || surface.h != cursorHeight || surface.format != format) {
-		if (!isCLUT8 && surface.format != format) {
+	if (_surface.w != cursorWidth || _surface.h != cursorHeight || _surface.format != format) {
+		if (!isCLUT8 && _surface.format != format) {
 			_rShift = format.rLoss - format.rShift;
 			_gShift = format.gLoss - format.gShift;
 			_bShift = format.bLoss - format.bShift;
@@ -93,21 +114,21 @@ void Cursor::convertTo(const Graphics::PixelFormat &format) {
 			_bMask = format.bMax() << format.bShift;
 		}
 
-		surface.create(cursorWidth, cursorHeight, format);
+		_surface.create(cursorWidth, cursorHeight, format);
 
 		const bool old_unalignedPitch = g_unalignedPitch;
 		g_unalignedPitch = true;
-		surfaceMask.create(surface.w / 8, surface.h, format);	// 1 bpl
+		_surfaceMask.create(_surface.w / 8, _surface.h, format);	// 1 bpl
 		g_unalignedPitch = old_unalignedPitch;
 	}
 
-	const int srcRectWidth = srcRect.width();
+	const int srcRectWidth = _srcRect.width();
 
-	const byte *src = _buf + srcRect.left;
-	byte *dst = (byte *)surface.getPixels();
-	uint16 *dstMask = (uint16 *)surfaceMask.getPixels();
+	const byte *src = _buf + _srcRect.left;
+	byte *dst = (byte *)_surface.getPixels();
+	uint16 *dstMask = (uint16 *)_surfaceMask.getPixels();
 	const int srcPadding = _width - srcRectWidth;
-	const int dstPadding = surface.w - srcRectWidth;
+	const int dstPadding = _surface.w - srcRectWidth;
 
 	for (int j = 0; j < cursorHeight; ++j) {
 		for (int i = 0; i < srcRectWidth; ++i) {
@@ -117,9 +138,9 @@ void Cursor::convertTo(const Graphics::PixelFormat &format) {
 			if (color != _keycolor) {
 				if (!isCLUT8) {
 					// Convert CLUT8 to RGB332/RGB121 palette
-					*dst++ = ((palette[color*3 + 0] >> _rShift) & _rMask)
-						   | ((palette[color*3 + 1] >> _gShift) & _gMask)
-						   | ((palette[color*3 + 2] >> _bShift) & _bMask);
+					*dst++ = ((_palette[color*3 + 0] >> _rShift) & _rMask)
+						   | ((_palette[color*3 + 1] >> _gShift) & _gMask)
+						   | ((_palette[color*3 + 2] >> _bShift) & _bMask);
 				} else {
 					*dst++ = color;
 				}
@@ -148,3 +169,106 @@ void Cursor::convertTo(const Graphics::PixelFormat &format) {
 		}
 	}
 }
+
+void Cursor::flushBackground(const Graphics::Surface &srcSurface, const Common::Rect &rect) {
+	if (_savedRect.isEmpty())
+		return;
+
+	if (rect.contains(_savedRect)) {
+		_savedRect = Common::Rect();
+	} else if (rect.intersects(_savedRect)) {
+		restoreBackground(srcSurface, true);
+	}
+}
+
+bool Cursor::restoreBackground(const Graphics::Surface &srcSurface, bool force) {
+	if (_savedRect.isEmpty() || (!force && !isChanged()))
+		return false;
+
+	Graphics::Surface &dstSurface = *_parentScreen->offsettedSurf;
+	const int dstBitsPerPixel     = _manager->getBitsPerPixel(dstSurface.format);
+
+	//debug("Cursor::restoreBackground: %d %d %d %d", _savedRect.left, _savedRect.top, _savedRect.width(), _savedRect.height());
+
+	if (srcSurface.getPixels()) {
+		_manager->copyRectToSurface(
+			dstSurface, dstBitsPerPixel, srcSurface,
+			_savedRect.left, _savedRect.top,
+			_savedRect);
+	} else {
+		const int bytesPerPixel = dstSurface.format.bytesPerPixel;
+
+		// restore native pixels (i.e. bitplanes)
+		Graphics::copyBlit(
+			(byte *)dstSurface.getPixels() + _savedRect.top * dstSurface.pitch + _savedRect.left * dstBitsPerPixel / 8,
+			(const byte *)_savedBackground.getPixels(),
+			dstSurface.pitch, _savedBackground.pitch,
+			_savedRect.width() * dstBitsPerPixel / 8, _savedRect.height(),	// fake 4bpp by 8bpp's width/2
+			bytesPerPixel);
+	}
+
+	_savedRect = Common::Rect();
+	return true;
+}
+
+bool Cursor::draw(bool directRendering, bool force) {
+	if (!isVisible() || (!force && !isChanged())) {
+		 _visibilityChanged = _positionChanged = _surfaceChanged = false;
+		return false;
+	}
+
+	Graphics::Surface &dstSurface = *_parentScreen->offsettedSurf;
+	const int dstBitsPerPixel     = _manager->getBitsPerPixel(dstSurface.format);
+
+	//debug("Cursor::draw: %d %d %d %d", _dstRect.left, _dstRect.top, _dstRect.width(), _dstRect.height());
+
+	// always work with aligned rect
+	_savedRect = _manager->alignRect(_dstRect);
+
+	if (_surfaceChanged || _width != _srcRect.width()) {
+		// TODO: check for change, not just different width so it's not called over and over again ...
+		// TODO: some sort of in-place C2P directly into convertTo() ...
+		convertTo(dstSurface.format);
+		{
+			// c2p in-place (will do nothing on regular Surface::copyRectToSurface)
+			Graphics::Surface surf;
+			surf.init(
+				_surface.w,
+				_surface.h,
+				_surface.pitch * dstBitsPerPixel / 8,	// 4bpp is not byte per pixel anymore
+				_surface.getPixels(),
+				_surface.format);
+			_manager->copyRectToSurface(
+				surf, dstBitsPerPixel, _surface,
+				0, 0,
+				Common::Rect(_surface.w, _surface.h));
+		}
+	}
+
+	if (directRendering) {
+		// store native pixels (i.e. bitplanes)
+		if (_savedBackground.w != _savedRect.width()
+			|| _savedBackground.h != _savedRect.height()
+			|| _savedBackground.format != dstSurface.format) {
+			_savedBackground.create(_savedRect.width(), _savedRect.height(), dstSurface.format);
+			_savedBackground.pitch = _savedBackground.pitch * dstBitsPerPixel / 8;
+		}
+
+		Graphics::copyBlit(
+			(byte *)_savedBackground.getPixels(),
+			(const byte *)dstSurface.getPixels() + _savedRect.top * dstSurface.pitch + _savedRect.left * dstBitsPerPixel / 8,
+			_savedBackground.pitch, dstSurface.pitch,
+			_savedRect.width() * dstBitsPerPixel / 8, _savedRect.height(),	// fake 4bpp by 8bpp's width/2
+			dstSurface.format.bytesPerPixel);
+	}
+
+	// don't use _srcRect.right as 'x2' as this must be aligned first
+	// (_surface.w is recalculated thanks to convertTo())
+	_manager->drawMaskedSprite(
+		dstSurface, dstBitsPerPixel, _surface, _surfaceMask,
+		_dstRect.left, _dstRect.top,
+		Common::Rect(0, _srcRect.top, _surface.w, _srcRect.bottom));
+
+	_visibilityChanged = _positionChanged = _surfaceChanged = false;
+	return true;
+}
diff --git a/backends/graphics/atari/atari-cursor.h b/backends/graphics/atari/atari-cursor.h
index 7f1795ab0ce..2de90f5da3a 100644
--- a/backends/graphics/atari/atari-cursor.h
+++ b/backends/graphics/atari/atari-cursor.h
@@ -26,51 +26,100 @@
 #include "common/scummsys.h"
 #include "graphics/surface.h"
 
+class AtariGraphicsManager;
+struct Screen;
+
+// Global state consists of:
+//	- palette (used for the overlay only atm)
+//	- shape (surface, dimensions, hotspot, keycolor)
+//	- visibility
+// These always get updates by ScummVM, no need to differentiate between engines and the overlay.
+
 struct Cursor {
-	void update(const Graphics::Surface &screen, bool isModified);
+	Cursor(AtariGraphicsManager *manager, Screen *screen)
+		: _manager(manager)
+		, _parentScreen(screen) {
+	}
+
+	void reset() {
+		_positionChanged = true;
+		_surfaceChanged = true;
+		_visibilityChanged = false;
+
+		_savedRect = Common::Rect();
+	}
+
+	// updates outOfScreen OR srcRect/dstRect (only if visible/needed)
+	void update();
+
+	// visibility
+	bool setVisible(bool visible) {
+		if (_visible == visible) {
+			return _visible;
+		}
+
+		bool last = _visible;
+
+		_visible = visible;
+
+		_visibilityChanged = true;
 
-	bool visible = false;
+		return last;
+	}
 
 	// position
 	Common::Point getPosition() const {
 		return Common::Point(_x, _y);
 	}
 	void setPosition(int x, int y) {
+		if (_x == x && _y == y)
+			return;
+
 		_x = x;
 		_y = y;
-	}
-	void updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen);
-	void swap() {
-		const int tmpX = _oldX;
-		const int tmpY = _oldY;
 
-		_oldX = _x;
-		_oldY = _y;
-
-		_x = tmpX;
-		_y = tmpY;
+		_positionChanged = true;
 	}
+	void updatePosition(int deltaX, int deltaY);
 
 	// surface
 	void setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor);
+	void setPalette(const byte *colors, uint start, uint num);
 	void convertTo(const Graphics::PixelFormat &format);
-	Graphics::Surface surface;
-	Graphics::Surface surfaceMask;
 
-	// rects (valid only if !outOfScreen)
-	bool isClipped() const {
-		return outOfScreen ? false : _width != srcRect.width();
+	bool isVisible() const {
+		return !_outOfScreen && _visible;
+	}
+	bool isChanged() const {
+		return _positionChanged || _surfaceChanged || _visibilityChanged;
 	}
-	bool outOfScreen = true;
-	Common::Rect srcRect;
-	Common::Rect dstRect;
 
-	// palette (only used for the overlay)
-	byte palette[256*3] = {};
+	bool intersects(const Common::Rect &rect) const {
+		return rect.intersects(_dstRect);
+	}
+
+	void flushBackground(const Graphics::Surface &srcSurface, const Common::Rect &rect);
+	bool restoreBackground(const Graphics::Surface &srcSurface, bool force);
+	bool draw(bool directRendering, bool force);
 
 private:
+	static byte _palette[256*3];
+
+	AtariGraphicsManager *_manager;
+	Screen *_parentScreen;
+
+	bool _positionChanged = true;
+	bool _surfaceChanged = true;
+	bool _visibilityChanged = false;
+
+	bool _visible = false;
 	int _x = -1, _y = -1;
-	int _oldX = -1, _oldY = -1;
+	bool _outOfScreen = true;
+	Common::Rect _srcRect;
+	Common::Rect _dstRect;
+
+	Graphics::Surface _savedBackground;	// used by direct rendering
+	Common::Rect _savedRect;
 
 	// related to 'surface'
 	const byte *_buf = nullptr;
@@ -80,6 +129,8 @@ private:
 	int _hotspotY;
 	uint32 _keycolor;
 
+	Graphics::Surface _surface;
+	Graphics::Surface _surfaceMask;
 	int _rShift, _gShift, _bShift;
 	int _rMask, _gMask, _bMask;
 };
diff --git a/backends/graphics/atari/atari-graphics.cpp b/backends/graphics/atari/atari-graphics.cpp
index 818be679cee..405a9ff2014 100644
--- a/backends/graphics/atari/atari-graphics.cpp
+++ b/backends/graphics/atari/atari-graphics.cpp
@@ -345,9 +345,9 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
 	_chunkySurface.init(_pendingState.width, _pendingState.height, _pendingState.width,
 		_chunkySurface.getPixels(), _pendingState.format);
 
-	_screen[FRONT_BUFFER]->reset(_pendingState.width, _pendingState.height, 8);
-	_screen[BACK_BUFFER1]->reset(_pendingState.width, _pendingState.height, 8);
-	_screen[BACK_BUFFER2]->reset(_pendingState.width, _pendingState.height, 8);
+	_screen[FRONT_BUFFER]->reset(_pendingState.width, _pendingState.height, 8, true);
+	_screen[BACK_BUFFER1]->reset(_pendingState.width, _pendingState.height, 8, true);
+	_screen[BACK_BUFFER2]->reset(_pendingState.width, _pendingState.height, 8, true);
 	_workScreen = _screen[_pendingState.mode <= GraphicsMode::SingleBuffering ? FRONT_BUFFER : BACK_BUFFER1];
 
 	s_screenSurf = nullptr;
@@ -361,15 +361,6 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
 	_palette.clear();
 	_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
 
-	static bool firstRun = true;
-	if (firstRun) {
-		_cursor.setPosition(getOverlayWidth() / 2, getOverlayHeight() / 2);
-		_cursor.swap();
-		firstRun = false;
-	}
-
-	warpMouse(_pendingState.width / 2, _pendingState.height / 2);
-
 	_currentState = _pendingState;
 
 	return OSystem::kTransactionSuccess;
@@ -513,30 +504,29 @@ void AtariGraphicsManager::updateScreen() {
 		_checkUnalignedPitch = false;
 	}
 
-	// updates outOfScreen OR srcRect/dstRect (only if visible/needed)
-	_cursor.update(*lockScreen(), _workScreen->cursorPositionChanged || _workScreen->cursorSurfaceChanged);
+	_workScreen->cursor.update();
 
 	bool screenUpdated = false;
 
 	if (isOverlayVisible()) {
 		assert(_workScreen == _screen[OVERLAY_BUFFER]);
 		if (isOverlayDirectRendering())
-			screenUpdated = updateScreenInternal<true>(Graphics::Surface());
+			screenUpdated = updateScreenInternal(Graphics::Surface());
 		else
-			screenUpdated = updateScreenInternal<false>(_overlaySurface);
+			screenUpdated = updateScreenInternal(_overlaySurface);
 	} else {
 		switch (_currentState.mode) {
 		case GraphicsMode::DirectRendering:
 			assert(_workScreen == _screen[FRONT_BUFFER]);
-			screenUpdated = updateScreenInternal<true>(Graphics::Surface());
+			screenUpdated = updateScreenInternal(Graphics::Surface());
 			break;
 		case GraphicsMode::SingleBuffering:
 			assert(_workScreen == _screen[FRONT_BUFFER]);
-			screenUpdated = updateScreenInternal<false>(_chunkySurface);
+			screenUpdated = updateScreenInternal(_chunkySurface);
 			break;
 		case GraphicsMode::TripleBuffering:
 			assert(_workScreen == _screen[BACK_BUFFER1]);
-			screenUpdated = updateScreenInternal<false>(_chunkySurface);
+			screenUpdated = updateScreenInternal(_chunkySurface);
 			break;
 		}
 	}
@@ -725,21 +715,17 @@ void AtariGraphicsManager::showOverlay(bool inGUI) {
 		return;
 
 	if (_currentState.mode == GraphicsMode::DirectRendering) {
-		// make sure that _oldCursorRect is used to restore the original game graphics
-		// (but only if resolution hasn't changed, see endGFXTransaction())
-		bool wasVisible = showMouse(false);
-
-		// revert back but don't update screen
-		_cursor.visible = wasVisible;
+		_workScreen->cursor.restoreBackground(Graphics::Surface(), true);
 	}
 
-	_cursor.swap();
 	_oldWorkScreen = _workScreen;
 	_workScreen = _screen[OVERLAY_BUFFER];
 
 	// do not cache dirtyRects and oldCursorRect
 	const int bitsPerPixel = getBitsPerPixel(getOverlayFormat());
-	_workScreen->reset(getOverlayWidth(), getOverlayHeight(), bitsPerPixel);
+	static bool resetCursorPosition = true;
+	_workScreen->reset(getOverlayWidth(), getOverlayHeight(), bitsPerPixel, resetCursorPosition);
+	resetCursorPosition = false;
 
 	_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
 
@@ -756,7 +742,6 @@ void AtariGraphicsManager::hideOverlay() {
 
 	_workScreen = _oldWorkScreen;
 	_oldWorkScreen = nullptr;
-	_cursor.swap();
 
 	// FIXME: perhaps there's a better way but this will do for now
 	_checkUnalignedPitch = true;
@@ -886,14 +871,13 @@ void AtariGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x,
 bool AtariGraphicsManager::showMouse(bool visible) {
 	//debug("showMouse: %d", visible);
 
-	if (_cursor.visible == visible) {
-		return visible;
-	}
+	bool last = _workScreen->cursor.setVisible(visible);
 
-	bool last = _cursor.visible;
-	_cursor.visible = visible;
+	if (!isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
+		_screen[BACK_BUFFER2]->cursor.setVisible(visible);
+		_screen[FRONT_BUFFER]->cursor.setVisible(visible);
+	}
 
-	cursorVisibilityChanged();
 	// don't rely on engines to call it (if they don't it confuses the cursor restore logic)
 	updateScreen();
 
@@ -903,8 +887,12 @@ bool AtariGraphicsManager::showMouse(bool visible) {
 void AtariGraphicsManager::warpMouse(int x, int y) {
 	//debug("warpMouse: %d, %d", x, y);
 
-	_cursor.setPosition(x, y);
-	cursorPositionChanged();
+	_workScreen->cursor.setPosition(x, y);
+
+	if (!isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
+		_screen[BACK_BUFFER2]->cursor.setPosition(x, y);
+		_screen[FRONT_BUFFER]->cursor.setPosition(x, y);
+	}
 }
 
 void AtariGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor,
@@ -917,20 +905,33 @@ void AtariGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int h
 	if (format)
 		assert(*format == PIXELFORMAT_CLUT8);
 
-	_cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
-	cursorSurfaceChanged();
+	_workScreen->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
+
+	if (!isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
+		_screen[BACK_BUFFER2]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
+		_screen[FRONT_BUFFER]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
+	}
 }
 
 void AtariGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) {
 	debug("setCursorPalette: %d, %d", start, num);
 
-	memcpy(&_cursor.palette[start * 3], colors, num * 3);
-	cursorSurfaceChanged();
+	_workScreen->cursor.setPalette(colors, start, num);
+
+	if (!isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
+		// avoid copying the same (shared) palette two more times...
+		_screen[BACK_BUFFER2]->cursor.setPalette(nullptr, 0, 0);
+		_screen[FRONT_BUFFER]->cursor.setPalette(nullptr, 0, 0);
+	}
 }
 
 void AtariGraphicsManager::updateMousePosition(int deltaX, int deltaY) {
-	_cursor.updatePosition(deltaX, deltaY, *lockScreen());
-	cursorPositionChanged();
+	_workScreen->cursor.updatePosition(deltaX, deltaY);
+
+	if (!isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
+		_screen[BACK_BUFFER2]->cursor.updatePosition(deltaX, deltaY);
+		_screen[FRONT_BUFFER]->cursor.updatePosition(deltaX, deltaY);
+	}
 }
 
 bool AtariGraphicsManager::notifyEvent(const Common::Event &event) {
@@ -983,103 +984,38 @@ void AtariGraphicsManager::freeSurfaces() {
 	_overlaySurface.free();
 }
 
-template <bool directRendering>	// hopefully compiler optimizes all the branching out
 bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurface) {
 	//debug("updateScreenInternal");
 
 	const Screen::DirtyRects &dirtyRects = _workScreen->dirtyRects;
 	Graphics::Surface *dstSurface        = _workScreen->offsettedSurf;
-	bool &cursorPositionChanged          = _workScreen->cursorPositionChanged;
-	bool &cursorSurfaceChanged           = _workScreen->cursorSurfaceChanged;
-	bool &cursorVisibilityChanged        = _workScreen->cursorVisibilityChanged;
-	Common::Rect &oldCursorRect          = _workScreen->oldCursorRect;
-	const bool &fullRedraw               = _workScreen->fullRedraw;
+	Cursor &cursor                       = _workScreen->cursor;
 
+	const bool directRendering           = srcSurface.getPixels() == nullptr;
 	const int dstBitsPerPixel            = getBitsPerPixel(dstSurface->format);
 
 	bool updated = false;
 
-	const bool cursorDrawEnabled = !_cursor.outOfScreen && _cursor.visible;
-	bool drawCursor = cursorDrawEnabled
-		&& (cursorPositionChanged || cursorSurfaceChanged || cursorVisibilityChanged || fullRedraw);
-
-	assert(!fullRedraw || oldCursorRect.isEmpty());
-
-	bool restoreCursor = !oldCursorRect.isEmpty()
-		&& (cursorPositionChanged || cursorSurfaceChanged || (cursorVisibilityChanged && !_cursor.visible));
+	const bool cursorDrawEnabled = cursor.isVisible();
+	bool forceCursorDraw = cursorDrawEnabled && (_workScreen->fullRedraw || cursor.isChanged());
 
 	lockSuperBlitter();
 
 	for (auto it = dirtyRects.begin(); it != dirtyRects.end(); ++it) {
-		if (cursorDrawEnabled && !drawCursor)
-			drawCursor = it->intersects(_cursor.dstRect);
+		if (cursorDrawEnabled && !forceCursorDraw)
+			forceCursorDraw = cursor.intersects(*it);
 
 		if (!directRendering) {
 			copyRectToSurface(*dstSurface, dstBitsPerPixel, srcSurface, it->left, it->top, *it);
-			updated = true;
+			updated |= true;
 		}
 	}
 
-	if (restoreCursor) {
-		//debug("Restore cursor: %d %d %d %d", oldCursorRect.left, oldCursorRect.top, oldCursorRect.width(), oldCursorRect.height());
-
-		// always restore aligned oldCursorRect
-		oldCursorRect = alignRect(oldCursorRect);
-
-		if (!directRendering) {
-			copyRectToSurface(
-				*dstSurface, dstBitsPerPixel, srcSurface,
-				oldCursorRect.left, oldCursorRect.top,
-				oldCursorRect);
-		} else {
-			_workScreen->restoreBackground(oldCursorRect);
-		}
-
-		oldCursorRect = Common::Rect();
-
-		updated = true;
-	}
+	updated |= cursor.restoreBackground(srcSurface, false);
 
 	unlockSuperBlitter();
 
-	if (drawCursor) {
-		//debug("Redraw cursor: %d %d %d %d", _cursor.dstRect.left, _cursor.dstRect.top, _cursor.dstRect.width(), _cursor.dstRect.height());
-
-		if (cursorSurfaceChanged || _cursor.isClipped()) {
-			_cursor.convertTo(dstSurface->format);
-			{
-				// copy in-place (will do nothing on regular Surface::copyRectToSurface)
-				Graphics::Surface surf;
-				surf.init(
-					_cursor.surface.w,
-					_cursor.surface.h,
-					_cursor.surface.pitch * dstBitsPerPixel / 8,	// 4bpp is not byte per pixel anymore
-					_cursor.surface.getPixels(),
-					_cursor.surface.format);
-				copyRectToSurface(
-					surf, dstBitsPerPixel, _cursor.surface,
-					0, 0,
-					Common::Rect(_cursor.surface.w, _cursor.surface.h));
-			}
-		}
-
-		if (directRendering)
-			_workScreen->storeBackground(alignRect(_cursor.dstRect));
-
-		// don't use _cursor.srcRect for width as this must be aligned first
-		// (_cursor.surface.w is recalculated thanks to _cursor.isClipped())
-		drawMaskedSprite(
-			*dstSurface, dstBitsPerPixel, _cursor.surface, _cursor.surfaceMask,
-			_cursor.dstRect.left, _cursor.dstRect.top,
-			Common::Rect(0, _cursor.srcRect.top, _cursor.surface.w, _cursor.srcRect.bottom));
-
-		cursorPositionChanged = cursorSurfaceChanged = false;
-		oldCursorRect = _cursor.dstRect;
-
-		updated = true;
-	}
-
-	cursorVisibilityChanged = false;
+	updated |= cursor.draw(directRendering, forceCursorDraw);
 
 	return updated;
 }
diff --git a/backends/graphics/atari/atari-graphics.h b/backends/graphics/atari/atari-graphics.h
index 164d32232b3..4bc238a01c2 100644
--- a/backends/graphics/atari/atari-graphics.h
+++ b/backends/graphics/atari/atari-graphics.h
@@ -37,6 +37,7 @@
 #define MAX_V_SHAKE  16
 
 class AtariGraphicsManager : public GraphicsManager, Common::EventObserver {
+	friend class Cursor;
 	friend class Screen;
 
 public:
@@ -97,7 +98,7 @@ public:
 						bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override;
 	void setCursorPalette(const byte *colors, uint start, uint num) override;
 
-	Common::Point getMousePosition() const { return _cursor.getPosition(); }
+	Common::Point getMousePosition() const { return _workScreen->cursor.getPosition(); }
 	void updateMousePosition(int deltaX, int deltaY);
 
 	bool notifyEvent(const Common::Event &event) override;
@@ -118,7 +119,6 @@ private:
 	int16 getMaximumScreenHeight() const { return 480; }
 	int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 640 : 640*1.2); }
 
-	template <bool directRendering>
 	bool updateScreenInternal(const Graphics::Surface &srcSurface);
 
 	void copyRectToScreenInternal(const void *buf, int pitch, int x, int y, int w, int h,
@@ -152,39 +152,6 @@ private:
 		return alignRect(rect.left, rect.top, rect.width(), rect.height());
 	}
 
-	void cursorPositionChanged() {
-		if (_overlayVisible) {
-			_screen[OVERLAY_BUFFER]->cursorPositionChanged = true;
-		} else {
-			_screen[FRONT_BUFFER]->cursorPositionChanged
-				= _screen[BACK_BUFFER1]->cursorPositionChanged
-				= _screen[BACK_BUFFER2]->cursorPositionChanged
-				= true;
-		}
-	}
-
-	void cursorSurfaceChanged() {
-		if (_overlayVisible) {
-			_screen[OVERLAY_BUFFER]->cursorSurfaceChanged = true;
-		} else {
-			_screen[FRONT_BUFFER]->cursorSurfaceChanged
-				= _screen[BACK_BUFFER1]->cursorSurfaceChanged
-				= _screen[BACK_BUFFER2]->cursorSurfaceChanged
-				= true;
-		}
-	}
-
-	void cursorVisibilityChanged() {
-		if (_overlayVisible) {
-			_screen[OVERLAY_BUFFER]->cursorVisibilityChanged = true;
-		} else {
-			_screen[FRONT_BUFFER]->cursorVisibilityChanged
-				= _screen[BACK_BUFFER1]->cursorVisibilityChanged
-				= _screen[BACK_BUFFER2]->cursorVisibilityChanged
-				= true;
-		}
-	}
-
 	int getOverlayPaletteSize() const {
 #ifndef DISABLE_FANCY_THEMES
 		return _tt ? 16 : 256;
@@ -244,8 +211,6 @@ private:
 	bool _overlayVisible = false;
 	Graphics::Surface _overlaySurface;
 
-	Cursor _cursor;
-
 	Palette _palette;
 	Palette _overlayPalette;
 };
diff --git a/backends/graphics/atari/atari-screen.cpp b/backends/graphics/atari/atari-screen.cpp
index d1f30dd66aa..a5e8ca713bc 100644
--- a/backends/graphics/atari/atari-screen.cpp
+++ b/backends/graphics/atari/atari-screen.cpp
@@ -23,13 +23,12 @@
 
 #include <mint/falcon.h>
 
-#include "graphics/blit.h"
-
 #include "atari-graphics.h"
 #include "atari-graphics-superblitter.h"
 
 Screen::Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette_)
 	: _manager(manager)
+	, cursor(manager, this)
 	, palette(palette_) {
 	const AtariGraphicsManager::AtariMemAlloc &allocFunc = _manager->getStRamAllocFunc();
 
@@ -64,12 +63,11 @@ Screen::~Screen() {
 	freeFunc((void *)*((uintptr *)surf.getPixels() - 1));
 }
 
-void Screen::reset(int width, int height, int bitsPerPixel) {
-	cursorPositionChanged = true;
-	cursorSurfaceChanged = true;
-	cursorVisibilityChanged = false;
+void Screen::reset(int width, int height, int bitsPerPixel, bool resetCursorPosition) {
 	clearDirtyRects();
-	oldCursorRect = Common::Rect();
+	cursor.reset();
+	if (resetCursorPosition)
+		cursor.setPosition(width / 2, height / 2);
 	rez = -1;
 	mode = -1;
 
@@ -147,62 +145,13 @@ void Screen::addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rec
 		dirtyRects.clear();
 		dirtyRects.emplace(srcSurface.w, srcSurface.h);
 
-		oldCursorRect = Common::Rect();
+		cursor.reset();
 
 		fullRedraw = true;
-		return;
-	}
-
-	dirtyRects.insert(rect);
-
-	if (!oldCursorRect.isEmpty()) {
-		const Common::Rect alignedOldCursorRect = _manager->alignRect(oldCursorRect);
-
-		// we have to check *aligned* oldCursorRect because it is background which gets copied,
-		// i.e. it has to be up to date even outside the cursor rectangle.
-		// do it now to avoid complex checking in updateScreenInternal()
-		if (rect.contains(alignedOldCursorRect)) {
-			oldCursorRect = Common::Rect();
-		} else if (rect.intersects(alignedOldCursorRect)) {
-			if (!directRendering) {
-				_manager->copyRectToSurface(
-					*offsettedSurf, _manager->getBitsPerPixel(offsettedSurf->format), srcSurface,
-					alignedOldCursorRect.left, alignedOldCursorRect.top,
-					alignedOldCursorRect);
-			} else {
-				restoreBackground(alignedOldCursorRect);
-			}
-
-			oldCursorRect = Common::Rect();
-		}
-	}
-}
-
-void Screen::storeBackground(const Common::Rect &rect) {
-	const int bitsPerPixel = _manager->getBitsPerPixel(offsettedSurf->format);
+	} else {
+		dirtyRects.insert(rect);
 
-	if (_cursorBackgroundSurf.w != rect.width()
-		|| _cursorBackgroundSurf.h != rect.height()
-		|| _cursorBackgroundSurf.format != offsettedSurf->format) {
-		_cursorBackgroundSurf.create(rect.width(), rect.height(), offsettedSurf->format);
-		_cursorBackgroundSurf.pitch = _cursorBackgroundSurf.pitch * bitsPerPixel / 8;
+		// do it now to avoid checking in AtariGraphicsManager::updateScreenInternal()
+		cursor.flushBackground(directRendering ? Graphics::Surface() : srcSurface, rect);
 	}
-
-	Graphics::copyBlit(
-		(byte *)_cursorBackgroundSurf.getPixels(),
-		(const byte *)offsettedSurf->getPixels() + rect.top * offsettedSurf->pitch + rect.left * bitsPerPixel / 8,
-		_cursorBackgroundSurf.pitch, offsettedSurf->pitch,
-		rect.width() * bitsPerPixel / 8, rect.height(),	// fake 4bpp by 8bpp's width/2
-		offsettedSurf->format.bytesPerPixel);
-}
-
-void Screen::restoreBackground(const Common::Rect &rect) {
-	const int bitsPerPixel = _manager->getBitsPerPixel(offsettedSurf->format);
-
-	Graphics::copyBlit(
-		(byte *)offsettedSurf->getPixels() + rect.top * offsettedSurf->pitch + rect.left * bitsPerPixel / 8,
-		(const byte *)_cursorBackgroundSurf.getPixels(),
-		offsettedSurf->pitch, _cursorBackgroundSurf.pitch,
-		rect.width() * bitsPerPixel / 8, rect.height(),	// fake 4bpp by 8bpp's width/2
-		offsettedSurf->format.bytesPerPixel);
 }
diff --git a/backends/graphics/atari/atari-screen.h b/backends/graphics/atari/atari-screen.h
index 7ffd188a5e7..0355f649aee 100644
--- a/backends/graphics/atari/atari-screen.h
+++ b/backends/graphics/atari/atari-screen.h
@@ -28,6 +28,8 @@
 #include "common/rect.h"
 #include "graphics/surface.h"
 
+#include "atari-cursor.h"
+
 template<>
 struct std::hash<Common::Rect>
 {
@@ -58,7 +60,7 @@ struct Screen {
 	Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette);
 	~Screen();
 
-	void reset(int width, int height, int bitsPerPixel);
+	void reset(int width, int height, int bitsPerPixel, bool resetCursorPosition);
 	// must be called before any rectangle drawing
 	void addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect, bool directRendering);
 
@@ -67,17 +69,13 @@ struct Screen {
 		fullRedraw = false;
 	}
 
-	void storeBackground(const Common::Rect &rect);
-	void restoreBackground(const Common::Rect &rect);
-
 	Graphics::Surface surf;
 	const Palette *palette;
-	bool cursorPositionChanged = true;
-	bool cursorSurfaceChanged = true;
-	bool cursorVisibilityChanged = false;
 	DirtyRects dirtyRects;
 	bool fullRedraw = false;
-	Common::Rect oldCursorRect;
+
+	Cursor cursor;
+
 	int rez = -1;
 	int mode = -1;
 	Graphics::Surface *const offsettedSurf = &_offsettedSurf;
@@ -103,8 +101,6 @@ private:
 	const AtariGraphicsManager *_manager;
 
 	Graphics::Surface _offsettedSurf;
-	// used by direct rendering
-	Graphics::Surface _cursorBackgroundSurf;
 };
 
 #endif // BACKENDS_GRAPHICS_ATARI_SCREEN_H


Commit: dd49c9e3ceaf7717ba1cd9cc0de7e1c7b856f89d
    https://github.com/scummvm/scummvm/commit/dd49c9e3ceaf7717ba1cd9cc0de7e1c7b856f89d
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2024-07-15T19:42:51+02:00

Commit Message:
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

Changed paths:
    backends/graphics/atari/atari-graphics-asm.S
    backends/graphics/atari/atari-graphics-asm.h
    backends/graphics/atari/atari-graphics.cpp
    backends/graphics/atari/atari-graphics.h
    backends/graphics/atari/atari-screen.h
    backends/mixer/atari/atari-mixer.cpp
    backends/platform/atari/osystem_atari.cpp
    backends/platform/atari/osystem_atari.h
    backends/platform/atari/readme.txt
    base/main.cpp


diff --git a/backends/graphics/atari/atari-graphics-asm.S b/backends/graphics/atari/atari-graphics-asm.S
index a20963db501..90a811172db 100644
--- a/backends/graphics/atari/atari-graphics-asm.S
+++ b/backends/graphics/atari/atari-graphics-asm.S
@@ -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
diff --git a/backends/graphics/atari/atari-graphics-asm.h b/backends/graphics/atari/atari-graphics-asm.h
index 26812443583..51a8bd40900 100644
--- a/backends/graphics/atari/atari-graphics-asm.h
+++ b/backends/graphics/atari/atari-graphics-asm.h
@@ -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.
  *
diff --git a/backends/graphics/atari/atari-graphics.cpp b/backends/graphics/atari/atari-graphics.cpp
index 405a9ff2014..0ef2662311e 100644
--- a/backends/graphics/atari/atari-graphics.cpp
+++ b/backends/graphics/atari/atari-graphics.cpp
@@ -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())
+		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);
-		VsetScreen(SCR_NOCHANGE, s_oldPhysbase, SCR_NOCHANGE, SCR_NOCHANGE);
+
+		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;
+			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;
 
-			_pendingScreenChange |= kPendingScreenChangeScreen;
-			updateScreen();
+		// 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 {
-			// ignore new value in overlay
-			_aspectRatioCorrection = _oldAspectRatioCorrection;
+			// 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());
diff --git a/backends/graphics/atari/atari-graphics.h b/backends/graphics/atari/atari-graphics.h
index 4bc238a01c2..36c6066d678 100644
--- a/backends/graphics/atari/atari-graphics.h
+++ b/backends/graphics/atari/atari-graphics.h
@@ -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;
+		bool aspectRatioCorrection = false;
+
+		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;
 	};
-	GraphicsState _pendingState{ (GraphicsMode)getDefaultGraphicsMode() };
-	GraphicsState _currentState{ (GraphicsMode)getDefaultGraphicsMode() };
-
-	enum PendingScreenChange {
-		kPendingScreenChangeNone	= 0,
-		kPendingScreenChangeMode	= 1<<0,
-		kPendingScreenChangeScreen	= 1<<1,
-		kPendingScreenChangePalette	= 1<<2
-	};
-	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;
diff --git a/backends/graphics/atari/atari-screen.h b/backends/graphics/atari/atari-screen.h
index 0355f649aee..abf1b156f7c 100644
--- a/backends/graphics/atari/atari-screen.h
+++ b/backends/graphics/atari/atari-screen.h
@@ -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
 
diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
index 7647e5acbb7..e7eae85fd91 100644
--- a/backends/mixer/atari/atari-mixer.cpp
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -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
diff --git a/backends/platform/atari/osystem_atari.cpp b/backends/platform/atari/osystem_atari.cpp
index 5b3a9cd7ec0..59e3fa01506 100644
--- a/backends/platform/atari/osystem_atari.cpp
+++ b/backends/platform/atari/osystem_atari.cpp
@@ -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();
diff --git a/backends/platform/atari/osystem_atari.h b/backends/platform/atari/osystem_atari.h
index 2afc60b97b2..6f428a64859 100644
--- a/backends/platform/atari/osystem_atari.h
+++ b/backends/platform/atari/osystem_atari.h
@@ -51,7 +51,6 @@ public:
 private:
 	long _startTime;
 
-	bool _videoInitialized = false;
 	bool _timerInitialized = false;
 
 	int16 _vdi_handle;
diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index fbc49f7de70..4081e2128fc 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -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
 ------------
diff --git a/base/main.cpp b/base/main.cpp
index 65e9cf34a2b..04cc2a02d9c 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -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.


Commit: 4c98561eec488fe229542e70bd9968bda9eb3628
    https://github.com/scummvm/scummvm/commit/4c98561eec488fe229542e70bd9968bda9eb3628
Author: Thorsten Otto (admin at tho-otto.de)
Date: 2024-07-15T19:42:51+02:00

Commit Message:
BACKENDS: ATARI: Move timer interrupt to ossystem_atari.cpp

Changed paths:
  R backends/platform/atari/atari_200hz.S
    backends/platform/atari/module.mk
    backends/platform/atari/osystem_atari.cpp


diff --git a/backends/platform/atari/atari_200hz.S b/backends/platform/atari/atari_200hz.S
deleted file mode 100644
index cec28f10c34..00000000000
--- a/backends/platform/atari/atari_200hz.S
+++ /dev/null
@@ -1,65 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
-*
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
- #include "symbols.h"
-
-	.globl	SYM(atari_200hz_init)
-	.globl	SYM(atari_200hz_shutdown)
-
-	.globl	SYM(counter_200hz)
-
-	.text
-
-SYM(atari_200hz_init):
-	move	sr,-(sp)
-	or	#0x700,sr
-
-	move.l	0x114.w,old_200hz
-	move.l	#my_200hz,0x114.w
-
-	move	(sp)+,sr
-	rts
-
-SYM(atari_200hz_shutdown):
-	move	sr,-(sp)
-	or	#0x700,sr
-
-	move.l	old_200hz,0x114.w
-
-	move	(sp)+,sr
-	rts
-
-	.ascii	"XBRA"
-	.ascii	"SCUM"
-old_200hz:
-	dc.l	0
-my_200hz:
-	addq.l	#1,SYM(counter_200hz)
-
-	move.l	old_200hz,-(sp)
-	rts
-
-
-	.bss
-	.even
-
-SYM(counter_200hz):
-	ds.l	1
diff --git a/backends/platform/atari/module.mk b/backends/platform/atari/module.mk
index ea1882a0480..015ae50f7eb 100644
--- a/backends/platform/atari/module.mk
+++ b/backends/platform/atari/module.mk
@@ -2,7 +2,6 @@ MODULE := backends/platform/atari
 
 MODULE_OBJS := \
 	osystem_atari.o \
-	atari_200hz.o \
 	atari_ikbd.o \
 	native_features.o \
 	dlmalloc.o
diff --git a/backends/platform/atari/osystem_atari.cpp b/backends/platform/atari/osystem_atari.cpp
index 59e3fa01506..3617b1b3f2f 100644
--- a/backends/platform/atari/osystem_atari.cpp
+++ b/backends/platform/atari/osystem_atari.cpp
@@ -72,17 +72,60 @@ typedef void (*KBDVEC)(void *);
 extern "C" KBDVEC atari_old_kbdvec;
 extern "C" KBDVEC atari_old_mousevec;
 
-extern "C" void atari_200hz_init();
-extern "C" void atari_200hz_shutdown();
-extern "C" volatile uint32 counter_200hz;
-
 extern void nf_init(void);
 extern void nf_print(const char* msg);
 
 static int s_app_id = -1;
 
+static volatile uint32 counter_200hz;
+
 static bool exit_already_called = false;
 
+static long atari_200hz_init(void)
+{
+	__asm__ __volatile__(
+	"\tmove		%%sr,-(%%sp)\n"
+	"\tor.w		#0x700,%%sr\n"
+
+	"\tmove.l	0x114.w,old_200hz\n"
+	"\tmove.l	#my_200hz,0x114.w\n"
+
+	"\tmove		(%%sp)+,%%sr\n"
+	"\tjbra		1f\n"
+
+	"\tdc.l		0x58425241\n" /* "XBRA" */
+	"\tdc.l		0x5343554d\n" /* "SCUM" */
+"old_200hz:\n"
+	"\tdc.l		0\n"
+"my_200hz:\n"
+	"\taddq.l	#1,%0\n"
+
+	"\tmove.l	old_200hz(%%pc),-(%%sp)\n"
+	"\trts\n"
+"1:\n"
+	: /* output */
+	: "m"(counter_200hz) /* inputs */
+	: "memory", "cc");
+
+	return 0;
+}
+
+static long atari_200hz_shutdown(void)
+{
+	__asm__ __volatile__(
+	"\tmove		%%sr,-(%%sp)\n"
+	"\tor.w		#0x700,%%sr\n"
+
+	"\tmove.l	old_200hz,0x114.w\n"
+
+	"\tmove		(%%sp)+,%%sr\n"
+	: /* output */
+	: /* inputs */
+	: "memory", "cc");
+
+	return 0;
+}
+
 static void critical_restore() {
 	extern void AtariAudioShutdown();
 	extern void AtariGraphicsShutdown();


Commit: 7dabb755dd526269c2b75b3e1a4b4b16947ff642
    https://github.com/scummvm/scummvm/commit/7dabb755dd526269c2b75b3e1a4b4b16947ff642
Author: Thorsten Otto (admin at tho-otto.de)
Date: 2024-07-15T19:42:51+02:00

Commit Message:
BACKENDS: ATARI: Fixes to build scripts

Changed paths:
    backends/platform/atari/build-release.sh
    backends/platform/atari/build-release030.sh


diff --git a/backends/platform/atari/build-release.sh b/backends/platform/atari/build-release.sh
index dbbd47ea15c..72261e46278 100755
--- a/backends/platform/atari/build-release.sh
+++ b/backends/platform/atari/build-release.sh
@@ -10,7 +10,7 @@ PLATFORM=m68k-atari-mintelf
 
 export ASFLAGS="-m68020-60"
 export CXXFLAGS="-m68020-60 -DUSE_MOVE16 -DUSE_SUPERVIDEL -DUSE_SV_BLITTER"
-export LDFLAGS="-m68020-60"
+export LDFLAGS="-m68020-60 -Wl,--msuper-memory"
 export PKG_CONFIG_LIBDIR="$(${PLATFORM}-gcc -print-sysroot)/usr/lib/m68020-60/pkgconfig"
 
 if [ ! -f config.log ]
@@ -37,13 +37,10 @@ then
 	--disable-detection-full
 fi
 
-make -j 16
+make -j$(getconf _NPROCESSORS_CONF)
 rm -rf dist-generic
 make dist-generic
 
-# make memory protection friendly
-${PLATFORM}-flags -S dist-generic/scummvm/scummvm.ttp
-
 # create symbol file and strip
 ${PLATFORM}-nm -C dist-generic/scummvm/scummvm.ttp | grep -vF ' .L' | grep ' [TtWV] ' | ${PLATFORM}-c++filt | sort -u > dist-generic/scummvm/scummvm.sym
 ${PLATFORM}-strip -s dist-generic/scummvm/scummvm.ttp
diff --git a/backends/platform/atari/build-release030.sh b/backends/platform/atari/build-release030.sh
index a94d4254714..2502bc7e9f0 100755
--- a/backends/platform/atari/build-release030.sh
+++ b/backends/platform/atari/build-release030.sh
@@ -10,7 +10,7 @@ PLATFORM=m68k-atari-mintelf
 
 export ASFLAGS="-m68030"
 export CXXFLAGS="-m68030 -DDISABLE_FANCY_THEMES"
-export LDFLAGS="-m68030"
+export LDFLAGS="-m68030 -Wl,--msuper-memory"
 export PKG_CONFIG_LIBDIR="$(${PLATFORM}-gcc -print-sysroot)/usr/lib/m68020-60/pkgconfig"
 
 if [ ! -f config.log ]
@@ -39,13 +39,10 @@ then
 	--disable-detection-full
 fi
 
-make -j 16
+make -j$(getconf _NPROCESSORS_CONF)
 rm -rf dist-generic
 make dist-generic
 
-# make memory protection friendly
-${PLATFORM}-flags -S dist-generic/scummvm/scummvm.ttp
-
 # create symbol file and strip
 ${PLATFORM}-nm -C dist-generic/scummvm/scummvm.ttp | grep -vF ' .L' | grep ' [TtWV] ' | ${PLATFORM}-c++filt | sort -u > dist-generic/scummvm/scummvm.sym
 ${PLATFORM}-strip -s dist-generic/scummvm/scummvm.ttp


Commit: 30670e0a46b234808d8e44f4978f4afd0e2bea20
    https://github.com/scummvm/scummvm/commit/30670e0a46b234808d8e44f4978f4afd0e2bea20
Author: Thorsten Otto (admin at tho-otto.de)
Date: 2024-07-15T19:42:51+02:00

Commit Message:
BACKENDS: ATARI: Add support for -mfastcall

Changed paths:
    backends/graphics/atari/atari-c2p-asm.S
    backends/graphics/atari/atari-graphics-asm.S
    backends/platform/atari/build-release.sh
    backends/platform/atari/build-release030.sh


diff --git a/backends/graphics/atari/atari-c2p-asm.S b/backends/graphics/atari/atari-c2p-asm.S
index 2da584648e8..14135025c7a 100644
--- a/backends/graphics/atari/atari-c2p-asm.S
+++ b/backends/graphics/atari/atari-c2p-asm.S
@@ -35,9 +35,15 @@
 
 | void asm_c2p1x1_8(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen);
 SYM(asm_c2p1x1_8):
+#ifdef __FASTCALL__
+									| a0: chunky
+	move.l	a1,d0					| chunky end
+	move.l	4(sp),a1				| screen
+#else
 	move.l	(4,sp),a0				| chunky
 	move.l	(8,sp),d0				| chunky end
 	move.l	(12,sp),a1				| screen
+#endif
 	movem.l	d2-d7/a2-a6,-(sp)
 	move.l	d0,a2
 	move.l	#0x0f0f0f0f,d4
@@ -234,10 +240,17 @@ c2p1x1_8_start:
 SYM(asm_c2p1x1_8_tt):
 	movem.l	d2-d7/a2-a6,-(sp)			| 6 + 5 = 11 longs
 
+#ifdef __FASTCALL__
+										| a0: chunky
+	move.l	a1,a2						| a2: chunky end
+	move.l	(11*4+4,sp),a1				| a1: screen
+										| d0.l: screen pitch (double width)
+#else
 	move.l	(11*4+4,sp),a0				| a0: chunky
 	move.l	(11*4+8,sp),a2				| a2: chunky end
 	move.l	(11*4+12,sp),a1				| a1: screen
 	move.l	(11*4+16,sp),d0				| d0.l: screen pitch (double width)
+#endif
 
 	move.l	sp,old_sp
 
@@ -449,12 +462,21 @@ c2p1x1_8_tt_start:
 SYM(asm_c2p1x1_8_rect):
 	movem.l	d2-d7/a2-a6,-(sp)			| 6 + 5 = 11 longs
 
+#ifdef __FASTCALL__
+										| a0: chunky
+	move.l	a1,chunky_end
+										| d0.l: chunky width
+	move.l	(11*4+4,sp),a1				| a1: screen
+	exg		d1,d2						| d2.l: chunky pitch
+										| d1.l: screen pitch
+#else
 	move.l	(11*4+4,sp),a0				| a0: chunky
 	move.l	(11*4+8,sp),chunky_end
 	move.l	(11*4+12,sp),d0				| d0.l: chunky width
 	move.l	(11*4+16,sp),d2				| d2.l: chunky pitch
 	move.l	(11*4+20,sp),a1				| a1: screen
 	move.l	(11*4+24,sp),d1				| d1.l: screen pitch
+#endif
 
 	move.l	sp,old_sp
 
@@ -679,9 +701,15 @@ c2p1x1_8_rect_done:
 
 | void asm_c2p1x1_4(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen);
 SYM(asm_c2p1x1_4):
+#ifdef __FASTCALL__
+									| a0: chunky
+	move.l	a1,d0					| chunky end
+	move.l	4(sp),a1				| screen
+#else
 	move.l	(4,sp),a0				| chunky
 	move.l	(8,sp),d0				| chunky end
 	move.l	(12,sp),a1				| screen
+#endif
 	movem.l	d2-d7/a2-a6,-(sp)
 	move.l	d0,a2
 	move.l	#0x0f0f0f0f,d4
@@ -785,12 +813,21 @@ c2p1x1_4_start:
 SYM(asm_c2p1x1_4_rect):
 	movem.l	d2-d7/a2-a6,-(sp)			| 6 + 5 = 11 longs
 
+#ifdef __FASTCALL__
+										| a0: chunky
+	move.l	a1,chunky_end
+										| d0.l: chunky width
+	move.l	(11*4+4,sp),a1				| a1: screen
+	exg		d1,d2						| d2.l: chunky pitch
+										| d1.l: screen pitch
+#else
 	move.l	(11*4+4,sp),a0				| a0: chunky
 	move.l	(11*4+8,sp),chunky_end
 	move.l	(11*4+12,sp),d0				| d0.l: chunky width
 	move.l	(11*4+16,sp),d2				| d2.l: chunky pitch
 	move.l	(11*4+20,sp),a1				| a1: screen
 	move.l	(11*4+24,sp),d1				| d1.l: screen pitch
+#endif
 
 	move.l	sp,old_sp
 
diff --git a/backends/graphics/atari/atari-graphics-asm.S b/backends/graphics/atari/atari-graphics-asm.S
index 90a811172db..8534876d7ae 100644
--- a/backends/graphics/atari/atari-graphics-asm.S
+++ b/backends/graphics/atari/atari-graphics-asm.S
@@ -32,6 +32,18 @@
 SYM(asm_draw_4bpl_sprite):
 	movem.l	d0-d7/a0-a2,-(sp)		| 11 longs
 
+#ifdef __FASTCALL__
+	move.l	a0,a2					| a2: dstBuffer
+									| a1: srcBuffer
+	move.l	(4+11*4,sp),a0			| a0: srcMask
+									| d0.w: destX
+									| d1.w: destY
+	move.l	d2,d3					| d3.w: dstPitch
+	ext.l	d3						| d3.l: dstPitch
+	move.l	(8+11*4,sp),d6			| d6.w: w
+	lsr.w	#4,d6					| d6.w: w/16
+	move.l	(12+11*4,sp),d7			| d7.w: h
+#else
 	move.l	(4+11*4,sp),a2			| a2: dstBuffer
 	move.l	(8+11*4,sp),a1			| a1: srcBuffer
 	move.l	(12+11*4,sp),a0			| a0: srcMask
@@ -42,6 +54,7 @@ SYM(asm_draw_4bpl_sprite):
 	move.l	(28+11*4,sp),d6			| d6.w: w
 	lsr.w	#4,d6				| d6.w: w/16
 	move.l	(32+11*4,sp),d7			| d7.w: h
+#endif
 
 | Draws a 4 bitplane sprite at any position on screen.
 | (c) 1999 Pieter van der Meer (EarX)
@@ -130,6 +143,18 @@ sprite4_xloop:
 SYM(asm_draw_8bpl_sprite):
 	movem.l	d0-d7/a0-a2,-(sp)		| 11 longs
 
+#ifdef __FASTCALL__
+	move.l	a0,a2					| a2: dstBuffer
+									| a1: srcBuffer
+	move.l	(4+11*4,sp),a0			| a0: srcMask
+									| d0.w: destX
+									| d1.w: destY
+	move.l	d2,d3					| d3.w: dstPitch
+	ext.l	d3						| d3.l: dstPitch
+	move.l	(8+11*4,sp),d6			| d6.w: w
+	lsr.w	#4,d6					| d6.w: w/16
+	move.l	(12+11*4,sp),d7			| d7.w: h
+#else
 	move.l	(4+11*4,sp),a2			| a2: dstBuffer
 	move.l	(8+11*4,sp),a1			| a1: srcBuffer
 	move.l	(12+11*4,sp),a0			| a0: srcMask
@@ -140,6 +165,7 @@ SYM(asm_draw_8bpl_sprite):
 	move.l	(28+11*4,sp),d6			| d6.w: w
 	lsr.w	#4,d6				| d6.w: w/16
 	move.l	(32+11*4,sp),d7			| d7.w: h
+#endif
 
 	move.w  d0,d2				| / Calculate the
 	andi.w  #0b111111110000,d0		| | number of bits
diff --git a/backends/platform/atari/build-release.sh b/backends/platform/atari/build-release.sh
index 72261e46278..4f6d14e075a 100755
--- a/backends/platform/atari/build-release.sh
+++ b/backends/platform/atari/build-release.sh
@@ -7,12 +7,20 @@ mkdir -p build-release
 cd build-release
 
 PLATFORM=m68k-atari-mintelf
+FASTCALL=false
 
 export ASFLAGS="-m68020-60"
 export CXXFLAGS="-m68020-60 -DUSE_MOVE16 -DUSE_SUPERVIDEL -DUSE_SV_BLITTER"
 export LDFLAGS="-m68020-60 -Wl,--msuper-memory"
 export PKG_CONFIG_LIBDIR="$(${PLATFORM}-gcc -print-sysroot)/usr/lib/m68020-60/pkgconfig"
 
+if $FASTCALL
+then
+	ASFLAGS="$ASFLAGS -mfastcall"
+	CXXFLAGS="$CXXFLAGS -mfastcall"
+	LDFLAGS="$LDFLAGS -mfastcall"
+fi
+
 if [ ! -f config.log ]
 then
 ../configure \
diff --git a/backends/platform/atari/build-release030.sh b/backends/platform/atari/build-release030.sh
index 2502bc7e9f0..d08b1b943fb 100755
--- a/backends/platform/atari/build-release030.sh
+++ b/backends/platform/atari/build-release030.sh
@@ -7,12 +7,20 @@ mkdir -p build-release030
 cd build-release030
 
 PLATFORM=m68k-atari-mintelf
+FASTCALL=false
 
 export ASFLAGS="-m68030"
 export CXXFLAGS="-m68030 -DDISABLE_FANCY_THEMES"
 export LDFLAGS="-m68030 -Wl,--msuper-memory"
 export PKG_CONFIG_LIBDIR="$(${PLATFORM}-gcc -print-sysroot)/usr/lib/m68020-60/pkgconfig"
 
+if $FASTCALL
+then
+	ASFLAGS="$ASFLAGS -mfastcall"
+	CXXFLAGS="$CXXFLAGS -mfastcall"
+	LDFLAGS="$LDFLAGS -mfastcall"
+fi
+
 if [ ! -f config.log ]
 then
 ../configure \




More information about the Scummvm-git-logs mailing list