§ Исходный код ядра

module cpu
(
    // Основной контур для процессора
    input   wire        clock,              // 25 mhz
    output  wire [19:0] address,
    input   wire [ 7:0] i_data,             // i_data = ram[address]
    output  reg  [ 7:0] o_data,
    output  reg         we,

    // Порты ввода-вывода
    output  reg  [15:0] port_address,
    output  reg         port_write,
    output  reg         port_read,
    input   wire [ 7:0] port_in,
    output  reg  [ 7:0] port_out,
    input   wire        port_ready,

    // PIC: Программируемый контроллер прерываний
    input   wire        irq_signal,         // Счетчик IRQ (0 или 1)
    input   wire [ 7:0] irq                 // Номер IRQ (0..255)
);

`include "cpu_decl.v"

// Выбор текущего адреса, segment_id[2] = 1 означает прерывание
assign address = bus ? {(segment_id[2] ? 16'h0000 : seg[segment_id]), 4'h0} + ea : {seg[SEG_CS], 4'h0} + ip;

// Вычисление условий
wire [7:0] branches = {

    (flags[SF] ^ flags[OF]) | flags[ZF], // 7: (ZF=1) OR (SF!=OF)
    (flags[SF] ^ flags[OF]),             // 6: SF!=OF
     flags[PF], flags[SF],               // 5: PF; 4: SF
     flags[CF] | flags[OF],              // 3: CF != OF
     flags[ZF], flags[CF], flags[OF]     // 2: OF; 1: CF; 0: ZF
};

// Исполнительный блок
always @(posedge clock) begin

    wb <= 0; // Запись в регистр
    wf <= 0; // Запись флагов
    port_read  <= 0; // Строб чтения из порта
    port_write <= 0; // Строб записи в порт

    case (fn)

        // Сброс перед запуском инструкции
        // -------------------------------------------------------------
        START: begin

            opcode      <= 0;
            bus         <= 0;       // address = CS:IP
            busen       <= 1;       // Считывать из памяти modrm rm-часть
            segment_id  <= SEG_DS;  // Значение сегмента по умолчанию DS:
            segment_px  <= 1'b0;    // Наличие сегментного префикса
            rep         <= 2'b0;    // Нет префикса REP:
            ea          <= 0;       // Эффективный адрес
            fnext       <= START;   // Возврат по умолчанию
            rep_ft      <= 0;       // =0 REP, =1 REPZ|NZ
            we          <= 0;       // Разрешение записи
            i_dir       <= 0;       // Ширина операнда 0=8, 1=16
            i_size      <= 0;       // Направление 0=[rm,r], 1=[r,rm]
            wb_data     <= 0;       // Данные на запись (modrm | reg)
            wb_reg      <= 0;       // Номер регистра на запись
            ip_start    <= ip;      // Для REP:
            s1 <= 0; s2 <= 0;
            s3 <= 0; s4 <= 0;

            // IRQ прерывание вызывается, если счетчик изменился (irq_accept != irq_signal) и IF=1
            if ((irq_accept ^ irq_signal) && flags[IF]) begin

                fn          <= INTR;
                wb_data     <= irq;
                irq_accept  <= irq_signal;

            end
            // TF=1 (Trace Flag включен) и IF=1
            else if (flags[IF] && flags[TF] && trace_ff) begin

                wb_data  <= 1;
                fn       <= INTR;

            end
            // Выбор номера главной фунции
            else fn <= halt ? START : LOAD;

            // FlipFlop: сначала выполнится инструкция, потом вызовается INT 1>
            if (flags[IF] && flags[TF]) trace_ff <= ~trace_ff;

        end

        // Дешифратор опкода
        // -------------------------------------------------------------
        LOAD: begin

            // Параметры по умолчанию
            i_size <= i_data[0];
            i_dir  <= i_data[1];
            opcode <= i_data;
            ip     <= ip + 1;

            // Поиск опкода по маске
            casex (i_data)

                8'b00001111: begin fn <= EXTEND; end // Префикс расширения
                8'b001xx110: begin fn <= LOAD; segment_id <= i_data[4:3]; segment_px <= 1; end // Сегментные префиксы
                8'b1111001x: begin fn <= LOAD; rep <= i_data[1:0]; end // REPNZ, REPZ
                // FS, GS, opsize, adsize
                8'b0110010x,
                8'b0110011x, /* begin fn <= UNDEF; end */
                // LOCK: FWAIT
                8'b10011011,
                8'b11110000: begin fn <= LOAD; end
                // ALU rm | ALU a,imm
                8'b00xxx0xx: begin fn <= MODRM; alu <= i_data[5:3]; end
                8'b00xxx10x: begin fn <= INSTR; alu <= i_data[5:3]; end
                // MOV rm, i | LEA r, m
                8'b1100011x, 8'b10001101: begin fn <= INSTR; busen <= 0; end
                // INC|DEC r
                8'b0100xxxx: begin fn <= INSTR;

                    alu <= i_data[3] ? ALU_SUB : ALU_ADD;
                    op1 <= r16[i_data[2:0]];
                    op2 <= 1;
                    i_size <= 1;

                end
                // XCHG r, a
                8'b10010xxx: begin fn <= INSTR;

                    op1 <= r16[REG_AX];
                    op2 <= r16[i_data[2:0]];
                    i_size <= 1;

                end
                8'b01010xxx: begin fn <= PUSH; // PUSH r
                    wb_data <= r16[i_data[2:0]];
                end
                // POP xxx
                8'b01011xxx, // POP r
                8'b000xx111, // POP s
                8'b10011101, // POPF
                8'b1100101x, // RETF [i]
                8'b1x001111, // POP rm | IRET
                8'b1100001x: begin fn <= POP; fnext <= INSTR; end // RET [i]
                // PUSH s
                8'b000xx110: begin fn <= PUSH; wb_data <= seg[i_data[4:3]]; end
                // PUSHF
                8'b10011100: begin fn <= PUSH; wb_data <= flags; end
                // Установка и снятие флагов CLx/STx, простые инструкции
                8'b1111100x: begin fn <= START; wf <= 1; wb_flag <= {flags[11:1], i_data[0]}; end // CF
                8'b1111101x: begin fn <= START; wf <= 1; wb_flag <= {flags[11:10], i_data[0], flags[8:0]}; end // IF
                8'b1111110x: begin fn <= START; wf <= 1; wb_flag <= {flags[11], i_data[0], flags[9:0]}; end // IF
                8'b10011110: begin fn <= START; wb_flag <= r16[REG_AX][15:8]; wf <= 1; end // SAHF
                8'b10011111: begin fn <= START; wb_data <= {flags[7:2],1'b1,flags[0]}; wb_reg <= REG_AH; i_size <= 0; wb <= 1; end // LAHF
                8'b11010110: begin fn <= START; wb_data <= {8{flags[0]}}; wb_reg <= REG_AL; i_size <= 0; wb <= 1; end // SALC
                8'b11110100: begin fn <= START; halt <= 1; end // HALT
                8'b11110101: begin fn <= START; wb_flag <= {flags[7:1], ~flags[0]}; wf <= 1; end // CMC
                8'b100000xx, // Grp#1 ALU
                8'b1000011x: begin fn <= MODRM; // XCHG rm, r
                    i_dir <= 0;
                end
                8'b1000010x: begin fn <= MODRM; // TEST rm | TEST a,i
                    alu <= ALU_AND;
                end
                8'b10011000: begin fn <= START; // CBW
                    i_size  <= 0;
                    wb_reg  <= REG_AH;
                    wb      <= 1;
                    wb_data <= {8{r16[REG_AX][7]}};
                end
                8'b10011001: begin fn <= START; // CWD
                    i_size  <= 1;
                    wb_reg  <= REG_DX;
                    wb      <= 1;
                    wb_data <= {16{r16[REG_AX][15]}};
                end
                // LOOP|NZ|Z
                8'b1110000x,
                8'b11100010: begin fn <= INSTR; i_size <= 1; wb_reg <= REG_CX; wb_data <= r16[REG_CX] - 1; wb <= 1; end
                // MOV s,rm
                8'b10001110: begin fn <= MODRM; i_size <= 1; end
                // XLATB
                8'b11010111: begin fn <= INSTR; ea <= r16[REG_BX] + r16[REG_AX][7:0]; bus <= 1; end
                // LES|LDS r,m
                8'b1100010x: begin fn <= MODRM; i_size <= 1; i_dir <= 1; end
                // INT 1,3,4
                8'b11001100: begin fn <= INTR; intr <= 3; end // INT 3
                8'b11001110: begin fn <= flags[OF] ? INTR : START; intr <= 4; end // INTO
                8'b11110001: begin fn <= INTR; intr <= 1; end // INT 1
                8'b1111x11x, // Grp#4|5
                8'b1100000x, // Сдвиги
                8'b110100xx: begin fn <= MODRM; i_dir <= 0; end
                // Переход к исполнению инструкции
                default: begin fn <= INSTR;

                    // Определить наличие байта ModRM для опкода
                    casex (i_data)

                        8'b1000xxxx, 8'b1100000x, 8'b110001xx, 8'b011010x1,
                        8'b110100xx, 8'b11011xxx, 8'b1111x11x, 8'b0110001x: fn <= MODRM;

                    endcase

                end

            endcase

        end

        // Считывание MODRM
        // -------------------------------------------------------------
        MODRM: case (s1)

            // Считывание адреса или регистров
            0: begin

                s1    <= 1;
                modrm <= i_data;
                ip    <= ip + 1;

                // Первый операнд (i_dir=1 будет выбрана reg-часть)
                case (i_dir ? i_data[5:3] : i_data[2:0])

                    3'b000: op1 <= i_size ? r16[REG_AX] : r16[REG_AX][ 7:0];
                    3'b001: op1 <= i_size ? r16[REG_CX] : r16[REG_CX][ 7:0];
                    3'b010: op1 <= i_size ? r16[REG_DX] : r16[REG_DX][ 7:0];
                    3'b011: op1 <= i_size ? r16[REG_BX] : r16[REG_BX][ 7:0];
                    3'b100: op1 <= i_size ? r16[REG_SP] : r16[REG_AX][15:8];
                    3'b101: op1 <= i_size ? r16[REG_BP] : r16[REG_CX][15:8];
                    3'b110: op1 <= i_size ? r16[REG_SI] : r16[REG_DX][15:8];
                    3'b111: op1 <= i_size ? r16[REG_DI] : r16[REG_BX][15:8];

                endcase

                // Второй операнд (i_dir=1 будет выбрана rm-часть)
                case (i_dir ? i_data[2:0] : i_data[5:3])

                    3'b000: op2 <= i_size ? r16[REG_AX] : r16[REG_AX][ 7:0];
                    3'b001: op2 <= i_size ? r16[REG_CX] : r16[REG_CX][ 7:0];
                    3'b010: op2 <= i_size ? r16[REG_DX] : r16[REG_DX][ 7:0];
                    3'b011: op2 <= i_size ? r16[REG_BX] : r16[REG_BX][ 7:0];
                    3'b100: op2 <= i_size ? r16[REG_SP] : r16[REG_AX][15:8];
                    3'b101: op2 <= i_size ? r16[REG_BP] : r16[REG_CX][15:8];
                    3'b110: op2 <= i_size ? r16[REG_SI] : r16[REG_DX][15:8];
                    3'b111: op2 <= i_size ? r16[REG_DI] : r16[REG_BX][15:8];

                endcase

                // Подготовка эффективного адреса
                case (i_data[2:0])

                    3'b000: ea <= r16[REG_BX] + r16[REG_SI];
                    3'b001: ea <= r16[REG_BX] + r16[REG_DI];
                    3'b010: ea <= r16[REG_BP] + r16[REG_SI];
                    3'b011: ea <= r16[REG_BP] + r16[REG_DI];
                    3'b100: ea <= r16[REG_SI];
                    3'b101: ea <= r16[REG_DI];
                    3'b110: ea <= i_data[7:6] == 2'b00 ? 0 : r16[REG_BP]; // disp16 | bp
                    3'b111: ea <= r16[REG_BX];

                endcase

                // Выбор сегмента SS: для BP
                if (!segment_px)
                casex (i_data)
                    8'bxx_xxx_01x, // [bp+si|di]
                    8'b01_xxx_110, // [bp+d8|d16]
                    8'b10_xxx_110: segment_id <= SEG_SS;
                endcase

                // Переход сразу к исполнению инструкции: операнды уже получены
                casex (i_data)

                    8'b00_xxx_110: begin s1 <= 2; end // +disp16
                    8'b00_xxx_xxx: begin s1 <= 4; bus <= busen; if (!busen) fn <= INSTR; end // Читать операнд из памяти
                    8'b01_xxx_xxx: begin s1 <= 1; end // +disp8
                    8'b10_xxx_xxx: begin s1 <= 2; end // +disp16
                    8'b11_xxx_xxx: begin fn <= INSTR; end // Перейти к исполению

                endcase

            end

            // Чтение 8 битного signed disp
            1: begin s1 <= 4; ip <= ip + 1; ea <= ea + signex; bus <= busen; if (!busen) fn <= INSTR; end

            // Чтение 16 битного unsigned disp16
            2: begin s1 <= 3; ip <= ip + 1; ea <= ea + i_data; end
            3: begin s1 <= 4; ip <= ip + 1; ea[15:8] <= ea[15:8] + i_data; bus <= busen; if (!busen) fn <= INSTR; end

            // Чтение операнда из памяти 8 bit
            4: begin

                if (i_dir) op2 <= i_data; else op1 <= i_data;
                if (i_size) begin s1 <= 5; ea <= ea + 1; end else fn <= INSTR;

            end

            // Операнд 16 bit
            5: begin

                if (i_dir) op2[15:8] <= i_data; else op1[15:8] <= i_data;

                ea <= ea - 1;
                fn <= INSTR;

            end

        endcase

        // Исполнение инструкции
        // -------------------------------------------------------------
        INSTR: casex (opcode)

            8'b00xxx0xx: begin              // <alu> rm

                wb_data <= alu_r;
                wb_flag <= alu_f;
                wf <= 1;
                fn <= (alu != ALU_CMP) ? WBACK : START;

            end
            8'b00xxx10x: case (s3)          // <alu> a, imm

                // Инициализация
                0: begin

                    op1 <= i_size ? r16[REG_AX] : r16[REG_AX][7:0];
                    op2 <= i_data;
                    s3  <= i_size ? 1 : 2;
                    ip  <= ip + 1;

                end

                // Считывание старшего байта
                1: begin s3 <= 2; op2[15:8] <= i_data; ip <= ip + 1; end

                // Запись в регистр и выход из процедуры
                2: begin

                    fn      <= START;
                    wb_flag <= alu_f;
                    wb_data <= alu_r;
                    wb_reg  <= REG_AX;
                    wb      <= (alu != ALU_CMP);
                    wf      <= 1;

                end

            endcase
            8'b1011xxxx: case (s3)          // MOV r, i

                // 8 bit
                0: begin

                    wb_data <= i_data;
                    wb_reg  <= opcode[2:0];
                    wb      <= ~opcode[3];
                    i_size  <= opcode[3];
                    fn      <= opcode[3] ? fn : START;
                    s3      <= 1;
                    ip      <= ip + 1;

                end

                // 16 bit
                1: begin

                    wb_data[15:8] <= i_data;
                    wb <= 1;
                    fn <= START;
                    ip <= ip + 1;

                end

            endcase
            8'b100010xx: begin              // MOV rm

                wb_data <= op2;
                fn      <= WBACK;

            end
            8'b1100011x: case (s3)          // MOV rm, i

                // 8 bit
                0: begin

                    wb_data <= i_data;
                    ip      <= ip + 1;
                    i_dir   <= 0;
                    s3      <= 1;

                    if (i_size == 0) begin fn <= WBACK; bus <= 1; end

                end

                // 16 bit
                1: begin wb_data[15:8] <= i_data; ip <= ip + 1; bus <= 1; fn <= WBACK; end

            endcase
            8'b10001101: begin              // LEA r16, m

                wb_data <= ea;
                wb_reg  <= modrm[5:3];
                wb      <= 1;
                fn      <= START;

            end
            8'b0111xxxx: begin              // Jccc

                // Проверка на выполнение условия в branches
                if (branches[ opcode[3:1] ] ^ opcode[0])
                    ip <= ip + 1 + signex;
                else
                    ip <= ip + 1;

                fn <= START;

            end
            8'b0100xxxx: begin              // INC | DEC r16

                wb_data <= alu_r;
                wb_flag <= {alu_f[11:1], flags[CF]};
                wb_reg  <= opcode[2:0];
                wb <= 1;
                wf <= 1;
                fn <= START;

            end
            8'b10010xxx: case (s3)          // XCHG ax, r16

                0: begin s3 <= 1;     wb_data <= op2; wb <= 1; wb_reg <= REG_AX; end
                1: begin fn <= START; wb_data <= op1; wb <= 1; wb_reg <= opcode[2:0]; end

            endcase
            8'b01011xxx: begin fn <= START; // POP r
                wb_reg <= opcode[2:0]; wb <= 1;
            end
            8'b000xx111: begin fn <= START; // POP s
                seg[opcode[4:3]] <= wb_data;
            end
            8'b100000xx: case (s3)          // <alu> imm

                // Считывание imm и номера кода операции
                0: begin s3 <= 1; alu <= modrm[5:3]; busen <= bus; bus <= 0; end
                1: begin s3 <= 2; op2 <= i_data; ip <= ip + 1; end
                2: begin s3 <= 3;

                    case (opcode[1:0])
                        2'b01: begin op2[15:8] <= i_data; ip <= ip + 1; end // imm16
                        2'b11: begin op2[15:8] <= {8{op2[7]}}; end // sign8
                    endcase

                end
                // Запись
                3: begin

                    bus     <= busen;
                    wb_data <= alu_r;
                    wb_flag <= alu_f;
                    wf  <= 1;
                    fn  <= (alu != ALU_CMP) ? WBACK : START;

                end

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

                // Прочесть адрес
                0: begin ea[7:0]  <= i_data; ip <= ip + 1; s3 <= 1; wb_reg <= REG_AX; end
                1: begin ea[15:8] <= i_data; ip <= ip + 1; bus <= 1; s3 <= i_dir ? 2 : 5; end

                // Запись A
                2: begin s3 <= 3; we <= 1;      o_data <= r16[REG_AX][ 7:0]; end
                3: begin s3 <= 4; we <= i_size; o_data <= r16[REG_AX][15:8]; ea <= ea + 1; end
                4: begin fn <= START; we <= 0; end

                // Чтение A
                5: begin s3 <= 6;     wb <= ~i_size; wb_data <= i_data; ea <= ea + 1; end
                6: begin fn <= START; wb <=  i_size; wb_data[15:8] <= i_data; end

            endcase
            8'b1000010x: begin              // TEST rm,r
                wb_flag <= alu_f; wf <= 1; fn <= START;
            end
            8'b100001xx: case (s3)          // XCHG rm,r

                0: begin s3 <= 1;     wb_data <= op1; wb_reg <= modrm[5:3]; wb <= 1; end
                1: begin fn <= WBACK; wb_data <= op2; end

            endcase
            8'b10011101: begin fn <= START; // POPF
                wb_flag <= {wb_data[11:2],1'b1,wb_data[0]}; wf <= 1;
            end
            8'b1010100x: case (s3)          // TEST a,i

                // Считывание младшего байта
                0: begin s3 <= i_size ? 1 : 2; alu <= ALU_AND; op1 <= r16[REG_AX]; op2 <= i_data; ip <= ip + 1; end

                // Если i_size, считывание старшего байта
                1: begin s3 <= 2; op2[15:8] <= i_data; ip <= ip + 1; end

                // Запись результата в АЛУ
                2: begin wf <= 1; wb_flag <= alu_f; fn <= START; end

            endcase
            8'b11101011: begin fn <= START; // JMP b8
                ip <= ip + 1 + signex;
            end
            8'b11101001: case (s3)          // JMP b16
                0: begin s3 <= 1; ea <= i_data; ip <= ip + 1; end
                1: begin fn <= START; ip <= ip + 1 + {i_data, ea[7:0]}; end
            endcase
            8'b11101010: case (s3)          // JMP far

                // Прочитаьть 4 байта для нового CS:IP
                0: begin ip <= ip + 1; s3 <= 1; ea <= i_data; end
                1: begin ip <= ip + 1; s3 <= 2; ea[15:8] <= i_data; end
                2: begin ip <= ip + 1; s3 <= 3; op1 <= i_data; end
                3: begin ip <= ea; seg[SEG_CS] <= {i_data, op1[7:0]}; fn <= START; end

            endcase
            8'b111000xx: begin fn <= START; // LOOP[NZ|Z], JCXZ

                // JCXZ
                if (opcode[1:0] == 2'b11)
                    ip <= ip + 1 + (r16[REG_CX] == 0 ? signex : 0);
                // LOOP, LOOPNZ, LOOPZ
                // Если бит 1 равен 1, то ZF=bit[0] не имеет значения
                else if (r16[REG_CX] && (opcode[1] || flags[ZF] == opcode[0]))
                    ip <= ip + 1 + signex;
                else
                    ip <= ip + 1;

            end
            8'b11101000: case (s3)          // CALL b16

                0: begin s3 <= 1; ea <= i_data; ip <= ip + 1; end
                1: begin fn <= PUSH; wb_data <= ip + 1; ip <= ip + 1 + {i_data, ea[7:0]}; end

            endcase
            8'b11000011: begin fn <= START; // RET
                ip <= wb_data;
            end
            8'b11000010: case (s3)          // RET i16

                // Младший байт
                0: begin s3 <= 1; ea <= i_data; ip <= ip + 1; end

                // Старший байт
                1: begin fn <= START;

                    i_size  <= 1;
                    ip      <= wb_data;
                    wb_data <= r16[REG_SP] + {i_data, ea[7:0]};
                    wb_reg  <= REG_SP;
                    wb      <= 1;

                 end

            endcase
            8'b11001011: case (s3)          // RETF

                0: begin s3 <= 1; op1 <= wb_data; fn <= POP; end
                1: begin s3 <= 2; seg[SEG_CS] <= wb_data; ip <= op1; fn <= START; end

            endcase
            8'b11001010: case (s3)          // RETF i16

                // Младший байт
                0: begin s3 <= 1;

                    fn  <= POP;
                    op1 <= wb_data;
                    op2 <= i_data;
                    ip  <= ip + 1;

                end

                // Старший байт
                1: begin fn <= START;

                    i_size  <= 1;
                    wb_data <= r16[REG_SP] + {i_data, op2[7:0]};
                    wb_reg  <= REG_SP;
                    wb      <= 1;

                    // Установка нового CS:IP из стека
                    seg[SEG_CS] <= wb_data; ip <= op1;

                end

            endcase
            8'b11001111: case (s3)          // IRET

                0: begin s3 <= 1; fn <= POP; ip <= wb_data; end
                1: begin s3 <= 2; fn <= POP; seg[SEG_CS] <= wb_data; end
                2: begin fn <= START; wb_flag <= {wb_data[11:2], 1'b1, wb_data[0]}; wf <= 1; end

            endcase
            8'b10001100: begin fn <= WBACK; // MOV rm,s
                wb_data <= seg[modrm[4:3]];
                i_size  <= 1;
            end
            8'b10001110: begin fn <= START; // MOV s,rm
                seg[modrm[4:3]] <= op2;
            end
            8'b10011010: case (s3)          // CALLF b16

                0: begin ip <= ip + 1; op1[ 7:0] <= i_data; s3 <= 1; end
                1: begin ip <= ip + 1; op1[15:8] <= i_data; s3 <= 2; end
                2: begin ip <= ip + 1; op2[ 7:0] <= i_data; s3 <= 3; end
                3: begin ip <= ip + 1; op2[15:8] <= i_data; s3 <= 4;
                         fn <= PUSH; wb_data <= seg[SEG_CS]; fnext <= INSTR; end
                4: begin fn <= PUSH; wb_data <= ip; s3 <= 5; fnext <= INSTR; end
                5: begin ip <= op1; seg[SEG_CS] <= op2; fn <= START; end

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

                0: begin s3 <= 1; wb_data <= op2; wb <= 1; ea <= ea + 2; wb_reg <= modrm[5:3];  end
                1: begin s3 <= 2; wb_data[7:0] <= i_data;  ea <= ea + 1; end
                2: begin seg[ opcode[0] ? SEG_DS : SEG_ES ] <= {i_data, wb_data[7:0]}; fn <= START; end

            endcase
            8'b10001111: case (s3)          // POP rm

                0: begin s3 <= 1; busen <= 0; fn <= MODRM; i_dir <= 0; end
                1: begin bus <= 1; fn <= WBACK; fnext <= START; end

            endcase
            8'b011010x0: case (s3)          // PUSH i

                0: begin s3 <= opcode[1] ? 2 : 1; wb_data <= signex; ip <= ip + 1; end
                1: begin s3 <= 2; wb_data[15:8] <= i_data; ip <= ip + 1; end
                2: begin fn <= PUSH; fnext <= START; end

            endcase
            8'b11010111: begin fn <= START; // XLATB
                wb_reg  <= REG_AX;
                wb_data <= i_data;
                i_size  <= 0;
                wb      <= 1;
            end
            8'b11001101: begin fn <= INTR;  // INT i
                intr <= i_data;
                ip   <= ip + 1;
            end
            8'b1100000x,
            8'b110100xx: case (s3)          // Grp#2 Сдвиги

                // Выбор второго операнда
                0: begin s3 <= 1;

                    alu <= modrm[5:3];

                    if (opcode[4]) op2 <= (opcode[1] ? r16[REG_CX][3:0] : 1);
                    else begin op2 <= i_data[3:0]; ip <= ip + 1; end

                end

                // Процедура сдвига на 0..15 шагов
                1: begin

                    if (op2) begin

                        op1 <= rot_r;
                        wf  <= 1;
                        wb_flag <= rot_f;

                    end
                    // Запись результата
                    else begin wb_data <= op1; fn <= WBACK; end

                    op2 <= op2 - 1;

                end

            endcase
            8'b1110x10x: case (s3)          // IN a,p

                // Чтение номера порта
                0: begin s3 <= 1;

                    port_address <= opcode[3] ? r16[REG_DX] : i_data;
                    if (opcode[3] == 0) ip <= ip + 1;
                    busen <= 0;

                end

                // Чтение, ожидание результата 1 такт
                1: begin s3 <= 2; port_read <= 1; end
                2: begin s3 <= 3; end

                // Запись ответа в AL|AH
                3: begin

                    if (i_size) begin s3 <= 1; busen <= 1; end
                    else fn <= START;

                    i_size  <= 0;
                    wb_reg  <= busen ? REG_AH : REG_AL;
                    wb_data <= port_in;
                    wb      <= 1;

                end

            endcase
            8'b1110x11x: case (s3)          // OUT p,a

                0: begin s3 <= 1;

                    port_address <= opcode[3] ? r16[REG_DX] : i_data;
                    if (opcode[3] == 0) ip <= ip + 1;

                    port_out    <= r16[REG_AX][7:0];
                    port_write  <= 1;

                    if (i_size == 0) fn <= START;

                end
                1: begin

                    port_out    <= r16[REG_AX][15:8];
                    port_write  <= 1;
                    fn <= START;

                end

            endcase
            8'b1111011x: case (modrm[5:3])  // Grp#3

                // TEST imm8/16
                0, 1: case (s3)

                    0: begin s3 <= 1; bus <= 0; alu <= ALU_AND; end
                    1: begin s3 <= i_size ? 2 : 3; op2 <= i_data; ip <= ip + 1; end
                    2: begin s3 <= 3; op2[15:8] <= i_data; ip <= ip + 1; end
                    3: begin wf <= 1; wb_flag <= alu_f; fn <= START; end

                endcase

                // NOT rm
                2: begin wb_data <= ~op1; fn <= WBACK; end

                // NEG rm
                3: case (s3)

                    0: begin s3 <= 1; alu <= ALU_SUB; op2 <= op1; op1 <= 0; end
                    1: begin fn <= WBACK; wb_data <= alu_r; wb_flag <= alu_f; wf <= 1; end

                endcase

            endcase
            8'b1111111x: case (modrm[5:3])  // Grp#4|5

                // INC|DEC rm
                0, 1: case (s3)

                    0: begin s3 <= 1; op2 <= 1; alu <= modrm[3] ? ALU_SUB : ALU_ADD; end
                    1: begin fn <= WBACK; wb_data <= alu_r; wf <= 1; wb_flag <= alu_f; end

                endcase

                // CALL rm
                2: begin ip <= op1; wb_data <= ip; fn <= i_size ? PUSH : UNDEF; end

                // CALL far rm
                3: case (s3)

                    0: begin s3 <= 1; ea <= ea + 2; ip <= op1; op1 <= ip; op2 <= seg[SEG_CS]; if (i_size == 0) fn <= UNDEF; end
                    1: begin s3 <= 2; ea <= ea + 1; wb_data <= i_data; fnext <= INSTR; end
                    2: begin s3 <= 3; fn <= PUSH; seg[SEG_CS] <= {i_data, wb_data[7:0]}; wb_data <= op2;  end
                    3: begin s3 <= 4; fn <= PUSH; wb_data <= op1; end
                    4: begin fn <= START; end

                endcase

                // JMP rm
                4: begin ip <= op1; fn <= i_size ? START : UNDEF; end

                // JMP far rm
                5: case (s3)

                    0: begin s3 <= 1; ea <= ea + 2; ip <= op1; if (i_size == 0) fn <= UNDEF; end
                    1: begin s3 <= 2; ea <= ea + 1; wb_data <= i_data; end
                    2: begin fn <= START;           seg[SEG_CS] <= {i_data, wb_data[7:0]}; end

                endcase

                // PUSH rm
                6: begin wb_data <= op1; fn <= PUSH; end
                7: begin fn <= UNDEF; end

            endcase
            8'b1010101x: case (s3)          // STOSx

                0: begin // STOSB

                    s3  <= i_size ? 1 : 2;
                    bus <= 1;
                    we  <= 1;
                    ea  <= r16[REG_DI];
                    o_data      <= r16[REG_AX][7:0];
                    segment_id  <= SEG_ES;

                end
                1: begin // STOSW

                    s3 <= 2;
                    we <= 1;
                    ea <= ea + 1;
                    o_data <= r16[REG_AX][15:8];

                end
                2: begin

                    we <= 0;
                    fn <= rep[1] ? REPF : START;
                    bus <= 0;

                    wb      <= 1;
                    wb_reg  <= REG_DI;
                    wb_data <= flags[DF] ? r16[REG_DI] - (i_size + 1) : r16[REG_DI] + (i_size + 1);
                    i_size  <= 1;

                end

            endcase
            8'b1010110x: case (s3)          // LODSx

                0: begin s3 <= 1; bus <= 1; ea <= r16[REG_SI]; end
                1: begin s3 <= i_size ? 2 : 3;

                    wb      <= 1;
                    wb_data <= i_data;
                    wb_reg  <= REG_AL;
                    i_size  <= 0;
                    ea      <= ea + 1;

                end
                2: begin s3 <= 3; wb <= 1; wb_data <= i_data; wb_reg <= REG_AH; end
                3: begin

                    fn  <= rep[1] ? REPF : START;
                    bus <= 0;

                    wb      <= 1;
                    wb_reg  <= REG_SI;
                    wb_data <= flags[DF] ? r16[REG_SI] - (opcode[0] + 1) : r16[REG_SI] + (opcode[0] + 1);
                    i_size  <= 1;

                end

            endcase
            8'b1010010x: case (s3)          // MOVSx

                // Загрузка 8 или 16 бит DS:SI
                0: begin s3 <= 1; ea <= r16[REG_SI]; bus <= 1; end
                1: begin s3 <= i_size ? 2 : 3; wb_data <= i_data; ea <= ea + 1; end
                2: begin s3 <= 3; wb_data[15:8] <= i_data; end

                // Запись 8 или 16 бит ES:DI
                3: begin s3 <= i_size ? 4 : 5;

                    we <= 1;
                    ea <= r16[REG_DI];
                    segment_id <= SEG_ES;
                    o_data     <= wb_data[7:0];

                end
                4: begin s3 <= 5; we <= 1; ea <= ea + 1; o_data <= wb_data[15:8]; end

                // Инкремент или декремент SI
                5: begin s3 <= 6; we <= 0; bus <= 0;

                    wb      <= 1;
                    wb_reg  <= REG_SI;
                    wb_data <= flags[DF] ? r16[REG_SI] - (opcode[0] + 1) : r16[REG_SI] + (opcode[0] + 1);
                    i_size  <= 1;

                end

                // Инкремент или декремент DI
                6: begin s3 <= 6;

                    wb      <= 1;
                    wb_reg  <= REG_DI;
                    wb_data <= flags[DF] ? r16[REG_DI] - (opcode[0] + 1) : r16[REG_DI] + (opcode[0] + 1);

                    // Использование REP:
                    fn  <= rep[1] ? REPF : START;

                end

            endcase
            8'b1010011x: case (s3)          // CMPSx

                0: begin s3 <= 1; bus <= 1; ea <= r16[REG_SI]; alu <= ALU_SUB; end
                1: begin s3 <= i_size ? 2 : 3; op1 <= i_data; ea <= ea + 1; end
                2: begin s3 <= 3; op1[15:8] <= i_data; end
                3: begin s3 <= 4;  segment_id <= SEG_ES; ea <= r16[REG_DI]; end
                4: begin s3 <= i_size ? 5 : 6; op2 <= i_data; ea <= ea + 1; end
                5: begin s3 <= 6; op2[15:8] <= i_data; end

                // Инкремент или декремент SI
                6: begin s3 <= 7;

                    wf      <= 1;
                    wb_flag <= alu_f;
                    bus     <= 0;
                    wb      <= 1;
                    wb_reg  <= REG_SI;
                    wb_data <= flags[DF] ? r16[REG_SI] - (opcode[0] + 1) : r16[REG_SI] + (opcode[0] + 1);
                    i_size  <= 1;

                end

                // Инкремент или декремент DI
                7: begin

                    wb      <= 1;
                    rep_ft  <= 1;       // Проверять на REPNZ или REPZ
                    wb_reg  <= REG_DI;
                    wb_data <= flags[DF] ? r16[REG_DI] - (opcode[0] + 1) : r16[REG_DI] + (opcode[0] + 1);

                    // Использование REP:
                    fn  <= rep[1] ? REPF : START;

                end

            endcase
            8'b1010111x: case (s3)          // SCASx

                0: begin s3 <= 1;

                    bus <= 1;
                    alu <= ALU_SUB;
                    op1 <= r16[REG_AX];
                    ea  <= r16[REG_DI];
                    segment_id <= SEG_ES;

                end
                1: begin s3 <= i_size ? 2 : 3; op2 <= i_data; ea <= ea + 1; end
                2: begin s3 <= 3; op2[15:8] <= i_data; end

                // Инкремент или декремент SI
                3: begin s3 <= 4;

                    wf      <= 1;
                    wb_flag <= alu_f;
                    bus     <= 0;
                    wb      <= 1;
                    wb_reg  <= REG_SI;
                    wb_data <= flags[DF] ? r16[REG_SI] - (opcode[0] + 1) : r16[REG_SI] + (opcode[0] + 1);
                    i_size  <= 1;

                end

                // Инкремент или декремент DI
                4: begin

                    wb      <= 1;
                    rep_ft  <= 1;       // Проверять на REPNZ или REPZ
                    wb_reg  <= REG_DI;
                    wb_data <= flags[DF] ? r16[REG_DI] - (opcode[0] + 1) : r16[REG_DI] + (opcode[0] + 1);

                    // Использование REP:
                    fn  <= rep[1] ? REPF : START;

                end

            endcase

        endcase

        // Расширенные инструции
        // -------------------------------------------------------------
        EXTEND: begin
        end

        // Прерывание intr
        // -------------------------------------------------------------
        INTR: case (s2)

            0: begin s2 <= 1;  fn <= PUSH; wb_data <= flags; fnext <= INTR; end
            1: begin s2 <= 2;  fn <= PUSH; wb_data <= seg[SEG_CS]; end
            2: begin s2 <= 3;  fn <= PUSH; wb_data <= ip; end
            3: begin s2 <= 4;  ea <= {intr, 2'b00}; segment_id[2] <= 1; bus <= 1; end
            4: begin s2 <= 5;  ea <= ea + 1; ip[ 7:0] <= i_data; end
            5: begin s2 <= 6;  ea <= ea + 1; ip[15:8] <= i_data; end
            6: begin s2 <= 7;  ea <= ea + 1; seg[SEG_CS][ 7:0] <= i_data; end
            7: begin bus <= 0; ea <= ea + 1; seg[SEG_CS][15:8] <= i_data; fn <= START; end

        endcase

        // Сохранение данных [wb_data, i_size, i_dir, modrm]
        // -------------------------------------------------------------
        WBACK: case (s2)

            // Выбор - регистр или память
            0: begin

                // reg-часть или rm:reg
                if (i_dir || modrm[7:6] == 2'b11) begin

                    wb_reg  <= i_dir ? modrm[5:3] : modrm[2:0];
                    wb      <= 1;
                    bus     <= 0;
                    fn      <= fnext;

                end
                // Если modrm указывает на память, записать первые 8 бит
                else begin o_data <= wb_data[7:0]; we <= 1; s2 <= 1; end

            end

            // Запись 16 бит?
            1: if (i_size) begin

                ea     <= ea + 1;
                o_data <= wb_data[15:8];
                s2     <= 2;

            end
            // Завершение записи 8 бит
            else begin we <= 0; bus <= 0; fn <= fnext; end

            // Запись 16 бит закончена
            2: begin   we <= 0; bus <= 0; fn <= fnext; end

        endcase

        // Запись в стек <- wb_data | bus,wb,wb_reg,segment_id,ea,i_size
        // -------------------------------------------------------------
        PUSH: case (s4)

            0: begin s4 <= 1;

                segment_id <= SEG_SS;
                ea      <= r16[REG_SP] - 2;
                bus     <= 1;
                o_data  <= wb_data[7:0];
                we      <= 1;

            end
            1: begin s4 <= 2;

                o_data  <= wb_data[15:8];
                wb      <= 1;
                wb_reg  <= REG_SP;
                wb_data <= r16[REG_SP] - 2;
                i_size  <= 1;
                ea      <= ea + 1;

            end
            2: begin we <= 0; bus <= 0; fn <= fnext; s4 <= 0; end

        endcase

        // Чтение из стека -> wb_data | bus,wb,wb_reg,segment_id,ea,i_size
        // -------------------------------------------------------------
        POP: case (s4)

            0: begin s4 <= 1;

                segment_id <= SEG_SS;
                ea      <= r16[REG_SP];
                bus     <= 1;
                wb      <= 1;
                wb_reg  <= REG_SP;
                wb_data <= r16[REG_SP] + 2;
                i_size  <= 1;

            end

            1: begin s4 <= 2; wb_data[ 7:0] <= i_data; ea <= ea + 1; end
            2: begin s4 <= 0; wb_data[15:8] <= i_data; bus <= 0; fn <= fnext; end

        endcase

        // Выполнение инструкции REP
        REPF: case (s4)

            // Уменьшить CX - 1
            0: begin s4 <= 1; wb_reg <= REG_CX; wb_data <= r16[REG_CX] - 1; wb <= 1; i_size <= 1; end
            1: begin

                // CX=0, повтор закончен
                if (r16[REG_CX] == 0) fn <= START;
                // REPNZ|REPZ
                else if (rep_ft) begin if (rep[0] == flags[ZF]) ip <= ip_start; end
                // REP:
                else ip <= ip_start;

                fn <= START;

            end

        endcase

        // Деление op1 на op2, i_size -> wb_data
        // DIV: case (s5)
        // endcase

    endcase

end

// wb=запись wb_data резрешена в регистр wb_reg
// [wb, wb_data, wb_reg, i_size]
always @(negedge clock) begin

    if (wb) begin

        if (i_size) r16[ wb_reg ] <= wb_data; // 16 bit
        else if (wb_reg[2])
             r16[ wb_reg[1:0] ][15:8] <= wb_data[7:0]; // HI8
        else r16[ wb_reg[1:0] ][ 7:0] <= wb_data[7:0]; // LO8

    end

    if (wf) flags <= wb_flag;

end

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

reg  [11:0] alu_f; // Результирующие флаги
reg  [16:0] alu_r; // Результат выполнения op1 <alu> op2
wire [ 3:0] alu_top = i_size ? 15 : 7;

always @* begin

    alu_f = flags;

    // Вычисление результата
    case (alu)

        ALU_ADD: alu_r = op1 + op2;
        ALU_ADC: alu_r = op1 + op2 + flags[CF];
        ALU_SBB: alu_r = op1 - op2 - flags[CF];
        ALU_SUB,
        ALU_CMP: alu_r = op1 - op2;
        ALU_XOR: alu_r = op1 ^ op2;
        ALU_OR:  alu_r = op1 | op2;
        ALU_AND: alu_r = op1 & op2;

    endcase

    // Общая схема переносов
    alu_f[CF] = alu_r[ alu_top + 1 ];
    alu_f[AF] = op1[4] ^ op2[4] ^ alu_r[4];

    // Вычисление флага OF
    case (alu)

        ALU_ADD,
        ALU_ADC: alu_f[OF] = (op1[alu_top] ^ op2[alu_top] ^ 1) & (op1[alu_top] ^ alu_r[alu_top]);
        ALU_SUB,
        ALU_SBB,
        ALU_CMP: alu_f[OF] = (op1[alu_top] ^ op2[alu_top] ^ 0) & (op1[alu_top] ^ alu_r[alu_top]);
        // Логические сбрасывают OF, CF, AF
        default: begin alu_f[OF] = 0; alu_f[CF] = 0; alu_f[AF] = alu_r[4]; end

    endcase

    // SZP устанавливаются для всех одинаково
    alu_f[SF] = alu_r[alu_top];
    alu_f[ZF] = (i_size ? alu_r[15:0] : alu_r[7:0]) == 0;
    alu_f[PF] = ~^alu_r[7:0];

end

// ---------------------------------------------------------------------
// Сдвиговый регистр
// ---------------------------------------------------------------------

reg [15:0] rot_r;
reg [11:0] rot_f;

always @* begin

    rot_f = flags;

    case (alu)

        ALU_ROL: rot_r = i_size ? {op1[14:0], op1[15]} : {op1[6:0], op1[7]};
        ALU_ROR: rot_r = i_size ? {op1[0], op1[15:1]}  : {op1[0], op1[7:1]};
        ALU_RCL: rot_r = i_size ? {op1[14:0], flags[CF]} : {op1[6:0], flags[CF]};
        ALU_RCR: rot_r = i_size ? {flags[CF], op1[15:1]}  : {flags[CF], op1[7:1]};
        ALU_SHL,
        ALU_SAL: rot_r = i_size ? {op1[14:0], 1'b0} : {op1[6:0], 1'b0};
        ALU_SHR: rot_r = i_size ? {1'b0, op1[15:1]}  : {1'b0, op1[7:1]};
        ALU_SHR: rot_r = i_size ? {op1[15], op1[15:1]} : {op1[7], op1[7:1]};

    endcase

    // Флаг CF
    rot_f[CF] = alu[0] ? op1[0] : op1[alu_top];

    // Флаг OF
    case (alu)

        ALU_ROL,
        ALU_RCL,
        ALU_SAL,
        ALU_SHL: rot_f[OF] = op1[alu_top] ^ op1[alu_top - 1];
        ALU_ROR,
        ALU_RCR: rot_f[OF] = rot_r[alu_top] ^ rot_r[alu_top - 1];
        ALU_SHR: rot_f[OF] = op1[alu_top];
        ALU_SAR: rot_f[OF] = 1'b0;

    endcase

    // S, Z, P для 4 инструкции
    if (alu == ALU_SHL || alu == ALU_SHR || alu == ALU_SAL || alu == ALU_SHR) begin

        rot_f[SF] = rot_r[alu_top];
        rot_f[ZF] = i_size ? (rot_r[15:0] == 0) : (rot_r[7:0] == 0);
        rot_f[PF] = ~^rot_r[7:0];

    end

end

endmodule
23 мар, 2021
© 2007-2023 Правда в том, что Якубович кусает конем