EarthWeb   
   datamation.com Brought to you by EarthWeb

HomeSubscribeSearchFAQSitemapContact Us
     

   

  Search Tips
  Advanced Search
   
  

  
Go to ITKnowledge Enterprise

Zen of Graphics Programming, 2nd Edition Zen of Graphics Programming, 2nd Edition
by Michael Abrash
Coriolis, The Coriolis Group
ISBN: 1883577896   Pub Date: 04/01/96

Search this book:
 
Previous Table of Contents Next


The Split Screen and Horizontal Panning: An Example

Listing 8.2 illustrates the interaction of horizontal smooth panning with the split screen, as well as the suppression of pel panning in the split screen. Listing 8.2 creates a virtual screen 1024 pixels across by setting the Offset register (CRTC register 13H) to 64, sets the normal screen to scan video data beginning far enough up in display memory to leave room for the split screen starting at offset zero, turns on the split screen, and fills in the normal screen and split screen with distinctive patterns. Next, Listing 8.2 pans the normal screen horizontally without setting bit 5 of the AC Mode Control register to 1. As you’d expect, the split screen jerks about quite horribly. After a key press, Listing 8.2 sets bit 5 of the Mode Control register and pans the normal screen again. This time, the split screen doesn’t budge an inch—if the code is running on a VGA.

LISTING 8.2 L8-2.ASM

; Demonstrates the interaction of the split screen and
; horizontal pel panning. On a VGA, first pans right in the top
; half while the split screen jerks around, because split screen
; pel panning suppression is disabled, then enables split screen
; pel panning suppression and pans right in the top half while the
; split screen remains stable. On an EGA, the split screen jerks
; around in both cases, because the EGA doesn't support split
; screen pel panning suppression.
;
; The jerking in the split screen occurs because the split screen
; is being pel panned (panned by single pixels--intrabyte panning),
; but is not and cannot be byte panned (panned by single bytes--
; "extrabyte" panning) because the start address of the split screen
; is forever fixed at 0.
;*********************************************************************
IS_VGA               equ 1           ;set to 0 to assemble for EGA
;
VGA_SEGMENT          equ 0a000h
LOGICAL_SCREEN_WIDTH equ 1024        ;# of pixels across virtual
                                     ; screen that we'll pan across
SCREEN_HEIGHT        equ 350
SPLIT_SCREEN_START   equ 200         ;start scan line for split screen
SPLIT_SCREEN_HEIGHT  equ SCREEN_HEIGHT-SPLIT_SCREEN_START-1
CRTC_INDEX           equ 3d4h       ;CRT Controller Index register
AC_INDEX             equ 3c0h       ;Attribute Controller Index reg
OVERFLOW             equ 7          ;index of Overflow reg in CRTC
MAXIMUM_SCAN_LINE    equ 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
HOFFSET              equ 13h        ;index of Horizontal Offset register
                                    ; in CRTC
LINE_COMPARE         equ 18h        ;index of Line Compare reg (bits 7-0
                                    ; of split screen start scan line)
                                    ; in CRTC
AC_MODE_CONTROL      equ 10h        ;index of Mode Control reg in AC
PEL_PANNING          equ 13h        ;index of Pel Panning reg in AC
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
PelPan               db ?          ;current intrabyte horizontal pel
                                   ; panning setting
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
;
; Set the Offset register to make the offset from the start of one
; scan line to the start of the next the desired number of pixels.
; This gives us a virtual screen wider than the actual screen to
; pan across.
; Note that the Offset register is programmed with the logical
; screen width in words, not bytes, hence the final division by 2.
;
        mov     dx,CRTC_INDEX
        mov     ax,(LOGICAL_SCREEN_WIDTH/8/2 shl 8) or HOFFSET
        OUT_WORD
;
; Set the start address to display the memory just past the split
; screen memory.
;
        mov    [StartAddress],SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
        call   SetStartAddress
;
; Set the split screen start scan line.
;
        mov    [SplitScreenLine],SPLIT_SCREEN_START
        call   SetSplitScreenScanLine
;
; Fill the split screen portion of display memory (starting at
; offset 0) with a choppy diagonal pattern sloping left.
;
         mov   ax,VGA_SEGMENT
         mov   es,ax
         sub   di,di
         mov   dx,SPLIT_SCREEN_HEIGHT
                                    ;fill all lines in the split screen
         mov   ax,0FF0h             ;starting fill pattern
         cld
RowLoop:
         mov   cx,LOGICAL_SCREEN_WIDTH/8/4
                                    ;fill 1 scan line
ColumnLoop:
        stosw                       ;draw part of a diagonal line
        mov    word ptr es:[di],0   ;make vertical blank spaces so
                                    ; panning effects can be seen easily
        inc    di
        inc    di
        loop   ColumnLoop
        rol    ax,1                 ;shift pattern word
        dec    dx
        jnz    RowLoop
