[Scummvm-git-logs] scummvm master -> 5ba26fdf35e3358dad4bca5c3e5aa7a4e62c202c

sev- noreply at scummvm.org
Sat Mar 4 22:38:49 UTC 2023


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

Summary:
a359ee1316 COMMON: Fix build when zlib not present
387ceddef1 AUDIO: Use 49170 Hz instead of 44100 Hz for the FreeMiNT platform
06c9928f3a COMMON: gfx_mode is not always propagated
9285e5a66e COMMON: Fix null graphics backend without USE_RGB_COLOR
4d21a496d2 BACKENDS: ATARI: Add new backend (graphics, mixer, platform)
276cf354bf BACKENDS: ATARI: Separate AtariEventSource from OSystem_Atari
72f6c4ef61 SCUMM: Increase {min,max}HeapThreshold for ATARI
896d7cf309 BACKENDS: ATARI: Use "saves" directory for savegames
4892d0b48b CONFIGURE: Adjustments for ATARI
5ba26fdf35 GRAPHICS: ATARI: Introduce accelerated blitting


Commit: a359ee131676b2ef308dc2b4f78df4068fcb19c8
    https://github.com/scummvm/scummvm/commit/a359ee131676b2ef308dc2b4f78df4068fcb19c8
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
COMMON: Fix build when zlib not present

Changed paths:
    common/compression/vise.cpp


diff --git a/common/compression/vise.cpp b/common/compression/vise.cpp
index 83bfbe25d3d..c1ada443a78 100644
--- a/common/compression/vise.cpp
+++ b/common/compression/vise.cpp
@@ -185,10 +185,14 @@ Common::SeekableReadStream *MacVISEArchive::ArchiveMember::createReadStream() co
 	//
 	// If this turns out to be significant, then this will need to be updated to pass information to the deflate decompressor to
 	// handle the non-standard behavior.
+#if defined(USE_ZLIB)
 	if (!Common::inflateZlibHeaderless(decompressedData, uncompressedSize, &compressedData[0], compressedSize)) {
 		free(decompressedData);
 		return nullptr;
 	}
+#else
+	return nullptr;
+#endif
 
 	return new Common::MemoryReadStream(decompressedData, uncompressedSize, DisposeAfterUse::YES);
 }


Commit: 387ceddef1d1ec302898708c5efab28951cc55d3
    https://github.com/scummvm/scummvm/commit/387ceddef1d1ec302898708c5efab28951cc55d3
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
AUDIO: Use 49170 Hz instead of 44100 Hz for the FreeMiNT platform

Changed paths:
    backends/mixer/sdl/sdl-mixer.cpp


diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp
index ae3ff5dfeac..e7d773e6ad3 100644
--- a/backends/mixer/sdl/sdl-mixer.cpp
+++ b/backends/mixer/sdl/sdl-mixer.cpp
@@ -33,6 +33,8 @@
 #define SAMPLES_PER_SEC 11025
 #elif defined(PLAYSTATION3) || defined(PSP2) || defined(NINTENDO_SWITCH)
 #define SAMPLES_PER_SEC 48000
+#elif defined(__MINT__)
+#define SAMPLES_PER_SEC 49170
 #else
 #define SAMPLES_PER_SEC 44100
 #endif


Commit: 06c9928f3a09a0a2e5f7087e9a14be4612345e24
    https://github.com/scummvm/scummvm/commit/06c9928f3a09a0a2e5f7087e9a14be4612345e24
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
COMMON: gfx_mode is not always propagated

Changed paths:
    engines/engine.cpp


diff --git a/engines/engine.cpp b/engines/engine.cpp
index dc93286b88a..b295e90d43f 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -223,6 +223,14 @@ void initCommonGFX() {
 
 		if (gameDomain->contains("shader"))
 			g_system->setShader(ConfMan.get("shader"));
+
+		// TODO: switching between OpenGL and SurfaceSDL is quite fragile
+		// and the SDL backend doesn't really need this so leave it out
+		// for now to avoid regressions
+#ifndef SDL_BACKEND
+		if (gameDomain->contains("gfx_mode"))
+			g_system->setGraphicsMode(ConfMan.get("gfx_mode").c_str());
+#endif
 	}
 }
 


Commit: 9285e5a66e1ddba14441361b4311c629d6dca490
    https://github.com/scummvm/scummvm/commit/9285e5a66e1ddba14441361b4311c629d6dca490
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
COMMON: Fix null graphics backend without USE_RGB_COLOR

Changed paths:
    backends/graphics/null/null-graphics.h


diff --git a/backends/graphics/null/null-graphics.h b/backends/graphics/null/null-graphics.h
index 60437e23238..885b0c99266 100644
--- a/backends/graphics/null/null-graphics.h
+++ b/backends/graphics/null/null-graphics.h
@@ -32,6 +32,7 @@ public:
 	void setFeatureState(OSystem::Feature f, bool enable) override {}
 	bool getFeatureState(OSystem::Feature f) const override { return false; }
 
+#ifdef USE_RGB_COLOR
 	Graphics::PixelFormat getScreenFormat() const override {
 		return _format;
 	}
@@ -46,6 +47,7 @@ public:
 		list.push_back(Graphics::PixelFormat::createFormatCLUT8());
 		return list;
 	}
