blob: 3c6065ca180771b61d0d7362c67ffe5a7c74fd00 [file] [log] [blame]
// Code for manipulating VGA framebuffers.
//
// Copyright (C) 2009-2014 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2001-2008 the LGPL VGABios developers Team
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "biosvar.h" // GET_BDA
#include "byteorder.h" // cpu_to_be16
#include "output.h" // dprintf
#include "stdvga.h" // stdvga_planar4_plane
#include "string.h" // memset_far
#include "vgabios.h" // vgafb_scroll
#include "vgahw.h" // vgahw_get_linelength
static inline void
memmove_stride(u16 seg, void *dst, void *src, int copylen, int stride, int lines)
{
if (src < dst) {
dst += stride * (lines - 1);
src += stride * (lines - 1);
stride = -stride;
}
for (; lines; lines--, dst+=stride, src+=stride)
memcpy_far(seg, dst, seg, src, copylen);
}
static inline void
memset_stride(u16 seg, void *dst, u8 val, int setlen, int stride, int lines)
{
for (; lines; lines--, dst+=stride)
memset_far(seg, dst, val, setlen);
}
static inline void
memset16_stride(u16 seg, void *dst, u16 val, int setlen, int stride, int lines)
{
for (; lines; lines--, dst+=stride)
memset16_far(seg, dst, val, setlen);
}
/****************************************************************
* Basic stdvga graphic manipulation
****************************************************************/
static void
gfx_planar(struct gfx_op *op)
{
if (!CONFIG_VGA_STDVGA_PORTS)
return;
void *dest_far = (void*)(op->y * op->linelength + op->x / 8);
int plane;
switch (op->op) {
default:
case GO_READ8:
memset(op->pixels, 0, sizeof(op->pixels));
for (plane = 0; plane < 4; plane++) {
stdvga_planar4_plane(plane);
u8 data = GET_FARVAR(SEG_GRAPH, *(u8*)dest_far);
int pixel;
for (pixel=0; pixel<8; pixel++)
op->pixels[pixel] |= ((data>>(7-pixel)) & 1) << plane;
}
break;
case GO_WRITE8:
for (plane = 0; plane<4; plane++) {
stdvga_planar4_plane(plane);
u8 data = 0;
int pixel;
for (pixel=0; pixel<8; pixel++)
data |= ((op->pixels[pixel]>>plane) & 1) << (7-pixel);
SET_FARVAR(SEG_GRAPH, *(u8*)dest_far, data);
}
break;
case GO_MEMSET:
for (plane = 0; plane < 4; plane++) {
stdvga_planar4_plane(plane);
u8 data = (op->pixels[0] & (1<<plane)) ? 0xff : 0x00;
memset_stride(SEG_GRAPH, dest_far, data
, op->xlen / 8, op->linelength, op->ylen);
}
break;
case GO_MEMMOVE: ;
void *src_far = (void*)(op->srcy * op->linelength + op->x / 8);
for (plane = 0; plane < 4; plane++) {
stdvga_planar4_plane(plane);
memmove_stride(SEG_GRAPH, dest_far, src_far
, op->xlen / 8, op->linelength, op->ylen);
}
break;
}
stdvga_planar4_plane(-1);
}
static void
gfx_cga(struct gfx_op *op)
{
int bpp = GET_GLOBAL(op->vmode_g->depth);
void *dest_far = (void*)(op->y / 2 * op->linelength + op->x / 8 * bpp);
switch (op->op) {
default:
case GO_READ8:
if (op->y & 1)
dest_far += 0x2000;
if (bpp == 1) {
u8 data = GET_FARVAR(SEG_CTEXT, *(u8*)dest_far);
int pixel;
for (pixel=0; pixel<8; pixel++)
op->pixels[pixel] = (data >> (7-pixel)) & 1;
} else {
u16 data = GET_FARVAR(SEG_CTEXT, *(u16*)dest_far);
data = be16_to_cpu(data);
int pixel;
for (pixel=0; pixel<8; pixel++)
op->pixels[pixel] = (data >> ((7-pixel)*2)) & 3;
}
break;
case GO_WRITE8:
if (op->y & 1)
dest_far += 0x2000;
if (bpp == 1) {
u8 data = 0;
int pixel;
for (pixel=0; pixel<8; pixel++)
data |= (op->pixels[pixel] & 1) << (7-pixel);
SET_FARVAR(SEG_CTEXT, *(u8*)dest_far, data);
} else {
u16 data = 0;
int pixel;
for (pixel=0; pixel<8; pixel++)
data |= (op->pixels[pixel] & 3) << ((7-pixel) * 2);
data = cpu_to_be16(data);
SET_FARVAR(SEG_CTEXT, *(u16*)dest_far, data);
}
break;
case GO_MEMSET: ;
u8 data = op->pixels[0];
if (bpp == 1)
data = (data&1) | ((data&1)<<1);
data &= 3;
data |= (data<<2) | (data<<4) | (data<<6);
memset_stride(SEG_CTEXT, dest_far, data
, op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
memset_stride(SEG_CTEXT, dest_far + 0x2000, data
, op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
break;
case GO_MEMMOVE: ;
void *src_far = (void*)(op->srcy / 2 * op->linelength + op->x / 8 * bpp);
memmove_stride(SEG_CTEXT, dest_far, src_far
, op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
memmove_stride(SEG_CTEXT, dest_far + 0x2000, src_far + 0x2000
, op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
break;
}
}
static void
gfx_packed(struct gfx_op *op)
{
void *dest_far = (void*)(op->y * op->linelength + op->x);
switch (op->op) {
default:
case GO_READ8:
memcpy_far(GET_SEG(SS), op->pixels, SEG_GRAPH, dest_far, 8);
break;
case GO_WRITE8:
memcpy_far(SEG_GRAPH, dest_far, GET_SEG(SS), op->pixels, 8);
break;
case GO_MEMSET:
memset_stride(SEG_GRAPH, dest_far, op->pixels[0]
, op->xlen, op->linelength, op->ylen);
break;
case GO_MEMMOVE: ;
void *src_far = (void*)(op->srcy * op->linelength + op->x);
memmove_stride(SEG_GRAPH, dest_far, src_far
, op->xlen, op->linelength, op->ylen);
break;
}
}
/****************************************************************
* Gfx interface
****************************************************************/
// Prepare a struct gfx_op for use.
static void
init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g)
{
memset(op, 0, sizeof(*op));
op->vmode_g = vmode_g;
op->linelength = vgahw_get_linelength(vmode_g);
}
// Issue a graphics operation.
static void
handle_gfx_op(struct gfx_op *op)
{
switch (GET_GLOBAL(op->vmode_g->memmodel)) {
case MM_PLANAR:
gfx_planar(op);
break;
case MM_CGA:
gfx_cga(op);
break;
case MM_PACKED:
gfx_packed(op);
break;
default:
break;
}
}
// Move characters when in graphics mode.
static void
gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
, struct cursorpos src, struct cursorpos movesize)
{
struct gfx_op op;
init_gfx_op(&op, vmode_g);
op.x = dest.x * 8;
op.xlen = movesize.x * 8;
int cheight = GET_BDA(char_height);
op.y = dest.y * cheight;
op.ylen = movesize.y * cheight;
op.srcy = src.y * cheight;
op.op = GO_MEMMOVE;
handle_gfx_op(&op);
}
// Clear are of screen in graphics mode.
static void
gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest
, struct carattr ca, struct cursorpos clearsize)
{
struct gfx_op op;
init_gfx_op(&op, vmode_g);
op.x = dest.x * 8;
op.xlen = clearsize.x * 8;
int cheight = GET_BDA(char_height);
op.y = dest.y * cheight;
op.ylen = clearsize.y * cheight;
op.pixels[0] = ca.attr;
op.op = GO_MEMSET;
handle_gfx_op(&op);
}
// Return the font for a given character
struct segoff_s
get_font_data(u8 c)
{
int char_height = GET_BDA(char_height);
struct segoff_s font;
if (char_height == 8 && c >= 128) {
font = GET_IVT(0x1f);
c -= 128;
} else {
font = GET_IVT(0x43);
}
font.offset += c * char_height;
return font;
}
// Write a character to the screen in graphics mode.
static void
gfx_write_char(struct vgamode_s *vmode_g
, struct cursorpos cp, struct carattr ca)
{
if (cp.x >= GET_BDA(video_cols))
return;
struct segoff_s font = get_font_data(ca.car);
struct gfx_op op;
init_gfx_op(&op, vmode_g);
op.x = cp.x * 8;
int cheight = GET_BDA(char_height);
op.y = cp.y * cheight;
int usexor = ca.attr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
int i;
for (i = 0; i < cheight; i++, op.y++) {
u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i));
if (usexor) {
op.op = GO_READ8;
handle_gfx_op(&op);
int j;
for (j = 0; j < 8; j++)
op.pixels[j] ^= (fontline & (0x80>>j)) ? (ca.attr & 0x7f) : 0x00;
} else {
int j;
for (j = 0; j < 8; j++)
op.pixels[j] = (fontline & (0x80>>j)) ? ca.attr : 0x00;
}
op.op = GO_WRITE8;
handle_gfx_op(&op);
}
}
// Set the pixel at the given position.
void
vgafb_write_pixel(u8 color, u16 x, u16 y)
{
struct vgamode_s *vmode_g = get_current_mode();
if (!vmode_g)
return;
struct gfx_op op;
init_gfx_op(&op, vmode_g);
op.x = ALIGN_DOWN(x, 8);
op.y = y;
op.op = GO_READ8;
handle_gfx_op(&op);
int usexor = color & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
if (usexor)
op.pixels[x & 0x07] ^= color & 0x7f;
else
op.pixels[x & 0x07] = color;
op.op = GO_WRITE8;
handle_gfx_op(&op);
}
// Return the pixel at the given position.
u8
vgafb_read_pixel(u16 x, u16 y)
{
struct vgamode_s *vmode_g = get_current_mode();
if (!vmode_g)
return 0;
struct gfx_op op;
init_gfx_op(&op, vmode_g);
op.x = ALIGN_DOWN(x, 8);
op.y = y;
op.op = GO_READ8;
handle_gfx_op(&op);
return op.pixels[x & 0x07];
}
/****************************************************************
* Text ops
****************************************************************/
// Move characters on screen.
void
vgafb_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
, struct cursorpos src, struct cursorpos movesize)
{
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
gfx_move_chars(vmode_g, dest, src, movesize);
return;
}
int cheight = 1;
int cwidth = 2;
int stride = GET_BDA(video_cols) * cwidth;
void *dest_far = (void*)(dest.y * cheight * stride + dest.x * cwidth);
void *src_far = (void*)(src.y * cheight * stride + src.x * cwidth);
u32 pageoffset = GET_BDA(video_pagesize) * dest.page;
u16 seg = GET_GLOBAL(vmode_g->sstart);
memmove_stride(seg, dest_far + pageoffset, src_far + pageoffset
, movesize.x * cwidth, stride, movesize.y * cheight);
}
// Clear are of screen.
void
vgafb_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest
, struct carattr ca, struct cursorpos clearsize)
{
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
gfx_clear_chars(vmode_g, dest, ca, clearsize);
return;
}
int cheight = 1;
int cwidth = 2;
int stride = GET_BDA(video_cols) * cwidth;
void *dest_far = (void*)(dest.y * cheight * stride + dest.x * cwidth);
u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car;
u32 pageoffset = GET_BDA(video_pagesize) * dest.page;
u16 seg = GET_GLOBAL(vmode_g->sstart);
memset16_stride(seg, dest_far + pageoffset, attr
, clearsize.x * cwidth, stride, clearsize.y * cheight);
}
// Write a character to the screen.
void
vgafb_write_char(struct cursorpos cp, struct carattr ca)
{
struct vgamode_s *vmode_g = get_current_mode();
if (!vmode_g)
return;
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
gfx_write_char(vmode_g, cp, ca);
return;
}
int cheight = 1;
int cwidth = 2;
int stride = GET_BDA(video_cols) * cwidth;
int addr = cp.y * cheight * stride + cp.x * cwidth;
void *dest_far = (void*)(GET_BDA(video_pagesize) * cp.page + addr);
if (ca.use_attr) {
u16 dummy = (ca.attr << 8) | ca.car;
SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)dest_far, dummy);
} else {
SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, ca.car);
}
}
// Return the character at the given position on the screen.
struct carattr
vgafb_read_char(struct cursorpos cp)
{
struct vgamode_s *vmode_g = get_current_mode();
if (!vmode_g)
goto fail;
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
// FIXME gfx mode
dprintf(1, "Read char in graphics mode\n");
goto fail;
}
int cheight = 1;
int cwidth = 2;
int stride = GET_BDA(video_cols) * cwidth;
int addr = cp.y * cheight * stride + cp.x * cwidth;
u16 *src_far = (void*)(GET_BDA(video_pagesize) * cp.page + addr);
u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *src_far);
struct carattr ca = {v, v>>8, 0};
return ca;
fail: ;
struct carattr ca2 = {0, 0, 0};
return ca2;
}