§ Описание

Ревизия 20250705
/* verilator lint_off WIDTHEXPAND */
/* verilator lint_off WIDTHTRUNC */
/* verilator lint_off CASEX */
/* verilator lint_off CASEOVERLAP */
/* verilator lint_off CASEINCOMPLETE */

module T8086
(
    input               clock,      // 25Мгц
    input               reset_n,    // =0 Сброс процессора
    input               ce,         // =1 Активация чипа
    output      [19:0]  a,          // Адрес в общей памяти
    input       [ 7:0]  i,          // Данные из памяти
    output reg  [ 7:0]  o,          // Данные в память
    output reg          w,          // Запись в память,
    output reg  [15:0]  pa,         // Порт адрес
    input       [ 7:0]  pi,         // Входящие данные с порта
    output reg  [ 7:0]  po,         // Исходящие из порта данные
    output reg          pr,         // Сигнал на чтение из порта
    output reg          pw,         // Сигнал на запись в порт
    output reg          halt,       // Остановка процессора
    output              m0          // Первый такт
);

assign m0 = (t == RUN) && (m == 0);
assign a  = cp ? {sgn,4'h0} + ea : {cs,4'h0} + ip;

`define TERM begin term <= 1; m <= 0; end
`define REG  modrm[5:3]

// Запись в Reg8, Reg16
`define W8   {dir, size} <= 2'b10; modrm[5:3]
`define W16  {dir, size} <= 2'b11; modrm[5:3]

