Using Mode X via direct VGA access under Linux

by Karsten Scheibler

Accessing the VGA Card under Linux is different from DOS, the big difference is that you can't use any VGA BIOS routines. So switching to the well known 320x200 Mode is more complicated, because you have to do all on your own.

This example code shows how to switch to the Mode X resolution 360x240. Mode X isn't only one video mode it stands for the non-standard (tweaked) modes like 320x240, 320x480 etc. These modes should be available on every VGA Card although they are called non-standard.

I don't want to describe VGA details here, look at www.goodnet.com/~tinara/FreeVGA for more.

This code must be executed as root (or has to be run with SetUID root), because it uses sys_ioperm.

All relevant values for the video mode are in the subroutine modex_switch_mode, they are mostly taken from the svgalib source. You may look there for more Mode X register values. If you extend this example with new video modes or other features please drop me a note, perhaps you have done something others are waiting for ;-)

The memory address of a pixel under Mode X is different compared to that you would expect under the framebuffer device. Because of the VGA specification there are 4 planes, this means you have 4 pixels at the same memory address they only differ in the plane they resist (==> modex_plane_{read|write}_select). There are also different write and read modes for this planes. Hint: if you want to move video memory use write mode 1 in combination with dword access (4 planes * 4 pixels = 16 pixels). If you don't know what i mean read the VGA description mentioned above.

If you like you may optimize the register accesses in the modex_set_* routines, you can combine some of the 8 bit out instructions (out dword dx, al) to 16 bit out instructions (out word dx, ax).

This code also works under vga16fb, but you HAVE TO DISABLE THE CURSOR (like done in the example code), otherwise the vga16fb code in the kernel changes some VGA registers to draw the cursor. The same goes for gpm. Here is the code which was included in the fb article to block any gpm activity.

Remember: THIS IS A QUICK HACK!


	%assign SYS_SOCKETCALL	102

	%assign SOCK_STREAM	1

	%assign AF_UNIX		1

	%assign SYS_SOCKET	1
	%assign SYS_CONNECT	3

				struc tsockaddrun
	tsockaddrun_family:	resw	1
	tsockaddrun_path:	resb	108
				endstruc

			push	dword 0
			push	dword SOCK_STREAM
			push	dword AF_UNIX
			mov	dword eax, SYS_SOCKETCALL
			mov	dword ebx, SYS_SOCKET
			mov	dword ecx, esp
			int	byte  080h
			add	dword esp, 12
			test	dword eax, eax
			js	.error
			push	dword tsockaddrun_size
			push	dword gpm_data
			push	dword eax
			mov	dword eax, SYS_SOCKETCALL
			mov	dword ebx, SYS_CONNECT
			mov	dword ecx, esp
			int	byte  080h
			add	dword esp, 12
	.error:

	section .data
	gpm_data:	istruc tsockaddrun
			at tsockaddrun_familiy,	dw AF_UNIX
			at tsockaddrun_path,	db "/dev/gpmctl", 0
			iend

	
 

Using direct VGA access with other fb-drivers running doesn't seem to work. With Kernel 2.2.15 and matroxfb the graphics doesn't look like expected (probably this registers modifications done here are not enough, the linear addressing mode seems to stay enabled).

I recommend using the terminal in raw keyboard mode to surpress terminal switching via Alt+F? and to disable the behavoir for Shift+PageUp/Shift+PageDown, because they have a big destructive potential to you drawed graphics. The same goes for the terminal blanking.

I also strongly recommend signal handlers in your program, you should ensure that your program exits correctly even if something wents wrong. It is mostly not a good idea to leave the user with a broken console if your code Segfaults ;-)

WARNING:
If your console runs in text mode then you should backup the video memory, because there seem to lie the charset at the beginning used for text display. I don't know if the charset is always placed on this position or if it is device dependent, please drop me a note if you find out more. Therefore this example code saves the whole 256 KB of VGA memory and restores them when exiting.
NOTE:
In this example it is important to save the charset AFTER switching to Mode X and restoring it before restoring the original VGA Mode.

