Thread View: rec.games.programmer
1 messages
1 total messages
Started by egg
Mon, 03 Aug 1992 17:22
CODE: VGA mode X S/Scrn and panning
Author: egg
Date: Mon, 03 Aug 1992 17:22
Date: Mon, 03 Aug 1992 17:22
519 lines
20305 bytes
20305 bytes
For anyone interested,
In the spirit of this news group, here is some code to set up a VGA
Mode X split screen and facilitate panning.
I think this group should be a place to seek information on techniqes and
ideas and for people to share their game programming experience (and code)
with others. I will contribute more code in future and urge that others to
the same for the benefit of all readers of this group.
Flames aint cool so if you disagree, well its your right but dont bother me.
Themie Gouthas
egg@dstos3.dsto.gov.au
-------------------------------------------------------------------------------
/* Simple Demo of MODE X Split screen and panning */
/* Compile using Turbo C and Tasm */
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
/* For explanation of following functions see source file comments */
extern unsigned int Set320x240Mode(unsigned int WidthInPixels);
extern void WritePixelX(int X, int Y, int PageOffset, int Color);
extern void SetStartAddr(int X, int Y, unsigned int PageOffset);
extern void SplitScrnLine(unsigned int line);
extern TextMode();
extern unsigned int SplitScrnOfs; /* Offset in video ram of split */
/* screen (always 0); */
extern unsigned int MainScrnOfs; /* Offset in video ram ov virtual */
/* screen */
extern unsigned int ScrnLogicalByteWidth; /* Virtual scrn's width in bytes */
extern unsigned int ScrnLogicalPixelWidth; /* Virtual scrn's width in pixels */
extern unsigned int ScrnLogicalHeight; /* Virtual scrn's height in pixels*/
extern unsigned int MaxScrollX; /* Max X pos (bytes) in virt screen of */
/* of physical screen */
extern unsigned int MaxScrollY; /* Max Y pos in virt screen of */
/* of physical screen */
/* draw a coloured rectangle */
void draw_rect(int x, int y, int width, int color, unsigned int page){
int i,j;
for (j=0;j<width;j++)
for(i=0;i<width;i++){
WritePixelX(x+j,y+i,page,color);
}
}
void main(){
int i,j;
char ch;
unsigned int maxx;
unsigned int curr_x=0,curr_y=0;
printf(" Mode X Split screen and panning demo \n\n");
printf(" Scroll: U, D, L, R Quit: Q\n\n\nPress a key to continue");
getch();
/* setup Mode X 320x240x256 with a logical width of ~ 500 pixels */
/* we actually get 496 due to the fact that the width must be divisible */
/* by 8 */
maxx = Set320x240Mode(500); /* maxx is set to 496 */
SplitScrnLine(160);
/* draw a row of blue and white rectangles at top of split screen */
for (i=0;i<24;i++)
draw_rect(i*20,0,20,15+(i%2)*19,SplitScrnOfs);
/* fill virtual screen with coloured rectangles */
for (j=0;j<10;j++)
for (i=0;i<24;i++)
draw_rect(i*20,j*20,20,j*20+i,MainScrnOfs);
/* Allow the user to scroll the screen */
/* U = Up, D = Down, L = Left, R = Right, Q = Quit */
do {
ch=toupper(getch());
switch (ch) {
case 'U':curr_y=(curr_y==0)?0:--curr_y;
SetStartAddr(curr_x,curr_y,MainScrnOfs);
break;
case 'D':curr_y=(curr_y==MaxScrollY)?curr_y:++curr_y;
SetStartAddr(curr_x,curr_y,MainScrnOfs);
break;
case 'L':curr_x=(curr_x==0)?0:--curr_x;
SetStartAddr(curr_x,curr_y,MainScrnOfs);
break;
case 'R':curr_x=(curr_x==MaxScrollX)?curr_x:++curr_x;
SetStartAddr(curr_x,curr_y,MainScrnOfs);
break;
}
} while (ch != 'Q');
TextMode();
printf("Logical screen Width in pixels = %d \n",ScrnLogicalPixelWidth);
printf("Logical screen width in bytes = %d \n",ScrnLogicalByteWidth);
printf("Logical screen height = %d \n",ScrnLogicalHeight);
printf("Split screen offset = %d \n",SplitScrnOfs);
printf("Virtual screen offset = %d \n",MainScrnOfs);
printf("Max start X coord (byte) of physical screen = %d\n",MaxScrollX);
printf("Max start Y coord of physical screen = %d\n",MaxScrollY);
}
------------------------------------------------------------------------
; Panning and split screen functions for MODE X 320x240 256 Col.
; Compile with Tasm.
; C callable.
LOCALS
MAP_MASK equ 02h ;index in SC of Map Mask register
SC_INDEX equ 03c4h ;Sequence Controller Index
GC_INDEX equ 03ceh ; Graphics controller Index
MISC_OUTPUT equ 06h ;Miscellaneous Output register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
INPUT_STATUS_0 equ 03dah ;Input status 0 register
INPUT_STATUS_1 equ 03dah ;Input status 1 register
CRTC_INDEX equ 03d4h ;CRT Controller Index
OVERFLOW equ 07h ; CRTC overflow register index
MAX_SCAN_LINE equ 09h ; CRTC maximum scan line register index
ADDR_HIGH equ 0ch ;Index of Start Address High reg in CRTC
ADDR_LOW equ 0dh ; Low
CRTC_OFFSET equ 13h ; CRTC offset register index
UNDERLINE equ 14h ; CRTC underline location register index
MODE_CONTROL equ 17h ; CRTC mode control register index
LINE_COMPARE equ 18h ; CRTC line compare reg. index (bits 0-7 of
; split screen scan line
PHYSICAL_HEIGHT equ 240 ; Physical screen height
PHYSICAL_WIDTH equ 320 ; Physical screen width
.model small
.data
CRTParms label word
dw 00d06h ;vertical total
dw 03e07h ;overflow (bit 8 of vertical counts)
dw 04109h ;cell height (2 to double-scan)
dw 0ea10h ;v sync start
dw 0ac11h ;v sync end and protect cr0-cr7
dw 0df12h ;vertical displayed
dw 00014h ;turn off dword mode
dw 0e715h ;v blank start
dw 00616h ;v blank end
dw 0e317h ;turn on byte mode
CRT_PARM_LENGTH equ (($-CRTParms)/2)
public _SplitScrnOfs
public _MainScrnOfs
public _ScrnLogicalByteWidth
public _ScrnLogicalPixelWidth
public _ScrnLogicalHeight
public _MaxScrollX
public _MaxScrollY
_SplitScrnOfs dw 0 ; Offset in video ram of Split Screen
; always = 0
_MainScrnOfs dw 0 ; Ofset in video ram of Main virtual
; screen ( = 0 if no split screen
; otherwise = offset of first byte
; after split screen
_ScrnLogicalByteWidth dw 0 ; Logical width in bytes of screen
_ScrnLogicalPixelWidth dw 0 ; Logical width in pixels of screen
_ScrnLogicalHeight dw 0 ; Logical Height of screen
_MaxScrollX dw 0 ; Max X start position of Physical
; screen within virtual screen (in
; bytes)
_MaxScrollY dw 0 ; Max Y start position of Physical
; screen within virtual screen
; Index/data pairs for CRT Controller registers that differ between
; mode 13h and mode X.
.code
public _Set320x240Mode
public _WritePixelX
public _SetStartAddr
public _TextMode
public _SplitScrnLine
; Macro to wait for the vertical retrace leading edge
WaitForVsync macro
mov dx,INPUT_STATUS_0
@@WaitNotVsync:
in al,dx
test al,08h
jnz @@WaitNotVsync
@@WaitVsync:
in al,dx
test al,08h
jnz @@WaitVsync
endm
; Mode X (320x240, 256 colors) Set 320x240 mode with a larger virtual
; logical screen width.
; C near-callable as:
;
; int Set320x240(unsigned int WidthInPixels);
;
; returns the actual width of the allocated virtual screen in pixels
; and also saves it in _ScrnLogicalPixelWidth. Saves virtual screen byte
; screen width in _ScrnLogicalByteWidth.
;
;
; NOTE: Slightly modified code - Originally by Michael Abrash from DDJ.
_Set320x240Mode proc near
ARG scrwidth:word
push bp ;preserve caller's stack frame
mov bp,sp
push si ;preserve C register vars
push di ; (don't count on BIOS preserving anything)
mov ax,13h ;let the BIOS set standard 256-color
int 10h ; mode (320x200 linear)
mov dx,SC_INDEX
mov ax,0604h
out dx,ax ;disable chain4 mode
mov ax,0100h
out dx,ax ;synchronous reset while setting Misc Output
; for safety, even though clock unchanged
mov dx,MISC_OUTPUT
mov al,0e3h
out dx,al ;select 25 MHz dot clock & 60 Hz scanning rate
mov dx,SC_INDEX
mov ax,0300h
out dx,ax ;undo reset (restart sequencer)
mov dx,CRTC_INDEX ;reprogram the CRT Controller
mov al,11h ;VSync End reg contains register write
out dx,al ; protect bit
inc dx ;CRT Controller Data register
in al,dx ;get current VSync End register setting
and al,7fh ;remove write protect on various
out dx,al ; CRTC registers
dec dx ;CRT Controller Index
cld
mov si,offset CRTParms ;point to CRT parameter table
mov cx,CRT_PARM_LENGTH ;# of table entries
SetCRTParmsLoop:
lodsw ;get the next CRT Index/Data pair
out dx,ax ;set the next CRT Index/Data pair
loop SetCRTParmsLoop
mov dx,SC_INDEX
mov ax,0f02h
out dx,ax ;enable writes to all four planes
mov ax,SCREEN_SEG ;now clear all display memory, 8 pixels
mov es,ax ; at a time
sub di,di ;point ES:DI to display memory
sub ax,ax ;clear to zero-value pixels
mov cx,8000h ;# of words in display memory
rep stosw ;clear all of display memory
; Mode X is set, now set the required logical page width.
mov dx,CRTC_INDEX
mov al,CRTC_OFFSET
out dx,al
inc dx
mov ax,[scrwidth]
shr ax,1
shr ax,1
shr ax,1
out dx,al
; The EXACT logical pixel width may not have been possible since
; it should be divisible by 8. Round down to the closest possible
; width and update the status variables
shl ax,1
mov bx,ax
mov [_ScrnLogicalByteWidth],ax ; Store the byte width of virtual
; screen
sub ax,PHYSICAL_WIDTH/4 ; Calculate and store Max Y position
mov [_MaxScrollX],ax ; of physical screen in virtual
; screen
mov ax,bx ; set ax to byte width of virt scrn
shl ax,1 ; convert to pixels
shl ax,1
mov [_ScrnLogicalPixelWidth],ax ; store virt scrn pixel width
mov cx,ax ; save ax (return value)
; calculate no. non split screen rows in video ram
mov ax,0ffffh ; cx = Maximum video ram offset
sub dx,dx ; DX:AX is divide operand, set DX = 0
div bx ; divide ax by ScrnLogicalByteWidth
mov [_ScrnLogicalHeight],ax ; Save Screen Logical Height
sub ax,PHYSICAL_HEIGHT ; Update the maximum Y position of
mov [_MaxScrollY],ax ; Physical screen in logical screen
mov ax,cx ; restore ax (return value)
pop di ;restore C register vars
pop si
pop bp ;restore caller's stack frame
ret
_Set320x240Mode endp
; Mode X (320x240, 256 colors) write pixel routine.
; No clipping is performed.
; C near-callable as:
; void WritePixelX(int X, int Y, int PageOffset, int Color);
;
; Modified code - originally by Michael Abrash from DDJ.
;
public _WritePixelX
ARG X:word,Y:word,PgOfs:word,Color:word
_WritePixelX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
mov ax,[_ScrnLogicalByteWidth]
mul [Y] ;offset of pixel's scan line in page
mov bx,[X]
shr bx,1
shr bx,1 ;X/4 = offset of pixel in scan line
add bx,ax ;offset of pixel in page
add bx,[PgOfs] ;offset of pixel in display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:BX to the pixel's address
mov cl,byte ptr [X]
and cl,011b ;CL = pixel's plane
mov ax,0100h + MAP_MASK ;AL = index in SC of Map Mask reg
shl ah,cl ;set only the bit for the pixel's
; plane to 1
mov dx,SC_INDEX ;set the Map Mask to enable only the
out dx,ax ; pixel's plane
mov al,byte ptr [Color]
mov es:[bx],al ;draw the pixel in the desired color
pop bp ;restore caller's stack frame
ret
_WritePixelX endp
; Mode X (320x240, 256 colors) Set 320x240 mode split screen starting row
; The split screen resides on the bottom half of the screen and has a
; starting address of A000:0000
;
; C near-callable as:
; void SetStartAddr(unsigned int Addr);
;
; Updates _MainScrnOffset to reflect the existence of the split screen region
; ie -MainScrnOffset is set to the offset of the first pixel beyond the split
; screen region
;
_SplitScrnLine proc near
ARG Line:word
push bp
mov bp,sp ; set up stack frame
mov ax,[Line]
dec ax ; Don't ask me why. It works this way !!
jns @@NotNeg ; Check that Split Scrn start scan line is +ve
mov ax,0 ; Since -ve set to 0
@@NotNeg:
push ax ; Save the decremented start scam line
shl ax,1 ; Mode X is actually composed of 480 scan lines
; so for start scanline multiply required ModeX
; scan line by 2
mov [Line],ax ; save the scanline
WaitForVsync ; wait for vertical retrace
mov dx,CRTC_INDEX
mov ah,byte ptr [Line]
mov al,LINE_COMPARE
cli ; Dont allow register setting to be interrupted
out dx,ax ; Bits 7-0 of the split screen scan line
mov ah,byte ptr [Line+1]
and ah,1
mov cl,4
shl ah,cl
mov al,OVERFLOW ; Bit 4 of overflow register = Bit 8 of split
out dx,al ; screen scan line,
inc dx ; So using readability of VGA registers
in al,dx ; Read the OVERFLOW register, and set the
and al, not 10h ; bit corresponding to Bit 8 (above)
or al,ah
out dx,al
dec dx
mov ah,byte ptr [Line+1]
and al,2
mov cl,3
ror ah,cl
mov al,MAX_SCAN_LINE ; Bit 6 of max scan line register =
out dx,al ; Bit 9 of split screen scan line
inc dx ; As we did before, update the apropriate
in al,dx ; bit without disturbing the rest
and al, not 40h
or al,ah
out dx,al
sti ; Registers are set, so interrupts are safe
pop ax ; Determine where the first byte
sub ax,PHYSICAL_HEIGHT ; of the non split screen video ram
neg ax ; starts and store it for future
mov bx,[_ScrnLogicalByteWidth] ; reference
mul bx
mov [_MainScrnOfs],ax
; calculate no. non split screen rows in video ram
mov cx,0ffffh ; cx = Maximum video ram offset
sub cx,ax ; cx = cx - _MainScrnOfs
xchg cx,ax ; swap cx and ax
sub dx,dx ; DX:AX is divide operand, set DX = 0
div bx ; divide ax (prev cx) by
; ScrnLogicalByteWidth
mov [_ScrnLogicalHeight],ax ; Save Screen Logical Height
sub ax,PHYSICAL_HEIGHT ; Update the maximum Y position of
mov [_MaxScrollY],ax ; Physical screen in logical screen
xchg cx,ax ; restore original ax (MainScrnOfs)
mov bh,al ; Set the visible screen start address
mov ch,ah ; to the top left corner of the virtual
jmp short StartAddrEntry ; screen
;pop bp
;ret
_SplitScrnLine endp
; Mode X (320x240, 256 colors) Set 320x240 mode start address
; of logical screen.
; C near-callable as:
;
; void SetStartAddr(unsigned int x, unsigned int y, unsigned int StartOffs);
;
; Params: StartOffset is offset of first byte of logical screen ram
; (Useful if you want to double buffer by splitting your non
; split screen video ram into 2 pages)
; X,Y coordinates of the top left hand corner of the physical screen
; within the logical screen
; X must not exceed (Logical screen width - Physical screen width)
; Y must not exceed (Logical screen height - Physical screen height)
;
; NOTE: X in bytes NOT pixels (1 Byte = 4 pixels)
_SetStartAddr proc near
ARG x:word,y:word,StartOffs
push bp
mov bp,sp
mov ax,[_ScrnLogicalByteWidth] ; Calculate Offset increment
mul [y] ; for Y
add ax,[StartOffs] ; add it to Start offset
add ax,[x] ; add the column offset for X
mov bh,al ; setup CRTC start addr regs and
; values in word registers for
mov ch,ah ; fast word outs
StartAddrEntry:
mov bl,ADDR_LOW
mov cl,ADDR_HIGH
WaitForVsync
mov dx,CRTC_INDEX
mov ax,bx
out dx,ax ;start address low
mov ax,cx
out dx,ax ;start address high
pop bp
ret
_SetStartAddr endp
;
; Return to text mode
;
_TextMode proc near
push bp
mov bp,sp
mov ax,00003h ; Restore Text Mode
int 10h
;mov ah,4ch ; Terminate the program
;int 21h
pop bp
ret
_TextMode endp
end
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads