unit frmMainunt;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, ToolWin, Menus, ImgList, IntvDis, frmDebuggerUnt, Base,
  ExtCtrls, StdCtrls, DXDraws, DXClass, DXInput, DXSounds, Keyboard,
  frmDebugger6502Unt, SmallPort, ZipMstr;

Const
  VertOffset = 4;
  MaxScan : Integer = 250;
  SetLocDelay = 5;
  Read_Delay  = 12500;

  ImportTime    = 30;
  ECSInvalidKey = 255;
  RepeatTime    = 80;
  Playback_Freq = 44100;

  KBDCKeyTable : Array[0..9,0..7] Of Byte =
   ((0,        0,       0,      0,   0,   0,   kLCTRL, kLSHIFT),   {  0-7  }
    (0,        0,       0,      0,   0,   0,   kLALT,  kCAPLOCK),  {  8-15 }
    (0,        kSLASH,  kCOMMA, kN,  kV,  kX,  0,      kSPACE),    { 16-23 }
    (kRT,      kPERIOD, kM,     kB,  kC,  kZ,  0,      kPGDN),     { 24-31 }
    (kDN,      kSCOLON, kK,     kH,  kF,  kS,  0,      kTAB),      { 32-39 }
    (kRBRACE,  kP,      kI,     kY,  kR,  kW,  0,      kQ),        { 40-47 }
    (kUP,      kMINUS,  kN9,    kN7, kN5, kN3, 0,      kN1),       { 48-55 }
    (kEQUAL,   kN0,     kN8,    kN6, kN4, kN2, 0,      kLBRACE),   { 56-63 }
    (kENTER,   kLF,     kO,     kU,  kT,  kE,  0,      kESC),      { 64-71 }
    (kBACKSPC, kOQUOTE, kL,     kJ,  kG,  kD,  0,      kA));       { 72-79 }

  ASCIIToKBDCShift : Array[0..255] Of Byte =
   (255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 00-0F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 10-1F }
     23, 55, 73, 53, 60, 52, 51,255, 50, 57, 58, 56,255,255,255,255, { 20-2F }
    255,255,255,255,255,255,255,255,255,255, 33,255, 18,255, 25, 17, { 30-3F }
     61, 79, 27, 28, 77, 69, 36, 76, 35, 42, 75, 34, 74, 26, 19, 66, { 40-4F }
     41, 47, 44, 37, 68, 67, 20, 45, 21, 43, 29,255,255,255, 59, 49, { 50-5F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 60-6F }
    255,255,255,255,255,255,255,255,255,255,255, 63,255, 40,255,255, { 70-7F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 80-8F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 90-9F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { A0-AF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { B0-BF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { C0-CF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { D0-DF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { E0-EF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255);{ F0-FF }

  ASCIIToKBDCUnshift : Array[0..255] Of Byte =
   (255,255,255,255,255,255,255,255,255,255,255,255,255, 64,255,255, { 00-0F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 10-1F }
     23,255,255,255,255,255,255, 73,255,255,255,255, 18, 49, 25,255, { 20-2F }
     57, 55, 61, 53, 60, 52, 59, 51, 58, 50,255, 33,255, 56,255, 17, { 30-3F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 40-4F }
    255,255,255,255,255,255,255,255,255,255,255, 63,255, 40,255,255, { 50-5F }
    255, 79, 27, 28, 77, 69, 36, 76, 35, 42, 75, 34, 74, 26, 19, 66, { 60-6F }
     41, 47, 44, 37, 68, 67, 20, 45, 21, 43, 29,255,255,255,255,255, { 70-7F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 80-8F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 90-9F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { A0-AF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { B0-BF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { C0-CF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { D0-DF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { E0-EF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255);{ F0-FF }

  ECSKeyTable : Array[0..7,0..7] Of Byte =
   ((kLF    ,kCOMMA,kN ,kV ,kX ,kSPACE,0,0),          { 0-7   }
    (kPERIOD,kM    ,kB ,kC ,kZ ,kDN   ,0,0),          { 8-15  }
    (kSCOLON,kK    ,kH ,kF ,kS ,kUP   ,0,0),          { 16-23 }
    (kP     ,kI    ,kY ,kR ,kW ,kQ    ,0,0),          { 24-31 }
    (kTAB   ,kN9   ,kN7,kN5,kN3,kN1   ,0,0),          { 32-39 }
    (kN0    ,kN8   ,kN6,kN4,kN2,kRT   ,0,0),          { 40-47 }
    (kENTER ,kO    ,kU ,kT ,kE ,kRCTRL,0,0),          { 48-55 }
    (0      ,kL    ,kJ ,kG ,kD ,kA    ,kRSHIFT,0));   { 56-63 }

  { left,   comma, n, v, x, space, NULL,  NULL }
  { period, m,     b, c, z, down,  NULL,  NULL }
  { scolon, k,     h, f, s, up,    NULL,  NULL }
  { p,      i,     y, r, w, q,     NULL,  NULL }
  { esc,    9,     7, 5, 3, 1,     NULL,  NULL }
  { 0,      8,     6, 4, 2, right, NULL,  NULL }
  { enter,  o,     u, t, e, ctl,   NULL,  NULL }
  { NULL,   l,     j, g, d, a,     shift, NULL }

  ASCIIToECSUnshift : Array[0..255] Of Byte =
   (255,255,255,255,255,255,255,255,255,255,255,255,255, 48,255,255, { 00-0F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 10-1F }
      5,255,255,255,255,255,255,255,255,255,255,255,  1,255,  8,255, { 20-2F }
     40, 37, 44, 36, 43, 35, 42, 34, 41, 33,255, 16,255,255,255,255, { 30-3F }
    255, 61, 10, 11, 60, 52, 19, 59, 18, 25, 58, 17, 57,  9,  2, 49, { 40-4F }
     24, 29, 27, 20, 51, 50,  3, 28,  4, 26, 12,255,255,255,255,255, { 50-5F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 60-6F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 70-7F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 80-8F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 90-9F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { A0-AF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { B0-BF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { C0-CF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { D0-DF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { E0-EF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255);{ F0-FF }

  ASCIIToECSShift : Array[0..255] Of Byte =
   (255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 00-0F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 10-1F }
    255,255, 44, 36, 43,  0,255, 45, 33, 40, 41, 35,255, 42,255, 34, { 20-2F }
    255,255,255,255,255,255,255,255,255,255, 16,255,  1, 37,  8, 13, { 30-3F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 40-4F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255, 21,255, { 50-5F }
    255, 61, 10, 11, 60, 52, 19, 59, 18, 25, 58, 17, 57,  9,  2, 49, { 60-6F }
     24, 29, 27, 20, 51, 50,  3, 28,  4, 26, 12,255,255,255,255,255, { 70-7F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 80-8F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { 90-9F }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { A0-AF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { B0-BF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { C0-CF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { D0-DF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, { E0-EF }
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255);{ F0-FF }

  DiscSize = 90;
  DiscXPos = 645;
  DiscYPos = 220;
  Condition : Packed Array[0..7] Of Byte = ($10{FF},$01,$02,$08,$04,$20,$40,$80);
  PreRead   : Packed Array[0..7] Of SmallInt = (0,0,0,0,0,0,-1,0);
  PostRead  : Packed Array[0..7] Of SmallInt = (0,0,0,0,1,1,0,1);
  PreWrite  : Packed Array[0..7] Of SmallInt = (0,0,0,0,0,0,0,0);
  PostWrite : Packed Array[0..7] Of SmallInt = (0,0,0,0,1,1,1,1);
  InterruptFlag = $7FF;  { Interrupt every 800h instructions }
  InterruptCycles = 2000000 / 60;

  ECSPrintName = 'INTVDOS.PRN';

  CS_ADVANCE = $2000;

  SP_VFLIP      = $08000000;
  SP_HFLIP      = $04000000;
  SP_4YSIZE     = $02000000;
  SP_2YSIZE     = $01000000;
  SP_RESOLUTION = $00800000;
  SP_2XSIZE     = $00000400;
  SP_VISIBLE    = $00000200;
  SP_COLLISION  = $00000100;

type
  TfrmMain = class(TDXForm)
    MainMenu1: TMainMenu;
    mnuFile: TMenuItem;
    Open1: TMenuItem;
    Exit1: TMenuItem;
    mnuEmulation: TMenuItem;
    mnuStart: TMenuItem;
    mnuStop: TMenuItem;
    ImageList1: TImageList;
    CoolBar1: TCoolBar;
    ToolBar1: TToolBar;
    btnOpen: TToolButton;
    btnStart: TToolButton;
    btnStop: TToolButton;
    ToolButton1: TToolButton;
    mnuInterfaces: TMenuItem;
    Configure1: TMenuItem;
    Enable1: TMenuItem;
    Preferences1: TMenuItem;
    Usedebugger1: TMenuItem;
    Rebuildgameprofile1: TMenuItem;
    Mouse1: TMenuItem;
    Keyboard2: TMenuItem;
    Controller2: TMenuItem;
    dlgSave: TSaveDialog;
    dlgOpen: TOpenDialog;
    Image1: TImage;
    DXDraw1: TDXDraw;
    mnuVideo: TMenuItem;
    mnuFullScreen: TMenuItem;
    mnuZoom: TMenuItem;
    mnuZoom1x: TMenuItem;
    mnuZoom2x: TMenuItem;
    mnuZoom4x: TMenuItem;
    DXSound1: TDXSound;
    Reset1: TMenuItem;
    btnResetCart: TToolButton;
    mnuDisassemble: TMenuItem;
    mnuTVScreen: TMenuItem;
    mnuScreenShot: TMenuItem;
    ImageList2: TImageList;
    ToolBar2: TToolBar;
    btnReset: TToolButton;
    ToolButton6: TToolButton;
    mnuHelp: TMenuItem;
    mnuAbout: TMenuItem;
    mnuSaveGame: TMenuItem;
    mnuLoadGame: TMenuItem;
    btnPrefs: TToolButton;
    mnuProfile: TMenuItem;
    btnProfile: TToolButton;
    DXInput1: TDXInput2;
    btnRecord: TToolButton;
    SmallPort1: TSmallPort;
    Intv2PC1: TMenuItem;
    imgOverlayScan: TImage;
    mnuRecordAVI: TMenuItem;
    mnuZoom3x: TMenuItem;
    mnuZoomFull: TMenuItem;
    btnManual: TToolButton;
    ZipMaster1: TZipMaster;
    btnNetPlay: TToolButton;
    procedure Exit1Click(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure Rebuildgameprofile1Click(Sender: TObject);
    procedure Configure1Click(Sender: TObject);
    procedure Mouse1Click(Sender: TObject);
    procedure Keyboard2Click(Sender: TObject);
    procedure Controller2Click(Sender: TObject);
    procedure Usedebugger1Click(Sender: TObject);
    procedure Preferences1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure Open1Click(Sender: TObject);
    procedure btnStartClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure mnuFullScreenClick(Sender: TObject);
    procedure mnuStartClick(Sender: TObject);
    procedure mnuStopClick(Sender: TObject);
    procedure mnuZoom1xClick(Sender: TObject);
    procedure mnuZoom2xClick(Sender: TObject);
    procedure mnuZoom4xClick(Sender: TObject);
    procedure DXSound1Initialize(Sender: TObject);
    procedure DXSound1Finalize(Sender: TObject);
    procedure Reset1Click(Sender: TObject);
    procedure btnResetCartClick(Sender: TObject);
    procedure mnuDisassembleClick(Sender: TObject);
    procedure mnuTVScreenClick(Sender: TObject);
    procedure mnuScreenShotClick(Sender: TObject);
    procedure btnResetClick(Sender: TObject);
    procedure CoolBar1Resize(Sender: TObject);
    procedure mnuAboutClick(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure DXDraw1KeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure DXDraw1KeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure mnuSaveGameClick(Sender: TObject);
    procedure mnuLoadGameClick(Sender: TObject);
    procedure btnPrefsClick(Sender: TObject);
    procedure mnuProfileClick(Sender: TObject);
    procedure btnProfileClick(Sender: TObject);
    procedure btnRecordClick(Sender: TObject);
    procedure Intv2PC1Click(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure mnuRecordAVIClick(Sender: TObject);
    procedure mnuZoom3xClick(Sender: TObject);
    procedure mnuZoomFullClick(Sender: TObject);
    procedure btnManualClick(Sender: TObject);
    procedure btnNetPlayClick(Sender: TObject);
  private
    { Private declarations }
    SWidth         : Integer;
    SHeight        : Integer;
    Zoom           : Integer;
    Secondary      : TDirectSoundBuffer;
    Showed         : Boolean;
//    Procedure SaveStatus;
//    Procedure RestoreStatus;
    Procedure SetImageSizes(Starting: Boolean);
    Procedure UpdateDebugger;
    Procedure DeactivateApp(Sender: TObject);
    Procedure SetOverlaySize;
    Procedure OpenImageFile(Const Filename: String; Img: TImage);
    Procedure ClearImage(Var Img: TImage; Stretch: Boolean);
    Procedure LoadImages(FileName: String);
  public
    { Public declarations }
    ShowObjectLocs : Boolean;
    UseDirectDraw  : Boolean;
    DDFullScreen   : Boolean;
    TVScreen       : Boolean;
    ROMFileName    : String;
    FPS            : Double;
    Frames         : Integer;
    BaseTime       : LongInt;
    Procedure Display;
    Procedure UpdateButtonStatuses;
    Procedure LoadGame(FileName,ZipFileName,Title: String);
  end;

var
  frmMain   : TfrmMain;
  Pal       : TPalette;
  Emulating : Integer;

Procedure Emulate;
Procedure Handle_IO_Write(Addr,Value: Word);
Procedure Handle_IO_Read(Addr,Value: Word);

implementation

{$R *.DFM}

Uses MMSystem,frmMenuUnt,Profile,IntvControls,frmPrefsUnt,ProtMenu,IntvSound,
     Equation,HexWrite,DirectX,Wave, frmExpRefUnt, WriteAVI, Equation6502,
     frmAboutUnt, frmProfileSettingsUnt, JPEG, frmAltMenuUnt, IVoice1,
     frmConfigureUnt,KBComponent,ICartROM, frmManualUnt, frmKailleraUnt;

Type
  _TBitmapInfo = Packed Record
    B : TBitmapInfo;
    bmiColors: Packed Array[0..255] Of TRGBQuad;
  end;
  _TLogPalette = Packed Record
    T : TLogPalette;
    P : Packed Array[0..255] Of TPaletteEntry;
  End;

Var
  SoundQueue_In       : Word;
  SoundQueue_Out      : Word;
  SoundQueue_Num      : Word;
  Time0               : Double;
  TimeErr             : Double;
  Locations           : Packed Array[0..1023] Of LongInt;
  ExpError            : Boolean;
  BI                  : _TBitmapInfo;
  BI1                 : _TBitmapInfo;
  PI                  : _TLogPalette;
  LocalBufSize        : LongInt;
  UseMMX              : Boolean;
  Menu                : TMainMenu;
  AVIFile             : TAVIFile;

Procedure CheckAVIStream;
Begin
  If AVIFile.Opened Then AVIFile.WriteFrame(ScnPtr,True);
End; { CheckAVIStream }

Procedure PaintSurface;
Var
  Color       : Word;
  BlockOut    : Word;
  BorderColor : LongInt;
  ColorStack  : Array[0..3] Of LongInt;
  I           : Integer;
  VertAddrOfs : Integer;
  BlankedSTIC : Boolean;

  Procedure DrawBacktab(OnBits: Boolean);
  Var
    _X,_Y,Y,Addr   : Word;
    Addr2,Addr3    : Word;
    Card           : Word;
    Ch             : Word;
    Addr1          : Word;
    FCol,BCol      : Word;
    ColorStackPtr  : Word;
    ColoredSquares : Boolean;
    CStack         : Word;

  Begin
    ColorStackPtr := 0;
    BCol          := ColorStack[0];
    Ch            := 0;
    For _Y := 0 To 11 Do
    Begin
      Addr2 := _Y * 160 * 16;
      For _X := 0 To 19 Do
      Begin
        Addr3 := 0;
        Addr  := BACKTAB + _Y * 20 + _X;
        Card  := IntvMem[Addr];
        If ScreenMode = smColorStack Then
        Begin
          ColoredSquares := ((Card And $1800) = $1000);
          If Not ColoredSquares Then
          Begin
            FCol  := (Card And $7) + ((Card And $1000) Shr 9);
            Ch    := (Card Shr 3) And $FF;
            If (Card And $800) <> 0 Then Ch := Ch And $3F;
            If Ch > 212 Then Ch := 0;
            If (Card And CS_ADVANCE) <> 0 Then
             ColorStackPtr := (ColorStackPtr + 1) And 3;
            BCol := ColorStack[ColorStackPtr];
          End;
        End
        Else
        Begin
          ColoredSquares := False;
          FCol := (Card And $7);
          BCol := ((Card Shr  9) And 1) +
                  ((Card Shr  9) And 2) +
                  ((Card Shr 11) And 4) +
                  ((Card Shr  9) And 8);
          Ch    := (Card Shr 3) And $3F;
        End;

        If Not ColoredSquares Then
        Begin
          If (Card And $800) = 0
           Then Addr1 := GROM_Loc + Ch * 8
           Else Addr1 := GRAM_Loc + Ch * 8;
          Asm
            PUSH  EBX
            PUSH  ESI
            MOV   WORD PTR Y,8

            TEST  BYTE PTR OnBits,0FFh
            JZ    @DoOffBits
            MOV   CL,BYTE PTR FCol
            JMP   @YLoop
@DoOffBits:
            MOV   CL,BYTE PTR BCol
@YLoop:
            MOVZX ESI,WORD PTR Addr1
            MOV   AH,BYTE PTR IntvMem[ESI * 2]  { Read card byte }
            MOV   AL,80h                        { Initial bit mask }
            MOVZX EBX,WORD PTR Addr2
            ADD   BX,WORD PTR Addr3

            MOV   EDX,8                         { Do 8 pixels }
            TEST  BYTE PTR OnBits,0FFh
            JZ    @XLoopOff
@XLoopOn:
            TEST  AH,AL
            JZ    @SkipOn
            CMP   EBX,160*96*2
            JAE   @Bot

            MOV   BYTE PTR DoubleBuf[EBX],CL
            MOV   BYTE PTR DoubleBuf[EBX+160],CL

            OR    BYTE PTR ScreenCollB[EBX],01h       { Write the collision bit }
            OR    BYTE PTR ScreenCollB[EBX+160],01h   { Write the collision bit }
@SkipOn:
            SHR   AL,1
            INC   EBX
            DEC   EDX
            JNZ   @XLoopOn
            JMP   @Bot
@XLoopOff:
            TEST  AH,AL
            JNZ   @SkipOff
            CMP   EBX,160*96*2
            JAE   @Bot

            MOV   BYTE PTR DoubleBuf[EBX],CL
            MOV   BYTE PTR DoubleBuf[EBX+160],CL
@SkipOff:
            SHR   AL,1
            INC   EBX
            DEC   EDX
            JNZ   @XLoopOff
@Bot:
            INC   WORD PTR Addr1
            ADD   WORD PTR Addr3,320
            DEC   WORD PTR Y
            JNZ   @YLoop
            POP  ESI
            POP  EBX
          End; { Asm }
        End
        Else
        Begin
          CStack := ColorStack[ColorStackPtr] And $FF;
          CStack := CStack + (CStack Shl 8);
          If Not OnBits Then
          Begin
            If (_X < 19) And (_Y < 11) Then
            Begin

              { No bounds-checking }

              For Y := 0 To 3 Do
              Begin
                Asm
                  { X loop from 0 to 3 }

                  PUSH  EBX
                  MOVZX EBX,WORD PTR Addr2
                  ADD   BX,WORD PTR Addr3
                  MOV   CX,WORD PTR Card
                  AND   ECX,7
                  CMP   ECX,7
                  JNE   @NotClear0To3
                  MOV   CX,WORD PTR CStack

                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+162],CX
                  ADD   EBX,4
                  JMP   @Do4To7
@NotClear0To3:
                  MOV   CH,CL

                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+160+2],CX
                  OR    WORD PTR ScreenCollB[EBX],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160],0101h { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+2],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160+2],0101h { Write the collision bit }

                  ADD   EBX,4
@Do4To7:
                  { X loop from 4 to 7 }

                  SUB   EAX,EAX
                  MOV   CX,WORD PTR Card
                  SHR   ECX,3
                  AND   ECX,7
                  CMP   ECX,7
                  JNE   @NotClear4To7
                  MOV   CX,WORD PTR CStack

                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+162],CX
                  JMP   @GetOut
@NotClear4To7:
                  MOV   CH,CL

                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+160+2],CX
                  OR    WORD PTR ScreenCollB[EBX],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160],0101h { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+2],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160+2],0101h { Write the collision bit }
@GetOut:
                  ADD   WORD PTR Addr3,320
                  POP   EBX
                End; { Asm }
              End; { For Y }
              For Y := 4 To 7 Do
              Begin
                Asm
                  { X loop from 0 to 3 }

                  PUSH  EBX
                  SUB   EAX,EAX
                  MOVZX EBX,WORD PTR Addr2
                  ADD   BX,WORD PTR Addr3
                  MOV   CX,WORD PTR Card
                  SHR   ECX,6
                  AND   ECX,7
                  CMP   ECX,7
                  JNE   @NotClear0To3
                  MOV   CX,WORD PTR CStack

                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+162],CX
                  ADD   EBX,4
                  JMP   @Do4To7
@NotClear0To3:
                  MOV   CH,CL

                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+160+2],CX
                  OR    WORD PTR ScreenCollB[EBX],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160],0101h { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+2],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160+2],0101h { Write the collision bit }

                  ADD   EBX,4
@Do4To7:
                  { X loop from 4 to 7 }

                  SUB   EAX,EAX
                  MOV   CX,WORD PTR Card
                  MOV   EDX,ECX
                  SHR   ECX,9
                  AND   ECX,3
                  SHR   EDX,11
                  AND   EDX,4
                  OR    ECX,EDX
                  CMP   ECX,7
                  JNE   @NotClear4To7
                  MOV   CX,WORD PTR CStack

                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+162],CX
                  JMP   @GetOut
@NotClear4To7:
                  MOV   CH,CL

                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+160+2],CX
                  OR    WORD PTR ScreenCollB[EBX],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160],0101h { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+2],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160+2],0101h { Write the collision bit }
@GetOut:
                  ADD   WORD PTR Addr3,320
                  POP   EBX
                End; { Asm }
              End; { For Y }
            End
            Else
            Begin

              { With bounds-checking }

              For Y := 0 To 3 Do
              Begin
                Asm
                  { X loop from 0 to 3 }

                  PUSH  EBX
                  MOVZX EBX,WORD PTR Addr2
                  ADD   BX,WORD PTR Addr3
                  MOV   CX,WORD PTR Card
                  AND   ECX,7
                  CMP   ECX,7
                  JNE   @NotClear0To3
                  MOV   CX,WORD PTR CStack

                  CMP   EBX,160*96*2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  CMP   EBX,160*96*2+2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+162],CX
                  ADD   EBX,4
                  JMP   @Do4To7
@NotClear0To3:
                  MOV   CH,CL

                  CMP   EBX,160*96*2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  OR    WORD PTR ScreenCollB[EBX],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160],0101h { Write the collision bit }
                  CMP   EBX,160*96*2+2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+160+2],CX
                  OR    WORD PTR ScreenCollB[EBX+2],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160+2],0101h { Write the collision bit }

                  ADD   EBX,4
                  JMP   @Do4To7
@Do4To7:
                  { X loop from 4 to 7 }

                  SUB   EAX,EAX
                  MOV   CX,WORD PTR Card
                  SHR   ECX,3
                  AND   ECX,7
                  CMP   ECX,7
                  JNE   @NotClear4To7
                  MOV   CX,WORD PTR CStack

                  CMP   EBX,160*96*2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  CMP   EBX,160*96*2+2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+162],CX
                  JMP   @GetOut
@NotClear4To7:
                  MOV   CH,CL

                  CMP   EBX,160*96*2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  OR    WORD PTR ScreenCollB[EBX],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160],0101h { Write the collision bit }
                  CMP   EBX,160*96*2+2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+160+2],CX
                  OR    WORD PTR ScreenCollB[EBX+2],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160+2],0101h { Write the collision bit }
@GetOut:
                  ADD   WORD PTR Addr3,320
                  POP   EBX
                End; { Asm }
              End; { For Y }
              For Y := 4 To 7 Do
              Begin
                Asm
                  { X loop from 0 to 3 }

                  PUSH  EBX
                  SUB   EAX,EAX
                  MOVZX EBX,WORD PTR Addr2
                  ADD   BX,WORD PTR Addr3
                  MOV   CX,WORD PTR Card
                  SHR   ECX,6
                  AND   ECX,7
                  CMP   ECX,7
                  JNE   @NotClear0To3
                  MOV   CX,WORD PTR CStack

                  CMP   EBX,160*96*2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  CMP   EBX,160*96*2+2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+162],CX

                  ADD   EBX,4
                  JMP   @Do4To7