short procedure for switching to Mode X:

  1. set signal handlers
  2. block all gpm activity
  3. use raw keyboard mode to check if your program was started on local console and to disable terminal switching
  4. check if there is a fb driver running (except vga16fb), you may examine /proc/fb or open /dev/fb and use ioctl's (see the fb article for more)
  5. get access to the VGA I/O ports
  6. save the current settings
  7. mmap the video memory
  8. switch to Mode X
  9. save the charset
  10. set the colormap

If you find mistakes or have suggestions, mail me: karsten.scheibler@bigfoot.de


-[sample Makefile for this code]---------------------------------------------
NASM=nasm -f elf -w+orphan-labels
STRIP=strip -R .note -R .comment
LD=ld -s
RM=rm -f

.PHONY: all  clean

all: modex

modex: modex.nasm
	${NASM} modex.nasm
	${LD} -e modex_start -o modex modex.o
	${STRIP} modex

clean:
	${RM} *.bak *~ modex modex.o core
-----------------------------------------------------------------------------


;****************************************************************************
;****************************************************************************
;*
;* USING MODE X (VIA DIRECT VGA ACCESS) UNDER LINUX
;*
;* written by Karsten Scheibler, 2000-DEC-09
;*
;****************************************************************************
;****************************************************************************





;you have to specify this as entry point to the linker, as done above in the
;Makefile

global modex_start


;****************************************************************************
;* some assign's ************************************************************
;****************************************************************************
%assign SYS_EXIT			0
%assign SYS_READ			3
%assign SYS_WRITE			4
%assign SYS_OPEN			5
%assign SYS_CLOSE			6
%assign SYS_MMAP			90
%assign SYS_IOPERM			101

%assign PROT_READ			1
%assign PROT_WRITE			2
%assign MAP_SHARED			1

%assign O_RDWR				000002q

%assign STDIN				0
%assign STDOUT				1

%assign VGA_CRT_REGISTERS		24
%assign VGA_ATTRIBUTE_REGISTERS		21
%assign VGA_GRAPHIC_REGISTERS		9
%assign VGA_SEQUENCER_REGISTERS		5
%assign VGA_MISC_REGISTERS		1

%assign VGA_IO_BASE			03c0h
%assign VGA_IO_SIZE			020h

%assign VGA_ATTRIBUTE_INDEX		03c0h
%assign VGA_ATTRIBUTE_DATA_WRITE	03c0h
%assign VGA_ATTRIBUTE_DATA_READ		03c1h
%assign VGA_MISC_DATA_WRITE		03c2h
%assign VGA_SEQUENCER_INDEX		03c4h
%assign VGA_SEQUENCER_DATA		03c5h
%assign VGA_PEL_MASK			03c6h
%assign VGA_PEL_INDEX_READ		03c7h
%assign VGA_PEL_INDEX_WRITE		03c8h
%assign VGA_PEL_DATA			03c9h
%assign VGA_MISC_DATA_READ		03cch
%assign VGA_GRAPHIC_INDEX		03ceh
%assign VGA_GRAPHIC_DATA		03cfh
%assign VGA_CRT_INDEX			03d4h
%assign VGA_CRT_DATA			03d5h
%assign VGA_INPUT_STATUS		03dah

%assign STATE_CRT_REGISTERS_SAVED		001h
%assign STATE_ATTRIBUTE_REGISTERS_SAVED		002h
%assign STATE_GRAPHIC_REGISTERS_SAVED		004h
%assign STATE_SEQUENCER_REGISTERS_SAVED		008h
%assign STATE_MISC_REGISTERS_SAVED		010h
%assign STATE_COLORMAP_SAVED			020h
%assign STATE_CHARSET_SAVED			040h



