§ В чем интерес

Сегодня я попробую сконвертировать код Verilog на JS и проверить, что из этого получится.

§ Исходный код на верилоге

module vga
(
    input               clock,
    output      [1:0]   r,
    output      [1:0]   g,
    output      [1:0]   b,
    output              hs,
    output              vs,
    output reg  [12:0]  address,
    input       [ 7:0]  data,
    input       [10:0]  cursor
);

reg [9:0]   x = 10'b0;
reg [8:0]   y = 9'b0;

// Мерцания
reg         flash  = 1'b0;
reg [ 3:0]  ticker = 1'b1;

// Временные регистры
reg [7:0]   rmask;      // Маска из знакогенератора
reg [7:0]   rcolor;     // Рисуемый цвет
reg [7:0]   tdata;      // Временный байт

wire xborder = x == 10'd799;
wire yborder = y ==  9'd448;
wire visible = x >= 48 && x < 48+640 && y >= 35 && y <= 35+400;

// Горизонтальная и вертикальная синхронизация
assign hs = x  < 48+640+16;
assign vs = y >= 35+400+12;

// Выравнивание
wire [9:0]  xv = x - 40; // Не 48, а 40, из-за конвейерного метода
wire [8:0]  yv = y - 35; // А здесь 35

// Итоговый пиксель
wire        pix = rmask[ ~xv[2:0] ];

// Если =0, то цвет букв становится цветом фона
wire        enfore  = ~(flash & rcolor[7]);

// Номер знакоместа от 0 до 1999
wire [10:0] cplace   = yv[8:4]*80 + xv[9:3];
wire        acursor  = (cursor + 1 == cplace) & (yv[3:0] >= 4'hE);

// Вычисление цвета
wire [ 3:0] curcolor = (pix & enfore) | (acursor & flash) ? rcolor[3:0] : rcolor[6:4];
wire [ 5:0] outcolor =
    //                    RR GG BB
    curcolor == 4'h0 ? 6'b00_00_00 : // Черный
    curcolor == 4'h1 ? 6'b00_00_01 : // Синий
    curcolor == 4'h2 ? 6'b00_01_00 : // Зеленый
    curcolor == 4'h3 ? 6'b00_01_01 : // Бирюзовый
    curcolor == 4'h4 ? 6'b01_00_00 : // Красный
    curcolor == 4'h5 ? 6'b01_00_01 : // Пурпурный
    curcolor == 4'h6 ? 6'b01_01_00 : // Коричневый
    curcolor == 4'h7 ? 6'b10_10_10 : // Светло-серый
    curcolor == 4'h8 ? 6'b01_01_01 : // Темно-серый
    curcolor == 4'h9 ? 6'b00_00_11 : // Ярко-синий
    curcolor == 4'hA ? 6'b00_11_00 : // Ярко-зеленый
    curcolor == 4'hB ? 6'b00_11_11 : // Голубой
    curcolor == 4'hC ? 6'b11_00_00 : // Ярко-красный
    curcolor == 4'hD ? 6'b11_00_11 : // Розовый
    curcolor == 4'hE ? 6'b11_11_00 : // Желтый
                       6'b11_11_11;  // Белый

assign {r, g, b} = visible ? outcolor : 6'b00_00_00;

always @(posedge clock) begin

    x <= xborder ? 1'b0 : x + 1'b1;
    y <= xborder && yborder ? 1'b0 : (xborder ? y + 1'b1 : y);

    /* verilator lint_off CASEINCOMPLETE */
    case (xv[2:0])

        // Запрос символа
        3'h0: begin address     <= {cplace, 1'b0}; end
        // Сохранить символ в tdata, запрос цвета (соседний байт)
        3'h1: begin address[0]  <= 1'b1; tdata <= data; end
        // Запрос знакогенератора, сохранение цвета
        3'h2: begin address     <= {1'b1, tdata, yv[3:0]}; tdata <= data; end
        // Новые значения для конвейера
        3'h7: begin rmask       <= data; rcolor <= tdata; end

    endcase

    if (xborder && yborder) begin

        ticker <= ticker + 1'b1;
        flash  <= ticker ? flash : ~flash;

    end

end

endmodule

§ Код на Жабноскрипте

class module_vga {

    constructor() {

        this.r = {
            x: 0,       // 9
            y: 0,       // 8
            flash: 0,   // 1
            ticker: 0,  // 4
            rmask: 0,   // 8
            rcolor: 0,  // 8
            tdata: 0,   // 8
        };

        this.io = {
            r: 0,
            g: 0,
            b: 0,
            hs: 0,
            vs: 0,
            address: 0,
            data: 0,
            cursor: 0,
        }

        this.w = {};
    }

