660 lines
19 KiB
C
660 lines
19 KiB
C
// 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" // get_current_mode
|
|
#include "vgafb.h" // vgafb_write_char
|
|
#include "vgahw.h" // vgahw_get_linelength
|
|
#include "vgautil.h" // VBE_framebuffer
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* Direct framebuffers in high mem
|
|
****************************************************************/
|
|
|
|
// Use int 1587 call to copy memory to/from the framebuffer.
|
|
void memcpy_high(void *dest, void *src, u32 len)
|
|
{
|
|
u64 gdt[6];
|
|
gdt[2] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)src);
|
|
gdt[3] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)dest);
|
|
|
|
// Call int 1587 to copy data.
|
|
len/=2;
|
|
u32 flags;
|
|
u32 eax = 0x8700;
|
|
u32 si = (u32)&gdt;
|
|
SET_SEG(ES, GET_SEG(SS));
|
|
asm volatile(
|
|
"stc\n"
|
|
"int $0x15\n"
|
|
"cli\n"
|
|
"cld\n"
|
|
"pushfl\n"
|
|
"popl %0\n"
|
|
: "=r" (flags), "+a" (eax), "+S" (si), "+c" (len)
|
|
: : "cc", "memory");
|
|
}
|
|
|
|
static void
|
|
memmove_stride_high(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_high(dst, src, copylen);
|
|
}
|
|
|
|
// Map a CGA color to a "direct" mode rgb value.
|
|
static u32
|
|
get_color(int depth, u8 attr)
|
|
{
|
|
int rbits, gbits, bbits;
|
|
switch (depth) {
|
|
case 15: rbits=5; gbits=5; bbits=5; break;
|
|
case 16: rbits=5; gbits=6; bbits=5; break;
|
|
default:
|
|
case 24: rbits=8; gbits=8; bbits=8; break;
|
|
}
|
|
int h = (attr&8) ? 1 : 0;
|
|
int r = (attr&4) ? 2 : 0, g = (attr&2) ? 2 : 0, b = (attr&1) ? 2 : 0;
|
|
if ((attr & 0xf) == 6)
|
|
g = 1;
|
|
int rv = DIV_ROUND_CLOSEST(((1<<rbits) - 1) * (r + h), 3);
|
|
int gv = DIV_ROUND_CLOSEST(((1<<gbits) - 1) * (g + h), 3);
|
|
int bv = DIV_ROUND_CLOSEST(((1<<bbits) - 1) * (b + h), 3);
|
|
return (rv << (gbits+bbits)) + (gv << bbits) + bv;
|
|
}
|
|
|
|
// Find the closest attribute for a given framebuffer color
|
|
static u8
|
|
reverse_color(int depth, u32 color)
|
|
{
|
|
int rbits, gbits, bbits;
|
|
switch (depth) {
|
|
case 15: rbits=5; gbits=5; bbits=5; break;
|
|
case 16: rbits=5; gbits=6; bbits=5; break;
|
|
default:
|
|
case 24: rbits=8; gbits=8; bbits=8; break;
|
|
}
|
|
int rv = (color >> (gbits+bbits)) & ((1<<rbits)-1);
|
|
int gv = (color >> bbits) & ((1<<gbits)-1);
|
|
int bv = color & ((1<<bbits)-1);
|
|
int r = DIV_ROUND_CLOSEST(rv * 3, (1<<rbits) - 1);
|
|
int g = DIV_ROUND_CLOSEST(gv * 3, (1<<gbits) - 1);
|
|
int b = DIV_ROUND_CLOSEST(bv * 3, (1<<bbits) - 1);
|
|
int h = r && g && b && (r != 2 || g != 2 || b != 2);
|
|
return (h ? 8 : 0) | ((r-h) ? 4 : 0) | ((g-h) ? 2 : 0) | ((b-h) ? 1 : 0);
|
|
}
|
|
|
|
static void
|
|
gfx_direct(struct gfx_op *op)
|
|
{
|
|
void *fb = (void*)GET_GLOBAL(VBE_framebuffer);
|
|
if (!fb)
|
|
return;
|
|
int depth = GET_GLOBAL(op->vmode_g->depth);
|
|
int bypp = DIV_ROUND_UP(depth, 8);
|
|
void *dest_far = (fb + op->displaystart + op->y * op->linelength
|
|
+ op->x * bypp);
|
|
u8 data[64];
|
|
int i;
|
|
switch (op->op) {
|
|
default:
|
|
case GO_READ8:
|
|
memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8);
|
|
for (i=0; i<8; i++)
|
|
op->pixels[i] = reverse_color(depth, *(u32*)&data[i*bypp]);
|
|
break;
|
|
case GO_WRITE8:
|
|
for (i=0; i<8; i++)
|
|
*(u32*)&data[i*bypp] = get_color(depth, op->pixels[i]);
|
|
memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
|
|
break;
|
|
case GO_MEMSET: ;
|
|
u32 color = get_color(depth, op->pixels[0]);
|
|
for (i=0; i<8; i++)
|
|
*(u32*)&data[i*bypp] = color;
|
|
memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
|
|
memcpy_high(dest_far + bypp * 8, dest_far, op->xlen * bypp - bypp * 8);
|
|
for (i=1; i < op->ylen; i++)
|
|
memcpy_high(dest_far + op->linelength * i
|
|
, dest_far, op->xlen * bypp);
|
|
break;
|
|
case GO_MEMMOVE: ;
|
|
void *src_far = (fb + op->displaystart + op->srcy * op->linelength
|
|
+ op->x * bypp);
|
|
memmove_stride_high(dest_far, src_far
|
|
, op->xlen * bypp, op->linelength, op->ylen);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* Gfx interface
|
|
****************************************************************/
|
|
|
|
// Prepare a struct gfx_op for use.
|
|
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);
|
|
op->displaystart = vgahw_get_displaystart(vmode_g);
|
|
}
|
|
|
|
// Issue a graphics operation.
|
|
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;
|
|
case MM_DIRECT:
|
|
gfx_direct(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 movesize, int lines)
|
|
{
|
|
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 = op.y + lines * cheight;
|
|
op.op = GO_MEMMOVE;
|
|
handle_gfx_op(&op);
|
|
}
|
|
|
|
// Clear area of screen in graphics mode.
|
|
static void
|
|
gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos win
|
|
, struct cursorpos winsize, struct carattr ca)
|
|
{
|
|
struct gfx_op op;
|
|
init_gfx_op(&op, vmode_g);
|
|
op.x = win.x * 8;
|
|
op.xlen = winsize.x * 8;
|
|
int cheight = GET_BDA(char_height);
|
|
op.y = win.y * cheight;
|
|
op.ylen = winsize.y * cheight;
|
|
op.pixels[0] = ca.attr;
|
|
if (vga_emulate_text())
|
|
op.pixels[0] = ca.attr >> 4;
|
|
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;
|
|
u8 fgattr = ca.attr, bgattr = 0x00;
|
|
int usexor = 0;
|
|
if (vga_emulate_text()) {
|
|
if (ca.use_attr) {
|
|
bgattr = fgattr >> 4;
|
|
fgattr = fgattr & 0x0f;
|
|
} else {
|
|
// Read bottom right pixel of the cell to guess bg color
|
|
op.op = GO_READ8;
|
|
op.y += cheight-1;
|
|
handle_gfx_op(&op);
|
|
op.y -= cheight-1;
|
|
bgattr = op.pixels[7];
|
|
fgattr = bgattr ^ 0x7;
|
|
}
|
|
} else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) {
|
|
usexor = 1;
|
|
fgattr &= 0x7f;
|
|
}
|
|
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)) ? fgattr : 0x00;
|
|
} else {
|
|
int j;
|
|
for (j = 0; j < 8; j++)
|
|
op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr;
|
|
}
|
|
op.op = GO_WRITE8;
|
|
handle_gfx_op(&op);
|
|
}
|
|
}
|
|
|
|
// Read a character from the screen in graphics mode.
|
|
static struct carattr
|
|
gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp)
|
|
{
|
|
u8 lines[16];
|
|
int cheight = GET_BDA(char_height);
|
|
if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines))
|
|
goto fail;
|
|
|
|
// Read cell from screen
|
|
struct gfx_op op;
|
|
init_gfx_op(&op, vmode_g);
|
|
op.op = GO_READ8;
|
|
op.x = cp.x * 8;
|
|
op.y = cp.y * cheight;
|
|
int car = 0;
|
|
u8 fgattr = 0x00, bgattr = 0x00;
|
|
if (vga_emulate_text()) {
|
|
// Read bottom right pixel of the cell to guess bg color
|
|
op.y += cheight-1;
|
|
handle_gfx_op(&op);
|
|
op.y -= cheight-1;
|
|
bgattr = op.pixels[7];
|
|
fgattr = bgattr ^ 0x7;
|
|
// Report space character for blank cells (skip null character check)
|
|
car = 1;
|
|
}
|
|
u8 i, j;
|
|
for (i=0; i<cheight; i++, op.y++) {
|
|
u8 line = 0;
|
|
handle_gfx_op(&op);
|
|
for (j=0; j<8; j++)
|
|
if (op.pixels[j] != bgattr) {
|
|
line |= 0x80 >> j;
|
|
fgattr = op.pixels[j];
|
|
}
|
|
lines[i] = line;
|
|
}
|
|
|
|
// Determine font
|
|
for (; car<256; car++) {
|
|
struct segoff_s font = get_font_data(car);
|
|
if (memcmp_far(GET_SEG(SS), lines
|
|
, font.seg, (void*)(font.offset+0), cheight) == 0)
|
|
return (struct carattr){car, fgattr | (bgattr << 4), 0};
|
|
}
|
|
fail:
|
|
return (struct carattr){0, 0, 0};
|
|
}
|
|
|
|
// 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
|
|
****************************************************************/
|
|
|
|
// Return the fb offset for the given character address when in text mode.
|
|
void *
|
|
text_address(struct cursorpos cp)
|
|
{
|
|
int stride = GET_BDA(video_cols) * 2;
|
|
u32 pageoffset = GET_BDA(video_pagesize) * cp.page;
|
|
return (void*)pageoffset + cp.y * stride + cp.x * 2;
|
|
}
|
|
|
|
// Move characters on screen.
|
|
static void
|
|
vgafb_move_chars(struct cursorpos dest, struct cursorpos movesize, int lines)
|
|
{
|
|
struct vgamode_s *vmode_g = get_current_mode();
|
|
if (!vmode_g)
|
|
return;
|
|
|
|
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
|
|
gfx_move_chars(vmode_g, dest, movesize, lines);
|
|
return;
|
|
}
|
|
|
|
int stride = GET_BDA(video_cols) * 2;
|
|
void *dest_addr = text_address(dest), *src_addr = dest_addr + lines * stride;
|
|
memmove_stride(GET_GLOBAL(vmode_g->sstart), dest_addr, src_addr
|
|
, movesize.x * 2, stride, movesize.y);
|
|
}
|
|
|
|
// Clear area of screen.
|
|
static void
|
|
vgafb_clear_chars(struct cursorpos win, struct cursorpos winsize
|
|
, struct carattr ca)
|
|
{
|
|
struct vgamode_s *vmode_g = get_current_mode();
|
|
if (!vmode_g)
|
|
return;
|
|
|
|
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
|
|
gfx_clear_chars(vmode_g, win, winsize, ca);
|
|
return;
|
|
}
|
|
|
|
int stride = GET_BDA(video_cols) * 2;
|
|
u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car;
|
|
memset16_stride(GET_GLOBAL(vmode_g->sstart), text_address(win), attr
|
|
, winsize.x * 2, stride, winsize.y);
|
|
}
|
|
|
|
// Scroll characters within a window on the screen
|
|
void
|
|
vgafb_scroll(struct cursorpos win, struct cursorpos winsize
|
|
, int lines, struct carattr ca)
|
|
{
|
|
if (!lines) {
|
|
// Clear window
|
|
vgafb_clear_chars(win, winsize, ca);
|
|
} else if (lines > 0) {
|
|
// Scroll the window up (eg, from page down key)
|
|
winsize.y -= lines;
|
|
vgafb_move_chars(win, winsize, lines);
|
|
|
|
win.y += winsize.y;
|
|
winsize.y = lines;
|
|
vgafb_clear_chars(win, winsize, ca);
|
|
} else {
|
|
// Scroll the window down (eg, from page up key)
|
|
win.y -= lines;
|
|
winsize.y += lines;
|
|
vgafb_move_chars(win, winsize, lines);
|
|
|
|
win.y += lines;
|
|
winsize.y = -lines;
|
|
vgafb_clear_chars(win, winsize, ca);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
void *dest_far = text_address(cp);
|
|
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)
|
|
return (struct carattr){0, 0, 0};
|
|
|
|
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT)
|
|
return gfx_read_char(vmode_g, cp);
|
|
|
|
u16 *dest_far = text_address(cp);
|
|
u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far);
|
|
return (struct carattr){v, v>>8, 0};
|
|
}
|