; 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