Tvůrce webu je i pro tebe! Postav třeba web. Bez grafika. Bez kodéra. Hned.
wz

5. Obsluha grafiky

Nyní se již dostáváme ke koncovému stavu obsluhy grafiky v souboru GRAPHIC.ASM:


; ============================================================================
;
;                         VEGASLOT - Graphic
;
; ============================================================================

SECTION		.text

; ------------- Constants

VESAModesMax	equ	256		; max. number of VESA videomodes

Screen1W	equ	320		; width of VGA screen
Screen1H	equ	200		; height of VGA screen
Screen2W	equ	800		; width of VESA screen
Screen2H	equ	600		; height of VESA screen

BMPHead		equ	54		; size of head of BMP picture
BMPPal		equ	256		; number of palettes in BMP picture
BMPPalSize	equ	BMPPal*4	; size of palettes in BMP file

					; size of BMP file
BMPData		equ	BMPHead + BMPPalSize ; start of data in BMP file
BMP1Size	equ	(Screen1W*Screen1H + BMPData + 3) & (~3) ; VGA
BMP2Size	equ	(Screen2W*Screen2H + BMPData + 3) & (~3) ; VESA
DataSize	equ	(BMP1Size+BMP2Size+15) & (~15) ; size of graphics

DarkLev		equ	8		; number of darkness level

; ------------- BMP file header (must be top-down direction!)
;
; BITMAPFILEHEADER (14 bytes):
;	0: (2) identifier = 4D42h = 'B','M'
;	2: (4) file size in bytes = 0FE38h/75738h = 65080/481080
;	6: (4) reserved = 0
;	10: (4) offset of the data bits = 436h = 1078
; BITMAPINFOHEADER (40 bytes):
;	14: (4) size of this structure = 28h = 40
;	18: (4) width = 140h/320h = 320/800
;	22: (4) height = 0FFFFFF38h/0FFFFFDA8h = -200/-600
;	26: (2) planes = 1
;	28: (2) number of bits per pixel = 8
;	30: (4) compression = 0 (=uncompressed)
;	34: (4) size of image = 0FA02h/75302h = 64002/480002
;	38: (4) X pixels per meter = 1710h = 5904
;	42: (4) Y pixels per meter = 1710h = 5904
;	46: (4) colors used = 0 (= all colors, e.g. 256)
;	50: (4) colors important = 0 (= all colors, e.g. 256)
; RGBQUAD (1024 bytes):
;	54: (256*4 = 1024) palettes B, G, R, X (X=unused)
; DATA (64002/480002 bytes):
;	1078: (320*200 = 64000 / 800*600 = 480000) bitmap data
;	65078 / 481078: (2) align = 0

; ----------------------------------------------------------------------------
;                              Init Graphic
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; OUTPUT:	CY = error, no VGA card
; DESTROY:	AX, CX
; ----------------------------------------------------------------------------

; ------------- Test VESA

InitGraph:	call	InitVESA	; init VESA
		jc	NoVESA		; error, no VESA

; ------------- Find required videomode (CX = number of videomodes)

		call	FindVESA	; find videomode
		jnc	InitGraph7	; videomode found OK

; ------------- Init VGA videomode (320x200/256)

NoVESA:		call	SetVGAMode	; set VGA mode
		jc	InitGraph9	; error, no VGA card

; ------------- Init parameters for VGA

InitGraph6:	call	InitVGAPar	; init VGA parameters

; ------------- Move VGA graphic up instead of VESA graphics

		call	MoveVGA		; move VGA graphics high
		jmp	short InitGraph8 ; init common parameters

; ------------- Switch to VESA videomode

InitGraph7:	call	SetVESAMode	; set VESA videomode
		jc	NoVESA		; error - cannot set videomode

; ------------- Prepare VESA parameters

		call	InitVESAPar	; init VESA parameters

; ------------- Calculate common parameters

InitGraph8:	call	InitGraphPar	; init common parameters

; ------------- Calculate offsets of symbols

		call	InitSymbOff	; init offsets of symbols

; ------------- Calculate offsets of digits

		call	InitDigOff	; init offsets of digits

; ------------- Init tables of dark colors

		call	InitDarkTab	; init tables of dark colors
		clc			; clear error flag
InitGraph9:	ret

; ----------------------------------------------------------------------------
;                              Init VESA
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; OUTPUT:	CX = number of videomodes
;		CY = error
; ----------------------------------------------------------------------------

; ------------- Push registers

InitVESA:	push	ax		; push AX
		push	bx		; push BX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		push	ds		; push DS
		push	es		; push ES

; ------------- Test VESA

		mov	al,0		; AL <- function code
		call	VESAInt		; test VESA
		jc	InitVESA8	; there is not VESA BIOS

; ------------- Check VESA version

		mov	ax,[VESA+4]	; VESA version
		mov	[VESAVer],ax	; store VESA version
		cmp	ax,102h		; minimal version 1.2
		jb	InitVESA8	; incorrect VESA version

