#include <palacios/vmm_types.h>
#include <palacios/vm_guest_mem.h>
#include <palacios/vmm_io.h>
+#include <interfaces/vmm_graphics_console.h>
#include "vga_regs.h"
+#ifndef V3_CONFIG_DEBUG_VGA
+#undef PrintDebug
+#define PrintDebug(fmt, args...)
+#endif
+
+#define DEBUG_MEM_DATA 0
+#define DEBUG_DEEP_MEM 0
+#define DEBUG_DEEP_RENDER 0
+
+
#define MEM_REGION_START 0xa0000
#define MEM_REGION_END 0xc0000
#define MEM_REGION_NUM_PAGES (((MEM_REGION_END)-(MEM_REGION_START))/4096)
typedef uint8_t *vga_map; // points to MAP_SIZE data
-/* HACK HACK HACK */
-
-struct fb_info {
- uint32_t rows;
- uint32_t cols;
- uint32_t bitsperpixel;
-};
-
-struct fb {
- struct fb_info *info;
- uint8_t * data;
-};
-
-//extern
-struct fb *palacios_linux_fb_hack_pointer;
-
-/* HACK HACK HACK */
+#define VGA_MAXX 1024
+#define VGA_MAXY 768
#define VGA_MISC_OUT_READ 0x3cc
#define VGA_DAC_NUM_ENTRIES 256
+
+#define VGA_FONT_WIDTH 8
+#define VGA_MAX_FONT_HEIGHT 32
+
struct vga_misc_regs {
/* Read: 0x3cc; Write: 0x3c2 */
struct vga_misc_out_reg vga_misc_out;
/* index 8 */
struct vga_preset_row_scan_reg vga_preset_row_scan;
/* index 9 */
- struct vga_max_row_scan_reg vga_row_scan;
+ struct vga_max_row_scan_reg vga_max_row_scan;
/* index 10 */
struct vga_cursor_start_reg vga_cursor_start;
/* index 11 */
/* index 17 */
struct vga_vertical_retrace_end_reg vga_vertical_retrace_end;
/* index 18 */
- vga_vertical_display_enable_end_reg vga_vertical_display_enable;
+ vga_vertical_display_enable_end_reg vga_vertical_display_enable_end;
/* index 19 */
vga_offset_reg vga_offset;
/* index 20 */
struct vga_internal {
- struct vm_device *dev;
+ struct vm_device *dev;
bool passthrough;
bool skip_next_passthrough_out; // for word access
+ struct v3_frame_buffer_spec target_spec;
+ v3_graphics_console_t host_cons;
+
uint32_t updates_since_render;
struct frame_buf *framebuf; // we render to this
};
+typedef enum {PLANAR_SHIFT, PACKED_SHIFT, C256_SHIFT} shift_mode_t;
-static int render(struct vga_internal *vga)
+
+static void find_text_char_dim(struct vga_internal *vga, uint32_t *w, uint32_t *h)
{
- vga->updates_since_render++;
+ *w = (vga->vga_sequencer.vga_clocking_mode.dot8 ? 8 : 9);
+
+ *h = vga->vga_crt_controller.vga_max_row_scan.max_scan_line+1;
+
+}
+
+static void find_text_res(struct vga_internal *vga, uint32_t *width, uint32_t *height)
+{
+ uint32_t vert_lsb, vert_msb;
+ uint32_t ph;
+ uint32_t ch, cw;
+
+ *width = (vga->vga_crt_controller.vga_horizontal_display_enable_end + 1)
+ - vga->vga_crt_controller.vga_end_horizontal_blanking.display_enable_skew;
+
+ vert_lsb = vga->vga_crt_controller.vga_vertical_display_enable_end; // 8 bits here
+ vert_msb = (vga->vga_crt_controller.vga_overflow.vertical_disp_enable_end9 << 1) // 2 bits here
+ + (vga->vga_crt_controller.vga_overflow.vertical_disp_enable_end8);
+
+ ph = ( (vert_msb << 8) + vert_lsb + 1) ; // pixels high (scanlines)
+
+ find_text_char_dim(vga,&cw, &ch);
+
+ *height = ph / ch;
+
+}
+
+
+static void find_text_data_start(struct vga_internal *vga, void **data)
+{
+ uint32_t offset;
+
+ offset = vga->vga_crt_controller.vga_start_address_high;
+ offset <<= 8;
+ offset += vga->vga_crt_controller.vga_start_address_low;
+
+ *data = vga->map[0]+offset;
+
+}
+
+
+static void find_text_attr_start(struct vga_internal *vga, void **data)
+{
+ uint32_t offset;
+
+ offset = vga->vga_crt_controller.vga_start_address_high;
+ offset <<= 8;
+ offset += vga->vga_crt_controller.vga_start_address_low;
+
+ *data = vga->map[1]+offset;
+
+}
+
+static void find_text_cursor_pos(struct vga_internal *vga, uint32_t *x, uint32_t *y, void **data)
+{
+ uint32_t w,h;
+ uint32_t offset;
+ uint32_t charsin;
+ void *buf;
+
+ find_text_res(vga,&w,&h);
+
+ find_text_data_start(vga,&buf);
+
+ offset = vga->vga_crt_controller.vga_cursor_location_high;
+ offset <<= 8;
+ offset += vga->vga_crt_controller.vga_cursor_location_low;
+
+ *data = vga->map[0]+offset;
+
+ charsin = (uint32_t)(*data - buf);
+
+ *x = charsin % w;
+ *y = charsin / w;
+
+}
+
- if (vga->updates_since_render<UPDATES_PER_RENDER) {
+static void find_text_font_start(struct vga_internal *vga, void **data, uint8_t char_map)
+{
+ uint32_t mapa_offset, mapb_offset;
+
+
+ switch (char_map) {
+ case 0:
+ mapa_offset = (vga->vga_sequencer.vga_char_map_select.char_map_a_sel_lsb << 1)
+ + vga->vga_sequencer.vga_char_map_select.char_map_a_sel_msb ;
+ *data = vga->map[2] + mapa_offset;
+ break;
+
+ case 1:
+ mapb_offset = (vga->vga_sequencer.vga_char_map_select.char_map_b_sel_lsb << 1)
+ + vga->vga_sequencer.vga_char_map_select.char_map_b_sel_msb ;
+ *data = vga->map[2] + mapb_offset;
+ break;
+ default:
+ PrintError("vga: unknown char_map given to find_text_font_start\n");
+ break;
+
+ }
+}
+
+static int extended_fontset(struct vga_internal *vga)
+{
+ if (vga->vga_sequencer.vga_mem_mode.extended_memory &&
+ ! ( (vga->vga_sequencer.vga_char_map_select.char_map_a_sel_lsb
+ == vga->vga_sequencer.vga_char_map_select.char_map_b_sel_lsb) &&
+ (vga->vga_sequencer.vga_char_map_select.char_map_a_sel_msb
+ == vga->vga_sequencer.vga_char_map_select.char_map_b_sel_msb))) {
+ return 1;
+ } else {
return 0;
}
- // PrintError("vga: render UNIMPLEMENTED\n");
+}
- vga->updates_since_render=0;
+static int blinking(struct vga_internal *vga)
+{
+ return vga->vga_attribute_controller.vga_attribute_mode_control.enable_blink;
+}
- if (!palacios_linux_fb_hack_pointer) {
- return 0;
+
+static void find_graphics_data_starting_offset(struct vga_internal *vga, uint32_t *offset)
+{
+
+ *offset = vga->vga_crt_controller.vga_start_address_high;
+ *offset <<= 8;
+ *offset += vga->vga_crt_controller.vga_start_address_low;
+}
+
+
+static void find_shift_mode(struct vga_internal *vga, shift_mode_t *mode)
+{
+ if (vga->vga_graphics_controller.vga_graphics_mode.c256) {
+ *mode=C256_SHIFT;
+ } else {
+ if (vga->vga_graphics_controller.vga_graphics_mode.shift_reg_mode) {
+ *mode=PACKED_SHIFT;
+ } else {
+ *mode=PLANAR_SHIFT;
+ }
+ }
+}
+
+
+static void find_graphics_res(struct vga_internal *vga, uint32_t *width, uint32_t *height)
+{
+ uint32_t vert_lsb, vert_msb;
+
+ *width = ((vga->vga_crt_controller.vga_horizontal_display_enable_end + 1)
+ - vga->vga_crt_controller.vga_end_horizontal_blanking.display_enable_skew);
+
+ *width *= (vga->vga_sequencer.vga_clocking_mode.dot8 ? 8 : 9);
+
+ vert_lsb = vga->vga_crt_controller.vga_vertical_display_enable_end; // 8 bits here
+ vert_msb = (vga->vga_crt_controller.vga_overflow.vertical_disp_enable_end9 << 1) // 2 bits here
+ + (vga->vga_crt_controller.vga_overflow.vertical_disp_enable_end8);
+
+ *height = ( (vert_msb << 8) + vert_lsb + 1) ; // pixels high (scanlines)
+
+ // At this point we have the resolution in dot clocks across and scanlines top-to-bottom
+ // This is usually the resolution in pixels, but it can be monkeyed with
+ // at least in the following ways
+
+ // vga sequencer dot clock divide by two
+ if (vga->vga_sequencer.vga_clocking_mode.dot_clock) {
+ *width/=2;
+ *height/=2;
+ }
+
+ // crt_controller.max_row_scan.double_scan => each row twice for 200=>400
+ if (vga->vga_crt_controller.vga_max_row_scan.double_scan) {
+ *height/=2;
+ }
+
+ // crt_controller.crt_mode_control.count_by_two => pixels twice as wide as normal
+ if (vga->vga_crt_controller.vga_crt_mode_control.count_by_two) {
+ *width /= 2;
+ }
+
+ // crt_controller.crt_mode_control.horizontal_retrace_select => pixels twice as tall as normal
+ if (vga->vga_crt_controller.vga_crt_mode_control.horizontal_retrace_select) {
+ *height /= 2;
+ }
+
+}
+
+
+static void find_graphics_cursor_pos(struct vga_internal *vga, uint32_t *x, uint32_t *y)
+{
+ // todo
+ *x=*y=0;
+}
+
+
+static void dac_lookup_24bit_color(struct vga_internal *vga,
+ uint8_t entry,
+ uint8_t *red,
+ uint8_t *green,
+ uint8_t *blue)
+{
+ // use internal or external palette?
+
+ vga_palette_reg *r = &(vga->vga_dac.vga_dac_palette[entry]);
+
+ // converting from 6 bits to 8 bits so << 2
+ *red = (*r & 0x3f) << 2;
+ *green = ((*r >> 8) & 0x3f) << 2;
+ *blue = ((*r >> 16) & 0x3f) << 2;
+
+}
+
+
+/*
+ Colors work like this:
+
+ 4 bit modes: index is to the internal palette on the attribute controller
+ that supplies 6 bits, but we need 8 to index the dac
+ 2 more (the msbs) are supplied from the color select register
+ we can optionally overwrite bits 5 and 4 from the color
+ select register as well, depending on a selection bit
+ in the mode control register. The result of all this is
+ 8 bit index for the dac
+
+ 8 bit modes: the attribute controller passes the index straight through
+ to the DAC.
+
+
+ The DAC translates from the 8 bit index into 6 bits per color channel
+ (18 bit color). We mulitply by 4 to get 24 bit color.
+*/
+
+static void find_24bit_color(struct vga_internal *vga,
+ uint8_t val,
+ uint8_t *red,
+ uint8_t *green,
+ uint8_t *blue)
+{
+ uint8_t di; // ultimate dac index
+
+ if (vga->vga_attribute_controller.vga_attribute_mode_control.pixel_width) {
+ // 8 bit mode does right to the DAC
+ di=val;
+ } else {
+ struct vga_internal_palette_reg pr = vga->vga_attribute_controller.vga_internal_palette[val%16];
+ di = pr.palette_data;
+
+ // Fix bits 5-4 if needed
+ if (vga->vga_attribute_controller.vga_attribute_mode_control.p54_select) {
+ di &= ~0x30; // clear 5-4
+ di |= vga->vga_attribute_controller.vga_color_select.sc4 << 4;
+ di |= vga->vga_attribute_controller.vga_color_select.sc5 << 5;
+ }
+
+ // We must always produce bits 6 and 7
+ di &= ~0xc0; // clear 7-6
+ di |= vga->vga_attribute_controller.vga_color_select.sc6 << 6;
+ di |= vga->vga_attribute_controller.vga_color_select.sc7 << 7;
+ }
+
+ dac_lookup_24bit_color(vga,di,red,green,blue);
+}
+
+static void render_graphics(struct vga_internal *vga, void *fb)
+{
+
+ struct v3_frame_buffer_spec *spec = &(vga->target_spec);
+
+ uint32_t gw, gh; // graphics w/h
+ uint32_t fw, fh; // fb w/h
+ uint32_t rgw, rgh; // region we can actually show on the frame buffer
+
+
+ uint32_t fx, fy; // pixel position within the frame buffer
+
+ uint32_t offset; // offset into the maps
+ uint8_t m; // map
+ uint8_t p; // pixel in the current map byte (0..7)
+
+ uint8_t r,g,b; // looked up colors for entry
+
+ void *pixel; // current pixel in the fb
+ uint8_t *red; // and the channels in the pixel
+ uint8_t *green; //
+ uint8_t *blue; //
+
+ uint8_t db[4]; // 4 bytes read at a time
+ uint8_t pb[8]; // 8 pixels assembled at a time
+
+ shift_mode_t sm; // shift mode
+
+ uint32_t cur_x, cur_y;
+
+
+ find_graphics_res(vga,&gw,&gh);
+
+ find_shift_mode(vga,&sm);
+
+ find_graphics_cursor_pos(vga,&cur_x,&cur_y);
+
+ find_graphics_data_starting_offset(vga,&offset);
+
+ fw = spec->width;
+ fh = spec->height;
+
+
+ PrintDebug("vga: attempting graphics render (%s): graphics_res=(%u,%u), fb_res=(%u,%u), "
+ "fb=0x%p offset=0x%x\n",
+ sm == PLANAR_SHIFT ? "planar shift" :
+ sm == PACKED_SHIFT ? "packed shift" :
+ sm == C256_SHIFT ? "color256 shift" : "UNKNOWN",
+ gw,gh,fw,fh,fb,offset);
+
+ // First we need to clip to what we can actually show
+ rgw = gw < fw ? gw : fw;
+ rgh = gh < fh ? gh : fh;
+
+ if (gw%8) {
+ PrintError("vga: warning: graphics width is not a multiple of 8\n");
+ }
+
+
+
+ // Now we scan across by row
+ for (fy=0;fy<gh;fy++) {
+ // by column
+ for (fx=0;fx<gw;
+ fx += (sm==C256_SHIFT ? 4 : 8) , offset++ ) {
+
+ // if any of these pixels are in the rendger region
+ if (fy < rgh && fx < rgw) {
+ // assemble all 4 or 8 pixels
+
+ // fetch the data bytes
+ for (m=0;m<4;m++) {
+ db[m]=*((uint8_t*)(vga->map[m]+offset));
+ }
+
+ // assemble
+ switch (sm) {
+ case PLANAR_SHIFT:
+ for (p=0;p<8;p++) {
+ pb[p]=
+ (( db[0] >> 7) & 0x1) |
+ (( db[1] >> 6) & 0x2) |
+ (( db[2] >> 5) & 0x4) |
+ (( db[3] >> 4) & 0x8) ;
+
+ for (m=0;m<4;m++) {
+ db[m] <<= 1;
+ }
+ }
+ break;
+
+ case PACKED_SHIFT:
+ // first 4 pixels use planes 0 and 2
+ for (p=0;p<4;p++) {
+ pb[p] =
+ ((db[2] >> 4) & 0xc) |
+ ((db[0] >> 6) & 0x3) ;
+ db[2] <<= 2;
+ db[0] <<= 2;
+ }
+ break;
+
+ // next 4 pixels use planes 1 and 3
+ for (p=4;p<8;p++) {
+ pb[p] =
+ ((db[3] >> 4) & 0xc) |
+ ((db[1] >> 6) & 0x3) ;
+ db[3] <<= 2;
+ db[1] <<= 2;
+ }
+ break;
+
+ case C256_SHIFT:
+ // this one is either very bizarre or as simple as this
+ for (p=0;p<4;p++) {
+ pb[p] = db[p];
+ }
+ break;
+ }
+
+ // draw each pixel
+ for (p=0;p< (sm==C256_SHIFT ? 4 : 8);p++) {
+
+ // find its color
+ find_24bit_color(vga,pb[p],&r,&g,&b);
+
+ // find its position in the framebuffer;
+ pixel = fb + (((fx + p) + (fy*spec->width)) * spec->bytes_per_pixel);
+ red = pixel + spec->red_offset;
+ green = pixel + spec->green_offset;
+ blue = pixel + spec->blue_offset;
+
+ // draw it
+ *red=r;
+ *green=g;
+ *blue=b;
+ }
+ }
+ }
}
+
+ PrintDebug("vga: render done\n");
+}
+
+
+static void render_text_cursor(struct vga_internal *vga, void *fb)
+{
+}
+
+
+
+//
+// A variant of this function could render to
+// a text console interface as well
+//
+static void render_text(struct vga_internal *vga, void *fb)
+{
+ // line graphics enable bit means to dupe column 8 to 9 when
+ // in 9 dot wide mode
+ // otherwise 9th dot is background
+
+ struct v3_frame_buffer_spec *spec = &(vga->target_spec);
+
+ uint32_t gw, gh; // graphics w/h
+ uint32_t tw, th; // text w/h
+ uint32_t rtw, rth; // rendered text w/h
+ uint32_t cw, ch; // char font w/h including 8/9
+ uint32_t fw, fh; // fb w/h
+
+ uint32_t px, py; // cursor position
+ uint32_t x, y, l, p; // text location, line and pixel within the char
+ uint32_t fx, fy; // pixel position within the frame buffer
+
+ uint8_t *text_start;
+ uint8_t *text; // points to current char
+ uint8_t *attr; // and its matching attribute
+ uint8_t *curs; // to where the cursor is
+ uint8_t *font; // to where the current font is
+
+ uint8_t fg_entry; // foreground color entry
+ uint8_t bg_entry; // background color entry
+ uint8_t fgr,fgg,fgb; // looked up foreground colors
+ uint8_t bgr,bgg,bgb; // looked up bg colors
+
+ uint8_t ct, ca; // the current char and attribute
+ struct vga_attribute_byte a; // decoded attribute
+
+ void *pixel; // current pixel in the fb
+ uint8_t *red; // and the channels in the pixel
+ uint8_t *green; //
+ uint8_t *blue; //
+
+
+
+ find_graphics_res(vga,&gw,&gh);
+ find_text_res(vga,&tw,&th);
+ find_text_char_dim(vga,&cw,&ch);
+ fw = spec->width;
+ fh = spec->height;
+
+ find_text_cursor_pos(vga,&px,&py,(void**)&curs);
+ find_text_data_start(vga,(void**)&text);
+ find_text_attr_start(vga,(void**)&attr);
+
+ find_text_font_start(vga,(void**)&font,0); // will need to switch this as we go since it is part of attr
+ PrintDebug("vga: attempting text render: graphics_res=(%u,%u), fb_res=(%u,%u), text_res=(%u,%u), "
+ "char_res=(%u,%u), cursor=(%u,%u) font=0x%p, text=0x%p, attr=0x%p, curs=0x%p, fb=0x%p"
+ "graphics extension=%u, extended_fontset=%d, blinking=%d\n",
+ gw,gh,fw,fh,tw,th,cw,ch,px,py,font,text,attr,curs,fb,
+ vga->vga_attribute_controller.vga_attribute_mode_control.enable_line_graphics_char_code,
+ extended_fontset(vga), blinking(vga));
+
+ text_start=text;
+
+ // First we need to clip to what we can actually show
+ rtw = tw < fw/cw ? tw : fw/cw;
+ rth = th < fh/ch ? th : fh/ch;
+
+
+
+ // Now let's scan by char across the whole thing
+ for (y=0;y<th;y++) {
+ for (x=0;x<tw;x++, text++, attr++) {
+ if (x < rtw && y < rth) {
+ // grab the character and attribute for the position
+ ct = *text;
+ ca = *attr;
+ a.val = ca;
+
+ // find the character's font bitmap (one byte per row)
+ find_text_font_start(vga,(void**)&font,
+ extended_fontset(vga) ? a.foreground_intensity_or_font_select : 0 );
+
+ font += ct * ((VGA_MAX_FONT_HEIGHT * VGA_FONT_WIDTH)/8);
+
+ // Now let's find out what colors we will be using
+ // foreground
+
+ if (!extended_fontset(vga)) {
+ fg_entry = a.foreground_intensity_or_font_select << 3;
+ } else {
+ fg_entry = 0;
+ }
+ fg_entry |= a.fore;
+
+ find_24bit_color(vga,fg_entry,&fgr,&fgg,&fgb);
+
+ if (!blinking(vga)) {
+ bg_entry = a.blinking_or_bg_intensity << 3;
+ } else {
+ bg_entry = 0;
+ }
+ bg_entry |= a.back;
+
+ find_24bit_color(vga,bg_entry,&bgr,&bgg,&bgb);
+
+ // Draw the character
+ for (l=0; l<ch; l++, font++) {
+ uint8_t frow = *font; // this is the row of of the font map
+ for (p=0;p<cw;p++) {
+ uint8_t fbit;
+
+ // a char can be 9 bits wide, but the font map
+ // is only 8 bits wide, which means we need to know where to
+ // get the 9th bit
+ if (p >= 8) {
+ // We get it from the font map if
+ // its line line graphics mode and its a graphics char
+ // otherwise it's the background color
+ if (vga->vga_attribute_controller.vga_attribute_mode_control.enable_line_graphics_char_code
+ && ct>=0xc0 && ct<=0xdf ) {
+ fbit = frow & 0x1;
+ } else {
+ fbit = 0;
+ }
+ } else {
+ fbit= (frow >> (7-p) ) & 0x1;
+ }
+
+ // We are now at the pixel level, with fbit being the pixel we draw (color+attr or bg+attr)
+ // For now, we will draw it as black/white
+
+ // find its position in the framebuffer;
+ fx = x*cw + p;
+ fy = y*ch + l;
+ pixel = fb + ((fx + (fy*spec->width)) * spec->bytes_per_pixel);
+ red = pixel + spec->red_offset;
+ green = pixel + spec->green_offset;
+ blue = pixel + spec->blue_offset;
+
+ // Are we on the cursor?
+ // if so, let's negate this pixel to invert the cell
+ if (curs==text) {
+ fbit^=0x1;
+ }
+ // update the framebuffer
+ if (fbit) {
+ *red=fgr;
+ *green=fgg;
+ *blue=fgb;
+ } else {
+ *red=bgr;
+ *green=bgg;
+ *blue=bgb;
+ }
+ }
+ }
+ }
+ }
+ PrintDebug("\n");
+ }
+
+}
+
+
+
+
+
+static void render_test(struct vga_internal *vga, void *fb)
+{
+ struct v3_frame_buffer_spec *s;
+
+ s=&(vga->target_spec);
+
+ if (fb && s->height>=480 && s->width>=640 ) {
+ uint8_t color = (uint8_t)(vga->updates_since_render);
+
+ uint32_t x, y;
+
+ for (y=0;y<480;y++) {
+ for (x=0;x<640;x++) {
+ void *pixel = fb + ((x + (y*s->width)) * s->bytes_per_pixel);
+ uint8_t *red = pixel + s->red_offset;
+ uint8_t *green = pixel + s->green_offset;
+ uint8_t *blue = pixel + s->blue_offset;
+
+ if (y<(480/4)) {
+ *red=color+x;
+ *green=0;
+ *blue=0;
+ } else if (y<(480/2)) {
+ *red=0;
+ *green=color+x;
+ *blue=0;
+ } else if (y<(3*(480/4))) {
+ *red=0;
+ *green=0;
+ *blue=color+x;
+ } else {
+ *red=*green=*blue=color+x;
+ }
+ }
+ }
+ }
+}
+
+static void render_black(struct vga_internal *vga, void *fb)
+{
+ struct v3_frame_buffer_spec *s;
+
+ s=&(vga->target_spec);
+
+ memset(fb,0,s->height*s->width*s->bytes_per_pixel);
+}
+
+static void render_maps(struct vga_internal *vga, void *fb)
+{
+
+ struct v3_frame_buffer_spec *s;
+
+
+ s=&(vga->target_spec);
+
+ if (fb && s->height>=768 && s->width>=1024 && !(vga->updates_since_render % 100)) {
+ // we draw the maps next, each being a 256x256 block appearing 32 pixels below the display block
+ uint8_t m;
+ uint32_t x,y;
+ uint8_t *b;
+
+ for (m=0;m<4;m++) {
+ b=(vga->map[m]);
+ for (y=480+32;y<768;y++) {
+ for (x=m*256;x<(m+1)*256;x++,b++) {
+ void *pixel = fb + ((x + (y*s->width)) * s->bytes_per_pixel);
+ uint8_t *red = pixel + s->red_offset;
+ uint8_t *green = pixel + s->green_offset;
+ uint8_t *blue = pixel + s->blue_offset;
+
+ *red=*green=*blue=*b;
+ }
+ }
+ }
+ }
+}
+
+
+static int render(struct vga_internal *vga)
+{
+ void *fb;
+
+
+ vga->updates_since_render++;
+
+ if (vga->updates_since_render%100) {
+ // skip render
+ return 0;
+ }
+
+ if (vga->host_cons && v3_graphics_console_inform_update(vga->host_cons)>0) {
+
+ fb = v3_graphics_console_get_frame_buffer_data_rw(vga->host_cons,&(vga->target_spec));
+
+ if (!(vga->vga_sequencer.vga_clocking_mode.screen_off)) {
+ if (vga->vga_attribute_controller.vga_attribute_mode_control.graphics) {
+ render_graphics(vga,fb);
+ } else {
+ render_text(vga,fb);
+ render_text_cursor(vga,fb);
+ }
+ } else {
+ render_black(vga,fb);
+ }
+
+ if (0) { render_test(vga,fb); }
+
+ // always render maps for now
+ render_maps(vga,fb);
+
+ v3_graphics_console_release_frame_buffer_data_rw(vga->host_cons);
+ }
return 0;
}
}
}
-static uint64_t find_offset(struct vga_internal *vga, addr_t guest_addr)
+static uint64_t find_offset_write(struct vga_internal *vga, addr_t guest_addr)
{
uint64_t mem_start, mem_end;
+ uint64_t size;
mem_start=mem_end=0;
get_mem_region(vga, &mem_start, &mem_end);
+
+ size=(mem_end-mem_start > 65536 ? 65536 : (mem_end-mem_start));
- return (guest_addr-mem_start) % (mem_end-mem_start > 65536 ? 65536 : (mem_end-mem_start));
-
+ if (vga->vga_sequencer.vga_mem_mode.odd_even) {
+ return (guest_addr-mem_start) % size;
+ } else {
+ // odd/even mode
+ return ((guest_addr-mem_start) >> 1 ) % size;
+ }
}
+
+
+// Determines which maps should be enabled for this single byte write
+// and what the increment (actually 1/increment for the copy loop
+//
+// memory_mode.odd_even == 0 => even address = maps 0 and 2 enabled; 1,3 otherwise
+//
+static uint8_t find_map_write(struct vga_internal *vga, addr_t guest_addr)
+{
+ uint8_t mm = vga->vga_sequencer.vga_map_mask.val;
+
+ if (vga->vga_sequencer.vga_mem_mode.odd_even) {
+ return mm;
+ } else {
+ if (guest_addr & 0x1) {
+ return mm & 0xa; // 0x1010
+ } else {
+ return mm & 0x5; // 0x0101
+ }
+ }
+}
+
+static uint8_t find_increment_write(struct vga_internal *vga, addr_t new_guest_addr)
+{
+ if (vga->vga_sequencer.vga_mem_mode.odd_even) {
+ return 1;
+ } else {
+ return !(new_guest_addr & 0x1);
+ }
+}
+
static int vga_write(struct guest_info * core,
uint_t length,
void * priv_data)
{
- int i;
struct vm_device *dev = (struct vm_device *)priv_data;
struct vga_internal *vga = (struct vga_internal *) dev->private_data;
memcpy(V3_VAddr((void*)guest_addr),src,length);
}
- PrintDebug("vga: data written was \"");
+#if DEBUG_MEM_DATA
+ int i;
+ PrintDebug("vga: data written was 0x");
+ for (i=0;i<length;i++) {
+ uint8_t c= ((char*)src)[i];
+ PrintDebug("%.2x", c);
+ }
+ PrintDebug(" \"");
for (i=0;i<length;i++) {
char c= ((char*)src)[i];
- PrintDebug("%c", (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') ? c : '.');
+ PrintDebug("%c", (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || (c==' ') ? c : '.');
}
PrintDebug("\"\n");
+#endif
/* Write mode determine by Graphics Mode Register (Index 05h).writemode */
-
+
+
switch (vga->vga_graphics_controller.vga_graphics_mode.write_mode) {
case 0: {
uint8_t ror = vga->vga_graphics_controller.vga_data_rotate.rotate_count;
uint8_t func = vga->vga_graphics_controller.vga_data_rotate.function;
- offset = find_offset(vga, guest_addr);
+ offset = find_offset_write(vga, guest_addr);
+#if DEBUG_DEEP_MEM
PrintDebug("vga: mode 0 write, offset=0x%llx, ror=%u, func=%u\n", offset,ror,func);
+#endif
- for (i=0;i<length;i++,offset++) {
+ for (i=0;i<length;i++,offset+=find_increment_write(vga,guest_addr+i)) {
// now for each map
uint8_t sr = vga->vga_graphics_controller.vga_set_reset.val & 0xf;
uint8_t esr = vga->vga_graphics_controller.vga_enable_set_reset.val &0xf;
uint8_t bm = vga->vga_graphics_controller.vga_bit_mask;
- uint8_t mm = vga->vga_sequencer.vga_map_mask.val;
+ uint8_t mm = find_map_write(vga,guest_addr+i);
+
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: write i=%u, mm=0x%x, bm=0x%x sr=0x%x esr=0x%x offset=0x%x\n",i,(unsigned int)mm,(unsigned int)bm, (unsigned int)sr, (unsigned int)esr,(unsigned int)offset);
+#endif
- for (mapnum=0;mapnum<4;mapnum++, sr>>=1, esr>>=1, bm>>=1, mm>>=1) {
+ for (mapnum=0;mapnum<4;mapnum++, sr>>=1, esr>>=1, mm>>=1) {
vga_map map = vga->map[mapnum];
uint8_t data = ((uint8_t *)src)[i];
uint8_t latchval = vga->latch[mapnum];
-
+
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: raw data=0x%x\n",data);
+#endif
// rotate data right
- data = (data>>ror) | data<<(8-ror);
-
+ if (ror) {
+ data = (data>>ror) | (data<<(8-ror));
+ }
+
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: data after ror=0x%x\n",data);
+#endif
// use SR bit if ESR is on for this map
- if (esr & 0x1) {
- data = (uint8_t)((((sint8_t)(sr&0x1))<<7)>>7); // expand sr bit
+ if (esr & 0x1) {
+ data = (sr&0x1) * -1;
+
}
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: data after esrr=0x%x\n",data);
+#endif
+
// Apply function
switch (func) {
case 0: // NOP
data ^= latchval;
break;
}
-
- // mux between latch and alu output
- if (bm & 0x1) {
- // use alu output, which is in data
- } else {
- // use latch value
- data=latchval;
- }
+
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: data after func=0x%x\n",data);
+#endif
+
+ // mux between the data byte and the latch byte on
+ // a per-bit basis
+ data = (bm & data) | ((~bm) & latchval);
+
+
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: data after bm mux=0x%x\n",data);
+#endif
// selective write
if (mm & 0x1) {
// write to this map
- //PrintDebug("vga: write map %u offset 0x%p map=0x%p pointer=0x%p\n",mapnum,(void*)offset,map,&(map[offset]));
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: write map %u offset 0x%p map=0x%p pointer=0x%p\n",mapnum,(void*)offset,map,&(map[offset]));
+#endif
map[offset] = data;
} else {
// skip this map
int i;
- uint64_t offset = find_offset(vga,guest_addr);
+ uint64_t offset = find_offset_write(vga,guest_addr);
+#if DEBUG_DEEP_MEM
PrintDebug("vga: mode 1 write, offset=0x%llx\n", offset);
+#endif
- for (i=0;i<length;i++,offset++) {
+ for (i=0;i<length;i++,offset+=find_increment_write(vga,guest_addr+i)) {
uint8_t mapnum;
- uint8_t mm = vga->vga_sequencer.vga_map_mask.val;
+ uint8_t mm = find_map_write(vga,guest_addr+i);
for (mapnum=0;mapnum<4;mapnum++, mm>>=1) {
vga_map map = vga->map[mapnum];
uint8_t func = vga->vga_graphics_controller.vga_data_rotate.function;
- offset = find_offset(vga, guest_addr);
+ offset = find_offset_write(vga, guest_addr);
+#if DEBUG_DEEP_MEM
PrintDebug("vga: mode 2 write, offset=0x%llx, func=%u\n", offset,func);
+#endif
- for (i=0;i<length;i++,offset++) {
+ for (i=0;i<length;i++,offset+=find_increment_write(vga,guest_addr+i)) {
// now for each map
uint8_t bm = vga->vga_graphics_controller.vga_bit_mask;
- uint8_t mm = vga->vga_sequencer.vga_map_mask.val;
+ uint8_t mm = find_map_write(vga,guest_addr+i);
for (mapnum=0;mapnum<4;mapnum++, bm>>=1, mm>>=1) {
vga_map map = vga->map[mapnum];
uint8_t latchval = vga->latch[mapnum];
// expand relevant bit to 8 bit
- // it's basically esr=1, sr=bit from write
- data = (uint8_t)(((sint8_t)(((data>>mapnum)&0x1)<<7))>>7);
-
+ // it's basically esr=1, sr=bit from mode 0 write
+ data = ((data>>mapnum)&0x1) * -1;
+
// Apply function
switch (func) {
case 0: // NOP
data ^= latchval;
break;
}
-
+
// mux between latch and alu output
- if (bm & 0x1) {
- // use alu output, which is in data
- } else {
- // use latch value
- data=latchval;
- }
+ data = (bm & data) | ((~bm) & latchval);
// selective write
if (mm & 0x1) {
uint8_t ror = vga->vga_graphics_controller.vga_data_rotate.rotate_count;
- offset = find_offset(vga, guest_addr);
+ offset = find_offset_write(vga, guest_addr);
PrintDebug("vga: mode 3 write, offset=0x%llx, ror=%u\n", offset,ror);
- for (i=0;i<length;i++,offset++) {
+ for (i=0;i<length;i++,offset+=find_increment_write(vga,guest_addr+i)) {
// now for each map
uint8_t data = ((uint8_t *)src)[i];
- data = (data>>ror) | data<<(8-ror);
+ if (ror) {
+ data = (data>>ror) | (data<<(8-ror));
+ }
uint8_t bm = vga->vga_graphics_controller.vga_bit_mask & data;
uint8_t sr = vga->vga_graphics_controller.vga_set_reset.val & 0xf;
- uint8_t mm = vga->vga_sequencer.vga_map_mask.val;
+ uint8_t mm = find_map_write(vga,guest_addr+i);
for (mapnum=0;mapnum<4;mapnum++, sr>>=1, bm>>=1, mm>>=1) {
vga_map map = vga->map[mapnum];
uint8_t latchval = vga->latch[mapnum];
- data = (uint8_t)((((sint8_t)(sr&0x1))<<7)>>7); // expand sr bit
-
-
+ // expand SR bit
+ data = (sr&0x1) * -1;
+
// mux between latch and alu output
- if (bm & 0x1) {
- // use alu output, which is in data
- } else {
- // use latch value
- data=latchval;
- }
+ data = (bm & data) | ((~bm) & latchval);
// selective write
if (mm & 0x1) {
}
-/*
-up to 256K mapped through a window of 32 to 128K
-
-most cards support linear mode as well
-
-Need to implement readability too
-
-Write extended memory bit to enable all 256K:
-
- Sequencer Memory Mode Register (Index 04h) . extended memory
-
-Must enable writes before effects happen:
-
- Miscellaneous Output Register (Read at 3CCh, Write at 3C2h).ram enable
-
-Choose which addresses are supported for CPU writes:
-
-Miscellaneous Graphics Register (Index 06h).memory map select
-
-00b -- A0000h-BFFFFh (128K region)
-01b -- A0000h-AFFFFh (64K region)
-10b -- B0000h-B7FFFh (32K region)
-11b -- B8000h-BFFFFh (32K region)
-
-There are three addressing modes: Chain 4, Odd/Even mode, and normal mode:
-
-Chain 4: This mode is used for MCGA emulation in the 320x200 256-color mode. The address is mapped to memory MOD 4 (shifted right 2 places.)
-
-Memory model: 64K 32 bit locations; divided into 4 64K bit planes
-
-
-
+static uint64_t find_offset_read(struct vga_internal *vga, addr_t guest_addr)
+{
+ uint64_t mem_start, mem_end;
+ uint64_t size;
+
+ mem_start=mem_end=0;
+
+ get_mem_region(vga, &mem_start, &mem_end);
+
+ size=(mem_end-mem_start > 65536 ? 65536 : (mem_end-mem_start));
-Assume linear framebuffer, starting at address buf:
+ if (!vga->vga_sequencer.vga_mem_mode.chain4) {
+ return (guest_addr-mem_start) % size;
+ } else {
+ // chain4 mode
+ return ((guest_addr - mem_start) >> 2) % size;
+ }
+}
-*/
+static uint8_t find_increment_read(struct vga_internal *vga, addr_t new_guest_addr)
+{
+ if (vga->vga_sequencer.vga_mem_mode.chain4) {
+ return !(new_guest_addr & 0x3);
+ } else {
+ return 1;
+ }
+}
static int vga_read(struct guest_info * core,
uint_t length,
void * priv_data)
{
- int i;
struct vm_device *dev = (struct vm_device *)priv_data;
struct vga_internal *vga = (struct vga_internal *) dev->private_data;
PrintDebug("vga: memory read: guest_addr=0x%p len=%u\n",(void*)guest_addr, length);
-
-
+
/*
Reading, 2 modes, set via Graphics Mode Register (index 05h).Read Mode:
which plane is determined by Read Map Select (Read Map Select Register (Index 04h)) */
uint8_t mapnum;
uint64_t offset;
-
- mapnum = vga->vga_graphics_controller.vga_read_map_select.map_select;
- offset = find_offset(vga,guest_addr);
-
- if (offset>=65536) {
- PrintError("vga: read to offset=%llu map=%u (%u bytes)\n",offset,mapnum,length);
- }
- memcpy(dst,(vga->map[mapnum])+offset,length);
- // load the latches with the last item read
- for (mapnum=0;mapnum<4;mapnum++) {
- vga->latch[mapnum] = vga->map[mapnum][offset+length-1];
+ if (vga->vga_sequencer.vga_mem_mode.chain4) {
+ uint32_t i;
+ offset = find_offset_read(vga,guest_addr);
+ // address bytes select the map
+ for (i=0;i<length;i++,offset+=find_increment_read(vga,guest_addr+i)) {
+ mapnum = (guest_addr+i) % 4;
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: mode 0 read, chain4, offset=0x%llx, mapnum=%u\n",offset,mapnum);
+#endif
+ ((uint8_t*)dst)[i] = *(vga->map[mapnum]+offset);
+
+ // presumably all the latches are to be reloaded, not just the selected one?
+ for (mapnum=0;mapnum<4;mapnum++) {
+ vga->latch[mapnum] = *(vga->map[mapnum]+offset);
+ }
+ }
+ } else {
+ mapnum = vga->vga_graphics_controller.vga_read_map_select.map_select;
+ offset = find_offset_read(vga,guest_addr);
+
+ if (offset>=65536) {
+ PrintError("vga: read to offset=%llu map=%u (%u bytes)\n",offset,mapnum,length);
+ }
+
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: mode 0 read, not-chain4, offset=0x%llx, mapnum=%u\n",offset,mapnum);
+#endif
+
+ memcpy(dst,(vga->map[mapnum])+offset,length);
+
+ // load the latches with the last item read
+ for (mapnum=0;mapnum<4;mapnum++) {
+ vga->latch[mapnum] = vga->map[mapnum][offset+length-1];
+ }
}
-
}
break;
uint8_t byte;
uint8_t bits;
- offset = find_offset(vga,guest_addr);
+ offset = find_offset_read(vga,guest_addr);
+
+#if DEBUG_DEEP_MEM
+ PrintDebug("vga: mode 1 read, offset=0x%llx, cc=0x%x, dc-0x%x\n",offset,cc,dc);
+#endif
+
for (i=0;i<length;i++,offset++) {
vga_map map;
}
- PrintDebug("vga: data read is \"");
+#if DEBUG_MEM_DATA
+ int i;
+ PrintDebug("vga: data read is 0x");
+ for (i=0;i<length;i++) {
+ uint8_t c= ((char*)dst)[i];
+ PrintDebug("%.2x", c);
+ }
+ PrintDebug(" \"");
for (i=0;i<length;i++) {
char c= ((char*)dst)[i];
- PrintDebug("%c", (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') ? c : '.');
+ PrintDebug("%c", (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || (c==' ') ? c : '.');
}
PrintDebug("\"\n");
+#endif
return length;
ret |= v3_dev_unhook_io(dev, VGA_DAC_PIXEL_MASK);
+ if (vga->host_cons) {
+ v3_graphics_console_close(vga->host_cons);
+ }
+
V3_Free(vga);
return 0;
char * dev_id = v3_cfg_val(cfg, "ID");
char * passthrough = v3_cfg_val(cfg, "passthrough");
-
- // DETERMINE THE FRAMEBUFFER AND SET IT EARLY
- // FRAMEBUFFER IS SUPPLIED BY THE BACKEND
+ char * hostframebuf = v3_cfg_val(cfg, "hostframebuf");
PrintDebug("vga: init_device\n");
vga->skip_next_passthrough_out=false;
}
+
+ if (hostframebuf && strcasecmp(hostframebuf,"enable")==0) {
+ struct v3_frame_buffer_spec req;
+
+ PrintDebug("vga: enabling host frame buffer console (GRAPHICS_CONSOLE)\n");
+
+ memset(&req,0,sizeof(struct v3_frame_buffer_spec));
+ memset(&(vga->target_spec),0,sizeof(struct v3_frame_buffer_spec));
+
+ req.height=VGA_MAXY;
+ req.width=VGA_MAXX;
+ req.bytes_per_pixel=4;
+ req.bits_per_channel=8;
+ req.red_offset=0;
+ req.green_offset=1;
+ req.blue_offset=2;
+
+
+ vga->host_cons = v3_graphics_console_open(vm,&req,&(vga->target_spec));
+
+ if (!vga->host_cons) {
+ PrintError("vga: unable to open host OS's graphics console\n");
+ free_vga(vga);
+ return -1;
+ }
+
+ if (memcmp(&req,&(vga->target_spec),sizeof(req))) {
+ PrintDebug("vga: warning: target spec differs from requested spec\n");
+ PrintDebug("vga: request: %u by %u by %u with %u bpc and r,g,b at %u, %u, %u\n", req.width, req.height, req.bytes_per_pixel, req.bits_per_channel, req.red_offset, req.green_offset, req.blue_offset);
+ PrintDebug("vga: response: %u by %u by %u with %u bpc and r,g,b at %u, %u, %u\n", vga->target_spec.width, vga->target_spec.height, vga->target_spec.bytes_per_pixel, vga->target_spec.bits_per_channel, vga->target_spec.red_offset, vga->target_spec.green_offset, vga->target_spec.blue_offset);
+
+ }
+ }
+
+ if (!vga->passthrough && !vga->host_cons) {
+ V3_Print("vga: neither passthrough nor host console are enabled - no way to display anything!\n");
+ }
+
+
// No memory store is allocated since we will use a full memory hook
// The VGA maps can be read as well as written
// Reads also affect writes, since they are how you fill the latches
init_vga(vga);
- PrintDebug("vga: successfully added and initialized, waiting for framebuffer attach\n");
+ PrintDebug("vga: successfully added and initialized.\n");
return 0;