48k - new ULA implementation - 80% faster

This commit is contained in:
Asnivor 2017-12-11 14:08:00 +00:00
parent 2b988954ee
commit a5b50fe547
15 changed files with 141 additions and 259 deletions

View File

@ -106,17 +106,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// Helper function to refresh memory array (probably not the best way to do things)
/// </summary>
public abstract void ReInitMemory();
/// <summary>
/// Returns the memory contention value for the specified T-State (cycle)
/// The ZX Spectrum memory access is contended when the ULA is accessing the lower 16k of RAM
/// </summary>
/// <param name="Cycle"></param>
/// <returns></returns>
public virtual byte GetContentionValue(int cycle)
{
var val = _renderingCycleTable[cycle % UlaFrameCycleCount].ContentionDelay;
return val;
}
}
}

View File

@ -30,28 +30,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="port"></param>
/// <param name="value"></param>
public abstract void WritePort(ushort port, byte value);
/// <summary>
/// Apply I/O contention if necessary
/// </summary>
/// <param name="port"></param>
public virtual void ContendPort(ushort port)
{
var lowBit = (port & 0x0001) != 0;
var ulaHigh = (port & 0xc000) == 0x4000;
var cfc = CurrentFrameCycle;
if (cfc < 1)
cfc = 1;
if (ulaHigh)
{
CPU.TotalExecutedCycles += GetContentionValue(cfc - 1);
}
else
{
if (!lowBit)
CPU.TotalExecutedCycles += GetContentionValue(cfc);
}
}
}
}

View File

@ -27,6 +27,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
copies or substantial portions of the Software.
*/
/*
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Screen *
@ -927,7 +929,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/*
public int VsyncNumerator => NullVideo.DefaultVsyncNum;
public int VsyncDenominator => NullVideo.DefaultVsyncDen;
*/
public int[] GetVideoBuffer()
{
/*
@ -965,7 +967,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
case ZXSpectrum.BorderType.Medium:
break;
}
*/
return _frameBuffer;
}
@ -973,4 +975,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
#endregion
}
*/
}

View File

