377 lines
8.5 KiB
C#
377 lines
8.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
namespace BizHawk.Emulation.Cores.Calculators
|
|
{
|
|
public class TI83LinkPort
|
|
{
|
|
// 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.
|
|
readonly TI83 Parent;
|
|
|
|
private FileStream CurrentFile;
|
|
//private int FileBytesLeft;
|
|
private byte[] VariableData;
|
|
|
|
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 TI83LinkPort(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.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.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.LinkState & 3) == 0)
|
|
StepsLeft--;
|
|
break;
|
|
|
|
case 3:
|
|
//Receive step 3: Raise the other device's line back up.
|
|
Parent.LinkInput = 0;
|
|
StepsLeft--;
|
|
break;
|
|
|
|
case 2:
|
|
//Receive step 4: Wait for the calc to raise its line back up.
|
|
if ((Parent.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.LinkState != 3)
|
|
{
|
|
int Bit = Parent.LinkState & 1;
|
|
int Shift = 8 - BitsLeft;
|
|
CurrentByte |= (byte)(Bit << Shift);
|
|
StepsLeft--;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
//send step 2: Lower our line.
|
|
Parent.LinkInput = Parent.LinkOutput ^ 3;
|
|
StepsLeft--;
|
|
break;
|
|
|
|
case 3:
|
|
//Send step 3: wait for the calc to raise its line.
|
|
if ((Parent.LinkOutput & 3) == 0)
|
|
StepsLeft--;
|
|
break;
|
|
|
|
case 2:
|
|
//Send step 4: raise the other devices lines.
|
|
Parent.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;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SendFileToCalc(FileStream FS, bool Verify)
|
|
{
|
|
if (Verify)
|
|
VerifyFile(FS);
|
|
|
|
FS.Seek(55, SeekOrigin.Begin);
|
|
CurrentFile = FS;
|
|
SendNextFile();
|
|
}
|
|
|
|
private void VerifyFile(FileStream FS)
|
|
{
|
|
//Verify the file format.
|
|
byte[] Expected = new byte[] { 0x2a, 0x2a, 0x54, 0x49, 0x38, 0x33, 0x2a, 0x2a, 0x1a, 0x0a, 0x00 };
|
|
byte[] Actual = new byte[11];
|
|
|
|
FS.Seek(0, SeekOrigin.Begin);
|
|
FS.Read(Actual, 0, 11);
|
|
|
|
//Check the header.
|
|
for (int n = 0; n < 11; n++)
|
|
if (Expected[n] != Actual[n])
|
|
{
|
|
FS.Close();
|
|
throw new IOException("Invalid Header.");
|
|
}
|
|
|
|
//Seek to the end of the comment.
|
|
FS.Seek(53, SeekOrigin.Begin);
|
|
|
|
int Size = FS.ReadByte() + FS.ReadByte() * 256;
|
|
|
|
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;
|
|
}
|
|
|
|
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.
|
|
foreach (byte B in VariableData)
|
|
CurrentData.Enqueue(B);
|
|
|
|
//Calculate the checksum.
|
|
ushort Checksum = 0;
|
|
for (int n = 2; n < VariableData.Length; n++)
|
|
Checksum += VariableData[n];
|
|
|
|
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;
|
|
NextStep = FinalizeFile;
|
|
Parent.LinkActive = true;
|
|
}
|
|
|
|
private void OutOfMemory()
|
|
{
|
|
CurrentFile.Close();
|
|
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;
|
|
NextStep = FinalizeFile;
|
|
Parent.LinkActive = true;
|
|
}
|
|
|
|
private void FinalizeFile()
|
|
{
|
|
// Resets the link software, and checks to see if there is an additional file to send.
|
|
CurrentData.Clear();
|
|
Parent.LinkActive = false;
|
|
NextStep = null;
|
|
SendNextFile();
|
|
}
|
|
}
|
|
}
|