X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=palacios%2Fsrc%2Fdevices%2Fvga.c;h=6e5687ce80a27618749eeac6c456b3b5fb482de3;hb=8a3dbb70c29175bad79764a0b2f3961b98138bb2;hp=57024a8baf3e566d5fb5965cd251e4d257bef659;hpb=14e8d7e88362f4f7c3c558bbdcb31d72011c10f5;p=palacios.git diff --git a/palacios/src/devices/vga.c b/palacios/src/devices/vga.c index 57024a8..6e5687c 100644 --- a/palacios/src/devices/vga.c +++ b/palacios/src/devices/vga.c @@ -17,9 +17,2799 @@ * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ -#include #include +#include #include +#include +#include +#include #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) + +#define MAP_SIZE 65536 +#define MAP_NUM 4 + +#define UPDATES_PER_RENDER 100 + +typedef uint8_t *vga_map; // points to MAP_SIZE data + +#define VGA_MAXX 1024 +#define VGA_MAXY 768 + + +#define VGA_MISC_OUT_READ 0x3cc +#define VGA_MISC_OUT_WRITE 0x3c2 + +#define VGA_INPUT_STAT0_READ 0x3c2 + +#define VGA_INPUT_STAT1_READ_MONO 0x3ba +#define VGA_INPUT_STAT1_READ_COLOR 0x3da + +#define VGA_FEATURE_CONTROL_READ 0x3ca +#define VGA_FEATURE_CONTROL_WRITE_MONO 0x3ba +#define VGA_FEATURE_CONTROL_WRITE_COLOR 0x3da + +#define VGA_VIDEO_SUBSYS_ENABLE 0x3c3 + +#define VGA_SEQUENCER_ADDRESS 0x3c4 +#define VGA_SEQUENCER_DATA 0x3c5 +#define VGA_SEQUENCER_NUM 5 + + +#define VGA_CRT_CONTROLLER_ADDRESS_MONO 0x3b4 +#define VGA_CRT_CONTROLLER_ADDRESS_COLOR 0x3d4 +#define VGA_CRT_CONTROLLER_DATA_MONO 0x3b5 +#define VGA_CRT_CONTROLLER_DATA_COLOR 0x3d5 +#define VGA_CRT_CONTROLLER_NUM 25 + + +#define VGA_GRAPHICS_CONTROLLER_ADDRESS 0x3ce +#define VGA_GRAPHICS_CONTROLLER_DATA 0x3cf +#define VGA_GRAPHICS_CONTROLLER_NUM 9 + +#define VGA_ATTRIBUTE_CONTROLLER_ADDRESS_AND_WRITE 0x3c0 +#define VGA_ATTRIBUTE_CONTROLLER_READ 0x3c1 +#define VGA_ATTRIBUTE_CONTROLLER_NUM 21 + +#define VGA_DAC_WRITE_ADDR 0x3c8 +#define VGA_DAC_READ_ADDR 0x3c7 +#define VGA_DAC_DATA 0x3c9 +#define VGA_DAC_PIXEL_MASK 0x3c6 + +#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; + /* Read: 0x3c2 */ + struct vga_input_stat0_reg vga_input_stat0; + /* Read: 0x3?a 3ba for mono; 3da for cga set by misc.io_addr_sel */ + struct vga_input_stat1_reg vga_input_stat1; + /* Read: 0x3ca; Write: 0x3?a 3ba for mono 3da for color - set by misc.io_addr_sel*/ + struct vga_feature_control_reg vga_feature_control; + /* Read: 0x3c3; Write: 0x3c3 */ + struct vga_video_subsys_enable_reg vga_video_subsys_enable; +} __attribute__((packed)); + +struct vga_sequencer_regs { + /* Address register is 0x3c4, data register is 0x3c5 */ + /* 0x3c4 */ + struct vga_sequencer_addr_reg vga_sequencer_addr; + + /* these can be accessed via the index, offset on start + or via the specific regs. For this reason, it is essential + that this is all packed and that the order does not change */ + + uint8_t vga_sequencer_regs[0]; + + /* Index 0 */ + struct vga_reset_reg vga_reset; + /* Index 1 */ + struct vga_clocking_mode_reg vga_clocking_mode; + /* Index 2 */ + struct vga_map_mask_reg vga_map_mask; + /* Index 3 */ + struct vga_char_map_select_reg vga_char_map_select; + /* Index 4 */ + struct vga_mem_mode_reg vga_mem_mode; +} __attribute__((packed)); + +struct vga_crt_controller_regs { + /* Address Register is 0x3b4 or 0x3d4 */ + /* Data register is 0x3b5 or 0x3d5 based on mono versus color */ + struct vga_crt_addr_reg vga_crt_addr; + + /* these can be accessed via the index, offset on start + or via the specific regs. For this reason, it is essential + that this is all packed and that the order does not change */ + + uint8_t vga_crt_controller_regs[0]; + + /* index 0 */ + vga_horizontal_total_reg vga_horizontal_total; + /* index 1 */ + vga_horizontal_display_enable_end_reg vga_horizontal_display_enable_end; + /* index 2 */ + vga_start_horizontal_blanking_reg vga_start_horizontal_blanking; + /* index 3 */ + struct vga_end_horizontal_blanking_reg vga_end_horizontal_blanking; + /* index 4 */ + vga_start_horizontal_retrace_pulse_reg vga_start_horizontal_retrace_pulse; + /* index 5 */ + struct vga_end_horizontal_retrace_reg vga_end_horizontal_retrace; + /* index 6 */ + vga_vertical_total_reg vga_vertical_total; + /* index 7 */ + struct vga_overflow_reg vga_overflow; + /* index 8 */ + struct vga_preset_row_scan_reg vga_preset_row_scan; + /* index 9 */ + struct vga_max_row_scan_reg vga_max_row_scan; + /* index 10 */ + struct vga_cursor_start_reg vga_cursor_start; + /* index 11 */ + struct vga_cursor_end_reg vga_cursor_end; + /* index 12 */ + vga_start_address_high_reg vga_start_address_high; + /* index 13 */ + vga_start_address_low_reg vga_start_address_low; + /* index 14 */ + vga_cursor_location_high_reg vga_cursor_location_high; + /* index 15 */ + vga_cursor_location_low_reg vga_cursor_location_low; + /* index 16 */ + vga_vertical_retrace_start_reg vga_vertical_retrace_start; + /* index 17 */ + struct vga_vertical_retrace_end_reg vga_vertical_retrace_end; + /* index 18 */ + vga_vertical_display_enable_end_reg vga_vertical_display_enable_end; + /* index 19 */ + vga_offset_reg vga_offset; + /* index 20 */ + struct vga_underline_location_reg vga_underline_location; + /* index 21 */ + vga_start_vertical_blanking_reg vga_start_vertical_blanking; + /* index 22 */ + vga_end_vertical_blanking_reg vga_end_vertical_blanking; + /* index 23 */ + struct vga_crt_mode_control_reg vga_crt_mode_control; + /* index 24 */ + vga_line_compare_reg vga_line_compare; +} __attribute__((packed)); + +struct vga_graphics_controller_regs { + /* Address: 0x3ce Data: 0x3cf */ + + /* 0x3ce */ + struct vga_graphics_ctrl_addr_reg vga_graphics_ctrl_addr; + + /* these can be accessed via the index, offset on start + or via the specific regs. For this reason, it is essential + that this is all packed and that the order does not change */ + + uint8_t vga_graphics_controller_regs[0]; + + /* Index 0 */ + struct vga_set_reset_reg vga_set_reset; + /* Index 1 */ + struct vga_enable_set_reset_reg vga_enable_set_reset; + /* Index 2 */ + struct vga_color_compare_reg vga_color_compare; + /* Index 3 */ + struct vga_data_rotate_reg vga_data_rotate; + /* Index 4 */ + struct vga_read_map_select_reg vga_read_map_select; + /* Index 5 */ + struct vga_graphics_mode_reg vga_graphics_mode; + /* Index 6 */ + struct vga_misc_reg vga_misc; + /* Index 7 */ + struct vga_color_dont_care_reg vga_color_dont_care; + /* Index 8 */ + vga_bit_mask_reg vga_bit_mask; +} __attribute__((packed)); + + +struct vga_attribute_contoller_regs { + /* + Address AND WRITE: 0x3c0 + Read: 0x3c1 + + The write protocol is to write the index to 0x3c0 followed by + the data. The read protocol is to write the index to 0x3c0 + and then read from 0x3c1 + + IMPORTANT: write address, write data flips state back to write address + write address, read data DOES NOT + + To reset to write address state, read input status register 1 +*/ + enum { ATTR_ADDR, ATTR_DATA } state; //state of the flip flop + + /* 0x3c0 */ + struct vga_attribute_controller_address_reg vga_attribute_controller_addr; + + + + /* these can be accessed via the index, offset on start + or via the specific regs. For this reason, it is essential + that this is all packed and that the order does not change */ + + uint8_t vga_attribute_controller_regs[0]; + + /* Indices 0..15 */ + vga_internal_palette_regs vga_internal_palette; + /* Index 16 */ + struct vga_attribute_mode_control_reg vga_attribute_mode_control; + /* Index 17 */ + vga_overscan_color_reg vga_overscan_color; + /* Index 18 */ + struct vga_color_plane_enable_reg vga_color_plane_enable; + /* Index 19 */ + struct vga_horizontal_pixel_pan_reg vga_horizontal_pixel_pan; + /* Index 20 */ + struct vga_color_select_reg vga_color_select; +} __attribute__((packed)); + +struct vga_dac_regs { + enum {DAC_READ=0, DAC_WRITE} state; + enum {RED=0,GREEN,BLUE} channel; + vga_dac_pixel_mask_reg vga_pixel_mask; + vga_dac_write_addr_reg vga_dac_write_addr; + vga_dac_read_addr_reg vga_dac_read_addr; + // the dac_data register is used only to access the registers + // and thus has no representation here + vga_palette_reg vga_dac_palette[VGA_DAC_NUM_ENTRIES]; +} __attribute__((packed)); + + +struct vga_internal { + 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 + + // void *mem_store; // This is the region where the memory hooks will go + + vga_map map[MAP_NUM]; // the maps that the host writes to + + uint8_t latch[MAP_NUM]; // written to in any read, used during writes + + /* Range of I/O ports here for backward compat with MDA and CGA */ + struct vga_misc_regs vga_misc; + + /* Address Register is 0x3b4 or 0x3d4 */ + /* Data register is 0x3b5 or 0x3d5 based on MDA/CGA/VGA (backward compat) */ + struct vga_crt_controller_regs vga_crt_controller; + + /* Address register is 0x3c4, data register is 0x3c5 */ + struct vga_sequencer_regs vga_sequencer; + + /* Address: 0x3ce Data: 0x3cf */ + struct vga_graphics_controller_regs vga_graphics_controller; + + /* + Address AND WRITE: 0x3c0 + Read: 0x3c1 + Flip-Flop + */ + struct vga_attribute_contoller_regs vga_attribute_controller; + + /* + address for reads: 0x3c7 (also resets state machine for access to 18 bit regs + address for writes: 0x3c8 ("") + data: 0x3c9 + pixel mask: 0x3c6 - do not write (init to 0xff) + */ + struct vga_dac_regs vga_dac; +}; + + +typedef enum {PLANAR_SHIFT, PACKED_SHIFT, C256_SHIFT} shift_mode_t; + + +static void find_text_char_dim(struct vga_internal *vga, uint32_t *w, uint32_t *h) +{ + *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; + +} + + +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; + } + +} + +static int blinking(struct vga_internal *vga) +{ + return vga->vga_attribute_controller.vga_attribute_mode_control.enable_blink; +} + + +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;fymap[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= 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 void get_mem_region(struct vga_internal *vga, uint64_t *mem_start, uint64_t *mem_end) +{ + switch (vga->vga_graphics_controller.vga_misc.memory_map) { + case 0: + *mem_start=0xa0000; + *mem_end=0xc0000; + break; + case 1: + *mem_start=0xa0000; + *mem_end=0xb0000; + break; + case 2: + *mem_start=0xb0000; + *mem_end=0xb8000; + break; + case 3: + *mem_start=0xb8000; + *mem_end=0xc0000; + break; + } +} + +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)); + + 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, + addr_t guest_addr, + void * src, + uint_t length, + void * priv_data) +{ + struct vm_device *dev = (struct vm_device *)priv_data; + struct vga_internal *vga = (struct vga_internal *) dev->private_data; + + PrintDebug("vga: memory write: guest_addr=0x%p len=%u\n",(void*)guest_addr, length); + + if (vga->passthrough) { + PrintDebug("vga: passthrough write to 0x%p\n", V3_VAddr((void*)guest_addr)); + memcpy(V3_VAddr((void*)guest_addr),src,length); + } + +#if DEBUG_MEM_DATA + int i; + PrintDebug("vga: data written was 0x"); + for (i=0;i='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: { + + /* + 00b -- Write Mode 0: In this mode, the host data is first rotated + as per the Rotate Count field, then the Enable Set/Reset mechanism + selects data from this or the Set/Reset field. Then the selected + Logical Operation is performed on the resulting data and the data + in the latch register. Then the Bit Mask field is used to select + which bits come from the resulting data and which come + from the latch register. Finally, only the bit planes enabled by + the Memory Plane Write Enable field are written to memory. + */ + + int i; + + uint8_t mapnum; + uint64_t offset; + + 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_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;ivga_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 = 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, 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 + 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 = (sr&0x1) * -1; + + } + +#if DEBUG_DEEP_MEM + PrintDebug("vga: data after esrr=0x%x\n",data); +#endif + + // Apply function + switch (func) { + case 0: // NOP + break; + case 1: // AND + data &= latchval; + break; + case 2: // OR + data |= latchval; + break; + case 3: // XOR + data ^= latchval; + break; + } + +#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 +#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 + } + } + } + } + break; + + + + case 1: { + /* + 01b -- Write Mode 1: In this mode, data is transferred directly + from the 32 bit latch register to display memory, affected only by + the Memory Plane Write Enable field. The host data is not used in this mode. + */ + + int i; + + 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>=1) { + vga_map map = vga->map[mapnum]; + uint8_t latchval = vga->latch[mapnum]; + + // selective write + if (mm & 0x1) { + // write to this map + map[offset] = latchval; + } else { + // skip this map + } + } + } + } + break; + + case 2: { + /* + 10b -- Write Mode 2: In this mode, the bits 3-0 of the host data + are replicated across all 8 bits of their respective planes. + Then the selected Logical Operation is performed on the resulting + data and the data in the latch register. Then the Bit Mask field is used to + select which bits come from the resulting data and which come from + the latch register. Finally, only the bit planes enabled by the + Memory Plane Write Enable field are written to memory. + */ + int i; + uint8_t mapnum; + uint64_t offset; + + uint8_t func = vga->vga_graphics_controller.vga_data_rotate.function; + + 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;ivga_graphics_controller.vga_bit_mask; + 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 data = ((uint8_t *)src)[i]; + uint8_t latchval = vga->latch[mapnum]; + + // expand relevant bit to 8 bit + // it's basically esr=1, sr=bit from mode 0 write + data = ((data>>mapnum)&0x1) * -1; + + // Apply function + switch (func) { + case 0: // NOP + break; + case 1: // AND + data &= latchval; + break; + case 2: // OR + data |= latchval; + break; + case 3: // XOR + data ^= latchval; + break; + } + + // mux between latch and alu output + data = (bm & data) | ((~bm) & latchval); + + // selective write + if (mm & 0x1) { + // write to this map + map[offset] = data; + } else { + // skip this map + } + } + } + } + break; + + case 3: { + /* 11b -- Write Mode 3: In this mode, the data in the Set/Reset field is used + as if the Enable Set/Reset field were set to 1111b. Then the host data is + first rotated as per the Rotate Count field, then logical ANDed with the + value of the Bit Mask field. The resulting value is used on the data + obtained from the Set/Reset field in the same way that the Bit Mask field + would ordinarily be used. to select which bits come from the expansion + of the Set/Reset field and which come from the latch register. Finally, + only the bit planes enabled by the Memory Plane Write Enable field + are written to memory. + */ + int i; + + uint8_t mapnum; + uint64_t offset; + + uint8_t ror = vga->vga_graphics_controller.vga_data_rotate.rotate_count; + + offset = find_offset_write(vga, guest_addr); + + PrintDebug("vga: mode 3 write, offset=0x%llx, ror=%u\n", offset,ror); + + for (i=0;i>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 = 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]; + + // expand SR bit + data = (sr&0x1) * -1; + + // mux between latch and alu output + data = (bm & data) | ((~bm) & latchval); + + // selective write + if (mm & 0x1) { + // write to this map + map[offset] = data; + } else { + // skip this map + } + } + } + + } + break; + + // There is no default + } + + render(vga); + + return length; +} + + + +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)); + + 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, + addr_t guest_addr, + void * dst, + uint_t length, + void * priv_data) +{ + 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: + */ + switch (vga->vga_graphics_controller.vga_graphics_mode.read_mode) { + case 0: { + /* 0 - a byte from ONE of the 4 planes is returned; + which plane is determined by Read Map Select (Read Map Select Register (Index 04h)) */ + uint8_t mapnum; + uint64_t offset; + + + 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;imap[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; + case 1: { + /* 1 - Compare video memory and reference color + (in Color Compare, except those not set in Color Don't Care), + each bit in returned result is one comparison between the reference color + + Ref color is *4* bits, and thus a one byte read returns a comparison + with 8 pixels + + */ + int i; + + uint8_t cc=vga->vga_graphics_controller.vga_color_compare.val & 0xf ; + uint8_t dc=vga->vga_graphics_controller.vga_color_dont_care.val & 0xf; + + uint8_t mapnum; + uint64_t offset; + uint8_t byte; + uint8_t bits; + + 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;imap[mapnum]; + if ( (dc>>mapnum)&0x1 ) { // don't care + bits=0; + } else { + // lower 4 bits + bits = (map[offset]&0xf) == cc; + bits <<= 1; + // upper 4 bits + bits |= (((map[offset]>>4))&0xf) == cc; + } + // not clear whether it is 0..k or k..0 + byte<<=2; + byte|=bits; + } + } + + // load the latches with the last item read + for (mapnum=0;mapnum<4;mapnum++) { + vga->latch[mapnum] = vga->map[mapnum][offset+length-1]; + } + + } + break; + // there is no default + } + + if (vga->passthrough) { + PrintDebug("vga: passthrough read from 0x%p\n",V3_VAddr((void*)guest_addr)); + memcpy(dst,V3_VAddr((void*)guest_addr),length); + } + + +#if DEBUG_MEM_DATA + int i; + PrintDebug("vga: data read is 0x"); + for (i=0;i='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || (c==' ') ? c : '.'); + } + PrintDebug("\"\n"); +#endif + + return length; + +} + + + + + +#define ERR_WRONG_SIZE(op,reg,len,min,max) \ + if (((len)<(min)) || ((len)>(max))) { \ + PrintError("vga: %s of %s wrong size (%d bytes, but only %d to %d allowed)\n",(op),(reg),(len),(min),(max)); \ + return -1; \ +} + +static inline void passthrough_io_in(uint16_t port, void * dest, uint_t length) { + switch (length) { + case 1: + *(uint8_t *)dest = v3_inb(port); + break; + case 2: + *(uint16_t *)dest = v3_inw(port); + break; + case 4: + *(uint32_t *)dest = v3_indw(port); + break; + default: + PrintError("vga: unsupported passthrough io in size %u\n",length); + break; + } +} + + +static inline void passthrough_io_out(uint16_t port, const void * src, uint_t length) { + switch (length) { + case 1: + v3_outb(port, *(uint8_t *)src); + break; + case 2: + v3_outw(port, *(uint16_t *)src); + break; + case 4: + v3_outdw(port, *(uint32_t *)src); + break; + default: + PrintError("vga: unsupported passthrough io out size %u\n",length); + break; + } +} + +#define PASSTHROUGH_IO_IN(vga,port,dest,len) \ + do { if ((vga)->passthrough) { passthrough_io_in(port,dest,len); } } while (0) + +#define PASSTHROUGH_IO_OUT(vga,port,src,len) \ + do { if ((vga)->passthrough && (!(vga)->skip_next_passthrough_out)) { passthrough_io_out(port,src,len); } (vga)->skip_next_passthrough_out=false; } while (0) + +#define PASSTHROUGH_IO_SKIP_NEXT_OUT(vga) \ + do { if ((vga)->passthrough) { (vga)->skip_next_passthrough_out=true; } } while (0) + +#define PASSTHROUGH_READ_CHECK(vga,inter,pass) \ + do { if ((vga)->passthrough) { if ((inter)!=(pass)) { PrintError("vga: passthrough error: passthrough value read is 0x%x, but internal value read is 0x%x\n",(pass),(inter)); } } } while (0) + +static int misc_out_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: misc out read data=0x%x\n", vga->vga_misc.vga_misc_out.val); + + ERR_WRONG_SIZE("read","misc out",len,1,1); + + *((uint8_t*)dest) = vga->vga_misc.vga_misc_out.val; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_misc.vga_misc_out.val,*((uint8_t*)dest)); + + return len; +} + +static int misc_out_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: misc out write data=0x%x\n", *((uint8_t*)src)); + + ERR_WRONG_SIZE("write","misc out",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + vga->vga_misc.vga_misc_out.val = *((uint8_t*)src) ; + + render(vga); + + return len; +} + + + +static int input_stat0_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: input stat0 read data=0x%x\n", vga->vga_misc.vga_input_stat0.val); + + ERR_WRONG_SIZE("read","input stat0",len,1,1); + + *((uint8_t*)dest) = vga->vga_misc.vga_input_stat0.val; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_misc.vga_input_stat0.val,*(uint8_t*)dest); + + return len; +} + + +static int input_stat1_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: input stat0 (%s) read data=0x%x\n", + port==0x3ba ? "mono" : "color", + vga->vga_misc.vga_input_stat1.val); + + ERR_WRONG_SIZE("read","input stat1",len,1,1); + + + *((uint8_t*)dest) = vga->vga_misc.vga_input_stat1.val; + + // Stunningly, reading stat1 is also a way to reset + // the state of attribute controller address/data flipflop + // That is some mighty fine crack the designers were smoking. + + vga->vga_attribute_controller.state=ATTR_ADDR; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_misc.vga_input_stat1.val,*(uint8_t*)dest); + + return len; +} + + +static int feature_control_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: feature control read data=0x%x\n", + vga->vga_misc.vga_feature_control.val); + + ERR_WRONG_SIZE("read","feature control",len,1,1); + + + *((uint8_t*)dest) = vga->vga_misc.vga_feature_control.val; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_misc.vga_feature_control.val,*(uint8_t*)dest); + + return len; +} + +static int feature_control_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: feature control (%s) write data=0x%x\n", + port==0x3ba ? "mono" : "color", + *((uint8_t*)src)); + + ERR_WRONG_SIZE("write","feature control",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + vga->vga_misc.vga_feature_control.val = *((uint8_t*)src) ; + + render(vga); + + return len; +} + + +static int video_subsys_enable_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: video subsys enable read data=0x%x\n", + vga->vga_misc.vga_video_subsys_enable.val); + + ERR_WRONG_SIZE("read","video subsys enable",len,1,1); + + *((uint8_t*)dest) = vga->vga_misc.vga_video_subsys_enable.val; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_misc.vga_video_subsys_enable.val,*(uint8_t*)dest); + + return len; +} + +static int video_subsys_enable_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: video subsys enable write data=0x%x\n", *((uint8_t*)src)); + + ERR_WRONG_SIZE("write","video subsys enable",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + vga->vga_misc.vga_video_subsys_enable.val = *((uint8_t*)src) ; + + render(vga); + + return len; +} + +static int sequencer_address_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: sequencer address read data=0x%x\n", + vga->vga_sequencer.vga_sequencer_addr.val); + + ERR_WRONG_SIZE("read","vga sequencer addr",len,1,1); + + *((uint8_t*)dest) = vga->vga_sequencer.vga_sequencer_addr.val; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_sequencer.vga_sequencer_addr.val,*(uint8_t*)dest); + + return len; +} + +static int sequencer_data_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t index; + uint8_t data; + + data=*((uint8_t*)src); + index=vga->vga_sequencer.vga_sequencer_addr.val; // should mask probably + + PrintDebug("vga: sequencer write data (index=%d) with 0x%x\n", + index, data); + + ERR_WRONG_SIZE("write","vga sequencer data",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + + if (index>=VGA_SEQUENCER_NUM) { + PrintError("vga: sequencer data write is for invalid index %d, ignoring\n",index); + } else { + vga->vga_sequencer.vga_sequencer_regs[index] = data; + } + + render(vga); + + return len; +} + +static int sequencer_address_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t new_addr; + + new_addr=*((uint8_t*)src); + + PrintDebug("vga: sequencer address write data=0x%x len=%u\n", len==1 ? *((uint8_t*)src) : len==2 ? *((uint16_t*)src) : *((uint32_t*)src), len); + + ERR_WRONG_SIZE("write","vga sequencer addr",len,1,2); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + vga->vga_sequencer.vga_sequencer_addr.val = *((uint8_t*)src) ; + + if (len==2) { + PASSTHROUGH_IO_SKIP_NEXT_OUT(vga); + // second byte is the data + if (sequencer_data_write(core,port,src+1,1,vga)!=1) { + PrintError("vga: write of data failed\n"); + return -1; + } + } + + return len; +} + +static int sequencer_data_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t index; + uint8_t data; + + index=vga->vga_sequencer.vga_sequencer_addr.val; // should mask probably + + if (index>=VGA_SEQUENCER_NUM) { + data=0; + PrintError("vga: sequencer data read at invalid index %d, returning zero\n",index); + } else { + data=vga->vga_sequencer.vga_sequencer_regs[index]; + } + + PrintDebug("vga: sequencer data read data (index=%d) = 0x%x\n", + index, data); + + ERR_WRONG_SIZE("read","vga sequencer data",len,1,1); + + *((uint8_t*)dest) = data; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,data,*(uint8_t*)dest); + + return len; +} + + + + + +static int crt_controller_address_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: crt controller (%s) address read data=0x%x\n", + port==0x3b4 ? "mono" : "color", + vga->vga_crt_controller.vga_crt_addr.val); + + ERR_WRONG_SIZE("read","vga crt controller addr",len,1,1); + + *((uint8_t*)dest) = vga->vga_crt_controller.vga_crt_addr.val; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_crt_controller.vga_crt_addr.val,*(uint8_t*)dest); + + return len; +} + +static int crt_controller_data_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t index; + uint8_t data; + + data=*((uint8_t*)src); + + index=vga->vga_crt_controller.vga_crt_addr.val; // should mask probably + + PrintDebug("vga: crt controller (%s) write data (index=%d) with 0x%x\n", + port==0x3b5 ? "mono" : "color", + index, data); + + ERR_WRONG_SIZE("write","vga crt controller data",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + if (index>=VGA_CRT_CONTROLLER_NUM) { + PrintError("vga; crt controller write is for illegal index %d, ignoring\n",index); + } else { + vga->vga_crt_controller.vga_crt_controller_regs[index] = data; + } + + render(vga); + + return len; +} + +static int crt_controller_address_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t new_addr; + + new_addr=*((uint8_t*)src); + + PrintDebug("vga: crt controller (%s) address write data=0x%x len=%u\n", + port==0x3b4 ? "mono" : "color", + len==1 ? *((uint8_t*)src) : len==2 ? *((uint16_t*)src) : *((uint32_t*)src), len); + + ERR_WRONG_SIZE("write","vga crt controller addr",len,1,2); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + vga->vga_crt_controller.vga_crt_addr.val = *((uint8_t*)src) ; + + if (len==2) { + PASSTHROUGH_IO_SKIP_NEXT_OUT(vga); + // second byte is the data + if (crt_controller_data_write(core,port,src+1,1,vga)!=1) { + PrintError("vga: write of data failed\n"); + return -1; + } + } + + return len; +} + +static int crt_controller_data_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t index; + uint8_t data; + + index=vga->vga_crt_controller.vga_crt_addr.val; // should mask probably + + if (index>=VGA_CRT_CONTROLLER_NUM) { + data=0; + PrintError("vga: crt controller data read for illegal index %d, returning zero\n",index); + } else { + data=vga->vga_crt_controller.vga_crt_controller_regs[index]; + } + + PrintDebug("vga: crt controller data (index=%d) = 0x%x\n",index,data); + + ERR_WRONG_SIZE("read","vga crt controller data",len,1,1); + + *((uint8_t*)dest) = data; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,data,*(uint8_t *)dest); + + return len; +} + + + +static int graphics_controller_address_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: graphics controller address read data=0x%x\n", + vga->vga_graphics_controller.vga_graphics_ctrl_addr.val); + + ERR_WRONG_SIZE("read","vga graphics controller addr",len,1,1); + + *((uint8_t*)dest) = vga->vga_graphics_controller.vga_graphics_ctrl_addr.val; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_graphics_controller.vga_graphics_ctrl_addr.val,*(uint8_t*)dest); + + return len; +} + +static int graphics_controller_data_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t index; + uint8_t data; + + data=*((uint8_t*)src); + index=vga->vga_graphics_controller.vga_graphics_ctrl_addr.val; // should mask probably + + + PrintDebug("vga: graphics_controller write data (index=%d) with 0x%x\n", + index, data); + + ERR_WRONG_SIZE("write","vga graphics controller data",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + if (index>=VGA_GRAPHICS_CONTROLLER_NUM) { + PrintError("vga: graphics controller write for illegal index %d ignored\n",index); + } else { + vga->vga_graphics_controller.vga_graphics_controller_regs[index] = data; + } + + render(vga); + + return len; +} + +static int graphics_controller_address_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t new_addr; + + new_addr=*((uint8_t*)src); + + PrintDebug("vga: graphics controller address write data=0x%x len=%u\n", + len==1 ? *((uint8_t*)src) : len==2 ? *((uint16_t*)src) : *((uint32_t*)src), len); + + ERR_WRONG_SIZE("write","vga graphics controller addr",len,1,2); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + vga->vga_graphics_controller.vga_graphics_ctrl_addr.val = *((uint8_t*)src) ; + + if (len==2) { + PASSTHROUGH_IO_SKIP_NEXT_OUT(vga); + // second byte is the data + if (graphics_controller_data_write(core,port,src+1,1,vga)!=1) { + PrintError("vga: write of data failed\n"); + return -1; + } + } + + return len; +} + +static int graphics_controller_data_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t index; + uint8_t data; + + index=vga->vga_graphics_controller.vga_graphics_ctrl_addr.val; // should mask probably + + + if (index>=VGA_GRAPHICS_CONTROLLER_NUM) { + data=0; + PrintError("vga: graphics controller data read from illegal index %d, returning zero\n",index); + } else { + data=vga->vga_graphics_controller.vga_graphics_controller_regs[index]; + } + + PrintDebug("vga: graphics controller data read data (index=%d) = 0x%x\n", + index, data); + + ERR_WRONG_SIZE("read","vga graphics controller data",len,1,1); + + *((uint8_t*)dest) = data; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,data,*(uint8_t*)dest); + + return len; +} + + + + +/* Note that these guys have a bizarre protocol*/ + +static int attribute_controller_address_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: attribute controller address read data=0x%x\n", + vga->vga_attribute_controller.vga_attribute_controller_addr.val); + + ERR_WRONG_SIZE("read","vga attribute controller addr",len,1,1); + + *((uint8_t*)dest) = vga->vga_attribute_controller.vga_attribute_controller_addr.val; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_attribute_controller.vga_attribute_controller_addr.val,*(uint8_t*)dest); + + // Reading the attribute controller does not change the state + + return len; +} + +static int attribute_controller_address_and_data_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + + if (vga->vga_attribute_controller.state==ATTR_ADDR) { + uint8_t new_addr = *((uint8_t*)src); + // We are to treat this as an address write, and flip state + // to expect data ON THIS SAME PORT + PrintDebug("vga: attribute controller address write data=0x%x\n", new_addr); + + ERR_WRONG_SIZE("write","vga attribute controller addr",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + vga->vga_attribute_controller.vga_attribute_controller_addr.val = new_addr; + + vga->vga_attribute_controller.state=ATTR_DATA; + return len; + + } else if (vga->vga_attribute_controller.state==ATTR_DATA) { + + uint8_t data = *((uint8_t*)src); + uint8_t index=vga->vga_attribute_controller.vga_attribute_controller_addr.val; // should mask probably + + PrintDebug("vga: attribute controller data write index %d with data=0x%x\n", index,data); + + ERR_WRONG_SIZE("write","vga attribute controller data",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + if (index>=VGA_ATTRIBUTE_CONTROLLER_NUM) { + PrintError("vga: attribute controller write to illegal index %d ignored\n",index); + } else { + vga->vga_attribute_controller.vga_attribute_controller_regs[index] = data; + } + + vga->vga_attribute_controller.state=ATTR_ADDR; + + return len; + } + + return -1; + +} + +static int attribute_controller_data_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t index; + uint8_t data; + + index=vga->vga_attribute_controller.vga_attribute_controller_addr.val; // should mask probably + + if (index>=VGA_ATTRIBUTE_CONTROLLER_NUM) { + data=0; + PrintError("vga: attribute controller read of illegal index %d, returning zero\n",index); + } else { + data=vga->vga_attribute_controller.vga_attribute_controller_regs[index]; + } + + PrintDebug("vga: attribute controller data read data (index=%d) = 0x%x\n", + index, data); + + ERR_WRONG_SIZE("read","vga attribute controller data",len,1,1); + + *((uint8_t*)dest) = data; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,data,*(uint8_t*)dest); + + return len; +} + + +/* + Note that these guys also have a strange protocol + since they need to squeeze 18 bits of data through + an 8 bit port +*/ +static int dac_write_address_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: dac write address read data=0x%x\n", + vga->vga_dac.vga_dac_write_addr); + + ERR_WRONG_SIZE("read","vga dac write addr",len,1,1); + + + *((uint8_t*)dest) = vga->vga_dac.vga_dac_write_addr; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_dac.vga_dac_write_addr,*(uint8_t*)dest); + + // This read does not reset the state machine + + return len; +} + +static int dac_write_address_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t new_addr; + + new_addr=*((uint8_t*)src); + + PrintDebug("vga: dac write address write data=0x%x\n", new_addr); + + ERR_WRONG_SIZE("write","vga dac write addr",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + // cannot be out of bounds since there are 256 regs + + vga->vga_dac.vga_dac_write_addr = *((uint8_t*)src) ; + + // Now we also need to reset the state machine + + vga->vga_dac.state=DAC_WRITE; + vga->vga_dac.channel=RED; + + return len; +} + + +static int dac_read_address_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: dac read address read data=0x%x\n", + vga->vga_dac.vga_dac_read_addr); + + ERR_WRONG_SIZE("read","vga dac read addr",len,1,1); + + *((uint8_t*)dest) = vga->vga_dac.vga_dac_read_addr; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_dac.vga_dac_read_addr,*(uint8_t*)dest); + + // This read does not reset the state machine + + return len; +} + +static int dac_read_address_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t new_addr; + + new_addr=*((uint8_t*)src); + + PrintDebug("vga: dac read address write data=0x%x\n", new_addr); + + ERR_WRONG_SIZE("write","vga dac read addr",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + // cannot be out of bounds since there are 256 regs + + vga->vga_dac.vga_dac_read_addr = *((uint8_t*)src) ; + + // Now we also need to reset the state machine + + vga->vga_dac.state=DAC_READ; + vga->vga_dac.channel=RED; + + return len; +} + + +static int dac_data_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t curreg; + uint8_t curchannel; + uint8_t data; + + if (vga->vga_dac.state!=DAC_READ) { + PrintError("vga: dac data read while in other state\n"); + // results undefined, so we continue + } + + ERR_WRONG_SIZE("read","vga dac read data",len,1,1); + + curreg = vga->vga_dac.vga_dac_read_addr; + curchannel = vga->vga_dac.channel; + data = (vga->vga_dac.vga_dac_palette[curreg] >> curchannel*8) & 0x3f; + + PrintDebug("vga: dac reg %u [%s] = 0x%x\n", + curreg, + curchannel == 0 ? "RED" : curchannel == 1 ? "GREEN" + : curchannel==2 ? "BLUE" : "BAD CHANNEL", + data); + + *((uint8_t*)dest) = data; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,data,*(uint8_t*)dest); + + curchannel = (curchannel+1)%3; + vga->vga_dac.channel=curchannel; + if (curchannel==0) { + curreg = (curreg + 1) % VGA_DAC_NUM_ENTRIES; + } + vga->vga_dac.vga_dac_read_addr = curreg; + vga->vga_dac.state=DAC_READ; + + return len; +} + + + +static int dac_data_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t curreg; + uint8_t curchannel; + uint8_t data; + vga_palette_reg data32; + vga_palette_reg mask32; + + if (vga->vga_dac.state!=DAC_WRITE) { + PrintError("vga: dac data write while in other state\n"); + // results undefined, so we continue + } + + ERR_WRONG_SIZE("read","vga dac write data",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + curreg = vga->vga_dac.vga_dac_write_addr; + curchannel = vga->vga_dac.channel; + data = *((uint8_t *)src); + + PrintDebug("vga: dac reg %u [%s] write with 0x%x\n", + curreg, + curchannel == 0 ? "RED" : curchannel == 1 ? "GREEN" + : curchannel==2 ? "BLUE" : "BAD CHANNEL", + data); + + data32 = data & 0x3f ; + data32 <<= curchannel*8; + mask32 = ~(0xff << (curchannel * 8)); + + vga->vga_dac.vga_dac_palette[curreg] &= mask32; + vga->vga_dac.vga_dac_palette[curreg] |= data32; + + curchannel = (curchannel+1)%3; + vga->vga_dac.channel=curchannel; + if (curchannel==0) { + curreg = (curreg + 1) % VGA_DAC_NUM_ENTRIES; + } + vga->vga_dac.vga_dac_write_addr = curreg; + vga->vga_dac.state=DAC_WRITE; + + render(vga); + + return len; +} + + + +static int dac_pixel_mask_read(struct guest_info *core, + uint16_t port, + void *dest, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + + PrintDebug("vga: dac pixel mask read data=0x%x\n", + vga->vga_dac.vga_pixel_mask); + + ERR_WRONG_SIZE("read","vga pixel mask",len,1,1); + + *((uint8_t*)dest) = vga->vga_dac.vga_pixel_mask; + + PASSTHROUGH_IO_IN(vga,port,dest,len); + + PASSTHROUGH_READ_CHECK(vga,vga->vga_dac.vga_pixel_mask,*(uint8_t*)dest); + + return len; +} + +static int dac_pixel_mask_write(struct guest_info *core, + uint16_t port, + void *src, + uint_t len, + void *priv_data) +{ + struct vga_internal *vga = (struct vga_internal *) priv_data; + uint8_t new_data; + + new_data=*((uint8_t*)src); + + PrintDebug("vga: dac pixel mask write data=0x%x\n", new_data); + + ERR_WRONG_SIZE("write","pixel mask",len,1,1); + + PASSTHROUGH_IO_OUT(vga,port,src,len); + + vga->vga_dac.vga_pixel_mask = new_data; + + return len; +} + +static int init_vga(struct vga_internal *vga) +{ + // TODO: startup spec of register contents, if any + PrintError("vga: init_vga is UNIMPLEMTED\n"); + return 0; +} + +static int free_vga(struct vga_internal *vga) +{ + int i; + int ret; + struct vm_device *dev = vga->dev; + + // Framebuffer deletion is user's responsibility + + // if (vga->mem_store) { + // V3_FreePages(v3_hva_to_hpa(vga->mem_store),MEM_REGION_NUM_PAGES); + // vga->mem_store=0; + //} + + for (i=0;imap[i]) { + V3_FreePages(V3_PAddr(vga->map[i]),MAP_SIZE/4096); + vga->map[i]=0; + } + } + + v3_unhook_mem(vga->dev->vm, V3_MEM_CORE_ANY, MEM_REGION_START); + + ret = 0; + + ret |= v3_dev_unhook_io(dev, VGA_MISC_OUT_READ); + // The following also covers VGA_INPUT_STAT0_READ + ret |= v3_dev_unhook_io(dev, VGA_MISC_OUT_WRITE); + // The following also covers VGA_FEATURE_CTRL_WRITE_MONO + ret |= v3_dev_unhook_io(dev, VGA_INPUT_STAT1_READ_MONO); + // The folowinn also covers VGA FEATURE_CONTROL_WRITE_COLOR + ret |= v3_dev_unhook_io(dev, VGA_INPUT_STAT1_READ_COLOR); + ret |= v3_dev_unhook_io(dev, VGA_FEATURE_CONTROL_READ); + + ret |= v3_dev_unhook_io(dev, VGA_VIDEO_SUBSYS_ENABLE); + + /* Sequencer registers */ + ret |= v3_dev_unhook_io(dev, VGA_SEQUENCER_ADDRESS); + ret |= v3_dev_unhook_io(dev, VGA_SEQUENCER_DATA); + + /* CRT controller registers */ + ret |= v3_dev_unhook_io(dev, VGA_CRT_CONTROLLER_ADDRESS_MONO); + ret |= v3_dev_unhook_io(dev, VGA_CRT_CONTROLLER_ADDRESS_COLOR); + ret |= v3_dev_unhook_io(dev, VGA_CRT_CONTROLLER_DATA_MONO); + ret |= v3_dev_unhook_io(dev, VGA_CRT_CONTROLLER_DATA_COLOR); + + /* graphics controller registers */ + ret |= v3_dev_unhook_io(dev, VGA_GRAPHICS_CONTROLLER_ADDRESS); + ret |= v3_dev_unhook_io(dev, VGA_GRAPHICS_CONTROLLER_DATA); + + /* attribute controller registers */ + ret |= v3_dev_unhook_io(dev, VGA_ATTRIBUTE_CONTROLLER_ADDRESS_AND_WRITE); + ret |= v3_dev_unhook_io(dev, VGA_ATTRIBUTE_CONTROLLER_READ); + + /* video DAC palette registers */ + ret |= v3_dev_unhook_io(dev, VGA_DAC_WRITE_ADDR); + ret |= v3_dev_unhook_io(dev, VGA_DAC_READ_ADDR); + ret |= v3_dev_unhook_io(dev, VGA_DAC_DATA); + 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; +} + +static struct v3_device_ops dev_ops = { + .free = (int (*)(void *))free_vga, +}; + + +static int vga_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { + struct vga_internal *vga; + int i; + int ret; + + char * dev_id = v3_cfg_val(cfg, "ID"); + char * passthrough = v3_cfg_val(cfg, "passthrough"); + char * hostframebuf = v3_cfg_val(cfg, "hostframebuf"); + + PrintDebug("vga: init_device\n"); + + vga = (struct vga_internal *)V3_Malloc(sizeof(struct vga_internal)); + + if (!vga) { + PrintError("vga: cannot allocate\n"); + return -1; + } + + memset(vga, 0, sizeof(struct vga_internal)); + + if (passthrough && strcasecmp(passthrough,"enable")==0) { + PrintDebug("vga: enabling passthrough\n"); + vga->passthrough=true; + 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 + + // Now allocate the maps + for (i=0;imap[i] = (vga_map) V3_VAddr((void*)V3_AllocPages(MAP_SIZE/4096)); + if (!(vga->map[i])) { + PrintError("vga: cannot allocate maps\n"); + free_vga(vga); + return -1; + } + memset(vga->map[i],0,MAP_SIZE); + } + + struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, vga); + + if (dev == NULL) { + PrintError("Could not attach device %s\n", dev_id); + free_vga(vga); + return -1; + } + + vga->dev = dev; + + if (v3_hook_full_mem(vm, V3_MEM_CORE_ANY, + MEM_REGION_START, MEM_REGION_END, + &vga_read, + &vga_write, + dev) == -1) { + PrintError("vga: memory book failed\n"); + v3_remove_device(dev); + return -1; + } + + ret = 0; + + /* Miscelaneous registers */ + ret |= v3_dev_hook_io(dev, VGA_MISC_OUT_READ, &misc_out_read, NULL); + // The following also covers VGA_INPUT_STAT0_READ + ret |= v3_dev_hook_io(dev, VGA_MISC_OUT_WRITE, &input_stat0_read, &misc_out_write); + // The following also covers VGA_FEATURE_CTRL_WRITE_MONO + ret |= v3_dev_hook_io(dev, VGA_INPUT_STAT1_READ_MONO, &input_stat1_read, &feature_control_write); + // The folowinn also covers VGA FEATURE_CONTROL_WRITE_COLOR + ret |= v3_dev_hook_io(dev, VGA_INPUT_STAT1_READ_COLOR, &input_stat1_read, &feature_control_write); + ret |= v3_dev_hook_io(dev, VGA_FEATURE_CONTROL_READ, &feature_control_read, NULL); + + ret |= v3_dev_hook_io(dev, VGA_VIDEO_SUBSYS_ENABLE, &video_subsys_enable_read, &video_subsys_enable_write); + + /* Sequencer registers */ + ret |= v3_dev_hook_io(dev, VGA_SEQUENCER_ADDRESS, &sequencer_address_read, &sequencer_address_write); + ret |= v3_dev_hook_io(dev, VGA_SEQUENCER_DATA, &sequencer_data_read, &sequencer_data_write); + + /* CRT controller registers */ + ret |= v3_dev_hook_io(dev, VGA_CRT_CONTROLLER_ADDRESS_MONO, &crt_controller_address_read,&crt_controller_address_write); + ret |= v3_dev_hook_io(dev, VGA_CRT_CONTROLLER_ADDRESS_COLOR, &crt_controller_address_read,&crt_controller_address_write); + ret |= v3_dev_hook_io(dev, VGA_CRT_CONTROLLER_DATA_MONO, &crt_controller_data_read,&crt_controller_data_write); + ret |= v3_dev_hook_io(dev, VGA_CRT_CONTROLLER_DATA_COLOR, &crt_controller_data_read,&crt_controller_data_write); + + /* graphics controller registers */ + ret |= v3_dev_hook_io(dev, VGA_GRAPHICS_CONTROLLER_ADDRESS, &graphics_controller_address_read,&graphics_controller_address_write); + ret |= v3_dev_hook_io(dev, VGA_GRAPHICS_CONTROLLER_DATA, &graphics_controller_data_read,&graphics_controller_data_write); + + /* attribute controller registers */ + ret |= v3_dev_hook_io(dev, VGA_ATTRIBUTE_CONTROLLER_ADDRESS_AND_WRITE, &attribute_controller_address_read,&attribute_controller_address_and_data_write); + ret |= v3_dev_hook_io(dev, VGA_ATTRIBUTE_CONTROLLER_READ, &attribute_controller_data_read,NULL); + + /* video DAC palette registers */ + ret |= v3_dev_hook_io(dev, VGA_DAC_WRITE_ADDR, &dac_write_address_read,&dac_write_address_write); + ret |= v3_dev_hook_io(dev, VGA_DAC_READ_ADDR, &dac_read_address_read,&dac_read_address_write); + ret |= v3_dev_hook_io(dev, VGA_DAC_DATA, &dac_data_read, &dac_data_write); + ret |= v3_dev_hook_io(dev, VGA_DAC_PIXEL_MASK, &dac_pixel_mask_read, &dac_pixel_mask_write); + + if (ret != 0) { + PrintError("vga: Error allocating VGA I/O ports\n"); + v3_remove_device(dev); + return -1; + } + + init_vga(vga); + + PrintDebug("vga: successfully added and initialized.\n"); + + return 0; + +} + +device_register("VGA", vga_init);