2011-01-18 10:28:10 +00:00
using System ;
using System.Globalization ;
using System.IO ;
using System.Collections.Generic ;
2013-10-27 22:07:40 +00:00
using BizHawk.Common ;
2013-11-04 01:06:36 +00:00
using BizHawk.Emulation.Common ;
2014-01-22 01:14:36 +00:00
using BizHawk.Emulation.Cores.Components.Z80 ;
2011-01-18 10:28:10 +00:00
//http://www.ticalc.org/pub/text/calcinfo/
2013-11-13 03:32:25 +00:00
namespace BizHawk.Emulation.Cores.Calculators
2011-01-18 10:28:10 +00:00
{
2014-04-25 01:19:57 +00:00
[ CoreAttributes (
"TI83Hawk" ,
"zeromus" ,
isPorted : false ,
isReleased : true
) ]
2013-09-14 19:34:14 +00:00
public class TI83 : IEmulator
{
//hardware
private readonly Z80A cpu = new Z80A ( ) ;
private readonly byte [ ] rom ;
private byte [ ] ram ;
private int romPageLow3Bits ;
private int romPageHighBit ;
private byte maskOn ;
private bool onPressed ;
private int keyboardMask ;
private int disp_mode ;
private int disp_move ;
private uint disp_x , disp_y ;
private int m_LinkOutput , m_LinkInput ;
private int m_LinkState
{
get
{
return ( m_LinkOutput | m_LinkInput ) ^ 3 ;
}
}
private bool LinkActive ;
private bool m_CursorMoved ;
//-------
public byte ReadMemory ( ushort addr )
{
byte ret ;
int romPage = romPageLow3Bits | ( romPageHighBit < < 3 ) ;
//Console.WriteLine("read memory: {0:X4}", addr);
if ( addr < 0x4000 )
ret = rom [ addr ] ; //ROM zero-page
else if ( addr < 0x8000 )
ret = rom [ romPage * 0x4000 + addr - 0x4000 ] ; //other rom page
else ret = ram [ addr - 0x8000 ] ;
2013-11-10 21:20:55 +00:00
CoreComm . MemoryCallbackSystem . CallRead ( addr ) ;
2013-09-14 19:34:14 +00:00
return ret ;
}
public void WriteMemory ( ushort addr , byte value )
{
if ( addr < 0x4000 )
return ; //ROM zero-page
else if ( addr < 0x8000 )
return ; //other rom page
else ram [ addr - 0x8000 ] = value ;
2013-11-10 21:20:55 +00:00
CoreComm . MemoryCallbackSystem . CallWrite ( addr ) ;
2013-09-14 19:34:14 +00:00
}
public void WriteHardware ( ushort addr , byte value )
{
switch ( addr )
{
case 0 : //PORT_LINK
romPageHighBit = ( value > > 4 ) & 1 ;
m_LinkOutput = value & 3 ;
if ( LinkActive )
{
//Prevent rom calls from disturbing link port activity
if ( LinkActive & & cpu . RegisterPC < 0x4000 )
return ;
LinkPort . Update ( ) ;
}
break ;
case 1 : //PORT_KEYBOARD:
lagged = false ;
keyboardMask = value ;
//Console.WriteLine("write PORT_KEYBOARD {0:X2}",value);
break ;
case 2 : //PORT_ROMPAGE
romPageLow3Bits = value & 0x7 ;
break ;
case 3 : //PORT_STATUS
maskOn = ( byte ) ( value & 1 ) ;
break ;
case 16 : //PORT_DISPCTRL
//Console.WriteLine("write PORT_DISPCTRL {0}",value);
WriteDispCtrl ( value ) ;
break ;
case 17 : //PORT_DISPDATA
//Console.WriteLine("write PORT_DISPDATA {0}",value);
WriteDispData ( value ) ;
break ;
}
}
public byte ReadHardware ( ushort addr )
{
switch ( addr )
{
case 0 : //PORT_LINK
LinkPort . Update ( ) ;
return ( byte ) ( ( romPageHighBit < < 4 ) | ( m_LinkState < < 2 ) | m_LinkOutput ) ;
case 1 : //PORT_KEYBOARD:
//Console.WriteLine("read PORT_KEYBOARD");
return ReadKeyboard ( ) ;
case 2 : //PORT_ROMPAGE
return ( byte ) romPageLow3Bits ;
case 3 : //PORT_STATUS
{
//Console.WriteLine("read PORT_STATUS");
// Bits:
// 0 - Set if ON key is down and ON key is trapped
// 1 - Update things (keyboard etc)
// 2 - Unknown, but used
// 3 - Set if ON key is up
// 4-7 - Unknown
//if (onPressed && maskOn) ret |= 1;
//if (!onPressed) ret |= 0x8;
return ( byte ) ( ( Controller . IsPressed ( "ON" ) ? maskOn : 8 ) | ( LinkActive ? 0 : 2 ) ) ;
}
case 4 : //PORT_INTCTRL
//Console.WriteLine("read PORT_INTCTRL");
return 0xFF ;
case 16 : //PORT_DISPCTRL
//Console.WriteLine("read DISPCTRL");
break ;
case 17 : //PORT_DISPDATA
return ReadDispData ( ) ;
}
return 0xFF ;
}
byte ReadKeyboard ( )
{
2013-11-10 18:15:32 +00:00
CoreComm . InputCallback . Call ( ) ;
2013-09-14 19:34:14 +00:00
//ref TI-9X
int ret = 0xFF ;
//Console.WriteLine("keyboardMask: {0:X2}",keyboardMask);
if ( ( keyboardMask & 1 ) = = 0 )
{
if ( Controller . IsPressed ( "DOWN" ) ) ret ^ = 1 ;
if ( Controller . IsPressed ( "LEFT" ) ) ret ^ = 2 ;
if ( Controller . IsPressed ( "RIGHT" ) ) ret ^ = 4 ;
if ( Controller . IsPressed ( "UP" ) ) ret ^ = 8 ;
}
if ( ( keyboardMask & 2 ) = = 0 )
{
if ( Controller . IsPressed ( "ENTER" ) ) ret ^ = 1 ;
if ( Controller . IsPressed ( "PLUS" ) ) ret ^ = 2 ;
if ( Controller . IsPressed ( "MINUS" ) ) ret ^ = 4 ;
if ( Controller . IsPressed ( "MULTIPLY" ) ) ret ^ = 8 ;
if ( Controller . IsPressed ( "DIVIDE" ) ) ret ^ = 16 ;
if ( Controller . IsPressed ( "EXP" ) ) ret ^ = 32 ;
if ( Controller . IsPressed ( "CLEAR" ) ) ret ^ = 64 ;
}
if ( ( keyboardMask & 4 ) = = 0 )
{
if ( Controller . IsPressed ( "DASH" ) ) ret ^ = 1 ;
if ( Controller . IsPressed ( "3" ) ) ret ^ = 2 ;
if ( Controller . IsPressed ( "6" ) ) ret ^ = 4 ;
if ( Controller . IsPressed ( "9" ) ) ret ^ = 8 ;
if ( Controller . IsPressed ( "PARACLOSE" ) ) ret ^ = 16 ;
if ( Controller . IsPressed ( "TAN" ) ) ret ^ = 32 ;
if ( Controller . IsPressed ( "VARS" ) ) ret ^ = 64 ;
}
if ( ( keyboardMask & 8 ) = = 0 )
{
if ( Controller . IsPressed ( "DOT" ) ) ret ^ = 1 ;
if ( Controller . IsPressed ( "2" ) ) ret ^ = 2 ;
if ( Controller . IsPressed ( "5" ) ) ret ^ = 4 ;
if ( Controller . IsPressed ( "8" ) ) ret ^ = 8 ;
if ( Controller . IsPressed ( "PARAOPEN" ) ) ret ^ = 16 ;
if ( Controller . IsPressed ( "COS" ) ) ret ^ = 32 ;
if ( Controller . IsPressed ( "PRGM" ) ) ret ^ = 64 ;
if ( Controller . IsPressed ( "STAT" ) ) ret ^ = 128 ;
}
if ( ( keyboardMask & 16 ) = = 0 )
{
if ( Controller . IsPressed ( "0" ) ) ret ^ = 1 ;
if ( Controller . IsPressed ( "1" ) ) ret ^ = 2 ;
if ( Controller . IsPressed ( "4" ) ) ret ^ = 4 ;
if ( Controller . IsPressed ( "7" ) ) ret ^ = 8 ;
if ( Controller . IsPressed ( "COMMA" ) ) ret ^ = 16 ;
if ( Controller . IsPressed ( "SIN" ) ) ret ^ = 32 ;
if ( Controller . IsPressed ( "MATRIX" ) ) ret ^ = 64 ;
if ( Controller . IsPressed ( "X" ) ) ret ^ = 128 ;
}
if ( ( keyboardMask & 32 ) = = 0 )
{
if ( Controller . IsPressed ( "STO" ) ) ret ^ = 2 ;
if ( Controller . IsPressed ( "LN" ) ) ret ^ = 4 ;
if ( Controller . IsPressed ( "LOG" ) ) ret ^ = 8 ;
if ( Controller . IsPressed ( "SQUARED" ) ) ret ^ = 16 ;
if ( Controller . IsPressed ( "NEG1" ) ) ret ^ = 32 ;
if ( Controller . IsPressed ( "MATH" ) )
ret ^ = 64 ;
if ( Controller . IsPressed ( "ALPHA" ) ) ret ^ = 128 ;
}
if ( ( keyboardMask & 64 ) = = 0 )
{
if ( Controller . IsPressed ( "GRAPH" ) ) ret ^ = 1 ;
if ( Controller . IsPressed ( "TRACE" ) ) ret ^ = 2 ;
if ( Controller . IsPressed ( "ZOOM" ) ) ret ^ = 4 ;
if ( Controller . IsPressed ( "WINDOW" ) ) ret ^ = 8 ;
if ( Controller . IsPressed ( "Y" ) ) ret ^ = 16 ;
if ( Controller . IsPressed ( "2ND" ) ) ret ^ = 32 ;
if ( Controller . IsPressed ( "MODE" ) ) ret ^ = 64 ;
if ( Controller . IsPressed ( "DEL" ) ) ret ^ = 128 ;
}
return ( byte ) ret ;
}
byte ReadDispData ( )
{
if ( m_CursorMoved )
{
m_CursorMoved = false ;
return 0x00 ; //not accurate this should be stale data or something
}
byte ret ;
if ( disp_mode = = 1 )
{
ret = vram [ disp_y * 12 + disp_x ] ;
}
else
{
int column = 6 * ( int ) disp_x ;
int offset = ( int ) disp_y * 12 + ( column > > 3 ) ;
int shift = 10 - ( column & 7 ) ;
ret = ( byte ) ( ( ( vram [ offset ] < < 8 ) | vram [ offset + 1 ] ) > > shift ) ;
}
doDispMove ( ) ;
return ret ;
}
void WriteDispData ( byte value )
{
int offset ;
if ( disp_mode = = 1 )
{
offset = ( int ) disp_y * 12 + ( int ) disp_x ;
vram [ offset ] = value ;
}
else
{
int column = 6 * ( int ) disp_x ;
offset = ( int ) disp_y * 12 + ( column > > 3 ) ;
if ( offset < 0x300 )
{
int shift = column & 7 ;
int mask = ~ ( 252 > > shift ) ;
int Data = value < < 2 ;
vram [ offset ] = ( byte ) ( vram [ offset ] & mask | ( Data > > shift ) ) ;
if ( shift > 2 & & offset < 0x2ff )
{
offset + + ;
shift = 8 - shift ;
mask = ~ ( 252 < < shift ) ;
vram [ offset ] = ( byte ) ( vram [ offset ] & mask | ( Data < < shift ) ) ;
}
}
}
doDispMove ( ) ;
}
void doDispMove ( )
{
switch ( disp_move )
{
case 0 : disp_y - - ; break ;
case 1 : disp_y + + ; break ;
case 2 : disp_x - - ; break ;
case 3 : disp_x + + ; break ;
}
disp_x & = 0xF ; //0xF or 0x1F? dunno
disp_y & = 0x3F ;
}
void WriteDispCtrl ( byte value )
{
if ( value < = 1 )
disp_mode = value ;
else if ( value > = 4 & & value < = 7 )
disp_move = value - 4 ;
else if ( ( value & 0xC0 ) = = 0x40 )
{
//hardware scroll
}
else if ( ( value & 0xE0 ) = = 0x20 )
{
disp_x = ( uint ) ( value & 0x1F ) ;
m_CursorMoved = true ;
}
else if ( ( value & 0xC0 ) = = 0x80 )
{
disp_y = ( uint ) ( value & 0x3F ) ;
m_CursorMoved = true ;
}
else if ( ( value & 0xC0 ) = = 0xC0 )
{
//contrast
}
else if ( value = = 2 )
{
}
else if ( value = = 3 )
{
}
else
{
}
}
public TI83 ( CoreComm comm , GameInfo game , byte [ ] rom )
{
CoreComm = comm ;
cpu . ReadMemory = ReadMemory ;
cpu . WriteMemory = WriteMemory ;
cpu . ReadHardware = ReadHardware ;
cpu . WriteHardware = WriteHardware ;
cpu . IRQCallback = IRQCallback ;
cpu . NMICallback = NMICallback ;
this . rom = rom ;
LinkPort = new Link ( this ) ;
//different calculators (different revisions?) have different initPC. we track this in the game database by rom hash
//if( *(unsigned long *)(m_pRom + 0x6ce) == 0x04D3163E ) m_Regs.PC.W = 0x6ce; //KNOWN
//else if( *(unsigned long *)(m_pRom + 0x6f6) == 0x04D3163E ) m_Regs.PC.W = 0x6f6; //UNKNOWN
if ( game [ "initPC" ] )
startPC = ushort . Parse ( game . OptionValue ( "initPC" ) , NumberStyles . HexNumber ) ;
HardReset ( ) ;
SetupMemoryDomains ( ) ;
}
void IRQCallback ( )
{
//Console.WriteLine("IRQ with vec {0} and cpu.InterruptMode {1}", cpu.RegisterI, cpu.InterruptMode);
cpu . Interrupt = false ;
}
void NMICallback ( )
{
Console . WriteLine ( "NMI" ) ;
cpu . NonMaskableInterrupt = false ;
}
public CoreComm CoreComm { get ; private set ; }
protected byte [ ] vram = new byte [ 0x300 ] ;
class MyVideoProvider : IVideoProvider
{
private readonly TI83 emu ;
public MyVideoProvider ( TI83 emu )
{
this . emu = emu ;
}
public int [ ] GetVideoBuffer ( )
{
//unflatten bit buffer
int [ ] pixels = new int [ 96 * 64 ] ;
int i = 0 ;
for ( int y = 0 ; y < 64 ; y + + )
for ( int x = 0 ; x < 96 ; x + + )
{
int offset = y * 96 + x ;
int bufbyte = offset > > 3 ;
int bufbit = offset & 7 ;
int bit = ( ( emu . vram [ bufbyte ] > > ( 7 - bufbit ) ) & 1 ) ;
if ( bit = = 0 )
unchecked { pixels [ i + + ] = ( int ) 0xFFFFFFFF ; }
else
pixels [ i + + ] = 0 ;
}
return pixels ;
}
public int VirtualWidth { get { return 96 ; } }
2014-04-30 23:48:37 +00:00
public int VirtualHeight { get { return 64 ; } }
2013-09-14 19:34:14 +00:00
public int BufferWidth { get { return 96 ; } }
public int BufferHeight { get { return 64 ; } }
public int BackgroundColor { get { return 0 ; } }
}
public IVideoProvider VideoProvider
{
get { return new MyVideoProvider ( this ) ; }
}
public ISoundProvider SoundProvider { get { return NullSound . SilenceProvider ; } }
public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound ( NullSound . SilenceProvider , 735 ) ; } }
public bool StartAsyncSound ( ) { return true ; }
public void EndAsyncSound ( ) { }
public static readonly ControllerDefinition TI83Controller =
new ControllerDefinition
{
Name = "TI83 Controller" ,
BoolButtons = { "0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "DOT" ,
2011-01-18 10:28:10 +00:00
"ON" , "ENTER" ,
"DOWN" , "LEFT" , "UP" , "RIGHT" ,
"PLUS" , "MINUS" , "MULTIPLY" , "DIVIDE" ,
2011-04-24 04:46:07 +00:00
"CLEAR" , "EXP" , "DASH" , "PARACLOSE" , "TAN" , "VARS" , "PARAOPEN" ,
2011-07-10 01:55:37 +00:00
"COS" , "PRGM" , "STAT" , "COMMA" , "SIN" , "MATRIX" , "X" ,
"STO" , "LN" , "LOG" , "SQUARED" , "NEG1" , "MATH" , "ALPHA" ,
"GRAPH" , "TRACE" , "ZOOM" , "WINDOW" , "Y" , "2ND" , "MODE" , "DEL"
2011-01-18 10:28:10 +00:00
}
2013-09-14 19:34:14 +00:00
} ;
public ControllerDefinition ControllerDefinition { get { return TI83Controller ; } }
public IController Controller { get ; set ; }
//configuration
ushort startPC ;
public void FrameAdvance ( bool render , bool rendersound )
{
lagged = true ;
//I eyeballed this speed
for ( int i = 0 ; i < 5 ; i + + )
{
onPressed = Controller . IsPressed ( "ON" ) ;
//and this was derived from other emus
cpu . ExecuteCycles ( 10000 ) ;
cpu . Interrupt = true ;
}
2013-12-07 00:53:06 +00:00
Frame + + ;
2013-09-14 19:34:14 +00:00
if ( lagged )
{
2014-03-22 04:46:01 +00:00
lagCount + + ;
isLag = true ;
2013-09-14 19:34:14 +00:00
}
else
2013-12-07 00:53:06 +00:00
{
2014-03-22 04:46:01 +00:00
isLag = false ;
2013-12-07 00:53:06 +00:00
}
2013-09-14 19:34:14 +00:00
}
public void HardReset ( )
{
cpu . Reset ( ) ;
ram = new byte [ 0x8000 ] ;
for ( int i = 0 ; i < 0x8000 ; i + + )
ram [ i ] = 0xFF ;
cpu . RegisterPC = startPC ;
cpu . IFF1 = false ;
cpu . IFF2 = false ;
cpu . InterruptMode = 2 ;
maskOn = 1 ;
romPageHighBit = 0 ;
romPageLow3Bits = 0 ;
keyboardMask = 0 ;
disp_mode = 0 ;
disp_move = 0 ;
disp_x = disp_y = 0 ;
}
2014-03-22 04:46:01 +00:00
private int lagCount = 0 ;
2013-09-14 19:34:14 +00:00
private bool lagged = true ;
2014-03-22 04:46:01 +00:00
private bool isLag = false ;
private int frame ;
public int Frame { get { return frame ; } set { frame = value ; } }
public int LagCount { get { return lagCount ; } set { lagCount = value ; } }
public bool IsLagFrame { get { return isLag ; } }
2013-09-14 19:34:14 +00:00
2013-11-03 16:29:51 +00:00
public void ResetCounters ( )
2013-09-14 19:34:14 +00:00
{
Frame = 0 ;
2014-03-22 04:46:01 +00:00
lagCount = 0 ;
isLag = false ;
2013-09-14 19:34:14 +00:00
}
public bool DeterministicEmulation { get { return true ; } }
public byte [ ] ReadSaveRam ( ) { return null ; }
public void StoreSaveRam ( byte [ ] data ) { }
public void ClearSaveRam ( ) { }
public bool SaveRamModified
{
get { return false ; }
set { }
}
2014-03-22 04:46:01 +00:00
public bool BinarySaveStatesPreferred { get { return false ; } }
public void SaveStateBinary ( BinaryWriter bw ) { SyncState ( Serializer . CreateBinaryWriter ( bw ) ) ; }
public void LoadStateBinary ( BinaryReader br ) { SyncState ( Serializer . CreateBinaryReader ( br ) ) ; }
public void SaveStateText ( TextWriter tw ) { SyncState ( Serializer . CreateTextWriter ( tw ) ) ; }
public void LoadStateText ( TextReader tr ) { SyncState ( Serializer . CreateTextReader ( tr ) ) ; }
2013-09-14 19:34:14 +00:00
2014-03-22 04:46:01 +00:00
void SyncState ( Serializer ser )
2013-09-14 19:34:14 +00:00
{
2014-03-22 04:46:01 +00:00
ser . BeginSection ( "TI83" ) ;
cpu . SyncState ( ser ) ;
ser . Sync ( "RAM" , ref ram , false ) ;
ser . Sync ( "romPageLow3Bits" , ref romPageLow3Bits ) ;
ser . Sync ( "romPageHighBit" , ref romPageHighBit ) ;
ser . Sync ( "disp_mode" , ref disp_mode ) ;
ser . Sync ( "disp_move" , ref disp_move ) ;
ser . Sync ( "disp_x" , ref disp_x ) ;
ser . Sync ( "disp_y" , ref disp_y ) ;
ser . Sync ( "m_CursorMoved" , ref m_CursorMoved ) ;
ser . Sync ( "maskOn" , ref maskOn ) ;
ser . Sync ( "onPressed" , ref onPressed ) ;
ser . Sync ( "keyboardMask" , ref keyboardMask ) ;
ser . Sync ( "m_LinkOutput" , ref m_LinkOutput ) ;
ser . Sync ( "VRAM" , ref vram , false ) ;
ser . Sync ( "Frame" , ref frame ) ;
ser . Sync ( "LagCount" , ref lagCount ) ;
ser . Sync ( "IsLag" , ref isLag ) ;
ser . EndSection ( ) ;
2013-09-14 19:34:14 +00:00
}
2014-03-22 04:46:01 +00:00
byte [ ] stateBuffer ;
2013-09-14 19:34:14 +00:00
public byte [ ] SaveStateBinary ( )
{
2014-03-22 04:46:01 +00:00
if ( stateBuffer = = null )
{
var stream = new MemoryStream ( ) ;
var writer = new BinaryWriter ( stream ) ;
SaveStateBinary ( writer ) ;
stateBuffer = stream . ToArray ( ) ;
writer . Close ( ) ;
return stateBuffer ;
}
else
{
var stream = new MemoryStream ( stateBuffer ) ;
var writer = new BinaryWriter ( stream ) ;
SaveStateBinary ( writer ) ;
writer . Close ( ) ;
return stateBuffer ;
}
2013-09-14 19:34:14 +00:00
}
public string SystemId { get { return "TI83" ; } }
public string BoardName { get { return null ; } }
2013-11-06 02:15:29 +00:00
private MemoryDomainList _memoryDomains ;
2013-09-14 19:34:14 +00:00
private const ushort RamSizeMask = 0x7FFF ;
private void SetupMemoryDomains ( )
{
2013-11-06 02:15:29 +00:00
var domains = new List < MemoryDomain >
{
new MemoryDomain (
"Main RAM" ,
ram . Length ,
MemoryDomain . Endian . Little ,
2014-02-26 20:18:48 +00:00
addr = > ram [ addr ] ,
( addr , value ) = > ram [ addr ] = value
2013-11-06 02:15:29 +00:00
)
} ;
_memoryDomains = new MemoryDomainList ( domains ) ;
2013-09-14 19:34:14 +00:00
}
2013-11-06 02:15:29 +00:00
public MemoryDomainList MemoryDomains { get { return _memoryDomains ; } }
2013-09-14 19:34:14 +00:00
public void Dispose ( ) { }
public Link LinkPort ;
2013-10-27 18:01:36 +00:00
2013-09-14 19:34:14 +00:00
public class Link
{
2014-01-07 05:53:57 +00:00
//Emulates TI linking software.
//See http://www.ticalc.org/archives/files/fileinfo/294/29418.html for documentation
//Note: Each hardware read/write to the link port calls tthe update method.
2013-09-14 19:34:14 +00:00
readonly TI83 Parent ;
2013-10-27 18:01:36 +00:00
private FileStream CurrentFile ;
//private int FileBytesLeft;
private byte [ ] VariableData ;
2013-09-17 04:11:13 +00:00
2013-09-14 19:34:14 +00:00
private Action NextStep ;
private Queue < byte > CurrentData = new Queue < byte > ( ) ;
private ushort BytesToSend ;
private byte BitsLeft ;
private byte CurrentByte ;
private byte StepsLeft ;
private Status CurrentStatus = Status . Inactive ;
private enum Status
{
Inactive ,
PrepareReceive ,
PrepareSend ,
Receive ,
Send
}
public Link ( TI83 Parent )
{
this . Parent = Parent ;
}
public void Update ( )
{
if ( CurrentStatus = = Status . PrepareReceive )
{
//Get the first byte, and start sending it.
CurrentByte = CurrentData . Dequeue ( ) ;
CurrentStatus = Status . Receive ;
BitsLeft = 8 ;
StepsLeft = 5 ;
}
if ( CurrentStatus = = Status . PrepareSend & & Parent . m_LinkState ! = 3 )
{
CurrentStatus = Status . Send ;
BitsLeft = 8 ;
StepsLeft = 5 ;
CurrentByte = 0 ;
}
if ( CurrentStatus = = Status . Receive )
{
switch ( StepsLeft )
{
case 5 :
//Receive step 1: Lower the other device's line.
Parent . m_LinkInput = ( ( CurrentByte & 1 ) = = 1 ) ? 2 : 1 ;
CurrentByte > > = 1 ;
StepsLeft - - ;
break ;
case 4 :
//Receive step 2: Wait for the calc to lower the other line.
if ( ( Parent . m_LinkState & 3 ) = = 0 )
StepsLeft - - ;
break ;
case 3 :
//Receive step 3: Raise the other device's line back up.
Parent . m_LinkInput = 0 ;
StepsLeft - - ;
break ;
case 2 :
//Receive step 4: Wait for the calc to raise its line back up.
if ( ( Parent . m_LinkState & 3 ) = = 3 )
StepsLeft - - ;
break ;
case 1 :
//Receive step 5: Finish.
BitsLeft - - ;
if ( BitsLeft = = 0 )
{
if ( CurrentData . Count > 0 )
CurrentStatus = Status . PrepareReceive ;
else
{
CurrentStatus = Status . Inactive ;
if ( NextStep ! = null )
NextStep ( ) ;
}
}
else
//next bit in the current byte.
StepsLeft = 5 ;
break ;
}
}
else if ( CurrentStatus = = Status . Send )
{
switch ( StepsLeft )
{
case 5 :
//Send step 1: Calc lowers a line.
if ( Parent . m_LinkState ! = 3 )
{
int Bit = Parent . m_LinkState & 1 ;
int Shift = 8 - BitsLeft ;
CurrentByte | = ( byte ) ( Bit < < Shift ) ;
StepsLeft - - ;
}
break ;
case 4 :
//send step 2: Lower our line.
Parent . m_LinkInput = Parent . m_LinkOutput ^ 3 ;
StepsLeft - - ;
break ;
case 3 :
//Send step 3: wait for the calc to raise its line.
if ( ( Parent . m_LinkOutput & 3 ) = = 0 )
StepsLeft - - ;
break ;
case 2 :
//Send step 4: raise the other devices lines.
Parent . m_LinkInput = 0 ;
StepsLeft - - ;
break ;
case 1 :
//Send step 5: Finish
BitsLeft - - ;
if ( BitsLeft = = 0 )
{
BytesToSend - - ;
CurrentData . Enqueue ( CurrentByte ) ;
if ( BytesToSend > 0 )
CurrentStatus = Status . PrepareSend ;
else
{
CurrentStatus = Status . Inactive ;
if ( NextStep ! = null )
NextStep ( ) ;
}
}
else
{
//next bit in the current byte.
StepsLeft = 5 ;
}
break ;
}
}
}
2013-10-27 18:01:36 +00:00
public void SendFileToCalc ( FileStream FS , bool Verify )
{
if ( Verify )
VerifyFile ( FS ) ;
2013-09-14 19:34:14 +00:00
2013-10-27 18:01:36 +00:00
FS . Seek ( 55 , SeekOrigin . Begin ) ;
CurrentFile = FS ;
SendNextFile ( ) ;
}
2013-09-14 19:34:14 +00:00
2013-09-17 04:11:13 +00:00
private void VerifyFile ( FileStream FS )
{
2013-10-27 18:01:36 +00:00
//Verify the file format.
byte [ ] Expected = new byte [ ] { 0x2a , 0x2a , 0x54 , 0x49 , 0x38 , 0x33 , 0x2a , 0x2a , 0x1a , 0x0a , 0x00 } ;
byte [ ] Actual = new byte [ 11 ] ;
2013-09-14 19:34:14 +00:00
2013-10-27 18:01:36 +00:00
FS . Seek ( 0 , SeekOrigin . Begin ) ;
FS . Read ( Actual , 0 , 11 ) ;
2013-09-14 19:34:14 +00:00
2013-10-27 18:01:36 +00:00
//Check the header.
for ( int n = 0 ; n < 11 ; n + + )
if ( Expected [ n ] ! = Actual [ n ] )
{
FS . Close ( ) ;
throw new IOException ( "Invalid Header." ) ;
}
2013-09-14 19:34:14 +00:00
2013-10-27 18:01:36 +00:00
//Seek to the end of the comment.
FS . Seek ( 53 , SeekOrigin . Begin ) ;
2013-09-14 19:34:14 +00:00
2013-09-17 04:11:13 +00:00
int Size = FS . ReadByte ( ) + FS . ReadByte ( ) * 256 ;
2013-09-14 19:34:14 +00:00
2013-10-27 18:01:36 +00:00
if ( FS . Length ! = Size + 57 )
{
FS . Close ( ) ;
throw new IOException ( "Invalid file length." ) ;
}
//Verify the checksum.
ushort Checksum = 0 ;
for ( int n = 0 ; n < Size ; n + + )
Checksum + = ( ushort ) FS . ReadByte ( ) ;
ushort ActualChecksum = ( ushort ) ( FS . ReadByte ( ) + FS . ReadByte ( ) * 256 ) ;
if ( Checksum ! = ActualChecksum )
{
FS . Close ( ) ;
throw new IOException ( "Invalid Checksum." ) ;
}
}
private void SendNextFile ( )
{
byte [ ] Header = new byte [ 13 ] ;
if ( ! CurrentFile . CanRead | | CurrentFile . Read ( Header , 0 , 13 ) ! = 13 )
{
//End of file.
CurrentFile . Close ( ) ;
return ;
}
int Size = Header [ 2 ] + Header [ 3 ] * 256 ;
VariableData = new byte [ Size + 2 ] ;
CurrentFile . Read ( VariableData , 0 , Size + 2 ) ;
//Request to send the file.
CurrentData . Clear ( ) ;
CurrentData . Enqueue ( 0x03 ) ;
CurrentData . Enqueue ( 0xC9 ) ;
foreach ( byte B in Header )
CurrentData . Enqueue ( B ) ;
//Calculate the checksum for the command.
ushort Checksum = 0 ;
for ( int n = 2 ; n < Header . Length ; n + + )
Checksum + = Header [ n ] ;
CurrentData . Enqueue ( ( byte ) ( Checksum % 256 ) ) ;
CurrentData . Enqueue ( ( byte ) ( Checksum / 256 ) ) ;
//Finalize the command.
CurrentStatus = Status . PrepareReceive ;
NextStep = ReceiveReqAck ;
Parent . LinkActive = true ;
}
2013-09-14 19:34:14 +00:00
private void ReceiveReqAck ( )
{
Parent . LinkActive = false ;
CurrentData . Clear ( ) ;
//Prepare to receive the Aknowledgement response from the calculator.
BytesToSend = 8 ;
CurrentStatus = Status . PrepareSend ;
NextStep = SendVariableData ;
}
private void SendVariableData ( )
{
//Check to see if out of memory first.
CurrentData . Dequeue ( ) ;
CurrentData . Dequeue ( ) ;
CurrentData . Dequeue ( ) ;
CurrentData . Dequeue ( ) ;
CurrentData . Dequeue ( ) ;
if ( CurrentData . Dequeue ( ) = = 0x36 )
OutOfMemory ( ) ;
else
{
CurrentData . Clear ( ) ;
CurrentData . Enqueue ( 0x03 ) ;
CurrentData . Enqueue ( 0x56 ) ;
CurrentData . Enqueue ( 0x00 ) ;
CurrentData . Enqueue ( 0x00 ) ;
CurrentData . Enqueue ( 0x03 ) ;
CurrentData . Enqueue ( 0x15 ) ;
//Add variable data.
2013-10-27 18:01:36 +00:00
foreach ( byte B in VariableData )
2013-09-14 19:34:14 +00:00
CurrentData . Enqueue ( B ) ;
//Calculate the checksum.
ushort Checksum = 0 ;
2013-10-27 18:01:36 +00:00
for ( int n = 2 ; n < VariableData . Length ; n + + )
Checksum + = VariableData [ n ] ;
2013-09-14 19:34:14 +00:00
CurrentData . Enqueue ( ( byte ) ( Checksum % 256 ) ) ;
CurrentData . Enqueue ( ( byte ) ( Checksum / 256 ) ) ;
CurrentStatus = Status . PrepareReceive ;
NextStep = ReceiveDataAck ;
Parent . LinkActive = true ;
}
}
private void ReceiveDataAck ( )
{
Parent . LinkActive = false ;
CurrentData . Clear ( ) ;
//Prepare to receive the Aknowledgement response from the calculator.
BytesToSend = 4 ;
CurrentStatus = Status . PrepareSend ;
NextStep = EndTransmission ;
}
private void EndTransmission ( )
{
CurrentData . Clear ( ) ;
//Send the end transmission command.
CurrentData . Enqueue ( 0x03 ) ;
CurrentData . Enqueue ( 0x92 ) ;
CurrentData . Enqueue ( 0x00 ) ;
CurrentData . Enqueue ( 0x00 ) ;
CurrentStatus = Status . PrepareReceive ;
2014-01-07 05:53:57 +00:00
NextStep = FinalizeFile ;
2013-09-14 19:34:14 +00:00
Parent . LinkActive = true ;
}
private void OutOfMemory ( )
{
2013-10-27 18:01:36 +00:00
CurrentFile . Close ( ) ;
2013-09-14 19:34:14 +00:00
Parent . LinkActive = false ;
CurrentData . Clear ( ) ;
//Prepare to receive the Aknowledgement response from the calculator.
BytesToSend = 3 ;
CurrentStatus = Status . PrepareSend ;
NextStep = EndOutOfMemory ;
}
private void EndOutOfMemory ( )
{
CurrentData . Clear ( ) ;
//Send the end transmission command.
CurrentData . Enqueue ( 0x03 ) ;
CurrentData . Enqueue ( 0x56 ) ;
CurrentData . Enqueue ( 0x01 ) ;
CurrentData . Enqueue ( 0x00 ) ;
CurrentStatus = Status . PrepareReceive ;
2014-01-07 05:53:57 +00:00
NextStep = FinalizeFile ;
2013-09-14 19:34:14 +00:00
Parent . LinkActive = true ;
}
2013-10-27 18:01:36 +00:00
//adelikat: This isn't used (yet?) and causes a warning. Did you really mean to override finalize? if so it should be protected virtual, else rename this
2014-01-07 05:53:57 +00:00
//Envian: I didn't notice there was a naming conflict. Fixed it since this method is necessary for the file upload proccess.
private void FinalizeFile ( )
2013-09-14 19:34:14 +00:00
{
2014-01-07 05:53:57 +00:00
//Resets the link software, and checks to see if there is an additional file to send.
2013-09-14 19:34:14 +00:00
CurrentData . Clear ( ) ;
Parent . LinkActive = false ;
NextStep = null ;
2013-10-27 18:01:36 +00:00
SendNextFile ( ) ;
2013-09-14 19:34:14 +00:00
}
}
2013-12-22 00:44:39 +00:00
public object GetSettings ( ) { return null ; }
public object GetSyncSettings ( ) { return null ; }
public bool PutSettings ( object o ) { return false ; }
2013-12-23 02:51:41 +00:00
public bool PutSyncSettings ( object o ) { return false ; }
2013-12-22 00:44:39 +00:00
2014-04-19 22:23:13 +00:00
public Dictionary < string , int > GetCpuFlagsAndRegisters ( )
2014-03-22 04:46:01 +00:00
{
2014-04-19 22:23:13 +00:00
return new Dictionary < string , int >
{
{ "A" , cpu . RegisterA } ,
{ "AF" , cpu . RegisterAF } ,
{ "B" , cpu . RegisterB } ,
{ "BC" , cpu . RegisterBC } ,
{ "C" , cpu . RegisterC } ,
{ "D" , cpu . RegisterD } ,
{ "DE" , cpu . RegisterDE } ,
{ "E" , cpu . RegisterE } ,
{ "F" , cpu . RegisterF } ,
{ "H" , cpu . RegisterH } ,
{ "HL" , cpu . RegisterHL } ,
{ "I" , cpu . RegisterI } ,
{ "IX" , cpu . RegisterIX } ,
{ "IY" , cpu . RegisterIY } ,
{ "L" , cpu . RegisterL } ,
{ "PC" , cpu . RegisterPC } ,
{ "R" , cpu . RegisterR } ,
{ "Shadow AF" , cpu . RegisterShadowAF } ,
{ "Shadow BC" , cpu . RegisterShadowBC } ,
{ "Shadow DE" , cpu . RegisterShadowDE } ,
{ "Shadow HL" , cpu . RegisterShadowHL } ,
{ "SP" , cpu . RegisterSP } ,
{ "Flag C" , cpu . RegisterF . Bit ( 0 ) ? 1 : 0 } ,
{ "Flag N" , cpu . RegisterF . Bit ( 1 ) ? 1 : 0 } ,
{ "Flag P/V" , cpu . RegisterF . Bit ( 2 ) ? 1 : 0 } ,
{ "Flag 3rd" , cpu . RegisterF . Bit ( 3 ) ? 1 : 0 } ,
{ "Flag H" , cpu . RegisterF . Bit ( 4 ) ? 1 : 0 } ,
{ "Flag 5th" , cpu . RegisterF . Bit ( 5 ) ? 1 : 0 } ,
{ "Flag Z" , cpu . RegisterF . Bit ( 6 ) ? 1 : 0 } ,
{ "Flag S" , cpu . RegisterF . Bit ( 7 ) ? 1 : 0 }
2014-03-22 04:46:01 +00:00
} ;
}
2013-09-14 19:34:14 +00:00
}
2011-01-18 10:28:10 +00:00
}