+#endif
 
 	void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL) override {
 		_width = width;


Commit: 4d21a496d2c216fcdf5e43dc2dead92ef31da09e
    https://github.com/scummvm/scummvm/commit/4d21a496d2c216fcdf5e43dc2dead92ef31da09e
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
BACKENDS: ATARI: Add new backend (graphics, mixer, platform)

Compile as:

./configure --backend=atari --host=m68k-atari-mint --enable-release --disable-mt32emu --disable-lua --disable-nuked-opl --disable-16bit --disable-scalers --disable-translation --disable-eventrecorder --disable-tts --disable-bink --opengl-mode=none --enable-verbose-build --prefix=/usr --bindir=/ --datarootdir=share --datadir=data && make -j 16 && m68k-atari-mint-flags -S -r ./scummvm && make install DESTDIR=$PWD/_release/ && mv $PWD/_release/scummvm $PWD/_release/scummvm.ttp

Changed paths:
  A backends/graphics/atari/atari-graphics-asm.S
  A backends/graphics/atari/atari-graphics-asm.h
  A backends/graphics/atari/atari-graphics-superblitter.h
  A backends/graphics/atari/atari-graphics-supervidel.h
  A backends/graphics/atari/atari-graphics-videl.h
  A backends/graphics/atari/atari-graphics.cpp
  A backends/graphics/atari/atari-graphics.h
  A backends/graphics/atari/atari_c2p-asm.S
  A backends/graphics/atari/atari_c2p-asm.h
  A backends/graphics/atari/videl-resolutions.cpp
  A backends/graphics/atari/videl-resolutions.h
  A backends/mixer/atari/atari-mixer.cpp
  A backends/mixer/atari/atari-mixer.h
  A backends/platform/atari/atari.cpp
  A backends/platform/atari/atari_ikbd.S
  A backends/platform/atari/module.mk
  A backends/platform/atari/native_features.cpp
  A backends/platform/atari/readme.txt
    backends/module.mk
    configure


diff --git a/backends/graphics/atari/atari-graphics-asm.S b/backends/graphics/atari/atari-graphics-asm.S
new file mode 100644
index 00000000000..aa2a40a0e46
--- /dev/null
+++ b/backends/graphics/atari/atari-graphics-asm.S
@@ -0,0 +1,354 @@
+/* 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/>.
+ *
+ */
+
+	.globl	_asm_screen_tt_save
+	.globl	_asm_screen_falcon_save
+
+	.globl	_asm_screen_tt_restore
+	.globl	_asm_screen_falcon_restore
+
+	.globl	_asm_screen_set_tt_palette
+	.globl	_asm_screen_set_falcon_palette
+
+	.globl	_asm_screen_set_vram
+	.globl	_asm_screen_set_scp_res
+
+	.globl	_vbl_counter
+
+	.text
+
+| extern void asm_screen_tt_save(void);
+|
+_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);
+|
+_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
+
+	| install custom VBL handler
+	move	sr,-(sp)
+	or	#0x700,sr
+
+	move.l	0x70.w,old_vbl
+	move.l	#vbl,0x70.w
+
+	move	(sp)+,sr
+
+	movem.l	(sp)+,d2-d7/a2
+	rts
+
+| extern void asm_screen_tt_restore(void);
+|
+_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);
+|
+_asm_screen_falcon_restore:
+	movem.l	d2-d7/a2,-(sp)
+
+	| uninstall custom VBL handler
+	move	sr,-(sp)
+	or	#0x700,sr
+
+	move.l	old_vbl,0x70.w
+
+	move	(sp)+,sr
+
+	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
+
+| extern void asm_screen_set_tt_palette(const uint16 pPalette[256]);
+|
+_asm_screen_set_tt_palette:
+	move.l	(4,sp),a0
+	lea	0xffff8400.w,a1
+	moveq	#256/2-1,d0
+
+set_tt_palette_loop:
+	move.l	(a0)+,(a1)+
+	dbra	d0,set_tt_palette_loop
+	rts
+
+| extern void asm_screen_set_falcon_palette(const uint32 pPalette[256]);
+|
+_asm_screen_set_falcon_palette:
+	move.l	(4,sp),a0
+	lea	pending_palette,a1
+	moveq	#256/2-1,d0
+
+set_falcon_palette_loop:
+	move.l	(a0)+,(a1)+
+	move.l	(a0)+,(a1)+
+	dbra	d0,set_falcon_palette_loop
+
+	addq.w	#1,has_pending_palette
+	rts
+
+| extern void asm_screen_set_vram(const void* pScreen);
+|
+_asm_screen_set_vram:
+	move.l	(4,sp),pending_vram
+
+	addq.w	#1,has_pending_vram
+	rts
+
+| void asm_screen_set_scp_res(const void* pScp);
+|
+_asm_screen_set_scp_res:
+	move.l	(4,sp),a0
+	lea	(122,a0),a0
+	lea	pending_scp,a1
+	moveq	#(158-122)/4-1,d0
+set_scp_res_loop:
+	move.l	(a0)+,(a1)+
+	dbra	d0,set_scp_res_loop
+
+	addq.w	#1,has_pending_scp
+	rts
+
+wait_vbl:
+	move.w	#0x25,-(sp)			| Vsync()
+	trap	#14				|
+	addq.l	#2,sp				|
+	rts
+
+	.ascii	"XBRA"
+	.ascii	"SCUM"
+old_vbl:
+	dc.l	0
+vbl:
+	movem.l	d0-d1/a0-a1,-(sp)
+
+	addq.l	#1,0x0462.w			| _vbclock
+	addq.l	#1,0x0466.w			| _frclock
+	addq.l	#1,_vbl_counter			| to avoid accessing protected memory
+
+	tst.w	has_pending_scp
+	beq.b	vbl_no_pending_scp
+
+	lea	pending_scp,a0
+	move.l	(a0)+,0xffff8282.w
+	move.l	(a0)+,0xffff8286.w
+	move.l	(a0)+,0xffff828a.w
+	move.l	(a0)+,0xffff82a2.w
+	move.l	(a0)+,0xffff82a6.w
+	move.l	(a0)+,0xffff82aa.w
+	move.w	(a0)+,0xffff820a.w
+	move.w	(a0)+,0xffff82c0.w
+	clr.w	0xffff8266.w
+	tst.w	(a0)+
+	bne.b	vbl_st
+
+vbl_falcon:
+	move.w	(a0)+,0xffff8266.w
+	bra.b	vbl_skip
+
+vbl_st:	addq.l	#1,a0
+	move.b	(a0)+,0xffff8260.w
+
+vbl_skip:
+	move.w	(a0)+,0xffff82c2.w
+	move.w	(a0)+,0xffff8210.w
+
+	clr.w	has_pending_scp
+vbl_no_pending_scp:
+
+	tst.w	has_pending_vram
+	beq.b	vbl_no_pending_vram
+
+	move.l	pending_vram,d0
+	move.l	d0,d1
+	lsr.w	#8,d0
+	move.l	d0,0xffff8200.w
+	move.b	d1,0xffff820d.w
+
+	clr.w	has_pending_vram
+vbl_no_pending_vram:
+
+	tst.w	has_pending_palette
+	beq.b	vbl_no_pending_palette
+
+	lea	pending_palette,a0
+	lea	0xffff9800.w,a1
+	moveq	#256/2-1,d0
+
+vbl_falcon_palette_loop:
+	move.l	(a0)+,(a1)+
+	move.l	(a0)+,(a1)+
+	dbra	d0,vbl_falcon_palette_loop
+
+	clr.w	has_pending_palette
+
+vbl_no_pending_palette:
+	movem.l	(sp)+,d0-d1/a0-a1
+	rte
+
+
+	.bss
+	.even
+
+has_pending_scp:
+	ds.w	1
+has_pending_vram:
+	ds.w	1
+has_pending_palette:
+	ds.w	1
+
+pending_scp:
+	ds.b	158-122				| pending SCP resolution data
+pending_vram:
+	ds.l	1				| pending vram pointer
+pending_palette:
+	ds.l	256				| pending palette
+
+save_pal:
+	ds.l	256+16/2			| old colours (sized for falcon+ste palette)
+save_video:
+	ds.b	32+12+2				| old video regs (size of falcon regs)
+
+_vbl_counter:
+	ds.l	1
diff --git a/backends/graphics/atari/atari-graphics-asm.h b/backends/graphics/atari/atari-graphics-asm.h
new file mode 100644
index 00000000000..a13f6c29d93
--- /dev/null
+++ b/backends/graphics/atari/atari-graphics-asm.h
@@ -0,0 +1,70 @@
+/* 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_ASM_H
+#define BACKENDS_GRAPHICS_ATARI_ASM_H
+
+#include "common/scummsys.h"
+
+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);
+
+/**
+ * Set Atari TT palette.
+ * @param pPalette 256 palette entries (0000 RRRR GGGG BBBB)
+ */
+void asm_screen_set_tt_palette(const uint16 pPalette[256]);
+/**
+ * Set Atari Falcon palette.
+ * @param pPalette 256 palette entries (RRRRRRrr GGGGGGgg 00000000 BBBBBBbb)
+ */
+void asm_screen_set_falcon_palette(const uint32 pPalette[256]);
+
+/**
+ * Set Atari TT/Falcon video base.
+ */
+void asm_screen_set_vram(const void* pScreen);
+
+/**
+ * Set Atari Falcon Videl resolution (Screenspain's SCP format).
+ */
+void asm_screen_set_scp_res(const void* pScp);
+
+}
+
+#endif
diff --git a/backends/graphics/atari/atari-graphics-superblitter.h b/backends/graphics/atari/atari-graphics-superblitter.h
new file mode 100644
index 00000000000..fb295b554a0
--- /dev/null
+++ b/backends/graphics/atari/atari-graphics-superblitter.h
@@ -0,0 +1,53 @@
+/* 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_SUPERBLITTER_H
+#define BACKENDS_GRAPHICS_ATARI_SUPERBLITTER_H
+
+#include <mint/trap14.h>
+
+#define ct60_vm(mode, value) (long)trap_14_wwl((short)0xc60e, (short)(mode), (long)(value))
+#define ct60_vmalloc(value) ct60_vm(0, value)
+#define ct60_vmfree(value)  ct60_vm(1, value)
+
+// bits 26:0
+#define SV_BLITTER_SRC1           ((volatile long*)0x80010058)
+#define SV_BLITTER_SRC2           ((volatile long*)0x8001005C)
+#define SV_BLITTER_DST            ((volatile long*)0x80010060)
+// The amount of bytes that are to be copied in a horizontal line, minus 1
+#define SV_BLITTER_COUNT          ((volatile long*)0x80010064)
+// The amount of bytes that are to be added to the line start adress after a line has been copied, in order to reach the next one
+#define SV_BLITTER_SRC1_OFFSET    ((volatile long*)0x80010068)
+#define SV_BLITTER_SRC2_OFFSET    ((volatile long*)0x8001006C)
+#define SV_BLITTER_DST_OFFSET     ((volatile long*)0x80010070)
+// bits 11:0 - The amount of horizontal lines to do
+#define SV_BLITTER_MASK_AND_LINES ((volatile long*)0x80010074)
+// bit    0 - busy / start
+// bits 4:1 - blit mode
+#define SV_BLITTER_CONTROL        ((volatile long*)0x80010078)
+// bits 9:0
+#define SV_VERSION                ((volatile long*)0x8001007C)
+// bit 0 - empty (read only)
+// bit 1 - full (read only)
+// bits 31:0 - data (write only)
+#define SV_BLITTER_FIFO           ((volatile long*)0x80010080)
+
+#endif
diff --git a/backends/graphics/atari/atari-graphics-supervidel.h b/backends/graphics/atari/atari-graphics-supervidel.h
new file mode 100644
index 00000000000..e7e98b38b5d
--- /dev/null
+++ b/backends/graphics/atari/atari-graphics-supervidel.h
@@ -0,0 +1,201 @@
+/* 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_SUPERVIDEL_H
+#define BACKENDS_GRAPHICS_ATARI_SUPERVIDEL_H
+
+#define USE_SV_BLITTER	// TODO: into configure?
+
+#include "backends/graphics/atari/atari-graphics.h"
+
+#include <cstring>
+#include <mint/osbind.h>
+
+#ifdef USE_SV_BLITTER
+#include "backends/graphics/atari/atari-graphics-superblitter.h"
+#endif
+
+#include "backends/graphics/atari/videl-resolutions.h"
+#include "common/scummsys.h"
+#include "common/textconsole.h"	// for error()
+
+class AtariSuperVidelManager : public AtariGraphicsManager {
+public:
+	AtariSuperVidelManager() {
+#ifdef USE_SV_BLITTER
+		_fwVersion = *SV_VERSION & 0x01ff;
+		debug("SuperVidel FW Revision: %d, using %s", _fwVersion, _fwVersion >= 9
+			  ? "fast async FIFO" : "slower sync blitting" );
+#endif
+
+		for (int i = 0; i < SCREENS; ++i) {
+			if (!allocateAtariSurface(_screen[i], _screenSurface,
+					SCREEN_WIDTH, SCREEN_HEIGHT, PIXELFORMAT8,
+					MX_STRAM, 0xA0000000))
+				error("Failed to allocate screen memory in ST RAM");
+			_screenAligned[i] = (byte*)_screenSurface.getPixels();
+		}
+		_screenSurface.setPixels(_screenAligned[getDefaultGraphicsMode() <= 1 ? FRONT_BUFFER : BACK_BUFFER1]);
+
+		if (!allocateAtariSurface(_chunkyBuffer, _chunkySurface,
+				SCREEN_WIDTH, SCREEN_HEIGHT, PIXELFORMAT8,
+				MX_PREFTTRAM))
+			error("Failed to allocate chunky buffer memory in ST/TT RAM");
+
+		if (!allocateAtariSurface(_overlayScreen, _screenOverlaySurface,
+				getOverlayWidth(), getOverlayHeight(), getOverlayFormat(),
+				MX_STRAM, 0xA0000000))
+			error("Failed to allocate overlay memory in ST RAM");
+
+		if (!allocateAtariSurface(_overlayBuffer, _overlaySurface,
+				getOverlayWidth(), getOverlayHeight(), getOverlayFormat(),
+				MX_PREFTTRAM))
+			error("Failed to allocate overlay buffer memory in ST/TT RAM");
+
+		// patch SPSHIFT for SuperVidel's BPS8C
+		for (byte *p : {scp_320x200x8_vga, scp_320x240x8_vga, scp_640x400x8_vga, scp_640x480x8_vga}) {
+			uint16 *p16 = (uint16*)(p + 122 + 30);
+			*p16 |= 0x1000;
+		}
+	}
+
+	~AtariSuperVidelManager() {
+#ifdef USE_SV_BLITTER
+		ct60_vmfree(_chunkyBuffer);
+#else
+		Mfree(_chunkyBuffer);
+#endif
+		_chunkyBuffer = nullptr;
+
+#ifdef USE_SV_BLITTER
+		ct60_vmfree(_overlayBuffer);
+#else
+		Mfree(_overlayBuffer);
+#endif
+		_overlayBuffer = nullptr;
+	}
+
+	virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const override {
+		static const OSystem::GraphicsMode graphicsModes[] = {
+			{"direct", "Direct rendering", 0},
+			{"single", "Single buffering", 1},
+			{"double", "Double buffering", 2},
+			{"triple", "Triple buffering", 3},
+			{nullptr, nullptr, 0 }
+		};
+		return graphicsModes;
+	}
+
+	int16 getOverlayHeight() const override { return 2 * OVERLAY_HEIGHT; }
+	int16 getOverlayWidth() const override { return 2 * OVERLAY_WIDTH; }
+
+private:
+	virtual void* allocFast(size_t bytes) const override {
+#ifdef USE_SV_BLITTER
+		return (void*)ct60_vmalloc(bytes);
+#else
+		return (void*)Mxalloc(bytes, MX_PREFTTRAM);
+#endif
+	}
+
+	void copySurfaceToSurface(const Graphics::Surface &srcSurface, Graphics::Surface &dstSurface) const override {
+#ifdef USE_SV_BLITTER
+		if (_fwVersion >= 9) {
+			*SV_BLITTER_FIFO = (long)srcSurface.getPixels();	// SV_BLITTER_SRC1
+			*SV_BLITTER_FIFO = 0x00000000;						// SV_BLITTER_SRC2
+			*SV_BLITTER_FIFO = (long)dstSurface.getPixels();	// SV_BLITTER_DST
+			*SV_BLITTER_FIFO = srcSurface.w - 1;				// SV_BLITTER_COUNT
+			*SV_BLITTER_FIFO = srcSurface.pitch;				// SV_BLITTER_SRC1_OFFSET
+			*SV_BLITTER_FIFO = 0x00000000;						// SV_BLITTER_SRC2_OFFSET
+			*SV_BLITTER_FIFO = dstSurface.pitch;				// SV_BLITTER_DST_OFFSET
+			*SV_BLITTER_FIFO = srcSurface.h;					// SV_BLITTER_MASK_AND_LINES
+			*SV_BLITTER_FIFO = 0x01;							// SV_BLITTER_CONTROL
+		} else {
+			sync();
+
+			*SV_BLITTER_SRC1           = (long)srcSurface.getPixels();
+			*SV_BLITTER_SRC2           = 0x00000000;
+			*SV_BLITTER_DST            = (long)dstSurface.getPixels();
+			*SV_BLITTER_COUNT          = srcSurface.w - 1;
+			*SV_BLITTER_SRC1_OFFSET    = srcSurface.pitch;
+			*SV_BLITTER_SRC2_OFFSET    = 0x00000000;
+			*SV_BLITTER_DST_OFFSET     = dstSurface.pitch;
+			*SV_BLITTER_MASK_AND_LINES = srcSurface.h;
+			*SV_BLITTER_CONTROL        = 0x01;
+		}
+#else
+		memcpy(dstSurface.getPixels(), srcSurface.getPixels(), srcSurface.h * srcSurface.pitch);
+#endif
+	}
+
+	void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, Graphics::Surface &dstSurface,
+						   const Common::Rect &subRect) const override {
+#ifdef USE_SV_BLITTER
+		if (_fwVersion >= 9) {
+			*SV_BLITTER_FIFO = (long)srcSurface.getBasePtr(subRect.left, subRect.top);	// SV_BLITTER_SRC1
+			*SV_BLITTER_FIFO = 0x00000000;												// SV_BLITTER_SRC2
+			*SV_BLITTER_FIFO = (long)dstSurface.getBasePtr(destX, destY);				// SV_BLITTER_DST
+			*SV_BLITTER_FIFO = subRect.width() - 1;										// SV_BLITTER_COUNT
+			*SV_BLITTER_FIFO = srcSurface.pitch;										// SV_BLITTER_SRC1_OFFSET
+			*SV_BLITTER_FIFO = 0x00000000;												// SV_BLITTER_SRC2_OFFSET
+			*SV_BLITTER_FIFO = dstSurface.pitch;										// SV_BLITTER_DST_OFFSET
+			*SV_BLITTER_FIFO = subRect.height();										// SV_BLITTER_MASK_AND_LINES
+			*SV_BLITTER_FIFO = 0x01;													// SV_BLITTER_CONTROL
+		} else {
+			sync();
+
+			*SV_BLITTER_SRC1           = (long)srcSurface.getBasePtr(subRect.left, subRect.top);
+			*SV_BLITTER_SRC2           = 0x00000000;
+			*SV_BLITTER_DST            = (long)dstSurface.getBasePtr(destX, destY);
+			*SV_BLITTER_COUNT          = subRect.width() - 1;
+			*SV_BLITTER_SRC1_OFFSET    = srcSurface.pitch;
+			*SV_BLITTER_SRC2_OFFSET    = 0x00000000;
+			*SV_BLITTER_DST_OFFSET     = dstSurface.pitch;
+			*SV_BLITTER_MASK_AND_LINES = subRect.height();
+			*SV_BLITTER_CONTROL        = 0x01;
+		}
+#else
+		dstSurface.copyRectToSurface(srcSurface, destX, destY, subRect);
+#endif
+	}
+
+	void copyRectToSurfaceWithKey(const Graphics::Surface &srcSurface, int destX, int destY, Graphics::Surface &dstSurface,
+								  const Common::Rect &subRect, uint32 key) const override {
+		sync();
+		dstSurface.copyRectToSurfaceWithKey(srcSurface, destX, destY, subRect, key);
+	}
+
+	virtual void sync() const override {
+#ifdef USE_SV_BLITTER
+		// while FIFO not empty...
+		if (_fwVersion >= 9)
+			while (!(*SV_BLITTER_FIFO & 1));
+		// while busy blitting...
+		while (*SV_BLITTER_CONTROL & 1);
+#endif
+	}
+
+#ifdef USE_SV_BLITTER
+	int _fwVersion = 0;
+#endif
+};
+
+#endif
diff --git a/backends/graphics/atari/atari-graphics-videl.h b/backends/graphics/atari/atari-graphics-videl.h
new file mode 100644
index 00000000000..6771085e79d
--- /dev/null
+++ b/backends/graphics/atari/atari-graphics-videl.h
@@ -0,0 +1,162 @@
+/* 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_VIDEL_H
+#define BACKENDS_GRAPHICS_ATARI_VIDEL_H
+
+#include "backends/graphics/atari/atari-graphics.h"
+
+#include <mint/osbind.h>
+
+#include "backends/graphics/atari/atari_c2p-asm.h"
+#include "common/system.h"
+#include "common/textconsole.h"	// for error()
+
+class AtariVidelManager : public AtariGraphicsManager {
+public:
+	AtariVidelManager() {
+		for (int i = 0; i < SCREENS; ++i) {
+			if (!allocateAtariSurface(_screen[i], _screenSurface, SCREEN_WIDTH, SCREEN_HEIGHT, PIXELFORMAT8, MX_STRAM))
+				error("Failed to allocate screen memory in ST RAM");
+			_screenAligned[i] = (byte*)_screenSurface.getPixels();
+		}
+		_screenSurface.setPixels(_screenAligned[getDefaultGraphicsMode() <= 1 ? FRONT_BUFFER : BACK_BUFFER1]);
+
+		if (!allocateAtariSurface(_chunkyBuffer, _chunkySurface, SCREEN_WIDTH, SCREEN_HEIGHT, PIXELFORMAT8, MX_PREFTTRAM))
+			error("Failed to allocate chunky buffer memory in ST/TT RAM");
+
+		if (!allocateAtariSurface(_overlayScreen, _screenOverlaySurface, getOverlayWidth(), getOverlayHeight(),
+								  getOverlayFormat(), MX_STRAM))
+			error("Failed to allocate overlay memory in ST RAM");
+
+		if (!allocateAtariSurface(_overlayBuffer, _overlaySurface, getOverlayWidth(), getOverlayHeight(),
+								  getOverlayFormat(), MX_PREFTTRAM))
+			error("Failed to allocate overlay buffer memory in ST/TT RAM");
+	}
+
+	~AtariVidelManager() {
+		Mfree(_chunkyBuffer);
+		_chunkyBuffer = nullptr;
+
+		Mfree(_overlayBuffer);
+		_overlayBuffer = nullptr;
+	}
+
+	virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const override {
+		static const OSystem::GraphicsMode graphicsModes[] = {
+			{"single", "Single buffering", 1},
+			{"double", "Double buffering", 2},
+			{"triple", "Triple buffering", 3},
+			{nullptr, nullptr, 0 }
+		};
+		return graphicsModes;
+	}
+
+	OSystem::TransactionError endGFXTransaction() override {
+		int error = OSystem::TransactionError::kTransactionSuccess;
+
+		if (_pendingState.mode == GraphicsMode::DirectRendering)
+			error |= OSystem::TransactionError::kTransactionModeSwitchFailed;
+
+		if (error != OSystem::TransactionError::kTransactionSuccess) {
+			// all our errors are fatal but engine.cpp takes only this one seriously
+			error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
+			return static_cast<OSystem::TransactionError>(error);
+		}
+
+		return AtariGraphicsManager::endGFXTransaction();
+	}
+
+	int16 getOverlayHeight() const override { return _vgaMonitor ? OVERLAY_HEIGHT : 2 * OVERLAY_HEIGHT; }
+	int16 getOverlayWidth() const override { return _vgaMonitor ? OVERLAY_WIDTH : 2 * OVERLAY_WIDTH; }
+
+private:
+	virtual void* allocFast(size_t bytes) const override {
+		return (void*)Mxalloc(bytes, MX_PREFTTRAM);
+	}
+
+	void copySurfaceToSurface(const Graphics::Surface &srcSurface, Graphics::Surface &dstSurface) const override {
+		asm_c2p1x1_8(
+			(const byte*)srcSurface.getPixels(),
+			(const byte*)srcSurface.getBasePtr(srcSurface.w, srcSurface.h-1),
+			(byte*)dstSurface.getPixels());
+	}
+
+	void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY,
+						   Graphics::Surface &dstSurface, const Common::Rect &subRect) const override {
+		// 'pChunkyEnd' is a delicate parameter: the c2p routine compares it to the address register
+		// used for pixel reading; two common mistakes:
+		// 1. (subRect.left, subRect.bottom) = beginning of the next line *including the offset*
+		// 2. (subRect.right, subRect.bottom) = even worse, end of the *next* line, not current one
+		asm_c2p1x1_8_rect(
+			(const byte*)srcSurface.getBasePtr(subRect.left, subRect.top),
+			(const byte*)srcSurface.getBasePtr(subRect.right, subRect.bottom-1),
+			subRect.width(),
+			srcSurface.pitch,
+			(byte*)dstSurface.getBasePtr(destX, destY),
+			dstSurface.pitch);
+	}
+
+	// TODO: allow specifying different background than _chunkySurface?
+	void copyRectToSurfaceWithKey(const Graphics::Surface &srcSurface, int destX, int destY,
+								  Graphics::Surface &dstSurface, const Common::Rect &subRect, uint32 key) const override {
+		Common::Rect backgroundRect(destX, destY, destX + subRect.width(), destY + subRect.height());
+
+		// ensure that background's left and right lie on a 16px boundary and double the width if needed
+		backgroundRect.moveTo(backgroundRect.left & 0xfff0, backgroundRect.top);
+
+		const int deltaX = destX - backgroundRect.left;
+
+		backgroundRect.right = (backgroundRect.right + deltaX + 15) & 0xfff0;
+		if (backgroundRect.right > _chunkySurface.w)
+			backgroundRect.right = _chunkySurface.w;
+
+		static Graphics::Surface cachedSurface;
+
+		if (cachedSurface.w != backgroundRect.width() || cachedSurface.h != backgroundRect.height()) {
+			cachedSurface.create(
+				backgroundRect.width(),
+				backgroundRect.height(),
+				_chunkySurface.format);
+		}
+
+		// copy background
+		cachedSurface.copyRectToSurface(_chunkySurface, 0, 0, backgroundRect);
+		// copy cursor
+		cachedSurface.copyRectToSurfaceWithKey(srcSurface, deltaX, 0, subRect, key);
+
+		copyRectToSurface(
+			cachedSurface,
+			backgroundRect.left, backgroundRect.top,
+			dstSurface,
+			Common::Rect(cachedSurface.w, cachedSurface.h));
+	}
+
+	void alignRect(const Graphics::Surface &srcSurface, Common::Rect &rect) const override {
+		// align on 16px
+		rect.left &= 0xfff0;
+		rect.right = (rect.right + 15) & 0xfff0;
+		if (rect.right > srcSurface.w)
+			rect.right = srcSurface.w;
+	}
+};
+
+#endif
diff --git a/backends/graphics/atari/atari-graphics.cpp b/backends/graphics/atari/atari-graphics.cpp
new file mode 100644
index 00000000000..462d7385553
--- /dev/null
+++ b/backends/graphics/atari/atari-graphics.cpp
@@ -0,0 +1,1011 @@
+/* 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 "backends/graphics/atari/atari-graphics.h"
+
+#include <mint/cookie.h>
+#include <mint/falcon.h>
+#include <mint/osbind.h>
+
+#include "backends/graphics/atari/atari-graphics-asm.h"
+#include "backends/graphics/atari/videl-resolutions.h"
+#include "backends/keymapper/action.h"
+#include "backends/keymapper/keymap.h"
+
+#include "common/config-manager.h"
+#include "common/str.h"
+#include "common/textconsole.h"	// for warning() & error()
+#include "common/translation.h"
+#include "graphics/blit.h"
+#include "gui/ThemeEngine.h"
+
+#define SCREEN_ACTIVE
+
+AtariGraphicsManager::AtariGraphicsManager() {
+	_vgaMonitor = VgetMonitor() == MON_VGA;
+
+	// make the standard GUI renderer default (!DISABLE_FANCY_THEMES implies anti-aliased rendering in ThemeEngine.cpp)
+	// (and without DISABLE_FANCY_THEMES we can't use 640x480 themes)
+	const char *standardThemeEngineName = GUI::ThemeEngine::findModeConfigName(GUI::ThemeEngine::kGfxStandard);
+	ConfMan.registerDefault("gui_renderer", standardThemeEngineName);
+	if (!ConfMan.hasKey("gui_renderer"))
+		ConfMan.set("gui_renderer", standardThemeEngineName);
+
+	// make the built-in theme default to avoid long loading times
+	ConfMan.registerDefault("gui_theme", "builtin");
+	if (!ConfMan.hasKey("gui_theme"))
+		ConfMan.set("gui_theme", "builtin");
+
+	// make "data" the default theme path (defining DATA_PATH as "data"
+	// is forbidden, it must be an absolute path, see bug #14174)
+	ConfMan.registerDefault("themepath", "data");
+	if (!ConfMan.hasKey("themepath"))
+		ConfMan.set("themepath", "data");
+
+	ConfMan.flushToDisk();
+
+	g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 10, false);
+}
+
+AtariGraphicsManager::~AtariGraphicsManager() {
+	g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
+
+	for (int i = 0; i < SCREENS; ++i) {
+		Mfree(_screen[i]);
+		_screen[i] = _screenAligned[i] = nullptr;
+	}
+
+	Mfree(_overlayScreen);
+	_overlayScreen = nullptr;
+}
+
+bool AtariGraphicsManager::hasFeature(OSystem::Feature f) const {
+	switch (f) {
+	case OSystem::Feature::kFeatureAspectRatioCorrection:
+		debug("hasFeature(kFeatureAspectRatioCorrection): %d", !_vgaMonitor);
+		return !_vgaMonitor;
+	case OSystem::Feature::kFeatureCursorPalette:
+		debug("hasFeature(kFeatureCursorPalette): %d", isOverlayVisible());
+		return isOverlayVisible();
+	case OSystem::Feature::kFeatureVSync:
+		debug("hasFeature(kFeatureVSync): %d", _vsync);
+		return true;
+	default:
+		return false;
+	}
+}
+
+void AtariGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
+	switch (f) {
+	case OSystem::Feature::kFeatureAspectRatioCorrection:
+		debug("setFeatureState(kFeatureAspectRatioCorrection): %d", enable);
+		_oldAspectRatioCorrection = _aspectRatioCorrection;
+		_aspectRatioCorrection = enable;
+		break;
+	case OSystem::Feature::kFeatureVSync:
+		debug("setFeatureState(kFeatureVSync): %d", enable);
+		_vsync = enable;
+		break;
+	default:
+		[[fallthrough]];
+	}
+}
+
+bool AtariGraphicsManager::getFeatureState(OSystem::Feature f) const {
+	switch (f) {
+	case OSystem::Feature::kFeatureAspectRatioCorrection:
+		//debug("getFeatureState(kFeatureAspectRatioCorrection): %d", _aspectRatioCorrection);
+		return _aspectRatioCorrection;
+	case OSystem::Feature::kFeatureCursorPalette:
+		//debug("getFeatureState(kFeatureCursorPalette): %d", isOverlayVisible());
+		return isOverlayVisible();
+	case OSystem::Feature::kFeatureVSync:
+		//debug("getFeatureState(kFeatureVSync): %d", _vsync);
+		return _vsync;
+	default:
+		return false;
+	}
+}
+
+bool AtariGraphicsManager::setGraphicsMode(int mode, uint flags) {
+	debug("setGraphicsMode: %d, %d", mode, flags);
+
+	if (mode >= 0 && mode <= 3) {
+		_pendingState.mode = (GraphicsMode)mode;
+		return true;
+	}
+
+	return false;
+}
+
+void AtariGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
+	debug("initSize: %d, %d, %d (vsync: %d, mode: %d)", width, height, format ? format->bytesPerPixel : 1, _vsync, (int)_pendingState.mode);
+
+	_pendingState.width = width;
+	_pendingState.height = height;
+	_pendingState.format = format ? *format : PIXELFORMAT8;
+}
+
+void AtariGraphicsManager::beginGFXTransaction() {
+	debug("beginGFXTransaction");
+}
+
+OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
+	debug("endGFXTransaction");
+
+	int error = OSystem::TransactionError::kTransactionSuccess;
+
+	// always initialize (clear screen, mouse init, ...)
+	//if (_pendingState == _currentState)
+	//	return static_cast<OSystem::TransactionError>(error);
+
+	if (_pendingState.format != PIXELFORMAT8)
+		error |= OSystem::TransactionError::kTransactionFormatNotSupported;
+
+	// TODO: Several engines support unusual resolutions like 256x240 (NES Maniac Mansion),
+	// 512x342 (MacVenture, WAGE) or 544x332 (Myst)
+	if ((_pendingState.width != 320 || (_pendingState.height != 200 && _pendingState.height != 240))
+			&& (_pendingState.width != 640 || (_pendingState.height != 400 && _pendingState.height != 480)))
+		error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
+
+	if (error != OSystem::TransactionError::kTransactionSuccess) {
+		// all our errors are fatal but engine.cpp takes only this one seriously
+		error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
+		return static_cast<OSystem::TransactionError>(error);
+	}
+
+	_chunkySurface.init(_pendingState.width, _pendingState.height, _pendingState.width,
+		_chunkySurface.getPixels(), _pendingState.format);
+	_screenSurface.init(_pendingState.width, _pendingState.height, _pendingState.width,
+		_screenAligned[(int)_pendingState.mode <= 1 ? FRONT_BUFFER : BACK_BUFFER1], _screenSurface.format);
+
+	// some games do not initialize their viewport entirely
+	if (_pendingState.mode != GraphicsMode::DirectRendering) {
+		memset(_chunkySurface.getPixels(), 0, _chunkySurface.pitch * _chunkySurface.h);
+
+		if (_pendingState.mode == GraphicsMode::SingleBuffering)
+			handleModifiedRect(_chunkySurface, Common::Rect(_chunkySurface.w, _chunkySurface.h), _modifiedChunkyRects);
+		else
+			_screenModified = true;
+	} else {
+		memset(_screenSurface.getPixels(), 0, _screenSurface.pitch * _screenSurface.h);
+	}
+
+	memset(_palette, 0, sizeof(_palette));
+	_pendingScreenChange = kPendingScreenChangeScreen | kPendingScreenChangePalette;
+
+	static bool firstRun = true;
+	if (firstRun) {
+		_cursor.setPosition(getOverlayWidth() / 2, getOverlayHeight() / 2, true);
+		_cursor.swap();
+		firstRun = false;
+	}
+
+	// reinitialize old cursor position, there's no use for it anymore and it's dangerous
+	//  to let it set to possibly bigger values then the upcoming resolution
+	_oldCursorRect = Common::Rect();
+
+	warpMouse(_pendingState.width / 2, _pendingState.height / 2);
+
+	_currentState = _pendingState;
+
+	return OSystem::kTransactionSuccess;
+}
+
+void AtariGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
+	//debug("setPalette: %d, %d", start, num);
+
+	memcpy(&_palette[start * 3], colors, num * 3);
+	_pendingScreenChange |= kPendingScreenChangePalette;
+}
+
+void AtariGraphicsManager::grabPalette(byte *colors, uint start, uint num) const {
+	//debug("grabPalette: %d, %d", start, num);
+
+	memcpy(colors, &_palette[start * 3], num * 3);
+}
+
+void AtariGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
+	//debug("copyRectToScreen: %d, %d, %d, %d, %d", pitch, x, y, w, h);
+
+	if (_currentState.mode != GraphicsMode::DirectRendering) {
+		_chunkySurface.copyRectToSurface(buf, pitch, x, y, w, h);
+
+		if (_currentState.mode == GraphicsMode::SingleBuffering)
+			handleModifiedRect(_chunkySurface, Common::Rect(x, y, x + w, y + h), _modifiedChunkyRects);
+		else
+			_screenModified = true;
+	} else {
+		// TODO: c2p with 16pix align
+		_screenSurface.copyRectToSurface(buf, pitch, x, y, w, h);
+
+		_modifiedScreenRect = Common::Rect(x, y, x + w, y + h);
+
+		bool vsync = _vsync;
+		_vsync = false;
+		updateScreen();
+		_vsync = vsync;
+	}
+}
+
+Graphics::Surface *AtariGraphicsManager::lockScreen() {
+	//debug("lockScreen");
+
+	return _currentState.mode != GraphicsMode::DirectRendering ? &_chunkySurface : &_screenSurface;
+}
+
+void AtariGraphicsManager::unlockScreen() {
+	if (_currentState.mode != GraphicsMode::DirectRendering) {
+		if (_currentState.mode == GraphicsMode::SingleBuffering)
+			handleModifiedRect(_chunkySurface, Common::Rect(_chunkySurface.w, _chunkySurface.h), _modifiedChunkyRects);
+		else
+			_screenModified = true;
+	} else {
+		_modifiedScreenRect = Common::Rect(_screenSurface.w, _screenSurface.h);
+
+		bool vsync = _vsync;
+		_vsync = false;
+		updateScreen();
+		_vsync = vsync;
+	}
+}
+
+void AtariGraphicsManager::fillScreen(uint32 col) {
+	debug("fillScreen: %d", col);
+
+	if (_currentState.mode != GraphicsMode::DirectRendering) {
+		const Common::Rect rect = Common::Rect(_chunkySurface.w, _chunkySurface.h);
+		_chunkySurface.fillRect(rect, col);
+
+		if (_currentState.mode == GraphicsMode::SingleBuffering)
+			handleModifiedRect(_chunkySurface, rect, _modifiedChunkyRects);
+		else
+			_screenModified = true;
+	} else {
+		const Common::Rect rect = Common::Rect(_screenSurface.w, _screenSurface.h);
+		_screenSurface.fillRect(rect, col);
+	}
+}
+
+void AtariGraphicsManager::updateScreen() {
+	//debug("updateScreen");
+
+	// updates outOfScreen OR srcRect/dstRect (only if visible/needed)
+	_cursor.update(isOverlayVisible() ? _screenOverlaySurface : _screenSurface);
+
+	bool screenUpdated = false;
+
+	if (isOverlayVisible()) {
+		screenUpdated = updateOverlay();
+	} else {
+		switch(_currentState.mode) {
+		case GraphicsMode::DirectRendering:
+			screenUpdated = updateDirectBuffer();
+			break;
+		case GraphicsMode::SingleBuffering:
+			screenUpdated = updateSingleBuffer();
+			break;
+		case GraphicsMode::DoubleBuffering:
+		case GraphicsMode::TripleBuffering:
+			screenUpdated = updateDoubleAndTripleBuffer();
+			break;
+		}
+	}
+
+	//if (_cursor.outOfScreen)
+	//	warning("mouse out of screen");
+
+	bool vsync = _vsync;
+
+	if (_screenModified) {
+		sync();
+
+		if (_currentState.mode == GraphicsMode::DoubleBuffering) {
+			byte *tmp = _screenAligned[FRONT_BUFFER];
+			_screenAligned[FRONT_BUFFER] = _screenAligned[BACK_BUFFER1];
+			_screenAligned[BACK_BUFFER1] = tmp;
+
+			// always wait for vbl
+			vsync = true;
+		} else if (_currentState.mode == GraphicsMode::TripleBuffering) {
+			if (vsync) {
+				// render into BACK_BUFFER1 and/or BACK_BUFFER2 and set the most recent one
+				_screenAligned[FRONT_BUFFER] = _screenAligned[BACK_BUFFER1];
+
+				byte *tmp = _screenAligned[BACK_BUFFER1];
+				_screenAligned[BACK_BUFFER1] = _screenAligned[BACK_BUFFER2];
+				_screenAligned[BACK_BUFFER2] = tmp;
+			} else {
+				// render into BACK_BUFFER1 and/or BACK_BUFFER2 and/or FRONT_BUFFER
+				byte *tmp = _screenAligned[FRONT_BUFFER];
+				_screenAligned[FRONT_BUFFER] = _screenAligned[BACK_BUFFER1];
+				_screenAligned[BACK_BUFFER1] = _screenAligned[BACK_BUFFER2];
+				_screenAligned[BACK_BUFFER2] = tmp;
+			}
+
+			// never wait for vbl (use it only as a flag for the modes above)
+			vsync = false;
+		}
+
+#ifdef SCREEN_ACTIVE
+		asm_screen_set_vram(_screenAligned[FRONT_BUFFER]);
+#endif
+		_screenSurface.setPixels(_screenAligned[BACK_BUFFER1]);
+		_screenModified = false;
+	}
+
+	// everything below this line is done in VBL so don't wait if nothing has been updated!
+	vsync &= screenUpdated;
+
+#ifdef SCREEN_ACTIVE
+	bool resolutionChanged = false;
+
+	if (_pendingScreenChange & kPendingScreenChangeOverlay) {
+		if (_vgaMonitor) {
+			if (getOverlayWidth() == 640 && getOverlayHeight() == 480)
+				asm_screen_set_scp_res(scp_640x480x16_vga);
+			else
+				asm_screen_set_scp_res(scp_320x240x16_vga);
+		} else {
+			//asm_screen_set_scp_res(scp_320x240x16_rgb);
+			asm_screen_set_scp_res(scp_640x480x16_rgb);
+		}
+
+		asm_screen_set_vram(_screenOverlaySurface.getPixels());
+		resolutionChanged = true;
+	}
+
+	if (_pendingScreenChange & kPendingScreenChangeScreen) {
+		setVidelResolution();
+		asm_screen_set_vram(_screenAligned[FRONT_BUFFER]);
+		resolutionChanged = true;
+	}
+
+	if ((_pendingScreenChange & kPendingScreenChangePalette) && !isOverlayVisible()) {
+		static uint falconPalette[256];
+
+		for (uint i = 0; i < 256; ++i) {
+			// RRRRRRRR GGGGGGGG BBBBBBBB -> RRRRRRrr GGGGGGgg 00000000 BBBBBBbb
+			falconPalette[i] = (_palette[i * 3 + 0] << 24) | (_palette[i * 3 + 1] << 16) | _palette[i * 3 + 2];
+		}
+#ifdef SCREEN_ACTIVE
+		asm_screen_set_falcon_palette(falconPalette);
+#endif
+	}
+
+	_pendingScreenChange = kPendingScreenChangeNone;
+
+	if (_oldAspectRatioCorrection != _aspectRatioCorrection) {
+		if (!isOverlayVisible() && !resolutionChanged) {
+			setVidelResolution();
+		}
+		_oldAspectRatioCorrection = _aspectRatioCorrection;
+	}
+
+	if (vsync)
+		waitForVbl();
+#endif
+	//debug("end of updateScreen");
+}
+
+void AtariGraphicsManager::setShakePos(int shakeXOffset, int shakeYOffset) {
+	debug("setShakePos: %d, %d", shakeXOffset, shakeYOffset);
+}
+
+void AtariGraphicsManager::showOverlay(bool inGUI) {
+	debug("showOverlay");
+
+	if (_overlayVisible)
+		return;
+
+	_pendingScreenChange &= ~kPendingScreenChangeScreen;
+	_pendingScreenChange |= kPendingScreenChangeOverlay;
+
+	_cursor.swap();
+	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);
+		updateDirectBuffer();
+		showMouse(wasVisible);
+	}
+
+	_overlayVisible = true;
+}
+
+void AtariGraphicsManager::hideOverlay() {
+	debug("hideOverlay");
+
+	if (!_overlayVisible)
+		return;
+
+	_pendingScreenChange &= ~kPendingScreenChangeOverlay;
+	_pendingScreenChange |= kPendingScreenChangeScreen;
+
+	_cursor.swap();
+	// don't fool game cursor logic (especially direct rendering)
+	// (the overlay doesn't need any restoration upon re-entering)
+	_oldCursorRect = Common::Rect();
+
+	_overlayVisible = false;
+}
+
+void AtariGraphicsManager::clearOverlay() {
+	debug("clearOverlay");
+
+	if (!_overlayVisible)
+		return;
+
+	const Graphics::Surface &sourceSurface = _currentState.mode == GraphicsMode::DirectRendering ? _screenSurface : _chunkySurface;
+
+	int w = sourceSurface.w;
+	int h = sourceSurface.h;
+	int vOffset = 0;
+
+	if (h == 200) {
+		h = 240;
+		vOffset = (240 - 200) / 2;
+	} else if (h == 400) {
+		h = 480;
+		vOffset = (480 - 400) / 2;
+	}
+
+	ScaleMode scaleMode;
+	if (w == _overlaySurface.w && h == _overlaySurface.h) {
+		scaleMode = ScaleMode::NONE;
+	} else if (w / _overlaySurface.w == 2 && h / _overlaySurface.h == 2) {
+		scaleMode = ScaleMode::DOWNSCALE;
+		vOffset /= 2;
+	} else if (_overlaySurface.w / w == 2 && _overlaySurface.h / h == 2) {
+		scaleMode = ScaleMode::UPSCALE;
+		vOffset *= 2;
+	} else {
+		warning("Unknown overlay (%d, %d) / screen (%d, %d) ratio: ",
+			_overlaySurface.w, _overlaySurface.h, w, h);
+		return;
+	}
+
+	memset(_overlaySurface.getBasePtr(0, 0), 0, _overlaySurface.pitch * vOffset);
+	copySurface8ToSurface16(
+		sourceSurface,
+		_palette,
+		_overlaySurface,
+		0, vOffset,
+		Common::Rect(sourceSurface.w, sourceSurface.h),
+		scaleMode);
+	memset(_overlaySurface.getBasePtr(0, _overlaySurface.h - vOffset), 0, _overlaySurface.pitch * vOffset);
+
+	handleModifiedRect(_overlaySurface, Common::Rect(_overlaySurface.w, _overlaySurface.h), _modifiedOverlayRects);
+}
+
+void AtariGraphicsManager::grabOverlay(Graphics::Surface &surface) const {
+	debug("grabOverlay: %d, %d, %d", surface.pitch, surface.w, surface.h);
+
+	memcpy(surface.getPixels(), _overlaySurface.getPixels(), surface.pitch * surface.h);
+}
+
+void AtariGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
+	//debug("copyRectToOverlay: %d, %d, %d, %d, %d", pitch, x, y, w, h);
+
+	_overlaySurface.copyRectToSurface(buf, pitch, x, y, w, h);
+
+	handleModifiedRect(_overlaySurface, Common::Rect(x, y, x + w, y + h), _modifiedOverlayRects);
+}
+
+bool AtariGraphicsManager::showMouse(bool visible) {
+	//debug("showMouse: %d", visible);
+
+	if (_cursor.visible == visible) {
+		return visible;
+	}
+
+	bool last = _cursor.visible;
+	_cursor.visible = visible;
+	return last;
+}
+
+void AtariGraphicsManager::warpMouse(int x, int y) {
+	//debug("warpMouse: %d, %d", x, y);
+
+	_cursor.setPosition(x, y, true);
+}
+
+void AtariGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor,
+										  bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
+	//debug("setMouseCursor: %d, %d, %d, %d, %d, %d", w, h, hotspotX, hotspotY, keycolor, format ? format->bytesPerPixel : 1);
+
+	const Graphics::PixelFormat cursorFormat = format ? *format : PIXELFORMAT8;
+	_cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor, cursorFormat);
+}
+
+void AtariGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) {
+	debug("setCursorPalette: %d, %d", start, num);
+
+	memcpy(&_cursor.palette[start * 3], colors, num * 3);
+	_cursor.surfaceChanged = true;
+}
+
+void AtariGraphicsManager::updateMousePosition(int deltaX, int deltaY) {
+	_cursor.updatePosition(deltaX, deltaY, isOverlayVisible() ? _screenOverlaySurface : _screenSurface);
+}
+
+bool AtariGraphicsManager::notifyEvent(const Common::Event &event) {
+	if (event.type != Common::EVENT_CUSTOM_BACKEND_ACTION_START) {
+		return false;
+	}
+
+	switch ((CustomEventAction) event.customType) {
+	case kActionToggleAspectRatioCorrection:
+		_aspectRatioCorrection = !_aspectRatioCorrection;
+		return true;
+	}
+
+	return false;
+}
+
+Common::Keymap *AtariGraphicsManager::getKeymap() const {
+	Common::Keymap *keymap = new Common::Keymap(Common::Keymap::kKeymapTypeGlobal, "atari-graphics", _("Graphics"));
+	Common::Action *act;
+
+	if (hasFeature(OSystem::kFeatureAspectRatioCorrection)) {
+		act = new Common::Action("ASPT", _("Toggle aspect ratio correction"));
+		act->addDefaultInputMapping("C+A+a");
+		act->setCustomBackendActionEvent(kActionToggleAspectRatioCorrection);
+		keymap->addAction(act);
+	}
+
+	return keymap;
+}
+
+bool AtariGraphicsManager::allocateAtariSurface(
+		byte *&buf, Graphics::Surface &surface, int width, int height,
+		const Graphics::PixelFormat &format, int mode, uintptr mask) {
+	buf = (mode == MX_STRAM)
+		? (byte*)Mxalloc(width * height * format.bytesPerPixel + 15, mode)
+		: (byte*)allocFast(width * height * format.bytesPerPixel + 15);
+
+	if (!buf)
+		return false;
+
+	byte *bufAligned = (byte*)((((uintptr)buf + 15) | mask) & 0xfffffff0);
+	memset(bufAligned, 0, width * height * format.bytesPerPixel);
+
+	surface.init(width, height, width * format.bytesPerPixel, bufAligned, format);
+	return true;
+}
+
+void AtariGraphicsManager::setVidelResolution() const {
+	if (_vgaMonitor) {
+		// TODO: aspect ratio correction
+		// TODO: supervidel 320x240...
+		if (_screenSurface.w == 320) {
+			if (_screenSurface.h == 200)
+				asm_screen_set_scp_res(scp_320x200x8_vga);
+			else
+				asm_screen_set_scp_res(scp_320x240x8_vga);
+		} else {
+			if (_screenSurface.h == 400)
+				asm_screen_set_scp_res(scp_640x400x8_vga);
+			else
+				asm_screen_set_scp_res(scp_640x480x8_vga);
+		}
+	} else {
+		if (_screenSurface.w == 320) {
+			if (_screenSurface.h == 240)
+				asm_screen_set_scp_res(scp_320x240x8_rgb);
+			else if (_screenSurface.h == 200 && _aspectRatioCorrection)
+				asm_screen_set_scp_res(scp_320x200x8_rgb60);
+			else
+				asm_screen_set_scp_res(scp_320x200x8_rgb);
+		} else {
+			if (_screenSurface.h == 480)
+				asm_screen_set_scp_res(scp_640x480x8_rgb);
+			else if (_screenSurface.h == 400 && _aspectRatioCorrection)
+				asm_screen_set_scp_res(scp_640x400x8_rgb60);
+			else
+				asm_screen_set_scp_res(scp_640x400x8_rgb);
+		}
+	}
+}
+
+void AtariGraphicsManager::waitForVbl() const {
+	extern volatile uint32 vbl_counter;
+	uint32 counter = vbl_counter;
+
+	while (counter == vbl_counter);
+}
+
+bool AtariGraphicsManager::updateOverlay() {
+	bool updated = false;
+	bool drawCursor = _cursor.isModified();
+
+	while (!_modifiedOverlayRects.empty()) {
+		const Common::Rect &rect = _modifiedOverlayRects.back();
+
+		if (!drawCursor && !_cursor.outOfScreen && _cursor.visible)
+			drawCursor = rect.intersects(_cursor.dstRect);
+
+		_screenOverlaySurface.copyRectToSurface(_overlaySurface, rect.left, rect.top, rect);
+
+		_modifiedOverlayRects.pop_back();
+
+		updated = true;
+	}
+
+	if (_cursor.outOfScreen)
+		return updated;
+
+	if ((_cursor.positionChanged || !_cursor.visible) && !_oldCursorRect.isEmpty()) {
+		_screenOverlaySurface.copyRectToSurface(_overlaySurface, _oldCursorRect.left, _oldCursorRect.top, _oldCursorRect);
+		_oldCursorRect = Common::Rect();
+
+		updated = true;
+	}
+
+	if (drawCursor && _cursor.visible) {
+		//debug("Redraw cursor (overlay): %d %d %d %d", _cursor.dstRect.left, _cursor.dstRect.top, _cursor.dstRect.width(), _cursor.dstRect.height());
+
+		copySurface8ToSurface16WithKey(
+			_cursor.surface,
+			_cursor.palette,
+			_screenOverlaySurface,
+			_cursor.dstRect.left, _cursor.dstRect.top,
+			_cursor.srcRect,
+			_cursor.keycolor);
+
+		_cursor.positionChanged = _cursor.surfaceChanged = false;
+		_oldCursorRect = _cursor.dstRect;
+
+		updated = true;
+	}
+
+	return updated;
+}
+
+bool AtariGraphicsManager::updateDirectBuffer() {
+	bool updated = false;
+
+	if (_cursor.outOfScreen)
+		return updated;
+
+	bool drawCursor = _cursor.isModified();
+
+	if (!drawCursor && _cursor.visible && !_modifiedScreenRect.isEmpty())
+		drawCursor = _modifiedScreenRect.intersects(_cursor.dstRect);
+
+	static Graphics::Surface cachedCursorSurface;
+
+	if (!_oldCursorRect.isEmpty() && !_modifiedScreenRect.isEmpty()) {
+		const Common::Rect intersectingRect = _modifiedScreenRect.findIntersectingRect(_oldCursorRect);
+		if (!intersectingRect.isEmpty()) {
+			// update cached surface
+			const Graphics::Surface intersectingScreenSurface = _screenSurface.getSubArea(intersectingRect);
+			cachedCursorSurface.copyRectToSurface(
+				intersectingScreenSurface,
+				intersectingRect.left - _oldCursorRect.left,
+				intersectingRect.top - _oldCursorRect.top,
+				Common::Rect(intersectingScreenSurface.w, intersectingScreenSurface.h));
+		}
+	}
+
+	_modifiedScreenRect = Common::Rect();
+
+	if ((_cursor.positionChanged || !_cursor.visible) && !_oldCursorRect.isEmpty()) {
+		_screenSurface.copyRectToSurface(
+			cachedCursorSurface,
+			_oldCursorRect.left, _oldCursorRect.top,
+			Common::Rect(_oldCursorRect.width(), _oldCursorRect.height()));
+
+		_oldCursorRect = Common::Rect();
+
+		updated = true;
+	}
+
+	if (drawCursor && _cursor.visible) {
+		//debug("Redraw cursor (direct): %d %d %d %d", _cursor.dstRect.left, _cursor.dstRect.top, _cursor.dstRect.width(), _cursor.dstRect.height());
+
+		if (cachedCursorSurface.w != _cursor.dstRect.width() || cachedCursorSurface.h != _cursor.dstRect.height()) {
+			cachedCursorSurface.create(_cursor.dstRect.width(), _cursor.dstRect.height(), _cursor.surface.format);
+		}
+
+		// background has been restored, so it's safe to read _screenSurface
+		if (_oldCursorRect.isEmpty())
+			cachedCursorSurface.copyRectToSurface(_screenSurface, 0, 0, _cursor.dstRect);
+
+		_screenSurface.copyRectToSurfaceWithKey(
+			_cursor.surface,
+			_cursor.dstRect.left, _cursor.dstRect.top,
+			_cursor.srcRect,
+			_cursor.keycolor);
+
+		_cursor.positionChanged = _cursor.surfaceChanged = false;
+		_oldCursorRect = _cursor.dstRect;
+
+		updated = true;
+	}
+
+	return updated;
+}
+
+bool AtariGraphicsManager::updateSingleBuffer() {
+	bool updated = false;
+	bool drawCursor = _cursor.isModified();
+
+	while (!_modifiedChunkyRects.empty()) {
+		const Common::Rect &rect = _modifiedChunkyRects.back();
+
+		if (!drawCursor && !_cursor.outOfScreen && _cursor.visible)
+			drawCursor = rect.intersects(_cursor.dstRect);
+
+		if (rect.width() == _screenSurface.w && rect.height() == _screenSurface.h)
+			copySurfaceToSurface(_chunkySurface, _screenSurface);
+		else
+			copyRectToSurface(_chunkySurface, rect.left, rect.top, _screenSurface, rect);
+
+		_modifiedChunkyRects.pop_back();
+
+		updated = true;
+	}
+
+	if (_cursor.outOfScreen)
+		return updated;
+
+	if ((_cursor.positionChanged || !_cursor.visible) && !_oldCursorRect.isEmpty()) {
+		alignRect(_chunkySurface, _oldCursorRect);
+		copyRectToSurface(_chunkySurface, _oldCursorRect.left, _oldCursorRect.top,
+			_screenSurface, _oldCursorRect);
+
+		_oldCursorRect = Common::Rect();
+
+		updated = true;
+	}
+
+	if (drawCursor && _cursor.visible) {
+		//debug("Redraw cursor (single): %d %d %d %d", _cursor.dstRect.left, _cursor.dstRect.top, _cursor.dstRect.width(), _cursor.dstRect.height());
+		copyRectToSurfaceWithKey(_cursor.surface, _cursor.dstRect.left, _cursor.dstRect.top,
+			_screenSurface, _cursor.srcRect, _cursor.keycolor);
+
+		_cursor.positionChanged = _cursor.surfaceChanged = false;
+		_oldCursorRect = _cursor.dstRect;
+
+		updated = true;
+	}
+
+	return updated;
+}
+
+bool AtariGraphicsManager::updateDoubleAndTripleBuffer() {
+	bool updated = false;
+	bool drawCursor = _cursor.isModified();
+
+	if (_screenModified) {
+		drawCursor = true;
+
+		copySurfaceToSurface(_chunkySurface, _screenSurface);
+
+		// updated in screen swapping
+		//_screenModified = false;
+		updated = true;
+	}
+
+	if (_cursor.outOfScreen)
+		return updated;
+
+	// render directly to the screen to be swapped (so we don't have to refresh full screen when only cursor moves)
+	Graphics::Surface frontBufferScreenSurface;
+	frontBufferScreenSurface.init(_screenSurface.w, _screenSurface.h, _screenSurface.pitch,
+		_screenAligned[_screenModified ? BACK_BUFFER1 : FRONT_BUFFER], _screenSurface.format);
+
+	if ((_cursor.positionChanged || !_cursor.visible) && !_oldCursorRect.isEmpty() && !_screenModified) {
+		alignRect(_chunkySurface, _oldCursorRect);
+		copyRectToSurface(_chunkySurface, _oldCursorRect.left, _oldCursorRect.top,
+			frontBufferScreenSurface, _oldCursorRect);
+
+		_oldCursorRect = Common::Rect();
+
+		updated = true;
+	}
+
+	if (drawCursor && _cursor.visible) {
+		//debug("Redraw cursor (double/triple): %d %d %d %d", _cursor.dstRect.left, _cursor.dstRect.top, _cursor.dstRect.width(), _cursor.dstRect.height());
+
+		copyRectToSurfaceWithKey(_cursor.surface, _cursor.dstRect.left, _cursor.dstRect.top,
+			frontBufferScreenSurface, _cursor.srcRect, _cursor.keycolor);
+
+		_cursor.positionChanged = _cursor.surfaceChanged = false;
+		_oldCursorRect = _cursor.dstRect;
+
+		updated = true;
+	}
+
+	return updated;
+}
+
+void AtariGraphicsManager::copySurface8ToSurface16(
+		const Graphics::Surface &srcSurface, const byte *srcPalette,
+		Graphics::Surface &dstSurface, int destX, int destY,
+		const Common::Rect subRect, ScaleMode scaleMode) const {
+	assert(srcSurface.format.bytesPerPixel == 1);
+	assert(dstSurface.format.bytesPerPixel == 2);
+
+	// faster (no memory (re-)allocation) version of Surface::convertTo()
+	const int w = subRect.width();
+	const int h = subRect.height();
+
+	const int srcScale = scaleMode == ScaleMode::DOWNSCALE ? 2 : 1;
+	const byte *srcRow = (const byte*)srcSurface.getBasePtr(subRect.left * srcScale, subRect.top * srcScale);
+	uint16 *dstRow = (uint16*)dstSurface.getBasePtr(destX, destY);	// already upscaled if needed
+
+	static uint32 srcPaletteMap[256];
+	Graphics::convertPaletteToMap(srcPaletteMap, srcPalette, 256, dstSurface.format);
+
+	// optimized paths for each case...
+	if (scaleMode == ScaleMode::NONE) {
+		for (int y = 0; y < h; y++) {
+			for (int x = 0; x < w; x++) {
+				*dstRow++ = srcPaletteMap[*srcRow++];
+			}
+
+			srcRow += srcSurface.w - w;
+			dstRow += dstSurface.w - w;
+		}
+	} else if (scaleMode == ScaleMode::DOWNSCALE) {
+		for (int y = 0; y < h / 2; y++) {
+			for (int x = 0; x < w / 2; x++) {
+				*dstRow++ = srcPaletteMap[*srcRow];
+				srcRow += 2;
+			}
+
+			srcRow += srcSurface.w - w + srcSurface.w;
+			dstRow += dstSurface.w - w / 2;
+		}
+	} else if (scaleMode == ScaleMode::UPSCALE) {
+		for (int y = 0; y < h; y++) {
+			for (int x = 0; x < w; x++) {
+				const uint16 pixel = srcPaletteMap[*srcRow++];
+
+				*(dstRow + dstSurface.w) = pixel;
+				*dstRow++ = pixel;
+				*(dstRow + dstSurface.w) = pixel;
+				*dstRow++ = pixel;
+			}
+
+			srcRow += srcSurface.w - w;
+			dstRow += dstSurface.w - w * 2 + dstSurface.w;
+		}
+	}
+}
+
+void AtariGraphicsManager::copySurface8ToSurface16WithKey(
+		const Graphics::Surface &srcSurface, const byte *srcPalette,
+		Graphics::Surface &dstSurface, int destX, int destY,
+		const Common::Rect subRect, uint32 key) const {
+	assert(srcSurface.format.bytesPerPixel == 1);
+	assert(dstSurface.format.bytesPerPixel == 2);
+
+	// faster (no memory (re-)allocation) version of Surface::convertTo()
+	const int w = subRect.width();
+	const int h = subRect.height();
+
+	const byte *srcRow = (const byte *)srcSurface.getBasePtr(subRect.left, subRect.top);
+	uint16 *dstRow = (uint16 *)dstSurface.getBasePtr(destX, destY);
+
+	static uint32 srcPaletteMap[256];
+	Graphics::convertPaletteToMap(srcPaletteMap, srcPalette, 256, dstSurface.format);
+
+	const uint16 keyColor = srcPaletteMap[key];
+
+	for (int y = 0; y < h; y++) {
+		for (int x = 0; x < w; x++) {
+			const uint16 pixel = srcPaletteMap[*srcRow++];
+
+			if (pixel != keyColor) {
+				*dstRow++ = pixel;
+			} else {
+				dstRow++;
+			}
+		}
+
+		srcRow += srcSurface.w - w;
+		dstRow += dstSurface.w - w;
+	}
+}
+
+void AtariGraphicsManager::handleModifiedRect(
+		const Graphics::Surface &surface,
+		Common::Rect rect, Common::Array<Common::Rect> &rects) const {
+	if (_currentState.mode == GraphicsMode::SingleBuffering)
+		alignRect(surface, rect);
+
+	if (rect.width() == surface.w && rect.height() == surface.h) {
+		//debug("handleModifiedRect: purge");
+
+		rects.clear();
+		rects.push_back(rect);
+		return;
+	}
+
+	for (const Common::Rect &r : rects) {
+		if (r.contains(rect))
+			return;
+	}
+
+	rects.push_back(rect);
+}
+
+void AtariGraphicsManager::Cursor::update(const Graphics::Surface &screen) {
+	if (!surface.getPixels()) {
+		outOfScreen = true;
+		return;
+	}
+
+	if (!visible || (!surfaceChanged && !positionChanged))
+		return;
+
+	srcRect = Common::Rect(surface.w, surface.h);
+
+	dstRect = Common::Rect(
+		x - hotspotX,	// left
+		y - hotspotY,	// top
+		x - hotspotX + surface.w,	// right
+		y - hotspotY + surface.h);	// bottom
+
+	outOfScreen = !screen.clip(srcRect, dstRect);
+}
+
+void AtariGraphicsManager::Cursor::updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen) {
+	if (!visible)
+		return;
+
+	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;
+
+	positionChanged = true;
+}
+
+void AtariGraphicsManager::Cursor::setSurface(const void *buf, int w, int h, int _hotspotX, int _hotspotY, uint32 _keycolor, const Graphics::PixelFormat &format) {
+	if (w == 0 || h == 0 || buf == nullptr) {
+		if (surface.getPixels())
+			surface.free();
+		return;
+	}
+
+	if (surface.w != w || surface.h != h || surface.format != format)
+		surface.create(w, h, format);
+
+	surface.copyRectToSurface(buf, surface.pitch, 0, 0, w, h);
+
+	hotspotX = _hotspotX;
+	hotspotY = _hotspotY;
+	keycolor = _keycolor;
+
+	surfaceChanged = true;
+}
diff --git a/backends/graphics/atari/atari-graphics.h b/backends/graphics/atari/atari-graphics.h
new file mode 100644
index 00000000000..277e2d6a269
--- /dev/null
+++ b/backends/graphics/atari/atari-graphics.h
@@ -0,0 +1,272 @@
+/* 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_H
+#define BACKENDS_GRAPHICS_ATARI_H
+
+#include "backends/graphics/graphics.h"
+
+#include "common/array.h"
+#include "common/events.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+
+// maximum screen dimensions
+constexpr int SCREEN_WIDTH = 640;
+constexpr int SCREEN_HEIGHT = 480;
+
+// minimum overlay dimensions
+constexpr int OVERLAY_WIDTH = 320;
+constexpr int OVERLAY_HEIGHT = 240;
+
+class AtariGraphicsManager : public GraphicsManager, Common::EventObserver {
+public:
+	AtariGraphicsManager();
+	virtual ~AtariGraphicsManager();
+
+	bool hasFeature(OSystem::Feature f) const override;
+	void setFeatureState(OSystem::Feature f, bool enable) override;
+	bool getFeatureState(OSystem::Feature f) const override;
+
+	int getDefaultGraphicsMode() const override { return (int)GraphicsMode::TripleBuffering; }
+	bool setGraphicsMode(int mode, uint flags = OSystem::kGfxModeNoFlags) override;
+	int getGraphicsMode() const override { return (int)_currentState.mode; }
+
+	void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL) override;
+
+	int getScreenChangeID() const override { return 0; }
+
+	void beginGFXTransaction() override;
+	OSystem::TransactionError endGFXTransaction() override;
+
+	int16 getHeight() const override { return _currentState.height; }
+	int16 getWidth() const override { return _currentState.width; }
+	void setPalette(const byte *colors, uint start, uint num) override;
+	void grabPalette(byte *colors, uint start, uint num) const override;
+	void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override;
+	Graphics::Surface *lockScreen() override;
+	void unlockScreen() override;
+	void fillScreen(uint32 col) override;
+	void updateScreen() override;
+	void setShakePos(int shakeXOffset, int shakeYOffset) override;
+	void setFocusRectangle(const Common::Rect& rect) override {}
+	void clearFocusRectangle() override {}
+
+	void showOverlay(bool inGUI) override;
+	void hideOverlay() override;
+	bool isOverlayVisible() const override { return _overlayVisible; }
+	Graphics::PixelFormat getOverlayFormat() const override { return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); }
+	void clearOverlay() override;
+	void grabOverlay(Graphics::Surface &surface) const override;
+	void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
+
+	bool showMouse(bool visible) override;
+	void warpMouse(int x, int y) override;
+	void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor,
+						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(); }
+	void updateMousePosition(int deltaX, int deltaY);
+
+	bool notifyEvent(const Common::Event &event) override;
+	Common::Keymap *getKeymap() const;
+
+protected:
+	const Graphics::PixelFormat PIXELFORMAT8 = Graphics::PixelFormat::createFormatCLUT8();
+	const Graphics::PixelFormat PIXELFORMAT16 = getOverlayFormat();
+
+	bool allocateAtariSurface(byte *&buf, Graphics::Surface &surface,
+							  int width, int height, const Graphics::PixelFormat &format,
+							  int mode, uintptr mask = 0x00000000);
+
+	bool _vgaMonitor = true;
+
+	enum class GraphicsMode : int {
+		DirectRendering,
+		SingleBuffering,
+		DoubleBuffering,
+		TripleBuffering
+	};
+
+	struct GraphicsState {
+		bool operator==(const GraphicsState &other) const {
+			return mode == other.mode
+				&& width == other.width
+				&& height == other.height
+				&& format == other.format;
+		}
+		bool operator!=(const GraphicsState &other) const {
+			return !(*this == other);
+		}
+
+		GraphicsMode mode;
+		int width;
+		int height;
+		Graphics::PixelFormat format;
+	};
+	GraphicsState _pendingState = {};
+
+	static const int SCREENS = 3;
+	static const int FRONT_BUFFER = 0;
+	static const int BACK_BUFFER1 = 1;
+	static const int BACK_BUFFER2 = 2;
+	byte *_screen[SCREENS] = {};	// for Mfree() purposes only
+	byte *_screenAligned[SCREENS] = {};
+	Graphics::Surface _screenSurface;
+
+	byte *_chunkyBuffer = nullptr;	// for Mfree() purposes only
+	Graphics::Surface _chunkySurface;
+
+	byte *_overlayScreen = nullptr;	// for Mfree() purposes only
+	Graphics::Surface _screenOverlaySurface;
+
+	byte *_overlayBuffer = nullptr;	// for Mfree() purposes only
+	Graphics::Surface _overlaySurface;
+
+private:
+	enum CustomEventAction {
+		kActionToggleAspectRatioCorrection = 100,
+	};
+
+	void setVidelResolution() const;
+	void waitForVbl() const;
+
+	bool updateOverlay();
+	bool updateDirectBuffer();
+	bool updateSingleBuffer();
+	bool updateDoubleAndTripleBuffer();
+
+	virtual void* allocFast(size_t bytes) const = 0;
+
+	virtual void copySurfaceToSurface(const Graphics::Surface &srcSurface, Graphics::Surface &dstSurface) const = 0;
+	virtual void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY,
+								   Graphics::Surface &dstSurface, const Common::Rect &subRect) const = 0;
+	virtual void copyRectToSurfaceWithKey(const Graphics::Surface &srcSurface, int destX, int destY,
+										  Graphics::Surface &dstSurface, const Common::Rect &subRect, uint32 key) const = 0;
+	virtual void alignRect(const Graphics::Surface &srcSurface, Common::Rect &rect) const {}
+	virtual void sync() const {}
+
+	enum class ScaleMode {
+		NONE,
+		UPSCALE,
+		DOWNSCALE
+	};
+	void copySurface8ToSurface16(const Graphics::Surface &srcSurface, const byte *srcPalette,
+								 Graphics::Surface &dstSurface, int destX, int destY,
+								 const Common::Rect subRect, ScaleMode scaleMode) const;
+	void copySurface8ToSurface16WithKey(const Graphics::Surface &srcSurface, const byte* srcPalette,
+										Graphics::Surface &dstSurface, int destX, int destY,
+										const Common::Rect subRect, uint32 key) const;
+
+	void handleModifiedRect(const Graphics::Surface &surface, Common::Rect rect, Common::Array<Common::Rect> &rects) const;
+
+	void updateCursorRect();
+
+	bool _aspectRatioCorrection = false;
+	bool _oldAspectRatioCorrection = false;
+	bool _vsync = true;
+
+	GraphicsState _currentState = {};
+
+	enum PendingScreenChange {
+		kPendingScreenChangeNone	= 0,
+		kPendingScreenChangeOverlay	= 1<<0,
+		kPendingScreenChangeScreen	= 1<<1,
+		kPendingScreenChangePalette	= 1<<2
+	};
+	int _pendingScreenChange = kPendingScreenChangeNone;
+
+	bool _screenModified = false;	// double/triple buffering only
+	Common::Rect _modifiedScreenRect;	// direct rendering only
+
+	Common::Array<Common::Rect> _modifiedChunkyRects;
+
+	bool _overlayVisible = false;
+	Common::Array<Common::Rect> _modifiedOverlayRects;
+
+	struct Cursor {
+		void update(const Graphics::Surface &screen);
+
+		bool isModified() const {
+			return surfaceChanged || positionChanged;
+		}
+
+		bool visible = false;
+
+		// position
+		Common::Point getPosition() const {
+			return Common::Point(x, y);
+		}
+		void setPosition(int x_, int y_, bool override = false) {
+			if (x == x_ && y == y_)
+				return;
+
+			if (!visible && !override)
+				return;
+
+			x = x_;
+			y = y_;
+			positionChanged = true;
+		}
+		void updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen);
+		bool positionChanged = false;
+		void swap() {
+			const int tmpX = oldX;
+			const int tmpY = oldY;
+
+			oldX = x;
+			oldY = y;
+
+			x = tmpX;
+			y = tmpY;
+
+			positionChanged = false;
+		}
+
+		// surface
+		void setSurface(const void *buf, int w, int h, int _hotspotX, int _hotspotY, uint32 _keycolor, const Graphics::PixelFormat &format);
+		bool surfaceChanged = false;
+		Graphics::Surface surface;
+		uint32 keycolor;
+
+		// rects (valid only if !outOfScreen)
+		bool outOfScreen = true;
+		Common::Rect srcRect;
+		Common::Rect dstRect;
+
+		// palette (only used for 16bpp screen surfaces)
+		byte palette[256*3] = {};
+
+	private:
+		int x = -1, y = -1;
+		int oldX = -1, oldY = -1;
+
+		int hotspotX;
+		int hotspotY;
+	} _cursor;
+
+	Common::Rect _oldCursorRect;
+
+	byte _palette[256*3] = {};
+};
+
+#endif
diff --git a/backends/graphics/atari/atari_c2p-asm.S b/backends/graphics/atari/atari_c2p-asm.S
new file mode 100644
index 00000000000..03b57afc380
--- /dev/null
+++ b/backends/graphics/atari/atari_c2p-asm.S
@@ -0,0 +1,474 @@
+/* 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/>.
+ *
+ */
+
+| C2P by Mikael Kalms (public domain)
+| See https://github.com/Kalmalyzer/kalms-c2p
+
+	.globl	_asm_c2p1x1_8
+	.globl	_asm_c2p1x1_8_rect
+
+
+	.text
+
+| void asm_c2p1x1_8(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen);
+_asm_c2p1x1_8:
+	move.l	(4,sp),a0				| chunky
+	move.l	(8,sp),d0				| chunky end
+	move.l	(12,sp),a1				| screen
+	movem.l	d2-d7/a2-a6,-(sp)
+	move.l	d0,a2
+	move.l	#0x0f0f0f0f,d4
+	move.l	#0x00ff00ff,d5
+	move.l	#0x55555555,d6
+
+	move.l	(a0)+,d0
+	move.l	(a0)+,d1
+	move.l	(a0)+,d2
+	move.l	(a0)+,d3
+
+	| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
+	| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
+	| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
+	| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
+
+	move.l	d1,d7
+	lsr.l	#4,d7
+	eor.l	d0,d7
+	and.l	d4,d7
+	eor.l	d7,d0
+	lsl.l	#4,d7
+	eor.l	d7,d1
+	move.l	d3,d7
+	lsr.l	#4,d7
+	eor.l	d2,d7
+	and.l	d4,d7
+	eor.l	d7,d2
+	lsl.l	#4,d7
+	eor.l	d7,d3
+
+	| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
+	| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
+	| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
+	| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
+
+	move.l	d2,d7
+	lsr.l	#8,d7
+	eor.l	d0,d7
+	and.l	d5,d7
+	eor.l	d7,d0
+	lsl.l	#8,d7
+	eor.l	d7,d2
+	move.l	d3,d7
+	lsr.l	#8,d7
+	eor.l	d1,d7
+	and.l	d5,d7
+	eor.l	d7,d1
+	lsl.l	#8,d7
+	eor.l	d7,d3
+
+	| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
+	| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
+	| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
+	| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
+
+	bra.s	c2p1x1_8_start
+
+c2p1x1_8_pix16:
+	move.l	(a0)+,d0
+	move.l	(a0)+,d1
+	move.l	(a0)+,d2
+	move.l	(a0)+,d3
+
+	| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
+	| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
+	| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
+	| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
+
+	move.l	d1,d7
+	lsr.l	#4,d7
+	move.l	a3,(a1)+
+	eor.l	d0,d7
+	and.l	d4,d7
+	eor.l	d7,d0
+	lsl.l	#4,d7
+	eor.l	d7,d1
+	move.l	d3,d7
+	lsr.l	#4,d7
+	eor.l	d2,d7
+	and.l	d4,d7
+	eor.l	d7,d2
+	move.l	a4,(a1)+
+	lsl.l	#4,d7
+	eor.l	d7,d3
+
+	| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
+	| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
+	| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
+	| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
+
+	move.l	d2,d7
+	lsr.l	#8,d7
+	eor.l	d0,d7
+	and.l	d5,d7
+	eor.l	d7,d0
+	move.l	a5,(a1)+
+	lsl.l	#8,d7
+	eor.l	d7,d2
+	move.l	d3,d7
+	lsr.l	#8,d7
+	eor.l	d1,d7
+	and.l	d5,d7
+	eor.l	d7,d1
+	move.l	a6,(a1)+
+	lsl.l	#8,d7
+	eor.l	d7,d3
+
+	| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
+	| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
+	| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
+	| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
+
+c2p1x1_8_start:
+	move.l	d2,d7
+	lsr.l	#1,d7
+	eor.l	d0,d7
+	and.l	d6,d7
+	eor.l	d7,d0
+	add.l	d7,d7
+	eor.l	d7,d2
+	move.l	d3,d7
+	lsr.l	#1,d7
+	eor.l	d1,d7
+	and.l	d6,d7
+	eor.l	d7,d1
+	add.l	d7,d7
+	eor.l	d7,d3
+
+	| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5
+	| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1
+	| a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
+	| a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
+
+	move.w	d2,d7
+	move.w	d0,d2
+	swap	d2
+	move.w	d2,d0
+	move.w	d7,d2
+	move.w	d3,d7
+	move.w	d1,d3
+	swap	d3
+	move.w	d3,d1
+	move.w	d7,d3
+
+	| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4
+	| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0
+	| c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
+	| c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
+
+	move.l	d2,d7
+	lsr.l	#2,d7
+	eor.l	d0,d7
+	and.l	#0x33333333,d7
+	eor.l	d7,d0
+	lsl.l	#2,d7
+	eor.l	d7,d2
+	move.l	d3,d7
+	lsr.l	#2,d7
+	eor.l	d1,d7
+	and.l	#0x33333333,d7
+	eor.l	d7,d1
+	lsl.l	#2,d7
+	eor.l	d7,d3
+
+	| a7b7c7d7e7f7g7h7 i7j7k7l7m7n7o7p7 a6b6c6d6e6f6g6h6 i6j6k6l6m6n6o6p6
+	| a3b3c3d3e3f3g3h3 i3j3k3l3m3n3o3p3 a2b2c2d2e2f2g2h2 i2j2k2l2m2n2o2p2
+	| a5b5c5d5e5f5g5h5 i5j5k5l5m5n5o5p5 a4b4c4d4e4f4g4h4 i4j4k4l4m4n4o4p4
+	| a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0
+
+	swap	d0
+	swap	d1
+	swap	d2
+	swap	d3
+
+	move.l	d0,a6
+	move.l	d2,a5
+	move.l	d1,a4
+	move.l	d3,a3
+
+	cmp.l	a0,a2
+	bne	c2p1x1_8_pix16
+
+	move.l	a3,(a1)+
+	move.l	a4,(a1)+
+	move.l	a5,(a1)+
+	move.l	a6,(a1)+
+
+	movem.l	(sp)+,d2-d7/a2-a6
+	rts
+
+
+| void asm_c2p1x1_8_rect(const byte *pChunky, const byte *pChunkyEnd, uint32 chunkyWidth, uint32 chunkyPitch, byte *pScreen, uint32 screenPitch);
+_asm_c2p1x1_8_rect:
+	movem.l	d2-d7/a2-a6,-(sp)			| 6 + 5 = 11 longs
+
+	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
+
+	move.l	sp,old_sp
+
+	lea	(a0,d0.l),a2				| a2: end of first src line
+	lea	(a1,d0.l),a7				| a7: end of first dst line
+
+	move.l	d1,screen_pitch
+
+	sub.l	d0,d1
+	move.l	d1,screen_offset
+
+	move.l	d2,chunky_pitch
+
+	sub.l	d0,d2
+	move.l	d2,chunky_offset
+
+	move.l	#0x0f0f0f0f,d4
+	move.l	#0x00ff00ff,d5
+	move.l	#0x55555555,d6
+
+	move.l	(a0)+,d0
+	move.l	(a0)+,d1
+	move.l	(a0)+,d2
+	move.l	(a0)+,d3
+
+	| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
+	| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
+	| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
+	| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
+
+	move.l	d1,d7
+	lsr.l	#4,d7
+	eor.l	d0,d7
+	and.l	d4,d7
+	eor.l	d7,d0
+	lsl.l	#4,d7
+	eor.l	d7,d1
+	move.l	d3,d7
+	lsr.l	#4,d7
+	eor.l	d2,d7
+	and.l	d4,d7
+	eor.l	d7,d2
+	lsl.l	#4,d7
+	eor.l	d7,d3
+
+	| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
+	| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
+	| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
+	| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
+
+	move.l	d2,d7
+	lsr.l	#8,d7
+	eor.l	d0,d7
+	and.l	d5,d7
+	eor.l	d7,d0
+	lsl.l	#8,d7
+	eor.l	d7,d2
+	move.l	d3,d7
+	lsr.l	#8,d7
+	eor.l	d1,d7
+	and.l	d5,d7
+	eor.l	d7,d1
+	lsl.l	#8,d7
+	eor.l	d7,d3
+
+	| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
+	| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
+	| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
+	| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
+
+	bra.s	c2p1x1_8_rect_start
+
+c2p1x1_8_rect_pix16:
+	move.l	(a0)+,d0
+	move.l	(a0)+,d1
+	move.l	(a0)+,d2
+	move.l	(a0)+,d3
+
+	| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
+	| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
+	| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
+	| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
+
+	move.l	d1,d7
+	lsr.l	#4,d7
+	move.l	a3,(a1)+
+	eor.l	d0,d7
+	and.l	d4,d7
+	eor.l	d7,d0
+	lsl.l	#4,d7
+	eor.l	d7,d1
+	move.l	d3,d7
+	lsr.l	#4,d7
+	eor.l	d2,d7
+	and.l	d4,d7
+	eor.l	d7,d2
+	move.l	a4,(a1)+
+	lsl.l	#4,d7
+	eor.l	d7,d3
+
+	| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
+	| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
+	| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
+	| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
+
+	move.l	d2,d7
+	lsr.l	#8,d7
+	eor.l	d0,d7
+	and.l	d5,d7
+	eor.l	d7,d0
+	move.l	a5,(a1)+
+	lsl.l	#8,d7
+	eor.l	d7,d2
+	move.l	d3,d7
+	lsr.l	#8,d7
+	eor.l	d1,d7
+	and.l	d5,d7
+	eor.l	d7,d1
+	move.l	a6,(a1)+
+	lsl.l	#8,d7
+	eor.l	d7,d3
+
+	| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
+	| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
+	| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
+	| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
+
+	cmp.l	a1,a7					| end of dst line?
+	bne.s	c2p1x1_8_rect_start
+
+	add.l	(screen_offset,pc),a1
+	add.l	(screen_pitch,pc),a7
+
+c2p1x1_8_rect_start:
+	move.l	d2,d7
+	lsr.l	#1,d7
+	eor.l	d0,d7
+	and.l	d6,d7
+	eor.l	d7,d0
+	add.l	d7,d7
+	eor.l	d7,d2
+	move.l	d3,d7
+	lsr.l	#1,d7
+	eor.l	d1,d7
+	and.l	d6,d7
+	eor.l	d7,d1
+	add.l	d7,d7
+	eor.l	d7,d3
+
+	| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5
+	| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1
+	| a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
+	| a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
+
+	move.w	d2,d7
+	move.w	d0,d2
+	swap	d2
+	move.w	d2,d0
+	move.w	d7,d2
+	move.w	d3,d7
+	move.w	d1,d3
+	swap	d3
+	move.w	d3,d1
+	move.w	d7,d3
+
+	| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4
+	| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0
+	| c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
+	| c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
+
+	move.l	d2,d7
+	lsr.l	#2,d7
+	eor.l	d0,d7
+	and.l	#0x33333333,d7
+	eor.l	d7,d0
+	lsl.l	#2,d7
+	eor.l	d7,d2
+	move.l	d3,d7
+	lsr.l	#2,d7
+	eor.l	d1,d7
+	and.l	#0x33333333,d7
+	eor.l	d7,d1
+	lsl.l	#2,d7
+	eor.l	d7,d3
+
+	| a7b7c7d7e7f7g7h7 i7j7k7l7m7n7o7p7 a6b6c6d6e6f6g6h6 i6j6k6l6m6n6o6p6
+	| a3b3c3d3e3f3g3h3 i3j3k3l3m3n3o3p3 a2b2c2d2e2f2g2h2 i2j2k2l2m2n2o2p2
+	| a5b5c5d5e5f5g5h5 i5j5k5l5m5n5o5p5 a4b4c4d4e4f4g4h4 i4j4k4l4m4n4o4p4
+	| a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0
+
+	swap	d0
+	swap	d1
+	swap	d2
+	swap	d3
+
+	move.l	d0,a6
+	move.l	d2,a5
+	move.l	d1,a4
+	move.l	d3,a3
+
+	cmp.l	a0,a2					| end of src line?
+	bne	c2p1x1_8_rect_pix16
+
+	cmp.l	(chunky_end,pc),a2
+	beq.s	c2p1x1_8_rect_done
+
+	add.l	(chunky_offset,pc),a0
+	add.l	(chunky_pitch,pc),a2
+
+	bra	c2p1x1_8_rect_pix16
+
+c2p1x1_8_rect_done:
+	move.l	a3,(a1)+
+	move.l	a4,(a1)+
+	move.l	a5,(a1)+
+	move.l	a6,(a1)+
+
+	move.l	old_sp,sp
+	movem.l	(sp)+,d2-d7/a2-a6
+	rts
+
+| place it within reach of 32K (PC relative)
+screen_pitch:
+	ds.l	1
+screen_offset:
+	ds.l	1
+chunky_pitch:
+	ds.l	1
+chunky_offset:
+	ds.l	1
+chunky_end:
+	ds.l	1
+
+	.bss
+	.even
+
+old_sp:	ds.l	1
diff --git a/backends/graphics/atari/atari_c2p-asm.h b/backends/graphics/atari/atari_c2p-asm.h
new file mode 100644
index 00000000000..66129f8cb7a
--- /dev/null
+++ b/backends/graphics/atari/atari_c2p-asm.h
@@ -0,0 +1,54 @@
+/* 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_C2P_ASM_H
+#define BACKENDS_GRAPHICS_ATARI_C2P_ASM_H
+
+#include "common/scummsys.h"
+
+extern "C" {
+
+/**
+ * Chunky to planar conversion routine. Converts a chunky (byte) buffer into eight bitplanes.
+ * Optimized for surface-to-surface copy with the same pitch.
+ *
+ * @param pChunky chunky buffer start
+ * @param pChunkyEnd chunky buffer end (including the last byte)
+ * @param pScreen bitplane screen start
+ */
+void asm_c2p1x1_8(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen);
+
+/**
+ * Chunky to planar conversion routine. Converts a chunky (byte) buffer into eight bitplanes.
+ * Optimized for arbitrary rectangle position and dimension (16px aligned).
+ *
+ * @param pChunky chunky buffer at rectangle's [X1, Y1] position
+ * @param pChunkyEnd chunky buffer at rectangle's [X2, Y2] position (included)
+ * @param chunkyWidth rectangle width
+ * @param chunkyPitch chunky buffer width (in bytes)
+ * @param pScreen bitplane screen at rectangle's [X1, Y1] position
+ * @param screenPitch bitplane screen width (in bytes)
+ */
+void asm_c2p1x1_8_rect(const byte *pChunky, const byte *pChunkyEnd, uint32 chunkyWidth, uint32 chunkyPitch, byte *pScreen, uint32 screenPitch);
+
+}
+
+#endif
diff --git a/backends/graphics/atari/videl-resolutions.cpp b/backends/graphics/atari/videl-resolutions.cpp
new file mode 100644
index 00000000000..337a1b4f3a8
--- /dev/null
+++ b/backends/graphics/atari/videl-resolutions.cpp
@@ -0,0 +1,262 @@
+/* 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 "backends/graphics/atari/videl-resolutions.h"
+
+#include "common/scummsys.h"
+
+const byte scp_320x200x8_rgb[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x48, 0x7a, 0xb0, 0x00, 0xa9, 0xc1, 0x08, 0x00, 0xc2, 0x8c, 0xb0,
+  0x01, 0x64, 0x05, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x3d, 0x09, 0x00, 0x00,
+  0x00, 0xc0, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x40,
+  0x00, 0xc8, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xfe, 0x00, 0x99, 0x00, 0x59, 0x00, 0x26, 0x00, 0x87,
+  0x00, 0xd9, 0x02, 0x71, 0x02, 0x11, 0x00, 0x81, 0x00, 0x81, 0x02, 0x11,
+  0x02, 0x6b, 0x02, 0x00, 0x01, 0x81, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+  0x00, 0xa0
+};
+
+const byte scp_320x200x8_rgb60[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x48, 0x7a, 0xb0, 0x00, 0xa7, 0xd8, 0xc0, 0x00, 0xc4, 0x74, 0xf8,
+  0x01, 0x60, 0x05, 0x00, 0x02, 0x54, 0x00, 0x00, 0x3d, 0x09, 0x00, 0x00,
+  0x00, 0xc0, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x06, 0xc0, 0x00, 0x23,
+  0x00, 0xc8, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xfe, 0x00, 0x98, 0x00, 0x58, 0x00, 0x25, 0x00, 0x86,
+  0x00, 0xd9, 0x02, 0x0d, 0x01, 0xd7, 0x00, 0x47, 0x00, 0x47, 0x01, 0xd7,
+  0x02, 0x07, 0x02, 0x00, 0x01, 0x81, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+  0x00, 0xa0
+};
+
+byte scp_320x200x8_vga[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x3a, 0x2f, 0x00, 0x00, 0x19, 0x74, 0x90, 0x00, 0x46, 0x4e, 0x20,
+  0x00, 0x2a, 0x02, 0x80, 0x02, 0x58, 0x00, 0x00, 0x7a, 0xee, 0x00, 0x00,
+  0x00, 0x3e, 0x00, 0x00, 0x08, 0x7a, 0x00, 0x00, 0x06, 0x8a, 0x00, 0x46,
+  0x01, 0x90, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xc6, 0x00, 0x8d, 0x00, 0x15, 0x02, 0x9a, 0x00, 0x7b,
+  0x00, 0x97, 0x04, 0x19, 0x03, 0xad, 0x00, 0x8d, 0x00, 0x8d, 0x03, 0xad,
+  0x04, 0x15, 0x02, 0x00, 0x01, 0x86, 0x00, 0x00, 0x00, 0x10, 0x00, 0x05,
+  0x00, 0xa0
+};
+
+const byte scp_320x240x8_rgb[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x01, 0x33, 0x32, 0x30, 0x2a,
+  0x32, 0x30, 0x30, 0x2c, 0x20, 0x32, 0x35, 0x36, 0x20, 0x46, 0x61, 0x72,
+  0x62, 0x65, 0x6e, 0x2c, 0x20, 0x35, 0x30, 0x2e, 0x30, 0x20, 0x48, 0x7a,
+  0x2c, 0x20, 0x31, 0x35, 0x36, 0x32, 0x35, 0x20, 0x48, 0x7a, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x48, 0x7a, 0xb0, 0x00, 0xa9, 0xc1, 0x08, 0x00, 0xc2, 0x8c, 0xb0,
+  0x01, 0x64, 0x05, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x3d, 0x09, 0x00, 0x00,
+  0x00, 0xc0, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x2c,
+  0x00, 0xf0, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xfe, 0x00, 0x99, 0x00, 0x59, 0x00, 0x26, 0x00, 0x87,
+  0x00, 0xd9, 0x02, 0x71, 0x02, 0x39, 0x00, 0x59, 0x00, 0x59, 0x02, 0x39,
+  0x02, 0x6b, 0x02, 0x00, 0x01, 0x81, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+  0x00, 0xa0
+};
+
+byte scp_320x240x8_vga[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x3a, 0x2f, 0x00, 0x00, 0x19, 0x74, 0x90, 0x00, 0x46, 0x4e, 0x20,
+  0x00, 0x2a, 0x02, 0x80, 0x02, 0x58, 0x00, 0x00, 0x7a, 0xee, 0x00, 0x00,
+  0x00, 0x3e, 0x00, 0x00, 0x03, 0xc1, 0x00, 0x00, 0x01, 0x93, 0x00, 0x1f,
+  0x01, 0xe0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xc6, 0x00, 0x8d, 0x00, 0x15, 0x02, 0x9a, 0x00, 0x7b,
+  0x00, 0x97, 0x04, 0x19, 0x03, 0xff, 0x00, 0x3f, 0x00, 0x3f, 0x03, 0xff,
+  0x04, 0x15, 0x02, 0x00, 0x01, 0x86, 0x00, 0x00, 0x00, 0x10, 0x00, 0x05,
+  0x00, 0xa0
+};
+
+const byte scp_320x240x16_rgb[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x48, 0x7a, 0xb0, 0x00, 0xa9, 0xc1, 0x08, 0x00, 0xc2, 0x8c, 0xb0,
+  0x01, 0x64, 0x05, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x3d, 0x09, 0x00, 0x00,
+  0x00, 0xc0, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x2c,
+  0x00, 0xf0, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xfe, 0x00, 0x99, 0x00, 0x59, 0x00, 0x38, 0x00, 0x99,
+  0x00, 0xd9, 0x02, 0x71, 0x02, 0x39, 0x00, 0x59, 0x00, 0x59, 0x02, 0x39,
+  0x02, 0x6b, 0x02, 0x00, 0x01, 0x81, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x40
+};
+
+const byte scp_320x240x16_vga[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x3a, 0x2f, 0x00, 0x00, 0x19, 0x74, 0x90, 0x00, 0x46, 0x4e, 0x20,
+  0x00, 0x2a, 0x02, 0x80, 0x02, 0x58, 0x00, 0x00, 0x7a, 0xee, 0x00, 0x00,
+  0x00, 0x3e, 0x00, 0x00, 0x03, 0xc1, 0x00, 0x00, 0x01, 0xb2, 0x00, 0x1e,
+  0x01, 0xe0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xc6, 0x00, 0x8d, 0x00, 0x15, 0x02, 0xac, 0x00, 0x8d,
+  0x00, 0x97, 0x04, 0x19, 0x03, 0xfd, 0x00, 0x3f, 0x00, 0x3d, 0x03, 0xfd,
+  0x04, 0x15, 0x02, 0x00, 0x01, 0x86, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05,
+  0x01, 0x40
+};
+
+const byte scp_640x400x8_rgb[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x48, 0x7a, 0xb0, 0x00, 0xab, 0xa9, 0x50, 0x00, 0xc1, 0x98, 0x8c,
+  0x01, 0x68, 0x05, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x3d, 0x09, 0x00, 0x00,
+  0x00, 0xc0, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x40,
+  0x00, 0xc8, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0xfe, 0x01, 0x34, 0x00, 0xb4, 0x00, 0x71, 0x01, 0x22,
+  0x01, 0xb3, 0x02, 0x70, 0x02, 0x11, 0x00, 0x81, 0x00, 0x80, 0x02, 0x10,
+  0x02, 0x6b, 0x02, 0x00, 0x01, 0x81, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06,
+  0x01, 0x40
+};
+
+const byte scp_640x400x8_rgb60[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x47, 0x86, 0x8c, 0x00, 0xb1, 0x62, 0x28, 0x00, 0xbd, 0xc7, 0xfc,
+  0x01, 0x74, 0x05, 0x00, 0x02, 0x53, 0x00, 0x00, 0x3c, 0xea, 0x00, 0x00,
+  0x00, 0xc0, 0x00, 0x00, 0x09, 0x40, 0x00, 0x00, 0x06, 0x40, 0x00, 0x25,
+  0x00, 0xc8, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0xff, 0x01, 0x39, 0x00, 0xba, 0x00, 0x77, 0x01, 0x27,
+  0x01, 0xb5, 0x02, 0x0c, 0x01, 0xdb, 0x00, 0x4b, 0x00, 0x4a, 0x01, 0xda,
+  0x02, 0x07, 0x02, 0x00, 0x01, 0x81, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06,
+  0x01, 0x40
+};
+
+byte scp_640x400x8_vga[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x02, 0x36, 0x34, 0x30, 0x2a,
+  0x34, 0x38, 0x30, 0x2c, 0x20, 0x32, 0x35, 0x36, 0x20, 0x46, 0x61, 0x72,
+  0x62, 0x65, 0x6e, 0x2c, 0x20, 0x36, 0x30, 0x2e, 0x30, 0x20, 0x48, 0x7a,
+  0x2c, 0x20, 0x33, 0x31, 0x34, 0x37, 0x30, 0x20, 0x48, 0x7a, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x3a, 0x2f, 0x00, 0x00, 0x1a, 0xaa, 0xe0, 0x00, 0x45, 0x17, 0xd0,
+  0x00, 0x2c, 0x02, 0x80, 0x02, 0x58, 0x00, 0x00, 0x7a, 0xee, 0x00, 0x00,
+  0x00, 0x3e, 0x00, 0x00, 0x08, 0x99, 0x00, 0x00, 0x06, 0x6b, 0x00, 0x47,
+  0x01, 0x90, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xc6, 0x00, 0x8e, 0x00, 0x16, 0x02, 0xac, 0x00, 0x85,
+  0x00, 0x97, 0x04, 0x19, 0x03, 0xaf, 0x00, 0x8f, 0x00, 0x8f, 0x03, 0xaf,
+  0x04, 0x15, 0x02, 0x00, 0x01, 0x86, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08,
+  0x01, 0x40
+};
+
+const byte scp_640x480x8_rgb[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x01, 0x36, 0x34, 0x30, 0x2a,
+  0x34, 0x30, 0x30, 0x2c, 0x20, 0x32, 0x35, 0x36, 0x20, 0x46, 0x61, 0x72,
+  0x62, 0x65, 0x6e, 0x2c, 0x20, 0x35, 0x30, 0x2e, 0x30, 0x20, 0x48, 0x7a,
+  0x20, 0x28, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x2e, 0x29, 0x2c, 0x20,
+  0x31, 0x35, 0x36, 0x32, 0x35, 0x20, 0x48, 0x7a, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x48, 0x7a, 0xb0, 0x00, 0xab, 0xa9, 0x50, 0x00, 0xc1, 0x98, 0x8c,
+  0x01, 0x68, 0x05, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x3d, 0x09, 0x00, 0x00,
+  0x00, 0xc0, 0x00, 0x00, 0x0a, 0xc0, 0x00, 0x00, 0x07, 0x40, 0x00, 0x2b,
+  0x00, 0xf0, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0xfe, 0x01, 0x34, 0x00, 0xb4, 0x00, 0x71, 0x01, 0x22,
+  0x01, 0xb3, 0x02, 0x70, 0x02, 0x37, 0x00, 0x57, 0x00, 0x56, 0x02, 0x36,
+  0x02, 0x6b, 0x02, 0x00, 0x01, 0x81, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06,
+  0x01, 0x40
+};
+
+byte scp_640x480x8_vga[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x3a, 0x2f, 0x00, 0x00, 0x1a, 0xaa, 0xe0, 0x00, 0x45, 0x17, 0xd0,
+  0x00, 0x2c, 0x02, 0x80, 0x02, 0x58, 0x00, 0x00, 0x7a, 0xee, 0x00, 0x00,
+  0x00, 0x3e, 0x00, 0x00, 0x03, 0xc1, 0x00, 0x00, 0x01, 0x93, 0x00, 0x1f,
+  0x01, 0xe0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xc6, 0x00, 0x8e, 0x00, 0x16, 0x02, 0xac, 0x00, 0x85,
+  0x00, 0x97, 0x04, 0x19, 0x03, 0xff, 0x00, 0x3f, 0x00, 0x3f, 0x03, 0xff,
+  0x04, 0x15, 0x02, 0x00, 0x01, 0x86, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08,
+  0x01, 0x40
+};
+
+const byte scp_640x480x16_rgb[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x01, 0x36, 0x34, 0x30, 0x2a,
+  0x34, 0x38, 0x30, 0x2c, 0x20, 0x54, 0x72, 0x75, 0x65, 0x20, 0x43, 0x6f,
+  0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x35, 0x30, 0x2e, 0x30, 0x20, 0x48, 0x7a,
+  0x20, 0x28, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x2e, 0x29, 0x2c, 0x20,
+  0x31, 0x35, 0x36, 0x32, 0x35, 0x20, 0x48, 0x7a, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x48, 0x7a, 0xb0, 0x00, 0xa9, 0xc1, 0x08, 0x00, 0xc3, 0x80, 0xd4,
+  0x01, 0x64, 0x05, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x3d, 0x09, 0x00, 0x00,
+  0x00, 0xc0, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x2c,
+  0x00, 0xf0, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0xfe, 0x01, 0x32, 0x00, 0xb2, 0x00, 0x81, 0x01, 0x32,
+  0x01, 0xb3, 0x02, 0x70, 0x02, 0x39, 0x00, 0x59, 0x00, 0x58, 0x02, 0x38,
+  0x02, 0x6b, 0x02, 0x00, 0x01, 0x81, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06,
+  0x02, 0x80
+};
+
+const byte scp_640x480x16_vga[] = {
+  0x53, 0x43, 0x50, 0x4e, 0x00, 0x09, 0x00, 0x02, 0x36, 0x34, 0x30, 0x2a,
+  0x34, 0x38, 0x30, 0x2c, 0x20, 0x54, 0x72, 0x75, 0x65, 0x20, 0x43, 0x6f,
+  0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x36, 0x30, 0x2e, 0x30, 0x20, 0x48, 0x7a,
+  0x2c, 0x20, 0x33, 0x31, 0x34, 0x37, 0x30, 0x20, 0x48, 0x7a, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x3a, 0x2f, 0x00, 0x00, 0x1a, 0xaa, 0xe0, 0x00, 0x45, 0x17, 0xd0,
+  0x00, 0x2c, 0x02, 0x80, 0x02, 0x58, 0x00, 0x00, 0x7a, 0xee, 0x00, 0x00,
+  0x00, 0x3e, 0x00, 0x00, 0x03, 0xc1, 0x00, 0x00, 0x01, 0x93, 0x00, 0x1f,
+  0x01, 0xe0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x01, 0xe8, 0x48, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xc6, 0x00, 0x8e, 0x00, 0x16, 0x02, 0xb5, 0x00, 0x8e,
+  0x00, 0x97, 0x04, 0x19, 0x03, 0xff, 0x00, 0x3f, 0x00, 0x3f, 0x03, 0xff,
+  0x04, 0x15, 0x02, 0x00, 0x01, 0x86, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08,
+  0x02, 0x80
+};
diff --git a/backends/graphics/atari/videl-resolutions.h b/backends/graphics/atari/videl-resolutions.h
new file mode 100644
index 00000000000..2dbae53374b
--- /dev/null
+++ b/backends/graphics/atari/videl-resolutions.h
@@ -0,0 +1,47 @@
+/* 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_VIDEL_RESOLUTIONS_H
+#define BACKENDS_GRAPHICS_ATARI_VIDEL_RESOLUTIONS_H
+
+#include "common/scummsys.h"
+
+constexpr int SCP_SIZE = 158;
+
+extern const byte scp_320x200x8_rgb[SCP_SIZE];
+extern const byte scp_320x200x8_rgb60[SCP_SIZE];
+extern       byte scp_320x200x8_vga[SCP_SIZE];
+
+extern const byte scp_320x240x8_rgb[SCP_SIZE];
+extern       byte scp_320x240x8_vga[SCP_SIZE];
+extern const byte scp_320x240x16_rgb[SCP_SIZE];
+extern const byte scp_320x240x16_vga[SCP_SIZE];
+
+extern const byte scp_640x400x8_rgb[SCP_SIZE];
+extern const byte scp_640x400x8_rgb60[SCP_SIZE];
+extern       byte scp_640x400x8_vga[SCP_SIZE];
+
+extern const byte scp_640x480x8_rgb[SCP_SIZE];
+extern       byte scp_640x480x8_vga[SCP_SIZE];
+extern const byte scp_640x480x16_rgb[SCP_SIZE];
+extern const byte scp_640x480x16_vga[SCP_SIZE];
+
+#endif
diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
new file mode 100644
index 00000000000..b4121897ee2
--- /dev/null
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -0,0 +1,221 @@
+/* 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 "backends/mixer/atari/atari-mixer.h"
+
+#include <math.h>
+
+#include <mint/falcon.h>
+
+#include "common/config-manager.h"
+#include "common/debug.h"
+
+#define DEFAULT_OUTPUT_RATE 24585
+
+AtariMixerManager::AtariMixerManager() : MixerManager() {
+	ConfMan.registerDefault("output_rate", DEFAULT_OUTPUT_RATE);
+
+	_outputRate = ConfMan.getInt("output_rate");
+	if (_outputRate <= 0)
+		_outputRate = DEFAULT_OUTPUT_RATE;
+
+	int diff50, diff33, diff25, diff20, diff16, diff12, diff10, diff8;
+	diff50 = abs(49170 - (int)_outputRate);
+	diff33 = abs(32780 - (int)_outputRate);
+	diff25 = abs(24585 - (int)_outputRate);
+	diff20 = abs(19668 - (int)_outputRate);
+	diff16 = abs(16390 - (int)_outputRate);
+	diff12 = abs(12292 - (int)_outputRate);
+	diff10 = abs(9834 - (int)_outputRate);
+	diff8  = abs(8195 - (int)_outputRate);
+
+	if (diff50 < diff33) {
+		_outputRate = 49170;
+		_clk = CLK50K;
+	} else if (diff33 < diff25) {
+		_outputRate = 32780;
+		_clk = CLK33K;
+	} else if (diff25 < diff20) {
+		_outputRate = 24585;
+		_clk = CLK25K;
+	} else if (diff20 < diff16) {
+		_outputRate = 19668;
+		_clk = CLK20K;
+	} else if (diff16 < diff12) {
+		_outputRate = 16390;
+		_clk = CLK16K;
+	} else if (diff12 < diff10) {
+		_outputRate = 12292;
+		_clk = CLK12K;
+	} else if (diff10 < diff8) {
+		_outputRate = 9834;
+		_clk = CLK10K;
+	} else {
+		_outputRate = 8195;
+		_clk = CLK8K;
+	}
+
+	ConfMan.setInt("output_rate", _outputRate);
+	debug("setting %d Hz mixing frequency", _outputRate);
+
+	_samples = 8192;
+	while (_samples * 16 > _outputRate * 2)
+		_samples >>= 1;
+
+	ConfMan.registerDefault("output_samples", (int)_samples);
+
+	int samples = ConfMan.getInt("output_samples");
+	if (samples > 0)
+		_samples = samples;
+
+	ConfMan.setInt("output_samples", (int)_samples);
+	debug("sample buffer size: %d", _samples);
+
+	ConfMan.flushToDisk();
+
+	_samplesBuf = new uint8[_samples * 4];
+
+	g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 10, false);
+}
+
+AtariMixerManager::~AtariMixerManager() {
+	g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
+
+	Buffoper(0x00);
+
+	Mfree(_atariSampleBuffer);
+	_atariSampleBuffer = _atariPhysicalSampleBuffer = _atariLogicalSampleBuffer = nullptr;
+
+	Sndstatus(SND_RESET);
+
+	Unlocksnd();
+
+	_atariInitialized = false;
+
+	delete[] _samplesBuf;
+}
+
+void AtariMixerManager::init() {
+	_mixer = new Audio::MixerImpl(_outputRate, _samples);
+	_mixer->setReady(true);
+
+	_atariSampleBufferSize = _samples * 4;
+
+	_atariSampleBuffer = (byte*)Mxalloc(_atariSampleBufferSize * 2, MX_STRAM);
+	if (!_atariSampleBuffer)
+		return;
+
+	_atariPhysicalSampleBuffer = _atariSampleBuffer;
+	_atariLogicalSampleBuffer = _atariSampleBuffer + _atariSampleBufferSize;
+
+	memset(_atariSampleBuffer, 0, 2 * _atariSampleBufferSize);
+
+	if (Locksnd() < 0)
+		return;
+
+	Sndstatus(SND_RESET);
+	Setmode(MODE_STEREO16);
+	Devconnect(DMAPLAY, DAC, CLK25M, _clk, NO_SHAKE);
+	Soundcmd(ADDERIN, MATIN);
+	Setbuffer(SR_PLAY, _atariSampleBuffer, _atariSampleBuffer + 2 * _atariSampleBufferSize);
+	Buffoper(SB_PLA_ENA | SB_PLA_RPT);
+
+	_atariInitialized = true;
+}
+
+void AtariMixerManager::suspendAudio() {
+	debug("suspendAudio");
+
+	Buffoper(0x00);
+
+	_audioSuspended = true;
+}
+
+int AtariMixerManager::resumeAudio() {
+	debug("resumeAudio 1");
+
+	if (!_audioSuspended || !_atariInitialized) {
+		return -2;
+	}
+
+	debug("resumeAudio 2");
+
+	Buffoper(SB_PLA_ENA | SB_PLA_RPT);
+
+	_audioSuspended = false;
+	return 0;
+}
+
+bool AtariMixerManager::notifyEvent(const Common::Event &event) {
+	switch (event.type) {
+	case Common::EVENT_QUIT:
+	case Common::EVENT_RETURN_TO_LAUNCHER:
+		debug("silencing the mixer");
+		memset(_atariSampleBuffer, 0, 2 * _atariSampleBufferSize);
+		return false;
+	case Common::EVENT_MUTE:
+		_muted = !_muted;
+		debug("audio %s", _muted ? "off" : "on");
+		return false;
+	default:
+		[[fallthrough]];
+	}
+
+	return false;
+}
+
+void AtariMixerManager::update() {
+	if (_audioSuspended && !_muted) {
+		return;
+	}
+
+	static bool loadSampleFlag = true;
+	byte *buf = nullptr;
+
+	SndBufPtr sPtr;
+	if (Buffptr(&sPtr) != 0)
+		return;
+
+	if (!loadSampleFlag) {
+		// we play from _atariPhysicalSampleBuffer (1st buffer)
+		if ((byte*)sPtr.play < _atariLogicalSampleBuffer) {
+			buf = _atariLogicalSampleBuffer;
+			loadSampleFlag = !loadSampleFlag;
+		}
+	} else {
+		// we play from _atariLogicalSampleBuffer (2nd buffer)
+		if ((byte*)sPtr.play >= _atariLogicalSampleBuffer) {
+			buf = _atariPhysicalSampleBuffer;
+			loadSampleFlag = !loadSampleFlag;
+		}
+	}
+
+	if (_atariInitialized && buf != nullptr) {
+		assert(_mixer);
+		// generates stereo 16-bit samples
+		int processed = _mixer->mixCallback(_samplesBuf, _samples * 4);
+		if (processed > 0) {
+			memcpy(buf, _samplesBuf, processed * 4);
+		} else {
+			memset(buf, 0, _atariSampleBufferSize);
+		}
+	}
+}
diff --git a/backends/mixer/atari/atari-mixer.h b/backends/mixer/atari/atari-mixer.h
new file mode 100644
index 00000000000..cbbd20a611f
--- /dev/null
+++ b/backends/mixer/atari/atari-mixer.h
@@ -0,0 +1,60 @@
+/* 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_MIXER_ATARI_H
+#define BACKENDS_MIXER_ATARI_H
+
+#include "backends/mixer/mixer.h"
+#include "common/events.h"
+
+/**
+ *  Atari XBIOS based audio mixer.
+ */
+
+class AtariMixerManager : public MixerManager, Common::EventObserver {
+public:
+	AtariMixerManager();
+	virtual ~AtariMixerManager();
+
+	virtual void init() override;
+	void update();
+
+	void suspendAudio() override;
+	int resumeAudio() override;
+
+	bool notifyEvent(const Common::Event &event) override;
+
+private:
+	int _clk;
+	uint32 _outputRate;
+	uint32 _samples;
+	uint8 *_samplesBuf;
+
+	bool _atariInitialized = false;
+	byte *_atariSampleBuffer = nullptr;
+	byte *_atariPhysicalSampleBuffer = nullptr;
+	byte *_atariLogicalSampleBuffer = nullptr;
+	size_t _atariSampleBufferSize;	// one buffer (logical/physical)
+
+	bool _muted = false;
+};
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 73247a32180..7b99c4a6103 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -321,6 +321,15 @@ MODULE_OBJS += \
 	plugins/3ds/3ds-provider.o
 endif
 
