VGA: Move cursor setting out of biosfn_write_teletype.
[seabios.git] / vgasrc / vga.c
index 0909c32bb2d0f5688170c9428ea87028466c7206..06b3993dbfbc4d5158b1c701d4b17f0f1bd5f0c0 100644 (file)
@@ -7,7 +7,6 @@
 
 
 // TODO:
-//  * remove recursion from biosfn_write_teletype()
 //  * review correctness of converted asm by comparing with RBIL
 //  * refactor redundant code into sub-functions
 //  * See if there is a method to the in/out stuff that can be encapsulated.
 
 #define SET_VGA(var, val) SET_FARVAR(get_global_seg(), (var), (val))
 
-
-// ===================================================================
-//
-// Video Utils
-//
-// ===================================================================
-
-// -------------------------------------------------------------------
 inline void
 call16_vgaint(u32 eax, u32 ebx)
 {
@@ -51,14 +42,6 @@ call16_vgaint(u32 eax, u32 ebx)
         : "cc", "memory");
 }
 
-
-// ===================================================================
-//
-// BIOS functions
-//
-// ===================================================================
-
-// -------------------------------------------------------------------
 static void
 biosfn_perform_gray_scale_summing(u16 start, u16 count)
 {
@@ -78,7 +61,6 @@ biosfn_perform_gray_scale_summing(u16 start, u16 count)
     vgahw_screen_enable();
 }
 
-// -------------------------------------------------------------------
 static void
 biosfn_set_cursor_shape(u8 CH, u8 CL)
 {
@@ -109,45 +91,48 @@ biosfn_get_cursor_shape(u8 page)
     return GET_BDA(cursor_type);
 }
 
-// -------------------------------------------------------------------
 static void
-biosfn_set_cursor_pos(u8 page, u16 cursor)
+set_cursor_pos(struct cursorpos cp)
 {
     // Should not happen...
-    if (page > 7)
+    if (cp.page > 7)
         return;
 
     // Bios cursor pos
-    SET_BDA(cursor_pos[page], cursor);
+    SET_BDA(cursor_pos[cp.page], (cp.y << 8) | cp.x);
 
     // Set the hardware cursor
     u8 current = GET_BDA(video_page);
-    if (page != current)
+    if (cp.page != current)
         return;
 
     // Get the dimensions
     u16 nbcols = GET_BDA(video_cols);
     u16 nbrows = GET_BDA(video_rows) + 1;
 
-    u8 xcurs = cursor & 0x00ff;
-    u8 ycurs = (cursor & 0xff00) >> 8;
-
     // Calculate the address knowing nbcols nbrows and page num
-    u16 address = SCREEN_IO_START(nbcols, nbrows, page) + xcurs + ycurs * nbcols;
+    u16 address = (SCREEN_IO_START(nbcols, nbrows, cp.page)
+                   + cp.x + cp.y * nbcols);
 
     vgahw_set_cursor_pos(address);
 }
 
-u16
-biosfn_get_cursor_pos(u8 page)
+struct cursorpos
+get_cursor_pos(u8 page)
 {
-    if (page > 7)
-        return 0;
+    if (page == 0xff)
+        // special case - use current page
+        page = GET_BDA(video_page);
+    if (page > 7) {
+        struct cursorpos cp = { 0, 0, 0xfe };
+        return cp;
+    }
     // FIXME should handle VGA 14/16 lines
-    return GET_BDA(cursor_pos[page]);
+    u16 xy = GET_BDA(cursor_pos[page]);
+    struct cursorpos cp = {xy, xy>>8, page};
+    return cp;
 }
 
