// // 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() doesnt 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 MIKIE_CPP //#include //#define TRACE_MIKIE #include "system.h" #include "mikie.h" #include "lynxdef.h" /* void CMikie::BlowOut() { C6502_REGS regs; mSystem.GetRegs(regs); //sprintf(addr,"Runtime Error - System Halted\nCMikie::Poke() - Read/Write to counter clocks at PC=$%04x.",regs.PC); //gError->Warning(addr); gSystemHalt=TRUE; } */ CMikie::CMikie(CSystem& parent) :mSystem(parent) { TRACE_MIKIE0("CMikie()"); int loop; for(loop=0;loop<16;loop++) mPalette[loop].Index=loop; DisplaySetAttributes(); Reset(); } CMikie::~CMikie() { TRACE_MIKIE0("~CMikie()"); } void CMikie::SetCPUSleep() { mSystem.gSystemCPUSleep = true; } void CMikie::ClearCPUSleep() { mSystem.gSystemCPUSleep = false; } void CMikie::Reset() { TRACE_MIKIE0("Reset()"); mAudioInputComparator=FALSE; // Initialises to unknown mDisplayAddress=0x00; // Initialises to unknown mLynxLine=0; mLynxLineDMACounter=0; mLynxAddr=0; mpDisplayCurrentLine = 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; for(int y = 0; y < 4; y++) { mAUDIO_BKUP[y]=0; mAUDIO_ENABLE_RELOAD[y]=0; mAUDIO_ENABLE_COUNT[y]=0; mAUDIO_LINKING[y]=0; mAUDIO_CURRENT[y]=0; mAUDIO_TIMER_DONE[y]=0; mAUDIO_LAST_CLOCK[y]=0; mAUDIO_BORROW_IN[y]=0; mAUDIO_BORROW_OUT[y]=0; mAUDIO_LAST_LINK_CARRY[y]=0; mAUDIO_LAST_COUNT[y]=0; mAUDIO_VOLUME[y]=0; mAUDIO_INTEGRATE_ENABLE[y]=0; mAUDIO_WAVESHAPER[y]=0; mAUDIO_OUTPUT[y] = 0; } mSTEREO=0xff; // 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; } uint32 CMikie::GetLfsrNext(uint32 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 seperate 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. uint32 switches,lfsr,next,swloop,result; static const uint32 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; } 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>4].Index]; bitmap_tmp++; } else { mLynxAddr++; *bitmap_tmp = mColourMap[mPalette[source>>4].Index]; bitmap_tmp++; *bitmap_tmp = mColourMap[mPalette[source&0x0f].Index]; bitmap_tmp++; } } } uint32 CMikie::DisplayRenderLine() { uint32 work_done=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; } // Logic says it should be 101 but testing on an actual lynx shows the rest // persiod 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; } // 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*DMA_RDWR_CYC; // Mikie screen DMA can only see the system RAM.... // (Step through bitmap, line at a time) if (mpDisplayCurrentLine < 102) { CopyLineSurface(); } else { // ?? printf("Lynx Line Overflow: %d\n", mpDisplayCurrentLine); } mpDisplayCurrentLine++; } return work_done; } uint32 CMikie::DisplayEndOfFrame() { // Stop any further line rendering mLynxLineDMACounter=0; mLynxLine=mTIM_2_BKUP; // Set the timer status flag if(mTimerInterruptMask&0x04) { TRACE_MIKIE0("Update() - TIMER2 IRQ Triggered (Frame Timer)"); mTimerStatusFlags|=0x04; } // blank remaining lines and blit to output while (mpDisplayCurrentLine < 102) { BlankLineSurface(); mpDisplayCurrentLine++; } mSystem.Blit(framebuffer); mpDisplayCurrentLine = 0; return 0; } // Peek/Poke memory handlers void CMikie::Poke(uint32 addr,uint8 data) { /* Sound register area */ if(addr >= 0xFD20 && addr <= 0xFD3F) { int which = (addr - 0xFD20) >> 3; // Each channel gets 8 ports/registers switch(addr & 0x7) { case (AUD0VOL&0x7): mAUDIO_VOLUME[which]=(int8)data; TRACE_MIKIE2("Poke(AUD0VOL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (AUD0SHFTFB&0x7): mAUDIO_WAVESHAPER[which]&=0x001fff; mAUDIO_WAVESHAPER[which]|=(uint32)data<<13; TRACE_MIKIE2("Poke(AUD0SHFTB,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (AUD0OUTVAL&0x7): mAUDIO_OUTPUT[which]=data; TRACE_MIKIE2("Poke(AUD0OUTVAL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (AUD0L8SHFT&0x7): mAUDIO_WAVESHAPER[which]&=0x1fff00; mAUDIO_WAVESHAPER[which]|=data; TRACE_MIKIE2("Poke(AUD0L8SHFT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (AUD0TBACK&0x7): mAUDIO_BKUP[which]=data; TRACE_MIKIE2("Poke(AUD0TBACK,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (AUD0CTL&0x7): mAUDIO_ENABLE_RELOAD[which]=data&0x10; mAUDIO_ENABLE_COUNT[which]=data&0x08; mAUDIO_LINKING[which]=data&0x07; mAUDIO_INTEGRATE_ENABLE[which]=data&0x20; if(data&0x40) mAUDIO_TIMER_DONE[which]=0; mAUDIO_WAVESHAPER[which]&=0x1fefff; mAUDIO_WAVESHAPER[which]|=(data&0x80)?0x001000:0x000000; if(data&0x48) { mAUDIO_LAST_COUNT[which]=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; } TRACE_MIKIE2("Poke(AUD0CTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (AUD0COUNT&0x7): mAUDIO_CURRENT[which]=data; TRACE_MIKIE2("Poke(AUD0COUNT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (AUD0MISC&0x7): mAUDIO_WAVESHAPER[which]&=0x1ff0ff; mAUDIO_WAVESHAPER[which]|=(data&0xf0)<<4; mAUDIO_BORROW_IN[which]=data&0x02; mAUDIO_BORROW_OUT[which]=data&0x01; mAUDIO_LAST_CLOCK[which]=data&0x04; TRACE_MIKIE2("Poke(AUD0MISC,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; } } else 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=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.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=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.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=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.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=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.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=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.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=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.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=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.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=mSystem.gSystemCycleCount; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; } TRACE_MIKIE2("Poke(TIM7CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); break; case (TIM0CNT&0xff): mTIM_0_CURRENT=data; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; TRACE_MIKIE2("Poke(TIM0CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); break; case (TIM1CNT&0xff): mTIM_1_CURRENT=data; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; TRACE_MIKIE2("Poke(TIM1CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); break; case (TIM2CNT&0xff): mTIM_2_CURRENT=data; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; TRACE_MIKIE2("Poke(TIM2CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); break; case (TIM3CNT&0xff): mTIM_3_CURRENT=data; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; TRACE_MIKIE2("Poke(TIM3CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); break; case (TIM4CNT&0xff): mTIM_4_CURRENT=data; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; TRACE_MIKIE2("Poke(TIM4CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); break; case (TIM5CNT&0xff): mTIM_5_CURRENT=data; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; TRACE_MIKIE2("Poke(TIM5CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); break; case (TIM6CNT&0xff): mTIM_6_CURRENT=data; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; TRACE_MIKIE2("Poke(TIM6CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); break; case (TIM7CNT&0xff): mTIM_7_CURRENT=data; mSystem.gNextTimerEvent=mSystem.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 (ATTEN_A&0xff): mAUDIO_ATTEN[0] = data; TRACE_MIKIE2("Poke(ATTEN_A ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (ATTEN_B&0xff): mAUDIO_ATTEN[1] = data; TRACE_MIKIE2("Poke(ATTEN_B ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (ATTEN_C&0xff): mAUDIO_ATTEN[2] = data; TRACE_MIKIE2("Poke(ATTEN_C ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (ATTEN_D&0xff): mAUDIO_ATTEN[3] = data; TRACE_MIKIE2("Poke(ATTEN_D ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (MPAN&0xff): TRACE_MIKIE2("Poke(MPAN ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); mPAN = data; CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (MSTEREO&0xff): TRACE_MIKIE2("Poke(MSTEREO ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); data^=0xff; mSTEREO=data; CombobulateSound(mSystem.gSystemCycleCount - startTS); break; case (INTRST&0xff): data^=0xff; mTimerStatusFlags&=data; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; 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; mSystem.gNextTimerEvent=mSystem.gSystemCycleCount; break; case (SYSCTL1&0xff): TRACE_MIKIE2("Poke(SYSCTL1 ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); if(!(data&0x02)) { C6502_REGS regs; mSystem.GetRegs(regs); TRACE_MIKIE1("Runtime Alert - System Halted\nCMikie::Poke(SYSCTL1) - Lynx power down occured at PC=$%04x.\nResetting system.",regs.PC); // can't reset in the middle of a frame, screws the timekeeping; and it doesn't make a difference anyway // mSystem.Reset(); mSystem.gSystemHalt=TRUE; } mSystem.CartAddressStrobe((data&0x01)?TRUE:FALSE); 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; break; case (IODAT&0xff): TRACE_MIKIE2("Poke(IODAT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); mIODAT=data; mSystem.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; 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): mSystem.gSuzieDoneTime = mSystem.gSystemCycleCount+mSystem.PaintSprites(); SetCPUSleep(); break; case (DISPCTL&0xff): TRACE_MIKIE2("Poke(DISPCTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); { TDISPCTL tmp; tmp.Byte=data; mDISPCTL_DMAEnable=tmp.Bits.DMAEnable; mDISPCTL_Flip=tmp.Bits.Flip; mDISPCTL_FourColour=tmp.Bits.FourColour; mDISPCTL_Colour=tmp.Bits.Colour; } 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): // Test registers are unimplemented // lets hope no programs use them. TRACE_MIKIE2("Poke(MTEST0/1,%02x) at PC=%04x",data,mSystem.mCpu->GetPC()); case (Mtest2&0xff): // Test registers are unimplemented // lets hope no programs use them. //gError->Warning("CMikie::Poke() - Write to MTEST2"); TRACE_MIKIE2("Poke(MTEST2,%02x) at PC=%04x",data,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; } } uint8 CMikie::Peek(uint32 addr) { /* Sound register area */ if(addr >= 0xFD20 && addr <= 0xFD3F) { int which = (addr - 0xFD20) >> 3; // Each channel gets 8 ports/registers switch(addr & 0x7) { case (AUD0VOL&0x7): return (uint8)mAUDIO_VOLUME[which]; break; case (AUD0SHFTFB&0x7): return (uint8)((mAUDIO_WAVESHAPER[which]>>13)&0xff); break; case (AUD0OUTVAL&0x7): return (uint8)mAUDIO_OUTPUT[which]; break; case (AUD0L8SHFT&0x7): return (uint8)(mAUDIO_WAVESHAPER[which]&0xff); break; case (AUD0TBACK&0x7): return (uint8)mAUDIO_BKUP[which]; break; case (AUD0CTL&0x7): { uint8 retval=0; retval|=(mAUDIO_INTEGRATE_ENABLE[which])?0x20:0x00; retval|=(mAUDIO_ENABLE_RELOAD[which])?0x10:0x00; retval|=(mAUDIO_ENABLE_COUNT[which])?0x08:0x00; retval|=(mAUDIO_WAVESHAPER[which]&0x001000)?0x80:0x00; retval|=mAUDIO_LINKING[which]; return retval; } break; case (AUD0COUNT&0x7): return (uint8)mAUDIO_CURRENT[which]; break; case (AUD0MISC&0x7): { uint8 retval=0; retval|=(mAUDIO_BORROW_OUT[which])?0x01:0x00; retval|=(mAUDIO_BORROW_IN[which])?0x02:0x00; retval|=(mAUDIO_LAST_CLOCK[which])?0x08:0x00; retval|=(mAUDIO_WAVESHAPER[which]>>4)&0xf0; return retval; } break; } } else switch(addr&0xff) { // Timer control registers case (TIM0BKUP&0xff): TRACE_MIKIE2("Peek(TIM0KBUP ,%02x) at PC=%04x",mTIM_0_BKUP,mSystem.mCpu->GetPC()); return (uint8)mTIM_0_BKUP; break; case (TIM1BKUP&0xff): TRACE_MIKIE2("Peek(TIM1KBUP ,%02x) at PC=%04x",mTIM_1_BKUP,mSystem.mCpu->GetPC()); return (uint8)mTIM_1_BKUP; break; case (TIM2BKUP&0xff): TRACE_MIKIE2("Peek(TIM2KBUP ,%02x) at PC=%04x",mTIM_2_BKUP,mSystem.mCpu->GetPC()); return (uint8)mTIM_2_BKUP; break; case (TIM3BKUP&0xff): TRACE_MIKIE2("Peek(TIM3KBUP ,%02x) at PC=%04x",mTIM_3_BKUP,mSystem.mCpu->GetPC()); return (uint8)mTIM_3_BKUP; break; case (TIM4BKUP&0xff): TRACE_MIKIE2("Peek(TIM4KBUP ,%02x) at PC=%04x",mTIM_4_BKUP,mSystem.mCpu->GetPC()); return (uint8)mTIM_4_BKUP; break; case (TIM5BKUP&0xff): TRACE_MIKIE2("Peek(TIM5KBUP ,%02x) at PC=%04x",mTIM_5_BKUP,mSystem.mCpu->GetPC()); return (uint8)mTIM_5_BKUP; break; case (TIM6BKUP&0xff): TRACE_MIKIE2("Peek(TIM6KBUP ,%02x) at PC=%04x",mTIM_6_BKUP,mSystem.mCpu->GetPC()); return (uint8)mTIM_6_BKUP; break; case (TIM7BKUP&0xff): TRACE_MIKIE2("Peek(TIM7KBUP ,%02x) at PC=%04x",mTIM_7_BKUP,mSystem.mCpu->GetPC()); return (uint8)mTIM_7_BKUP; break; case (TIM0CTLA&0xff): { uint8 retval=0; 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; } break; case (TIM1CTLA&0xff): { uint8 retval=0; 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; } break; case (TIM2CTLA&0xff): { uint8 retval=0; 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; } break; case (TIM3CTLA&0xff): { uint8 retval=0; 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; } break; case (TIM4CTLA&0xff): { uint8 retval=0; 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; } break; case (TIM5CTLA&0xff): { uint8 retval=0; 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; } break; case (TIM6CTLA&0xff): { uint8 retval=0; 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; } break; case (TIM7CTLA&0xff): { uint8 retval=0; 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; } break; case (TIM0CNT&0xff): Update(); TRACE_MIKIE2("Peek(TIM0CNT ,%02x) at PC=%04x",mTIM_0_CURRENT,mSystem.mCpu->GetPC()); return (uint8)mTIM_0_CURRENT; break; case (TIM1CNT&0xff): Update(); TRACE_MIKIE2("Peek(TIM1CNT ,%02x) at PC=%04x",mTIM_1_CURRENT,mSystem.mCpu->GetPC()); return (uint8)mTIM_1_CURRENT; break; case (TIM2CNT&0xff): Update(); TRACE_MIKIE2("Peek(TIM2CNT ,%02x) at PC=%04x",mTIM_2_CURRENT,mSystem.mCpu->GetPC()); return (uint8)mTIM_2_CURRENT; break; case (TIM3CNT&0xff): Update(); TRACE_MIKIE2("Peek(TIM3CNT ,%02x) at PC=%04x",mTIM_3_CURRENT,mSystem.mCpu->GetPC()); return (uint8)mTIM_3_CURRENT; break; case (TIM4CNT&0xff): Update(); TRACE_MIKIE2("Peek(TIM4CNT ,%02x) at PC=%04x",mTIM_4_CURRENT,mSystem.mCpu->GetPC()); return (uint8)mTIM_4_CURRENT; break; case (TIM5CNT&0xff): Update(); TRACE_MIKIE2("Peek(TIM5CNT ,%02x) at PC=%04x",mTIM_5_CURRENT,mSystem.mCpu->GetPC()); return (uint8)mTIM_5_CURRENT; break; case (TIM6CNT&0xff): Update(); TRACE_MIKIE2("Peek(TIM6CNT ,%02x) at PC=%04x",mTIM_6_CURRENT,mSystem.mCpu->GetPC()); return (uint8)mTIM_6_CURRENT; break; case (TIM7CNT&0xff): Update(); TRACE_MIKIE2("Peek(TIM7CNT ,%02x) at PC=%04x",mTIM_7_CURRENT,mSystem.mCpu->GetPC()); return (uint8)mTIM_7_CURRENT; break; case (TIM0CTLB&0xff): { uint8 retval=0; 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; } // BlowOut(); break; case (TIM1CTLB&0xff): { uint8 retval=0; 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; } // BlowOut(); break; case (TIM2CTLB&0xff): { uint8 retval=0; 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; } // BlowOut(); break; case (TIM3CTLB&0xff): { uint8 retval=0; 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; } // BlowOut(); break; case (TIM4CTLB&0xff): { uint8 retval=0; 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; } // BlowOut(); break; case (TIM5CTLB&0xff): { uint8 retval=0; 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; } // BlowOut(); break; case (TIM6CTLB&0xff): { uint8 retval=0; 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; } // BlowOut(); break; case (TIM7CTLB&0xff): { uint8 retval=0; 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; } // BlowOut(); break; // Extra audio control registers case (ATTEN_A&0xff): TRACE_MIKIE1("Peek(ATTEN_A) at PC=%04x",mSystem.mCpu->GetPC()); return (uint8) mAUDIO_ATTEN[0]; break; case (ATTEN_B&0xff): TRACE_MIKIE1("Peek(ATTEN_B) at PC=%04x",mSystem.mCpu->GetPC()); return (uint8) mAUDIO_ATTEN[1]; break; case (ATTEN_C&0xff): TRACE_MIKIE1("Peek(ATTEN_C) at PC=%04x",mSystem.mCpu->GetPC()); return (uint8) mAUDIO_ATTEN[2]; break; case (ATTEN_D&0xff): TRACE_MIKIE1("Peek(ATTEN_D) at PC=%04x",mSystem.mCpu->GetPC()); return (uint8) mAUDIO_ATTEN[3]; break; case (MPAN&0xff): TRACE_MIKIE1("Peek(MPAN) at PC=%04x",mSystem.mCpu->GetPC()); return (uint8) mPAN; break; case (MSTEREO&0xff): TRACE_MIKIE2("Peek(MSTEREO,%02x) at PC=%04x",(uint8)mSTEREO^0xff,mSystem.mCpu->GetPC()); return (uint8) mSTEREO^0xff; break; // Miscellaneous registers case (SERCTL&0xff): { uint32 retval=0; 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 (uint8)retval; } break; case (SERDAT&0xff): mUART_RX_READY=0; TRACE_MIKIE2("Peek(SERDAT ,%02x) at PC=%04x",(uint8)mUART_RX_DATA,mSystem.mCpu->GetPC()); return (uint8)(mUART_RX_DATA&0xff); break; case (IODAT&0xff): { uint32 retval=0; retval|=(mIODIR&0x10)?mIODAT&0x10:0x10; // IODIR = output bit : input high (eeprom write done) 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 (uint8)retval; } break; case (INTRST&0xff): case (INTSET&0xff): TRACE_MIKIE2("Peek(INTSET ,%02x) at PC=%04x",mTimerStatusFlags,mSystem.mCpu->GetPC()); return (uint8)mTimerStatusFlags; break; case (MAGRDY0&0xff): case (MAGRDY1&0xff): TRACE_MIKIE2("Peek(MAGRDY0/1,%02x) at PC=%04x",0x00,mSystem.mCpu->GetPC()); return 0x00; break; 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; break; case (MIKEYHREV&0xff): TRACE_MIKIE2("Peek(MIKEYHREV,%02x) at PC=%04x",0x01,mSystem.mCpu->GetPC()); return 0x01; break; // 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; 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("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)); break; // Errors on write only register accesses // For easier debugging case (DISPADRL&0xff): TRACE_MIKIE2("Peek(DISPADRL,%02x) at PC=%04x",(uint8)(mDisplayAddress&0xff),mSystem.mCpu->GetPC()); return (uint8)(mDisplayAddress&0xff); case (DISPADRH&0xff): TRACE_MIKIE2("Peek(DISPADRH,%02x) at PC=%04x",(uint8)(mDisplayAddress>>8)&0xff,mSystem.mCpu->GetPC()); return (uint8)(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()); // gError->Warning("EMULATOR DETECT REGISTER HAS BEEN READ"); return 0x42; break; // Errors on illegal location accesses default: TRACE_MIKIE2("Peek(%04x) - Peek from illegal location at PC=$%04x",addr,mSystem.mCpu->GetPC()); break; } return 0xff; } void CMikie::CombobulateSound(uint32 teatime) { int cur_lsample = 0; int cur_rsample = 0; int x; teatime >>= 2; for(x = 0; x < 4; x++) { /// Assumption (seems there is no documentation for the Attenuation registers) /// a) they are linear from $0 to $f /// b) an attenuation of $0 is equal to channel OFF (bits in mSTEREO not set) /// c) an attenuation of $f is equal to no attenuation (bits in PAN not set) /// These assumptions can only checked with an oszilloscope... /// the values stored in mSTEREO are bit-inverted ... /// mSTEREO was found to be set like that already (why?), but unused if(mSTEREO & (0x10 << x)) { if(mPAN & (0x10 << x)) cur_lsample += (mAUDIO_OUTPUT[x]*(mAUDIO_ATTEN[x]&0xF0))/(15*16); else cur_lsample += mAUDIO_OUTPUT[x]; } if(mSTEREO & (0x01 << x)) { if(mPAN & (0x01 << x)) cur_rsample += (mAUDIO_OUTPUT[x]*(mAUDIO_ATTEN[x]&0x0F))/15; else cur_rsample += mAUDIO_OUTPUT[x]; } } if(cur_lsample != last_lsample) { miksynth.offset_inline(teatime, cur_lsample - last_lsample, mikbuf.left()); last_lsample = cur_lsample; } if(cur_rsample != last_rsample) { miksynth.offset_inline(teatime, cur_rsample - last_rsample, mikbuf.right()); last_rsample = cur_rsample; } } void CMikie::CheckWrap() { // // To stop problems with cycle count wrap we will check and then correct the // cycle counter. // #define DEC(X) do { if((X)) { (X) -= 0x80000000; } } while (0) if(mSystem.gSystemCycleCount>0xf0000000) { DEC(mSystem.gSystemCycleCount); DEC(mTIM_0_LAST_COUNT); DEC(mTIM_1_LAST_COUNT); DEC(mTIM_2_LAST_COUNT); DEC(mTIM_3_LAST_COUNT); DEC(mTIM_4_LAST_COUNT); DEC(mTIM_5_LAST_COUNT); DEC(mTIM_6_LAST_COUNT); DEC(mTIM_7_LAST_COUNT); DEC(mAUDIO_LAST_COUNT[0]); DEC(mAUDIO_LAST_COUNT[1]); DEC(mAUDIO_LAST_COUNT[2]); DEC(mAUDIO_LAST_COUNT[3]); DEC(startTS); DEC(mSystem.gSuzieDoneTime); DEC(mSystem.gNextTimerEvent); } #undef DEC } void CMikie::Update() { int32 divide; int32 decval; uint32 tmp; uint32 mikie_work_done=0; // TRACE_MIKIE0("Update()"); mSystem.gNextTimerEvent=0xffffffff; if(mSystem.gSuzieDoneTime) { if(mSystem.gSystemCycleCount >= mSystem.gSuzieDoneTime) { ClearCPUSleep(); mSystem.gSuzieDoneTime = 0; } else if(mSystem.gSuzieDoneTime > mSystem.gSystemCycleCount) mSystem.gNextTimerEvent = mSystem.gSuzieDoneTime; } // 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 preidction 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 downto 1us == cyclecount >> 4 divide=(4+mTIM_0_LINKING); decval=(mSystem.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=(mSystem.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 = (mUART_Rx_output_ptr + 1) % 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 hasnt 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 neccessary // 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 underun, check and fix if(mTIM_4_CURRENT&0x80000000) { mTIM_4_CURRENT=mTIM_4_BKUP; mTIM_4_LAST_COUNT=mSystem.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=(mSystem.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=(mSystem.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=(mSystem.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=(mSystem.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=(mSystem.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_LINKING[y]); decval=(mSystem.gSystemCycleCount-mAUDIO_LAST_COUNT[y])>>divide; } if(decval) { mAUDIO_LAST_COUNT[y] += decval<127) temp=127; if(temp<-128) temp=-128; mAUDIO_OUTPUT[y]=(int8)temp; } else { if(mAUDIO_WAVESHAPER[y]&0x0001) mAUDIO_OUTPUT[y]=mAUDIO_VOLUME[y]; else mAUDIO_OUTPUT[y]=-mAUDIO_VOLUME[y]; } CombobulateSound(mSystem.gSystemCycleCount - startTS); } else { mAUDIO_BORROW_OUT[y]=FALSE; } // Set carry in as we did a count mAUDIO_BORROW_IN[y]=TRUE; } else { // Clear carry in as we didn't count mAUDIO_BORROW_IN[y]=FALSE; // Clear carry out mAUDIO_BORROW_OUT[y]=FALSE; } // Prediction for next timer event cycle number if(mAUDIO_LINKING[y]!=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_CURRENT[y]&0x80000000)?1:((mAUDIO_CURRENT[y]+1)<Warning("CMikie::Update() - gSystemCycleCount==gNextTimerEvent, system lock likely"); // TRACE_MIKIE1("Update() - NextTimerEvent = %012d",gNextTimerEvent); // Update system IRQ status as a result of timer activity // OR is required to ensure serial IRQ's are not masked accidentally mSystem.gSystemIRQ=(mTimerStatusFlags)?TRUE:FALSE; if(mSystem.gSystemIRQ && mSystem.gSystemCPUSleep) { ClearCPUSleep(); /*puts("ARLARM"); */ } //else if(gSuzieDoneTime) SetCPUSleep(); // Now all the timer updates are done we can increment the system // counter for any work done within the Update() function, gSystemCycleCounter // cannot be updated until this point otherwise it screws up the counters. mSystem.gSystemCycleCount+=mikie_work_done; } SYNCFUNC(CMikie) { NSS(startTS); // SSS(miksynth); // SSS(mikbuf); // mpDisplayCurrent; NSS(mpDisplayCurrentLine); NSS(framebuffer); NSS(last_lsample); NSS(last_rsample); NSS(mDisplayAddress); NSS(mAudioInputComparator); NSS(mTimerStatusFlags); NSS(mTimerInterruptMask); NSS(mPalette); NSS(mColourMap); NSS(mIODAT); NSS(mIODIR); NSS(mIODAT_REST_SIGNAL); NSS(mDISPCTL_DMAEnable); NSS(mDISPCTL_Flip); NSS(mDISPCTL_FourColour); NSS(mDISPCTL_Colour); NSS(mTIM_0_BKUP); NSS(mTIM_0_ENABLE_RELOAD); NSS(mTIM_0_ENABLE_COUNT); NSS(mTIM_0_LINKING); NSS(mTIM_0_CURRENT); NSS(mTIM_0_TIMER_DONE); NSS(mTIM_0_LAST_CLOCK); NSS(mTIM_0_BORROW_IN); NSS(mTIM_0_BORROW_OUT); NSS(mTIM_0_LAST_LINK_CARRY); NSS(mTIM_0_LAST_COUNT); NSS(mTIM_1_BKUP); NSS(mTIM_1_ENABLE_RELOAD); NSS(mTIM_1_ENABLE_COUNT); NSS(mTIM_1_LINKING); NSS(mTIM_1_CURRENT); NSS(mTIM_1_TIMER_DONE); NSS(mTIM_1_LAST_CLOCK); NSS(mTIM_1_BORROW_IN); NSS(mTIM_1_BORROW_OUT); NSS(mTIM_1_LAST_LINK_CARRY); NSS(mTIM_1_LAST_COUNT); NSS(mTIM_2_BKUP); NSS(mTIM_2_ENABLE_RELOAD); NSS(mTIM_2_ENABLE_COUNT); NSS(mTIM_2_LINKING); NSS(mTIM_2_CURRENT); NSS(mTIM_2_TIMER_DONE); NSS(mTIM_2_LAST_CLOCK); NSS(mTIM_2_BORROW_IN); NSS(mTIM_2_BORROW_OUT); NSS(mTIM_2_LAST_LINK_CARRY); NSS(mTIM_2_LAST_COUNT); NSS(mTIM_3_BKUP); NSS(mTIM_3_ENABLE_RELOAD); NSS(mTIM_3_ENABLE_COUNT); NSS(mTIM_3_LINKING); NSS(mTIM_3_CURRENT); NSS(mTIM_3_TIMER_DONE); NSS(mTIM_3_LAST_CLOCK); NSS(mTIM_3_BORROW_IN); NSS(mTIM_3_BORROW_OUT); NSS(mTIM_3_LAST_LINK_CARRY); NSS(mTIM_3_LAST_COUNT); NSS(mTIM_4_BKUP); NSS(mTIM_4_ENABLE_RELOAD); NSS(mTIM_4_ENABLE_COUNT); NSS(mTIM_4_LINKING); NSS(mTIM_4_CURRENT); NSS(mTIM_4_TIMER_DONE); NSS(mTIM_4_LAST_CLOCK); NSS(mTIM_4_BORROW_IN); NSS(mTIM_4_BORROW_OUT); NSS(mTIM_4_LAST_LINK_CARRY); NSS(mTIM_4_LAST_COUNT); NSS(mTIM_5_BKUP); NSS(mTIM_5_ENABLE_RELOAD); NSS(mTIM_5_ENABLE_COUNT); NSS(mTIM_5_LINKING); NSS(mTIM_5_CURRENT); NSS(mTIM_5_TIMER_DONE); NSS(mTIM_5_LAST_CLOCK); NSS(mTIM_5_BORROW_IN); NSS(mTIM_5_BORROW_OUT); NSS(mTIM_5_LAST_LINK_CARRY); NSS(mTIM_5_LAST_COUNT); NSS(mTIM_6_BKUP); NSS(mTIM_6_ENABLE_RELOAD); NSS(mTIM_6_ENABLE_COUNT); NSS(mTIM_6_LINKING); NSS(mTIM_6_CURRENT); NSS(mTIM_6_TIMER_DONE); NSS(mTIM_6_LAST_CLOCK); NSS(mTIM_6_BORROW_IN); NSS(mTIM_6_BORROW_OUT); NSS(mTIM_6_LAST_LINK_CARRY); NSS(mTIM_6_LAST_COUNT); NSS(mTIM_7_BKUP); NSS(mTIM_7_ENABLE_RELOAD); NSS(mTIM_7_ENABLE_COUNT); NSS(mTIM_7_LINKING); NSS(mTIM_7_CURRENT); NSS(mTIM_7_TIMER_DONE); NSS(mTIM_7_LAST_CLOCK); NSS(mTIM_7_BORROW_IN); NSS(mTIM_7_BORROW_OUT); NSS(mTIM_7_LAST_LINK_CARRY); NSS(mTIM_7_LAST_COUNT); NSS(mAUDIO_BKUP); NSS(mAUDIO_ENABLE_RELOAD); NSS(mAUDIO_ENABLE_COUNT); NSS(mAUDIO_LINKING); NSS(mAUDIO_CURRENT); NSS(mAUDIO_TIMER_DONE); NSS(mAUDIO_LAST_CLOCK); NSS(mAUDIO_BORROW_IN); NSS(mAUDIO_BORROW_OUT); NSS(mAUDIO_LAST_LINK_CARRY); NSS(mAUDIO_LAST_COUNT); NSS(mAUDIO_VOLUME); NSS(mAUDIO_INTEGRATE_ENABLE); NSS(mAUDIO_WAVESHAPER); NSS(mAUDIO_OUTPUT); NSS(mAUDIO_ATTEN); NSS(mSTEREO); NSS(mPAN); NSS(mUART_RX_IRQ_ENABLE); NSS(mUART_TX_IRQ_ENABLE); NSS(mUART_RX_COUNTDOWN); NSS(mUART_TX_COUNTDOWN); NSS(mUART_SENDBREAK); NSS(mUART_TX_DATA); NSS(mUART_RX_DATA); NSS(mUART_RX_READY); NSS(mUART_PARITY_ENABLE); NSS(mUART_PARITY_EVEN); NSS(mUART_CABLE_PRESENT); // mpUART_TX_CALLBACK; // mUART_TX_CALLBACK_OBJECT; NSS(mUART_Rx_input_queue); NSS(mUART_Rx_input_ptr); NSS(mUART_Rx_output_ptr); NSS(mUART_Rx_waiting); NSS(mUART_Rx_framing_error); NSS(mUART_Rx_overun_error); // mpRamPointer; NSS(mLynxLine); NSS(mLynxLineDMACounter); NSS(mLynxAddr); }