+ifeq ($(BACKEND),atari)
+MODULE_OBJS += \
+	graphics/atari/atari_c2p-asm.o \
+	graphics/atari/atari-graphics.o \
+	graphics/atari/atari-graphics-asm.o \
+	graphics/atari/videl-resolutions.o \
+	mixer/atari/atari-mixer.o
+endif
+
 ifeq ($(BACKEND),ds)
 MODULE_OBJS += \
 	events/ds/ds-events.o \
diff --git a/backends/platform/atari/atari.cpp b/backends/platform/atari/atari.cpp
new file mode 100644
index 00000000000..ec3a0337f9a
--- /dev/null
+++ b/backends/platform/atari/atari.cpp
@@ -0,0 +1,660 @@
+/* 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 <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <mint/cookie.h>
+#include <mint/falcon.h>
+#include <mint/osbind.h>
+
+// We use some stdio.h functionality here thus we need to allow some
+// symbols. Alternatively, we could simply allow everything by defining
+// FORBIDDEN_SYMBOL_ALLOW_ALL
+#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
+#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
+#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
+#define FORBIDDEN_SYMBOL_EXCEPTION_fputs
+#define FORBIDDEN_SYMBOL_EXCEPTION_exit
+#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
+#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
+
+#include "common/scummsys.h"
+
+#if defined(ATARI)
+#include "backends/graphics/atari/atari-graphics-asm.h"
+#include "backends/keymapper/hardware-input.h"
+#include "backends/modular-backend.h"
+#include "backends/mutex/null/null-mutex.h"
+#include "base/main.h"
+
+#include "backends/saves/default/default-saves.h"
+#include "backends/timer/default/default-timer.h"
+#include "backends/events/default/default-events.h"
+#include "backends/mixer/atari/atari-mixer.h"
+#include "backends/graphics/atari/atari-graphics.h"
+#include "backends/graphics/atari/atari-graphics-supervidel.h"
+#include "backends/graphics/atari/atari-graphics-videl.h"
+#include "common/hashmap.h"
+#include "gui/debugger.h"
+
+/*
+ * Include header files needed for the getFilesystemFactory() method.
+ */
+#include "backends/fs/posix/posix-fs-factory.h"
+
+class OSystem_Atari : public ModularMixerBackend, public ModularGraphicsBackend, Common::EventSource {
+public:
+	OSystem_Atari();
+	virtual ~OSystem_Atari();
+
+	void initBackend() override;
+
+	bool pollEvent(Common::Event &event) override;
+
+	Common::MutexInternal *createMutex() override;
+	uint32 getMillis(bool skipRecord = false) override;
+	void delayMillis(uint msecs) override;
+	void getTimeAndDate(TimeDate &td, bool skipRecord = false) const override;
+
+	Common::KeymapArray getGlobalKeymaps() override;
+	Common::HardwareInputSet *getHardwareInputSet() override;
+
+	void quit() override;
+
+	void logMessage(LogMessageType::Type type, const char *message) override;
+
+	void addSysArchivesToSearchSet(Common::SearchSet &s, int priority) override;
+	Common::String getDefaultConfigFileName() override;
+
+private:
+	AtariGraphicsManager *_atariGraphicsManager;
+
+	clock_t _startTime;
+
+	bool _video_initialized = false;
+	bool _ikbd_initialized = false;
+
+	bool _oldLmbDown = false;
+	bool _oldRmbDown = false;
+
+	bool _lshiftActive = false;
+	bool _rshiftActive = false;
+	bool _ctrlActive = false;
+	bool _altActive = false;
+	bool _capslockActive = false;
+
+	byte _unshiftToAscii[128];
+	byte _shiftToAscii[128];
+	byte _capsToAscii[128];
+
+	const Common::KeyCode _asciiToKeycode[128 - 32 - 1] = {
+		Common::KEYCODE_SPACE,
+		Common::KEYCODE_EXCLAIM,
+		Common::KEYCODE_QUOTEDBL,
+		Common::KEYCODE_HASH,
+		Common::KEYCODE_DOLLAR,
+		Common::KEYCODE_PERCENT,
+		Common::KEYCODE_AMPERSAND,
+		Common::KEYCODE_QUOTE,
+		Common::KEYCODE_LEFTPAREN,
+		Common::KEYCODE_RIGHTPAREN,
+		Common::KEYCODE_ASTERISK,
+		Common::KEYCODE_PLUS,
+		Common::KEYCODE_COMMA,
+		Common::KEYCODE_MINUS,
+		Common::KEYCODE_PERIOD,
+		Common::KEYCODE_SLASH,
+		Common::KEYCODE_0,
+		Common::KEYCODE_1,
+		Common::KEYCODE_2,
+		Common::KEYCODE_3,
+		Common::KEYCODE_4,
+		Common::KEYCODE_5,
+		Common::KEYCODE_6,
+		Common::KEYCODE_7,
+		Common::KEYCODE_8,
+		Common::KEYCODE_9,
+		Common::KEYCODE_COLON,
+		Common::KEYCODE_SEMICOLON,
+		Common::KEYCODE_LESS,
+		Common::KEYCODE_EQUALS,
+		Common::KEYCODE_GREATER,
+		Common::KEYCODE_QUESTION,
+		Common::KEYCODE_AT,
+		Common::KEYCODE_a,
+		Common::KEYCODE_b,
+		Common::KEYCODE_c,
+		Common::KEYCODE_d,
+		Common::KEYCODE_e,
+		Common::KEYCODE_f,
+		Common::KEYCODE_g,
+		Common::KEYCODE_h,
+		Common::KEYCODE_i,
+		Common::KEYCODE_j,
+		Common::KEYCODE_k,
+		Common::KEYCODE_l,
+		Common::KEYCODE_m,
+		Common::KEYCODE_n,
+		Common::KEYCODE_o,
+		Common::KEYCODE_p,
+		Common::KEYCODE_q,
+		Common::KEYCODE_r,
+		Common::KEYCODE_s,
+		Common::KEYCODE_t,
+		Common::KEYCODE_u,
+		Common::KEYCODE_v,
+		Common::KEYCODE_w,
+		Common::KEYCODE_x,
+		Common::KEYCODE_y,
+		Common::KEYCODE_z,
+		Common::KEYCODE_LEFTBRACKET,
+		Common::KEYCODE_BACKSLASH,
+		Common::KEYCODE_RIGHTBRACKET,
+		Common::KEYCODE_CARET,
+		Common::KEYCODE_UNDERSCORE,
+		Common::KEYCODE_BACKQUOTE,
+		Common::KEYCODE_a,
+		Common::KEYCODE_b,
+		Common::KEYCODE_c,
+		Common::KEYCODE_d,
+		Common::KEYCODE_e,
+		Common::KEYCODE_f,
+		Common::KEYCODE_g,
+		Common::KEYCODE_h,
+		Common::KEYCODE_i,
+		Common::KEYCODE_j,
+		Common::KEYCODE_k,
+		Common::KEYCODE_l,
+		Common::KEYCODE_m,
+		Common::KEYCODE_n,
+		Common::KEYCODE_o,
+		Common::KEYCODE_p,
+		Common::KEYCODE_q,
+		Common::KEYCODE_r,
+		Common::KEYCODE_s,
+		Common::KEYCODE_t,
+		Common::KEYCODE_u,
+		Common::KEYCODE_v,
+		Common::KEYCODE_w,
+		Common::KEYCODE_x,
+		Common::KEYCODE_y,
+		Common::KEYCODE_z,
+		Common::KEYCODE_INVALID,	// {
+		Common::KEYCODE_INVALID,	// |
+		Common::KEYCODE_INVALID,	// }
+		Common::KEYCODE_TILDE
+	};
+	Common::HashMap<byte, Common::KeyCode> _scancodeToKeycode;
+};
+
+extern "C" void atari_ikbd_init();
+extern "C" void atari_ikbd_shutdown();
+
+extern void nf_init(void);
+extern void nf_print(const char* msg);
+
+OSystem_Atari::OSystem_Atari() {
+	_fsFactory = new POSIXFilesystemFactory();
+
+	_scancodeToKeycode[0x01] = Common::KEYCODE_ESCAPE;
+	_scancodeToKeycode[0x0e] = Common::KEYCODE_BACKSPACE;
+	_scancodeToKeycode[0x0f] = Common::KEYCODE_TAB;
+	_scancodeToKeycode[0x1c] = Common::KEYCODE_RETURN;
+	_scancodeToKeycode[0x39] = Common::KEYCODE_SPACE;
+	_scancodeToKeycode[0x3b] = Common::KEYCODE_F1;
+	_scancodeToKeycode[0x3c] = Common::KEYCODE_F2;
+	_scancodeToKeycode[0x3d] = Common::KEYCODE_F3;
+	_scancodeToKeycode[0x3e] = Common::KEYCODE_F4;
+	_scancodeToKeycode[0x3f] = Common::KEYCODE_F5;
+	_scancodeToKeycode[0x40] = Common::KEYCODE_F6;
+	_scancodeToKeycode[0x41] = Common::KEYCODE_F7;
+	_scancodeToKeycode[0x42] = Common::KEYCODE_F8;
+	_scancodeToKeycode[0x43] = Common::KEYCODE_F9;
+	_scancodeToKeycode[0x44] = Common::KEYCODE_F10;
+	_scancodeToKeycode[0x45] = Common::KEYCODE_PAGEUP;	// Eiffel only
+	_scancodeToKeycode[0x46] = Common::KEYCODE_PAGEDOWN;	// Eiffel only
+	_scancodeToKeycode[0x47] = Common::KEYCODE_HOME;
+	_scancodeToKeycode[0x48] = Common::KEYCODE_UP;
+	_scancodeToKeycode[0x4a] = Common::KEYCODE_KP_MINUS;
+	_scancodeToKeycode[0x4b] = Common::KEYCODE_LEFT;
+	_scancodeToKeycode[0x4c] = Common::KEYCODE_LMETA;
+	_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[0x55] = Common::KEYCODE_END;	// Eiffel only
+	_scancodeToKeycode[0x5b] = Common::KEYCODE_TILDE;	// Eiffel only
+	_scancodeToKeycode[0x61] = Common::KEYCODE_F12;	// UNDO
+	_scancodeToKeycode[0x62] = Common::KEYCODE_F11;	// HELP
+	_scancodeToKeycode[0x63] = Common::KEYCODE_SLASH;	// KEYPAD /
+	_scancodeToKeycode[0x64] = Common::KEYCODE_KP_DIVIDE;
+	_scancodeToKeycode[0x65] = Common::KEYCODE_KP_MULTIPLY;
+	_scancodeToKeycode[0x66] = Common::KEYCODE_KP_MULTIPLY;	// duplicate?
+	_scancodeToKeycode[0x67] = Common::KEYCODE_7;	// KEYPAD 7
+	_scancodeToKeycode[0x68] = Common::KEYCODE_8;	// KEYPAD 8
+	_scancodeToKeycode[0x69] = Common::KEYCODE_9;	// KEYPAD 9
+	_scancodeToKeycode[0x6a] = Common::KEYCODE_4;	// KEYPAD 4
+	_scancodeToKeycode[0x6b] = Common::KEYCODE_5;	// KEYPAD 5
+	_scancodeToKeycode[0x6c] = Common::KEYCODE_6;	// KEYPAD 6
+	_scancodeToKeycode[0x6d] = Common::KEYCODE_1;	// KEYPAD 1
+	_scancodeToKeycode[0x6e] = Common::KEYCODE_2;	// KEYPAD 2
+	_scancodeToKeycode[0x6f] = Common::KEYCODE_3;	// KEYPAD 3
+	_scancodeToKeycode[0x70] = Common::KEYCODE_0;	// KEYPAD 0
+	_scancodeToKeycode[0x71] = Common::KEYCODE_KP_PERIOD;
+	_scancodeToKeycode[0x72] = Common::KEYCODE_KP_ENTER;
+}
+
+OSystem_Atari::~OSystem_Atari() {
+	debug("OSystem_Atari::~OSystem_Atari()");
+
+	if (_video_initialized) {
+		Supexec(asm_screen_falcon_restore);
+		_video_initialized = false;
+	}
+
+	if (_ikbd_initialized) {
+		Supexec(atari_ikbd_shutdown);
+		_ikbd_initialized = false;
+	}
+}
+
+static void ikbd_and_video_restore() {
+	Supexec(asm_screen_falcon_restore);
+	Supexec(atari_ikbd_shutdown);
+}
+
+void OSystem_Atari::initBackend() {
+	enum {
+		MCH_ST = 0,
+		MCH_STE,
+		MCH_TT,
+		MCH_FALCON,
+		MCH_CLONE,
+		MCH_ARANYM
+	};
+
+	long mch = MCH_ST<<16;
+	Getcookie(C__MCH, &mch);
+	mch >>= 16;
+
+	if (mch != MCH_FALCON && mch != MCH_ARANYM) {
+		error("ScummVM works only on Atari Falcon and ARAnyM");
+	}
+
+	if (mch == MCH_ARANYM && Getcookie(C_fVDI, NULL) == C_FOUND) {
+		logMessage(LogMessageType::kError, "Disable fVDI, ScummVM accesses Videl directly\n");
+		quit();
+	}
+
+	nf_init();
+
+	_startTime = clock();
+
+	bool superVidel = VgetMonitor() == MON_VGA && Getcookie(C_SupV, NULL) == C_FOUND;
+
+	_timerManager = new DefaultTimerManager();
+	_eventManager = new DefaultEventManager(makeKeyboardRepeatingEventSource(this));
+	_savefileManager = new DefaultSaveFileManager();
+	if (superVidel)
+        _atariGraphicsManager = new AtariSuperVidelManager();
+	else
+		_atariGraphicsManager = new AtariVidelManager();
+	_graphicsManager = _atariGraphicsManager;
+	_mixerManager = new AtariMixerManager();
+	// Setup and start mixer
+	_mixerManager->init();
+
+	_KEYTAB *pKeyTables = (_KEYTAB*)Keytbl(KT_NOCHANGE, KT_NOCHANGE, KT_NOCHANGE);
+
+	memcpy(_unshiftToAscii, pKeyTables->unshift, 128);
+	memcpy(_shiftToAscii, pKeyTables->shift, 128);
+	memcpy(_capsToAscii, pKeyTables->caps, 128);
+
+	Supexec(atari_ikbd_init);
+	_ikbd_initialized = true;
+
+	Supexec(asm_screen_falcon_save);
+	_video_initialized = true;
+
+	Setexc(VEC_PROCTERM, ikbd_and_video_restore);
+
+	BaseBackend::initBackend();
+}
+
+//! bit 0: rmb
+//! bit 1: lmb
+volatile uint8	g_atari_ikbd_mouse_buttons_state = 0;
+volatile int16	g_atari_ikbd_mouse_delta_x = 0;
+volatile int16	g_atari_ikbd_mouse_delta_y = 0;
+
+#define SCANCODES_SIZE 256
+volatile uint8	g_atari_ikbd_scancodes[SCANCODES_SIZE];
+uint16			g_atari_ikbd_scancodes_mask = SCANCODES_SIZE-1;
+volatile uint16	g_atari_ikbb_scancodes_head = 0;
+static uint16	g_atari_ikbb_scancodes_tail = 0;
+
+bool OSystem_Atari::pollEvent(Common::Event &event) {
+	{
+		static uint32 startMillis = getMillis();
+		static uint32 oldMillis = getMillis();
+		uint32 curMillis = getMillis();
+
+		uint32 diff = curMillis - oldMillis;
+		oldMillis = curMillis;
+
+		if (diff > 0) {
+			static float avgFpsSum;
+			static int avgFpsCount;
+
+			avgFpsSum += 1000.0f / diff;
+			avgFpsCount++;
+
+			if (curMillis - startMillis >= 1000) {
+				float avgFps = avgFpsSum / avgFpsCount;
+				debug("*** Average FPS in 1s: %f ***", avgFps);
+				startMillis = curMillis;
+				avgFpsSum = 0;
+				avgFpsCount = 0;
+			}
+		}
+	}
+
+	((DefaultTimerManager *)_timerManager)->checkTimers();
+	((AtariMixerManager *)_mixerManager)->update();
+
+	if ((g_atari_ikbd_mouse_buttons_state & 0x01) && !_oldRmbDown) {
+		event.type = Common::EVENT_RBUTTONDOWN;
+		event.mouse = _atariGraphicsManager->getMousePosition();
+		_oldRmbDown = true;
+		return true;
+	}
+
+	if (!(g_atari_ikbd_mouse_buttons_state & 0x01) && _oldRmbDown) {
+		event.type = Common::EVENT_RBUTTONUP;
+		event.mouse = _atariGraphicsManager->getMousePosition();
+		_oldRmbDown = false;
+		return true;
+	}
+
+	if ((g_atari_ikbd_mouse_buttons_state & 0x02) && !_oldLmbDown) {
+		event.type = Common::EVENT_LBUTTONDOWN;
+		event.mouse = _atariGraphicsManager->getMousePosition();
+		_oldLmbDown = true;
+		return true;
+	}
+
+	if (!(g_atari_ikbd_mouse_buttons_state & 0x02) && _oldLmbDown) {
+		event.type = Common::EVENT_LBUTTONUP;
+		event.mouse = _atariGraphicsManager->getMousePosition();
+		_oldLmbDown = false;
+		return true;
+	}
+
+	if (g_atari_ikbd_mouse_delta_x != 0 || g_atari_ikbd_mouse_delta_y != 0) {
+		const int deltaX = g_atari_ikbd_mouse_delta_x;
+		const int deltaY = g_atari_ikbd_mouse_delta_y;
+
+		g_atari_ikbd_mouse_delta_x = g_atari_ikbd_mouse_delta_y = 0;
+
+		_atariGraphicsManager->updateMousePosition(deltaX, deltaY);
+
+		event.type = Common::EVENT_MOUSEMOVE;
+		event.mouse = _atariGraphicsManager->getMousePosition();
+		event.relMouse = Common::Point(deltaX, deltaY);
+		return true;
+	}
+
+	if (g_atari_ikbb_scancodes_head != g_atari_ikbb_scancodes_tail) {
+		byte scancode = g_atari_ikbd_scancodes[g_atari_ikbb_scancodes_tail++];
+		g_atari_ikbb_scancodes_tail &= SCANCODES_SIZE-1;
+
+		bool pressed = !(scancode & 0x80);
+		scancode &= 0x7f;
+
+		if (scancode == 0x1d)
+			_ctrlActive = pressed;
+
+		if (scancode == 0x2a)
+			_lshiftActive = pressed;
+
+		if (scancode == 0x36)
+			_rshiftActive = pressed;
+
+		if (scancode == 0x38)
+			_altActive = pressed;
+
+		if (scancode == 0x3a && pressed)
+			_capslockActive = !_capslockActive;
+
+		// Eiffel only
+		if (scancode == 0x59) {
+			event.type = Common::EVENT_WHEELUP;
+			event.mouse = _atariGraphicsManager->getMousePosition();
+			return true;
+		}
+
+		// Eiffel only
+		if (scancode == 0x5a) {
+			event.type = Common::EVENT_WHEELDOWN;
+			event.mouse = _atariGraphicsManager->getMousePosition();
+			return true;
+		}
+
+		uint16 ascii;
+		if (_lshiftActive || _rshiftActive) {
+			ascii = _shiftToAscii[scancode];
+		} else if (_capslockActive) {
+			ascii = _capsToAscii[scancode];
+		} else {
+			ascii = _unshiftToAscii[scancode];
+		}
+
+		Common::KeyCode keycode = _scancodeToKeycode.getValOrDefault(scancode, Common::KEYCODE_INVALID);
+		switch (keycode) {
+		case Common::KEYCODE_BACKSPACE:
+			ascii = Common::ASCII_BACKSPACE;
+			break;
+		case Common::KEYCODE_TAB:
+			ascii = Common::ASCII_TAB;
+			break;
+		case Common::KEYCODE_RETURN:
+		case Common::KEYCODE_KP_ENTER:
+			ascii = Common::ASCII_RETURN;
+			break;
+		case Common::KEYCODE_ESCAPE:
+			ascii = Common::ASCII_ESCAPE;
+			break;
+		case Common::KEYCODE_SPACE:
+			ascii = Common::ASCII_SPACE;
+			break;
+		case Common::KEYCODE_F1:
+			ascii = Common::ASCII_F1;
+			break;
+		case Common::KEYCODE_F2:
+			ascii = Common::ASCII_F2;
+			break;
+		case Common::KEYCODE_F3:
+			ascii = Common::ASCII_F3;
+			break;
+		case Common::KEYCODE_F4:
+			ascii = Common::ASCII_F4;
+			break;
+		case Common::KEYCODE_F5:
+			ascii = Common::ASCII_F5;
+			break;
+		case Common::KEYCODE_F6:
+			ascii = Common::ASCII_F6;
+			break;
+		case Common::KEYCODE_F7:
+			ascii = Common::ASCII_F7;
+			break;
+		case Common::KEYCODE_F8:
+			ascii = Common::ASCII_F8;
+			break;
+		case Common::KEYCODE_F9:
+			ascii = Common::ASCII_F9;
+			break;
+		case Common::KEYCODE_F10:
+			ascii = Common::ASCII_F10;
+			break;
+		case Common::KEYCODE_F11:
+			ascii = Common::ASCII_F11;
+			break;
+		case Common::KEYCODE_F12:
+			ascii = Common::ASCII_F12;
+			break;
+		default:
+			break;
+		}
+
+		if (ascii >= ' ' && ascii <= '~') {
+			if (keycode == Common::KEYCODE_INVALID)
+				keycode = _asciiToKeycode[ascii - ' '];
+		}
+
+		event.type = pressed ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
+		event.kbd = Common::KeyState(keycode, ascii);
+		event.kbd.flags |= _ctrlActive ? Common::KBD_CTRL : 0;
+		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;
+
+		return true;
+	}
+
+	return false;
+}
+
+Common::MutexInternal *OSystem_Atari::createMutex() {
+	return new NullMutexInternal();
+}
+
+uint32 OSystem_Atari::getMillis(bool skipRecord) {
+	// CLOCKS_PER_SEC is 200, so no need to use floats
+	return 1000 * (clock() - _startTime) / CLOCKS_PER_SEC;
+}
+
+void OSystem_Atari::delayMillis(uint msecs) {
+	usleep(msecs * 1000);
+}
+
+void OSystem_Atari::getTimeAndDate(TimeDate &td, bool skipRecord) const {
+	debug("getTimeAndDate");
+	time_t curTime = time(0);
+	// TODO: if too slow (e.g. when calling RandomSource::RandomSource()), rewrite
+	struct tm t = *localtime(&curTime);
+	td.tm_sec = t.tm_sec;
+	td.tm_min = t.tm_min;
+	td.tm_hour = t.tm_hour;
+	td.tm_mday = t.tm_mday;
+	td.tm_mon = t.tm_mon;
+	td.tm_year = t.tm_year;
+	td.tm_wday = t.tm_wday;
+}
+
+Common::KeymapArray OSystem_Atari::getGlobalKeymaps() {
+	Common::KeymapArray globalMaps = BaseBackend::getGlobalKeymaps();
+
+	Common::Keymap *keymap = _atariGraphicsManager->getKeymap();
+	globalMaps.push_back(keymap);
+
+	return globalMaps;
+}
+
+Common::HardwareInputSet *OSystem_Atari::getHardwareInputSet() {
+	Common::CompositeHardwareInputSet *inputSet = new Common::CompositeHardwareInputSet();
+	inputSet->addHardwareInputSet(new Common::MouseHardwareInputSet(Common::defaultMouseButtons));
+	inputSet->addHardwareInputSet(new Common::KeyboardHardwareInputSet(Common::defaultKeys, Common::defaultModifiers));
+
+	return inputSet;
+}
+
+void OSystem_Atari::quit() {
+	Common::String str = Common::String::format("OSystem_Atari::quit()\n");
+	logMessage(LogMessageType::kDebug, str.c_str());
+
+	g_system->destroy();
+
+	exit(0);
+}
+
+void OSystem_Atari::logMessage(LogMessageType::Type type, const char *message) {
+	FILE *output = 0;
+
+	if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
+		output = stdout;
+	else
+		output = stderr;
+
+	fputs(message, output);
+	fflush(output);
+
+	nf_print(message);
+}
+
+void OSystem_Atari::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
+#ifdef DATA_PATH
+	// Add the global DATA_PATH to the directory search list
+	// See also OSystem_SDL::addSysArchivesToSearchSet()
+	Common::FSNode dataNode(DATA_PATH);
+	if (dataNode.exists() && dataNode.isDirectory()) {
+		s.add(DATA_PATH, new Common::FSDirectory(dataNode, 4), priority);
+	}
+#endif
+}
+
+Common::String OSystem_Atari::getDefaultConfigFileName() {
+	const Common::String baseConfigName = OSystem::getDefaultConfigFileName();
+
+	Common::String configFile;
+
+	const char *envVar = getenv("HOME");
+	if (envVar && *envVar) {
+		configFile = envVar;
+		configFile += '/';
+		configFile += baseConfigName;
+
+		if (configFile.size() < MAXPATHLEN)
+			return configFile;
+	}
+
+	return baseConfigName;
+}
+
+OSystem *OSystem_Atari_create() {
+	return new OSystem_Atari();
+}
+
+int main(int argc, char *argv[]) {
+	g_system = OSystem_Atari_create();
+	assert(g_system);
+
+	// Invoke the actual ScummVM main entry point:
+	int res = scummvm_main(argc, argv);
+	g_system->destroy();
+	return res;
+}
+
+#endif
diff --git a/backends/platform/atari/atari_ikbd.S b/backends/platform/atari/atari_ikbd.S
new file mode 100644
index 00000000000..b5a796611f9
--- /dev/null
+++ b/backends/platform/atari/atari_ikbd.S
@@ -0,0 +1,233 @@
+/* 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/>.
+ *
+ */
+
+/*
+ *	IKBD 6301 interrupt routine
+ *
+ *	Patrice Mandin
+ */
+
+	.globl	_atari_ikbd_init
+	.globl	_atari_ikbd_shutdown
+
+	.globl	_g_atari_ikbd_mouse_buttons_state
+	.globl	_g_atari_ikbd_mouse_delta_x
+	.globl	_g_atari_ikbd_mouse_delta_y
+
+	.globl	_g_atari_ikbd_scancodes
+	.globl	_g_atari_ikbd_scancodes_size
+	.globl	_g_atari_ikbb_scancodes_head
+
+
+	.text
+
+_atari_ikbd_init:
+	| Disable interrupts
+
+	movew	sr,d1
+	movew	#0x2700,sr
+
+	| Save MFP registers used for keyboard
+
+	lea	0xfffffa00:w,a0
+	btst	#6,a0@(0x09)
+	sne	ikbd_ierb
+	btst	#6,a0@(0x15)
+	sne	ikbd_imrb
+
+	| Set our routine
+
+	movel	0x118:w,old_ikbd
+	movel	#ikbd,0x118:w
+	bset	#6,0xfffffa09:w	| IERB
+	bset	#6,0xfffffa15:w	| IMRB
+
+	| Set mouse relative mode
+
+	moveb	#8,0xfffffc02:w
+
+	| Reenable interrupts
+
+	movew	d1,sr
+	rts
+
+_atari_ikbd_shutdown:
+	| Disable interrupts
+
+	movew	sr,d1
+	movew	#0x2700,sr
+
+	| Restore previous MFP registers
+
+	lea	0xfffffa00:w,a0
+
+	bclr	#6,a0@(0x09)
+	tstb	ikbd_ierb
+	beqs	ikbd_restoreierb
+	bset	#6,a0@(0x09)
+ikbd_restoreierb:
+
+	bclr	#6,a0@(0x15)
+	tstb	ikbd_imrb
+	beqs	ikbd_restoreimrb
+	bset	#6,a0@(0x15)
+ikbd_restoreimrb:
+
+	movel	old_ikbd,0x118:w
+
+	| Clear keyboard buffer
+
+	lea	0xfffffc00:w,a0
+ikbd_videbuffer:
+	btst	#0,a0@
+	beqs	ikbd_finbuffer
+	tstb	a0@(0x02)
+	bras	ikbd_videbuffer
+ikbd_finbuffer:
+
+	| Reenable interrupts
+
+	movew	d1,sr
+	rts
+
+	.bss
+
+	.even
+ikbd_ierb:
+	.ds.b	1
+ikbd_imrb:
+	.ds.b	1
+
+/*--- Our custom IKBD vector ---*/
+
+	.text
+	.even
+	.ascii	"XBRA"
+	.ascii	"SCUM"
+old_ikbd:
+	.dc.l	0
+ikbd:
+	moveml	d0-d1/a0,sp at -
+
+	| Check if source is IKBD or MIDI
+
+	btst	#0,0xfffffc00.w
+	beqs	ikbd_oldmidi
+
+	moveb	0xfffffc02:w,d0
+
+	| Not supported packets ?
+
+	| status report
+	cmpb	#0xf6,d0
+	beqs	ikbd_endit_stack
+	| absolute mouse position record
+	cmpb	#0xf7,d0
+	beqs	ikbd_endit_stack
+	| time-of-day
+	cmpb	#0xfc,d0
+	beqs	ikbd_endit_stack
+
+	| Joystick packet ?
+
+	| joystick report (both sticks), joystick 0 event, joystick 1 event
+	cmpb	#0xfd,d0
+	bhss	ikbd_endit_stack
+
+	| Mouse packet ?
+
+	cmpb	#0xf8,d0
+	blos	ikbd_no_mouse
+	cmpb	#0xfc,d0
+	bhss	ikbd_no_mouse
+
+	| Mouse packet, byte #1
+
+ikbd_yes_mouse:
+	andw	#3,d0
+	moveb	d0,_g_atari_ikbd_mouse_buttons_state
+
+	movel	#ikbd_mousex,0x118:w
+	bras	ikbd_endit_stack
+
+	| Keyboard press/release
+
+ikbd_no_mouse:
+	lea	_g_atari_ikbd_scancodes,a0
+	movew	_g_atari_ikbb_scancodes_head,d1
+
+	| g_atari_ikbd_scancodes[g_atari_ikbb_scancodes_head] = scancode
+
+	moveb	d0,(0.b,a0,d1.w)
+
+	addql	#1,d1
+	andw	_g_atari_ikbd_scancodes_mask,d1
+	movew	d1,_g_atari_ikbb_scancodes_head
+
+	| End of interrupt
+
+ikbd_endit_stack:
+	moveml	sp at +,d0-d1/a0
+
+	bclr	#6,0xfffffa11:w
+	rte
+
+	| Call old MIDI interrupt
+
+ikbd_oldmidi:
+	moveml	sp at +,d0-d1/a0
+
+	movel	old_ikbd,sp at -
+	rts
+
+	| Mouse packet, byte #2
+
+ikbd_mousex:
+	moveml	d0-d1/a0,sp at -
+
+	| Check if source is IKBD or MIDI
+	btst	#0,0xfffffc00.w
+	beqs	ikbd_oldmidi
+
+	moveb	0xfffffc02:w,d0
+	extw	d0
+	addw	d0,_g_atari_ikbd_mouse_delta_x
+
+	movel	#ikbd_mousey,0x118:w
+	bras	ikbd_endit_stack
+
+	| Mouse packet, byte #3
+
+ikbd_mousey:
+	moveml	d0-d1/a0,sp at -
+
+	| Check if source is IKBD or MIDI
+
+	btst	#0,0xfffffc00.w
+	beqs	ikbd_oldmidi
+
+	moveb	0xfffffc02:w,d0
+	extw	d0
+
+	addw	d0,_g_atari_ikbd_mouse_delta_y
+
+	movel	#ikbd,0x118:w
+	bras	ikbd_endit_stack
diff --git a/backends/platform/atari/module.mk b/backends/platform/atari/module.mk
new file mode 100644
index 00000000000..16bd53ec57b
--- /dev/null
+++ b/backends/platform/atari/module.mk
@@ -0,0 +1,11 @@
+MODULE := backends/platform/atari
+
+MODULE_OBJS := \
+	atari.o \
+	atari_ikbd.o \
+	native_features.o
+
+# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
+MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
+OBJS := $(MODULE_OBJS) $(OBJS)
+MODULE_DIRS += $(sort $(dir $(MODULE_OBJS)))
diff --git a/backends/platform/atari/native_features.cpp b/backends/platform/atari/native_features.cpp
new file mode 100644
index 00000000000..9763ec8ceae
--- /dev/null
+++ b/backends/platform/atari/native_features.cpp
@@ -0,0 +1,96 @@
+/* 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/>.
+ *
+ */
+
+// Taken from mintlib (https://github.com/freemint/mintlib)
+// (c) Thorsten Otto
+
+#include <mint/osbind.h>
+#include <stdint.h>
+
+#define NATFEAT_ID   0x7300
+#define NATFEAT_CALL 0x7301
+
+#define ASM_NATFEAT3(opcode) "\t.word " #opcode "\n"
+#define ASM_NATFEAT2(opcode) ASM_NATFEAT3(opcode)
+#define ASM_NATFEAT(n) ASM_NATFEAT2(n)
+
+static unsigned short const nf_id_opcodes[] = { NATFEAT_ID, 0x4e75 };
+static unsigned short const nf_call_opcodes[] = { NATFEAT_CALL, 0x4e75 };
+
+#define _nf_get_id(feature_name) ((long (__CDECL *)(const char *))nf_id_opcodes)(feature_name)
+#define _nf_call(id, ...) ((long (__CDECL *)(long, ...))nf_call_opcodes)(id, __VA_ARGS__)
+
+/*
+ * on ColdFire, the NATFEAT_ID opcode is actually
+ * "mvs.b d0,d1",
+ * which means the following code will NOT detect
+ * the presence of an emulator (should there ever
+ * be an emulator capable of emulating a ColdFire processor).
+ * Luckily, executing the code on a CF processor is still
+ * harmless since all it does is clobber D1.
+ */
+static long _nf_detect_tos(void) {
+	register long ret __asm__ ("d0");
+	register const char *nf_version __asm__("a1") = "NF_VERSION";
+
+	__asm__ volatile(
+	"\tmove.l	%1,-(%%sp)\n"
+	"\tmoveq	#0,%%d0\n"			/* assume no NatFeats available */
+	"\tmove.l	%%d0,-(%%sp)\n"
+	"\tlea		(1f:w,%%pc),%%a1\n"
+	"\tmove.l	(0x0010).w,%%a0\n"	/* illegal instruction vector */
+	"\tmove.l	%%a1,(0x0010).w\n"
+	"\tmove.l	%%sp,%%a1\n"		/* save the ssp */
+
+	"\tnop\n"						/* flush pipelines (for 68040+) */
+
+	ASM_NATFEAT(NATFEAT_ID)			/* Jump to NATFEAT_ID */
+	"\ttst.l	%%d0\n"
+	"\tbeq.s	1f\n"
+	"\tmoveq	#1,%%d0\n"			/* NatFeats detected */
+	"\tmove.l	%%d0,(%%sp)\n"
+
+"1:\n"
+	"\tmove.l	%%a1,%%sp\n"
+	"\tmove.l	%%a0,(0x0010).w\n"
+	"\tmove.l	(%%sp)+,%%d0\n"
+	"\taddq.l	#4,%%sp\n"			/* pop nf_version argument */
+
+	"\tnop\n"						/* flush pipelines (for 68040+) */
+	: "=g"(ret)  /* outputs */
+	: "g"(nf_version)		/* inputs  */
+	: __CLOBBER_RETURN("d0") "a0", "d1", "cc" AND_MEMORY
+	);
+	return ret;
+}
+
+static long nf_stderr_id;
+
+void nf_init(void) {
+	long ret = Supexec(_nf_detect_tos);
+	if (ret == 1)
+		nf_stderr_id = _nf_get_id("NF_STDERR");
+}
+
+void nf_print(const char* msg) {
+	if (nf_stderr_id)
+		_nf_call(nf_stderr_id | 0, (uint32_t)msg);
+}
diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
new file mode 100644
index 00000000000..f235c595132
--- /dev/null
+++ b/backends/platform/atari/readme.txt
@@ -0,0 +1,422 @@
+ScummVM
+=======
+
+This is a new port of ScummVM (https://www.scummvm.org), a program which allows
+you to run certain classic graphical adventure and role-playing games, provided
+you already have their data files.
+
+You can find a full list with details on which games are supported and how well
+on the compatibility page: https://www.scummvm.org/compatibility.
+
+
+Yet another port?
+-----------------
+
+Yes, I am aware of the official Atari/FreeMiNT port done by KeithS over the
+years (https://docs.scummvm.org/en/v2.6.1/other_platforms/atari.html). It is
+even updated every release and put on the official ScummVM website. That port
+is basically just a recompiled SDL backend for our platform - that certainly
+has some advantages (works in GEM, can be easily compiled for the FireBee etc.)
+but I have decided to take a different route:
+
+- Reduced executable size, basically whatever is not essential or plausible on
+  our platform is left out. That reduces the file size to half. See also the
+  next point.
+
+- Because there's a limited horsepower available on our platform, features like
+  hi-res 16bpp graphics, software synthesizers, scalers, real-time software
+  MP3/OGG/FLAC playback etc., are omitted. This saves memory and disk space,
+  making the whole port more lightweight.
+
+- This port natively talks to the hardware, avoiding intermediate layers like
+  SDL. Thus, it has more optimisations, fewer redraws, fewer data copying and
+  is less crash-prone.
+
+- Because we limit scope only to 8bpp games, it opens a door to more thorough
+  testing and there is a certain interest in this in the community. 16bpp games
+  could be played only in ARAnyM or similar, limiting the test audience a lot.
+
+After I had seen how snappy NovaCoder's ScummVM on the Amiga is (who coded
+his own backend), I decided to do the same and see whether I could do better.
+And I could!
+
+
+Main features
+-------------
+
+- Optimized for the Atari Falcon (ideally with the CT60/CT63/CT60e but for the
+less hungry games even a CT2/DFB at 50 MHz or the AfterBurner040 could be enough).
+
+- Full support for the SuperVidel, incl. the SuperBlitter (!)
+
+- Removed features found too demanding for our platform; the most visible
+  change is the exclusion of the 16bpp games (those are mostly hi-res anyway)
+  but games in 640x480 at 8bpp work nicely.
+
+- Direct rendering and single/double/triple buffering support.
+
+- Custom (and optimal) drawing routines (especially for the cursor).
+
+- Custom (Super)Videl resolutions for the best possible performance and visual
+  experience (320x240 in RGB, chunky modes with SuperVidel, 640x480 at 16bpp for
+  the overlay in RGB/SuperVidel, ...)
+
+- Custom (hardware based) aspect ratio correction (!)
+
+- Support for PC keys (page up, page down, pause, F11/F12, ...) and mouse wheel
+  (Eiffel/Aranym only)
+
+- Still without any assembly optimizations...
+
+This makes such games as The Curse of Monkey Island better playable (on
+SuperVidel nearly always also with CD (WAV) music and speech). Also, AdLib
+emulation works nicely with many games without noticeable slow downs.
+
+
+Platform-specific features outside the GUI
+------------------------------------------
+
+Keyboard shortcut "CONTROL+u": immediate mute on/off toggle (disables also
+sample mixing, contrary to what "Mute all" in the options does!)
+
+Keyboard shortcut "CONTROL+ALT+a": immediate aspect ratio correction on/off
+toggle.
+
+"output_rate" in scummvm.ini: sample rate for mixing, can be 49170, 32780,
+24585, 19668, 16390, 12292, 9834, 8195 (the lower the value, the faster the
+mixing but also in worse quality). Default is 24585 Hz (16-bit, stereo).
+
+"output_samples" in scummvm.ini: number of samples to preload. Default is 2048
+which equals to about 83ms of audio lag and seems to be about right for most
+games on my CT60 at 66 MHz.
+
+If you want to play with those two values, the rule of thumb is: (lag in ms) =
+(output_samples / output_rate) * 1000. But it's totally OK just to double the
+samples value to get rid of stuttering in a heavier game.
+
+
+Graphics modes
+--------------
+
+This topic is more complex than it looks. ScummVM renders game graphics using
+rectangles and this port offers following options to render them:
+
+Direct rendering (vsync on/off) - present only with the SuperVidel
+Single buffering (vsync on/off)
+Double buffering (vsync always on, the checkbox is ignored)
+Triple buffering (vsync always off, the checkbox just selects a different kind)
+
+Direct rendering:
+~~~~~~~~~~~~~~~~~
+
+This is direct writing of the pixels into (SuperVidel's) screen buffer. Since
+the updates are supplied as rectangles and not the whole screen there's no way
+to implement direct writing *and* double/triple buffering. Vsync() only
+synchronizes the point when the rendering process begins - if it takes more
+than the time reserved for the vertical blank interrupt (what happens
+with most of the games), you'll see screen tearing.
+
+Pros:
+
+- fastest possible rendering (especially in 640x480 with a lot of small
+  rectangle updates where the buffer copying drags performance down)
+
+Cons:
+
+- screen tearing in most cases
+
+- SuperVidel only: using C2P would be not only suboptimal (every rectangle
+  would be C2P'ed instead of just copy and C2P of the final screen) but poses an
+  additional problem as C2P requires data aligned on a 16px boundary and
+  ScummVM supplies arbitrarily-sized rectangles (this is solvable by custom
+  Surface allocation but it's not bullet-proof). In theory I could implement
+  direct rendering for the Falcon hicolor (320x240 at 16bpp) but this creates
+  another set of issues like when palette would be updated but not the whole
+  screen - so some rectangles would be rendered in old palette and some in new.
+
+SuperBlitter used: sometimes (when ScummVM allocates surface via its create()
+function; custom/small buffers originating in the engine code are still copied
+using the CPU).
+
+Single buffering:
+~~~~~~~~~~~~~~~~~
+
+This is very similar to the previous mode with the difference that the engine
+uses an intermediate buffer for storing the rectangles but yet it remembers
+which ones they were. It works also on plain Videl and applies the chunky to
+planar process to each one of the rectangles separately, avoiding fullscreen
+updates (but if such is needed, there is an optimized code path for it). Vsync()
+is used the same way as in the previous mode, i.e. screen tearing is still
+possible.
+
+Pros:
+
+- second fastest possible rendering
+
+- doesn't update the whole screen (works best with a moderate amount of
+  rectangles to update)
+
+Cons:
+
+- screen tearing in most cases
+
+- if there are too many smaller rectangles, it can be less efficient than
+  updating the whole buffer at once
+
+SuperBlitter used: yes, for rectangle blitting to screen and cursor restoration.
+Sometimes also for generic copying between buffers (see above).
+
+Double buffering:
+~~~~~~~~~~~~~~~~~
+
+The most common rendering mode. It extends the idea of single buffering - it
+renders into two buffers, one is visible while the other one is used for
+updating. At the end of the update process the two buffers are swapped, so the
+newly updated one is displayed. By definition, Vsync() must be always enabled
+(the buffers are swapped in the vertical blank handler) otherwise you'd see
+screen tearing.
+
+Pros:
+
+- stable frame rate, leading to fixed e.g. 30 FPS rendering for the whole time
+  if game takes, say, 1.7 - 1.9 frames per update
+
+- no screen tearing in any situation
+
+Cons:
+
+- since two buffers are present, the buffer is always blitted into the screen
+  surface as whole, even if only one tiny little rectangle is changed (excluding
+  the cursor)
+
+- frame rate is set to 60/30/15/etc FPS so you can see big irregular jumps
+  between 30 and 15 FPS for example; this is happening when screen updates take
+  variable amount of time but since Vsync() is always called, the rendering
+  pipeline has to wait until the next frame even if only 1% of the frame time
+  has been used.
+
+SuperBlitter used: yes, for rectangle blitting to screen and cursor restoration.
+Sometimes also for generic copying between buffers (see above).
+
+Triple buffering:
+~~~~~~~~~~~~~~~~~
+
+Best of both worlds - screen tearing is avoided thanks to using of multiple
+buffers and the rendering pipeline doesn't have to wait until Vsync(). The vsync
+flag is used only to differentiate between two (very similar) modes of
+operation:
+
+1. "True triple buffering" as described in
+https://en.wikipedia.org/wiki/Multiple_buffering#Triple_buffering (vsync on)
+
+2. "Swap chain" as described in https://en.wikipedia.org/wiki/Swap_chain (vsync
+off)
+
+Pros:
+
+- best compromise between performance and visual experience
+
+- works well with both higher and lower frame rates
+
+Cons:
+
+- since three buffers are present, the buffer is always blitted into the screen
+  surface as whole, even if only one tiny little rectangle is changed (excluding
+  the cursor)
+
+- slightly irregular frame rate (depends solely on the game's complexity)
+
+- in case of extremely fast rendering in 1.), one or more buffers are
+  dropped in favor of showing only the most recent one (unlikely)
+
+- in case of extremely fast rendering in 2.), screen tearing is possible
+  because the rendering pipeline starts overwriting the buffer which is
+  currently displayed (unlikely)
+
+SuperBlitter used: yes, for rectangle blitting to screen and cursor restoration.
+Sometimes also for generic copying between buffers (see above).
+
+Triple buffering with vsync on is the default mode for this port.
+
+
+SuperVidel and SuperBlitter
+---------------------------
+
+As mentioned, this port uses SuperVidel and its SuperBlitter heavily. That
+means that if the SuperVidel is detected, it does the following:
+
+- patches all 8bpp VGA resolutions to chunky ones, rendering all C2P routines
+  useless
+
+- patches all surface addresses with OR'ing 0xA0000000, i.e. using SV RAM
+  instead of slow ST RAM (and even instead of TT RAM for allowing pure
+  SuperBlitter copying)
+
+- when SuperVidel FW version >= 9 is detected, the async FIFO buffer is used
+  instead of the slower sync blitting (where one has to wait for every
+  rectangle blit to finish). This applies only for chunky buffer -> screen
+  surfaces copy (as the generic surface copy can't rely on this behavior) but
+  despite this limitation it sometimes leads to nearly zero-cost rendering
+  and makes a *huge* difference for 640x480 fullscreen updates.
+
+
+Performance considerations/pitfalls
+-----------------------------------
+
+It's important to understand what affects performance on our limited platform
+to avoid unpleasant playing experiences.
+
+Game engines with unexpected performance hit
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A typical example from this category is the Gobliins engine (and its
+sequels). At first it looks like our machine / backend is doing something
+terribly wrong but the truth is it is the engine itself which is doing a lot of
+unnecessary redraws and updates, sometimes even before reaching the backend.
+The only real solution is to profile and fix the engine.
+
+Too many fullscreen updates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Somewhat related to the previous point - sometimes the engine authors didn't
+realize the impact of every update on the overall performance and instead of
+updating only the rectangles that really had changed, they ask for a full screen
+update. Not a problem on a >1 GHz machine but very visible on Atari! Also, this
+is (by definition) the case of animated intros, especially those in 640x480.
+
+MIDI vs. AdLib vs. sampled music
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It could seem that sample music replay must be the most demanding one but on the
+contrary! _Always_ choose a CD version of a game (with *.wav tracks) to any
+other version. With one exception: if you have a native MIDI device able to
+replay the given game's MIDI notes (using the STMIDI plugin). MIDI emulation
+(synthesis) can easily take down as many as 10 FPS.
+
+CD music slows everything down
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some games use separate audio *and* video streams (files). Even if the CPU is
+able to handle both, the bottleneck becomes ... disk access. This is visible in
+The Curse Of Monkey Island for example -- there's audible stuttering during the
+intro sequence (and during the game as well). Increasing "output_samples" makes
+the rendering literally crawling! Why? Because disk I/O is busy with loading
+even *more* sample data so there's less time for video loading and rendering.
+Try to put "musdisk1.bun" and "musdisk2.bun" into a ramdisk (i.e. u:/ram in
+FreeMiNT), you'll be pleasantly surprised with the performance boost gained.
+
+"Mute" vs. "Mute all" in GUI vs. "No music" in GUI
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Not the same thing. "Mute" (available only via the shortcut CONTROL+u) generates
+an event to which the sample mixer can react (i.e. stop mixing the silence...).
+
+"Mute all" doesn't generate anything, it basically just lowers the volume of the
+music to zero.
+
+"No music" means using the null audio plugin which prevents generating any MIDI
+music (and therefore avoiding the expensive synthesis emulation) but beware, it
+doesn't affect CD (*.wav) playback at all!
+
+So for the best performance, always choose "No music" in the GUI options when
+the game contains MIDI tracks and "Mute" when the game contains a sampled
+soundtrack.
+
+Please note that it is not that bad, you surely can play The Secret of Monkey
+Island with AdLib enabled (but the CD/talkie versions sound better and
+are cheaper to play ;)).
+
+Vsync in GUI
+~~~~~~~~~~~~
+
+Carefully with the vsync option. It can easily cripple direct/single buffer
+rendering by 10-15 FPS if not used with caution. That happens if a game takes,
+say, 1.2 frames per update (so causing screen tearing anyway and rendering the
+option useless) but Vsync() forces it to wait 2 full frames instead.
+
+By the way, the vsync flag in Global Options affects also the overlay rendering
+(with all the pitfalls which apply to the single buffering mode)
+
+Slow GUI
+~~~~~~~~
+
+Themes handling is quite slow - each theme must be depacked, each one contains
+quite a few XML files to parse and quite a few images to load/convert. That's
+the reason why the built-in one is used as default, it dramatically speeds up
+loading time. A compromise solution is to depack the theme in an equally named
+directory (i.e. avoiding the depacking phase) but you need a filesystem with
+long name support for that to work.
+
+
+Known issues
+------------
+
+- aspect ratio correction works on RGB only (yet)
+
+- SuperVidel's DVI output is stretched when in 320x200 or 640x400; I'll  wait
+  for other people's experiences, maybe only my LCD is so lame.
+
+- adding a game in TOS and loading it in FreeMiNT (and vice versa) generates
+  incompatible paths. Either use only one system or edit scummvm.ini and set
+  there only relative paths (mintlib bug/limitation).
+
+- the talkie version of MI1 needs to be merged from two sources: first generate
+  the DOS version and then additionally also the flac version. Then convert all
+  *.flac files into *.wav and replace monkey.sof (flac) with monster.sou (DOS).
+  And of course, don't forget to set the extra path in Game options to the
+  folder where *.wav files are located! For MI2 just use the DOS version,
+  there are no CD tracks available. :(
+
+
+Future plans
+------------
+
+- aspect ratio correction for VGA/SuperVidel
+
+- unified file paths in scummvm.ini
+
+- 8bpp overlay (and get rid of all that 16bpp handling code)
+
+- profiling :) (see also https://github.com/scummvm/scummvm/pull/2382)
+
+- DSP-based sample mixer
+
+- avoid loading music/speech files (and thus slowing down everything) if muted
+
+- assembly copy routines for screen/chunky surfaces (even with SuperVidel
+  present it is not possible to use the SuperBlitter for every surface)
+
+- cached audio/video streams (i.e. don't load only "output_samples" number of
+  samples but cache, say, 1 second so disk i/o wont be so stressed)
+
+- using LDG or Thorsten Otto's sharedlibs: https://tho-otto.de/sharedlibs.php
+  for game engine plugins to relieve the huge binary size
+
+- reuse modified rects in double/triple buffer in the next frame - that way we
+  wouldn't need to refresh the whole screen in every case
+
+- add support for the TT030; this would be easily possible when I rewrite the
+  renderer with a more flexible resolution switching
+
+- ignore (queue) updateScreen() calls to avoid aggressive drawing / buffer
+  switching from some engines; update every X ms instead
+
+- don't hardcode some of the buffers for cacheing purposes, determine the size
+  based on amount of free RAM
+
+- true audio CD support via MetaDOS API
+
+- OPL2LPT and Retrowave support (if I manage to purchase it somewhere)
+
+Closing words
+—------------
+
+I have opened a pull request with all of my code
+(https://github.com/scummvm/scummvm/pull/4687) so who knows, maybe ScummVM
+2.8.0 for Atari will be already present on the official website. :-)
+
+
+MiKRO / Mystic Bytes, XX.XX.2023
+Kosice / Slovakia
+miro.kropacek at gmail.com
+http://mikro.atari.org
diff --git a/configure b/configure
index 7c263e049e8..d7b180dce8e 100755
--- a/configure
+++ b/configure
@@ -1637,6 +1637,11 @@ kos32)
 	_host_cpu=i686
 	_host_alias=kos32
 	;;
+m68k-atari-mint)
+	_host_os=mint
+	_host_cpu=m68k
+	_host_alias=m68k-atari-mint
+	;;
 maemo)
 	_host_os=maemo
 	_host_cpu=arm
