From ed63e7ea75cf8a1573d0c5febca8e53ad9a003e8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 30 Dec 2020 22:55:59 -0500 Subject: [PATCH 01/52] Starts building out a PowerPC decoder. --- .../Clock Signal.xcodeproj/project.pbxproj | 24 +++ Processors/Decoders/PowerPC/PowerPC.cpp | 174 ++++++++++++++++++ Processors/Decoders/PowerPC/PowerPC.hpp | 86 +++++++++ 3 files changed, 284 insertions(+) create mode 100644 Processors/Decoders/PowerPC/PowerPC.cpp create mode 100644 Processors/Decoders/PowerPC/PowerPC.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index fb227632f..f48e8ec52 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -844,6 +844,8 @@ 4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; }; 4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsPanel.swift */; }; 4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; }; + 4BDDBBD7259D757800CEFF58 /* PowerPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBBD5259D757800CEFF58 /* PowerPC.cpp */; }; + 4BDDBBD8259D757800CEFF58 /* PowerPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBBD5259D757800CEFF58 /* PowerPC.cpp */; }; 4BE0A3EE237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; }; 4BE0A3EF237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; }; 4BE211DE253E4E4800435408 /* 65C02_no_Rockwell_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */; }; @@ -1767,6 +1769,8 @@ 4BDB3D8522833321002D3CEE /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = ""; }; 4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMMachine.hpp; sourceTree = ""; }; 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MachineCycleTests.swift; sourceTree = ""; }; + 4BDDBBD5259D757800CEFF58 /* PowerPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PowerPC.cpp; sourceTree = ""; }; + 4BDDBBD6259D757800CEFF58 /* PowerPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PowerPC.hpp; sourceTree = ""; }; 4BE0A3EC237BB170002AB46F /* ST.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ST.cpp; sourceTree = ""; }; 4BE0A3ED237BB170002AB46F /* ST.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ST.hpp; sourceTree = ""; }; 4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 65C02_no_Rockwell_test.bin; path = "Klaus Dormann/65C02_no_Rockwell_test.bin"; sourceTree = ""; }; @@ -3539,6 +3543,7 @@ 4B4DEC15252BFA9C004583AC /* 6502Esque */, 4BF8D4CC251C0C9C00BBE21B /* 65816 */, 4BFF1D332233778C00838EA1 /* 68000 */, + 4BDDBBD3259D757800CEFF58 /* Decoders */, 4B77069E1EC9045B0053B588 /* Z80 */, ); name = Processors; @@ -3881,6 +3886,23 @@ path = 5380; sourceTree = ""; }; + 4BDDBBD3259D757800CEFF58 /* Decoders */ = { + isa = PBXGroup; + children = ( + 4BDDBBD4259D757800CEFF58 /* PowerPC */, + ); + path = Decoders; + sourceTree = ""; + }; + 4BDDBBD4259D757800CEFF58 /* PowerPC */ = { + isa = PBXGroup; + children = ( + 4BDDBBD5259D757800CEFF58 /* PowerPC.cpp */, + 4BDDBBD6259D757800CEFF58 /* PowerPC.hpp */, + ); + path = PowerPC; + sourceTree = ""; + }; 4BE5F85A1C3E1C2500C43F01 /* Resources */ = { isa = PBXGroup; children = ( @@ -4607,6 +4629,7 @@ 4BD67DD1209BF27B00AB2146 /* Encoder.cpp in Sources */, 4B89451F201967B4007DE474 /* Tape.cpp in Sources */, 4B055AA81FAE85EF0060FFFF /* Shifter.cpp in Sources */, + 4BDDBBD8259D757800CEFF58 /* PowerPC.cpp in Sources */, 4B8318B422D3E546006DB630 /* DriveSpeedAccumulator.cpp in Sources */, 4B055AC81FAE9AFB0060FFFF /* C1540.cpp in Sources */, 4B055A8F1FAE85A90060FFFF /* FileHolder.cpp in Sources */, @@ -4749,6 +4772,7 @@ 4B45189F1F75FD1C00926311 /* AcornADF.cpp in Sources */, 4B7BA03023C2B19C00B98D9E /* Jasmin.cpp in Sources */, 4B7136911F789C93008B8ED9 /* SegmentParser.cpp in Sources */, + 4BDDBBD7259D757800CEFF58 /* PowerPC.cpp in Sources */, 4B4518A21F75FD1C00926311 /* G64.cpp in Sources */, 4B89452C201967B4007DE474 /* Tape.cpp in Sources */, 4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */, diff --git a/Processors/Decoders/PowerPC/PowerPC.cpp b/Processors/Decoders/PowerPC/PowerPC.cpp new file mode 100644 index 000000000..6e9b70f82 --- /dev/null +++ b/Processors/Decoders/PowerPC/PowerPC.cpp @@ -0,0 +1,174 @@ +// +// PowerPC.cpp +// Clock Signal +// +// Created by Thomas Harte on 12/30/20. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#include "PowerPC.hpp" + +using namespace CPU::Decoder::PowerPC; + +Decoder::Decoder(Model model) : model_(model) {} + +Instruction Decoder::decode(uint32_t opcode) { + switch(opcode >> 26) { + case 31: + const uint8_t dest = (opcode >> 21) & 0x1f; + const uint8_t a = (opcode >> 16) & 0x1f; + const uint8_t b = (opcode >> 11) & 0x1f; + +#define OECase(x) case x: case 0x200 + x + switch((opcode >> 1) & 0x3ff) { + case 0: + // cmp; 601 10-26 + break; + case 4: + // tw; 601 10-214 + break; + OECase(8): + // subfcx; 601 10-207 + break; +// case 9: +// // mulhdux +// break; + OECase(10): + // addcx; 601 10-9 + break; + case 11: + // mulhwux; 601 10-142 + break; + case 19: + // mfcr; 601 10-122 + break; + case 20: + // lwarx + break; + case 21: + // ldx + break; + // lwzx + // slwx + // cntlzwx + // sldx + // andx + // ampl + // subfx + // ldux + // dcbst + // lwzux + // cntlzdx + // andcx + // td + // mulhx + // mulhwx + // mfmsr + // ldarx + // dcbf + // lbzx + // negx + // norx + // subfex + // adex + // mtcrf + // mtmsr + // stfx + // stwcx. + // stwx + // stdux + // stwux + // subfzex + // addzex + // mtsr + // stdcx. + // stbx + // subfmex + // mulld + // addmex + // mullwx + // mtsrin + // scbtst + // stbux + // addx + // dcbt + // lhzx + // eqvx + // tlbie + // eciwx + // lhzux + // xorx + // mfspr + // lwax + // lhax + // lbia + // mftb + // lwaux + // lhaux + // sthx + // orcx + // sradix + // slbie + // ecowx + // sthux + // orx + // divdux + // divwux + // mtspr + // dcbi + // nandx + // divdx + // divwx + // slbia + // mcrxr + // lswx + // lwbrx + // lfsx + // srwx + // srdx + // tlbsync + // lfsux + // mfsr + // lswi + // sync + // lfdx + // lfdux + // mfsrin + // stswx + // stwbrx + // stfsx + // stfsux + // stswi + // stfdx + // stfdux + // lhbrx + // srawx + // sradx + // srawix + // eieio + // ethbrx + // extshx + // extsbx + // lcbi + // stfiwx + // extsw + // dcbz + OECase(138): + // addex + break; + OECase(266): + // addx + break; + case 28: + // andx + break; + case 60: + // andcx + break; + } +#undef OECase + break; + } + + return Instruction(); +} diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp new file mode 100644 index 000000000..80a709725 --- /dev/null +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -0,0 +1,86 @@ +// +// PowerPC.hpp +// Clock Signal +// +// Created by Thomas Harte on 12/30/20. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef PowerPC_hpp +#define PowerPC_hpp + +#include + +namespace CPU { +namespace Decoder { +namespace PowerPC { + +enum class Model { + MPC601, +}; + +// TODO: complete the following table. +enum class Operation: uint8_t { + Undefined, + + // These 601-exclusive instructions; a lot of them are carry-overs + // from POWER. + absx, clcs, divx, divsx, dozx, dozi, lscbxx, maskgx, maskirx, mulx, + nabsx, rlmix, rribx, slex, sleqx, sliqx, slliqx, sllqx, slqx, + sraiqx, sraqx, srex, sreax, sreqx, sriqx, srliqx, srlqx, srqx, + + // 32- and 64-bit PowerPC instructions. + addx, addcx, addex, addi, addic, addic_, addis, addmex, addzex, andx, + andcx, andi_, andis_, bx, bcx, bcctrx, bclrx, cmp, cmpi, cmpl, cmpli, + cntlzwx, crand, crandc, creqv, crnand, crnor, cror, crorc, crxor, dcbf, + dcbst, dcbt, dcbtst, dcbz, divwx, divwux, eciwx, ecowx, eieio, eqvx, + extsbx, extshx, fabsx, faddx, faddsx, fcmpo, fcmpu, fctiwx, fctiwzx, + fdivx, fdivsx, fmaddx, fmaddsx, fmrx, fmsubx, fmsubsx, fmulx, fmulsx, + fnabsx, fnegx, fnmaddx, fnmaddsx, fnmsubx, fnmsubsx, frspx, fsubx, fsubsx, + icbi, isync, lbz, lbzu, lbzux, lbzx, lfd, lfdu, lfdux, lfdx, lfs, lfsu, + lfsux, lfsx, lha, lhau, lhaux, lhax, lhbrx, lhz, lhzu, lhzux, lhzx, lmw, + lswi, lswx, lwarx, lwbrx, lwz, lwzu, lwzux, lwzx, mcrf, mcrfs, mcrxr, + mfcr, mffsx, mfmsr, mfspr, mfsr, mfsrin, mtcrf, mtfsb0x, mtfsb1x, mtfsfx, + mtfsfix, mtmsr, mtspr, mtsr, mtsrin, mulhwx, mulhwux, mulli, mullwx, + nandx, negx, norx, orx, orcx, ori, oris, rfi, rlwimix, rlwinmx, rlwnmx, + sc, slwx, srawx, srawix, srwx, stb, stbu, stbux, stbx, stfd, stfdu, + stfdux, stfdx, stfs, stfsu, stfsux, stfsx, sth, sthbrx, sthu, sthux, sthx, + stmw, stswi, stswx, stw, stwbrx, stwcx_, stwu, stwux, stwx, subfx, subfcx, + subfex, subfic, subfmex, subfzex, sync, tlbie, tw, twi, xorx, xori, xoris, + + // 32-bit, supervisor level. + dcbi, + + // Optional. + fresx, frsqrtex, fselx, fsqrtx, frsqrtsx, slbia, slbie, + + // 64-bit only PowerPC instructions. + cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx +}; + +struct Instruction { + Operation operation = Operation::Undefined; + + // + + Instruction() {} + Instruction(Operation operation) : operation(operation) {} +}; + +struct Decoder { + public: + Decoder(Model model); + + Instruction decode(uint32_t opcode); + + private: + Model model_; +}; + +} +} +} + +#include + +#endif /* PowerPC_hpp */ From 233a69a1d875d30802ad7419a9d6fcad85efd816 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 31 Dec 2020 16:02:52 -0500 Subject: [PATCH 02/52] Decodes operations for the simplest 45. --- Processors/Decoders/PowerPC/PowerPC.cpp | 222 +++++++----------------- Processors/Decoders/PowerPC/PowerPC.hpp | 19 +- 2 files changed, 79 insertions(+), 162 deletions(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.cpp b/Processors/Decoders/PowerPC/PowerPC.cpp index 6e9b70f82..e21c900c7 100644 --- a/Processors/Decoders/PowerPC/PowerPC.cpp +++ b/Processors/Decoders/PowerPC/PowerPC.cpp @@ -13,162 +13,70 @@ using namespace CPU::Decoder::PowerPC; Decoder::Decoder(Model model) : model_(model) {} Instruction Decoder::decode(uint32_t opcode) { - switch(opcode >> 26) { - case 31: - const uint8_t dest = (opcode >> 21) & 0x1f; - const uint8_t a = (opcode >> 16) & 0x1f; - const uint8_t b = (opcode >> 11) & 0x1f; - -#define OECase(x) case x: case 0x200 + x - switch((opcode >> 1) & 0x3ff) { - case 0: - // cmp; 601 10-26 - break; - case 4: - // tw; 601 10-214 - break; - OECase(8): - // subfcx; 601 10-207 - break; -// case 9: -// // mulhdux -// break; - OECase(10): - // addcx; 601 10-9 - break; - case 11: - // mulhwux; 601 10-142 - break; - case 19: - // mfcr; 601 10-122 - break; - case 20: - // lwarx - break; - case 21: - // ldx - break; - // lwzx - // slwx - // cntlzwx - // sldx - // andx - // ampl - // subfx - // ldux - // dcbst - // lwzux - // cntlzdx - // andcx - // td - // mulhx - // mulhwx - // mfmsr - // ldarx - // dcbf - // lbzx - // negx - // norx - // subfex - // adex - // mtcrf - // mtmsr - // stfx - // stwcx. - // stwx - // stdux - // stwux - // subfzex - // addzex - // mtsr - // stdcx. - // stbx - // subfmex - // mulld - // addmex - // mullwx - // mtsrin - // scbtst - // stbux - // addx - // dcbt - // lhzx - // eqvx - // tlbie - // eciwx - // lhzux - // xorx - // mfspr - // lwax - // lhax - // lbia - // mftb - // lwaux - // lhaux - // sthx - // orcx - // sradix - // slbie - // ecowx - // sthux - // orx - // divdux - // divwux - // mtspr - // dcbi - // nandx - // divdx - // divwx - // slbia - // mcrxr - // lswx - // lwbrx - // lfsx - // srwx - // srdx - // tlbsync - // lfsux - // mfsr - // lswi - // sync - // lfdx - // lfdux - // mfsrin - // stswx - // stwbrx - // stfsx - // stfsux - // stswi - // stfdx - // stfdux - // lhbrx - // srawx - // sradx - // srawix - // eieio - // ethbrx - // extshx - // extsbx - // lcbi - // stfiwx - // extsw - // dcbz - OECase(138): - // addex - break; - OECase(266): - // addx - break; - case 28: - // andx - break; - case 60: - // andcx - break; - } -#undef OECase - break; - } + // Quick bluffer's guide to PowerPC instruction encoding: + // + // There is a six-bit field at the very top of the instruction. + // Sometimes that fully identifies an instruction, but usually + // it doesn't. + // + // There is an addition 9- or 10-bit field starting one bit above + // least significant that disambiguates the rest. Strictly speaking + // it's a 10-bit field, but the mnemonics for many instructions treat + // it as a 9-bit field with a flag at the top. + // + // I've decided to hew directly to the mnemonics. - return Instruction(); +#define Bind(mask, operation) case mask: return Instruction(Operation::operation, opcode); +#define BindConditional(condition, mask, operation) \ + case mask: \ + if(condition()) return Instruction(Operation::operation, opcode); \ + return Instruction(opcode); + +#define Six(x) (unsigned(x) << 26) + + // First pass: weed out all those instructions identified entirely by the + // top six bits. + switch(opcode & Six(0b111111)) { + default: break; + + BindConditional(is64bit, Six(0b000010), tdi); + + Bind(Six(0b000011), twi); + Bind(Six(0b000111), mulli); + Bind(Six(0b001000), subfic); + Bind(Six(0b001100), addic); Bind(Six(0b001101), addic_); + Bind(Six(0b001110), addi); Bind(Six(0b001111), addis); + Bind(Six(0b010000), bcx); + Bind(Six(0b010010), bx); + Bind(Six(0b010100), rlwimix); + Bind(Six(0b010101), rlwinmx); + Bind(Six(0b010111), rlwnmx); + + Bind(Six(0b011000), ori); Bind(Six(0b011001), oris); + Bind(Six(0b011010), xori); Bind(Six(0b011011), xoris); + Bind(Six(0b011100), andi_); Bind(Six(0b011101), andis_); + Bind(Six(0b100000), lwz); Bind(Six(0b100001), lwzu); + Bind(Six(0b100010), lbz); Bind(Six(0b100011), lbzu); + Bind(Six(0b100100), stw); Bind(Six(0b100101), stwu); + Bind(Six(0b100110), stb); Bind(Six(0b100111), stbu); + Bind(Six(0b101000), lhz); Bind(Six(0b101001), lhzu); + Bind(Six(0b101010), lha); Bind(Six(0b101011), lhau); + Bind(Six(0b101100), sth); Bind(Six(0b101101), sthu); + Bind(Six(0b101110), lmw); Bind(Six(0b101111), stmw); + Bind(Six(0b110000), lfs); Bind(Six(0b110001), lfsu); + Bind(Six(0b110010), lfd); Bind(Six(0b110011), lfdu); + Bind(Six(0b110100), stfs); Bind(Six(0b110101), stfsu); + Bind(Six(0b110110), stfd); Bind(Six(0b110111), stfdu); + + // Assumed below here: reserved bits can be ignored. + // This might need to be a function of CPU model. + Bind(Six(0b001010), cmpli); Bind(Six(0b001011), cmpi); + } + +#undef Six + +#undef Bind +#undef BindConditional + + return Instruction(opcode); } diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 80a709725..f9f7e9761 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -55,16 +55,17 @@ enum class Operation: uint8_t { fresx, frsqrtex, fselx, fsqrtx, frsqrtsx, slbia, slbie, // 64-bit only PowerPC instructions. - cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx + cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx, tdi }; struct Instruction { - Operation operation = Operation::Undefined; + const Operation operation = Operation::Undefined; + const uint32_t opcode = 0; - // + Instruction(uint32_t opcode) : opcode(opcode) {} + Instruction(Operation operation, uint32_t opcode) : operation(operation), opcode(opcode) {} - Instruction() {} - Instruction(Operation operation) : operation(operation) {} + // TODO: all field decoding here. }; struct Decoder { @@ -75,6 +76,14 @@ struct Decoder { private: Model model_; + + bool is64bit() { + return false; + } + + bool is601() { + return model_ == Model::MPC601; + } }; } From db50b0fe23b5202e552b6194a12a01b800c24b3b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 31 Dec 2020 16:51:31 -0500 Subject: [PATCH 03/52] Gets started on 6+10 decoding, places stake as to other fields. --- Processors/Decoders/PowerPC/PowerPC.cpp | 44 ++++++++++++++++++++++++- Processors/Decoders/PowerPC/PowerPC.hpp | 38 +++++++++++++++++++-- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.cpp b/Processors/Decoders/PowerPC/PowerPC.cpp index e21c900c7..5b751220e 100644 --- a/Processors/Decoders/PowerPC/PowerPC.cpp +++ b/Processors/Decoders/PowerPC/PowerPC.cpp @@ -32,7 +32,8 @@ Instruction Decoder::decode(uint32_t opcode) { if(condition()) return Instruction(Operation::operation, opcode); \ return Instruction(opcode); -#define Six(x) (unsigned(x) << 26) +#define Six(x) (unsigned(x) << 26) +#define SixTen(x, y) (Six(x) | (y << 1)) // First pass: weed out all those instructions identified entirely by the // top six bits. @@ -73,6 +74,47 @@ Instruction Decoder::decode(uint32_t opcode) { Bind(Six(0b001010), cmpli); Bind(Six(0b001011), cmpi); } + // Second pass: all those with a top six bits and a bottom nine or ten. + switch(opcode & SixTen(0b111111, 0b1111111111)) { + default: break; + + BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux); + BindConditional(is64bit, SixTen(0b011111, 0b0000010101), ldx); + BindConditional(is64bit, SixTen(0b011111, 0b0000011011), sldx); + + Bind(SixTen(0b010011, 0b0000000000), mcrf); + Bind(SixTen(0b010011, 0b0000010000), bclrx); + Bind(SixTen(0b010011, 0b0000100001), crnor); + Bind(SixTen(0b010011, 0b0000110010), rfi); + Bind(SixTen(0b010011, 0b0010000001), crandc); + Bind(SixTen(0b010011, 0b0010010110), isync); + Bind(SixTen(0b010011, 0b0011000001), crxor); + Bind(SixTen(0b010011, 0b0011100001), crnand); + Bind(SixTen(0b010011, 0b0100000001), crand); + Bind(SixTen(0b010011, 0b0100100001), creqv); + Bind(SixTen(0b010011, 0b0110100001), crorc); + Bind(SixTen(0b010011, 0b0111000001), cror); + Bind(SixTen(0b010011, 0b1000010000), bcctrx); + Bind(SixTen(0b011111, 0b0000000000), cmp); + Bind(SixTen(0b011111, 0b0000000100), tw); + Bind(SixTen(0b011111, 0b0000001000), subfcx); Bind(SixTen(0b011111, 0b1000001000), subfcx); + Bind(SixTen(0b011111, 0b0000001010), addcx); Bind(SixTen(0b011111, 0b1000001010), addcx); + Bind(SixTen(0b011111, 0b0000001011), mulhwux); + Bind(SixTen(0b011111, 0b0000010011), mfcr); + Bind(SixTen(0b011111, 0b0000010100), lwarx); + Bind(SixTen(0b011111, 0b0000010111), lwzx); + Bind(SixTen(0b011111, 0b0000011000), slwx); + Bind(SixTen(0b011111, 0b0000011010), cntlzwx); + Bind(SixTen(0b011111, 0b0000011100), andx); + Bind(SixTen(0b011111, 0b0000100000), cmpl); + Bind(SixTen(0b011111, 0b0000101000), subfx); Bind(SixTen(0b011111, 0b1000101000), subfx); + } + + // Check for sc. + if((opcode & 0b010001'00000'00000'00000000000000'1'0) == 0b010001'00000'00000'00000000000000'1'0) { + return Instruction(Operation::sc, opcode); + } + #undef Six #undef Bind diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index f9f7e9761..19aae5f1d 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -55,9 +55,16 @@ enum class Operation: uint8_t { fresx, frsqrtex, fselx, fsqrtx, frsqrtsx, slbia, slbie, // 64-bit only PowerPC instructions. - cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx, tdi + cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx, tdi, mulhdux, ldx, + sldx }; +/*! + Holds a decoded PowerPC instruction. + + Implementation note: because the PowerPC encoding is particularly straightforward, + only the operation has been decoded ahead of time; all other fields are decoded on-demand. +*/ struct Instruction { const Operation operation = Operation::Undefined; const uint32_t opcode = 0; @@ -65,7 +72,34 @@ struct Instruction { Instruction(uint32_t opcode) : opcode(opcode) {} Instruction(Operation operation, uint32_t opcode) : operation(operation), opcode(opcode) {} - // TODO: all field decoding here. + // Instruction fields are decoded below; naming is as directly dictated by + // Motorola's documentation, and the definitions below are sorted by synonym. + uint16_t uimm() { return uint16_t(opcode & 0xffff); } + int16_t simm() { return int16_t(opcode & 0xffff); } + + int to() { return (opcode >> 21) & 0x1f; } + int d() { return (opcode >> 21) & 0x1f; } + int bo() { return (opcode >> 21) & 0x1f; } + int crbD() { return (opcode >> 21) & 0x1f; } + int s() { return (opcode >> 21) & 0x1f; } + + int a() { return (opcode >> 16) & 0x1f; } + int bi() { return (opcode >> 16) & 0x1f; } + int crbA() { return (opcode >> 16) & 0x1f; } + + int b() { return (opcode >> 11) & 0x1f; } + int crbB() { return (opcode >> 11) & 0x1f; } + + int crfd() { return (opcode >> 23) & 0x07; } + + int bd() { return (opcode >> 2) & 0x3fff; } + + int li() { return (opcode >> 2) & 0x0fff; } + + // Various single bit fields. + int l() { return (opcode >> 21) & 0x01; } + int aa() { return (opcode >> 1) & 0x01; } + int lk() { return opcode & 0x01; } }; struct Decoder { From ebfa35c2c7a7c341078fe8fb01a9bc68e03cd40b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 31 Dec 2020 18:14:38 -0500 Subject: [PATCH 04/52] Conquers another page of instructions; adds supervisor flag. --- Processors/Decoders/PowerPC/PowerPC.cpp | 73 ++++++++++++++++++++++++- Processors/Decoders/PowerPC/PowerPC.hpp | 15 ++++- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.cpp b/Processors/Decoders/PowerPC/PowerPC.cpp index 5b751220e..8c908ffa9 100644 --- a/Processors/Decoders/PowerPC/PowerPC.cpp +++ b/Processors/Decoders/PowerPC/PowerPC.cpp @@ -26,11 +26,16 @@ Instruction Decoder::decode(uint32_t opcode) { // // I've decided to hew directly to the mnemonics. -#define Bind(mask, operation) case mask: return Instruction(Operation::operation, opcode); +#define Bind(mask, operation) case mask: return Instruction(Operation::operation, opcode); +#define BindSupervisor(mask, operation) case mask: return Instruction(Operation::operation, opcode, true); #define BindConditional(condition, mask, operation) \ case mask: \ if(condition()) return Instruction(Operation::operation, opcode); \ return Instruction(opcode); +#define BindSupervisorConditional(condition, mask, operation) \ + case mask: \ + if(condition()) return Instruction(Operation::operation, opcode, true); \ + return Instruction(opcode); #define Six(x) (unsigned(x) << 26) #define SixTen(x, y) (Six(x) | (y << 1)) @@ -81,6 +86,19 @@ Instruction Decoder::decode(uint32_t opcode) { BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux); BindConditional(is64bit, SixTen(0b011111, 0b0000010101), ldx); BindConditional(is64bit, SixTen(0b011111, 0b0000011011), sldx); + BindConditional(is64bit, SixTen(0b011111, 0b0000110101), ldux); + BindConditional(is64bit, SixTen(0b011111, 0b0000111010), cntlzdx); + BindConditional(is64bit, SixTen(0b011111, 0b0001000100), td); + BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx); + BindConditional(is64bit, SixTen(0b011111, 0b0001010100), ldarx); + BindConditional(is64bit, SixTen(0b011111, 0b0010010101), stdx); + BindConditional(is64bit, SixTen(0b011111, 0b0010110101), stdux); + BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulld); BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulld); + BindConditional(is64bit, SixTen(0b011111, 0b0101010101), lwax); + BindConditional(is64bit, SixTen(0b011111, 0b0101110101), lwaux); +// BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix); // TODO: encoding is unclear re: the sh flag. + BindConditional(is64bit, SixTen(0b011111, 0b0110110010), slbie); + BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux); Bind(SixTen(0b010011, 0b0000000000), mcrf); Bind(SixTen(0b010011, 0b0000010000), bclrx); @@ -108,14 +126,67 @@ Instruction Decoder::decode(uint32_t opcode) { Bind(SixTen(0b011111, 0b0000011100), andx); Bind(SixTen(0b011111, 0b0000100000), cmpl); Bind(SixTen(0b011111, 0b0000101000), subfx); Bind(SixTen(0b011111, 0b1000101000), subfx); + Bind(SixTen(0b011111, 0b0000110110), dcbst); + Bind(SixTen(0b011111, 0b0000110111), lwzux); + Bind(SixTen(0b011111, 0b0000111100), andcx); + Bind(SixTen(0b011111, 0b0001001011), mulhwx); + Bind(SixTen(0b011111, 0b0001010011), mfmsr); + Bind(SixTen(0b011111, 0b0001010110), dcbf); + Bind(SixTen(0b011111, 0b0001010111), lbzx); + Bind(SixTen(0b011111, 0b0001101000), negx); Bind(SixTen(0b011111, 0b1001101000), negx); + Bind(SixTen(0b011111, 0b0001110111), lbzux); + Bind(SixTen(0b011111, 0b0001111100), norx); + Bind(SixTen(0b011111, 0b0010001000), subfex); Bind(SixTen(0b011111, 0b1010001000), subfex); + Bind(SixTen(0b011111, 0b0010001010), addex); Bind(SixTen(0b011111, 0b1010001010), addex); + Bind(SixTen(0b011111, 0b0010010000), mtcrf); + Bind(SixTen(0b011111, 0b0010010010), mtmsr); + Bind(SixTen(0b011111, 0b0010010111), stwx); + Bind(SixTen(0b011111, 0b0010110111), stwux); + Bind(SixTen(0b011111, 0b0011001000), subfzex); Bind(SixTen(0b011111, 0b1011001000), subfzex); + Bind(SixTen(0b011111, 0b0011001010), addzex); Bind(SixTen(0b011111, 0b1011001010), addzex); + Bind(SixTen(0b011111, 0b0011010111), stbx); + Bind(SixTen(0b011111, 0b0011101000), subfmex); Bind(SixTen(0b011111, 0b1011101000), subfmex); + Bind(SixTen(0b011111, 0b0011101010), addmex); Bind(SixTen(0b011111, 0b1011101010), addmex); + Bind(SixTen(0b011111, 0b0011101011), mullwx); Bind(SixTen(0b011111, 0b1011101011), mullwx); + Bind(SixTen(0b011111, 0b0011110110), dcbtst); + Bind(SixTen(0b011111, 0b0011110111), stbux); + Bind(SixTen(0b011111, 0b0100001010), addx); Bind(SixTen(0b011111, 0b1100001010), addx); + Bind(SixTen(0b011111, 0b0100010110), dcbt); + Bind(SixTen(0b011111, 0b0100010111), lhzx); + Bind(SixTen(0b011111, 0b0100011100), eqvx); + Bind(SixTen(0b011111, 0b0100110110), eciwx); + Bind(SixTen(0b011111, 0b0100110111), lhzux); + Bind(SixTen(0b011111, 0b0100111100), xorx); + Bind(SixTen(0b011111, 0b0101010111), lhax); + Bind(SixTen(0b011111, 0b0101110011), mftb); + Bind(SixTen(0b011111, 0b0101110111), lhaux); + Bind(SixTen(0b011111, 0b0110010111), sthx); + Bind(SixTen(0b011111, 0b0110011100), orcx); + Bind(SixTen(0b011111, 0b0110110110), ecowx); + Bind(SixTen(0b011111, 0b0110110111), sthux); + Bind(SixTen(0b011111, 0b0110111100), orx); + Bind(SixTen(0b011111, 0b0111001011), divwux); Bind(SixTen(0b011111, 0b1111001011), divwux); + Bind(SixTen(0b011111, 0b0111010110), dcbi); + + Bind(SixTen(0b011111, 0b0101010011), mfspr); // Flagged as "supervisor and user"? + Bind(SixTen(0b011111, 0b0111010011), mtspr); // Flagged as "supervisor and user"? + + BindSupervisorConditional(is32bit, SixTen(0b011111, 0b0011010010), mtsr); + BindSupervisorConditional(is32bit, SixTen(0b011111, 0b0011110010), mtsrin); + + BindSupervisor(SixTen(0b011111, 0b0100110010), tlbie); // TODO: mark formally as optional? + BindSupervisor(SixTen(0b011111, 0b0101110010), tlbia); // TODO: mark formally as optional? } + // TODO: stwcx., stdcx. + // Check for sc. if((opcode & 0b010001'00000'00000'00000000000000'1'0) == 0b010001'00000'00000'00000000000000'1'0) { return Instruction(Operation::sc, opcode); } #undef Six +#undef SixTen #undef Bind #undef BindConditional diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 19aae5f1d..0cdb2c8ab 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -46,17 +46,20 @@ enum class Operation: uint8_t { sc, slwx, srawx, srawix, srwx, stb, stbu, stbux, stbx, stfd, stfdu, stfdux, stfdx, stfs, stfsu, stfsux, stfsx, sth, sthbrx, sthu, sthux, sthx, stmw, stswi, stswx, stw, stwbrx, stwcx_, stwu, stwux, stwx, subfx, subfcx, - subfex, subfic, subfmex, subfzex, sync, tlbie, tw, twi, xorx, xori, xoris, + subfex, subfic, subfmex, subfzex, sync, tw, twi, xorx, xori, xoris, mftb, // 32-bit, supervisor level. dcbi, + // Supervisor, optional. + tlbia, tlbie, + // Optional. fresx, frsqrtex, fselx, fsqrtx, frsqrtsx, slbia, slbie, // 64-bit only PowerPC instructions. cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx, tdi, mulhdux, ldx, - sldx + sldx, ldux, td, mulhdx, ldarx, stdx, stdux, mulld, lwax, lwaux, sradix, }; /*! @@ -67,10 +70,11 @@ enum class Operation: uint8_t { */ struct Instruction { const Operation operation = Operation::Undefined; + const bool is_supervisor = false; const uint32_t opcode = 0; Instruction(uint32_t opcode) : opcode(opcode) {} - Instruction(Operation operation, uint32_t opcode) : operation(operation), opcode(opcode) {} + Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {} // Instruction fields are decoded below; naming is as directly dictated by // Motorola's documentation, and the definitions below are sorted by synonym. @@ -100,6 +104,7 @@ struct Instruction { int l() { return (opcode >> 21) & 0x01; } int aa() { return (opcode >> 1) & 0x01; } int lk() { return opcode & 0x01; } + int rc() { return opcode & 0x01; } }; struct Decoder { @@ -115,6 +120,10 @@ struct Decoder { return false; } + bool is32bit() { + return true; + } + bool is601() { return model_ == Model::MPC601; } From d318ab4e70b23765ff71b00e919719f88db66de8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 31 Dec 2020 21:12:36 -0500 Subject: [PATCH 05/52] Edges further onwards. --- Processors/Decoders/PowerPC/PowerPC.cpp | 111 ++++++++++++++++++++++-- Processors/Decoders/PowerPC/PowerPC.hpp | 18 ++-- 2 files changed, 116 insertions(+), 13 deletions(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.cpp b/Processors/Decoders/PowerPC/PowerPC.cpp index 8c908ffa9..45874a408 100644 --- a/Processors/Decoders/PowerPC/PowerPC.cpp +++ b/Processors/Decoders/PowerPC/PowerPC.cpp @@ -10,6 +10,18 @@ using namespace CPU::Decoder::PowerPC; +// Unmapped: +// +// absx, clcs, divx, divsx, dozx, dozi, lscbxx, maskgx, maskirx, mulx, +// nabsx, rlmix, rribx, slex, sleqx, sliqx, slliqx, sllqx, slqx, +// sraiqx, sraqx, srex, sreax, sreqx, sriqx, srliqx, srlqx, srqx, +// +// stwcx_, +// +// frsqrtsx, +// +// extswx, + Decoder::Decoder(Model model) : model_(model) {} Instruction Decoder::decode(uint32_t opcode) { @@ -99,6 +111,10 @@ Instruction Decoder::decode(uint32_t opcode) { // BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix); // TODO: encoding is unclear re: the sh flag. BindConditional(is64bit, SixTen(0b011111, 0b0110110010), slbie); BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux); + BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx); BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx); + BindConditional(is64bit, SixTen(0b011111, 0b1000011011), srdx); + BindConditional(is64bit, SixTen(0b011111, 0b1100011010), sradx); + BindConditional(is64bit, SixTen(0b011111, 0b1111011010), extsw); Bind(SixTen(0b010011, 0b0000000000), mcrf); Bind(SixTen(0b010011, 0b0000010000), bclrx); @@ -167,19 +183,104 @@ Instruction Decoder::decode(uint32_t opcode) { Bind(SixTen(0b011111, 0b0110111100), orx); Bind(SixTen(0b011111, 0b0111001011), divwux); Bind(SixTen(0b011111, 0b1111001011), divwux); Bind(SixTen(0b011111, 0b0111010110), dcbi); + Bind(SixTen(0b011111, 0b0111011100), nandx); + Bind(SixTen(0b011111, 0b0111101011), divwx); Bind(SixTen(0b011111, 0b1111101011), divwx); + Bind(SixTen(0b011111, 0b1000000000), mcrxr); + Bind(SixTen(0b011111, 0b1000010101), lswx); + Bind(SixTen(0b011111, 0b1000010110), lwbrx); + Bind(SixTen(0b011111, 0b1000010111), lfsx); + Bind(SixTen(0b011111, 0b1000011000), srwx); + Bind(SixTen(0b011111, 0b1000110111), lfsux); + Bind(SixTen(0b011111, 0b1001010101), lswi); + Bind(SixTen(0b011111, 0b1001010110), sync); + Bind(SixTen(0b011111, 0b1001010111), lfdx); + Bind(SixTen(0b011111, 0b1001110111), lfdux); + Bind(SixTen(0b011111, 0b1010010101), stswx); + Bind(SixTen(0b011111, 0b1010010110), stwbrx); + Bind(SixTen(0b011111, 0b1010010111), stfsx); + Bind(SixTen(0b011111, 0b1010110111), stfsux); + Bind(SixTen(0b011111, 0b1011010101), stswi); + Bind(SixTen(0b011111, 0b1011010111), stfdx); + Bind(SixTen(0b011111, 0b1011110111), stfdux); + Bind(SixTen(0b011111, 0b1100010110), lhbrx); + Bind(SixTen(0b011111, 0b1100011000), srawx); + Bind(SixTen(0b011111, 0b1100111000), srawix); + Bind(SixTen(0b011111, 0b1101010110), eieio); + Bind(SixTen(0b011111, 0b1110010110), sthbrx); + Bind(SixTen(0b011111, 0b1110011010), extshx); + Bind(SixTen(0b011111, 0b1110111010), extsbx); + Bind(SixTen(0b011111, 0b1111010110), icbi); + Bind(SixTen(0b011111, 0b1111010111), stfiwx); + Bind(SixTen(0b011111, 0b1111110110), dcbz); + Bind(SixTen(0b111111, 0b0000000000), fcmpu); + Bind(SixTen(0b111111, 0b0000001100), frspx); + Bind(SixTen(0b111111, 0b0000001110), fctiwx); + Bind(SixTen(0b111111, 0b0000001111), fctiwzx); + Bind(SixTen(0b111111, 0b0000100000), fcmpo); + Bind(SixTen(0b111111, 0b0000100110), mtfsb1x); + Bind(SixTen(0b111111, 0b0000101000), fnegx); + Bind(SixTen(0b111111, 0b0001000000), mcrfs); + Bind(SixTen(0b111111, 0b0001000110), mtfsb0x); + Bind(SixTen(0b111111, 0b0001001000), fmrx); + Bind(SixTen(0b111111, 0b0010000110), mtfsfix); + Bind(SixTen(0b111111, 0b0010001000), fnabsx); + Bind(SixTen(0b111111, 0b0100001000), fabsx); + Bind(SixTen(0b111111, 0b1001000111), mffsx); + Bind(SixTen(0b111111, 0b1011000111), mtfsfx); + Bind(SixTen(0b111111, 0b1100101110), fctidx); + Bind(SixTen(0b111111, 0b1100101111), fctidzx); + Bind(SixTen(0b111111, 0b1101001110), fcfidx); Bind(SixTen(0b011111, 0b0101010011), mfspr); // Flagged as "supervisor and user"? Bind(SixTen(0b011111, 0b0111010011), mtspr); // Flagged as "supervisor and user"? BindSupervisorConditional(is32bit, SixTen(0b011111, 0b0011010010), mtsr); BindSupervisorConditional(is32bit, SixTen(0b011111, 0b0011110010), mtsrin); + BindSupervisorConditional(is32bit, SixTen(0b011111, 0b1001010011), mfsr); + BindSupervisorConditional(is32bit, SixTen(0b011111, 0b1010010011), mfsrin); - BindSupervisor(SixTen(0b011111, 0b0100110010), tlbie); // TODO: mark formally as optional? - BindSupervisor(SixTen(0b011111, 0b0101110010), tlbia); // TODO: mark formally as optional? + BindSupervisorConditional(is64bit, SixTen(0b011111, 0b0111110010), slbia); // optional + + // The following are all optional; should I record that? + BindSupervisor(SixTen(0b011111, 0b0100110010), tlbie); + BindSupervisor(SixTen(0b011111, 0b0101110010), tlbia); + BindSupervisor(SixTen(0b011111, 0b1000110110), tlbsync); } - - // TODO: stwcx., stdcx. - + + // Third pass: like six-ten except that the top five of the final ten + // are reserved (i.e. ignored here). + switch(opcode & SixTen(0b111111, 0b11111)) { + default: break; + + Bind(SixTen(0b111011, 0b10010), fdivsx); + Bind(SixTen(0b111011, 0b10100), fsubsx); + Bind(SixTen(0b111011, 0b10101), faddsx); + Bind(SixTen(0b111011, 0b11001), fmulsx); + Bind(SixTen(0b111011, 0b11100), fmsubsx); + Bind(SixTen(0b111011, 0b11101), fmaddsx); + Bind(SixTen(0b111011, 0b11110), fnmsubsx); + Bind(SixTen(0b111011, 0b11111), fnmaddsx); + + Bind(SixTen(0b111111, 0b10010), fdivx); + Bind(SixTen(0b111111, 0b10100), fsubx); + Bind(SixTen(0b111111, 0b10101), faddx); + Bind(SixTen(0b111111, 0b11001), fmulx); + Bind(SixTen(0b111111, 0b11100), fmsubx); + Bind(SixTen(0b111111, 0b11101), fmaddx); + Bind(SixTen(0b111111, 0b11110), fnmsubx); + Bind(SixTen(0b111111, 0b11111), fnmaddx); + + BindConditional(is64bit, SixTen(0b111011, 0b10110), fsqrtsx); + BindConditional(is64bit, SixTen(0b111011, 0b11000), fresx); + + // Optional... + Bind(SixTen(0b111111, 0b10110), fsqrtx); + Bind(SixTen(0b111111, 0b10111), fselx); + Bind(SixTen(0b111111, 0b11010), frsqrtex); + } + + // TODO: stwcx., stdcx. stwcx_ + // Check for sc. if((opcode & 0b010001'00000'00000'00000000000000'1'0) == 0b010001'00000'00000'00000000000000'1'0) { return Instruction(Operation::sc, opcode); diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 0cdb2c8ab..e13228e46 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -19,7 +19,6 @@ enum class Model { MPC601, }; -// TODO: complete the following table. enum class Operation: uint8_t { Undefined, @@ -47,19 +46,20 @@ enum class Operation: uint8_t { stfdux, stfdx, stfs, stfsu, stfsux, stfsx, sth, sthbrx, sthu, sthux, sthx, stmw, stswi, stswx, stw, stwbrx, stwcx_, stwu, stwux, stwx, subfx, subfcx, subfex, subfic, subfmex, subfzex, sync, tw, twi, xorx, xori, xoris, mftb, - + // 32-bit, supervisor level. dcbi, - + // Supervisor, optional. - tlbia, tlbie, - + tlbia, tlbie, tlbsync, + // Optional. - fresx, frsqrtex, fselx, fsqrtx, frsqrtsx, slbia, slbie, + fresx, frsqrtex, fselx, fsqrtx, frsqrtsx, slbia, slbie, stfiwx, // 64-bit only PowerPC instructions. - cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx, tdi, mulhdux, ldx, - sldx, ldux, td, mulhdx, ldarx, stdx, stdux, mulld, lwax, lwaux, sradix, + cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx, tdi, mulhdux, + ldx, sldx, ldux, td, mulhdx, ldarx, stdx, stdux, mulld, lwax, lwaux, + sradix, srdx, sradx, extsw, fsqrtsx }; /*! @@ -94,6 +94,8 @@ struct Instruction { int b() { return (opcode >> 11) & 0x1f; } int crbB() { return (opcode >> 11) & 0x1f; } + int c() { return (opcode >> 6) & 0x1f; } + int crfd() { return (opcode >> 23) & 0x07; } int bd() { return (opcode >> 2) & 0x3fff; } From 0ef42f93ffc8eebd8fef7e4b6063a6d09be89528 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 1 Jan 2021 11:46:26 -0500 Subject: [PATCH 06/52] Further rounds out decoder. --- Processors/Decoders/PowerPC/PowerPC.cpp | 89 +++++++++++++------ Processors/Decoders/PowerPC/PowerPC.hpp | 109 ++++++++++++++++++------ 2 files changed, 148 insertions(+), 50 deletions(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.cpp b/Processors/Decoders/PowerPC/PowerPC.cpp index 45874a408..39fcea43a 100644 --- a/Processors/Decoders/PowerPC/PowerPC.cpp +++ b/Processors/Decoders/PowerPC/PowerPC.cpp @@ -10,18 +10,6 @@ using namespace CPU::Decoder::PowerPC; -// Unmapped: -// -// absx, clcs, divx, divsx, dozx, dozi, lscbxx, maskgx, maskirx, mulx, -// nabsx, rlmix, rribx, slex, sleqx, sliqx, slliqx, sllqx, slqx, -// sraiqx, sraqx, srex, sreax, sreqx, sriqx, srliqx, srlqx, srqx, -// -// stwcx_, -// -// frsqrtsx, -// -// extswx, - Decoder::Decoder(Model model) : model_(model) {} Instruction Decoder::decode(uint32_t opcode) { @@ -37,6 +25,12 @@ Instruction Decoder::decode(uint32_t opcode) { // it as a 9-bit field with a flag at the top. // // I've decided to hew directly to the mnemonics. + // + // Various opcodes in the 1995 documentation define reserved bits, + // which are given the nominal value of 0. It does not give a formal + // definition of a reserved bit. As a result this code does not + // currently check the value of reserved bits. That may need to change + // if/when I add support for extended instruction sets. #define Bind(mask, operation) case mask: return Instruction(Operation::operation, opcode); #define BindSupervisor(mask, operation) case mask: return Instruction(Operation::operation, opcode, true); @@ -50,7 +44,7 @@ Instruction Decoder::decode(uint32_t opcode) { return Instruction(opcode); #define Six(x) (unsigned(x) << 26) -#define SixTen(x, y) (Six(x) | (y << 1)) +#define SixTen(x, y) (Six(x) | ((y) << 1)) // First pass: weed out all those instructions identified entirely by the // top six bits. @@ -86,36 +80,69 @@ Instruction Decoder::decode(uint32_t opcode) { Bind(Six(0b110100), stfs); Bind(Six(0b110101), stfsu); Bind(Six(0b110110), stfd); Bind(Six(0b110111), stfdu); - // Assumed below here: reserved bits can be ignored. - // This might need to be a function of CPU model. + BindConditional(is601, Six(9), dozi); + BindConditional(is601, Six(22), rlmix); + Bind(Six(0b001010), cmpli); Bind(Six(0b001011), cmpi); } // Second pass: all those with a top six bits and a bottom nine or ten. switch(opcode & SixTen(0b111111, 0b1111111111)) { default: break; - - BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux); + + // 64-bit instructions. + BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux); BindConditional(is64bit, SixTen(0b011111, 0b1000001001), mulhdux); BindConditional(is64bit, SixTen(0b011111, 0b0000010101), ldx); BindConditional(is64bit, SixTen(0b011111, 0b0000011011), sldx); BindConditional(is64bit, SixTen(0b011111, 0b0000110101), ldux); BindConditional(is64bit, SixTen(0b011111, 0b0000111010), cntlzdx); BindConditional(is64bit, SixTen(0b011111, 0b0001000100), td); - BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx); + BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx); BindConditional(is64bit, SixTen(0b011111, 0b1001001001), mulhdx); BindConditional(is64bit, SixTen(0b011111, 0b0001010100), ldarx); BindConditional(is64bit, SixTen(0b011111, 0b0010010101), stdx); BindConditional(is64bit, SixTen(0b011111, 0b0010110101), stdux); BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulld); BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulld); BindConditional(is64bit, SixTen(0b011111, 0b0101010101), lwax); BindConditional(is64bit, SixTen(0b011111, 0b0101110101), lwaux); -// BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix); // TODO: encoding is unclear re: the sh flag. + BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix); BindConditional(is64bit, SixTen(0b011111, 0b1100111010), sradix); BindConditional(is64bit, SixTen(0b011111, 0b0110110010), slbie); BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx); BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx); BindConditional(is64bit, SixTen(0b011111, 0b1000011011), srdx); BindConditional(is64bit, SixTen(0b011111, 0b1100011010), sradx); - BindConditional(is64bit, SixTen(0b011111, 0b1111011010), extsw); + BindConditional(is64bit, SixTen(0b111111, 0b1111011010), extsw); + // Power instructions; these are all taken from the MPC601 manual rather than + // the PowerPC Programmer's Reference Guide, hence the decimal encoding of the + // ten-bit field. + BindConditional(is601, SixTen(0b011111, 360), absx); BindConditional(is601, SixTen(0b011111, 512 + 360), absx); + BindConditional(is601, SixTen(0b011111, 531), clcs); + BindConditional(is601, SixTen(0b011111, 331), divx); BindConditional(is601, SixTen(0b011111, 512 + 331), divx); + BindConditional(is601, SixTen(0b011111, 363), divsx); BindConditional(is601, SixTen(0b011111, 512 + 363), divsx); + BindConditional(is601, SixTen(0b011111, 264), dozx); BindConditional(is601, SixTen(0b011111, 512 + 264), dozx); + BindConditional(is601, SixTen(0b011111, 277), lscbxx); + BindConditional(is601, SixTen(0b011111, 29), maskgx); + BindConditional(is601, SixTen(0b011111, 541), maskirx); + BindConditional(is601, SixTen(0b011111, 107), mulx); BindConditional(is601, SixTen(0b011111, 512 + 107), mulx); + BindConditional(is601, SixTen(0b011111, 488), nabsx); BindConditional(is601, SixTen(0b011111, 512 + 488), nabsx); + BindConditional(is601, SixTen(0b011111, 537), rribx); + BindConditional(is601, SixTen(0b011111, 153), slex); + BindConditional(is601, SixTen(0b011111, 217), sleqx); + BindConditional(is601, SixTen(0b011111, 184), sliqx); + BindConditional(is601, SixTen(0b011111, 248), slliqx); + BindConditional(is601, SixTen(0b011111, 216), sllqx); + BindConditional(is601, SixTen(0b011111, 152), slqx); + BindConditional(is601, SixTen(0b011111, 952), sraiqx); + BindConditional(is601, SixTen(0b011111, 920), sraqx); + BindConditional(is601, SixTen(0b011111, 665), srex); + BindConditional(is601, SixTen(0b011111, 921), sreax); + BindConditional(is601, SixTen(0b011111, 729), sreqx); + BindConditional(is601, SixTen(0b011111, 696), sriqx); + BindConditional(is601, SixTen(0b011111, 760), srliqx); + BindConditional(is601, SixTen(0b011111, 728), srlqx); + BindConditional(is601, SixTen(0b011111, 664), srqx); + + // 32-bit instructions. Bind(SixTen(0b010011, 0b0000000000), mcrf); Bind(SixTen(0b010011, 0b0000010000), bclrx); Bind(SixTen(0b010011, 0b0000100001), crnor); @@ -133,7 +160,7 @@ Instruction Decoder::decode(uint32_t opcode) { Bind(SixTen(0b011111, 0b0000000100), tw); Bind(SixTen(0b011111, 0b0000001000), subfcx); Bind(SixTen(0b011111, 0b1000001000), subfcx); Bind(SixTen(0b011111, 0b0000001010), addcx); Bind(SixTen(0b011111, 0b1000001010), addcx); - Bind(SixTen(0b011111, 0b0000001011), mulhwux); + Bind(SixTen(0b011111, 0b0000001011), mulhwux); Bind(SixTen(0b011111, 0b1000001011), mulhwux); Bind(SixTen(0b011111, 0b0000010011), mfcr); Bind(SixTen(0b011111, 0b0000010100), lwarx); Bind(SixTen(0b011111, 0b0000010111), lwzx); @@ -145,7 +172,7 @@ Instruction Decoder::decode(uint32_t opcode) { Bind(SixTen(0b011111, 0b0000110110), dcbst); Bind(SixTen(0b011111, 0b0000110111), lwzux); Bind(SixTen(0b011111, 0b0000111100), andcx); - Bind(SixTen(0b011111, 0b0001001011), mulhwx); + Bind(SixTen(0b011111, 0b0001001011), mulhwx); Bind(SixTen(0b011111, 0b1001001011), mulhwx); Bind(SixTen(0b011111, 0b0001010011), mfmsr); Bind(SixTen(0b011111, 0b0001010110), dcbf); Bind(SixTen(0b011111, 0b0001010111), lbzx); @@ -279,10 +306,22 @@ Instruction Decoder::decode(uint32_t opcode) { Bind(SixTen(0b111111, 0b11010), frsqrtex); } - // TODO: stwcx., stdcx. stwcx_ + // stwcx. and stdcx. + switch(opcode & 0b111111'00'00000000'000'111111111'1){ + case 0b011111'00'00000000'00000'0010010110'1: return Instruction(Operation::stwcx_, opcode); + case 0b011111'00'00000000'00000'0011010110'1: + if(is64bit()) return Instruction(Operation::stdcx_, opcode); + return Instruction(opcode); + } - // Check for sc. - if((opcode & 0b010001'00000'00000'00000000000000'1'0) == 0b010001'00000'00000'00000000000000'1'0) { + // std and stdu + switch(opcode & 0b111111'00'00000000'00000000'000000'11){ + case 0b111110'00'00000000'00000000'000000'00: return Instruction(Operation::std, opcode); + case 0b111110'00'00000000'00000000'000000'01: return Instruction(Operation::stdu, opcode); + } + + // sc + if((opcode & 0b111111'00'00000000'00000000'000000'1'0) == 0b010001'00'00000000'00000000'000000'1'0) { return Instruction(Operation::sc, opcode); } diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index e13228e46..39a713fa1 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -9,6 +9,7 @@ #ifndef PowerPC_hpp #define PowerPC_hpp +#include #include namespace CPU { @@ -54,12 +55,12 @@ enum class Operation: uint8_t { tlbia, tlbie, tlbsync, // Optional. - fresx, frsqrtex, fselx, fsqrtx, frsqrtsx, slbia, slbie, stfiwx, + fresx, frsqrtex, fselx, fsqrtx, slbia, slbie, stfiwx, // 64-bit only PowerPC instructions. cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx, tdi, mulhdux, ldx, sldx, ldux, td, mulhdx, ldarx, stdx, stdux, mulld, lwax, lwaux, - sradix, srdx, sradx, extsw, fsqrtsx + sradix, srdx, sradx, extsw, fsqrtsx, std, stdu, stdcx_, }; /*! @@ -73,40 +74,98 @@ struct Instruction { const bool is_supervisor = false; const uint32_t opcode = 0; + // PowerPC uses a fixed-size instruction word. + size_t size() { + return 4; + } + Instruction(uint32_t opcode) : opcode(opcode) {} Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {} - // Instruction fields are decoded below; naming is as directly dictated by - // Motorola's documentation, and the definitions below are sorted by synonym. + // Instruction fields are decoded below; naming is a compromise between + // Motorola's documentation and IBM's. + // + // I've dutifully implemented various synonyms with unique entry points, + // in order to capture that information here rather than thrusting it upon + // the reader of whatever implementation may follow. + + // TODO: d, ds, FM, MB, ME, NB, OPCD, SH, SR, XO + + /// Immediate field used to specify an unsigned 16-bit integer. uint16_t uimm() { return uint16_t(opcode & 0xffff); } + /// Immediate field used to specify a signed 16-bit integer. int16_t simm() { return int16_t(opcode & 0xffff); } + /// Immediate field used as data to be placed into a field in the floating point status and condition register. + int32_t imm() { return (opcode >> 12) & 0xf; } - int to() { return (opcode >> 21) & 0x1f; } - int d() { return (opcode >> 21) & 0x1f; } - int bo() { return (opcode >> 21) & 0x1f; } - int crbD() { return (opcode >> 21) & 0x1f; } - int s() { return (opcode >> 21) & 0x1f; } + /// Specifies the conditions on which to trap. + int32_t to() { return (opcode >> 21) & 0x1f; } - int a() { return (opcode >> 16) & 0x1f; } - int bi() { return (opcode >> 16) & 0x1f; } - int crbA() { return (opcode >> 16) & 0x1f; } + /// Register source A or destination. + uint32_t rA() { return (opcode >> 16) & 0x1f; } + /// Register source B. + uint32_t rB() { return (opcode >> 11) & 0x1f; } + /// Register destination. + uint32_t rD() { return (opcode >> 21) & 0x1f; } + /// Register source. + uint32_t rS() { return (opcode >> 21) & 0x1f; } - int b() { return (opcode >> 11) & 0x1f; } - int crbB() { return (opcode >> 11) & 0x1f; } + /// Floating point register source A. + uint32_t frA() { return (opcode >> 16) & 0x1f; } + /// Floating point register source B. + uint32_t frB() { return (opcode >> 11) & 0x1f; } + /// Floating point register source C. + uint32_t frC() { return (opcode >> 6) & 0x1f; } + /// Floating point register source. + uint32_t frS() { return (opcode >> 21) & 0x1f; } + /// Floating point register destination. + uint32_t frD() { return (opcode >> 21) & 0x1f; } - int c() { return (opcode >> 6) & 0x1f; } + /// Branch conditional options. + uint32_t bo() { return (opcode >> 21) & 0x1f; } + /// Source condition register bit for branch conditionals. + uint32_t bi() { return (opcode >> 16) & 0x1f; } + /// Branch displacement; provided as already sign extended. + int16_t bd() { return int16_t(opcode & 0xfffc); } - int crfd() { return (opcode >> 23) & 0x07; } - - int bd() { return (opcode >> 2) & 0x3fff; } - - int li() { return (opcode >> 2) & 0x0fff; } + /// Condition register source bit A. + uint32_t crbA() { return (opcode >> 16) & 0x1f; } + /// Condition register source bit B. + uint32_t crbB() { return (opcode >> 11) & 0x1f; } + /// Condition register (or floating point status & condition register) destination bit. + uint32_t crbD() { return (opcode >> 21) & 0x1f; } - // Various single bit fields. - int l() { return (opcode >> 21) & 0x01; } - int aa() { return (opcode >> 1) & 0x01; } - int lk() { return opcode & 0x01; } - int rc() { return opcode & 0x01; } + /// Condition register (or floating point status & condition register) destination field. + uint32_t crfD() { return (opcode >> 23) & 0x07; } + /// Condition register (or floating point status & condition register) source field. + uint32_t crfS() { return (opcode >> 18) & 0x07; } + + /// Mask identifying fields to be updated by mtcrf. + uint32_t crm() { return (opcode >> 12) & 0xff; } + + /// Mask identifying fields to be updated by mtfsf. + uint32_t fm() { return (opcode >> 17) & 0xff; } + + /// A 24-bit signed number; provided as already sign extended. + int32_t li() { + constexpr uint32_t extensions[2] = { + 0x0000'0000, + 0xfc00'0000 + }; + const uint32_t value = (opcode & 0x3fff'fffc) | extensions[(opcode >> 26) & 1]; + return int32_t(value); + } + + /// Absolute address bit; @c 0 or @c non-0. + uint32_t aa() { return opcode & 0x02; } + /// Link bit; @c 0 or @c non-0. + uint32_t lk() { return opcode & 0x01; } + /// Record bit; @c 0 or @c non-0. + uint32_t rc() { return opcode & 0x01; } + /// Whether to compare 32-bit or 64-bit numbers [for 64-bit implementations only]; @c 0 or @c non-0. + uint32_t l() { return opcode & 0x200000; } + /// Enables setting of OV and SO in the XER; @c 0 or @c non-0. + uint32_t oe() { return opcode & 0x800; } }; struct Decoder { From 8151c8e409eb692f94dbf33c4ceb84e73946a620 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 1 Jan 2021 16:38:40 -0500 Subject: [PATCH 07/52] Rounds out field list. --- Processors/Decoders/PowerPC/PowerPC.hpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 39a713fa1..3dd214134 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -89,12 +89,17 @@ struct Instruction { // in order to capture that information here rather than thrusting it upon // the reader of whatever implementation may follow. - // TODO: d, ds, FM, MB, ME, NB, OPCD, SH, SR, XO + // Currently omitted: OPCD and XO, which I think are unnecessary given that + // full decoding has already occurred. /// Immediate field used to specify an unsigned 16-bit integer. uint16_t uimm() { return uint16_t(opcode & 0xffff); } /// Immediate field used to specify a signed 16-bit integer. int16_t simm() { return int16_t(opcode & 0xffff); } + /// Immediate field used to specify a signed 16-bit integer. + int16_t d() { return int16_t(opcode & 0xffff); } + /// Immediate field used to specify a signed 14-bit integer [64-bit only]. + int16_t ds() { return int16_t(opcode & 0xfffc); } /// Immediate field used as data to be placed into a field in the floating point status and condition register. int32_t imm() { return (opcode >> 12) & 0xf; } @@ -128,6 +133,11 @@ struct Instruction { /// Branch displacement; provided as already sign extended. int16_t bd() { return int16_t(opcode & 0xfffc); } + /// Specifies the first 1 bit of a 32/64-bit mask for rotate operations. + uint32_t mb() { return (opcode >> 6) & 0x1f; } + /// Specifies the first 1 bit of a 32/64-bit mask for rotate operations. + uint32_t me() { return (opcode >> 1) & 0x1f; } + /// Condition register source bit A. uint32_t crbA() { return (opcode >> 16) & 0x1f; } /// Condition register source bit B. @@ -146,6 +156,16 @@ struct Instruction { /// Mask identifying fields to be updated by mtfsf. uint32_t fm() { return (opcode >> 17) & 0xff; } + /// Specifies the number of bytes to move in an immediate string load or store. + uint32_t nb() { return (opcode >> 11) & 0x1f; } + + /// Specifies a shift amount. + /// TODO: possibly bit 30 is also used in 64-bit mode, find out. + uint32_t sh() { return (opcode >> 11) & 0x1f; } + + /// Specifies one of the 16 segment registers [32-bit only]. + uint32_t sr() { return (opcode >> 16) & 0xf; } + /// A 24-bit signed number; provided as already sign extended. int32_t li() { constexpr uint32_t extensions[2] = { From 3d1783ddae092e430c3201e7987eb07ba6e67478 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 1 Jan 2021 17:32:57 -0500 Subject: [PATCH 08/52] Add exposition as to the purpose of decoders. --- .../Clock Signal.xcodeproj/project.pbxproj | 2 + Processors/Decoders/README.md | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 Processors/Decoders/README.md diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index f48e8ec52..d284159a0 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1076,6 +1076,7 @@ 4B3BA0CD1D318B44005DD7A7 /* TestMachine6502.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestMachine6502.mm; sourceTree = ""; }; 4B3BF5AE1F146264005B6C36 /* CSW.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSW.cpp; sourceTree = ""; }; 4B3BF5AF1F146264005B6C36 /* CSW.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CSW.hpp; sourceTree = ""; }; + 4B3F769E259FCE0F00178AEC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 4B3FCC3E201EC24200960631 /* MultiMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMachine.hpp; sourceTree = ""; }; 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMachine.cpp; sourceTree = ""; }; 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPM.cpp; path = Parsers/CPM.cpp; sourceTree = ""; }; @@ -3889,6 +3890,7 @@ 4BDDBBD3259D757800CEFF58 /* Decoders */ = { isa = PBXGroup; children = ( + 4B3F769E259FCE0F00178AEC /* README.md */, 4BDDBBD4259D757800CEFF58 /* PowerPC */, ); path = Decoders; diff --git a/Processors/Decoders/README.md b/Processors/Decoders/README.md new file mode 100644 index 000000000..c8975eef7 --- /dev/null +++ b/Processors/Decoders/README.md @@ -0,0 +1,49 @@ +# Decoders + +A decoder extracts fully-decoded instructions from a data stream for its associated architecture. + +An instruction is 'fully-decoded' when an instance of a suitable struct or class has been created that can provide at least the instruction's length, and will usually also provide as relevant: +* the operation in use; +* its addressing mode; and +* relevant registers. + +It will have access to the original data stream again before being asked to provide any immediate values associated with the instruction. + +In deciding what to expose, what to store ahead of time and what to obtain just-in-time a decoder should have an eye on three main potential types of consumer: +1. disassemblers; +2. instruction executors; and +3. bus-centric CPU emulators. + +The first of those is likely to decode an instruction, output it to somewhere, and then immediately forget about it. + +The second may opt to cache the decoded forms of instructions to reduce recurrent costs, but will always be dealing with an actual instruction stream. The chance of caching means that decoded instructions should seek to be small; however it also implies that as much processing cost as is reasonable should be spent up-front. + +The third may use a decoder live (especially if the instruction stream is complicated, or instruction words are large) or may use one once ahead of time in order to build up internal tables related to abstract interpretation of operations. The first use suggests a further premium on speed, the second implies that 'fully-decoded' instructions shouldn't seek to collect all possible immediate values ahead of time whenever doing so is avoidable — as a reult of thumb, they will usually return an instruction as soon as its length is fully known. + +## Likely Interfaces + +These examples assume that the processor itself doesn't hold any state that affects instruction parsing. Whether processors with such state offer more than one decoder or take state as an argument will be a question of measure and effect. + +### Fixed-size instruction words + +If the instructions are a fixed size, the decoder can provide what is functionally a simple lookup, whether implemented as such or not: + + Instruction decode(word_type instruction) { ... } + +### Variable-size instruction words + +If instructions are a variable size, the decoder should maintain internal state such that it can be provided with fragments of instructions until a full decoding has occurred. + +A sample interface: + + size_t decode(Instruction &target, word_type *stream, size_t length) { ... } + +Returns either the number of further words necessary fully to decode, if known, or a negative number to indicate a generic sense of 'more needed'. A value of `0` means 'an instruction was fully decoded', and the instruction itself should be able to report its size. + +`target` is populated only if an instruction is decoded; `stream` and `length` provide as much further instruction stream data as the caller currently possesses. + +## Tying Decoders into Instruction Executors + +It is assumed that disassemblers and bus-centric CPU emulators have limited generic functionality; for executors it is assumed that a processor-specific instruction fetcher and a dispatcher will be provided to couple with the decoder. + +Therefore decoders should adopt whatever interface is most natural; the expected uses information above is to provide a motivation for the scope of responsibilities and hints as to likely performance objectives only. Beyond requiring that decoded instructions be a tangible struct or class, it is not intended to be prescriptive as to form or interface. From a41be61f9941b887785bf1c7af67b5e08d536aba Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 1 Jan 2021 17:36:47 -0500 Subject: [PATCH 09/52] Slightly fleshes out models, for a sensible beginning. --- Processors/Decoders/PowerPC/PowerPC.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 3dd214134..37d4ebb29 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -17,7 +17,12 @@ namespace Decoder { namespace PowerPC { enum class Model { + /// i.e. 32-bit, with POWER carry-over instructions. MPC601, + /// i.e. 32-bit, no POWER instructions. + MPC603, + /// i.e. 64-bit. + MPC620, }; enum class Operation: uint8_t { @@ -198,11 +203,11 @@ struct Decoder { Model model_; bool is64bit() { - return false; + return model_ == Model::MPC620; } bool is32bit() { - return true; + return !is64bit(); } bool is601() { From dc9d370952040bcdd69d6a6fd62d755cde93914e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 2 Jan 2021 19:16:07 -0500 Subject: [PATCH 10/52] Does the easier part of the easier half of 8086 decoding. --- .../Clock Signal.xcodeproj/project.pbxproj | 16 +++ Processors/Decoders/x86/x86.cpp | 126 +++++++++++++++++ Processors/Decoders/x86/x86.hpp | 127 ++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 Processors/Decoders/x86/x86.cpp create mode 100644 Processors/Decoders/x86/x86.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index d284159a0..be155af1e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -183,6 +183,8 @@ 4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */; }; 4B3BA0D11D318B44005DD7A7 /* TestMachine6502.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CD1D318B44005DD7A7 /* TestMachine6502.mm */; }; 4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BF5AE1F146264005B6C36 /* CSW.cpp */; }; + 4B3F76AE25A0196100178AEC /* x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76AD25A0196100178AEC /* x86.cpp */; }; + 4B3F76AF25A0196100178AEC /* x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76AD25A0196100178AEC /* x86.cpp */; }; 4B3FCC40201EC24200960631 /* MultiMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */; }; 4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */; }; 4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B448E7F1F1C45A00009ABD6 /* TZX.cpp */; }; @@ -1077,6 +1079,8 @@ 4B3BF5AE1F146264005B6C36 /* CSW.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSW.cpp; sourceTree = ""; }; 4B3BF5AF1F146264005B6C36 /* CSW.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CSW.hpp; sourceTree = ""; }; 4B3F769E259FCE0F00178AEC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 4B3F76AC25A0196100178AEC /* x86.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = x86.hpp; sourceTree = ""; }; + 4B3F76AD25A0196100178AEC /* x86.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x86.cpp; sourceTree = ""; }; 4B3FCC3E201EC24200960631 /* MultiMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMachine.hpp; sourceTree = ""; }; 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMachine.cpp; sourceTree = ""; }; 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPM.cpp; path = Parsers/CPM.cpp; sourceTree = ""; }; @@ -2324,6 +2328,15 @@ path = Bridges; sourceTree = ""; }; + 4B3F76AB25A0196100178AEC /* x86 */ = { + isa = PBXGroup; + children = ( + 4B3F76AC25A0196100178AEC /* x86.hpp */, + 4B3F76AD25A0196100178AEC /* x86.cpp */, + ); + path = x86; + sourceTree = ""; + }; 4B3FCC3D201EC24200960631 /* MultiMachine */ = { isa = PBXGroup; children = ( @@ -3892,6 +3905,7 @@ children = ( 4B3F769E259FCE0F00178AEC /* README.md */, 4BDDBBD4259D757800CEFF58 /* PowerPC */, + 4B3F76AB25A0196100178AEC /* x86 */, ); path = Decoders; sourceTree = ""; @@ -4666,6 +4680,7 @@ 4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */, 4B0E04EB1FC9E78800F43484 /* CAS.cpp in Sources */, 4BB0A65D2045009000FB3688 /* ColecoVision.cpp in Sources */, + 4B3F76AF25A0196100178AEC /* x86.cpp in Sources */, 4BB0A65C2044FD3000FB3688 /* SN76489.cpp in Sources */, 4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */, 4B055AB91FAE86170060FFFF /* Acorn.cpp in Sources */, @@ -4738,6 +4753,7 @@ 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */, 4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */, 4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */, + 4B3F76AE25A0196100178AEC /* x86.cpp in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */, diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp new file mode 100644 index 000000000..63fa1888a --- /dev/null +++ b/Processors/Decoders/x86/x86.cpp @@ -0,0 +1,126 @@ +// +// x86.cpp +// Clock Signal +// +// Created by Thomas Harte on 1/1/21. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#include "x86.hpp" + +using namespace CPU::Decoder::x86; + +Instruction Decoder::decode(uint8_t *source, size_t length) { + uint8_t *const limit = source + length; + +#define MapPartial(value, op, sz, fmt, phs) \ + case value: \ + operation_ = Operation::op; \ + operand_size_ = Size::sz; \ + format_ = Format::fmt; \ + phase_ = Phase::phs; \ + break; + +#define MapComplete(value, op, sz, src, dest) \ + case value: \ + operation_ = Operation::op; \ + operand_size_ = Size::sz; \ + source_ = Source::src; \ + destination_ = Source::dest; \ + phase_ = Phase::ReadyToPost; \ + break; + + while(phase_ == Phase::Instruction && source != limit) { + switch(*source) { +#define PartialBlock(start, operation) \ + MapPartial(start + 0x00, operation, Byte, MemReg_Reg, ModRM); \ + MapPartial(start + 0x01, operation, Word, MemReg_Reg, ModRM); \ + MapPartial(start + 0x02, operation, Byte, Reg_MemReg, ModRM); \ + MapPartial(start + 0x03, operation, Word, Reg_MemReg, ModRM); \ + MapPartial(start + 0x04, operation, Byte, Ac_Data, AwaitingOperands); \ + MapPartial(start + 0x05, operation, Word, Ac_Data, AwaitingOperands); + + PartialBlock(0x00, ADD); + MapComplete(0x06, PUSH, Word, ES, None); + MapComplete(0x07, POP, Word, ES, None); + + PartialBlock(0x08, OR); + MapComplete(0x0e, PUSH, Word, CS, None); + /* 0x0f: not used. */ + + PartialBlock(0x10, ADC); + MapComplete(0x16, PUSH, Word, SS, None); + MapComplete(0x17, POP, Word, SS, None); + + PartialBlock(0x18, SBB); + MapComplete(0x1e, PUSH, Word, DS, None); + MapComplete(0x1f, POP, Word, DS, None); + + PartialBlock(0x20, AND); + case 0x26: segment_override_ = Source::ES; break; + MapComplete(0x27, DAA, Implied, None, None); + + PartialBlock(0x28, SUB); + case 0x2e: segment_override_ = Source::CS; break; + MapComplete(0x2f, DAS, Implied, None, None); + + PartialBlock(0x30, XOR); + case 0x36: segment_override_ = Source::SS; break; + MapComplete(0x37, AAA, Implied, None, None); + + PartialBlock(0x38, CMP); + case 0x3e: segment_override_ = Source::DS; break; + MapComplete(0x3f, AAS, Implied, None, None); + +#undef PartialBlock + +#define RegisterBlock(start, operation) \ + MapComplete(start + 0x00, operation, Word, AX, AX); \ + MapComplete(start + 0x01, operation, Word, CX, CX); \ + MapComplete(start + 0x02, operation, Word, DX, DX); \ + MapComplete(start + 0x03, operation, Word, BX, BX); \ + MapComplete(start + 0x04, operation, Word, SP, SP); \ + MapComplete(start + 0x05, operation, Word, BP, BP); \ + MapComplete(start + 0x06, operation, Word, SI, SI); \ + MapComplete(start + 0x07, operation, Word, DI, DI); \ + + RegisterBlock(0x40, INC); + RegisterBlock(0x48, DEC); + RegisterBlock(0x50, PUSH); + RegisterBlock(0x58, POP); + +#undef RegisterBlock + + /* 0x60–0x6f: not used. */ + + MapPartial(0x70, JO, Byte, Disp, AwaitingOperands); + MapPartial(0x71, JNO, Byte, Disp, AwaitingOperands); + MapPartial(0x72, JB, Byte, Disp, AwaitingOperands); + MapPartial(0x73, JNB, Byte, Disp, AwaitingOperands); + MapPartial(0x74, JE, Byte, Disp, AwaitingOperands); + MapPartial(0x75, JNE, Byte, Disp, AwaitingOperands); + MapPartial(0x76, JBE, Byte, Disp, AwaitingOperands); + MapPartial(0x77, JNBE, Byte, Disp, AwaitingOperands); + MapPartial(0x78, JS, Byte, Disp, AwaitingOperands); + MapPartial(0x79, JNS, Byte, Disp, AwaitingOperands); + MapPartial(0x7a, JP, Byte, Disp, AwaitingOperands); + MapPartial(0x7b, JNP, Byte, Disp, AwaitingOperands); + MapPartial(0x7c, JL, Byte, Disp, AwaitingOperands); + MapPartial(0x7d, JNL, Byte, Disp, AwaitingOperands); + MapPartial(0x7e, JLE, Byte, Disp, AwaitingOperands); + MapPartial(0x7f, JNLE, Byte, Disp, AwaitingOperands); + + } + ++source; + ++consumed_; + } + +#undef MapInstr + + + if(phase_ == Phase::ReadyToPost) { + // TODO: construct actual Instruction. + } + + return Instruction(); +} diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp new file mode 100644 index 000000000..c8616d4d5 --- /dev/null +++ b/Processors/Decoders/x86/x86.hpp @@ -0,0 +1,127 @@ +// +// x86.hpp +// Clock Signal +// +// Created by Thomas Harte on 1/1/21. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#ifndef x86_hpp +#define x86_hpp + +#include +#include + +namespace CPU { +namespace Decoder { +namespace x86 { + +enum class Model { + i8086, +}; + +enum class Operation: uint8_t { + Invalid, + + AAA, AAD, AAM, AAS, ADC, ADD, AND, CALL, CBW, CLC, CLD, CLI, CMC, + CMP, CMPS, CWD, DAA, DAS, DEC, DIV, ESC, HLT, IDIV, IMUL, IN, + INC, INT, INTO, IRET, + JO, JNO, + JB, JNB, + JE, JNE, + JBE, JNBE, + JS, JNS, + JP, JNP, + JL, JNL, + JLE, JNLE, + JMP, JCXZ, + LAHF, LDS, LEA, + LODS, LOOPE, LOOPNE, MOV, MOVS, MUL, NEG, NOP, NOT, OR, OUT, + POP, POPF, PUSH, PUSHF, RCL, RCR, REP, RET, ROL, ROR, SAHF, + SAR, SBB, SCAS, SHL, SHR, STC, STD, STI, STOS, SUB, TEST, + WAIT, XCHG, XLAT, XOR +}; + +enum class Size: uint8_t { + Implied = 0, + Byte = 1, + Word = 2, +}; + +enum class Source: uint8_t { + None, + + AL, AH, AX, + BL, BH, BX, + CL, CH, CX, + DL, DH, DX, + + CS, DS, ES, SS, + SI, DI, + BP, SP, + + Memory, Immediate +}; + +class Instruction { + public: + const Operation operation = Operation::Invalid; + const Size operand_size = Size::Byte; + + const Source source = Source::AL; + const Source destination = Source::AL; + + int size() const { + return size_; + } + + private: + int size_ = 0; +}; + +/*! + Implements Intel x86 instruction decoding. + + This is an experimental implementation; it has not yet undergone significant testing. +*/ +struct Decoder { + public: + Decoder(Model model); + + /*! + @returns an @c Instruction with a positive size to indicate successful decoding; a + negative size specifies the number of further bytes required fully to decode, and + a zero size indicates that further bytes are required but the decoder does not yet + know exactly how many. + */ + Instruction decode(uint8_t *source, size_t length); + + private: + enum class Phase { + Instruction, + ModRM, + AwaitingOperands, + ReadyToPost + } phase_ = Phase::Instruction; + + enum class Format: uint8_t { + MemReg_Reg, + Reg_MemReg, + Ac_Data, + MemReg_Data, + Disp + } format_ = Format::MemReg_Reg; + + int consumed_ = 0; + Operation operation_ = Operation::Invalid; + Size operand_size_ = Size::Implied; + Source source_ = Source::None; + Source destination_ = Source::None; + Source segment_override_ = Source::None; +}; + +} +} +} + +#endif /* x86_hpp */ From 290972cedf4286588c7ccf066e47e0cc700447f1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 2 Jan 2021 19:16:21 -0500 Subject: [PATCH 11/52] Adds health warning. --- Processors/Decoders/PowerPC/PowerPC.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 37d4ebb29..9b6613b6d 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -193,6 +193,11 @@ struct Instruction { uint32_t oe() { return opcode & 0x800; } }; +/*! + Implements PowerPC instruction decoding. + + This is an experimental implementation; it has not yet undergone significant testing. +*/ struct Decoder { public: Decoder(Model model); From 9c5dc0ed298f5e47c740940120e5b6983fcae0a6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 2 Jan 2021 19:29:43 -0500 Subject: [PATCH 12/52] Deferring ModRM work, proceeds to 0x9f. --- Processors/Decoders/x86/x86.cpp | 57 +++++++++++++++++++++++++++------ Processors/Decoders/x86/x86.hpp | 3 ++ 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 63fa1888a..a8832a065 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -13,13 +13,13 @@ using namespace CPU::Decoder::x86; Instruction Decoder::decode(uint8_t *source, size_t length) { uint8_t *const limit = source + length; -#define MapPartial(value, op, sz, fmt, phs) \ - case value: \ - operation_ = Operation::op; \ - operand_size_ = Size::sz; \ - format_ = Format::fmt; \ - phase_ = Phase::phs; \ - break; +#define MapPartial(value, op, sz, fmt, phs) \ + case value: \ + operation_ = Operation::op; \ + operand_size_ = Size::sz; \ + format_ = Format::fmt; \ + phase_ = Phase::phs; \ + break #define MapComplete(value, op, sz, src, dest) \ case value: \ @@ -28,10 +28,13 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { source_ = Source::src; \ destination_ = Source::dest; \ phase_ = Phase::ReadyToPost; \ - break; + break while(phase_ == Phase::Instruction && source != limit) { - switch(*source) { + // Retain the instruction byte, in case additional decoding is deferred + // to the ModRM byte. + instr_ = *source; + switch(instr_) { #define PartialBlock(start, operation) \ MapPartial(start + 0x00, operation, Byte, MemReg_Reg, ModRM); \ MapPartial(start + 0x01, operation, Word, MemReg_Reg, ModRM); \ @@ -110,6 +113,42 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { MapPartial(0x7e, JLE, Byte, Disp, AwaitingOperands); MapPartial(0x7f, JNLE, Byte, Disp, AwaitingOperands); + // TODO: + // + // 0x80, 0x81, 0x82, 0x83, which all require more + // input, from the ModRM byte. + + MapPartial(0x84, TEST, Byte, MemReg_Reg, ModRM); + MapPartial(0x85, TEST, Word, MemReg_Reg, ModRM); + MapPartial(0x86, XCHG, Byte, Reg_MemReg, ModRM); + MapPartial(0x87, XCHG, Word, Reg_MemReg, ModRM); + MapPartial(0x88, MOV, Byte, MemReg_Reg, ModRM); + MapPartial(0x89, MOV, Word, MemReg_Reg, ModRM); + MapPartial(0x8a, MOV, Byte, Reg_MemReg, ModRM); + MapPartial(0x8b, MOV, Word, Reg_MemReg, ModRM); + /* 0x8c: not used. */ + MapPartial(0x8d, LEA, Word, Reg_Addr, ModRM); + MapPartial(0x8e, MOV, Word, SegReg_MemReg, ModRM); + + // TODO: 0x8f, which requires further selection from the ModRM byte. + + MapComplete(0x90, NOP, Implied, None, None); // Or XCHG AX, AX? + MapComplete(0x91, XCHG, Word, AX, CX); + MapComplete(0x92, XCHG, Word, AX, DX); + MapComplete(0x93, XCHG, Word, AX, BX); + MapComplete(0x94, XCHG, Word, AX, SP); + MapComplete(0x95, XCHG, Word, AX, BP); + MapComplete(0x96, XCHG, Word, AX, SI); + MapComplete(0x97, XCHG, Word, AX, DI); + + MapComplete(0x98, CBW, Implied, None, None); + MapComplete(0x99, CWD, Implied, None, None); + MapPartial(0x9a, CALL, Word, Disp, AwaitingOperands); + MapComplete(0x9b, WAIT, Implied, None, None); + MapComplete(0x9c, PUSHF, Implied, None, None); + MapComplete(0x9d, POPF, Implied, None, None); + MapComplete(0x9e, SAHF, Implied, None, None); + MapComplete(0x9f, LAHF, Implied, None, None); } ++source; ++consumed_; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index c8616d4d5..71a4706e4 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -109,6 +109,8 @@ struct Decoder { Reg_MemReg, Ac_Data, MemReg_Data, + SegReg_MemReg, + Reg_Addr, Disp } format_ = Format::MemReg_Reg; @@ -118,6 +120,7 @@ struct Decoder { Source source_ = Source::None; Source destination_ = Source::None; Source segment_override_ = Source::None; + uint8_t instr_ = 0x00; }; } From 32c942d1548594754da89b12d4487f290092e560 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 2 Jan 2021 21:11:19 -0500 Subject: [PATCH 13/52] Muddles drunkenly towards decoding ModRM. --- Processors/Decoders/x86/x86.cpp | 238 +++++++++++++++++++++----------- Processors/Decoders/x86/x86.hpp | 29 +++- 2 files changed, 184 insertions(+), 83 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index a8832a065..119b7c6d0 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -8,26 +8,27 @@ #include "x86.hpp" +#include + using namespace CPU::Decoder::x86; Instruction Decoder::decode(uint8_t *source, size_t length) { uint8_t *const limit = source + length; -#define MapPartial(value, op, sz, fmt, phs) \ - case value: \ - operation_ = Operation::op; \ - operand_size_ = Size::sz; \ - format_ = Format::fmt; \ - phase_ = Phase::phs; \ +#define MapPartial(value, op, lrg, fmt, phs) \ + case value: \ + operation_ = Operation::op; \ + large_operand_ = lrg; \ + format_ = Format::fmt; \ + phase_ = Phase::phs; \ break -#define MapComplete(value, op, sz, src, dest) \ - case value: \ - operation_ = Operation::op; \ - operand_size_ = Size::sz; \ - source_ = Source::src; \ - destination_ = Source::dest; \ - phase_ = Phase::ReadyToPost; \ +#define MapComplete(value, op, src, dest) \ + case value: \ + operation_ = Operation::op; \ + source_ = Source::src; \ + destination_ = Source::dest; \ + phase_ = Phase::ReadyToPost; \ break while(phase_ == Phase::Instruction && source != limit) { @@ -36,56 +37,56 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { instr_ = *source; switch(instr_) { #define PartialBlock(start, operation) \ - MapPartial(start + 0x00, operation, Byte, MemReg_Reg, ModRM); \ - MapPartial(start + 0x01, operation, Word, MemReg_Reg, ModRM); \ - MapPartial(start + 0x02, operation, Byte, Reg_MemReg, ModRM); \ - MapPartial(start + 0x03, operation, Word, Reg_MemReg, ModRM); \ - MapPartial(start + 0x04, operation, Byte, Ac_Data, AwaitingOperands); \ - MapPartial(start + 0x05, operation, Word, Ac_Data, AwaitingOperands); + MapPartial(start + 0x00, operation, false, MemReg_Reg, ModRM); \ + MapPartial(start + 0x01, operation, true, MemReg_Reg, ModRM); \ + MapPartial(start + 0x02, operation, false, Reg_MemReg, ModRM); \ + MapPartial(start + 0x03, operation, true, Reg_MemReg, ModRM); \ + MapPartial(start + 0x04, operation, false, Ac_Data, AwaitingOperands); \ + MapPartial(start + 0x05, operation, true, Ac_Data, AwaitingOperands); PartialBlock(0x00, ADD); - MapComplete(0x06, PUSH, Word, ES, None); - MapComplete(0x07, POP, Word, ES, None); + MapComplete(0x06, PUSH, ES, None); + MapComplete(0x07, POP, ES, None); PartialBlock(0x08, OR); - MapComplete(0x0e, PUSH, Word, CS, None); + MapComplete(0x0e, PUSH, CS, None); /* 0x0f: not used. */ PartialBlock(0x10, ADC); - MapComplete(0x16, PUSH, Word, SS, None); - MapComplete(0x17, POP, Word, SS, None); + MapComplete(0x16, PUSH, SS, None); + MapComplete(0x17, POP, SS, None); PartialBlock(0x18, SBB); - MapComplete(0x1e, PUSH, Word, DS, None); - MapComplete(0x1f, POP, Word, DS, None); + MapComplete(0x1e, PUSH, DS, None); + MapComplete(0x1f, POP, DS, None); PartialBlock(0x20, AND); case 0x26: segment_override_ = Source::ES; break; - MapComplete(0x27, DAA, Implied, None, None); + MapComplete(0x27, DAA, None, None); PartialBlock(0x28, SUB); case 0x2e: segment_override_ = Source::CS; break; - MapComplete(0x2f, DAS, Implied, None, None); + MapComplete(0x2f, DAS, None, None); PartialBlock(0x30, XOR); case 0x36: segment_override_ = Source::SS; break; - MapComplete(0x37, AAA, Implied, None, None); + MapComplete(0x37, AAA, None, None); PartialBlock(0x38, CMP); case 0x3e: segment_override_ = Source::DS; break; - MapComplete(0x3f, AAS, Implied, None, None); + MapComplete(0x3f, AAS, None, None); #undef PartialBlock #define RegisterBlock(start, operation) \ - MapComplete(start + 0x00, operation, Word, AX, AX); \ - MapComplete(start + 0x01, operation, Word, CX, CX); \ - MapComplete(start + 0x02, operation, Word, DX, DX); \ - MapComplete(start + 0x03, operation, Word, BX, BX); \ - MapComplete(start + 0x04, operation, Word, SP, SP); \ - MapComplete(start + 0x05, operation, Word, BP, BP); \ - MapComplete(start + 0x06, operation, Word, SI, SI); \ - MapComplete(start + 0x07, operation, Word, DI, DI); \ + MapComplete(start + 0x00, operation, AX, AX); \ + MapComplete(start + 0x01, operation, CX, CX); \ + MapComplete(start + 0x02, operation, DX, DX); \ + MapComplete(start + 0x03, operation, BX, BX); \ + MapComplete(start + 0x04, operation, SP, SP); \ + MapComplete(start + 0x05, operation, BP, BP); \ + MapComplete(start + 0x06, operation, SI, SI); \ + MapComplete(start + 0x07, operation, DI, DI); \ RegisterBlock(0x40, INC); RegisterBlock(0x48, DEC); @@ -96,59 +97,68 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { /* 0x60–0x6f: not used. */ - MapPartial(0x70, JO, Byte, Disp, AwaitingOperands); - MapPartial(0x71, JNO, Byte, Disp, AwaitingOperands); - MapPartial(0x72, JB, Byte, Disp, AwaitingOperands); - MapPartial(0x73, JNB, Byte, Disp, AwaitingOperands); - MapPartial(0x74, JE, Byte, Disp, AwaitingOperands); - MapPartial(0x75, JNE, Byte, Disp, AwaitingOperands); - MapPartial(0x76, JBE, Byte, Disp, AwaitingOperands); - MapPartial(0x77, JNBE, Byte, Disp, AwaitingOperands); - MapPartial(0x78, JS, Byte, Disp, AwaitingOperands); - MapPartial(0x79, JNS, Byte, Disp, AwaitingOperands); - MapPartial(0x7a, JP, Byte, Disp, AwaitingOperands); - MapPartial(0x7b, JNP, Byte, Disp, AwaitingOperands); - MapPartial(0x7c, JL, Byte, Disp, AwaitingOperands); - MapPartial(0x7d, JNL, Byte, Disp, AwaitingOperands); - MapPartial(0x7e, JLE, Byte, Disp, AwaitingOperands); - MapPartial(0x7f, JNLE, Byte, Disp, AwaitingOperands); +#define MapJump(value, operation) MapPartial(value, operation, false, Disp, AwaitingOperands); + MapJump(0x70, JO); + MapJump(0x71, JNO); + MapJump(0x72, JB); + MapJump(0x73, JNB); + MapJump(0x74, JE); + MapJump(0x75, JNE); + MapJump(0x76, JBE); + MapJump(0x77, JNBE); + MapJump(0x78, JS); + MapJump(0x79, JNS); + MapJump(0x7a, JP); + MapJump(0x7b, JNP); + MapJump(0x7c, JL); + MapJump(0x7d, JNL); + MapJump(0x7e, JLE); + MapJump(0x7f, JNLE); +#undef MapJump // TODO: // // 0x80, 0x81, 0x82, 0x83, which all require more // input, from the ModRM byte. - MapPartial(0x84, TEST, Byte, MemReg_Reg, ModRM); - MapPartial(0x85, TEST, Word, MemReg_Reg, ModRM); - MapPartial(0x86, XCHG, Byte, Reg_MemReg, ModRM); - MapPartial(0x87, XCHG, Word, Reg_MemReg, ModRM); - MapPartial(0x88, MOV, Byte, MemReg_Reg, ModRM); - MapPartial(0x89, MOV, Word, MemReg_Reg, ModRM); - MapPartial(0x8a, MOV, Byte, Reg_MemReg, ModRM); - MapPartial(0x8b, MOV, Word, Reg_MemReg, ModRM); + MapPartial(0x84, TEST, false, MemReg_Reg, ModRM); + MapPartial(0x85, TEST, true, MemReg_Reg, ModRM); + MapPartial(0x86, XCHG, false, Reg_MemReg, ModRM); + MapPartial(0x87, XCHG, true, Reg_MemReg, ModRM); + MapPartial(0x88, MOV, false, MemReg_Reg, ModRM); + MapPartial(0x89, MOV, true, MemReg_Reg, ModRM); + MapPartial(0x8a, MOV, false, Reg_MemReg, ModRM); + MapPartial(0x8b, MOV, true, Reg_MemReg, ModRM); /* 0x8c: not used. */ - MapPartial(0x8d, LEA, Word, Reg_Addr, ModRM); - MapPartial(0x8e, MOV, Word, SegReg_MemReg, ModRM); + MapPartial(0x8d, LEA, true, Reg_Addr, ModRM); + MapPartial(0x8e, MOV, true, SegReg_MemReg, ModRM); // TODO: 0x8f, which requires further selection from the ModRM byte. - MapComplete(0x90, NOP, Implied, None, None); // Or XCHG AX, AX? - MapComplete(0x91, XCHG, Word, AX, CX); - MapComplete(0x92, XCHG, Word, AX, DX); - MapComplete(0x93, XCHG, Word, AX, BX); - MapComplete(0x94, XCHG, Word, AX, SP); - MapComplete(0x95, XCHG, Word, AX, BP); - MapComplete(0x96, XCHG, Word, AX, SI); - MapComplete(0x97, XCHG, Word, AX, DI); + MapComplete(0x90, NOP, None, None); // Or XCHG AX, AX? + MapComplete(0x91, XCHG, AX, CX); + MapComplete(0x92, XCHG, AX, DX); + MapComplete(0x93, XCHG, AX, BX); + MapComplete(0x94, XCHG, AX, SP); + MapComplete(0x95, XCHG, AX, BP); + MapComplete(0x96, XCHG, AX, SI); + MapComplete(0x97, XCHG, AX, DI); - MapComplete(0x98, CBW, Implied, None, None); - MapComplete(0x99, CWD, Implied, None, None); - MapPartial(0x9a, CALL, Word, Disp, AwaitingOperands); - MapComplete(0x9b, WAIT, Implied, None, None); - MapComplete(0x9c, PUSHF, Implied, None, None); - MapComplete(0x9d, POPF, Implied, None, None); - MapComplete(0x9e, SAHF, Implied, None, None); - MapComplete(0x9f, LAHF, Implied, None, None); + MapComplete(0x98, CBW, None, None); + MapComplete(0x99, CWD, None, None); + MapPartial(0x9a, CALL, true, Addr, AwaitingOperands); + MapComplete(0x9b, WAIT, None, None); + MapComplete(0x9c, PUSHF, None, None); + MapComplete(0x9d, POPF, None, None); + MapComplete(0x9e, SAHF, None, None); + MapComplete(0x9f, LAHF, None, None); + + MapPartial(0xa0, MOV, false, Reg_Addr, AwaitingOperands); + + // Other prefix bytes. + case 0xf0: lock_ = true; break; + case 0xf2: repetition_ = Repetition::RepNE; break; + case 0xf3: repetition_ = Repetition::RepE; break; } ++source; ++consumed_; @@ -156,6 +166,74 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { #undef MapInstr + if(phase_ == Phase::ModRM && source != limit) { + const uint8_t mod = *source >> 6; // i.e. mode. + const uint8_t reg = (*source >> 3) & 7; // i.e. register. + const uint8_t rm = *source & 7; // i.e. register/memory. + + switch(format_) { + case Format::Reg_MemReg: + case Format::MemReg_Reg: { + Source memreg; + + constexpr Source reg_table[2][8] = { + { + Source::AL, Source::CL, Source::DL, Source::BL, + Source::AH, Source::CH, Source::DH, Source::BH, + }, { + Source::AX, Source::CX, Source::DX, Source::BX, + Source::SP, Source::BP, Source::SI, Source::DI, + } + }; + + switch(mod) { + case 0: { + add_offset_ = false; + constexpr Source rm_table[8] = { + Source::IndBXPlusSI, Source::IndBXPlusDI, + Source::IndBPPlusSI, Source::IndBPPlusDI, + Source::IndSI, Source::IndDI, + Source::DirectAddress, Source::IndBX, + }; + memreg = rm_table[rm]; + } break; + + default: { + add_offset_ = true; + large_offset_ = (mod == 2); + constexpr Source rm_table[8] = { + Source::IndBXPlusSI, Source::IndBXPlusDI, + Source::IndBPPlusSI, Source::IndBPPlusDI, + Source::IndSI, Source::IndDI, + Source::IndBP, Source::IndBX, + }; + memreg = rm_table[rm]; + } break; + + // Other operand is just a register. + case 3: memreg = reg_table[large_operand_][rm]; break; + } + + if(format_ == Format::Reg_MemReg) { + destination_ = reg_table[large_operand_][reg]; + source_ = memreg; + } else { + source_ = reg_table[large_operand_][reg]; + destination_ = memreg; + } + phase_ = (add_offset_ || memreg == Source::DirectAddress) ? Phase::AwaitingOperands : Phase::ReadyToPost; + } break; + + default: assert(false); + } + + ++source; + ++consumed_; + } + + if(phase_ == Phase::AwaitingOperands && source != limit) { + // TODO: calculate number of expected operands. + } if(phase_ == Phase::ReadyToPost) { // TODO: construct actual Instruction. diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 71a4706e4..9c692747b 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -46,6 +46,7 @@ enum class Size: uint8_t { Implied = 0, Byte = 1, Word = 2, + DWord = 4, }; enum class Source: uint8_t { @@ -60,7 +61,17 @@ enum class Source: uint8_t { SI, DI, BP, SP, - Memory, Immediate + IndBXPlusSI, + IndBXPlusDI, + IndBPPlusSI, + IndBPPlusDI, + IndSI, + IndDI, + DirectAddress, + IndBP, + IndBX, + + Immediate }; class Instruction { @@ -111,16 +122,28 @@ struct Decoder { MemReg_Data, SegReg_MemReg, Reg_Addr, - Disp + Addr_Reg, + Disp, + Addr } format_ = Format::MemReg_Reg; + // TODO: figure out which Formats can be folded together, + // and which are improperly elided. + + enum class Repetition: uint8_t { + None, RepE, RepNE + } repetition_ = Repetition::None; + int consumed_ = 0; Operation operation_ = Operation::Invalid; - Size operand_size_ = Size::Implied; + bool large_operand_ = false; Source source_ = Source::None; Source destination_ = Source::None; Source segment_override_ = Source::None; uint8_t instr_ = 0x00; + bool lock_ = false; + bool add_offset_ = false; + bool large_offset_ = false; }; } From 1a3effc692d5424b164a364fc07e0949dc7d27ae Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 2 Jan 2021 21:19:45 -0500 Subject: [PATCH 14/52] Modifies contract again. This is why I'm doing this now. --- Processors/Decoders/README.md | 8 +++++--- Processors/Decoders/x86/x86.hpp | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Processors/Decoders/README.md b/Processors/Decoders/README.md index c8975eef7..ca5bbcbfe 100644 --- a/Processors/Decoders/README.md +++ b/Processors/Decoders/README.md @@ -36,11 +36,13 @@ If instructions are a variable size, the decoder should maintain internal state A sample interface: - size_t decode(Instruction &target, word_type *stream, size_t length) { ... } + Instruction decode(word_type *stream, size_t length) { ... } -Returns either the number of further words necessary fully to decode, if known, or a negative number to indicate a generic sense of 'more needed'. A value of `0` means 'an instruction was fully decoded', and the instruction itself should be able to report its size. +The returned instruction has a size that is one of: +* a positive number, indicating a successful decoding that consumed that many `word_type`s; or +* a negative number, indicating the [negatived] minimum number of `word_type`s that the caller should try to get hold of before calling `decode` again. -`target` is populated only if an instruction is decoded; `stream` and `length` provide as much further instruction stream data as the caller currently possesses. +A caller is permitted to react in any way it prefers to negative numbers; they're a hint potentially to reduce calling overhead only. ## Tying Decoders into Instruction Executors diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 9c692747b..4ddbe0d1a 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -101,9 +101,10 @@ struct Decoder { /*! @returns an @c Instruction with a positive size to indicate successful decoding; a - negative size specifies the number of further bytes required fully to decode, and - a zero size indicates that further bytes are required but the decoder does not yet - know exactly how many. + negative size specifies the [negatived] number of further bytes the caller should ideally + collect before calling again. The caller is free to call with fewer, but may not get a decoded + instruction in response, and the decoder may still not be able to complete decoding + even if given that number of bytes. */ Instruction decode(uint8_t *source, size_t length); From c934e22cee22b23b5cd1a12244944d127eaa6862 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 2 Jan 2021 22:47:42 -0500 Subject: [PATCH 15/52] Introduces a first test of PowerPC decoding. Corrected as a result: the bcx conditional, that stdu is 64-bit only, extraction of the li field. --- .../Clock Signal.xcodeproj/project.pbxproj | 4 + .../Clock SignalTests/PowerPCDecoderTests.mm | 203 ++++++++++++++++++ Processors/Decoders/PowerPC/PowerPC.cpp | 16 +- Processors/Decoders/PowerPC/PowerPC.hpp | 11 +- 4 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index be155af1e..7abbafd64 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -185,6 +185,7 @@ 4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BF5AE1F146264005B6C36 /* CSW.cpp */; }; 4B3F76AE25A0196100178AEC /* x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76AD25A0196100178AEC /* x86.cpp */; }; 4B3F76AF25A0196100178AEC /* x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76AD25A0196100178AEC /* x86.cpp */; }; + 4B3F76B925A1635300178AEC /* PowerPCDecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */; }; 4B3FCC40201EC24200960631 /* MultiMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */; }; 4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */; }; 4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B448E7F1F1C45A00009ABD6 /* TZX.cpp */; }; @@ -1081,6 +1082,7 @@ 4B3F769E259FCE0F00178AEC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 4B3F76AC25A0196100178AEC /* x86.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = x86.hpp; sourceTree = ""; }; 4B3F76AD25A0196100178AEC /* x86.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x86.cpp; sourceTree = ""; }; + 4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PowerPCDecoderTests.mm; sourceTree = ""; }; 4B3FCC3E201EC24200960631 /* MultiMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMachine.hpp; sourceTree = ""; }; 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMachine.cpp; sourceTree = ""; }; 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPM.cpp; path = Parsers/CPM.cpp; sourceTree = ""; }; @@ -3479,6 +3481,7 @@ 4BC0CB272446BC7B00A79DBB /* OPLTests.mm */, 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */, 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */, + 4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */, 4BE76CF822641ED300ACD6FA /* QLTests.mm */, 4B2AF8681E513FC20027EE29 /* TIATests.mm */, 4B1D08051E0F7A1100763741 /* TimeTests.mm */, @@ -4919,6 +4922,7 @@ 4B778F4223A5F1A70000D260 /* MemoryFuzzer.cpp in Sources */, 4B778F0123A5EBA00000D260 /* MacintoshIMG.cpp in Sources */, 4BFF1D3D2235C3C100838EA1 /* EmuTOSTests.mm in Sources */, + 4B3F76B925A1635300178AEC /* PowerPCDecoderTests.mm in Sources */, 4B778F0A23A5EC150000D260 /* TapePRG.cpp in Sources */, 4B778F0823A5EC150000D260 /* CSW.cpp in Sources */, 4B778F5323A5F23F0000D260 /* SerialBus.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm new file mode 100644 index 000000000..87f4aa444 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm @@ -0,0 +1,203 @@ +// +// PowerPCDecoderTests.m +// Clock Signal +// +// Created by Thomas Harte on 02/01/2021. +// Copyright 2021 Thomas Harte. All rights reserved. +// + +#import + +#include "../../../Processors/Decoders/PowerPC/PowerPC.hpp" + +namespace { + using Operation = CPU::Decoder::PowerPC::Operation; +} + +@interface PowerPCDecoderTests : XCTestCase +@end + +/*! + Tests PowerPC decoding by throwing a bunch of randomly-generated + word streams and checking that the result matches what I got from a + disassembler elsewhere. +*/ +@implementation PowerPCDecoderTests { + CPU::Decoder::PowerPC::Instruction instructions[32]; +} + +- (void)decode:(const uint32_t *)stream { + CPU::Decoder::PowerPC::Decoder decoder(CPU::Decoder::PowerPC::Model::MPC601); + for(int c = 0; c < 32; c++) { + instructions[c] = decoder.decode(stream[c]); + } +} + +- (void)testStream1 { + const uint32_t stream[] = { + 0x32eeefa5, 0xc2ee0786, 0x80ce552c, 0x88d5f02a, + 0xf8c2e801, 0xe83d5cdf, 0x7fa51fbb, 0xaacea8b0, + 0x7d4d4d21, 0x1314cf89, 0x47e0014b, 0xdf67566d, + 0xfb29a33e, 0x312cbf53, 0x706f1a15, 0xa87b7011, + 0x2107090c, 0xce04935e, 0x642e464a, 0x931e8eba, + 0xee396d5b, 0x4901183d, 0x31ccaa9a, 0x42b61a86, + 0x1ed4751d, 0x86af76e4, 0x151405a9, 0xca0ac015, + 0x60dd1f9d, 0xecff44f6, 0xf2c1110e, 0x9aa6653b, + }; + [self decode:stream]; + + // addic r23,r14,-4187 + XCTAssertEqual(instructions[0].operation, Operation::addic); + XCTAssertEqual(instructions[0].rD(), 23); + XCTAssertEqual(instructions[0].rA(), 14); + XCTAssertEqual(instructions[0].simm(), -4187); + + // lfs f23,1926(r14) + XCTAssertEqual(instructions[1].operation, Operation::lfs); + XCTAssertEqual(instructions[1].frD(), 23); + XCTAssertEqual(instructions[1].rA(), 14); + XCTAssertEqual(instructions[1].d(), 1926); + + // lwz r6,21804(r14) + XCTAssertEqual(instructions[2].operation, Operation::lwz); + XCTAssertEqual(instructions[2].rD(), 6); + XCTAssertEqual(instructions[2].rA(), 14); + XCTAssertEqual(instructions[2].d(), 21804); + + // lbz r6,-4054(r21) + XCTAssertEqual(instructions[3].operation, Operation::lbz); + XCTAssertEqual(instructions[3].rD(), 6); + XCTAssertEqual(instructions[3].rA(), 21); + XCTAssertEqual(instructions[3].d(), -4054); + + // .long 0xf8c2e801 + // .long 0xe83d5cdf + // .long 0x7fa51fbb + XCTAssertEqual(instructions[4].operation, Operation::Undefined); + XCTAssertEqual(instructions[5].operation, Operation::Undefined); + XCTAssertEqual(instructions[6].operation, Operation::Undefined); + + // lha r22,-22352(r14) + XCTAssertEqual(instructions[7].operation, Operation::lha); + XCTAssertEqual(instructions[7].rD(), 22); + XCTAssertEqual(instructions[7].rA(), 14); + XCTAssertEqual(instructions[7].d(), -22352); + + // .long 0x7d4d4d21 + // .long 0x1314cf89 + // .long 0x47e0014b + XCTAssertEqual(instructions[8].operation, Operation::Undefined); + XCTAssertEqual(instructions[9].operation, Operation::Undefined); + // CLK decodes this as sc because it ignores reserved bits; the disassembler + // I used checks the reserved bits. For now: don't test. +// XCTAssertEqual(instructions[10].operation, Operation::Undefined); + + // stfdu f27,22125(r7) + XCTAssertEqual(instructions[11].operation, Operation::stfdu); + XCTAssertEqual(instructions[11].frS(), 27); + XCTAssertEqual(instructions[11].rA(), 7); + XCTAssertEqual(instructions[11].d(), 22125); + + // .long 0xfb29a33e + XCTAssertEqual(instructions[12].operation, Operation::Undefined); + + // addic r9,r12,-16557 + XCTAssertEqual(instructions[13].operation, Operation::addic); + XCTAssertEqual(instructions[13].rD(), 9); + XCTAssertEqual(instructions[13].rA(), 12); + XCTAssertEqual(instructions[13].simm(), -16557); + + // andi. r15,r3,6677 + XCTAssertEqual(instructions[14].operation, Operation::andi_); + XCTAssertEqual(instructions[14].rA(), 15); + XCTAssertEqual(instructions[14].rS(), 3); + XCTAssertEqual(instructions[14].uimm(), 6677); + + // lha r3,28689(r27) + XCTAssertEqual(instructions[15].operation, Operation::lha); + XCTAssertEqual(instructions[15].rD(), 3); + XCTAssertEqual(instructions[15].rA(), 27); + XCTAssertEqual(instructions[15].d(), 28689); + + // subfic r8,r7,2316 + XCTAssertEqual(instructions[16].operation, Operation::subfic); + XCTAssertEqual(instructions[16].rD(), 8); + XCTAssertEqual(instructions[16].rA(), 7); + XCTAssertEqual(instructions[16].simm(), 2316); + + // lfdu f16,-27810(r4) + XCTAssertEqual(instructions[17].operation, Operation::lfdu); + XCTAssertEqual(instructions[17].frD(), 16); + XCTAssertEqual(instructions[17].rA(), 4); + XCTAssertEqual(instructions[17].d(), -27810); + + // oris r14,r1,17994 + XCTAssertEqual(instructions[18].operation, Operation::oris); + XCTAssertEqual(instructions[18].rA(), 14); + XCTAssertEqual(instructions[18].rS(), 1); + XCTAssertEqual(instructions[18].uimm(), 17994); + + // stw r24,-28998(r30) + XCTAssertEqual(instructions[19].operation, Operation::stw); + XCTAssertEqual(instructions[19].rS(), 24); + XCTAssertEqual(instructions[19].rA(), 30); + XCTAssertEqual(instructions[19].d(), -28998); + + // .long 0xee396d5b + XCTAssertEqual(instructions[20].operation, Operation::Undefined); + + // bl 0x01011890 [disassmebled at address 0x54] + XCTAssertEqual(instructions[21].operation, Operation::bx); + XCTAssertEqual(instructions[21].li() + 0x54, 0x01011890); + XCTAssertTrue(instructions[21].lk()); + XCTAssertFalse(instructions[21].aa()); + + // addic r14,r12,-21862 + XCTAssertEqual(instructions[22].operation, Operation::addic); + XCTAssertEqual(instructions[22].rD(), 14); + XCTAssertEqual(instructions[22].rA(), 12); + XCTAssertEqual(instructions[22].simm(), -21862); + + // .long 0x42b61a86 [10101] + XCTAssertEqual(instructions[23].operation, Operation::Undefined); + + // mulli r22,r20,29981 + XCTAssertEqual(instructions[24].operation, Operation::mulli); + XCTAssertEqual(instructions[24].rD(), 22); + XCTAssertEqual(instructions[24].rA(), 20); + XCTAssertEqual(instructions[24].simm(), 29981); + + // lwzu r21,30436(r15) + XCTAssertEqual(instructions[25].operation, Operation::lwzu); + XCTAssertEqual(instructions[25].rD(), 21); + XCTAssertEqual(instructions[25].rA(), 15); + XCTAssertEqual(instructions[25].d(), 30436); + + // .long 0x151405a9 + XCTAssertEqual(instructions[26].operation, Operation::Undefined); + + // lfd f16,-16363(r10) + XCTAssertEqual(instructions[27].operation, Operation::lfd); + XCTAssertEqual(instructions[27].frD(), 16); + XCTAssertEqual(instructions[27].rA(), 10); + XCTAssertEqual(instructions[27].d(), -16363); + + // ori r29,r6,8093 + XCTAssertEqual(instructions[28].operation, Operation::ori); + XCTAssertEqual(instructions[28].rA(), 29); + XCTAssertEqual(instructions[28].rS(), 6); + XCTAssertEqual(instructions[28].uimm(), 8093); + + // .long 0xecff44f6 + // .long 0xf2c1110e + XCTAssertEqual(instructions[29].operation, Operation::Undefined); + XCTAssertEqual(instructions[30].operation, Operation::Undefined); + + // stb r21,25915(r6) + XCTAssertEqual(instructions[31].operation, Operation::stb); + XCTAssertEqual(instructions[31].rS(), 21); + XCTAssertEqual(instructions[31].rA(), 6); + XCTAssertEqual(instructions[31].d(), 25915); +} + +@end diff --git a/Processors/Decoders/PowerPC/PowerPC.cpp b/Processors/Decoders/PowerPC/PowerPC.cpp index 39fcea43a..bf9105e0d 100644 --- a/Processors/Decoders/PowerPC/PowerPC.cpp +++ b/Processors/Decoders/PowerPC/PowerPC.cpp @@ -58,7 +58,17 @@ Instruction Decoder::decode(uint32_t opcode) { Bind(Six(0b001000), subfic); Bind(Six(0b001100), addic); Bind(Six(0b001101), addic_); Bind(Six(0b001110), addi); Bind(Six(0b001111), addis); - Bind(Six(0b010000), bcx); + case Six(0b010000): { + // This might be a bcx, but check for a valid bo field. + switch((opcode >> 21) & 0x1f) { + case 0: case 1: case 2: case 3: case 4: case 5: + case 8: case 9: case 10: case 11: case 12: case 13: + case 16: case 17: case 18: case 19: case 20: + return Instruction(Operation::bcx, opcode); + + default: return Instruction(opcode); + } + } break; Bind(Six(0b010010), bx); Bind(Six(0b010100), rlwimix); Bind(Six(0b010101), rlwinmx); @@ -317,7 +327,9 @@ Instruction Decoder::decode(uint32_t opcode) { // std and stdu switch(opcode & 0b111111'00'00000000'00000000'000000'11){ case 0b111110'00'00000000'00000000'000000'00: return Instruction(Operation::std, opcode); - case 0b111110'00'00000000'00000000'000000'01: return Instruction(Operation::stdu, opcode); + case 0b111110'00'00000000'00000000'000000'01: + if(is64bit()) return Instruction(Operation::stdu, opcode); + return Instruction(opcode); } // sc diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 9b6613b6d..b73ddd5ef 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -75,15 +75,16 @@ enum class Operation: uint8_t { only the operation has been decoded ahead of time; all other fields are decoded on-demand. */ struct Instruction { - const Operation operation = Operation::Undefined; - const bool is_supervisor = false; - const uint32_t opcode = 0; + Operation operation = Operation::Undefined; + bool is_supervisor = false; + uint32_t opcode = 0; // PowerPC uses a fixed-size instruction word. - size_t size() { + int size() { return 4; } + Instruction() {} Instruction(uint32_t opcode) : opcode(opcode) {} Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {} @@ -177,7 +178,7 @@ struct Instruction { 0x0000'0000, 0xfc00'0000 }; - const uint32_t value = (opcode & 0x3fff'fffc) | extensions[(opcode >> 26) & 1]; + const uint32_t value = (opcode & 0x03ff'fffc) | extensions[(opcode >> 26) & 1]; return int32_t(value); } From eb8d0eefd546cedd7041c3983e06187e275d900f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 11:14:30 -0500 Subject: [PATCH 16/52] Factors out some boilerplate and introduces second sequence. --- .../Clock SignalTests/PowerPCDecoderTests.mm | 312 +++++++++++------- 1 file changed, 198 insertions(+), 114 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm index 87f4aa444..192caba3a 100644 --- a/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm @@ -12,6 +12,7 @@ namespace { using Operation = CPU::Decoder::PowerPC::Operation; + using Instruction = CPU::Decoder::PowerPC::Instruction; } @interface PowerPCDecoderTests : XCTestCase @@ -23,9 +24,83 @@ namespace { disassembler elsewhere. */ @implementation PowerPCDecoderTests { - CPU::Decoder::PowerPC::Instruction instructions[32]; + Instruction instructions[32]; } +// MARK: - Specific instruction asserts. + +- (void)assertUndefined:(Instruction &)instruction { + XCTAssertEqual(instruction.operation, Operation::Undefined); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation rD:(int)rD rA:(int)rA simm:(int)simm { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.rD(), rD); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.simm(), simm); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation rD:(int)rD rA:(int)rA d:(int)d { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.rD(), rD); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.d(), d); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation rA:(int)rA rS:(int)rS uimm:(int)uimm { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.rS(), rS); + XCTAssertEqual(instruction.uimm(), uimm); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation rS:(int)rS rA:(int)rA d:(int)d { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.rS(), rS); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.d(), d); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation frD:(int)frD rA:(int)rA d:(int)d { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.frD(), frD); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.d(), d); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation frS:(int)frS rA:(int)rA d:(int)d { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.frS(), frS); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.d(), d); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation li:(int)li lk:(BOOL)lk aa:(BOOL)aa { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.li(), li); + XCTAssertEqual(!!instruction.lk(), lk); + XCTAssertEqual(!!instruction.aa(), aa); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation rA:(int)rA rS:(int)rS rB:(int)rB mb:(int)mb me:(int)me rc:(BOOL)rc { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.rS(), rS); + XCTAssertEqual(instruction.rB(), rB); + XCTAssertEqual(instruction.mb(), mb); + XCTAssertEqual(instruction.me(), me); + XCTAssertEqual(!!instruction.rc(), rc); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation to:(int)to rA:(int)rA simm:(int)simm { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.to(), to); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.simm(), simm); +} + +// MARK: - Decoder + - (void)decode:(const uint32_t *)stream { CPU::Decoder::PowerPC::Decoder decoder(CPU::Decoder::PowerPC::Model::MPC601); for(int c = 0; c < 32; c++) { @@ -33,8 +108,10 @@ namespace { } } -- (void)testStream1 { - const uint32_t stream[] = { +// MARK: - Tests + +- (void)testSequence1 { + const uint32_t sequence[] = { 0x32eeefa5, 0xc2ee0786, 0x80ce552c, 0x88d5f02a, 0xf8c2e801, 0xe83d5cdf, 0x7fa51fbb, 0xaacea8b0, 0x7d4d4d21, 0x1314cf89, 0x47e0014b, 0xdf67566d, @@ -44,160 +121,167 @@ namespace { 0x1ed4751d, 0x86af76e4, 0x151405a9, 0xca0ac015, 0x60dd1f9d, 0xecff44f6, 0xf2c1110e, 0x9aa6653b, }; - [self decode:stream]; + [self decode:sequence]; // addic r23,r14,-4187 - XCTAssertEqual(instructions[0].operation, Operation::addic); - XCTAssertEqual(instructions[0].rD(), 23); - XCTAssertEqual(instructions[0].rA(), 14); - XCTAssertEqual(instructions[0].simm(), -4187); - // lfs f23,1926(r14) - XCTAssertEqual(instructions[1].operation, Operation::lfs); - XCTAssertEqual(instructions[1].frD(), 23); - XCTAssertEqual(instructions[1].rA(), 14); - XCTAssertEqual(instructions[1].d(), 1926); - // lwz r6,21804(r14) - XCTAssertEqual(instructions[2].operation, Operation::lwz); - XCTAssertEqual(instructions[2].rD(), 6); - XCTAssertEqual(instructions[2].rA(), 14); - XCTAssertEqual(instructions[2].d(), 21804); - // lbz r6,-4054(r21) - XCTAssertEqual(instructions[3].operation, Operation::lbz); - XCTAssertEqual(instructions[3].rD(), 6); - XCTAssertEqual(instructions[3].rA(), 21); - XCTAssertEqual(instructions[3].d(), -4054); + [self assert:instructions[0] operation:Operation::addic rD:23 rA:14 simm:-4187]; + [self assert:instructions[1] operation:Operation::lfs frD:23 rA:14 d:1926]; + [self assert:instructions[2] operation:Operation::lwz rD:6 rA:14 d:21804]; + [self assert:instructions[3] operation:Operation::lbz rD:6 rA:21 d:-4054]; // .long 0xf8c2e801 // .long 0xe83d5cdf // .long 0x7fa51fbb - XCTAssertEqual(instructions[4].operation, Operation::Undefined); - XCTAssertEqual(instructions[5].operation, Operation::Undefined); - XCTAssertEqual(instructions[6].operation, Operation::Undefined); - // lha r22,-22352(r14) - XCTAssertEqual(instructions[7].operation, Operation::lha); - XCTAssertEqual(instructions[7].rD(), 22); - XCTAssertEqual(instructions[7].rA(), 14); - XCTAssertEqual(instructions[7].d(), -22352); + [self assertUndefined:instructions[4]]; + [self assertUndefined:instructions[5]]; + [self assertUndefined:instructions[6]]; + [self assert:instructions[7] operation:Operation::lha rD:22 rA:14 d:-22352]; // .long 0x7d4d4d21 // .long 0x1314cf89 // .long 0x47e0014b - XCTAssertEqual(instructions[8].operation, Operation::Undefined); - XCTAssertEqual(instructions[9].operation, Operation::Undefined); + [self assertUndefined:instructions[8]]; + [self assertUndefined:instructions[9]]; // CLK decodes this as sc because it ignores reserved bits; the disassembler // I used checks the reserved bits. For now: don't test. // XCTAssertEqual(instructions[10].operation, Operation::Undefined); // stfdu f27,22125(r7) - XCTAssertEqual(instructions[11].operation, Operation::stfdu); - XCTAssertEqual(instructions[11].frS(), 27); - XCTAssertEqual(instructions[11].rA(), 7); - XCTAssertEqual(instructions[11].d(), 22125); - // .long 0xfb29a33e - XCTAssertEqual(instructions[12].operation, Operation::Undefined); - // addic r9,r12,-16557 - XCTAssertEqual(instructions[13].operation, Operation::addic); - XCTAssertEqual(instructions[13].rD(), 9); - XCTAssertEqual(instructions[13].rA(), 12); - XCTAssertEqual(instructions[13].simm(), -16557); - // andi. r15,r3,6677 - XCTAssertEqual(instructions[14].operation, Operation::andi_); - XCTAssertEqual(instructions[14].rA(), 15); - XCTAssertEqual(instructions[14].rS(), 3); - XCTAssertEqual(instructions[14].uimm(), 6677); + [self assert:instructions[11] operation:Operation::stfdu frS:27 rA:7 d:22125]; + [self assertUndefined:instructions[12]]; + [self assert:instructions[13] operation:Operation::addic rD:9 rA:12 simm:-16557]; + [self assert:instructions[14] operation:Operation::andi_ rA:15 rS:3 uimm:6677]; // lha r3,28689(r27) - XCTAssertEqual(instructions[15].operation, Operation::lha); - XCTAssertEqual(instructions[15].rD(), 3); - XCTAssertEqual(instructions[15].rA(), 27); - XCTAssertEqual(instructions[15].d(), 28689); - // subfic r8,r7,2316 - XCTAssertEqual(instructions[16].operation, Operation::subfic); - XCTAssertEqual(instructions[16].rD(), 8); - XCTAssertEqual(instructions[16].rA(), 7); - XCTAssertEqual(instructions[16].simm(), 2316); - // lfdu f16,-27810(r4) - XCTAssertEqual(instructions[17].operation, Operation::lfdu); - XCTAssertEqual(instructions[17].frD(), 16); - XCTAssertEqual(instructions[17].rA(), 4); - XCTAssertEqual(instructions[17].d(), -27810); - // oris r14,r1,17994 - XCTAssertEqual(instructions[18].operation, Operation::oris); - XCTAssertEqual(instructions[18].rA(), 14); - XCTAssertEqual(instructions[18].rS(), 1); - XCTAssertEqual(instructions[18].uimm(), 17994); + [self assert:instructions[15] operation:Operation::lha rD:3 rA:27 d:28689]; + [self assert:instructions[16] operation:Operation::subfic rD:8 rA:7 simm:2316]; + [self assert:instructions[17] operation:Operation::lfdu frD:16 rA:4 d:-27810]; + [self assert:instructions[18] operation:Operation::oris rA:14 rS:1 uimm:17994]; // stw r24,-28998(r30) - XCTAssertEqual(instructions[19].operation, Operation::stw); - XCTAssertEqual(instructions[19].rS(), 24); - XCTAssertEqual(instructions[19].rA(), 30); - XCTAssertEqual(instructions[19].d(), -28998); - // .long 0xee396d5b - XCTAssertEqual(instructions[20].operation, Operation::Undefined); - // bl 0x01011890 [disassmebled at address 0x54] - XCTAssertEqual(instructions[21].operation, Operation::bx); - XCTAssertEqual(instructions[21].li() + 0x54, 0x01011890); - XCTAssertTrue(instructions[21].lk()); - XCTAssertFalse(instructions[21].aa()); - // addic r14,r12,-21862 - XCTAssertEqual(instructions[22].operation, Operation::addic); - XCTAssertEqual(instructions[22].rD(), 14); - XCTAssertEqual(instructions[22].rA(), 12); - XCTAssertEqual(instructions[22].simm(), -21862); + [self assert:instructions[19] operation:Operation::stw rS:24 rA:30 d:-28998]; + [self assertUndefined:instructions[20]]; + [self assert:instructions[21] operation:Operation::bx li:0x01011890 - 0x54 lk:TRUE aa:FALSE]; + [self assert:instructions[22] operation:Operation::addic rD:14 rA:12 simm:-21862]; // .long 0x42b61a86 [10101] - XCTAssertEqual(instructions[23].operation, Operation::Undefined); - // mulli r22,r20,29981 - XCTAssertEqual(instructions[24].operation, Operation::mulli); - XCTAssertEqual(instructions[24].rD(), 22); - XCTAssertEqual(instructions[24].rA(), 20); - XCTAssertEqual(instructions[24].simm(), 29981); - // lwzu r21,30436(r15) - XCTAssertEqual(instructions[25].operation, Operation::lwzu); - XCTAssertEqual(instructions[25].rD(), 21); - XCTAssertEqual(instructions[25].rA(), 15); - XCTAssertEqual(instructions[25].d(), 30436); - // .long 0x151405a9 - XCTAssertEqual(instructions[26].operation, Operation::Undefined); + [self assertUndefined:instructions[23]]; + [self assert:instructions[24] operation:Operation::mulli rD:22 rA:20 simm:29981]; + [self assert:instructions[25] operation:Operation::lwzu rD:21 rA:15 d:30436]; + [self assertUndefined:instructions[26]]; // lfd f16,-16363(r10) - XCTAssertEqual(instructions[27].operation, Operation::lfd); - XCTAssertEqual(instructions[27].frD(), 16); - XCTAssertEqual(instructions[27].rA(), 10); - XCTAssertEqual(instructions[27].d(), -16363); - // ori r29,r6,8093 - XCTAssertEqual(instructions[28].operation, Operation::ori); - XCTAssertEqual(instructions[28].rA(), 29); - XCTAssertEqual(instructions[28].rS(), 6); - XCTAssertEqual(instructions[28].uimm(), 8093); - // .long 0xecff44f6 // .long 0xf2c1110e - XCTAssertEqual(instructions[29].operation, Operation::Undefined); - XCTAssertEqual(instructions[30].operation, Operation::Undefined); - // stb r21,25915(r6) - XCTAssertEqual(instructions[31].operation, Operation::stb); - XCTAssertEqual(instructions[31].rS(), 21); - XCTAssertEqual(instructions[31].rA(), 6); - XCTAssertEqual(instructions[31].d(), 25915); + [self assert:instructions[27] operation:Operation::lfd frD:16 rA:10 d:-16363]; + [self assert:instructions[28] operation:Operation::ori rA:29 rS:6 uimm:8093]; + [self assertUndefined:instructions[29]]; + [self assertUndefined:instructions[30]]; + [self assert:instructions[31] operation:Operation::stb rS:21 rA:6 d:25915]; +} + +- (void)testSequence2 { + const uint32_t sequence[] = { + 0x90252dae, 0x7429ee14, 0x618935bc, 0xd6c94af0, + 0xba1d295f, 0x649e3869, 0x6def742c, 0x5c64cdce, + 0x762d59ee, 0x565c8189, 0xc7c59f81, 0xce1157fd, + 0xc86aef59, 0x81325882, 0x1336fad6, 0xe1ddfa2b, + 0x18c60357, 0x4c122cb5, 0xccb1f749, 0xdbdcebc3, + 0x0fc60187, 0x117eb911, 0x80334c43, 0xe65371e8, + 0xa047c94d, 0xe671dd0b, 0xe07992bb, 0x6a332fe8, + 0xfc361c6b, 0x5e8b5a28, 0xb2b64a22, 0x045dd156, + }; + [self decode:sequence]; + + // stw r1,11694(r5) + // andis. r9,r1,60948 + // ori r9,r12,13756 + // stfsu f22,19184(r9) + [self assert:instructions[0] operation:Operation::stw rS:1 rA:5 d:11694]; + [self assert:instructions[1] operation:Operation::andis_ rA:9 rS:1 uimm:60948]; + [self assert:instructions[2] operation:Operation::ori rA:9 rS:12 uimm:13756]; + [self assert:instructions[3] operation:Operation::stfsu frS:22 rA:9 d:19184]; + + // lmw r16,10591(r29) + // oris r30,r4,14441 + // xoris r15,r15,29740 + // rlwnm r4,r3,r25,23,7 + [self assert:instructions[4] operation:Operation::lmw rD:16 rA:29 d:10591]; + [self assert:instructions[5] operation:Operation::oris rA:30 rS:4 uimm:14441]; + [self assert:instructions[6] operation:Operation::xoris rA:15 rS:15 uimm:29740]; + [self assert:instructions[7] operation:Operation::rlwnmx rA:4 rS:3 rB:25 mb:23 me:7 rc:FALSE]; + + // andis. r13,r17,23022 + // rlwinm. r28,r18,16,6,4 + // lfsu f30,-24703(r5) + // lfdu f16,22525(r17) + [self assert:instructions[8] operation:Operation::andis_ rA:13 rS:17 uimm:23022]; + [self assert:instructions[9] operation:Operation::rlwinmx rA:28 rS:18 rB:16 mb:6 me:4 rc:TRUE]; + [self assert:instructions[10] operation:Operation::lfsu frD:30 rA:5 d:-24703]; + [self assert:instructions[11] operation:Operation::lfdu frD:16 rA:17 d:22525]; + + // lfd f3,-4263(r10) + // lwz r9,22658(r18) + // .long 0x1336fad6 + // .long 0xe1ddfa2b + [self assert:instructions[12] operation:Operation::lfd frD:3 rA:10 d:-4263]; + [self assert:instructions[13] operation:Operation::lwz rD:9 rA:18 d:22658]; + [self assertUndefined:instructions[14]]; + [self assertUndefined:instructions[15]]; + + // .long 0x18c60357 + // .long 0x4c122cb5 + // lfdu f5,-2231(r17) + // stfd f30,-5181(r28) + [self assertUndefined:instructions[16]]; + [self assertUndefined:instructions[17]]; + [self assert:instructions[18] operation:Operation::lfdu frD:5 rA:17 d:-2231]; + [self assert:instructions[19] operation:Operation::stfd frD:30 rA:28 d:-5181]; + + // twi 30,r6,391 + // .long 0x117eb911 + // lwz r1,19523(r19) + // .long 0xe65371e8 + [self assert:instructions[20] operation:Operation::twi to:30 rA:6 simm:391]; + [self assertUndefined:instructions[21]]; + [self assert:instructions[22] operation:Operation::lwz rD:1 rA:19 d:19523]; + [self assertUndefined:instructions[23]]; + + // lhz r2,-14003(r7) + // .long 0xe671dd0b + // .long 0xe07992bb + // xori r19,r17,12264 + [self assert:instructions[24] operation:Operation::lhz rD:2 rA:7 d:-14003]; + [self assertUndefined:instructions[25]]; + [self assertUndefined:instructions[26]]; + [self assert:instructions[27] operation:Operation::xori rA:19 rS:17 uimm:12264]; + + // .long 0xfc361c6b + // rlwnm r11,r20,r11,8,20 + // sth r21,18978(r22) + // .long 0x45dd156 +// [self assertUndefined:instructions[28]]; // Disabled due to reserved field; I'm decoding this as faddx. + [self assert:instructions[29] operation:Operation::rlwnmx rA:11 rS:20 rB:11 mb:8 me:20 rc:FALSE]; + [self assert:instructions[30] operation:Operation::sth rS:21 rA:22 d:18978]; + [self assertUndefined:instructions[31]]; } @end From 5401ff6c786eb7968191892b41a86bd3522d5f67 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 11:14:43 -0500 Subject: [PATCH 17/52] Proactively fixes li sign extension. --- Processors/Decoders/PowerPC/PowerPC.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index b73ddd5ef..9a9bf33f6 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -178,7 +178,7 @@ struct Instruction { 0x0000'0000, 0xfc00'0000 }; - const uint32_t value = (opcode & 0x03ff'fffc) | extensions[(opcode >> 26) & 1]; + const uint32_t value = (opcode & 0x03ff'fffc) | extensions[(opcode >> 25) & 1]; return int32_t(value); } From adf1484ecc02e7666ec52bc6e3b7b25b345f1dc3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 16:21:23 -0500 Subject: [PATCH 18/52] Introduces third test sequence, uneventfully. --- .../Clock SignalTests/PowerPCDecoderTests.mm | 105 +++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm index 192caba3a..3ea0c5759 100644 --- a/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm @@ -75,13 +75,22 @@ namespace { XCTAssertEqual(instruction.d(), d); } -- (void)assert:(Instruction &)instruction operation:(Operation)operation li:(int)li lk:(BOOL)lk aa:(BOOL)aa { +- (void)assert:(Instruction &)instruction operation:(Operation)operation li:(uint32_t)li lk:(BOOL)lk aa:(BOOL)aa { XCTAssertEqual(instruction.operation, operation); XCTAssertEqual(instruction.li(), li); XCTAssertEqual(!!instruction.lk(), lk); XCTAssertEqual(!!instruction.aa(), aa); } +- (void)assert:(Instruction &)instruction operation:(Operation)operation bo:(int)bo bi:(int)bi bd:(int)bd lk:(BOOL)lk aa:(BOOL)aa { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.bo(), bo); + XCTAssertEqual(instruction.bi(), bi); + XCTAssertEqual(instruction.bd(), bd); + XCTAssertEqual(!!instruction.lk(), lk); + XCTAssertEqual(!!instruction.aa(), aa); +} + - (void)assert:(Instruction &)instruction operation:(Operation)operation rA:(int)rA rS:(int)rS rB:(int)rB mb:(int)mb me:(int)me rc:(BOOL)rc { XCTAssertEqual(instruction.operation, operation); XCTAssertEqual(instruction.rA(), rA); @@ -99,6 +108,14 @@ namespace { XCTAssertEqual(instruction.simm(), simm); } +- (void)assert:(Instruction &)instruction operation:(Operation)operation crfD:(int)crfD l:(BOOL)l rA:(int)rA uimm:(int)uimm { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.crfD(), crfD); + XCTAssertEqual(!!instruction.l(), l); + XCTAssertEqual(instruction.rA(), rA); + XCTAssertEqual(instruction.uimm(), uimm); +} + // MARK: - Decoder - (void)decode:(const uint32_t *)stream { @@ -284,4 +301,90 @@ namespace { [self assertUndefined:instructions[31]]; } +- (void)testSequence3 { + const uint32_t sequence[] = { + 0xbcaf3520, 0xfa9df12d, 0xc631efca, 0xa3e7f409, + 0x3ddca273, 0x3cfb234d, 0x551dc325, 0x8c1a0f37, + 0x5b3ca99b, 0xce08cc1e, 0x7b1dfd3a, 0xf19aee7c, + 0x52c852e9, 0xc681c0c1, 0xd3b1fda5, 0xe2b401cb, + 0x433cb83d, 0x54412f41, 0x532d624a, 0x0b3117c5, + 0x988144ba, 0xc7a96ad0, 0x28331474, 0x5620c367, + 0xab0a2607, 0xe826acf4, 0x41969154, 0x6471d09f, + 0x6a25f04f, 0x4a15996d, 0x272c96ef, 0xab3171a9, + }; + [self decode:sequence]; + + // stmw r5,13600(r15) + // .long 0xfa9df12d + // lfsu f17,-4150(r17) + // lhz r31,-3063(r7) + [self assert:instructions[0] operation:Operation::stmw rS:5 rA:15 d:13600]; + [self assertUndefined:instructions[1]]; + [self assert:instructions[2] operation:Operation::lfsu frD:17 rA:17 d:-4150]; + [self assert:instructions[3] operation:Operation::lhz rD:31 rA:7 d:-3063]; + + // addis r14,r28,-23949 + // addis r7,r27,9037 + // rlwinm. r29,r8,24,12,18 + // lbzu r0,3895(r26) + [self assert:instructions[4] operation:Operation::addis rD:14 rA:28 simm:-23949]; + [self assert:instructions[5] operation:Operation::addis rD:7 rA:27 simm:9037]; + [self assert:instructions[6] operation:Operation::rlwinmx rA:29 rS:8 rB:24 mb:12 me:18 rc:TRUE]; + [self assert:instructions[7] operation:Operation::lbzu rD:0 rA:26 d:3895]; + + // rlmi. r28,r25,r21,6,13 + // lfdu f16,-13282(r8) + // .long 0x7b1dfd3a + // .long 0xf19aee7c + [self assert:instructions[8] operation:Operation::rlmix rA:28 rS:25 rB:21 mb:6 me:13 rc:TRUE]; + [self assert:instructions[9] operation:Operation::lfdu frD:16 rA:8 d:-13282]; + [self assertUndefined:instructions[10]]; + [self assertUndefined:instructions[11]]; + + // rlwimi. r8,r22,10,11,20 + // lfsu f20,-16191(r1) + // stfs f29,-603(r17) + // .long 0xe2b401cb + [self assert:instructions[12] operation:Operation::rlwimix rA:8 rS:22 rB:10 mb:11 me:20 rc:TRUE]; + [self assert:instructions[13] operation:Operation::lfsu frD:20 rA:1 d:-16191]; + [self assert:instructions[14] operation:Operation::stfs frS:29 rA:17 d:-603]; + [self assertUndefined:instructions[15]]; + + // .long 0x433cb83d + // rlwinm. r1,r2,5,29,0 + // rlwimi r13,r25,12,9,5 + // .long 0xb3117c5 + [self assertUndefined:instructions[16]]; + [self assert:instructions[17] operation:Operation::rlwinmx rA:1 rS:2 rB:5 mb:29 me:0 rc:TRUE]; + [self assert:instructions[18] operation:Operation::rlwimix rA:13 rS:25 rB:12 mb:9 me:5 rc:FALSE]; + [self assertUndefined:instructions[19]]; + + // stb r4,17594(r1) + // lfsu f29,27344(r9) + // cmpli cr0,1,r19,5236 + // rlwinm. r0,r17,24,13,19 + [self assert:instructions[20] operation:Operation::stb rS:4 rA:1 d:17594]; + [self assert:instructions[21] operation:Operation::lfsu frD:29 rA:9 d:27344]; + [self assert:instructions[22] operation:Operation::cmpli crfD:0 l:TRUE rA:19 uimm:5236]; + [self assert:instructions[23] operation:Operation::rlwinmx rA:0 rS:17 rB:24 mb:13 me:19 rc:TRUE]; + + // lha r24,9735(r10) + // .long 0xe826acf4 + // beq+ cr5,0xffffffffffff91bc [at address 0x68] + // oris r17,r3,53407 + [self assert:instructions[24] operation:Operation::lha rD:24 rA:10 d:9735]; + [self assertUndefined:instructions[25]]; + [self assert:instructions[26] operation:Operation::bcx bo:12 bi:22 bd:0xffff91bc - 0x68 lk:FALSE aa:FALSE]; + [self assert:instructions[27] operation:Operation::oris rA:17 rS:3 uimm:53407]; + + // xori r5,r17,61519 + // bl 0xfffffffffe1599e0 [at address 0x74] + // dozi r25,r12,-26897 + // lha r25,29097(r17) + [self assert:instructions[28] operation:Operation::xori rA:5 rS:17 uimm:61519]; + [self assert:instructions[29] operation:Operation::bx li:0xfe1599e0 - 0x74 lk:TRUE aa:FALSE]; + [self assert:instructions[30] operation:Operation::dozi rD:25 rA:12 simm:-26897]; + [self assert:instructions[31] operation:Operation::lha rD:25 rA:17 d:29097]; +} + @end From 367cb1789d22731797e3a8a64f7504b81bb636c3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 16:37:35 -0500 Subject: [PATCH 19/52] Starts building an x86 test. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++ .../Mac/Clock SignalTests/x86DecoderTests.mm | 66 +++++++++++++++++++ Processors/Decoders/x86/x86.cpp | 3 + 3 files changed, 75 insertions(+) create mode 100644 OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 7abbafd64..b51a45e98 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -867,6 +867,8 @@ 4BEE149A227FC0EA00133682 /* IWM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE1498227FC0EA00133682 /* IWM.cpp */; }; 4BEE1EC022B5E236000A26A6 /* MacGCRTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */; }; 4BEE1EC122B5E2FD000A26A6 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCE209BF27B00AB2146 /* Encoder.cpp */; }; + 4BEE4BD425A26E2B00011BD2 /* x86DecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */; }; + 4BEE4BE125A26F8100011BD2 /* x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76AD25A0196100178AEC /* x86.cpp */; }; 4BEEE6BD20DC72EB003723BF /* CompositeOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BEEE6BB20DC72EA003723BF /* CompositeOptions.xib */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; 4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; }; @@ -1807,6 +1809,7 @@ 4BEE1498227FC0EA00133682 /* IWM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = IWM.cpp; sourceTree = ""; }; 4BEE1499227FC0EA00133682 /* IWM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = IWM.hpp; sourceTree = ""; }; 4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MacGCRTests.mm; sourceTree = ""; }; + 4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = x86DecoderTests.mm; sourceTree = ""; }; 4BEEE6BC20DC72EA003723BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/CompositeOptions.xib"; sourceTree = SOURCE_ROOT; }; 4BEF6AA81D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DigitalPhaseLockedLoopBridge.h; sourceTree = ""; }; 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DigitalPhaseLockedLoopBridge.mm; sourceTree = ""; }; @@ -3485,6 +3488,7 @@ 4BE76CF822641ED300ACD6FA /* QLTests.mm */, 4B2AF8681E513FC20027EE29 /* TIATests.mm */, 4B1D08051E0F7A1100763741 /* TimeTests.mm */, + 4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */, 4BB73EB81B587A5100552FC2 /* Info.plist */, 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */, 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */, @@ -4967,6 +4971,7 @@ 4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */, 4B778F0F23A5EC560000D260 /* PCMTrack.cpp in Sources */, 4B778F1123A5EC650000D260 /* FileHolder.cpp in Sources */, + 4BEE4BE125A26F8100011BD2 /* x86.cpp in Sources */, 4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */, 4B778F2023A5EDCE0000D260 /* HFV.cpp in Sources */, 4B778F3323A5F0FB0000D260 /* MassStorageDevice.cpp in Sources */, @@ -5047,6 +5052,7 @@ 4B778F2123A5EDD50000D260 /* TrackSerialiser.cpp in Sources */, 4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */, 4B778F3923A5F11C0000D260 /* Shifter.cpp in Sources */, + 4BEE4BD425A26E2B00011BD2 /* x86DecoderTests.mm in Sources */, 4B778F3623A5F1040000D260 /* Target.cpp in Sources */, 4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */, 4B778F3D23A5F1750000D260 /* ncr5380.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm new file mode 100644 index 000000000..4dab3defb --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -0,0 +1,66 @@ +// +// x86DecoderTests.m +// Clock Signal +// +// Created by Thomas Harte on 03/01/2021. +// Copyright 2021 Thomas Harte. All rights reserved. +// + +#import + +#include +#include +#include "../../../Processors/Decoders/x86/x86.hpp" + +namespace { + using Operation = CPU::Decoder::x86::Operation; + using Instruction = CPU::Decoder::x86::Instruction; +} + +@interface x86DecoderTests : XCTestCase +@end + +/*! + Tests PowerPC decoding by throwing a bunch of randomly-generated + word streams and checking that the result matches what I got from a + disassembler elsewhere. +*/ +@implementation x86DecoderTests { + std::vector instructions; +} + +// MARK: - Specific instruction asserts. + +/* ... TODO ... */ + +// MARK: - Decoder + +- (void)decode:(const std::initializer_list &)stream { + CPU::Decoder::x86::Decoder decoder(CPU::Decoder::x86::Model::i8086); + + // Start with a very dumb implementation: post one byte at a time. + instructions.clear(); + for(auto item: stream) { + const auto next = decoder.decode(&item, 1); + if(next.size() > 0) { + instructions.push_back(next); + } + } +} + +// MARK: - Tests + +- (void)testSequence1 { + [self decode:{ + 0x2d, 0x77, 0xea, 0x72, 0xfc, 0x4b, 0xb5, 0x28, 0xc3, 0xca, 0x26, 0x48, 0x65, 0x6d, 0x7b, 0x9f, + 0xc2, 0x65, 0x42, 0x4e, 0xef, 0x70, 0x20, 0x94, 0xc4, 0xd4, 0x93, 0x43, 0x3c, 0x8e, 0x6a, 0x65, + 0x1a, 0x78, 0x45, 0x10, 0x7f, 0x3c, 0x19, 0x5a, 0x16, 0x31, 0x64, 0x2c, 0xe7, 0xc6, 0x7d, 0xb0, + 0xb5, 0x49, 0x67, 0x61, 0xba, 0xc0, 0xcb, 0x14, 0x7e, 0x71, 0xd0, 0x50, 0x78, 0x3d, 0x03, 0x1d, + 0xe5, 0xc9, 0x97, 0xc3, 0x9b, 0xe6, 0xd3, 0x6c, 0x58, 0x4d, 0x76, 0x80, 0x44, 0xd6, 0x9f, 0xa5, + 0xbd, 0xa1, 0x12, 0xc5, 0x29, 0xc9, 0x9e, 0xd8, 0xf3, 0xcf, 0x92, 0x39, 0x5d, 0x90, 0x15, 0xc3, + 0xb8, 0xad, 0xe8, 0xc8, 0x16, 0x4a, 0xb0, 0x9e, 0xf9, 0xbf, 0x56, 0xea, 0x4e, 0xfd, 0xe4, 0x5a, + 0x23, 0xaa, 0x2c, 0x5b, 0x2a, 0xd2, 0xf7, 0x5f, 0x18, 0x86, 0x90, 0x25, 0x64, 0xb7, 0xc3 + }]; +} + +@end diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 119b7c6d0..8e9833235 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -12,6 +12,9 @@ using namespace CPU::Decoder::x86; +// Only 8086 is suppoted for now. +Decoder::Decoder(Model) {} + Instruction Decoder::decode(uint8_t *source, size_t length) { uint8_t *const limit = source + length; From 11b6c1d4b58048b5ee52eef3d6418e0179f16f0e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 17:03:50 -0500 Subject: [PATCH 20/52] Proceeds to three instructions correctly decoded. 'Wow'. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 72 +++++++++++++++++++ Processors/Decoders/x86/x86.cpp | 52 ++++++++++++-- Processors/Decoders/x86/x86.hpp | 18 +++-- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 4dab3defb..9e7857364 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -61,6 +61,78 @@ namespace { 0xb8, 0xad, 0xe8, 0xc8, 0x16, 0x4a, 0xb0, 0x9e, 0xf9, 0xbf, 0x56, 0xea, 0x4e, 0xfd, 0xe4, 0x5a, 0x23, 0xaa, 0x2c, 0x5b, 0x2a, 0xd2, 0xf7, 0x5f, 0x18, 0x86, 0x90, 0x25, 0x64, 0xb7, 0xc3 }]; + + // 68 instructions are expected. + XCTAssertEqual(instructions.size(), 68); + + // sub $0xea77,%ax + // jb 0x00000001 + // dec %bx + // mov $0x28,%ch + // ret + // lret $0x4826 + // gs insw (%dx),%es:(%di) + // jnp 0xffffffaf + // ret $0x4265 + // dec %si + // out %ax,(%dx) + // jo 0x00000037 + // xchg %ax,%sp + // (bad) + // aam $0x93 + // inc %bx + // cmp $0x8e,%al + // push $0x65 + // sbb 0x45(%bx,%si),%bh + // adc %bh,0x3c(%bx) + // sbb %bx,0x16(%bp,%si) + // xor %sp,0x2c(%si) + // out %ax,$0xc6 + // jge 0xffffffe0 + // mov $0x49,%ch + // addr32 popa + // mov $0xcbc0,%dx + // adc $0x7e,%al + // jno 0x0000000b + // push %ax + // js 0x0000007b + // add (%di),%bx + // in $0xc9,%ax + // xchg %ax,%di + // ret + // fwait + // out %al,$0xd3 + // insb (%dx),%es:(%di) + // pop %ax + // dec %bp + // jbe 0xffffffcc + // inc %sp + // (bad) + // lahf + // movsw %ds:(%si),%es:(%di) + // mov $0x12a1,%bp + // lds (%bx,%di),%bp + // leave + // sahf + // fdiv %st(3),%st + // iret + // xchg %ax,%dx + // cmp %bx,-0x70(%di) + // adc $0xb8c3,%ax + // lods %ds:(%si),%ax + // call 0x0000172d + // dec %dx + // mov $0x9e,%al + // stc + // mov $0xea56,%di + // dec %si + // std + // in $0x5a,%al + // and 0x5b2c(%bp,%si),%bp + // sub %dl,%dl + // negw 0x18(%bx) + // xchg %dl,0x6425(%bx,%si) + // mov $0xc3,%bh } @end diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 8e9833235..55f25ed96 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -8,6 +8,7 @@ #include "x86.hpp" +#include #include using namespace CPU::Decoder::x86; @@ -16,7 +17,8 @@ using namespace CPU::Decoder::x86; Decoder::Decoder(Model) {} Instruction Decoder::decode(uint8_t *source, size_t length) { - uint8_t *const limit = source + length; + uint8_t *const begin = source; + uint8_t *const end = source + length; #define MapPartial(value, op, lrg, fmt, phs) \ case value: \ @@ -31,10 +33,11 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { operation_ = Operation::op; \ source_ = Source::src; \ destination_ = Source::dest; \ + format_ = Format::Implied; \ phase_ = Phase::ReadyToPost; \ break - while(phase_ == Phase::Instruction && source != limit) { + while(phase_ == Phase::Instruction && source != end) { // Retain the instruction byte, in case additional decoding is deferred // to the ModRM byte. instr_ = *source; @@ -169,7 +172,7 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { #undef MapInstr - if(phase_ == Phase::ModRM && source != limit) { + if(phase_ == Phase::ModRM && source != end) { const uint8_t mod = *source >> 6; // i.e. mode. const uint8_t reg = (*source >> 3) & 7; // i.e. register. const uint8_t rm = *source & 7; // i.e. register/memory. @@ -234,12 +237,51 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { ++consumed_; } - if(phase_ == Phase::AwaitingOperands && source != limit) { + if(phase_ == Phase::AwaitingOperands && source != end) { // TODO: calculate number of expected operands. + const int required_bytes = large_operand_ ? 2 : 1; + + const int outstanding_bytes = required_bytes - operand_bytes_; + const int bytes_to_consume = std::min(int(end - source), outstanding_bytes); + source += bytes_to_consume; + consumed_ += bytes_to_consume; + operand_bytes_ += bytes_to_consume; + if(bytes_to_consume == outstanding_bytes) { + phase_ = Phase::ReadyToPost; + } else { + // Provide a genuine measure of further bytes required. + return Instruction(-(outstanding_bytes - bytes_to_consume)); + } } if(phase_ == Phase::ReadyToPost) { - // TODO: construct actual Instruction. + Instruction result; + switch(format_) { + case Format::Ac_Data: + if(large_operand_) { + result = Instruction(operation_, Size::Word, Source::AX, Source::Immediate, consumed_); + } else { + result = Instruction(operation_, Size::Byte, Source::AL, Source::Immediate, consumed_); + } + break; + + case Format::Disp: + result = Instruction(operation_, Size::Byte, Source::Immediate, Source::None, consumed_); + break; + + case Format::Implied: + result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, source_, destination_, consumed_); + break; + + default: break; + } + + // Reset parser. + consumed_ = operand_bytes_ = 0; + lock_ = add_offset_ = large_offset_ = false; + phase_ = Phase::Instruction; + + return result; } return Instruction(); diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 4ddbe0d1a..3c796d5b8 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -76,18 +76,23 @@ enum class Source: uint8_t { class Instruction { public: - const Operation operation = Operation::Invalid; - const Size operand_size = Size::Byte; + Operation operation = Operation::Invalid; + Size operand_size = Size::Byte; - const Source source = Source::AL; - const Source destination = Source::AL; + Source source = Source::AL; + Source destination = Source::AL; int size() const { return size_; } + Instruction() {} + Instruction(int size) : size_(size) {} + Instruction(Operation operation, Size operand_size, Source source, Source destination, int size) : + operation(operation), operand_size(operand_size), source(source), destination(destination), size_(size) {} + private: - int size_ = 0; + int size_ = -1; }; /*! @@ -117,6 +122,7 @@ struct Decoder { } phase_ = Phase::Instruction; enum class Format: uint8_t { + Implied, MemReg_Reg, Reg_MemReg, Ac_Data, @@ -134,8 +140,8 @@ struct Decoder { None, RepE, RepNE } repetition_ = Repetition::None; - int consumed_ = 0; + int operand_bytes_ = 0; Operation operation_ = Operation::Invalid; bool large_operand_ = false; Source source_ = Source::None; From adcb2e03e8c980b81f6f562b2e8ec43f9f194886 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 17:28:29 -0500 Subject: [PATCH 21/52] Attempts to consolidate source/destination ordering. --- Processors/Decoders/x86/x86.cpp | 28 +++++++++++++++++----------- Processors/Decoders/x86/x86.hpp | 14 ++++++++++---- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 55f25ed96..31528bb53 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -17,7 +17,6 @@ using namespace CPU::Decoder::x86; Decoder::Decoder(Model) {} Instruction Decoder::decode(uint8_t *source, size_t length) { - uint8_t *const begin = source; uint8_t *const end = source + length; #define MapPartial(value, op, lrg, fmt, phs) \ @@ -42,6 +41,10 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { // to the ModRM byte. instr_ = *source; switch(instr_) { + default: + reset_parsing(); + return Instruction(); + #define PartialBlock(start, operation) \ MapPartial(start + 0x00, operation, false, MemReg_Reg, ModRM); \ MapPartial(start + 0x01, operation, true, MemReg_Reg, ModRM); \ @@ -220,13 +223,9 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { case 3: memreg = reg_table[large_operand_][rm]; break; } - if(format_ == Format::Reg_MemReg) { - destination_ = reg_table[large_operand_][reg]; - source_ = memreg; - } else { - source_ = reg_table[large_operand_][reg]; - destination_ = memreg; - } + // These will be switched over at ReadyToPost if the format_ requires it. + source_ = reg_table[large_operand_][reg]; + destination_ = memreg; phase_ = (add_offset_ || memreg == Source::DirectAddress) ? Phase::AwaitingOperands : Phase::ReadyToPost; } break; @@ -273,12 +272,19 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, source_, destination_, consumed_); break; - default: break; + case Format::MemReg_Reg: + result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, source_, destination_, consumed_); + break; + + case Format::Reg_MemReg: + result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, destination_, source_, consumed_); + break; + + default: assert(false); } // Reset parser. - consumed_ = operand_bytes_ = 0; - lock_ = add_offset_ = large_offset_ = false; + reset_parsing(); phase_ = Phase::Instruction; return result; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 3c796d5b8..f7a70ec9f 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -140,17 +140,23 @@ struct Decoder { None, RepE, RepNE } repetition_ = Repetition::None; - int consumed_ = 0; - int operand_bytes_ = 0; Operation operation_ = Operation::Invalid; bool large_operand_ = false; Source source_ = Source::None; Source destination_ = Source::None; - Source segment_override_ = Source::None; uint8_t instr_ = 0x00; - bool lock_ = false; bool add_offset_ = false; bool large_offset_ = false; + + int consumed_ = 0; + int operand_bytes_ = 0; + bool lock_ = false; + Source segment_override_ = Source::None; + void reset_parsing() { + consumed_ = operand_bytes_ = 0; + lock_ = false; + segment_override_ = Source::None; + } }; } From 0ae217f51ddd2a5c32ff1c3a799cef976cbaa127 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 19:33:16 -0500 Subject: [PATCH 22/52] Improves exposition, adds decoding of the 0xbx patch of MOVs. --- Processors/Decoders/x86/x86.cpp | 30 ++++++++++++++++------- Processors/Decoders/x86/x86.hpp | 42 ++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 31528bb53..403841d3a 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -27,6 +27,15 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { phase_ = Phase::phs; \ break +#define MapRegData(value, op, lrg, dest) \ + case value: \ + operation_ = Operation::op; \ + large_operand_ = lrg; \ + source_ = Source::Immediate; \ + destination_ = Source::dest; \ + phase_ = Phase::AwaitingOperands; \ + break + #define MapComplete(value, op, src, dest) \ case value: \ operation_ = Operation::op; \ @@ -50,8 +59,8 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { MapPartial(start + 0x01, operation, true, MemReg_Reg, ModRM); \ MapPartial(start + 0x02, operation, false, Reg_MemReg, ModRM); \ MapPartial(start + 0x03, operation, true, Reg_MemReg, ModRM); \ - MapPartial(start + 0x04, operation, false, Ac_Data, AwaitingOperands); \ - MapPartial(start + 0x05, operation, true, Ac_Data, AwaitingOperands); + MapRegData(start + 0x04, operation, false, AL); \ + MapRegData(start + 0x05, operation, true, AX); PartialBlock(0x00, ADD); MapComplete(0x06, PUSH, ES, None); @@ -164,6 +173,15 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { MapPartial(0xa0, MOV, false, Reg_Addr, AwaitingOperands); + MapRegData(0xb0, MOV, false, AL); MapRegData(0xb1, MOV, false, CL); + MapRegData(0xb2, MOV, false, DL); MapRegData(0xb3, MOV, false, BL); + MapRegData(0xb4, MOV, false, AH); MapRegData(0xb5, MOV, false, CH); + MapRegData(0xb6, MOV, false, DH); MapRegData(0xb7, MOV, false, BH); + MapRegData(0xb8, MOV, true, AX); MapRegData(0xb9, MOV, true, CX); + MapRegData(0xba, MOV, true, DX); MapRegData(0xbb, MOV, true, BX); + MapRegData(0xbc, MOV, true, SP); MapRegData(0xbd, MOV, true, BP); + MapRegData(0xbe, MOV, true, SI); MapRegData(0xbf, MOV, true, DI); + // Other prefix bytes. case 0xf0: lock_ = true; break; case 0xf2: repetition_ = Repetition::RepNE; break; @@ -256,12 +274,8 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { if(phase_ == Phase::ReadyToPost) { Instruction result; switch(format_) { - case Format::Ac_Data: - if(large_operand_) { - result = Instruction(operation_, Size::Word, Source::AX, Source::Immediate, consumed_); - } else { - result = Instruction(operation_, Size::Byte, Source::AL, Source::Immediate, consumed_); - } + case Format::Reg_Data: + result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, source_, destination_, consumed_); break; case Format::Disp: diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index f7a70ec9f..3661aa834 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -115,31 +115,46 @@ struct Decoder { private: enum class Phase { + /// Captures all prefixes and continues until an instruction byte is encountered. Instruction, + /// Receives a ModRM byte and either populates the source_ and dest_ fields appropriately + /// or completes decoding of the instruction, as per the instruction format. ModRM, + /// Waits for sufficiently many bytes to pass for all associated operands to be captured. AwaitingOperands, + /// Forms and returns an Instruction, and resets parsing state. ReadyToPost } phase_ = Phase::Instruction; + /// During the ModRM phase, format dictates interpretation of the ModRM byte. + /// + /// During the ReadyToPost phase, format determines how transiently-recorded fields + /// are packaged into an Instruction. enum class Format: uint8_t { Implied, + + // In both cases: pass the ModRM for mode, register and register/memory + // flags and populate the source_ and destination_ fields appropriate. + // During the ModRM phase they'll be populated as source_ = register, + // destination_ = register/memory; the ReadyToPost phase should switch + // those around as necessary. MemReg_Reg, Reg_MemReg, - Ac_Data, - MemReg_Data, - SegReg_MemReg, + + Reg_Data, + + // Reg_Addr, Addr_Reg, + + SegReg_MemReg, Disp, Addr } format_ = Format::MemReg_Reg; // TODO: figure out which Formats can be folded together, // and which are improperly elided. - enum class Repetition: uint8_t { - None, RepE, RepNE - } repetition_ = Repetition::None; - + // Ephemeral decoding state. Operation operation_ = Operation::Invalid; bool large_operand_ = false; Source source_ = Source::None; @@ -148,14 +163,23 @@ struct Decoder { bool add_offset_ = false; bool large_offset_ = false; - int consumed_ = 0; - int operand_bytes_ = 0; + // Prefix capture fields. + enum class Repetition: uint8_t { + None, RepE, RepNE + } repetition_ = Repetition::None; bool lock_ = false; Source segment_override_ = Source::None; + + // Size capture. + int consumed_ = 0; + int operand_bytes_ = 0; + + /// Resets size capture and all fields with default values. void reset_parsing() { consumed_ = operand_bytes_ = 0; lock_ = false; segment_override_ = Source::None; + repetition_ = Repetition::None; } }; From 782dc3d046007c25b32aae0fc1c4e23cd26420ed Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 19:37:37 -0500 Subject: [PATCH 23/52] Distinguishes inter- and intra-segment RET. --- Processors/Decoders/x86/x86.cpp | 3 +++ Processors/Decoders/x86/x86.hpp | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 403841d3a..9eb37f99b 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -182,6 +182,9 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { MapRegData(0xbc, MOV, true, SP); MapRegData(0xbd, MOV, true, BP); MapRegData(0xbe, MOV, true, SI); MapRegData(0xbf, MOV, true, DI); + MapComplete(0xc3, RETIntra, None, None); + MapComplete(0xcb, RETInter, None, None); + // Other prefix bytes. case 0xf0: lock_ = true; break; case 0xf2: repetition_ = Repetition::RepNE; break; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 3661aa834..f123eb025 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -37,9 +37,12 @@ enum class Operation: uint8_t { JMP, JCXZ, LAHF, LDS, LEA, LODS, LOOPE, LOOPNE, MOV, MOVS, MUL, NEG, NOP, NOT, OR, OUT, - POP, POPF, PUSH, PUSHF, RCL, RCR, REP, RET, ROL, ROR, SAHF, + POP, POPF, PUSH, PUSHF, RCL, RCR, REP, ROL, ROR, SAHF, SAR, SBB, SCAS, SHL, SHR, STC, STD, STI, STOS, SUB, TEST, - WAIT, XCHG, XLAT, XOR + WAIT, XCHG, XLAT, XOR, + + RETInter, + RETIntra, }; enum class Size: uint8_t { From 29cf96c7038fbe9337877c611e190bd976d350d3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 19:39:28 -0500 Subject: [PATCH 24/52] Adds decoding of disp16 RETs. --- Processors/Decoders/x86/x86.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 9eb37f99b..d4e08bfea 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -182,7 +182,9 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { MapRegData(0xbc, MOV, true, SP); MapRegData(0xbd, MOV, true, BP); MapRegData(0xbe, MOV, true, SI); MapRegData(0xbf, MOV, true, DI); + MapPartial(0xc2, RETIntra, true, Disp, AwaitingOperands); MapComplete(0xc3, RETIntra, None, None); + MapPartial(0xca, RETInter, true, Disp, AwaitingOperands); MapComplete(0xcb, RETInter, None, None); // Other prefix bytes. From a8738b533aa4d939d6d37f79f82a166d2c925c4e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 20:07:46 -0500 Subject: [PATCH 25/52] Switch for now to block-level decoding. It's easier to step debug. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 9e7857364..0bd6bf67b 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -38,22 +38,33 @@ namespace { - (void)decode:(const std::initializer_list &)stream { CPU::Decoder::x86::Decoder decoder(CPU::Decoder::x86::Model::i8086); - // Start with a very dumb implementation: post one byte at a time. + // TODO: test that byte-at-a-time decoding gives the same results, as a freebie. +// instructions.clear(); +// for(auto item: stream) { +// const auto next = decoder.decode(&item, 1); +// if(next.size() > 0) { +// instructions.push_back(next); +// } +// } + instructions.clear(); - for(auto item: stream) { - const auto next = decoder.decode(&item, 1); - if(next.size() > 0) { - instructions.push_back(next); - } + const uint8_t *byte = stream.begin(); + while(byte != stream.end()) { + const auto next = decoder.decode(byte, stream.end() - byte); + if(next.size() <= 0) break; + instructions.push_back(next); + byte += next.size(); } } // MARK: - Tests - (void)testSequence1 { + // 0x6a 0x65 decodes as PUSH $65 in Online Disassembler; The 8086 Book doesn't seem to think that's + // valid. So I've manually omitted it below. [self decode:{ 0x2d, 0x77, 0xea, 0x72, 0xfc, 0x4b, 0xb5, 0x28, 0xc3, 0xca, 0x26, 0x48, 0x65, 0x6d, 0x7b, 0x9f, - 0xc2, 0x65, 0x42, 0x4e, 0xef, 0x70, 0x20, 0x94, 0xc4, 0xd4, 0x93, 0x43, 0x3c, 0x8e, 0x6a, 0x65, + 0xc2, 0x65, 0x42, 0x4e, 0xef, 0x70, 0x20, 0x94, 0xc4, 0xd4, 0x93, 0x43, 0x3c, 0x8e, /* 0x6a, 0x65, */ 0x1a, 0x78, 0x45, 0x10, 0x7f, 0x3c, 0x19, 0x5a, 0x16, 0x31, 0x64, 0x2c, 0xe7, 0xc6, 0x7d, 0xb0, 0xb5, 0x49, 0x67, 0x61, 0xba, 0xc0, 0xcb, 0x14, 0x7e, 0x71, 0xd0, 0x50, 0x78, 0x3d, 0x03, 0x1d, 0xe5, 0xc9, 0x97, 0xc3, 0x9b, 0xe6, 0xd3, 0x6c, 0x58, 0x4d, 0x76, 0x80, 0x44, 0xd6, 0x9f, 0xa5, @@ -63,7 +74,7 @@ namespace { }]; // 68 instructions are expected. - XCTAssertEqual(instructions.size(), 68); + XCTAssertEqual(instructions.size(), 67); // sub $0xea77,%ax // jb 0x00000001 From 38bca5f0f01b8b48717e51670fd61ed3999b49c4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Jan 2021 20:08:13 -0500 Subject: [PATCH 26/52] Finally runs into the wall of trying to merge operands and offsets. --- Processors/Decoders/x86/x86.cpp | 33 ++++++++++++++++++++++----------- Processors/Decoders/x86/x86.hpp | 6 +++--- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index d4e08bfea..2c5069404 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -16,8 +16,8 @@ using namespace CPU::Decoder::x86; // Only 8086 is suppoted for now. Decoder::Decoder(Model) {} -Instruction Decoder::decode(uint8_t *source, size_t length) { - uint8_t *const end = source + length; +Instruction Decoder::decode(const uint8_t *source, size_t length) { + const uint8_t *const end = source + length; #define MapPartial(value, op, lrg, fmt, phs) \ case value: \ @@ -49,10 +49,15 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { // Retain the instruction byte, in case additional decoding is deferred // to the ModRM byte. instr_ = *source; + ++source; + ++consumed_; + switch(instr_) { - default: + default: { + const Instruction instruction(consumed_); reset_parsing(); - return Instruction(); + return instruction; + } #define PartialBlock(start, operation) \ MapPartial(start + 0x00, operation, false, MemReg_Reg, ModRM); \ @@ -115,7 +120,7 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { /* 0x60–0x6f: not used. */ -#define MapJump(value, operation) MapPartial(value, operation, false, Disp, AwaitingOperands); +#define MapJump(value, operation) MapPartial(value, operation, false, Immediate, AwaitingOperands); MapJump(0x70, JO); MapJump(0x71, JNO); MapJump(0x72, JB); @@ -164,7 +169,7 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { MapComplete(0x98, CBW, None, None); MapComplete(0x99, CWD, None, None); - MapPartial(0x9a, CALL, true, Addr, AwaitingOperands); + MapPartial(0x9a, CALL, true, Immediate, AwaitingOperands); MapComplete(0x9b, WAIT, None, None); MapComplete(0x9c, PUSHF, None, None); MapComplete(0x9d, POPF, None, None); @@ -182,18 +187,24 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { MapRegData(0xbc, MOV, true, SP); MapRegData(0xbd, MOV, true, BP); MapRegData(0xbe, MOV, true, SI); MapRegData(0xbf, MOV, true, DI); - MapPartial(0xc2, RETIntra, true, Disp, AwaitingOperands); + MapPartial(0xc2, RETIntra, true, Immediate, AwaitingOperands); MapComplete(0xc3, RETIntra, None, None); - MapPartial(0xca, RETInter, true, Disp, AwaitingOperands); + MapPartial(0xca, RETInter, true, Immediate, AwaitingOperands); MapComplete(0xcb, RETInter, None, None); + MapPartial(0xd4, AAM, false, Immediate, AwaitingOperands); + MapPartial(0xd5, AAD, false, Immediate, AwaitingOperands); + +// MapPartial(0xe4, IN, false, Immediate, AwaitingOperands); + + MapComplete(0xec, IN, DX, AL); MapComplete(0xed, IN, DX, AX); + MapComplete(0xee, OUT, AL, DX); MapComplete(0xef, OUT, AX, DX); + // Other prefix bytes. case 0xf0: lock_ = true; break; case 0xf2: repetition_ = Repetition::RepNE; break; case 0xf3: repetition_ = Repetition::RepE; break; } - ++source; - ++consumed_; } #undef MapInstr @@ -283,7 +294,7 @@ Instruction Decoder::decode(uint8_t *source, size_t length) { result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, source_, destination_, consumed_); break; - case Format::Disp: + case Format::Immediate: result = Instruction(operation_, Size::Byte, Source::Immediate, Source::None, consumed_); break; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index f123eb025..0576273a0 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -114,7 +114,7 @@ struct Decoder { instruction in response, and the decoder may still not be able to complete decoding even if given that number of bytes. */ - Instruction decode(uint8_t *source, size_t length); + Instruction decode(const uint8_t *source, size_t length); private: enum class Phase { @@ -151,8 +151,8 @@ struct Decoder { Addr_Reg, SegReg_MemReg, - Disp, - Addr + + Immediate, } format_ = Format::MemReg_Reg; // TODO: figure out which Formats can be folded together, // and which are improperly elided. From fda2293d6b81f9f8fac586a11652d343c5b3d1ce Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 4 Jan 2021 22:36:39 -0500 Subject: [PATCH 27/52] Improves `const`ness. --- Processors/Decoders/PowerPC/PowerPC.hpp | 80 ++++++++++++------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 9a9bf33f6..480c89829 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -80,7 +80,7 @@ struct Instruction { uint32_t opcode = 0; // PowerPC uses a fixed-size instruction word. - int size() { + int size() const { return 4; } @@ -99,81 +99,81 @@ struct Instruction { // full decoding has already occurred. /// Immediate field used to specify an unsigned 16-bit integer. - uint16_t uimm() { return uint16_t(opcode & 0xffff); } + uint16_t uimm() const { return uint16_t(opcode & 0xffff); } /// Immediate field used to specify a signed 16-bit integer. - int16_t simm() { return int16_t(opcode & 0xffff); } + int16_t simm() const { return int16_t(opcode & 0xffff); } /// Immediate field used to specify a signed 16-bit integer. - int16_t d() { return int16_t(opcode & 0xffff); } + int16_t d() const { return int16_t(opcode & 0xffff); } /// Immediate field used to specify a signed 14-bit integer [64-bit only]. - int16_t ds() { return int16_t(opcode & 0xfffc); } + int16_t ds() const { return int16_t(opcode & 0xfffc); } /// Immediate field used as data to be placed into a field in the floating point status and condition register. - int32_t imm() { return (opcode >> 12) & 0xf; } + int32_t imm() const { return (opcode >> 12) & 0xf; } /// Specifies the conditions on which to trap. - int32_t to() { return (opcode >> 21) & 0x1f; } + int32_t to() const { return (opcode >> 21) & 0x1f; } /// Register source A or destination. - uint32_t rA() { return (opcode >> 16) & 0x1f; } + uint32_t rA() const { return (opcode >> 16) & 0x1f; } /// Register source B. - uint32_t rB() { return (opcode >> 11) & 0x1f; } + uint32_t rB() const { return (opcode >> 11) & 0x1f; } /// Register destination. - uint32_t rD() { return (opcode >> 21) & 0x1f; } + uint32_t rD() const { return (opcode >> 21) & 0x1f; } /// Register source. - uint32_t rS() { return (opcode >> 21) & 0x1f; } + uint32_t rS() const { return (opcode >> 21) & 0x1f; } /// Floating point register source A. - uint32_t frA() { return (opcode >> 16) & 0x1f; } + uint32_t frA() const { return (opcode >> 16) & 0x1f; } /// Floating point register source B. - uint32_t frB() { return (opcode >> 11) & 0x1f; } + uint32_t frB() const { return (opcode >> 11) & 0x1f; } /// Floating point register source C. - uint32_t frC() { return (opcode >> 6) & 0x1f; } + uint32_t frC() const { return (opcode >> 6) & 0x1f; } /// Floating point register source. - uint32_t frS() { return (opcode >> 21) & 0x1f; } + uint32_t frS() const { return (opcode >> 21) & 0x1f; } /// Floating point register destination. - uint32_t frD() { return (opcode >> 21) & 0x1f; } + uint32_t frD() const { return (opcode >> 21) & 0x1f; } /// Branch conditional options. - uint32_t bo() { return (opcode >> 21) & 0x1f; } + uint32_t bo() const { return (opcode >> 21) & 0x1f; } /// Source condition register bit for branch conditionals. - uint32_t bi() { return (opcode >> 16) & 0x1f; } + uint32_t bi() const { return (opcode >> 16) & 0x1f; } /// Branch displacement; provided as already sign extended. - int16_t bd() { return int16_t(opcode & 0xfffc); } + int16_t bd() const { return int16_t(opcode & 0xfffc); } /// Specifies the first 1 bit of a 32/64-bit mask for rotate operations. - uint32_t mb() { return (opcode >> 6) & 0x1f; } + uint32_t mb() const { return (opcode >> 6) & 0x1f; } /// Specifies the first 1 bit of a 32/64-bit mask for rotate operations. - uint32_t me() { return (opcode >> 1) & 0x1f; } + uint32_t me() const { return (opcode >> 1) & 0x1f; } /// Condition register source bit A. - uint32_t crbA() { return (opcode >> 16) & 0x1f; } + uint32_t crbA() const { return (opcode >> 16) & 0x1f; } /// Condition register source bit B. - uint32_t crbB() { return (opcode >> 11) & 0x1f; } + uint32_t crbB() const { return (opcode >> 11) & 0x1f; } /// Condition register (or floating point status & condition register) destination bit. - uint32_t crbD() { return (opcode >> 21) & 0x1f; } + uint32_t crbD() const { return (opcode >> 21) & 0x1f; } /// Condition register (or floating point status & condition register) destination field. - uint32_t crfD() { return (opcode >> 23) & 0x07; } + uint32_t crfD() const { return (opcode >> 23) & 0x07; } /// Condition register (or floating point status & condition register) source field. - uint32_t crfS() { return (opcode >> 18) & 0x07; } + uint32_t crfS() const { return (opcode >> 18) & 0x07; } /// Mask identifying fields to be updated by mtcrf. - uint32_t crm() { return (opcode >> 12) & 0xff; } + uint32_t crm() const { return (opcode >> 12) & 0xff; } /// Mask identifying fields to be updated by mtfsf. - uint32_t fm() { return (opcode >> 17) & 0xff; } + uint32_t fm() const { return (opcode >> 17) & 0xff; } /// Specifies the number of bytes to move in an immediate string load or store. - uint32_t nb() { return (opcode >> 11) & 0x1f; } + uint32_t nb() const { return (opcode >> 11) & 0x1f; } /// Specifies a shift amount. /// TODO: possibly bit 30 is also used in 64-bit mode, find out. - uint32_t sh() { return (opcode >> 11) & 0x1f; } + uint32_t sh() const { return (opcode >> 11) & 0x1f; } /// Specifies one of the 16 segment registers [32-bit only]. - uint32_t sr() { return (opcode >> 16) & 0xf; } + uint32_t sr() const { return (opcode >> 16) & 0xf; } /// A 24-bit signed number; provided as already sign extended. - int32_t li() { + int32_t li() const { constexpr uint32_t extensions[2] = { 0x0000'0000, 0xfc00'0000 @@ -183,15 +183,15 @@ struct Instruction { } /// Absolute address bit; @c 0 or @c non-0. - uint32_t aa() { return opcode & 0x02; } + uint32_t aa() const { return opcode & 0x02; } /// Link bit; @c 0 or @c non-0. - uint32_t lk() { return opcode & 0x01; } + uint32_t lk() const { return opcode & 0x01; } /// Record bit; @c 0 or @c non-0. - uint32_t rc() { return opcode & 0x01; } + uint32_t rc() const { return opcode & 0x01; } /// Whether to compare 32-bit or 64-bit numbers [for 64-bit implementations only]; @c 0 or @c non-0. - uint32_t l() { return opcode & 0x200000; } + uint32_t l() const { return opcode & 0x200000; } /// Enables setting of OV and SO in the XER; @c 0 or @c non-0. - uint32_t oe() { return opcode & 0x800; } + uint32_t oe() const { return opcode & 0x800; } }; /*! @@ -208,15 +208,15 @@ struct Decoder { private: Model model_; - bool is64bit() { + bool is64bit() const { return model_ == Model::MPC620; } - bool is32bit() { + bool is32bit() const { return !is64bit(); } - bool is601() { + bool is601() const { return model_ == Model::MPC601; } }; From 3b55d3f158d9ab3181144aa74ca139eaa6d81df6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 5 Jan 2021 21:25:12 -0500 Subject: [PATCH 28/52] Nudges up to a need to decode operation from the ModRegRM byte. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 20 +- Processors/Decoders/x86/x86.cpp | 351 +++++++++++------- Processors/Decoders/x86/x86.hpp | 68 ++-- 3 files changed, 253 insertions(+), 186 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 0bd6bf67b..c1508b3e2 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -60,21 +60,27 @@ namespace { // MARK: - Tests - (void)testSequence1 { - // 0x6a 0x65 decodes as PUSH $65 in Online Disassembler; The 8086 Book doesn't seem to think that's - // valid. So I've manually omitted it below. + // Sequences the Online Disassembler believes to exist but The 8086 Book does not: + // + // 0x6a 0x65 push $65 + // 0x65 0x6d gs insw (%dx),%es:(%di) + // 0x67 0x61 addr32 popa + // 0x6c insb (%dx), %es:(%di) + // 0xc9 leave + // [self decode:{ - 0x2d, 0x77, 0xea, 0x72, 0xfc, 0x4b, 0xb5, 0x28, 0xc3, 0xca, 0x26, 0x48, 0x65, 0x6d, 0x7b, 0x9f, + 0x2d, 0x77, 0xea, 0x72, 0xfc, 0x4b, 0xb5, 0x28, 0xc3, 0xca, 0x26, 0x48, /* 0x65, 0x6d, */ 0x7b, 0x9f, 0xc2, 0x65, 0x42, 0x4e, 0xef, 0x70, 0x20, 0x94, 0xc4, 0xd4, 0x93, 0x43, 0x3c, 0x8e, /* 0x6a, 0x65, */ 0x1a, 0x78, 0x45, 0x10, 0x7f, 0x3c, 0x19, 0x5a, 0x16, 0x31, 0x64, 0x2c, 0xe7, 0xc6, 0x7d, 0xb0, - 0xb5, 0x49, 0x67, 0x61, 0xba, 0xc0, 0xcb, 0x14, 0x7e, 0x71, 0xd0, 0x50, 0x78, 0x3d, 0x03, 0x1d, - 0xe5, 0xc9, 0x97, 0xc3, 0x9b, 0xe6, 0xd3, 0x6c, 0x58, 0x4d, 0x76, 0x80, 0x44, 0xd6, 0x9f, 0xa5, - 0xbd, 0xa1, 0x12, 0xc5, 0x29, 0xc9, 0x9e, 0xd8, 0xf3, 0xcf, 0x92, 0x39, 0x5d, 0x90, 0x15, 0xc3, + 0xb5, 0x49, /* 0x67, 0x61, */ 0xba, 0xc0, 0xcb, 0x14, 0x7e, 0x71, 0xd0, 0x50, 0x78, 0x3d, 0x03, 0x1d, + 0xe5, 0xc9, 0x97, 0xc3, 0x9b, 0xe6, 0xd3, /* 0x6c, */ 0x58, 0x4d, 0x76, 0x80, 0x44, 0xd6, 0x9f, 0xa5, + 0xbd, 0xa1, 0x12, 0xc5, 0x29, /* 0xc9, */ 0x9e, 0xd8, 0xf3, 0xcf, 0x92, 0x39, 0x5d, 0x90, 0x15, 0xc3, 0xb8, 0xad, 0xe8, 0xc8, 0x16, 0x4a, 0xb0, 0x9e, 0xf9, 0xbf, 0x56, 0xea, 0x4e, 0xfd, 0xe4, 0x5a, 0x23, 0xaa, 0x2c, 0x5b, 0x2a, 0xd2, 0xf7, 0x5f, 0x18, 0x86, 0x90, 0x25, 0x64, 0xb7, 0xc3 }]; // 68 instructions are expected. - XCTAssertEqual(instructions.size(), 67); + XCTAssertEqual(instructions.size(), 63); // sub $0xea77,%ax // jb 0x00000001 diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 2c5069404..a74faa8f2 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -19,35 +19,79 @@ Decoder::Decoder(Model) {} Instruction Decoder::decode(const uint8_t *source, size_t length) { const uint8_t *const end = source + length; -#define MapPartial(value, op, lrg, fmt, phs) \ - case value: \ - operation_ = Operation::op; \ - large_operand_ = lrg; \ - format_ = Format::fmt; \ - phase_ = Phase::phs; \ + // MARK: - Prefixes (if present) and the opcode. + +/// Covers anything which is complete as soon as the opcode is encountered. +#define MapComplete(value, op, src, dest, size) \ + case value: \ + operation_ = Operation::op; \ + source_ = Source::src; \ + destination_ = Source::dest; \ + phase_ = Phase::ReadyToPost; \ + operation_size_ = size; \ break -#define MapRegData(value, op, lrg, dest) \ - case value: \ - operation_ = Operation::op; \ - large_operand_ = lrg; \ - source_ = Source::Immediate; \ - destination_ = Source::dest; \ - phase_ = Phase::AwaitingOperands; \ +/// Handles instructions of the form rr, kk and rr, jjkk, i.e. a destination register plus an operand. +#define MapRegData(value, op, dest, size) \ + case value: \ + operation_ = Operation::op; \ + source_ = Source::Immediate; \ + destination_ = Source::dest; \ + phase_ = Phase::AwaitingDisplacementOrOperand; \ + operand_size_ = size; \ break -#define MapComplete(value, op, src, dest) \ - case value: \ - operation_ = Operation::op; \ - source_ = Source::src; \ - destination_ = Source::dest; \ - format_ = Format::Implied; \ - phase_ = Phase::ReadyToPost; \ +/// Handles instructions of the form Ax, jjkk where the latter is implicitly an address. +#define MapRegAddr(value, op, dest, op_size, addr_size) \ + case value: \ + operation_ = Operation::op; \ + destination_ = Source::dest; \ + source_ = Source::DirectAddress; \ + phase_ = Phase::AwaitingDisplacementOrOperand; \ + operand_size_ = addr_size; \ + operation_size_ = op_size; \ + break + +/// Handles instructions of the form jjkk, Ax where the former is implicitly an address. +#define MapAddrReg(value, op, source, op_size, addr_size) \ + case value: \ + operation_ = Operation::op; \ + source_ = Source::source; \ + destination_ = Source::DirectAddress; \ + phase_ = Phase::AwaitingDisplacementOrOperand; \ + operand_size_ = addr_size; \ + operation_size_ = op_size; \ + break + +/// Covers both `mem/reg, reg` and `reg, mem/reg`. +#define MapMemRegReg(value, op, format, size) \ + case value: \ + operation_ = Operation::op; \ + phase_ = Phase::ModRegRM; \ + modregrm_format_ = ModRegRMFormat::format; \ + operand_size_ = 0; \ + operation_size_ = size; \ + break + +/// Handles JO, JNO, JB, etc — jumps with a single byte displacement. +#define MapJump(value, op) \ + case value: \ + operation_ = Operation::op; \ + phase_ = Phase::AwaitingDisplacementOrOperand; \ + operand_size_ = 1; \ + break + +/// Handles far CALL and far JMP — fixed four byte operand operations. +#define MapFar(value, op) \ + case value: \ + operation_ = Operation::op; \ + phase_ = Phase::AwaitingDisplacementOrOperand; \ + operand_size_ = 4; \ break while(phase_ == Phase::Instruction && source != end) { // Retain the instruction byte, in case additional decoding is deferred - // to the ModRM byte. + // to the ModRegRM byte. instr_ = *source; ++source; ++consumed_; @@ -59,57 +103,57 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { return instruction; } -#define PartialBlock(start, operation) \ - MapPartial(start + 0x00, operation, false, MemReg_Reg, ModRM); \ - MapPartial(start + 0x01, operation, true, MemReg_Reg, ModRM); \ - MapPartial(start + 0x02, operation, false, Reg_MemReg, ModRM); \ - MapPartial(start + 0x03, operation, true, Reg_MemReg, ModRM); \ - MapRegData(start + 0x04, operation, false, AL); \ - MapRegData(start + 0x05, operation, true, AX); +#define PartialBlock(start, operation) \ + MapMemRegReg(start + 0x00, operation, MemReg_Reg, 1); \ + MapMemRegReg(start + 0x01, operation, MemReg_Reg, 2); \ + MapMemRegReg(start + 0x02, operation, Reg_MemReg, 1); \ + MapMemRegReg(start + 0x03, operation, Reg_MemReg, 2); \ + MapRegData(start + 0x04, operation, AL, 1); \ + MapRegData(start + 0x05, operation, AX, 2); PartialBlock(0x00, ADD); - MapComplete(0x06, PUSH, ES, None); - MapComplete(0x07, POP, ES, None); + MapComplete(0x06, PUSH, ES, None, 2); + MapComplete(0x07, POP, ES, None, 2); PartialBlock(0x08, OR); - MapComplete(0x0e, PUSH, CS, None); - /* 0x0f: not used. */ + MapComplete(0x0e, PUSH, CS, None, 2); + // 0x0f: not used. PartialBlock(0x10, ADC); - MapComplete(0x16, PUSH, SS, None); - MapComplete(0x17, POP, SS, None); + MapComplete(0x16, PUSH, SS, None, 2); + MapComplete(0x17, POP, SS, None, 2); PartialBlock(0x18, SBB); - MapComplete(0x1e, PUSH, DS, None); - MapComplete(0x1f, POP, DS, None); + MapComplete(0x1e, PUSH, DS, None, 2); + MapComplete(0x1f, POP, DS, None, 2); PartialBlock(0x20, AND); case 0x26: segment_override_ = Source::ES; break; - MapComplete(0x27, DAA, None, None); + MapComplete(0x27, DAA, None, None, 1); PartialBlock(0x28, SUB); case 0x2e: segment_override_ = Source::CS; break; - MapComplete(0x2f, DAS, None, None); + MapComplete(0x2f, DAS, None, None, 1); PartialBlock(0x30, XOR); case 0x36: segment_override_ = Source::SS; break; - MapComplete(0x37, AAA, None, None); + MapComplete(0x37, AAA, None, None, 1); PartialBlock(0x38, CMP); case 0x3e: segment_override_ = Source::DS; break; - MapComplete(0x3f, AAS, None, None); + MapComplete(0x3f, AAS, None, None, 1); #undef PartialBlock #define RegisterBlock(start, operation) \ - MapComplete(start + 0x00, operation, AX, AX); \ - MapComplete(start + 0x01, operation, CX, CX); \ - MapComplete(start + 0x02, operation, DX, DX); \ - MapComplete(start + 0x03, operation, BX, BX); \ - MapComplete(start + 0x04, operation, SP, SP); \ - MapComplete(start + 0x05, operation, BP, BP); \ - MapComplete(start + 0x06, operation, SI, SI); \ - MapComplete(start + 0x07, operation, DI, DI); \ + MapComplete(start + 0x00, operation, AX, AX, 2); \ + MapComplete(start + 0x01, operation, CX, CX, 2); \ + MapComplete(start + 0x02, operation, DX, DX, 2); \ + MapComplete(start + 0x03, operation, BX, BX, 2); \ + MapComplete(start + 0x04, operation, SP, SP, 2); \ + MapComplete(start + 0x05, operation, BP, BP, 2); \ + MapComplete(start + 0x06, operation, SI, SI, 2); \ + MapComplete(start + 0x07, operation, DI, DI, 2); \ RegisterBlock(0x40, INC); RegisterBlock(0x48, DEC); @@ -118,9 +162,8 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { #undef RegisterBlock - /* 0x60–0x6f: not used. */ + // 0x60–0x6f: not used. -#define MapJump(value, operation) MapPartial(value, operation, false, Immediate, AwaitingOperands); MapJump(0x70, JO); MapJump(0x71, JNO); MapJump(0x72, JB); @@ -137,68 +180,104 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { MapJump(0x7d, JNL); MapJump(0x7e, JLE); MapJump(0x7f, JNLE); -#undef MapJump // TODO: // // 0x80, 0x81, 0x82, 0x83, which all require more - // input, from the ModRM byte. + // input, from the ModRegRM byte. - MapPartial(0x84, TEST, false, MemReg_Reg, ModRM); - MapPartial(0x85, TEST, true, MemReg_Reg, ModRM); - MapPartial(0x86, XCHG, false, Reg_MemReg, ModRM); - MapPartial(0x87, XCHG, true, Reg_MemReg, ModRM); - MapPartial(0x88, MOV, false, MemReg_Reg, ModRM); - MapPartial(0x89, MOV, true, MemReg_Reg, ModRM); - MapPartial(0x8a, MOV, false, Reg_MemReg, ModRM); - MapPartial(0x8b, MOV, true, Reg_MemReg, ModRM); - /* 0x8c: not used. */ - MapPartial(0x8d, LEA, true, Reg_Addr, ModRM); - MapPartial(0x8e, MOV, true, SegReg_MemReg, ModRM); + MapMemRegReg(0x84, TEST, MemReg_Reg, 1); + MapMemRegReg(0x85, TEST, MemReg_Reg, 2); + MapMemRegReg(0x86, XCHG, Reg_MemReg, 1); + MapMemRegReg(0x87, XCHG, Reg_MemReg, 2); + MapMemRegReg(0x88, MOV, MemReg_Reg, 1); + MapMemRegReg(0x89, MOV, MemReg_Reg, 2); + MapMemRegReg(0x8a, MOV, Reg_MemReg, 1); + MapMemRegReg(0x8b, MOV, Reg_MemReg, 2); + // 0x8c: not used. + MapMemRegReg(0x8d, LEA, Reg_MemReg, 2); +// MapMemRegReg(0x8e, MOV, SegReg_MemReg, 1); // TODO: SegReg_MemReg - // TODO: 0x8f, which requires further selection from the ModRM byte. + // TODO: 0x8f, which requires further selection from the ModRegRM byte. - MapComplete(0x90, NOP, None, None); // Or XCHG AX, AX? - MapComplete(0x91, XCHG, AX, CX); - MapComplete(0x92, XCHG, AX, DX); - MapComplete(0x93, XCHG, AX, BX); - MapComplete(0x94, XCHG, AX, SP); - MapComplete(0x95, XCHG, AX, BP); - MapComplete(0x96, XCHG, AX, SI); - MapComplete(0x97, XCHG, AX, DI); + MapComplete(0x90, NOP, None, None, 0); // Or XCHG AX, AX? + MapComplete(0x91, XCHG, AX, CX, 2); + MapComplete(0x92, XCHG, AX, DX, 2); + MapComplete(0x93, XCHG, AX, BX, 2); + MapComplete(0x94, XCHG, AX, SP, 2); + MapComplete(0x95, XCHG, AX, BP, 2); + MapComplete(0x96, XCHG, AX, SI, 2); + MapComplete(0x97, XCHG, AX, DI, 2); - MapComplete(0x98, CBW, None, None); - MapComplete(0x99, CWD, None, None); - MapPartial(0x9a, CALL, true, Immediate, AwaitingOperands); - MapComplete(0x9b, WAIT, None, None); - MapComplete(0x9c, PUSHF, None, None); - MapComplete(0x9d, POPF, None, None); - MapComplete(0x9e, SAHF, None, None); - MapComplete(0x9f, LAHF, None, None); + MapComplete(0x98, CBW, None, None, 1); + MapComplete(0x99, CWD, None, None, 2); + MapFar(0x9a, CALL); + MapComplete(0x9b, WAIT, None, None, 0); + MapComplete(0x9c, PUSHF, None, None, 2); + MapComplete(0x9d, POPF, None, None, 2); + MapComplete(0x9e, SAHF, None, None, 1); + MapComplete(0x9f, LAHF, None, None, 1); - MapPartial(0xa0, MOV, false, Reg_Addr, AwaitingOperands); + MapRegAddr(0xa0, MOV, AL, 1, 1); MapRegAddr(0xa1, MOV, AX, 2, 2); + MapAddrReg(0xa2, MOV, AL, 1, 1); MapAddrReg(0xa3, MOV, AX, 2, 2); - MapRegData(0xb0, MOV, false, AL); MapRegData(0xb1, MOV, false, CL); - MapRegData(0xb2, MOV, false, DL); MapRegData(0xb3, MOV, false, BL); - MapRegData(0xb4, MOV, false, AH); MapRegData(0xb5, MOV, false, CH); - MapRegData(0xb6, MOV, false, DH); MapRegData(0xb7, MOV, false, BH); - MapRegData(0xb8, MOV, true, AX); MapRegData(0xb9, MOV, true, CX); - MapRegData(0xba, MOV, true, DX); MapRegData(0xbb, MOV, true, BX); - MapRegData(0xbc, MOV, true, SP); MapRegData(0xbd, MOV, true, BP); - MapRegData(0xbe, MOV, true, SI); MapRegData(0xbf, MOV, true, DI); + MapComplete(0xa4, MOVS, None, None, 1); + MapComplete(0xa5, MOVS, None, None, 2); + MapComplete(0xa6, CMPS, None, None, 1); + MapComplete(0xa7, CMPS, None, None, 2); - MapPartial(0xc2, RETIntra, true, Immediate, AwaitingOperands); - MapComplete(0xc3, RETIntra, None, None); - MapPartial(0xca, RETInter, true, Immediate, AwaitingOperands); - MapComplete(0xcb, RETInter, None, None); + MapComplete(0xaa, STOS, None, None, 1); + MapComplete(0xab, STOS, None, None, 2); + MapComplete(0xac, LODS, None, None, 1); + MapComplete(0xad, LODS, None, None, 2); + MapComplete(0xae, SCAS, None, None, 1); + MapComplete(0xaf, SCAS, None, None, 2); - MapPartial(0xd4, AAM, false, Immediate, AwaitingOperands); - MapPartial(0xd5, AAD, false, Immediate, AwaitingOperands); + MapRegData(0xb0, MOV, AL, 1); MapRegData(0xb1, MOV, CL, 1); + MapRegData(0xb2, MOV, DL, 1); MapRegData(0xb3, MOV, BL, 1); + MapRegData(0xb4, MOV, AH, 1); MapRegData(0xb5, MOV, CH, 1); + MapRegData(0xb6, MOV, DH, 1); MapRegData(0xb7, MOV, BH, 1); + MapRegData(0xb8, MOV, AX, 2); MapRegData(0xb9, MOV, CX, 2); + MapRegData(0xba, MOV, DX, 2); MapRegData(0xbb, MOV, BX, 2); + MapRegData(0xbc, MOV, SP, 2); MapRegData(0xbd, MOV, BP, 2); + MapRegData(0xbe, MOV, SI, 2); MapRegData(0xbf, MOV, DI, 2); -// MapPartial(0xe4, IN, false, Immediate, AwaitingOperands); + MapRegData(0xc2, RETIntra, None, 2); + MapComplete(0xc3, RETIntra, None, None, 2); - MapComplete(0xec, IN, DX, AL); MapComplete(0xed, IN, DX, AX); - MapComplete(0xee, OUT, AL, DX); MapComplete(0xef, OUT, AX, DX); + MapMemRegReg(0xc4, LES, Reg_MemReg, 4); + MapMemRegReg(0xc5, LDS, Reg_MemReg, 4); + + MapRegData(0xca, RETInter, None, 2); + MapComplete(0xcb, RETInter, None, None, 4); + + MapComplete(0xcf, IRET, None, None, 0); + + MapRegData(0xd4, AAM, None, 1); + MapRegData(0xd5, AAD, None, 1); + + MapMemRegReg(0xd8, ESC, MemReg_Reg, 0); + MapMemRegReg(0xd9, ESC, MemReg_Reg, 0); + MapMemRegReg(0xda, ESC, MemReg_Reg, 0); + MapMemRegReg(0xdb, ESC, MemReg_Reg, 0); + MapMemRegReg(0xdc, ESC, MemReg_Reg, 0); + MapMemRegReg(0xdd, ESC, MemReg_Reg, 0); + MapMemRegReg(0xde, ESC, MemReg_Reg, 0); + MapMemRegReg(0xdf, ESC, MemReg_Reg, 0); + + MapRegAddr(0xe4, IN, AL, 1, 1); MapRegAddr(0xe5, IN, AX, 2, 1); + MapAddrReg(0xe6, OUT, AL, 1, 1); MapAddrReg(0xe7, OUT, AX, 2, 1); + + MapRegData(0xe8, CALL, None, 2); + MapRegData(0xe9, JMP, None, 2); + + MapFar(0xea, JMP); + + MapComplete(0xec, IN, DX, AL, 1); MapComplete(0xed, IN, DX, AX, 1); + MapComplete(0xee, OUT, AL, DX, 1); MapComplete(0xef, OUT, AX, DX, 1); + + MapComplete(0xf9, STC, None, None, 1); + MapComplete(0xfd, STD, None, None, 1); // Other prefix bytes. case 0xf0: lock_ = true; break; @@ -209,17 +288,22 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { #undef MapInstr - if(phase_ == Phase::ModRM && source != end) { + // MARK: - ModRegRM byte, if any. + + if(phase_ == Phase::ModRegRM && source != end) { const uint8_t mod = *source >> 6; // i.e. mode. const uint8_t reg = (*source >> 3) & 7; // i.e. register. const uint8_t rm = *source & 7; // i.e. register/memory. + ++source; + ++consumed_; - switch(format_) { - case Format::Reg_MemReg: - case Format::MemReg_Reg: { + switch(modregrm_format_) { + case ModRegRMFormat::Reg_MemReg: + case ModRegRMFormat::MemReg_Reg: { Source memreg; - constexpr Source reg_table[2][8] = { + constexpr Source reg_table[3][8] = { + {}, { Source::AL, Source::CL, Source::DL, Source::BL, Source::AH, Source::CH, Source::DH, Source::BH, @@ -231,7 +315,6 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { switch(mod) { case 0: { - add_offset_ = false; constexpr Source rm_table[8] = { Source::IndBXPlusSI, Source::IndBXPlusDI, Source::IndBPPlusSI, Source::IndBPPlusDI, @@ -242,8 +325,6 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { } break; default: { - add_offset_ = true; - large_offset_ = (mod == 2); constexpr Source rm_table[8] = { Source::IndBXPlusSI, Source::IndBXPlusDI, Source::IndBPPlusSI, Source::IndBPPlusDI, @@ -251,28 +332,35 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { Source::IndBP, Source::IndBX, }; memreg = rm_table[rm]; + + displacement_size_ = 1 + (mod == 2); } break; // Other operand is just a register. - case 3: memreg = reg_table[large_operand_][rm]; break; + case 3: + memreg = reg_table[operation_size_][rm]; + break; } - // These will be switched over at ReadyToPost if the format_ requires it. - source_ = reg_table[large_operand_][reg]; - destination_ = memreg; - phase_ = (add_offset_ || memreg == Source::DirectAddress) ? Phase::AwaitingOperands : Phase::ReadyToPost; + if(modregrm_format_ == ModRegRMFormat::Reg_MemReg) { + source_ = memreg; + destination_ = reg_table[operation_size_][reg]; + } else { + source_ = reg_table[operation_size_][reg]; + destination_ = memreg; + } + phase_ = Phase::AwaitingDisplacementOrOperand; } break; default: assert(false); } - - ++source; - ++consumed_; } - if(phase_ == Phase::AwaitingOperands && source != end) { + // MARK: - Displacement and operand. + + if(phase_ == Phase::AwaitingDisplacementOrOperand && source != end) { // TODO: calculate number of expected operands. - const int required_bytes = large_operand_ ? 2 : 1; + const int required_bytes = displacement_size_ + operand_size_; const int outstanding_bytes = required_bytes - operand_bytes_; const int bytes_to_consume = std::min(int(end - source), outstanding_bytes); @@ -287,38 +375,15 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { } } + // MARK: - Check for completion. + if(phase_ == Phase::ReadyToPost) { - Instruction result; - switch(format_) { - case Format::Reg_Data: - result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, source_, destination_, consumed_); - break; - - case Format::Immediate: - result = Instruction(operation_, Size::Byte, Source::Immediate, Source::None, consumed_); - break; - - case Format::Implied: - result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, source_, destination_, consumed_); - break; - - case Format::MemReg_Reg: - result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, source_, destination_, consumed_); - break; - - case Format::Reg_MemReg: - result = Instruction(operation_, large_operand_ ? Size::Word : Size::Byte, destination_, source_, consumed_); - break; - - default: assert(false); - } - - // Reset parser. + Instruction result(operation_, Size(operation_size_), source_, destination_, consumed_); reset_parsing(); phase_ = Phase::Instruction; - return result; } + // i.e. not done yet. return Instruction(); } diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 0576273a0..9d8ac2d3e 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -40,6 +40,7 @@ enum class Operation: uint8_t { POP, POPF, PUSH, PUSHF, RCL, RCR, REP, ROL, ROR, SAHF, SAR, SBB, SCAS, SHL, SHR, STC, STD, STI, STOS, SUB, TEST, WAIT, XCHG, XLAT, XOR, + LES, RETInter, RETIntra, @@ -77,6 +78,10 @@ enum class Source: uint8_t { Immediate }; +enum class Repetition: uint8_t { + None, RepE, RepNE +}; + class Instruction { public: Operation operation = Operation::Invalid; @@ -89,6 +94,10 @@ class Instruction { return size_; } + bool lock() const { + return false; + } + Instruction() {} Instruction(int size) : size_(size) {} Instruction(Operation operation, Size operand_size, Source source, Source destination, int size) : @@ -96,6 +105,8 @@ class Instruction { private: int size_ = -1; + int16_t displacement_ = 0; + int16_t operand_ = 0; }; /*! @@ -120,66 +131,51 @@ struct Decoder { enum class Phase { /// Captures all prefixes and continues until an instruction byte is encountered. Instruction, - /// Receives a ModRM byte and either populates the source_ and dest_ fields appropriately + /// Receives a ModRegRM byte and either populates the source_ and dest_ fields appropriately /// or completes decoding of the instruction, as per the instruction format. - ModRM, - /// Waits for sufficiently many bytes to pass for all associated operands to be captured. - AwaitingOperands, + ModRegRM, + /// Waits for sufficiently many bytes to pass for the required displacement and operand to be captured. + /// Cf. displacement_size_ and operand_size_. + AwaitingDisplacementOrOperand, /// Forms and returns an Instruction, and resets parsing state. ReadyToPost } phase_ = Phase::Instruction; - /// During the ModRM phase, format dictates interpretation of the ModRM byte. + /// During the ModRegRM phase, format dictates interpretation of the ModRegRM byte. /// /// During the ReadyToPost phase, format determines how transiently-recorded fields /// are packaged into an Instruction. - enum class Format: uint8_t { - Implied, - - // In both cases: pass the ModRM for mode, register and register/memory - // flags and populate the source_ and destination_ fields appropriate. - // During the ModRM phase they'll be populated as source_ = register, - // destination_ = register/memory; the ReadyToPost phase should switch - // those around as necessary. + enum class ModRegRMFormat: uint8_t { + // Parse the ModRegRM for mode, register and register/memory flags + // and populate the source_ and destination_ fields appropriate. MemReg_Reg, Reg_MemReg, - Reg_Data, - - // - Reg_Addr, - Addr_Reg, - - SegReg_MemReg, - - Immediate, - } format_ = Format::MemReg_Reg; - // TODO: figure out which Formats can be folded together, - // and which are improperly elided. + } modregrm_format_ = ModRegRMFormat::MemReg_Reg; // Ephemeral decoding state. Operation operation_ = Operation::Invalid; - bool large_operand_ = false; + uint8_t instr_ = 0x00; // TODO: is this desired, versus loading more context into ModRegRMFormat? + int consumed_ = 0, operand_bytes_ = 0; + + // Source and destination locations. Source source_ = Source::None; Source destination_ = Source::None; - uint8_t instr_ = 0x00; - bool add_offset_ = false; - bool large_offset_ = false; + + // Facts about the instruction. + int displacement_size_ = 0; // i.e. size of in-stream displacement, if any. + int operand_size_ = 0; // i.e. size of in-stream operand, if any. + int operation_size_ = 0; // i.e. size of data manipulated by the operation. // Prefix capture fields. - enum class Repetition: uint8_t { - None, RepE, RepNE - } repetition_ = Repetition::None; + Repetition repetition_ = Repetition::None; bool lock_ = false; Source segment_override_ = Source::None; - // Size capture. - int consumed_ = 0; - int operand_bytes_ = 0; - /// Resets size capture and all fields with default values. void reset_parsing() { consumed_ = operand_bytes_ = 0; + displacement_size_ = operand_size_ = 0; lock_ = false; segment_override_ = Source::None; repetition_ = Repetition::None; From 9d7d45338ff3a7e75a51371f85353ef6ea334025 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 5 Jan 2021 21:34:35 -0500 Subject: [PATCH 29/52] Ostensibly gets the instruction stream correct for test case 1. Subject to operand and displacement currently being absent, and likely inconsistencies in field population, most of which are omitted from the Instruction anyway. --- Processors/Decoders/x86/x86.cpp | 106 +++++++++++++++++++------------- Processors/Decoders/x86/x86.hpp | 7 ++- 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index a74faa8f2..1fef40838 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -276,6 +276,9 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { MapComplete(0xec, IN, DX, AL, 1); MapComplete(0xed, IN, DX, AX, 1); MapComplete(0xee, OUT, AL, DX, 1); MapComplete(0xef, OUT, AX, DX, 1); + MapMemRegReg(0xf6, Invalid, MemRegTEST_to_IDIV, 1); + MapMemRegReg(0xf7, Invalid, MemRegTEST_to_IDIV, 2); + MapComplete(0xf9, STC, None, None, 1); MapComplete(0xfd, STD, None, None, 1); @@ -297,51 +300,49 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { ++source; ++consumed_; + Source memreg; + constexpr Source reg_table[3][8] = { + {}, + { + Source::AL, Source::CL, Source::DL, Source::BL, + Source::AH, Source::CH, Source::DH, Source::BH, + }, { + Source::AX, Source::CX, Source::DX, Source::BX, + Source::SP, Source::BP, Source::SI, Source::DI, + } + }; + switch(mod) { + case 0: { + constexpr Source rm_table[8] = { + Source::IndBXPlusSI, Source::IndBXPlusDI, + Source::IndBPPlusSI, Source::IndBPPlusDI, + Source::IndSI, Source::IndDI, + Source::DirectAddress, Source::IndBX, + }; + memreg = rm_table[rm]; + } break; + + default: { + constexpr Source rm_table[8] = { + Source::IndBXPlusSI, Source::IndBXPlusDI, + Source::IndBPPlusSI, Source::IndBPPlusDI, + Source::IndSI, Source::IndDI, + Source::IndBP, Source::IndBX, + }; + memreg = rm_table[rm]; + + displacement_size_ = 1 + (mod == 2); + } break; + + // Other operand is just a register. + case 3: + memreg = reg_table[operation_size_][rm]; + break; + } + switch(modregrm_format_) { case ModRegRMFormat::Reg_MemReg: case ModRegRMFormat::MemReg_Reg: { - Source memreg; - - constexpr Source reg_table[3][8] = { - {}, - { - Source::AL, Source::CL, Source::DL, Source::BL, - Source::AH, Source::CH, Source::DH, Source::BH, - }, { - Source::AX, Source::CX, Source::DX, Source::BX, - Source::SP, Source::BP, Source::SI, Source::DI, - } - }; - - switch(mod) { - case 0: { - constexpr Source rm_table[8] = { - Source::IndBXPlusSI, Source::IndBXPlusDI, - Source::IndBPPlusSI, Source::IndBPPlusDI, - Source::IndSI, Source::IndDI, - Source::DirectAddress, Source::IndBX, - }; - memreg = rm_table[rm]; - } break; - - default: { - constexpr Source rm_table[8] = { - Source::IndBXPlusSI, Source::IndBXPlusDI, - Source::IndBPPlusSI, Source::IndBPPlusDI, - Source::IndSI, Source::IndDI, - Source::IndBP, Source::IndBX, - }; - memreg = rm_table[rm]; - - displacement_size_ = 1 + (mod == 2); - } break; - - // Other operand is just a register. - case 3: - memreg = reg_table[operation_size_][rm]; - break; - } - if(modregrm_format_ == ModRegRMFormat::Reg_MemReg) { source_ = memreg; destination_ = reg_table[operation_size_][reg]; @@ -349,11 +350,30 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { source_ = reg_table[operation_size_][reg]; destination_ = memreg; } - phase_ = Phase::AwaitingDisplacementOrOperand; } break; + case ModRegRMFormat::MemRegTEST_to_IDIV: + source_ = destination_ = memreg; + + switch(reg) { + default: + reset_parsing(); + return Instruction(); + + case 0: operation_ = Operation::TEST; break; + case 2: operation_ = Operation::NOT; break; + case 3: operation_ = Operation::NEG; break; + case 4: operation_ = Operation::MUL; break; + case 5: operation_ = Operation::IMUL; break; + case 6: operation_ = Operation::DIV; break; + case 7: operation_ = Operation::IDIV; break; + } + break; + default: assert(false); } + + phase_ = Phase::AwaitingDisplacementOrOperand; } // MARK: - Displacement and operand. diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 9d8ac2d3e..a24a5898e 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -146,11 +146,16 @@ struct Decoder { /// During the ReadyToPost phase, format determines how transiently-recorded fields /// are packaged into an Instruction. enum class ModRegRMFormat: uint8_t { - // Parse the ModRegRM for mode, register and register/memory flags + // Parse the ModRegRM for mode, register and register/memory fields // and populate the source_ and destination_ fields appropriate. MemReg_Reg, Reg_MemReg, + // Parse for mode and register/memory fields, populating both + // source_ and destination_ fields with the result. Use the 'register' + // field to pick an operation from the TEST/NOT/NEG/MUL/IMUL/DIV/IDIV group. + MemRegTEST_to_IDIV, + } modregrm_format_ = ModRegRMFormat::MemReg_Reg; // Ephemeral decoding state. From 17cbba85fc96d1a02197686caca1bcec28a1dbe2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 5 Jan 2021 21:47:12 -0500 Subject: [PATCH 30/52] Formalises what's missing in terms of opcodes and fills in some blanks. --- Processors/Decoders/x86/x86.cpp | 32 +++++++++++++++++++++++++++++--- Processors/Decoders/x86/x86.hpp | 4 ++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 1fef40838..af768627c 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -225,7 +225,8 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { MapComplete(0xa5, MOVS, None, None, 2); MapComplete(0xa6, CMPS, None, None, 1); MapComplete(0xa7, CMPS, None, None, 2); - + MapRegData(0xa8, TEST, AL, 1); + MapRegData(0xa9, TEST, AX, 2); MapComplete(0xaa, STOS, None, None, 1); MapComplete(0xab, STOS, None, None, 2); MapComplete(0xac, LODS, None, None, 1); @@ -244,18 +245,22 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { MapRegData(0xc2, RETIntra, None, 2); MapComplete(0xc3, RETIntra, None, None, 2); - MapMemRegReg(0xc4, LES, Reg_MemReg, 4); MapMemRegReg(0xc5, LDS, Reg_MemReg, 4); MapRegData(0xca, RETInter, None, 2); MapComplete(0xcb, RETInter, None, None, 4); + MapComplete(0xcc, INT3, None, None, 0); + MapRegData(0xcd, INT, None, 1); + MapComplete(0xce, INTO, None, None, 0); MapComplete(0xcf, IRET, None, None, 0); MapRegData(0xd4, AAM, None, 1); MapRegData(0xd5, AAD, None, 1); + MapComplete(0xd7, XLAT, None, None, 1); + MapMemRegReg(0xd8, ESC, MemReg_Reg, 0); MapMemRegReg(0xd9, ESC, MemReg_Reg, 0); MapMemRegReg(0xda, ESC, MemReg_Reg, 0); @@ -265,23 +270,44 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { MapMemRegReg(0xde, ESC, MemReg_Reg, 0); MapMemRegReg(0xdf, ESC, MemReg_Reg, 0); + MapJump(0xe0, LOOPNE); MapJump(0xe1, LOOPE); + MapJump(0xe2, LOOP); MapJump(0xe3, JPCX); + MapRegAddr(0xe4, IN, AL, 1, 1); MapRegAddr(0xe5, IN, AX, 2, 1); MapAddrReg(0xe6, OUT, AL, 1, 1); MapAddrReg(0xe7, OUT, AX, 2, 1); MapRegData(0xe8, CALL, None, 2); MapRegData(0xe9, JMP, None, 2); - MapFar(0xea, JMP); + MapJump(0xeb, JMP); MapComplete(0xec, IN, DX, AL, 1); MapComplete(0xed, IN, DX, AX, 1); MapComplete(0xee, OUT, AL, DX, 1); MapComplete(0xef, OUT, AX, DX, 1); + MapComplete(0xf4, HLT, None, None, 1); + MapComplete(0xf5, CMC, None, None, 1); MapMemRegReg(0xf6, Invalid, MemRegTEST_to_IDIV, 1); MapMemRegReg(0xf7, Invalid, MemRegTEST_to_IDIV, 2); + MapComplete(0xf8, CLC, None, None, 1); MapComplete(0xf9, STC, None, None, 1); + MapComplete(0xfa, CLI, None, None, 1); + MapComplete(0xfb, STI, None, None, 1); + MapComplete(0xfc, CLD, None, None, 1); MapComplete(0xfd, STD, None, None, 1); + /* + Unimplemented (but should be): + + 0x8e, 0x8f, + 0xc6, 0xc7, + 0xd0, 0xd1, 0xd2, 0xd3, + 0xfe, 0xff + + [and consider which others are unused but seem to be + known to consume a second byte?] + */ + // Other prefix bytes. case 0xf0: lock_ = true; break; case 0xf2: repetition_ = Repetition::RepNE; break; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index a24a5898e..dfb87e8a1 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -25,7 +25,7 @@ enum class Operation: uint8_t { AAA, AAD, AAM, AAS, ADC, ADD, AND, CALL, CBW, CLC, CLD, CLI, CMC, CMP, CMPS, CWD, DAA, DAS, DEC, DIV, ESC, HLT, IDIV, IMUL, IN, - INC, INT, INTO, IRET, + INC, INT, INT3, INTO, IRET, JO, JNO, JB, JNB, JE, JNE, @@ -40,7 +40,7 @@ enum class Operation: uint8_t { POP, POPF, PUSH, PUSHF, RCL, RCR, REP, ROL, ROR, SAHF, SAR, SBB, SCAS, SHL, SHR, STC, STD, STI, STOS, SUB, TEST, WAIT, XCHG, XLAT, XOR, - LES, + LES, LOOP, JPCX, RETInter, RETIntra, From 995904993da323d916f1b2c700cf90aeae50ad9b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 6 Jan 2021 21:18:24 -0500 Subject: [PATCH 31/52] Fills in 8f, c2, c3, ca and cb. Also switches to RETN and RETF for near/far RET as this seems idiomatic. --- Processors/Decoders/x86/x86.cpp | 33 ++++++++++++++++++++++++--------- Processors/Decoders/x86/x86.hpp | 15 +++++++++++++-- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index af768627c..3cd0c458a 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -197,8 +197,7 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { // 0x8c: not used. MapMemRegReg(0x8d, LEA, Reg_MemReg, 2); // MapMemRegReg(0x8e, MOV, SegReg_MemReg, 1); // TODO: SegReg_MemReg - - // TODO: 0x8f, which requires further selection from the ModRegRM byte. + MapMemRegReg(0x8f, POP, MemRegPOP, 2); MapComplete(0x90, NOP, None, None, 0); // Or XCHG AX, AX? MapComplete(0x91, XCHG, AX, CX, 2); @@ -243,13 +242,15 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { MapRegData(0xbc, MOV, SP, 2); MapRegData(0xbd, MOV, BP, 2); MapRegData(0xbe, MOV, SI, 2); MapRegData(0xbf, MOV, DI, 2); - MapRegData(0xc2, RETIntra, None, 2); - MapComplete(0xc3, RETIntra, None, None, 2); + MapRegData(0xc2, RETN, None, 2); + MapComplete(0xc3, RETN, None, None, 2); MapMemRegReg(0xc4, LES, Reg_MemReg, 4); MapMemRegReg(0xc5, LDS, Reg_MemReg, 4); + MapMemRegReg(0xc6, MOV, MemRegMOV, 1); + MapMemRegReg(0xc7, MOV, MemRegMOV, 2); - MapRegData(0xca, RETInter, None, 2); - MapComplete(0xcb, RETInter, None, None, 4); + MapRegData(0xca, RETF, None, 2); + MapComplete(0xcb, RETF, None, None, 4); MapComplete(0xcc, INT3, None, None, 0); MapRegData(0xcd, INT, None, 1); @@ -299,8 +300,7 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { /* Unimplemented (but should be): - 0x8e, 0x8f, - 0xc6, 0xc7, + 0x8e, 0xd0, 0xd1, 0xd2, 0xd3, 0xfe, 0xff @@ -384,7 +384,7 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { switch(reg) { default: reset_parsing(); - return Instruction(); + return Instruction(consumed_); case 0: operation_ = Operation::TEST; break; case 2: operation_ = Operation::NOT; break; @@ -396,6 +396,21 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { } break; + case ModRegRMFormat::MemRegPOP: + source_ = destination_ = memreg; + + if(reg != 0) { + reset_parsing(); + return Instruction(consumed_); + } + break; + + case ModRegRMFormat::MemRegMOV: + source_ = Source::Immediate; + destination_ = memreg; + operand_size_ = operation_size_; + break; + default: assert(false); } diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index dfb87e8a1..dbe403756 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -23,6 +23,7 @@ enum class Model { enum class Operation: uint8_t { Invalid, + /// ASCII adjust for addition; AAA, AAD, AAM, AAS, ADC, ADD, AND, CALL, CBW, CLC, CLD, CLI, CMC, CMP, CMPS, CWD, DAA, DAS, DEC, DIV, ESC, HLT, IDIV, IMUL, IN, INC, INT, INT3, INTO, IRET, @@ -42,8 +43,8 @@ enum class Operation: uint8_t { WAIT, XCHG, XLAT, XOR, LES, LOOP, JPCX, - RETInter, - RETIntra, + RETF, + RETN, }; enum class Size: uint8_t { @@ -156,6 +157,16 @@ struct Decoder { // field to pick an operation from the TEST/NOT/NEG/MUL/IMUL/DIV/IDIV group. MemRegTEST_to_IDIV, + // Parse for mode and register/memory fields, populating both + // source_ and destination_ fields with the result. Use the 'register' + // field to check for the POP operation. + MemRegPOP, + + // Parse for mode and register/memory fields, populating both + // the destination_ field with the result and setting source_ to Immediate. + // Use the 'register' field to check for the MOV operation. + MemRegMOV, + } modregrm_format_ = ModRegRMFormat::MemReg_Reg; // Ephemeral decoding state. From fd49b72e31a5a18d474ebe218d51e982feea4cbe Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 7 Jan 2021 21:30:01 -0500 Subject: [PATCH 32/52] Simplifies macros, implements d0, d1, d2 and d3. --- Processors/Decoders/x86/x86.cpp | 449 +++++++++++++++++--------------- Processors/Decoders/x86/x86.hpp | 62 ++++- 2 files changed, 291 insertions(+), 220 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 3cd0c458a..45f058f6f 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -21,73 +21,54 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { // MARK: - Prefixes (if present) and the opcode. +/// Helper macro for those that follow. +#define SetOpSrcDestSize(op, src, dest, size) \ + operation_ = Operation::op; \ + source_ = Source::src; \ + destination_ = Source::dest; \ + operation_size_ = size + /// Covers anything which is complete as soon as the opcode is encountered. -#define MapComplete(value, op, src, dest, size) \ - case value: \ - operation_ = Operation::op; \ - source_ = Source::src; \ - destination_ = Source::dest; \ - phase_ = Phase::ReadyToPost; \ - operation_size_ = size; \ - break +#define Complete(op, src, dest, size) \ + SetOpSrcDestSize(op, src, dest, size); \ + phase_ = Phase::ReadyToPost /// Handles instructions of the form rr, kk and rr, jjkk, i.e. a destination register plus an operand. -#define MapRegData(value, op, dest, size) \ - case value: \ - operation_ = Operation::op; \ - source_ = Source::Immediate; \ - destination_ = Source::dest; \ - phase_ = Phase::AwaitingDisplacementOrOperand; \ - operand_size_ = size; \ - break +#define RegData(op, dest, size) \ + SetOpSrcDestSize(op, DirectAddress, dest, size); \ + phase_ = Phase::AwaitingDisplacementOrOperand /// Handles instructions of the form Ax, jjkk where the latter is implicitly an address. -#define MapRegAddr(value, op, dest, op_size, addr_size) \ - case value: \ - operation_ = Operation::op; \ - destination_ = Source::dest; \ - source_ = Source::DirectAddress; \ - phase_ = Phase::AwaitingDisplacementOrOperand; \ - operand_size_ = addr_size; \ - operation_size_ = op_size; \ - break +#define RegAddr(op, dest, op_size, addr_size) \ + SetOpSrcDestSize(op, DirectAddress, dest, op_size); \ + operand_size_ = addr_size; \ + phase_ = Phase::AwaitingDisplacementOrOperand /// Handles instructions of the form jjkk, Ax where the former is implicitly an address. -#define MapAddrReg(value, op, source, op_size, addr_size) \ - case value: \ - operation_ = Operation::op; \ - source_ = Source::source; \ - destination_ = Source::DirectAddress; \ - phase_ = Phase::AwaitingDisplacementOrOperand; \ - operand_size_ = addr_size; \ - operation_size_ = op_size; \ - break +#define AddrReg(op, source, op_size, addr_size) \ + SetOpSrcDestSize(op, source, DirectAddress, op_size); \ + operand_size_ = addr_size; \ + phase_ = Phase::AwaitingDisplacementOrOperand /// Covers both `mem/reg, reg` and `reg, mem/reg`. -#define MapMemRegReg(value, op, format, size) \ - case value: \ - operation_ = Operation::op; \ - phase_ = Phase::ModRegRM; \ - modregrm_format_ = ModRegRMFormat::format; \ - operand_size_ = 0; \ - operation_size_ = size; \ - break +#define MemRegReg(op, format, size) \ + operation_ = Operation::op; \ + phase_ = Phase::ModRegRM; \ + modregrm_format_ = ModRegRMFormat::format; \ + operand_size_ = 0; \ + operation_size_ = size /// Handles JO, JNO, JB, etc — jumps with a single byte displacement. -#define MapJump(value, op) \ - case value: \ - operation_ = Operation::op; \ - phase_ = Phase::AwaitingDisplacementOrOperand; \ - operand_size_ = 1; \ - break +#define Jump(op) \ + operation_ = Operation::op; \ + phase_ = Phase::AwaitingDisplacementOrOperand; \ + operand_size_ = 1 /// Handles far CALL and far JMP — fixed four byte operand operations. -#define MapFar(value, op) \ - case value: \ - operation_ = Operation::op; \ - phase_ = Phase::AwaitingDisplacementOrOperand; \ - operand_size_ = 4; \ - break +#define Far(op) \ + operation_ = Operation::op; \ + phase_ = Phase::AwaitingDisplacementOrOperand; \ + operand_size_ = 4; \ while(phase_ == Phase::Instruction && source != end) { // Retain the instruction byte, in case additional decoding is deferred @@ -103,205 +84,232 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { return instruction; } -#define PartialBlock(start, operation) \ - MapMemRegReg(start + 0x00, operation, MemReg_Reg, 1); \ - MapMemRegReg(start + 0x01, operation, MemReg_Reg, 2); \ - MapMemRegReg(start + 0x02, operation, Reg_MemReg, 1); \ - MapMemRegReg(start + 0x03, operation, Reg_MemReg, 2); \ - MapRegData(start + 0x04, operation, AL, 1); \ - MapRegData(start + 0x05, operation, AX, 2); +#define PartialBlock(start, operation) \ + case start + 0x00: MemRegReg(operation, MemReg_Reg, 1); break; \ + case start + 0x01: MemRegReg(operation, MemReg_Reg, 2); break; \ + case start + 0x02: MemRegReg(operation, Reg_MemReg, 1); break; \ + case start + 0x03: MemRegReg(operation, Reg_MemReg, 2); break; \ + case start + 0x04: RegData(operation, AL, 1); break; \ + case start + 0x05: RegData(operation, AX, 2) - PartialBlock(0x00, ADD); - MapComplete(0x06, PUSH, ES, None, 2); - MapComplete(0x07, POP, ES, None, 2); + PartialBlock(0x00, ADD); break; + case 0x06: Complete(PUSH, ES, None, 2); break; + case 0x07: Complete(POP, ES, None, 2); break; - PartialBlock(0x08, OR); - MapComplete(0x0e, PUSH, CS, None, 2); - // 0x0f: not used. + PartialBlock(0x08, OR); break; + case 0x0e: Complete(PUSH, CS, None, 2); break; - PartialBlock(0x10, ADC); - MapComplete(0x16, PUSH, SS, None, 2); - MapComplete(0x17, POP, SS, None, 2); + PartialBlock(0x10, ADC); break; + case 0x16: Complete(PUSH, SS, None, 2); break; + case 0x17: Complete(POP, SS, None, 2); break; - PartialBlock(0x18, SBB); - MapComplete(0x1e, PUSH, DS, None, 2); - MapComplete(0x1f, POP, DS, None, 2); + PartialBlock(0x18, SBB); break; + case 0x1e: Complete(PUSH, DS, None, 2); break; + case 0x1f: Complete(POP, DS, None, 2); break; - PartialBlock(0x20, AND); - case 0x26: segment_override_ = Source::ES; break; - MapComplete(0x27, DAA, None, None, 1); + PartialBlock(0x20, AND); break; + case 0x26: segment_override_ = Source::ES; break; + case 0x27: Complete(DAA, AL, AL, 1); break; - PartialBlock(0x28, SUB); - case 0x2e: segment_override_ = Source::CS; break; - MapComplete(0x2f, DAS, None, None, 1); + PartialBlock(0x28, SUB); break; + case 0x2e: segment_override_ = Source::CS; break; + case 0x2f: Complete(DAS, AL, AL, 1); break; - PartialBlock(0x30, XOR); - case 0x36: segment_override_ = Source::SS; break; - MapComplete(0x37, AAA, None, None, 1); + PartialBlock(0x30, XOR); break; + case 0x36: segment_override_ = Source::SS; break; + case 0x37: Complete(AAA, AL, AX, 1); break; - PartialBlock(0x38, CMP); - case 0x3e: segment_override_ = Source::DS; break; - MapComplete(0x3f, AAS, None, None, 1); + PartialBlock(0x38, CMP); break; + case 0x3e: segment_override_ = Source::DS; break; + case 0x3f: Complete(AAS, AL, AX, 1); break; #undef PartialBlock -#define RegisterBlock(start, operation) \ - MapComplete(start + 0x00, operation, AX, AX, 2); \ - MapComplete(start + 0x01, operation, CX, CX, 2); \ - MapComplete(start + 0x02, operation, DX, DX, 2); \ - MapComplete(start + 0x03, operation, BX, BX, 2); \ - MapComplete(start + 0x04, operation, SP, SP, 2); \ - MapComplete(start + 0x05, operation, BP, BP, 2); \ - MapComplete(start + 0x06, operation, SI, SI, 2); \ - MapComplete(start + 0x07, operation, DI, DI, 2); \ +#define RegisterBlock(start, operation) \ + case start + 0x00: Complete(operation, AX, AX, 2); break; \ + case start + 0x01: Complete(operation, CX, CX, 2); break; \ + case start + 0x02: Complete(operation, DX, DX, 2); break; \ + case start + 0x03: Complete(operation, BX, BX, 2); break; \ + case start + 0x04: Complete(operation, SP, SP, 2); break; \ + case start + 0x05: Complete(operation, BP, BP, 2); break; \ + case start + 0x06: Complete(operation, SI, SI, 2); break; \ + case start + 0x07: Complete(operation, DI, DI, 2) - RegisterBlock(0x40, INC); - RegisterBlock(0x48, DEC); - RegisterBlock(0x50, PUSH); - RegisterBlock(0x58, POP); + RegisterBlock(0x40, INC); break; + RegisterBlock(0x48, DEC); break; + RegisterBlock(0x50, PUSH); break; + RegisterBlock(0x58, POP); break; #undef RegisterBlock // 0x60–0x6f: not used. - MapJump(0x70, JO); - MapJump(0x71, JNO); - MapJump(0x72, JB); - MapJump(0x73, JNB); - MapJump(0x74, JE); - MapJump(0x75, JNE); - MapJump(0x76, JBE); - MapJump(0x77, JNBE); - MapJump(0x78, JS); - MapJump(0x79, JNS); - MapJump(0x7a, JP); - MapJump(0x7b, JNP); - MapJump(0x7c, JL); - MapJump(0x7d, JNL); - MapJump(0x7e, JLE); - MapJump(0x7f, JNLE); + case 0x70: Jump(JO); break; + case 0x71: Jump(JNO); break; + case 0x72: Jump(JB); break; + case 0x73: Jump(JNB); break; + case 0x74: Jump(JE); break; + case 0x75: Jump(JNE); break; + case 0x76: Jump(JBE); break; + case 0x77: Jump(JNBE); break; + case 0x78: Jump(JS); break; + case 0x79: Jump(JNS); break; + case 0x7a: Jump(JP); break; + case 0x7b: Jump(JNP); break; + case 0x7c: Jump(JL); break; + case 0x7d: Jump(JNL); break; + case 0x7e: Jump(JLE); break; + case 0x7f: Jump(JNLE); break; // TODO: // // 0x80, 0x81, 0x82, 0x83, which all require more // input, from the ModRegRM byte. - MapMemRegReg(0x84, TEST, MemReg_Reg, 1); - MapMemRegReg(0x85, TEST, MemReg_Reg, 2); - MapMemRegReg(0x86, XCHG, Reg_MemReg, 1); - MapMemRegReg(0x87, XCHG, Reg_MemReg, 2); - MapMemRegReg(0x88, MOV, MemReg_Reg, 1); - MapMemRegReg(0x89, MOV, MemReg_Reg, 2); - MapMemRegReg(0x8a, MOV, Reg_MemReg, 1); - MapMemRegReg(0x8b, MOV, Reg_MemReg, 2); + case 0x84: MemRegReg(TEST, MemReg_Reg, 1); break; + case 0x85: MemRegReg(TEST, MemReg_Reg, 2); break; + case 0x86: MemRegReg(XCHG, Reg_MemReg, 1); break; + case 0x87: MemRegReg(XCHG, Reg_MemReg, 2); break; + case 0x88: MemRegReg(MOV, MemReg_Reg, 1); break; + case 0x89: MemRegReg(MOV, MemReg_Reg, 2); break; + case 0x8a: MemRegReg(MOV, Reg_MemReg, 1); break; + case 0x8b: MemRegReg(MOV, Reg_MemReg, 2); break; // 0x8c: not used. - MapMemRegReg(0x8d, LEA, Reg_MemReg, 2); -// MapMemRegReg(0x8e, MOV, SegReg_MemReg, 1); // TODO: SegReg_MemReg - MapMemRegReg(0x8f, POP, MemRegPOP, 2); + case 0x8d: MemRegReg(LEA, Reg_MemReg, 2); break; +// case 0x84: MemRegReg(0x8e, MOV, SegReg_MemReg, 1); // TODO: SegReg_MemReg + case 0x8f: MemRegReg(POP, MemRegPOP, 2); break; - MapComplete(0x90, NOP, None, None, 0); // Or XCHG AX, AX? - MapComplete(0x91, XCHG, AX, CX, 2); - MapComplete(0x92, XCHG, AX, DX, 2); - MapComplete(0x93, XCHG, AX, BX, 2); - MapComplete(0x94, XCHG, AX, SP, 2); - MapComplete(0x95, XCHG, AX, BP, 2); - MapComplete(0x96, XCHG, AX, SI, 2); - MapComplete(0x97, XCHG, AX, DI, 2); + case 0x90: Complete(NOP, None, None, 0); break; // Or XCHG AX, AX? + case 0x91: Complete(XCHG, AX, CX, 2); break; + case 0x92: Complete(XCHG, AX, DX, 2); break; + case 0x93: Complete(XCHG, AX, BX, 2); break; + case 0x94: Complete(XCHG, AX, SP, 2); break; + case 0x95: Complete(XCHG, AX, BP, 2); break; + case 0x96: Complete(XCHG, AX, SI, 2); break; + case 0x97: Complete(XCHG, AX, DI, 2); break; - MapComplete(0x98, CBW, None, None, 1); - MapComplete(0x99, CWD, None, None, 2); - MapFar(0x9a, CALL); - MapComplete(0x9b, WAIT, None, None, 0); - MapComplete(0x9c, PUSHF, None, None, 2); - MapComplete(0x9d, POPF, None, None, 2); - MapComplete(0x9e, SAHF, None, None, 1); - MapComplete(0x9f, LAHF, None, None, 1); + case 0x98: Complete(CBW, AL, AH, 1); break; + case 0x99: Complete(CWD, AX, DX, 2); break; + case 0x9a: Far(CALLF); break; + case 0x9b: Complete(WAIT, None, None, 0); break; + case 0x9c: Complete(PUSHF, None, None, 2); break; + case 0x9d: Complete(POPF, None, None, 2); break; + case 0x9e: Complete(SAHF, None, None, 1); break; + case 0x9f: Complete(LAHF, None, None, 1); break; - MapRegAddr(0xa0, MOV, AL, 1, 1); MapRegAddr(0xa1, MOV, AX, 2, 2); - MapAddrReg(0xa2, MOV, AL, 1, 1); MapAddrReg(0xa3, MOV, AX, 2, 2); + case 0xa0: RegAddr(MOV, AL, 1, 1); break; + case 0xa1: RegAddr(MOV, AX, 2, 2); break; + case 0xa2: AddrReg(MOV, AL, 1, 1); break; + case 0xa3: AddrReg(MOV, AX, 2, 2); break; - MapComplete(0xa4, MOVS, None, None, 1); - MapComplete(0xa5, MOVS, None, None, 2); - MapComplete(0xa6, CMPS, None, None, 1); - MapComplete(0xa7, CMPS, None, None, 2); - MapRegData(0xa8, TEST, AL, 1); - MapRegData(0xa9, TEST, AX, 2); - MapComplete(0xaa, STOS, None, None, 1); - MapComplete(0xab, STOS, None, None, 2); - MapComplete(0xac, LODS, None, None, 1); - MapComplete(0xad, LODS, None, None, 2); - MapComplete(0xae, SCAS, None, None, 1); - MapComplete(0xaf, SCAS, None, None, 2); + case 0xa4: Complete(MOVS, None, None, 1); break; + case 0xa5: Complete(MOVS, None, None, 2); break; + case 0xa6: Complete(CMPS, None, None, 1); break; + case 0xa7: Complete(CMPS, None, None, 2); break; + case 0xa8: RegData(TEST, AL, 1); break; + case 0xa9: RegData(TEST, AX, 2); break; + case 0xaa: Complete(STOS, None, None, 1); break; + case 0xab: Complete(STOS, None, None, 2); break; + case 0xac: Complete(LODS, None, None, 1); break; + case 0xad: Complete(LODS, None, None, 2); break; + case 0xae: Complete(SCAS, None, None, 1); break; + case 0xaf: Complete(SCAS, None, None, 2); break; - MapRegData(0xb0, MOV, AL, 1); MapRegData(0xb1, MOV, CL, 1); - MapRegData(0xb2, MOV, DL, 1); MapRegData(0xb3, MOV, BL, 1); - MapRegData(0xb4, MOV, AH, 1); MapRegData(0xb5, MOV, CH, 1); - MapRegData(0xb6, MOV, DH, 1); MapRegData(0xb7, MOV, BH, 1); - MapRegData(0xb8, MOV, AX, 2); MapRegData(0xb9, MOV, CX, 2); - MapRegData(0xba, MOV, DX, 2); MapRegData(0xbb, MOV, BX, 2); - MapRegData(0xbc, MOV, SP, 2); MapRegData(0xbd, MOV, BP, 2); - MapRegData(0xbe, MOV, SI, 2); MapRegData(0xbf, MOV, DI, 2); + case 0xb0: RegData(MOV, AL, 1); break; + case 0xb1: RegData(MOV, CL, 1); break; + case 0xb2: RegData(MOV, DL, 1); break; + case 0xb3: RegData(MOV, BL, 1); break; + case 0xb4: RegData(MOV, AH, 1); break; + case 0xb5: RegData(MOV, CH, 1); break; + case 0xb6: RegData(MOV, DH, 1); break; + case 0xb7: RegData(MOV, BH, 1); break; + case 0xb8: RegData(MOV, AX, 2); break; + case 0xb9: RegData(MOV, CX, 2); break; + case 0xba: RegData(MOV, DX, 2); break; + case 0xbb: RegData(MOV, BX, 2); break; + case 0xbc: RegData(MOV, SP, 2); break; + case 0xbd: RegData(MOV, BP, 2); break; + case 0xbe: RegData(MOV, SI, 2); break; + case 0xbf: RegData(MOV, DI, 2); break; - MapRegData(0xc2, RETN, None, 2); - MapComplete(0xc3, RETN, None, None, 2); - MapMemRegReg(0xc4, LES, Reg_MemReg, 4); - MapMemRegReg(0xc5, LDS, Reg_MemReg, 4); - MapMemRegReg(0xc6, MOV, MemRegMOV, 1); - MapMemRegReg(0xc7, MOV, MemRegMOV, 2); + case 0xc2: RegData(RETN, None, 2); break; + case 0xc3: Complete(RETN, None, None, 2); break; + case 0xc4: MemRegReg(LES, Reg_MemReg, 4); break; + case 0xc5: MemRegReg(LDS, Reg_MemReg, 4); break; + case 0xc6: MemRegReg(MOV, MemRegMOV, 1); break; + case 0xc7: MemRegReg(MOV, MemRegMOV, 2); break; - MapRegData(0xca, RETF, None, 2); - MapComplete(0xcb, RETF, None, None, 4); + case 0xca: RegData(RETF, None, 2); break; + case 0xcb: Complete(RETF, None, None, 4); break; - MapComplete(0xcc, INT3, None, None, 0); - MapRegData(0xcd, INT, None, 1); - MapComplete(0xce, INTO, None, None, 0); - MapComplete(0xcf, IRET, None, None, 0); + case 0xcc: Complete(INT3, None, None, 0); break; + case 0xcd: RegData(INT, None, 1); break; + case 0xce: Complete(INTO, None, None, 0); break; + case 0xcf: Complete(IRET, None, None, 0); break; - MapRegData(0xd4, AAM, None, 1); - MapRegData(0xd5, AAD, None, 1); + case 0xd0: case 0xd1: + phase_ = Phase::ModRegRM; + modregrm_format_ = ModRegRMFormat::MemRegROL_to_SAR; + operation_size_ = 1 + (instr_ & 1); + source_ = Source::Immediate; + // TODO: set operand to 1. + break; + case 0xd2: case 0xd3: + phase_ = Phase::ModRegRM; + modregrm_format_ = ModRegRMFormat::MemRegROL_to_SAR; + operation_size_ = 1 + (instr_ & 1); + source_ = Source::CL; + break; + case 0xd4: RegData(AAM, AX, 1); break; + case 0xd5: RegData(AAD, AX, 1); break; - MapComplete(0xd7, XLAT, None, None, 1); + case 0xd7: Complete(XLAT, None, None, 1); break; - MapMemRegReg(0xd8, ESC, MemReg_Reg, 0); - MapMemRegReg(0xd9, ESC, MemReg_Reg, 0); - MapMemRegReg(0xda, ESC, MemReg_Reg, 0); - MapMemRegReg(0xdb, ESC, MemReg_Reg, 0); - MapMemRegReg(0xdc, ESC, MemReg_Reg, 0); - MapMemRegReg(0xdd, ESC, MemReg_Reg, 0); - MapMemRegReg(0xde, ESC, MemReg_Reg, 0); - MapMemRegReg(0xdf, ESC, MemReg_Reg, 0); + case 0xd8: MemRegReg(ESC, MemReg_Reg, 0); break; + case 0xd9: MemRegReg(ESC, MemReg_Reg, 0); break; + case 0xda: MemRegReg(ESC, MemReg_Reg, 0); break; + case 0xdb: MemRegReg(ESC, MemReg_Reg, 0); break; + case 0xdc: MemRegReg(ESC, MemReg_Reg, 0); break; + case 0xdd: MemRegReg(ESC, MemReg_Reg, 0); break; + case 0xde: MemRegReg(ESC, MemReg_Reg, 0); break; + case 0xdf: MemRegReg(ESC, MemReg_Reg, 0); break; - MapJump(0xe0, LOOPNE); MapJump(0xe1, LOOPE); - MapJump(0xe2, LOOP); MapJump(0xe3, JPCX); + case 0xe0: Jump(LOOPNE); break; + case 0xe1: Jump(LOOPE); break; + case 0xe2: Jump(LOOP); break; + case 0xe3: Jump(JPCX); break; - MapRegAddr(0xe4, IN, AL, 1, 1); MapRegAddr(0xe5, IN, AX, 2, 1); - MapAddrReg(0xe6, OUT, AL, 1, 1); MapAddrReg(0xe7, OUT, AX, 2, 1); + case 0xe4: RegAddr(IN, AL, 1, 1); break; + case 0xe5: RegAddr(IN, AX, 2, 1); break; + case 0xe6: AddrReg(OUT, AL, 1, 1); break; + case 0xe7: AddrReg(OUT, AX, 2, 1); break; - MapRegData(0xe8, CALL, None, 2); - MapRegData(0xe9, JMP, None, 2); - MapFar(0xea, JMP); - MapJump(0xeb, JMP); + case 0xe8: RegData(CALLD, None, 2); break; + case 0xe9: RegData(JMP, None, 2); break; + case 0xea: Far(JMP); break; + case 0xeb: Jump(JMP); break; - MapComplete(0xec, IN, DX, AL, 1); MapComplete(0xed, IN, DX, AX, 1); - MapComplete(0xee, OUT, AL, DX, 1); MapComplete(0xef, OUT, AX, DX, 1); + case 0xec: Complete(IN, DX, AL, 1); break; + case 0xed: Complete(IN, DX, AX, 1); break; + case 0xee: Complete(OUT, AL, DX, 1); break; + case 0xef: Complete(OUT, AX, DX, 1); break; - MapComplete(0xf4, HLT, None, None, 1); - MapComplete(0xf5, CMC, None, None, 1); - MapMemRegReg(0xf6, Invalid, MemRegTEST_to_IDIV, 1); - MapMemRegReg(0xf7, Invalid, MemRegTEST_to_IDIV, 2); + case 0xf4: Complete(HLT, None, None, 1); break; + case 0xf5: Complete(CMC, None, None, 1); break; + case 0xf6: MemRegReg(Invalid, MemRegTEST_to_IDIV, 1); break; + case 0xf7: MemRegReg(Invalid, MemRegTEST_to_IDIV, 2); break; - MapComplete(0xf8, CLC, None, None, 1); - MapComplete(0xf9, STC, None, None, 1); - MapComplete(0xfa, CLI, None, None, 1); - MapComplete(0xfb, STI, None, None, 1); - MapComplete(0xfc, CLD, None, None, 1); - MapComplete(0xfd, STD, None, None, 1); + case 0xf8: Complete(CLC, None, None, 1); break; + case 0xf9: Complete(STC, None, None, 1); break; + case 0xfa: Complete(CLI, None, None, 1); break; + case 0xfb: Complete(STI, None, None, 1); break; + case 0xfc: Complete(CLD, None, None, 1); break; + case 0xfd: Complete(STD, None, None, 1); break; /* Unimplemented (but should be): 0x8e, - 0xd0, 0xd1, 0xd2, 0xd3, 0xfe, 0xff [and consider which others are unused but seem to be @@ -315,7 +323,14 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { } } -#undef MapInstr +#undef Far +#undef Jump +#undef MemRegReg +#undef AddrReg +#undef RegAddr +#undef RegData +#undef Complete +#undef SetOpSrcDestSize // MARK: - ModRegRM byte, if any. @@ -396,6 +411,24 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { } break; + case ModRegRMFormat::MemRegROL_to_SAR: + destination_ = memreg; + + switch(reg) { + default: + reset_parsing(); + return Instruction(consumed_); + + case 0: operation_ = Operation::ROL; break; + case 2: operation_ = Operation::ROR; break; + case 3: operation_ = Operation::RCL; break; + case 4: operation_ = Operation::RCR; break; + case 5: operation_ = Operation::SAL; break; + case 6: operation_ = Operation::SHR; break; + case 7: operation_ = Operation::SAR; break; + } + break; + case ModRegRMFormat::MemRegPOP: source_ = destination_ = memreg; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index dbe403756..dc855ec09 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -23,23 +23,56 @@ enum class Model { enum class Operation: uint8_t { Invalid, - /// ASCII adjust for addition; - AAA, AAD, AAM, AAS, ADC, ADD, AND, CALL, CBW, CLC, CLD, CLI, CMC, - CMP, CMPS, CWD, DAA, DAS, DEC, DIV, ESC, HLT, IDIV, IMUL, IN, + /// ASCII adjust after addition; source will be AL and destination will be AX. + AAA, + /// ASCII adjust before division; destination will be AX and source will be a multiplier. + AAD, + /// ASCII adjust after multiplication; destination will be AX and source will be a divider. + AAM, + /// ASCII adjust after subtraction; source will be AL and destination will be AX. + AAS, + /// Add with carry; source, destination, operand and displacement will be populated appropriately. + ADC, + /// Add; source, destination, operand and displacement will be populated appropriately. + ADD, + /// And; source, destination, operand and displacement will be populated appropriately. + AND, + /// Far call; followed by a 32-bit operand. + CALLF, + /// Displacement call; followed by a 16-bit operand providing a call offset. + CALLD, + /* TODO: other CALLs */ + /// Convert byte into word; source will be AL, destination will be AH. + CBW, + /// Clear carry flag; no source or destination provided. + CLC, + /// Clear direction flag; no source or destination provided. + CLD, + /// Clear interrupt flag; no source or destination provided. + CLI, + /// Complement carry flag; no source or destination provided. + CMC, + /// Compare; source, destination, operand and displacement will be populated appropriately. + CMP, + /// Compare [bytes or words, per operation size]; source and destination implied to be DS:[SI] and ES:[DI]. + CMPS, + /// Convert word to double word; source will be AX and destination will be DX. + CWD, + /// Decimal adjust after addition; source and destination will be AL. + DAA, + /// Decimal adjust after subtraction; source and destination will be AL. + DAS, + /// Dec; source, destination, operand and displacement will be populated appropriately. + DEC, + DIV, ESC, HLT, IDIV, IMUL, IN, INC, INT, INT3, INTO, IRET, - JO, JNO, - JB, JNB, - JE, JNE, - JBE, JNBE, - JS, JNS, - JP, JNP, - JL, JNL, - JLE, JNLE, + JO, JNO, JB, JNB, JE, JNE, JBE, JNBE, + JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, JMP, 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, - SAR, SBB, SCAS, SHL, SHR, STC, STD, STI, STOS, SUB, TEST, + SAR, SBB, SCAS, SAL, SHR, STC, STD, STI, STOS, SUB, TEST, WAIT, XCHG, XLAT, XOR, LES, LOOP, JPCX, @@ -167,6 +200,11 @@ struct Decoder { // Use the 'register' field to check for the MOV operation. MemRegMOV, + // Parse for mode and register/memory fields, populating the + // destination_ field with the result. Use the 'register' field + // to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group. + MemRegROL_to_SAR, + } modregrm_format_ = ModRegRMFormat::MemReg_Reg; // Ephemeral decoding state. From 205649cac2f77959bb771c88ae7d400555502191 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 7 Jan 2021 21:36:05 -0500 Subject: [PATCH 33/52] Decodes 8e. --- Processors/Decoders/x86/x86.cpp | 19 +++++++++++++++++-- Processors/Decoders/x86/x86.hpp | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 45f058f6f..2837cec08 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -176,7 +176,7 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { case 0x8b: MemRegReg(MOV, Reg_MemReg, 2); break; // 0x8c: not used. case 0x8d: MemRegReg(LEA, Reg_MemReg, 2); break; -// case 0x84: MemRegReg(0x8e, MOV, SegReg_MemReg, 1); // TODO: SegReg_MemReg + case 0x8e: MemRegReg(MOV, SegReg, 2); break; case 0x8f: MemRegReg(POP, MemRegPOP, 2); break; case 0x90: Complete(NOP, None, None, 0); break; // Or XCHG AX, AX? @@ -309,7 +309,6 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { /* Unimplemented (but should be): - 0x8e, 0xfe, 0xff [and consider which others are unused but seem to be @@ -411,6 +410,22 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { } break; + case ModRegRMFormat::SegReg: { + source_ = memreg; + + constexpr Source seg_table[4] = { + Source::ES, Source::CS, + Source::SS, Source::DS, + }; + + if(reg & 4) { + reset_parsing(); + return Instruction(consumed_); + } + + destination_ = seg_table[reg]; + } break; + case ModRegRMFormat::MemRegROL_to_SAR: destination_ = memreg; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index dc855ec09..bb6a7b312 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -205,6 +205,11 @@ struct Decoder { // to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group. MemRegROL_to_SAR, + // Parse for mode and register/memory fields, populating the + // source_ field with the result. Fills destination_ with a segment + // register based on the reg field. + SegReg, + } modregrm_format_ = ModRegRMFormat::MemReg_Reg; // Ephemeral decoding state. From 30c2c0f050ad083e70a7e75b521c112465c212a2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 7 Jan 2021 21:59:00 -0500 Subject: [PATCH 34/52] 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. From 306df7554e27d0a6a9d5dd52f711df785a887b40 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 8 Jan 2021 21:33:01 -0500 Subject: [PATCH 35/52] Starts trying to find a good packing for X86 instructions. To consider: do I really need `size` on every instruction? --- Processors/Decoders/x86/x86.hpp | 55 +++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 7f43b8ca9..d8891aeaf 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -91,13 +91,13 @@ enum class Size: uint8_t { enum class Source: uint8_t { None, + CS, DS, ES, SS, AL, AH, AX, BL, BH, BX, CL, CH, CX, DL, DH, DX, - CS, DS, ES, SS, SI, DI, BP, SP, @@ -121,28 +121,43 @@ enum class Repetition: uint8_t { class Instruction { public: Operation operation = Operation::Invalid; - Size operand_size = Size::Byte; - - Source source = Source::AL; - Source destination = Source::AL; - - int size() const { - return size_; - } - - bool lock() const { - return false; - } - - Instruction() {} - Instruction(int size) : size_(size) {} - Instruction(Operation operation, Size operand_size, Source source, Source destination, int size) : - operation(operation), operand_size(operand_size), source(source), destination(destination), size_(size) {} private: - int size_ = -1; + // b0, b1: a Repetition; + // b2+: size. + uint8_t repetition_size_ = 0; + + // b0–b5: source; + // b6–b11: repetition; + // b12–b14: segment override; + // b15: lock. + uint16_t sources_ = 0; + + // Unpackable fields. int16_t displacement_ = 0; - int16_t operand_ = 0; + int16_t operand_ = 0; // ... or used to store a segment for far operations. + + public: + Source source() const { return Source(sources_ & 0x3f); } + Source destination() const { return Source((sources_ >> 6) & 0x3f); } + bool lock() const { return sources_ & 0x8000; } + Source segment_override() const { return Source((sources_ >> 12) & 7); } + + Size operand_size() const { return Size::Implied; } + Size operation_size() const { return Size::Implied; } + + uint16_t segment() const { return uint16_t(operand_); } + + Repetition repetition() const { return Repetition(repetition_size_ & 3); } + int size() const { return int(repetition_size_ >> 2); } + + template type displacement(); + template type immediate(); + + + Instruction() {} + Instruction(int) {} + Instruction(Operation, Size, Source, Source, int) {} }; /*! From 86577b772b09b4f8a50256cc2de1245fd9f1e3b2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 8 Jan 2021 22:22:07 -0500 Subject: [PATCH 36/52] Rethinks `size`; packs all captured information into an x86 Instruction. Albeit that operand and displacement are't yet captured. Or extractable. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 6 +- Processors/Decoders/PowerPC/PowerPC.hpp | 8 +-- Processors/Decoders/x86/x86.cpp | 55 +++++++++++++------ Processors/Decoders/x86/x86.hpp | 54 +++++++++++------- 4 files changed, 80 insertions(+), 43 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index c1508b3e2..b8f947f95 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -50,10 +50,10 @@ namespace { instructions.clear(); const uint8_t *byte = stream.begin(); while(byte != stream.end()) { - const auto next = decoder.decode(byte, stream.end() - byte); - if(next.size() <= 0) break; + const auto [size, next] = decoder.decode(byte, stream.end() - byte); + if(size <= 0) break; instructions.push_back(next); - byte += next.size(); + byte += size; } } diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 480c89829..e5f086653 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -73,17 +73,15 @@ enum class Operation: uint8_t { Implementation note: because the PowerPC encoding is particularly straightforward, only the operation has been decoded ahead of time; all other fields are decoded on-demand. + + It would be possible to partition the ordering of Operations into user followed by supervisor, + eliminating the storage necessary for a flag, but it wouldn't save anything due to alignment. */ struct Instruction { Operation operation = Operation::Undefined; bool is_supervisor = false; uint32_t opcode = 0; - // PowerPC uses a fixed-size instruction word. - int size() const { - return 4; - } - Instruction() {} Instruction(uint32_t opcode) : opcode(opcode) {} Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {} diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 236289bf9..635523ed5 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -10,13 +10,14 @@ #include #include +#include using namespace CPU::Decoder::x86; // Only 8086 is suppoted for now. Decoder::Decoder(Model) {} -Instruction Decoder::decode(const uint8_t *source, size_t length) { +std::pair Decoder::decode(const uint8_t *source, size_t length) { const uint8_t *const end = source + length; // MARK: - Prefixes (if present) and the opcode. @@ -36,6 +37,7 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { /// Handles instructions of the form rr, kk and rr, jjkk, i.e. a destination register plus an operand. #define RegData(op, dest, size) \ SetOpSrcDestSize(op, DirectAddress, dest, size); \ + operand_size_ = size; \ phase_ = Phase::AwaitingDisplacementOrOperand /// Handles instructions of the form Ax, jjkk where the latter is implicitly an address. @@ -79,9 +81,9 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { switch(instr_) { default: { - const Instruction instruction(consumed_); + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return instruction; + return result; } #define PartialBlock(start, operation) \ @@ -390,9 +392,11 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { source_ = destination_ = memreg; switch(reg) { - default: + default: { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; + } case 0: operation_ = Operation::TEST; break; case 2: operation_ = Operation::NOT; break; @@ -413,8 +417,9 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { }; if(reg & 4) { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; } destination_ = seg_table[reg]; @@ -424,9 +429,11 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { destination_ = memreg; switch(reg) { - default: + default: { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; + } case 0: operation_ = Operation::ROL; break; case 2: operation_ = Operation::ROR; break; @@ -442,9 +449,11 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { source_ = destination_ = memreg; switch(reg) { - default: + default: { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; + } case 0: operation_ = Operation::INC; break; case 1: operation_ = Operation::DEC; break; @@ -455,9 +464,11 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { source_ = destination_ = memreg; switch(reg) { - default: + default: { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; + } case 0: operation_ = Operation::INC; break; case 1: operation_ = Operation::DEC; break; @@ -482,7 +493,7 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { if(reg != 0) { reset_parsing(); - return Instruction(consumed_); + return std::make_pair(consumed_, Instruction()); } break; @@ -513,19 +524,31 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { phase_ = Phase::ReadyToPost; } else { // Provide a genuine measure of further bytes required. - return Instruction(-(outstanding_bytes - bytes_to_consume)); + return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction()); } } // MARK: - Check for completion. if(phase_ == Phase::ReadyToPost) { - Instruction result(operation_, Size(operation_size_), source_, destination_, consumed_); + const auto result = std::make_pair( + consumed_, + Instruction( + operation_, + source_, + destination_, + lock_, + segment_override_, + repetition_, + Size(operation_size_), + 0, + 0) + ); reset_parsing(); phase_ = Phase::Instruction; return result; } // i.e. not done yet. - return Instruction(); + return std::make_pair(0, Instruction()); } diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index d8891aeaf..5efba12fd 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -124,42 +124,58 @@ class Instruction { private: // b0, b1: a Repetition; - // b2+: size. + // b2+: operation size. uint8_t repetition_size_ = 0; // b0–b5: source; - // b6–b11: repetition; + // b6–b11: destination; // b12–b14: segment override; // b15: lock. uint16_t sources_ = 0; // Unpackable fields. - int16_t displacement_ = 0; - int16_t operand_ = 0; // ... or used to store a segment for far operations. + uint16_t displacement_ = 0; + uint16_t operand_ = 0; // ... or used to store a segment for far operations. public: - Source source() const { return Source(sources_ & 0x3f); } - Source destination() const { return Source((sources_ >> 6) & 0x3f); } - bool lock() const { return sources_ & 0x8000; } - Source segment_override() const { return Source((sources_ >> 12) & 7); } - - Size operand_size() const { return Size::Implied; } - Size operation_size() const { return Size::Implied; } - - uint16_t segment() const { return uint16_t(operand_); } + Source source() const { return Source(sources_ & 0x3f); } + Source destination() const { return Source((sources_ >> 6) & 0x3f); } + bool lock() const { return sources_ & 0x8000; } + Source segment_override() const { return Source((sources_ >> 12) & 7); } Repetition repetition() const { return Repetition(repetition_size_ & 3); } - int size() const { return int(repetition_size_ >> 2); } + Size operation_size() const { return Size(repetition_size_ >> 2); } + + uint16_t segment() const { return uint16_t(operand_); } template type displacement(); template type immediate(); - Instruction() {} - Instruction(int) {} - Instruction(Operation, Size, Source, Source, int) {} + Instruction( + Operation operation, + Source source, + Source destination, + bool lock, + Source segment_override, + Repetition repetition, + Size operation_size, + uint16_t displacement, + uint16_t operand) : + operation(operation), + repetition_size_(uint8_t((int(operation_size) << 2) | int(repetition))), + sources_(uint16_t( + int(source) | + (int(destination) << 6) | + (int(segment_override) << 12) | + (int(lock) << 15) + )), + displacement_(displacement), + operand_(operand) {} }; +static_assert(sizeof(Instruction) <= 8); + /*! Implements Intel x86 instruction decoding. @@ -170,13 +186,13 @@ struct Decoder { Decoder(Model model); /*! - @returns an @c Instruction with a positive size to indicate successful decoding; a + @returns an @c Instruction plus a size; a positive size to indicate successful decoding; a negative size specifies the [negatived] number of further bytes the caller should ideally collect before calling again. The caller is free to call with fewer, but may not get a decoded instruction in response, and the decoder may still not be able to complete decoding even if given that number of bytes. */ - Instruction decode(const uint8_t *source, size_t length); + std::pair decode(const uint8_t *source, size_t length); private: enum class Phase { From 97a64db5e002f7a299ea5225859c2fe79124602f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 8 Jan 2021 22:38:56 -0500 Subject: [PATCH 37/52] Edges closer towards full x86 recording. --- Processors/Decoders/PowerPC/PowerPC.hpp | 8 +++++--- Processors/Decoders/x86/x86.cpp | 4 ++-- Processors/Decoders/x86/x86.hpp | 16 ++++++++++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index e5f086653..20b9184de 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -82,9 +82,9 @@ struct Instruction { bool is_supervisor = false; uint32_t opcode = 0; - Instruction() {} - Instruction(uint32_t opcode) : opcode(opcode) {} - Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {} + Instruction() noexcept {} + Instruction(uint32_t opcode) noexcept : opcode(opcode) {} + Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) noexcept : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {} // Instruction fields are decoded below; naming is a compromise between // Motorola's documentation and IBM's. @@ -192,6 +192,8 @@ struct Instruction { uint32_t oe() const { return opcode & 0x800; } }; +static_assert(sizeof(Instruction) <= 8); + /*! Implements PowerPC instruction decoding. diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 635523ed5..68f5a0267 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -541,8 +541,8 @@ std::pair Decoder::decode(const uint8_t *source, size_t length segment_override_, repetition_, Size(operation_size_), - 0, - 0) + displacement_, + operand_) ); reset_parsing(); phase_ = Phase::Instruction; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 5efba12fd..aaab81365 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -134,7 +134,7 @@ class Instruction { uint16_t sources_ = 0; // Unpackable fields. - uint16_t displacement_ = 0; + int16_t displacement_ = 0; uint16_t operand_ = 0; // ... or used to store a segment for far operations. public: @@ -148,10 +148,10 @@ class Instruction { uint16_t segment() const { return uint16_t(operand_); } - template type displacement(); - template type immediate(); + int16_t displacement() const { return displacement_; } + uint16_t operand() const { return operand_; } - Instruction() {} + Instruction() noexcept {} Instruction( Operation operation, Source source, @@ -160,8 +160,8 @@ class Instruction { Source segment_override, Repetition repetition, Size operation_size, - uint16_t displacement, - uint16_t operand) : + int16_t displacement, + uint16_t operand) noexcept : operation(operation), repetition_size_(uint8_t((int(operation_size) << 2) | int(repetition))), sources_(uint16_t( @@ -264,6 +264,10 @@ struct Decoder { Source source_ = Source::None; Source destination_ = Source::None; + // Immediate fields. + int16_t displacement_ = 0; + uint16_t operand_ = 0; + // Facts about the instruction. int displacement_size_ = 0; // i.e. size of in-stream displacement, if any. int operand_size_ = 0; // i.e. size of in-stream operand, if any. From 68fe16a0925eae2858ed52ba1ce2fa8ac3f09f49 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 8 Jan 2021 22:45:27 -0500 Subject: [PATCH 38/52] Marks intent for operand/displacement. --- Processors/Decoders/x86/x86.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 68f5a0267..7a8e38de2 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -254,7 +254,7 @@ std::pair Decoder::decode(const uint8_t *source, size_t length modregrm_format_ = ModRegRMFormat::MemRegROL_to_SAR; operation_size_ = 1 + (instr_ & 1); source_ = Source::Immediate; - // TODO: set operand to 1. + operand_ = 1; break; case 0xd2: case 0xd3: phase_ = Phase::ModRegRM; @@ -512,19 +512,26 @@ std::pair Decoder::decode(const uint8_t *source, size_t length // MARK: - Displacement and operand. if(phase_ == Phase::AwaitingDisplacementOrOperand && source != end) { - // TODO: calculate number of expected operands. const int required_bytes = displacement_size_ + operand_size_; - const int outstanding_bytes = required_bytes - operand_bytes_; - const int bytes_to_consume = std::min(int(end - source), outstanding_bytes); - source += bytes_to_consume; - consumed_ += bytes_to_consume; - operand_bytes_ += bytes_to_consume; - if(bytes_to_consume == outstanding_bytes) { + if(!required_bytes) { phase_ = Phase::ReadyToPost; } else { - // Provide a genuine measure of further bytes required. - return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction()); + const int outstanding_bytes = required_bytes - operand_bytes_; + const int bytes_to_consume = std::min(int(end - source), outstanding_bytes); + + // TODO: fill displacement_ and operand_ here... efficiently? + + source += bytes_to_consume; + consumed_ += bytes_to_consume; + operand_bytes_ += bytes_to_consume; + + if(bytes_to_consume == outstanding_bytes) { + phase_ = Phase::ReadyToPost; + } else { + // Provide a genuine measure of further bytes required. + return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction()); + } } } From 718f950071c28d50cb90846ee5c04baf588b08af Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 8 Jan 2021 22:50:59 -0500 Subject: [PATCH 39/52] Implements 80 and 81. --- Processors/Decoders/x86/x86.cpp | 23 +++++++++++++++++++++-- Processors/Decoders/x86/x86.hpp | 6 ++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 7a8e38de2..ae890a476 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -165,8 +165,11 @@ std::pair Decoder::decode(const uint8_t *source, size_t length // TODO: // - // 0x80, 0x81, 0x82, 0x83, which all require more - // input, from the ModRegRM byte. + // 0x82, 0x83, which will require me to do something + // re: word-sign extended. + + case 0x80: MemRegReg(Invalid, MemRegADD_to_CMP, 1); break; + case 0x81: MemRegReg(Invalid, MemRegADD_to_CMP, 2); break; case 0x84: MemRegReg(TEST, MemReg_Reg, 1); break; case 0x85: MemRegReg(TEST, MemReg_Reg, 2); break; @@ -503,6 +506,22 @@ std::pair Decoder::decode(const uint8_t *source, size_t length operand_size_ = operation_size_; break; + case ModRegRMFormat::MemRegADD_to_CMP: + destination_ = memreg; + operand_size_ = operation_size_; + + switch(reg) { + default: operation_ = Operation::ADD; break; + case 1: operation_ = Operation::OR; break; + case 2: operation_ = Operation::ADC; break; + case 3: operation_ = Operation::SBB; break; + case 4: operation_ = Operation::AND; break; + case 5: operation_ = Operation::SUB; break; + case 6: operation_ = Operation::XOR; break; + case 7: operation_ = Operation::CMP; break; + } + break; + default: assert(false); } diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index aaab81365..d39da8f88 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -238,6 +238,12 @@ struct Decoder { // to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group. MemRegROL_to_SAR, + // Parse for mode and register/memory fields, populating the + // destination_ field with the result. Use the 'register' field + // to pick an operation from the ADD/OR/ADC/SBB/AND/SUB/XOR/CMP group and + // waits for an operand equal to the operation size. + MemRegADD_to_CMP, + // Parse for mode and register/memory fields, populating the // source_ field with the result. Fills destination_ with a segment // register based on the reg field. From 5f807b6e4732ac1c5ff9d2ab14614b570d5eb21d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 8 Jan 2021 23:02:06 -0500 Subject: [PATCH 40/52] Ensures that the operand is the only thing failing in decoding of the first instruction. --- OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm | 12 +++++++++++- Processors/Decoders/x86/x86.cpp | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index b8f947f95..25688c417 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -15,6 +15,8 @@ namespace { using Operation = CPU::Decoder::x86::Operation; using Instruction = CPU::Decoder::x86::Instruction; + using Source = CPU::Decoder::x86::Source; + using Size = CPU::Decoder::x86::Size; } @interface x86DecoderTests : XCTestCase @@ -31,7 +33,13 @@ namespace { // MARK: - Specific instruction asserts. -/* ... TODO ... */ +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size operand:(uint16_t)operand destination:(Source)destination { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); + XCTAssertEqual(instruction.destination(), destination); + XCTAssertEqual(instruction.source(), Source::Immediate); + XCTAssertEqual(instruction.operand(), operand); +} // MARK: - Decoder @@ -82,6 +90,8 @@ namespace { // 68 instructions are expected. XCTAssertEqual(instructions.size(), 63); + [self assert:instructions[0] operation:Operation::SUB size:2 operand:0xea77 destination:Source::AX]; + // sub $0xea77,%ax // jb 0x00000001 // dec %bx diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index ae890a476..d4814f388 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -37,6 +37,7 @@ std::pair Decoder::decode(const uint8_t *source, size_t length /// Handles instructions of the form rr, kk and rr, jjkk, i.e. a destination register plus an operand. #define RegData(op, dest, size) \ SetOpSrcDestSize(op, DirectAddress, dest, size); \ + source_ = Source::Immediate; \ operand_size_ = size; \ phase_ = Phase::AwaitingDisplacementOrOperand From 9ba5b7c1d4389426f77f359356c959f3f3f9a2b6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 8 Jan 2021 23:21:01 -0500 Subject: [PATCH 41/52] Adds a few more asserts. It's still just operands and displacements failing, which is nice. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 25688c417..310d4723f 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -33,6 +33,13 @@ namespace { // MARK: - Specific instruction asserts. +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); + XCTAssertEqual(instruction.source(), source); + XCTAssertEqual(instruction.destination(), destination); +} + - (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size operand:(uint16_t)operand destination:(Source)destination { XCTAssertEqual(instruction.operation, operation); XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); @@ -41,6 +48,12 @@ namespace { XCTAssertEqual(instruction.operand(), operand); } +- (void)assert:(Instruction &)instruction operation:(Operation)operation displacement:(int16_t)displacement { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.displacement(), displacement); +} + + // MARK: - Decoder - (void)decode:(const std::initializer_list &)stream { @@ -90,12 +103,15 @@ namespace { // 68 instructions are expected. XCTAssertEqual(instructions.size(), 63); - [self assert:instructions[0] operation:Operation::SUB size:2 operand:0xea77 destination:Source::AX]; - // sub $0xea77,%ax // jb 0x00000001 // dec %bx // mov $0x28,%ch + [self assert:instructions[0] operation:Operation::SUB size:2 operand:0xea77 destination:Source::AX]; + [self assert:instructions[1] operation:Operation::JB displacement:0x01]; + [self assert:instructions[2] operation:Operation::DEC size:2 source:Source::BX destination:Source::BX]; + [self assert:instructions[3] operation:Operation::MOV size:1 operand:0x28 destination:Source::CH]; + // ret // lret $0x4826 // gs insw (%dx),%es:(%di) From 762ecab3aaeb1f1d9703ccbca41575f02b4f025f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 10 Jan 2021 22:55:25 -0500 Subject: [PATCH 42/52] Adds operand/displacement capture. This gets unit test as far as a disagreement over how to handle bad 0xc4 suffixes. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 24 +++++++++++++++++-- Processors/Decoders/x86/x86.cpp | 22 +++++++++++++---- Processors/Decoders/x86/x86.hpp | 1 + 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 310d4723f..6d4321818 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -33,6 +33,10 @@ namespace { // MARK: - Specific instruction asserts. +- (void)assert:(Instruction &)instruction operation:(Operation)operation { + XCTAssertEqual(instruction.operation, operation); +} + - (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination { XCTAssertEqual(instruction.operation, operation); XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); @@ -53,6 +57,10 @@ namespace { XCTAssertEqual(instruction.displacement(), displacement); } +- (void)assert:(Instruction &)instruction operation:(Operation)operation operand:(uint16_t)operand { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operand(), operand); +} // MARK: - Decoder @@ -108,23 +116,35 @@ namespace { // dec %bx // mov $0x28,%ch [self assert:instructions[0] operation:Operation::SUB size:2 operand:0xea77 destination:Source::AX]; - [self assert:instructions[1] operation:Operation::JB displacement:0x01]; + [self assert:instructions[1] operation:Operation::JB displacement:0xfffc]; [self assert:instructions[2] operation:Operation::DEC size:2 source:Source::BX destination:Source::BX]; [self assert:instructions[3] operation:Operation::MOV size:1 operand:0x28 destination:Source::CH]; // ret // lret $0x4826 - // gs insw (%dx),%es:(%di) + // [[ gs insw (%dx),%es:(%di) ]] // jnp 0xffffffaf // ret $0x4265 + [self assert:instructions[4] operation:Operation::RETN]; + [self assert:instructions[5] operation:Operation::RETF operand:0x4826]; + [self assert:instructions[6] operation:Operation::JNP displacement:0xff9f]; + [self assert:instructions[7] operation:Operation::RETN operand:0x4265]; + // dec %si // out %ax,(%dx) // jo 0x00000037 // xchg %ax,%sp + [self assert:instructions[8] operation:Operation::DEC size:2 source:Source::SI destination:Source::SI]; + [self assert:instructions[9] operation:Operation::OUT size:2 source:Source::AX destination:Source::DX]; + [self assert:instructions[10] operation:Operation::JO displacement:0x20]; + [self assert:instructions[11] operation:Operation::XCHG size:2 source:Source::AX destination:Source::SP]; + // (bad) // aam $0x93 // inc %bx // cmp $0x8e,%al +// [self assert:instructions[12] operation:Operation::Invalid]; + // push $0x65 // sbb 0x45(%bx,%si),%bh // adc %bh,0x3c(%bx) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index d4814f388..0b63a6399 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -65,7 +65,7 @@ std::pair Decoder::decode(const uint8_t *source, size_t length #define Jump(op) \ operation_ = Operation::op; \ phase_ = Phase::AwaitingDisplacementOrOperand; \ - operand_size_ = 1 + displacement_size_ = 1 /// Handles far CALL and far JMP — fixed four byte operand operations. #define Far(op) \ @@ -298,7 +298,7 @@ std::pair Decoder::decode(const uint8_t *source, size_t length case 0xec: Complete(IN, DX, AL, 1); break; case 0xed: Complete(IN, DX, AX, 1); break; case 0xee: Complete(OUT, AL, DX, 1); break; - case 0xef: Complete(OUT, AX, DX, 1); break; + case 0xef: Complete(OUT, AX, DX, 2); break; case 0xf4: Complete(HLT, None, None, 1); break; case 0xf5: Complete(CMC, None, None, 1); break; @@ -540,14 +540,28 @@ std::pair Decoder::decode(const uint8_t *source, size_t length const int outstanding_bytes = required_bytes - operand_bytes_; const int bytes_to_consume = std::min(int(end - source), outstanding_bytes); - // TODO: fill displacement_ and operand_ here... efficiently? + // TODO: I can surely do better than this? + for(int c = 0; c < bytes_to_consume; c++) { + inward_data_ = (inward_data_ >> 8) | (uint64_t(source[0]) << 56); + ++source; + } - source += bytes_to_consume; consumed_ += bytes_to_consume; operand_bytes_ += bytes_to_consume; if(bytes_to_consume == outstanding_bytes) { phase_ = Phase::ReadyToPost; + + switch(operand_size_) { + default: operand_ = 0; break; + case 1: operand_ = inward_data_ >> 56; inward_data_ <<= 8; break; + case 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; break; + } + switch(displacement_size_) { + default: displacement_ = 0; break; + case 1: displacement_ = int8_t(inward_data_ >> 56); break; + case 2: displacement_ = int16_t(inward_data_ >> 48); break; + } } else { // Provide a genuine measure of further bytes required. return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction()); diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index d39da8f88..0c7894bfe 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -273,6 +273,7 @@ struct Decoder { // Immediate fields. int16_t displacement_ = 0; uint16_t operand_ = 0; + uint64_t inward_data_ = 0; // Facts about the instruction. int displacement_size_ = 0; // i.e. size of in-stream displacement, if any. From 5058a8b96a3c88d4407e43843a8bd8b792d41ab2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 12 Jan 2021 21:49:22 -0500 Subject: [PATCH 43/52] Completes the first test stream. ... and improves decoding consistency in conjunction. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 123 ++++++++++++++++-- Processors/Decoders/x86/x86.cpp | 9 +- Processors/Decoders/x86/x86.hpp | 2 + 3 files changed, 123 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 6d4321818..f2a5711b7 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -37,11 +37,42 @@ namespace { XCTAssertEqual(instruction.operation, operation); } -- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination { +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination displacement:(int16_t)displacement { XCTAssertEqual(instruction.operation, operation); XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); XCTAssertEqual(instruction.source(), source); XCTAssertEqual(instruction.destination(), destination); + XCTAssertEqual(instruction.displacement(), displacement); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination displacement:(int16_t)displacement operand:(uint16_t)operand { + [self assert:instruction operation:operation size:size source:source destination:destination displacement:displacement]; + XCTAssertEqual(instruction.operand(), operand); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination operand:(uint16_t)operand { + [self assert:instruction operation:operation size:size source:source destination:destination displacement:0 operand:operand]; +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination { + [self assert:instruction operation:operation size:size source:source destination:destination displacement:0]; +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); + XCTAssertEqual(instruction.source(), source); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size destination:(Source)destination { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); + XCTAssertEqual(instruction.destination(), destination); } - (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size operand:(uint16_t)operand destination:(Source)destination { @@ -50,6 +81,7 @@ namespace { XCTAssertEqual(instruction.destination(), destination); XCTAssertEqual(instruction.source(), Source::Immediate); XCTAssertEqual(instruction.operand(), operand); + XCTAssertEqual(instruction.displacement(), 0); } - (void)assert:(Instruction &)instruction operation:(Operation)operation displacement:(int16_t)displacement { @@ -60,6 +92,7 @@ namespace { - (void)assert:(Instruction &)instruction operation:(Operation)operation operand:(uint16_t)operand { XCTAssertEqual(instruction.operation, operation); XCTAssertEqual(instruction.operand(), operand); + XCTAssertEqual(instruction.displacement(), 0); } // MARK: - Decoder @@ -81,6 +114,7 @@ namespace { while(byte != stream.end()) { const auto [size, next] = decoder.decode(byte, stream.end() - byte); if(size <= 0) break; + NSLog(@"%@ %@", @(instructions.size()), @(size)); instructions.push_back(next); byte += size; } @@ -122,7 +156,7 @@ namespace { // ret // lret $0x4826 - // [[ gs insw (%dx),%es:(%di) ]] + // [[ omitted: gs insw (%dx),%es:(%di) ]] // jnp 0xffffffaf // ret $0x4265 [self assert:instructions[4] operation:Operation::RETN]; @@ -139,63 +173,132 @@ namespace { [self assert:instructions[10] operation:Operation::JO displacement:0x20]; [self assert:instructions[11] operation:Operation::XCHG size:2 source:Source::AX destination:Source::SP]; - // (bad) - // aam $0x93 + // ODA has: + // c4 (bad) + // d4 93 aam $0x93 + // + // That assumes that upon discovering that the d4 doesn't make a valid LES, + // it can become an instruction byte. I'm not persuaded. So I'm taking: + // + // c4 d4 (bad) + // 93 XCHG AX, BX + [self assert:instructions[12] operation:Operation::Invalid]; + [self assert:instructions[13] operation:Operation::XCHG size:2 source:Source::AX destination:Source::BX]; + // inc %bx // cmp $0x8e,%al -// [self assert:instructions[12] operation:Operation::Invalid]; - - // push $0x65 + // [[ omitted: push $0x65 ]] // sbb 0x45(%bx,%si),%bh // adc %bh,0x3c(%bx) + [self assert:instructions[14] operation:Operation::INC size:2 source:Source::BX destination:Source::BX]; + [self assert:instructions[15] operation:Operation::CMP size:1 operand:0x8e destination:Source::AL]; + [self assert:instructions[16] operation:Operation::SBB size:1 source:Source::IndBXPlusSI destination:Source::BH displacement:0x45]; + [self assert:instructions[17] operation:Operation::ADC size:1 source:Source::BH destination:Source::IndBX displacement:0x3c]; + // sbb %bx,0x16(%bp,%si) // xor %sp,0x2c(%si) // out %ax,$0xc6 // jge 0xffffffe0 + [self assert:instructions[18] operation:Operation::SBB size:2 source:Source::BX destination:Source::IndBPPlusSI displacement:0x16]; + [self assert:instructions[19] operation:Operation::XOR size:2 source:Source::SP destination:Source::IndSI displacement:0x2c]; + [self assert:instructions[20] operation:Operation::OUT size:2 source:Source::AX destination:Source::DirectAddress operand:0xc6]; + [self assert:instructions[21] operation:Operation::JNL displacement:0xffb0]; + // mov $0x49,%ch - // addr32 popa + // [[ omitted: addr32 popa ]] // mov $0xcbc0,%dx // adc $0x7e,%al // jno 0x0000000b + [self assert:instructions[22] operation:Operation::MOV size:1 operand:0x49 destination:Source::CH]; + [self assert:instructions[23] operation:Operation::MOV size:2 operand:0xcbc0 destination:Source::DX]; + [self assert:instructions[24] operation:Operation::ADC size:1 operand:0x7e destination:Source::AL]; + [self assert:instructions[25] operation:Operation::JNO displacement:0xffd0]; + // push %ax // js 0x0000007b // add (%di),%bx // in $0xc9,%ax + [self assert:instructions[26] operation:Operation::PUSH size:2 source:Source::AX]; + [self assert:instructions[27] operation:Operation::JS displacement:0x3d]; + [self assert:instructions[28] operation:Operation::ADD size:2 source:Source::IndDI destination:Source::BX]; + [self assert:instructions[29] operation:Operation::IN size:2 source:Source::DirectAddress destination:Source::AX operand:0xc9]; + // xchg %ax,%di // ret // fwait // out %al,$0xd3 - // insb (%dx),%es:(%di) + [self assert:instructions[30] operation:Operation::XCHG size:2 source:Source::AX destination:Source::DI]; + [self assert:instructions[31] operation:Operation::RETN]; + [self assert:instructions[32] operation:Operation::WAIT]; + [self assert:instructions[33] operation:Operation::OUT size:1 source:Source::AL destination:Source::DirectAddress operand:0xd3]; + + // [[ omitted: insb (%dx),%es:(%di) ]] // pop %ax // dec %bp // jbe 0xffffffcc // inc %sp + [self assert:instructions[34] operation:Operation::POP size:2 destination:Source::AX]; + [self assert:instructions[35] operation:Operation::DEC size:2 source:Source::BP destination:Source::BP]; + [self assert:instructions[36] operation:Operation::JBE displacement:0xff80]; + [self assert:instructions[37] operation:Operation::INC size:2 source:Source::SP destination:Source::SP]; + // (bad) // lahf // movsw %ds:(%si),%es:(%di) // mov $0x12a1,%bp + [self assert:instructions[38] operation:Operation::Invalid]; + [self assert:instructions[39] operation:Operation::LAHF]; + [self assert:instructions[40] operation:Operation::MOVS size:2]; + [self assert:instructions[41] operation:Operation::MOV size:2 operand:0x12a1 destination:Source::BP]; + // lds (%bx,%di),%bp - // leave + // [[ omitted: leave ]] // sahf // fdiv %st(3),%st // iret + [self assert:instructions[42] operation:Operation::LDS size:4]; + [self assert:instructions[43] operation:Operation::SAHF]; + [self assert:instructions[44] operation:Operation::ESC]; + [self assert:instructions[45] operation:Operation::IRET]; + // xchg %ax,%dx // cmp %bx,-0x70(%di) // adc $0xb8c3,%ax // lods %ds:(%si),%ax + [self assert:instructions[46] operation:Operation::XCHG size:2 source:Source::AX destination:Source::DX]; + [self assert:instructions[47] operation:Operation::CMP size:2 source:Source::BX destination:Source::IndDI displacement:0xff90]; + [self assert:instructions[48] operation:Operation::ADC size:2 operand:0xb8c3 destination:Source::AX]; + [self assert:instructions[49] operation:Operation::LODS size:2]; + // call 0x0000172d // dec %dx // mov $0x9e,%al // stc + [self assert:instructions[50] operation:Operation::CALLD operand:0x16c8]; + [self assert:instructions[51] operation:Operation::DEC size:2 source:Source::DX destination:Source::DX]; + [self assert:instructions[52] operation:Operation::MOV size:1 operand:0x9e destination:Source::AL]; + [self assert:instructions[53] operation:Operation::STC]; + // mov $0xea56,%di // dec %si // std // in $0x5a,%al + [self assert:instructions[54] operation:Operation::MOV size:2 operand:0xea56 destination:Source::DI]; + [self assert:instructions[55] operation:Operation::DEC size:2 source:Source::SI destination:Source::SI]; + [self assert:instructions[56] operation:Operation::STD]; + [self assert:instructions[57] operation:Operation::IN size:1 source:Source::DirectAddress destination:Source::AL operand:0x5a]; + // and 0x5b2c(%bp,%si),%bp // sub %dl,%dl // negw 0x18(%bx) // xchg %dl,0x6425(%bx,%si) + [self assert:instructions[58] operation:Operation::AND size:2 source:Source::IndBPPlusSI destination:Source::BP displacement:0x5b2c]; + [self assert:instructions[59] operation:Operation::SUB size:1 source:Source::DL destination:Source::DL]; + [self assert:instructions[60] operation:Operation::NEG size:2 source:Source::IndBX destination:Source::IndBX displacement:0x18]; + [self assert:instructions[61] operation:Operation::XCHG size:1 source:Source::IndBXPlusSI destination:Source::DL displacement:0x6425]; + // mov $0xc3,%bh + [self assert:instructions[62] operation:Operation::MOV size:1 operand:0xc3 destination:Source::BH]; } @end diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 0b63a6399..73b26c5d4 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -51,6 +51,7 @@ std::pair Decoder::decode(const uint8_t *source, size_t length #define AddrReg(op, source, op_size, addr_size) \ SetOpSrcDestSize(op, source, DirectAddress, op_size); \ operand_size_ = addr_size; \ + destination_ = Source::DirectAddress; \ phase_ = Phase::AwaitingDisplacementOrOperand /// Covers both `mem/reg, reg` and `reg, mem/reg`. @@ -377,6 +378,13 @@ std::pair Decoder::decode(const uint8_t *source, size_t length // Other operand is just a register. case 3: memreg = reg_table[operation_size_][rm]; + + // LES and LDS accept a real memory argument only. + if(operation_ == Operation::LES || operation_ == Operation::LDS) { + const auto result = std::make_pair(consumed_, Instruction()); + reset_parsing(); + return result; + } break; } @@ -586,7 +594,6 @@ std::pair Decoder::decode(const uint8_t *source, size_t length operand_) ); reset_parsing(); - phase_ = Phase::Instruction; return result; } diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 0c7894bfe..3e0d9c79d 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -289,9 +289,11 @@ struct Decoder { void reset_parsing() { consumed_ = operand_bytes_ = 0; displacement_size_ = operand_size_ = 0; + displacement_ = operand_ = 0; lock_ = false; segment_override_ = Source::None; repetition_ = Repetition::None; + phase_ = Phase::Instruction; } }; From a24ae727a733572bb5536e76049d72bf44dd3b86 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 13 Jan 2021 20:29:44 -0500 Subject: [PATCH 44/52] Takes a run at 0x82 and 0x83, completing the set. --- Processors/Decoders/x86/x86.cpp | 37 +++++++++++++++++++++++++++------ Processors/Decoders/x86/x86.hpp | 6 ++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 73b26c5d4..576a14487 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -165,13 +165,10 @@ std::pair Decoder::decode(const uint8_t *source, size_t length case 0x7e: Jump(JLE); break; case 0x7f: Jump(JNLE); break; - // TODO: - // - // 0x82, 0x83, which will require me to do something - // re: word-sign extended. - case 0x80: MemRegReg(Invalid, MemRegADD_to_CMP, 1); break; case 0x81: MemRegReg(Invalid, MemRegADD_to_CMP, 2); break; + case 0x82: MemRegReg(Invalid, MemRegADC_to_CMP, 1); break; + case 0x83: MemRegReg(Invalid, MemRegADC_to_CMP, 2); break; case 0x84: MemRegReg(TEST, MemReg_Reg, 1); break; case 0x85: MemRegReg(TEST, MemReg_Reg, 2); break; @@ -531,6 +528,27 @@ std::pair Decoder::decode(const uint8_t *source, size_t length } break; + case ModRegRMFormat::MemRegADC_to_CMP: + destination_ = memreg; + source_ = Source::Immediate; + operand_size_ = 1; // ... and always 1; it'll be sign extended if + // the operation requires it. + + switch(reg) { + default: { + const auto result = std::make_pair(consumed_, Instruction()); + reset_parsing(); + return result; + } + + case 0: operation_ = Operation::ADD; break; + case 2: operation_ = Operation::ADC; break; + case 3: operation_ = Operation::SBB; break; + case 5: operation_ = Operation::SUB; break; + case 7: operation_ = Operation::CMP; break; + } + break; + default: assert(false); } @@ -562,7 +580,14 @@ std::pair Decoder::decode(const uint8_t *source, size_t length switch(operand_size_) { default: operand_ = 0; break; - case 1: operand_ = inward_data_ >> 56; inward_data_ <<= 8; break; + case 1: + operand_ = inward_data_ >> 56; inward_data_ <<= 8; + + // Sign extend if a single byte operand is feeding a two-byte instruction. + if(operation_size_ == 2) { + operand_ |= (operand_ & 0x80) ? 0xff : 0x00; + } + break; case 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; break; } switch(displacement_size_) { diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 3e0d9c79d..17557ff4e 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -259,6 +259,12 @@ struct Decoder { // '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, + + // Parse for mode and register/memory fields, populating the + // source_ and destination_ fields with the result. Uses the + // 'register' field to pick from ADD/ADC/SBB/SUB/CMP, altering + // the source to ::Immediate and setting an appropriate operand size. + MemRegADC_to_CMP, } modregrm_format_ = ModRegRMFormat::MemReg_Reg; // Ephemeral decoding state. From 8c0e06e645f9237ba24a5403fb25593a97047cbe Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 13 Jan 2021 20:42:21 -0500 Subject: [PATCH 45/52] Adds a test for 0x83 and fixes sign extension. ODA doesn't seem to accept 0x82, but testing 0x83 adds some confidence. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 17 ++++++++++++++++- Processors/Decoders/x86/x86.cpp | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index f2a5711b7..eec2c5512 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -142,7 +142,7 @@ namespace { 0x23, 0xaa, 0x2c, 0x5b, 0x2a, 0xd2, 0xf7, 0x5f, 0x18, 0x86, 0x90, 0x25, 0x64, 0xb7, 0xc3 }]; - // 68 instructions are expected. + // 63 instructions are expected. XCTAssertEqual(instructions.size(), 63); // sub $0xea77,%ax @@ -301,4 +301,19 @@ namespace { [self assert:instructions[62] operation:Operation::MOV size:1 operand:0xc3 destination:Source::BH]; } +- (void)test83 { + [self decode:{ + 0x83, 0x10, 0x80, // adcw $0xff80,(%bx,%si) + 0x83, 0x3b, 0x04, // cmpw $0x4,(%bp,%di) + 0x83, 0x2f, 0x09, // subw $0x9,(%bx) + }]; + + // 68 instructions are expected. + XCTAssertEqual(instructions.size(), 3); + + [self assert:instructions[0] operation:Operation::ADC size:2 source:Source::Immediate destination:Source::IndBXPlusSI operand:0xff80]; + [self assert:instructions[1] operation:Operation::CMP size:2 source:Source::Immediate destination:Source::IndBPPlusDI operand:0x4]; + [self assert:instructions[2] operation:Operation::SUB size:2 source:Source::Immediate destination:Source::IndBX operand:0x9]; +} + @end diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 576a14487..25f05a7cd 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -585,7 +585,7 @@ std::pair Decoder::decode(const uint8_t *source, size_t length // Sign extend if a single byte operand is feeding a two-byte instruction. if(operation_size_ == 2) { - operand_ |= (operand_ & 0x80) ? 0xff : 0x00; + operand_ |= (operand_ & 0x80) ? 0xff00 : 0x0000; } break; case 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; break; From 2c72a77a25d3a865d7aae6e8b53c898966dd4831 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 13 Jan 2021 21:51:18 -0500 Subject: [PATCH 46/52] Adds byte-by-byte decoder test; corrects divergences. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 30 ++++---- Processors/Decoders/x86/x86.cpp | 76 +++++++++---------- Processors/Decoders/x86/x86.hpp | 9 +++ 3 files changed, 62 insertions(+), 53 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index eec2c5512..084f6bde2 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -23,7 +23,7 @@ namespace { @end /*! - Tests PowerPC decoding by throwing a bunch of randomly-generated + Tests 8086 decoding by throwing a bunch of randomly-generated word streams and checking that the result matches what I got from a disassembler elsewhere. */ @@ -98,26 +98,30 @@ namespace { // MARK: - Decoder - (void)decode:(const std::initializer_list &)stream { + // Decode by offering up all data at once. CPU::Decoder::x86::Decoder decoder(CPU::Decoder::x86::Model::i8086); - - // TODO: test that byte-at-a-time decoding gives the same results, as a freebie. -// instructions.clear(); -// for(auto item: stream) { -// const auto next = decoder.decode(&item, 1); -// if(next.size() > 0) { -// instructions.push_back(next); -// } -// } - instructions.clear(); const uint8_t *byte = stream.begin(); while(byte != stream.end()) { const auto [size, next] = decoder.decode(byte, stream.end() - byte); if(size <= 0) break; - NSLog(@"%@ %@", @(instructions.size()), @(size)); instructions.push_back(next); byte += size; } + + // Grab a byte-at-a-time decoding and check that it matches the previous. + { + CPU::Decoder::x86::Decoder decoder(CPU::Decoder::x86::Model::i8086); + + auto previous_instruction = instructions.begin(); + for(auto item: stream) { + const auto [size, next] = decoder.decode(&item, 1); + if(size > 0) { + XCTAssert(next == *previous_instruction); + ++previous_instruction; + } + } + } } // MARK: - Tests @@ -256,7 +260,7 @@ namespace { // sahf // fdiv %st(3),%st // iret - [self assert:instructions[42] operation:Operation::LDS size:4]; + [self assert:instructions[42] operation:Operation::LDS size:2]; [self assert:instructions[43] operation:Operation::SAHF]; [self assert:instructions[44] operation:Operation::ESC]; [self assert:instructions[45] operation:Operation::IRET]; diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 25f05a7cd..e414a0a40 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -238,8 +238,8 @@ std::pair Decoder::decode(const uint8_t *source, size_t length case 0xc2: RegData(RETN, None, 2); break; case 0xc3: Complete(RETN, None, None, 2); break; - case 0xc4: MemRegReg(LES, Reg_MemReg, 4); break; - case 0xc5: MemRegReg(LDS, Reg_MemReg, 4); break; + case 0xc4: MemRegReg(LES, Reg_MemReg, 2); break; + case 0xc5: MemRegReg(LDS, Reg_MemReg, 2); break; case 0xc6: MemRegReg(MOV, MemRegMOV, 1); break; case 0xc7: MemRegReg(MOV, MemRegMOV, 2); break; @@ -552,7 +552,7 @@ std::pair Decoder::decode(const uint8_t *source, size_t length default: assert(false); } - phase_ = Phase::AwaitingDisplacementOrOperand; + phase_ = (displacement_size_ + operand_size_) ? Phase::AwaitingDisplacementOrOperand : Phase::ReadyToPost; } // MARK: - Displacement and operand. @@ -560,45 +560,41 @@ std::pair Decoder::decode(const uint8_t *source, size_t length if(phase_ == Phase::AwaitingDisplacementOrOperand && source != end) { const int required_bytes = displacement_size_ + operand_size_; - if(!required_bytes) { + const int outstanding_bytes = required_bytes - operand_bytes_; + const int bytes_to_consume = std::min(int(end - source), outstanding_bytes); + + // TODO: I can surely do better than this? + for(int c = 0; c < bytes_to_consume; c++) { + inward_data_ = (inward_data_ >> 8) | (uint64_t(source[0]) << 56); + ++source; + } + + consumed_ += bytes_to_consume; + operand_bytes_ += bytes_to_consume; + + if(bytes_to_consume == outstanding_bytes) { phase_ = Phase::ReadyToPost; + + switch(operand_size_) { + default: operand_ = 0; break; + case 1: + operand_ = inward_data_ >> 56; inward_data_ <<= 8; + + // Sign extend if a single byte operand is feeding a two-byte instruction. + if(operation_size_ == 2 && operation_ != Operation::IN && operation_ != Operation::OUT) { + operand_ |= (operand_ & 0x80) ? 0xff00 : 0x0000; + } + break; + case 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; break; + } + switch(displacement_size_) { + default: displacement_ = 0; break; + case 1: displacement_ = int8_t(inward_data_ >> 56); break; + case 2: displacement_ = int16_t(inward_data_ >> 48); break; + } } else { - const int outstanding_bytes = required_bytes - operand_bytes_; - const int bytes_to_consume = std::min(int(end - source), outstanding_bytes); - - // TODO: I can surely do better than this? - for(int c = 0; c < bytes_to_consume; c++) { - inward_data_ = (inward_data_ >> 8) | (uint64_t(source[0]) << 56); - ++source; - } - - consumed_ += bytes_to_consume; - operand_bytes_ += bytes_to_consume; - - if(bytes_to_consume == outstanding_bytes) { - phase_ = Phase::ReadyToPost; - - switch(operand_size_) { - default: operand_ = 0; break; - case 1: - operand_ = inward_data_ >> 56; inward_data_ <<= 8; - - // Sign extend if a single byte operand is feeding a two-byte instruction. - if(operation_size_ == 2) { - operand_ |= (operand_ & 0x80) ? 0xff00 : 0x0000; - } - break; - case 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; break; - } - switch(displacement_size_) { - default: displacement_ = 0; break; - case 1: displacement_ = int8_t(inward_data_ >> 56); break; - case 2: displacement_ = int16_t(inward_data_ >> 48); break; - } - } else { - // Provide a genuine measure of further bytes required. - return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction()); - } + // Provide a genuine measure of further bytes required. + return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction()); } } diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 17557ff4e..2929aed44 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -122,6 +122,14 @@ class Instruction { public: Operation operation = Operation::Invalid; + bool operator ==(const Instruction &rhs) const { + return + repetition_size_ == rhs.repetition_size_ && + sources_ == rhs.sources_ && + displacement_ == rhs.displacement_ && + operand_ == rhs.operand_; + } + private: // b0, b1: a Repetition; // b2+: operation size. @@ -300,6 +308,7 @@ struct Decoder { segment_override_ = Source::None; repetition_ = Repetition::None; phase_ = Phase::Instruction; + source_ = destination_ = Source::None; } }; From ca94e9038eb2622f5adab76a54b989b6caac76b9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 14 Jan 2021 22:15:38 -0500 Subject: [PATCH 47/52] Introduces 'far' test, fixes parsing. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 17 +++++++++++++++-- Processors/Decoders/x86/x86.cpp | 2 ++ Processors/Decoders/x86/x86.hpp | 4 +++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 084f6bde2..8cfc6c3b8 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -95,6 +95,12 @@ namespace { XCTAssertEqual(instruction.displacement(), 0); } +- (void)assert:(Instruction &)instruction operation:(Operation)operation segment:(uint16_t)segment offset:(uint16_t)offset { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.segment(), segment); + XCTAssertEqual(instruction.offset(), offset); +} + // MARK: - Decoder - (void)decode:(const std::initializer_list &)stream { @@ -312,12 +318,19 @@ namespace { 0x83, 0x2f, 0x09, // subw $0x9,(%bx) }]; - // 68 instructions are expected. XCTAssertEqual(instructions.size(), 3); - [self assert:instructions[0] operation:Operation::ADC size:2 source:Source::Immediate destination:Source::IndBXPlusSI operand:0xff80]; [self assert:instructions[1] operation:Operation::CMP size:2 source:Source::Immediate destination:Source::IndBPPlusDI operand:0x4]; [self assert:instructions[2] operation:Operation::SUB size:2 source:Source::Immediate destination:Source::IndBX operand:0x9]; } +- (void)testFar { + [self decode:{ + 0x9a, 0x12, 0x34, 0x56, 0x78, // lcall 0x7856, 0x3412 + }]; + + XCTAssertEqual(instructions.size(), 1); + [self assert:instructions[0] operation:Operation::CALLF segment:0x7856 offset:0x3412]; +} + @end diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index e414a0a40..3dfae8f89 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -585,7 +585,9 @@ std::pair Decoder::decode(const uint8_t *source, size_t length operand_ |= (operand_ & 0x80) ? 0xff00 : 0x0000; } break; + case 4: displacement_size_ = 2; [[fallthrough]]; case 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; break; + break; } switch(displacement_size_) { default: displacement_ = 0; break; diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 2929aed44..841da5360 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -37,10 +37,11 @@ enum class Operation: uint8_t { ADD, /// And; source, destination, operand and displacement will be populated appropriately. AND, - /// Far call; followed by a 32-bit operand. + /// Far call; see the segment() and offset() fields. CALLF, /// Displacement call; followed by a 16-bit operand providing a call offset. CALLD, + /// Near call. CALLN, /// Convert byte into word; source will be AL, destination will be AH. CBW, @@ -155,6 +156,7 @@ class Instruction { Size operation_size() const { return Size(repetition_size_ >> 2); } uint16_t segment() const { return uint16_t(operand_); } + uint16_t offset() const { return uint16_t(displacement_); } int16_t displacement() const { return displacement_; } uint16_t operand() const { return operand_; } From ddb4bb14218ccf9167ec9b6915de814ec0b4496c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 15 Jan 2021 18:16:01 -0500 Subject: [PATCH 48/52] Better plans project layout. --- .../PowerPC/Decoder.cpp | 4 +- InstructionSets/PowerPC/Decoder.hpp | 58 ++++++ .../PowerPC/Instruction.hpp | 54 +---- .../Decoders => InstructionSets}/README.md | 33 +-- .../x86/Decoder.cpp | 4 +- .../x86/Decoder.hpp | 179 +---------------- InstructionSets/x86/Instruction.hpp | 188 ++++++++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 99 +++++---- .../Clock SignalTests/PowerPCDecoderTests.mm | 2 +- .../Mac/Clock SignalTests/x86DecoderTests.mm | 2 +- 10 files changed, 340 insertions(+), 283 deletions(-) rename Processors/Decoders/PowerPC/PowerPC.cpp => InstructionSets/PowerPC/Decoder.cpp (99%) create mode 100644 InstructionSets/PowerPC/Decoder.hpp rename Processors/Decoders/PowerPC/PowerPC.hpp => InstructionSets/PowerPC/Instruction.hpp (90%) rename {Processors/Decoders => InstructionSets}/README.md (50%) rename Processors/Decoders/x86/x86.cpp => InstructionSets/x86/Decoder.cpp (99%) rename Processors/Decoders/x86/x86.hpp => InstructionSets/x86/Decoder.hpp (53%) create mode 100644 InstructionSets/x86/Instruction.hpp diff --git a/Processors/Decoders/PowerPC/PowerPC.cpp b/InstructionSets/PowerPC/Decoder.cpp similarity index 99% rename from Processors/Decoders/PowerPC/PowerPC.cpp rename to InstructionSets/PowerPC/Decoder.cpp index bf9105e0d..efa550070 100644 --- a/Processors/Decoders/PowerPC/PowerPC.cpp +++ b/InstructionSets/PowerPC/Decoder.cpp @@ -1,12 +1,12 @@ // -// PowerPC.cpp +// Decoder.cpp // Clock Signal // // Created by Thomas Harte on 12/30/20. // Copyright © 2020 Thomas Harte. All rights reserved. // -#include "PowerPC.hpp" +#include "Decoder.hpp" using namespace CPU::Decoder::PowerPC; diff --git a/InstructionSets/PowerPC/Decoder.hpp b/InstructionSets/PowerPC/Decoder.hpp new file mode 100644 index 000000000..4b252c892 --- /dev/null +++ b/InstructionSets/PowerPC/Decoder.hpp @@ -0,0 +1,58 @@ +// +// Decoder.hpp +// Clock Signal +// +// Created by Thomas Harte on 12/30/20. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef InstructionSets_PowerPC_Decoder_hpp +#define InstructionSets_PowerPC_Decoder_hpp + +#include "Instruction.hpp" + +namespace CPU { +namespace Decoder { +namespace PowerPC { + +enum class Model { + /// i.e. 32-bit, with POWER carry-over instructions. + MPC601, + /// i.e. 32-bit, no POWER instructions. + MPC603, + /// i.e. 64-bit. + MPC620, +}; + +/*! + Implements PowerPC instruction decoding. + + This is an experimental implementation; it has not yet undergone significant testing. +*/ +struct Decoder { + public: + Decoder(Model model); + + Instruction decode(uint32_t opcode); + + private: + Model model_; + + bool is64bit() const { + return model_ == Model::MPC620; + } + + bool is32bit() const { + return !is64bit(); + } + + bool is601() const { + return model_ == Model::MPC601; + } +}; + +} +} +} + +#endif /* InstructionSets_PowerPC_Decoder_hpp */ diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/InstructionSets/PowerPC/Instruction.hpp similarity index 90% rename from Processors/Decoders/PowerPC/PowerPC.hpp rename to InstructionSets/PowerPC/Instruction.hpp index 20b9184de..a1d1237ca 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/InstructionSets/PowerPC/Instruction.hpp @@ -1,30 +1,20 @@ // -// PowerPC.hpp +// Instruction.hpp // Clock Signal // -// Created by Thomas Harte on 12/30/20. -// Copyright © 2020 Thomas Harte. All rights reserved. +// Created by Thomas Harte on 1/15/21. +// Copyright © 2021 Thomas Harte. All rights reserved. // -#ifndef PowerPC_hpp -#define PowerPC_hpp +#ifndef InstructionSets_PowerPC_Instruction_h +#define InstructionSets_PowerPC_Instruction_h -#include #include namespace CPU { namespace Decoder { namespace PowerPC { -enum class Model { - /// i.e. 32-bit, with POWER carry-over instructions. - MPC601, - /// i.e. 32-bit, no POWER instructions. - MPC603, - /// i.e. 64-bit. - MPC620, -}; - enum class Operation: uint8_t { Undefined, @@ -33,7 +23,7 @@ enum class Operation: uint8_t { absx, clcs, divx, divsx, dozx, dozi, lscbxx, maskgx, maskirx, mulx, nabsx, rlmix, rribx, slex, sleqx, sliqx, slliqx, sllqx, slqx, sraiqx, sraqx, srex, sreax, sreqx, sriqx, srliqx, srlqx, srqx, - + // 32- and 64-bit PowerPC instructions. addx, addcx, addex, addi, addic, addic_, addis, addmex, addzex, andx, andcx, andi_, andis_, bx, bcx, bcctrx, bclrx, cmp, cmpi, cmpl, cmpli, @@ -192,39 +182,11 @@ struct Instruction { uint32_t oe() const { return opcode & 0x800; } }; +// Sanity check on Instruction size. static_assert(sizeof(Instruction) <= 8); -/*! - Implements PowerPC instruction decoding. - - This is an experimental implementation; it has not yet undergone significant testing. -*/ -struct Decoder { - public: - Decoder(Model model); - - Instruction decode(uint32_t opcode); - - private: - Model model_; - - bool is64bit() const { - return model_ == Model::MPC620; - } - - bool is32bit() const { - return !is64bit(); - } - - bool is601() const { - return model_ == Model::MPC601; - } -}; - } } } -#include - -#endif /* PowerPC_hpp */ +#endif /* InstructionSets_PowerPC_Instruction_h */ diff --git a/Processors/Decoders/README.md b/InstructionSets/README.md similarity index 50% rename from Processors/Decoders/README.md rename to InstructionSets/README.md index ca5bbcbfe..941c88771 100644 --- a/Processors/Decoders/README.md +++ b/InstructionSets/README.md @@ -1,24 +1,25 @@ -# Decoders +# Instruction Sets + +## Decoders A decoder extracts fully-decoded instructions from a data stream for its associated architecture. -An instruction is 'fully-decoded' when an instance of a suitable struct or class has been created that can provide at least the instruction's length, and will usually also provide as relevant: +The meaning of 'fully-decoded' is flexible but it means that a caller can easily discern at least: * the operation in use; * its addressing mode; and * relevant registers. -It will have access to the original data stream again before being asked to provide any immediate values associated with the instruction. +It may be assumed that callers will have access to the original data stream for immediate values, if it is sensible to do so. -In deciding what to expose, what to store ahead of time and what to obtain just-in-time a decoder should have an eye on three main potential types of consumer: -1. disassemblers; -2. instruction executors; and -3. bus-centric CPU emulators. +In deciding what to expose, what to store ahead of time and what to obtain just-in-time a decoder should have an eye on two principal consumers: +1. disassemblers; and +2. instruction executors. -The first of those is likely to decode an instruction, output it to somewhere, and then immediately forget about it. +It may also be reasonable to make allowances for bus-centric CPU emulators, but those will be tightly coupled to specific decoders so no general rules need apply. -The second may opt to cache the decoded forms of instructions to reduce recurrent costs, but will always be dealing with an actual instruction stream. The chance of caching means that decoded instructions should seek to be small; however it also implies that as much processing cost as is reasonable should be spent up-front. +Disassemblers are likely to decode an instruction, output it, and then immediately forget about it. -The third may use a decoder live (especially if the instruction stream is complicated, or instruction words are large) or may use one once ahead of time in order to build up internal tables related to abstract interpretation of operations. The first use suggests a further premium on speed, the second implies that 'fully-decoded' instructions shouldn't seek to collect all possible immediate values ahead of time whenever doing so is avoidable — as a reult of thumb, they will usually return an instruction as soon as its length is fully known. +Instruction executors may opt to cache decoded instructions to reduce recurrent costs, but will always be dealing with an actual instruction stream. The chance of caching means that decoded instructions should seek to be small. If helpful then a decoder might prefer to return a `std::pair` or similar of ephemeral information and stuff that it is meaningful to store. ## Likely Interfaces @@ -30,19 +31,21 @@ If the instructions are a fixed size, the decoder can provide what is functional Instruction decode(word_type instruction) { ... } +For now I have preferred not to make this a simple constructor on `Instruction` because I'm reserving the option of switching to an ephemeral/permanent split in what's returned. More consideration needs to be applied here. + ### Variable-size instruction words -If instructions are a variable size, the decoder should maintain internal state such that it can be provided with fragments of instructions until a full decoding has occurred. +If instructions are a variable size, the decoder should maintain internal state such that it can be provided with fragments of instructions until a full decoding has occurred — this avoids an assumption that all source bytes will always be laid out linearly in memory. A sample interface: - Instruction decode(word_type *stream, size_t length) { ... } + std::pair decode(word_type *stream, size_t length) { ... } -The returned instruction has a size that is one of: -* a positive number, indicating a successful decoding that consumed that many `word_type`s; or +In this sample the returned pair provides an `int` size that is one of: +* a positive number, indicating a completed decoding that consumed that many `word_type`s; or * a negative number, indicating the [negatived] minimum number of `word_type`s that the caller should try to get hold of before calling `decode` again. -A caller is permitted to react in any way it prefers to negative numbers; they're a hint potentially to reduce calling overhead only. +A caller is permitted to react in any way it prefers to negative numbers; they're a hint potentially to reduce calling overhead only. A size of `0` would be taken to have the same meaning as a size of `-1`. ## Tying Decoders into Instruction Executors diff --git a/Processors/Decoders/x86/x86.cpp b/InstructionSets/x86/Decoder.cpp similarity index 99% rename from Processors/Decoders/x86/x86.cpp rename to InstructionSets/x86/Decoder.cpp index 3dfae8f89..dd54142e8 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/InstructionSets/x86/Decoder.cpp @@ -6,7 +6,7 @@ // Copyright © 2021 Thomas Harte. All rights reserved. // -#include "x86.hpp" +#include "Decoder.hpp" #include #include @@ -17,7 +17,7 @@ using namespace CPU::Decoder::x86; // Only 8086 is suppoted for now. Decoder::Decoder(Model) {} -std::pair Decoder::decode(const uint8_t *source, size_t length) { +std::pair Decoder::decode(const uint8_t *source, size_t length) { const uint8_t *const end = source + length; // MARK: - Prefixes (if present) and the opcode. diff --git a/Processors/Decoders/x86/x86.hpp b/InstructionSets/x86/Decoder.hpp similarity index 53% rename from Processors/Decoders/x86/x86.hpp rename to InstructionSets/x86/Decoder.hpp index 841da5360..b5235a0aa 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/InstructionSets/x86/Decoder.hpp @@ -1,16 +1,17 @@ // -// x86.hpp +// Decoder.hpp // Clock Signal // // Created by Thomas Harte on 1/1/21. // Copyright © 2021 Thomas Harte. All rights reserved. // -#ifndef x86_hpp -#define x86_hpp +#ifndef InstructionSets_x86_Decoder_hpp +#define InstructionSets_x86_Decoder_hpp -#include -#include +#include "Instruction.hpp" + +#include namespace CPU { namespace Decoder { @@ -20,172 +21,6 @@ enum class Model { i8086, }; -enum class Operation: uint8_t { - Invalid, - - /// ASCII adjust after addition; source will be AL and destination will be AX. - AAA, - /// ASCII adjust before division; destination will be AX and source will be a multiplier. - AAD, - /// ASCII adjust after multiplication; destination will be AX and source will be a divider. - AAM, - /// ASCII adjust after subtraction; source will be AL and destination will be AX. - AAS, - /// Add with carry; source, destination, operand and displacement will be populated appropriately. - ADC, - /// Add; source, destination, operand and displacement will be populated appropriately. - ADD, - /// And; source, destination, operand and displacement will be populated appropriately. - AND, - /// Far call; see the segment() and offset() fields. - CALLF, - /// Displacement call; followed by a 16-bit operand providing a call offset. - CALLD, - /// Near call. - CALLN, - /// Convert byte into word; source will be AL, destination will be AH. - CBW, - /// Clear carry flag; no source or destination provided. - CLC, - /// Clear direction flag; no source or destination provided. - CLD, - /// Clear interrupt flag; no source or destination provided. - CLI, - /// Complement carry flag; no source or destination provided. - CMC, - /// Compare; source, destination, operand and displacement will be populated appropriately. - CMP, - /// Compare [bytes or words, per operation size]; source and destination implied to be DS:[SI] and ES:[DI]. - CMPS, - /// Convert word to double word; source will be AX and destination will be DX. - CWD, - /// Decimal adjust after addition; source and destination will be AL. - DAA, - /// Decimal adjust after subtraction; source and destination will be AL. - DAS, - /// Dec; source, destination, operand and displacement will be populated appropriately. - DEC, - DIV, ESC, HLT, IDIV, IMUL, IN, - INC, INT, INT3, INTO, IRET, - JO, JNO, JB, JNB, JE, JNE, JBE, JNBE, - JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, - 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, - SAR, SBB, SCAS, SAL, SHR, STC, STD, STI, STOS, SUB, TEST, - WAIT, XCHG, XLAT, XOR, - LES, LOOP, JPCX, - - RETF, - RETN, -}; - -enum class Size: uint8_t { - Implied = 0, - Byte = 1, - Word = 2, - DWord = 4, -}; - -enum class Source: uint8_t { - None, - CS, DS, ES, SS, - - AL, AH, AX, - BL, BH, BX, - CL, CH, CX, - DL, DH, DX, - - SI, DI, - BP, SP, - - IndBXPlusSI, - IndBXPlusDI, - IndBPPlusSI, - IndBPPlusDI, - IndSI, - IndDI, - DirectAddress, - IndBP, - IndBX, - - Immediate -}; - -enum class Repetition: uint8_t { - None, RepE, RepNE -}; - -class Instruction { - public: - Operation operation = Operation::Invalid; - - bool operator ==(const Instruction &rhs) const { - return - repetition_size_ == rhs.repetition_size_ && - sources_ == rhs.sources_ && - displacement_ == rhs.displacement_ && - operand_ == rhs.operand_; - } - - private: - // b0, b1: a Repetition; - // b2+: operation size. - uint8_t repetition_size_ = 0; - - // b0–b5: source; - // b6–b11: destination; - // b12–b14: segment override; - // b15: lock. - uint16_t sources_ = 0; - - // Unpackable fields. - int16_t displacement_ = 0; - uint16_t operand_ = 0; // ... or used to store a segment for far operations. - - public: - Source source() const { return Source(sources_ & 0x3f); } - Source destination() const { return Source((sources_ >> 6) & 0x3f); } - bool lock() const { return sources_ & 0x8000; } - Source segment_override() const { return Source((sources_ >> 12) & 7); } - - Repetition repetition() const { return Repetition(repetition_size_ & 3); } - Size operation_size() const { return Size(repetition_size_ >> 2); } - - uint16_t segment() const { return uint16_t(operand_); } - uint16_t offset() const { return uint16_t(displacement_); } - - int16_t displacement() const { return displacement_; } - uint16_t operand() const { return operand_; } - - Instruction() noexcept {} - Instruction( - Operation operation, - Source source, - Source destination, - bool lock, - Source segment_override, - Repetition repetition, - Size operation_size, - int16_t displacement, - uint16_t operand) noexcept : - operation(operation), - repetition_size_(uint8_t((int(operation_size) << 2) | int(repetition))), - sources_(uint16_t( - int(source) | - (int(destination) << 6) | - (int(segment_override) << 12) | - (int(lock) << 15) - )), - displacement_(displacement), - operand_(operand) {} -}; - -static_assert(sizeof(Instruction) <= 8); - /*! Implements Intel x86 instruction decoding. @@ -318,4 +153,4 @@ struct Decoder { } } -#endif /* x86_hpp */ +#endif /* InstructionSets_x86_Decoder_hpp */ diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp new file mode 100644 index 000000000..98657bf56 --- /dev/null +++ b/InstructionSets/x86/Instruction.hpp @@ -0,0 +1,188 @@ +// +// Instruction.hpp +// Clock Signal +// +// Created by Thomas Harte on 1/15/21. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#ifndef InstructionSets_x86_Instruction_h +#define InstructionSets_x86_Instruction_h + +#include + +namespace CPU { +namespace Decoder { +namespace x86 { + +enum class Operation: uint8_t { + Invalid, + + /// ASCII adjust after addition; source will be AL and destination will be AX. + AAA, + /// ASCII adjust before division; destination will be AX and source will be a multiplier. + AAD, + /// ASCII adjust after multiplication; destination will be AX and source will be a divider. + AAM, + /// ASCII adjust after subtraction; source will be AL and destination will be AX. + AAS, + /// Add with carry; source, destination, operand and displacement will be populated appropriately. + ADC, + /// Add; source, destination, operand and displacement will be populated appropriately. + ADD, + /// And; source, destination, operand and displacement will be populated appropriately. + AND, + /// Far call; see the segment() and offset() fields. + CALLF, + /// Displacement call; followed by a 16-bit operand providing a call offset. + CALLD, + /// Near call. + CALLN, + /// Convert byte into word; source will be AL, destination will be AH. + CBW, + /// Clear carry flag; no source or destination provided. + CLC, + /// Clear direction flag; no source or destination provided. + CLD, + /// Clear interrupt flag; no source or destination provided. + CLI, + /// Complement carry flag; no source or destination provided. + CMC, + /// Compare; source, destination, operand and displacement will be populated appropriately. + CMP, + /// Compare [bytes or words, per operation size]; source and destination implied to be DS:[SI] and ES:[DI]. + CMPS, + /// Convert word to double word; source will be AX and destination will be DX. + CWD, + /// Decimal adjust after addition; source and destination will be AL. + DAA, + /// Decimal adjust after subtraction; source and destination will be AL. + DAS, + /// Dec; source, destination, operand and displacement will be populated appropriately. + DEC, + DIV, ESC, HLT, IDIV, IMUL, IN, + INC, INT, INT3, INTO, IRET, + JO, JNO, JB, JNB, JE, JNE, JBE, JNBE, + JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, + 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, + SAR, SBB, SCAS, SAL, SHR, STC, STD, STI, STOS, SUB, TEST, + WAIT, XCHG, XLAT, XOR, + LES, LOOP, JPCX, + + RETF, + RETN, +}; + +enum class Size: uint8_t { + Implied = 0, + Byte = 1, + Word = 2, + DWord = 4, +}; + +enum class Source: uint8_t { + None, + CS, DS, ES, SS, + + AL, AH, AX, + BL, BH, BX, + CL, CH, CX, + DL, DH, DX, + + SI, DI, + BP, SP, + + IndBXPlusSI, + IndBXPlusDI, + IndBPPlusSI, + IndBPPlusDI, + IndSI, + IndDI, + DirectAddress, + IndBP, + IndBX, + + Immediate +}; + +enum class Repetition: uint8_t { + None, RepE, RepNE +}; + +class Instruction { + public: + Operation operation = Operation::Invalid; + + bool operator ==(const Instruction &rhs) const { + return + repetition_size_ == rhs.repetition_size_ && + sources_ == rhs.sources_ && + displacement_ == rhs.displacement_ && + operand_ == rhs.operand_; + } + + private: + // b0, b1: a Repetition; + // b2+: operation size. + uint8_t repetition_size_ = 0; + + // b0–b5: source; + // b6–b11: destination; + // b12–b14: segment override; + // b15: lock. + uint16_t sources_ = 0; + + // Unpackable fields. + int16_t displacement_ = 0; + uint16_t operand_ = 0; // ... or used to store a segment for far operations. + + public: + Source source() const { return Source(sources_ & 0x3f); } + Source destination() const { return Source((sources_ >> 6) & 0x3f); } + bool lock() const { return sources_ & 0x8000; } + Source segment_override() const { return Source((sources_ >> 12) & 7); } + + Repetition repetition() const { return Repetition(repetition_size_ & 3); } + Size operation_size() const { return Size(repetition_size_ >> 2); } + + uint16_t segment() const { return uint16_t(operand_); } + uint16_t offset() const { return uint16_t(displacement_); } + + int16_t displacement() const { return displacement_; } + uint16_t operand() const { return operand_; } + + Instruction() noexcept {} + Instruction( + Operation operation, + Source source, + Source destination, + bool lock, + Source segment_override, + Repetition repetition, + Size operation_size, + int16_t displacement, + uint16_t operand) noexcept : + operation(operation), + repetition_size_(uint8_t((int(operation_size) << 2) | int(repetition))), + sources_(uint16_t( + int(source) | + (int(destination) << 6) | + (int(segment_override) << 12) | + (int(lock) << 15) + )), + displacement_(displacement), + operand_(operand) {} +}; + +static_assert(sizeof(Instruction) <= 8); + +} +} +} + +#endif /* InstructionSets_x86_Instruction_h */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 7aae614d8..92ace4adc 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -183,8 +183,6 @@ 4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */; }; 4B3BA0D11D318B44005DD7A7 /* TestMachine6502.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CD1D318B44005DD7A7 /* TestMachine6502.mm */; }; 4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BF5AE1F146264005B6C36 /* CSW.cpp */; }; - 4B3F76AE25A0196100178AEC /* x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76AD25A0196100178AEC /* x86.cpp */; }; - 4B3F76AF25A0196100178AEC /* x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76AD25A0196100178AEC /* x86.cpp */; }; 4B3F76B925A1635300178AEC /* PowerPCDecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */; }; 4B3FCC40201EC24200960631 /* MultiMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */; }; 4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */; }; @@ -847,8 +845,6 @@ 4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; }; 4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsPanel.swift */; }; 4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; }; - 4BDDBBD7259D757800CEFF58 /* PowerPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBBD5259D757800CEFF58 /* PowerPC.cpp */; }; - 4BDDBBD8259D757800CEFF58 /* PowerPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBBD5259D757800CEFF58 /* PowerPC.cpp */; }; 4BE0A3EE237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; }; 4BE0A3EF237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; }; 4BE211DE253E4E4800435408 /* 65C02_no_Rockwell_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */; }; @@ -862,13 +858,20 @@ 4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */; }; 4BEBFB512002DB30000708CC /* DiskROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */; }; 4BEBFB522002DB30000708CC /* DiskROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */; }; + 4BEDA3BA25B25563000C2DBD /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B425B25563000C2DBD /* Decoder.cpp */; }; + 4BEDA3BB25B25563000C2DBD /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B425B25563000C2DBD /* Decoder.cpp */; }; + 4BEDA3BC25B25563000C2DBD /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B425B25563000C2DBD /* Decoder.cpp */; }; + 4BEDA3BD25B25563000C2DBD /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B625B25563000C2DBD /* README.md */; }; + 4BEDA3BE25B25563000C2DBD /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B625B25563000C2DBD /* README.md */; }; + 4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B925B25563000C2DBD /* Decoder.cpp */; }; + 4BEDA3C025B25563000C2DBD /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B925B25563000C2DBD /* Decoder.cpp */; }; + 4BEDA3C125B25563000C2DBD /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B925B25563000C2DBD /* Decoder.cpp */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; 4BEE149A227FC0EA00133682 /* IWM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE1498227FC0EA00133682 /* IWM.cpp */; }; 4BEE1EC022B5E236000A26A6 /* MacGCRTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */; }; 4BEE1EC122B5E2FD000A26A6 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCE209BF27B00AB2146 /* Encoder.cpp */; }; 4BEE4BD425A26E2B00011BD2 /* x86DecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */; }; - 4BEE4BE125A26F8100011BD2 /* x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F76AD25A0196100178AEC /* x86.cpp */; }; 4BEEE6BD20DC72EB003723BF /* CompositeOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BEEE6BB20DC72EA003723BF /* CompositeOptions.xib */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; 4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; }; @@ -1081,9 +1084,6 @@ 4B3BA0CD1D318B44005DD7A7 /* TestMachine6502.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestMachine6502.mm; sourceTree = ""; }; 4B3BF5AE1F146264005B6C36 /* CSW.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSW.cpp; sourceTree = ""; }; 4B3BF5AF1F146264005B6C36 /* CSW.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CSW.hpp; sourceTree = ""; }; - 4B3F769E259FCE0F00178AEC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 4B3F76AC25A0196100178AEC /* x86.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = x86.hpp; sourceTree = ""; }; - 4B3F76AD25A0196100178AEC /* x86.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x86.cpp; sourceTree = ""; }; 4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PowerPCDecoderTests.mm; sourceTree = ""; }; 4B3FCC3E201EC24200960631 /* MultiMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMachine.hpp; sourceTree = ""; }; 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMachine.cpp; sourceTree = ""; }; @@ -1777,8 +1777,6 @@ 4BDB3D8522833321002D3CEE /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = ""; }; 4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMMachine.hpp; sourceTree = ""; }; 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MachineCycleTests.swift; sourceTree = ""; }; - 4BDDBBD5259D757800CEFF58 /* PowerPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PowerPC.cpp; sourceTree = ""; }; - 4BDDBBD6259D757800CEFF58 /* PowerPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PowerPC.hpp; sourceTree = ""; }; 4BE0A3EC237BB170002AB46F /* ST.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ST.cpp; sourceTree = ""; }; 4BE0A3ED237BB170002AB46F /* ST.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ST.hpp; sourceTree = ""; }; 4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 65C02_no_Rockwell_test.bin; path = "Klaus Dormann/65C02_no_Rockwell_test.bin"; sourceTree = ""; }; @@ -1801,6 +1799,13 @@ 4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSXDSK.hpp; sourceTree = ""; }; 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DiskROM.cpp; path = MSX/DiskROM.cpp; sourceTree = ""; }; 4BEBFB502002DB30000708CC /* DiskROM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DiskROM.hpp; path = MSX/DiskROM.hpp; sourceTree = ""; }; + 4BEDA3B425B25563000C2DBD /* Decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decoder.cpp; sourceTree = ""; }; + 4BEDA3B525B25563000C2DBD /* Decoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Decoder.hpp; sourceTree = ""; }; + 4BEDA3B625B25563000C2DBD /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 4BEDA3B825B25563000C2DBD /* Decoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Decoder.hpp; sourceTree = ""; }; + 4BEDA3B925B25563000C2DBD /* Decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decoder.cpp; sourceTree = ""; }; + 4BEDA3D225B257F2000C2DBD /* Instruction.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Instruction.hpp; sourceTree = ""; }; + 4BEDA3DB25B2588F000C2DBD /* Instruction.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Instruction.hpp; sourceTree = ""; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = ""; }; 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = ""; }; @@ -2332,15 +2337,6 @@ path = Bridges; sourceTree = ""; }; - 4B3F76AB25A0196100178AEC /* x86 */ = { - isa = PBXGroup; - children = ( - 4B3F76AC25A0196100178AEC /* x86.hpp */, - 4B3F76AD25A0196100178AEC /* x86.cpp */, - ); - path = x86; - sourceTree = ""; - }; 4B3FCC3D201EC24200960631 /* MultiMachine */ = { isa = PBXGroup; children = ( @@ -3407,6 +3403,7 @@ 4B31B88E1FBFBCD800C140D5 /* Configurable */, 4B055A761FAE78210060FFFF /* Frameworks */, 4B86E2581F8C628F006FAA45 /* Inputs */, + 4BEDA3B225B25563000C2DBD /* InstructionSets */, 4BB73EDC1B587CA500552FC2 /* Machines */, 4B7BA03C23D55E7900B98D9E /* Numeric */, 4B366DFD1B5C165F0026627B /* Outputs */, @@ -3562,7 +3559,6 @@ 4B4DEC15252BFA9C004583AC /* 6502Esque */, 4BF8D4CC251C0C9C00BBE21B /* 65816 */, 4BFF1D332233778C00838EA1 /* 68000 */, - 4BDDBBD3259D757800CEFF58 /* Decoders */, 4B77069E1EC9045B0053B588 /* Z80 */, ); name = Processors; @@ -3905,25 +3901,6 @@ path = 5380; sourceTree = ""; }; - 4BDDBBD3259D757800CEFF58 /* Decoders */ = { - isa = PBXGroup; - children = ( - 4B3F769E259FCE0F00178AEC /* README.md */, - 4BDDBBD4259D757800CEFF58 /* PowerPC */, - 4B3F76AB25A0196100178AEC /* x86 */, - ); - path = Decoders; - sourceTree = ""; - }; - 4BDDBBD4259D757800CEFF58 /* PowerPC */ = { - isa = PBXGroup; - children = ( - 4BDDBBD5259D757800CEFF58 /* PowerPC.cpp */, - 4BDDBBD6259D757800CEFF58 /* PowerPC.hpp */, - ); - path = PowerPC; - sourceTree = ""; - }; 4BE5F85A1C3E1C2500C43F01 /* Resources */ = { isa = PBXGroup; children = ( @@ -3950,6 +3927,37 @@ name = Zexall; sourceTree = ""; }; + 4BEDA3B225B25563000C2DBD /* InstructionSets */ = { + isa = PBXGroup; + children = ( + 4BEDA3B625B25563000C2DBD /* README.md */, + 4BEDA3B325B25563000C2DBD /* PowerPC */, + 4BEDA3B725B25563000C2DBD /* x86 */, + ); + name = InstructionSets; + path = ../../InstructionSets; + sourceTree = ""; + }; + 4BEDA3B325B25563000C2DBD /* PowerPC */ = { + isa = PBXGroup; + children = ( + 4BEDA3B425B25563000C2DBD /* Decoder.cpp */, + 4BEDA3B525B25563000C2DBD /* Decoder.hpp */, + 4BEDA3D225B257F2000C2DBD /* Instruction.hpp */, + ); + path = PowerPC; + sourceTree = ""; + }; + 4BEDA3B725B25563000C2DBD /* x86 */ = { + isa = PBXGroup; + children = ( + 4BEDA3B925B25563000C2DBD /* Decoder.cpp */, + 4BEDA3B825B25563000C2DBD /* Decoder.hpp */, + 4BEDA3DB25B2588F000C2DBD /* Instruction.hpp */, + ); + path = x86; + sourceTree = ""; + }; 4BEE0A691D72496600532C7B /* Cartridge */ = { isa = PBXGroup; children = ( @@ -4180,6 +4188,7 @@ 4B2C45421E3C3896002A2389 /* cartridge.png in Resources */, 4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */, 4B79E4451E3AF38600141F11 /* floppy35.png in Resources */, + 4BEDA3BD25B25563000C2DBD /* README.md in Resources */, 4B55DD8420DF06680043F2E5 /* MachinePicker.xib in Resources */, 4BDA00DA22E60EE300AC3CD0 /* ROMRequester.xib in Resources */, 4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */, @@ -4216,6 +4225,7 @@ 4BB2994E1B587D8400A49093 /* dexn in Resources */, 4BB299971B587D8400A49093 /* nopa in Resources */, 4B018B89211930DE002A3937 /* 65C02_extended_opcodes_test.bin in Resources */, + 4BEDA3BE25B25563000C2DBD /* README.md in Resources */, 4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */, 4BB299521B587D8400A49093 /* eoray in Resources */, 4BB299411B587D8400A49093 /* cpyb in Resources */, @@ -4650,7 +4660,7 @@ 4BD67DD1209BF27B00AB2146 /* Encoder.cpp in Sources */, 4B89451F201967B4007DE474 /* Tape.cpp in Sources */, 4B055AA81FAE85EF0060FFFF /* Shifter.cpp in Sources */, - 4BDDBBD8259D757800CEFF58 /* PowerPC.cpp in Sources */, + 4BEDA3BC25B25563000C2DBD /* Decoder.cpp in Sources */, 4B8318B422D3E546006DB630 /* DriveSpeedAccumulator.cpp in Sources */, 4B055AC81FAE9AFB0060FFFF /* C1540.cpp in Sources */, 4B055A8F1FAE85A90060FFFF /* FileHolder.cpp in Sources */, @@ -4685,7 +4695,7 @@ 4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */, 4B0E04EB1FC9E78800F43484 /* CAS.cpp in Sources */, 4BB0A65D2045009000FB3688 /* ColecoVision.cpp in Sources */, - 4B3F76AF25A0196100178AEC /* x86.cpp in Sources */, + 4BEDA3C125B25563000C2DBD /* Decoder.cpp in Sources */, 4BB0A65C2044FD3000FB3688 /* SN76489.cpp in Sources */, 4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */, 4B055AB91FAE86170060FFFF /* Acorn.cpp in Sources */, @@ -4758,7 +4768,7 @@ 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */, 4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */, 4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */, - 4B3F76AE25A0196100178AEC /* x86.cpp in Sources */, + 4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */, @@ -4795,7 +4805,7 @@ 4B45189F1F75FD1C00926311 /* AcornADF.cpp in Sources */, 4B7BA03023C2B19C00B98D9E /* Jasmin.cpp in Sources */, 4B7136911F789C93008B8ED9 /* SegmentParser.cpp in Sources */, - 4BDDBBD7259D757800CEFF58 /* PowerPC.cpp in Sources */, + 4BEDA3BA25B25563000C2DBD /* Decoder.cpp in Sources */, 4B4518A21F75FD1C00926311 /* G64.cpp in Sources */, 4B89452C201967B4007DE474 /* Tape.cpp in Sources */, 4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */, @@ -4969,7 +4979,6 @@ 4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */, 4B778F0F23A5EC560000D260 /* PCMTrack.cpp in Sources */, 4B778F1123A5EC650000D260 /* FileHolder.cpp in Sources */, - 4BEE4BE125A26F8100011BD2 /* x86.cpp in Sources */, 4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */, 4B778F2023A5EDCE0000D260 /* HFV.cpp in Sources */, 4B778F3323A5F0FB0000D260 /* MassStorageDevice.cpp in Sources */, @@ -4982,6 +4991,7 @@ 4B778F4823A5F1E70000D260 /* StaticAnalyser.cpp in Sources */, 4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */, 4B778F2823A5EEF80000D260 /* Cartridge.cpp in Sources */, + 4BEDA3C025B25563000C2DBD /* Decoder.cpp in Sources */, 4B778F4C23A5F2090000D260 /* StaticAnalyser.cpp in Sources */, 4B778F2623A5EE350000D260 /* Acorn.cpp in Sources */, 4B778F5B23A5F2DE0000D260 /* Tape.cpp in Sources */, @@ -4992,6 +5002,7 @@ 4BEE1EC122B5E2FD000A26A6 /* Encoder.cpp in Sources */, 4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */, 4B778EFF23A5EB940000D260 /* D64.cpp in Sources */, + 4BEDA3BB25B25563000C2DBD /* Decoder.cpp in Sources */, 4B778F2423A5EDEE0000D260 /* PRG.cpp in Sources */, 4B778F5A23A5F2D50000D260 /* 6502.cpp in Sources */, 4B778F6223A5F35F0000D260 /* File.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm index 3ea0c5759..2ec9be6d2 100644 --- a/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/PowerPCDecoderTests.mm @@ -8,7 +8,7 @@ #import -#include "../../../Processors/Decoders/PowerPC/PowerPC.hpp" +#include "../../../InstructionSets/PowerPC/Decoder.hpp" namespace { using Operation = CPU::Decoder::PowerPC::Operation; diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 8cfc6c3b8..20b5f8731 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -10,7 +10,7 @@ #include #include -#include "../../../Processors/Decoders/x86/x86.hpp" +#include "../../../InstructionSets/x86/Decoder.hpp" namespace { using Operation = CPU::Decoder::x86::Operation; From fa4938f29c5a4b4aeddb939ef4c6d6352d1c7ad7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 15 Jan 2021 18:27:55 -0500 Subject: [PATCH 49/52] Establishes the reason I'm sort-of documenting these. --- InstructionSets/x86/Instruction.hpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index 98657bf56..ed3cad56f 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -15,6 +15,12 @@ namespace CPU { namespace Decoder { namespace x86 { +/* + Operations are documented below to establish expectations as to which + instruction fields will be meaningful for each; this is a work-in-progress + and may currently contain errors in the opcode descriptions — especially + where implicit register dependencies are afoot. +*/ enum class Operation: uint8_t { Invalid, @@ -58,12 +64,27 @@ enum class Operation: uint8_t { DAA, /// Decimal adjust after subtraction; source and destination will be AL. DAS, - /// Dec; source, destination, operand and displacement will be populated appropriately. + /// Decrement; source, destination, operand and displacement will be populated appropriately. DEC, - DIV, ESC, HLT, IDIV, IMUL, IN, - INC, INT, INT3, INTO, IRET, + /// Unsigned divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. + DIV, + /// Signed divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. + IDIV, + /// Escape, for a coprocessor; perform the bus cycles necessary to read the source and destination and perform a NOP. + ESC, + HLT, + IMUL, + IN, + INC, + INT, + INT3, + INTO, + IRET, + + // Various jumps; see the displacement to calculate targets. JO, JNO, JB, JNB, JE, JNE, JBE, JNBE, JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, + JMPN, JMPF, JCXZ, From e8ce70dccbdf83fec85161f2c4e6f645ca779325 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 15 Jan 2021 18:52:59 -0500 Subject: [PATCH 50/52] Chips further away at documentation. --- InstructionSets/x86/Decoder.cpp | 2 +- InstructionSets/x86/Instruction.hpp | 67 ++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/InstructionSets/x86/Decoder.cpp b/InstructionSets/x86/Decoder.cpp index dd54142e8..1b9228172 100644 --- a/InstructionSets/x86/Decoder.cpp +++ b/InstructionSets/x86/Decoder.cpp @@ -376,7 +376,7 @@ std::pair Decoder::decode(const uint8_t *so case 3: memreg = reg_table[operation_size_][rm]; - // LES and LDS accept a real memory argument only. + // LES and LDS accept a memory argument only, not a register. if(operation_ == Operation::LES || operation_ == Operation::LDS) { const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index ed3cad56f..0406269d0 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -36,8 +36,6 @@ enum class Operation: uint8_t { ADC, /// Add; source, destination, operand and displacement will be populated appropriately. ADD, - /// And; source, destination, operand and displacement will be populated appropriately. - AND, /// Far call; see the segment() and offset() fields. CALLF, /// Displacement call; followed by a 16-bit operand providing a call offset. @@ -66,36 +64,83 @@ enum class Operation: uint8_t { DAS, /// Decrement; source, destination, operand and displacement will be populated appropriately. DEC, + /// Increment; source, destination, operand and displacement will be populated appropriately. + INC, /// Unsigned divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. DIV, /// Signed divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. IDIV, /// Escape, for a coprocessor; perform the bus cycles necessary to read the source and destination and perform a NOP. ESC, + /// Stops the processor until the next interrupt is fired. HLT, + /// Unsigned multiply; multiplies the source value by AX or AL, storing the result in DX:AX or AX. + MUL, + /// Signed multiply; multiplies the source value by AX or AL, storing the result in DX:AX or AX. IMUL, + /// Reads from the port specified by source to the destination. IN, - INC, + /// Writes from the port specified by destination from the source. + OUT, + /// Generates a software interrupt of the level stated in the operand. INT, + /// Generates a software interrupt of level 3. INT3, + /// Generates a software interrupt of level 4 if overflow is set. INTO, - IRET, // Various jumps; see the displacement to calculate targets. JO, JNO, JB, JNB, JE, JNE, JBE, JNBE, JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, + /// Near jump; if an operand is not ::None then it gives an absolute destination; otherwise see the displacement. JMPN, + /// Far jump to the indicated segment and offset. 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, - SAR, SBB, SCAS, SAL, SHR, STC, STD, STI, STOS, SUB, TEST, - WAIT, XCHG, XLAT, XOR, - LES, LOOP, JPCX, + /// Relative jump performed only if CX = 0; see the displacement. + JPCX, + /// Load status flags to AH. + LAHF, + /// Load status flags from AH. + SAHF, + /// Load a segment and offset from the source into DS and the destination. + LDS, + /// Computes the effective address of the source and loads it into the destination. + LEA, + /// Load string; reads from DS:SI into AL or AX, subject to segment override. + LODS, + /// Move string; moves a byte or word from DS:SI to ES:DI. If a segment override is provided, it overrides the the source. + MOVS, + // Perform a possibly-conditional loop, decrementing CX. See the displacement. + LOOP, LOOPE, LOOPNE, + + /// Loads the destination with the source. + MOV, + /// Negatives; source and destination point to the same thing, to negative. + NEG, + /// Logical NOT; source and destination point to the same thing, to negative. + NOT, + /// Logical AND; source, destination, operand and displacement will be populated appropriately. + AND, + /// Logical OR of source onto destination. + OR, + /// Logical XOR of source onto destination. + XOR, + /// NOP; no further fields. + NOP, + + POP, POPF, PUSH, PUSHF, RCL, RCR, REP, + ROL, ROR, + SAR, SBB, SCAS, SAL, SHR, STC, STD, STI, STOS, SUB, TEST, + WAIT, XCHG, XLAT, + LES, + + /// Return from interrupt. + IRET, + /// Near return; if source is not ::None then it will be an ::Immediate indicating how many additional bytes to remove from the stack. RETF, + /// Far return; if source is not ::None then it will be an ::Immediate indicating how many additional bytes to remove from the stack. RETN, }; From 47d20699d8adf4f4d3b3b84cb41b46499a45916b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 15 Jan 2021 20:48:31 -0500 Subject: [PATCH 51/52] Completes list, ensures POP acts as documented. --- InstructionSets/x86/Decoder.cpp | 6 +- InstructionSets/x86/Instruction.hpp | 134 +++++++++++++++++++--------- 2 files changed, 96 insertions(+), 44 deletions(-) diff --git a/InstructionSets/x86/Decoder.cpp b/InstructionSets/x86/Decoder.cpp index 1b9228172..2634ec9e3 100644 --- a/InstructionSets/x86/Decoder.cpp +++ b/InstructionSets/x86/Decoder.cpp @@ -98,18 +98,18 @@ std::pair Decoder::decode(const uint8_t *so PartialBlock(0x00, ADD); break; case 0x06: Complete(PUSH, ES, None, 2); break; - case 0x07: Complete(POP, ES, None, 2); break; + case 0x07: Complete(POP, None, ES, 2); break; PartialBlock(0x08, OR); break; case 0x0e: Complete(PUSH, CS, None, 2); break; PartialBlock(0x10, ADC); break; case 0x16: Complete(PUSH, SS, None, 2); break; - case 0x17: Complete(POP, SS, None, 2); break; + case 0x17: Complete(POP, None, SS, 2); break; PartialBlock(0x18, SBB); break; case 0x1e: Complete(PUSH, DS, None, 2); break; - case 0x1f: Complete(POP, DS, None, 2); break; + case 0x1f: Complete(POP, None, DS, 2); break; PartialBlock(0x20, AND); break; case 0x26: segment_override_ = Source::ES; break; diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index 0406269d0..ba13d8bf1 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -32,24 +32,9 @@ enum class Operation: uint8_t { AAM, /// ASCII adjust after subtraction; source will be AL and destination will be AX. AAS, - /// Add with carry; source, destination, operand and displacement will be populated appropriately. - ADC, - /// Add; source, destination, operand and displacement will be populated appropriately. - ADD, - /// Far call; see the segment() and offset() fields. - CALLF, - /// Displacement call; followed by a 16-bit operand providing a call offset. - CALLD, - /// Near call. - CALLN, + /// Convert byte into word; source will be AL, destination will be AH. CBW, - /// Clear carry flag; no source or destination provided. - CLC, - /// Clear direction flag; no source or destination provided. - CLD, - /// Clear interrupt flag; no source or destination provided. - CLI, /// Complement carry flag; no source or destination provided. CMC, /// Compare; source, destination, operand and displacement will be populated appropriately. @@ -66,22 +51,61 @@ enum class Operation: uint8_t { DEC, /// Increment; source, destination, operand and displacement will be populated appropriately. INC, - /// Unsigned divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. - DIV, - /// Signed divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. - IDIV, /// Escape, for a coprocessor; perform the bus cycles necessary to read the source and destination and perform a NOP. ESC, + /// Stops the processor until the next interrupt is fired. HLT, + /// Waits until the WAIT input is asserted; if an interrupt occurs then it is serviced but returns to the WAIT. + WAIT, + + + /// Add with carry; source, destination, operand and displacement will be populated appropriately. + ADC, + /// Add; source, destination, operand and displacement will be populated appropriately. + ADD, + /// Subtract with borrow; source, destination, operand and displacement will be populated appropriately. + SBB, + /// Subtract; source, destination, operand and displacement will be populated appropriately. + SUB, /// Unsigned multiply; multiplies the source value by AX or AL, storing the result in DX:AX or AX. MUL, /// Signed multiply; multiplies the source value by AX or AL, storing the result in DX:AX or AX. IMUL, + /// Unsigned divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. + DIV, + /// Signed divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. + IDIV, + + /// Reads from the port specified by source to the destination. IN, /// Writes from the port specified by destination from the source. OUT, + + + // Various jumps; see the displacement to calculate targets. + JO, JNO, JB, JNB, JE, JNE, JBE, JNBE, + JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, + + /// Far call; see the segment() and offset() fields. + CALLF, + /// Displacement call; followed by a 16-bit operand providing a call offset. + CALLD, + /// Near call. + CALLN, + /// Return from interrupt. + IRET, + /// Near return; if source is not ::None then it will be an ::Immediate indicating how many additional bytes to remove from the stack. + RETF, + /// Far return; if source is not ::None then it will be an ::Immediate indicating how many additional bytes to remove from the stack. + RETN, + /// Near jump; if an operand is not ::None then it gives an absolute destination; otherwise see the displacement. + JMPN, + /// Far jump to the indicated segment and offset. + JMPF, + /// Relative jump performed only if CX = 0; see the displacement. + JPCX, /// Generates a software interrupt of the level stated in the operand. INT, /// Generates a software interrupt of level 3. @@ -89,28 +113,25 @@ enum class Operation: uint8_t { /// Generates a software interrupt of level 4 if overflow is set. INTO, - // Various jumps; see the displacement to calculate targets. - JO, JNO, JB, JNB, JE, JNE, JBE, JNBE, - JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, - - /// Near jump; if an operand is not ::None then it gives an absolute destination; otherwise see the displacement. - JMPN, - /// Far jump to the indicated segment and offset. - JMPF, - /// Relative jump performed only if CX = 0; see the displacement. - JPCX, /// Load status flags to AH. LAHF, /// Load status flags from AH. SAHF, /// Load a segment and offset from the source into DS and the destination. LDS, + /// Load a segment and offset from the source into ES and the destination. + LES, /// Computes the effective address of the source and loads it into the destination. LEA, + /// Load string; reads from DS:SI into AL or AX, subject to segment override. LODS, /// Move string; moves a byte or word from DS:SI to ES:DI. If a segment override is provided, it overrides the the source. MOVS, + /// Scan string; reads a byte or word from DS:SI and compares it to AL or AX. + SCAS, + /// Store string; store AL or AX to ES:DI. + STOS, // Perform a possibly-conditional loop, decrementing CX. See the displacement. LOOP, LOOPE, LOOPNE, @@ -129,19 +150,50 @@ enum class Operation: uint8_t { XOR, /// NOP; no further fields. NOP, + /// POP from the stack to destination. + POP, + /// POP from the stack to the flags register. + POPF, + /// PUSH the source to the stack. + PUSH, + /// PUSH the flags register to the stack. + PUSHF, + /// Rotate the destination left through carry the number of bits indicated by source. + RCL, + /// Rotate the destination right through carry the number of bits indicated by source. + RCR, + /// Rotate the destination left the number of bits indicated by source. + ROL, + /// Rotate the destination right the number of bits indicated by source. + ROR, + /// Arithmetic shift left the destination by the number of bits indicated by source. + SAL, + /// Arithmetic shift right the destination by the number of bits indicated by source. + SAR, + /// Logical shift right the destination by the number of bits indicated by source. + SHR, - POP, POPF, PUSH, PUSHF, RCL, RCR, REP, - ROL, ROR, - SAR, SBB, SCAS, SAL, SHR, STC, STD, STI, STOS, SUB, TEST, - WAIT, XCHG, XLAT, - LES, + /// Clear carry flag; no source or destination provided. + CLC, + /// Clear direction flag; no source or destination provided. + CLD, + /// Clear interrupt flag; no source or destination provided. + CLI, + /// Set carry flag. + STC, + /// Set decimal flag. + STD, + /// Set interrupt flag. + STI, - /// Return from interrupt. - IRET, - /// Near return; if source is not ::None then it will be an ::Immediate indicating how many additional bytes to remove from the stack. - RETF, - /// Far return; if source is not ::None then it will be an ::Immediate indicating how many additional bytes to remove from the stack. - RETN, + /// Compares source and destination. + TEST, + + /// Exchanges the contents of the source and destination. + XCHG, + + /// Load AL with DS:[AL+BX]. + XLAT, }; enum class Size: uint8_t { From 9c2c918760326a09ab66d8770b5e3e227a828bb0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 15 Jan 2021 21:07:02 -0500 Subject: [PATCH 52/52] Better sorts by function, corrects TEST description. --- InstructionSets/x86/Instruction.hpp | 35 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index ba13d8bf1..334b01658 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -32,25 +32,16 @@ enum class Operation: uint8_t { AAM, /// ASCII adjust after subtraction; source will be AL and destination will be AX. AAS, - - /// Convert byte into word; source will be AL, destination will be AH. - CBW, - /// Complement carry flag; no source or destination provided. - CMC, - /// Compare; source, destination, operand and displacement will be populated appropriately. - CMP, - /// Compare [bytes or words, per operation size]; source and destination implied to be DS:[SI] and ES:[DI]. - CMPS, - /// Convert word to double word; source will be AX and destination will be DX. - CWD, /// Decimal adjust after addition; source and destination will be AL. DAA, /// Decimal adjust after subtraction; source and destination will be AL. DAS, - /// Decrement; source, destination, operand and displacement will be populated appropriately. - DEC, - /// Increment; source, destination, operand and displacement will be populated appropriately. - INC, + + /// Convert byte into word; source will be AL, destination will be AH. + CBW, + /// Convert word to double word; source will be AX and destination will be DX. + CWD, + /// Escape, for a coprocessor; perform the bus cycles necessary to read the source and destination and perform a NOP. ESC, @@ -59,7 +50,6 @@ enum class Operation: uint8_t { /// Waits until the WAIT input is asserted; if an interrupt occurs then it is serviced but returns to the WAIT. WAIT, - /// Add with carry; source, destination, operand and displacement will be populated appropriately. ADC, /// Add; source, destination, operand and displacement will be populated appropriately. @@ -77,13 +67,16 @@ enum class Operation: uint8_t { /// Signed divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. IDIV, + /// Increment; source, destination, operand and displacement will be populated appropriately. + INC, + /// Decrement; source, destination, operand and displacement will be populated appropriately. + DEC, /// Reads from the port specified by source to the destination. IN, /// Writes from the port specified by destination from the source. OUT, - // Various jumps; see the displacement to calculate targets. JO, JNO, JB, JNB, JE, JNE, JBE, JNBE, JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, @@ -124,6 +117,8 @@ enum class Operation: uint8_t { /// Computes the effective address of the source and loads it into the destination. LEA, + /// Compare [bytes or words, per operation size]; source and destination implied to be DS:[SI] and ES:[DI]. + CMPS, /// Load string; reads from DS:SI into AL or AX, subject to segment override. LODS, /// Move string; moves a byte or word from DS:SI to ES:DI. If a segment override is provided, it overrides the the source. @@ -185,8 +180,12 @@ enum class Operation: uint8_t { STD, /// Set interrupt flag. STI, + /// Complement carry flag; no source or destination provided. + CMC, - /// Compares source and destination. + /// Compare; source, destination, operand and displacement will be populated appropriately. + CMP, + /// Sets flags based on the result of a logical AND of source and destination. TEST, /// Exchanges the contents of the source and destination.