§ Описание пинов

  • clock — тактовая частота, обычно 25 мгц
  • reset_n — сброс процессора на 0
  • ce — если 1, такты включены (chip enabled)
  • in — входящие данные
  • address — 16-битный адрес (в том числе порт)
  • we — запись в память
  • pw — запись в порт
  • rd — чтение из порта
  • out — значение на запись в память или порт

§ Ядро процессора

Процессор содержит почти все 256 инструкции, кроме отключенных EXX и EX AF,AF', если раскомментировать, то они активируются. Нет префиксов IX,IY, битовых префиксов и ED-инструкции.
/* verilator lint_off WIDTH */
/* verilator lint_off CASEX */
/* verilator lint_off CASEOVERLAP */
/* verilator lint_off CASEINCOMPLETE */

module kr580
(
    // Шина данных
    input               clock,
    input               reset_n,
    input               ce,
    input        [ 7:0] in,
    output       [15:0] address,    // Указатель на адрес
    output  reg         we,         // Разрешить запись (высокий уровень)
    output  reg         pw,         // Port write
    output  reg         rd,         // Port read (на IN значение порта address)
    output  reg  [ 7:0] out,

    // Interrupt
    input   wire        irq
);

// Набор АЛУ
localparam
    ALU_ADD = 0, ALU_ADC = 1, ALU_SUB = 2, ALU_SBC = 3,
    ALU_AND = 4, ALU_XOR = 5, ALU_OR  = 6, ALU_CP  = 7,
    ALU_RLC = 0, ALU_RRC = 1, ALU_RL  = 2, ALU_RR  = 3,
    ALU_DAA = 4, ALU_CPL = 5, ALU_SCF = 6, ALU_CCF = 7;

// Регистры
localparam
    REG_B  = 0, REG_C  = 1, REG_D  = 2, REG_E = 3,
    REG_H  = 4, REG_L  = 5, REG_M  = 6, REG_A = 7,
    REG_BC = 0, REG_DE = 1, REG_HL = 2, REG_SP = 3;

// Флаги
localparam CF = 0, NF = 1, PF = 2, AF = 4, ZF = 6, SF = 7;

// Специальные
localparam
    EX_DE_HL = 1, EX_AF  = 2,
    INCSP2   = 3, DECSP2 = 4,
    EXX      = 5;

initial begin we  = 0; out = 0; end

// Указатель на необходимые данные
assign address = alt ? cp : pc;

// Управляющие регистры
reg  [ 3:0] t       = 0;        // Это t-state
reg  [ 2:0] m       = 0;        // Это m-state для префиксов
reg         ei      = 0;        // Enabled Interrupt
reg         ei_     = 0;        // Это необходимо для EI+RET конструкции
reg  [15:0] cp      = 0;
reg         alt     = 1'b0;     // =0 pc  =1 cp

// Регистры. Есть также регистры AF'BC'DE'HL' из дополнительного набора
reg  [15:0] bc  = 16'hAFB1, de  = 16'h0305, hl  = 16'hBEEF; // Базовый
reg  [15:0] bc_ = 16'h0000, de_ = 16'h0000, hl_ = 16'h0000; // Дополнительный
reg  [15:0] pc  = 16'h0000, sp  = 16'h0001;
reg  [ 1:0] im  = 2'b00;
reg  [ 7:0] i   = 8'h00,
            a   = 8'h0A,       a_ = 8'hFF,
            f   = 8'b11000100, f_ = 8'h00;
                 //  SZ5A3PNC

// Сохраненный опкод
wire [ 7:0] opcode          = t ? latch : in;
reg  [ 7:0] latch           = 8'h00;
reg         irqp            = 1'b0;

// Управление записью в регистры
reg         _b = 1'b0;      // Сигнал на запись 8 битного регистра
reg         _w = 1'b0;      // Сигнал на запись 16 битного регистра (_u:_l)
reg  [ 2:0] _n = 3'h0;      // Номер регистра
reg  [ 7:0] _l = 8'h00;     // Что писать
reg  [ 7:0] _u = 8'h00;     // Что писать
reg  [ 7:0] _f = 8'h00;     // Сохранение флага
reg         fw;             // Писать флаги
reg  [ 2:0] spec;           // Специальные операции; EX DE,HL; EX AF,AF'