;****************************************************************************
;* modex_start **************************************************************
;****************************************************************************
section .text
modex_start:

			;get access to the VGA i/o ports

			mov	dword eax, SYS_IOPERM
			mov	dword ebx, VGA_IO_BASE
			mov	dword ecx, VGA_IO_SIZE
			mov	dword edx, 1
			int	byte  080h
			test	dword eax, eax
			js	near  modex_error

			;save current settings

			call	modex_save_registers

			;mmap the VGA memory

			mov	dword eax, SYS_OPEN
			mov	dword ebx, .mem_path
			mov	dword ecx, O_RDWR
			int	byte  080h
			test	dword eax, eax
			js	near  modex_error
			mov	dword [modex_mmap.handle], eax
			mov	dword eax, SYS_MMAP
			mov	dword ebx, modex_mmap
			int	byte  080h
			test	dword eax, eax
			jz	near  modex_error
			mov	dword [modex_address], eax
			mov	dword eax, SYS_CLOSE
			mov	dword ebx, [modex_mmap.handle]
			int	byte  080h
			test	dword eax, eax
			js	near  modex_error

			call	modex_clear_screen
			call	modex_cursor_off

			;initialize the VGA with our values

			call	modex_switch_mode
			call	modex_save_charset

			;set colormap

			sub	dword esp, 1024
			xor	dword eax, eax
			xor	dword ebx, ebx
.set_colormap:		mov	dword [esp + 4 * ebx], eax
			add	dword eax, 000010101h
			inc	dword ebx
			test	byte  bl, bl
			jnz	.set_colormap
			mov	dword ebx, esp
			call	modex_set_colormap
			add	dword esp, 1024

			;draw something

			mov	dword ebx, 240
			mov	dword edi, [modex_address]
.line:			mov	dword ecx, (360 / 4)
			mov	dword eax, ebx
.pixel:			push	dword ebx
			push	dword ecx
			push	dword eax
			mov	dword ebx, 00100h
			call	modex_select_write_plane
			pop	dword eax
			mov	byte  [edi], al
			inc	dword eax
			push	dword eax
			mov	dword ebx, 00200h
			call	modex_select_write_plane
			pop	dword eax
			mov	byte  [edi], al
			inc	dword eax
			push	dword eax
			mov	dword ebx, 00400h
			call	modex_select_write_plane
			pop	dword eax
			mov	byte  [edi], al
			inc	dword eax
			push	dword eax
			mov	dword ebx, 00800h
			call	modex_select_write_plane
			pop	dword eax
			stosb
			pop	dword ecx
			pop	dword ebx
			inc	dword eax
			dec	dword ecx
			jnz	.pixel
			dec	dword ebx
			jnz	.line

			;wait for keypress

			sub	dword esp, 4
			mov	dword eax, SYS_READ
			mov	dword ebx, STDIN
			mov	dword ecx, esp
			mov	dword edx, 4
			int	byte  080h
			add	dword esp, 4

			;exit nicely

			jmp	modex_end

.mem_path:		db	"/dev/mem", 0

section .data
			align	4
modex_state:		dd	0
modex_address		dd	0
modex_mmap:
.start:			dd	0
.length:		dd	010000h
.protection:		dd	(PROT_READ | PROT_WRITE)
.flags:			dd	MAP_SHARED
.handle:		dd	0
.offset:		dd	0a0000h

section .bss
modex_registers:
.crt:			resb	VGA_CRT_REGISTERS
.attribute:		resb	VGA_ATTRIBUTE_REGISTERS
.graphic:		resb	VGA_GRAPHIC_REGISTERS
.sequencer:		resb	VGA_SEQUENCER_REGISTERS
.misc:			resb	VGA_MISC_REGISTERS
			align	4
.colormap:		resd	256



;****************************************************************************
;* modex_cursor_off *********************************************************
;****************************************************************************
section .text
modex_cursor_off:
			mov	dword eax, SYS_WRITE
			mov	dword ebx, STDOUT
			mov	dword ecx, .text
			mov	dword edx, 6
			int	byte  080h
			ret

.text:			db	01bh, "[?25l"



;****************************************************************************
;* modex_cursor_on **********************************************************
;****************************************************************************
section .text
modex_cursor_on:
			mov	dword eax, SYS_WRITE
			mov	dword ebx, STDOUT
			mov	dword ecx, .text
			mov	dword edx, 6
			int	byte  080h
			ret

