VGA: Inline several functions in vga.c.
[seabios.git] / vgasrc / vga.c
1 // VGA bios implementation
2 //
3 // Copyright (C) 2009  Kevin O'Connor <kevin@koconnor.net>
4 // 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
9 // TODO:
10 //  * review correctness of converted asm by comparing with RBIL
11 //  * refactor redundant code into sub-functions
12 //  * See if there is a method to the in/out stuff that can be encapsulated.
13 //  * remove "biosfn" prefixes
14 //  * verify all funcs static
15 //
16 //  * convert vbe/clext code
17 //
18 //  * extract hw code from bios interfaces
19
20 #include "bregs.h" // struct bregs
21 #include "biosvar.h" // GET_BDA
22 #include "util.h" // memset
23 #include "vgatables.h" // find_vga_entry
24
25 // XXX
26 #define CONFIG_VBE 0
27 #define CONFIG_CIRRUS 0
28
29 // XXX
30 #define DEBUG_VGA_POST 1
31 #define DEBUG_VGA_10 3
32
33 #define SET_VGA(var, val) SET_FARVAR(get_global_seg(), (var), (val))
34
35
36 // ===================================================================
37 //
38 // Video Utils
39 //
40 // ===================================================================
41
42 // -------------------------------------------------------------------
43 inline void
44 call16_vgaint(u32 eax, u32 ebx)
45 {
46     asm volatile(
47         "int $0x10\n"
48         "cli\n"
49         "cld"
50         :
51         : "a"(eax), "b"(ebx)
52         : "cc", "memory");
53 }
54
55
56 // ===================================================================
57 //
58 // BIOS functions
59 //
60 // ===================================================================
61
62 // -------------------------------------------------------------------
63 static void
64 biosfn_perform_gray_scale_summing(u16 start, u16 count)
65 {
66     vgahw_screen_disable();
67     int i;
68     for (i = start; i < start+count; i++) {
69         u8 rgb[3];
70         vgahw_get_dac_regs(GET_SEG(SS), rgb, i, 1);
71
72         // intensity = ( 0.3 * Red ) + ( 0.59 * Green ) + ( 0.11 * Blue )
73         u16 intensity = ((77 * rgb[0] + 151 * rgb[1] + 28 * rgb[2]) + 0x80) >> 8;
74         if (intensity > 0x3f)
75             intensity = 0x3f;
76
77         vgahw_set_dac_regs(GET_SEG(SS), rgb, i, 1);
78     }
79     vgahw_screen_enable();
80 }
81
82 // -------------------------------------------------------------------
83 static void
84 biosfn_set_cursor_shape(u8 CH, u8 CL)
85 {
86     CH &= 0x3f;
87     CL &= 0x1f;
88
89     u16 curs = (CH << 8) + CL;
90     SET_BDA(cursor_type, curs);
91
92     u8 modeset_ctl = GET_BDA(modeset_ctl);
93     u16 cheight = GET_BDA(char_height);
94     if ((modeset_ctl & 0x01) && (cheight > 8) && (CL < 8) && (CH < 0x20)) {
95         if (CL != (CH + 1))
96             CH = ((CH + 1) * cheight / 8) - 1;
97         else
98             CH = ((CL + 1) * cheight / 8) - 2;
99         CL = ((CL + 1) * cheight / 8) - 1;
100     }
101     vgahw_set_cursor_shape(CH, CL);
102 }
103
104 static u16
105 biosfn_get_cursor_shape(u8 page)
106 {
107     if (page > 7)
108         return 0;
109     // FIXME should handle VGA 14/16 lines
110     return GET_BDA(cursor_type);
111 }
112
113 // -------------------------------------------------------------------
114 static void
115 biosfn_set_cursor_pos(u8 page, u16 cursor)
116 {
117     // Should not happen...
118     if (page > 7)
119         return;
120
121     // Bios cursor pos
122     SET_BDA(cursor_pos[page], cursor);
123
124     // Set the hardware cursor
125     u8 current = GET_BDA(video_page);
126     if (page != current)
127         return;
128
129     // Get the dimensions
130     u16 nbcols = GET_BDA(video_cols);
131     u16 nbrows = GET_BDA(video_rows) + 1;
132
133     u8 xcurs = cursor & 0x00ff;
134     u8 ycurs = (cursor & 0xff00) >> 8;
135
136     // Calculate the address knowing nbcols nbrows and page num
137     u16 address = SCREEN_IO_START(nbcols, nbrows, page) + xcurs + ycurs * nbcols;
138
139     vgahw_set_cursor_pos(address);
140 }
141
142 u16
143 biosfn_get_cursor_pos(u8 page)
144 {
145     if (page > 7)
146         return 0;
147     // FIXME should handle VGA 14/16 lines
148     return GET_BDA(cursor_pos[page]);
149 }
150
151 // -------------------------------------------------------------------
152 static void
153 biosfn_set_active_page(u8 page)
154 {
155     if (page > 7)
156         return;
157
158     // Get the mode
159     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
160     if (!vmode_g)
161         return;
162
163     // Get pos curs pos for the right page
164     u16 cursor = biosfn_get_cursor_pos(page);
165
166     u16 address;
167     if (GET_GLOBAL(vmode_g->class) == TEXT) {
168         // Get the dimensions
169         u16 nbcols = GET_BDA(video_cols);
170         u16 nbrows = GET_BDA(video_rows) + 1;
171
172         // Calculate the address knowing nbcols nbrows and page num
173         address = SCREEN_MEM_START(nbcols, nbrows, page);
174         SET_BDA(video_pagestart, address);
175
176         // Start address
177         address = SCREEN_IO_START(nbcols, nbrows, page);
178     } else {
179         struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
180         address = page * GET_GLOBAL(vparam_g->slength);
181     }
182
183     vgahw_set_active_page(address);
184
185     // And change the BIOS page
186     SET_BDA(video_page, page);
187
188     dprintf(1, "Set active page %02x address %04x\n", page, address);
189
190     // Display the cursor, now the page is active
191     biosfn_set_cursor_pos(page, cursor);
192 }
193
194 static void
195 biosfn_set_video_mode(u8 mode)
196 {                               // mode: Bit 7 is 1 if no clear screen
197     if (CONFIG_CIRRUS)
198         cirrus_set_video_mode(mode);
199
200     if (CONFIG_VBE)
201         if (vbe_has_vbe_display())
202             dispi_set_enable(VBE_DISPI_DISABLED);
203
204     // The real mode
205     u8 noclearmem = mode & 0x80;
206     mode = mode & 0x7f;
207
208     // find the entry in the video modes
209     struct vgamode_s *vmode_g = find_vga_entry(mode);
210     dprintf(1, "mode search %02x found %p\n", mode, vmode_g);
211     if (!vmode_g)
212         return;
213
214     struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
215     u16 twidth = GET_GLOBAL(vparam_g->twidth);
216     u16 theightm1 = GET_GLOBAL(vparam_g->theightm1);
217     u16 cheight = GET_GLOBAL(vparam_g->cheight);
218
219     // Read the bios mode set control
220     u8 modeset_ctl = GET_BDA(modeset_ctl);
221
222     // Then we know the number of lines
223 // FIXME
224
225     // if palette loading (bit 3 of modeset ctl = 0)
226     if ((modeset_ctl & 0x08) == 0) {    // Set the PEL mask
227         outb(GET_GLOBAL(vmode_g->pelmask), VGAREG_PEL_MASK);
228
229         // Set the whole dac always, from 0
230         outb(0x00, VGAREG_DAC_WRITE_ADDRESS);
231
232         // From which palette
233         u8 *palette_g = GET_GLOBAL(vmode_g->dac);
234         u16 palsize = GET_GLOBAL(vmode_g->dacsize) / 3;
235         // Always 256*3 values
236         u16 i;
237         for (i = 0; i < 0x0100; i++) {
238             if (i < palsize) {
239                 outb(GET_GLOBAL(palette_g[(i * 3) + 0]), VGAREG_DAC_DATA);
240                 outb(GET_GLOBAL(palette_g[(i * 3) + 1]), VGAREG_DAC_DATA);
241                 outb(GET_GLOBAL(palette_g[(i * 3) + 2]), VGAREG_DAC_DATA);
242             } else {
243                 outb(0, VGAREG_DAC_DATA);
244                 outb(0, VGAREG_DAC_DATA);
245                 outb(0, VGAREG_DAC_DATA);
246             }
247         }
248         if ((modeset_ctl & 0x02) == 0x02)
249             biosfn_perform_gray_scale_summing(0x00, 0x100);
250     }
251     // Reset Attribute Ctl flip-flop
252     inb(VGAREG_ACTL_RESET);
253
254     // Set Attribute Ctl
255     u16 i;
256     for (i = 0; i <= 0x13; i++) {
257         outb(i, VGAREG_ACTL_ADDRESS);
258         outb(GET_GLOBAL(vparam_g->actl_regs[i]), VGAREG_ACTL_WRITE_DATA);
259     }
260     outb(0x14, VGAREG_ACTL_ADDRESS);
261     outb(0x00, VGAREG_ACTL_WRITE_DATA);
262
263     // Set Sequencer Ctl
264     outb(0, VGAREG_SEQU_ADDRESS);
265     outb(0x03, VGAREG_SEQU_DATA);
266     for (i = 1; i <= 4; i++) {
267         outb(i, VGAREG_SEQU_ADDRESS);
268         outb(GET_GLOBAL(vparam_g->sequ_regs[i - 1]), VGAREG_SEQU_DATA);
269     }
270
271     // Set Grafx Ctl
272     for (i = 0; i <= 8; i++) {
273         outb(i, VGAREG_GRDC_ADDRESS);
274         outb(GET_GLOBAL(vparam_g->grdc_regs[i]), VGAREG_GRDC_DATA);
275     }
276
277     // Set CRTC address VGA or MDA
278     u16 crtc_addr = VGAREG_VGA_CRTC_ADDRESS;
279     if (GET_GLOBAL(vmode_g->memmodel) == MTEXT)
280         crtc_addr = VGAREG_MDA_CRTC_ADDRESS;
281
282     // Disable CRTC write protection
283     outw(0x0011, crtc_addr);
284     // Set CRTC regs
285     for (i = 0; i <= 0x18; i++) {
286         outb(i, crtc_addr);
287         outb(GET_GLOBAL(vparam_g->crtc_regs[i]), crtc_addr + 1);
288     }
289
290     // Set the misc register
291     outb(GET_GLOBAL(vparam_g->miscreg), VGAREG_WRITE_MISC_OUTPUT);
292
293     // Enable video
294     outb(0x20, VGAREG_ACTL_ADDRESS);
295     inb(VGAREG_ACTL_RESET);
296
297     if (noclearmem == 0x00) {
298         if (GET_GLOBAL(vmode_g->class) == TEXT) {
299             memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
300         } else {
301             if (mode < 0x0d) {
302                 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
303             } else {
304                 outb(0x02, VGAREG_SEQU_ADDRESS);
305                 u8 mmask = inb(VGAREG_SEQU_DATA);
306                 outb(0x0f, VGAREG_SEQU_DATA);   // all planes
307                 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
308                 outb(mmask, VGAREG_SEQU_DATA);
309             }
310         }
311     }
312     // Set the BIOS mem
313     SET_BDA(video_mode, mode);
314     SET_BDA(video_cols, twidth);
315     SET_BDA(video_pagesize, GET_GLOBAL(vparam_g->slength));
316     SET_BDA(crtc_address, crtc_addr);
317     SET_BDA(video_rows, theightm1);
318     SET_BDA(char_height, cheight);
319     SET_BDA(video_ctl, (0x60 | noclearmem));
320     SET_BDA(video_switches, 0xF9);
321     SET_BDA(modeset_ctl, GET_BDA(modeset_ctl) & 0x7f);
322
323     // FIXME We nearly have the good tables. to be reworked
324     SET_BDA(dcc_index, 0x08);   // 8 is VGA should be ok for now
325     SET_BDA(video_savetable_ptr, (u32)video_save_pointer_table);
326     SET_BDA(video_savetable_seg, get_global_seg());
327
328     // FIXME
329     SET_BDA(video_msr, 0x00); // Unavailable on vanilla vga, but...
330     SET_BDA(video_pal, 0x00); // Unavailable on vanilla vga, but...
331
332     // Set cursor shape
333     if (GET_GLOBAL(vmode_g->class) == TEXT)
334         biosfn_set_cursor_shape(0x06, 0x07);
335     // Set cursor pos for page 0..7
336     for (i = 0; i < 8; i++)
337         biosfn_set_cursor_pos(i, 0x0000);
338
339     // Set active page 0
340     biosfn_set_active_page(0x00);
341
342     // Write the fonts in memory
343     if (GET_GLOBAL(vmode_g->class) == TEXT) {
344         call16_vgaint(0x1104, 0);
345         call16_vgaint(0x1103, 0);
346     }
347     // Set the ints 0x1F and 0x43
348     SET_IVT(0x1f, get_global_seg(), (u32)&vgafont8[128 * 8]);
349
350     switch (cheight) {
351     case 8:
352         SET_IVT(0x43, get_global_seg(), (u32)vgafont8);
353         break;
354     case 14:
355         SET_IVT(0x43, get_global_seg(), (u32)vgafont14);
356         break;
357     case 16:
358         SET_IVT(0x43, get_global_seg(), (u32)vgafont16);
359         break;
360     }
361 }
362
363 // -------------------------------------------------------------------
364 static void
365 biosfn_write_teletype(u8 car, u8 page, u8 attr, u8 flag)
366 {                               // flag = WITH_ATTR / NO_ATTR
367     // special case if page is 0xff, use current page
368     if (page == 0xff)
369         page = GET_BDA(video_page);
370
371     // Get the mode
372     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
373     if (!vmode_g)
374         return;
375
376     // Get the cursor pos for the page
377     u16 cursor = biosfn_get_cursor_pos(page);
378     u8 xcurs = cursor & 0x00ff;
379     u8 ycurs = (cursor & 0xff00) >> 8;
380
381     // Get the dimensions
382     u16 nbrows = GET_BDA(video_rows) + 1;
383     u16 nbcols = GET_BDA(video_cols);
384
385     switch (car) {
386     case 7:
387         //FIXME should beep
388         break;
389
390     case 8:
391         if (xcurs > 0)
392             xcurs--;
393         break;
394
395     case '\r':
396         xcurs = 0;
397         break;
398
399     case '\n':
400         ycurs++;
401         break;
402
403     case '\t':
404         do {
405             biosfn_write_teletype(' ', page, attr, flag);
406             cursor = biosfn_get_cursor_pos(page);
407             xcurs = cursor & 0x00ff;
408             ycurs = (cursor & 0xff00) >> 8;
409         } while (xcurs % 8 == 0);
410         break;
411
412     default:
413         if (flag == WITH_ATTR)
414             biosfn_write_char_attr(car, page, attr, 1);
415         else
416             biosfn_write_char_only(car, page, attr, 1);
417         xcurs++;
418     }
419
420     // Do we need to wrap ?
421     if (xcurs == nbcols) {
422         xcurs = 0;
423         ycurs++;
424     }
425     // Do we need to scroll ?
426     if (ycurs == nbrows) {
427         if (GET_GLOBAL(vmode_g->class) == TEXT)
428             biosfn_scroll(0x01, 0x07, 0, 0, nbrows - 1, nbcols - 1, page,
429                           SCROLL_UP);
430         else
431             biosfn_scroll(0x01, 0x00, 0, 0, nbrows - 1, nbcols - 1, page,
432                           SCROLL_UP);
433         ycurs -= 1;
434     }
435     // Set the cursor for the page
436     cursor = ycurs;
437     cursor <<= 8;
438     cursor += xcurs;
439     biosfn_set_cursor_pos(page, cursor);
440 }
441
442 static void
443 biosfn_write_string(u8 flag, u8 page, u8 attr, u16 count, u8 row, u8 col,
444                     u16 seg, u8 *offset_far)
445 {
446     // Read curs info for the page
447     u16 oldcurs = biosfn_get_cursor_pos(page);
448
449     // if row=0xff special case : use current cursor position
450     if (row == 0xff) {
451         col = oldcurs & 0x00ff;
452         row = (oldcurs & 0xff00) >> 8;
453     }
454
455     u16 newcurs = row;
456     newcurs <<= 8;
457     newcurs += col;
458     biosfn_set_cursor_pos(page, newcurs);
459
460     while (count-- != 0) {
461         u8 car = GET_FARVAR(seg, *offset_far);
462         offset_far++;
463         if ((flag & 0x02) != 0) {
464             attr = GET_FARVAR(seg, *offset_far);
465             offset_far++;
466         }
467
468         biosfn_write_teletype(car, page, attr, WITH_ATTR);
469     }
470
471     // Set back curs pos
472     if ((flag & 0x01) == 0)
473         biosfn_set_cursor_pos(page, oldcurs);
474 }
475
476 static void
477 set_scan_lines(u8 lines)
478 {
479     vgahw_set_scan_lines(lines);
480     if (lines == 8)
481         biosfn_set_cursor_shape(0x06, 0x07);
482     else
483         biosfn_set_cursor_shape(lines - 4, lines - 3);
484     SET_BDA(char_height, lines);
485     u16 vde = vgahw_get_vde();
486     u8 rows = vde / lines;
487     SET_BDA(video_rows, rows - 1);
488     u16 cols = GET_BDA(video_cols);
489     SET_BDA(video_pagesize, rows * cols * 2);
490 }
491
492 static void
493 biosfn_save_bda_state(u16 seg, struct saveBDAstate *info)
494 {
495     SET_FARVAR(seg, info->video_mode, GET_BDA(video_mode));
496     SET_FARVAR(seg, info->video_cols, GET_BDA(video_cols));
497     SET_FARVAR(seg, info->video_pagesize, GET_BDA(video_pagesize));
498     SET_FARVAR(seg, info->crtc_address, GET_BDA(crtc_address));
499     SET_FARVAR(seg, info->video_rows, GET_BDA(video_rows));
500     SET_FARVAR(seg, info->char_height, GET_BDA(char_height));
501     SET_FARVAR(seg, info->video_ctl, GET_BDA(video_ctl));
502     SET_FARVAR(seg, info->video_switches, GET_BDA(video_switches));
503     SET_FARVAR(seg, info->modeset_ctl, GET_BDA(modeset_ctl));
504     SET_FARVAR(seg, info->cursor_type, GET_BDA(cursor_type));
505     u16 i;
506     for (i=0; i<8; i++)
507         SET_FARVAR(seg, info->cursor_pos[i], GET_BDA(cursor_pos[i]));
508     SET_FARVAR(seg, info->video_pagestart, GET_BDA(video_pagestart));
509     SET_FARVAR(seg, info->video_page, GET_BDA(video_page));
510     /* current font */
511     SET_FARVAR(seg, *(u32*)&info->font0_off, GET_IVT(0x1f).segoff);
512     SET_FARVAR(seg, *(u32*)&info->font1_off, GET_IVT(0x43).segoff);
513 }
514
515 static void
516 biosfn_restore_bda_state(u16 seg, struct saveBDAstate *info)
517 {
518     SET_BDA(video_mode, GET_FARVAR(seg, info->video_mode));
519     SET_BDA(video_cols, GET_FARVAR(seg, info->video_cols));
520     SET_BDA(video_pagesize, GET_FARVAR(seg, info->video_pagesize));
521     SET_BDA(crtc_address, GET_FARVAR(seg, info->crtc_address));
522     SET_BDA(video_rows, GET_FARVAR(seg, info->video_rows));
523     SET_BDA(char_height, GET_FARVAR(seg, info->char_height));
524     SET_BDA(video_ctl, GET_FARVAR(seg, info->video_ctl));
525     SET_BDA(video_switches, GET_FARVAR(seg, info->video_switches));
526     SET_BDA(modeset_ctl, GET_FARVAR(seg, info->modeset_ctl));
527     SET_BDA(cursor_type, GET_FARVAR(seg, info->cursor_type));
528     u16 i;
529     for (i = 0; i < 8; i++)
530         SET_BDA(cursor_pos[i], GET_FARVAR(seg, info->cursor_pos[i]));
531     SET_BDA(video_pagestart, GET_FARVAR(seg, info->video_pagestart));
532     SET_BDA(video_page, GET_FARVAR(seg, info->video_page));
533     /* current font */
534     SET_IVT(0x1f, GET_FARVAR(seg, info->font0_seg)
535             , GET_FARVAR(seg, info->font0_off));
536     SET_IVT(0x43, GET_FARVAR(seg, info->font1_seg)
537             , GET_FARVAR(seg, info->font1_off));
538 }
539
540
541 /****************************************************************
542  * VGA int 10 handler
543  ****************************************************************/
544
545 static void
546 handle_1000(struct bregs *regs)
547 {
548     // XXX - inline
549     biosfn_set_video_mode(regs->al);
550     switch(regs->al & 0x7F) {
551     case 6:
552         regs->al = 0x3F;
553         break;
554     case 0:
555     case 1:
556     case 2:
557     case 3:
558     case 4:
559     case 5:
560     case 7:
561         regs->al = 0x30;
562         break;
563     default:
564         regs->al = 0x20;
565     }
566 }
567
568 static void
569 handle_1001(struct bregs *regs)
570 {
571     biosfn_set_cursor_shape(regs->ch, regs->cl);
572 }
573
574 static void
575 handle_1002(struct bregs *regs)
576 {
577     biosfn_set_cursor_pos(regs->bh, regs->dx);
578 }
579
580 static void
581 handle_1003(struct bregs *regs)
582 {
583     regs->cx = biosfn_get_cursor_shape(regs->bh);
584     regs->dx = biosfn_get_cursor_pos(regs->bh);
585 }
586
587 // Read light pen pos (unimplemented)
588 static void
589 handle_1004(struct bregs *regs)
590 {
591     debug_stub(regs);
592     regs->ax = regs->bx = regs->cx = regs->dx = 0;
593 }
594
595 static void
596 handle_1005(struct bregs *regs)
597 {
598     biosfn_set_active_page(regs->al);
599 }
600
601 static void
602 handle_1006(struct bregs *regs)
603 {
604     biosfn_scroll(regs->al, regs->bh, regs->ch, regs->cl, regs->dh, regs->dl
605                   , 0xFF, SCROLL_UP);
606 }
607
608 static void
609 handle_1007(struct bregs *regs)
610 {
611     biosfn_scroll(regs->al, regs->bh, regs->ch, regs->cl, regs->dh, regs->dl
612                   , 0xFF, SCROLL_DOWN);
613 }
614
615 static void
616 handle_1008(struct bregs *regs)
617 {
618     // XXX - inline
619     biosfn_read_char_attr(regs->bh, &regs->ax);
620 }
621
622 static void
623 handle_1009(struct bregs *regs)
624 {
625     // XXX - inline
626     biosfn_write_char_attr(regs->al, regs->bh, regs->bl, regs->cx);
627 }
628
629 static void
630 handle_100a(struct bregs *regs)
631 {
632     // XXX - inline
633     biosfn_write_char_only(regs->al, regs->bh, regs->bl, regs->cx);
634 }
635
636
637 static void
638 handle_100b00(struct bregs *regs)
639 {
640     vgahw_set_border_color(regs->bl);
641 }
642
643 static void
644 handle_100b01(struct bregs *regs)
645 {
646     vgahw_set_palette(regs->bl);
647 }
648
649 static void
650 handle_100bXX(struct bregs *regs)
651 {
652     debug_stub(regs);
653 }
654
655 static void
656 handle_100b(struct bregs *regs)
657 {
658     switch (regs->bh) {
659     case 0x00: handle_100b00(regs); break;
660     case 0x01: handle_100b01(regs); break;
661     default:   handle_100bXX(regs); break;
662     }
663 }
664
665
666 static void
667 handle_100c(struct bregs *regs)
668 {
669     // XXX - inline
670     biosfn_write_pixel(regs->bh, regs->al, regs->cx, regs->dx);
671 }
672
673 static void
674 handle_100d(struct bregs *regs)
675 {
676     // XXX - inline
677     biosfn_read_pixel(regs->bh, regs->cx, regs->dx, &regs->ax);
678 }
679
680 static void
681 handle_100e(struct bregs *regs)
682 {
683     // Ralf Brown Interrupt list is WRONG on bh(page)
684     // We do output only on the current page !
685     biosfn_write_teletype(regs->al, 0xff, regs->bl, NO_ATTR);
686 }
687
688 static void
689 handle_100f(struct bregs *regs)
690 {
691     regs->bh = GET_BDA(video_page);
692     regs->al = GET_BDA(video_mode) | (GET_BDA(video_ctl) & 0x80);
693     regs->ah = GET_BDA(video_cols);
694 }
695
696
697 static void
698 handle_101000(struct bregs *regs)
699 {
700     if (regs->bl > 0x14)
701         return;
702     vgahw_set_single_palette_reg(regs->bl, regs->bh);
703 }
704
705 static void
706 handle_101001(struct bregs *regs)
707 {
708     vgahw_set_overscan_border_color(regs->bh);
709 }
710
711 static void
712 handle_101002(struct bregs *regs)
713 {
714     vgahw_set_all_palette_reg(regs->es, (u8*)(regs->dx + 0));
715 }
716
717 static void
718 handle_101003(struct bregs *regs)
719 {
720     vgahw_toggle_intensity(regs->bl);
721 }
722
723 static void
724 handle_101007(struct bregs *regs)
725 {
726     if (regs->bl > 0x14)
727         return;
728     regs->bh = vgahw_get_single_palette_reg(regs->bl);
729 }
730
731 static void
732 handle_101008(struct bregs *regs)
733 {
734     regs->bh = vgahw_get_overscan_border_color(regs);
735 }
736
737 static void
738 handle_101009(struct bregs *regs)
739 {
740     vgahw_get_all_palette_reg(regs->es, (u8*)(regs->dx + 0));
741 }
742
743 static void
744 handle_101010(struct bregs *regs)
745 {
746     u8 rgb[3] = {regs->dh, regs->ch, regs->cl};
747     vgahw_set_dac_regs(GET_SEG(SS), rgb, regs->bx, 1);
748 }
749
750 static void
751 handle_101012(struct bregs *regs)
752 {
753     vgahw_set_dac_regs(regs->es, (u8*)(regs->dx + 0), regs->bx, regs->cx);
754 }
755
756 static void
757 handle_101013(struct bregs *regs)
758 {
759     vgahw_select_video_dac_color_page(regs->bl, regs->bh);
760 }
761
762 static void
763 handle_101015(struct bregs *regs)
764 {
765     u8 rgb[3];
766     vgahw_get_dac_regs(GET_SEG(SS), rgb, regs->bx, 1);
767     regs->dh = rgb[0];
768     regs->ch = rgb[1];
769     regs->cl = rgb[2];
770 }
771
772 static void
773 handle_101017(struct bregs *regs)
774 {
775     vgahw_get_dac_regs(regs->es, (u8*)(regs->dx + 0), regs->bx, regs->cx);
776 }
777
778 static void
779 handle_101018(struct bregs *regs)
780 {
781     vgahw_set_pel_mask(regs->bl);
782 }
783
784 static void
785 handle_101019(struct bregs *regs)
786 {
787     regs->bl = vgahw_get_pel_mask();
788 }
789
790 static void
791 handle_10101a(struct bregs *regs)
792 {
793     vgahw_read_video_dac_state(&regs->bl, &regs->bh);
794 }
795
796 static void
797 handle_10101b(struct bregs *regs)
798 {
799     biosfn_perform_gray_scale_summing(regs->bx, regs->cx);
800 }
801
802 static void
803 handle_1010XX(struct bregs *regs)
804 {
805     debug_stub(regs);
806 }
807
808 static void
809 handle_1010(struct bregs *regs)
810 {
811     switch (regs->al) {
812     case 0x00: handle_101000(regs); break;
813     case 0x01: handle_101001(regs); break;
814     case 0x02: handle_101002(regs); break;
815     case 0x03: handle_101003(regs); break;
816     case 0x07: handle_101007(regs); break;
817     case 0x08: handle_101008(regs); break;
818     case 0x09: handle_101009(regs); break;
819     case 0x10: handle_101010(regs); break;
820     case 0x12: handle_101012(regs); break;
821     case 0x13: handle_101013(regs); break;
822     case 0x15: handle_101015(regs); break;
823     case 0x17: handle_101017(regs); break;
824     case 0x18: handle_101018(regs); break;
825     case 0x19: handle_101019(regs); break;
826     case 0x1a: handle_10101a(regs); break;
827     case 0x1b: handle_10101b(regs); break;
828     default:   handle_1010XX(regs); break;
829     }
830 }
831
832
833 static void
834 handle_101100(struct bregs *regs)
835 {
836     biosfn_load_text_user_pat(regs->es, regs->bp
837                               , regs->cx, regs->dx, regs->bl, regs->bh);
838 }
839
840 static void
841 handle_101101(struct bregs *regs)
842 {
843     biosfn_load_text_8_14_pat(regs->bl);
844 }
845
846 static void
847 handle_101102(struct bregs *regs)
848 {
849     biosfn_load_text_8_8_pat(regs->bl);
850 }
851
852 static void
853 handle_101103(struct bregs *regs)
854 {
855     vgahw_set_text_block_specifier(regs->bl);
856 }
857
858 static void
859 handle_101104(struct bregs *regs)
860 {
861     biosfn_load_text_8_16_pat(regs->bl);
862 }
863
864 static void
865 handle_101110(struct bregs *regs)
866 {
867     biosfn_load_text_user_pat(regs->es, regs->bp
868                               , regs->cx, regs->dx, regs->bl, regs->bh);
869     set_scan_lines(regs->bh);
870 }
871
872 static void
873 handle_101111(struct bregs *regs)
874 {
875     biosfn_load_text_8_14_pat(regs->bl);
876     set_scan_lines(14);
877 }
878
879 static void
880 handle_101112(struct bregs *regs)
881 {
882     biosfn_load_text_8_8_pat(regs->bl);
883     set_scan_lines(8);
884 }
885
886 static void
887 handle_101114(struct bregs *regs)
888 {
889     biosfn_load_text_8_16_pat(regs->bl);
890     set_scan_lines(16);
891 }
892
893 static void
894 handle_101130(struct bregs *regs)
895 {
896     switch (regs->bh) {
897     case 0x00: {
898         u32 segoff = GET_IVT(0x1f).segoff;
899         regs->es = segoff >> 16;
900         regs->bp = segoff;
901         break;
902     }
903     case 0x01: {
904         u32 segoff = GET_IVT(0x43).segoff;
905         regs->es = segoff >> 16;
906         regs->bp = segoff;
907         break;
908     }
909     case 0x02:
910         regs->es = get_global_seg();
911         regs->bp = (u32)vgafont14;
912         break;
913     case 0x03:
914         regs->es = get_global_seg();
915         regs->bp = (u32)vgafont8;
916         break;
917     case 0x04:
918         regs->es = get_global_seg();
919         regs->bp = (u32)vgafont8 + 128 * 8;
920         break;
921     case 0x05:
922         regs->es = get_global_seg();
923         regs->bp = (u32)vgafont14alt;
924         break;
925     case 0x06:
926         regs->es = get_global_seg();
927         regs->bp = (u32)vgafont16;
928         break;
929     case 0x07:
930         regs->es = get_global_seg();
931         regs->bp = (u32)vgafont16alt;
932         break;
933     default:
934         dprintf(1, "Get font info BH(%02x) was discarded\n", regs->bh);
935         return;
936     }
937     // Set byte/char of on screen font
938     regs->cx = GET_BDA(char_height) & 0xff;
939
940     // Set Highest char row
941     regs->dx = GET_BDA(video_rows);
942 }
943
944 static void
945 handle_1011XX(struct bregs *regs)
946 {
947     debug_stub(regs);
948 }
949
950 static void
951 handle_1011(struct bregs *regs)
952 {
953     switch (regs->al) {
954     case 0x00: handle_101100(regs); break;
955     case 0x01: handle_101101(regs); break;
956     case 0x02: handle_101102(regs); break;
957     case 0x03: handle_101103(regs); break;
958     case 0x04: handle_101104(regs); break;
959     case 0x10: handle_101110(regs); break;
960     case 0x11: handle_101111(regs); break;
961     case 0x12: handle_101112(regs); break;
962     case 0x14: handle_101114(regs); break;
963     case 0x30: handle_101130(regs); break;
964     default:   handle_1011XX(regs); break;
965     }
966 }
967
968
969 static void
970 handle_101210(struct bregs *regs)
971 {
972     u16 crtc_addr = GET_BDA(crtc_address);
973     if (crtc_addr == VGAREG_MDA_CRTC_ADDRESS)
974         regs->bx = 0x0103;
975     else
976         regs->bx = 0x0003;
977     regs->cx = GET_BDA(video_switches) & 0x0f;
978 }
979
980 static void
981 handle_101230(struct bregs *regs)
982 {
983     u8 mctl = GET_BDA(modeset_ctl);
984     u8 vswt = GET_BDA(video_switches);
985     switch (regs->al) {
986     case 0x00:
987         // 200 lines
988         mctl = (mctl & ~0x10) | 0x80;
989         vswt = (vswt & ~0x0f) | 0x08;
990         break;
991     case 0x01:
992         // 350 lines
993         mctl &= ~0x90;
994         vswt = (vswt & ~0x0f) | 0x09;
995         break;
996     case 0x02:
997         // 400 lines
998         mctl = (mctl & ~0x80) | 0x10;
999         vswt = (vswt & ~0x0f) | 0x09;
1000         break;
1001     default:
1002         dprintf(1, "Select vert res (%02x) was discarded\n", regs->al);
1003         break;
1004     }
1005     SET_BDA(modeset_ctl, mctl);
1006     SET_BDA(video_switches, vswt);
1007     regs->al = 0x12;
1008 }
1009
1010 static void
1011 handle_101231(struct bregs *regs)
1012 {
1013     u8 v = (regs->al & 0x01) << 3;
1014     u8 mctl = GET_BDA(video_ctl) & ~0x08;
1015     SET_BDA(video_ctl, mctl | v);
1016     regs->al = 0x12;
1017 }
1018
1019 static void
1020 handle_101232(struct bregs *regs)
1021 {
1022     vgahw_enable_video_addressing(regs->al);
1023     regs->al = 0x12;
1024 }
1025
1026 static void
1027 handle_101233(struct bregs *regs)
1028 {
1029     u8 v = ((regs->al << 1) & 0x02) ^ 0x02;
1030     u8 v2 = GET_BDA(modeset_ctl) & ~0x02;
1031     SET_BDA(modeset_ctl, v | v2);
1032     regs->al = 0x12;
1033 }
1034
1035 static void
1036 handle_101234(struct bregs *regs)
1037 {
1038     u8 v = (regs->al & 0x01) ^ 0x01;
1039     u8 v2 = GET_BDA(modeset_ctl) & ~0x01;
1040     SET_BDA(modeset_ctl, v | v2);
1041     regs->al = 0x12;
1042 }
1043
1044 static void
1045 handle_101235(struct bregs *regs)
1046 {
1047     debug_stub(regs);
1048     regs->al = 0x12;
1049 }
1050
1051 static void
1052 handle_101236(struct bregs *regs)
1053 {
1054     debug_stub(regs);
1055     regs->al = 0x12;
1056 }
1057
1058 static void
1059 handle_1012XX(struct bregs *regs)
1060 {
1061     debug_stub(regs);
1062 }
1063
1064 static void
1065 handle_1012(struct bregs *regs)
1066 {
1067     switch (regs->bl) {
1068     case 0x10: handle_101210(regs); break;
1069     case 0x30: handle_101230(regs); break;
1070     case 0x31: handle_101231(regs); break;
1071     case 0x32: handle_101232(regs); break;
1072     case 0x33: handle_101233(regs); break;
1073     case 0x34: handle_101234(regs); break;
1074     case 0x35: handle_101235(regs); break;
1075     case 0x36: handle_101236(regs); break;
1076     default:   handle_1012XX(regs); break;
1077     }
1078
1079     // XXX - cirrus has 1280, 1281, 1282, 1285, 129a, 12a0, 12a1, 12a2, 12ae
1080 }
1081
1082
1083 static void
1084 handle_1013(struct bregs *regs)
1085 {
1086     // XXX - inline
1087     biosfn_write_string(regs->al, regs->bh, regs->bl, regs->cx
1088                         , regs->dh, regs->dl, regs->es, (void*)(regs->bp + 0));
1089 }
1090
1091
1092 static void
1093 handle_101a00(struct bregs *regs)
1094 {
1095     regs->bx = GET_BDA(dcc_index);
1096     regs->al = 0x1a;
1097 }
1098
1099 static void
1100 handle_101a01(struct bregs *regs)
1101 {
1102     SET_BDA(dcc_index, regs->bl);
1103     dprintf(1, "Alternate Display code (%02x) was discarded\n", regs->bh);
1104     regs->al = 0x1a;
1105 }
1106
1107 static void
1108 handle_101aXX(struct bregs *regs)
1109 {
1110     debug_stub(regs);
1111 }
1112
1113 static void
1114 handle_101a(struct bregs *regs)
1115 {
1116     switch (regs->al) {
1117     case 0x00: handle_101a00(regs); break;
1118     case 0x01: handle_101a01(regs); break;
1119     default:   handle_101aXX(regs); break;
1120     }
1121 }
1122
1123
1124 struct funcInfo {
1125     u16 static_functionality_off;
1126     u16 static_functionality_seg;
1127     u8 bda_0x49[30];
1128     u8 bda_0x84[3];
1129     u8 dcc_index;
1130     u8 dcc_alt;
1131     u16 colors;
1132     u8 pages;
1133     u8 scan_lines;
1134     u8 primary_char;
1135     u8 secondar_char;
1136     u8 misc;
1137     u8 non_vga_mode;
1138     u8 reserved_2f[2];
1139     u8 video_mem;
1140     u8 save_flags;
1141     u8 disp_info;
1142     u8 reserved_34[12];
1143 };
1144
1145 static void
1146 handle_101b(struct bregs *regs)
1147 {
1148     u16 seg = regs->es;
1149     struct funcInfo *info = (void*)(regs->di+0);
1150     memset_far(seg, info, 0, sizeof(*info));
1151     // Address of static functionality table
1152     SET_FARVAR(seg, info->static_functionality_off, (u32)static_functionality);
1153     SET_FARVAR(seg, info->static_functionality_seg, get_global_seg());
1154
1155     // Hard coded copy from BIOS area. Should it be cleaner ?
1156     memcpy_far(seg, info->bda_0x49, SEG_BDA, (void*)0x49, 30);
1157     memcpy_far(seg, info->bda_0x84, SEG_BDA, (void*)0x84, 3);
1158
1159     SET_FARVAR(seg, info->dcc_index, GET_BDA(dcc_index));
1160     SET_FARVAR(seg, info->colors, 16);
1161     SET_FARVAR(seg, info->pages, 8);
1162     SET_FARVAR(seg, info->scan_lines, 2);
1163     SET_FARVAR(seg, info->video_mem, 3);
1164     regs->al = 0x1B;
1165 }
1166
1167
1168 static void
1169 handle_101c00(struct bregs *regs)
1170 {
1171     u16 flags = regs->cx;
1172     u16 size = 0;
1173     if (flags & 1)
1174         size += sizeof(struct saveVideoHardware);
1175     if (flags & 2)
1176         size += sizeof(struct saveBDAstate);
1177     if (flags & 4)
1178         size += sizeof(struct saveDACcolors);
1179     regs->bx = size;
1180     regs->al = 0x1c;
1181 }
1182
1183 static void
1184 handle_101c01(struct bregs *regs)
1185 {
1186     u16 flags = regs->cx;
1187     u16 seg = regs->es;
1188     void *data = (void*)(regs->bx+0);
1189     if (flags & 1) {
1190         vgahw_save_state(seg, data);
1191         data += sizeof(struct saveVideoHardware);
1192     }
1193     if (flags & 2) {
1194         biosfn_save_bda_state(seg, data);
1195         data += sizeof(struct saveBDAstate);
1196     }
1197     if (flags & 4)
1198         vgahw_save_dac_state(seg, data);
1199     regs->al = 0x1c;
1200 }
1201
1202 static void
1203 handle_101c02(struct bregs *regs)
1204 {
1205     u16 flags = regs->cx;
1206     u16 seg = regs->es;
1207     void *data = (void*)(regs->bx+0);
1208     if (flags & 1) {
1209         vgahw_restore_state(seg, data);
1210         data += sizeof(struct saveVideoHardware);
1211     }
1212     if (flags & 2) {
1213         biosfn_restore_bda_state(seg, data);
1214         data += sizeof(struct saveBDAstate);
1215     }
1216     if (flags & 4)
1217         vgahw_restore_dac_state(seg, data);
1218     regs->al = 0x1c;
1219 }
1220
1221 static void
1222 handle_101cXX(struct bregs *regs)
1223 {
1224     debug_stub(regs);
1225 }
1226
1227 static void
1228 handle_101c(struct bregs *regs)
1229 {
1230     switch (regs->al) {
1231     case 0x00: handle_101c00(regs); break;
1232     case 0x01: handle_101c01(regs); break;
1233     case 0x02: handle_101c02(regs); break;
1234     default:   handle_101cXX(regs); break;
1235     }
1236 }
1237
1238
1239 static void
1240 handle_104f00(struct bregs *regs)
1241 {
1242     // XXX - vbe_biosfn_return_controller_information(&AX,ES,DI);
1243     // XXX - OR cirrus_vesa_00h
1244 }
1245
1246 static void
1247 handle_104f01(struct bregs *regs)
1248 {
1249     // XXX - vbe_biosfn_return_mode_information(&AX,CX,ES,DI);
1250     // XXX - OR cirrus_vesa_01h
1251 }
1252
1253 static void
1254 handle_104f02(struct bregs *regs)
1255 {
1256     // XXX - vbe_biosfn_set_mode(&AX,BX,ES,DI);
1257     // XXX - OR cirrus_vesa_02h
1258 }
1259
1260 static void
1261 handle_104f03(struct bregs *regs)
1262 {
1263     // XXX - vbe_biosfn_return_current_mode
1264     // XXX - OR cirrus_vesa_03h
1265 }
1266
1267 static void
1268 handle_104f04(struct bregs *regs)
1269 {
1270     // XXX - vbe_biosfn_save_restore_state(&AX, CX, DX, ES, &BX);
1271 }
1272
1273 static void
1274 handle_104f05(struct bregs *regs)
1275 {
1276     // XXX - vbe_biosfn_display_window_control
1277     // XXX - OR cirrus_vesa_05h
1278 }
1279
1280 static void
1281 handle_104f06(struct bregs *regs)
1282 {
1283     // XXX - vbe_biosfn_set_get_logical_scan_line_length
1284     // XXX - OR cirrus_vesa_06h
1285 }
1286
1287 static void
1288 handle_104f07(struct bregs *regs)
1289 {
1290     // XXX - vbe_biosfn_set_get_display_start
1291     // XXX - OR cirrus_vesa_07h
1292 }
1293
1294 static void
1295 handle_104f08(struct bregs *regs)
1296 {
1297     // XXX - vbe_biosfn_set_get_dac_palette_format
1298 }
1299
1300 static void
1301 handle_104f0a(struct bregs *regs)
1302 {
1303     // XXX - vbe_biosfn_return_protected_mode_interface
1304 }
1305
1306 static void
1307 handle_104fXX(struct bregs *regs)
1308 {
1309     debug_stub(regs);
1310     regs->ax = 0x0100;
1311 }
1312
1313 static void
1314 handle_104f(struct bregs *regs)
1315 {
1316     if (! CONFIG_VBE || !vbe_has_vbe_display()) {
1317         handle_104fXX(regs);
1318         return;
1319     }
1320
1321     switch (regs->al) {
1322     case 0x00: handle_104f00(regs); break;
1323     case 0x01: handle_104f01(regs); break;
1324     case 0x02: handle_104f02(regs); break;
1325     case 0x03: handle_104f03(regs); break;
1326     case 0x04: handle_104f04(regs); break;
1327     case 0x05: handle_104f05(regs); break;
1328     case 0x06: handle_104f06(regs); break;
1329     case 0x07: handle_104f07(regs); break;
1330     case 0x08: handle_104f08(regs); break;
1331     case 0x0a: handle_104f0a(regs); break;
1332     default:   handle_104fXX(regs); break;
1333     }
1334 }
1335
1336
1337 static void
1338 handle_10XX(struct bregs *regs)
1339 {
1340     debug_stub(regs);
1341 }
1342
1343 // INT 10h Video Support Service Entry Point
1344 void VISIBLE16
1345 handle_10(struct bregs *regs)
1346 {
1347     debug_enter(regs, DEBUG_VGA_10);
1348     switch (regs->ah) {
1349     case 0x00: handle_1000(regs); break;
1350     case 0x01: handle_1001(regs); break;
1351     case 0x02: handle_1002(regs); break;
1352     case 0x03: handle_1003(regs); break;
1353     case 0x04: handle_1004(regs); break;
1354     case 0x05: handle_1005(regs); break;
1355     case 0x06: handle_1006(regs); break;
1356     case 0x07: handle_1007(regs); break;
1357     case 0x08: handle_1008(regs); break;
1358     case 0x09: handle_1009(regs); break;
1359     case 0x0a: handle_100a(regs); break;
1360     case 0x0b: handle_100b(regs); break;
1361     case 0x0c: handle_100c(regs); break;
1362     case 0x0d: handle_100d(regs); break;
1363     case 0x0e: handle_100e(regs); break;
1364     case 0x0f: handle_100f(regs); break;
1365     case 0x10: handle_1010(regs); break;
1366     case 0x11: handle_1011(regs); break;
1367     case 0x12: handle_1012(regs); break;
1368     case 0x13: handle_1013(regs); break;
1369     case 0x1a: handle_101a(regs); break;
1370     case 0x1b: handle_101b(regs); break;
1371     case 0x1c: handle_101c(regs); break;
1372     case 0x4f: handle_104f(regs); break;
1373     default:   handle_10XX(regs); break;
1374     }
1375 }
1376
1377
1378 /****************************************************************
1379  * VGA post
1380  ****************************************************************/
1381
1382 static void
1383 init_bios_area()
1384 {
1385     // init detected hardware BIOS Area
1386     // set 80x25 color (not clear from RBIL but usual)
1387     u16 eqf = GET_BDA(equipment_list_flags);
1388     SET_BDA(equipment_list_flags, (eqf & 0xffcf) | 0x20);
1389
1390     // Just for the first int10 find its children
1391
1392     // the default char height
1393     SET_BDA(char_height, 0x10);
1394
1395     // Clear the screen
1396     SET_BDA(video_ctl, 0x60);
1397
1398     // Set the basic screen we have
1399     SET_BDA(video_switches, 0xf9);
1400
1401     // Set the basic modeset options
1402     SET_BDA(modeset_ctl, 0x51);
1403
1404     // Set the  default MSR
1405     SET_BDA(video_msr, 0x09);
1406 }
1407
1408 void VISIBLE16
1409 vga_post(struct bregs *regs)
1410 {
1411     debug_enter(regs, DEBUG_VGA_POST);
1412
1413     vgahw_init();
1414
1415     init_bios_area();
1416
1417     if (CONFIG_VBE)
1418         vbe_init();
1419
1420     extern void entry_10(void);
1421     SET_IVT(0x10, get_global_seg(), (u32)entry_10);
1422
1423     if (CONFIG_CIRRUS)
1424         cirrus_init();
1425
1426     // XXX - clear screen and display info
1427
1428     // XXX: fill it
1429     SET_VGA(video_save_pointer_table[0], (u32)video_param_table);
1430     SET_VGA(video_save_pointer_table[1], get_global_seg());
1431
1432     // Fixup checksum
1433     extern u8 _rom_header_size, _rom_header_checksum;
1434     SET_VGA(_rom_header_checksum, 0);
1435     u8 sum = -checksum_far(get_global_seg(), 0, _rom_header_size * 512);
1436     SET_VGA(_rom_header_checksum, sum);
1437 }