#include <kernel/kernel.h>
#include <wchar.h>
#include "video.h"
//! Physical address of the VGA frame buffer
addr_t video_base = 0xa0000;
int maskbit[640], y80[480], xconv[640], startmasks[8], endmasks[8];
void vga4Close(video_t *vid)
{
}
int vga4EnumModes(video_t *vid, unsigned index, videomode_t *mode)
{
/* We only support one mode -- BIOS 12h (set by the real-mode loader) */
if (index == 0)
{
mode->cookie = 0;
mode->width = 640;
mode->height = 480;
mode->bitsPerPixel = 4;
mode->bytesPerLine = (mode->width * mode->bitsPerPixel) / 8;
return VID_ENUM_STOP;
}
else
return VID_ENUM_ERROR;
}
bool vga4SetMode(video_t *vid, videomode_t *mode)
{
/* Clear the screen when we "change" modes */
//vid->vidFillRect(vid, 0, 0, mode->width, mode->height, 0);
return true;
}
void vga4PreCalc()
{
unsigned long j;
startmasks[7] = 255;
startmasks[6] = 127;
startmasks[5] = 63;
startmasks[4] = 31;
startmasks[3] = 15;
startmasks[2] = 7;
startmasks[1] = 3;
startmasks[0] = 1;
endmasks[0] = 255;
endmasks[1] = 128;
endmasks[2] = 192;
endmasks[3] = 224;
endmasks[4] = 240;
endmasks[5] = 248;
endmasks[6] = 252;
endmasks[7] = 254;
for (j = 0; j < 80; j++)
{
maskbit[j * 8] = 128;
maskbit[j * 8 + 1] = 64;
maskbit[j * 8 + 2] = 32;
maskbit[j * 8 + 3] = 16;
maskbit[j * 8 + 4] = 8;
maskbit[j * 8 + 5] = 4;
maskbit[j * 8 + 6] = 2;
maskbit[j * 8 + 7] = 1;
}
for (j = 0; j < 480; j++)
y80[j] = j * 80;
for (j = 0; j < 640; j++)
xconv[j] = j >> 3;
}
void vga4PutPixel(video_t *vid, int x, int y, pixel_t c)
{
addr_t offset;
volatile byte a;
c = (byte) c;
offset = video_base + xconv[x] + y80[y];
out16(VGA_GC_INDEX, 0x08 | (maskbit[x] << 8));
if (c)
{
out16(VGA_SEQ_INDEX, 0x02 | (c << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0xff);
}
if (~c)
{
out16(VGA_SEQ_INDEX, 0x02 | (~c << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0);
}
}
void vga4GetByte(addr_t offset,
byte *b, byte *g,
byte *r, byte *i)
{
out16(VGA_GC_INDEX, 0x0304);
*i = i386_lpeek8(video_base + offset);
out16(VGA_GC_INDEX, 0x0204);
*r = i386_lpeek8(video_base + offset);
out16(VGA_GC_INDEX, 0x0104);
*g = i386_lpeek8(video_base + offset);
out16(VGA_GC_INDEX, 0x0004);
*b = i386_lpeek8(video_base + offset);
}
pixel_t vga4GetPixel(video_t *vid, int x, int y)
{
byte mask, b, g, r, i;
addr_t offset;
offset = xconv[x] + y80[y];
vga4GetByte(offset, &b, &g, &r, &i);
mask = maskbit[x];
b &= mask;
g &= mask;
r &= mask;
i &= mask;
mask = 7 - (x % 8);
g >>= mask;
b >>= mask;
r >>= mask;
i >>= mask;
return b + 2 * g + 4 * r + 8 * i;
}
void vga4HLine(video_t *vid, int x1, int x2, int y, pixel_t c)
{
int midx, leftpix, rightx, midpix, rightpix;
byte leftmask, rightmask;
addr_t offset;
volatile byte a;
c = (byte) c;
offset = xconv[x1] + y80[y];
offset += video_base;
/* midx = start of middle region */
midx = (x1 + 7) & -8;
/* leftpix = number of pixels to left of middle */
leftpix = midx - x1;
if (leftpix > 0)
{
/* leftmask = pixels set to left of middle */
leftmask = 0xff >> (8 - leftpix);
//leftmask = startmasks[leftpix];
out16(VGA_GC_INDEX, 0x08 | (leftmask << 8));
out16(VGA_SEQ_INDEX, 0x02 | (c << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0xff);
out16(VGA_SEQ_INDEX, 0x02 | (~c << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0);
offset++;
}
/* rightx = end of middle region */
rightx = x2 & -8;
/* midpix = number of pixels in middle */
midpix = rightx - midx;
out16(VGA_GC_INDEX, 0xff08);
if (c)
{
out16(VGA_SEQ_INDEX, 0x02 | (c << 8));
i386_lmemset(offset, 0xff, midpix / 8);
}
if (~c)
{
out16(VGA_SEQ_INDEX, 0x02 | (~c << 8));
i386_lmemset(offset, 0, midpix / 8);
}
offset += midpix / 8;
/* rightpix = number of pixels to right of middle */
rightpix = x2 - rightx;
if (rightpix > 0)
{
/* rightmask = pixels set to right of middle */
rightmask = 0xff << (8 - rightpix);
//rightmask = endmasks[rightpix];
out16(VGA_GC_INDEX, 0x08 | (rightmask << 8));
out16(VGA_SEQ_INDEX, 0x02 | (c << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0xff);
out16(VGA_SEQ_INDEX, 0x02 | (~c << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0);
}
}
void vga4TextOut(video_t *vid,
int x, int y, vga_font_t *font, const wchar_t *str,
size_t len, pixel_t afg, pixel_t abg)
{
int ay;
byte *data, fg, bg;
addr_t offset;
volatile int a;
unsigned char ch[2];
fg = (byte) afg;
bg = (byte) abg;
if (len == -1)
len = wcslen(str);
for (; len > 0; str++, len--)
{
ch[0] = 0;
wcstombs(ch, str, 1);
if (ch[0] < font->First || ch[0] > font->Last)
ch[0] = '?';
data = font->Bitmaps + font->Height * (ch[0] - font->First);
offset = video_base + xconv[x] + y80[y];
for (ay = 0; ay < font->Height; ay++)
{
if (afg != (pixel_t) -1)
{
/* draw letter */
out16(VGA_GC_INDEX, 0x08 | (data[ay] << 8));
if (fg)
{
out16(VGA_SEQ_INDEX, 0x02 | (fg << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0xff);
}
if (~fg)
{
out16(VGA_SEQ_INDEX, 0x02 | (~fg << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0);
}
}
if (abg != (pixel_t) -1)
{
/* draw background */
out16(VGA_GC_INDEX, 0x08 | (~data[ay] << 8));
if (bg)
{
out16(VGA_SEQ_INDEX, 0x02 | (bg << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0xff);
}
if (~bg)
{
out16(VGA_SEQ_INDEX, 0x02 | (~bg << 8));
a = i386_lpeek8(offset);
i386_lpoke8(offset, 0);
}
}
offset += 80;
}
x += 8;
}
}
video_t vga4 =
{
vga4Close,
vga4EnumModes,
vga4SetMode,
vga4PutPixel,
vga4GetPixel,
vga4HLine,
NULL, /* vline */
NULL, /* line */
NULL, /* fillrect */
vga4TextOut
};
video_t *vga4Init(videomode_t *mode)
{
vga4PreCalc();
return &vga4;
}