@ -120,10 +120,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
BuzzerDevice.StartFrame();
if (AYDevice != null)
AYDevice.StartFrame();
PollInput();
var curr = CPU.TotalExecutedCycles;
while (CurrentFrameCycle <= ULADevice.FrameLength) // UlaFrameCycleCount)
{
// check for interrupt
@ -132,13 +131,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// run a single CPU instruction
CPU.ExecuteOne();
// run a rendering cycle according to the current CPU cycle count
/*
var lastCycle = CurrentFrameCycle;
RenderScreen(LastRenderedULACycle + 1, lastCycle);
LastRenderedULACycle = lastCycle;
*/
// update AY
if (AYDevice != null)
AYDevice.UpdateSound(CurrentFrameCycle);
@ -146,9 +138,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// we have reached the end of a frame
LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow;
LastRenderedULACycle = OverFlow;
ULADevice.UpdateScreenBuffer(ULADevice.FrameLength);
// paint the buffer if needed
if (ULADevice.needsPaint)
ULADevice.UpdateScreenBuffer(ULADevice.FrameLength);
BuzzerDevice.EndFrame();
@ -157,16 +150,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
FrameCount++;
// setup for next frame
OverFlow = CurrentFrameCycle % UlaFrameCycleCount;
ULADevice.ResetInterrupt();
FrameCompleted = true;
if (FrameCount % FlashToggleFrames == 0)
{
_flashPhase = !_flashPhase;
}
//RenderScreen(0, OverFlow);
}
/// <summary>
@ -174,8 +159,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public virtual void HardReset()
{
ResetBorder();
ResetInterrupt();
//ResetBorder();
ULADevice.ResetInterrupt();
}
/// <summary>
@ -183,8 +168,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public virtual void SoftReset()
{
ResetBorder();
ResetInterrupt();
//ResetBorder();
ULADevice.ResetInterrupt();
}
public void SyncState(Serializer ser)
@ -195,50 +180,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
ser.Sync("FrameCount", ref FrameCount);
ser.Sync("_frameCycles", ref _frameCycles);
ser.Sync("LastFrameStartCPUTick", ref LastFrameStartCPUTick);
ser.Sync("LastULAOutByte", ref LastULAOutByte);
ser.Sync("_flashPhase", ref _flashPhase);
ser.Sync("_frameBuffer", ref _frameBuffer, false);
ser.Sync("_flashOffColors", ref _flashOffColors, false);
ser.Sync("_flashOnColors", ref _flashOnColors, false);
ser.Sync("InterruptCycle", ref InterruptCycle);
ser.Sync("InterruptRaised", ref InterruptRaised);
ser.Sync("InterruptRevoked", ref InterruptRevoked);
ser.Sync("UlaFrameCycleCount", ref UlaFrameCycleCount);
ser.Sync("FirstScreenPixelCycle", ref FirstScreenPixelCycle);
ser.Sync("FirstDisplayPixelCycle", ref FirstDisplayPixelCycle);
ser.Sync("FirstPixelCycleInLine", ref FirstPixelCycleInLine);
ser.Sync("AttributeDataPrefetchTime", ref AttributeDataPrefetchTime);
ser.Sync("PixelDataPrefetchTime", ref PixelDataPrefetchTime);
ser.Sync("ScreenLineTime", ref ScreenLineTime);
ser.Sync("NonVisibleBorderRightTime", ref NonVisibleBorderRightTime);
ser.Sync("BorderRightTime", ref BorderRightTime);
ser.Sync("DisplayLineTime", ref DisplayLineTime);
ser.Sync("BorderLeftTime", ref BorderLeftTime);
ser.Sync("HorizontalBlankingTime", ref HorizontalBlankingTime);
ser.Sync("ScreenWidth", ref ScreenWidth);
ser.Sync("BorderRightPixels", ref BorderRightPixels);
ser.Sync("BorderLeftPixels", ref BorderLeftPixels);
ser.Sync("FirstDisplayLine", ref FirstDisplayLine);
ser.Sync("ScreenLines", ref ScreenLines);
ser.Sync("NonVisibleBorderBottomLines", ref NonVisibleBorderBottomLines);
ser.Sync("BorderBottomLines", ref BorderBottomLines);
ser.Sync("BorderTopLines", ref BorderTopLines);
ser.Sync("NonVisibleBorderTopLines", ref NonVisibleBorderTopLines);
ser.Sync("VerticalSyncLines", ref VerticalSyncLines);
ser.Sync("FlashToggleFrames", ref FlashToggleFrames);
ser.Sync("DisplayLines", ref DisplayLines);
ser.Sync("DisplayWidth", ref DisplayWidth);
ser.Sync("_pixelByte1", ref _pixelByte1);
ser.Sync("_pixelByte2", ref _pixelByte2);
ser.Sync("_attrByte1", ref _attrByte1);
ser.Sync("_attrByte2", ref _attrByte2);
ser.Sync("_xPos", ref _xPos);
ser.Sync("_yPos", ref _yPos);
ser.Sync("DisplayWidth", ref DisplayWidth);
ser.Sync("DisplayWidth", ref DisplayWidth);
ser.Sync("DisplayWidth", ref DisplayWidth);
ser.Sync("DisplayWidth", ref DisplayWidth);
ser.Sync("_borderColour", ref _borderColour);
ser.Sync("LastULAOutByte", ref LastULAOutByte);
ser.Sync("ROM0", ref ROM0, false);
ser.Sync("ROM1", ref ROM1, false);
ser.Sync("ROM2", ref ROM2, false);

View File

