/* Copyright (c) 2013-2016 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include void mTileCacheInit(struct mTileCache* cache) { // TODO: Reconfigurable cache for space savings cache->cache = NULL; cache->config = mTileCacheConfigurationFillShouldStore(0); cache->status = NULL; cache->globalPaletteVersion = NULL; cache->palette = NULL; } static void _freeCache(struct mTileCache* cache) { unsigned size = 1 << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig); unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig); if (cache->cache) { mappedMemoryFree(cache->cache, 8 * 8 * sizeof(color_t) * tiles * size); cache->cache = NULL; } if (cache->status) { mappedMemoryFree(cache->status, tiles * size * sizeof(*cache->status)); cache->status = NULL; } free(cache->globalPaletteVersion); cache->globalPaletteVersion = NULL; free(cache->palette); cache->palette = NULL; } static void _redoCacheSize(struct mTileCache* cache) { if (!mTileCacheConfigurationIsShouldStore(cache->config)) { return; } unsigned size = mTileCacheSystemInfoGetPaletteCount(cache->sysConfig); unsigned bpp = mTileCacheSystemInfoGetPaletteBPP(cache->sysConfig); cache->bpp = bpp; bpp = 1 << (1 << bpp); size = 1 << size; cache->entriesPerTile = size; unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig); cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles * size); cache->status = anonymousMemoryMap(tiles * size * sizeof(*cache->status)); cache->globalPaletteVersion = malloc(size * sizeof(*cache->globalPaletteVersion)); cache->palette = malloc(size * bpp * sizeof(*cache->palette)); } void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config) { if (cache->config == config) { return; } _freeCache(cache); cache->config = config; _redoCacheSize(cache); } void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config, uint32_t tileBase, uint32_t paletteBase) { _freeCache(cache); cache->sysConfig = config; cache->tileBase = tileBase; cache->paletteBase = paletteBase; _redoCacheSize(cache); } void mTileCacheDeinit(struct mTileCache* cache) { _freeCache(cache); } void mTileCacheWriteVRAM(struct mTileCache* cache, uint32_t address) { if (address < cache->tileBase) { return; } address -= cache->tileBase; unsigned bpp = cache->bpp + 3; unsigned count = cache->entriesPerTile; address >>= bpp; if (address >= mTileCacheSystemInfoGetMaxTiles(cache->sysConfig)) { return; } size_t i; for (i = 0; i < count; ++i) { cache->status[address * count + i].vramClean = 0; ++cache->status[address * count + i].vramVersion; } } void mTileCacheWritePalette(struct mTileCache* cache, uint32_t entry, color_t color) { if (entry < cache->paletteBase) { return; } entry -= cache->paletteBase; unsigned maxEntry = (1 << (1 << cache->bpp)) * cache->entriesPerTile; if (entry >= maxEntry) { return; } cache->palette[entry] = color; entry >>= (1 << mTileCacheSystemInfoGetPaletteBPP(cache->sysConfig)); ++cache->globalPaletteVersion[entry]; } static void _regenerateTile4(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) { uint8_t* start = (uint8_t*) &cache->vram[tileId << 3]; paletteId <<= 2; color_t* palette = &cache->palette[paletteId]; int i; for (i = 0; i < 8; ++i) { uint8_t tileDataLower = start[0]; uint8_t tileDataUpper = start[1]; start += 2; int pixel; pixel = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7); tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6); tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5); tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4); tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3); tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2); tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1); tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = ((tileDataUpper & 1) << 1) | (tileDataLower & 1); tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; tile += 8; } } static void _regenerateTile16(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) { uint32_t* start = (uint32_t*) &cache->vram[tileId << 4]; paletteId <<= 4; color_t* palette = &cache->palette[paletteId]; int i; for (i = 0; i < 8; ++i) { uint32_t line = *start; ++start; int pixel; pixel = line & 0xF; tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 4) & 0xF; tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 8) & 0xF; tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 12) & 0xF; tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 16) & 0xF; tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 20) & 0xF; tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 24) & 0xF; tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 28) & 0xF; tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; tile += 8; } } static void _regenerateTile256(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) { uint32_t* start = (uint32_t*) &cache->vram[tileId << 5]; paletteId <<= 8; color_t* palette = &cache->palette[paletteId]; int i; for (i = 0; i < 8; ++i) { uint32_t line = *start; ++start; int pixel; pixel = line & 0xFF; tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 8) & 0xFF; tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 16) & 0xFF; tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 24) & 0xFF; tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; line = *start; ++start; pixel = line & 0xFF; tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 8) & 0xFF; tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 16) & 0xFF; tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; pixel = (line >> 24) & 0xFF; tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel]; tile += 8; } } static inline color_t* _tileLookup(struct mTileCache* cache, unsigned tileId, unsigned paletteId) { if (mTileCacheConfigurationIsShouldStore(cache->config)) { unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig); #ifndef NDEBUG if (tileId >= tiles) { abort(); } if (paletteId >= 1U << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig)) { abort(); } #endif return &cache->cache[(tileId + paletteId * tiles) << 6]; } else { return cache->temporaryTile; } } const color_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId) { unsigned count = cache->entriesPerTile; unsigned bpp = cache->bpp; struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId]; struct mTileCacheEntry desiredStatus = { .paletteVersion = cache->globalPaletteVersion[paletteId], .vramVersion = status->vramVersion, .vramClean = 1, .paletteId = paletteId }; color_t* tile = _tileLookup(cache, tileId, paletteId); if (!mTileCacheConfigurationIsShouldStore(cache->config) || memcmp(status, &desiredStatus, sizeof(*status))) { switch (bpp) { case 0: return NULL; case 1: _regenerateTile4(cache, tile, tileId, paletteId); break; case 2: _regenerateTile16(cache, tile, tileId, paletteId); break; case 3: _regenerateTile256(cache, tile, tileId, paletteId); break; } *status = desiredStatus; } return tile; } const color_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId) { unsigned count = cache->entriesPerTile; unsigned bpp = cache->bpp; struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId]; struct mTileCacheEntry desiredStatus = { .paletteVersion = cache->globalPaletteVersion[paletteId], .vramVersion = status->vramVersion, .vramClean = 1, .paletteId = paletteId }; color_t* tile = NULL; if (memcmp(status, &desiredStatus, sizeof(*status))) { tile = _tileLookup(cache, tileId, paletteId); switch (bpp) { case 0: return NULL; case 1: _regenerateTile4(cache, tile, tileId, paletteId); break; case 2: _regenerateTile16(cache, tile, tileId, paletteId); break; case 3: _regenerateTile256(cache, tile, tileId, paletteId); break; } *status = desiredStatus; } if (memcmp(status, &entry[paletteId], sizeof(*status))) { tile = _tileLookup(cache, tileId, paletteId); entry[paletteId] = *status; } return tile; } const color_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) { return &cache->palette[paletteId << (1 << cache->bpp)]; } const uint16_t* mTileCacheGetVRAM(struct mTileCache* cache, unsigned tileId) { unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig); if (tileId >= tiles) { return NULL; } return &cache->vram[tileId << (cache->bpp + 2)]; }