12:06
Verilog VGA на Javascript — Лисья нора
§ В чем интерес
Сегодня я попробую сконвертировать код 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); } }