/*
    CaPriCe Forever - Amstrad CPC 464/664/6128 emulator
    Copyright (C) 2014-2019 by Frédéric Coste

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <vcl.h>
#pragma hdrstop
#include "TFormINOUTEditor.h"
#include "TFormMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"


//=================================
// Options


//=================================



const AnsiString CRTCRegisters[] =
{
  /* 00 */ "(Horizontal Total)",
  /* 01 */ "(Horizontal Displayed)",
  /* 02 */ "(Horizontal Sync Position)",
  /* 03 */ "(Sync Width)",
  /* 04 */ "(Vertical Total)",
  /* 05 */ "(Vertical Total Adjust)",
  /* 06 */ "(Vertical Displayed)",
  /* 07 */ "(Vertical Sync Position)",
  /* 08 */ "(Interlace && Skew)",
  /* 09 */ "(Maximum Raster Address)",
  /* 10 */ "(Cursor Start Raster)",
  /* 11 */ "(Cursor End Raster)",
  /* 12 */ "(Start Address MSB)",
  /* 13 */ "(Start Address LSB)",
  /* 14 */ "(Cursor MSB)",
  /* 15 */ "(Cursor LSB)",
  /* 16 */ "(Light Pen MSB)",
  /* 17 */ "(Light Pen LSB)",
  /* 18 */ "",
  /* 19 */ "",
  /* 20 */ "",
  /* 21 */ "",
  /* 22 */ "",
  /* 23 */ "",
  /* 24 */ "",
  /* 25 */ "",
  /* 26 */ "",
  /* 27 */ "",
  /* 28 */ "",
  /* 29 */ "",
  /* 30 */ "",
  /* 31 */ "(Status)",
};

const AnsiString PSGRegisters[] =
{
  /* 00 */ "(Ch A Fine Tone Period)",
  /* 01 */ "(Ch A Coarse Tone Period)",
  /* 02 */ "(Ch B Fine Tone Period)",
  /* 03 */ "(Ch B Coarse Tone Period)",
  /* 04 */ "(Ch C Fine Tone Period)",
  /* 05 */ "(Ch C Coarse Tone Period)",
  /* 06 */ "(Noise Period Control)",
  /* 07 */ "(Mixer Control-I/O Enable)",
  /* 08 */ "(Ch A Amplitude Control)",
  /* 09 */ "(Ch B Amplitude Control)",
  /* 10 */ "(Ch C Amplitude Control)",
  /* 11 */ "(Envelope Fine Period)",
  /* 12 */ "(Envelope Coarse Period)",
  /* 13 */ "(Envelope Shape/Cycle Control)",
  /* 14 */ "(I/O Port Data)",
  /* 15 */ "(I/O Port Data)",
};