; ------------- Store VESA videomodes table

		push	ds		; push DS
		pop	es		; ES <- DS
		mov	di,VESAModes	; table of VESA videomodes (dest.)
		lds	si,[VESA+14]	; table of VESA videomodes (source)
		xor	cx,cx		; CX <- counter of videomodes
		cld			; direction up
InitVESA2:	lodsw			; load videomode
		stosw			; save videomode
		inc	ax		; is it end of table?
		jz	InitVESA3	; end of table
		inc	cx		; increase number of videomodes
		cmp	cx,VESAModesMax	; is videomode table full?
		jb	InitVESA2	; next videomode
InitVESA3:	or	cx,cx		; test number of modes
		jnz	InitVESA9	; there are some videomodes, OK
InitVESA8:	xor	cx,cx		; CX <- 0 error, no videomode
		stc			; error flag

; ------------- Pop registers

InitVESA9:	pop	es		; pop ES
		pop	ds		; pop DS
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                           Find VESA videomode
; ----------------------------------------------------------------------------
; INPUT:	CX = number of videomodes
;		DS = data segment
; OUTPUT:	CY = error, videomode not found
; ----------------------------------------------------------------------------

; ------------- Push registers

FindVESA:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

; ------------- Load next videomode

		mov	si,VESAModes	; SI <- table of videomodes
FindVESA2:	cld			; direction up
		lodsw			; AX <- load videomode
		mov	[VESAMode],ax	; push videomode

; ------------- Get information to videomode

		push	cx		; push CX
		xchg	ax,cx		; CX <- required videomode
		mov	al,1		; AL <- function code
		call	VESAInt		; get videomode information
		pop	cx		; pop CX
		jc	FindVESA4	; error

; ------------- Is mode supported in hardware?

		test	byte [VESA],1	; is mode supported in hardware?
		jz	FindVESA4	; mode is not supported

; ------------- Is it graphic mode?

		test	byte [VESA],10h	; is it graphic videomode?
		jz	FindVESA4	; it is not graphic videomode

; ------------- Check segment of videomemory

		mov	ax,[VESA+8]	; window A start segment
		cmp	ax,0a000h	; minimal memory segment
		jb	FindVESA4	; invalid memory segment
		cmp	ax,0f000h	; maximal memory segment
		jae	FindVESA4	; invalid memory segment
		mov	[VideoSegm],ax	; store memory segment

; ------------- Check width, height and bites of videomode

		cmp	word [VESA+18],Screen2W ; check horizontal resolution
	        jne	FindVESA4	; invalid horizontal resolution
		cmp	word [VESA+20],Screen2H ; check vertical resolution
		jne	FindVESA4	; invalid horizontal resolution
		cmp	byte [VESA+25],8 ; check number of bits per pixel
		jne	FindVESA4	; invalid bits per pixel

; ------------- All OK, store other parameters of VESA videomode

		mov	ax,[VESA+16]	; bytes per scan line
		mov	[ScanLine],ax	; store bytes per scan line
		clc			; flag OK
		jmp	short FindVESA8	; all OK

; ------------- Next videomode

FindVESA4:	loop	FindVESA2	; next videomode
		stc			; error flag

; ------------- Pop registers

FindVESA8:	pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                           Set VGA videomode
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; OUTPUT:	CY = error
; ----------------------------------------------------------------------------

; ------------- Push registers

SetVGAMode:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX

; ------------- Set VGA videomode

		mov	ax,13h		; AH <- function, AL <- videomode
		call	Int10		; call Int 10h interrupt

; ------------- Check, if videomode is set OK
		
		mov	ah,0fh		; AH <- function code
		call	Int10		; call Int 10h interrupt
		cmp	al,13h		; is videomode OK?
		je	SetVGAMode2	; videomode is OK
		stc			; error flag

; ------------- Pop registers

SetVGAMode2:	pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                         Prepare VGA parameters
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; ----------------------------------------------------------------------------

InitVGAPar:	mov	word [VESAMap],0 ; current VESA memory window
		mov	word [ScanLine],320; store bytes per scan line
		mov	word [VideoSegm],0a000h ; videomemory segment
		mov	word [ScreenW],Screen1W ; width of VGA screen
		mov	word [ScreenH],Screen1H ; height of VGA screen
		ret

; ----------------------------------------------------------------------------
;                         Move VGA graphics high
; ----------------------------------------------------------------------------

; ------------- Push registers

MoveVGA:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI
		push	di		; push DI
		push	ds		; push DS
		push	es		; push ES

; ------------- Prepare segments

		mov	ds,[cs:GraphSeg]; DS <- old segment of VGA graphics
		mov	ax,BMP1Size	; size of VGA graphics
		mov	cl,4		; CL <- 4 number of rotations
		shr	ax,cl		; size of VESA graphics in segments
		add	[cs:GraphSeg],ax; new segment of VGA graphics
		mov	es,[cs:GraphSeg]; ES <- new segment of VGA graphics

