File syslinux-3.82-gfxboot.diff of Package syslinux

diff --git a/core/isolinux.asm b/core/isolinux.asm
index 2627c2d..a0910fb 100644
--- a/core/isolinux.asm
+++ b/core/isolinux.asm
@@ -1135,73 +1135,23 @@ all_read:
 ; (which will be at 16 only for a single-session disk!); from the PVD
 ; we should be able to find the rest of what we need to know.
 ;
-get_fs_structures:
-		mov eax,[bi_pvd]
-		mov bx,trackbuf
-		call getonesec
-
-		mov eax,[trackbuf+156+2]
-		mov [RootDir+dir_lba],eax
-		mov [CurrentDir+dir_lba],eax
-%ifdef DEBUG_MESSAGES
-		mov si,dbg_rootdir_msg
-		call writemsg
-		call writehex8
-		call crlf
-%endif
-		mov eax,[trackbuf+156+10]
-		mov [RootDir+dir_len],eax
-		mov [CurrentDir+dir_len],eax
-		add eax,SECTOR_SIZE-1
-		shr eax,SECTOR_SHIFT
-		mov [RootDir+dir_clust],eax
-		mov [CurrentDir+dir_clust],eax
-
-		; Look for an isolinux directory, and if found,
-		; make it the current directory instead of the root
-		; directory.
-		; Also copy the name of the directory to CurrentDirName
-		mov word [CurrentDirName],ROOT_DIR_WORD	; Write '/',0 to the CurrentDirName
+		call iso_mount
 		mov di,boot_dir			; Search for /boot/isolinux
-		mov al,02h
-		push di
-		call searchdir_iso
-		pop di
-		jnz .found_dir
-		mov di,isolinux_dir
-		mov al,02h			; Search for /isolinux
-		push di
-		call searchdir_iso
-		pop di
-		jz .no_isolinux_dir
+		call setcwd
+		jnc .found_dir
+		mov di,isolinux_dir		; Search for /isolinux
+		call setcwd
 .found_dir:
-		; Copy current directory name to CurrentDirName
-		push si
-		push di
-		mov si,di
-		mov di,CurrentDirName
-		call strcpy
-		mov byte [di],0	;done in case it's not word aligned
-		dec di
-		mov byte [di],'/'
-		pop di
-		pop si
 
-		mov [CurrentDir+dir_len],eax
-		mov eax,[si+file_left]
-		mov [CurrentDir+dir_clust],eax
-		xor eax,eax			; Free this file pointer entry
-		xchg eax,[si+file_sector]
-		mov [CurrentDir+dir_lba],eax
 %ifdef DEBUG_MESSAGES
 		push si
 		mov si,dbg_isodir_msg
 		call writemsg
 		pop si
+		mov eax,[CurrentDir+dir_lba]
 		call writehex8
 		call crlf
 %endif
-.no_isolinux_dir:
 
 ;
 ; Locate the configuration file
@@ -1706,6 +1656,90 @@ getfssec:
 		TRACER 'f'
 		ret
 
+;
+; setcwd: Set current working directory.
+;
+;  On entry:
+;	DI	-> directory name
+;  On exit:
+;	CF = 1	-> error
+;
+; On error, the old working directory is kept.
+;
+setcwd:
+		mov al,02h
+		push di
+		call searchdir_iso
+		pop di
+		stc
+		jz .err
+		mov [CurrentDir+dir_len],eax
+		mov eax,[si+file_left]
+		mov [CurrentDir+dir_clust],eax
+		xor eax,eax
+		xchg eax,[si+file_sector]
+		mov [CurrentDir+dir_lba],eax
+		mov si,di
+		mov di,CurrentDirName
+		cmp si,di
+		jz .ok
+		mov cx,FILENAME_MAX
+		push ds
+		pop es
+.copy:
+		lodsb
+		stosb
+		or al,al
+		loopnz .copy
+		mov byte [di-1],0
+		jnz .err
+.ok:
+		clc
+.err:
+		ret
+
+;
+; Read fs meta data and setup RootDir and CurrentDir.
+;
+;  On exit:
+;	CF = 1	-> error
+;
+iso_mount:
+		mov eax,[bi_pvd]
+		mov bx,trackbuf
+		call getonesec
+
+		mov eax,[trackbuf+156+10]
+		mov [RootDir+dir_len],eax
+		add eax,SECTOR_SIZE-1
+		shr eax,SECTOR_SHIFT
+		mov [RootDir+dir_clust],eax
+		mov eax,[trackbuf+156+2]
+		mov [RootDir+dir_lba],eax
+
+		push ds
+		pop es
+		mov si,RootDir
+		mov di,CurrentDir
+		mov cx,dir_t_size
+		rep movsb
+
+%ifdef DEBUG_MESSAGES
+		mov si,dbg_rootdir_msg
+		call writemsg
+		call writehex8
+		call crlf
+%endif
+
+		mov di,CurrentDirName
+		call setcwd
+		jnc .ok
+		mov word [CurrentDirName],ROOT_DIR_WORD
+.ok:
+		clc
+		ret
+
+
 ; -----------------------------------------------------------------------------
 ;  Common modules
 ; -----------------------------------------------------------------------------
diff --git a/core/abort.inc b/core/abort.inc
index 5b16b9d..cc59fa7 100644
--- a/core/abort.inc
+++ b/core/abort.inc
@@ -24,6 +24,10 @@
 ;            assumes CS == DS
 ;
 dot_pause:
+		push ax
+		mov al,5
+		call [comboot_hook]
+		pop ax
 		push si
 		mov si,dot_msg
 		call writestr_qchk
@@ -63,6 +67,8 @@ abort_check:
 abort_load:
 		mov bx,error_or_command
 abort_load_chain:
+		mov al,80h
+		call [comboot_hook]		; may not return
 		RESET_STACK_AND_SEGS AX
                 call writestr                  ; Expects SI -> error msg
 
diff --git a/core/loadhigh.inc b/core/loadhigh.inc
index 8ff9da1..91061fc 100644
--- a/core/loadhigh.inc
+++ b/core/loadhigh.inc
@@ -101,6 +101,8 @@ load_high:
 		ret
 
 .overflow:	mov si,err_nohighmem
+		mov al,83h
+		call [comboot_hook]		; may not return
 		jmp abort_load
 
 		section .data
diff --git a/core/runkernel.inc b/core/runkernel.inc
index 8bfc8b8..f458fc7 100644
--- a/core/runkernel.inc
+++ b/core/runkernel.inc
@@ -228,6 +228,8 @@ new_kernel:
 		mov [LoadFlags],al
 
 any_kernel:
+		mov al,4
+		call [comboot_hook]
 		mov si,loading_msg
                 call writestr_qchk
                 mov si,KernelCName		; Print kernel name part of
@@ -319,6 +321,9 @@ load_initrd:
 ;
                 call abort_check		; Last chance!!
 
+		mov al,6
+		call [comboot_hook]
+
 		mov si,ready_msg
 		call writestr_qchk
 
@@ -489,6 +494,8 @@ old_kernel:
 		xor ax,ax
 		cmp word [InitRDPtr],ax		; Old kernel can't have initrd
                 je .load
+		mov al,82h
+		call [comboot_hook]		; may not return
                 mov si,err_oldkernel
                 jmp abort_load
 .load:
@@ -613,6 +620,8 @@ loadinitrd:
 		ret
 
 .notthere:
+		mov al,81h
+		call [comboot_hook]		; may not return
                 mov si,err_noinitrd
                 call writestr
                 mov si,InitRDCName
diff --git a/core/ui.inc b/core/ui.inc
index 1b40717..9413f16 100644
--- a/core/ui.inc
+++ b/core/ui.inc
@@ -402,8 +402,12 @@ vk_check:
 %if HAS_LOCALBOOT
 		; Is this a "localboot" pseudo-kernel?
 		cmp al,VK_LOCALBOOT		; al == KernelType
+		jne .no_local_boot
+		mov al,7
+		call [comboot_hook]
 		mov ax,[VKernelBuf+vk_rname]	; Possible localboot type
-		je local_boot
+		jmp local_boot
+.no_local_boot:
 %endif
 		jmp get_kernel
 
@@ -468,6 +472,8 @@ bad_kernel:
 .really:
 		mov si,KernelName
                 mov di,KernelCName
+		mov al,81h
+		call [comboot_hook]		; may not return
 		push di
                 call unmangle_name              ; Get human form
 		mov si,err_notfound		; Complain about missing kernel
@@ -510,7 +516,10 @@ on_error:
 ;
 ; kernel_corrupt: Called if the kernel file does not seem healthy
 ;
-kernel_corrupt: mov si,err_notkernel
+kernel_corrupt:
+		mov al,82h
+		call [comboot_hook]		; may not return
+		mov si,err_notkernel
                 jmp abort_load
 
 ;
diff --git a/core/comboot.inc b/core/comboot.inc
index cdba16d..1a1dbfe 100644
--- a/core/comboot.inc
+++ b/core/comboot.inc
@@ -96,24 +96,23 @@ is_comboot_image:
 		shl ax,6		; Kilobytes -> paragraphs
 		mov word [es:02h],ax
 
-%ifndef DEPEND
-%if real_mode_seg != comboot_seg
-%error "This code assumes real_mode_seg == comboot_seg"
-%endif
-%endif
 		; Copy the command line from high memory
+		push word real_mode_seg
+		pop ds
 		mov si,cmd_line_here
 		mov cx,125		; Max cmdline len (minus space and CR)
 		mov di,081h		; Offset in PSP for command line
 		mov al,' '		; DOS command lines begin with a space
 		stosb
 