TFormINOUTEditor *FormINOUTEditor;
//---------------------------------------------------------------------------
__fastcall TFormINOUTEditor::TFormINOUTEditor(TComponent* Owner)
  : TForm(Owner)
{
  mEmulatorP = NULL;
  mSettingsP = NULL;

  mPort = 0xffff;
  mVal = 0;
  ComboBoxINOUT->ItemIndex = 0;

  RichEditOperations->ParentFont = true;

  mFormEditValue = new TFormEditValue(this);

  // Apply theme
  Font->Name = "Amstrad CPC464 Regular";
  Font->Size = 6;
  for (int Loop=0; Loop < ComponentCount; Loop++)
  {
    if (Components[Loop]->Name.Pos("Panel") == 1)
    {
      ((TPanel*)Components[Loop])->Font->Color = clBlack;
    }
    else if (Components[Loop]->Name.Pos("ComboBox") == 1)
    {
      ((TComboBox*)Components[Loop])->Font->Color = clBlack;
    }
  }
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::FormDestroy(TObject *Sender)
{
  delete mFormEditValue;

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::FormShow(TObject *Sender)
{
  if (FormMain == NULL)
  {
    Close();
    return;
  }

	FormMain->OnINOUTFormShow();

	// Force linespacing
	PARAFORMAT2 ParaFormat;
	memset((void*)&ParaFormat, 0, sizeof(PARAFORMAT2));
	ParaFormat.cbSize = sizeof(PARAFORMAT2);
	ParaFormat.dwMask = PFM_LINESPACING;
	ParaFormat.dyLineSpacing = 25;
	ParaFormat.bLineSpacingRule = 5;
	RichEditOperations->Clear();
	RichEditOperations->Perform(EM_SETPARAFORMAT, 0, (LPARAM)&ParaFormat);

	if (mSettingsP)
	{
		mSettingsP->InputOutputEditorFormVisible = true;
	}

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::FormHide(TObject *Sender)
{
	if (FormMain != NULL)
	{
		FormMain->OnINOUTFormHide();
	}

	if (mSettingsP)
  {
    mSettingsP->InputOutputEditorFormVisible = false;
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::SetSettings(TSettings* Value)
{
  mSettingsP = Value;
  mFormEditValue->Settings = mSettingsP;

  if (    (mSettingsP->InputOutputEditorFormLeft != 0)
       || (mSettingsP->InputOutputEditorFormTop != 0)
       || (mSettingsP->InputOutputEditorFormWidth != 0)
       || (mSettingsP->InputOutputEditorFormHeight != 0) )
  {
    Position = poDesigned;
    Left = mSettingsP->InputOutputEditorFormLeft;
    Top = mSettingsP->InputOutputEditorFormTop;
    Width = mSettingsP->InputOutputEditorFormWidth;
    Height = mSettingsP->InputOutputEditorFormHeight;
  }

	// Apply theme
	Color = mSettingsP->ThemeBackgroundColor;
	Font->Color = mSettingsP->ThemeText1Color;
	for (int Loop=0; Loop < ComponentCount; Loop++)
	{
		if (Components[Loop]->Name.Pos("PanelButton") == 1)
		{
			((TPanel*)Components[Loop])->Color = mSettingsP->ThemeButtonColor;
			((TPanel*)Components[Loop])->Font->Color = mSettingsP->ThemeButtonTextColor;
		}
		else if (Components[Loop]->Name.Pos("GroupBox") == 1)
		{
			((TGroupBox*)Components[Loop])->Font->Color = mSettingsP->ThemeText2Color;
		}
		else if (Components[Loop]->Name.Pos("Label") == 1)
		{
			((TLabel*)Components[Loop])->Font->Color = mSettingsP->ThemeText1Color;
		}
		else if (Components[Loop]->Name.Pos("RichEdit") == 1)
		{
			((TRichEdit*)Components[Loop])->Font->Color = mSettingsP->ThemeText1Color;
		}

		// Refresh display
		if ( (Visible == true) && (((TControl*)Components[Loop])->Visible == true) )
		{
			((TControl*)Components[Loop])->Invalidate();
		}
	}

	// Update values
	Port = mPort;
	Val = mVal;

	if (mSettingsP->HideAll == false)
	{
		Visible = mSettingsP->InputOutputEditorFormVisible;
	}
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::WMMove(TMessage &Message)
{
	if ( (Visible == true) && (mSettingsP) )
	{
		mSettingsP->InputOutputEditorFormLeft = Left;
		mSettingsP->InputOutputEditorFormTop = Top;
	}

  NOT_USED(Message);
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::FormResize(TObject *Sender)
{
  if ( (Visible == true) && (mSettingsP) )
  {
    mSettingsP->InputOutputEditorFormWidth = Width;
    mSettingsP->InputOutputEditorFormHeight = Height;
  }

  // Fix vertical scroll issue
  RichEditOperations->Refresh();

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::SetPort(tUShort Port)
{
  mPort = Port;

  PanelInstructionPort->Caption = AnsiString::IntToHex(mPort, 4);

  // Update port groupbox
  for (int Loop=0; Loop < 16; Loop++)
  {
    TPanel* Panel = (TPanel*)FindComponent("PanelCheckPortA" + AnsiString(Loop));
    if (Panel && mSettingsP)
    {
      if (Port & (1 << Loop))
      {
        Panel->Color = mSettingsP->ThemeButtonTextColor;
        Panel->Font->Color = mSettingsP->ThemeButtonColor;
      }
      else
      {
        Panel->Color = mSettingsP->ThemeButtonColor;
        Panel->Font->Color = mSettingsP->ThemeButtonTextColor;
      }
    }
  }

  // IN ?
  if (ComboBoxINOUT->ItemIndex == 1)
  {
    // Update read vlaue
    tRegister RegPort;
    RegPort.UShort.l = Port;
    Val = Engine_Z80_IN_Handler(mEmulatorP->NativeCPC, RegPort);
  }

  UpdateOperations();
}
//---------------------------------------------------------------------------
void __fastcall TFormINOUTEditor::SetVal(tUChar Val)
{
  mVal = Val;

  PanelInstructionValue->Caption = AnsiString::IntToHex(mVal, 2);

  // Update port groupbox
  for (int Loop=0; Loop < 8; Loop++)
  {
    TPanel* Panel = (TPanel*)FindComponent("PanelCheckDataD" + AnsiString(Loop));
    if (Panel && mSettingsP)
    {
      if (Val & 0x01)
      {
        Panel->Color = mSettingsP->ThemeButtonTextColor;
        Panel->Font->Color = mSettingsP->ThemeButtonColor;
      }
      else
      {
        Panel->Color = mSettingsP->ThemeButtonColor;
        Panel->Font->Color = mSettingsP->ThemeButtonTextColor;
      }
    }

    Val >>= 1;
  }

  UpdateOperations();
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::PanelCheckPortClick(TObject *Sender)
{
  int Index = ((TPanel*)Sender)->Tag;
  Port = Port ^(1 << Index);

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::PanelCheckDataClick(TObject *Sender)
{
  int Index = ((TPanel*)Sender)->Tag;
  Val = Val ^(1 << Index);

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::ComboBoxINOUTChange(TObject *Sender)
{
  if (mSettingsP == NULL) return;

  if (ComboBoxINOUT->ItemIndex == 0)
  {
    PanelInstructionValue->ParentColor = false;
    PanelInstructionValue->Color = clWhite;
    PanelInstructionValue->Font->Color = clBlack;
  }
  else
  {
    PanelInstructionValue->ParentColor = true;
    PanelInstructionValue->Font->Color = mSettingsP->ThemeText1Color;
  }

  // Update display
  Port = Port;

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::PanelButtonMouseDown(TObject *Sender, TMouseButton Button,
          TShiftState Shift, int X, int Y)
{
  ((TPanel*)Sender)->Color = mSettingsP->ThemeButtonTextColor;
  ((TPanel*)Sender)->Font->Color = mSettingsP->ThemeButtonColor;

  NOT_USED(Button);
  NOT_USED(Shift);
  NOT_USED(X);
  NOT_USED(Y);
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::PanelButtonMouseUp(TObject *Sender, TMouseButton Button,
          TShiftState Shift, int X, int Y)
{
  ((TPanel*)Sender)->Color = mSettingsP->ThemeButtonColor;
  ((TPanel*)Sender)->Font->Color = mSettingsP->ThemeButtonTextColor;

  NOT_USED(Button);
  NOT_USED(Shift);
  NOT_USED(X);
  NOT_USED(Y);
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::PanelInstructionPortClick(TObject *Sender)
{
  mFormEditValue->Title = "Port";
  mFormEditValue->CurrentValue = mPort;
  mFormEditValue->Hexadecimal = true;
  mFormEditValue->MinValue = 0;
  mFormEditValue->MaxValue = 65535;
  if (mFormEditValue->ShowModal() == mrOk)
  {
    Port = (tUShort)mFormEditValue->NewValue;
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::PanelInstructionValueClick(TObject *Sender)
{
  if (ComboBoxINOUT->ItemIndex != 0) return;

  mFormEditValue->Title = "Value";
  mFormEditValue->CurrentValue = mVal;
  mFormEditValue->Hexadecimal = true;
  mFormEditValue->MinValue = 0;
  mFormEditValue->MaxValue = 255;
  if (mFormEditValue->ShowModal() == mrOk)
  {
    Val = (tUChar)mFormEditValue->NewValue;
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormINOUTEditor::PanelButtonPeekPCClick(TObject *Sender)
{
tNativeCPC* CPC = mEmulatorP->NativeCPC;
tZ80* Z80 = CPC->Z80;
tUShort NewPort = 0xFFFF;
tUChar NewVal = 0;

  // Get instruction from PC
  tUChar Instruction = Engine_ReadBankedRead(CPC, _PC);
  bool bInput = false;
  bool bOutput = false;
  switch (Instruction)
  {
    case 0xD3: // OUTA
    {
      bOutput = true;
      NewPort = ((_A << 8) + Engine_ReadBankedRead(CPC, _PC+1));
      NewVal = _A;
    }
    break;

    case 0xDB: // INA
    {
      bInput = true;
      NewPort = ((_A << 8) + Engine_ReadBankedRead(CPC, _PC+1));
    }
    break;

    case 0xED:
    {
      Instruction = Engine_ReadBankedRead(CPC, _PC+1);
      switch (Instruction)
      {
        case 0x40: // IN B,(C)
        case 0x48: // IN C,(C)
        case 0x50: // IN D,(C)
        case 0x58: // IN E,(C)
        case 0x60: // IN H,(C)
        case 0x68: // IN L,(C)
        case 0x70: // IN F,(C)
        case 0x78: // IN A,(C)
        case 0xA2: // INI
        case 0xAA: // IND
        case 0xB2: // INIR
        case 0xBA: // INDR
        {
          bInput = true;
          NewPort = _BC;
        }
        break;

        case 0x41: // OUT (C),B
        case 0x49: // OUT (C),C
        case 0x51: // OUT (C),D
        case 0x59: // OUT (C),E
        case 0x61: // OUT (C),H
        case 0x69: // OUT (C),L
        case 0x71: // OUT (C),0
        case 0x79: // OUT (C),A
        case 0xA3: // OUTI
        case 0xAB: // OUTD
        case 0xB3: // OTIR
        case 0xBB: // ITDR
        {
          bOutput = true;
          NewPort = _BC;
          if (Instruction == 0x41) NewVal=_B;
          else if (Instruction == 0x49) NewVal=_C;
          else if (Instruction == 0x51) NewVal=_D;
          else if (Instruction == 0x59) NewVal=_E;
          else if (Instruction == 0x61) NewVal=_H;
          else if (Instruction == 0x69) NewVal=_L;
          else if (Instruction == 0x71) NewVal=0;
          else if (Instruction == 0x79) NewVal=_A;
          else if (Instruction == 0xA3) NewVal=Engine_ReadBankedRead(CPC, _HL);
          else if (Instruction == 0xAB) NewVal=Engine_ReadBankedRead(CPC, _HL);
          else if (Instruction == 0xB3) NewVal=Engine_ReadBankedRead(CPC, _HL);
          else if (Instruction == 0xBB) NewVal=Engine_ReadBankedRead(CPC, _HL);
        }
        break;
      }
    }
    break;

    default:
    {
    }
  }

  if (bOutput == true)
  {
    Port = NewPort;
    Val = NewVal;
    ComboBoxINOUT->ItemIndex = 0;
    ComboBoxINOUTChange(Sender);
  }
  else if (bInput == true)
  {
    Port = NewPort;
    tRegister RegPort;
    RegPort.UShort.l = Port;
    Val = Engine_Z80_IN_Handler(CPC, RegPort);
    ComboBoxINOUT->ItemIndex = 1;
    ComboBoxINOUTChange(Sender);
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::UpdateOperations(void)
{
tNativeCPC* CPC = mEmulatorP->NativeCPC;
bool bOutput = (ComboBoxINOUT->ItemIndex == 0) ? true : false;

  RichEditOperations->Clear();
  RichEditOperations->Lines->BeginUpdate();

  // A15=0? GateArray
  if ((mPort & 0xc000) == 0x4000)
  {
    if (bOutput == true)
    {
      DisplayGateArrayOutput();
    }
  }

  // A14=0? CRTC
  if ((mPort & 0x4000) == 0)
  {
    if (bOutput == true)
    {
      DisplayCRTCOutput();
    }
    else
    {
      DisplayCRTCInput();
    }
  }

  // A13=0? ROM select
  if ((mPort & 0x2000) == 0)
  {
    RichEditOperations->Lines->Add("UpperROM: " + AnsiString(Val) + " Selected");
    RichEditOperations->Lines->Add("");
  }

  // A12=0? Printer
  if ((mPort & 0x1000) == 0)
  {
    RichEditOperations->Lines->Add("Printer: Data = &" + AnsiString::IntToHex(Val, 2));
    RichEditOperations->Lines->Add("");
  }

  // A11=0? PPI
  if ((mPort & 0x0800) == 0)
  {
    if (bOutput == true)
    {
      DisplayPPIOutput();
    }
    else
    {
      DisplayPPIInput();
    }
  }

  // A10=0? Expansion Peripherals
  if ((mPort & 0x0400) == 0)
  {
    //
  }

  // 512Ko Bank memory
  if (CPC->prefP->Use4MMemoryExtension)
  {
    if ( (((mPort >> 8) & 0xf8) == 0x78) && ((mVal & 0xc0) == 0xc0) )
    {
      // Set 512Ko banks offset
      AnsiString Line = "Memory: 512Ko Bank = " + AnsiString((7 - ((mPort >> 8) & 7)));
      RichEditOperations->Lines->Add(Line);
      RichEditOperations->Lines->Add("");
    }
	}

#ifdef USE_XMASS
	if (CPC->prefP->UseXMass)
	{
		if ((mPort & 0xfff8) == 0xfd08)
		{
			if (bOutput == true)
			{
				DisplayIDEOutput();
			}
			else
			{
				DisplayIDEInput();
			}
		}
	}
#endif /* USE_XMASS */

  RichEditOperations->Lines->EndUpdate();
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::DisplayGateArrayOutput(void)
{
tNativeCPC* CPC = mEmulatorP->NativeCPC;
tGateArray* GateArray = CPC->GateArray;

  switch ((Val >> 6) & 3)
  {
    case 0:
    {
      AnsiString Line = "GateArray: Pen = " + AnsiString(Val & 0x10 ? 0x10 : Val & 0x0f);
      RichEditOperations->Lines->Add(Line);
    }
    break;

    case 1:
    {
      AnsiString Line = "GateArray: (Pen " + AnsiString(GateArray->pen) + ") Colour = " + AnsiString(Val & 0x1f);
      RichEditOperations->Lines->Add(Line);
    }
    break;

    case 2:
    {
      AnsiString Line = "GateArray: LowerROM = " + AnsiString((Val & 0x04) ? 0 : 1);
      RichEditOperations->Lines->Add(Line);
      Line = "GateArray: UpperROM = " + AnsiString((Val & 0x08) ? 0 : 1);
      RichEditOperations->Lines->Add(Line);
      Line = "GateArray: ITDelay = " + AnsiString((Val & 0x10) ? 1 : 0);
      RichEditOperations->Lines->Add(Line);
      Line = "GateArray: Mode = " + AnsiString(Val & 0x03);
      RichEditOperations->Lines->Add(Line);
    }
    break;

    case 3:
    {
      AnsiString Line = "GateArray: RAMBank= " + AnsiString((Val >> 3) & 7);
      RichEditOperations->Lines->Add(Line);
      Line = "GateArray: RAMMapping= " + AnsiString(Val & 7);
      RichEditOperations->Lines->Add(Line);
    }
    break;
  }

  RichEditOperations->Lines->Add("");
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::DisplayCRTCOutput(void)
{
tNativeCPC* CPC = mEmulatorP->NativeCPC;

  AnsiString Line = "CRTC: ";

  switch ((mPort >> 8) & 3)
  {
    case 0:
    {
      Line += "Select R" + AnsiString(mVal) + " ";

      if ( (mVal <= 31) && (CRTCRegisters[mVal].Length() > 0) )
      {
        Line += CRTCRegisters[mVal];
      }
      else
      {
        Line += "(UNKNOWN)";
      }
    }
    break;

    case 1:
    {
      tUChar RegSelect = CPC->CRTC->reg_select;
      Line += "R" + AnsiString(RegSelect) + " ";
      if (RegSelect < 16)
      {
        Line += CRTCRegisters[RegSelect & 31];
      }
      Line += "= " + AnsiString(Val) + " (&" + AnsiString::IntToHex(Val, 2) + ")";
    }
    break;

    default:
    {
      Line += "UNKNOWN";
    }
  }

  RichEditOperations->Lines->Add(Line);
  RichEditOperations->Lines->Add("");
}
//---------------------------------------------------------------------------
void __fastcall TFormINOUTEditor::DisplayCRTCInput(void)
{
tNativeCPC* CPC = mEmulatorP->NativeCPC;

  AnsiString Line = "CRTC: ";

  switch ((mPort >> 8) & 3)
  {
    case 3:
    {
      Line += "Read R" + AnsiString(CPC->CRTC->reg_select) + " ";

      if ( (mVal <= 31) && (CRTCRegisters[CPC->CRTC->reg_select].Length() > 0) )
      {
        Line += CRTCRegisters[CPC->CRTC->reg_select];
      }
      else
      {
        Line += "(UNKNOWN)";
      }

      Line += "= &" + AnsiString::IntToHex(CPC->CRTC->registers[CPC->CRTC->reg_select], 2);
    }
    break;

    case 2:
    {
      Line += "Read Status";
    }
    break;

    default:
    {
      Line += "UNKNOWN";
    }
  }

  RichEditOperations->Lines->Add(Line);
  RichEditOperations->Lines->Add("");
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::DisplayPPIOutput(void)
{
tNativeCPC* CPC = mEmulatorP->NativeCPC;
tPPI* PPI = CPC->PPI;

  switch ((mPort >> 8) & 3)
  {
    case 0: // write to port A?
    {
      if (!(PPI->control & 0x10)) // port A set to output?
      {
        tPSG* PSG = CPC->PSG;
        if ((PSG->control & 0xc0) == 0xc0) // latch address?
        {
          AnsiString Line = "PSG: Register select = " + AnsiString(Val);
          RichEditOperations->Lines->Add(Line);
        }
        else
        {
          AnsiString Line = "PSG: Register " + AnsiString(PSG->reg_select) + "  = " + AnsiString(Val);
          RichEditOperations->Lines->Add(Line);
        }
      }
      else
      {
        AnsiString Line = "PPI: Port A set as INPUT";
        RichEditOperations->Lines->Add(Line);
      }
    }
    break;

    case 1: // write to port B?
    {
      AnsiString Line = "PPI: Port B should be set as input";
      RichEditOperations->Lines->Add(Line);
    }
    break;

    case 2: // write to port C?
    {
      if (!(PPI->control & 1)) // output lower half?
      {
        AnsiString Line = "PPI: Keyboard line = " + AnsiString(Val & 0x0f);
        RichEditOperations->Lines->Add(Line);
      }
      else
      {
        AnsiString Line = "PPI: Lower Half Port C set as INPUT";
        RichEditOperations->Lines->Add(Line);
      }

      if (!(PPI->control & 0x10)) // output upper half?
      {
        AnsiString Line = "PPI: Tape motor = " + AnsiString((Val & 0x10) ? 1 : 0);
        RichEditOperations->Lines->Add(Line);
        Line = "PPI: Tape output = " + AnsiString((Val & 0x20) ? 1 : 0);
        RichEditOperations->Lines->Add(Line);
        switch ((Val >> 6) & 3)
        {
          case 0: Line = "PPI: PSG Function = INACTIVE"; break;
          case 1: Line = "PPI: PSG Function = READ FROM"; break;
          case 2: Line = "PPI: PSG Function = WRITE TO"; break;
          case 3: Line = "PPI: PSG Function = LATCH ADDRESS"; break;
        }
        RichEditOperations->Lines->Add(Line);
      }
      else
      {
        AnsiString Line = "PPI: Upper Half Port C set as INPUT !!";
        RichEditOperations->Lines->Add(Line);
      }
    }
    break;

    case 3:
		{
			// Mode Set
			if (Val & 0x80)
			{
				AnsiString Line = "PPI: Set Group A Mode " + AnsiString((Val >> 5) & 3);
				RichEditOperations->Lines->Add(Line);
				Line = "PPI: Set Group B Mode " + AnsiString((Val >> 2) & 1);
				RichEditOperations->Lines->Add(Line);
				if (Val & 0x10)
				{
					Line = "PPI: Port A as INPUT";
				}
				else
				{
					Line = "PPI: Port A as OUTPUT";
				}
				RichEditOperations->Lines->Add(Line);
				if (Val & 0x02)
				{
					Line = "PPI: Port B as INPUT";
				}
				else
				{
					Line = "PPI: Port B as OUTPUT";
				}
				RichEditOperations->Lines->Add(Line);
				if (Val & 0x08)
				{
					Line = "PPI: Port C UPPER as INPUT";
				}
				else
				{
					Line = "PPI: Port C UPPER as OUTPUT";
				}
				RichEditOperations->Lines->Add(Line);
				if (Val & 0x01)
				{
					Line = "PPI: Port C LOWER as INPUT";
				}
				else
				{
					Line = "PPI: Port C LOWER as OUTPUT";
				}
				RichEditOperations->Lines->Add(Line);
			}
			// Bit Set
			else
			{
				AnsiString Line = "PPI: Bit" + AnsiString((Val >> 1) & 7) + " = " + AnsiString(Val & 0x01);
				RichEditOperations->Lines->Add(Line);
			}
		}
    break;
  }
  
  RichEditOperations->Lines->Add("");
}
//---------------------------------------------------------------------------
void __fastcall TFormINOUTEditor::DisplayPPIInput(void)
{
tNativeCPC* CPC = mEmulatorP->NativeCPC;
tPPI* PPI = CPC->PPI;

  switch ((mPort >> 8) & 3)
  {
    case 0: // read from port A?
    {
      AnsiString Line = "PPI: Port A = &" + AnsiString::IntToHex(Val, 2);
      RichEditOperations->Lines->Add(Line);

      if (PPI->control & 0x10) // port A set to input?
      {
        tPSG* PSG = CPC->PSG;
        if ((PSG->control & 0xc0) == 0x40) // PSG control set to read?
        {
          if (PSG->reg_select == 14) // PSG port A?
          {
            if (!(PSG->RegisterAY.Index[7] & 0x40)) // port A in input mode?
            {
              AnsiString Line = "PPI: Keyboard Matrix[" + AnsiString(CPC->keyboard_line & 0x0f);
              Line +=  "]= &" + AnsiString::IntToHex(Val, 2);
              RichEditOperations->Lines->Add(Line);
            }
          }
        }
      }
    }
    break;

    case 1: // read from port B?
    {
      AnsiString Line = "PPI: Port B = &" + AnsiString::IntToHex(Val, 2);
      RichEditOperations->Lines->Add(Line);
      if (PPI->control & 2) // port B set to input?
      {
        AnsiString Line = "PPI: Port B Tape= " + AnsiString((Val >> 7) & 0x01);
        RichEditOperations->Lines->Add(Line);
        Line = "PPI: Port B Printer Busy= " + AnsiString((Val >> 6) & 0x01);
        RichEditOperations->Lines->Add(Line);
        Line = "PPI: Port B Expansion= " + AnsiString((Val >> 5) & 0x01);
        RichEditOperations->Lines->Add(Line);
        Line = "PPI: Port B 50Hz= " + AnsiString((Val >> 4) & 0x01);
        RichEditOperations->Lines->Add(Line);
        Line = "PPI: Port B Brand= " + AnsiString((Val >> 1) & 0x07);
        RichEditOperations->Lines->Add(Line);
        Line = "PPI: Port B VSync= " + AnsiString((Val >> 0) & 0x01);
        RichEditOperations->Lines->Add(Line);
      }
      else
      {
        AnsiString Line = "PPI: Port B should be set as INPUT !!";
        RichEditOperations->Lines->Add(Line);
      }
    }
    break;

    case 2: // read from port C?
    {
			AnsiString Line = "PPI: Port C = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
			if (PPI->control & 9) // either half set to input?
			{
				AnsiString Line = "PPI: Port C should be set as OUTPUT !!";
				RichEditOperations->Lines->Add(Line);
			}
		}
    break;

    case 3:
    {
      AnsiString Line = "PPI: ILLEGAL Condition !!";
			RichEditOperations->Lines->Add(Line);
		}
		break;
	}

	RichEditOperations->Lines->Add("");
}
//---------------------------------------------------------------------------


void __fastcall TFormINOUTEditor::DisplayIDEOutput(void)
{
	switch (mPort & 7)
	{
		case 0:
		{
			AnsiString Line = "X-Mass: Write Data = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
		}
		break;

		case 1:
		{
			AnsiString Line = "X-Mass: Write Features = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
		}
		break;

		case 7:
		{
			AnsiString Line = "X-Mass: Write Command = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
		}
		break;

		default:
		{
			AnsiString Line = "X-Mass: Write Register " + AnsiString(mPort & 7) + " = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
		}
		break;
	}

	RichEditOperations->Lines->Add("");
}
//---------------------------------------------------------------------------
void __fastcall TFormINOUTEditor::DisplayIDEInput(void)
{
	switch (mPort & 7)
	{
		case 0:
		{
			AnsiString Line = "X-Mass: Read Data = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
		}
		break;

		case 1:
		{
			AnsiString Line = "X-Mass: Read Error = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
		}
		break;

		case 7:
		{
			AnsiString Line = "X-Mass: Read Status = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
		}
		break;

		default:
		{
			AnsiString Line = "X-Mass: Read Register " + AnsiString(mPort & 7) + " = &" + AnsiString::IntToHex(Val, 2);
			RichEditOperations->Lines->Add(Line);
		}
		break;
	}

	RichEditOperations->Lines->Add("");
}
//---------------------------------------------------------------------------