@@ -3553,6 +3558,17 @@ if test -n "$_host"; then
 			_port_mk="backends/platform/sdl/kolibrios/kolibrios.mk"
 			;;
 		m68k-atari-mint)
+			# auto -> yes
+			if test "$_debug_build" = "no"; then
+				# --disable-debug 
+				append_var LDFLAGS "-s"
+			fi
+
+			# auto -> no
+			if test "$_optimizations" = "yes"; then
+				# --enable-release, --enable-optimizations
+				append_var CXXFLAGS "-fomit-frame-pointer"
+			fi
 			_seq_midi=no
 			_timidity=no
 			;;
@@ -3841,6 +3857,16 @@ case $_backend in
 			_sdlnet=no
 		fi
 		;;
+	atari)
+		append_var DEFINES "-DATARI"
+		#append_var DEFINES "-DDISABLE_FANCY_THEMES"
+		#append_var DEFINES "-DDISABLE_SID"
+		#append_var DEFINES "-DDISABLE_NES_APU"
+		#append_var DEFINES "-DDISABLE_DOSBOX_OPL"
+		append_var ASFLAGS "-m68030"
+		append_var CXXFLAGS "-m68020-60"
+		append_var LDFLAGS "-m68020-60"
+		;;
 	dc)
 		append_var INCLUDES '-I$(srcdir)/backends/platform/dc'
 		append_var INCLUDES "-isystem $RONINDIR/include"