-.loop:		es lodsb
+.loop:		lodsb
 		and al,al
 		jz .done
 		stosb
 		loop .loop
 .done:
+		push cs
+		pop ds
 
 		mov al,0Dh		; CR after last character
 		stosb
diff --git a/core/layout.inc b/core/layout.inc
index 8c2e248..ca95d2b 100644
--- a/core/layout.inc
+++ b/core/layout.inc
@@ -123,4 +123,4 @@ real_mode_seg	equ cache_seg + 1000h
 pktbuf_seg	equ cache_seg		; PXELINUX packet buffers
 %endif
 
-comboot_seg	equ real_mode_seg	; COMBOOT image loading zone
+comboot_seg	equ real_mode_seg + 1000h	; COMBOOT image loading zone
diff --git a/core/runkernel.inc b/core/runkernel.inc
index f458fc7..f6ed644 100644
--- a/core/runkernel.inc
+++ b/core/runkernel.inc
@@ -165,7 +165,7 @@ opt_mem:
 		ret
 
 opt_quiet:
-		mov byte [QuietBoot],1
+		or byte [QuietBoot],1
 		ret
 
 %if IS_PXELINUX
@@ -634,7 +634,7 @@ loadinitrd:
 ;		assumes CS == DS
 ;
 writestr_qchk:
-		test byte [QuietBoot],01h
+		test byte [QuietBoot],03h
 		jz writestr
 		ret
 
@@ -689,4 +689,6 @@ KernelVersion	resw 1			; Kernel protocol version
 ;
 InitRDPtr	resw 1			; Pointer to initrd= option in command line
 LoadFlags	resb 1			; Loadflags from kernel
-QuietBoot	resb 1			; Set if a quiet boot is requested
+
+		section .data
+QuietBoot	db 0			; Set if a quiet boot is requested
diff --git a/core/ui.inc b/core/ui.inc
index 9413f16..353d59a 100644
--- a/core/ui.inc
+++ b/core/ui.inc
@@ -600,7 +600,7 @@ kernel_good:
 		;
 		xor ax,ax
 		mov [InitRDPtr],ax
-		mov [QuietBoot],al
+		and byte [QuietBoot],~1
 %if IS_PXELINUX
 		mov [KeepPXE],al
 %endif
diff --git a/core/runkernel.inc b/core/runkernel.inc
index f6ed644..286c9c8 100644
--- a/core/runkernel.inc
+++ b/core/runkernel.inc
@@ -259,7 +259,7 @@ read_kernel:
                 mov ecx,8000h			; 32K
 		sub ecx,esi			; Number of bytes to copy
 		add esi,(real_mode_seg << 4)	; Pointer to source
-                mov edi,100000h                 ; Copy to address 100000h
+                mov edi,[KernelStart]           ; Copy to kernel address
 
                 call bcopy			; Transfer to high memory
 
@@ -431,7 +431,7 @@ setup_move:
 
 		mov eax,10000h			; Target address of low kernel
 		stosd
-		mov eax,100000h			; Where currently loaded
+		mov eax,[KernelStart]		; Where currently loaded
 		stosd
 		neg eax
 		add eax,[KernelEnd]
@@ -439,9 +439,13 @@ setup_move:
 		inc cx
 
 		mov bx,9000h			; Revised real mode segment
+		jmp .loading_initrd
 
 .loading_high:
+		mov eax,[KernelStart]
+		mov [fs:su_code32start],eax
 
+.loading_initrd:
 		cmp word [InitRDPtr],0		; Did we have an initrd?
 		je .no_initrd
 
@@ -692,3 +696,5 @@ LoadFlags	resb 1			; Loadflags from kernel
 
 		section .data
 QuietBoot	db 0			; Set if a quiet boot is requested
+		alignz 4
+KernelStart	dd 100000h
diff --git a/core/comboot.inc b/core/comboot.inc
index 1a1dbfe..1923308 100644
--- a/core/comboot.inc
+++ b/core/comboot.inc
@@ -962,6 +962,45 @@ comapi_shufraw:
 		mov ecx,P_ECX
 		jmp shuffle_and_boot_raw
 
+
+;
+; INT 22h AX=0025h	Set current working directory
+;
+%if IS_ISOLINUX
+comapi_setcwd:
+		mov si,P_BX
+		mov di,TmpDirName
+		mov cx,FILENAME_MAX
+		mov ds,P_ES
+.copy:
+		lodsb
+		stosb
+		or al,al
+		loopnz .copy
+		push cs
+		pop ds
+		stc
+		jnz .err
+		mov di,TmpDirName
+		call setcwd
+.err:
+		ret
+%else
+comapi_setcwd	equ comapi_err
+%endif
+
+
+;
+; INT 22h AX=0026h	Read filesystem meta data
+;
+%if IS_ISOLINUX
+comapi_mount:
+;		call iso_mount
+		ret
+%else
+comapi_mount	equ comapi_err
+%endif
+
 		section .data
 
 %macro		int21 2
@@ -969,6 +1008,109 @@ comapi_shufraw:
 		dw %2
 %endmacro
 
+
+;
+; INT 22h AX=0027h	Run command, return on error
+;
+; Terminates the COMBOOT program and executes the command line in
+; ES:BX as if it had been entered by the user.
+; CS:SI: comboot callback (via far call)
+; EDI kernel load address
+; EDX memory end (sets MyHighMemSize if != 0)
+;
+comapi_run2:
+		push word P_CS
+		push word P_SI
+		pop dword [comboot_far]
+		push dword P_EDI
+		pop dword [KernelStart]
+		mov edx,P_EDX
+		or edx,edx
+		jz .nohimemsize
+%if HIGHMEM_SLOP != 0
+		sub edx,HIGHMEM_SLOP
+%endif
+.nohimemsize:
+		mov [AltHighMemSize],edx
+		mov ds,P_ES
+		mov si,P_BX
+		mov di,command_line
+		call strcpy
+		push cs
+		pop ds
+		push cs
+		pop es
+		mov [comboot_sp_save],sp	; save stack pointer
+		mov word [comboot_hook],comboot_hook_entry
+		or byte [QuietBoot],2
+		jmp load_kernel		; Run a new kernel
+
+comapi_run2_cont:
+		mov word [comboot_hook],comboot_hook_nop
+		mov sp,[comboot_sp_save]	; fix stack pointer
+		and byte [QuietBoot],~2
+		clc
+		ret
+
+
+;
+; INT 22h AX=0028h	Get memory size
+;
+comapi_memsize:
+		push dword [HighMemSize]
+		pop dword P_EAX
+		clc
+		ret
+
+
+;
+; Callback function used at various places during kernel/initrd loading.
+;
+; The function either returns or continues at comapi_run2_cont.
+;
+; AL:
+;  bit 7: 0/1 return/don't return
+;  bit 0-6: function code
+;    0: abort kernel/initrd loading
+;    1: kernel/initrd not found
+;    2: kernel corrupt
+;    3: out of memory (while initrd loading)
+;    4: progress start
+;    5: progress increment
+;    6: progress end: kernel loaded, stop gfxboot
+;    7: stop gfxboot
+;
+comboot_hook_entry:
+		pushad
+		push gs
+		push fs
+		push es
+		push ds
+		call far [comboot_far]
+		pop ds
+		pop es
+		pop fs
+		pop gs
+		popad
+		pushad
+		and al,7fh
+		cmp al,6
+		jnz .notlast
+		push es
+		mov si,DOSSaveVectors
+		mov di,4*20h
+		mov cx,20h
+		push word 0
+		pop es
+		rep movsd		; Restore DOS-range vectors
+		pop es
+.notlast:
+		popad
+		test al,80h
+		jnz comapi_run2_cont
+comboot_hook_nop:
+		ret
+
 int21_table:
 		int21	00h, comboot_return
 		int21	01h, comboot_getkey
@@ -1021,8 +1163,16 @@ int22_table:
 		dw comapi_closedir	; 0022 close directory
 		dw comapi_shufsize	; 0023 query shuffler size
 		dw comapi_shufraw	; 0024 cleanup, shuffle and boot raw
+		dw comapi_setcwd	; 0025 set current working directory
+		dw comapi_mount		; 0026 read fs structures (aka mount)
+		dw comapi_run2		; 0027 like 0003, but return on error
+		dw comapi_memsize	; 0028 get memory size
 int22_count	equ ($-int22_table)/2
 
+comboot_sp_save	dw 0
+comboot_hook	dw comboot_hook_nop
+comboot_far	dd 0
+
 APIKeyWait	db 0
 APIKeyFlag	db 0
 
@@ -1041,8 +1191,10 @@ feature_flags_len equ ($-feature_flags)
 err_notdos	db ': attempted DOS system call INT ',0
 err_comlarge	db 'COMBOOT image too large.', CR, LF, 0
 
-		section .bss1
+		section .bss2
 		alignb 4
+AltHighMemSize	resd	1
 DOSErrTramp	resd	33		; Error trampolines
+TmpDirName	resb	FILENAME_MAX
 ConfigName	resb	FILENAME_MAX
 CurrentDirName	resb	FILENAME_MAX
diff --git a/core/comboot.inc b/core/comboot.inc
index 1923308..f39bfb1 100644
--- a/core/comboot.inc
+++ b/core/comboot.inc
@@ -1043,12 +1043,14 @@ comapi_run2:
 		mov [comboot_sp_save],sp	; save stack pointer
 		mov word [comboot_hook],comboot_hook_entry
 		or byte [QuietBoot],2