; ------------- Prepare offsets

		mov	si,[cs:GraphOff]; SI <- old offset of VGA graphics
		sub	si,BMPData	; SI <- offset of VGA graphics
		mov	di,(BMP1Size & 0Fh) + BMPData ; new offset 
		mov	word [cs:GraphOff],di ; new offset of VGA graphics
		sub	di,BMPData	; DI <- new offset of VGA graphics

; ------------- Move VGA graphics

		mov	cx,BMP1Size/2	; size of VGA graphics inwords
		cld			; direction up
		rep	movsw		; moving VGA graphics up

; ------------- Pop registers

		pop	es		; pop ES
		pop	ds		; pop DS
		pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                          Set VESA videomode
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; OUTPUT:	CY = error
; ----------------------------------------------------------------------------

; ------------- Push registers

SetVESAMode:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX

; ------------- Set VESA videomode

		mov	al,2		; AL <- function code
		mov	bx,[VESAMode]	; BX <- VESA videomode
		call	VESAInt		; set VESA videomode
		jc	SetVESAMode2	; error - cannot set videomode

; ------------- Check VESA videomode

		mov	al,3		; AL <- function code
		call	VESAInt		; get VESA videomode
		jc	SetVESAMode2	; error
		cmp	bx,[VESAMode]	; is ot OK?
		je	SetVESAMode2	; it is OK
		stc			; error flag

; ------------- Pop registers

SetVESAMode2:	pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                         Prepare VESA parameters
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; ----------------------------------------------------------------------------

; ------------- Push registers

InitVESAPar:	push	ax		; push AX
		push	cx		; push CX

; ------------- Init VESA parameters

		mov	byte [UseVESA],1; flag - using VESA mode
		mov	ax,BMP1Size	; size of VGA graphics
		mov	cl,4		; CL <- 4 number of rotations
		shr	ax,cl		; size of VESA graphics in segments
		add	[GraphSeg],ax	; segment of VESA graphics
		mov	word [GraphOff],(BMP1Size & 0Fh) + BMPData ; offset
		mov	word [ScreenW],Screen2W ; width of VESA screen
		mov	word [ScreenH],Screen2H ; height of VESA screen

; ------------- Pop registers

		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                  Init common parameters of graphics
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; ----------------------------------------------------------------------------

; ------------- Push registers

InitGraphPar:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI

; ------------- Prepare registers

		mov	bx,1280		; BX <- prototypal videomode X
		mov	cx,960		; CX <- prototypal videomode Y
		mov	si,[ScreenW]	; SI <- width of screen
		mov	di,[ScreenH]	; DI <- height of screen

; ------------- Width and left coordinate of reel window

		mov	ax,256		; width of symbol in 1280x960
		mul	si	 	; * width of screen
		div	bx		; calculate width of symbol
		mov	[SymbolW],ax	; width of symbol
		mov	dx,3		; DX <- 3 symbols horizontal
		mul	dx		; width of reel window
		mov	[WindowW],ax	; width of reel window
		mov	dx,si		; DX <- width of screen
		sub	dx,ax		; DX = borders
		shr	dx,1		; one border
		mov	[WindowL],dx	; left coordinate of reel window

; ------------- Height and top coordinate of reel window

		mov	ax,192		; height of symbol in 1280x960
		mul	di	 	; * height of screen
		div	cx		; calculate height of symbol
		mov	[SymbolH],ax	; height of symbol
		mov	dx,3		; DX <- 3 symbols vertical
		mul	dx		; height of reel window
		mov	[WindowH],ax	; height of reel window
		mov	dx,di		; DX <- height of screen
		sub	dx,ax		; DX = borders
		shr	dx,1		; one border
		mov	[WindowT],dx	; top coordinate of reel window
		add	dx,ax		; DX <- bottom coordinate of window
		mov	[WindowB],dx	; bottom coordinate of reel window

; ------------- Dark borders

		mov	ax,128		; height of dark border in 1280x960
		mul	di		; * height of screen
		div	cx		; calculate height of border
		mov	[Border],ax	; height of border
		add	ax,[WindowT]	; AX <- line of top border
		mov	[BorderT],ax	; line of top border
		mov	ax,[WindowB]	; AX <- bottom of reel window
		sub	ax,[Border]	; AX <- line of bottom border
		mov	[BorderB],ax	; line of bottom border

; ------------- Width of digits

		mov	ax,64		; width of digit in 1280x960
		mul	si		; * width of screen
		div	bx		; calculate width of digit
		mov	[DigitW],ax	; width of digit

; ------------- Height of digits

		mov	ax,96		; height of digit in 1280x960
		mul	di		; * height of screen
		div	cx		; calculate height of digit
		mov	[DigitH],ax	; height of digit

; ------------- Left coordinate of first digit

		mov	ax,160		; left coordinate of digit in 1280x960
		mul	si		; * width of screen
		div	bx		; calculate left coordinate of digit
		mov	[DigitL],ax	; left coordinate of first digit

; ------------- Top coordinate of digits

		mov	ax,64		; top coordinate of digit in 1280x960
		mul	di		; * height of screen
		div	cx		; calculate top coordinate of digits
		mov	[DigitT],ax	; top coordinate of digits