Commit: 276cf354bf01b69e3bac272ca4d3c113fdc38d58
    https://github.com/scummvm/scummvm/commit/276cf354bf01b69e3bac272ca4d3c113fdc38d58
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
BACKENDS: ATARI: Separate AtariEventSource from OSystem_Atari

Changed paths:
  A backends/events/atari/atari-events.cpp
  A backends/events/atari/atari-events.h
  A backends/platform/atari/osystem_atari.cpp
  A backends/platform/atari/osystem_atari.h
  R backends/platform/atari/atari.cpp
    backends/module.mk
    backends/platform/atari/module.mk


diff --git a/backends/events/atari/atari-events.cpp b/backends/events/atari/atari-events.cpp
new file mode 100644
index 00000000000..65f8f773c32
--- /dev/null
+++ b/backends/events/atari/atari-events.cpp
@@ -0,0 +1,295 @@
+/* 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/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
+
+#include "backends/events/atari/atari-events.h"
+
+#include <mint/osbind.h>
+
+#include "backends/graphics/atari/atari-graphics.h"
+#include "backends/platform/atari/osystem_atari.h"
+#include "common/rect.h"
+
+//! bit 0: rmb
+//! bit 1: lmb
+volatile uint8	g_atari_ikbd_mouse_buttons_state = 0;
+volatile int16	g_atari_ikbd_mouse_delta_x = 0;
+volatile int16	g_atari_ikbd_mouse_delta_y = 0;
+
+#define SCANCODES_SIZE 256
+volatile uint8	g_atari_ikbd_scancodes[SCANCODES_SIZE];
+uint16			g_atari_ikbd_scancodes_mask = SCANCODES_SIZE-1;
+volatile uint16	g_atari_ikbb_scancodes_head = 0;
+static uint16	g_atari_ikbb_scancodes_tail = 0;
+
+
+AtariEventSource::AtariEventSource() {
+	_system = dynamic_cast<OSystem_Atari*>(g_system);
+	assert(_system != nullptr);
+
+	_KEYTAB *pKeyTables = (_KEYTAB*)Keytbl(KT_NOCHANGE, KT_NOCHANGE, KT_NOCHANGE);
+
+	memcpy(_unshiftToAscii, pKeyTables->unshift, 128);
+	memcpy(_shiftToAscii, pKeyTables->shift, 128);
+	memcpy(_capsToAscii, pKeyTables->caps, 128);
+
+	_scancodeToKeycode[0x01] = Common::KEYCODE_ESCAPE;
+	_scancodeToKeycode[0x0e] = Common::KEYCODE_BACKSPACE;
+	_scancodeToKeycode[0x0f] = Common::KEYCODE_TAB;
+	_scancodeToKeycode[0x1c] = Common::KEYCODE_RETURN;
+	_scancodeToKeycode[0x39] = Common::KEYCODE_SPACE;
+	_scancodeToKeycode[0x3b] = Common::KEYCODE_F1;
+	_scancodeToKeycode[0x3c] = Common::KEYCODE_F2;
+	_scancodeToKeycode[0x3d] = Common::KEYCODE_F3;
+	_scancodeToKeycode[0x3e] = Common::KEYCODE_F4;
+	_scancodeToKeycode[0x3f] = Common::KEYCODE_F5;
+	_scancodeToKeycode[0x40] = Common::KEYCODE_F6;
+	_scancodeToKeycode[0x41] = Common::KEYCODE_F7;
+	_scancodeToKeycode[0x42] = Common::KEYCODE_F8;
+	_scancodeToKeycode[0x43] = Common::KEYCODE_F9;
+	_scancodeToKeycode[0x44] = Common::KEYCODE_F10;
+	_scancodeToKeycode[0x45] = Common::KEYCODE_PAGEUP;	// Eiffel only
+	_scancodeToKeycode[0x46] = Common::KEYCODE_PAGEDOWN;	// Eiffel only
+	_scancodeToKeycode[0x47] = Common::KEYCODE_HOME;
+	_scancodeToKeycode[0x48] = Common::KEYCODE_UP;
+	_scancodeToKeycode[0x4a] = Common::KEYCODE_KP_MINUS;
+	_scancodeToKeycode[0x4b] = Common::KEYCODE_LEFT;
+	_scancodeToKeycode[0x4c] = Common::KEYCODE_LMETA;
+	_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[0x55] = Common::KEYCODE_END;	// Eiffel only
+	_scancodeToKeycode[0x5b] = Common::KEYCODE_TILDE;	// Eiffel only
+	_scancodeToKeycode[0x61] = Common::KEYCODE_F12;	// UNDO
+	_scancodeToKeycode[0x62] = Common::KEYCODE_F11;	// HELP
+	_scancodeToKeycode[0x63] = Common::KEYCODE_SLASH;	// KEYPAD /
+	_scancodeToKeycode[0x64] = Common::KEYCODE_KP_DIVIDE;
+	_scancodeToKeycode[0x65] = Common::KEYCODE_KP_MULTIPLY;
+	_scancodeToKeycode[0x66] = Common::KEYCODE_KP_MULTIPLY;	// duplicate?
+	_scancodeToKeycode[0x67] = Common::KEYCODE_7;	// KEYPAD 7
+	_scancodeToKeycode[0x68] = Common::KEYCODE_8;	// KEYPAD 8
+	_scancodeToKeycode[0x69] = Common::KEYCODE_9;	// KEYPAD 9
+	_scancodeToKeycode[0x6a] = Common::KEYCODE_4;	// KEYPAD 4
+	_scancodeToKeycode[0x6b] = Common::KEYCODE_5;	// KEYPAD 5
+	_scancodeToKeycode[0x6c] = Common::KEYCODE_6;	// KEYPAD 6
+	_scancodeToKeycode[0x6d] = Common::KEYCODE_1;	// KEYPAD 1
+	_scancodeToKeycode[0x6e] = Common::KEYCODE_2;	// KEYPAD 2
+	_scancodeToKeycode[0x6f] = Common::KEYCODE_3;	// KEYPAD 3
+	_scancodeToKeycode[0x70] = Common::KEYCODE_0;	// KEYPAD 0
+	_scancodeToKeycode[0x71] = Common::KEYCODE_KP_PERIOD;
+	_scancodeToKeycode[0x72] = Common::KEYCODE_KP_ENTER;
+}
+
+bool AtariEventSource::pollEvent(Common::Event &event) {
+	if (!_graphicsManager)
+		return false;
+
+	static uint32 startMillis = _system->getMillis();
+	static uint32 oldMillis = _system->getMillis();
+	uint32 curMillis = _system->getMillis();
+
+	uint32 diff = curMillis - oldMillis;
+	oldMillis = curMillis;
+
+	if (diff > 0) {
+		static float avgFpsSum;
+		static int avgFpsCount;
+
+		avgFpsSum += 1000.0f / diff;
+		avgFpsCount++;
+
+		if (curMillis - startMillis >= 1000) {
+			float avgFps = avgFpsSum / avgFpsCount;
+			debug("*** Average FPS in 1s: %f ***", avgFps);
+			startMillis = curMillis;
+			avgFpsSum = 0;
+			avgFpsCount = 0;
+		}
+	}
+
+	_system->update();
+
+	if ((g_atari_ikbd_mouse_buttons_state & 0x01) && !_oldRmbDown) {
+		event.type = Common::EVENT_RBUTTONDOWN;
+		event.mouse = _graphicsManager->getMousePosition();
+		_oldRmbDown = true;
+		return true;
+	}
+
+	if (!(g_atari_ikbd_mouse_buttons_state & 0x01) && _oldRmbDown) {
+		event.type = Common::EVENT_RBUTTONUP;
+		event.mouse = _graphicsManager->getMousePosition();
+		_oldRmbDown = false;
+		return true;
+	}
+
+	if ((g_atari_ikbd_mouse_buttons_state & 0x02) && !_oldLmbDown) {
+		event.type = Common::EVENT_LBUTTONDOWN;
+		event.mouse = _graphicsManager->getMousePosition();
+		_oldLmbDown = true;
+		return true;
+	}
+
+	if (!(g_atari_ikbd_mouse_buttons_state & 0x02) && _oldLmbDown) {
+		event.type = Common::EVENT_LBUTTONUP;
+		event.mouse = _graphicsManager->getMousePosition();
+		_oldLmbDown = false;
+		return true;
+	}
+
+	if (g_atari_ikbd_mouse_delta_x != 0 || g_atari_ikbd_mouse_delta_y != 0) {
+		const int deltaX = g_atari_ikbd_mouse_delta_x;
+		const int deltaY = g_atari_ikbd_mouse_delta_y;
+
+		g_atari_ikbd_mouse_delta_x = g_atari_ikbd_mouse_delta_y = 0;
+
+		_graphicsManager->updateMousePosition(deltaX, deltaY);
+
+		event.type = Common::EVENT_MOUSEMOVE;
+		event.mouse = _graphicsManager->getMousePosition();
+		event.relMouse = Common::Point(deltaX, deltaY);
+		return true;
+	}
+
+	if (g_atari_ikbb_scancodes_head != g_atari_ikbb_scancodes_tail) {
+		byte scancode = g_atari_ikbd_scancodes[g_atari_ikbb_scancodes_tail++];
+		g_atari_ikbb_scancodes_tail &= SCANCODES_SIZE-1;
+
+		bool pressed = !(scancode & 0x80);
+		scancode &= 0x7f;
+
+		if (scancode == 0x1d)
+			_ctrlActive = pressed;
+
+		if (scancode == 0x2a)
+			_lshiftActive = pressed;
+
+		if (scancode == 0x36)
+			_rshiftActive = pressed;
+
+		if (scancode == 0x38)
+			_altActive = pressed;
+
+		if (scancode == 0x3a && pressed)
+			_capslockActive = !_capslockActive;
+
+		// Eiffel only
+		if (scancode == 0x59) {
+			event.type = Common::EVENT_WHEELUP;
+			event.mouse = _graphicsManager->getMousePosition();
+			return true;
+		}
+
+		// Eiffel only
+		if (scancode == 0x5a) {
+			event.type = Common::EVENT_WHEELDOWN;
+			event.mouse = _graphicsManager->getMousePosition();
+			return true;
+		}
+
+		uint16 ascii;
+		if (_lshiftActive || _rshiftActive) {
+			ascii = _shiftToAscii[scancode];
+		} else if (_capslockActive) {
+			ascii = _capsToAscii[scancode];
+		} else {
+			ascii = _unshiftToAscii[scancode];
+		}
+
+		Common::KeyCode keycode = _scancodeToKeycode.getValOrDefault(scancode, Common::KEYCODE_INVALID);
+		switch (keycode) {
+		case Common::KEYCODE_BACKSPACE:
+			ascii = Common::ASCII_BACKSPACE;
+			break;
+		case Common::KEYCODE_TAB:
+			ascii = Common::ASCII_TAB;
+			break;
+		case Common::KEYCODE_RETURN:
+		case Common::KEYCODE_KP_ENTER:
+			ascii = Common::ASCII_RETURN;
+			break;
+		case Common::KEYCODE_ESCAPE:
+			ascii = Common::ASCII_ESCAPE;
+			break;
+		case Common::KEYCODE_SPACE:
+			ascii = Common::ASCII_SPACE;
+			break;
+		case Common::KEYCODE_F1:
+			ascii = Common::ASCII_F1;
+			break;
+		case Common::KEYCODE_F2:
+			ascii = Common::ASCII_F2;
+			break;
+		case Common::KEYCODE_F3:
+			ascii = Common::ASCII_F3;
+			break;
+		case Common::KEYCODE_F4:
+			ascii = Common::ASCII_F4;
+			break;
+		case Common::KEYCODE_F5:
+			ascii = Common::ASCII_F5;
+			break;
+		case Common::KEYCODE_F6:
+			ascii = Common::ASCII_F6;
+			break;
+		case Common::KEYCODE_F7:
+			ascii = Common::ASCII_F7;
+			break;
+		case Common::KEYCODE_F8:
+			ascii = Common::ASCII_F8;
+			break;
+		case Common::KEYCODE_F9:
+			ascii = Common::ASCII_F9;
+			break;
+		case Common::KEYCODE_F10:
+			ascii = Common::ASCII_F10;
+			break;
+		case Common::KEYCODE_F11:
+			ascii = Common::ASCII_F11;
+			break;
+		case Common::KEYCODE_F12:
+			ascii = Common::ASCII_F12;
+			break;
+		default:
+			break;
+		}
+
+		if (ascii >= ' ' && ascii <= '~') {
+			if (keycode == Common::KEYCODE_INVALID)
+				keycode = _asciiToKeycode[ascii - ' '];
+		}
+
+		event.type = pressed ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
+		event.kbd = Common::KeyState(keycode, ascii);
+		event.kbd.flags |= _ctrlActive ? Common::KBD_CTRL : 0;
+		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;
+
+		return true;
+	}
+
+	return false;
+}
diff --git a/backends/events/atari/atari-events.h b/backends/events/atari/atari-events.h
new file mode 100644
index 00000000000..c98b6e307cf
--- /dev/null
+++ b/backends/events/atari/atari-events.h
@@ -0,0 +1,160 @@
+/* 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 BACKEND_EVENTS_ATARI_H
+#define BACKEND_EVENTS_ATARI_H
+
+#include "common/events.h"
+
+#include "common/hashmap.h"
+
+class AtariGraphicsManager;
+class OSystem_Atari;
+
+/**
+ * The Atari event source.
+ */
+class AtariEventSource : public Common::EventSource {
+public:
+	AtariEventSource();
+
+	bool pollEvent(Common::Event &event) override;
+
+	void setGraphicsManager(AtariGraphicsManager *graphicsManager) { _graphicsManager = graphicsManager; }
+
+private:
+	OSystem_Atari *_system = nullptr;
+	AtariGraphicsManager *_graphicsManager = nullptr;
+
+	bool _oldLmbDown = false;
+	bool _oldRmbDown = false;
+
+	bool _lshiftActive = false;
+	bool _rshiftActive = false;
+	bool _ctrlActive = false;
+	bool _altActive = false;
+	bool _capslockActive = false;
+
+	byte _unshiftToAscii[128];
+	byte _shiftToAscii[128];
+	byte _capsToAscii[128];
+
+	const Common::KeyCode _asciiToKeycode[128 - 32 - 1] = {
+		Common::KEYCODE_SPACE,
+		Common::KEYCODE_EXCLAIM,
+		Common::KEYCODE_QUOTEDBL,
+		Common::KEYCODE_HASH,
+		Common::KEYCODE_DOLLAR,
+		Common::KEYCODE_PERCENT,
+		Common::KEYCODE_AMPERSAND,
+		Common::KEYCODE_QUOTE,
+		Common::KEYCODE_LEFTPAREN,
+		Common::KEYCODE_RIGHTPAREN,
+		Common::KEYCODE_ASTERISK,
+		Common::KEYCODE_PLUS,
+		Common::KEYCODE_COMMA,
+		Common::KEYCODE_MINUS,
+		Common::KEYCODE_PERIOD,
+		Common::KEYCODE_SLASH,
+		Common::KEYCODE_0,
+		Common::KEYCODE_1,
+		Common::KEYCODE_2,
+		Common::KEYCODE_3,
+		Common::KEYCODE_4,
+		Common::KEYCODE_5,
+		Common::KEYCODE_6,
+		Common::KEYCODE_7,
+		Common::KEYCODE_8,
+		Common::KEYCODE_9,
+		Common::KEYCODE_COLON,
+		Common::KEYCODE_SEMICOLON,
+		Common::KEYCODE_LESS,
+		Common::KEYCODE_EQUALS,
+		Common::KEYCODE_GREATER,
+		Common::KEYCODE_QUESTION,
+		Common::KEYCODE_AT,
+		Common::KEYCODE_a,
+		Common::KEYCODE_b,
+		Common::KEYCODE_c,
+		Common::KEYCODE_d,
+		Common::KEYCODE_e,
+		Common::KEYCODE_f,
+		Common::KEYCODE_g,
+		Common::KEYCODE_h,
+		Common::KEYCODE_i,
+		Common::KEYCODE_j,
+		Common::KEYCODE_k,
+		Common::KEYCODE_l,
+		Common::KEYCODE_m,
+		Common::KEYCODE_n,
+		Common::KEYCODE_o,
+		Common::KEYCODE_p,
+		Common::KEYCODE_q,
+		Common::KEYCODE_r,
+		Common::KEYCODE_s,
+		Common::KEYCODE_t,
+		Common::KEYCODE_u,
+		Common::KEYCODE_v,
+		Common::KEYCODE_w,
+		Common::KEYCODE_x,
+		Common::KEYCODE_y,
+		Common::KEYCODE_z,
+		Common::KEYCODE_LEFTBRACKET,
+		Common::KEYCODE_BACKSLASH,
+		Common::KEYCODE_RIGHTBRACKET,
+		Common::KEYCODE_CARET,
+		Common::KEYCODE_UNDERSCORE,
+		Common::KEYCODE_BACKQUOTE,
+		Common::KEYCODE_a,
+		Common::KEYCODE_b,
+		Common::KEYCODE_c,
+		Common::KEYCODE_d,
+		Common::KEYCODE_e,
+		Common::KEYCODE_f,
+		Common::KEYCODE_g,
+		Common::KEYCODE_h,
+		Common::KEYCODE_i,
+		Common::KEYCODE_j,
+		Common::KEYCODE_k,
+		Common::KEYCODE_l,
+		Common::KEYCODE_m,
+		Common::KEYCODE_n,
+		Common::KEYCODE_o,
+		Common::KEYCODE_p,
+		Common::KEYCODE_q,
+		Common::KEYCODE_r,
+		Common::KEYCODE_s,
+		Common::KEYCODE_t,
+		Common::KEYCODE_u,
+		Common::KEYCODE_v,
+		Common::KEYCODE_w,
+		Common::KEYCODE_x,
+		Common::KEYCODE_y,
+		Common::KEYCODE_z,
+		Common::KEYCODE_INVALID,	// {
+		Common::KEYCODE_INVALID,	// |
+		Common::KEYCODE_INVALID,	// }
+		Common::KEYCODE_TILDE
+	};
+	Common::HashMap<byte, Common::KeyCode> _scancodeToKeycode;
+};
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 7b99c4a6103..9cbb3f47a46 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -323,6 +323,7 @@ endif
 
 ifeq ($(BACKEND),atari)
 MODULE_OBJS += \