// Определение условий
wire [15:0] pci = pc + 1;
wire [15:0] pcn = pci + {{8{in[7]}}, in[7:0]};
wire [7:0]  ccc = {f[SF], ~f[SF], f[PF], ~f[PF], f[CF], ~f[CF], f[ZF], ~f[ZF]};
wire        ccx = ccc[op53] || opcode == 8'hC9 || opcode == 8'hC3 || opcode == 8'hCD; // CCC; RET, JP, CALL

// -----------------------------------------------------------------------------
// Работа с регистрами
// -----------------------------------------------------------------------------

// Алиасы к проводам
wire [2:0] op20 = opcode[2:0];
wire [2:0] op53 = opcode[5:3];
wire [1:0] op54 = opcode[5:4];

// Вариант 1. Источник opcode[2:0]
wire [7:0] r20 =
    op20 == REG_B ? bc[15:8] : op20 == REG_C ? bc[ 7:0] :
    op20 == REG_D ? de[15:8] : op20 == REG_E ? de[ 7:0] :
    op20 == REG_H ? hl[15:8] : op20 == REG_L ? hl[ 7:0] :
    op20 == REG_M ? in : a;

// Вариант 2. Источник opcode[5:3]
wire [7:0] r53 =
    op53 == REG_B ? bc[15:8] : op53 == REG_C ? bc[ 7:0] :
    op53 == REG_D ? de[15:8] : op53 == REG_E ? de[ 7:0] :
    op53 == REG_H ? hl[15:8] : op53 == REG_L ? hl[ 7:0] :
    op53 == REG_M ? in : a;

// 16-битный регистр, источник opcode[5:4]
wire [15:0] r16 =
    op54 == REG_BC ? bc :
    op54 == REG_DE ? de :
    op54 == REG_HL ? hl : sp;

// 16-битные операции HL + R16
wire [16:0] hladd = hl + r16;

// Инструкции INC и DEC
wire [8:0] incdec = opcode[0] ? r53 - 1 : r53 + 1;  // Расчет результата
wire       r53ido = r53 == (127 + opcode[0]);       // Флаг overflow
wire [7:0] idflag = {incdec[7], incdec[7:0] == 8'b0, 1'b0, incdec[4] ^ r53[4], 1'b0, r53ido, 1'b0, incdec[8]};

// -----------------------------------------------------------------------------
// Исполнение инструкции
// -----------------------------------------------------------------------------