@NotClear0To3:
                  MOV   CH,CL

                  CMP   EBX,160*96*2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  OR    WORD PTR ScreenCollB[EBX],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160],0101h { Write the collision bit }
                  CMP   EBX,160*96*2+2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+160+2],CX
                  OR    WORD PTR ScreenCollB[EBX+2],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160+2],0101h { Write the collision bit }

                  ADD   EBX,4
                  JMP   @Do4To7
@Do4To7:
                  { X loop from 4 to 7 }

                  SUB   EAX,EAX
                  MOV   CX,WORD PTR Card
                  MOV   EDX,ECX
                  SHR   ECX,9
                  AND   ECX,3
                  SHR   EDX,11
                  AND   EDX,4
                  OR    ECX,EDX
                  CMP   ECX,7
                  JNE   @NotClear4To7
                  MOV   CX,WORD PTR CStack

                  CMP   EBX,160*96*2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  CMP   EBX,160*96*2+2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+162],CX
                  JMP   @GetOut
@NotClear4To7:
                  MOV   CH,CL

                  CMP   EBX,160*96*2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX],CX
                  MOV   WORD PTR DoubleBuf[EBX+160],CX
                  OR    WORD PTR ScreenCollB[EBX],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160],0101h { Write the collision bit }
                  CMP   EBX,160*96*2+2
                  JAE   @GetOut
                  MOV   WORD PTR DoubleBuf[EBX+2],CX
                  MOV   WORD PTR DoubleBuf[EBX+160+2],CX
                  OR    WORD PTR ScreenCollB[EBX+2],0101h     { Write the collision bit }
                  OR    WORD PTR ScreenCollB[EBX+160+2],0101h { Write the collision bit }
@GetOut:
                  ADD   WORD PTR Addr3,320
                  POP   EBX
                End; { Asm }
              End; { For Y }
            End;
          End;
        End;
        Inc(Addr2,8);
      End; { For _X }
    End; { For _Y }
  End; { DrawBacktab }

  Procedure DrawSprites(Priority: Boolean);
  Var
    _X,_Y,X,Y      : Integer;
    Addr           : Word;
    X1             : Integer;
    Addr1          : Word;
    Addr2,Addr3    : Word;
    I              : Word;
    FCol           : Word;
    SpriteFlags    : LongInt;
    YSize          : Word;
    SpriteMult     : Word;
    SpriteHeight   : Word;
    HasPriority    : Boolean;
    MinX,MinY      : Integer;
    FlagCollisions : Boolean;
    Drawing        : Boolean;
    Card           : Word;

  Begin
    For I := 7 DownTo 0 Do
    Begin
      HasPriority := ((IntvMem[I + $10] And $2000) = 0);
      Drawing := (HasPriority = Priority);
      Begin
        X     := SpriteX[I];
        Y     := SpriteY[I];
        MinX  := X;
        MinY  := Y;
        If MinX < 0 Then MinX := 0;
        If MinY < 0 Then MinY := 0;
        FCol  := (IntvMem[I + $10] And $7) + ((IntvMem[I + $10] Shr 9) And 8);
        Addr  := Word((LongInt(MinY) * 160 + Word(MinX)) And $FFFF);
        Addr1 := 0;
        SpriteFlags := IntvMem[I] + (LongInt(IntvMem[I + 8]) Shl 16);
        If (IntvMem[I + $0010] And $800) = 0
         Then Addr3 := GROM_Loc + (IntvMem[I + $10] And $07F8)
         Else Addr3 := GRAM_Loc + (IntvMem[I + $10] And $03F8);
        SpriteMult := 1;
        If (SpriteFlags And SP_2YSIZE) <> 0 Then SpriteMult := SpriteMult * 2;
        If (SpriteFlags And SP_4YSIZE) <> 0 Then SpriteMult := SpriteMult * 4;
        SpriteHeight := 8 * SpriteMult;
        FlagCollisions := ((SpriteFlags And SP_COLLISION) <> 0);

        If (SpriteFlags And SP_VFLIP) <> 0 Then
        Begin
          If (SpriteFlags And SP_RESOLUTION) <> 0
           Then Inc(Addr3,15)
           Else Inc(Addr3,7);
        End;
        If (SpriteFlags And SP_RESOLUTION) = 0
         Then YSize := SpriteHeight
         Else YSize := SpriteHeight * 2;
        For _Y := 0 To YSize - 1 Do
        Begin
          If (Y + _Y >= 0) And (Y + _Y < 192) Then
          Begin
            Addr2 := 0;
            X1    := X;
            Card  := IntvMem[Addr3];        { Read the card bits }
            For _X := 0 To 7 Do
            Begin
              If (X1 + _X >= 0) And (X1 + _X < 160) Then
              Begin
                Asm
                  PUSH  EBX
                  PUSH  ESI
                  MOV   SI,WORD PTR Card        { Read the card bits }
                  MOV   CX,WORD PTR _X

                  TEST  WORD PTR SpriteFlags[2],0400h
                  JZ    @HorzFlip

                  MOV   AX,1
                  SHL   AX,CL
                  JMP   @Draw
@HorzFlip:
                  MOV   AX,80h
                  SHR   AX,CL
@Draw:
                  TEST  SI,AX                   { Is the bit on? }
                  JZ    @L1

                  MOV   DL,BYTE PTR FCol

                  MOV   CX,WORD PTR I           { Sprite number }
                  MOV   AX,0101h
                  SHL   AX,CL

                  MOVZX EBX,WORD PTR Addr
                  ADD   BX,WORD PTR Addr1
                  ADD   BX,WORD PTR Addr2

                  TEST  WORD PTR SpriteFlags,SP_VISIBLE
                  JZ    @NotVisible

                  TEST  BYTE PTR Drawing,0FFh
                  JZ    @NotVisible

                  MOV   BYTE PTR DoubleBuf[EBX],DL  { Write pixel }
@NotVisible:
                  TEST  BYTE PTR FlagCollisions,0FFh
                  JZ    @NoColl

                  PUSH  EDX
                  MOV   CX,AX
                  OR    BYTE PTR ScreenColl[EBX],CL   { Write the collision bit }
                  MOV   CL,BYTE PTR ScreenColl[EBX]   { Read the collision bit }
                  MOV   DL,BYTE PTR ScreenCollB[EBX]  { Read the collision bit }
                  MOVZX ESI,WORD PTR I                { Sprite number }
                  ADD   ESI,18h
                  OR    BYTE PTR IntvMem[ESI * 2],CL  { Write the collision bit }
                  OR    BYTE PTR IntvMem[ESI * 2 + 1],DL{ Write the collision bit }
                  POP   EDX
@NoColl:
                  TEST  WORD PTR SpriteFlags,SP_2XSIZE
                  JZ    @L1

                  MOV   CX,WORD PTR X1
                  ADD   CX,WORD PTR _X
                  ADD   CX,2
                  CMP   CX,160
                  JAE   @L1

                  TEST  WORD PTR SpriteFlags,SP_VISIBLE
                  JZ    @NotVisible1

                  TEST  BYTE PTR Drawing,0FFh
                  JZ    @NotVisible1

                  MOV   BYTE PTR DoubleBuf[EBX+1],DL   { Write pixel }
@NotVisible1:
                  TEST  BYTE PTR FlagCollisions,0FFh
                  JZ    @L1

                  PUSH  EDX
                  MOV   CX,AX
                  OR    BYTE PTR ScreenColl[EBX+1],CL  { Write the collision bit }
                  MOV   CL,BYTE PTR ScreenColl[EBX+1]  { Read the collision bit }
                  MOV   DL,BYTE PTR ScreenCollB[EBX+1]  { Read the collision bit }
                  MOVZX ESI,WORD PTR I
                  ADD   ESI,18h
                  OR    BYTE PTR IntvMem[ESI * 2],CL  { Write the collision bit }
                  OR    BYTE PTR IntvMem[ESI * 2 + 1],DL{ Write the collision bit }
                  POP   EDX
@L1:
                  INC   WORD PTR Addr2
                  TEST  WORD PTR SpriteFlags,SP_2XSIZE
                  JZ    @Not2XSize
                  INC   WORD PTR Addr2
                  INC   WORD PTR X1
@Not2XSize:
                  POP   ESI
                  POP   EBX
                End; { Asm }
              End
              Else
              Begin
                If FlagCollisions And
                   ((IntvMem[Addr3] And (1 Shl ((X1 + _X) And 7))) <> 0) Then
                 IntvMem[I + $18] := IntvMem[I + $18] Or $200;
              End;
            End; { For _X }
            Inc(Addr1,160);
          End
          Else
          Begin
            If FlagCollisions And (IntvMem[Addr3] <> 0) Then
             IntvMem[I + $18] := IntvMem[I + $18] Or $200;
          End;
          If (_Y And (SpriteMult - 1)) = (SpriteMult - 1) Then
          Begin
            If (SpriteFlags And SP_VFLIP) = 0 Then Inc(Addr3) Else Dec(Addr3);
          End;
        End; { For _Y }
      End;
    End; { For I }

    { Coalesce collision bits }

    For X := 0 To 7 Do
    Begin
      If (IntvMem[X] And $100) <> 0 Then
      Begin
        I := IntvMem[X + $18] And ((1 Shl X) Xor $FFFF);
        For Y := 0 To 7 Do
        Begin
          If (Y <> X) And ((I And (1 Shl Y)) <> 0) Then
           IntvMem[Y + $18] := IntvMem[Y + $18] Or (1 Shl X);
        End; { For Y }
        IntvMem[X + $18] := (IntvMem[X + $18] And $FF00) + (I And $FF);
      End;
    End; { For X }
  End; { DrawSprites }

  Procedure DrawKeyboardChars;
  Var
    X,Y,X1,Y1 : Integer;
    AddrIn    : Integer;
    AddrOut1  : Integer;
    AddrOut2  : Integer;
    Mask      : Byte;

  Begin
    Y := Mem6502[$40C6] + 1;
    If Y > 23 Then Dec(Y,24);
    AddrIn   := $B800 + Y * 64;
    If AddrIn < $B800 Then Inc(AddrIn,24 * 64);
    AddrOut1 := VertAddrOfs;
    For Y := 0 To 23 Do
    Begin
      For X := 0 To 39 Do
      Begin
        AddrOut2 := 0;
        For Y1 := 0 To 7 Do
        Begin
          Mask := $80;
          For X1 := 0 To 7 Do
          Begin
            If (KBDAlpha[Mem6502[AddrIn] * 8 + Y1] And Mask) <> 0
             Then ScnBuf[AddrOut1 + AddrOut2 + X1] := 7;
            Mask := Mask Shr 1;
          End; // For X1
          Inc(AddrOut2,320);
        End; // For Y1
        Inc(AddrIn);
        Inc(AddrOut1,8);
      End; // For X
      Inc(AddrIn,24);
      If AddrIn >= $B800 + 24 * 64 Then Dec(AddrIn,24 * 64);
      Inc(AddrOut1,7 * 320);
    End; // For Y

    // Draw cursor

    If (Mem6502[$40CD] <= 23) And
       (Mem6502[$40CC] >= 1)  And
       (Mem6502[$40CC] <= 40) Then
    Begin
      Y := Mem6502[$40CD] - Mem6502[$40C6] - 1;
      While Y < 0 Do Inc(Y,24);
      While Y > 23 Do Dec(Y,24);
      AddrOut1 := Y * 8 * 320 + 7 * 320 + (Mem6502[$40CC] - 1) * 8 + VertAddrOfs;
      For X := 0 To 7 Do ScnBuf[AddrOut1 + X] := 7;
    End;
  End; // DrawKeyboardChars

