blob: 5d1ecc936ca7eaf05078564a699e96e27a39fbf6 [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'Connor2d2fa312013-09-14 21:55:26 -040013#include "vgabios.h" // vgafb_scroll
Kevin O'Connorf864b602014-03-24 12:49:44 -040014#include "vgahw.h" // vgahw_get_linelength
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040015
Kevin O'Connor7fd2af62014-03-20 21:16:28 -040016static inline void
17memmove_stride(u16 seg, void *dst, void *src, int copylen, int stride, int lines)
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040018{
Kevin O'Connor7fd2af62014-03-20 21:16:28 -040019 if (src < dst) {
20 dst += stride * (lines - 1);
21 src += stride * (lines - 1);
22 stride = -stride;
23 }
Kevin O'Connor2c34f412009-05-31 15:25:14 -040024 for (; lines; lines--, dst+=stride, src+=stride)
25 memcpy_far(seg, dst, seg, src, copylen);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040026}
27
Kevin O'Connor2c34f412009-05-31 15:25:14 -040028static inline void
29memset_stride(u16 seg, void *dst, u8 val, int setlen, int stride, int lines)
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040030{
Kevin O'Connor2c34f412009-05-31 15:25:14 -040031 for (; lines; lines--, dst+=stride)
32 memset_far(seg, dst, val, setlen);
33}
34
35static inline void
36memset16_stride(u16 seg, void *dst, u16 val, int setlen, int stride, int lines)
37{
38 for (; lines; lines--, dst+=stride)
39 memset16_far(seg, dst, val, setlen);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040040}
41
Kevin O'Connorf864b602014-03-24 12:49:44 -040042
43/****************************************************************
44 * Basic stdvga graphic manipulation
45 ****************************************************************/
46
Kevin O'Connorc0c7df62009-05-17 18:11:33 -040047static void
Kevin O'Connorf864b602014-03-24 12:49:44 -040048gfx_planar(struct gfx_op *op)
Kevin O'Connor217f2bc2009-05-31 00:46:47 -040049{
Kevin O'Connorefbf4d62014-02-09 11:50:21 -050050 if (!CONFIG_VGA_STDVGA_PORTS)
51 return;
Kevin O'Connorf864b602014-03-24 12:49:44 -040052 void *dest_far = (void*)(op->y * op->linelength + op->x / 8);
53 int plane;
54 switch (op->op) {
55 default:
56 case GO_READ8:
57 memset(op->pixels, 0, sizeof(op->pixels));
58 for (plane = 0; plane < 4; plane++) {
59 stdvga_planar4_plane(plane);
60 u8 data = GET_FARVAR(SEG_GRAPH, *(u8*)dest_far);
61 int pixel;
62 for (pixel=0; pixel<8; pixel++)
63 op->pixels[pixel] |= ((data>>(7-pixel)) & 1) << plane;
64 }
65 break;
66 case GO_WRITE8:
67 for (plane = 0; plane<4; plane++) {
68 stdvga_planar4_plane(plane);
69 u8 data = 0;
70 int pixel;
71 for (pixel=0; pixel<8; pixel++)
72 data |= ((op->pixels[pixel]>>plane) & 1) << (7-pixel);
73 SET_FARVAR(SEG_GRAPH, *(u8*)dest_far, data);
74 }
75 break;
76 case GO_MEMSET:
77 for (plane = 0; plane < 4; plane++) {
78 stdvga_planar4_plane(plane);
79 u8 data = (op->pixels[0] & (1<<plane)) ? 0xff : 0x00;
80 memset_stride(SEG_GRAPH, dest_far, data
81 , op->xlen / 8, op->linelength, op->ylen);
82 }
83 break;
84 case GO_MEMMOVE: ;
85 void *src_far = (void*)(op->srcy * op->linelength + op->x / 8);
86 for (plane = 0; plane < 4; plane++) {
87 stdvga_planar4_plane(plane);
88 memmove_stride(SEG_GRAPH, dest_far, src_far
89 , op->xlen / 8, op->linelength, op->ylen);
90 }
91 break;
92 }
93 stdvga_planar4_plane(-1);
94}
95
96static void
97gfx_cga(struct gfx_op *op)
98{
99 int bpp = GET_GLOBAL(op->vmode_g->depth);
100 void *dest_far = (void*)(op->y / 2 * op->linelength + op->x / 8 * bpp);
101 switch (op->op) {
102 default:
103 case GO_READ8:
104 if (op->y & 1)
105 dest_far += 0x2000;
106 if (bpp == 1) {
107 u8 data = GET_FARVAR(SEG_CTEXT, *(u8*)dest_far);
108 int pixel;
109 for (pixel=0; pixel<8; pixel++)
110 op->pixels[pixel] = (data >> (7-pixel)) & 1;
111 } else {
112 u16 data = GET_FARVAR(SEG_CTEXT, *(u16*)dest_far);
113 data = be16_to_cpu(data);
114 int pixel;
115 for (pixel=0; pixel<8; pixel++)
116 op->pixels[pixel] = (data >> ((7-pixel)*2)) & 3;
117 }
118 break;
119 case GO_WRITE8:
120 if (op->y & 1)
121 dest_far += 0x2000;
122 if (bpp == 1) {
123 u8 data = 0;
124 int pixel;
125 for (pixel=0; pixel<8; pixel++)
126 data |= (op->pixels[pixel] & 1) << (7-pixel);
127 SET_FARVAR(SEG_CTEXT, *(u8*)dest_far, data);
128 } else {
129 u16 data = 0;
130 int pixel;
131 for (pixel=0; pixel<8; pixel++)
132 data |= (op->pixels[pixel] & 3) << ((7-pixel) * 2);
133 data = cpu_to_be16(data);
134 SET_FARVAR(SEG_CTEXT, *(u16*)dest_far, data);
135 }
136 break;
137 case GO_MEMSET: ;
138 u8 data = op->pixels[0];
139 if (bpp == 1)
140 data = (data&1) | ((data&1)<<1);
141 data &= 3;
142 data |= (data<<2) | (data<<4) | (data<<6);
143 memset_stride(SEG_CTEXT, dest_far, data
144 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
145 memset_stride(SEG_CTEXT, dest_far + 0x2000, data
146 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
147 break;
148 case GO_MEMMOVE: ;
149 void *src_far = (void*)(op->srcy / 2 * op->linelength + op->x / 8 * bpp);
150 memmove_stride(SEG_CTEXT, dest_far, src_far
151 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
152 memmove_stride(SEG_CTEXT, dest_far + 0x2000, src_far + 0x2000
153 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
154 break;
155 }
156}
157
158static void
159gfx_packed(struct gfx_op *op)
160{
161 void *dest_far = (void*)(op->y * op->linelength + op->x);
162 switch (op->op) {
163 default:
164 case GO_READ8:
165 memcpy_far(GET_SEG(SS), op->pixels, SEG_GRAPH, dest_far, 8);
166 break;
167 case GO_WRITE8:
168 memcpy_far(SEG_GRAPH, dest_far, GET_SEG(SS), op->pixels, 8);
169 break;
170 case GO_MEMSET:
171 memset_stride(SEG_GRAPH, dest_far, op->pixels[0]
172 , op->xlen, op->linelength, op->ylen);
173 break;
174 case GO_MEMMOVE: ;
175 void *src_far = (void*)(op->srcy * op->linelength + op->x);
Kevin O'Connor7fd2af62014-03-20 21:16:28 -0400176 memmove_stride(SEG_GRAPH, dest_far, src_far
Kevin O'Connorf864b602014-03-24 12:49:44 -0400177 , op->xlen, op->linelength, op->ylen);
Kevin O'Connora0263082012-04-14 20:22:18 -0400178 break;
Kevin O'Connor217f2bc2009-05-31 00:46:47 -0400179 }
180}
181
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400182
183/****************************************************************
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400184 * Direct framebuffers in high mem
185 ****************************************************************/
186
187// Use int 1587 call to copy memory to/from the framebuffer.
188static void
189memcpy_high(void *dest, void *src, u32 len)
190{
191 u64 gdt[6];
192 gdt[2] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)src);
193 gdt[3] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)dest);
194
195 // Call int 1587 to copy data.
196 len/=2;
197 u32 flags;
198 u32 eax = 0x8700;
199 u32 si = (u32)&gdt;
200 SET_SEG(ES, GET_SEG(SS));
201 asm volatile(
202 "stc\n"
203 "int $0x15\n"
204 "cli\n"
205 "cld\n"
206 "pushfl\n"
207 "popl %0\n"
208 : "=r" (flags), "+a" (eax), "+S" (si), "+c" (len)
209 : : "cc", "memory");
210}
211
212static void
213memmove_stride_high(void *dst, void *src, int copylen, int stride, int lines)
214{
215 if (src < dst) {
216 dst += stride * (lines - 1);
217 src += stride * (lines - 1);
218 stride = -stride;
219 }
220 for (; lines; lines--, dst+=stride, src+=stride)
221 memcpy_high(dst, src, copylen);
222}
223
224// Map a CGA color to a "direct" mode rgb value.
225static u32
226get_color(int depth, u8 attr)
227{
228 int rbits, gbits, bbits;
229 switch (depth) {
230 case 15: rbits=5; gbits=5; bbits=5; break;
231 case 16: rbits=5; gbits=6; bbits=5; break;
232 default:
233 case 24: rbits=8; gbits=8; bbits=8; break;
234 }
235 int h = (attr&8) ? 1 : 0;
236 int r = (attr&4) ? 2 : 0, g = (attr&2) ? 2 : 0, b = (attr&1) ? 2 : 0;
237 if ((attr & 0xf) == 6)
238 g = 1;
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400239 int rv = DIV_ROUND_CLOSEST(((1<<rbits) - 1) * (r + h), 3);
240 int gv = DIV_ROUND_CLOSEST(((1<<gbits) - 1) * (g + h), 3);
241 int bv = DIV_ROUND_CLOSEST(((1<<bbits) - 1) * (b + h), 3);
242 return (rv << (gbits+bbits)) + (gv << bbits) + bv;
243}
244
245// Find the closest attribute for a given framebuffer color
246static u8
247reverse_color(int depth, u32 color)
248{
249 int rbits, gbits, bbits;
250 switch (depth) {
251 case 15: rbits=5; gbits=5; bbits=5; break;
252 case 16: rbits=5; gbits=6; bbits=5; break;
253 default:
254 case 24: rbits=8; gbits=8; bbits=8; break;
255 }
256 int rv = (color >> (gbits+bbits)) & ((1<<rbits)-1);
257 int gv = (color >> bbits) & ((1<<gbits)-1);
258 int bv = color & ((1<<bbits)-1);
259 int r = DIV_ROUND_CLOSEST(rv * 3, (1<<rbits) - 1);
260 int g = DIV_ROUND_CLOSEST(gv * 3, (1<<gbits) - 1);
261 int b = DIV_ROUND_CLOSEST(bv * 3, (1<<bbits) - 1);
262 int h = r && g && b && (r != 2 || g != 2 || b != 2);
263 return (h ? 8 : 0) | ((r-h) ? 4 : 0) | ((g-h) ? 2 : 0) | ((b-h) ? 1 : 0);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400264}
265
266static void
267gfx_direct(struct gfx_op *op)
268{
269 void *fb = (void*)GET_GLOBAL(VBE_framebuffer);
270 if (!fb)
271 return;
272 int depth = GET_GLOBAL(op->vmode_g->depth);
273 int bypp = DIV_ROUND_UP(depth, 8);
274 void *dest_far = (fb + op->displaystart + op->y * op->linelength
275 + op->x * bypp);
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400276 u8 data[64];
277 int i;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400278 switch (op->op) {
279 default:
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400280 case GO_READ8:
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400281 memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8);
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400282 for (i=0; i<8; i++)
283 op->pixels[i] = reverse_color(depth, *(u32*)&data[i*bypp]);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400284 break;
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400285 case GO_WRITE8:
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400286 for (i=0; i<8; i++)
287 *(u32*)&data[i*bypp] = get_color(depth, op->pixels[i]);
288 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
289 break;
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400290 case GO_MEMSET: ;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400291 u32 color = get_color(depth, op->pixels[0]);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400292 for (i=0; i<8; i++)
293 *(u32*)&data[i*bypp] = color;
294 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
295 memcpy_high(dest_far + bypp * 8, dest_far, op->xlen * bypp - bypp * 8);
296 for (i=1; i < op->ylen; i++)
297 memcpy_high(dest_far + op->linelength * i
298 , dest_far, op->xlen * bypp);
299 break;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400300 case GO_MEMMOVE: ;
301 void *src_far = (fb + op->displaystart + op->srcy * op->linelength
302 + op->x * bypp);
303 memmove_stride_high(dest_far, src_far
304 , op->xlen * bypp, op->linelength, op->ylen);
305 break;
306 }
307}
308
309
310/****************************************************************
Kevin O'Connorf864b602014-03-24 12:49:44 -0400311 * Gfx interface
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400312 ****************************************************************/
313
Kevin O'Connorf864b602014-03-24 12:49:44 -0400314// Prepare a struct gfx_op for use.
Kevin O'Connor7c790292014-02-11 15:34:58 -0500315void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400316init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g)
317{
318 memset(op, 0, sizeof(*op));
319 op->vmode_g = vmode_g;
320 op->linelength = vgahw_get_linelength(vmode_g);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400321 op->displaystart = vgahw_get_displaystart(vmode_g);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400322}
323
324// Issue a graphics operation.
Kevin O'Connor7c790292014-02-11 15:34:58 -0500325void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400326handle_gfx_op(struct gfx_op *op)
327{
328 switch (GET_GLOBAL(op->vmode_g->memmodel)) {
329 case MM_PLANAR:
330 gfx_planar(op);
331 break;
332 case MM_CGA:
333 gfx_cga(op);
334 break;
335 case MM_PACKED:
336 gfx_packed(op);
337 break;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400338 case MM_DIRECT:
339 gfx_direct(op);
340 break;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400341 default:
342 break;
343 }
344}
345
346// Move characters when in graphics mode.
347static void
348gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
349 , struct cursorpos src, struct cursorpos movesize)
350{
351 struct gfx_op op;
352 init_gfx_op(&op, vmode_g);
353 op.x = dest.x * 8;
354 op.xlen = movesize.x * 8;
355 int cheight = GET_BDA(char_height);
356 op.y = dest.y * cheight;
357 op.ylen = movesize.y * cheight;
358 op.srcy = src.y * cheight;
359 op.op = GO_MEMMOVE;
360 handle_gfx_op(&op);
361}
362
Kevin O'Connore638f972015-01-06 09:36:41 -0500363// Clear area of screen in graphics mode.
Kevin O'Connorf864b602014-03-24 12:49:44 -0400364static void
365gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest
366 , struct carattr ca, struct cursorpos clearsize)
367{
368 struct gfx_op op;
369 init_gfx_op(&op, vmode_g);
370 op.x = dest.x * 8;
371 op.xlen = clearsize.x * 8;
372 int cheight = GET_BDA(char_height);
373 op.y = dest.y * cheight;
374 op.ylen = clearsize.y * cheight;
375 op.pixels[0] = ca.attr;
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400376 if (vga_emulate_text())
377 op.pixels[0] = ca.attr >> 4;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400378 op.op = GO_MEMSET;
379 handle_gfx_op(&op);
380}
381
382// Return the font for a given character
383struct segoff_s
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500384get_font_data(u8 c)
385{
386 int char_height = GET_BDA(char_height);
387 struct segoff_s font;
388 if (char_height == 8 && c >= 128) {
389 font = GET_IVT(0x1f);
390 c -= 128;
391 } else {
392 font = GET_IVT(0x43);
393 }
394 font.offset += c * char_height;
395 return font;
396}
397
Kevin O'Connorf864b602014-03-24 12:49:44 -0400398// Write a character to the screen in graphics mode.
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400399static void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400400gfx_write_char(struct vgamode_s *vmode_g
Kevin O'Connord3b38152009-05-26 00:05:37 -0400401 , struct cursorpos cp, struct carattr ca)
402{
Kevin O'Connorf864b602014-03-24 12:49:44 -0400403 if (cp.x >= GET_BDA(video_cols))
404 return;
405
406 struct segoff_s font = get_font_data(ca.car);
407 struct gfx_op op;
408 init_gfx_op(&op, vmode_g);
409 op.x = cp.x * 8;
410 int cheight = GET_BDA(char_height);
411 op.y = cp.y * cheight;
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400412 u8 fgattr = ca.attr, bgattr = 0x00;
413 int usexor = 0;
414 if (vga_emulate_text()) {
415 if (ca.use_attr) {
416 bgattr = fgattr >> 4;
417 fgattr = fgattr & 0x0f;
418 } else {
419 // Read bottom right pixel of the cell to guess bg color
420 op.op = GO_READ8;
421 op.y += cheight-1;
422 handle_gfx_op(&op);
423 op.y -= cheight-1;
424 bgattr = op.pixels[7];
425 fgattr = bgattr ^ 0x7;
426 }
427 } else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) {
428 usexor = 1;
429 fgattr &= 0x7f;
430 }
Kevin O'Connorf864b602014-03-24 12:49:44 -0400431 int i;
432 for (i = 0; i < cheight; i++, op.y++) {
433 u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i));
434 if (usexor) {
435 op.op = GO_READ8;
436 handle_gfx_op(&op);
437 int j;
438 for (j = 0; j < 8; j++)
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400439 op.pixels[j] ^= (fontline & (0x80>>j)) ? fgattr : 0x00;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400440 } else {
441 int j;
442 for (j = 0; j < 8; j++)
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400443 op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400444 }
445 op.op = GO_WRITE8;
446 handle_gfx_op(&op);
447 }
448}
449
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500450// Read a character from the screen in graphics mode.
451static struct carattr
452gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp)
453{
454 u8 lines[16];
455 int cheight = GET_BDA(char_height);
456 if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines))
457 goto fail;
458
459 // Read cell from screen
460 struct gfx_op op;
461 init_gfx_op(&op, vmode_g);
462 op.op = GO_READ8;
463 op.x = cp.x * 8;
464 op.y = cp.y * cheight;
Kevin O'Connore638f972015-01-06 09:36:41 -0500465 int car = 0;
466 u8 fgattr = 0x00, bgattr = 0x00;
467 if (vga_emulate_text()) {
468 // Read bottom right pixel of the cell to guess bg color
469 op.y += cheight-1;
470 handle_gfx_op(&op);
471 op.y -= cheight-1;
472 bgattr = op.pixels[7];
473 fgattr = bgattr ^ 0x7;
474 // Report space character for blank cells (skip null character check)
475 car = 1;
476 }
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500477 u8 i, j;
478 for (i=0; i<cheight; i++, op.y++) {
479 u8 line = 0;
480 handle_gfx_op(&op);
481 for (j=0; j<8; j++)
Kevin O'Connore638f972015-01-06 09:36:41 -0500482 if (op.pixels[j] != bgattr) {
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500483 line |= 0x80 >> j;
Kevin O'Connore638f972015-01-06 09:36:41 -0500484 fgattr = op.pixels[j];
485 }
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500486 lines[i] = line;
487 }
488
489 // Determine font
Kevin O'Connore638f972015-01-06 09:36:41 -0500490 for (; car<256; car++) {
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500491 struct segoff_s font = get_font_data(car);
492 if (memcmp_far(GET_SEG(SS), lines
493 , font.seg, (void*)(font.offset+0), cheight) == 0)
Kevin O'Connore638f972015-01-06 09:36:41 -0500494 return (struct carattr){car, fgattr | (bgattr << 4), 0};
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500495 }
496fail:
497 return (struct carattr){0, 0, 0};
498}
499
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400500// Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell
501void
502gfx_set_swcursor(struct vgamode_s *vmode_g, int enable, struct cursorpos cp)
503{
504 u16 cursor_type = get_cursor_shape();
505 u8 start = cursor_type >> 8, end = cursor_type & 0xff;
506 struct gfx_op op;
507 init_gfx_op(&op, vmode_g);
508 op.x = cp.x * 8;
509 int cheight = GET_BDA(char_height);
510 op.y = cp.y * cheight + start;
511
512 int i;
513 for (i = start; i < cheight && i <= end; i++, op.y++) {
514 op.op = GO_READ8;
515 handle_gfx_op(&op);
516 int j;
517 for (j = 0; j < 8; j++)
518 op.pixels[j] ^= 0x07;
519 op.op = GO_WRITE8;
520 handle_gfx_op(&op);
521 }
522}
523
Kevin O'Connorf864b602014-03-24 12:49:44 -0400524// Set the pixel at the given position.
525void
526vgafb_write_pixel(u8 color, u16 x, u16 y)
527{
528 struct vgamode_s *vmode_g = get_current_mode();
529 if (!vmode_g)
530 return;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400531 vgafb_set_swcursor(0);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400532
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 int usexor = color & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
541 if (usexor)
542 op.pixels[x & 0x07] ^= color & 0x7f;
543 else
544 op.pixels[x & 0x07] = color;
545 op.op = GO_WRITE8;
546 handle_gfx_op(&op);
547}
548
549// Return the pixel at the given position.
550u8
551vgafb_read_pixel(u16 x, u16 y)
552{
553 struct vgamode_s *vmode_g = get_current_mode();
554 if (!vmode_g)
555 return 0;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400556 vgafb_set_swcursor(0);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400557
558 struct gfx_op op;
559 init_gfx_op(&op, vmode_g);
560 op.x = ALIGN_DOWN(x, 8);
561 op.y = y;
562 op.op = GO_READ8;
563 handle_gfx_op(&op);
564
565 return op.pixels[x & 0x07];
566}
567
568
569/****************************************************************
570 * Text ops
571 ****************************************************************/
572
Kevin O'Connora02d4182014-04-05 22:48:05 -0400573// Return the fb offset for the given character address when in text mode.
574void *
575text_address(struct cursorpos cp)
576{
577 int stride = GET_BDA(video_cols) * 2;
578 u32 pageoffset = GET_BDA(video_pagesize) * cp.page;
579 return (void*)pageoffset + cp.y * stride + cp.x * 2;
580}
581
Kevin O'Connorf864b602014-03-24 12:49:44 -0400582// Move characters on screen.
583void
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400584vgafb_move_chars(struct cursorpos dest
Kevin O'Connorf864b602014-03-24 12:49:44 -0400585 , struct cursorpos src, struct cursorpos movesize)
586{
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400587 struct vgamode_s *vmode_g = get_current_mode();
588 if (!vmode_g)
589 return;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400590 vgafb_set_swcursor(0);
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400591
Kevin O'Connorf864b602014-03-24 12:49:44 -0400592 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
593 gfx_move_chars(vmode_g, dest, src, movesize);
594 return;
595 }
596
Kevin O'Connora02d4182014-04-05 22:48:05 -0400597 int stride = GET_BDA(video_cols) * 2;
598 memmove_stride(GET_GLOBAL(vmode_g->sstart)
599 , text_address(dest), text_address(src)
600 , movesize.x * 2, stride, movesize.y);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400601}
602
Kevin O'Connore638f972015-01-06 09:36:41 -0500603// Clear area of screen.
Kevin O'Connorf864b602014-03-24 12:49:44 -0400604void
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400605vgafb_clear_chars(struct cursorpos dest
Kevin O'Connorf864b602014-03-24 12:49:44 -0400606 , struct carattr ca, struct cursorpos clearsize)
607{
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400608 struct vgamode_s *vmode_g = get_current_mode();
609 if (!vmode_g)
610 return;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400611 vgafb_set_swcursor(0);
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400612
Kevin O'Connorf864b602014-03-24 12:49:44 -0400613 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
614 gfx_clear_chars(vmode_g, dest, ca, clearsize);
615 return;
616 }
617
Kevin O'Connora02d4182014-04-05 22:48:05 -0400618 int stride = GET_BDA(video_cols) * 2;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400619 u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car;
Kevin O'Connora02d4182014-04-05 22:48:05 -0400620 memset16_stride(GET_GLOBAL(vmode_g->sstart), text_address(dest), attr
621 , clearsize.x * 2, stride, clearsize.y);
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;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400631 vgafb_set_swcursor(0);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400632
633 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
634 gfx_write_char(vmode_g, cp, ca);
635 return;
636 }
637
Kevin O'Connora02d4182014-04-05 22:48:05 -0400638 void *dest_far = text_address(cp);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400639 if (ca.use_attr) {
640 u16 dummy = (ca.attr << 8) | ca.car;
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500641 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)dest_far, dummy);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400642 } else {
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500643 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, ca.car);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400644 }
645}
646
Kevin O'Connorf864b602014-03-24 12:49:44 -0400647// Return the character at the given position on the screen.
Kevin O'Connor09262412009-05-25 11:44:11 -0400648struct carattr
Kevin O'Connord3b38152009-05-26 00:05:37 -0400649vgafb_read_char(struct cursorpos cp)
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400650{
Kevin O'Connor4a73f932012-01-21 11:08:35 -0500651 struct vgamode_s *vmode_g = get_current_mode();
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400652 if (!vmode_g)
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500653 return (struct carattr){0, 0, 0};
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400654 vgafb_set_swcursor(0);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400655
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500656 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT)
657 return gfx_read_char(vmode_g, cp);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400658
Kevin O'Connora02d4182014-04-05 22:48:05 -0400659 u16 *dest_far = text_address(cp);
660 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far);
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500661 return (struct carattr){v, v>>8, 0};
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400662}
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400663
664// Draw/undraw a cursor on the screen
665void
666vgafb_set_swcursor(int enable)
667{
668 if (!vga_emulate_text())
669 return;
670 u8 flags = GET_BDA_EXT(flags);
671 if (!!(flags & BF_SWCURSOR) == enable)
672 // Already in requested mode.
673 return;
674 struct vgamode_s *vmode_g = get_current_mode();
675 if (!vmode_g)
676 return;
677 struct cursorpos cp = get_cursor_pos(0xff);
678 if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows)
679 || GET_BDA(cursor_type) >= 0x2000)
680 // Cursor not visible
681 return;
682
683 SET_BDA_EXT(flags, (flags & ~BF_SWCURSOR) | (enable ? BF_SWCURSOR : 0));
684
685 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
686 gfx_set_swcursor(vmode_g, enable, cp);
687 return;
688 }
689
690 // In text mode, swap foreground and background attributes for cursor
691 void *dest_far = text_address(cp) + 1;
692 u8 attr = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far);
693 attr = (attr >> 4) | (attr << 4);
694 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, attr);
695}