1 /* Copyright (c) 2013-2017 Jeffrey Pfau
2  *
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <mgba/core/map-cache.h>
7 
8 #include <mgba-util/memory.h>
9 
mMapCacheInit(struct mMapCache * cache)10 void mMapCacheInit(struct mMapCache* cache) {
11 	// TODO: Reconfigurable cache for space savings
12 	cache->cache = NULL;
13 	cache->config = mMapCacheConfigurationFillShouldStore(0);
14 	cache->sysConfig = 0;
15 	cache->status = NULL;
16 }
17 
_freeCache(struct mMapCache * cache)18 static void _freeCache(struct mMapCache* cache) {
19 	size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
20 	if (cache->cache) {
21 		mappedMemoryFree(cache->cache, 8 * 8 * sizeof(color_t) * tiles);
22 		cache->cache = NULL;
23 	}
24 	if (cache->status) {
25 		mappedMemoryFree(cache->status, tiles * sizeof(*cache->status));
26 		cache->status = NULL;
27 	}
28 }
29 
_redoCacheSize(struct mMapCache * cache)30 static void _redoCacheSize(struct mMapCache* cache) {
31 	if (!mMapCacheConfigurationIsShouldStore(cache->config)) {
32 		return;
33 	}
34 
35 	size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
36 	cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles);
37 	cache->status = anonymousMemoryMap(tiles * sizeof(*cache->status));
38 }
39 
mMapCacheConfigure(struct mMapCache * cache,mMapCacheConfiguration config)40 void mMapCacheConfigure(struct mMapCache* cache, mMapCacheConfiguration config) {
41 	if (config == cache->config) {
42 		return;
43 	}
44 	_freeCache(cache);
45 	cache->config = config;
46 	_redoCacheSize(cache);
47 }
48 
mMapCacheConfigureSystem(struct mMapCache * cache,mMapCacheSystemInfo config)49 void mMapCacheConfigureSystem(struct mMapCache* cache, mMapCacheSystemInfo config) {
50 	if (config == cache->sysConfig) {
51 		return;
52 	}
53 	_freeCache(cache);
54 	cache->sysConfig = config;
55 	_redoCacheSize(cache);
56 
57 	size_t mapSize = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
58 	cache->mapSize = mapSize << mMapCacheSystemInfoGetMapAlign(cache->sysConfig);
59 }
60 
mMapCacheConfigureMap(struct mMapCache * cache,uint32_t mapStart)61 void mMapCacheConfigureMap(struct mMapCache* cache, uint32_t mapStart) {
62 	size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
63 	memset(cache->status, 0, tiles * sizeof(*cache->status));
64 	cache->mapStart = mapStart;
65 }
66 
mMapCacheDeinit(struct mMapCache * cache)67 void mMapCacheDeinit(struct mMapCache* cache) {
68 	_freeCache(cache);
69 }
70 
mMapCacheWriteVRAM(struct mMapCache * cache,uint32_t address)71 void mMapCacheWriteVRAM(struct mMapCache* cache, uint32_t address) {
72 	if (address >= cache->mapStart && address < cache->mapStart + cache->mapSize) {
73 		address -= cache->mapStart;
74 		struct mMapCacheEntry* status = &cache->status[address >> mMapCacheSystemInfoGetMapAlign(cache->sysConfig)];
75 		++status->vramVersion;
76 		status->flags = mMapCacheEntryFlagsClearVramClean(status->flags);
77 		status->tileStatus[mMapCacheEntryFlagsGetPaletteId(status->flags)].vramClean = 0;
78 	}
79 }
80 
_cleanTile(struct mMapCache * cache,const color_t * tile,color_t * mapOut,const struct mMapCacheEntry * status)81 static inline void _cleanTile(struct mMapCache* cache, const color_t* tile, color_t* mapOut, const struct mMapCacheEntry* status) {
82 	size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
83 	int x, y;
84 	switch (mMapCacheEntryFlagsGetMirror(status->flags)) {
85 	case 0:
86 		memcpy(mapOut, tile, sizeof(color_t) * 8);
87 		memcpy(&mapOut[stride], &tile[0x08], sizeof(color_t) * 8);
88 		memcpy(&mapOut[stride * 2], &tile[0x10], sizeof(color_t) * 8);
89 		memcpy(&mapOut[stride * 3], &tile[0x18], sizeof(color_t) * 8);
90 		memcpy(&mapOut[stride * 4], &tile[0x20], sizeof(color_t) * 8);
91 		memcpy(&mapOut[stride * 5], &tile[0x28], sizeof(color_t) * 8);
92 		memcpy(&mapOut[stride * 6], &tile[0x30], sizeof(color_t) * 8);
93 		memcpy(&mapOut[stride * 7], &tile[0x38], sizeof(color_t) * 8);
94 		break;
95 	case 1:
96 		for (y = 0; y < 8; ++y) {
97 			for (x = 0; x < 8; ++x) {
98 				mapOut[y * stride + (7 - x)] = tile[y * 8 + x];
99 			}
100 		}
101 		break;
102 	case 2:
103 		memcpy(&mapOut[stride * 7], tile, sizeof(color_t) * 8);
104 		memcpy(&mapOut[stride * 6], &tile[0x08], sizeof(color_t) * 8);
105 		memcpy(&mapOut[stride * 5], &tile[0x10], sizeof(color_t) * 8);
106 		memcpy(&mapOut[stride * 4], &tile[0x18], sizeof(color_t) * 8);
107 		memcpy(&mapOut[stride * 3], &tile[0x20], sizeof(color_t) * 8);
108 		memcpy(&mapOut[stride * 2], &tile[0x28], sizeof(color_t) * 8);
109 		memcpy(&mapOut[stride], &tile[0x30], sizeof(color_t) * 8);
110 		memcpy(mapOut, &tile[0x38], sizeof(color_t) * 8);
111 		break;
112 	case 3:
113 		for (y = 0; y < 8; ++y) {
114 			for (x = 0; x < 8; ++x) {
115 				mapOut[(7 - y) * stride + (7 - x)] = tile[y * 8 + x];
116 			}
117 		}
118 		break;
119 	}
120 }
121 
mMapCacheTileId(struct mMapCache * cache,unsigned x,unsigned y)122 uint32_t mMapCacheTileId(struct mMapCache* cache, unsigned x, unsigned y) {
123 	int tilesWide = mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
124 	int tilesHigh = mMapCacheSystemInfoGetTilesHigh(cache->sysConfig);
125 	int stride = 1 << mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig);
126 	x &= (1 << tilesWide) - 1;
127 	y &= (1 << tilesHigh) - 1;
128 	unsigned xMajor = x & ~(stride - 1);
129 	unsigned yMajor = y >> mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig);
130 	x &= stride - 1;
131 	y &= stride - 1;
132 	yMajor <<= tilesWide;
133 	y += xMajor + yMajor;
134 	return stride * y + x;
135 }
136 
mMapCacheCleanTile(struct mMapCache * cache,struct mMapCacheEntry * entry,unsigned x,unsigned y)137 void mMapCacheCleanTile(struct mMapCache* cache, struct mMapCacheEntry* entry, unsigned x, unsigned y) {
138 	size_t location = mMapCacheTileId(cache, x, y);
139 	struct mMapCacheEntry* status = &cache->status[location];
140 	const color_t* tile = NULL;
141 	if (!mMapCacheEntryFlagsIsVramClean(status->flags)) {
142 		status->flags = mMapCacheEntryFlagsFillVramClean(status->flags);
143 		cache->mapParser(cache, status, &cache->vram[cache->mapStart + (location << mMapCacheSystemInfoGetMapAlign(cache->sysConfig))]);
144 	}
145 	unsigned tileId = status->tileId + cache->tileStart;
146 	if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
147 		tileId = 0;
148 	}
149 	tile = mTileCacheGetTileIfDirty(cache->tileCache, status->tileStatus, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
150 	if (!tile) {
151 		if (mMapCacheEntryFlagsIsVramClean(status->flags) && memcmp(status, &entry[location], sizeof(*entry)) == 0) {
152 			return;
153 		}
154 		tile = mTileCacheGetTile(cache->tileCache, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
155 	}
156 
157 	size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
158 	color_t* mapOut = &cache->cache[(y * stride + x) * 8];
159 	_cleanTile(cache, tile, mapOut, status);
160 	entry[location] = *status;
161 }
162 
mMapCacheCheckTile(struct mMapCache * cache,const struct mMapCacheEntry * entry,unsigned x,unsigned y)163 bool mMapCacheCheckTile(struct mMapCache* cache, const struct mMapCacheEntry* entry, unsigned x, unsigned y) {
164 	size_t location = mMapCacheTileId(cache, x, y);
165 	struct mMapCacheEntry* status = &cache->status[location];
166 	int paletteId = mMapCacheEntryFlagsGetPaletteId(status->flags);
167 	const color_t* tile = NULL;
168 	if (mMapCacheEntryFlagsIsVramClean(status->flags) && memcmp(status, &entry[location], sizeof(*entry)) == 0) {
169 		unsigned tileId = status->tileId + cache->tileStart;
170 		if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
171 			tileId = 0;
172 		}
173 		tile = mTileCacheGetTileIfDirty(cache->tileCache, &status->tileStatus[paletteId], tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
174 		return !tile;
175 	}
176 	return false;
177 }
178 
mMapCacheCleanRow(struct mMapCache * cache,unsigned y)179 void mMapCacheCleanRow(struct mMapCache* cache, unsigned y) {
180 	// TODO: Cache
181 	int tilesWide = 1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
182 	int macroTile = (1 << mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig)) - 1;
183 	size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
184 	int location = 0;
185 	int x;
186 	for (x = 0; x < tilesWide; ++x) {
187 		if (!(x & macroTile)) {
188 			location = mMapCacheTileId(cache, x, y);
189 		} else {
190 			++location;
191 		}
192 		struct mMapCacheEntry* status = &cache->status[location];
193 		if (!mMapCacheEntryFlagsIsVramClean(status->flags)) {
194 			status->flags = mMapCacheEntryFlagsFillVramClean(status->flags);
195 			cache->mapParser(cache, status, &cache->vram[cache->mapStart + (location << mMapCacheSystemInfoGetMapAlign(cache->sysConfig))]);
196 		}
197 		unsigned tileId = status->tileId + cache->tileStart;
198 		if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
199 			tileId = 0;
200 		}
201 		const color_t* tile = mTileCacheGetTile(cache->tileCache, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
202 		color_t* mapOut = &cache->cache[(y * stride + x) * 8];
203 		_cleanTile(cache, tile, mapOut, status);
204 	}
205 }
206 
mMapCacheGetRow(struct mMapCache * cache,unsigned y)207 const color_t* mMapCacheGetRow(struct mMapCache* cache, unsigned y) {
208 	size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
209 	return &cache->cache[y * stride];
210 }
211