-// -------------------------------------------------------------------
 static void
 biosfn_set_active_page(u8 page)
 {
@@ -160,10 +145,10 @@ biosfn_set_active_page(u8 page)
         return;
 
     // Get pos curs pos for the right page
-    u16 cursor = biosfn_get_cursor_pos(page);
+    struct cursorpos cp = get_cursor_pos(page);
 
     u16 address;
-    if (GET_GLOBAL(vmode_g->class) == TEXT) {
+    if (GET_GLOBAL(vmode_g->memmodel) & TEXT) {
         // Get the dimensions
         u16 nbcols = GET_BDA(video_cols);
         u16 nbrows = GET_BDA(video_rows) + 1;
@@ -187,215 +172,85 @@ biosfn_set_active_page(u8 page)
     dprintf(1, "Set active page %02x address %04x\n", page, address);
 
     // Display the cursor, now the page is active
-    biosfn_set_cursor_pos(page, cursor);
+    set_cursor_pos(cp);
 }
 
-static void
-biosfn_set_video_mode(u8 mode)
-{                               // mode: Bit 7 is 1 if no clear screen
-    if (CONFIG_CIRRUS)
-        cirrus_set_video_mode(mode);
-
-    if (CONFIG_VBE)
-        if (vbe_has_vbe_display())
-            dispi_set_enable(VBE_DISPI_DISABLED);
-
-    // The real mode
-    u8 noclearmem = mode & 0x80;
-    mode = mode & 0x7f;
-
-    // find the entry in the video modes
-    struct vgamode_s *vmode_g = find_vga_entry(mode);
-    dprintf(1, "mode search %02x found %p\n", mode, vmode_g);
-    if (!vmode_g)
-        return;
-
-    // Read the bios mode set control
-    u8 modeset_ctl = GET_BDA(modeset_ctl);
-
-    // Then we know the number of lines
-// FIXME
-
-    // if palette loading (bit 3 of modeset ctl = 0)
-    if ((modeset_ctl & 0x08) == 0) {    // Set the PEL mask
-        vgahw_set_pel_mask(GET_GLOBAL(vmode_g->pelmask));
-
-        // From which palette
-        u8 *palette_g = GET_GLOBAL(vmode_g->dac);
-        u16 palsize = GET_GLOBAL(vmode_g->dacsize) / 3;
-
-        // Always 256*3 values
-        vgahw_set_dac_regs(get_global_seg(), palette_g, 0, palsize);
-        u16 i;
-        for (i = palsize; i < 0x0100; i++) {
-            static u8 rgb[3] VAR16;
-            vgahw_set_dac_regs(get_global_seg(), rgb, i, 1);
-        }
+static struct cursorpos
+check_scroll(struct cursorpos cp)
+{
+    // Get the dimensions
+    u16 nbrows = GET_BDA(video_rows) + 1;
+    u16 nbcols = GET_BDA(video_cols);
 
-        if ((modeset_ctl & 0x02) == 0x02)
-            biosfn_perform_gray_scale_summing(0x00, 0x100);
+    // Do we need to wrap ?
+    if (cp.x == nbcols) {
+        cp.x = 0;
+        cp.y++;
     }
-
-    struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
-    vgahw_set_mode(vparam_g);
-
-    if (noclearmem == 0x00)
-        clear_screen(vmode_g);
-
-    // Set CRTC address VGA or MDA
-    u16 crtc_addr = VGAREG_VGA_CRTC_ADDRESS;
-    if (GET_GLOBAL(vmode_g->memmodel) == MTEXT)
-        crtc_addr = VGAREG_MDA_CRTC_ADDRESS;
-
-    // Set the BIOS mem
-    u16 cheight = GET_GLOBAL(vparam_g->cheight);
-    SET_BDA(video_mode, mode);
-    SET_BDA(video_cols, GET_GLOBAL(vparam_g->twidth));
-    SET_BDA(video_pagesize, GET_GLOBAL(vparam_g->slength));
-    SET_BDA(crtc_address, crtc_addr);
-    SET_BDA(video_rows, GET_GLOBAL(vparam_g->theightm1));
-    SET_BDA(char_height, cheight);
-    SET_BDA(video_ctl, (0x60 | noclearmem));
-    SET_BDA(video_switches, 0xF9);
-    SET_BDA(modeset_ctl, GET_BDA(modeset_ctl) & 0x7f);
-
-    // FIXME We nearly have the good tables. to be reworked
-    SET_BDA(dcc_index, 0x08);   // 8 is VGA should be ok for now
-    SET_BDA(video_savetable_ptr, (u32)video_save_pointer_table);
-    SET_BDA(video_savetable_seg, get_global_seg());
-
-    // FIXME
-    SET_BDA(video_msr, 0x00); // Unavailable on vanilla vga, but...
-    SET_BDA(video_pal, 0x00); // Unavailable on vanilla vga, but...
-
-    // Set cursor shape
-    if (GET_GLOBAL(vmode_g->class) == TEXT)
-        biosfn_set_cursor_shape(0x06, 0x07);
-    // Set cursor pos for page 0..7
-    int i;
-    for (i = 0; i < 8; i++)
-        biosfn_set_cursor_pos(i, 0x0000);
-
-    // Set active page 0
-    biosfn_set_active_page(0x00);
-
-    // Write the fonts in memory
-    if (GET_GLOBAL(vmode_g->class) == TEXT) {
-        call16_vgaint(0x1104, 0);
-        call16_vgaint(0x1103, 0);
+    // Do we need to scroll ?
+    if (cp.y == nbrows) {
+        // Get the mode
+        struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
+        if (!vmode_g)
+            return cp;
+
+        if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
+            biosfn_scroll(0x01, 0x07, 0, 0, nbrows - 1, nbcols - 1, cp.page,
+                          SCROLL_UP);
+        else
+            biosfn_scroll(0x01, 0x00, 0, 0, nbrows - 1, nbcols - 1, cp.page,
+                          SCROLL_UP);
+        cp.y--;
     }
-    // Set the ints 0x1F and 0x43
-    SET_IVT(0x1f, get_global_seg(), (u32)&vgafont8[128 * 8]);
 
-    switch (cheight) {
-    case 8:
-        SET_IVT(0x43, get_global_seg(), (u32)vgafont8);
-        break;
-    case 14:
-        SET_IVT(0x43, get_global_seg(), (u32)vgafont14);
-        break;
-    case 16:
-        SET_IVT(0x43, get_global_seg(), (u32)vgafont16);
-        break;
-    }
+    return cp;
 }
 
-// -------------------------------------------------------------------
-static void
-biosfn_write_teletype(u8 car, u8 page, u8 attr, u8 flag)
-{                               // flag = WITH_ATTR / NO_ATTR
-    // special case if page is 0xff, use current page
-    if (page == 0xff)
-        page = GET_BDA(video_page);
-
-    // Get the mode
-    struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
-    if (!vmode_g)
-        return;
-
-    // Get the cursor pos for the page
-    u16 cursor = biosfn_get_cursor_pos(page);
-    u8 xcurs = cursor & 0x00ff;
-    u8 ycurs = (cursor & 0xff00) >> 8;
-
-    // Get the dimensions
-    u16 nbrows = GET_BDA(video_rows) + 1;
-    u16 nbcols = GET_BDA(video_cols);
-
-    switch (car) {
+static struct cursorpos
+write_teletype(struct cursorpos cp, struct carattr ca)
+{
+    switch (ca.car) {
     case 7:
         //FIXME should beep
         break;
 
     case 8:
-        if (xcurs > 0)
-            xcurs--;
+        if (cp.x > 0)
+            cp.x--;
         break;
 
     case '\r':
-        xcurs = 0;
+        cp.x = 0;
         break;
 
     case '\n':
-        ycurs++;
+        cp.y++;
         break;
 
     case '\t':
         do {
-            biosfn_write_teletype(' ', page, attr, flag);
-            cursor = biosfn_get_cursor_pos(page);
-            xcurs = cursor & 0x00ff;
-            ycurs = (cursor & 0xff00) >> 8;
-        } while (xcurs % 8 == 0);
+            struct carattr dummyca = {' ', ca.attr, ca.use_attr};
+            vgafb_write_char(cp, dummyca);
+            cp.x++;
+            cp = check_scroll(cp);
+        } while (cp.x % 8);
         break;
 
     default:
-        if (flag == WITH_ATTR)
-            biosfn_write_char_attr(car, page, attr, 1);
-        else
-            biosfn_write_char_only(car, page, attr, 1);
-        xcurs++;
+        vgafb_write_char(cp, ca);
+        cp.x++;
     }
 
-    // Do we need to wrap ?
-    if (xcurs == nbcols) {
-        xcurs = 0;
-        ycurs++;
-    }
-    // Do we need to scroll ?
-    if (ycurs == nbrows) {
-        if (GET_GLOBAL(vmode_g->class) == TEXT)
-            biosfn_scroll(0x01, 0x07, 0, 0, nbrows - 1, nbcols - 1, page,
-                          SCROLL_UP);
-        else
-            biosfn_scroll(0x01, 0x00, 0, 0, nbrows - 1, nbcols - 1, page,
-                          SCROLL_UP);
-        ycurs -= 1;
-    }
-    // Set the cursor for the page
-    cursor = ycurs;
-    cursor <<= 8;
-    cursor += xcurs;
-    biosfn_set_cursor_pos(page, cursor);
+    return check_scroll(cp);
 }
 
 static void
-biosfn_write_string(u8 flag, u8 page, u8 attr, u16 count, u8 row, u8 col,
-                    u16 seg, u8 *offset_far)
+write_string(struct cursorpos cp, u8 flag, u8 attr, u16 count,
+             u16 seg, u8 *offset_far)
 {
-    // Read curs info for the page
-    u16 oldcurs = biosfn_get_cursor_pos(page);
-
     // if row=0xff special case : use current cursor position
-    if (row == 0xff) {
-        col = oldcurs & 0x00ff;
-        row = (oldcurs & 0xff00) >> 8;
-    }
-
-    u16 newcurs = row;
-    newcurs <<= 8;
-    newcurs += col;
-    biosfn_set_cursor_pos(page, newcurs);
+    if (cp.y == 0xff)
+        cp = get_cursor_pos(cp.page);
 
     while (count-- != 0) {
         u8 car = GET_FARVAR(seg, *offset_far);
@@ -405,12 +260,12 @@ biosfn_write_string(u8 flag, u8 page, u8 attr, u16 count, u8 row, u8 col,
             offset_far++;
         }
 
-        biosfn_write_teletype(car, page, attr, WITH_ATTR);
+        struct carattr ca = {car, attr, 1};
+        cp = write_teletype(cp, ca);
     }
 
-    // Set back curs pos
-    if ((flag & 0x01) == 0)
-        biosfn_set_cursor_pos(page, oldcurs);
+    if (flag & 0x01)
+        set_cursor_pos(cp);
 }
 
 static void
@@ -482,26 +337,123 @@ biosfn_restore_bda_state(u16 seg, struct saveBDAstate *info)
  * VGA int 10 handler
  ****************************************************************/
 
+// set video mode
 static void
 handle_1000(struct bregs *regs)
 {
-    // XXX - inline
-    biosfn_set_video_mode(regs->al);
-    switch(regs->al & 0x7F) {
-    case 6:
-        regs->al = 0x3F;
-        break;
-    case 0:
-    case 1:
-    case 2:
-    case 3:
-    case 4:
-    case 5:
-    case 7:
+    u8 noclearmem = regs->al & 0x80;
+    u8 mode = regs->al & 0x7f;
+
+    // Set regs->al
+    if (mode > 7)
+        regs->al = 0x20;
+    else if (mode == 6)
+        regs->al = 0x3f;
+    else
         regs->al = 0x30;
+
+    if (CONFIG_CIRRUS)
+        cirrus_set_video_mode(mode);
+
+    if (CONFIG_VBE)
+        if (vbe_has_vbe_display())
+            dispi_set_enable(VBE_DISPI_DISABLED);
+
+    // find the entry in the video modes
+    struct vgamode_s *vmode_g = find_vga_entry(mode);
+    dprintf(1, "mode search %02x found %p\n", mode, vmode_g);
+    if (!vmode_g)
+        return;
+
+    // Read the bios mode set control
+    u8 modeset_ctl = GET_BDA(modeset_ctl);
+
+    // Then we know the number of lines
+// FIXME
+
+    // if palette loading (bit 3 of modeset ctl = 0)
+    if ((modeset_ctl & 0x08) == 0) {    // Set the PEL mask
+        vgahw_set_pel_mask(GET_GLOBAL(vmode_g->pelmask));
+
+        // From which palette
+        u8 *palette_g = GET_GLOBAL(vmode_g->dac);
+        u16 palsize = GET_GLOBAL(vmode_g->dacsize) / 3;
+
+        // Always 256*3 values
+        vgahw_set_dac_regs(get_global_seg(), palette_g, 0, palsize);
+        u16 i;
+        for (i = palsize; i < 0x0100; i++) {
+            static u8 rgb[3] VAR16;
+            vgahw_set_dac_regs(get_global_seg(), rgb, i, 1);
+        }
+
+        if ((modeset_ctl & 0x02) == 0x02)
+            biosfn_perform_gray_scale_summing(0x00, 0x100);
+    }
+
+    struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
+    vgahw_set_mode(vparam_g);
+
+    if (noclearmem == 0x00)
+        clear_screen(vmode_g);
+
+    // Set CRTC address VGA or MDA
+    u16 crtc_addr = VGAREG_VGA_CRTC_ADDRESS;
+    if (GET_GLOBAL(vmode_g->memmodel) == MTEXT)
+        crtc_addr = VGAREG_MDA_CRTC_ADDRESS;
+
+    // Set the BIOS mem
+    u16 cheight = GET_GLOBAL(vparam_g->cheight);
+    SET_BDA(video_mode, mode);
+    SET_BDA(video_cols, GET_GLOBAL(vparam_g->twidth));
+    SET_BDA(video_pagesize, GET_GLOBAL(vparam_g->slength));
+    SET_BDA(crtc_address, crtc_addr);
+    SET_BDA(video_rows, GET_GLOBAL(vparam_g->theightm1));
+    SET_BDA(char_height, cheight);
+    SET_BDA(video_ctl, (0x60 | noclearmem));
+    SET_BDA(video_switches, 0xF9);
+    SET_BDA(modeset_ctl, GET_BDA(modeset_ctl) & 0x7f);
+
+    // FIXME We nearly have the good tables. to be reworked
+    SET_BDA(dcc_index, 0x08);   // 8 is VGA should be ok for now
+    SET_BDA(video_savetable_ptr, (u32)video_save_pointer_table);
+    SET_BDA(video_savetable_seg, get_global_seg());
+
+    // FIXME
+    SET_BDA(video_msr, 0x00); // Unavailable on vanilla vga, but...
+    SET_BDA(video_pal, 0x00); // Unavailable on vanilla vga, but...
+
+    // Set cursor shape
+    if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
+        biosfn_set_cursor_shape(0x06, 0x07);
+    // Set cursor pos for page 0..7
+    int i;
+    for (i = 0; i < 8; i++) {
+        struct cursorpos cp = {0, 0, i};
+        set_cursor_pos(cp);
+    }
+
+    // Set active page 0
+    biosfn_set_active_page(0x00);
+
+    // Write the fonts in memory
+    if (GET_GLOBAL(vmode_g->memmodel) & TEXT) {
+        call16_vgaint(0x1104, 0);
+        call16_vgaint(0x1103, 0);
+    }
+    // Set the ints 0x1F and 0x43
+    SET_IVT(0x1f, get_global_seg(), (u32)&vgafont8[128 * 8]);
+
+    switch (cheight) {
+    case 8:
+        SET_IVT(0x43, get_global_seg(), (u32)vgafont8);
+        break;
+    case 14:
+        SET_IVT(0x43, get_global_seg(), (u32)vgafont14);
+        break;
+    case 16:
+        SET_IVT(0x43, get_global_seg(), (u32)vgafont16);
         break;
-    default:
-        regs->al = 0x20;
     }
 }
 
@@ -514,14 +466,17 @@ handle_1001(struct bregs *regs)
 static void
 handle_1002(struct bregs *regs)
 {
-    biosfn_set_cursor_pos(regs->bh, regs->dx);
+    struct cursorpos cp = {regs->dl, regs->dh, regs->bh};
+    set_cursor_pos(cp);
 }
 
 static void
 handle_1003(struct bregs *regs)
 {
     regs->cx = biosfn_get_cursor_shape(regs->bh);
-    regs->dx = biosfn_get_cursor_pos(regs->bh);
+    struct cursorpos cp = get_cursor_pos(regs->bh);
+    regs->dl = cp.x;
+    regs->dh = cp.y;
 }
 
 // Read light pen pos (unimplemented)
@@ -555,22 +510,33 @@ handle_1007(struct bregs *regs)
 static void
 handle_1008(struct bregs *regs)
 {
-    // XXX - inline
-    biosfn_read_char_attr(regs->bh, &regs->ax);
+    struct carattr ca = vgafb_read_char(get_cursor_pos(regs->bh));
+    regs->al = ca.car;
+    regs->ah = ca.attr;
+}
+
+static void
+write_chars(u8 page, struct carattr ca, u16 count)
+{
+    struct cursorpos cp = get_cursor_pos(page);
+    while (count--) {
+        vgafb_write_char(cp, ca);
+        cp.x++;
+    }
 }
 
 static void
 handle_1009(struct bregs *regs)
 {
-    // XXX - inline
-    biosfn_write_char_attr(regs->al, regs->bh, regs->bl, regs->cx);
+    struct carattr ca = {regs->al, regs->bl, 1};
+    write_chars(regs->bh, ca, regs->cx);
 }
 
 static void
 handle_100a(struct bregs *regs)
 {
-    // XXX - inline
-    biosfn_write_char_only(regs->al, regs->bh, regs->bl, regs->cx);
+    struct carattr ca = {regs->al, regs->bl, 0};
+    write_chars(regs->bh, ca, regs->cx);
 }
 
 
@@ -622,7 +588,10 @@ handle_100e(struct bregs *regs)
 {
     // Ralf Brown Interrupt list is WRONG on bh(page)
     // We do output only on the current page !
-    biosfn_write_teletype(regs->al, 0xff, regs->bl, NO_ATTR);
+    struct carattr ca = {regs->al, regs->bl, 0};
+    struct cursorpos cp = get_cursor_pos(0xff);
+    cp = write_teletype(cp, ca);
+    set_cursor_pos(cp);
 }
 
 static void
@@ -1024,8 +993,9 @@ static void
 handle_1013(struct bregs *regs)
 {
     // XXX - inline
-    biosfn_write_string(regs->al, regs->bh, regs->bl, regs->cx
-                        , regs->dh, regs->dl, regs->es, (void*)(regs->bp + 0));
+    struct cursorpos cp = {regs->dl, regs->dh, regs->bh};
+    write_string(cp, regs->al, regs->bl, regs->cx
+                 , regs->es, (void*)(regs->bp + 0));
 }