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 ;-)
short procedure for switching to Mode X:
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 *