#include "snes9x.h"
#include "memmap.h"
#include "display.h"
#define BIOS_SIZE 0x100000
#define FLASH_SIZE 0x200000
#define PSRAM_SIZE 0x80000
#define Map Memory.Map
#define BlockIsRAM Memory.BlockIsRAM
#define BlockIsROM Memory.BlockIsROM
#define RAM Memory.RAM
#define SRAM Memory.SRAM
#define PSRAM Memory.BSRAM
#define BIOSROM Memory.BIOSROM
#define MAP_BSX Memory.MAP_BSX
#define MAP_CPU Memory.MAP_CPU
#define MAP_PPU Memory.MAP_PPU
#define MAP_NONE Memory.MAP_NONE
#define BSXPPUBASE 0x2180
struct SBSX_RTC
{
int hours;
int minutes;
int seconds;
int ticks;
};
static struct SBSX_RTC BSX_RTC;
static const uint8 flashcard[20] =
{
0x4D, 0x00, 0x50, 0x00,
0x00, 0x00,
0x2B, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const uint8 init2192[32] =
{
00, 00, 00, 00, 00,
01, 01, 00, 00, 00,
00,
00,
00,
10, 10, 10, 10, 10,
10, 10, 10, 10, 10,
00, 00, 00, 00, 00, 00, 00, 00, 00
};
static bool8 FlashMode;
static uint32 FlashSize;
static uint8 *MapROM, *FlashROM;
static void BSX_Map_SNES (void);
static void BSX_Map_LoROM (void);
static void BSX_Map_HiROM (void);
static void BSX_Map_MMC (void);
static void BSX_Map_FlashIO (void);
static void BSX_Map_SRAM (void);
static void BSX_Map_PSRAM (void);
static void BSX_Map_BIOS (void);
static void BSX_Map_RAM (void);
static void BSX_Map_Dirty (void);
static void BSX_Map (void);
static void BSX_Set_Bypass_FlashIO (uint16, uint8);
static uint8 BSX_Get_Bypass_FlashIO (uint16);
static bool8 BSX_LoadBIOS (void);
static void map_psram_mirror_sub (uint32);
static int is_bsx (unsigned char *);
static void BSX_Map_SNES (void)
{
int c;
for (c = 0; c < 0x400; c += 16)
{
Map[c + 0] = Map[c + 0x800] = RAM;
Map[c + 1] = Map[c + 0x801] = RAM;
BlockIsRAM[c + 0] = BlockIsRAM[c + 0x800] = TRUE;
BlockIsRAM[c + 1] = BlockIsRAM[c + 0x801] = TRUE;
Map[c + 2] = Map[c + 0x802] = (uint8 *) MAP_PPU;
Map[c + 3] = Map[c + 0x803] = (uint8 *) MAP_PPU;
Map[c + 4] = Map[c + 0x804] = (uint8 *) MAP_CPU;
Map[c + 5] = Map[c + 0x805] = (uint8 *) MAP_CPU;
Map[c + 6] = Map[c + 0x806] = (uint8 *) MAP_NONE;
Map[c + 7] = Map[c + 0x807] = (uint8 *) MAP_NONE;
}
}
static void BSX_Map_LoROM (void)
{
int i, c;
for (c = 0; c < 0x400; c += 16)
{
for (i = c + 8; i < c + 16; i++)
{
Map[i] = Map[i + 0x800] = &MapROM[(c << 11) % FlashSize] - 0x8000;
BlockIsRAM[i] = BlockIsRAM[i + 0x800] = BSX.write_enable;
BlockIsROM[i] = BlockIsROM[i + 0x800] = !BSX.write_enable;
}
}
for (c = 0; c < 0x400; c += 16)
{
for (i = c; i < c + 8; i++)
Map[i + 0x400] = Map[i + 0xC00] = &MapROM[(c << 11) % FlashSize];
for (i = c + 8; i < c + 16; i++)
Map[i + 0x400] = Map[i + 0xC00] = &MapROM[(c << 11) % FlashSize] - 0x8000;
for (i = c; i < c + 16; i++)
{
BlockIsRAM[i + 0x400] = BlockIsRAM[i + 0xC00] = BSX.write_enable;
BlockIsROM[i + 0x400] = BlockIsROM[i + 0xC00] = !BSX.write_enable;
}
}
}
static void BSX_Map_HiROM (void)
{
int i, c;
for (c = 0; c < 0x400; c += 16)
{
for (i = c + 8; i < c + 16; i++)
{
Map[i] = Map[i + 0x800] = &MapROM[(c << 12) % FlashSize];
BlockIsRAM[i] = BlockIsRAM[i + 0x800] = BSX.write_enable;
BlockIsROM[i] = BlockIsROM[i + 0x800] = !BSX.write_enable;
}
}
for (c = 0; c < 0x400; c += 16)
{
for (i = c; i < c + 16; i++)
{
Map[i + 0x400] = Map[i + 0xC00] = &MapROM[(c << 12) % FlashSize];
BlockIsRAM[i + 0x400] = BlockIsRAM[i + 0xC00] = BSX.write_enable;
BlockIsROM[i + 0x400] = BlockIsROM[i + 0xC00] = !BSX.write_enable;
}
}
}
static void BSX_Map_MMC (void)
{
int c;
for (c = 0x010; c < 0x0F0; c += 16)
{
Map[c + 5] = (uint8 *) MAP_BSX;
BlockIsRAM[c + 5] = BlockIsROM[c + 5] = FALSE;
}
}
static void BSX_Map_FlashIO (void)
{
int c;
if (BSX.MMC[0x0C] || BSX.MMC[0x0D])
{
for (c = 0; c < 16; c++)
{
Map[c + 0xC00] = (uint8 *) MAP_BSX;
BlockIsRAM[c + 0xC00] = TRUE;
BlockIsROM[c + 0xC00] = FALSE;
}
}
}
static void BSX_Map_SRAM (void)
{
int c;
for (c = 0x100; c < 0x180; c += 16)
{
Map[c + 5] = (uint8 *) SRAM + ((c & 0x70) << 8) - 0x5000;
BlockIsRAM[c + 5] = TRUE;
BlockIsROM[c + 5] = FALSE;
}
}
static void map_psram_mirror_sub (uint32 bank)
{
int i, c;
bank <<= 4;
if (BSX.MMC[0x02])
{
for (c = 0; c < 0x100; c += 16)
{
for (i = c; i < c + 16; i++)
{
Map[i + bank] = &PSRAM[(c << 12) % PSRAM_SIZE];
BlockIsRAM[i + bank] = TRUE;
BlockIsROM[i + bank] = FALSE;
}
}
}
else
{
for (c = 0; c < 0x100; c += 16)
{
for (i = c; i < c + 8; i++)
Map[i + bank] = &PSRAM[(c << 11) % PSRAM_SIZE];
for (i = c + 8; i < c + 16; i++)
Map[i + bank] = &PSRAM[(c << 11) % PSRAM_SIZE] - 0x8000;
for (i = c; i < c + 16; i++)
{
BlockIsRAM[i + bank] = TRUE;
BlockIsROM[i + bank] = FALSE;
}
}
}
}
static void BSX_Map_PSRAM (void)
{
int c;
for (c = 0; c < 0x80; c++)
{
Map[c + 0x700] = &PSRAM[((c & 0x70) << 12) % PSRAM_SIZE];
BlockIsRAM[c + 0x700] = TRUE;
BlockIsROM[c + 0x700] = FALSE;
}
for (c = 0x200; c < 0x400; c += 16)
{
Map[c + 6] = &PSRAM[((c & 0x70) << 12) % PSRAM_SIZE];
Map[c + 7] = &PSRAM[((c & 0x70) << 12) % PSRAM_SIZE];
BlockIsRAM[c + 6] = TRUE;
BlockIsRAM[c + 7] = TRUE;
BlockIsROM[c + 6] = FALSE;
BlockIsROM[c + 7] = FALSE;
}
if (!BSX.MMC[0x05])
map_psram_mirror_sub(0x40);
if (!BSX.MMC[0x06])
map_psram_mirror_sub(0x50);
if (!BSX.MMC[0x03])
map_psram_mirror_sub(0x60);
}
static void BSX_Map_BIOS (void)
{
int i,c;
if (BSX.MMC[0x07])
{
for (c = 0; c < 0x200; c += 16)
{
for (i = c + 8; i < c + 16; i++)
{
Map[i] = &BIOSROM[(c << 11) % BIOS_SIZE] - 0x8000;
BlockIsRAM[i] = FALSE;
BlockIsROM[i] = TRUE;
}
}
}
if (BSX.MMC[0x08])
{
for (c = 0; c < 0x200; c += 16)
{
for (i = c + 8; i < c + 16; i++)
{
Map[i + 0x800] = &BIOSROM[(c << 11) % BIOS_SIZE] - 0x8000;
BlockIsRAM[i + 0x800] = FALSE;
BlockIsROM[i + 0x800] = TRUE;
}
}
}
}
static void BSX_Map_RAM (void)
{
int c;
for (c = 0; c < 16; c++)
{
Map[c + 0x7E0] = RAM;
Map[c + 0x7F0] = RAM + 0x10000;
BlockIsRAM[c + 0x7E0] = TRUE;
BlockIsRAM[c + 0x7F0] = TRUE;
BlockIsROM[c + 0x7E0] = FALSE;
BlockIsROM[c + 0x7F0] = FALSE;
}
}
static void BSX_Map_Dirty (void)
{
int i, c;
if (BSX.MMC[0x02])
{
for (c = 0; c < 0x200; c += 16)
{
for (i = c + 8; i < c + 16; i++)
{
Map[i] = Map[i + 0x800] = &MapROM[(c << 12) % FlashSize];
BlockIsRAM[i] = BlockIsRAM[i + 0x800] = BSX.write_enable;
BlockIsROM[i] = BlockIsROM[i + 0x800] = !BSX.write_enable;
}
}
}
else
{
for (c = 0; c < 0x200; c += 16)
{
for (i = c + 8; i < c + 16; i++)
{
Map[i] = Map[i + 0x800] = &MapROM[(c << 11) % FlashSize] - 0x8000;
BlockIsRAM[i] = BlockIsRAM[i + 0x800] = BSX.write_enable;
BlockIsROM[i] = BlockIsROM[i + 0x800] = !BSX.write_enable;
}
}
}
}
static void BSX_Map (void)
{
#ifdef BSX_DEBUG
printf("BS: Remapping\n");
for (int i = 0; i < 32; i++)
printf("BS: MMC %02X: %d\n", i, BSX.MMC[i]);
#endif
memcpy(BSX.prevMMC, BSX.MMC, sizeof(BSX.MMC));
if (BSX.dirty2 && !BSX.dirty)
{
BSX_Map_Dirty();
BSX_Map_BIOS();
BSX.dirty2 = FALSE;
Memory.map_WriteProtectROM();
return;
}
if (BSX.MMC[0x01])
{
MapROM = PSRAM;
FlashSize = PSRAM_SIZE;
}
else
{
MapROM = FlashROM;
FlashSize = FLASH_SIZE;
}
BSX_Map_SNES();
if (BSX.MMC[0x02])
BSX_Map_HiROM();
else
BSX_Map_LoROM();
BSX_Map_PSRAM();
BSX_Map_SRAM();
BSX_Map_RAM();
BSX_Map_BIOS();
BSX_Map_FlashIO();
BSX_Map_MMC();
BSX.dirty = FALSE;
BSX.dirty2 = FALSE;
Memory.map_WriteProtectROM();
}
static uint8 BSX_Get_Bypass_FlashIO (uint16 offset)
{
if (BSX.MMC[0x02])
return (MapROM[offset]);
else
{
if (offset < 0x8000)
return (MapROM[offset]);
else
return (MapROM[offset - 0x8000]);
}
}
static void BSX_Set_Bypass_FlashIO (uint16 offset, uint8 byte)
{
if (BSX.MMC[0x02])
MapROM[offset] = byte;
else
{
if (offset < 0x8000)
MapROM[offset] = byte;
else
MapROM[offset - 0x8000] = byte;
}
}
uint8 S9xGetBSX (uint32 address)
{
uint8 bank = (address >> 16) & 0xFF;
uint16 offset = address & 0xFFFF;
uint8 t = 0;
if ((bank >= 0x01 && bank <= 0x0E) && (offset == 0x5000))
return (BSX.MMC[bank]);
if (bank == 0xC0)
{
t = BSX_Get_Bypass_FlashIO(offset);
switch (offset)
{
case 0x0002:
if (BSX.flash_enable)
t = 0x80;
break;
case 0x5555:
if (BSX.flash_enable)
t = 0x80;
break;
case 0xFF00:
case 0xFF02:
case 0xFF04:
case 0xFF06:
case 0xFF08:
case 0xFF0A:
case 0xFF0C:
case 0xFF0E:
case 0xFF10:
case 0xFF12:
if (BSX.read_enable)
t = flashcard[offset - 0xFF00];
break;
}
}
return (t);
}
void S9xSetBSX (uint8 byte, uint32 address)
{
uint8 bank = (address >> 16) & 0xFF;
uint16 offset = address & 0xFFFF;
if ((bank >= 0x01 && bank <= 0x0E) && (offset == 0x5000))
{
switch (bank)
{
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
case 0x0D:
if (BSX.MMC[bank] != byte)
{
BSX.MMC[bank] = byte;
BSX.dirty = TRUE;
}
break;
case 0x07:
case 0x08:
if (BSX.MMC[bank] != byte)
{
BSX.MMC[bank] = byte;
BSX.dirty2 = TRUE;
}
break;
case 0x0E:
BSX.MMC[bank] = byte;
if (byte && (BSX.dirty || BSX.dirty2))
BSX_Map();
break;
}
}
if (bank == 0xC0)
{
BSX.old_write = BSX.new_write;
BSX.new_write = address;
if (BSX.old_write == BSX.new_write && BSX.write_enable)
{
BSX_Set_Bypass_FlashIO(offset, byte);
return;
}
switch (offset)
{
case 0x0000:
BSX.flash_command <<= 8;
BSX.flash_command |= byte;
if ((BSX.flash_command & 0xFFFF) == 0x38D0)
{
BSX.flash_enable = TRUE;
BSX.read_enable = TRUE;
}
break;
case 0x2AAA:
BSX.flash_command <<= 8;
BSX.flash_command |= byte;
break;
case 0x5555:
BSX.flash_command <<= 8;
BSX.flash_command |= byte;
switch (BSX.flash_command & 0xFFFFFF)
{
case 0xAA55F0:
BSX.flash_enable = FALSE;
BSX.write_enable = FALSE;
BSX.read_enable = FALSE;
break;
case 0xAA55A0:
BSX.old_write = 0;
BSX.new_write = 0;
BSX.flash_enable = TRUE;
BSX.write_enable = TRUE;
BSX_Map();
break;
case 0xAA5570:
BSX.write_enable = FALSE;
BSX_Map();
break;
case 0xAA5580:
case 0xAA5510:
break;
}
break;
}
}
}
uint8 S9xGetBSXPPU (uint16 address)
{
uint8 t;
switch (address)
{
case 0x2188:
t = BSX.PPU[0x2188 - BSXPPUBASE];
break;
case 0x2189:
t = BSX.PPU[0x2189 - BSXPPUBASE];
break;
case 0x218A:
t = BSX.PPU[0x218A - BSXPPUBASE];
break;
case 0x218C:
t = BSX.PPU[0x218C - BSXPPUBASE];
break;
case 0x218E:
t = BSX.PPU[0x218E - BSXPPUBASE];
break;
case 0x218F:
t = BSX.PPU[0x218F - BSXPPUBASE];
break;
case 0x2190:
t = BSX.PPU[0x2190 - BSXPPUBASE];
break;
case 0x2192:
t = BSX.PPU[0x2192 - BSXPPUBASE];
t = BSX.test2192[BSX.out_index++];
if (BSX.out_index == 32)
BSX.out_index = 0;
BSX_RTC.ticks++;
if (BSX_RTC.ticks >= 1000)
{
BSX_RTC.ticks = 0;
BSX_RTC.seconds++;
}
if (BSX_RTC.seconds >= 60)
{
BSX_RTC.seconds = 0;
BSX_RTC.minutes++;
}
if (BSX_RTC.minutes >= 60)
{
BSX_RTC.minutes = 0;
BSX_RTC.hours++;
}
if (BSX_RTC.hours >= 24)
BSX_RTC.hours = 0;
BSX.test2192[10] = BSX_RTC.seconds;
BSX.test2192[11] = BSX_RTC.minutes;
BSX.test2192[12] = BSX_RTC.hours;
break;
case 0x2193:
t = BSX.PPU[0x2193 - BSXPPUBASE] & ~0x0C;
break;
case 0x2194:
t = BSX.PPU[0x2194 - BSXPPUBASE];
break;
case 0x2196:
t = BSX.PPU[0x2196 - BSXPPUBASE];
break;
case 0x2197:
t = BSX.PPU[0x2197 - BSXPPUBASE];
break;
case 0x2199:
t = BSX.PPU[0x2199 - BSXPPUBASE];
break;
default:
t = OpenBus;
break;
}
return (t);
}
void S9xSetBSXPPU (uint8 byte, uint16 address)
{
switch (address)
{
case 0x2188:
BSX.PPU[0x2188 - BSXPPUBASE] = byte;
break;
case 0x2189:
BSX.PPU[0x2189 - BSXPPUBASE] = byte;
break;
case 0x218A:
BSX.PPU[0x218A - BSXPPUBASE] = byte;
break;
case 0x218B:
BSX.PPU[0x218B - BSXPPUBASE] = byte;
break;
case 0x218C:
BSX.PPU[0x218C - BSXPPUBASE] = byte;
break;
case 0x218E:
BSX.PPU[0x218E - BSXPPUBASE] = byte;
break;
case 0x218F:
BSX.PPU[0x218F - BSXPPUBASE] = byte;
BSX.PPU[0x218E - BSXPPUBASE] >>= 1;
BSX.PPU[0x218E - BSXPPUBASE] = BSX.PPU[0x218F - BSXPPUBASE] - BSX.PPU[0x218E - BSXPPUBASE];
BSX.PPU[0x218F - BSXPPUBASE] >>= 1;
BSX.PPU[0x2190 - BSXPPUBASE] = 0x80;
break;
case 0x2191:
BSX.PPU[0x2191 - BSXPPUBASE] = byte;
BSX.out_index = 0;
break;
case 0x2192:
BSX.PPU[0x2192 - BSXPPUBASE] = 0x01;
BSX.PPU[0x2190 - BSXPPUBASE] = 0x80;
break;
case 0x2193:
BSX.PPU[0x2193 - BSXPPUBASE] = byte;
break;
case 0x2194:
BSX.PPU[0x2194 - BSXPPUBASE] = byte;
break;
case 0x2197:
BSX.PPU[0x2197 - BSXPPUBASE] = byte;
break;
case 0x2199:
BSX.PPU[0x2199 - BSXPPUBASE] = byte;
break;
}
}
uint8 * S9xGetBasePointerBSX (uint32 address)
{
return (MapROM);
}
static bool8 BSX_LoadBIOS (void)
{
FILE *fp;
char path[PATH_MAX + 1], name[PATH_MAX + 1];
bool8 r = FALSE;
strcpy(path, S9xGetDirectory(BIOS_DIR));
strcat(path, SLASH_STR);
strcpy(name, path);
strcat(name, "BS-X.bin");
fp = fopen(name, "rb");
if (!fp)
{
strcpy(name, path);
strcat(name, "BS-X.bios");
fp = fopen(name, "rb");
}
if (fp)
{
size_t size;
size = fread((void *) BIOSROM, 1, BIOS_SIZE, fp);
fclose(fp);
if (size == BIOS_SIZE)
r = TRUE;
}
#ifdef BSX_DEBUG
if (r)
printf("BS: BIOS found.\n");
else
printf("BS: BIOS not found!\n");
#endif
return (r);
}
void S9xInitBSX (void)
{
Settings.BS = FALSE;
if (!memcmp(&Memory.ROM[0x7FC0], "Satellaview BS-X ", 21))
{
Settings.BS = TRUE;
Settings.BSXItself = TRUE;
Memory.LoROM = TRUE;
Memory.HiROM = FALSE;
memmove(BIOSROM, Memory.ROM, BIOS_SIZE);
FlashMode = FALSE;
FlashSize = FLASH_SIZE;
BSX.bootup = TRUE;
}
else
{
Settings.BSXItself = FALSE;
int r1, r2;
r1 = (is_bsx(Memory.ROM + 0x7FC0) == 1);
r2 = (is_bsx(Memory.ROM + 0xFFC0) == 1);
Settings.BS = (r1 | r2) ? TRUE : FALSE;
if (Settings.BS)
{
Memory.LoROM = r1 ? TRUE : FALSE;
Memory.HiROM = r2 ? TRUE : FALSE;
uint8 *header = r1 ? Memory.ROM + 0x7FC0 : Memory.ROM + 0xFFC0;
FlashMode = (header[0x18] & 0xEF) == 0x20 ? FALSE : TRUE;
FlashSize = (header[0x19] & 0x20) ? PSRAM_SIZE : FLASH_SIZE;
#ifdef BSX_DEBUG
for (int i = 0; i <= 0x1F; i++)
printf("BS: ROM Header %02X: %02X\n", i, header[i]);
printf("BS: FlashMode: %d, FlashSize: %x\n", FlashMode, FlashSize);
#endif
BSX.bootup = Settings.BSXBootup;
if (!BSX_LoadBIOS())
{
BSX.bootup = FALSE;
memset(BIOSROM, 0, BIOS_SIZE);
}
}
}
if (Settings.BS)
{
MapROM = NULL;
FlashROM = Memory.ROM;
time_t t;
struct tm *tmr;
time(&t);
tmr = localtime(&t);
BSX_RTC.ticks = 0;
memcpy(BSX.test2192, init2192, sizeof(init2192));
BSX.test2192[10] = BSX_RTC.seconds = tmr->tm_sec;
BSX.test2192[11] = BSX_RTC.minutes = tmr->tm_min;
BSX.test2192[12] = BSX_RTC.hours = tmr->tm_hour;
#ifdef BSX_DEBUG
printf("BS: Current Time: %02d:%02d:%02d\n", BSX_RTC.hours, BSX_RTC.minutes, BSX_RTC.seconds);
#endif
SNESGameFixes.SRAMInitialValue = 0x00;
}
}
void S9xResetBSX (void)
{
if (Settings.BSXItself)
memset(Memory.ROM, 0, FLASH_SIZE);
memset(BSX.PPU, 0, sizeof(BSX.PPU));
memset(BSX.MMC, 0, sizeof(BSX.MMC));
memset(BSX.prevMMC, 0, sizeof(BSX.prevMMC));
BSX.dirty = FALSE;
BSX.dirty2 = FALSE;
BSX.flash_enable = FALSE;
BSX.write_enable = FALSE;
BSX.read_enable = FALSE;
BSX.flash_command = 0;
BSX.old_write = 0;
BSX.new_write = 0;
BSX.out_index = 0;
memset(BSX.output, 0, sizeof(BSX.output));
if (BSX.bootup)
BSX.MMC[0x07] = BSX.MMC[0x08] = 0x80;
else
{
BSX.MMC[0x02] = FlashMode ? 0x80: 0;
if (FlashSize == PSRAM_SIZE)
{
memcpy(PSRAM, FlashROM, PSRAM_SIZE);
BSX.MMC[0x01] = 0x80;
BSX.MMC[0x03] = 0x80;
BSX.MMC[0x04] = 0x80;
BSX.MMC[0x0C] = 0x80;
BSX.MMC[0x0D] = 0x80;
}
else
{
BSX.MMC[0x03] = 0x80;
BSX.MMC[0x05] = 0x80;
BSX.MMC[0x06] = 0x80;
}
BSX.MMC[0x0E] = 0x80;
}
BSX_Map();
}
void S9xBSXPostLoadState (void)
{
uint8 temp[16];
bool8 pd1, pd2;
pd1 = BSX.dirty;
pd2 = BSX.dirty2;
memcpy(temp, BSX.MMC, sizeof(BSX.MMC));
memcpy(BSX.MMC, BSX.prevMMC, sizeof(BSX.MMC));
BSX_Map();
memcpy(BSX.MMC, temp, sizeof(BSX.MMC));
BSX.dirty = pd1;
BSX.dirty2 = pd2;
}
static bool valid_normal_bank (unsigned char bankbyte)
{
switch (bankbyte)
{
case 32: case 33: case 48: case 49:
return (true);
break;
}
return (false);
}
static int is_bsx (unsigned char *p)
{
if ((p[26] == 0x33 || p[26] == 0xFF) && (!p[21] || (p[21] & 131) == 128) && valid_normal_bank(p[24]))
{
unsigned char m = p[22];
if (!m && !p[23])
return (2);
if ((m == 0xFF && p[23] == 0xFF) || (!(m & 0xF) && ((m >> 4) - 1 < 12)))
return (1);
}
return (0);
}