#include "opt_syscons.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/thread.h>
#include <machine/console.h>
#include <machine/framebuffer.h>
#include "syscons.h"
#include <bus/isa/isareg.h>
static vr_draw_border_t kms_draw_border;
static vr_draw_t kms_draw;
static vr_draw_cursor_t kms_cursor;
static vr_blink_cursor_t kms_blink;
#ifndef SC_NO_CUTPASTE
static vr_draw_mouse_t kms_mouse;
#endif
static void kms_nop(scr_stat *scp, ...);
static sc_rndr_sw_t kmsrndrsw = {
kms_draw_border,
kms_draw,
(vr_set_cursor_t *)kms_nop,
kms_cursor,
kms_blink,
#ifndef SC_NO_CUTPASTE
kms_mouse,
#else
(vr_draw_mouse_t *)kms_nop,
#endif
};
RENDERER(kms, V_INFO_MM_TEXT, kmsrndrsw, kms_set);
#ifndef SC_NO_MODE_CHANGE
static sc_rndr_sw_t grrndrsw = {
(vr_draw_border_t *)kms_nop,
(vr_draw_t *)kms_nop,
(vr_set_cursor_t *)kms_nop,
(vr_draw_cursor_t *)kms_nop,
(vr_blink_cursor_t *)kms_nop,
(vr_draw_mouse_t *)kms_nop,
};
RENDERER(kms, V_INFO_MM_OTHER, grrndrsw, kms_set);
#endif
RENDERER_MODULE(kms, kms_set);
static uint32_t colormap24[16] = {
0x00000000,
0x000000aa,
0x0000aa00,
0x0000aaaa,
0x00aa0000,
0x00aa00aa,
0x00aa5500,
0x00aaaaaa,
0x00555555,
0x005555ff,
0x0055ff55,
0x0055ffff,
0x00ff5555,
0x00ff55ff,
0x00ffff55,
0x00ffffff,
};
static uint16_t colormap565[16] = {
0x0000,
0x0015,
0x0540,
0x0555,
0xA800,
0xA815,
0xAAA0,
0xAD55,
0x52AA,
0x52BF,
0x57EA,
0x57FF,
0xFAAA,
0xFCBF,
0xFFEA,
0xffff,
};
#ifndef SC_NO_CUTPASTE
static u_short mouse_and_mask[16] = {
0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80,
0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000
};
static u_short mouse_or_mask[16] = {
0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800,
0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000
};
#endif
static void
kms_nop(scr_stat *scp, ...)
{
}
static inline int
get_pixel_size(uint16_t depth)
{
return depth / 8;
}
#define BLIT_SET 0
#define BLIT_MASK 1
static inline void
blit_blk32(scr_stat *scp, u_char *char_data, int sw, int sh,
vm_offset_t draw_pos, int dw, int dh,
int line_width, uint8_t fgidx, uint8_t bgidx, int how)
{
vm_offset_t p;
int pos;
int x;
int y;
int sx, sx_inc;
int sy, sy_inc;
uint8_t c;
uint32_t fg = colormap24[fgidx];
uint32_t bg = colormap24[bgidx];
if (dw)
sx_inc = (sw << 16) / dw;
else
sx_inc = 0;
if (dh)
sy_inc = (sh << 16) / dh;
else
sy_inc = 0;
sy = 0;
c = 0;
for (y = 0; y < dh; ++y) {
sx = 0;
p = draw_pos;
switch(how) {
case BLIT_SET:
for (x = 0; x < dw; ++x) {
if ((sx & 0x00070000) == 0)
c = char_data[sx >> 19];
pos = ~(sx >> 16) & 7;
writel(p + x * 4, (c & (1 << pos) ? fg : bg));
sx += sx_inc;
}
break;
case BLIT_MASK:
for (x = 0; x < dw; ++x) {
if ((sx & 0x00070000) == 0)
c = char_data[sx >> 19];
pos = ~(sx >> 16) & 7;
if (c & (1 << pos))
writel(p + x * 4, fg);
sx += sx_inc;
}
break;
}
draw_pos += line_width;
sy += sy_inc;
if (sy >= 0x10000) {
char_data += (sy >> 16) * (sw >> 3);
sy &= 0x0FFFF;
}
}
}
static inline void
blit_blk24(scr_stat *scp, u_char *char_data, int sw, int sh,
vm_offset_t draw_pos, int dw, int dh,
int line_width, uint8_t fgidx, uint8_t bgidx, int how)
{
vm_offset_t p;
int pos;
int x;
int y;
int sx, sx_inc;
int sy, sy_inc;
uint8_t c;
uint32_t fg = colormap24[fgidx];
uint32_t bg = colormap24[bgidx];
if (dw)
sx_inc = (sw << 16) / dw;
else
sx_inc = 0;
if (dh)
sy_inc = (sh << 16) / dh;
else
sy_inc = 0;
sy = 0;
c = 0;
for (y = 0; y < dh; ++y) {
sx = 0;
p = draw_pos;
switch(how) {
case BLIT_SET:
for (x = 0; x < dw; ++x) {
if ((sx & 0x00070000) == 0)
c = char_data[sx >> 19];
pos = ~(sx >> 16) & 7;
uint32_t col = c & (1 << pos) ? fg : bg;
writeb(p, col >> 16);
writeb(p + 1, col >> 8);
writeb(p + 2, col >> 0);
p += 3;
sx += sx_inc;
}
break;
case BLIT_MASK:
for (x = 0; x < dw; ++x) {
if ((sx & 0x00070000) == 0)
c = char_data[sx >> 19];
pos = ~(sx >> 16) & 7;
if (c & (1 << pos)) {
writeb(p, fg >> 16);
writeb(p + 1, fg >> 8);
writeb(p + 2, fg >> 0);
}
p += 3;
sx += sx_inc;
}
break;
}
draw_pos += line_width;
sy += sy_inc;
if (sy >= 0x10000) {
char_data += (sy >> 16) * (sw >> 3);
sy &= 0x0FFFF;
}
}
}
static inline void
blit_blk16(scr_stat *scp, u_char *char_data, int sw, int sh,
vm_offset_t draw_pos, int dw, int dh,
int line_width, uint8_t fgidx, uint8_t bgidx, int how)
{
vm_offset_t p;
int pos;
int x;
int y;
int sx, sx_inc;
int sy, sy_inc;
uint8_t c;
uint16_t fg = colormap565[fgidx];
uint16_t bg = colormap565[bgidx];
if (dw)
sx_inc = (sw << 16) / dw;
else
sx_inc = 0;
if (dh)
sy_inc = (sh << 16) / dh;
else
sy_inc = 0;
sy = 0;
c = 0;
for (y = 0; y < dh; ++y) {
sx = 0;
p = draw_pos;
switch(how) {
case BLIT_SET:
for (x = 0; x < dw; ++x) {
if ((sx & 0x00070000) == 0)
c = char_data[sx >> 19];
pos = ~(sx >> 16) & 7;
writew(p + x * 2, (c & (1 << pos) ? fg : bg));
sx += sx_inc;
}
break;
case BLIT_MASK:
for (x = 0; x < dw; ++x) {
if ((sx & 0x00070000) == 0)
c = char_data[sx >> 19];
pos = ~(sx >> 16) & 7;
if (c & (1 << pos))
writew(p + x * 2, fg);
sx += sx_inc;
}
break;
}
draw_pos += line_width;
sy += sy_inc;
if (sy >= 0x10000) {
char_data += (sy >> 16) * (sw >> 3);
sy &= 0x0FFFF;
}
}
}
static inline void
blit_blk(scr_stat *scp, u_char *char_data, int sw, int sh,
vm_offset_t draw_pos, int dw, int dh,
int line_width, uint8_t fgidx, uint8_t bgidx, int how,
int pixel_size)
{
if (pixel_size == 2) {
blit_blk16(scp, char_data, sw, sh, draw_pos, dw, dh, line_width,
fgidx, bgidx, how);
} else if (pixel_size == 3) {
blit_blk24(scp, char_data, sw, sh, draw_pos, dw, dh, line_width,
fgidx, bgidx, how);
} else {
blit_blk32(scp, char_data, sw, sh, draw_pos, dw, dh, line_width,
fgidx, bgidx, how);
}
}
static void
fill_rect32(scr_stat *scp, vm_offset_t draw_pos, int width, int height,
int line_width, uint32_t fg)
{
int i, j;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++)
writel(draw_pos + j * 4, fg);
draw_pos += line_width;
}
}
static void
fill_rect24(scr_stat *scp, vm_offset_t draw_pos, int width, int height,
int line_width, uint32_t fg)
{
int i, j, d;
d = line_width - width * 3;
KKASSERT(d >= 0);
if ((draw_pos & 0x3) == 0 && (line_width & 0x3) == 0 &&
(width & 0x3) == 0) {
uint32_t fga = fg | (fg << 24);
uint32_t fgb = (fg << 16) | ((fg >> 8) & 0xffff);
uint32_t fgc = (fg << 8) | ((fg >> 16) & 0xff);
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 4) {
writel(draw_pos, fga);
writel(draw_pos + 4, fgb);
writel(draw_pos + 8, fgc);
draw_pos += 12;
}
draw_pos += d;
}
} else {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
writeb(draw_pos, fg >> 16);
writeb(draw_pos + 1, fg >> 8);
writeb(draw_pos + 2, fg >> 0);
draw_pos += 3;
}
draw_pos += line_width;
}
}
}
static void
fill_rect16(scr_stat *scp, vm_offset_t draw_pos, int width, int height,
int line_width, uint16_t fg)
{
int i, j;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++)
writew(draw_pos + j * 2, fg);
draw_pos += line_width;
}
}
static void
kms_draw_border(scr_stat *scp, int color)
{
sc_softc_t *sc = scp->sc;
int line_width, pixel_size;
int rightpixel, bottompixel;
vm_offset_t draw_pos;
if (sc->fbi->vaddr == 0)
return;
line_width = sc->fbi->stride;
pixel_size = get_pixel_size(sc->fbi->depth);
rightpixel = sc->fbi->width - scp->xsize * scp->blk_width;
bottompixel = sc->fbi->height - scp->ysize * scp->blk_height;
draw_pos = sc->fbi->vaddr + scp->blk_width * pixel_size * scp->xsize;
if (pixel_size == 2) {
fill_rect16(scp, draw_pos, rightpixel,
scp->blk_height * scp->ysize, line_width,
colormap565[color]);
} else if (pixel_size == 3) {
fill_rect24(scp, draw_pos, rightpixel,
scp->blk_height * scp->ysize, line_width,
colormap24[color]);
} else {
fill_rect32(scp, draw_pos, rightpixel,
scp->blk_height * scp->ysize, line_width,
colormap24[color]);
}
draw_pos = sc->fbi->vaddr + scp->blk_height * scp->ysize * line_width;
if (pixel_size == 2) {
fill_rect16(scp, draw_pos, sc->fbi->width,
sc->fbi->height - scp->blk_height * scp->ysize, line_width,
colormap565[color]);
} else if (pixel_size == 3) {
fill_rect24(scp, draw_pos, sc->fbi->width,
sc->fbi->height - scp->blk_height * scp->ysize, line_width,
colormap24[color]);
} else {
fill_rect32(scp, draw_pos, sc->fbi->width,
sc->fbi->height - scp->blk_height * scp->ysize, line_width,
colormap24[color]);
}
}
static void
kms_draw(scr_stat *scp, int from, int count, int flip)
{
sc_softc_t *sc = scp->sc;
u_char *char_data;
int a, i;
uint8_t fgidx, bgidx;
vm_offset_t draw_pos, p;
int line_width, pixel_size;
if (sc->fbi->vaddr == 0)
return;
line_width = sc->fbi->stride;
pixel_size = get_pixel_size(sc->fbi->depth);
draw_pos = sc->fbi->vaddr +
scp->blk_height * line_width * (from / scp->xsize);
if (from + count > scp->xsize * scp->ysize)
count = scp->xsize * scp->ysize - from;
p = draw_pos + scp->blk_width * pixel_size * (from % scp->xsize);
for (i = from; count-- > 0; i++) {
char_data = &(scp->font[sc_vtb_getc(&scp->vtb, i) *
scp->font_height]);
a = sc_vtb_geta(&scp->vtb, i);
if (flip) {
fgidx = ((a & 0xf000) >> 4) >> 8;
bgidx = (a & 0x0f00) >> 8;
} else {
fgidx = (a & 0x0f00) >> 8;
bgidx = ((a & 0xf000) >> 4) >> 8;
}
blit_blk(scp, char_data, scp->font_width, scp->font_height,
p, scp->blk_width, scp->blk_height, line_width,
fgidx, bgidx, BLIT_SET, pixel_size);
p += scp->blk_width * pixel_size;
if ((i % scp->xsize) == scp->xsize - 1) {
draw_pos += scp->blk_height * line_width;
p = draw_pos;
}
}
}
static void
draw_kmscursor(scr_stat *scp, int at, int on, int flip)
{
sc_softc_t *sc = scp->sc;
int line_width, pixel_size;
int cursor_base;
int blk_base;
int a;
uint8_t fgidx, bgidx;
unsigned char *char_data;
vm_offset_t draw_pos;
if (sc->fbi->vaddr == 0)
return;
line_width = sc->fbi->stride;
pixel_size = get_pixel_size(sc->fbi->depth);
cursor_base = scp->cursor_base;
blk_base = scp->blk_height * cursor_base / scp->font_height;
draw_pos = sc->fbi->vaddr +
scp->blk_width * pixel_size * (at % scp->xsize) +
scp->blk_height * line_width * (at / scp->xsize) +
blk_base * line_width;
a = sc_vtb_geta(&scp->vtb, at);
if (flip) {
fgidx = ((on) ? (a & 0x0f00) : ((a & 0xf000) >> 4)) >> 8;
bgidx = ((on) ? ((a & 0xf000) >> 4) : (a & 0x0f00)) >> 8;
} else {
fgidx = ((on) ? ((a & 0xf000) >> 4) : (a & 0x0f00)) >> 8;
bgidx = ((on) ? (a & 0x0f00) : ((a & 0xf000) >> 4)) >> 8;
}
char_data = &scp->font[sc_vtb_getc(&scp->vtb, at) * scp->font_height];
char_data += cursor_base;
blit_blk(scp, char_data,
scp->font_width, scp->font_height - cursor_base,
draw_pos, scp->blk_width, scp->blk_height - blk_base,
line_width, fgidx, bgidx, BLIT_SET, pixel_size);
}
static int pxlblinkrate = 0;
static void
kms_cursor(scr_stat *scp, int at, int blink, int on, int flip)
{
if (scp->cursor_height <= 0)
return;
if (on) {
if (!blink) {
scp->status |= VR_CURSOR_ON;
draw_kmscursor(scp, at, on, flip);
} else if (++pxlblinkrate & 4) {
pxlblinkrate = 0;
scp->status ^= VR_CURSOR_ON;
draw_kmscursor(scp, at,
scp->status & VR_CURSOR_ON, flip);
}
} else {
if (scp->status & VR_CURSOR_ON)
draw_kmscursor(scp, at, on, flip);
scp->status &= ~VR_CURSOR_ON;
}
if (blink)
scp->status |= VR_CURSOR_BLINK;
else
scp->status &= ~VR_CURSOR_BLINK;
}
static void
kms_blink(scr_stat *scp, int at, int flip)
{
if (!(scp->status & VR_CURSOR_BLINK))
return;
if (!(++pxlblinkrate & 4))
return;
pxlblinkrate = 0;
scp->status ^= VR_CURSOR_ON;
draw_kmscursor(scp, at, scp->status & VR_CURSOR_ON, flip);
}
#ifndef SC_NO_CUTPASTE
static void
draw_kmsmouse(scr_stat *scp, int x, int y)
{
sc_softc_t *sc = scp->sc;
int line_width, pixel_size;
int blk_width, blk_height;
vm_offset_t draw_pos;
if (sc->fbi->vaddr == 0)
return;
line_width = sc->fbi->stride;
pixel_size = get_pixel_size(sc->fbi->depth);
if (x + scp->font_width < scp->font_width * scp->xsize)
blk_width = scp->blk_width;
else
blk_width = scp->font_width * scp->xsize - x;
if (y + scp->font_height < scp->font_height * scp->ysize)
blk_height = scp->blk_height;
else
blk_height = scp->font_height * scp->ysize - y;
draw_pos = sc->fbi->vaddr + y * scp->blk_height / scp->font_height *
line_width +
x * scp->blk_width / scp->font_width * pixel_size;
blit_blk(scp, (unsigned char *)mouse_and_mask, 16, 16,
draw_pos, blk_width, blk_height, line_width,
0, 0, BLIT_MASK, pixel_size);
blit_blk(scp, (unsigned char *)mouse_or_mask, 16, 16,
draw_pos, blk_width, blk_height, line_width,
15, 0, BLIT_MASK, pixel_size);
}
static void
remove_kmsmouse(scr_stat *scp, int x, int y)
{
int col, row;
int pos;
int i;
col = x / scp->font_width - scp->xoff;
row = y / scp->font_height - scp->yoff;
pos = row * scp->xsize + col;
i = (col < scp->xsize - 1) ? 2 : 1;
(*scp->rndr->draw)(scp, pos, i, FALSE);
if (row < scp->ysize - 1)
(*scp->rndr->draw)(scp, pos + scp->xsize, i, FALSE);
}
static void
kms_mouse(scr_stat *scp, int x, int y, int on)
{
if (on)
draw_kmsmouse(scp, x, y);
else
remove_kmsmouse(scp, x, y);
}
#endif