690 lines
16 KiB
C++
690 lines
16 KiB
C++
//---------------------------------------------------------------------------
|
|
// NEOPOP : Emulator as in Dreamland
|
|
//
|
|
// Copyright (c) 2001-2002 by neopop_uk
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version. See also the license.txt file for
|
|
// additional informations.
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "neopop.h"
|
|
#include "Z80_interface.h"
|
|
#include "bios.h"
|
|
#include "gfx.h"
|
|
#include "mem.h"
|
|
#include "interrupt.h"
|
|
#include "sound.h"
|
|
#include "flash.h"
|
|
#include "rtc.h"
|
|
|
|
//=============================================================================
|
|
|
|
namespace MDFN_IEN_NGP
|
|
{
|
|
extern uint8 settings_language;
|
|
extern bool lagged;
|
|
extern void (*inputcallback)();
|
|
|
|
//Hack way of returning good Flash status.
|
|
bool FlashStatusEnable = FALSE;
|
|
static uint32 FlashStatus;
|
|
|
|
uint8 CPUExRAM[16384];
|
|
|
|
bool debug_abort_memory = FALSE;
|
|
bool debug_mask_memory_error_messages = FALSE;
|
|
|
|
bool memory_unlock_flash_write = FALSE;
|
|
bool memory_flash_error = FALSE;
|
|
bool memory_flash_command = FALSE;
|
|
|
|
uint8 SC0BUF; // Serial channel 0 buffer.
|
|
uint8 COMMStatus;
|
|
|
|
// In very very very rare conditions(like on embedded platforms with no virtual memory and very limited RAM and
|
|
// malloc happens to return a pointer aligned to a 64KiB boundary), a FastReadMap entry may be NULL even if
|
|
// it points to valid data when it's added to the address of the read, but
|
|
// if this happens, it will only make the emulator slightly slower.
|
|
static uint8 *FastReadMap[256], *FastReadMapReal[256];
|
|
|
|
void SetFRM(void) // Call this function after rom is loaded
|
|
{
|
|
for (unsigned int x = 0; x < 256; x++)
|
|
FastReadMapReal[x] = NULL;
|
|
|
|
for (unsigned int x = 0x20; x <= 0x3f; x++)
|
|
{
|
|
if (ngpc_rom.length > (x * 65536 + 65535 - 0x20000))
|
|
FastReadMapReal[x] = &ngpc_rom.data[x * 65536 - 0x200000] - x * 65536;
|
|
}
|
|
|
|
for (unsigned int x = 0x80; x <= 0x9f; x++)
|
|
{
|
|
if (ngpc_rom.length > (x * 65536 + 65535 - 0x80000))
|
|
FastReadMapReal[x] = &ngpc_rom.data[x * 65536 - 0x800000] - x * 65536;
|
|
}
|
|
}
|
|
|
|
void RecacheFRM(void)
|
|
{
|
|
for (int x = 0; x < 256; x++)
|
|
FastReadMap[x] = FlashStatusEnable ? NULL : FastReadMapReal[x];
|
|
}
|
|
|
|
static void *translate_address_read(uint32 address)
|
|
{
|
|
address &= 0xFFFFFF;
|
|
|
|
//Get Flash status?
|
|
if (FlashStatusEnable)
|
|
{
|
|
if ((address >= ROM_START && address <= ROM_END) || (address >= HIROM_START && address <= HIROM_END))
|
|
{
|
|
FlashStatusEnable = FALSE;
|
|
RecacheFRM();
|
|
if (address == 0x220000 || address == 0x230000)
|
|
{
|
|
FlashStatus = 0xFFFFFFFF;
|
|
return &FlashStatus;
|
|
}
|
|
}
|
|
}
|
|
|
|
//ROM (LOW)
|
|
if (address >= ROM_START && address <= ROM_END)
|
|
{
|
|
if (address < ROM_START + ngpc_rom.length)
|
|
return ngpc_rom.data + (address - ROM_START);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
//ROM (HIGH)
|
|
if (address >= HIROM_START && address <= HIROM_END)
|
|
{
|
|
if (address < HIROM_START + (ngpc_rom.length - 0x200000))
|
|
return ngpc_rom.data + 0x200000 + (address - HIROM_START);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
// ===================================
|
|
|
|
//BIOS Access?
|
|
if ((address & 0xFF0000) == 0xFF0000)
|
|
{
|
|
return ngpc_bios + (address & 0xFFFF); // BIOS ROM
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
static void *translate_address_write(uint32 address)
|
|
{
|
|
address &= 0xFFFFFF;
|
|
|
|
if (memory_unlock_flash_write)
|
|
{
|
|
//ROM (LOW)
|
|
if (address >= ROM_START && address <= ROM_END)
|
|
{
|
|
if (address < ROM_START + ngpc_rom.length)
|
|
return ngpc_rom.data + (address - ROM_START);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
//ROM (HIGH)
|
|
if (address >= HIROM_START && address <= HIROM_END)
|
|
{
|
|
if (address < HIROM_START + (ngpc_rom.length - 0x200000))
|
|
return ngpc_rom.data + 0x200000 + (address - HIROM_START);
|
|
else
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//ROM (LOW)
|
|
if (address >= ROM_START && address <= ROM_END)
|
|
{
|
|
//Ignore Flash commands
|
|
if (address == 0x202AAA || address == 0x205555)
|
|
{
|
|
// system_debug_message("%06X: Enable Flash command from %06X", pc, address);
|
|
memory_flash_command = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
//Set Flash status reading?
|
|
if (address == 0x220000 || address == 0x230000)
|
|
{
|
|
// system_debug_message("%06X: Flash status read from %06X", pc, address);
|
|
FlashStatusEnable = TRUE;
|
|
RecacheFRM();
|
|
return NULL;
|
|
}
|
|
|
|
if (memory_flash_command)
|
|
{
|
|
//Write the 256byte block around the flash data
|
|
flash_write(address & 0xFFFF00, 256);
|
|
|
|
//Need to issue a new command before writing will work again.
|
|
memory_flash_command = FALSE;
|
|
|
|
// system_debug_message("%06X: Direct Flash write to %06X", pc, address & 0xFFFF00);
|
|
// system_debug_stop();
|
|
|
|
//Write to the rom itself.
|
|
if (address < ROM_START + ngpc_rom.length)
|
|
return ngpc_rom.data + (address - ROM_START);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===================================
|
|
return NULL;
|
|
}
|
|
|
|
/* WARNING: 32-bit loads and stores apparently DON'T have to be 4-byte-aligned(so we must +2 instead of |2). */
|
|
/* Treat all 32-bit operations as two 16-bit operations */
|
|
uint8 loadB(uint32 address)
|
|
{
|
|
address &= 0xFFFFFF;
|
|
|
|
if (FastReadMap[address >> 16])
|
|
return (FastReadMap[address >> 16][address]);
|
|
|
|
if (address == 0x6f82)
|
|
{
|
|
lagged = false;
|
|
if (inputcallback)
|
|
inputcallback();
|
|
}
|
|
|
|
uint8 *ptr = (uint8 *)translate_address_read(address);
|
|
|
|
if (ptr)
|
|
return *ptr;
|
|
|
|
if (address >= 0x8000 && address <= 0xbfff)
|
|
return (NGPGfx->read8(address));
|
|
if (address >= 0x4000 && address <= 0x7fff)
|
|
{
|
|
return (*(uint8 *)(CPUExRAM + address - 0x4000));
|
|
}
|
|
|
|
if (address >= 0x70 && address <= 0x7F)
|
|
{
|
|
return (int_read8(address));
|
|
}
|
|
|
|
if (address >= 0x90 && address <= 0x97)
|
|
{
|
|
return (rtc_read8(address));
|
|
}
|
|
|
|
if (address >= 0x20 && address <= 0x29)
|
|
{
|
|
return (timer_read8(address));
|
|
}
|
|
|
|
if (address == 0x50)
|
|
return (SC0BUF);
|
|
|
|
if (address == 0xBC)
|
|
return Z80_ReadComm();
|
|
|
|
//printf("UNK B R: %08x\n", address);
|
|
|
|
return (0);
|
|
}
|
|
|
|
uint16 loadW(uint32 address)
|
|
{
|
|
address &= 0xFFFFFF;
|
|
|
|
if (address & 1)
|
|
{
|
|
uint16 ret;
|
|
|
|
ret = loadB(address);
|
|
ret |= loadB(address + 1) << 8;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
if (FastReadMap[address >> 16])
|
|
return (MDFN_de16lsb<true>(&FastReadMap[address >> 16][address]));
|
|
|
|
if (address == 0x6f82)
|
|
{
|
|
lagged = false;
|
|
if (inputcallback)
|
|
inputcallback();
|
|
}
|
|
|
|
uint16 *ptr = (uint16 *)translate_address_read(address);
|
|
if (ptr)
|
|
return MDFN_de16lsb<true>(ptr);
|
|
|
|
if (address >= 0x8000 && address <= 0xbfff)
|
|
return (NGPGfx->read16(address));
|
|
|
|
if (address >= 0x4000 && address <= 0x7fff)
|
|
{
|
|
return (MDFN_de16lsb<true>(CPUExRAM + address - 0x4000));
|
|
}
|
|
if (address == 0x50)
|
|
return (SC0BUF);
|
|
|
|
if (address >= 0x70 && address <= 0x7F)
|
|
{
|
|
uint16 ret;
|
|
|
|
ret = int_read8(address);
|
|
ret |= int_read8(address + 1) << 8;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
if (address >= 0x90 && address <= 0x97)
|
|
{
|
|
uint16 ret;
|
|
|
|
ret = rtc_read8(address);
|
|
ret |= rtc_read8(address + 1) << 8;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
if (address >= 0x20 && address <= 0x29)
|
|
{
|
|
uint16 ret;
|
|
|
|
ret = timer_read8(address);
|
|
ret |= timer_read8(address + 1) << 8;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
if (address == 0xBC)
|
|
return Z80_ReadComm();
|
|
|
|
//printf("UNK W R: %08x\n", address);
|
|
|
|
return (0);
|
|
}
|
|
|
|
uint32 loadL(uint32 address)
|
|
{
|
|
uint32 ret;
|
|
|
|
ret = loadW(address);
|
|
ret |= loadW(address + 2) << 16;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
void storeB(uint32 address, uint8 data)
|
|
{
|
|
address &= 0xFFFFFF;
|
|
|
|
if (address >= 0x8000 && address <= 0xbfff)
|
|
{
|
|
NGPGfx->write8(address, data);
|
|
return;
|
|
}
|
|
if (address >= 0x4000 && address <= 0x7fff)
|
|
{
|
|
*(uint8 *)(CPUExRAM + address - 0x4000) = data;
|
|
return;
|
|
}
|
|
if (address >= 0x70 && address <= 0x7F)
|
|
{
|
|
int_write8(address, data);
|
|
return;
|
|
}
|
|
if (address >= 0x20 && address <= 0x29)
|
|
{
|
|
timer_write8(address, data);
|
|
return;
|
|
}
|
|
|
|
if (address == 0x50)
|
|
{
|
|
SC0BUF = data;
|
|
return;
|
|
}
|
|
|
|
if (address == 0x6f) // Watchdog timer
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (address == 0xb2) // Comm?
|
|
{
|
|
COMMStatus = data & 1;
|
|
return;
|
|
}
|
|
|
|
if (address == 0xb9)
|
|
{
|
|
if (data == 0x55)
|
|
Z80_SetEnable(1);
|
|
else if (data == 0xAA)
|
|
Z80_SetEnable(0);
|
|
return;
|
|
}
|
|
|
|
if (address == 0xb8)
|
|
{
|
|
if (data == 0x55)
|
|
MDFNNGPCSOUND_SetEnable(1);
|
|
else if (data == 0xAA)
|
|
MDFNNGPCSOUND_SetEnable(0);
|
|
return;
|
|
}
|
|
|
|
if (address == 0xBA)
|
|
{
|
|
Z80_nmi();
|
|
return;
|
|
}
|
|
|
|
if (address == 0xBC)
|
|
{
|
|
Z80_WriteComm(data);
|
|
return;
|
|
}
|
|
|
|
if (address >= 0xa0 && address <= 0xA3)
|
|
{
|
|
if (!Z80_IsEnabled())
|
|
{
|
|
if (address == 0xA1)
|
|
Write_SoundChipLeft(data);
|
|
else if (address == 0xA0)
|
|
Write_SoundChipRight(data);
|
|
}
|
|
//DAC Write
|
|
if (address == 0xA2)
|
|
{
|
|
dac_write_left(data);
|
|
}
|
|
else if (address == 0xA3)
|
|
{
|
|
dac_write_right(data);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//printf("%08x %02x\n", address, data);
|
|
uint8 *ptr = (uint8 *)translate_address_write(address);
|
|
|
|
//Write
|
|
if (ptr)
|
|
{
|
|
*ptr = data;
|
|
}
|
|
//else
|
|
// printf("ACK: %08x %02x\n", address, data);
|
|
}
|
|
|
|
void storeW(uint32 address, uint16 data)
|
|
{
|
|
address &= 0xFFFFFF;
|
|
|
|
if (address & 1)
|
|
{
|
|
storeB(address + 0, data & 0xFF);
|
|
storeB(address + 1, data >> 8);
|
|
return;
|
|
}
|
|
|
|
if (address >= 0x8000 && address <= 0xbfff)
|
|
{
|
|
NGPGfx->write16(address, data);
|
|
return;
|
|
}
|
|
if (address >= 0x4000 && address <= 0x7fff)
|
|
{
|
|
MDFN_en16lsb<true>(CPUExRAM + address - 0x4000, data);
|
|
return;
|
|
}
|
|
if (address >= 0x70 && address <= 0x7F)
|
|
{
|
|
int_write8(address, data & 0xFF);
|
|
int_write8(address + 1, data >> 8);
|
|
return;
|
|
}
|
|
|
|
if (address >= 0x20 && address <= 0x29)
|
|
{
|
|
timer_write8(address, data & 0xFF);
|
|
timer_write8(address + 1, data >> 8);
|
|
}
|
|
|
|
if (address == 0x50)
|
|
{
|
|
SC0BUF = data & 0xFF;
|
|
return;
|
|
}
|
|
|
|
if (address == 0x6e) // Watchdog timer(technically 0x6f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (address == 0xb2) // Comm?
|
|
{
|
|
COMMStatus = data & 1;
|
|
return;
|
|
}
|
|
|
|
if (address == 0xb8)
|
|
{
|
|
if ((data & 0xFF00) == 0x5500)
|
|
Z80_SetEnable(1);
|
|
else if ((data & 0xFF00) == 0xAA00)
|
|
Z80_SetEnable(0);
|
|
|
|
if ((data & 0xFF) == 0x55)
|
|
MDFNNGPCSOUND_SetEnable(1);
|
|
else if ((data & 0xFF) == 0xAA)
|
|
MDFNNGPCSOUND_SetEnable(0);
|
|
return;
|
|
}
|
|
|
|
if (address == 0xBA)
|
|
{
|
|
Z80_nmi();
|
|
return;
|
|
}
|
|
|
|
if (address == 0xBC)
|
|
{
|
|
Z80_WriteComm(data);
|
|
return;
|
|
}
|
|
|
|
if (address >= 0xa0 && address <= 0xA3)
|
|
{
|
|
storeB(address, data & 0xFF);
|
|
storeB(address + 1, data >> 8);
|
|
return;
|
|
}
|
|
|
|
uint16 *ptr = (uint16 *)translate_address_write(address);
|
|
|
|
//Write
|
|
if (ptr)
|
|
{
|
|
MDFN_en16lsb<true>(ptr, data);
|
|
}
|
|
//else
|
|
// printf("ACK16: %08x %04x\n", address, data);
|
|
}
|
|
|
|
void storeL(uint32 address, uint32 data)
|
|
{
|
|
storeW(address, data & 0xFFFF);
|
|
storeW(address + 2, data >> 16);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
static const uint8 systemMemory[] =
|
|
{
|
|
// 0x00 // 0x08
|
|
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0xFF, 0xFF,
|
|
// 0x10 // 0x18
|
|
0x34, 0x3C, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x3F, 0xFF, 0x2D, 0x01, 0xFF, 0xFF, 0x03, 0xB2,
|
|
// 0x20 // 0x28
|
|
0x80, 0x00, 0x01, 0x90, 0x03, 0xB0, 0x90, 0x62, 0x05, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x4C, 0x4C,
|
|
// 0x30 // 0x38
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0xFF, 0x80, 0x7F,
|
|
// 0x40 // 0x48
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x50 // 0x58
|
|
0x00, 0x20, 0x69, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
// 0x60 // 0x68
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x03, 0x03, 0x02, 0x00, 0x00, 0x4E,
|
|
// 0x70 // 0x78
|
|
0x02, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x80 // 0x88
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x90 // 0x98
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0xA0 // 0xA8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0xB0 // 0xB8
|
|
0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0xC0 // 0xC8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0xD0 // 0xD8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0xE0 // 0xE8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0xF0 // 0xF8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
|
|
//=============================================================================
|
|
|
|
void reset_memory(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
FlashStatusEnable = FALSE;
|
|
RecacheFRM();
|
|
|
|
memory_flash_command = FALSE;
|
|
|
|
//=============================================================================
|
|
//000000 -> 000100 CPU Internal RAM (Timers/DMA/Z80)
|
|
//=====================================================
|
|
|
|
for (i = 0; i < sizeof(systemMemory); i++)
|
|
storeB(i, systemMemory[i]);
|
|
|
|
//=============================================================================
|
|
//006C00 -> 006FFF BIOS Workspace
|
|
//==================================
|
|
|
|
storeL(0x6C00, MDFN_de32lsb(rom_header->startPC)); //Start
|
|
|
|
storeW(0x6C04, MDFN_de16lsb(rom_header->catalog));
|
|
storeW(0x6E82, MDFN_de16lsb(rom_header->catalog));
|
|
|
|
storeB(0x6C06, rom_header->subCatalog);
|
|
storeB(0x6E84, rom_header->subCatalog);
|
|
|
|
for (i = 0; i < 12; i++)
|
|
storeB(0x6c08 + i, ngpc_rom.data[0x24 + i]);
|
|
|
|
storeB(0x6C58, 0x01);
|
|
|
|
//32MBit cart?
|
|
if (ngpc_rom.length > 0x200000)
|
|
storeB(0x6C59, 0x01);
|
|
else
|
|
storeB(0x6C59, 0x00);
|
|
|
|
storeB(0x6C55, 1); //Commercial game
|
|
|
|
storeB(0x6F80, 0xFF); //Lots of battery power!
|
|
storeB(0x6F81, 0x03);
|
|
|
|
storeB(0x6F84, 0x40); // "Power On" startup
|
|
storeB(0x6F85, 0x00); // No shutdown request
|
|
storeB(0x6F86, 0x00); // No user answer (?)
|
|
|
|
//Language: 0 = Japanese, 1 = English
|
|
storeB(0x6F87, settings_language);
|
|
|
|
//Color Mode Selection: 0x00 = B&W, 0x10 = Colour
|
|
storeB(0x6F91, rom_header->mode);
|
|
storeB(0x6F95, rom_header->mode);
|
|
|
|
//Interrupt table
|
|
for (i = 0; i < 0x12; i++)
|
|
storeL(0x6FB8 + i * 4, 0x00FF23DF);
|
|
|
|
//=============================================================================
|
|
//008000 -> 00BFFF Video RAM
|
|
//=============================
|
|
|
|
storeB(0x8000, 0xC0); // Both interrupts allowed
|
|
|
|
//Hardware window
|
|
storeB(0x8002, 0x00);
|
|
storeB(0x8003, 0x00);
|
|
storeB(0x8004, 0xFF);
|
|
storeB(0x8005, 0xFF);
|
|
|
|
storeB(0x8006, 0xc6); // Frame Rate Register
|
|
|
|
storeB(0x8012, 0x00); // NEG / OOWC setting.
|
|
|
|
storeB(0x8118, 0x80); // BGC on!
|
|
|
|
storeB(0x83E0, 0xFF); // Default background colour
|
|
storeB(0x83E1, 0x0F);
|
|
|
|
storeB(0x83F0, 0xFF); // Default window colour
|
|
storeB(0x83F1, 0x0F);
|
|
|
|
storeB(0x8400, 0xFF); // LED on
|
|
storeB(0x8402, 0x80); // Flash cycle = 1.3s
|
|
|
|
storeB(0x87E2, loadB(0x6F95) ? 0x00 : 0x80);
|
|
|
|
//
|
|
// Metal Slug - 2nd Mission oddly relies on a specific character RAM pattern.
|
|
//
|
|
{
|
|
static const uint8 char_data[64] = {
|
|
255, 63, 255, 255, 0, 252, 255, 255, 255, 63, 3, 0, 255, 255, 255, 255,
|
|
240, 243, 252, 243, 255, 3, 255, 195, 255, 243, 243, 243, 240, 243, 240, 195,
|
|
207, 15, 207, 15, 207, 15, 207, 207, 207, 255, 207, 255, 207, 255, 207, 63,
|
|
255, 192, 252, 195, 240, 207, 192, 255, 192, 255, 240, 207, 252, 195, 255, 192};
|
|
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
storeB(0xA1C0 + i, char_data[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|