From 30c2c0f050ad083e70a7e75b521c112465c212a2 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 7 Jan 2021 21:59:00 -0500
Subject: [PATCH] Attempts to complete operand recognition.

---
 Processors/Decoders/x86/x86.cpp | 55 ++++++++++++++++++++++++++-------
 Processors/Decoders/x86/x86.hpp | 16 ++++++++--
 2 files changed, 58 insertions(+), 13 deletions(-)

diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp
index 2837cec08..236289bf9 100644
--- a/Processors/Decoders/x86/x86.cpp
+++ b/Processors/Decoders/x86/x86.cpp
@@ -285,9 +285,9 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) {
 			case 0xe7: AddrReg(OUT, AX, 2, 1);	break;
 
 			case 0xe8: RegData(CALLD, None, 2);	break;
-			case 0xe9: RegData(JMP, None, 2);	break;
-			case 0xea: Far(JMP);				break;
-			case 0xeb: Jump(JMP);				break;
+			case 0xe9: RegData(JMPN, None, 2);	break;
+			case 0xea: Far(JMPF);				break;
+			case 0xeb: Jump(JMPN);				break;
 
 			case 0xec: Complete(IN, DX, AL, 1);		break;
 			case 0xed: Complete(IN, DX, AX, 1);		break;
@@ -306,14 +306,8 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) {
 			case 0xfc: Complete(CLD, None, None, 1);	break;
 			case 0xfd: Complete(STD, None, None, 1);	break;
 
-			/*
-				Unimplemented (but should be):
-
-					0xfe, 0xff
-
-				[and consider which others are unused but seem to be
-				known to consume a second byte?]
-			*/
+			case 0xfe: MemRegReg(Invalid, MemRegINC_DEC, 1);		break;
+			case 0xff: MemRegReg(Invalid, MemRegINC_to_PUSH, 1);	break;
 
 			// Other prefix bytes.
 			case 0xf0:	lock_ = true;						break;
@@ -444,6 +438,45 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) {
 				}
 			break;
 
+			case ModRegRMFormat::MemRegINC_DEC:
+				source_ = destination_ = memreg;
+
+				switch(reg) {
+					default:
+						reset_parsing();
+					return Instruction(consumed_);
+
+					case 0:		operation_ = Operation::INC;	break;
+					case 1:		operation_ = Operation::DEC;	break;
+				}
+			break;
+
+			case ModRegRMFormat::MemRegINC_to_PUSH:
+				source_ = destination_ = memreg;
+
+				switch(reg) {
+					default:
+						reset_parsing();
+					return Instruction(consumed_);
+
+					case 0:		operation_ = Operation::INC;	break;
+					case 1:		operation_ = Operation::DEC;	break;
+					case 2:		operation_ = Operation::CALLN;	break;
+					case 3:
+						operation_ = Operation::CALLF;
+						operand_size_ = 4;
+						source_ = Source::Immediate;
+					break;
+					case 4:		operation_ = Operation::JMPN;	break;
+					case 5:
+						operation_ = Operation::JMPF;
+						operand_size_ = 4;
+						source_ = Source::Immediate;
+					break;
+					case 6:	operation_ = Operation::PUSH;		break;
+				}
+			break;
+
 			case ModRegRMFormat::MemRegPOP:
 				source_ = destination_ = memreg;
 
diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp
index bb6a7b312..7f43b8ca9 100644
--- a/Processors/Decoders/x86/x86.hpp
+++ b/Processors/Decoders/x86/x86.hpp
@@ -41,7 +41,7 @@ enum class Operation: uint8_t {
 	CALLF,
 	/// Displacement call; followed by a 16-bit operand providing a call offset.
 	CALLD,
-	/* TODO: other CALLs */
+	CALLN,
 	/// Convert byte into word; source will be AL, destination will be AH.
 	CBW,
 	/// Clear carry flag; no source or destination provided.
@@ -68,7 +68,9 @@ enum class Operation: uint8_t {
 	INC, INT, INT3, INTO, IRET,
 	JO,	JNO,	JB, JNB,	JE, JNE,	JBE, JNBE,
 	JS, JNS,	JP, JNP,	JL, JNL,	JLE, JNLE,
-	JMP, JCXZ,
+	JMPN,
+	JMPF,
+	JCXZ,
 	LAHF, LDS, LEA,
 	LODS, LOOPE, LOOPNE, MOV, MOVS, MUL, NEG, NOP, NOT, OR, OUT,
 	POP, POPF, PUSH, PUSHF, RCL, RCR, REP, ROL, ROR, SAHF,
@@ -210,6 +212,16 @@ struct Decoder {
 			// register based on the reg field.
 			SegReg,
 
+			// Parse for mode and register/memory fields, populating the
+			// source_ and destination_ fields with the result. Uses the
+			// 'register' field to pick INC or DEC.
+			MemRegINC_DEC,
+
+			// Parse for mode and register/memory fields, populating the
+			// source_ and destination_ fields with the result. Uses the
+			// 'register' field to pick from INC/DEC/CALL/JMP/PUSH, altering
+			// the source to ::Immediate and setting an operand size if necessary.
+			MemRegINC_to_PUSH,
 		} modregrm_format_ = ModRegRMFormat::MemReg_Reg;
 
 		// Ephemeral decoding state.