+		mov byte [comboot_run2_active],1
 		jmp load_kernel		; Run a new kernel
 
 comapi_run2_cont:
 		mov word [comboot_hook],comboot_hook_nop
 		mov sp,[comboot_sp_save]	; fix stack pointer
 		and byte [QuietBoot],~2
+		mov byte [comboot_run2_active],0
 		clc
 		ret
 
@@ -1172,6 +1174,7 @@ int22_count	equ ($-int22_table)/2
 comboot_sp_save	dw 0
 comboot_hook	dw comboot_hook_nop
 comboot_far	dd 0
+comboot_run2_active	db 0
 
 APIKeyWait	db 0
 APIKeyFlag	db 0
diff --git a/core/ui.inc b/core/ui.inc
index 353d59a..e37f2a7 100644
--- a/core/ui.inc
+++ b/core/ui.inc
@@ -379,9 +379,13 @@ vk_check:
 		push word real_mode_seg
 		pop es
 		mov di,cmd_line_here
+		; append line already included in this case
+		cmp byte [comboot_run2_active],0
+		jnz .no_append_copy
 		mov si,VKernelBuf+vk_append
 		mov cx,[VKernelBuf+vk_appendlen]
 		rep movsb
+.no_append_copy:
 		mov [CmdLinePtr],di		; Where to add rest of cmd
 		pop es
 		mov di,KernelName
diff --git a/core/comboot.inc b/core/comboot.inc
index f39bfb1..0874526 100644
--- a/core/comboot.inc
+++ b/core/comboot.inc
@@ -1088,6 +1088,7 @@ comboot_hook_entry:
 		push fs
 		push es
 		push ds
+		mov ecx,[KernelSize]
 		call far [comboot_far]
 		pop ds
 		pop es
diff --git a/core/ui.inc b/core/ui.inc
index e37f2a7..6137dc9 100644
--- a/core/ui.inc
+++ b/core/ui.inc
@@ -616,7 +616,11 @@ kernel_good:
 		mov [KernelCNameLen],di
 
 		; Default memory limit, can be overridden by image loaders
+		mov eax,[AltHighMemSize]
+		or eax,eax
+		jnz .altsize
 		mov eax,[HighMemRsvd]
+.altsize:
 		mov [MyHighMemSize],eax
 
 		popad
@@ -637,6 +641,7 @@ kernel_good:
 ; At this point, EAX contains the size of the kernel, SI contains
 ; the file handle/cluster pointer, and ECX contains the extension (if any.)
 ;
+		mov [KernelSize],eax
 		movzx di,byte [KernelType]
 		add di,di
 		jmp [kerneltype_table+di]
diff --git a/doc/comboot.txt b/doc/comboot.txt
index f5fefda..1450021 100644
--- a/doc/comboot.txt
+++ b/doc/comboot.txt
@@ -955,3 +955,38 @@ AX=0024h [3.80] Cleanup, shuffle and boot, raw version
 	with read/write data segments, matching the respective code
 	segment.  For mode 0, B=0 and the limits will be 64K, for mode
 	1, B=1 and the limits will be 4 GB.
+
+
+AX=0025h [3.84]	Set current working directory
+	Input:	AX	00025h
+		ES:BX	null-terminated directory name string
+	Output:	None
+
+	Sets the current working directory.  For SYSLINUX, ISOLINUX,
+	and PXELINUX, this will be an absolute path.
+
+
+AX=0026h [3.84]	Read file system metadata [ISOLINUX]
+	Input:	AX	00026h
+	Output:	None
+
+	Reads filesystem data (e.g. after a CDROM change).
+
+
+AX=0027h [3.84] Run command
+	Input:	AX	0027h
+		ES:BX	null-terminated command string
+		SI	comboot callback function (called via far call)
+		EDI	kernel load address
+		EDX	if != 0: initrd load address (that is: memory end)
+	Output:	Does not return if the kernel loads correctly.
+
+	Executes the command line as if it had been entered by the user.
+	Note that this function does return (with CF = 0) if there are
+	problems or the user aborted the load. Else it terminates the
+	COMBOOT program and starts the kernel.
+
+AX=0028h [3.84] Get memory size
+	Input:	AX	0028h
+	Output:	EAX	high memory size (in bytes)
+
diff --git a/modules/Makefile b/modules/Makefile
index 80eb995..65661a4 100644
--- a/modules/Makefile
+++ b/modules/Makefile
@@ -19,6 +19,11 @@ include $(topdir)/MCONFIG.embedded
 
 INCLUDES   = -I$(com32)/include
 
+CFLAGS_COM   = -O2 -Wall -Wno-pointer-sign -fomit-frame-pointer -m32 -march=i386 \
+               -fno-align-functions -fno-align-labels -fno-align-jumps -fno-align-loops \
+               -fno-builtin -nostdinc -I .
+ASMFLAGS_COM = -O99 -felf
+
 BINS = pxechain.com gfxboot.com poweroff.com
 
 all: $(BINS)
@@ -49,6 +54,15 @@ $(LIB): $(LIBOBJS)
 %.ppm.gz: %.png
 	$(PNGTOPNM) $< | gzip -9 > $@
 
+libio.o: libio.asm
+	nasm $(ASMFLAGS_COM) -o $@ -l $*.lst $<
+
+gfxboot.o: gfxboot.c libio.h
+	$(CC) -g $(CFLAGS_COM) -c -o $@ $<
+
+gfxboot.com: gfxboot.o libio.o
+	ld -M -Tcom.ld -o $@ $^ >$*.map
+
 tidy dist:
 	rm -f *.o *.a *.lst *.elf *.map .*.d
 