// Если cpen=0, то выйти из процедуры считывания операндов
`define CPEN cp <= cpen; if (cpen == 0) begin m1 <= 0; t <= RUN; end

localparam

    RUN         = 0,
    MODRM       = 1,
    WB          = 2,
    PUSH        = 3,
    POP         = 4,
    INTERRUPT   = 5,
    DIV         = 6,
    UNDEF       = 7;

localparam CF  = 0, PF  = 2, AF  = 4, ZF  = 6, SF  = 7, TF  = 8, IF  = 9, DF  = 10, OF = 11;
localparam ADD = 0, OR  = 1, ADC = 2, SBB = 3, AND = 4, SUB = 5, XOR = 6, CMP = 7;
localparam ROL = 0, ROR = 1, RCL = 2, RCR = 3, SHL = 4, SHR = 5, SAL = 6, SAR = 7;

// Для того чтобы не использовать ES:
localparam FS  = 16'hA000, GS = 16'hB800;

// ---------------------------------------------------------------------
// Регистры общего назначения и сегментные
reg [15:0]  ax = 16'hDE0F, cx = 16'hBEEF, dx = 16'hBABE, bx = 16'hF00D,
            sp = 16'h50A0, bp = 16'hF1FA, si = 16'hFACE, di = 16'h0777;
reg [15:0]  es = 16'hFEBA, cs = 16'h0000, ss = 16'h1000, ds = 16'hDAEB;
reg [15:0]  ip = 16'h0000;
//                      0DIT SZ A  P C
reg [11:0]  flags = 12'b0000_0000_0010;
// ---------------------------------------------------------------------
reg         cp;                     // =1 Указатель на SGN:EA =0 Иначе CS:IP
reg         cpm;                    // =0 Устанавливается cp после MODRM
reg         size;                   // =1 16bit =0 8bit
reg         dir;                    // =0 rm,r; =1 r,rm
reg         term;                   // =1 Конец инструкции
reg         over;                   // =1 Сегмент переопределен
reg         cpen;                   // =0 То пропускает чтение операндов
reg [ 3:0]  t, next;                // Исполняемая команда в данный момент
reg [ 3:0]  m, m1;                  // Фаза исполнения T, MODRM и субфаза
reg [ 2:0]  m2, m3, m4;             // Фаза исполнения WB, PUSH/POP
reg [ 2:0]  alu;                    // Функция АЛУ или сдвигов
reg [ 7:0]  opcache, modrm;         // Кеш опкода
reg [ 7:0]  interrupt;              // Номер прерывания
reg [ 1:0]  rep;                    // Наличие REP:
reg [15:0]  sgn, ea;                // Выбранный SEGMENT:EA
reg [15:0]  op1, op2, wb, t16;      // Операнды; wb-что записывать
// ---------------------------------------------------------------------

wire [ 7:0] opcode  = m ? opcache : i;
wire        o1      = opcode[1];
wire [15:0] ipn     = ip + 1;
wire [15:0] sign    = {{8{i[7]}}, i};

// Мультиплексор на выбор регистров из диапазона [2:0]
wire [15:0] i20 =
    i[2:0] == 3'h0 ? (size ? ax : ax[ 7:0]) :
    i[2:0] == 3'h1 ? (size ? cx : cx[ 7:0]) :
    i[2:0] == 3'h2 ? (size ? dx : dx[ 7:0]) :
    i[2:0] == 3'h3 ? (size ? bx : bx[ 7:0]) :
    i[2:0] == 3'h4 ? (size ? sp : ax[15:8]) :
    i[2:0] == 3'h5 ? (size ? bp : cx[15:8]) :
    i[2:0] == 3'h6 ? (size ? si : dx[15:8]) :
                     (size ? di : bx[15:8]);

// Мультиплексор на выбор регистров из диапазона [5:3]
wire [15:0] i53 =
    i[5:3] == 3'h0 ? (size ? ax : ax[ 7:0]) :
    i[5:3] == 3'h1 ? (size ? cx : cx[ 7:0]) :
    i[5:3] == 3'h2 ? (size ? dx : dx[ 7:0]) :
    i[5:3] == 3'h3 ? (size ? bx : bx[ 7:0]) :
    i[5:3] == 3'h4 ? (size ? sp : ax[15:8]) :
    i[5:3] == 3'h5 ? (size ? bp : cx[15:8]) :
    i[5:3] == 3'h6 ? (size ? si : dx[15:8]) :
                     (size ? di : bx[15:8]);

// 16-битный регистр
wire [15:0] r16 =
    i[2:0] == 3'h0 ? ax : i[2:0] == 3'h1 ? cx :
    i[2:0] == 3'h2 ? dx : i[2:0] == 3'h3 ? bx :
    i[2:0] == 3'h4 ? sp : i[2:0] == 3'h5 ? bp :
    i[2:0] == 3'h6 ? si : di;

// ---------------------------------------------------------------------
// Базовое арифметическо-логическое устройство
// ---------------------------------------------------------------------

wire [16:0] ar =
    alu == ADD ? op1 + op2 :
    alu == ADC ? op1 + op2 + flags[CF] :
    alu == SBB ? op1 - op2 - flags[CF] :
    alu == AND ? op1 & op2 :
    alu == XOR ? op1 ^ op2 :
    alu == OR  ? op1 | op2 :
                 op1 - op2;

wire [3:0]  top = size ? 15 : 7;
wire        isa = alu == ADD || alu == ADC;
wire        isl = alu != AND && alu != OR && alu != XOR;

wire new_o = (op1[top] ^ op2[top] ^ isa) & (op1[top] ^ ar[top]);
wire new_s = ar[top];
wire new_z = 0 == (size ? ar[15:0] : ar[7:0]);
wire new_a = op1[4] ^ op2[4] ^ ar[4];
wire new_p = ~^ar[7:0];
wire new_c = ar[top + 1];

wire [11:0] af = {isl&new_o, flags[10:8], new_s, new_z, 1'b0, isl&new_a, 1'b0, new_p, 1'b1, isl&new_c};

// ---------------------------------------------------------------------
// Десятичная коррекция DAA, DAS, AAA, AAS
// ---------------------------------------------------------------------

wire        das   = opcode[3];
wire        daa_a = flags[AF] || ax[3:0] > 8'h09;
wire        daa_c = flags[CF] || ax[7:0] > 8'h9F || daa_t[8];
wire [ 8:0] daa_t = das ? (ax[7:0] - (daa_a ? 8'h06 : 0)) : (ax[7:0] + (daa_a ? 8'h06 : 0));
wire [ 7:0] daa   = das ? (daa_t   - (daa_c ? 8'h60 : 0)) : (daa_t   + (daa_c ? 8'h60 : 0));
wire [11:0] daa_f = {flags[11:8], daa[7], daa == 0, 1'b0, daa_a | flags[AF], 1'b0, ~^daa, 1'b1, daa_c | flags[CF]};
wire [ 3:0] aaa_a = das ? (ax[ 7:0] - (daa_a ? 8'h06 : 0)) : (ax[ 7:0] + (daa_a ? 8'h06 : 0));
wire [ 7:0] aaa_b = das ? (ax[15:8] - (daa_a ? 8'h01 : 0)) : (ax[15:8] + (daa_a ? 8'h01 : 0));

// Знаковое умножение это просто умножение обычное
reg         imulw;
wire [31:0] imul_r = op1 * op2;
wire        imul_o = |imul_r[31:16];
wire        imul_z =  imul_r[15:0] == 0;

// ---------------------------------------------------------------------
// УСЛОВИЯ
// ---------------------------------------------------------------------

// Вычисление условий
wire [7:0] branch =
{
    (flags[SF] ^ flags[OF]) | flags[ZF], // 7: (ZF=1) OR (SF!=OF)
    (flags[SF] ^ flags[OF]),             // 6: SF != OF
     flags[PF],
     flags[SF],
     flags[CF] | flags[ZF],              // 3: CF != OF
     flags[ZF],
     flags[CF],
     flags[OF]
};

wire [15:0] sinc = flags[DF] ? si - (size ? 2 : 1) : si + (size ? 2 : 1);
wire [15:0] dinc = flags[DF] ? di - (size ? 2 : 1) : di + (size ? 2 : 1);

// Разрешение выполнения инструкции с REP: или без
wire        repa = (rep[1] && cx || rep[1] == 0);           // Начало исполнения
wire        repb = (rep[1] && cx != 1);                     // Продолжение исполения
wire [15:0] repc = (i[0] ? 2:1)*(rep[1] ? cx-1 : 0);        // Количество отступов для LODSx

// -----------------------------------------------------------------------------
// Вычисление сдвигов
// -----------------------------------------------------------------------------

wire [15:0] ri = size ? op1 : {op1[7:0], op1[7:0]};
wire [ 3:0] rs = size ? op2[3:0] : op2[2:0];

// Параллельный сдвиги
wire [32:0] _rol = {1'b0, ri, ri} << rs;
wire [32:0] _ror = {ri, ri, 1'b0} >> rs;
wire [32:0] _rcl = {1'b0, ri, flags[CF], ri[15:1]} << rs;
wire [32:0] _rcr = {ri[14:0], flags[CF], ri, 1'b0} >> rs;
wire [16:0] _shl = ri << op2[7:0];
wire [16:0] _shr = {ri, flags[CF]} >> op2[7:0];
wire [32:0] _sar = {{16{ri[15]}}, ri, 1'b0} >> op2[7:0];

// Результат сдвигов
wire [15:0] barr =
    alu == ROL ? (size ? _rol[31:16] : _rol[23:16]) :
    alu == RCL ? (size ? _rcl[31:16] : _rcl[23:16]) :
    alu == ROR ? (size ? _ror[16:1]  : _ror[16:9])  :
    alu == RCR ? (size ? _rcr[16:1]  : _rcr[16:9])  :
    alu == SHR ? (size ? _shr[16:1]  : _shr[16:9])  :
    alu == SAR ? (size ? _sar[16:1]  : _sar[16:9])  :
                 (size ? _shl[15:0]  : _shl[ 7:0]);  // SHL, SAL

wire rtfl = alu == SHL || alu == SHR || alu == SAL || alu == SHR;

// Флаг переполнения OF
wire rtof =
    alu == SHR ? barr[top] :
    alu == SAR ? 1'b0 : barr[top-1] ^ barr[top];

// Флаг переноса CF после выполнения сдвига
wire rtcf =
    alu == ROL ? _rol[32] : alu == RCL ? _rcl[32] :
    alu == ROR ? _ror[0]  : alu == RCR ? _rcr[0] :
    alu == SHL ? _shl[16] : alu == SHR ? _shr[0] :
    alu == SAR ? _sar[0]  : 1'b0;

// SZP флаги
wire rtsf = rtfl ? barr[top] : flags[SF];
wire rtzf = rtfl ? (size ? barr[15:0] : barr[7:0]) == 0 : flags[ZF];
wire rtpf = rtfl ?  ~^barr[7:0] : flags[PF];

wire [11:0] barf = {rtof, flags[10:8], rtsf, rtzf, 1'b0, flags[AF], 1'b0, rtpf, 1'b1, rtcf};

// -----------------------------------------------------------------------------
// Модуль деления
// -----------------------------------------------------------------------------

reg         divs;
reg  [31:0] diva, divb, divr;

// Запрошенный 32 или 16 битный
wire [31:0] divi  = size ? {dx, ax} : {ax, 16'h0000};

// ШАГ 1,2,3,4
wire [31:0] div1  = {divr [30:0], diva [31]};       // Сдвиг
wire [31:0] div2  = {div1r[30:0], div1a[31]};
wire [31:0] div3  = {div2r[30:0], div2a[31]};
wire [31:0] div4  = {div3r[30:0], div3a[31]};
wire [32:0] div1c = div1 - divb;                    // Сравнение после сдвига
wire [32:0] div2c = div2 - divb;
wire [32:0] div3c = div3 - divb;
wire [32:0] div4c = div4 - divb;
wire [31:0] div1r = div1c[32] ? div1 : div1c[31:0]; // Вычисление нового остатка
wire [31:0] div2r = div2c[32] ? div2 : div2c[31:0];
wire [31:0] div3r = div3c[32] ? div3 : div3c[31:0];
wire [31:0] div4r = div4c[32] ? div4 : div4c[31:0];
wire [31:0] div1a = {diva [30:0], ~div1c[32]};      // Заполнение результата
wire [31:0] div2a = {div1a[30:0], ~div2c[32]};
wire [31:0] div3a = {div2a[30:0], ~div3c[32]};
wire [31:0] div4a = {div3a[30:0], ~div4c[32]};
// -----------------------------------------------------------------------------


always @(posedge clock)
// Сброс процессора
if (reset_n == 0) begin

    t       <= RUN;              // Исполнение инструкции начинается сразу
    m       <= 0;
    cp      <= 0;                // Установить на CS:IP
    cs      <= 0;
    ip      <= 0;
    ea      <= 0;
    sgn     <= 0;
    rep     <= 0;
    w       <= 0;
    term    <= 1;
    over    <= 0;
    halt    <= 0;
    modrm   <= 0;

// Запуск выполнения команд процессора
end else if (ce) begin

    w    <= 0;
    pw   <= 0;

    case (t)

    // -------------------------------------------------------------
    // ВЫПОЛНЕИЕ ИНСТРУКЦИИ
    // -------------------------------------------------------------
    RUN: begin

        // Защелкивание опкода на первом такте
        if (m == 0) begin

            // Сброс префиксов по достижении конца инструкции (на следующем такте)
            if (term) begin sgn <= ds; rep <= 2'b00; over <= 0; end

            m       <= 1;
            m1      <= 0;
            m2      <= 0;
            m3      <= 0;
            ip      <= ipn;
            next    <= RUN;
            cpm     <= 1;
            cpen    <= 1;
            term    <= 0;
            opcache <= i;
            dir     <= i[1];
            size    <= i[0];

        end

        casex (opcode)

        8'b00xxx0xx: case (m) // ### AL-операции с операндами ModRM [3T+]
        0: begin t <= MODRM; alu <= opcode[5:3]; end
        1: begin t <= alu == CMP ? RUN : WB; wb <= ar; flags <= af; `TERM end
        endcase

        8'b00xxx10x: case (m) // ### AL-операции AL/AX + imm [3/4T]
        0: begin alu <= opcode[5:3];        op1 <= i[0] ? ax : ax[7:0]; end
        1: begin ip <= ipn; m <= size?2:3;  op2 <= i; end
        2: begin ip <= ipn; m <= 3;         op2[15:8] <= i; end
        3: begin flags <= af; if (alu != CMP) ax <= size ? ar : {ax[15:8], ar[7:0]}; `TERM end
        endcase

        8'b000xx110: case (m) // ### PUSH es/cs/ss/ds [4T]
        0: begin t <= PUSH; case (i[4:3]) 0:wb<=es; 1:wb<=cs; 2:wb<=ss; 3:wb<=ds; endcase `TERM; end
        endcase

        8'b00001111: case (m) // ### ::Extended::
        0: begin end
        endcase

        8'b000xx111: case (m) // ### POP es/../ss/ds [5T]
        0: begin t <= POP; end
        1: begin case (opcode[4:3]) 0:es<=wb; 2:ss<=wb; 3:ds<=wb; endcase `TERM; end
        endcase

        8'b001xx110: case (m) // ### Префикс es/cs/ss/ds: [1T]
        0: begin over <= 1; case (i[4:3]) 0:sgn<=es; 1:sgn<=cs; 2:sgn<=ss; 3:sgn<=ds; endcase m <= 0; end
        endcase

        8'b0010x111: case (m) // ### Десятичная коррекция [1T]
        0: begin ax[7:0] <= daa; flags <= daa_f; `TERM; end
        endcase

        8'b0011x111: case (m) // ### ASCII коррекция [1T]
        0: begin ax <= {aaa_b, ax[7:4], aaa_a}; {flags[AF], flags[CF]} <= {daa_a, daa_a}; `TERM; end
        endcase

        8'b0100xxxx: case (m) // ### INC/DEC r16 [4T]
        0: begin ip <= ip; {dir, size} <= 2'b11; alu <= opcode[3] ? SUB : ADD; end
        1: begin ip <= ipn; op1 <= i20; op2 <= 1; m <= 2; end
        2: begin wb <= ar; `REG <= opcode[2:0]; t <= WB; flags <= {af[11:1], flags[CF]}; `TERM; end
        endcase

        8'b01010xxx: case (m) // ### PUSH r16 [5T]
        0: begin ip <= ip; size <= 1'b1; end
        1: begin ip <= ipn; wb <= i20; t <= PUSH; `TERM; end
        endcase

        8'b01011xxx: case (m) // ### POP r16 [6T]
        0: begin t <= POP; {size, dir} <= 2'b11; end
        1: begin t <= WB; `REG <= opcode[2:0]; `TERM; end
        endcase

        8'b01100000: case (m) // ### PUSHA [18T]
        0: begin ea <= sp; sgn <= ss; cp <= 1; end
        1: begin

            w   <= 1;
            m1  <= m1 + 1;
            ea  <= ea - 1;

            if (m1 == 15) begin m <= 2; m1 <= 0; end

            case (m1)
            0:  o <= ax[15:8];  1: o <= ax[7:0];
            2:  o <= cx[15:8];  3: o <= cx[7:0];
            4:  o <= dx[15:8];  5: o <= dx[7:0];
            6:  o <= bx[15:8];  7: o <= bx[7:0];
            8:  o <= sp[15:8];  9: o <= sp[7:0];
            10: o <= bp[15:8]; 11: o <= bp[7:0];
            12: o <= si[15:8]; 13: o <= si[7:0];
            14: o <= di[15:8]; 15: o <= di[7:0];
            endcase

        end
        2: begin cp <= 0; sp <= sp - 16; `TERM; end
        endcase

        8'b01100001: case (m) // ### POPA [18T]
        0: begin ea <= sp; sgn <= ss; cp <= 1; end
        1: begin

            m1  <= m1 + 1;
            ea  <= ea + 1;

            if (m1 == 15) begin m <= 2; m1 <= 0; end

            case (m1)
            0:  di[ 7:0] <= i;  1: di[15:8] <= i;
            2:  si[ 7:0] <= i;  3: si[15:8] <= i;
            4:  bp[ 7:0] <= i;  5: bp[15:8] <= i;
            6:  sp[ 7:0] <= i;  7: sp[15:8] <= i;
            8:  bx[ 7:0] <= i;  9: bx[15:8] <= i;
            10: dx[ 7:0] <= i; 11: dx[15:8] <= i;
            12: cx[ 7:0] <= i; 13: cx[15:8] <= i;
            14: ax[ 7:0] <= i; 15: ax[15:8] <= i;
            endcase

        end
        2: begin cp <= 0; sp <= ea; `TERM; end
        endcase

        8'b0110010x: case (m) // ### FS: GS: [1T]
        0: begin m <= 0; over <= 1; sgn <= i[0] ? GS : FS; end
        endcase

        8'b011010x0: case (m) // ### PUSH s8/u16 [5/6T]
        1: begin ip <= ipn; wb <= o1 ? sign : i; if (op1) begin t <= PUSH; `TERM; end else m <= 2; end
        2: begin ip <= ipn; wb[15:8] <= i; t <= PUSH; `TERM; end
        endcase

        8'b011010x1: case (m) // ### IMUL rm16,rm,imm [7T+]
        0: begin {dir, size} <= 2'b11; t <= MODRM; end
        1: begin cp  <= 0;       m <= 2;  end
        2: begin op2 <= sign;    m <= 3 + o1; ip <= ipn; end
        3: begin op2[15:8] <= i; m <= 4;      ip <= ipn; end
        4: begin

            t  <= WB;
            wb <= imul_r[15:0];

            flags[CF] <= imul_o;
            flags[OF] <= imul_o;
            flags[ZF] <= imul_z;

            `TERM;

        end
        endcase

        8'b0111xxxx: case (m) // ### JCC short [1/2T]

            0: if (branch[i[3:1]] == i[0]) begin ip <= ip + 2; `TERM; end
            1: begin ip <= ip + 1 + sign; `TERM; end

        endcase

        8'b100000xx: case (m) // ### ALU GROUP [5*T]

            0: begin t <= MODRM; dir <= 0; cpm <= 0; end
            1: begin

                ip  <= ip + 1;
                alu <= `REG;
                op2 <= opcode[1:0] == 3 ? sign : i;
                m   <= opcode[1:0] == 1 ? 2 : 3;

            end
            2: begin m <= 3; op2[15:8] <= i; ip <= ip + 1; end
            3: begin m <= (alu == CMP ? RUN : WB); wb <= ar; flags <= af; `TERM; end

        endcase

        8'b1000010x: case (m) // ### TEST rm, r [3T+]

            0: begin t <= MODRM; alu <= AND; end
            1: begin flags <= af; `TERM; end

        endcase

        8'b1000011x: case (m) // ### XCHG r,rm [6*T]

            0: begin t <= MODRM; end
            1: begin t <= WB; wb <= op2; m <= 2; end
            2: begin t <= WB; wb <= op1; dir <= 0; `TERM; end

        endcase

        8'b100010xx: case (m) // ### MOV rm,r|r,rm [4*T]

            0: begin t <= MODRM; cpen <= i[1]; end
            1: begin t <= WB; wb <= op2; cp <= 1; `TERM; end

        endcase

        8'b10001100: case (m) // ### MOV rm, sreg

            0: begin t <= MODRM; cpen <= 0; {dir, size} <= 2'b01; end
            1: begin t <= WB; case (`REG) 0: wb <= es; 1: wb <= cs; 2: wb <= ss; 3: wb <= ds; endcase `TERM; end

        endcase

        8'b10001101: case (m) // ### LEA r, rm [4T+]

            0: begin t <= MODRM; cpen <= 0; {dir, size} <= 2'b11; end
            1: begin t <= WB; wb <= ea; `TERM; end

        endcase

        8'b10001110: case (m) // ### MOV sreg, rm

            0: begin t <= MODRM; {dir, size} <= 2'b11; end
            1: begin case (`REG) 0: es <= op2; 2: ss <= op2; 3: ds <= op2; endcase `TERM; end

        endcase

        8'b10001111: case (m) // ### POP rm

            0: begin t <= POP;   end
            1: begin t <= MODRM; m <= 2; cpen <= 0; dir <= 0; end
            2: begin t <= WB; cp <= 1; `TERM; end

        endcase

        8'b10010000,          // ### FWAIT
        8'b11110000,          // ### LOCK:
        8'b10011011: case (m) // ### NOP [1T]

            0: begin `TERM; end

        endcase

        8'b10010xxx: case (m) // ### 2T XCHG ax, r [2T]

            0: begin

                ax   <= r16;
                wb   <= ax;
                t    <= WB;
                `W16 <= opcode[2:0];
                `TERM;

            end

        endcase

        8'b10011000: case (m) // ### CBW

            0: begin ax <= {{8{ax[7]}}, ax[7:0]}; `TERM; end

        endcase

        8'b10011001: case (m) // ### CWD

            0: begin dx <= {16{ax[15]}}; `TERM; end

        endcase

        8'b10011010: case (m) // ### CALL FAR [13T]

            0: begin next <= RUN; end
            1: begin m <= 2; ip <= ip + 1; op1[ 7:0] <= i; end
            2: begin m <= 3; ip <= ip + 1; op1[15:8] <= i; end
            3: begin m <= 4; ip <= ip + 1; op2[ 7:0] <= i; end
            4: begin m <= 5; ip <= ip + 1; op2[15:8] <= i; t <= PUSH; wb <= cs; end
            5: begin m <= 6; wb <= ip;  t  <= PUSH; end
            6: begin `TERM;  ip <= op1; cs <= op2; end

        endcase

        8'b10011100: case (m) // ### PUSHF [4T]

            0: begin t <= PUSH; wb <= flags; `TERM; end

        endcase

        8'b10011101: case (m) // ## POPF [5T]

            0: begin t <= POP; end
            1: begin flags <= wb | 2; `TERM; end

        endcase

        8'b10011110: case (m) // ### SAHF

            0: begin flags <= ax[15:8]; `TERM; end

        endcase

        8'b10011111: case (m) // ### LAHF

            0: begin ax[15:8]  <= flags[7:0] | 2; `TERM; end

        endcase

        8'b101000xx: case (m) // ### MOV a,[m] | [m],a

            // Прочесть адрес
            1: begin ea[ 7:0] <= i; ip <= ip + 1; m <= 2; end
            2: begin ea[15:8] <= i; ip <= ip + 1; m <= dir ? 3 : 6; cp <= 1; end

            // Запись A в память
            3: begin w <= 1; o <= ax[ 7:0]; m <= size ? 4 : 5; end
            4: begin w <= 1; o <= ax[15:8]; m <= 5; ea <= ea + 1; end
            5: begin `TERM; cp <= 0; end

            // Чтение A из памяти
            6: begin m <= 7; ax[ 7:0] <= i; ea <= ea + 1; if (!size) begin `TERM; cp <= 0; end end
            7: begin `TERM;  ax[15:8] <= i; cp <= 0; end

        endcase

        8'b1010100x: case (m) // ### TEST a, i [4*T]

            0: begin alu <= AND; op1 <= opcode[0] ? ax : ax[7:0]; end
            1: begin m   <= size ? 2 : 3; op2       <= i; ip <= ip + 1; end
            2: begin m   <= 3;            op2[15:8] <= i; ip <= ip + 1; end
            3: begin flags <= af; `TERM; end

        endcase

        8'b1010010x: case (m) // ### MOVSx [2*+4/2*CX]

            1: begin

                m   <= 2;
                cp  <= repa;
                op1 <= sgn;
                ea  <= si;

                if (!repa) begin `TERM; end

            end

            // Запись младшего байта [size=0]
            2: begin

                m   <= size ? 3 : 5;
                wb  <= i;
                o   <= i;

                if (size)  ea <= ea + 1;
                else begin ea <= di; sgn <= es; w <= 1; end

            end

            // Чтение старшего байта, запись младшего байта [size=1]
            3: begin

                m   <= 4;
                wb  <= i;
                w   <= 1;
                ea  <= di;
                o   <= wb[7:0];
                sgn <= es;

            end

            // Запись старшего байта
            4: begin t <= 5; ea <= ea + 1; w <= 1; o <= wb[7:0]; end

            // Инкремент или декремент SI/DI, выключение записи
            5: begin

                t   <= 2;
                sgn <= op1;
                ea  <= sinc;
                si  <= sinc;
                di  <= dinc;
                cx  <= cx - rep[1];

                if (!repb) begin `TERM; cp <= 0; end

            end

        endcase

        8'b1010011x: case (m) // ### CMPSx :: (3|5)*CX+2*

            1: begin

                m   <= 2;
                cp  <= repa;
                ea  <= si;
                alu <= SUB;

                if (!repa) begin `TERM; end

            end

            // Чтение DS:SI
            2: begin

                t16 <= sgn;
                t   <= size ? 3 : 5;
                ea  <= size ? ea + 1 : di;
                sgn <= size ? sgn : es;
                op1 <= i;

            end

            // Старший байт из SI+1
            3: begin m <= 4; op1[15:8] <= i; sgn <= es; ea <= di; end

            // Чтение из ES:DI
            4: begin m <= size ? 5 : 6; op2 <= i; ea <= ea + 1; end
            5: begin m <= 6; op2[15:8] <= i; end

            // Сравнение и повтор цикла (если необходимо)
            6: begin

                m       <= 2;
                ea      <= sinc;
                si      <= sinc;
                di      <= dinc;
                flags   <= af;
                sgn     <= t16;
                cx      <= cx - rep[1];

                if (!(repb && (rep[0] == af[ZF]))) begin `TERM; cp <= 0; end

            end

        endcase

        8'b1010101x: case (m) // ### STOSx :: 3+(2/1)*CX

            1: begin m <= 2; cp <= repa; if (!repa) begin `TERM; end end
            2: begin // STOSB

                m   <= size ? 3 : 2;
                o   <= ax[7:0];
                sgn <= es;
                ea  <= di;
                w   <= 1;
                di  <= flags[DF] ? di - 1 : di + 1;

                if (!size) begin cx <= cx - rep[1]; if (!repb) t <= 4; end

            end
            3: begin // STOSW

                m   <= repb ? 2 : 4;
                w   <= 1;
                ea  <= ea + 1;
                o   <= ax[15:8];
                di  <= flags[DF] ? di - 1 : di + 1;
                cx  <= cx - rep[1];

            end

            4: begin cp <= 0; `TERM; end

        endcase

        8'b1010110x: case (m) // ### LODSx :: 3*

            1: begin

                m  <= 2;
                cp <= repa;
                ea <= flags[DF] ? si - repc : si + repc;

                if (!repa) begin `TERM; end

            end

            2: begin

                m       <= 3;
                ea      <= ea + 1;
                si      <= flags[DF] ? ea-1-size : ea+1+size;
                ax[7:0] <= i;

                if (!size) begin `TERM; end

            end

            3: begin ax[15:8] <= i; `TERM; cp <= 0; end

        endcase

        8'b1010111x: case (m) // ### SCASx :: 2*+(2|3)*CX

            1: begin

                m   <= 2;
                cp  <= repa;
                ea  <= di;
                alu <= SUB;
                sgn <= es;

                if (!repa) begin `TERM; end

            end

            // Прочитать младший байт
            2: begin

                t   <= size ? 3 : 4;
                op1 <= size ? ax : ax[7:0];
                op2 <= i;
                ea  <= ea + 1;

            end

            // Прочитать старший байт
            3: begin m <= 4; op2[15:8] <= i; di <= dinc; end

            // Сравнить A со значением из памяти
            4: begin

                t       <= 2;
                flags   <= af;
                di      <= dinc;
                ea      <= dinc;
                cx      <= cx - rep[1];

                if (!(repb && (rep[0] == af[ZF]))) begin `TERM; cp <= 0; end

            end

        endcase

        8'b1011xxxx: case (m) // ### MOV r,i [3*T]

            1: begin ip <= ip + 1; wb <= i; m <= 2; if (!opcode[3]) begin t <= WB; `W8 <= opcode[2:0]; `TERM; end end
            2: begin ip <= ip + 1; wb[15:8] <= i; t <= WB; `W16 <= opcode[2:0]; `TERM; end

        endcase

        8'b1100000x: case (m) // ### [ROT] rm, i

            0: begin t <= MODRM; dir <= 0; cpm <= 0; end
            1: begin m <= 2; op2 <= i; ip <= ip + 1; alu <= modrm[5:3]; end
            2: begin t <= WB; wb <= barr; flags <= barf; `TERM; end

        endcase

        8'b1100001x: case (m) // ### RET;RET imm [5/7T]

            0: begin t <= POP; m <= opcode[0] ? 3 : 1; t16 <= 0; end
            1: begin m <= 2; t16[ 7:0] <= i; ip <= ip + 1; end
            2: begin m <= 3; t16[15:8] <= i; end
            3: begin ip <= wb; sp <= sp + t16; `TERM; end

        endcase

        8'b1100010x: case (m) // ### LES|LDS r,m

            0: begin t <= MODRM; {dir, size} <= 2'b11; end
            1: begin m <= 2; ea <= ea + 2; end
            2: begin m <= 3; ea <= ea + 1; wb[7:0] <= i; end
            3: begin t <= WB; wb <= op2; if (opcode[0]) ds <= {i, wb[7:0]}; else es <= {i, wb[7:0]}; `TERM; end

        endcase

        8'b1100011x: case (m) // ### MOV rm, i

            0: begin t <= MODRM; {cpm, cpen, dir} <= 0; end
            1: begin wb <= i; ip <= ip + 1; if (size) m <= 2; else begin t <= WB; `TERM; end end
            2: begin wb[15:8] <= i; ip <= ip + 1; t <= WB; `TERM; end

        endcase

        8'b1100101x: case (m) // ### RETF; RETF i16

            0: begin t <= POP; end
            1: begin t <= POP; m  <= 2;  op1 <= wb;  op2 <= i; ip <= ip + 1; end
            2: begin `TERM;    cs <= wb; ip  <= op1; if (!opcode[0]) sp <= sp + {i, op2[7:0]}; end

        endcase

        8'b110011x0: case (m) // ### INT 3; INTO

            0: begin

                t <= (i[1] && flags[OF]) || !i[1] ? INTERRUPT : RUN;
                interrupt <= i[1] ? 4 : 3;
                `TERM;

            end

        endcase

        8'b11001101: case (m) // ### INT i

            1: begin t <= INTERRUPT; interrupt <= i; ip <= ip + 1; `TERM; end

        endcase

        8'b11001111: case (m) // ### IRET

            0: begin t <= POP; end
            1: begin t <= POP; m <= 2; ip <= wb; end
            2: begin t <= POP; m <= 3; cs <= wb; end
            3: begin `TERM; flags <= wb[11:0] | 2; end

        endcase

        8'b110100xx: case (m) // ### [ROT] rm, (1|cl)

            0: begin t <= MODRM; dir <= 0; end
            1: begin m <= 2; alu <= modrm[5:3]; op2 <= opcode[1] ? cx[7:0] : 1; end
            2: begin t <= WB; wb <= barr; flags <= barf; `TERM; end

        endcase

        8'b11010110: case (m) // ### SALC 1T

            0: begin ax[7:0] <= {8{flags[CF]}}; `TERM; end

        endcase

        8'b11010111: case (m) // ### XLATB

            0: begin ea <= bx; cp <= 1; end
            1: begin ax[7:0] <= i; cp <= 0; `TERM; end

        endcase

        8'b11011xxx: case (m) // ### FPU

            0: begin t <= MODRM; {cpen, dir, size, cpm} <= 4'b0; `TERM; end

        endcase

        8'b1110000x,
        8'b11100010: case (m) // ### LOOPN?Z

            0: begin

                cx <= cx - 1;
                if (cx == 1 || (!i[1] && flags[ZF] ^ i[0])) begin ip <= ip + 2; `TERM; end

            end
            1: begin ip <= ip + 1 + sign; `TERM; end

        endcase

        8'b11100011: case (m) // ### JCXZ x

            0: begin if (cx) begin ip <= ip + 2; `TERM; end end
            1: begin ip <= ip + 1 + sign; `TERM; end

        endcase

        8'b1110x10x: case (m) // ### IN a,p 3/4T*

            0: begin m <= i[3] ? 2 : 1; pa <= dx; pr <= i[3]; end
            1: begin m <= 2; pr <= 1; pa <= i; ip <= ip + 1; end
            2: begin m <= 3; pr <= 1; pa <= size ? pa + 1 : pa; end
            3: begin m <= 4; ax[7:0] <= pi; if (size == 0) begin `TERM; end end
            4: begin ax[15:8] <= pi; `TERM; end

        endcase

        8'b1110x11x: case (m) // ### OUT p,a 2/3T

            1: begin

                m <= 2;

                pa  <= opcode[3] ? dx : i;
                po  <= ax[7:0];
                pw  <= 1;

                if (!opcode[3]) ip <= ip + 1;
                if (!size) begin `TERM; end

            end
            2: begin

                pa  <= pa + 1;
                po  <= ax[15:8];
                pw  <= 1;
                `TERM;

            end

        endcase

        8'b11101000: case (m) // ### CALL b16 6T

            1: begin m <= 2;    ip <= ip + 1; ea <= i; end
            2: begin t <= PUSH; wb <= ip + 1; ip <= ip + 1 + {i, ea[7:0]}; `TERM; end

        endcase

        8'b11101001: case (m) // ### JMP o16 3T

            1: begin ea <= i; ip <= ip + 1; m <= 2; end
            2: begin ip <= ip + 1 + {i, ea[7:0]}; `TERM; end

        endcase

        8'b11101010: case (m) // ### JMP far 5T

            // Прочитаьть 4 байта для нового CS:IP
            1: begin ip <= ip + 1; m <= 2; ea       <= i; end
            2: begin ip <= ip + 1; m <= 3; ea[15:8] <= i; end
            3: begin ip <= ip + 1; m <= 4; op1      <= i; end
            4: begin ip <= ea;     cs <= {i, op1[7:0]}; `TERM; end

        endcase

        8'b11101011: case (m) // ### JMP b 2T

            1: begin ip <= ip + sign + 1; `TERM; end

        endcase

        8'b11110001: case (m) // ### INT 1

            0: begin t <= INTERRUPT; interrupt <= 1; `TERM; end

        endcase

        8'b1111001x: case (m) // ### REPNZ, REPZ

            0: begin m <= 0; rep <= i[1:0]; end

        endcase

        8'b11110100: case (m) // ### 1T HLT -- Остановка процессора

            0: begin ip <= ip; halt <= 1; `TERM; end

        endcase

        8'b11110101: case (m) // ### CMC

            0: begin flags[CF] <= ~flags[CF]; `TERM; end

        endcase

        8'b1111100x: case (m) // ### CLC, STC

            0: begin flags[CF] <= i[0]; `TERM; end

        endcase

        8'b1111101x: case (m) // ### CLI, STI

            0: begin flags[IF] <= i[0]; `TERM; end

        endcase

        8'b1111110x: case (m) // ### CLD, STD

            0: begin flags[DF] <= i[0]; `TERM; end

        endcase

        8'b1111011x: case (m) // ### GROUP #3 rm, op [F6-F7]

            // Запрос операндов
            0: begin t <= MODRM; cpm <= 0; dir <= 1'b0; end

            // Исполнение инструкции
            default: case (`REG)

                // 5T+ TEST imm8/16
                0, 1: case (m)

                    1: begin m  <= 2; alu <= AND; end
                    2: begin ip <= ip + 1; op2[15:0] <= i; m <= size ? 3 : 4; end
                    3: begin ip <= ip + 1; op2[15:8] <= i; m <= 4; end
                    4: begin flags <= af; `TERM; end

                endcase

                // 4T+ NOT rm
                2: begin wb <= ~op1; t <= WB; `TERM; end

                // 5T+ NEG rm
                3: case (m)

                    1: begin m <= 2; alu <= SUB; op2 <= op1; op1 <= 0; end
                    2: begin t <= WB; wb <= ar; flags <= af; `TERM; end

                endcase

                // 4T+ MUL, IMUL rm [IMUL знакорасширяется op1/op2]
                4, 5: case (m)

                    1: begin

                        m       <= 2;
                        op1     <= size ? op1 : {modrm[3] ? {8{op1[7]}} : 8'h00, op1[7:0]};
                        op2     <= size ? ax  : {modrm[3] ? {8{ ax[7]}} : 8'h00,  ax[7:0]};
                        imulw   <= modrm[3];

                    end
                    2: begin

                        if (size) {dx, ax} <= imul_r[31:0]; else ax <= imul_r[15:0];

                        flags[ZF] <= imul_z;
                        flags[CF] <= imul_o;
                        flags[OF] <= imul_o;

                        `TERM;

                    end

                endcase

                // DIV [op1, op2] Беззнаковое деление
                // IDIV Деление со знаком
                6, 7: case (m)

                    // Запрос
                    1: begin

                        m    <= 2;
                        t    <= DIV;
                        divr <= 0;
                        op1  <= size ? 8 : 4;

                        if (modrm[3]) begin

                            divs <= divi[31] ^ op1[size ? 15 : 7];
                            diva <= divi[31] ? -divi : divi;
                            divb <= size ? (op1[15] ? -op1 : op1) : (op1[7] ? -op1[7:0] : op1[7:0]);

                        end else begin

                            divs <= 0;
                            diva <= divi;
                            divb <= op1;

                        end

                    end

                    // Результат
                    2: begin

                        // #0 Overflow если в старшем слове или байте есть ненулевое значение
                        t <= (size ? diva[31:16] : diva[15:8]) ? INTERRUPT : RUN;
                        interrupt <= 0;

                        // Результат DIV или IDIV
                        if (size) {dx, ax} <= {sign ? -divr[15:0] : divr[15:0], sign ? -diva[15:0] : diva[15:0]};
                        else      ax       <= {sign ? -divr[ 7:0] : divr[ 7:0], sign ? -diva[ 7:0] : diva[ 7:0]};

                        `TERM;

                    end

                endcase

            endcase

        endcase

        8'b1111111x: case (m) // ### GROUP #4 rm, op [FE-FF]

            // Запрос операндов
            0: begin t <= MODRM; cpm <= 0; dir <= 1'b0; end

            // Исполнение инструкции
            default: case (`REG)

                // 5T+ INC|DEC rm
                0, 1: case (m)

                    1: begin m <= 2; op2 <= 1; alu <= modrm[3] ? SUB : ADD; end
                    2: begin t <= WB; wb <= ar; flags <= af; `TERM; end

                endcase

                // CALL rm
                2: begin

                    ip <= op1;
                    wb <= ip;
                    t  <= size ? PUSH : UNDEF;
                    `TERM;

                end

                // CALL far rm
                3: case (m)

                    1: begin m <= 2;    ea <= ea + 2; ip <= op1; op1 <= ip; op2 <= cs; if (size == 0) t <= UNDEF; end
                    2: begin m <= 3;    ea <= ea + 1; wb <= i; end
                    3: begin m <= 4;    t <= PUSH; cs <= {i, wb[7:0]}; wb <= op2; end
                    4: begin wb <= op1; t <= PUSH; `TERM; end

                endcase

                // 3T+ JMP rm
                4: begin ip <= op1; `TERM; if (size == 0) t <= UNDEF; end

                // 7T+ JMP far rm
                5: case (m)

                    1: begin m <= 2; ea <= ea + 2; ip <= op1; if (size == 0) t <= UNDEF; end
                    2: begin m <= 3; ea <= ea + 1; wb <= i; end
                    3: begin cs <= {i, wb[7:0]}; `TERM; end

                endcase

                // PUSH rm
                6: begin t <= PUSH; wb <= op1; `TERM; end
                7: begin t <= UNDEF; end

            endcase

        endcase

        default: ip <= ip;    // $$$ НЕИЗВЕСТНАЯ ИНСТРУКЦИЯ
        endcase

    end

    // -------------------------------------------------------------
    // СЧИТЫВАНИЕ ОПЕРАНДОВ ИЗ РЕГИСТРОВ ИЛИ ИЗ ПАМЯТИ
    // -------------------------------------------------------------
    MODRM: case (m1)

        0: begin

            modrm   <= i;
            ip      <= ipn;

            // Читать регистры
            op1     <= dir ? i53 : i20;
            op2     <= dir ? i20 : i53;

            // Вычисление эффективного адреса
            case (i[2:0])
            3'b000: ea <= bx + si;
            3'b001: ea <= bx + di;
            3'b010: ea <= bp + si;
            3'b011: ea <= bp + di;
            3'b100: ea <= si;
            3'b101: ea <= di;
            3'b110: ea <= i[7:6] ? bp : 0;
            3'b111: ea <= bx;
            endcase

            // Роутинг
            casex (i)
            8'b00_xxx_110: begin m1 <= 2; end // DISP16
            8'b00_xxx_xxx: begin m1 <= 4; `CPEN; end // Читать операнд
            8'b01_xxx_xxx: begin m1 <= 1; end // DISP8
            8'b10_xxx_xxx: begin m1 <= 2; end // DISP16
            8'b11_xxx_xxx: begin m1 <= 0; t <= RUN; end // Регистры. Вернуться к RUN
            endcase

            // Проставить сегмент SS: там где есть упоминания BP
            if (!over && ((^i[7:6] && i[2:0] == 3'b110) || i[2:1] == 2'b01)) sgn <= ss;

        end

        // DISP8
        1: begin m1 <= 4; ip <= ipn; ea <= ea + sign;       `CPEN; end
        2: begin m1 <= 3; ip <= ipn; ea <= ea + {8'h00, i}; end
        3: begin m1 <= 4; ip <= ipn; ea <= ea + {i, 8'h00}; `CPEN; end

        // Чтение 8-битный операнд
        4: begin

            if (dir) op2 <= i; else op1 <= i;
            if (size) begin m1 <= 5; ea <= ea + 1; end else begin m1 <= 0; cp <= cpm; t <= RUN; end

        end

        // Читать 16-битный операнд
        5: begin

            if (dir) op2[15:8] <= i; else op1[15:8] <= i;

            t  <= RUN;
            m1 <= 0;
            cp <= cpm;
            ea <= ea - 1;

        end

    endcase

    // -------------------------------------------------------------
    // [1T,2-3T] ЗАПИСЬ РЕЗУЛЬТАТОВ WB,DIR,SIZE,MODRM В ПАМЯТЬ/РЕГИСТРЫ
    // -------------------------------------------------------------
    WB: case (m2)

        // Записать в регистры, если это явно указано
        0: if (dir || modrm[7:6] == 2'b11) begin

            case (dir ? modrm[5:3] : modrm[2:0])
            0: if (size) ax <= wb; else ax[ 7:0] <= wb[7:0];
            1: if (size) cx <= wb; else cx[ 7:0] <= wb[7:0];
            2: if (size) dx <= wb; else dx[ 7:0] <= wb[7:0];
            3: if (size) bx <= wb; else bx[ 7:0] <= wb[7:0];
            4: if (size) sp <= wb; else ax[15:8] <= wb[7:0];
            5: if (size) bp <= wb; else cx[15:8] <= wb[7:0];
            6: if (size) si <= wb; else dx[15:8] <= wb[7:0];
            7: if (size) di <= wb; else bx[15:8] <= wb[7:0];
            endcase

            t  <= next;
            cp <= 0;

        // Либо в память (1 или 2 байта)
        end else begin

            w  <= 1;
            cp <= 1;
            m2 <= 1;
            o  <= wb[7:0];

        end

        // Запись в память, 2 байта
        1: begin

            w  <= size;
            cp <= size;
            t  <= size ? WB : next;
            ea <= ea + 1;
            m2 <= size ? 1 : 0;
            o  <= wb[15:8];
            size <= 0;

        end

    endcase

    // -------------------------------------------------------------
    // [3T] ВЫГРУЗКА WB -> В СТЕК
    // -------------------------------------------------------------
    PUSH: case (m3)
    0: begin m3 <= 1; ea <= sp - 2; w <= 1; o <= wb[7:0]; cp <= 1; sgn <= ss; sp <= sp - 2; end
    1: begin m3 <= 2; ea <= ea + 1; w <= 1; o <= wb[15:8]; end
    2: begin m3 <= 0; cp <= 0; t <= next; end
    endcase

    // -------------------------------------------------------------
    // [3T] ЗАГРУЗКА ИЗ СТЕКА -> WB
    // -------------------------------------------------------------
    POP: case (m3)
    0: begin m3 <= 1; cp <= 1; ea <= sp; sp <= sp + 2; sgn <= ss; cp <= 1; end
    1: begin m3 <= 2; wb <= i; ea <= ea + 1; end
    2: begin m3 <= 0; wb[15:8] <= i; cp <= 0; t <= next; end
    endcase

    // -------------------------------------------------------------
    // Прерывание interrupt; считается за выполнение инструкции
    // -------------------------------------------------------------
    INTERRUPT: case (m4)
    0: begin m4 <= 1; t <= PUSH; wb <= flags; next <= INTERRUPT; end
    1: begin m4 <= 2; t <= PUSH; wb <= cs; end
    2: begin m4 <= 3; t <= PUSH; wb <= ip; end
    3: begin m4 <= 4; ea <= {interrupt, 2'b00}; sgn <= 0; cp <= 1; end
    4: begin m4 <= 5; ip[ 7:0] <= i; ea <= ea + 1; end
    5: begin m4 <= 6; ip[15:8] <= i; ea <= ea + 1; end
    6: begin m4 <= 7; cs[ 7:0] <= i; ea <= ea + 1; end
    7: begin m4 <= 0; cs[15:8] <= i; cp <= 0; t <= RUN; flags[IF] <= 1'b0; end
    endcase

    // -------------------------------------------------------------
    // Деление diva на divb; повторять op1 раз (количество сдвигов)
    // divr = 0 на старте и является остатком; diva это результат
    // -------------------------------------------------------------
    DIV: begin

        {divr, diva} <= {div4r, div4a};
        t   <= op1 != 1 ? DIV : RUN;
        op1 <= op1 - 1;

    end

    endcase

end

endmodule