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