kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
add SNES and Atari Lynx emulation
rodzic
2a69fb40ec
commit
f5208fa857
|
@ -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++))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#undef INTSET
|
||||
#undef PS
|
||||
|
||||
#include "system.h"
|
||||
#include "lynxdef.h"
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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:;
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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, ®S [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 ®S [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"
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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)
|
||||
};
|
|
@ -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!
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
Plik diff jest za duży
Load Diff
|
@ -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;
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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().
|
||||
|
|
Ładowanie…
Reference in New Issue