always @(posedge clock)
// Сброс
if (reset_n == 1'b0) begin t <= 0; pc <= 0; alt <= 0; end
// Процессинг
else if (ce) begin

    // Подготовка управляющих сигналов
    alt  <= 0;
    _b   <= 0;
    _w   <= 0;
    we   <= 0;
    pw   <= 0;
    rd   <= 0;
    fw   <= 0;
    spec <= 0;

    // Запуск прерывания RST #38; IM 2 режима нет
    if (t == 0 && ei && (irqp != irq)) begin

        t       <= 1;
        alt     <= 1;
        we      <= 1;
        cp      <= sp - 1;
        irqp    <= irq;
        out     <= (in == 8'h76) ? pci[15:8] : pc[15:7]; // HALT?
        latch   <= 8'hFF;
        {ei,ei_} <= 0;

        // HALT добавляет PC+1
        if (in == 8'h76) pc <= pc + 1;

    end
    // Исполнение опкодов
    else begin

        t <= t + 1;

        // Запись опкода на первом такте
        if (t == 0) begin

            latch <= in;        // Запомнить опкод
            ei    <= ei_;       // Сброс EI-триггера
            pc    <= pc + 1;    // По умолчанию PC+1 на T=0

        end

        // Выполнять инструкции
        casex (opcode)

        // 1 NOP
        8'b00_000_000: t <= 0;

        // 1 EX AF,AF'
        8'b00_001_000: begin t <= 0; spec <= EX_AF; end

        // 1+ DJNZ *
        8'b00_010_000: case (t)

            0: begin

                _b <= 1;
                _n <= REG_B;
                _l <= bc[15:8] - 1;

                if (bc[15:8] == 1) pc <= pc + 2;

            end
            1: begin t <= 0; pc <= pcn; end

        endcase

        // 2 JR *
        8'b00_011_000: if (t == 1) begin t <= 0; pc <= pcn; end

        // 1+ JR cc, *
        8'b00_1xx_000: case (t)

            0: if (!ccc[opcode[4:3]]) begin t <= 0; pc <= pc + 2; end
            1: begin t <= 0; pc <= pcn; end

        endcase

        // 3 LD r, i16
        8'b00_xx0_001: case (t)

            0: begin _n <= opcode[5:4]; end
            1: begin pc <= pc + 1; _l <= in; end
            2: begin pc <= pc + 1; _u <= in; _w <= 1; t <= 0; end

        endcase

        // 1 ADD HL, r
        8'b00_xx1_001: begin

            t  <= 0;
            _w <= 1;
            _n <= REG_HL;

            {_u, _l} <= hladd;

            _f[NF]  <= 1'b0;
            _f[CF]  <= hladd[16];
            _f[ZF]  <= hladd[15:0] == 0;
            _f[SF]  <= hladd[15];

        end

        // 2 LD (BC|DE), A
        8'b00_0x0_010: case (t)

            0: begin alt <= 1; cp <= opcode[4] ? de : bc; out <= a; we <= 1; end
            1: begin alt <= 0; t <= 0; end

        endcase

        // 2 LD A, (BC|DE)
        8'b00_0x1_010: case (t)

            0: begin alt <= 1; cp <= opcode[4] ? de : bc;  end
            1: begin t   <= 0; _n <= REG_A; _b <= 1; _l <= in; end

        endcase

        // 5 LD (**), HL
        8'b00_100_010: case (t)

            1: begin cp[ 7:0] <= in; pc <= pc + 1; end
            2: begin cp[15:8] <= in; we <= 1; alt <= 1; out <= hl[ 7:0]; pc <= pc + 1;  end
            3: begin cp <= cp + 1;   we <= 1; alt <= 1; out <= hl[15:8]; end
            4: begin t <= 0; end

        endcase

        // 5 LD HL, (**)
        8'b00_101_010: case (t)

            1: begin pc <= pc + 1; cp[ 7:0] <= in;   end
            2: begin pc <= pc + 1; cp[15:8] <= in;   alt <= 1; end
            3: begin _n <= REG_L; _b <= 1; _l <= in; alt <= 1; cp <= cp + 1; end
            4: begin _n <= REG_H; _b <= 1; _l <= in; t <= 0; end

        endcase

        // 4 LD (**), A
        8'b00_110_010: case (t)

            1: begin cp[ 7:0] <= in; pc <= pc + 1; out <= a; end
            2: begin cp[15:8] <= in; pc <= pc + 1; we <= 1; alt <= 1; end
            3: begin t <= 0; end

        endcase

        // 4 LD A, (**)
        8'b00_111_010: case (t)

            1: begin pc <= pc + 1; cp[ 7:0] <= in; end
            2: begin pc <= pc + 1; cp[15:8] <= in; alt <= 1; end
            3: begin _b <= 1; _n <= REG_A; _l <= in; t <= 0; end

        endcase

        // 1 INC r16
        8'b00_000_011: begin t <= 0; {_u, _l} <= bc + 1; _n <= 0; _w <= 1; end
        8'b00_010_011: begin t <= 0; {_u, _l} <= de + 1; _n <= 1; _w <= 1; end
        8'b00_100_011: begin t <= 0; {_u, _l} <= hl + 1; _n <= 2; _w <= 1; end
        8'b00_110_011: begin t <= 0; {_u, _l} <= sp + 1; _n <= 3; _w <= 1; end

        // 1 DEC r16
        8'b00_001_011: begin t <= 0; {_u, _l} <= bc - 1; _n <= 0; _w <= 1; end
        8'b00_011_011: begin t <= 0; {_u, _l} <= de - 1; _n <= 1; _w <= 1; end
        8'b00_101_011: begin t <= 0; {_u, _l} <= hl - 1; _n <= 2; _w <= 1; end
        8'b00_111_011: begin t <= 0; {_u, _l} <= sp - 1; _n <= 3; _w <= 1; end

        // 3 INC|DEC (HL)
        8'b00_110_10x: case (t)

            0: begin cp <= hl; alt <= 1; end
            1: begin

                _f  <= idflag;
                out <= incdec;
                fw  <= 1;
                we  <= 1;
                alt <= 1;

            end
            2: t <= 0;

        endcase

        // 1 INC|DEC r8
        8'b00_xxx_10x: begin

            t   <= 0;
            _n  <= op53;
            _b  <= 1;
            _l  <= incdec;
            _f  <= idflag;
            fw  <= 1;

        end

        // 2+ LD r, i8
        8'b00_xxx_110: case (t)

            0: cp <= hl;
            1: begin

                t   <= op53 == REG_M ? 2 : 0;
                we  <= op53 == REG_M;
                alt <= op53 == REG_M;
                _b  <= op53 != REG_M;
                _n  <= op53;
                _l  <= in;
                out <= in;
                pc  <= pc + 1;

            end

            2: t <= 0;

        endcase

        // 1 RLCA, RRCA, RLA, RRA, DAA, CPL, SCF, CCF
        8'b00_xxx_111: begin

            t  <= 0;
            fw <= 1;
            _n <= REG_A;
            _b <= 1;
            _l <= alu_sr;
            _f <= alu_sf;

        end

        // 1 HALT: Остановить процессор
        8'b01_110_110: begin pc <= pc; t <= 0; end

        // --------------------------

        // 2 LD r, (HL)
        8'b01_xxx_110: case (t)

            0: begin alt <= 1; cp <= hl; end
            1: begin t <= 0; _b <= 1; _n <= op53; _l <= in; end

        endcase

        // 2 LD (HL), r
        8'b01_110_xxx: case (t)

            0: begin alt <= 1; cp <= hl; we <= 1; out <= r20; end
            1: begin t <= 0; end

        endcase

        // 1 LD r, r
        8'b01_xxx_xxx: begin

            t  <= 0;
            _b <= 1;
            _n <= op53;
            _l <= r20;

        end

        // 2 <ALU> A, (HL)
        8'b10_xxx_110: case (t)

            0: begin cp <= hl; alt <= 1; end
            1: begin

                t   <= 0;
                fw  <= 1;
                _b  <= (op53 != ALU_CP);
                _l  <= alu_r;
                _f  <= alu_f;
                _n  <= REG_A;

            end

        endcase

        // 1 <ALU> A, r
        8'b10_xxx_xxx: begin

            t   <= 0;
            fw  <= 1;
            _b  <= (op53 != ALU_CP);
            _l  <= alu_r;
            _f  <= alu_f;
            _n  <= REG_A;

        end

        // --------------------------

        // 1/3 RET c | RET
        8'b11_001_001,
        8'b11_xxx_000: case (t)

            0: begin t <= ccx; alt <= ccx; cp <= sp; end
            1: begin pc[ 7:0] <= in; alt <= 1; cp <= cp + 1; end
            2: begin pc[15:8] <= in; spec <= INCSP2; t <= 0; end

        endcase

        // 3 POP r16
        8'b11_xx0_001: case (t)

            0: begin cp <= sp; alt <= 1; spec <= INCSP2; end
            1: begin _l <= in; alt <= 1; cp <= cp + 1; end
            2: begin

                _u <= in;
                t  <= 0;

                if (opcode[5:4] == 2'b11)
                     begin _n <= REG_A; _b <= 1; _l <= in; fw <= 1'b1; _f <= _l; end // POP AF
                else begin _n <= opcode[5:4];    _w <= 1; end

            end

        endcase

        // 1 EXX
        // 1 JP (HL)
        // 1 LD SP, HL
        8'b11_011_001: begin t <= 0; spec <= EXX; end
        8'b11_101_001: begin t <= 0; pc <= hl; end
        8'b11_111_001: begin t <= 0; _w <= 1; _n <= REG_SP; {_u, _l} <= hl; end

        // 1/3 JP c, ** | JP **
        8'b11_000_011,
        8'b11_xxx_010: case (t)

            0: if (!ccx) begin t <= 0; pc <= pc + 3; end
            1: begin _l <= in; pc <= pc + 1'b1; end
            2: begin pc <= {in, _l}; t <= 0; end

        endcase

        // 3 OUT (*), A
        8'b11_010_011: case (t)

            1: begin

                pw  <= 1;
                alt <= 1;
                cp  <= in;
                pc  <= pc + 1;
                out <= a;

            end
            2: t <= 0;

        endcase

        // 3 IN  A, (*)
        8'b11_011_011: case (t)

            1: begin pc <= pc + 1; cp <= in; rd <= 1; alt <= 1; end
            2: begin _b <= 1; _n <= REG_A; _l <= in; t <= 0; end

        endcase

        // 5 EX (SP), HL
        8'b11_100_011: case (t)

            0: begin alt <= 1; cp <= sp; end
            1: begin alt <= 1; _l <= in; out <= hl[ 7:0]; we <= 1; end
            2: begin alt <= 1; cp <= cp + 1; end
            3: begin alt <= 1; _u <= in; out <= hl[15:8]; we <= 1; _w <= 1; _n <= REG_HL; end
            4: begin t <= 0; end

        endcase

        // 1 EX DE, HL
        // 1 DI, EI
        8'b11_101_011: begin t <= 0; spec <= EX_DE_HL; end
        8'b11_11x_011: begin t <= 0; ei_ <= opcode[3]; end

        // 1/5 CALL c, **
        8'b11_001_101,
        8'b11_xxx_100: case (t)

            0: if (!ccx) begin t <= 0; pc <= pc + 3; end
            1: begin _l <= in; pc <= pc + 1; end
            2: begin

                alt  <= 1;
                we   <= 1;
                cp   <= sp - 1;
                out  <= pci[15:8];
                _u   <= in;

            end
            3: begin

                alt  <= 1;
                we   <= 1;
                out  <= pci[ 7:0];
                cp   <= cp - 1;
                pc   <= {_u, _l};
                spec <= DECSP2;

            end
            4: t <= 0;

        endcase

        // 3 PUSH r16
        8'b11_xx0_101: case (t)

            0: begin

                alt  <= 1;
                we   <= 1;
                cp   <= sp - 1;
                out  <= (op54 == 2'b11) ? a : r16[15:8];
                spec <= DECSP2;

            end

            1: begin

                t   <= 2;
                alt <= 1;
                we  <= 1;
                cp  <= cp - 1;
                out <= (op54 == 2'b11) ? f : r16[ 7:0];

            end

            2: t <= 0;

        endcase

        // 2 <ALU> A, i8
        8'b11_xxx_110: case (t)

            // Так как op20 равно 6, то в _r20 = in
            1: begin

                t   <= 0;
                pc  <= pc + 1;
                fw  <= 1;
                _b  <= (op53 != ALU_CP);
                _l  <= alu_r;
                _f  <= alu_f;
                _n  <= REG_A;

            end

        endcase

        // 3 RST #
        8'b11_xxx_111: case (t)

            0: begin out <= pci[15:8]; cp <= sp - 1; we <= 1; alt <= 1; end
            1: begin out <= pc [ 7:0]; cp <= cp - 1; we <= 1; alt <= 1; end
            2: begin spec <= DECSP2; pc <= {op53, 3'b000}; t <= 0; end

        endcase

        endcase

    end

end

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

wire is_add = (op53 == ALU_ADD) || (op53 == ALU_ADC);
wire is_lgc = (op53 == ALU_AND) || (op53 == ALU_OR) || op53 == ALU_XOR;

// Определение результата
wire [16:0] alu_r =
    op53 == ALU_ADD ? a + r20 :
    op53 == ALU_ADC ? a + r20 + f[CF] :
    op53 == ALU_SBC ? a - r20 - f[CF] :
    op53 == ALU_AND ? a & r20 :
    op53 == ALU_XOR ? a ^ r20 :
    op53 == ALU_OR  ? a | r20 : a - r20; // OR; SUB, CP

// Вычисление флагов
wire carry  =   alu_r[8];
wire sign   =   alu_r[7];
wire zero   = ~|alu_r[7:0];
wire prty   = ~^alu_r[7:0];
wire aux    =  a[4] ^ r20[4] ^ alu_r[4];
wire over   = (a[7] ^ r20[7] ^ is_add) && (a[7] != alu_r[7]);

wire [7:0]  alu_f =
//           SF    ZF    F5        AF    F3        PF/OF    NF    CF
    is_lgc? {sign, zero, alu_r[5], 1'b0, alu_r[3], prty,    1'b0, 1'b0} : // AND, OR, XOR
            {sign, zero, alu_r[5], aux,  alu_r[3], over, !is_add, carry}; // ADD, ADC, SUB, SBC, CP

// -----------------------------------------------------------------------------
// Сдвиги и работа над регистром A
// -----------------------------------------------------------------------------

wire [7:0] daa = // Коррекция BCD-чисел после сложения и вычитания
    (f[NF]) ? a - ((f[AF] | (a[3:0] > 4'h9)) ? 8'h06 : 0) - ((f[CF] | (a[7:0] > 8'h99)) ? 8'h60 : 0) :
              a + ((f[AF] | (a[3:0] > 4'h9)) ? 8'h06 : 0) + ((f[CF] | (a[7:0] > 8'h99)) ? 8'h60 : 0);

// Промежуточное вычисление флагов результата
wire sr_prty = ~^alu_sr[7:0];
wire sr_zero =   alu_sr[7:0] == 8'b0;

// Вычисление сдвигов
wire [7:0] alu_sr =
    op53 == ALU_RLC ? {a[6:0], a[  7]} :
    op53 == ALU_RRC ? {a[0],   a[7:1]} :
    op53 == ALU_RL  ? {a[6:0], f[ CF]} :
    op53 == ALU_RR  ? {f[CF],  a[7:1]} :
    op53 == ALU_DAA ? daa :
    op53 == ALU_CPL ? ~a : a;

// Вычисление флагов
wire [7:0] alu_sf =
    op53 == ALU_RLC || op53 == ALU_RL ? {alu_sr[7], sr_zero, 3'b000, sr_prty, 1'b1, a[7]} :
    op53 == ALU_RRC || op53 == ALU_RR ? {alu_sr[7], sr_zero, 3'b000, sr_prty, 1'b1, a[0]} :
    op53 == ALU_DAA ? {daa[7], sr_zero, daa[5], a[4] ^ daa[4], daa[3], sr_prty, f[NF], f[CF] | (a > 8'h99)} :
    op53 == ALU_CPL ? {f[SF], f[ZF], 1'b0, 1'b1,  1'b0, f[PF], 1'b1,  f[CF]} :
    op53 == ALU_SCF ? {f[SF], f[ZF], 1'b0, f[AF], 1'b0, f[PF], 1'b1,   1'b1} :
                      {f[SF], f[ZF], 1'b0, f[AF], 1'b0, f[PF], 1'b1, !f[CF]};

// -----------------------------------------------------------------------------
// Запись в регистры
// -----------------------------------------------------------------------------

always @(negedge clock)
begin

    // Сохранение флагов
    if (fw) f <= _f;

    // Сохранение регистров
    case (spec)
    INCSP2:   sp <= sp + 2;
    DECSP2:   sp <= sp - 2;
    // Опциональная поддержка инструкции EX AF,AF`; EXX
    // EXX:      begin {bc, de, hl, bc_, de_, hl_} <= {bc_, de_, hl_, bc, de, hl}; end
    // EX_AF:    begin {a, f, a_, f_} <= {a_, f_, a, f}; end
    EX_DE_HL: begin {de, hl} <= {hl, de}; end
    default:

        // Запись в 16-битные регистры
        if (_w) case (_n)

            REG_BC: bc <= {_u, _l};
            REG_DE: de <= {_u, _l};
            REG_HL: hl <= {_u, _l};
            REG_SP: sp <= {_u, _l};

        endcase
        // Запись в 8 битные регистры
        else if (_b) case (_n)

            REG_B: bc[15:8] <= _l;
            REG_C: bc[ 7:0] <= _l;
            REG_D: de[15:8] <= _l;
            REG_E: de[ 7:0] <= _l;
            REG_H: hl[15:8] <= _l;
            REG_L: hl[ 7:0] <= _l;
            REG_A: a        <= _l;

        endcase

    endcase

end

endmodule