; ============================================================================
;
; 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!
|