; ------------- Pop registers

		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                        Init tables of dark colors
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; ----------------------------------------------------------------------------

; ------------- Push registers

InitDarkTab:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI

; ------------- Init tables

		mov	bl,255		; BL <- brightness level
		mov	di,DarkTab	; DI <- table of dark colors
		mov	cx,DarkLev	; CX <- number of dark levels
InitDarkTab2:	sub	bl,16		; BL <- new brightness level
		call	InitDark	; init table of dark colors
		add	di,256		; DI <- next dark color table
		loop	InitDarkTab2	; init next dark color table

; ------------- Init dark levels lines

		mov	cx,[ScreenH]	; CX <- height of screen
		xor	bx,bx		; BX <- 0 index of line
InitDarkTab3:	mov	byte [bx+Darkness],0ffh ; preset - full brightness
		mov	ax,[BorderT]	; AX <- top border
		dec	ax		; AX <- first line of top border
		sub	ax,bx		; AX <- distance from top border
		jle	InitDarkTab4	; it is below top border
		cmp	ax,[Border]	; is it in top border?
		jb	InitDarkTab5	; it is in top border
		jmp	short InitDarkTab6 ; it is above top border
InitDarkTab4:	mov	ax,bx		; AX <- line
		sub	ax,[BorderB]	; AX <- distance from bottom border
		jl	InitDarkTab6	; it is below reel window
		cmp	ax,[Border]	; is it in bottom border?
		jae	InitDarkTab6	; it is not in bottom border
InitDarkTab5:	mov	dx,DarkLev	; DX <- number of dark levels
		mul	dx		; distance * number of levels
		div	word [Border]	; / border -> index of level
		mov	[bx+Darkness],al ; index of dark level
InitDarkTab6:	inc	bx		; increase index of line
		loop	InitDarkTab3	; init next line

; ------------- Pop registers

		pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                      Init one table of dark colors
; ----------------------------------------------------------------------------
; INPUT:	BL = brightness level 0 to 255
;		DI = table of dark colors
; LOCALS:	SS:[BP-2] (2) brightness level (0 to 255)
;		SS:[BP-4] (2) required BLUE (0 to 255)
;		SS:[BP-6] (2) required GREEN (0 to 255)
;		SS:[BP-8] (2) required RED (0 to 255)
;		SS:[BP-10] (2) palette counter
;		SS:[BP-12] (2) offset of palettes (constant)
;		SS:[BP-14] (2) table of palettes (pointer)
;		DS = segment of palettes
;		DI = table of dark colors
; ----------------------------------------------------------------------------

; ------------- Push registers

InitDark:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP
		push	ds		; push DS

; ------------- Prepare locals

		mov	bp,sp		; BP <- stack pointer
		sub	sp,14		; space for localc

; ------------- Init locals

		mov	bh,0		; BX = brightness level
		mov	[bp-2],bx	; brightness level
		mov	word [bp-10],256 ; palette counter
		mov	ax,[cs:GraphOff]; AX <- offset of graphics
		sub	ax,BMPPalSize	; AX <- offset of palettes
		mov	[bp-12],ax	; offset of palettes
		mov	[bp-14],ax	; table of palettes
		mov	ds,[cs:GraphSeg]; DS <- segment of graphics
		cld

; ------------- Prepare required palettes

InitDark2:	mov	si,[bp-14]	; SI <- table of palettes
		lodsb			; AL <- BLUE
		mov	ah,0		; AX = BLUE
		mul	word [bp-2]	; * brightness
		mov	bx,255		; BX <- 255 max. value
		div	bx		; AX <- required BLUE
		mov	[bp-4],ax	; required BLUE

		lodsb			; AL <- GREEN
		mov	ah,0		; AX = GREEN
		mul	word [bp-2]	; * brightness
		mov	bx,255		; BX <- 255 max. value
		div	bx		; AX <- required GREEN
		mov	[bp-6],ax	; required GREEN

		lodsb			; AL <- RED
		mov	ah,0		; AX = RED
		mul	word [bp-2]	; * brightness
		mov	bx,255		; BX <- 255 max. value
		div	bx		; AX <- required RED
		mov	[bp-8],ax	; required RED

		inc	si		; skip flag byte
		mov	[bp-14],si	; new table of palettes

; ------------- Prepare to find nearest color

		mov	bx,3*255	; BX <- nearest interval
		mov	si,[bp-12]	; SI <- offset of palettes
		mov	cx,256		; palettes to test

; ------------- Calc interval of one color

InitDark3:	lodsb			; AL <- BLUE
		mov	ah,0		; AX = BLUE
		sub	ax,[bp-4]	; interval BLUE
		jns	InitDark4	; it is positive value
		neg	ax		; absolute value
InitDark4:	xchg	ax,dx		; DX <- accumulator

		lodsb			; AL <- GREEN
		mov	ah,0		; AX = GREEN
		sub	ax,[bp-6]	; interval GREEN
		jns	InitDark5	; it is positive value
		neg	ax		; absolute value