;
; Fill the portion of display memory that will be displayed in the
; normal screen (the non-split screen part of the display) with a
; choppy diagonal pattern sloping right.
;
        mov    di,SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
        mov    dx,SCREEN_HEIGHT     ;fill all lines
        mov    ax,0c510h            ;starting fill pattern
        cld
RowLoop2:
        mov    cx,LOGICAL_SCREEN_WIDTH/8/4
                                    ;fill 1 scan line
ColumnLoop2:
        stosw                       ;draw part of a diagonal line
        mov    word ptr es:[di],0        ;make vertical blank spaces so
                                         ; panning effects can be seen easily
        inc    di
        inc    di
        loop   ColumnLoop2
        ror    ax,1                      ;shift pattern word
        dec    dx
        jnz    RowLoop2
;
; Pel pan the non-split screen portion of the display; because
; split screen pel panning suppression is not turned on, the split
; screen jerks back and forth as the pel panning setting cycles.
;
        mov    cx,200                    ;pan 200 pixels to the left
        call   PanRight
;
; Wait for a key press (don't echo character).
;
        mov    ah,8                 ;DOS console input without echo function
        int    21h
;
; Return to the original screen location, with pel panning turned off.
;
        mov    [StartAddress],SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
        call   SetStartAddress
        mov    [PelPan],0
        call   SetPelPan
;
; Turn on split screen pel panning suppression, so the split screen
; won't be affected by pel panning. Not done on EGA because both
; readable registers and the split screen pel panning suppression bit
; aren't supported by EGAs.
;
if IS_VGA
        mov    dx,INPUT_STATUS_0
        in     al,dx                      ;reset the AC Index/Data toggle to
                                          ; Index state
        mov    al,20h+AC_MODE_CONTROL
                                          ;bit 5 set to 1 to keep video on
        mov    dx,AC_INDEX                ;point to AC Index/Data register
        out    dx,al
        inc    dx                   ;point to AC Data reg (for reads only)
        in     al,dx                      ;get the current AC Mode Control reg
        or     al,20h                     ;enable split screen pel panning
                                          ; suppression
        dec    dx                         ;point to AC Index/Data reg (Data for
                                          ; writes only)
        out    dx,al                ;write the new AC Mode Control setting
                                          ; with split screen pel panning
                                          ; suppression turned on
endif
;
; Pel pan the non-split screen portion of the display; because
; split screen pel panning suppression is turned on, the split
; screen will not move as the pel panning setting cycles.
;
        mov    cx,200                     ;pan 200 pixels to the left
        call   PanRight
;
; Wait for a key press (don't echo character).
;
        mov    ah,8                 ;DOS console input without echo function
        int    21h
;
; 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
Start endp
;*********************************************************************
; Waits for the leading edge of the vertical sync pulse.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
WaitForVerticalSyncStart proc near
        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
;
WaitForVerticalSyncEnd proc near
        mov    dx,INPUT_STATUS_0
WaitVerticalSync2:
        in     al,dx
        test   al,08h
        jz     WaitVerticalSync2
WaitNotVerticalSync2:
        in     al,dx
        test   al,08h
        jnz    WaitNotVerticalSync2
        ret
WaitForVerticalSyncEnd  endp
;*********************************************************************
; 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 horizontal pel panning setting to the value specified
; by PelPan. Waits until the start of vertical sync to do so, so
; the new pel pan setting can be loaded during non-display time
; and can be ready by the start of the next frame.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
SetPelPan       proc near
        call    WaitForVerticalSyncStart ;also resets the AC
                                         ; Index/Data toggle
                                         ; to Index state
        mov     dx,AC_INDEX
        mov     al,PEL_PANNING+20h   ;bit 5 set to 1 to keep video on
        out     dx,al                     ;point the AC Index to Pel Pan reg
        mov     al,[PelPan]
        out     dx,al                     ;load the new Pel Pan setting
        ret
SetPelPan       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
;*********************************************************************
; Pan horizontally to the right the number of pixels specified by CX.
;
; Input: CX = # of pixels by which to pan horizontally
;
; Output: none
;
; Registers altered: AX, CX, DX
;
PanRight        proc  near
PanLoop:
        inc     [PelPan]
        and     [PelPan],07h
        jnz     DoSetStartAddress
        inc     [StartAddress]
DoSetStartAddress:
        call    SetStartAddress
        call    SetPelPan
        loop    PanLoop
        ret
PanRight        endp
;*********************************************************************
Code    ends
        end     Start


Previous Table of Contents Next

homesubscribesearchfaqsitemapcontactus
Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.