+	events/atari/atari-events.o \
 	graphics/atari/atari_c2p-asm.o \
 	graphics/atari/atari-graphics.o \
 	graphics/atari/atari-graphics-asm.o \
diff --git a/backends/platform/atari/atari.cpp b/backends/platform/atari/atari.cpp
deleted file mode 100644
index ec3a0337f9a..00000000000
--- a/backends/platform/atari/atari.cpp
+++ /dev/null
@@ -1,660 +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 <sys/time.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <mint/cookie.h>
-#include <mint/falcon.h>
-#include <mint/osbind.h>
-
-// We use some stdio.h functionality here thus we need to allow some
-// symbols. Alternatively, we could simply allow everything by defining
-// FORBIDDEN_SYMBOL_ALLOW_ALL
-#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
-#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
-#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
-#define FORBIDDEN_SYMBOL_EXCEPTION_fputs
-#define FORBIDDEN_SYMBOL_EXCEPTION_exit
-#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
-#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
-
-#include "common/scummsys.h"
-
-#if defined(ATARI)
-#include "backends/graphics/atari/atari-graphics-asm.h"
-#include "backends/keymapper/hardware-input.h"
-#include "backends/modular-backend.h"
-#include "backends/mutex/null/null-mutex.h"
-#include "base/main.h"
-
-#include "backends/saves/default/default-saves.h"
-#include "backends/timer/default/default-timer.h"
-#include "backends/events/default/default-events.h"
-#include "backends/mixer/atari/atari-mixer.h"
-#include "backends/graphics/atari/atari-graphics.h"
-#include "backends/graphics/atari/atari-graphics-supervidel.h"
-#include "backends/graphics/atari/atari-graphics-videl.h"
-#include "common/hashmap.h"
-#include "gui/debugger.h"
-
-/*
- * Include header files needed for the getFilesystemFactory() method.
- */
-#include "backends/fs/posix/posix-fs-factory.h"
-
-class OSystem_Atari : public ModularMixerBackend, public ModularGraphicsBackend, Common::EventSource {
-public:
-	OSystem_Atari();
-	virtual ~OSystem_Atari();
-
-	void initBackend() override;
-
-	bool pollEvent(Common::Event &event) override;
-
-	Common::MutexInternal *createMutex() override;
-	uint32 getMillis(bool skipRecord = false) override;
-	void delayMillis(uint msecs) override;
-	void getTimeAndDate(TimeDate &td, bool skipRecord = false) const override;
-
-	Common::KeymapArray getGlobalKeymaps() override;
-	Common::HardwareInputSet *getHardwareInputSet() override;
-
-	void quit() override;
-
-	void logMessage(LogMessageType::Type type, const char *message) override;
-
-	void addSysArchivesToSearchSet(Common::SearchSet &s, int priority) override;
-	Common::String getDefaultConfigFileName() override;
-
-private:
-	AtariGraphicsManager *_atariGraphicsManager;
-
-	clock_t _startTime;
-
-	bool _video_initialized = false;
-	bool _ikbd_initialized = false;
-
-	bool _oldLmbDown = false;
-	bool _oldRmbDown = false;
-
-	bool _lshiftActive = false;
-	bool _rshiftActive = false;
-	bool _ctrlActive = false;
-	bool _altActive = false;
-	bool _capslockActive = false;
-
-	byte _unshiftToAscii[128];
-	byte _shiftToAscii[128];
-	byte _capsToAscii[128];
-
-	const Common::KeyCode _asciiToKeycode[128 - 32 - 1] = {
-		Common::KEYCODE_SPACE,
-		Common::KEYCODE_EXCLAIM,
-		Common::KEYCODE_QUOTEDBL,
-		Common::KEYCODE_HASH,
-		Common::KEYCODE_DOLLAR,
-		Common::KEYCODE_PERCENT,
-		Common::KEYCODE_AMPERSAND,
-		Common::KEYCODE_QUOTE,
-		Common::KEYCODE_LEFTPAREN,
-		Common::KEYCODE_RIGHTPAREN,
-		Common::KEYCODE_ASTERISK,
-		Common::KEYCODE_PLUS,
-		Common::KEYCODE_COMMA,
-		Common::KEYCODE_MINUS,
-		Common::KEYCODE_PERIOD,
-		Common::KEYCODE_SLASH,
-		Common::KEYCODE_0,
-		Common::KEYCODE_1,
-		Common::KEYCODE_2,
-		Common::KEYCODE_3,
-		Common::KEYCODE_4,
-		Common::KEYCODE_5,
-		Common::KEYCODE_6,
-		Common::KEYCODE_7,
-		Common::KEYCODE_8,
-		Common::KEYCODE_9,
-		Common::KEYCODE_COLON,
-		Common::KEYCODE_SEMICOLON,
-		Common::KEYCODE_LESS,
-		Common::KEYCODE_EQUALS,
-		Common::KEYCODE_GREATER,
-		Common::KEYCODE_QUESTION,
-		Common::KEYCODE_AT,
-		Common::KEYCODE_a,
-		Common::KEYCODE_b,
-		Common::KEYCODE_c,
-		Common::KEYCODE_d,
-		Common::KEYCODE_e,
-		Common::KEYCODE_f,
-		Common::KEYCODE_g,
-		Common::KEYCODE_h,
-		Common::KEYCODE_i,
-		Common::KEYCODE_j,
-		Common::KEYCODE_k,
-		Common::KEYCODE_l,
-		Common::KEYCODE_m,
-		Common::KEYCODE_n,
-		Common::KEYCODE_o,
-		Common::KEYCODE_p,
-		Common::KEYCODE_q,
-		Common::KEYCODE_r,
-		Common::KEYCODE_s,
-		Common::KEYCODE_t,
-		Common::KEYCODE_u,
-		Common::KEYCODE_v,
-		Common::KEYCODE_w,
-		Common::KEYCODE_x,
-		Common::KEYCODE_y,
-		Common::KEYCODE_z,
-		Common::KEYCODE_LEFTBRACKET,
-		Common::KEYCODE_BACKSLASH,
-		Common::KEYCODE_RIGHTBRACKET,
-		Common::KEYCODE_CARET,
-		Common::KEYCODE_UNDERSCORE,
-		Common::KEYCODE_BACKQUOTE,
-		Common::KEYCODE_a,
-		Common::KEYCODE_b,
-		Common::KEYCODE_c,
-		Common::KEYCODE_d,
-		Common::KEYCODE_e,
-		Common::KEYCODE_f,
-		Common::KEYCODE_g,
-		Common::KEYCODE_h,
-		Common::KEYCODE_i,
-		Common::KEYCODE_j,
-		Common::KEYCODE_k,
-		Common::KEYCODE_l,
-		Common::KEYCODE_m,
-		Common::KEYCODE_n,
-		Common::KEYCODE_o,
-		Common::KEYCODE_p,
-		Common::KEYCODE_q,
-		Common::KEYCODE_r,
-		Common::KEYCODE_s,
-		Common::KEYCODE_t,
-		Common::KEYCODE_u,
-		Common::KEYCODE_v,
-		Common::KEYCODE_w,
-		Common::KEYCODE_x,
-		Common::KEYCODE_y,
-		Common::KEYCODE_z,
-		Common::KEYCODE_INVALID,	// {
-		Common::KEYCODE_INVALID,	// |
-		Common::KEYCODE_INVALID,	// }
-		Common::KEYCODE_TILDE
-	};
-	Common::HashMap<byte, Common::KeyCode> _scancodeToKeycode;
-};
-
-extern "C" void atari_ikbd_init();
-extern "C" void atari_ikbd_shutdown();
-
-extern void nf_init(void);
-extern void nf_print(const char* msg);
-
-OSystem_Atari::OSystem_Atari() {
-	_fsFactory = new POSIXFilesystemFactory();
-
-	_scancodeToKeycode[0x01] = Common::KEYCODE_ESCAPE;
-	_scancodeToKeycode[0x0e] = Common::KEYCODE_BACKSPACE;
-	_scancodeToKeycode[0x0f] = Common::KEYCODE_TAB;
-	_scancodeToKeycode[0x1c] = Common::KEYCODE_RETURN;
-	_scancodeToKeycode[0x39] = Common::KEYCODE_SPACE;
-	_scancodeToKeycode[0x3b] = Common::KEYCODE_F1;
-	_scancodeToKeycode[0x3c] = Common::KEYCODE_F2;
-	_scancodeToKeycode[0x3d] = Common::KEYCODE_F3;
-	_scancodeToKeycode[0x3e] = Common::KEYCODE_F4;
-	_scancodeToKeycode[0x3f] = Common::KEYCODE_F5;
-	_scancodeToKeycode[0x40] = Common::KEYCODE_F6;
-	_scancodeToKeycode[0x41] = Common::KEYCODE_F7;
-	_scancodeToKeycode[0x42] = Common::KEYCODE_F8;
-	_scancodeToKeycode[0x43] = Common::KEYCODE_F9;
-	_scancodeToKeycode[0x44] = Common::KEYCODE_F10;
-	_scancodeToKeycode[0x45] = Common::KEYCODE_PAGEUP;	// Eiffel only
-	_scancodeToKeycode[0x46] = Common::KEYCODE_PAGEDOWN;	// Eiffel only
-	_scancodeToKeycode[0x47] = Common::KEYCODE_HOME;
-	_scancodeToKeycode[0x48] = Common::KEYCODE_UP;
-	_scancodeToKeycode[0x4a] = Common::KEYCODE_KP_MINUS;
-	_scancodeToKeycode[0x4b] = Common::KEYCODE_LEFT;
-	_scancodeToKeycode[0x4c] = Common::KEYCODE_LMETA;
-	_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[0x55] = Common::KEYCODE_END;	// Eiffel only
-	_scancodeToKeycode[0x5b] = Common::KEYCODE_TILDE;	// Eiffel only
-	_scancodeToKeycode[0x61] = Common::KEYCODE_F12;	// UNDO
-	_scancodeToKeycode[0x62] = Common::KEYCODE_F11;	// HELP
-	_scancodeToKeycode[0x63] = Common::KEYCODE_SLASH;	// KEYPAD /
-	_scancodeToKeycode[0x64] = Common::KEYCODE_KP_DIVIDE;
-	_scancodeToKeycode[0x65] = Common::KEYCODE_KP_MULTIPLY;
-	_scancodeToKeycode[0x66] = Common::KEYCODE_KP_MULTIPLY;	// duplicate?
-	_scancodeToKeycode[0x67] = Common::KEYCODE_7;	// KEYPAD 7
-	_scancodeToKeycode[0x68] = Common::KEYCODE_8;	// KEYPAD 8
-	_scancodeToKeycode[0x69] = Common::KEYCODE_9;	// KEYPAD 9
-	_scancodeToKeycode[0x6a] = Common::KEYCODE_4;	// KEYPAD 4
-	_scancodeToKeycode[0x6b] = Common::KEYCODE_5;	// KEYPAD 5
-	_scancodeToKeycode[0x6c] = Common::KEYCODE_6;	// KEYPAD 6
-	_scancodeToKeycode[0x6d] = Common::KEYCODE_1;	// KEYPAD 1
-	_scancodeToKeycode[0x6e] = Common::KEYCODE_2;	// KEYPAD 2
-	_scancodeToKeycode[0x6f] = Common::KEYCODE_3;	// KEYPAD 3
-	_scancodeToKeycode[0x70] = Common::KEYCODE_0;	// KEYPAD 0
-	_scancodeToKeycode[0x71] = Common::KEYCODE_KP_PERIOD;
-	_scancodeToKeycode[0x72] = Common::KEYCODE_KP_ENTER;
-}
-
-OSystem_Atari::~OSystem_Atari() {
-	debug("OSystem_Atari::~OSystem_Atari()");
-
-	if (_video_initialized) {
-		Supexec(asm_screen_falcon_restore);
-		_video_initialized = false;
-	}
-
-	if (_ikbd_initialized) {
-		Supexec(atari_ikbd_shutdown);
-		_ikbd_initialized = false;
-	}
-}
-
-static void ikbd_and_video_restore() {
-	Supexec(asm_screen_falcon_restore);
-	Supexec(atari_ikbd_shutdown);
-}
-
-void OSystem_Atari::initBackend() {
-	enum {
-		MCH_ST = 0,
-		MCH_STE,
-		MCH_TT,
-		MCH_FALCON,
-		MCH_CLONE,
-		MCH_ARANYM
-	};
-
-	long mch = MCH_ST<<16;
-	Getcookie(C__MCH, &mch);
-	mch >>= 16;
-
-	if (mch != MCH_FALCON && mch != MCH_ARANYM) {
-		error("ScummVM works only on Atari Falcon and ARAnyM");
-	}
-
-	if (mch == MCH_ARANYM && Getcookie(C_fVDI, NULL) == C_FOUND) {
-		logMessage(LogMessageType::kError, "Disable fVDI, ScummVM accesses Videl directly\n");
-		quit();
-	}
-
-	nf_init();
-
-	_startTime = clock();
-
-	bool superVidel = VgetMonitor() == MON_VGA && Getcookie(C_SupV, NULL) == C_FOUND;
-
-	_timerManager = new DefaultTimerManager();
-	_eventManager = new DefaultEventManager(makeKeyboardRepeatingEventSource(this));
-	_savefileManager = new DefaultSaveFileManager();
-	if (superVidel)
-        _atariGraphicsManager = new AtariSuperVidelManager();
-	else
-		_atariGraphicsManager = new AtariVidelManager();
-	_graphicsManager = _atariGraphicsManager;
-	_mixerManager = new AtariMixerManager();
-	// Setup and start mixer
-	_mixerManager->init();
-
-	_KEYTAB *pKeyTables = (_KEYTAB*)Keytbl(KT_NOCHANGE, KT_NOCHANGE, KT_NOCHANGE);
-
-	memcpy(_unshiftToAscii, pKeyTables->unshift, 128);
-	memcpy(_shiftToAscii, pKeyTables->shift, 128);
-	memcpy(_capsToAscii, pKeyTables->caps, 128);
-
-	Supexec(atari_ikbd_init);
-	_ikbd_initialized = true;
-
-	Supexec(asm_screen_falcon_save);
-	_video_initialized = true;
-
-	Setexc(VEC_PROCTERM, ikbd_and_video_restore);
-
-	BaseBackend::initBackend();
-}
-
-//! bit 0: rmb
-//! bit 1: lmb
-volatile uint8	g_atari_ikbd_mouse_buttons_state = 0;
-volatile int16	g_atari_ikbd_mouse_delta_x = 0;
-volatile int16	g_atari_ikbd_mouse_delta_y = 0;
-
-#define SCANCODES_SIZE 256
-volatile uint8	g_atari_ikbd_scancodes[SCANCODES_SIZE];
-uint16			g_atari_ikbd_scancodes_mask = SCANCODES_SIZE-1;
-volatile uint16	g_atari_ikbb_scancodes_head = 0;
-static uint16	g_atari_ikbb_scancodes_tail = 0;
-
-bool OSystem_Atari::pollEvent(Common::Event &event) {
-	{
-		static uint32 startMillis = getMillis();
-		static uint32 oldMillis = getMillis();
-		uint32 curMillis = getMillis();
-
-		uint32 diff = curMillis - oldMillis;
-		oldMillis = curMillis;
-
-		if (diff > 0) {
-			static float avgFpsSum;
-			static int avgFpsCount;
-
-			avgFpsSum += 1000.0f / diff;
-			avgFpsCount++;
-
-			if (curMillis - startMillis >= 1000) {
-				float avgFps = avgFpsSum / avgFpsCount;
-				debug("*** Average FPS in 1s: %f ***", avgFps);
-				startMillis = curMillis;
-				avgFpsSum = 0;
-				avgFpsCount = 0;
-			}
-		}
-	}
-
-	((DefaultTimerManager *)_timerManager)->checkTimers();
-	((AtariMixerManager *)_mixerManager)->update();
-
-	if ((g_atari_ikbd_mouse_buttons_state & 0x01) && !_oldRmbDown) {
-		event.type = Common::EVENT_RBUTTONDOWN;
-		event.mouse = _atariGraphicsManager->getMousePosition();
-		_oldRmbDown = true;
-		return true;
-	}
-
-	if (!(g_atari_ikbd_mouse_buttons_state & 0x01) && _oldRmbDown) {
-		event.type = Common::EVENT_RBUTTONUP;
-		event.mouse = _atariGraphicsManager->getMousePosition();
-		_oldRmbDown = false;
-		return true;
-	}
-
-	if ((g_atari_ikbd_mouse_buttons_state & 0x02) && !_oldLmbDown) {
-		event.type = Common::EVENT_LBUTTONDOWN;
-		event.mouse = _atariGraphicsManager->getMousePosition();
-		_oldLmbDown = true;
-		return true;
-	}
-
-	if (!(g_atari_ikbd_mouse_buttons_state & 0x02) && _oldLmbDown) {
-		event.type = Common::EVENT_LBUTTONUP;
-		event.mouse = _atariGraphicsManager->getMousePosition();
-		_oldLmbDown = false;
-		return true;
-	}
-
-	if (g_atari_ikbd_mouse_delta_x != 0 || g_atari_ikbd_mouse_delta_y != 0) {
-		const int deltaX = g_atari_ikbd_mouse_delta_x;
-		const int deltaY = g_atari_ikbd_mouse_delta_y;
-
-		g_atari_ikbd_mouse_delta_x = g_atari_ikbd_mouse_delta_y = 0;
-
-		_atariGraphicsManager->updateMousePosition(deltaX, deltaY);
-
-		event.type = Common::EVENT_MOUSEMOVE;
-		event.mouse = _atariGraphicsManager->getMousePosition();
-		event.relMouse = Common::Point(deltaX, deltaY);
-		return true;
-	}
-
-	if (g_atari_ikbb_scancodes_head != g_atari_ikbb_scancodes_tail) {
-		byte scancode = g_atari_ikbd_scancodes[g_atari_ikbb_scancodes_tail++];
-		g_atari_ikbb_scancodes_tail &= SCANCODES_SIZE-1;
-
-		bool pressed = !(scancode & 0x80);
-		scancode &= 0x7f;
-
-		if (scancode == 0x1d)
-			_ctrlActive = pressed;
-
-		if (scancode == 0x2a)
-			_lshiftActive = pressed;
-
-		if (scancode == 0x36)
-			_rshiftActive = pressed;
-
-		if (scancode == 0x38)
-			_altActive = pressed;
-
-		if (scancode == 0x3a && pressed)
-			_capslockActive = !_capslockActive;
-
-		// Eiffel only
-		if (scancode == 0x59) {
-			event.type = Common::EVENT_WHEELUP;
-			event.mouse = _atariGraphicsManager->getMousePosition();
-			return true;
-		}
-
-		// Eiffel only
-		if (scancode == 0x5a) {
-			event.type = Common::EVENT_WHEELDOWN;
-			event.mouse = _atariGraphicsManager->getMousePosition();
-			return true;
-		}
-
-		uint16 ascii;
-		if (_lshiftActive || _rshiftActive) {
-			ascii = _shiftToAscii[scancode];
-		} else if (_capslockActive) {
-			ascii = _capsToAscii[scancode];
-		} else {
-			ascii = _unshiftToAscii[scancode];
-		}
-
-		Common::KeyCode keycode = _scancodeToKeycode.getValOrDefault(scancode, Common::KEYCODE_INVALID);
-		switch (keycode) {
-		case Common::KEYCODE_BACKSPACE:
-			ascii = Common::ASCII_BACKSPACE;
-			break;
-		case Common::KEYCODE_TAB:
-			ascii = Common::ASCII_TAB;
-			break;
-		case Common::KEYCODE_RETURN:
-		case Common::KEYCODE_KP_ENTER:
-			ascii = Common::ASCII_RETURN;
-			break;
-		case Common::KEYCODE_ESCAPE:
-			ascii = Common::ASCII_ESCAPE;
-			break;
-		case Common::KEYCODE_SPACE:
-			ascii = Common::ASCII_SPACE;
-			break;
-		case Common::KEYCODE_F1:
-			ascii = Common::ASCII_F1;
-			break;
-		case Common::KEYCODE_F2:
-			ascii = Common::ASCII_F2;
-			break;
-		case Common::KEYCODE_F3:
-			ascii = Common::ASCII_F3;
-			break;
-		case Common::KEYCODE_F4:
-			ascii = Common::ASCII_F4;
-			break;
-		case Common::KEYCODE_F5:
-			ascii = Common::ASCII_F5;
-			break;
-		case Common::KEYCODE_F6:
-			ascii = Common::ASCII_F6;
-			break;
-		case Common::KEYCODE_F7:
-			ascii = Common::ASCII_F7;
-			break;
-		case Common::KEYCODE_F8:
-			ascii = Common::ASCII_F8;
-			break;
-		case Common::KEYCODE_F9:
-			ascii = Common::ASCII_F9;
-			break;
-		case Common::KEYCODE_F10:
-			ascii = Common::ASCII_F10;
-			break;
-		case Common::KEYCODE_F11:
-			ascii = Common::ASCII_F11;
-			break;
-		case Common::KEYCODE_F12:
-			ascii = Common::ASCII_F12;
-			break;
-		default:
-			break;
-		}
-
-		if (ascii >= ' ' && ascii <= '~') {
-			if (keycode == Common::KEYCODE_INVALID)
-				keycode = _asciiToKeycode[ascii - ' '];
-		}
-
-		event.type = pressed ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
-		event.kbd = Common::KeyState(keycode, ascii);
-		event.kbd.flags |= _ctrlActive ? Common::KBD_CTRL : 0;
-		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;
-
-		return true;
-	}
-
-	return false;
-}
-
-Common::MutexInternal *OSystem_Atari::createMutex() {
-	return new NullMutexInternal();
-}
-
-uint32 OSystem_Atari::getMillis(bool skipRecord) {
-	// CLOCKS_PER_SEC is 200, so no need to use floats
-	return 1000 * (clock() - _startTime) / CLOCKS_PER_SEC;
-}
-
-void OSystem_Atari::delayMillis(uint msecs) {
-	usleep(msecs * 1000);
-}
-
-void OSystem_Atari::getTimeAndDate(TimeDate &td, bool skipRecord) const {
-	debug("getTimeAndDate");
-	time_t curTime = time(0);
-	// TODO: if too slow (e.g. when calling RandomSource::RandomSource()), rewrite
-	struct tm t = *localtime(&curTime);
-	td.tm_sec = t.tm_sec;
-	td.tm_min = t.tm_min;
-	td.tm_hour = t.tm_hour;
-	td.tm_mday = t.tm_mday;
-	td.tm_mon = t.tm_mon;
-	td.tm_year = t.tm_year;
-	td.tm_wday = t.tm_wday;
-}
-
-Common::KeymapArray OSystem_Atari::getGlobalKeymaps() {
-	Common::KeymapArray globalMaps = BaseBackend::getGlobalKeymaps();
-
-	Common::Keymap *keymap = _atariGraphicsManager->getKeymap();
-	globalMaps.push_back(keymap);
-
-	return globalMaps;
-}
-
-Common::HardwareInputSet *OSystem_Atari::getHardwareInputSet() {
-	Common::CompositeHardwareInputSet *inputSet = new Common::CompositeHardwareInputSet();
-	inputSet->addHardwareInputSet(new Common::MouseHardwareInputSet(Common::defaultMouseButtons));
-	inputSet->addHardwareInputSet(new Common::KeyboardHardwareInputSet(Common::defaultKeys, Common::defaultModifiers));
-
-	return inputSet;
-}
-
-void OSystem_Atari::quit() {
-	Common::String str = Common::String::format("OSystem_Atari::quit()\n");
-	logMessage(LogMessageType::kDebug, str.c_str());
-
-	g_system->destroy();
-
-	exit(0);
-}
-
-void OSystem_Atari::logMessage(LogMessageType::Type type, const char *message) {
-	FILE *output = 0;
-
-	if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
-		output = stdout;
-	else
-		output = stderr;
-
-	fputs(message, output);
-	fflush(output);
-
-	nf_print(message);
-}
-
-void OSystem_Atari::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
-#ifdef DATA_PATH
-	// Add the global DATA_PATH to the directory search list
-	// See also OSystem_SDL::addSysArchivesToSearchSet()
-	Common::FSNode dataNode(DATA_PATH);
-	if (dataNode.exists() && dataNode.isDirectory()) {
-		s.add(DATA_PATH, new Common::FSDirectory(dataNode, 4), priority);
-	}
-#endif
-}
-
-Common::String OSystem_Atari::getDefaultConfigFileName() {
-	const Common::String baseConfigName = OSystem::getDefaultConfigFileName();
-
-	Common::String configFile;
-
-	const char *envVar = getenv("HOME");
-	if (envVar && *envVar) {
-		configFile = envVar;
-		configFile += '/';
-		configFile += baseConfigName;
-
-		if (configFile.size() < MAXPATHLEN)
-			return configFile;
-	}
-
-	return baseConfigName;
-}
-
-OSystem *OSystem_Atari_create() {
-	return new OSystem_Atari();
-}
-
-int main(int argc, char *argv[]) {
-	g_system = OSystem_Atari_create();
-	assert(g_system);
-
-	// Invoke the actual ScummVM main entry point:
-	int res = scummvm_main(argc, argv);
-	g_system->destroy();
-	return res;
-}
-
-#endif
diff --git a/backends/platform/atari/module.mk b/backends/platform/atari/module.mk
index 16bd53ec57b..d3982a71a11 100644
--- a/backends/platform/atari/module.mk
+++ b/backends/platform/atari/module.mk
@@ -1,7 +1,7 @@
 MODULE := backends/platform/atari
 
 MODULE_OBJS := \