InitDark5:	add	dx,ax		; add to accumulator

		lodsb			; AL <- RED
		mov	ah,0		; AX = RED
		sub	ax,[bp-8]	; interval RED
		jns	InitDark6	; it is positive value
		neg	ax		; absolute value
InitDark6:	add	dx,ax		; add to accumulator

		inc	si		; skip flag byte

; ------------- Compare, if it is closer color

		cmp	dx,bx		; is it closer color?
		jae	InitDark7	; it is not closer color
		mov	bx,dx		; BX <- new nearest interval
		mov	ax,si		; AX <- pointer
		sub	ax,[bp-12]	; AX <- offset in table
		shr	ax,1
		shr	ax,1		; AX <- index of color + 1
		dec	ax		; AX <- index of color
		mov	[cs:di],al	; index of new color

; ------------- Test next color

InitDark7:	loop	InitDark3	; test next color

; ------------- Next color

		inc	di		; increase destination address
		dec	word [bp-10]	; palette counter
		jnz	InitDark2	; next color

; ------------- Pop registers

		mov	sp,bp		; pop SP
		pop	ds		; pop DS
		pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop 	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                           Calc source offset
; ----------------------------------------------------------------------------
; INPUT:	BX = X coordinate
;		CX = Y coordinate
; OUTPUT:	AX:SI = source offset
; ----------------------------------------------------------------------------

CalcSrc:	push	dx
		mov	ax,[cs:ScreenW]	; width of screen
		mul	cx		; DX:AX <- offset of line
		add	ax,bx		; add X coordinate
		adc	dx,0		; carry
		xchg	ax,si		; SI <- source offset LOW
		xchg	ax,dx		; AX <- source offset HIGH
		pop	dx
		ret

; ----------------------------------------------------------------------------
;                           Calc destination offset
; ----------------------------------------------------------------------------
; INPUT:	BX = X coordinate
;		CX = Y coordinate
; OUTPUT:	DX:DI = destination offset
; ----------------------------------------------------------------------------

CalcDest:	push	ax		; push AX
		mov	ax,[cs:ScanLine]; lenght of line of display
		mul	cx		; DX:AX <- offset of line
		add	ax,bx		; add X coordinate
		adc	dx,0		; carry
		xchg	ax,di		; DI <- destination offset LOW
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                     Display background of graphics
; ----------------------------------------------------------------------------
; INPUT:	DS = data segment
; ----------------------------------------------------------------------------

; ------------- Set palettes

DispGraph:	call	SetPalettes	; set palettes

; ------------- Push registers

		push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI

; ------------- Draw background picture

		xor	ax,ax		; AX <- 0 offset of source line HIGH
		xor	si,si		; SI <- 0 offset of source line LOW
		xor	dx,dx		; DX <- 0 destination offset HIGH
		xor	di,di		; DI <- 0 destination offset LOW
		mov	bx,[ScreenW]	; screen width
		mov	cx,[ScreenH]	; screen height
DispGraph2:	call	DrawLine	; draw one line
		loop	DispGraph2	; draw next line

; ------------- Pop registers

		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop 	bx		; pop BX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                              Set palettes
; ----------------------------------------------------------------------------

; ------------- Push registers

SetPalettes:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	ds		; push DS
		cli			; disable interrupt

; ------------- Address of palettes

		mov	si,[cs:GraphOff]; SI <- offset of graphics
		sub	si,BMPPalSize	; SI <- offset of palettes
		mov	ds,[cs:GraphSeg]; DS <- segment of graphics

; ------------- Set palette pointer

		mov	dx,3c8h		; DX <- palette pointer register
		mov	al,0		; AL <- first palette register
		out	dx,al		; set first palette register to 0

; ------------- Set palettes

		cld			; direction up
		inc	dx		; DX <- palette registers
		mov	cx,BMPPal	; CX <- number of palettes
SetPal2:	mov	al,[si+2]	; AL <- load RED from DS:SI+2
		shr	al,1		; AL / 2
		shr	al,1		; AL / 4
		out	dx,al		; set RED color component
                mov	al,[si+1]	; AL <- load GREEN from DS:SI+1
		shr	al,1		; AL / 2
		shr	al,1		; AL / 4
		out	dx,al		; set GREEN color component
		lodsb			; AL <- load BLUE from DS:SI
		shr	al,1		; AL / 2
		shr	al,1		; AL / 4
		out	dx,al		; set BLUE color component
		add	si,3		; shift to next palette
		loop	SetPal2		; set next palette

; ------------- Pop registers

		sti			; enable interrupt
		pop	ds		; pop DS
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                  Draw graphic line with normal brightness
; ----------------------------------------------------------------------------
; INPUT:	AX:SI = offset of source line
;		DX:DI = destination offset in videomemory
;		BX = length of line
; OUTPUT:	AX:SI = offset of next source line
;		DX:DI = destination offset of next line
; ----------------------------------------------------------------------------

; ------------- Push registers

DrawLine:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	si		; push SI
		push	ds		; push DS
		push	es		; push ES

