summaryrefslogtreecommitdiff
path: root/src/vga.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vga.c')
-rw-r--r--src/vga.c429
1 files changed, 429 insertions, 0 deletions
diff --git a/src/vga.c b/src/vga.c
new file mode 100644
index 0000000..d671bea
--- /dev/null
+++ b/src/vga.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2004-2005 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include "bios.h"
+
+/* VGA init. We use the Bochs VESA VBE extensions */
+#define VBE_DISPI_INDEX_ID 0x0
+#define VBE_DISPI_INDEX_XRES 0x1
+#define VBE_DISPI_INDEX_YRES 0x2
+#define VBE_DISPI_INDEX_BPP 0x3
+#define VBE_DISPI_INDEX_ENABLE 0x4
+#define VBE_DISPI_INDEX_BANK 0x5
+#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+#define VBE_DISPI_INDEX_X_OFFSET 0x8
+#define VBE_DISPI_INDEX_Y_OFFSET 0x9
+#define VBE_DISPI_INDEX_NB 0xa
+
+#define VBE_DISPI_ID0 0xB0C0
+#define VBE_DISPI_ID1 0xB0C1
+#define VBE_DISPI_ID2 0xB0C2
+
+#define VBE_DISPI_DISABLED 0x00
+#define VBE_DISPI_ENABLED 0x01
+#define VBE_DISPI_LFB_ENABLED 0x40
+#define VBE_DISPI_NOCLEARMEM 0x80
+
+#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
+
+static void vga_text_init(void);
+
+unsigned long vga_fb_phys_addr;
+int vga_fb_width;
+int vga_fb_height;
+int vga_fb_linesize;
+int vga_fb_bpp;
+int vga_fb_depth;
+uint8_t rgb_to_index[256];
+
+static void vbe_outw(int index, int val)
+{
+ outw(0x1ce, index);
+ outw(0x1d0, val);
+}
+
+/* init VGA in standard state for PREP boot */
+void vga_prep_init(void)
+{
+ outb(0x3c0, 0x00); /* set blanking */
+ vbe_outw(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
+}
+
+/* build standard RGB palette */
+void vga_build_rgb_palette(void)
+{
+ static const uint8_t pal_value[6] = { 0x00, 0x33, 0x66, 0x99, 0xcc, 0xff };
+ int i, r, g, b;
+
+ i = 0;
+ for(r = 0; r < 6; r++) {
+ for(g = 0; g < 6; g++) {
+ for(b = 0; b < 6; b++) {
+ vga_set_palette(i, RGB(pal_value[r], pal_value[g],
+ pal_value[b]));
+ i++;
+ }
+ }
+ }
+ for(i = 0; i < 256; i++) {
+ rgb_to_index[i] = ((i * 5) + 128) / 255;
+ }
+}
+
+void vga_set_address (uint32_t address)
+{
+ vga_fb_phys_addr = address;
+}
+
+/* depth = 8, 15, 16 or 32 */
+void vga_set_mode(int width, int height, int depth)
+{
+ vbe_outw(VBE_DISPI_INDEX_XRES, width);
+ vbe_outw(VBE_DISPI_INDEX_YRES, height);
+ vbe_outw(VBE_DISPI_INDEX_BPP, depth);
+ vbe_outw(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED);
+ outb(0x3c0, 0x20); /* disable blanking */
+
+ if (vga_fb_phys_addr == 0x00000000)
+ vga_fb_phys_addr = VBE_DISPI_LFB_PHYSICAL_ADDRESS;
+ vga_fb_width = width;
+ vga_fb_height = height;
+ vga_fb_depth = depth;
+ vga_fb_bpp = (depth + 7) >> 3;
+ vga_fb_linesize = width * vga_fb_bpp;
+
+ if (depth == 8)
+ vga_build_rgb_palette();
+ vga_text_init();
+}
+
+/* for depth = 8 mode, set a hardware palette entry */
+void vga_set_palette(int i, unsigned int rgba)
+{
+ unsigned int r, g, b;
+
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ outb(0x3c8, i);
+ outb(0x3c9, r >> 2);
+ outb(0x3c9, g >> 2);
+ outb(0x3c9, b >> 2);
+}
+
+/* convert a RGBA color to a color index usable in graphic primitives */
+unsigned int vga_get_color(unsigned int rgba)
+{
+ unsigned int r, g, b, color;
+
+ switch(vga_fb_depth) {
+ case 8:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = (rgb_to_index[r] * 6 * 6) +
+ (rgb_to_index[g] * 6) +
+ (rgb_to_index[b]);
+ break;
+ case 15:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+ break;
+ case 16:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+ break;
+ case 32:
+ default:
+ color = rgba;
+ break;
+ }
+ return color;
+}
+
+void vga_draw_buf (const void *buf, int buf_linesize,
+ int posx, int posy, int width, int height)
+{
+ const uint8_t *s;
+ uint8_t *d;
+ int y, wb;
+
+ s = buf;
+ d = (uint8_t *)vga_fb_phys_addr +
+ vga_fb_linesize * posy + vga_fb_bpp * posx;
+ wb = width * vga_fb_bpp;
+ for (y = 0; y < height; y++) {
+ memcpy(d, s, wb);
+ s += buf_linesize;
+ d += vga_fb_linesize;
+ }
+}
+
+void vga_fill_rect (int posx, int posy, int width, int height, uint32_t color)
+{
+ uint8_t *d, *d1;
+ int x, y;
+
+ d1 = (uint8_t *)vga_fb_phys_addr +
+ vga_fb_linesize * posy + vga_fb_bpp * posx;
+ for (y = 0; y < height; y++) {
+ d = d1;
+ switch(vga_fb_bpp) {
+ case 1:
+ for (x = 0; x < width; x++) {
+ *((uint8_t *)d) = color;
+ d++;
+ }
+ break;
+ case 2:
+ for (x = 0; x < width; x++) {
+ *((uint16_t *)d) = color;
+ d += 2;
+ }
+ break;
+ case 4:
+ for (x = 0; x < width; x++) {
+ *((uint32_t *)d) = color;
+ d += 4;
+ }
+ break;
+ }
+ d1 += vga_fb_linesize;
+ }
+}
+
+/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
+void vga_bitblt(int xs, int ys, int xd, int yd, int w, int h)
+{
+ const uint8_t *s;
+ uint8_t *d;
+ int wb, y;
+
+ wb = w * vga_fb_bpp;
+ if (yd <= ys) {
+ s = (uint8_t *)vga_fb_phys_addr +
+ vga_fb_linesize * ys + vga_fb_bpp * xs;
+ d = (uint8_t *)vga_fb_phys_addr +
+ vga_fb_linesize * yd + vga_fb_bpp * xd;
+ for (y = 0; y < h; y++) {
+ memmove(d, s, wb);
+ d += vga_fb_linesize;
+ s += vga_fb_linesize;
+ }
+ } else {
+ s = (uint8_t *)vga_fb_phys_addr +
+ vga_fb_linesize * (ys + h - 1) + vga_fb_bpp * xs;
+ d = (uint8_t *)vga_fb_phys_addr +
+ vga_fb_linesize * (yd + h - 1) + vga_fb_bpp * xd;
+ for (y = 0; y < h; y++) {
+ memmove(d, s, wb);
+ d -= vga_fb_linesize;
+ s -= vga_fb_linesize;
+ }
+ }
+}
+
+/***********************************************************/
+/* basic char display */
+
+#define FONT_HEIGHT 16
+#define FONT_WIDTH 8
+
+#include "vgafont.h"
+
+#define cbswap_32(__x) \
+((uint32_t)( \
+ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
+
+/* XXX: endianness */
+#if 0
+#define PAT(x) cbswap_32(x)
+#else
+#define PAT(x) x
+#endif
+
+static const uint32_t dmask16[16] = {
+ PAT(0x00000000),
+ PAT(0x000000ff),
+ PAT(0x0000ff00),
+ PAT(0x0000ffff),
+ PAT(0x00ff0000),
+ PAT(0x00ff00ff),
+ PAT(0x00ffff00),
+ PAT(0x00ffffff),
+ PAT(0xff000000),
+ PAT(0xff0000ff),
+ PAT(0xff00ff00),
+ PAT(0xff00ffff),
+ PAT(0xffff0000),
+ PAT(0xffff00ff),
+ PAT(0xffffff00),
+ PAT(0xffffffff),
+};
+
+static const uint32_t dmask4[4] = {
+ PAT(0x00000000),
+ PAT(0x0000ffff),
+ PAT(0xffff0000),
+ PAT(0xffffffff),
+};
+
+int text_width, text_height, text_fgcol, text_bgcol, text_x, text_y;
+
+static void vga_text_init(void)
+{
+ text_width = vga_fb_width / FONT_WIDTH;
+ text_height = vga_fb_height / FONT_HEIGHT;
+ text_x = 0;
+ text_y = 0;
+ vga_text_set_fgcol(RGB(0xff, 0xff, 0xff));
+ vga_text_set_bgcol(RGB(0x00, 0x00, 0x00));
+}
+
+static inline unsigned int col_expand(unsigned int col)
+{
+ switch(vga_fb_bpp) {
+ case 1:
+ col |= col << 8;
+ col |= col << 16;
+ break;
+ case 2:
+ col |= col << 16;
+ break;
+ default:
+ text_fgcol = 0xffffff;
+ break;
+ }
+
+ return col;
+}
+
+void vga_text_set_fgcol(unsigned int rgba)
+{
+ text_fgcol = col_expand(vga_get_color(rgba));
+}
+
+void vga_text_set_bgcol(unsigned int rgba)
+{
+ text_bgcol = col_expand(vga_get_color(rgba));
+}
+
+void vga_putcharxy(int x, int y, int ch,
+ unsigned int fgcol, unsigned int bgcol)
+{
+ uint8_t *d;
+ const uint8_t *font_ptr;
+ unsigned int font_data, linesize, xorcol;
+ int i;
+
+ d = (uint8_t *)vga_fb_phys_addr +
+ vga_fb_linesize * y * FONT_HEIGHT + vga_fb_bpp * x * FONT_WIDTH;
+ linesize = vga_fb_linesize;
+ font_ptr = vgafont16 + FONT_HEIGHT * ch;
+ xorcol = bgcol ^ fgcol;
+ switch(vga_fb_depth) {
+ case 8:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ case 16:
+ case 15:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ case 32:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ }
+}
+
+static void vga_put_lf(void)
+{
+ text_x = 0;
+ text_y++;
+ if (text_y >= text_height) {
+ text_y = text_height - 1;
+ vga_bitblt(0, FONT_HEIGHT, 0, 0,
+ text_width * FONT_WIDTH,
+ (text_height - 1) * FONT_HEIGHT);
+ vga_fill_rect(0, (text_height - 1) * FONT_HEIGHT,
+ text_width * FONT_WIDTH, FONT_HEIGHT, text_bgcol);
+ }
+}
+
+void vga_putchar(int ch)
+{
+ if (ch == '\r') {
+ text_x = 0;
+ } else if (ch == '\n') {
+ vga_put_lf();
+ } else if (ch == '\b') {
+ if (text_x == 0) {
+ if (text_y != 0) {
+ text_x = text_width;
+ text_y--;
+ goto eat_char;
+ }
+ } else {
+ eat_char:
+ vga_putcharxy(--text_x, text_y, ' ', text_fgcol, text_bgcol);
+ }
+ } else {
+ vga_putcharxy(text_x, text_y, ch, text_fgcol, text_bgcol);
+ text_x++;
+ if (text_x >= text_width)
+ vga_put_lf();
+ }
+}
+
+void vga_puts(const char *s)
+{
+ while (*s) {
+ vga_putchar(*(uint8_t *)s);
+ s++;
+ }
+}