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 ;
2013-11-14 15:01:32 +00:00
using BizHawk.Emulation.Common.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
{
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 ;
//-------
2013-11-11 03:20:33 +00:00
public List < KeyValuePair < string , int > > GetCpuFlagsAndRegisters ( )
{
2013-11-11 16:45:45 +00:00
return new List < KeyValuePair < string , int > >
{
new KeyValuePair < string , int > ( "A" , cpu . RegisterA ) ,
new KeyValuePair < string , int > ( "AF" , cpu . RegisterAF ) ,
new KeyValuePair < string , int > ( "B" , cpu . RegisterB ) ,
new KeyValuePair < string , int > ( "BC" , cpu . RegisterBC ) ,
new KeyValuePair < string , int > ( "C" , cpu . RegisterC ) ,
new KeyValuePair < string , int > ( "D" , cpu . RegisterD ) ,
new KeyValuePair < string , int > ( "DE" , cpu . RegisterDE ) ,
new KeyValuePair < string , int > ( "E" , cpu . RegisterE ) ,
new KeyValuePair < string , int > ( "F" , cpu . RegisterF ) ,
new KeyValuePair < string , int > ( "H" , cpu . RegisterH ) ,
new KeyValuePair < string , int > ( "HL" , cpu . RegisterHL ) ,
new KeyValuePair < string , int > ( "I" , cpu . RegisterI ) ,
new KeyValuePair < string , int > ( "IX" , cpu . RegisterIX ) ,
new KeyValuePair < string , int > ( "IY" , cpu . RegisterIY ) ,
new KeyValuePair < string , int > ( "L" , cpu . RegisterL ) ,
new KeyValuePair < string , int > ( "PC" , cpu . RegisterPC ) ,
new KeyValuePair < string , int > ( "R" , cpu . RegisterR ) ,
new KeyValuePair < string , int > ( "Shadow AF" , cpu . RegisterShadowAF ) ,
new KeyValuePair < string , int > ( "Shadow BC" , cpu . RegisterShadowBC ) ,
new KeyValuePair < string , int > ( "Shadow DE" , cpu . RegisterShadowDE ) ,
new KeyValuePair < string , int > ( "Shadow HL" , cpu . RegisterShadowHL ) ,
new KeyValuePair < string , int > ( "SP" , cpu . RegisterSP ) ,
new KeyValuePair < string , int > ( "Flag C" , cpu . RegisterF . Bit ( 0 ) ? 1 : 0 ) ,
new KeyValuePair < string , int > ( "Flag N" , cpu . RegisterF . Bit ( 1 ) ? 1 : 0 ) ,
new KeyValuePair < string , int > ( "Flag P/V" , cpu . RegisterF . Bit ( 2 ) ? 1 : 0 ) ,
new KeyValuePair < string , int > ( "Flag 3rd" , cpu . RegisterF . Bit ( 3 ) ? 1 : 0 ) ,
new KeyValuePair < string , int > ( "Flag H" , cpu . RegisterF . Bit ( 4 ) ? 1 : 0 ) ,
new KeyValuePair < string , int > ( "Flag 5th" , cpu . RegisterF . Bit ( 5 ) ? 1 : 0 ) ,
new KeyValuePair < string , int > ( "Flag Z" , cpu . RegisterF . Bit ( 6 ) ? 1 : 0 ) ,
new KeyValuePair < string , int > ( "Flag S" , cpu . RegisterF . Bit ( 7 ) ? 1 : 0 ) ,
} ;
2013-11-11 03:20:33 +00:00
}
2013-09-14 19:34:14 +00:00
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 ; } }
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 )
{
_lagcount + + ;
islag = true ;
}
else
2013-12-07 00:53:06 +00:00
{
2013-09-14 19:34:14 +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 ;
}
private int _lagcount = 0 ;
private bool lagged = true ;
private bool islag = false ;
public int Frame { get ; set ; }
2013-11-03 16:29:51 +00:00
public void ResetCounters ( )
2013-09-14 19:34:14 +00:00
{
Frame = 0 ;
_lagcount = 0 ;
islag = false ;
}
public int LagCount { get { return _lagcount ; } set { _lagcount = value ; } }
public bool IsLagFrame { get { return islag ; } }
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 { }
}
public void SaveStateText ( TextWriter writer )
{
writer . WriteLine ( "[TI83]\n" ) ;
writer . WriteLine ( "Frame {0}" , Frame ) ;
cpu . SaveStateText ( writer ) ;
writer . Write ( "RAM " ) ;
ram . SaveAsHex ( writer ) ;
writer . WriteLine ( "romPageLow3Bits {0}" , romPageLow3Bits ) ;
writer . WriteLine ( "romPageHighBit {0}" , romPageHighBit ) ;
writer . WriteLine ( "disp_mode {0}" , disp_mode ) ;
writer . WriteLine ( "disp_move {0}" , disp_move ) ;
writer . WriteLine ( "disp_x {0}" , disp_x ) ;
writer . WriteLine ( "disp_y {0}" , disp_y ) ;
writer . WriteLine ( "m_CursorMoved {0}" , m_CursorMoved ) ;
writer . WriteLine ( "maskOn {0}" , maskOn ) ;
writer . WriteLine ( "onPressed {0}" , onPressed ) ;
writer . WriteLine ( "keyboardMask {0}" , keyboardMask ) ;
writer . WriteLine ( "m_LinkOutput {0}" , m_LinkOutput ) ;
writer . WriteLine ( "m_LinkInput {0}" , m_LinkInput ) ;
writer . WriteLine ( "lag {0}" , _lagcount ) ;
writer . WriteLine ( "islag {0}" , islag ) ;
writer . WriteLine ( "vram {0}" , Util . BytesToHexString ( vram ) ) ;
writer . WriteLine ( "[/TI83]" ) ;
}
public void LoadStateText ( TextReader reader )
{
while ( true )
{
string [ ] args = reader . ReadLine ( ) . Split ( ' ' ) ;
if ( args [ 0 ] . Trim ( ) = = "" ) continue ;
if ( args [ 0 ] = = "[TI83]" ) continue ;
if ( args [ 0 ] = = "[/TI83]" ) break ;
if ( args [ 0 ] = = "Frame" )
Frame = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "[Z80]" )
cpu . LoadStateText ( reader ) ;
else if ( args [ 0 ] = = "RAM" )
ram . ReadFromHex ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "romPageLow3Bits" )
romPageLow3Bits = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "romPageHighBit" )
romPageHighBit = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "disp_mode" )
disp_mode = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "disp_move" )
disp_move = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "disp_x" )
disp_x = uint . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "disp_y" )
disp_y = uint . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "m_CursorMoved" )
m_CursorMoved = bool . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "maskOn" )
maskOn = byte . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "onPressed" )
onPressed = bool . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "keyboardMask" )
keyboardMask = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "m_LinkOutput" )
m_LinkOutput = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "m_LinkInput" )
m_LinkInput = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "lag" )
_lagcount = int . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "islag" )
islag = bool . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "vram" )
vram = Util . HexStringToBytes ( args [ 1 ] ) ;
else
Console . WriteLine ( "Skipping unrecognized identifier " + args [ 0 ] ) ;
}
}
public void SaveStateBinary ( BinaryWriter writer )
{
}
public void LoadStateBinary ( BinaryReader reader )
{
}
public byte [ ] SaveStateBinary ( )
{
return new byte [ 0 ] ;
}
public bool BinarySaveStatesPreferred { get { return false ; } }
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 ,
addr = > ram [ addr & RamSizeMask ] ,
( addr , value ) = > ram [ addr & RamSizeMask ] = value
)
} ;
_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
2013-09-14 19:34:14 +00:00
}
2011-01-18 10:28:10 +00:00
}