_CREATING YOUR OWN MULTIPLAYER GAME SYSTEMS_ by Rahner James and Linus Sphinx Listing One ; **************************************************************************** ; * Title: GKERNEL.ASM ; * Copyright (c) March 1994, Ryu Consulting ; * Written by Rahner James ; * Code to move the virtual graphics buffer to the physical buffer ; * Very important note: These functions are designed for performance, so ; * very little (if any) range checking is performed on the data unless ; * the RANGE_CHECK label is defined ; **************************************************************************** .386 .model small, syscall ;RANGE_CHECK equ 0 ; Defined if range checking is enabled MAX_VIRTUAL_WIDTH equ 320 MAX_VIRTUAL_HEIGHT equ 200 VIDEO_START equ 0A0000h TIMER_INTERRUPT equ 1CH .data Display_Width dd 320 Display_Height dd 200 Virtual_Display_Ptr dd 0 ; -> virtual display buffer Virtual_Display_Bytes dd 0 ; Size of virtual display in bytes Virtual_Display_Words dd 0 ; Size of virtual display in 16-bit words Virtual_Display_Dwords dd 0 ; Size of virtual display in 32-bit words Virtual_Display_End dd 0 ; End of the virtual display Lowest_X dd 0 Lowest_Y dd 0 Highest_X dd 0 Highest_Y dd 0 Video_Access_Count dd 0 ; Number of video accesses since refresh Update_Display_Flag dd 0 ; Set to !0 if the virtual buffer should ; be moved to the display Line_Start_Table dd MAX_VIRTUAL_HEIGHT dup(0) Old_Timer_Vector dd 0,0 ; -> old timer ISR, !0 if installed Old_Video_Mode dd 0FFh .code extern malloc_:near, free_:near, __GETDS:near ; **************************************************************************** ; * void MOVE_VIRTUAL_TO_DISPLAY( void ) ; * Moves the virtual buffer to the display buffer ; * Given: Video_Access_Count = number of accesses made to the video ; * Returns: Virtual buffer copied to the display ; * Lowest_X, Lowest_Y = lowest point in virtual display accessed ; * Highest_X, Highest_Y = highest point in virtual display accessed ; * Video_Access_Count = number of accesses made to the video ; * Update_Display_Flag set to 0 ; **************************************************************************** move_virtual_to_display_ proc uses eax ebx ecx edx edi esi cmp Video_Access_Count, 0 ; See if we need to do this jz short done ; Quit if not ; * Setup the registers for the movement mov esi, Lowest_Y ; ESI = lowest Y value mov esi, Line_Start_Table[esi*4] ; ESI -> virtual line start mov eax, Lowest_X ; EAX = left X and al, not 3 add esi, eax ; ESI -> upper left pixel in ; virtual buffer mov edi, esi sub edi, Virtual_Display_Ptr ; EDI = offset from start of table add edi, VIDEO_START ; EDI -> upper left pixel in display mov edx, Highest_X ; EDX = right most side mov ebx, Display_Width sub edx, eax ; EDX = width in bytes - 1 sub ebx, edx ; EBX = display width remainder + 1 dec ebx and bl, not 3 ; Clear the LSBits add edx, 4 shr edx, 2 ; DL = width/4 (will work up to 1023 ; pixels wide) mov eax, Highest_Y sub eax, Lowest_Y inc eax ; * Loop through the rectangle, putting it down @@: mov ecx, edx rep movsd add edi, ebx add esi, ebx dec eax jnz @B done: mov Lowest_X, -1 mov Lowest_Y, -1 mov Highest_X, 0 mov Highest_Y, 0 mov Video_Access_Count, 0 mov Update_Display_Flag, 0 ; Stop any updates ret move_virtual_to_display_ endp ; **************************************************************************** ; * void far TIMER_ISR( void ) ; * Timer tick ISR ; * Given: nothing ; * Returns: 0 if all went well ; **************************************************************************** timer_isr proc push ds push es pushad call __GETDS inc byte ptr ds:[0B009Eh] popad pop es pop ds iretd timer_isr endp ; **************************************************************************** ; * int GR_INIT( int EAX ) ; * Sets the graphics screen to a mode ; * Given: EAX = mode to set the graphics monitor to ; * 0 = normal EGA, mode 10h, 640x350, 16-color ; * 1 = normal VGA, mode 13h, 320x200, 256-color ; * Returns: 0 if all went well ; **************************************************************************** gr_init_ proc near uses ebx ecx edx edi mov Update_Display_Flag, 0 ; Stop any updates mov Video_Access_Count, 0 mov Lowest_X, -1 mov Lowest_Y, -1 mov Highest_X, 0 mov Highest_Y, 0 mov word ptr ds:[0B009Eh], 730h ; * Make sure we are using a fresh memory buffer cmp Virtual_Display_Ptr, 0 ; See if already setup jz short gr10_init ; Skip if not push eax xor eax, eax xchg eax, Virtual_Display_Ptr call free_ pop eax gr10_init: ; * Get the old video mode, if need be cmp byte ptr Old_Video_Mode, 0FFh ; been here before? jne short gr20_init push eax mov ah, 0fh int 10h mov byte ptr Old_Video_Mode, al pop eax gr20_init: ; * Revector the timer if we need to cmp Old_Timer_Vector, 0 ; timer already installed? jnz short gr30_init push eax mov eax, 204h ; EAX = DPMI Get Protected-mode mov bl, TIMER_INTERRUPT ; Interrupt Vector command int 31h mov Old_Timer_Vector, edx mov Old_Timer_Vector+4, ecx mov eax, 205h ; EAX = DPMI Set Protected-mode mov bl, TIMER_INTERRUPT ; Interrupt Vector command mov cx, cs mov edx, offset timer_isr int 31h pop eax gr30_init: cmp eax, 1 ; See if it's us jne derr ; Quit with an error if not ; * VGA mode 13h, 320x200, 256 colors ; * Initialize all variables before memory allocation mov Display_Width, 320 mov Display_Height, 200 mov Virtual_Display_Bytes, 320*200 mov Virtual_Display_Words, (320*200)/2 mov Virtual_Display_Dwords, (320*200)/4 mov eax, 13h int 10h ; * Allocate memory and setup the pointers gr100_init: mov eax, Virtual_Display_Bytes call malloc_ or eax, eax ; See if allocation error jz short derr mov eax, VIDEO_START ; debugging mov Virtual_Display_Ptr, eax mov ecx, Display_Height ; ECX = number of lines mov ebx, offset Line_Start_Table ; EBX -> line start table @@: mov [ebx], eax add eax, Display_Width add ebx, 4 loop @B mov Virtual_Display_End, eax ; -> the end of the buffer dood: xor eax, eax ; Clear out the virtual buffer mov edi, Virtual_Display_Ptr mov ecx, Virtual_Display_Dwords rep stosd mov Video_Access_Count, 1 mov Update_Display_Flag, 1 ; Starts any updates done: ret derr: or eax, -1 jmp done gr_init_ endp ; **************************************************************************** ; * int GR_STOP( void ) ; * Stops all graphics library processing ; * Given: nothing ; * Returns: 0 if all went well ; **************************************************************************** gr_stop_ proc near uses ebx mov Update_Display_Flag, 0 ; Stop any updates mov Video_Access_Count, 0 mov Lowest_X, -1 mov Lowest_Y, -1 mov Highest_X, 0 mov Highest_Y, 0 ; * Free any allocated memory xor eax, eax xchg Virtual_Display_Ptr, eax ; See if already setup or eax, eax jz short @F ; Skip if not call free_ ; * Restore timer interrupt cmp Old_Timer_Vector, 0 ; timer already installed? jz short @F mov edx, Old_Timer_Vector mov ecx, Old_Timer_Vector+4 mov eax, 205h ; EAX = DPMI Set Protected-mode mov bl, TIMER_INTERRUPT ; Interrupt Vector command int 31h ; * Get the old video mode, if need be @@: cmp byte ptr Old_Video_Mode, 0FFh ; been here before? je short @F mov eax, Old_Video_Mode int 10h mov byte ptr Old_Video_Mode, 0FFh @@: xor eax, eax ret gr_stop_ endp ; **************************************************************************** ; * int GR_SET_PIXEL( int EAX, int EDX, BYTE BL ) ; * Sets a pixel in the virtual buffer ; * Given: EAX = X coordinate ; * EDX = Y coordinate ; * BL = pixel color ; * Returns: EAX = 0 if all went well only if range checking is enabled ; **************************************************************************** gr_set_pixel_ proc near ifdef RANGE_CHECK cmp Virtual_Display_Ptr, 0 ; See if we are enabled jz derr cmp Display_Width, eax jbe derr cmp Display_Height, edx jbe derr endif cmp Lowest_X, eax jb short @F mov Lowest_X, eax @@: cmp Highest_X, eax ja short @F mov Highest_X, eax @@: cmp Lowest_Y, edx jb short @F mov Lowest_Y, edx @@: cmp Highest_Y, edx ja short @F mov Highest_Y, edx @@: mov edx, Line_Start_Table[edx*4] ; EDX -> start of display line add edx, eax mov [edx], bl ; Put the pixel inc Video_Access_Count ret ifdef RANGE_CHECK derr: or eax, -1 ret endif gr_set_pixel_ endp ; **************************************************************************** ; * int GR_RECT( int ECX, int EAX, int EDX, int ESI, BYTE BL ) ; * Set a rectangle to a color ; * Given: ECX,EAX = X1,Y1 of upper left corner ; * EDX,ESI = X2,Y2 of lower right corner, must be > EAX,EDX ; * BL = color ; * Returns: EAX = 0 if all went well only if range checking is enabled ; **************************************************************************** gr_rect_ proc near push edi ifdef RANGE_CHECK cmp Virtual_Display_Ptr, 0 ; See if we are enabled jz derr cmp Display_Width, ecx jbe derr cmp Display_Width, edx jbe derr cmp Display_Height, eax jbe derr cmp Display_Height, esi jbe derr endif cmp Lowest_X, ecx jb short @F mov Lowest_X, ecx @@: cmp Highest_X, edx ja short @F mov Highest_X, edx @@: cmp Lowest_Y, eax jb short @F mov Lowest_Y, eax @@: cmp Highest_Y, esi ja short @F mov Highest_Y, esi @@: inc Video_Access_Count mov edi, Line_Start_Table[eax*4] ; EDI -> start of display line add edi, ecx ; EDI -> upperleft corner of screen sub esi, eax ; ESI = height - 1 inc esi mov bh, bl ; EAX = the color in all four bytes mov eax, ebx shl eax, 16 mov ax, bx sub edx, ecx ; EDX = width of the rectangle - 1 inc edx mov ebx, Display_Width ; EBX = width of the display in bytes sub ebx, edx ; EBX = wrap around value to add to ; EDI after each STOS ror dx, 2 ; DL = width / 4 (will work up to 1023 pixels wide) shr dh, 6 ; DH = width % 4 for remainder xor ecx, ecx ; Clear the MSWord of ECX @@: mov cl, dl rep stosd mov cl, dh rep stosb add edi, ebx dec esi jnz @B pop edi ifdef RANGE_CHECK xor eax, eax endif ret ifdef RANGE_CHECK derr: or eax, -1 ret endif gr_rect_ endp end