; ------------- Push length of line

		push	bx		; push length of line

; ------------- Set memory window

		call	SetMap		; set memory window

; ------------- Source address

		mov	cx,si		; CX <- offset LOW of source line
		and	si,0fh		; normalize source offset
		add	si,[cs:GraphOff]; add offset of graphics
		ror	ax,1		; rotate AX right through carry
		rcr	cx,1		; CX <- carry 1 bit
		ror	ax,1		; rotate AX right through carry
		rcr	cx,1		; CX <- carry 1 bit
		ror	ax,1		; rotate AX right through carry
		rcr	cx,1		; CX <- carry 1 bit
		ror	ax,1		; rotate AX right through carry
		rcr	cx,1		; CX <- carry 1 bit
		add	cx,[cs:GraphSeg]; add segment of graphics
		mov	ds,cx		; DS <- source segment

; ------------- Length of first part of line

		inc	dx		; DX <- preset next memory window
		xor	cx,cx		; CX <- 0
		sub	cx,di		; CX <- bytes to end of window
		jz	DrawLine1	; offset = 0
		cmp	cx,bx		; will be end of window?
		jbe	DrawLine2	; there will be next window
DrawLine1:	mov	cx,bx		; CX <- length of line
		dec	dx		; DX <- return current memory window
DrawLine2:	sub	bx,cx		; BX = length of second part
		
; ------------- Copy first part of line

		cld			; direction up
		mov	es,[cs:VideoSegm] ; segment of videomemory
		shr	cx,1		; CX = length of line in words
		rep	movsw		; copy first part of line
		adc	cx,cx		; CX <- odd byte
		rep	movsb		; copy off byte of line

; ------------- Set next memory window

		call	SetMap		; set memory window

; ------------- Copy second part of line

		mov	cx,bx		; CX <- length of second part
		shr	cx,1		; CX = length of line in words
		rep	movsw		; copy first part of line
		adc	cx,cx		; CX <- odd byte
		rep	movsb		; copy off byte of line

; ------------- Increase destination offset

		pop	ax		; AX <- pop length of line
		mov	cx,[cs:ScanLine] ; length of line of display
		sub	cx,ax		; CX <- rest to end of line
		add	di,cx		; add rest to end of line
		adc	dx,0		; carry

; ------------- Pop registers

		pop	es		; pop ES
		pop	ds		; pop DS
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX

; ------------- Increase source offset

		add	si,[cs:ScreenW]	; width of screen
		adc	ax,0		; carry
		ret

; ----------------------------------------------------------------------------
;                  Draw graphic line with darkness
; ----------------------------------------------------------------------------
; INPUT:	AX:SI = offset of source line
;		DX:DI = destination offset in videomemory
;		BX = length of line
;		BP = table of dark colors
; OUTPUT:	AX:SI = offset of next source line
;		DX:DI = destination offset of next line
; ----------------------------------------------------------------------------

; ------------- Push registers

DrawDark:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	si		; push SI
		push	ds		; push DS
		push	es		; push ES

; ------------- Push length of line

		push	bx		; push length of line

; ------------- Set memory window

		call	SetMap		; set memory window

; ------------- Source address

		mov	cx,si		; CX <- offset LOW of source line
		and	si,0fh		; normalize source offset
		add	si,[cs:GraphOff]; add offset of graphics
		ror	ax,1		; rotate AX right through carry
		rcr	cx,1		; CX <- carry 1 bit
		ror	ax,1		; rotate AX right through carry
		rcr	cx,1		; CX <- carry 1 bit
		ror	ax,1		; rotate AX right through carry
		rcr	cx,1		; CX <- carry 1 bit
		ror	ax,1		; rotate AX right through carry
		rcr	cx,1		; CX <- carry 1 bit
		add	cx,[cs:GraphSeg]; add segment of graphics
		mov	ds,cx		; DS <- source segment

; ------------- Draw line

		cld			; direction up
		mov	cx,bx		; CX <- length of line
		mov	es,[cs:VideoSegm] ; segment of videomemory
		mov	ah,0		; AH <- 0
DrawDark2:	lodsb			; AX <- one byte
		push	bp		; push BP
		add	bp,ax		; BP <- address of dark byte
		mov	al,[cs:bp]	; AL <- dark byte
		pop	bp		; pop BP
		stosb			; store one byte
		or	di,di		; is it new segment?
		jnz	DrawDark3	; it is not new segment
		inc	dx		; increase memory window
		call	SetMap		; set new memory window
DrawDark3:	loop	DrawDark2	; next byte of line

; ------------- Increase destination offset

		pop	ax		; AX <- pop length of line
		mov	cx,[cs:ScanLine] ; length of line of display
		sub	cx,ax		; CX <- rest to end of line
		add	di,cx		; add rest to end of line
		adc	dx,0		; carry

; ------------- Pop registers

		pop	es		; pop ES
		pop	ds		; pop DS
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX

; ------------- Increase source offset

		add	si,[cs:ScreenW]	; width of screen
		adc	ax,0		; carry
		ret

