Trim the SGB tilemap (claiming back nearly 1kB of space) - pret/pokered GitHub Wiki
The SGB tilemap is a large 32 * 32 area of 16-bit tile data. That's a whole 2kB chunk that sits on the ROM. It turns out that a large portion of it is filled with zeros: the GB screen area in the center, and the last 256 bottom bytes. By using the same SGB load function as in Gen2, it's possible to trim all this blank data, and add the zeros on the fly.
Contents
- Edit SGB copy functions
- Add a tilemap trimming tool
- Trim the SGB tilemap
- (Optional) Trim the border palettes
1. Edit SGB copy functions
First edit engine/gfx/palettes.asm. We want to add a new check for loading the SGB tilemap to VRAM, besides the existing ones (plain copy, and border Gfx copy).
LoadSGB:
xor a
ld [wOnSGB], a
call CheckSGB
ret nc
ld a, 1
ld [wOnSGB], a
ld a, [wOnCGB]
and a
jr z, .notCGB
ret
.notCGB
di
call PrepareSuperNintendoVRAMTransfer
ei
ld a, 1
ld [wCopyingSGBTileData], a
ld de, ChrTrnPacket
ld hl, SGBBorderGraphics
call CopyGfxToSuperNintendoVRAM
- xor a
+ ld a, 2
ld [wCopyingSGBTileData], a
CopyGfxToSuperNintendoVRAM:
di
push de
call DisableLCD
ld a, $e4
ldh [rBGP], a
ld de, vChars1
ld a, [wCopyingSGBTileData]
and a
- jr z, .notCopyingTileData
+ jr z, .standardCopy
+ dec a
+ jr z, .borderTiles
+ ; tilemap
+ call CopySGBTilemapPals
+ jr .next
+ .borderTiles
call CopySGBBorderTiles
jr .next
- .notCopyingTileData
+ .standardCopy
ld bc, 256 tiles
call CopyData
.next
Insert these two functions anywhere in this file (I chose to add it after CopySGBBorderTiles).
CopySGBTilemapPals:
ld bc, (6 + SCREEN_WIDTH + 6) * 5 * 2
call CopyData
ld b, SCREEN_HEIGHT
.loop
push bc
ld bc, 6 * 2
call CopyData
ld bc, SCREEN_WIDTH * 2
call ClearBytes
ld bc, 6 * 2
call CopyData
pop bc
dec b
jr nz, .loop
ld bc, (6 + SCREEN_WIDTH + 6) * 5 * 2
call CopyData
ld bc, $100
call ClearBytes
ld bc, 3 * 16 * COLOR_SIZE ; 3 SNES palettes
call CopyData
ret
ClearBytes::
; clear bc bytes of data starting from de
.loop
xor a
ld [de], a
inc de
dec bc
ld a, c
or b
jr nz, .loop
ret
2. Add a tilemap trimming tool
Now we'll automate the cropping of the GB screen area within the tilemap file. Create a new file named trim_sgb_tilemap.c in tools/, and paste the following lines.
#define PROGRAM_NAME "trim_sgb_tilemap"
#define USAGE_OPTS "[-h|--help] [-r|--revert] infile.tilemap outfile.tilemap"
#include "common.h"
void parse_args(int argc, char *argv[], bool *revert) {
struct option long_options[] = {
{"revert", no_argument, 0, 'r'},
{"help", no_argument, 0, 'h'},
{0}
};
for (int opt; (opt = getopt_long(argc, argv, "rh", long_options)) != -1;) {
switch (opt) {
case 'r':
*revert = true;
break;
case 'h':
usage_exit(0);
break;
default:
usage_exit(1);
}
}
}
uint8_t output[0x700];
int frame_start = (5 * 32 + 6) * 2;
void trim_tilemap(const uint8_t *data, long filesize) {
int pos = 0;
int frame_end = (22 * 32 + 26) * 2;
for (int i = 0; i < frame_start; i++) {
output[pos++] = data[i];
}
for (int row = 0; row < 17; row++) {
for (int i = 0; i < 12 * 2; i++) {
output[pos++] = data[frame_start + 20 * 2 + row * 32 * 2 + i];
}
}
for (int i = frame_end; i < filesize; i++) {
output[pos++] = data[i];
}
}
void expand_tilemap(const uint8_t *data, long filesize) {
int pos = 0;
int frame_end = frame_start + 18 * 12 * 2;
for (int i = 0; i < frame_start; i++) {
output[pos++] = data[i];
}
for (int row = 0; row < 18; row++) {
for (int i = 0; i < 20 * 2; i++) {
output[pos++] = 0;
}
for (int i = 0; i < 12 * 2; i++) {
output[pos++] = data[frame_start + row * 12 * 2 + i];
}
}
for (int i = frame_end; i < filesize; i++) {
output[pos++] = data[i];
}
}
int main(int argc, char *argv[]) {
bool revert = false;
parse_args(argc, argv, &revert);
argc -= optind;
argv += optind;
if (argc < 1) {
usage_exit(1);
}
int in_size = revert ? 0x430 : 0x700;
long filesize;
uint8_t *data = read_u8(argv[0], &filesize);
if (filesize != in_size) {
error_exit("Input SGB tilemap has wrong size.");
}
if (revert) {
expand_tilemap(data, filesize);
} else {
trim_tilemap(data, filesize);
}
write_u8(argv[1], output, revert ? 0x700 : 0x430);
free(data);
return 0;
}
Edit tools/Makefile to add this file to build:
tools := \
gfx \
make_patch \
pkmncompress \
+ trim_sgb_tilemap \
scan_includes
Add a line to tools/.gitignore.
gfx
make_patch
pkmncompress
+ trim_sgb_tilemap
scan_includes
The tool has a --reverse option that can be used to restore a full 32 * 32 tilemap from a cropped one, if needed.
3. Trim the SGB tilemap
Edit data/sgb/sgb_border.asm:
BorderPalettes:
IF DEF(_RED)
- INCBIN "gfx/sgb/red_border.tilemap"
+ INCBIN "gfx/sgb/red_border.sgb.tilemap"
ENDC
IF DEF(_BLUE)
- INCBIN "gfx/sgb/blue_border.tilemap"
+ INCBIN "gfx/sgb/blue_border.sgb.tilemap"
ENDC
- ds $100
Finally, edit Makefile:
clean: tidy
find gfx \
\( -iname '*.1bpp' \
-o -iname '*.2bpp' \
- -o -iname '*.pic' \) \
+ -o -iname '*.pic' \
+ -o -iname '*.sgb.tilemap' \) \
-delete
%.pic: %.2bpp
tools/pkmncompress $< $@
+ %.sgb.tilemap: %.tilemap
+ tools/trim_sgb_tilemap $< $@
### File extensions that are never generated and should be manually created
That's it, the net decrease is about 900 bytes.
4. (Optional) Trim the border palettes
If you have kept the original borders (or if your own custom borders still use only 4 colors in each border palette), you can trim all the unused color slots, and pad with zeros during function call.
Edit the previous CopySGBTilemapPals function:
CopySGBTilemapPals:
ld bc, (6 + SCREEN_WIDTH + 6) * 5 * 2
call CopyData
ld b, SCREEN_HEIGHT
.loop
push bc
ld bc, 6 * 2
call CopyData
ld bc, SCREEN_WIDTH * 2
call ClearBytes
ld bc, 6 * 2
call CopyData
pop bc
dec b
jr nz, .loop
ld bc, (6 + SCREEN_WIDTH + 6) * 5 * 2
call CopyData
ld bc, $100
call ClearBytes
- ld bc, 3 * 16 * COLOR_SIZE ; 3 SNES palettes
- call CopyData
+ ld c, 3
+ .pal_loop
+ push bc
+ ld bc, 4 * COLOR_SIZE
+ call CopyData
+ ld bc, 12 * COLOR_SIZE
+ call ClearBytes
+ pop bc
+ dec c
+ jr nz, .pal_loop
ret
Edit data/sgb/sgb_border.asm:
IF DEF(_RED)
RGB 30,29,29 ; PAL_SGB1
RGB 25,22,25
RGB 25,17,21
RGB 24,14,12
ENDC
IF DEF(_BLUE)
RGB 0,0,0 ; PAL_SGB1 (the first color is not defined, but if used, turns up as 30,29,29... o_O)
RGB 10,17,26
RGB 5,9,20
RGB 16,20,27
ENDC
- ds $18
IF DEF(_RED)
RGB 30,29,29 ; PAL_SGB2
RGB 22,31,16
RGB 27,20,6
RGB 15,15,15
ENDC
IF DEF(_BLUE)
RGB 30,29,29 ; PAL_SGB2
RGB 27,11,6
RGB 5,9,20
RGB 28,25,15
ENDC
- ds $18
IF DEF(_RED)
RGB 30,29,29 ; PAL_SGB3
RGB 31,31,17
RGB 18,21,29
RGB 15,15,15
ENDC
IF DEF(_BLUE)
RGB 30,29,29 ; PAL_SGB3
RGB 12,15,11
RGB 5,9,20
RGB 14,22,17
ENDC
- ds $18
Another 36 bytes trimmed, though this time the gain is minimal.