BizHawk/lynx/mikie.cpp

2748 lines
74 KiB
C++

//
// 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 <crtdbg.h>
//#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<UART_MAX_RX_QUEUE)
{
// Trigger incoming receive IF none waiting otherwise
// we NEVER get to receive it!!!
if(!mUART_Rx_waiting) mUART_RX_COUNTDOWN=UART_RX_TIME_PERIOD;
// Receive the byte
mUART_Rx_input_queue[mUART_Rx_input_ptr]=data;
mUART_Rx_input_ptr = (mUART_Rx_input_ptr + 1) % UART_MAX_RX_QUEUE;
mUART_Rx_waiting++;
TRACE_MIKIE2("ComLynxRxData() - input ptr=%02d waiting=%02d",mUART_Rx_input_ptr,mUART_Rx_waiting);
}
else
{
TRACE_MIKIE0("ComLynxRxData() - UART RX Overun");
}
}
void CMikie::ComLynxTxLoopback(int data)
{
TRACE_MIKIE1("ComLynxTxLoopback() - Received %04x",data);
if(mUART_Rx_waiting<UART_MAX_RX_QUEUE)
{
// Trigger incoming receive IF none waiting otherwise
// we NEVER get to receive it!!!
if(!mUART_Rx_waiting) mUART_RX_COUNTDOWN=UART_RX_TIME_PERIOD;
// Receive the byte - INSERT into front of queue
mUART_Rx_output_ptr = (mUART_Rx_output_ptr - 1) % UART_MAX_RX_QUEUE;
mUART_Rx_input_queue[mUART_Rx_output_ptr]=data;
mUART_Rx_waiting++;
TRACE_MIKIE2("ComLynxTxLoopback() - input ptr=%02d waiting=%02d",mUART_Rx_input_ptr,mUART_Rx_waiting);
}
else
{
TRACE_MIKIE0("ComLynxTxLoopback() - UART RX Overun");
}
}
void CMikie::ComLynxTxCallback(void (*function)(int data,uint32 objref),uint32 objref)
{
mpUART_TX_CALLBACK=function;
mUART_TX_CALLBACK_OBJECT=objref;
}
void CMikie::DisplaySetAttributes()
{
//
// Calculate the colour lookup tabes for the relevant mode
//
TPALETTE Spot;
for(Spot.Index=0;Spot.Index<4096;Spot.Index++)
{
mColourMap[Spot.Index] =
0xff000000 |
(Spot.Colours.Red * 15 + 30) << 16 |
(Spot.Colours.Green * 15 + 30) << 8 |
(Spot.Colours.Blue * 15 + 30);
}
}
void CMikie::BlankLineSurface()
{
uint32* bitmap_tmp = framebuffer + mpDisplayCurrentLine * SCREEN_WIDTH;
for (int i = 0; i < SCREEN_WIDTH; i++)
bitmap_tmp[i] = 0xff000000;
}
void CMikie::CopyLineSurface()
{
uint32* bitmap_tmp = framebuffer + mpDisplayCurrentLine * SCREEN_WIDTH;
for (int loop = 0; loop < SCREEN_WIDTH / 2; loop++)
{
uint32 source = mpRamPointer[(uint16)mLynxAddr];
if(mDISPCTL_Flip)
{
mLynxAddr--;
*bitmap_tmp=mColourMap[mPalette[source&0x0f].Index];
bitmap_tmp++;
*bitmap_tmp=mColourMap[mPalette[source>>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<<divide;
mTIM_0_CURRENT-=decval;
if(mTIM_0_CURRENT&0x80000000)
{
// Set carry out
mTIM_0_BORROW_OUT=TRUE;
// // Reload if neccessary
// if(mTIM_0_ENABLE_RELOAD)
// {
mTIM_0_CURRENT+=mTIM_0_BKUP+1;
// }
// else
// {
// mTIM_0_CURRENT=0;
// }
mTIM_0_TIMER_DONE=TRUE;
// Interupt flag setting code moved into DisplayRenderLine()
// Line timer has expired, render a line, we cannot incrememnt
// the global counter at this point as it will screw the other timers
// so we save under work done and inc at the end.
mikie_work_done+=DisplayRenderLine();
}
else
{
mTIM_0_BORROW_OUT=FALSE;
}
// Set carry in as we did a count
mTIM_0_BORROW_IN=TRUE;
}
else
{
// Clear carry in as we didn't count
mTIM_0_BORROW_IN=FALSE;
// Clear carry out
mTIM_0_BORROW_OUT=FALSE;
}
}
// Prediction for next timer event cycle number
// if(mTIM_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=(mTIM_0_CURRENT&0x80000000)?1:((mTIM_0_CURRENT+1)<<divide);
tmp+=mSystem.gSystemCycleCount;
if(tmp<mSystem.gNextTimerEvent)
{
mSystem.gNextTimerEvent=tmp;
// TRACE_MIKIE1("Update() - TIMER 0 Set NextTimerEvent = %012d",gNextTimerEvent);
}
}
// TRACE_MIKIE1("Update() - mTIM_0_CURRENT = %012d",mTIM_0_CURRENT);
// TRACE_MIKIE1("Update() - mTIM_0_BKUP = %012d",mTIM_0_BKUP);
// TRACE_MIKIE1("Update() - mTIM_0_LASTCNT = %012d",mTIM_0_LAST_COUNT);
// TRACE_MIKIE1("Update() - mTIM_0_LINKING = %012d",mTIM_0_LINKING);
}
//
// Timer 2 of Group A
//
//
// Optimisation, assume T2 (Frame timer) is never in one-shot
// always in linked mode i.e clocked by Line Timer
//
// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..)
// if(mTIM_2_ENABLE_COUNT && (mTIM_2_ENABLE_RELOAD || !mTIM_2_TIMER_DONE))
if(mTIM_2_ENABLE_COUNT)
{
decval=0;
// if(mTIM_2_LINKING==0x07)
{
if(mTIM_0_BORROW_OUT) decval=1;
mTIM_2_LAST_LINK_CARRY=mTIM_0_BORROW_OUT;
divide = 0;
}
// else
// {
// // Ordinary clocked mode as opposed to linked mode
// // 16MHz clock downto 1us == cyclecount >> 4
// divide=(4+mTIM_2_LINKING);
// decval=(gSystemCycleCount-mTIM_2_LAST_COUNT)>>divide;
// }
if(decval)
{
// mTIM_2_LAST_COUNT+=decval<<divide;
mTIM_2_CURRENT-=decval;
if(mTIM_2_CURRENT&0x80000000)
{
// Set carry out
mTIM_2_BORROW_OUT=TRUE;
// // Reload if neccessary
// if(mTIM_2_ENABLE_RELOAD)
// {
mTIM_2_CURRENT+=mTIM_2_BKUP+1;
// }
// else
// {
// mTIM_2_CURRENT=0;
// }
mTIM_2_TIMER_DONE=TRUE;
// Interupt flag setting code moved into DisplayEndOfFrame(), also
// park any CPU cycles lost for later inclusion
mikie_work_done+=DisplayEndOfFrame();
}
else
{
mTIM_2_BORROW_OUT=FALSE;
}
// Set carry in as we did a count
mTIM_2_BORROW_IN=TRUE;
}
else
{
// Clear carry in as we didn't count
mTIM_2_BORROW_IN=FALSE;
// Clear carry out
mTIM_2_BORROW_OUT=FALSE;
}
// Prediction for next timer event cycle number
// We dont need to predict this as its the frame timer and will always
// be beaten by the line timer on Timer 0
// if(mTIM_2_LINKING!=7)
// {
// tmp=gSystemCycleCount+((mTIM_2_CURRENT+1)<<divide);
// if(tmp<gNextTimerEvent) gNextTimerEvent=tmp;
// }
// TRACE_MIKIE1("Update() - mTIM_2_CURRENT = %012d",mTIM_2_CURRENT);
// TRACE_MIKIE1("Update() - mTIM_2_BKUP = %012d",mTIM_2_BKUP);
// TRACE_MIKIE1("Update() - mTIM_2_LASTCNT = %012d",mTIM_2_LAST_COUNT);
// TRACE_MIKIE1("Update() - mTIM_2_LINKING = %012d",mTIM_2_LINKING);
}
//
// Timer 4 of Group A
//
// For the sake of speed it is assumed that Timer 4 (UART timer)
// never uses one-shot mode, never uses linking, hence the code
// is commented out. Timer 4 is at the end of a chain and seems
// no reason to update its carry in-out variables
//
// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..)
// if(mTIM_4_ENABLE_COUNT && (mTIM_4_ENABLE_RELOAD || !mTIM_4_TIMER_DONE))
if(mTIM_4_ENABLE_COUNT)
{
decval=0;
// if(mTIM_4_LINKING==0x07)
// {
//// if(mTIM_2_BORROW_OUT && !mTIM_4_LAST_LINK_CARRY) decval=1;
// if(mTIM_2_BORROW_OUT) decval=1;
// mTIM_4_LAST_LINK_CARRY=mTIM_2_BORROW_OUT;
// }
// else
{
// Ordinary clocked mode as opposed to linked mode
// 16MHz clock downto 1us == cyclecount >> 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<<divide;
mTIM_4_CURRENT-=decval;
if(mTIM_4_CURRENT&0x80000000)
{
// Set carry out
mTIM_4_BORROW_OUT=TRUE;
//
// Update the UART counter models for Rx & Tx
//
//
// According to the docs IRQ's are level triggered and hence will always assert
// what a pain in the arse
//
// Rx & Tx are loopedback due to comlynx structure
//
// Receive
//
if(!mUART_RX_COUNTDOWN)
{
// Fetch a byte from the input queue
if(mUART_Rx_waiting>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)<<divide);
tmp+=mSystem.gSystemCycleCount;
if(tmp<mSystem.gNextTimerEvent)
{
mSystem.gNextTimerEvent=tmp;
TRACE_MIKIE1("Update() - TIMER 4 Set NextTimerEvent = %012d",gNextTimerEvent);
}
// }
// TRACE_MIKIE1("Update() - mTIM_4_CURRENT = %012d",mTIM_4_CURRENT);
// TRACE_MIKIE1("Update() - mTIM_4_BKUP = %012d",mTIM_4_BKUP);
// TRACE_MIKIE1("Update() - mTIM_4_LASTCNT = %012d",mTIM_4_LAST_COUNT);
// TRACE_MIKIE1("Update() - mTIM_4_LINKING = %012d",mTIM_4_LINKING);
}
// Emulate the UART bug where UART IRQ is level sensitive
// in that it will continue to generate interrupts as long
// as they are enabled and the interrupt condition is true
// If Tx is inactive i.e ready for a byte to eat and the
// IRQ is enabled then generate it always
if((mUART_TX_COUNTDOWN&UART_TX_INACTIVE) && mUART_TX_IRQ_ENABLE)
{
TRACE_MIKIE0("Update() - UART TX IRQ Triggered");
mTimerStatusFlags|=0x10;
}
// Is data waiting and the interrupt enabled, if so then
// what are we waiting for....
if(mUART_RX_READY && mUART_RX_IRQ_ENABLE)
{
TRACE_MIKIE0("Update() - UART RX IRQ Triggered");
mTimerStatusFlags|=0x10;
}
//
// Timer 1 of Group B
//
// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..)
if(mTIM_1_ENABLE_COUNT && (mTIM_1_ENABLE_RELOAD || !mTIM_1_TIMER_DONE))
{
divide = 0;
if(mTIM_1_LINKING!=0x07)
{
// Ordinary clocked mode as opposed to linked mode
// 16MHz clock downto 1us == cyclecount >> 4
divide=(4+mTIM_1_LINKING);
decval=(mSystem.gSystemCycleCount-mTIM_1_LAST_COUNT)>>divide;
if(decval)
{
mTIM_1_LAST_COUNT+=decval<<divide;
mTIM_1_CURRENT-=decval;
if(mTIM_1_CURRENT&0x80000000)
{
// Set carry out
mTIM_1_BORROW_OUT=TRUE;
// Set the timer status flag
if(mTimerInterruptMask&0x02)
{
TRACE_MIKIE0("Update() - TIMER1 IRQ Triggered");
mTimerStatusFlags|=0x02;
}
// Reload if neccessary
if(mTIM_1_ENABLE_RELOAD)
{
mTIM_1_CURRENT+=mTIM_1_BKUP+1;
}
else
{
mTIM_1_CURRENT=0;
}
mTIM_1_TIMER_DONE=TRUE;
}
else
{
mTIM_1_BORROW_OUT=FALSE;
}
// Set carry in as we did a count
mTIM_1_BORROW_IN=TRUE;
}
else
{
// Clear carry in as we didn't count
mTIM_1_BORROW_IN=FALSE;
// Clear carry out
mTIM_1_BORROW_OUT=FALSE;
}
}
// Prediction for next timer event cycle number
if(mTIM_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=(mTIM_1_CURRENT&0x80000000)?1:((mTIM_1_CURRENT+1)<<divide);
tmp+=mSystem.gSystemCycleCount;
if(tmp<mSystem.gNextTimerEvent)
{
mSystem.gNextTimerEvent=tmp;
TRACE_MIKIE1("Update() - TIMER 1 Set NextTimerEvent = %012d",gNextTimerEvent);
}
}
// TRACE_MIKIE1("Update() - mTIM_1_CURRENT = %012d",mTIM_1_CURRENT);
// TRACE_MIKIE1("Update() - mTIM_1_BKUP = %012d",mTIM_1_BKUP);
// TRACE_MIKIE1("Update() - mTIM_1_LASTCNT = %012d",mTIM_1_LAST_COUNT);
// TRACE_MIKIE1("Update() - mTIM_1_LINKING = %012d",mTIM_1_LINKING);
}
//
// Timer 3 of Group A
//
// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..)
if(mTIM_3_ENABLE_COUNT && (mTIM_3_ENABLE_RELOAD || !mTIM_3_TIMER_DONE))
{
decval=0;
if(mTIM_3_LINKING==0x07)
{
if(mTIM_1_BORROW_OUT) decval=1;
mTIM_3_LAST_LINK_CARRY=mTIM_1_BORROW_OUT;
divide = 0;
}
else
{
// Ordinary clocked mode as opposed to linked mode
// 16MHz clock downto 1us == cyclecount >> 4
divide=(4+mTIM_3_LINKING);
decval=(mSystem.gSystemCycleCount-mTIM_3_LAST_COUNT)>>divide;
}
if(decval)
{
mTIM_3_LAST_COUNT+=decval<<divide;
mTIM_3_CURRENT-=decval;
if(mTIM_3_CURRENT&0x80000000)
{
// Set carry out
mTIM_3_BORROW_OUT=TRUE;
// Set the timer status flag
if(mTimerInterruptMask&0x08)
{
TRACE_MIKIE0("Update() - TIMER3 IRQ Triggered");
mTimerStatusFlags|=0x08;
}
// Reload if neccessary
if(mTIM_3_ENABLE_RELOAD)
{
mTIM_3_CURRENT+=mTIM_3_BKUP+1;
}
else
{
mTIM_3_CURRENT=0;
}
mTIM_3_TIMER_DONE=TRUE;
}
else
{
mTIM_3_BORROW_OUT=FALSE;
}
// Set carry in as we did a count
mTIM_3_BORROW_IN=TRUE;
}
else
{
// Clear carry in as we didn't count
mTIM_3_BORROW_IN=FALSE;
// Clear carry out
mTIM_3_BORROW_OUT=FALSE;
}
// Prediction for next timer event cycle number
if(mTIM_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=(mTIM_3_CURRENT&0x80000000)?1:((mTIM_3_CURRENT+1)<<divide);
tmp+=mSystem.gSystemCycleCount;
if(tmp<mSystem.gNextTimerEvent)
{
mSystem.gNextTimerEvent=tmp;
TRACE_MIKIE1("Update() - TIMER 3 Set NextTimerEvent = %012d",gNextTimerEvent);
}
}
// TRACE_MIKIE1("Update() - mTIM_3_CURRENT = %012d",mTIM_3_CURRENT);
// TRACE_MIKIE1("Update() - mTIM_3_BKUP = %012d",mTIM_3_BKUP);
// TRACE_MIKIE1("Update() - mTIM_3_LASTCNT = %012d",mTIM_3_LAST_COUNT);
// TRACE_MIKIE1("Update() - mTIM_3_LINKING = %012d",mTIM_3_LINKING);
}
//
// Timer 5 of Group A
//
// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..)
if(mTIM_5_ENABLE_COUNT && (mTIM_5_ENABLE_RELOAD || !mTIM_5_TIMER_DONE))
{
decval=0;
if(mTIM_5_LINKING==0x07)
{
if(mTIM_3_BORROW_OUT) decval=1;
mTIM_5_LAST_LINK_CARRY=mTIM_3_BORROW_OUT;
divide = 0;
}
else
{
// Ordinary clocked mode as opposed to linked mode
// 16MHz clock downto 1us == cyclecount >> 4
divide=(4+mTIM_5_LINKING);
decval=(mSystem.gSystemCycleCount-mTIM_5_LAST_COUNT)>>divide;
}
if(decval)
{
mTIM_5_LAST_COUNT+=decval<<divide;
mTIM_5_CURRENT-=decval;
if(mTIM_5_CURRENT&0x80000000)
{
// Set carry out
mTIM_5_BORROW_OUT=TRUE;
// Set the timer status flag
if(mTimerInterruptMask&0x20)
{
TRACE_MIKIE0("Update() - TIMER5 IRQ Triggered");
mTimerStatusFlags|=0x20;
}
// Reload if neccessary
if(mTIM_5_ENABLE_RELOAD)
{
mTIM_5_CURRENT+=mTIM_5_BKUP+1;
}
else
{
mTIM_5_CURRENT=0;
}
mTIM_5_TIMER_DONE=TRUE;
}
else
{
mTIM_5_BORROW_OUT=FALSE;
}
// Set carry in as we did a count
mTIM_5_BORROW_IN=TRUE;
}
else
{
// Clear carry in as we didn't count
mTIM_5_BORROW_IN=FALSE;
// Clear carry out
mTIM_5_BORROW_OUT=FALSE;
}
// Prediction for next timer event cycle number
if(mTIM_5_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_5_CURRENT&0x80000000)?1:((mTIM_5_CURRENT+1)<<divide);
tmp+=mSystem.gSystemCycleCount;
if(tmp<mSystem.gNextTimerEvent)
{
mSystem.gNextTimerEvent=tmp;
TRACE_MIKIE1("Update() - TIMER 5 Set NextTimerEvent = %012d",gNextTimerEvent);
}
}
// TRACE_MIKIE1("Update() - mTIM_5_CURRENT = %012d",mTIM_5_CURRENT);
// TRACE_MIKIE1("Update() - mTIM_5_BKUP = %012d",mTIM_5_BKUP);
// TRACE_MIKIE1("Update() - mTIM_5_LASTCNT = %012d",mTIM_5_LAST_COUNT);
// TRACE_MIKIE1("Update() - mTIM_5_LINKING = %012d",mTIM_5_LINKING);
}
//
// Timer 7 of Group A
//
// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..)
if(mTIM_7_ENABLE_COUNT && (mTIM_7_ENABLE_RELOAD || !mTIM_7_TIMER_DONE))
{
decval=0;
if(mTIM_7_LINKING==0x07)
{
if(mTIM_5_BORROW_OUT) decval=1;
mTIM_7_LAST_LINK_CARRY=mTIM_5_BORROW_OUT;
divide = 0;
}
else
{
// Ordinary clocked mode as opposed to linked mode
// 16MHz clock downto 1us == cyclecount >> 4
divide=(4+mTIM_7_LINKING);
decval=(mSystem.gSystemCycleCount-mTIM_7_LAST_COUNT)>>divide;
}
if(decval)
{
mTIM_7_LAST_COUNT+=decval<<divide;
mTIM_7_CURRENT-=decval;
if(mTIM_7_CURRENT&0x80000000)
{
// Set carry out
mTIM_7_BORROW_OUT=TRUE;
// Set the timer status flag
if(mTimerInterruptMask&0x80)
{
TRACE_MIKIE0("Update() - TIMER7 IRQ Triggered");
mTimerStatusFlags|=0x80;
}
// Reload if neccessary
if(mTIM_7_ENABLE_RELOAD)
{
mTIM_7_CURRENT+=mTIM_7_BKUP+1;
}
else
{
mTIM_7_CURRENT=0;
}
mTIM_7_TIMER_DONE=TRUE;
}
else
{
mTIM_7_BORROW_OUT=FALSE;
}
// Set carry in as we did a count
mTIM_7_BORROW_IN=TRUE;
}
else
{
// Clear carry in as we didn't count
mTIM_7_BORROW_IN=FALSE;
// Clear carry out
mTIM_7_BORROW_OUT=FALSE;
}
// Prediction for next timer event cycle number
if(mTIM_7_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_7_CURRENT&0x80000000)?1:((mTIM_7_CURRENT+1)<<divide);
tmp+=mSystem.gSystemCycleCount;
if(tmp<mSystem.gNextTimerEvent)
{
mSystem.gNextTimerEvent=tmp;
TRACE_MIKIE1("Update() - TIMER 7 Set NextTimerEvent = %012d",gNextTimerEvent);
}
}
// TRACE_MIKIE1("Update() - mTIM_7_CURRENT = %012d",mTIM_7_CURRENT);
// TRACE_MIKIE1("Update() - mTIM_7_BKUP = %012d",mTIM_7_BKUP);
// TRACE_MIKIE1("Update() - mTIM_7_LASTCNT = %012d",mTIM_7_LAST_COUNT);
// TRACE_MIKIE1("Update() - mTIM_7_LINKING = %012d",mTIM_7_LINKING);
}
//
// Timer 6 has no group
//
// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..)
if(mTIM_6_ENABLE_COUNT && (mTIM_6_ENABLE_RELOAD || !mTIM_6_TIMER_DONE))
{
// if(mTIM_6_LINKING!=0x07)
{
// Ordinary clocked mode as opposed to linked mode
// 16MHz clock downto 1us == cyclecount >> 4
divide=(4+mTIM_6_LINKING);
decval=(mSystem.gSystemCycleCount-mTIM_6_LAST_COUNT)>>divide;
if(decval)
{
mTIM_6_LAST_COUNT+=decval<<divide;
mTIM_6_CURRENT-=decval;
if(mTIM_6_CURRENT&0x80000000)
{
// Set carry out
mTIM_6_BORROW_OUT=TRUE;
// Set the timer status flag
if(mTimerInterruptMask&0x40)
{
TRACE_MIKIE0("Update() - TIMER6 IRQ Triggered");
mTimerStatusFlags|=0x40;
}
// Reload if neccessary
if(mTIM_6_ENABLE_RELOAD)
{
mTIM_6_CURRENT+=mTIM_6_BKUP+1;
}
else
{
mTIM_6_CURRENT=0;
}
mTIM_6_TIMER_DONE=TRUE;
}
else
{
mTIM_6_BORROW_OUT=FALSE;
}
// Set carry in as we did a count
mTIM_6_BORROW_IN=TRUE;
}
else
{
// Clear carry in as we didn't count
mTIM_6_BORROW_IN=FALSE;
// Clear carry out
mTIM_6_BORROW_OUT=FALSE;
}
}
// Prediction for next timer event cycle number
// (Timer 6 doesn't support linking)
// if(mTIM_6_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_6_CURRENT&0x80000000)?1:((mTIM_6_CURRENT+1)<<divide);
tmp+=mSystem.gSystemCycleCount;
if(tmp<mSystem.gNextTimerEvent)
{
mSystem.gNextTimerEvent=tmp;
TRACE_MIKIE1("Update() - TIMER 6 Set NextTimerEvent = %012d",gNextTimerEvent);
}
}
// TRACE_MIKIE1("Update() - mTIM_6_CURRENT = %012d",mTIM_6_CURRENT);
// TRACE_MIKIE1("Update() - mTIM_6_BKUP = %012d",mTIM_6_BKUP);
// TRACE_MIKIE1("Update() - mTIM_6_LASTCNT = %012d",mTIM_6_LAST_COUNT);
// TRACE_MIKIE1("Update() - mTIM_6_LINKING = %012d",mTIM_6_LINKING);
}
//
// Update the sound subsystem
//
{
int y;
for(y = 0; y < 4; y++)
{
if(mAUDIO_ENABLE_COUNT[y] && (mAUDIO_ENABLE_RELOAD[y] || !mAUDIO_TIMER_DONE[y]))
{
decval=0;
if(mAUDIO_LINKING[y]==0x07)
{
int bort;
if(y)
bort = mAUDIO_BORROW_OUT[y - 1];
else
bort = mTIM_7_BORROW_OUT;
if(bort) decval=1;
mAUDIO_LAST_LINK_CARRY[y]=bort;
divide = 0;
}
else
{
// Ordinary clocked mode as opposed to linked mode
// 16MHz clock downto 1us == cyclecount >> 4
divide=(4+mAUDIO_LINKING[y]);
decval=(mSystem.gSystemCycleCount-mAUDIO_LAST_COUNT[y])>>divide;
}
if(decval)
{
mAUDIO_LAST_COUNT[y] += decval<<divide;
mAUDIO_CURRENT[y]-=decval;
if(mAUDIO_CURRENT[y]&0x80000000)
{
// Set carry out
mAUDIO_BORROW_OUT[y]=TRUE;
// Reload if neccessary
if(mAUDIO_ENABLE_RELOAD[y])
{
mAUDIO_CURRENT[y]+=mAUDIO_BKUP[y]+1;
if(mAUDIO_CURRENT[y]&0x80000000) mAUDIO_CURRENT[y]=0;
}
else
{
// Set timer done
mAUDIO_TIMER_DONE[y]=TRUE;
mAUDIO_CURRENT[y]=0;
}
//
// Update audio circuitry
//
if(mAUDIO_BKUP[y] || mAUDIO_LINKING[y])
mAUDIO_WAVESHAPER[y] = GetLfsrNext(mAUDIO_WAVESHAPER[y]);
if(mAUDIO_INTEGRATE_ENABLE[y])
{
int32 temp=mAUDIO_OUTPUT[y];
if(mAUDIO_WAVESHAPER[y]&0x0001) temp+=mAUDIO_VOLUME[y]; else temp-=mAUDIO_VOLUME[y];
if(temp>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)<<divide);
tmp+=mSystem.gSystemCycleCount;
if(tmp<mSystem.gNextTimerEvent)
{
mSystem.gNextTimerEvent=tmp;
TRACE_MIKIE1("Update() - AUDIO 0 Set NextTimerEvent = %012d",gNextTimerEvent);
}
}
}
}
}
// if(gSystemCycleCount==gNextTimerEvent) gError->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);
}