blob: f8f35c2d2fbabb2bf816b5652e4f68bbe174e4ab [file] [log] [blame]
Kevin O'Connorc0c7df62009-05-17 18:11:33 -04001// Code for manipulating VGA framebuffers.
2//
Kevin O'Connorf864b602014-03-24 12:49:44 -04003// Copyright (C) 2009-2014 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorc0c7df62009-05-17 18:11:33 -04004// Copyright (C) 2001-2008 the LGPL VGABios developers Team
5//
6// This file may be distributed under the terms of the GNU LGPLv3 license.
7
8#include "biosvar.h" // GET_BDA
Kevin O'Connorb3064592012-08-14 21:20:10 -04009#include "byteorder.h" // cpu_to_be16
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "output.h" // dprintf
Kevin O'Connor160d34a2012-01-16 18:48:26 -050011#include "stdvga.h" // stdvga_planar4_plane
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040012#include "string.h" // memset_far
Kevin O'Connor2f2ec112016-08-05 11:14:58 -040013#include "vgabios.h" // get_current_mode
Kevin O'Connor0397e802016-08-04 17:53:45 -040014#include "vgafb.h" // vgafb_write_char
Kevin O'Connorf864b602014-03-24 12:49:44 -040015#include "vgahw.h" // vgahw_get_linelength
Kevin O'Connor2f2ec112016-08-05 11:14:58 -040016#include "vgautil.h" // VBE_framebuffer
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040017
Kevin O'Connor7fd2af62014-03-20 21:16:28 -040018static inline void
19memmove_stride(u16 seg, void *dst, void *src, int copylen, int stride, int lines)
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040020{
Kevin O'Connor7fd2af62014-03-20 21:16:28 -040021 if (src < dst) {
22 dst += stride * (lines - 1);
23 src += stride * (lines - 1);
24 stride = -stride;
25 }
Kevin O'Connor2c34f412009-05-31 15:25:14 -040026 for (; lines; lines--, dst+=stride, src+=stride)
27 memcpy_far(seg, dst, seg, src, copylen);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040028}
29
Kevin O'Connor2c34f412009-05-31 15:25:14 -040030static inline void
31memset_stride(u16 seg, void *dst, u8 val, int setlen, int stride, int lines)
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040032{
Kevin O'Connor2c34f412009-05-31 15:25:14 -040033 for (; lines; lines--, dst+=stride)
34 memset_far(seg, dst, val, setlen);
35}
36
37static inline void
38memset16_stride(u16 seg, void *dst, u16 val, int setlen, int stride, int lines)
39{
40 for (; lines; lines--, dst+=stride)
41 memset16_far(seg, dst, val, setlen);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040042}
43
Kevin O'Connorf864b602014-03-24 12:49:44 -040044
45/****************************************************************
46 * Basic stdvga graphic manipulation
47 ****************************************************************/
48
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040049static void
Kevin O'Connorf864b602014-03-24 12:49:44 -040050gfx_planar(struct gfx_op *op)
Kevin O'Connor217f2bc2009-05-31 00:46:47 -040051{
Kevin O'Connorefbf4d62014-02-09 11:50:21 -050052 if (!CONFIG_VGA_STDVGA_PORTS)
53 return;
Kevin O'Connorf864b602014-03-24 12:49:44 -040054 void *dest_far = (void*)(op->y * op->linelength + op->x / 8);
55 int plane;
56 switch (op->op) {
57 default:
58 case GO_READ8:
59 memset(op->pixels, 0, sizeof(op->pixels));
60 for (plane = 0; plane < 4; plane++) {
61 stdvga_planar4_plane(plane);
62 u8 data = GET_FARVAR(SEG_GRAPH, *(u8*)dest_far);
63 int pixel;
64 for (pixel=0; pixel<8; pixel++)
65 op->pixels[pixel] |= ((data>>(7-pixel)) & 1) << plane;
66 }
67 break;
68 case GO_WRITE8:
69 for (plane = 0; plane<4; plane++) {
70 stdvga_planar4_plane(plane);
71 u8 data = 0;
72 int pixel;
73 for (pixel=0; pixel<8; pixel++)
74 data |= ((op->pixels[pixel]>>plane) & 1) << (7-pixel);
75 SET_FARVAR(SEG_GRAPH, *(u8*)dest_far, data);
76 }
77 break;
78 case GO_MEMSET:
79 for (plane = 0; plane < 4; plane++) {
80 stdvga_planar4_plane(plane);
81 u8 data = (op->pixels[0] & (1<<plane)) ? 0xff : 0x00;
82 memset_stride(SEG_GRAPH, dest_far, data
83 , op->xlen / 8, op->linelength, op->ylen);
84 }
85 break;
86 case GO_MEMMOVE: ;
87 void *src_far = (void*)(op->srcy * op->linelength + op->x / 8);
88 for (plane = 0; plane < 4; plane++) {
89 stdvga_planar4_plane(plane);
90 memmove_stride(SEG_GRAPH, dest_far, src_far
91 , op->xlen / 8, op->linelength, op->ylen);
92 }
93 break;
94 }
95 stdvga_planar4_plane(-1);
96}
97
98static void
99gfx_cga(struct gfx_op *op)
100{
101 int bpp = GET_GLOBAL(op->vmode_g->depth);
102 void *dest_far = (void*)(op->y / 2 * op->linelength + op->x / 8 * bpp);
103 switch (op->op) {
104 default:
105 case GO_READ8:
106 if (op->y & 1)
107 dest_far += 0x2000;
108 if (bpp == 1) {
109 u8 data = GET_FARVAR(SEG_CTEXT, *(u8*)dest_far);
110 int pixel;
111 for (pixel=0; pixel<8; pixel++)
112 op->pixels[pixel] = (data >> (7-pixel)) & 1;
113 } else {
114 u16 data = GET_FARVAR(SEG_CTEXT, *(u16*)dest_far);
115 data = be16_to_cpu(data);
116 int pixel;
117 for (pixel=0; pixel<8; pixel++)
118 op->pixels[pixel] = (data >> ((7-pixel)*2)) & 3;
119 }
120 break;
121 case GO_WRITE8:
122 if (op->y & 1)
123 dest_far += 0x2000;
124 if (bpp == 1) {
125 u8 data = 0;
126 int pixel;
127 for (pixel=0; pixel<8; pixel++)
128 data |= (op->pixels[pixel] & 1) << (7-pixel);
129 SET_FARVAR(SEG_CTEXT, *(u8*)dest_far, data);
130 } else {
131 u16 data = 0;
132 int pixel;
133 for (pixel=0; pixel<8; pixel++)
134 data |= (op->pixels[pixel] & 3) << ((7-pixel) * 2);
135 data = cpu_to_be16(data);
136 SET_FARVAR(SEG_CTEXT, *(u16*)dest_far, data);
137 }
138 break;
139 case GO_MEMSET: ;
140 u8 data = op->pixels[0];
141 if (bpp == 1)
142 data = (data&1) | ((data&1)<<1);
143 data &= 3;
144 data |= (data<<2) | (data<<4) | (data<<6);
145 memset_stride(SEG_CTEXT, dest_far, data
146 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
147 memset_stride(SEG_CTEXT, dest_far + 0x2000, data
148 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
149 break;
150 case GO_MEMMOVE: ;
151 void *src_far = (void*)(op->srcy / 2 * op->linelength + op->x / 8 * bpp);
152 memmove_stride(SEG_CTEXT, dest_far, src_far
153 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
154 memmove_stride(SEG_CTEXT, dest_far + 0x2000, src_far + 0x2000
155 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
156 break;
157 }
158}
159
160static void
161gfx_packed(struct gfx_op *op)
162{
163 void *dest_far = (void*)(op->y * op->linelength + op->x);
164 switch (op->op) {
165 default:
166 case GO_READ8:
167 memcpy_far(GET_SEG(SS), op->pixels, SEG_GRAPH, dest_far, 8);
168 break;
169 case GO_WRITE8:
170 memcpy_far(SEG_GRAPH, dest_far, GET_SEG(SS), op->pixels, 8);
171 break;
172 case GO_MEMSET:
173 memset_stride(SEG_GRAPH, dest_far, op->pixels[0]
174 , op->xlen, op->linelength, op->ylen);
175 break;
176 case GO_MEMMOVE: ;
177 void *src_far = (void*)(op->srcy * op->linelength + op->x);
Kevin O'Connor7fd2af62014-03-20 21:16:28 -0400178 memmove_stride(SEG_GRAPH, dest_far, src_far
Kevin O'Connorf864b602014-03-24 12:49:44 -0400179 , op->xlen, op->linelength, op->ylen);
Kevin O'Connora0263082012-04-14 20:22:18 -0400180 break;
Kevin O'Connor217f2bc2009-05-31 00:46:47 -0400181 }
182}
183
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400184
185/****************************************************************
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400186 * Direct framebuffers in high mem
187 ****************************************************************/
188
189// Use int 1587 call to copy memory to/from the framebuffer.
Gerd Hoffmann03f50612019-03-08 12:34:38 +0100190void memcpy_high(void *dest, void *src, u32 len)
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400191{
192 u64 gdt[6];
193 gdt[2] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)src);
194 gdt[3] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)dest);
195
196 // Call int 1587 to copy data.
197 len/=2;
198 u32 flags;
199 u32 eax = 0x8700;
200 u32 si = (u32)&gdt;
201 SET_SEG(ES, GET_SEG(SS));
202 asm volatile(
203 "stc\n"
204 "int $0x15\n"
205 "cli\n"
206 "cld\n"
207 "pushfl\n"
208 "popl %0\n"
209 : "=r" (flags), "+a" (eax), "+S" (si), "+c" (len)
210 : : "cc", "memory");
211}
212
213static void
214memmove_stride_high(void *dst, void *src, int copylen, int stride, int lines)
215{
216 if (src < dst) {
217 dst += stride * (lines - 1);
218 src += stride * (lines - 1);
219 stride = -stride;
220 }
221 for (; lines; lines--, dst+=stride, src+=stride)
222 memcpy_high(dst, src, copylen);
223}
224
225// Map a CGA color to a "direct" mode rgb value.
226static u32
227get_color(int depth, u8 attr)
228{
229 int rbits, gbits, bbits;
230 switch (depth) {
231 case 15: rbits=5; gbits=5; bbits=5; break;
232 case 16: rbits=5; gbits=6; bbits=5; break;
233 default:
234 case 24: rbits=8; gbits=8; bbits=8; break;
235 }
236 int h = (attr&8) ? 1 : 0;
237 int r = (attr&4) ? 2 : 0, g = (attr&2) ? 2 : 0, b = (attr&1) ? 2 : 0;
238 if ((attr & 0xf) == 6)
239 g = 1;
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400240 int rv = DIV_ROUND_CLOSEST(((1<<rbits) - 1) * (r + h), 3);
241 int gv = DIV_ROUND_CLOSEST(((1<<gbits) - 1) * (g + h), 3);
242 int bv = DIV_ROUND_CLOSEST(((1<<bbits) - 1) * (b + h), 3);
243 return (rv << (gbits+bbits)) + (gv << bbits) + bv;
244}
245
246// Find the closest attribute for a given framebuffer color
247static u8
248reverse_color(int depth, u32 color)
249{
250 int rbits, gbits, bbits;
251 switch (depth) {
252 case 15: rbits=5; gbits=5; bbits=5; break;
253 case 16: rbits=5; gbits=6; bbits=5; break;
254 default:
255 case 24: rbits=8; gbits=8; bbits=8; break;
256 }
257 int rv = (color >> (gbits+bbits)) & ((1<<rbits)-1);
258 int gv = (color >> bbits) & ((1<<gbits)-1);
259 int bv = color & ((1<<bbits)-1);
260 int r = DIV_ROUND_CLOSEST(rv * 3, (1<<rbits) - 1);
261 int g = DIV_ROUND_CLOSEST(gv * 3, (1<<gbits) - 1);
262 int b = DIV_ROUND_CLOSEST(bv * 3, (1<<bbits) - 1);
263 int h = r && g && b && (r != 2 || g != 2 || b != 2);
264 return (h ? 8 : 0) | ((r-h) ? 4 : 0) | ((g-h) ? 2 : 0) | ((b-h) ? 1 : 0);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400265}
266
267static void
268gfx_direct(struct gfx_op *op)
269{
270 void *fb = (void*)GET_GLOBAL(VBE_framebuffer);
271 if (!fb)
272 return;
273 int depth = GET_GLOBAL(op->vmode_g->depth);
274 int bypp = DIV_ROUND_UP(depth, 8);
275 void *dest_far = (fb + op->displaystart + op->y * op->linelength
276 + op->x * bypp);
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400277 u8 data[64];
278 int i;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400279 switch (op->op) {
280 default:
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400281 case GO_READ8:
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400282 memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8);
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400283 for (i=0; i<8; i++)
284 op->pixels[i] = reverse_color(depth, *(u32*)&data[i*bypp]);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400285 break;
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400286 case GO_WRITE8:
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400287 for (i=0; i<8; i++)
288 *(u32*)&data[i*bypp] = get_color(depth, op->pixels[i]);
289 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
290 break;
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400291 case GO_MEMSET: ;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400292 u32 color = get_color(depth, op->pixels[0]);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400293 for (i=0; i<8; i++)
294 *(u32*)&data[i*bypp] = color;
295 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
296 memcpy_high(dest_far + bypp * 8, dest_far, op->xlen * bypp - bypp * 8);
297 for (i=1; i < op->ylen; i++)
298 memcpy_high(dest_far + op->linelength * i
299 , dest_far, op->xlen * bypp);
300 break;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400301 case GO_MEMMOVE: ;
302 void *src_far = (fb + op->displaystart + op->srcy * op->linelength
303 + op->x * bypp);
304 memmove_stride_high(dest_far, src_far
305 , op->xlen * bypp, op->linelength, op->ylen);
306 break;
307 }
308}
309
310
311/****************************************************************
Kevin O'Connorf864b602014-03-24 12:49:44 -0400312 * Gfx interface
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400313 ****************************************************************/
314
Kevin O'Connorf864b602014-03-24 12:49:44 -0400315// Prepare a struct gfx_op for use.
Kevin O'Connor7c790292014-02-11 15:34:58 -0500316void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400317init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g)
318{
319 memset(op, 0, sizeof(*op));
320 op->vmode_g = vmode_g;
321 op->linelength = vgahw_get_linelength(vmode_g);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400322 op->displaystart = vgahw_get_displaystart(vmode_g);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400323}
324
325// Issue a graphics operation.
Kevin O'Connor7c790292014-02-11 15:34:58 -0500326void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400327handle_gfx_op(struct gfx_op *op)
328{
329 switch (GET_GLOBAL(op->vmode_g->memmodel)) {
330 case MM_PLANAR:
331 gfx_planar(op);
332 break;
333 case MM_CGA:
334 gfx_cga(op);
335 break;
336 case MM_PACKED:
337 gfx_packed(op);
338 break;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400339 case MM_DIRECT:
340 gfx_direct(op);
341 break;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400342 default:
343 break;
344 }
345}
346
347// Move characters when in graphics mode.
348static void
349gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400350 , struct cursorpos movesize, int lines)
Kevin O'Connorf864b602014-03-24 12:49:44 -0400351{
352 struct gfx_op op;
353 init_gfx_op(&op, vmode_g);
354 op.x = dest.x * 8;
355 op.xlen = movesize.x * 8;
356 int cheight = GET_BDA(char_height);
357 op.y = dest.y * cheight;
358 op.ylen = movesize.y * cheight;
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400359 op.srcy = op.y + lines * cheight;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400360 op.op = GO_MEMMOVE;
361 handle_gfx_op(&op);
362}
363
Kevin O'Connore638f972015-01-06 09:36:41 -0500364// Clear area of screen in graphics mode.
Kevin O'Connorf864b602014-03-24 12:49:44 -0400365static void
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400366gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos win
367 , struct cursorpos winsize, struct carattr ca)
Kevin O'Connorf864b602014-03-24 12:49:44 -0400368{
369 struct gfx_op op;
370 init_gfx_op(&op, vmode_g);
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400371 op.x = win.x * 8;
372 op.xlen = winsize.x * 8;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400373 int cheight = GET_BDA(char_height);
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400374 op.y = win.y * cheight;
375 op.ylen = winsize.y * cheight;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400376 op.pixels[0] = ca.attr;
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400377 if (vga_emulate_text())
378 op.pixels[0] = ca.attr >> 4;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400379 op.op = GO_MEMSET;
380 handle_gfx_op(&op);
381}
382
383// Return the font for a given character
384struct segoff_s
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500385get_font_data(u8 c)
386{
387 int char_height = GET_BDA(char_height);
388 struct segoff_s font;
389 if (char_height == 8 && c >= 128) {
390 font = GET_IVT(0x1f);
391 c -= 128;
392 } else {
393 font = GET_IVT(0x43);
394 }
395 font.offset += c * char_height;
396 return font;
397}
398
Kevin O'Connorf864b602014-03-24 12:49:44 -0400399// Write a character to the screen in graphics mode.
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400400static void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400401gfx_write_char(struct vgamode_s *vmode_g
Kevin O'Connord3b38152009-05-26 00:05:37 -0400402 , struct cursorpos cp, struct carattr ca)
403{
Kevin O'Connorf864b602014-03-24 12:49:44 -0400404 if (cp.x >= GET_BDA(video_cols))
405 return;
406
407 struct segoff_s font = get_font_data(ca.car);
408 struct gfx_op op;
409 init_gfx_op(&op, vmode_g);
410 op.x = cp.x * 8;
411 int cheight = GET_BDA(char_height);
412 op.y = cp.y * cheight;
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400413 u8 fgattr = ca.attr, bgattr = 0x00;
414 int usexor = 0;
415 if (vga_emulate_text()) {
416 if (ca.use_attr) {
417 bgattr = fgattr >> 4;
418 fgattr = fgattr & 0x0f;
419 } else {
420 // Read bottom right pixel of the cell to guess bg color
421 op.op = GO_READ8;
422 op.y += cheight-1;
423 handle_gfx_op(&op);
424 op.y -= cheight-1;
425 bgattr = op.pixels[7];
426 fgattr = bgattr ^ 0x7;
427 }
428 } else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) {
429 usexor = 1;
430 fgattr &= 0x7f;
431 }
Kevin O'Connorf864b602014-03-24 12:49:44 -0400432 int i;
433 for (i = 0; i < cheight; i++, op.y++) {
434 u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i));
435 if (usexor) {
436 op.op = GO_READ8;
437 handle_gfx_op(&op);
438 int j;
439 for (j = 0; j < 8; j++)
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400440 op.pixels[j] ^= (fontline & (0x80>>j)) ? fgattr : 0x00;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400441 } else {
442 int j;
443 for (j = 0; j < 8; j++)
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400444 op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400445 }
446 op.op = GO_WRITE8;
447 handle_gfx_op(&op);
448 }
449}
450
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500451// Read a character from the screen in graphics mode.
452static struct carattr
453gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp)
454{
455 u8 lines[16];
456 int cheight = GET_BDA(char_height);
457 if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines))
458 goto fail;
459
460 // Read cell from screen
461 struct gfx_op op;
462 init_gfx_op(&op, vmode_g);
463 op.op = GO_READ8;
464 op.x = cp.x * 8;
465 op.y = cp.y * cheight;
Kevin O'Connore638f972015-01-06 09:36:41 -0500466 int car = 0;
467 u8 fgattr = 0x00, bgattr = 0x00;
468 if (vga_emulate_text()) {
469 // Read bottom right pixel of the cell to guess bg color
470 op.y += cheight-1;
471 handle_gfx_op(&op);
472 op.y -= cheight-1;
473 bgattr = op.pixels[7];
474 fgattr = bgattr ^ 0x7;
475 // Report space character for blank cells (skip null character check)
476 car = 1;
477 }
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500478 u8 i, j;
479 for (i=0; i<cheight; i++, op.y++) {
480 u8 line = 0;
481 handle_gfx_op(&op);
482 for (j=0; j<8; j++)
Kevin O'Connore638f972015-01-06 09:36:41 -0500483 if (op.pixels[j] != bgattr) {
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500484 line |= 0x80 >> j;
Kevin O'Connore638f972015-01-06 09:36:41 -0500485 fgattr = op.pixels[j];
486 }
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500487 lines[i] = line;
488 }
489
490 // Determine font
Kevin O'Connore638f972015-01-06 09:36:41 -0500491 for (; car<256; car++) {
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500492 struct segoff_s font = get_font_data(car);
493 if (memcmp_far(GET_SEG(SS), lines
494 , font.seg, (void*)(font.offset+0), cheight) == 0)
Kevin O'Connore638f972015-01-06 09:36:41 -0500495 return (struct carattr){car, fgattr | (bgattr << 4), 0};
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500496 }
497fail:
498 return (struct carattr){0, 0, 0};
499}
500
Kevin O'Connorf864b602014-03-24 12:49:44 -0400501// Set the pixel at the given position.
502void
503vgafb_write_pixel(u8 color, u16 x, u16 y)
504{
505 struct vgamode_s *vmode_g = get_current_mode();
506 if (!vmode_g)
507 return;
508
509 struct gfx_op op;
510 init_gfx_op(&op, vmode_g);
511 op.x = ALIGN_DOWN(x, 8);
512 op.y = y;
513 op.op = GO_READ8;
514 handle_gfx_op(&op);
515
516 int usexor = color & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
517 if (usexor)
518 op.pixels[x & 0x07] ^= color & 0x7f;
519 else
520 op.pixels[x & 0x07] = color;
521 op.op = GO_WRITE8;
522 handle_gfx_op(&op);
523}
524
525// Return the pixel at the given position.
526u8
527vgafb_read_pixel(u16 x, u16 y)
528{
529 struct vgamode_s *vmode_g = get_current_mode();
530 if (!vmode_g)
531 return 0;
532
533 struct gfx_op op;
534 init_gfx_op(&op, vmode_g);
535 op.x = ALIGN_DOWN(x, 8);
536 op.y = y;
537 op.op = GO_READ8;
538 handle_gfx_op(&op);
539
540 return op.pixels[x & 0x07];
541}
542
543
544/****************************************************************
545 * Text ops
546 ****************************************************************/
547
Kevin O'Connora02d4182014-04-05 22:48:05 -0400548// Return the fb offset for the given character address when in text mode.
549void *
550text_address(struct cursorpos cp)
551{
552 int stride = GET_BDA(video_cols) * 2;
553 u32 pageoffset = GET_BDA(video_pagesize) * cp.page;
554 return (void*)pageoffset + cp.y * stride + cp.x * 2;
555}
556
Kevin O'Connorf864b602014-03-24 12:49:44 -0400557// Move characters on screen.
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400558static void
559vgafb_move_chars(struct cursorpos dest, struct cursorpos movesize, int lines)
Kevin O'Connorf864b602014-03-24 12:49:44 -0400560{
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400561 struct vgamode_s *vmode_g = get_current_mode();
562 if (!vmode_g)
563 return;
564
Kevin O'Connorf864b602014-03-24 12:49:44 -0400565 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400566 gfx_move_chars(vmode_g, dest, movesize, lines);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400567 return;
568 }
569
Kevin O'Connora02d4182014-04-05 22:48:05 -0400570 int stride = GET_BDA(video_cols) * 2;
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400571 void *dest_addr = text_address(dest), *src_addr = dest_addr + lines * stride;
572 memmove_stride(GET_GLOBAL(vmode_g->sstart), dest_addr, src_addr
Kevin O'Connora02d4182014-04-05 22:48:05 -0400573 , movesize.x * 2, stride, movesize.y);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400574}
575
Kevin O'Connore638f972015-01-06 09:36:41 -0500576// Clear area of screen.
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400577static void
578vgafb_clear_chars(struct cursorpos win, struct cursorpos winsize
579 , struct carattr ca)
Kevin O'Connorf864b602014-03-24 12:49:44 -0400580{
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400581 struct vgamode_s *vmode_g = get_current_mode();
582 if (!vmode_g)
583 return;
584
Kevin O'Connorf864b602014-03-24 12:49:44 -0400585 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400586 gfx_clear_chars(vmode_g, win, winsize, ca);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400587 return;
588 }
589
Kevin O'Connora02d4182014-04-05 22:48:05 -0400590 int stride = GET_BDA(video_cols) * 2;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400591 u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car;
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400592 memset16_stride(GET_GLOBAL(vmode_g->sstart), text_address(win), attr
593 , winsize.x * 2, stride, winsize.y);
594}
595
596// Scroll characters within a window on the screen
597void
598vgafb_scroll(struct cursorpos win, struct cursorpos winsize
599 , int lines, struct carattr ca)
600{
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400601 if (!lines) {
602 // Clear window
603 vgafb_clear_chars(win, winsize, ca);
604 } else if (lines > 0) {
605 // Scroll the window up (eg, from page down key)
606 winsize.y -= lines;
607 vgafb_move_chars(win, winsize, lines);
608
609 win.y += winsize.y;
610 winsize.y = lines;
611 vgafb_clear_chars(win, winsize, ca);
612 } else {
613 // Scroll the window down (eg, from page up key)
614 win.y -= lines;
615 winsize.y += lines;
616 vgafb_move_chars(win, winsize, lines);
617
618 win.y += lines;
619 winsize.y = -lines;
620 vgafb_clear_chars(win, winsize, ca);
621 }
Kevin O'Connorf864b602014-03-24 12:49:44 -0400622}
623
624// Write a character to the screen.
625void
626vgafb_write_char(struct cursorpos cp, struct carattr ca)
627{
628 struct vgamode_s *vmode_g = get_current_mode();
629 if (!vmode_g)
630 return;
631
632 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
633 gfx_write_char(vmode_g, cp, ca);
634 return;
635 }
636
Kevin O'Connora02d4182014-04-05 22:48:05 -0400637 void *dest_far = text_address(cp);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400638 if (ca.use_attr) {
639 u16 dummy = (ca.attr << 8) | ca.car;
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500640 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)dest_far, dummy);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400641 } else {
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500642 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, ca.car);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400643 }
644}
645
Kevin O'Connorf864b602014-03-24 12:49:44 -0400646// Return the character at the given position on the screen.
Kevin O'Connor09262412009-05-25 11:44:11 -0400647struct carattr
Kevin O'Connord3b38152009-05-26 00:05:37 -0400648vgafb_read_char(struct cursorpos cp)
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400649{
Kevin O'Connor4a73f932012-01-21 11:08:35 -0500650 struct vgamode_s *vmode_g = get_current_mode();
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400651 if (!vmode_g)
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500652 return (struct carattr){0, 0, 0};
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400653
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500654 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT)
655 return gfx_read_char(vmode_g, cp);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400656
Kevin O'Connora02d4182014-04-05 22:48:05 -0400657 u16 *dest_far = text_address(cp);
658 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far);
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500659 return (struct carattr){v, v>>8, 0};
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400660}