' http://www.muslim-programers.com/1/inst.html
' http://www.cs.lmu.edu/~ray/notes/x86encode/

' Intel 80x86 Architecture: http://cs.oberlin.edu/~jdonalds/317/lecture06.html
' Map Of Instructions: http://www.mlsite.net/8086/ and http://www.sandpile.org/x86/opc_1.htm
' http://en.wikibooks.org/wiki/X86_Assembly/Machine_Language_Conversion
' http://www.xs4all.nl/~ganswijk/chipdir/iset/8086bin.txt
' The Intel 8086 / 8088/ 80186 / 80286 / 80386 / 80486 Instruction Set: http://zsmith.co/intel.html

Imports System.Threading
Imports x8086NetEmu.x8086.GPFlags

Public Class x8086
    Public Enum Models
        PCE_IBMPC_5150
        PCE_IBMPC_5160
    End Enum
    Private mModel As Models = Models.PCE_IBMPC_5150
    Private mVic20 As Boolean

    Private mRegisters As GPRegisters = New GPRegisters()
    Private mFlags As GPFlags = New GPFlags()
    Private mVideoAdapter As CGAAdapter
    Private mKeyboard As KeyboardAdapter
    Private mFloppyController As FloppyControllerAdapter
    Private mAdapters As Adapters = New Adapters(Me)
    Private mPorts As IOPorts = New IOPorts(Me)
    Private mEnableExceptions As Boolean
    Private mDebugMode As Boolean
    Private mIsPaused As Boolean

    Private opCode As Byte
    Private opCodeSize As Integer = 0

    Private addrMode As AddressingMode
    Private mIsExecuting As Boolean = False

    Private mEmulateINT13 As Boolean = True

    Private Enum REPLoopModes
        None
        REPE
        REPENE
    End Enum
    Private repeLoopMode As REPLoopModes

    Private forceNewIPAddress As Integer
    Private Property IPAddrOff As Integer
        Get
            useIPAddrOff = False
            Return forceNewIPAddress
        End Get
        Set(value As Integer)
            forceNewIPAddress = value
            useIPAddrOff = True
        End Set
    End Property
    Private useIPAddrOff As Boolean
    Private tmpIPAddrOff As Integer

    Private clkCyc As Integer = 0

    Public Const KHz As Long = 1000
    Public Const MHz As Long = KHz * KHz
    Public Const BaseClock As Long = 4.7727 * MHz
    Private mCyclesPerSecond As Long = BaseClock ' CPU clock speed

    Private doReSchedule As Boolean
    Private leftCycleFrags As Long

    Private cancelAllThreads As Boolean
    Private debugWaiter As AutoResetEvent

    Private trapEnabled As Boolean
    Private trapSkipFirst As Boolean

    Public Sched As Scheduler
    Public DMA As DMAI8237
    Public PIC As PIC8259
    Public PIT As PIT8254
    Public PPI As PPI8255_OLD

    Private FPU As x8087

    Public Event EmulationTerminated()
    Public Event InstructionDecoded()
    Public Event [Error](message As String)
    Public Shared Event Output(message As String)

    Public Sub New(Optional v20 As Boolean = False)
        mVic20 = v20

        debugWaiter = New AutoResetEvent(False)
        addrMode = New AddressingMode()

        Sched = New Scheduler()
        Sched.SetCPU(Me)

        FPU = New x8087(Me)

        PIC = New PIC8259(Me)
        DMA = New DMAI8237(Me)

        Dim intPIT = PIC.GetIrqLine(0)
        PIT = New PIT8254(Me, intPIT)

        Dim intPPI = PIC.GetIrqLine(1)
        PPI = New PPI8255_OLD(Me, intPPI)

        mPorts.Add(PIC)
        mPorts.Add(DMA)
        mPorts.Add(PIT)
        mPorts.Add(PPI)

        Init()
    End Sub

    Public Shared ReadOnly Property IsRunningOnMono As Boolean
        Get
            Return Type.GetType("Mono.Runtime") IsNot Nothing
        End Get
    End Property

    Public Shared Function FixPath(fileName As String) As String
#If Win32 Then
        Return fileName
