DE0で8bit CPUのデコーダを動かす

日曜日にデコーダが動いてたのだけど、動画をとるのがめんどくてブログかいてなかった。
とりあえず、ここで書いた、デコード後の内部命令がLCDに表示される。
ぼくのかんがえたさいきょうのCPU 〜 仕様確定



はっきりいって、命令にバイト数をつかえてきれいなコード体系にできれば、こんな面倒なことをしなくていいんだけど、メモリが少ない前提なので、やっぱこういう部分でがんばらないといけない。
あと、8080/Z80の命令体系に従わない部分をつくるなら、もうめんどうなところは全部命令体系を変更したほうが実装が楽だし回路も小さくなったなーとか思ったり。


これを実装するときは、まずJavaでコードを書いて、それなりに動くことを確認してから、手作業ではあるけど機械的Verilogのコードに置き換えてます。
手続き的な処理を書くときには、このように書いたほうがよさげ。
このJavaコードは、そのまま逆アセンブラエミュレータのデコード部として使えます。ただ、エミュレータにするのであれば、デコード部と実行部をわける必要はないので、そのまま使うわけではないけど。


まだこのままでは使えないけど、デコーダ部のコードだけ、ぺろっと貼っておきます。

`define OP_STORE 4'd0
`define OP_LOAD 4'd1
`define OP_IN   4'd2
`define OP_OUT  4'd3
`define OP_MOVE 4'd4
`define OP_JR   4'd5
`define OP_RLA  4'd6
`define OP_RRA  4'd7
`define OP_ADD  4'd8
`define OP_SUB  4'd9
`define OP_AND  4'd10
`define OP_XOR  4'd11
`define OP_OR   4'd12
`define OP_MUL  4'd13
`define OP_DIV  4'd14
`define OP_MOD  4'd15

