add SNES and Atari Lynx emulation

master
jean-marcharvengt 2022-06-11 21:22:05 +02:00
rodzic 2a69fb40ec
commit f5208fa857
68 zmienionych plików z 22774 dodań i 3330 usunięć

Wyświetl plik

@ -1140,8 +1140,8 @@ static void lineOSKB(int xoff, bool bottom, char * str, int row)
char c[4] = {' ',0,' ',0};
const char * cpt = str;
int i=0;
int fb_width,fb_height;
tft.get_frame_buffer_size(&fb_width, &fb_height);
int fb_width,fb_height,fb_stride;
tft.get_frame_buffer_size(&fb_width, &fb_height, &fb_stride);
int ypos = (bottom?(fb_height-2*8):0);
int line = row + (bottom?2:0);
while ((c[1] = *cpt++))

Wyświetl plik

@ -512,9 +512,10 @@ void TFT_T_DMA::wait(void) {
rstop = 0;
}
int TFT_T_DMA::get_frame_buffer_size(int *width, int *height){
int TFT_T_DMA::get_frame_buffer_size(int *width, int *height, int *stride){
if (width != nullptr) *width = TFT_REALWIDTH;
if (height != nullptr) *height = TFT_REALHEIGHT;
if (stride != nullptr) *stride = TFT_REALWIDTH;
return TFT_REALWIDTH;
}

Wyświetl plik

@ -190,7 +190,7 @@ class TFT_T_DMA
boolean isflipped(void);
void startDMA(void);
void stopDMA();
int get_frame_buffer_size(int *width, int *height);
int get_frame_buffer_size(int *width, int *height, int *stride);
// Touch screen functions
#define TOUCH_ENABLED() ((_touch_cs != 255) && (_touch_irq != 255))
@ -230,5 +230,3 @@ class TFT_T_DMA
#endif
#endif

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,415 @@
#include "emuapi.h"
#ifdef HAS_SND
#include "AudioPlaySystem.h"
#include <Arduino.h>
#define SAMPLERATE AUDIO_SAMPLE_RATE_EXACT
#define CLOCKFREQ 985248
#ifndef CUSTOM_SND
PROGMEM static const short square[]={
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
};
PROGMEM const short noise[] {
-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,32767,-32767,
-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,
-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,32767,32767,-32767,
-32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,-32767,32767,32767,-32767,
-32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,-32767,-32767,
32767,-32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767,-32767,
32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,32767,32767,-32767,32767,-32767,32767,-32767,-32767,
32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,32767,-32767,32767,-32767,-32767,
};
#define NOISEBSIZE 0x100
typedef struct
{
unsigned int spos;
unsigned int sinc;
unsigned int vol;
} Channel;
static Channel chan[6] = {
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0} };
#endif
volatile bool playing = false;
static void snd_Reset(void)
{
#ifndef CUSTOM_SND
chan[0].vol = 0;
chan[1].vol = 0;
chan[2].vol = 0;
chan[3].vol = 0;
chan[4].vol = 0;
chan[5].vol = 0;
chan[0].sinc = 0;
chan[1].sinc = 0;
chan[2].sinc = 0;
chan[3].sinc = 0;
chan[4].sinc = 0;
chan[5].sinc = 0;
#endif
}
#ifdef CUSTOM_SND
//extern "C" {
void SND_Process(void *sndbuffer, int sndn);
//}
#endif
FASTRUN void AudioPlaySystem::snd_Mixer(short * stream, int len )
{
if (playing)
{
#ifdef CUSTOM_SND
SND_Process((void*)stream, len);
#else
int i;
long s;
len = len >> 1;
short v0=chan[0].vol;
short v1=chan[1].vol;
short v2=chan[2].vol;
short v3=chan[3].vol;
short v4=chan[4].vol;
short v5=chan[5].vol;
for (i=0;i<len;i++)
{
s =((v0*square[(chan[0].spos>>8)&0x3f])>>11);
s+=((v1*square[(chan[1].spos>>8)&0x3f])>>11);
s+=((v2*square[(chan[2].spos>>8)&0x3f])>>11);
s+=((v3*noise[(chan[3].spos>>8)&(NOISEBSIZE-1)])>>11);
s+=((v4*noise[(chan[4].spos>>8)&(NOISEBSIZE-1)])>>11);
s+=((v5*noise[(chan[5].spos>>8)&(NOISEBSIZE-1)])>>11);
*stream++ = (short)(s);
*stream++ = (short)(s);
chan[0].spos += chan[0].sinc;
chan[1].spos += chan[1].sinc;
chan[2].spos += chan[2].sinc;
chan[3].spos += chan[3].sinc;
chan[4].spos += chan[4].sinc;
chan[5].spos += chan[5].sinc;
}
#endif
}
}
void AudioPlaySystem::begin(void)
{
this->reset();
}
void AudioPlaySystem::start(void)
{
playing = true;
}
void AudioPlaySystem::setSampleParameters(float clockfreq, float samplerate) {
}
void AudioPlaySystem::reset(void)
{
snd_Reset();
}
void AudioPlaySystem::stop(void)
{
//__disable_irq();
playing = false;
//__enable_irq();
}
bool AudioPlaySystem::isPlaying(void)
{
return playing;
}
void AudioPlaySystem::sound(int C, int F, int V) {
#ifndef CUSTOM_SND
if (C < 6) {
chan[C].vol = V;
chan[C].sinc = F>>1;
}
#endif
}
void AudioPlaySystem::step(void) {
}
/*******************************************************************
Experimental I2S interrupt based sound driver for PCM51xx !!!
*******************************************************************/
FLASHMEM static void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4
{
if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
| CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
| CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
const int div_post_pll = 1; // other values: 2,4
CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass
}
#define AUDIO_SAMPLE_RATE_EXACT 11025.0 //44117.64706 //11025.0 //22050.0 //44117.64706 //31778.0
FLASHMEM static void config_sai1()
{
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
double fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = (fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2, true);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
n1 = n1 / 2; //Double Speed for TDM
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK
// configure transmitter
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF
| I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1));
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1));
//CORE_PIN23_CONFIG = 3; // MCLK
CORE_PIN21_CONFIG = 3; // RX_BCLK
CORE_PIN20_CONFIG = 3; // RX_SYNC
CORE_PIN7_CONFIG = 3; // TX_DATA0
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;//<-- not using DMA */;
}
FLASHMEM static void config_pt8211()
{
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
double fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = (fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2, true);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
//n1 = n1 / 2; //Double Speed for TDM
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK
// configure transmitter
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(0);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(1);
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSD /*| I2S_TCR4_FSE*/ | I2S_TCR4_FSP ; //PT8211
I2S1_TCR5 = I2S_TCR5_WNW(15) | I2S_TCR5_W0W(15) | I2S_TCR5_FBT(15);
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(0);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP | I2S_RCR2_MSEL(1)| I2S_RCR2_BCD | I2S_RCR2_DIV(1) ;
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(15) | I2S_RCR4_MF /*| I2S_RCR4_FSE*/ | I2S_RCR4_FSP | I2S_RCR4_FSD; //PT8211
I2S1_RCR5 = I2S_RCR5_WNW(15) | I2S_RCR5_W0W(15) | I2S_RCR5_FBT(15);
CORE_PIN21_CONFIG = 3; // RX_BCLK
CORE_PIN20_CONFIG = 3; // RX_SYNC
CORE_PIN7_CONFIG = 3; // TX_DATA0
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;//<-- not using DMA */;
}
//DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx[1024];
static bool fillfirsthalf = true;
static uint16_t cnt = 0;
static uint16_t sampleBufferSize = 0;
static void (*fillsamples)(short * stream, int len) = nullptr;
static uint32_t * i2s_tx_buffer __attribute__((aligned(32)));
static uint16_t * i2s_tx_buffer16;
static uint16_t * txreg = (uint16_t *)((uint32_t)&I2S1_TDR0 + 2);
FASTRUN void AudioPlaySystem::AUDIO_isr() {
*txreg = i2s_tx_buffer16[cnt];
cnt = cnt + 1;
cnt = cnt & (sampleBufferSize*2-1);
if (cnt == 0) {
fillfirsthalf = false;
NVIC_SET_PENDING(IRQ_SOFTWARE);
}
else if (cnt == sampleBufferSize) {
fillfirsthalf = true;
NVIC_SET_PENDING(IRQ_SOFTWARE);
}
/*
I2S1_TDR0 = i2s_tx_buffer[cnt];
cnt = cnt + 1;
cnt = cnt & (sampleBufferSize-1);
if (cnt == 0) {
fillfirsthalf = false;
NVIC_SET_PENDING(IRQ_SOFTWARE);
}
else if (cnt == sampleBufferSize/2) {
fillfirsthalf = true;
NVIC_SET_PENDING(IRQ_SOFTWARE);
}
*/
}
FASTRUN void AudioPlaySystem::SOFTWARE_isr() {
//Serial.println("x");
if (fillfirsthalf) {
fillsamples((short *)i2s_tx_buffer, sampleBufferSize);
arm_dcache_flush_delete((void*)i2s_tx_buffer, (sampleBufferSize/2)*sizeof(uint32_t));
}
else {
fillsamples((short *)&i2s_tx_buffer[sampleBufferSize/2], sampleBufferSize);
arm_dcache_flush_delete((void*)&i2s_tx_buffer[sampleBufferSize/2], (sampleBufferSize/2)*sizeof(uint32_t));
}
}
// display VGA image
FLASHMEM void AudioPlaySystem::begin_audio(int samplesize, void (*callback)(short * stream, int len))
{
fillsamples = callback;
i2s_tx_buffer = (uint32_t*)malloc(samplesize*sizeof(uint32_t)); //&i2s_tx[0];
if (i2s_tx_buffer == NULL) {
Serial.println("could not allocate audio samples");
return;
}
memset((void*)i2s_tx_buffer,0, samplesize*sizeof(uint32_t));
arm_dcache_flush_delete((void*)i2s_tx_buffer, samplesize*sizeof(uint32_t));
i2s_tx_buffer16 = (uint16_t*)i2s_tx_buffer;
sampleBufferSize = samplesize;
#ifdef PT8211
txreg = (uint16_t *)((uint32_t)&I2S1_TDR0);
config_pt8211();
#else
txreg = (uint16_t *)((uint32_t)&I2S1_TDR0 + 2);
config_sai1();
#endif
attachInterruptVector(IRQ_SAI1, AUDIO_isr);
NVIC_ENABLE_IRQ(IRQ_SAI1);
NVIC_SET_PRIORITY(IRQ_QTIMER3, 0); // 0 highest priority, 255 = lowest priority
NVIC_SET_PRIORITY(IRQ_SAI1, 127);
attachInterruptVector(IRQ_SOFTWARE, SOFTWARE_isr);
NVIC_SET_PRIORITY(IRQ_SOFTWARE, 208);
NVIC_ENABLE_IRQ(IRQ_SOFTWARE);
I2S1_TCSR |= 1<<8; // start generating TX FIFO interrupts
Serial.print("Audio sample buffer = ");
Serial.println(samplesize);
}
FLASHMEM void AudioPlaySystem::end_audio()
{
if (i2s_tx_buffer != NULL) {
free(i2s_tx_buffer);
}
}
#endif

Wyświetl plik

@ -0,0 +1,31 @@
#ifndef audioplaysystem_h_
#define audioplaysystem_h_
#ifdef HAS_SND
#include "platform_config.h"
class AudioPlaySystem
{
public:
AudioPlaySystem(void) { };
void begin(void);
void setSampleParameters(float clockfreq, float samplerate);
void reset(void);
void start(void);
void stop(void);
bool isPlaying(void);
void sound(int C, int F, int V);
void buzz(int size, int val);
void step(void);
static void snd_Mixer(short * stream, int len );
void begin_audio(int samplesize, void (*callback)(short * stream, int len));
void end_audio();
static void AUDIO_isr(void);
static void SOFTWARE_isr(void);
};
#endif
#endif

Wyświetl plik

