Improved screen rendering

This commit is contained in:
copy 2015-04-12 21:40:25 +02:00
parent b9a328f163
commit 265afd0f45
2 changed files with 220 additions and 276 deletions

View file

@ -43,6 +43,7 @@ function ScreenAdapter(screen_container, bus)
scale_y = 1,
graphical_mode_width,
graphical_mode_height,
modified_pixel_min = 0,
modified_pixel_max = 0,
@ -144,14 +145,14 @@ function ScreenAdapter(screen_container, bus)
this.set_mode(data);
}, this);
bus.register("screen-put-pixel-linear", function(data)
bus.register("screen-fill-buffer-end", function(data)
{
this.put_pixel_linear(data[0], data[1]);
}, this);
bus.register("screen-put-pixel-linear32", function(data)
{
this.put_pixel_linear32(data[0], data[1]);
var min = data[0];
var max = data[1];
this.update_buffer(min, max);
}, this);
bus.register("screen-put-char", function(data)
{
//console.log(data);
@ -230,51 +231,12 @@ function ScreenAdapter(screen_container, bus)
function update_graphical()
{
if(modified_pixel_min < modified_pixel_max)
{
var top = modified_pixel_min / graphical_mode_width >> 2;
var height = ((modified_pixel_max - modified_pixel_min) / graphical_mode_width >> 2) + 1;
graphic_context.putImageData(
graphic_image_data,
0, 0,
0, top,
graphical_mode_width, height
);
modified_pixel_min = 1e7;
modified_pixel_max = 0;
}
this.bus.send("screen-fill-buffer");
this.timer();
}
update_graphical = update_graphical.bind(this);
// put a single color component in the linear buffer
this.put_pixel_linear = function(index, color)
{
if(index >= graphic_buffer.length)
{
return;
}
modified_pixel_min = index < modified_pixel_min ? index : modified_pixel_min;
modified_pixel_max = index > modified_pixel_max ? index : modified_pixel_max;
// (addr + 1) ^ 3: Change BGR (svga) order to RGB (canvas)
graphic_buffer[(index + 1) ^ 3] = color;
};
// put a single color
this.put_pixel_linear32 = function(index, color)
{
modified_pixel_min = index < modified_pixel_min ? index : modified_pixel_min;
modified_pixel_max = index > modified_pixel_max ? index : modified_pixel_max;
// change BGR order to RGB
graphic_buffer32[index >> 2] = 0xFF000000 | color >> 16 & 0xFF | color << 16 | color & 0xFF00;
};
this.destroy = function()
{
};
@ -351,12 +313,10 @@ function ScreenAdapter(screen_container, bus)
graphic_buffer = new Uint8Array(graphic_image_data.data.buffer);
graphic_buffer32 = new Int32Array(graphic_image_data.data.buffer);
for(var i = 3; i < graphic_buffer.length; i += 4)
{
graphic_buffer[i] = 255;
}
graphical_mode_width = width;
graphical_mode_height = height;
this.bus.send("screen-tell-buffer", [graphic_buffer32], [graphic_buffer32.buffer]);
};
this.set_scale = function(s_x, s_y)
@ -474,5 +434,25 @@ function ScreenAdapter(screen_container, bus)
row_element.appendChild(fragment);
};
this.update_buffer = function(min, max)
{
if(max < min)
{
return;
}
var min_y = min / graphical_mode_width | 0;
var max_y = max / graphical_mode_width | 0;
graphic_context.putImageData(
graphic_image_data,
0, 0,
0, min_y,
graphical_mode_width, max_y - min_y + 1
);
};
this.init();
}

View file

