BMV: Difference between revisions
(→Audio) |
(→Video) |
||
(7 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
* Extensions: bmv | * Extensions: bmv | ||
* Company: Teeny Weeny Games | * Company: Teeny Weeny Games | ||
* Samples: http://samples.mplayerhq.hu/game-formats/bmv/ | |||
BMV is the file extension for FMV used in Discworld 2 and Discworld 3 games, actual format differs for them. | BMV is the file extension for FMV used in Discworld 2 and Discworld 3 games, actual format differs for them. | ||
Line 29: | Line 30: | ||
Data is stored in this order: | Data is stored in this order: | ||
audio (3675 bytes) | audio (3675 bytes for old audio block, 1 + blobs*65 for newer one) | ||
commands | commands | ||
palette (768 bytes) | palette (768 bytes) | ||
Line 39: | Line 40: | ||
=== Video === | === Video === | ||
Video is packed | Video packing is rather exotic: frame data may be decoded in forward or backward direction. | ||
In the latter case input data is read from the end to beginning and frame updated the same way (right to left, bottom to top). | |||
Video decoder cycles between three states corresponding to the decoding modes: | |||
# copy data from the given offset of the frame | |||
# copy pixel values from input | |||
# fill some segment with the left (or right for backwards decoding) neighbour value | |||
The length of segment is defined by code which is read before performing operation. The lowest bit of that code specifies whether the next mode should be skipped or not. | |||
Length code is coded in unusual way too: each nibble provide next bits of the code. | |||
If any of two upper bits of the nibble are set that means that code decoding is complete, otherwise only two low bits of the nibble are prepended to the code. Code nibbles are packed into bytes, so one byte can actually contain codes for current and next operation. | |||
=== Audio === | === Audio === | ||
Line 46: | Line 58: | ||
Each blob contains a byte with multipliers and actual data. Two multipliers are stored in a single byte which is cyclically rotated left by one | Each blob contains a byte with multipliers and actual data. Two multipliers are stored in a single byte which is cyclically rotated left by one | ||
mult_table[16] = { 16512, 8256, 4128, 2064, 1032, 516, 258, 192 | mult_table[16] = { 16512, 8256, 4128, 2064, 1032, 516, 258, 192, 129, 88, 64, 56, 48, 40, 36, 32 } | ||
t = get_byte(); | t = get_byte(); | ||
Line 53: | Line 65: | ||
mul2 = mult_table[t >> 4]; | mul2 = mult_table[t >> 4]; | ||
Audio is decoded by fetching byte, multiplying it by mul1 or mul2 (even samples - by mul1, odd samples - by mul2) and dividing by 32: | |||
int8_t *src; | int8_t *src; | ||
Line 61: | Line 73: | ||
blobsize--; | blobsize--; | ||
} | } | ||
== Discworld 3 BMV == | |||
=== Container === | |||
Container consists of two chunks - header and data. Each chunk starts with magic ('BMVi' or 'DATA') and payload size. | |||
Header data (little-endian): | |||
bytes 0- 3 'BMVi' | |||
bytes 4- 7 payload size | |||
bytes 8-11 slot size | |||
bytes 12-15 number of frames | |||
bytes 16-17 prefetch slots | |||
bytes 18-19 some cache size | |||
bytes 20-21 frames per 256 seconds | |||
bytes 22-23 audio max size | |||
bytes 24-25 audio blob size (41 for new audio coding, otherwise old scheme is used) | |||
byte 26 audio ID | |||
byte 27 video ID | |||
bytes 28-29 frame width | |||
bytes 30-31 frame height | |||
Frames stored in data chunk seems to have the same structure as v2, except that palette must not be present and audio is stored in fixed size per chunk <code>audio_max_size<code> or <code>audio_max_size - audio_blob_size</code> if flag <code>0x40</code> is set. | |||
=== Audio === | |||
New audio coding stores 64 samples in 41 bytes. Packets for left and right channel are interleaved, coding mode is the last byte in a packet for the left channel and the first one for the right channel. | |||
Compression is done by using delta coding and quantising delta values into 5 bits. Mode byte consists of two nibbles selecting which subpart of the table should be used (high nibble for the first 32 samples). | |||
Samples are packed into 16-bit words, top bits form codeword for two last values (MSB), the rest is divided into three 5-bit groups. | |||
for (i = 0; i < 10; i++) { | |||
code = get_16bits(); | |||
last_cw = (last_cw << 1) | (code >> 15); | |||
pred += quant_table[mode][(code >> 10) & 0x1F]; | |||
*dst++ = pred; | |||
pred += quant_table[mode][(code >> 5) & 0x1F]; | |||
*dst++ = pred; | |||
pred += quant_table[mode][ code & 0x1F]; | |||
*dst++ = pred; | |||
} | |||
pred += quant_table[mode][(last_cw >> 5) & 0x1F]; | |||
*dst++ = pred; | |||
pred += quant_table[mode][ last_cw & 0x1F]; | |||
*dst++ = pred; | |||
==== Quantisation table ==== | |||
// mode 0x0 | |||
0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, | |||
0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, | |||
0xC000, 0xC400, 0xC800, 0xCC00, 0xD000, 0xD400, 0xD800, 0xDC00, | |||
0xE000, 0xE400, 0xE800, 0xEC00, 0xF000, 0xF400, 0xF800, 0xFC00, | |||
// mode 0x1 | |||
0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0A00, 0x0C00, 0x0E00, | |||
0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1A00, 0x1C00, 0x1E00, | |||
0xE000, 0xE200, 0xE400, 0xE600, 0xE800, 0xEA00, 0xEC00, 0xEE00, | |||
0xF000, 0xF200, 0xF400, 0xF600, 0xF800, 0xFA00, 0xFC00, 0xFE00, | |||
// mode 0x2 | |||
0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700, | |||
0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, 0x0F00, | |||
0xF000, 0xF100, 0xF200, 0xF300, 0xF400, 0xF500, 0xF600, 0xF700, | |||
0xF800, 0xF900, 0xFA00, 0xFB00, 0xFC00, 0xFD00, 0xFE00, 0xFF00, | |||
// mode 0x3 | |||
0x0000, 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380, | |||
0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780, | |||
0xF800, 0xF880, 0xF900, 0xF980, 0xFA00, 0xFA80, 0xFB00, 0xFB80, | |||
0xFC00, 0xFC80, 0xFD00, 0xFD80, 0xFE00, 0xFE80, 0xFF00, 0xFF80, | |||
// mode 0x4 | |||
0x0000, 0x0048, 0x0090, 0x00D8, 0x0120, 0x0168, 0x01B0, 0x01F8, | |||
0x0240, 0x0288, 0x02D0, 0x0318, 0x0360, 0x03A8, 0x03F0, 0x0438, | |||
0xFB80, 0xFBC8, 0xFC10, 0xFC58, 0xFCA0, 0xFCE8, 0xFD30, 0xFD78, | |||
0xFDC0, 0xFE08, 0xFE50, 0xFE98, 0xFEE0, 0xFF28, 0xFF70, 0xFFB8, | |||
// mode 0x5 | |||
0x0000, 0x0030, 0x0060, 0x0090, 0x00C0, 0x00F0, 0x0120, 0x0150, | |||
0x0180, 0x01B0, 0x01E0, 0x0210, 0x0240, 0x0270, 0x02A0, 0x02D0, | |||
0xFD00, 0xFD30, 0xFD60, 0xFD90, 0xFDC0, 0xFDF0, 0xFE20, 0xFE50, | |||
0xFE80, 0xFEB0, 0xFEE0, 0xFF10, 0xFF40, 0xFF70, 0xFFA0, 0xFFD0, | |||
// mode 0x6 | |||
0x0000, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0, 0x00C0, 0x00E0, | |||
0x0100, 0x0120, 0x0140, 0x0160, 0x0180, 0x01A0, 0x01C0, 0x01E0, | |||
0xFE00, 0xFE20, 0xFE40, 0xFE60, 0xFE80, 0xFEA0, 0xFEC0, 0xFEE0, | |||
0xFF00, 0xFF20, 0xFF40, 0xFF60, 0xFF80, 0xFFA0, 0xFFC0, 0xFFE0, | |||
// mode 0x7 | |||
0x0000, 0x0016, 0x002C, 0x0042, 0x0058, 0x006E, 0x0084, 0x009A, | |||
0x00B0, 0x00C6, 0x00DC, 0x00F2, 0x0108, 0x011E, 0x0134, 0x014A, | |||
0xFEA0, 0xFEB6, 0xFECC, 0xFEE2, 0xFEF8, 0xFF0E, 0xFF24, 0xFF3A, | |||
0xFF50, 0xFF66, 0xFF7C, 0xFF92, 0xFFA8, 0xFFBE, 0xFFD4, 0xFFEA, | |||
// mode 0x8 | |||
0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, | |||
0x0080, 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, | |||
0xFF00, 0xFF10, 0xFF20, 0xFF30, 0xFF40, 0xFF50, 0xFF60, 0xFF70, | |||
0xFF80, 0xFF90, 0xFFA0, 0xFFB0, 0xFFC0, 0xFFD0, 0xFFE0, 0xFFF0, | |||
// mode 0x9 | |||
0x0000, 0x000B, 0x0016, 0x0021, 0x002C, 0x0037, 0x0042, 0x004D, | |||
0x0058, 0x0063, 0x006E, 0x0079, 0x0084, 0x008F, 0x009A, 0x00A5, | |||
0xFF50, 0xFF5B, 0xFF66, 0xFF71, 0xFF7C, 0xFF87, 0xFF92, 0xFF9D, | |||
0xFFA8, 0xFFB3, 0xFFBE, 0xFFC9, 0xFFD4, 0xFFDF, 0xFFEA, 0xFFF5, | |||
// mode 0xA | |||
0x0000, 0x0008, 0x0010, 0x0018, 0x0020, 0x0028, 0x0030, 0x0038, | |||
0x0040, 0x0048, 0x0050, 0x0058, 0x0060, 0x0068, 0x0070, 0x0078, | |||
0xFF80, 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, | |||
0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, | |||
// mode 0xB | |||
0x0000, 0x0006, 0x000C, 0x0012, 0x0018, 0x001E, 0x0024, 0x002A, | |||
0x0030, 0x0036, 0x003C, 0x0042, 0x0048, 0x004E, 0x0054, 0x005A, | |||
0xFFA0, 0xFFA6, 0xFFAC, 0xFFB2, 0xFFB8, 0xFFBE, 0xFFC4, 0xFFCA, | |||
0xFFD0, 0xFFD6, 0xFFDC, 0xFFE2, 0xFFE8, 0xFFEE, 0xFFF4, 0xFFFA, | |||
// mode 0xC | |||
0x0000, 0x0004, 0x0008, 0x000C, 0x0010, 0x0014, 0x0018, 0x001C, | |||
0x0020, 0x0024, 0x0028, 0x002C, 0x0030, 0x0034, 0x0038, 0x003C, | |||
0xFFC0, 0xFFC4, 0xFFC8, 0xFFCC, 0xFFD0, 0xFFD4, 0xFFD8, 0xFFDC, | |||
0xFFE0, 0xFFE4, 0xFFE8, 0xFFEC, 0xFFF0, 0xFFF4, 0xFFF8, 0xFFFC, | |||
// mode 0xD | |||
0x0000, 0x0002, 0x0005, 0x0008, 0x000B, 0x000D, 0x0010, 0x0013, | |||
0x0016, 0x0018, 0x001B, 0x001E, 0x0021, 0x0023, 0x0026, 0x0029, | |||
0xFFD4, 0xFFD6, 0xFFD9, 0xFFDC, 0xFFDF, 0xFFE1, 0xFFE4, 0xFFE7, | |||
0xFFEA, 0xFFEC, 0xFFEF, 0xFFF2, 0xFFF5, 0xFFF7, 0xFFFA, 0xFFFD, | |||
// mode 0xE | |||
0x0000, 0x0001, 0x0003, 0x0005, 0x0007, 0x0008, 0x000A, 0x000C, | |||
0x000E, 0x000F, 0x0011, 0x0013, 0x0015, 0x0016, 0x0018, 0x001A, | |||
0xFFE4, 0xFFE5, 0xFFE7, 0xFFE9, 0xFFEB, 0xFFEC, 0xFFEE, 0xFFF0, | |||
0xFFF2, 0xFFF3, 0xFFF5, 0xFFF7, 0xFFF9, 0xFFFA, 0xFFFC, 0xFFFE, | |||
// mode 0xF | |||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, | |||
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, | |||
0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7, | |||
0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, | |||
[[Category:Audio Codecs]] | [[Category:Audio Codecs]] |
Revision as of 08:35, 6 November 2011
- Extensions: bmv
- Company: Teeny Weeny Games
- Samples: http://samples.mplayerhq.hu/game-formats/bmv/
BMV is the file extension for FMV used in Discworld 2 and Discworld 3 games, actual format differs for them.
Discworld 2 BMV
Container format
File has no header and consists of series of packets. Each packet starts with the byte showing its type and except NOP and END packets all other packets have 24-bit little-endian length field.
Packet type meaning:
0x00 NOP 0x01 END 0x02 Inter frame 0x03 Intra frame Frame types can additionally have a set of flags:
0x04 packet contains scroll offset 0x08 palette is included 0x10 commands (e.g. for subtitles) 0x20 audio is present 0x40 extended mode 0x80 text is present (for command)
Data is stored in this order:
audio (3675 bytes for old audio block, 1 + blobs*65 for newer one) commands palette (768 bytes) screen offset (2 bytes) picture data
Since text string are loaded from game resources, it is not described here.
Video
Video packing is rather exotic: frame data may be decoded in forward or backward direction. In the latter case input data is read from the end to beginning and frame updated the same way (right to left, bottom to top).
Video decoder cycles between three states corresponding to the decoding modes:
- copy data from the given offset of the frame
- copy pixel values from input
- fill some segment with the left (or right for backwards decoding) neighbour value
The length of segment is defined by code which is read before performing operation. The lowest bit of that code specifies whether the next mode should be skipped or not.
Length code is coded in unusual way too: each nibble provide next bits of the code. If any of two upper bits of the nibble are set that means that code decoding is complete, otherwise only two low bits of the nibble are prepended to the code. Code nibbles are packed into bytes, so one byte can actually contain codes for current and next operation.
Audio
Audio is decoded per blobs. Each blob contains a byte with multipliers and actual data. Two multipliers are stored in a single byte which is cyclically rotated left by one
mult_table[16] = { 16512, 8256, 4128, 2064, 1032, 516, 258, 192, 129, 88, 64, 56, 48, 40, 36, 32 }
t = get_byte(); t = (t >> 1) | ((t & 0x1) << 7); mul1 = mult_table[t & 0xF]; mul2 = mult_table[t >> 4];
Audio is decoded by fetching byte, multiplying it by mul1 or mul2 (even samples - by mul1, odd samples - by mul2) and dividing by 32:
int8_t *src; for(i = 0; i < 64; i += 2){ outsamples[i ] = (*src++ * mul1) >> 5; outsamples[i+1] = (*src++ * mul2) >> 5; blobsize--; }
Discworld 3 BMV
Container
Container consists of two chunks - header and data. Each chunk starts with magic ('BMVi' or 'DATA') and payload size.
Header data (little-endian):
bytes 0- 3 'BMVi' bytes 4- 7 payload size bytes 8-11 slot size bytes 12-15 number of frames bytes 16-17 prefetch slots bytes 18-19 some cache size bytes 20-21 frames per 256 seconds bytes 22-23 audio max size bytes 24-25 audio blob size (41 for new audio coding, otherwise old scheme is used) byte 26 audio ID byte 27 video ID bytes 28-29 frame width bytes 30-31 frame height
Frames stored in data chunk seems to have the same structure as v2, except that palette must not be present and audio is stored in fixed size per chunk audio_max_size
or
audio_max_size - audio_blob_size
if flag 0x40
is set.
Audio
New audio coding stores 64 samples in 41 bytes. Packets for left and right channel are interleaved, coding mode is the last byte in a packet for the left channel and the first one for the right channel.
Compression is done by using delta coding and quantising delta values into 5 bits. Mode byte consists of two nibbles selecting which subpart of the table should be used (high nibble for the first 32 samples).
Samples are packed into 16-bit words, top bits form codeword for two last values (MSB), the rest is divided into three 5-bit groups.
for (i = 0; i < 10; i++) {
code = get_16bits();
last_cw = (last_cw << 1) | (code >> 15);
pred += quant_table[mode][(code >> 10) & 0x1F];
*dst++ = pred;
pred += quant_table[mode][(code >> 5) & 0x1F];
*dst++ = pred;
pred += quant_table[mode][ code & 0x1F];
*dst++ = pred;
}
pred += quant_table[mode][(last_cw >> 5) & 0x1F];
*dst++ = pred;
pred += quant_table[mode][ last_cw & 0x1F];
*dst++ = pred;
Quantisation table
// mode 0x0
0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00,
0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
0xC000, 0xC400, 0xC800, 0xCC00, 0xD000, 0xD400, 0xD800, 0xDC00,
0xE000, 0xE400, 0xE800, 0xEC00, 0xF000, 0xF400, 0xF800, 0xFC00,
// mode 0x1
0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0A00, 0x0C00, 0x0E00,
0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1A00, 0x1C00, 0x1E00,
0xE000, 0xE200, 0xE400, 0xE600, 0xE800, 0xEA00, 0xEC00, 0xEE00,
0xF000, 0xF200, 0xF400, 0xF600, 0xF800, 0xFA00, 0xFC00, 0xFE00,
// mode 0x2
0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700,
0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, 0x0F00,
0xF000, 0xF100, 0xF200, 0xF300, 0xF400, 0xF500, 0xF600, 0xF700,
0xF800, 0xF900, 0xFA00, 0xFB00, 0xFC00, 0xFD00, 0xFE00, 0xFF00,
// mode 0x3
0x0000, 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380,
0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
0xF800, 0xF880, 0xF900, 0xF980, 0xFA00, 0xFA80, 0xFB00, 0xFB80,
0xFC00, 0xFC80, 0xFD00, 0xFD80, 0xFE00, 0xFE80, 0xFF00, 0xFF80,
// mode 0x4
0x0000, 0x0048, 0x0090, 0x00D8, 0x0120, 0x0168, 0x01B0, 0x01F8,
0x0240, 0x0288, 0x02D0, 0x0318, 0x0360, 0x03A8, 0x03F0, 0x0438,
0xFB80, 0xFBC8, 0xFC10, 0xFC58, 0xFCA0, 0xFCE8, 0xFD30, 0xFD78,
0xFDC0, 0xFE08, 0xFE50, 0xFE98, 0xFEE0, 0xFF28, 0xFF70, 0xFFB8,
// mode 0x5
0x0000, 0x0030, 0x0060, 0x0090, 0x00C0, 0x00F0, 0x0120, 0x0150,
0x0180, 0x01B0, 0x01E0, 0x0210, 0x0240, 0x0270, 0x02A0, 0x02D0,
0xFD00, 0xFD30, 0xFD60, 0xFD90, 0xFDC0, 0xFDF0, 0xFE20, 0xFE50,
0xFE80, 0xFEB0, 0xFEE0, 0xFF10, 0xFF40, 0xFF70, 0xFFA0, 0xFFD0,
// mode 0x6
0x0000, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0, 0x00C0, 0x00E0,
0x0100, 0x0120, 0x0140, 0x0160, 0x0180, 0x01A0, 0x01C0, 0x01E0,
0xFE00, 0xFE20, 0xFE40, 0xFE60, 0xFE80, 0xFEA0, 0xFEC0, 0xFEE0,
0xFF00, 0xFF20, 0xFF40, 0xFF60, 0xFF80, 0xFFA0, 0xFFC0, 0xFFE0,
// mode 0x7
0x0000, 0x0016, 0x002C, 0x0042, 0x0058, 0x006E, 0x0084, 0x009A,
0x00B0, 0x00C6, 0x00DC, 0x00F2, 0x0108, 0x011E, 0x0134, 0x014A,
0xFEA0, 0xFEB6, 0xFECC, 0xFEE2, 0xFEF8, 0xFF0E, 0xFF24, 0xFF3A,
0xFF50, 0xFF66, 0xFF7C, 0xFF92, 0xFFA8, 0xFFBE, 0xFFD4, 0xFFEA,
// mode 0x8
0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070,
0x0080, 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0,
0xFF00, 0xFF10, 0xFF20, 0xFF30, 0xFF40, 0xFF50, 0xFF60, 0xFF70,
0xFF80, 0xFF90, 0xFFA0, 0xFFB0, 0xFFC0, 0xFFD0, 0xFFE0, 0xFFF0,
// mode 0x9
0x0000, 0x000B, 0x0016, 0x0021, 0x002C, 0x0037, 0x0042, 0x004D,
0x0058, 0x0063, 0x006E, 0x0079, 0x0084, 0x008F, 0x009A, 0x00A5,
0xFF50, 0xFF5B, 0xFF66, 0xFF71, 0xFF7C, 0xFF87, 0xFF92, 0xFF9D,
0xFFA8, 0xFFB3, 0xFFBE, 0xFFC9, 0xFFD4, 0xFFDF, 0xFFEA, 0xFFF5,
// mode 0xA
0x0000, 0x0008, 0x0010, 0x0018, 0x0020, 0x0028, 0x0030, 0x0038,
0x0040, 0x0048, 0x0050, 0x0058, 0x0060, 0x0068, 0x0070, 0x0078,
0xFF80, 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8,
0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8,
// mode 0xB
0x0000, 0x0006, 0x000C, 0x0012, 0x0018, 0x001E, 0x0024, 0x002A,
0x0030, 0x0036, 0x003C, 0x0042, 0x0048, 0x004E, 0x0054, 0x005A,
0xFFA0, 0xFFA6, 0xFFAC, 0xFFB2, 0xFFB8, 0xFFBE, 0xFFC4, 0xFFCA,
0xFFD0, 0xFFD6, 0xFFDC, 0xFFE2, 0xFFE8, 0xFFEE, 0xFFF4, 0xFFFA,
// mode 0xC
0x0000, 0x0004, 0x0008, 0x000C, 0x0010, 0x0014, 0x0018, 0x001C,
0x0020, 0x0024, 0x0028, 0x002C, 0x0030, 0x0034, 0x0038, 0x003C,
0xFFC0, 0xFFC4, 0xFFC8, 0xFFCC, 0xFFD0, 0xFFD4, 0xFFD8, 0xFFDC,
0xFFE0, 0xFFE4, 0xFFE8, 0xFFEC, 0xFFF0, 0xFFF4, 0xFFF8, 0xFFFC,
// mode 0xD
0x0000, 0x0002, 0x0005, 0x0008, 0x000B, 0x000D, 0x0010, 0x0013,
0x0016, 0x0018, 0x001B, 0x001E, 0x0021, 0x0023, 0x0026, 0x0029,
0xFFD4, 0xFFD6, 0xFFD9, 0xFFDC, 0xFFDF, 0xFFE1, 0xFFE4, 0xFFE7,
0xFFEA, 0xFFEC, 0xFFEF, 0xFFF2, 0xFFF5, 0xFFF7, 0xFFFA, 0xFFFD,
// mode 0xE
0x0000, 0x0001, 0x0003, 0x0005, 0x0007, 0x0008, 0x000A, 0x000C,
0x000E, 0x000F, 0x0011, 0x0013, 0x0015, 0x0016, 0x0018, 0x001A,
0xFFE4, 0xFFE5, 0xFFE7, 0xFFE9, 0xFFEB, 0xFFEC, 0xFFEE, 0xFFF0,
0xFFF2, 0xFFF3, 0xFFF5, 0xFFF7, 0xFFF9, 0xFFFA, 0xFFFC, 0xFFFE,
// mode 0xF
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7,
0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF,