BizHawk/waterbox/ngp/mem.cpp

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]);
}
}
}
}
//=============================================================================