Search code examples
assemblygraphicsx86-16tasmdosbox

How to use pages in assembly (TASM)?


I want to create a game like Guitar Hero in assembly (TASM) and I think I need to use pages to scroll the screen up. How do I print rectangles on the page above and scroll the page up?


Solution

  • Indeed, different video modes have different numbers of "pages" that are available, depending on how much memory is available to the video hardware. You can see a mostly-complete list here. Not all video modes support multiple pages, though. For example, mode 13h, a common graphics mode on the VGA (popular because it allows a simplified way of directly manipulating video memory, thus making programming easier) only supports one page, which means page flipping in the normal way is impossible.* You need to pick a mode that supports multiple pages. Not being much of a graphics-mode person myself, I wouldn't know which ones are popular and which one to recommend. Maybe 0Dh, a 320×200 resolution with 16 colors and 8 pages?

    The standard way to flip between pages is to use BIOS service 10h (video functions), function 05h. This selects the active display page. As you can see in the online documentation here, you set the AH register to the function identifier (05h), AL to the display page you want to select, and then invoke interrupt 10h:

    mov  al, 01h   ; select display page 1
    mov  ah, 05h   ; function 05h: select active display page
    int  10h
    

    Or, for best performance, a real assembly programmer would set both of these halves of the AX register at once, like so:

    mov  ax, 0501h  ; AL == display page, AH == function 05h
    int  10h
    

    If the requested page does not exist, then the interrupt call will have no effect. You can verify this by checking the current display page number afterwards, using function 0Fh, which returns the active display page in the BH register:

    mov  ax, 0F00h   ; AH == function 0Fh
    int  10h
    

    So that's how you use pages, but that really has nothing to do with scrolling. You can't have multiple pages displayed on the screen at a time. For scrolling purposes, what you actually want is function 06h—scroll window up. The DX register contains the coordinates of the upper-left corner of the rectangle to be scrolled (DL == column, DH == row), and the CX register contains the coordinates of the lower-right corner (CL == column, CH == row). The AL register contains the number of lines to scroll, and the BH register contains the screen attributes (colors) to be used in the newly-cleared region.

    For example, if you wanted to scroll the rectangular region of the screen defined by (0, 0)×(80, 20) up by 10 lines, you would do the following:

    xor  dx, dx     ; DL ==  0, DH ==  0
    mov  cx, 1450h  ; CL == 80, CH == 20
    mov  ax, 060Ah  ; AL == 10, AH == 06h
    mov  bh, 0Eh    ; yellow foreground, black background
    int  10h
    

    A very common use for this function is to clear the entire screen. As you might imagine, this is done by setting DX and CX to define the coordinates of the entire screen, and then setting AL to 0 to blank the entire rectangle. The screen will then be cleared and filled with the color attributes specified in BH. Remember that the screen's origin (top-left corner) is at (0, 0). The extent (lower-right corner) depends on the mode that you're in.

    There is a complementary function, 07h, that scrolls a window down, rather than up. It otherwise works in exactly the same way as function 06h.

    Note that, like I said before, and as is mentioned in the linked documentation, these scroll-screen functions work only on the currently active display page. So once you've scrolled the screen—either up or down—the nearly-exposed region will have been filled with the color/video attributes that you specified, and you will then need to draw the new graphics into it.

    The only disadvantage here is that you may experience some flicker as you draw the new content onto the screen. Page flipping would help prevent this, but it would take quite a bit more work to implement. You wouldn't have the BIOS helping you by automatically scrolling a portion of the screen. You'd have to maintain two pages—one that contains what is currently being displayed on the screen, and a second that has been drawn as if it were already scrolled. You would then flip directly from the current page to the new page.

    * You can actually simulate page flipping in mode 13h, though, by using multiple segments, taking advantage of the speedy writes to video memory that this mode allows. Basically, you have one segment register that addresses the actual video memory, and then you have one or more additional segment registers that address "scratch" memory areas that you treat as if they were video memory. Then, when you want to page flip, you just blit all the data from one of your scratch areas into the actual video memory. If you're familiar with modern graphics programming techniques, this is just double buffering—you draw into an off-screen buffer, and then when you're ready, you copy the data from this off-screen buffer onto the screen. Everything old is new again.