mirror of
https://github.com/wavemotion-dave/NINTV-DS.git
synced 2025-06-18 13:55:33 -04:00
1419 lines
54 KiB
C++
1419 lines
54 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 <nds/fifomessages.h>
|
|
|
|
#include<stdio.h>
|
|
|
|
#include <fat.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
|
|
#include "ds_tools.h"
|
|
#include "videoaud.h"
|
|
#include "savestate.h"
|
|
#include "config.h"
|
|
#include "cheat.h"
|
|
#include "manual.h"
|
|
#include "bgBottom.h"
|
|
#include "bgTop.h"
|
|
#include "bgMenu-Green.h"
|
|
#include "bgMenu-White.h"
|
|
#include "Emulator.h"
|
|
#include "Rip.h"
|
|
#include "highscore.h"
|
|
#include "overlay.h"
|
|
#include "loadgame.h"
|
|
#include "debugger.h"
|
|
#include "AudioMixer.h"
|
|
#include "HandController.h"
|
|
#include "printf.h"
|
|
|
|
// --------------------------------------------------------
|
|
// A set of boolean values so we know what to load and
|
|
// how to load it. Put them in fast memory for tiny boost.
|
|
// --------------------------------------------------------
|
|
UINT8 bStartSoundFifo __attribute__((section(".dtcm"))) = false;
|
|
UINT8 bUseJLP __attribute__((section(".dtcm"))) = false;
|
|
UINT8 bUseECS __attribute__((section(".dtcm"))) = false;
|
|
UINT8 bUseIVoice __attribute__((section(".dtcm"))) = false;
|
|
UINT8 bInitEmulator __attribute__((section(".dtcm"))) = false;
|
|
UINT8 bUseDiscOverlay __attribute__((section(".dtcm"))) = false;
|
|
UINT8 bGameLoaded __attribute__((section(".dtcm"))) = false;
|
|
|
|
// -------------------------------------------------------------
|
|
// This one is accessed rather often so we'll put it in .dtcm
|
|
// -------------------------------------------------------------
|
|
UINT8 b_dsi_mode __attribute__((section(".dtcm"))) = true;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// The user can configure how many frames should be run per second (FPS).
|
|
// This is useful for games like Treasure of Tarmin where the user might
|
|
// want to run the game at 120% speed to get faster gameplay in the same time.
|
|
// ------------------------------------------------------------------------------
|
|
UINT16 target_frames[] __attribute__((section(".dtcm"))) = {60, 66, 72, 78, 84, 90, 999};
|
|
UINT32 target_frame_timing[] __attribute__((section(".dtcm"))) = {546, 496, 454, 420, 390, 364, 0};
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// Here are the main classes for the emulator, the RIP (game rom), video bus, etc.
|
|
// ---------------------------------------------------------------------------------
|
|
RunState runState __attribute__((section(".dtcm"))) = Stopped;
|
|
Emulator *currentEmu __attribute__((section(".dtcm"))) = NULL;
|
|
Rip *currentRip __attribute__((section(".dtcm"))) = NULL;
|
|
VideoBus *videoBus __attribute__((section(".dtcm"))) = NULL;
|
|
AudioMixer *audioMixer __attribute__((section(".dtcm"))) = NULL;
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// Some emulator frame calcualtions and once/second computations for frame rate...
|
|
// ---------------------------------------------------------------------------------
|
|
UINT16 emu_frames=0;
|
|
UINT16 frames_per_sec_calc=0;
|
|
UINT8 oneSecTick=FALSE;
|
|
bool bIsFatalError = false;
|
|
|
|
// -------------------------------------------------------------
|
|
// Background screen buffer indexes for the DS video engine...
|
|
// -------------------------------------------------------------
|
|
int bg0, bg0b, bg1b;
|
|
|
|
// -------------------------------------------------------------
|
|
// For the ECS Keybaord (if present)
|
|
// -------------------------------------------------------------
|
|
extern UINT8 ecs_key_pressed;
|
|
|
|
// ---------------------------------------------------------------
|
|
// When we hit our target frames per second (default NTSC 60 FPS)
|
|
// we start again - clear the timer and clear the counter...
|
|
// ---------------------------------------------------------------
|
|
void reset_emu_frames(void)
|
|
{
|
|
TIMER0_CR=0;
|
|
TIMER0_DATA=0;
|
|
TIMER0_CR=TIMER_ENABLE | TIMER_DIV_1024;
|
|
emu_frames=0;
|
|
}
|
|
|
|
|
|
void FatalError(const char *msg)
|
|
{
|
|
dsPrintValue(0,1,0,(char*)msg);
|
|
bIsFatalError = true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// A handy function to output a simple pre-built font that is stored just below
|
|
// the 192 pixel row marker on the lower screen background image... this is
|
|
// our "printf()" for the DS lower screen and is used to give simple error
|
|
// messages, produce the menu/file loading text and output the FPS counter.
|
|
// ---------------------------------------------------------------------------------
|
|
void dsPrintValue(int x, int y, unsigned int isSelect, char *pchStr)
|
|
{
|
|
u16 *pusEcran,*pusMap;
|
|
u16 usCharac;
|
|
char *pTrTxt=pchStr;
|
|
char ch;
|
|
|
|
pusEcran=(u16*) (bgGetMapPtr(bg1b))+x+(y<<5);
|
|
pusMap=(u16*) (bgGetMapPtr(bg0b)+(2*isSelect+24)*32);
|
|
|
|
while((*pTrTxt)!='\0' )
|
|
{
|
|
ch = *pTrTxt;
|
|
if (ch >= 'a' && ch <= 'z') ch -= 32; // Faster than strcpy/strtoupper
|
|
usCharac=0x0000;
|
|
if ((ch) == '|')
|
|
usCharac=*(pusMap);
|
|
else if (((ch)<' ') || ((ch)>'_'))
|
|
usCharac=*(pusMap);
|
|
else if((ch)<'@')
|
|
usCharac=*(pusMap+(ch)-' ');
|
|
else
|
|
usCharac=*(pusMap+32+(ch)-'@');
|
|
*pusEcran++=usCharac;
|
|
pTrTxt++;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Setup the emulator and basic perhipheral chips (BIOS, etc).
|
|
// ------------------------------------------------------------------
|
|
BOOL InitializeEmulator(void)
|
|
{
|
|
//find the currentEmulator required to run this RIP
|
|
if (currentRip == NULL) return FALSE;
|
|
|
|
currentEmu = Emulator::GetEmulator();
|
|
if (currentEmu == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//load the BIOS files required for this currentEmulator
|
|
if (!LoadPeripheralRoms(currentEmu))
|
|
{
|
|
FatalError("BIOS (EXEC, GROM) MISSING");
|
|
return FALSE;
|
|
}
|
|
|
|
//load peripheral roms
|
|
INT32 count = currentEmu->GetPeripheralCount();
|
|
for (INT32 i = 0; i < count; i++)
|
|
{
|
|
Peripheral* p = currentEmu->GetPeripheral(i);
|
|
PeripheralCompatibility usage = currentRip->GetPeripheralUsage(p->GetShortName());
|
|
if (usage == PERIPH_INCOMPATIBLE || usage == PERIPH_COMPATIBLE) {
|
|
currentEmu->UsePeripheral(i, FALSE);
|
|
continue;
|
|
}
|
|
|
|
BOOL loaded = LoadPeripheralRoms(p);
|
|
if (loaded)
|
|
{
|
|
//peripheral loaded, might as well use it.
|
|
currentEmu->UsePeripheral(i, TRUE);
|
|
}
|
|
else if (usage == PERIPH_OPTIONAL)
|
|
{
|
|
//didn't load, but the peripheral is optional, so just skip it
|
|
currentEmu->UsePeripheral(i, FALSE);
|
|
}
|
|
else //usage == PERIPH_REQUIRED, but it didn't load
|
|
{
|
|
if (bUseECS)
|
|
FatalError("NO ECS.BIN ");
|
|
else
|
|
FatalError("NO IVOICE.BIN");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// No audio to start... it will turn on 1 frame in...
|
|
TIMER2_CR=0; irqDisable(IRQ_TIMER2);
|
|
|
|
//hook the audio and video up to the currentEmulator
|
|
currentEmu->InitVideo(videoBus,currentEmu->GetVideoWidth(),currentEmu->GetVideoHeight());
|
|
currentEmu->InitAudio(audioMixer, mySoundFrequency);
|
|
|
|
// Clear the audio mixer...
|
|
audioMixer->resetProcessor();
|
|
|
|
//put the RIP in the currentEmulator
|
|
currentEmu->SetRip(currentRip);
|
|
|
|
// Read out any Cheats from disk...
|
|
LoadCheats();
|
|
|
|
// Apply any cheats/hacks to the current game (do this before loading Fast Memory)
|
|
currentEmu->ApplyCheats();
|
|
|
|
//Reset everything
|
|
currentEmu->Reset();
|
|
|
|
// Load up the fast ROM memory for quick fetches
|
|
currentEmu->LoadFastMemory();
|
|
|
|
// Make sure we're starting fresh...
|
|
reset_emu_frames();
|
|
|
|
// And put the sound engine back to the start...
|
|
bStartSoundFifo = true;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// The Intellivision renders output at 160x192 but the DS is 256x192... so there
|
|
// is some stretching involved but it's not CPU intensive since the DS hardware
|
|
// has an aline stretch built in... but we allow the user to tweak these stretch
|
|
// and offset settings to try and produce the best video output possible...
|
|
// ----------------------------------------------------------------------------------
|
|
void HandleScreenStretch(void)
|
|
{
|
|
char tmpStr[33];
|
|
|
|
dsShowBannerScreen();
|
|
swiWaitForVBlank();
|
|
|
|
dsPrintValue(2, 5, 0, (char*)"PRESS UP/DN TO STRETCH SCREEN");
|
|
dsPrintValue(2, 7, 0, (char*)"LEFT/RIGHT TO SHIFT SCREEN");
|
|
dsPrintValue(2, 15, 0, (char*)"PRESS X TO RESET TO DEFAULTS");
|
|
dsPrintValue(1, 16,0, (char*)"START to SAVE, PRESS B TO EXIT");
|
|
|
|
bool bDone = false;
|
|
int last_stretch_x = -99;
|
|
int last_offset_x = -99;
|
|
while (!bDone)
|
|
{
|
|
int keys_pressed = keysCurrent();
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
if (myConfig.stretch_x > 0x0080) REG_BG3PA = --myConfig.stretch_x;
|
|
}
|
|
else if (keys_pressed & KEY_DOWN)
|
|
{
|
|
if (myConfig.stretch_x < 0x0100) REG_BG3PA = ++myConfig.stretch_x;
|
|
}
|
|
else if (keys_pressed & KEY_RIGHT)
|
|
{
|
|
REG_BG3X = (++myConfig.offset_x) << 8;
|
|
}
|
|
else if (keys_pressed & KEY_LEFT)
|
|
{
|
|
REG_BG3X = (--myConfig.offset_x) << 8;
|
|
}
|
|
else if (keys_pressed & KEY_X)
|
|
{
|
|
myConfig.stretch_x = ((160 / 256) << 8) | (160 % 256);
|
|
myConfig.offset_x = 0;
|
|
REG_BG3PA = myConfig.stretch_x;
|
|
REG_BG3X = (myConfig.offset_x) << 8;
|
|
}
|
|
else if ((keys_pressed & KEY_B) || (keys_pressed & KEY_A))
|
|
{
|
|
bDone = true;
|
|
}
|
|
else if (keys_pressed & KEY_START)
|
|
{
|
|
extern void SaveConfig(bool bShow);
|
|
dsPrintValue(2,20,0, (char*)" SAVING CONFIGURATION ");
|
|
SaveConfig(FALSE);
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
dsPrintValue(2,20,0, (char*)" ");
|
|
}
|
|
|
|
// If any values changed... output them.
|
|
if ((myConfig.stretch_x != last_stretch_x) || (myConfig.offset_x != last_offset_x))
|
|
{
|
|
last_stretch_x = myConfig.stretch_x;
|
|
last_offset_x = myConfig.offset_x;
|
|
sprintf(tmpStr, "STRETCH_X = 0x%04X", myConfig.stretch_x);
|
|
dsPrintValue(6, 11, 0, (char*)tmpStr);
|
|
sprintf(tmpStr, " OFFSET_X = %-5d", myConfig.offset_x);
|
|
dsPrintValue(6, 12, 0, (char*)tmpStr);
|
|
}
|
|
|
|
WAITVBL;WAITVBL;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Show some information about the game/emulator on the bottom LCD screen.
|
|
// -------------------------------------------------------------------------
|
|
void dsShowEmuInfo(void)
|
|
{
|
|
char dbg[33];
|
|
UINT8 bDone = false;
|
|
UINT8 idx = 3;
|
|
|
|
dsShowBannerScreen();
|
|
swiWaitForVBlank();
|
|
|
|
if (currentRip != NULL)
|
|
{
|
|
sprintf(dbg, "CPU Mode: %s", isDSiMode() ? "DSI 134MHz 16MB":"DS 67MHz 4 MB"); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "Binary Size: %-9u ", currentRip->GetSize()); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "Binary CRC: %08X ", currentRip->GetCRC()); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "Intellivoice: %s ", (bUseIVoice ? "YES":"NO")); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "JLP Enabled: %s ", (bUseJLP ? "YES":"NO")); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "ECS Enabled: %s ", (bUseECS ? "YES":"NO")); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "Total Frames: %-9u ", global_frames); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "Memory Used: %-9d ", getMemUsed()); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "RAM Indexes: %d / %d ", fast_ram16_idx, slow_ram16_idx); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "MEMS MAPPED: %-9d ", currentEmu->memoryBus.getMemCount());dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "RIP ROM Count: %-9d ", currentRip->GetROMCount()); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "RIP RAM Count: %-9d ", currentRip->GetRAMCount()); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "Compat Tags: %02X %02X %02X %02X %02X", tag_compatibility[0],
|
|
tag_compatibility[1], tag_compatibility[2], tag_compatibility[3],
|
|
tag_compatibility[4]); dsPrintValue(0, idx++, 0, dbg);
|
|
}
|
|
else
|
|
{
|
|
sprintf(dbg, "CPU Mode: %s", isDSiMode() ? "DSI 134MHz 16MB":"DS 67MHz 4 MB"); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "Memory Used: %-9d ", getMemUsed()); dsPrintValue(0, idx++, 0, dbg);
|
|
sprintf(dbg, "NO GAME IS LOADED!"); dsPrintValue(0, idx++, 0, dbg);
|
|
}
|
|
|
|
while (!bDone)
|
|
{
|
|
if (keysCurrent() & (KEY_A | KEY_B | KEY_Y | KEY_X))
|
|
{
|
|
bDone = true;
|
|
}
|
|
WAITVBL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// The main menu provides a full set of options the user can pick. We started to run
|
|
// out of room on the main screen to show all the possible things the user can pick
|
|
// so we now just store all the extra goodies in this menu... By default the SELECT
|
|
// button will bring this up.
|
|
// -------------------------------------------------------------------------------------
|
|
#define MAIN_MENU_ITEMS 12
|
|
const char *main_menu[MAIN_MENU_ITEMS] =
|
|
{
|
|
"RESET EMULATOR",
|
|
"LOAD NEW GAME",
|
|
"GAME CONFIG",
|
|
"GAME SCORES",
|
|
"SAVE/RESTORE STATE",
|
|
"GAME MANUAL",
|
|
"SCREEN STRETCH",
|
|
"GLOBAL CONFIG",
|
|
"SELECT CHEATS",
|
|
"GAME/EMULATOR INFO",
|
|
"QUIT EMULATOR",
|
|
"EXIT THIS MENU",
|
|
};
|
|
|
|
|
|
int menu_entry(void)
|
|
{
|
|
UINT8 current_entry = 0;
|
|
extern int bg0, bg0b, bg1b;
|
|
char bDone = 0;
|
|
|
|
dsShowBannerScreen();
|
|
swiWaitForVBlank();
|
|
dsPrintValue(8,3,0, (char*)"MAIN MENU");
|
|
dsPrintValue(4,20,0, (char*)"PRESS UP/DOWN AND A=SELECT");
|
|
|
|
for (int i=0; i<MAIN_MENU_ITEMS; i++)
|
|
{
|
|
dsPrintValue(8,5+i, (i==0 ? 1:0), (char*)main_menu[i]);
|
|
}
|
|
|
|
int last_keys_pressed = -1;
|
|
while (!bDone)
|
|
{
|
|
int keys_pressed = keysCurrent();
|
|
|
|
if (keys_pressed != last_keys_pressed)
|
|
{
|
|
last_keys_pressed = keys_pressed;
|
|
if (keys_pressed & KEY_DOWN)
|
|
{
|
|
dsPrintValue(8,5+current_entry, 0, (char*)main_menu[current_entry]);
|
|
if (current_entry < (MAIN_MENU_ITEMS-1)) current_entry++; else current_entry=0;
|
|
dsPrintValue(8,5+current_entry, 1, (char*)main_menu[current_entry]);
|
|
}
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
dsPrintValue(8,5+current_entry, 0, (char*)main_menu[current_entry]);
|
|
if (current_entry > 0) current_entry--; else current_entry=(MAIN_MENU_ITEMS-1);
|
|
dsPrintValue(8,5+current_entry, 1, (char*)main_menu[current_entry]);
|
|
}
|
|
if (keys_pressed & KEY_SELECT)
|
|
{
|
|
return OVL_META_CONFIG;
|
|
}
|
|
if (keys_pressed & KEY_A)
|
|
{
|
|
switch (current_entry)
|
|
{
|
|
case 0:
|
|
return OVL_META_RESET;
|
|
break;
|
|
case 1:
|
|
return OVL_META_LOAD;
|
|
break;
|
|
case 2:
|
|
return OVL_META_CONFIG;
|
|
break;
|
|
case 3:
|
|
return OVL_META_SCORES;
|
|
break;
|
|
case 4:
|
|
return OVL_META_STATE;
|
|
break;
|
|
case 5:
|
|
return OVL_META_MANUAL;
|
|
break;
|
|
case 6:
|
|
return OVL_META_STRETCH;
|
|
break;
|
|
case 7:
|
|
return OVL_META_GCONFIG;
|
|
break;
|
|
case 8:
|
|
return OVL_META_CHEATS;
|
|
break;
|
|
case 9:
|
|
return OVL_META_EMUINFO;
|
|
break;
|
|
case 10:
|
|
return OVL_META_QUIT;
|
|
break;
|
|
case 11:
|
|
bDone=1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (keys_pressed & KEY_B)
|
|
{
|
|
bDone=1;
|
|
}
|
|
swiWaitForVBlank();
|
|
}
|
|
}
|
|
return OVL_MAX;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// Meta keys are things that aren't specifically emulating the Intellivision controller.
|
|
// For example: Game Config, Quit or Load Game are all meta-commands.
|
|
// -------------------------------------------------------------------------------------
|
|
char newFile[256];
|
|
void ds_handle_meta(int meta_key)
|
|
{
|
|
if (meta_key == OVL_MAX) return;
|
|
|
|
// -------------------------------------------------------------------
|
|
// On the way in, make sure no keys are pressed before continuing...
|
|
// -------------------------------------------------------------------
|
|
while (keysCurrent())
|
|
{
|
|
WAITVBL;
|
|
}
|
|
switch (meta_key)
|
|
{
|
|
case OVL_META_RESET:
|
|
if (bGameLoaded)
|
|
{
|
|
extern u8 bCheatChanged;
|
|
// -------------------------------------------------------------------------------------------
|
|
// If any CHEAT has been changed, we must load back the original RIP and re-apply any cheats.
|
|
// -------------------------------------------------------------------------------------------
|
|
if (bCheatChanged)
|
|
{
|
|
bCheatChanged = false;
|
|
|
|
//put the RIP in the currentEmulator
|
|
currentEmu->SetRip(currentRip);
|
|
|
|
// Apply any cheats/hacks to the current game (do this before loading Fast Memory)
|
|
currentEmu->ApplyCheats();
|
|
}
|
|
|
|
// Perform the actual reset on everything
|
|
currentEmu->Reset();
|
|
|
|
// Load up the fast ROM memory for quick fetches
|
|
currentEmu->LoadFastMemory();
|
|
|
|
// And put the Sound Fifo back at the start...
|
|
bStartSoundFifo = true;
|
|
|
|
// Make sure we're starting fresh...
|
|
reset_emu_frames();
|
|
}
|
|
break;
|
|
|
|
case OVL_META_LOAD:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (dsWaitForRom(newFile))
|
|
{
|
|
if (LoadCart(newFile))
|
|
{
|
|
dsInitPalette();
|
|
}
|
|
else return; // We've already set FatalError() from LoadCart()
|
|
}
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_CONFIG:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (currentRip != NULL)
|
|
{
|
|
dsChooseOptions(0);
|
|
reset_emu_frames();
|
|
dsInitPalette();
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
}
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_GCONFIG:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
dsChooseOptions(1);
|
|
reset_emu_frames();
|
|
dsInitPalette();
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_CHEATS:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
CheatMenu();
|
|
reset_emu_frames();
|
|
dsInitPalette();
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_SCORES:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (currentRip != NULL)
|
|
{
|
|
highscore_display(currentRip->GetCRC());
|
|
dsShowScreenMain(false);
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
}
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_STATE:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (currentRip != NULL)
|
|
{
|
|
savestate_entry();
|
|
dsShowScreenMain(false);
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
}
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_MENU:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
ds_handle_meta(menu_entry());
|
|
if (currentRip != NULL)
|
|
{
|
|
dsShowScreenMain(false);
|
|
}
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_SWITCH:
|
|
if (currentRip != NULL)
|
|
{
|
|
myConfig.controller_type = 1-myConfig.controller_type;
|
|
}
|
|
break;
|
|
|
|
case OVL_META_MANUAL:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (currentRip != NULL)
|
|
{
|
|
dsShowManual();
|
|
dsShowScreenMain(false);
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
}
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_EMUINFO:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
dsShowEmuInfo();
|
|
dsShowScreenMain(false);
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_STRETCH:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (currentRip != NULL)
|
|
{
|
|
HandleScreenStretch();
|
|
dsShowScreenMain(false);
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
}
|
|
bStartSoundFifo = true;
|
|
break;
|
|
|
|
case OVL_META_QUIT:
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (dsWaitOnQuit()){ runState = Quit; }
|
|
bStartSoundFifo = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// Read the DS keys for input and handle it... this could be meta commands (LOAD, QUIT, CONFIG) or
|
|
// it could be actual intellivision keypad emulation (KEY_1, KEY_2, DISC movement, etc).
|
|
// This is called every frame - so 60 times per second which is fairly responsive...
|
|
// -------------------------------------------------------------------------------------------------
|
|
ITCM_CODE void pollInputs(void)
|
|
{
|
|
UINT16 ctrl_disc, ctrl_keys, ctrl_side;
|
|
unsigned short keys_pressed = keysCurrent();
|
|
static short last_pressed = -1;
|
|
|
|
for (int i=0; i<15; i++) {ds_key_input[0][i] = 0; ds_key_input[1][i] = 0;}
|
|
for (int i=0; i<16; i++) {ds_disc_input[0][i] = 0; ds_disc_input[1][i] = 0;}
|
|
|
|
// Check for Dual Action
|
|
if (myConfig.controller_type == 2) // Standard Dual-Action (disc and side buttons on controller #1... keypad from controller #2)
|
|
{
|
|
ctrl_disc = 0;
|
|
ctrl_side = 0;
|
|
ctrl_keys = 1;
|
|
}
|
|
else if (myConfig.controller_type == 3) // Same as #2 above except side-buttons use controller #2
|
|
{
|
|
ctrl_disc = 0;
|
|
ctrl_side = 1;
|
|
ctrl_keys = 1;
|
|
}
|
|
else
|
|
{
|
|
ctrl_disc = myConfig.controller_type;
|
|
ctrl_side = myConfig.controller_type;
|
|
ctrl_keys = myConfig.controller_type;
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// Handle 8 directions on keypad... best we can do with the d-pad
|
|
// unless there is a custom .ovl overlay file mapping the disc.
|
|
// ---------------------------------------------------------------
|
|
|
|
if (myConfig.dpad_config == 0) // Normal handling
|
|
{
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
if (keys_pressed & KEY_RIGHT) ds_disc_input[ctrl_disc][2] = 1;
|
|
else if (keys_pressed & KEY_LEFT) ds_disc_input[ctrl_disc][14] = 1;
|
|
else ds_disc_input[ctrl_disc][0] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_DOWN)
|
|
{
|
|
if (keys_pressed & KEY_RIGHT) ds_disc_input[ctrl_disc][6] = 1;
|
|
else if (keys_pressed & KEY_LEFT) ds_disc_input[ctrl_disc][10] = 1;
|
|
else ds_disc_input[ctrl_disc][8] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_RIGHT)
|
|
{
|
|
ds_disc_input[ctrl_disc][4] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_LEFT)
|
|
{
|
|
ds_disc_input[ctrl_disc][12] = 1;
|
|
}
|
|
}
|
|
else if (myConfig.dpad_config == 1) // Reverse Left/Right
|
|
{
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
if (keys_pressed & KEY_RIGHT) ds_disc_input[ctrl_disc][14] = 1;
|
|
else if (keys_pressed & KEY_LEFT) ds_disc_input[ctrl_disc][2] = 1;
|
|
else ds_disc_input[ctrl_disc][0] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_DOWN)
|
|
{
|
|
if (keys_pressed & KEY_RIGHT) ds_disc_input[ctrl_disc][10] = 1;
|
|
else if (keys_pressed & KEY_LEFT) ds_disc_input[ctrl_disc][6] = 1;
|
|
else ds_disc_input[ctrl_disc][8] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_RIGHT)
|
|
{
|
|
ds_disc_input[ctrl_disc][12] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_LEFT)
|
|
{
|
|
ds_disc_input[ctrl_disc][4] = 1;
|
|
}
|
|
}
|
|
else if (myConfig.dpad_config == 2) // Reverse Up/Down
|
|
{
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
if (keys_pressed & KEY_RIGHT) ds_disc_input[ctrl_disc][6] = 1;
|
|
else if (keys_pressed & KEY_LEFT) ds_disc_input[ctrl_disc][10] = 1;
|
|
else ds_disc_input[ctrl_disc][8] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_DOWN)
|
|
{
|
|
if (keys_pressed & KEY_RIGHT) ds_disc_input[ctrl_disc][2] = 1;
|
|
else if (keys_pressed & KEY_LEFT) ds_disc_input[ctrl_disc][14] = 1;
|
|
else ds_disc_input[ctrl_disc][0] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_RIGHT)
|
|
{
|
|
ds_disc_input[ctrl_disc][4] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_LEFT)
|
|
{
|
|
ds_disc_input[ctrl_disc][12] = 1;
|
|
}
|
|
}
|
|
else if (myConfig.dpad_config == 3) // Diagnoals
|
|
{
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
ds_disc_input[ctrl_disc][2] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_DOWN)
|
|
{
|
|
ds_disc_input[ctrl_disc][10] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_RIGHT)
|
|
{
|
|
ds_disc_input[ctrl_disc][6] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_LEFT)
|
|
{
|
|
ds_disc_input[ctrl_disc][14] = 1;
|
|
}
|
|
}
|
|
else if (myConfig.dpad_config == 4) // Strict 4-way
|
|
{
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
ds_disc_input[ctrl_disc][0] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_DOWN)
|
|
{
|
|
ds_disc_input[ctrl_disc][8] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_RIGHT)
|
|
{
|
|
ds_disc_input[ctrl_disc][4] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_LEFT)
|
|
{
|
|
ds_disc_input[ctrl_disc][12] = 1;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// Now handle the main DS keys... these can be re-mapped to any Intellivision function
|
|
// -------------------------------------------------------------------------------------
|
|
if ((keys_pressed & KEY_A) && (keys_pressed & KEY_X))
|
|
{
|
|
if (myConfig.key_AX_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_AX_map);
|
|
}
|
|
else if (myConfig.key_AX_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_AX_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_AX_map] = 1;
|
|
}
|
|
else
|
|
if ((keys_pressed & KEY_X) && (keys_pressed & KEY_Y))
|
|
{
|
|
if (myConfig.key_XY_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_XY_map);
|
|
}
|
|
else if (myConfig.key_XY_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_XY_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_XY_map] = 1;
|
|
}
|
|
else
|
|
if ((keys_pressed & KEY_Y) && (keys_pressed & KEY_B))
|
|
{
|
|
if (myConfig.key_YB_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_YB_map);
|
|
}
|
|
else if (myConfig.key_YB_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_YB_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_YB_map] = 1;
|
|
}
|
|
else
|
|
if ((keys_pressed & KEY_B) && (keys_pressed & KEY_A))
|
|
{
|
|
if (myConfig.key_BA_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_BA_map);
|
|
}
|
|
else if (myConfig.key_BA_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_BA_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_BA_map] = 1;
|
|
}
|
|
else
|
|
{
|
|
if (keys_pressed & KEY_A)
|
|
{
|
|
if (myConfig.key_A_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_A_map);
|
|
}
|
|
else if (myConfig.key_A_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_A_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_A_map] = 1;
|
|
}
|
|
|
|
if (keys_pressed & KEY_B)
|
|
{
|
|
if (myConfig.key_B_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_B_map);
|
|
}
|
|
else if (myConfig.key_B_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_B_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_B_map] = 1;
|
|
}
|
|
|
|
if (keys_pressed & KEY_X)
|
|
{
|
|
if (myConfig.key_X_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_X_map);
|
|
}
|
|
else if (myConfig.key_X_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_X_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_X_map] = 1;
|
|
}
|
|
|
|
if (keys_pressed & KEY_Y)
|
|
{
|
|
if (myConfig.key_Y_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_Y_map);
|
|
}
|
|
else if (myConfig.key_Y_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_Y_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_Y_map] = 1;
|
|
}
|
|
}
|
|
|
|
if (keys_pressed & KEY_L)
|
|
{
|
|
if (myConfig.key_L_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_L_map);
|
|
}
|
|
else if (myConfig.key_L_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_L_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_L_map] = 1;
|
|
}
|
|
|
|
if (keys_pressed & KEY_R)
|
|
{
|
|
if (myConfig.key_R_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_R_map);
|
|
}
|
|
else if (myConfig.key_R_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_R_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_R_map] = 1;
|
|
}
|
|
|
|
if (keys_pressed & KEY_START)
|
|
{
|
|
if (myConfig.key_START_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_START_map);
|
|
}
|
|
else if (myConfig.key_START_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_START_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_START_map] = 1;
|
|
}
|
|
|
|
if (keys_pressed & KEY_SELECT)
|
|
{
|
|
if (myConfig.key_SELECT_map >= OVL_META_RESET)
|
|
{
|
|
if (last_pressed != keys_pressed) ds_handle_meta(myConfig.key_SELECT_map);
|
|
}
|
|
else if (myConfig.key_SELECT_map >= OVL_BTN_FIRE)
|
|
ds_key_input[ctrl_side][myConfig.key_SELECT_map] = 1;
|
|
else
|
|
ds_key_input[ctrl_keys][myConfig.key_SELECT_map] = 1;
|
|
}
|
|
|
|
last_pressed = keys_pressed;
|
|
|
|
ecs_key_pressed = (myConfig.overlay_selected == 10) ? 0:255;
|
|
|
|
// -----------------------------------------------------------------
|
|
// Now handle the on-screen Intellivision overlay and meta keys...
|
|
// -----------------------------------------------------------------
|
|
if (keys_pressed & KEY_TOUCH)
|
|
{
|
|
touchPosition touch;
|
|
touchRead(&touch);
|
|
#ifdef DEBUG_ENABLE
|
|
if (debugger_input(touch.px, touch.py) == DBG_PRESS_META)
|
|
{
|
|
while (keysCurrent() & KEY_TOUCH) // Wait for release
|
|
{
|
|
WAITVBL;
|
|
}
|
|
WAITVBL;
|
|
}
|
|
#endif
|
|
// -----------------------------------------------------------
|
|
// Did we map any hotspots on the overlay to disc directions?
|
|
// -----------------------------------------------------------
|
|
if (bUseDiscOverlay)
|
|
{
|
|
for (int i=0; i < DISC_MAX; i++)
|
|
{
|
|
if (touch.px > myDisc[i].x1 && touch.px < myDisc[i].x2 && touch.py > myDisc[i].y1 && touch.py < myDisc[i].y2) ds_disc_input[ctrl_disc][i] = 1;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Handle the 12 keypad keys on the intellivision controller overlay...
|
|
// ----------------------------------------------------------------------
|
|
for (int i=0; i <= OVL_KEY_ENTER; i++)
|
|
{
|
|
if (touch.px > myOverlay[i].x1 && touch.px < myOverlay[i].x2 && touch.py > myOverlay[i].y1 && touch.py < myOverlay[i].y2) ds_key_input[ctrl_keys][i] = 1;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Handle the 3 side buttons (top=Fire... Left-Action and Right-Action)
|
|
// ----------------------------------------------------------------------
|
|
for (int i=OVL_BTN_FIRE; i<=OVL_BTN_R_ACT; i++)
|
|
{
|
|
if (touch.px > myOverlay[i].x1 && touch.px < myOverlay[i].x2 && touch.py > myOverlay[i].y1 && touch.py < myOverlay[i].y2) ds_key_input[ctrl_side][i] = 1;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// Handled "META" keys here... this includes things like RESET, LOAD, CONFIG, etc
|
|
// ----------------------------------------------------------------------------------
|
|
|
|
// RESET
|
|
if (touch.px > myOverlay[OVL_META_RESET].x1 && touch.px < myOverlay[OVL_META_RESET].x2 && touch.py > myOverlay[OVL_META_RESET].y1 && touch.py < myOverlay[OVL_META_RESET].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_RESET);
|
|
}
|
|
// LOAD
|
|
else if (touch.px > myOverlay[OVL_META_LOAD].x1 && touch.px < myOverlay[OVL_META_LOAD].x2 && touch.py > myOverlay[OVL_META_LOAD].y1 && touch.py < myOverlay[OVL_META_LOAD].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_LOAD);
|
|
}
|
|
// CONFIG
|
|
else if (touch.px > myOverlay[OVL_META_CONFIG].x1 && touch.px < myOverlay[OVL_META_CONFIG].x2 && touch.py > myOverlay[OVL_META_CONFIG].y1 && touch.py < myOverlay[OVL_META_CONFIG].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_CONFIG);
|
|
}
|
|
// HIGHSCORES
|
|
else if (touch.px > myOverlay[OVL_META_SCORES].x1 && touch.px < myOverlay[OVL_META_SCORES].x2 && touch.py > myOverlay[OVL_META_SCORES].y1 && touch.py < myOverlay[OVL_META_SCORES].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_SCORES);
|
|
}
|
|
// STATE
|
|
else if (touch.px > myOverlay[OVL_META_STATE].x1 && touch.px < myOverlay[OVL_META_STATE].x2 && touch.py > myOverlay[OVL_META_STATE].y1 && touch.py < myOverlay[OVL_META_STATE].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_STATE);
|
|
}
|
|
// QUIT
|
|
else if (touch.px > myOverlay[OVL_META_QUIT].x1 && touch.px < myOverlay[OVL_META_QUIT].x2 && touch.py > myOverlay[OVL_META_QUIT].y1 && touch.py < myOverlay[OVL_META_QUIT].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_QUIT);
|
|
}
|
|
// MENU
|
|
else if (touch.px > myOverlay[OVL_META_MENU].x1 && touch.px < myOverlay[OVL_META_MENU].x2 && touch.py > myOverlay[OVL_META_MENU].y1 && touch.py < myOverlay[OVL_META_MENU].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_MENU);
|
|
}
|
|
// SWITCH
|
|
else if (touch.px > myOverlay[OVL_META_SWITCH].x1 && touch.px < myOverlay[OVL_META_SWITCH].x2 && touch.py > myOverlay[OVL_META_SWITCH].y1 && touch.py < myOverlay[OVL_META_SWITCH].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_SWITCH);
|
|
}
|
|
// MANUAL
|
|
else if (touch.px > myOverlay[OVL_META_MANUAL].x1 && touch.px < myOverlay[OVL_META_MANUAL].x2 && touch.py > myOverlay[OVL_META_MANUAL].y1 && touch.py < myOverlay[OVL_META_MANUAL].y2)
|
|
{
|
|
ds_handle_meta(OVL_META_MANUAL);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------------------
|
|
// And, finally, if the ECS mini-keypad is being shown, we can directly check for any ECS keyboard keys...
|
|
// ---------------------------------------------------------------------------------------------------------
|
|
if (myConfig.overlay_selected == 10)
|
|
{
|
|
if ((touch.px > 5) && (touch.px < 98))
|
|
{
|
|
if (touch.py >= 25 && touch.py < 43) // Row: 1 2 3 4 5
|
|
{
|
|
if (touch.px <= 23) ecs_key_pressed = 1;
|
|
else if (touch.px <= 41) ecs_key_pressed = 2;
|
|
else if (touch.px <= 60) ecs_key_pressed = 3;
|
|
else if (touch.px <= 78) ecs_key_pressed = 4;
|
|
else if (touch.px <= 97) ecs_key_pressed = 5;
|
|
|
|
}
|
|
else if (touch.py >= 43 && touch.py < 60) // Row: 6 7 8 9 0
|
|
{
|
|
if (touch.px <= 23) ecs_key_pressed = 6;
|
|
else if (touch.px <= 41) ecs_key_pressed = 7;
|
|
else if (touch.px <= 60) ecs_key_pressed = 8;
|
|
else if (touch.px <= 78) ecs_key_pressed = 9;
|
|
else if (touch.px <= 97) ecs_key_pressed = 10;
|
|
}
|
|
else if (touch.py >= 60 && touch.py < 78) // Row: A B C D E
|
|
{
|
|
if (touch.px <= 23) ecs_key_pressed = 11;
|
|
else if (touch.px <= 41) ecs_key_pressed = 12;
|
|
else if (touch.px <= 60) ecs_key_pressed = 13;
|
|
else if (touch.px <= 78) ecs_key_pressed = 14;
|
|
else if (touch.px <= 97) ecs_key_pressed = 15;
|
|
}
|
|
else if (touch.py >= 78 && touch.py < 95) // Row: F G H I J
|
|
{
|
|
if (touch.px <= 23) ecs_key_pressed = 16;
|
|
else if (touch.px <= 41) ecs_key_pressed = 17;
|
|
else if (touch.px <= 60) ecs_key_pressed = 18;
|
|
else if (touch.px <= 78) ecs_key_pressed = 19;
|
|
else if (touch.px <= 97) ecs_key_pressed = 20;
|
|
}
|
|
else if (touch.py >= 95 && touch.py < 112) // Row: K L M N O
|
|
{
|
|
if (touch.px <= 23) ecs_key_pressed = 21;
|
|
else if (touch.px <= 41) ecs_key_pressed = 22;
|
|
else if (touch.px <= 60) ecs_key_pressed = 23;
|
|
else if (touch.px <= 78) ecs_key_pressed = 24;
|
|
else if (touch.px <= 97) ecs_key_pressed = 25;
|
|
}
|
|
else if (touch.py >= 112 && touch.py < 130) // Row: P Q R S T
|
|
{
|
|
if (touch.px <= 23) ecs_key_pressed = 26;
|
|
else if (touch.px <= 41) ecs_key_pressed = 27;
|
|
else if (touch.px <= 60) ecs_key_pressed = 28;
|
|
else if (touch.px <= 78) ecs_key_pressed = 29;
|
|
else if (touch.px <= 97) ecs_key_pressed = 30;
|
|
}
|
|
else if (touch.py >= 130 && touch.py < 148) // Row: U V W X Y
|
|
{
|
|
if (touch.px <= 23) ecs_key_pressed = 31;
|
|
else if (touch.px <= 41) ecs_key_pressed = 32;
|
|
else if (touch.px <= 60) ecs_key_pressed = 33;
|
|
else if (touch.px <= 78) ecs_key_pressed = 34;
|
|
else if (touch.px <= 97) ecs_key_pressed = 35;
|
|
}
|
|
else if (touch.py >= 148 && touch.py < 166) // Row: Z [arrows]
|
|
{
|
|
if (touch.px <= 23) ecs_key_pressed = 36;
|
|
else if (touch.px <= 41) ecs_key_pressed = 37;
|
|
else if (touch.px <= 60) ecs_key_pressed = 38;
|
|
else if (touch.px <= 78) ecs_key_pressed = 39;
|
|
else if (touch.px <= 97) ecs_key_pressed = 40;
|
|
}
|
|
else if (touch.py >= 166 && touch.py < 190) // Row: SPC RET
|
|
{
|
|
if (touch.px <= 50) ecs_key_pressed = 41;
|
|
else if (touch.px <= 97) ecs_key_pressed = 42;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// This shows the main upper screen which is where the emulation takes place...
|
|
// the lower screen is used for overlay and debugger information.
|
|
// ------------------------------------------------------------------------------
|
|
void dsShowScreenMain(bool bFull)
|
|
{
|
|
// Init BG mode for 16 bits colors
|
|
if (bFull)
|
|
{
|
|
videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE );
|
|
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE);
|
|
vramSetBankA(VRAM_A_MAIN_BG); vramSetBankC(VRAM_C_SUB_BG);
|
|
bg0 = bgInit(0, BgType_Text8bpp, BgSize_T_256x256, 31,0);
|
|
bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0);
|
|
bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 30,0);
|
|
bgSetPriority(bg0b,1);bgSetPriority(bg1b,0);
|
|
|
|
decompress(bgTopTiles, bgGetGfxPtr(bg0), LZ77Vram);
|
|
decompress(bgTopMap, (void*) bgGetMapPtr(bg0), LZ77Vram);
|
|
dmaCopy((void *) bgTopPal,(u16*) BG_PALETTE,256*2);
|
|
}
|
|
|
|
// Now show the bottom screen - usualy some form of overlay...
|
|
#ifdef DEBUG_ENABLE
|
|
show_debug_overlay();
|
|
#else
|
|
show_overlay();
|
|
#endif
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------
|
|
// Most of the main screen video memory is not used so we we will re-purpose it for emulation memory.
|
|
// The DS VRAM is 16-bit access and you can only write 16-bits at a time... but this is fine for the
|
|
// Intellivision 16-bit CPU. The memory is not as fast as a main memory cache-hit but is much faster
|
|
// than a main memory cache miss... so on average this memory tends to be quite useful! Also, since
|
|
// this emulator is using a large chunk of the 4MB available in the older DS-LITE/PHAT hardware, using
|
|
// almost 400k of video memory reduces the burden of memory usage on the older hardware.
|
|
// --------------------------------------------------------------------------------------------------------
|
|
void dsInitScreenMain(void)
|
|
{
|
|
vramSetBankB(VRAM_B_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06820000 - Used for Memory Bus Read Counter
|
|
vramSetBankD(VRAM_D_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06860000 - Used for Cart "Fast Buffer" 64k x 16 = 128k
|
|
vramSetBankE(VRAM_E_LCD ); // Not using this for video but 64K of faster RAM always useful! Mapped at 0x06880000 - Used for Custom Tile Overlay Buffer (60K for tile[] buffer, 4K for map[] buffer)
|
|
vramSetBankF(VRAM_F_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06890000 - ECS RAM Buffer (2K Words - some unused memory here could be stolen)
|
|
vramSetBankG(VRAM_G_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06894000 - JLP RAM Buffer (8K Words or 16K Bytes)
|
|
vramSetBankH(VRAM_H_LCD ); // Not using this for video but 32K of faster RAM always useful! Mapped at 0x06898000 - Slow RAM buffer (16K Words or 32K Bytes)
|
|
vramSetBankI(VRAM_I_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x068A0000 - Unused
|
|
|
|
WAITVBL;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------------
|
|
// Generic show-menu which is just the lower screen that is mostly blank with the NINTELLIVISION
|
|
// banner at the top. The menu has 2 font choices... a high-contrast white on black and a more
|
|
// retro feel green on black (default). The user can change this option in global configuration.
|
|
// ----------------------------------------------------------------------------------------------
|
|
void dsShowBannerScreen(void)
|
|
{
|
|
if (myGlobalConfig.menu_color == 0)
|
|
{
|
|
decompress(bgMenu_WhiteTiles, bgGetGfxPtr(bg0b), LZ77Vram);
|
|
decompress(bgMenu_WhiteMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
|
|
dmaCopy((void *) bgMenu_WhitePal,(u16*) BG_PALETTE_SUB,256*2);
|
|
unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32);
|
|
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
|
|
}
|
|
else
|
|
{
|
|
decompress(bgMenu_GreenTiles, bgGetGfxPtr(bg0b), LZ77Vram);
|
|
decompress(bgMenu_GreenMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
|
|
dmaCopy((void *) bgMenu_GreenPal,(u16*) BG_PALETTE_SUB,256*2);
|
|
unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32);
|
|
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------
|
|
// Generic show the top screen... which is rendered as an 8-bit bitmap handling. The video
|
|
// render code will basically dump the internal Intellivision "pixelBuffer" into the video
|
|
// memory via a DMA copy (slightly faster than memcpy or for-loops).
|
|
// ------------------------------------------------------------------------------------------
|
|
void dsShowScreenEmu(void)
|
|
{
|
|
videoSetMode(MODE_5_2D);
|
|
vramSetBankA(VRAM_A_MAIN_BG_0x06000000); // The main emulated (top screen) display.
|
|
|
|
bg0 = bgInit(3, BgType_Bmp8, BgSize_B8_256x256, 0,0);
|
|
memset((void*)0x06000000, 0x00, 128*1024);
|
|
|
|
REG_BG3PA = myConfig.stretch_x;
|
|
REG_BG3PB = 0; REG_BG3PC = 0;
|
|
REG_BG3PD = ((100 / 100) << 8) | (100 % 100) ;
|
|
REG_BG3X = (myConfig.offset_x) << 8;
|
|
REG_BG3Y = 0;
|
|
|
|
dsInitPalette();
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------
|
|
// Start Timer 0 - this is our frame timer counter...
|
|
// ------------------------------------------------------------------------------------------
|
|
ITCM_CODE void dsInitTimer(void)
|
|
{
|
|
TIMER0_DATA=0;
|
|
TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------
|
|
// When we're done with the emulator, we stop the audio...
|
|
// ------------------------------------------------------------------------------------------
|
|
void dsFreeEmu(void)
|
|
{
|
|
// Stop timer of sound
|
|
TIMER2_CR=0; irqDisable(IRQ_TIMER2);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------
|
|
// When the user asks to QUIT we want to confirm and make sure before we shut down...
|
|
// ------------------------------------------------------------------------------------------
|
|
bool dsWaitOnQuit(void)
|
|
{
|
|
bool bRet=false, bDone=false;
|
|
unsigned int keys_pressed;
|
|
unsigned int posdeb=0;
|
|
char szName[32];
|
|
|
|
dsShowBannerScreen();
|
|
|
|
strcpy(szName,"Quit NINTV-DS?");
|
|
dsPrintValue(16-strlen(szName)/2,2,0,szName);
|
|
sprintf(szName,"%s","A TO CONFIRM, B TO GO BACK");
|
|
dsPrintValue(16-strlen(szName)/2,23,0,szName);
|
|
|
|
while(!bDone)
|
|
{
|
|
strcpy(szName," YES ");
|
|
dsPrintValue(5,10+0,(posdeb == 0 ? 1 : 0),szName);
|
|
strcpy(szName," NO ");
|
|
dsPrintValue(5,14+1,(posdeb == 2 ? 1 : 0),szName);
|
|
swiWaitForVBlank();
|
|
|
|
// Check pad
|
|
keys_pressed=keysCurrent();
|
|
if (keys_pressed & KEY_UP) {
|
|
if (posdeb) posdeb-=2;
|
|
}
|
|
if (keys_pressed & KEY_DOWN) {
|
|
if (posdeb<1) posdeb+=2;
|
|
}
|
|
if (keys_pressed & KEY_A) {
|
|
bRet = (posdeb ? false : true);
|
|
bDone = true;
|
|
}
|
|
if (keys_pressed & KEY_B) {
|
|
bDone = true;
|
|
}
|
|
}
|
|
|
|
dsShowScreenMain(false);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------
|
|
// Our main loop! This is the top leel master loop which runs the emulation... we sit in
|
|
// this loop looking for input and calling into the emulation engine to handle 1 frame at
|
|
// a time and then render that frame to the DS video screen... it all starts here folks!
|
|
// ------------------------------------------------------------------------------------------
|
|
ITCM_CODE void Run(char *initial_file)
|
|
{
|
|
// Setup 1 second timer for things like FPS
|
|
TIMER1_CR = 0;
|
|
TIMER1_DATA = 0;
|
|
TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024;
|
|
|
|
reset_emu_frames();
|
|
|
|
runState = Running;
|
|
|
|
// ------------------------------------------------------
|
|
// If we were passed in a file on the command line...
|
|
// ------------------------------------------------------
|
|
if (initial_file)
|
|
{
|
|
if (LoadCart(initial_file))
|
|
{
|
|
dsInitPalette();
|
|
}
|
|
}
|
|
|
|
while(runState == Running)
|
|
{
|
|
// If we have been told to start up the sound FIFO... do so here...
|
|
if (bStartSoundFifo)
|
|
{
|
|
if (currentRip != NULL)
|
|
{
|
|
dsInstallSoundEmuFIFO();
|
|
bStartSoundFifo = false;
|
|
reset_emu_frames();
|
|
}
|
|
}
|
|
|
|
// Time 1 frame...
|
|
while(TIMER0_DATA < (target_frame_timing[myConfig.target_fps]*(emu_frames+1)))
|
|
;
|
|
|
|
// Have we processed target (default 60) frames... start over...
|
|
if (++emu_frames >= target_frames[myConfig.target_fps])
|
|
{
|
|
reset_emu_frames();
|
|
}
|
|
|
|
//poll the input
|
|
pollInputs();
|
|
|
|
if (bInitEmulator) // If the inputs told us we loaded a new file... cleanup and start over...
|
|
{
|
|
InitializeEmulator();
|
|
bInitEmulator = false;
|
|
continue;
|
|
}
|
|
|
|
if (bGameLoaded && !bIsFatalError)
|
|
{
|
|
//run the emulation
|
|
currentEmu->Run();
|
|
|
|
// render the output
|
|
currentEmu->Render();
|
|
}
|
|
|
|
if (TIMER1_DATA >= 32728) // 1000MS (1 sec)
|
|
{
|
|
char tmp[33];
|
|
TIMER1_CR = 0;
|
|
TIMER1_DATA = 0;
|
|
TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024;
|
|
oneSecTick=TRUE;
|
|
if ((frames_per_sec_calc > 0) && (myGlobalConfig.show_fps > 0))
|
|
{
|
|
if (frames_per_sec_calc==(target_frames[myConfig.target_fps]+1)) frames_per_sec_calc--;
|
|
tmp[0] = '0' + (frames_per_sec_calc/100);
|
|
tmp[1] = '0' + ((frames_per_sec_calc % 100) /10);
|
|
tmp[2] = '0' + ((frames_per_sec_calc % 100) %10);
|
|
tmp[3] = 0;
|
|
dsPrintValue(0,0,0,tmp);
|
|
}
|
|
frames_per_sec_calc=0;
|
|
|
|
// If we've been asked to put out the debug info at the top of the screen... mostly for developer use
|
|
if (myGlobalConfig.show_fps == 2)
|
|
{
|
|
sprintf(tmp,"%-6d %-6d %-5d %-5d", debug[0], debug[1], debug[2], debug[3]);
|
|
//sprintf(tmp,"%04X %04X %04X %04X %02X %02X", debug[0], debug[1], debug[2], debug[3], debug[4], debug[5]);
|
|
dsPrintValue(6,0,0,tmp);
|
|
}
|
|
|
|
// If we are using the JLP, we call into the 1 second tick function in case there is a dirty flash that needs writing...
|
|
if (bUseJLP)
|
|
{
|
|
currentRip->JLP16Bit->tick_one_second();
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_ENABLE
|
|
debugger();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------------
|
|
// Entry point called from main(). Optionally can be passed a file to start running - this
|
|
// is necessary for TWL++ which may pass us a .int file on the command line.
|
|
// ----------------------------------------------------------------------------------------------
|
|
void dsMainLoop(char *initial_file)
|
|
{
|
|
// -------------------------------------------------------------------------
|
|
// These are used to write to the DS screen and produce DS audio output...
|
|
// -------------------------------------------------------------------------
|
|
videoBus = new VideoBusDS();
|
|
audioMixer = new AudioMixerDS();
|
|
|
|
FindAndLoadConfig();
|
|
dsShowScreenMain(true);
|
|
|
|
Run(initial_file); // This will only return if the user opts to QUIT
|
|
}
|
|
|
|
void _putchar(char character) {}; // Not used but needed to link printf()
|
|
|
|
// End of Line
|