NINTV-DS/arm9/source/emucore/JLP.cpp

324 lines
12 KiB
C++

// =====================================================================================
// Copyright (c) 2021-2023 Dave Bernazzani (wavemotion-dave)
//
// Copying and distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted in any medium without
// royalty provided the this copyright notice is used and wavemotion-dave (NINTV-DS)
// and Kyle Davis (BLISS) are thanked profusely.
//
// The NINTV-DS emulator is offered as-is, without any warranty.
// =====================================================================================
#include <nds.h>
#include <stdio.h>
#include <fat.h>
#include <dirent.h>
#include <unistd.h>
#include "JLP.h"
#include "RAM.h"
#include "Rip.h"
#include "../config.h"
#include "../ds_tools.h"
extern Rip *currentRip;
JLP::JLP()
: RAM(JLP_RAM_SIZE, JLP_RAM_ADDRESS, 0xFFFF, 0xFFFF)
{}
void JLP::reset()
{
enabled = TRUE;
for (UINT16 i = 0; i < JLP_RAM_SIZE; i++)
jlp_ram[i] = 0xFFFF;
jlp_ram[JLP_RAM_SIZE-1] = 0; /* The last byte of jlp RAM reads back as 0 */
jlp_ram[0x23] = 0; /* First valid flash row number */
jlp_ram[0x24] = NUM_JLP_ROWS; /* Last valid flash row number */
jlp_ram[0x2D] = 0; /* Command regs read as 0 */
jlp_ram[0x2E] = 0; /* Command regs read as 0 */
jlp_ram[0x2F] = 0; /* Command regs read as 0 */
flash_read = 1; // Force flash to read...
flash_write_time = 0; // And reset the time to write...
}
// If the JLP flash needs to be written, we write it to the backing file. We do it this way so that quick-succession writes
// to the flash do not force a write to the backing file which is slow and wasteful... so we have a 2 second backing timer.
void JLP::tick_one_second(void)
{
if (flash_write_time > 0)
{
if (--flash_write_time == 0)
{
WriteFlashFile();
}
}
}
UINT16 JLP::peek(UINT16 location)
{
if (location == 0x9FFE) {return (UINT16)random();}
return jlp_ram[(location&readAddressMask) - this->location];
}
UINT32 JLP::crc16(UINT16 data, UINT16 crc)
{
crc ^= data;
for (int i = 0; i < 16; i++)
{
crc = (crc >> 1) ^ (crc & 1 ? JLP_CRC_POLY : 0);
}
return crc;
}
char flash_filename[128];
void JLP::GetFlashFilename(void)
{
if (myGlobalConfig.save_dir == 1)
{
DIR* dir = opendir("/roms/sav"); // See if directory exists
if (dir) closedir(dir); // Directory exists. All good.
else mkdir("/roms/sav", 0777); // Doesn't exist - make it...
strcpy(flash_filename, "/roms/sav/");
}
else if (myGlobalConfig.save_dir == 2)
{
DIR* dir = opendir("/roms/intv/sav"); // See if directory exists
if (dir) closedir(dir); // Directory exists. All good.
else mkdir("/roms/intv/sav", 0777); // Doesn't exist - make it...
strcpy(flash_filename, "/roms/intv/sav/");
}
else if (myGlobalConfig.save_dir == 3)
{
DIR* dir = opendir("/data/sav"); // See if directory exists
if (dir) closedir(dir); // Directory exists. All good.
else mkdir("/data/sav", 0777); // Doesn't exist - make it...
strcpy(flash_filename, "/data/sav/");
}
else strcpy(flash_filename, "");
strcat(flash_filename, currentRip->GetFileName());
flash_filename[strlen(flash_filename)-4] = 0;
strcat(flash_filename, ".jlp");
}
void JLP::ReadFlashFile(void)
{
FILE *fp;
GetFlashFilename();
fp = fopen(flash_filename, "rb");
if (fp != NULL)
{
fread(jlp_flash, 1, JLP_FLASH_SIZE, fp);
fclose(fp);
}
else
{
memset(jlp_flash, 0xFF, JLP_FLASH_SIZE);
}
flash_read = 0;
}
void JLP::WriteFlashFile(void)
{
FILE *fp;
dsPrintValue(23,0,0, (char*)"JLP FLASH");
GetFlashFilename();
fp = fopen(flash_filename, "wb");
if (fp != NULL)
{
fwrite(jlp_flash, 1, JLP_FLASH_SIZE, fp);
fclose(fp);
}
dsPrintValue(23,0,0,(char*)" ");
}
void JLP::ScheduleWriteFlashFile(void)
{
flash_write_time = 2;
}
void JLP::RamToFlash(void)
{
UINT32 addr = jlp_ram[(0x8025&readAddressMask) - this->location] - JLP_RAM_ADDRESS;
UINT32 row = (jlp_ram[(0x8026&readAddressMask) - this->location] - jlp_ram[(0x8023&readAddressMask) - this->location]) * 192;
int i, a;
if (jlp_ram[(0x8026&readAddressMask) - this->location] > NUM_JLP_ROWS) return;
if (flash_read) ReadFlashFile();
/* Refuse to write if row isn't empty. */
for (i = 0; i < 192; i++)
if (jlp_flash[row + i] != 0xFF)
{
return;
}
/* Explicitly copy little-endian regardless of machine endian */
for (i = a = 0; i < 192; i += 2, a++)
{
jlp_flash[row + i + 0] = jlp_ram[addr + a] & 0xFF;
jlp_flash[row + i + 1] = jlp_ram[addr + a] >> 8;
}
ScheduleWriteFlashFile();
}
void JLP::FlashToRam(void)
{
UINT32 addr = jlp_ram[(0x8025&readAddressMask) - this->location] - JLP_RAM_ADDRESS;
UINT32 row = (jlp_ram[(0x8026&readAddressMask) - this->location] - jlp_ram[(0x8023&readAddressMask) - this->location]) * 192;
int i, a;
if (jlp_ram[(0x8026&readAddressMask) - this->location] > NUM_JLP_ROWS) return;
// Check to see if we need to read in the flash file...
if (flash_read) ReadFlashFile();
/* Explicitly copy little-endian regardless of machine endian */
for (i = a = 0; i < 192; i += 2, a++)
{
UINT16 lo = jlp_flash[row + i + 0];
UINT16 hi = jlp_flash[row + i + 1];
jlp_ram[addr + a] = lo | (hi << 8);
}
}
void JLP::EraseSector(void)
{
UINT32 row = ((jlp_ram[(0x8026&readAddressMask) - this->location] - jlp_ram[(0x8023&readAddressMask) - this->location]) & -8) * 192;
if (jlp_ram[(0x8026&readAddressMask) - this->location] > NUM_JLP_ROWS) return;
if (flash_read) ReadFlashFile();
memset((void *)&jlp_flash[row], 0xFF, 192 * 8);
ScheduleWriteFlashFile();
}
void JLP::poke(UINT16 location, UINT16 value)
{
UINT32 prod=0, quot=0, rem=0;
if ((location >= 0x8040))
{
jlp_ram[(location&writeAddressMask)-this->location] = value;
}
/* -------------------------------------------------------------------- */
/* Check for mult/div writes */
/* $9F8(0,1): s16($9F80) x s16($9F81) -> s32($9F8F:$9F8E) */
/* $9F8(2,3): s16($9F82) x u16($9F83) -> s32($9F8F:$9F8E) */
/* $9F8(4,5): u16($9F84) x s16($9F85) -> s32($9F8F:$9F8E) */
/* $9F8(6,7): u16($9F86) x u16($9F87) -> u32($9F8F:$9F8E) */
/* $9F8(8,9): s16($9F88) / s16($9F89) -> quot($9F8E), rem($9F8F) */
/* $9F8(A,B): u16($9F8A) / u16($9F8B) -> quot($9F8E), rem($9F8F) */
/* -------------------------------------------------------------------- */
switch(location)
{
case 0x9F80:
case 0x9F81:
prod = (UINT32) ((INT32)(INT16)jlp_ram[(0x9F80&readAddressMask) - this->location] * (INT32)(INT16)jlp_ram[(0x9F81&readAddressMask) - this->location]);
jlp_ram[(0x9F8E&writeAddressMask)-this->location] = prod & 0xFFFF;
jlp_ram[(0x9F8F&writeAddressMask)-this->location] = prod >> 16;
break;
case 0x9F82:
case 0x9F83:
prod = (UINT32) ((INT32)(INT16)jlp_ram[(0x9F82&readAddressMask) - this->location] * (INT32)(UINT16)jlp_ram[(0x9F83&readAddressMask) - this->location]);
jlp_ram[(0x9F8E&writeAddressMask)-this->location] = prod & 0xFFFF;
jlp_ram[(0x9F8F&writeAddressMask)-this->location] = prod >> 16;
break;
case 0x9F84:
case 0x9F85:
prod = (UINT32) ((INT32)(UINT16)jlp_ram[(0x9F84&readAddressMask) - this->location] * (INT32)(INT16)jlp_ram[(0x9F85&readAddressMask) - this->location]);
jlp_ram[(0x9F8E&writeAddressMask)-this->location] = prod & 0xFFFF;
jlp_ram[(0x9F8F&writeAddressMask)-this->location] = prod >> 16;
break;
case 0x9F86:
case 0x9F87:
prod = (UINT32) ((UINT32)(UINT16)jlp_ram[(0x9F86&readAddressMask) - this->location] * (UINT32)(UINT16)jlp_ram[(0x9F87&readAddressMask) - this->location]);
jlp_ram[(0x9F8E&writeAddressMask)-this->location] = prod & 0xFFFF;
jlp_ram[(0x9F8F&writeAddressMask)-this->location] = prod >> 16;
break;
case 0x9F88:
case 0x9F89:
if (jlp_ram[(0x9F89&readAddressMask) - this->location] != 0)
{
quot = (UINT32) ((INT32)(INT16)jlp_ram[(0x9F88&readAddressMask) - this->location] / (INT32)(INT16)jlp_ram[(0x9F89&readAddressMask) - this->location]);
rem = (UINT32) ((INT32)(INT16)jlp_ram[(0x9F88&readAddressMask) - this->location] % (INT32)(INT16)jlp_ram[(0x9F89&readAddressMask) - this->location]);
}
jlp_ram[(0x9F8E&writeAddressMask)-this->location] = quot;
jlp_ram[(0x9F8F&writeAddressMask)-this->location] = rem;
break;
case 0x9F8A:
case 0x9F8B:
if (jlp_ram[(0x9F8B&readAddressMask) - this->location] != 0)
{
quot = (UINT32) ((UINT32)(UINT16)jlp_ram[(0x9F8A&readAddressMask) - this->location] / (UINT32)(UINT16)jlp_ram[(0x9F8B&readAddressMask) - this->location]);
rem = (UINT32) ((UINT32)(UINT16)jlp_ram[(0x9F8A&readAddressMask) - this->location] % (UINT32)(UINT16)jlp_ram[(0x9F8B&readAddressMask) - this->location]);
}
jlp_ram[(0x9F8E&writeAddressMask)-this->location] = quot;
jlp_ram[(0x9F8F&writeAddressMask)-this->location] = rem;
break;
//CRC-16 accelerator at $9FFC / $9FFD (poly 0xAD52, right-shifting)
case 0x9FFC:
jlp_ram[(0x9FFD&writeAddressMask)-this->location] = crc16(value, (UINT16)jlp_ram[(0x9FFD&readAddressMask) - this->location]);
break;
case 0x9FFD:
jlp_ram[(0x9FFD&writeAddressMask)-this->location] = value;
break;
/* -------------------------------------------------------------------- */
/* Check for Save Game Arch V2 writes */
/* */
/* $8025 JLP RAM address to operate on */
/* $8026 Flash row to operate on */
/* */
/* $802D Copy JLP RAM to flash row (unlock: $C0DE) */
/* $802E Copy flash row to JLP RAM (unlock: $DEC0) */
/* $802F Erase flash sector (unlock: $BEEF) */
/* -------------------------------------------------------------------- */
case 0x8025:
case 0x8026:
jlp_ram[(location & writeAddressMask)-this->location] = value;
break;
case 0x802D:
if (value == 0xC0DE) RamToFlash();
break;
case 0x802E:
if (value == 0xDEC0) FlashToRam();
break;
case 0x802F:
if (value == 0xBEEF) EraseSector();
break;
}
}
void JLP::getState(JLPState *state)
{
for (int i=0; i<JLP_RAM_SIZE; i++) state->jlp_ram[i] = jlp_ram[i];
}
void JLP::setState(JLPState *state)
{
for (int i=0; i<JLP_RAM_SIZE; i++) jlp_ram[i] = state->jlp_ram[i];
flash_read = 1; // Force flash to read...
}