; ----------------------------------------------------------------------------
;                        Set address of memory window
; ----------------------------------------------------------------------------
; INPUT:	DX = 64 KB segment address
; REMARK:	It can be called in VGA mode, VESAMap = 0 and memory < 64 KB
; ----------------------------------------------------------------------------

; ------------- Test, if address will change

SetMap:		cmp	dx,[cs:VESAMap]	; memory window changes?
		je	SetMap2		; memory window is not changed
		mov	[cs:VESAMap],dx	; store new memory window

; ------------- Push registers

		push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX

; ------------- Set new memory window

		mov	ax,4f05h	; AX <- function code
		xor	bx,bx		; BX <- 0 select window A
		call	Int10		; set memory window

; ------------- Pop registers

		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
SetMap2:	ret

; ----------------------------------------------------------------------------
;                            Call VESA services
; ----------------------------------------------------------------------------
; INPUT:        AL = function code
;		CS:[VESA} = input parameters
; OUTPUT:	CY=error (no SVGA or other error)
;		CS:[VESA] = output parameters
; ----------------------------------------------------------------------------

; ------------- Push registers

VESAInt:	push	ax		; push AX
		push	di		; push DI
		push	es		; push ES

; ------------- Call VESA service

		push	cs		; push CS
		pop	es		; ES <- CS program segment
		mov	di,VESA		; VESA data buffer
		mov	ah,4fh		; AH <- function code
%ifndef NOVESA
		call	Int10		; call VESA service
%endif

; ------------- Check return code

		cmp	ax,4fh		; is it VESA and is it OK?
		je	VESAInt2	; it is VESA and all is OK
		stc			; set error flag

; ------------- Pop registers

VESAInt2:	pop	es		; pop ES
		pop	di		; pop DI
		pop	ax		; pop AX
		ret

; ----------------------------------------------------------------------------
;                Call Int 10h interrupt with saving registers
; ----------------------------------------------------------------------------
; Notes: Some BIOSes destroy some registers, so we call it with saving them.
; ----------------------------------------------------------------------------

; ------------- Push registers

Int10:		pushf			; push flags
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP
		push	ds		; push DS
		push	es		; push ES

; ------------- Call INT 10h

		int	10h		; call INT 10h

; ------------- Pop registers

		pop	es		; pop ES
		pop	ds		; pop DS
		pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		popf			; pop flags
		ret

; ----------------------------------------------------------------------------
;                                    Data
; ----------------------------------------------------------------------------

OldVMode:	db	3		; old video mode

; ------------- Graphics

ScreenW:	dw	320		; width of currect screen
ScreenH:	dw	200		; height of current screen
GraphSeg:	dw	0		; segment of graphics
GraphOff:	dw	0		; offset of graphics
ScanLine:	dw	320		; bytes per scan line
VideoSegm:	dw	0a000h		; video segment (window A)

; ------------- VESA

UseVESA:	db	0		; flag, 1=use VESA mode
VESAVer:	dw	0		; VESA version (0102h = 1.2)
VESAMode:	dw	0		; VESA videomode
VESAMap:	dw	0ffffh		; VESA current page of memory

; ------------- Error texts

ErrorNoVGA:	db	"ERROR: Cannot find VGA graphics card!",13,10
		db	"       Press any key to quit...",13,10,0

; ------------- VESA (must not be in unitialized area, it is used
;                     when VGA graphics is not moved away yet)

VESA:		times	256 db 0	; VESA data buffer
VESAModes:	times	VESAModesMax dw 0 ; table of VESA videomodes

; ----------------------------------------------------------------------------
;          Uninitialized data (60 KB area in place of VGA graphics)
; ----------------------------------------------------------------------------

SECTION		.bss

; ------------- Dark color tables

DarkTab:	resb	DarkLev*256	; dark color tables
Darkness:	resb	Screen2H	; dark levels of lines
Darkness0:	resb	Screen2H	; dark levels of lines of reel 0
Darkness1:	resb	Screen2H	; dark levels of lines of reel 1
Darkness2:	resb	Screen2H	; dark levels of lines of reel 2
; Size of Darkness table must be big enough to hold lines of every videomode!

Část souboru PROGRAM.ASM pro vykreslení obrázku grafiky nahraďte kódem:


; ------------- Display background

GameStart:	call	SetPalettes	; set palettes

		call	DispGraph	; display background image

		mov	bx,[WindowL]	; BX <- left coordinate of reels
		mov	cx,[WindowT]	; CX <- top coordinate of reels
		call	CalcSrc		; calc source offset -> AX:SI
		call	CalcDest	; calc destination offset -> DX:DI
		mov	bx,[WindowW]	; BX <- width of reels
		mov	cx,[WindowH]	; CX <- height of reels
		mov	bp,DarkTab+5*256 ; BP <- dark table
GameStart2:	call	DrawDark	; draw dark line
		loop	GameStart2	; next line

		mov	ah,10h		; AH <- function code
		int	16h		; input character

		mov	ah,0		; AH <- function code
		mov	al,[OldVMode]	; AL <- old video mode
		call	Int10		; call Int 10h interrupt

		jmp	short Quit	; quit the program

