906 lines
47 KiB
C#
906 lines
47 KiB
C#
using System;
|
|
|
|
namespace StoicGoose.Core.CPU
|
|
{
|
|
public sealed partial class V30MZ
|
|
{
|
|
delegate int Instruction(V30MZ cpu);
|
|
readonly static Instruction[] instructions = new Instruction[256]
|
|
{
|
|
/* 0x00 */ /* ADD Eb Gb */ (cpu) => { cpu.WriteOpcodeEb(cpu.Add8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeGb())); return 1; },
|
|
/* ADD Ew Gw */ (cpu) => { cpu.WriteOpcodeEw(cpu.Add16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeGw())); return 1; },
|
|
/* ADD Gb Eb */ (cpu) => { cpu.WriteOpcodeGb(cpu.Add8(false, cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb())); return 1; },
|
|
/* ADD Gw Ew */ (cpu) => { cpu.WriteOpcodeGw(cpu.Add16(false, cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw())); return 1; },
|
|
/* 0x04 */ /* ADD AL Ib */ (cpu) => { cpu.ax.Low = cpu.Add8(false, cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* ADD AX Iw */ (cpu) => { cpu.ax.Word = cpu.Add16(false, cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* PUSH ES */ (cpu) => { cpu.Push(cpu.es); return 1; },
|
|
/* POP ES */ (cpu) => { cpu.es = cpu.Pop(); return 1; },
|
|
/* 0x08 */ /* OR Eb Gb */ (cpu) => { cpu.WriteOpcodeEb(cpu.Or8(cpu.ReadOpcodeEb(), cpu.ReadOpcodeGb())); return 1; },
|
|
/* OR Ew Gw */ (cpu) => { cpu.WriteOpcodeEw(cpu.Or16(cpu.ReadOpcodeEw(), cpu.ReadOpcodeGw())); return 1; },
|
|
/* OR Gb Eb */ (cpu) => { cpu.WriteOpcodeGb(cpu.Or8(cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb())); return 1; },
|
|
/* OR Gw Ew */ (cpu) => { cpu.WriteOpcodeGw(cpu.Or16(cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw())); return 1; },
|
|
/* 0x0C */ /* OR AL Ib */ (cpu) => { cpu.ax.Low = cpu.Or8(cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* OR AX Iw */ (cpu) => { cpu.ax.Word = cpu.Or16(cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* PUSH CS */ (cpu) => { cpu.Push(cpu.cs); return 1; },
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
|
|
/* 0x10 */ /* ADC Eb Gb */ (cpu) => { cpu.WriteOpcodeEb(cpu.Add8(true, cpu.ReadOpcodeEb(), cpu.ReadOpcodeGb())); return 1; },
|
|
/* ADC Ew Gw */ (cpu) => { cpu.WriteOpcodeEw(cpu.Add16(true, cpu.ReadOpcodeEw(), cpu.ReadOpcodeGw())); return 1; },
|
|
/* ADC Gb Eb */ (cpu) => { cpu.WriteOpcodeGb(cpu.Add8(true, cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb())); return 1; },
|
|
/* ADC Gw Ew */ (cpu) => { cpu.WriteOpcodeGw(cpu.Add16(true, cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw())); return 1; },
|
|
/* 0x14 */ /* ADC AL Ib */ (cpu) => { cpu.ax.Low = cpu.Add8(true, cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* ADC AX Iw */ (cpu) => { cpu.ax.Word = cpu.Add16(true, cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* PUSH SS */ (cpu) => { cpu.Push(cpu.ss); return 1; },
|
|
/* POP SS */ (cpu) => { cpu.ss = cpu.Pop(); return 1; },
|
|
/* 0x18 */ /* SBB Eb Gb */ (cpu) => { cpu.WriteOpcodeEb(cpu.Sub8(true, cpu.ReadOpcodeEb(), cpu.ReadOpcodeGb())); return 1; },
|
|
/* SBB Ew Gw */ (cpu) => { cpu.WriteOpcodeEw(cpu.Sub16(true, cpu.ReadOpcodeEw(), cpu.ReadOpcodeGw())); return 1; },
|
|
/* SBB Gb Eb */ (cpu) => { cpu.WriteOpcodeGb(cpu.Sub8(true, cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb())); return 1; },
|
|
/* SBB Gw Ew */ (cpu) => { cpu.WriteOpcodeGw(cpu.Sub16(true, cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw())); return 1; },
|
|
/* 0x1C */ /* SBB AL Ib */ (cpu) => { cpu.ax.Low = cpu.Sub8(true, cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* SBB AX Iw */ (cpu) => { cpu.ax.Word = cpu.Sub16(true, cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* PUSH DS */ (cpu) => { cpu.Push(cpu.ds); return 1; },
|
|
/* POP DS */ (cpu) => { cpu.ds = cpu.Pop(); return 1; },
|
|
|
|
/* 0x20 */ /* AND Eb Gb */ (cpu) => { cpu.WriteOpcodeEb(cpu.And8(cpu.ReadOpcodeEb(), cpu.ReadOpcodeGb())); return 1; },
|
|
/* AND Ew Gw */ (cpu) => { cpu.WriteOpcodeEw(cpu.And16(cpu.ReadOpcodeEw(), cpu.ReadOpcodeGw())); return 1; },
|
|
/* AND Gb Eb */ (cpu) => { cpu.WriteOpcodeGb(cpu.And8(cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb())); return 1; },
|
|
/* AND Gw Ew */ (cpu) => { cpu.WriteOpcodeGw(cpu.And16(cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw())); return 1; },
|
|
/* 0x24 */ /* AND AL Ib */ (cpu) => { cpu.ax.Low = cpu.And8(cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* AND AX Iw */ (cpu) => { cpu.ax.Word = cpu.And16(cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* (Prefix ES) */ (cpu) => 0,
|
|
/* DAA */ (cpu) => { cpu.Daa(false); return 10; },
|
|
/* 0x28 */ /* SUB Eb Gb */ (cpu) => { cpu.WriteOpcodeEb(cpu.Sub8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeGb())); return 1; },
|
|
/* SUB Ew Gw */ (cpu) => { cpu.WriteOpcodeEw(cpu.Sub16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeGw())); return 1; },
|
|
/* SUB Gb Eb */ (cpu) => { cpu.WriteOpcodeGb(cpu.Sub8(false, cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb())); return 1; },
|
|
/* SUB Gw Ew */ (cpu) => { cpu.WriteOpcodeGw(cpu.Sub16(false, cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw())); return 1; },
|
|
/* 0x2C */ /* SUB AL Ib */ (cpu) => { cpu.ax.Low = cpu.Sub8(false, cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* SUB AX Iw */ (cpu) => { cpu.ax.Word = cpu.Sub16(false, cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* (Prefix CS) */ (cpu) => 0,
|
|
/* DAS */ (cpu) => { cpu.Daa(true); return 10; },
|
|
|
|
/* 0x30 */ /* XOR Eb Gb */ (cpu) => { cpu.WriteOpcodeEb(cpu.Xor8(cpu.ReadOpcodeEb(), cpu.ReadOpcodeGb())); return 1; },
|
|
/* XOR Ew Gw */ (cpu) => { cpu.WriteOpcodeEw(cpu.Xor16(cpu.ReadOpcodeEw(), cpu.ReadOpcodeGw())); return 1; },
|
|
/* XOR Gb Eb */ (cpu) => { cpu.WriteOpcodeGb(cpu.Xor8(cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb())); return 1; },
|
|
/* XOR Gw Ew */ (cpu) => { cpu.WriteOpcodeGw(cpu.Xor16(cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw())); return 1; },
|
|
/* 0x34 */ /* XOR AL Ib */ (cpu) => { cpu.ax.Low = cpu.Xor8(cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* XOR AX Iw */ (cpu) => { cpu.ax.Word = cpu.Xor16(cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* (Prefix SS) */ (cpu) => 0,
|
|
/* AAA */ (cpu) => { cpu.Aaa(false); return 9; },
|
|
/* 0x38 */ /* CMP Eb Gb */ (cpu) => { cpu.Sub8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeGb()); return 1; },
|
|
/* CMP Ew Gw */ (cpu) => { cpu.Sub16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeGw()); return 1; },
|
|
/* CMP Gb Eb */ (cpu) => { cpu.Sub8(false, cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb()); return 1; },
|
|
/* CMP Gw Ew */ (cpu) => { cpu.Sub16(false, cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw()); return 1; },
|
|
/* 0x3C */ /* CMP AL Ib */ (cpu) => { cpu.Sub8(false, cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* CMP AX Iw */ (cpu) => { cpu.Sub16(false, cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* (Prefix DS) */ (cpu) => 0,
|
|
/* AAS */ (cpu) => { cpu.Aaa(true); return 9; },
|
|
|
|
/* 0x40 */ /* INC AX */ (cpu) => { cpu.ax.Word = cpu.Inc16(cpu.ax.Word); return 1; },
|
|
/* INC CX */ (cpu) => { cpu.cx.Word = cpu.Inc16(cpu.cx.Word); return 1; },
|
|
/* INC DX */ (cpu) => { cpu.dx.Word = cpu.Inc16(cpu.dx.Word); return 1; },
|
|
/* INC BX */ (cpu) => { cpu.bx.Word = cpu.Inc16(cpu.bx.Word); return 1; },
|
|
/* 0x44 */ /* INC SP */ (cpu) => { cpu.sp = cpu.Inc16(cpu.sp); return 1; },
|
|
/* INC BP */ (cpu) => { cpu.bp = cpu.Inc16(cpu.bp); return 1; },
|
|
/* INC SI */ (cpu) => { cpu.si = cpu.Inc16(cpu.si); return 1; },
|
|
/* INC DI */ (cpu) => { cpu.di = cpu.Inc16(cpu.di); return 1; },
|
|
/* 0x48 */ /* DEC AX */ (cpu) => { cpu.ax.Word = cpu.Dec16(cpu.ax.Word); return 1; },
|
|
/* DEC CX */ (cpu) => { cpu.cx.Word = cpu.Dec16(cpu.cx.Word); return 1; },
|
|
/* DEC DX */ (cpu) => { cpu.dx.Word = cpu.Dec16(cpu.dx.Word); return 1; },
|
|
/* DEC BX */ (cpu) => { cpu.bx.Word = cpu.Dec16(cpu.bx.Word); return 1; },
|
|
/* 0x4C */ /* DEC SP */ (cpu) => { cpu.sp = cpu.Dec16(cpu.sp); return 1; },
|
|
/* DEC BP */ (cpu) => { cpu.bp = cpu.Dec16(cpu.bp); return 1; },
|
|
/* DEC SI */ (cpu) => { cpu.si = cpu.Dec16(cpu.si); return 1; },
|
|
/* DEC DI */ (cpu) => { cpu.di = cpu.Dec16(cpu.di); return 1; },
|
|
|
|
/* 0x50 */ /* PUSH AX */ (cpu) => { cpu.Push(cpu.ax.Word); return 1; },
|
|
/* PUSH CX */ (cpu) => { cpu.Push(cpu.cx.Word); return 1; },
|
|
/* PUSH DX */ (cpu) => { cpu.Push(cpu.dx.Word); return 1; },
|
|
/* PUSH BX */ (cpu) => { cpu.Push(cpu.bx.Word); return 1; },
|
|
/* 0x54 */ /* PUSH SP */ (cpu) => { cpu.Push(cpu.sp); return 1; },
|
|
/* PUSH BP */ (cpu) => { cpu.Push(cpu.bp); return 1; },
|
|
/* PUSH SI */ (cpu) => { cpu.Push(cpu.si); return 1; },
|
|
/* PUSH DI */ (cpu) => { cpu.Push(cpu.di); return 1; },
|
|
/* 0x58 */ /* POP AX */ (cpu) => { cpu.ax.Word = cpu.Pop(); return 1; },
|
|
/* POP CX */ (cpu) => { cpu.cx.Word = cpu.Pop(); return 1; },
|
|
/* POP DX */ (cpu) => { cpu.dx.Word = cpu.Pop(); return 1; },
|
|
/* POP BX */ (cpu) => { cpu.bx.Word = cpu.Pop(); return 1; },
|
|
/* 0x5C */ /* POP SP */ (cpu) => { cpu.sp = cpu.Pop(); return 1; },
|
|
/* POP BP */ (cpu) => { cpu.bp = cpu.Pop(); return 1; },
|
|
/* POP SI */ (cpu) => { cpu.si = cpu.Pop(); return 1; },
|
|
/* POP DI */ (cpu) => { cpu.di = cpu.Pop(); return 1; },
|
|
|
|
/* 0x60 */ /* PUSHA */ Opcode0x60,
|
|
/* POPA */ Opcode0x61,
|
|
/* BOUND Gw E */ Opcode0x62,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* 0x64 */ /* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* 0x68 */ /* PUSH Iw */ (cpu) => { cpu.Push(cpu.ReadOpcodeIw()); return 1; },
|
|
/* IMUL Gw Ew Iw */ (cpu) => { cpu.ReadModRM(); cpu.WriteOpcodeGw((ushort)cpu.Mul16(true, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw())); return 4; },
|
|
/* PUSH Ib */ (cpu) => { cpu.Push((ushort)(sbyte)cpu.ReadOpcodeIb()); return 1; },
|
|
/* IMUL Gb Eb Ib */ (cpu) => { cpu.ReadModRM(); cpu.WriteOpcodeGw((ushort)cpu.Mul16(true, cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb())); return 4; },
|
|
/* 0x6C */ /* INSB */ Opcode0x6C,
|
|
/* INSW */ Opcode0x6D,
|
|
/* OUTSB */ Opcode0x6E,
|
|
/* OUTSW */ Opcode0x6F,
|
|
|
|
/* 0x70 */ /* JO */ (cpu) => cpu.JumpConditional(cpu.IsFlagSet(Flags.Overflow)),
|
|
/* JNO */ (cpu) => cpu.JumpConditional(!cpu.IsFlagSet(Flags.Overflow)),
|
|
/* JB */ (cpu) => cpu.JumpConditional(cpu.IsFlagSet(Flags.Carry)),
|
|
/* JNB */ (cpu) => cpu.JumpConditional(!cpu.IsFlagSet(Flags.Carry)),
|
|
/* 0x74 */ /* JZ */ (cpu) => cpu.JumpConditional(cpu.IsFlagSet(Flags.Zero)),
|
|
/* JNZ */ (cpu) => cpu.JumpConditional(!cpu.IsFlagSet(Flags.Zero)),
|
|
/* JBE */ (cpu) => cpu.JumpConditional(cpu.IsFlagSet(Flags.Carry) || cpu.IsFlagSet(Flags.Zero)),
|
|
/* JA */ (cpu) => cpu.JumpConditional(!cpu.IsFlagSet(Flags.Carry) && !cpu.IsFlagSet(Flags.Zero)),
|
|
/* 0x78 */ /* JS */ (cpu) => cpu.JumpConditional(cpu.IsFlagSet(Flags.Sign)),
|
|
/* JNS */ (cpu) => cpu.JumpConditional(!cpu.IsFlagSet(Flags.Sign)),
|
|
/* JPE */ (cpu) => cpu.JumpConditional(cpu.IsFlagSet(Flags.Parity)),
|
|
/* JPO */ (cpu) => cpu.JumpConditional(!cpu.IsFlagSet(Flags.Parity)),
|
|
/* 0x7C */ /* JL */ (cpu) => cpu.JumpConditional(!cpu.IsFlagSet(Flags.Zero) && cpu.IsFlagSet(Flags.Sign) != cpu.IsFlagSet(Flags.Overflow)),
|
|
/* JGE */ (cpu) => cpu.JumpConditional(cpu.IsFlagSet(Flags.Zero) || cpu.IsFlagSet(Flags.Sign) == cpu.IsFlagSet(Flags.Overflow)),
|
|
/* JLE */ (cpu) => cpu.JumpConditional(cpu.IsFlagSet(Flags.Zero) || cpu.IsFlagSet(Flags.Sign) != cpu.IsFlagSet(Flags.Overflow)),
|
|
/* JG */ (cpu) => cpu.JumpConditional(!cpu.IsFlagSet(Flags.Zero) && cpu.IsFlagSet(Flags.Sign) == cpu.IsFlagSet(Flags.Overflow)),
|
|
|
|
/* 0x80 */ /* GRP1 Eb Ib */ Opcode0x80,
|
|
/* GRP1 Ew Iw */ Opcode0x81,
|
|
/* GRP1 Eb Ib */ Opcode0x80,
|
|
/* GRP1 Ew Ib */ Opcode0x83,
|
|
/* 0x84 */ /* TEST Gb Eb */ (cpu) => { cpu.And8(cpu.ReadOpcodeGb(), cpu.ReadOpcodeEb()); return 1; },
|
|
/* TEST Gw Ew */ (cpu) => { cpu.And16(cpu.ReadOpcodeGw(), cpu.ReadOpcodeEw()); return 1; },
|
|
/* XCHG Gb Eb */ (cpu) => { var temp = cpu.ReadOpcodeGb(); cpu.WriteOpcodeGb(cpu.ReadOpcodeEb()); cpu.WriteOpcodeEb(temp); return 3; },
|
|
/* XCHG Gw Ew */ (cpu) => { var temp = cpu.ReadOpcodeGw(); cpu.WriteOpcodeGw(cpu.ReadOpcodeEw()); cpu.WriteOpcodeEw(temp); return 3; },
|
|
/* 0x88 */ /* MOV Eb Gb */ (cpu) => { cpu.WriteOpcodeEb(cpu.ReadOpcodeGb()); return 1; },
|
|
/* MOV Ew Gw */ (cpu) => { cpu.WriteOpcodeEw(cpu.ReadOpcodeGw()); return 1; },
|
|
/* MOV Gb Eb */ (cpu) => { cpu.WriteOpcodeGb(cpu.ReadOpcodeEb()); return 1; },
|
|
/* MOV Gw Ew */ (cpu) => { cpu.WriteOpcodeGw(cpu.ReadOpcodeEw()); return 1; },
|
|
/* 0x8C */ /* MOV Ew Sw */ (cpu) => { cpu.WriteOpcodeEw(cpu.ReadOpcodeSw()); return 1; },
|
|
/* LEA Gw M */ Opcode0x8D,
|
|
/* MOV Sw Ew */ (cpu) => { cpu.WriteOpcodeSw(cpu.ReadOpcodeEw()); return 1; },
|
|
/* POP Ew */ (cpu) => { cpu.WriteOpcodeEw(cpu.Pop()); return 1; },
|
|
|
|
/* 0x90 */ /* NOP (XCHG AX AX) */ (cpu) => { Exchange(ref cpu.ax.Word, ref cpu.ax.Word); return 3; },
|
|
/* XCHG CX AX */ (cpu) => { Exchange(ref cpu.cx.Word, ref cpu.ax.Word); return 3; },
|
|
/* XCHG DX AX */ (cpu) => { Exchange(ref cpu.dx.Word, ref cpu.ax.Word); return 3; },
|
|
/* XCHG BX AX */ (cpu) => { Exchange(ref cpu.bx.Word, ref cpu.ax.Word); return 3; },
|
|
/* 0x94 */ /* XCHG SP AX */ (cpu) => { Exchange(ref cpu.sp, ref cpu.ax.Word); return 3; },
|
|
/* XCHG BP AX */ (cpu) => { Exchange(ref cpu.bp, ref cpu.ax.Word); return 3; },
|
|
/* XCHG SI AX */ (cpu) => { Exchange(ref cpu.si, ref cpu.ax.Word); return 3; },
|
|
/* XCHG DI AX */ (cpu) => { Exchange(ref cpu.di, ref cpu.ax.Word); return 3; },
|
|
/* 0x98 */ /* CBW */ (cpu) => { cpu.ax.Word = (ushort)(sbyte)cpu.ax.Low; return 2; },
|
|
/* CWD */ (cpu) => { var value = (uint)(short)cpu.ax.Word; cpu.dx.Word = (ushort)((value >> 16) & 0xFFFF); cpu.ax.Word = (ushort)((value >> 0) & 0xFFFF); return 2; },
|
|
/* CALL Ap */ Opcode0x9A,
|
|
/* WAIT */ (cpu) => 1,
|
|
/* 0x9C */ /* PUSHF */ (cpu) => { cpu.Push((ushort)cpu.flags); return 1; },
|
|
/* POPF */ (cpu) => { cpu.flags = (Flags)cpu.Pop(); return 1; },
|
|
/* SAHF */ Opcode0x9E,
|
|
/* LAHF */ (cpu) => { cpu.ax.High = (byte)cpu.flags; return 2; },
|
|
|
|
/* 0xA0 */ /* MOV AL Aw */ (cpu) => { cpu.ax.Low = cpu.ReadMemory8(cpu.GetSegmentViaOverride(SegmentNumber.DS), cpu.ReadOpcodeIw()); return 1; },
|
|
/* MOV AX Aw */ (cpu) => { cpu.ax.Word = cpu.ReadMemory16(cpu.GetSegmentViaOverride(SegmentNumber.DS), cpu.ReadOpcodeIw()); return 1; },
|
|
/* MOV Aw AL */ (cpu) => { cpu.WriteMemory8(cpu.GetSegmentViaOverride(SegmentNumber.DS), cpu.ReadOpcodeIw(), cpu.ax.Low); return 1; },
|
|
/* MOV Aw AX */ (cpu) => { cpu.WriteMemory16(cpu.GetSegmentViaOverride(SegmentNumber.DS), cpu.ReadOpcodeIw(), cpu.ax.Word); return 1; },
|
|
/* 0xA4 */ /* MOVSB */ Opcode0xA4,
|
|
/* MOVSW */ Opcode0xA5,
|
|
/* CMPSB */ Opcode0xA6,
|
|
/* CMPSW */ Opcode0xA7,
|
|
/* 0xA8 */ /* TEST AL Ib */ (cpu) => { cpu.And8(cpu.ax.Low, cpu.ReadOpcodeIb()); return 1; },
|
|
/* TEST AX Iw */ (cpu) => { cpu.And16(cpu.ax.Word, cpu.ReadOpcodeIw()); return 1; },
|
|
/* STOSB */ Opcode0xAA,
|
|
/* STOSW */ Opcode0xAB,
|
|
/* 0xAC */ /* LODSB */ Opcode0xAC,
|
|
/* LODSW */ Opcode0xAD,
|
|
/* SCASB */ Opcode0xAE,
|
|
/* SCASW */ Opcode0xAF,
|
|
|
|
/* 0xB0 */ /* MOV AL Ib */ (cpu) => { cpu.ax.Low = cpu.ReadOpcodeIb(); return 1; },
|
|
/* MOV CL Ib */ (cpu) => { cpu.cx.Low = cpu.ReadOpcodeIb(); return 1; },
|
|
/* MOV DL Ib */ (cpu) => { cpu.dx.Low = cpu.ReadOpcodeIb(); return 1; },
|
|
/* MOV BL Ib */ (cpu) => { cpu.bx.Low = cpu.ReadOpcodeIb(); return 1; },
|
|
/* 0xB4 */ /* MOV AH Ib */ (cpu) => { cpu.ax.High = cpu.ReadOpcodeIb(); return 1; },
|
|
/* MOV CH Ib */ (cpu) => { cpu.cx.High = cpu.ReadOpcodeIb(); return 1; },
|
|
/* MOV DH Ib */ (cpu) => { cpu.dx.High = cpu.ReadOpcodeIb(); return 1; },
|
|
/* MOV BH Ib */ (cpu) => { cpu.bx.High = cpu.ReadOpcodeIb(); return 1; },
|
|
/* 0xB8 */ /* MOV AX Iw */ (cpu) => { cpu.ax.Word = cpu.ReadOpcodeIw(); return 1; },
|
|
/* MOV CX Iw */ (cpu) => { cpu.cx.Word = cpu.ReadOpcodeIw(); return 1; },
|
|
/* MOV DX Iw */ (cpu) => { cpu.dx.Word = cpu.ReadOpcodeIw(); return 1; },
|
|
/* MOV BX Iw */ (cpu) => { cpu.bx.Word = cpu.ReadOpcodeIw(); return 1; },
|
|
/* 0xBC */ /* MOV SP Iw */ (cpu) => { cpu.sp = cpu.ReadOpcodeIw(); return 1; },
|
|
/* MOV BP Iw */ (cpu) => { cpu.bp = cpu.ReadOpcodeIw(); return 1; },
|
|
/* MOV SI Iw */ (cpu) => { cpu.si = cpu.ReadOpcodeIw(); return 1; },
|
|
/* MOV DI Iw */ (cpu) => { cpu.di = cpu.ReadOpcodeIw(); return 1; },
|
|
|
|
/* 0xC0 */ /* GRP2 Eb Ib */ Opcode0xC0,
|
|
/* GRP2 Ew Ib */ Opcode0xC1,
|
|
/* RET Iw */ (cpu) => { var offset = cpu.ReadOpcodeIw(); cpu.ip = cpu.Pop(); cpu.sp += offset; return 5; },
|
|
/* RET */ (cpu) => { cpu.ip = cpu.Pop(); return 5; },
|
|
/* 0xC4 */ /* LES Gw Mp */ Opcode0xC4,
|
|
/* LDS Gw Mp */ Opcode0xC5,
|
|
/* MOV Eb Ib */ (cpu) => { cpu.ReadModRM(); cpu.WriteOpcodeEb(cpu.ReadOpcodeIb()); return 1; },
|
|
/* MOV Ew Iw */ (cpu) => { cpu.ReadModRM(); cpu.WriteOpcodeEw(cpu.ReadOpcodeIw()); return 1; },
|
|
/* 0xC8 */ /* ENTER */ Opcode0xC8,
|
|
/* LEAVE */ (cpu) => { cpu.sp = cpu.bp; cpu.bp = cpu.Pop(); return 1; },
|
|
/* RETF Iw */ (cpu) => { var offset = cpu.ReadOpcodeIw(); cpu.ip = cpu.Pop(); cpu.cs = cpu.Pop(); cpu.sp += offset; return 8; },
|
|
/* RETF */ (cpu) => { cpu.ip = cpu.Pop(); cpu.cs = cpu.Pop(); return 7; },
|
|
/* 0xCC */ /* INT 3 */ (cpu) => { cpu.Interrupt(3); return 8; },
|
|
/* INT Ib */ (cpu) => { cpu.Interrupt(cpu.ReadOpcodeIb()); return 9; },
|
|
/* INTO */ (cpu) => { if (cpu.IsFlagSet(Flags.Overflow)) cpu.Interrupt(4); return 5; },
|
|
/* IRET */ (cpu) => { cpu.ip = cpu.Pop(); cpu.cs = cpu.Pop(); cpu.flags = (Flags)cpu.Pop(); return 9; },
|
|
|
|
/* 0xD0 */ /* GRP2 Eb 1 */ Opcode0xD0,
|
|
/* GRP2 Ew 1 */ Opcode0xD1,
|
|
/* GRP2 Eb CL */ Opcode0xD2,
|
|
/* GRP2 Ew CL */ Opcode0xD3,
|
|
/* 0xD4 */ /* AAM */ Opcode0xD4,
|
|
/* AAD */ Opcode0xD5,
|
|
/* (undocumented XLAT) */ (cpu) => { cpu.ax.Low = cpu.ReadMemory8(cpu.GetSegmentViaOverride(SegmentNumber.DS), (ushort)(cpu.bx.Word + cpu.ax.Low)); return 4; },
|
|
/* XLAT */ (cpu) => { cpu.ax.Low = cpu.ReadMemory8(cpu.GetSegmentViaOverride(SegmentNumber.DS), (ushort)(cpu.bx.Word + cpu.ax.Low)); return 4; },
|
|
/* 0xD8 */ /* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* 0xDC */ /* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
|
|
/* 0xE0 */ /* LOOPNZ Jb */ (cpu) => { return cpu.LoopWhile(!cpu.IsFlagSet(Flags.Zero)); },
|
|
/* LOOPZ Jb */ (cpu) => { return cpu.LoopWhile(cpu.IsFlagSet(Flags.Zero)); },
|
|
/* LOOP Jb */ (cpu) => { return cpu.Loop(); },
|
|
/* JCXZ */ (cpu) => { return cpu.JumpConditional(cpu.cx.Word == 0); },
|
|
/* 0xE4 */ /* IN Ib AL */ (cpu) => { cpu.ax.Low = cpu.machine.ReadPort(cpu.ReadOpcodeIb()); return 6; },
|
|
/* IN Ib AX */ (cpu) => { cpu.ax.Word = cpu.ReadPort16(cpu.ReadOpcodeIb()); return 6; },
|
|
/* OUT Ib AL */ (cpu) => { cpu.machine.WritePort(cpu.ReadOpcodeIb(), cpu.ax.Low); return 6; },
|
|
/* OUT Ib AX */ (cpu) => { cpu.WritePort16(cpu.ReadOpcodeIb(), cpu.ax.Word); return 6; },
|
|
/* 0xE8 */ /* CALL Jv */ (cpu) => { var offset = cpu.ReadOpcodeIw(); cpu.Push(cpu.ip); cpu.ip += offset; return 4; },
|
|
/* JMP Jv */ (cpu) => { var offset = cpu.ReadOpcodeIw(); cpu.ip += offset; return 3; },
|
|
/* JMP Ap */ (cpu) => { var newIp = cpu.ReadOpcodeIw(); var newCs = cpu.ReadOpcodeIw(); cpu.ip = newIp; cpu.cs = newCs; return 5; },
|
|
/* JMP Jb */ (cpu) => { cpu.ip = cpu.ReadOpcodeJb(); return 3; },
|
|
/* 0xEC */ /* IN AL DX */ (cpu) => { cpu.ax.Low = cpu.machine.ReadPort(cpu.dx.Word); return 4; },
|
|
/* IN AX DX */ (cpu) => { cpu.ax.Word = cpu.ReadPort16(cpu.dx.Word); return 4; },
|
|
/* OUT DX AL */ (cpu) => { cpu.machine.WritePort(cpu.dx.Word, cpu.ax.Low); return 4; },
|
|
/* OUT DX AX */ (cpu) => { cpu.WritePort16(cpu.dx.Word, cpu.ax.Word); return 4; },
|
|
|
|
/* 0xF0 */ /* (Prefix LOCK) */ (cpu) => 0,
|
|
/* (Invalid; NOP?) */ (cpu) => 3,
|
|
/* (Prefix REPNZ) */ (cpu) => 0,
|
|
/* (Prefix REPZ) */ (cpu) => 0,
|
|
/* 0xF4 */ /* HLT */ (cpu) => { cpu.halted = true; return 8; },
|
|
/* CMC */ (cpu) => { cpu.SetClearFlagConditional(Flags.Carry, !cpu.IsFlagSet(Flags.Carry)); return 4; },
|
|
/* GRP3 Eb */ Opcode0xF6,
|
|
/* GRP3 Ew */ Opcode0xF7,
|
|
/* 0xF8 */ /* CLC */ (cpu) => { cpu.ClearFlags(Flags.Carry); return 4; },
|
|
/* STC */ (cpu) => { cpu.SetFlags(Flags.Carry); return 4; },
|
|
/* CLI */ (cpu) => { cpu.ClearFlags(Flags.InterruptEnable); return 4; },
|
|
/* STI */ (cpu) => { cpu.SetFlags(Flags.InterruptEnable); return 4; },
|
|
/* 0xFC */ /* CLD */ (cpu) => { cpu.ClearFlags(Flags.Direction); return 4; },
|
|
/* STD */ (cpu) => { cpu.SetFlags(Flags.Direction); return 4; },
|
|
/* GRP4 Eb */ Opcode0xFE,
|
|
/* GRP4 Ew */ Opcode0xFF
|
|
};
|
|
|
|
private static int Opcode0x60(V30MZ cpu)
|
|
{
|
|
/* PUSHA */
|
|
var oldSp = cpu.sp;
|
|
cpu.Push(cpu.ax.Word);
|
|
cpu.Push(cpu.cx.Word);
|
|
cpu.Push(cpu.dx.Word);
|
|
cpu.Push(cpu.bx.Word);
|
|
cpu.Push(oldSp);
|
|
cpu.Push(cpu.bp);
|
|
cpu.Push(cpu.si);
|
|
cpu.Push(cpu.di);
|
|
return 8;
|
|
}
|
|
|
|
private static int Opcode0x61(V30MZ cpu)
|
|
{
|
|
/* POPA */
|
|
cpu.di = cpu.Pop();
|
|
cpu.si = cpu.Pop();
|
|
cpu.bp = cpu.Pop();
|
|
cpu.Pop(); /* don't restore SP */
|
|
cpu.bx.Word = cpu.Pop();
|
|
cpu.dx.Word = cpu.Pop();
|
|
cpu.cx.Word = cpu.Pop();
|
|
cpu.ax.Word = cpu.Pop();
|
|
return 8;
|
|
}
|
|
|
|
private static int Opcode0x62(V30MZ cpu)
|
|
{
|
|
/* BOUND Gw E */
|
|
cpu.ReadModRM();
|
|
var lo = cpu.ReadMemory16(cpu.modRm.Segment, (ushort)(cpu.modRm.Offset + 0));
|
|
var hi = cpu.ReadMemory16(cpu.modRm.Segment, (ushort)(cpu.modRm.Offset + 2));
|
|
var reg = cpu.GetRegister16((RegisterNumber16)cpu.modRm.Mem);
|
|
if (reg < lo || reg > hi) cpu.Interrupt(5);
|
|
return 12;
|
|
}
|
|
|
|
private static int Opcode0x6C(V30MZ cpu)
|
|
{
|
|
/* INSB */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.InString(false);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.InString(false); cycles += 5; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0x6D(V30MZ cpu)
|
|
{
|
|
/* INSW */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.InString(true);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.InString(true); cycles += 5; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0x6E(V30MZ cpu)
|
|
{
|
|
/* OUTSB */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.OutString(false);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.OutString(false); cycles += 6; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0x6F(V30MZ cpu)
|
|
{
|
|
/* OUTSW */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.OutString(true);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.OutString(true); cycles += 6; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0x80(V30MZ cpu)
|
|
{
|
|
/* GRP1 Eb Ib */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ADD */ cpu.WriteOpcodeEb(cpu.Add8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x1: /* OR */ cpu.WriteOpcodeEb(cpu.Or8(cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x2: /* ADC */ cpu.WriteOpcodeEb(cpu.Add8(true, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x3: /* SBB */ cpu.WriteOpcodeEb(cpu.Sub8(true, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x4: /* AND */ cpu.WriteOpcodeEb(cpu.And8(cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x5: /* SUB */ cpu.WriteOpcodeEb(cpu.Sub8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x6: /* XOR */ cpu.WriteOpcodeEb(cpu.Xor8(cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x7: /* CMP */ cpu.Sub8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb()); cycles = 1; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0x81(V30MZ cpu)
|
|
{
|
|
/* GRP1 Ew Iw */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ADD */ cpu.WriteOpcodeEw(cpu.Add16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw())); cycles = 1; break;
|
|
case 0x1: /* OR */ cpu.WriteOpcodeEw(cpu.Or16(cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw())); cycles = 1; break;
|
|
case 0x2: /* ADC */ cpu.WriteOpcodeEw(cpu.Add16(true, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw())); cycles = 1; break;
|
|
case 0x3: /* SBB */ cpu.WriteOpcodeEw(cpu.Sub16(true, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw())); cycles = 1; break;
|
|
case 0x4: /* AND */ cpu.WriteOpcodeEw(cpu.And16(cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw())); cycles = 1; break;
|
|
case 0x5: /* SUB */ cpu.WriteOpcodeEw(cpu.Sub16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw())); cycles = 1; break;
|
|
case 0x6: /* XOR */ cpu.WriteOpcodeEw(cpu.Xor16(cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw())); cycles = 1; break;
|
|
case 0x7: /* CMP */ cpu.Sub16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw()); cycles = 1; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0x83(V30MZ cpu)
|
|
{
|
|
/* GRP1 Ew Ib */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ADD */ cpu.WriteOpcodeEw(cpu.Add16(false, cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x1: /* OR */ cpu.WriteOpcodeEw(cpu.Or16(cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x2: /* ADC */ cpu.WriteOpcodeEw(cpu.Add16(true, cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x3: /* SBB */ cpu.WriteOpcodeEw(cpu.Sub16(true, cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x4: /* AND */ cpu.WriteOpcodeEw(cpu.And16(cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x5: /* SUB */ cpu.WriteOpcodeEw(cpu.Sub16(false, cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x6: /* XOR */ cpu.WriteOpcodeEw(cpu.Xor16(cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb())); cycles = 1; break;
|
|
case 0x7: /* CMP */ cpu.Sub16(false, cpu.ReadOpcodeEw(), (ushort)(sbyte)cpu.ReadOpcodeIb()); cycles = 1; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0x8D(V30MZ cpu)
|
|
{
|
|
/* LEA Gw M */
|
|
cpu.ReadModRM();
|
|
if (cpu.modRm.Mod != ModRM.Modes.Register)
|
|
{
|
|
cpu.WriteOpcodeGw(cpu.modRm.Offset);
|
|
}
|
|
return 8;
|
|
}
|
|
|
|
private static int Opcode0x9A(V30MZ cpu)
|
|
{
|
|
/* CALL Ap */
|
|
var newIp = cpu.ReadOpcodeIw();
|
|
var newCs = cpu.ReadOpcodeIw();
|
|
|
|
cpu.Push(cpu.cs);
|
|
cpu.Push(cpu.ip);
|
|
|
|
cpu.ip = newIp;
|
|
cpu.cs = newCs;
|
|
|
|
return 9;
|
|
}
|
|
|
|
private static int Opcode0x9E(V30MZ cpu)
|
|
{
|
|
/* SAHF */
|
|
cpu.SetClearFlagConditional(Flags.Sign, ((Flags)cpu.ax.High & Flags.Sign) == Flags.Sign);
|
|
cpu.SetClearFlagConditional(Flags.Zero, ((Flags)cpu.ax.High & Flags.Zero) == Flags.Zero);
|
|
cpu.SetClearFlagConditional(Flags.Auxiliary, ((Flags)cpu.ax.High & Flags.Auxiliary) == Flags.Auxiliary);
|
|
cpu.SetClearFlagConditional(Flags.Parity, ((Flags)cpu.ax.High & Flags.Parity) == Flags.Parity);
|
|
cpu.SetClearFlagConditional(Flags.Carry, ((Flags)cpu.ax.High & Flags.Carry) == Flags.Carry);
|
|
return 4;
|
|
}
|
|
|
|
private static int Opcode0xA4(V30MZ cpu)
|
|
{
|
|
/* MOVSB */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.MoveString(false);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.MoveString(false); cycles += 5; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xA5(V30MZ cpu)
|
|
{
|
|
/* MOVSW */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.MoveString(true);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.MoveString(true); cycles += 5; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xA6(V30MZ cpu)
|
|
{
|
|
/* CMPSB */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.CompareString(false);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.CompareString(false); cycles += 4; } while (--cpu.cx.Word != 0 && (cpu.prefixRepeatOnNotEqual ? !cpu.IsFlagSet(Flags.Zero) : cpu.IsFlagSet(Flags.Zero)));
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xA7(V30MZ cpu)
|
|
{
|
|
/* CMPSW */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.CompareString(true);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.CompareString(true); cycles += 4; } while (--cpu.cx.Word != 0 && (cpu.prefixRepeatOnNotEqual ? !cpu.IsFlagSet(Flags.Zero) : cpu.IsFlagSet(Flags.Zero)));
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xAA(V30MZ cpu)
|
|
{
|
|
/* STOSB */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.StoreString(false);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.StoreString(false); cycles += 2; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xAB(V30MZ cpu)
|
|
{
|
|
/* STOSW */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.StoreString(true);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.StoreString(true); cycles += 2; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xAC(V30MZ cpu)
|
|
{
|
|
/* LODSB */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.LoadString(false);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.LoadString(false); cycles += 2; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xAD(V30MZ cpu)
|
|
{
|
|
/* LODSW */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.LoadString(true);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.LoadString(true); cycles += 2; } while (--cpu.cx.Word != 0);
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xAE(V30MZ cpu)
|
|
{
|
|
/* SCASB */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.ScanString(false);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.ScanString(false); cycles += 3; } while (--cpu.cx.Word != 0 && (cpu.prefixRepeatOnNotEqual ? !cpu.IsFlagSet(Flags.Zero) : cpu.IsFlagSet(Flags.Zero)));
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xAF(V30MZ cpu)
|
|
{
|
|
/* SCASW */
|
|
var cycles = 5;
|
|
if (!cpu.prefixHasRepeat)
|
|
cpu.ScanString(true);
|
|
else if (cpu.cx.Word != 0)
|
|
{
|
|
do { cpu.ScanString(true); cycles += 3; } while (--cpu.cx.Word != 0 && (cpu.prefixRepeatOnNotEqual ? !cpu.IsFlagSet(Flags.Zero) : cpu.IsFlagSet(Flags.Zero)));
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xC0(V30MZ cpu)
|
|
{
|
|
/* GRP2 Eb Ib */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ROL */ cpu.WriteOpcodeEb(cpu.Rol8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x1: /* ROR */ cpu.WriteOpcodeEb(cpu.Ror8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x2: /* RCL */ cpu.WriteOpcodeEb(cpu.Rol8(true, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x3: /* RCR */ cpu.WriteOpcodeEb(cpu.Ror8(true, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x4: /* SHL */ cpu.WriteOpcodeEb(cpu.Shl8(cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x5: /* SHR */ cpu.WriteOpcodeEb(cpu.Shr8(false, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x6: /* --- */ cycles = 3; break;
|
|
case 0x7: /* SAR */ cpu.WriteOpcodeEb(cpu.Shr8(true, cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xC1(V30MZ cpu)
|
|
{
|
|
/* GRP2 Ew Ib */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ROL */ cpu.WriteOpcodeEw(cpu.Rol16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x1: /* ROR */ cpu.WriteOpcodeEw(cpu.Ror16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x2: /* RCL */ cpu.WriteOpcodeEw(cpu.Rol16(true, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x3: /* RCR */ cpu.WriteOpcodeEw(cpu.Ror16(true, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x4: /* SHL */ cpu.WriteOpcodeEw(cpu.Shl16(cpu.ReadOpcodeEw(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x5: /* SHR */ cpu.WriteOpcodeEw(cpu.Shr16(false, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
case 0x6: /* --- */ cycles = 3; break;
|
|
case 0x7: /* SAR */ cpu.WriteOpcodeEw(cpu.Shr16(true, cpu.ReadOpcodeEw(), cpu.ReadOpcodeIb())); cycles = 3; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xC4(V30MZ cpu)
|
|
{
|
|
/* LES Gw Mp */
|
|
cpu.ReadModRM();
|
|
if (cpu.modRm.Mod != ModRM.Modes.Register)
|
|
{
|
|
cpu.WriteOpcodeGw(cpu.ReadOpcodeEw());
|
|
cpu.es = cpu.ReadMemory16(cpu.modRm.Segment, (ushort)(cpu.modRm.Offset + 2));
|
|
}
|
|
return 8;
|
|
}
|
|
|
|
private static int Opcode0xC5(V30MZ cpu)
|
|
{
|
|
/* LDS Gw Mp */
|
|
cpu.ReadModRM();
|
|
if (cpu.modRm.Mod != ModRM.Modes.Register)
|
|
{
|
|
cpu.WriteOpcodeGw(cpu.ReadOpcodeEw());
|
|
cpu.ds = cpu.ReadMemory16(cpu.modRm.Segment, (ushort)(cpu.modRm.Offset + 2));
|
|
}
|
|
return 8;
|
|
}
|
|
|
|
private static int Opcode0xC8(V30MZ cpu)
|
|
{
|
|
/* ENTER */
|
|
var offset = cpu.ReadOpcodeIw();
|
|
var length = (byte)(cpu.ReadOpcodeIb() & 0x1F);
|
|
|
|
cpu.Push(cpu.bp);
|
|
cpu.bp = cpu.sp;
|
|
cpu.sp -= offset;
|
|
|
|
if (length != 0)
|
|
{
|
|
for (var i = 1; i < length; i++)
|
|
cpu.Push(cpu.ReadMemory16(cpu.ss, (ushort)(cpu.bp - i * 2)));
|
|
cpu.Push(cpu.bp);
|
|
}
|
|
return 7;
|
|
}
|
|
|
|
private static int Opcode0xD0(V30MZ cpu)
|
|
{
|
|
/* GRP2 Eb 1 */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ROL */ cpu.WriteOpcodeEb(cpu.Rol8(false, cpu.ReadOpcodeEb(), 1)); cycles = 1; break;
|
|
case 0x1: /* ROR */ cpu.WriteOpcodeEb(cpu.Ror8(false, cpu.ReadOpcodeEb(), 1)); cycles = 1; break;
|
|
case 0x2: /* RCL */ cpu.WriteOpcodeEb(cpu.Rol8(true, cpu.ReadOpcodeEb(), 1)); cycles = 1; break;
|
|
case 0x3: /* RCR */ cpu.WriteOpcodeEb(cpu.Ror8(true, cpu.ReadOpcodeEb(), 1)); cycles = 1; break;
|
|
case 0x4: /* SHL */ cpu.WriteOpcodeEb(cpu.Shl8(cpu.ReadOpcodeEb(), 1)); cycles = 1; break;
|
|
case 0x5: /* SHR */ cpu.WriteOpcodeEb(cpu.Shr8(false, cpu.ReadOpcodeEb(), 1)); cycles = 1; break;
|
|
case 0x6: /* --- */ cycles = 3; break;
|
|
case 0x7: /* SAR */ cpu.WriteOpcodeEb(cpu.Shr8(true, cpu.ReadOpcodeEb(), 1)); cycles = 1; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xD1(V30MZ cpu)
|
|
{
|
|
/* GRP2 Ew 1 */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ROL */ cpu.WriteOpcodeEw(cpu.Rol16(false, cpu.ReadOpcodeEw(), 1)); cycles = 1; break;
|
|
case 0x1: /* ROR */ cpu.WriteOpcodeEw(cpu.Ror16(false, cpu.ReadOpcodeEw(), 1)); cycles = 1; break;
|
|
case 0x2: /* RCL */ cpu.WriteOpcodeEw(cpu.Rol16(true, cpu.ReadOpcodeEw(), 1)); cycles = 1; break;
|
|
case 0x3: /* RCR */ cpu.WriteOpcodeEw(cpu.Ror16(true, cpu.ReadOpcodeEw(), 1)); cycles = 1; break;
|
|
case 0x4: /* SHL */ cpu.WriteOpcodeEw(cpu.Shl16(cpu.ReadOpcodeEw(), 1)); cycles = 1; break;
|
|
case 0x5: /* SHR */ cpu.WriteOpcodeEw(cpu.Shr16(false, cpu.ReadOpcodeEw(), 1)); cycles = 1; break;
|
|
case 0x6: /* --- */ cycles = 3; break;
|
|
case 0x7: /* SAR */ cpu.WriteOpcodeEw(cpu.Shr16(true, cpu.ReadOpcodeEw(), 1)); cycles = 1; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xD2(V30MZ cpu)
|
|
{
|
|
/* GRP2 Eb CL */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ROL */ cpu.WriteOpcodeEb(cpu.Rol8(false, cpu.ReadOpcodeEb(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x1: /* ROR */ cpu.WriteOpcodeEb(cpu.Ror8(false, cpu.ReadOpcodeEb(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x2: /* RCL */ cpu.WriteOpcodeEb(cpu.Rol8(true, cpu.ReadOpcodeEb(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x3: /* RCR */ cpu.WriteOpcodeEb(cpu.Ror8(true, cpu.ReadOpcodeEb(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x4: /* SHL */ cpu.WriteOpcodeEb(cpu.Shl8(cpu.ReadOpcodeEb(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x5: /* SHR */ cpu.WriteOpcodeEb(cpu.Shr8(false, cpu.ReadOpcodeEb(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x6: /* --- */ cycles = 3; break;
|
|
case 0x7: /* SAR */ cpu.WriteOpcodeEb(cpu.Shr8(true, cpu.ReadOpcodeEb(), cpu.cx.Low)); cycles = 3; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xD3(V30MZ cpu)
|
|
{
|
|
/* GRP2 Ew CL */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* ROL */ cpu.WriteOpcodeEw(cpu.Rol16(false, cpu.ReadOpcodeEw(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x1: /* ROR */ cpu.WriteOpcodeEw(cpu.Ror16(false, cpu.ReadOpcodeEw(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x2: /* RCL */ cpu.WriteOpcodeEw(cpu.Rol16(true, cpu.ReadOpcodeEw(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x3: /* RCR */ cpu.WriteOpcodeEw(cpu.Ror16(true, cpu.ReadOpcodeEw(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x4: /* SHL */ cpu.WriteOpcodeEw(cpu.Shl16(cpu.ReadOpcodeEw(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x5: /* SHR */ cpu.WriteOpcodeEw(cpu.Shr16(false, cpu.ReadOpcodeEw(), cpu.cx.Low)); cycles = 3; break;
|
|
case 0x6: /* --- */ cycles = 3; break;
|
|
case 0x7: /* SAR */ cpu.WriteOpcodeEw(cpu.Shr16(true, cpu.ReadOpcodeEw(), cpu.cx.Low)); cycles = 3; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
/* NOTE: AAM/AAD: While NEC V20/V30 do ignore immediate & always use base 10 (1), V30MZ does *not* ignore the immediate (2)
|
|
* (1): https://www.vcfed.org/forum/forum/technical-support/vintage-computer-programming/36551/
|
|
* (2): https://github.com/xdanieldzd/StoicGoose/issues/9
|
|
*/
|
|
|
|
private static int Opcode0xD4(V30MZ cpu)
|
|
{
|
|
/* AAM */
|
|
var value = cpu.ReadOpcodeIb();
|
|
if (value == 0)
|
|
{
|
|
/* Division-by-zero exception */
|
|
cpu.Interrupt(0);
|
|
}
|
|
else
|
|
{
|
|
cpu.ax.High = (byte)(cpu.ax.Low / value);
|
|
cpu.ax.Low = (byte)(cpu.ax.Low % value);
|
|
cpu.SetClearFlagConditional(Flags.Parity, CalculateParity(cpu.ax.Low));
|
|
cpu.SetClearFlagConditional(Flags.Zero, (cpu.ax.Word & 0xFFFF) == 0);
|
|
cpu.SetClearFlagConditional(Flags.Sign, (cpu.ax.Word & 0x8000) != 0);
|
|
}
|
|
return 16;
|
|
}
|
|
|
|
private static int Opcode0xD5(V30MZ cpu)
|
|
{
|
|
/* AAD */
|
|
var value = cpu.ReadOpcodeIb();
|
|
cpu.ax.Low = (byte)(cpu.ax.High * value + cpu.ax.Low);
|
|
cpu.ax.High = 0;
|
|
cpu.SetClearFlagConditional(Flags.Parity, CalculateParity(cpu.ax.Low));
|
|
cpu.SetClearFlagConditional(Flags.Zero, (cpu.ax.Word & 0xFFFF) == 0);
|
|
cpu.SetClearFlagConditional(Flags.Sign, (cpu.ax.Word & 0x8000) != 0);
|
|
return 6;
|
|
}
|
|
|
|
private static int Opcode0xF6(V30MZ cpu)
|
|
{
|
|
/* GRP3 Eb */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* TEST */ cpu.And8(cpu.ReadOpcodeEb(), cpu.ReadOpcodeIb()); cycles = 1; break;
|
|
case 0x1: /* --- */ cycles = 3; break;
|
|
case 0x2: /* NOT */ cpu.WriteOpcodeEb((byte)~cpu.ReadOpcodeEb()); cycles = 1; break;
|
|
case 0x3: /* NEG */ cpu.WriteOpcodeEb(cpu.Neg8(cpu.ReadOpcodeEb())); cycles = 1; break;
|
|
case 0x4: /* MUL */ cpu.ax.Word = cpu.Mul8(false, cpu.ax.Low, cpu.ReadOpcodeEb()); cycles = 3; break;
|
|
case 0x5: /* IMUL */ cpu.ax.Word = cpu.Mul8(true, cpu.ax.Low, cpu.ReadOpcodeEb()); cycles = 3; break;
|
|
case 0x6: /* DIV */ cpu.ax.Word = cpu.Div8(false, cpu.ax.Word, cpu.ReadOpcodeEb()); cycles = 15; break;
|
|
case 0x7: /* IDIV */ cpu.ax.Word = cpu.Div8(true, cpu.ax.Word, cpu.ReadOpcodeEb()); cycles = 17; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xF7(V30MZ cpu)
|
|
{
|
|
/* GRP3 Ew */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* TEST */ cpu.And16(cpu.ReadOpcodeEw(), cpu.ReadOpcodeIw()); cycles = 1; break;
|
|
case 0x1: /* --- */ cycles = 3; break;
|
|
case 0x2: /* NOT */ cpu.WriteOpcodeEw((ushort)~cpu.ReadOpcodeEw()); cycles = 1; break;
|
|
case 0x3: /* NEG */ cpu.WriteOpcodeEw(cpu.Neg16(cpu.ReadOpcodeEw())); cycles = 1; break;
|
|
case 0x4: /* MUL */ { var result = cpu.Mul16(false, cpu.ax.Word, cpu.ReadOpcodeEw()); cpu.dx.Word = (ushort)((result >> 16) & 0xFFFF); cpu.ax.Word = (ushort)((result >> 0) & 0xFFFF); cycles = 3; } break;
|
|
case 0x5: /* IMUL */ { var result = cpu.Mul16(true, cpu.ax.Word, cpu.ReadOpcodeEw()); cpu.dx.Word = (ushort)((result >> 16) & 0xFFFF); cpu.ax.Word = (ushort)((result >> 0) & 0xFFFF); cycles = 3; } break;
|
|
case 0x6: /* DIV */ { var result = cpu.Div16(false, (uint)(cpu.dx.Word << 16 | cpu.ax.Word), cpu.ReadOpcodeEw()); cpu.dx.Word = (ushort)((result >> 16) & 0xFFFF); cpu.ax.Word = (ushort)((result >> 0) & 0xFFFF); cycles = 23; } break;
|
|
case 0x7: /* IDIV */ { var result = cpu.Div16(true, (uint)(cpu.dx.Word << 16 | cpu.ax.Word), cpu.ReadOpcodeEw()); cpu.dx.Word = (ushort)((result >> 16) & 0xFFFF); cpu.ax.Word = (ushort)((result >> 0) & 0xFFFF); cycles = 24; } break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xFE(V30MZ cpu)
|
|
{
|
|
/* GRP4 Eb */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* INC */ cpu.WriteOpcodeEb(cpu.Inc8(cpu.ReadOpcodeEb())); cycles = 1; break;
|
|
case 0x1: /* DEC */ cpu.WriteOpcodeEb(cpu.Dec8(cpu.ReadOpcodeEb())); cycles = 1; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
private static int Opcode0xFF(V30MZ cpu)
|
|
{
|
|
/* GRP4 Ew */
|
|
int cycles;
|
|
cpu.ReadModRM();
|
|
switch (cpu.modRm.Reg)
|
|
{
|
|
case 0x0: /* INC */ cpu.WriteOpcodeEw(cpu.Inc16(cpu.ReadOpcodeEw())); cycles = 1; break;
|
|
case 0x1: /* DEC */ cpu.WriteOpcodeEw(cpu.Dec16(cpu.ReadOpcodeEw())); cycles = 1; break;
|
|
case 0x2: /* CALL */
|
|
{
|
|
var offset = cpu.ReadOpcodeEw();
|
|
cpu.Push(cpu.ip);
|
|
cpu.ip = offset;
|
|
cycles = 5;
|
|
}
|
|
break;
|
|
case 0x3: /* CALL Mp */
|
|
{
|
|
if (cpu.modRm.Mod != ModRM.Modes.Register)
|
|
{
|
|
var offset = cpu.ReadMemory16(cpu.modRm.Segment, (ushort)(cpu.modRm.Offset + 0));
|
|
var segment = cpu.ReadMemory16(cpu.modRm.Segment, (ushort)(cpu.modRm.Offset + 2));
|
|
cpu.Push(cpu.cs);
|
|
cpu.Push(cpu.ip);
|
|
cpu.cs = segment;
|
|
cpu.ip = offset;
|
|
}
|
|
cycles = 11;
|
|
}
|
|
break;
|
|
case 0x4: /* JMP */ cpu.ip = cpu.ReadOpcodeEw(); cycles = 4; break;
|
|
case 0x5: /* JMP Mp */
|
|
{
|
|
if (cpu.modRm.Mod != ModRM.Modes.Register)
|
|
{
|
|
cpu.ip = cpu.ReadMemory16(cpu.modRm.Segment, (ushort)(cpu.modRm.Offset + 0));
|
|
cpu.cs = cpu.ReadMemory16(cpu.modRm.Segment, (ushort)(cpu.modRm.Offset + 2));
|
|
}
|
|
cycles = 9;
|
|
}
|
|
break;
|
|
case 0x6: /* PUSH */ cpu.Push(cpu.ReadOpcodeEw()); cycles = 3; break;
|
|
case 0x7: /* --- */ cycles = 3; break;
|
|
default: throw new Exception("Invalid opcode");
|
|
}
|
|
return cycles;
|
|
}
|
|
}
|
|
}
|