blob: 46adfb5e06143d63eeb46ddb122bae2892c4c949 [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.
190static void
191memcpy_high(void *dest, void *src, u32 len)
192{
193 u64 gdt[6];
194 gdt[2] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)src);
195 gdt[3] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)dest);
196
197 // Call int 1587 to copy data.
198 len/=2;
199 u32 flags;
200 u32 eax = 0x8700;
201 u32 si = (u32)&gdt;
202 SET_SEG(ES, GET_SEG(SS));
203 asm volatile(
204 "stc\n"
205 "int $0x15\n"
206 "cli\n"
207 "cld\n"
208 "pushfl\n"
209 "popl %0\n"
210 : "=r" (flags), "+a" (eax), "+S" (si), "+c" (len)
211 : : "cc", "memory");
212}
213
214static void
215memmove_stride_high(void *dst, void *src, int copylen, int stride, int lines)
216{
217 if (src < dst) {
218 dst += stride * (lines - 1);
219 src += stride * (lines - 1);
220 stride = -stride;
221 }
222 for (; lines; lines--, dst+=stride, src+=stride)
223 memcpy_high(dst, src, copylen);
224}
225
226// Map a CGA color to a "direct" mode rgb value.
227static u32
228get_color(int depth, u8 attr)
229{
230 int rbits, gbits, bbits;
231 switch (depth) {
232 case 15: rbits=5; gbits=5; bbits=5; break;
233 case 16: rbits=5; gbits=6; bbits=5; break;
234 default:
235 case 24: rbits=8; gbits=8; bbits=8; break;
236 }
237 int h = (attr&8) ? 1 : 0;
238 int r = (attr&4) ? 2 : 0, g = (attr&2) ? 2 : 0, b = (attr&1) ? 2 : 0;
239 if ((attr & 0xf) == 6)
240 g = 1;
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400241 int rv = DIV_ROUND_CLOSEST(((1<<rbits) - 1) * (r + h), 3);
242 int gv = DIV_ROUND_CLOSEST(((1<<gbits) - 1) * (g + h), 3);
243 int bv = DIV_ROUND_CLOSEST(((1<<bbits) - 1) * (b + h), 3);
244 return (rv << (gbits+bbits)) + (gv << bbits) + bv;
245}
246
247// Find the closest attribute for a given framebuffer color
248static u8
249reverse_color(int depth, u32 color)
250{
251 int rbits, gbits, bbits;
252 switch (depth) {
253 case 15: rbits=5; gbits=5; bbits=5; break;
254 case 16: rbits=5; gbits=6; bbits=5; break;
255 default:
256 case 24: rbits=8; gbits=8; bbits=8; break;
257 }
258 int rv = (color >> (gbits+bbits)) & ((1<<rbits)-1);
259 int gv = (color >> bbits) & ((1<<gbits)-1);
260 int bv = color & ((1<<bbits)-1);
261 int r = DIV_ROUND_CLOSEST(rv * 3, (1<<rbits) - 1);
262 int g = DIV_ROUND_CLOSEST(gv * 3, (1<<gbits) - 1);
263 int b = DIV_ROUND_CLOSEST(bv * 3, (1<<bbits) - 1);
264 int h = r && g && b && (r != 2 || g != 2 || b != 2);
265 return (h ? 8 : 0) | ((r-h) ? 4 : 0) | ((g-h) ? 2 : 0) | ((b-h) ? 1 : 0);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400266}
267
268static void
269gfx_direct(struct gfx_op *op)
270{
271 void *fb = (void*)GET_GLOBAL(VBE_framebuffer);
272 if (!fb)
273 return;
274 int depth = GET_GLOBAL(op->vmode_g->depth);
275 int bypp = DIV_ROUND_UP(depth, 8);
276 void *dest_far = (fb + op->displaystart + op->y * op->linelength
277 + op->x * bypp);
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400278 u8 data[64];
279 int i;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400280 switch (op->op) {
281 default:
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400282 case GO_READ8:
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400283 memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8);
Kevin O'Connor136d4ec2014-10-17 00:20:14 -0400284 for (i=0; i<8; i++)
285 op->pixels[i] = reverse_color(depth, *(u32*)&data[i*bypp]);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400286 break;
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400287 case GO_WRITE8:
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400288 for (i=0; i<8; i++)
289 *(u32*)&data[i*bypp] = get_color(depth, op->pixels[i]);
290 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
291 break;
Kevin O'Connor33617ef2015-07-30 12:42:47 -0400292 case GO_MEMSET: ;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400293 u32 color = get_color(depth, op->pixels[0]);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400294 for (i=0; i<8; i++)
295 *(u32*)&data[i*bypp] = color;
296 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
297 memcpy_high(dest_far + bypp * 8, dest_far, op->xlen * bypp - bypp * 8);
298 for (i=1; i < op->ylen; i++)
299 memcpy_high(dest_far + op->linelength * i
300 , dest_far, op->xlen * bypp);
301 break;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400302 case GO_MEMMOVE: ;
303 void *src_far = (fb + op->displaystart + op->srcy * op->linelength
304 + op->x * bypp);
305 memmove_stride_high(dest_far, src_far
306 , op->xlen * bypp, op->linelength, op->ylen);
307 break;
308 }
309}
310
311
312/****************************************************************
Kevin O'Connorf864b602014-03-24 12:49:44 -0400313 * Gfx interface
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400314 ****************************************************************/
315
Kevin O'Connorf864b602014-03-24 12:49:44 -0400316// Prepare a struct gfx_op for use.
Kevin O'Connor7c790292014-02-11 15:34:58 -0500317void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400318init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g)
319{
320 memset(op, 0, sizeof(*op));
321 op->vmode_g = vmode_g;
322 op->linelength = vgahw_get_linelength(vmode_g);
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400323 op->displaystart = vgahw_get_displaystart(vmode_g);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400324}
325
326// Issue a graphics operation.
Kevin O'Connor7c790292014-02-11 15:34:58 -0500327void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400328handle_gfx_op(struct gfx_op *op)
329{
330 switch (GET_GLOBAL(op->vmode_g->memmodel)) {
331 case MM_PLANAR:
332 gfx_planar(op);
333 break;
334 case MM_CGA:
335 gfx_cga(op);
336 break;
337 case MM_PACKED:
338 gfx_packed(op);
339 break;
Kevin O'Connor098c2fc2014-04-05 17:11:06 -0400340 case MM_DIRECT:
341 gfx_direct(op);
342 break;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400343 default:
344 break;
345 }
346}
347
348// Move characters when in graphics mode.
349static void
350gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400351 , struct cursorpos movesize, int lines)
Kevin O'Connorf864b602014-03-24 12:49:44 -0400352{
353 struct gfx_op op;
354 init_gfx_op(&op, vmode_g);
355 op.x = dest.x * 8;
356 op.xlen = movesize.x * 8;
357 int cheight = GET_BDA(char_height);
358 op.y = dest.y * cheight;
359 op.ylen = movesize.y * cheight;
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400360 op.srcy = op.y + lines * cheight;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400361 op.op = GO_MEMMOVE;
362 handle_gfx_op(&op);
363}
364
Kevin O'Connore638f972015-01-06 09:36:41 -0500365// Clear area of screen in graphics mode.
Kevin O'Connorf864b602014-03-24 12:49:44 -0400366static void
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400367gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos win
368 , struct cursorpos winsize, struct carattr ca)
Kevin O'Connorf864b602014-03-24 12:49:44 -0400369{
370 struct gfx_op op;
371 init_gfx_op(&op, vmode_g);
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400372 op.x = win.x * 8;
373 op.xlen = winsize.x * 8;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400374 int cheight = GET_BDA(char_height);
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400375 op.y = win.y * cheight;
376 op.ylen = winsize.y * cheight;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400377 op.pixels[0] = ca.attr;
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400378 if (vga_emulate_text())
379 op.pixels[0] = ca.attr >> 4;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400380 op.op = GO_MEMSET;
381 handle_gfx_op(&op);
382}
383
384// Return the font for a given character
385struct segoff_s
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500386get_font_data(u8 c)
387{
388 int char_height = GET_BDA(char_height);
389 struct segoff_s font;
390 if (char_height == 8 && c >= 128) {
391 font = GET_IVT(0x1f);
392 c -= 128;
393 } else {
394 font = GET_IVT(0x43);
395 }
396 font.offset += c * char_height;
397 return font;
398}
399
Kevin O'Connorf864b602014-03-24 12:49:44 -0400400// Write a character to the screen in graphics mode.
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400401static void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400402gfx_write_char(struct vgamode_s *vmode_g
Kevin O'Connord3b38152009-05-26 00:05:37 -0400403 , struct cursorpos cp, struct carattr ca)
404{
Kevin O'Connorf864b602014-03-24 12:49:44 -0400405 if (cp.x >= GET_BDA(video_cols))
406 return;
407
408 struct segoff_s font = get_font_data(ca.car);
409 struct gfx_op op;
410 init_gfx_op(&op, vmode_g);
411 op.x = cp.x * 8;
412 int cheight = GET_BDA(char_height);
413 op.y = cp.y * cheight;
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400414 u8 fgattr = ca.attr, bgattr = 0x00;
415 int usexor = 0;
416 if (vga_emulate_text()) {
417 if (ca.use_attr) {
418 bgattr = fgattr >> 4;
419 fgattr = fgattr & 0x0f;
420 } else {
421 // Read bottom right pixel of the cell to guess bg color
422 op.op = GO_READ8;
423 op.y += cheight-1;
424 handle_gfx_op(&op);
425 op.y -= cheight-1;
426 bgattr = op.pixels[7];
427 fgattr = bgattr ^ 0x7;
428 }
429 } else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) {
430 usexor = 1;
431 fgattr &= 0x7f;
432 }
Kevin O'Connorf864b602014-03-24 12:49:44 -0400433 int i;
434 for (i = 0; i < cheight; i++, op.y++) {
435 u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i));
436 if (usexor) {
437 op.op = GO_READ8;
438 handle_gfx_op(&op);
439 int j;
440 for (j = 0; j < 8; j++)
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400441 op.pixels[j] ^= (fontline & (0x80>>j)) ? fgattr : 0x00;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400442 } else {
443 int j;
444 for (j = 0; j < 8; j++)
Kevin O'Connor6fed3072014-10-30 12:09:19 -0400445 op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400446 }
447 op.op = GO_WRITE8;
448 handle_gfx_op(&op);
449 }
450}
451
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500452// Read a character from the screen in graphics mode.
453static struct carattr
454gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp)
455{
456 u8 lines[16];
457 int cheight = GET_BDA(char_height);
458 if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines))
459 goto fail;
460
461 // Read cell from screen
462 struct gfx_op op;
463 init_gfx_op(&op, vmode_g);
464 op.op = GO_READ8;
465 op.x = cp.x * 8;
466 op.y = cp.y * cheight;
Kevin O'Connore638f972015-01-06 09:36:41 -0500467 int car = 0;
468 u8 fgattr = 0x00, bgattr = 0x00;
469 if (vga_emulate_text()) {
470 // Read bottom right pixel of the cell to guess bg color
471 op.y += cheight-1;
472 handle_gfx_op(&op);
473 op.y -= cheight-1;
474 bgattr = op.pixels[7];
475 fgattr = bgattr ^ 0x7;
476 // Report space character for blank cells (skip null character check)
477 car = 1;
478 }
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500479 u8 i, j;
480 for (i=0; i<cheight; i++, op.y++) {
481 u8 line = 0;
482 handle_gfx_op(&op);
483 for (j=0; j<8; j++)
Kevin O'Connore638f972015-01-06 09:36:41 -0500484 if (op.pixels[j] != bgattr) {
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500485 line |= 0x80 >> j;
Kevin O'Connore638f972015-01-06 09:36:41 -0500486 fgattr = op.pixels[j];
487 }
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500488 lines[i] = line;
489 }
490
491 // Determine font
Kevin O'Connore638f972015-01-06 09:36:41 -0500492 for (; car<256; car++) {
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500493 struct segoff_s font = get_font_data(car);
494 if (memcmp_far(GET_SEG(SS), lines
495 , font.seg, (void*)(font.offset+0), cheight) == 0)
Kevin O'Connore638f972015-01-06 09:36:41 -0500496 return (struct carattr){car, fgattr | (bgattr << 4), 0};
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500497 }
498fail:
499 return (struct carattr){0, 0, 0};
500}
501
Kevin O'Connorf864b602014-03-24 12:49:44 -0400502// Set the pixel at the given position.
503void
504vgafb_write_pixel(u8 color, u16 x, u16 y)
505{
506 struct vgamode_s *vmode_g = get_current_mode();
507 if (!vmode_g)
508 return;
509
510 struct gfx_op op;
511 init_gfx_op(&op, vmode_g);
512 op.x = ALIGN_DOWN(x, 8);
513 op.y = y;
514 op.op = GO_READ8;
515 handle_gfx_op(&op);
516
517 int usexor = color & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
518 if (usexor)
519 op.pixels[x & 0x07] ^= color & 0x7f;
520 else
521 op.pixels[x & 0x07] = color;
522 op.op = GO_WRITE8;
523 handle_gfx_op(&op);
524}
525
526// Return the pixel at the given position.
527u8
528vgafb_read_pixel(u16 x, u16 y)
529{
530 struct vgamode_s *vmode_g = get_current_mode();
531 if (!vmode_g)
532 return 0;
533
534 struct gfx_op op;
535 init_gfx_op(&op, vmode_g);
536 op.x = ALIGN_DOWN(x, 8);
537 op.y = y;
538 op.op = GO_READ8;
539 handle_gfx_op(&op);
540
541 return op.pixels[x & 0x07];
542}
543
544
545/****************************************************************
546 * Text ops
547 ****************************************************************/
548
Kevin O'Connora02d4182014-04-05 22:48:05 -0400549// Return the fb offset for the given character address when in text mode.
550void *
551text_address(struct cursorpos cp)
552{
553 int stride = GET_BDA(video_cols) * 2;
554 u32 pageoffset = GET_BDA(video_pagesize) * cp.page;
555 return (void*)pageoffset + cp.y * stride + cp.x * 2;
556}
557
Kevin O'Connorf864b602014-03-24 12:49:44 -0400558// Move characters on screen.
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400559static void
560vgafb_move_chars(struct cursorpos dest, struct cursorpos movesize, int lines)
Kevin O'Connorf864b602014-03-24 12:49:44 -0400561{
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400562 struct vgamode_s *vmode_g = get_current_mode();
563 if (!vmode_g)
564 return;
565
Kevin O'Connorf864b602014-03-24 12:49:44 -0400566 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400567 gfx_move_chars(vmode_g, dest, movesize, lines);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400568 return;
569 }
570
Kevin O'Connora02d4182014-04-05 22:48:05 -0400571 int stride = GET_BDA(video_cols) * 2;
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400572 void *dest_addr = text_address(dest), *src_addr = dest_addr + lines * stride;
573 memmove_stride(GET_GLOBAL(vmode_g->sstart), dest_addr, src_addr
Kevin O'Connora02d4182014-04-05 22:48:05 -0400574 , movesize.x * 2, stride, movesize.y);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400575}
576
Kevin O'Connore638f972015-01-06 09:36:41 -0500577// Clear area of screen.
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400578static void
579vgafb_clear_chars(struct cursorpos win, struct cursorpos winsize
580 , struct carattr ca)
Kevin O'Connorf864b602014-03-24 12:49:44 -0400581{
Kevin O'Connor7217ae72014-10-17 21:47:10 -0400582 struct vgamode_s *vmode_g = get_current_mode();
583 if (!vmode_g)
584 return;
585
Kevin O'Connorf864b602014-03-24 12:49:44 -0400586 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400587 gfx_clear_chars(vmode_g, win, winsize, ca);
Kevin O'Connorf864b602014-03-24 12:49:44 -0400588 return;
589 }
590
Kevin O'Connora02d4182014-04-05 22:48:05 -0400591 int stride = GET_BDA(video_cols) * 2;
Kevin O'Connorf864b602014-03-24 12:49:44 -0400592 u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car;
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400593 memset16_stride(GET_GLOBAL(vmode_g->sstart), text_address(win), attr
594 , winsize.x * 2, stride, winsize.y);
595}
596
597// Scroll characters within a window on the screen
598void
599vgafb_scroll(struct cursorpos win, struct cursorpos winsize
600 , int lines, struct carattr ca)
601{
Kevin O'Connor09e24ac2016-07-15 10:54:51 -0400602 if (!lines) {
603 // Clear window
604 vgafb_clear_chars(win, winsize, ca);
605 } else if (lines > 0) {
606 // Scroll the window up (eg, from page down key)
607 winsize.y -= lines;
608 vgafb_move_chars(win, winsize, lines);
609
610 win.y += winsize.y;
611 winsize.y = lines;
612 vgafb_clear_chars(win, winsize, ca);
613 } else {
614 // Scroll the window down (eg, from page up key)
615 win.y -= lines;
616 winsize.y += lines;
617 vgafb_move_chars(win, winsize, lines);
618
619 win.y += lines;
620 winsize.y = -lines;
621 vgafb_clear_chars(win, winsize, ca);
622 }
Kevin O'Connorf864b602014-03-24 12:49:44 -0400623}
624
625// Write a character to the screen.
626void
627vgafb_write_char(struct cursorpos cp, struct carattr ca)
628{
629 struct vgamode_s *vmode_g = get_current_mode();
630 if (!vmode_g)
631 return;
632
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'Connorc0c7df62009-05-17 18:11:33 -0400654
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500655 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT)
656 return gfx_read_char(vmode_g, cp);
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400657
Kevin O'Connora02d4182014-04-05 22:48:05 -0400658 u16 *dest_far = text_address(cp);
659 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far);
Paolo Bonzini60e0e552015-01-02 12:32:25 -0500660 return (struct carattr){v, v>>8, 0};
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400661}