.text:			db	01bh, "[?25h"



;****************************************************************************
;* modex_clear_screen *******************************************************
;****************************************************************************
section .text
modex_clear_screen:
			mov	dword eax, SYS_WRITE
			mov	dword ebx, STDOUT
			mov	dword ecx, .text
			mov	dword edx, 10
			int	byte  080h
			ret

.text:			db	01bh, "[1;1H", 01bh, "[2J"



;****************************************************************************
;* modex_get_crt_registers **************************************************
;****************************************************************************
;* ebx=>  pointer where to store crt registers
;****************************************************************************
section .text
modex_get_crt_registers:
			xor	dword ecx, ecx
.loop:			mov	dword edx, VGA_CRT_INDEX
			mov	byte  al, cl
			out	word  dx, al
			mov	dword edx, VGA_CRT_DATA
			in	byte  al, dx
			mov	byte  [ebx + ecx], al
			inc	dword ecx
			cmp	dword ecx, VGA_CRT_REGISTERS
			jb	.loop
			ret



;****************************************************************************
;* modex_get_attribute_registers ********************************************
;****************************************************************************
;* ebx=>  pointer where to store attribute registers
;****************************************************************************
section .text
modex_get_attribute_registers:
			xor	dword ecx, ecx
.loop:			mov	dword edx, VGA_INPUT_STATUS
			in	byte  al, dx
			mov	dword edx, VGA_ATTRIBUTE_INDEX
			mov	byte  al, cl
			out	word  dx, al
			mov	dword edx, VGA_ATTRIBUTE_DATA_READ
			in	byte  al, dx
			mov	byte  [ebx + ecx], al
			inc	dword ecx
			cmp	dword ecx, VGA_ATTRIBUTE_REGISTERS
			jb	.loop
			ret



;****************************************************************************
;* modex_get_graphic_registers **********************************************
;****************************************************************************
;* ebx=>  pointer where to store graphics registers
;****************************************************************************
section .text
modex_get_graphic_registers:
			xor	dword ecx, ecx
.loop:			mov	dword edx, VGA_GRAPHIC_INDEX
			mov	byte  al, cl
			out	word  dx, al
			mov	dword edx, VGA_GRAPHIC_DATA
			in	byte  al, dx
			mov	byte  [ebx + ecx], al
			inc	dword ecx
			cmp	dword ecx, VGA_GRAPHIC_REGISTERS
			jb	.loop
			ret



;****************************************************************************
;* modex_get_sequencer_registers ********************************************
;****************************************************************************
;* ebx=>  pointer where to store sequencer registers
;****************************************************************************
section .text
modex_get_sequencer_registers:
			xor	dword ecx, ecx
.loop:			mov	dword edx, VGA_SEQUENCER_INDEX
			mov	byte  al, cl
			out	word  dx, al
			mov	dword edx, VGA_SEQUENCER_DATA
			in	byte  al, dx
			mov	byte  [ebx + ecx], al
			inc	dword ecx
			cmp	dword ecx, VGA_SEQUENCER_REGISTERS
			jb	.loop
			ret



;****************************************************************************
;* modex_get_misc_registers *************************************************
;****************************************************************************
;* ebx=>  pointer where to store misc register
;****************************************************************************
section .text
modex_get_misc_registers:
			mov	dword edx, VGA_MISC_DATA_READ
			in	byte  al, dx
			mov	byte  [ebx], al
			ret



;****************************************************************************
;* modex_get_colormap *******************************************************
;****************************************************************************
;* ebx=>  pointer where to store colormap
;****************************************************************************
section .text
modex_get_colormap:
			xor	dword ecx, ecx
			xor	dword eax, eax
			mov	dword edx, VGA_PEL_INDEX_READ
			out	word  dx, al
			mov	dword edx, VGA_PEL_DATA
.loop:			in	byte  al, dx
			shl	dword eax, 8
			in	byte  al, dx
			shl	dword eax, 8
			in	byte  al, dx
			mov	dword [ebx + 4 * ecx], eax
			inc	dword ecx
			test	byte  cl, cl
			jnz	.loop
			ret



