/* 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 .
*
*/
#include "atari-surface.h"
#include "graphics/surface.h"
#include
#include
#include
#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)
#include "backends/graphics/atari/atari-c2p-asm.h"
#include "backends/graphics/atari/atari-graphics-asm.h"
#include "backends/graphics/atari/atari-supervidel.h"
#include "backends/platform/atari/atari-debug.h"
#include "backends/platform/atari/dlmalloc.h"
#include "common/textconsole.h" // error()
static struct MemoryPool {
void create() {
if (base)
_mspace = create_mspace_with_base((void *)base, size, 0);
if (_mspace)
atari_debug("Allocated mspace at 0x%08lx (%ld bytes)", base, size);
else
error("mspace allocation failed at 0x%08lx (%ld bytes)", base, size);
}
void destroy() {
if (_mspace) {
destroy_mspace(_mspace);
_mspace = nullptr;
}
}
void *malloc(size_t bytes) {
assert(_mspace);
return mspace_malloc(_mspace, bytes);
}
void *calloc(size_t n_elements, size_t elem_size) {
assert(_mspace);
return mspace_calloc(_mspace, n_elements, elem_size);
}
void free(void *mem) {
assert(_mspace);
mspace_free(_mspace, mem);
}
long base;
long size;
private:
mspace _mspace;
} s_videoRamPool, s_blitterPool;
static MemoryPool *s_currentPool;
namespace Graphics {
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 (s_currentPool) {
pixels = s_currentPool->calloc(height * pitch, format.bytesPerPixel);
if (!pixels)
error("Not enough VRAM to allocate a surface");
if (s_currentPool == &s_blitterPool) {
assert(pixels >= (void *)0xA1000000);
} else if (s_currentPool == &s_videoRamPool) {
#ifdef USE_SUPERVIDEL
if (g_hasSuperVidel)
assert(pixels >= (void *)0xA0000000 && pixels < (void *)0xA1000000);
else
#endif
assert(pixels < (void *)0x01000000);
}
} else {
pixels = ::calloc(height * pitch, format.bytesPerPixel);
if (!pixels)
error("Not enough RAM to allocate a surface");
}
assert(((uintptr)pixels & (MALLOC_ALIGNMENT - 1)) == 0);
}
}
void Surface::free() {
if (pixels) {
if (s_currentPool)
s_currentPool->free(pixels);
else
::free(pixels);
pixels = nullptr;
}
w = h = pitch = 0;
format = PixelFormat();
}
} // End of namespace Graphics
///////////////////////////////////////////////////////////////////////////////
AtariSurface::AtariSurface(int16 width, int16 height, const Graphics::PixelFormat &pixelFormat) {
create(width, height, pixelFormat);
}
AtariSurface::~AtariSurface() {
free();
}
void AtariSurface::create(int16 width, int16 height, const Graphics::PixelFormat &pixelFormat) {
MemoryPool *oldPool = s_currentPool;
s_currentPool = &s_videoRamPool;
Graphics::ManagedSurface::create(width * (format == PIXELFORMAT_RGB121 ? 4 : 8) / 8, height, pixelFormat);
w = width;
s_currentPool = oldPool;
}
void AtariSurface::free() {
MemoryPool *oldPool = s_currentPool;
s_currentPool = &s_videoRamPool;
Graphics::ManagedSurface::free();
s_currentPool = oldPool;
}
void AtariSurface::copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height) {
assert(width % 16 == 0);
assert(destX % 16 == 0);
assert(format.bytesPerPixel == 1);
const byte *pChunky = (const byte *)buffer;
const byte *pChunkyEnd = pChunky + (height - 1) * srcPitch + width;
byte *pScreen = (byte *)getBasePtr(0, destY) + destX * getBitsPerPixel()/8;
if (getBitsPerPixel() == 8) {
if (srcPitch == width) {
if (srcPitch == pitch) {
asm_c2p1x1_8(pChunky, pChunkyEnd, pScreen);
return;
} else if (srcPitch == pitch/2) {
asm_c2p1x1_8_tt(pChunky, pChunkyEnd, pScreen, pitch);
return;
}
}
asm_c2p1x1_8_rect(
pChunky, pChunkyEnd,
width,
srcPitch,
pScreen,
pitch);
} else {
if (srcPitch == width && srcPitch/2 == pitch) {
asm_c2p1x1_4(pChunky, pChunkyEnd, pScreen);
return;
}
asm_c2p1x1_4_rect(
pChunky, pChunkyEnd,
width,
srcPitch,
pScreen,
pitch);
}
}
void AtariSurface::drawMaskedSprite(
const Graphics::Surface &srcSurface, const Graphics::Surface &srcMask,
const Graphics::Surface &boundingSurface,
int destX, int destY,
const Common::Rect &subRect) {
assert(srcSurface.w == srcMask.w);
assert(srcSurface.h == srcMask.h);
bool skipFirstPix16 = false;
bool skipLastPix16 = false;
int srcSurfaceLeft = 0;
int srcSurfaceWidth = srcSurface.w;
int dstSurfaceLeft = 0;
if (subRect.left > 0) {
skipFirstPix16 = true;
const int offset = subRect.left & (-16);
srcSurfaceLeft += offset;
srcSurfaceWidth -= offset;
destX = 16 - (subRect.left & (16-1));
dstSurfaceLeft -= 16;
}
if (destX + srcSurfaceWidth > boundingSurface.w) {
skipLastPix16 = true;
const int offset = (destX + srcSurfaceWidth - boundingSurface.w) & (-16);
srcSurfaceWidth -= offset;
}
assert(srcSurfaceLeft % 16 == 0);
assert(srcSurfaceWidth % 16 == 0);
destX += (this->w - boundingSurface.w) / 2;
if (getBitsPerPixel() == 8) {
asm_draw_8bpl_sprite(
(uint16 *)getBasePtr(dstSurfaceLeft, 0),
(const uint16 *)srcSurface.getBasePtr(srcSurfaceLeft, subRect.top),
(const uint16 *)srcMask.getBasePtr(srcSurfaceLeft / 8, subRect.top),
destX, destY,
pitch, srcSurface.w, srcSurfaceWidth, subRect.height(),
skipFirstPix16, skipLastPix16);
} else {
asm_draw_4bpl_sprite(
(uint16 *)getBasePtr(dstSurfaceLeft / 2, 0),
(const uint16 *)srcSurface.getBasePtr(srcSurfaceLeft / 2, subRect.top),
(const uint16 *)srcMask.getBasePtr(srcSurfaceLeft / 8, subRect.top),
destX, destY,
pitch, srcSurface.w / 2, srcSurfaceWidth, subRect.height(),
skipFirstPix16, skipLastPix16);
}
}
///////////////////////////////////////////////////////////////////////////////
#ifdef USE_SUPERVIDEL
static long hasSvRamBoosted() {
register long ret __asm__ ("d0") = 0;
__asm__ volatile(
"\tmovec %%itt0,%%d1\n"
"\tcmp.l #0xA007E060,%%d1\n"
"\tbne.s 1f\n"
"\tmovec %%dtt0,%%d1\n"
"\tcmp.l #0xA007E060,%%d1\n"
"\tbne.s 1f\n"
"\tmoveq #1,%%d0\n"
"1:\n"
: "=g"(ret) /* outputs */
: /* inputs */
: __CLOBBER_RETURN("d0") "d1", "cc"
);
return ret;
}
#endif // USE_SUPERVIDEL
void AtariSurfaceInit() {
#ifdef USE_SUPERVIDEL
g_hasSuperVidel = Getcookie(C_SupV, NULL) == C_FOUND && VgetMonitor() == MON_VGA;
if (g_hasSuperVidel) {
#ifdef USE_SV_BLITTER
g_superVidelFwVersion = *SV_VERSION & 0x01ff;
atari_debug("SuperVidel FW Revision: %d, using %s", g_superVidelFwVersion,
g_superVidelFwVersion >= 9 ? "fast async FIFO" : "slower sync blitting");
#else
atari_debug("SuperVidel FW Revision: %d, SuperBlitter not used", *SV_VERSION & 0x01ff);
#endif
if (Supexec(hasSvRamBoosted))
atari_debug("SV_XBIOS has the pmmu boost enabled");
else
atari_warning("SV_XBIOS has the pmmu boost disabled, set 'pmmu_boost = true' in C:\\SV.INF");
#ifdef USE_SV_BLITTER
s_blitterPool.size = ct60_vmalloc(-1) - (16 * 1024 * 1024); // SV XBIOS seems to forget the initial 16 MB ST RAM mirror
s_blitterPool.base = s_blitterPool.size > 0 ? ct60_vmalloc(s_blitterPool.size) : 0;
s_blitterPool.create();
// default pool is either null or blitter
s_currentPool = &s_blitterPool;
#endif
}
#endif // USE_SUPERVIDEL
s_videoRamPool.size = 2 * 1024 * 1024; // allocate 2 MiB, leave the rest for SDMA / Blitter usage
s_videoRamPool.base = s_videoRamPool.size > 0 ? Mxalloc(s_videoRamPool.size, MX_STRAM) : 0;
#ifdef USE_SUPERVIDEL
if (g_hasSuperVidel && s_videoRamPool.base)
s_videoRamPool.base |= 0xA0000000;
#endif
s_videoRamPool.create();
}
void AtariSurfaceDeinit() {
s_videoRamPool.destroy();
if (s_videoRamPool.base) {
#ifdef USE_SUPERVIDEL
if (g_hasSuperVidel)
s_videoRamPool.base &= 0x00FFFFFF;
#endif
Mfree(s_videoRamPool.base);
s_videoRamPool.base = 0;
s_videoRamPool.size = 0;
}
#ifdef USE_SV_BLITTER
s_blitterPool.destroy();
if (s_blitterPool.base) {
ct60_vmfree(s_blitterPool.base);
s_blitterPool.base = 0;
s_blitterPool.size = 0;
}
#endif
}