@ -17,6 +17,9 @@ var
/** @const */
MAX_BPP = 32;
/** @const */
var VGA_PLANAR_REAL_BUFFER_START = 4 * VGA_BANK_SIZE;
/**
* @constructor
@ -76,9 +79,6 @@ function VGAScreen(cpu, bus, vga_memory_size)
/** @type {boolean} */
this.graphical_mode = false;
/** @type {boolean} */
this.do_complete_redraw = false;
/*
* VGA palette containing 256 colors for video mode 13 etc.
* Needs to be initialised by the BIOS
@ -104,7 +104,7 @@ function VGAScreen(cpu, bus, vga_memory_size)
this.svga_enabled = false;
/** @type {number} */
this.svga_bpp = 0;
this.svga_bpp = 32;
/**
* The video buffer offset created by VBE_DISPI_INDEX_Y_OFFSET
@ -214,6 +214,21 @@ function VGAScreen(cpu, bus, vga_memory_size)
this.svga_memory = new Uint8Array(this.vga_memory_size);
this.diff_addr_min = this.vga_memory_size;
this.diff_addr_max = 0;
this.dest_buffer = undefined;
bus.register("screen-tell-buffer", function(data)
{
this.dest_buffer = data[0];
}, this);
bus.register("screen-fill-buffer", function()
{
this.screen_fill_buffer();
}, this);
this._state_restore();
var me = this;
@ -268,7 +283,7 @@ VGAScreen.prototype._state_restore = function()
this.update_cursor();
}
this.do_complete_redraw = true;
this.complete_redraw();
};
VGAScreen.prototype.vga_memory_read = function(addr)
@ -317,12 +332,8 @@ VGAScreen.prototype.vga_memory_write = function(addr, value)
VGAScreen.prototype.vga_memory_write_graphical_linear = function(addr, value)
{
var offset = addr << 2,
color = this.vga256_palette[value];
this.bus.send("screen-put-pixel-linear", [offset | 2, color >> 16 & 0xFF]);
this.bus.send("screen-put-pixel-linear", [offset | 1, color >> 8 & 0xFF]);
this.bus.send("screen-put-pixel-linear", [offset, color & 0xFF]);
this.diff_addr_min = addr < this.diff_addr_min ? addr : this.diff_addr_min;
this.diff_addr_max = addr > this.diff_addr_max ? addr : this.diff_addr_max;
this.vga_memory[addr] = value;
};
@ -353,7 +364,7 @@ VGAScreen.prototype.vga_memory_write_graphical_planar = function(addr, value)
dbg_assert((this.planar_rotate_reg & 7) === 0, "unimplemented");
dbg_assert(write_mode !== 3, "unimplemented");
dbg_assert((this.planar_mode & 0x70) === 0, "unimplemented");
if(write_mode === 0)
{
plane0_byte = plane1_byte = plane2_byte = plane3_byte = value;
@ -470,29 +481,30 @@ VGAScreen.prototype.vga_memory_write_graphical_planar = function(addr, value)
}
// Shift these, so that the bits for the color are in
// the correct position in the while loop
// the correct position in the for loop
plane1_byte <<= 1;
plane2_byte <<= 2;
plane3_byte <<= 3;
// 8 pixels per byte, we start at high (addr << 3 | 7)
// << 2 because we're using put_pixel_linear
var offset = (addr << 3 | 7) << 2;
var offset = (addr << 3 | 7);
var actual_buffer_addr = offset + VGA_PLANAR_REAL_BUFFER_START;
this.diff_addr_min = actual_buffer_addr - 7 < this.diff_addr_min ? actual_buffer_addr - 7 : this.diff_addr_min;
this.diff_addr_max = actual_buffer_addr > this.diff_addr_max ? actual_buffer_addr : this.diff_addr_max;
for(var i = 0; i < 8; i++)
{
var color_index =
var color_index =
plane0_byte >> i & 1 |
plane1_byte >> i & 2 |
plane2_byte >> i & 4 |
plane3_byte >> i & 8,
color = this.vga256_palette[this.dac_map[color_index]];
color = this.dac_map[color_index];
this.bus.send("screen-put-pixel-linear", [offset | 2, color >> 16]);
this.bus.send("screen-put-pixel-linear", [offset | 1, color >> 8 & 0xFF]);
this.bus.send("screen-put-pixel-linear", [offset, color & 0xFF]);
this.svga_memory[offset + VGA_PLANAR_REAL_BUFFER_START] = color;
offset -= 4;
offset--;
}
};
@ -517,36 +529,6 @@ VGAScreen.prototype.text_mode_redraw = function()
}
};
VGAScreen.prototype.graphical_linear_redraw = function()
{
// TODO
};
VGAScreen.prototype.graphical_planar_redraw = function()
{
var addr = 0;
for(var y = 0; y < this.screen_height; y++)
{
for(var x = 0; x < this.screen_width; x += 8)
{
for(var i = 0; i < 8; i++)
{
var index = y * this.screen_width + x << 2;
var color =
this.plane0[addr] >> i & 1 |
this.plane1[addr] >> i << 1 & 2 |
this.plane2[addr] >> i << 2 & 4 |
this.plane3[addr] >> i << 3 & 8;
this.bus.send("screen-put-pixel-linear32", [index, this.vga256_palette[this.dac_map[color]]]);
}
addr++;
}
}
};
VGAScreen.prototype.vga_memory_write_text_mode = function(addr, value)
{
if(addr < 0x18000)
@ -612,172 +594,33 @@ VGAScreen.prototype.svga_memory_write8 = function(addr, value)
addr &= 0xFFFFFFF;
this.svga_memory[addr] = value;
if(!this.svga_enabled)
{
return;
}
addr -= this.svga_offset;
if(addr < 0)
{
return;
}
switch(this.svga_bpp)
{
case 32:
// 4th byte is meaningless
if((addr & 3) !== 3)
{
this.bus.send("screen-put-pixel-linear", [addr, value]);
}
break;
case 24:
addr = (addr << 2) / 3 | 0;
this.bus.send("screen-put-pixel-linear", [addr, value]);
break;
case 16:
if(addr & 1)
{
var word = this.svga_memory16[addr >> 1],
red = word & 0x1F,
green = word >> 5 & 0x3F,
blue = value >> 3 & 0x1F;
blue = blue * 0xFF / 0x1F | 0;
green = green * 0xFF / 0x3F | 0;
red = red * 0xFF / 0x1F | 0;
addr <<= 1;
this.bus.send("screen-put-pixel-linear", [addr, red]);
this.bus.send("screen-put-pixel-linear", [addr - 1, green]);
this.bus.send("screen-put-pixel-linear", [addr - 2, blue]);
}
break;
case 8:
var color = this.vga256_palette[value],
offset = addr << 2;
this.bus.send("screen-put-pixel-linear", [offset, color >> 16 & 0xFF]);
this.bus.send("screen-put-pixel-linear", [offset | 1, color >> 8 & 0xFF]);
this.bus.send("screen-put-pixel-linear", [offset | 2, color & 0xFF]);
break;
default:
if(DEBUG)
{
throw "SVGA: Unsupported BPP: " + this.svga_bpp;
}
}
this.diff_addr_min = addr < this.diff_addr_min ? addr : this.diff_addr_min;
this.diff_addr_max = addr > this.diff_addr_max ? addr : this.diff_addr_max;
};
VGAScreen.prototype.svga_memory_write32 = function(addr, value)
{
addr &= 0xFFFFFFF;
if(addr & 3 || this.svga_bpp !== 32)
{
this.svga_memory_write8(addr, value & 0xFF);
this.svga_memory_write8(addr + 1, value >> 8 & 0xFF);
this.svga_memory_write8(addr + 2, value >> 16 & 0xFF);
this.svga_memory_write8(addr + 3, value >> 24 & 0xFF);
return;
}
this.diff_addr_min = addr < this.diff_addr_min ? addr : this.diff_addr_min;
this.diff_addr_max = addr + 3 > this.diff_addr_max ? addr + 3 : this.diff_addr_max;
this.svga_memory32[addr >> 2] = value;
if(!this.svga_enabled)
{
return;
}
addr -= this.svga_offset;
if(addr < 0)
{
return;
}
switch(this.svga_bpp)
{
case 32:
this.bus.send("screen-put-pixel-linear32", [addr, value]);
break;
default:
if(DEBUG)
{
throw "SVGA: Unsupported BPP: " + this.svga_bpp;
}
}
this.svga_memory[addr] = value;
this.svga_memory[addr + 1] = value >> 8;
this.svga_memory[addr + 2] = value >> 16;
this.svga_memory[addr + 3] = value >> 24;
};
VGAScreen.prototype.svga_redraw = function()
VGAScreen.prototype.complete_redraw = function()
{
var addr = this.svga_offset;
var count = this.svga_height * this.svga_width;
var pixel = 0;
if(this.svga_bpp === 32)
if(this.graphical_mode)
{
var buf32 = new Int32Array(this.svga_memory.buffer);
addr >>= 2;
count <<= 2;
for(; pixel < count; )
{
this.bus.send("screen-put-pixel-linear32", [pixel, buf32[addr++]]);
pixel += 4;
}
}
else if(this.svga_bpp === 24)
{
count <<= 2;
for(; pixel < count; )
{
this.bus.send("screen-put-pixel-linear", [pixel++, this.svga_memory[addr++]]);
this.bus.send("screen-put-pixel-linear", [pixel++, this.svga_memory[addr++]]);
this.bus.send("screen-put-pixel-linear", [pixel++, this.svga_memory[addr++]]);
pixel++;
}
this.diff_addr_min = this.vga_memory_size;
this.diff_addr_max = 0;
}
else
{
// TODO
}
};
VGAScreen.prototype.timer = function()
{
if(this.do_complete_redraw)
{
this.do_complete_redraw = false;
if(this.svga_enabled)
{
this.svga_redraw();
}
else if(this.graphical_mode)
{
if(this.graphical_mode_is_linear)
{
this.graphical_linear_redraw();
}
else
{
this.graphical_planar_redraw();
}
}
else
{
this.text_mode_redraw();
}
this.text_mode_redraw();
}
};
@ -800,6 +643,14 @@ VGAScreen.prototype.set_size_text = function(cols_count, rows_count)
VGAScreen.prototype.set_size_graphical = function(width, height, bpp)
{
this.screen_width = width;
this.screen_height = height;
this.stats.bpp = bpp;
this.stats.is_graphical = true;
this.stats.res_x = width;
this.stats.res_y = height;
this.bus.send("screen-set-size-graphical", [width, height, bpp]);
};
@ -812,26 +663,29 @@ VGAScreen.prototype.set_video_mode = function(mode)
{
var is_graphical = false;
var width = 0;
var height = 0;
switch(mode)
{
case 0x03:
this.set_size_text(this.text_mode_width, 25);
break;
case 0x10:
this.screen_width = 640;
this.screen_height = 350;
width = 640;
height = 350;
is_graphical = true;
this.graphical_mode_is_linear = false;
break;
case 0x12:
this.screen_width = 640;
this.screen_height = 480;
width = 640;
height = 480;
is_graphical = true;
this.graphical_mode_is_linear = false;
break;
case 0x13:
this.screen_width = 320;
this.screen_height = 200;
width = 320;
height = 200;
is_graphical = true;
this.graphical_mode_is_linear = true;
break;
@ -843,10 +697,7 @@ VGAScreen.prototype.set_video_mode = function(mode)
if(is_graphical)
{
this.set_size_graphical(this.screen_width, this.screen_height, 8);
this.stats.res_x = this.screen_width;
this.stats.res_y = this.screen_height;
this.stats.bpp = 8;
this.set_size_graphical(width, height, 8);
}
this.graphical_mode = is_graphical;
@ -987,7 +838,8 @@ VGAScreen.prototype.port3C9_write = function(color_byte)
this.vga256_palette[index] = color;
this.dac_color_index++;
this.do_complete_redraw = true;
// Needs to be throttled:
//this.complete_redraw();
};
VGAScreen.prototype.port3CC_read = function()
@ -1019,19 +871,19 @@ VGAScreen.prototype.port3CF_write = function(value)
break;
case 4:
this.plane_read = value;
dbg_assert(value < 4);
//dbg_assert(value < 4, "unimplemented");
dbg_log("plane read: " + h(value), LOG_VGA);
break;
case 5:
this.planar_mode = value;
dbg_log("planar mode: " + h(value), LOG_VGA);
//dbg_log("planar mode: " + h(value), LOG_VGA);
break;
case 8:
this.planar_bitmap = value;
//dbg_log("planar bitmap: " + h(value), LOG_VGA);
break;
default:
dbg_log("3CF / graphics write " + h(this.graphics_index) + ": " + h(value), LOG_VGA);
//dbg_log("3CF / graphics write " + h(this.graphics_index) + ": " + h(value), LOG_VGA);
}
};
@ -1086,12 +938,12 @@ VGAScreen.prototype.port3D5_write = function(value)
break;
case 0xC:
this.start_address = this.start_address & 0xff | value << 8;
this.do_complete_redraw = true;
this.complete_redraw();
break;
case 0xD:
this.start_address = this.start_address & 0xff00 | value;
this.do_complete_redraw = true;
//dbg_log("start addr: " + h(this.start_address, 4), LOG_VGA);
this.complete_redraw();
dbg_log("start addr: " + h(this.start_address, 4), LOG_VGA);
break;
case 0xE:
this.cursor_address = this.cursor_address & 0xFF | value << 8;
@ -1217,7 +1069,7 @@ VGAScreen.prototype.port1CF_write = function(value)
// y offset
this.svga_offset = value * this.svga_bytes_per_line();
dbg_log("SVGA offset: " + h(this.svga_offset) + " y=" + h(value), LOG_VGA);
this.do_complete_redraw = true;
this.complete_redraw();
break;
default:
}
@ -1228,6 +1080,13 @@ VGAScreen.prototype.port1CF_write = function(value)
this.svga_enabled = false;
}
dbg_assert(this.svga_bpp !== 4, "unimplemented svga bpp: 4");
dbg_assert(this.svga_bpp !== 15, "unimplemented svga bpp: 15");
dbg_assert(this.svga_bpp === 4 || this.svga_bpp === 8 ||
this.svga_bpp === 15 || this.svga_bpp === 16 ||
this.svga_bpp === 24 || this.svga_bpp === 32,
"unexpected svga bpp: " + this.svga_bpp);
dbg_log("SVGA: enabled=" + this.svga_enabled + ", " + this.svga_width + "x" + this.svga_height + "x" + this.svga_bpp, LOG_VGA);
if(this.svga_enabled && this.dispi_index === 4)
@ -1235,10 +1094,6 @@ VGAScreen.prototype.port1CF_write = function(value)
this.set_size_graphical(this.svga_width, this.svga_height, this.svga_bpp);
this.bus.send("screen-set-mode", true);
this.stats.bpp = this.svga_bpp;
this.stats.is_graphical = true;
this.stats.res_x = this.svga_width;
this.stats.res_y = this.svga_height;
}
};
@ -1274,4 +1129,113 @@ VGAScreen.prototype.svga_register_read = function(n)
return 0xFF;
};
VGAScreen.prototype.screen_fill_buffer = function()
{
if(!this.graphical_mode && !this.svga_enabled)
{
// text mode
return;
}
if(!this.dest_buffer)
{
dbg_log("Cannot fill buffer: No destination buffer", LOG_VGA);
return;
}
if(this.diff_addr_max < this.diff_addr_min)
{
return;
}
var bpp = 0;
var offset = 0;
if(this.svga_enabled)
{
bpp = this.svga_bpp;
}
else
{
if(this.graphical_mode_is_linear)
{
bpp = 8;
}
else
{
bpp = 8;
offset = VGA_PLANAR_REAL_BUFFER_START;
}
}
var buffer = this.dest_buffer;
var start = this.diff_addr_min;
var end = this.diff_addr_max;
switch(bpp)
{
case 32:
var start_pixel = start >> 2;
var end_pixel = (end >> 2) + 1;
for(var i = start_pixel; i < end_pixel; i++)
{
var dword = this.svga_memory32[i];
buffer[i] = dword << 16 | dword >> 16 & 0xFF | dword & 0xFF00 | 0xFF000000;
}
break;
case 24:
var start_pixel = start / 3 | 0;
var end_pixel = (end / 3 | 0) + 1;
var addr = start_pixel * 3;
for(var i = start_pixel; addr < end; i++)
{
var red = this.svga_memory[addr++];
var green = this.svga_memory[addr++];
var blue = this.svga_memory[addr++];
buffer[i] = red << 16 | green << 8 | blue | 0xFF000000;
}
break;
case 16:
var start_pixel = start >> 1;
var end_pixel = (end >> 1) + 1;
for(var i = start_pixel; i < end_pixel; i++)
{
var word = this.svga_memory16[i];
var blue = (word >> 11) * 0xFF / 0x1F | 0;
var green = (word >> 5 & 0x3F) * 0xFF / 0x3F | 0;
var red = (word & 0x1F) * 0xFF / 0x1F | 0;
buffer[i] = red << 16 | green << 8 | blue | 0xFF000000;
}
break;
case 8:
var start_pixel = start - offset;
var end_pixel = end - offset + 1;
for(var i = start; i < end; i++)
{
var color = this.vga256_palette[this.svga_memory[i]];
buffer[i - offset] = color & 0xFF00 | color << 16 | color >> 16 | 0xFF000000;
}
break;
default:
dbg_assert(false, "Unsupported BPP: " + bpp);
}
this.diff_addr_min = this.vga_memory_size;
this.diff_addr_max = 0;
this.bus.send("screen-fill-buffer-end", [start_pixel, end_pixel]);
};