gigatron/rom/Contrib/lb3361/runjs/html/vga.js
2025-01-28 19:17:01 +03:00

84 lines
2.4 KiB
JavaScript

export const VSYNC = 0x80;
export const HSYNC = 0x40;
/** Vga */
export class Vga {
/** Create a new Vga
* @param {HTMLCanvasElement} canvas
* @param {Gigatron} cpu
* @param {Object} options
*/
constructor(canvas, cpu, options) {
this.canvas = canvas;
this.canvas.width = options.horizontal.visible;
this.canvas.height = options.vertical.visible;
this.ctx = canvas.getContext('2d', {
alpha: false,
});
this.imageData = this.ctx.getImageData(
0, 0,
canvas.width, canvas.height);
this.pixels = this.imageData.data;
this.cpu = cpu;
this.row = 0;
this.minRow = options.vertical.backPorch + options.vertical.pulse;
this.maxRow = this.minRow + options.vertical.visible;
this.col = 0;
this.minCol = options.horizontal.backPorch + options.horizontal.pulse;
this.maxCol = this.minCol + options.horizontal.visible;
this.pixel = 0;
this.out = 0;
// turn all pixels black with full alpha
for (let i = 0; i < this.pixels.length; i++) {
this.pixels[i] = (i % 4) == 3 ? 255 : 0;
}
this.render();
}
/** draw the pixels into the canvas */
render() {
this.ctx.putImageData(this.imageData, 0, 0);
}
/** advance simulation by one tick */
tick() {
let out = this.cpu.out;
let falling = this.out & ~out;
if (falling & VSYNC) {
this.row = -1; // After 4 more CPU cycles HSYNC increments row to 0
this.pixel = 0;
this.render();
}
if (falling & HSYNC) {
this.col = 0;
this.row++;
}
// Chrome optimizer put this before the falling calculation
// if it follows immediately after it, so it got moved down here
this.out = out;
if ((this.row >= this.minRow && this.row < this.maxRow) &&
(this.col >= this.minCol && this.col < this.maxCol)) {
let pixels = this.pixels;
let pixel = this.pixel;
let r = (out ) & 3;
let g = (out >> 2) & 3;
let b = (out >> 4) & 3;
for (let i = 0; i < 4; i++) {
pixels[pixel++] = 85 * r;
pixels[pixel++] = 85 * g;
pixels[pixel++] = 85 * b;
pixel++;
}
this.pixel = pixel;
}
this.col += 4;
}
}