    precompute() {

        this.w.xborder = +this.r.x === 799 ? 1 : 0;
        this.w.yborder = +this.r.y === 448 ? 1 : 0;
        this.w.visible = this.r.x >= 48 && this.r.x < 48 + 640 && this.r.y >= 35 && this.r.y <= 35 + 400 ? 1 : 0;

        this.io.hs = this.r.x < 48 + 640 + 16 ? 1 : 0;
        this.io.vs = this.r.y >= 35 + 400 + 12 ? 1 : 0;

        this.w.xv = this.r.x - 40;
        this.w.yv = this.r.y - 35;

        this.w.pix      = (this.r.rmask >> ((~this.w.xv) & 7)) & 1;
        this.w.enfore   = (this.r.flash & (this.r.rcolor >> 7) & 1) ? 0 : 1;
        this.w.cplace   = 80*((this.w.yv >> 4) & 31) + (this.w.xv >> 3);
        this.w.acursor  = (this.io.cursor + 1 === this.w.cplace) && ((this.w.yv & 15) >= 14) ? 1 : 0;
        this.w.curcolor = (this.w.pix & this.w.enfore) || (this.w.acursor & this.r.flash) ? (this.r.rcolor & 15) : (this.r.rcolor >> 4) & 7;

        switch (this.w.curcolor) {

            case 0: this.w.outcolor  = 0 * 16 + 0 * 4 + 0; break;
            case 1: this.w.outcolor  = 0 * 16 + 0 * 4 + 1; break;
            case 2: this.w.outcolor  = 0 * 16 + 1 * 4 + 0; break;
            case 3: this.w.outcolor  = 0 * 16 + 1 * 4 + 1; break;
            case 4: this.w.outcolor  = 1 * 16 + 0 * 4 + 0; break;
            case 5: this.w.outcolor  = 1 * 16 + 0 * 4 + 1; break;
            case 6: this.w.outcolor  = 1 * 16 + 1 * 4 + 0; break;
            case 7: this.w.outcolor  = 2 * 16 + 2 * 4 + 2; break;
            case 8: this.w.outcolor  = 1 * 16 + 1 * 4 + 1; break;
            case 9: this.w.outcolor  = 0 * 16 + 0 * 4 + 3; break;
            case 10: this.w.outcolor = 0 * 16 + 3 * 4 + 0; break;
            case 11: this.w.outcolor = 0 * 16 + 3 * 4 + 3; break;
            case 12: this.w.outcolor = 3 * 16 + 0 * 4 + 0; break;
            case 13: this.w.outcolor = 3 * 16 + 0 * 4 + 3; break;
            case 14: this.w.outcolor = 3 * 16 + 3 * 4 + 0; break;
            case 15: this.w.outcolor = 3 * 16 + 3 * 4 + 3; break;
        }

        this.io.r = this.w.visible ? (this.w.outcolor >> 4) & 3 : 0;
        this.io.g = this.w.visible ? (this.w.outcolor >> 2) & 3 : 0;
        this.io.b = this.w.visible ? (this.w.outcolor >> 0) & 3 : 0;
    }

    clock() {

        this.precompute();

        let _x  = this.w.xborder ? 0 : this.r.x + 1;
        let _y  = this.w.xborder && this.w.yborder ? 0 : (this.w.xborder ? this.r.y + 1 : this.r.y);
        let _tdata  = this.r.tdata,
            _rmask  = this.r.rmask,
            _rcolor = this.r.rcolor;

        switch (this.w.xv & 7) {

            // Запрос символа
            case 0:

                this.io.address = this.w.cplace << 1;
                break;

            // Сохранить символ в tdata, запрос цвета (соседний байт)
            case 1:

                this.io.address |= 1;
                _tdata = this.io.data;
                break;

            // Запрос знакогенератора, сохранение цвета
            case 2:

                this.io.address = (this.w.yv & 15) + 16*(this.r.tdata & 255) + 4096;
                _tdata = this.io.data;
                break;

            case 7:

                _rmask  = this.io.data;
                _rcolor = this.r.tdata;
                break;
        }

        if (this.w.xborder && this.w.yborder) {

            this.r.flash  = this.r.ticker > 0 ? this.r.flash : 1 - this.r.flash;
            this.r.ticker = (this.r.ticker + 1) & 15;
        }

        this.r.x = _x;
        this.r.y = _y;
        this.r.tdata = _tdata;
        this.r.rmask  = _rmask;
        this.r.rcolor = _rcolor;
    }
}

class vgajs extends Dos2D {

    init() {

        this.cls(0);

        this.mod  = new module_vga();
        this.vmem = new Uint8Array(4096);

        this.mod.io.cursor = 2;

        for (let i = 0; i < 4000; i += 2) {

            this.vmem[i]   = (i >> 1) & 255;
            this.vmem[i+1] = (i >> 1) & 255;
        }

        this.x = 0; this._hs = 1;
        this.y = 0; this._vs = 0;
    }

    // 640 x 400 x 70
    vga(hs, vs, color) {

        if (hs) this.x++;

        if (this._hs === 0 && hs === 1) {
            this.x = 0;
            this.y++;
        }

        if (this._vs === 1 && vs === 0) {
            this.x = 0;
            this.y = 0;
        }

        this._hs = hs;
        this._vs = vs;

        this.pset(this.x - 48, this.y - 35, color);
    }

    loop() {

        // this.measure(0);
        for (let i = 0; i < 75000; i++) {

            // Чтение из видеопамяти
            let address = this.mod.io.address;
            if (address >= 4096) {
                this.mod.io.data = this._font16[ address & 0xFFF ];
            } else {
                this.mod.io.data = this.vmem[address];
            }

            this.mod.clock();

            let hs    = this.mod.io.hs;
            let vs    = this.mod.io.vs;
            let color = (65536 * this.mod.io.r * 85) + 256 * this.mod.io.g * 85 + this.mod.io.b * 85;

            this.vga(hs, vs, color);
        }
        // this.measure(1);
    }
}