blob: 1a94fcf704f5607252560eeb3254e6c4a094773b [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);
276 switch (op->op) {
277 default:
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400278 case GO_READ8: {
279 u8 data[64];
280 memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8);
281 int i;
282 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'Connor136d4ec2014-10-17 00:20:14 -0400285 }
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400286 case GO_WRITE8: {
287 u8 data[64];
288 int i;
289 for (i=0; i<8; i++)
290 *(u32*)&data[i*bypp] = get_color(depth, op->pixels[i]);
291 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
292 break;
293 }
294 case GO_MEMSET: {
295 u32 color = get_color(depth, op->pixels[0]);
296 u8 data[64];
297 int i;
298 for (i=0; i<8; i++)
299 *(u32*)&data[i*bypp] = color;
300 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
301 memcpy_high(dest_far + bypp * 8, dest_far, op->xlen * bypp - bypp * 8);
302 for (i=1; i < op->ylen; i++)
303 memcpy_high(dest_far + op->linelength * i
304 , dest_far, op->xlen * bypp);
305 break;
306 }
307 case GO_MEMMOVE: ;
308 void *src_far = (fb + op->displaystart + op->srcy * op->linelength
309 + op->x * bypp);
310 memmove_stride_high(dest_far, src_far
311 , op->xlen * bypp, op->linelength, op->ylen);
312 break;
313 }
314}
315
316
317/****************************************************************
Kevin O'Connorf864b602014-03-24 12:49:44 -0400318 * Gfx interface
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400319 ****************************************************************/
320
Kevin O'Connorf864b602014-03-24 12:49:44 -0400321// Prepare a struct gfx_op for use.
Kevin O'Connor7c790292014-02-11 15:34:58 -0500322void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400323init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g)
324{
325 memset(op, 0, sizeof(*op));
326 op->vmode_g = vmode_g;
327 op->linelength = vgahw_get_linelength(vmode_g);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400328 op->displaystart = vgahw_get_displaystart(vmode_g);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400329}
330
331// Issue a graphics operation.
Kevin O'Connor7c790292014-02-11 15:34:58 -0500332void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400333handle_gfx_op(struct gfx_op *op)
334{
335 switch (GET_GLOBAL(op->vmode_g->memmodel)) {
336 case MM_PLANAR:
337 gfx_planar(op);
338 break;
339 case MM_CGA:
340 gfx_cga(op);
341 break;
342 case MM_PACKED:
343 gfx_packed(op);
344 break;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400345 case MM_DIRECT:
346 gfx_direct(op);
347 break;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400348 default:
349 break;
350 }
351}
352
353// Move characters when in graphics mode.
354static void
355gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
356 , struct cursorpos src, struct cursorpos movesize)
357{
358 struct gfx_op op;
359 init_gfx_op(&op, vmode_g);
360 op.x = dest.x * 8;
361 op.xlen = movesize.x * 8;
362 int cheight = GET_BDA(char_height);
363 op.y = dest.y * cheight;
364 op.ylen = movesize.y * cheight;
365 op.srcy = src.y * cheight;
366 op.op = GO_MEMMOVE;
367 handle_gfx_op(&op);
368}
369
Kevin O'Connore638f972015-01-06 09:36:41 -0500370// Clear area of screen in graphics mode.
Kevin O'Connorf864b602014-03-24 12:49:44 -0400371static void
372gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest
373 , struct carattr ca, struct cursorpos clearsize)
374{
375 struct gfx_op op;
376 init_gfx_op(&op, vmode_g);
377 op.x = dest.x * 8;
378 op.xlen = clearsize.x * 8;
379 int cheight = GET_BDA(char_height);
380 op.y = dest.y * cheight;
381 op.ylen = clearsize.y * cheight;
382 op.pixels[0] = ca.attr;
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400383 if (vga_emulate_text())
384 op.pixels[0] = ca.attr >> 4;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400385 op.op = GO_MEMSET;
386 handle_gfx_op(&op);
387}
388
389// Return the font for a given character
390struct segoff_s
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500391get_font_data(u8 c)
392{
393 int char_height = GET_BDA(char_height);
394 struct segoff_s font;
395 if (char_height == 8 && c >= 128) {
396 font = GET_IVT(0x1f);
397 c -= 128;
398 } else {
399 font = GET_IVT(0x43);
400 }
401 font.offset += c * char_height;
402 return font;
403}
404
Kevin O'Connorf864b602014-03-24 12:49:44 -0400405// Write a character to the screen in graphics mode.
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400406static void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400407gfx_write_char(struct vgamode_s *vmode_g
Kevin O'Connord3b38152009-05-26 00:05:37 -0400408 , struct cursorpos cp, struct carattr ca)
409{
Kevin O'Connorf864b602014-03-24 12:49:44 -0400410 if (cp.x >= GET_BDA(video_cols))
411 return;
412
413 struct segoff_s font = get_font_data(ca.car);
414 struct gfx_op op;
415 init_gfx_op(&op, vmode_g);
416 op.x = cp.x * 8;
417 int cheight = GET_BDA(char_height);
418 op.y = cp.y * cheight;
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400419 u8 fgattr = ca.attr, bgattr = 0x00;
420 int usexor = 0;
421 if (vga_emulate_text()) {
422 if (ca.use_attr) {
423 bgattr = fgattr >> 4;
424 fgattr = fgattr & 0x0f;
425 } else {
426 // Read bottom right pixel of the cell to guess bg color
427 op.op = GO_READ8;
428 op.y += cheight-1;
429 handle_gfx_op(&op);
430 op.y -= cheight-1;
431 bgattr = op.pixels[7];
432 fgattr = bgattr ^ 0x7;
433 }
434 } else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) {
435 usexor = 1;
436 fgattr &= 0x7f;
437 }
Kevin O'Connorf864b602014-03-24 12:49:44 -0400438 int i;
439 for (i = 0; i < cheight; i++, op.y++) {
440 u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i));
441 if (usexor) {
442 op.op = GO_READ8;
443 handle_gfx_op(&op);
444 int j;
445 for (j = 0; j < 8; j++)
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400446 op.pixels[j] ^= (fontline & (0x80>>j)) ? fgattr : 0x00;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400447 } else {
448 int j;
449 for (j = 0; j < 8; j++)
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400450 op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400451 }
452 op.op = GO_WRITE8;
453 handle_gfx_op(&op);
454 }
455}
456
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500457// Read a character from the screen in graphics mode.
458static struct carattr
459gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp)
460{
461 u8 lines[16];
462 int cheight = GET_BDA(char_height);
463 if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines))
464 goto fail;
465
466 // Read cell from screen
467 struct gfx_op op;
468 init_gfx_op(&op, vmode_g);
469 op.op = GO_READ8;
470 op.x = cp.x * 8;
471 op.y = cp.y * cheight;
Kevin O'Connore638f972015-01-06 09:36:41 -0500472 int car = 0;
473 u8 fgattr = 0x00, bgattr = 0x00;
474 if (vga_emulate_text()) {
475 // Read bottom right pixel of the cell to guess bg color
476 op.y += cheight-1;
477 handle_gfx_op(&op);
478 op.y -= cheight-1;
479 bgattr = op.pixels[7];
480 fgattr = bgattr ^ 0x7;
481 // Report space character for blank cells (skip null character check)
482 car = 1;
483 }
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500484 u8 i, j;
485 for (i=0; i<cheight; i++, op.y++) {
486 u8 line = 0;
487 handle_gfx_op(&op);
488 for (j=0; j<8; j++)
Kevin O'Connore638f972015-01-06 09:36:41 -0500489 if (op.pixels[j] != bgattr) {
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500490 line |= 0x80 >> j;
Kevin O'Connore638f972015-01-06 09:36:41 -0500491 fgattr = op.pixels[j];
492 }
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500493 lines[i] = line;
494 }
495
496 // Determine font
Kevin O'Connore638f972015-01-06 09:36:41 -0500497 for (; car<256; car++) {
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500498 struct segoff_s font = get_font_data(car);
499 if (memcmp_far(GET_SEG(SS), lines
500 , font.seg, (void*)(font.offset+0), cheight) == 0)
Kevin O'Connore638f972015-01-06 09:36:41 -0500501 return (struct carattr){car, fgattr | (bgattr << 4), 0};
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500502 }
503fail:
504 return (struct carattr){0, 0, 0};
505}
506
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400507// Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell
508void
509gfx_set_swcursor(struct vgamode_s *vmode_g, int enable, struct cursorpos cp)
510{
511 u16 cursor_type = get_cursor_shape();
512 u8 start = cursor_type >> 8, end = cursor_type & 0xff;
513 struct gfx_op op;
514 init_gfx_op(&op, vmode_g);
515 op.x = cp.x * 8;
516 int cheight = GET_BDA(char_height);
517 op.y = cp.y * cheight + start;
518
519 int i;
520 for (i = start; i < cheight && i <= end; i++, op.y++) {
521 op.op = GO_READ8;
522 handle_gfx_op(&op);
523 int j;
524 for (j = 0; j < 8; j++)
525 op.pixels[j] ^= 0x07;
526 op.op = GO_WRITE8;
527 handle_gfx_op(&op);
528 }
529}
530
Kevin O'Connorf864b602014-03-24 12:49:44 -0400531// Set the pixel at the given position.
532void
533vgafb_write_pixel(u8 color, u16 x, u16 y)
534{
535 struct vgamode_s *vmode_g = get_current_mode();
536 if (!vmode_g)
537 return;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400538 vgafb_set_swcursor(0);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400539
540 struct gfx_op op;
541 init_gfx_op(&op, vmode_g);
542 op.x = ALIGN_DOWN(x, 8);
543 op.y = y;
544 op.op = GO_READ8;
545 handle_gfx_op(&op);
546
547 int usexor = color & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
548 if (usexor)
549 op.pixels[x & 0x07] ^= color & 0x7f;
550 else
551 op.pixels[x & 0x07] = color;
552 op.op = GO_WRITE8;
553 handle_gfx_op(&op);
554}
555
556// Return the pixel at the given position.
557u8
558vgafb_read_pixel(u16 x, u16 y)
559{
560 struct vgamode_s *vmode_g = get_current_mode();
561 if (!vmode_g)
562 return 0;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400563 vgafb_set_swcursor(0);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400564
565 struct gfx_op op;
566 init_gfx_op(&op, vmode_g);
567 op.x = ALIGN_DOWN(x, 8);
568 op.y = y;
569 op.op = GO_READ8;
570 handle_gfx_op(&op);
571
572 return op.pixels[x & 0x07];
573}
574
575
576/****************************************************************
577 * Text ops
578 ****************************************************************/
579
Kevin O'Connora02d4182014-04-05 22:48:05 -0400580// Return the fb offset for the given character address when in text mode.
581void *
582text_address(struct cursorpos cp)
583{
584 int stride = GET_BDA(video_cols) * 2;
585 u32 pageoffset = GET_BDA(video_pagesize) * cp.page;
586 return (void*)pageoffset + cp.y * stride + cp.x * 2;
587}
588
Kevin O'Connorf864b602014-03-24 12:49:44 -0400589// Move characters on screen.
590void
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400591vgafb_move_chars(struct cursorpos dest
Kevin O'Connorf864b602014-03-24 12:49:44 -0400592 , struct cursorpos src, struct cursorpos movesize)
593{
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400594 struct vgamode_s *vmode_g = get_current_mode();
595 if (!vmode_g)
596 return;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400597 vgafb_set_swcursor(0);
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400598
Kevin O'Connorf864b602014-03-24 12:49:44 -0400599 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
600 gfx_move_chars(vmode_g, dest, src, movesize);
601 return;
602 }
603
Kevin O'Connora02d4182014-04-05 22:48:05 -0400604 int stride = GET_BDA(video_cols) * 2;
605 memmove_stride(GET_GLOBAL(vmode_g->sstart)
606 , text_address(dest), text_address(src)
607 , movesize.x * 2, stride, movesize.y);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400608}
609
Kevin O'Connore638f972015-01-06 09:36:41 -0500610// Clear area of screen.
Kevin O'Connorf864b602014-03-24 12:49:44 -0400611void
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400612vgafb_clear_chars(struct cursorpos dest
Kevin O'Connorf864b602014-03-24 12:49:44 -0400613 , struct carattr ca, struct cursorpos clearsize)
614{
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400615 struct vgamode_s *vmode_g = get_current_mode();
616 if (!vmode_g)
617 return;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400618 vgafb_set_swcursor(0);
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400619
Kevin O'Connorf864b602014-03-24 12:49:44 -0400620 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
621 gfx_clear_chars(vmode_g, dest, ca, clearsize);
622 return;
623 }
624
Kevin O'Connora02d4182014-04-05 22:48:05 -0400625 int stride = GET_BDA(video_cols) * 2;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400626 u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car;
Kevin O'Connora02d4182014-04-05 22:48:05 -0400627 memset16_stride(GET_GLOBAL(vmode_g->sstart), text_address(dest), attr
628 , clearsize.x * 2, stride, clearsize.y);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400629}
630
631// Write a character to the screen.
632void
633vgafb_write_char(struct cursorpos cp, struct carattr ca)
634{
635 struct vgamode_s *vmode_g = get_current_mode();
636 if (!vmode_g)
637 return;
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400638 vgafb_set_swcursor(0);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400639
640 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
641 gfx_write_char(vmode_g, cp, ca);
642 return;
643 }
644
Kevin O'Connora02d4182014-04-05 22:48:05 -0400645 void *dest_far = text_address(cp);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400646 if (ca.use_attr) {
647 u16 dummy = (ca.attr << 8) | ca.car;
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500648 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)dest_far, dummy);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400649 } else {
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500650 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, ca.car);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400651 }
652}
653
Kevin O'Connorf864b602014-03-24 12:49:44 -0400654// Return the character at the given position on the screen.
Kevin O'Connor09262412009-05-25 11:44:11 -0400655struct carattr
Kevin O'Connord3b38152009-05-26 00:05:37 -0400656vgafb_read_char(struct cursorpos cp)
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400657{
Kevin O'Connor4a73f932012-01-21 11:08:35 -0500658 struct vgamode_s *vmode_g = get_current_mode();
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400659 if (!vmode_g)
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500660 return (struct carattr){0, 0, 0};
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400661 vgafb_set_swcursor(0);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400662
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500663 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT)
664 return gfx_read_char(vmode_g, cp);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400665
Kevin O'Connora02d4182014-04-05 22:48:05 -0400666 u16 *dest_far = text_address(cp);
667 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far);
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500668 return (struct carattr){v, v>>8, 0};
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400669}
Kevin O'Connorb4eb6fc2014-10-17 22:15:42 -0400670
671// Draw/undraw a cursor on the screen
672void
673vgafb_set_swcursor(int enable)
674{
675 if (!vga_emulate_text())
676 return;
677 u8 flags = GET_BDA_EXT(flags);
678 if (!!(flags & BF_SWCURSOR) == enable)
679 // Already in requested mode.
680 return;
681 struct vgamode_s *vmode_g = get_current_mode();
682 if (!vmode_g)
683 return;
684 struct cursorpos cp = get_cursor_pos(0xff);
685 if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows)
686 || GET_BDA(cursor_type) >= 0x2000)
687 // Cursor not visible
688 return;
689
690 SET_BDA_EXT(flags, (flags & ~BF_SWCURSOR) | (enable ? BF_SWCURSOR : 0));
691
692 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
693 gfx_set_swcursor(vmode_g, enable, cp);
694 return;
695 }
696
697 // In text mode, swap foreground and background attributes for cursor
698 void *dest_far = text_address(cp) + 1;
699 u8 attr = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far);
700 attr = (attr >> 4) | (attr << 4);
701 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, attr);
702}