Na konec souboru PROGRAM.ASM (před %include "GRAPHIC.ASM") doplňte:


InitSymbOff:	ret
InitDigOff:	ret

; ------------- Symbols

SymbolW:	dw	64		; width of one symbol
SymbolH:	dw	40		; height of one symbol
WindowW:	dw	192		; width of reel window
WindowH:	dw	120		; height of reel window
WindowL:	dw	64		; left coordinate of reel window
WindowT:	dw	40		; top coordinate of reel window
WindowB:	dw	160		; bottom coordinate of reel window

Border:		dw	5		; height of dark border
BorderT:	dw	0		; line of top border
BorderB:	dw	0		; line of bottom border

; ------------- Digits

DigitW:		dw	0		; width of one digit
DigitH:		dw	0		; height of one digit
DigitL:		dw	0		; left coordinate of first digit
DigitT:		dw	0		; top coordinate od digits
BankL:		dw	0		; left coordinate of BANK
WinL:		dw	0		; left coordinate of WIN
BetL:		dw	0		; left coordinate of BET
CredL:		dw	0		; left coordinate of CREDIT

V popisu obsluhy grafiky budeme pokračovat ve funkci InitGraph a to funkcí InitGraphPar. Tato funkce provede výpočet skutečných souřadnic grafických prvků v obrázku nezávisle na rozlišení videomódu. Výpočty se provádí ze souřadnic a rozměrů prvků známých při rozlišení 1280x960, údaje se přepočítávají na skutečné rozměry displeje. Jsou takto vypočítány rozměry symbolů válců, parametry okna válců, parametry oblasti tmavých okrajů na válcích a parametry číslic.

Na konci inicializace je volána funkce InitDarkTab, která vypočítá tabulky tmavých barev. Program vykresluje tmavé oblasti obrázku tak, že skutečné barvy před vykreslením nahradí tmavými odstíny barev. K dispozici je 8 odstínů tmavých barev odstupňovaných po 1/16 (tj. od plného až po poloviční jas). K tomu je potřeba připravit 8 tabulek po 256 bajtech, tabulky obsahují náhradní barvy s tmavším odstínem.

Jedna tabulka tmavých barev je vygenerována funkcí InitDark. Funkce prochází jednotlivé palety obrázku. U každé palety načte jas RGB složek a vypočítá jejich požadovanou novou hodnotu (po ztmavení). S touto hodnotou projde seznam palet a vyhledá paletu, která se požadované barvě co nejvíce blíží. Nalezenou barvu uloží do generované tabulky náhradních tmavých barev.

Po vygenerování všech tabulek tmavých barev sestaví funkce InitDarkTab tabulku jasů linek pro válce (tabulka Darness). Každá položka tabulky odpovídá jasu linky s danou souřadnicí Y a slouží k určení jasu linek válců v závislosti na souřadnici Y.

Hlavní vykreslovací funkcí grafiky je funkce DrawLine. Funkce zajišťuje přenesení části grafické linky ze zdrojového obrázku do videopaměti s tím, že musí obsluhovat přepínání 64KB oken videopaměti a segmentaci zdrojového obrázku. Vstupními parametry funkce je 32-bitový offset linky ve zdrojovém obrázku, 32-bitový offset ve videopaměti a délka linky.

Na počátku zajistí funkce nastavení výchozího okna videopaměti podle registru DX, který je vyšším registrem 32-bitového cílového offsetu (proto není podporována jiná granularita oken videopaměti než 64 KB). Ze zdrojového offsetu vypočítá zdrojovou adresu linky v obrázku ve formátu segment:offset. Linku rozdělí na 2 části - první část před hranicí 64 KB a druhá část za hranicí 64 KB. Přenese obě části linky s tím, že mezi částmi linky zajistí přepnutí okna videopaměti, je-li to potřeba.

Obdobně pracuje funkce DrawDark, která vykresluje grafickou linku podle dané tabulky tmavých barev. Na rozdíl od předešlé funkce pracuje méně efektivně, linku vykresluje jedním cyklem. Každý bod před vykreslením nahradí jeho tmavší variantou z dané tabulky tmavých barev. Přepínání paměťových oken řeší při přetečení cílového offsetu přes hranici 64 KB.

Funkčnost obsluhy grafiky ověříme v souboru PROGRAM.ASM voláním funkce DispGraph, která vykreslí obrázek pozadí přes celou pochu. Následuje překreslení oblasti válců tmavším odstínem barev voláním funkce DrawDark se zvolenou tabulkou tmavých barev, abychom ověřili správnost funkce pro vygenerování tmavých barev.

Zadáním symbolu NOVGA při překladu programu lze program přeložit tak, že použije VGA grafiku i když je VESA rozhraní k dispozici. V ukázkovém příkladu ke stažení je tato verze programu vygenerována pod názvem PROGVGA.EXE.

Download zdrojového kódu obsluhy grafiky

Zpět na aplikaci VEGASLOT