-	atari.o \
+	osystem_atari.o \
 	atari_ikbd.o \
 	native_features.o
 
diff --git a/backends/platform/atari/osystem_atari.cpp b/backends/platform/atari/osystem_atari.cpp
new file mode 100644
index 00000000000..44259335b59
--- /dev/null
+++ b/backends/platform/atari/osystem_atari.cpp
@@ -0,0 +1,265 @@
+/* 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 <stdio.h>
+#include <unistd.h>
+
+#include <mint/cookie.h>
+#include <mint/falcon.h>
+#include <mint/osbind.h>
+
+// We use some stdio.h functionality here thus we need to allow some
+// symbols. Alternatively, we could simply allow everything by defining
+// FORBIDDEN_SYMBOL_ALLOW_ALL
+#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
+#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
+#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
+#define FORBIDDEN_SYMBOL_EXCEPTION_fputs
+#define FORBIDDEN_SYMBOL_EXCEPTION_exit
+#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
+#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
+
+#include "backends/platform/atari/osystem_atari.h"
+
+#if defined(ATARI)
+#include "backends/graphics/atari/atari-graphics-asm.h"
+#include "backends/keymapper/hardware-input.h"
+#include "backends/mutex/null/null-mutex.h"
+#include "base/main.h"
+
+#include "backends/saves/default/default-saves.h"
+#include "backends/timer/default/default-timer.h"
+#include "backends/events/atari/atari-events.h"
+#include "backends/events/default/default-events.h"
+#include "backends/mixer/atari/atari-mixer.h"
+#include "backends/graphics/atari/atari-graphics.h"
+#include "backends/graphics/atari/atari-graphics-supervidel.h"
+#include "backends/graphics/atari/atari-graphics-videl.h"
+#include "gui/debugger.h"
+
+/*
+ * Include header files needed for the getFilesystemFactory() method.
+ */
+#include "backends/fs/posix/posix-fs-factory.h"
+
+extern "C" void atari_ikbd_init();
+extern "C" void atari_ikbd_shutdown();
+
+extern void nf_init(void);
+extern void nf_print(const char* msg);
+
+OSystem_Atari::OSystem_Atari() {
+	_fsFactory = new POSIXFilesystemFactory();
+}
+
+OSystem_Atari::~OSystem_Atari() {
+	debug("OSystem_Atari::~OSystem_Atari()");
+
+	if (_video_initialized) {
+		Supexec(asm_screen_falcon_restore);
+		_video_initialized = false;
+	}
+
+	if (_ikbd_initialized) {
+		Supexec(atari_ikbd_shutdown);
+		_ikbd_initialized = false;
+	}
+}
+
+static void ikbd_and_video_restore() {
+	Supexec(asm_screen_falcon_restore);
+	Supexec(atari_ikbd_shutdown);
+}
+
+void OSystem_Atari::initBackend() {
+	enum {
+		MCH_ST = 0,
+		MCH_STE,
+		MCH_TT,
+		MCH_FALCON,
+		MCH_CLONE,
+		MCH_ARANYM
+	};
+
+	long mch = MCH_ST<<16;
+	Getcookie(C__MCH, &mch);
+	mch >>= 16;
+
+	if (mch != MCH_FALCON && mch != MCH_ARANYM) {
+		error("ScummVM works only on Atari Falcon and ARAnyM");
+	}
+
+	if (mch == MCH_ARANYM && Getcookie(C_fVDI, NULL) == C_FOUND) {
+		error("Disable fVDI, ScummVM accesses Videl directly");
+	}
+
+	nf_init();
+
+	_startTime = clock();
+
+	bool superVidel = VgetMonitor() == MON_VGA && Getcookie(C_SupV, NULL) == C_FOUND;
+
+	_timerManager = new DefaultTimerManager();
+	_savefileManager = new DefaultSaveFileManager();
+
+	AtariEventSource *atariEventSource = new AtariEventSource();
+	_eventManager = new DefaultEventManager(makeKeyboardRepeatingEventSource(atariEventSource));
+
+	// AtariGraphicsManager needs _eventManager ready
+	AtariGraphicsManager *atariGraphicsManager;
+	if (superVidel)
+		atariGraphicsManager = new AtariSuperVidelManager();
+	else
+		atariGraphicsManager = new AtariVidelManager();
+	_graphicsManager = atariGraphicsManager;
+
+	atariEventSource->setGraphicsManager(atariGraphicsManager);
+
+	_mixerManager = new AtariMixerManager();
+	// Setup and start mixer
+	_mixerManager->init();
+
+	Supexec(atari_ikbd_init);
+	_ikbd_initialized = true;
+
+	Supexec(asm_screen_falcon_save);
+	_video_initialized = true;
+
+	Setexc(VEC_PROCTERM, ikbd_and_video_restore);
+
+	BaseBackend::initBackend();
+}
+
+Common::MutexInternal *OSystem_Atari::createMutex() {
+	return new NullMutexInternal();
+}
+
+uint32 OSystem_Atari::getMillis(bool skipRecord) {
+	// CLOCKS_PER_SEC is 200, so no need to use floats
+	return 1000 * (clock() - _startTime) / CLOCKS_PER_SEC;
+}
+
+void OSystem_Atari::delayMillis(uint msecs) {
+	usleep(msecs * 1000);
+}
+
+void OSystem_Atari::getTimeAndDate(TimeDate &td, bool skipRecord) const {
+	debug("getTimeAndDate");
+	time_t curTime = time(0);
+	// TODO: if too slow (e.g. when calling RandomSource::RandomSource()), rewrite
+	struct tm t = *localtime(&curTime);
+	td.tm_sec = t.tm_sec;
+	td.tm_min = t.tm_min;
+	td.tm_hour = t.tm_hour;
+	td.tm_mday = t.tm_mday;
+	td.tm_mon = t.tm_mon;
+	td.tm_year = t.tm_year;
+	td.tm_wday = t.tm_wday;
+}
+
+Common::KeymapArray OSystem_Atari::getGlobalKeymaps() {
+	Common::KeymapArray globalMaps = BaseBackend::getGlobalKeymaps();
+
+	Common::Keymap *keymap = ((AtariGraphicsManager*)_graphicsManager)->getKeymap();
+	globalMaps.push_back(keymap);
+
+	return globalMaps;
+}
+
+Common::HardwareInputSet *OSystem_Atari::getHardwareInputSet() {
+	Common::CompositeHardwareInputSet *inputSet = new Common::CompositeHardwareInputSet();
+	inputSet->addHardwareInputSet(new Common::MouseHardwareInputSet(Common::defaultMouseButtons));
+	inputSet->addHardwareInputSet(new Common::KeyboardHardwareInputSet(Common::defaultKeys, Common::defaultModifiers));
+
+	return inputSet;
+}
+
+void OSystem_Atari::quit() {
+	debug("OSystem_Atari::quit()");
+
+	g_system->destroy();
+
+	exit(0);
+}
+
+void OSystem_Atari::logMessage(LogMessageType::Type type, const char *message) {
+	FILE *output = 0;
+
+	if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
+		output = stdout;
+	else
+		output = stderr;
+
+	fputs(message, output);
+	fflush(output);
+
+	nf_print(message);
+}
+
+void OSystem_Atari::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
+#ifdef DATA_PATH
+	// Add the global DATA_PATH to the directory search list
+	// See also OSystem_SDL::addSysArchivesToSearchSet()
+	Common::FSNode dataNode(DATA_PATH);
+	if (dataNode.exists() && dataNode.isDirectory()) {
+		s.add(DATA_PATH, new Common::FSDirectory(dataNode, 4), priority);
+	}
+#endif
+}
+
+Common::String OSystem_Atari::getDefaultConfigFileName() {
+	const Common::String baseConfigName = OSystem::getDefaultConfigFileName();
+
+	Common::String configFile;
+
+	const char *envVar = getenv("HOME");
+	if (envVar && *envVar) {
+		configFile = envVar;
+		configFile += '/';
+		configFile += baseConfigName;
+
+		if (configFile.size() < MAXPATHLEN)
+			return configFile;
+	}
+
+	return baseConfigName;
+}
+
+void OSystem_Atari::update() {
+	((DefaultTimerManager *)_timerManager)->checkTimers();
+	((AtariMixerManager *)_mixerManager)->update();
+}
+
+OSystem *OSystem_Atari_create() {
+	return new OSystem_Atari();
+}
+
+int main(int argc, char *argv[]) {
+	g_system = OSystem_Atari_create();
+	assert(g_system);
+
+	// Invoke the actual ScummVM main entry point:
+	int res = scummvm_main(argc, argv);
+	g_system->destroy();
+	return res;
+}
+
+#endif
diff --git a/backends/platform/atari/osystem_atari.h b/backends/platform/atari/osystem_atari.h
new file mode 100644
index 00000000000..c27e207135b
--- /dev/null
+++ b/backends/platform/atari/osystem_atari.h
@@ -0,0 +1,60 @@
+/* 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 PLATFORM_ATARI_H
+#define PLATFORM_ATARI_H
+
+#include "backends/modular-backend.h"
+
+#include <time.h>
+
+class OSystem_Atari : public ModularMixerBackend, public ModularGraphicsBackend {
+public:
+	OSystem_Atari();
+	virtual ~OSystem_Atari();
+
+	void initBackend() override;
+
+	Common::MutexInternal *createMutex() override;
+	uint32 getMillis(bool skipRecord = false) override;
+	void delayMillis(uint msecs) override;
+	void getTimeAndDate(TimeDate &td, bool skipRecord = false) const override;
+
+	Common::KeymapArray getGlobalKeymaps() override;
+	Common::HardwareInputSet *getHardwareInputSet() override;
+
+	void quit() override;
+
+	void logMessage(LogMessageType::Type type, const char *message) override;
+
+	void addSysArchivesToSearchSet(Common::SearchSet &s, int priority) override;
+	Common::String getDefaultConfigFileName() override;
+
+	void update();
+
+private:
+	clock_t _startTime;
+
+	bool _video_initialized = false;
+	bool _ikbd_initialized = false;
+};
+
+#endif


Commit: 72f6c4ef612e019fa008cb5a4593c89bb952b62e
    https://github.com/scummvm/scummvm/commit/72f6c4ef612e019fa008cb5a4593c89bb952b62e
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
SCUMM: Increase {min,max}HeapThreshold for ATARI

Changed paths:
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 4d574c206d8..40fa616d415 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1409,6 +1409,7 @@ void ScummEngine::setupScumm(const Common::String &macResourceFile) {
 		_bootParam = -1;
 	}
 
+#ifndef ATARI
 	int maxHeapThreshold = -1;
 
 	if (_game.features & GF_16BIT_COLOR) {
@@ -1423,6 +1424,10 @@ void ScummEngine::setupScumm(const Common::String &macResourceFile) {
 	}
 
 	_res->setHeapThreshold(400000, maxHeapThreshold);
+#else
+	// RAM is cheap, disk I/O isn't... helps with retaining the resources in COMI and similar
+	_res->setHeapThreshold(16 * 1024 * 1024, 32 * 1024 * 1024);
+#endif
 
 	free(_compositeBuf);
 	_compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _outputPixelFormat.bytesPerPixel);


Commit: 896d7cf309f76eda87856d624d3e8afc2515a40f
    https://github.com/scummvm/scummvm/commit/896d7cf309f76eda87856d624d3e8afc2515a40f
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
BACKENDS: ATARI: Use "saves" directory for savegames

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 44259335b59..4c7d504a859 100644
--- a/backends/platform/atari/osystem_atari.cpp
+++ b/backends/platform/atari/osystem_atari.cpp
@@ -118,7 +118,7 @@ void OSystem_Atari::initBackend() {
 	bool superVidel = VgetMonitor() == MON_VGA && Getcookie(C_SupV, NULL) == C_FOUND;
 
 	_timerManager = new DefaultTimerManager();
-	_savefileManager = new DefaultSaveFileManager();
+	_savefileManager = new DefaultSaveFileManager("saves");
 
 	AtariEventSource *atariEventSource = new AtariEventSource();
 	_eventManager = new DefaultEventManager(makeKeyboardRepeatingEventSource(atariEventSource));


Commit: 4892d0b48b6bdf58daced8467331c48172da7553
    https://github.com/scummvm/scummvm/commit/4892d0b48b6bdf58daced8467331c48172da7553
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
CONFIGURE: Adjustments for ATARI

- add NDEBUG for smaller file size and better performance when building
  with --enable-release

- add exe extensions for both ATARI and FreeMiNT

- use "dist-generic" instead of the clunky ./configure paths

Build as:

./configure --backend=atari --host=m68k-atari-mint --enable-release --disable-mt32emu --disable-lua --disable-nuked-opl --disable-16bit --disable-scalers --disable-translation --disable-eventrecorder --disable-tts --disable-bink --opengl-mode=none --enable-verbose-build && make -j 16 && rm -rf dist-generic; make dist-generic

Changed paths:
    .gitignore
    configure
    ports.mk


diff --git a/.gitignore b/.gitignore
index cee8d1665df..b33ad8edfbd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,7 @@ lib*.a
 /*.bnr
 /romfs
 /dist_3ds
+/dist-generic
 /.project
 /.cproject
 /.settings
@@ -231,6 +232,8 @@ ScummVM.config
 ScummVM.creator
 ScummVM.files
 ScummVM.includes
+ScummVM.cflags
+ScummVM.cxxflags
 
 #Ignore Komodo IDE/Edit project files
 *.komodoproject
@@ -288,3 +291,7 @@ android_project
 dists/emscripten/libs/
 dists/emscripten/games/
 dists/emscripten/emsdk-*
+
+#Ignore Atari/FreeMiNT files
+scummvm.gtp
+scummvm.ttp
diff --git a/configure b/configure
index d7b180dce8e..79b718e28b2 100755
--- a/configure
+++ b/configure
@@ -563,6 +563,15 @@ get_system_exe_extension() {
 	mingw* | *os2-emx)
 		_exeext=".exe"
 		;;
+	mint)
+		if test "$_backend" = "atari"; then
+			# ATARI backend
+			_exeext=".ttp"
+		else
+			# SDL backend
+			_exeext=".gtp"
+		fi
+		;;
 	emscripten)
 		_exeext=".html"
 		;;
@@ -793,7 +802,7 @@ Usage: $0 [OPTIONS]...
 
 Configuration:
   -h, --help              display this help and exit
-  --backend=BACKEND       backend to build (3ds, android, dc, dingux, ds, gcw0,
+  --backend=BACKEND       backend to build (3ds, atari, android, dc, dingux, ds, gcw0,
                           gph, iphone, ios7, ios7-arm64, maemo, n64, null, opendingux,
                           openpandora, psp, psp2, samsungtv, sdl, switch, wii) [sdl]
 
@@ -3558,7 +3567,7 @@ if test -n "$_host"; then
 			_port_mk="backends/platform/sdl/kolibrios/kolibrios.mk"
 			;;
 		m68k-atari-mint)
-			# auto -> yes
+			# auto -> yes (overriden by $_release_build = yes)
 			if test "$_debug_build" = "no"; then
 				# --disable-debug 
 				append_var LDFLAGS "-s"
@@ -3569,6 +3578,13 @@ if test -n "$_host"; then
 				# --enable-release, --enable-optimizations
 				append_var CXXFLAGS "-fomit-frame-pointer"
 			fi
+
+			# auto -> no
+			if test "$_release_build" = "yes"; then
+				# --enable-release
+				append_var DEFINES "-DNDEBUG"
+			fi
+			
 			_seq_midi=no
 			_timidity=no
 			;;
diff --git a/ports.mk b/ports.mk
index b6506bddcfd..2d9f69e84d3 100644
--- a/ports.mk
+++ b/ports.mk
@@ -60,6 +60,9 @@ dist-generic: $(EXECUTABLE) $(PLUGINS)
 	mkdir -p ./dist-generic/scummvm/data
 	mkdir -p ./dist-generic/scummvm/doc
 	cp $(EXECUTABLE) ./dist-generic/scummvm
+ifeq ($(BACKEND), atari)
+	m68k-atari-mint-flags -S ./dist-generic/scummvm/$(EXECUTABLE)
+endif
 	cp $(DIST_FILES_DOCS) ./dist-generic/scummvm/doc
 	cp $(DIST_FILES_THEMES) ./dist-generic/scummvm/data
 ifdef DIST_FILES_ENGINEDATA


Commit: 5ba26fdf35e3358dad4bca5c3e5aa7a4e62c202c
    https://github.com/scummvm/scummvm/commit/5ba26fdf35e3358dad4bca5c3e5aa7a4e62c202c
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-03-04T23:38:42+01:00

Commit Message:
GRAPHICS: ATARI: Introduce accelerated blitting

Changed paths:
  A graphics/blit-atari.cpp
    configure
    graphics/blit.cpp
    graphics/module.mk
    graphics/surface.cpp


diff --git a/configure b/configure
index 79b718e28b2..f93a6b31a69 100755
--- a/configure
+++ b/configure
@@ -3874,7 +3874,7 @@ case $_backend in
 		fi
 		;;
 	atari)
-		append_var DEFINES "-DATARI"
+		define_in_config_if_yes yes "ATARI"
 		#append_var DEFINES "-DDISABLE_FANCY_THEMES"
 		#append_var DEFINES "-DDISABLE_SID"
 		#append_var DEFINES "-DDISABLE_NES_APU"
diff --git a/graphics/blit-atari.cpp b/graphics/blit-atari.cpp
new file mode 100644
index 00000000000..aa80e235290
--- /dev/null
+++ b/graphics/blit-atari.cpp
@@ -0,0 +1,100 @@
+/* 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 "graphics/blit.h"
+#include "graphics/surface.h"
+
+#include <cstdlib>	// calloc
+#include <cstring>	// memcpy
+#include <mint/cookie.h>
+#include <mint/falcon.h>
+
+#include "backends/graphics/atari/atari-graphics-superblitter.h"
+
+namespace Graphics {
+
+// hijack surface overrides here as well as these are tightly related
+// to the blitting routine below
+void Surface::create(int16 width, int16 height, const PixelFormat &f) {
+	assert(width >= 0 && height >= 0);
+	free();
+
+	w = width;
+	h = height;
+	format = f;
+	pitch = w * format.bytesPerPixel;
+
+	if (width && height) {
+		if (VgetMonitor() == MON_VGA && Getcookie(C_SupV, NULL) == C_FOUND)
+			pixels = (void*)ct60_vmalloc(width * height * format.bytesPerPixel);
+		else
+			pixels = calloc(width * height, format.bytesPerPixel);
+		assert(pixels);
+	}
+}
+
+void Surface::free() {
+	if (((uintptr)pixels & 0xFF000000) >= 0xA0000000)
+		ct60_vmfree(pixels);
+	else
+		::free(pixels);
+
+	pixels = 0;
+	w = h = pitch = 0;
+	format = PixelFormat();
+}
+
+// Function to blit a rect (version optimized for Atari Falcon with SuperVidel's SuperBlitter)
+void copyBlit(byte *dst, const byte *src,
+			   const uint dstPitch, const uint srcPitch,
+			   const uint w, const uint h,
+			   const uint bytesPerPixel) {
+	if (dst == src)
+		return;
+
+	if (((uintptr)src & 0xFF000000) >= 0xA0000000 && ((uintptr)dst & 0xFF000000) >= 0xA0000000) {
+		// while busy blitting...
+		while (*SV_BLITTER_CONTROL & 1);
+
+		*SV_BLITTER_SRC1           = (long)src;
+		*SV_BLITTER_SRC2           = 0x00000000;
+		*SV_BLITTER_DST            = (long)dst;
+		*SV_BLITTER_COUNT          = w * bytesPerPixel - 1;
+		*SV_BLITTER_SRC1_OFFSET    = srcPitch;
+		*SV_BLITTER_SRC2_OFFSET    = 0x00000000;
+		*SV_BLITTER_DST_OFFSET     = dstPitch;
+		*SV_BLITTER_MASK_AND_LINES = h;
+		*SV_BLITTER_CONTROL        = 0x01;
+
+		// wait until we finish otherwise we may overwrite pixels written manually afterwards
+		while (*SV_BLITTER_CONTROL & 1);
+	} else if (dstPitch == srcPitch && ((w * bytesPerPixel) == dstPitch)) {
+		memcpy(dst, src, dstPitch * h);
+	} else {
+		for (uint i = 0; i < h; ++i) {
+			memcpy(dst, src, w * bytesPerPixel);
+			dst += dstPitch;
+			src += srcPitch;
+		}
+	}
+}
+
+} // End of namespace Graphics
diff --git a/graphics/blit.cpp b/graphics/blit.cpp
index 2cb90a6ff55..cb2d353f825 100644
--- a/graphics/blit.cpp
+++ b/graphics/blit.cpp
@@ -24,6 +24,8 @@
 
 namespace Graphics {
 
+// see graphics/blit-atari.cpp, Atari Falcon's SuperVidel addon allows accelerated blitting
+#ifndef ATARI
 // Function to blit a rect
 void copyBlit(byte *dst, const byte *src,
 			   const uint dstPitch, const uint srcPitch,
@@ -42,6 +44,7 @@ void copyBlit(byte *dst, const byte *src,
 		}
 	}
 }
+#endif
 
 namespace {
 
diff --git a/graphics/module.mk b/graphics/module.mk
index 59e1ed4f08b..33ddb8527e7 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -63,6 +63,11 @@ MODULE_OBJS += \
 	scaler/downscalerARM.o
 endif
 
+ifdef ATARI
+MODULE_OBJS += \
+	blit-atari.o
+endif
+
 ifdef USE_TINYGL
 MODULE_OBJS += \
 	tinygl/api.o \
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 98c6cf85d92..6da32b87d63 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -63,6 +63,8 @@ void Surface::drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY,
 		error("Surface::drawThickLine: bytesPerPixel must be 1, 2, or 4");
 }
 
+// see graphics/blit-atari.cpp, Atari Falcon's SuperVidel addon allows accelerated blitting
+#ifndef ATARI
 void Surface::create(int16 width, int16 height, const PixelFormat &f) {
 	assert(width >= 0 && height >= 0);
 	free();
@@ -84,6 +86,7 @@ void Surface::free() {
 	w = h = pitch = 0;
 	format = PixelFormat();
 }
+#endif
 
 void Surface::init(int16 width, int16 height, int16 newPitch, void *newPixels, const PixelFormat &f) {
 	w = width;




More information about the Scummvm-git-logs mailing list