Previous | Table of Contents | Next

Finally, after another keypress, Listing 30.1 halts.

LISTING 30.1 L30-1.ASM

    ; Demonstrates the VGA/EGA split screen in action.
    ;
    ;*********************************************************************
    IS_VGA               equ  1        ;set to 0 to assemble for EGA
    ;
    VGA_SEGMENT          equ  0a000h
    SCREEN_WIDTH         equ  640
    SCREEN_HEIGHT        equ  350
    CRTC_INDEX           equ  3d4h     ;CRT Controller Index register
    OVERFLOW             equ  7        ;index of Overflow reg in CRTC
    MAXIMUM_SCAN_LINEequ 9             ;index of Maximum Scan Line register
                                       ; in CRTC
    START_ADDRESS_HIGH   equ  0ch      ;index of Start Address High register
                                       ; in CRTC
    START_ADDRESS_LOW    equ  0dh      ;index of Start Address Low register
                                       ; in CRTC
    LINE_COMPARE         equ  18h      ;index of Line Compare reg (bits 7-0
                                       ; of split screen start scan line)
                                       ; in CRTC
    INPUT_STATUS_0       equ  3dah     ;Input Status 0 register
    WORD_OUTS_OK         equ  1        ;set to 0 to assemble for
                                       ; computers that can't handle
                                       ; word outs to indexed VGA registers
    ;*********************************************************************
    ; Macro to output a word value to a port.
    ;
    OUT_WORD     macro
    if WORD_OUTS_OK
          out    dx,ax
    else
          out    dx,al
          inc    dx
          xchg   ah,al
          out    dx,al
          dec    dx
          xchg   ah,al
    endif
          endm
    ;*********************************************************************
    MyStack      segment para stack 'STACK'
         db      512 dup (0)
    MyStack      ends
    ;*********************************************************************
    Data  segment
    SplitScreenLine   dw   ?   ;line the split screen currently
                               ; starts after
    StartAddress      dw   ?   ;display memory offset at which
                               ; scanning for video data starts
    ; Message displayed in split screen.
    SplitScreenMsg    db   'Split screen text row #'
    DigitInsert       dw   ?
                      db   '...$'
    Data  ends
    ;*********************************************************************
    Code  segment
          assume      cs:Code, ds:Data
    ;*********************************************************************
    Start proc  near
          mov   ax,Data
          mov   ds,ax
    ;
    ; Select mode 10h, 640x350 16-color graphics mode.
    ;
          mov   ax,0010h              ;AH=0 is select mode function
                                      ;AL=10h is mode to select,
                                      ; 640x350 16-color graphics mode
          int   10h
    ;
    ; Put text into display memory starting at offset 0, with each row
    ; labelled as to number. This is the part of memory that will be
    ; displayed in the split screen portion of the display.
    ;
           mov   cx,25                ;# of lines of text we'll draw into
                                      ; the split screen part of memory
    FillSplitScreenLoop:
           mov    ah,2                ;set cursor location function #
           sub    bh,bh               ;set cursor in page 0
           mov    dh,25
           sub    dh,cl               ;calculate row to draw in
           sub    dl,dl               ;start in column 0
           int    10h                 ;set the cursor location
           mov    al,25
           sub    al,cl               ;calculate row to draw in again
           sub    ah,ah               ;make the value a word for division
           mov    dh,10
           div    dh                  ;split the row # into two digits
           add    ax,'00'             ;convert the digits to ASCII
           mov    [DigitInsert],ax    ;put the digits into the text
                                      ; to be displayed
           mov    ah,9
           mov    dx,offset SplitScreenMsg
           int    21h                 ;print the text
           loop   FillSplitScreenLoop
    ;
    ; Fill display memory starting at 8000h with a diagonally striped
    ; pattern.
    ;
           mov    ax,VGA_SEGMENT
           mov    es,ax
           mov    di,8000h
           mov    dx,SCREEN_HEIGHT     ;fill all lines
           mov    ax,8888h             ;starting fill pattern
           cld
    RowLoop:
           mov    cx,SCREEN_WIDTH/8/2  ;fill 1 scan line a word at a time
           rep    stosw                ;fill the scan line
           ror    ax,1                 ;shift pattern word
           dec    dx
           jnz    RowLoop
    ;
    ; Set the start address to 8000h and display that part of memory.
    ;
           mov    [StartAddress],8000h
           call   SetStartAddress
    ;
    ; Slide the split screen half way up the screen and then back down
    ; a quarter of the screen.
    ;
           mov    [SplitScreenLine],SCREEN_HEIGHT-1
                                        ;set the initial line just off
                                        ; the bottom of the screen
           mov    cx,SCREEN_HEIGHT/2
           call   SplitScreenUp
           mov    cx,SCREEN_HEIGHT/4
           call   SplitScreenDown
    ;
    ; Now move up another half a screen and then back down a quarter.
    ;
           mov    cx,SCREEN_HEIGHT/2
           call   SplitScreenUp
           mov    cx,SCREEN_HEIGHT/4
           call   SplitScreenDown
    ;
    ; Finally move up to the top of the screen.
    ;
           mov    cx,SCREEN_HEIGHT/2-2
           call   SplitScreenUp
    ;
    ; Wait for a key press (don't echo character).
    ;
           mov    ah,8                   ;DOS console input without echo function
           int    21h
    ;
    ; Turn the split screen off.
    ;
           mov    [SplitScreenLine],0ffffh
           call   SetSplitScreenScanLine
    ;
    ; Wait for a key press (don't echo character).
    ;
           mov    ah,8                    ;DOS console input without echo function
           int    21h
    ;
    ; Display the memory at 0 (the same memory the split screen displays).
    ;
           mov    [StartAddress],0
           call   SetStartAddress
    ;
    ; Flip between the split screen and the normal screen every 10th
    ; frame until a key is pressed.
    ;
    FlipLoop:
           xor    [SplitScreenLine],0ffffh
           call   SetSplitScreenScanLine
           mov    cx,10
    CountVerticalSyncsLoop:
           call   WaitForVerticalSyncEnd
           loop   CountVerticalSyncsLoop
           mov    ah,0bh                  ;DOS character available status
           int    21h
           and    al,al ;character available?
           jz     FlipLoop                 ;no, toggle split screen on/off status
           mov    ah,1
           int    21h                      ;clear the character
    ;
    ; Return to text mode and DOS.
    ;
           mov    ax,0003h                 ;AH=0 is select mode function
    ;AL=3 is mode to select, text mode
           int    10h                      ;return to text mode
           mov    ah,4ch
           int    21h                      ;return to DOS
    Startendp
    ;*********************************************************************
    ; Waits for the leading edge of the vertical sync pulse.
    ;
    ; Input: none
    ;
    ; Output: none
    ;
    ; Registers altered: AL, DX
    ;
    WaitForVerticalSyncStartprocnear
           mov    dx,INPUT_STATUS_0
    WaitNotVerticalSync:
           in     al,dx
           test   al,08h
           jnz    WaitNotVerticalSync
    WaitVerticalSync:
           in     al,dx
           test   al,08h
           jz     WaitVerticalSync
           ret
    WaitForVerticalSyncStart       endp
    ;*********************************************************************
    ; Waits for the trailing edge of the vertical sync pulse.
    ;
    ; Input: none
    ;
    ; Output: none
    ;
    ; Registers altered: AL, DX
    ;
    WaitForVerticalSyncEndprocnear
           mov    dx,INPUT_STATUS_0
    WaitVerticalSync2:
           in     al,dx
           test   al,08h
           jz     WaitVerticalSync2
    WaitNotVerticalSync2:
           in     al,dx
           test   al,08h
           jnz    WaitNotVerticalSync2
           ret
    WaitForVerticalSyncEndendp
    ;*********************************************************************
    ; Sets the start address to the value specifed by StartAddress.
    ; Wait for the trailing edge of vertical sync before setting so that
    ; one half of the address isn't loaded before the start of the frame
    ; and the other half after, resulting in flicker as one frame is
    ; displayed with mismatched halves. The new start address won't be
    ; loaded until the start of the next frame; that is, one full frame
    ; will be displayed before the new start address takes effect.
    ;
    ; Input: none
    ;
    ; Output: none
    ;
    ; Registers altered: AX, DX
    ;
    SetStartAddress       proc    near
           call    WaitForVerticalSyncEnd
           mov     dx,CRTC_INDEX
           mov     al,START_ADDRESS_HIGH
           mov     ah,byte ptr [StartAddress+1]
           cli                       ;make sure both registers get set at once
           OUT_WORD
           mov     al,START_ADDRESS_LOW
           mov     ah,byte ptr [StartAddress]
           OUT_WORD
           sti
           ret
    SetStartAddress     endp
    ;*********************************************************************
    ; Sets the scan line the split screen starts after to the scan line
    ; specified by SplitScreenLine.
    ;
    ; Input: none
    ;
    ; Output: none
    ;
    ; All registers preserved
    ;
    SetSplitScreenScanLine    proc  near
           push    ax
           push    cx
           push    dx
    ;
    ; Wait for the leading edge of the vertical sync pulse. This ensures
    ; that we don't get mismatched portions of the split screen setting
    ; while setting the two or three split screen registers (register 18h
    ; set but register 7 not yet set when a match occurs, for example),
    ; which could produce brief flickering.
    ;
           call WaitForVerticalSyncStart
    ;
    ; Set the split screen scan line.
    ;
           mov    dx,CRTC_INDEX
           mov    ah,byte ptr [SplitScreenLine]
           mov    al,LINE_COMPARE
           cli                               ;make sure all the registers get set at once
           OUT_WORD                          ;set bits 7-0 of the split screen scan line
           mov    ah,byte ptr [SplitScreenLine+1]
           and    ah,1
           mov    cl,4
           shl    ah,cl                      ;move bit 8 of the split split screen scan
                                             ; line into position for the Overflow reg
           mov    al,OVERFLOW
    if IS_VGA
    ;
    ; The Split Screen, Overflow, and Line Compare registers all contain
    ; part of the split screen start scan line on the VGA. We'll take
    ; advantage of the readable registers of the VGA to leave other bits
    ; in the registers we access undisturbed.
    ;
           out    dx,al                      ;set CRTC Index reg to point to Overflow
           inc    dx                         ;point to CRTC Data reg
           in     al,dx                      ;get the current Overflow reg setting
           and    al,not 10h                 ;turn off split screen bit 8
           or     al,ah                      ;insert the new split screen bit 8
                                             ; (works in any mode)
           out    dx,al                      ;set the new split screen bit 8
           dec    dx                         ;point to CRTC Index reg
           mov    ah,byte ptr [SplitScreenLine+1]
           and    ah,2
           mov    cl,3
           ror    ah,cl                      ;move bit 9 of the split split screen scan
                                             ; line into position for the Maximum Scan
                                             ; Line register
           mov    al,MAXIMUM_SCAN_LINE
           out    dx,al                      ;set CRTC Index reg to point to Maximum
                                             ; Scan Line
           inc    dx                         ;point to CRTC Data reg
           in     al,dx                      ;get the current Maximum Scan Line setting
           and    al,not 40h                 ;turn off split screen bit 9
           or     al,ah                      ;insert the new split screen bit 9
                                             ; (works in any mode)
           out    dx,al                      ;set the new split screen bit 9
    else
    ;
    ; Only the Split Screen and Overflow registers contain part of the
    ; Split Screen start scan line and need to be set on the EGA.
    ; EGA registers are not readable, so we have to set the non-split
    ; screen bits of the Overflow register to a preset value, in this
    ; case the value for 350-scan-line modes.
    ;
           or    ah,0fh                       ;insert the new split screen bit 8
                                              ; (only works in 350-scan-line EGA modes)
           OUT_WORD                           ;set the new split screen bit 8
    endif
           sti
           pop    dx
           pop    cx
           pop    ax
           ret
    SetSplitScreenScanLine endp
    ;*********************************************************************
    ; Moves the split screen up the specified number of scan lines.
    ;
    ; Input: CX = # of scan lines to move the split screen up by
    ;
    ; Output: none
    ;
    ; Registers altered: CX
    ;
    SplitScreenUp    proc    near
    SplitScreenUpLoop:
           dec    [SplitScreenLine]
           call   SetSplitScreenScanLine
           loop   SplitScreenUpLoop
           ret
    SplitScreenUp    endp
    ;*********************************************************************
    ; Moves the split screen down the specified number of scan lines.
    ;
    ; Input: CX = # of scan lines to move the split screen down by
    ;
    ; Output: none
    ;
    ; Registers altered: CX
    ;
    SplitScreenDown    proc    near
    SplitScreenDownLoop:
           inc    [SplitScreenLine]
           call   SetSplitScreenScanLine
           loop   SplitScreenDownLoop
           ret
    SplitScreenDown    endp
    ;*********************************************************************
    Code    ends
            end    Start

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash