/* 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 }