;****************************************************************************
;* modex_set_crt_registers **************************************************
;****************************************************************************
;* ebx=>  pointer to stored crt registers
;****************************************************************************
section .text
modex_set_crt_registers:

			;deprotect CRT registers 0 - 7

			mov	dword edx, VGA_CRT_INDEX
			mov	byte  al, 011h
			out	word  dx, al
			mov	dword edx, VGA_CRT_DATA
			in	byte  al, dx
			and	byte  al, 07fh
			out	word  dx, al

			;write to the registers

			xor	dword ecx, ecx
.loop:			mov	dword edx, VGA_CRT_INDEX
			mov	byte  al, cl
			out	word  dx, al
			mov	dword edx, VGA_CRT_DATA
			mov	byte  al, [ebx + ecx]
			out	word  dx, al
			inc	dword ecx
			cmp	dword ecx, VGA_CRT_REGISTERS
			jb	.loop
			ret



;****************************************************************************
;* modex_set_attribute_registers ********************************************
;****************************************************************************
;* ebx=>  pointer to stored attibute registers
;****************************************************************************
section .text
modex_set_attribute_registers:
			xor	dword ecx, ecx
.loop:			mov	dword edx, VGA_INPUT_STATUS
			in	byte  al, dx
			mov	dword edx, VGA_ATTRIBUTE_INDEX
			mov	byte  al, cl
			out	word  dx, al
			mov	dword edx, VGA_ATTRIBUTE_DATA_WRITE
			mov	byte  al, [ebx + ecx]
			out	word  dx, al
			inc	dword ecx
			cmp	dword ecx, VGA_ATTRIBUTE_REGISTERS
			jb	.loop
			ret



;****************************************************************************
;* modex_set_graphic_registers **********************************************
;****************************************************************************
;* ebx=>  pointer to stored graphic registers
;****************************************************************************
section .text
modex_set_graphic_registers:
			xor	dword ecx, ecx
.loop:			mov	dword edx, VGA_GRAPHIC_INDEX
			mov	byte  al, cl
			out	word  dx, al
			mov	dword edx, VGA_GRAPHIC_DATA
			mov	byte  al, [ebx + ecx]
			out	word  dx, al
			inc	dword ecx
			cmp	dword ecx, VGA_GRAPHIC_REGISTERS
			jb	.loop
			ret



;****************************************************************************
;* modex_set_sequencer_registers ********************************************
;****************************************************************************
;* ebx=>  pointer to stored sequencer registers
;****************************************************************************
section .text
modex_set_sequencer_registers:

			;synchronous reset on

			mov	dword edx, VGA_SEQUENCER_INDEX
			xor	dword eax, eax
			out	word  dx, al
			mov	dword edx, VGA_SEQUENCER_DATA
			inc	dword eax
			out	word  dx, al

			;write to the registers

			mov	dword edx, VGA_SEQUENCER_INDEX
			out	word  dx, al
			mov	dword edx, VGA_SEQUENCER_DATA
			mov	byte  al, [ebx + 1]
			or	byte  al, 020h
			out	word  dx, al
			mov	dword ecx, 2
.loop:			mov	dword edx, VGA_SEQUENCER_INDEX
			mov	byte  al, cl
			out	word  dx, al
			mov	dword edx, VGA_SEQUENCER_DATA
			mov	byte  al, [ebx + ecx]
			out	word  dx, al
			inc	dword ecx
			cmp	dword ecx, VGA_SEQUENCER_REGISTERS
			jb	.loop

			;synchronous reset off

			mov	dword edx, VGA_SEQUENCER_INDEX
			xor	dword eax, eax
			out	word  dx, al
			mov	dword edx, VGA_SEQUENCER_DATA
			mov	byte  al, 3
			out	word  dx, al
			ret



;****************************************************************************
;* modex_set_misc_registers *************************************************
;****************************************************************************
;* ebx=>  pointer to stored misc register
;****************************************************************************
section .text
modex_set_misc_registers:
			mov	dword edx, VGA_MISC_DATA_WRITE
			mov	byte  al, [ebx]
			out	word dx, al
			ret



