diff --git a/MCUME_teensy41/teensy64/emuapi.cpp b/MCUME_teensy41/teensy64/emuapi.cpp index c367cb0..a374cf7 100644 --- a/MCUME_teensy41/teensy64/emuapi.cpp +++ b/MCUME_teensy41/teensy64/emuapi.cpp @@ -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++)) diff --git a/MCUME_teensy41/teensy64/tft_t_dma.cpp b/MCUME_teensy41/teensy64/tft_t_dma.cpp index 176962e..2d46f97 100644 --- a/MCUME_teensy41/teensy64/tft_t_dma.cpp +++ b/MCUME_teensy41/teensy64/tft_t_dma.cpp @@ -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; } diff --git a/MCUME_teensy41/teensy64/tft_t_dma.h b/MCUME_teensy41/teensy64/tft_t_dma.h index f03dcb8..b16fcf2 100644 --- a/MCUME_teensy41/teensy64/tft_t_dma.h +++ b/MCUME_teensy41/teensy64/tft_t_dma.h @@ -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 - - diff --git a/MCUME_teensy41/teensyhandy/.DS_Store b/MCUME_teensy41/teensyhandy/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/MCUME_teensy41/teensyhandy/.DS_Store differ diff --git a/MCUME_teensy41/teensyhandy/AudioPlaySystem.cpp b/MCUME_teensy41/teensyhandy/AudioPlaySystem.cpp new file mode 100644 index 0000000..85da04d --- /dev/null +++ b/MCUME_teensy41/teensyhandy/AudioPlaySystem.cpp @@ -0,0 +1,415 @@ +#include "emuapi.h" + +#ifdef HAS_SND + +#include "AudioPlaySystem.h" +#include +#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>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 diff --git a/MCUME_teensy41/teensyhandy/AudioPlaySystem.h b/MCUME_teensy41/teensyhandy/AudioPlaySystem.h new file mode 100644 index 0000000..6d2e23b --- /dev/null +++ b/MCUME_teensy41/teensyhandy/AudioPlaySystem.h @@ -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 diff --git a/MCUME_teensy41/teensyhandy/c6502mak.h b/MCUME_teensy41/teensyhandy/c6502mak.h new file mode 100644 index 0000000..e7bf411 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/c6502mak.h @@ -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;\ +} + diff --git a/MCUME_teensy41/teensyhandy/c65c02.h b/MCUME_teensy41/teensyhandy/c65c02.h new file mode 100644 index 0000000..755ae05 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/c65c02.h @@ -0,0 +1,1830 @@ +// +// 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 Emulation class // +////////////////////////////////////////////////////////////////////////////// +// // +// This class emulates a 65C02 processor. It is interfaced to the rest of // +// the system via the PEEK/POKE macros and a number of global variables // +// // +// K. Wilkins // +// August 1997 // +// // +////////////////////////////////////////////////////////////////////////////// +// Revision History: // +// ----------------- // +// // +// 01Aug1997 KW Document header added & class documented. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef C65C02_H +#define C65C02_H + +//#include +//#define TRACE_CPU + +#ifdef TRACE_CPU + +#define TRACE_CPU0(msg) _RPT1(_CRT_WARN,"C65C02::"msg" (Time=%012d)\n",gSystemCycleCount) +#define TRACE_CPU1(msg,arg1) _RPT2(_CRT_WARN,"C65C02::"msg" (Time=%012d)\n",arg1,gSystemCycleCount) +#define TRACE_CPU2(msg,arg1,arg2) _RPT3(_CRT_WARN,"C65C02::"msg" (Time=%012d)\n",arg1,arg2,gSystemCycleCount) +#define TRACE_CPU3(msg,arg1,arg2,arg3) _RPT4(_CRT_WARN,"C65C02::"msg" (Time=%012d)\n",arg1,arg2,arg3,gSystemCycleCount) + +#else + +#define TRACE_CPU0(msg) +#define TRACE_CPU1(msg,arg1) +#define TRACE_CPU2(msg,arg1,arg2) +#define TRACE_CPU3(msg,arg1,arg2,arg3) + +#endif + +// +// Handy definitions +// + +#define NMI_VECTOR 0xfffa +#define BOOT_VECTOR 0xfffc +#define IRQ_VECTOR 0xfffe + +#define MAX_CPU_BREAKPOINTS 8 + +#define CPU_RDWR_CYC 5 + +// +// ACCESS MACROS +// +#define CPU_PEEK_RAM(m) (mRamPointer[m]) +#define CPU_POKE_RAM(m1,m2) mRamPointer[m1]=m2 + +#define CPU_PEEK(m) (((m<0xfc00)?CPU_PEEK_RAM(m):mSystem.Peek_CPU(m))) +#define CPU_PEEKW(m) (((m<0xfc00)?(mRamPointer[m]+(mRamPointer[m+1]<<8)):mSystem.PeekW_CPU(m))) +#define CPU_POKE(m1,m2) {if(m1<0xfc00) CPU_POKE_RAM(m1,m2); else mSystem.Poke_CPU(m1,m2);} + + +enum +{ + illegal=0, + accu, + imm, + absl, + zp, + zpx, + zpy, + absx, + absy, + iabsx, + impl, + rel, + zrel, + indx, + indy, + iabs, + ind +}; + +typedef struct +{ + int PS; // Processor status register 8 bits + int A; // Accumulator 8 bits + int X; // X index register 8 bits + int Y; // Y index register 8 bits + int SP; // Stack Pointer 8 bits + int Opcode; // Instruction opcode 8 bits + int Operand;// Intructions operand 16 bits + int PC; // Program Counter 16 bits + bool NMI; + bool IRQ; + bool WAIT; +#ifdef _LYNXDBG + int cpuBreakpoints[MAX_CPU_BREAKPOINTS]; +#endif +}C6502_REGS; + +// +// The CPU emulation macros +// +#include "c6502mak.h" +// +// The CPU emulation macros +// + +class C65C02 +{ + public: + C65C02(CSystemBase& parent) + :mSystem(parent) + { + TRACE_CPU0("C65C02()"); + // Compute the BCD lookup table + // for(UWORD t=0;t<256;++t) + // { + // mBCDTable[0][t]=((t >> 4) * 10) + (t & 0x0f); + // mBCDTable[1][t]=(((t % 100) / 10) << 4) | (t % 10); + // } +#ifdef _LYNXDBG + for(int loop=0;loop>8); + // CPU_POKE(0x0100+mSP--,mPC&0x00ff); + // CPU_POKE(0x0100+mSP--,PS()); + // + // // Pick up the new PC + // mPC=CPU_PEEKW(NMI_VECTOR); + // } + + if(gSystemIRQ && !mI) + { + TRACE_CPU1("Update() IRQ taken at PC=%04x",mPC); + // IRQ signal clearance is handled by CMikie::Update() as this + // is the only source of interrupts + + // Push processor status + PUSH(mPC>>8); + PUSH(mPC&0xff); + PUSH(PS()&0xef); // Clear B flag on stack + + mI=TRUE; // Stop further interrupts + mD=FALSE; // Clear decimal mode + + // Pick up the new PC + mPC=CPU_PEEKW(IRQ_VECTOR); + + // Save the sleep state as an irq has possibly woken the processor + gSystemCPUSleep_Saved=gSystemCPUSleep; + gSystemCPUSleep=FALSE; + + // Log the irq entry time + gIRQEntryCycle=gSystemCycleCount; + + // Clear the interrupt status line + gSystemIRQ=FALSE; + } + + // + // If the CPU is asleep then skip to the next timer event + // + if(gSystemCPUSleep) return; + + // Fetch opcode + mOpcode=CPU_PEEK(mPC); + TRACE_CPU2("Update() PC=$%04x, Opcode=%02x",mPC,mOpcode); + mPC++; + + // Execute Opcode + + switch(mOpcode) + { + + // + // 0x00 + // + case 0x00: + gSystemCycleCount+=(1+(6*CPU_RDWR_CYC)); + // IMPLIED + xBRK(); + break; + case 0x01: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_X(); + xORA(); + break; + case 0x02: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x03: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x04: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xZEROPAGE(); + xTSB(); + break; + case 0x05: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xORA(); + break; + case 0x06: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xZEROPAGE(); + xASL(); + break; + case 0x07: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x08: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + // IMPLIED + xPHP(); + break; + case 0x09: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xIMMEDIATE(); + xORA(); + break; + case 0x0A: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xASLA(); + break; + case 0x0B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x0C: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xTSB(); + break; + case 0x0D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xORA(); + break; + case 0x0E: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xASL(); + break; + case 0x0F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x10 + // + case 0x10: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBPL(); + break; + case 0x11: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT_Y(); + xORA(); + break; + case 0x12: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT(); + xORA(); + break; + case 0x13: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x14: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xZEROPAGE(); + xTRB(); + break; + case 0x15: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xORA(); + break; + case 0x16: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xASL(); + break; + case 0x17: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x18: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xCLC(); + break; + case 0x19: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xORA(); + break; + case 0x1A: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xINCA(); + break; + case 0x1B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x1C: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xTRB(); + break; + case 0x1D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xORA(); + break; + case 0x1E: + gSystemCycleCount+=(1+(6*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xASL(); + break; + case 0x1F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x20 + // + case 0x20: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xJSR(); + break; + case 0x21: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_X(); + xAND(); + break; + case 0x22: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x23: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x24: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xBIT(); + break; + case 0x25: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xAND(); + break; + case 0x26: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xZEROPAGE(); + xROL(); + break; + case 0x27: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x28: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // IMPLIED + xPLP(); + break; + case 0x29: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xAND(); + break; + case 0x2A: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xROLA(); + break; + case 0x2B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x2C: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xBIT(); + break; + case 0x2D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xAND(); + break; + case 0x2E: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xROL(); + break; + case 0x2F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x30 + // + case 0x30: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBMI(); + break; + case 0x31: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT_Y(); + xAND(); + break; + case 0x32: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT(); + xAND(); + break; + case 0x33: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x34: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xBIT(); + break; + case 0x35: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xAND(); + break; + case 0x36: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xROL(); + break; + case 0x37: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x38: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xSEC(); + break; + case 0x39: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xAND(); + break; + case 0x3A: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xDECA(); + break; + case 0x3B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x3C: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xBIT(); + break; + case 0x3D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xAND(); + break; + case 0x3E: + gSystemCycleCount+=(1+(6*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xROL(); + break; + case 0x3F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x40 + // + case 0x40: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + // Only clear IRQ if this is not a BRK instruction based RTI + + // B flag is on the stack cant test the flag + int tmp; + PULL(tmp); + PUSH (tmp); + if(!(tmp&0x10)) + { + gSystemCPUSleep=gSystemCPUSleep_Saved; + + // If were in sleep mode then we need to push the + // wakeup counter along by the same number of cycles + // we have used during the sleep period + if(gSystemCPUSleep) + { + gCPUWakeupTime+=gSystemCycleCount-gIRQEntryCycle; + } + } + // IMPLIED + xRTI(); + break; + case 0x41: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_X(); + xEOR(); + break; + case 0x42: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x43: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x44: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x45: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xEOR(); + break; + case 0x46: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xZEROPAGE(); + xLSR(); + break; + case 0x47: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x48: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + // IMPLIED + xPHA(); + break; + case 0x49: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xEOR(); + break; + case 0x4A: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xLSRA(); + break; + case 0x4B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x4C: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xABSOLUTE(); + xJMP(); + break; + case 0x4D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xEOR(); + break; + case 0x4E: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xLSR(); + break; + case 0x4F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x50 + // + case 0x50: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBVC(); + break; + case 0x51: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT_Y(); + xEOR(); + break; + case 0x52: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT(); + xEOR(); + break; + case 0x53: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x54: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x55: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xEOR(); + break; + case 0x56: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xLSR(); + break; + case 0x57: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x58: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xCLI(); + break; + case 0x59: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xEOR(); + break; + case 0x5A: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + // IMPLIED + xPHY(); + break; + case 0x5B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x5C: + gSystemCycleCount+=(1+(7*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x5D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xEOR(); + break; + case 0x5E: + gSystemCycleCount+=(1+(6*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xLSR(); + break; + case 0x5F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x60 + // + case 0x60: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + // IMPLIED + xRTS(); + break; + case 0x61: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_X(); + xADC(); + break; + case 0x62: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x63: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x64: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xSTZ(); + break; + case 0x65: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xADC(); + break; + case 0x66: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xZEROPAGE(); + xROR(); + break; + case 0x67: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x68: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // IMPLIED + xPLA(); + break; + case 0x69: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xADC(); + break; + case 0x6A: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xRORA(); + break; + case 0x6B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x6C: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_ABSOLUTE(); + xJMP(); + break; + case 0x6D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xADC(); + break; + case 0x6E: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xROR(); + break; + case 0x6F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x70 + // + case 0x70: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBVS(); + break; + case 0x71: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT_Y(); + xADC(); + break; + case 0x72: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT(); + xADC(); + break; + case 0x73: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x74: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xSTZ(); + break; + case 0x75: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xADC(); + break; + case 0x76: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xROR(); + break; + case 0x77: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x78: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xSEI(); + break; + case 0x79: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xADC(); + break; + case 0x7A: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // IMPLIED + xPLY(); + break; + case 0x7B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x7C: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_ABSOLUTE_X(); + xJMP(); + break; + case 0x7D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xADC(); + break; + case 0x7E: + gSystemCycleCount+=(1+(6*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xROR(); + break; + case 0x7F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x80 + // + case 0x80: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBRA(); + break; + case 0x81: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_X(); + xSTA(); + break; + case 0x82: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x83: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x84: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xSTY(); + break; + case 0x85: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xSTA(); + break; + case 0x86: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xSTX(); + break; + case 0x87: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x88: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xDEY(); + break; + case 0x89: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xBIT(); + break; + case 0x8A: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xTXA(); + break; + case 0x8B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x8C: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xSTY(); + break; + case 0x8D: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xSTA(); + break; + case 0x8E: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xSTX(); + break; + case 0x8F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0x90 + // + case 0x90: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBCC(); + break; + case 0x91: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_Y(); + xSTA(); + break; + case 0x92: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT(); + xSTA(); + break; + case 0x93: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x94: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xSTY(); + break; + case 0x95: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xSTA(); + break; + case 0x96: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_Y(); + xSTX(); + break; + case 0x97: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0x98: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xTYA(); + break; + case 0x99: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xSTA(); + break; + case 0x9A: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xTXS(); + break; + case 0x9B: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0x9C: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xSTZ(); + break; + case 0x9D: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xSTA(); + break; + case 0x9E: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xSTZ(); + break; + case 0x9F: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0xA0 + // + case 0xA0: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xLDY(); + break; + case 0xA1: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_X(); + xLDA(); + break; + case 0xA2: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xLDX(); + break; + case 0xA3: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xA4: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xLDY(); + break; + case 0xA5: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xLDA(); + break; + case 0xA6: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xLDX(); + break; + case 0xA7: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0xA8: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xTAY(); + break; + case 0xA9: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xLDA(); + break; + case 0xAA: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xTAX(); + break; + case 0xAB: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xAC: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xLDY(); + break; + case 0xAD: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xLDA(); + break; + case 0xAE: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xLDX(); + break; + case 0xAF: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0xB0 + // + case 0xB0: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBCS(); + break; + case 0xB1: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT_Y(); + xLDA(); + break; + case 0xB2: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT(); + xLDA(); + break; + case 0xB3: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xB4: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xLDY(); + break; + case 0xB5: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xLDA(); + break; + case 0xB6: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_Y(); + xLDX(); + break; + case 0xB7: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0xB8: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xCLV(); + break; + case 0xB9: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xLDA(); + break; + case 0xBA: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xTSX(); + break; + case 0xBB: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xBC: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xLDY(); + break; + case 0xBD: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xLDA(); + break; + case 0xBE: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xLDX(); + break; + case 0xBF: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0xC0 + // + case 0xC0: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xCPY(); + break; + case 0xC1: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_X(); + xCMP(); + break; + case 0xC2: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xC3: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xC4: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xCPY(); + break; + case 0xC5: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xCMP(); + break; + case 0xC6: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xZEROPAGE(); + xDEC(); + break; + case 0xC7: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0xC8: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xINY(); + break; + case 0xC9: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xCMP(); + break; + case 0xCA: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xDEX(); + break; + case 0xCB: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xWAI(); + break; + case 0xCC: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xCPY(); + break; + case 0xCD: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xCMP(); + break; + case 0xCE: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xDEC(); + break; + case 0xCF: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0xD0 + // + case 0xD0: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBNE(); + break; + case 0xD1: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT_Y(); + xCMP(); + break; + case 0xD2: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT(); + xCMP(); + break; + case 0xD3: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xD4: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xD5: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xCMP(); + break; + case 0xD6: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xDEC(); + break; + case 0xD7: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0xD8: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xCLD(); + break; + case 0xD9: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xCMP(); + break; + case 0xDA: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + // IMPLIED + xPHX(); + break; + case 0xDB: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xSTP(); + break; + case 0xDC: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xDD: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xCMP(); + break; + case 0xDE: + gSystemCycleCount+=(1+(6*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xDEC(); + break; + case 0xDF: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0xE0 + // + case 0xE0: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xCPX(); + break; + case 0xE1: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xINDIRECT_X(); + xSBC(); + break; + case 0xE2: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xE3: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xE4: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xCPX(); + break; + case 0xE5: + gSystemCycleCount+=(1+(2*CPU_RDWR_CYC)); + xZEROPAGE(); + xSBC(); + break; + case 0xE6: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xZEROPAGE(); + xINC(); + break; + case 0xE7: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0xE8: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xINX(); + break; + case 0xE9: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + xIMMEDIATE(); + xSBC(); + break; + case 0xEA: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xNOP(); + break; + case 0xEB: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xEC: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xCPX(); + break; + case 0xED: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE(); + xSBC(); + break; + case 0xEE: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xABSOLUTE(); + xINC(); + break; + case 0xEF: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + // + // 0xF0 + // + case 0xF0: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // RELATIVE (IN FUNCTION) + xBEQ(); + break; + case 0xF1: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT_Y(); + xSBC(); + break; + case 0xF2: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + xINDIRECT(); + xSBC(); + break; + case 0xF3: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xF4: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xF5: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xSBC(); + break; + case 0xF6: + gSystemCycleCount+=(1+(5*CPU_RDWR_CYC)); + xZEROPAGE_X(); + xINC(); + break; + case 0xF7: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + + case 0xF8: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // IMPLIED + xSED(); + break; + case 0xF9: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_Y(); + xSBC(); + break; + case 0xFA: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // IMPLIED + xPLX(); + break; + case 0xFB: + gSystemCycleCount+=(1+(1*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xFC: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + case 0xFD: + gSystemCycleCount+=(1+(3*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xSBC(); + break; + case 0xFE: + gSystemCycleCount+=(1+(6*CPU_RDWR_CYC)); + xABSOLUTE_X(); + xINC(); + break; + case 0xFF: + gSystemCycleCount+=(1+(4*CPU_RDWR_CYC)); + // *** ILLEGAL *** + xILLEGAL(); + break; + } + +#ifdef _LYNXDBG + + // Trigger breakpoint if required + + for(int loop=0;loop +#include +#include +#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< _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 diff --git a/MCUME_teensy41/teensyhandy/eeprom.cpp b/MCUME_teensy41/teensyhandy/eeprom.cpp new file mode 100644 index 0000000..4ff42f8 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/eeprom.cpp @@ -0,0 +1,316 @@ +////////////////////////////////////////////////////////////////////////////// +// Lynx 3wire EEPROM Class // +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#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; + } + } +} diff --git a/MCUME_teensy41/teensyhandy/eeprom.h b/MCUME_teensy41/teensyhandy/eeprom.h new file mode 100644 index 0000000..811d557 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/eeprom.h @@ -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 diff --git a/MCUME_teensy41/teensyhandy/emuapi.cpp b/MCUME_teensy41/teensyhandy/emuapi.cpp new file mode 100644 index 0000000..78c6cc1 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/emuapi.cpp @@ -0,0 +1,1764 @@ +#define KEYMAP_PRESENT 1 + +extern "C" { + #include "emuapi.h" + #include "iopins.h" +} + +#ifdef HAS_T4_VGA +#include "vga_t_dma.h" +#else +#include "tft_t_dma.h" +#endif + +#ifdef HAS_USBKEY +#include "USBHost_t36.h" // Read this header first for key info +USBHost myusb; +USBHub hub1(myusb); +KeyboardController keyboard1(myusb); +USBHIDParser hid1(myusb); +MouseController mouse1(myusb); +MIDIDevice midi1(myusb); +#endif + +static bool emu_writeConfig(void); +static bool emu_readConfig(void); +static bool emu_eraseConfig(void); + +static bool mouseDetected = false; +static bool keyboardDetected = false; +static uint8_t usbnavpad=0; + +#include +static File file; + +#define MAX_FILES 64 +#define AUTORUN_FILENAME "autorun.txt" + +#define MAX_FILENAME_SIZE 34 +#define MAX_MENULINES 9 +#define TEXT_HEIGHT 16 +#define TEXT_WIDTH 8 +#define MENU_FILE_XOFFSET (2*TEXT_WIDTH) +#define MENU_FILE_YOFFSET (2*TEXT_HEIGHT) +#define MENU_FILE_W (MAX_FILENAME_SIZE*TEXT_WIDTH) +#define MENU_FILE_H (MAX_MENULINES*TEXT_HEIGHT) +#define MENU_FILE_BGCOLOR RGBVAL16(0x00,0x00,0x40) +#define MENU_JOYS_YOFFSET (12*TEXT_HEIGHT) +#define MENU_VBAR_XOFFSET (0*TEXT_WIDTH) +#define MENU_VBAR_YOFFSET (MENU_FILE_YOFFSET) + +#define MENU_TFT_XOFFSET (MENU_FILE_XOFFSET+MENU_FILE_W+8) +#define MENU_TFT_YOFFSET (MENU_VBAR_YOFFSET+32) +#define MENU_VGA_XOFFSET (MENU_FILE_XOFFSET+MENU_FILE_W+8) +#define MENU_VGA_YOFFSET (MENU_VBAR_YOFFSET+MENU_FILE_H-32-37) + +extern TFT_T_DMA tft; + +static int nbFiles=0; +static int curFile=0; +static int topFile=0; +static char selection[MAX_FILENAME_PATH]=""; +static char second_selection[MAX_FILENAME_PATH]=""; +static char files[MAX_FILES][MAX_FILENAME_SIZE]; +static char selected_filename[MAX_FILENAME_SIZE]=""; +static char second_selected_filename[MAX_FILENAME_SIZE]=""; +static bool menuRedraw=true; +static bool autorun=false; + +static const unsigned short * keys; +#ifdef TEECOMPUTER +static unsigned char keymatrix[6]; +static int keymatrix_hitrow=-1; +static uint32_t keypress_t_ms=0; +static uint32_t last_t_ms=0; +static uint32_t hundred_ms_cnt=0; +static bool ledflash_toggle=false; +#endif +static bool key_extmode=false; +static bool key_sh=false; +static bool key_fn=false; + +static boolean joySwapped = false; +static uint16_t bLastState; +#ifdef PIN_JOY2_A1X +static int xRef; +static int yRef; +#endif +static bool menuOn=true; + + +/******************************** + * Generic output and malloc +********************************/ +void emu_printf(const char * text) +{ + Serial.println(text); +} + +void emu_printf(int val) +{ + Serial.println(val); +} + +void emu_printi(int val) +{ + Serial.println(val); +} + +void emu_printh(int val) +{ + Serial.println(val,HEX); +} + +static int malbufpt = 0; +static char malbuf[EXTRA_HEAP]; + +void * emu_Malloc(unsigned int size) +{ + void * retval = malloc(size); + if (!retval) { + emu_printf("failled to allocate"); + emu_printf(size); + emu_printf("fallback"); + if ( (malbufpt+size) < sizeof(malbuf) ) { + retval = (void *)&malbuf[malbufpt]; + malbufpt += size; + } + else { + emu_printf("failure to allocate"); + } + } + else { + emu_printf("could allocate dynamic "); + emu_printf(size); + } + + return retval; +} + +void * emu_MallocI(unsigned int size) +{ + void * retval = NULL; + + if ( (malbufpt+size) < sizeof(malbuf) ) { + retval = (void *)&malbuf[malbufpt]; + malbufpt += size; + emu_printf("could allocate static "); + emu_printf(size); + } + else { + emu_printf("failure to allocate"); + } + + return retval; +} +void emu_Free(void * pt) +{ + free(pt); +} + +#define SMEMPOOL (0x400000+400000) +EXTMEM static unsigned char slowmem[SMEMPOOL]; +static int slowmempt = 0; + +void * emu_SMalloc(unsigned int size) +{ + void * mem = (void*)&slowmem[slowmempt]; + slowmempt += size; + + if ( slowmempt > SMEMPOOL ) { + mem = NULL; + emu_printf("failure to allocate slow"); + } + else { + emu_printf("could allocate slow static "); + emu_printf(size); + } + return mem; +} + +void emu_SFree(void * pt) +{ +} +/******************************** + * Input and keyboard +********************************/ +#ifdef PIN_JOY2_A1X +int emu_ReadAnalogJoyX(int min, int max) +{ + int val = analogRead(PIN_JOY2_A1X); +#if INVX + val = 4095 - val; +#endif + val = val-xRef; + val = ((val*140)/100); + if ( (val > -512) && (val < 512) ) val = 0; + val = val+2048; + return (val*(max-min))/4096; +} +#endif + +#ifdef PIN_JOY2_A2Y +int emu_ReadAnalogJoyY(int min, int max) +{ + int val = analogRead(PIN_JOY2_A2Y); +#if INVY + val = 4095 - val; +#endif + val = val-yRef; + val = ((val*120)/100); + if ( (val > -512) && (val < 512) ) val = 0; + //val = (val*(max-min))/4096; + val = val+2048; + //return val+(max-min)/2; + return (val*(max-min))/4096; +} +#endif + +static uint16_t readAnalogJoystick(void) +{ + uint16_t joysval = 0; + +#ifdef PIN_JOY2_A1X + int xReading = emu_ReadAnalogJoyX(0,256); + if (xReading > 128) joysval |= MASK_JOY2_LEFT; + else if (xReading < 128) joysval |= MASK_JOY2_RIGHT; + + int yReading = emu_ReadAnalogJoyY(0,256); + if (yReading < 128) joysval |= MASK_JOY2_UP; + else if (yReading > 128) joysval |= MASK_JOY2_DOWN; +#endif + +#ifdef PIN_JOY2_BTN + joysval |= (digitalRead(PIN_JOY2_BTN) == HIGH ? 0 : MASK_JOY2_BTN); +#endif + return (joysval); +} + + +int emu_SwapJoysticks(int statusOnly) { + if (!statusOnly) { + if (joySwapped) { + joySwapped = false; + } + else { + joySwapped = true; + } + } + return(joySwapped?1:0); +} + +int emu_GetPad(void) +{ + return(bLastState/*|((joySwapped?1:0)<<7)*/); +} + +int emu_ReadKeys(void) +{ + uint16_t retval; + uint16_t j1 = readAnalogJoystick(); + uint16_t j2 = 0; + + // Second joystick +#ifdef PIN_JOY1_1 + if ( digitalRead(PIN_JOY1_1) == LOW ) j2 |= MASK_JOY2_UP; +#endif +#ifdef PIN_JOY1_2 + if ( digitalRead(PIN_JOY1_2) == LOW ) j2 |= MASK_JOY2_DOWN; +#endif +#ifdef PIN_JOY1_3 + if ( digitalRead(PIN_JOY1_3) == LOW ) j2 |= MASK_JOY2_RIGHT; +#endif +#ifdef PIN_JOY1_4 + if ( digitalRead(PIN_JOY1_4) == LOW ) j2 |= MASK_JOY2_LEFT; +#endif +#ifdef PIN_JOY1_BTN + if ( digitalRead(PIN_JOY1_BTN) == LOW ) j2 |= MASK_JOY2_BTN; +#endif + if (joySwapped) { + retval = ((j1 << 8) | j2); + } + else { + retval = ((j2 << 8) | j1); + } + + if (usbnavpad & MASK_JOY2_UP) retval |= MASK_JOY2_UP; + if (usbnavpad & MASK_JOY2_DOWN) retval |= MASK_JOY2_DOWN; + if (usbnavpad & MASK_JOY2_LEFT) retval |= MASK_JOY2_LEFT; + if (usbnavpad & MASK_JOY2_RIGHT) retval |= MASK_JOY2_RIGHT; + if (usbnavpad & MASK_JOY2_BTN) retval |= MASK_JOY2_BTN; + if (usbnavpad & MASK_KEY_USER1) retval |= MASK_KEY_USER1; + if (usbnavpad & MASK_KEY_USER2) retval |= MASK_KEY_USER2; + +#ifdef PIN_KEY_USER1 + if ( digitalRead(PIN_KEY_USER1) == LOW ) retval |= MASK_KEY_USER1; +#endif +#ifdef PIN_KEY_USER2 + if ( digitalRead(PIN_KEY_USER2) == LOW ) retval |= MASK_KEY_USER2; +#endif +#ifdef PIN_KEY_USER3 + if ( digitalRead(PIN_KEY_USER3) == LOW ) retval |= MASK_KEY_USER3; +#endif +#ifdef PIN_KEY_USER4 + if ( digitalRead(PIN_KEY_USER4) == LOW ) retval |= MASK_KEY_USER4; +#endif + +#ifdef TEECOMPUTER + keymatrix_hitrow = -1; + unsigned char row; + unsigned short cols[6]={KCOLOUT1,KCOLOUT2,KCOLOUT3,KCOLOUT4,KCOLOUT5,KCOLOUT6}; + for (int i=0;i<6;i++){ + pinMode(cols[i],OUTPUT); + digitalWrite(cols[i], 0); + row=0; + row |= (digitalRead(KROWIN1) ? 0 : 0x01); + row |= (digitalRead(KROWIN2) ? 0 : 0x02); + row |= (digitalRead(KROWIN3) ? 0 : 0x04); + row |= (digitalRead(KROWIN4) ? 0 : 0x08); + row |= (digitalRead(KROWIN5) ? 0 : 0x10); + row |= (digitalRead(KROWIN6) ? 0 : 0x20); + row |= (digitalRead(KROWIN7) ? 0 : 0x40); + digitalWrite(cols[i], 1); + pinMode(cols[i],INPUT_DISABLE); + keymatrix[i]=row; + } + + bool fn_pressed=false; + if ( keymatrix[5] & 0x08 ) {fn_pressed=true; keymatrix[5] &= ~0x08;}; + + bool sh_pressed=false; + if ( keymatrix[5] & 0x10 ) {sh_pressed=true; keymatrix[5] &= ~0x10;}; + + for (int i=0;i<6;i++){ + row = keymatrix[i]; + if (row) keymatrix_hitrow=i; + } + + //6,9,15,8,7,22 +#if INVX + if ( row & 0x40 ) retval |= MASK_JOY2_LEFT; + if ( row & 0x20 ) retval |= MASK_JOY2_RIGHT; +#else + if ( row & 0x20 ) retval |= MASK_JOY2_LEFT; + if ( row & 0x40 ) retval |= MASK_JOY2_RIGHT; +#endif +#if INVY + if ( row & 0x4 ) retval |= MASK_JOY2_DOWN; + if ( row & 0x1 ) retval |= MASK_JOY2_UP; +#else + if ( row & 0x1 ) retval |= MASK_JOY2_DOWN; + if ( row & 0x4 ) retval |= MASK_JOY2_UP; +#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) { + last_t_ms = time_ms; + if (ledflash_toggle == false) { + ledflash_toggle = true; + } + else { + ledflash_toggle = false; + } + } + + if ( sh_pressed ) { + key_sh = true; + } + else { + key_sh = false; + if ( fn_pressed ) { + if (key_fn == false) + { + // Release to Press transition + if (hundred_ms_cnt == 0) { + keypress_t_ms=time_ms; + hundred_ms_cnt += 1; // 1 + } + else { + hundred_ms_cnt += 1; // 2 + if (hundred_ms_cnt >= 2) + { + hundred_ms_cnt = 0; + if ( (time_ms-keypress_t_ms) < 500) + { + if (key_extmode == false) + { + key_extmode = true; + } + else + { + key_extmode = false; + } + } + } + } + } + else { + // Keep press + if (hundred_ms_cnt == 1) { + if ((millis()-keypress_t_ms) > 1000) + { + if (key_extmode == false) + { + key_extmode = true; + } + else + { + key_extmode = false; + } + hundred_ms_cnt = 0; + } + } + } + key_fn = true; + } + else { + key_fn = false; + } + } + + // Handle LED + if (key_extmode == true) { + digitalWrite(KLED, (ledflash_toggle?1:0)); + } + else { + if ( (key_fn == true) || (key_sh == true) ) { + digitalWrite(KLED, 1); + } + else { + digitalWrite(KLED, 0); + } + } + + if ( key_fn ) retval |= MASK_KEY_USER2; + if ( ( key_fn ) && (keymatrix[4] == 0x10 )) retval |= MASK_KEY_USER1; +#endif + + if ( (fn_pressed) && (sh_pressed) ) +#else + if ( ((retval & (MASK_KEY_USER1+MASK_KEY_USER2)) == (MASK_KEY_USER1+MASK_KEY_USER2)) + || (retval & MASK_KEY_USER4 ) ) +#endif + { +// Reset procedure T3.X and T4.0 by Frank Boesing !! +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) + uint32_t tmp = SNVS_LPCR; // save control register + + SNVS_LPSR |= 1; + + // disable alarm + SNVS_LPCR &= ~0x02; + while (SNVS_LPCR & 0x02); + + __disable_irq(); + //get Time: + uint32_t lsb, msb; + do { + msb = SNVS_LPSRTCMR; + lsb = SNVS_LPSRTCLR; + } while ( (SNVS_LPSRTCLR != lsb) | (SNVS_LPSRTCMR != msb) ); + uint32_t secs = (msb << 17) | (lsb >> 15); + + //set alarm + secs += 2; + SNVS_LPTAR = secs; + while (SNVS_LPTAR != secs); + + SNVS_LPCR = tmp | 0x02; // restore control register and set alarm + while (!(SNVS_LPCR & 0x02)); + + SNVS_LPCR |= (1 << 6); // turn off power + while (1) asm("wfi"); +#else + *(volatile uint32_t *)0xE000ED0C = 0x5FA0004; + while (true) { + ; + } +#endif + } + + return (retval); +} + +unsigned short emu_DebounceLocalKeys(void) +{ + uint16_t bCurState = emu_ReadKeys(); + uint16_t bClick = bCurState & ~bLastState; + bLastState = bCurState; + + return (bClick); +} + +int emu_ReadI2CKeyboard(void) { + int retval=0; +#ifdef TEECOMPUTER + if (key_extmode) { + if (key_fn) { + keys = (const unsigned short *)key_map5; // fn-extra + } + else if (key_sh) { + keys = (const unsigned short *)key_map4; // shift-functionkeys + } + else { + keys = (const unsigned short *)key_map3; // def-digitkeys + } + } + else { + if (key_fn) { + keys = (const unsigned short *)key_map2; // fn-shiftothers + } + else if (key_sh) { + keys = (const unsigned short *)key_map1; // shift-uppercase + } + else { + keys = (const unsigned short *)key_map0; // def-lowercase + } + } + + + if (keymatrix_hitrow >=0 ) { + unsigned short match = ((unsigned short)keymatrix_hitrow<<8) | keymatrix[keymatrix_hitrow]; + for (unsigned int i=0; i= 128) { + midiDataCnt = 0; + midiLastCmd = value; + switch (value & 0xF0) { + case 128: // 0x80 + midiCmdNbParam = 2; + //Serial.print("note off: "); + //Serial.println(value&0xf); + break; + case 144: //0x90 + midiCmdNbParam = 2; + //Serial.print("note on: "); + //Serial.println(value&0xf); + break; + case 160: //0xA0 + midiCmdNbParam = 2; + //Serial.print("aftertouch: "); // rare + //Serial.println(value&0xf); + break; + case 176: //0xB0 + midiCmdNbParam = 2; + //Serial.print("continuous controller: "); + //Serial.println(value&0xf); + break; + case 192: //0xC0 + midiCmdNbParam = 1; + //Serial.print("patch change: "); //some + //Serial.println(value&0xf); + break; + case 208: //0xD0 + midiCmdNbParam = 1; + Serial.print("channel pressure: "); + Serial.println(value&0xf); + break; + case 224: //0xE0 + midiCmdNbParam = 2; + //Serial.print("pitch bend: "); + //Serial.println(value&0xf); + break; + case 240: //0xF0 + // non-musical commands + switch (value) { + case 0xF0: + //Serial.println("NI: System Exclusive"); + break; + case 0xF1: + //Serial.println("NI: System Common - MIDI Time Code Quarter Frame"); + break; + case 0xF2: + midiCmdNbParam = 2; + break; + case 0xF3: + //Serial.println("NI: System Common - Song Select"); + break; + case 0xF6: + //Serial.println("NI: System Common - Tune Request"); + break; + case 0xF8: + //Serial.println("System Real Time - Timing Clock"); + midi1.sendRealTime(value, 0); + break; + case 0xFA: + //Serial.println("System Real Time - Start"); + midi1.sendRealTime(value, 0); + break; + case 0xFB: + //Serial.println("System Real Time - Continue"); + midi1.sendRealTime(value, 0); + break; + case 0xFC: + //Serial.println("System Real Time - Stop"); + midi1.sendRealTime(value, 0); + break; + case 0xFE: + //Serial.println("System Real Time - Active Sensing"); + midi1.sendRealTime(value, 0); + break; + case 0xFF: + //Serial.println("System Real Time - System Reset"); + midi1.sendRealTime(value, 0); + break; + } + //SystemExclusive = 0xF0, // System Exclusive + //TimeCodeQuarterFrame = 0xF1, // System Common - MIDI Time Code Quarter Frame + //SongPosition = 0xF2, // System Common - Song Position Pointer + //SongSelect = 0xF3, // System Common - Song Select + //TuneRequest = 0xF6, // System Common - Tune Request + //Clock = 0xF8, // System Real Time - Timing Clock + //Start = 0xFA, // System Real Time - Start + //Continue = 0xFB, // System Real Time - Continue + //Stop = 0xFC, // System Real Time - Stop + //ActiveSensing = 0xFE, // System Real Time - Active Sensing + //SystemReset = 0xFF, // System Real Time - System Reset + break; + default: + Serial.print("??: "); + Serial.println(value&0xf); + break; + } + } + else { + if (midiDataCnt<16) midiBuffer[midiDataCnt++] = value ; + if ( (midiLastCmd & 0xF0) == 240) { + if (midiLastCmd == 0xF2) { + if (midiDataCnt == midiCmdNbParam) { + //Serial.println("System Common - Song Position Pointer"); + midi1.sendSongPosition(((int)midiBuffer[1]<<7)+(int)midiBuffer[0], 0); + } + } + else { + Serial.println(value); + } + } + else if (midiDataCnt == midiCmdNbParam) { + unsigned char chan = (midiLastCmd&0xf)+1; + //Serial.print("ch "); + //Serial.println(chan); + switch (midiLastCmd & 0xF0) { + case 128: //0x80 + //Serial.print("note off: "); + midi1.sendNoteOff(midiBuffer[0], midiBuffer[1], chan); + break; + case 144: //0x90 + //Serial.print("note on: "); + midi1.sendNoteOn(midiBuffer[0], midiBuffer[1], chan); + break; + case 160: //0xA0 + //Serial.print("aftertouch: "); + midi1.sendPolyPressure(midiBuffer[0], midiBuffer[1], chan); + break; + case 176: //0xB0 + //Serial.print("continuous controller: "); + midi1.sendControlChange(midiBuffer[0], midiBuffer[1], chan); + break; + case 192: //0xC0 + //Serial.print("patch change: "); + midi1.sendProgramChange(midiBuffer[0], chan); + break; + case 208: //0xD0 + //Serial.print("channel pressure: "); + midi1.sendAfterTouch(midiBuffer[0], chan); + break; + case 224: //0xE0 + //Serial.print("pitch bend: "); + midi1.sendPitchBend((((int)midiBuffer[1]<<7)+(int)midiBuffer[0])-8192, chan); + break; + default: + Serial.print("??: "); + break; + } + } + } +#endif +} + +/******************************** + * Menu file loader UI +********************************/ +static int readNbFiles(void) { + int totalFiles = 0; + + File entry; + file = SD.open(selection); + while ( (true) && (totalFiles=0) { + menuRedraw=true; + curFile -= 9; + } else if (curFile!=0) { + menuRedraw=true; + curFile--; + } + } + else if ( (bClick & MASK_JOY2_DOWN) || (bClick & MASK_JOY1_DOWN) ) { + if ((curFile<(nbFiles-1)) && (nbFiles)) { + curFile++; + menuRedraw=true; + } + } + else if ( (bClick & MASK_JOY2_LEFT) || (bClick & MASK_JOY1_LEFT) ) { + if ((curFile<(nbFiles-9)) && (nbFiles)) { + curFile += 9; + menuRedraw=true; + } + else if ((curFile<(nbFiles-1)) && (nbFiles)) { + curFile++; + menuRedraw=true; + } + } + + if (menuRedraw && nbFiles) { + int fileIndex = 0; + tft.drawRectNoDma(MENU_FILE_XOFFSET,MENU_FILE_YOFFSET, MENU_FILE_W, MENU_FILE_H, MENU_FILE_BGCOLOR); +// if (curFile <= (MAX_MENULINES/2-1)) topFile=0; +// else topFile=curFile-(MAX_MENULINES/2); + if (curFile <= (MAX_MENULINES-1)) topFile=0; + else topFile=curFile-(MAX_MENULINES/2); + + //Serial.print("curfile: "); + //Serial.println(curFile); + //Serial.print("topFile: "); + //Serial.println(topFile); + + int i=0; + while (i=nbFiles) { + // no more files + break; + } + char * filename = &files[fileIndex][0]; + if (fileIndex >= topFile) { + if ((i+topFile) < nbFiles ) { + if ((i+topFile)==curFile) { + tft.drawTextNoDma(MENU_FILE_XOFFSET,i*TEXT_HEIGHT+MENU_FILE_YOFFSET, filename, RGBVAL16(0xff,0xff,0x00), RGBVAL16(0xff,0x00,0x00), true); + strcpy(selected_filename,filename); + } + else { + tft.drawTextNoDma(MENU_FILE_XOFFSET,i*TEXT_HEIGHT+MENU_FILE_YOFFSET, filename, RGBVAL16(0xff,0xff,0xff), MENU_FILE_BGCOLOR, true); + } + } + i++; + } + fileIndex++; + } + + +// tft.drawTextNoDma(48,MENU_JOYS_YOFFSET+8, (emu_SwapJoysticks(1)?(char*)"SWAP=1":(char*)"SWAP=0"), RGBVAL16(0x00,0xff,0xff), RGBVAL16(0x00,0x00,0xff), false); + tft.drawTextNoDma(48,MENU_JOYS_YOFFSET+8, "FLOPPY2:", RGBVAL16(0x00,0xff,0xff), RGBVAL16(0x00,0x00,0xff), false); + tft.drawRectNoDma(120,MENU_JOYS_YOFFSET+8, MENU_FILE_W, 8, RGBVAL16(0x00,0x00,0x00)); + tft.drawTextNoDma(120,MENU_JOYS_YOFFSET+8, second_selected_filename, RGBVAL16(0xff,0xff,0xff), RGBVAL16(0x00,0x00,0x00), false); + menuRedraw=false; + } + + return (action); +} + +bool menuActive(void) +{ + return (menuOn); +} + +void toggleMenu(bool on) { + if (on) { + menuOn=true; + backgroundMenu(); + } else { + menuOn = false; + } +} + +char * menuSelection(void) +{ + return (selection); +} + +char * menuSecondSelection(void) +{ + return (second_selection); +} + + +/******************************** + * OSKB handling +********************************/ +static bool oskbOn = false; +static int cxpos = 0; +static int cypos = 0; +static uint16_t oskbBLastState = 0; +#define OSKBHEIGHT 4 +#define OSKBXOFF 16 +#define OSKBYOFF 0 + +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,fbstride; + + tft.get_frame_buffer_size(&fb_width, &fb_height, &fbstride); + int ypos = (bottom?(fb_height-2*8):0); + int line = row + (bottom?2:0); + while ((c[1] = *cpt++)) + { + uint16_t bg; + if (row&1) bg = (i&1)?RGBVAL16(0xff,0xff,0xff):RGBVAL16(0xe0,0xe0,0xe0); + else bg = (i&1)?RGBVAL16(0xe0,0xe0,0xe0):RGBVAL16(0xff,0xff,0xff); + if ( (cxpos == i) && (cypos == line) ) bg = RGBVAL16(0x00,0xff,0xff); + tft.drawTextNoDma(OSKBXOFF+xoff+24*i,OSKBYOFF+ypos+8*row , &c[0], RGBVAL16(0x00,0x00,0x00), bg, false); + i++; + } +} + +static int linelenOSKB() { + if (cypos == 0) return strlen(keylables_map0_0); + else if (cypos == 1) return strlen(keylables_map0_1); + else if (cypos == 2) return strlen(keylables_map0_2); + else return strlen(keylables_map0_3); +} + +static void drawOSKB() { + if (key_extmode) { + if (key_fn) { + lineOSKB(16,false, keylables_map5_0, 0); + lineOSKB(8, false, keylables_map5_1, 1); + lineOSKB(0, true, keylables_map5_2, 0); + lineOSKB(96,true, keylables_map5_3, 1); + } + else if (key_sh) { + lineOSKB(16,false, keylables_map4_0, 0); + lineOSKB(8, false, keylables_map4_1, 1); + lineOSKB(0, true, keylables_map4_2, 0); + lineOSKB(96,true, keylables_map4_3, 1); + } + else { + lineOSKB(16,false, keylables_map3_0, 0); + lineOSKB(8, false, keylables_map3_1, 1); + lineOSKB(0, true, keylables_map3_2, 0); + lineOSKB(96,true, keylables_map3_3, 1); + } + } + else { + if (key_fn) { + lineOSKB(16,false, keylables_map2_0, 0); + lineOSKB(8, false, keylables_map2_1, 1); + lineOSKB(0, true, keylables_map2_2, 0); + lineOSKB(96,true, keylables_map2_3, 1); + } + else if (key_sh) { + lineOSKB(16,false, keylables_map1_0, 0); + lineOSKB(8, false, keylables_map1_1, 1); + lineOSKB(0, true, keylables_map1_2, 0); + lineOSKB(96,true, keylables_map1_3, 1); + } + else { + lineOSKB(16,false, keylables_map0_0, 0); + lineOSKB(8, false, keylables_map0_1, 1); + lineOSKB(0, true, keylables_map0_2, 0); + lineOSKB(96,true, keylables_map0_3, 1); + } + } +} + +int handleOSKB(void) { + int retval = 0; + if (oskbOn) { + uint16_t bClick = bLastState & ~oskbBLastState; + oskbBLastState = bLastState; + bool updated = true; + if (bClick & MASK_KEY_USER1) + { + } + else if (bClick & MASK_JOY2_RIGHT) + { + cxpos++; + if (cxpos >= linelenOSKB()) cxpos = 0; + } + else if (bClick & MASK_JOY2_LEFT) + { + cxpos--; + if (cxpos < 0) cxpos = linelenOSKB()-1; + } + else if (bClick & MASK_JOY2_DOWN) + { + cypos++; + if (cypos >= OSKBHEIGHT) cypos = 0; + if (cxpos >= linelenOSKB()) cxpos = linelenOSKB()-1; + } + else if (bClick & MASK_JOY2_UP) + { + cypos--; + if (cypos < 0) cypos = OSKBHEIGHT-1; + if (cxpos >= linelenOSKB()) cxpos = linelenOSKB()-1; + } + else if (oskbBLastState & MASK_JOY2_BTN) + { + //if (retval) { toggleOSKB(false); updated=false; }; + } + else { + updated=false; + } + /*if (updated)*/ drawOSKB(); + } + return retval; +} + +void toggleOSKB(bool forceon) { + if (forceon) { + oskbOn = true; + drawOSKB(); + } + else { + if (oskbOn) { + oskbOn = false; + } + else { + oskbOn = true; + drawOSKB(); + } + } +} + + + +/******************************** + * File IO +********************************/ +static File file_handlers[NB_FILE_HANDLER]; + +static void FileHandlersInit(void) { + for (int i=0; i= 0) { + if ((file_handlers[handler] = SD.open(filepath, O_READ))) { + // emu_printi(handler+1); + retval = handler+1; + } + else { + file_handlers[handler] = file; + emu_printf("FileOpen failed"); + } + } + return (retval); +#endif +} + +int emu_FileRead(void * buf, int size, int handler) +{ +// emu_printf("FileRead"); +// emu_printi(handler); +#ifdef HCFH + return (file.read(buf, size)); +#else + return (getFileHandler(handler).read(buf, size)); +#endif +} + +int emu_FileGetc(int handler) { +// emu_printf("FileGetc"); +// emu_printi(handler); +#ifdef HCFH + unsigned char c; + int retval = file.read(&c, 1); + if (retval != 1) { + emu_printf("emu_FileGetc failed"); + } + return (int)c; +#else + unsigned char c; + int retval = getFileHandler(handler).read(&c, 1); + if (retval != 1) { + emu_printf("emu_FileGetc failed"); + } + return (int)c; +#endif +} + +int emu_FileSeek(int handler, int seek, int origin) +{ +// emu_printf("FileSeek"); +// emu_printi(handler); +// emu_printi(seek); +#ifdef HCFH + file.seek(seek); + return (seek); +#else + getFileHandler(handler).seek(seek); + return (seek); +#endif +} + +int emu_FileTell(int handler) { +// emu_printf("FileTell"); +// emu_printi(handler); +#ifdef HCFH + return (50); +#else + File file = getFileHandler(handler); + return (emu_FileSize((char*)file.name())); +#endif +} + + +void emu_FileClose(int handler) +{ +// emu_printf("FileClose"); +// emu_printi(handler); +#ifdef HCFH + file.close(); +#else + getFileHandler(handler).close(); + file_handlers[handler-1] = file; +#endif +} + + +static File lofile; + +unsigned int emu_FileSize(const char * filepath) +{ + unsigned int filesize=0; + emu_printf(filepath); + if ((lofile = SD.open(filepath, O_READ))) + { + emu_printf("filesize is..."); + filesize = lofile.size(); + emu_printf(filesize); + lofile.close(); + } + else { + emu_printf("filesize failed"); + } + return(filesize); +} + +unsigned int emu_LoadFile(const char * filepath, void * buf, int size) +{ + unsigned int filesize = 0; + emu_printf("LoadFile..."); + emu_printf(filepath); + if ((lofile = SD.open(filepath, O_READ))) + { + filesize = lofile.size(); + emu_printf(filesize); + if ((unsigned int)size >= filesize) + { + if (lofile.read(buf, filesize) != filesize) + { + emu_printf("File read failed"); + } + } + lofile.close(); + } + return(filesize); +} + +unsigned int emu_LoadFileSeek(const char * filepath, void * buf, int size, int seek) +{ + unsigned int filesize = 0; + emu_printf("LoadFileSeek..."); + emu_printf(filepath); + if ((lofile = SD.open(filepath, O_READ))) + { + lofile.seek(seek); + emu_printf(size); + if (lofile.read(buf, size) != (unsigned int)size) { + emu_printf("File read failed"); + } + lofile.close(); + } + return(filesize); +} + +static bool emu_writeConfig(void) +{ + bool retval = false; + if ((lofile = SD.open(ROMSDIR "/" AUTORUN_FILENAME, O_CREAT | O_WRITE))) + { + if (lofile.write(selection, strlen(selection)) != strlen(selection)) { + emu_printf("Config write failed"); + } + else { + retval = true; + } + lofile.close(); + } + return retval; +} + +static bool emu_readConfig(void) +{ + bool retval = false; + + if ((lofile = SD.open(ROMSDIR "/" AUTORUN_FILENAME, O_READ))) + { + unsigned int filesize = lofile.size(); + unsigned int sizeread = lofile.read(selection, filesize); + if (sizeread != filesize) { + emu_printf("Config read failed"); + } + else { + if (sizeread == filesize) + { + selection[filesize]=0; + retval = true; + } + } + lofile.close(); + } + return retval; +} + +static bool emu_eraseConfig(void) +{ + SD.remove (ROMSDIR "/" AUTORUN_FILENAME); +} + +/******************************** + * File IO compatibility +********************************/ +#ifdef HAS_EXTFF + +#include "ff.h" + +typedef struct { + File f; + int offset; + int size; + int used; +} FileDesc; + +typedef int * FIL; + + + +#define NO_MMAP_HANDLES 32 + +static FileDesc fds[NO_MMAP_HANDLES]; +static int nextHandle=0; + +static void freeHandle(int h) { + fds[h].used = 0; +} + + +static int getFreeHandle() { + int n=NO_MMAP_HANDLES; + while (fds[nextHandle].used!=0 && n!=0) { + nextHandle++; + if (nextHandle==NO_MMAP_HANDLES) nextHandle=0; + n-1; + } + if (n==0) { + emu_printf("getFreeHandle error"); + return; + } + + int r=nextHandle; + fds[r].used = 1; + nextHandle++; + if (nextHandle==NO_MMAP_HANDLES) nextHandle=0; + + return r; +} + +FRESULT f_open (FIL* fp, const char * path, unsigned char mode) +{ + emu_printf("fopen"); + emu_printf((char*)path); + int i=getFreeHandle(); + emu_printf(i); + fds[i].f = SD.open(path, O_READ); + *fp = i; + if (fds[i].f) { + fds[i].size = fds[i].f.size(); + emu_printi(fds[i].size); + return(FR_OK); + } + emu_printf("error"); + freeHandle(fds[i].f); + return(FR_NO_FILE); +} + +FRESULT f_close (FIL* fp) +{ + int i = *fp; + emu_printf("fclose"); + emu_printi(i); + fds[i].f.close(); + freeHandle(i); + return(FR_OK); +} + +FRESULT f_read (FIL* fp, void* buff, unsigned int btr, unsigned int * br) +{ + int i = *fp; + + if (btr < 64738) { + int nr = fds[i].f.read(buff, btr); + //emu_printf("fread"); + //emu_printi(btr); + //emu_printi(nr); + *br = nr; + if (nr <= 0) + return(FR_DISK_ERR); + else + return(FR_OK); + } + + unsigned char buffer[256]; + + int remaining = btr; + int byteread = 0; + int retval=0; + + while (remaining>0) { + if (remaining < 256) + retval = fds[i].f.read(buffer, remaining); + else + retval = fds[i].f.read(buffer, 256); + if (retval>0) { + memcpy(buff,buffer,retval); + buff += retval; + byteread += retval; + remaining -= retval; + } + else { + break; + } + } + *br = byteread; + //emu_printi(byteread); + if (byteread <= 0) + return(FR_DISK_ERR); + else + return(FR_OK); +} + +FRESULT f_readn (FIL* fp, void* buff, unsigned int btr, unsigned int * br) +{ + return(f_read (fp, buff, btr, br)); +} + +FRESULT f_write (FIL* fp, const void* buff, unsigned int btw, unsigned int * bw) +{ + return(FR_OK); +} +FRESULT f_writen (FIL* fp, const void* buff, unsigned int btw, unsigned int * bw) +{ + return(FR_OK); +} +FRESULT f_lseek (FIL* fp, unsigned long ofs) +{ + int i = *fp; + //emu_printf("fseek"); + //emu_printi(ofs); + fds[i].f.seek(ofs); + return(FR_OK); +} + + +FRESULT f_unlink (const char * path) +{ + return(FR_OK); +} +FRESULT f_rename (const char * path_old, const char * path_new) +{ + return(FR_OK); +} +FRESULT f_stat (const char * path, FILINFO* fno) +{ + return(FR_OK); +} + +unsigned long f_tell (FIL * fp) +{ + int i = *fp; + emu_printf("ftell"); + return(fds[i].size); + //return(fds[i].f.ftell()); +} + +unsigned long f_size (FIL * fp) +{ + int i = *fp; + emu_printf("fsize"); + emu_printi(fds[i].size); + return(fds[i].size); +} + +FRESULT f_mkdir (const char* path) +{ + return(FR_OK); +} +#endif + + +/******************************** + * Initialization +********************************/ +void emu_init(void) +{ + Serial.begin(115200); + +#ifdef HAS_USBKEY + myusb.begin(); + keyboard1.attachPress(OnPress); + keyboard1.attachRelease(OnRelease); +#endif + + while (!SD.begin(SD_CS)) + { + Serial.println("SD begin failed, retrying..."); + delay(1000); + } + strcpy(selection,ROMSDIR); + + FileHandlersInit(); + + nbFiles = readNbFiles(); + + Serial.print("SD initialized, files found: "); + Serial.println(nbFiles); + + emu_InitJoysticks(); +#ifdef SWAP_JOYSTICK + joySwapped = true; +#else + joySwapped = false; +#endif + +#ifdef TEECOMPUTER +#ifndef HAS_T4_VGA + tft.flipscreen(false); +#endif +#endif + int keypressed = emu_ReadKeys(); + if (keypressed & MASK_JOY2_DOWN) { + tft.fillScreenNoDma( RGBVAL16(0xff,0x00,0x00) ); + tft.drawTextNoDma(64,48, (char*)" AUTURUN file erased", RGBVAL16(0xff,0xff,0x00), RGBVAL16(0xff,0x00,0x00), true); + emu_eraseConfig(); + delay(1000); + } + else { + if (emu_readConfig()) { + autorun = true; + } + } + + toggleMenu(true); +} + + +void emu_start(void) +{ + usbnavpad = 0; +} diff --git a/MCUME_teensy41/teensyhandy/emuapi.h b/MCUME_teensy41/teensyhandy/emuapi.h new file mode 100644 index 0000000..c30ea6a --- /dev/null +++ b/MCUME_teensy41/teensyhandy/emuapi.h @@ -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 diff --git a/MCUME_teensy41/teensyhandy/font8x8.h b/MCUME_teensy41/teensyhandy/font8x8.h new file mode 100644 index 0000000..a0913e4 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/font8x8.h @@ -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 +}; + + diff --git a/MCUME_teensy41/teensyhandy/handy.h b/MCUME_teensy41/teensyhandy/handy.h new file mode 100644 index 0000000..de7b097 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/handy.h @@ -0,0 +1,5 @@ +#undef INTSET +#undef PS + +#include "system.h" +#include "lynxdef.h" diff --git a/MCUME_teensy41/teensyhandy/iopins.h b/MCUME_teensy41/teensyhandy/iopins.h new file mode 100644 index 0000000..24bcda2 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/iopins.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 diff --git a/MCUME_teensy41/teensyhandy/lynx.cpp b/MCUME_teensy41/teensyhandy/lynx.cpp new file mode 100644 index 0000000..bb88263 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/lynx.cpp @@ -0,0 +1,306 @@ +#include +#include + +extern "C" { +#include "emuapi.h" +#include "platform_config.h" +} +#include +#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>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> 16); + *dst++ = (val & 0xffff); + pt &= (AUDIO_BUFFER_LENGTH-1); + } +*/ +} + diff --git a/MCUME_teensy41/teensyhandy/lynx.h b/MCUME_teensy41/teensyhandy/lynx.h new file mode 100644 index 0000000..b541492 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/lynx.h @@ -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); diff --git a/MCUME_teensy41/teensyhandy/lynxbase.h b/MCUME_teensy41/teensyhandy/lynxbase.h new file mode 100644 index 0000000..a1c2c96 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/lynxbase.h @@ -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 diff --git a/MCUME_teensy41/teensyhandy/lynxdec.cpp b/MCUME_teensy41/teensyhandy/lynxdec.cpp new file mode 100644 index 0000000..8574463 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/lynxdec.cpp @@ -0,0 +1,270 @@ +/* + Wookie @ + http://atariage.com/forums/topic/129030-lynx-encryption/ +*/ + +#include +#include +#include + +#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); +} diff --git a/MCUME_teensy41/teensyhandy/lynxdef.h b/MCUME_teensy41/teensyhandy/lynxdef.h new file mode 100644 index 0000000..898c8fc --- /dev/null +++ b/MCUME_teensy41/teensyhandy/lynxdef.h @@ -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 diff --git a/MCUME_teensy41/teensyhandy/mikie.cpp b/MCUME_teensy41/teensyhandy/mikie.cpp new file mode 100644 index 0000000..1f383fa --- /dev/null +++ b/MCUME_teensy41/teensyhandy/mikie.cpp @@ -0,0 +1,3435 @@ +// +// 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 chip emulation class // +////////////////////////////////////////////////////////////////////////////// +// // +// This class emulates all of the Mikey hardware with the exception of the // +// CPU and memory selector. Update() does most of the work and does screen // +// DMA and counter updates, it also schecules in which cycle the next timer // +// update will occur so that the CSystem->Update() doesn't have to call it // +// every cycle, massive speedup but big complexity headache. // +// // +// K. Wilkins // +// August 1997 // +// // +////////////////////////////////////////////////////////////////////////////// +// Revision History: // +// ----------------- // +// // +// 01Aug1997 KW Document header added & class documented. // +// // +////////////////////////////////////////////////////////////////////////////// + +//#define TRACE_MIKIE + +#include +#include +#include +#include "system.h" +#include "mikie.h" +#include "lynxdef.h" + +static inline ULONG GetLfsrNext(ULONG current) +{ + // The table is built thus: + // Bits 0-11 LFSR (12 Bits) + // Bits 12-20 Feedback switches (9 Bits) + // (Order = 7,0,1,2,3,4,5,10,11) + // Order is mangled to make peek/poke easier as + // bit 7 is in a separate register + // + // Total 21 bits = 2MWords @ 4 Bytes/Word = 8MB !!!!! + // + // If the index is a combination of Current LFSR+Feedback the + // table will give the next value. +#if 0 + ULONG result = 0; + if (current & (1<<12)) result ^= (current>>7)&1; + if (current & (1<<13)) result ^= (current>>0)&1; + if (current & (1<<14)) result ^= (current>>1)&1; + if (current & (1<<15)) result ^= (current>>2)&1; + if (current & (1<<16)) result ^= (current>>3)&1; + if (current & (1<<17)) result ^= (current>>4)&1; + if (current & (1<<18)) result ^= (current>>5)&1; + if (current & (1<<19)) result ^= (current>>10)&1; + if (current & (1<<20)) result ^= (current>>11)&1; + return (current&0xFFFFF000) | ((current<<1)&0xFFE) | (result?0:1); +#else + + static ULONG switches,lfsr,next,swloop,result; + static const ULONG switchbits[9]={7,0,1,2,3,4,5,10,11}; + + switches=current>>12; + lfsr=current&0xfff; + result=0; + for(swloop=0;swloop<9;swloop++) { + if((switches>>swloop)&0x001) result^=(lfsr>>switchbits[swloop])&0x001; + } + result=(result)?0:1; + next=(switches<<12)|((lfsr<<1)&0xffe)|result; + return next; +#endif +} + + +CMikie::CMikie(CSystem& parent, ULONG displayformat, ULONG samplerate) +:mSystem(parent) +{ + TRACE_MIKIE0("CMikie()"); + + mpDisplayCurrent=NULL; + mpRamPointer=NULL; + mDisplayFormat=displayformat; + mAudioSampleRate=samplerate; + mDisplayPitch=HANDY_SCREEN_STRIDE * sizeof(HandyPixel); + + mUART_CABLE_PRESENT=FALSE; + mpUART_TX_CALLBACK=NULL; + + BuildPalette(); + Reset(); +} + +CMikie::~CMikie() +{ + TRACE_MIKIE0("~CMikie()"); +} + + +void CMikie::Reset(void) +{ + TRACE_MIKIE0("Reset()"); + + mAudioInputComparator=FALSE; // Initialises to unknown + mDisplayAddress=0x00; // Initialises to unknown + mLynxLine=0; + mLynxLineDMACounter=0; + mLynxAddr=0; + + mTimerStatusFlags=0x00; // Initialises to ZERO, i.e No IRQ's + mTimerInterruptMask=0x00; + + mpRamPointer=mSystem.GetRamPointer(); // Fetch pointer to system RAM + + mTIM_0_BKUP=0; + mTIM_0_ENABLE_RELOAD=0; + mTIM_0_ENABLE_COUNT=0; + mTIM_0_LINKING=0; + mTIM_0_CURRENT=0; + mTIM_0_TIMER_DONE=0; + mTIM_0_LAST_CLOCK=0; + mTIM_0_BORROW_IN=0; + mTIM_0_BORROW_OUT=0; + mTIM_0_LAST_LINK_CARRY=0; + mTIM_0_LAST_COUNT=0; + + mTIM_1_BKUP=0; + mTIM_1_ENABLE_RELOAD=0; + mTIM_1_ENABLE_COUNT=0; + mTIM_1_LINKING=0; + mTIM_1_CURRENT=0; + mTIM_1_TIMER_DONE=0; + mTIM_1_LAST_CLOCK=0; + mTIM_1_BORROW_IN=0; + mTIM_1_BORROW_OUT=0; + mTIM_1_LAST_LINK_CARRY=0; + mTIM_1_LAST_COUNT=0; + + mTIM_2_BKUP=0; + mTIM_2_ENABLE_RELOAD=0; + mTIM_2_ENABLE_COUNT=0; + mTIM_2_LINKING=0; + mTIM_2_CURRENT=0; + mTIM_2_TIMER_DONE=0; + mTIM_2_LAST_CLOCK=0; + mTIM_2_BORROW_IN=0; + mTIM_2_BORROW_OUT=0; + mTIM_2_LAST_LINK_CARRY=0; + mTIM_2_LAST_COUNT=0; + + mTIM_3_BKUP=0; + mTIM_3_ENABLE_RELOAD=0; + mTIM_3_ENABLE_COUNT=0; + mTIM_3_LINKING=0; + mTIM_3_CURRENT=0; + mTIM_3_TIMER_DONE=0; + mTIM_3_LAST_CLOCK=0; + mTIM_3_BORROW_IN=0; + mTIM_3_BORROW_OUT=0; + mTIM_3_LAST_LINK_CARRY=0; + mTIM_3_LAST_COUNT=0; + + mTIM_4_BKUP=0; + mTIM_4_ENABLE_RELOAD=0; + mTIM_4_ENABLE_COUNT=0; + mTIM_4_LINKING=0; + mTIM_4_CURRENT=0; + mTIM_4_TIMER_DONE=0; + mTIM_4_LAST_CLOCK=0; + mTIM_4_BORROW_IN=0; + mTIM_4_BORROW_OUT=0; + mTIM_4_LAST_LINK_CARRY=0; + mTIM_4_LAST_COUNT=0; + + mTIM_5_BKUP=0; + mTIM_5_ENABLE_RELOAD=0; + mTIM_5_ENABLE_COUNT=0; + mTIM_5_LINKING=0; + mTIM_5_CURRENT=0; + mTIM_5_TIMER_DONE=0; + mTIM_5_LAST_CLOCK=0; + mTIM_5_BORROW_IN=0; + mTIM_5_BORROW_OUT=0; + mTIM_5_LAST_LINK_CARRY=0; + mTIM_5_LAST_COUNT=0; + + mTIM_6_BKUP=0; + mTIM_6_ENABLE_RELOAD=0; + mTIM_6_ENABLE_COUNT=0; + mTIM_6_LINKING=0; + mTIM_6_CURRENT=0; + mTIM_6_TIMER_DONE=0; + mTIM_6_LAST_CLOCK=0; + mTIM_6_BORROW_IN=0; + mTIM_6_BORROW_OUT=0; + mTIM_6_LAST_LINK_CARRY=0; + mTIM_6_LAST_COUNT=0; + + mTIM_7_BKUP=0; + mTIM_7_ENABLE_RELOAD=0; + mTIM_7_ENABLE_COUNT=0; + mTIM_7_LINKING=0; + mTIM_7_CURRENT=0; + mTIM_7_TIMER_DONE=0; + mTIM_7_LAST_CLOCK=0; + mTIM_7_BORROW_IN=0; + mTIM_7_BORROW_OUT=0; + mTIM_7_LAST_LINK_CARRY=0; + mTIM_7_LAST_COUNT=0; + + mAUDIO_0_BKUP=0; + mAUDIO_0_ENABLE_RELOAD=0; + mAUDIO_0_ENABLE_COUNT=0; + mAUDIO_0_LINKING=0; + mAUDIO_0_CURRENT=0; + mAUDIO_0_TIMER_DONE=0; + mAUDIO_0_LAST_CLOCK=0; + mAUDIO_0_BORROW_IN=0; + mAUDIO_0_BORROW_OUT=0; + mAUDIO_0_LAST_LINK_CARRY=0; + mAUDIO_0_LAST_COUNT=0; + mAUDIO_0_VOLUME=0; + mAUDIO_OUTPUT[0]=0; + mAUDIO_0_INTEGRATE_ENABLE=0; + mAUDIO_0_WAVESHAPER=0; + + mAUDIO_1_BKUP=0; + mAUDIO_1_ENABLE_RELOAD=0; + mAUDIO_1_ENABLE_COUNT=0; + mAUDIO_1_LINKING=0; + mAUDIO_1_CURRENT=0; + mAUDIO_1_TIMER_DONE=0; + mAUDIO_1_LAST_CLOCK=0; + mAUDIO_1_BORROW_IN=0; + mAUDIO_1_BORROW_OUT=0; + mAUDIO_1_LAST_LINK_CARRY=0; + mAUDIO_1_LAST_COUNT=0; + mAUDIO_1_VOLUME=0; + mAUDIO_OUTPUT[1]=0; + mAUDIO_1_INTEGRATE_ENABLE=0; + mAUDIO_1_WAVESHAPER=0; + + mAUDIO_2_BKUP=0; + mAUDIO_2_ENABLE_RELOAD=0; + mAUDIO_2_ENABLE_COUNT=0; + mAUDIO_2_LINKING=0; + mAUDIO_2_CURRENT=0; + mAUDIO_2_TIMER_DONE=0; + mAUDIO_2_LAST_CLOCK=0; + mAUDIO_2_BORROW_IN=0; + mAUDIO_2_BORROW_OUT=0; + mAUDIO_2_LAST_LINK_CARRY=0; + mAUDIO_2_LAST_COUNT=0; + mAUDIO_2_VOLUME=0; + mAUDIO_OUTPUT[2]=0; + mAUDIO_2_INTEGRATE_ENABLE=0; + mAUDIO_2_WAVESHAPER=0; + + mAUDIO_3_BKUP=0; + mAUDIO_3_ENABLE_RELOAD=0; + mAUDIO_3_ENABLE_COUNT=0; + mAUDIO_3_LINKING=0; + mAUDIO_3_CURRENT=0; + mAUDIO_3_TIMER_DONE=0; + mAUDIO_3_LAST_CLOCK=0; + mAUDIO_3_BORROW_IN=0; + mAUDIO_3_BORROW_OUT=0; + mAUDIO_3_LAST_LINK_CARRY=0; + mAUDIO_3_LAST_COUNT=0; + mAUDIO_3_VOLUME=0; + mAUDIO_OUTPUT[3]=0; + mAUDIO_3_INTEGRATE_ENABLE=0; + mAUDIO_3_WAVESHAPER=0; + + mSTEREO=0x00; // xored! All channels enabled + mPAN=0x00; // all channels panning OFF + mAUDIO_ATTEN[0]=0xff; // Full volume + mAUDIO_ATTEN[1]=0xff; + mAUDIO_ATTEN[2]=0xff; + mAUDIO_ATTEN[3]=0xff; + + // Start with an empty palette + + for(int loop=0;loop<16;loop++) { + mPalette[loop].Index=loop; + } + + // Initialise IODAT register + + mIODAT=0x00; + mIODIR=0x00; + mIODAT_REST_SIGNAL=0x00; + + // + // Initialise display control register vars + // + mDISPCTL_DMAEnable=FALSE; + mDISPCTL_Flip=FALSE; + mDISPCTL_FourColour=0; + mDISPCTL_Colour=0; + + // + // Initialise the UART variables + // + mUART_RX_IRQ_ENABLE=0; + mUART_TX_IRQ_ENABLE=0; + + mUART_TX_COUNTDOWN=UART_TX_INACTIVE; + mUART_RX_COUNTDOWN=UART_RX_INACTIVE; + + mUART_Rx_input_ptr=0; + mUART_Rx_output_ptr=0; + mUART_Rx_waiting=0; + mUART_Rx_framing_error=0; + mUART_Rx_overun_error=0; + + mUART_SENDBREAK=0; + mUART_TX_DATA=0; + mUART_RX_DATA=0; + mUART_RX_READY=0; + + mUART_PARITY_ENABLE=0; + mUART_PARITY_EVEN=0; + + ResetDisplayPtr(); +} + + +bool CMikie::ContextSave(LSS_FILE *fp) +{ + TRACE_MIKIE0("ContextSave()"); + + if(!lss_printf(fp,"CMikie::ContextSave")) return 0; + + if(!lss_write(&mDisplayAddress,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAudioInputComparator,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTimerStatusFlags,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTimerInterruptMask,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(mPalette,sizeof(TPALETTE),16,fp)) return 0; + + if(!lss_write(&mIODAT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mIODAT_REST_SIGNAL,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mIODIR,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mDISPCTL_DMAEnable,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mDISPCTL_Flip,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mDISPCTL_FourColour,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mDISPCTL_Colour,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mTIM_0_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_0_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mTIM_1_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_1_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mTIM_2_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_2_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mTIM_3_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_3_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mTIM_4_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_4_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mTIM_5_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_5_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mTIM_6_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_6_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mTIM_7_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mTIM_7_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mAUDIO_0_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_VOLUME,sizeof(SBYTE),1,fp)) return 0; + if(!lss_write(&mAUDIO_OUTPUT[0],sizeof(SBYTE),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_INTEGRATE_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_0_WAVESHAPER,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mAUDIO_1_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_VOLUME,sizeof(SBYTE),1,fp)) return 0; + if(!lss_write(&mAUDIO_OUTPUT[1],sizeof(SBYTE),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_INTEGRATE_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_1_WAVESHAPER,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mAUDIO_2_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_VOLUME,sizeof(SBYTE),1,fp)) return 0; + if(!lss_write(&mAUDIO_OUTPUT[2],sizeof(SBYTE),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_INTEGRATE_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_2_WAVESHAPER,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mAUDIO_3_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_VOLUME,sizeof(SBYTE),1,fp)) return 0; + if(!lss_write(&mAUDIO_OUTPUT[3],sizeof(SBYTE),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_INTEGRATE_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mAUDIO_3_WAVESHAPER,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mSTEREO,sizeof(ULONG),1,fp)) return 0; + + // + // Serial related variables + // + if(!lss_write(&mUART_RX_IRQ_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mUART_TX_IRQ_ENABLE,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mUART_TX_COUNTDOWN,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mUART_RX_COUNTDOWN,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mUART_SENDBREAK,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mUART_TX_DATA,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mUART_RX_DATA,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mUART_RX_READY,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mUART_PARITY_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mUART_PARITY_EVEN,sizeof(ULONG),1,fp)) return 0; + + return 1; +} + +bool CMikie::ContextLoad(LSS_FILE *fp) +{ + TRACE_MIKIE0("ContextLoad()"); + + char teststr[32]="XXXXXXXXXXXXXXXXXXX"; + if(!lss_read(teststr,sizeof(char),19,fp)) return 0; + if(strcmp(teststr,"CMikie::ContextSave")!=0) return 0; + + if(!lss_read(&mDisplayAddress,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAudioInputComparator,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTimerStatusFlags,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTimerInterruptMask,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(mPalette,sizeof(TPALETTE),16,fp)) return 0; + + if(!lss_read(&mIODAT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mIODAT_REST_SIGNAL,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mIODIR,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mDISPCTL_DMAEnable,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mDISPCTL_Flip,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mDISPCTL_FourColour,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mDISPCTL_Colour,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mTIM_0_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_0_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mTIM_1_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_1_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mTIM_2_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_2_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mTIM_3_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_3_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mTIM_4_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_4_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mTIM_5_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_5_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mTIM_6_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_6_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mTIM_7_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mTIM_7_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mAUDIO_0_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_VOLUME,sizeof(SBYTE),1,fp)) return 0; + if(!lss_read(&mAUDIO_OUTPUT[0],sizeof(SBYTE),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_INTEGRATE_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_0_WAVESHAPER,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mAUDIO_1_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_VOLUME,sizeof(SBYTE),1,fp)) return 0; + if(!lss_read(&mAUDIO_OUTPUT[1],sizeof(SBYTE),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_INTEGRATE_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_1_WAVESHAPER,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mAUDIO_2_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_VOLUME,sizeof(SBYTE),1,fp)) return 0; + if(!lss_read(&mAUDIO_OUTPUT[2],sizeof(SBYTE),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_INTEGRATE_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_2_WAVESHAPER,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mAUDIO_3_BKUP,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_ENABLE_RELOAD,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_ENABLE_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_LINKING,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_CURRENT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_TIMER_DONE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_LAST_CLOCK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_BORROW_IN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_BORROW_OUT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_LAST_LINK_CARRY,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_LAST_COUNT,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_VOLUME,sizeof(SBYTE),1,fp)) return 0; + if(!lss_read(&mAUDIO_OUTPUT[3],sizeof(SBYTE),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_INTEGRATE_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mAUDIO_3_WAVESHAPER,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mSTEREO,sizeof(ULONG),1,fp)) return 0; + + // + // Serial related variables + // + if(!lss_read(&mUART_RX_IRQ_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mUART_TX_IRQ_ENABLE,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mUART_TX_COUNTDOWN,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mUART_RX_COUNTDOWN,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mUART_SENDBREAK,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mUART_TX_DATA,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mUART_RX_DATA,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mUART_RX_READY,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mUART_PARITY_ENABLE,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mUART_PARITY_EVEN,sizeof(ULONG),1,fp)) return 0; + return 1; +} + +void CMikie::PresetForHomebrew(void) +{ + TRACE_MIKIE0("PresetForHomebrew()"); + + // + // After all of that nice timer init we'll start timers running as some homebrew + // i.e LR.O doesn't bother to setup the timers + + mTIM_0_BKUP=0x9e; + mTIM_0_ENABLE_RELOAD=TRUE; + mTIM_0_ENABLE_COUNT=TRUE; + + mTIM_2_BKUP=0x68; + mTIM_2_ENABLE_RELOAD=TRUE; + mTIM_2_ENABLE_COUNT=TRUE; + mTIM_2_LINKING=7; + + mDISPCTL_DMAEnable=TRUE; + mDISPCTL_Flip=FALSE; + mDISPCTL_FourColour=0; + mDISPCTL_Colour=TRUE; +} + +void CMikie::ComLynxCable(int status) +{ + mUART_CABLE_PRESENT=status; +} + +void CMikie::ComLynxRxData(int data) +{ + TRACE_MIKIE1("ComLynxRxData() - Received %04x",data); + // Copy over the data + if(mUART_Rx_waiting>2)&003); + } +#else + for(Spot.Index=0;Spot.Index<4096;Spot.Index++) { + mColourMap[Spot.Index]=((Spot.Colours.Red<<12)&0xf000) | ((Spot.Colours.Red<<8)&0x0800); + mColourMap[Spot.Index]|=((Spot.Colours.Green<<7)&0x0780) | ((Spot.Colours.Green<<3)&0x0060); + mColourMap[Spot.Index]|=((Spot.Colours.Blue<<1)&0x001e) | ((Spot.Colours.Blue>>3)&0x0001); + } + + if (mDisplayFormat == MIKIE_PIXEL_FORMAT_16BPP_565_BE) { + for(int i=0;i<4096;i++) { + mColourMap[i] = mColourMap[i] << 8 | mColourMap[i] >> 8; + } + } +#endif + + // Reset screen related counters/vars + mTIM_0_CURRENT=0; + mTIM_2_CURRENT=0; + + // Fix lastcount so that timer update will definitely occur + mTIM_0_LAST_COUNT-=(1<<(4+mTIM_0_LINKING))+1; + mTIM_2_LAST_COUNT-=(1<<(4+mTIM_2_LINKING))+1; + + // Force immediate timer update + gNextTimerEvent=gSystemCycleCount; +} + +inline void CMikie::ResetDisplayPtr() +{ + if (mDisplayRotate != mDisplayRotate_Pending) + { + mDisplayRotate = mDisplayRotate_Pending; + } + + switch(mDisplayRotate) + { + case MIKIE_ROTATE_L: + mpDisplayCurrent=gPrimaryFrameBuffer+(mDisplayPitch*(HANDY_SCREEN_WIDTH-1)) + sizeof(HandyPixel)*(320-HANDY_SCREEN_HEIGHT*2)/2; + break; + case MIKIE_ROTATE_R: + mpDisplayCurrent=gPrimaryFrameBuffer + sizeof(HandyPixel)*(320 -(320-HANDY_SCREEN_HEIGHT*2)/2); + break; + default: + mpDisplayCurrent=gPrimaryFrameBuffer; + break; + } +} + +inline ULONG CMikie::DisplayRenderLine(void) +{ + HandyPixel *bitmap_tmp=NULL; + ULONG source,loop; + ULONG work_done=0; + + if(!gPrimaryFrameBuffer) return 0; + if(!mpDisplayCurrent) return 0; + if(!mDISPCTL_DMAEnable) return 0; + + // if(mLynxLine&0x80000000) return 0; + + // Set the timer interrupt flag + if(mTimerInterruptMask&0x01) { + TRACE_MIKIE0("Update() - TIMER0 IRQ Triggered (Line Timer)"); + mTimerStatusFlags|=0x01; + gSystemIRQ=TRUE; // Added 19/09/06 fix for IRQ issue + } + + // Logic says it should be 101 but testing on an actual lynx shows the rest + // period is between lines 102,101,100 with the new line being latched at + // the beginning of count==99 hence the code below !! + + // Emulate REST signal + if(mLynxLine==mTIM_2_BKUP-2 || mLynxLine==mTIM_2_BKUP-3 || mLynxLine==mTIM_2_BKUP-4) mIODAT_REST_SIGNAL=TRUE; + else mIODAT_REST_SIGNAL=FALSE; + + if(mLynxLine==(mTIM_2_BKUP-3)) { + if(mDISPCTL_Flip) { + mLynxAddr=mDisplayAddress&0xfffc; + mLynxAddr+=3; + } else { + mLynxAddr=mDisplayAddress&0xfffc; + } + // Trigger line rending to start + mLynxLineDMACounter=102; + + // Reset frame buffer pointer to top of screen + ResetDisplayPtr(); + } + + // Decrement line counter logic + if(mLynxLine) mLynxLine--; + + // Do 102 lines, nothing more, less is OK. + if(mLynxLineDMACounter) { + // TRACE_MIKIE1("Update() - Screen DMA, line %03d",line_count); + mLynxLineDMACounter--; + + // Cycle hit for a 80 RAM access in rendering a line + work_done+=(80+100)*DMA_RDWR_CYC; + + if (!gRenderFrame) return work_done; + + // Mikie screen DMA can only see the system RAM.... + // (Step through bitmap, line at a time) + + // Assign the temporary pointer; + bitmap_tmp=(HandyPixel*)mpDisplayCurrent; + + switch(mDisplayRotate) + { + + case MIKIE_ROTATE_L: + for(loop=0;loop>4].Index]; + *(bitmap_tmp+1)=mColourMap[mPalette[source>>4].Index]; + bitmap_tmp-=HANDY_SCREEN_STRIDE; + } + else + { + mLynxAddr++; + *(bitmap_tmp)=mColourMap[mPalette[source>>4].Index]; + *(bitmap_tmp+1)=mColourMap[mPalette[source>>4].Index]; + bitmap_tmp-=HANDY_SCREEN_STRIDE; + *(bitmap_tmp)=mColourMap[mPalette[source&0x0f].Index]; + *(bitmap_tmp+1)=mColourMap[mPalette[source&0x0f].Index]; + bitmap_tmp-=HANDY_SCREEN_STRIDE; + } + } + mpDisplayCurrent+=2*sizeof(HandyPixel); + break; + case MIKIE_ROTATE_R: + for(loop=0;loop>4].Index]; + *(bitmap_tmp-1)=mColourMap[mPalette[source>>4].Index]; + bitmap_tmp+=HANDY_SCREEN_STRIDE; + } + else + { + mLynxAddr++; + *(bitmap_tmp)=mColourMap[mPalette[source>>4].Index]; + *(bitmap_tmp-1)=mColourMap[mPalette[source>>4].Index]; + bitmap_tmp+=HANDY_SCREEN_STRIDE; + *(bitmap_tmp)=mColourMap[mPalette[source&0x0f].Index]; + *(bitmap_tmp-1)=mColourMap[mPalette[source&0x0f].Index]; + bitmap_tmp+=HANDY_SCREEN_STRIDE; + } + } + mpDisplayCurrent-=2*sizeof(HandyPixel); + break; + + default: + HandyPixel *bitmappt=bitmap_tmp; + + for(loop=0;loop>4].Index]; + *(bitmappt++)=mColourMap[mPalette[source>>4].Index]; + *(bitmappt+HANDY_SCREEN_STRIDE)=mColourMap[mPalette[source>>4].Index]; + *(bitmappt++)=mColourMap[mPalette[source>>4].Index]; + } + else + { + mLynxAddr++; + *(bitmappt+HANDY_SCREEN_STRIDE)=mColourMap[mPalette[source>>4].Index]; + *(bitmappt++)=mColourMap[mPalette[source>>4].Index]; + *(bitmappt+HANDY_SCREEN_STRIDE)=mColourMap[mPalette[source>>4].Index]; + *(bitmappt++)=mColourMap[mPalette[source>>4].Index]; + *(bitmappt+HANDY_SCREEN_STRIDE)=mColourMap[mPalette[source&0x0f].Index]; + *(bitmappt++)=mColourMap[mPalette[source&0x0f].Index]; + *(bitmappt+HANDY_SCREEN_STRIDE)=mColourMap[mPalette[source&0x0f].Index]; + *(bitmappt++)=mColourMap[mPalette[source&0x0f].Index]; + } + } + mpDisplayCurrent+=mDisplayPitch*2; + break; + } + } + return work_done; +} + +// Peek/Poke memory handlers + +void CMikie::Poke(ULONG addr,UBYTE data) +{ + switch(addr&0xff) { + case (TIM0BKUP&0xff): + mTIM_0_BKUP=data; + TRACE_MIKIE2("Poke(TIM0BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM1BKUP&0xff): + mTIM_1_BKUP=data; + TRACE_MIKIE2("Poke(TIM1BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM2BKUP&0xff): + mTIM_2_BKUP=data; + TRACE_MIKIE2("Poke(TIM2BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM3BKUP&0xff): + mTIM_3_BKUP=data; + TRACE_MIKIE2("Poke(TIM3BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM4BKUP&0xff): + mTIM_4_BKUP=data; + TRACE_MIKIE2("Poke(TIM4BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM5BKUP&0xff): + mTIM_5_BKUP=data; + TRACE_MIKIE2("Poke(TIM5BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM6BKUP&0xff): + mTIM_6_BKUP=data; + TRACE_MIKIE2("Poke(TIM6BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM7BKUP&0xff): + mTIM_7_BKUP=data; + TRACE_MIKIE2("Poke(TIM7BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + + case (TIM0CTLA&0xff): + mTimerInterruptMask&=(0x01^0xff); + mTimerInterruptMask|=(data&0x80)?0x01:0x00; + mTIM_0_ENABLE_RELOAD=data&0x10; + mTIM_0_ENABLE_COUNT=data&0x08; + mTIM_0_LINKING=data&0x07; + if(data&0x40) mTIM_0_TIMER_DONE=0; + if(data&0x48) { + mTIM_0_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(TIM0CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM1CTLA&0xff): + mTimerInterruptMask&=(0x02^0xff); + mTimerInterruptMask|=(data&0x80)?0x02:0x00; + mTIM_1_ENABLE_RELOAD=data&0x10; + mTIM_1_ENABLE_COUNT=data&0x08; + mTIM_1_LINKING=data&0x07; + if(data&0x40) mTIM_1_TIMER_DONE=0; + if(data&0x48) { + mTIM_1_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(TIM1CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM2CTLA&0xff): + mTimerInterruptMask&=(0x04^0xff); + mTimerInterruptMask|=(data&0x80)?0x04:0x00; + mTIM_2_ENABLE_RELOAD=data&0x10; + mTIM_2_ENABLE_COUNT=data&0x08; + mTIM_2_LINKING=data&0x07; + if(data&0x40) mTIM_2_TIMER_DONE=0; + if(data&0x48) { + mTIM_2_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(TIM2CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM3CTLA&0xff): + mTimerInterruptMask&=(0x08^0xff); + mTimerInterruptMask|=(data&0x80)?0x08:0x00; + mTIM_3_ENABLE_RELOAD=data&0x10; + mTIM_3_ENABLE_COUNT=data&0x08; + mTIM_3_LINKING=data&0x07; + if(data&0x40) mTIM_3_TIMER_DONE=0; + if(data&0x48) { + mTIM_3_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(TIM3CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM4CTLA&0xff): + // Timer 4 can never generate interrupts as its timer output is used + // to drive the UART clock generator + mTIM_4_ENABLE_RELOAD=data&0x10; + mTIM_4_ENABLE_COUNT=data&0x08; + mTIM_4_LINKING=data&0x07; + if(data&0x40) mTIM_4_TIMER_DONE=0; + if(data&0x48) { + mTIM_4_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(TIM4CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM5CTLA&0xff): + mTimerInterruptMask&=(0x20^0xff); + mTimerInterruptMask|=(data&0x80)?0x20:0x00; + mTIM_5_ENABLE_RELOAD=data&0x10; + mTIM_5_ENABLE_COUNT=data&0x08; + mTIM_5_LINKING=data&0x07; + if(data&0x40) mTIM_5_TIMER_DONE=0; + if(data&0x48) { + mTIM_5_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(TIM5CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM6CTLA&0xff): + mTimerInterruptMask&=(0x40^0xff); + mTimerInterruptMask|=(data&0x80)?0x40:0x00; + mTIM_6_ENABLE_RELOAD=data&0x10; + mTIM_6_ENABLE_COUNT=data&0x08; + mTIM_6_LINKING=data&0x07; + if(data&0x40) mTIM_6_TIMER_DONE=0; + if(data&0x48) { + mTIM_6_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(TIM6CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM7CTLA&0xff): + mTimerInterruptMask&=(0x80^0xff); + mTimerInterruptMask|=(data&0x80)?0x80:0x00; + mTIM_7_ENABLE_RELOAD=data&0x10; + mTIM_7_ENABLE_COUNT=data&0x08; + mTIM_7_LINKING=data&0x07; + if(data&0x40) mTIM_7_TIMER_DONE=0; + if(data&0x48) { + mTIM_7_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(TIM7CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + + case (TIM0CNT&0xff): + mTIM_0_CURRENT=data; + gNextTimerEvent=gSystemCycleCount; + TRACE_MIKIE2("Poke(TIM0CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM1CNT&0xff): + mTIM_1_CURRENT=data; + gNextTimerEvent=gSystemCycleCount; + TRACE_MIKIE2("Poke(TIM1CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM2CNT&0xff): + mTIM_2_CURRENT=data; + gNextTimerEvent=gSystemCycleCount; + TRACE_MIKIE2("Poke(TIM2CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM3CNT&0xff): + mTIM_3_CURRENT=data; + gNextTimerEvent=gSystemCycleCount; + TRACE_MIKIE2("Poke(TIM3CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM4CNT&0xff): + mTIM_4_CURRENT=data; + gNextTimerEvent=gSystemCycleCount; + TRACE_MIKIE2("Poke(TIM4CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM5CNT&0xff): + mTIM_5_CURRENT=data; + gNextTimerEvent=gSystemCycleCount; + TRACE_MIKIE2("Poke(TIM5CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM6CNT&0xff): + mTIM_6_CURRENT=data; + gNextTimerEvent=gSystemCycleCount; + TRACE_MIKIE2("Poke(TIM6CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (TIM7CNT&0xff): + mTIM_7_CURRENT=data; + gNextTimerEvent=gSystemCycleCount; + TRACE_MIKIE2("Poke(TIM7CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (TIM0CTLB&0xff): + mTIM_0_TIMER_DONE=data&0x08; + mTIM_0_LAST_CLOCK=data&0x04; + mTIM_0_BORROW_IN=data&0x02; + mTIM_0_BORROW_OUT=data&0x01; + TRACE_MIKIE2("Poke(TIM0CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // BlowOut(); + break; + case (TIM1CTLB&0xff): + mTIM_1_TIMER_DONE=data&0x08; + mTIM_1_LAST_CLOCK=data&0x04; + mTIM_1_BORROW_IN=data&0x02; + mTIM_1_BORROW_OUT=data&0x01; + TRACE_MIKIE2("Poke(TIM1CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // BlowOut(); + break; + case (TIM2CTLB&0xff): + mTIM_2_TIMER_DONE=data&0x08; + mTIM_2_LAST_CLOCK=data&0x04; + mTIM_2_BORROW_IN=data&0x02; + mTIM_2_BORROW_OUT=data&0x01; + TRACE_MIKIE2("Poke(TIM2CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // BlowOut(); + break; + case (TIM3CTLB&0xff): + mTIM_3_TIMER_DONE=data&0x08; + mTIM_3_LAST_CLOCK=data&0x04; + mTIM_3_BORROW_IN=data&0x02; + mTIM_3_BORROW_OUT=data&0x01; + TRACE_MIKIE2("Poke(TIM3CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // BlowOut(); + break; + case (TIM4CTLB&0xff): + mTIM_4_TIMER_DONE=data&0x08; + mTIM_4_LAST_CLOCK=data&0x04; + mTIM_4_BORROW_IN=data&0x02; + mTIM_4_BORROW_OUT=data&0x01; + TRACE_MIKIE2("Poke(TIM4CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // BlowOut(); + break; + case (TIM5CTLB&0xff): + mTIM_5_TIMER_DONE=data&0x08; + mTIM_5_LAST_CLOCK=data&0x04; + mTIM_5_BORROW_IN=data&0x02; + mTIM_5_BORROW_OUT=data&0x01; + TRACE_MIKIE2("Poke(TIM5CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // BlowOut(); + break; + case (TIM6CTLB&0xff): + mTIM_6_TIMER_DONE=data&0x08; + mTIM_6_LAST_CLOCK=data&0x04; + mTIM_6_BORROW_IN=data&0x02; + mTIM_6_BORROW_OUT=data&0x01; + TRACE_MIKIE2("Poke(TIM6CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // BlowOut(); + break; + case (TIM7CTLB&0xff): + mTIM_7_TIMER_DONE=data&0x08; + mTIM_7_LAST_CLOCK=data&0x04; + mTIM_7_BORROW_IN=data&0x02; + mTIM_7_BORROW_OUT=data&0x01; + TRACE_MIKIE2("Poke(TIM7CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // BlowOut(); + break; + + case (AUD0VOL&0xff): + mAUDIO_0_VOLUME=(SBYTE)data; + TRACE_MIKIE2("Poke(AUD0VOL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD0SHFTFB&0xff): + mAUDIO_0_WAVESHAPER&=0x001fff; + mAUDIO_0_WAVESHAPER|=(ULONG)data<<13; + TRACE_MIKIE2("Poke(AUD0SHFTB,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD0OUTVAL&0xff): + mAUDIO_OUTPUT[0]=data; + TRACE_MIKIE2("Poke(AUD0OUTVAL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD0L8SHFT&0xff): + mAUDIO_0_WAVESHAPER&=0x1fff00; + mAUDIO_0_WAVESHAPER|=data; + TRACE_MIKIE2("Poke(AUD0L8SHFT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD0TBACK&0xff): + // Counter is disabled when backup is zero for optimisation + // due to the fact that the output frequency will be above audio + // range, we must update the last use position to stop problems + if(!mAUDIO_0_BKUP && data) { + mAUDIO_0_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + mAUDIO_0_BKUP=data; + TRACE_MIKIE2("Poke(AUD0TBACK,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD0CTL&0xff): + mAUDIO_0_ENABLE_RELOAD=data&0x10; + mAUDIO_0_ENABLE_COUNT=data&0x08; + mAUDIO_0_LINKING=data&0x07; + mAUDIO_0_INTEGRATE_ENABLE=data&0x20; + if(data&0x40) mAUDIO_0_TIMER_DONE=0; + mAUDIO_0_WAVESHAPER&=0x1fefff; + mAUDIO_0_WAVESHAPER|=(data&0x80)?0x001000:0x000000; + if(data&0x48) { + mAUDIO_0_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(AUD0CTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD0COUNT&0xff): + mAUDIO_0_CURRENT=data; + TRACE_MIKIE2("Poke(AUD0COUNT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD0MISC&0xff): + mAUDIO_0_WAVESHAPER&=0x1ff0ff; + mAUDIO_0_WAVESHAPER|=(data&0xf0)<<4; + mAUDIO_0_BORROW_IN=data&0x02; + mAUDIO_0_BORROW_OUT=data&0x01; + mAUDIO_0_LAST_CLOCK=data&0x04; + TRACE_MIKIE2("Poke(AUD0MISC,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (AUD1VOL&0xff): + mAUDIO_1_VOLUME=(SBYTE)data; + TRACE_MIKIE2("Poke(AUD1VOL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD1SHFTFB&0xff): + mAUDIO_1_WAVESHAPER&=0x001fff; + mAUDIO_1_WAVESHAPER|=(ULONG)data<<13; + TRACE_MIKIE2("Poke(AUD1SHFTFB,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD1OUTVAL&0xff): + mAUDIO_OUTPUT[1]=data; + TRACE_MIKIE2("Poke(AUD1OUTVAL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD1L8SHFT&0xff): + mAUDIO_1_WAVESHAPER&=0x1fff00; + mAUDIO_1_WAVESHAPER|=data; + TRACE_MIKIE2("Poke(AUD1L8SHFT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD1TBACK&0xff): + // Counter is disabled when backup is zero for optimisation + // due to the fact that the output frequency will be above audio + // range, we must update the last use position to stop problems + if(!mAUDIO_1_BKUP && data) { + mAUDIO_1_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + mAUDIO_1_BKUP=data; + TRACE_MIKIE2("Poke(AUD1TBACK,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD1CTL&0xff): + mAUDIO_1_ENABLE_RELOAD=data&0x10; + mAUDIO_1_ENABLE_COUNT=data&0x08; + mAUDIO_1_LINKING=data&0x07; + mAUDIO_1_INTEGRATE_ENABLE=data&0x20; + if(data&0x40) mAUDIO_1_TIMER_DONE=0; + mAUDIO_1_WAVESHAPER&=0x1fefff; + mAUDIO_1_WAVESHAPER|=(data&0x80)?0x001000:0x000000; + if(data&0x48) { + mAUDIO_1_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(AUD1CTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD1COUNT&0xff): + mAUDIO_1_CURRENT=data; + TRACE_MIKIE2("Poke(AUD1COUNT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD1MISC&0xff): + mAUDIO_1_WAVESHAPER&=0x1ff0ff; + mAUDIO_1_WAVESHAPER|=(data&0xf0)<<4; + mAUDIO_1_BORROW_IN=data&0x02; + mAUDIO_1_BORROW_OUT=data&0x01; + mAUDIO_1_LAST_CLOCK=data&0x04; + TRACE_MIKIE2("Poke(AUD1MISC,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (AUD2VOL&0xff): + mAUDIO_2_VOLUME=(SBYTE)data; + TRACE_MIKIE2("Poke(AUD2VOL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD2SHFTFB&0xff): + mAUDIO_2_WAVESHAPER&=0x001fff; + mAUDIO_2_WAVESHAPER|=(ULONG)data<<13; + TRACE_MIKIE2("Poke(AUD2VSHFTFB,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD2OUTVAL&0xff): + mAUDIO_OUTPUT[2]=data; + TRACE_MIKIE2("Poke(AUD2OUTVAL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD2L8SHFT&0xff): + mAUDIO_2_WAVESHAPER&=0x1fff00; + mAUDIO_2_WAVESHAPER|=data; + TRACE_MIKIE2("Poke(AUD2L8SHFT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD2TBACK&0xff): + // Counter is disabled when backup is zero for optimisation + // due to the fact that the output frequency will be above audio + // range, we must update the last use position to stop problems + if(!mAUDIO_2_BKUP && data) { + mAUDIO_2_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + mAUDIO_2_BKUP=data; + TRACE_MIKIE2("Poke(AUD2TBACK,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD2CTL&0xff): + mAUDIO_2_ENABLE_RELOAD=data&0x10; + mAUDIO_2_ENABLE_COUNT=data&0x08; + mAUDIO_2_LINKING=data&0x07; + mAUDIO_2_INTEGRATE_ENABLE=data&0x20; + if(data&0x40) mAUDIO_2_TIMER_DONE=0; + mAUDIO_2_WAVESHAPER&=0x1fefff; + mAUDIO_2_WAVESHAPER|=(data&0x80)?0x001000:0x000000; + if(data&0x48) { + mAUDIO_2_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(AUD2CTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD2COUNT&0xff): + mAUDIO_2_CURRENT=data; + TRACE_MIKIE2("Poke(AUD2COUNT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD2MISC&0xff): + mAUDIO_2_WAVESHAPER&=0x1ff0ff; + mAUDIO_2_WAVESHAPER|=(data&0xf0)<<4; + mAUDIO_2_BORROW_IN=data&0x02; + mAUDIO_2_BORROW_OUT=data&0x01; + mAUDIO_2_LAST_CLOCK=data&0x04; + TRACE_MIKIE2("Poke(AUD2MISC,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (AUD3VOL&0xff): + mAUDIO_3_VOLUME=(SBYTE)data; + TRACE_MIKIE2("Poke(AUD3VOL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD3SHFTFB&0xff): + mAUDIO_3_WAVESHAPER&=0x001fff; + mAUDIO_3_WAVESHAPER|=(ULONG)data<<13; + TRACE_MIKIE2("Poke(AUD3SHFTFB,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD3OUTVAL&0xff): + mAUDIO_OUTPUT[3]=data; + TRACE_MIKIE2("Poke(AUD3OUTVAL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD3L8SHFT&0xff): + mAUDIO_3_WAVESHAPER&=0x1fff00; + mAUDIO_3_WAVESHAPER|=data; + TRACE_MIKIE2("Poke(AUD3L8SHFT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD3TBACK&0xff): + // Counter is disabled when backup is zero for optimisation + // due to the fact that the output frequency will be above audio + // range, we must update the last use position to stop problems + if(!mAUDIO_3_BKUP && data) { + mAUDIO_3_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + mAUDIO_3_BKUP=data; + TRACE_MIKIE2("Poke(AUD3TBACK,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD3CTL&0xff): + mAUDIO_3_ENABLE_RELOAD=data&0x10; + mAUDIO_3_ENABLE_COUNT=data&0x08; + mAUDIO_3_LINKING=data&0x07; + mAUDIO_3_INTEGRATE_ENABLE=data&0x20; + if(data&0x40) mAUDIO_3_TIMER_DONE=0; + mAUDIO_3_WAVESHAPER&=0x1fefff; + mAUDIO_3_WAVESHAPER|=(data&0x80)?0x001000:0x000000; + if(data&0x48) { + mAUDIO_3_LAST_COUNT=gSystemCycleCount; + gNextTimerEvent=gSystemCycleCount; + } + TRACE_MIKIE2("Poke(AUD3CTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD3COUNT&0xff): + mAUDIO_3_CURRENT=data; + TRACE_MIKIE2("Poke(AUD3COUNT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (AUD3MISC&0xff): + mAUDIO_3_WAVESHAPER&=0x1ff0ff; + mAUDIO_3_WAVESHAPER|=(data&0xf0)<<4; + mAUDIO_3_BORROW_IN=data&0x02; + mAUDIO_3_BORROW_OUT=data&0x01; + mAUDIO_3_LAST_CLOCK=data&0x04; + TRACE_MIKIE2("Poke(AUD3MISC,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (ATTEN_A&0xff): + mAUDIO_ATTEN[0] = data; + TRACE_MIKIE2("Poke(ATTEN_A ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (ATTEN_B&0xff): + mAUDIO_ATTEN[1] = data; + TRACE_MIKIE2("Poke(ATTEN_B ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (ATTEN_C&0xff): + mAUDIO_ATTEN[2] = data; + TRACE_MIKIE2("Poke(ATTEN_C ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (ATTEN_D&0xff): + mAUDIO_ATTEN[3] = data; + TRACE_MIKIE2("Poke(ATTEN_D ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (MPAN&0xff): + TRACE_MIKIE2("Poke(MPAN ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mPAN = data; + break; + + case (MSTEREO&0xff): + TRACE_MIKIE2("Poke(MSTEREO,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mSTEREO=data; + // if(!(mSTEREO&0x11) && (data&0x11)) + // { + // mAUDIO_0_LAST_COUNT=gSystemCycleCount; + // gNextTimerEvent=gSystemCycleCount; + // } + // if(!(mSTEREO&0x22) && (data&0x22)) + // { + // mAUDIO_1_LAST_COUNT=gSystemCycleCount; + // gNextTimerEvent=gSystemCycleCount; + // } + // if(!(mSTEREO&0x44) && (data&0x44)) + // { + // mAUDIO_2_LAST_COUNT=gSystemCycleCount; + // gNextTimerEvent=gSystemCycleCount; + // } + // if(!(mSTEREO&0x88) && (data&0x88)) + // { + // mAUDIO_3_LAST_COUNT=gSystemCycleCount; + // gNextTimerEvent=gSystemCycleCount; + // } + break; + + case (INTRST&0xff): + data^=0xff; + mTimerStatusFlags&=data; + gNextTimerEvent=gSystemCycleCount; + // 22/09/06 Fix to championship rally, IRQ not getting cleared with edge triggered system + gSystemIRQ=(mTimerStatusFlags&mTimerInterruptMask)?TRUE:FALSE; + // 22/09/06 Fix to championship rally, IRQ not getting cleared with edge triggered system + TRACE_MIKIE2("Poke(INTRST ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (INTSET&0xff): + TRACE_MIKIE2("Poke(INTSET ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mTimerStatusFlags|=data; + // 22/09/06 Fix to championship rally, IRQ not getting cleared with edge triggered system + gSystemIRQ=(mTimerStatusFlags&mTimerInterruptMask)?TRUE:FALSE; + // 22/09/06 Fix to championship rally, IRQ not getting cleared with edge triggered system + gNextTimerEvent=gSystemCycleCount; + break; + + case (SYSCTL1&0xff): + TRACE_MIKIE2("Poke(SYSCTL1 ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + if(!(data&0x02)) { + C6502_REGS regs; + mSystem.mCpu->GetRegs(regs); + log_printf("CMikie::Poke(SYSCTL1) - Lynx power down occurred at PC=$%04x.\nResetting system.\n", regs.PC); + mSystem.Reset(); + gSystemHalt=TRUE; + } + mSystem.mCart->CartAddressStrobe((data&0x01)?TRUE:FALSE); + if(mSystem.mEEPROM->Available()) mSystem.mEEPROM->ProcessEepromCounter(mSystem.mCart->GetCounterValue()); + break; + + case (MIKEYSREV&0xff): + TRACE_MIKIE2("Poke(MIKEYSREV,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (IODIR&0xff): + TRACE_MIKIE2("Poke(IODIR ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mIODIR=data; + if(mSystem.mEEPROM->Available()) mSystem.mEEPROM->ProcessEepromIO(mIODIR,mIODAT); + break; + + case (IODAT&0xff): + TRACE_MIKIE2("Poke(IODAT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mIODAT=data; + mSystem.mCart->CartAddressData((mIODAT&0x02)?TRUE:FALSE); + // Enable cart writes to BANK1 on AUDIN if AUDIN is set to output + if(mIODIR&0x10) mSystem.mCart->mWriteEnableBank1=(mIODAT&0x10)?TRUE:FALSE;// there is no reason to use AUDIN as Write Enable or latch. private patch??? TODO + if(mSystem.mEEPROM->Available()) mSystem.mEEPROM->ProcessEepromIO(mIODIR,mIODAT); + break; + + case (SERCTL&0xff): + TRACE_MIKIE2("Poke(SERCTL ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mUART_TX_IRQ_ENABLE=(data&0x80)?true:false; + mUART_RX_IRQ_ENABLE=(data&0x40)?true:false; + mUART_PARITY_ENABLE=(data&0x10)?true:false; + mUART_SENDBREAK=data&0x02; + mUART_PARITY_EVEN=data&0x01; + + // Reset all errors if required + if(data&0x08) { + mUART_Rx_overun_error=0; + mUART_Rx_framing_error=0; + } + + if(mUART_SENDBREAK) { + // Trigger send break, it will self sustain as long as sendbreak is set + mUART_TX_COUNTDOWN=UART_TX_TIME_PERIOD; + // Loop back what we transmitted + ComLynxTxLoopback(UART_BREAK_CODE); + } + break; + + case (SERDAT&0xff): + TRACE_MIKIE2("Poke(SERDAT ,%04x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // + // Fake transmission, set counter to be decremented by Timer 4 + // + // ComLynx only has one output pin, hence Rx & Tx are shorted + // therefore any transmitted data will loopback + // + mUART_TX_DATA=data; + // Calculate Parity data + if(mUART_PARITY_ENABLE) { + // Calc parity value + // Leave at zero !! + } else { + // If disabled then the PAREVEN bit is sent + if(mUART_PARITY_EVEN) data|=0x0100; + } + // Set countdown to transmission + mUART_TX_COUNTDOWN=UART_TX_TIME_PERIOD; + // Loop back what we transmitted + ComLynxTxLoopback(mUART_TX_DATA); + break; + + case (SDONEACK&0xff): + TRACE_MIKIE2("Poke(SDONEACK,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + case (CPUSLEEP&0xff): + TRACE_MIKIE2("Poke(CPUSLEEP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + // + // We must do "cycles_used" cycles of the system with the CPU sleeping + // to compensate for the sprite painting, Mikie update will autowake the + // CPU at the right time. + // + TRACE_MIKIE0("*********************************************************"); + TRACE_MIKIE0("**** CPU SLEEP STARTED ****"); + TRACE_MIKIE0("*********************************************************"); + gCPUWakeupTime=gSystemCycleCount+(SLONG)mSystem.mSusie->PaintSprites(); + gSystemCPUSleep=TRUE; + TRACE_MIKIE2("Poke(CPUSLEEP,%02x) wakeup at cycle =%012d",data,gCPUWakeupTime); + break; + + case (DISPCTL&0xff): + TRACE_MIKIE2("Poke(DISPCTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mDISPCTL_DMAEnable=data&0x1; + mDISPCTL_Flip=data&0x2; + mDISPCTL_FourColour=data&0x4; + mDISPCTL_Colour=data&0x8; + break; + case (PBKUP&0xff): + TRACE_MIKIE2("Poke(PBKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (DISPADRL&0xff): + TRACE_MIKIE2("Poke(DISPADRL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mDisplayAddress&=0xff00; + mDisplayAddress+=data; + break; + + case (DISPADRH&0xff): + TRACE_MIKIE2("Poke(DISPADRH,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mDisplayAddress&=0x00ff; + mDisplayAddress+=(data<<8); + break; + + case (Mtest0&0xff): + case (Mtest1&0xff): + case (Mtest2&0xff): + // Test registers are unimplemented + // lets hope no programs use them. + TRACE_MIKIE2("Poke(MTEST0/1/2,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + break; + + case (0xfd97&0xff): + // This code is to intercept calls to the fake ROM + switch (mSystem.mCpu->GetPC()) { + case 0xFE00+3: mSystem.HLE_BIOS_FE00(); break; + case 0xFE19+3: mSystem.HLE_BIOS_FE19(); break; + case 0xFE4A+3: mSystem.HLE_BIOS_FE4A(); break; + case 0xFF80+3: mSystem.HLE_BIOS_FF80(); break; + default: log_printf("BIOS: Missing function $%04X\n", mSystem.mCpu->GetPC()); + } + break; + case (GREEN0&0xff): + case (GREEN1&0xff): + case (GREEN2&0xff): + case (GREEN3&0xff): + case (GREEN4&0xff): + case (GREEN5&0xff): + case (GREEN6&0xff): + case (GREEN7&0xff): + case (GREEN8&0xff): + case (GREEN9&0xff): + case (GREENA&0xff): + case (GREENB&0xff): + case (GREENC&0xff): + case (GREEND&0xff): + case (GREENE&0xff): + case (GREENF&0xff): + TRACE_MIKIE2("Poke(GREENPAL0-F,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mPalette[addr&0x0f].Colours.Green=data&0x0f; + break; + + case (BLUERED0&0xff): + case (BLUERED1&0xff): + case (BLUERED2&0xff): + case (BLUERED3&0xff): + case (BLUERED4&0xff): + case (BLUERED5&0xff): + case (BLUERED6&0xff): + case (BLUERED7&0xff): + case (BLUERED8&0xff): + case (BLUERED9&0xff): + case (BLUEREDA&0xff): + case (BLUEREDB&0xff): + case (BLUEREDC&0xff): + case (BLUEREDD&0xff): + case (BLUEREDE&0xff): + case (BLUEREDF&0xff): + TRACE_MIKIE2("Poke(BLUEREDPAL0-F,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); + mPalette[addr&0x0f].Colours.Blue=(data&0xf0)>>4; + mPalette[addr&0x0f].Colours.Red=data&0x0f; + break; + + // Errors on read only register accesses + + case (MAGRDY0&0xff): + case (MAGRDY1&0xff): + case (AUDIN&0xff): + case (MIKEYHREV&0xff): + TRACE_MIKIE3("Poke(%04x,%02x) - Poke to read only register location at PC=%04x",addr,data,mSystem.mCpu->GetPC()); + break; + + // Errors on illegal location accesses + + default: + TRACE_MIKIE3("Poke(%04x,%02x) - Poke to illegal location at PC=%04x",addr,data,mSystem.mCpu->GetPC()); + break; + } + + if(addr >= AUD0VOL && addr <= MSTEREO) + UpdateSound(); +} + + + +UBYTE CMikie::Peek(ULONG addr) +{ + ULONG retval=0; + + switch(addr & 0xff) { + // Timer control registers + case (TIM0BKUP&0xff): + TRACE_MIKIE2("Peek(TIM0KBUP ,%02x) at PC=%04x",mTIM_0_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_0_BKUP; + case (TIM1BKUP&0xff): + TRACE_MIKIE2("Peek(TIM1KBUP ,%02x) at PC=%04x",mTIM_1_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_1_BKUP; + case (TIM2BKUP&0xff): + TRACE_MIKIE2("Peek(TIM2KBUP ,%02x) at PC=%04x",mTIM_2_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_2_BKUP; + case (TIM3BKUP&0xff): + TRACE_MIKIE2("Peek(TIM3KBUP ,%02x) at PC=%04x",mTIM_3_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_3_BKUP; + case (TIM4BKUP&0xff): + TRACE_MIKIE2("Peek(TIM4KBUP ,%02x) at PC=%04x",mTIM_4_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_4_BKUP; + case (TIM5BKUP&0xff): + TRACE_MIKIE2("Peek(TIM5KBUP ,%02x) at PC=%04x",mTIM_5_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_5_BKUP; + case (TIM6BKUP&0xff): + TRACE_MIKIE2("Peek(TIM6KBUP ,%02x) at PC=%04x",mTIM_6_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_6_BKUP; + case (TIM7BKUP&0xff): + TRACE_MIKIE2("Peek(TIM7KBUP ,%02x) at PC=%04x",mTIM_7_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_7_BKUP; + + case (TIM0CTLA&0xff): + retval|=(mTimerInterruptMask&0x01)?0x80:0x00; + retval|=(mTIM_0_ENABLE_RELOAD)?0x10:0x00; + retval|=(mTIM_0_ENABLE_COUNT)?0x08:0x00; + retval|=mTIM_0_LINKING; + TRACE_MIKIE2("Peek(TIM0CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM1CTLA&0xff): + retval|=(mTimerInterruptMask&0x02)?0x80:0x00; + retval|=(mTIM_1_ENABLE_RELOAD)?0x10:0x00; + retval|=(mTIM_1_ENABLE_COUNT)?0x08:0x00; + retval|=mTIM_1_LINKING; + TRACE_MIKIE2("Peek(TIM1CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM2CTLA&0xff): + retval|=(mTimerInterruptMask&0x04)?0x80:0x00; + retval|=(mTIM_2_ENABLE_RELOAD)?0x10:0x00; + retval|=(mTIM_2_ENABLE_COUNT)?0x08:0x00; + retval|=mTIM_2_LINKING; + TRACE_MIKIE2("Peek(TIM2CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM3CTLA&0xff): + retval|=(mTimerInterruptMask&0x08)?0x80:0x00; + retval|=(mTIM_3_ENABLE_RELOAD)?0x10:0x00; + retval|=(mTIM_3_ENABLE_COUNT)?0x08:0x00; + retval|=mTIM_3_LINKING; + TRACE_MIKIE2("Peek(TIM3CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM4CTLA&0xff): + retval|=(mTimerInterruptMask&0x10)?0x80:0x00; + retval|=(mTIM_4_ENABLE_RELOAD)?0x10:0x00; + retval|=(mTIM_4_ENABLE_COUNT)?0x08:0x00; + retval|=mTIM_4_LINKING; + TRACE_MIKIE2("Peek(TIM4CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM5CTLA&0xff): + retval|=(mTimerInterruptMask&0x20)?0x80:0x00; + retval|=(mTIM_5_ENABLE_RELOAD)?0x10:0x00; + retval|=(mTIM_5_ENABLE_COUNT)?0x08:0x00; + retval|=mTIM_5_LINKING; + TRACE_MIKIE2("Peek(TIM5CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM6CTLA&0xff): + retval|=(mTimerInterruptMask&0x40)?0x80:0x00; + retval|=(mTIM_6_ENABLE_RELOAD)?0x10:0x00; + retval|=(mTIM_6_ENABLE_COUNT)?0x08:0x00; + retval|=mTIM_6_LINKING; + TRACE_MIKIE2("Peek(TIM6CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM7CTLA&0xff): + retval|=(mTimerInterruptMask&0x80)?0x80:0x00; + retval|=(mTIM_7_ENABLE_RELOAD)?0x10:0x00; + retval|=(mTIM_7_ENABLE_COUNT)?0x08:0x00; + retval|=mTIM_7_LINKING; + TRACE_MIKIE2("Peek(TIM7CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM0CNT&0xff): + Update(); + TRACE_MIKIE2("Peek(TIM0CNT ,%02x) at PC=%04x",mTIM_0_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_0_CURRENT; + case (TIM1CNT&0xff): + Update(); + TRACE_MIKIE2("Peek(TIM1CNT ,%02x) at PC=%04x",mTIM_1_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_1_CURRENT; + case (TIM2CNT&0xff): + Update(); + TRACE_MIKIE2("Peek(TIM2CNT ,%02x) at PC=%04x",mTIM_2_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_2_CURRENT; + case (TIM3CNT&0xff): + Update(); + TRACE_MIKIE2("Peek(TIM3CNT ,%02x) at PC=%04x",mTIM_3_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_3_CURRENT; + case (TIM4CNT&0xff): + Update(); + TRACE_MIKIE2("Peek(TIM4CNT ,%02x) at PC=%04x",mTIM_4_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_4_CURRENT; + case (TIM5CNT&0xff): + Update(); + TRACE_MIKIE2("Peek(TIM5CNT ,%02x) at PC=%04x",mTIM_5_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_5_CURRENT; + case (TIM6CNT&0xff): + Update(); + TRACE_MIKIE2("Peek(TIM6CNT ,%02x) at PC=%04x",mTIM_6_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_6_CURRENT; + case (TIM7CNT&0xff): + Update(); + TRACE_MIKIE2("Peek(TIM7CNT ,%02x) at PC=%04x",mTIM_7_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mTIM_7_CURRENT; + + case (TIM0CTLB&0xff): + retval|=(mTIM_0_TIMER_DONE)?0x08:0x00; + retval|=(mTIM_0_LAST_CLOCK)?0x04:0x00; + retval|=(mTIM_0_BORROW_IN)?0x02:0x00; + retval|=(mTIM_0_BORROW_OUT)?0x01:0x00; + TRACE_MIKIE2("Peek(TIM0CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM1CTLB&0xff): + retval|=(mTIM_1_TIMER_DONE)?0x08:0x00; + retval|=(mTIM_1_LAST_CLOCK)?0x04:0x00; + retval|=(mTIM_1_BORROW_IN)?0x02:0x00; + retval|=(mTIM_1_BORROW_OUT)?0x01:0x00; + TRACE_MIKIE2("Peek(TIM1CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM2CTLB&0xff): + retval|=(mTIM_2_TIMER_DONE)?0x08:0x00; + retval|=(mTIM_2_LAST_CLOCK)?0x04:0x00; + retval|=(mTIM_2_BORROW_IN)?0x02:0x00; + retval|=(mTIM_2_BORROW_OUT)?0x01:0x00; + TRACE_MIKIE2("Peek(TIM2CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM3CTLB&0xff): + retval|=(mTIM_3_TIMER_DONE)?0x08:0x00; + retval|=(mTIM_3_LAST_CLOCK)?0x04:0x00; + retval|=(mTIM_3_BORROW_IN)?0x02:0x00; + retval|=(mTIM_3_BORROW_OUT)?0x01:0x00; + TRACE_MIKIE2("Peek(TIM3CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM4CTLB&0xff): + retval|=(mTIM_4_TIMER_DONE)?0x08:0x00; + retval|=(mTIM_4_LAST_CLOCK)?0x04:0x00; + retval|=(mTIM_4_BORROW_IN)?0x02:0x00; + retval|=(mTIM_4_BORROW_OUT)?0x01:0x00; + TRACE_MIKIE2("Peek(TIM4CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM5CTLB&0xff): + retval|=(mTIM_5_TIMER_DONE)?0x08:0x00; + retval|=(mTIM_5_LAST_CLOCK)?0x04:0x00; + retval|=(mTIM_5_BORROW_IN)?0x02:0x00; + retval|=(mTIM_5_BORROW_OUT)?0x01:0x00; + TRACE_MIKIE2("Peek(TIM5CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM6CTLB&0xff): + retval|=(mTIM_6_TIMER_DONE)?0x08:0x00; + retval|=(mTIM_6_LAST_CLOCK)?0x04:0x00; + retval|=(mTIM_6_BORROW_IN)?0x02:0x00; + retval|=(mTIM_6_BORROW_OUT)?0x01:0x00; + TRACE_MIKIE2("Peek(TIM6CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (TIM7CTLB&0xff): + retval|=(mTIM_7_TIMER_DONE)?0x08:0x00; + retval|=(mTIM_7_LAST_CLOCK)?0x04:0x00; + retval|=(mTIM_7_BORROW_IN)?0x02:0x00; + retval|=(mTIM_7_BORROW_OUT)?0x01:0x00; + TRACE_MIKIE2("Peek(TIM7CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + // Audio control registers + + case (AUD0VOL&0xff): + TRACE_MIKIE2("Peek(AUD0VOL,%02x) at PC=%04x",(UBYTE)mAUDIO_0_VOLUME,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_0_VOLUME; + case (AUD0SHFTFB&0xff): + TRACE_MIKIE2("Peek(AUD0SHFTFB,%02x) at PC=%04x",(UBYTE)(mAUDIO_0_WAVESHAPER>>13)&0xff,mSystem.mCpu->GetPC()); + return (UBYTE)((mAUDIO_0_WAVESHAPER>>13)&0xff); + case (AUD0OUTVAL&0xff): + TRACE_MIKIE2("Peek(AUD0OUTVAL,%02x) at PC=%04x",(UBYTE)mAUDIO_OUTPUT[0],mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_OUTPUT[0]; + case (AUD0L8SHFT&0xff): + TRACE_MIKIE2("Peek(AUD0L8SHFT,%02x) at PC=%04x",(UBYTE)(mAUDIO_0_WAVESHAPER&0xff),mSystem.mCpu->GetPC()); + return (UBYTE)(mAUDIO_0_WAVESHAPER&0xff); + case (AUD0TBACK&0xff): + TRACE_MIKIE2("Peek(AUD0TBACK,%02x) at PC=%04x",(UBYTE)mAUDIO_0_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_0_BKUP; + case (AUD0CTL&0xff): + retval|=(mAUDIO_0_INTEGRATE_ENABLE)?0x20:0x00; + retval|=(mAUDIO_0_ENABLE_RELOAD)?0x10:0x00; + retval|=(mAUDIO_0_ENABLE_COUNT)?0x08:0x00; + retval|=(mAUDIO_0_WAVESHAPER&0x001000)?0x80:0x00; + retval|=mAUDIO_0_LINKING; + TRACE_MIKIE2("Peek(AUD0CTL,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (AUD0COUNT&0xff): + TRACE_MIKIE2("Peek(AUD0COUNT,%02x) at PC=%04x",(UBYTE)mAUDIO_0_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_0_CURRENT; + case (AUD0MISC&0xff): + retval|=(mAUDIO_0_BORROW_OUT)?0x01:0x00; + retval|=(mAUDIO_0_BORROW_IN)?0x02:0x00; + retval|=(mAUDIO_0_LAST_CLOCK)?0x08:0x00; + retval|=(mAUDIO_0_WAVESHAPER>>4)&0xf0; + TRACE_MIKIE2("Peek(AUD0MISC,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (AUD1VOL&0xff): + TRACE_MIKIE2("Peek(AUD1VOL,%02x) at PC=%04x",(UBYTE)mAUDIO_1_VOLUME,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_1_VOLUME; + case (AUD1SHFTFB&0xff): + TRACE_MIKIE2("Peek(AUD1SHFTFB,%02x) at PC=%04x",(UBYTE)(mAUDIO_1_WAVESHAPER>>13)&0xff,mSystem.mCpu->GetPC()); + return (UBYTE)((mAUDIO_1_WAVESHAPER>>13)&0xff); + case (AUD1OUTVAL&0xff): + TRACE_MIKIE2("Peek(AUD1OUTVAL,%02x) at PC=%04x",(UBYTE)mAUDIO_OUTPUT[1],mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_OUTPUT[1]; + case (AUD1L8SHFT&0xff): + TRACE_MIKIE2("Peek(AUD1L8SHFT,%02x) at PC=%04x",(UBYTE)(mAUDIO_1_WAVESHAPER&0xff),mSystem.mCpu->GetPC()); + return (UBYTE)(mAUDIO_1_WAVESHAPER&0xff); + case (AUD1TBACK&0xff): + TRACE_MIKIE2("Peek(AUD1TBACK,%02x) at PC=%04x",(UBYTE)mAUDIO_1_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_1_BKUP; + case (AUD1CTL&0xff): + retval|=(mAUDIO_1_INTEGRATE_ENABLE)?0x20:0x00; + retval|=(mAUDIO_1_ENABLE_RELOAD)?0x10:0x00; + retval|=(mAUDIO_1_ENABLE_COUNT)?0x08:0x00; + retval|=(mAUDIO_1_WAVESHAPER&0x001000)?0x80:0x00; + retval|=mAUDIO_1_LINKING; + TRACE_MIKIE2("Peek(AUD1CTL,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (AUD1COUNT&0xff): + TRACE_MIKIE2("Peek(AUD1COUNT,%02x) at PC=%04x",(UBYTE)mAUDIO_1_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_1_CURRENT; + case (AUD1MISC&0xff): + retval|=(mAUDIO_1_BORROW_OUT)?0x01:0x00; + retval|=(mAUDIO_1_BORROW_IN)?0x02:0x00; + retval|=(mAUDIO_1_LAST_CLOCK)?0x08:0x00; + retval|=(mAUDIO_1_WAVESHAPER>>4)&0xf0; + TRACE_MIKIE2("Peek(AUD1MISC,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (AUD2VOL&0xff): + TRACE_MIKIE2("Peek(AUD2VOL,%02x) at PC=%04x",(UBYTE)mAUDIO_2_VOLUME,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_2_VOLUME; + case (AUD2SHFTFB&0xff): + TRACE_MIKIE2("Peek(AUD2SHFTFB,%02x) at PC=%04x",(UBYTE)(mAUDIO_2_WAVESHAPER>>13)&0xff,mSystem.mCpu->GetPC()); + return (UBYTE)((mAUDIO_2_WAVESHAPER>>13)&0xff); + case (AUD2OUTVAL&0xff): + TRACE_MIKIE2("Peek(AUD2OUTVAL,%02x) at PC=%04x",(UBYTE)mAUDIO_OUTPUT[2],mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_OUTPUT[2]; + case (AUD2L8SHFT&0xff): + TRACE_MIKIE2("Peek(AUD2L8SHFT,%02x) at PC=%04x",(UBYTE)(mAUDIO_2_WAVESHAPER&0xff),mSystem.mCpu->GetPC()); + return (UBYTE)(mAUDIO_2_WAVESHAPER&0xff); + case (AUD2TBACK&0xff): + TRACE_MIKIE2("Peek(AUD2TBACK,%02x) at PC=%04x",(UBYTE)mAUDIO_2_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_2_BKUP; + case (AUD2CTL&0xff): + retval|=(mAUDIO_2_INTEGRATE_ENABLE)?0x20:0x00; + retval|=(mAUDIO_2_ENABLE_RELOAD)?0x10:0x00; + retval|=(mAUDIO_2_ENABLE_COUNT)?0x08:0x00; + retval|=(mAUDIO_2_WAVESHAPER&0x001000)?0x80:0x00; + retval|=mAUDIO_2_LINKING; + TRACE_MIKIE2("Peek(AUD2CTL,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (AUD2COUNT&0xff): + TRACE_MIKIE2("Peek(AUD2COUNT,%02x) at PC=%04x",(UBYTE)mAUDIO_2_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_2_CURRENT; + case (AUD2MISC&0xff): + retval|=(mAUDIO_2_BORROW_OUT)?0x01:0x00; + retval|=(mAUDIO_2_BORROW_IN)?0x02:0x00; + retval|=(mAUDIO_2_LAST_CLOCK)?0x08:0x00; + retval|=(mAUDIO_2_WAVESHAPER>>4)&0xf0; + TRACE_MIKIE2("Peek(AUD2MISC,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (AUD3VOL&0xff): + TRACE_MIKIE2("Peek(AUD3VOL,%02x) at PC=%04x",(UBYTE)mAUDIO_3_VOLUME,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_3_VOLUME; + case (AUD3SHFTFB&0xff): + TRACE_MIKIE2("Peek(AUD3SHFTFB,%02x) at PC=%04x",(UBYTE)(mAUDIO_3_WAVESHAPER>>13)&0xff,mSystem.mCpu->GetPC()); + return (UBYTE)((mAUDIO_3_WAVESHAPER>>13)&0xff); + case (AUD3OUTVAL&0xff): + TRACE_MIKIE2("Peek(AUD3OUTVAL,%02x) at PC=%04x",(UBYTE)mAUDIO_OUTPUT[3],mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_OUTPUT[3]; + case (AUD3L8SHFT&0xff): + TRACE_MIKIE2("Peek(AUD3L8SHFT,%02x) at PC=%04x",(UBYTE)(mAUDIO_3_WAVESHAPER&0xff),mSystem.mCpu->GetPC()); + return (UBYTE)(mAUDIO_3_WAVESHAPER&0xff); + case (AUD3TBACK&0xff): + TRACE_MIKIE2("Peek(AUD3TBACK,%02x) at PC=%04x",(UBYTE)mAUDIO_3_BKUP,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_3_BKUP; + case (AUD3CTL&0xff): + retval|=(mAUDIO_3_INTEGRATE_ENABLE)?0x20:0x00; + retval|=(mAUDIO_3_ENABLE_RELOAD)?0x10:0x00; + retval|=(mAUDIO_3_ENABLE_COUNT)?0x08:0x00; + retval|=(mAUDIO_3_WAVESHAPER&0x001000)?0x80:0x00; + retval|=mAUDIO_3_LINKING; + TRACE_MIKIE2("Peek(AUD3CTL,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (AUD3COUNT&0xff): + TRACE_MIKIE2("Peek(AUD3COUNT,%02x) at PC=%04x",(UBYTE)mAUDIO_3_CURRENT,mSystem.mCpu->GetPC()); + return (UBYTE)mAUDIO_3_CURRENT; + case (AUD3MISC&0xff): + retval|=(mAUDIO_3_BORROW_OUT)?0x01:0x00; + retval|=(mAUDIO_3_BORROW_IN)?0x02:0x00; + retval|=(mAUDIO_3_LAST_CLOCK)?0x08:0x00; + retval|=(mAUDIO_3_WAVESHAPER>>4)&0xf0; + TRACE_MIKIE2("Peek(AUD3MISC,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (ATTEN_A&0xff): + TRACE_MIKIE1("Peek(ATTEN_A) at PC=%04x",mSystem.mCpu->GetPC()); + return (UBYTE) mAUDIO_ATTEN[0]; + case (ATTEN_B&0xff): + TRACE_MIKIE1("Peek(ATTEN_B) at PC=%04x",mSystem.mCpu->GetPC()); + return (UBYTE) mAUDIO_ATTEN[1]; + case (ATTEN_C&0xff): + TRACE_MIKIE1("Peek(ATTEN_C) at PC=%04x",mSystem.mCpu->GetPC()); + return (UBYTE) mAUDIO_ATTEN[2]; + case (ATTEN_D&0xff): + TRACE_MIKIE1("Peek(ATTEN_D) at PC=%04x",mSystem.mCpu->GetPC()); + return (UBYTE) mAUDIO_ATTEN[3]; + case (MPAN&0xff): + TRACE_MIKIE1("Peek(MPAN) at PC=%04x",mSystem.mCpu->GetPC()); + return (UBYTE) mPAN; + + case (MSTEREO&0xff): + TRACE_MIKIE2("Peek(MSTEREO,%02x) at PC=%04x",(UBYTE)mSTEREO^0xff,mSystem.mCpu->GetPC()); + return (UBYTE) mSTEREO; + + // Miscellaneous registers + + case (SERCTL&0xff): + retval|=(mUART_TX_COUNTDOWN&UART_TX_INACTIVE)?0xA0:0x00; // Indicate TxDone & TxAllDone + retval|=(mUART_RX_READY)?0x40:0x00; // Indicate Rx data ready + retval|=(mUART_Rx_overun_error)?0x08:0x0; // Framing error + retval|=(mUART_Rx_framing_error)?0x04:0x00; // Rx overrun + retval|=(mUART_RX_DATA&UART_BREAK_CODE)?0x02:0x00; // Indicate break received + retval|=(mUART_RX_DATA&0x0100)?0x01:0x00; // Add parity bit + TRACE_MIKIE2("Peek(SERCTL ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return (UBYTE)retval; + + case (SERDAT&0xff): + mUART_RX_READY=0; + TRACE_MIKIE2("Peek(SERDAT ,%02x) at PC=%04x",(UBYTE)mUART_RX_DATA,mSystem.mCpu->GetPC()); + return (UBYTE)(mUART_RX_DATA&0xff); + + case (IODAT&0xff): + // IODIR = output bit : input high (eeprom write done) + if(mSystem.mEEPROM->Available()) { + mSystem.mEEPROM->ProcessEepromBusy(); + retval|=(mIODIR&0x10)?mIODAT&0x10:(mSystem.mEEPROM->OutputBit()?0x10:0x00); + } else { + retval|=mIODAT&0x10; + } + retval|=(mIODIR&0x08)?(((mIODAT&0x08)&&mIODAT_REST_SIGNAL)?0x00:0x08):0x00; // REST = output bit : input low + retval|=(mIODIR&0x04)?mIODAT&0x04:((mUART_CABLE_PRESENT)?0x04:0x00); // NOEXP = output bit : input low + retval|=(mIODIR&0x02)?mIODAT&0x02:0x00; // CARTAD = output bit : input low + retval|=(mIODIR&0x01)?mIODAT&0x01:0x01; // EXTPW = output bit : input high (Power connected) + TRACE_MIKIE2("Peek(IODAT ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC()); + return (UBYTE)retval; + + case (INTRST&0xff): + case (INTSET&0xff): + TRACE_MIKIE2("Peek(INTSET ,%02x) at PC=%04x",mTimerStatusFlags,mSystem.mCpu->GetPC()); + return (UBYTE)mTimerStatusFlags; + case (MAGRDY0&0xff): + case (MAGRDY1&0xff): + TRACE_MIKIE2("Peek(MAGRDY0/1,%02x) at PC=%04x",0x00,mSystem.mCpu->GetPC()); + return 0x00; + case (AUDIN&0xff): + // TRACE_MIKIE2("Peek(AUDIN,%02x) at PC=%04x",mAudioInputComparator?0x80:0x00,mSystem.mCpu->GetPC()); + // if(mAudioInputComparator) return 0x80; else return 0x00; + TRACE_MIKIE2("Peek(AUDIN,%02x) at PC=%04x",0x80,mSystem.mCpu->GetPC()); + return 0x80; + case (MIKEYHREV&0xff): + TRACE_MIKIE2("Peek(MIKEYHREV,%02x) at PC=%04x",0x01,mSystem.mCpu->GetPC()); + return 0x01; + + // Pallette registers + + case (GREEN0&0xff): + case (GREEN1&0xff): + case (GREEN2&0xff): + case (GREEN3&0xff): + case (GREEN4&0xff): + case (GREEN5&0xff): + case (GREEN6&0xff): + case (GREEN7&0xff): + case (GREEN8&0xff): + case (GREEN9&0xff): + case (GREENA&0xff): + case (GREENB&0xff): + case (GREENC&0xff): + case (GREEND&0xff): + case (GREENE&0xff): + case (GREENF&0xff): + TRACE_MIKIE2("Peek(GREENPAL0-F,%02x) at PC=%04x",mPalette[addr&0x0f].Colours.Green,mSystem.mCpu->GetPC()); + return mPalette[addr&0x0f].Colours.Green; + case (BLUERED0&0xff): + case (BLUERED1&0xff): + case (BLUERED2&0xff): + case (BLUERED3&0xff): + case (BLUERED4&0xff): + case (BLUERED5&0xff): + case (BLUERED6&0xff): + case (BLUERED7&0xff): + case (BLUERED8&0xff): + case (BLUERED9&0xff): + case (BLUEREDA&0xff): + case (BLUEREDB&0xff): + case (BLUEREDC&0xff): + case (BLUEREDD&0xff): + case (BLUEREDE&0xff): + case (BLUEREDF&0xff): + TRACE_MIKIE2("Peek(BLUEREDPAL0-F,%02x) at PC=%04x",(mPalette[addr&0x0f].Colours.Red | (mPalette[addr&0x0f].Colours.Blue<<4)),mSystem.mCpu->GetPC()); + return (mPalette[addr&0x0f].Colours.Red | (mPalette[addr&0x0f].Colours.Blue<<4)); + + // Errors on write only register accesses + // For easier debugging + + case (DISPADRL&0xff): + TRACE_MIKIE2("Peek(DISPADRL,%02x) at PC=%04x",(UBYTE)(mDisplayAddress&0xff),mSystem.mCpu->GetPC()); + return (UBYTE)(mDisplayAddress&0xff); + case (DISPADRH&0xff): + TRACE_MIKIE2("Peek(DISPADRH,%02x) at PC=%04x",(UBYTE)(mDisplayAddress>>8)&0xff,mSystem.mCpu->GetPC()); + return (UBYTE)(mDisplayAddress>>8)&0xff; + + case (DISPCTL&0xff): + case (SYSCTL1&0xff): + case (MIKEYSREV&0xff): + case (IODIR&0xff): + case (SDONEACK&0xff): + case (CPUSLEEP&0xff): + case (PBKUP&0xff): + case (Mtest0&0xff): + case (Mtest1&0xff): + case (Mtest2&0xff): + TRACE_MIKIE2("Peek(%04x) - Peek from write only register location at PC=$%04x",addr,mSystem.mCpu->GetPC()); + break; + + // Register to let programs know handy is running + + case (0xfd97&0xff): + TRACE_MIKIE2("Peek(%04x) - **** HANDY DETECT ATTEMPTED **** at PC=$%04x",addr,mSystem.mCpu->GetPC()); + break; + //return 0x42; + // Errors on illegal location accesses + + default: + TRACE_MIKIE2("Peek(%04x) - Peek from illegal location at PC=$%04x",addr,mSystem.mCpu->GetPC()); + break; + } + return 0xff; +} + +inline void CMikie::Update(void) +{ + SLONG divide = 0; + SLONG decval = 0; + ULONG tmp = 0; + ULONG mikie_work_done=0; + + // + // To stop problems with cycle count wrap we will check and then correct the + // cycle counter. + // + + // TRACE_MIKIE0("Update()"); + + if(gSystemCycleCount>0xf0000000) { + gSystemCycleCount-=0x80000000; + gThrottleNextCycleCheckpoint-=0x80000000; + gAudioLastUpdateCycle-=0x80000000; + mTIM_0_LAST_COUNT-=0x80000000; + mTIM_1_LAST_COUNT-=0x80000000; + mTIM_2_LAST_COUNT-=0x80000000; + mTIM_3_LAST_COUNT-=0x80000000; + mTIM_4_LAST_COUNT-=0x80000000; + mTIM_5_LAST_COUNT-=0x80000000; + mTIM_6_LAST_COUNT-=0x80000000; + mTIM_7_LAST_COUNT-=0x80000000; + mAUDIO_0_LAST_COUNT-=0x80000000; + mAUDIO_1_LAST_COUNT-=0x80000000; + mAUDIO_2_LAST_COUNT-=0x80000000; + mAUDIO_3_LAST_COUNT-=0x80000000; + // Only correct if sleep is active + if(gCPUWakeupTime) { + gCPUWakeupTime-=0x80000000; + gIRQEntryCycle-=0x80000000; + } + } + + gNextTimerEvent=0xffffffff; + + // + // Check if the CPU needs to be woken up from sleep mode + // + if(gCPUWakeupTime) { + if(gSystemCycleCount>=gCPUWakeupTime) { + TRACE_MIKIE0("*********************************************************"); + TRACE_MIKIE0("**** CPU SLEEP COMPLETED ****"); + TRACE_MIKIE0("*********************************************************"); + gSystemCPUSleep=FALSE; + gSystemCPUSleep_Saved=FALSE; + gCPUWakeupTime=0; + } else { + if(gCPUWakeupTime>gSystemCycleCount) gNextTimerEvent=gCPUWakeupTime; + } + } + + // Timer updates, rolled out flat in group order + // + // Group A: + // Timer 0 -> Timer 2 -> Timer 4. + // + // Group B: + // Timer 1 -> Timer 3 -> Timer 5 -> Timer 7 -> Audio 0 -> Audio 1-> Audio 2 -> Audio 3 -> Timer 1. + // + + // + // Within each timer code block we will predict the cycle count number of + // the next timer event + // + // We don't need to count linked timers as the timer they are linked + // from will always generate earlier events. + // + // As Timer 4 (UART) will generate many events we will ignore it + // + // We set the next event to the end of time at first and let the timers + // overload it. Any writes to timer controls will force next event to + // be immediate and hence a new prediction will be done. The prediction + // causes overflow as opposed to zero i.e. current+1 + // (In reality T0 line counter should always be running.) + // + + + // + // Timer 0 of Group A + // + + // + // Optimisation, assume T0 (Line timer) is never in one-shot, + // never placed in link mode + // + + // KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..) + // if(mTIM_0_ENABLE_COUNT && (mTIM_0_ENABLE_RELOAD || !mTIM_0_TIMER_DONE)) + if(mTIM_0_ENABLE_COUNT) { + // Timer 0 has no linking + // if(mTIM_0_LINKING!=0x07) + { + // Ordinary clocked mode as opposed to linked mode + // 16MHz clock down to 1us == cyclecount >> 4 + divide=(4+mTIM_0_LINKING); + decval=(gSystemCycleCount-mTIM_0_LAST_COUNT)>>divide; + + if(decval) { + mTIM_0_LAST_COUNT+=decval<2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mTIM_0_CURRENT&0x80000000)?1:((mTIM_0_CURRENT+1)<> 4 + // divide=(4+mTIM_2_LINKING); + // decval=(gSystemCycleCount-mTIM_2_LAST_COUNT)>>divide; + // } + + if(decval) { + // mTIM_2_LAST_COUNT+=decval<> 4 + // Additional /8 (+3) for 8 clocks per bit transmit + divide=4+3+mTIM_4_LINKING; + decval=(gSystemCycleCount-mTIM_4_LAST_COUNT)>>divide; + } + + if(decval) { + mTIM_4_LAST_COUNT+=decval<0) { + mUART_RX_DATA = mUART_Rx_input_queue[mUART_Rx_output_ptr++]; + mUART_Rx_output_ptr %= UART_MAX_RX_QUEUE; + mUART_Rx_waiting--; + TRACE_MIKIE2("Update() - RX Byte output ptr=%02d waiting=%02d",mUART_Rx_output_ptr,mUART_Rx_waiting); + } else { + TRACE_MIKIE0("Update() - RX Byte but no data waiting ????"); + } + + // Retrigger input if more bytes waiting + if(mUART_Rx_waiting>0) { + mUART_RX_COUNTDOWN=UART_RX_TIME_PERIOD+UART_RX_NEXT_DELAY; + TRACE_MIKIE1("Update() - RX Byte retriggered, %d waiting",mUART_Rx_waiting); + } else { + mUART_RX_COUNTDOWN=UART_RX_INACTIVE; + TRACE_MIKIE0("Update() - RX Byte nothing waiting, deactivated"); + } + + // If RX_READY already set then we have an overrun + // as previous byte hasn't been read + if(mUART_RX_READY) mUART_Rx_overun_error=1; + + // Flag byte as being recvd + mUART_RX_READY=1; + } else if(!(mUART_RX_COUNTDOWN&UART_RX_INACTIVE)) { + mUART_RX_COUNTDOWN--; + } + + if(!mUART_TX_COUNTDOWN) { + if(mUART_SENDBREAK) { + mUART_TX_DATA=UART_BREAK_CODE; + // Auto-Respawn new transmit + mUART_TX_COUNTDOWN=UART_TX_TIME_PERIOD; + // Loop back what we transmitted + ComLynxTxLoopback(mUART_TX_DATA); + } else { + // Serial activity finished + mUART_TX_COUNTDOWN=UART_TX_INACTIVE; + } + + // If a networking object is attached then use its callback to send the data byte. + if(mpUART_TX_CALLBACK) { + TRACE_MIKIE0("Update() - UART_TX_CALLBACK"); + (*mpUART_TX_CALLBACK)(mUART_TX_DATA,mUART_TX_CALLBACK_OBJECT); + } + + } else if(!(mUART_TX_COUNTDOWN&UART_TX_INACTIVE)) { + mUART_TX_COUNTDOWN--; + } + + // Set the timer status flag + // Timer 4 is the uart timer and doesn't generate IRQ's using this method + + // 16 Clocks = 1 bit transmission. Hold separate Rx & Tx counters + + // Reload if necessary + // if(mTIM_4_ENABLE_RELOAD) + // { + mTIM_4_CURRENT+=mTIM_4_BKUP+1; + // The low reload values on TIM4 coupled with a longer + // timer service delay can sometimes cause + // an underrun, check and fix + if(mTIM_4_CURRENT&0x80000000) { + mTIM_4_CURRENT=mTIM_4_BKUP; + mTIM_4_LAST_COUNT=gSystemCycleCount; + } + // } + // else + // { + // mTIM_4_CURRENT=0; + // } + // mTIM_4_TIMER_DONE=TRUE; + } + // else + // { + // mTIM_4_BORROW_OUT=FALSE; + // } + // // Set carry in as we did a count + // mTIM_4_BORROW_IN=TRUE; + } + // else + // { + // // Clear carry in as we didn't count + // mTIM_4_BORROW_IN=FALSE; + // // Clear carry out + // mTIM_4_BORROW_OUT=FALSE; + // } + // + // // Prediction for next timer event cycle number + // + // if(mTIM_4_LINKING!=7) + // { + // Sometimes timeupdates can be >2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mTIM_4_CURRENT&0x80000000)?1:((mTIM_4_CURRENT+1)<> 4 + divide=(4+mTIM_1_LINKING); + decval=(gSystemCycleCount-mTIM_1_LAST_COUNT)>>divide; + + if(decval) { + mTIM_1_LAST_COUNT+=decval<2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mTIM_1_CURRENT&0x80000000)?1:((mTIM_1_CURRENT+1)<> 4 + divide=(4+mTIM_3_LINKING); + decval=(gSystemCycleCount-mTIM_3_LAST_COUNT)>>divide; + } + + if(decval) { + mTIM_3_LAST_COUNT+=decval<2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mTIM_3_CURRENT&0x80000000)?1:((mTIM_3_CURRENT+1)<> 4 + divide=(4+mTIM_5_LINKING); + decval=(gSystemCycleCount-mTIM_5_LAST_COUNT)>>divide; + } + + if(decval) { + mTIM_5_LAST_COUNT+=decval<2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mTIM_5_CURRENT&0x80000000)?1:((mTIM_5_CURRENT+1)<> 4 + divide=(4+mTIM_7_LINKING); + decval=(gSystemCycleCount-mTIM_7_LAST_COUNT)>>divide; + } + + if(decval) { + mTIM_7_LAST_COUNT+=decval<2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mTIM_7_CURRENT&0x80000000)?1:((mTIM_7_CURRENT+1)<> 4 + divide=(4+mTIM_6_LINKING); + decval=(gSystemCycleCount-mTIM_6_LAST_COUNT)>>divide; + + if(decval) { + mTIM_6_LAST_COUNT+=decval<2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mTIM_6_CURRENT&0x80000000)?1:((mTIM_6_CURRENT+1)<> 4 + divide=(4+mAUDIO_0_LINKING); + decval=(gSystemCycleCount-mAUDIO_0_LAST_COUNT)>>divide; + } + + if(decval) { + mAUDIO_0_LAST_COUNT+=decval<127) temp=127; + if(temp<-128) temp=-128; + mAUDIO_OUTPUT[0]=(SBYTE)temp; + } else { + if(mAUDIO_0_WAVESHAPER&0x0001) mAUDIO_OUTPUT[0]=mAUDIO_0_VOLUME; + else mAUDIO_OUTPUT[0]=-mAUDIO_0_VOLUME; + } + } else { + mAUDIO_0_BORROW_OUT=FALSE; + } + // Set carry in as we did a count + mAUDIO_0_BORROW_IN=TRUE; + } else { + // Clear carry in as we didn't count + mAUDIO_0_BORROW_IN=FALSE; + // Clear carry out + mAUDIO_0_BORROW_OUT=FALSE; + } + + // Prediction for next timer event cycle number + + if(mAUDIO_0_LINKING!=7) { + // Sometimes timeupdates can be >2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mAUDIO_0_CURRENT&0x80000000)?1:((mAUDIO_0_CURRENT+1)<> 4 + divide=(4+mAUDIO_1_LINKING); + decval=(gSystemCycleCount-mAUDIO_1_LAST_COUNT)>>divide; + } + + if(decval) { + mAUDIO_1_LAST_COUNT+=decval<127) temp=127; + if(temp<-128) temp=-128; + mAUDIO_OUTPUT[1]=(SBYTE)temp; + } else { + if(mAUDIO_1_WAVESHAPER&0x0001) mAUDIO_OUTPUT[1]=mAUDIO_1_VOLUME; + else mAUDIO_OUTPUT[1]=-mAUDIO_1_VOLUME; + } + } else { + mAUDIO_1_BORROW_OUT=FALSE; + } + // Set carry in as we did a count + mAUDIO_1_BORROW_IN=TRUE; + } else { + // Clear carry in as we didn't count + mAUDIO_1_BORROW_IN=FALSE; + // Clear carry out + mAUDIO_1_BORROW_OUT=FALSE; + } + + // Prediction for next timer event cycle number + + if(mAUDIO_1_LINKING!=7) { + // Sometimes timeupdates can be >2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mAUDIO_1_CURRENT&0x80000000)?1:((mAUDIO_1_CURRENT+1)<> 4 + divide=(4+mAUDIO_2_LINKING); + decval=(gSystemCycleCount-mAUDIO_2_LAST_COUNT)>>divide; + } + + if(decval) { + mAUDIO_2_LAST_COUNT+=decval<127) temp=127; + if(temp<-128) temp=-128; + mAUDIO_OUTPUT[2]=(SBYTE)temp; + } else { + if(mAUDIO_2_WAVESHAPER&0x0001) mAUDIO_OUTPUT[2]=mAUDIO_2_VOLUME; + else mAUDIO_OUTPUT[2]=-mAUDIO_2_VOLUME; + } + } else { + mAUDIO_2_BORROW_OUT=FALSE; + } + // Set carry in as we did a count + mAUDIO_2_BORROW_IN=TRUE; + } else { + // Clear carry in as we didn't count + mAUDIO_2_BORROW_IN=FALSE; + // Clear carry out + mAUDIO_2_BORROW_OUT=FALSE; + } + + // Prediction for next timer event cycle number + + if(mAUDIO_2_LINKING!=7) { + // Sometimes timeupdates can be >2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mAUDIO_2_CURRENT&0x80000000)?1:((mAUDIO_2_CURRENT+1)<> 4 + divide=(4+mAUDIO_3_LINKING); + decval=(gSystemCycleCount-mAUDIO_3_LAST_COUNT)>>divide; + } + + if(decval) { + mAUDIO_3_LAST_COUNT+=decval<127) temp=127; + if(temp<-128) temp=-128; + mAUDIO_OUTPUT[3]=(SBYTE)temp; + } else { + if(mAUDIO_3_WAVESHAPER&0x0001) mAUDIO_OUTPUT[3]=mAUDIO_3_VOLUME; + else mAUDIO_OUTPUT[3]=-mAUDIO_3_VOLUME; + } + } else { + mAUDIO_3_BORROW_OUT=FALSE; + } + // Set carry in as we did a count + mAUDIO_3_BORROW_IN=TRUE; + } else { + // Clear carry in as we didn't count + mAUDIO_3_BORROW_IN=FALSE; + // Clear carry out + mAUDIO_3_BORROW_OUT=FALSE; + } + + // Prediction for next timer event cycle number + + if(mAUDIO_3_LINKING!=7) { + // Sometimes timeupdates can be >2x rollover in which case + // then CURRENT may still be negative and we can use it to + // calc the next timer value, we just want another update ASAP + tmp=(mAUDIO_3_CURRENT&0x80000000)?1:((mAUDIO_3_CURRENT+1)< 0; --samples) + { + gAudioBuffer[gAudioBufferPointer++] = (sample_l<<16)+sample_r; + //gAudioBuffer[gAudioBufferPointer++] = sample_r; + gAudioBufferPointer &= (AUDIO_BUFFER_LENGTH-1); + gAudioLastUpdateCycle += HANDY_AUDIO_SAMPLE_PERIOD; + } +} diff --git a/MCUME_teensy41/teensyhandy/mikie.h b/MCUME_teensy41/teensyhandy/mikie.h new file mode 100644 index 0000000..fb87cf1 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/mikie.h @@ -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 diff --git a/MCUME_teensy41/teensyhandy/platform_config.h b/MCUME_teensy41/teensyhandy/platform_config.h new file mode 100644 index 0000000..3464dc2 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/platform_config.h @@ -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 diff --git a/MCUME_teensy41/teensyhandy/ram.h b/MCUME_teensy41/teensyhandy/ram.h new file mode 100644 index 0000000..affdef9 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/ram.h @@ -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 diff --git a/MCUME_teensy41/teensyhandy/susie.cpp b/MCUME_teensy41/teensyhandy/susie.cpp new file mode 100644 index 0000000..a7f9935 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/susie.cpp @@ -0,0 +1,1866 @@ +// +// 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 // +////////////////////////////////////////////////////////////////////////////// +// Suzy emulation class // +////////////////////////////////////////////////////////////////////////////// +// // +// This class emulates the Suzy chip within the lynx. This provides math // +// and sprite painting facilities. SpritePaint() is called from within // +// the Mikey POKE functions when SPRGO is set and is called via the system // +// object to keep the interface clean. // +// // +// K. Wilkins // +// August 1997 // +// // +////////////////////////////////////////////////////////////////////////////// +// Revision History: // +// ----------------- // +// // +// 01Aug1997 KW Document header added & class documented. // +// // +////////////////////////////////////////////////////////////////////////////// + +//#define TRACE_SUSIE + +#include +#include +#include +#include "system.h" +#include "susie.h" +#include "lynxdef.h" + +CSusie::CSusie(CSystem& parent) + :mSystem(parent) +{ + TRACE_SUSIE0("CSusie()"); + Reset(); +} + +CSusie::~CSusie() +{ + TRACE_SUSIE0("~CSusie()"); +} + +void CSusie::Reset(void) +{ + TRACE_SUSIE0("Reset()"); + + // Fetch pointer to system RAM, faster than object access + // and seeing as Susie only ever sees RAM. + + mRamPointer=mSystem.GetRamPointer(); + + // Reset ALL variables + + mTMPADR.Word=0; + mTILTACUM.Word=0; + mHOFF.Word=0; + mVOFF.Word=0; + mVIDBAS.Word=0; + mCOLLBAS.Word=0; + mVIDADR.Word=0; + mCOLLADR.Word=0; + mSCBNEXT.Word=0; + mSPRDLINE.Word=0; + mHPOSSTRT.Word=0; + mVPOSSTRT.Word=0; + mSPRHSIZ.Word=0; + mSPRVSIZ.Word=0; + mSTRETCH.Word=0; + mTILT.Word=0; + mSPRDOFF.Word=0; + mSPRVPOS.Word=0; + mCOLLOFF.Word=0; + mVSIZACUM.Word=0; + mHSIZACUM.Word=0; + mHSIZOFF.Word=0x007f; + mVSIZOFF.Word=0x007f; + mSCBADR.Word=0; + mPROCADR.Word=0; + + // Must be initialised to this due to + // stun runner math initialisation bug + // see whatsnew for 0.7 + mMATHABCD.Long=0xffffffff; + mMATHEFGH.Long=0xffffffff; + mMATHJKLM.Long=0xffffffff; + mMATHNP.Long=0xffff; + + mMATHAB_sign=1; + mMATHCD_sign=1; + mMATHEFGH_sign=1; + + mSPRCTL0_Type=0; + mSPRCTL0_Vflip=0; + mSPRCTL0_Hflip=0; + mSPRCTL0_PixelBits=0; + + mSPRCTL1_StartLeft=0; + mSPRCTL1_StartUp=0; + mSPRCTL1_SkipSprite=0; + mSPRCTL1_ReloadPalette=0; + mSPRCTL1_ReloadDepth=0; + mSPRCTL1_Sizing=0; + mSPRCTL1_Literal=0; + + mSPRCOLL_Number=0; + mSPRCOLL_Collide=0; + + mSPRSYS_StopOnCurrent=0; + mSPRSYS_LeftHand=0; + mSPRSYS_VStretch=0; + mSPRSYS_NoCollide=0; + mSPRSYS_Accumulate=0; + mSPRSYS_SignedMath=0; + mSPRSYS_Status=0; + mSPRSYS_UnsafeAccess=0; + mSPRSYS_LastCarry=0; + mSPRSYS_Mathbit=0; + mSPRSYS_MathInProgress=0; + + mSUZYBUSEN=FALSE; + + mSPRINIT.Byte=0; + + mSPRGO=FALSE; + mEVERON=FALSE; + + for(int loop=0;loop<16;loop++) mPenIndex[loop]=loop; + + mJOYSTICK.Byte=0; + mSWITCHES.Byte=0; +} + +bool CSusie::ContextSave(LSS_FILE *fp) +{ + TRACE_SUSIE0("ContextSave()"); + + if(!lss_printf(fp,"CSusie::ContextSave")) return 0; + + if(!lss_write(&mTMPADR,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mTILTACUM,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mHOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mVOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mVIDBAS,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mCOLLBAS,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mVIDADR,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mCOLLADR,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mSCBNEXT,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mSPRDLINE,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mHPOSSTRT,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mVPOSSTRT,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mSPRHSIZ,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mSPRVSIZ,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mSTRETCH,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mTILT,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mSPRDOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mSPRVPOS,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mCOLLOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mVSIZACUM,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mHSIZACUM,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mHSIZOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mVSIZOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mSCBADR,sizeof(UUWORD),1,fp)) return 0; + if(!lss_write(&mPROCADR,sizeof(UUWORD),1,fp)) return 0; + + if(!lss_write(&mMATHABCD,sizeof(TMATHABCD),1,fp)) return 0; + if(!lss_write(&mMATHEFGH,sizeof(TMATHEFGH),1,fp)) return 0; + if(!lss_write(&mMATHJKLM,sizeof(TMATHJKLM),1,fp)) return 0; + if(!lss_write(&mMATHNP,sizeof(TMATHNP),1,fp)) return 0; + + if(!lss_write(&mSPRCTL0_Type,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL0_Vflip,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL0_Hflip,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL0_PixelBits,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mSPRCTL1_StartLeft,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL1_StartUp,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL1_SkipSprite,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL1_ReloadPalette,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL1_ReloadDepth,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL1_Sizing,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCTL1_Literal,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mSPRCOLL_Number,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRCOLL_Collide,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mSPRSYS_StopOnCurrent,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_LeftHand,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_VStretch,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_NoCollide,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_Accumulate,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_SignedMath,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_Status,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_UnsafeAccess,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_LastCarry,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_Mathbit,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mSPRSYS_MathInProgress,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mSUZYBUSEN,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mSPRINIT,sizeof(TSPRINIT),1,fp)) return 0; + + if(!lss_write(&mSPRGO,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mEVERON,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(mPenIndex,sizeof(UBYTE),16,fp)) return 0; + + if(!lss_write(&mLineType,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mLineShiftRegCount,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mLineShiftReg,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mLineRepeatCount,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mLinePixel,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mLinePacketBitsLeft,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mCollision,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mLineBaseAddress,sizeof(ULONG),1,fp)) return 0; + if(!lss_write(&mLineCollisionAddress,sizeof(ULONG),1,fp)) return 0; + + if(!lss_write(&mJOYSTICK,sizeof(TJOYSTICK),1,fp)) return 0; + if(!lss_write(&mSWITCHES,sizeof(TSWITCHES),1,fp)) return 0; + + return 1; +} + +bool CSusie::ContextLoad(LSS_FILE *fp) +{ + TRACE_SUSIE0("ContextLoad()"); + + char teststr[32]="XXXXXXXXXXXXXXXXXXX"; + if(!lss_read(teststr,sizeof(char),19,fp)) return 0; + if(strcmp(teststr,"CSusie::ContextSave")!=0) return 0; + + if(!lss_read(&mTMPADR,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mTILTACUM,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mHOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mVOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mVIDBAS,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mCOLLBAS,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mVIDADR,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mCOLLADR,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mSCBNEXT,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mSPRDLINE,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mHPOSSTRT,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mVPOSSTRT,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mSPRHSIZ,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mSPRVSIZ,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mSTRETCH,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mTILT,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mSPRDOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mSPRVPOS,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mCOLLOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mVSIZACUM,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mHSIZACUM,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mHSIZOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mVSIZOFF,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mSCBADR,sizeof(UUWORD),1,fp)) return 0; + if(!lss_read(&mPROCADR,sizeof(UUWORD),1,fp)) return 0; + + if(!lss_read(&mMATHABCD,sizeof(TMATHABCD),1,fp)) return 0; + if(!lss_read(&mMATHEFGH,sizeof(TMATHEFGH),1,fp)) return 0; + if(!lss_read(&mMATHJKLM,sizeof(TMATHJKLM),1,fp)) return 0; + if(!lss_read(&mMATHNP,sizeof(TMATHNP),1,fp)) return 0; + + if(!lss_read(&mSPRCTL0_Type,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL0_Vflip,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL0_Hflip,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL0_PixelBits,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mSPRCTL1_StartLeft,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL1_StartUp,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL1_SkipSprite,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL1_ReloadPalette,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL1_ReloadDepth,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL1_Sizing,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCTL1_Literal,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mSPRCOLL_Number,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRCOLL_Collide,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mSPRSYS_StopOnCurrent,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_LeftHand,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_VStretch,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_NoCollide,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_Accumulate,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_SignedMath,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_Status,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_UnsafeAccess,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_LastCarry,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_Mathbit,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mSPRSYS_MathInProgress,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mSUZYBUSEN,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mSPRINIT,sizeof(TSPRINIT),1,fp)) return 0; + + if(!lss_read(&mSPRGO,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mEVERON,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(mPenIndex,sizeof(UBYTE),16,fp)) return 0; + + if(!lss_read(&mLineType,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mLineShiftRegCount,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mLineShiftReg,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mLineRepeatCount,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mLinePixel,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mLinePacketBitsLeft,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mCollision,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mLineBaseAddress,sizeof(ULONG),1,fp)) return 0; + if(!lss_read(&mLineCollisionAddress,sizeof(ULONG),1,fp)) return 0; + + if(!lss_read(&mJOYSTICK,sizeof(TJOYSTICK),1,fp)) return 0; + if(!lss_read(&mSWITCHES,sizeof(TSWITCHES),1,fp)) return 0; + + return 1; +} + +ULONG CSusie::PaintSprites(void) +{ + int sprcount=0; + int data=0; + int everonscreen=0; + + TRACE_SUSIE0(" "); + TRACE_SUSIE0(" "); + TRACE_SUSIE0(" "); + TRACE_SUSIE0("**************************************************************"); + TRACE_SUSIE0("********************** PaintSprites **************************"); + TRACE_SUSIE0("**************************************************************"); + TRACE_SUSIE0(" "); + + TRACE_SUSIE1("PaintSprites() VIDBAS $%04x",mVIDBAS.Word); + TRACE_SUSIE1("PaintSprites() COLLBAS $%04x",mCOLLBAS.Word); + TRACE_SUSIE1("PaintSprites() SPRSYS $%02x",Peek(SPRSYS)); + + if(!mSUZYBUSEN || !mSPRGO) { + TRACE_SUSIE0("PaintSprites() Returned !mSUZYBUSEN || !mSPRGO"); + return 0; + } + + //ULONG mPenIndex[16]; + mCycles=0; + + while (1) + { + TRACE_SUSIE1("PaintSprites() ************ Rendering Sprite %03d ************",sprcount); + + everonscreen=0;// everon has to be reset for every sprite, thus line was moved inside this loop + + // Step 1 load up the SCB params into Susie + + // And thus it is documented that only the top byte of SCBNEXT is used. + // Its mentioned under the bits that are broke section in the bluebook + if(!(mSCBNEXT.Word&0xff00)) { + TRACE_SUSIE0("PaintSprites() mSCBNEXT==0 - FINISHED"); + mSPRSYS_Status=0; // Engine has finished + mSPRGO=FALSE; + break; + } else { + mSPRSYS_Status=1; + } + + mTMPADR.Word=mSCBNEXT.Word; // Copy SCB pointer + mSCBADR.Word=mSCBNEXT.Word; // Copy SCB pointer + TRACE_SUSIE1("PaintSprites() SCBADDR $%04x",mSCBADR.Word); + + data=RAM_PEEK(mTMPADR.Word); // Fetch control 0 + TRACE_SUSIE1("PaintSprites() SPRCTL0 $%02x",data); + mSPRCTL0_Type=data&0x0007; + mSPRCTL0_Vflip=data&0x0010; + mSPRCTL0_Hflip=data&0x0020; + mSPRCTL0_PixelBits=((data&0x00c0)>>6)+1; + mTMPADR.Word+=1; + + data=RAM_PEEK(mTMPADR.Word); // Fetch control 1 + TRACE_SUSIE1("PaintSprites() SPRCTL1 $%02x",data); + mSPRCTL1_StartLeft=data&0x0001; + mSPRCTL1_StartUp=data&0x0002; + mSPRCTL1_SkipSprite=data&0x0004; + mSPRCTL1_ReloadPalette=data&0x0008; + mSPRCTL1_ReloadDepth=(data&0x0030)>>4; + mSPRCTL1_Sizing=data&0x0040; + mSPRCTL1_Literal=data&0x0080; + mTMPADR.Word+=1; + + data=RAM_PEEK(mTMPADR.Word); // Collision num + TRACE_SUSIE1("PaintSprites() SPRCOLL $%02x",data); + mSPRCOLL_Number=data&0x000f; + mSPRCOLL_Collide=data&0x0020; + mTMPADR.Word+=1; + + mSCBNEXT.Word=RAM_PEEKW(mTMPADR.Word); // Next SCB + TRACE_SUSIE1("PaintSprites() SCBNEXT $%04x",mSCBNEXT.Word); + mTMPADR.Word+=2; + + mCycles+=5*SPR_RDWR_CYC; + + // Initialise the collision depositary + + // Although Tom Schenck says this is correct, it doesnt appear to be + // if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide) + // { + // mCollision=RAM_PEEK((mSCBADR.Word+mCOLLOFF.Word)&0xffff); + // mCollision&=0x0f; + // } + mCollision=0; + + // Check if this is a skip sprite + + if(!mSPRCTL1_SkipSprite) { + mSPRDLINE.Word=RAM_PEEKW(mTMPADR.Word); // Sprite pack data + TRACE_SUSIE1("PaintSprites() SPRDLINE $%04x",mSPRDLINE.Word); + mTMPADR.Word+=2; + + mHPOSSTRT.Word=RAM_PEEKW(mTMPADR.Word); // Sprite horizontal start position + TRACE_SUSIE1("PaintSprites() HPOSSTRT $%04x",mHPOSSTRT.Word); + mTMPADR.Word+=2; + + mVPOSSTRT.Word=RAM_PEEKW(mTMPADR.Word); // Sprite vertical start position + TRACE_SUSIE1("PaintSprites() VPOSSTRT $%04x",mVPOSSTRT.Word); + mTMPADR.Word+=2; + + mCycles+=6*SPR_RDWR_CYC; + + // bool enable_sizing = FALSE; + bool enable_stretch = FALSE; + bool enable_tilt = FALSE; + + // Optional section defined by reload type in Control 1 + + TRACE_SUSIE1("PaintSprites() mSPRCTL1.Bits.ReloadDepth=%d",mSPRCTL1_ReloadDepth); + switch(mSPRCTL1_ReloadDepth) { + case 1: + TRACE_SUSIE0("PaintSprites() Sizing Enabled"); + // enable_sizing=TRUE; + + mSPRHSIZ.Word=RAM_PEEKW(mTMPADR.Word); // Sprite Horizontal size + mTMPADR.Word+=2; + + mSPRVSIZ.Word=RAM_PEEKW(mTMPADR.Word); // Sprite Verticalal size + mTMPADR.Word+=2; + + mCycles+=4*SPR_RDWR_CYC; + break; + + case 2: + TRACE_SUSIE0("PaintSprites() Sizing Enabled"); + TRACE_SUSIE0("PaintSprites() Stretch Enabled"); + // enable_sizing=TRUE; + enable_stretch=TRUE; + + mSPRHSIZ.Word=RAM_PEEKW(mTMPADR.Word); // Sprite Horizontal size + mTMPADR.Word+=2; + + mSPRVSIZ.Word=RAM_PEEKW(mTMPADR.Word); // Sprite Verticalal size + mTMPADR.Word+=2; + + mSTRETCH.Word=RAM_PEEKW(mTMPADR.Word); // Sprite stretch + mTMPADR.Word+=2; + + mCycles+=6*SPR_RDWR_CYC; + break; + + case 3: + TRACE_SUSIE0("PaintSprites() Sizing Enabled"); + TRACE_SUSIE0("PaintSprites() Stretch Enabled"); + TRACE_SUSIE0("PaintSprites() Tilt Enabled"); + // enable_sizing=TRUE; + enable_stretch=TRUE; + enable_tilt=TRUE; + + mSPRHSIZ.Word=RAM_PEEKW(mTMPADR.Word); // Sprite Horizontal size + mTMPADR.Word+=2; + + mSPRVSIZ.Word=RAM_PEEKW(mTMPADR.Word); // Sprite Verticalal size + mTMPADR.Word+=2; + + mSTRETCH.Word=RAM_PEEKW(mTMPADR.Word); // Sprite stretch + mTMPADR.Word+=2; + + mTILT.Word=RAM_PEEKW(mTMPADR.Word); // Sprite tilt + mTMPADR.Word+=2; + + mCycles+=8*SPR_RDWR_CYC; + break; + + default: + break; + } + + TRACE_SUSIE1("PaintSprites() SPRHSIZ $%04x",mSPRHSIZ.Word); + TRACE_SUSIE1("PaintSprites() SPRVSIZ $%04x",mSPRVSIZ.Word); + TRACE_SUSIE1("PaintSprites() STRETCH $%04x",mSTRETCH.Word); + TRACE_SUSIE1("PaintSprites() TILT $%04x",mTILT.Word); + + + // Optional Palette reload + + if(!mSPRCTL1_ReloadPalette) { + TRACE_SUSIE0("PaintSprites() Palette reloaded"); + for(int loop=0;loop<8;loop++) { + UBYTE data_tmp=RAM_PEEK(mTMPADR.Word++); + mPenIndex[loop*2]=(data_tmp>>4)&0x0f; + mPenIndex[(loop*2)+1]=data_tmp&0x0f; + } + // Increment cycle count for the reads + mCycles+=8*SPR_RDWR_CYC; + } + + // Now we can start painting + + // Quadrant drawing order is: SE,NE,NW,SW + // start quadrant is given by sprite_control1:0 & 1 + + // Setup screen start end variables + + int screen_h_start=(SWORD)mHOFF.Word; + int screen_h_end=(SWORD)mHOFF.Word+HANDY_SCREEN_WIDTH; + int screen_v_start=(SWORD)mVOFF.Word; + int screen_v_end=(SWORD)mVOFF.Word+HANDY_SCREEN_HEIGHT; + + int world_h_mid=screen_h_start+0x8000+(HANDY_SCREEN_WIDTH/2); + int world_v_mid=screen_v_start+0x8000+(HANDY_SCREEN_HEIGHT/2); + + TRACE_SUSIE2("PaintSprites() screen_h_start $%04x screen_h_end $%04x",screen_h_start,screen_h_end); + TRACE_SUSIE2("PaintSprites() screen_v_start $%04x screen_v_end $%04x",screen_v_start,screen_v_end); + TRACE_SUSIE2("PaintSprites() world_h_mid $%04x world_v_mid $%04x",world_h_mid,world_v_mid); + + bool superclip=FALSE; + int quadrant=0; + int hsign,vsign; + + if(mSPRCTL1_StartLeft) { + if(mSPRCTL1_StartUp) quadrant=2; else quadrant=3; + } else { + if(mSPRCTL1_StartUp) quadrant=1; else quadrant=0; + } + TRACE_SUSIE1("PaintSprites() Quadrant=%d",quadrant); + + // Check ref is inside screen area + + if((SWORD)mHPOSSTRT.Word=screen_h_end || + (SWORD)mVPOSSTRT.Word=screen_v_end) superclip=TRUE; + + TRACE_SUSIE1("PaintSprites() Superclip=%d",superclip); + + + // Quadrant mapping is: SE NE NW SW + // 0 1 2 3 + // hsign +1 +1 -1 -1 + // vsign +1 -1 -1 +1 + // + // + // 2 | 1 + // ------- + // 3 | 0 + // + + // Loop for 4 quadrants + + for(int loop=0;loop<4;loop++) { + TRACE_SUSIE1("PaintSprites() -------- Rendering Quadrant %03d --------",quadrant); + + int sprite_v=mVPOSSTRT.Word; + int sprite_h=mHPOSSTRT.Word; + + bool render=FALSE; + + // Set quadrand multipliers + hsign=(quadrant==0 || quadrant==1)?1:-1; + vsign=(quadrant==0 || quadrant==3)?1:-1; + + // Preflip TRACE_SUSIE2("PaintSprites() hsign=%d vsign=%d",hsign,vsign); + + //Use h/v flip to invert v/hsign + + if(mSPRCTL0_Vflip) vsign=-vsign; + if(mSPRCTL0_Hflip) hsign=-hsign; + + TRACE_SUSIE2("PaintSprites() Hflip=%d Vflip=%d",mSPRCTL0_Hflip,mSPRCTL0_Vflip); + TRACE_SUSIE2("PaintSprites() Hsign=%d Vsign=%d",hsign,vsign); + TRACE_SUSIE2("PaintSprites() Hpos =%04x Vpos =%04x",mHPOSSTRT.Word,mVPOSSTRT.Word); + TRACE_SUSIE2("PaintSprites() Hsizoff =%04x Vsizoff =%04x",mHSIZOFF.Word,mVSIZOFF.Word); + + // Two different rendering algorithms used, on-screen & superclip + // when on screen we draw in x until off screen then skip to next + // line, BUT on superclip we draw all the way to the end of any + // given line checking each pixel is on screen. + + if(superclip) { + // Check on the basis of each quad, we only render the quad + // IF the screen is in the quad, relative to the centre of + // the screen which is calculated below. + + // Quadrant mapping is: SE NE NW SW + // 0 1 2 3 + // hsign +1 +1 -1 -1 + // vsign +1 -1 -1 +1 + // + // + // 2 | 1 + // ------- + // 3 | 0 + // + // Quadrant mapping for superclipping must also take into account + // the hflip, vflip bits & negative tilt to be able to work correctly + // + int modquad=quadrant; + static int vquadflip[4]={1,0,3,2}; + static int hquadflip[4]={3,2,1,0}; + + if(mSPRCTL0_Vflip) modquad=vquadflip[modquad]; + if(mSPRCTL0_Hflip) modquad=hquadflip[modquad]; + + // This is causing Eurosoccer to fail!! + // if(enable_tilt && mTILT.Word&0x8000) modquad=hquadflip[modquad]; + + switch(modquad) { + case 3: + if((sprite_h>=screen_h_start || sprite_hworld_v_mid)) render=TRUE; + break; + case 2: + if((sprite_h>=screen_h_start || sprite_h=screen_v_start || sprite_vworld_h_mid) && (sprite_v>=screen_v_start || sprite_vworld_h_mid) && (sprite_vworld_v_mid)) render=TRUE; + break; + } + } else { + render=TRUE; + } + + // Is this quad to be rendered ?? + + TRACE_SUSIE1("PaintSprites() Render status %d",render); + + int pixel_height=0; + int pixel_width=0; + // static int pixel=0; + int hoff=0,voff=0; + // int hloop=0; + int vloop=0; + bool onscreen=0; + static int vquadoff=0; + static int hquadoff=0; + + if(render) { // && gRenderFrame + // Set the vertical position & offset + voff=(SWORD)mVPOSSTRT.Word-screen_v_start; + + // Zero the stretch,tilt & acum values + mTILTACUM.Word=0; + + // Perform the SIZOFF + if(vsign==1) mVSIZACUM.Word=mVSIZOFF.Word; + else mVSIZACUM.Word=0; + + // Take the sign of the first quad (0) as the basic + // sign, all other quads drawing in the other direction + // get offset by 1 pixel in the other direction, this + // fixes the squashed look on the multi-quad sprites. + // if(vsign==-1 && loop>0) voff+=vsign; + if(loop==0) vquadoff=vsign; + if(vsign!=vquadoff) voff+=vsign; + + for(;;) { + // Vertical scaling is done here + mVSIZACUM.Word+=mSPRVSIZ.Word; + pixel_height=mVSIZACUM.Byte.High; + mVSIZACUM.Byte.High=0; + + // Update the next data line pointer and initialise our line + mSPRDOFF.Word=(UWORD)LineInit(0); + + // If 1 == next quad, ==0 end of sprite, anyways its END OF LINE + if(mSPRDOFF.Word==1) { // End of quad + mSPRDLINE.Word+=mSPRDOFF.Word; + break; + } + + if(mSPRDOFF.Word==0) { // End of sprite + loop=4; // Halt the quad loop + break; + } + + // Draw one horizontal line of the sprite + for(vloop=0;vloop=HANDY_SCREEN_HEIGHT) break; + if(vsign==-1 && voff<0) break; + + // Only allow the draw to take place if the line is visible + if(voff>=0 && voff>8); + mTILTACUM.Byte.High=0; + hoff=(int)((SWORD)mHPOSSTRT.Word)-screen_h_start; + + // Zero/Force the horizontal scaling accumulator + if(hsign==1) mHSIZACUM.Word=mHSIZOFF.Word; + else mHSIZACUM.Word=0; + + // Take the sign of the first quad (0) as the basic + // sign, all other quads drawing in the other direction + // get offset by 1 pixel in the other direction, this + // fixes the squashed look on the multi-quad sprites. + // if(hsign==-1 && loop>0) hoff+=hsign; + if(loop==0) hquadoff=hsign; + if(hsign!=hquadoff) hoff+=hsign; + + // Initialise our line + LineInit(voff); + onscreen=FALSE; + + ULONG pixel = mLinePixel; // Much faster + switch(mSPRCTL0_Type) + { + case sprite_background_shadow: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL \ + WritePixel(hoff,pixel); \ + if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide && pixel!=0x0e) \ + { \ + WriteCollision(hoff,mSPRCOLL_Number); \ + } + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile01b + #define LoopContinue LoopContinue01b + #include "susie_pixel_loop.h" + break; + case sprite_background_noncollide: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL WritePixel(hoff,pixel); + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile02b + #define LoopContinue LoopContinue02b + #include "susie_pixel_loop.h" + break; + case sprite_noncollide: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL if(pixel!=0x00) WritePixel(hoff,pixel); + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile03b + #define LoopContinue LoopContinue03b + #include "susie_pixel_loop.h" + break; + case sprite_boundary: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL \ + if(pixel!=0x00 && pixel!=0x0f) \ + { \ + WritePixel(hoff,pixel); \ + } \ + if(pixel!=0x00) \ + { \ + if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide) \ + { \ + ULONG collision=ReadCollision(hoff); \ + if(collision>mCollision) \ + { \ + mCollision=collision; \ + } \ + { \ + WriteCollision(hoff,mSPRCOLL_Number); \ + } \ + } \ + } + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile04b + #define LoopContinue LoopContinue04b + #include "susie_pixel_loop.h" + break; + case sprite_normal: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL \ + if(pixel!=0x00) \ + { \ + WritePixel(hoff,pixel); \ + if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide) \ + { \ + ULONG collision=ReadCollision(hoff); \ + if(collision>mCollision) \ + { \ + mCollision=collision; \ + } \ + { \ + WriteCollision(hoff,mSPRCOLL_Number); \ + } \ + } \ + } + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile05b + #define LoopContinue LoopContinue05b + #include "susie_pixel_loop.h" + break; + case sprite_boundary_shadow: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL \ + if(pixel!=0x00 && pixel!=0x0e && pixel!=0x0f) \ + { \ + WritePixel(hoff,pixel); \ + } \ + if(pixel!=0x00 && pixel!=0x0e) \ + { \ + if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide) \ + { \ + ULONG collision=ReadCollision(hoff); \ + if(collision>mCollision) \ + { \ + mCollision=collision; \ + } \ + { \ + WriteCollision(hoff,mSPRCOLL_Number); \ + } \ + } \ + } + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile06b + #define LoopContinue LoopContinue06b + #include "susie_pixel_loop.h" + break; + case sprite_shadow: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL \ + if(pixel!=0x00) \ + { \ + WritePixel(hoff,pixel); \ + } \ + if(pixel!=0x00 && pixel!=0x0e) \ + { \ + if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide) \ + { \ + ULONG collision=ReadCollision(hoff); \ + if(collision>mCollision) \ + { \ + mCollision=collision; \ + } \ + { \ + WriteCollision(hoff,mSPRCOLL_Number); \ + } \ + } \ + } + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile07b + #define LoopContinue LoopContinue07b + #include "susie_pixel_loop.h" + break; + case sprite_xor_shadow: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL \ + if(pixel!=0x00) \ + { \ + WritePixel(hoff,ReadPixel(hoff)^pixel); \ + } \ + if(pixel!=0x00 && pixel!=0x0e) \ + { \ + if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide && pixel!=0x0e) \ + { \ + ULONG collision=ReadCollision(hoff); \ + if(collision>mCollision) \ + { \ + mCollision=collision; \ + } \ + { \ + WriteCollision(hoff,mSPRCOLL_Number); \ + } \ + } \ + } + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile08b + #define LoopContinue LoopContinue08b + #include "susie_pixel_loop.h" + break; + default: + #undef PROCESS_PIXEL + #define PROCESS_PIXEL + + #undef EndWhile + #undef LoopContinue + #define EndWhile EndWhile09b + #define LoopContinue LoopContinue09b + #include "susie_pixel_loop.h" + break; + } + } + voff+=vsign; + + // For every destination line we can modify SPRHSIZ & SPRVSIZ & TILTACUM + if(enable_stretch) { + mSPRHSIZ.Word+=mSTRETCH.Word; + // if(mSPRSYS_VStretch) mSPRVSIZ.Word+=mSTRETCH.Word; + } + if(enable_tilt) { + // Manipulate the tilt stuff + mTILTACUM.Word+=mTILT.Word; + } + } + // According to the docs this increments per dest line + // but only gets set when the source line is read + if(mSPRSYS_VStretch) mSPRVSIZ.Word+=mSTRETCH.Word*pixel_height; + + // Update the line start for our next run thru the loop + mSPRDLINE.Word+=mSPRDOFF.Word; + } + } else { + // Skip thru data to next quad + for(;;) { + // Read the start of line offset + + mSPRDOFF.Word=(UWORD)LineInit(0); + + // We dont want to process data so mSPRDLINE is useless to us + mSPRDLINE.Word+=mSPRDOFF.Word; + + // If 1 == next quad, ==0 end of sprite, anyways its END OF LINE + + if(mSPRDOFF.Word==1) break; // End of quad + if(mSPRDOFF.Word==0) { // End of sprite + loop=4; // Halt the quad loop + break; + } + + } + } + + // Increment quadrant and mask to 2 bit value (0-3) + quadrant++; + quadrant&=0x03; + } + + // Write the collision depositary if required + + if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide) { + switch(mSPRCTL0_Type) { + case sprite_xor_shadow: + case sprite_boundary: + case sprite_normal: + case sprite_boundary_shadow: + case sprite_shadow: { + UWORD coldep=mSCBADR.Word+mCOLLOFF.Word; + RAM_POKE(coldep,(UBYTE)mCollision); + TRACE_SUSIE2("PaintSprites() COLLOFF=$%04x SCBADR=$%04x",mCOLLOFF.Word,mSCBADR.Word); + TRACE_SUSIE2("PaintSprites() Wrote $%02x to SCB collision depositary at $%04x",(UBYTE)mCollision,coldep); + } + break; + default: + break; + } + } + + if(mEVERON) { + UWORD coldep=mSCBADR.Word+mCOLLOFF.Word; + UBYTE coldat=RAM_PEEK(coldep); + if(!everonscreen) coldat|=0x80; + else coldat&=0x7f; + RAM_POKE(coldep,coldat); + TRACE_SUSIE0("PaintSprites() EVERON IS ACTIVE"); + TRACE_SUSIE2("PaintSprites() Wrote $%02x to SCB collision depositary at $%04x",coldat,coldep); + } + + // Perform Sprite debugging if required, single step on sprite draw + if(gSingleStepModeSprites) { + // char message[256]; + //log_printf("CSusie:PaintSprites() - Rendered Sprite %03d\n",sprcount); + //SingleStepModeSprites=0; + } + } else { + TRACE_SUSIE0("PaintSprites() mSPRCTL1.Bits.SkipSprite==TRUE"); + } + + // Increase sprite number + sprcount++; + + // Check if we abort after 1st sprite is complete + + // if(mSPRSYS.Read.StopOnCurrent) + // { + // mSPRSYS.Read.Status=0; // Engine has finished + // mSPRGO=FALSE; + // break; + // } + + // Check sprcount for looping SCB, random large number chosen + if(sprcount>4096) { + // Stop the system, otherwise we may just come straight back in..... + gSystemHalt=TRUE; + // Display warning message + log_printf("CSusie:PaintSprites(): Single draw sprite limit exceeded (>4096). The SCB is most likely looped back on itself. Reset/Exit is recommended\n"); + // Signal error to the caller + return 0; + } + } + // Fudge factor to fix many flickering issues, also the keypress + // problem with Hard Drivin and the strange pause in Dirty Larry. + // mCycles>>=2; + return mCycles; +} + +void CSusie::Poke(ULONG addr,UBYTE data) +{ + switch(addr&0xff) { + case (TMPADRL&0xff): + mTMPADR.Byte.Low=data; + mTMPADR.Byte.High=0; + TRACE_SUSIE2("Poke(TMPADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (TMPADRH&0xff): + mTMPADR.Byte.High=data; + TRACE_SUSIE2("Poke(TMPADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (TILTACUML&0xff): + mTILTACUM.Byte.Low=data; + mTILTACUM.Byte.High=0; + TRACE_SUSIE2("Poke(TILTACUML,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (TILTACUMH&0xff): + mTILTACUM.Byte.High=data; + TRACE_SUSIE2("Poke(TILTACUMH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (HOFFL&0xff): + mHOFF.Byte.Low=data; + mHOFF.Byte.High=0; + TRACE_SUSIE2("Poke(HOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (HOFFH&0xff): + mHOFF.Byte.High=data; + TRACE_SUSIE2("Poke(HOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VOFFL&0xff): + mVOFF.Byte.Low=data; + mVOFF.Byte.High=0; + TRACE_SUSIE2("Poke(VOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VOFFH&0xff): + mVOFF.Byte.High=data; + TRACE_SUSIE2("Poke(VOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VIDBASL&0xff): + mVIDBAS.Byte.Low=data; + mVIDBAS.Byte.High=0; + TRACE_SUSIE2("Poke(VIDBASL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VIDBASH&0xff): + mVIDBAS.Byte.High=data; + TRACE_SUSIE2("Poke(VIDBASH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (COLLBASL&0xff): + mCOLLBAS.Byte.Low=data; + mCOLLBAS.Byte.High=0; + TRACE_SUSIE2("Poke(COLLBASL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (COLLBASH&0xff): + mCOLLBAS.Byte.High=data; + TRACE_SUSIE2("Poke(COLLBASH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VIDADRL&0xff): + mVIDADR.Byte.Low=data; + mVIDADR.Byte.High=0; + TRACE_SUSIE2("Poke(VIDADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VIDADRH&0xff): + mVIDADR.Byte.High=data; + TRACE_SUSIE2("Poke(VIDADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (COLLADRL&0xff): + mCOLLADR.Byte.Low=data; + mCOLLADR.Byte.High=0; + TRACE_SUSIE2("Poke(COLLADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (COLLADRH&0xff): + mCOLLADR.Byte.High=data; + TRACE_SUSIE2("Poke(COLLADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SCBNEXTL&0xff): + mSCBNEXT.Byte.Low=data; + mSCBNEXT.Byte.High=0; + TRACE_SUSIE2("Poke(SCBNEXTL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SCBNEXTH&0xff): + mSCBNEXT.Byte.High=data; + TRACE_SUSIE2("Poke(SCBNEXTH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRDLINEL&0xff): + mSPRDLINE.Byte.Low=data; + mSPRDLINE.Byte.High=0; + TRACE_SUSIE2("Poke(SPRDLINEL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRDLINEH&0xff): + mSPRDLINE.Byte.High=data; + TRACE_SUSIE2("Poke(SPRDLINEH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (HPOSSTRTL&0xff): + mHPOSSTRT.Byte.Low=data; + mHPOSSTRT.Byte.High=0; + TRACE_SUSIE2("Poke(HPOSSTRTL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (HPOSSTRTH&0xff): + mHPOSSTRT.Byte.High=data; + TRACE_SUSIE2("Poke(HPOSSTRTH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VPOSSTRTL&0xff): + mVPOSSTRT.Byte.Low=data; + mVPOSSTRT.Byte.High=0; + TRACE_SUSIE2("Poke(VPOSSTRTL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VPOSSTRTH&0xff): + mVPOSSTRT.Byte.High=data; + TRACE_SUSIE2("Poke(VPOSSTRTH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRHSIZL&0xff): + mSPRHSIZ.Byte.Low=data; + mSPRHSIZ.Byte.High=0; + TRACE_SUSIE2("Poke(SPRHSIZL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRHSIZH&0xff): + mSPRHSIZ.Byte.High=data; + TRACE_SUSIE2("Poke(SPRHSIZH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRVSIZL&0xff): + mSPRVSIZ.Byte.Low=data; + mSPRVSIZ.Byte.High=0; + TRACE_SUSIE2("Poke(SPRVSIZL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRVSIZH&0xff): + mSPRVSIZ.Byte.High=data; + TRACE_SUSIE2("Poke(SPRVSIZH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (STRETCHL&0xff): + mSTRETCH.Byte.Low=data; + mSTRETCH.Byte.High=0; + TRACE_SUSIE2("Poke(STRETCHL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (STRETCHH&0xff): + TRACE_SUSIE2("Poke(STRETCHH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mSTRETCH.Byte.High=data; + break; + case (TILTL&0xff): + mTILT.Byte.Low=data; + mTILT.Byte.High=0; + TRACE_SUSIE2("Poke(TILTL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (TILTH&0xff): + mTILT.Byte.High=data; + TRACE_SUSIE2("Poke(TILTH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRDOFFL&0xff): + TRACE_SUSIE2("Poke(SPRDOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mSPRDOFF.Byte.Low=data; + mSPRDOFF.Byte.High=0; + break; + case (SPRDOFFH&0xff): + TRACE_SUSIE2("Poke(SPRDOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mSPRDOFF.Byte.High=data; + break; + case (SPRVPOSL&0xff): + TRACE_SUSIE2("Poke(SPRVPOSL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mSPRVPOS.Byte.Low=data; + mSPRVPOS.Byte.High=0; + break; + case (SPRVPOSH&0xff): + mSPRVPOS.Byte.High=data; + TRACE_SUSIE2("Poke(SPRVPOSH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (COLLOFFL&0xff): + mCOLLOFF.Byte.Low=data; + mCOLLOFF.Byte.High=0; + TRACE_SUSIE2("Poke(COLLOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (COLLOFFH&0xff): + mCOLLOFF.Byte.High=data; + TRACE_SUSIE2("Poke(COLLOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VSIZACUML&0xff): + mVSIZACUM.Byte.Low=data; + mVSIZACUM.Byte.High=0; + TRACE_SUSIE2("Poke(VSIZACUML,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VSIZACUMH&0xff): + mVSIZACUM.Byte.High=data; + TRACE_SUSIE2("Poke(VSIZACUMH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (HSIZOFFL&0xff): + mHSIZOFF.Byte.Low=data; + mHSIZOFF.Byte.High=0; + TRACE_SUSIE2("Poke(HSIZOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (HSIZOFFH&0xff): + mHSIZOFF.Byte.High=data; + TRACE_SUSIE2("Poke(HSIZOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VSIZOFFL&0xff): + mVSIZOFF.Byte.Low=data; + mVSIZOFF.Byte.High=0; + TRACE_SUSIE2("Poke(VSIZOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (VSIZOFFH&0xff): + mVSIZOFF.Byte.High=data; + TRACE_SUSIE2("Poke(VSIZOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SCBADRL&0xff): + mSCBADR.Byte.Low=data; + mSCBADR.Byte.High=0; + TRACE_SUSIE2("Poke(SCBADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SCBADRH&0xff): + mSCBADR.Byte.High=data; + TRACE_SUSIE2("Poke(SCBADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (PROCADRL&0xff): + mPROCADR.Byte.Low=data; + mPROCADR.Byte.High=0; + TRACE_SUSIE2("Poke(PROCADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (PROCADRH&0xff): + mPROCADR.Byte.High=data; + TRACE_SUSIE2("Poke(PROCADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + + case (MATHD&0xff): + TRACE_SUSIE2("Poke(MATHD,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mMATHABCD.Bytes.D=data; + // mMATHABCD.Bytes.C=0; + // The hardware manual says that the sign shouldnt change + // but if I dont do this then stun runner will hang as it + // does the init in the wrong order and if the previous + // calc left a zero there then we'll get a sign error + Poke(MATHC,0); + break; + case (MATHC&0xff): + TRACE_SUSIE2("Poke(MATHC,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mMATHABCD.Bytes.C=data; + // Perform sign conversion if required + if(mSPRSYS_SignedMath) { + // Account for the math bug that 0x8000 is +ve & 0x0000 is -ve by subracting 1 + if((mMATHABCD.Words.CD-1)&0x8000) { + UWORD conv; + conv=mMATHABCD.Words.CD^0xffff; + conv++; + mMATHCD_sign=-1; + TRACE_SUSIE2("MATH CD signed conversion complete %04x to %04x",mMATHABCD.Words.CD,conv); + mMATHABCD.Words.CD=conv; + } else { + mMATHCD_sign=1; + } + } + break; + case (MATHB&0xff): + TRACE_SUSIE2("Poke(MATHB,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mMATHABCD.Bytes.B=data; + mMATHABCD.Bytes.A=0; + break; + case (MATHA&0xff): + TRACE_SUSIE2("Poke(MATHA,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mMATHABCD.Bytes.A=data; + // Perform sign conversion if required + if(mSPRSYS_SignedMath) { + // Account for the math bug that 0x8000 is +ve & 0x0000 is -ve by subracting 1 + if((mMATHABCD.Words.AB-1)&0x8000) { + UWORD conv; + conv=mMATHABCD.Words.AB^0xffff; + conv++; + mMATHAB_sign=-1; + TRACE_SUSIE2("MATH AB signed conversion complete %04x to %04x",mMATHABCD.Words.AB,conv); + mMATHABCD.Words.AB=conv; + } else { + mMATHAB_sign=1; + } + } + + TRACE_SUSIE2("DoMathMultiply() AB=$%04x * CD=$%04x",mMATHABCD.Words.AB,mMATHABCD.Words.CD); + + mSPRSYS_Mathbit=FALSE; + + // Multiplies with out sign or accumulate take 44 ticks to complete. + // Multiplies with sign and accumulate take 54 ticks to complete. + // + // AB EFGH + // * CD / NP + // ------- ----------- + // EFGH ABCD + // Accumulate in JKLM Remainder in (JK)LM + // + + // Basic multiply is ALWAYS unsigned, sign conversion is done later + mMATHEFGH.Long=(ULONG)mMATHABCD.Words.AB*(ULONG)mMATHABCD.Words.CD; + + if(mSPRSYS_SignedMath) { + // Add the sign bits, only >0 is +ve result + mMATHEFGH_sign=mMATHAB_sign+mMATHCD_sign; + if(!mMATHEFGH_sign) { + mMATHEFGH.Long^=0xffffffff; + mMATHEFGH.Long++; + } + } + + // Check overflow, if B31 has changed from 1->0 then its overflow time + if(mSPRSYS_Accumulate) { + TRACE_SUSIE0("DoMathMultiply() - ACCUMULATED JKLM+=EFGH"); + ULONG tmp=mMATHJKLM.Long+mMATHEFGH.Long; + // Let sign change indicate overflow + if((tmp&0x80000000)!=(mMATHJKLM.Long&0x80000000)) { + TRACE_SUSIE0("DoMathMultiply() - OVERFLOW DETECTED"); + // mSPRSYS_Mathbit=TRUE; + } + // Save accumulated result + mMATHJKLM.Long=tmp; + } + TRACE_SUSIE1("DoMathMultiply() Results (Multi) EFGH=$%08x",mMATHEFGH.Long); + TRACE_SUSIE1("DoMathMultiply() Results (Accum) JKLM=$%08x",mMATHJKLM.Long); + break; + + case (MATHP&0xff): + mMATHNP.Bytes.P=data; + mMATHNP.Bytes.N=0; + TRACE_SUSIE2("Poke(MATHP,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (MATHN&0xff): + mMATHNP.Bytes.N=data; + TRACE_SUSIE2("Poke(MATHN,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + + case (MATHH&0xff): + mMATHEFGH.Bytes.H=data; + mMATHEFGH.Bytes.G=0; + TRACE_SUSIE2("Poke(MATHH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (MATHG&0xff): + mMATHEFGH.Bytes.G=data; + TRACE_SUSIE2("Poke(MATHG,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (MATHF&0xff): + mMATHEFGH.Bytes.F=data; + mMATHEFGH.Bytes.E=0; + TRACE_SUSIE2("Poke(MATHF,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (MATHE&0xff): + mMATHEFGH.Bytes.E=data; + TRACE_SUSIE2("Poke(MATHE,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + mSPRSYS_Mathbit=FALSE; + + // + // Divides take 176 + 14*N ticks + // (N is the number of most significant zeros in the divisor.) + // + // AB EFGH + // * CD / NP + // ------- ----------- + // EFGH ABCD + // Accumulate in JKLM Remainder in (JK)LM + // + + // Divide is ALWAYS unsigned arithmetic... + if(mMATHNP.Long) { + TRACE_SUSIE0("DoMathDivide() - UNSIGNED"); + mMATHABCD.Long=mMATHEFGH.Long/mMATHNP.Long; + mMATHJKLM.Long=mMATHEFGH.Long%mMATHNP.Long; + // mSPRSYS_Mathbit=FALSE; + } else { + TRACE_SUSIE0("DoMathDivide() - DIVIDE BY ZERO ERROR"); + mMATHABCD.Long=0xffffffff; + mMATHJKLM.Long=0; + mSPRSYS_Mathbit=TRUE; + } + break; + + case (MATHM&0xff): + mMATHJKLM.Bytes.M=data; + mMATHJKLM.Bytes.L=0; + mSPRSYS_Mathbit=FALSE; + TRACE_SUSIE2("Poke(MATHM,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (MATHL&0xff): + mMATHJKLM.Bytes.L=data; + TRACE_SUSIE2("Poke(MATHL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (MATHK&0xff): + mMATHJKLM.Bytes.K=data; + mMATHJKLM.Bytes.J=0; + TRACE_SUSIE2("Poke(MATHK,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (MATHJ&0xff): + mMATHJKLM.Bytes.J=data; + TRACE_SUSIE2("Poke(MATHJ,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + + case (SPRCTL0&0xff): + // mSPRCTL0_Type=data&0x0007; + mSPRCTL0_Vflip=data&0x0010; + mSPRCTL0_Hflip=data&0x0020; + mSPRCTL0_PixelBits=((data&0x00c0)>>6)+1; + TRACE_SUSIE2("Poke(SPRCTL0,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRCTL1&0xff): + mSPRCTL1_StartLeft=data&0x0001; + mSPRCTL1_StartUp=data&0x0002; + mSPRCTL1_SkipSprite=data&0x0004; + mSPRCTL1_ReloadPalette=data&0x0008; + mSPRCTL1_ReloadDepth=(data&0x0030)>>4; + mSPRCTL1_Sizing=data&0x0040; + mSPRCTL1_Literal=data&0x0080; + TRACE_SUSIE2("Poke(SPRCTL1,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRCOLL&0xff): + mSPRCOLL_Number=data&0x000f; + mSPRCOLL_Collide=data&0x0020; + TRACE_SUSIE2("Poke(SPRCOLL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRINIT&0xff): + mSPRINIT.Byte=data; + TRACE_SUSIE2("Poke(SPRINIT,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SUZYBUSEN&0xff): + mSUZYBUSEN=data&0x01; + TRACE_SUSIE2("Poke(SUZYBUSEN,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRGO&0xff): + mSPRGO=data&0x01; + mEVERON=data&0x04; + TRACE_SUSIE2("Poke(SPRGO,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (SPRSYS&0xff): + mSPRSYS_StopOnCurrent=data&0x0002; + if(data&0x0004) mSPRSYS_UnsafeAccess=0; + mSPRSYS_LeftHand=data&0x0008; + mSPRSYS_VStretch=data&0x0010; + mSPRSYS_NoCollide=data&0x0020; + mSPRSYS_Accumulate=data&0x0040; + mSPRSYS_SignedMath=data&0x0080; + TRACE_SUSIE2("Poke(SPRSYS,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + + // Cartridge writing ports + + case (RCART0&0xff): + if(mSystem.mCart->CartGetAudin() && mSystem.mMikie->SwitchAudInValue()){ + mSystem.mCart->Poke0A(data); + }else{ + mSystem.mCart->Poke0(data); + } + mSystem.mEEPROM->ProcessEepromCounter(mSystem.mCart->GetCounterValue()); + TRACE_SUSIE2("Poke(RCART0,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + case (RCART1&0xff): + if(mSystem.mCart->CartGetAudin() && mSystem.mMikie->SwitchAudInValue()){ + mSystem.mCart->Poke1A(data); + }else{ + mSystem.mCart->Poke1(data); + } + mSystem.mEEPROM->ProcessEepromCounter(mSystem.mCart->GetCounterValue()); + TRACE_SUSIE2("Poke(RCART1,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + + // These are not so important, so lets ignore them for the moment + + case (LEDS&0xff): + case (PPORTSTAT&0xff): + case (PPORTDATA&0xff): + case (HOWIE&0xff): + TRACE_SUSIE2("Poke(LEDS/PPORTSTST/PPORTDATA/HOWIE,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC()); + break; + + // Errors on read only register accesses + + case (SUZYHREV&0xff): + case (JOYSTICK&0xff): + case (SWITCHES&0xff): + TRACE_SUSIE3("Poke(%04x,%02x) - Poke to read only register location at PC=%04x",addr,data,mSystem.mCpu->GetPC()); + break; + + // Errors on illegal location accesses + + default: + TRACE_SUSIE3("Poke(%04x,%02x) - Poke to illegal location at PC=%04x",addr,data,mSystem.mCpu->GetPC()); + break; + } +} + +UBYTE CSusie::Peek(ULONG addr) +{ + UBYTE retval=0; + switch(addr&0xff) { + case (TMPADRL&0xff): + retval=mTMPADR.Byte.Low; + TRACE_SUSIE2("Peek(TMPADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (TMPADRH&0xff): + retval=mTMPADR.Byte.High; + TRACE_SUSIE2("Peek(TMPADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (TILTACUML&0xff): + retval=mTILTACUM.Byte.Low; + TRACE_SUSIE2("Peek(TILTACUML)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (TILTACUMH&0xff): + retval=mTILTACUM.Byte.High; + TRACE_SUSIE2("Peek(TILTACUMH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (HOFFL&0xff): + retval=mHOFF.Byte.Low; + TRACE_SUSIE2("Peek(HOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (HOFFH&0xff): + retval=mHOFF.Byte.High; + TRACE_SUSIE2("Peek(HOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VOFFL&0xff): + retval=mVOFF.Byte.Low; + TRACE_SUSIE2("Peek(VOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VOFFH&0xff): + retval=mVOFF.Byte.High; + TRACE_SUSIE2("Peek(VOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VIDBASL&0xff): + retval=mVIDBAS.Byte.Low; + TRACE_SUSIE2("Peek(VIDBASL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VIDBASH&0xff): + retval=mVIDBAS.Byte.High; + TRACE_SUSIE2("Peek(VIDBASH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (COLLBASL&0xff): + retval=mCOLLBAS.Byte.Low; + TRACE_SUSIE2("Peek(COLLBASL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (COLLBASH&0xff): + retval=mCOLLBAS.Byte.High; + TRACE_SUSIE2("Peek(COLLBASH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VIDADRL&0xff): + retval=mVIDADR.Byte.Low; + TRACE_SUSIE2("Peek(VIDADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VIDADRH&0xff): + retval=mVIDADR.Byte.High; + TRACE_SUSIE2("Peek(VIDADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (COLLADRL&0xff): + retval=mCOLLADR.Byte.Low; + TRACE_SUSIE2("Peek(COLLADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (COLLADRH&0xff): + retval=mCOLLADR.Byte.High; + TRACE_SUSIE2("Peek(COLLADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SCBNEXTL&0xff): + retval=mSCBNEXT.Byte.Low; + TRACE_SUSIE2("Peek(SCBNEXTL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SCBNEXTH&0xff): + retval=mSCBNEXT.Byte.High; + TRACE_SUSIE2("Peek(SCBNEXTH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRDLINEL&0xff): + retval=mSPRDLINE.Byte.Low; + TRACE_SUSIE2("Peek(SPRDLINEL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRDLINEH&0xff): + retval=mSPRDLINE.Byte.High; + TRACE_SUSIE2("Peek(SPRDLINEH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (HPOSSTRTL&0xff): + retval=mHPOSSTRT.Byte.Low; + TRACE_SUSIE2("Peek(HPOSSTRTL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (HPOSSTRTH&0xff): + retval=mHPOSSTRT.Byte.High; + TRACE_SUSIE2("Peek(HPOSSTRTH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VPOSSTRTL&0xff): + retval=mVPOSSTRT.Byte.Low; + TRACE_SUSIE2("Peek(VPOSSTRTL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VPOSSTRTH&0xff): + retval=mVPOSSTRT.Byte.High; + TRACE_SUSIE2("Peek(VPOSSTRTH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRHSIZL&0xff): + retval=mSPRHSIZ.Byte.Low; + TRACE_SUSIE2("Peek(SPRHSIZL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRHSIZH&0xff): + retval=mSPRHSIZ.Byte.High; + TRACE_SUSIE2("Peek(SPRHSIZH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRVSIZL&0xff): + retval=mSPRVSIZ.Byte.Low; + TRACE_SUSIE2("Peek(SPRVSIZL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRVSIZH&0xff): + retval=mSPRVSIZ.Byte.High; + TRACE_SUSIE2("Peek(SPRVSIZH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (STRETCHL&0xff): + retval=mSTRETCH.Byte.Low; + TRACE_SUSIE2("Peek(STRETCHL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (STRETCHH&0xff): + retval=mSTRETCH.Byte.High; + TRACE_SUSIE2("Peek(STRETCHH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (TILTL&0xff): + retval=mTILT.Byte.Low; + TRACE_SUSIE2("Peek(TILTL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (TILTH&0xff): + retval=mTILT.Byte.High; + TRACE_SUSIE2("Peek(TILTH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRDOFFL&0xff): + retval=mSPRDOFF.Byte.Low; + TRACE_SUSIE2("Peek(SPRDOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRDOFFH&0xff): + retval=mSPRDOFF.Byte.High; + TRACE_SUSIE2("Peek(SPRDOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRVPOSL&0xff): + retval=mSPRVPOS.Byte.Low; + TRACE_SUSIE2("Peek(SPRVPOSL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SPRVPOSH&0xff): + retval=mSPRVPOS.Byte.High; + TRACE_SUSIE2("Peek(SPRVPOSH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (COLLOFFL&0xff): + retval=mCOLLOFF.Byte.Low; + TRACE_SUSIE2("Peek(COLLOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (COLLOFFH&0xff): + retval=mCOLLOFF.Byte.High; + TRACE_SUSIE2("Peek(COLLOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VSIZACUML&0xff): + retval=mVSIZACUM.Byte.Low; + TRACE_SUSIE2("Peek(VSIZACUML)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VSIZACUMH&0xff): + retval=mVSIZACUM.Byte.High; + TRACE_SUSIE2("Peek(VSIZACUMH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (HSIZOFFL&0xff): + retval=mHSIZOFF.Byte.Low; + TRACE_SUSIE2("Peek(HSIZOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (HSIZOFFH&0xff): + retval=mHSIZOFF.Byte.High; + TRACE_SUSIE2("Peek(HSIZOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VSIZOFFL&0xff): + retval=mVSIZOFF.Byte.Low; + TRACE_SUSIE2("Peek(VSIZOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (VSIZOFFH&0xff): + retval=mVSIZOFF.Byte.High; + TRACE_SUSIE2("Peek(VSIZOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SCBADRL&0xff): + retval=mSCBADR.Byte.Low; + TRACE_SUSIE2("Peek(SCBADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SCBADRH&0xff): + retval=mSCBADR.Byte.High; + TRACE_SUSIE2("Peek(SCBADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (PROCADRL&0xff): + retval=mPROCADR.Byte.Low; + TRACE_SUSIE2("Peek(PROCADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (PROCADRH&0xff): + retval=mPROCADR.Byte.High; + TRACE_SUSIE2("Peek(PROCADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHD&0xff): + retval=mMATHABCD.Bytes.D; + TRACE_SUSIE2("Peek(MATHD)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHC&0xff): + retval=mMATHABCD.Bytes.C; + TRACE_SUSIE2("Peek(MATHC)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHB&0xff): + retval=mMATHABCD.Bytes.B; + TRACE_SUSIE2("Peek(MATHB)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHA&0xff): + retval=mMATHABCD.Bytes.A; + TRACE_SUSIE2("Peek(MATHA)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (MATHP&0xff): + retval=mMATHNP.Bytes.P; + TRACE_SUSIE2("Peek(MATHP)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHN&0xff): + retval=mMATHNP.Bytes.N; + TRACE_SUSIE2("Peek(MATHN)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (MATHH&0xff): + retval=mMATHEFGH.Bytes.H; + TRACE_SUSIE2("Peek(MATHH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHG&0xff): + retval=mMATHEFGH.Bytes.G; + TRACE_SUSIE2("Peek(MATHG)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHF&0xff): + retval=mMATHEFGH.Bytes.F; + TRACE_SUSIE2("Peek(MATHF)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHE&0xff): + retval=mMATHEFGH.Bytes.E; + TRACE_SUSIE2("Peek(MATHE)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHM&0xff): + retval=mMATHJKLM.Bytes.M; + TRACE_SUSIE2("Peek(MATHM)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHL&0xff): + retval=mMATHJKLM.Bytes.L; + TRACE_SUSIE2("Peek(MATHL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHK&0xff): + retval=mMATHJKLM.Bytes.K; + TRACE_SUSIE2("Peek(MATHK)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (MATHJ&0xff): + retval=mMATHJKLM.Bytes.J; + TRACE_SUSIE2("Peek(MATHJ)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (SUZYHREV&0xff): + retval=0x01; + TRACE_SUSIE2("Peek(SUZYHREV)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (SPRSYS&0xff): + retval=0x0000; + // retval+=(mSPRSYS_Status)?0x0001:0x0000; + // Use gSystemCPUSleep to signal the status instead, if we are asleep then + // we must be rendering sprites + retval+=(gSystemCPUSleep)?0x0001:0x0000; + retval+=(mSPRSYS_StopOnCurrent)?0x0002:0x0000; + retval+=(mSPRSYS_UnsafeAccess)?0x0004:0x0000; + retval+=(mSPRSYS_LeftHand)?0x0008:0x0000; + retval+=(mSPRSYS_VStretch)?0x0010:0x0000; + retval+=(mSPRSYS_LastCarry)?0x0020:0x0000; + retval+=(mSPRSYS_Mathbit)?0x0040:0x0000; + retval+=(mSPRSYS_MathInProgress)?0x0080:0x0000; + TRACE_SUSIE2("Peek(SPRSYS)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (JOYSTICK&0xff): + if(mSPRSYS_LeftHand) { + retval= mJOYSTICK.Byte; + } else { + TJOYSTICK Modified=mJOYSTICK; + Modified.Bits.Left=mJOYSTICK.Bits.Right; + Modified.Bits.Right=mJOYSTICK.Bits.Left; + Modified.Bits.Down=mJOYSTICK.Bits.Up; + Modified.Bits.Up=mJOYSTICK.Bits.Down; + retval= Modified.Byte; + } + // TRACE_SUSIE2("Peek(JOYSTICK)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + case (SWITCHES&0xff): + retval=mSWITCHES.Byte; + // TRACE_SUSIE2("Peek(SWITCHES)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + // Cartridge reading ports + + case (RCART0&0xff): + if(mSystem.mCart->CartGetAudin() && mSystem.mMikie->SwitchAudInValue()){ + retval=mSystem.mCart->Peek0A(); + }else{ + retval=mSystem.mCart->Peek0(); + } + mSystem.mEEPROM->ProcessEepromCounter(mSystem.mCart->GetCounterValue()); + // TRACE_SUSIE2("Peek(RCART0)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + case (RCART1&0xff): + if(mSystem.mCart->CartGetAudin() && mSystem.mMikie->SwitchAudInValue()){ + retval=mSystem.mCart->Peek1A(); + }else{ + retval=mSystem.mCart->Peek1(); + } + mSystem.mEEPROM->ProcessEepromCounter(mSystem.mCart->GetCounterValue()); + // TRACE_SUSIE2("Peek(RCART1)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC()); + return retval; + + // These are no so important so lets ignore them for the moment + + case (LEDS&0xff): + case (PPORTSTAT&0xff): + case (PPORTDATA&0xff): + case (HOWIE&0xff): + TRACE_SUSIE1("Peek(LEDS/PPORTSTAT/PPORTDATA) at PC=$%04x",mSystem.mCpu->GetPC()); + break; + + // Errors on write only register accesses + + case (SPRCTL0&0xff): + case (SPRCTL1&0xff): + case (SPRCOLL&0xff): + case (SPRINIT&0xff): + case (SUZYBUSEN&0xff): + case (SPRGO&0xff): + TRACE_SUSIE2("Peek(%04x) - Peek from write only register location at PC=$%04x",addr,mSystem.mCpu->GetPC()); + break; + + // Errors on illegal location accesses + + default: + TRACE_SUSIE2("Peek(%04x) - Peek from illegal location at PC=$%04x",addr,mSystem.mCpu->GetPC()); + break; + } + + return 0xff; +} diff --git a/MCUME_teensy41/teensyhandy/susie.h b/MCUME_teensy41/teensyhandy/susie.h new file mode 100644 index 0000000..ca60fc1 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/susie.h @@ -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>(mLineShiftRegCount-bits); \ + retval_bits&=(1<>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 diff --git a/MCUME_teensy41/teensyhandy/susie_pixel_loop.h b/MCUME_teensy41/teensyhandy/susie_pixel_loop.h new file mode 100644 index 0000000..b9e3b8a --- /dev/null +++ b/MCUME_teensy41/teensyhandy/susie_pixel_loop.h @@ -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=0 && hoff + +#include +#include +#include +#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 diff --git a/MCUME_teensy41/teensyhandy/system.h b/MCUME_teensy41/teensyhandy/system.h new file mode 100644 index 0000000..a6b5065 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/system.h @@ -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 + +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 +//#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) 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) 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 diff --git a/MCUME_teensy41/teensyhandy/teensyhandy.ino b/MCUME_teensy41/teensyhandy/teensyhandy.ino new file mode 100644 index 0000000..21c4f4b --- /dev/null +++ b/MCUME_teensy41/teensyhandy/teensyhandy.ino @@ -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>8, + 320 & 0xFF, // YEND = 320 + ST7735_INVON , DELAY, // 7: hack + 10, + ST7735_NORON , DELAY, // 8: Normal display on, no args, w/delay + 10, // 10 ms delay + ST7735_DISPON , DELAY, // 9: Main screen turn on, no args, w/delay + 255 +#endif +}; + +static void dmaInterrupt() { + dmatx.clearInterrupt(); + curTransfer++; + if (curTransfer >= nbTransfer) { + curTransfer = 0; + if (cancelled) { + dmatx.disable(); + rstop = 1; + } + } + arm_dcache_flush(blocks[curTransfer], LINES_PER_BLOCK*TFT_WIDTH*2); +} + +static void setDmaStruct() { + uint32_t remaining = TFT_HEIGHT*TFT_WIDTH*2; + int i=0; + uint16_t col=RGBVAL16(0x00,0x00,0x00); + uint16_t * fb = (uint16_t*)((int)malloc(remaining+64)&0xffffffe0); + while (remaining > 0) { + //uint16_t * fb = blocks[i]; + int32_t len = (remaining >= (LINES_PER_BLOCK*TFT_WIDTH*2)?LINES_PER_BLOCK*TFT_WIDTH*2:remaining); +#ifdef TFT_DEBUG + Serial.println((unsigned long)blocks[i]); + Serial.println(remaining); +#endif + switch (i) { + case 0: + //if (fb == 0) fb = (uint16_t*)((int)malloc(len+64)&0xffffffe0); + //fb=&fb0[0]; +#ifdef TFT_DEBUG + col = RGBVAL16(0x00,0xff,0x00); +#endif + break; + case 1: + //if (fb == 0) fb = (uint16_t*)((int)malloc(len+64)&0xffffffe0); + //fb=&fb1[0]; +#ifdef TFT_DEBUG + col = RGBVAL16(0x00,0xff,0xff); +#endif + break; + case 2: + //if (fb == 0) fb = (uint16_t*)((int)malloc(len+64)&0xffffffe0); + //fb=&fb2[0]; +#ifdef TFT_DEBUG + col = RGBVAL16(0x00,0x00,0xff); +#endif + break; + case 3: + //if (fb == 0) fb = (uint16_t*)((int)malloc(len+64)&0xffffffe0); + //fb=&fb3[0]; +#ifdef TFT_DEBUG + col = RGBVAL16(0xff,0x00,0xff); +#endif + break; + } + blocks[i] = fb; + if (blocks[i] == 0) { + Serial.print("ILI9341 allocaltion failed for block "); + Serial.println(i); + delay(10000); + } + + for (int j=0;jATTR_DST = 1; + dmasettings[i].replaceSettingsOnCompletion(dmasettings[i+1]); + dmasettings[i].interruptAtCompletion(); + fb += len/2; + remaining -= len; + i++; + } + dmasettings[i-1].replaceSettingsOnCompletion(dmasettings[0]); + nbTransfer = i; +#ifdef TFT_DEBUG + Serial.println(nbTransfer); +#endif +} + + +TFT_T_DMA::TFT_T_DMA(uint8_t cs, uint8_t dc, uint8_t rst, uint8_t mosi, uint8_t sclk, uint8_t miso, uint8_t touch_cs, uint8_t touch_irq) +{ + _cs = cs; + _dc = dc; + _rst = rst; + _mosi = mosi; + _sclk = sclk; + _miso = miso; + pinMode(_dc, OUTPUT); + pinMode(_cs, OUTPUT); + digitalWrite(_cs, 1); + digitalWrite(_dc, 1); + if ( (touch_cs != 255) && (touch_irq != 255) ) { + _touch_irq = touch_irq; + _touch_cs = touch_cs; + pinMode(_touch_cs, OUTPUT); + pinMode(touch_irq, INPUT_PULLUP); + digitalWrite(_touch_cs, 1); + } +} + + +void TFT_T_DMA::setArea(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2) { + int dx=0; + int dy=0; +#ifdef ST7789 + if (TFT_REALWIDTH == TFT_REALHEIGHT) + { +#ifdef ROTATE_SCREEN + if (!flipped) { + dy += 80; + } +#else + if (flipped) { + dx += 80; + } +#endif + } +#endif + + digitalWrite(_dc, 0); + SPI.transfer(TFT_CASET); + digitalWrite(_dc, 1); + SPI.transfer16(x1+dx); + digitalWrite(_dc, 1); + SPI.transfer16(x2+dx); + digitalWrite(_dc, 0); + SPI.transfer(TFT_PASET); + digitalWrite(_dc, 1); + SPI.transfer16(y1+dy); + digitalWrite(_dc, 1); + SPI.transfer16(y2+dy); + + digitalWrite(_dc, 0); + SPI.transfer(TFT_RAMWR); + digitalWrite(_dc, 1); + + return; + + + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + digitalWrite(_cs, 0); + + digitalWrite(_dc, 0); + SPI.transfer(TFT_CASET); + digitalWrite(_dc, 1); + + SPI.transfer16(x1+dx); + SPI.transfer16(x2+dx); + + digitalWrite(_dc, 0); + SPI.transfer(TFT_PASET); + digitalWrite(_dc, 1); + SPI.transfer16(y1+dy); + SPI.transfer16(y2+dy); + + digitalWrite(_dc, 0); + SPI.transfer(TFT_RAMWR); + digitalWrite(_dc, 1); + + digitalWrite(_cs, 1); + SPI.endTransaction(); +} + + + + +void TFT_T_DMA::begin(void) { + SPI.setMOSI(_mosi); + SPI.setMISO(_miso); + SPI.setSCK(_sclk); + SPI.begin(); + + // Initialize display + if (_rst != 0xff) { + pinMode(_rst, OUTPUT); + digitalWrite(_rst, HIGH); + delay(100); + digitalWrite(_rst, LOW); + delay(100); + digitalWrite(_rst, HIGH); + delay(200); + } + + + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + const uint8_t *addr = init_commands; + digitalWrite(_cs, 0); +#ifdef ILI9341 + while (1) { + uint8_t count = *addr++; + if (count-- == 0) break; + + digitalWrite(_dc, 0); + SPI.transfer(*addr++); + + while (count-- > 0) { + digitalWrite(_dc, 1); + SPI.transfer(*addr++); + } + } + + digitalWrite(_dc, 0); + SPI.transfer(ILI9341_SLPOUT); + digitalWrite(_dc, 1); + digitalWrite(_cs, 1); + SPI.endTransaction(); + + digitalWrite(_dc, 1); + digitalWrite(_cs, 1); + SPI.endTransaction(); + + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + digitalWrite(_dc, 0); + digitalWrite(_cs, 0); + SPI.transfer(ILI9341_DISPON); + digitalWrite(_dc, 1); + digitalWrite(_cs, 1); +#endif +#ifdef ST7789 + uint8_t numCommands, numArgs; + uint16_t ms; + numCommands = *addr++; // Number of commands to follow + while(numCommands--) { // For each command... + digitalWrite(_dc, 0); + SPI.transfer(*addr++); + numArgs = *addr++; // Number of args to follow + ms = numArgs & DELAY; // If hibit set, delay follows args + numArgs &= ~DELAY; // Mask out delay bit + while(numArgs > 1) { // For each argument... + digitalWrite(_dc, 1); + SPI.transfer(*addr++); + numArgs--; + } + + if (numArgs) { + digitalWrite(_dc, 1); + SPI.transfer(*addr++); + } + if(ms) { + ms = *addr++; // Read post-command delay time (ms) + if(ms == 255) ms = 500; // If 255, delay for 500 ms + digitalWrite(_cs, 1); + SPI.endTransaction(); + delay(ms); + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + digitalWrite(_cs, 0); + } + } + digitalWrite(_cs, 1); +#endif + setArea(0, 0, TFT_REALWIDTH-1, TFT_REALHEIGHT-1); + SPI.endTransaction(); + + cancelled = false; + +#ifdef FLIP_SCREEN + flipscreen(true); +#else + flipscreen(false); +#endif +#ifdef ST7789 + if (TFT_REALWIDTH != TFT_REALHEIGHT) + { + flipscreen(true); + } +#endif +}; + + + +void TFT_T_DMA::flipscreen(bool flip) +{ + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + digitalWrite(_dc, 0); + digitalWrite(_cs, 0); + SPI.transfer(TFT_MADCTL); + digitalWrite(_dc, 1); + if (flip) { + flipped=true; +#ifdef ILI9341 + SPI.transfer(ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR); +#endif +#ifdef ST7789 +#ifdef ROTATE_SCREEN + SPI.transfer(ST77XX_MADCTL_RGB); +#else + SPI.transfer(ST77XX_MADCTL_MY | ST77XX_MADCTL_MV |ST77XX_MADCTL_RGB); +#endif +#endif + } + else { + flipped=false; +#ifdef ILI9341 + SPI.transfer(ILI9341_MADCTL_MX | ILI9341_MADCTL_MY | ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR); +#endif +#ifdef ST7789 +#ifdef ROTATE_SCREEN + SPI.transfer(ST77XX_MADCTL_MX | ST77XX_MADCTL_MY | ST77XX_MADCTL_RGB); +#else + SPI.transfer(ST77XX_MADCTL_MX | ST77XX_MADCTL_MV | ST77XX_MADCTL_RGB); +#endif +#endif + } + digitalWrite(_cs, 1); + SPI.endTransaction(); +} + +boolean TFT_T_DMA::isflipped(void) +{ + return(flipped); +} + + +#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x,HEX) + + +void TFT_T_DMA::startDMA(void) { + curTransfer = 0; + rstop = 0; + //dmatx.begin(true); + dmatx.attachInterrupt(dmaInterrupt); + setDmaStruct(); + setArea((TFT_REALWIDTH-TFT_WIDTH)/2, (TFT_REALHEIGHT-TFT_HEIGHT)/2, (TFT_REALWIDTH-TFT_WIDTH)/2 + TFT_WIDTH-1, (TFT_REALHEIGHT-TFT_HEIGHT)/2+TFT_HEIGHT-1); + fillScreen(RGBVAL16(0x00,0x00,0x00)); + + digitalWrite(_cs, HIGH); + SPI.begin(); + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0)); + + + LPSPIP_CR &= ~LPSPI_CR_MEN;//disable LPSPI: + LPSPIP_CFGR1 |= LPSPI_CFGR1_NOSTALL; //prevent stall from RX + LPSPIP_TCR = 15; // Framesize 16 Bits + //LPSPIP_FCR = 0; // Fifo Watermark + LPSPIP_DER = LPSPI_DER_TDDE; //TX DMA Request Enable + LPSPIP_CR |= LPSPI_CR_MEN; //enable LPSPI: + dmatx.triggerAtHardwareEvent( DMAMUX_SOURCE_LPSPIP_TX ); + + dmatx = dmasettings[0]; + digitalWrite(_cs, 0); + setArea((TFT_REALWIDTH-TFT_WIDTH)/2, (TFT_REALHEIGHT-TFT_HEIGHT)/2, (TFT_REALWIDTH-TFT_WIDTH)/2+TFT_WIDTH-1, (TFT_REALHEIGHT-TFT_HEIGHT)/2+TFT_HEIGHT-1); + digitalWrite(_dc, 0); + SPI.transfer(TFT_RAMWR); + digitalWrite(_dc, 1); + dmatx.enable(); +} + + +void TFT_T_DMA::stopDMA(void) { + rstop = 0; + wait(); + delay(50); + cancelled = false; + dmatx.detachInterrupt(); + fillScreen(RGBVAL16(0x00,0x00,0x00)); + SPI.end(); +#ifdef ST7789 + begin(); +#endif +#ifdef ILI9341 + SPI.begin(); + digitalWrite(_cs, 0); + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + SPI.endTransaction(); + digitalWrite(_cs, 1); + digitalWrite(_dc, 1); +#endif + setArea(0, 0, TFT_REALWIDTH-1, TFT_REALHEIGHT-1); +} + +void TFT_T_DMA::wait(void) { + rstop = 1; + unsigned long m = millis(); + cancelled = true; + while (!rstop) { + if ((millis() - m) > 100) break; + delay(10); + asm volatile("wfi"); + }; + rstop = 0; +} + +int TFT_T_DMA::get_frame_buffer_size(int *width, int *height, int *stride){ + if (width != nullptr) *width = TFT_WIDTH; + if (stride != nullptr) *stride = TFT_WIDTH; + if (height != nullptr) *height = TFT_HEIGHT; + return TFT_REALWIDTH; +} + + +/*********************************************************************************************** + Touch functions + ***********************************************************************************************/ +/* Code based on ... + * + * @file XPT2046.cpp + * @date 19.02.2016 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the XPT2046 driver for Arduino. + */ + +#define ADC_MAX 0x0fff + +void TFT_T_DMA::enableTouchIrq() +{ + SPI.beginTransaction(SPI_SETTING); + digitalWrite(_touch_cs, LOW); + const uint8_t buf[4] = { (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Y)), 0x00, 0x00, 0x00 }; + SPI.transfer((void*)&buf[0],3); + digitalWrite(_touch_cs, HIGH); + SPI.endTransaction(); +} + +//Default callibration for non flipped +#define TX_MIN 30 +#define TY_MIN 20 +#define TX_MAX 300 +#define TY_MAX 220 + +//Default callibration for flipped +#define TFX_MIN 20 +#define TFY_MIN 25 +#define TFX_MAX 288 +#define TFY_MAX 221 + +static uint16_t txMin; +static uint16_t tyMin; +static uint16_t txMax; +static uint16_t tyMax; + + +void TFT_T_DMA::callibrateTouch(uint16_t xMin,uint16_t yMin,uint16_t xMax,uint16_t yMax) { + if ( (xMin >= 0) && (yMin >= 0) && (xMax < 320) && (yMax < 200) ) { + txMin = xMin; + tyMin = yMin; + txMax = xMax; + tyMax = yMax; + } + else { + if (flipped) { + txMin = TFX_MIN; + tyMin = TFY_MIN; + txMax = TFX_MAX; + tyMax = TFY_MAX; + } + else { + txMin = TX_MIN; + tyMin = TY_MIN; + txMax = TX_MAX; + tyMax = TY_MAX; + } + } +} + + +void TFT_T_DMA::readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { + if ( TOUCH_ENABLED() ) { + uint16_t x = 0; + uint16_t y = 0; + uint16_t z1 = 0; + uint16_t z2 = 0; + uint8_t i = 0; + int16_t xraw=0, yraw=0; + + SPI.beginTransaction(SPI_SETTING); + digitalWrite(_touch_cs, LOW); + + for(; i < 15; i++) { + // SPI requirer 32bit aliment + uint8_t buf[12] = { + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Y) | XPT2046_CFG_PWR(3)), 0x00, 0x00, + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_X) | XPT2046_CFG_PWR(3)), 0x00, 0x00, + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z1)| XPT2046_CFG_PWR(3)), 0x00, 0x00, + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(3)), 0x00, 0x00 + }; + SPI.transfer(&buf[0], &buf[0], 12); + y += (buf[1] << 8 | buf[2])>>3; + x += (buf[4] << 8 | buf[5])>>3; + z1 += (buf[7] << 8 | buf[8])>>3; + z2 += (buf[10] << 8 | buf[11])>>3; + } + enableTouchIrq(); + + if(i == 0) { + *oX = 0; + *oY = 0; + *oZ = 0; + } + else { + x /= i; + y /= i; + z1 /= i; + z2 /= i; + } + + digitalWrite(_touch_cs, HIGH); + SPI.endTransaction(); + int z = z1 + ADC_MAX - z2; + if (flipped) { + xraw = x; + yraw = y; + } else { + xraw = ADC_MAX - x; + yraw = ADC_MAX - y; + } + xraw=(xraw*TFT_REALWIDTH)/(ADC_MAX+1); + yraw=(yraw*TFT_REALHEIGHT)/(ADC_MAX+1); + + *oX = xraw; + *oY = yraw; + *oZ = z; + } + else + { + *oX = 0; + *oY = 0; + *oZ = 0; + } +} + +void TFT_T_DMA::readCal(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { + readRaw(oX,oY,oZ); + // callibrate ... + if(*oX >= txMin) *oX = ((*oX - txMin)*TFT_REALWIDTH)/(txMax-txMin); + if(*oY >= tyMin) *oY = ((*oY - tyMin)*TFT_REALHEIGHT)/(tyMax-tyMin); + //Serial.print(*oX); + //Serial.print(" "); + //Serial.println(*oY); +} + + +/*********************************************************************************************** + No DMA functions + ***********************************************************************************************/ +void TFT_T_DMA::fillScreenNoDma(uint16_t color) { + + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + digitalWrite(_cs, 0); + setArea(0, 0, TFT_REALWIDTH-1, TFT_REALHEIGHT-1); + //digitalWrite(_dc, 0); + //SPI.transfer(TFT_RAMWR); + int i,j; + for (j=0; j(arx+arw)) || ((x+w)(ary+arh)) || ((y+h) arx) && (x<(arx+arw)) ) { + arw = arw - (x-arx); + arx = arx + (x-arx); + } else { + bmp_offx = arx; + } + if ( ((x+w) > arx) && ((x+w)<(arx+arw)) ) { + arw -= (arx+arw-x-w); + } + if ( (y > ary) && (y<(ary+arh)) ) { + arh = arh - (y-ary); + ary = ary + (y-ary); + } else { + bmp_offy = ary; + } + if ( ((y+h) > ary) && ((y+h)<(ary+arh)) ) { + arh -= (ary+arh-y-h); + } + } + + + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + digitalWrite(_cs, 0); + setArea(arx, ary, arx+arw-1, ary+arh-1); + //digitalWrite(_dc, 0); + //SPI.transfer(TFT_RAMWR); + + bitmap = bitmap + bmp_offy*w + bmp_offx; + for (int row=0;row> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + } + bits = *charpt++; + //digitalWrite(_dc, 1); + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + bits = bits >> 1; + if (bits&0x01) SPI.transfer16(fgcolor); + else SPI.transfer16(bgcolor); + } + x +=8; +#ifdef ILI9341 + digitalWrite(_dc, 0); + SPI.transfer(ILI9341_SLPOUT); + digitalWrite(_dc, 1); +#endif + digitalWrite(_cs, 1); + SPI.endTransaction(); + } + + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE)); + digitalWrite(_cs, 0); + setArea(0, 0, (TFT_REALWIDTH-1), (TFT_REALHEIGHT-1)); + digitalWrite(_cs, 1); + SPI.endTransaction(); +} + + + + + + +/*********************************************************************************************** + DMA functions + ***********************************************************************************************/ +uint16_t * TFT_T_DMA::getLineBuffer(int j) +{ + uint16_t * block=blocks[j>>6]; + return(&block[(j&0x3F)*TFT_REALWIDTH]); +} + +void TFT_T_DMA::writeScreen(int width, int height, int stride, uint8_t *buf, uint16_t *palette16) { + uint8_t *buffer=buf; + uint8_t *src; + + int i,j,y=0; + if (width*2 <= TFT_REALWIDTH) { + for (j=0; j>6]; + uint16_t * dst=&block[(y&0x3F)*TFT_WIDTH]; + src=buffer; + for (i=0; i>6]; + dst=&block[(y&0x3F)*TFT_WIDTH]; + src=buffer; + for (i=0; i>6]; + uint16_t * dst=&block[(y&0x3F)*TFT_WIDTH+(TFT_WIDTH-width)/2]; + src=buffer; + for (i=0; i>6]; + dst=&block[(y&0x3F)*TFT_WIDTH+(TFT_WIDTH-width)/2]; + src=buffer; + for (i=0; i>6]; + uint16_t * dst=&block[(y&0x3F)*TFT_WIDTH]; + if (width > TFT_WIDTH) { +#ifdef TFT_LINEARINT + int delta = (width/(width-TFT_WIDTH))-1; + int pos = delta; + for (int i=0; i> 8]]; + pos +=step; + } +#endif + } + else if ((width*2) == TFT_WIDTH) { + for (int i=0; i>6]; + uint16_t * dst=&block[(y&0x3F)*TFT_WIDTH]; + if (width > TFT_WIDTH) { +#ifdef TFT_LINEARINT + int delta = (width/(width-TFT_WIDTH))-1; + int pos = delta; + for (int i=0; i> 8]; + pos +=step; + } +#endif + } + else if ((width*2) == TFT_WIDTH) { + for (int i=0; i>6]; + uint16_t * dst=&block[(j&0x3F)*TFT_WIDTH]; + for (i=0; i>6]; + uint16_t * dst=&block[(l&0x3F)*TFT_WIDTH+x]; + for (i=0; i>6]; + dst=&block[(l&0x3F)*TFT_WIDTH+x]; + bits = *charpt; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + l++; + } + block=blocks[l>>6]; + dst=&block[(l&0x3F)*TFT_WIDTH+x]; + bits = *charpt++; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + bits = bits >> 1; + if (bits&0x01) *dst++=fgcolor; + else *dst++=bgcolor; + l++; + } + x +=8; + } +} + +void TFT_T_DMA::drawSprite(int16_t x, int16_t y, const uint16_t *bitmap) { + drawSprite(x,y,bitmap, 0,0,0,0); +} + +void TFT_T_DMA::drawSprite(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t arx, uint16_t ary, uint16_t arw, uint16_t arh) +{ + int bmp_offx = 0; + int bmp_offy = 0; + uint16_t *bmp_ptr; + + int w =*bitmap++; + int h = *bitmap++; + + + if ( (arw == 0) || (arh == 0) ) { + // no crop window + arx = x; + ary = y; + arw = w; + arh = h; + } + else { + if ( (x>(arx+arw)) || ((x+w)(ary+arh)) || ((y+h) arx) && (x<(arx+arw)) ) { + arw = arw - (x-arx); + arx = arx + (x-arx); + } else { + bmp_offx = arx; + } + if ( ((x+w) > arx) && ((x+w)<(arx+arw)) ) { + arw -= (arx+arw-x-w); + } + if ( (y > ary) && (y<(ary+arh)) ) { + arh = arh - (y-ary); + ary = ary + (y-ary); + } else { + bmp_offy = ary; + } + if ( ((y+h) > ary) && ((y+h)<(ary+arh)) ) { + arh -= (ary+arh-y-h); + } + } + + + int l=ary; + bitmap = bitmap + bmp_offy*w + bmp_offx; + for (int row=0;row>6]; + uint16_t * dst=&block[(l&0x3F)*TFT_WIDTH+arx]; + bmp_ptr = (uint16_t*)bitmap; + for (int col=0;col +#include +#include +#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 diff --git a/MCUME_teensy41/teensyhandy/tft_t_dma_config.h b/MCUME_teensy41/teensyhandy/tft_t_dma_config.h new file mode 100644 index 0000000..354cf74 --- /dev/null +++ b/MCUME_teensy41/teensyhandy/tft_t_dma_config.h @@ -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 + diff --git a/MCUME_teensy41/teensyhandy/vga_t_dma.h b/MCUME_teensy41/teensyhandy/vga_t_dma.h new file mode 100644 index 0000000..81a986b --- /dev/null +++ b/MCUME_teensy41/teensyhandy/vga_t_dma.h @@ -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 +#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 diff --git a/MCUME_teensy41/teensysnes/SNES_SPC.cpp b/MCUME_teensy41/teensysnes/SNES_SPC.cpp new file mode 100644 index 0000000..7f4f9b7 --- /dev/null +++ b/MCUME_teensy41/teensysnes/SNES_SPC.cpp @@ -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 + +/* 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" diff --git a/MCUME_teensy41/teensysnes/SNES_SPC.h b/MCUME_teensy41/teensysnes/SNES_SPC.h new file mode 100644 index 0000000..a086869 --- /dev/null +++ b/MCUME_teensy41/teensysnes/SNES_SPC.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 + +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 diff --git a/MCUME_teensy41/teensysnes/SNES_SPC_misc.cpp b/MCUME_teensy41/teensysnes/SNES_SPC_misc.cpp new file mode 100644 index 0000000..659377a --- /dev/null +++ b/MCUME_teensy41/teensysnes/SNES_SPC_misc.cpp @@ -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 + +/* 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 ); +} diff --git a/MCUME_teensy41/teensysnes/SPC_CPU.h b/MCUME_teensy41/teensysnes/SPC_CPU.h new file mode 100644 index 0000000..664fc48 --- /dev/null +++ b/MCUME_teensy41/teensysnes/SPC_CPU.h @@ -0,0 +1,1220 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +/* 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 */ + +//// Memory access + +#if SPC_MORE_ACCURACY + #define SUSPICIOUS_OPCODE( name ) ((void) 0) +#else + #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" ) +#endif + +#define CPU_READ( time, offset, addr )\ + cpu_read( addr, time + offset ) + +#define CPU_WRITE( time, offset, addr, data )\ + cpu_write( data, addr, time + offset ) + +#if SPC_MORE_ACCURACY + #define CPU_READ_TIMER( time, offset, addr, out )\ + { out = CPU_READ( time, offset, addr ); } + +#else + // timers are by far the most common thing read from dp + #define CPU_READ_TIMER( time, offset, addr_, out )\ + {\ + rel_time_t adj_time = time + offset;\ + int dp_addr = addr_;\ + int ti = dp_addr - (r_t0out + 0xF0);\ + if ( (unsigned) ti < timer_count )\ + {\ + Timer* t = &m.timers [ti];\ + if ( adj_time >= t->next_time )\ + t = run_timer_( t, adj_time );\ + out = t->counter;\ + t->counter = 0;\ + }\ + else\ + {\ + out = ram [dp_addr];\ + int i = dp_addr - 0xF0;\ + if ( (unsigned) i < 0x10 )\ + out = cpu_read_smp_reg( i, adj_time );\ + }\ + } +#endif + +#define TIME_ADJ( n ) (n) + +#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out ) +#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) ) +#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) ) + +#define DP_ADDR( addr ) (dp + (addr)) + +#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out ) +#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) +#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) + +#define READ_PROG16( addr ) GET_LE16( ram + (addr) ) + +#define SET_PC( n ) (pc = ram + (n)) +#define GET_PC() (pc - ram) +#define READ_PC( pc ) (*(pc)) +#define READ_PC16( pc ) GET_LE16( pc ) + +// TODO: remove non-wrapping versions? +#define SPC_NO_SP_WRAPAROUND 0 + +#define SET_SP( v ) (sp = ram + 0x101 + (v)) +#define GET_SP() (sp - 0x101 - ram) + +#if SPC_NO_SP_WRAPAROUND +#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) +#define PUSH( v ) (void) (*--sp = (uint8_t) (v)) +#define POP( out ) (void) ((out) = *sp++) + +#else +#define PUSH16( data )\ +{\ + int addr = (sp -= 2) - ram;\ + if ( addr > 0x100 )\ + {\ + SET_LE16( sp, data );\ + }\ + else\ + {\ + ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ + sp [1] = (uint8_t) (data >> 8);\ + sp += 0x100;\ + }\ +} + +#define PUSH( data )\ +{\ + *--sp = (uint8_t) (data);\ + if ( sp - ram == 0x100 )\ + sp += 0x100;\ +} + +#define POP( out )\ +{\ + out = *sp++;\ + if ( sp - ram == 0x201 )\ + {\ + out = sp [-0x101];\ + sp -= 0x100;\ + }\ +} + +#endif + +#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) + +unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time ) +{ + unsigned addr = READ_PC16( pc ); + unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); + return t << 8 & 0x100; +} + +//// Status flag handling + +// Hex value in name to clarify code and bit shifting. +// Flag stored in indicated variable during emulation +int const n80 = 0x80; // nz +int const v40 = 0x40; // psw +int const p20 = 0x20; // dp +int const b10 = 0x10; // psw +int const h08 = 0x08; // psw +int const i04 = 0x04; // psw +int const z02 = 0x02; // nz +int const c01 = 0x01; // c + +int const nz_neg_mask = 0x880; // either bit set indicates N flag set + +#define GET_PSW( out )\ +{\ + out = psw & ~(n80 | p20 | z02 | c01);\ + out |= c >> 8 & c01;\ + out |= dp >> 3 & p20;\ + out |= ((nz >> 4) | nz) & n80;\ + if ( !(uint8_t) nz ) out |= z02;\ +} + +#define SET_PSW( in )\ +{\ + psw = in;\ + c = in << 8;\ + dp = in << 3 & 0x100;\ + nz = (in << 4 & 0x800) | (~in & z02);\ +} + +SPC_CPU_RUN_FUNC +{ + uint8_t* const ram = RAM; + int a = m.cpu_regs.a; + int x = m.cpu_regs.x; + int y = m.cpu_regs.y; + uint8_t const* pc; + uint8_t* sp; + int psw; + int c; + int nz; + int dp; + + SET_PC( m.cpu_regs.pc ); + SET_SP( m.cpu_regs.sp ); + SET_PSW( m.cpu_regs.psw ); + + goto loop; + + + // Main loop + +cbranch_taken_loop: + pc += *(BOOST::int8_t const*) pc; +inc_pc_loop: + pc++; +loop: +{ + unsigned opcode; + unsigned data; + + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + opcode = *pc; + if ( (rel_time += m.cycle_table [opcode]) > 0 ) + goto out_of_time; + + #ifdef SPC_CPU_OPCODE_HOOK + SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); + #endif + /* + //SUB_CASE_COUNTER( 1 ); + #define PROFILE_TIMER_LOOP( op, addr, len )\ + if ( opcode == op )\ + {\ + int cond = (unsigned) ((addr) - 0xFD) < 3 &&\ + pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ + SUB_CASE_COUNTER( op && cond );\ + } + + PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); + PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); + PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); + */ + + // TODO: if PC is at end of memory, this will get wrong operand (very obscure) + data = *++pc; + switch ( opcode ) + { + +// Common instructions + +#define BRANCH( cond )\ +{\ + pc++;\ + pc += (BOOST::int8_t) data;\ + if ( cond )\ + goto loop;\ + pc -= (BOOST::int8_t) data;\ + rel_time -= 2;\ + goto loop;\ +} + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ) // 89% taken + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ) + + case 0x3F:{// CALL + int old_addr = GET_PC() + 2; + SET_PC( READ_PC16( pc ) ); + PUSH16( old_addr ); + goto loop; + } + + case 0x6F:// RET + #if SPC_NO_SP_WRAPAROUND + { + SET_PC( GET_LE16( sp ) ); + sp += 2; + } + #else + { + int addr = sp - ram; + SET_PC( GET_LE16( sp ) ); + sp += 2; + if ( addr < 0x1FF ) + goto loop; + + SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); + sp -= 0x100; + } + #endif + goto loop; + + case 0xE4: // MOV a,dp + ++pc; + // 80% from timer + READ_DP_TIMER( 0, data, a = nz ); + goto loop; + + case 0xFA:{// MOV dp,dp + int temp; + READ_DP_TIMER( -2, data, temp ); + data = temp + no_read_before_write ; + } + // fall through + case 0x8F:{// MOV dp,#imm + int temp = READ_PC( pc + 1 ); + pc += 2; + + #if !SPC_MORE_ACCURACY + { + int i = dp + temp; + ram [i] = (uint8_t) data; + i -= 0xF0; + if ( (unsigned) i < 0x10 ) // 76% + { + REGS [i] = (uint8_t) data; + + // Registers other than $F2 and $F4-$F7 + //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) + if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12% + cpu_write_smp_reg( data, rel_time, i ); + } + } + #else + WRITE_DP( 0, temp, data ); + #endif + goto loop; + } + + case 0xC4: // MOV dp,a + ++pc; + #if !SPC_MORE_ACCURACY + { + int i = dp + data; + ram [i] = (uint8_t) a; + i -= 0xF0; + if ( (unsigned) i < 0x10 ) // 39% + { + unsigned sel = i - 2; + REGS [i] = (uint8_t) a; + + if ( sel == 1 ) // 51% $F3 + dsp_write( a, rel_time ); + else if ( sel > 1 ) // 1% not $F2 or $F3 + cpu_write_smp_reg_( a, rel_time, i ); + } + } + #else + WRITE_DP( 0, data, a ); + #endif + goto loop; + +#define CASE( n ) case n: + +// Define common address modes based on opcode for immediate mode. Execution +// ends with data set to the address of the operand. +#define ADDR_MODES_( op )\ + CASE( op - 0x02 ) /* (X) */\ + data = x + dp;\ + pc--;\ + goto end_##op;\ + CASE( op + 0x0F ) /* (dp)+Y */\ + data = READ_PROG16( data + dp ) + y;\ + goto end_##op;\ + CASE( op - 0x01 ) /* (dp+X) */\ + data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ + goto end_##op;\ + CASE( op + 0x0E ) /* abs+Y */\ + data += y;\ + goto abs_##op;\ + CASE( op + 0x0D ) /* abs+X */\ + data += x;\ + CASE( op - 0x03 ) /* abs */\ + abs_##op:\ + data += 0x100 * READ_PC( ++pc );\ + goto end_##op;\ + CASE( op + 0x0C ) /* dp+X */\ + data = (uint8_t) (data + x); + +#define ADDR_MODES_NO_DP( op )\ + ADDR_MODES_( op )\ + data += dp;\ + end_##op: + +#define ADDR_MODES( op )\ + ADDR_MODES_( op )\ + CASE( op - 0x04 ) /* dp */\ + data += dp;\ + end_##op: + +// 1. 8-bit Data Transmission Commands. Group I + + ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr + a = nz = READ( 0, data ); + goto inc_pc_loop; + + case 0xBF:{// MOV A,(X)+ + int temp = x + dp; + x = (uint8_t) (x + 1); + a = nz = READ( -1, temp ); + goto loop; + } + + case 0xE8: // MOV A,imm + a = data; + nz = data; + goto inc_pc_loop; + + case 0xF9: // MOV X,dp+Y + data = (uint8_t) (data + y); + case 0xF8: // MOV X,dp + READ_DP_TIMER( 0, data, x = nz ); + goto inc_pc_loop; + + case 0xE9: // MOV X,abs + data = READ_PC16( pc ); + ++pc; + data = READ( 0, data ); + case 0xCD: // MOV X,imm + x = data; + nz = data; + goto inc_pc_loop; + + case 0xFB: // MOV Y,dp+X + data = (uint8_t) (data + x); + case 0xEB: // MOV Y,dp + // 70% from timer + pc++; + READ_DP_TIMER( 0, data, y = nz ); + goto loop; + + case 0xEC:{// MOV Y,abs + int temp = READ_PC16( pc ); + pc += 2; + READ_TIMER( 0, temp, y = nz ); + //y = nz = READ( 0, temp ); + goto loop; + } + + case 0x8D: // MOV Y,imm + y = data; + nz = data; + goto inc_pc_loop; + +// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 + + ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A + WRITE( 0, data, a ); + goto inc_pc_loop; + + { + int temp; + case 0xCC: // MOV abs,Y + temp = y; + goto mov_abs_temp; + case 0xC9: // MOV abs,X + temp = x; + mov_abs_temp: + WRITE( 0, READ_PC16( pc ), temp ); + pc += 2; + goto loop; + } + + case 0xD9: // MOV dp+Y,X + data = (uint8_t) (data + y); + case 0xD8: // MOV dp,X + WRITE( 0, data + dp, x ); + goto inc_pc_loop; + + case 0xDB: // MOV dp+X,Y + data = (uint8_t) (data + x); + case 0xCB: // MOV dp,Y + WRITE( 0, data + dp, y ); + goto inc_pc_loop; + +// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. + + case 0x7D: // MOV A,X + a = x; + nz = x; + goto loop; + + case 0xDD: // MOV A,Y + a = y; + nz = y; + goto loop; + + case 0x5D: // MOV X,A + x = a; + nz = a; + goto loop; + + case 0xFD: // MOV Y,A + y = a; + nz = a; + goto loop; + + case 0x9D: // MOV X,SP + x = nz = GET_SP(); + goto loop; + + case 0xBD: // MOV SP,X + SET_SP( x ); + goto loop; + + //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) + + case 0xAF: // MOV (X)+,A + WRITE_DP( 0, x, a + no_read_before_write ); + x++; + goto loop; + +// 5. 8-BIT LOGIC OPERATION COMMANDS + +#define LOGICAL_OP( op, func )\ + ADDR_MODES( op ) /* addr */\ + data = READ( 0, data );\ + case op: /* imm */\ + nz = a func##= data;\ + goto inc_pc_loop;\ + { unsigned addr;\ + case op + 0x11: /* X,Y */\ + data = READ_DP( -2, y );\ + addr = x + dp;\ + goto addr_##op;\ + case op + 0x01: /* dp,dp */\ + data = READ_DP( -3, data );\ + case op + 0x10:{/*dp,imm*/\ + uint8_t const* addr2 = pc + 1;\ + pc += 2;\ + addr = READ_PC( addr2 ) + dp;\ + }\ + addr_##op:\ + nz = data func READ( -1, addr );\ + WRITE( 0, addr, nz );\ + goto loop;\ + } + + LOGICAL_OP( 0x28, & ); // AND + + LOGICAL_OP( 0x08, | ); // OR + + LOGICAL_OP( 0x48, ^ ); // EOR + +// 4. 8-BIT ARITHMETIC OPERATION COMMANDS + + ADDR_MODES( 0x68 ) // CMP addr + data = READ( 0, data ); + case 0x68: // CMP imm + nz = a - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x79: // CMP (X),(Y) + data = READ_DP( -2, y ); + nz = READ_DP( -1, x ) - data; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x69: // CMP dp,dp + data = READ_DP( -3, data ); + case 0x78: // CMP dp,imm + nz = READ_DP( -1, READ_PC( ++pc ) ) - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x3E: // CMP X,dp + data += dp; + goto cmp_x_addr; + case 0x1E: // CMP X,abs + data = READ_PC16( pc ); + pc++; + cmp_x_addr: + data = READ( 0, data ); + case 0xC8: // CMP X,imm + nz = x - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x7E: // CMP Y,dp + data += dp; + goto cmp_y_addr; + case 0x5E: // CMP Y,abs + data = READ_PC16( pc ); + pc++; + cmp_y_addr: + data = READ( 0, data ); + case 0xAD: // CMP Y,imm + nz = y - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + { + int addr; + case 0xB9: // SBC (x),(y) + case 0x99: // ADC (x),(y) + pc--; // compensate for inc later + data = READ_DP( -2, y ); + addr = x + dp; + goto adc_addr; + case 0xA9: // SBC dp,dp + case 0x89: // ADC dp,dp + data = READ_DP( -3, data ); + case 0xB8: // SBC dp,imm + case 0x98: // ADC dp,imm + addr = READ_PC( ++pc ) + dp; + adc_addr: + nz = READ( -1, addr ); + goto adc_data; + +// catch ADC and SBC together, then decode later based on operand +#undef CASE +#define CASE( n ) case n: case (n) + 0x20: + ADDR_MODES( 0x88 ) // ADC/SBC addr + data = READ( 0, data ); + case 0xA8: // SBC imm + case 0x88: // ADC imm + addr = -1; // A + nz = a; + adc_data: { + int flags; + if ( opcode >= 0xA0 ) // SBC + data ^= 0xFF; + + flags = data ^ nz; + nz += data + (c >> 8 & 1); + flags ^= nz; + + psw = (psw & ~(v40 | h08)) | + (flags >> 1 & h08) | + ((flags + 0x80) >> 2 & v40); + c = nz; + if ( addr < 0 ) + { + a = (uint8_t) nz; + goto inc_pc_loop; + } + WRITE( 0, addr, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + } + + } + +// 6. ADDITION & SUBTRACTION COMMANDS + +#define INC_DEC_REG( reg, op )\ + nz = reg op;\ + reg = (uint8_t) nz;\ + goto loop; + + case 0xBC: INC_DEC_REG( a, + 1 ) // INC A + case 0x3D: INC_DEC_REG( x, + 1 ) // INC X + case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y + + case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A + case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X + case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y + + case 0x9B: // DEC dp+X + case 0xBB: // INC dp+X + data = (uint8_t) (data + x); + case 0x8B: // DEC dp + case 0xAB: // INC dp + data += dp; + goto inc_abs; + case 0x8C: // DEC abs + case 0xAC: // INC abs + data = READ_PC16( pc ); + pc++; + inc_abs: + nz = (opcode >> 4 & 2) - 1; + nz += READ( -1, data ); + WRITE( 0, data, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + +// 7. SHIFT, ROTATION COMMANDS + + case 0x5C: // LSR A + c = 0; + case 0x7C:{// ROR A + nz = (c >> 1 & 0x80) | (a >> 1); + c = a << 8; + a = nz; + goto loop; + } + + case 0x1C: // ASL A + c = 0; + case 0x3C:{// ROL A + int temp = c >> 8 & 1; + c = a << 1; + nz = c | temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x0B: // ASL dp + c = 0; + data += dp; + goto rol_mem; + case 0x1B: // ASL dp+X + c = 0; + case 0x3B: // ROL dp+X + data = (uint8_t) (data + x); + case 0x2B: // ROL dp + data += dp; + goto rol_mem; + case 0x0C: // ASL abs + c = 0; + case 0x2C: // ROL abs + data = READ_PC16( pc ); + pc++; + rol_mem: + nz = c >> 8 & 1; + nz |= (c = READ( -1, data ) << 1); + WRITE( 0, data, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + + case 0x4B: // LSR dp + c = 0; + data += dp; + goto ror_mem; + case 0x5B: // LSR dp+X + c = 0; + case 0x7B: // ROR dp+X + data = (uint8_t) (data + x); + case 0x6B: // ROR dp + data += dp; + goto ror_mem; + case 0x4C: // LSR abs + c = 0; + case 0x6C: // ROR abs + data = READ_PC16( pc ); + pc++; + ror_mem: { + int temp = READ( -1, data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + WRITE( 0, data, nz ); + goto inc_pc_loop; + } + + case 0x9F: // XCN + nz = a = (a >> 4) | (uint8_t) (a << 4); + goto loop; + +// 8. 16-BIT TRANSMISION COMMANDS + + case 0xBA: // MOVW YA,dp + a = READ_DP( -2, data ); + nz = (a & 0x7F) | (a >> 1); + y = READ_DP( 0, (uint8_t) (data + 1) ); + nz |= y; + goto inc_pc_loop; + + case 0xDA: // MOVW dp,YA + WRITE_DP( -1, data, a ); + WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); + goto inc_pc_loop; + +// 9. 16-BIT OPERATION COMMANDS + + case 0x3A: // INCW dp + case 0x1A:{// DECW dp + int temp; + // low byte + data += dp; + temp = READ( -3, data ); + temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW + nz = ((temp >> 1) | temp) & 0x7F; + WRITE( -2, data, /*(uint8_t)*/ temp ); + + // high byte + data = (uint8_t) (data + 1) + dp; + temp = (uint8_t) ((temp >> 8) + READ( -1, data )); + nz |= temp; + WRITE( 0, data, temp ); + + goto inc_pc_loop; + } + + case 0x7A: // ADDW YA,dp + case 0x9A:{// SUBW YA,dp + int lo = READ_DP( -2, data ); + int hi = READ_DP( 0, (uint8_t) (data + 1) ); + int result; + int flags; + + if ( opcode == 0x9A ) // SUBW + { + lo = (lo ^ 0xFF) + 1; + hi ^= 0xFF; + } + + lo += a; + result = y + hi + (lo >> 8); + flags = hi ^ y ^ result; + + psw = (psw & ~(v40 | h08)) | + (flags >> 1 & h08) | + ((flags + 0x80) >> 2 & v40); + c = result; + a = (uint8_t) lo; + result = (uint8_t) result; + y = result; + nz = (((lo >> 1) | lo) & 0x7F) | result; + + goto inc_pc_loop; + } + + case 0x5A: { // CMPW YA,dp + int temp = a - READ_DP( -1, data ); + nz = ((temp >> 1) | temp) & 0x7F; + temp = y + (temp >> 8); + temp -= READ_DP( 0, (uint8_t) (data + 1) ); + nz |= temp; + c = ~temp; + nz &= 0xFF; + goto inc_pc_loop; + } + +// 10. MULTIPLICATION & DIVISON COMMANDS + + case 0xCF: { // MUL YA + unsigned temp = y * a; + a = (uint8_t) temp; + nz = ((temp >> 1) | temp) & 0x7F; + y = temp >> 8; + nz |= y; + goto loop; + } + + case 0x9E: // DIV YA,X + { + unsigned ya = y * 0x100 + a; + + psw &= ~(h08 | v40); + + if ( y >= x ) + psw |= v40; + + if ( (y & 15) >= (x & 15) ) + psw |= h08; + + if ( y < x * 2 ) + { + a = ya / x; + y = ya - a * x; + } + else + { + a = 255 - (ya - x * 0x200) / (256 - x); + y = x + (ya - x * 0x200) % (256 - x); + } + + nz = (uint8_t) a; + a = (uint8_t) a; + + goto loop; + } + +// 11. DECIMAL COMPENSATION COMMANDS + + case 0xDF: // DAA + SUSPICIOUS_OPCODE( "DAA" ); + if ( a > 0x99 || c & 0x100 ) + { + a += 0x60; + c = 0x100; + } + + if ( (a & 0x0F) > 9 || psw & h08 ) + a += 0x06; + + nz = a; + a = (uint8_t) a; + goto loop; + + case 0xBE: // DAS + SUSPICIOUS_OPCODE( "DAS" ); + if ( a > 0x99 || !(c & 0x100) ) + { + a -= 0x60; + c = 0; + } + + if ( (a & 0x0F) > 9 || !(psw & h08) ) + a -= 0x06; + + nz = a; + a = (uint8_t) a; + goto loop; + +// 12. BRANCHING COMMANDS + + case 0x2F: // BRA rel + pc += (BOOST::int8_t) data; + goto inc_pc_loop; + + case 0x30: // BMI + BRANCH( (nz & nz_neg_mask) ) + + case 0x10: // BPL + BRANCH( !(nz & nz_neg_mask) ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + + case 0x70: // BVS + BRANCH( psw & v40 ) + + case 0x50: // BVC + BRANCH( !(psw & v40) ) + + #define CBRANCH( cond )\ + {\ + pc++;\ + if ( cond )\ + goto cbranch_taken_loop;\ + rel_time -= 2;\ + goto inc_pc_loop;\ + } + + case 0x03: // BBS dp.bit,rel + case 0x23: + case 0x43: + case 0x63: + case 0x83: + case 0xA3: + case 0xC3: + case 0xE3: + CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) + + case 0x13: // BBC dp.bit,rel + case 0x33: + case 0x53: + case 0x73: + case 0x93: + case 0xB3: + case 0xD3: + case 0xF3: + CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) + + case 0xDE: // CBNE dp+X,rel + data = (uint8_t) (data + x); + // fall through + case 0x2E:{// CBNE dp,rel + int temp; + // 61% from timer + READ_DP_TIMER( -4, data, temp ); + CBRANCH( temp != a ) + } + + case 0x6E: { // DBNZ dp,rel + unsigned temp = READ_DP( -4, data ) - 1; + WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); + CBRANCH( temp ) + } + + case 0xFE: // DBNZ Y,rel + y = (uint8_t) (y - 1); + BRANCH( y ) + + case 0x1F: // JMP [abs+X] + SET_PC( READ_PC16( pc ) + x ); + // fall through + case 0x5F: // JMP abs + SET_PC( READ_PC16( pc ) ); + goto loop; + +// 13. SUB-ROUTINE CALL RETURN COMMANDS + + case 0x0F:{// BRK + int temp; + int ret_addr = GET_PC(); + SUSPICIOUS_OPCODE( "BRK" ); + SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified + PUSH16( ret_addr ); + GET_PSW( temp ); + psw = (psw | b10) & ~i04; + PUSH( temp ); + goto loop; + } + + case 0x4F:{// PCALL offset + int ret_addr = GET_PC() + 1; + SET_PC( 0xFF00 | data ); + PUSH16( ret_addr ); + goto loop; + } + + case 0x01: // TCALL n + case 0x11: + case 0x21: + case 0x31: + case 0x41: + case 0x51: + case 0x61: + case 0x71: + case 0x81: + case 0x91: + case 0xA1: + case 0xB1: + case 0xC1: + case 0xD1: + case 0xE1: + case 0xF1: { + int ret_addr = GET_PC(); + SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); + PUSH16( ret_addr ); + goto loop; + } + +// 14. STACK OPERATION COMMANDS + + { + int temp; + case 0x7F: // RET1 + temp = *sp; + SET_PC( GET_LE16( sp + 1 ) ); + sp += 3; + goto set_psw; + case 0x8E: // POP PSW + POP( temp ); + set_psw: + SET_PSW( temp ); + goto loop; + } + + case 0x0D: { // PUSH PSW + int temp; + GET_PSW( temp ); + PUSH( temp ); + goto loop; + } + + case 0x2D: // PUSH A + PUSH( a ); + goto loop; + + case 0x4D: // PUSH X + PUSH( x ); + goto loop; + + case 0x6D: // PUSH Y + PUSH( y ); + goto loop; + + case 0xAE: // POP A + POP( a ); + goto loop; + + case 0xCE: // POP X + POP( x ); + goto loop; + + case 0xEE: // POP Y + POP( y ); + goto loop; + +// 15. BIT OPERATION COMMANDS + + case 0x02: // SET1 + case 0x22: + case 0x42: + case 0x62: + case 0x82: + case 0xA2: + case 0xC2: + case 0xE2: + case 0x12: // CLR1 + case 0x32: + case 0x52: + case 0x72: + case 0x92: + case 0xB2: + case 0xD2: + case 0xF2: { + int bit = 1 << (opcode >> 5); + int mask = ~bit; + if ( opcode & 0x10 ) + bit = 0; + data += dp; + WRITE( 0, data, (READ( -1, data ) & mask) | bit ); + goto inc_pc_loop; + } + + case 0x0E: // TSET1 abs + case 0x4E: // TCLR1 abs + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -2, data ); + nz = (uint8_t) (a - temp); + temp &= ~a; + if ( opcode == 0x0E ) + temp |= a; + WRITE( 0, data, temp ); + } + goto loop; + + case 0x4A: // AND1 C,mem.bit + c &= MEM_BIT( 0 ); + pc += 2; + goto loop; + + case 0x6A: // AND1 C,/mem.bit + c &= ~MEM_BIT( 0 ); + pc += 2; + goto loop; + + case 0x0A: // OR1 C,mem.bit + c |= MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0x2A: // OR1 C,/mem.bit + c |= ~MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0x8A: // EOR1 C,mem.bit + c ^= MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0xEA: // NOT1 mem.bit + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -1, data & 0x1FFF ); + temp ^= 1 << (data >> 13); + WRITE( 0, data & 0x1FFF, temp ); + } + goto loop; + + case 0xCA: // MOV1 mem.bit,C + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -2, data & 0x1FFF ); + unsigned bit = data >> 13; + temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); + WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); + } + goto loop; + + case 0xAA: // MOV1 C,mem.bit + c = MEM_BIT( 0 ); + pc += 2; + goto loop; + +// 16. PROGRAM PSW FLAG OPERATION COMMANDS + + case 0x60: // CLRC + c = 0; + goto loop; + + case 0x80: // SETC + c = ~0; + goto loop; + + case 0xED: // NOTC + c ^= 0x100; + goto loop; + + case 0xE0: // CLRV + psw &= ~(v40 | h08); + goto loop; + + case 0x20: // CLRP + dp = 0; + goto loop; + + case 0x40: // SETP + dp = 0x100; + goto loop; + + case 0xA0: // EI + SUSPICIOUS_OPCODE( "EI" ); + psw |= i04; + goto loop; + + case 0xC0: // DI + SUSPICIOUS_OPCODE( "DI" ); + psw &= ~i04; + goto loop; + +// 17. OTHER COMMANDS + + case 0x00: // NOP + goto loop; + + case 0xFF:{// STOP + // handle PC wrap-around + unsigned addr = GET_PC() - 1; + if ( addr >= 0x10000 ) + { + addr &= 0xFFFF; + SET_PC( addr ); + dprintf( "SPC: PC wrapped around\n" ); + goto loop; + } + } + // fall through + case 0xEF: // SLEEP + SUSPICIOUS_OPCODE( "STOP/SLEEP" ); + --pc; + rel_time = 0; + m.cpu_error = "SPC emulation error"; + goto stop; + } // switch + + assert( 0 ); // catch any unhandled instructions +} +out_of_time: + rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode +stop: + + // Uncache registers + if ( GET_PC() >= 0x10000 ) + dprintf( "SPC: PC wrapped around\n" ); + m.cpu_regs.pc = (uint16_t) GET_PC(); + m.cpu_regs.sp = ( uint8_t) GET_SP(); + m.cpu_regs.a = ( uint8_t) a; + m.cpu_regs.x = ( uint8_t) x; + m.cpu_regs.y = ( uint8_t) y; + { + int temp; + GET_PSW( temp ); + m.cpu_regs.psw = (uint8_t) temp; + } +} +SPC_CPU_RUN_FUNC_END diff --git a/MCUME_teensy41/teensysnes/SPC_DSP.cpp b/MCUME_teensy41/teensysnes/SPC_DSP.cpp new file mode 100644 index 0000000..b0d9747 --- /dev/null +++ b/MCUME_teensy41/teensysnes/SPC_DSP.cpp @@ -0,0 +1,1061 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ +#include "SPC_DSP.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 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" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#if INT_MAX < 0x7FFFFFFF + #error "Requires that int type have at least 32 bits" +#endif + +// TODO: add to blargg_endian.h +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = +{ + 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, + 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, + 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, + 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, + 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, + 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, + 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, + 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF +}; + +// if ( io < -32768 ) io = -32768; +// if ( io > 32767 ) io = 32767; +#define CLAMP16( io )\ +{\ + if ( (int16_t) io != io )\ + io = (io >> 31) ^ 0x7FFF;\ +} + +// Access global DSP register +#define REG(n) m.regs [r_##n] + +// Access voice DSP register +#define VREG(r,n) r [v_##n] + +#define WRITE_SAMPLES( l, r, out ) \ +{\ + out [0] = l;\ + out [1] = r;\ + out += 2;\ + if ( out >= m.out_end )\ + {\ + check( out == m.out_end );\ + check( m.out_end != &m.extra [extra_size] || \ + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ + out = m.extra;\ + m.out_end = &m.extra [extra_size];\ + }\ +}\ + +void SPC_DSP::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // must be even + if ( !out ) + { + out = m.extra; + size = extra_size; + } + m.out_begin = out; + m.out = out; + m.out_end = out + size; +} + +// Volume registers and efb are signed! Easy to forget int8_t cast. +// Prefixes are to avoid accidental use of locals with same names. + +// Gaussian interpolation + +static short const gauss [512] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, + 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, + 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, + 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, + 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, + 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, + 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, + 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, + 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, + 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, + 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, + 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, + 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, + 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, + 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, + 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, + 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, + 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, +1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, +1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, +1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, +1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, +1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, +1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, +1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, +}; + +inline int SPC_DSP::interpolate( voice_t const* v ) +{ + // Make pointers into gaussian based on fractional position between samples + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = gauss + 255 - offset; + short const* rev = gauss + offset; // mirror left half of gaussian + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = (fwd [ 0] * in [0]) >> 11; + out += (fwd [256] * in [1]) >> 11; + out += (rev [256] * in [2]) >> 11; + out = (int16_t) out; + out += (rev [ 0] * in [3]) >> 11; + + CLAMP16( out ); + out &= ~1; + return out; +} + + +//// Counters + +int const simple_counter_range = 2048 * 5 * 3; // 30720 + +static unsigned const counter_rates [32] = +{ + simple_counter_range + 1, // never fires + 2048, 1536, + 1280, 1024, 768, + 640, 512, 384, + 320, 256, 192, + 160, 128, 96, + 80, 64, 48, + 40, 32, 24, + 20, 16, 12, + 10, 8, 6, + 5, 4, 3, + 2, + 1 +}; + +static unsigned const counter_offsets [32] = +{ + 1, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 0, + 0 +}; + +inline void SPC_DSP::init_counter() +{ + m.counter = 0; +} + +inline void SPC_DSP::run_counters() +{ + if ( --m.counter < 0 ) + m.counter = simple_counter_range - 1; +} + +inline unsigned SPC_DSP::read_counter( int rate ) +{ + return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; +} + + +//// Envelope + +inline void SPC_DSP::run_envelope( voice_t* const v ) +{ + int env = v->env; + if ( v->env_mode == env_release ) // 60% + { + if ( (env -= 0x8) < 0 ) + env = 0; + v->env = env; + } + else + { + int rate; + int env_data = VREG(v->regs,adsr1); + if ( m.t_adsr0 & 0x80 ) // 99% ADSR + { + if ( v->env_mode >= env_decay ) // 99% + { + env--; + env -= env >> 8; + rate = env_data & 0x1F; + if ( v->env_mode == env_decay ) // 1% + rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; + } + else // env_attack + { + rate = (m.t_adsr0 & 0x0F) * 2 + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } + else // GAIN + { + int mode; + env_data = VREG(v->regs,gain); + mode = env_data >> 5; + if ( mode < 4 ) // direct + { + env = env_data * 0x10; + rate = 31; + } + else + { + rate = env_data & 0x1F; + if ( mode == 4 ) // 4: linear decrease + { + env -= 0x20; + } + else if ( mode < 6 ) // 5: exponential decrease + { + env--; + env -= env >> 8; + } + else // 6,7: linear increase + { + env += 0x20; + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) + env += 0x8 - 0x20; // 7: two-slope linear increase + } + } + } + + // Sustain level + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) + v->env_mode = env_sustain; + + v->hidden_env = env; + + // unsigned cast because linear decrease going negative also triggers this + if ( (unsigned) env > 0x7FF ) + { + env = (env < 0 ? 0 : 0x7FF); + if ( v->env_mode == env_attack ) + v->env_mode = env_decay; + } + + if ( !read_counter( rate ) ) + v->env = env; // nothing else is controlled by the counter + } +} + + +//// BRR Decoding + +inline void SPC_DSP::decode_brr( voice_t* v ) +{ + // Arrange the four input nybbles in 0xABCD order for easy decoding + int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; + + int const header = m.t_brr_header; + + // Write to next four samples in circular buffer + int* pos = &v->buf [v->buf_pos]; + int* end; + if ( (v->buf_pos += 4) >= brr_buf_size ) + v->buf_pos = 0; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract nybble and sign-extend + int s = (int16_t) nybbles >> 12; + + // Shift sample based on header + int const shift = header >> 4; + s = (s << shift) >> 1; + if ( shift >= 0xD ) // handle invalid range + s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) + + // Apply IIR filter (8 is the most commonly used) + int const filter = header & 0x0C; + int const p1 = pos [brr_buf_size - 1]; + int const p2 = pos [brr_buf_size - 2] >> 1; + if ( filter >= 8 ) + { + s += p1; + s -= p2; + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 + { + s += p2 >> 4; + s += (p1 * -3) >> 6; + } + else // s += p1 * 0.8984375 - p2 * 0.40625 + { + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } + } + else if ( filter ) // s += p1 * 0.46875 + { + s += p1 >> 1; + s += (-p1) >> 5; + } + + // Adjust and write sample + CLAMP16( s ); + s = (int16_t) (s * 2); + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around + } +} + + +//// Misc + +#define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n() + +MISC_CLOCK( 27 ) +{ + m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON +} +MISC_CLOCK( 28 ) +{ + m.t_non = REG(non); + m.t_eon = REG(eon); + m.t_dir = REG(dir); +} +MISC_CLOCK( 29 ) +{ + if ( (m.every_other_sample ^= 1) != 0 ) + m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read +} +MISC_CLOCK( 30 ) +{ + if ( m.every_other_sample ) + { + m.kon = m.new_kon; + m.t_koff = REG(koff) | m.mute_mask; + } + + run_counters(); + + // Noise + if ( !read_counter( REG(flg) & 0x1F ) ) + { + int feedback = (m.noise << 13) ^ (m.noise << 14); + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); + } +} + + +//// Voices + +#define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v ) + +inline VOICE_CLOCK( V1 ) +{ + m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; + m.t_srcn = VREG(v->regs,srcn); +} +inline VOICE_CLOCK( V2 ) +{ + // Read sample pointer (ignored if not needed) + uint8_t const* entry = &m.ram [m.t_dir_addr]; + if ( !v->kon_delay ) + entry += 2; + m.t_brr_next_addr = GET_LE16A( entry ); + + m.t_adsr0 = VREG(v->regs,adsr0); + + // Read pitch, spread over two clocks + m.t_pitch = VREG(v->regs,pitchl); +} +inline VOICE_CLOCK( V3a ) +{ + m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; +} +inline VOICE_CLOCK( V3b ) +{ + // Read BRR header and byte + m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; + m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking +} +VOICE_CLOCK( V3c ) +{ + // Pitch modulation using previous voice's output + if ( m.t_pmon & v->vbit ) + m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; + + if ( v->kon_delay ) + { + // Get ready to start BRR decoding on next sample + if ( v->kon_delay == 5 ) + { + v->brr_addr = m.t_brr_next_addr; + v->brr_offset = 1; + v->buf_pos = 0; + m.t_brr_header = 0; // header is ignored on this sample + m.kon_check = true; + + if (take_spc_snapshot) + { + take_spc_snapshot = 0; + if (spc_snapshot_callback) + spc_snapshot_callback(); + } + } + + // Envelope is never run during KON + v->env = 0; + v->hidden_env = 0; + + // Disable BRR decoding until last three samples + v->interp_pos = 0; + if ( --v->kon_delay & 3 ) + v->interp_pos = 0x4000; + + // Pitch is never added during KON + m.t_pitch = 0; + } + + // Gaussian interpolation + { + int output = interpolate( v ); + + // Noise + if ( m.t_non & v->vbit ) + output = (int16_t) (m.noise * 2); + + // Apply envelope + m.t_output = (output * v->env) >> 11 & ~1; + v->t_envx_out = (uint8_t) (v->env >> 4); + } + + // Immediate silence due to end of sample or soft reset + if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) + { + v->env_mode = env_release; + v->env = 0; + } + + if ( m.every_other_sample ) + { + // KOFF + if ( m.t_koff & v->vbit ) + v->env_mode = env_release; + + // KON + if ( m.kon & v->vbit ) + { + v->kon_delay = 5; + v->env_mode = env_attack; + } + } + + // Run envelope for next sample + if ( !v->kon_delay ) + run_envelope( v ); +} + +inline void SPC_DSP::voice_output( voice_t const* v, int ch ) +{ + // Apply left/right volume + int amp = (m.t_output * (int8_t) VREG(v->regs,voll + ch)) >> 7; + amp *= ((stereo_switch & (1 << (v->voice_number + ch * voice_count))) ? 1 : 0); + + // Add to output total + m.t_main_out [ch] += amp; + CLAMP16( m.t_main_out [ch] ); + + // Optionally add to echo total + if ( m.t_eon & v->vbit ) + { + m.t_echo_out [ch] += amp; + CLAMP16( m.t_echo_out [ch] ); + } +} +VOICE_CLOCK( V4 ) +{ + // Decode BRR + m.t_looped = 0; + if ( v->interp_pos >= 0x4000 ) + { + decode_brr( v ); + + if ( (v->brr_offset += 2) >= brr_block_size ) + { + // Start decoding next BRR block + assert( v->brr_offset == brr_block_size ); + v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; + if ( m.t_brr_header & 1 ) + { + v->brr_addr = m.t_brr_next_addr; + m.t_looped = v->vbit; + } + v->brr_offset = 1; + } + } + + // Apply pitch + v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; + + // Keep from getting too far ahead (when using pitch modulation) + if ( v->interp_pos > 0x7FFF ) + v->interp_pos = 0x7FFF; + + // Output left + voice_output( v, 0 ); +} +inline VOICE_CLOCK( V5 ) +{ + // Output right + voice_output( v, 1 ); + + // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier + int endx_buf = REG(endx) | m.t_looped; + + // Clear bit in ENDX if KON just began + if ( v->kon_delay == 5 ) + endx_buf &= ~v->vbit; + m.endx_buf = (uint8_t) endx_buf; +} +inline VOICE_CLOCK( V6 ) +{ + (void) v; // avoid compiler warning about unused v + m.outx_buf = (uint8_t) (m.t_output >> 8); +} +inline VOICE_CLOCK( V7 ) +{ + // Update ENDX + REG(endx) = m.endx_buf; + + m.envx_buf = v->t_envx_out; +} +inline VOICE_CLOCK( V8 ) +{ + // Update OUTX + VREG(v->regs,outx) = m.outx_buf; +} +inline VOICE_CLOCK( V9 ) +{ + // Update ENVX + VREG(v->regs,envx) = m.envx_buf; +} + +// Most voices do all these in one clock, so make a handy composite +inline VOICE_CLOCK( V3 ) +{ + voice_V3a( v ); + voice_V3b( v ); + voice_V3c( v ); +} + +// Common combinations of voice steps on different voices. This greatly reduces +// code size and allows everything to be inlined in these functions. +VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } +VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } +VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } + + +//// Echo + +// Current echo buffer pointer for left/right channel +#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) + +// Sample in echo history buffer, where 0 is the oldest +#define ECHO_FIR( i ) (m.echo_hist_pos [i]) + +// Calculate FIR point for left/right channel +#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) + +#define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n() + +inline void SPC_DSP::echo_read( int ch ) +{ + int s = GET_LE16SA( ECHO_PTR( ch ) ); + // second copy simplifies wrap-around handling + ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; +} + +ECHO_CLOCK( 22 ) +{ + // History + if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) + m.echo_hist_pos = m.echo_hist; + + m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; + echo_read( 0 ); + + // FIR (using l and r temporaries below helps compiler optimize) + int l = CALC_FIR( 0, 0 ); + int r = CALC_FIR( 0, 1 ); + + m.t_echo_in [0] = l; + m.t_echo_in [1] = r; +} +ECHO_CLOCK( 23 ) +{ + int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); + int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; + + echo_read( 1 ); +} +ECHO_CLOCK( 24 ) +{ + int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); + int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; +} +ECHO_CLOCK( 25 ) +{ + int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); + int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); + + l = (int16_t) l; + r = (int16_t) r; + + l += (int16_t) CALC_FIR( 7, 0 ); + r += (int16_t) CALC_FIR( 7, 1 ); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_in [0] = l & ~1; + m.t_echo_in [1] = r & ~1; +} +inline int SPC_DSP::echo_output( int ch ) +{ + int out = (int16_t) ((m.t_main_out [ch] * (int8_t) REG(mvoll + ch * 0x10)) >> 7) + + (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); + CLAMP16( out ); + return out; +} +ECHO_CLOCK( 26 ) +{ + // Left output volumes + // (save sample for next clock so we can output both together) + m.t_main_out [0] = echo_output( 0 ); + + // Echo feedback + int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); + int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_out [0] = l & ~1; + m.t_echo_out [1] = r & ~1; +} +ECHO_CLOCK( 27 ) +{ + // Output + int l = m.t_main_out [0]; + int r = echo_output( 1 ); + m.t_main_out [0] = 0; + m.t_main_out [1] = 0; + + // TODO: global muting isn't this simple (turns DAC on and off + // or something, causing small ~37-sample pulse when first muted) + if ( REG(flg) & 0x40 ) + { + l = 0; + r = 0; + } + + // Output sample to DAC + #ifdef SPC_DSP_OUT_HOOK + SPC_DSP_OUT_HOOK( l, r ); + #else + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + #endif +} +ECHO_CLOCK( 28 ) +{ + m.t_echo_enabled = REG(flg); +} +inline void SPC_DSP::echo_write( int ch ) +{ + if ( !(m.t_echo_enabled & 0x20) ) + SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); + m.t_echo_out [ch] = 0; +} +ECHO_CLOCK( 29 ) +{ + m.t_esa = REG(esa); + + if ( !m.echo_offset ) + m.echo_length = (REG(edl) & 0x0F) * 0x800; + + m.echo_offset += 4; + if ( m.echo_offset >= m.echo_length ) + m.echo_offset = 0; + + // Write left echo + echo_write( 0 ); + + m.t_echo_enabled = REG(flg); +} +ECHO_CLOCK( 30 ) +{ + // Write right echo + echo_write( 1 ); +} + + +//// Timing + +// Execute clock for a particular voice +#define V( clock, voice ) voice_##clock( &m.voices [voice] ); + +/* The most common sequence of clocks uses composite operations +for efficiency. For example, the following are equivalent to the +individual steps on the right: + +V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) +V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) +V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ + +// Voice 0 1 2 3 4 5 6 7 +#define GEN_DSP_TIMING \ +PHASE( 0) V(V5,0)V(V2,1)\ +PHASE( 1) V(V6,0)V(V3,1)\ +PHASE( 2) V(V7_V4_V1,0)\ +PHASE( 3) V(V8_V5_V2,0)\ +PHASE( 4) V(V9_V6_V3,0)\ +PHASE( 5) V(V7_V4_V1,1)\ +PHASE( 6) V(V8_V5_V2,1)\ +PHASE( 7) V(V9_V6_V3,1)\ +PHASE( 8) V(V7_V4_V1,2)\ +PHASE( 9) V(V8_V5_V2,2)\ +PHASE(10) V(V9_V6_V3,2)\ +PHASE(11) V(V7_V4_V1,3)\ +PHASE(12) V(V8_V5_V2,3)\ +PHASE(13) V(V9_V6_V3,3)\ +PHASE(14) V(V7_V4_V1,4)\ +PHASE(15) V(V8_V5_V2,4)\ +PHASE(16) V(V9_V6_V3,4)\ +PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ +PHASE(18) V(V8_V5_V2,5)\ +PHASE(19) V(V9_V6_V3,5)\ +PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ +PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ +PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ +PHASE(23) V(V7,7) echo_23();\ +PHASE(24) V(V8,7) echo_24();\ +PHASE(25) V(V3b,0) V(V9,7) echo_25();\ +PHASE(26) echo_26();\ +PHASE(27) misc_27(); echo_27();\ +PHASE(28) misc_28(); echo_28();\ +PHASE(29) misc_29(); echo_29();\ +PHASE(30) misc_30();V(V3c,0) echo_30();\ +PHASE(31) V(V4,0) V(V1,2)\ + +#if !SPC_DSP_CUSTOM_RUN + +void SPC_DSP::run( int clocks_remain ) +{ + require( clocks_remain > 0 ); + + int const phase = m.phase; + m.phase = (phase + clocks_remain) & 31; + switch ( phase ) + { + loop: + + #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: + GEN_DSP_TIMING + #undef PHASE + + if ( --clocks_remain ) + goto loop; + } +} + +#endif + + +//// Setup + +void SPC_DSP::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + set_output( 0, 0 ); + reset(); + + stereo_switch = 0xffff; + take_spc_snapshot = 0; + spc_snapshot_callback = 0; + + #ifndef NDEBUG + // be sure this sign-extends + assert( (int16_t) 0x8000 == -0x8000 ); + + // be sure right shift preserves sign + assert( (-1 >> 1) == -1 ); + + // check clamp macro + int i; + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); + + blargg_verify_byte_order(); + #endif +} + +void SPC_DSP::soft_reset_common() +{ + require( m.ram ); // init() must have been called already + + m.noise = 0x4000; + m.echo_hist_pos = m.echo_hist; + m.every_other_sample = 1; + m.echo_offset = 0; + m.phase = 0; + + init_counter(); + + for (int i = 0; i < voice_count; i++) + m.voices[i].voice_number = i; +} + +void SPC_DSP::soft_reset() +{ + REG(flg) = 0xE0; + soft_reset_common(); +} + +void SPC_DSP::load( uint8_t const regs [register_count] ) +{ + memcpy( m.regs, regs, sizeof m.regs ); + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); + + // Internal state + for ( int i = voice_count; --i >= 0; ) + { + voice_t* v = &m.voices [i]; + v->brr_offset = 1; + v->vbit = 1 << i; + v->regs = &m.regs [i * 0x10]; + } + m.new_kon = REG(kon); + m.t_dir = REG(dir); + m.t_esa = REG(esa); + + soft_reset_common(); +} + +void SPC_DSP::reset() { load( initial_regs ); } + + +//// State save/load + +#if !SPC_NO_COPY_STATE_FUNCS + +void SPC_State_Copier::copy( void* state, size_t size ) +{ + func( buf, state, size ); +} + +int SPC_State_Copier::copy_int( int state, int size ) +{ + BOOST::uint8_t s [2]; + SET_LE16( s, state ); + func( buf, &s, size ); + return GET_LE16( s ); +} + +void SPC_State_Copier::skip( int count ) +{ + if ( count > 0 ) + { + char temp [64]; + memset( temp, 0, sizeof temp ); + do + { + int n = sizeof temp; + if ( n > count ) + n = count; + count -= n; + func( buf, temp, n ); + } + while ( count ); + } +} + +void SPC_State_Copier::extra() +{ + int n = 0; + SPC_State_Copier& copier = *this; + SPC_COPY( uint8_t, n ); + skip( n ); +} + +void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy ) +{ + SPC_State_Copier copier( io, copy ); + + // DSP registers + copier.copy( m.regs, register_count ); + + // Internal state + + // Voices + int i; + for ( i = 0; i < voice_count; i++ ) + { + voice_t* v = &m.voices [i]; + + // BRR buffer + int i; + for ( i = 0; i < brr_buf_size; i++ ) + { + int s = v->buf [i]; + SPC_COPY( int16_t, s ); + v->buf [i] = v->buf [i + brr_buf_size] = s; + } + + SPC_COPY( uint16_t, v->interp_pos ); + SPC_COPY( uint16_t, v->brr_addr ); + SPC_COPY( uint16_t, v->env ); + SPC_COPY( int16_t, v->hidden_env ); + SPC_COPY( uint8_t, v->buf_pos ); + SPC_COPY( uint8_t, v->brr_offset ); + SPC_COPY( uint8_t, v->kon_delay ); + { + int m = v->env_mode; + SPC_COPY( uint8_t, m ); + v->env_mode = (enum env_mode_t) m; + } + SPC_COPY( uint8_t, v->t_envx_out ); + + copier.extra(); + } + + // Echo history + for ( i = 0; i < echo_hist_size; i++ ) + { + int j; + for ( j = 0; j < 2; j++ ) + { + int s = m.echo_hist_pos [i] [j]; + SPC_COPY( int16_t, s ); + m.echo_hist [i] [j] = s; // write back at offset 0 + } + } + m.echo_hist_pos = m.echo_hist; + memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); + + // Misc + SPC_COPY( uint8_t, m.every_other_sample ); + SPC_COPY( uint8_t, m.kon ); + + SPC_COPY( uint16_t, m.noise ); + SPC_COPY( uint16_t, m.counter ); + SPC_COPY( uint16_t, m.echo_offset ); + SPC_COPY( uint16_t, m.echo_length ); + SPC_COPY( uint8_t, m.phase ); + + SPC_COPY( uint8_t, m.new_kon ); + SPC_COPY( uint8_t, m.endx_buf ); + SPC_COPY( uint8_t, m.envx_buf ); + SPC_COPY( uint8_t, m.outx_buf ); + + SPC_COPY( uint8_t, m.t_pmon ); + SPC_COPY( uint8_t, m.t_non ); + SPC_COPY( uint8_t, m.t_eon ); + SPC_COPY( uint8_t, m.t_dir ); + SPC_COPY( uint8_t, m.t_koff ); + + SPC_COPY( uint16_t, m.t_brr_next_addr ); + SPC_COPY( uint8_t, m.t_adsr0 ); + SPC_COPY( uint8_t, m.t_brr_header ); + SPC_COPY( uint8_t, m.t_brr_byte ); + SPC_COPY( uint8_t, m.t_srcn ); + SPC_COPY( uint8_t, m.t_esa ); + SPC_COPY( uint8_t, m.t_echo_enabled ); + + SPC_COPY( int16_t, m.t_main_out [0] ); + SPC_COPY( int16_t, m.t_main_out [1] ); + SPC_COPY( int16_t, m.t_echo_out [0] ); + SPC_COPY( int16_t, m.t_echo_out [1] ); + SPC_COPY( int16_t, m.t_echo_in [0] ); + SPC_COPY( int16_t, m.t_echo_in [1] ); + + SPC_COPY( uint16_t, m.t_dir_addr ); + SPC_COPY( uint16_t, m.t_pitch ); + SPC_COPY( int16_t, m.t_output ); + SPC_COPY( uint16_t, m.t_echo_ptr ); + SPC_COPY( uint8_t, m.t_looped ); + + copier.extra(); +} +#endif + + +//// Snes9x Accessor + +void SPC_DSP::set_spc_snapshot_callback( void (*callback) (void) ) +{ + spc_snapshot_callback = callback; +} + +void SPC_DSP::dump_spc_snapshot( void ) +{ + take_spc_snapshot = 1; +} + +void SPC_DSP::set_stereo_switch( int value ) +{ + stereo_switch = value; +} + +SPC_DSP::uint8_t SPC_DSP::reg_value( int ch, int addr ) +{ + return m.voices[ch].regs[addr]; +} + +int SPC_DSP::envx_value( int ch ) +{ + return m.voices[ch].env; +} diff --git a/MCUME_teensy41/teensysnes/SPC_DSP.h b/MCUME_teensy41/teensysnes/SPC_DSP.h new file mode 100644 index 0000000..6705a49 --- /dev/null +++ b/MCUME_teensy41/teensysnes/SPC_DSP.h @@ -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 + +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 diff --git a/MCUME_teensy41/teensysnes/apu.cpp b/MCUME_teensy41/teensysnes/apu.cpp index 1bdc0fa..db021be 100644 --- a/MCUME_teensy41/teensysnes/apu.cpp +++ b/MCUME_teensy41/teensysnes/apu.cpp @@ -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 + (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 #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); -} - } diff --git a/MCUME_teensy41/teensysnes/apu.h b/MCUME_teensy41/teensysnes/apu.h index 5a5b766..e171304 100644 --- a/MCUME_teensy41/teensysnes/apu.h +++ b/MCUME_teensy41/teensysnes/apu.h @@ -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 diff --git a/MCUME_teensy41/teensysnes/blargg_common.h b/MCUME_teensy41/teensysnes/blargg_common.h new file mode 100644 index 0000000..75edff3 --- /dev/null +++ b/MCUME_teensy41/teensysnes/blargg_common.h @@ -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 +#include +#include +#include + +#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 (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 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 + #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 for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST + +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #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 diff --git a/MCUME_teensy41/teensysnes/blargg_config.h b/MCUME_teensy41/teensysnes/blargg_config.h new file mode 100644 index 0000000..9dc69db --- /dev/null +++ b/MCUME_teensy41/teensysnes/blargg_config.h @@ -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 diff --git a/MCUME_teensy41/teensysnes/blargg_endian.h b/MCUME_teensy41/teensysnes/blargg_endian.h new file mode 100644 index 0000000..f2daca6 --- /dev/null +++ b/MCUME_teensy41/teensysnes/blargg_endian.h @@ -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 + #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 diff --git a/MCUME_teensy41/teensysnes/blargg_source.h b/MCUME_teensy41/teensysnes/blargg_source.h new file mode 100644 index 0000000..5e45c4f --- /dev/null +++ b/MCUME_teensy41/teensysnes/blargg_source.h @@ -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 + +// 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 +inline T min( T x, T y ) +{ + if ( x < y ) + return x; + return y; +} + +template +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 diff --git a/MCUME_teensy41/teensysnes/emuapi.cpp b/MCUME_teensy41/teensysnes/emuapi.cpp index d94a4a5..c4fb601 100644 --- a/MCUME_teensy41/teensysnes/emuapi.cpp +++ b/MCUME_teensy41/teensysnes/emuapi.cpp @@ -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)) { diff --git a/MCUME_teensy41/teensysnes/emuapi.h b/MCUME_teensy41/teensysnes/emuapi.h index ce7803e..8ee9648 100644 --- a/MCUME_teensy41/teensysnes/emuapi.h +++ b/MCUME_teensy41/teensysnes/emuapi.h @@ -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); diff --git a/MCUME_teensy41/teensysnes/gfx.cpp b/MCUME_teensy41/teensysnes/gfx.cpp index dfb1273..670aa9e 100644 --- a/MCUME_teensy41/teensysnes/gfx.cpp +++ b/MCUME_teensy41/teensysnes/gfx.cpp @@ -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); diff --git a/MCUME_teensy41/teensysnes/hacks.h b/MCUME_teensy41/teensysnes/hacks.h index ad47005..3850b7c 100644 --- a/MCUME_teensy41/teensysnes/hacks.h +++ b/MCUME_teensy41/teensysnes/hacks.h @@ -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"}, diff --git a/MCUME_teensy41/teensysnes/iplrom.hpp b/MCUME_teensy41/teensysnes/iplrom.hpp deleted file mode 100644 index 23c3217..0000000 --- a/MCUME_teensy41/teensysnes/iplrom.hpp +++ /dev/null @@ -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) -}; diff --git a/MCUME_teensy41/teensysnes/license.txt b/MCUME_teensy41/teensysnes/license.txt new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/MCUME_teensy41/teensysnes/license.txt @@ -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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/MCUME_teensy41/teensysnes/memory.cpp b/MCUME_teensy41/teensysnes/memory.cpp index cddadf1..4a9840d 100644 --- a/MCUME_teensy41/teensysnes/memory.cpp +++ b/MCUME_teensy41/teensysnes/memory.cpp @@ -4,6 +4,8 @@ For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ +#include + #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() diff --git a/MCUME_teensy41/teensysnes/platform_config.h b/MCUME_teensy41/teensysnes/platform_config.h index 3464dc2..3c46dc2 100644 --- a/MCUME_teensy41/teensysnes/platform_config.h +++ b/MCUME_teensy41/teensysnes/platform_config.h @@ -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 diff --git a/MCUME_teensy41/teensysnes/port.h b/MCUME_teensy41/teensysnes/port.h index 0dcd7c4..67af21f 100644 --- a/MCUME_teensy41/teensysnes/port.h +++ b/MCUME_teensy41/teensysnes/port.h @@ -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) diff --git a/MCUME_teensy41/teensysnes/resampler.h b/MCUME_teensy41/teensysnes/resampler.h new file mode 100644 index 0000000..51a3226 --- /dev/null +++ b/MCUME_teensy41/teensysnes/resampler.h @@ -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 */ diff --git a/MCUME_teensy41/teensysnes/ring_buffer.h b/MCUME_teensy41/teensysnes/ring_buffer.h new file mode 100644 index 0000000..b743c47 --- /dev/null +++ b/MCUME_teensy41/teensysnes/ring_buffer.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 + +#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 diff --git a/MCUME_teensy41/teensysnes/smp.cpp b/MCUME_teensy41/teensysnes/smp.cpp deleted file mode 100644 index 69c47e7..0000000 --- a/MCUME_teensy41/teensysnes/smp.cpp +++ /dev/null @@ -1,2957 +0,0 @@ -#include -#include "snes9x.h" - -namespace SNES -{ -#include "smp.hpp" - -SMP smp; - -#include "iplrom.hpp" - -#define op_read1(addr) (((((addr) & 0xfff0) != 0x00f0) && (addr) < 0xffc0 && ++ticks) ? apuram[addr] : op_read(addr)) -#define op_read2(addr) ({uint32 x = (addr); op_read1(x); }) - -#define op_readpc() op_read(regs.pc++) -#define op_readdp(addr) op_read((regs.p.p << 8) + ((addr)&0xff)) -#define op_writedp(addr, data) op_write((regs.p.p << 8) + ((addr)&0xff), data) - - -uint8 SMP::op_adc(uint8 x, uint8 y) -{ - int r = x + y + regs.p.c; - regs.p.n = r & 0x80; - regs.p.v = ~(x ^ y) & (x ^ r) & 0x80; - regs.p.h = (x ^ y ^ r) & 0x10; - regs.p.z = (uint8)r == 0; - regs.p.c = r > 0xff; - return r; -} - -uint16 SMP::op_addw(uint16 x, uint16 y) -{ - uint16 r; - regs.p.c = 0; - r = op_adc(x, y); - r |= op_adc(x >> 8, y >> 8) << 8; - regs.p.z = r == 0; - return r; -} - -uint8 SMP::op_and(uint8 x, uint8 y) -{ - x &= y; - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -uint8 SMP::op_cmp(uint8 x, uint8 y) -{ - int r = x - y; - regs.p.n = r & 0x80; - regs.p.z = (uint8)r == 0; - regs.p.c = r >= 0; - return x; -} - -uint16 SMP::op_cmpw(uint16 x, uint16 y) -{ - int r = x - y; - regs.p.n = r & 0x8000; - regs.p.z = (uint16)r == 0; - regs.p.c = r >= 0; - return x; -} - -uint8 SMP::op_eor(uint8 x, uint8 y) -{ - x ^= y; - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -uint8 SMP::op_or(uint8 x, uint8 y) -{ - x |= y; - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -uint8 SMP::op_sbc(uint8 x, uint8 y) -{ - int r = x - y - !regs.p.c; - regs.p.n = r & 0x80; - regs.p.v = (x ^ y) & (x ^ r) & 0x80; - regs.p.h = !((x ^ y ^ r) & 0x10); - regs.p.z = (uint8)r == 0; - regs.p.c = r >= 0; - return r; -} - -uint16 SMP::op_subw(uint16 x, uint16 y) -{ - uint16 r; - regs.p.c = 1; - r = op_sbc(x, y); - r |= op_sbc(x >> 8, y >> 8) << 8; - regs.p.z = r == 0; - return r; -} - -uint8 SMP::op_inc(uint8 x) -{ - x++; - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -uint8 SMP::op_dec(uint8 x) -{ - x--; - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -uint8 SMP::op_asl(uint8 x) -{ - regs.p.c = x & 0x80; - x <<= 1; - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -uint8 SMP::op_lsr(uint8 x) -{ - regs.p.c = x & 0x01; - x >>= 1; - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -uint8 SMP::op_rol(uint8 x) -{ - unsigned carry = (unsigned)regs.p.c; - regs.p.c = x & 0x80; - x = (x << 1) | carry; - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -uint8 SMP::op_ror(uint8 x) -{ - unsigned carry = (unsigned)regs.p.c << 7; - regs.p.c = x & 0x01; - x = carry | (x >> 1); - regs.p.n = x & 0x80; - regs.p.z = x == 0; - return x; -} - -void SMP::tick() -{ - timer0.tick(); - timer1.tick(); - timer2.tick(); - - clock++; -} - -void SMP::tick(unsigned clocks) -{ - timer0.tick(clocks); - timer1.tick(clocks); - timer2.tick(clocks); - - clock += clocks; -} - -IRAM_ATTR unsigned SMP::op_read(unsigned addr) -{ - tick(); - - if ((addr & 0xfff0) == 0x00f0) - { - switch (addr) - { - case 0xf2: - return status.dsp_addr; - case 0xf3: - return 0; // dsp.read(status.dsp_addr & 0x7f); - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - return registers[addr & 3]; - case 0xf8: - return status.ram00f8; - case 0xf9: - return status.ram00f9; - case 0xfd: - { - unsigned result = timer0.stage3_ticks & 15; - timer0.stage3_ticks = 0; - return result; - } - case 0xfe: - { - unsigned result = timer1.stage3_ticks & 15; - timer1.stage3_ticks = 0; - return result; - } - case 0xff: - { - unsigned result = timer2.stage3_ticks & 15; - timer2.stage3_ticks = 0; - return result; - } - default: - return 0x00; - } - } - - if (addr >= 0xffc0 && status.iplrom_enable) - return iplrom[addr & 0x3f]; - - return apuram[addr]; -} - -IRAM_ATTR void SMP::op_write(unsigned addr, uint8 data) -{ - tick(); - - if ((addr & 0xfff0) == 0x00f0) - { - switch (addr) - { - case 0xf1: - status.iplrom_enable = data & 0x80; - - if (data & 0x30) - { - if (data & 0x20) - { - registers[3] = 0x00; - registers[2] = 0x00; - } - if (data & 0x10) - { - registers[1] = 0x00; - registers[0] = 0x00; - } - } - - if (timer2.enable == false && (data & 0x04)) - { - timer2.stage2_ticks = 0; - timer2.stage3_ticks = 0; - } - timer2.enable = data & 0x04; - - if (timer1.enable == false && (data & 0x02)) - { - timer1.stage2_ticks = 0; - timer1.stage3_ticks = 0; - } - timer1.enable = data & 0x02; - - if (timer0.enable == false && (data & 0x01)) - { - timer0.stage2_ticks = 0; - timer0.stage3_ticks = 0; - } - timer0.enable = data & 0x01; - - break; - - case 0xf2: - status.dsp_addr = data; - break; - - case 0xf3: - if (status.dsp_addr & 0x80) - break; - // dsp.write(status.dsp_addr, data); - break; - - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - apuram[0xf4 + (addr & 3)] = data; - break; - - case 0xf8: - status.ram00f8 = data; - break; - - case 0xf9: - status.ram00f9 = data; - break; - - case 0xfa: - timer0.target = data; - break; - - case 0xfb: - timer1.target = data; - break; - - case 0xfc: - timer2.target = data; - break; - } - } - - //all writes go to RAM, even MMIO writes - apuram[addr] = data; -} - -IRAM_ATTR void SMP::execute(int cycles) -{ - clock -= cycles; - - while (clock < 0) - { - if (opcode_cycle == 0) - opcode_number = op_readpc(); - - switch (opcode_number) - { - case 0x00: - ticks++; - break; - - case 0xef: - ticks += (2); - regs.pc--; - break; - - case 0xff: - ticks += (2); - regs.pc--; - break; - - case 0x9f: - ticks += (4); - regs.B.a = (regs.B.a >> 4) | (regs.B.a << 4); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - break; - - case 0xdf: - ticks += (2); - if (regs.p.c || (regs.B.a) > 0x99) - { - regs.B.a += 0x60; - regs.p.c = 1; - } - if (regs.p.h || (regs.B.a & 15) > 0x09) - { - regs.B.a += 0x06; - } - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - break; - - case 0xbe: - ticks += (2); - if (!regs.p.c || (regs.B.a) > 0x99) - { - regs.B.a -= 0x60; - regs.p.c = 0; - } - if (!regs.p.h || (regs.B.a & 15) > 0x09) - { - regs.B.a -= 0x06; - } - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - break; - - case 0x60: - ticks++; - regs.p.c = 0; - break; - - case 0x20: - ticks++; - regs.p.p = 0; - break; - - case 0x80: - ticks++; - regs.p.c = 1; - break; - - case 0x40: - ticks++; - regs.p.p = 1; - break; - - case 0xe0: - ticks++; - regs.p.v = 0; - regs.p.h = 0; - break; - - case 0xed: - ticks += (2); - regs.p.c = !regs.p.c; - break; - - case 0xa0: - ticks += (2); - regs.p.i = 1; - break; - - case 0xc0: - ticks += (2); - regs.p.i = 0; - break; - - case 0x02: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x01; - op_writedp(dp, rd); - break; - - case 0x12: - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x01; - op_writedp(dp, rd); - break; - - case 0x22: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x02; - op_writedp(dp, rd); - break; - - case 0x32: - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x02; - op_writedp(dp, rd); - break; - - case 0x42: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x04; - op_writedp(dp, rd); - break; - - case 0x52: - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x04; - op_writedp(dp, rd); - break; - - case 0x62: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x08; - op_writedp(dp, rd); - break; - - case 0x72: - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x08; - op_writedp(dp, rd); - break; - - case 0x82: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x10; - op_writedp(dp, rd); - break; - - case 0x92: - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x10; - op_writedp(dp, rd); - break; - - case 0xa2: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x20; - op_writedp(dp, rd); - break; - - case 0xb2: - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x20; - op_writedp(dp, rd); - break; - - case 0xc2: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x40; - op_writedp(dp, rd); - break; - - case 0xd2: - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x40; - op_writedp(dp, rd); - break; - - case 0xe2: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x80; - op_writedp(dp, rd); - break; - - case 0xf2: - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x80; - op_writedp(dp, rd); - break; - - case 0x2d: - ticks += (3); - stack[regs.sp--] = (regs.B.a); - break; - - case 0x4d: - ticks += (3); - stack[regs.sp--] = (regs.x); - break; - - case 0x6d: - ticks += (3); - stack[regs.sp--] = (regs.B.y); - break; - - case 0x0d: - ticks += (3); - stack[regs.sp--] = (regs.p); - break; - - case 0xae: - ticks += (3); - regs.B.a = stack[++regs.sp]; - break; - - case 0xce: - ticks += (3); - regs.x = stack[++regs.sp]; - break; - - case 0xee: - ticks += (3); - regs.B.y = stack[++regs.sp]; - break; - - case 0x8e: - ticks += (3); - regs.p = stack[++regs.sp]; - break; - - case 0xcf: - ticks += (8); - ya = regs.B.y * regs.B.a; - regs.B.a = ya; - regs.B.y = ya >> 8; - //result is set based on y (high-byte) only - regs.p.n = !!(regs.B.y & 0x80); - regs.p.z = (regs.B.y == 0); - break; - - case 0x9e: - ticks += (11); - ya = regs.ya; - //overflow set if quotient >= 256 - regs.p.v = !!(regs.B.y >= regs.x); - regs.p.h = !!((regs.B.y & 15) >= (regs.x & 15)); - if (regs.B.y < (regs.x << 1)) - { - //if quotient is <= 511 (will fit into 9-bit result) - regs.B.a = ya / regs.x; - regs.B.y = ya % regs.x; - } - else - { - //otherwise, the quotient won't fit into regs.p.v + regs.B.a - //this emulates the odd behavior of the S-SMP in this case - regs.B.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); - regs.B.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); - } - //result is set based on a (quotient) only - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - break; - - case 0x7d: - ticks++; - regs.B.a = regs.x; - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - break; - - case 0xdd: - ticks++; - regs.B.a = regs.B.y; - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - break; - - case 0x5d: - ticks++; - regs.x = regs.B.a; - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); - break; - - case 0xfd: - ticks++; - regs.B.y = regs.B.a; - regs.p.n = !!(regs.B.y & 0x80); - regs.p.z = (regs.B.y == 0); - break; - - case 0x9d: - ticks++; - regs.x = regs.sp; - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); - break; - - case 0xbd: - ticks++; - regs.sp = regs.x; - break; - - case 0xe8: - regs.B.a = op_readpc(); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - break; - - case 0xcd: - regs.x = op_readpc(); - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); - break; - - case 0x8d: - regs.B.y = op_readpc(); - regs.p.n = !!(regs.B.y & 0x80); - regs.p.z = (regs.B.y == 0); - break; - - case 0xe6: - switch (++opcode_cycle) - { - case 1: - ticks++; - break; - case 2: - regs.B.a = op_readdp(regs.x); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xbf: - switch (++opcode_cycle) - { - case 1: - ticks++; - break; - case 2: - regs.B.a = op_readdp(regs.x++); - ticks++; - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xe4: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - break; - case 2: - regs.B.a = op_readdp(sp); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xf8: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - break; - case 2: - regs.x = op_readdp(sp); - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xeb: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - break; - case 2: - regs.B.y = op_readdp(sp); - regs.p.n = !!(regs.B.y & 0x80); - regs.p.z = (regs.B.y == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xf4: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - ticks++; - break; - case 2: - regs.B.a = op_readdp(sp + regs.x); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xf9: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - ticks++; - break; - case 2: - regs.x = op_readdp(sp + regs.B.y); - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xfb: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - ticks++; - break; - case 2: - regs.B.y = op_readdp(sp + regs.x); - regs.p.n = !!(regs.B.y & 0x80); - regs.p.z = (regs.B.y == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xe5: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - break; - case 2: - sp |= op_readpc() << 8; - break; - case 3: - regs.B.a = op_read(sp); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xe9: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - sp |= op_readpc() << 8; - break; - case 2: - regs.x = op_read(sp); - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xec: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - sp |= op_readpc() << 8; - break; - case 2: - regs.B.y = op_read(sp); - regs.p.n = !!(regs.B.y & 0x80); - regs.p.z = (regs.B.y == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xf5: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - sp |= op_readpc() << 8; - ticks++; - break; - case 2: - regs.B.a = op_read(sp + regs.x); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xf6: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - sp |= op_readpc() << 8; - ticks++; - break; - case 2: - regs.B.a = op_read(sp + regs.B.y); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xe7: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc() + regs.x; - ticks++; - break; - case 2: - sp = op_readdp(dp); - break; - case 3: - sp |= op_readdp(dp + 1) << 8; - break; - case 4: - regs.B.a = op_read(sp); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xf7: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - ticks++; - break; - case 2: - sp = op_readdp(dp); - break; - case 3: - sp |= op_readdp(dp + 1) << 8; - break; - case 4: - regs.B.a = op_read(sp + regs.B.y); - regs.p.n = !!(regs.B.a & 0x80); - regs.p.z = (regs.B.a == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xfa: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - break; - case 2: - rd = op_readdp(sp); - break; - case 3: - dp = op_readpc(); - break; - case 4: - op_writedp(dp, rd); - opcode_cycle = 0; - break; - } - break; - - case 0x8f: - switch (++opcode_cycle) - { - case 1: - rd = op_readpc(); - dp = op_readpc(); - break; - case 2: - op_readdp(dp); - break; - case 3: - op_writedp(dp, rd); - opcode_cycle = 0; - break; - } - break; - - case 0xc6: - switch (++opcode_cycle) - { - case 1: - ticks++; - break; - case 2: - op_readdp(regs.x); - break; - case 3: - op_writedp(regs.x, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xaf: - switch (++opcode_cycle) - { - case 1: - ticks += 2; - break; - case 2: - op_writedp(regs.x++, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xc4: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - break; - case 2: - op_readdp(dp); - break; - case 3: - op_writedp(dp, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xd8: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - break; - case 2: - op_readdp(dp); - break; - case 3: - op_writedp(dp, regs.x); - opcode_cycle = 0; - break; - } - break; - - case 0xcb: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - break; - case 2: - op_readdp(dp); - break; - case 3: - op_writedp(dp, regs.B.y); - opcode_cycle = 0; - break; - } - break; - - case 0xd4: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - ticks++; - dp += regs.x; - break; - case 2: - op_readdp(dp); - break; - case 3: - op_writedp(dp, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xd9: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - ticks++; - dp += regs.B.y; - break; - case 2: - op_readdp(dp); - break; - case 3: - op_writedp(dp, regs.x); - opcode_cycle = 0; - break; - } - break; - - case 0xdb: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - ticks++; - dp += regs.x; - break; - case 2: - op_readdp(dp); - break; - case 3: - op_writedp(dp, regs.B.y); - opcode_cycle = 0; - break; - } - break; - - case 0xc5: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - break; - case 2: - dp |= op_readpc() << 8; - break; - case 3: - op_read(dp); - break; - case 4: - op_write(dp, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xc9: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - break; - case 2: - dp |= op_readpc() << 8; - break; - case 3: - op_read(dp); - break; - case 4: - op_write(dp, regs.x); - opcode_cycle = 0; - break; - } - break; - - case 0xcc: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - break; - case 2: - dp |= op_readpc() << 8; - break; - case 3: - op_read(dp); - break; - case 4: - op_write(dp, regs.B.y); - opcode_cycle = 0; - break; - } - break; - - case 0xd5: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - dp += regs.x; - break; - case 2: - op_read(dp); - break; - case 3: - op_write(dp, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xd6: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - dp += regs.B.y; - break; - case 2: - op_read(dp); - break; - case 3: - op_write(dp, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xc7: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - ticks++; - sp += regs.x; - break; - case 2: - dp = op_readdp(sp); - break; - case 3: - dp |= op_readdp(sp + 1) << 8; - break; - case 4: - op_read(dp); - break; - case 5: - op_write(dp, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xd7: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - break; - case 2: - dp = op_readdp(sp); - break; - case 3: - dp |= op_readdp(sp + 1) << 8; - ticks++; - dp += regs.B.y; - break; - case 4: - op_read(dp); - break; - case 5: - op_write(dp, regs.B.a); - opcode_cycle = 0; - break; - } - break; - - case 0xba: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - break; - case 2: - regs.B.a = op_readdp(sp); - ticks++; - break; - case 3: - regs.B.y = op_readdp(sp + 1); - regs.p.n = !!(regs.ya & 0x8000); - regs.p.z = (regs.ya == 0); - opcode_cycle = 0; - break; - } - break; - - case 0xda: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - break; - case 2: - op_readdp(dp); - break; - case 3: - op_writedp(dp, regs.B.a); - break; - case 4: - op_writedp(dp + 1, regs.B.y); - opcode_cycle = 0; - break; - } - break; - - case 0xaa: - switch (++opcode_cycle) - { - case 1: - sp = op_readpc(); - sp |= op_readpc() << 8; - break; - case 2: - bit = sp >> 13; - sp &= 0x1fff; - rd = op_read(sp); - regs.p.c = !!(rd & (1 << bit)); - opcode_cycle = 0; - break; - } - break; - - case 0xca: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - dp |= op_readpc() << 8; - break; - case 2: - bit = dp >> 13; - dp &= 0x1fff; - rd = op_read(dp); - if (regs.p.c) - rd |= (1 << bit); - else - rd &= ~(1 << bit); - ticks++; - break; - case 3: - op_write(dp, rd); - opcode_cycle = 0; - break; - } - break; - - case 0x2f: - rd = op_readpc(); - if (0) break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xf0: - rd = op_readpc(); - if (!regs.p.z) break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xd0: - rd = op_readpc(); - if (regs.p.z) break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xb0: - rd = op_readpc(); - if (!regs.p.c) break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x90: - rd = op_readpc(); - if (regs.p.c)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x70: - rd = op_readpc(); - if (!regs.p.v)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x50: - rd = op_readpc(); - if (regs.p.v)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x30: - rd = op_readpc(); - if (!regs.p.n)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x10: - rd = op_readpc(); - if (regs.p.n)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x03: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x01) != 0x01)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x13: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x01) == 0x01)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x23: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x02) != 0x02) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x33: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x02) == 0x02) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x43: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x04) != 0x04) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x53: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x04) == 0x04) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x63: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x08) != 0x08) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x73: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x08) == 0x08) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x83: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x10) != 0x10) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x93: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x10) == 0x10) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xa3: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x20) != 0x20) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xb3: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x20) == 0x20) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xc3: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x40) != 0x40) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xd3: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x40) == 0x40) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xe3: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x80) != 0x80) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xf3: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if ((sp & 0x80) == 0x80) - { - break; - } - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x2e: - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - ticks++; - if (regs.B.a == sp)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xde: - dp = op_readpc(); - ticks++; - sp = op_readdp(dp + regs.x); - rd = op_readpc(); - ticks++; - if (regs.B.a == sp)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x6e: - dp = op_readpc(); - wr = op_readdp(dp); - op_writedp(dp, --wr); - rd = op_readpc(); - if (wr == 0x00)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0xfe: - rd = op_readpc(); - ticks++; - regs.B.y--; - ticks++; - if (regs.B.y == 0x00)break; - ticks += (2); - regs.pc += (int8)rd; - break; - - case 0x5f: - rd = op_readpc(); - rd |= op_readpc() << 8; - regs.pc = rd; - break; - - case 0x1f: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - dp += regs.x; - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - regs.pc = rd; - break; - - case 0x3f: - rd = op_readpc(); - rd |= op_readpc() << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x4f: - rd = op_readpc(); - ticks += (4); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = 0xff00 | rd; - break; - - case 0x01: - dp = 0xffde - (0 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x11: - dp = 0xffde - (1 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x21: - dp = 0xffde - (2 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x31: - dp = 0xffde - (3 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x41: - dp = 0xffde - (4 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x51: - dp = 0xffde - (5 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x61: - dp = 0xffde - (6 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x71: - dp = 0xffde - (7 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x81: - dp = 0xffde - (8 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x91: - dp = 0xffde - (9 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0xa1: - dp = 0xffde - (10 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0xb1: - dp = 0xffde - (11 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0xc1: - dp = 0xffde - (12 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0xd1: - dp = 0xffde - (13 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0xe1: - dp = 0xffde - (14 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0xf1: - dp = 0xffde - (15 << 1); - rd = op_read(dp); - rd |= op_read(dp + 1) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - regs.pc = rd; - break; - - case 0x0f: - rd = op_read(0xffde); - rd |= op_read(0xffdf) << 8; - ticks += (5); - stack[regs.sp--] = (regs.pc >> 8); - stack[regs.sp--] = (regs.pc); - stack[regs.sp--] = (regs.p); - regs.pc = rd; - regs.p.b = 1; - regs.p.i = 0; - break; - - case 0x6f: - rd = (stack[++regs.sp]); - rd |= (stack[++regs.sp] << 8); - ticks += (4); - regs.pc = rd; - break; - - case 0x7f: - regs.p = stack[++regs.sp]; - rd = (stack[++regs.sp]); - rd |= (stack[++regs.sp] << 8); - ticks += (4); - regs.pc = rd; - break; - - case 0x88: - rd = op_readpc(); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x28: - rd = op_readpc(); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x68: - rd = op_readpc(); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0xc8: - rd = op_readpc(); - regs.x = op_cmp(regs.x, rd); - break; - - case 0xad: - rd = op_readpc(); - regs.B.y = op_cmp(regs.B.y, rd); - break; - - case 0x48: - rd = op_readpc(); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x08: - rd = op_readpc(); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0xa8: - rd = op_readpc(); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0x86: - ticks++; - rd = op_readdp(regs.x); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x26: - ticks++; - rd = op_readdp(regs.x); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x66: - ticks++; - rd = op_readdp(regs.x); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0x46: - ticks++; - rd = op_readdp(regs.x); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x06: - ticks++; - rd = op_readdp(regs.x); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0xa6: - ticks++; - rd = op_readdp(regs.x); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0x84: - dp = op_readpc(); - rd = op_readdp(dp); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x24: - dp = op_readpc(); - rd = op_readdp(dp); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x64: - dp = op_readpc(); - rd = op_readdp(dp); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0x3e: - dp = op_readpc(); - rd = op_readdp(dp); - regs.x = op_cmp(regs.x, rd); - break; - - case 0x7e: - switch (++opcode_cycle) - { - case 1: - dp = op_readpc(); - break; - case 2: - rd = op_readdp(dp); - regs.B.y = op_cmp(regs.B.y, rd); - opcode_cycle = 0; - break; - } - break; - - case 0x44: - dp = op_readpc(); - rd = op_readdp(dp); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x04: - dp = op_readpc(); - rd = op_readdp(dp); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0xa4: - dp = op_readpc(); - rd = op_readdp(dp); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0x94: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x34: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x74: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0x54: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x14: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0xb4: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0x85: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x25: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x65: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0x1e: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.x = op_cmp(regs.x, rd); - break; - - case 0x5e: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.B.y = op_cmp(regs.B.y, rd); - break; - - case 0x45: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x05: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0xa5: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0x95: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.x); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x96: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.B.y); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x35: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.x); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x36: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.B.y); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x75: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.x); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0x76: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.B.y); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0x55: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.x); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x56: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.B.y); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x15: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.x); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0x16: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.B.y); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0xb5: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.x); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0xb6: - dp = op_readpc(); - dp |= op_readpc() << 8; - ticks++; - rd = op_read(dp + regs.B.y); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0x87: - dp = op_readpc() + regs.x; - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x27: - dp = op_readpc() + regs.x; - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x67: - dp = op_readpc() + regs.x; - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0x47: - dp = op_readpc() + regs.x; - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x07: - dp = op_readpc() + regs.x; - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0xa7: - dp = op_readpc() + regs.x; - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0x97: - dp = op_readpc(); - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp + regs.B.y); - regs.B.a = op_adc(regs.B.a, rd); - break; - - case 0x37: - dp = op_readpc(); - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp + regs.B.y); - regs.B.a = op_and(regs.B.a, rd); - break; - - case 0x77: - dp = op_readpc(); - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp + regs.B.y); - regs.B.a = op_cmp(regs.B.a, rd); - break; - - case 0x57: - dp = op_readpc(); - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp + regs.B.y); - regs.B.a = op_eor(regs.B.a, rd); - break; - - case 0x17: - dp = op_readpc(); - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp + regs.B.y); - regs.B.a = op_or(regs.B.a, rd); - break; - - case 0xb7: - dp = op_readpc(); - ticks++; - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_read(sp + regs.B.y); - regs.B.a = op_sbc(regs.B.a, rd); - break; - - case 0x99: - ticks++; - rd = op_readdp(regs.B.y); - wr = op_readdp(regs.x); - wr = op_adc(wr, rd); - op_writedp(regs.x, wr); - break; - - case 0x39: - ticks++; - rd = op_readdp(regs.B.y); - wr = op_readdp(regs.x); - wr = op_and(wr, rd); - op_writedp(regs.x, wr); - break; - - case 0x79: - ticks++; - rd = op_readdp(regs.B.y); - wr = op_readdp(regs.x); - wr = op_cmp(wr, rd); - ticks++; - break; - - case 0x59: - ticks++; - rd = op_readdp(regs.B.y); - wr = op_readdp(regs.x); - wr = op_eor(wr, rd); - op_writedp(regs.x, wr); - break; - - case 0x19: - ticks++; - rd = op_readdp(regs.B.y); - wr = op_readdp(regs.x); - wr = op_or(wr, rd); - op_writedp(regs.x, wr); - break; - - case 0xb9: - ticks++; - rd = op_readdp(regs.B.y); - wr = op_readdp(regs.x); - wr = op_sbc(wr, rd); - op_writedp(regs.x, wr); - break; - - case 0x89: - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_adc(wr, rd); - op_writedp(dp, wr); - break; - - case 0x29: - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_and(wr, rd); - op_writedp(dp, wr); - break; - - case 0x69: - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_cmp(wr, rd); - ticks++; - break; - - case 0x49: - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_eor(wr, rd); - op_writedp(dp, wr); - break; - - case 0x09: - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_or(wr, rd); - op_writedp(dp, wr); - break; - - case 0xa9: - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_sbc(wr, rd); - op_writedp(dp, wr); - break; - - case 0x98: - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_adc(wr, rd); - op_writedp(dp, wr); - break; - - case 0x38: - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_and(wr, rd); - op_writedp(dp, wr); - break; - - case 0x78: - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_cmp(wr, rd); - ticks++; - break; - - case 0x58: - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_eor(wr, rd); - op_writedp(dp, wr); - break; - - case 0x18: - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_or(wr, rd); - op_writedp(dp, wr); - break; - - case 0xb8: - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_sbc(wr, rd); - op_writedp(dp, wr); - break; - - case 0x7a: - dp = op_readpc(); - rd = op_readdp(dp); - ticks++; - rd |= op_readdp(dp + 1) << 8; - regs.ya = op_addw(regs.ya, rd); - break; - - case 0x9a: - dp = op_readpc(); - rd = op_readdp(dp); - ticks++; - rd |= op_readdp(dp + 1) << 8; - regs.ya = op_subw(regs.ya, rd); - break; - - case 0x5a: - dp = op_readpc(); - rd = op_readdp(dp); - rd |= op_readdp(dp + 1) << 8; - op_cmpw(regs.ya, rd); - break; - - case 0x4a: - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_read(dp); - regs.p.c = regs.p.c & !!(rd & (1 << bit)); - break; - - case 0x6a: - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_read(dp); - regs.p.c = regs.p.c & !(rd & (1 << bit)); - break; - - case 0x8a: - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_read(dp); - ticks++; - regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); - break; - - case 0xea: - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_read(dp); - rd ^= (1 << bit); - op_write(dp, rd); - break; - - case 0x0a: - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_read(dp); - ticks++; - regs.p.c = regs.p.c | !!(rd & (1 << bit)); - break; - - case 0x2a: - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_read(dp); - ticks++; - regs.p.c = regs.p.c | !(rd & (1 << bit)); - break; - - case 0xbc: - ticks++; - regs.B.a = op_inc(regs.B.a); - break; - - case 0x3d: - ticks++; - regs.x = op_inc(regs.x); - break; - - case 0xfc: - ticks++; - regs.B.y = op_inc(regs.B.y); - break; - - case 0x9c: - ticks++; - regs.B.a = op_dec(regs.B.a); - break; - - case 0x1d: - ticks++; - regs.x = op_dec(regs.x); - break; - - case 0xdc: - ticks++; - regs.B.y = op_dec(regs.B.y); - break; - - case 0x1c: - ticks++; - regs.B.a = op_asl(regs.B.a); - break; - - case 0x5c: - ticks++; - regs.B.a = op_lsr(regs.B.a); - break; - - case 0x3c: - ticks++; - regs.B.a = op_rol(regs.B.a); - break; - - case 0x7c: - ticks++; - regs.B.a = op_ror(regs.B.a); - break; - - case 0xab: - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_inc(rd); - op_writedp(dp, rd); - break; - - case 0x8b: - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_dec(rd); - op_writedp(dp, rd); - break; - - case 0x0b: - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_asl(rd); - op_writedp(dp, rd); - break; - - case 0x4b: - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_lsr(rd); - op_writedp(dp, rd); - break; - - case 0x2b: - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_rol(rd); - op_writedp(dp, rd); - break; - - case 0x6b: - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_ror(rd); - op_writedp(dp, rd); - break; - - case 0xbb: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - rd = op_inc(rd); - op_writedp(dp + regs.x, rd); - break; - - case 0x9b: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - rd = op_dec(rd); - op_writedp(dp + regs.x, rd); - break; - - case 0x1b: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - rd = op_asl(rd); - op_writedp(dp + regs.x, rd); - break; - - case 0x5b: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - rd = op_lsr(rd); - op_writedp(dp + regs.x, rd); - break; - - case 0x3b: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - rd = op_rol(rd); - op_writedp(dp + regs.x, rd); - break; - - case 0x7b: - dp = op_readpc(); - ticks++; - rd = op_readdp(dp + regs.x); - rd = op_ror(rd); - op_writedp(dp + regs.x, rd); - break; - - case 0xac: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - rd = op_inc(rd); - op_write(dp, rd); - break; - - case 0x8c: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - rd = op_dec(rd); - op_write(dp, rd); - break; - - case 0x0c: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - rd = op_asl(rd); - op_write(dp, rd); - break; - - case 0x4c: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - rd = op_lsr(rd); - op_write(dp, rd); - break; - - case 0x2c: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - rd = op_rol(rd); - op_write(dp, rd); - break; - - case 0x6c: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - rd = op_ror(rd); - op_write(dp, rd); - break; - - case 0x0e: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.p.n = !!((regs.B.a - rd) & 0x80); - regs.p.z = ((regs.B.a - rd) == 0); - op_read(dp); - op_write(dp, rd | regs.B.a); - break; - - case 0x4e: - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_read(dp); - regs.p.n = !!((regs.B.a - rd) & 0x80); - regs.p.z = ((regs.B.a - rd) == 0); - op_read(dp); - op_write(dp, rd & ~regs.B.a); - break; - - case 0x3a: - dp = op_readpc(); - rd = op_readdp(dp); - rd++; - op_writedp(dp++, rd); - rd += op_readdp(dp) << 8; - op_writedp(dp, rd >> 8); - regs.p.n = !!(rd & 0x8000); - regs.p.z = (rd == 0); - break; - - case 0x1a: - dp = op_readpc(); - rd = op_readdp(dp); - rd--; - op_writedp(dp++, rd); - rd += op_readdp(dp) << 8; - op_writedp(dp, rd >> 8); - regs.p.n = !!(rd & 0x8000); - regs.p.z = (rd == 0); - break; - } - } - - if (ticks) - { - tick(ticks << 1); - ticks = 0; - } -} - -template -void SMP::Timer::tick() -{ - if (++stage1_ticks < cycle_frequency) - return; - - stage1_ticks = 0; - if (enable == false) - return; - - if (++stage2_ticks != target) - return; - - stage2_ticks = 0; - stage3_ticks = (stage3_ticks + 1) & 15; -} - -template -void SMP::Timer::tick(unsigned clocks) -{ - stage1_ticks += clocks; - if (stage1_ticks < cycle_frequency) - return; - - stage1_ticks -= cycle_frequency; - if (enable == false) - return; - - if (++stage2_ticks != target) - return; - - stage2_ticks = 0; - stage3_ticks = (stage3_ticks + 1) & 15; -} - -void SMP::power() -{ - smp.clock = 0; - - timer0.target = 0; - timer1.target = 0; - timer2.target = 0; - - reset(); -} - -void SMP::reset() -{ - memset(apuram, 0x00, 0x10000); - - opcode_number = 0; - opcode_cycle = 0; - - ticks = 0; - - regs.pc = 0xffc0; - regs.sp = 0xef; - regs.B.a = 0x00; - regs.x = 0x00; - regs.B.y = 0x00; - regs.p = 0x02; - - //$00f1 - status.iplrom_enable = true; - - //$00f2 - status.dsp_addr = 0x00; - - //$00f8,$00f9 - status.ram00f8 = 0x00; - status.ram00f9 = 0x00; - - //timers - timer0.enable = timer1.enable = timer2.enable = false; - timer0.stage1_ticks = timer1.stage1_ticks = timer2.stage1_ticks = 0; - timer0.stage2_ticks = timer1.stage2_ticks = timer2.stage2_ticks = 0; - timer0.stage3_ticks = timer1.stage3_ticks = timer2.stage3_ticks = 0; - - registers[0] = registers[1] = registers[2] = registers[3] = 0; -} - -SMP::SMP() -{ - apuram = new uint8[64 * 1024]; - stack = &apuram[0x100]; -} - -SMP::~SMP() -{ - delete[] apuram; -} - -void SMP::save_state(uint8 **block) -{ - uint8 *ptr = *block; - memcpy(ptr, apuram, 64 * 1024); - ptr += 64 * 1024; - -#undef INT32 -#define INT32(i) \ -SET_LE32(ptr, (i)); \ -ptr += sizeof(int32) - INT32(clock); - - INT32(opcode_number); - INT32(opcode_cycle); - - INT32(regs.pc); - INT32(regs.sp); - INT32(regs.B.a); - INT32(regs.x); - INT32(regs.B.y); - - INT32(regs.p.n); - INT32(regs.p.v); - INT32(regs.p.p); - INT32(regs.p.b); - INT32(regs.p.h); - INT32(regs.p.i); - INT32(regs.p.z); - INT32(regs.p.c); - - INT32(status.iplrom_enable); - - INT32(status.dsp_addr); - - INT32(status.ram00f8); - INT32(status.ram00f9); - - INT32(timer0.enable); - INT32(timer0.target); - INT32(timer0.stage1_ticks); - INT32(timer0.stage2_ticks); - INT32(timer0.stage3_ticks); - - INT32(timer1.enable); - INT32(timer1.target); - INT32(timer1.stage1_ticks); - INT32(timer1.stage2_ticks); - INT32(timer1.stage3_ticks); - - INT32(timer2.enable); - INT32(timer2.target); - INT32(timer2.stage1_ticks); - INT32(timer2.stage2_ticks); - INT32(timer2.stage3_ticks); - - INT32(rd); - INT32(wr); - INT32(dp); - INT32(sp); - INT32(ya); - INT32(bit); - - *block = ptr; -} - -void SMP::load_state(uint8 **block) -{ - uint8 *ptr = *block; - memcpy(apuram, ptr, 64 * 1024); - ptr += 64 * 1024; - -#undef INT32 -#define INT32(i) \ -i = GET_LE32(ptr); \ -ptr += sizeof(int32) - INT32(clock); - - INT32(opcode_number); - INT32(opcode_cycle); - - INT32(regs.pc); - INT32(regs.sp); - INT32(regs.B.a); - INT32(regs.x); - INT32(regs.B.y); - - INT32(regs.p.n); - INT32(regs.p.v); - INT32(regs.p.p); - INT32(regs.p.b); - INT32(regs.p.h); - INT32(regs.p.i); - INT32(regs.p.z); - INT32(regs.p.c); - - INT32(status.iplrom_enable); - - INT32(status.dsp_addr); - - INT32(status.ram00f8); - INT32(status.ram00f9); - - INT32(timer0.enable); - INT32(timer0.target); - INT32(timer0.stage1_ticks); - INT32(timer0.stage2_ticks); - INT32(timer0.stage3_ticks); - - INT32(timer1.enable); - INT32(timer1.target); - INT32(timer1.stage1_ticks); - INT32(timer1.stage2_ticks); - INT32(timer1.stage3_ticks); - - INT32(timer2.enable); - INT32(timer2.target); - INT32(timer2.stage1_ticks); - INT32(timer2.stage2_ticks); - INT32(timer2.stage3_ticks); - - INT32(rd); - INT32(wr); - INT32(dp); - INT32(sp); - INT32(ya); - INT32(bit); - - *block = ptr; -} - -} // namespace SNES diff --git a/MCUME_teensy41/teensysnes/smp.hpp b/MCUME_teensy41/teensysnes/smp.hpp deleted file mode 100644 index 9244e2b..0000000 --- a/MCUME_teensy41/teensysnes/smp.hpp +++ /dev/null @@ -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 - 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; diff --git a/MCUME_teensy41/teensysnes/snes.cpp b/MCUME_teensy41/teensysnes/snes.cpp index 90e6de0..338de10 100644 --- a/MCUME_teensy41/teensysnes/snes.cpp +++ b/MCUME_teensy41/teensysnes/snes.cpp @@ -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 diff --git a/MCUME_teensy41/teensysnes/snes.hpp b/MCUME_teensy41/teensysnes/snes.hpp new file mode 100755 index 0000000..239c4de --- /dev/null +++ b/MCUME_teensy41/teensysnes/snes.hpp @@ -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 diff --git a/MCUME_teensy41/teensysnes/snes9x.cpp b/MCUME_teensy41/teensysnes/snes9x.cpp index 6ddfdd9..22bc794 100644 --- a/MCUME_teensy41/teensysnes/snes9x.cpp +++ b/MCUME_teensy41/teensysnes/snes9x.cpp @@ -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); diff --git a/MCUME_teensy41/teensysnes/snes9x.h b/MCUME_teensy41/teensysnes/snes9x.h index 177bf3d..8a7fd23 100644 --- a/MCUME_teensy41/teensysnes/snes9x.h +++ b/MCUME_teensy41/teensysnes/snes9x.h @@ -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; diff --git a/MCUME_teensy41/teensysnes/teensysnes.ino b/MCUME_teensy41/teensysnes/teensysnes.ino index fe4041a..b7ae8ad 100644 --- a/MCUME_teensy41/teensysnes/teensysnes.ino +++ b/MCUME_teensy41/teensysnes/teensysnes.ino @@ -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); diff --git a/MCUME_teensy41/teensysnes/tile.cpp b/MCUME_teensy41/teensysnes/tile.cpp index 5e46326..2274c4c 100644 --- a/MCUME_teensy41/teensysnes/tile.cpp +++ b/MCUME_teensy41/teensysnes/tile.cpp @@ -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().