@ -6,11 +6,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Another ULA implementation (maybe it will be more performant & accurate)
/// -edit: it is :)
/// </summary>
public abstract class ULABase : IVideoProvider
{
#region ULA Configuration
#region General
/// <summary>
@ -65,15 +64,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
#endregion
#region Interrupts
/// <summary>
/// The number of T-States that the INT pin is simulated to be held low
/// </summary>
public int InterruptPeriod;
#endregion
#region Contention
/// <summary>
@ -142,6 +132,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
protected bool flashOn = false;
protected int flashCounter;
public int FlashCounter
{
get { return flashCounter; }
set
{
flashCounter = value;
}
}
/// <summary>
/// Internal frame counter used for flasher operations
/// </summary>
protected int frameCounter = 0;
/// <summary>
/// Last 8-bit bitmap read from display memory
/// (Floating bus implementation)
@ -230,10 +237,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
protected int AttributeLength;
/// <summary>
/// Raised when ULA has finished painting the entire screen
/// </summary>
public bool needsPaint = false;
#endregion
#region Interrupt
/// <summary>
/// The number of T-States that the INT pin is simulated to be held low
/// </summary>
public int InterruptPeriod;
/// <summary>
/// The longest instruction cycle count
/// </summary>
@ -282,9 +299,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// interrupt should have already been raised and the cpu may or
// may not have caught it. The time has passed so revoke the signal
InterruptRevoked = true;
//CPU.IFF1 = true;
_machine.CPU.FlagI = false;
//CPU.NonMaskableInterruptPending = true;
}
@ -295,23 +310,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
return;
}
// if CPU is masking the interrupt do not raise it
//if (!CPU.IFF1)
//return;
// Raise the interrupt
InterruptRaised = true;
//CPU.IFF1 = false;
//CPU.IFF2 = false;
_machine.CPU.FlagI = true;
//FrameCount++;
ULAUpdateStart();
// Signal the start of ULA processing
ULAUpdateStart();
}
#endregion
#endregion
#endregion
#region Construction & Initialisation
@ -320,11 +327,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
_machine = machine;
}
public virtual void Init()
{
}
#endregion
#region Methods
@ -379,6 +381,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
ULAByteCtr = 0;
screenByteCtr = DisplayStart;
lastTState = actualULAStart;
needsPaint = true;
flashCounter++;
if (flashCounter > 15)
{
flashOn = !flashOn;
flashCounter = 0;
}
}
/// <summary>
@ -406,6 +417,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
}
/// <summary>
/// Updates the screen buffer based on the number of T-States supplied
/// </summary>
/// <param name="_tstates"></param>
public virtual void UpdateScreenBuffer(int _tstates)
{
if (_tstates < actualULAStart)
@ -415,6 +430,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
else if (_tstates >= FrameLength)
{
_tstates = FrameLength - 1;
needsPaint = true;
}
//the additional 1 tstate is required to get correct number of bytes to output in ircontention.sna
@ -542,6 +559,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
#endregion
#region IStatable
public void SyncState(Serializer ser)
{
ser.BeginSection("ULA");
ser.Sync("ScreenBuffer", ref ScreenBuffer, false);
ser.Sync("FrameLength", ref FrameLength);
ser.Sync("ClockSpeed", ref ClockSpeed);
ser.Sync("LateTiming", ref LateTiming);
ser.Sync("borderColour", ref borderColour);
ser.EndSection();
}
#endregion
#region Attribution

View File

@ -168,6 +168,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
default:
break;
}
// update ULA screen buffer if necessary
if ((addr & 49152) == 16384)
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
}
/// <summary>
@ -178,13 +182,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
var data = ReadBus(addr);
if ((addr & 0xC000) == 0x4000)
{
// addr is in RAM not ROM - apply memory contention if neccessary
var delay = GetContentionValue(CurrentFrameCycle);
CPU.TotalExecutedCycles += delay;
}
return data;
}
@ -196,17 +197,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
if (addr < 0x4000)
{
// Do nothing - we cannot write to ROM
return;
}
else if (addr < 0xC000)
{
// possible contended RAM
var delay = GetContentionValue(CurrentFrameCycle);
CPU.TotalExecutedCycles += delay;
}
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
WriteBus(addr, value);
}

View File

@ -8,8 +8,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public partial class ZX128 : SpectrumBase
{
private int AYTStates = 0;
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>
@ -23,7 +21,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
ContendPort((ushort)port);
ULADevice.Contend(port);
CPU.TotalExecutedCycles++;
// Kempston Joystick
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
@ -124,9 +123,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// if unused port the floating memory bus should be returned (still todo)
}
CPU.TotalExecutedCycles += 3;
return (byte)result;
}
@ -170,13 +167,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x01) == 0;
ContendPort(port);
ULADevice.Contend(port);
// Only even addresses address the ULA
if (lowBitReset)
{
// store the last OUT byte
LastULAOutByte = value;
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
/*
Bit 7 6 5 4 3 2 1 0
@ -186,13 +184,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
*/
// Border - LSB 3 bits hold the border colour
BorderColour = value & BORDER_BIT;
if (ULADevice.borderColour != (value & BORDER_BIT))
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
ULADevice.borderColour = value & BORDER_BIT;
// Buzzer
BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0);
// Tape
TapeDevice.ProcessMicBit((value & MIC_BIT) != 0);
}
// Active AY Register

View File

@ -29,19 +29,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// init addressable memory from ROM and RAM banks
ReInitMemory();
//DisplayLineTime = 132;
//VsyncNumerator = 3546900 * 2;
InitScreenConfig(borderType);
InitScreen();
ResetULACycle();
ULADevice = new ULA48(this);
BuzzerDevice = new Buzzer(this);
BuzzerDevice.Init(44100, UlaFrameCycleCount);
BuzzerDevice.Init(44100, ULADevice.FrameLength);
AYDevice = new AY38912();
AYDevice.Init(44100, UlaFrameCycleCount);
AYDevice.Init(44100, ULADevice.FrameLength);
KeyboardDevice = new Keyboard48(this);
KempstonDevice = new KempstonJoystick(this);