Begin
  VertAddrOfs := 320 * VertOffset;
  HDelay   := IntvMem[$0030] And 7;
  VDelay   := IntvMem[$0031] And 7;
  BlockOut := IntvMem[$0032] And 3;

  FillChar(ScreenColl,SizeOf(ScreenColl),#0);   { Clear the collision array }
  FillChar(ScreenCollB,SizeOf(ScreenCollB),#0); { Clear the collision array }

  ColorStack[0] := IntvMem[ColorStackLoc + 0] And $F;
  ColorStack[1] := IntvMem[ColorStackLoc + 1] And $F;
  ColorStack[2] := IntvMem[ColorStackLoc + 2] And $F;
  ColorStack[3] := IntvMem[ColorStackLoc + 3] And $F;
  BorderColor   := IntvMem[BorderColorLoc] And $F;

  BlankedSTIC := True;
  I           := 0;
  While (I < $1F) And BlankedSTIC Do
  Begin
    If IntvMem[I] <> 0 Then BlankedSTIC := False Else Inc(I);
  End; // While

  { Clear collision registers }

  IntvMem[$18] := 0;
  IntvMem[$19] := 0;
  IntvMem[$1A] := 0;
  IntvMem[$1B] := 0;
  IntvMem[$1C] := 0;
  IntvMem[$1D] := 0;
  IntvMem[$1E] := 0;
  IntvMem[$1F] := 0;

  { Draw BACKTAB OFF pixels }

  DrawBacktab(False);

  { Draw sprites that are behind the background }

  If Not BlankedSTIC Then DrawSprites(False);

  { Draw BACKTAB ON pixels }

  DrawBacktab(True);

  { Draw sprites that are on top of the background }

  If Not BlankedSTIC Then DrawSprites(True);

  { Block out first column in border color }

  If (BlockOut And 1) <> 0 Then
  Begin
    Color := BorderColor;
    Asm
      PUSH  EDI
      MOV   EDX,16 * 12
      MOV   AX,DS
      MOV   ES,AX
      MOV   EDI,DWORD PTR VertAddrOfs
      ADD   EDI,OFFSET ScnBuf
      MOV   AL,BYTE PTR Color
      MOV   AH,AL
      MOV   CX,AX
      SHL   EAX,16
      MOV   AX,CX
      CLD
@YLoop:
      STOSD
      STOSD
      STOSD
      STOSD
      ADD   EDI,320 - 16
      DEC   EDX
      JNZ   @YLoop
      POP   EDI
    End; { Asm }
  End;

  { Always block out last pixel column in border color }

  Color := BorderColor;
  Asm
    MOV   EDX,16 * 12
    MOV   ECX,DWORD PTR VertAddrOfs
    ADD   ECX,318
    MOV   AL,BYTE PTR Color
    MOV   AH,AL
    CLD
@YLoop:
    MOV   WORD PTR ScnBuf[ECX],AX
    ADD   ECX,320
    DEC   EDX
    JNZ   @YLoop
  End; { Asm }

  { Block out first row and/or vertical delay area in border color }

  Color := BorderColor;
  Asm
    PUSH  EBX
    PUSH  EDI
    MOV   AX,DS
    MOV   ES,AX
    MOV   EDI,DWORD PTR VertAddrOfs
    ADD   EDI,OFFSET ScnBuf
    MOV   AL,BYTE PTR Color
    MOV   AH,AL
    MOV   BX,AX
    SHL   EAX,16
    MOV   AX,BX
    CLD
    SUB   ECX,ECX
    TEST  WORD PTR BlockOut,2
    JZ    @L2
    MOV   CX,80 * 16
@L2:
    PUSH  EAX
    MOV   BX,WORD PTR VDelay
    MOV   AX,160
    MUL   BX
    CMP   CX,AX
    JAE   @L3
    MOV   CX,AX
@L3:
    POP   EAX
    TEST  CX,CX
    JZ    @L1
    REP   STOSD
@L1:
    POP   EDI
    POP   EBX
  End; { Asm }

  { Always block out top of screen in border color }

  Color := BorderColor;
  Asm
    PUSH  EDI
    MOV   AX,DS
    MOV   ES,AX
    MOV   EDI,OFFSET ScnBuf
    MOV   AL,BYTE PTR Color
    MOV   AH,AL
    MOV   CX,AX
    SHL   EAX,16
    MOV   AX,CX
    CLD
    MOV   ECX,80 * 4
    REP   STOSD
    POP   EDI
  End; { Asm }

  { Always block out bottom of screen in border color }

  Color := BorderColor;
  Asm
    PUSH  EDI
    MOV   AX,DS
    MOV   ES,AX
    MOV   EDI,320 * 96 * 2
    ADD   EDI,OFFSET ScnBuf
    ADD   EDI,DWORD PTR VertAddrOfs
    MOV   AL,BYTE PTR Color
    MOV   AH,AL
    MOV   CX,AX
    SHL   EAX,16
    MOV   AX,CX
    CLD
    MOV   CX,80 * 4
    REP   STOSD
    POP   EDI
  End; { Asm }

  { Copy graphics page }

  I := VDelay * 320 + HDelay;
  If (BlockOut And 1) <> 0 Then
  Begin
    Asm
      PUSH   EBX
      PUSH   ESI
      PUSH   EDI
      CLD
      MOV    EDX,192
      SUB    EBX,EBX
      TEST   WORD PTR BlockOut,2
      JZ     @L3
      MOV    EDX,192-16
      MOV    EBX,160*16
@L3:
      MOV    AX,DS
      MOV    ES,AX
      MOV    EDI,DWORD PTR VertAddrOfs
      ADD    EDI,OFFSET ScnBuf
      ADD    EDI,EBX
      ADD    EDI,EBX
      ADD    EDI,16
      MOV    ESI,OFFSET DoubleBuf
      ADD    ESI,EBX
      ADD    ESI,8
      SUB    ESI,DWORD PTR I
@L2:
      MOV    ECX,159-8
@L1:
      LODSB
      MOV    AH,AL
      STOSW
      LOOP   @L1
      ADD    EDI,2+16
      ADD    ESI,1+8
      DEC    EDX
      JNZ    @L2
      POP    EDI
      POP    ESI
      POP    EBX
    End; { Asm }
  End
  Else
  Begin
    Asm
      PUSH   EBX
      PUSH   ESI
      PUSH   EDI
      CLD
      MOV    EDX,192
      SUB    EBX,EBX
      TEST   WORD PTR BlockOut,2
      JZ     @L3
      MOV    EDX,192-16
      MOV    EBX,160*16
@L3:
      MOV    AX,DS
      MOV    ES,AX
      MOV    EDI,DWORD PTR VertAddrOfs
      ADD    EDI,OFFSET ScnBuf
      ADD    EDI,EBX
      ADD    EDI,EBX
      MOV    ESI,OFFSET DoubleBuf
      ADD    ESI,EBX
      SUB    ESI,DWORD PTR I
@L2:
      MOV    ECX,159
@L1:
      LODSB
      MOV    AH,AL
      STOSW
      LOOP   @L1
      ADD    EDI,2
      INC    ESI
      DEC    EDX
      JNZ    @L2
      POP    EDI
      POP    ESI
      POP    EBX
    End; { Asm }
  End;

  // Overlay keyboard component screen information

  If Cart = ct6502 Then DrawKeyboardChars;
End; // PaintSurface

Procedure QueueSoundSample;
Begin
  Inc(SoundQueue_Num,2);
  IVoice_Tk(Voice,2);
  ProcessSound(@(SoundQueue[SoundQueue_In]),2);
  SoundQueue_In := (SoundQueue_In + 2) And 1023;
End; { QueueSoundSample }

Procedure FillBuffer;
Var
  Queued   : Integer;
  Position : Integer;
  WriteCrs : Integer;
  Amount   : Integer;
  I,J,K    : Integer;

Begin
  If Config.DoSound Then
  Begin

    // Get the current buffer position

    frmMain.Secondary.IBuffer.GetCurrentPosition(DWORD(Position),DWORD(WriteCrs));
    Amount := WriteCrs - Position;
    If Amount < 0 Then Inc(Amount,SndBufSize);
    Amount := SndBufSize - Amount;

    If OldPos < 0 Then I := 2048 Else
    Begin
      If (OldPos < Position) And (OldPos < LastWrite) Then J := OldPos + SndBufSize Else J := OldPos;
      If WriteCrs < LastWrite Then K := WriteCrs + SndBufSize Else K := WriteCrs;
      I := WriteCrs - LastWrite;
      If I < 0 Then Inc(I,SndBufSize);

      I := K + I + 384 - J;

      If I < -128 Then I := 2;   // Don't let it get too far ahead
      If I < 0 Then I := 128;    // Introduce some slack
    End;

    If Amount >= I Then
    Begin
      Amount := I;

      // Retrieve from the sound queue first

      Queued := 0;
      While (SoundQueue_Num > 0) And (Queued < Amount) Do
      Begin
        WorkSoundBuf[Queued] := SoundQueue[SoundQueue_Out];
        Inc(Queued);
        SoundQueue_Out := (SoundQueue_Out + 1) And 1023;
        Dec(SoundQueue_Num);
      End; // While

      // Generate any remaining needed sound data

      If Queued < Amount Then
      Begin
        IVoice_Tk(Voice,Amount - Queued);
        ProcessSound(@WorkSoundBuf[Queued],Amount - Queued);
      End;

      // Lock the DirectSound buffer

      frmMain.Secondary.Lock(OldPos,Amount,SoundBufPtr,SoundBufSize,SoundBufPtr2,SoundBufSize2,0);

      // Move the data into the buffer

      If Amount > SoundBufSize
       Then I := SoundBufSize
       Else I := Amount;
      Move(WorkSoundBuf[0],SoundBufPtr^,I);
      If Amount > SoundBufSize Then
      Begin
        If Amount - I > SoundBufSize2
         Then J := SoundBufSize2
         Else J := Amount - I;
        Move(WorkSoundBuf[I],SoundBufPtr2^,J);
      End;

      // Unlock the DirectSound buffer

      frmMain.Secondary.Unlock(SoundBufPtr,SoundBufSize,SoundBufPtr2,SoundBufSize2);

      // Save the new position

      OldPos    := (OldPos + Amount) Mod SndBufSize;
      LastWrite := WriteCrs;
    End;
  End;
End; { FillBuffer }

(*
Procedure FillBuffer;
Var
  Queued   : Word;
  Position : Integer;
  WriteCrs : Integer;
  Amount   : Integer;

Begin
  If Config.DoSound Then
  Begin

    // Get the current buffer position

    frmMain.Secondary.IBuffer.GetCurrentPosition(DWORD(Position),DWORD(WriteCrs));
    Amount := Position - WriteCrs;
    If Amount < 0 Then Inc(Amount,SndBufSize);

    // We don't want to update the buffers too often, for speed purposes (the DOS
    // version also updates every 128 bytes)

    If Amount >= 128 Then
    Begin

      // Retrieve from the sound queue first

      Queued := 0;
      While (SoundQueue_Num > 0) And (Queued < Amount) Do
      Begin
        WorkSoundBuf[Queued] := SoundQueue[SoundQueue_Out];
        Inc(Queued);
        SoundQueue_Out := (SoundQueue_Out + 1) And 1023;
        Dec(SoundQueue_Num);
      End; // While

      // Generate any remaining needed sound data

      If Queued < Amount Then ProcessSound(@WorkSoundBuf[Queued],Amount - Queued);

      // Lock the DirectSound buffer

      frmMain.Secondary.Lock(OldPos,Amount,SoundBufPtr,SoundBufSize,SoundBufPtr2,SoundBufSize2,0);

      // Move the data into the buffer

      Asm
        CLD
        PUSH  ESI
        PUSH  EDI
        MOV   ESI,OFFSET WorkSoundBuf

        MOV   ECX,DWORD PTR SoundBufSize
        MOV   EDI,DWORD PTR SoundBufPtr
        MOV   EAX,ECX
        TEST  ECX,3
        JZ    @OnPara1
        AND   ECX,3
        REP   MOVSB
        MOV   ECX,EAX
@OnPara1:
        SHR   ECX,2
        REP   MOVSD

        MOV   ECX,DWORD PTR SoundBufSize2
        TEST  ECX,ECX
        JZ    @Done
        MOV   EDI,DWORD PTR SoundBufPtr2
        MOV   EAX,ECX
        TEST  ECX,3
        JZ    @OnPara2
        AND   ECX,3
        REP   MOVSB
        MOV   ECX,EAX
@OnPara2:
        SHR   ECX,2
        REP   MOVSD
@Done:        
        POP   EDI
        POP   ESI
      End; // Asm

      // Unlock the DirectSound buffer

      frmMain.Secondary.Unlock(SoundBufPtr,SoundBufSize,SoundBufPtr2,SoundBufSize2);

      // Save the new position

      OldPos := (OldPos + Amount) Mod SndBufSize;
    End;
  End;
End; { FillBuffer }
*)
(*
Procedure FillBuffer;
Type BPtr = ^Byte;
Var
  Queued   : Word;
  P,Q      : Integer;
  Position : Integer;
  WriteCrs : Integer;
  Amount   : Integer;

Begin
  If Config.DoSound Then
  Begin

    // Get the current buffer position

    frmMain.Secondary.IBuffer.GetCurrentPosition(DWORD(Position),DWORD(WriteCrs));
    Amount := Position - WriteCrs;
    If Amount < 0 Then Inc(Amount,SndBufSize);

    // We don't want to update the buffers too often, for speed purposes (the DOS
    // version also updates every 128 bytes)

    If Amount >= 128 Then
    Begin

      // Retrieve from the sound queue first

      Queued := 0;
//      P      := 0;
      While (SoundQueue_Num > 0) And (Queued < Amount) Do
      Begin
        WorkSoundBuf[Queued] := SoundQueue[SoundQueue_Out];
        Inc(Queued);
        SoundQueue_Out := (SoundQueue_Out + 1) And 1023;
        Dec(SoundQueue_Num);
      End; // While

      // Generate any remaining needed sound data

      If Queued < Amount Then
      Begin
        IVoice_Tk(Voice,Amount - Queued);
        ProcessSound(@WorkSoundBuf[Queued],Amount - Queued);
      End;

      // Lock the DirectSound buffer

      frmMain.Secondary.Lock(OldPos,Amount,SoundBufPtr,SoundBufSize,SoundBufPtr2,SoundBufSize2,0);

      // Move the data into the buffer

      If Amount > SoundBufSize
       Then P := SoundBufSize
       Else P := Amount;
      Move(WorkSoundBuf[0],SoundBufPtr^,P);
      If P < Amount Then
      Begin
        If (Amount - P) > SoundBufSize2
         Then Q := SoundBufSize2
         Else Q := Amount - P;
        Move(WorkSoundBuf[P],SoundBufPtr2^,Q);
      End;
{
      Asm
        CLD
        PUSH  ESI
        PUSH  EDI
        PUSH  EBX
        MOV   ESI,OFFSET WorkSoundBuf
        MOV   EBX,DWORD PTR Amount

        MOV   ECX,DWORD PTR SoundBufSize
        CMP   ECX,EBX
        JBE   @Smaller1
        MOV   ECX,EBX
@Smaller1:
        SUB   EBX,ECX
        MOV   EDI,DWORD PTR SoundBufPtr
        MOV   EAX,ECX
        TEST  ECX,3
        JZ    @OnPara1
        AND   ECX,3
        REP   MOVSB
        MOV   ECX,EAX
@OnPara1:
        SHR   ECX,2
        REP   MOVSD

        TEST  EBX,EBX
        JZ    @Done

        MOV   ECX,DWORD PTR SoundBufSize2
        TEST  ECX,ECX
        JZ    @Done
        CMP   ECX,EBX
        JBE   @Smaller2
        MOV   ECX,EBX
@Smaller2:
        MOV   EDI,DWORD PTR SoundBufPtr2
        MOV   EAX,ECX
        TEST  ECX,3
        JZ    @OnPara2
        AND   ECX,3
        REP   MOVSB
        MOV   ECX,EAX
@OnPara2:
        SHR   ECX,2
        REP   MOVSD
@Done:
        POP   EBX
        POP   EDI
        POP   ESI
      End; // Asm
}
//      Move(WorkSoundBuf[0],SoundBufPtr^,SoundBufSize);
//      If SoundBufSize2 > 0 Then Move(WorkSoundBuf[SoundBufSize],SoundBufPtr2^,SoundBufSize2);

{
      // Retrieve from the sound queue first

      Queued := 0;
      P      := 0;
      While (SoundQueue_Num > 0) And (Queued < Amount) Do
      Begin
        If P < SoundBufSize
         Then BPtr(LongInt(SoundBufPtr)  + P)^                := SoundQueue[SoundQueue_Out]
         Else BPtr(LongInt(SoundBufPtr2) + P - SoundBufSize)^ := SoundQueue[SoundQueue_Out];
        Inc(P);
        Inc(Queued);
        SoundQueue_Out := (SoundQueue_Out + 1) And 1023;
        Dec(SoundQueue_Num);
      End; // While

      // Generate any remaining needed sound data

      If Queued < Amount Then
      Begin
        If P < SoundBufSize Then
        Begin
          If SoundBufSize2 > 0 Then
          Begin
            IVoice_Tk(Voice,SoundBufSize - Queued);
            ProcessSound(Pointer(LongInt(SoundBufPtr) + P),SoundBufSize - Queued);
          End
          Else
          Begin
            IVoice_Tk(Voice,Amount - Queued);
            ProcessSound(Pointer(LongInt(SoundBufPtr) + P),Amount - Queued);
          End;
        End;
        If SoundBufSize2 > 0 Then
        Begin
          If P >= SoundBufSize Then
          Begin
            IVoice_Tk(Voice,SoundBufSize2 - (P - SoundBufSize));
            ProcessSound(Pointer(LongInt(SoundBufPtr2) + P - SoundBufSize),SoundBufSize2 - (P - SoundBufSize));
          End
          Else
          Begin
            IVoice_Tk(Voice,SoundBufSize2);
            ProcessSound(SoundBufPtr2,SoundBufSize2);
          End;
        End;
      End;
}
      // Unlock the DirectSound buffer

      frmMain.Secondary.Unlock(SoundBufPtr,SoundBufSize,SoundBufPtr2,SoundBufSize2);

      // Save the new position

      OldPos := (OldPos + Amount) Mod SndBufSize; 
//      OldPos := Position;
    End;
  End;
End; { FillBuffer }
*)
Procedure ResetClock;
Begin
  Time0            := GetTickCount;
  frmMain.BaseTime := Trunc(Time0);
  frmMain.Frames   := 0;
  frmMain.FPS      := 0;
End; { ResetClock }

Function CreateFile(Var F: File; St: String): Boolean;
Var
  Error    : Boolean;
  Continue : Boolean;

Begin
  Error   := False;
  If St = '' Then Error := True;

  Continue := True;
  If (Not Error) And FileExists(St) Then
  Begin
    Continue := (Application.MessageBox('File already exists. Overwrite?','Warning',
     MB_YESNO + MB_ICONQUESTION) = IDYES);
  End;
  If Error Then Continue := False;

  If Continue Then
  Begin
    Assign(F,St);
    {$I-}
    ReWrite(F,1);
    {$I+}
    If IOResult <> 0 Then Error := True;
    If Error Then
    Begin
      If St <> '' Then
      Begin
        ShowMessage('Error creating file.');
      End;
      CreateFile := False;
    End
    Else CreateFile := True;
  End
  Else CreateFile := False;
End; { CreateFile }

Function CanOpenFile(Width,Height: Word; St: String): Boolean;
Var Error: Boolean;
Begin
  Error   := False;
  If St = '' Then Error := True;
  If Not FileExists(St) Then Error := True;
  If Error Then
  Begin
    If St <> '' Then ShowMessage('Error opeing file.');
    CanOpenFile := False;
  End
  Else CanOpenFile := True;
End; { CanOpenFile }

Var
  Info  : TIntvControls;
  Info0 : TIntvControls;

Procedure SlowDown(Milliseconds: Word);
Var
  T : LongInt;
  R : Double;

  Procedure Net;
  Var Received: Integer;
  Begin
    If UsingKaillera Then
    Begin
      KValues[1].P       := Info0.P[1];
      KValues[1].Console := Info0.Console;
      Received := KailleraModifyPlayValues(@KValues,SizeOf(TKailleraControls));
      If Received > 0 Then
      Begin
        Info.Console := KValues[1].Console Or KValues[2].Console Or KValues[3].Console Or KValues[4].Console;
        Info.P[1]    := KValues[1].P;
        Info.P[2]    := KValues[2].P;
        Info.P[3]    := KValues[3].P;
        Info.P[4]    := KValues[4].P;
      End;
    End;
  End; // Net

Begin
  Net;

  R := Time0 + (Milliseconds / Config.FrameRate);
  Repeat
    T := GetTickCount;
    If T < (R - 1000) Then T := Trunc(R);
  Until (T >= R);
  Time0 := R;
End; { SlowDown }

Procedure NewPalette;
Var I: Integer;
Begin
  FillChar(BI.bmiColors,SizeOf(BI.bmiColors),#0);
  For I := 1 To 255 Do
  Begin
    BI.bmiColors[I - 1].rgbBlue     := Pal[I].B;
    BI.bmiColors[I - 1].rgbGreen    := Pal[I].G;
    BI.bmiColors[I - 1].rgbRed      := Pal[I].R;
    BI.bmiColors[I - 1].rgbReserved := 0;
  End; { For I }
  BI.B.bmiHeader.biSize          := SizeOf(TBitmapInfoHeader);
  BI.B.bmiHeader.biWidth         := ScnBufWidth;
  BI.B.bmiHeader.biHeight        := ScnBufHeight;
  BI.B.bmiHeader.biPlanes        := 1;
  BI.B.bmiHeader.biBitCount      := 8;
  BI.B.bmiHeader.biCompression   := BI_RGB;
  BI.B.bmiHeader.biSizeImage     := 0;
  BI.B.bmiHeader.biXPelsPerMeter := 2048;
  BI.B.bmiHeader.biYPelsPerMeter := 2048;
  BI.B.bmiHeader.biClrUsed       := 0;
  BI.B.bmiHeader.biClrImportant  := 0;

  BI1 := BI;
//  BI1.B.bmiHeader.biWidth         := ScnBufWidth;

  PI.T.palVersion    := $300;
  PI.T.palNumEntries := 256;
  FillChar(PI.P,SizeOf(PI.P),#0);
  For I := 0 To 255 Do
  Begin
    PI.P[I].peRed   := Pal[I].R;
    PI.P[I].peGreen := Pal[I].G;
    PI.P[I].peBlue  := Pal[I].B;
    PI.P[I].peFlags := 0;
  End; { For I }

  If frmMain.UseDirectDraw Then
  Begin
    For I := 0 To 255 Do
    Begin
      frmMain.DXDraw1.ColorTable[I].rgbRed         := Pal[I].R;
      frmMain.DXDraw1.ColorTable[I].rgbGreen       := Pal[I].G;
      frmMain.DXDraw1.ColorTable[I].rgbBlue        := Pal[I].B;
      frmMain.DXDraw1.ColorTable[I].rgbReserved    := 0;
      frmMain.DXDraw1.DefColorTable[I].rgbRed      := Pal[I].R;
      frmMain.DXDraw1.DefColorTable[I].rgbGreen    := Pal[I].G;
      frmMain.DXDraw1.DefColorTable[I].rgbBlue     := Pal[I].B;
      frmMain.DXDraw1.DefColorTable[I].rgbReserved := 0;

      frmKaillera.DXDraw1.ColorTable[I].rgbRed         := Pal[I].R;
      frmKaillera.DXDraw1.ColorTable[I].rgbGreen       := Pal[I].G;
      frmKaillera.DXDraw1.ColorTable[I].rgbBlue        := Pal[I].B;
      frmKaillera.DXDraw1.ColorTable[I].rgbReserved    := 0;
      frmKaillera.DXDraw1.DefColorTable[I].rgbRed      := Pal[I].R;
      frmKaillera.DXDraw1.DefColorTable[I].rgbGreen    := Pal[I].G;
      frmKaillera.DXDraw1.DefColorTable[I].rgbBlue     := Pal[I].B;
      frmKaillera.DXDraw1.DefColorTable[I].rgbReserved := 0;
    End; { For I }
    frmMain.DXDraw1.UpdatePalette;
    frmKaillera.DXDraw1.UpdatePalette;
  End;
End; // NewPalette;

Procedure InitEmulator;
Begin
  FillChar(Info,SizeOf(Info),0);
  IVoice_Init(Voice,Playback_Freq);
  If BreakExpColl <> Nil Then
  Begin
    BreakExpColl.Free;
    BreakExpColl := Nil;
  End;
  If BreakExpColl6502 <> Nil Then
  Begin
    BreakExpColl6502.Free;
    BreakExpColl6502 := Nil;
  End;

  { There is no initial break expression.  Initialize the pointer. }

  BreakExpColl     := Nil;
  BreakExpColl6502 := Nil;

  { Initialize the PC }

  Regs[7] := Boot_Loc;

  LeftController     := $FF;
  RightController    := $FF;
  ECSLeftController  := $FF;
  ECSRightController := $FF;

  If Debugger Then
  Begin
    If Debug6502
     Then DebugAddr := Game6502.PC
     Else DebugAddr := Regs[7];
  End;
  Trace := 0;

  // Initialize the 6502 core

  Game6502.PC       := $C000;
  Game6502.P        := 0;
  Game6502.A        := 0;
  Game6502.X        := 0;
  Game6502.Y        := 0;
  Game6502.S        := 0;
  Game6502.OldByte  := 0;
  Game6502.AddCycle := 0;
  Game6502.Cycles   := 0;
  Game6502.ResetOfs := 0;

  // Initialize BASIC program import info

  Importing   := False;
  ImportRead  := False;
  ECSKey      := False;         { No key currently being typed in   }
  ImportKey   := ECSInvalidKey; { No key currently being typed in   }
  ImportShift := False;         { ECS Shift key not needed          }
  ECSDelay    := 0;             { Reset auto-repeat delay           }
  ECSPCKey    := 0;             { No PC keyboard key being pressed  }
End; { InitEmulator }

Procedure EventHandler;
Var Cap: TCapability;
Begin

//  IntvMem[$8301] := 0;
//  IntvMem[$8306] := 0;
//  IntvMem[$8340] := IntvMem[$8340] And $FB;

  frmMain.Display;
  Cap.Cont[1] := imAllCaps;
  Cap.Cont[2] := imAllCaps;
  Cap.Cont[3] := imAllCaps;
  Cap.Cont[4] := imAllCaps;

  { Initialize the info structure }

  Info0.P[1]    := $FF;
  Info0.P[2]    := $FF;
  Info0.P[3]    := $FF;
  Info0.P[4]    := $FF;
  Info0.Console := 0;

  { Get controller and console switch information }

  InterfaceHandler.GetControllerInfo(Cap,Info0);

  { Make sure that basic console switches work }

  If UsingKaillera Then
  Begin
    If frmMain.DXInput1.Keyboard.DIKeys[DIK_SLASH] Then
    Begin
      frmKaillera.edtChat.Enabled := True;
      frmKaillera.edtChat.Text    := '';
      frmKaillera.FocusControl(frmKaillera.edtChat);
    End;
  End;

  frmMain.DXInput1.Update;
  If frmMain.DXInput1.Keyboard.DIKeys[DIK_ESCAPE]  Then
  Begin
    Info0.Console := Info0.Console Or cfPower;
    Repeat
      Application.ProcessMessages;
      frmMain.DXInput1.Update;
    Until Not frmMain.DXInput1.Keyboard.DIKeys[DIK_ESCAPE];
  End;
  If frmMain.DXInput1.Keyboard.DIKeys[DIK_F7] And
     (Cart In [ctECS,ct6502]) And Not Importing Then
  Begin
    Repeat
      Application.ProcessMessages;
      frmMain.DXInput1.Update;
    Until Not frmMain.DXInput1.Keyboard.DIKeys[DIK_F7];
    frmMain.dlgOpen.Filter := 'Basic programs (*.bas)|*.BAS';
    If frmMain.dlgOpen.Execute Then
    Begin
      IOResult;  // Clear the internal IO error flag
      AssignFile(ImportFile,frmMain.dlgOpen.FileName);
      Reset(ImportFile,1);
      ImportRead  := True;
      Importing   := True;
      ImportCount := 0;
      ImportCont  := $FF;
      ImportShift := False;
    End;
    frmMain.DXInput1.Update;
    ResetClock;
  End;

  If frmMain.DXInput1.Keyboard.DIKeys[DIK_F10] Then Info0.Console := Info0.Console Or cfReset;
  If frmMain.DXInput1.Keyboard.DIKeys[DIK_F12] Then
  Begin
    Repeat
      Application.ProcessMessages;
      frmMain.DXInput1.Update;
    Until Not frmMain.DXInput1.Keyboard.DIKeys[DIK_F12];
    DoSlowDown := Not DoSlowDown;
    ResetClock;
  End;

  { Check to see if we are streaming to an .AVI file }

  CheckAVIStream;

  If UsingKaillera Then
  Begin
{
    Info.Console := Info.Console And (Not (cfDiff1 Or cfDiff2));
    Info.Console := Info.Console Or (KValues[1].Console And cfDiff1) Or (KValues[2].Console And cfDiff2);
    Info.Input4  := ((KValues[1].Console And $80) <> 0);
    Info.Input5  := ((KValues[2].Console And $80) <> 0);
    Case Game.Controller Of
      crPaddle:
      Begin
        Info.Pad[1]  := KValues[1].PadDriv;
        Info.Pad[2]  := KValues[2].PadDriv;
        Info.Pad[3]  := KValues[3].PadDriv;
        Info.Pad[4]  := KValues[4].PadDriv;
        Info.PortA := (Info.PortA And $33) Or
                      (KValues[1].PortA And $80) Or
                      ((KValues[2].PortA And $80) Shr 1) Or
                      ((KValues[3].PortA And $80) Shr 4) Or
                      ((KValues[4].PortA And $80) Shr 5);
      End;
      crDriving:
      Begin
        Info.Driv[1] := KValues[1].PadDriv;
        Info.Driv[2] := KValues[2].PadDriv;
        Info.PortA := (KValues[1].PortA And $F0) Or (KValues[2].PortA And $0F);
      End;
    Else
      Info.PortA := (KValues[1].PortA And $F0) Or (KValues[2].PortA And $0F);
    End; // Case
}
  End
  Else Move(Info0,Info,SizeOf(Info));

  // Handle console switch information

  If (Info.Console And cfReset) <> 0 Then InitEmulator;

  { Copy info }

  { Check other special keys }

  { Exiting }

  If (Info.Console And cfPower) <> 0 Then
  Begin
    If Emulating > 0 Then NewList := True;
    Emulating  := 0;
    If UsingKaillera Then KailleraEndGame;
  End;

  LeftController     := Info.P[1];
  RightController    := Info.P[2];
  ECSLeftController  := Info.P[3];
  ECSRightController := Info.P[4];
End; { EventHandler }

Procedure GetECSControllers;
Var
  I,J     : Integer;
  Found   : Boolean;
  Ch      : Char;
  Shift   : Boolean;
  ECSCode : Integer;

Begin
  ECSLeftController  := $FF;
  ECSRightController := $FF;

  { Look for a key that has been pressed }

  Found := False;
  I     := 0;
  While (I <= 255) And Not Found Do
  Begin
    If (frmMain.DXInput1.Keyboard.DIKeys[I]) And (ECSKeyList[I] = 0) Then Found := True Else Inc(I);
  End; { While }
  If Not Found Then
  Begin
    If frmMain.DXInput1.Keyboard.DIKeys[ECSPCKey] Then
    Begin
      I     := ECSPCKey;
      Found := True;
    End;
    If Not Found Then
    Begin
      I := 0;
      While (I <= 255) And Not Found Do
      Begin
        If frmMain.DXInput1.Keyboard.DIKeys[I] Then Found := True Else Inc(I);
      End; { While }
    End;
  End;
  For J := 0 To 255 Do
  Begin
    If frmMain.DXInput1.Keyboard.DIKeys[J]
     Then ECSKeyList[J] := 1
     Else ECSKeyList[J] := 0;
  End; // For I
//  Move(Key,ECSKeyList,SizeOf(ECSKeyList));

  { If a key has been pressed, find out the ASCII code }

  If Found Then
  Begin
    ECSPCKey := I;
    If frmMain.DXInput1.Keyboard.DIKeys[DIK_LSHIFT] Or frmMain.DXInput1.Keyboard.DIKeys[DIK_RSHIFT]
     Then Ch := ASCIITranslationShift[ECSPCKey]
     Else Ch := ASCIITranslation[ECSPCKey];

    { If the character is a letter, swap upper- and lower-case so that }
    { upper case is the default                                        }

    If Ch In ['A'..'Z'] Then Ch := Char(Ord(Ch) + 32)
    Else If Ch In ['a'..'z'] Then Ch := UpCase(Ch);

    { Lots of characters are mapped to <space> by default.  Only allow this }
    { if the space bar was depressed.                                       }

    If (Ch = ' ') And (ECSPCKey <> kSPACE) Then Ch := #0;

    { Special cases for control characters }

    Case ECSPCKey Of
     kTAB: Begin
             ECSCode := 32;
             Shift   := False;
           End;
  kEENTER: Begin                      { Only use main enter key }
             ECSCode := ECSInvalidKey;
             Shift   := False;
           End;
   kENTER: Begin
             ECSCode := 48;
             Shift   := False;
           End;
 kBACKSPC,
      kLF: Begin
             ECSCode := 0;
             Shift   := False;
           End;
      kRT: Begin
             ECSCode := 45;
             Shift   := False;
           End;
      kUP: Begin
             ECSCode := 21;
             Shift   := False;
           End;
      kDN: Begin
             ECSCode := 13;
             Shift   := False;
           End;
   kLCTRL,
   kRCTRL: Begin                      { Ctl by itself doesn't do anything }
             ECSCode := 53;
             Shift   := False;
           End;
  kLSHIFT,
  kRSHIFT: Begin                      { Shift by itself doesn't do anything }
             ECSCode := ECSInvalidKey;
             Shift   := False;
           End;
    Else

      { Find out the ECS key code and whehter the ECS shift key is needed }

      If ASCIIToECSUnshift[Ord(Ch)] <> ECSInvalidKey Then
      Begin
        Shift   := False;
        ECSCode := ASCIIToECSUnshift[Ord(Ch)];
      End
      Else
      If ASCIIToECSShift[Ord(Ch)] <> ECSInvalidKey Then
      Begin
        Shift   := True;
        ECSCode := ASCIIToECSShift[Ord(Ch)];
      End
      Else
      Begin
        Shift   := False;
        ECSCode := ECSInvalidKey;
      End;
    End; { Case }

    { Get the key entered }

    If (ECSCode <> 255) And Not Importing Then
    Begin

      { If the key is being held down, wait for the auto-repeat delay }
      { to elapse                                                     }

      If Not ECSKey Then
      Begin
        If (ImportKey = ECSCode)   And
           (ImportShift = Shift)   And
           (ECSDelay < RepeatTime) Then Inc(ECSDelay)
        Else
        Begin
          Importing   := True;       { Inputting a key                       }
          ImportRead  := False;      { Not reading from a file               }
          ImportKey   := ECSCode;    { ECS key code                          }
          ImportCount := 0;          { Just starting the key input           }
          ImportShift := Shift;      { ECS Shift key needed/not needed       }
          ECSKey      := True;       { Key is being typed in (not from file) }
          ECSDelay    := 0;          { Reset auto-repeat delay               }
        End;
      End;
    End;
  End
  Else
  Begin

    { If no key is pressed and we're not importing from a file, blank the }
    { key variables so we know if a key was released.  This is important  }
    { for implementing auto-repeat.                                       }

    If ECSKey Then Importing := False;     { No keys being input          }
    ECSKey   := False;         { No key is being typed in     }
    ECSPCKey := 0;             { No PC keyboard key being pressed  }
    ECSDelay := 0;             { Reset auto-repeat delay      }
    If Not Importing Then
    Begin
      ImportRead  := False;         { Not reading from a file      }
      ImportKey   := ECSInvalidKey; { No key being pressed         }
      ImportCount := 0;             { Reset the input time counter }
      ImportShift := False;         { ECS Shift key not being used }
    End;
  End;

  IntvMem[$00FE] := IntvMem[$00FE] Or $FF00;
  IntvMem[$00FF] := IntvMem[$00FF] Or $FF00;
End; { GetECSControllers }

Function CanWrite(Addr: Word): Boolean;
Var B: Boolean;
Begin
  B := True;

  { Prevent EXEC ROM from being overwritten }

  If (Addr >= $1000) And (Addr <= $1FFF) Then B := False;
  CanWrite := B;
End; { CanWrite }

Procedure Handle_IO_Write(Addr,Value: Word);
Var
  I         : Word;
  St        : String;

Begin
{
  If LogOutput Then
  Begin
    WriteLn(LogFile);
    WriteLn(LogFile,'CP1600: W[',HexWord(Addr),'] = ',HexWord(Value));
  End;
}
  ReadMem  := False;
  WriteMem := True;
  RWMemLoc := Addr;
  
  If Addr < 8 Then
  Begin
    Begin
      SpriteX[Addr] := Integer(IntvMem[Addr]     And $FF) - 8;
      SpriteY[Addr] := Integer(IntvMem[Addr + 8] And $7F) * 2 - 16;
    End;
  End
  Else
   If Addr < 16 Then
   Begin
     I := Addr - 8;
     Begin
       SpriteY[I] := Integer(IntvMem[Addr] And $7F) * 2 - 16;
     End;
   End
   Else
   Begin
     If Addr < $200 Then
     Begin
       If Addr = $0021 Then ScreenMode := smForeBack;
       If (Cart = ctECS) And
          ((Addr = $00F8) Or
           (Addr = $00FE) Or
           (Addr = $00FF)) Then GetECSControllers;
       If (Addr >= $100) And (Addr <= $1FF) Then IntvMem[Addr] := IntvMem[Addr] And $FF;
       If (Addr >= $1F0) And (Addr <= $1FD) And Config.DoSound Then WriteToPSG(Addr - $1F0,IntvMem[Addr],0);
       If Cart = ctECS Then
       Begin
         If (Addr >= $F0) And (Addr <= $FF) Then IntvMem[Addr] := IntvMem[Addr] And $FF;
         If (Addr >= $F0) And (Addr <= $FD) And Config.DoSound Then WriteToPSG(Addr - $F0,IntvMem[Addr],1);
       End
       Else
       Begin
         If (Addr >= $F0) And (Addr <= $FF) Then IntvMem[Addr] := $FFFF;
       End;

       { Intellivoice }

       If (Addr = $80) Or (Addr = $81) Then IVoice_Wr(Voice,Addr - $80,Value);

       { ECS BIOS will only write to  [E1h] (cassette) if ([E0h] and 2) <> 0 }
       { ECS BIOS will only read from [E1h] (cassette) if ([E0h] and 1) <> 0 }

       If ((Addr >= $33) And (Addr <= $DF)) Or
          ((Addr >= $E3) And (Addr <= $EF)) Then
        IntvMem[Addr] := $FFFF;

       If Cart <> ctECS Then
       Begin
         If (Addr >= $E0) And (Addr <= $E2) Then IntvMem[Addr] := $FFFF;
       End
       Else
       Begin

         { ECS cassette ports are 8-bit ports }

         If (Addr >= $E0) And (Addr <= $EF) Then IntvMem[Addr] := IntvMem[Addr] And $FF;

         { 0080h .... ECS cassette status (R/W) }
         { 0081h .... ECS cassette data   (R/W) }
         { 0082h .... ECS cassette control (W)  }

         { ECS cassette status port }
         {                          }
         { bit assignments          }
         { MSB 7 ..... unknown      }
         {     6 ..... unknown      }
         {     5 ..... unknown      }
         {     4 ..... unknown      }
         {     3 ..... unknown      }
         {     2 ..... unknown      }
         {     1 ..... write enable }
         { LSB 0 ..... read enable  }

         If Addr = $00E0 Then IntvMem[Addr] := IntvMem[Addr] Or 3;

         { ECS cassette data port }

         If Addr = $00E1 Then
         Begin
           ECSSave := True;
           If Not ECSPrint Then
           Begin
             If ECSSaveBufI < 16 Then
             Begin
               ECSSaveBuf[ECSSaveBufI] := Value;
               Inc(ECSSaveBufI);

               { If 16 words have been written, extract the title and create }
               { the output file                                             }

               If ECSSaveBufI = 16 Then
               Begin
                 St := Chr(ECSSaveBuf[12]) + Chr(ECSSaveBuf[13]) +
                       Chr(ECSSaveBuf[14]) + Chr(ECSSaveBuf[15]);
                 Assign(ECSCassFile,St);
                 ReWrite(ECSCassFile,1);
                 BlockWrite(ECSCassFile,ECSSaveBuf,SizeOf(ECSSaveBuf));
               End;
             End
             Else BlockWrite(ECSCassFile,Value,1);
           End
           Else
           Begin
             If ECSSaveBufI = 0 Then
             Begin
//               St := Trim(EnterStringPopup('Enter print file name:',''));
               If St = '' Then St := ECSPrintName;
               Assign(ECSCassFile,St);
               ReWrite(ECSCassFile,1);
               ECSSaveBufI := 1;
             End;
             BlockWrite(ECSCassFile,Value,1);
           End;
         End;

         { ECS cassette control port }

         If Addr = $00E2 Then
         Begin

           { If an output file is already open, close it }

           If ECSSave Then
           Begin
             Close(ECSCassFile);
             ECSSave     := False;
             ECSPrint    := False;
             ECSSaveBufI := 0;
             ResetClock;
           End;

           { If an input file is already open, close it }

           If ECSLoad Then
           Begin
             If ECSLoadable Then Close(ECSCassFile);
             ECSLoad     := False;
             ECSLoadable := False;
             ResetClock;
           End;

           { See if we're writing to the cassette or to the printer }

           If Value = $0039 Then
           Begin
             ECSSaveBufI := 0;
             ECSPrint    := False;
           End
           Else
           If Value = $0023 Then
           Begin
             ECSSaveBufI := 0;
             ECSPrint    := True;
           End;
         End;
       End;
     End
     Else
     Begin

       { Keyboard Component }

       If Cart = ct6502 Then
       Begin
         If (Addr >= $8000) And (Addr <= $BFFF) Then
         Begin
           Mem6502[Addr - $4000] := Value Shr 8;
           Mem6502[Addr - $8000] := Value And $FF;
           IntvMem[Addr]         := Value And $3FF;
         End;
       End;  

       { The following line is needed for World Cup Soccer to work.  It    }
       { attempts to write to a memory location where there is no RAM, so  }
       { the emulator has to keep it at zero.  However, the ECS has RAM at }
       { this area, so the emulator has to allow the write in that case.   }

       If (Addr = $47CD) And (Cart <> ctECS) Then IntvMem[$47CD] := 0;

       { Force high byte in GRAM to be zero }

       If (Addr >= $3800) And (Addr < $3800 + 64 * 8) Then
        IntvMem[Addr] := IntvMem[Addr] And $FF;

       { In the ECS, 4000-47FF is 8-bit memory }

       If (Addr >= $4000) And (Addr < $4800) And (Cart = ctECS) Then
        IntvMem[Addr] := IntvMem[Addr] And $FF;

       If (Addr = $EFFF) And (Value = $EA50) And (Cart = ctECS) Then
       Begin
         Move(GameE,IntvMem[$E000],SizeOf(GameE));
       End;
       If (Addr = $EFFF) And (Value = $EA51) And (Cart = ctECS) Then
       Begin
         Move(ECSE,IntvMem[$E000],SizeOf(ECSE));
       End;
     End;
  End;
End; { Handle_IO_Write }

Procedure Handle_IO_Read(Addr,Value: Word);
Var
  St    : String;
  C     : Char;
  I,J   : Integer;
  B     : Byte;

Begin
{
  If LogOutput Then
   WriteLn(LogFile,'CP1600: R[',HexWord(Addr),'] = ',HexWord(IntvMem[Addr]));
}
  ReadMem  := True;
  WriteMem := False;
  RWMemLoc := Addr;

  If Addr = $0021 Then ScreenMode := smColorStack;

  If Cart = ctECS Then
  Begin

    { Read from ECS cassette port }

    If Addr = $00E1 Then
    Begin
      If Not ECSLoad Then
      Begin
        ECSLoad := True;

        { The four-letter file name for CLOD and CVRF is stored internally }
        { at 4080h-4083h                                                   }

        St := Chr(IntvMem[$4080]) + Chr(IntvMem[$4081]) +
              Chr(IntvMem[$4082]) + Chr(IntvMem[$4083]);

// Remark out for now

{
        If CanOpenFile(St) Then
        Begin
          Assign(ECSCassFile,St);
          Reset(ECSCassFile,1);
          ECSLoadable := True;
        End
        Else ECSLoadable := False;
}
      End;
      If ECSLoad Then
      Begin
        If ECSLoadable Then
        Begin
          If Not Eof(ECSCassFile) Then
          Begin
            BlockRead(ECSCassFile,B,1);
            IntvMem[Addr] := B;
          End;
        End
        Else IntvMem[Addr] := 0;
      End;
    End;

    { Read ECS controllers.  Also supports importing ECS Basic text files }
    { by automatically typing their contents into the emulator.           }

    If ((Addr = $00FE) Or
        (Addr = $00FF)) Then
    Begin
      GetECSControllers;
      If Importing Then
      Begin

        { The last char was input; get another }

        If ImportRead Then
        Begin
          ECSKey      := False;         { No key currently being typed in   }
          ImportKey   := ECSInvalidKey; { No key currently being typed in   }
          ImportShift := False;         { ECS Shift key not needed          }
          ECSDelay    := 0;             { Reset auto-repeat delay           }
          ECSPCKey    := 0;             { No PC keyboard key being pressed  }
          If Not Eof(ImportFile) Then
          Begin
            BlockRead(ImportFile,C,1);

            { Try to find the corresponding key }

            If ASCIIToECSUnshift[Byte(C)] <> 255 Then
            Begin
              ImportKey   := ASCIIToECSUnshift[Byte(C)];
              ImportShift := False;
              ImportRead  := False;
            End
            Else
            If ASCIIToECSShift[Byte(C)] <> 255 Then
            Begin
              ImportKey   := ASCIIToECSShift[Byte(C)];
              ImportShift := True;
              ImportRead  := False;
            End;
          End
          Else
          Begin
            CloseFile(ImportFile);
            Importing   := False;
            ImportRead  := True;
            ECSKey      := False;         { No key currently being typed in   }
            ImportKey   := ECSInvalidKey; { No key currently being typed in   }
            ImportShift := False;         { ECS Shift key not needed          }
            ECSDelay    := 0;             { Reset auto-repeat delay           }
            ECSPCKey    := 0;             { No PC keyboard key being pressed  }
            ResetClock;
          End;
        End;

        { See if the ECS can read the char }

        ECSLeftController := $FF;

        If Importing And Not ImportRead Then
        Begin
          J := ImportKey And 7;
          I := (ImportKey Shr 3) And 7;
          If (ImportCount >= (ImportTime Div 2)) And
             ((IntvMem[$00F8] And $C0) = $40) Then
          Begin
            If ImportShift And ((IntvMem[$00FE] And (1 Shl 6)) = 0) Then
             ECSLeftController := ECSLeftController And $7F;
            If ((IntvMem[$00FE] And (1 Shl J)) = 0){ And
               ((IntvMem[$00F8] And $C0) = $40)} And
               (ImportCount >= 3 * (ImportTime Div 4)) Then
            Begin
              ECSLeftController := $FF Xor (1 Shl I);
              ImportCont         := ECSLeftController;
              If ImportCount >= ImportTime Then
              Begin
                ImportCount := 0;
                If ECSKey Then
                Begin

                  { Don't change ImportKey or ImportShift because we want }
                  { to remember the key for auto-repeat                   }

                  Importing   := False;   { No more character input }
                  ImportRead  := False;   { Not reading from file   }
                  ECSDelay    := 0;       { Reset auto-repeat delay }
                  ECSKey      := False;
                End
                Else ImportRead := True;
              End;
            End;
          End;
          Inc(ImportCount);
        End;
        If (IntvMem[$00F8] And $40) = 0 Then IntvMem[$00FE] := ECSRightController Or $FF00;
        If (IntvMem[$00F8] And $80) = 0 Then IntvMem[$00FF] := ECSLeftController Or $FF00;
      End;
    End;
  End;

  { Intellivoice }

  If (Addr = $80) Or (Addr = $81) Then IntvMem[Addr] := IVoice_Rd(Voice,Addr - $80);

{
  If Addr = $8301 Then IntvMem[$8301] := 0;
  If Addr = $8306 Then IntvMem[$8306] := 0;
  If Addr = $8340 Then IntvMem[$8340] := IntvMem[$8340] And $FB;
}
End; { Handle_IO_Read }

Procedure Interpret;
Var
  I          : LongInt;
  ECSEnable  : Word;
//  Counter6502 : Integer;

  Procedure Perform_Interrupt(Addr: Word);
  Begin
    IntvMem[Regs[6]] := Regs[7];
    Inc(Regs[6]);
    Regs[7] := Addr;
  End; { Perform_Interrupt }

  Function Interruptable: Boolean;
  Var Instr: Word;
  Begin
    Instr := IntvMem[Regs[7]] And $3FF;
    If (Instr = 0) Or
       ((Instr >= 8)   And (Instr < $40))  Or
       ((Instr >= $80) And (Instr < $240)) Or
       (Instr >= $280)
     Then Interruptable := True
     Else Interruptable := False;
  End; { Interruptable }

Label EmulateLoop,EmulateStart,
      _HLT,_SDBD,_EIS,_DIS,_Jxxx,_TCI,_CLRC,_SETC,_INCR,_DECR,_COMR,_NEGR,
      _ADCR,_GSWD,_NOP,_SIN,_RSWD,_SWAP,_SLL,_RLC,_SLLC,_SLR,_SAR,_RRC,
      _SARC,_MOVR,_ADDR,_SUBR,_CMPR,_ANDR,_XORR,_Bxxxx,_MVO,_MVOimp,
      _MVI,_MVIimp,_ADD,_ADDimp,_SUB,_SUBimp,_CMP,_CMPimp,_AND,_ANDimp,
      _XOR,_XORimp;
Var
  SoundCount : LongInt;
  T          : LongInt;

Begin

//  Counter6502 := 0;

  EBCA       := 0;
  EBCI       := False;
  InsCount   := 0;
  SlowCount  := 0;
  SoundCount := 0;

  { Fill in clock cycle array }

  Cycles[0] := 4;          { HLT }
  Cycles[1] := 4;          { SDBD }
  Cycles[2] := 4;          { EIS }
  Cycles[3] := 4;          { DIS }
  Cycles[4] := 12;         { J/JD/JE/JSR/JSRD/JSRE }
  Cycles[5] := 4;          { TCI }
  Cycles[6] := 4;          { CLRC }
  Cycles[7] := 4;          { SETC }
  For I := 8 To $3F Do     { INCR/DECR/COMR/NEGR/ADCR/GSWD/RSWD }
   Cycles[I] := 6;
  For I := $40 To $7F Do   { SWAP/SLL/RLC/SLLC/SLR/SAR/RRC/SARC }
  Begin
    If (I And 4) = 0
     Then Cycles[I] := 6
     Else Cycles[I] := 8;
  End; { For I }
  For I := $80 To $1FF Do  { ADDR/SUBR/CMPR/ANDR/XORR }
  Begin
    If (I And 7) = 7
     Then Cycles[I] := 7
     Else Cycles[I] := 6;
  End; { For I }
  For I := $200 To $23F Do { Bcond/BEXT }
   Cycles[I] := 7;
  For I := $240 To $27F Do { MVO/MVO@ }
  Begin
    If (I And $38) = 0
     Then Cycles[I] := 11
     Else Cycles[I] := 9;
  End; { For I }
  For I := $280 To $3FF Do { MVI/MVI@/ADD/ADD@/SUB/SUB@/CMP/CMP@/AND/AND@/XOR/XOR@ }
  Begin
    If (I And $38) = 0
     Then Cycles[I] := 10
     Else
     Begin
       If (I And 7) = 7
        Then Cycles[I] := 8
        Else Cycles[I] := 11;
     End;
  End; { For I }

  Asm
    MOV   DWORD PTR Locations[0000h * 4],OFFSET _HLT
    MOV   DWORD PTR Locations[0001h * 4],OFFSET _SDBD
    MOV   DWORD PTR Locations[0002h * 4],OFFSET _EIS
    MOV   DWORD PTR Locations[0003h * 4],OFFSET _DIS
    MOV   DWORD PTR Locations[0004h * 4],OFFSET _Jxxx
    MOV   DWORD PTR Locations[0005h * 4],OFFSET _TCI
    MOV   DWORD PTR Locations[0006h * 4],OFFSET _CLRC
    MOV   DWORD PTR Locations[0007h * 4],OFFSET _SETC
    MOV   DWORD PTR Locations[0008h * 4],OFFSET _INCR  { Must be copied }
    MOV   DWORD PTR Locations[0010h * 4],OFFSET _DECR
    MOV   DWORD PTR Locations[0018h * 4],OFFSET _COMR
    MOV   DWORD PTR Locations[0020h * 4],OFFSET _NEGR
    MOV   DWORD PTR Locations[0028h * 4],OFFSET _ADCR
    MOV   DWORD PTR Locations[0030h * 4],OFFSET _GSWD
    MOV   DWORD PTR Locations[0031h * 4],OFFSET _GSWD
    MOV   DWORD PTR Locations[0032h * 4],OFFSET _GSWD
    MOV   DWORD PTR Locations[0033h * 4],OFFSET _GSWD
    MOV   DWORD PTR Locations[0034h * 4],OFFSET _NOP
    MOV   DWORD PTR Locations[0035h * 4],OFFSET _NOP
    MOV   DWORD PTR Locations[0036h * 4],OFFSET _SIN
    MOV   DWORD PTR Locations[0037h * 4],OFFSET _SIN
    MOV   DWORD PTR Locations[0038h * 4],OFFSET _RSWD
    MOV   DWORD PTR Locations[0040h * 4],OFFSET _SWAP
    MOV   DWORD PTR Locations[0048h * 4],OFFSET _SLL
    MOV   DWORD PTR Locations[0050h * 4],OFFSET _RLC
    MOV   DWORD PTR Locations[0058h * 4],OFFSET _SLLC
    MOV   DWORD PTR Locations[0060h * 4],OFFSET _SLR
    MOV   DWORD PTR Locations[0068h * 4],OFFSET _SAR
    MOV   DWORD PTR Locations[0070h * 4],OFFSET _RRC
    MOV   DWORD PTR Locations[0078h * 4],OFFSET _SARC
    MOV   DWORD PTR Locations[0080h * 4],OFFSET _MOVR
    MOV   DWORD PTR Locations[00C0h * 4],OFFSET _ADDR
    MOV   DWORD PTR Locations[0100h * 4],OFFSET _SUBR
    MOV   DWORD PTR Locations[0140h * 4],OFFSET _CMPR
    MOV   DWORD PTR Locations[0180h * 4],OFFSET _ANDR
    MOV   DWORD PTR Locations[01C0h * 4],OFFSET _XORR
    MOV   DWORD PTR Locations[0200h * 4],OFFSET _Bxxxx
    MOV   DWORD PTR Locations[0240h * 4],OFFSET _MVO
    MOV   DWORD PTR Locations[0248h * 4],OFFSET _MVOimp
    MOV   DWORD PTR Locations[0280h * 4],OFFSET _MVI
    MOV   DWORD PTR Locations[0288h * 4],OFFSET _MVIimp
    MOV   DWORD PTR Locations[02C0h * 4],OFFSET _ADD
    MOV   DWORD PTR Locations[02C8h * 4],OFFSET _ADDimp
    MOV   DWORD PTR Locations[0300h * 4],OFFSET _SUB
    MOV   DWORD PTR Locations[0308h * 4],OFFSET _SUBimp
    MOV   DWORD PTR Locations[0340h * 4],OFFSET _CMP
    MOV   DWORD PTR Locations[0348h * 4],OFFSET _CMPimp
    MOV   DWORD PTR Locations[0380h * 4],OFFSET _AND
    MOV   DWORD PTR Locations[0388h * 4],OFFSET _ANDimp
    MOV   DWORD PTR Locations[03C0h * 4],OFFSET _XOR
    MOV   DWORD PTR Locations[03C8h * 4],OFFSET _XORimp
  End; { Asm }
  For I := 1 To 7 Do
  Begin
    Locations[I + $08]  := Locations[8];
    Locations[I + $10]  := Locations[$10];
    Locations[I + $18]  := Locations[$18];
    Locations[I + $20]  := Locations[$20];
    Locations[I + $28]  := Locations[$28];
    Locations[I + $38]  := Locations[$38];
    Locations[I + $40]  := Locations[$40];
    Locations[I + $48]  := Locations[$48];
    Locations[I + $50]  := Locations[$50];
    Locations[I + $58]  := Locations[$58];
    Locations[I + $60]  := Locations[$60];
    Locations[I + $68]  := Locations[$68];
    Locations[I + $70]  := Locations[$70];
    Locations[I + $78]  := Locations[$78];
    Locations[I + $240] := Locations[$240];
    Locations[I + $280] := Locations[$280];
    Locations[I + $2C0] := Locations[$2C0];
    Locations[I + $300] := Locations[$300];
    Locations[I + $340] := Locations[$340];
    Locations[I + $380] := Locations[$380];
    Locations[I + $3C0] := Locations[$3C0];
  End; { For I }
  For I := 1 To 63 Do
  Begin
    Locations[I + $80]  := Locations[$80];
    Locations[I + $C0]  := Locations[$C0];
    Locations[I + $100] := Locations[$100];
    Locations[I + $140] := Locations[$140];
    Locations[I + $180] := Locations[$180];
    Locations[I + $1C0] := Locations[$1C0];
    Locations[I + $200] := Locations[$200];
  End; { For I }
  For I := 1 To 55 Do
  Begin
    Locations[I + $248] := Locations[$248];
    Locations[I + $288] := Locations[$288];
    Locations[I + $2C8] := Locations[$2C8];
    Locations[I + $308] := Locations[$308];
    Locations[I + $348] := Locations[$348];
    Locations[I + $388] := Locations[$388];
    Locations[I + $3C8] := Locations[$3C8];
  End; { For I }
  If LogOutput Then
  Begin
    Assign(LogFile,'INTVEMU.LOG');
    ReWrite(LogFile);
  End;
//  IntPending     := False;
//  IntPending6502 := False;


  Goto EmulateStart;

EmulateLoop:

  Asm
    POPAD
  End; // Asm

EmulateStart:

  { Do I/O emulation here }

{  If LogOutput Then WriteLn(LogFile,HexWord(Regs[7]) + '---' + Disassemble(@IntvMem,Regs[7]));}



(*
  If LogOutput Then
   WriteLn(LogFile,HexWord(Regs[0]),' ',
                 HexWord(Regs[1]),' ',
                 HexWord(Regs[2]),' ',
                 HexWord(Regs[3]),' ',
                 HexWord(Regs[4]),' ',
                 HexWord(Regs[5]),' ',
                 HexWord(Regs[6]),'-',
                 HexWord(Regs[7]),'--');
//                 Disassemble(PD1600Mem(IntvMemPtr[Regs[7] Shr 12]),Regs[7] And $FFF));
*)



{
  For I := $2F0 To $2FF Do Write(LogFile,HexWord(IntvMem[I]),' ');
  Write(LogFile,' [035F]=' + HexWord(IntvMem[$035F]));
  WriteLn(LogFile);
  If IntvMem[Regs[7]] > $3FF Then CloseFile(LogFile);
}

{  WriteLn(LogFile,HexWord(Regs[7]));}



{  If (InsCount And $FFF) = 0 Then EventHandler;}


(*
  If ((InsCount And InterruptFlag) = 0) And
     (Tracing <> 1)                     And
     Interrupts                         Then
  Begin
    If Interruptable And Not DoubleByte
     Then
     Begin
       Perform_Interrupt($1004);
       Form1.FormPaint(Form1);
{       If LogOutput Then WriteLn(LogFile,'Repainting');}
     End
     Else Dec(InsCount);
  End;
*)



(*
  If InsCount >= InterruptCycles Then
  Begin
    EventHandler;
    If Config.FrameRate <> 0 Then SlowDown;
    If Interrupts Then
    Begin
      If Interruptable And Not DoubleByte
       Then
       Begin
         Perform_Interrupt($1004);
         Form1.FormPaint(Form1);
       End;
{       Else Dec(InsCount);}
    End;
    InsCount := 0;
  End;
*)

{  WriteOut(1,1,HexWord(Regs[7]),15);}



  If (SoundCount And $7F = 0) And Config.DoSound Then
  Begin

    // Queue two sound samples and update the sound playback buffer

    If SoundQueue_Num < 1022 Then QueueSoundSample;
    FillBuffer;
  End;

//  If (SoundCount And $1FF = 0) And Config.DoSound Then FillBuffer;

  Inc(SoundCount);

  If InsCount >= InterruptCycles / 100 Then
  Begin
    PollIntv2PC;
    If (Config.FrameRate <> 0) And
       (ECSKey Or Not (Importing Or ECSSave Or ECSLoad Or Not DoSlowDown)) Then SlowDown(10);
    InsCount := InsCount - Trunc(InterruptCycles / 100);
    Inc(SlowCount);


   // Calculate measured FPS for display

   T := GetTickCount;
   If T > frmMain.BaseTime Then frmMain.FPS := 1000 * frmMain.Frames / (T - frmMain.BaseTime);

  End;

{  If InsCount >= InterruptCycles Then}
  If SlowCount >= 100 Then
  Begin
    EventHandler;
{    If Config.FrameRate <> 0 Then SlowDown(1000);}
    IntPending := True;
    IntPending6502 := True;
{    InsCount   := InsCount - Trunc(InterruptCycles);}
    SlowCount := 0;
  End;
  If IntPending Then
  Begin
    If Interrupts > 2 Then
    Begin
      If Not DoubleByte Then
      Begin
        Perform_Interrupt($1004);
        PaintSurface;
        IntPending := False;

{
  If LogOutput Then
  Begin
   WriteLn(LogFile,'Interrupt');
   WriteLn(LogFile,HexWord(Regs[0]),' ',
                 HexWord(Regs[1]),' ',
                 HexWord(Regs[2]),' ',
                 HexWord(Regs[3]),' ',
                 HexWord(Regs[4]),' ',
                 HexWord(Regs[5]),' ',
                 HexWord(Regs[6]),'-',
                 HexWord(Regs[7]),'--',
                 HexWord(IntvMemW($0100)),' ',
                 HexWord(IntvMemW($0101)),' ',
                 Disassemble(PD1600Mem(IntvMemPtr[Regs[7] Shr 12]),Regs[7] And $FFF));
  End;
}
      End;
    End;
  End;

  If IntPending6502 Then
  Begin
    Perform6502Interrupt;
    PaintSurface;
    IntPending6502 := False;
  End;


{
  I := GRAM_LOC + $29 * 8 + 4;
  I := IntvMemPtr[I Shr 12]^[I And $FFF];
  If I <> OldCard Then
  Begin
    If CardChange > 5 Then
    Begin
      Emulating  := False;
      NewList    := True;
      FormPaint(Self);

      If LogOutput Then Close(LogFile);

      Exit;
    End
    Else Inc(CardChange);
    OldCard := I;
  End;
}
{
  If (Regs[7] = Breakpoint) And (Tracing <> 1) And Breaking Then
  Begin
    Emulating  := False;
    NewList    := True;
    FormPaint(Self);

    If LogOutput Then Close(LogFile);

    Exit;
  End;
}
{  Inc(InsCount);}

  ECSEnable := IntvMem[$01F8];
  If (ECSEnable And $40) = 0 Then IntvMem[$01FE] := RightController;
  If (ECSEnable And $80) = 0 Then IntvMem[$01FF] := LeftController;
  If Cart = ctECS Then
  Begin
    ECSEnable := IntvMem[$00F8];
    If (ECSEnable And $40) = 0 Then IntvMem[$00FE] := ECSRightController Or $FF00;
    If (ECSEnable And $80) = 0 Then IntvMem[$00FF] := ECSLeftController Or $FF00;
  End;

  { Stop if we have reached a debugger breakpoint }

  If Debugger Then
  Begin
    If (Trace = 0) And
       (((DebugAddr = Game6502.PC) And Debug6502)      Or
        ((DebugAddr = Regs[7])     And Not Debug6502)) Then Emulating := 0;
    If Trace >= 2 Then
    Begin
      Trace     := 0;
      Emulating := 0;
    End;
    If (BreakExpColl <> Nil) And
       Evaluate(BreakExpColl,IntvMem,Regs,StatusWord,SpriteX,SpriteY,
                ReadMem,WriteMem,RWMemLoc,ExpError)
     Then Emulating := 0;
    If (BreakExpColl6502 <> Nil) And
       Evaluate6502(BreakExpColl6502,Mem6502,
                    Game6502.X,Game6502.Y,Game6502.A,Game6502.P,Game6502.S,Game6502.PC,
                    ReadMem6502,WriteMem6502,RWMemLoc6502,ExpError)
     Then Emulating := 0;
    If Trace > 0 Then Inc(Trace);
  End;
  ReadMem  := False;
  WriteMem := False;
  RWMemLoc := 0;

  ReadMem6502  := False;
  WriteMem6502 := False;
  RWMemLoc6502 := 0;

  { Stop if we have to }

  If Emulating = 0 Then
  Begin

    If LogOutput Then Close(LogFile);
    If AVIFile.Opened Then frmMain.btnRecordClick(frmMain);
    Exit;
  End;

  Interrupts := Interrupts Or 1;

//  If (Counter6502 And 1) = 0 Then
  If Cart = ct6502 Then Interpret6502(Game6502.PC,Game6502.P,Game6502.A,Game6502.X,Game6502.Y,Game6502.S);
//  Counter6502 := Counter6502 Xor 1; 

  Asm
    PUSHAD
    MOVZX EDI,WORD PTR Regs[7 * 2]       { Regs[7] is the PC; save it in EDI }
    MOVZX EAX,WORD PTR IntvMem[EDI * 2]  { Save the opcode in EAX }
    MOV   ESI,EAX                        { Quadruple it to get the destination }
    AND   ESI,03FFh
    MOVZX BX,BYTE PTR Cycles[ESI]        { Get the number of cycles }
    ADD   WORD PTR InsCount,BX
    SHL   ESI,2
    MOV   EBX,DWORD PTR Locations[ESI]   { Get the destination }
    JMP   EBX                            { Jump to it }
  End; { Asm }

_HLT:                                  { HLT   0000000000 }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    JMP   EmulateLoop
  End; { Asm }

_SDBD:                                 { SDBD  0000000001 }

  Asm
    MOV   BYTE PTR DoubleByte,1
    INC   WORD PTR Regs[7 * 2]
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_EIS:                                  { EIS   0000000010 }

  Asm
    MOV   BYTE PTR Interrupts,2
    MOV   BYTE PTR DoubleByte,0
    INC   WORD PTR Regs[7 * 2]
    JMP   EmulateLoop
  End; { Asm }

_DIS:                                  { DIS   0000000011 }

  Asm
    MOV   BYTE PTR Interrupts,0
    MOV   BYTE PTR DoubleByte,0
    INC   WORD PTR Regs[7 * 2]
    JMP   EmulateLoop
  End; { Asm }

_Jxxx:                                 { Jxxx  0000000100 }

  Asm
    MOV   ESI,EDI
    INC   SI
    MOV   AX,WORD PTR IntvMem[ESI * 2]
    MOV   EBX,EAX
    AND   EBX,3
    CMP   EBX,1
    JNZ   @L1
    MOV   BYTE PTR Interrupts,2
@L1:
    CMP   EBX,2
    JNZ   @L2
    MOV   BYTE PTR Interrupts,0
@L2:
    MOV   EBX,EAX
    AND   EBX,300h
    SHR   EBX,7
    OR    EBX,8
    MOVZX ECX,WORD PTR Regs[7 * 2]
    ADD   ECX,3
    MOV   WORD PTR Regs[EBX],CX
    AND   EAX,0FCh

    MOV   ESI,EDI
    ADD   SI,2
    MOVZX EBX,WORD PTR IntvMem[ESI * 2]

    AND   EBX,3FFh
    SHL   EAX,8
    OR    EAX,EBX
    MOV   WORD PTR Regs[7 * 2],AX
    MOV   BYTE PTR DoubleByte,0
    JMP   EmulateLoop
  End; { Asm }

_TCI:                                  { TCI   0000000101 }

  Asm
    MOV   BYTE PTR DoubleByte,0
    MOV   BYTE PTR Interrupts,2
    DEC   WORD PTR Regs[6 * 2]
    MOVZX EDI,WORD PTR Regs[6 * 2]
    MOV   AX,WORD PTR IntvMem[EDI * 2]
    MOV   WORD PTR Regs[7 * 2],AX
    JMP   EmulateLoop
  End; { Asm }

_CLRC:                                 { CLRC  0000000110 }

  Asm
    MOV   BYTE PTR DoubleByte,0
    AND   WORD PTR StatusWord,0Eh
    INC   WORD PTR Regs[7 * 2]
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_SETC:                                 { SETC  0000000111 }

  Asm
    MOV   BYTE PTR DoubleByte,0
    OR    WORD PTR StatusWord,1
    INC   WORD PTR Regs[7 * 2]
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_INCR:                                 { INCR  0000001ddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,7
    INC   WORD PTR Regs[EBX * 2]
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_DECR:                                 { DECR  0000010ddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,7
    DEC   WORD PTR Regs[EBX * 2]
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_COMR:                                 { COMR  0000011ddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,7
    NOT   WORD PTR Regs[EBX * 2]
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_NEGR:                                 { NEGR  0000100ddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,7
    NEG   WORD PTR Regs[EBX * 2]
    MOV   AL,2
    LAHF
    JO    @L1
    XOR   AL,AL
@L1:
    AND   AH,0C1h
    OR    AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_ADCR:                                 { ADCR  0000101ddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,7
    MOV   AX,WORD PTR StatusWord
    AND   EAX,1
    ADD   WORD PTR Regs[EBX * 2],AX
    MOV   AL,2
    LAHF
    JO    @L1
    XOR   AL,AL
@L1:
    AND   AH,0C1h
    OR    AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_GSWD:                                 { GSWD  00001100rr }

  Asm
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,3
    MOV   AX,WORD PTR StatusWord
    MOV   AH,AL
    AND   EAX,0F0Fh
    SHL   EAX,4
    MOV   WORD PTR Regs[EBX * 2],AX
    INC   WORD PTR Regs[7 * 2]
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_NOP:                                  { NOP   000011010m }

  Asm
    MOV   BYTE PTR DoubleByte,0
    INC   WORD PTR Regs[7 * 2]
    JMP   EmulateLoop
  End; { Asm }

_SIN:                                  { SIN   000011011m }

  Asm
    MOV   BYTE PTR DoubleByte,0
    INC   WORD PTR Regs[7 * 2]
    JMP   EmulateLoop
  End; { Asm }

_RSWD:                                 { RSWD  0000111sss }

  Asm
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,7
    MOV   AX,WORD PTR Regs[EBX * 2]
    SHR   EAX,4
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    INC   WORD PTR Regs[7 * 2]
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_SWAP:                                 { SWAP  0001000mrr }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,3
    MOV   CX,WORD PTR Regs[EBX * 2]
    TEST  EAX,4
    JNZ   @L3
    MOV   DX,CX
    XCHG  CH,CL
    MOV   WORD PTR Regs[EBX * 2],CX
    JMP   @L1
@L3:
    MOV   CH,CL
    MOV   WORD PTR Regs[EBX * 2],CX
    MOV   EDX,ECX
@L1:
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_SLL:                                  { SLL   0001001mrr }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   CL,1
    TEST  EAX,4
    JZ    @L2
    INC   CL
@L2:
    MOV   EBX,EAX
    AND   EBX,3
    SHL   WORD PTR Regs[EBX * 2],CL
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_RLC:                                  { RLC   0001010mrr }

  Asm
    AND   BYTE PTR Interrupts,0FEh
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,3
    MOV   DX,WORD PTR Regs[EBX * 2]
    TEST  EAX,4
    JNZ   @TwoBits
    MOV   AX,WORD PTR StatusWord
    AND   EAX,0Fh
    SHR   AL,1
    ADC   DX,DX
    LAHF
    MOV   WORD PTR Regs[EBX * 2],DX
    ADC   AL,AL
    SHR   AH,4
    AND   EAX,0C03h
    OR    AL,AH
    SUB   AH,AH
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
@TwoBits:
    MOV   AX,WORD PTR StatusWord
    AND   EAX,0Fh
    SHR   EAX,1
    ADC   DX,DX
    RCR   AX,1
    ADC   DX,DX
    RCR   AX,0Fh
    MOV   WORD PTR Regs[EBX * 2],DX
    OR    DX,DX
    LAHF
    SHR   AH,4
    AND   EAX,0C03h
    OR    AL,AH
    SUB   AH,AH
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_SLLC:                                 { SLLC  0001011mrr }

  Asm
    AND   BYTE PTR Interrupts,0FEh
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,3
    TEST  EAX,4
    JNZ   @TwoBits
    MOV   CX,WORD PTR StatusWord
    AND   ECX,2
    SHL   WORD PTR Regs[EBX * 2],1
    LAHF
    ADC   ECX,0
    SHR   AH,4
    AND   AH,0Ch
    OR    CL,AH
    MOV   WORD PTR StatusWord,CX
    JMP   EmulateLoop
@TwoBits:
    XOR   ECX,ECX
    MOV   DX,WORD PTR Regs[EBX * 2]
    ADD   DX,DX
    ADC   CH,CH
    ADD   DX,DX
    ADC   CL,CL
    MOV   WORD PTR Regs[EBX * 2],DX
    OR    DX,DX
    LAHF
    ADD   CL,CL
    OR    CL,CH
    SHR   AH,4
    AND   AH,0Ch
    OR    CL,AH
    SUB   CH,CH
    MOV   WORD PTR StatusWord,CX
    JMP   EmulateLoop
  End; { Asm }

_SLR:                                  { SLR   0001100mrr }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,3
    MOV   DX,WORD PTR Regs[EBX * 2]
    MOV   CL,1
    TEST  EAX,4
    JZ    @L1
    INC   CL
@L1:
    SHR   DX,CL
    MOV   WORD PTR Regs[EBX * 2],DX
    XCHG  DL,DH
    OR    DX,DX
    LAHF
    MOV   AL,BYTE PTR StatusWord
    AND   EAX,0C003h
    SHR   AH,4
    OR    AL,AH
    SUB   AH,AH
    MOV   WORD PTR StatusWord,AX
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_SAR:                                  { SAR   0001101mrr }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,3
    MOV   DX,WORD PTR Regs[EBX * 2]
    MOV   CL,1
    TEST  EAX,4
    JZ    @L1
    INC   CL
@L1:
    SAR   DX,CL
    MOV   WORD PTR Regs[EBX * 2],DX
    XCHG  DL,DH
    OR    DX,DX
    LAHF
    MOV   AL,BYTE PTR StatusWord
    AND   EAX,0C003h
    SHR   AH,4
    OR    AL,AH
    SUB   AH,AH
    MOV   WORD PTR StatusWord,AX
    AND   BYTE PTR Interrupts,0FEh
    JMP   EmulateLoop
  End; { Asm }

_RRC:                                  { RRC   0001110mrr }

  Asm
    AND   BYTE PTR Interrupts,0FEh
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,3
    MOV   DX,WORD PTR Regs[EBX * 2]
    TEST  EAX,4
    JNZ   @TwoBits
    MOV   AX,WORD PTR StatusWord
    SHR   EAX,1
    RCR   DX,1
    MOV   WORD PTR Regs[EBX * 2],DX
    ADC   AX,AX
    XCHG  DL,DH
    OR    DX,DX
    LAHF
    SHR   AH,4
    AND   EAX,0C03h
    OR    AL,AH
    SUB   AH,AH
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
@TwoBits:
    MOV   CX,WORD PTR StatusWord
    SHL   CL,6
    MOV   AX,DX
    SHR   DX,2
    OR    DH,CL
    MOV   WORD PTR Regs[EBX * 2],DX
    XCHG  DL,DH
    OR    DX,DX
    LAHF
    SHR   AH,4
    AND   EAX,0C03h
    OR    AL,AH
    SUB   AH,AH
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_SARC:                                 { SARC  0001111mrr }

  Asm
    AND   BYTE PTR Interrupts,0FEh
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    AND   EBX,3
    MOV   DX,WORD PTR Regs[EBX * 2]
    TEST  EAX,4
    JNZ   @TwoBits
    MOV   CX,WORD PTR StatusWord
    AND   ECX,2
    SAR   DX,1
    MOV   WORD PTR Regs[EBX * 2],DX
    ADC   CX,0
    XCHG  DL,DH
    OR    DX,DX
    LAHF
    SHR   AH,4
    AND   AH,0Ch
    OR    CL,AH
    MOV   WORD PTR StatusWord,CX
    JMP   EmulateLoop
@TwoBits:
    MOV   AX,DX
    SAR   DX,2
    MOV   WORD PTR Regs[EBX * 2],DX
    XCHG  DL,DH
    OR    DX,DX
    LAHF
    SHR   AH,4
    AND   AX,0C03h
    OR    AL,AH
    SUB   AH,AH
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_MOVR:                                 { MOVR  0010sssddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    MOV   DX,WORD PTR Regs[EDI]
    MOV   WORD PTR Regs[EBX * 2],DX
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_ADDR:                                 { ADDR  0011sssddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    MOV   AX,WORD PTR Regs[EDI]
    ADD   WORD PTR Regs[EBX * 2],AX
    MOV   AL,2
    LAHF
    JO    @L1
    XOR   AL,AL
@L1:
    AND   AH,0C1h
    OR    AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_SUBR:                                 { SUBR  0100sssddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    MOV   AX,WORD PTR Regs[EDI]
    SUB   WORD PTR Regs[EBX * 2],AX
    MOV   AL,3
    LAHF
    JO    @L1
    MOV   AL,1
@L1:
    AND   AH,0C1h
    XOR   AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_CMPR:                                 { CMPR  0101sssddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    MOV   AX,WORD PTR Regs[EBX * 2]
    SUB   AX,WORD PTR Regs[EDI]
    MOV   AL,3
    LAHF
    JO    @L1
    MOV   AL,1
@L1:
    AND   AH,0C1h
    XOR   AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_ANDR:                                 { ANDR  0110sssddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    MOV   AX,WORD PTR Regs[EDI]
    AND   WORD PTR Regs[EBX * 2],AX
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_XORR:                                 { XORR  0111sssddd }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   BYTE PTR DoubleByte,0
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    MOV   AX,WORD PTR Regs[EDI]
    XOR   WORD PTR Regs[EBX * 2],AX
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_Bxxxx:                                { Bxxxx 1000z0cccc/1000z1eeee }

  Asm
    TEST  EAX,10h
    JNZ   @L1
    MOV   BX,WORD PTR StatusWord
    AND   EBX,0Fh
    MOV   ECX,EBX
    MOV   EDX,EBX
    SHR   ECX,2
    XOR   ECX,EBX  { Get S xor O }

    OR    ECX,1    { Set to 1 for "always true" test }

    AND   ECX,3
    SHL   ECX,4
    OR    EBX,ECX

    SHR   ECX,3    { Get Z or (S xor O) }
    OR    ECX,EBX
    AND   ECX,4
    SHL   ECX,4
    OR    EBX,ECX

    MOV   ECX,EDX
    SHR   ECX,3
    XOR   ECX,EBX  { Get S xor C }
    AND   ECX,1
    SHL   ECX,7
    OR    EBX,ECX

    MOV   ESI,EAX  { Get just the condition code }
    AND   ESI,0Fh
    SUB   ECX,ECX  { Default to false }
    TEST  ESI,8
    JZ    @L2
    XOR   ECX,1    { Switch defualt if "not" condition code }
    AND   ESI,7    { Strip "not" part of code }
@L2:
    CMP   ESI,3
    JNE   @L3
    XOR   ECX,1    { Switch defualt if checking sign bit }
@L3:
    TEST  BL,BYTE PTR Condition[ESI]
    JZ    @L4
    XOR   ECX,1    { Switch if condition is met }
@L4:
    ADD   WORD PTR Regs[7 * 2],2
    TEST  ECX,1
    JZ    @L5

    ADD   WORD PTR InsCount,2

    MOV   ESI,EDI
    INC   SI
    MOVZX EDI,WORD PTR IntvMem[ESI * 2]

    TEST  EAX,20h
    JNZ   @L6
    ADD   WORD PTR Regs[7 * 2],DI
    MOV   BYTE PTR DoubleByte,0
    JMP   EmulateLoop
@L6:
    INC   EDI
    SUB   WORD PTR Regs[7 * 2],DI
@L5:
    MOV   BYTE PTR DoubleByte,0
    JMP   EmulateLoop

    { Support branch on external condition here }

@L1:
    MOV   BYTE PTR DoubleByte,0
    MOV   CL,AL
    AND   CL,0Fh
    ADD   WORD PTR Regs[7 * 2],2
    TEST  BYTE PTR EBCI,0FFh
    JZ    @L7
    MOV   BYTE PTR EBCA,CL

    ADD   WORD PTR InsCount,2

    MOV   ESI,EDI
    INC   SI
    MOVZX EDI,WORD PTR IntvMem[ESI * 2]

    TEST  EAX,20h
    JNZ   @L8
    ADD   WORD PTR Regs[7 * 2],DI
    JMP   @L7
@L8:
    INC   EDI
    SUB   WORD PTR Regs[7 * 2],DI
@L7:
    MOV   BYTE PTR DoubleByte,0
    JMP   EmulateLoop
  End; { Asm }

_MVO:                                  { MVO   1001000sss }

  Asm
    AND   BYTE PTR Interrupts,0FEh
    ADD   WORD PTR Regs[7 * 2],2
    MOV   EBX,EAX
    AND   EBX,7

    MOV   ESI,EDI
    INC   SI
    MOVZX EAX,WORD PTR IntvMem[ESI * 2]

    MOV   EDI,EAX

    MOV   AX,WORD PTR Regs[EBX * 2]

    PUSHAD
    MOV   EAX,EDI
    CALL  CanWrite
    TEST  AX,0FFh
    JZ    @NoWrite
    POPAD

    MOV   WORD PTR IntvMem[EDI * 2],AX

    MOV   EDX,EAX
    MOV   EAX,EDI
    CALL  Handle_IO_Write

    JMP   EmulateLoop
@NoWrite:
    POPAD

    JMP   EmulateLoop
  End; { Asm }

_MVOimp:                               { MVO@  1001mmmsss   (mmm <> 0) }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    SUB   ESI,ESI
    TEST  BYTE PTR DoubleByte,1
    JNZ   @Double
    MOVZX ESI,WORD PTR Regs[EDI]
    MOV   CX,WORD PTR Regs[EBX * 2]
    ADD   SI,WORD PTR PreWrite[EDI]

    PUSHAD
    MOV   EAX,ESI
    CALL  CanWrite
    TEST  AX,0FFh
    JZ    @NoWrite
    POPAD

    MOV   WORD PTR IntvMem[ESI * 2],CX

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Write
@NoWrite:
    POPAD

    ADD   SI,WORD PTR PostWrite[EDI]
    MOV   WORD PTR Regs[EDI],SI
    JMP   EmulateLoop
@Double:
    MOVZX ESI,WORD PTR Regs[EDI]
    MOV   CX,WORD PTR Regs[EBX * 2]
    ADD   SI,WORD PTR PreWrite[EDI]

    PUSHAD
    MOV   EAX,ESI
    CALL  CanWrite
    TEST  AX,0FFh
    JZ    @NoWrite1
    POPAD

    MOV   BYTE PTR IntvMem[ESI * 2],CL
    MOV   BYTE PTR IntvMem[(ESI * 2) + 1],0
    POP   EDI

    PUSHAD
    MOV   EAX,ESI
    SUB   EDX,EDX
    MOV   DL,CL
    CALL  Handle_IO_Write
@NoWrite1:
    POPAD
    
    ADD   SI,WORD PTR PostWrite[EDI]

    MOV   BYTE PTR DoubleByte,0

    PUSHAD
    MOV   EAX,ESI
    CALL  CanWrite
    TEST  AX,0FFh
    JZ    @NoWrite2
    POPAD

    MOV   BYTE PTR IntvMem[ESI * 2],CH
    MOV   BYTE PTR IntvMem[(ESI * 2) + 1],0
    POP   EDI

    PUSHAD
    MOV   EAX,ESI
    SUB   EDX,EDX
    MOV   DL,CH
    CALL  Handle_IO_Write
@NoWrite2:
    POPAD

    ADD   SI,WORD PTR PostWrite[EDI]
    MOV   WORD PTR Regs[EDI],SI
    JMP   EmulateLoop
  End; { Asm }

_MVI:                                  { MVI   1010000ddd }

  Asm
    ADD   WORD PTR Regs[7 * 2],2
    MOV   EBX,EAX
    AND   EBX,7

    MOV   ESI,EDI
    INC   SI
    MOVZX EAX,WORD PTR IntvMem[ESI * 2]

    MOV   EDI,EAX

    MOV   AX,WORD PTR IntvMem[EDI * 2]

    PUSHAD
    MOV   EDX,EAX
    MOV   EAX,EDI
    CALL  Handle_IO_Read
    POPAD

    MOV   AX,WORD PTR IntvMem[EDI * 2]

    MOV   WORD PTR Regs[EBX * 2],AX
    JMP   EmulateLoop
  End; { Asm }

_MVIimp:                               { MVI@  1010mmmddd   (mmm <> 0) }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    ADD   EBX,EBX
    AND   EDI,0Eh
    TEST  BYTE PTR DoubleByte,1
    JNZ   @Double
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOVZX ECX,WORD PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    MOV   WORD PTR Regs[EBX],CX
    ADD   SI,WORD PTR PostRead[EDI]
    CMP   EDI,EBX
    JE    EmulateLoop
    MOV   WORD PTR Regs[EDI],SI
    JMP   EmulateLoop

@Double:
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOVZX ECX,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    ADD   SI,WORD PTR PostRead[EDI]

    PUSHAD
    MOV   EAX,ESI
    MOVZX EDX,BYTE PTR IntvMem[ESI * 2]
    CALL  Handle_IO_Read
    POPAD

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

    ADD   SI,WORD PTR PostRead[EDI]
    MOV   WORD PTR Regs[EBX],CX
    MOV   BYTE PTR DoubleByte,0
    CMP   EDI,EBX
    JE    EmulateLoop
    MOV   WORD PTR Regs[EDI],SI
    JMP   EmulateLoop
  End; { Asm }

_ADD:                                  { ADD   1011000ddd }

  Asm
    ADD   WORD PTR Regs[7 * 2],2
    MOV   EBX,EAX
    AND   EBX,7

    MOV   ESI,EDI
    INC   SI
    MOVZX EDI,WORD PTR IntvMem[ESI * 2]
    MOV   AX,WORD PTR IntvMem[EDI * 2]

    PUSHAD
    MOV   EDX,EAX
    MOV   EAX,EDI
    CALL  Handle_IO_Read
    POPAD

    MOV   AX,WORD PTR IntvMem[EDI * 2]

    ADD   WORD PTR Regs[EBX * 2],AX
    MOV   AL,2
    LAHF
    JO    @L1
    XOR   AL,AL
@L1:
    AND   AH,0C1h
    OR    AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_ADDimp:                               { ADD@  1011mmmddd   (mmm <> 0) }

  Asm
    MOV   WORD PTR StatusWord,0
    INC   WORD PTR Regs[7 * 2]
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    SUB   ESI,ESI
    TEST  BYTE PTR DoubleByte,1
    JNZ   @Double
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    JMP   @L5

@Double:
    MOV   BYTE PTR DoubleByte,0
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    ADD   SI,WORD PTR PostRead[EDI]

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

@L5:

    ADD   SI,WORD PTR PostRead[EDI]
    MOV   WORD PTR Regs[EDI],SI

    ADD   WORD PTR Regs[EBX * 2],CX
    MOV   AL,2
    LAHF
    JO    @L1
    XOR   AL,AL
@L1:
    AND   AH,0C1h
    OR    AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_SUB:                                  { SUB   1100000ddd }

  Asm
    ADD   WORD PTR Regs[7 * 2],2
    MOV   EBX,EAX
    AND   EBX,7

    MOV   ESI,EDI
    INC   SI
    MOVZX EDI,WORD PTR IntvMem[ESI * 2]
    MOV   AX,WORD PTR IntvMem[EDI * 2]

    PUSHAD
    MOV   EDX,EAX
    MOV   EAX,EDI
    CALL  Handle_IO_Read
    POPAD

    MOV   AX,WORD PTR IntvMem[EDI * 2]

    SUB   WORD PTR Regs[EBX * 2],AX
    MOV   AL,3
    LAHF
    JO    @L1
    MOV   AL,1
@L1:
    AND   AH,0C1h
    XOR   AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_SUBimp:                               { SUB@  1100mmmddd   (mmm <> 0) }

  Asm
    MOV   WORD PTR StatusWord,0
    INC   WORD PTR Regs[7 * 2]
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    SUB   ESI,ESI
    TEST  BYTE PTR DoubleByte,1
    JNZ   @L4
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    JMP   @L5
@L4:
    MOV   BYTE PTR DoubleByte,0
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    ADD   SI,WORD PTR PostRead[EDI]

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

@L5:
    ADD   SI,WORD PTR PostRead[EDI]
    MOV   WORD PTR Regs[EDI],SI

    SUB   WORD PTR Regs[EBX * 2],CX
    MOV   AL,3
    LAHF
    JO    @L1
    MOV   AL,1
@L1:
    AND   AH,0C1h
    XOR   AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_CMP:                                  { CMP   1101000ddd }

  Asm
    ADD   WORD PTR Regs[7 * 2],2
    MOV   EBX,EAX
    AND   EBX,7

    MOV   ESI,EDI
    INC   SI
    MOVZX EDI,WORD PTR IntvMem[ESI * 2]
    MOV   AX,WORD PTR IntvMem[EDI * 2]

    PUSHAD
    MOV   EDX,EAX
    MOV   EAX,EDI
    CALL  Handle_IO_Read
    POPAD

    MOV   AX,WORD PTR IntvMem[EDI * 2]

    MOV   DX,WORD PTR Regs[EBX * 2]
    SUB   DX,AX
    MOV   AL,3
    LAHF
    JO    @L1
    MOV   AL,1
@L1:
    AND   AH,0C1h
    XOR   AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_CMPimp:                               { CMP@  1101mmmddd   (mmm <> 0) }

  Asm
    MOV   WORD PTR StatusWord,0
    INC   WORD PTR Regs[7 * 2]
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    SUB   ESI,ESI
    TEST  BYTE PTR DoubleByte,1
    JNZ   @L4
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    JMP   @L5
@L4:
    MOV   BYTE PTR DoubleByte,0
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    ADD   SI,WORD PTR PostRead[EDI]

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

@L5:
    ADD   SI,WORD PTR PostRead[EDI]
    MOV   WORD PTR Regs[EDI],SI

    MOV   DX,WORD PTR Regs[EBX * 2]
    SUB   DX,CX
    MOV   AL,3
    LAHF
    JO    @L1
    MOV   AL,1
@L1:
    AND   AH,0C1h
    XOR   AL,AH
    SHR   AH,4
    OR    AL,AH
    AND   EAX,0Fh
    MOV   WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_AND:                                  { AND   1110000ddd }

  Asm
    ADD   WORD PTR Regs[7 * 2],2
    MOV   EBX,EAX
    AND   EBX,7

    MOV   ESI,EDI
    INC   SI
    MOVZX EDI,WORD PTR IntvMem[ESI * 2]
    MOV   AX,WORD PTR IntvMem[EDI * 2]

    PUSHAD
    MOV   EDX,EAX
    MOV   EAX,EDI
    CALL  Handle_IO_Read
    POPAD

    MOV   AX,WORD PTR IntvMem[EDI * 2]

    AND   WORD PTR Regs[EBX * 2],AX
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_ANDimp:                               { AND@  1110mmmddd   (mmm <> 0) }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    SUB   ESI,ESI
    TEST  BYTE PTR DoubleByte,1
    JNZ   @L4
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    JMP   @L5
@L4:
    MOV   BYTE PTR DoubleByte,0
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    ADD   SI,WORD PTR PostRead[EDI]

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

@L5:
    ADD   SI,WORD PTR PostRead[EDI]
    MOV   WORD PTR Regs[EDI],SI

    AND   WORD PTR Regs[EBX * 2],CX
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_XOR:                                  { XOR   1111000ddd }

  Asm
    ADD   WORD PTR Regs[7 * 2],2
    MOV   EBX,EAX
    AND   EBX,7

    MOV   ESI,EDI
    INC   SI
    MOVZX EDI,WORD PTR IntvMem[ESI * 2]
    MOV   AX,WORD PTR IntvMem[EDI * 2]

    PUSHAD
    MOV   EDX,EAX
    MOV   EAX,EDI
    CALL  Handle_IO_Read
    POPAD

    MOV   AX,WORD PTR IntvMem[EDI * 2]

    XOR   WORD PTR Regs[EBX * 2],AX
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }

_XORimp:                               { XOR@  1111mmmddd   (mmm <> 0) }

  Asm
    INC   WORD PTR Regs[7 * 2]
    MOV   EBX,EAX
    MOV   EDI,EAX
    AND   EBX,7
    SHR   EDI,2
    AND   EDI,0Eh
    SUB   ESI,ESI
    TEST  BYTE PTR DoubleByte,1
    JNZ   @L4
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CX,WORD PTR IntvMem[ESI * 2]

    JMP   @L5
@L4:
    MOV   BYTE PTR DoubleByte,0
    MOVZX ESI,WORD PTR Regs[EDI]
    ADD   SI,WORD PTR PreRead[EDI]

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CL,BYTE PTR IntvMem[ESI * 2]

    ADD   SI,WORD PTR PostRead[EDI]

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

    PUSHAD
    MOV   EAX,ESI
    MOV   EDX,ECX
    CALL  Handle_IO_Read
    POPAD

    MOV   CH,BYTE PTR IntvMem[ESI * 2]

@L5:
    ADD   SI,WORD PTR PostRead[EDI]
    MOV   WORD PTR Regs[EDI],SI

    XOR   WORD PTR Regs[EBX * 2],CX
    MOV   DX,WORD PTR Regs[EBX * 2]
    OR    DX,DX
    LAHF
    SHR   EAX,12
    AND   EAX,0Ch
    AND   WORD PTR StatusWord,3
    OR    WORD PTR StatusWord,AX
    JMP   EmulateLoop
  End; { Asm }
End; // Interpret

Procedure InitGameType;
Var
  I : Integer;
  F : File;

Begin
  FillChar(IntvMem,SizeOf(IntvMem),#0);  { Blank them with nulls }
  FillChar(Mem6502,SizeOf(Mem6502),#255);  { Blank them with nulls }
  FillChar(KBDBuffer,SizeOf(KBDBuffer),#0);  // Clear Keyboard Component screen buffer

  VoiceReady  := True;
  VoicePtr    := Nil;
  VoiceLen    := 0;
  VoicePos    := 0;
  FillChar(VoiceFIFO,SizeOf(VoiceFIFO),#0);
  VoiceFRead  := 0;
  VoiceFWrite := 1;

  If FileExists(ExtractFilePath(Application.ExeName) + Config.ROM6502) Then
  Begin
    IOResult;  // Clear the internal IO error flag
    AssignFile(F,ExtractFilePath(Application.ExeName) + Config.ROM6502);
    Reset(F,1);
    BlockRead(F,Mem6502[$C000],FileSize(F));
    CloseFile(F);
  End;

  For I := 0    To 65535 Do IntvMem[I] := $FC00;
  For I := $200 To $2EF  Do IntvMem[I] := 0;
  For I := 0    To $32   Do IntvMem[I] := 0;
  For I := $33  To $FF   Do IntvMem[I] := $FFFF;

  FillChar(Regs,SizeOf(Regs),#0);
  DoubleByte           := False;
  StatusWord           := 0;
  Interrupts           := 0;
  InsCount             := 0;
  SlowCount            := 0;
  LeftDiscPos          := 0;
  RightDiscPos         := 0;
  LeftMouseDown        := False;
  RightMouseDown       := False;
  ScrollKBDC           := 0;
End; // InitGameType

Procedure TfrmMain.UpdateButtonStatuses;
Begin
End; // TfrmMain.UpdateButtonStatuses

Procedure ExitFullScreenMode;
Begin
  If frmMain.UseDirectDraw And frmMain.DDFullScreen Then
  Begin
    frmMain.DXDraw1.Finalize;

    frmMain.CoolBar1.Visible  := True;    // Show and enable the coolbar
    frmMain.CoolBar1.Enabled  := True;
    frmMain.Menu              := Menu;    // Get the main menu back
    frmMain.RestoreWindow;
    frmMain.DXDraw1.Cursor  := crDefault;
    frmMain.BorderStyle     := bsSizeable;
    frmMain.DXDraw1.Options := frmMain.DXDraw1.Options - [doFullScreen];
    frmMain.DXDraw1.Initialize;

    If frmMain.TVScreen Then
    Begin
      frmMain.DXDraw1.Display.Width    := 320;
      frmMain.DXDraw1.Display.Height   := 200;
      frmMain.DXDraw1.Display.BitCount := 8;
      frmMain.DXDraw1.SurfaceWidth  := frmMain.DXDraw1.Display.Width;
      frmMain.DXDraw1.SurfaceHeight := frmMain.DXDraw1.Display.Height;
      frmMain.DXDraw1.Width         := frmMain.DXDraw1.Display.Width;
      frmMain.DXDraw1.Height        := frmMain.DXDraw1.Display.Height;
    End;
  End;
End; // ExitFullScreenMode

Procedure TfrmMain.SetOverlaySize;
Var X,Y: Integer;
Begin
  imgOverlayScan.Left := 320 * Zoom + 8;
  X := ClientWidth  - imgOverlayScan.Left;
  Y := ClientHeight - imgOverlayScan.Top;
  If X > Y * (441 / 706) Then
  Begin
    imgOverlayScan.Height := Y;
    imgOverlayScan.Width  := Round(Y * 441 / 706);
  End
  Else
  Begin
    imgOverlayScan.Width  := X;
    imgOverlayScan.Height := Round(X * 706 / 441);
  End;
End; // TfrmMain.SetOverlaySize

Procedure TfrmMain.OpenImageFile(Const Filename: String; Img: TImage);
Begin
  Try
    Img.Picture.LoadFromFile(Filename);
  Except
    On EInvalidGraphic Do Img.Picture.Graphic := Nil;
  End;
End; // TfrmMain.OpenImageFile

Procedure TfrmMain.ClearImage(Var Img: TImage; Stretch: Boolean);
Var R: TRect;
Begin
  R.Left   := Img.Left;
  R.Top    := Img.Top;
  R.Right  := Img.Width;
  R.Bottom := Img.Height;
  Img.Free;
  Img := TImage.Create(Self);
  Img.Parent      := Self;
  Img.Left        := R.Left;
  Img.Top         := R.Top;
  Img.Width       := R.Right;
  Img.Height      := R.Bottom;
  Img.Stretch     := Stretch;
  Img.Visible     := True;
  Img.Enabled     := True;
  Img.AutoSize    := False;
  Img.Transparent := False;
End; // TfrmMain.ClearImage

Procedure TfrmMain.LoadImages(FileName: String);
Var
  St : String;
  I  : Integer;
  OS : String;

begin
  ClearImage(imgOverlayScan,True);
  St    := FileName;
//  I := Pos('.',St);
  I := Length(St);
  While (St[I] <> '.') And (I > 0) Do Dec(I);
  If I <> 0 Then St := Copy(St,1,I - 1);
  OS := Config.OverlayScanPath;
  If (OS <> '') And Not (OS[Length(OS)] In [':','\']) Then OS := OS + '\';
  If FileExists(OS + St + '.BMP') Then OpenImageFile(OS + St + '.BMP',imgOverlayScan) Else
   If FileExists(OS + St + '.JPG') Then OpenImageFile(OS + St + '.JPG',imgOverlayScan);
end; // TfrmMain.LoadImages

Procedure Emulate;
Var
  B         : Boolean;
  R         : TRect;
  Ctrl      : TControl;

  Procedure ExtractWord(Var St: String; Blaster: String; I: Word);
  Var Ch: Char;
  Begin
    St := '';
    Repeat
      Ch := Blaster[I];
      Inc(I);
      If Ch <> ' ' Then St := St + Ch;
    Until (Ch = ' ') Or (I > Length(Blaster));
  End; { ExtractWord }

Begin
//  frmMain.UseDirectDraw := False;



  frmMain.mnuFile.Enabled       := False;
  frmMain.mnuStart.Enabled      := False;
  frmMain.mnuInterfaces.Enabled := False;
//  frmMain.mnuVideo.Enabled      := False;

  frmMain.mnuFullScreen.Enabled := False;
  frmMain.mnuTVScreen.Enabled   := False;
  frmMain.mnuZoom.Enabled       := False;

  frmMain.btnOpen.Enabled       := False;
  frmMain.btnStart.Enabled      := False;
  frmMain.btnProfile.Enabled    := False;
  frmMain.mnuProfile.Enabled    := False;
  frmMain.btnStop.Enabled       := True;
  frmMain.mnuStop.Enabled       := True;
  frmMain.btnReset.Enabled      := True;
  frmMain.btnRecord.Enabled     := True;

  frmMain.UpdateButtonStatuses;

  FillChar(Key,SizeOf(Key),#0);

  Debugger := frmMain.UseDebugger1.Checked;

  // Set the image overlay size

  frmMain.SetOverlaySize;
  frmMain.LoadImages(ExtractFileName(frmMain.ROMFileName));

  // We must change the border style BEFORE initializing sound, or we end up with no sound

  If frmMain.UseDirectDraw And frmMain.DDFullScreen Then
   frmMain.BorderStyle := bsNone;

  // If zooming to full but still in windowed mode, see if we have to hide stuff

  If (frmMain.Zoom = 0) And Config.HideAllOnZoomFull And Not frmMain.DDFullScreen Then
  Begin
    frmMain.StoreWindow;
    frmMain.BorderStyle       := bsNone;
    frmMain.CoolBar1.Visible  := False;          // Hide and disable the coolbar
    frmMain.CoolBar1.Enabled  := False;
    Menu                      := frmMain.Menu;   // Save the main menu
    frmMain.Menu              := Nil;            // Get rid of it for now
  End;

  If Config.DoSound Then
  Begin
    SoundQueue_In  := 0;
    SoundQueue_Out := 0;
    SoundQueue_Num := 0;

    If Cart = ctECS
     Then InitSound(PlayBack_Freq,2,ResetSound)
     Else InitSound(PlayBack_Freq,1,ResetSound);
{    If Debugger Then }ResetSound := False;


//    BufPlaying := 0;
//    BufToFill  := 0;
//    FillChar(SmallSoundBuf,SizeOf(SmallSoundBuf),#0);

    // Empty the sound buffer

    frmMain.Secondary.Lock(0,SndBufSize,SoundBufPtr,SoundBufSize,SoundBufPtr2,SoundBufSize2,DSBLOCK_ENTIREBUFFER);
    FillChar(SoundBufPtr^,SoundBufSize,#0);
    frmMain.Secondary.Unlock(SoundBufPtr,SoundBufSize,SoundBufPtr2,SoundBufSize2);

    // Start the DirectSound buffers playing

    frmMain.DXSound1.Primary.Play(DSBPLAY_LOOPING);
    frmMain.Secondary.Play(DSBPLAY_LOOPING);
  End;

  Time0   := GetTickCount;
  TimeErr := 0;

  NewPalette;

      frmMain.SetImageSizes(True);

      InterfaceHandler.ActivateInterfaces;

      // Blank the window area

      If frmMain.UseDirectDraw Then
      Begin
        If Not frmMain.DDFullScreen Then
        Begin
          R.Left   := 0;
          R.Top    := 0;
          R.Right  := frmMain.DXDraw1.Surface.Width;
          R.Bottom := frmMain.DXDraw1.Surface.Height;
          frmMain.DXDraw1.Surface.Canvas.Brush.Color := clBlack;
//          frmMain.DXDraw1.Surface.Canvas.FillRect(R);  // Causes MAJOR problems!
          If UsingKaillera Then
          Begin
            R.Left   := 0;
            R.Top    := 0;
            R.Right  := frmKaillera.DXDraw1.Surface.Width;
            R.Bottom := frmKaillera.DXDraw1.Surface.Height;
            frmKaillera.DXDraw1.Surface.Canvas.Brush.Color := clBlack;
            frmKaillera.DXDraw1.Surface.Canvas.FillRect(R);
          End;
        End;
      End
      Else
      Begin
        R.Left   := 0;
        R.Top    := 0;
        R.Right  := frmMain.Image1.Width;
        R.Bottom := frmMain.Image1.Height;
        frmMain.Image1.Canvas.Brush.Color := clBlack;
//        frmMain.Image1.Canvas.FillRect(R);             // Causes MAJOR problems!
      End;
      FillChar(ScnBuf,SizeOf(ScnBuf),#0);
      FillChar(ScnBuf2,SizeOf(ScnBuf2),#0);
      FillChar(DebugScnBuf,SizeOf(DebugScnBuf),#0);

      Menu := Nil;
      If frmMain.UseDirectDraw And frmMain.DDFullScreen Then
      Begin
        Application.OnDeactivate := frmMain.DeactivateApp;
        frmMain.DXDraw1.Options  := frmMain.DXDraw1.Options + [doAllowModeX];
        If frmMain.TVScreen Then
        Begin
          frmMain.DXDraw1.Display.Width    := 640;
          frmMain.DXDraw1.Display.Height   := 400;
          frmMain.DXDraw1.Display.BitCount := 8;
        End
        Else
        Begin
          frmMain.DXDraw1.Display.Width    := 320;
          frmMain.DXDraw1.Display.Height   := 200;
          frmMain.DXDraw1.Display.BitCount := 8;
        End;

        // Match the display dimensions

        frmMain.DXDraw1.SurfaceWidth  := frmMain.DXDraw1.Display.Width;
        frmMain.DXDraw1.SurfaceHeight := frmMain.DXDraw1.Display.Height;
        frmMain.DXDraw1.Width         := frmMain.DXDraw1.Display.Width;
        frmMain.DXDraw1.Height        := frmMain.DXDraw1.Display.Height;

        // Save the form size and position

        frmMain.DXDraw1.Finalize;
        frmMain.StoreWindow;

        // Ready the form for fullscreen mode

        frmMain.Top               := 0;              // Move the form to the corner
        frmMain.Left              := 0;
        frmMain.CoolBar1.Visible  := False;          // Hide and disablr the coolbar
        frmMain.CoolBar1.Enabled  := False;
        Menu                      := frmMain.Menu;   // Save the main menu
        frmMain.Menu              := Nil;            // Get rid of it for now

        // Ditch the mouse cursor and switch to fullscreen mode

        frmMain.DXDraw1.Cursor  := crNone;
        frmMain.DXDraw1.Options := frmMain.DXDraw1.Options + [doFullScreen];
        frmMain.DXDraw1.Initialize;

        // Blank the display area

        FillChar(ScnBuf,SizeOf(ScnBuf),#0);
        FillChar(ScnBuf2,SizeOf(ScnBuf2),#0);
        R.Left   := 0;
        R.Top    := 0;
        R.Right  := frmMain.DXDraw1.Display.Width;//frmMain.SWidth;
        R.Bottom := frmMain.DXDraw1.Display.Height;//frmMain.SHeight;
        FillChar(SDesc,SizeOf(SDesc),#0);
        SDesc.dwSize := SizeOf(SDesc);
        frmMain.DXDraw1.Primary.Lock(R,SDesc);

        // Make the form fill the full screen

        frmMain.Width  := SDesc.lPitch;      // Match the screen width
        frmMain.Height := 768;               // Should never be bigger than this

        // Fill the entire surface with black

        FillChar(SDesc.lpSurface^,Integer(SDesc.lPitch) * Integer(SDesc.dwHeight),0);
        frmMain.DXDraw1.Primary.Unlock(SDesc.lpSurface);
      End;

      // Hide the mouse cursor

      If frmMain.UseDirectDraw And frmMain.DDFullScreen Then ShowCursor(False);

      Ctrl := GetCaptureControl;
      SetCaptureControl(frmMain);

      // Final emulator setup

      Importing   := False;         { Not importing ECS program         }
      ECSSave     := False;         { Not saving program to cassette    }
      ECSLoad     := False;         { Not loading program from cassette }
      ECSLoadable := False;         { Can load program from cassette    }
      DoSlowDown  := True;          { Slow down PC if necessary         }
      ECSKey      := False;         { No key currently being typed in   }
      ImportKey   := ECSInvalidKey; { No key currently being typed in   }
      ImportShift := False;         { ECS Shift key not needed          }
      ECSDelay    := 0;             { Reset auto-repeat delay           }
      ECSPCKey    := 0;             { No PC keyboard key being pressed  }
      Move(Key,ECSKeyList,SizeOf(ECSKeyList));

      ReadMem  := False;
      WriteMem := False;
      RWMemLoc := 0;

      ReadMem6502  := False;
      WriteMem6502 := False;
      RWMemLoc6502 := 0;

      OldPos    := -1;
      LastWrite := 0;

//      LogOutput := True;
      LogOutput := False;

      ResetClock;

      Emulating := 2;
      Interpret;
      Emulating := 0;

      SetCaptureControl(Ctrl);

      ShowCursor(True);                  // Show the mouse cursor

      // Stop the sound

      If Config.DoSound Then
      Begin
        frmMain.Secondary.Stop;
        frmMain.DXSound1.Primary.Stop;
      End;

      // Switch back to windowed mode

      ExitFullScreenMode;

      // If zooming to full but still in windowed mode, see if we have to unhide stuff

      If (frmMain.Zoom = 0) And Config.HideAllOnZoomFull And Not frmMain.DDFullScreen Then
      Begin
        frmMain.CoolBar1.Visible  := True;    // Show and enable the coolbar
        frmMain.CoolBar1.Enabled  := True;
        frmMain.Menu              := frmMain.MainMenu1;    // Get the main menu back
        frmMain.RestoreWindow;
        frmMain.BorderStyle       := bsSizeable;
        frmMain.Refresh;
      End;

      frmMain.SetImageSizes(False);

      InterfaceHandler.DeactivateInterfaces;

      // Temporarily turn the full screen flag off and re-display

      B := frmMain.DDFullScreen;
      frmMain.DDFullScreen := False;
      frmMain.Display;
      frmMain.DDFullScreen := B;

//      frmMain.Image1.Picture.Bitmap.ReleasePalette;
//      DeleteObject(frmMain.Palette);

//      Key[kESC] := 0;

(*
    Until (Not Debugger){ Or Not
     Debug(@AtariSeg,Start,P,A,X,Y,S,
           ROMBackup,Config.GameResolution,HaltLine,BreakExpColl,Game)};
*)
    { Get rid of the break expresion PCollection }

    Debugger := frmMain.UseDebugger1.Checked;

    frmMain.UpdateDebugger;

    If BreakExpColl <> Nil Then
    Begin
      BreakExpColl.Free;
      BreakExpColl := Nil;
    End;

  frmMain.mnuFile.Enabled       := True;
  frmMain.mnuStart.Enabled      := True;
  frmMain.mnuInterfaces.Enabled := True;
//  frmMain.mnuVideo.Enabled      := True;

  frmMain.mnuFullScreen.Enabled := True;
  frmMain.mnuTVScreen.Enabled   := True;
  frmMain.mnuZoom.Enabled       := True;

  frmMain.btnOpen.Enabled       := True;
  frmMain.btnStart.Enabled      := True;
  frmMain.btnProfile.Enabled    := True;
  frmMain.mnuProfile.Enabled    := True;
  frmMain.btnStop.Enabled       := False;
  frmMain.mnuStop.Enabled       := False;
  frmMain.btnReset.Enabled      := False;
  frmMain.btnRecord.Enabled     := False;
End; { Emulate }

// -------------------------------------------------------------

procedure TfrmMain.Exit1Click(Sender: TObject);
begin
  Close;
end;

procedure TfrmMain.btnOpenClick(Sender: TObject);
begin
  If Config.AlternateGameMenu
   Then frmAltMenu.ShowModal
   Else frmMenu.ShowModal;
  LoadGame(MenuInfo.FileName,MenuInfo.ZipFileName,MenuInfo.Title);
end;

procedure TfrmMain.Rebuildgameprofile1Click(Sender: TObject);
Begin
  MakeProfile(ExtractFilePath(Application.ExeName));
end;

procedure TfrmMain.Configure1Click(Sender: TObject);
begin
  InterfaceHandler.DeactivateInterfaces;
  frmConfigure.ShowModal;
  frmMenu.SaveConfiguration(Config,Base.Version);
  InterfaceHandler.ActivateInterfaces;
end;

procedure TfrmMain.Mouse1Click(Sender: TObject);
Var Intf: TInterface;
begin
  If InterfaceHandler.Stack.Count > 0 Then
  Begin
    InterfaceHandler.DeactivateInterfaces;
    Intf := TInterface(InterfaceHandler.Stack.Objects[0]);
    Intf.Enabled := Not Intf.Enabled;
    Mouse1.Checked := Intf.Enabled;
    InterfaceHandler.ActivateInterfaces;
    frmMenu.SaveConfiguration(Config,Base.Version);
  End;
end;

procedure TfrmMain.Keyboard2Click(Sender: TObject);
Var Intf: TInterface;
begin
  If InterfaceHandler.Stack.Count > 1 Then
  Begin
    InterfaceHandler.DeactivateInterfaces;
    Intf := TInterface(InterfaceHandler.Stack.Objects[1]);
    Intf.Enabled := Not Intf.Enabled;
    Keyboard2.Checked := Intf.Enabled;
    InterfaceHandler.ActivateInterfaces;
    frmMenu.SaveConfiguration(Config,Base.Version);
  End;
end;

procedure TfrmMain.Controller2Click(Sender: TObject);
Var Intf: TInterface;
begin
  If InterfaceHandler.Stack.Count > 2 Then
  Begin
    InterfaceHandler.DeactivateInterfaces;
    Intf := TInterface(InterfaceHandler.Stack.Objects[2]);
    Intf.Enabled := Not Intf.Enabled;
    Controller2.Checked := Intf.Enabled;
    InterfaceHandler.ActivateInterfaces;
    frmMenu.SaveConfiguration(Config,Base.Version);
  End;
end;

procedure TfrmMain.Usedebugger1Click(Sender: TObject);
begin
  Debugger := Not Debugger;
  UseDebugger1.Checked := Debugger;
  If Debugger And Not frmDebugger.Visible Then
  Begin
    frmDebugger.Show;
  End;
  If Not Debugger Then ShowObjectLocs := False;
end;

procedure TfrmMain.Preferences1Click(Sender: TObject);
begin
  frmPrefs.ShowModal;
end;

procedure TfrmMain.FormShow(Sender: TObject);
Var
  I    : Integer;
  Intf : TInterface;
  Ver  : Array[0..15] Of Char;

begin
  // Every time we change the border style FormShow gets called. We only want
  // it to be called once.

  If Not Showed Then
  Begin
    UsingKaillera := False;
    kailleraGetVersion(PChar(@Ver[0]));
    frmMenu.InitConfig(Config,Base.Version);
    Caption := 'IntvWin ' + Base.Version;
    Usedebugger1.Checked := Debugger;
    SetImageSizes(False);
    btnStop.Enabled              := False;
    btnRecord.Enabled            := False;
    mnuStop.Enabled              := False;
    mnuDisassemble.Enabled       := False;
    mnuScreenShot.Enabled        := False;
    btnStart.Enabled             := False;
    btnResetCart.Enabled         := False;
    mnuTVScreen.Enabled          := DDFullScreen;
    mnuFullScreen.Checked        := DDFullScreen;
    mnuTVScreen.Checked          := TVScreen;
    frmMain.btnReset.Enabled     := False;
    frmMain.btnProfile.Enabled   := False;
    frmMain.btnManual.Enabled    := False;
    frmMain.mnuProfile.Enabled   := False;

    // See which interfaces are enabled

    For I := 0 To InterfaceHandler.Stack.Count - 1 Do
    Begin
      Intf := TInterface(InterfaceHandler.Stack.Objects[I]);
      If UpperCase(Intf.Name) = 'MOUSE'    Then Mouse1.Checked      := Intf.Enabled;
      If UpperCase(Intf.Name) = 'KEYBOARD' Then Keyboard2.Checked   := Intf.Enabled;
      If UpperCase(Intf.Name) = 'DIGITAL'  Then Controller2.Checked := Intf.Enabled;
      If UpperCase(Intf.Name) = 'INTV2PC'  Then Intv2PC1.Checked    := Intf.Enabled;
    End; // For I

    // Set the zoom mode

    Case Config.ZoomMode Of
      0: mnuZoomFullClick(Self);
      2: mnuZoom2xClick(Self);
      3: mnuZoom3xClick(Self);
      4: mnuZoom4xClick(Self);
    Else
      mnuZoom1xClick(Self);
    End; // Case

    // Grow the window if necessary to accommodate the play window

    If DXDraw1.Width > ClientWidth Then ClientWidth := DXDraw1.Width;
    If DXDraw1.Top + DXDraw1.Height > ClientHeight Then ClientHeight := DXDraw1.Top + DXDraw1.Height;

    // Initialize the sound buffer

    DXSound1.Driver := TDXSound.Drivers[0].GUID;
    DXSound1.Initialize;
    Showed := True;
  End;  
end;

procedure TfrmMain.Open1Click(Sender: TObject);
begin
  If Config.AlternateGameMenu
   Then frmAltMenu.ShowModal
   Else frmMenu.ShowModal;
  LoadGame(MenuInfo.FileName,MenuInfo.ZipFileName,MenuInfo.Title);
end;

procedure TfrmMain.btnStartClick(Sender: TObject);
begin
  If Debugger Then frmDebugger.btnGoClick(Self) Else Emulate;
end;

procedure TfrmMain.btnStopClick(Sender: TObject);
begin
  If Emulating > 0 Then NewList := True;
  Emulating  := 0;
end;

Procedure TfrmMain.Display;
Var
  P     : Pointer;
  P1    : Pointer;
  DDFS  : Boolean;
  WAdd  : LongInt;
  R     : TRect;
  I,J,K : Integer;

  Procedure ShowObjects;
  Var
    I,J : Integer;
    X1  : Integer;
    Y1  : Integer;
    X2  : Integer;
    Y2  : Integer;

    Procedure PutPixel(X,Y,Color: Integer);
    Begin
      If (X >= 0) And (X <= 319) And (Y >= 0) And (Y <= 199) Then ScnBuf[Y * 320 + X] := Color;
    End; // PutPixel

  Begin
    For I := 0 To 7 Do
    Begin
      X1 := SpriteX[I] * 2;
      Y1 := SpriteY[I] + VertOffset;
      If IntvMem[I] And $400 <> 0
       Then X2 := X1 + 32
       Else X2 := X1 + 16;
      Y2 := 8;
      If IntvMem[I + 8] And $0080 <> 0 Then Y2 := 16;
      If IntvMem[I + 8] And $0100 <> 0 Then Y2 := Y2 * 2;
      If IntvMem[I + 8] And $0200 <> 0 Then Y2 := Y2 * 4;
      Inc(Y2,Y1);


      For J := X1 To X2 - 1 Do PutPixel(J,Y1,7);
      For J := X1 To X2 - 1 Do PutPixel(J,Y2 - 1,7);
      For J := Y1 To Y2 - 1 Do PutPixel(X1,J,7);
      For J := Y1 To Y2 - 1 Do PutPixel(X2 - 1,J,7);
    End; // For I
  End; // ShowObjectLoc

Begin
  Inc(Frames);
  DDFS := DDFullScreen And UseDirectDraw;
  P    := ScnPtr2;

  // Show object locations in black

  If ShowObjectLocs Then ShowObjects;

  // Display game window

  If DDFS Then
  Begin
    R.Left   := 0;
    R.Top    := 0;
    R.Right  := frmMain.DXDraw1.Display.Width;
    R.Bottom := frmMain.DXDraw1.Display.Height;
    FillChar(SDesc,SizeOf(SDesc),#0);
    SDesc.dwSize := SizeOf(SDesc);
    frmMain.DXDraw1.Primary.Lock(R,SDesc);
    P := SDesc.lpSurface;
    If TVScreen Then
    Begin

      WAdd := SDesc.lPitch - 640;

      Asm
        PUSH  ESI
        PUSH  EDI
        PUSH  EBX
        PUSH  EBP
        MOV   EBX,DWORD PTR WAdd
        MOV   ESI,DWORD PTR ScnPtr
        MOV   EDI,DWORD PTR P

        MOV   EBP,200


@LTV5:
        PUSH  EBP
        MOV   EBP,80

        // Move a line of dot data

@LTV4:
        MOV   EAX,DWORD PTR [ESI]
        ADD   ESI,4
        MOV   ECX,EAX

        MOV   EDX,EAX
        MOV   AH,AL
        MOV   DL,DH
        SHL   EDX,16
        MOV   DX,AX
        MOV   DWORD PTR [EDI],EDX
        SHR   ECX,16
        MOV   EDX,ECX
        MOV   CH,CL
        MOV   DL,DH
        SHL   EDX,16
        MOV   DX,CX
        MOV   DWORD PTR [EDI + 4],EDX

        ADD   EDI,8

        DEC   EBP
        JNZ   @LTV4
        ADD   EDI,EBX                 // Added JD 3-30-00

        // Create a blank line

        SUB   EAX,EAX
        MOV   ECX,160
@LTV2:
        MOV   DWORD PTR [EDI],EAX
        ADD   EDI,4
        DEC   ECX
        JNZ   @LTV2
        ADD   EDI,EBX                 // Added JD 3-30-00

        // Move to the next line

        POP   EBP
        DEC   EBP
        JNZ   @LTV5

        POP   EBP
        POP   EBX
        POP   EDI
        POP   ESI
      End; { Asm }
    End
    Else
    Begin
      WAdd := SDesc.lPitch - 320;
      J  := 0;
      P1 := P;
      For I := 0 To 199 Do
      Begin
        Move(ScnBuf[J],P1^,320);
        Inc(J,320);
        Inc(LongInt(P1),SDesc.lPitch);
      End; // For I
    End;
    frmMain.DXDraw1.Primary.Unlock(SDesc.lpSurface);
  End
  Else
  Begin
    J := 0;
    K := 199 * 320;
    For I := 0 To 199 Do
    Begin
      Move(ScnBuf[J],ScnBuf2[K],320);
      Inc(J,320);
      Dec(K,320);
    End; // For I
  End;
  If UseDirectDraw Then
  Begin
    If DDFullScreen Then
    Begin
      { Do nothing; data was written directly }

      If Config.ShowFPS Then
      Begin
        DXDraw1.Primary.Canvas.Font.Color  := clLime;
        DXDraw1.Primary.Canvas.Font.Name   := 'Arial';
        DXDraw1.Primary.Canvas.Font.Size   := 12;
        DXDraw1.Primary.Canvas.Brush.Style := bsClear;
        DXDraw1.Primary.Canvas.TextOut(10,10,Format('%f',[FPS]) + ' fps');
        DXDraw1.Primary.Canvas.Release;
      End;

    End
    Else
    Begin
      If DXDraw1.CanDraw Then
      Begin
        If UsingKaillera Then
        Begin
          SetDIBitsToDevice(frmKaillera.DXDraw1.Surface.Canvas.Handle,0,0,320,200,0,0,0,ScnBufHeight,ScnPtr2,BI1.B,DIB_RGB_COLORS);

          If Config.ShowFPS Then
          Begin
            frmKaillera.DXDraw1.Surface.Canvas.Font.Color  := clLime;
            frmKaillera.DXDraw1.Surface.Canvas.Font.Name   := 'Arial';
            frmKaillera.DXDraw1.Surface.Canvas.Font.Size   := 12;
            frmKaillera.DXDraw1.Surface.Canvas.Brush.Style := bsClear;
            frmKaillera.DXDraw1.Surface.Canvas.TextOut(10,10,Format('%f',[FPS]) + ' fps');
          End;

          frmKaillera.DXDraw1.Surface.Canvas.Release;
          frmKaillera.DXDraw1.Flip;
        End
        Else
        Begin
          SetDIBitsToDevice(DXDraw1.Surface.Canvas.Handle,0,0,320,200,0,0,0,ScnBufHeight,ScnPtr2,BI1.B,DIB_RGB_COLORS);

          If Config.ShowFPS Then
          Begin
            DXDraw1.Surface.Canvas.Font.Color  := clLime;
            DXDraw1.Surface.Canvas.Font.Name   := 'Arial';
            DXDraw1.Surface.Canvas.Font.Size   := 12;
            DXDraw1.Surface.Canvas.Brush.Style := bsClear;
            DXDraw1.Surface.Canvas.TextOut(10,10,Format('%f',[FPS]) + ' fps');
          End;

          DXDraw1.Surface.Canvas.Release;
          DXDraw1.Flip;
        End;
      End;
    End;
  End
  Else SetDIBitsToDevice(Image1.Canvas.Handle,0,0,ScnBufWidth,ScnBufHeight,0,0,0,SHeight,ScnPtr2,BI1.B,DIB_RGB_COLORS);
  If Image1.Visible Then Image1.Repaint;
  Application.ProcessMessages;
End;

procedure TfrmMain.FormCreate(Sender: TObject);
Var
  B  : Boolean;
  St : String;

  Procedure ErrorFileNotFound(Const Path,St: String);
  Begin
    ShowMessage('Sorry, but IntvWin was unable to find file ' + St + ' in' + #13#10 +
                'directory ' + ExpandFileName(Path) + #13#10 +
                #13#10 +
                'You should make sure that IntvWin was run from the' + #13#10 +
                'right directory and that ' + St + ' is present.');
  End; { ErrorFileNotFound }

begin
  St := 'IntvWin ' + Base.Version;
  KInfo.AppName               := StrAlloc(Length(St) + 1);
  StrPCopy(KInfo.AppName,St);
  KInfo.GameList              := ConcatAllNames;
  KInfo.GameCallBack          := @KailleraGameCallBack;
  KInfo.ChatReceivedCallBack  := @KailleraChatReceivedCallBack;
  KInfo.ClientDroppedCallBack := @KailleraClientDroppedCallBack;
  KInfo.MoreInfoCallBack      := @KailleraMoreInfoCallBack;
  KailleraSetInfo(KInfo);

  Showed         := False;
  Debugger       := False;
  UseDirectDraw  := True;  //False;
  DDFullScreen   := False;
  TVScreen       := False;
  SWidth         := ScnBufWidth;
  SHeight        := ScnBufHeight;
  Zoom           := 1;
  ShowObjectLocs := False;

  // Determine if MMX is available

  UseMMX := False;
  B      := False;

  { Find out if this processor supports MMX }

  Asm
//    CLI

    { Find out if the CPUID instruction is available }

    PUSHAD
    PUSHFD
    POP   ECX                 { Get original EFLAGS }
    MOV   EAX,ECX
    XOR   EAX,200000h         { Flip ID bit in EFLAGS }
    PUSH  EAX                 { Save new EFLAGS value on stack }
    POPFD                     { Replace current EFLAGS value }
    PUSHFD                    { Get new EFLAGS }
    POP   EAX                 { Store new EFLAGS in EAX }
    XOR   EAX,ECX             { Can't toggle ID bit, }
    JE    @NoMMX              { Processor = 80486 or lower }

    { See if MMX is available }

    MOV   EAX,1               { Get feature flag info }
    DB    0Fh,0A2h            { CPUID }
    TEST  EDX,0800000h        { See if MMX is available }
    JZ    @NoMMX
    MOV   B,1

@NoMMX:
    PUSH  ECX
    POPFD
    POPAD
//    STI
  End; { Asm }
  UseMMX := B;
end;

procedure TfrmMain.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  LoadKeyArray;
end;

procedure TfrmMain.FormKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  LoadKeyArray;
end;

procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  If Emulating > 0 Then NewList := True;
  Emulating  := 0;
  Zoom := 1;
end;

Procedure TfrmMain.SetImageSizes(Starting: Boolean);
Begin
  LocalBufSize := 160 * 200;
  If Zoom > 0 Then
  Begin
    SWidth  := 320 * Zoom;
    SHeight := 200 * Zoom;
  End
  Else
  Begin
    If ClientWidth > 0 Then SWidth  := ClientWidth;
    If ClientHeight > 0 Then
    Begin
      If Config.HideAllOnZoomFull And Starting And Not DDFullScreen
       Then SHeight := ClientHeight
       Else SHeight := ClientHeight - (CoolBar1.Top + CoolBar1.Height);
    End;
  End;
  If UseDirectDraw Then
  Begin
    If Not DDFullScreen Then
    Begin
      DXDraw1.Width         := SWidth;
      DXDraw1.Height        := SHeight;
      DXDraw1.SurfaceWidth  := 320;//SWidth;
      DXDraw1.SurfaceHeight := 200;//SHeight;

      frmKaillera.DXDraw1.Width         := 320;
      frmKaillera.DXDraw1.Height        := 200;
      frmKaillera.DXDraw1.SurfaceWidth  := 320;
      frmKaillera.DXDraw1.SurfaceHeight := 200;
    End;
  End
  Else
  Begin
    Image1.Width  := SWidth;
    Image1.Height := SHeight;
  End;
  Image1.Visible  := Not UseDirectDraw;
  DXDraw1.Visible := UseDirectDraw;
  Image1.Left     := 0;
  DXDraw1.Left    := 0;
  If (Zoom = 0) And Config.HideAllOnZoomFull And Starting And Not DDFullScreen Then
  Begin
    Image1.Top  := 0;
    DXDraw1.Top := 0;
  End
  Else
  Begin
    Image1.Top  := CoolBar1.Top + CoolBar1.Height;
    DXDraw1.Top := CoolBar1.Top + CoolBar1.Height;
  End;
End; // TfrmMain.SetImageSizes

procedure TfrmMain.mnuFullScreenClick(Sender: TObject);
begin
  mnuFullScreen.Checked := Not mnuFullScreen.Checked;
  If UseDirectDraw Then DDFullScreen := mnuFullScreen.Checked;
  mnuTVScreen.Enabled := DDFullScreen;
  frmMenu.SaveConfiguration(Config,Base.Version);
end;

procedure TfrmMain.mnuStartClick(Sender: TObject);
begin
  Emulate;
end;

procedure TfrmMain.mnuStopClick(Sender: TObject);
begin
  If Emulating > 0 Then NewList := True;
  Emulating  := 0;
end;

procedure TfrmMain.mnuZoom1xClick(Sender: TObject);
begin
  Config.ZoomMode   := 1;
  Zoom              := 1;
  mnuZoom1x.Checked := True;
  SetImageSizes(False);
end;

procedure TfrmMain.mnuZoom2xClick(Sender: TObject);
begin
  Config.ZoomMode   := 2;
  Zoom              := 2;
  mnuZoom2x.Checked := True;
  SetImageSizes(False);
end;

procedure TfrmMain.mnuZoom4xClick(Sender: TObject);
begin
  Config.ZoomMode   := 4;
  Zoom              := 4;
  mnuZoom4x.Checked := True;
  SetImageSizes(False);
end;

Procedure TfrmMain.LoadGame(FileName,ZipFileName,Title: String);
Var
  F          : File;
  I,J        : Integer;
  GROM       : Packed Array[0..2047] Of Byte;
  MemStr     : String;
  Game_Start : Word;
  Words      : Integer;

Begin
  If FileName <> '' Then
  Begin
    ROMFileName := UpperCase(FileName);
    ZipFileName := UpperCase(ZipFileName);

    If (ROMFileName <> '') And
       (FileExists(ROMFileName) Or
        ((ZipFileName <> '') And FileExists(ZipFileName)))  Then
    Begin
      IntPending     := False;
      IntPending6502 := False;
      If Emulating = 0 Then
      Begin
        btnStart.Enabled           := True;
        btnStop.Enabled            := False;
        btnRecord.Enabled          := False;
        mnuDisassemble.Enabled     := True;
        mnuScreenShot.Enabled      := True;
        frmMain.btnProfile.Enabled := True;
        frmMain.btnManual.Enabled  := True;
        frmMain.mnuProfile.Enabled := True;
      End;
      btnResetCart.Enabled   := True;
      Caption := 'IntvWin ' + Base.Version + ' - ' + Title + ' [' +
       ExtractFileName(FileName) + ']';
      InitGameType;

      { Read the exec }

      IOResult;  // Clear the internal IO error flag
      If FileExists(ExtractFilePath(Application.ExeName) + Config.Exec) Then
      Begin
        AssignFile(F,ExtractFilePath(Application.ExeName) + Config.Exec);
        Reset(F,1);
        For I := 0 To (FileSize(F) Div 2) - 1 Do
        Begin
          BlockRead(F,IntvMem[EXEC_Loc + I],2);
          IntvMem[EXEC_Loc + I] := (IntvMem[EXEC_Loc + I] Shr 8) + Word(IntvMem[EXEC_Loc + I] Shl 8);
        End; { For I }
        CloseFile(F);
      End;

      { Read graphics ROM }

      IOResult;  // Clear the internal IO error flag
      If FileExists(ExtractFilePath(Application.ExeName) + Config.GROM) Then
      Begin
        AssignFile(F,ExtractFilePath(Application.ExeName) + Config.GROM);
        Reset(F,1);
        BlockRead(F,GROM,FileSize(F));
        CloseFile(F);
      End;
      For I := 0 To 2047 Do IntvMem[GROM_Loc + I] := GROM[I];

      { Read Intellivoice ROM }

      IOResult;  // Clear the internal IO error flag
      If FileExists(ExtractFilePath(Application.ExeName) + Config.IVoiceROM) Then
      Begin
        AssignFile(F,ExtractFilePath(Application.ExeName) + Config.IVoiceROM);
        Reset(F,1);
        BlockRead(F,Mask,FileSize(F));
        CloseFile(F);
      End;

      { Read the game }

      Cart := ctStandard;
      IOResult;  // Clear the internal IO error flag
      If Trim(ExtractFileName(ZipFileName)) <> '' Then
      Begin
        ZipMaster1.ZipFilename := ZipFileName;
        ZipMaster1.FSpecArgs.Clear;
        ZipMaster1.FSpecArgs.Add(ExtractFileName(FileName));
        ZipMaster1.Extract;
        AssignFile(F,ExtractFileName(FileName));
      End
      Else AssignFile(F,ROMFileName);
      Reset(F,1);
      If (ExtractFileExt(ROMFileName) <> '.ROM') Or Not ICartROM_Load(F,IntvMem,False) Then
      Begin
        Seek(F,0);
        If MenuInfo.ProfileRec <> Nil Then
        Begin
          MemStr := MenuInfo.ProfileRec.MemStr;
          Cart   := MenuInfo.ProfileRec.Cart;
        End
        Else
        Begin
          MemStr := '';
          Cart   := ctStandard;
        End;
        If (MemStr <> ''){ And
           (FileSize(F) >= I * $2000)} Then
        Begin
          For J := 1 To Length(MemStr) Do
          Begin
            Case UpCase(MemStr[J]) Of
              '0'..'9': Game_Start := (Ord(UpCase(MemStr[J])) - Ord('0')) * $1000;
              'A'..'F': Game_Start := (Ord(UpCase(MemStr[J])) - Ord('A')) * $1000 + $A000;
            Else
              Game_Start := $5000;
            End; { Case }
            If Cart = ct4800 Then Inc(Game_Start,$800);
            If FileSize(F) - FilePos(F) >= $2000
             Then Words := $1000
             Else Words := (FileSize(F) - FilePos(F)) Div 2;
            If Words > 0 Then
            Begin
              If Cart = ct6502 Then BlockRead(F,Mem6502[Game_Start],$1000)
              Else
              Begin
                For I := 0 To Words - 1 Do
                Begin
                  BlockRead(F,IntvMem[Game_Start + I],2);
                  IntvMem[Game_Start + I] := (IntvMem[Game_Start + I] Shr 8) + Word(IntvMem[Game_Start + I] Shl 8);
                End; { For I }
              End;  
            End;
          End; { For J }
        End
        Else
        Begin
          For I := 0 To (FileSize(F) Div 2) - 1 Do
          Begin
            If (FileSize(F) Shr 1) <= $B000
             Then
             Begin
               BlockRead(F,IntvMem[Game_Loc + I],2);
               IntvMem[Game_Loc + I] := IntvMem[Game_Loc + I] Shr 8 + IntvMem[Game_Loc + I] Shl 8;
             End
             Else
             Begin
               Game_Start := $5000 - ((FileSize(F) Shr 1) - $B000);
               BlockRead(F,IntvMem[Game_Start + I],2);
               IntvMem[Game_Start + I] := IntvMem[Game_Start + I] Shr 8 + IntvMem[Game_Start + I] Shl 8;
             End;
          End; { For I }
        End;
      End;
      CloseFile(F);

      { Save game E000-EFFF, in case the game is World Series Baseball }

      Move(IntvMem[$E000],GameE,SizeOf(GameE));

      { Read the ECS ROM into 2000h-2FFFh, 7000h-7FFFh, and E000h-EFFFh }

      If Cart = ctECS Then
      Begin
        If FileExists(ExtractFilePath(Application.ExeName) + Config.ECS) Then
        Begin
          IOResult;  // Clear the internal IO error flag        
          AssignFile(F,ExtractFilePath(Application.ExeName) + Config.ECS);
          Reset(F,1);
          For I := 0 To $FFF Do
          Begin
            BlockRead(F,IntvMem[$2000 + I],2);
            IntvMem[$2000 + I] := IntvMem[$2000 + I] Shr 8 + IntvMem[$2000 + I] Shl 8;
          End; { For I }
          For I := 0 To $FFF Do
          Begin
            BlockRead(F,IntvMem[$7000 + I],2);
            IntvMem[$7000 + I] := IntvMem[$7000 + I] Shr 8 + IntvMem[$7000 + I] Shl 8;
          End; { For I }
          For I := 0 To $FFF Do
          Begin
            BlockRead(F,IntvMem[$E000 + I],2);
            IntvMem[$E000 + I] := IntvMem[$E000 + I] Shr 8 + IntvMem[$E000 + I] Shl 8;
          End; { For I }
          CloseFile(F);
        End;  

        { Save ECS E000-EFFF, in case the game is World Series Baseball }

        Move(IntvMem[$E000],ECSE,SizeOf(ECSE));
      End;

      { Read the Keyboard Component ROM into 7000h-7FFFh }

      If Cart = ct6502 Then
      Begin
        If FileExists(ExtractFilePath(Application.ExeName) + Config.KBDC) Then
        Begin
          IOResult;  // Clear the internal IO error flag
          AssignFile(F,ExtractFilePath(Application.ExeName) + Config.KBDC);
          Reset(F,1);
          For I := 0 To $FFF Do
          Begin
            BlockRead(F,IntvMem[$7000 + I],2);
            IntvMem[$7000 + I] := IntvMem[$7000 + I] Shr 8 + IntvMem[$7000 + I] Shl 8;
          End; { For I }
          CloseFile(F);
        End;
        If FileExists(ExtractFilePath(Application.ExeName) + Config.KBDAlpha) Then
        Begin
          IOResult;  // Clear the internal IO error flag        
          AssignFile(F,ExtractFilePath(Application.ExeName) + Config.KBDAlpha);
          Reset(F,1);
          BlockRead(F,KBDAlpha,SizeOf(KBDAlpha));
          CloseFile(F);
        End;
      End;

      InitEmulator;
      UpdateDebugger;
      frmMain.UpdateButtonStatuses;
      ResetSound := True;
      If Trim(ExtractFileName(ZipFileName)) <> '' Then DeleteFile(FileName);
    End;
  End;
End; // TfrmMain.LoadGame

Procedure TfrmMain.UpdateDebugger;
Begin
  If frmMain.UseDebugger1.Checked Then
  Begin
    If Not frmDebugger.Visible Then frmDebugger.Show;
    frmDebugger.InitDebugger;      // Force initialization
    frmDebugger6502.InitDebugger;  // Force initialization
    frmDebugger.Repaint;
    frmDebugger6502.Repaint;
  End;
End; // TfrmMain.UpdateDebugger

procedure TfrmMain.DXSound1Initialize(Sender: TObject);
Var
  WaveFormat : TWaveFormatEx;
  DSBD       : DSBUFFERDESC;

begin
  {  44100Hz, 8bit, Mono  }

  MakePCMWaveFormatEx(WaveFormat, Playback_Freq, 8, 1);
  DXSound1.Primary.SetFormat(WaveFormat);
//  DXSound1.Primary.SetSize(WaveFormat,256);
  DXSound1.Primary.Volume := 0;

  Secondary := TDirectSoundBuffer.Create(DXSound1.DSound);
  FillChar(DSBD,SizeOf(DSBD),#0);
  DSBD.dwSize        := SizeOf(DSBD);
  DSBD.dwFlags       := DSBCAPS_GETCURRENTPOSITION2 + DSBCAPS_GLOBALFOCUS;
  DSBD.dwBufferBytes := SndBufSize;
  DSBD.lpwfxFormat   := @WaveFormat;
  Secondary.CreateBuffer(DSBD);
  Secondary.Volume   := 0;
end;

procedure TfrmMain.DXSound1Finalize(Sender: TObject);
begin
  Secondary.Free;
end;

procedure TfrmMain.Reset1Click(Sender: TObject);
begin
  btnResetCartClick(Self);
end;

procedure TfrmMain.btnResetCartClick(Sender: TObject);
Var Running: Boolean;
begin
  Running := (Emulating > 0);
  If Running Then
  Begin
    btnStop.Click;
    Application.ProcessMessages;
  End;
  LoadGame(MenuInfo.FileName,MenuInfo.ZipFileName,MenuInfo.Title);
  If Running Then btnStart.Click;
end;

procedure TfrmMain.mnuDisassembleClick(Sender: TObject);
Var F,E: String;
begin
  dlgSave.Filter     := 'Assembler files (*.asm)|*.ASM';
  dlgSave.DefaultExt := 'ASM';
  F := ExtractFileName(ROMFileName);
  E := ExtractFileExt(ROMFileName);
  F := Copy(F,1,Length(F) - Length(E));
  dlgSave.FileName := F + '.' + 'ASM';
  If dlgSave.Execute Then
  Begin
    ListingFileName := dlgSave.FileName;
//    Disassemble(Game.Bankswitch,Game.Cart,Game.ROMSize);
  End;
end;

procedure TfrmMain.mnuTVScreenClick(Sender: TObject);
begin
  mnuTVScreen.Checked := Not mnuTVScreen.Checked;
  If UseDirectDraw Then TVScreen := mnuTVScreen.Checked;
  frmMenu.SaveConfiguration(Config,Base.Version);
end;

procedure TfrmMain.mnuScreenShotClick(Sender: TObject);
Var
  BMP  : TBitmap;
  X,Y  : Integer;
  Addr : Integer;
  C    : TColor;
  B    : Byte;
  F,E  : String;
  S    : TFileStream;
  Img  : TJPEGImage;

begin
  dlgSave.DefaultExt := 'JPG';
  dlgSave.Filter     := 'Image files|*.JPG;*.BMP';
  dlgSave.InitialDir := Config.ScreenShotPath;
  F := ExtractFileName(ROMFileName);
  E := ExtractFileExt(ROMFileName);
  F := Copy(F,1,Length(F) - Length(E));
  dlgSave.FileName := F + '.' + 'JPG';
  If dlgSave.Execute Then
  Begin
    BMP             := TBitmap.Create;
    BMP.Width       := 320;
    BMP.Height      := 200;
    BMP.PixelFormat := pf24Bit;
    Addr            := 0;//(200 - 1) * 320;
    C               := 0;
    For Y := 0 To 199 Do
    Begin
      For X := 0 To 319 Do
      Begin
        B                      := ScnBuf[Addr];
        TRGBQUAD(C).rgbBlue    := Pal[B].R;   // Don't ask me why, but these have to be reversed
        TRGBQUAD(C).rgbGreen   := Pal[B].G;
        TRGBQUAD(C).rgbRed     := Pal[B].B;
        BMP.Canvas.Pixels[X,Y] := C;
        Inc(Addr);
      End; // For X
//      Dec(Addr,640);
    End; // For Y
    If Copy(dlgSave.FileName,Length(dlgSave.FileName) - 3,4) = '.JPG' Then
    Begin
      Img := TJPEGImage.Create;
      Img.Assign(BMP);
      S := TFileStream.Create(dlgSave.FileName,fmCreate);
      Img.SaveToStream(S);
      S.Free;
      Img.Free;
    End
    Else BMP.SaveToFile(dlgSave.FileName);
    BMP.Free;
  End;
end;

procedure TfrmMain.btnResetClick(Sender: TObject);
begin
  btnResetCartClick(Self);
end;

procedure TfrmMain.CoolBar1Resize(Sender: TObject);
begin
  frmMain.SetImageSizes(Emulating > 0);
end;

procedure TfrmMain.mnuAboutClick(Sender: TObject);
begin
  frmAbout.ShowModal;
end;

procedure TfrmMain.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Case Button Of
    mbLeft: _MouseB := _MouseB Or 1;
   mbRight: _MouseB := _MouseB Or 2;
  mbMiddle: _MouseB := _MouseB Or 4;
  End; { Case }
end;

procedure TfrmMain.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Case Button Of
    mbLeft: _MouseB := _MouseB And Not 1;
   mbRight: _MouseB := _MouseB And Not 2;
  mbMiddle: _MouseB := _MouseB And Not 4;
  End; { Case }
end;

procedure TfrmMain.DXDraw1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  LoadKeyArray;
end;

procedure TfrmMain.DXDraw1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  LoadKeyArray;
end;

Procedure TfrmMain.DeactivateApp(Sender: TObject);
Begin
  If UseDirectDraw    And
     DDFullScreen     And
     (Emulating > 0)  And
     (doFullScreen In DXDraw1.Options) Then
     Begin
       If Emulating > 1 Then Dec(Emulating) Else Exiting := True;
     End;
//       ExitFullScreenMode;
End; // TfrmMain.DeactivateApp

procedure TfrmMain.mnuSaveGameClick(Sender: TObject);
begin
//  SaveStatus;
end;

procedure TfrmMain.mnuLoadGameClick(Sender: TObject);
begin
//  RestoreStatus;
end;

procedure TfrmMain.btnPrefsClick(Sender: TObject);
begin
  frmPrefs.ShowModal;
end;

procedure TfrmMain.mnuProfileClick(Sender: TObject);
begin
  frmProfileSettings.ShowModal;
end;

procedure TfrmMain.btnProfileClick(Sender: TObject);
begin
  mnuProfileClick(Self);
end;

Procedure InitPalette;
Var I: Integer;
Begin
  FillChar(Pal,SizeOf(Pal),#0);
  For I := 0 To 22 Do
  Begin
    Pal[I].R := _Palette[I].R Shl 2;
    Pal[I].G := _Palette[I].G Shl 2;
    Pal[I].B := _Palette[I].B Shl 2;
  End; // For I
End; // InitPalette

procedure TfrmMain.btnRecordClick(Sender: TObject);
Var
  F  : File;
  St : String;

begin
  If Not AVIFile.Opened Then
  Begin
    dlgSave.Filter := 'AVI video files (*.avi)|*.AVI';
    If dlgSave.Execute Then
    Begin
      St := dlgSave.FileName;
      If St <> '' Then
      Begin
        If CreateFile(F,St) Then
        Begin
          CloseFile(F);
          btnRecord.ImageIndex := 7;
          AVIFile.Open(St,320,200,acRLE,exNone,Pal);
          mnuRecordAVI.Checked := True;
        End;
      End;
    End;
  End
  Else
  Begin
    AVIFile.Close;
    btnRecord.ImageIndex := 6;
    mnuRecordAVI.Checked := False;
  End;
end;

procedure TfrmMain.Intv2PC1Click(Sender: TObject);
Var Intf: TInterface;
begin
  If InterfaceHandler.Stack.Count > 3 Then
  Begin
    InterfaceHandler.DeactivateInterfaces;
    Intf := TInterface(InterfaceHandler.Stack.Objects[3]);
    Intf.Enabled := Not Intf.Enabled;
    Intv2PC1.Checked := Intf.Enabled;
    Intv2PC          := Intf.Enabled;
    InterfaceHandler.ActivateInterfaces;
    frmMenu.SaveConfiguration(Config,Base.Version);
  End;
end;

procedure TfrmMain.FormResize(Sender: TObject);
begin
  SetOverlaySize;
  If Zoom = 0 Then SetImageSizes(Emulating > 0);
end;

procedure TfrmMain.mnuRecordAVIClick(Sender: TObject);
begin
  btnRecordClick(Self);
end;

procedure TfrmMain.mnuZoom3xClick(Sender: TObject);
begin
  Config.ZoomMode   := 3;
  Zoom              := 3;
  mnuZoom3x.Checked := True;
  SetImageSizes(False);
end;

procedure TfrmMain.mnuZoomFullClick(Sender: TObject);
begin
  Config.ZoomMode   := 0;
  Zoom              := 0;
  mnuZoomFull.Checked := True;
  SetImageSizes(False);
end;

procedure TfrmMain.btnManualClick(Sender: TObject);
Var
  MP   : String;
  St   : String;

  Procedure OpenManual(FileName: String; RTF: Boolean);
  Var Flags: OLEVariant;
  Begin
//    frmManual.reManual.PlainText := Not RTF;
//    frmManual.reManual.Lines.Clear;
//    frmManual.reManual.Lines.LoadFromFile(FileName);
    Flags := 0;
    frmManual.wbManual.Navigate('file://' + FileName, Flags, Flags, Flags, Flags);
    frmManual.ShowModal;
  End; // OpenManual

begin
  If MenuInfo.FileName <> '' Then
  Begin
    St := RemoveFileExt(ExtractFileName(MenuInfo.FileName));
    MP := Config.ManualPath;
    If (MP <> '') And Not (MP[Length(MP)] In [':','\']) Then MP := MP + '\';

         If FileExists(MP + St + '.TXT')  Then OpenManual(MP + St + '.TXT',False)
    Else If FileExists(MP + St + '.RTF')  Then OpenManual(MP + St + '.RTF',True)
    Else If FileExists(MP + St + '.PDF')  Then OpenManual(MP + St + '.PDF',True)
    Else If FileExists(MP + St + '.HTM')  Then OpenManual(MP + St + '.HTM',True)
    Else If FileExists(MP + St + '.HTML') Then OpenManual(MP + St + '.HTML',True);
    ResetClock;
  End;
end;

procedure TfrmMain.btnNetPlayClick(Sender: TObject);
begin
  If Emulating > 0 Then
  Begin
    btnStopClick(Self);
    Application.ProcessMessages;
    btnResetCartClick(Self);
    Application.ProcessMessages;
  End;
  frmKaillera.Show;
  KailleraSelectServerDialog(Handle);
  frmKaillera.Hide;
end;

Initialization
  AVIFile.Init;
  Emulating := 0;
  FileMode  := 0;
  InitPalette;
  InitGameType;
end.

// ----------------------------------------------------------------------
// IntvWin - Mattel Intellivision Emulator
// Copyright (C) 2000 John Dullea
//
//  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 2 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, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// ----------------------------------------------------------------------