;****************************************************************************
;* modex_set_colormap *******************************************************
;****************************************************************************
;* ebx=>  pointer to stored colormap
;****************************************************************************
section .text
modex_set_colormap:
			xor	dword ecx, ecx
			xor	dword eax, eax
			mov	dword edx, VGA_PEL_INDEX_WRITE
			out	word  dx, al
			mov	dword edx, VGA_PEL_DATA
.loop:			mov	dword eax, [ebx + 4 * ecx]
			rol	dword eax, 16
			out	word  dx, al
			rol	dword eax, 8
			out	word  dx, al
			rol	dword eax, 8
			out	word  dx, al
			inc	dword ecx
			test	byte  cl, cl
			jnz	.loop
			ret



;****************************************************************************
;* modex_screen_on **********************************************************
;****************************************************************************
section .text
modex_screen_on:

			;turn on the screen

			mov	dword edx, VGA_SEQUENCER_INDEX
			mov	byte  al, 1
			out	word  dx, al
			mov	dword edx, VGA_SEQUENCER_DATA
			in	byte  al, dx
			and	byte  al, 0dfh
			out	word  dx, al

			;enable video output

			mov	dword edx, VGA_INPUT_STATUS
			in	byte  al, dx
			mov	dword edx, VGA_ATTRIBUTE_DATA_WRITE
			mov	byte  al, 020h
			out	word  dx, al
			ret



;****************************************************************************
;* modex_switch_mode ********************************************************
;****************************************************************************
section .text
modex_switch_mode:
			mov	dword ebx, .vga_misc_registers
			call	modex_set_misc_registers
			mov	dword ebx, .vga_sequencer_registers
			call	modex_set_sequencer_registers
			mov	dword ebx, .vga_crt_registers
			call	modex_set_crt_registers
			mov	dword ebx, .vga_graphic_registers
			call	modex_set_graphic_registers
			mov	dword ebx, .vga_attribute_registers
			call	modex_set_attribute_registers
			call	modex_screen_on
			ret

.vga_crt_registers:
			db	06bh, 059h, 05ah, 08eh, 05eh, 08ah, 00dh, 03eh
			db	000h, 041h, 000h, 000h, 000h, 000h, 000h, 000h
			db	0eah, 0ach, 0dfh, 02dh, 000h, 0e7h, 006h, 0e3h
.vga_attribute_registers:
			db	000h, 001h, 002h, 003h, 004h, 005h, 006h, 007h
			db	008h, 009h, 00ah, 00bh, 00ch, 00dh, 00eh, 00fh
			db	041h, 000h, 00fh, 000h, 000h
.vga_graphic_registers:
			db	000h, 000h, 000h, 000h, 000h, 040h, 005h, 00fh
			db	0ffh
.vga_sequencer_registers:
			db	003h, 001h, 00fh, 000h, 006h
.vga_misc_registers:
			db	0e7h



;****************************************************************************
;* modex_select_write_plane *************************************************
;****************************************************************************
;* bl==>  write mode
;* bh==>  write plane
;****************************************************************************
section .text
modex_select_write_plane:
			and	dword ebx, 00f03h

			;enable set/reset = 0

			mov	dword edx, VGA_GRAPHIC_INDEX
			mov	byte  al, 1
			out	word  dx, al
			mov	dword edx, VGA_GRAPHIC_DATA
			xor	dword eax, eax
			out	word  dx, al

			;logical operation = none, rotate = 0

			mov	dword edx, VGA_GRAPHIC_INDEX
			mov	byte  al, 3
			out	word  dx, al
			mov	dword edx, VGA_GRAPHIC_DATA
			xor	dword eax, eax
			out	word  dx, al

			;select write mode

			mov	dword edx, VGA_GRAPHIC_INDEX
			mov	byte  al, 5
			out	word  dx, al
			mov	dword edx, VGA_GRAPHIC_DATA
			in	byte  al, dx
			and	byte  al, 0fch
			or	byte  al, bl
			out	word  dx, al

			;bitmask = 0ffh

			mov	dword edx, VGA_GRAPHIC_INDEX
			mov	byte  al, 8
			out	word  dx, al
			mov	dword edx, VGA_GRAPHIC_DATA
			mov	byte  al, 0ffh
			out	word  dx, al

			;select write plane

			mov	dword edx, VGA_SEQUENCER_INDEX
			mov	byte  al, 2
			out	word  dx, al
			mov	dword edx, VGA_SEQUENCER_DATA
			mov	byte  al, bh
			out	word  dx, al
			ret



