2030 lines
40 KiB
ArmAsm
2030 lines
40 KiB
ArmAsm
/* video.S
|
|
*
|
|
* Display adapter & video mode setup, version 2.13 (14-May-99)
|
|
*
|
|
* Copyright (C) 1995 -- 1998 Martin Mares <mj@ucw.cz>
|
|
* Based on the original setup.S code (C) Linus Torvalds and Mats Anderson
|
|
*
|
|
* Rewritten to use GNU 'as' by Chris Noe <stiker@northlink.com> May 1999
|
|
*
|
|
* For further information, look at Documentation/svga.txt.
|
|
*
|
|
*/
|
|
|
|
/* Enable autodetection of SVGA adapters and modes. */
|
|
#undef CONFIG_VIDEO_SVGA
|
|
|
|
/* Enable autodetection of VESA modes */
|
|
#define CONFIG_VIDEO_VESA
|
|
|
|
/* Enable compacting of mode table */
|
|
#define CONFIG_VIDEO_COMPACT
|
|
|
|
/* Retain screen contents when switching modes */
|
|
#define CONFIG_VIDEO_RETAIN
|
|
|
|
/* Enable local mode list */
|
|
#undef CONFIG_VIDEO_LOCAL
|
|
|
|
/* Force 400 scan lines for standard modes (hack to fix bad BIOS behaviour */
|
|
#undef CONFIG_VIDEO_400_HACK
|
|
|
|
/* Hack that lets you force specific BIOS mode ID and specific dimensions */
|
|
#undef CONFIG_VIDEO_GFX_HACK
|
|
#define VIDEO_GFX_BIOS_AX 0x4f02 /* 800x600 on ThinkPad */
|
|
#define VIDEO_GFX_BIOS_BX 0x0102
|
|
#define VIDEO_GFX_DUMMY_RESOLUTION 0x6425 /* 100x37 */
|
|
|
|
/* This code uses an extended set of video mode numbers. These include:
|
|
* Aliases for standard modes
|
|
* NORMAL_VGA (-1)
|
|
* EXTENDED_VGA (-2)
|
|
* ASK_VGA (-3)
|
|
* Video modes numbered by menu position -- NOT RECOMMENDED because of lack
|
|
* of compatibility when extending the table. These are between 0x00 and 0xff.
|
|
*/
|
|
#define VIDEO_FIRST_MENU 0x0000
|
|
|
|
/* Standard BIOS video modes (BIOS number + 0x0100) */
|
|
#define VIDEO_FIRST_BIOS 0x0100
|
|
|
|
/* VESA BIOS video modes (VESA number + 0x0200) */
|
|
#define VIDEO_FIRST_VESA 0x0200
|
|
|
|
/* Video7 special modes (BIOS number + 0x0900) */
|
|
#define VIDEO_FIRST_V7 0x0900
|
|
|
|
/* Special video modes */
|
|
#define VIDEO_FIRST_SPECIAL 0x0f00
|
|
#define VIDEO_80x25 0x0f00
|
|
#define VIDEO_8POINT 0x0f01
|
|
#define VIDEO_80x43 0x0f02
|
|
#define VIDEO_80x28 0x0f03
|
|
#define VIDEO_CURRENT_MODE 0x0f04
|
|
#define VIDEO_80x30 0x0f05
|
|
#define VIDEO_80x34 0x0f06
|
|
#define VIDEO_80x60 0x0f07
|
|
#define VIDEO_GFX_HACK 0x0f08
|
|
#define VIDEO_LAST_SPECIAL 0x0f09
|
|
|
|
/* Video modes given by resolution */
|
|
#define VIDEO_FIRST_RESOLUTION 0x1000
|
|
|
|
/* The "recalculate timings" flag */
|
|
#define VIDEO_RECALC 0x8000
|
|
|
|
/* Positions of various video parameters passed to the kernel */
|
|
/* (see also include/linux/tty.h) */
|
|
#define PARAM_CURSOR_POS 0x00
|
|
#define PARAM_VIDEO_PAGE 0x04
|
|
#define PARAM_VIDEO_MODE 0x06
|
|
#define PARAM_VIDEO_COLS 0x07
|
|
#define PARAM_VIDEO_EGA_BX 0x0a
|
|
#define PARAM_VIDEO_LINES 0x0e
|
|
#define PARAM_HAVE_VGA 0x0f
|
|
#define PARAM_FONT_POINTS 0x10
|
|
|
|
#define PARAM_LFB_WIDTH 0x12
|
|
#define PARAM_LFB_HEIGHT 0x14
|
|
#define PARAM_LFB_DEPTH 0x16
|
|
#define PARAM_LFB_BASE 0x18
|
|
#define PARAM_LFB_SIZE 0x1c
|
|
#define PARAM_LFB_LINELENGTH 0x24
|
|
#define PARAM_LFB_COLORS 0x26
|
|
#define PARAM_VESAPM_SEG 0x2e
|
|
#define PARAM_VESAPM_OFF 0x30
|
|
#define PARAM_LFB_PAGES 0x32
|
|
#define PARAM_VESA_ATTRIB 0x34
|
|
#define PARAM_CAPABILITIES 0x36
|
|
|
|
/* Define DO_STORE according to CONFIG_VIDEO_RETAIN */
|
|
#ifdef CONFIG_VIDEO_RETAIN
|
|
#define DO_STORE call store_screen
|
|
#else
|
|
#define DO_STORE
|
|
#endif /* CONFIG_VIDEO_RETAIN */
|
|
|
|
# This is the main entry point called by setup.S
|
|
# %ds *must* be pointing to the bootsector
|
|
video: pushw %ds # We use different segments
|
|
pushw %ds # FS contains original DS
|
|
popw %fs
|
|
pushw %cs # DS is equal to CS
|
|
popw %ds
|
|
pushw %cs # ES is equal to CS
|
|
popw %es
|
|
xorw %ax, %ax
|
|
movw %ax, %gs # GS is zero
|
|
cld
|
|
call basic_detect # Basic adapter type testing (EGA/VGA/MDA/CGA)
|
|
#ifdef CONFIG_VIDEO_SELECT
|
|
movw %fs:(0x01fa), %ax # User selected video mode
|
|
cmpw $ASK_VGA, %ax # Bring up the menu
|
|
jz vid2
|
|
|
|
call mode_set # Set the mode
|
|
jc vid1
|
|
|
|
leaw badmdt, %si # Invalid mode ID
|
|
call prtstr
|
|
vid2: call mode_menu
|
|
vid1:
|
|
#ifdef CONFIG_VIDEO_RETAIN
|
|
call restore_screen # Restore screen contents
|
|
#endif /* CONFIG_VIDEO_RETAIN */
|
|
call store_edid
|
|
#endif /* CONFIG_VIDEO_SELECT */
|
|
call mode_params # Store mode parameters
|
|
popw %ds # Restore original DS
|
|
ret
|
|
|
|
# Detect if we have CGA, MDA, EGA or VGA and pass it to the kernel.
|
|
basic_detect:
|
|
movb $0, %fs:(PARAM_HAVE_VGA)
|
|
movb $0x12, %ah # Check EGA/VGA
|
|
movb $0x10, %bl
|
|
int $0x10
|
|
movw %bx, %fs:(PARAM_VIDEO_EGA_BX) # Identifies EGA to the kernel
|
|
cmpb $0x10, %bl # No, it's a CGA/MDA/HGA card.
|
|
je basret
|
|
|
|
incb adapter
|
|
movw $0x1a00, %ax # Check EGA or VGA?
|
|
int $0x10
|
|
cmpb $0x1a, %al # 1a means VGA...
|
|
jne basret # anything else is EGA.
|
|
|
|
incb %fs:(PARAM_HAVE_VGA) # We've detected a VGA
|
|
incb adapter
|
|
basret: ret
|
|
|
|
# Store the video mode parameters for later usage by the kernel.
|
|
# This is done by asking the BIOS except for the rows/columns
|
|
# parameters in the default 80x25 mode -- these are set directly,
|
|
# because some very obscure BIOSes supply insane values.
|
|
mode_params:
|
|
#ifdef CONFIG_VIDEO_SELECT
|
|
cmpb $0, graphic_mode
|
|
jnz mopar_gr
|
|
#endif
|
|
movb $0x03, %ah # Read cursor position
|
|
xorb %bh, %bh
|
|
int $0x10
|
|
movw %dx, %fs:(PARAM_CURSOR_POS)
|
|
movb $0x0f, %ah # Read page/mode/width
|
|
int $0x10
|
|
movw %bx, %fs:(PARAM_VIDEO_PAGE)
|
|
movw %ax, %fs:(PARAM_VIDEO_MODE) # Video mode and screen width
|
|
cmpb $0x7, %al # MDA/HGA => segment differs
|
|
jnz mopar0
|
|
|
|
movw $0xb000, video_segment
|
|
mopar0: movw %gs:(0x485), %ax # Font size
|
|
movw %ax, %fs:(PARAM_FONT_POINTS) # (valid only on EGA/VGA)
|
|
movw force_size, %ax # Forced size?
|
|
orw %ax, %ax
|
|
jz mopar1
|
|
|
|
movb %ah, %fs:(PARAM_VIDEO_COLS)
|
|
movb %al, %fs:(PARAM_VIDEO_LINES)
|
|
ret
|
|
|
|
mopar1: movb $25, %al
|
|
cmpb $0, adapter # If we are on CGA/MDA/HGA, the
|
|
jz mopar2 # screen must have 25 lines.
|
|
|
|
movb %gs:(0x484), %al # On EGA/VGA, use the EGA+ BIOS
|
|
incb %al # location of max lines.
|
|
mopar2: movb %al, %fs:(PARAM_VIDEO_LINES)
|
|
ret
|
|
|
|
#ifdef CONFIG_VIDEO_SELECT
|
|
# Fetching of VESA frame buffer parameters
|
|
mopar_gr:
|
|
leaw modelist+1024, %di
|
|
movb $0x23, %fs:(PARAM_HAVE_VGA)
|
|
movw 16(%di), %ax
|
|
movw %ax, %fs:(PARAM_LFB_LINELENGTH)
|
|
movw 18(%di), %ax
|
|
movw %ax, %fs:(PARAM_LFB_WIDTH)
|
|
movw 20(%di), %ax
|
|
movw %ax, %fs:(PARAM_LFB_HEIGHT)
|
|
movb 25(%di), %al
|
|
movb $0, %ah
|
|
movw %ax, %fs:(PARAM_LFB_DEPTH)
|
|
movb 29(%di), %al
|
|
movb $0, %ah
|
|
movw %ax, %fs:(PARAM_LFB_PAGES)
|
|
movl 40(%di), %eax
|
|
movl %eax, %fs:(PARAM_LFB_BASE)
|
|
movl 31(%di), %eax
|
|
movl %eax, %fs:(PARAM_LFB_COLORS)
|
|
movl 35(%di), %eax
|
|
movl %eax, %fs:(PARAM_LFB_COLORS+4)
|
|
movw 0(%di), %ax
|
|
movw %ax, %fs:(PARAM_VESA_ATTRIB)
|
|
|
|
# get video mem size
|
|
leaw modelist+1024, %di
|
|
movw $0x4f00, %ax
|
|
int $0x10
|
|
xorl %eax, %eax
|
|
movw 18(%di), %ax
|
|
movl %eax, %fs:(PARAM_LFB_SIZE)
|
|
|
|
# store mode capabilities
|
|
movl 10(%di), %eax
|
|
movl %eax, %fs:(PARAM_CAPABILITIES)
|
|
|
|
# switching the DAC to 8-bit is for <= 8 bpp only
|
|
movw %fs:(PARAM_LFB_DEPTH), %ax
|
|
cmpw $8, %ax
|
|
jg dac_done
|
|
|
|
# get DAC switching capability
|
|
xorl %eax, %eax
|
|
movb 10(%di), %al
|
|
testb $1, %al
|
|
jz dac_set
|
|
|
|
# attempt to switch DAC to 8-bit
|
|
movw $0x4f08, %ax
|
|
movw $0x0800, %bx
|
|
int $0x10
|
|
cmpw $0x004f, %ax
|
|
jne dac_set
|
|
movb %bh, dac_size # store actual DAC size
|
|
|
|
dac_set:
|
|
# set color size to DAC size
|
|
movb dac_size, %al
|
|
movb %al, %fs:(PARAM_LFB_COLORS+0)
|
|
movb %al, %fs:(PARAM_LFB_COLORS+2)
|
|
movb %al, %fs:(PARAM_LFB_COLORS+4)
|
|
movb %al, %fs:(PARAM_LFB_COLORS+6)
|
|
|
|
# set color offsets to 0
|
|
movb $0, %fs:(PARAM_LFB_COLORS+1)
|
|
movb $0, %fs:(PARAM_LFB_COLORS+3)
|
|
movb $0, %fs:(PARAM_LFB_COLORS+5)
|
|
movb $0, %fs:(PARAM_LFB_COLORS+7)
|
|
|
|
dac_done:
|
|
# get protected mode interface informations
|
|
movw $0x4f0a, %ax
|
|
xorw %bx, %bx
|
|
xorw %di, %di
|
|
int $0x10
|
|
cmp $0x004f, %ax
|
|
jnz no_pm
|
|
|
|
movw %es, %fs:(PARAM_VESAPM_SEG)
|
|
movw %di, %fs:(PARAM_VESAPM_OFF)
|
|
no_pm: ret
|
|
|
|
# The video mode menu
|
|
mode_menu:
|
|
leaw keymsg, %si # "Return/Space/Timeout" message
|
|
call prtstr
|
|
call flush
|
|
nokey: call getkt
|
|
|
|
cmpb $0x0d, %al # ENTER ?
|
|
je listm # yes - manual mode selection
|
|
|
|
cmpb $0x20, %al # SPACE ?
|
|
je defmd1 # no - repeat
|
|
|
|
call beep
|
|
jmp nokey
|
|
|
|
defmd1: ret # No mode chosen? Default 80x25
|
|
|
|
listm: call mode_table # List mode table
|
|
listm0: leaw name_bann, %si # Print adapter name
|
|
call prtstr
|
|
movw card_name, %si
|
|
orw %si, %si
|
|
jnz an2
|
|
|
|
movb adapter, %al
|
|
leaw old_name, %si
|
|
orb %al, %al
|
|
jz an1
|
|
|
|
leaw ega_name, %si
|
|
decb %al
|
|
jz an1
|
|
|
|
leaw vga_name, %si
|
|
jmp an1
|
|
|
|
an2: call prtstr
|
|
leaw svga_name, %si
|
|
an1: call prtstr
|
|
leaw listhdr, %si # Table header
|
|
call prtstr
|
|
movb $0x30, %dl # DL holds mode number
|
|
leaw modelist, %si
|
|
lm1: cmpw $ASK_VGA, (%si) # End?
|
|
jz lm2
|
|
|
|
movb %dl, %al # Menu selection number
|
|
call prtchr
|
|
call prtsp2
|
|
lodsw
|
|
call prthw # Mode ID
|
|
call prtsp2
|
|
movb 0x1(%si), %al
|
|
call prtdec # Rows
|
|
movb $0x78, %al # the letter 'x'
|
|
call prtchr
|
|
lodsw
|
|
call prtdec # Columns
|
|
movb $0x0d, %al # New line
|
|
call prtchr
|
|
movb $0x0a, %al
|
|
call prtchr
|
|
incb %dl # Next character
|
|
cmpb $0x3a, %dl
|
|
jnz lm1
|
|
|
|
movb $0x61, %dl
|
|
jmp lm1
|
|
|
|
lm2: leaw prompt, %si # Mode prompt
|
|
call prtstr
|
|
leaw edit_buf, %di # Editor buffer
|
|
lm3: call getkey
|
|
cmpb $0x0d, %al # Enter?
|
|
jz lment
|
|
|
|
cmpb $0x08, %al # Backspace?
|
|
jz lmbs
|
|
|
|
cmpb $0x20, %al # Printable?
|
|
jc lm3
|
|
|
|
cmpw $edit_buf+4, %di # Enough space?
|
|
jz lm3
|
|
|
|
stosb
|
|
call prtchr
|
|
jmp lm3
|
|
|
|
lmbs: cmpw $edit_buf, %di # Backspace
|
|
jz lm3
|
|
|
|
decw %di
|
|
movb $0x08, %al
|
|
call prtchr
|
|
call prtspc
|
|
movb $0x08, %al
|
|
call prtchr
|
|
jmp lm3
|
|
|
|
lment: movb $0, (%di)
|
|
leaw crlft, %si
|
|
call prtstr
|
|
leaw edit_buf, %si
|
|
cmpb $0, (%si) # Empty string = default mode
|
|
jz lmdef
|
|
|
|
cmpb $0, 1(%si) # One character = menu selection
|
|
jz mnusel
|
|
|
|
cmpw $0x6373, (%si) # "scan" => mode scanning
|
|
jnz lmhx
|
|
|
|
cmpw $0x6e61, 2(%si)
|
|
jz lmscan
|
|
|
|
lmhx: xorw %bx, %bx # Else => mode ID in hex
|
|
lmhex: lodsb
|
|
orb %al, %al
|
|
jz lmuse1
|
|
|
|
subb $0x30, %al
|
|
jc lmbad
|
|
|
|
cmpb $10, %al
|
|
jc lmhx1
|
|
|
|
subb $7, %al
|
|
andb $0xdf, %al
|
|
cmpb $10, %al
|
|
jc lmbad
|
|
|
|
cmpb $16, %al
|
|
jnc lmbad
|
|
|
|
lmhx1: shlw $4, %bx
|
|
orb %al, %bl
|
|
jmp lmhex
|
|
|
|
lmuse1: movw %bx, %ax
|
|
jmp lmuse
|
|
|
|
mnusel: lodsb # Menu selection
|
|
xorb %ah, %ah
|
|
subb $0x30, %al
|
|
jc lmbad
|
|
|
|
cmpb $10, %al
|
|
jc lmuse
|
|
|
|
cmpb $0x61-0x30, %al
|
|
jc lmbad
|
|
|
|
subb $0x61-0x30-10, %al
|
|
cmpb $36, %al
|
|
jnc lmbad
|
|
|
|
lmuse: call mode_set
|
|
jc lmdef
|
|
|
|
lmbad: leaw unknt, %si
|
|
call prtstr
|
|
jmp lm2
|
|
lmscan: cmpb $0, adapter # Scanning only on EGA/VGA
|
|
jz lmbad
|
|
|
|
movw $0, mt_end # Scanning of modes is
|
|
movb $1, scanning # done as new autodetection.
|
|
call mode_table
|
|
jmp listm0
|
|
lmdef: ret
|
|
|
|
# Additional parts of mode_set... (relative jumps, you know)
|
|
setv7: # Video7 extended modes
|
|
DO_STORE
|
|
subb $VIDEO_FIRST_V7>>8, %bh
|
|
movw $0x6f05, %ax
|
|
int $0x10
|
|
stc
|
|
ret
|
|
|
|
_setrec: jmp setrec # Ugly...
|
|
_set_80x25: jmp set_80x25
|
|
|
|
# Aliases for backward compatibility.
|
|
setalias:
|
|
movw $VIDEO_80x25, %ax
|
|
incw %bx
|
|
jz mode_set
|
|
|
|
movb $VIDEO_8POINT-VIDEO_FIRST_SPECIAL, %al
|
|
incw %bx
|
|
jnz setbad # Fall-through!
|
|
|
|
# Setting of user mode (AX=mode ID) => CF=success
|
|
mode_set:
|
|
movw %ax, %fs:(0x01fa) # Store mode for use in acpi_wakeup.S
|
|
movw %ax, %bx
|
|
cmpb $0xff, %ah
|
|
jz setalias
|
|
|
|
testb $VIDEO_RECALC>>8, %ah
|
|
jnz _setrec
|
|
|
|
cmpb $VIDEO_FIRST_RESOLUTION>>8, %ah
|
|
jnc setres
|
|
|
|
cmpb $VIDEO_FIRST_SPECIAL>>8, %ah
|
|
jz setspc
|
|
|
|
cmpb $VIDEO_FIRST_V7>>8, %ah
|
|
jz setv7
|
|
|
|
cmpb $VIDEO_FIRST_VESA>>8, %ah
|
|
jnc check_vesa
|
|
|
|
orb %ah, %ah
|
|
jz setmenu
|
|
|
|
decb %ah
|
|
jz setbios
|
|
|
|
setbad: clc
|
|
movb $0, do_restore # The screen needn't be restored
|
|
ret
|
|
|
|
setvesa:
|
|
DO_STORE
|
|
subb $VIDEO_FIRST_VESA>>8, %bh
|
|
movw $0x4f02, %ax # VESA BIOS mode set call
|
|
int $0x10
|
|
cmpw $0x004f, %ax # AL=4f if implemented
|
|
jnz setbad # AH=0 if OK
|
|
|
|
stc
|
|
ret
|
|
|
|
setbios:
|
|
DO_STORE
|
|
int $0x10 # Standard BIOS mode set call
|
|
pushw %bx
|
|
movb $0x0f, %ah # Check if really set
|
|
int $0x10
|
|
popw %bx
|
|
cmpb %bl, %al
|
|
jnz setbad
|
|
|
|
stc
|
|
ret
|
|
|
|
setspc: xorb %bh, %bh # Set special mode
|
|
cmpb $VIDEO_LAST_SPECIAL-VIDEO_FIRST_SPECIAL, %bl
|
|
jnc setbad
|
|
|
|
addw %bx, %bx
|
|
jmp *spec_inits(%bx)
|
|
|
|
setmenu:
|
|
orb %al, %al # 80x25 is an exception
|
|
jz _set_80x25
|
|
|
|
pushw %bx # Set mode chosen from menu
|
|
call mode_table # Build the mode table
|
|
popw %ax
|
|
shlw $2, %ax
|
|
addw %ax, %si
|
|
cmpw %di, %si
|
|
jnc setbad
|
|
|
|
movw (%si), %ax # Fetch mode ID
|
|
_m_s: jmp mode_set
|
|
|
|
setres: pushw %bx # Set mode chosen by resolution
|
|
call mode_table
|
|
popw %bx
|
|
xchgb %bl, %bh
|
|
setr1: lodsw
|
|
cmpw $ASK_VGA, %ax # End of the list?
|
|
jz setbad
|
|
|
|
lodsw
|
|
cmpw %bx, %ax
|
|
jnz setr1
|
|
|
|
movw -4(%si), %ax # Fetch mode ID
|
|
jmp _m_s
|
|
|
|
check_vesa:
|
|
leaw modelist+1024, %di
|
|
subb $VIDEO_FIRST_VESA>>8, %bh
|
|
movw %bx, %cx # Get mode information structure
|
|
movw $0x4f01, %ax
|
|
int $0x10
|
|
addb $VIDEO_FIRST_VESA>>8, %bh
|
|
cmpw $0x004f, %ax
|
|
jnz setbad
|
|
|
|
movb (%di), %al # Check capabilities.
|
|
andb $0x19, %al
|
|
cmpb $0x09, %al
|
|
jz setvesa # This is a text mode
|
|
|
|
movb (%di), %al # Check capabilities.
|
|
andb $0x99, %al
|
|
cmpb $0x99, %al
|
|
jnz _setbad # Doh! No linear frame buffer.
|
|
|
|
subb $VIDEO_FIRST_VESA>>8, %bh
|
|
orw $0x4000, %bx # Use linear frame buffer
|
|
movw $0x4f02, %ax # VESA BIOS mode set call
|
|
int $0x10
|
|
cmpw $0x004f, %ax # AL=4f if implemented
|
|
jnz _setbad # AH=0 if OK
|
|
|
|
movb $1, graphic_mode # flag graphic mode
|
|
movb $0, do_restore # no screen restore
|
|
stc
|
|
ret
|
|
|
|
_setbad: jmp setbad # Ugly...
|
|
|
|
# Recalculate vertical display end registers -- this fixes various
|
|
# inconsistencies of extended modes on many adapters. Called when
|
|
# the VIDEO_RECALC flag is set in the mode ID.
|
|
|
|
setrec: subb $VIDEO_RECALC>>8, %ah # Set the base mode
|
|
call mode_set
|
|
jnc rct3
|
|
|
|
movw %gs:(0x485), %ax # Font size in pixels
|
|
movb %gs:(0x484), %bl # Number of rows
|
|
incb %bl
|
|
mulb %bl # Number of visible
|
|
decw %ax # scan lines - 1
|
|
movw $0x3d4, %dx
|
|
movw %ax, %bx
|
|
movb $0x12, %al # Lower 8 bits
|
|
movb %bl, %ah
|
|
outw %ax, %dx
|
|
movb $0x07, %al # Bits 8 and 9 in the overflow register
|
|
call inidx
|
|
xchgb %al, %ah
|
|
andb $0xbd, %ah
|
|
shrb %bh
|
|
jnc rct1
|
|
orb $0x02, %ah
|
|
rct1: shrb %bh
|
|
jnc rct2
|
|
orb $0x40, %ah
|
|
rct2: movb $0x07, %al
|
|
outw %ax, %dx
|
|
stc
|
|
rct3: ret
|
|
|
|
# Table of routines for setting of the special modes.
|
|
spec_inits:
|
|
.word set_80x25
|
|
.word set_8pixel
|
|
.word set_80x43
|
|
.word set_80x28
|
|
.word set_current
|
|
.word set_80x30
|
|
.word set_80x34
|
|
.word set_80x60
|
|
.word set_gfx
|
|
|
|
# Set the 80x25 mode. If already set, do nothing.
|
|
set_80x25:
|
|
movw $0x5019, force_size # Override possibly broken BIOS
|
|
use_80x25:
|
|
#ifdef CONFIG_VIDEO_400_HACK
|
|
movw $0x1202, %ax # Force 400 scan lines
|
|
movb $0x30, %bl
|
|
int $0x10
|
|
#else
|
|
movb $0x0f, %ah # Get current mode ID
|
|
int $0x10
|
|
cmpw $0x5007, %ax # Mode 7 (80x25 mono) is the only one available
|
|
jz st80 # on CGA/MDA/HGA and is also available on EGAM
|
|
|
|
cmpw $0x5003, %ax # Unknown mode, force 80x25 color
|
|
jnz force3
|
|
|
|
st80: cmpb $0, adapter # CGA/MDA/HGA => mode 3/7 is always 80x25
|
|
jz set80
|
|
|
|
movb %gs:(0x0484), %al # This is EGA+ -- beware of 80x50 etc.
|
|
orb %al, %al # Some buggy BIOS'es set 0 rows
|
|
jz set80
|
|
|
|
cmpb $24, %al # It's hopefully correct
|
|
jz set80
|
|
#endif /* CONFIG_VIDEO_400_HACK */
|
|
force3: DO_STORE
|
|
movw $0x0003, %ax # Forced set
|
|
int $0x10
|
|
set80: stc
|
|
ret
|
|
|
|
# Set the 80x50/80x43 8-pixel mode. Simple BIOS calls.
|
|
set_8pixel:
|
|
DO_STORE
|
|
call use_80x25 # The base is 80x25
|
|
set_8pt:
|
|
movw $0x1112, %ax # Use 8x8 font
|
|
xorb %bl, %bl
|
|
int $0x10
|
|
movw $0x1200, %ax # Use alternate print screen
|
|
movb $0x20, %bl
|
|
int $0x10
|
|
movw $0x1201, %ax # Turn off cursor emulation
|
|
movb $0x34, %bl
|
|
int $0x10
|
|
movb $0x01, %ah # Define cursor scan lines 6-7
|
|
movw $0x0607, %cx
|
|
int $0x10
|
|
set_current:
|
|
stc
|
|
ret
|
|
|
|
# Set the 80x28 mode. This mode works on all VGA's, because it's a standard
|
|
# 80x25 mode with 14-point fonts instead of 16-point.
|
|
set_80x28:
|
|
DO_STORE
|
|
call use_80x25 # The base is 80x25
|
|
set14: movw $0x1111, %ax # Use 9x14 font
|
|
xorb %bl, %bl
|
|
int $0x10
|
|
movb $0x01, %ah # Define cursor scan lines 11-12
|
|
movw $0x0b0c, %cx
|
|
int $0x10
|
|
stc
|
|
ret
|
|
|
|
# Set the 80x43 mode. This mode is works on all VGA's.
|
|
# It's a 350-scanline mode with 8-pixel font.
|
|
set_80x43:
|
|
DO_STORE
|
|
movw $0x1201, %ax # Set 350 scans
|
|
movb $0x30, %bl
|
|
int $0x10
|
|
movw $0x0003, %ax # Reset video mode
|
|
int $0x10
|
|
jmp set_8pt # Use 8-pixel font
|
|
|
|
# Set the 80x30 mode (all VGA's). 480 scanlines, 16-pixel font.
|
|
set_80x30:
|
|
call use_80x25 # Start with real 80x25
|
|
DO_STORE
|
|
movw $0x3cc, %dx # Get CRTC port
|
|
inb %dx, %al
|
|
movb $0xd4, %dl
|
|
rorb %al # Mono or color?
|
|
jc set48a
|
|
|
|
movb $0xb4, %dl
|
|
set48a: movw $0x0c11, %ax # Vertical sync end (also unlocks CR0-7)
|
|
call outidx
|
|
movw $0x0b06, %ax # Vertical total
|
|
call outidx
|
|
movw $0x3e07, %ax # (Vertical) overflow
|
|
call outidx
|
|
movw $0xea10, %ax # Vertical sync start
|
|
call outidx
|
|
movw $0xdf12, %ax # Vertical display end
|
|
call outidx
|
|
movw $0xe715, %ax # Vertical blank start
|
|
call outidx
|
|
movw $0x0416, %ax # Vertical blank end
|
|
call outidx
|
|
pushw %dx
|
|
movb $0xcc, %dl # Misc output register (read)
|
|
inb %dx, %al
|
|
movb $0xc2, %dl # (write)
|
|
andb $0x0d, %al # Preserve clock select bits and color bit
|
|
orb $0xe2, %al # Set correct sync polarity
|
|
outb %al, %dx
|
|
popw %dx
|
|
movw $0x501e, force_size
|
|
stc # That's all.
|
|
ret
|
|
|
|
# Set the 80x34 mode (all VGA's). 480 scans, 14-pixel font.
|
|
set_80x34:
|
|
call set_80x30 # Set 480 scans
|
|
call set14 # And 14-pt font
|
|
movw $0xdb12, %ax # VGA vertical display end
|
|
movw $0x5022, force_size
|
|
setvde: call outidx
|
|
stc
|
|
ret
|
|
|
|
# Set the 80x60 mode (all VGA's). 480 scans, 8-pixel font.
|
|
set_80x60:
|
|
call set_80x30 # Set 480 scans
|
|
call set_8pt # And 8-pt font
|
|
movw $0xdf12, %ax # VGA vertical display end
|
|
movw $0x503c, force_size
|
|
jmp setvde
|
|
|
|
# Special hack for ThinkPad graphics
|
|
set_gfx:
|
|
#ifdef CONFIG_VIDEO_GFX_HACK
|
|
movw $VIDEO_GFX_BIOS_AX, %ax
|
|
movw $VIDEO_GFX_BIOS_BX, %bx
|
|
int $0x10
|
|
movw $VIDEO_GFX_DUMMY_RESOLUTION, force_size
|
|
stc
|
|
#endif
|
|
ret
|
|
|
|
#ifdef CONFIG_VIDEO_RETAIN
|
|
|
|
# Store screen contents to temporary buffer.
|
|
store_screen:
|
|
cmpb $0, do_restore # Already stored?
|
|
jnz stsr
|
|
|
|
testb $CAN_USE_HEAP, loadflags # Have we space for storing?
|
|
jz stsr
|
|
|
|
pushw %ax
|
|
pushw %bx
|
|
pushw force_size # Don't force specific size
|
|
movw $0, force_size
|
|
call mode_params # Obtain params of current mode
|
|
popw force_size
|
|
movb %fs:(PARAM_VIDEO_LINES), %ah
|
|
movb %fs:(PARAM_VIDEO_COLS), %al
|
|
movw %ax, %bx # BX=dimensions
|
|
mulb %ah
|
|
movw %ax, %cx # CX=number of characters
|
|
addw %ax, %ax # Calculate image size
|
|
addw $modelist+1024+4, %ax
|
|
cmpw heap_end_ptr, %ax
|
|
jnc sts1 # Unfortunately, out of memory
|
|
|
|
movw %fs:(PARAM_CURSOR_POS), %ax # Store mode params
|
|
leaw modelist+1024, %di
|
|
stosw
|
|
movw %bx, %ax
|
|
stosw
|
|
pushw %ds # Store the screen
|
|
movw video_segment, %ds
|
|
xorw %si, %si
|
|
rep
|
|
movsw
|
|
popw %ds
|
|
incb do_restore # Screen will be restored later
|
|
sts1: popw %bx
|
|
popw %ax
|
|
stsr: ret
|
|
|
|
# Restore screen contents from temporary buffer.
|
|
restore_screen:
|
|
cmpb $0, do_restore # Has the screen been stored?
|
|
jz res1
|
|
|
|
call mode_params # Get parameters of current mode
|
|
movb %fs:(PARAM_VIDEO_LINES), %cl
|
|
movb %fs:(PARAM_VIDEO_COLS), %ch
|
|
leaw modelist+1024, %si # Screen buffer
|
|
lodsw # Set cursor position
|
|
movw %ax, %dx
|
|
cmpb %cl, %dh
|
|
jc res2
|
|
|
|
movb %cl, %dh
|
|
decb %dh
|
|
res2: cmpb %ch, %dl
|
|
jc res3
|
|
|
|
movb %ch, %dl
|
|
decb %dl
|
|
res3: movb $0x02, %ah
|
|
movb $0x00, %bh
|
|
int $0x10
|
|
lodsw # Display size
|
|
movb %ah, %dl # DL=number of lines
|
|
movb $0, %ah # BX=phys. length of orig. line
|
|
movw %ax, %bx
|
|
cmpb %cl, %dl # Too many?
|
|
jc res4
|
|
|
|
pushw %ax
|
|
movb %dl, %al
|
|
subb %cl, %al
|
|
mulb %bl
|
|
addw %ax, %si
|
|
addw %ax, %si
|
|
popw %ax
|
|
movb %cl, %dl
|
|
res4: cmpb %ch, %al # Too wide?
|
|
jc res5
|
|
|
|
movb %ch, %al # AX=width of src. line
|
|
res5: movb $0, %cl
|
|
xchgb %ch, %cl
|
|
movw %cx, %bp # BP=width of dest. line
|
|
pushw %es
|
|
movw video_segment, %es
|
|
xorw %di, %di # Move the data
|
|
addw %bx, %bx # Convert BX and BP to _bytes_
|
|
addw %bp, %bp
|
|
res6: pushw %si
|
|
pushw %di
|
|
movw %ax, %cx
|
|
rep
|
|
movsw
|
|
popw %di
|
|
popw %si
|
|
addw %bp, %di
|
|
addw %bx, %si
|
|
decb %dl
|
|
jnz res6
|
|
|
|
popw %es # Done
|
|
res1: ret
|
|
#endif /* CONFIG_VIDEO_RETAIN */
|
|
|
|
# Write to indexed VGA register (AL=index, AH=data, DX=index reg. port)
|
|
outidx: outb %al, %dx
|
|
pushw %ax
|
|
movb %ah, %al
|
|
incw %dx
|
|
outb %al, %dx
|
|
decw %dx
|
|
popw %ax
|
|
ret
|
|
|
|
# Build the table of video modes (stored after the setup.S code at the
|
|
# `modelist' label. Each video mode record looks like:
|
|
# .word MODE-ID (our special mode ID (see above))
|
|
# .byte rows (number of rows)
|
|
# .byte columns (number of columns)
|
|
# Returns address of the end of the table in DI, the end is marked
|
|
# with a ASK_VGA ID.
|
|
mode_table:
|
|
movw mt_end, %di # Already filled?
|
|
orw %di, %di
|
|
jnz mtab1x
|
|
|
|
leaw modelist, %di # Store standard modes:
|
|
movl $VIDEO_80x25 + 0x50190000, %eax # The 80x25 mode (ALL)
|
|
stosl
|
|
movb adapter, %al # CGA/MDA/HGA -- no more modes
|
|
orb %al, %al
|
|
jz mtabe
|
|
|
|
decb %al
|
|
jnz mtabv
|
|
|
|
movl $VIDEO_8POINT + 0x502b0000, %eax # The 80x43 EGA mode
|
|
stosl
|
|
jmp mtabe
|
|
|
|
mtab1x: jmp mtab1
|
|
|
|
mtabv: leaw vga_modes, %si # All modes for std VGA
|
|
movw $vga_modes_end-vga_modes, %cx
|
|
rep # I'm unable to use movsw as I don't know how to store a half
|
|
movsb # of the expression above to cx without using explicit shr.
|
|
|
|
cmpb $0, scanning # Mode scan requested?
|
|
jz mscan1
|
|
|
|
call mode_scan
|
|
mscan1:
|
|
|
|
#ifdef CONFIG_VIDEO_LOCAL
|
|
call local_modes
|
|
#endif /* CONFIG_VIDEO_LOCAL */
|
|
|
|
#ifdef CONFIG_VIDEO_VESA
|
|
call vesa_modes # Detect VESA VGA modes
|
|
#endif /* CONFIG_VIDEO_VESA */
|
|
|
|
#ifdef CONFIG_VIDEO_SVGA
|
|
cmpb $0, scanning # Bypass when scanning
|
|
jnz mscan2
|
|
|
|
call svga_modes # Detect SVGA cards & modes
|
|
mscan2:
|
|
#endif /* CONFIG_VIDEO_SVGA */
|
|
|
|
mtabe:
|
|
|
|
#ifdef CONFIG_VIDEO_COMPACT
|
|
leaw modelist, %si
|
|
movw %di, %dx
|
|
movw %si, %di
|
|
cmt1: cmpw %dx, %si # Scan all modes
|
|
jz cmt2
|
|
|
|
leaw modelist, %bx # Find in previous entries
|
|
movw 2(%si), %cx
|
|
cmt3: cmpw %bx, %si
|
|
jz cmt4
|
|
|
|
cmpw 2(%bx), %cx # Found => don't copy this entry
|
|
jz cmt5
|
|
|
|
addw $4, %bx
|
|
jmp cmt3
|
|
|
|
cmt4: movsl # Copy entry
|
|
jmp cmt1
|
|
|
|
cmt5: addw $4, %si # Skip entry
|
|
jmp cmt1
|
|
|
|
cmt2:
|
|
#endif /* CONFIG_VIDEO_COMPACT */
|
|
|
|
movw $ASK_VGA, (%di) # End marker
|
|
movw %di, mt_end
|
|
mtab1: leaw modelist, %si # SI=mode list, DI=list end
|
|
ret0: ret
|
|
|
|
# Modes usable on all standard VGAs
|
|
vga_modes:
|
|
.word VIDEO_8POINT
|
|
.word 0x5032 # 80x50
|
|
.word VIDEO_80x43
|
|
.word 0x502b # 80x43
|
|
.word VIDEO_80x28
|
|
.word 0x501c # 80x28
|
|
.word VIDEO_80x30
|
|
.word 0x501e # 80x30
|
|
.word VIDEO_80x34
|
|
.word 0x5022 # 80x34
|
|
.word VIDEO_80x60
|
|
.word 0x503c # 80x60
|
|
#ifdef CONFIG_VIDEO_GFX_HACK
|
|
.word VIDEO_GFX_HACK
|
|
.word VIDEO_GFX_DUMMY_RESOLUTION
|
|
#endif
|
|
|
|
vga_modes_end:
|
|
# Detect VESA modes.
|
|
|
|
#ifdef CONFIG_VIDEO_VESA
|
|
vesa_modes:
|
|
cmpb $2, adapter # VGA only
|
|
jnz ret0
|
|
|
|
movw %di, %bp # BP=original mode table end
|
|
addw $0x200, %di # Buffer space
|
|
movw $0x4f00, %ax # VESA Get card info call
|
|
int $0x10
|
|
movw %bp, %di
|
|
cmpw $0x004f, %ax # Successful?
|
|
jnz ret0
|
|
|
|
cmpw $0x4556, 0x200(%di)
|
|
jnz ret0
|
|
|
|
cmpw $0x4153, 0x202(%di)
|
|
jnz ret0
|
|
|
|
movw $vesa_name, card_name # Set name to "VESA VGA"
|
|
pushw %gs
|
|
lgsw 0x20e(%di), %si # GS:SI=mode list
|
|
movw $128, %cx # Iteration limit
|
|
vesa1:
|
|
# gas version 2.9.1, using BFD version 2.9.1.0.23 buggers the next inst.
|
|
# XXX: lodsw %gs:(%si), %ax # Get next mode in the list
|
|
gs; lodsw
|
|
cmpw $0xffff, %ax # End of the table?
|
|
jz vesar
|
|
|
|
cmpw $0x0080, %ax # Check validity of mode ID
|
|
jc vesa2
|
|
|
|
orb %ah, %ah # Valid IDs: 0x0000-0x007f/0x0100-0x07ff
|
|
jz vesan # Certain BIOSes report 0x80-0xff!
|
|
|
|
cmpw $0x0800, %ax
|
|
jnc vesae
|
|
|
|
vesa2: pushw %cx
|
|
movw %ax, %cx # Get mode information structure
|
|
movw $0x4f01, %ax
|
|
int $0x10
|
|
movw %cx, %bx # BX=mode number
|
|
addb $VIDEO_FIRST_VESA>>8, %bh
|
|
popw %cx
|
|
cmpw $0x004f, %ax
|
|
jnz vesan # Don't report errors (buggy BIOSES)
|
|
|
|
movb (%di), %al # Check capabilities. We require
|
|
andb $0x19, %al # a color text mode.
|
|
cmpb $0x09, %al
|
|
jnz vesan
|
|
|
|
cmpw $0xb800, 8(%di) # Standard video memory address required
|
|
jnz vesan
|
|
|
|
testb $2, (%di) # Mode characteristics supplied?
|
|
movw %bx, (%di) # Store mode number
|
|
jz vesa3
|
|
|
|
xorw %dx, %dx
|
|
movw 0x12(%di), %bx # Width
|
|
orb %bh, %bh
|
|
jnz vesan
|
|
|
|
movb %bl, 0x3(%di)
|
|
movw 0x14(%di), %ax # Height
|
|
orb %ah, %ah
|
|
jnz vesan
|
|
|
|
movb %al, 2(%di)
|
|
mulb %bl
|
|
cmpw $8193, %ax # Small enough for Linux console driver?
|
|
jnc vesan
|
|
|
|
jmp vesaok
|
|
|
|
vesa3: subw $0x8108, %bx # This mode has no detailed info specified,
|
|
jc vesan # so it must be a standard VESA mode.
|
|
|
|
cmpw $5, %bx
|
|
jnc vesan
|
|
|
|
movw vesa_text_mode_table(%bx), %ax
|
|
movw %ax, 2(%di)
|
|
vesaok: addw $4, %di # The mode is valid. Store it.
|
|
vesan: loop vesa1 # Next mode. Limit exceeded => error
|
|
vesae: leaw vesaer, %si
|
|
call prtstr
|
|
movw %bp, %di # Discard already found modes.
|
|
vesar: popw %gs
|
|
ret
|
|
|
|
# Dimensions of standard VESA text modes
|
|
vesa_text_mode_table:
|
|
.byte 60, 80 # 0108
|
|
.byte 25, 132 # 0109
|
|
.byte 43, 132 # 010A
|
|
.byte 50, 132 # 010B
|
|
.byte 60, 132 # 010C
|
|
#endif /* CONFIG_VIDEO_VESA */
|
|
|
|
# Scan for video modes. A bit dirty, but should work.
|
|
mode_scan:
|
|
movw $0x0100, %cx # Start with mode 0
|
|
scm1: movb $0, %ah # Test the mode
|
|
movb %cl, %al
|
|
int $0x10
|
|
movb $0x0f, %ah
|
|
int $0x10
|
|
cmpb %cl, %al
|
|
jnz scm2 # Mode not set
|
|
|
|
movw $0x3c0, %dx # Test if it's a text mode
|
|
movb $0x10, %al # Mode bits
|
|
call inidx
|
|
andb $0x03, %al
|
|
jnz scm2
|
|
|
|
movb $0xce, %dl # Another set of mode bits
|
|
movb $0x06, %al
|
|
call inidx
|
|
shrb %al
|
|
jc scm2
|
|
|
|
movb $0xd4, %dl # Cursor location
|
|
movb $0x0f, %al
|
|
call inidx
|
|
orb %al, %al
|
|
jnz scm2
|
|
|
|
movw %cx, %ax # Ok, store the mode
|
|
stosw
|
|
movb %gs:(0x484), %al # Number of rows
|
|
incb %al
|
|
stosb
|
|
movw %gs:(0x44a), %ax # Number of columns
|
|
stosb
|
|
scm2: incb %cl
|
|
jns scm1
|
|
|
|
movw $0x0003, %ax # Return back to mode 3
|
|
int $0x10
|
|
ret
|
|
|
|
tstidx: outw %ax, %dx # OUT DX,AX and inidx
|
|
inidx: outb %al, %dx # Read from indexed VGA register
|
|
incw %dx # AL=index, DX=index reg port -> AL=data
|
|
inb %dx, %al
|
|
decw %dx
|
|
ret
|
|
|
|
# Try to detect type of SVGA card and supply (usually approximate) video
|
|
# mode table for it.
|
|
|
|
#ifdef CONFIG_VIDEO_SVGA
|
|
svga_modes:
|
|
leaw svga_table, %si # Test all known SVGA adapters
|
|
dosvga: lodsw
|
|
movw %ax, %bp # Default mode table
|
|
orw %ax, %ax
|
|
jz didsv1
|
|
|
|
lodsw # Pointer to test routine
|
|
pushw %si
|
|
pushw %di
|
|
pushw %es
|
|
movw $0xc000, %bx
|
|
movw %bx, %es
|
|
call *%ax # Call test routine
|
|
popw %es
|
|
popw %di
|
|
popw %si
|
|
orw %bp, %bp
|
|
jz dosvga
|
|
|
|
movw %bp, %si # Found, copy the modes
|
|
movb svga_prefix, %ah
|
|
cpsvga: lodsb
|
|
orb %al, %al
|
|
jz didsv
|
|
|
|
stosw
|
|
movsw
|
|
jmp cpsvga
|
|
|
|
didsv: movw %si, card_name # Store pointer to card name
|
|
didsv1: ret
|
|
|
|
# Table of all known SVGA cards. For each card, we store a pointer to
|
|
# a table of video modes supported by the card and a pointer to a routine
|
|
# used for testing of presence of the card. The video mode table is always
|
|
# followed by the name of the card or the chipset.
|
|
svga_table:
|
|
.word ati_md, ati_test
|
|
.word oak_md, oak_test
|
|
.word paradise_md, paradise_test
|
|
.word realtek_md, realtek_test
|
|
.word s3_md, s3_test
|
|
.word chips_md, chips_test
|
|
.word video7_md, video7_test
|
|
.word cirrus5_md, cirrus5_test
|
|
.word cirrus6_md, cirrus6_test
|
|
.word cirrus1_md, cirrus1_test
|
|
.word ahead_md, ahead_test
|
|
.word everex_md, everex_test
|
|
.word genoa_md, genoa_test
|
|
.word trident_md, trident_test
|
|
.word tseng_md, tseng_test
|
|
.word 0
|
|
|
|
# Test routines and mode tables:
|
|
|
|
# S3 - The test algorithm was taken from the SuperProbe package
|
|
# for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org
|
|
s3_test:
|
|
movw $0x0f35, %cx # we store some constants in cl/ch
|
|
movw $0x03d4, %dx
|
|
movb $0x38, %al
|
|
call inidx
|
|
movb %al, %bh # store current CRT-register 0x38
|
|
movw $0x0038, %ax
|
|
call outidx # disable writing to special regs
|
|
movb %cl, %al # check whether we can write special reg 0x35
|
|
call inidx
|
|
movb %al, %bl # save the current value of CRT reg 0x35
|
|
andb $0xf0, %al # clear bits 0-3
|
|
movb %al, %ah
|
|
movb %cl, %al # and write it to CRT reg 0x35
|
|
call outidx
|
|
call inidx # now read it back
|
|
andb %ch, %al # clear the upper 4 bits
|
|
jz s3_2 # the first test failed. But we have a
|
|
|
|
movb %bl, %ah # second chance
|
|
movb %cl, %al
|
|
call outidx
|
|
jmp s3_1 # do the other tests
|
|
|
|
s3_2: movw %cx, %ax # load ah with 0xf and al with 0x35
|
|
orb %bl, %ah # set the upper 4 bits of ah with the orig value
|
|
call outidx # write ...
|
|
call inidx # ... and reread
|
|
andb %cl, %al # turn off the upper 4 bits
|
|
pushw %ax
|
|
movb %bl, %ah # restore old value in register 0x35
|
|
movb %cl, %al
|
|
call outidx
|
|
popw %ax
|
|
cmpb %ch, %al # setting lower 4 bits was successful => bad
|
|
je no_s3 # writing is allowed => this is not an S3
|
|
|
|
s3_1: movw $0x4838, %ax # allow writing to special regs by putting
|
|
call outidx # magic number into CRT-register 0x38
|
|
movb %cl, %al # check whether we can write special reg 0x35
|
|
call inidx
|
|
movb %al, %bl
|
|
andb $0xf0, %al
|
|
movb %al, %ah
|
|
movb %cl, %al
|
|
call outidx
|
|
call inidx
|
|
andb %ch, %al
|
|
jnz no_s3 # no, we can't write => no S3
|
|
|
|
movw %cx, %ax
|
|
orb %bl, %ah
|
|
call outidx
|
|
call inidx
|
|
andb %ch, %al
|
|
pushw %ax
|
|
movb %bl, %ah # restore old value in register 0x35
|
|
movb %cl, %al
|
|
call outidx
|
|
popw %ax
|
|
cmpb %ch, %al
|
|
jne no_s31 # writing not possible => no S3
|
|
movb $0x30, %al
|
|
call inidx # now get the S3 id ...
|
|
leaw idS3, %di
|
|
movw $0x10, %cx
|
|
repne
|
|
scasb
|
|
je no_s31
|
|
|
|
movb %bh, %ah
|
|
movb $0x38, %al
|
|
jmp s3rest
|
|
|
|
no_s3: movb $0x35, %al # restore CRT register 0x35
|
|
movb %bl, %ah
|
|
call outidx
|
|
no_s31: xorw %bp, %bp # Detection failed
|
|
s3rest: movb %bh, %ah
|
|
movb $0x38, %al # restore old value of CRT register 0x38
|
|
jmp outidx
|
|
|
|
idS3: .byte 0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
|
|
.byte 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
|
|
|
|
s3_md: .byte 0x54, 0x2b, 0x84
|
|
.byte 0x55, 0x19, 0x84
|
|
.byte 0
|
|
.ascii "S3"
|
|
.byte 0
|
|
|
|
# ATI cards.
|
|
ati_test:
|
|
leaw idati, %si
|
|
movw $0x31, %di
|
|
movw $0x09, %cx
|
|
repe
|
|
cmpsb
|
|
je atiok
|
|
|
|
xorw %bp, %bp
|
|
atiok: ret
|
|
|
|
idati: .ascii "761295520"
|
|
|
|
ati_md: .byte 0x23, 0x19, 0x84
|
|
.byte 0x33, 0x2c, 0x84
|
|
.byte 0x22, 0x1e, 0x64
|
|
.byte 0x21, 0x19, 0x64
|
|
.byte 0x58, 0x21, 0x50
|
|
.byte 0x5b, 0x1e, 0x50
|
|
.byte 0
|
|
.ascii "ATI"
|
|
.byte 0
|
|
|
|
# AHEAD
|
|
ahead_test:
|
|
movw $0x200f, %ax
|
|
movw $0x3ce, %dx
|
|
outw %ax, %dx
|
|
incw %dx
|
|
inb %dx, %al
|
|
cmpb $0x20, %al
|
|
je isahed
|
|
|
|
cmpb $0x21, %al
|
|
je isahed
|
|
|
|
xorw %bp, %bp
|
|
isahed: ret
|
|
|
|
ahead_md:
|
|
.byte 0x22, 0x2c, 0x84
|
|
.byte 0x23, 0x19, 0x84
|
|
.byte 0x24, 0x1c, 0x84
|
|
.byte 0x2f, 0x32, 0xa0
|
|
.byte 0x32, 0x22, 0x50
|
|
.byte 0x34, 0x42, 0x50
|
|
.byte 0
|
|
.ascii "Ahead"
|
|
.byte 0
|
|
|
|
# Chips & Tech.
|
|
chips_test:
|
|
movw $0x3c3, %dx
|
|
inb %dx, %al
|
|
orb $0x10, %al
|
|
outb %al, %dx
|
|
movw $0x104, %dx
|
|
inb %dx, %al
|
|
movb %al, %bl
|
|
movw $0x3c3, %dx
|
|
inb %dx, %al
|
|
andb $0xef, %al
|
|
outb %al, %dx
|
|
cmpb $0xa5, %bl
|
|
je cantok
|
|
|
|
xorw %bp, %bp
|
|
cantok: ret
|
|
|
|
chips_md:
|
|
.byte 0x60, 0x19, 0x84
|
|
.byte 0x61, 0x32, 0x84
|
|
.byte 0
|
|
.ascii "Chips & Technologies"
|
|
.byte 0
|
|
|
|
# Cirrus Logic 5X0
|
|
cirrus1_test:
|
|
movw $0x3d4, %dx
|
|
movb $0x0c, %al
|
|
outb %al, %dx
|
|
incw %dx
|
|
inb %dx, %al
|
|
movb %al, %bl
|
|
xorb %al, %al
|
|
outb %al, %dx
|
|
decw %dx
|
|
movb $0x1f, %al
|
|
outb %al, %dx
|
|
incw %dx
|
|
inb %dx, %al
|
|
movb %al, %bh
|
|
xorb %ah, %ah
|
|
shlb $4, %al
|
|
movw %ax, %cx
|
|
movb %bh, %al
|
|
shrb $4, %al
|
|
addw %ax, %cx
|
|
shlw $8, %cx
|
|
addw $6, %cx
|
|
movw %cx, %ax
|
|
movw $0x3c4, %dx
|
|
outw %ax, %dx
|
|
incw %dx
|
|
inb %dx, %al
|
|
andb %al, %al
|
|
jnz nocirr
|
|
|
|
movb %bh, %al
|
|
outb %al, %dx
|
|
inb %dx, %al
|
|
cmpb $0x01, %al
|
|
je iscirr
|
|
|
|
nocirr: xorw %bp, %bp
|
|
iscirr: movw $0x3d4, %dx
|
|
movb %bl, %al
|
|
xorb %ah, %ah
|
|
shlw $8, %ax
|
|
addw $0x0c, %ax
|
|
outw %ax, %dx
|
|
ret
|
|
|
|
cirrus1_md:
|
|
.byte 0x1f, 0x19, 0x84
|
|
.byte 0x20, 0x2c, 0x84
|
|
.byte 0x22, 0x1e, 0x84
|
|
.byte 0x31, 0x25, 0x64
|
|
.byte 0
|
|
.ascii "Cirrus Logic 5X0"
|
|
.byte 0
|
|
|
|
# Cirrus Logic 54XX
|
|
cirrus5_test:
|
|
movw $0x3c4, %dx
|
|
movb $6, %al
|
|
call inidx
|
|
movb %al, %bl # BL=backup
|
|
movw $6, %ax
|
|
call tstidx
|
|
cmpb $0x0f, %al
|
|
jne c5fail
|
|
|
|
movw $0x1206, %ax
|
|
call tstidx
|
|
cmpb $0x12, %al
|
|
jne c5fail
|
|
|
|
movb $0x1e, %al
|
|
call inidx
|
|
movb %al, %bh
|
|
movb %bh, %ah
|
|
andb $0xc0, %ah
|
|
movb $0x1e, %al
|
|
call tstidx
|
|
andb $0x3f, %al
|
|
jne c5xx
|
|
|
|
movb $0x1e, %al
|
|
movb %bh, %ah
|
|
orb $0x3f, %ah
|
|
call tstidx
|
|
xorb $0x3f, %al
|
|
andb $0x3f, %al
|
|
c5xx: pushf
|
|
movb $0x1e, %al
|
|
movb %bh, %ah
|
|
outw %ax, %dx
|
|
popf
|
|
je c5done
|
|
|
|
c5fail: xorw %bp, %bp
|
|
c5done: movb $6, %al
|
|
movb %bl, %ah
|
|
outw %ax, %dx
|
|
ret
|
|
|
|
cirrus5_md:
|
|
.byte 0x14, 0x19, 0x84
|
|
.byte 0x54, 0x2b, 0x84
|
|
.byte 0
|
|
.ascii "Cirrus Logic 54XX"
|
|
.byte 0
|
|
|
|
# Cirrus Logic 64XX -- no known extra modes, but must be identified, because
|
|
# it's misidentified by the Ahead test.
|
|
cirrus6_test:
|
|
movw $0x3ce, %dx
|
|
movb $0x0a, %al
|
|
call inidx
|
|
movb %al, %bl # BL=backup
|
|
movw $0xce0a, %ax
|
|
call tstidx
|
|
orb %al, %al
|
|
jne c2fail
|
|
|
|
movw $0xec0a, %ax
|
|
call tstidx
|
|
cmpb $0x01, %al
|
|
jne c2fail
|
|
|
|
movb $0xaa, %al
|
|
call inidx # 4X, 5X, 7X and 8X are valid 64XX chip ID's.
|
|
shrb $4, %al
|
|
subb $4, %al
|
|
jz c6done
|
|
|
|
decb %al
|
|
jz c6done
|
|
|
|
subb $2, %al
|
|
jz c6done
|
|
|
|
decb %al
|
|
jz c6done
|
|
|
|
c2fail: xorw %bp, %bp
|
|
c6done: movb $0x0a, %al
|
|
movb %bl, %ah
|
|
outw %ax, %dx
|
|
ret
|
|
|
|
cirrus6_md:
|
|
.byte 0
|
|
.ascii "Cirrus Logic 64XX"
|
|
.byte 0
|
|
|
|
# Everex / Trident
|
|
everex_test:
|
|
movw $0x7000, %ax
|
|
xorw %bx, %bx
|
|
int $0x10
|
|
cmpb $0x70, %al
|
|
jne noevrx
|
|
|
|
shrw $4, %dx
|
|
cmpw $0x678, %dx
|
|
je evtrid
|
|
|
|
cmpw $0x236, %dx
|
|
jne evrxok
|
|
|
|
evtrid: leaw trident_md, %bp
|
|
evrxok: ret
|
|
|
|
noevrx: xorw %bp, %bp
|
|
ret
|
|
|
|
everex_md:
|
|
.byte 0x03, 0x22, 0x50
|
|
.byte 0x04, 0x3c, 0x50
|
|
.byte 0x07, 0x2b, 0x64
|
|
.byte 0x08, 0x4b, 0x64
|
|
.byte 0x0a, 0x19, 0x84
|
|
.byte 0x0b, 0x2c, 0x84
|
|
.byte 0x16, 0x1e, 0x50
|
|
.byte 0x18, 0x1b, 0x64
|
|
.byte 0x21, 0x40, 0xa0
|
|
.byte 0x40, 0x1e, 0x84
|
|
.byte 0
|
|
.ascii "Everex/Trident"
|
|
.byte 0
|
|
|
|
# Genoa.
|
|
genoa_test:
|
|
leaw idgenoa, %si # Check Genoa 'clues'
|
|
xorw %ax, %ax
|
|
movb %es:(0x37), %al
|
|
movw %ax, %di
|
|
movw $0x04, %cx
|
|
decw %si
|
|
decw %di
|
|
l1: incw %si
|
|
incw %di
|
|
movb (%si), %al
|
|
testb %al, %al
|
|
jz l2
|
|
|
|
cmpb %es:(%di), %al
|
|
l2: loope l1
|
|
orw %cx, %cx
|
|
je isgen
|
|
|
|
xorw %bp, %bp
|
|
isgen: ret
|
|
|
|
idgenoa: .byte 0x77, 0x00, 0x99, 0x66
|
|
|
|
genoa_md:
|
|
.byte 0x58, 0x20, 0x50
|
|
.byte 0x5a, 0x2a, 0x64
|
|
.byte 0x60, 0x19, 0x84
|
|
.byte 0x61, 0x1d, 0x84
|
|
.byte 0x62, 0x20, 0x84
|
|
.byte 0x63, 0x2c, 0x84
|
|
.byte 0x64, 0x3c, 0x84
|
|
.byte 0x6b, 0x4f, 0x64
|
|
.byte 0x72, 0x3c, 0x50
|
|
.byte 0x74, 0x42, 0x50
|
|
.byte 0x78, 0x4b, 0x64
|
|
.byte 0
|
|
.ascii "Genoa"
|
|
.byte 0
|
|
|
|
# OAK
|
|
oak_test:
|
|
leaw idoakvga, %si
|
|
movw $0x08, %di
|
|
movw $0x08, %cx
|
|
repe
|
|
cmpsb
|
|
je isoak
|
|
|
|
xorw %bp, %bp
|
|
isoak: ret
|
|
|
|
idoakvga: .ascii "OAK VGA "
|
|
|
|
oak_md: .byte 0x4e, 0x3c, 0x50
|
|
.byte 0x4f, 0x3c, 0x84
|
|
.byte 0x50, 0x19, 0x84
|
|
.byte 0x51, 0x2b, 0x84
|
|
.byte 0
|
|
.ascii "OAK"
|
|
.byte 0
|
|
|
|
# WD Paradise.
|
|
paradise_test:
|
|
leaw idparadise, %si
|
|
movw $0x7d, %di
|
|
movw $0x04, %cx
|
|
repe
|
|
cmpsb
|
|
je ispara
|
|
|
|
xorw %bp, %bp
|
|
ispara: ret
|
|
|
|
idparadise: .ascii "VGA="
|
|
|
|
paradise_md:
|
|
.byte 0x41, 0x22, 0x50
|
|
.byte 0x47, 0x1c, 0x84
|
|
.byte 0x55, 0x19, 0x84
|
|
.byte 0x54, 0x2c, 0x84
|
|
.byte 0
|
|
.ascii "Paradise"
|
|
.byte 0
|
|
|
|
# Trident.
|
|
trident_test:
|
|
movw $0x3c4, %dx
|
|
movb $0x0e, %al
|
|
outb %al, %dx
|
|
incw %dx
|
|
inb %dx, %al
|
|
xchgb %al, %ah
|
|
xorb %al, %al
|
|
outb %al, %dx
|
|
inb %dx, %al
|
|
xchgb %ah, %al
|
|
movb %al, %bl # Strange thing ... in the book this wasn't
|
|
andb $0x02, %bl # necessary but it worked on my card which
|
|
jz setb2 # is a trident. Without it the screen goes
|
|
# blurred ...
|
|
andb $0xfd, %al
|
|
jmp clrb2
|
|
|
|
setb2: orb $0x02, %al
|
|
clrb2: outb %al, %dx
|
|
andb $0x0f, %ah
|
|
cmpb $0x02, %ah
|
|
je istrid
|
|
|
|
xorw %bp, %bp
|
|
istrid: ret
|
|
|
|
trident_md:
|
|
.byte 0x50, 0x1e, 0x50
|
|
.byte 0x51, 0x2b, 0x50
|
|
.byte 0x52, 0x3c, 0x50
|
|
.byte 0x57, 0x19, 0x84
|
|
.byte 0x58, 0x1e, 0x84
|
|
.byte 0x59, 0x2b, 0x84
|
|
.byte 0x5a, 0x3c, 0x84
|
|
.byte 0
|
|
.ascii "Trident"
|
|
.byte 0
|
|
|
|
# Tseng.
|
|
tseng_test:
|
|
movw $0x3cd, %dx
|
|
inb %dx, %al # Could things be this simple ! :-)
|
|
movb %al, %bl
|
|
movb $0x55, %al
|
|
outb %al, %dx
|
|
inb %dx, %al
|
|
movb %al, %ah
|
|
movb %bl, %al
|
|
outb %al, %dx
|
|
cmpb $0x55, %ah
|
|
je istsen
|
|
|
|
isnot: xorw %bp, %bp
|
|
istsen: ret
|
|
|
|
tseng_md:
|
|
.byte 0x26, 0x3c, 0x50
|
|
.byte 0x2a, 0x28, 0x64
|
|
.byte 0x23, 0x19, 0x84
|
|
.byte 0x24, 0x1c, 0x84
|
|
.byte 0x22, 0x2c, 0x84
|
|
.byte 0x21, 0x3c, 0x84
|
|
.byte 0
|
|
.ascii "Tseng"
|
|
.byte 0
|
|
|
|
# Video7.
|
|
video7_test:
|
|
movw $0x3cc, %dx
|
|
inb %dx, %al
|
|
movw $0x3b4, %dx
|
|
andb $0x01, %al
|
|
jz even7
|
|
|
|
movw $0x3d4, %dx
|
|
even7: movb $0x0c, %al
|
|
outb %al, %dx
|
|
incw %dx
|
|
inb %dx, %al
|
|
movb %al, %bl
|
|
movb $0x55, %al
|
|
outb %al, %dx
|
|
inb %dx, %al
|
|
decw %dx
|
|
movb $0x1f, %al
|
|
outb %al, %dx
|
|
incw %dx
|
|
inb %dx, %al
|
|
movb %al, %bh
|
|
decw %dx
|
|
movb $0x0c, %al
|
|
outb %al, %dx
|
|
incw %dx
|
|
movb %bl, %al
|
|
outb %al, %dx
|
|
movb $0x55, %al
|
|
xorb $0xea, %al
|
|
cmpb %bh, %al
|
|
jne isnot
|
|
|
|
movb $VIDEO_FIRST_V7>>8, svga_prefix # Use special mode switching
|
|
ret
|
|
|
|
video7_md:
|
|
.byte 0x40, 0x2b, 0x50
|
|
.byte 0x43, 0x3c, 0x50
|
|
.byte 0x44, 0x3c, 0x64
|
|
.byte 0x41, 0x19, 0x84
|
|
.byte 0x42, 0x2c, 0x84
|
|
.byte 0x45, 0x1c, 0x84
|
|
.byte 0
|
|
.ascii "Video 7"
|
|
.byte 0
|
|
|
|
# Realtek VGA
|
|
realtek_test:
|
|
leaw idrtvga, %si
|
|
movw $0x45, %di
|
|
movw $0x0b, %cx
|
|
repe
|
|
cmpsb
|
|
je isrt
|
|
|
|
xorw %bp, %bp
|
|
isrt: ret
|
|
|
|
idrtvga: .ascii "REALTEK VGA"
|
|
|
|
realtek_md:
|
|
.byte 0x1a, 0x3c, 0x50
|
|
.byte 0x1b, 0x19, 0x84
|
|
.byte 0x1c, 0x1e, 0x84
|
|
.byte 0x1d, 0x2b, 0x84
|
|
.byte 0x1e, 0x3c, 0x84
|
|
.byte 0
|
|
.ascii "REALTEK"
|
|
.byte 0
|
|
|
|
#endif /* CONFIG_VIDEO_SVGA */
|
|
|
|
# User-defined local mode table (VGA only)
|
|
#ifdef CONFIG_VIDEO_LOCAL
|
|
local_modes:
|
|
leaw local_mode_table, %si
|
|
locm1: lodsw
|
|
orw %ax, %ax
|
|
jz locm2
|
|
|
|
stosw
|
|
movsw
|
|
jmp locm1
|
|
|
|
locm2: ret
|
|
|
|
# This is the table of local video modes which can be supplied manually
|
|
# by the user. Each entry consists of mode ID (word) and dimensions
|
|
# (byte for column count and another byte for row count). These modes
|
|
# are placed before all SVGA and VESA modes and override them if table
|
|
# compacting is enabled. The table must end with a zero word followed
|
|
# by NUL-terminated video adapter name.
|
|
local_mode_table:
|
|
.word 0x0100 # Example: 40x25
|
|
.byte 25,40
|
|
.word 0
|
|
.ascii "Local"
|
|
.byte 0
|
|
#endif /* CONFIG_VIDEO_LOCAL */
|
|
|
|
# Read a key and return the ASCII code in al, scan code in ah
|
|
getkey: xorb %ah, %ah
|
|
int $0x16
|
|
ret
|
|
|
|
# Read a key with a timeout of 30 seconds.
|
|
# The hardware clock is used to get the time.
|
|
getkt: call gettime
|
|
addb $30, %al # Wait 30 seconds
|
|
cmpb $60, %al
|
|
jl lminute
|
|
|
|
subb $60, %al
|
|
lminute:
|
|
movb %al, %cl
|
|
again: movb $0x01, %ah
|
|
int $0x16
|
|
jnz getkey # key pressed, so get it
|
|
|
|
call gettime
|
|
cmpb %cl, %al
|
|
jne again
|
|
|
|
movb $0x20, %al # timeout, return `space'
|
|
ret
|
|
|
|
# Flush the keyboard buffer
|
|
flush: movb $0x01, %ah
|
|
int $0x16
|
|
jz empty
|
|
|
|
xorb %ah, %ah
|
|
int $0x16
|
|
jmp flush
|
|
|
|
empty: ret
|
|
|
|
# Print hexadecimal number.
|
|
prthw: pushw %ax
|
|
movb %ah, %al
|
|
call prthb
|
|
popw %ax
|
|
prthb: pushw %ax
|
|
shrb $4, %al
|
|
call prthn
|
|
popw %ax
|
|
andb $0x0f, %al
|
|
prthn: cmpb $0x0a, %al
|
|
jc prth1
|
|
|
|
addb $0x07, %al
|
|
prth1: addb $0x30, %al
|
|
jmp prtchr
|
|
|
|
# Print decimal number in al
|
|
prtdec: pushw %ax
|
|
pushw %cx
|
|
xorb %ah, %ah
|
|
movb $0x0a, %cl
|
|
idivb %cl
|
|
cmpb $0x09, %al
|
|
jbe lt100
|
|
|
|
call prtdec
|
|
jmp skip10
|
|
|
|
lt100: addb $0x30, %al
|
|
call prtchr
|
|
skip10: movb %ah, %al
|
|
addb $0x30, %al
|
|
call prtchr
|
|
popw %cx
|
|
popw %ax
|
|
ret
|
|
|
|
store_edid:
|
|
#ifdef CONFIG_FIRMWARE_EDID
|
|
pushw %es # just save all registers
|
|
pushw %ax
|
|
pushw %bx
|
|
pushw %cx
|
|
pushw %dx
|
|
pushw %di
|
|
|
|
pushw %fs
|
|
popw %es
|
|
|
|
movl $0x13131313, %eax # memset block with 0x13
|
|
movw $32, %cx
|
|
movw $0x140, %di
|
|
cld
|
|
rep
|
|
stosl
|
|
|
|
pushw %es # save ES
|
|
xorw %di, %di # Report Capability
|
|
pushw %di
|
|
popw %es # ES:DI must be 0:0
|
|
movw $0x4f15, %ax
|
|
xorw %bx, %bx
|
|
xorw %cx, %cx
|
|
int $0x10
|
|
popw %es # restore ES
|
|
|
|
cmpb $0x00, %ah # call successful
|
|
jne no_edid
|
|
|
|
cmpb $0x4f, %al # function supported
|
|
jne no_edid
|
|
|
|
movw $0x4f15, %ax # do VBE/DDC
|
|
movw $0x01, %bx
|
|
movw $0x00, %cx
|
|
movw $0x00, %dx
|
|
movw $0x140, %di
|
|
int $0x10
|
|
|
|
no_edid:
|
|
popw %di # restore all registers
|
|
popw %dx
|
|
popw %cx
|
|
popw %bx
|
|
popw %ax
|
|
popw %es
|
|
#endif
|
|
ret
|
|
|
|
# VIDEO_SELECT-only variables
|
|
mt_end: .word 0 # End of video mode table if built
|
|
edit_buf: .space 6 # Line editor buffer
|
|
card_name: .word 0 # Pointer to adapter name
|
|
scanning: .byte 0 # Performing mode scan
|
|
do_restore: .byte 0 # Screen contents altered during mode change
|
|
svga_prefix: .byte VIDEO_FIRST_BIOS>>8 # Default prefix for BIOS modes
|
|
graphic_mode: .byte 0 # Graphic mode with a linear frame buffer
|
|
dac_size: .byte 6 # DAC bit depth
|
|
|
|
# Status messages
|
|
keymsg: .ascii "Press <RETURN> to see video modes available, "
|
|
.ascii "<SPACE> to continue or wait 30 secs"
|
|
.byte 0x0d, 0x0a, 0
|
|
|
|
listhdr: .byte 0x0d, 0x0a
|
|
.ascii "Mode: COLSxROWS:"
|
|
|
|
crlft: .byte 0x0d, 0x0a, 0
|
|
|
|
prompt: .byte 0x0d, 0x0a
|
|
.asciz "Enter mode number or `scan': "
|
|
|
|
unknt: .asciz "Unknown mode ID. Try again."
|
|
|
|
badmdt: .ascii "You passed an undefined mode number."
|
|
.byte 0x0d, 0x0a, 0
|
|
|
|
vesaer: .ascii "Error: Scanning of VESA modes failed. Please "
|
|
.ascii "report to <mj@ucw.cz>."
|
|
.byte 0x0d, 0x0a, 0
|
|
|
|
old_name: .asciz "CGA/MDA/HGA"
|
|
|
|
ega_name: .asciz "EGA"
|
|
|
|
svga_name: .ascii " "
|
|
|
|
vga_name: .asciz "VGA"
|
|
|
|
vesa_name: .asciz "VESA"
|
|
|
|
name_bann: .asciz "Video adapter: "
|
|
#endif /* CONFIG_VIDEO_SELECT */
|
|
|
|
# Other variables:
|
|
adapter: .byte 0 # Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA
|
|
video_segment: .word 0xb800 # Video memory segment
|
|
force_size: .word 0 # Use this size instead of the one in BIOS vars
|