`define COND_NONE 3'd0
`define COND_NZ 3'd1
`define COND_Z 3'd2
`define COND_NC 3'd3
`define COND_C 3'd4

`define REG_BC 4'd0
`define REG_DE 4'd1
`define REG_HL 4'd2
`define REG_SP 4'd3
`define REG_AF 4'd4
`define REG_VW 4'd5
`define REG_PC 4'd6
`define REG_II 4'd7

`define REG_B 4'd0
`define REG_C 4'd1
`define REG_D 4'd2
`define REG_E 4'd3
`define REG_H 4'd4
`define REG_L 4'd5
`define REG_SPh 4'd6
`define REG_SPl 4'd7
`define REG_A 4'd8
`define REG_F 4'd9
`define REG_V 4'd10
`define REG_W 4'd11
`define REG_PCh 4'd12
`define REG_PCl 4'd13
`define REG_I 4'd14
`define REG_NONE 4'd15

`define TRUE 1'b1;
`define FALSE 1'b0;

module decoder(
    input [7:0] code,
    input [7:0] param,
    output reg[3:0] op_code
);
    //reg[3:0] op_code;
    reg op_dw;
    reg[1:0] op_byte;
    reg[2:0] op_cond;
    reg[15:0] op_param1;
    reg[15:0] op_param2;
    reg[3:0] op_outreg;
    
    reg[7:0] regs[15];
    wire [7:0] reg8_A = regs[`REG_A];
    wire [7:0] reg8_F = regs[`REG_F];
    wire [7:0] reg8_V = regs[`REG_V];
    wire [7:0] reg8_I = regs[`REG_I];
    wire [15:0] reg_BC = {regs[`REG_B], regs[`REG_C]};
    wire [15:0] reg_DE = {regs[`REG_D], regs[`REG_E]};
    wire [15:0] reg_HL = {regs[`REG_H], regs[`REG_L]};
    wire [15:0] reg_SP = {regs[`REG_SPh], regs[`REG_SPl]};
    wire [15:0] reg_AF = {regs[`REG_A], regs[`REG_F]};
    wire [15:0] reg_VW = {regs[`REG_V], regs[`REG_W]};
    wire [15:0] reg_PC = {regs[`REG_PCh], regs[`REG_PCl]};

    wire[1:0] sect = code[7:6];
    wire[2:0] src = code[2:0];
    wire[2:0] dest = code[5:3];
    wire[3:0] subop = code[3:0];
    wire[1:0] reg16 = code[5:4];
    wire[3:0] destReg = 
        (dest == `REG_SPh) ? `REG_V :
        (dest == `REG_SPl) ? `REG_A :
                            {1'b0, dest};
    wire[3:0] srcReg =
        (src == `REG_SPh) ? `REG_V :
        (src == `REG_SPl) ? `REG_A :
                            {1'b0, src};
    wire[7:0] reg8_src = regs[srcReg];
    wire[7:0] reg8_dest = regs[destReg];
    wire[7:0] reg8_reg16 = regs[reg16];
    wire[15:0] reg16_reg = {regs[{1'd0, reg16, 1'd0}], regs[{1'd0, reg16, 1'd1}]};

    always @(code) begin
        case(sect)
        2'b00: begin
            case(src)
            3'b000: begin
                case(dest)
                3'b000: begin
                    //NOP(内部的にはLD A,Aを行う)
                    op_param2 <= reg8_A;
                    op_outreg <= `REG_A;
                    op_dw <= `FALSE;
                    op_code <= `OP_MOVE;
                end
                3'b001: begin
                    //LD V,A
                    op_param2 <= reg8_A;
                    op_outreg <= `REG_V;
                    op_dw <= `FALSE;
                    op_code <= `OP_MOVE;
                end
                3'b010: begin
                    // HALT(内部的にはJR nn)
                    op_param1 <= reg_PC;
                    op_param2 <= 16'hffff;
                    op_code <= `OP_JR;
                    op_outreg <= `REG_PC;
                    op_dw <= `TRUE;
                end
                default: begin
                    // JR cond, e
                    op_cond <= dest - 4'b0011;
                    op_code <= `OP_JR;
                    op_param1 <= reg_PC;
                    op_param2 <= param;
                    op_outreg <= `REG_PC;
                    op_dw <= `TRUE;
                end
                endcase
            end
            4'b0001: begin
                // LD r, nn
                op_param2 <= param;
                op_code <= `OP_MOVE;
                op_outreg <= reg16;
                op_dw <= `TRUE;
            end
            4'b1001: begin
                // ADD HL, r
                op_param1 <= reg_HL;
                op_param2 <= reg16_reg;
                op_code <= `OP_ADD;
                op_outreg <= `REG_HL;
                op_dw <= `TRUE;
            end
            4'b0010: begin
                op_code <= `OP_STORE;
                case(reg16)
                2'b00: begin
                    //LD (BC), A;
                    op_param2 <= reg_BC;
                    op_param1 <= reg8_A;
                    op_outreg <= `REG_NONE;
                    op_dw <= `FALSE;
                end
                2'b01: begin
                    //LD (DE), A;
                    op_param2 <= reg_DE;
                    op_param1 <= reg8_A;
                    op_outreg <= `REG_NONE;
                    op_dw <= `FALSE;
                end
                2'b10: begin
                    //LD(nn), HL
                    op_param2 <= param;
                    op_param1 <= reg_HL;
                    op_dw <= `TRUE;
                    op_outreg <= `REG_NONE;
                end
                2'b11: begin
                    //LD(nn), A
                    op_param2 <= param;
                    op_param1 <= reg8_A;
                    op_dw <= `FALSE;
                    op_outreg <= `REG_NONE;
                end
                endcase
            end
            4'b1010: begin
                op_code <= `OP_LOAD;
                case(reg16)
                2'b00: begin
                    //LD A, (BC);
                    op_outreg <= `REG_A;
                    op_dw <= `FALSE;
                    op_param2 <= reg_BC;
                    op_code <= `OP_STORE;
                end
                2'b01: begin
                    //LD A, (DE);
                    op_outreg <= `REG_A;
                    op_dw <= `FALSE;
                    op_param2 <= reg_DE;
                    op_code <= `OP_STORE;
                end
                2'b10: begin
                    //LD HL, (nn)
                    op_outreg <= `REG_HL;
                    op_param2 <= param;
                    op_dw <= `TRUE;
                end
                2'b11: begin
                    //LD A, (nn)
                    op_outreg <= `REG_A;
                    op_dw <= `FALSE;
                    op_param2 <= param;
                end 
                endcase
            end
            4'b0011: begin
                //INC rr
                op_outreg <= reg16;
                op_param1 <= reg16_reg;
                op_param2 <= 1;
                op_code <= `OP_ADD;
                op_dw <= `TRUE;
            end
            4'b1011: begin
                //DEC rr
                op_outreg <= reg16;
                op_param1 <= reg16_reg;
                op_param2 <= 1;
                op_code <= `OP_SUB;
                op_dw <= `TRUE;
            end
            3'b100: begin
                //INC r
                op_param1 <= reg8_dest;
                op_outreg <= destReg;
                op_dw <= `FALSE;
                op_code <= `OP_ADD;
                op_param2 <= 1;
            end
            3'b101: begin
                //DEC r
                op_param1 <= reg8_dest;
                op_outreg <= destReg;
                op_dw <= `FALSE;
                op_code <= `OP_SUB;
                op_param2 <= 1;
            end
            3'b110: begin
                op_dw <= `FALSE;
                case(dest)
                `REG_SPh: begin
                    // LD (HL), n
                    op_outreg <= `REG_NONE;
                    op_param1 <= param;
                    op_param2 <= reg_HL;
                    op_code <= `OP_STORE;
                end
                default: begin
                    // LD r, n
                    op_param2 <= param;
                    op_outreg <= destReg;
                    op_code <= `OP_MOVE;
                end
                endcase
            end
            3'b111: begin
                op_dw <= `FALSE;
                case(dest)
                3'b000: begin
                    // RLCA
                    op_param1 <= reg8_A;
                    op_param2 <= reg8_F;
                    op_outreg <= `REG_A;
                    op_code <= `OP_RLA;
                end
                3'b001: begin
                    // RRCA
                    op_param1 <= reg8_A;
                    op_param2 <= reg8_F;
                    op_code <= `OP_RRA;
                    op_outreg <= `REG_A;
                end
                3'b010: begin
                    // RLA
                    op_param1 <= reg8_A;
                    op_param2 <= 0;
                    op_code <= `OP_RLA;
                    op_outreg <= `REG_A;
                end
                3'b011: begin
                    // RRA
                    op_param1 <= reg8_A;
                    op_param2 <= 0;
                    op_outreg <= `REG_A;
                    op_code <= `OP_RRA;
                end
                3'b100: begin
                    // LD A, (HL)
                    op_param2 <= reg_HL;
                    op_code <= `OP_LOAD;
                    op_outreg <= `REG_A;
                end
                3'b101: begin
                    // CPL
                    op_param1 <= reg8_A;
                    op_param2 <= 8'hff;
                    op_code <= `OP_XOR;
                    op_outreg <= `REG_A;
                end
                3'b110: begin
                    // SCF
                    op_param1 <= reg8_F;
                    op_param2 <= 1;
                    op_code <= `OP_OR;
                    op_outreg <= `REG_F;
                end
                3'b111: begin
                    // CCF
                    op_param1 <= reg8_F;
                    op_param2 <= 1;
                    op_code <= `OP_XOR;
                    op_outreg <= `REG_F;
                end
                endcase
            end
            endcase
        end
        2'b01: begin
            // LD r1, r2
            op_dw <= `FALSE;
            if(dest == `REG_SPh) begin
                // LD (HL), r
                op_code <= `OP_STORE;
                op_param2 <= reg_HL;
                op_param1 <= reg8_src;
                op_outreg <= `REG_NONE;
            end else if(src == `REG_SPh) begin
                if(dest == `REG_SPl) begin
                // LD A, V
                    op_code <= `OP_MOVE;
                    op_outreg <= `REG_A;
                    op_param2 <= reg8_V;
                end else begin
                    // LD r, (HL)
                    op_outreg <= destReg;
                    op_code <= `OP_LOAD;
                    op_param2 <= reg_HL;
                end 
            end else begin
                // LD r1, r2
                op_code <= `OP_MOVE;
                op_outreg <= destReg;
                op_param2 <= reg8_src;
            end 
        end
        2'b10: begin
            op_outreg <= (dest == 3'b111) ? `REG_NONE : `REG_A;
            op_dw <= `FALSE;
            op_param1 <= reg8_A;
            op_param2 <= reg8_src;
            case(dest)
            3'b000: begin
                // ADD
                op_code <= `OP_ADD;
                op_cond <= 0;
            end
            3'b001: begin
                // ADC
                op_code <= `OP_ADD;
                op_cond <= reg8_F[0];
            end
            3'b010: begin
                // SUB
                op_code <= `OP_SUB;
                op_cond <= 0;
            end
            3'b011: begin
                // SBC
                op_code <= `OP_SUB;
                op_cond <= reg8_F[0];
            end
            3'b100: begin
                // AND
                op_code <= `OP_AND;
            end
            3'b101: begin
                // XOR
                op_code <= `OP_XOR;
            end
            3'b110: begin
                // OR
                op_code <= `OP_OR;
            end
            3'b111: begin
                // CP
                op_code <= `OP_SUB;
            end 
            endcase
        end
        2'b11: begin
            case(subop)
            4'b0110, 4'b1110: begin
                //op A,n
                op_outreg <= (dest == 3'b111) ? `REG_NONE : `REG_A;
                op_dw <= `FALSE;
                case(dest)
                3'b000: begin
                    // ADD
                    op_code <= `OP_ADD;
                    op_cond <= 0;
                end
                3'b001: begin
                    // ADC
                    op_code <= `OP_ADD;
                    op_cond <= reg8_F[0];
                end
                3'b010: begin
                    // SUB
                    op_code <= `OP_SUB;
                    op_cond <= 0;
                end
                3'b011: begin
                    // SBC
                    op_code <= `OP_SUB;
                    op_cond <= reg8_F[0];
                end
                3'b100: begin
                    // AND
                    op_code <= `OP_AND;
                end
                3'b101: begin
                    // XOR
                    op_code <= `OP_XOR;
                end
                3'b110: begin
                    // OR
                    op_code <= `OP_OR;
                end
                3'b111: begin
                    // CP
                    op_code <= `OP_SUB;
                end 
                endcase
                op_param1 <= reg8_A;
                op_param2 <= param;
                
            end
            4'b0010, 4'b1010: begin
                op_dw <= `TRUE;
                if(!dest[2]) begin
                    // JP f, nn
                    op_param2 <= param;
                    op_outreg <= `REG_PC;
                    op_cond <= {1'b0, dest[1:0]} + 3'd1;
                    op_code <= `OP_MOVE;
                end else begin
                    case(dest)
                    3'b100: begin
                        //INCW HL
                        op_outreg <= `REG_HL;
                        op_param1 <= reg_HL;
                        op_param2 <= 2;
                        op_code <= `OP_ADD;
                    end
                    3'b101: begin
                        // OUT (BC), HL
                        op_code <= `OP_OUT;
                        op_param1 <= reg_HL;
                        op_param2 <= reg_BC;
                        op_outreg <= `REG_NONE;
                    end
                    3'b110: begin
                        //INCW SP
                        op_outreg <= `REG_SP;
                        op_param1 <= reg_SP;
                        op_param2 <= 2;
                        op_code <= `OP_ADD;
                    end
                    3'b111: begin
                        // IN HL, (BC)
                        op_code <= `OP_IN;
                        op_param2 <= reg_BC;
                        op_outreg <= `REG_HL;
                    end 
                    endcase
                end 
            end
            4'b0000: begin
                op_dw <= `TRUE;
                case(reg16)
                2'b00: begin
                    // LD (HL), BC
                    op_param1 <= reg_BC;
                    op_param2 <= reg_HL;
                    op_code <= `OP_STORE;
                    op_outreg <= `REG_NONE;
                end
                2'b01: begin
                    // LD (HL), DE
                    op_param1 <= reg_DE;
                    op_param2 <= reg_HL;
                    op_code <= `OP_STORE;
                    op_outreg <= `REG_NONE;
                end
                2'b10: begin
                    // LD BC, (HL)
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_BC;
                    op_code <= `OP_LOAD;
                end
                2'b11: begin
                    // LD DE, (HL)
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_DE;
                    op_code <= `OP_LOAD;
                end
                endcase
            end
            4'b0001: begin
                // LD (SP), rr
                op_code <= `OP_STORE;
                op_outreg <= `REG_NONE;
                op_param1 <= (reg16 == `REG_SP) ? reg_AF : reg16_reg;
                op_param2 <= reg_SP;
                op_dw <= `TRUE;
            end
            4'b0011: begin
                case(reg16)
                2'b00: begin
                    // JP nn
                    op_outreg <= `REG_PC;
                    op_param2 <= param;
                    op_code <= `OP_MOVE;
                    op_dw <= `TRUE;
                end
                2'b01: begin
                    // OUT (n), A
                    op_param1 <= reg8_A;
                    op_param2 <= param;
                    op_code <= `OP_OUT;
                    op_outreg <= `REG_NONE;
                    op_dw <= `FALSE;
                end
                2'b10: begin
                    // OUT (BC), A
                    op_param1 <= reg8_A;
                    op_param2 <= reg_BC;
                    op_code <= `OP_OUT;
                    op_outreg <= `REG_NONE;
                    op_dw <= `FALSE;
                end
                2'b11: begin
                    // DI
                    op_param2 <= 1;
                    op_outreg <= `REG_I;
                    op_dw <= `FALSE;
                    op_code <= `OP_MOVE;
                end
                endcase
            end
            4'b0100: begin
                op_dw <= `TRUE;
                case(reg16)
                2'b00: begin
                    // LD VW, (SP)
                    op_param2 <= reg_SP;
                    op_outreg <= `REG_VW;
                    op_code <= `OP_LOAD;
                end
                2'b01: begin
                    // LD (SP), VW
                    op_param1 <= reg_VW;
                    op_param2 <= reg_SP;
                    op_outreg <= `REG_VW;
                    op_code <= `OP_STORE;
                end
                2'b10: begin
                    // DECW HL
                    op_outreg <= `REG_HL;
                    op_param1 <= reg_HL;
                    op_param2 <= 2;
                    op_code <= `OP_SUB;
                end
                2'b11: begin
                    // DECW SP
                    op_outreg <= `REG_SP;
                    op_param1 <= reg_SP;
                    op_param2 <= 2;
                    op_code <= `OP_SUB;
                end
                endcase
            end
            4'b0101: begin
                // LD rr, (SP)
                op_code <= `OP_LOAD;
                op_dw <= `TRUE;
                op_outreg <= reg16 == `REG_SP ? `REG_AF : reg16;
                op_param2 <= reg_SP;
            end
            4'b0111: begin
                // MUL A, r
                op_param1 <= reg8_A;
                op_param2 <= reg8_reg16;
                op_outreg <= `REG_HL;
                op_dw <= `TRUE;
                op_code <= `OP_MUL;
            end
            4'b1000: begin
                op_dw <= `TRUE;
                case(reg16)
                2'b00: begin
                    // JP Z, (VW)
                    op_cond <= `COND_Z;
                    op_code <= `OP_MOVE;
                    op_outreg <= `REG_PC;
                    op_param2 <= reg_VW;
                end
                2'b01: begin
                    // JP C, (VW)
                    op_cond <= `COND_C;
                    op_code <= `OP_MOVE;
                    op_outreg <= `REG_PC;
                    op_param2 <= reg_VW;
                end
                2'b10: begin
                    // SUB HL, BC
                    op_code <= `OP_SUB;
                    op_param1 <= reg_HL;
                    op_param2 <= reg_BC;
                    op_outreg <= `REG_HL;
                end
                2'b11: begin
                    // SUB HL, DE
                    op_code <= `OP_SUB;
                    op_param1 <= reg_HL;
                    op_param2 <= reg_DE;
                    op_outreg <= `REG_HL;
                end 
                endcase
            end
            4'b1001: begin
                op_dw <= `TRUE;
                case(reg16)
                2'b00: begin
                    //JP (VW)
                    op_code <= `OP_MOVE;
                    op_param2 <= reg_VW;
                    op_outreg <= `REG_PC;
                end
                2'b01: begin
                    //MUL BC, DE
                    op_code <= `OP_MUL;
                    op_param1 <= reg_BC;
                    op_param2 <= reg_DE;
                    op_outreg <= `REG_HL;
                end
                2'b10: begin
                    //JP (HL)
                    op_code <= `OP_MOVE;
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_PC;
                end
                2'b11: begin
                    //LD SP, HL
                    op_code <= `OP_MOVE;
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_SP;
                end
                endcase
            end
            4'b1011: begin
                op_dw <= `FALSE;
                case(reg16)
                2'b00: begin
                    // LD V, (HL)
                    op_code <= `OP_LOAD;
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_V;
                end
                2'b01: begin
                    // IN A, (n)
                    op_code <= `OP_IN;
                    op_outreg <= `REG_A;
                    op_param2 <= param;
                end
                2'b10: begin
                    // IN A, (BC)
                    op_code <= `OP_IN;
                    op_outreg <= `REG_A;
                    op_param2 <= reg_BC;
                end
                2'b11: begin
                    // EI
                    op_code <= `OP_MOVE;
                    op_param2 <= 0;
                    op_outreg <= `REG_I;
                end
                endcase
            end
            4'b1100: begin
                op_dw <= `TRUE;
                if(reg16 == 2'b11) begin
                    // DIV BC, DE
                    op_param1 <= reg_BC;
                    op_param2 <= reg_DE;
                    op_code <= `OP_DIV;
                    op_outreg <= `REG_HL;
                end else begin
                    // LD rr, VW
                    op_param2 <= reg_VW;
                    op_outreg <= reg16;
                    op_code <= `OP_MOVE;
                end 
            end
            4'b1101: begin
                op_dw <= `TRUE;
                if(reg16 == 2'b11) begin
                    // MOD BC, DE
                    op_param1 <= reg_BC;
                    op_param2 <= reg_DE;
                    op_code <= `OP_MOD;
                    op_outreg <= `REG_HL;
                end else begin
                    // LD VW, rr
                    op_param2 <= reg16_reg;
                    op_outreg <= `REG_VW;
                    op_code <= `OP_MOVE;
                end 
            end
            4'b1111: begin
                // DIV A, r
                op_dw <= `FALSE;
                op_param1 <= reg8_A;
                op_param2 <= reg8_reg16;
                op_outreg <= `REG_A;
                op_code <= `OP_DIV;
            end
            endcase
        end
        endcase
    end

endmodule