mirror of
https://github.com/wavemotion-dave/NINTV-DS.git
synced 2025-06-18 13:55:33 -04:00
1091 lines
32 KiB
C++
1091 lines
32 KiB
C++
// =====================================================================================================
|
|
// NINTELLIVISION (c) 2021 by wavemotion-dave
|
|
// =====================================================================================================
|
|
#include <nds.h>
|
|
#include <nds/fifomessages.h>
|
|
|
|
#include<stdio.h>
|
|
|
|
#include <fat.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
|
|
#include "ds_tools.h"
|
|
#include "bgBottom.h"
|
|
#include "bgTop.h"
|
|
#include "bgFileSel.h"
|
|
#include "bgOptions.h"
|
|
|
|
#include "clickNoQuit_wav.h"
|
|
#include "clickQuit_wav.h"
|
|
|
|
#include "Emulator.h"
|
|
#include "InputProducerManager.h"
|
|
#include "Rip.h"
|
|
|
|
typedef enum _RunState
|
|
{
|
|
Stopped,
|
|
Paused,
|
|
Running,
|
|
Quit
|
|
} RunState;
|
|
|
|
static short key_A_map = 12;
|
|
static short key_B_map = 12;
|
|
static short key_X_map = 13;
|
|
static short key_Y_map = 14;
|
|
static short key_L_map = 0;
|
|
static short key_R_map = 1;
|
|
|
|
#define WAITVBL swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank();
|
|
|
|
int bg0, bg0b, bg1b;
|
|
bool bFirstGameLoaded = false;
|
|
|
|
ITCM_CODE 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++;
|
|
}
|
|
}
|
|
|
|
class AudioMixerDS : public AudioMixer
|
|
{
|
|
public:
|
|
AudioMixerDS(UINT16* buffer);
|
|
void resetProcessor();
|
|
void flushAudio();
|
|
void init(UINT32 sampleRate);
|
|
void release();
|
|
|
|
UINT16* outputBuffer;
|
|
UINT32 outputBufferSize;
|
|
UINT32 outputBufferWritePosition;
|
|
};
|
|
|
|
AudioMixerDS::AudioMixerDS(UINT16* buffer)
|
|
: outputBuffer(NULL),
|
|
outputBufferSize(0),
|
|
outputBufferWritePosition(0)
|
|
{
|
|
this->outputBuffer = buffer;
|
|
this->outputBufferWritePosition = 0;
|
|
this->outputBufferSize = 2048;
|
|
}
|
|
|
|
void AudioMixerDS::resetProcessor()
|
|
{
|
|
if (outputBuffer)
|
|
{
|
|
outputBufferWritePosition = 0;
|
|
}
|
|
|
|
// clears the emulator side of the audio mixer
|
|
AudioMixer::resetProcessor();
|
|
}
|
|
|
|
|
|
void AudioMixerDS::init(UINT32 sampleRate)
|
|
{
|
|
release();
|
|
|
|
outputBufferWritePosition = 0;
|
|
|
|
AudioMixer::init(sampleRate);
|
|
}
|
|
|
|
void AudioMixerDS::release()
|
|
{
|
|
AudioMixer::release();
|
|
}
|
|
|
|
void AudioMixerDS::flushAudio()
|
|
{
|
|
if (sampleCount < this->sampleSize) {
|
|
return;
|
|
}
|
|
|
|
// Write to DS ARM7 audio buffer?
|
|
memcpy(outputBuffer, this->sampleBuffer, this->sampleBufferSize);
|
|
|
|
// clear the sample count
|
|
AudioMixer::flushAudio();
|
|
}
|
|
|
|
|
|
|
|
class VideoBusDS : public VideoBus
|
|
{
|
|
public:
|
|
VideoBusDS();
|
|
void init(UINT32 width, UINT32 height);
|
|
void release();
|
|
void render();
|
|
};
|
|
|
|
VideoBusDS::VideoBusDS() { }
|
|
|
|
void VideoBusDS::init(UINT32 width, UINT32 height)
|
|
{
|
|
release();
|
|
|
|
// initialize the pixelBuffer
|
|
VideoBus::init(width, height);
|
|
}
|
|
|
|
void VideoBusDS::release()
|
|
{
|
|
VideoBus::release();
|
|
}
|
|
|
|
int debug1, debug2;
|
|
static int frames=0;
|
|
ITCM_CODE void VideoBusDS::render()
|
|
{
|
|
frames++;
|
|
VideoBus::render();
|
|
|
|
if (frames & 1) return;
|
|
UINT32 *ds_video=(UINT32*)0x06000000;
|
|
UINT32 *source_video = (UINT32*)pixelBuffer;
|
|
|
|
for (int j=0; j<192; j++)
|
|
{
|
|
memcpy(ds_video, source_video, 160);
|
|
ds_video += 64;
|
|
source_video += 40;
|
|
}
|
|
}
|
|
|
|
RunState runState;
|
|
Emulator *currentEmu;
|
|
Rip *currentRip;
|
|
VideoBus *videoBus;
|
|
AudioMixer *audioMixer;
|
|
|
|
|
|
BOOL LoadCart(const CHAR* filename)
|
|
{
|
|
if (strlen(filename) < 5)
|
|
return FALSE;
|
|
|
|
//convert .bin and .rom to .rip, since our emulation only knows how to run .rip
|
|
const CHAR* extStart = filename + strlen(filename) - 4;
|
|
if (strcmpi(extStart, ".int") == 0 || strcmpi(extStart, ".bin") == 0)
|
|
{
|
|
//load the bin file as a Rip
|
|
CHAR* cfgFilename = (CHAR*)"knowncarts.cfg";
|
|
currentRip = Rip::LoadBin(filename, cfgFilename);
|
|
if (currentRip == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CHAR fileSubname[MAX_PATH];
|
|
strcpy(fileSubname, filename);
|
|
strcat(fileSubname, ".rip");
|
|
}
|
|
else
|
|
{
|
|
//load the designated Rip
|
|
currentRip = Rip::LoadRip(filename);
|
|
if (currentRip == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
dsShowScreenEmu();
|
|
bFirstGameLoaded = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL LoadPeripheralRoms(Peripheral* peripheral)
|
|
{
|
|
UINT16 count = peripheral->GetROMCount();
|
|
for (UINT16 i = 0; i < count; i++) {
|
|
ROM* r = peripheral->GetROM(i);
|
|
if (r->isLoaded())
|
|
continue;
|
|
|
|
//TODO: get filenames from config file
|
|
//TODO: handle file loading errors
|
|
CHAR nextFile[MAX_PATH];
|
|
strcpy(nextFile, ".");
|
|
strcat(nextFile, "/");
|
|
strcat(nextFile, r->getDefaultFileName());
|
|
if (!r->load(nextFile, r->getDefaultFileOffset()))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL InitializeEmulator(void)
|
|
{
|
|
//release the current emulator
|
|
//ReleaseEmulator();
|
|
|
|
//find the currentEmulator required to run this RIP
|
|
currentEmu = Emulator::GetEmulatorByID(currentRip->GetTargetSystemID());
|
|
if (currentEmu == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//load the BIOS files required for this currentEmulator
|
|
if (!LoadPeripheralRoms(currentEmu))
|
|
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
|
|
return FALSE;
|
|
}
|
|
|
|
//hook the audio and video up to the currentEmulator
|
|
currentEmu->InitVideo(videoBus,currentEmu->GetVideoWidth(),currentEmu->GetVideoHeight());
|
|
currentEmu->InitAudio(audioMixer,22050);
|
|
|
|
//put the RIP in the currentEmulator
|
|
currentEmu->SetRip(currentRip);
|
|
|
|
//finally, run everything
|
|
currentEmu->Reset();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
char newFile[256];
|
|
void pollInputs(void)
|
|
{
|
|
extern int ds_key_input[16]; // Set to '1' if pressed... 0 if released
|
|
extern int ds_disc_input[16]; // Set to '1' if pressed... 0 if released.
|
|
unsigned short keys_pressed = keysCurrent();
|
|
|
|
for (int i=0; i<15; i++) ds_key_input[i] = 0;
|
|
for (int i=0; i<16; i++) ds_disc_input[i] = 0;
|
|
|
|
// Handle 8 directions on keypad... best we can do...
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
if (keys_pressed & KEY_RIGHT) ds_disc_input[2] = 1;
|
|
else if (keys_pressed & KEY_LEFT) ds_disc_input[14] = 1;
|
|
else ds_disc_input[0] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_DOWN)
|
|
{
|
|
if (keys_pressed & KEY_RIGHT) ds_disc_input[6] = 1;
|
|
else if (keys_pressed & KEY_LEFT) ds_disc_input[10] = 1;
|
|
else ds_disc_input[8] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_RIGHT)
|
|
{
|
|
ds_disc_input[4] = 1;
|
|
}
|
|
else if (keys_pressed & KEY_LEFT)
|
|
{
|
|
ds_disc_input[12] = 1;
|
|
}
|
|
|
|
if (keys_pressed & KEY_A) ds_key_input[key_A_map] = 1;
|
|
if (keys_pressed & KEY_B) ds_key_input[key_B_map] = 1;
|
|
if (keys_pressed & KEY_X) ds_key_input[key_X_map] = 1;
|
|
if (keys_pressed & KEY_Y) ds_key_input[key_Y_map] = 1;
|
|
if (keys_pressed & KEY_L) ds_key_input[key_L_map] = 1;
|
|
if (keys_pressed & KEY_R) ds_key_input[key_R_map] = 1;
|
|
|
|
if (keys_pressed & KEY_START)
|
|
{
|
|
if (bFirstGameLoaded) currentEmu->Reset();
|
|
}
|
|
if (keys_pressed & KEY_SELECT)
|
|
{
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (dsWaitForRom(newFile))
|
|
{
|
|
LoadCart(newFile);
|
|
InitializeEmulator();
|
|
}
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (127) | SOUND_SET_VOLUME);
|
|
}
|
|
|
|
// Now handle the on-screen Intellivision keypad...
|
|
if (keys_pressed & KEY_TOUCH)
|
|
{
|
|
//char tmp[12];
|
|
touchPosition touch;
|
|
touchRead(&touch);
|
|
//sprintf(tmp, "%3d %3d", touch.px, touch.py);
|
|
//dsPrintValue(0,1,0,tmp);
|
|
|
|
if (touch.px > 120 && touch.px < 155 && touch.py > 30 && touch.py < 60) ds_key_input[0] = 1;
|
|
if (touch.px > 158 && touch.px < 192 && touch.py > 30 && touch.py < 60) ds_key_input[1] = 1;
|
|
if (touch.px > 195 && touch.px < 230 && touch.py > 30 && touch.py < 60) ds_key_input[2] = 1;
|
|
|
|
if (touch.px > 120 && touch.px < 155 && touch.py > 65 && touch.py < 95) ds_key_input[3] = 1;
|
|
if (touch.px > 158 && touch.px < 192 && touch.py > 65 && touch.py < 95) ds_key_input[4] = 1;
|
|
if (touch.px > 195 && touch.px < 230 && touch.py > 65 && touch.py < 95) ds_key_input[5] = 1;
|
|
|
|
if (touch.px > 120 && touch.px < 155 && touch.py > 101 && touch.py < 135) ds_key_input[6] = 1;
|
|
if (touch.px > 158 && touch.px < 192 && touch.py > 101 && touch.py < 135) ds_key_input[7] = 1;
|
|
if (touch.px > 195 && touch.px < 230 && touch.py > 101 && touch.py < 135) ds_key_input[8] = 1;
|
|
|
|
if (touch.px > 120 && touch.px < 155 && touch.py > 140 && touch.py < 175) ds_key_input[9] = 1;
|
|
if (touch.px > 158 && touch.px < 192 && touch.py > 140 && touch.py < 175) ds_key_input[10] = 1;
|
|
if (touch.px > 195 && touch.px < 230 && touch.py > 140 && touch.py < 175) ds_key_input[11] = 1;
|
|
|
|
|
|
// RESET
|
|
if (touch.px > 23 && touch.px < 82 && touch.py > 34 && touch.py < 53)
|
|
{
|
|
if (bFirstGameLoaded) currentEmu->Reset();
|
|
}
|
|
|
|
// LOAD
|
|
if (touch.px > 23 && touch.px < 82 && touch.py > 72 && touch.py < 91)
|
|
{
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (dsWaitForRom(newFile))
|
|
{
|
|
LoadCart(newFile);
|
|
InitializeEmulator();
|
|
}
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (127) | SOUND_SET_VOLUME);
|
|
}
|
|
|
|
// CONFIG
|
|
if (touch.px > 23 && touch.px < 82 && touch.py > 111 && touch.py < 130)
|
|
{
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
dsChooseOptions();
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (127) | SOUND_SET_VOLUME);
|
|
}
|
|
|
|
// QUIT
|
|
if (touch.px > 23 && touch.px < 82 && touch.py > 150 && touch.py < 170)
|
|
{
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME);
|
|
if (dsWaitOnQuit())
|
|
{
|
|
runState = Quit;
|
|
}
|
|
fifoSendValue32(FIFO_USER_01,(1<<16) | (127) | SOUND_SET_VOLUME);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int full_speed=0;
|
|
int emu_frames=1;
|
|
void Run()
|
|
{
|
|
TIMER1_CR = 0;
|
|
TIMER1_DATA = 0;
|
|
TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024;
|
|
|
|
TIMER0_CR=0;
|
|
TIMER0_DATA=0;
|
|
TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024;
|
|
|
|
runState = Running;
|
|
while(runState == Running)
|
|
{
|
|
if (!full_speed)
|
|
{
|
|
while(TIMER0_DATA < (546*emu_frames))
|
|
;
|
|
}
|
|
|
|
// Have we processed 60 frames... start over...
|
|
if (++emu_frames == 60)
|
|
{
|
|
TIMER0_CR=0;
|
|
TIMER0_DATA=0;
|
|
TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024;
|
|
emu_frames=1;
|
|
}
|
|
|
|
//poll the input
|
|
pollInputs();
|
|
|
|
if (bFirstGameLoaded)
|
|
{
|
|
//run the emulation
|
|
currentEmu->Run();
|
|
|
|
// render the output
|
|
currentEmu->Render();
|
|
|
|
//flush the audio
|
|
currentEmu->FlushAudio();
|
|
}
|
|
|
|
if (TIMER1_DATA >= 32728) // 1000MS (1 sec)
|
|
{
|
|
char tmp[15];
|
|
TIMER1_CR = 0;
|
|
TIMER1_DATA = 0;
|
|
TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024;
|
|
if (frames > 0)
|
|
{
|
|
sprintf(tmp, "%03d", frames);
|
|
dsPrintValue(0,0,0,tmp);
|
|
}
|
|
//sprintf(tmp, "%4d %4d", debug1, debug2);
|
|
//dsPrintValue(0,1,0,tmp);
|
|
frames=0;
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
decompress(bgBottomTiles, bgGetGfxPtr(bg0b), LZ77Vram);
|
|
decompress(bgBottomMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
|
|
dmaCopy((void *) bgBottomPal,(u16*) BG_PALETTE_SUB,256*2);
|
|
unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32);
|
|
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
|
|
|
|
REG_BLDCNT=0; REG_BLDCNT_SUB=0; REG_BLDY=0; REG_BLDY_SUB=0;
|
|
|
|
// ShowStatusLine();
|
|
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
#define SOUND_SIZE 734
|
|
UINT16 soundBuf[SOUND_SIZE+8] = {0};
|
|
void dsMainLoop(void)
|
|
{
|
|
// -----------------------------------------------------------------------------------------
|
|
// Eventually these will be used to write to the DS screen and produce DS audio output...
|
|
// -----------------------------------------------------------------------------------------
|
|
videoBus = new VideoBusDS();
|
|
audioMixer = new AudioMixerDS(soundBuf);
|
|
|
|
dsShowScreenMain(true);
|
|
InitializeEmulator();
|
|
|
|
Run();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
void dsInstallSoundEmuFIFO(void)
|
|
{
|
|
FifoMessage msg;
|
|
msg.SoundPlay.data = &soundBuf;
|
|
msg.SoundPlay.freq = 22050;
|
|
msg.SoundPlay.volume = 127;
|
|
msg.SoundPlay.pan = 64;
|
|
msg.SoundPlay.loop = 1;
|
|
msg.SoundPlay.format = ((1)<<4) | SoundFormat_16Bit;
|
|
msg.SoundPlay.loopPoint = 0;
|
|
msg.SoundPlay.dataSize = SOUND_SIZE >> 2;
|
|
msg.type = EMUARM7_PLAY_SND;
|
|
fifoSendDatamsg(FIFO_USER_01, sizeof(msg), (u8*)&msg);
|
|
}
|
|
|
|
|
|
void vblankIntr()
|
|
{
|
|
}
|
|
|
|
UINT32 gamePalette[32] =
|
|
{
|
|
0x000000, 0x002DFF, 0xFF3D10, 0xC9CFAB,
|
|
0x386B3F, 0x00A756, 0xFAEA50, 0xFFFCFF,
|
|
0xBDACC8, 0x24B8FF, 0xFFB41F, 0x546E00,
|
|
0xFF4E57, 0xA496FF, 0x75CC80, 0xB51A58,
|
|
0x000000, 0x002DFF, 0xFF3D10, 0xC9CFAB,
|
|
0x386B3F, 0x00A756, 0xFAEA50, 0xFFFCFF,
|
|
0xBDACC8, 0x24B8FF, 0xFFB41F, 0x546E00,
|
|
0xFF4E57, 0xA496FF, 0x75CC80, 0xB51A58,
|
|
};
|
|
|
|
|
|
void dsInitPalette(void)
|
|
{
|
|
// Init DS Specific palette
|
|
for(int i = 0; i < 256; i++)
|
|
{
|
|
unsigned char r, g, b;
|
|
|
|
r = (unsigned char) ((gamePalette[i%32] & 0x00ff0000) >> 19);
|
|
g = (unsigned char) ((gamePalette[i%32] & 0x0000ff00) >> 11);
|
|
b = (unsigned char) ((gamePalette[i%32] & 0x000000ff) >> 3);
|
|
|
|
BG_PALETTE[i]=RGB15(r,g,b);
|
|
}
|
|
}
|
|
|
|
void dsInitScreenMain(void)
|
|
{
|
|
// Init vbl and hbl func
|
|
SetYtrigger(190); //trigger 2 lines before vsync
|
|
irqSet(IRQ_VBLANK, vblankIntr);
|
|
irqEnable(IRQ_VBLANK);
|
|
vramSetBankD(VRAM_D_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06860000 -
|
|
vramSetBankE(VRAM_E_LCD ); // Not using this for video but 64K of faster RAM always useful! Mapped at 0x06880000 -
|
|
vramSetBankF(VRAM_F_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06890000 -
|
|
vramSetBankG(VRAM_G_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06894000 -
|
|
vramSetBankH(VRAM_H_LCD ); // Not using this for video but 32K of faster RAM always useful! Mapped at 0x06898000 -
|
|
vramSetBankI(VRAM_I_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x068A0000 -
|
|
|
|
WAITVBL;
|
|
}
|
|
|
|
void dsShowScreenEmu(void)
|
|
{
|
|
videoSetMode(MODE_5_2D);
|
|
vramSetBankA(VRAM_A_MAIN_BG_0x06000000); // The main emulated (top screen) display.
|
|
vramSetBankB(VRAM_B_MAIN_BG_0x06060000); // This is where we will put our frame buffers to aid DMA Copy routines...
|
|
|
|
bg0 = bgInit(3, BgType_Bmp8, BgSize_B8_256x256, 0,0);
|
|
memset((void*)0x06000000, 0x00, 128*1024);
|
|
|
|
int stretch_x = ((160 / 256) << 8) | (160 % 256);
|
|
REG_BG3PA = stretch_x;
|
|
REG_BG3PB = 0; REG_BG3PC = 0;
|
|
REG_BG3PD = ((100 / 100) << 8) | (100 % 100) ;
|
|
REG_BG3X = 0;
|
|
REG_BG3Y = 0;
|
|
|
|
dsInitPalette();
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
ITCM_CODE void dsInitTimer(void)
|
|
{
|
|
TIMER0_DATA=0;
|
|
TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024;
|
|
}
|
|
|
|
void dsFreeEmu(void)
|
|
{
|
|
// Stop timer of sound
|
|
TIMER2_CR=0; irqDisable(IRQ_TIMER2);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Load Rom
|
|
//----------------------------------------------------------------------------------
|
|
FICA2600 vcsromlist[512];
|
|
unsigned short int countvcs=0, ucFicAct=0;
|
|
char szName[256];
|
|
char szName2[256];
|
|
|
|
|
|
// Find files (.int) available
|
|
int a26Filescmp (const void *c1, const void *c2)
|
|
{
|
|
FICA2600 *p1 = (FICA2600 *) c1;
|
|
FICA2600 *p2 = (FICA2600 *) c2;
|
|
|
|
if (p1->filename[0] == '.' && p2->filename[0] != '.')
|
|
return -1;
|
|
if (p2->filename[0] == '.' && p1->filename[0] != '.')
|
|
return 1;
|
|
if (p1->directory && !(p2->directory))
|
|
return -1;
|
|
if (p2->directory && !(p1->directory))
|
|
return 1;
|
|
return strcasecmp (p1->filename, p2->filename);
|
|
}
|
|
|
|
static char filenametmp[255];
|
|
void vcsFindFiles(void)
|
|
{
|
|
DIR *pdir;
|
|
struct dirent *pent;
|
|
|
|
countvcs = 0;
|
|
|
|
pdir = opendir(".");
|
|
|
|
if (pdir) {
|
|
|
|
while (((pent=readdir(pdir))!=NULL))
|
|
{
|
|
strcpy(filenametmp,pent->d_name);
|
|
if (pent->d_type == DT_DIR)
|
|
{
|
|
if (!( (filenametmp[0] == '.') && (strlen(filenametmp) == 1))) {
|
|
vcsromlist[countvcs].directory = true;
|
|
strcpy(vcsromlist[countvcs].filename,filenametmp);
|
|
countvcs++;
|
|
}
|
|
}
|
|
else {
|
|
if (strlen(filenametmp)>4) {
|
|
if ( (strcasecmp(strrchr(filenametmp, '.'), ".int") == 0) ) {
|
|
vcsromlist[countvcs].directory = false;
|
|
strcpy(vcsromlist[countvcs].filename,filenametmp);
|
|
countvcs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir(pdir);
|
|
}
|
|
if (countvcs)
|
|
qsort (vcsromlist, countvcs, sizeof (FICA2600), a26Filescmp);
|
|
}
|
|
|
|
void dsDisplayFiles(unsigned int NoDebGame,u32 ucSel)
|
|
{
|
|
unsigned int ucBcl,ucGame;
|
|
|
|
// Display all games if possible
|
|
unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32);
|
|
dmaFillWords(dmaVal | (dmaVal<<16),(void*) (bgGetMapPtr(bg1b)),32*24*2);
|
|
sprintf(szName,"%04d/%04d GAMES",(int)(1+ucSel+NoDebGame),countvcs);
|
|
dsPrintValue(16-strlen(szName)/2,2,0,szName);
|
|
dsPrintValue(31,5,0,(char *) (NoDebGame>0 ? "<" : " "));
|
|
dsPrintValue(31,22,0,(char *) (NoDebGame+14<countvcs ? ">" : " "));
|
|
sprintf(szName, "A=CHOOSE B=BACK ");
|
|
dsPrintValue(16-strlen(szName)/2,23,0,szName);
|
|
for (ucBcl=0;ucBcl<17; ucBcl++) {
|
|
ucGame= ucBcl+NoDebGame;
|
|
if (ucGame < countvcs)
|
|
{
|
|
strcpy(szName,vcsromlist[ucGame].filename);
|
|
szName[29]='\0';
|
|
if (vcsromlist[ucGame].directory)
|
|
{
|
|
szName[27]='\0';
|
|
sprintf(szName2,"[%s]",szName);
|
|
dsPrintValue(0,5+ucBcl,(ucSel == ucBcl ? 1 : 0),szName2);
|
|
}
|
|
else
|
|
{
|
|
dsPrintValue(1,5+ucBcl,(ucSel == ucBcl ? 1 : 0),szName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
unsigned int dsWaitForRom(char *chosen_filename)
|
|
{
|
|
bool bDone=false, bRet=false;
|
|
u32 ucHaut=0x00, ucBas=0x00,ucSHaut=0x00, ucSBas=0x00,romSelected= 0, firstRomDisplay=0,nbRomPerPage, uNbRSPage;
|
|
u32 uLenFic=0, ucFlip=0, ucFlop=0;
|
|
|
|
strcpy(chosen_filename, "tmpz");
|
|
vcsFindFiles(); // Initial get of files...
|
|
|
|
decompress(bgFileSelTiles, bgGetGfxPtr(bg0b), LZ77Vram);
|
|
decompress(bgFileSelMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
|
|
dmaCopy((void *) bgFileSelPal,(u16*) BG_PALETTE_SUB,256*2);
|
|
unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32);
|
|
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
|
|
|
|
nbRomPerPage = (countvcs>=17 ? 17 : countvcs);
|
|
uNbRSPage = (countvcs>=5 ? 5 : countvcs);
|
|
|
|
if (ucFicAct>countvcs-nbRomPerPage)
|
|
{
|
|
firstRomDisplay=countvcs-nbRomPerPage;
|
|
romSelected=ucFicAct-countvcs+nbRomPerPage;
|
|
}
|
|
else
|
|
{
|
|
firstRomDisplay=ucFicAct;
|
|
romSelected=0;
|
|
}
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
while (!bDone)
|
|
{
|
|
if (keysCurrent() & KEY_UP)
|
|
{
|
|
if (!ucHaut)
|
|
{
|
|
ucFicAct = (ucFicAct>0 ? ucFicAct-1 : countvcs-1);
|
|
if (romSelected>uNbRSPage) { romSelected -= 1; }
|
|
else {
|
|
if (firstRomDisplay>0) { firstRomDisplay -= 1; }
|
|
else {
|
|
if (romSelected>0) { romSelected -= 1; }
|
|
else {
|
|
firstRomDisplay=countvcs-nbRomPerPage;
|
|
romSelected=nbRomPerPage-1;
|
|
}
|
|
}
|
|
}
|
|
ucHaut=0x01;
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
else {
|
|
|
|
ucHaut++;
|
|
if (ucHaut>10) ucHaut=0;
|
|
}
|
|
uLenFic=0; ucFlip=0; ucFlop=0;
|
|
}
|
|
else
|
|
{
|
|
ucHaut = 0;
|
|
}
|
|
if (keysCurrent() & KEY_DOWN)
|
|
{
|
|
if (!ucBas) {
|
|
ucFicAct = (ucFicAct< countvcs-1 ? ucFicAct+1 : 0);
|
|
if (romSelected<uNbRSPage-1) { romSelected += 1; }
|
|
else {
|
|
if (firstRomDisplay<countvcs-nbRomPerPage) { firstRomDisplay += 1; }
|
|
else {
|
|
if (romSelected<nbRomPerPage-1) { romSelected += 1; }
|
|
else {
|
|
firstRomDisplay=0;
|
|
romSelected=0;
|
|
}
|
|
}
|
|
}
|
|
ucBas=0x01;
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
else
|
|
{
|
|
ucBas++;
|
|
if (ucBas>10) ucBas=0;
|
|
}
|
|
uLenFic=0; ucFlip=0; ucFlop=0;
|
|
}
|
|
else {
|
|
ucBas = 0;
|
|
}
|
|
if ((keysCurrent() & KEY_R) || (keysCurrent() & KEY_RIGHT))
|
|
{
|
|
if (!ucSBas)
|
|
{
|
|
ucFicAct = (ucFicAct< countvcs-nbRomPerPage ? ucFicAct+nbRomPerPage : countvcs-nbRomPerPage);
|
|
if (firstRomDisplay<countvcs-nbRomPerPage) { firstRomDisplay += nbRomPerPage; }
|
|
else { firstRomDisplay = countvcs-nbRomPerPage; }
|
|
if (ucFicAct == countvcs-nbRomPerPage) romSelected = 0;
|
|
ucSBas=0x01;
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
else
|
|
{
|
|
ucSBas++;
|
|
if (ucSBas>10) ucSBas=0;
|
|
}
|
|
uLenFic=0; ucFlip=0; ucFlop=0;
|
|
}
|
|
else {
|
|
ucSBas = 0;
|
|
}
|
|
if ((keysCurrent() & KEY_L) || (keysCurrent() & KEY_LEFT))
|
|
{
|
|
if (!ucSHaut)
|
|
{
|
|
ucFicAct = (ucFicAct> nbRomPerPage ? ucFicAct-nbRomPerPage : 0);
|
|
if (firstRomDisplay>nbRomPerPage) { firstRomDisplay -= nbRomPerPage; }
|
|
else { firstRomDisplay = 0; }
|
|
if (ucFicAct == 0) romSelected = 0;
|
|
if (romSelected > ucFicAct) romSelected = ucFicAct;
|
|
ucSHaut=0x01;
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
else
|
|
{
|
|
ucSHaut++;
|
|
if (ucSHaut>10) ucSHaut=0;
|
|
}
|
|
uLenFic=0; ucFlip=0; ucFlop=0;
|
|
}
|
|
else {
|
|
ucSHaut = 0;
|
|
}
|
|
|
|
if ( keysCurrent() & KEY_B )
|
|
{
|
|
bDone=true;
|
|
while (keysCurrent() & KEY_B);
|
|
}
|
|
|
|
if (keysCurrent() & KEY_A || keysCurrent() & KEY_Y)
|
|
{
|
|
if (!vcsromlist[ucFicAct].directory)
|
|
{
|
|
bRet=true;
|
|
bDone=true;
|
|
strcpy(chosen_filename, vcsromlist[ucFicAct].filename);
|
|
if (keysCurrent() & KEY_Y) full_speed=1; else full_speed=0;
|
|
}
|
|
else
|
|
{
|
|
chdir(vcsromlist[ucFicAct].filename);
|
|
vcsFindFiles();
|
|
ucFicAct = 0;
|
|
nbRomPerPage = (countvcs>=16 ? 16 : countvcs);
|
|
uNbRSPage = (countvcs>=5 ? 5 : countvcs);
|
|
if (ucFicAct>countvcs-nbRomPerPage) {
|
|
firstRomDisplay=countvcs-nbRomPerPage;
|
|
romSelected=ucFicAct-countvcs+nbRomPerPage;
|
|
}
|
|
else {
|
|
firstRomDisplay=ucFicAct;
|
|
romSelected=0;
|
|
}
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
while (keysCurrent() & KEY_A);
|
|
}
|
|
}
|
|
|
|
// If the filename is too long... scroll it.
|
|
if (strlen(vcsromlist[ucFicAct].filename) > 29)
|
|
{
|
|
ucFlip++;
|
|
if (ucFlip >= 15)
|
|
{
|
|
ucFlip = 0;
|
|
uLenFic++;
|
|
if ((uLenFic+29)>strlen(vcsromlist[ucFicAct].filename))
|
|
{
|
|
ucFlop++;
|
|
if (ucFlop >= 15)
|
|
{
|
|
uLenFic=0;
|
|
ucFlop = 0;
|
|
}
|
|
else
|
|
uLenFic--;
|
|
}
|
|
strncpy(szName,vcsromlist[ucFicAct].filename+uLenFic,29);
|
|
szName[29] = '\0';
|
|
dsPrintValue(1,5+romSelected,1,szName);
|
|
}
|
|
}
|
|
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
dsShowScreenMain(false);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
bool dsWaitOnQuit(void)
|
|
{
|
|
bool bRet=false, bDone=false;
|
|
unsigned int keys_pressed;
|
|
unsigned int posdeb=0;
|
|
char szName[32];
|
|
|
|
decompress(bgFileSelTiles, bgGetGfxPtr(bg0b), LZ77Vram);
|
|
decompress(bgFileSelMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
|
|
dmaCopy((void *) bgFileSelPal,(u16*) BG_PALETTE_SUB,256*2);
|
|
unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32);
|
|
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
|
|
|
|
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;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Options are handled here... we have a number of things the user can tweak
|
|
// and these options are applied immediately. The user can also save off
|
|
// their option choices for the currently running game into the XEGS.DAT
|
|
// configuration database. When games are loaded back up, XEGS.DAT is read
|
|
// to see if we have a match and the user settings can be restored for the
|
|
// game.
|
|
// -----------------------------------------------------------------------------
|
|
struct options_t
|
|
{
|
|
const char *label;
|
|
const char *option[16];
|
|
short *option_val;
|
|
short option_max;
|
|
};
|
|
|
|
const struct options_t Option_Table[] =
|
|
{
|
|
{"A BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_A_map, 15},
|
|
{"B BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_B_map, 15},
|
|
{"X BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_X_map, 15},
|
|
{"Y BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_Y_map, 15},
|
|
{"L BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_L_map, 15},
|
|
{"R BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_R_map, 15},
|
|
{NULL, {"", ""}, NULL, 2},
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Allows the user to move the cursor up and down through the various table
|
|
// enties above to select options for the game they wish to play.
|
|
// -----------------------------------------------------------------------------
|
|
void dsChooseOptions(void)
|
|
{
|
|
int optionHighlighted;
|
|
int idx;
|
|
bool bDone=false;
|
|
int keys_pressed;
|
|
int last_keys_pressed = 999;
|
|
char strBuf[64];
|
|
|
|
// Show the Options background...
|
|
decompress(bgOptionsTiles, bgGetGfxPtr(bg0b), LZ77Vram);
|
|
decompress(bgOptionsMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
|
|
dmaCopy((void *) bgOptionsPal,(u16*) BG_PALETTE_SUB,256*2);
|
|
unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32);
|
|
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
|
|
|
|
idx=0;
|
|
while (true)
|
|
{
|
|
sprintf(strBuf, " %-12s : %-12s ", Option_Table[idx].label, Option_Table[idx].option[*(Option_Table[idx].option_val)]);
|
|
dsPrintValue(1,5+idx, (idx==0 ? 1:0), strBuf);
|
|
idx++;
|
|
if (Option_Table[idx].label == NULL) break;
|
|
}
|
|
|
|
dsPrintValue(2,23, 0, (char *)"A=TOGGLE, B=SAVE+EXIT");
|
|
optionHighlighted = 0;
|
|
while (!bDone)
|
|
{
|
|
keys_pressed = keysCurrent();
|
|
if (keys_pressed != last_keys_pressed)
|
|
{
|
|
last_keys_pressed = keys_pressed;
|
|
if (keysCurrent() & KEY_UP) // Previous option
|
|
{
|
|
sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,5+optionHighlighted,0, strBuf);
|
|
if (optionHighlighted > 0) optionHighlighted--; else optionHighlighted=(idx-1);
|
|
sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,5+optionHighlighted,1, strBuf);
|
|
}
|
|
if (keysCurrent() & KEY_DOWN) // Next option
|
|
{
|
|
sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,5+optionHighlighted,0, strBuf);
|
|
if (optionHighlighted < (idx-1)) optionHighlighted++; else optionHighlighted=0;
|
|
sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,5+optionHighlighted,1, strBuf);
|
|
}
|
|
|
|
if (keysCurrent() & KEY_A) // Toggle option
|
|
{
|
|
*(Option_Table[optionHighlighted].option_val) = (*(Option_Table[optionHighlighted].option_val) + 1) % Option_Table[optionHighlighted].option_max;
|
|
sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,5+optionHighlighted,1, strBuf);
|
|
}
|
|
if (keysCurrent() & KEY_START) // Save Options
|
|
{
|
|
// TODO: write them out... dsWriteConfig();
|
|
}
|
|
if (keysCurrent() & KEY_B) // Exit options
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
// Restore original bottom graphic
|
|
dsShowScreenMain(false);
|
|
|
|
// Give a third of a second time delay...
|
|
for (int i=0; i<20; i++)
|
|
{
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// End of Line
|
|
|