blob: 3c6065ca180771b61d0d7362c67ffe5a7c74fd00 [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'Connorf864b602014-03-24 12:49:44 -0400184 * Gfx interface
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400185 ****************************************************************/
186
Kevin O'Connorf864b602014-03-24 12:49:44 -0400187// Prepare a struct gfx_op for use.
188static void
189init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g)
190{
191 memset(op, 0, sizeof(*op));
192 op->vmode_g = vmode_g;
193 op->linelength = vgahw_get_linelength(vmode_g);
194}
195
196// Issue a graphics operation.
197static void
198handle_gfx_op(struct gfx_op *op)
199{
200 switch (GET_GLOBAL(op->vmode_g->memmodel)) {
201 case MM_PLANAR:
202 gfx_planar(op);
203 break;
204 case MM_CGA:
205 gfx_cga(op);
206 break;
207 case MM_PACKED:
208 gfx_packed(op);
209 break;
210 default:
211 break;
212 }
213}
214
215// Move characters when in graphics mode.
216static void
217gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
218 , struct cursorpos src, struct cursorpos movesize)
219{
220 struct gfx_op op;
221 init_gfx_op(&op, vmode_g);
222 op.x = dest.x * 8;
223 op.xlen = movesize.x * 8;
224 int cheight = GET_BDA(char_height);
225 op.y = dest.y * cheight;
226 op.ylen = movesize.y * cheight;
227 op.srcy = src.y * cheight;
228 op.op = GO_MEMMOVE;
229 handle_gfx_op(&op);
230}
231
232// Clear are of screen in graphics mode.
233static void
234gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest
235 , struct carattr ca, struct cursorpos clearsize)
236{
237 struct gfx_op op;
238 init_gfx_op(&op, vmode_g);
239 op.x = dest.x * 8;
240 op.xlen = clearsize.x * 8;
241 int cheight = GET_BDA(char_height);
242 op.y = dest.y * cheight;
243 op.ylen = clearsize.y * cheight;
244 op.pixels[0] = ca.attr;
245 op.op = GO_MEMSET;
246 handle_gfx_op(&op);
247}
248
249// Return the font for a given character
250struct segoff_s
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500251get_font_data(u8 c)
252{
253 int char_height = GET_BDA(char_height);
254 struct segoff_s font;
255 if (char_height == 8 && c >= 128) {
256 font = GET_IVT(0x1f);
257 c -= 128;
258 } else {
259 font = GET_IVT(0x43);
260 }
261 font.offset += c * char_height;
262 return font;
263}
264
Kevin O'Connorf864b602014-03-24 12:49:44 -0400265// Write a character to the screen in graphics mode.
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400266static void
Kevin O'Connorf864b602014-03-24 12:49:44 -0400267gfx_write_char(struct vgamode_s *vmode_g
Kevin O'Connord3b38152009-05-26 00:05:37 -0400268 , struct cursorpos cp, struct carattr ca)
269{
Kevin O'Connorf864b602014-03-24 12:49:44 -0400270 if (cp.x >= GET_BDA(video_cols))
271 return;
272
273 struct segoff_s font = get_font_data(ca.car);
274 struct gfx_op op;
275 init_gfx_op(&op, vmode_g);
276 op.x = cp.x * 8;
277 int cheight = GET_BDA(char_height);
278 op.y = cp.y * cheight;
279 int usexor = ca.attr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
280 int i;
281 for (i = 0; i < cheight; i++, op.y++) {
282 u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i));
283 if (usexor) {
284 op.op = GO_READ8;
285 handle_gfx_op(&op);
286 int j;
287 for (j = 0; j < 8; j++)
288 op.pixels[j] ^= (fontline & (0x80>>j)) ? (ca.attr & 0x7f) : 0x00;
289 } else {
290 int j;
291 for (j = 0; j < 8; j++)
292 op.pixels[j] = (fontline & (0x80>>j)) ? ca.attr : 0x00;
293 }
294 op.op = GO_WRITE8;
295 handle_gfx_op(&op);
296 }
297}
298
299// Set the pixel at the given position.
300void
301vgafb_write_pixel(u8 color, u16 x, u16 y)
302{
303 struct vgamode_s *vmode_g = get_current_mode();
304 if (!vmode_g)
305 return;
306
307 struct gfx_op op;
308 init_gfx_op(&op, vmode_g);
309 op.x = ALIGN_DOWN(x, 8);
310 op.y = y;
311 op.op = GO_READ8;
312 handle_gfx_op(&op);
313
314 int usexor = color & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
315 if (usexor)
316 op.pixels[x & 0x07] ^= color & 0x7f;
317 else
318 op.pixels[x & 0x07] = color;
319 op.op = GO_WRITE8;
320 handle_gfx_op(&op);
321}
322
323// Return the pixel at the given position.
324u8
325vgafb_read_pixel(u16 x, u16 y)
326{
327 struct vgamode_s *vmode_g = get_current_mode();
328 if (!vmode_g)
329 return 0;
330
331 struct gfx_op op;
332 init_gfx_op(&op, vmode_g);
333 op.x = ALIGN_DOWN(x, 8);
334 op.y = y;
335 op.op = GO_READ8;
336 handle_gfx_op(&op);
337
338 return op.pixels[x & 0x07];
339}
340
341
342/****************************************************************
343 * Text ops
344 ****************************************************************/
345
346// Move characters on screen.
347void
348vgafb_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
349 , struct cursorpos src, struct cursorpos movesize)
350{
351 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
352 gfx_move_chars(vmode_g, dest, src, movesize);
353 return;
354 }
355
356 int cheight = 1;
357 int cwidth = 2;
358 int stride = GET_BDA(video_cols) * cwidth;
359 void *dest_far = (void*)(dest.y * cheight * stride + dest.x * cwidth);
360 void *src_far = (void*)(src.y * cheight * stride + src.x * cwidth);
361 u32 pageoffset = GET_BDA(video_pagesize) * dest.page;
362 u16 seg = GET_GLOBAL(vmode_g->sstart);
363 memmove_stride(seg, dest_far + pageoffset, src_far + pageoffset
364 , movesize.x * cwidth, stride, movesize.y * cheight);
365}
366
367// Clear are of screen.
368void
369vgafb_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest
370 , struct carattr ca, struct cursorpos clearsize)
371{
372 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
373 gfx_clear_chars(vmode_g, dest, ca, clearsize);
374 return;
375 }
376
377 int cheight = 1;
378 int cwidth = 2;
379 int stride = GET_BDA(video_cols) * cwidth;
380 void *dest_far = (void*)(dest.y * cheight * stride + dest.x * cwidth);
381 u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car;
382 u32 pageoffset = GET_BDA(video_pagesize) * dest.page;
383 u16 seg = GET_GLOBAL(vmode_g->sstart);
384 memset16_stride(seg, dest_far + pageoffset, attr
385 , clearsize.x * cwidth, stride, clearsize.y * cheight);
386}
387
388// Write a character to the screen.
389void
390vgafb_write_char(struct cursorpos cp, struct carattr ca)
391{
392 struct vgamode_s *vmode_g = get_current_mode();
393 if (!vmode_g)
394 return;
395
396 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
397 gfx_write_char(vmode_g, cp, ca);
398 return;
399 }
400
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500401 int cheight = 1;
402 int cwidth = 2;
403 int stride = GET_BDA(video_cols) * cwidth;
404 int addr = cp.y * cheight * stride + cp.x * cwidth;
405 void *dest_far = (void*)(GET_BDA(video_pagesize) * cp.page + addr);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400406 if (ca.use_attr) {
407 u16 dummy = (ca.attr << 8) | ca.car;
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500408 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)dest_far, dummy);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400409 } else {
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500410 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, ca.car);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400411 }
412}
413
Kevin O'Connorf864b602014-03-24 12:49:44 -0400414// Return the character at the given position on the screen.
Kevin O'Connor09262412009-05-25 11:44:11 -0400415struct carattr
Kevin O'Connord3b38152009-05-26 00:05:37 -0400416vgafb_read_char(struct cursorpos cp)
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400417{
Kevin O'Connor4a73f932012-01-21 11:08:35 -0500418 struct vgamode_s *vmode_g = get_current_mode();
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400419 if (!vmode_g)
Kevin O'Connor09262412009-05-25 11:44:11 -0400420 goto fail;
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400421
Kevin O'Connord4398ad2012-01-01 12:32:53 -0500422 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
Kevin O'Connord3b38152009-05-26 00:05:37 -0400423 // FIXME gfx mode
424 dprintf(1, "Read char in graphics mode\n");
425 goto fail;
426 }
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400427
Kevin O'Connor1f31f002013-11-30 10:52:45 -0500428 int cheight = 1;
429 int cwidth = 2;
430 int stride = GET_BDA(video_cols) * cwidth;
431 int addr = cp.y * cheight * stride + cp.x * cwidth;
432 u16 *src_far = (void*)(GET_BDA(video_pagesize) * cp.page + addr);
433 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *src_far);
Kevin O'Connord3b38152009-05-26 00:05:37 -0400434 struct carattr ca = {v, v>>8, 0};
Kevin O'Connor09262412009-05-25 11:44:11 -0400435 return ca;
Kevin O'Connord3b38152009-05-26 00:05:37 -0400436
437fail: ;
438 struct carattr ca2 = {0, 0, 0};
439 return ca2;
Kevin O'Connorc0c7df62009-05-17 18:11:33 -0400440}