View File

@ -307,7 +307,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
default:
break;
}
}
}
// update ULA screen buffer if necessary
if ((addr & 49152) == 16384)
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
}
/// <summary>
@ -318,13 +322,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
var data = ReadBus(addr);
if ((addr & 0xC000) == 0x4000)
{
// addr is in RAM not ROM - apply memory contention if neccessary
var delay = GetContentionValue(CurrentFrameCycle);
CPU.TotalExecutedCycles += delay;
}
return data;
}
@ -336,18 +337,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
if (addr < 0x4000)
{
// Do nothing - we cannot write to ROM
return;
}
else if (addr < 0xC000)
{
// possible contended RAM
var delay = GetContentionValue(CurrentFrameCycle);
CPU.TotalExecutedCycles += delay;
}
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
WriteBus(addr, value);
}

View File

@ -21,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
ContendPort((ushort)port);
ULADevice.Contend(port);
// Kempston Joystick
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
@ -141,7 +141,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x01) == 0;
ContendPort(port);
ULADevice.Contend(port);
// Only even addresses address the ULA
if (lowBitReset)
@ -157,7 +157,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
*/
// Border - LSB 3 bits hold the border colour
BorderColour = value & BORDER_BIT;
if (ULADevice.borderColour != (value & BORDER_BIT))
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
ULADevice.borderColour = value & BORDER_BIT;
// Buzzer
BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0);

View File

@ -29,19 +29,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// init addressable memory from ROM and RAM banks
ReInitMemory();
//DisplayLineTime = 132;
//VsyncNumerator = 3546900;
InitScreenConfig(borderType);
InitScreen();
ResetULACycle();
ULADevice = new ULA48(this);
BuzzerDevice = new Buzzer(this);
BuzzerDevice.Init(44100, UlaFrameCycleCount);
BuzzerDevice.Init(44100, ULADevice.FrameLength);
AYDevice = new AY38912();
AYDevice.Init(44100, UlaFrameCycleCount);
AYDevice.Init(44100, ULADevice.FrameLength);
KeyboardDevice = new Keyboard48(this);
KempstonDevice = new KempstonJoystick(this);

View File

@ -94,20 +94,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
CPU.TotalExecutedCycles += 3;
var data = ReadBus(addr);
if ((addr & 0xC000) == 0x4000)
{
// addr is in RAM not ROM - apply memory contention if neccessary
if (addr >= 0x8000)
{
data = 0xFF;
}
else
{
var delay = GetContentionValue(CurrentFrameCycle);
CPU.TotalExecutedCycles += delay;
}
}
return data;
}
@ -119,22 +111,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
if (addr < 0x4000)
{
// Do nothing - we cannot write to ROM
return;
}
else if (addr >= 0x8000)
{
// memory does not exist
return;
}
else if (addr < 0x8000)
{
// possible contended RAM
var delay = GetContentionValue(CurrentFrameCycle);
CPU.TotalExecutedCycles += delay;
}
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
CPU.TotalExecutedCycles += 3;
WriteBus(addr, value);
}

View File

@ -77,13 +77,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
var data = ReadBus(addr);
if ((addr & 0xC000) == 0x4000)
{
// addr is in RAM not ROM - apply memory contention if neccessary
var delay = GetContentionValue(CurrentFrameCycle);
CPU.TotalExecutedCycles += delay;
}
return data;
}
@ -95,24 +93,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
if (addr < 0x4000)
{
// Do nothing - we cannot write to ROM
return;
}
/*
else if (addr < 0xC000)
{
// possible contended RAM
var delay = GetContentionValue(CurrentFrameCycle);
CPU.TotalExecutedCycles += delay;
}
*/
// apply contention if necessry
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
WriteBus(addr, value);
}

View File

@ -21,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
ContendPort((ushort)port);
ULADevice.Contend(port);
// Kempston Joystick
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
@ -159,8 +159,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Tape
TapeDevice.ProcessMicBit((value & MIC_BIT) != 0);
CPU.TotalExecutedCycles += 3;
}
}
}

View File

@ -25,13 +25,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
ULADevice = new ULA48(this);
InitScreenConfig(borderType);
InitScreen();
ResetULACycle();
BuzzerDevice = new Buzzer(this);
BuzzerDevice.Init(44100, UlaFrameCycleCount);
BuzzerDevice.Init(44100, ULADevice.FrameLength);
KeyboardDevice = new Keyboard48(this);
KempstonDevice = new KempstonJoystick(this);