diff --git a/modules/com.ld b/modules/com.ld
new file mode 100644
index 0000000..a98f9aa
--- /dev/null
+++ b/modules/com.ld
@@ -0,0 +1,16 @@
+/*
+  linker script for DOS program (.COM)
+ */
+
+OUTPUT_FORMAT("binary")
+OUTPUT_ARCH(i386)
+SEARCH_DIR(".")
+ENTRY(_start)
+SECTIONS
+{
+  .init 0x100 : { *(.init) }
+  .text       : { *(.text) }
+  .rodata     : { *(.rodata*) }
+  .data       : { *(.data) }
+  .bss        : { __bss_start = .; *(.bss) }
+}
diff --git a/modules/gfxboot.c b/modules/gfxboot.c
new file mode 100644
index 0000000..01c6c84
--- /dev/null
+++ b/modules/gfxboot.c
@@ -0,0 +1,1039 @@
+/*
+ *
+ * gfxboot.c
+ *
+ * A comboot program to load gfxboot graphics.
+ *
+ * It is based on work done by Sebastian Herbszt in gfxboot.asm.
+ *
+ * Copyright (c) 2009 Steffen Winterfeldt.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, Inc., 53 Temple Place Ste 330, Boston MA
+ * 02111-1307, USA; either version 2 of the License, or (at your option) any
+ * later version; incorporated herein by reference.
+ *
+ */
+
+#include <libio.h>
+
+#define ID_SYSLINUX		0x31
+#define ID_PXELINUX		0x32
+#define ID_ISOLINUX		0x33
+#define ID_EXTLINUX		0x34
+
+#define MAX_CONFIG_LINE_LEN	2048
+#define MAX_CMDLINE_LEN		1024
+
+// basic memory layout in MB
+#define GFX_MEMORY_START	1
+#define	GFX_MEMORY_SIZE		7
+// assume initrd needs at least that much
+#define INITRD_MIN_MEMORY	64
+
+#define GFX_BC_INIT		0
+#define GFX_BC_DONE		1
+#define GFX_BC_INPUT		2
+#define GFX_BC_MENU_INIT	3
+#define GFX_BC_INFOBOX_INIT	4
+#define GFX_BC_INFOBOX_DONE	5
+#define GFX_BC_PROGRESS_INIT	6
+#define GFX_BC_PROGRESS_DONE	7
+#define GFX_BC_PROGRESS_UPDATE	8
+#define GFX_BC_PROGRESS_LIMIT	9		// unused
+#define GFX_BC_PASSWORD_INIT	10
+#define GFX_BC_PASSWORD_DONE	11
+
+// for now, static values
+#define MENU_LABEL_SIZE		128
+#define MENU_ARG_SIZE		512
+#define MENU_ENTRY_SIZE		(MENU_LABEL_SIZE + MENU_ARG_SIZE)
+// entry 0 is reserved for the default entry
+#define MAX_MENU_ENTRIES	(0x10000 / MENU_ENTRY_SIZE)
+
+
+typedef struct {
+  uint16_t handle;
+  uint16_t block_size;
+  int file_size;		// file size (-1: unknown)
+  uint32_t buf;			// must be block_size
+  unsigned buf_size;		// in block_size units
+  unsigned data_len;		// valid bytes in buf
+  unsigned buf_idx;		// read pos in buffer
+} fd_t;
+
+
+// gfx config data (52 bytes)
+typedef struct __attribute__ ((packed)) {
+  uint8_t bootloader;		// 0: boot loader type (0: lilo, 1: syslinux, 2: grub)
+  uint8_t sector_shift;		// 1: sector shift
+  uint8_t media_type;		// 2: media type (0: disk, 1: floppy, 2: cdrom)
+  uint8_t failsafe;		// 3: turn on failsafe mode (bitmask)
+				//    0: SHIFT pressed
+				//    1: skip gfxboot
+				//    2: skip monitor detection
+  uint8_t sysconfig_size;	// 4: size of sysconfig data
+  uint8_t boot_drive;		// 5: BIOS boot drive
+  uint16_t callback;		// 6: offset to callback handler
+  uint16_t bootloader_seg;	// 8: code/data segment used by bootloader; must follow gfx_callback
+  uint16_t reserved_1;		// 10
+  uint32_t user_info_0;		// 12: data for info box
+  uint32_t user_info_1;		// 16: data for info box
+  uint32_t bios_mem_size;	// 20: BIOS memory size (in bytes)
+  uint16_t xmem_0;		// 24: extended mem area 0 (start:size in MB; 12:4 bits)
+  uint16_t xmem_1;		// 26: extended mem area 1
+  uint16_t xmem_2;		// 28: extended mem area 2
+  uint16_t xmem_3;		// 30: extended mem area 3
+  uint32_t file;		// 32: start of gfx file
+  uint32_t archive_start;	// 36: start of cpio archive
+  uint32_t archive_end;		// 40: end of cpio archive
+  uint32_t mem0_start;		// 44: low free memory start
+  uint32_t mem0_end;		// 48: low free memory end
+} gfx_config_t;
+
+
+// menu description (18 bytes)
+typedef struct __attribute__ ((packed)) {
+  uint16_t entries;
+  uint32_t default_entry;	// seg:ofs
+  uint32_t label_list;		// seg:ofs
+  uint16_t label_size;
+  uint32_t arg_list;		// seg:ofs
+  uint16_t arg_size;
+} menu_t;
+
+
+// e820 mem descriptor
+typedef struct __attribute__ ((packed)) {
+  uint64_t base;
+  uint64_t len;
+  uint32_t type;
+  uint32_t cont;
+} mem_info_t;
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// global file descriptor, implicitly used by read(), getc(), fgets()
+fd_t fd;
+
+gfx_config_t gfx_config;
+menu_t menu;
+
+struct {
+  uint32_t jmp_table[12];
+  uint16_t code_seg;
+  char fname_buf[64];
+} gfx;
+
+unsigned comboot, comboot_len;
+unsigned io_buf, io_buf_len;
+unsigned menu_buf, menu_buf_len;
+unsigned freemem, freemem_len;
+
+unsigned initrd_end;
+unsigned kernel_start;
+
+int timeout;
+
+char cmdline[MAX_CMDLINE_LEN];
+char current_label[64];
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+int open(char *name);
+int open32(uint32_t name);
+int read(void *buf, int size);
+int read32(uint32_t buf, int size);
+int getc(void);
+char *fgets(char *s, int size);
+
+int strlen(char *s);
+int strcmp(char *s1, char *s2);
+char *skip_spaces(char *s);
+char *skip_nonspaces(char *s);
+void chop_line(char *s);
+
+int atoi(char *s);
+
+uint32_t get_config_file_name32(void);
+int read_config_file(void);
+
+unsigned magic_ok(uint32_t buf);
+unsigned find_file(uint32_t buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len);
+
+int get_mem_info(mem_info_t *mi);
+int gfx_init(char *file);
+void gfx_done(void);
+int gfx_input(void);
+int gfx_menu_init(void);
+
+void gfx_infobox32(int type, uint32_t str1, uint32_t str2);
+void gfx_infobox(int type, char *str1, char *str2);
+
+void boot(void);
+void show_message(char *file);
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int main(int argc, char **argv)
+{
+  x86regs_t r;
+  uint8_t syslinux_id;
+  int menu_index;
+
+  r.eax = 0x0a;		// Get Derivative-Specific Information
+  r.ecx = 9;
+  x86int(0x22, &r);
+  syslinux_id = (uint8_t) r.eax;
+  gfx_config.sector_shift = (uint8_t) r.ecx;
+  gfx_config.boot_drive = (uint8_t) r.edx;
+
+  // define our memory layout
+  // all must be at least 16 bytes aligned
+
+  // 64k comboot code
+  comboot = comboot_seg() << 4;
+  comboot_len = 0x10000;
+
+  // 16k file io buffer
+  io_buf = comboot + comboot_len;
+  io_buf_len = 0x4000;
+
+  // 64k for parsed menu data
+  menu_buf = io_buf + io_buf_len;
+  menu_buf_len = 0x10000;
+
+  // use remaining mem for gfx core
+  freemem = menu_buf + menu_buf_len;
+  // comboot api passes low memory end at address 2
+  freemem_len = ((*(uint16_t *) 2) << 4) - freemem;
+
+  gfx_config.bootloader = 1;
+  gfx_config.sysconfig_size = sizeof gfx_config;
+  gfx_config.bootloader_seg = comboot >> 4;
+
+  // not gfx_cb() directly, we need a wrapper
+  gfx_config.callback = (uint32_t) _gfx_cb;
+
+  if(syslinux_id == ID_PXELINUX) {
+    gfx_config.sector_shift = 11;
+    gfx_config.boot_drive = 0;
+  }
+
+  if(argc < 2) {
+    printf("Usage: gfxboot.com bootlogo_file [message_file]\n");
+    if(argc > 2) show_message(argv[2]);
+
+    return 0;
+  }
+
+  if(read_config_file()) {
+    printf("Error reading config file\n");
+    if(argc > 2) show_message(argv[2]);
+
+    return 0;
+  }
+
+  if(gfx_init(argv[1])) {
+    printf("Error setting up gfxboot\n");
+    if(argc > 2) show_message(argv[2]);
+
+    return 0;
+  }
+
+  gfx_menu_init();
+
+  for(;;) {
+    menu_index = gfx_input();
+
+    // abort gfx, return to text mode prompt
+    if(menu_index == -1) {
+      gfx_done();
+      break;
+    }
+
+    // get label name, is needed later for messages etc.
+    memcpy(current_label, cmdline, sizeof current_label);
+    current_label[sizeof current_label - 1] = 0;
+    *skip_nonspaces(current_label) = 0;
+
+    // does not return if it succeeds
+    boot();
+  }
+
+  if(argc > 2) show_message(argv[2]);
+
+  return 0;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// return:
+//   0: ok, -1: error
+//
+int open32(uint32_t name)
+{
+  x86regs_t r;
+
+  fd.handle = 0;
+  fd.data_len = fd.buf_idx = 0;
+
+  r.esi = name & 0xf;
+  r.eax = 0x06;		// Open file
+  r.es = name >> 4;
+  x86int(0x22, &r);
+
+  fd.block_size = r.ecx;
+  fd.file_size = r.eax;
+
+  if(
+    (r.eflags & X86_CF) ||
+    !fd.file_size || !fd.block_size || fd.block_size > io_buf_len
+  ) return -1;
+
+  fd.handle = r.esi;
+
+  fd.buf = io_buf;
+  fd.buf_size = io_buf_len / fd.block_size;
+
+  // printf("block size = 0x%x, file size = %d\n", fd.block_size, fd.file_size);
+
+  return 0;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// return:
+//   0: ok, -1: error
+//
+int open(char *name)
+{
+  return open32((uint32_t) name + comboot);
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// Note: buf is 32bit address
+//
+int read32(uint32_t buf, int size)
+{
+  x86regs_t r;
+  int i, len = 0;
+
+  while(size) {
+    i = fd.data_len - fd.buf_idx;
+
+    if(i > 0) {
+      i = i < size ? i : size;
+      memcpy32(buf, fd.buf + fd.buf_idx, i);
+      len += i;
+      buf += i;
+      size -= i;
+      fd.buf_idx += i;
+    }
+
+    if(!size || !fd.handle) break;
+
+    r.eax = 0x07;		// Read file
+    r.ecx = fd.buf_size;
+    r.ebx = 0;
+    r.es = fd.buf >> 4;
+    r.esi = fd.handle;
+    x86int(0x22, &r);
+    fd.handle = r.esi;
+    fd.data_len = r.ecx;
+    fd.buf_idx = 0;
+  }
+
+  return len;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int read(void *buf, int size)
+{
+  return read32((uint32_t) buf + comboot, size);
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int getc()
+{
+  char buf[1];
+
+  return read(buf, 1) ? *buf : EOF;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+char *fgets(char *s, int size)
+{
+  char *buf = s;
+  int c = EOF;
+
+  while(size--) {
+    c = getc();
+    if(c == EOF) break;
+    *buf++ = c;
+    if(c == 0x0a) break;
+  }
+
+  *buf = 0;
+
+  return c == EOF && s == buf ? 0 : s;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int strlen(char *s)
+{
+  char *s0 = s + 1;
+
+  while(*s++);
+
+  return s - s0;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int strcmp(char *s1, char *s2)
+{
+  while(*s1 && *s1 == *s2) s1++, s2++;
+
+  return (uint8_t) *s1 - (uint8_t) *s2;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+char *skip_spaces(char *s)
+{
+  while(*s && (*s == ' ' || *s == '\t')) s++;
+
+  return s;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+char *skip_nonspaces(char *s)
+{
+  while(*s && *s != ' ' && *s != '\t') s++;
+
+  return s;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void chop_line(char *s)
+{
+  int i = strlen(s);
+
+  if(!i) return;
+
+  while(--i >= 0) {
+    if(s[i] == ' ' || s[i] == '\t' || s[i] == '\n') {
+      s[i] = 0;
+    }
+    else {
+      break;
+    }
+  }
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int atoi(char *s)
+{
+  int i = 0, sign = 1;
+
+  if(*s == '-') s++, sign = -1;
+  if(*s == '+') s++;
+
+  while(*s >= '0' && *s <= '9') {
+    i = 10 * i + *s++ - '0';
+  }
+
+  return sign * i;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// return:
+//   config file name (32 bit address)
+//
+uint32_t get_config_file_name32()
+{
+  x86regs_t r;
+
+  r.eax = 0x0e;		// Get configuration file name
+  x86int(0x22, &r);
+
+  return (r.es << 4) + (uint16_t) r.ebx;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// Read and parse syslinux config file.
+//
+// return:
+//   0: ok, 1: error
+//
+int read_config_file(void)
+{
+  char *s, *t, buf[MAX_CONFIG_LINE_LEN];
+  unsigned u;
+  int menu_idx = 0;
+
+  // clear memory before we start
+  memset32(menu_buf, 0, menu_buf_len);
+
+  if(open32(get_config_file_name32()) == -1) return 1;
+
+  while((s = fgets(buf, sizeof buf))) {
+    chop_line(s);
+    s = skip_spaces(s);
+    if(!*s || *s == '#') continue;
+    t = skip_nonspaces(s);
+    if(*t) *t++ = 0;
+    t = skip_spaces(t);
+
+    if(!strcmp(s, "timeout")) {
+      timeout = atoi(t);
+    }
+
+    if(!strcmp(s, "default")) {
+      u = strlen(t);
+      if(u > MENU_LABEL_SIZE - 1) u = MENU_LABEL_SIZE - 1;
+      memcpy32(menu_buf, comboot + (uint32_t) t, u);
+    }
+
+    if(!strcmp(s, "label")) {
+      menu_idx++;
+      if(menu_idx < MAX_MENU_ENTRIES) {
+        u = strlen(t);
+        if(u > MENU_LABEL_SIZE - 1) u = MENU_LABEL_SIZE - 1;
+        memcpy32(
+          menu_buf + menu_idx * MENU_LABEL_SIZE,
+          comboot + (uint32_t) t,
+          u
+        );
+      }
+    }
+
+    if(!strcmp(s, "append")) {
+      if(menu_idx < MAX_MENU_ENTRIES) {
+        u = strlen(t);
+        if(u > MENU_ARG_SIZE - 1) u = MENU_ARG_SIZE - 1;
+        memcpy32(
+          menu_buf + menu_idx * MENU_ARG_SIZE + MAX_MENU_ENTRIES * MENU_LABEL_SIZE,
+          comboot + (uint32_t) t,
+          u
+        );
+      }
+    }
+  }
+
+  menu.entries = menu_idx;
+  menu.label_size = MENU_LABEL_SIZE;
+  menu.arg_size = MENU_ARG_SIZE;
+  menu.default_entry = ((menu_buf >> 4) << 16) + (menu_buf & 0xf);
+  u = menu_buf + MENU_LABEL_SIZE;
+  menu.label_list = ((u >> 4) << 16) + (u & 0xf);
+  u = menu_buf + MAX_MENU_ENTRIES * MENU_LABEL_SIZE + MENU_ARG_SIZE;
+  menu.arg_list = ((u >> 4) << 16) + (u & 0xf);
+
+  return 0;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// Check header and return code start offset.
+//
+// Note: buf is 32bit address
+//
+unsigned magic_ok(uint32_t buf)
+{
+  if(
+    _mem32(buf) == 0x0b2d97f00 &&		/* magic id */
+    (_mem8(buf + 4) == 8)			/* version 8 */
+  ) {
+    return _mem32(buf + 8);
+  }
+
+  return 0;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// Search cpio archive for gfx file.
+//
+// Note: buf is 32bit address
+//
+unsigned find_file(uint32_t buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len)
+{
+  unsigned i, fname_len, code_start = 0;
+
+  *gfx_file_start = 0;
+
+  for(i = 0; i < len;) {
+    if((len - i) >= 0x1a && _mem16(buf + i) == 0x71c7) {
+      fname_len = _mem16(buf + i + 20);
+      *file_len = _mem16(buf + i + 24) + (_mem16(buf + i + 22) << 16);
+      i += 26 + fname_len;
+      i = ((i + 1) & ~1);
+      if((code_start = magic_ok(buf + i))) {
+        *gfx_file_start = i;
+        return code_start;
+      }
+      i += *file_len;
+      i = ((i + 1) & ~1);
+    }
+    else {
+      break;
+    }
+  }
+
+  return code_start;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void gfx_cb(x86regs_t *r)
+{
+  uint8_t f_nr = r->eax;
+
+  switch(f_nr) {
+    case 0:	// cb_status
+      // edx = filename buffer (64 bytes)
+      r->edx = comboot + (uint32_t) gfx.fname_buf;
+      r->eax = 0;
+      // printf("<0x%x, %p + 0x%x>", r->edx, gfx.fname_buf, comboot);
+      break;
+
+    case 1:	// cb_fopen
+      // file name in gfx.fname_buf
+      // al = 0: ok, 1: file not found
+      // ecx = file length (al = 0)
+      if(open(gfx.fname_buf) == -1) {
+        r->eax = 1;
+      }
+      else {
+        r->eax = 0;
+        r->ecx = fd.file_size;
+      }
+      break;
+
+    case 2:	// cb_fread
+      // edx = buffer address (linear)
+      // ecx = data length (< 64k)
+      if(!fd.handle) {
+        r->eax = r->ecx = 0;
+        break;
+      }
+      r->esi = fd.handle;
+      r->ebx = 0;
+      r->es = io_buf >> 4;
+      r->ecx = io_buf_len / fd.block_size;
+      r->eax = 7;
+      x86int(0x22, r);
+      fd.handle = r->esi;
+      if((r->eflags & X86_CF)) {
+        r->eax = 1;
+      }
+      else {
+        r->edx = io_buf;
+        r->eax = 0;
+      }
+      break;
+
+    case 3:	// cb_getcwd
+      // edx filename
+      r->eax = 0x1f;		// Get current working directory
+      x86int(0x22, r);
+      r->edx = (r->es << 4) + (uint16_t) r->ebx;
+      r->eax = 0;
+      break;
+
+    case 4:	// cb_chdir
+      r->es = comboot >> 4;
+      r->ebx = (uint32_t) gfx.fname_buf;
+      r->eax = 0x25;		// Get current working directory
+      x86int(0x22, r);
+      break;
+
+    case 5:	// cb_readsector
+      // in: edx = sector
+      // out: edx = buffer (linear address)
+      r->esi = r->edi = 0;
+      r->ebx = 0;
+      r->es = io_buf >> 4;
+      r->ecx = 1;
+      r->eax = 0x19;		// Read disk
+      x86int(0x22, r);
+      r->eax = 0;
+      r->edx = io_buf;
+      break;
+
+    case 6:	// cb_mount
+      r->eax = 0x26;
+      x86int(0x22, r);
+      r->eax = (r->eflags & X86_CF) ? 1 : 0;
+      break;
+
+    default:
+      r->eax = 0xff;
+  }
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// return:
+//   0: ok, 1: error
+//
+int get_mem_info(mem_info_t *mi)
+{
+  x86regs_t r;
+
+  mi->base = mi->len = mi->type = 0;
+
+  r.eax = 0xe820;
+  r.edx = 0x534d4150;
+  r.ebx = mi->cont;
+  r.ecx = 20;
+  r.es = comboot >> 4;
+  r.edi = (unsigned) mi;
+  x86int(0x15, &r);
+
+  mi->cont = 0;
+  if(r.eax != 0x534d4150 || (r.eflags & X86_CF)) return 1;
+  mi->cont = r.ebx;
+
+  return 0;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// return:
+//   0: ok, 1: error
+//
+int gfx_init(char *file)
+{
+  x86regs_t r;
+  int file_max, file_size, ofs;
+  unsigned u, code_start, file_start = 0, file_len = 0;
+  unsigned start2, len2, end2;
+  mem_info_t mi;
+
+  gfx_config.mem0_start = freemem;
+  gfx_config.mem0_end = freemem + freemem_len;
+
+  kernel_start = (GFX_MEMORY_START + GFX_MEMORY_SIZE) << 20;
+  initrd_end = 0;
+
+  gfx_config.xmem_0 = (GFX_MEMORY_START << 4) + GFX_MEMORY_SIZE;
+
+  r.eax = 0x28;		// Get memory size
+  x86int(0x22, &r);
+  u = (r.eflags & X86_CF) ? 0 : r.eax;
+
+  // round up
+  gfx_config.bios_mem_size = u ? ((u + 0xfffff) >> 20) << 20 : 256;
+
+  if(u > 0) {
+    // new interface
+
+    if(u < INITRD_MIN_MEMORY << 20) {
+      // ok, maybe there is a bigger block...
+
+      mi.cont = 0;
+      start2 = len2 = 0;
+      while(!get_mem_info(&mi)) {
+#if 0
+        printf(
+          "%08x%08x %08x%08x %08x %08x\n",
+          (unsigned) (mi.base >> 32), (unsigned) mi.base,
+          (unsigned) (mi.len >> 32), (unsigned) mi.len,
+          mi.type, mi.cont
+        );
+#endif
+        if(mi.type == 1) {
+          if((mi.base >> 32) || (mi.base + mi.len) >> 32) break;
+          if(mi.len > len2) {
+            start2 = mi.base;
+            len2 = mi.len;
+          }
+        }
+
+        if(!mi.cont) break;
+      }
+
+#if 0
+      printf("largest: %08x %08x\n", start2, len2);
+      getchar();
+#endif
+
+      if(len2 && len2 > 2 << 20 && len2 > u) {
+        // align to full MBs
+        end2 = (start2 + len2) & ~0xfffff;
+        start2 = (start2 + 0xfffff) & ~0xfffff;
+        len2 = end2 - start2;
+      }
+      else {
+        start2 = len2 = 0;
+      }
+
+      if(len2) {
+        u = len2;
+        initrd_end = end2;
+        // we could relocate the kernel as well...
+        // kernel_start = start2;
+      }
+    }
+
+    if(u < INITRD_MIN_MEMORY << 20) {
+      // a bit too small for us
+      printf("%u MB RAM is a bit small... - giving up\n", u >> 20);
+
+      return 1;
+    }
+  }
+
+#if 0
+  printf("mem = 0x%05x, mem0_start = 0x%05x, mem0_end = 0x%05x\n",
+    gfx.mem, gfx_config.mem0_start, gfx_config.mem0_end
+  );
+#endif
+
+  if(open(file) == -1) return 1;
+
+  // leave room for later alignment
+  gfx_config.archive_start = gfx_config.mem0_start + 0x10;
+
+  // read at most that much
+  file_size = file_max = gfx_config.mem0_end - gfx_config.archive_start;
+
+  if(fd.file_size != -1 && fd.file_size > file_size) return 1;
+
+  // if we have the real size, use it
+  if(fd.file_size != -1) file_size = fd.file_size;
+
+  file_size = read32(gfx_config.archive_start, file_size);
+
+  if(!file_size || file_size == file_max) return 1;
+
+  gfx_config.archive_end = gfx_config.archive_start + file_size;
+
+  // update free mem pointer & align it a bit
+  gfx_config.mem0_start = (gfx_config.archive_end + 3) & ~3;
+
+  // locate file inside cpio archive
+  if(!(code_start = find_file(gfx_config.archive_start, file_size, &file_start, &file_len))) {
+    printf("%s: invalid file format\n", file);
+
+    return 1;
+  }
+
+#if 0
+  printf("code_start = 0x%x, archive_start = 0x%x, file size = 0x%x, file_start = 0x%x, file_len = 0x%x\n",
+    code_start, gfx_config.archive_start, file_size, file_start, file_len
+  );
+#endif
+
+  if((ofs = (gfx_config.archive_start + file_start + code_start) & 0xf)) {
+    printf("oops: needs to be aligned!\n");
+
+    memcpy32(gfx_config.archive_start - ofs, gfx_config.archive_start, file_size);
+    gfx_config.archive_start -= ofs;
+    gfx_config.archive_end -= ofs;
+
+    return 1;
+  }
+
+  gfx_config.file = gfx_config.archive_start + file_start;
+  gfx.code_seg = (gfx_config.file + code_start) >> 4;
+
+  for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) {
+    gfx.jmp_table[u] = (gfx.code_seg << 16) + _mem16(gfx_config.file + code_start + 2 * u);
+  }
+
+#if 0
+  for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) {
+    printf("%d: 0x%08x\n", u, gfx.jmp_table[u]);
+  }
+#endif
+
+  // we are ready to start
+
+  r.esi = comboot + (uint32_t) &gfx_config;
+  farcall(gfx.jmp_table[GFX_BC_INIT], &r);
+
+  if((r.eflags & X86_CF)) {
+    printf("graphics initialization failed\n");
+
+    return 1;
+  }
+
+  return 0;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void gfx_done()
+{
+  x86regs_t r;
+
+  farcall(gfx.jmp_table[GFX_BC_DONE], &r);
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// return:
+//   boot menu index (-1: go to text mode prompt)
+//
+int gfx_input()
+{
+  x86regs_t r;
+
+  r.edi = comboot + (uint32_t) cmdline;
+  r.ecx = sizeof cmdline;
+  r.eax = timeout * 182 / 100;
+  timeout = 0;		// use timeout only first time
+  farcall(gfx.jmp_table[GFX_BC_INPUT], &r);
+  if((r.eflags & X86_CF)) r.eax = 1;
+
+  if(r.eax == 1) return -1;
+
+  return r.ebx;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int gfx_menu_init()
+{
+  x86regs_t r;
+
+  r.esi = comboot + (uint32_t) &menu;
+  farcall(gfx.jmp_table[GFX_BC_MENU_INIT], &r);
+
+  return 0;
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void gfx_infobox(int type, char *str1, char *str2)
+{
+  gfx_infobox32(type, comboot + (uint32_t) str1, str2 ? comboot + (uint32_t) str2 : 0);
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void gfx_infobox32(int type, uint32_t str1, uint32_t str2)
+{
+  x86regs_t r;
+
+  r.eax = type;
+  r.esi = str1;
+  r.edi = str2;
+  farcall(gfx.jmp_table[GFX_BC_INFOBOX_INIT], &r);
+  r.edi = r.eax = 0;
+  farcall(gfx.jmp_table[GFX_BC_INPUT], &r);
+  farcall(gfx.jmp_table[GFX_BC_INFOBOX_DONE], &r);
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// Load & run kernel.
+//
+// Returns only on error.
+//
+void boot()
+{
+  x86regs_t r;
+
+  r.es = comboot >> 4;
+  r.ebx = (uint32_t) cmdline;
+  r.edi = kernel_start;			// kernel load address
+  r.edx = initrd_end;			// end of initrd load area (or 0)
+  r.esi = (uint32_t) _syslinux_hook;	// cs:si = syslinux callbacks
+  r.eax = 0x27;				// Load & run kernel (extended API)
+
+  x86int(0x22, &r);
+  if(!(r.eflags & X86_CF)) return;
+
+  r.es = comboot >> 4;
+  r.ebx = (uint32_t) cmdline;
+  r.eax = 3;		// Run command
+  x86int(0x22, &r);
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void syslinux_hook(x86regs_t *r)
+{
+  uint8_t f_nr = r->eax;
+
+  // bit 7: 0/1 continue/don't continue kernel loading in syslinux
+
+  switch(f_nr & 0x7f) {
+    case 0:		// abort kernel/initrd loading
+      gfx_infobox(0, "abort kernel loading", 0);
+      break;
+
+    case 1:		// kernel/initrd not found
+      gfx_infobox(0, "kernel not found: ", current_label);
+      break;
+
+    case 2:		// kernel corrupt
+      gfx_infobox(0, "kernel broken", 0);
+      break;
+
+    case 3:		// out of memory (while initrd loading)
+      gfx_infobox(0, "out of memory", 0);
+      break;
+
+    case 4:		// progress start
+      r->eax = r->ecx >> gfx_config.sector_shift;	// kernel size in sectors
+      r->esi = comboot + (uint32_t) current_label;
+      farcall(gfx.jmp_table[GFX_BC_PROGRESS_INIT], r);
+      break;
+
+    case 5:		// progress increment
+      // always 64k
+      r->eax = 0x10000 >> gfx_config.sector_shift;
+      farcall(gfx.jmp_table[GFX_BC_PROGRESS_UPDATE], r);
+      break;
+
+    case 6:		// progress end: kernel loaded, stop gfxboot
+      farcall(gfx.jmp_table[GFX_BC_PROGRESS_DONE], r);
+      gfx_done();
+      break;
+
+    case 7:		// stop gfxboot
+      gfx_done();
+      break;
+  }
+}
+
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void show_message(char *file)
+{
+  int c;
+
+  if(open(file) == -1) return;
+
+  while((c = getc()) != EOF) {
+    if(c < ' ' && c != '\n' && c != '\t') continue;
+    printf("%c", c);
+  }
+}
+
+
diff --git a/modules/libio.asm b/modules/libio.asm
new file mode 100644
index 0000000..1f77b44
--- /dev/null
+++ b/modules/libio.asm
@@ -0,0 +1,854 @@
+;
+; libio.asm
+;
+; A very minimalistic libc fragment.
+;
+; Copyright (c) 2009 Steffen Winterfeldt.
+;
+; This program is free software; you can redistribute it and/or modify it
+; under the terms of the GNU General Public License as published by the Free
+; Software Foundation, Inc., 53 Temple Place Ste 330, Boston MA 02111-1307,
+; USA; either version 2 of the License, or (at your option) any later
+; version; incorporated herein by reference.
+;
+
+
+; max argv elements passed to main()
+%define MAX_ARGS	8
+
+
+		bits 16
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; interface functions
+;
+; Make sure not to modify registers!
+;
+
+		global printf
+		global getchar
+		global clrscr
+		global memcpy
+		global memcpy32
+		global memset
+		global memset32
+		global x86int
+		global farcall
+		global reboot
+
+		global _gfx_cb
+		extern gfx_cb
+		global _syslinux_hook
+		extern syslinux_hook
+
+		global _start
+		extern _main
+
+		extern __bss_start
+
+		section .init
+
+_start:
+		cld
+
+		; clear static memory
+		mov di,__bss_start
+		mov cx,sp
+		sub cx,di
+		xor ax,ax
+		rep stosb
+
+		; parse args
+		mov ebx,80h
+		movzx si,byte [bx]
+		mov byte [si+81h],0dh	; just make sure
+		xor ecx,ecx
+		sub sp,MAX_ARGS * 4
+		mov ebp,esp
+		inc cx
+cmd_10:
+		inc bx
+		call skip_spaces
+		cmp al,0dh
+		jz cmd_60
+		imul si,cx,4
+		mov [bp+si],ebx
+		call skip_nonspaces
+		mov byte [bx],0
+		inc cx
+		cmp cx,MAX_ARGS
+		jae cmd_60
+		cmp al,0dh
+		jnz cmd_10
+cmd_60:
+		mov byte [bx],0
+
+		mov [bp],ebx		; argv[0] = ""
+
+		push ebp
+		push ecx
+
+		call dword _main
+
+		add sp,MAX_ARGS * 4 + 8
+
+		ret
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+skip_spaces:
+		mov al,[bx]
+		cmp al,0dh
+		jz skip_spaces_90
+		cmp al,' '
+		jz skip_spaces_10
+		cmp al,9
+		jnz skip_spaces_90
+skip_spaces_10:
+		inc bx
+		jmp skip_spaces
+skip_spaces_90:
+		ret
+
+skip_nonspaces:
+		mov al,[bx]
+		cmp al,0dh
+		jz skip_nonspaces_90
+		cmp al,' '
+		jz skip_nonspaces_90
+		cmp al,9
+		jz skip_nonspaces_90
+		inc bx
+		jmp skip_nonspaces
+skip_nonspaces_90:
+		ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		section .text
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Write text to console.
+;
+; args on stack
+;
+; Note: 32 bit call/ret!
+;
+printf:
+		mov [pf_args],sp
+
+		pushad
+
+		call pf_next_arg
+		call pf_next_arg
+		mov si,ax
+printf_10:
+		lodsb
+		or al,al
+		jz printf_90
+		cmp al,'%'
+		jnz printf_70
+		mov byte [pf_pad],' '
+		lodsb
+		dec si
+		cmp al,'0'
+		jnz printf_20
+		mov [pf_pad],al
+printf_20:
+		call get_number
+		mov [pf_num],ecx
+		lodsb
+		or al,al
+		jz printf_90
+		cmp al,'%'
+		jz printf_70
+
+		cmp al,'S'
+		jnz printf_23
+		mov byte [pf_raw_char],1
+		jmp printf_24
+printf_23:
+		cmp al,'s'
+		jnz printf_30
+printf_24:
+		push si
+
+		call pf_next_arg
+		mov si,ax
+		call puts
+
+		sub ecx,[pf_num]
+		neg ecx
+		mov al,' '
+		call putc_n
+
+		pop si
+
+		mov byte [pf_raw_char],0
+		jmp printf_10
+
+printf_30:
+		cmp al,'u'
+		jnz printf_35
+
+		mov dx,10
+printf_31:
+		push si
+
+		call pf_next_arg
+		or dh,dh
+		jz printf_34
+		test eax,eax
+		jns printf_34
+		neg eax
+		push eax
+		mov al,'-'
+		call putc
+		pop eax
+printf_34:
+		mov cl,[pf_num]
+		mov ch,[pf_pad]
+		call number
+		call puts
+
+		pop si
+
+		jmp printf_10
+
+printf_35:
+		cmp al,'x'
+		jnz printf_36
+
+printf_35a:
+		mov dx,10h
+		jmp printf_31
+
+printf_36:
+		cmp al,'d'
+		jnz printf_37
+printf_36a:
+		mov dx,10ah
+		jmp printf_31
+
+printf_37:
+		cmp al,'i'
+		jz printf_36a
+
+		cmp al,'p'
+		jnz printf_40
+		mov al,'0'
+		call putc
+		mov al,'x'
+		call putc
+		jmp printf_35a
+
+printf_40:
+		cmp al,'c'
+		jnz printf_45
+
+		push si
+		call pf_next_arg
+		call putc
+		pop si
+		jmp printf_10
+printf_45:
+
+		; more ...
+
+
+printf_70:
+		call putc
+		jmp printf_10
+printf_90:
+		popad
+
+		o32 ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Get next printf arg from [pf_args].
+;
+; return:
+;  eax		arg
+;
+; changes no regs
+;
+pf_next_arg:
+		movzx eax,word [pf_args]
+		add word [pf_args],4
+		mov eax,[eax]
+		ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Convert string to number.
+;
+;  si		string
+;
+; return:
+;  ecx		number
+;  si		points past number
+;  CF		not a number
+;
+get_number:
+
+		xor ecx,ecx
+		mov ah,1
+get_number_10:
+		lodsb
+		or al,al
+		jz get_number_90
+		sub al,'0'
+		jb get_number_90
+		cmp al,9
+		ja get_number_90
+		movzx eax,al
+		imul ecx,ecx,10
+		add ecx,eax
+		jmp get_number_10
+get_number_90:
+		dec si
+		shr ah,1
+		ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Convert a number to string.
+;
+;  eax		number
+;  cl		field size
+;  ch		padding char
+;  dl		base
+;
+; return:
+;  si		string
+;
+number:
+		mov di,num_buf
+		push ax
+		push cx
+		mov al,ch
+		mov cx,num_buf_end - num_buf
+		rep stosb
+		pop cx
+		pop ax
+		movzx cx,cl
+		movzx ebx,dl
+number_10:
+		xor edx,edx
+		div ebx
+		cmp dl,9
+		jbe number_20
+		add dl,27h
+number_20:
+		add dl,'0'
+		dec edi
+		mov [di],dl
+		or eax,eax
+		jz number_30
+		cmp di,num_buf
+		ja number_10
+number_30:
+		mov si,di
+		or cx,cx
+		jz number_90
+		cmp cx,num_buf_end - num_buf
+		jae number_90
+		mov si,num_buf_end
+		sub si,cx
+number_90:
+		ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Write string.
+;
+;  si		text
+;
+; return:
+;  cx		length
+;
+puts:
+		xor cx,cx
+puts_10:
+		lodsb
+		or al,al
+		jz puts_90
+		call putc
+		inc cx
+		jmp puts_10
+puts_90:
+		ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Write char multiple times.
+;
+;  al		char
+;  cx		count (does nothing if count <= 0)
+;
+putc_n:
+		cmp cx,0
+		jle putc_n_90
+		call putc
+		dec cx
+		jmp putc_n
+putc_n_90:
+		ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Print char.
+;
+;  al		char
+;
+putc:
+		pusha
+		cmp al,0ah
+		jnz putc_30
+		push ax
+		mov al,0dh
+		call putc_50
+		pop ax
+putc_30:
+		call putc_50
+		popa
+		ret
+putc_50:
+		mov bx,7
+		mov ah,0eh
+		int 10h
+		ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Read char from stdin.
+;
+; return:
+;  eax		char
+;
+; Note: 32 bit call/ret!
+;
+getchar:
+		pushad
+		mov ah,10h
+		int 16h
+		mov [gc_tmp],al
+		popad
+		movzx eax,byte [gc_tmp]
+		o32 ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; Clear screen.
+;
+; Note: 32 bit call/ret!
+;
+clrscr:
+		pushad
+		push es
+		push word 40h
+		pop es
+		mov ax,600h
+		mov bh,7
+		xor cx,cx
+		mov dl,[es:4ah]
+		or dl,dl
+		jnz clrscr_20
+		mov dl,80
+clrscr_20:
+		dec dl
+		mov dh,[es:84h]
+		or dh,dh
+		jnz clrscr_30
+		mov dh,24
+clrscr_30:
+		int 10h
+		mov ah,2
+		mov bh,[es:62h]
+		xor dx,dx
+		int 10h
+		pop es
+		popad
+		o32 ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; dst = memcpy(dst, src, size).
+;
+; args on stack
+;
+; return:
+;  eax		dst
+;
+; Note: 32 bit call/ret!
+;
+memcpy:
+		pushad
+
+		mov edi,[esp+0x20+4]
+		mov esi,[esp+0x20+8]
+		mov ecx,[esp+0x20+12]
+
+		rep movsb
+
+		popad
+
+		mov eax,[esp+4]
+
+		o32 ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; dst = memset(dst, val, size).
+;
+; args on stack
+;
+; return:
+;  eax		dst
+;
+; Note: 32 bit call/ret!
+;
+memset:
+		pushad
+
+		mov edi,[esp+0x20+4]
+		mov al,[esp+0x20+8]
+		mov ecx,[esp+0x20+12]
+
+		rep stosb
+
+		popad
+
+		mov eax,[esp+4]
+
+		o32 ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; dst = memset32(dst, val, size).
+;
+; args on stack
+;
+; return:
+;  eax		dst
+;
+; Note: 32 bit call/ret!
+;
+memset32:
+		pushad
+
+		push es
+
+		mov ebx,[esp+0x22+4]	; edi
+		mov al,[esp+0x22+8]
+		mov edx,[esp+0x22+12]
+
+memset32_20:
+		mov edi,ebx
+		mov ecx,ebx
+		and di,0fh
+		shr ecx,4
+		mov es,cx
+
+		mov ecx,0fff0h
+		cmp edx,ecx
+		ja memset32_40
+		mov ecx,edx
+memset32_40:
+		add ebx,ecx
+		sub edx,ecx
+
+		rep stosb
+
+		jnz memset32_20
+
+		pop es
+
+		popad
+
+		mov eax,[esp+4]
+
+		o32 ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; dst = memcpy32(dst, src, size).
+;
+; dst, src are 32bit linear addresses
+;
+; args on stack
+;
+; return:
+;  eax		dst
+;
+; Note: 32 bit call/ret!
+;
+memcpy32:
+		pushad
+
+		push ds
+		push es
+
+		mov ebx,[esp+0x24+4]	; edi
+		mov eax,[esp+0x24+8]	; esi
+		mov edx,[esp+0x24+12]
+
+memcpy32_20:
+		mov edi,ebx
+		mov ecx,ebx
+		and di,0fh
+		shr ecx,4
+		mov es,cx
+
+		mov esi,eax
+		mov ecx,eax
+		and si,0fh
+		shr ecx,4
+		mov ds,cx
+
+		mov ecx,0fff0h
+		cmp edx,ecx
+		ja memcpy32_40
+		mov ecx,edx
+memcpy32_40:
+		add ebx,ecx
+		add eax,ecx
+		sub edx,ecx
+
+		rep movsb
+
+		jnz memcpy32_20
+
+		pop es
+		pop ds
+
+		popad
+
+		mov eax,[esp+4]
+
+		o32 ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; x86int(int, *regs).
+;
+; args on stack
+;
+; Note: 32 bit call/ret!
+;
+x86int:
+		pushad
+
+		mov al,[esp+0x20+4]
+		mov [x86int_p],al
+		mov ebx,[esp+0x20+8]
+
+		mov ecx,[bx+8]
+		mov edx,[bx+0ch]
+		mov esi,[bx+10h]
+		mov edi,[bx+14h]
+		mov ebp,[bx+18h]
+		mov ah,[bx+1ch]		; eflags
+		sahf
+		mov eax,[bx]
+
+		mov es,[bx+22h]
+		mov fs,[bx+24h]
+		mov gs,[bx+26h]
+		mov ds,[bx+20h]
+
+		mov ebx,[cs:bx+4]
+
+		int 0h
+x86int_p	equ $-1
+
+		push ebx
+		mov ebx,[esp+0x24+8]
+		pop dword [cs:bx+4]
+
+		mov [cs:bx],eax
+		mov [cs:bx+20h],ds
+
+		mov ax,cs
+		mov ds,ax
+
+		mov [cs:bx+22h],es
+		mov [cs:bx+24h],fs
+		mov [cs:bx+26h],gs
+
+		mov es,ax
+		mov fs,ax
+		mov gs,ax
+
+		mov [bx+8],ecx
+		mov [bx+0ch],edx
+		mov [bx+10h],esi
+		mov [bx+14h],edi
+		mov [bx+18h],ebp
+		pushfd
+		pop dword [bx+1ch]
+
+		popad
+
+		o32 ret
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; farcall(addr, *regs).
+;
+; args on stack
+;
+; Note: 32 bit call/ret!
+;
+farcall:
+		pushad
+
+		mov ebx,[esp+0x20+8]
+
+		mov ecx,[bx+8]
+		mov edx,[bx+0ch]
+		mov esi,[bx+10h]
+		mov edi,[bx+14h]
+		mov ebp,[bx+18h]
+		mov ah,[bx+1ch]		; eflags
+		sahf
+		mov eax,[bx]
+
+		mov [farcall_stack],sp
+		sub word [farcall_stack],1000h	; 4k stack should be enough for gfxboot
+		mov [farcall_stack+2],ss
+
+		mov es,[bx+22h]
+		mov fs,[bx+24h]
+		mov gs,[bx+26h]
+		mov ds,[bx+20h]
+
+		mov ebx,[cs:bx+4]
+
+		call far [esp+0x20+4]
+
+		push ebx
+		mov ebx,[esp+0x24+8]
+		pop dword [cs:bx+4]
+
+		mov [cs:bx],eax
+		mov [cs:bx+20h],ds
+
+		mov ax,cs
+		mov ds,ax
+
+		mov [cs:bx+22h],es
+		mov [cs:bx+24h],fs
+		mov [cs:bx+26h],gs
+
+		mov es,ax
+		mov fs,ax
+		mov gs,ax
+
+		mov [bx+8],ecx
+		mov [bx+0ch],edx
+		mov [bx+10h],esi
+		mov [bx+14h],edi
+		mov [bx+18h],ebp
+		pushfd
+		pop dword [bx+1ch]
+
+		popad
+
+		o32 ret
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; wrapper around gfx_cb()
+;
+; we need to switch stack to ensure ss = cs = ds = es for gcc
+;
+_gfx_cb:
+		push cs
+		pop ds
+		push cs
+		pop es
+		mov [cb_stack],sp
+		mov [cb_stack+2],ss
+		lss sp,[farcall_stack]
+		sub sp,28h		; sizeof x86regs_t
+		mov [esp+18h],ebp
+		mov ebp,esp
+		push ebp
+		mov [bp],eax
+		mov [bp+4],ebx
+		mov [bp+8],ecx
+		mov [bp+0ch],edx
+		mov [bp+10h],esi
+		mov [bp+14h],edi
+		call dword gfx_cb
+		lea ebp,[esp+4]
+		mov eax,[bp]
+		mov ebx,[bp+4]
+		mov ecx,[bp+8]
+		mov edx,[bp+0ch]
+		mov esi,[bp+10h]
+		mov edi,[bp+14h]
+		mov ebp,[bp+18h]
+		lss sp,[cb_stack]
+		retf
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+; wrapper around syslinux_hook()
+;
+; ensure cs = ds = es for gcc
+;
+_syslinux_hook:
+		push cs
+		pop ds
+		push cs
+		pop es
+		sub sp,28h		; sizeof x86regs_t
+		mov [esp+18h],ebp
+		mov ebp,esp
+		push ebp
+		mov [bp],eax
+		mov [bp+4],ebx
+		mov [bp+8],ecx
+		mov [bp+0ch],edx
+		mov [bp+10h],esi
+		mov [bp+14h],edi
+		call dword syslinux_hook
+		lea ebp,[esp+4]
+		mov eax,[bp]
+		mov ebx,[bp+4]
+		mov ecx,[bp+8]
+		mov edx,[bp+0ch]
+		mov esi,[bp+10h]
+		mov edi,[bp+14h]
+		mov ebp,[bp+18h]
+		add sp,28h+4
+		retf
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+reboot:
+		mov word [472h],1234h
+		jmp 0ffffh:0
+		int 19h
+		jmp $
+
+
+; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		section .data
+
+farcall_stack	dd 0
+cb_stack	dd 0
+
+; buffer for number conversions
+; must be large enough for ps_status_info()
+num_buf		times 23h db 0
+num_buf_end	db 0
+
+; temp data for printf
+pf_args		dw 0
+pf_num		dd 0
+pf_sig		db 0
+pf_pad		db 0
+pf_raw_char	db 0
+gc_tmp		db 0
+
diff --git a/modules/libio.h b/modules/libio.h
new file mode 100644
index 0000000..16af520
--- /dev/null
+++ b/modules/libio.h
@@ -0,0 +1,133 @@
+/*
+ *
+ * libio.h	include file for libio
+ *
+ * Copyright (c) 2009 Steffen Winterfeldt.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, Inc., 53 Temple Place Ste 330, Boston MA
+ * 02111-1307, USA; either version 2 of the License, or (at your option) any
+ * later version; incorporated herein by reference.
+ *
+ */
+
+#ifndef _LIBIO_H
+#define _LIBIO_H
+
+
+asm(".code16gcc\n");
+
+
+#define int8_t char
+#define int16_t short
+#define int32_t int
+#define int64_t long long
+#define uint8_t unsigned char
+#define uint16_t unsigned short
+#define uint32_t unsigned
+#define uint64_t unsigned long long
+
+#define X86_CF 0x0001
+#define X86_PF 0x0004
+#define X86_AF 0x0010
+#define X86_ZF 0x0040
+#define X86_SF 0x0080
+#define X86_TF 0x0100
+#define X86_IF 0x0200
+#define X86_DF 0x0400
+#define X86_OF 0x0800
+
+#define EOF -1
+
+#define main _main
+
+
+typedef struct __attribute ((packed)) {
+  uint32_t eax, ebx, ecx, edx, esi, edi, ebp, eflags;
+  uint16_t ds, es, fs, gs;
+} x86regs_t;
+
+
+static inline uint16_t comboot_seg(void)
+{
+  uint16_t u;
+
+  asm("mov %%cs, %0" : "=r" (u));
+
+  return u;
+}
+
+
+static inline uint8_t _mem8(uint32_t p)
+{
+  uint8_t u;
+
+  asm(
+    "movl %1,%%esi\n"
+    "shrl $4,%%esi\n"
+    "mov %%si,%%fs\n"
+    "movl %1,%%esi\n"
+    "and $15, %%si\n"
+    "movb %%fs:(%%si),%0\n"
+    : "=abcd" (u) : "r" (p) : "si"
+  );
+
+  return u;
+}
+
+
+static inline uint16_t _mem16(uint32_t p)
+{
+  uint16_t u;
+
+  asm(
+    "movl %1,%%esi\n"
+    "shrl $4,%%esi\n"
+    "mov %%si,%%fs\n"
+    "movl %1,%%esi\n"
+    "and $15, %%si\n"
+    "movw %%fs:(%%si),%0\n"
+    : "=r" (u) : "r" (p) : "si"
+  );
+
+  return u;
+}
+
+
+static inline uint32_t _mem32(uint32_t p)
+{
+  uint32_t u;
+
+  asm(
+    "movl %1,%%esi\n"
+    "shrl $4,%%esi\n"
+    "mov %%si,%%fs\n"
+    "movl %1,%%esi\n"
+    "and $15, %%si\n"
+    "movl %%fs:(%%si),%0\n"
+    : "=r" (u) : "r" (p) : "si"
+  );
+
+  return u;
+}
+
+
+int _main(int argc, char **argv);
+void printf(char *format, ...) __attribute__ ((format (printf, 1, 2)));
+int getchar(void);
+void clrscr(void);
+void *memcpy(void *dest, const void *src, int n);
+uint32_t memcpy32(uint32_t dest, uint32_t src, int n);
+void *memset(void *dest, int c, int n);
+uint32_t memset32(uint32_t dest, int c, int n);
+void x86int(unsigned intr, x86regs_t *regs);
+void farcall(uint32_t seg_ofs, x86regs_t *regs);
+void gfx_cb(x86regs_t *r);
+void _gfx_cb(void);
+void syslinux_hook(x86regs_t *r);
+void _syslinux_hook(void);
+void reboot(void);
+
+#endif	/* _LIBIO_H */
+
openSUSE Build Service is sponsored by