#Else
        If Environment.OSVersion.Platform = PlatformID.Unix Then
            Return fileName.Replace("\", IO.Path.DirectorySeparatorChar)
        Else
            Return fileName
        End If
#End If
    End Function

    Public Property ClockFrequency As Long
        Get
            Return mCyclesPerSecond
        End Get
        Set(value As Long)
            mCyclesPerSecond = value + (value Mod BaseClock)
        End Set
    End Property

    Public Sub Init()
        Sched.StopSimulation()

        InitSystem()
        LoadBIOS()
        FlushCycles()

        Dim syncQuantum As Double = 0.001
        Dim syncSimulationSpeed As Double = 1.0
        Sched.SetSynchronization(True, Scheduler.CLOCKRATE * syncQuantum, Scheduler.CLOCKRATE * syncSimulationSpeed / 1000)

        SetupSystem()

        Registers.CS = &HFFFF
        Registers.IP = &H0

        mEnableExceptions = False
        mIsExecuting = False
        mIsPaused = False
        mIsHalted = False
        doReSchedule = False
    End Sub

    Public Sub InitSystem()
        For i = 0 To Memory.Length - 1
            Memory(i) = 0
        Next

        mIsExecuting = True
        cancelAllThreads = True

        trapEnabled = False
        trapSkipFirst = False

        mIsHalted = False
        mIsExecuting = False
        isDecoding = False
        repeLoopMode = REPLoopModes.None
        IPAddrOff = 0
        useIPAddrOff = False

        mRegisters.ActiveSegmentRegister = GPRegisters.RegistersTypes.DS

        mRegisters.AX = 0
        mRegisters.BX = 0
        mRegisters.CX = 0
        mRegisters.DX = 0

        mRegisters.BP = 0
        mRegisters.IP = 0
        mRegisters.SP = 0

        mRegisters.CS = 0
        mRegisters.DS = 0
        mRegisters.ES = 0
        mRegisters.SS = 0

        mRegisters.SI = 0
        mRegisters.DI = 0

        mFlags.EFlags = 0
        mFlags.IF = 0 ' ???
    End Sub

    Private Sub SetupSystem()
        If PPI Is Nothing Then Exit Sub

        ' http://docs.huihoo.com/help-pc/int-int_11.html
        PPI.SetSwitchData(Binary.From("0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1".Replace(" ", "")))
        '                             │F│E│D│C│B│A│9│8│7│6│5│4│3│2│1│0│  AX
        '                              │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──── IPL diskette installed
        '                              │ │ │ │ │ │ │ │ │ │ │ │ │ │ └───── math coprocessor
        '                              │ │ │ │ │ │ │ │ │ │ │ │ └─┴────── old PC system board RAM < 256K
        '                              │ │ │ │ │ │ │ │ │ │ │ │ │ └───── pointing device installed (PS/2)
        '                              │ │ │ │ │ │ │ │ │ │ │ │ └────── not used on PS/2
        '                              │ │ │ │ │ │ │ │ │ │ └─┴─────── initial video mode
        '                              │ │ │ │ │ │ │ │ └─┴────────── # of diskette drives, less 1
        '                              │ │ │ │ │ │ │ └───────────── 0 if DMA installed
        '                              │ │ │ │ └─┴─┴────────────── number of serial ports
        '                              │ │ │ └─────────────────── game adapter installed
        '                              │ │ └──────────────────── unused, internal modem (PS/2)
        '                              └─┴───────────────────── number of printer ports

        'PPI.PortA(0) = &H30 Or &HC
        'PPI.PortA(1) = &H0
        'PPI.PortB = &H8
        'PPI.PortC(0) = If(mModel = Models.PCE_IBMPC_5160, 1, 0)
        'PPI.PortC(1) = 0

        '' Floppy count
        'Dim count = 2 ' Foreced, for now...
        'Select Case mModel
        '    Case Models.PCE_IBMPC_5150
        '        PPI.PortA(0) = PPI.PortA(0) And (Not &HC1)
        '        If count > 0 Then
        '            PPI.PortA(0) = PPI.PortA(0) Or &H1
        '            PPI.PortA(0) = PPI.PortA(0) Or (((count - 1) And &H3) << 6)
        '        End If
        '    Case Models.PCE_IBMPC_5160
        '        PPI.PortC(1) = PPI.PortC(1) And (Not &HC)
        '        If count > 0 Then
        '            PPI.PortC(1) = PPI.PortC(1) Or (((count - 1) And &H3) << 2)
        '        End If
        'End Select

        '' Video Mode
        'Dim videoMode As CGAAdapter.VideoModes = CGAAdapter.VideoModes.Mode3_Text_Color_80x25 ' Foreced, for now...
        'Select Case mModel
        '    Case Models.PCE_IBMPC_5150
        '        PPI.PortA(0) = PPI.PortA(0) And (Not &H30)
        '        PPI.PortA(0) = PPI.PortA(0) Or ((videoMode And &H3) << 4)
        '    Case Models.PCE_IBMPC_5160
        '        PPI.PortC(1) = PPI.PortC(1) And (Not &H3)
        '        PPI.PortC(1) = PPI.PortC(1) Or (videoMode And &H3)
        'End Select

        '' RAM size
        'Dim size = Memory.Length ' Foreced, for now...
        'Select Case mModel
        '    Case Models.PCE_IBMPC_5150
        '        size = If(size < 65536, 0, (size - 65536) / 32768)
        '        PPI.PortC(0) = PPI.PortC(0) And &HF0
        '        PPI.PortC(1) = PPI.PortC(1) And &HFE
        '        PPI.PortC(0) = PPI.PortC(0) Or (size And &HF)
        '        PPI.PortC(1) = PPI.PortC(1) Or ((size >> 4) And &H1)
        '    Case Models.PCE_IBMPC_5160
        '        size = size >> 16
        '        If size > 0 Then
        '            size -= 1
        '            If size > 3 Then size = 3
        '        End If
        '        PPI.PortC(0) = PPI.PortC(0) And &HF3
        '        PPI.PortC(0) = PPI.PortC(0) Or ((size << 2) And &HC)
        'End Select
    End Sub

    Private Sub LoadBIOS()
        ' BIOS
        LoadBIN("roms\PCXTBIOS.ROM", &HFE00, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\xtbios2\EPROMS\2764\XTBIOS.ROM", &HFE00, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\xtbios25\EPROMS\2764\PCXTBIOS.ROM", &HFE00, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\PCemV0.7\roms\genxt\pcxt.rom", &HFE00, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\fake86-0.12.9.19-win32\Binaries\pcxtbios.bin", &HFE00, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\award-2.05.rom", &HFE00, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\phoenix-2.51.rom", &HFE00, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\PCE - PC Emulator\bin\rom\ibm-pc-1982.rom", &HFE00, &H0)
        'LoadBIN("E:\Documents\Visual Studio 2012\Projects\x8086NetEmu\Other Emulators & Resources\fake86-0.12.9.19-win32\Binaries\pcxtbios.BIN", &HFE00, &H0)

        ' VGA
        'LoadBIN("..\..\Other Emulators & Resources\PCemV0.7\roms\TRIDENT.BIN", &HC000, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\xtbios2\TEST\ET4000.BIN", &HC000, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\fake86-0.12.9.19-win32\Binaries\videorom.bin", &HC000, &H0)

        ' BASIC C1.1
        LoadBIN("roms\BASICC11.BIN", &HF600, &H0)
        'LoadBIN("..\..\Other Emulators & Resources\xtbios2\TEST\BASICC11.BIN", &HF600, &H0)

        ' Lots of ROMs: http://www.hampa.ch/pce/download.html
    End Sub

    Public Sub Close()
        cancelAllThreads = True
        If DebugMode Then debugWaiter.Set()
        Sched.StopSimulation()

        For Each adapter As Adapter In Adapters
            adapter.CloseAdapter()
        Next
    End Sub

    Public Sub SoftReset()
        ' Just as Bill would've have wanted it... ;)
        PPI.PutKeyData(Keys.ControlKey, False)
        PPI.PutKeyData(Keys.Menu, False)
        PPI.PutKeyData(Keys.Delete, False)
    End Sub

    Public Sub HardReset()
        Init()
        Run(mDebugMode)
    End Sub

    Public Sub StepInto()
        debugWaiter.Set()
    End Sub

    Public Sub Run(Optional debugMode As Boolean = False)
        mDebugMode = debugMode
        cancelAllThreads = False

#If Win32 Then
        If PIT IsNot Nothing Then PIT.Speaker.Enabled = False
        If mVideoAdapter IsNot Nothing Then mVideoAdapter.Reset()
#End If

        If mDebugMode Then RaiseEvent InstructionDecoded()
        Sched.Start()
    End Sub

    Public Sub Pause()
        If mIsExecuting Then
            mIsPaused = True

            Do
                Thread.Sleep(10)
            Loop While mIsExecuting

#If Win32 Then
            PIT.Speaker.Enabled = False
#End If
        End If
    End Sub

    Public Sub [Resume]()
        doReSchedule = False
        mIsPaused = False
    End Sub

    Private Sub FlushCycles()
        Dim t As Long = clkCyc * Scheduler.CLOCKRATE
        t += leftCycleFrags
        Sched.AdvanceTime(t / mCyclesPerSecond)
        clkCyc = 0
        leftCycleFrags = t Mod mCyclesPerSecond

        SetReschedule()
    End Sub

    Public Sub SetReschedule()
        doReSchedule = True
    End Sub

    Private Function CheckPendingInterrupts() As Boolean
        If mFlags.IF = 1 AndAlso PIC IsNot Nothing Then

            ' http://ntsecurity.nu/onmymind/2007/2007-08-22.html
            If repeLoopMode <> REPLoopModes.None OrElse mRegisters.ActiveSegmentChanged Then Return False

            Dim intNum As Integer = PIC.GetPendingInterrupt()
            If intNum >= 0 Then
                mIsHalted = False
                HandleHardwareInterrupt(intNum)
                Return True
            End If
        End If

        Return False
    End Function

    Public Sub PreExecute()
        If mIsExecuting OrElse isDecoding OrElse mIsPaused Then Exit Sub

        doReSchedule = False

        Dim maxRunTime As Long = Sched.GetTimeToNextEvent()
        If maxRunTime > Scheduler.CLOCKRATE Then maxRunTime = Scheduler.CLOCKRATE
        Dim maxRunCycl As Long = (maxRunTime * mCyclesPerSecond - leftCycleFrags + Scheduler.CLOCKRATE - 1) / Scheduler.CLOCKRATE

        Dim pendingINTs As Boolean = CheckPendingInterrupts()
        If mIsHalted Then SetReschedule()

        While clkCyc < maxRunCycl AndAlso Not doReSchedule
            If mDebugMode Then debugWaiter.WaitOne()

            If Not pendingINTs Then pendingINTs = CheckPendingInterrupts()

            If isDecoding Then
                Thread.Sleep(1)
            Else
                Execute()
            End If

            If mDebugMode Then RaiseEvent InstructionDecoded()
        End While

        If clkCyc > 0 Then FlushCycles()
    End Sub

    Public Sub Execute()
        mIsExecuting = True

        trapEnabled = (mFlags.TF = 1) AndAlso Not trapSkipFirst
        trapSkipFirst = False

        opCode = RAM8(mRegisters.CS, mRegisters.IP)
        opCodeSize = 1

        ' Hack from fake86 to force BIOS into detecting a EGA/VGA adapter
        'Memory(&H410) = &H41

        ' Hack to "allow" the use of hard drives (fake86)
        'Memory(&H475) = 1

        Select Case opCode
            Case &H0 To &H3 ' add reg<->reg / reg<->mem
                SetAddressing()
                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), mRegisters.Val(addrMode.Register1), , Operation.Add)
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.Add)
                    End If
                    clkCyc += 3
                Else
                    If addrMode.Direction = 0 Then
                        RAMn = DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register1), , Operation.Add)
                        clkCyc += 16
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), addrMode.IndMem, , Operation.Add)
                        clkCyc += 9
                    End If
                End If

            Case &H4 ' add al, imm
                mRegisters.AL = DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.Add)
                clkCyc += 4

            Case &H5 ' add ax, imm
                mRegisters.AX = DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.Add)
                clkCyc += 4

            Case &H6 ' push es
                PushIntoStack(mRegisters.ES)
                clkCyc += 10

            Case &H7 ' pop es
                mRegisters.ES = PopFromStack()
                clkCyc += 8

            Case &H8 To &HB ' or
                SetAddressing()
                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), mRegisters.Val(addrMode.Register1), , Operation.LogicOr)
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.LogicOr)
                    End If
                    clkCyc += 3
                Else
                    If addrMode.Direction = 0 Then
                        RAMn = DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register1), , Operation.LogicOr)
                        clkCyc += 16
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), addrMode.IndMem, , Operation.LogicOr)
                        clkCyc += 9
                    End If
                End If

            Case &HC ' or al and imm
                mRegisters.AL = DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.LogicOr)
                clkCyc += 4

            Case &HD ' or ax and imm
                mRegisters.AX = DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.LogicOr)
                clkCyc += 4

            Case &HE ' push cs
                PushIntoStack(mRegisters.CS)
                clkCyc += 10

            Case &HF ' pop cs
                If Not mVic20 Then
                    mRegisters.CS = PopFromStack()
                    clkCyc += 8
                End If

            Case &H10 To &H13 ' adc
                SetAddressing()
                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), mRegisters.Val(addrMode.Register1), , Operation.AddWithCarry)
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.AddWithCarry)
                    End If
                    clkCyc += 3
                Else
                    If addrMode.Direction = 0 Then
                        RAMn = DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register1), , Operation.AddWithCarry)
                        clkCyc += 16
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), addrMode.IndMem, , Operation.AddWithCarry)
                        clkCyc += 9
                    End If
                End If

            Case &H14 ' adc al and imm
                mRegisters.AL = DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.AddWithCarry)
                clkCyc += 3

            Case &H15 ' adc ax and imm
                mRegisters.AX = DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.AddWithCarry)
                clkCyc += 3

            Case &H16 ' push ss
                PushIntoStack(mRegisters.SS)
                clkCyc += 10

            Case &H17 ' pop ss
                mRegisters.SS = PopFromStack()
                clkCyc += 8

            Case &H18 To &H1B ' sbb
                SetAddressing()
                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), mRegisters.Val(addrMode.Register1), , Operation.SubstractWithCarry)
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.SubstractWithCarry)
                    End If
                    clkCyc += 3
                Else
                    If addrMode.Direction = 0 Then
                        RAMn = DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register1), , Operation.SubstractWithCarry)
                        clkCyc += 16
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), addrMode.IndMem, , Operation.SubstractWithCarry)
                        clkCyc += 9
                    End If
                End If

            Case &H1C ' sbb al and imm
                mRegisters.AL = DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.SubstractWithCarry)
                clkCyc += 4

            Case &H1D ' sbb ax and imm
                mRegisters.AX = DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.SubstractWithCarry)
                clkCyc += 4

            Case &H1E ' push ds
                PushIntoStack(mRegisters.DS)
                clkCyc += 10

            Case &H1F ' pop ds
                mRegisters.DS = PopFromStack()
                clkCyc += 8

            Case &H20 To &H23 ' and reg/mem and reg to either
                SetAddressing()
                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), mRegisters.Val(addrMode.Register1), , Operation.LogicAnd)
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.LogicAnd)
                    End If
                    clkCyc += 3
                Else
                    If addrMode.Direction = 0 Then
                        RAMn = DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register1), , Operation.LogicAnd)
                        clkCyc += 16
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), addrMode.IndMem, , Operation.LogicAnd)
                        clkCyc += 9
                    End If
                End If

            Case &H24 ' and al and imm
                mRegisters.AL = DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.LogicAnd)
                clkCyc += 4

            Case &H25 ' and ax and imm
                mRegisters.AX = DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.LogicAnd)
                clkCyc += 4

            Case &H27 ' daa
                If (mRegisters.AL And &HF) > 9 OrElse mFlags.AF = 1 Then
                    Dim result As Integer = mRegisters.AL + 6
                    mRegisters.AL = result And &HFF
                    mFlags.AF = 1
                    mFlags.CF = If((result And &HFF00) <> 0, 1, 0)
                Else
                    mFlags.AF = 0
                End If
                If (mRegisters.AL And &HF0) > &H90 OrElse mFlags.CF = 1 Then
                    mRegisters.AL = AddValues(mRegisters.AL, &H60, DataSize.Byte)
                    mFlags.CF = 1
                Else
                    mFlags.CF = 0
                End If
                SetSZPFlags(mRegisters.AL, DataSize.Byte)
                clkCyc += 4

            Case &H28 To &H2B ' sub reg/mem with reg to either
                SetAddressing()
                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), mRegisters.Val(addrMode.Register1), , Operation.Substract)
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.Substract)
                    End If
                    clkCyc += 3
                Else
                    If addrMode.Direction = 0 Then
                        RAMn = DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register1), , Operation.Substract)
                        clkCyc += 16
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), addrMode.IndMem, , Operation.Substract)
                        clkCyc += 9
                    End If
                End If

            Case &H2C ' sub al and imm
                mRegisters.AL = DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.Substract)
                clkCyc += 4

            Case &H2D ' sub ax and imm
                mRegisters.AX = DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.Substract)
                clkCyc += 4

            Case &H2F ' das
                If (mRegisters.AL And &HF) > 9 OrElse mFlags.AF = 1 Then
                    Dim result As Integer = mRegisters.AL - 6
                    mRegisters.AL = result And &HFF
                    mFlags.AF = 1
                    mFlags.CF = If((result And &HFF00) <> 0, 1, 0)
                Else
                    mFlags.AF = 0
                End If
                If (mRegisters.AL And &HF0) > &H90 OrElse mFlags.CF = 1 Then
                    mRegisters.AL = AddValues(mRegisters.AL, -&H60, DataSize.Byte)
                    mFlags.CF = 1
                Else
                    mFlags.CF = 0
                End If
                SetSZPFlags(mRegisters.AL, DataSize.Byte)
                clkCyc += 4

            Case &H30 To &H33 ' xor reg/mem and reg to either
                SetAddressing()
                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), mRegisters.Val(addrMode.Register1), , Operation.LogicXor)
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(Registers.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.LogicXor)
                    End If
                    clkCyc += 3
                Else
                    If addrMode.Direction = 0 Then
                        RAMn = DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register1), , Operation.LogicXor)
                        clkCyc += 16
                    Else
                        mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), addrMode.IndMem, , Operation.LogicXor)
                        clkCyc += 9
                    End If
                End If

            Case &H34 ' xor al and imm
                mRegisters.AL = DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.LogicXor)
                clkCyc += 4

            Case &H35 ' xor ax and imm
                mRegisters.AX = DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.LogicXor)
                clkCyc += 4

            Case &H37 ' aaa
                If (mRegisters.AL And &HF) > 9 OrElse mFlags.AF = 1 Then
                    mRegisters.AL = AddValues(mRegisters.AL, 6, DataSize.Byte)
                    mRegisters.AH = AddValues(mRegisters.AH, 1, DataSize.Byte)
                    mFlags.AF = 1
                    mFlags.CF = 1
                Else
                    mFlags.AF = 0
                    mFlags.CF = 0
                End If
                clkCyc += 8

            Case &H38 To &H3B ' cmp reg/mem and reg
                SetAddressing()
                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        DoOper(mRegisters.Val(addrMode.Register2), mRegisters.Val(addrMode.Register1), , Operation.Compare)
                    Else
                        DoOper(mRegisters.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.Compare)
                    End If
                    clkCyc += 3
                Else
                    If addrMode.Direction = 0 Then
                        DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register1), , Operation.Compare)
                    Else
                        DoOper(mRegisters.Val(addrMode.Register1), addrMode.IndMem, , Operation.Compare)
                    End If
                    clkCyc += 9
                End If

            Case &H3C ' cmp al and imm
                DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.Compare)
                clkCyc += 4

            Case &H3D ' cmp ax and imm
                DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.Compare)
                clkCyc += 4

            Case &H3F ' aas
                If (mRegisters.AL And &HF) > 9 OrElse mFlags.AF = 1 Then
                    mRegisters.AL = AddValues(mRegisters.AL, -6, DataSize.Byte)
                    mRegisters.AH = AddValues(mRegisters.AH, -1, DataSize.Byte)
                    mFlags.AF = 1
                    mFlags.CF = 1
                Else
                    mFlags.AF = 0
                    mFlags.CF = 0
                End If
                clkCyc += 8

            Case &H26, &H2E, &H36, &H3E ' segment override prefix
                addrMode.Decode(opCode, opCode)
                mRegisters.ActiveSegmentRegister = (addrMode.Register1 - GPRegisters.RegistersTypes.AH) + GPRegisters.RegistersTypes.ES

            Case &H40 To &H47 ' inc reg
                SetRegister1(opCode)
                mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), 1, DataSize.Word, Operation.Increment)
                clkCyc += 3

            Case &H48 To &H4F ' dec reg
                SetRegister1(opCode)
                mRegisters.Val(addrMode.Register1) = DoOper(mRegisters.Val(addrMode.Register1), 1, DataSize.Word, Operation.Decrement)
                clkCyc += 3

            Case &H50 To &H57 ' push reg
                SetRegister1(opCode)
                If addrMode.Register1 = GPRegisters.RegistersTypes.SP Then
                    PushIntoStack(mRegisters.Val(addrMode.Register1) - 2)
                Else
                    PushIntoStack(mRegisters.Val(addrMode.Register1))
                End If
                clkCyc += 11

            Case &H58 To &H5F ' pop reg
                SetRegister1(opCode)
                mRegisters.Val(addrMode.Register1) = PopFromStack()
                clkCyc += 8

            Case &H60 ' pusha
                If mVic20 Then
                    Dim sp = mRegisters.SP
                    PushIntoStack(mRegisters.AX)
                    PushIntoStack(mRegisters.CX)
                    PushIntoStack(mRegisters.DX)
                    PushIntoStack(mRegisters.BX)
                    PushIntoStack(sp)
                    PushIntoStack(mRegisters.BP)
                    PushIntoStack(mRegisters.SI)
                    PushIntoStack(mRegisters.DI)
                    clkCyc += 11 * 8 ' TODO: Need to investigate this...
                End If

            Case &H61 ' popa 
                If mVic20 Then
                    mRegisters.DI = PopFromStack()
                    mRegisters.SI = PopFromStack()
                    mRegisters.BP = PopFromStack()
                    Dim sp = PopFromStack()
                    mRegisters.BX = PopFromStack()
                    mRegisters.DX = PopFromStack()
                    mRegisters.CX = PopFromStack()
                    mRegisters.AX = PopFromStack()
                    clkCyc += 8 * 8 ' TODO: Need to investigate this...
                End If

            Case &H68 ' push
                ' Alpha Code (from fake86)
                If mVic20 Then
                    PushIntoStack(RAM8(mRegisters.CS, mRegisters.IP + 1))
                    PushIntoStack(RAM8(mRegisters.CS, mRegisters.IP))
                    clkCyc += 3 ' TODO: Need to investigate this...
                End If

            Case &H6C To &H6F ' Ignore 80186/V20 port operations... for now...
                opCodeSize += 1
                clkCyc += 3

            Case &H70 ' jo
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.OF = 1 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H71 ' jno
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.OF = 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H72 ' jb/jnae
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.CF = 1 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H73 ' jnb/jae
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.CF = 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H74 ' je/jz
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.ZF = 1 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H75 ' jne/jnz
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.ZF = 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H76 ' jbe/jna
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.CF = 1 OrElse mFlags.ZF = 1 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If


            Case &H77 ' jnbe/ja
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.CF = 0 AndAlso mFlags.ZF = 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If


            Case &H78 ' js
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.SF = 1 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H79 ' jns
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.SF = 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H7A ' jp/jpe
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.PF = 1 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H7B ' jnp/jpo
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.PF = 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H7C ' jl/jnge
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.SF <> mFlags.OF Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H7D ' jnl/jge
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.SF = mFlags.OF Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H7E ' jle/jng
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.ZF = 1 OrElse (mFlags.SF <> mFlags.OF) Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H7F ' jnle/jg
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mFlags.ZF = 0 AndAlso (mFlags.SF = mFlags.OF) Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 16
                Else
                    clkCyc += 4
                End If

            Case &H80 To &H83 : ExecuteGroup1()

            Case &H84 To &H85 ' test reg with reg/mem
                SetAddressing()
                If addrMode.IsDirect Then
                    DoOper(mRegisters.Val(addrMode.Register1), mRegisters.Val(addrMode.Register2), , Operation.Test)
                    clkCyc += 3
                Else
                    DoOper(addrMode.IndMem, mRegisters.Val(addrMode.Register2), , Operation.Test)
                    clkCyc += 9
                End If

            Case &H86 To &H87 ' xchg reg/mem with reg
                SetAddressing()
                If addrMode.IsDirect Then
                    Dim tmp As Integer = mRegisters.Val(addrMode.Register1)
                    mRegisters.Val(addrMode.Register1) = mRegisters.Val(addrMode.Register2)
                    mRegisters.Val(addrMode.Register2) = tmp
                    clkCyc += 4
                Else
                    RAMn = mRegisters.Val(addrMode.Register1)
                    mRegisters.Val(addrMode.Register1) = addrMode.IndMem
                    clkCyc += 17
                End If

            Case &H88 To &H8C ' mov ind <-> reg8/reg16
                SetAddressing()
                If opCode = &H8C Then
                    If (addrMode.Register1 And &H4) = &H4 Then
                        addrMode.Register1 = addrMode.Register1 And (Not shl2)
                    Else
                        addrMode.Register1 += GPRegisters.RegistersTypes.ES
                        If addrMode.Register2 > &H3 Then
                            addrMode.Register2 = (addrMode.Register2 + GPRegisters.RegistersTypes.BX + 1) Or shl3
                        Else
                            addrMode.Register2 += GPRegisters.RegistersTypes.AX
                        End If
                    End If
                End If

                If addrMode.IsDirect Then
                    If addrMode.Direction = 0 Then
                        mRegisters.Val(addrMode.Register2) = mRegisters.Val(addrMode.Register1)
                    Else
                        mRegisters.Val(addrMode.Register1) = mRegisters.Val(addrMode.Register2)
                    End If
                    clkCyc += 2
                Else
                    If addrMode.Direction = 0 Then
                        If addrMode.Register1 < GPRegisters.RegistersTypes.AX Then
                            RAM8(mRegisters.ActiveSegmentValue, addrMode.IndAdr) = mRegisters.Val(addrMode.Register1)
                        Else
                            RAM16(mRegisters.ActiveSegmentValue, addrMode.IndAdr) = mRegisters.Val(addrMode.Register1)
                        End If
                        clkCyc += 9
                    Else
                        mRegisters.Val(addrMode.Register1) = addrMode.IndMem
                        clkCyc += 8
                    End If
                End If

            Case &H8D ' lea
                SetAddressing()
                mRegisters.Val(addrMode.Register1) = addrMode.IndAdr
                clkCyc += 2

            Case &H8E  ' mov reg/mem to seg reg
                SetAddressing(DataSize.Word)
                SetRegister2(ParamNOPS(SelPrm.First, , DataSize.Byte))
                If addrMode.IsDirect Then
                    SetRegister1(ParamNOPS(SelPrm.First, , DataSize.Byte))
                    mRegisters.Val(addrMode.Register2) = mRegisters.Val(addrMode.Register1)
                    clkCyc += 2
                Else
                    mRegisters.Val(addrMode.Register2) = addrMode.IndMem
                    clkCyc += 8
                End If

            Case &H8F ' pop reg/mem
                SetAddressing()
                RAMn = PopFromStack()
                clkCyc += 17

            Case &H90 ' nop
                clkCyc += 3

            Case &H91 To &H97 ' xchg reg with acc
                SetRegister1(opCode)
                Dim tmp As Integer = mRegisters.AX
                mRegisters.AX = mRegisters.Val(addrMode.Register1)
                mRegisters.Val(addrMode.Register1) = tmp
                clkCyc += 3

            Case &H98 ' cbw
                mRegisters.AH = If((mRegisters.AL And &H80) <> 0, &HFF, &H0)
                clkCyc += 2

            Case &H99 ' cwd
                mRegisters.DX = If((mRegisters.AH And &H80) <> 0, &HFFFF, &H0)
                clkCyc += 5

            Case &H9A ' call direct intersegment
                IPAddrOff = Param(SelPrm.First, , DataSize.Word)
                Dim cs As Integer = Param(SelPrm.Second, , DataSize.Word)

                PushIntoStack(mRegisters.CS)
                PushIntoStack(mRegisters.IP + opCodeSize)

                mRegisters.CS = cs

                clkCyc += 28

            Case &H9B ' wait
                clkCyc += 4

            Case &H9C ' pushf
                PushIntoStack((mFlags.EFlags Or &HF800))
                clkCyc += 10

            Case &H9D ' popf
                trapSkipFirst = (mFlags.TF = 0)
                mFlags.EFlags = PopFromStack()
                clkCyc += 8

            Case &H9E ' sahf
                mFlags.EFlags = (mFlags.EFlags And &HFF00) Or mRegisters.AH
                clkCyc += 4

            Case &H9F ' lahf
                mRegisters.AH = (mFlags.EFlags And &HFF)
                clkCyc += 4

            Case &HA0 To &HA3 ' mov mem to acc | mov acc to mem
                addrMode.Decode(opCode, opCode)
                If addrMode.Direction = 0 Then
                    If addrMode.Size = DataSize.Byte Then
                        mRegisters.AL = RAM8(mRegisters.ActiveSegmentValue, Param(SelPrm.First, , DataSize.Word))
                    Else
                        mRegisters.AX = RAM16(mRegisters.ActiveSegmentValue, Param(SelPrm.First, , DataSize.Word))
                    End If
                Else
                    If addrMode.Size = DataSize.Byte Then
                        RAM8(mRegisters.ActiveSegmentValue, Param(SelPrm.First, , DataSize.Word)) = mRegisters.AL
                    Else
                        RAM16(mRegisters.ActiveSegmentValue, Param(SelPrm.First, , DataSize.Word)) = mRegisters.AX
                    End If
                End If
                clkCyc += 10

            Case &HA4 To &HA7, &HAA To &HAF : HandleREPMode()

            Case &HA8 ' test al imm8
                DoOper(mRegisters.AL, Param(SelPrm.First, , DataSize.Byte), DataSize.Byte, Operation.Test)
                clkCyc += 4

            Case &HA9 ' test ax imm16
                DoOper(mRegisters.AX, Param(SelPrm.First, , DataSize.Word), DataSize.Word, Operation.Test)
                clkCyc += 4

            Case &HB0 To &HBF ' mov imm to reg
                addrMode.Register1 = (opCode And &H7)
                If (opCode And &H8) = &H8 Then
                    addrMode.Register1 += GPRegisters.RegistersTypes.AX
                    If (opCode And &H4) = &H4 Then addrMode.Register1 += GPRegisters.RegistersTypes.ES
                    addrMode.Size = DataSize.Word
                Else
                    addrMode.Size = DataSize.Byte
                End If
                mRegisters.Val(addrMode.Register1) = Param(SelPrm.First)
                clkCyc += 4

            Case &HC2 ' ret within segment adding imm to sp
                IPAddrOff = PopFromStack()
                mRegisters.SP = AddValues(mRegisters.SP, Param(SelPrm.First, , DataSize.Word), DataSize.Word)
                clkCyc += 20

            Case &HC3 ' ret within segment
                IPAddrOff = PopFromStack()
                clkCyc += 16

            Case &HC4 To &HC5 ' les | lds
                SetAddressing(DataSize.Word)
                Dim segmentRegister As GPRegisters.RegistersTypes = If(opCode = &HC4, GPRegisters.RegistersTypes.ES, GPRegisters.RegistersTypes.DS)

                If (addrMode.Register1 And shl2) = shl2 Then
                    addrMode.Register1 = (addrMode.Register1 + GPRegisters.RegistersTypes.BX + 1) Or shl3
                Else
                    addrMode.Register1 = (addrMode.Register1 Or shl3)
                End If
                If addrMode.IsDirect Then
                    If (addrMode.Register2 And shl2) = shl2 Then
                        addrMode.Register2 = (addrMode.Register2 + GPRegisters.RegistersTypes.BX + 1) Or shl3
                    Else
                        addrMode.Register2 = (addrMode.Register2 Or shl3)
                    End If

                    mRegisters.Val(addrMode.Register1) = mRegisters.Val(addrMode.Register2)
                    mRegisters.Val(segmentRegister) = 0
                Else
                    mRegisters.Val(addrMode.Register1) = addrMode.IndMem
                    mRegisters.Val(segmentRegister) = RAM16(mRegisters.ActiveSegmentValue, addrMode.IndAdr, 2)
                End If
                clkCyc += 16

            Case &HC6 To &HC7 ' mov imm to reg/mem
                SetAddressing()
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register1) = Param(SelPrm.First, opCodeSize)
                    clkCyc += 4
                Else
                    RAMn = Param(SelPrm.First, opCodeSize)
                    clkCyc += 10
                End If

            Case &HC8 ' enter (80186+)
                If mVic20 Then
                    ' Alpha Code (from fake86)
                    Dim stackSize = Param(SelPrm.First, , DataSize.Word)
                    Dim nestLevel = Param(SelPrm.Second, , DataSize.Byte)
                    PushIntoStack(mRegisters.BP)
                    Dim frameTemp = mRegisters.SP
                    If nestLevel > 0 Then
                        For i As Integer = 1 To nestLevel - 1
                            mRegisters.BP = AddValues(mRegisters.BP, -2, DataSize.Word)
                            PushIntoStack(mRegisters.BP)
                        Next
                        PushIntoStack(mRegisters.SP)
                    End If
                    mRegisters.BP = frameTemp
                    mRegisters.SP = AddValues(mRegisters.BP, -stackSize, DataSize.Word)
                Else
                    OpCodeNotImplemented(opCode)
                End If
                opCodeSize += 3

            Case &HC9 ' leave (80186+)
                If mVic20 Then
                    ' Alpha Code (from fake86)
                    mRegisters.SP = mRegisters.BP
                    mRegisters.BP = PopFromStack()
                Else
                    OpCodeNotImplemented(opCode, "LEAVE")
                End If

            Case &HCA ' ret intersegment adding imm to sp
                Dim n As Integer = Param(SelPrm.First, , DataSize.Word)
                IPAddrOff = PopFromStack()
                mRegisters.CS = PopFromStack()
                mRegisters.SP = AddValues(mRegisters.SP, n, DataSize.Word)
                clkCyc += 17

            Case &HCB ' ret intersegment (retf)
                IPAddrOff = PopFromStack()
                mRegisters.CS = PopFromStack()
                clkCyc += 18

            Case &HCC ' int with type 3
                HandleInterrupt(3, False)
                clkCyc += 52

            Case &HCD ' int with type specified
                HandleInterrupt(Param(SelPrm.First, , DataSize.Byte), False)
                clkCyc += 51

            Case &HCE ' into
                If mFlags.OF = 1 Then
                    HandleInterrupt(4, False)
                    clkCyc += 53
                Else
                    clkCyc += 4
                End If

            Case &HCF ' iret
                IPAddrOff = PopFromStack()
                mRegisters.CS = PopFromStack()
                mFlags.EFlags = PopFromStack()
                clkCyc += 32

            Case &HD0 To &HD3 : ExecuteGroup2()

            Case &HD4 ' aam
                Dim div As Integer = Param(SelPrm.First, , DataSize.Byte)
                If div = 0 Then
                    HandleInterrupt(0, False)
                    Exit Select
                End If
                mRegisters.AH = mRegisters.AL \ div
                mRegisters.AL = mRegisters.AL Mod div
                SetSZPFlags(mRegisters.AX, DataSize.Word)
                clkCyc += 83

            Case &HD5 ' aad
                mRegisters.AL = AddValues(mRegisters.AL, mRegisters.AH * Param(SelPrm.First, , DataSize.Byte), DataSize.Byte)
                mRegisters.AH = 0
                SetSZPFlags(mRegisters.AX, DataSize.Word)
                mFlags.SF = 0
                clkCyc += 60

            Case &HD6 ' xlat
                If Not mVic20 Then
                    mRegisters.AL = If(mFlags.CF = 1, &HFF, &H0)
                    clkCyc += 4
                End If

            Case &HD7 ' xlatb
                mRegisters.AL = RAM8(mRegisters.ActiveSegmentValue, AddValues(mRegisters.BX, mRegisters.AL, DataSize.Word))
                clkCyc += 11

            Case &HD8 To &HDF ' Ignore coprocessor instructions
                SetAddressing()

                'FPU.Execute(opCode)

                HandleInterrupt(7, False)
                OpCodeNotImplemented(opCode, "FPU Not Available")
                opCodeSize += 1
                clkCyc += 2

            Case &HE0 ' loopne/loopnz
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                mRegisters.CX = AddValues(mRegisters.CX, -1, DataSize.Word)
                If mRegisters.CX <> 0 AndAlso mFlags.ZF = 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 19
                Else
                    clkCyc += 5
                End If

            Case &HE1 ' loope/loopz
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                mRegisters.CX = AddValues(mRegisters.CX, -1, DataSize.Word)
                If mRegisters.CX <> 0 AndAlso mFlags.ZF = 1 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 18
                Else
                    clkCyc += 6
                End If

            Case &HE2 ' loop
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                mRegisters.CX = AddValues(mRegisters.CX, -1, DataSize.Word)
                If mRegisters.CX <> 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 17
                Else
                    clkCyc += 5
                End If

            Case &HE3 ' jcxz
                tmpIPAddrOff = OffsetIP(DataSize.Byte)
                If mRegisters.CX = 0 Then
                    IPAddrOff = tmpIPAddrOff
                    clkCyc += 18
                Else
                    clkCyc += 6
                End If

            Case &HE4 ' in to al from fixed port
                mRegisters.AL = ReceiveFromPort(Param(SelPrm.First, , DataSize.Byte)) And &HFF
                clkCyc += 10

            Case &HE5 ' inw to ax from fixed port
                mRegisters.AX = ReceiveFromPort(Param(SelPrm.First, , DataSize.Byte))
                clkCyc += 10

            Case &HE6  ' out to al to fixed port
                SendToPort(Param(SelPrm.First, , DataSize.Byte), mRegisters.AL)
                clkCyc += 10

            Case &HE7  ' outw to ax to fixed port
                SendToPort(Param(SelPrm.First, , DataSize.Byte), mRegisters.AX)
                clkCyc += 10

            Case &HE8 ' call direct within segment
                IPAddrOff = OffsetIP(DataSize.Word)
                PushIntoStack(mRegisters.IP + opCodeSize)
                clkCyc += 19

            Case &HE9 ' jmp direct within segment
                IPAddrOff = OffsetIP(DataSize.Word)
                clkCyc += 15

            Case &HEA ' jmp direct intersegment
                IPAddrOff = Param(SelPrm.First, , DataSize.Word)
                mRegisters.CS = Param(SelPrm.Second, , DataSize.Word)
                clkCyc += 15

            Case &HEB ' jmp direct within segment short
                IPAddrOff = OffsetIP(DataSize.Byte)
                clkCyc += 15

            Case &HEC  ' in to al from variable port
                mRegisters.AL = ReceiveFromPort(mRegisters.DX) And &HFF
                clkCyc += 8

            Case &HED ' inw to ax from variable port
                mRegisters.AX = ReceiveFromPort(mRegisters.DX)
                clkCyc += 8

            Case &HEE ' out to port dx from al
                SendToPort(mRegisters.DX, mRegisters.AL)
                clkCyc += 8

            Case &HEF ' out to port dx from ax
                SendToPort(mRegisters.DX, mRegisters.AX)
                clkCyc += 8

            Case &HF0 ' lock
                OpCodeNotImplemented(opCode, "LOCK")
                clkCyc += 2

            Case &HF2 ' repne/repnz
                repeLoopMode = REPLoopModes.REPENE
                clkCyc += 2

            Case &HF3 ' repe/repz
                repeLoopMode = REPLoopModes.REPE
                clkCyc += 2

            Case &HF4 ' hlt
                clkCyc += 2
                If Not mIsHalted Then SystemHalted()
                mIsHalted = True
                trapEnabled = True
                opCodeSize = 0

            Case &HF5 ' cmc
                mFlags.CF = If(mFlags.CF = 0, 1, 0)
                clkCyc += 2

            Case &HF6 To &HF7 : ExecuteGroup3()

            Case &HF8 ' clc
                mFlags.CF = 0
                clkCyc += 2

            Case &HF9 ' stc
                mFlags.CF = 1
                clkCyc += 2

            Case &HFA ' cli
                mFlags.IF = 0
                clkCyc += 2

            Case &HFB ' sti
                mFlags.IF = 1
                clkCyc += 2

            Case &HFC ' cld
                mFlags.DF = 0
                clkCyc += 2

            Case &HFD ' std
                mFlags.DF = 1
                clkCyc += 2

            Case &HFE To &HFF : ExecuteGroup4_And_5()

            Case Else
                OpCodeNotImplemented(opCode)
                If mVic20 Then HandleInterrupt(6, False)
        End Select

        If useIPAddrOff Then
            mRegisters.IP = IPAddrOff
        Else
            IncIP(opCodeSize)
        End If
        clkCyc += opCodeSize * 4

        If mRegisters.ActiveSegmentChanged AndAlso repeLoopMode = REPLoopModes.None Then
            Select Case opCode
                Case &H26, &H2E, &H36, &H3E ' Do Jump
                Case Else
                    mRegisters.ActiveSegmentRegister = GPRegisters.RegistersTypes.DS
                    clkCyc += 2
            End Select
        End If

        If trapEnabled Then HandleInterrupt(1, False)

        mIsExecuting = False

        ' Bootstrap
        'If mRegisters.IP = &H7C00 Then DebugMode = True

        ' IBMBIO.COM
        ' If mRegisters.CS = &H60 AndAlso mRegisters.IP = 0 Then DebugMode = True

        ' call    SEG_DOS:0 (DOS INIT)
        ' If mRegisters.CS = &H60 AndAlso mRegisters.IP = &H205 Then DebugMode = True

        ' FreeDOS - After Printing Banner
        'If mRegisters.CS = &H1261 AndAlso mRegisters.IP = &H58 Then DebugMode = True

        'If mRegisters.CS = &H0 AndAlso mRegisters.IP = &H7CB9 Then DebugMode = True
    End Sub

    Private Sub ExecuteGroup1() ' &H80 To &H83
        SetAddressing()

        Dim arg1 As Integer = If(addrMode.IsDirect, mRegisters.Val(addrMode.Register2), addrMode.IndMem) ' reg
        Dim arg2 As Integer = Param(SelPrm.First, opCodeSize, If(opCode = &H83, DataSize.Byte, addrMode.Size)) ' imm

        If opCode = &H83 Then arg2 = To16bitsWithSign(arg2)

        Select Case addrMode.Reg
            Case 0 ' 000    --   add imm to reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(arg1, arg2, , Operation.Add)
                    clkCyc += 4
                Else
                    RAMn = DoOper(arg1, arg2, , Operation.Add)
                    clkCyc += 17
                End If

            Case 1 ' 001    --  or imm to reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(arg1, arg2, , Operation.LogicOr)
                    clkCyc += 4
                Else
                    RAMn = DoOper(arg1, arg2, , Operation.LogicOr)
                    clkCyc += 17
                End If

            Case 2 ' 010    --  adc imm to reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(arg1, arg2, , Operation.AddWithCarry)
                    clkCyc += 4
                Else
                    RAMn = DoOper(arg1, arg2, , Operation.AddWithCarry)
                    clkCyc += 17
                End If

            Case 3 ' 011    --  sbb imm from reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(arg1, arg2, , Operation.SubstractWithCarry)
                    clkCyc += 4
                Else
                    RAMn = DoOper(arg1, arg2, , Operation.SubstractWithCarry)
                    clkCyc += 17
                End If

            Case 4 ' 100    --  and imm to reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(arg1, arg2, , Operation.LogicAnd)
                    clkCyc += 4
                Else
                    RAMn = DoOper(arg1, arg2, , Operation.LogicAnd)
                    clkCyc += 17
                End If

            Case 5 ' 101    --  sub imm from reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(arg1, arg2, , Operation.Substract)
                    clkCyc += 4
                Else
                    RAMn = DoOper(arg1, arg2, , Operation.Substract)
                    clkCyc += 17
                End If

            Case 6 ' 110    --  xor imm to reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(arg1, arg2, , Operation.LogicXor)
                    clkCyc += 4
                Else
                    RAMn = DoOper(arg1, arg2, , Operation.LogicXor)
                    clkCyc += 17
                End If

            Case 7 ' 111    --  cmp imm with reg/mem
                DoOper(arg1, arg2, , Operation.Compare)
                If addrMode.IsDirect Then
                    clkCyc += 4
                Else
                    clkCyc += 10
                End If

        End Select
    End Sub

    Private Sub ExecuteGroup2() ' &HD0 To &HD3
        SetAddressing()

        Dim newValue As Integer
        Dim count As Integer
        Dim oldValue As Integer

        Dim mask80_8000 As Integer
        Dim mask07_15 As Integer
        Dim maskFF_FFFF As Integer
        Dim mask8_16 As Integer
        Dim mask9_17 As Integer
        Dim mask100_10000 As Integer
        Dim maskFF00_FFFF0000 As Integer

        If addrMode.Size = DataSize.Byte Then
            mask80_8000 = &H80
            mask07_15 = &H7
            maskFF_FFFF = &HFF
            mask8_16 = 8
            mask9_17 = 9
            mask100_10000 = &H100
            maskFF00_FFFF0000 = &HFF00
        Else
            mask80_8000 = &H8000
            mask07_15 = &HF
            maskFF_FFFF = &HFFFF
            mask8_16 = 16
            mask9_17 = 17
            mask100_10000 = &H10000
            maskFF00_FFFF0000 = &HFFFF0000
        End If

        If addrMode.IsDirect Then
            oldValue = mRegisters.Val(addrMode.Register2)
            If opCode >= &HD2 Then
                clkCyc += 8
            Else
                clkCyc += 2
            End If
        Else
            oldValue = addrMode.IndMem
            If opCode >= &HD2 Then
                clkCyc += 20
            Else
                clkCyc += 13
            End If
        End If

        If opCode >= &HD2 Then
            count = mRegisters.CL
            If count = 0 Then
                clkCyc += 8
                Exit Sub
            Else
                clkCyc += 4 * count
            End If
        Else
            count = 1
            clkCyc += 2
        End If

        ' 80186/V20 class CPUs limit shift count to 31 (fake86)
        If mVic20 Then count = count And &H1F

        Select Case addrMode.Reg
            Case 0 ' 000    --  rol
                If count = 1 Then
                    newValue = (oldValue << 1) Or (oldValue >> mask07_15)
                    mFlags.CF = If((oldValue And mask80_8000) <> 0, 1, 0)
                Else
                    newValue = (oldValue << (count And mask07_15)) Or (oldValue >> (mask8_16 - (count And mask07_15)))
                    mFlags.CF = newValue And &H1
                End If

            Case 1 ' 001    --  ror
                If count = 1 Then
                    newValue = (oldValue >> 1) Or (oldValue << mask07_15)
                    mFlags.CF = oldValue And &H1
                Else
                    newValue = (oldValue >> (count And mask07_15)) Or (oldValue << (mask8_16 - (count And mask07_15)))
                    mFlags.CF = If((newValue And mask80_8000) <> 0, 1, 0)
                End If

            Case 2 ' 010    --  rcl
                If count = 1 Then
                    newValue = (oldValue << 1) Or mFlags.CF
                    mFlags.CF = If((oldValue And mask80_8000) <> 0, 1, 0)
                Else
                    oldValue = oldValue Or (mFlags.CF << mask8_16)
                    newValue = (oldValue << (count Mod mask9_17)) Or (oldValue >> (mask9_17 - (count Mod mask9_17)))
                    mFlags.CF = If((newValue And mask100_10000) <> 0, 1, 0)
                End If

            Case 3 ' 011    --  rcr
                If count = 1 Then
                    newValue = (oldValue >> 1) Or (mFlags.CF << mask07_15)
                    mFlags.CF = oldValue And &H1
                Else
                    oldValue = oldValue Or (mFlags.CF << mask8_16)
                    newValue = (oldValue >> (count Mod mask9_17)) Or (oldValue << (mask9_17 - (count Mod mask9_17)))
                    mFlags.CF = If((newValue And mask100_10000) <> 0, 1, 0)
                End If

            Case 4, 6 ' 100/110    --  shl/sal
                If count = 1 Then
                    newValue = oldValue << 1
                    mFlags.CF = If((oldValue And mask80_8000) <> 0, 1, 0)
                Else
                    newValue = If(count > mask8_16, 0, (oldValue << count))
                    mFlags.CF = If((newValue And mask100_10000) <> 0, 1, 0)
                End If
                SetSZPFlags(newValue, addrMode.Size)

            Case 5 ' 101    --  shr
                If count = 1 Then
                    newValue = oldValue >> 1
                    mFlags.CF = oldValue And &H1
                Else
                    newValue = If(count > mask8_16, 0, (oldValue >> (count - 1)))
                    mFlags.CF = newValue And &H1
                    newValue = (newValue >> 1) And maskFF_FFFF
                End If
                SetSZPFlags(newValue, addrMode.Size)

            Case 7 ' 111    --  sar
                If count = 1 Then
                    newValue = (oldValue >> 1) Or (oldValue And mask80_8000)
                    mFlags.CF = oldValue And &H1
                Else
                    oldValue = oldValue Or If((oldValue And mask80_8000) <> 0, maskFF00_FFFF0000, 0)
                    newValue = oldValue >> If(count >= mask8_16, mask07_15, count - 1)
                    mFlags.CF = newValue And &H1
                    newValue = (newValue >> 1) And maskFF_FFFF
                End If
                SetSZPFlags(newValue, addrMode.Size)

            Case Else
                OpCodeNotImplemented(opCode, "Unknown Reg Mode for Opcode")
        End Select

        If addrMode.IsDirect Then
            mRegisters.Val(addrMode.Register2) = newValue And maskFF_FFFF
        Else
            RAMn = newValue And maskFF_FFFF
        End If

        mFlags.OF = If(((newValue Xor oldValue) And mask80_8000) <> 0, 1, 0)
    End Sub

    Private Sub ExecuteGroup3() ' &HF6 To &HF7
        SetAddressing()

        Select Case addrMode.Reg
            Case 0 ' 000    --  test
                If addrMode.IsDirect Then
                    DoOper(mRegisters.Val(addrMode.Register2), Param(SelPrm.First, opCodeSize), , Operation.Test)
                    clkCyc += 5
                Else
                    DoOper(addrMode.IndMem, Param(SelPrm.First, opCodeSize), , Operation.Test)
                    clkCyc += 11
                End If

            Case 2 ' 010    --  not
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = AddValues(Not mRegisters.Val(addrMode.Register2), 0, addrMode.Size)
                    clkCyc += 3
                Else
                    RAMn = AddValues(Not addrMode.IndMem, 0, addrMode.Size)
                    clkCyc += 16
                End If

            Case 3 ' 011    --  neg
                Dim result As Integer
                Dim value As Integer
                If addrMode.IsDirect Then
                    value = mRegisters.Val(addrMode.Register2)
                    result = AddValues(Not value, 1, addrMode.Size)
                    DoOper(0, value, , Operation.Substract)
                    mRegisters.Val(addrMode.Register2) = result
                    clkCyc += 3
                Else
                    result = AddValues(Not addrMode.IndMem, 1, addrMode.Size)
                    DoOper(0, addrMode.IndMem, , Operation.Substract)
                    RAMn = result
                    clkCyc += 16
                End If
                mFlags.CF = If(result = 0, 0, 1)

            Case 4 ' 100    --  mul
                Dim result As Long
                If addrMode.IsDirect Then
                    If addrMode.Size = DataSize.Byte Then
                        result = mRegisters.AL * mRegisters.Val(addrMode.Register2)
                        mRegisters.AX = result And &HFFFF
                        clkCyc += 70
                    Else
                        result = CLng(mRegisters.AX) * mRegisters.Val(addrMode.Register2)
                        mRegisters.AX = result And &HFFFF
                        mRegisters.DX = (result >> 16) And &HFFFF
                        clkCyc += 118
                    End If
                Else
                    If addrMode.Size = DataSize.Byte Then
                        result = mRegisters.AL * addrMode.IndMem
                        mRegisters.AX = result And &HFFFF
                        clkCyc += 76
                    Else
                        result = CLng(mRegisters.AX) * addrMode.IndMem
                        mRegisters.AX = result And &HFFFF
                        mRegisters.DX = (result >> 16) And &HFFFF
                        clkCyc += 134
                    End If
                End If

                SetSZPFlags(result And &HFFFF, addrMode.Size)

                If mRegisters.DX = 0 Then
                    mFlags.CF = 0
                    mFlags.OF = 0
                Else
                    mFlags.CF = 1
                    mFlags.OF = 1
                End If
                If Not mVic20 Then mFlags.ZF = 0

            Case 5 ' 101    --  imul
                Dim result As Long

                If addrMode.IsDirect Then
                    If addrMode.Size = DataSize.Byte Then
                        Dim m1 As Integer = To16bitsWithSign(mRegisters.AL)
                        If (m1 And &H80) = &H80 Then m1 = m1 Or &HFFFFFF00
                        Dim m2 As Integer = To16bitsWithSign(mRegisters.Val(addrMode.Register2))
                        If (m2 And &H80) = &H80 Then m2 = m2 Or &HFFFFFF00

                        result = m1 * m2
                        mRegisters.AX = result
                        clkCyc += 70
                    Else
                        Dim m1 As Long = CLng(mRegisters.AX)
                        If (m1 And &H8000) <> 0 Then m1 = m1 Or &HFFFF0000
                        Dim m2 As Long = CLng(mRegisters.Val(addrMode.Register2))
                        If (m2 And &H8000) <> 0 Then m2 = m2 Or &HFFFF0000

                        result = m1 * m2
                        mRegisters.AX = result And &HFFFF
                        mRegisters.DX = result >> 16
                        clkCyc += 118
                    End If
                Else
                    If addrMode.Size = DataSize.Byte Then
                        Dim m1 As Integer = To16bitsWithSign(mRegisters.AL)
                        If (m1 And &H80) = &H80 Then m1 = m1 Or &HFFFFFF00
                        Dim m2 As Integer = To16bitsWithSign(addrMode.IndMem)
                        If (m2 And &H80) = &H80 Then m2 = m2 Or &HFFFFFF00

                        result = m1 * m2
                        mRegisters.AX = result
                        clkCyc += 76
                    Else
                        Dim m1 As Long = CLng(mRegisters.AX)
                        If (m1 And &H8000) <> 0 Then m1 = m1 Or &HFFFF0000
                        Dim m2 As Long = CLng(addrMode.IndMem)
                        If (m2 And &H8000) <> 0 Then m2 = m2 Or &HFFFF0000

                        result = m1 * m2
                        mRegisters.AX = result And &HFFFF
                        mRegisters.DX = result >> 16
                        clkCyc += 134
                    End If
                End If

                If addrMode.Size = DataSize.Byte Then
                    If mRegisters.AH <> 0 Then
                        mFlags.CF = 1
                        mFlags.OF = 1
                    Else
                        mFlags.CF = 0
                        mFlags.OF = 0
                    End If
                Else
                    If mRegisters.DX <> 0 Then
                        mFlags.CF = 1
                        mFlags.OF = 1
                    Else
                        mFlags.CF = 0
                        mFlags.OF = 0
                    End If
                End If
                If Not mVic20 Then mFlags.ZF = 0

            Case 6 ' 110    --  div
                Dim div As Integer
                Dim num As Integer
                Dim result As Integer
                Dim remind As Integer

                If addrMode.IsDirect Then
                    div = mRegisters.Val(addrMode.Register2)
                    If addrMode.Size = DataSize.Byte Then
                        clkCyc += 80
                        num = mRegisters.AX
                    Else
                        clkCyc += 144
                        num = (mRegisters.DX << 16) Or mRegisters.AX
                    End If
                Else
                    div = addrMode.IndMem
                    If addrMode.Size = DataSize.Byte Then
                        clkCyc += 86
                        num = mRegisters.AX
                    Else
                        clkCyc += 150
                        num = (mRegisters.DX << 16) Or mRegisters.AX
                    End If
                End If

                If div = 0 Then
                    HandleInterrupt(0, False)
                    Exit Select
                End If

                result = num \ div
                remind = num Mod div

                If addrMode.Size = DataSize.Byte Then
                    If result And &HFF00 Then
                        HandleInterrupt(0, False)
                        Exit Select
                    End If
                    mRegisters.AL = result And &HFF
                    mRegisters.AH = remind And &HFF
                Else
                    If result And &HFFFF0000 Then
                        HandleInterrupt(0, False)
                        Exit Select
                    End If
                    mRegisters.AX = result And &HFFFF
                    mRegisters.DX = remind And &HFFFF
                End If

            Case 7 ' 111    --  idiv
                Dim div As Integer
                Dim num As Integer
                Dim result As Integer
                Dim remind As Integer
                Dim sign As Boolean

                If addrMode.IsDirect Then
                    div = mRegisters.Val(addrMode.Register2)
                    If addrMode.Size = DataSize.Byte Then
                        clkCyc += 80
                        num = mRegisters.AX

                        sign = ((num Xor div) And &H8000) <> 0
                        num = If(num < &H8000, num, ((Not num) + 1) And &HFFFF)
                        div = If(div < &H8000, div, ((Not div) + 1) And &HFFFF)
                    Else
                        clkCyc += 144
                        num = (mRegisters.DX << 16) Or mRegisters.AX

                        div = If((div And &H8000) <> 0, (div Or &HFFFF0000), div)
                        sign = ((num Xor div) And &H80000000) <> 0
                        num = If(num < &H80000000, num, ((Not num) + 1) And &HFFFFFFFF)
                        div = If(div < &H80000000, div, ((Not div) + 1) And &HFFFFFFFF)
                    End If
                Else
                    div = addrMode.IndMem
                    If addrMode.Size = DataSize.Byte Then
                        clkCyc += 86
                        num = mRegisters.AX

                        sign = ((num Xor div) And &H8000) <> 0
                        num = If(num < &H8000, num, ((Not num) + 1) And &HFFFF)
                        div = If(div < &H8000, div, ((Not div) + 1) And &HFFFF)
                    Else
                        clkCyc += 150
                        num = (mRegisters.DX << 16) Or mRegisters.AX

                        div = If((div And &H8000) <> 0, (div Or &HFFFF0000), div)
                        sign = ((num Xor div) And &H80000000) <> 0
                        num = If(num < &H80000000, num, ((Not num) + 1) And &HFFFFFFFF)
                        div = If(div < &H80000000, div, ((Not div) + 1) And &HFFFFFFFF)
                    End If
                End If

                If div = 0 Then
                    HandleInterrupt(0, False)
                    Exit Select
                End If

                result = num \ div
                remind = num Mod div

                If addrMode.Size = DataSize.Byte Then
                    If (result And &HFF00) <> 0 Then
                        HandleInterrupt(0, False)
                        Exit Sub
                    End If

                    If sign Then
                        result = ((Not result) + 1) And &HFF
                        remind = ((Not remind) + 1) And &HFF
                    End If

                    mRegisters.AL = result And &HFF
                    mRegisters.AH = remind And &HFF
                Else
                    If (result And &HFFFF0000) <> 0 Then
                        HandleInterrupt(0, False)
                        Exit Sub
                    End If

                    If sign Then
                        result = ((Not result) + 1) And &HFFFF
                        remind = ((Not remind) + 1) And &HFFFF
                    End If

                    mRegisters.AX = result And &HFFFF
                    mRegisters.DX = remind And &HFFFF
                End If
        End Select
    End Sub

    Private Sub ExecuteGroup4_And_5() ' &HFE to &hFF
        SetAddressing()

        ' Let's make sure that it is "safe" to combine both groups 4 and 5
        'If opCode = &HFE AndAlso addrMode.Reg > 1 Then Stop

        Select Case addrMode.Reg
            Case 0 ' 000    --  inc reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), 1, , Operation.Increment)
                    clkCyc += 3
                Else
                    RAMn = DoOper(RAMn, 1, , Operation.Increment)
                    clkCyc += 15
                End If

            Case 1 ' 001    --  dec reg/mem
                If addrMode.IsDirect Then
                    mRegisters.Val(addrMode.Register2) = DoOper(mRegisters.Val(addrMode.Register2), 1, , Operation.Decrement)
                    clkCyc += 3
                Else
                    RAMn = DoOper(RAMn, 1, , Operation.Decrement)
                    clkCyc += 15
                End If

            Case 2 ' 010    --  call indirect within segment
                PushIntoStack(mRegisters.IP + opCodeSize)
                If addrMode.IsDirect Then
                    IPAddrOff = mRegisters.Val(addrMode.Register2)
                Else
                    IPAddrOff = addrMode.IndMem
                End If
                clkCyc += 11

            Case 3 ' 011    --  call indirect intersegment
                PushIntoStack(mRegisters.CS)
                PushIntoStack(mRegisters.IP + opCodeSize)
                If addrMode.IsDirect Then
                    OpCodeNotImplemented(opCode, "CALL Indirect Intersegment / Direct Mode")
                Else
                    IPAddrOff = addrMode.IndMem
                    mRegisters.CS = RAM16(mRegisters.ActiveSegmentValue, addrMode.IndAdr, 2)
                End If
                clkCyc += 37

            Case 4 ' 100    --  jmp indirect within segment
                If addrMode.IsDirect Then
                    IPAddrOff = mRegisters.Val(addrMode.Register2)
                Else
                    IPAddrOff = addrMode.IndMem
                End If
                clkCyc += 15

            Case 5 ' 101    --  jmp indirect intersegment
                If addrMode.IsDirect Then
                    OpCodeNotImplemented(opCode, "JMP Indirect Intersegment / Direct Mode")
                Else
                    IPAddrOff = addrMode.IndMem
                    mRegisters.CS = RAM16(mRegisters.ActiveSegmentValue, addrMode.IndAdr, 2)
                End If
                clkCyc += 24

            Case 6 ' 110    --  push reg/mem
                If addrMode.IsDirect Then
                    PushIntoStack(mRegisters.Val(addrMode.Register2))
                Else
                    PushIntoStack(addrMode.IndMem)
                End If

                'If addrMode.IndAdr = (&H10000 Or mRegisters.CS) Then
                '    PushIntoStack(mRegisters.CS - 2)
                '    Stop
                'Else
                '    PushIntoStack(addrMode.IndMem)
                'End If
                clkCyc += 16

        End Select
    End Sub

    Private Sub HandleREPMode()
        If repeLoopMode = REPLoopModes.None Then
            ExecStringOpCode()
        Else
            If mRegisters.CX = 0 Then
                repeLoopMode = REPLoopModes.None
            Else
                mRegisters.CX = AddValues(mRegisters.CX, -1, DataSize.Word)
                If ExecStringOpCode() Then
                    Select Case repeLoopMode
                        Case REPLoopModes.REPE
                            If mFlags.ZF = 0 Then
                                repeLoopMode = REPLoopModes.None
                                Exit Sub
                            End If
                        Case REPLoopModes.REPENE
                            If mFlags.ZF = 1 Then
                                repeLoopMode = REPLoopModes.None
                                Exit Sub
                            End If
                    End Select
                End If
                IncIP(-1)
            End If
        End If
    End Sub

    Private Function ExecStringOpCode() As Boolean
        Select Case opCode
            Case &HA4  ' movsb
                RAM8(mRegisters.ES, mRegisters.DI) = RAM8(mRegisters.ActiveSegmentValue, mRegisters.SI)
                If mFlags.DF = 0 Then
                    mRegisters.SI = AddValues(mRegisters.SI, 1, DataSize.Word)
                    mRegisters.DI = AddValues(mRegisters.DI, 1, DataSize.Word)
                Else
                    mRegisters.SI = AddValues(mRegisters.SI, -1, DataSize.Word)
                    mRegisters.DI = AddValues(mRegisters.DI, -1, DataSize.Word)
                End If
                clkCyc += 18
                Return False

            Case &HA5 ' movsw
                RAM16(mRegisters.ES, mRegisters.DI) = RAM16(mRegisters.ActiveSegmentValue, mRegisters.SI)
                If mFlags.DF = 0 Then
                    mRegisters.SI = AddValues(mRegisters.SI, 2, DataSize.Word)
                    mRegisters.DI = AddValues(mRegisters.DI, 2, DataSize.Word)
                Else
                    mRegisters.SI = AddValues(mRegisters.SI, -2, DataSize.Word)
                    mRegisters.DI = AddValues(mRegisters.DI, -2, DataSize.Word)
                End If
                clkCyc += 18
                Return False

            Case &HA6  ' cmpsb
                DoOper(RAM8(mRegisters.ActiveSegmentValue, mRegisters.SI), RAM8(mRegisters.ES, mRegisters.DI), DataSize.Byte, Operation.Compare)
                If mFlags.DF = 0 Then
                    mRegisters.SI = AddValues(mRegisters.SI, 1, DataSize.Word)
                    mRegisters.DI = AddValues(mRegisters.DI, 1, DataSize.Word)
                Else
                    mRegisters.SI = AddValues(mRegisters.SI, -1, DataSize.Word)
                    mRegisters.DI = AddValues(mRegisters.DI, -1, DataSize.Word)
                End If
                clkCyc += 22
                Return True

            Case &HA7 ' cmpsw
                DoOper(RAM16(mRegisters.ActiveSegmentValue, mRegisters.SI), RAM16(mRegisters.ES, mRegisters.DI), DataSize.Word, Operation.Compare)
                If mFlags.DF = 0 Then
                    mRegisters.SI = AddValues(mRegisters.SI, 2, DataSize.Word)
                    mRegisters.DI = AddValues(mRegisters.DI, 2, DataSize.Word)
                Else
                    mRegisters.SI = AddValues(mRegisters.SI, -2, DataSize.Word)
                    mRegisters.DI = AddValues(mRegisters.DI, -2, DataSize.Word)
                End If
                clkCyc += 22
                Return True

            Case &HAA ' stosb
                RAM8(mRegisters.ES, mRegisters.DI) = mRegisters.AL
                If mFlags.DF = 0 Then
                    mRegisters.DI = AddValues(mRegisters.DI, 1, DataSize.Word)
                Else
                    mRegisters.DI = AddValues(mRegisters.DI, -1, DataSize.Word)
                End If
                clkCyc += 11
                Return False

            Case &HAB 'stosw
                RAM16(mRegisters.ES, mRegisters.DI) = mRegisters.AX
                If mFlags.DF = 0 Then
                    mRegisters.DI = AddValues(mRegisters.DI, 2, DataSize.Word)
                Else
                    mRegisters.DI = AddValues(mRegisters.DI, -2, DataSize.Word)
                End If
                clkCyc += 11
                Return False

            Case &HAC ' lodsb
                mRegisters.AL = RAM8(mRegisters.ActiveSegmentValue, mRegisters.SI)
                If mFlags.DF = 0 Then
                    mRegisters.SI = AddValues(mRegisters.SI, 1, DataSize.Word)
                Else
                    mRegisters.SI = AddValues(mRegisters.SI, -1, DataSize.Word)
                End If
                clkCyc += 12
                Return False

            Case &HAD ' lodsw
                mRegisters.AX = RAM16(mRegisters.ActiveSegmentValue, mRegisters.SI)
                If mFlags.DF = 0 Then
                    mRegisters.SI = AddValues(mRegisters.SI, 2, DataSize.Word)
                Else
                    mRegisters.SI = AddValues(mRegisters.SI, -2, DataSize.Word)
                End If
                clkCyc += 16
                Return False

            Case &HAE ' scasb
                DoOper(mRegisters.AL, RAM8(mRegisters.ES, mRegisters.DI), DataSize.Byte, Operation.Substract)
                If mFlags.DF = 0 Then
                    mRegisters.DI = AddValues(mRegisters.DI, 1, DataSize.Word)
                Else
                    mRegisters.DI = AddValues(mRegisters.DI, -1, DataSize.Word)
                End If
                clkCyc += 15
                Return True

            Case &HAF ' scasw
                DoOper(mRegisters.AX, RAM16(mRegisters.ES, mRegisters.DI), DataSize.Word, Operation.Substract)
                If mFlags.DF = 0 Then
                    mRegisters.DI = AddValues(mRegisters.DI, 2, DataSize.Word)
                Else
                    mRegisters.DI = AddValues(mRegisters.DI, -2, DataSize.Word)
                End If
                clkCyc += 15
                Return True
        End Select

        Return False
    End Function
End Class