;****************************************************************************
;* modex_select_read_plane **************************************************
;****************************************************************************
;* bl==>  read mode
;* bh==>  read plane
;****************************************************************************
section .text
modex_select_read_plane:
			and	dword ebx, 00301h
			shl	byte  bl, 3

			;select read mode

			mov	dword edx, VGA_GRAPHIC_INDEX
			mov	byte  al, 5
			out	word  dx, al
			mov	dword edx, VGA_GRAPHIC_DATA
			in	byte  al, dx
			and	byte  al, 0f7h
			or	byte  al, bl
			out	word  dx, al

			;select read plane

			mov	dword edx, VGA_GRAPHIC_INDEX
			mov	byte  al, 4
			out	word  dx, al
			mov	dword edx, VGA_GRAPHIC_DATA
			mov	byte  al, bh
			out	word  dx, al
			ret



;****************************************************************************
;* modex_save_registers *****************************************************
;****************************************************************************
section .text
modex_save_registers:
.crt:			test	dword [modex_state], STATE_CRT_REGISTERS_SAVED
			jnz	.attribute
			mov	dword ebx, modex_registers.crt
			call	modex_get_crt_registers
			or	dword [modex_state], STATE_CRT_REGISTERS_SAVED

.attribute:		test	dword [modex_state], STATE_ATTRIBUTE_REGISTERS_SAVED
			jnz	.graphic
			mov	dword ebx, modex_registers.attribute
			call	modex_get_attribute_registers
			or	dword [modex_state], STATE_ATTRIBUTE_REGISTERS_SAVED

.graphic:		test	dword [modex_state], STATE_GRAPHIC_REGISTERS_SAVED
			jnz	.sequencer
			mov	dword ebx, modex_registers.graphic
			call	modex_get_graphic_registers
			or	dword [modex_state], STATE_GRAPHIC_REGISTERS_SAVED

.sequencer:		test	dword [modex_state], STATE_SEQUENCER_REGISTERS_SAVED
			jnz	.misc
			mov	dword ebx, modex_registers.sequencer
			call	modex_get_sequencer_registers
			or	dword [modex_state], STATE_SEQUENCER_REGISTERS_SAVED

.misc:			test	dword [modex_state], STATE_MISC_REGISTERS_SAVED
			jnz	.colormap
			mov	dword ebx, modex_registers.misc
			call	modex_get_misc_registers
			or	dword [modex_state], STATE_MISC_REGISTERS_SAVED

.colormap:		test	dword [modex_state], STATE_COLORMAP_SAVED
			jnz	.end
			mov	dword ebx, modex_registers.colormap
			call	modex_get_colormap
			or	dword [modex_state], STATE_COLORMAP_SAVED
.end:			ret



;****************************************************************************
;* modex_restore_registers **************************************************
;****************************************************************************
section .text
modex_restore_registers:
.crt:			test	dword [modex_state], STATE_CRT_REGISTERS_SAVED
			jz	.attribute
			mov	dword ebx, modex_registers.crt
			call	modex_set_crt_registers

.attribute:		test	dword [modex_state], STATE_ATTRIBUTE_REGISTERS_SAVED
			jz	.graphic
			mov	dword ebx, modex_registers.attribute
			call	modex_set_attribute_registers

.graphic:		test	dword [modex_state], STATE_GRAPHIC_REGISTERS_SAVED
			jz	.sequencer
			mov	dword ebx, modex_registers.graphic
			call	modex_set_graphic_registers