@ -0,0 +1,851 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// 65C02 Macro definitions //
//////////////////////////////////////////////////////////////////////////////
// //
// This file contains all of the required address mode and operand //
// macro definitions for the 65C02 emulation //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
//
// Addressing mode decoding
//
#define xIMMEDIATE() {mOperand=mPC;mPC++;}
#define xABSOLUTE() {mOperand=CPU_PEEKW(mPC);mPC+=2;}
#define xZEROPAGE() {mOperand=CPU_PEEK_RAM(mPC);mPC++;}
#define xZEROPAGE_X() {mOperand=CPU_PEEK_RAM(mPC)+mX;mPC++;mOperand&=0xff;}
#define xZEROPAGE_Y() {mOperand=CPU_PEEK_RAM(mPC)+mY;mPC++;mOperand&=0xff;}
#define xABSOLUTE_X() {mOperand=CPU_PEEKW(mPC);mPC+=2;mOperand+=mX;mOperand&=0xffff;}
#define xABSOLUTE_Y() {mOperand=CPU_PEEKW(mPC);mPC+=2;mOperand+=mY;mOperand&=0xffff;}
#define xINDIRECT_ABSOLUTE_X() {mOperand=CPU_PEEKW(mPC);mPC+=2;mOperand+=mX;mOperand&=0xffff;mOperand=CPU_PEEKW(mOperand);}
#define xRELATIVE() {mOperand=CPU_PEEK(mPC);mPC++;mOperand=(mPC+mOperand)&0xffff;}
#define xINDIRECT_X() {mOperand=CPU_PEEK(mPC);mPC++;mOperand=mOperand+mX;mOperand&=0x00ff;mOperand=CPU_PEEKW(mOperand);}
#define xINDIRECT_Y() {mOperand=CPU_PEEK(mPC);mPC++;mOperand=CPU_PEEKW(mOperand);mOperand=mOperand+mY;mOperand&=0xffff;}
#define xINDIRECT_ABSOLUTE() {mOperand=CPU_PEEKW(mPC);mPC+=2;mOperand=CPU_PEEKW(mOperand);}
#define xINDIRECT() {mOperand=CPU_PEEK(mPC);mPC++;mOperand=CPU_PEEKW(mOperand);}
//
// Helper Macros
//
//#define SET_Z(m) { mZ=(m)?false:true; }
//#define SET_N(m) { mN=(m&0x80)?true:false; }
//#define SET_NZ(m) SET_Z(m) SET_N(m)
#define SET_Z(m) { mZ=!(m); }
#define SET_N(m) { mN=(m)&0x80; }
#define SET_NZ(m) { mZ=!(m); mN=(m)&0x80; }
#define PULL(m) { mSP++; mSP&=0xff; m=CPU_PEEK_RAM(mSP+0x0100); }
#define PUSH(m) { CPU_POKE_RAM(0x0100+mSP,m); mSP--; mSP&=0xff; }
//
// Opcode execution
//
/*
#define xADC()\
{\
UBYTE value=CPU_PEEK(mOperand);\
UBYTE oldA=mA;\
if(!mD)\
{\
SWORD sum=(SWORD)((SBYTE)mA)+(SWORD)((SBYTE)value)+(mC?1:0);\
mV=((sum > 127) || (sum < -128));\
sum=(SWORD)mA + (SWORD)value + (mC?1:0);\
mA=(UBYTE)sum;\
mC=(sum>0xff);\
SET_NZ(mA);\
}\
else\
{\
SWORD sum=mBCDTable[0][mA]+mBCDTable[0][value]+(mC?1:0);\
mC=(sum > 99);\
mA=mBCDTable[1][sum & 0xff];\
SET_NZ(mA);\
mV=((oldA^mA)&0x80) && ((mA^value)&0x80);\
}\
}
*/
#define xADC()\
{\
int value=CPU_PEEK(mOperand);\
if(mD)\
{\
int c = mC?1:0;\
int lo = (mA & 0x0f) + (value & 0x0f) + c;\
int hi = (mA & 0xf0) + (value & 0xf0);\
mV=0;\
mC=0;\
if (lo > 0x09)\
{\
hi += 0x10;\
lo += 0x06;\
}\
if (~(mA^value) & (mA^hi) & 0x80) mV=1;\
if (hi > 0x90) hi += 0x60;\
if (hi & 0xff00) mC=1;\
mA = (lo & 0x0f) + (hi & 0xf0);\
}\
else\
{\
int c = mC?1:0;\
int sum = mA + value + c;\
mV=0;\
mC=0;\
if (~(mA^value) & (mA^sum) & 0x80) mV=1;\
if (sum & 0xff00) mC=1;\
mA = (UBYTE) sum;\
}\
SET_NZ(mA)\
}
#define xAND()\
{\
mA&=CPU_PEEK(mOperand);\
SET_NZ(mA);\
}
#define xASL()\
{\
int value=CPU_PEEK(mOperand);\
mC=value&0x80;\
value<<=1;\
value&=0xff;\
SET_NZ(value);\
CPU_POKE(mOperand,value);\
}
#define xASLA()\
{\
mC=mA&0x80;\
mA<<=1;\
mA&=0xff;\
SET_NZ(mA);\
}
#define xBCC()\
{\
if(!mC)\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}\
else\
{\
mPC++;\
mPC&=0xffff;\
}\
}
#define xBCS()\
{\
if(mC)\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}\
else\
{\
mPC++;\
mPC&=0xffff;\
}\
}
#define xBEQ()\
{\
if(mZ)\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}\
else\
{\
mPC++;\
mPC&=0xffff;\
}\
}
// This version of bit, not setting N and V status flags in immediate, seems to be correct.
// The same behaviour is reported on the 65C02 used in old Apple computers, at least.
// (From a pragmatic sense, using the normal version of bit for immediate
// mode breaks the title screen of "California Games" in a subtle way.)
#define xBIT()\
{\
int value=CPU_PEEK(mOperand);\
SET_Z(mA&value);\
\
if(mOpcode!=0x89)\
{\
mN=value&0x80;\
mV=value&0x40;\
}\
}
//
// DONT USE THIS VERSION OF BIT, IT BREAKS CALGAMES TITLE SCREEN !!!!
//
/*
#define xBIT()\
{\
int value=CPU_PEEK(mOperand);\
SET_Z(mA&value);\
mN=value&0x80;\
mV=value&0x40;\
}
*/
#define xBMI()\
{\
if(mN)\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}\
else\
{\
mPC++;\
mPC&=0xffff;\
}\
}
#define xBNE()\
{\
if(!mZ)\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}\
else\
{\
mPC++;\
mPC&=0xffff;\
}\
}
#define xBPL()\
{\
if(!mN)\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}\
else\
{\
mPC++;\
mPC&=0xffff;\
}\
}
#define xBRA()\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}
/*
#define xBRK()\
{\
mPC++;\
PUSH(mPC>>8);\
PUSH(mPC&0xff);\
PUSH(PS()|0x10);\
mD=FALSE;\
mI=TRUE;\
mPC=CPU_PEEKW(IRQ_VECTOR);\
}
*/
#define xBRK()\
{\
mPC++;\
PUSH(mPC>>8);\
PUSH(mPC&0xff);\
PUSH(PS()|0x10);\
\
mD=FALSE;\
mI=TRUE;\
\
mPC=CPU_PEEKW(IRQ_VECTOR);\
}
// KW 4/11/98 B flag needed to be set IN the stack status word = 0x10.
#define xBVC()\
{\
if(!mV)\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}\
else\
{\
mPC++;\
mPC&=0xffff;\
}\
}
#define xBVS()\
{\
if(mV)\
{\
int offset=(signed char)CPU_PEEK(mPC);\
mPC++;\
mPC+=offset;\
mPC&=0xffff;\
}\
else\
{\
mPC++;\
mPC&=0xffff;\
}\
}
#define xCLC()\
{\
mC=FALSE;\
}
#define xCLD()\
{\
mD=FALSE;\
}
#define xCLI()\
{\
mI=FALSE;\
}
#define xCLV()\
{\
mV=FALSE;\
}
//
// Alternate CMP code
//
/*
#define xCMP()\
{\
UBYTE value=CPU_PEEK(mOperand);\
if(mA+0x100-value>0xff) mC=TRUE; else mC=FALSE;\
value=mA+0x100-value;\
mZ=!value;\
mN=value&0x0080;\
}
#define xCPX()\
{\
UBYTE value=CPU_PEEK(mOperand);\
if(mX+0x100-value>0xff) mC=TRUE; else mC=FALSE;\
value=mX+0x100-value;\
mZ=!value;\
mN=value&0x0080;\
}
#define xCPY()\
{\
UBYTE value=CPU_PEEK(mOperand);\
if(mY+0x100-value>0xff) mC=TRUE; else mC=FALSE;\
value=mY+0x100-value;\
mZ=!value;\
mN=value&0x0080;\
}
#define xCMP()\
{\
UWORD value=(UWORD)mA-CPU_PEEK(mOperand);\
SET_NZ(value);\
mC=!(value&0x0100);\
}
#define xCPX()\
{\
UWORD value=(UWORD)mX-CPU_PEEK(mOperand);\
SET_NZ(value);\
mC=!(value&0x0100);\
}
#define xCPY()\
{\
UWORD value=(UWORD)mY-CPU_PEEK(mOperand);\
SET_NZ(value);\
mC=!(value&0x0100);\
}
*/
#define xCMP()\
{\
int value=CPU_PEEK(mOperand);\
mC=0;\
if (mA >= value) mC=1;\
SET_NZ((UBYTE)(mA - value))\
}
#define xCPX()\
{\
int value=CPU_PEEK(mOperand);\
mC=0;\
if (mX >= value) mC=1;\
SET_NZ((UBYTE)(mX - value))\
}
#define xCPY()\
{\
int value=CPU_PEEK(mOperand);\
mC=0;\
if (mY >= value) mC=1;\
SET_NZ((UBYTE)(mY - value))\
}
#define xDEC()\
{\
int value=CPU_PEEK(mOperand)-1;\
value&=0xff;\
CPU_POKE(mOperand,value);\
SET_NZ(value);\
}
#define xDECA()\
{\
mA--;\
mA&=0xff;\
SET_NZ(mA);\
}
#define xDEX()\
{\
mX--;\
mX&=0xff;\
SET_NZ(mX);\
}
#define xDEY()\
{\
mY--;\
mY&=0xff;\
SET_NZ(mY);\
}
#define xEOR()\
{\
mA^=CPU_PEEK(mOperand);\
SET_NZ(mA);\
}
#define xINC()\
{\
int value=CPU_PEEK(mOperand)+1;\
value&=0xff;\
CPU_POKE(mOperand,value);\
SET_NZ(value);\
}
#define xINCA()\
{\
mA++;\
mA&=0xff;\
SET_NZ(mA);\
}
#define xINX()\
{\
mX++;\
mX&=0xff;\
SET_NZ(mX);\
}
#define xINY()\
{\
mY++;\
mY&=0xff;\
SET_NZ(mY);\
}
#define xJMP()\
{\
mPC=mOperand;\
}
#define xJSR()\
{\
PUSH((mPC-1)>>8);\
PUSH((mPC-1)&0xff);\
mPC=mOperand;\
}
#define xLDA()\
{\
mA=CPU_PEEK(mOperand);\
SET_NZ(mA);\
}
#define xLDX()\
{\
mX=CPU_PEEK(mOperand);\
SET_NZ(mX);\
}
#define xLDY()\
{\
mY=CPU_PEEK(mOperand);\
SET_NZ(mY);\
}
#define xLSR()\
{\
int value=CPU_PEEK(mOperand);\
mC=value&0x01;\
value=(value>>1)&0x7f;\
CPU_POKE(mOperand,value);\
SET_NZ(value);\
}
#define xLSRA()\
{\
mC=mA&0x01;\
mA=(mA>>1)&0x7f;\
SET_NZ(mA);\
}
#define xNOP()\
{\
}
#define xORA()\
{\
mA|=CPU_PEEK(mOperand);\
SET_NZ(mA);\
}
#define xPHA()\
{\
PUSH(mA);\
}
#define xPHP()\
{\
PUSH(PS());\
}
#define xPHX()\
{\
PUSH(mX);\
}
#define xPHY()\
{\
PUSH(mY);\
}
#define xPLA()\
{\
PULL(mA);\
SET_NZ(mA);\
}
#define xPLP()\
{\
int P;\
PULL(P);\
PS(P);\
}
#define xPLX()\
{\
PULL(mX);\
SET_NZ(mX);\
}
#define xPLY()\
{\
PULL(mY);\
SET_NZ(mY);\
}
#define xROL()\
{\
int value=CPU_PEEK(mOperand);\
int oldC=mC;\
mC=value&0x80;\
value=(value<<1)|(oldC?1:0);\
value&=0xff;\
CPU_POKE(mOperand,value);\
SET_NZ(value);\
}
#define xROLA()\
{\
int oldC=mC;\
mC=mA&0x80;\
mA=(mA<<1)|(oldC?1:0);\
mA&=0xff;\
SET_NZ(mA);\
}
#define xROR()\
{\
int value=CPU_PEEK(mOperand);\
int oldC=mC;\
mC=value&0x01;\
value=((value>>1)&0x7f)|(oldC?0x80:0x00);\
value&=0xff;\
CPU_POKE(mOperand,value);\
SET_NZ(value);\
}
#define xRORA()\
{\
int oldC=mC;\
mC=mA&0x01;\
mA=((mA>>1)&0x7f)|(oldC?0x80:0x00);\
mA&=0xff;\
SET_NZ(mA);\
}
#define xRTI()\
{\
int tmp;\
PULL(tmp);\
PS(tmp);\
PULL(mPC);\
PULL(tmp);\
mPC|=tmp<<8;\
}
#define xRTS()\
{\
int tmp;\
PULL(mPC);\
PULL(tmp);\
mPC|=tmp<<8;\
mPC++;\
}
/*
#define xSBC()\
{\
UBYTE oldA=mA;\
if(!mD)\
{\
UBYTE value=~(CPU_PEEK(mOperand));\
SWORD difference=(SWORD)((SBYTE)mA)+(SWORD)((SBYTE)value)+(mC?1:0);\
mV=((difference>127)||(difference<-128));\
difference=((SWORD)mA)+((SWORD)value)+ (mC?1:0);\
mA=(UBYTE)difference;\
mC=(difference>0xff);\
SET_NZ(mA);\
}\
else\
{\
UBYTE value=CPU_PEEK(mOperand);\
SWORD difference=mBCDTable[0][mA]-mBCDTable[0][value]-(mC?0:1);\
if(difference<0) difference+=100;\
mA=mBCDTable[1][difference];\
mC=(oldA>=(value+(mC?0:1)));\
mV=((oldA^mA)&0x80)&&((mA^value)&0x80);\
SET_NZ(mA);\
}\
}
*/
#define xSBC()\
{\
int value=CPU_PEEK(mOperand);\
if (mD)\
{\
int c = mC?0:1;\
int sum = mA - value - c;\
int lo = (mA & 0x0f) - (value & 0x0f) - c;\
int hi = (mA & 0xf0) - (value & 0xf0);\
mV=0;\
mC=0;\
if ((mA^value) & (mA^sum) & 0x80) mV=1;\
if (lo & 0xf0) lo -= 6;\
if (lo & 0x80) hi -= 0x10;\
if (hi & 0x0f00) hi -= 0x60;\
if ((sum & 0xff00) == 0) mC=1;\
mA = (lo & 0x0f) + (hi & 0xf0);\
}\
else\
{\
int c = mC?0:1;\
int sum = mA - value - c;\
mV=0;\
mC=0;\
if ((mA^value) & (mA^sum) & 0x80) mV=1;\
if ((sum & 0xff00) == 0) mC=1;\
mA = (UBYTE) sum;\
}\
SET_NZ(mA)\
}
#define xSEC()\
{\
mC=true;\
}
#define xSED()\
{\
mD=true;\
}
#define xSEI()\
{\
mI=true;\
}
#define xSTA()\
{\
CPU_POKE(mOperand,mA);\
}
#define xSTP()\
{\
gSystemCPUSleep=TRUE;\
}
#define xSTX()\
{\
CPU_POKE(mOperand,mX);\
}
#define xSTY()\
{\
CPU_POKE(mOperand,mY);\
}
#define xSTZ()\
{\
CPU_POKE(mOperand,0);\
}
#define xTAX()\
{\
mX=mA;\
SET_NZ(mX);\
}
#define xTAY()\
{\
mY=mA;\
SET_NZ(mY);\
}
#define xTRB()\
{\
int value=CPU_PEEK(mOperand);\
SET_Z(mA&value);\
value=value&(mA^0xff);\
CPU_POKE(mOperand,value);\
}
//
// THE COMMENTED OUT CODE IS DERIVED FROM THE MAME 65C02 MODEL AND
// LOOKS TO BE INCORRECT i.e When plugged into Handy things stop working
//
/*
#define xTRB()\
{\
int value=CPU_PEEK(mOperand);\
value &= ~mA;\
SET_NZ(value);\
CPU_POKE(mOperand,value);\
}
*/
#define xTSB()\
{\
int value=CPU_PEEK(mOperand);\
SET_Z(mA&value);\
value=value|mA;\
CPU_POKE(mOperand,value);\
}
//
// THE COMMENTED OUT CODE IS DERIVED FROM THE MAME 65C02 MODEL AND
// LOOKS TO BE INCORRECT i.e When plugged into Handy things stop working
//
/*
#define xTSB()\
{\
int value=CPU_PEEK(mOperand);\
value |= mA;\
SET_NZ(value);\
CPU_POKE(mOperand,value);\
}
*/
#define xTSX()\
{\
mX=mSP;\
SET_NZ(mX);\
}
#define xTXA()\
{\
mA=mX;\
SET_NZ(mA);\
}
#define xTXS()\
{\
mSP=mX;\
}
#define xTYA()\
{\
mA=mY;\
SET_NZ(mA);\
}
#define xWAI()\
{\
gSystemCPUSleep=TRUE;\
}

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,389 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// Lynx Cartridge Class //
//////////////////////////////////////////////////////////////////////////////
// //
// This class emulates the Lynx cartridge interface, given a filename it //
// will contstruct a cartridge object via the constructor. //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
//#define TRACE_CART
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "system.h"
#include "cart.h"
#define CART_INC_COUNTER() {if(!mStrobe) mCounter = (mCounter + 1) & 0x07ff;}
CCart::CCart(UBYTE *gamedata,ULONG gamesize)
{
TRACE_CART1("CCart() called with %s",gamefile);
mWriteEnableBank1=FALSE;
mCartRAM=FALSE;
mCRC32=0;
mBank=bank0;
mFileHeader = (LYNX_HEADER){
.magic = {0, 0, 0, 0},
.page_size_bank0 = UWORD(gamesize >> 8),
.page_size_bank1 = 0,
.version = 0,
.cartname = {'N','O',' ','H','E','A','D','E','R'},
.manufname = {'H','A','N','D','Y'},
.rotation = 0,
.aud_bits = 0,
.eeprom = 0,
.spare = {0, 0, 0},
};
// Open up the file
if (gamedata && gamesize > sizeof(LYNX_HEADER)) {
if (memcmp(gamedata, "LYNX", 4) == 0) {
memcpy(&mFileHeader, gamedata, sizeof(LYNX_HEADER));
#ifdef MSB_FIRST
mFileHeader.page_size_bank0 = ((mFileHeader.page_size_bank0>>8) | (mFileHeader.page_size_bank0<<8));
mFileHeader.page_size_bank1 = ((mFileHeader.page_size_bank1>>8) | (mFileHeader.page_size_bank1<<8));
mFileHeader.version = ((mFileHeader.version>>8) | (mFileHeader.version<<8));
#endif
gamedata += sizeof(LYNX_HEADER);
gamesize -= sizeof(LYNX_HEADER);
} else {
log_printf("No header found: %02X %02X %02X %02X.\n", gamedata[0], gamedata[1], gamedata[2], gamedata[3]);
}
mCRC32 = crc32_le(0, gamedata, gamesize);
gCPUBootAddress = 0;
}
switch(mFileHeader.page_size_bank0) {
case 0x000:
mMaskBank0=0;
mShiftCount0=0;
mCountMask0=0;
break;
case 0x100:
mMaskBank0=0x00ffff;
mShiftCount0=8;
mCountMask0=0x0ff;
break;
case 0x200:
mMaskBank0=0x01ffff;
mShiftCount0=9;
mCountMask0=0x1ff;
break;
case 0x400:
mMaskBank0=0x03ffff;
mShiftCount0=10;
mCountMask0=0x3ff;
break;
case 0x800:
mMaskBank0=0x07ffff;
mShiftCount0=11;
mCountMask0=0x7ff;
break;
default:
log_printf("Invalid bank0 size (0x%03X).\n", mFileHeader.page_size_bank0);
break;
}
TRACE_CART1("CCart() - Bank0 = $%06x",mMaskBank0);
switch(mFileHeader.page_size_bank1) {
case 0x000:
// Dont allow an empty Bank1 - Use it for shadow SRAM/EEPROM
mMaskBank1=0x00ffff;
mShiftCount1=8;
mCountMask1=0x0ff;
mWriteEnableBank1=TRUE;
mCartRAM=TRUE;
break;
case 0x100:
mMaskBank1=0x00ffff;
mShiftCount1=8;
mCountMask1=0x0ff;
break;
case 0x200:
mMaskBank1=0x01ffff;
mShiftCount1=9;
mCountMask1=0x1ff;
break;
case 0x400:
mMaskBank1=0x03ffff;
mShiftCount1=10;
mCountMask1=0x3ff;
break;
case 0x800:
mMaskBank1=0x07ffff;
mShiftCount1=11;
mCountMask1=0x7ff;
break;
default:
log_printf("Invalid bank1 size (0x%03X).\n", mFileHeader.page_size_bank1);
break;
}
TRACE_CART1("CCart() - Bank1 = $%06x",mMaskBank1);
// Make some space for the new carts
mCartBank0 = (UBYTE*) new UBYTE[mMaskBank0+1];
mCartBank1 = (UBYTE*) new UBYTE[mMaskBank1+1];
mCartBank0A = NULL;
mCartBank1A = NULL;
memset(mCartBank0, DEFAULT_CART_CONTENTS, mMaskBank0+1);
memset(mCartBank1, DEFAULT_CART_CONTENTS, mMaskBank1+1);
// Stop here if running homebrew from RAM
if (!gamedata)
return;
log_printf("Cart name='%s', crc32=%08X, bank0=%d, bank1=%d\n",
mFileHeader.cartname, mCRC32, mMaskBank0+1, mMaskBank1+1);
// TODO: the following code to read the banks is not very nice .. should be reworked
// TODO: actually its dangerous, if more than one bank is used ... (only homebrews)
int cartsize = gamesize;
int bank0size = __min(cartsize, (int)(mMaskBank0+1));
int bank1size = __min(cartsize, (int)(mMaskBank1+1));
if(bank0size==1) bank0size=0;// workaround ...
if(bank1size==1) bank1size=0;// workaround ...
memcpy(mCartBank0, gamedata, bank0size);
cartsize = __max(0, cartsize - bank0size);
memcpy(mCartBank1, gamedata+bank0size, __min(cartsize, bank1size));
cartsize = __max(0, cartsize - bank1size);
if (CartGetAudin()){// TODO clean up code
mCartBank0A = (UBYTE*) new UBYTE[mMaskBank0+1];
mCartBank1A = (UBYTE*) new UBYTE[mMaskBank1+1];
memset(mCartBank0A, DEFAULT_CART_CONTENTS, mMaskBank0+1);
memset(mCartBank1A, DEFAULT_CART_CONTENTS, mMaskBank1+1);
memcpy(mCartBank0A, gamedata+(bank0size+bank1size), __min(cartsize, bank0size));
cartsize = __max(0, cartsize - bank0size);
memcpy(mCartBank1A, gamedata+(bank0size+bank1size+bank0size), __min(cartsize, bank1size));
cartsize = __max(0, cartsize - bank1size);
}
}
CCart::~CCart()
{
TRACE_CART0("~CCart()");
if (mCartBank0) delete[] mCartBank0;
if (mCartBank1) delete[] mCartBank1;
if (mCartBank0A) delete[] mCartBank0A;
if (mCartBank1A) delete[] mCartBank1A;
}
void CCart::Reset(void)
{
TRACE_CART0("Reset()");
mCounter=0;
mShifter=0;
mAddrData=0;
mStrobe=0;
}
bool CCart::ContextSave(LSS_FILE *fp)
{
TRACE_CART0("ContextSave()");
if(!lss_printf(fp,"CCart::ContextSave")) return 0;
if(!lss_write(&mCounter,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mShifter,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mAddrData,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mStrobe,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mShiftCount0,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mCountMask0,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mShiftCount1,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mCountMask1,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mBank,sizeof(EMMODE),1,fp)) return 0;
if(!lss_write(&mDummy,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mWriteEnableBank1,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&mCartRAM,sizeof(ULONG),1,fp)) return 0;
if(mCartRAM) {
if(!lss_write(&mMaskBank1,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(mCartBank1,sizeof(UBYTE),mMaskBank1+1,fp)) return 0;
}
return 1;
}
bool CCart::ContextLoad(LSS_FILE *fp)
{
TRACE_CART0("ContextLoad()");
char teststr[32]="XXXXXXXXXXXXXXXXXX";
if(!lss_read(teststr,sizeof(char),18,fp)) return 0;
if(strcmp(teststr,"CCart::ContextSave")!=0) return 0;
if(!lss_read(&mCounter,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mShifter,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mAddrData,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mStrobe,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mShiftCount0,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mCountMask0,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mShiftCount1,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mCountMask1,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mBank,sizeof(EMMODE),1,fp)) return 0;
if(!lss_read(&mDummy,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mWriteEnableBank1,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&mCartRAM,sizeof(ULONG),1,fp)) return 0;
if(mCartRAM) {
if(!lss_read(&mMaskBank1,sizeof(ULONG),1,fp)) return 0;
delete[] mCartBank1;
mCartBank1 = new UBYTE[mMaskBank1+1];
if(!lss_read(mCartBank1,sizeof(UBYTE),mMaskBank1+1,fp)) return 0;
}
return 1;
}
void CCart::CartAddressStrobe(bool strobe)
{
static int last_strobe=0;
mStrobe=strobe;
if(mStrobe) mCounter=0;
//
// Either of the two below seem to work OK.
//
// if(!strobe && last_strobe)
//
if(mStrobe && !last_strobe) {
// Clock a bit into the shifter
mShifter=mShifter<<1;
mShifter+=mAddrData?1:0;
mShifter&=0xff;
}
last_strobe=mStrobe;
TRACE_CART2("CartAddressStrobe(strobe=%d) mShifter=$%06x",strobe,mShifter);
}
void CCart::CartAddressData(bool data)
{
TRACE_CART1("CartAddressData($%02x)",data);
mAddrData=data;
}
inline void CCart::Poke(ULONG addr, UBYTE data)
{
if(mBank==bank1 && mWriteEnableBank1) {
mCartBank1[addr&mMaskBank1]=data;
}
}
inline UBYTE CCart::Peek(ULONG addr)
{
if(mBank==bank0) {
return(mCartBank0[addr&mMaskBank0]);
} else {
return(mCartBank1[addr&mMaskBank1]);
}
}
void CCart::Poke0(UBYTE data)
{
CART_INC_COUNTER();
}
void CCart::Poke0A(UBYTE data)
{
CART_INC_COUNTER();
}
void CCart::Poke1(UBYTE data)
{
if(mWriteEnableBank1) {
ULONG address=(mShifter<<mShiftCount1)+(mCounter&mCountMask1);
mCartBank1[address&mMaskBank1]=data;
}
CART_INC_COUNTER();
}
void CCart::Poke1A(UBYTE data)
{
if(mWriteEnableBank1 && mCartBank1A) {
ULONG address=(mShifter<<mShiftCount1)+(mCounter&mCountMask1);
mCartBank1A[address&mMaskBank1]=data;
}
CART_INC_COUNTER();
}
UBYTE CCart::Peek0(void)
{
ULONG address=(mShifter<<mShiftCount0)+(mCounter&mCountMask0);
UBYTE data=mCartBank0[address&mMaskBank0];
CART_INC_COUNTER();
return data;
}
UBYTE CCart::Peek0A(void)
{
ULONG address=(mShifter<<mShiftCount0)+(mCounter&mCountMask0);
UBYTE data=mCartBank0A?mCartBank0A[address&mMaskBank0]:0xFF;
CART_INC_COUNTER();
return data;
}
UBYTE CCart::Peek1(void)
{
ULONG address=(mShifter<<mShiftCount1)+(mCounter&mCountMask1);
UBYTE data=mCartBank1[address&mMaskBank1];
CART_INC_COUNTER();
return data;
}
UBYTE CCart::Peek1A(void)
{
ULONG address=(mShifter<<mShiftCount1)+(mCounter&mCountMask1);
UBYTE data=mCartBank1A?mCartBank1A[address&mMaskBank1]:0xFF;
CART_INC_COUNTER();
return data;
}

Wyświetl plik

@ -0,0 +1,184 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// Lynx cartridge class header file //
//////////////////////////////////////////////////////////////////////////////
// //
// This header file provides the interface definition and code for some of //
// the simpler cartridge API. //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef CART_H
#define CART_H
#define EPYX_HEADER_OLD 512
#define EPYX_HEADER_NEW 410
#ifndef __max
#define __max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
#endif
#ifndef __min
#define __min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _b : _a; })
#endif
#ifdef TRACE_CART
#define TRACE_CART0(msg) _RPT1(_CRT_WARN,"CCart::"msg" (Time=%012d)\n",gSystemCycleCount)
#define TRACE_CART1(msg,arg1) _RPT2(_CRT_WARN,"CCart::"msg" (Time=%012d)\n",arg1,gSystemCycleCount)
#define TRACE_CART2(msg,arg1,arg2) _RPT3(_CRT_WARN,"CCart::"msg" (Time=%012d)\n",arg1,arg2,gSystemCycleCount)
#define TRACE_CART3(msg,arg1,arg2,arg3) _RPT4(_CRT_WARN,"CCart::"msg" (Time=%012d)\n",arg1,arg2,arg3,gSystemCycleCount)
#else
#define TRACE_CART0(msg)
#define TRACE_CART1(msg,arg1)
#define TRACE_CART2(msg,arg1,arg2)
#define TRACE_CART3(msg,arg1,arg2,arg3)
#endif
#define DEFAULT_CART_CONTENTS 0xFF
enum CTYPE {UNUSED,C64K,C128K,C256K,C512K,C1024K};
#define CART_NO_ROTATE 0
#define CART_ROTATE_LEFT 1
#define CART_ROTATE_RIGHT 2
typedef struct
{
UBYTE magic[4];
UWORD page_size_bank0;
UWORD page_size_bank1;
UWORD version;
UBYTE cartname[32];
UBYTE manufname[16];
UBYTE rotation;
UBYTE aud_bits;
UBYTE eeprom;
UBYTE spare[3];
}LYNX_HEADER;
class CCart : public CLynxBase
{
// Function members
public:
CCart(UBYTE *gamedata,ULONG gamesize);
~CCart();
public:
// Access for sensible members of the clan
void Reset(void);
bool ContextSave(LSS_FILE *fp);
bool ContextLoad(LSS_FILE *fp);
bool ContextLoadLegacy(LSS_FILE *fp);
void Poke(ULONG addr,UBYTE data);
UBYTE Peek(ULONG addr);
ULONG ReadCycle(void) {return 15;};
ULONG WriteCycle(void) {return 15;};
void BankSelect(EMMODE newbank) {mBank=newbank;}
ULONG ObjectSize(void) {return (mBank==bank0)?mMaskBank0+1:mMaskBank1+1;};
const char* CartGetName(void) { return (const char*)mFileHeader.cartname;};
const char* CartGetManufacturer(void) { return (const char*)mFileHeader.manufname; };
ULONG CartGetRotate(void) {return (mFileHeader.rotation > 2) ? CART_NO_ROTATE : mFileHeader.rotation;};
bool CartGetAudin(void) { return mFileHeader.aud_bits&0x01;};
UBYTE CartGetEEPROMType(void) { return mFileHeader.eeprom;};
ULONG CRC32(void) { return mCRC32; };
// Access for the lynx itself, it has no idea of address etc as this is done by the
// cartridge emulation hardware
void CartAddressStrobe(bool strobe);
void CartAddressData(bool data);
void Poke0(UBYTE data);
void Poke1(UBYTE data);
void Poke0A(UBYTE data);
void Poke1A(UBYTE data);
UBYTE Peek0(void);
UBYTE Peek1(void);
UBYTE Peek0A(void);
UBYTE Peek1A(void);
void SetShifterValue(UBYTE a){mShifter=a; mCounter=0;}; // for fake bios
inline ULONG GetCounterValue(void) {return mCounter;}; // for eeprom
// Data members
public:
ULONG mWriteEnableBank1;
private:
EMMODE mBank;
ULONG mCartRAM;
ULONG mMaskBank0;
ULONG mMaskBank1;
UBYTE *mCartBank0;
UBYTE *mCartBank1;
UBYTE *mCartBank0A;
UBYTE *mCartBank1A;
LYNX_HEADER mFileHeader;
ULONG mCRC32;
ULONG mCounter;
ULONG mShifter;
ULONG mAddrData;
ULONG mStrobe;
ULONG mShiftCount0;
ULONG mCountMask0;
ULONG mShiftCount1;
ULONG mCountMask1;
ULONG mDummy;
};
#endif

Wyświetl plik

@ -0,0 +1,316 @@
//////////////////////////////////////////////////////////////////////////////
// Lynx 3wire EEPROM Class //
//////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "system.h"
#include "eeprom.h"
CEEPROM::CEEPROM(UBYTE typ)
{
*filename=0;
memset(romdata, 0xff, sizeof(romdata));
Reset();
SetEEPROMType(type);
}
void CEEPROM::Reset(void)
{
busy_count=0;
state=EE_NONE;
readdata=0;
data=0;
addr=0;
sendbits=0;
readonly=true;
counter=0;
iodir=0;
iodat=0;
mAUDIN_ext=0;
}
CEEPROM::~CEEPROM()
{
}
void CEEPROM::Load(void)
{
if(!Available()) return;
FILE *fe;
if((fe=fopen(filename,"rb"))!=NULL){
log_printf("EEPROM: Loading from '%s'\n",filename);
fread(romdata,1,1024,fe);
fclose(fe);
}
}
void CEEPROM::Save(void)
{
if(!Available()) return;
FILE *fe;
if((fe=fopen(filename,"wb+"))!=NULL){
log_printf("EEPROM: Saving to '%s'\n",filename);
fwrite(romdata,1,Size(),fe);
fclose(fe);
}
}
bool CEEPROM::ContextSave(LSS_FILE *fp)
{
if(!lss_printf(fp,"CEEPROM::ContextSave")) return 0;
if(!lss_write(&busy_count,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&state,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&readdata,sizeof(UWORD),1,fp)) return 0;
if(!lss_write(&data,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&addr,sizeof(UWORD),1,fp)) return 0;
if(!lss_write(&sendbits,sizeof(ULONG),1,fp)) return 0;
if(!lss_write(&readonly,sizeof(UBYTE),1,fp)) return 0;
if(!lss_write(&counter,sizeof(UWORD),1,fp)) return 0;
if(!lss_write(&iodir,sizeof(UBYTE),1,fp)) return 0;
if(!lss_write(&iodat,sizeof(UBYTE),1,fp)) return 0;
if(!lss_write(&mAUDIN_ext,sizeof(UBYTE),1,fp)) return 0;
if(!lss_write(&romdata,sizeof(UWORD),1024,fp)) return 0;
return 1;
}
bool CEEPROM::ContextLoad(LSS_FILE *fp)
{
char teststr[32]="XXXXXXXXXXXXXXXXXXXX";
if(!lss_read(teststr,sizeof(char),20,fp)) return 0;
teststr[20]=0;
if(strcmp(teststr,"CEEPROM::ContextSave")!=0) return 0;
if(!lss_read(&busy_count,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&state,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&readdata,sizeof(UWORD),1,fp)) return 0;
if(!lss_read(&data,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&addr,sizeof(UWORD),1,fp)) return 0;
if(!lss_read(&sendbits,sizeof(ULONG),1,fp)) return 0;
if(!lss_read(&readonly,sizeof(UBYTE),1,fp)) return 0;
if(!lss_read(&counter,sizeof(UWORD),1,fp)) return 0;
if(!lss_read(&iodir,sizeof(UBYTE),1,fp)) return 0;
if(!lss_read(&iodat,sizeof(UBYTE),1,fp)) return 0;
if(!lss_read(&mAUDIN_ext,sizeof(UBYTE),1,fp)) return 0;
if(!lss_read(&romdata,sizeof(UWORD),1024,fp)) return 0;
return 1;
}
void CEEPROM::SetEEPROMType(UBYTE b)
{
type=b;
const char *type = "none";
switch(b&0x7) {
case 1: // 93C46 , 8 bit mode
ADDR_MASK = 0x7F;
CMD_BITS = 10;
ADDR_BITS = 7;
type = "93C46";
break;
case 2: // 93C56 , 8 bit mode
ADDR_MASK = 0xFF;
CMD_BITS = 12;
ADDR_BITS = 9;
type = "93C56";
break;
case 3: // 93C66 , 8 bit mode
ADDR_MASK = 0x1FF;
CMD_BITS = 12;
ADDR_BITS = 9;
type = "93C66";
break;
case 4: // 93C76 , 8 bit mode
ADDR_MASK = 0x3FF;
CMD_BITS = 14;
ADDR_BITS = 11;
type = "93C76";
break;
case 5: // 93C86 , 8 bit mode
ADDR_MASK = 0x7FF;
CMD_BITS = 14;
ADDR_BITS = 11;
type = "93C86";
break;
case 0: // NONE, fallthrou
default:
ADDR_MASK = 0;
CMD_BITS = 1;
ADDR_BITS = 1;
break;
}
if(b&0x80) { // 8 bit access
DONE_MASK = 0x100;
log_printf("EEPROM: Type: %s 8bit\n", type);
} else { // 16 bit access
ADDR_MASK>>=1;
CMD_BITS--;
ADDR_BITS--;
DONE_MASK = 0x10000;
log_printf("EEPROM: Type: %s 16bit\n", type);
}
}
int CEEPROM::Size(void)
{
int m=ADDR_MASK+1;
if(type&0x80) return m; else return m*2;
}
void CEEPROM::ProcessEepromBusy(void)
{
if(state==EE_BUSY || state==EE_NONE) {
if(busy_count<2) {
busy_count++;
readdata=0x0000;// RDY
mAUDIN_ext=0;
} else {
readdata=0xFFFF;// RDY
mAUDIN_ext=1;
state=EE_WAIT;
}
// printf("(%d)",busy_count);
}
}
void CEEPROM::ProcessEepromCounter(UWORD cnt)
{
// Update if either counter strobed or AUDIN changed
UpdateEeprom( cnt);
}
void CEEPROM::ProcessEepromIO(UBYTE iodir_loc,UBYTE iodat_loc)
{
// Update if either counter strobed or AUDIN changed
iodat=iodat_loc;
iodir=iodir_loc;
}
void CEEPROM::UpdateEeprom(UWORD cnt)
{
// Update if either counter strobed or AUDIN changed
bool CLKp, CLKn;
CLKp=counter&0x02;
counter=cnt;
CLKn=counter&0x02;
if( CLKp!=CLKn && CLKn) { // Rising edge
bool CS, DI;
mAUDIN_ext=(readdata&(DONE_MASK>>1)) ? 1 : 0 ;
readdata<<=1;
CS=cnt&0x80;
DI=false;
if(iodir&0x10) {
DI=iodat&0x10;
}
if(!CS) state=EE_NONE;
switch(state) {
case EE_NONE:
data=0;
if( CS) {
if(DI && (iodir&0x10)) {
//if( state!=EE_START) printf("EE Start...\n");
mAUDIN_ext=0;
state=EE_START;
data=0;
sendbits=CMD_BITS-1;
} else if(!(iodir&0x10)) {
state=EE_BUSY;
//printf("BUSY\n");
readdata=0x0000;// RDY
mAUDIN_ext=0;
busy_count=0;
}
}
break;
case EE_START:
data<<=1;
if(DI) data++;
sendbits--;
if( sendbits>0) break;
state=EE_NONE;
// if(data!=(0xFFFF&((ADDR_MASK<<2)|0x3))) printf("EE Byte $%02X .. ",(int)data);
addr=data&ADDR_MASK;
switch(data>>ADDR_BITS) {
case 0x3:
if(!readonly) {
// printf("ERASE ADD $%02X RO %d\n",(int)addr,readonly);
romdata[addr]=0xFFFF;
}
break;
case 0x2:
if(type&0x80) readdata=((unsigned char *)romdata)[addr]; else readdata=romdata[addr];
mAUDIN_ext=0;
// printf("Read ADD $%02X $%04X\n",(int)addr,readdata);
state=EE_WAIT;
break;
case 0x1:
// printf("Write ADD $%02X RO %d\n",(int)addr,readonly);
data=0x1;
state=EE_DATA;
break;
case 0x00:
if((data>>(ADDR_BITS-2))==0x0) {
// printf("EWDS\n");
readonly=true;
break;
};
if((data>>(ADDR_BITS-2))==0x3) {
// printf("EWEN\n");
readonly=false;
break;
};
if((data>>(ADDR_BITS-2))==0x1) {
// printf("WRAL\n");
break;
};
if((data>>(ADDR_BITS-2))==0x2) {
// printf("ERAL\n");
break;
};
// falltrhou
default:
// printf("Unknown $%03X\n",(int)data);
break;
}
break;
case EE_DATA:
data<<=1;
if(DI) data++;
if(data&DONE_MASK) {
state=EE_NONE;
// printf("EE Written Data $%04X ",(unsigned int)data&0xFFFF);
if(readonly) {
// printf("WRITE PROT!\n");
} else {
if(type &0x80){
((unsigned char *)romdata)[addr]=(data&0xFF);
} else {
romdata[addr]=(data&0xFFFF);
}
// printf("done\n");
}
busy_count=0;
readdata=0x0000;// RDY
mAUDIN_ext=0;
state=EE_WAIT;
}
break;
case EE_WAIT:
// printf(".%d.",mAUDIN_ext);
break;
}
}
}

Wyświetl plik

@ -0,0 +1,82 @@
//////////////////////////////////////////////////////////////////////////////
// Lynx 3wire EEPROM class header file //
//////////////////////////////////////////////////////////////////////////////
#ifndef EEPROM_H
#define EEPROM_H
#ifndef __min
#define __min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _b : _a; })
#endif
enum {EE_NONE=0, EE_START, EE_DATA, EE_BUSY, EE_WAIT};
class CEEPROM : public CLynxBase
{
// Function members
public:
CEEPROM(UBYTE type);
~CEEPROM();
bool ContextSave(LSS_FILE *fp);
bool ContextLoad(LSS_FILE *fp);
void Reset(void);
bool Available(void){ return type!=0;};
void ProcessEepromIO(UBYTE iodir,UBYTE iodat);
void ProcessEepromCounter(UWORD cnt);
void ProcessEepromBusy(void);
bool OutputBit(void)
{
return mAUDIN_ext;
};
void SetEEPROMType(UBYTE b);
int Size(void);
void InitFrom(char *data,int count){ memcpy(romdata,data,__min(count,Size()));};
void Poke(ULONG addr,UBYTE data) { };
UBYTE Peek(ULONG addr)
{
return(0);
};
void SetFilename(char* f){strcpy(filename,f);};
char* GetFilename(void){ return filename;};
void Load(void);
void Save(void);
private:
char filename[256];
void UpdateEeprom(UWORD cnt);
UBYTE type; // 0 ... no eeprom
UWORD ADDR_MASK;
UBYTE CMD_BITS;
UBYTE ADDR_BITS;
ULONG DONE_MASK;
UBYTE iodir, iodat;
UWORD counter;
int busy_count;
int state;
UWORD readdata;
ULONG data;
UWORD romdata[1024];// 128, 256, 512, 1024 WORDS bzw 128 bytes fuer byte zugriff
UWORD addr;
int sendbits;
bool readonly;
bool mAUDIN_ext;// OUTPUT
public:
};
#endif

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,219 @@
#ifndef EMUAPI_H
#define EMUAPI_H
#include "platform_config.h"
#define CUSTOM_SND 1
//#define TIMER_REND 1
#define EXTPAD 1
#define EXTRA_HEAP 0x10
// Title: < >
#define TITLE " LYNX Emulator "
#define ROMSDIR "lynx"
#define emu_Init(ROM) { lnx_Init(); lnx_Start(ROM);}
#define emu_Step(x) { lnx_Step(); }
#define emu_Input(x) { lnx_Input(x); }
#define MAX_FILENAME_PATH 64
#define NB_FILE_HANDLER 4
#define PALETTE_SIZE 256
#define VID_FRAME_SKIP 0x0
#define TFT_VBUFFER_YCROP 0
#define SINGLELINE_RENDERING 1
#define R32(rgb) ((rgb>>16)&0xff)
#define G32(rgb) ((rgb>>8)&0xff)
#define B32(rgb) (rgb & 0xff)
#define ACTION_NONE 0
#define ACTION_MAXKBDVAL 16
#define ACTION_EXITKBD 128
#define ACTION_RUN1 129
#define ACTION_RUN2 130
#define ACTION_RUN3 131
#ifdef KEYMAP_PRESENT
#define keylables_map0_0 (char *)"QWERTYUIOP\x1a"
#define keylables_map0_1 (char *)" ASDFGHJKL\x19"
#define keylables_map0_2 (char *)" ZXCVBNM,.;/"
#define keylables_map0_3 (char *)" +\x10-"
const unsigned short key_map0[] = {
'q','w','E','R','T','Y','U','I','O','P',157, //default C64 uppercase always
0,'A','S','D','F','G','H','J','K','L',10,
0,'Z','X','C','V','B','N','M',',','.',';','/',
0,0,0,0,
0,'+',' ','-'
};
#define keylables_map1_0 (char *)"1234567890 "
#define keylables_map1_1 (char *)" "
#define keylables_map1_2 (char *)" "
#define keylables_map1_3 (char *)" "
const unsigned short key_map1[] = {
'1','2','3','4','5','6','7','8','9','0',0, // digit keys
0, 0,0,0,0,0,0,0,0,0,0,
0, 0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,
0,0,' ',0
};
#define keylables_map2_0 (char *)"!\"#$%^&*()@"
#define keylables_map2_1 (char *)" "
#define keylables_map2_2 (char *)" <>:?"
#define keylables_map2_3 (char *)" =\x10_"
const unsigned short key_map2[] = {
'!','"','#','$','%','^','&','*','(',')','@', // shiftothers
0, 0,0,0,0,0,0,0,0,0,0,
0, 0,0,0,0,0,0,0,'<','>',':','?',
153,151,150,152, //U L R D
0,'=',' ','_'
};
#define keylables_map3_0 (char *)"\x11\x12\x13\x14\x15\x16\x17\x18 "
#define keylables_map3_1 (char *)" "
#define keylables_map3_2 (char *)" "
#define keylables_map3_3 (char *)" "
const unsigned short key_map3[] = {
129,130,131,132,133,134,135,136,0,0,0, // function keys
0, 0,0,0,0,0,0,0,0,0,0,
0, 0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,
0,0,' ',0
};
#define keylables_map4_0 (char *)"QWERTYUIOP@"
#define keylables_map4_1 (char *)" ASDFGHJKL\x19"
#define keylables_map4_2 (char *)" ZXCVBNM<>:?"
#define keylables_map4_3 (char *)" =\x10_"
const unsigned short key_map4[] = {
'Q','W','E','R','T','Y','U','I','O','P','@', //shift uppercase
0,'A','S','D','F','G','H','J','K','L',10,
0,'Z','X','C','V','B','N','M','<','>',':','?',
0,0,0,0,
0,'=',' ','_'
};
#define keylables_map5_0 (char *)" "
#define keylables_map5_1 (char *)" "
#define keylables_map5_2 (char *)" "
#define keylables_map5_3 (char *)" "
const unsigned short key_map5[] = {
0,0,0,0,0,0,0,0,0,0,0, // extra keys
0, 0,0,0,0,0,0,0,0,0,0,
0, 0,0,0,0,0,0,0,0,0,0,0,
153,151,150,152, //U L R D
0,0,' ',0
};
const unsigned short matkeys[] = {
0x004,0x008,0x108,0x104,0x208,0x204,0x308,0x304,0x408,0x404,0x410, // row 1
0x502,0x002,0x020,0x102,0x120,0x202,0x220,0x302,0x320,0x402,0x420, // row 2
0x508,0x001,0x040,0x101,0x140,0x201,0x240,0x210,0x340,0x301,0x401,0x440, // row 3
0x504,0x520,0x540,0x501, // UP LEFT RIGHT DOWN
0x510,0x010,0x110,0x310, // row 4
};
#endif
#define MASK_JOY2_RIGHT 0x0001
#define MASK_JOY2_LEFT 0x0002
#define MASK_JOY2_UP 0x0004
#define MASK_JOY2_DOWN 0x0008
#define MASK_JOY2_BTN 0x0010
#define MASK_KEY_USER1 0x0020
#define MASK_KEY_USER2 0x0040
#define MASK_KEY_USER3 0x0080
#define MASK_JOY1_RIGHT 0x0100
#define MASK_JOY1_LEFT 0x0200
#define MASK_JOY1_UP 0x0400
#define MASK_JOY1_DOWN 0x0800
#define MASK_JOY1_BTN 0x1000
#define MASK_KEY_USER4 0x2000
#ifdef __cplusplus
extern "C" {
#endif
extern void emu_init(void);
extern void emu_start(void);
extern void emu_printf(const char * text);
extern void emu_printi(int val);
extern void emu_printh(int val);
extern void * emu_Malloc(unsigned int size);
extern void * emu_MallocI(unsigned int size);
extern void emu_Free(void * pt);
extern void * emu_SMalloc(unsigned int size);
extern void emu_SFree(void * pt);
extern int emu_FileOpen(const char * filepath, const char * mode);
extern int emu_FileRead(void * buf, int size, int handler);
extern int emu_FileGetc(int handler);
extern int emu_FileSeek(int handler, int seek, int origin);
extern int emu_FileTell(int handler);
extern void emu_FileClose(int handler);
extern unsigned int emu_FileSize(const char * filepath);
extern unsigned int emu_LoadFile(const char * filepath, void * buf, int size);
extern unsigned int emu_LoadFileSeek(const char * filepath, void * buf, int size, int seek);
extern void emu_SetPaletteEntry(unsigned char r, unsigned char g, unsigned char b, int index);
extern void emu_DrawScreen(unsigned char * VBuf, int width, int height, int stride);
extern void emu_DrawLine(unsigned char * VBuf, int width, int height, int line);
extern void emu_DrawLine16(unsigned short * VBuf, int width, int height, int line);
extern void emu_DrawLine8(unsigned char * VBuf, int width, int height, int line);
extern void emu_CopyLine(int width, int height, int ysrc, int ydst);
extern void emu_DrawVsync(void);
extern int emu_FrameSkip(void);
extern void * emu_LineBuffer(int line);
extern int emu_LineStride(void);
extern int emu_LineWidth(void);
extern void emu_tweakVideo(int shiftdelta, int numdelta, int denomdelta);
extern bool menuActive(void);
extern char * menuSelection(void);
extern char * menuSecondSelection(void);
extern void toggleMenu(bool on);
extern int handleMenu(unsigned short bClick);
extern int handleOSKB(void);
extern void toggleOSKB(bool forceon);
extern void emu_InitJoysticks(void);
extern int emu_SwapJoysticks(int statusOnly);
extern unsigned short emu_DebounceLocalKeys(void);
extern int emu_ReadKeys(void);
extern int emu_GetPad(void);
extern int emu_GetMouse(int *x, int *y, int *buts);
extern int emu_MouseDetected(void);
extern int emu_KeyboardDetected(void);
extern int emu_ReadAnalogJoyX(int min, int max);
extern int emu_ReadAnalogJoyY(int min, int max);
extern int emu_ReadI2CKeyboard(void);
extern unsigned char emu_ReadI2CKeyboard2(int row);
extern void emu_KeyboardOnUp(int keymodifer, int key);
extern void emu_KeyboardOnDown(int keymodifer, int key);
extern void emu_MidiOnDataReceived(unsigned char data);
extern void emu_sndPlaySound(int chan, int volume, int freq);
extern void emu_sndPlayBuzz(int size, int val);
extern void emu_sndInit();
extern void emu_resetus(void);
extern int emu_us(void);
extern int emu_setKeymap(int index);
#ifdef __cplusplus
}
#endif
#endif

Wyświetl plik

@ -0,0 +1,148 @@
// Font: c64_lower.64c
PROGMEM const unsigned char font8x8[128][8] =
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F
{ 0x7f, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7f, 0x00 }, // Space // 0x10
{ 0x00, 0x27, 0x31, 0x27, 0x21, 0x71, 0x00, 0x00 }, // F1 // 0x11
{ 0x00, 0x77, 0x41, 0x77, 0x11, 0x71, 0x00, 0x00 }, // F2
{ 0x00, 0x77, 0x41, 0x77, 0x41, 0x71, 0x00, 0x00 }, // F3
{ 0x00, 0x17, 0x51, 0x77, 0x41, 0x41, 0x00, 0x00 }, // F4
{ 0x00, 0x77, 0x11, 0x77, 0x41, 0x71, 0x00, 0x00 }, // F5
{ 0x00, 0x77, 0x11, 0x77, 0x51, 0x71, 0x00, 0x00 }, // F6
{ 0x00, 0x77, 0x41, 0x47, 0x41, 0x41, 0x00, 0x00 }, // F7
{ 0x00, 0x77, 0x51, 0x77, 0x51, 0x71, 0x00, 0x00 }, // F8 // 0x18
{ 0x00, 0x00, 0x20, 0x24, 0x3e, 0x04, 0x00, 0x00 }, // Return // 0x19
{ 0x00, 0x59, 0x4b, 0x5b, 0x4b, 0xd9, 0x00, 0x00 }, // Del // 0x1A
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space)
{ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!)
{ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (")
{ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#)
{ 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($)
{ 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%)
{ 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&)
{ 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (')
{ 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (()
{ 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ())
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*)
{ 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,)
{ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.)
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/)
{ 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0)
{ 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1)
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2)
{ 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3)
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4)
{ 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5)
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6)
{ 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7)
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8)
{ 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9)
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:)
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (//)
{ 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<)
{ 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=)
{ 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>)
{ 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?)
{ 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@)
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A)
{ 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B)
{ 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C)
{ 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D)
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E)
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F)
{ 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G)
{ 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H)
{ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I)
{ 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J)
{ 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K)
{ 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L)
{ 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M)
{ 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N)
{ 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O)
{ 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P)
{ 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q)
{ 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R)
{ 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S)
{ 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T)
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U)
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V)
{ 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W)
{ 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X)
{ 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y)
{ 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z)
{ 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([)
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\)
{ 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (])
{ 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_)
{ 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`)
{ 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a)
{ 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b)
{ 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c)
{ 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d)
{ 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e)
{ 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f)
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g)
{ 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h)
{ 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i)
{ 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j)
{ 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k)
{ 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l)
{ 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m)
{ 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n)
{ 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o)
{ 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p)
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q)
{ 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r)
{ 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s)
{ 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v)
{ 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w)
{ 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y)
{ 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z)
{ 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({)
{ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|)
{ 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (})
{ 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F
};

Wyświetl plik

@ -0,0 +1,5 @@
#undef INTSET
#undef PS
#include "system.h"
#include "lynxdef.h"

Wyświetl plik

@ -0,0 +1,124 @@
#ifndef IOPINS_H
#define IOPINS_H
#include "platform_config.h"
#ifdef TEECOMPUTER
// Teecomputer layout
// VGA
// R 3 2K
// R 4 1K
// R 33 500
// G 11 2K
// G 13 1K
// G 2 500
// B 10 820
// B 12 390
// HSYNC 15 82
// VSYNC 8 82
// Display
#define TFT_SCLK 27
#define TFT_MOSI 26
#define TFT_MISO 255
#define TFT_TOUCH_CS 255
#define TFT_TOUCH_INT 255
#define TFT_DC 23
#define TFT_CS 22 // 255 for LORES ST7789 (NO CS)
#define TFT_RST 255 // 255 for ILI/ST if connected to 3.3V or 24 if really needed
// SD
#define SD_CS BUILTIN_SDCARD
// Audio
#define AUDIO_I2S_DIN 7
#define AUDIO_I2S_BCK 21
#define AUDIO_I2S_LCK 20
// Keyboard matrix
#define KLED 14
//Cols (out)
//pico 1,2,3,4,5,14
//teen 16,6,24,25,28,31
#define KCOLOUT1 16
#define KCOLOUT2 6
#define KCOLOUT3 24
#define KCOLOUT4 25
#define KCOLOUT5 28
#define KCOLOUT6 31
//Rows (in)
//pico 9,8,6,15,7,22
//teen 19,18,17,5,29,30,32 //5,6,16,17,18,19
#define KROWIN1 19
#define KROWIN2 18
#define KROWIN3 17
#define KROWIN4 5
#define KROWIN5 29
#define KROWIN6 30
#define KROWIN7 32
#define PIN_KEY_USER1 41
#define PIN_KEY_USER2 40
// Second joystick (external)
#define PIN_JOY1_BTN 34
#define PIN_JOY1_1 35 // UP
#define PIN_JOY1_2 36 // DOWN
#define PIN_JOY1_3 38 // RIGHT
#define PIN_JOY1_4 37 // LEFT
#else
// Original Layout
#define TFT_SCLK 13
#define TFT_MOSI 11
#define TFT_MISO 12
#define TFT_TOUCH_CS 255
#define TFT_TOUCH_INT 255
#define TFT_DC 9
#define TFT_CS 22 // 255 for LORES ST7789 (NO CS)
#define TFT_RST 23 // 255 for ILI/ST if connected to 3.3V
// SD
#define SD_CS BUILTIN_SDCARD
// I2C keyboard
#define I2C_SCL_IO 19
#define I2C_SDA_IO 18
// Analog joystick (primary) for JOY2 and 5 extra buttons
#ifdef HAS_T4_VGA
#define PIN_JOY2_A1X A3
#define PIN_JOY2_A2Y A2
#define PIN_JOY2_BTN 14
#define PIN_KEY_USER1 22
#define PIN_KEY_USER2 23
// Second joystick
#define PIN_JOY1_BTN 34
#define PIN_JOY1_1 35 // UP
#define PIN_JOY1_2 36 // DOWN
#define PIN_JOY1_3 38 // RIGHT
#define PIN_JOY1_4 37 // LEFT
#else
#define PIN_JOY2_A1X A1
#define PIN_JOY2_A2Y A2
#define PIN_JOY2_BTN 17
#define PIN_KEY_USER1 3 //34
#define PIN_KEY_USER2 4 //35
// Second joystick
#define PIN_JOY1_BTN 2
#define PIN_JOY1_1 14 // UP
#define PIN_JOY1_2 7 // DOWN
#define PIN_JOY1_3 6 // RIGHT
#define PIN_JOY1_4 5 // LEFT
#endif
#endif
#endif

Wyświetl plik

@ -0,0 +1,306 @@
#include <stdio.h>
#include <string.h>
extern "C" {
#include "emuapi.h"
#include "platform_config.h"
}
#include <Arduino.h>
#include "handy.h"
// Emulation includes
static CSystem *lynx = NULL;
#define AUDIORPTRMASK (((AUDIO_BUFFER_LENGTH-1)<<8)+0xff)
static ULONG sndbuffer[AUDIO_BUFFER_LENGTH]; // (short)(L<<16)+(short)R
static long audioowptr = 0;
static long audioorptr = 0;
static long audiorptr = 0;
static long deltar = 0;
#define AVG_COUNT 16
static int avgcounter = AVG_COUNT;
static long avgr = 0;
static long avgw = 0;
static long inc = 0;
static long incdelta = 0;
static long sndinc = 0x100; // default read increment
// distance between read and write buffer
static long pdelta = 0x1000;
static bool pdown = true;
static int dpad_mapped_up;
static int dpad_mapped_down;
static int dpad_mapped_left;
static int dpad_mapped_right;
static int width, height;
#define DISPLAY_ROTATION_OFF 0
#define DISPLAY_ROTATION_LEFT 1
#define DISPLAY_ROTATION_RIGHT 2
static int ik; // joypad key
static int pik=0;
static int ihk; // I2C keyboard key
static int iusbhk;// USB keyboard key
void lnx_Input(int bClick) {
ik = emu_GetPad();
ihk = emu_ReadI2CKeyboard();
}
void emu_KeyboardOnDown(int keymodifer, int key) {
int keyCode = -1; //INV_KEY;
if ((key >=0xc0) && (key <=0xdf)) {
keyCode = ((key-0xc0) & 0x1f) + 0x7f;
}
else {
keyCode = key & 0x7f;
}
//Serial.println(keyCode);
if (keyCode != -1) {
iusbhk = keyCode;
}
}
void emu_KeyboardOnUp(int keymodifer, int key) {
iusbhk = 0;
}
void lnx_Init(void)
{
#ifdef HAS_SND
emu_sndInit();
#endif
}
void lnx_Start(char * filename)
{
emu_printf("emu starting");
#ifdef HAS_SND
#endif
lynx = new CSystem(filename, MIKIE_PIXEL_FORMAT_16BPP_565, HANDY_AUDIO_SAMPLE_FREQ);
if (lynx->mFileType == HANDY_FILETYPE_ILLEGAL)
{
emu_printf("illegal");
return;
}
gPrimaryFrameBuffer = (UBYTE*)((HandyPixel*)emu_LineBuffer(0)+1 );
gAudioBuffer = &sndbuffer[0];
memset(&sndbuffer[0], AUDIO_BUFFER_LENGTH*4,0);
gAudioEnabled = 1;
gAudioBufferPointer = AUDIO_BUFFER_LENGTH/2;
int rotation = DISPLAY_ROTATION_OFF;
uint32_t crc32 = lynx->mCart->CRC32();
emu_printh(crc32);
switch (crc32)
{
case 0x97501709: // Centipede
case 0x0271B6E9: // Lexis
case 0x006FD398: // NFL Football
case 0x1D9EC645:
case 0xBCD10C3A: // Raiden
rotation = DISPLAY_ROTATION_LEFT;
break;
case 0x7F0EC7AD: // Gauntlet
case 0xAC564BAA: // Gauntlet - The Third Encounter
case 0xFE19F59F:
case 0xA53649F1: // Klax
case 0xB8C75C2C: // Klax
rotation = DISPLAY_ROTATION_RIGHT;
break;
default:
if (lynx->mCart->CartGetRotate() == CART_ROTATE_LEFT)
rotation = DISPLAY_ROTATION_LEFT;
if (lynx->mCart->CartGetRotate() == CART_ROTATE_RIGHT)
rotation = DISPLAY_ROTATION_RIGHT;
}
switch(rotation)
{
case DISPLAY_ROTATION_LEFT:
width = HANDY_SCREEN_HEIGHT;
height = HANDY_SCREEN_WIDTH;
lynx->mMikie->SetRotation(MIKIE_ROTATE_L);
dpad_mapped_up = BUTTON_RIGHT;
dpad_mapped_down = BUTTON_LEFT;
dpad_mapped_left = BUTTON_UP;
dpad_mapped_right = BUTTON_DOWN;
break;
case DISPLAY_ROTATION_RIGHT:
width = HANDY_SCREEN_HEIGHT;
height = HANDY_SCREEN_WIDTH;
lynx->mMikie->SetRotation(MIKIE_ROTATE_R);
dpad_mapped_up = BUTTON_LEFT;
dpad_mapped_down = BUTTON_RIGHT;
dpad_mapped_left = BUTTON_DOWN;
dpad_mapped_right = BUTTON_UP;
break;
default:
width = HANDY_SCREEN_WIDTH;
height = HANDY_SCREEN_HEIGHT;
lynx->mMikie->SetRotation(MIKIE_NO_ROTATE);
dpad_mapped_up = BUTTON_UP;
dpad_mapped_down = BUTTON_DOWN;
dpad_mapped_left = BUTTON_LEFT;
dpad_mapped_right = BUTTON_RIGHT;
break;
}
emu_printf("emu started");
}
void lnx_Step(void)
{
ULONG buttons = 0;
int k=ik;
#ifdef TEECOMPUTER
int hk = ihk;
if (hk == 'q') {
buttons |= BUTTON_PAUSE;
}
#endif
if ( (k & MASK_JOY2_UP) || (k & MASK_JOY1_UP) ) buttons |= dpad_mapped_up;
if ( (k & MASK_JOY2_DOWN) || (k & MASK_JOY1_DOWN) ) buttons |= dpad_mapped_down;
if ( (k & MASK_JOY2_LEFT) || (k & MASK_JOY1_LEFT) ) buttons |= dpad_mapped_left;
if ( (k & MASK_JOY2_RIGHT) || (k & MASK_JOY1_RIGHT) ) buttons |= dpad_mapped_right;
if ( (k & MASK_JOY2_BTN) || (k & MASK_JOY1_BTN) ) buttons |= BUTTON_A;
if ( (k & MASK_KEY_USER1) ) buttons |= BUTTON_B;
if ( (k & MASK_KEY_USER2) ) buttons |= BUTTON_OPT2;
if ( (k & MASK_KEY_USER3) ) buttons |= BUTTON_OPT1;
lynx->SetButtonData(buttons);
lynx->UpdateFrame(true);
emu_DrawVsync();
pik = k;
// #sample written per frame
long ptr = gAudioBufferPointer;
long wdelta = ptr - audioowptr;
if (wdelta < 0) wdelta = AUDIO_BUFFER_LENGTH+wdelta;
audioowptr = ptr;
// #sample read per frame
int rdelta = deltar;
deltar = 0;
// Compute average R/W over AVG_COUNT frame
avgcounter--;
avgw += wdelta;
avgr += rdelta;
inc = (wdelta<<8)/(rdelta);
if (avgcounter == 0) {
wdelta = avgw/AVG_COUNT;
rdelta = avgr/AVG_COUNT;
avgw = 0;
avgr = 0;
avgcounter = AVG_COUNT;
//emu_printi(wdelta);
//emu_printi(rdelta);
long delta = ptr - audiorptr>>8;
if (delta < 0) delta = AUDIO_BUFFER_LENGTH+delta;
// we try to be keep read and write buffer at half distance of each other
bool down;
if (delta < pdelta) {
down = true; // keep going down
if (delta < (AUDIO_BUFFER_LENGTH/2)) {
if ( (down) && (pdown) )
incdelta += 2;
else
incdelta = 1;
}
}
else if (delta > pdelta) {
down = false; // goes up again
if (delta > (AUDIO_BUFFER_LENGTH/2)) {
if ( (!down) && (!pdown) )
incdelta -= 2;
else
incdelta = -1;
}
}
else {
incdelta = 0;
}
// Hard reset sound buffer?
if ( (delta < AUDIO_BUFFER_LENGTH/4) || (delta > (AUDIO_BUFFER_LENGTH-AUDIO_BUFFER_LENGTH/4)) ) {
/*
memset(sndbuffer,sizeof(sndbuffer),0);
gAudioBufferPointer = 0;
audioowptr = 0;
audiorptr=(AUDIO_BUFFER_LENGTH/2)<<8;
delta = AUDIO_BUFFER_LENGTH/2;
*/
//emu_printf("reset");
}
pdelta = delta;
pdown = down;
//emu_printi(delta);
}
//emu_printi(incdelta);
sndinc = inc+incdelta;
}
void SND_Process(void *stream, int len) {
short * dst = (short*)stream;
len = len >> 1;
for (int i=0;i<len;i++)
{
ULONG val = sndbuffer[audiorptr>>8];
*dst++ = (val >> 16);
*dst++ = (val & 0xffff);
audiorptr += sndinc;
audiorptr &= AUDIORPTRMASK;
deltar +=1;
}
/*
long pt = (gAudioBufferPointer) - len;
if (pt < 0) pt+=AUDIO_BUFFER_LENGTH;
for (int i=0;i<len;i++)
{
ULONG val = sndbuffer[pt++];
*dst++ = (val >> 16);
*dst++ = (val & 0xffff);
pt &= (AUDIO_BUFFER_LENGTH-1);
}
*/
}

Wyświetl plik

@ -0,0 +1,4 @@
extern void lnx_Init(void);
extern void lnx_Step(void);
extern void lnx_Start(char * filename);
extern void lnx_Input(int key);

Wyświetl plik

@ -0,0 +1,60 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//
// Generic Lynx base class.
//
#ifndef LYNXBASE_H
#define LYNXBASE_H
// bank0 - Cartridge bank 0
// bank1 - Cartridge bank 1
// ram - all ram
// cpu - system memory as viewed by the cpu
enum EMMODE {bank0,bank1,ram,cpu};
class CLynxBase
{
// Function members
public:
virtual ~CLynxBase() {};
public:
virtual void Reset(void) {};
virtual bool ContextLoad(FILE *fp) { return 0; };
virtual bool ContextSave(FILE *fp) { return 0; };
virtual void Poke(ULONG addr,UBYTE data)=0;
virtual UBYTE Peek(ULONG addr)=0;
virtual void PokeW(ULONG addr,UWORD data) {}; // ONLY mSystem overloads these, they are never use by the clients
virtual UWORD PeekW(ULONG addr) {return 0;};
virtual void BankSelect(EMMODE newbank){};
virtual ULONG ObjectSize(void) {return 1;};
virtual ULONG ReadCycle(void) {return 5;};
virtual ULONG WriteCycle(void) {return 5;};
};
#endif

Wyświetl plik

@ -0,0 +1,270 @@
/*
Wookie @
http://atariage.com/forums/topic/129030-lynx-encryption/
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHUNK_LENGTH (51)
const unsigned char lynx_public_mod[CHUNK_LENGTH] = {
0x35, 0xB5, 0xA3, 0x94, 0x28, 0x06, 0xD8, 0xA2,
0x26, 0x95, 0xD7, 0x71, 0xB2, 0x3C, 0xFD, 0x56,
0x1C, 0x4A, 0x19, 0xB6, 0xA3, 0xB0, 0x26, 0x00,
0x36, 0x5A, 0x30, 0x6E, 0x3C, 0x4D, 0x63, 0x38,
0x1B, 0xD4, 0x1C, 0x13, 0x64, 0x89, 0x36, 0x4C,
0xF2, 0xBA, 0x2A, 0x58, 0xF4, 0xFE, 0xE1, 0xFD,
0xAC, 0x7E, 0x79
};
#define min(x,y) ((x < y) ? x : y)
/* result = 2 * result */
void double_value(unsigned char *result, const int length)
{
int i, x;
x = 0;
for (i = length - 1; i >= 0; i--) {
x += 2 * result[i];
result[i] = (unsigned char) (x & 0xFF);
x >>= 8;
}
/* shouldn't carry */
}
/* result -= value */
int minus_equals_value(unsigned char *result,
const unsigned char *value,
const int length)
{
int i, x;
unsigned char *tmp;
/* allocate temporary buffer */
tmp = (unsigned char*)calloc(1, length);
x = 0;
for (i = length - 1; i >= 0; i--) {
x += result[i] - value[i];
tmp[i] = (unsigned char) (x & 0xFF);
x >>= 8;
}
if (x >= 0) {
/* move the result back to BB */
memcpy(result, tmp, length);
/* free the temporary buffer */
free(tmp);
/* this had a carry */
return 1;
}
/* free the temporary buffer */
free(tmp);
/* this didn't carry */
return 0;
}
/* result += value */
void plus_equals_value(unsigned char *result,
const unsigned char *value,
const int length)
{
int i, tmp;
int carry = 0;
for(i = length - 1; i >= 0; i--) {
tmp = result[i] + value[i] + carry;
if (tmp >= 256)
carry = 1;
else
carry = 0;
result[i] = (unsigned char) (tmp);
}
}
/* L = M * N mod modulus */
void lynx_mont(unsigned char *L, /* result */
const unsigned char *M, /* original chunk of encrypted data */
const unsigned char *N, /* copy of encrypted data */
const unsigned char *modulus,/* modulus */
const int length)
{
int i, j;
int carry;
unsigned char tmp;
unsigned char increment;
/* L = 0 */
memset(L, 0, length);
for(i = 0; i < length; i++) {
/* get the next byte from N */
tmp = N[i];
for(j = 0; j < 8; j++) {
/* L = L * 2 */
double_value(L, length);
/* carry is true if the MSB in tmp is set */
increment = (tmp & 0x80) / 0x80;
/* shift tmp's bits to the left by one */
tmp <<= 1;
if(increment != 0) {
/* increment the result... */
/* L += M */
plus_equals_value(L, M, length);
/* do a modulus correction */
/* L -= modulus */
carry = minus_equals_value(L, modulus, length);
/* if there was a carry, do it again */
/* L -= modulus */
if (carry != 0)
minus_equals_value(L, modulus, length);
} else {
/* instead decrement the result */
/* L -= modulus */
minus_equals_value(L, modulus, length);
}
}
}
}
/* this decrypts a single block of encrypted data by using the montgomery
* multiplication method to do modular exponentiation.
*/
int decrypt_block(int accumulator,
unsigned char * result,
const unsigned char * encrypted,
const unsigned char * public_exp,
const unsigned char * public_mod,
const int length)
{
int i;
unsigned char* rptr = result;
const unsigned char* eptr = encrypted;
unsigned char *A;
unsigned char *B;
unsigned char *TMP;
/* allocate the working buffers */
A = (unsigned char*) calloc(1, length);
B = (unsigned char*)calloc(1, length);
TMP = (unsigned char*)calloc(1, length);
/* this copies the next length sized block of data from the encrypted
* data into our temporary memory buffer in reverse order */
for(i = length - 1; i >= 0; i--) {
B[i] = *eptr;
eptr++;
}
/* so it took me a while to wrap my head around this because I couldn't
* figure out how the exponent was used in the process. RSA is
* a ^ b (mod c) and I couldn't figure out how that was being done until
* I realized that the public exponent for lynx decryption is just 3. That
* means that to decrypt each block, we only have to multiply each
* block by itself twice to raise it to the 3rd power:
* n^3 == n * n * n
*/
/* TODO: convert this to a loop that calls lynx_mont public_exp number of
* times so that we can raise the encrypted block of data to the power of
* public_exp and mod it by public_mod. this will make this flexible
* enough to be used to encrypt data as well.
*/
/* do Montgomery multiplication: A = B^2 */
lynx_mont(A, B, B, public_mod, length);
/* copy the result into the temp buffer: TMP = B^2 */
memcpy(TMP, A, length);
/* do Montgomery multiplication again: A = B^3 */
lynx_mont(A, B, TMP, public_mod, length);
/* So I'm not sure if this is part of the Montgomery multiplication
* algorithm since I don't fully understand how that works. This may be
* just another obfuscation step done during the encryption process.
* The output of the decryption process has to be accumulated and masked
* to get the original bytes. If I had to place a bet, I would bet that
* this is not part of Montgomery multiplication and is just an obfuscation
* preprocessing step done on the plaintext data before it gets encrypted.
*/
for(i = length - 1; i > 0; i--) {
accumulator += A[i];
accumulator &= 0xFF;
(*rptr) = (unsigned char)(accumulator);
rptr++;
}
/* free the temporary buffer memory */
free(A);
free(B);
free(TMP);
return accumulator;
}
/* this function decrypts a single frame of encrypted data. a frame consists of
* a single byte block count followed by the count number of blocks of
* encrypted data.
*/
int decrypt_frame(unsigned char * result,
const unsigned char * encrypted,
const unsigned char * public_exp,
const unsigned char * public_mod,
const int length)
{
int i;
int blocks;
int accumulator;
unsigned char* rptr = result;
const unsigned char* eptr = encrypted;
/* reset the accumulator for the modulus step */
accumulator = 0;
/* calculate how many encrypted blocks there are */
blocks = 256 - *eptr;
/* move our index to the beginning of the next block */
eptr++;
for(i = 0; i < blocks; i++) {
/* decrypt a single block of encrypted data */
accumulator = decrypt_block(accumulator, rptr, eptr, public_exp, public_mod, length);
/* move result pointer ahead */
rptr += (length - 1);
/* move read pointer ahead */
eptr += length;
}
/* return the number of blocks decrypted */
return blocks;
}
/* this is a completely refactored version of what happens in the Lynx at boot
* time. the original code was a very rough reverse of the Lynx ROM code, this
* is much easier to understand.
*/
void lynx_decrypt(unsigned char * result, const unsigned char * encrypted, const int length)
{
/* decrypt the first frame of encrypted data */
decrypt_frame(&result[0], &encrypted[0], /* lynx_public_exp */ 0, lynx_public_mod, length);
}

Wyświetl plik

@ -0,0 +1,281 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// Generic lynx definition header file //
//////////////////////////////////////////////////////////////////////////////
// //
// This header file provides the definition of all of the useful hardware //
// addresses within the Lynx. //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#define TMPADR 0xfc00
#define TMPADRL 0xfc00
#define TMPADRH 0xfc01
#define TILTACUM 0xfc02
#define TILTACUML 0xfc02
#define TILTACUMH 0xfc03
#define HOFF 0xfc04
#define HOFFL 0xfc04
#define HOFFH 0xfc05
#define VOFF 0xfc06
#define VOFFL 0xfc06
#define VOFFH 0xfc07
#define VIDBAS 0xfc08
#define VIDBASL 0xfc08
#define VIDBASH 0xfc09
#define COLLBAS 0xfc0a
#define COLLBASL 0xfc0a
#define COLLBASH 0xfc0b
#define VIDADR 0xfc0c
#define VIDADRL 0xfc0c
#define VIDADRH 0xfc0d
#define COLLADR 0xfc0e
#define COLLADRL 0xfc0e
#define COLLADRH 0xfc0f
#define SCBNEXT 0xfc10
#define SCBNEXTL 0xfc10
#define SCBNEXTH 0xfc11
#define SPRDLINE 0xfc12
#define SPRDLINEL 0xfc12
#define SPRDLINEH 0xfc13
#define HPOSSTRT 0xfc14
#define HPOSSTRTL 0xfc14
#define HPOSSTRTH 0xfc15
#define VPOSSTRT 0xfc16
#define VPOSSTRTL 0xfc16
#define VPOSSTRTH 0xfc17
#define SPRHSIZ 0xfc18
#define SPRHSIZL 0xfc18
#define SPRHSIZH 0xfc19
#define SPRVSIZ 0xfc1a
#define SPRVSIZL 0xfc1a
#define SPRVSIZH 0xfc1b
#define STRETCH 0xfc1c
#define STRETCHL 0xfc1c
#define STRETCHH 0xfc1d
#define TILT 0xfc1e
#define TILTL 0xfc1e
#define TILTH 0xfc1f
#define SPRDOFF 0xfc20
#define SPRDOFFL 0xfc20
#define SPRDOFFH 0xfc21
#define SPRVPOS 0xfc22
#define SPRVPOSL 0xfc22
#define SPRVPOSH 0xfc23
#define COLLOFF 0xfc24
#define COLLOFFL 0xfc24
#define COLLOFFH 0xfc25
#define VSIZACUM 0xfc26
#define VSIZACUML 0xfc26
#define VSIZACUMH 0xfc27
#define HSIZOFF 0xfc28
#define HSIZOFFL 0xfc28
#define HSIZOFFH 0xfc29
#define VSIZOFF 0xfc2a
#define VSIZOFFL 0xfc2a
#define VSIZOFFH 0xfc2b
#define SCBADR 0xfc2c
#define SCBADRL 0xfc2c
#define SCBADRH 0xfc2d
#define PROCADR 0xfc2e
#define PROCADRL 0xfc2e
#define PROCADRH 0xfc2f
#define MATHD 0xfc52
#define MATHC 0xfc53
#define MATHB 0xfc54
#define MATHA 0xfc55
#define MATHP 0xfc56
#define MATHN 0xfc57
#define MATHH 0xfc60
#define MATHG 0xfc61
#define MATHF 0xfc62
#define MATHE 0xfc63
#define MATHM 0xfc6c
#define MATHL 0xfc6d
#define MATHK 0xfc6e
#define MATHJ 0xfc6f
#define SPRCTL0 0xfc80
#define SPRCTL1 0xfc81
#define SPRCOLL 0xfc82
#define SPRINIT 0xfc83
#define SUZYHREV 0xfc88
#define SUZYSREV 0xfc89
#define SUZYBUSEN 0xfc90
#define SPRGO 0xfc91
#define SPRSYS 0xfc92
#define JOYSTICK 0xfcb0
#define SWITCHES 0xfcb1
#define RCART0 0xfcb2
#define RCART1 0xfcb3
#define LEDS 0xfcc0
#define PPORTSTAT 0xfcc2
#define PPORTDATA 0xfcc3
#define HOWIE 0xfcc4
#define TIM0BKUP 0xfd00
#define TIM0CTLA 0xfd01
#define TIM0CNT 0xfd02
#define TIM0CTLB 0xfd03
#define TIM1BKUP 0xfd04
#define TIM1CTLA 0xfd05
#define TIM1CNT 0xfd06
#define TIM1CTLB 0xfd07
#define TIM2BKUP 0xfd08
#define TIM2CTLA 0xfd09
#define TIM2CNT 0xfd0a
#define TIM2CTLB 0xfd0b
#define TIM3BKUP 0xfd0c
#define TIM3CTLA 0xfd0d
#define TIM3CNT 0xfd0e
#define TIM3CTLB 0xfd0f
#define TIM4BKUP 0xfd10
#define TIM4CTLA 0xfd11
#define TIM4CNT 0xfd12
#define TIM4CTLB 0xfd13
#define TIM5BKUP 0xfd14
#define TIM5CTLA 0xfd15
#define TIM5CNT 0xfd16
#define TIM5CTLB 0xfd17
#define TIM6BKUP 0xfd18
#define TIM6CTLA 0xfd19
#define TIM6CNT 0xfd1a
#define TIM6CTLB 0xfd1b
#define TIM7BKUP 0xfd1c
#define TIM7CTLA 0xfd1d
#define TIM7CNT 0xfd1e
#define TIM7CTLB 0xfd1f
#define AUD0VOL 0xfd20
#define AUD0SHFTFB 0xfd21
#define AUD0OUTVAL 0xfd22
#define AUD0L8SHFT 0xfd23
#define AUD0TBACK 0xfd24
#define AUD0CTL 0xfd25
#define AUD0COUNT 0xfd26
#define AUD0MISC 0xfd27
#define AUD1VOL 0xfd28
#define AUD1SHFTFB 0xfd29
#define AUD1OUTVAL 0xfd2a
#define AUD1L8SHFT 0xfd2b
#define AUD1TBACK 0xfd2c
#define AUD1CTL 0xfd2d
#define AUD1COUNT 0xfd2e
#define AUD1MISC 0xfd2f
#define AUD2VOL 0xfd30
#define AUD2SHFTFB 0xfd31
#define AUD2OUTVAL 0xfd32
#define AUD2L8SHFT 0xfd33
#define AUD2TBACK 0xfd34
#define AUD2CTL 0xfd35
#define AUD2COUNT 0xfd36
#define AUD2MISC 0xfd37
#define AUD3VOL 0xfd38
#define AUD3SHFTFB 0xfd39
#define AUD3OUTVAL 0xfd3a
#define AUD3L8SHFT 0xfd3b
#define AUD3TBACK 0xfd3c
#define AUD3CTL 0xfd3d
#define AUD3COUNT 0xfd3e
#define AUD3MISC 0xfd3f
#define ATTEN_A 0xFD40 //
#define ATTEN_B 0xFD41
#define ATTEN_C 0xFD42 // Lynx2 Regs see macros/handy.equ
#define ATTEN_D 0xFD43
#define MPAN 0xFD44 //
#define MSTEREO 0xfd50
#define INTRST 0xfd80
#define INTSET 0xfd81
#define MAGRDY0 0xfd84
#define MAGRDY1 0xfd85
#define AUDIN 0xfd86
#define SYSCTL1 0xfd87
#define MIKEYHREV 0xfd88
#define MIKEYSREV 0xfd89
#define IODIR 0xfd8a
#define IODAT 0xfd8b
#define SERCTL 0xfd8c
#define SERDAT 0xfd8d
#define SDONEACK 0xfd90
#define CPUSLEEP 0xfd91
#define DISPCTL 0xfd92
#define PBKUP 0xfd93
#define DISPADR 0xfd94
#define DISPADRL 0xfd94
#define DISPADRH 0xfd95
#define Mtest0 0xfd9c
#define Mtest1 0xfd9d
#define Mtest2 0xfd9e
#define GREEN0 0xfda0
#define GREEN1 0xfda1
#define GREEN2 0xfda2
#define GREEN3 0xfda3
#define GREEN4 0xfda4
#define GREEN5 0xfda5
#define GREEN6 0xfda6
#define GREEN7 0xfda7
#define GREEN8 0xfda8
#define GREEN9 0xfda9
#define GREENA 0xfdaa
#define GREENB 0xfdab
#define GREENC 0xfdac
#define GREEND 0xfdad
#define GREENE 0xfdae
#define GREENF 0xfdaf
#define BLUERED0 0xfdb0
#define BLUERED1 0xfdb1
#define BLUERED2 0xfdb2
#define BLUERED3 0xfdb3
#define BLUERED4 0xfdb4
#define BLUERED5 0xfdb5
#define BLUERED6 0xfdb6
#define BLUERED7 0xfdb7
#define BLUERED8 0xfdb8
#define BLUERED9 0xfdb9
#define BLUEREDA 0xfdba
#define BLUEREDB 0xfdbb
#define BLUEREDC 0xfdbc
#define BLUEREDD 0xfdbd
#define BLUEREDE 0xfdbe
#define BLUEREDF 0xfdbf
#define MMAPCTL 0xfff9
#define CPUNMI 0xfffa
#define CPUNMIL 0xfffa
#define CPUNMIH 0xfffb
#define CPURESET 0xfffc
#define CPURESETL 0xfffc
#define CPURESETH 0xfffd
#define CPUINT 0xfffe
#define CPUINTL 0xfffe
#define CPUINTH 0xffff

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,410 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// Mikey class header file //
//////////////////////////////////////////////////////////////////////////////
// //
// This header file provides the interface definition and some of the code //
// for the Mikey chip within the Lynx. The most crucial code is the //
// Update() function which as you can probably guess updates all of the //
// Mikey hardware counters and screen DMA from the prevous time it was //
// called. Yes I know how to spell Mikey but I cant be bothered to change //
// it everywhere. //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef MIKIE_H
#define MIKIE_H
//#define TRACE_MIKIE
#ifdef TRACE_MIKIE
#define TRACE_MIKIE0(msg) _RPT1(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",gSystemCycleCount)
#define TRACE_MIKIE1(msg,arg1) _RPT2(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,gSystemCycleCount)
#define TRACE_MIKIE2(msg,arg1,arg2) _RPT3(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,arg2,gSystemCycleCount)
#define TRACE_MIKIE3(msg,arg1,arg2,arg3) _RPT4(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,arg2,arg3,gSystemCycleCount)
#else
#define TRACE_MIKIE0(msg)
#define TRACE_MIKIE1(msg,arg1)
#define TRACE_MIKIE2(msg,arg1,arg2)
#define TRACE_MIKIE3(msg,arg1,arg2,arg3)
#endif
class CSystem;
#define MIKIE_START 0xfd00
#define MIKIE_SIZE 0x100
#define DMA_RDWR_CYC 4
//
// Define counter types and defines
//
#define CTRL_A_IRQEN 0x80
#define CTRL_A_RTD 0x40
#define CTRL_A_RELOAD 0x10
#define CTRL_A_COUNT 0x08
#define CTRL_A_DIVIDE 0x07
#define CTRL_B_TDONE 0x08
#define CTRL_B_LASTCK 0x04
#define CTRL_B_CIN 0x02
#define CTRL_B_COUT 0x01
#define LINE_TIMER 0x00
#define SCREEN_TIMER 0x02
#define LINE_WIDTH 160
#define LINE_SIZE 80
#define UART_TX_INACTIVE 0x80000000
#define UART_RX_INACTIVE 0x80000000
#define UART_BREAK_CODE 0x00008000
#define UART_MAX_RX_QUEUE 32
#define UART_TX_TIME_PERIOD (11)
#define UART_RX_TIME_PERIOD (11)
#define UART_RX_NEXT_DELAY (44)
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE unused:8;
UBYTE unused2:8;
UBYTE unused3:4;
UBYTE Blue:4;
UBYTE Red:4;
UBYTE Green:4;
#else
UBYTE Green:4;
UBYTE Red:4;
UBYTE Blue:4;
#endif
}Colours;
ULONG Index;
};
}TPALETTE;
//
// Enumerated types for possible mikie windows independant modes
//
enum
{
MIKIE_BAD_MODE=0,
MIKIE_NO_ROTATE,
MIKIE_ROTATE_L,
MIKIE_ROTATE_R
};
enum
{
MIKIE_PIXEL_FORMAT_16BPP_565=0,
MIKIE_PIXEL_FORMAT_16BPP_565_BE
};
class CMikie : public CLynxBase
{
public:
CMikie(CSystem& parent, ULONG displayformat, ULONG samplerate);
~CMikie();
bool ContextSave(LSS_FILE *fp);
bool ContextLoad(LSS_FILE *fp);
void Reset(void);
UBYTE Peek(ULONG addr);
void Poke(ULONG addr,UBYTE data);
ULONG ObjectSize(void) {return MIKIE_SIZE;};
void PresetForHomebrew(void);
void ComLynxCable(int status);
void ComLynxRxData(int data);
void ComLynxTxLoopback(int data);
void ComLynxTxCallback(void (*function)(int data,ULONG objref),ULONG objref);
void BuildPalette(void);
void Update(void);
void SetRotation(UBYTE data) {mDisplayRotate_Pending = data;};
inline bool SwitchAudInDir(void){ return(mIODIR&0x10);};
inline bool SwitchAudInValue(void){ return (mIODAT&0x10);};
private:
inline void UpdateSound(void);
inline void UpdateCalcSound(void);
inline void ResetDisplayPtr();
ULONG DisplayRenderLine(void);
void BlowOut(void);
private:
CSystem &mSystem;
// Hardware storage
ULONG mDisplayAddress;
ULONG mAudioInputComparator;
ULONG mTimerStatusFlags;
ULONG mTimerInterruptMask;
UBYTE mDisplayRotate;
UBYTE mDisplayRotate_Pending;
TPALETTE mPalette[16];
HandyPixel mColourMap[4096];
ULONG mIODAT;
ULONG mIODIR;
ULONG mIODAT_REST_SIGNAL;
ULONG mDISPCTL_DMAEnable;
ULONG mDISPCTL_Flip;
ULONG mDISPCTL_FourColour;
ULONG mDISPCTL_Colour;
ULONG mTIM_0_BKUP;
ULONG mTIM_0_ENABLE_RELOAD;
ULONG mTIM_0_ENABLE_COUNT;
ULONG mTIM_0_LINKING;
ULONG mTIM_0_CURRENT;
ULONG mTIM_0_TIMER_DONE;
ULONG mTIM_0_LAST_CLOCK;
ULONG mTIM_0_BORROW_IN;
ULONG mTIM_0_BORROW_OUT;
ULONG mTIM_0_LAST_LINK_CARRY;
ULONG mTIM_0_LAST_COUNT;
ULONG mTIM_1_BKUP;
ULONG mTIM_1_ENABLE_RELOAD;
ULONG mTIM_1_ENABLE_COUNT;
ULONG mTIM_1_LINKING;
ULONG mTIM_1_CURRENT;
ULONG mTIM_1_TIMER_DONE;
ULONG mTIM_1_LAST_CLOCK;
ULONG mTIM_1_BORROW_IN;
ULONG mTIM_1_BORROW_OUT;
ULONG mTIM_1_LAST_LINK_CARRY;
ULONG mTIM_1_LAST_COUNT;
ULONG mTIM_2_BKUP;
ULONG mTIM_2_ENABLE_RELOAD;
ULONG mTIM_2_ENABLE_COUNT;
ULONG mTIM_2_LINKING;
ULONG mTIM_2_CURRENT;
ULONG mTIM_2_TIMER_DONE;
ULONG mTIM_2_LAST_CLOCK;
ULONG mTIM_2_BORROW_IN;
ULONG mTIM_2_BORROW_OUT;
ULONG mTIM_2_LAST_LINK_CARRY;
ULONG mTIM_2_LAST_COUNT;
ULONG mTIM_3_BKUP;
ULONG mTIM_3_ENABLE_RELOAD;
ULONG mTIM_3_ENABLE_COUNT;
ULONG mTIM_3_LINKING;
ULONG mTIM_3_CURRENT;
ULONG mTIM_3_TIMER_DONE;
ULONG mTIM_3_LAST_CLOCK;
ULONG mTIM_3_BORROW_IN;
ULONG mTIM_3_BORROW_OUT;
ULONG mTIM_3_LAST_LINK_CARRY;
ULONG mTIM_3_LAST_COUNT;
ULONG mTIM_4_BKUP;
ULONG mTIM_4_ENABLE_RELOAD;
ULONG mTIM_4_ENABLE_COUNT;
ULONG mTIM_4_LINKING;
ULONG mTIM_4_CURRENT;
ULONG mTIM_4_TIMER_DONE;
ULONG mTIM_4_LAST_CLOCK;
ULONG mTIM_4_BORROW_IN;
ULONG mTIM_4_BORROW_OUT;
ULONG mTIM_4_LAST_LINK_CARRY;
ULONG mTIM_4_LAST_COUNT;
ULONG mTIM_5_BKUP;
ULONG mTIM_5_ENABLE_RELOAD;
ULONG mTIM_5_ENABLE_COUNT;
ULONG mTIM_5_LINKING;
ULONG mTIM_5_CURRENT;
ULONG mTIM_5_TIMER_DONE;
ULONG mTIM_5_LAST_CLOCK;
ULONG mTIM_5_BORROW_IN;
ULONG mTIM_5_BORROW_OUT;
ULONG mTIM_5_LAST_LINK_CARRY;
ULONG mTIM_5_LAST_COUNT;
ULONG mTIM_6_BKUP;
ULONG mTIM_6_ENABLE_RELOAD;
ULONG mTIM_6_ENABLE_COUNT;
ULONG mTIM_6_LINKING;
ULONG mTIM_6_CURRENT;
ULONG mTIM_6_TIMER_DONE;
ULONG mTIM_6_LAST_CLOCK;
ULONG mTIM_6_BORROW_IN;
ULONG mTIM_6_BORROW_OUT;
ULONG mTIM_6_LAST_LINK_CARRY;
ULONG mTIM_6_LAST_COUNT;
ULONG mTIM_7_BKUP;
ULONG mTIM_7_ENABLE_RELOAD;
ULONG mTIM_7_ENABLE_COUNT;
ULONG mTIM_7_LINKING;
ULONG mTIM_7_CURRENT;
ULONG mTIM_7_TIMER_DONE;
ULONG mTIM_7_LAST_CLOCK;
ULONG mTIM_7_BORROW_IN;
ULONG mTIM_7_BORROW_OUT;
ULONG mTIM_7_LAST_LINK_CARRY;
ULONG mTIM_7_LAST_COUNT;
ULONG mAUDIO_0_BKUP;
ULONG mAUDIO_0_ENABLE_RELOAD;
ULONG mAUDIO_0_ENABLE_COUNT;
ULONG mAUDIO_0_LINKING;
ULONG mAUDIO_0_CURRENT;
ULONG mAUDIO_0_TIMER_DONE;
ULONG mAUDIO_0_LAST_CLOCK;
ULONG mAUDIO_0_BORROW_IN;
ULONG mAUDIO_0_BORROW_OUT;
ULONG mAUDIO_0_LAST_LINK_CARRY;
ULONG mAUDIO_0_LAST_COUNT;
SBYTE mAUDIO_0_VOLUME;
ULONG mAUDIO_0_INTEGRATE_ENABLE;
ULONG mAUDIO_0_WAVESHAPER;
ULONG mAUDIO_1_BKUP;
ULONG mAUDIO_1_ENABLE_RELOAD;
ULONG mAUDIO_1_ENABLE_COUNT;
ULONG mAUDIO_1_LINKING;
ULONG mAUDIO_1_CURRENT;
ULONG mAUDIO_1_TIMER_DONE;
ULONG mAUDIO_1_LAST_CLOCK;
ULONG mAUDIO_1_BORROW_IN;
ULONG mAUDIO_1_BORROW_OUT;
ULONG mAUDIO_1_LAST_LINK_CARRY;
ULONG mAUDIO_1_LAST_COUNT;
SBYTE mAUDIO_1_VOLUME;
ULONG mAUDIO_1_INTEGRATE_ENABLE;
ULONG mAUDIO_1_WAVESHAPER;
ULONG mAUDIO_2_BKUP;
ULONG mAUDIO_2_ENABLE_RELOAD;
ULONG mAUDIO_2_ENABLE_COUNT;
ULONG mAUDIO_2_LINKING;
ULONG mAUDIO_2_CURRENT;
ULONG mAUDIO_2_TIMER_DONE;
ULONG mAUDIO_2_LAST_CLOCK;
ULONG mAUDIO_2_BORROW_IN;
ULONG mAUDIO_2_BORROW_OUT;
ULONG mAUDIO_2_LAST_LINK_CARRY;
ULONG mAUDIO_2_LAST_COUNT;
SBYTE mAUDIO_2_VOLUME;
ULONG mAUDIO_2_INTEGRATE_ENABLE;
ULONG mAUDIO_2_WAVESHAPER;
ULONG mAUDIO_3_BKUP;
ULONG mAUDIO_3_ENABLE_RELOAD;
ULONG mAUDIO_3_ENABLE_COUNT;
ULONG mAUDIO_3_LINKING;
ULONG mAUDIO_3_CURRENT;
ULONG mAUDIO_3_TIMER_DONE;
ULONG mAUDIO_3_LAST_CLOCK;
ULONG mAUDIO_3_BORROW_IN;
ULONG mAUDIO_3_BORROW_OUT;
ULONG mAUDIO_3_LAST_LINK_CARRY;
ULONG mAUDIO_3_LAST_COUNT;
SBYTE mAUDIO_3_VOLUME;
ULONG mAUDIO_3_INTEGRATE_ENABLE;
ULONG mAUDIO_3_WAVESHAPER;
SBYTE mAUDIO_OUTPUT[4];
UBYTE mAUDIO_ATTEN[4];
ULONG mSTEREO;
ULONG mPAN;
//
// Serial related variables
//
ULONG mUART_RX_IRQ_ENABLE;
ULONG mUART_TX_IRQ_ENABLE;
ULONG mUART_RX_COUNTDOWN;
ULONG mUART_TX_COUNTDOWN;
ULONG mUART_SENDBREAK;
ULONG mUART_TX_DATA;
ULONG mUART_RX_DATA;
ULONG mUART_RX_READY;
ULONG mUART_PARITY_ENABLE;
ULONG mUART_PARITY_EVEN;
SLONG mUART_CABLE_PRESENT;
void (*mpUART_TX_CALLBACK)(int data,ULONG objref);
ULONG mUART_TX_CALLBACK_OBJECT;
SLONG mUART_Rx_input_queue[UART_MAX_RX_QUEUE];
ULONG mUART_Rx_input_ptr;
ULONG mUART_Rx_output_ptr;
SLONG mUART_Rx_waiting;
SLONG mUART_Rx_framing_error;
SLONG mUART_Rx_overun_error;
//
// Screen related
//
UBYTE *mpDisplayCurrent;
UBYTE *mpRamPointer;
ULONG mLynxLine;
ULONG mLynxLineDMACounter;
ULONG mLynxAddr;
ULONG mAudioSampleRate;
ULONG mDisplayFormat;
ULONG mDisplayPitch;
};
#endif

Wyświetl plik

@ -0,0 +1,41 @@
#ifndef _PLATFORM_CONFIG_H_
#define _PLATFORM_CONFIG_H_
#define TEECOMPUTER 1
#ifdef TEECOMPUTER
//#define ILI9341 1
//#define ST7789 1
//#define TFTSPI1 1
#define HAS_T4_VGA 1
#define HAS_SND 1
#define HAS_USBKEY 1
//#define INVX 1
#define PT8211 1
#else
#define HAS_T4_VGA 1
//#define INVX 1
#define INVY 1
#define HAS_SND 1
#define HAS_USBKEY 1
#endif
//#define ILI9341 1
//#define ST7789 1
//#define SWAP_JOYSTICK 1
//#define LOHRES 1
//#define ROTATE_SCREEN 1
//#define EXTERNAL_SD 1
//#define USE_SDFAT 1
//#define SD_FAT_TYPE 1
//#define USE_SDFS 1
//#define SDFSDEV "1:"
#endif

Wyświetl plik

@ -0,0 +1,145 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// RAM object header file //
//////////////////////////////////////////////////////////////////////////////
// //
// This header file provides the interface definition for the RAM class //
// that emulates the Handy system RAM (64K) //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef RAM_H
#define RAM_H
#define RAM_SIZE 65536
typedef struct
{
UWORD jump;
UWORD load_address;
UWORD size;
UBYTE magic[4];
}HOME_HEADER;
static UBYTE STATIC_RAM[RAM_SIZE];
class CRam : public CLynxBase
{
// Function members
public:
CRam(UBYTE *filedata, ULONG filesize)
{
mRamData = (UBYTE*)&STATIC_RAM;
if (filedata && filesize > 64 && memcmp(filedata + 6, "BS93", 4) == 0) {
#ifdef MSB_FIRST
mHomebrewAddr = filedata[2] << 8 | filedata[3];
mHomebrewSize = filedata[4] << 8 | filedata[5];
#else
mHomebrewAddr = filedata[3] << 8 | filedata[2];
mHomebrewSize = filedata[5] << 8 | filedata[4];
#endif
mHomebrewSize = filesize > mHomebrewSize ? mHomebrewSize : filesize;
mHomebrewAddr -= 10;
mHomebrewData = new UBYTE[mHomebrewSize];
memcpy(mHomebrewData, filedata, mHomebrewSize);
log_printf("Homebrew found: size=%d, addr=0x%04X\n", mHomebrewSize, mHomebrewAddr);
} else {
mHomebrewData = NULL;
mHomebrewAddr = 0;
mHomebrewSize = 0;
}
Reset();
}
~CRam()
{
if (mHomebrewData) {
delete[] mHomebrewData;
mHomebrewData=NULL;
}
}
void Reset(void)
{
if (mHomebrewData) {
// Load the cart into RAM
memset(mRamData, 0x00, RAM_SIZE);
memcpy(mRamData+mHomebrewAddr, mHomebrewData, mHomebrewSize);
gCPUBootAddress = mHomebrewAddr;
} else {
memset(mRamData, 0xFF, RAM_SIZE);
}
}
void Clear(void)
{
memset(mRamData, 0, RAM_SIZE);
}
bool ContextSave(LSS_FILE *fp)
{
if(!lss_printf(fp,"CRam::ContextSave")) return 0;
if(!lss_write(mRamData,sizeof(UBYTE),RAM_SIZE,fp)) return 0;
return 1;
}
bool ContextLoad(LSS_FILE *fp)
{
char teststr[32]="XXXXXXXXXXXXXXXXX";
if(!lss_read(teststr,sizeof(char),17,fp)) return 0;
if(strcmp(teststr,"CRam::ContextSave")!=0) return 0;
if(!lss_read(mRamData,sizeof(UBYTE),RAM_SIZE,fp)) return 0;
return 1;
}
void Poke(ULONG addr, UBYTE data) {mRamData[addr] = data;};
UBYTE Peek(ULONG addr) {return mRamData[addr];};
ULONG ObjectSize(void) {return RAM_SIZE;};
UBYTE* GetRamPointer(void) {return mRamData;};
// Data members
private:
UBYTE *mRamData;
UBYTE *mHomebrewData;
ULONG mHomebrewSize;
ULONG mHomebrewAddr;
};
#endif

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,589 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// Susie object header file //
//////////////////////////////////////////////////////////////////////////////
// //
// This header file provides the interface definition for the Suzy class //
// which provides math and sprite support to the emulator //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef SUSIE_H
#define SUSIE_H
#ifdef TRACE_SUSIE
#define TRACE_SUSIE0(msg) _RPT1(_CRT_WARN,"CSusie::"msg" (Time=%012d)\n",gSystemCycleCount)
#define TRACE_SUSIE1(msg,arg1) _RPT2(_CRT_WARN,"CSusie::"msg" (Time=%012d)\n",arg1,gSystemCycleCount)
#define TRACE_SUSIE2(msg,arg1,arg2) _RPT3(_CRT_WARN,"CSusie::"msg" (Time=%012d)\n",arg1,arg2,gSystemCycleCount)
#define TRACE_SUSIE3(msg,arg1,arg2,arg3) _RPT4(_CRT_WARN,"CSusie::"msg" (Time=%012d)\n",arg1,arg2,arg3,gSystemCycleCount)
#else
#define TRACE_SUSIE0(msg)
#define TRACE_SUSIE1(msg,arg1)
#define TRACE_SUSIE2(msg,arg1,arg2)
#define TRACE_SUSIE3(msg,arg1,arg2,arg3)
#endif
class CSystem;
#define SUSIE_START 0xfc00
#define SUSIE_SIZE 0x100
#define LINE_END 0x80
#define SPR_RDWR_CYC 3
//
// Define button values
//
#define BUTTON_A 0x0001
#define BUTTON_B 0x0002
#define BUTTON_OPT2 0x0004
#define BUTTON_OPT1 0x0008
#define BUTTON_LEFT 0x0010
#define BUTTON_RIGHT 0x0020
#define BUTTON_UP 0x0040
#define BUTTON_DOWN 0x0080
#define BUTTON_PAUSE 0x0100
enum {line_error=0,line_abs_literal,line_literal,line_packed};
enum {math_finished=0,math_divide,math_multiply,math_init_divide,math_init_multiply};
enum {sprite_background_shadow=0,
sprite_background_noncollide,
sprite_boundary_shadow,
sprite_boundary,
sprite_normal,
sprite_noncollide,
sprite_xor_shadow,
sprite_shadow};
// Define register typdefs
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE High;
UBYTE Low;
#else
UBYTE Low;
UBYTE High;
#endif
}Byte;
UWORD Word;
};
}UUWORD;
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE Fc1:1;
UBYTE Fc2:1;
UBYTE Fc3:1;
UBYTE reserved:1;
UBYTE Ac1:1;
UBYTE Ac2:1;
UBYTE Ac3:1;
UBYTE Ac4:1;
#else
UBYTE Ac4:1;
UBYTE Ac3:1;
UBYTE Ac2:1;
UBYTE Ac1:1;
UBYTE reserved:1;
UBYTE Fc3:1;
UBYTE Fc2:1;
UBYTE Fc1:1;
#endif
}Bits;
UBYTE Byte;
};
}TSPRINIT;
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE Up:1;
UBYTE Down:1;
UBYTE Left:1;
UBYTE Right:1;
UBYTE Option1:1;
UBYTE Option2:1;
UBYTE Inside:1;
UBYTE Outside:1;
#else
UBYTE Outside:1;
UBYTE Inside:1;
UBYTE Option2:1;
UBYTE Option1:1;
UBYTE Right:1;
UBYTE Left:1;
UBYTE Down:1;
UBYTE Up:1;
#endif
}Bits;
UBYTE Byte;
};
}TJOYSTICK;
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE spare:5;
UBYTE Cart1IO:1;
UBYTE Cart0IO:1;
UBYTE Pause:1;
#else
UBYTE Pause:1;
UBYTE Cart0IO:1;
UBYTE Cart1IO:1;
UBYTE spare:5;
#endif
}Bits;
UBYTE Byte;
};
}TSWITCHES;
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE A;
UBYTE B;
UBYTE C;
UBYTE D;
#else
UBYTE D;
UBYTE C;
UBYTE B;
UBYTE A;
#endif
}Bytes;
struct
{
#ifdef MSB_FIRST
UWORD AB;
UWORD CD;
#else
UWORD CD;
UWORD AB;
#endif
}Words;
ULONG Long;
};
}TMATHABCD;
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE E;
UBYTE F;
UBYTE G;
UBYTE H;
#else
UBYTE H;
UBYTE G;
UBYTE F;
UBYTE E;
#endif
}Bytes;
struct
{
#ifdef MSB_FIRST
UWORD EF;
UWORD GH;
#else
UWORD GH;
UWORD EF;
#endif
}Words;
ULONG Long;
};
}TMATHEFGH;
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE J;
UBYTE K;
UBYTE L;
UBYTE M;
#else
UBYTE M;
UBYTE L;
UBYTE K;
UBYTE J;
#endif
}Bytes;
struct
{
#ifdef MSB_FIRST
UWORD JK;
UWORD LM;
#else
UWORD LM;
UWORD JK;
#endif
}Words;
ULONG Long;
};
}TMATHJKLM;
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
UBYTE xx2;
UBYTE xx1;
UBYTE N;
UBYTE P;
#else
UBYTE P;
UBYTE N;
UBYTE xx1;
UBYTE xx2;
#endif
}Bytes;
struct
{
#ifdef MSB_FIRST
UWORD xx1;
UWORD NP;
#else
UWORD NP;
UWORD xx1;
#endif
}Words;
ULONG Long;
};
}TMATHNP;
//
// As the Susie sprite engine only ever sees system RAM
// wa can access this directly without the hassle of
// going through the system object, much faster
//
#define RAM_PEEK(m) (mRamPointer[(m)])
#define RAM_PEEKW(m) (mRamPointer[(m)]+(mRamPointer[(m)+1]<<8))
#define RAM_POKE(m1,m2) {mRamPointer[(m1)]=(m2);}
#define MY_GET_BITS(retval_bits, bits) \
/* ULONG retval_bits; */ \
if(mLinePacketBitsLeft<=bits) retval_bits = 0; \
else \
{ \
if(mLineShiftRegCount<bits) \
{ \
mLineShiftReg<<=24; \
mLineShiftReg|=RAM_PEEK(mTMPADR.Word++)<<16; \
mLineShiftReg|=RAM_PEEK(mTMPADR.Word++)<<8; \
mLineShiftReg|=RAM_PEEK(mTMPADR.Word++); \
mLineShiftRegCount+=24; \
mCycles+=3*SPR_RDWR_CYC; \
} \
retval_bits=mLineShiftReg>>(mLineShiftRegCount-bits); \
retval_bits&=(1<<bits)-1; \
mLineShiftRegCount-=bits; \
mLinePacketBitsLeft-=bits; \
}
class CSusie : public CLynxBase
{
public:
CSusie(CSystem& parent);
~CSusie();
void Reset(void);
bool ContextSave(LSS_FILE *fp);
bool ContextLoad(LSS_FILE *fp);
UBYTE Peek(ULONG addr);
void Poke(ULONG addr,UBYTE data);
ULONG ReadCycle(void) {return 9;};
ULONG WriteCycle(void) {return 5;};
ULONG ObjectSize(void) {return SUSIE_SIZE;};
void SetButtonData(ULONG data) {mJOYSTICK.Byte=(UBYTE)data;mSWITCHES.Byte=(UBYTE)(data>>8);};
ULONG GetButtonData(void) {return mJOYSTICK.Byte+(mSWITCHES.Byte<<8);};
ULONG PaintSprites(void);
private:
inline ULONG LineInit(ULONG voff) {
// TRACE_SUSIE0("LineInit()");
mLineShiftReg=0;
mLineShiftRegCount=0;
mLineRepeatCount=0;
mLinePixel=0;
mLineType=line_error;
mLinePacketBitsLeft=0xffff;
// Initialise the temporary pointer
mTMPADR=mSPRDLINE;
// First read the Offset to the next line
ULONG offset;
MY_GET_BITS(offset,8)
// TRACE_SUSIE1("LineInit() Offset=%04x",offset);
// Specify the MAXIMUM number of bits in this packet, it
// can terminate early but can never use more than this
// without ending the current packet, we count down in LineGetBits()
mLinePacketBitsLeft=(offset-1)*8;
// Literals are a special case and get their count set on a line basis
if(mSPRCTL1_Literal)
{
mLineType=line_abs_literal;
mLineRepeatCount=((offset-1)*8)/mSPRCTL0_PixelBits;
// Why is this necessary, is this compensating for the 1,1 offset bug
// mLineRepeatCount--;
}
// TRACE_SUSIE1("LineInit() mLineRepeatCount=$%04x",mLineRepeatCount);
// Set the line base address for use in the calls to pixel painting
if(voff>101)
{
log_printf("CSusie::LineInit() Out of bounds (voff)\n");
voff=0;
}
mLineBaseAddress=mVIDBAS.Word+(voff*(HANDY_SCREEN_WIDTH/2));
mLineCollisionAddress=mCOLLBAS.Word+(voff*(HANDY_SCREEN_WIDTH/2));
// TRACE_SUSIE1("LineInit() mLineBaseAddress=$%04x",mLineBaseAddress);
// TRACE_SUSIE1("LineInit() mLineCollisionAddress=$%04x",mLineCollisionAddress);
// Return the offset to the next line
return offset;
};
inline void WritePixel(ULONG hoff,ULONG pixel) {
ULONG scr_addr=mLineBaseAddress+(hoff>>1);
UBYTE dest=RAM_PEEK(scr_addr);
if(!(hoff&0x01)) {
// Upper nibble screen write
dest&=0x0f;
dest|=pixel<<4;
} else {
// Lower nibble screen write
dest&=0xf0;
dest|=pixel;
}
RAM_POKE(scr_addr,dest);
// Increment cycle count for the read/modify/write
mCycles+=2*SPR_RDWR_CYC;
}
inline ULONG ReadPixel(ULONG hoff) {
UBYTE data=RAM_PEEK(mLineBaseAddress+(hoff>>1));
// Increment cycle count for the read/modify/write
mCycles+=SPR_RDWR_CYC;
return (hoff&1) ? (data&0xf) : (data>>4);
}
inline void WriteCollision(ULONG hoff,ULONG pixel) {
ULONG col_addr=mLineCollisionAddress+(hoff>>1);
UBYTE dest=RAM_PEEK(col_addr);
if(!(hoff&0x01)) {
// Upper nibble screen write
dest&=0x0f;
dest|=pixel<<4;
} else {
// Lower nibble screen write
dest&=0xf0;
dest|=pixel;
}
RAM_POKE(col_addr,dest);
// Increment cycle count for the read/modify/write
mCycles+=2*SPR_RDWR_CYC;
}
inline ULONG ReadCollision(ULONG hoff) {
UBYTE data=RAM_PEEK(mLineCollisionAddress+(hoff>>1));
// Increment cycle count for the read/modify/write
mCycles+=SPR_RDWR_CYC;
return (hoff&1) ? (data&0xf) : (data>>4);
}
private:
CSystem& mSystem;
ULONG mCycles;
UUWORD mTMPADR; // ENG
UUWORD mTILTACUM; // ENG
UUWORD mHOFF; // CPU
UUWORD mVOFF; // CPU
UUWORD mVIDBAS; // CPU
UUWORD mCOLLBAS; // CPU
UUWORD mVIDADR; // ENG
UUWORD mCOLLADR; // ENG
UUWORD mSCBNEXT; // SCB
UUWORD mSPRDLINE; // SCB
UUWORD mHPOSSTRT; // SCB
UUWORD mVPOSSTRT; // SCB
UUWORD mSPRHSIZ; // SCB
UUWORD mSPRVSIZ; // SCB
UUWORD mSTRETCH; // ENG
UUWORD mTILT; // ENG
UUWORD mSPRDOFF; // ENG
UUWORD mSPRVPOS; // ENG
UUWORD mCOLLOFF; // CPU
UUWORD mVSIZACUM; // ENG
UUWORD mHSIZACUM; // K.s creation
UUWORD mHSIZOFF; // CPU
UUWORD mVSIZOFF; // CPU
UUWORD mSCBADR; // ENG
UUWORD mPROCADR; // ENG
TMATHABCD mMATHABCD; // ENG
TMATHEFGH mMATHEFGH; // ENG
TMATHJKLM mMATHJKLM; // ENG
TMATHNP mMATHNP; // ENG
SLONG mMATHAB_sign;
SLONG mMATHCD_sign;
SLONG mMATHEFGH_sign;
SLONG mSPRCTL0_Type; // SCB
SLONG mSPRCTL0_Vflip;
SLONG mSPRCTL0_Hflip;
SLONG mSPRCTL0_PixelBits;
SLONG mSPRCTL1_StartLeft; // SCB
SLONG mSPRCTL1_StartUp;
SLONG mSPRCTL1_SkipSprite;
SLONG mSPRCTL1_ReloadPalette;
SLONG mSPRCTL1_ReloadDepth;
SLONG mSPRCTL1_Sizing;
SLONG mSPRCTL1_Literal;
SLONG mSPRCOLL_Number; //CPU
SLONG mSPRCOLL_Collide;
SLONG mSPRSYS_StopOnCurrent; //CPU
SLONG mSPRSYS_LeftHand;
SLONG mSPRSYS_VStretch;
SLONG mSPRSYS_NoCollide;
SLONG mSPRSYS_Accumulate;
SLONG mSPRSYS_SignedMath;
SLONG mSPRSYS_Status;
SLONG mSPRSYS_UnsafeAccess;
SLONG mSPRSYS_LastCarry;
SLONG mSPRSYS_Mathbit;
SLONG mSPRSYS_MathInProgress;
ULONG mSUZYBUSEN; // CPU
TSPRINIT mSPRINIT; // CPU
ULONG mSPRGO; // CPU
SLONG mEVERON;
UBYTE mPenIndex[16]; // SCB
// Line rendering related variables
ULONG mLineType;
ULONG mLineShiftRegCount;
ULONG mLineShiftReg;
ULONG mLineRepeatCount;
ULONG mLinePixel;
ULONG mLinePacketBitsLeft;
SLONG mCollision;
UBYTE *mRamPointer;
ULONG mLineBaseAddress;
ULONG mLineCollisionAddress;
// Joystick switches
TJOYSTICK mJOYSTICK;
TSWITCHES mSWITCHES;
};
#endif

Wyświetl plik

@ -0,0 +1,109 @@
// Now render an individual destination line
while(true)
{
ULONG tmp;
if(!mLineRepeatCount)
{
// Normal sprites fetch their counts on a packet basis
if(mLineType!=line_abs_literal)
{
MY_GET_BITS(tmp,1)
if(tmp) mLineType=line_literal; else mLineType=line_packed;
}
// Pixel store is empty what should we do
switch(mLineType)
{
case line_abs_literal:
// This means end of line for us
mLinePixel=LINE_END;
goto EndWhile;
case line_literal:
MY_GET_BITS(mLineRepeatCount,4)
mLineRepeatCount++;
break;
case line_packed:
//
// From reading in between the lines only a packed line with
// a zero size i.e 0b00000 as a header is allowable as a packet end
//
MY_GET_BITS(mLineRepeatCount,4)
if(!mLineRepeatCount)
{
mLinePixel=LINE_END;
mLineRepeatCount++;
goto EndWhile;
}
else
{
MY_GET_BITS(tmp,mSPRCTL0_PixelBits)
pixel=mPenIndex[tmp];
}
mLineRepeatCount++;
break;
default:
pixel = 0;
goto LoopContinue;
}
}
/*
if(pixel==LINE_END)
{
printf("ERROR!\n");
goto EndWhile;
}
*/
mLineRepeatCount--;
switch(mLineType)
{
case line_abs_literal:
MY_GET_BITS(pixel,mSPRCTL0_PixelBits)
// Check the special case of a zero in the last pixel
if(!mLineRepeatCount && !pixel)
{
mLinePixel=LINE_END;
goto EndWhile;
}
else
pixel=mPenIndex[pixel];
break;
case line_literal:
MY_GET_BITS(tmp,mSPRCTL0_PixelBits)
pixel=mPenIndex[tmp];
break;
case line_packed:
break;
default:
pixel=0;
goto LoopContinue;
}
LoopContinue:;
// This is allowed to update every pixel
mHSIZACUM.Word+=mSPRHSIZ.Word;
pixel_width=mHSIZACUM.Byte.High;
mHSIZACUM.Byte.High=0;
for(int hloop=0;hloop<pixel_width;hloop++)
{
// Draw if onscreen but break loop on transition to offscreen
if(hoff>=0 && hoff<HANDY_SCREEN_WIDTH)
{
//ProcessPixel(hoff,pixel);
PROCESS_PIXEL
onscreen=TRUE;
everonscreen=TRUE;
}
else
{
if(onscreen) break;
}
hoff += hsign;
}
}
mLinePixel = pixel;
EndWhile:;

Wyświetl plik

@ -0,0 +1,70 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// Systembase object class definition //
//////////////////////////////////////////////////////////////////////////////
// //
// This header file provides the interface definition for the systembase //
// class that is required to get around cross dependencies between //
// cpu/mikie/system classes //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef SYSBASE_H
#define SYSBASE_H
class CSystemBase
{
// Function members
public:
virtual ~CSystemBase() {};
public:
virtual void Reset(void)=0;
virtual void Poke_CPU(ULONG addr,UBYTE data)=0;
virtual UBYTE Peek_CPU(ULONG addr)=0;
virtual void PokeW_CPU(ULONG addr,UWORD data)=0;
virtual UWORD PeekW_CPU(ULONG addr)=0;
virtual UBYTE* GetRamPointer(void)=0;
#ifdef _LYNXDBG
virtual void DebugTrace(int address)=0;
#endif
};
#endif

Wyświetl plik

@ -0,0 +1,549 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// System object class //
//////////////////////////////////////////////////////////////////////////////
// //
// This class provides the glue to bind of of the emulation objects //
// together via peek/poke handlers and pass thru interfaces to lower //
// objects, all control of the emulator is done via this class. Update() //
// does most of the work and each call emulates one CPU instruction and //
// updates all of the relevant hardware if required. It must be remembered //
// that if that instruction involves setting SPRGO then, it will cause a //
// sprite painting operation and then a corresponding update of all of the //
// hardware which will usually involve recursive calls to Update, see //
// Mikey SPRGO code for more details. //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#include <Arduino.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "system.h"
//
// Define the global variable list
//
ULONG gSystemCycleCount=0;
ULONG gNextTimerEvent=0;
ULONG gCPUWakeupTime=0;
ULONG gIRQEntryCycle=0;
ULONG gCPUBootAddress=0;
ULONG gBreakpointHit=FALSE;
ULONG gSingleStepMode=FALSE;
ULONG gSingleStepModeSprites=FALSE;
ULONG gSystemIRQ=FALSE;
ULONG gSystemNMI=FALSE;
ULONG gSystemCPUSleep=FALSE;
ULONG gSystemCPUSleep_Saved=FALSE;
ULONG gSystemHalt=FALSE;
ULONG gThrottleMaxPercentage=100;
ULONG gThrottleLastTimerCount=0;
ULONG gThrottleNextCycleCheckpoint=0;
ULONG gEndOfFrame=0;
ULONG gTimerCount=0;
ULONG gRenderFrame=1;
ULONG gAudioEnabled=FALSE;
ULONG *gAudioBuffer;
ULONG gAudioBufferPointer=0;
ULONG gAudioLastUpdateCycle=0;
UBYTE *gPrimaryFrameBuffer=NULL;
extern void lynx_decrypt(unsigned char * result, const unsigned char * encrypted, const int length);
PROGMEM static const ULONG crc32Table[256] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
ULONG crc32_le(ULONG crc32, UBYTE const * array, ULONG size)
{
for (ULONG i = 0; i < size; i++)
crc32 = ((crc32 >> 8) & 0x00FFFFFF) ^ crc32Table[(crc32 ^ array[i]) & 0xFF];
return (~crc32);
}
#if 0
int lss_read(void* dest, int varsize, int varcount, LSS_FILE *fp)
{
ULONG copysize;
copysize=varsize*varcount;
if((fp->index + copysize) > fp->index_limit) copysize=fp->index_limit - fp->index;
memcpy(dest,fp->memptr+fp->index,copysize);
fp->index+=copysize;
return copysize;
}
int lss_write(void* src, int varsize, int varcount, LSS_FILE *fp)
{
ULONG copysize;
copysize=varsize*varcount;
//if((fp->index + copysize) > fp->index_limit) copysize=fp->index_limit - fp->index;
memcpy(fp->memptr+fp->index,src,copysize);
fp->index+=copysize;
return copysize;
}
int lss_printf(LSS_FILE *fp, const char *str)
{
ULONG copysize;
copysize=strlen(str);
memcpy(fp->memptr+fp->index,str,copysize);
fp->index+=copysize;
return copysize;
}
#endif
CSystem::CSystem(const char* filename, long displayformat, long samplerate)
: mCart(NULL),
mRam(NULL),
mCpu(NULL),
mMikie(NULL),
mSusie(NULL),
mEEPROM(NULL)
{
UBYTE *filedata = NULL;
ULONG filesize = 0;
FILE *fp;
log_printf("Loading '%s'...\n", filename);
filesize = emu_FileSize((char*)filename);
if (filesize > 0) {
filedata = (UBYTE*)emu_SMalloc(filesize);
emu_LoadFile((char*)filename, (char*)filedata, filesize);
}
/*
if ((fp = fopen(filename, "rb"))) {
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
filedata = (UBYTE*)emu_SMalloc(filesize);
fseek(fp, 0, SEEK_SET);
if (!filedata) {
log_printf("-> memory allocation failed (%d bytes)!\n", filesize);
} else if (fread(filedata, filesize, 1, fp) != 1) {
log_printf("-> fread failed (%d bytes)!\n", filesize);
} else {
log_printf("-> read ok. size=%d, crc32=%08X\n", filesize, crc32_le(0, filedata, filesize));
}
fclose(fp);
}
*/
else {
log_printf("-> fopen failed!\n");
}
// Now try and determine the filetype we have opened
if (!filedata || filesize < 16) {
mFileType = HANDY_FILETYPE_ILLEGAL;
mCart = new CCart(0, 0);
mRam = new CRam(0, 0);
} else if (!memcmp(filedata + 6, "BS93", 4)) {
mFileType = HANDY_FILETYPE_HOMEBREW;
mCart = new CCart(0, 0);
mRam = new CRam(filedata, filesize);
} else {
mFileType = memcmp(filedata, "LYNX", 4) ? HANDY_FILETYPE_RAW : HANDY_FILETYPE_LNX;
mCart = new CCart(filedata, filesize);
mRam = new CRam(0, 0);
// Setup BIOS
memset(mBiosRom, 0x88, sizeof(mBiosRom));
mBiosRom[0x00] = 0x8d;
mBiosRom[0x01] = 0x97;
mBiosRom[0x02] = 0xfd;
mBiosRom[0x03] = 0x60; // RTS
mBiosRom[0x19] = 0x8d;
mBiosRom[0x20] = 0x97;
mBiosRom[0x21] = 0xfd;
mBiosRom[0x4A] = 0x8d;
mBiosRom[0x4B] = 0x97;
mBiosRom[0x4C] = 0xfd;
mBiosRom[0x180] = 0x8d;
mBiosRom[0x181] = 0x97;
mBiosRom[0x182] = 0xfd;
}
// Vectors
mBiosVectors[0] = 0x00;
mBiosVectors[1] = 0x30;
mBiosVectors[2] = 0x80;
mBiosVectors[3] = 0xFF;
mBiosVectors[4] = 0x80;
mBiosVectors[5] = 0xFF;
// Regain some memory before initializing the rest
//emu_SFree(filedata);
mRamPointer = mRam->GetRamPointer();
mMemMapReg = 0x00;
mCycleCountBreakpoint = 0xffffffff;
mpDebugCallback = NULL;
mDebugCallbackObject = 0;
mMikie = new CMikie(*this, displayformat, samplerate);
mSusie = new CSusie(*this);
mCpu = new C65C02(*this);
mEEPROM = new CEEPROM(mCart->CartGetEEPROMType());
// Now init is complete do a reset, this will cause many things to be reset twice
// but what the hell, who cares, I don't.....
Reset();
}
void CSystem::SaveEEPROM(void)
{
if(mEEPROM!=NULL) mEEPROM->Save();
}
CSystem::~CSystem()
{
// Cleanup all our objects
if(mEEPROM!=NULL) delete mEEPROM;
if(mCart!=NULL) delete mCart;
if(mRam!=NULL) delete mRam;
if(mCpu!=NULL) delete mCpu;
if(mMikie!=NULL) delete mMikie;
if(mSusie!=NULL) delete mSusie;
}
void CSystem::HLE_BIOS_FE00(void)
{
// Select Block in A
C6502_REGS regs;
mCpu->GetRegs(regs);
mCart->SetShifterValue(regs.A);
// we just put an RTS behind in fake ROM!
}
void CSystem::HLE_BIOS_FE19(void)
{
// (not) initial jump from reset vector
// Clear full 64k memory!
memset(mRamPointer, 0x00, RAM_SIZE);
// Set Load adresse to $200 ($05,$06)
Poke_CPU(0x0005,0x00);
Poke_CPU(0x0006,0x02);
// Call to $FE00
mCart->SetShifterValue(0);
// Fallthrough $FE4A
HLE_BIOS_FE4A();
}
void CSystem::HLE_BIOS_FE4A(void)
{
UWORD addr=PeekW_CPU(0x0005);
// Load from Cart (loader blocks)
unsigned char buff[256];// maximum 5 blocks
unsigned char res[256];
buff[0]=mCart->Peek0();
int blockcount = 0x100 - buff[0];
for (int i = 1; i < 1+51*blockcount; ++i) { // first encrypted loader
buff[i] = mCart->Peek0();
}
lynx_decrypt(res, buff, 51);
for (int i = 0; i < 50*blockcount; ++i) {
Poke_CPU(addr++, res[i]);
}
// Load Block(s), decode to ($05,$06)
// jmp $200
C6502_REGS regs;
mCpu->GetRegs(regs);
regs.PC=0x0200;
mCpu->SetRegs(regs);
}
void CSystem::HLE_BIOS_FF80(void)
{
// initial jump from reset vector ... calls FE19
HLE_BIOS_FE19();
}
void CSystem::Reset(void)
{
gSystemCycleCount=0;
gNextTimerEvent=0;
gCPUBootAddress=0;
gBreakpointHit=FALSE;
gSingleStepMode=FALSE;
gSingleStepModeSprites=FALSE;
gSystemIRQ=FALSE;
gSystemNMI=FALSE;
gSystemCPUSleep=FALSE;
gSystemHalt=FALSE;
gThrottleLastTimerCount=0;
gThrottleNextCycleCheckpoint=0;
gTimerCount=0;
gAudioBufferPointer=0;
gAudioLastUpdateCycle=0;
#ifdef _LYNXDBG
gSystemHalt=TRUE;
#endif
mCart->Reset();
mEEPROM->Reset();
mRam->Reset();
mMikie->Reset();
mSusie->Reset();
mCpu->Reset();
// Homebrew hashup
if(mFileType==HANDY_FILETYPE_HOMEBREW) {
mMikie->PresetForHomebrew();
C6502_REGS regs;
mCpu->GetRegs(regs);
regs.PC=(UWORD)gCPUBootAddress;
mCpu->SetRegs(regs);
}
}
bool CSystem::ContextSave(LSS_FILE *fp)
{
bool status=1;
// fp->index = 0;
if(!lss_printf(fp, LSS_VERSION)) status=0;
// Save ROM CRC
ULONG checksum=mCart->CRC32();
if(!lss_write(&checksum,sizeof(ULONG),1,fp)) status=0;
if(!lss_printf(fp, "CSystem::ContextSave")) status=0;
if(!lss_write(&mCycleCountBreakpoint,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gSystemCycleCount,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gNextTimerEvent,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gCPUWakeupTime,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gCPUBootAddress,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gIRQEntryCycle,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gBreakpointHit,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gSingleStepMode,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gSystemIRQ,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gSystemNMI,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gSystemCPUSleep,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gSystemCPUSleep_Saved,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gSystemHalt,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gThrottleMaxPercentage,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gThrottleLastTimerCount,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gThrottleNextCycleCheckpoint,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gTimerCount,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&gAudioLastUpdateCycle,sizeof(ULONG),1,fp)) status=0;
if(!lss_write(&mMemMapReg,sizeof(UBYTE),1,fp)) status=0;
// Save other device contexts
if(!mCart->ContextSave(fp)) status=0;
if(!mRam->ContextSave(fp)) status=0;
if(!mMikie->ContextSave(fp)) status=0;
if(!mSusie->ContextSave(fp)) status=0;
if(!mCpu->ContextSave(fp)) status=0;
if(!mEEPROM->ContextSave(fp)) status=0;
return status;
}
bool CSystem::ContextLoad(LSS_FILE *fp)
{
bool status=1;
// fp->index=0;
char teststr[32];
// Check identifier
if(!lss_read(teststr,sizeof(char),4,fp)) status=0;
teststr[4]=0;
if(strcmp(teststr,LSS_VERSION)==0) {
ULONG checksum;
// Read CRC32 and check against the CART for a match
lss_read(&checksum,sizeof(ULONG),1,fp);
if(mCart->CRC32()!=checksum) {
log_printf("CSystem::ContextLoad() LSS Snapshot CRC does not match the loaded cartridge image...\n");
// return 0;
}
// Check our block header
if(!lss_read(teststr,sizeof(char),20,fp)) status=0;
teststr[20]=0;
if(strcmp(teststr,"CSystem::ContextSave")!=0) status=0;
if(!lss_read(&mCycleCountBreakpoint,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gSystemCycleCount,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gNextTimerEvent,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gCPUWakeupTime,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gCPUBootAddress,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gIRQEntryCycle,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gBreakpointHit,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gSingleStepMode,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gSystemIRQ,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gSystemNMI,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gSystemCPUSleep,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gSystemCPUSleep_Saved,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gSystemHalt,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gThrottleMaxPercentage,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gThrottleLastTimerCount,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gThrottleNextCycleCheckpoint,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gTimerCount,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&gAudioLastUpdateCycle,sizeof(ULONG),1,fp)) status=0;
if(!lss_read(&mMemMapReg,sizeof(UBYTE),1,fp)) status=0;
if(!mCart->ContextLoad(fp)) status=0;
if(!mRam->ContextLoad(fp)) status=0;
if(!mMikie->ContextLoad(fp)) status=0;
if(!mSusie->ContextLoad(fp)) status=0;
if(!mCpu->ContextLoad(fp)) status=0;
if(!mEEPROM->ContextLoad(fp)) status=0;
gAudioBufferPointer = 0;
} else {
log_printf("CSystem::ContextLoad() Not a recognised LSS file!\n");
}
return status;
}
#ifdef _LYNXDBG
void CSystem::DebugTrace(int address)
{
char message[1024+1];
int count=0;
log_printf(message,"%08x - DebugTrace(): ",gSystemCycleCount);
count=strlen(message);
if(address) {
if(address==0xffff) {
C6502_REGS regs;
char linetext[1024];
// Register dump
mCpu->GetRegs(regs);
log_printf(linetext,"PC=$%04x SP=$%02x PS=0x%02x A=0x%02x X=0x%02x Y=0x%02x",regs.PC,regs.SP, regs.PS,regs.A,regs.X,regs.Y);
strcat(message,linetext);
count=strlen(message);
} else {
// The RAM address contents should be dumped to an open debug file in this function
do {
message[count++]=Peek_RAM(address);
} while(count<1024 && Peek_RAM(address++)!=0);
}
} else {
strcat(message,"CPU Breakpoint");
count=strlen(message);
}
message[count]=0;
// Callback to dump the message
if(mpDebugCallback) {
(*mpDebugCallback)(mDebugCallbackObject,message);
}
}
void CSystem::DebugSetCallback(void (*function)(ULONG objref,char *message),ULONG objref)
{
mDebugCallbackObject=objref;
mpDebugCallback=function;
}
#endif

Wyświetl plik

@ -0,0 +1,371 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// System object header file //
//////////////////////////////////////////////////////////////////////////////
// //
// This header file provides the interface definition and inline code for //
// the system object, this object if what binds together all of the Handy //
// hardware emulation objects, its the glue that holds the system together //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef SYSTEM_H
#define SYSTEM_H
#include "emuapi.h"
// #pragma inline_depth (255)
// #pragma inline_recursion (on)
#include <cstdint>
typedef int8_t SBYTE;
typedef uint8_t UBYTE;
typedef int16_t SWORD;
typedef uint16_t UWORD;
typedef int32_t SLONG;
typedef uint32_t ULONG;
#ifdef HAS_T4_VGA
typedef UBYTE HandyPixel;
#else
typedef UWORD HandyPixel;
#endif
extern ULONG crc32_le(ULONG crc, UBYTE const * buf, ULONG len);
#ifndef TRUE
#define TRUE true
#endif
#ifndef FALSE
#define FALSE false
#endif
#define HANDY_SYSTEM_FREQ 16000000
#define HANDY_TIMER_FREQ 20
#define HANDY_AUDIO_SAMPLE_FREQ 22050 //22050 //24000 // 48000
#define HANDY_AUDIO_SAMPLE_PERIOD (HANDY_SYSTEM_FREQ/HANDY_AUDIO_SAMPLE_FREQ)
#define AUDIO_BUFFER_LENGTH 4096 //512 //((HANDY_AUDIO_SAMPLE_FREQ/60+25)*2)
#ifdef SDL_PATCH
//#define HANDY_AUDIO_BUFFER_SIZE 4096 // Needed for SDL 8bit MONO
//#define HANDY_AUDIO_BUFFER_SIZE 8192 // Needed for SDL STEREO 8bit
//#define HANDY_AUDIO_BUFFER_SIZE 16384 // Needed for SDL STEREO 16bit
#else
//#define HANDY_AUDIO_BUFFER_SIZE (HANDY_AUDIO_SAMPLE_FREQ/4)
//#define HANDY_AUDIO_BUFFER_SIZE (HANDY_AUDIO_SAMPLE_FREQ)
#endif
#define HANDY_FILETYPE_LNX 0
#define HANDY_FILETYPE_HOMEBREW 1
#define HANDY_FILETYPE_SNAPSHOT 2
#define HANDY_FILETYPE_ILLEGAL 3
#define HANDY_FILETYPE_RAW 4
#define HANDY_SCREEN_WIDTH 160
#define HANDY_SCREEN_STRIDE emu_LineStride()
#define HANDY_SCREEN_HEIGHT 102
//
// Define the global variable list
//
extern ULONG gSystemCycleCount;
extern ULONG gNextTimerEvent;
extern ULONG gCPUWakeupTime;
extern ULONG gIRQEntryCycle;
extern ULONG gCPUBootAddress;
extern ULONG gBreakpointHit;
extern ULONG gSingleStepMode;
extern ULONG gSingleStepModeSprites;
extern ULONG gSystemIRQ;
extern ULONG gSystemNMI;
extern ULONG gSystemCPUSleep;
extern ULONG gSystemCPUSleep_Saved;
extern ULONG gSystemHalt;
extern ULONG gThrottleMaxPercentage;
extern ULONG gThrottleLastTimerCount;
extern ULONG gThrottleNextCycleCheckpoint;
extern ULONG gEndOfFrame;
extern ULONG gTimerCount;
extern ULONG gRenderFrame;
extern ULONG gAudioEnabled;
extern ULONG *gAudioBuffer;
extern ULONG gAudioBufferPointer;
extern ULONG gAudioLastUpdateCycle;
extern UBYTE *gPrimaryFrameBuffer;
// typedef struct lssfile
// {
// UBYTE *memptr;
// ULONG index;
// ULONG index_limit;
// } LSS_FILE;
// int lss_read(void* dest, int varsize, int varcount, LSS_FILE *fp);
// int lss_write(void* src, int varsize, int varcount, LSS_FILE *fp);
// int lss_printf(LSS_FILE *fp, const char *str);
#define LSS_FILE FILE
#define lss_read(d, vs, vc, fp) (fread(d, vs, vc, fp) > 0)
#define lss_write(s, vs, vc, fp) (fwrite(s, vs, vc, fp) > 0)
#define lss_printf(fp, str) (fputs(str, fp) >= 0)
//
// Define logging functions
//
//#include <rg_system.h>
//#define log_printf(x...) printf(x)
//#define log_printf(x...) rg_system_log(RG_LOG_USER, NULL, x)
#define log_printf(x...)
//
// Define the interfaces before we start pulling in the classes
// as many classes look for articles from the interfaces to
// allow compilation
#include "sysbase.h"
class CSystem;
//
// Now pull in the parts that build the system
//
#include "lynxbase.h"
#include "ram.h"
#include "cart.h"
#include "eeprom.h"
#include "susie.h"
#include "mikie.h"
#include "c65c02.h"
#define TOP_START 0xfc00
#define TOP_MASK 0x03ff
#define TOP_SIZE 0x400
#define SYSTEM_SIZE 65536
#define LSS_VERSION "LSS3"
class CSystem : public CSystemBase
{
public:
CSystem(const char* gamefile, long displayformat, long samplerate);
~CSystem();
public:
void HLE_BIOS_FE00(void);
void HLE_BIOS_FE19(void);
void HLE_BIOS_FE4A(void);
void HLE_BIOS_FF80(void);
void Reset(void);
bool ContextSave(LSS_FILE *fp);
bool ContextLoad(LSS_FILE *fp);
void SaveEEPROM(void);
inline void Update(void)
{
//
// Only update if there is a predicted timer event
//
if(gSystemCycleCount>=gNextTimerEvent)
{
mMikie->Update();
}
//
// Step the processor through 1 instruction
//
mCpu->Update();
#ifdef _LYNXDBG
// Check breakpoint
static ULONG lastcycle=0;
if(lastcycle<mCycleCountBreakpoint && gSystemCycleCount>=mCycleCountBreakpoint) gBreakpointHit=TRUE;
lastcycle=gSystemCycleCount;
// Check single step mode
if(gSingleStepMode) gBreakpointHit=TRUE;
#endif
//
// If the CPU is asleep then skip to the next timer event
//
if(gSystemCPUSleep)
{
gSystemCycleCount=gNextTimerEvent;
}
}
inline void UpdateFrame(bool draw)
{
gEndOfFrame = FALSE;
gRenderFrame = draw;
while(gEndOfFrame != TRUE)
{
if(gSystemCycleCount>=gNextTimerEvent)
{
mMikie->Update();
}
mCpu->Update();
#ifdef _LYNXDBG
// Check breakpoint
static ULONG lastcycle=0;
if(lastcycle<mCycleCountBreakpoint && gSystemCycleCount>=mCycleCountBreakpoint) gBreakpointHit=TRUE;
lastcycle=gSystemCycleCount;
// Check single step mode
if(gSingleStepMode) gBreakpointHit=TRUE;
#endif
if(gSystemCPUSleep)
{
gSystemCycleCount=gNextTimerEvent;
}
}
}
//
// CPU
//
inline void Poke_CPU(ULONG addr, UBYTE data) {
if (addr < 0xFC00) { // 0000-FBFF Always RAM
// mRamPointer[addr] = data;
// return;
}
else if (addr == 0xFFF9) { // MMU
mMemMapReg = data;
return;
}
else if ((addr >> 8) == 0xFC) { // FC00-FCFF Susie area
if ((mMemMapReg & 0x1) == 0) {
mSusie->Poke(addr, data);
return;
}
}
else if ((addr >> 8) == 0xFD) { // FD00-FDFF Mikie area
if ((mMemMapReg & 0x2) == 0) {
mMikie->Poke(addr, data);
return;
}
}
else if (addr < 0xFFF8) { // FE00-FFF7 Bios ROM
if ((mMemMapReg & 0x4) == 0)
return;
}
else { // FFFA-FFFF Vector area
if ((mMemMapReg & 0x8) == 0)
return;
}
mRamPointer[addr] = data;
};
inline UBYTE Peek_CPU(ULONG addr) {
if (addr < 0xFC00) { // 0000-FBFF Always RAM
// return mRamPointer[addr];
}
else if (addr == 0xFFF9) {
return mMemMapReg;
}
else if ((addr >> 8) == 0xFC) { // FC00-FCFF Susie area
if ((mMemMapReg & 0x1) == 0)
return mSusie->Peek(addr);
}
else if ((addr >> 8) == 0xFD) { // FD00-FDFF Mikie area
if ((mMemMapReg & 0x2) == 0)
return mMikie->Peek(addr);
}
else if (addr < 0xFFF8) { // FE00-FFF7 Bios ROM
if ((mMemMapReg & 0x4) == 0)
return mBiosRom[addr & 0x1FF];
}
else { // FFFA-FFFF Vector area
if ((mMemMapReg & 0x8) == 0)
return mBiosVectors[addr - 0xFFFA];
}
return mRamPointer[addr];
};
inline void PokeW_CPU(ULONG addr,UWORD data) { Poke_CPU(addr, data&0xff); Poke_CPU(addr + 1, data >> 8); };
inline UWORD PeekW_CPU(ULONG addr) { return ((Peek_CPU(addr))+(Peek_CPU(addr+1)<<8)); };
// Mikey system interfacing
void ComLynxCable(int status) { mMikie->ComLynxCable(status); };
void ComLynxRxData(int data) { mMikie->ComLynxRxData(data); };
void ComLynxTxCallback(void (*function)(int data,ULONG objref),ULONG objref) { mMikie->ComLynxTxCallback(function,objref); };
// Miscellaneous
void SetButtonData(ULONG data) {mSusie->SetButtonData(data);};
ULONG GetButtonData(void) {return mSusie->GetButtonData();};
void SetCycleBreakpoint(ULONG breakpoint) {mCycleCountBreakpoint=breakpoint;};
UBYTE* GetRamPointer(void) {return mRam->GetRamPointer();};
// Debugging
void DebugTrace(int address);
void DebugSetCallback(void (*function)(ULONG objref, char *message),ULONG objref);
void (*mpDebugCallback)(ULONG objref, char *message);
ULONG mDebugCallbackObject;
public:
ULONG mCycleCountBreakpoint;
CCart *mCart;
CRam *mRam;
C65C02 *mCpu;
CMikie *mMikie;
CSusie *mSusie;
CEEPROM *mEEPROM;
ULONG mFileType;
UBYTE mMemMapReg;
UBYTE *mRamPointer;
UBYTE mBiosVectors[0x8];
UBYTE mBiosRom[0x200];
};
#endif

Wyświetl plik

@ -0,0 +1,211 @@
extern "C" {
#include "emuapi.h"
#include "iopins.h"
}
#include "lynx.h"
#ifdef HAS_T4_VGA
#include "vga_t_dma.h"
TFT_T_DMA tft;
#else
#include "tft_t_dma.h"
TFT_T_DMA tft = TFT_T_DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO, TFT_TOUCH_CS, TFT_TOUCH_INT);
#endif
bool vgaMode = false;
static unsigned char palette8[PALETTE_SIZE];
static unsigned short palette16[PALETTE_SIZE];
static IntervalTimer myTimer;
volatile boolean vbl=true;
static int skip=0;
static elapsedMicros tius;
static int fbstride;
static int fbwidth;
static int fbheight;
static void vblCount() {
if (vbl) {
vbl = false;
} else {
vbl = true;
}
}
void emu_SetPaletteEntry(unsigned char r, unsigned char g, unsigned char b, int index)
{
if (index<PALETTE_SIZE) {
//Serial.println("%d: %d %d %d\n", index, r,g,b);
palette8[index] = RGBVAL8(r,g,b);
palette16[index] = RGBVAL16(r,g,b);
}
}
void emu_DrawVsync(void)
{
volatile boolean vb=vbl;
skip += 1;
skip &= VID_FRAME_SKIP;
if (!vgaMode) {
#ifdef HAS_T4_VGA
while (vbl==vb) {};
#else
while (vbl==vb) {};
#endif
}
}
void emu_DrawLine(unsigned char * VBuf, int width, int height, int line)
{
if (!vgaMode) {
#ifdef HAS_T4_VGA
tft.writeLine(width,1,line, VBuf, palette8);
#else
tft.writeLine(width,1,line, VBuf, palette16);
#endif
}
}
void emu_DrawLine8(unsigned char * VBuf, int width, int height, int line)
{
if (!vgaMode) {
if (skip==0) {
#ifdef HAS_T4_VGA
tft.writeLine(width,height,line, VBuf);
#endif
}
}
}
void emu_DrawLine16(unsigned short * VBuf, int width, int height, int line)
{
if (!vgaMode) {
if (skip==0) {
#ifdef HAS_T4_VGA
tft.writeLine16(width,height,line, VBuf);
#else
tft.writeLine(width,height,line, VBuf);
#endif
}
}
}
void emu_DrawScreen(unsigned char * VBuf, int width, int height, int stride)
{
if (!vgaMode) {
if (skip==0) {
#ifdef HAS_T4_VGA
tft.writeScreen(width,height-TFT_VBUFFER_YCROP,stride, VBuf+(TFT_VBUFFER_YCROP/2)*stride, palette8);
#else
tft.writeScreen(width,height-TFT_VBUFFER_YCROP,stride, VBuf+(TFT_VBUFFER_YCROP/2)*stride, palette16);
#endif
}
}
}
int emu_FrameSkip(void)
{
return skip;
}
void * emu_LineBuffer(int line)
{
if (!vgaMode) {
return (void*)tft.getLineBuffer(line);
}
}
int emu_LineStride(void)
{
return fbstride;
}
int emu_LineWidth(void)
{
return fbwidth;
}
// ****************************************************
// the setup() method runs once, when the sketch starts
// ****************************************************
void setup() {
#ifdef HAS_T4_VGA
tft.begin(VGA_MODE_320x240);
// NVIC_SET_PRIORITY(IRQ_QTIMER3, 0);
#else
tft.begin();
#endif
tft.get_frame_buffer_size(&fbwidth, &fbheight, &fbstride);
emu_init();
}
// ****************************************************
// the loop() method runs continuously
// ****************************************************
void loop(void)
{
if (menuActive()) {
uint16_t bClick = emu_DebounceLocalKeys();
int action = handleMenu(bClick);
char * filename = menuSelection();
if (action == ACTION_RUN1) {
toggleMenu(false);
vgaMode = false;
emu_start();
tft.fillScreenNoDma( RGBVAL16(0x00,0x00,0x00) );
tft.startDMA();
emu_Init(filename);
myTimer.begin(vblCount, 12000/*16666*/);
}
delay(20);
}
else {
uint16_t bClick = emu_DebounceLocalKeys();
emu_Input(bClick);
emu_Step();
//uint16_t bClick = emu_DebounceLocalKeys();
//if (bClick & MASK_KEY_USER1) {
// emu_Input(bClick);
//}
}
}
#ifdef HAS_SND
#include "AudioPlaySystem.h"
AudioPlaySystem mymixer;
void emu_sndInit() {
Serial.println("sound init");
mymixer.begin_audio(256, mymixer.snd_Mixer);
mymixer.start();
}
void emu_sndPlaySound(int chan, int volume, int freq)
{
if (chan < 6) {
mymixer.sound(chan, freq, volume);
}
/*
Serial.print(chan);
Serial.print(":" );
Serial.print(volume);
Serial.print(":" );
Serial.println(freq);
*/
}
void emu_sndPlayBuzz(int size, int val) {
mymixer.buzz(size,val);
//Serial.print((val==1)?1:0);
//Serial.print(":");
//Serial.println(size);
}
#endif

Wyświetl plik

@ -0,0 +1,232 @@
/*
Based on C64 ILI9341 dma driver from Frank Bösing, 2017
*/
#ifndef _TFT_T_DMAH_
#define _TFT_T_DMAH_
#ifdef __cplusplus
#include <Arduino.h>
#include <SPI.h>
#include <DMAChannel.h>
#endif
#include "tft_t_dma_config.h"
#define RGBVAL32(r,g,b) ( (r<<16) | (g<<8) | b )
#define RGBVAL16(r,g,b) ( (((r>>3)&0x1f)<<11) | (((g>>2)&0x3f)<<5) | (((b>>3)&0x1f)<<0) )
#define RGBVAL8(r,g,b) ( (((r>>5)&0x07)<<5) | (((g>>5)&0x07)<<2) | (((b>>6)&0x3)<<0) )
#define R16(rgb) ((rgb>>8)&0xf8)
#define G16(rgb) ((rgb>>3)&0xfc)
#define B16(rgb) ((rgb<<3)&0xf8)
#define PAL_COLOR_MASK 0xff
#ifdef LOHRES
#define TFT_WIDTH 240
#define TFT_REALWIDTH 240
#else
#define TFT_WIDTH 320
#define TFT_REALWIDTH 320
#endif
#define TFT_HEIGHT 240
#define TFT_REALHEIGHT 240
//#define WIDTH 272
//#define HEIGHT 228
#define LINES_PER_BLOCK 64
#define NR_OF_BLOCK 4
#define SCREEN_DMA_NUM_SETTINGS NR_OF_BLOCK
#ifdef ILI9341
#define ILI9341_NOP 0x00
#define ILI9341_SWRESET 0x01
#define ILI9341_RDDID 0x04
#define ILI9341_RDDST 0x09
#define ILI9341_SLPIN 0x10
#define ILI9341_SLPOUT 0x11
#define ILI9341_PTLON 0x12
#define ILI9341_NORON 0x13
#define ILI9341_RDMODE 0x0A
#define ILI9341_RDMADCTL 0x0B
#define ILI9341_RDPIXFMT 0x0C
#define ILI9341_RDIMGFMT 0x0D
#define ILI9341_RDSELFDIAG 0x0F
#define ILI9341_INVOFF 0x20
#define ILI9341_INVON 0x21
#define ILI9341_GAMMASET 0x26
#define ILI9341_DISPOFF 0x28
#define ILI9341_DISPON 0x29
#define ILI9341_CASET 0x2A
#define ILI9341_PASET 0x2B
#define ILI9341_RAMWR 0x2C
#define ILI9341_RAMRD 0x2E
#define ILI9341_PTLAR 0x30
#define ILI9341_MADCTL 0x36
#define ILI9341_VSCRSADD 0x37
#define ILI9341_PIXFMT 0x3A
#define ILI9341_FRMCTR1 0xB1
#define ILI9341_FRMCTR2 0xB2
#define ILI9341_FRMCTR3 0xB3
#define ILI9341_INVCTR 0xB4
#define ILI9341_DFUNCTR 0xB6
#define ILI9341_PWCTR1 0xC0
#define ILI9341_PWCTR2 0xC1
#define ILI9341_PWCTR3 0xC2
#define ILI9341_PWCTR4 0xC3
#define ILI9341_PWCTR5 0xC4
#define ILI9341_VMCTR1 0xC5
#define ILI9341_VMCTR2 0xC7
#define ILI9341_RDID1 0xDA
#define ILI9341_RDID2 0xDB
#define ILI9341_RDID3 0xDC
#define ILI9341_RDID4 0xDD
#define ILI9341_GMCTRP1 0xE0
#define ILI9341_GMCTRN1 0xE1
#define ILI9341_MADCTL_MY 0x80
#define ILI9341_MADCTL_MX 0x40
#define ILI9341_MADCTL_MV 0x20
#define ILI9341_MADCTL_ML 0x10
#define ILI9341_MADCTL_RGB 0x00
#define ILI9341_MADCTL_BGR 0x08
#define ILI9341_MADCTL_MH 0x04
#define TFT_CASET ILI9341_CASET
#define TFT_PASET ILI9341_PASET
#define TFT_RAMWR ILI9341_RAMWR
#define TFT_MADCTL ILI9341_MADCTL
#endif
#ifdef ST7789
#define ST7735_NOP 0x00
#define ST7735_SWRESET 0x01
#define ST7735_RDDID 0x04
#define ST7735_RDDST 0x09
#define ST7735_SLPIN 0x10
#define ST7735_SLPOUT 0x11
#define ST7735_PTLON 0x12
#define ST7735_NORON 0x13
#define ST7735_INVOFF 0x20
#define ST7735_INVON 0x21
#define ST7735_DISPOFF 0x28
#define ST7735_DISPON 0x29
#define ST7735_CASET 0x2A
#define ST7735_RASET 0x2B
#define ST7735_RAMWR 0x2C
#define ST7735_RAMRD 0x2E
#define ST7735_PTLAR 0x30
#define ST7735_COLMOD 0x3A
#define ST7735_MADCTL 0x36
#define ST7735_FRMCTR1 0xB1
#define ST7735_FRMCTR2 0xB2
#define ST7735_FRMCTR3 0xB3
#define ST7735_INVCTR 0xB4
#define ST7735_DISSET5 0xB6
#define ST7735_PWCTR1 0xC0
#define ST7735_PWCTR2 0xC1
#define ST7735_PWCTR3 0xC2
#define ST7735_PWCTR4 0xC3
#define ST7735_PWCTR5 0xC4
#define ST7735_VMCTR1 0xC5
#define ST7735_RDID1 0xDA
#define ST7735_RDID2 0xDB
#define ST7735_RDID3 0xDC
#define ST7735_RDID4 0xDD
#define ST7735_PWCTR6 0xFC
#define ST7735_GMCTRP1 0xE0
#define ST7735_GMCTRN1 0xE1
#define ST77XX_MADCTL_MY 0x80
#define ST77XX_MADCTL_MX 0x40
#define ST77XX_MADCTL_MV 0x20
#define ST77XX_MADCTL_ML 0x10
#define ST77XX_MADCTL_RGB 0x00
#define ST77XX_MADCTL_BGR 0x08
#define ST77XX_MADCTL_MH 0x04
#define TFT_CASET ST7735_CASET
#define TFT_PASET ST7735_RASET
#define TFT_RAMWR ST7735_RAMWR
#define TFT_MADCTL ST7735_MADCTL
#endif
#ifdef __cplusplus
class TFT_T_DMA
{
public:
TFT_T_DMA(uint8_t _CS, uint8_t _DC, uint8_t _RST = 255, uint8_t _MOSI=11, uint8_t _SCLK=13, uint8_t _MISO=12, uint8_t touch_cs=38, uint8_t touch_irq=37);
void setArea(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2);
void begin(void);
void flipscreen(bool flip);
boolean isflipped(void);
void startDMA(void);
void stopDMA();
int get_frame_buffer_size(int *width, int *height, int *stride);
// Touch screen functions
#define TOUCH_ENABLED() ((_touch_cs != 255) && (_touch_irq != 255))
bool isTouching(void) { return ((!TOUCH_ENABLED())?false:(digitalRead(_touch_irq) == LOW)); }
void readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ);
void readCal(uint16_t * oX, uint16_t * oY, uint16_t * oZ);
void callibrateTouch(uint16_t xMin,uint16_t yMin,uint16_t xMax,uint16_t yMax);
// NoDMA functions
void writeScreenNoDma(const uint16_t *pcolors);
void fillScreenNoDma(uint16_t color);
void drawTextNoDma(int16_t x, int16_t y, const char * text, uint16_t fgcolor, uint16_t bgcolor, bool doublesize);
void drawRectNoDma(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap);
void drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t croparx, uint16_t cropary, uint16_t croparw, uint16_t croparh);
// DMA functions
uint16_t * getLineBuffer(int j);
void writeScreen(int width, int height, int stride, uint8_t *buffer, uint16_t *palette16);
void writeLine(int width, int height, int stride, uint8_t *buffer, uint16_t *palette16);
void writeLine(int width, int height, int y, uint16_t *buf);
void fillScreen(uint16_t color);
void drawText(int16_t x, int16_t y, const char * text, uint16_t fgcolor, uint16_t bgcolor, bool doublesize);
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void drawSprite(int16_t x, int16_t y, const uint16_t *bitmap);
void drawSprite(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t croparx, uint16_t cropary, uint16_t croparw, uint16_t croparh);
protected:
uint8_t _rst, _cs, _dc;
uint8_t _miso, _mosi, _sclk;
uint8_t _touch_irq=255, _touch_cs=255;
bool flipped=false;
void wait(void);
void enableTouchIrq();
};
#endif
#endif

Wyświetl plik

@ -0,0 +1,14 @@
#include "platform_config.h"
//#define ST7789 1
//#define ILI9341 1
#define TFT_LINEARINT 1
#define LINEARINT_HACK 1
//#define FLIP_SCREEN 1
//#define TFT_DEBUG 1
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
//#define TFT_STATICFB 1
#endif

Wyświetl plik

@ -0,0 +1,53 @@
/*
Wrapping class to extend VGA_T4 to TFT_T_DMA
*/
#ifndef _VGA_T_DMAH_
#define _VGA_T_DMAH_
#ifdef __cplusplus
#include <VGA_t4.h>
#endif
#define RGBVAL16(r,g,b) ( (((r>>5)&0x07)<<5) | (((g>>5)&0x07)<<2) | (((b>>6)&0x3)<<0) )
#define RGBVAL8(r,g,b) ( (((r>>5)&0x07)<<5) | (((g>>5)&0x07)<<2) | (((b>>6)&0x3)<<0) )
#define TFT_WIDTH 320
#define TFT_REALWIDTH 320
#define TFT_HEIGHT 240
#define TFT_REALHEIGHT 240
#ifdef __cplusplus
class TFT_T_DMA: public VGA_T4
{
public:
// Fake touch screen functions
bool isTouching(void) { return false; }
void readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { }
void readCal(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { };
void callibrateTouch(uint16_t xMin,uint16_t yMin,uint16_t xMax,uint16_t yMax) { }
// fake DMA functions
void startDMA(void) { };
void stopDMA(void) { };
// fake no DMA functions
void writeScreenNoDma(const vga_pixel *pcolors) { writeScreen(pcolors); }
void fillScreenNoDma(vga_pixel color) { clear(color); }
void drawTextNoDma(int16_t x, int16_t y, const char * text, vga_pixel fgcolor, vga_pixel bgcolor, bool doublesize) { drawText(x,y,text,fgcolor,bgcolor,doublesize); }
void drawRectNoDma(int16_t x, int16_t y, int16_t w, int16_t h, vga_pixel color) { drawRect(x, y, w, h, color); }
void drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap) { drawSprite(x, y, bitmap); }
void drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t croparx, uint16_t cropary, uint16_t croparw, uint16_t croparh) { drawSprite(x, y, bitmap, croparx, cropary, croparw, croparh); }
};
#endif
#endif

Wyświetl plik

@ -0,0 +1,564 @@
// Core SPC emulation: CPU, timers, SMP registers, memory
// snes_spc 0.9.0. http://www.slack.net/~ant/
#include "SNES_SPC.h"
#include <string.h>
/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
#define RAM (m.ram.ram)
#define REGS (m.smp_regs [0])
#define REGS_IN (m.smp_regs [1])
// (n ? n : 256)
#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which
// do crazy echo buffer accesses.
#ifndef SPC_MORE_ACCURACY
#define SPC_MORE_ACCURACY 0
#endif
#ifdef BLARGG_ENABLE_OPTIMIZER
#include BLARGG_ENABLE_OPTIMIZER
#endif
//// Timers
#if SPC_DISABLE_TEMPO
#define TIMER_DIV( t, n ) ((n) >> t->prescaler)
#define TIMER_MUL( t, n ) ((n) << t->prescaler)
#else
#define TIMER_DIV( t, n ) ((n) / t->prescaler)
#define TIMER_MUL( t, n ) ((n) * t->prescaler)
#endif
SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time )
{
int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
t->next_time += TIMER_MUL( t, elapsed );
if ( t->enabled )
{
int remain = IF_0_THEN_256( t->period - t->divider );
int divider = t->divider + elapsed;
int over = elapsed - remain;
if ( over >= 0 )
{
int n = over / t->period;
t->counter = (t->counter + 1 + n) & 0x0F;
divider = over - n * t->period;
}
t->divider = (uint8_t) divider;
}
return t;
}
inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time )
{
if ( time >= t->next_time )
t = run_timer_( t, time );
return t;
}
//// ROM
void SNES_SPC::enable_rom( int enable )
{
if ( m.rom_enabled != enable )
{
m.rom_enabled = enable;
if ( enable )
memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram );
memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size );
// TODO: ROM can still get overwritten when DSP writes to echo buffer
}
}
//// DSP
#if SPC_LESS_ACCURATE
int const max_reg_time = 29;
signed char const SNES_SPC::reg_times_ [256] =
{
-1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22,
2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23,
5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23,
8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24,
11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24,
14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24,
17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25,
20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
};
#define RUN_DSP( time, offset ) \
int count = (time) - (offset) - m.dsp_time;\
if ( count >= 0 )\
{\
int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\
m.dsp_time += clock_count;\
dsp.run( clock_count );\
}
#else
#define RUN_DSP( time, offset ) \
{\
int count = (time) - m.dsp_time;\
if ( !SPC_MORE_ACCURACY || count )\
{\
assert( count > 0 );\
m.dsp_time = (time);\
dsp.run( count );\
}\
}
#endif
int SNES_SPC::dsp_read( rel_time_t time )
{
RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
int result = dsp.read( REGS [r_dspaddr] & 0x7F );
#ifdef SPC_DSP_READ_HOOK
SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
#endif
return result;
}
inline void SNES_SPC::dsp_write( int data, rel_time_t time )
{
RUN_DSP( time, reg_times [REGS [r_dspaddr]] )
#if SPC_LESS_ACCURATE
else if ( m.dsp_time == skipping_time )
{
int r = REGS [r_dspaddr];
if ( r == SPC_DSP::r_kon )
m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff );
if ( r == SPC_DSP::r_koff )
{
m.skipped_koff |= data;
m.skipped_kon &= ~data;
}
}
#endif
#ifdef SPC_DSP_WRITE_HOOK
SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
#endif
if ( REGS [r_dspaddr] <= 0x7F )
dsp.write( REGS [r_dspaddr], data );
else if ( !SPC_MORE_ACCURACY )
dprintf( "SPC wrote to DSP register > $7F\n" );
}
//// Memory access extras
#if SPC_MORE_ACCURACY
#define MEM_ACCESS( time, addr ) \
{\
if ( time >= m.dsp_time )\
{\
RUN_DSP( time, max_reg_time );\
}\
}
#elif !defined (NDEBUG)
// Debug-only check for read/write within echo buffer, since this might result in
// inaccurate emulation due to the DSP not being caught up to the present.
bool SNES_SPC::check_echo_access( int addr )
{
if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
{
int start = 0x100 * dsp.read( SPC_DSP::r_esa );
int size = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
int end = start + (size ? size : 4);
if ( start <= addr && addr < end )
{
if ( !m.echo_accessed )
{
m.echo_accessed = 1;
return true;
}
}
}
return false;
}
#define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
#else
#define MEM_ACCESS( time, addr )
#endif
//// CPU write
#if SPC_MORE_ACCURACY
static unsigned char const glitch_probs [3] [256] =
{
0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02,
0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01,
0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05,
0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01,
0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01,
0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04,
0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02,
0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06,
0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03,
0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02,
0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03,
};
#endif
// divided into multiple functions to keep rarely-used functionality separate
// so often-used functionality can be optimized better by compiler
// If write isn't preceded by read, data has this added to it
int const no_read_before_write = 0x2000;
void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr )
{
switch ( addr )
{
case r_t0target:
case r_t1target:
case r_t2target: {
Timer* t = &m.timers [addr - r_t0target];
int period = IF_0_THEN_256( data );
if ( t->period != period )
{
t = run_timer( t, time );
#if SPC_MORE_ACCURACY
// Insane behavior when target is written just after counter is
// clocked and counter matches new period and new period isn't 1, 2, 4, or 8
if ( t->divider == (period & 0xFF) &&
t->next_time == time + TIMER_MUL( t, 1 ) &&
((period - 1) | ~0x0F) & period )
{
//dprintf( "SPC pathological timer target write\n" );
// If the period is 3, 5, or 9, there's a probability this behavior won't occur,
// based on the previous period
int prob = 0xFF;
int old_period = t->period & 0xFF;
if ( period == 3 ) prob = glitch_probs [0] [old_period];
if ( period == 5 ) prob = glitch_probs [1] [old_period];
if ( period == 9 ) prob = glitch_probs [2] [old_period];
// The glitch suppresses incrementing of one of the counter bits, based on
// the lowest set bit in the new period
int b = 1;
while ( !(period & b) )
b <<= 1;
if ( (rand() >> 4 & 0xFF) <= prob )
t->divider = (t->divider - b) & 0xFF;
}
#endif
t->period = period;
}
break;
}
case r_t0out:
case r_t1out:
case r_t2out:
if ( !SPC_MORE_ACCURACY )
dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
if ( data < no_read_before_write / 2 )
run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
break;
// Registers that act like RAM
case 0x8:
case 0x9:
REGS_IN [addr] = (uint8_t) data;
break;
case r_test:
if ( (uint8_t) data != 0x0A )
dprintf( "SPC wrote to test register\n" );
break;
case r_control:
// port clears
if ( data & 0x10 )
{
REGS_IN [r_cpuio0] = 0;
REGS_IN [r_cpuio1] = 0;
}
if ( data & 0x20 )
{
REGS_IN [r_cpuio2] = 0;
REGS_IN [r_cpuio3] = 0;
}
// timers
{
for ( int i = 0; i < timer_count; i++ )
{
Timer* t = &m.timers [i];
int enabled = data >> i & 1;
if ( t->enabled != enabled )
{
t = run_timer( t, time );
t->enabled = enabled;
if ( enabled )
{
t->divider = 0;
t->counter = 0;
}
}
}
}
enable_rom( data & 0x80 );
break;
}
}
void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr )
{
if ( addr == r_dspdata ) // 99%
dsp_write( data, time );
else
cpu_write_smp_reg_( data, time, addr );
}
void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time )
{
if ( i < rom_size )
{
m.hi_ram [i] = (uint8_t) data;
if ( m.rom_enabled )
RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
}
else
{
assert( *(&(RAM [0]) + i + rom_addr) == (uint8_t) data );
*(&(RAM [0]) + i + rom_addr) = cpu_pad_fill; // restore overwritten padding
cpu_write( data, i + rom_addr - 0x10000, time );
}
}
int const bits_in_int = CHAR_BIT * sizeof (int);
void SNES_SPC::cpu_write( int data, int addr, rel_time_t time )
{
MEM_ACCESS( time, addr )
// RAM
RAM [addr] = (uint8_t) data;
int reg = addr - 0xF0;
if ( reg >= 0 ) // 64%
{
// $F0-$FF
if ( reg < reg_count ) // 87%
{
REGS [reg] = (uint8_t) data;
// Ports
#ifdef SPC_PORT_WRITE_HOOK
if ( (unsigned) (reg - r_cpuio0) < port_count )
SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
(uint8_t) data, &REGS [r_cpuio0] );
#endif
// Registers other than $F2 and $F4-$F7
//if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
// TODO: this is a bit on the fragile side
if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
cpu_write_smp_reg( data, time, reg );
}
// High mem/address wrap-around
else
{
reg -= rom_addr - 0xF0;
if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
cpu_write_high( data, reg, time );
}
}
}
//// CPU read
inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time )
{
int result = REGS_IN [reg];
reg -= r_dspaddr;
// DSP addr and data
if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3
{
result = REGS [r_dspaddr];
if ( (unsigned) reg == 1 )
result = dsp_read( time ); // 0xF3
}
return result;
}
int SNES_SPC::cpu_read( int addr, rel_time_t time )
{
MEM_ACCESS( time, addr )
// RAM
int result = RAM [addr];
int reg = addr - 0xF0;
if ( reg >= 0 ) // 40%
{
reg -= 0x10;
if ( (unsigned) reg >= 0xFF00 ) // 21%
{
reg += 0x10 - r_t0out;
// Timers
if ( (unsigned) reg < timer_count ) // 90%
{
Timer* t = &m.timers [reg];
if ( time >= t->next_time )
t = run_timer_( t, time );
result = t->counter;
t->counter = 0;
}
// Other registers
else if ( reg < 0 ) // 10%
{
result = cpu_read_smp_reg( reg + r_t0out, time );
}
else // 1%
{
assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
}
}
}
return result;
}
//// Run
// Prefix and suffix for CPU emulator function
#define SPC_CPU_RUN_FUNC \
BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\
{\
rel_time_t rel_time = m.spc_time - end_time;\
assert( rel_time <= 0 );\
m.spc_time = end_time;\
m.dsp_time += rel_time;\
m.timers [0].next_time += rel_time;\
m.timers [1].next_time += rel_time;\
m.timers [2].next_time += rel_time;
#define SPC_CPU_RUN_FUNC_END \
m.spc_time += rel_time;\
m.dsp_time -= rel_time;\
m.timers [0].next_time -= rel_time;\
m.timers [1].next_time -= rel_time;\
m.timers [2].next_time -= rel_time;\
assert( m.spc_time <= end_time );\
return &REGS [r_cpuio0];\
}
int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
void SNES_SPC::end_frame( time_t end_time )
{
// Catch CPU up to as close to end as possible. If final instruction
// would exceed end, does NOT execute it and leaves m.spc_time < end.
if ( end_time > m.spc_time )
run_until_( end_time );
m.spc_time -= end_time;
m.extra_clocks += end_time;
// Greatest number of clocks early that emulation can stop early due to
// not being able to execute current instruction without going over
// allowed time.
assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 );
// Catch timers up to CPU
for ( int i = 0; i < timer_count; i++ )
run_timer( &m.timers [i], 0 );
// Catch DSP up to CPU
if ( m.dsp_time < 0 )
{
RUN_DSP( 0, max_reg_time );
}
// Save any extra samples beyond what should be generated
if ( m.buf_begin )
save_extra();
}
// Inclusion here allows static memory access functions and better optimization
#include "SPC_CPU.h"

Wyświetl plik

@ -0,0 +1,288 @@
// SNES SPC-700 APU emulator
// snes_spc 0.9.0
#ifndef SNES_SPC_H
#define SNES_SPC_H
#include "SPC_DSP.h"
#include "blargg_endian.h"
struct SNES_SPC {
public:
typedef BOOST::uint8_t uint8_t;
// Must be called once before using
blargg_err_t init();
// Sample pairs generated per second
enum { sample_rate = 32000 };
// Emulator use
// Sets IPL ROM data. Library does not include ROM data. Most SPC music files
// don't need ROM, but a full emulator must provide this.
enum { rom_size = 0x40 };
void init_rom( uint8_t const rom [rom_size] );
// Sets destination for output samples
typedef short sample_t;
void set_output( sample_t* out, int out_size );
// Number of samples written to output since last set
int sample_count() const;
// Resets SPC to power-on state. This resets your output buffer, so you must
// call set_output() after this.
void reset();
// Emulates pressing reset switch on SNES. This resets your output buffer, so
// you must call set_output() after this.
void soft_reset();
// 1024000 SPC clocks per second, sample pair every 32 clocks
typedef int time_t;
enum { clock_rate = 1024000 };
enum { clocks_per_sample = 32 };
// Emulated port read/write at specified time
enum { port_count = 4 };
int read_port ( time_t, int port );
void write_port( time_t, int port, int data );
// Runs SPC to end_time and starts a new time frame at 0
void end_frame( time_t end_time );
// Sound control
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
// Reduces emulation accuracy.
enum { voice_count = 8 };
void mute_voices( int mask );
// If true, prevents channels and global volumes from being phase-negated.
// Only supported by fast DSP.
void disable_surround( bool disable = true );
// Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.
enum { tempo_unit = 0x100 };
void set_tempo( int );
// SPC music files
// Loads SPC data into emulator
enum { spc_min_file_size = 0x10180 };
enum { spc_file_size = 0x10200 };
blargg_err_t load_spc( void const* in, long size );
// Clears echo region. Useful after loading an SPC as many have garbage in echo.
void clear_echo();
// Plays for count samples and write samples to out. Discards samples if out
// is NULL. Count must be a multiple of 2 since output is stereo.
blargg_err_t play( int count, sample_t* out );
// Skips count samples. Several times faster than play() when using fast DSP.
blargg_err_t skip( int count );
// State save/load (only available with accurate DSP)
#if !SPC_NO_COPY_STATE_FUNCS
// Saves/loads state
enum { state_size = 68 * 1024L }; // maximum space needed when saving
typedef SPC_DSP::copy_func_t copy_func_t;
void copy_state( unsigned char** io, copy_func_t );
// Writes minimal header to spc_out
static void init_header( void* spc_out );
// Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out.
// Does not set up SPC header; use init_header() for that.
void save_spc( void* spc_out );
// Returns true if new key-on events occurred since last check. Useful for
// trimming silence while saving an SPC.
bool check_kon();
#endif
//// Snes9x Accessor
void dsp_set_spc_snapshot_callback( void (*callback) (void) );
void dsp_dump_spc_snapshot( void );
void dsp_set_stereo_switch( int );
uint8_t dsp_reg_value( int, int );
int dsp_envx_value( int );
public:
BLARGG_DISABLE_NOTHROW
typedef BOOST::uint16_t uint16_t;
// Time relative to m_spc_time. Speeds up code a bit by eliminating need to
// constantly add m_spc_time to time from CPU. CPU uses time that ends at
// 0 to eliminate reloading end time every instruction. It pays off.
typedef int rel_time_t;
struct Timer
{
rel_time_t next_time; // time of next event
int prescaler;
int period;
int divider;
int enabled;
int counter;
};
enum { reg_count = 0x10 };
enum { timer_count = 3 };
enum { extra_size = SPC_DSP::extra_size };
enum { signature_size = 35 };
private:
SPC_DSP dsp;
#if SPC_LESS_ACCURATE
static signed char const reg_times_ [256];
signed char reg_times [256];
#endif
struct state_t
{
Timer timers [timer_count];
uint8_t smp_regs [2] [reg_count];
struct
{
int pc;
int a;
int x;
int y;
int psw;
int sp;
} cpu_regs;
rel_time_t dsp_time;
time_t spc_time;
bool echo_accessed;
int tempo;
int skipped_kon;
int skipped_koff;
const char* cpu_error;
int extra_clocks;
sample_t* buf_begin;
sample_t const* buf_end;
sample_t* extra_pos;
sample_t extra_buf [extra_size];
int rom_enabled;
uint8_t rom [rom_size];
uint8_t hi_ram [rom_size];
unsigned char cycle_table [256];
struct
{
// padding to neutralize address overflow
union {
uint8_t padding1 [0x100];
uint16_t align; // makes compiler align data for 16-bit access
} padding1 [1];
uint8_t ram [0x10000];
uint8_t padding2 [0x100];
} ram;
};
state_t m;
enum { rom_addr = 0xFFC0 };
enum { skipping_time = 127 };
// Value that padding should be filled with
enum { cpu_pad_fill = 0xFF };
enum {
r_test = 0x0, r_control = 0x1,
r_dspaddr = 0x2, r_dspdata = 0x3,
r_cpuio0 = 0x4, r_cpuio1 = 0x5,
r_cpuio2 = 0x6, r_cpuio3 = 0x7,
r_f8 = 0x8, r_f9 = 0x9,
r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,
r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF
};
void timers_loaded();
void enable_rom( int enable );
void reset_buf();
void save_extra();
void load_regs( uint8_t const in [reg_count] );
void ram_loaded();
void regs_loaded();
void reset_time_regs();
void reset_common( int timer_counter_init );
Timer* run_timer_ ( Timer* t, rel_time_t );
Timer* run_timer ( Timer* t, rel_time_t );
int dsp_read ( rel_time_t );
void dsp_write ( int data, rel_time_t );
void cpu_write_smp_reg_( int data, rel_time_t, int addr );
void cpu_write_smp_reg ( int data, rel_time_t, int addr );
void cpu_write_high ( int data, int i, rel_time_t );
void cpu_write ( int data, int addr, rel_time_t );
int cpu_read_smp_reg ( int i, rel_time_t );
int cpu_read ( int addr, rel_time_t );
unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t );
bool check_echo_access ( int addr );
uint8_t* run_until_( time_t end_time );
struct spc_file_t
{
char signature [signature_size];
uint8_t has_id666;
uint8_t version;
uint8_t pcl, pch;
uint8_t a;
uint8_t x;
uint8_t y;
uint8_t psw;
uint8_t sp;
char text [212];
uint8_t ram [0x10000];
uint8_t dsp [128];
uint8_t unused [0x40];
uint8_t ipl_rom [0x40];
};
static char const signature [signature_size + 1];
void save_regs( uint8_t out [reg_count] );
};
#include <assert.h>
inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }
inline int SNES_SPC::read_port( time_t t, int port )
{
assert( (unsigned) port < port_count );
return run_until_( t ) [port];
}
inline void SNES_SPC::write_port( time_t t, int port, int data )
{
assert( (unsigned) port < port_count );
run_until_( t ) [0x10 + port] = data;
m.ram.ram [0xF4 + port] = data;
}
inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); }
inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); }
#if !SPC_NO_COPY_STATE_FUNCS
inline bool SNES_SPC::check_kon() { return dsp.check_kon(); }
#endif
#endif

Wyświetl plik

@ -0,0 +1,413 @@
// SPC emulation support: init, sample buffering, reset, SPC loading
// snes_spc 0.9.0. http://www.slack.net/~ant/
#include "SNES_SPC.h"
#include <string.h>
/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
#define RAM (m.ram.ram)
#define REGS (m.smp_regs [0])
#define REGS_IN (m.smp_regs [1])
// (n ? n : 256)
#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
//// Init
blargg_err_t SNES_SPC::init()
{
memset( &m, 0, sizeof m );
dsp.init( RAM );
m.tempo = tempo_unit;
// Most SPC music doesn't need ROM, and almost all the rest only rely
// on these two bytes
m.rom [0x3E] = 0xFF;
m.rom [0x3F] = 0xC0;
static unsigned char const cycle_table [128] =
{// 01 23 45 67 89 AB CD EF
0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0
0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1
0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2
0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3
0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4
0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5
0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6
0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7
0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8
0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9
0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A
0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B
0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C
0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D
0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E
0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F
};
// unpack cycle table
for ( int i = 0; i < 128; i++ )
{
int n = cycle_table [i];
m.cycle_table [i * 2 + 0] = n >> 4;
m.cycle_table [i * 2 + 1] = n & 0x0F;
}
#if SPC_LESS_ACCURATE
memcpy( reg_times, reg_times_, sizeof reg_times );
#endif
reset();
return 0;
}
void SNES_SPC::init_rom( uint8_t const in [rom_size] )
{
memcpy( m.rom, in, sizeof m.rom );
}
void SNES_SPC::set_tempo( int t )
{
m.tempo = t;
int const timer2_shift = 4; // 64 kHz
int const other_shift = 3; // 8 kHz
#if SPC_DISABLE_TEMPO
m.timers [2].prescaler = timer2_shift;
m.timers [1].prescaler = timer2_shift + other_shift;
m.timers [0].prescaler = timer2_shift + other_shift;
#else
if ( !t )
t = 1;
int const timer2_rate = 1 << timer2_shift;
int rate = (timer2_rate * tempo_unit + (t >> 1)) / t;
if ( rate < timer2_rate / 4 )
rate = timer2_rate / 4; // max 4x tempo
m.timers [2].prescaler = rate;
m.timers [1].prescaler = rate << other_shift;
m.timers [0].prescaler = rate << other_shift;
#endif
}
// Timer registers have been loaded. Applies these to the timers. Does not
// reset timer prescalers or dividers.
void SNES_SPC::timers_loaded()
{
int i;
for ( i = 0; i < timer_count; i++ )
{
Timer* t = &m.timers [i];
t->period = IF_0_THEN_256( REGS [r_t0target + i] );
t->enabled = REGS [r_control] >> i & 1;
t->counter = REGS_IN [r_t0out + i] & 0x0F;
}
set_tempo( m.tempo );
}
// Loads registers from unified 16-byte format
void SNES_SPC::load_regs( uint8_t const in [reg_count] )
{
memcpy( REGS, in, reg_count );
memcpy( REGS_IN, REGS, reg_count );
// These always read back as 0
REGS_IN [r_test ] = 0;
REGS_IN [r_control ] = 0;
REGS_IN [r_t0target] = 0;
REGS_IN [r_t1target] = 0;
REGS_IN [r_t2target] = 0;
}
// RAM was just loaded from SPC, with $F0-$FF containing SMP registers
// and timer counts. Copies these to proper registers.
void SNES_SPC::ram_loaded()
{
m.rom_enabled = 0;
load_regs( &RAM [0xF0] );
// Put STOP instruction around memory to catch PC underflow/overflow
memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 );
memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 );
}
// Registers were just loaded. Applies these new values.
void SNES_SPC::regs_loaded()
{
enable_rom( REGS [r_control] & 0x80 );
timers_loaded();
}
void SNES_SPC::reset_time_regs()
{
m.cpu_error = 0;
m.echo_accessed = 0;
m.spc_time = 0;
m.dsp_time = 0;
#if SPC_LESS_ACCURATE
m.dsp_time = clocks_per_sample + 1;
#endif
for ( int i = 0; i < timer_count; i++ )
{
Timer* t = &m.timers [i];
t->next_time = 1;
t->divider = 0;
}
regs_loaded();
m.extra_clocks = 0;
reset_buf();
}
void SNES_SPC::reset_common( int timer_counter_init )
{
int i;
for ( i = 0; i < timer_count; i++ )
REGS_IN [r_t0out + i] = timer_counter_init;
// Run IPL ROM
memset( &m.cpu_regs, 0, sizeof m.cpu_regs );
m.cpu_regs.pc = rom_addr;
REGS [r_test ] = 0x0A;
REGS [r_control] = 0xB0; // ROM enabled, clear ports
for ( i = 0; i < port_count; i++ )
REGS_IN [r_cpuio0 + i] = 0;
reset_time_regs();
}
void SNES_SPC::soft_reset()
{
reset_common( 0 );
dsp.soft_reset();
}
void SNES_SPC::reset()
{
m.cpu_regs.pc = 0xFFC0;
m.cpu_regs.a = 0x00;
m.cpu_regs.x = 0x00;
m.cpu_regs.y = 0x00;
m.cpu_regs.psw = 0x02;
m.cpu_regs.sp = 0xEF;
memset( RAM, 0x00, 0x10000 );
ram_loaded();
reset_common( 0x0F );
dsp.reset();
}
char const SNES_SPC::signature [signature_size + 1] =
"SNES-SPC700 Sound File Data v0.30\x1A\x1A";
blargg_err_t SNES_SPC::load_spc( void const* data, long size )
{
spc_file_t const* const spc = (spc_file_t const*) data;
// be sure compiler didn't insert any padding into fle_t
assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 );
// Check signature and file size
if ( size < signature_size || memcmp( spc, signature, 27 ) )
return "Not an SPC file";
if ( size < spc_min_file_size )
return "Corrupt SPC file";
// CPU registers
m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl;
m.cpu_regs.a = spc->a;
m.cpu_regs.x = spc->x;
m.cpu_regs.y = spc->y;
m.cpu_regs.psw = spc->psw;
m.cpu_regs.sp = spc->sp;
// RAM and registers
memcpy( RAM, spc->ram, 0x10000 );
ram_loaded();
// DSP registers
dsp.load( spc->dsp );
reset_time_regs();
return 0;
}
void SNES_SPC::clear_echo()
{
if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
{
int addr = 0x100 * dsp.read( SPC_DSP::r_esa );
int end = addr + 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
if ( end > 0x10000 )
end = 0x10000;
memset( &RAM [addr], 0xFF, end - addr );
}
}
//// Sample output
void SNES_SPC::reset_buf()
{
// Start with half extra buffer of silence
sample_t* out = m.extra_buf;
while ( out < &m.extra_buf [extra_size / 2] )
*out++ = 0;
m.extra_pos = out;
m.buf_begin = 0;
dsp.set_output( 0, 0 );
}
void SNES_SPC::set_output( sample_t* out, int size )
{
require( (size & 1) == 0 ); // size must be even
m.extra_clocks &= clocks_per_sample - 1;
if ( out )
{
sample_t const* out_end = out + size;
m.buf_begin = out;
m.buf_end = out_end;
// Copy extra to output
sample_t const* in = m.extra_buf;
while ( in < m.extra_pos && out < out_end )
*out++ = *in++;
// Handle output being full already
if ( out >= out_end )
{
// Have DSP write to remaining extra space
out = dsp.extra();
out_end = &dsp.extra() [extra_size];
// Copy any remaining extra samples as if DSP wrote them
while ( in < m.extra_pos )
*out++ = *in++;
assert( out <= out_end );
}
dsp.set_output( out, out_end - out );
}
else
{
reset_buf();
}
}
void SNES_SPC::save_extra()
{
// Get end pointers
sample_t const* main_end = m.buf_end; // end of data written to buf
sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra()
if ( m.buf_begin <= dsp_end && dsp_end <= main_end )
{
main_end = dsp_end;
dsp_end = dsp.extra(); // nothing in DSP's extra
}
// Copy any extra samples at these ends into extra_buf
sample_t* out = m.extra_buf;
sample_t const* in;
for ( in = m.buf_begin + sample_count(); in < main_end; in++ )
*out++ = *in;
for ( in = dsp.extra(); in < dsp_end ; in++ )
*out++ = *in;
m.extra_pos = out;
assert( out <= &m.extra_buf [extra_size] );
}
blargg_err_t SNES_SPC::play( int count, sample_t* out )
{
require( (count & 1) == 0 ); // must be even
if ( count )
{
set_output( out, count );
end_frame( count * (clocks_per_sample / 2) );
}
const char* err = m.cpu_error;
m.cpu_error = 0;
return err;
}
blargg_err_t SNES_SPC::skip( int count )
{
#if SPC_LESS_ACCURATE
if ( count > 2 * sample_rate * 2 )
{
set_output( 0, 0 );
// Skip a multiple of 4 samples
time_t end = count;
count = (count & 3) + 1 * sample_rate * 2;
end = (end - count) * (clocks_per_sample / 2);
m.skipped_kon = 0;
m.skipped_koff = 0;
// Preserve DSP and timer synchronization
// TODO: verify that this really preserves it
int old_dsp_time = m.dsp_time + m.spc_time;
m.dsp_time = end - m.spc_time + skipping_time;
end_frame( end );
m.dsp_time = m.dsp_time - skipping_time + old_dsp_time;
dsp.write( SPC_DSP::r_koff, m.skipped_koff & ~m.skipped_kon );
dsp.write( SPC_DSP::r_kon , m.skipped_kon );
clear_echo();
}
#endif
return play( count, 0 );
}
//// Snes9x Accessor
void SNES_SPC::dsp_set_spc_snapshot_callback( void (*callback) (void) )
{
dsp.set_spc_snapshot_callback( callback );
}
void SNES_SPC::dsp_dump_spc_snapshot( void )
{
dsp.dump_spc_snapshot();
}
void SNES_SPC::dsp_set_stereo_switch( int value )
{
dsp.set_stereo_switch( value );
}
SNES_SPC::uint8_t SNES_SPC::dsp_reg_value( int ch, int addr )
{
return dsp.reg_value( ch, addr );
}
int SNES_SPC::dsp_envx_value( int ch )
{
return dsp.envx_value( ch );
}

Plik diff jest za duży Load Diff

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,317 @@
// Highly accurate SNES SPC-700 DSP emulator
// snes_spc 0.9.0
#ifndef SPC_DSP_H
#define SPC_DSP_H
#include "blargg_common.h"
extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }
class SPC_DSP {
public:
typedef BOOST::uint8_t uint8_t;
// Setup
// Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k );
// Sets destination for output samples. If out is NULL or out_size is 0,
// doesn't generate any.
typedef short sample_t;
void set_output( sample_t* out, int out_size );
// Number of samples written to output since it was last set, always
// a multiple of 2. Undefined if more samples were generated than
// output buffer could hold.
int sample_count() const;
// Emulation
// Resets DSP to power-on state
void reset();
// Emulates pressing reset switch on SNES
void soft_reset();
// Reads/writes DSP registers. For accuracy, you must first call run()
// to catch the DSP up to present.
int read ( int addr ) const;
void write( int addr, int data );
// Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
// a pair of samples is be generated.
void run( int clock_count );
// Sound control
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
// Reduces emulation accuracy.
enum { voice_count = 8 };
void mute_voices( int mask );
// State
// Resets DSP and uses supplied values to initialize registers
enum { register_count = 128 };
void load( uint8_t const regs [register_count] );
// Saves/loads exact emulator state
enum { state_size = 640 }; // maximum space needed when saving
typedef dsp_copy_func_t copy_func_t;
void copy_state( unsigned char** io, copy_func_t );
// Returns non-zero if new key-on events occurred since last call
bool check_kon();
// Snes9x Accessor
int stereo_switch;
int take_spc_snapshot;
void (*spc_snapshot_callback) (void);
void set_spc_snapshot_callback( void (*callback) (void) );
void dump_spc_snapshot( void );
void set_stereo_switch( int );
uint8_t reg_value( int, int );
int envx_value( int );
// DSP register addresses
// Global registers
enum {
r_mvoll = 0x0C, r_mvolr = 0x1C,
r_evoll = 0x2C, r_evolr = 0x3C,
r_kon = 0x4C, r_koff = 0x5C,
r_flg = 0x6C, r_endx = 0x7C,
r_efb = 0x0D, r_pmon = 0x2D,
r_non = 0x3D, r_eon = 0x4D,
r_dir = 0x5D, r_esa = 0x6D,
r_edl = 0x7D,
r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
};
// Voice registers
enum {
v_voll = 0x00, v_volr = 0x01,
v_pitchl = 0x02, v_pitchh = 0x03,
v_srcn = 0x04, v_adsr0 = 0x05,
v_adsr1 = 0x06, v_gain = 0x07,
v_envx = 0x08, v_outx = 0x09
};
public:
enum { extra_size = 16 };
sample_t* extra() { return m.extra; }
sample_t const* out_pos() const { return m.out; }
void disable_surround( bool ) { } // not supported
public:
BLARGG_DISABLE_NOTHROW
typedef BOOST::int8_t int8_t;
typedef BOOST::int16_t int16_t;
enum { echo_hist_size = 8 };
enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
enum { brr_buf_size = 12 };
struct voice_t
{
int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
int buf_pos; // place in buffer where next samples will be decoded
int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
int brr_addr; // address of current BRR block
int brr_offset; // current decoding offset in BRR block
uint8_t* regs; // pointer to voice's DSP registers
int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.
int kon_delay; // KON delay/current setup phase
env_mode_t env_mode;
int env; // current envelope level
int hidden_env; // used by GAIN mode 7, very obscure quirk
uint8_t t_envx_out;
int voice_number;
};
private:
enum { brr_block_size = 9 };
struct state_t
{
uint8_t regs [register_count];
// Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
int echo_hist [echo_hist_size * 2] [2];
int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
int every_other_sample; // toggles every sample
int kon; // KON value when last checked
int noise;
int counter;
int echo_offset; // offset from ESA in echo buffer
int echo_length; // number of bytes that echo_offset will stop at
int phase; // next clock cycle to run (0-31)
bool kon_check; // set when a new KON occurs
// Hidden registers also written to when main register is written to
int new_kon;
uint8_t endx_buf;
uint8_t envx_buf;
uint8_t outx_buf;
// Temporary state between clocks
// read once per sample
int t_pmon;
int t_non;
int t_eon;
int t_dir;
int t_koff;
// read a few clocks ahead then used
int t_brr_next_addr;
int t_adsr0;
int t_brr_header;
int t_brr_byte;
int t_srcn;
int t_esa;
int t_echo_enabled;
// internal state that is recalculated every sample
int t_dir_addr;
int t_pitch;
int t_output;
int t_looped;
int t_echo_ptr;
// left/right sums
int t_main_out [2];
int t_echo_out [2];
int t_echo_in [2];
voice_t voices [voice_count];
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask;
sample_t* out;
sample_t* out_end;
sample_t* out_begin;
sample_t extra [extra_size];
};
state_t m;
void init_counter();
void run_counters();
unsigned read_counter( int rate );
int interpolate( voice_t const* v );
void run_envelope( voice_t* const v );
void decode_brr( voice_t* v );
void misc_27();
void misc_28();
void misc_29();
void misc_30();
void voice_output( voice_t const* v, int ch );
void voice_V1( voice_t* const );
void voice_V2( voice_t* const );
void voice_V3( voice_t* const );
void voice_V3a( voice_t* const );
void voice_V3b( voice_t* const );
void voice_V3c( voice_t* const );
void voice_V4( voice_t* const );
void voice_V5( voice_t* const );
void voice_V6( voice_t* const );
void voice_V7( voice_t* const );
void voice_V8( voice_t* const );
void voice_V9( voice_t* const );
void voice_V7_V4_V1( voice_t* const );
void voice_V8_V5_V2( voice_t* const );
void voice_V9_V6_V3( voice_t* const );
void echo_read( int ch );
int echo_output( int ch );
void echo_write( int ch );
void echo_22();
void echo_23();
void echo_24();
void echo_25();
void echo_26();
void echo_27();
void echo_28();
void echo_29();
void echo_30();
void soft_reset_common();
};
#include <assert.h>
inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; }
inline int SPC_DSP::read( int addr ) const
{
assert( (unsigned) addr < register_count );
return m.regs [addr];
}
inline void SPC_DSP::write( int addr, int data )
{
assert( (unsigned) addr < register_count );
m.regs [addr] = (uint8_t) data;
switch ( addr & 0x0F )
{
case v_envx:
m.envx_buf = (uint8_t) data;
break;
case v_outx:
m.outx_buf = (uint8_t) data;
break;
case 0x0C:
if ( addr == r_kon )
m.new_kon = (uint8_t) data;
if ( addr == r_endx ) // always cleared, regardless of data written
{
m.endx_buf = 0;
m.regs [r_endx] = 0;
}
break;
}
}
inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }
inline bool SPC_DSP::check_kon()
{
bool old = m.kon_check;
m.kon_check = 0;
return old;
}
#if !SPC_NO_COPY_STATE_FUNCS
class SPC_State_Copier {
SPC_DSP::copy_func_t func;
unsigned char** buf;
public:
SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; }
void copy( void* state, size_t size );
int copy_int( int state, int size );
void skip( int count );
void extra();
};
#define SPC_COPY( type, state )\
{\
state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\
assert( (BOOST::type) state == state );\
}
#endif
#endif

Wyświetl plik

@ -1,68 +1,406 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
/***********************************************************************************
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
#include <cmath>
(c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com),
Jerremy Koot (jkoot@snes9x.com)
(c) Copyright 2002 - 2004 Matthew Kendora
(c) Copyright 2002 - 2005 Peter Bortas (peter@bortas.org)
(c) Copyright 2004 - 2005 Joel Yliluoma (http://iki.fi/bisqwit/)
(c) Copyright 2001 - 2006 John Weidman (jweidman@slip.net)
(c) Copyright 2002 - 2006 funkyass (funkyass@spam.shaw.ca),
Kris Bleakley (codeviolation@hotmail.com)
(c) Copyright 2002 - 2010 Brad Jorsch (anomie@users.sourceforge.net),
Nach (n-a-c-h@users.sourceforge.net),
zones (kasumitokoduck@yahoo.com)
(c) Copyright 2006 - 2007 nitsuja
(c) Copyright 2009 - 2010 BearOso,
OV2
BS-X C emulator code
(c) Copyright 2005 - 2006 Dreamer Nom,
zones
C4 x86 assembler and some C emulation code
(c) Copyright 2000 - 2003 _Demo_ (_demo_@zsnes.com),
Nach,
zsKnight (zsknight@zsnes.com)
C4 C++ code
(c) Copyright 2003 - 2006 Brad Jorsch,
Nach
DSP-1 emulator code
(c) Copyright 1998 - 2006 _Demo_,
Andreas Naive (andreasnaive@gmail.com),
Gary Henderson,
Ivar (ivar@snes9x.com),
John Weidman,
Kris Bleakley,
Matthew Kendora,
Nach,
neviksti (neviksti@hotmail.com)
DSP-2 emulator code
(c) Copyright 2003 John Weidman,
Kris Bleakley,
Lord Nightmare (lord_nightmare@users.sourceforge.net),
Matthew Kendora,
neviksti
DSP-3 emulator code
(c) Copyright 2003 - 2006 John Weidman,
Kris Bleakley,
Lancer,
z80 gaiden
DSP-4 emulator code
(c) Copyright 2004 - 2006 Dreamer Nom,
John Weidman,
Kris Bleakley,
Nach,
z80 gaiden
OBC1 emulator code
(c) Copyright 2001 - 2004 zsKnight,
pagefault (pagefault@zsnes.com),
Kris Bleakley
Ported from x86 assembler to C by sanmaiwashi
SPC7110 and RTC C++ emulator code used in 1.39-1.51
(c) Copyright 2002 Matthew Kendora with research by
zsKnight,
John Weidman,
Dark Force
SPC7110 and RTC C++ emulator code used in 1.52+
(c) Copyright 2009 byuu,
neviksti
S-DD1 C emulator code
(c) Copyright 2003 Brad Jorsch with research by
Andreas Naive,
John Weidman
S-RTC C emulator code
(c) Copyright 2001 - 2006 byuu,
John Weidman
ST010 C++ emulator code
(c) Copyright 2003 Feather,
John Weidman,
Kris Bleakley,
Matthew Kendora
Super FX x86 assembler emulator code
(c) Copyright 1998 - 2003 _Demo_,
pagefault,
zsKnight
Super FX C emulator code
(c) Copyright 1997 - 1999 Ivar,
Gary Henderson,
John Weidman
Sound emulator code used in 1.5-1.51
(c) Copyright 1998 - 2003 Brad Martin
(c) Copyright 1998 - 2006 Charles Bilyue'
Sound emulator code used in 1.52+
(c) Copyright 2004 - 2007 Shay Green (gblargg@gmail.com)
SH assembler code partly based on x86 assembler code
(c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se)
2xSaI filter
(c) Copyright 1999 - 2001 Derek Liauw Kie Fa
HQ2x, HQ3x, HQ4x filters
(c) Copyright 2003 Maxim Stepin (maxim@hiend3d.com)
NTSC filter
(c) Copyright 2006 - 2007 Shay Green
GTK+ GUI code
(c) Copyright 2004 - 2010 BearOso
Win32 GUI code
(c) Copyright 2003 - 2006 blip,
funkyass,
Matthew Kendora,
Nach,
nitsuja
(c) Copyright 2009 - 2010 OV2
Mac OS GUI code
(c) Copyright 1998 - 2001 John Stiles
(c) Copyright 2001 - 2010 zones
Specific ports contains the works of other authors. See headers in
individual files.
Snes9x homepage: http://www.snes9x.com/
Permission to use, copy, modify and/or distribute Snes9x in both binary
and source form, for non-commercial purposes, is hereby granted without
fee, providing that this license information and copyright notice appear
with all copies and any derived work.
This software is provided 'as-is', without any express or implied
warranty. In no event shall the authors be held liable for any damages
arising from the use of this software or it's derivatives.
Snes9x is freeware for PERSONAL USE only. Commercial users should
seek permission of the copyright holders first. Commercial use includes,
but is not limited to, charging money for Snes9x or software derived from
Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
using Snes9x as a promotion for your commercial product.
The copyright holders request that bug fixes and improvements to the code
should be forwarded to them so everyone can benefit from the modifications
in future versions.
Super NES and Super Nintendo Entertainment System are trademarks of
Nintendo Co., Limited and its subsidiary companies.
***********************************************************************************/
#include <math.h>
#include "snes9x.h"
#include "apu.h"
//#include "snapshot.h"
//#include "display.h"
#include "resampler.h"
namespace SNES
#define APU_DEFAULT_INPUT_RATE 32000
#define APU_MINIMUM_SAMPLE_COUNT 512
#define APU_MINIMUM_SAMPLE_BLOCK 128
#define APU_NUMERATOR_NTSC 5632
#define APU_DENOMINATOR_NTSC 118125
#define APU_NUMERATOR_PAL 102400
#define APU_DENOMINATOR_PAL 2128137
SNES_SPC *spc_core = NULL;
static uint8 APUROM[64] =
{
#include "smp.hpp"
} // namespace SNES
static const int APU_NUMERATOR_NTSC = 15664;
static const int APU_DENOMINATOR_NTSC = 328125;
static const int APU_NUMERATOR_PAL = 34176;
static const int APU_DENOMINATOR_PAL = 709379;
0xCD, 0xEF, 0xBD, 0xE8, 0x00, 0xC6, 0x1D, 0xD0,
0xFC, 0x8F, 0xAA, 0xF4, 0x8F, 0xBB, 0xF5, 0x78,
0xCC, 0xF4, 0xD0, 0xFB, 0x2F, 0x19, 0xEB, 0xF4,
0xD0, 0xFC, 0x7E, 0xF4, 0xD0, 0x0B, 0xE4, 0xF5,
0xCB, 0xF4, 0xD7, 0x00, 0xFC, 0xD0, 0xF3, 0xAB,
0x01, 0x10, 0xEF, 0x7E, 0xF4, 0x10, 0xEB, 0xBA,
0xF6, 0xDA, 0x00, 0xBA, 0xF4, 0xC4, 0xF4, 0xDD,
0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF
};
namespace spc
{
static apu_callback callback = NULL;
static void *callback_data = NULL;
static apu_callback sa_callback = NULL;
static void *extra_data = NULL;
static bool8 sound_in_sync = TRUE;
static bool8 sound_enabled = FALSE;
static bool8 sound_in_sync = TRUE;
static bool8 sound_enabled = FALSE;
static int32 reference_time;
static uint32 remainder;
static int buffer_size;
static int lag_master = 0;
static int lag = 0;
static const int timing_hack_numerator = 256;
static int timing_hack_denominator = 256;
/* Set these to NTSC for now. Will change to PAL in S9xAPUTimingSetSpeedup
if necessary on game load. */
static uint32 ratio_numerator = APU_NUMERATOR_NTSC;
static uint32 ratio_denominator = APU_DENOMINATOR_NTSC;
static uint8 *landing_buffer = NULL;
static uint8 *shrink_buffer = NULL;
static double dynamic_rate_multiplier = 1.0;
} // namespace spc
static Resampler *resampler = NULL;
extern "C" {
static int32 reference_time;
static uint32 remainder;
bool8 S9xMixSamples(uint8 *dest, int sample_count)
{
return true;
static const int32 timing_hack_numerator = SNES_SPC::tempo_unit;
static int32 timing_hack_denominator = SNES_SPC::tempo_unit;
}
int S9xGetSampleCount(void)
static void EightBitize (uint8 *, int);
static void DeStereo (uint8 *, int);
static void ReverseStereo (uint8 *, int);
static void UpdatePlaybackRate (void);
static void from_apu_to_state (uint8 **, void *, size_t);
static void to_apu_from_state (uint8 **, void *, size_t);
static void SPCSnapshotCallback (void);
static inline int S9xAPUGetClock (int32);
static inline int S9xAPUGetClockRemainder (int32);
static void EightBitize (uint8 *buffer, int sample_count)
{
return -1;
uint8 *buf8 = (uint8 *) buffer;
int16 *buf16 = (int16 *) buffer;
for (int i = 0; i < sample_count; i++)
buf8[i] = (uint8) ((buf16[i] / 256) + 128);
}
void S9xClearSamples(void)
static void DeStereo (uint8 *buffer, int sample_count)
{
int16 *buf = (int16 *) buffer;
int32 s1, s2;
for (int i = 0; i < sample_count >> 1; i++)
{
s1 = (int32) buf[2 * i];
s2 = (int32) buf[2 * i + 1];
buf[i] = (int16) ((s1 + s2) >> 1);
}
}
void S9xLandSamples(void)
static void ReverseStereo (uint8 *src_buffer, int sample_count)
{
int16 *buffer = (int16 *) src_buffer;
for (int i = 0; i < sample_count; i += 2)
{
buffer[i + 1] ^= buffer[i];
buffer[i] ^= buffer[i + 1];
buffer[i + 1] ^= buffer[i];
}
}
bool8 S9xSoundSync(void)
void S9xToggleSoundChannel (int c)
{
static uint8 sound_switch = 255;
if (c == 8)
sound_switch = 255;
else
sound_switch ^= 1 << c;
S9xSetSoundControl(sound_switch);
}
bool8 S9xMixSamples (uint8 *buffer, int sample_count)
{
static int shrink_buffer_size = -1;
uint8 *dest;
if (!Settings.SixteenBitSound || !Settings.Stereo)
{
/* We still need both stereo samples for generating the mono sample */
if (!Settings.Stereo)
sample_count <<= 1;
/* We still have to generate 16-bit samples for bit-dropping, too */
if (shrink_buffer_size < (sample_count << 1))
{
delete[] spc::shrink_buffer;
spc::shrink_buffer = new uint8[sample_count << 1];
shrink_buffer_size = sample_count << 1;
}
dest = spc::shrink_buffer;
}
else {
dest = buffer;
}
if (Settings.Mute)
{
memset(dest, 0, sample_count << 1);
spc::resampler->clear();
return (FALSE);
}
else
{
if (spc::resampler->avail() >= (sample_count + spc::lag))
{
spc::resampler->read((short *) dest, sample_count);
if (spc::lag == spc::lag_master)
spc::lag = 0;
}
else
{
memset(buffer, (Settings.SixteenBitSound ? 0 : 128), (sample_count << (Settings.SixteenBitSound ? 1 : 0)) >> (Settings.Stereo ? 0 : 1));
if (spc::lag == 0)
spc::lag = spc::lag_master;
return (FALSE);
}
}
if (Settings.ReverseStereo && Settings.Stereo)
ReverseStereo(dest, sample_count);
if (!Settings.Stereo || !Settings.SixteenBitSound)
{
if (!Settings.Stereo)
{
DeStereo(dest, sample_count);
sample_count >>= 1;
}
if (!Settings.SixteenBitSound)
EightBitize(dest, sample_count);
memcpy(buffer, dest, (sample_count << (Settings.SixteenBitSound ? 1 : 0)));
}
return (TRUE);
}
int S9xGetSampleCount (void)
{
return (spc::resampler->avail() >> (Settings.Stereo ? 0 : 1));
}
void S9xFinalizeSamples (void)
{
if (!Settings.Mute)
{
if (!spc::resampler->push((short *) spc::landing_buffer, spc_core->sample_count()))
{
/* We weren't able to process the entire buffer. Potential overrun. */
spc::sound_in_sync = FALSE;
if (Settings.SoundSync && !Settings.TurboMode)
return;
}
}
if (!Settings.SoundSync || Settings.TurboMode || Settings.Mute)
spc::sound_in_sync = TRUE;
else
if (spc::resampler->space_empty() >= spc::resampler->space_filled())
spc::sound_in_sync = TRUE;
else
spc::sound_in_sync = FALSE;
spc_core->set_output((SNES_SPC::sample_t *) spc::landing_buffer, spc::buffer_size >> 1);
}
void S9xLandSamples (void)
{
//emu_printf("S9xAPUExecute");
if (spc::sa_callback != NULL)
spc::sa_callback(spc::extra_data);
else
S9xFinalizeSamples();
}
void S9xClearSamples (void)
{
spc::resampler->clear();
spc::lag = spc::lag_master;
}
bool8 S9xSyncSound (void)
{
if (!Settings.SoundSync || spc::sound_in_sync)
return (TRUE);
@ -72,164 +410,259 @@ bool8 S9xSoundSync(void)
return (spc::sound_in_sync);
}
void S9xSetSamplesAvailableCallback(apu_callback callback, void *data)
void S9xSetSamplesAvailableCallback (apu_callback callback, void *data)
{
spc::callback = callback;
spc::callback_data = data;
spc::sa_callback = callback;
spc::extra_data = data;
}
static void UpdatePlaybackRate(void)
static void UpdatePlaybackRate (void)
{
if (Settings.SoundInputRate == 0)
Settings.SoundInputRate = APU_DEFAULT_INPUT_RATE;
double time_ratio = (double) Settings.SoundInputRate * spc::timing_hack_numerator / (Settings.SoundPlaybackRate * spc::timing_hack_denominator);
spc::resampler->time_ratio(time_ratio);
}
void S9xUpdateDynamicRate(int avail, int buffer_size)
bool8 S9xInitSound (int buffer_ms, int lag_ms)
{
spc::dynamic_rate_multiplier = 1.0 + (Settings.DynamicRateLimit * (buffer_size - 2 * avail)) /
(double)(1000 * buffer_size);
// buffer_ms : buffer size given in millisecond
// lag_ms : allowable time-lag given in millisecond
UpdatePlaybackRate();
}
int sample_count = buffer_ms * 32000 / 1000;
int lag_sample_count = lag_ms * 32000 / 1000;
bool8 S9xSoundInit(int buffer_ms)
{
if (!S9xInitAPU())
return FALSE;
spc::lag_master = lag_sample_count;
if (Settings.Stereo)
spc::lag_master <<= 1;
spc::lag = spc::lag_master;
UpdatePlaybackRate();
if (sample_count < APU_MINIMUM_SAMPLE_COUNT)
sample_count = APU_MINIMUM_SAMPLE_COUNT;
return TRUE;
}
spc::buffer_size = sample_count;
if (Settings.Stereo)
spc::buffer_size <<= 1;
if (Settings.SixteenBitSound)
spc::buffer_size <<= 1;
void S9xSetSoundControl(uint8 voice_switch)
{
//printf("Sound buffer size: %d (%d samples)\n", spc::buffer_size, sample_count);
}
if (spc::landing_buffer)
delete[] spc::landing_buffer;
spc::landing_buffer = new uint8[spc::buffer_size * 2];
if (!spc::landing_buffer)
return (FALSE);
void S9xSetSoundMute(bool8 mute)
{
Settings.Mute = mute || !spc::sound_enabled;
}
void S9xToggleSoundChannel(int c)
{
uint8 sound_switch = 0;
if (c == 8)
sound_switch = 255;
/* The resampler and spc unit use samples (16-bit short) as
arguments. Use 2x in the resampler for buffer leveling with SoundSync */
if (!spc::resampler)
{
spc::resampler = new Resampler(spc::buffer_size >> (Settings.SoundSync ? 0 : 1));
if (!spc::resampler)
{
delete[] spc::landing_buffer;
return (FALSE);
}
}
else
sound_switch ^= 1 << c;
spc::resampler->resize(spc::buffer_size >> (Settings.SoundSync ? 0 : 1));
S9xSetSoundControl(sound_switch);
spc_core->set_output((SNES_SPC::sample_t *) spc::landing_buffer, spc::buffer_size >> 1);
UpdatePlaybackRate();
spc::sound_enabled = S9xOpenSoundDevice();
return (spc::sound_enabled);
}
bool8 S9xInitAPU(void)
void S9xSetSoundControl (uint8 voice_switch)
{
spc_core->dsp_set_stereo_switch(voice_switch << 8 | voice_switch);
}
void S9xSetSoundMute (bool8 mute)
{
Settings.Mute = mute;
if (!spc::sound_enabled)
Settings.Mute = TRUE;
}
void S9xDumpSPCSnapshot (void)
{
spc_core->dsp_dump_spc_snapshot();
}
static void SPCSnapshotCallback (void)
{
// S9xSPCDump(S9xGetFilenameInc((".spc"), SPC_DIR));
// printf("Dumped key-on triggered spc snapshot.\n");
}
bool8 S9xInitAPU (void)
{
spc_core = new SNES_SPC;
if (!spc_core)
return (FALSE);
spc_core->init();
spc_core->init_rom(APUROM);
spc_core->dsp_set_spc_snapshot_callback(SPCSnapshotCallback);
spc::landing_buffer = NULL;
spc::shrink_buffer = NULL;
spc::resampler = NULL;
return (TRUE);
}
void S9xDeinitAPU(void)
void S9xDeinitAPU (void)
{
if (spc_core)
{
delete spc_core;
spc_core = NULL;
}
if (spc::resampler)
{
delete spc::resampler;
spc::resampler = NULL;
}
if (spc::landing_buffer)
{
delete[] spc::landing_buffer;
spc::landing_buffer = NULL;
}
if (spc::shrink_buffer)
{
delete[] spc::shrink_buffer;
spc::shrink_buffer = NULL;
}
}
void S9xAPUExecute(void)
static inline int S9xAPUGetClock (int32 cpucycles)
{
int cycles = (spc::ratio_numerator * (CPU.Cycles - spc::reference_time) + spc::remainder);
SNES::smp.execute(cycles / spc::ratio_denominator);
spc::remainder = (cycles % spc::ratio_denominator);
spc::reference_time = CPU.Cycles;
if (Settings.PAL)
return ((int) floor(((double) APU_NUMERATOR_PAL * spc::timing_hack_numerator * (cpucycles - spc::reference_time) + spc::remainder) /
((double) APU_DENOMINATOR_PAL * spc::timing_hack_denominator)));
else
return (APU_NUMERATOR_NTSC * spc::timing_hack_numerator * (cpucycles - spc::reference_time) + spc::remainder) /
(APU_DENOMINATOR_NTSC * spc::timing_hack_denominator);
}
uint8 S9xAPUReadPort(uint32 port)
static inline int S9xAPUGetClockRemainder (int32 cpucycles)
{
S9xAPUExecute();
return ((uint8)SNES::smp.apuram[0xf4 + (port & 3)]);
if (Settings.PAL)
return ((int) fmod (((double) APU_NUMERATOR_PAL * spc::timing_hack_numerator * (cpucycles - spc::reference_time) + spc::remainder),
((double) APU_DENOMINATOR_PAL * spc::timing_hack_denominator)));
else
return (APU_NUMERATOR_NTSC * spc::timing_hack_numerator * (cpucycles - spc::reference_time) + spc::remainder) %
(APU_DENOMINATOR_NTSC * spc::timing_hack_denominator);
}
void S9xAPUWritePort(uint32 port, uint8 byte)
uint8 S9xAPUReadPort (int port)
{
S9xAPUExecute();
SNES::smp.registers[port & 3] = byte;
return ((uint8) spc_core->read_port(S9xAPUGetClock(CPU.Cycles), port));
}
void S9xAPUSetReferenceTime(int32 cpucycles)
void S9xAPUWritePort (int port, uint8 byte)
{
spc_core->write_port(S9xAPUGetClock(CPU.Cycles), port, byte);
}
void S9xAPUSetReferenceTime (int32 cpucycles)
{
spc::reference_time = cpucycles;
}
void S9xAPUEndScanline(void)
void S9xAPUExecute (void)
{
S9xAPUExecute();
/* Accumulate partial APU cycles */
spc_core->end_frame(S9xAPUGetClock(CPU.Cycles));
spc::remainder = S9xAPUGetClockRemainder(CPU.Cycles);
S9xAPUSetReferenceTime(CPU.Cycles);
}
void S9xAPUTimingSetSpeedup(int ticks)
void S9xAPUEndScanline (void)
{
if (ticks != 0)
printf("APU speedup hack: %d\n", ticks);
S9xAPUExecute();
spc::timing_hack_denominator = 256 - ticks;
if (spc_core->sample_count() >= APU_MINIMUM_SAMPLE_BLOCK || !spc::sound_in_sync)
S9xLandSamples();
}
spc::ratio_numerator = (Settings.Region == S9X_PAL) ? APU_NUMERATOR_PAL : APU_NUMERATOR_NTSC;
spc::ratio_denominator = (Settings.Region == S9X_PAL) ? APU_DENOMINATOR_PAL : APU_DENOMINATOR_NTSC;
spc::ratio_denominator = spc::ratio_denominator * spc::timing_hack_denominator / spc::timing_hack_numerator;
void S9xAPUTimingSetSpeedup (int ticks)
{
// if (ticks != 0)
// printf("APU speedup hack: %d\n", ticks);
spc_core->set_tempo(SNES_SPC::tempo_unit - ticks);
spc::timing_hack_denominator = SNES_SPC::tempo_unit - ticks;
UpdatePlaybackRate();
}
void S9xResetAPU(void)
void S9xResetAPU (void)
{
spc::reference_time = 0;
spc::remainder = 0;
spc_core->reset();
spc_core->set_output((SNES_SPC::sample_t *) spc::landing_buffer, spc::buffer_size >> 1);
SNES::smp.power();
S9xClearSamples();
spc::resampler->clear();
}
void S9xSoftResetAPU(void)
void S9xSoftResetAPU (void)
{
spc::reference_time = 0;
spc::remainder = 0;
spc_core->soft_reset();
spc_core->set_output((SNES_SPC::sample_t *) spc::landing_buffer, spc::buffer_size >> 1);
SNES::smp.reset();
S9xClearSamples();
spc::resampler->clear();
}
void S9xAPUSaveState(uint8 *block)
static void from_apu_to_state (uint8 **buf, void *var, size_t size)
{
uint8 *ptr = block;
memcpy(*buf, var, size);
*buf += size;
}
SNES::smp.save_state(&ptr);
// SNES::dsp.save_state(&ptr);
static void to_apu_from_state (uint8 **buf, void *var, size_t size)
{
memcpy(var, *buf, size);
*buf += size;
}
void S9xAPUSaveState (uint8 *block)
{
uint8 *ptr = block;
spc_core->copy_state(&ptr, from_apu_to_state);
SET_LE32(ptr, spc::reference_time);
ptr += sizeof(int32);
SET_LE32(ptr, spc::remainder);
ptr += sizeof(int32);
// SET_LE32(ptr, SNES::dsp.clock);
ptr += sizeof(int32);
memcpy(ptr, SNES::smp.registers, 4);
ptr += sizeof(int32);
memset(ptr, 0, SPC_SAVE_STATE_BLOCK_SIZE - (ptr - block));
}
void S9xAPULoadState(uint8 *block)
void S9xAPULoadState (uint8 *block)
{
uint8 *ptr = block;
uint8 *ptr = block;
SNES::smp.load_state(&ptr);
// SNES::dsp.load_state(&ptr);
S9xResetAPU();
spc_core->copy_state(&ptr, to_apu_from_state);
spc::reference_time = GET_LE32(ptr);
ptr += sizeof(int32);
spc::remainder = GET_LE32(ptr);
ptr += sizeof(int32);
// SNES::dsp.clock = GET_LE32(ptr);
ptr += sizeof(int32);
memcpy(SNES::smp.registers, ptr, 4);
}
}

Wyświetl plik

@ -1,55 +1,218 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
/***********************************************************************************
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
(c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com),
Jerremy Koot (jkoot@snes9x.com)
(c) Copyright 2002 - 2004 Matthew Kendora
(c) Copyright 2002 - 2005 Peter Bortas (peter@bortas.org)
(c) Copyright 2004 - 2005 Joel Yliluoma (http://iki.fi/bisqwit/)
(c) Copyright 2001 - 2006 John Weidman (jweidman@slip.net)
(c) Copyright 2002 - 2006 funkyass (funkyass@spam.shaw.ca),
Kris Bleakley (codeviolation@hotmail.com)
(c) Copyright 2002 - 2010 Brad Jorsch (anomie@users.sourceforge.net),
Nach (n-a-c-h@users.sourceforge.net),
zones (kasumitokoduck@yahoo.com)
(c) Copyright 2006 - 2007 nitsuja
(c) Copyright 2009 - 2010 BearOso,
OV2
BS-X C emulator code
(c) Copyright 2005 - 2006 Dreamer Nom,
zones
C4 x86 assembler and some C emulation code
(c) Copyright 2000 - 2003 _Demo_ (_demo_@zsnes.com),
Nach,
zsKnight (zsknight@zsnes.com)
C4 C++ code
(c) Copyright 2003 - 2006 Brad Jorsch,
Nach
DSP-1 emulator code
(c) Copyright 1998 - 2006 _Demo_,
Andreas Naive (andreasnaive@gmail.com),
Gary Henderson,
Ivar (ivar@snes9x.com),
John Weidman,
Kris Bleakley,
Matthew Kendora,
Nach,
neviksti (neviksti@hotmail.com)
DSP-2 emulator code
(c) Copyright 2003 John Weidman,
Kris Bleakley,
Lord Nightmare (lord_nightmare@users.sourceforge.net),
Matthew Kendora,
neviksti
DSP-3 emulator code
(c) Copyright 2003 - 2006 John Weidman,
Kris Bleakley,
Lancer,
z80 gaiden
DSP-4 emulator code
(c) Copyright 2004 - 2006 Dreamer Nom,
John Weidman,
Kris Bleakley,
Nach,
z80 gaiden
OBC1 emulator code
(c) Copyright 2001 - 2004 zsKnight,
pagefault (pagefault@zsnes.com),
Kris Bleakley
Ported from x86 assembler to C by sanmaiwashi
SPC7110 and RTC C++ emulator code used in 1.39-1.51
(c) Copyright 2002 Matthew Kendora with research by
zsKnight,
John Weidman,
Dark Force
SPC7110 and RTC C++ emulator code used in 1.52+
(c) Copyright 2009 byuu,
neviksti
S-DD1 C emulator code
(c) Copyright 2003 Brad Jorsch with research by
Andreas Naive,
John Weidman
S-RTC C emulator code
(c) Copyright 2001 - 2006 byuu,
John Weidman
ST010 C++ emulator code
(c) Copyright 2003 Feather,
John Weidman,
Kris Bleakley,
Matthew Kendora
Super FX x86 assembler emulator code
(c) Copyright 1998 - 2003 _Demo_,
pagefault,
zsKnight
Super FX C emulator code
(c) Copyright 1997 - 1999 Ivar,
Gary Henderson,
John Weidman
Sound emulator code used in 1.5-1.51
(c) Copyright 1998 - 2003 Brad Martin
(c) Copyright 1998 - 2006 Charles Bilyue'
Sound emulator code used in 1.52+
(c) Copyright 2004 - 2007 Shay Green (gblargg@gmail.com)
SH assembler code partly based on x86 assembler code
(c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se)
2xSaI filter
(c) Copyright 1999 - 2001 Derek Liauw Kie Fa
HQ2x, HQ3x, HQ4x filters
(c) Copyright 2003 Maxim Stepin (maxim@hiend3d.com)
NTSC filter
(c) Copyright 2006 - 2007 Shay Green
GTK+ GUI code
(c) Copyright 2004 - 2010 BearOso
Win32 GUI code
(c) Copyright 2003 - 2006 blip,
funkyass,
Matthew Kendora,
Nach,
nitsuja
(c) Copyright 2009 - 2010 OV2
Mac OS GUI code
(c) Copyright 1998 - 2001 John Stiles
(c) Copyright 2001 - 2010 zones
Specific ports contains the works of other authors. See headers in
individual files.
Snes9x homepage: http://www.snes9x.com/
Permission to use, copy, modify and/or distribute Snes9x in both binary
and source form, for non-commercial purposes, is hereby granted without
fee, providing that this license information and copyright notice appear
with all copies and any derived work.
This software is provided 'as-is', without any express or implied
warranty. In no event shall the authors be held liable for any damages
arising from the use of this software or it's derivatives.
Snes9x is freeware for PERSONAL USE only. Commercial users should
seek permission of the copyright holders first. Commercial use includes,
but is not limited to, charging money for Snes9x or software derived from
Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
using Snes9x as a promotion for your commercial product.
The copyright holders request that bug fixes and improvements to the code
should be forwarded to them so everyone can benefit from the modifications
in future versions.
Super NES and Super Nintendo Entertainment System are trademarks of
Nintendo Co., Limited and its subsidiary companies.
***********************************************************************************/
#ifndef _APU_H_
#define _APU_H_
#include "snes9x.h"
#include "SNES_SPC.h"
typedef void (*apu_callback) (void *);
#define SPC_SAVE_STATE_BLOCK_SIZE (1024 * 65)
#ifdef __cplusplus
extern "C" {
#endif
#define SPC_SAVE_STATE_BLOCK_SIZE (SNES_SPC::state_size + 8)
bool8 S9xInitAPU (void);
void S9xDeinitAPU (void);
void S9xResetAPU (void);
void S9xSoftResetAPU (void);
uint8 S9xAPUReadPort (uint32);
void S9xAPUWritePort (uint32, uint8);
uint8 S9xAPUReadPort (int);
void S9xAPUWritePort (int, uint8);
void S9xAPUExecute (void);
void S9xAPUEndScanline (void);
void S9xAPUSetReferenceTime (int32);
void S9xAPUTimingSetSpeedup (int);
void S9xAPULoadState (uint8 *);
void S9xAPUSaveState (uint8 *);
void S9xDumpSPCSnapshot (void);
bool8 S9xSoundInit (int);
bool8 S9xSoundSync (void);
bool8 S9xInitSound (int, int);
bool8 S9xOpenSoundDevice (void);
bool8 S9xSyncSound (void);
int S9xGetSampleCount (void);
void S9xSetSoundControl (uint8);
void S9xToggleSoundChannel(int c);
void S9xSetSoundMute (bool8);
void S9xLandSamples (void);
void S9xFinalizeSamples (void);
void S9xClearSamples (void);
bool8 S9xMixSamples (uint8 *, int);
void S9xSetSamplesAvailableCallback (apu_callback, void *);
void S9xUpdateDynamicRate (int, int);
void S9xToggleSoundChannel (int);
#ifdef __cplusplus
}
#endif
#define DSP_INTERPOLATION_NONE 0
#define DSP_INTERPOLATION_LINEAR 1
#define DSP_INTERPOLATION_GAUSSIAN 2
#define DSP_INTERPOLATION_CUBIC 3
#define DSP_INTERPOLATION_SINC 4
extern SNES_SPC *spc_core;
#endif

Wyświetl plik

@ -0,0 +1,186 @@
// Sets up common environment for Shay Green's libraries.
// To change configuration options, modify blargg_config.h, not this file.
// snes_spc 0.9.0
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#undef BLARGG_COMMON_H
// allow blargg_config.h to #include blargg_common.h
#include "blargg_config.h"
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
// BLARGG_RESTRICT: equivalent to restrict, where supported
#if defined (__GNUC__) || _MSC_VER >= 1100
#define BLARGG_RESTRICT __restrict
#else
#define BLARGG_RESTRICT
#endif
// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr)
#ifndef STATIC_CAST
#define STATIC_CAST(T,expr) ((T) (expr))
#endif
// blargg_err_t (0 on success, otherwise error string)
#ifndef blargg_err_t
typedef const char* blargg_err_t;
#endif
// blargg_vector - very lightweight vector of POD types (no constructor/destructor)
template<class T>
class blargg_vector {
T* begin_;
size_t size_;
public:
blargg_vector() : begin_( 0 ), size_( 0 ) { }
~blargg_vector() { free( begin_ ); }
size_t size() const { return size_; }
T* begin() const { return begin_; }
T* end() const { return begin_ + size_; }
blargg_err_t resize( size_t n )
{
// TODO: blargg_common.cpp to hold this as an outline function, ugh
void* p = realloc( begin_, n * sizeof (T) );
if ( p )
begin_ = (T*) p;
else if ( n > size_ ) // realloc failure only a problem if expanding
return "Out of memory";
size_ = n;
return 0;
}
void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); }
T& operator [] ( size_t n ) const
{
assert( n <= size_ ); // <= to allow past-the-end value
return begin_ [n];
}
};
#ifndef BLARGG_DISABLE_NOTHROW
// throw spec mandatory in ISO C++ if operator new can return NULL
#if __cplusplus >= 199711 || defined (__GNUC__)
#define BLARGG_THROWS( spec ) throw spec
#else
#define BLARGG_THROWS( spec )
#endif
#define BLARGG_DISABLE_NOTHROW \
void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\
void operator delete ( void* p ) { free( p ); }
#define BLARGG_NEW new
#else
#include <new>
#define BLARGG_NEW new (std::nothrow)
#endif
// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant)
#define BLARGG_4CHAR( a, b, c, d ) \
((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF))
// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
#ifndef BOOST_STATIC_ASSERT
#ifdef _MSC_VER
// MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified
#define BOOST_STATIC_ASSERT( expr ) \
void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] )
#else
// Some other compilers fail when declaring same function multiple times in class,
// so differentiate them by line
#define BOOST_STATIC_ASSERT( expr ) \
void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] )
#endif
#endif
// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1,
// compiler is assumed to support bool. If undefined, availability is determined.
#ifndef BLARGG_COMPILER_HAS_BOOL
#if defined (__MWERKS__)
#if !__option(bool)
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#elif defined (_MSC_VER)
#if _MSC_VER < 1100
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#elif defined (__GNUC__)
// supports bool
#elif __cplusplus < 199711
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#endif
#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
// If you get errors here, modify your blargg_config.h file
typedef int bool;
const bool true = 1;
const bool false = 0;
#endif
// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF
typedef long blargg_long;
#else
typedef int blargg_long;
#endif
#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF
typedef unsigned long blargg_ulong;
#else
typedef unsigned blargg_ulong;
#endif
// BOOST::int8_t etc.
// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc.
#if defined (HAVE_STDINT_H)
#include <stdint.h>
#define BOOST
// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
#elif defined (HAVE_INTTYPES_H)
#include <inttypes.h>
#define BOOST
#else
struct BOOST
{
#if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F
typedef signed char int8_t;
typedef unsigned char uint8_t;
#else
// No suitable 8-bit type available
typedef struct see_blargg_common_h int8_t;
typedef struct see_blargg_common_h uint8_t;
#endif
#if USHRT_MAX == 0xFFFF
typedef short int16_t;
typedef unsigned short uint16_t;
#else
// No suitable 16-bit type available
typedef struct see_blargg_common_h int16_t;
typedef struct see_blargg_common_h uint16_t;
#endif
#if ULONG_MAX == 0xFFFFFFFF
typedef long int32_t;
typedef unsigned long uint32_t;
#elif UINT_MAX == 0xFFFFFFFF
typedef int int32_t;
typedef unsigned int uint32_t;
#else
// No suitable 32-bit type available
typedef struct see_blargg_common_h int32_t;
typedef struct see_blargg_common_h uint32_t;
#endif
};
#endif
#endif
#endif

Wyświetl plik

@ -0,0 +1,24 @@
// snes_spc 0.9.0 user configuration file. Don't replace when updating library.
// snes_spc 0.9.0
#ifndef BLARGG_CONFIG_H
#define BLARGG_CONFIG_H
// Uncomment to disable debugging checks
//#define NDEBUG 1
// Uncomment to enable platform-specific (and possibly non-portable) optimizations
//#define BLARGG_NONPORTABLE 1
// Uncomment if automatic byte-order determination doesn't work
//#define BLARGG_BIG_ENDIAN 1
// Uncomment if you get errors in the bool section of blargg_common.h
//#define BLARGG_COMPILER_HAS_BOOL 1
// Use standard config.h if present
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#endif

Wyświetl plik

@ -0,0 +1,185 @@
// CPU Byte Order Utilities
// snes_spc 0.9.0
#ifndef BLARGG_ENDIAN
#define BLARGG_ENDIAN
#include "blargg_common.h"
// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16)
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
#define BLARGG_CPU_X86 1
#define BLARGG_CPU_CISC 1
#endif
#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc)
#define BLARGG_CPU_POWERPC 1
#define BLARGG_CPU_RISC 1
#endif
// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only
// one may be #defined to 1. Only needed if something actually depends on byte order.
#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
#ifdef __GLIBC__
// GCC handles this for us
#include <endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define BLARGG_LITTLE_ENDIAN 1
#elif __BYTE_ORDER == __BIG_ENDIAN
#define BLARGG_BIG_ENDIAN 1
#endif
#else
#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \
(defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234)
#define BLARGG_LITTLE_ENDIAN 1
#endif
#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \
defined (__sparc__) || BLARGG_CPU_POWERPC || \
(defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321)
#define BLARGG_BIG_ENDIAN 1
#elif !defined (__mips__)
// No endian specified; assume little-endian, since it's most common
#define BLARGG_LITTLE_ENDIAN 1
#endif
#endif
#endif
#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN
#undef BLARGG_LITTLE_ENDIAN
#undef BLARGG_BIG_ENDIAN
#endif
inline void blargg_verify_byte_order()
{
#ifndef NDEBUG
#if BLARGG_BIG_ENDIAN
volatile int i = 1;
assert( *(volatile char*) &i == 0 );
#elif BLARGG_LITTLE_ENDIAN
volatile int i = 1;
assert( *(volatile char*) &i != 0 );
#endif
#endif
}
inline unsigned get_le16( void const* p )
{
return (unsigned) ((unsigned char const*) p) [1] << 8 |
(unsigned) ((unsigned char const*) p) [0];
}
inline unsigned get_be16( void const* p )
{
return (unsigned) ((unsigned char const*) p) [0] << 8 |
(unsigned) ((unsigned char const*) p) [1];
}
inline blargg_ulong get_le32( void const* p )
{
return (blargg_ulong) ((unsigned char const*) p) [3] << 24 |
(blargg_ulong) ((unsigned char const*) p) [2] << 16 |
(blargg_ulong) ((unsigned char const*) p) [1] << 8 |
(blargg_ulong) ((unsigned char const*) p) [0];
}
inline blargg_ulong get_be32( void const* p )
{
return (blargg_ulong) ((unsigned char const*) p) [0] << 24 |
(blargg_ulong) ((unsigned char const*) p) [1] << 16 |
(blargg_ulong) ((unsigned char const*) p) [2] << 8 |
(blargg_ulong) ((unsigned char const*) p) [3];
}
inline void set_le16( void* p, unsigned n )
{
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
((unsigned char*) p) [0] = (unsigned char) n;
}
inline void set_be16( void* p, unsigned n )
{
((unsigned char*) p) [0] = (unsigned char) (n >> 8);
((unsigned char*) p) [1] = (unsigned char) n;
}
inline void set_le32( void* p, blargg_ulong n )
{
((unsigned char*) p) [0] = (unsigned char) n;
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
((unsigned char*) p) [2] = (unsigned char) (n >> 16);
((unsigned char*) p) [3] = (unsigned char) (n >> 24);
}
inline void set_be32( void* p, blargg_ulong n )
{
((unsigned char*) p) [3] = (unsigned char) n;
((unsigned char*) p) [2] = (unsigned char) (n >> 8);
((unsigned char*) p) [1] = (unsigned char) (n >> 16);
((unsigned char*) p) [0] = (unsigned char) (n >> 24);
}
#if BLARGG_NONPORTABLE
// Optimized implementation if byte order is known
#if BLARGG_LITTLE_ENDIAN
#define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr))
#define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr))
#define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
#define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
#elif BLARGG_BIG_ENDIAN
#define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr))
#define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr))
#define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
#define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
#if BLARGG_CPU_POWERPC
// PowerPC has special byte-reversed instructions
#if defined (__MWERKS__)
#define GET_LE16( addr ) (__lhbrx( addr, 0 ))
#define GET_LE32( addr ) (__lwbrx( addr, 0 ))
#define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 ))
#define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 ))
#elif defined (__GNUC__)
#define GET_LE16( addr ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr), "0" (ppc_lhbrx_) ); ppc_lhbrx_;})
#define GET_LE32( addr ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr), "0" (ppc_lwbrx_) ); ppc_lwbrx_;})
#define SET_LE16( addr, in ) ({asm( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) );})
#define SET_LE32( addr, in ) ({asm( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) );})
#endif
#endif
#endif
#endif
#ifndef GET_LE16
#define GET_LE16( addr ) get_le16( addr )
#define SET_LE16( addr, data ) set_le16( addr, data )
#endif
#ifndef GET_LE32
#define GET_LE32( addr ) get_le32( addr )
#define SET_LE32( addr, data ) set_le32( addr, data )
#endif
#ifndef GET_BE16
#define GET_BE16( addr ) get_be16( addr )
#define SET_BE16( addr, data ) set_be16( addr, data )
#endif
#ifndef GET_BE32
#define GET_BE32( addr ) get_be32( addr )
#define SET_BE32( addr, data ) set_be32( addr, data )
#endif
// auto-selecting versions
inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); }
inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); }
inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); }
inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); }
inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); }
inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); }
inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); }
inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); }
#endif

Wyświetl plik

@ -0,0 +1,100 @@
/* Included at the beginning of library source files, after all other #include lines.
Sets up helpful macros and services used in my source code. They don't need
module an annoying module prefix on their names since they are defined after
all other #include lines. */
// snes_spc 0.9.0
#ifndef BLARGG_SOURCE_H
#define BLARGG_SOURCE_H
// If debugging is enabled, abort program if expr is false. Meant for checking
// internal state and consistency. A failed assertion indicates a bug in the module.
// void assert( bool expr );
#include <assert.h>
// If debugging is enabled and expr is false, abort program. Meant for checking
// caller-supplied parameters and operations that are outside the control of the
// module. A failed requirement indicates a bug outside the module.
// void require( bool expr );
#undef require
#define require( expr ) assert( expr )
// Like printf() except output goes to debug log file. Might be defined to do
// nothing (not even evaluate its arguments).
// void dprintf( const char* format, ... );
static inline void blargg_dprintf_( const char*, ... ) { }
#undef dprintf
#define dprintf (1) ? (void) 0 : blargg_dprintf_
// If enabled, evaluate expr and if false, make debug log entry with source file
// and line. Meant for finding situations that should be examined further, but that
// don't indicate a problem. In all cases, execution continues normally.
#undef check
#define check( expr ) ((void) 0)
// If expr yields error string, return it from current function, otherwise continue.
#undef RETURN_ERR
#define RETURN_ERR( expr ) do { \
blargg_err_t blargg_return_err_ = (expr); \
if ( blargg_return_err_ ) return blargg_return_err_; \
} while ( 0 )
// If ptr is 0, return out of memory error string.
#undef CHECK_ALLOC
#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
// Avoid any macros which evaluate their arguments multiple times
#undef min
#undef max
#define DEF_MIN_MAX( type ) \
static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\
static inline type max( type x, type y ) { if ( y < x ) return x; return y; }
DEF_MIN_MAX( int )
DEF_MIN_MAX( unsigned )
DEF_MIN_MAX( long )
DEF_MIN_MAX( unsigned long )
DEF_MIN_MAX( float )
DEF_MIN_MAX( double )
#undef DEF_MIN_MAX
/*
// using const references generates crappy code, and I am currenly only using these
// for built-in types, so they take arguments by value
// TODO: remove
inline int min( int x, int y )
template<class T>
inline T min( T x, T y )
{
if ( x < y )
return x;
return y;
}
template<class T>
inline T max( T x, T y )
{
if ( x < y )
return y;
return x;
}
*/
// TODO: good idea? bad idea?
#undef byte
#define byte byte_
typedef unsigned char byte;
// deprecated
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
#define BLARGG_RETURN_ERR RETURN_ERR
// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check
#ifdef BLARGG_SOURCE_BEGIN
#include BLARGG_SOURCE_BEGIN
#endif
#endif

Wyświetl plik

@ -20,6 +20,14 @@ USBHIDParser hid1(myusb);
MouseController mouse1(myusb);
MIDIDevice midi1(myusb);
#endif
#ifdef HAS_USBJOY
#include "USBHost_t36.h" // Read this header first for key info
USBHost myusb;
USBHub hub1(myusb);
USBHIDParser hid1(myusb);
#define COUNT_JOYSTICKS 4
JoystickController joysticks[COUNT_JOYSTICKS](myusb);
#endif
static bool emu_writeConfig(void);
static bool emu_readConfig(void);
@ -158,7 +166,7 @@ void emu_Free(void * pt)
free(pt);
}
#define SMEMPOOL (0x400000+400000)
#define SMEMPOOL (0x800000) //(0x400000+400000)
EXTMEM static unsigned char slowmem[SMEMPOOL];
static int slowmempt = 0;
@ -352,6 +360,11 @@ int emu_ReadKeys(void)
#endif
if ( row & 0x02 ) retval |= MASK_JOY2_BTN;
#ifdef EXTPAD
if ( fn_pressed ) retval |= MASK_KEY_USER1;
if ( sh_pressed ) retval |= MASK_KEY_USER3;
digitalWrite(KLED, 0);
#else
// Handle LED flash
uint32_t time_ms=millis();
if ((time_ms-last_t_ms) > 100) {
@ -435,8 +448,9 @@ int emu_ReadKeys(void)
if ( key_fn ) retval |= MASK_KEY_USER2;
if ( ( key_fn ) && (keymatrix[4] == 0x10 )) retval |= MASK_KEY_USER1;
#endif
if ( (key_fn) && (key_sh) )
if ( (fn_pressed) && (sh_pressed) )
#else
if ( ((retval & (MASK_KEY_USER1+MASK_KEY_USER2)) == (MASK_KEY_USER1+MASK_KEY_USER2))
|| (retval & MASK_KEY_USER4 ) )
@ -476,8 +490,10 @@ int emu_ReadKeys(void)
while (true) {
;
}
#endif
#endif
}
emu_GetJoystick();
return (retval);
}
@ -643,6 +659,23 @@ int emu_GetMouse(int *x, int *y, int *buts) {
return 0;
}
int emu_GetJoystick(void) {
#ifdef HAS_USBJOY
//myusb.Task();
for (int joystick_index = 0; joystick_index < COUNT_JOYSTICKS; joystick_index++) {
if (joysticks[joystick_index].available()) {
uint64_t axis_mask = joysticks[joystick_index].axisMask();
uint64_t axis_changed_mask = joysticks[joystick_index].axisChangedMask();
uint32_t buttons = joysticks[joystick_index].getButtons();
Serial.printf("Joystick(%d): buttons = %x", joystick_index, buttons);
Serial.println();
}
}
return 1;
#endif
return 0;
}
#ifdef HAS_USBKEY
void OnPress(auto key)
{
@ -1708,6 +1741,9 @@ void emu_init(void)
keyboard1.attachPress(OnPress);
keyboard1.attachRelease(OnRelease);
#endif
#ifdef HAS_USBJOY
myusb.begin();
#endif
while (!SD.begin(SD_CS))
{

Wyświetl plik

@ -5,8 +5,9 @@
#define CUSTOM_SND 1
//#define TIMER_REND 1
#define EXTPAD 1
#define EXTRA_HEAP 0x10
#define EXTRA_HEAP 0x14000
// Title: < >
#define TITLE " SNES Emulator "
@ -42,7 +43,7 @@
#define keylables_map0_2 (char *)" ZXCVBNM,.;/"
#define keylables_map0_3 (char *)" +\x10-"
const unsigned short key_map0[] = {
'Q','W','E','R','T','Y','U','I','O','P',157, //default C64 uppercase always
'q','w','E','R','T','Y','U','I','O','P',157, //default C64 uppercase always
0,'A','S','D','F','G','H','J','K','L',10,
0,'Z','X','C','V','B','N','M',',','.',';','/',
0,0,0,0,
@ -194,6 +195,7 @@ extern int emu_ReadKeys(void);
extern int emu_GetPad(void);
extern int emu_GetMouse(int *x, int *y, int *buts);
extern int emu_MouseDetected(void);
extern int emu_GetJoystick(void);
extern int emu_KeyboardDetected(void);
extern int emu_ReadAnalogJoyX(int min, int max);
extern int emu_ReadAnalogJoyY(int min, int max);

Wyświetl plik

@ -1543,7 +1543,7 @@ bool8 S9xGraphicsInit (void)
GFX.SubScreen = (SNESPixel *) emu_SMalloc(GFX.ScreenSize * sizeof(SNESPixel));
GFX.ZBuffer = (uint8 *) emu_Malloc(GFX.ScreenSize);
GFX.SubZBuffer = (uint8 *) emu_SMalloc(GFX.ScreenSize);
GFX.SubZBuffer = (uint8 *) emu_Malloc(GFX.ScreenSize);
GFX.ZERO = (SNESPixel *) GFX.SubScreen; // This will cause garbage but for now it's okay
IPPU.TileCacheData = (uint8 *) emu_SMalloc(4096 * 64);

Wyświetl plik

@ -20,7 +20,7 @@ typedef struct
const char *patch;
} s9x_hack_t;
const s9x_hack_t GameHacks[] =
PROGMEM const s9x_hack_t GameHacks[] =
{
{0x10000004, "0", 0x7E0147, 0x0, 0x1E, 0x2A, 0x18EC3, 0x0, "18EC3=EAEA,18E52=EAEA,18E70=EAEA,18E79=EAEA,185CC=EAEA,18612=EAEA,1865E=EAEA,186A5=EAEA,1871D=EAEA,18762=EAEA,184F1=EAEA,18531=EAEA,18560=EAEA,1858D=EAEA,656=42FC,18ED5=42D2,18EDC=EAEA,189F1=EAEA,18A0A=EAEA,18A44=EAEA,18A81=EAEA,18AC7=EAEA,18AF4=EAEA,18803=EAEA,1884A=EAEA,18896=EAEA,188DB=EAEA,188DD=EAEA,18955=EAEA,1899A=EAEA,18B20=EAEA,18B7E=EAEA,18BCA=EAEA,18C11=EAEA,18C89=EAEA,18CD0=EAEA,18D3C=EAEA,18D5A=EAEA,18D85=EAEA,15943=42FA,18EF7=42DB,18E00=42DB,18E2C=EAEA,15811=42FA,18DAE=EAEA,18DD7=80,189D3=EAEA,87=42FA,1534F=42F6"},
{0xFBF3C0FF, "3x3 Eyes - Seima Korin Den (J)", 0x4, 0x40044804, 0x0, 0x0, 0x0, 0x0, "214=42D8"},

Wyświetl plik

@ -1,40 +0,0 @@
//this is the IPLROM for the S-SMP coprocessor.
//the S-SMP does not allow writing to the IPLROM.
//all writes are instead mapped to the extended
//RAM region, accessible when $f1.d7 is clear.
PROGMEM const uint8 SMP::iplrom[64] = {
/*ffc0*/ 0xcd, 0xef, //mov x,#$ef
/*ffc2*/ 0xbd, //mov sp,x
/*ffc3*/ 0xe8, 0x00, //mov a,#$00
/*ffc5*/ 0xc6, //mov (x),a
/*ffc6*/ 0x1d, //dec x
/*ffc7*/ 0xd0, 0xfc, //bne $ffc5
/*ffc9*/ 0x8f, 0xaa, 0xf4, //mov $f4,#$aa
/*ffcc*/ 0x8f, 0xbb, 0xf5, //mov $f5,#$bb
/*ffcf*/ 0x78, 0xcc, 0xf4, //cmp $f4,#$cc
/*ffd2*/ 0xd0, 0xfb, //bne $ffcf
/*ffd4*/ 0x2f, 0x19, //bra $ffef
/*ffd6*/ 0xeb, 0xf4, //mov y,$f4
/*ffd8*/ 0xd0, 0xfc, //bne $ffd6
/*ffda*/ 0x7e, 0xf4, //cmp y,$f4
/*ffdc*/ 0xd0, 0x0b, //bne $ffe9
/*ffde*/ 0xe4, 0xf5, //mov a,$f5
/*ffe0*/ 0xcb, 0xf4, //mov $f4,y
/*ffe2*/ 0xd7, 0x00, //mov ($00)+y,a
/*ffe4*/ 0xfc, //inc y
/*ffe5*/ 0xd0, 0xf3, //bne $ffda
/*ffe7*/ 0xab, 0x01, //inc $01
/*ffe9*/ 0x10, 0xef, //bpl $ffda
/*ffeb*/ 0x7e, 0xf4, //cmp y,$f4
/*ffed*/ 0x10, 0xeb, //bpl $ffda
/*ffef*/ 0xba, 0xf6, //movw ya,$f6
/*fff1*/ 0xda, 0x00, //movw $00,ya
/*fff3*/ 0xba, 0xf4, //movw ya,$f4
/*fff5*/ 0xc4, 0xf4, //mov $f4,a
/*fff7*/ 0xdd, //mov a,y
/*fff8*/ 0x5d, //mov x,a
/*fff9*/ 0xd0, 0xdb, //bne $ffd6
/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x)
/*fffe*/ 0xc0, 0xff //reset vector location ($ffc0)
};

Wyświetl plik

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

Wyświetl plik

@ -4,6 +4,8 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <Arduino.h>
#include "snes9x.h"
#include "memory.h"
#include "apu.h"
@ -624,7 +626,7 @@ bool8 S9xMemoryInit (void)
Memory.RAM = (uint8 *) emu_Malloc(0x20000); // calloc(1, 0x20000); // 128k
Memory.VRAM = (uint8 *) emu_Malloc(0x10000); //calloc(1, 0x10000); // 64k
Memory.SRAM = (uint8 *) emu_Malloc(0x8000); //calloc(1, 0x8000); // 32k
Memory.ROM_MAX_SIZE = 0x400000;
Memory.ROM_MAX_SIZE = 0x500000;
//Memory.ROM = (uint8 *) calloc(1, Memory.ROM_MAX_SIZE);
// Note: we don't care if ROM alloc fails. It's just to grab a large heap block
// before it gets fragmented. The actual useful alloc is done in S9xLoadROM()

Wyświetl plik

@ -9,7 +9,8 @@
//#define TFTSPI1 1
#define HAS_T4_VGA 1
#define HAS_SND 1
#define HAS_USBKEY 1
//#define HAS_USBKEY 1
//#define HAS_USBJOY 1 // not working yet
//#define INVX 1
#define PT8211 1
@ -19,7 +20,7 @@
//#define INVX 1
#define INVY 1
#define HAS_SND 1
#define HAS_USBKEY 1
//#define HAS_USBKEY 1
#endif

Wyświetl plik

@ -102,7 +102,6 @@ typedef uint8 SNESPixel;
#define FIRST_COLOR_MASK 0xF800
#define SECOND_COLOR_MASK 0x07E0
#define THIRD_COLOR_MASK 0x001F
#define ALPHA_BITS_MASK 0x0000
// RGB are 5,5,5 => 0x00-0x1F values
@ -114,25 +113,54 @@ typedef uint8 SNESPixel;
#define RGB_REMOVE_LOW_BITS_MASK (~RGB_LOW_BITS_MASK)
#else
/*
#define RED_SHIFT_BITS 10
#define GREEN_SHIFT_BITS 5
//7C00 3E0 1F
//RRRRRGGGGGBBBBB
#define RED_LOW_BIT_MASK 0x0800
#define RED_HI_BIT_MASK 0x4000
#define GREEN_LOW_BIT_MASK 0x0020
#define GREEN_HI_BIT_MASK 0x0200
#define BLUE_LOW_BIT_MASK 0x0001
#define BLUE_HI_BIT_MASK 0x0010
#define FIRST_COLOR_MASK 0x7C00
#define SECOND_COLOR_MASK 0x03E0
#define THIRD_COLOR_MASK 0x001F
*/
#define RED_SHIFT_BITS 11
#define GREEN_SHIFT_BITS 6
#define RED_LOW_BIT_MASK 0x0800
#define GREEN_LOW_BIT_MASK 0x0020
#define BLUE_LOW_BIT_MASK 0x0001
#define RED_HI_BIT_MASK 0x8000
#define GREEN_HI_BIT_MASK 0x0400
#define BLUE_HI_BIT_MASK 0x0010
#define FIRST_COLOR_MASK 0xF800
#define SECOND_COLOR_MASK 0x07E0
#define THIRD_COLOR_MASK 0x001F
/* RGB332 format */
#define RED_SHIFT_BITS 5
#define GREEN_SHIFT_BITS 2
//#define RED_SHIFT_BITS 5
//#define GREEN_SHIFT_BITS 2
//842 184 21
//000 000 00
/*
#define RED_LOW_BIT_MASK 0x20
#define GREEN_LOW_BIT_MASK 0x04
#define BLUE_LOW_BIT_MASK 0x01
#define RED_HI_BIT_MASK 0x80
#define GREEN_HI_BIT_MASK 0x10
#define BLUE_HI_BIT_MASK 0x02
#define FIRST_COLOR_MASK 0xE0
#define SECOND_COLOR_MASK 0x1C
#define THIRD_COLOR_MASK 0x03
#define ALPHA_BITS_MASK 0x00
*/
//#define BUILD_PIXEL(R, G, B) (((SNESPixel)(R) << 5) /*| ((SNESPixel)(G) << 2) */ | (SNESPixel)(B>>3) )
#define BUILD_PIXEL(R, G, B) (((SNESPixel)(R&0x1C) << 3) | ((SNESPixel)(G&0x1C)) | (SNESPixel)(B>>3) )
#define RGB_LOW_BITS_MASK (RED_LOW_BIT_MASK | GREEN_LOW_BIT_MASK | BLUE_LOW_BIT_MASK)

Wyświetl plik

@ -0,0 +1,162 @@
/* Simple resampler based on bsnes's ruby audio library */
#ifndef __RESAMPLER_H
#define __RESAMPLER_H
#include "ring_buffer.h"
#undef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#undef CLAMP
#undef short_clamp
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#define short_clamp(n) ((short) CLAMP((n), -32768, 32767))
class Resampler : public ring_buffer
{
protected:
double r_step;
double r_frac;
int r_left[4], r_right[4];
double
hermite (double mu1, double a, double b, double c, double d)
{
const double tension = 0.0; //-1 = low, 0 = normal, 1 = high
const double bias = 0.0; //-1 = left, 0 = even, 1 = right
double mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1 + bias) * (1 - tension) / 2;
m0 += (c - b) * (1 - bias) * (1 - tension) / 2;
m1 = (c - b) * (1 + bias) * (1 - tension) / 2;
m1 += (d - c) * (1 - bias) * (1 - tension) / 2;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
public:
Resampler (int num_samples) : ring_buffer (num_samples << 1)
{
r_frac = 0.0;
}
~Resampler ()
{
}
void
time_ratio (double ratio)
{
r_step = ratio;
clear ();
}
void
clear (void)
{
ring_buffer::clear ();
r_frac = 0;
r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0;
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
}
void
read (short *data, int num_samples)
{
int i_position = start >> 1;
short *internal_buffer = (short *) buffer;
int o_position = 0;
int consumed = 0;
while (o_position < num_samples && consumed < buffer_size)
{
int s_left = internal_buffer[i_position];
int s_right = internal_buffer[i_position + 1];
const double margin_of_error = 1.0e-10;
if (fabs(r_step - 1.0) < margin_of_error)
{
data[o_position] = (short) s_left;
data[o_position + 1] = (short) s_right;
o_position += 2;
i_position = (i_position + 2) % (buffer_size >> 1);
consumed += 2;
continue;
}
r_left [0] = r_left [1];
r_left [1] = r_left [2];
r_left [2] = r_left [3];
r_left [3] = s_left;
r_right[0] = r_right[1];
r_right[1] = r_right[2];
r_right[2] = r_right[3];
r_right[3] = s_right;
while (r_frac <= 1.0 && o_position < num_samples)
{
data[o_position] = short_clamp (hermite (r_frac, r_left [0], r_left [1], r_left [2], r_left [3]));
data[o_position + 1] = short_clamp (hermite (r_frac, r_right[0], r_right[1], r_right[2], r_right[3]));
o_position += 2;
r_frac += r_step;
}
if (r_frac > 1.0)
{
r_frac -= 1.0;
i_position = (i_position + 2) % (buffer_size >> 1);
consumed += 2;
}
}
size -= consumed << 1;
start = (start + (consumed << 1)) % buffer_size;
}
bool
push (short *src, int num_samples)
{
if (max_write () < num_samples)
return false;
ring_buffer::push ((unsigned char *) src, num_samples << 1);
return true;
}
int
max_write (void)
{
return space_empty () >> 1;
}
void
resize (int num_samples)
{
ring_buffer::resize (num_samples << 1);
}
int
avail (void)
{
return (int) floor (((size >> 2) - r_frac) / r_step) * 2;
}
};
#endif /* __RESAMPLER_H */

Wyświetl plik

@ -0,0 +1,111 @@
/* Simple byte-based ring buffer. Licensed under public domain (C) BearOso. */
#ifndef __RING_BUFFER_H
#define __RING_BUFFER_H
#include <string.h>
#undef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
class ring_buffer
{
protected:
int size;
int buffer_size;
int start;
unsigned char *buffer;
public:
ring_buffer (int buffer_size)
{
this->buffer_size = buffer_size;
buffer = new unsigned char[this->buffer_size];
memset (buffer, 0, this->buffer_size);
size = 0;
start = 0;
}
~ring_buffer (void)
{
delete[] buffer;
}
bool
push (unsigned char *src, int bytes)
{
if (space_empty () < bytes)
return false;
int end = (start + size) % buffer_size;
int first_write_size = MIN (bytes, buffer_size - end);
memcpy (buffer + end, src, first_write_size);
if (bytes > first_write_size)
memcpy (buffer, src + first_write_size, bytes - first_write_size);
size += bytes;
return true;
}
bool
pull (unsigned char *dst, int bytes)
{
if (space_filled () < bytes)
return false;
memcpy (dst, buffer + start, MIN (bytes, buffer_size - start));
if (bytes > (buffer_size - start))
memcpy (dst + (buffer_size - start), buffer, bytes - (buffer_size - start));
start = (start + bytes) % buffer_size;
size -= bytes;
return true;
}
inline int
space_empty (void)
{
return buffer_size - size;
}
inline int
space_filled (void)
{
return size;
}
void
clear (void)
{
start = 0;
size = 0;
memset (buffer, 0, buffer_size);
}
void
resize (int size)
{
delete[] buffer;
buffer_size = size;
buffer = new unsigned char[buffer_size];
memset (buffer, 0, this->buffer_size);
size = 0;
start = 0;
}
inline void
cache_silence (void)
{
clear ();
size = buffer_size;
}
};
#endif

Wyświetl plik

@ -1,127 +0,0 @@
typedef int_least32_t blargg_long;
typedef uint_least32_t blargg_ulong;
#define GET_LE16( addr ) (*(uint16_t*) (addr))
#define GET_LE32( addr ) (*(uint32_t*) (addr))
#define SET_LE16( addr, data ) (void) (*(uint16_t*) (addr) = (data))
#define SET_LE32( addr, data ) (void) (*(uint32_t*) (addr) = (data))
#define GET_LE16SA( addr ) ((int16_t) GET_LE16( addr ))
#define GET_LE16A( addr ) GET_LE16( addr )
#define SET_LE16A( addr, data ) SET_LE16( addr, data )
class SMP
{
public:
unsigned frequency;
int32 clock;
static const uint8 iplrom[64];
uint32 registers[4];
uint8 *apuram;
uint8 *stack;
SMP();
~SMP();
void load_state(uint8 **);
void save_state(uint8 **);
void execute(int cycles = 0);
void power();
void reset();
private:
struct Flags
{
bool n, v, p, b, h, i, z, c;
inline operator unsigned() const
{
return (n << 7) | (v << 6) | (p << 5) | (b << 4) | (h << 3) | (i << 2) | (z << 1) | (c << 0);
};
inline unsigned operator=(unsigned data)
{
n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10;
h = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01;
return data;
}
inline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); }
inline unsigned operator^=(unsigned data) { return operator=(operator unsigned() ^ data); }
inline unsigned operator&=(unsigned data) { return operator=(operator unsigned() & data); }
};
uint32 opcode_cycle;
uint8 opcode_number;
uint32 ticks;
uint16 rd, wr, dp, sp, ya, bit;
struct Regs
{
uint32 pc;
uint8 sp;
union
{
uint16 ya;
#ifndef __BIG_ENDIAN__
struct { uint8 a, y; } B;
#else
struct { uint8 y, a; } B;
#endif
};
uint8 x;
Flags p;
} regs;
struct Status
{
bool iplrom_enable; // $00f1
unsigned dsp_addr; // $00f2
unsigned ram00f8; // $00f8
unsigned ram00f9; // $00f9
} status;
template <unsigned frequency>
struct Timer
{
bool enable;
unsigned target;
unsigned stage1_ticks;
unsigned stage2_ticks;
unsigned stage3_ticks;
inline void tick();
inline void tick(unsigned clocks);
};
Timer<128> timer0;
Timer<128> timer1;
Timer<16> timer2;
inline void tick();
inline void tick(unsigned clocks);
unsigned op_read(unsigned addr);
void op_write(unsigned addr, uint8 data);
inline uint8 op_adc(uint8 x, uint8 y);
inline uint16 op_addw(uint16 x, uint16 y);
inline uint8 op_and(uint8 x, uint8 y);
inline uint8 op_cmp(uint8 x, uint8 y);
inline uint16 op_cmpw(uint16 x, uint16 y);
inline uint8 op_eor(uint8 x, uint8 y);
inline uint8 op_inc(uint8 x);
inline uint8 op_dec(uint8 x);
inline uint8 op_or(uint8 x, uint8 y);
inline uint8 op_sbc(uint8 x, uint8 y);
inline uint16 op_subw(uint16 x, uint16 y);
inline uint8 op_asl(uint8 x);
inline uint8 op_lsr(uint8 x);
inline uint8 op_rol(uint8 x);
inline uint8 op_ror(uint8 x);
};
extern SMP smp;

Wyświetl plik

@ -11,6 +11,7 @@ extern "C" {
// Emulation includes
#include "snes9x.h"
#include "apu.h"
static int ik; // joypad key
static int pik=0;
@ -46,9 +47,9 @@ void emu_KeyboardOnUp(int keymodifer, int key) {
void snes_Init(void)
{
s9x_init();
#ifdef HAS_SND
#endif
#ifdef HAS_SND
emu_sndInit();
#endif
}
@ -72,68 +73,115 @@ void snes_Start(char * filename)
emu_printf("emu started");
}
static int prev_hk = 0;
void snes_Step(void)
{
int k=ik;
#ifdef TEECOMPUTER
int hk = ihk;
if (hk == 'q') { // SELECT
S9xReportButton(7, true);
//emu_printf("selectd");
}
else if (hk == 'w') { // START
S9xReportButton(6, true);
//emu_printf("startd");
}
else {
if (prev_hk == 'q') {
S9xReportButton(7, false);
//emu_printf("selectu");
}
else if (prev_hk == 'w') {
S9xReportButton(6, false);
//emu_printf("startu");
}
}
prev_hk = hk;
// Ignore joypad if shift/fn is pressed!!!
if ( !(k & MASK_KEY_USER1) && !(k & MASK_KEY_USER2) )
//if ( !(k & MASK_KEY_USER1) && !(k & MASK_KEY_USER2) )
#endif
{
if ( !(pik & MASK_JOY2_BTN) && (k & MASK_JOY2_BTN) ) {
S9xReportButton(5, true);
if ( ( !(pik & MASK_JOY2_BTN) && (k & MASK_JOY2_BTN) ) || ( !(pik & MASK_JOY1_BTN) && (k & MASK_JOY1_BTN) ) ) {
S9xReportButton(9, true);
}
else if ( (pik & MASK_JOY2_BTN) && !(k & MASK_JOY2_BTN) ) {
S9xReportButton(5, false);
else if ( ( (pik & MASK_JOY2_BTN) && !(k & MASK_JOY2_BTN) ) || ( (pik & MASK_JOY1_BTN) && !(k & MASK_JOY1_BTN) ) ) {
S9xReportButton(9, false);
}
if ( !(pik & MASK_JOY2_UP) && (k & MASK_JOY2_UP) ) {
if ( ( !(pik & MASK_JOY2_UP) && (k & MASK_JOY2_UP) ) || ( !(pik & MASK_JOY1_UP) && (k & MASK_JOY1_UP) ) ) {
S9xReportButton(2, true);
}
else if ( (pik & MASK_JOY2_UP) && !(k & MASK_JOY2_UP) ) {
else if ( ( (pik & MASK_JOY2_UP) && !(k & MASK_JOY2_UP) ) || ( (pik & MASK_JOY1_UP) && !(k & MASK_JOY1_UP) ) ) {
S9xReportButton(2, false);
}
if ( !(pik & MASK_JOY2_DOWN) && (k & MASK_JOY2_DOWN) ) {
if ( ( !(pik & MASK_JOY2_DOWN) && (k & MASK_JOY2_DOWN) ) || ( !(pik & MASK_JOY1_DOWN) && (k & MASK_JOY1_DOWN) ) ) {
S9xReportButton(3, true);
}
else if ( (pik & MASK_JOY2_DOWN) && !(k & MASK_JOY2_DOWN) ) {
else if ( ( (pik & MASK_JOY2_DOWN) && !(k & MASK_JOY2_DOWN) ) || ( (pik & MASK_JOY1_DOWN) && !(k & MASK_JOY1_DOWN) ) ) {
S9xReportButton(3, false);
}
if ( !(pik & MASK_JOY2_RIGHT) && (k & MASK_JOY2_RIGHT) ) {
if ( ( !(pik & MASK_JOY2_RIGHT) && (k & MASK_JOY2_RIGHT) ) || ( !(pik & MASK_JOY1_RIGHT) && (k & MASK_JOY1_RIGHT) ) ) {
S9xReportButton(1, true);
}
else if ( (pik & MASK_JOY2_RIGHT) && !(k & MASK_JOY2_RIGHT) ) {
else if ( ( (pik & MASK_JOY2_RIGHT) && !(k & MASK_JOY2_RIGHT) ) || ( (pik & MASK_JOY1_RIGHT) && !(k & MASK_JOY1_RIGHT) ) ) {
S9xReportButton(1, false);
}
if ( !(pik & MASK_JOY2_LEFT) && (k & MASK_JOY2_LEFT) ) {
if ( ( !(pik & MASK_JOY2_LEFT) && (k & MASK_JOY2_LEFT) ) || ( !(pik & MASK_JOY1_LEFT) && (k & MASK_JOY1_LEFT) ) ) {
S9xReportButton(0, true);
}
else if ( (pik & MASK_JOY2_LEFT) && !(k & MASK_JOY2_LEFT) ) {
else if ( ( (pik & MASK_JOY2_LEFT) && !(k & MASK_JOY2_LEFT) ) || ( (pik & MASK_JOY1_LEFT) && !(k & MASK_JOY1_LEFT) ) ) {
S9xReportButton(0, false);
}
}
#ifdef TEECOMPUTER
// But 2
if ( !(pik & MASK_KEY_USER1) && (k & MASK_KEY_USER1) ) {
S9xReportButton(4, true);
S9xReportButton(5, true);
}
else if ( (pik & MASK_KEY_USER1) && !(k & MASK_KEY_USER1) ) {
S9xReportButton(4, false);
S9xReportButton(5, false);
}
// But 3
if ( !(pik & MASK_KEY_USER2) && (k & MASK_KEY_USER2) ) {
S9xReportButton(7, true);
}
else if ( (pik & MASK_KEY_USER2) && !(k & MASK_KEY_USER2) ) {
S9xReportButton(7, false);
}
// But 4
if ( !(pik & MASK_KEY_USER3) && (k & MASK_KEY_USER3) ) {
S9xReportButton(4, true);
}
else if ( (pik & MASK_KEY_USER3) && !(k & MASK_KEY_USER3) ) {
S9xReportButton(4, false);
}
#else
// But 2
if ( !(pik & MASK_KEY_USER1) && (k & MASK_KEY_USER1) ) {
S9xReportButton(5, true);
}
else if ( (pik & MASK_KEY_USER1) && !(k & MASK_KEY_USER1) ) {
S9xReportButton(5, false);
}
// Start
if ( !(pik & MASK_KEY_USER2) && (k & MASK_KEY_USER2) ) {
S9xReportButton(6, true);
}
else if ( (pik & MASK_KEY_USER2) && !(k & MASK_KEY_USER2) ) {
S9xReportButton(6, false);
}
#endif
pik = k;
s9x_step();
#ifdef HAS_SND
#endif
}
#ifdef HAS_SND
void SND_Process( void * stream, int len )
{
S9xMixSamples(stream, len);
}
#endif

Wyświetl plik

@ -0,0 +1,58 @@
#ifndef __SNES_HPP
#define __SNES_HPP
#include "snes9x.h"
//#include "../../resampler.h"
//#include "../../../msu1.h"
#if defined(__GNUC__)
#define inline inline
#define alwaysinline inline __attribute__((always_inline))
#elif defined(_MSC_VER)
#define inline inline
#define alwaysinline inline __forceinline
#else
#define inline inline
#define alwaysinline inline
#endif
#define debugvirtual
namespace SNES
{
struct Processor
{
unsigned frequency;
int32 clock;
};
#include "smp.hpp"
#include "sdsp.hpp"
class CPU
{
public:
uint8 registers[4];
inline void reset ()
{
registers[0] = registers[1] = registers[2] = registers[3] = 0;
}
alwaysinline void port_write (uint8 port, uint8 data)
{
registers[port & 3] = data;
}
alwaysinline uint8 port_read (uint8 port)
{
return registers[port & 3];
}
};
extern CPU cpu;
} // namespace SNES
#endif

Wyświetl plik

@ -22,7 +22,7 @@ void S9xInitSettings(void)
memset(&Settings, 0, sizeof(Settings));
// Sound
Settings.SoundSync = true;
Settings.SoundSync = false;
Settings.Stereo = true;
Settings.SoundPlaybackRate = 32000;
Settings.SoundInputRate = 31950;
@ -49,18 +49,25 @@ void S9xInitSettings(void)
void s9x_init(void) {
S9xInitSettings();
Settings.Stereo = FALSE;
Settings.Stereo = true;
Settings.SoundPlaybackRate = AUDIO_SAMPLE_RATE;
Settings.SoundSync = FALSE;
Settings.Mute = TRUE;
Settings.Transparency = TRUE;
Settings.SkipFrames = 0;
Settings.Paused = FALSE;
Settings.SoundSync = false;
Settings.Mute = false;
Settings.Transparency = true;
Settings.SkipFrames = 1;
Settings.Paused = false;
Settings.SixteenBitSound = true;
Settings.ReverseStereo = false;
Settings.PAL = false;
if (!S9xMemoryInit())
emu_printf("Memory init failed!");
if (!S9xSoundInit(0))
if (!S9xInitAPU())
emu_printf("APU init failed!");
if (!S9xInitSound(40,0))
emu_printf("Sound init failed!");
if (!S9xGraphicsInit())
@ -80,12 +87,24 @@ void s9x_step(void) {
{
emu_DrawVsync();
}
IPPU.RenderThisFrame = (((++frames_counter) & 1) == 1);
//GFX.Screen = (uint16*)currentUpdate->buffer;
if (++frames_counter == Settings.SkipFrames)
{
frames_counter = 0;
IPPU.RenderThisFrame = true;
}
else
{
IPPU.RenderThisFrame = false;
}
//IPPU.RenderThisFrame = (((++frames_counter) & Settings.SkipFrames) == Settings.SkipFrames);
//GFX.Screen = (uint16*)currentUpdate->buffer;
}
bool8 S9xOpenSoundDevice(void) {
return (TRUE);
}
void S9xMessage(int, int, char const*) {
@ -99,6 +118,8 @@ void S9xExit(void) {
}
bool8 S9xBlitUpdate(int width, int height)
{
return (TRUE);

Wyświetl plik

@ -174,7 +174,13 @@ struct SSettings
bool8 TurboMode;
bool8 DisableGameSpecificHacks;
bool8 SixteenBitSound;
bool8 ReverseStereo;
bool8 PAL;
uint16 InterpolationMethod;
bool8 SeparateEchoBuffer;
bool8 MSU1;
#ifdef DEBUGGER
bool8 TraceDMA;
bool8 TraceHDMA;

Wyświetl plik

@ -49,6 +49,7 @@ void emu_DrawVsync(void)
skip &= VID_FRAME_SKIP;
if (!vgaMode) {
#ifdef HAS_T4_VGA
//while (vbl==vb) {};
tft.waitSync();
#else
while (vbl==vb) {};
@ -160,7 +161,7 @@ void loop(void)
tft.fillScreenNoDma( RGBVAL16(0x00,0x00,0x00) );
tft.startDMA();
emu_Init(filename);
myTimer.begin(vblCount, 20000); //to run every 20ms
myTimer.begin(vblCount, 16666); //to run every 20ms
}
delay(20);
}
@ -168,7 +169,7 @@ void loop(void)
uint16_t bClick = emu_DebounceLocalKeys();
emu_Input(bClick);
emu_Step();
delay(10);
//delay(10);
//uint16_t bClick = emu_DebounceLocalKeys();
//if (bClick & MASK_KEY_USER1) {
// emu_Input(bClick);

Wyświetl plik

@ -165,9 +165,10 @@ void S9UpdateLineMatrix(int line)
#define COLOR_ADD1_2(C1, C2) \
((((((C1) & RGB_REMOVE_LOW_BITS_MASK) + \
((C2) & RGB_REMOVE_LOW_BITS_MASK)) >> 1) + \
((C1) & (C2) & RGB_LOW_BITS_MASK)) | ALPHA_BITS_MASK)
((C1) & (C2) & RGB_LOW_BITS_MASK)) )
#define COLOR_ADD_BRIGHTNESS1_2 COLOR_ADD1_2
static inline uint16 COLOR_ADD_BRIGHTNESS(uint32 C1, uint32 C2)
{
return ((brightness_cap[ (C1 >> RED_SHIFT_BITS) + (C2 >> RED_SHIFT_BITS) ] << RED_SHIFT_BITS) |
@ -181,6 +182,8 @@ static inline uint16 COLOR_ADD_BRIGHTNESS(uint32 C1, uint32 C2)
static inline uint16 COLOR_ADD(uint32 C1, uint32 C2)
{
// return C1;
const uint32 RED_MASK = 0x1F << RED_SHIFT_BITS;
const uint32 GREEN_MASK = 0x1F << GREEN_SHIFT_BITS;
const uint32 BLUE_MASK = 0x1F;
@ -194,7 +197,12 @@ static inline uint16 COLOR_ADD(uint32 C1, uint32 C2)
#if GREEN_SHIFT_BITS == 6
retval |= (retval & 0x0400) >> 5;
#endif
// int rr = (retval & 0xF800) >> (2+11);
// int gg = (retval & 0x07E0) >> (3+5);
// int bb = (retval & 0x001F) >> 3 ;
// retval = (rr << 5) + (gg << 2) + bb;
return retval;
}
#define COLOR_SUB1_2(C1, C2) \
@ -203,6 +211,8 @@ static inline uint16 COLOR_ADD(uint32 C1, uint32 C2)
static inline uint16 COLOR_SUB (uint32 C1, uint32 C2)
{
// return C1;
int rb1 = (C1 & (THIRD_COLOR_MASK | FIRST_COLOR_MASK)) | ((0x20 << 0) | (0x20 << RED_SHIFT_BITS));
int rb2 = C2 & (THIRD_COLOR_MASK | FIRST_COLOR_MASK);
int rb = rb1 - rb2;
@ -213,7 +223,12 @@ static inline uint16 COLOR_SUB (uint32 C1, uint32 C2)
#if GREEN_SHIFT_BITS == 6
retval |= (retval & 0x0400) >> 5;
#endif
// int rr = (retval & 0xF800) >> (2+11);
// int gg = (retval & 0x07E0) >> (3+5);
// int bb = (retval & 0x001F) >> 3 ;
// retval = (rr << 5) + (gg << 2) + bb;
return retval;
}
// Here are the tile converters, selected by S9xSelectTileConverter().