.sequencer:		test	dword [modex_state], STATE_SEQUENCER_REGISTERS_SAVED
			jz	.misc
			mov	dword ebx, modex_registers.sequencer
			call	modex_set_sequencer_registers

.misc:			test	dword [modex_state], STATE_MISC_REGISTERS_SAVED
			jz	.screen_on
			mov	dword ebx, modex_registers.misc
			call	modex_set_misc_registers

.screen_on:		test	dword [modex_state], (STATE_SEQUENCER_REGISTERS_SAVED | STATE_ATTRIBUTE_REGISTERS_SAVED)
			jz	.colormap
			call	modex_screen_on

.colormap:		test	dword [modex_state], STATE_COLORMAP_SAVED
			jz	.end
			mov	dword ebx, modex_registers.colormap
			call	modex_set_colormap
.end:			ret



;****************************************************************************
;* modex_save_charset *******************************************************
;****************************************************************************
section .text
modex_save_charset:
			test	dword [modex_state], STATE_CHARSET_SAVED
			jnz	.end
			or	dword [modex_state], STATE_CHARSET_SAVED
			xor	dword ebx, ebx
			call	modex_select_read_plane
			mov	dword ecx, 04000h
			mov	dword esi, [modex_address]
			mov	dword edi, modex_charset_saved
			cld
			rep movsd
			mov	dword ebx, 00100h
			call	modex_select_read_plane
			mov	dword ecx, 04000h
			mov	dword esi, [modex_address]
			mov	dword edi, (modex_charset_saved + 010000h)
			cld
			rep movsd
			mov	dword ebx, 00200h
			call	modex_select_read_plane
			mov	dword ecx, 04000h
			mov	dword esi, [modex_address]
			mov	dword edi, (modex_charset_saved + 020000h)
			cld
			rep movsd
			mov	dword ebx, 00300h
			call	modex_select_read_plane
			mov	dword ecx, 04000h
			mov	dword esi, [modex_address]
			mov	dword edi, (modex_charset_saved + 030000h)
			cld
			rep movsd
.end:			ret



;****************************************************************************
;* modex_restore_charset ****************************************************
;****************************************************************************
section .text
modex_restore_charset:
			test	dword [modex_state], STATE_CHARSET_SAVED
			jz	.end
			mov	dword ebx, 00100h
			call	modex_select_write_plane
			mov	dword ecx, 04000h
			mov	dword esi, modex_charset_saved
			mov	dword edi, [modex_address]
			cld
			rep movsd
			mov	dword ebx, 00200h
			call	modex_select_write_plane
			mov	dword ecx, 04000h
			mov	dword esi, (modex_charset_saved + 010000h)
			mov	dword edi, [modex_address]
			cld
			rep movsd
			mov	dword ebx, 00400h
			call	modex_select_write_plane
			mov	dword ecx, 04000h
			mov	dword esi, (modex_charset_saved + 020000h)
			mov	dword edi, [modex_address]
			cld
			rep movsd
			mov	dword ebx, 00800h
			call	modex_select_write_plane
			mov	dword ecx, 04000h
			mov	dword esi, (modex_charset_saved + 030000h)
			mov	dword edi, [modex_address]
			cld
			rep movsd
.end:			ret



;****************************************************************************
;* modex_exit ***************************************************************
;****************************************************************************
section .text
modex_end:
			call	modex_restore_charset
			call	modex_restore_registers
			call	modex_clear_screen
			call	modex_cursor_on
			xor	dword eax, eax
			mov	dword ebx, eax
			inc	dword eax
			int	byte  080h



;****************************************************************************
;* modex_error **************************************************************
;****************************************************************************
section .text
modex_error:
			call	modex_restore_charset
			call	modex_restore_registers
			call	modex_clear_screen
			call	modex_cursor_on
			xor	dword eax, eax
			inc	dword eax
			mov	dword ebx, eax
			int	byte  080h



;****************************************************************************
;* uninitialized data *******************************************************
;****************************************************************************
section .bss
			alignb	4
modex_charset_saved:	resb	040000h
;********************************************* karsten.scheibler@bigfoot.de *