summaryrefslogtreecommitdiff
path: root/patches
diff options
context:
space:
mode:
authorDaniel Baumann <mail@daniel-baumann.ch>2014-02-08 07:45:50 -0800
committerDaniel Baumann <mail@daniel-baumann.ch>2014-02-08 07:45:50 -0800
commit10177fb8fe31b38d78cbf299addf0f6bd0f08c86 (patch)
tree4fecc8217000b83d88b0bc813f199174a392d64e /patches
Import gfxboot_4.5.2-1.1.orig.tar.xz
[dgit import orig gfxboot_4.5.2-1.1.orig.tar.xz]
Diffstat (limited to 'patches')
-rw-r--r--patches/gfxboot_c32.diff1080
-rw-r--r--patches/grub-0.97.diff1006
-rw-r--r--patches/lilo-22.7.diff1133
-rw-r--r--patches/syslinux-3.63.diff2352
-rw-r--r--patches/syslinux-3.82/01_isolinux_mount_chdir.diff175
-rw-r--r--patches/syslinux-3.82/02_comboot_hooks.diff117
-rw-r--r--patches/syslinux-3.82/03_comboot_ne_real_mode_seg.diff44
-rw-r--r--patches/syslinux-3.82/04_new_quiet_flag.diff43
-rw-r--r--patches/syslinux-3.82/05_variable_kernel_address.diff42
-rw-r--r--patches/syslinux-3.82/06_comboot_new_api.diff189
-rw-r--r--patches/syslinux-3.82/07_no_append.diff45
-rw-r--r--patches/syslinux-3.82/08_kernel_size.diff36
-rw-r--r--patches/syslinux-3.82/09_doc.diff43
-rw-r--r--patches/syslinux-3.82/10_gfxboot_c.diff2099
-rw-r--r--patches/syslinux-3.83/01_isolinux_mount_chdir.diff175
-rw-r--r--patches/syslinux-3.83/02_comboot_hooks.diff117
-rw-r--r--patches/syslinux-3.83/03_comboot_ne_real_mode_seg.diff44
-rw-r--r--patches/syslinux-3.83/04_new_quiet_flag.diff43
-rw-r--r--patches/syslinux-3.83/05_variable_kernel_address.diff42
-rw-r--r--patches/syslinux-3.83/06_comboot_new_api.diff189
-rw-r--r--patches/syslinux-3.83/07_no_append.diff45
-rw-r--r--patches/syslinux-3.83/08_kernel_size.diff36
-rw-r--r--patches/syslinux-3.83/09_doc.diff43
-rw-r--r--patches/syslinux-3.83/10_gfxboot_c.diff2099
-rw-r--r--patches/syslinux-3.86/01_isolinux_mount_chdir.diff175
-rw-r--r--patches/syslinux-3.86/02_comboot_new_api.diff70
-rw-r--r--patches/syslinux-3.86/03_doc.diff25
-rw-r--r--patches/syslinux-3.86/04_no_gfxboot_com.diff13
28 files changed, 11520 insertions, 0 deletions
diff --git a/patches/gfxboot_c32.diff b/patches/gfxboot_c32.diff
new file mode 100644
index 0000000..4dae3ea
--- /dev/null
+++ b/patches/gfxboot_c32.diff
@@ -0,0 +1,1080 @@
+diff --git a/Makefile b/Makefile
+index 76abff5..c964e8d 100644
+--- a/Makefile
++++ b/Makefile
+@@ -32,7 +32,7 @@ include $(topdir)/MCONFIG
+ # List of module objects that should be installed for all derivatives
+ MODULES = memdisk/memdisk memdump/memdump.com modules/*.com \
+ com32/menu/*.c32 com32/modules/*.c32 com32/mboot/*.c32 \
+- com32/hdt/*.c32 com32/rosh/*.c32
++ com32/hdt/*.c32 com32/rosh/*.c32 com32/gfxboot/*.c32
+
+ # syslinux.exe is BTARGET so as to not require everyone to have the
+ # mingw suite installed
+diff --git a/com32/Makefile b/com32/Makefile
+index 4a58485..69a125e 100644
+--- a/com32/Makefile
++++ b/com32/Makefile
+@@ -1,3 +1,3 @@
+-SUBDIRS = lib gpllib libutil modules mboot menu samples rosh cmenu hdt
++SUBDIRS = lib gpllib libutil modules mboot menu samples rosh cmenu hdt gfxboot
+ all tidy dist clean spotless install:
+ set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
+diff --git a/com32/gfxboot/Makefile b/com32/gfxboot/Makefile
+new file mode 100644
+index 0000000..2affcde
+--- /dev/null
++++ b/com32/gfxboot/Makefile
+@@ -0,0 +1,44 @@
++## -----------------------------------------------------------------------
++##
++## Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
++## Copyright 2009 Intel Corporation; author: H. Peter Anvin
++##
++## 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.
++##
++## -----------------------------------------------------------------------
++
++topdir = ../..
++include ../MCONFIG
++
++MODULES = gfxboot.c32
++
++all: $(MODULES)
++
++gfxboot.elf : gfxboot.o realmode_callback.o $(LIBS) $(C_LIBS)
++ $(LD) $(LDFLAGS) -o $@ $^
++
++realmode_callback.o: realmode_callback.asm
++ nasm -f bin -O99 -o $*.tmp -l $*.lst $<
++ objcopy -B i386 -I binary -O elf32-i386 \
++ --redefine-sym _binary_$*_tmp_start=$*_start \
++ --redefine-sym _binary_$*_tmp_end=$*_end \
++ --strip-symbol _binary_$*_tmp_size \
++ $*.tmp $@
++
++tidy dist:
++ rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp
++
++clean: tidy
++ rm -f *.lnx
++
++spotless: clean
++ rm -f *.lss *.c32 *.com
++ rm -f *~ \#*
++
++install:
++
++-include .*.d
+diff --git a/com32/gfxboot/gfxboot.c b/com32/gfxboot/gfxboot.c
+new file mode 100644
+index 0000000..e1b865a
+--- /dev/null
++++ b/com32/gfxboot/gfxboot.c
+@@ -0,0 +1,806 @@
++/*
++ *
++ * gfxboot.c
++ *
++ * A com32 module to load gfxboot graphics.
++ *
++ * 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 <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <fcntl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++
++#include <syslinux/loadfile.h>
++#include <syslinux/config.h>
++#include <syslinux/linux.h>
++#include <syslinux/boot.h>
++#include <console.h>
++#include <com32.h>
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++#define MAX_CONFIG_LINE_LEN 2048
++#define MAX_CMDLINE_LEN 2048
++
++// buffer for realmode callback
++// must be at least block size; can in theory be larger than 4k, but there's
++// not enough space left
++#define REALMODE_BUF_SIZE 4096
++
++// gfxboot working memory in MB
++#define GFX_MEMORY_SIZE 7
++
++// read chunk size for progress bar
++#define CHUNK_SIZE (64 << 10)
++
++// callback function numbers
++#define GFX_CB_INIT 0
++#define GFX_CB_DONE 1
++#define GFX_CB_INPUT 2
++#define GFX_CB_MENU_INIT 3
++#define GFX_CB_INFOBOX_INIT 4
++#define GFX_CB_INFOBOX_DONE 5
++#define GFX_CB_PROGRESS_INIT 6
++#define GFX_CB_PROGRESS_DONE 7
++#define GFX_CB_PROGRESS_UPDATE 8
++#define GFX_CB_PROGRESS_LIMIT 9 // unused
++#define GFX_CB_PASSWORD_INIT 10
++#define GFX_CB_PASSWORD_DONE 11
++
++// real mode code chunk, will be placed into bounce buffer
++extern void realmode_callback_start, realmode_callback_end;
++
++// gets in the way
++#undef linux
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// gfxboot config data (64 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 serial_port; // 10: syslinux initialized serial port from 'serial' option
++ 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) - obsolete
++ uint16_t xmem_1; // 26: extended mem area 1 - obsolete
++ uint16_t xmem_2; // 28: extended mem area 2 - obsolete
++ uint16_t xmem_3; // 30: extended mem area 3 - obsolete
++ 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
++ uint32_t xmem_start; // 52: extended mem start
++ uint32_t xmem_end; // 56: extended mem end
++ uint16_t features; // 60: feature flags returned by GFX_CB_INIT
++ // 0: GFX_CB_MENU_INIT accepts 32 bit addresses
++ // 1: knows about xmem_start, xmem_end
++ uint16_t reserved_1; // 62:
++} gfx_config_t;
++
++
++// gfxboot menu description (18 bytes)
++typedef struct __attribute__ ((packed)) {
++ uint16_t entries;
++ char *default_entry;
++ char *label_list;
++ uint16_t label_size;
++ char *arg_list;
++ uint16_t arg_size;
++} gfx_menu_t;
++
++
++// menu description
++typedef struct menu_s {
++ struct menu_s *next;
++ char *label;
++ char *kernel;
++ char *linux;
++ char *localboot;
++ char *initrd;
++ char *append;
++ char *ipappend;
++} menu_t;
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++gfx_config_t gfx_config;
++gfx_menu_t gfx_menu;
++
++menu_t *menu;
++menu_t *menu_default;
++
++struct {
++ uint32_t jmp_table[12];
++ uint16_t code_seg;
++ char fname_buf[64];
++} gfx;
++
++void *lowmem_buf;
++unsigned lowmem_buf_size;
++
++int timeout;
++
++char cmdline[MAX_CMDLINE_LEN];
++
++void *save_buf;
++unsigned save_buf_size;
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++void show_message(char *file);
++char *get_config_file_name(void);
++char *skip_spaces(char *s);
++char *skip_nonspaces(char *s);
++void chop_line(char *s);
++int read_config_file(void);
++unsigned magic_ok(unsigned char *buf, unsigned *code_size);
++unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len, unsigned *code_size);
++int gfx_init(char *file);
++int gfx_menu_init(void);
++void gfx_done(void);
++int gfx_input(void);
++ssize_t save_read(int fd, void *buf, size_t size);
++void *load_one(char *file, ssize_t *file_size);
++void boot(void);
++void boot_entry(menu_t *menu_ptr, char *arg);
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++int main(int argc, char **argv)
++{
++ int menu_index;
++ enum syslinux_filesystem syslinux_id;
++ com32sys_t r;
++
++ openconsole(&dev_stdcon_r, &dev_stdcon_w);
++
++ syslinux_id = syslinux_version()->filesystem;
++
++ lowmem_buf = __com32.cs_bounce;
++ lowmem_buf_size = __com32.cs_bounce_size;
++
++ r.eax.l = 0x0a; // Get Derivative-Specific Information
++ r.ecx.l = 9;
++ __intcall(0x22, &r, &r);
++ gfx_config.sector_shift = (uint8_t) r.ecx.l;
++ gfx_config.boot_drive = (uint8_t) r.edx.l;
++
++ if(syslinux_id == SYSLINUX_FS_PXELINUX) {
++ gfx_config.sector_shift = 11;
++ gfx_config.boot_drive = 0;
++ }
++
++ gfx_config.bootloader = 1;
++ gfx_config.sysconfig_size = sizeof gfx_config;
++ gfx_config.bootloader_seg = 0; // apparently not needed
++
++ save_buf_size = lowmem_buf_size;
++ save_buf = malloc(save_buf_size);
++
++ if(argc < 2) {
++ printf("Usage: gfxboot.c32 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;
++ }
++
++ // does not return if it succeeds
++ boot();
++ }
++
++ if(argc > 2) show_message(argv[2]);
++
++ return 0;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++void show_message(char *file)
++{
++ int c;
++ FILE *f;
++
++ if(!(f = fopen(file, "r"))) return;
++
++ while((c = getc(f)) != EOF) {
++ if(c < ' ' && c != '\n' && c != '\t') continue;
++ printf("%c", c);
++ }
++
++ fclose(f);
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++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;
++ }
++ }
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Read and parse syslinux config file.
++//
++// return:
++// 0: ok, 1: error
++//
++int read_config_file(void)
++{
++ FILE *f;
++ char *s, *t, buf[MAX_CONFIG_LINE_LEN];
++ unsigned u, menu_idx = 0, label_size = 0, append_size = 0;
++ menu_t *menu_ptr = NULL, **menu_next = &menu;
++
++ menu_default = calloc(1, sizeof *menu_default);
++
++ if(!(f = fopen(syslinux_config_file(), "r"))) return 1;
++
++ while((s = fgets(buf, sizeof buf, f))) {
++ 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);
++ continue;
++ }
++
++ if(!strcmp(s, "default")) {
++ menu_default->label = strdup(t);
++ u = strlen(t);
++ if(u > label_size) label_size = u;
++ continue;
++ }
++
++ if(!strcmp(s, "label")) {
++ menu_ptr = *menu_next = calloc(1, sizeof **menu_next);
++ menu_next = &menu_ptr->next;
++ menu_idx++;
++ menu_ptr->label = strdup(t);
++ u = strlen(t);
++ if(u > label_size) label_size = u;
++ continue;
++ }
++
++ if(!strcmp(s, "kernel") && menu_ptr) {
++ menu_ptr->kernel = strdup(t);
++ continue;
++ }
++
++ if(!strcmp(s, "linux") && menu_ptr) {
++ menu_ptr->linux = strdup(t);
++ continue;
++ }
++
++ if(!strcmp(s, "localboot") && menu_ptr) {
++ menu_ptr->localboot = strdup(t);
++ continue;
++ }
++
++ if(!strcmp(s, "initrd") && menu_ptr) {
++ menu_ptr->initrd = strdup(t);
++ continue;
++ }
++
++ if(!strcmp(s, "append")) {
++ (menu_ptr ?: menu_default)->append = strdup(t);
++ u = strlen(t);
++ if(u > append_size) append_size = u;
++ continue;
++ }
++
++ if(!strcmp(s, "ipappend")) {
++ (menu_ptr ?: menu_default)->ipappend = strdup(t);
++ continue;
++ }
++ }
++
++ fclose(f);
++
++ // final '\0'
++ label_size++;
++ append_size++;
++
++ gfx_menu.entries = menu_idx;
++ gfx_menu.label_size = label_size;
++ gfx_menu.arg_size = append_size;
++
++ gfx_menu.default_entry = menu_default->label;
++ if(!gfx_menu.default_entry && menu) {
++ gfx_menu.default_entry = menu->label;
++ }
++
++ gfx_menu.label_list = calloc(menu_idx, label_size);
++ gfx_menu.arg_list = calloc(menu_idx, append_size);
++
++ for(u = 0, menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next, u++) {
++ if(!menu_ptr->append) menu_ptr->append = menu_default->append;
++ if(!menu_ptr->ipappend) menu_ptr->ipappend = menu_default->ipappend;
++
++ if(menu_ptr->label) strcpy(gfx_menu.label_list + u * label_size, menu_ptr->label);
++ if(menu_ptr->append) strcpy(gfx_menu.arg_list + u * append_size, menu_ptr->append);
++ }
++
++ return 0;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Check header and return code start offset.
++//
++unsigned magic_ok(unsigned char *buf, unsigned *code_size)
++{
++ if(
++ *(unsigned *) buf == 0x0b2d97f00 && // magic id
++ (buf[4] == 8) // version 8
++ ) {
++ *code_size = *(unsigned *) (buf + 12);
++ return *(unsigned *) (buf + 8);
++ }
++
++ return 0;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Search cpio archive for gfx file.
++//
++unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len, unsigned *code_size)
++{
++ unsigned i, fname_len, code_start = 0;
++
++ *gfx_file_start = 0;
++ *code_size = 0;
++
++ for(i = 0; i < len;) {
++ if((len - i) >= 0x1a && (buf[i] + (buf[i + 1] << 8)) == 0x71c7) {
++ fname_len = *(unsigned short *) (buf + i + 20);
++ *file_len = *(unsigned short *) (buf + i + 24) + (*(unsigned short *) (buf + i + 22) << 16);
++ i += 26 + fname_len;
++ i = ((i + 1) & ~1);
++ if((code_start = magic_ok(buf + i, code_size))) {
++ *gfx_file_start = i;
++ return code_start;
++ }
++ i += *file_len;
++ i = ((i + 1) & ~1);
++ }
++ else {
++ break;
++ }
++ }
++
++ return code_start;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Initialize gfxboot code.
++//
++// return:
++// 0: ok, 1: error
++//
++int gfx_init(char *file)
++{
++ size_t archive_size = 0;
++ void *archive;
++ unsigned code_start, code_size, file_start, file_len, u;
++ com32sys_t r;
++ void *lowmem = lowmem_buf;
++ unsigned lowmem_size = lowmem_buf_size;
++
++ printf("Loading %s...\n", file);
++ if(loadfile(file, &archive, &archive_size)) return 1;
++
++ if(!archive_size) return 1;
++
++ // printf("%s: %d\n", file, archive_size);
++
++ gfx_config.archive_start = (uint32_t) archive;
++ gfx_config.archive_end = gfx_config.archive_start + archive_size;
++
++ // locate file inside cpio archive
++ if(!(code_start = find_file(archive, archive_size, &file_start, &file_len, &code_size))) {
++ printf("%s: invalid file format\n", file);
++ return 1;
++ }
++
++#if 0
++ printf(
++ "code_start = 0x%x, code_size = 0x%x\n"
++ "archive_start = 0x%x, archive size = 0x%x\n"
++ "file_start = 0x%x, file_len = 0x%x\n",
++ code_start, code_size,
++ gfx_config.archive_start, archive_size,
++ file_start, file_len
++ );
++#endif
++
++ gfx_config.file = gfx_config.archive_start + file_start;
++
++ u = &realmode_callback_end - &realmode_callback_start;
++ u = (u + REALMODE_BUF_SIZE + 0xf) & ~0xf;
++
++ if(u + code_size > lowmem_size) {
++ printf("bounce buffer too small: size %u, needed %u\n", lowmem_size, u + code_size);
++ return 1;
++ }
++
++ memcpy(lowmem + REALMODE_BUF_SIZE, &realmode_callback_start, &realmode_callback_end - &realmode_callback_start);
++
++ // fill in buffer size and location
++ *(uint16_t *) (lowmem + REALMODE_BUF_SIZE) = REALMODE_BUF_SIZE;
++ *(uint16_t *) (lowmem + REALMODE_BUF_SIZE + 2) = (uint32_t) lowmem >> 4;
++
++ gfx_config.bootloader_seg = ((uint32_t) lowmem + REALMODE_BUF_SIZE) >> 4;
++ gfx_config.callback = 4; // start address
++
++ lowmem += u;
++ lowmem_size -= u;
++
++ memcpy(lowmem, archive + file_start + code_start, code_size);
++
++ gfx_config.mem0_start = (uint32_t) lowmem + code_size;
++ gfx_config.mem0_end = (uint32_t) lowmem + lowmem_size;
++ // align a bit
++ gfx_config.mem0_start = (gfx_config.mem0_start + 0xf) & ~0xf;
++
++ gfx_config.xmem_start = (uint32_t) malloc(GFX_MEMORY_SIZE << 20);
++ if(gfx_config.xmem_start) {
++ gfx_config.xmem_end = gfx_config.xmem_start + (GFX_MEMORY_SIZE << 20);
++ }
++
++ // fake; not used anyway
++ gfx_config.bios_mem_size = 256 << 20;
++
++ gfx.code_seg = (uint32_t) lowmem >> 4;
++
++ for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) {
++ gfx.jmp_table[u] = (gfx.code_seg << 16) + *(uint16_t *) (lowmem + 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.l = (uint32_t) &gfx_config;
++ __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INIT], &r, &r);
++
++ if((r.eflags.l & EFLAGS_CF)) {
++ printf("graphics initialization failed\n");
++
++ return 1;
++ }
++
++ if((gfx_config.features & 3) != 3) {
++ gfx_done();
++
++ printf("%s: boot graphics code too old, please use newer version\n", file);
++
++ return 1;
++ }
++
++
++ return 0;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++int gfx_menu_init()
++{
++ com32sys_t r;
++
++ r.esi.l = (uint32_t) &gfx_menu;
++ __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_MENU_INIT], &r, &r);
++
++ return 0;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++void gfx_done()
++{
++ com32sys_t r;
++
++ __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_DONE], &r, &r);
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Run gfxboot main loop.
++//
++// return:
++// boot menu index (-1: go to text mode prompt)
++//
++int gfx_input()
++{
++ com32sys_t r;
++
++ r.edi.l = (uint32_t) cmdline;
++ r.ecx.l = sizeof cmdline;
++ r.eax.l = timeout * 182 / 100;
++ timeout = 0; // use timeout only first time
++ __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INPUT], &r, &r);
++ if((r.eflags.l & EFLAGS_CF)) r.eax.l = 1;
++
++ if(r.eax.l == 1) return -1;
++
++ return r.ebx.l;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Like read(2) but preserve bounce buffer.
++//
++ssize_t save_read(int fd, void *buf, size_t size)
++{
++ ssize_t i;
++
++ memcpy(save_buf, lowmem_buf, save_buf_size);
++ i = read(fd, buf, size);
++ memcpy(lowmem_buf, save_buf, save_buf_size);
++
++ return i;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Read file and update progress bar.
++//
++void *load_one(char *file, ssize_t *file_size)
++{
++ int fd;
++ void *buf = NULL;
++ struct stat sbuf;
++ ssize_t size = 0, cur, i;
++ com32sys_t r;
++
++ *file_size = 0;
++
++ if((fd = open(file, O_RDONLY)) == -1) {
++ printf("%s: file not found\n", file);
++ return buf;
++ }
++
++ if(!fstat(fd, &sbuf) && S_ISREG(sbuf.st_mode)) size = sbuf.st_size;
++
++ i = 0;
++
++ if(size) {
++ buf = malloc(size);
++ for(i = 1, cur = 0 ; cur < size && i > 0; cur += i) {
++ i = save_read(fd, buf + cur, CHUNK_SIZE);
++ r.eax.l = i >> gfx_config.sector_shift;
++ __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_UPDATE], &r, &r);
++ }
++ }
++ else {
++ do {
++ buf = realloc(buf, size + CHUNK_SIZE);
++ i = save_read(fd, buf + size, CHUNK_SIZE);
++ size += i;
++ r.eax.l = i >> gfx_config.sector_shift;
++ __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_UPDATE], &r, &r);
++ } while(i > 0);
++ }
++
++ close(fd);
++
++ if(i == -1) {
++ free(buf);
++ buf = NULL;
++ size = 0;
++ }
++
++ *file_size = size;
++
++ return buf;
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Locate menu entry and boot.
++//
++void boot()
++{
++ char *label, *arg, *s;
++ menu_t *menu_ptr;
++
++ label = skip_spaces(cmdline);
++ arg = skip_spaces(s = skip_nonspaces(label));
++ *s = 0;
++
++ for(menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next) {
++ if(menu_ptr->label && !strcmp(menu_ptr->label, label)) break;
++ }
++
++ boot_entry(menu_ptr, arg);
++}
++
++
++// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++// Load & run kernel.
++//
++// Returns only on error.
++//
++void boot_entry(menu_t *menu_ptr, char *arg)
++{
++ void *kernel, *initrd_buf;
++ ssize_t kernel_size = 0, initrd_size = 0;
++ struct initramfs *initrd = NULL;
++ char *file, *cmd_buf;
++ int fd;
++ struct stat sbuf;
++ com32sys_t r;
++ char *s, *s0, *t, *initrd_arg;
++
++ if(!menu_ptr) return;
++
++ if(menu_ptr->localboot) {
++ gfx_done();
++ syslinux_local_boot(atoi(arg));
++
++ return;
++ }
++
++ file = menu_ptr->kernel;
++ if(!file) file = menu_ptr->linux;
++ if(!file) return;
++
++ // first, load kernel
++
++ r.eax.l = 0; // kernel size in sectors
++
++ if((fd = open(file, O_RDONLY)) >= 0) {
++ if(!fstat(fd, &sbuf) && S_ISREG(sbuf.st_mode)) r.eax.l = sbuf.st_size >> gfx_config.sector_shift;
++ close(fd);
++ }
++
++ r.esi.l = (uint32_t) file;
++ __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_INIT], &r, &r);
++
++ kernel = load_one(file, &kernel_size);
++
++ if(!kernel) {
++ gfx_done();
++ printf("%s: read error\n", file);
++ return;
++ }
++
++ if(kernel_size < 1024 || *(uint32_t *) (kernel + 0x202) != 0x53726448) {
++ // not a linux kernel
++ gfx_done();
++ asprintf(&cmd_buf, "%s %s", menu_ptr->label, arg);
++ syslinux_run_command(cmd_buf);
++ return;
++ }
++
++ // printf("kernel = %p, size = %d\n", kernel, kernel_size);
++
++ // parse cmdline for "initrd" option
++
++ initrd_arg = menu_ptr->initrd;
++
++ s = s0 = strdup(arg);
++
++ while(*s && strncmp(s, "initrd=", sizeof "initrd=" - 1)) {
++ s = skip_spaces(skip_nonspaces(s));
++ }
++
++ if(*s) {
++ s += sizeof "initrd=" - 1;
++ *skip_nonspaces(s) = 0;
++ initrd_arg = s;
++ }
++
++ if(initrd_arg) {
++ initrd = initramfs_init();
++
++ while((t = strsep(&s, ","))) {
++ initrd_buf = load_one(t, &initrd_size);
++
++ if(!initrd_buf) {
++ printf("%s: read error\n", t);
++ free(s0);
++ return;
++ }
++
++ initramfs_add_data(initrd, initrd_buf, initrd_size, initrd_size, 4);
++
++ // printf("initrd = %p, size = %d\n", initrd_buf, initrd_size);
++ }
++ }
++
++ free(s0);
++
++ __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_DONE], &r, &r);
++
++ syslinux_boot_linux(kernel, kernel_size, initrd, arg);
++}
++
++
+diff --git a/com32/gfxboot/realmode_callback.asm b/com32/gfxboot/realmode_callback.asm
+new file mode 100644
+index 0000000..fb5461d
+--- /dev/null
++++ b/com32/gfxboot/realmode_callback.asm
+@@ -0,0 +1,190 @@
++ bits 16
++
++ section .text
++
++ ; must be filled in
++f_buf_size dw 0
++f_buf_seg dw 0
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++gfx_cb:
++ push cs
++ pop ds
++
++ cmp al,cb_len
++ jae gfx_cb_80
++
++ movzx bx,al
++ add bx,bx
++ call word [bx+cb_table]
++ jmp gfx_cb_90
++
++gfx_cb_80:
++ mov al,0ffh
++gfx_cb_90:
++ retf
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; Return status info.
++;
++; return:
++; edx filename buffer (64 bytes)
++;
++cb_status:
++ mov edx,cs
++ shl edx,4
++ add edx,f_name
++ xor al,al
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; Open file.
++;
++; return:
++; al 0: ok, 1: file not found
++; ecx file length (al = 0)
++;
++cb_fopen:
++ mov si,f_name
++ push ds
++ pop es
++ mov ax,6
++ int 22h
++ xchg edx,eax
++ mov al,1
++ jc cb_fopen_90
++ cmp cx,[f_buf_size]
++ ja cb_fopen_90
++ or cx,cx
++ jz cb_fopen_90
++ mov [f_block_size],cx
++ or edx,edx
++ jz cb_fopen_90
++ mov [f_handle],si
++ mov [f_size],edx
++ mov ecx,edx
++ mov ax,[f_buf_size]
++ cwd
++ div word [f_block_size]
++ mov [f_blocks],ax
++
++ xor al,al
++cb_fopen_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; Read next chunk.
++;
++; return:
++; edx buffer address (linear)
++; ecx data length (< 64k)
++;
++cb_fread:
++ xor ecx,ecx
++ mov si,[f_handle]
++ or si,si
++ jz cb_fread_80
++ mov cx,[f_blocks]
++ mov es,[f_buf_seg]
++ xor bx,bx
++ mov ax,7
++ int 22h
++ mov [f_handle],si
++ mov al,1
++ jc cb_fread_90
++cb_fread_80:
++ xor al,al
++cb_fread_90:
++ movzx edx,word [f_buf_seg]
++ shl edx,4
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; Return current working directory.
++;
++; return:
++; edx filename
++;
++cb_getcwd:
++ mov ax,15h
++ int 22h
++ mov edx,es
++ shl edx,4
++ movzx ebx,bx
++ add edx,ebx
++ xor al,al
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; Set current working directory.
++;
++cb_chdir:
++ mov bx,f_name
++ push ds
++ pop es
++ mov ax,25h
++ int 22h
++ xor al,al
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; read sector
++;
++; edx sector
++;
++; return:
++; edx buffer (linear address)
++;
++; Note: does not return on error!
++;
++cb_readsector:
++ xor edi,edi
++ xor esi,esi
++ mov cx,1
++ mov es,[f_buf_seg]
++ xor bx,bx
++ mov ax,19h
++ int 22h
++ movzx edx,word [f_buf_seg]
++ shl edx,4
++ xor al,al
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; Re-read fs structures.
++;
++cb_mount:
++ mov ax,26h
++ int 22h
++ setc al
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++ align 2, db 0
++
++cb_table dw cb_status
++ dw cb_fopen
++ dw cb_fread
++ dw cb_getcwd
++ dw cb_chdir
++ dw cb_readsector
++ dw cb_mount
++cb_len equ ($-cb_table)/2
++
++f_handle dw 0
++f_block_size dw 0
++f_blocks dw 0
++f_size dd 0
++f_name times 64 db 0
++f_name_len equ $ - f_name
++
diff --git a/patches/grub-0.97.diff b/patches/grub-0.97.diff
new file mode 100644
index 0000000..6874b06
--- /dev/null
+++ b/patches/grub-0.97.diff
@@ -0,0 +1,1006 @@
+--- docs/grub.texi
++++ docs/grub.texi
+@@ -2118,6 +2118,7 @@
+ * default:: Set the default entry
+ * fallback:: Set the fallback entry
+ * hiddenmenu:: Hide the menu interface
++* gfxmenu:: Use graphical menu interface
+ * timeout:: Set the timeout
+ * title:: Start a menu entry
+ @end menu
+@@ -2150,6 +2151,15 @@
+ @end deffn
+
+
++@node gfxmenu
++@subsection gfxmenu
++
++@deffn Command gfxmenu file
++Use the graphical menu interface. The graphics data are taken from
++@var{file} and must be created using 'mkbootmsg' from the gfxboot package.
++@end deffn
++
++
+ @node hiddenmenu
+ @subsection hiddenmenu
+
+--- grub/asmstub.c
++++ grub/asmstub.c
+@@ -498,6 +498,32 @@
+ return 0;
+ }
+
++/* graphical menu functions . */
++int
++gfx_init (gfx_data_t *gfx_data)
++{
++ return 0;
++}
++
++int
++gfx_done (gfx_data_t *gfx_data)
++{
++ return 0;
++}
++
++int
++gfx_input (gfx_data_t *gfx_data, int *menu_entry)
++{
++ return 0;
++}
++
++int
++gfx_setup_menu (gfx_data_t *gfx_data)
++{
++ return 0;
++}
++
++
+ /* low-level timing info */
+ int
+ getrtsecs (void)
+--- stage2/asm.S
++++ stage2/asm.S
+@@ -1614,6 +1614,286 @@
+ popl %ebp
+ ret
+
++
++/*
++ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++ *
++ * graphical menu functions
++ *
++ */
++
++/*
++ * int gfx_init (gfx_data_t *gfx_data)
++ *
++ * init gfx things
++ *
++ * return vales:
++ * 0: ok
++ * 1: failed
++ * sets gfx_data->ok
++ */
++
++ENTRY(gfx_init)
++ pushl %ebp
++ movl %esp, %ebp
++
++ pushl %edi
++ pushl %esi
++ pushl %ebx
++
++ movl 8(%ebp),%edx
++ movl %edx,%edi
++ leal gfx_ofs_sys_cfg(%edx),%esi
++ andl $0xf,%edi
++ shrl $4,%edx
++
++ pushl %ebp
++
++ call EXT_C(prot_to_real)
++ .code16
++
++ pushw %ds
++ movw %dx,%ds
++
++ lcall *gfx_ofs_jmp_table + 4 * 0 (%di)
++
++ sbbl %ebx,%ebx
++ negl %ebx
++
++ popw %ds
++
++ DATA32 call EXT_C(real_to_prot)
++ .code32
++
++ popl %ebp
++
++ movl %ebx,%eax
++ xorl $1,%ebx
++ movl 8(%ebp),%edx
++ movl %ebx,gfx_ofs_ok(%edx)
++
++ popl %ebx
++ popl %esi
++ popl %edi
++
++ popl %ebp
++ ret
++
++
++/*
++ * int gfx_done (gfx_data_t *gfx_data)
++ *
++ * shut down gfx things
++ *
++ * return vales:
++ * always 0
++ * sets gfx_data->ok
++ */
++
++ENTRY(gfx_done)
++ pushl %ebp
++ movl %esp, %ebp
++
++ pushl %edi
++ pushl %esi
++ pushl %ebx
++
++ movl 8(%ebp),%edx
++ movl %edx,%ebx
++ andl $0xf,%ebx
++ shrl $4,%edx
++
++ pushl %ebp
++
++ call EXT_C(prot_to_real)
++ .code16
++
++ pushw %ds
++
++ movw %dx,%ds
++
++ lcall *gfx_ofs_jmp_table + 4 * 1 (%bx)
++
++ popw %ds
++
++ DATA32 call EXT_C(real_to_prot)
++ .code32
++
++ popl %ebp
++
++ xorl %eax,%eax
++ movl 8(%ebp),%edx
++ movl %eax,gfx_ofs_ok(%edx)
++
++ popl %ebx
++ popl %esi
++ popl %edi
++
++ popl %ebp
++ ret
++
++
++/*
++ * int gfx_input (gfx_data_t *gfx_data, int *menu_entry)
++ *
++ * let user enter a command line
++ *
++ * uses gfx_data->cmdline as buffer
++ *
++ * return values:
++ * 1: abort
++ * 2: boot
++ * menu_entry: selected entry
++ */
++
++ENTRY(gfx_input)
++ pushl %ebp
++ movl %esp, %ebp
++
++ pushl %edi
++ pushl %esi
++ pushl %ebx
++
++ movl 8(%ebp),%edx
++ movl %edx,%ebx
++ leal gfx_ofs_sys_cfg(%edx),%esi
++ andl $0xf,%ebx
++ shrl $4,%edx
++
++ pushl %ebp
++
++ call EXT_C(prot_to_real)
++ .code16
++
++ pushw %ds
++
++ movw %dx,%ds
++
++ movl gfx_ofs_cmdline(%bx),%edi
++ movl gfx_ofs_cmdline_len(%bx),%ecx
++ movl gfx_ofs_timeout(%bx),%eax
++ imull $18,%eax
++
++ lcall *gfx_ofs_jmp_table + 4 * 2 (%bx)
++
++ movl %eax,%ecx
++
++ popw %ds
++
++ DATA32 call EXT_C(real_to_prot)
++ .code32
++
++ popl %ebp
++
++ movl 12(%ebp),%edx
++ movl %ebx,(%edx)
++
++ movl %ecx,%eax
++
++ popl %ebx
++ popl %esi
++ popl %edi
++
++ popl %ebp
++ ret
++
++
++/*
++ * int gfx_setup_menu (gfx_data_t *gfx_data)
++ *
++ * draw boot menu
++ *
++ * return values:
++ * always 0
++ */
++
++/* menu entry descriptor */
++#define menu_entries 0
++#define menu_default 2 /* seg:ofs */
++#define menu_ent_list 6 /* seg:ofs */
++#define menu_ent_size 10
++#define menu_arg_list 12 /* seg:ofs */
++#define menu_arg_size 16
++#define sizeof_menu_desc 18
++
++ENTRY(gfx_setup_menu)
++ pushl %ebp
++ movl %esp, %ebp
++
++ pushl %edi
++ pushl %esi
++ pushl %ebx
++
++ movl 8(%ebp),%edx
++ movl %edx,%ebx
++ andl $0xf,%ebx
++ shrl $4,%edx
++
++ call EXT_C(prot_to_real)
++ .code16
++
++ pushw %ds
++
++ movw %dx,%ds
++ shll $4,%edx
++
++ subw $sizeof_menu_desc,%sp
++ movw %esp,%ebp
++
++ movl gfx_ofs_menu_entries(%bx),%eax
++ movw %ax,menu_entries(%bp)
++
++ movl gfx_ofs_menu_default_entry(%bx),%eax
++ subl %edx,%eax
++ movw %ax,menu_default(%bp)
++ movw %ds,menu_default+2(%bp)
++
++ movl gfx_ofs_menu_list(%bx),%eax
++ subl %edx,%eax
++ movw %ax,menu_ent_list(%bp)
++ movw %ds,menu_ent_list+2(%bp)
++
++ movl gfx_ofs_menu_entry_len(%bx),%eax
++ movw %ax,menu_ent_size(%bp)
++
++ movl gfx_ofs_args_list(%bx),%eax
++ subl %edx,%eax
++ movw %ax,menu_arg_list(%bp)
++ movw %ds,menu_arg_list+2(%bp)
++
++ movl gfx_ofs_args_entry_len(%bx),%eax
++ movw %ax,menu_arg_size(%bp)
++
++ movl %ss,%esi
++ shll $4,%esi
++ addl %ebp,%esi
++
++ lcall %ds: *gfx_ofs_jmp_table + 4 * 3 (%bx)
++
++ addw $sizeof_menu_desc,%sp
++
++ popw %ds
++
++ DATA32 call EXT_C(real_to_prot)
++ .code32
++
++ xorl %eax,%eax
++
++ popl %ebx
++ popl %esi
++ popl %edi
++
++ popl %ebp
++ ret
++
++
++/*
++ *
++ * end graphics stuff
++ *
++ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++ */
++
+
+ /*
+ * gateA20(int linear)
+--- stage2/builtins.c
++++ stage2/builtins.c
+@@ -63,6 +63,8 @@
+ int fallback_entries[MAX_FALLBACK_ENTRIES];
+ /* The number of current entry. */
+ int current_entryno;
++/* graphics file */
++char graphics_file[64];
+ /* The address for Multiboot command-line buffer. */
+ static char *mb_cmdline;
+ /* The password. */
+@@ -1351,6 +1353,26 @@
+ };
+
+
++/* graphics */
++static int
++gfxmenu_func (char *arg, int flags)
++{
++ memmove(graphics_file, arg, sizeof graphics_file - 1);
++ graphics_file[sizeof graphics_file - 1] = 0;
++
++ return 0;
++}
++
++static struct builtin builtin_gfxmenu =
++{
++ "gfxmenu",
++ gfxmenu_func,
++ BUILTIN_MENU | BUILTIN_HELP_LIST,
++ "gfxmenu FILE",
++ "Use the graphical menu from FILE."
++};
++
++
+ /* geometry */
+ static int
+ geometry_func (char *arg, int flags)
+@@ -4874,6 +4896,7 @@
+ &builtin_find,
+ &builtin_fstest,
+ &builtin_geometry,
++ &builtin_gfxmenu,
+ &builtin_halt,
+ &builtin_help,
+ &builtin_hiddenmenu,
+--- stage2/shared.h
++++ stage2/shared.h
+@@ -374,6 +374,22 @@
+ #endif /* WITHOUT_LIBC_STUBS */
+
+
++/* see typedef gfx_data_t below */
++#define gfx_ofs_ok 0x00
++#define gfx_ofs_code_seg 0x04
++#define gfx_ofs_jmp_table 0x08
++#define gfx_ofs_sys_cfg 0x38
++#define gfx_ofs_cmdline 0x6c
++#define gfx_ofs_cmdline_len 0x70
++#define gfx_ofs_menu_list 0x74
++#define gfx_ofs_menu_default_entry 0x78
++#define gfx_ofs_menu_entries 0x7c
++#define gfx_ofs_menu_entry_len 0x80
++#define gfx_ofs_args_list 0x84
++#define gfx_ofs_args_entry_len 0x88
++#define gfx_ofs_timeout 0x8c
++
++
+ #ifndef ASM_FILE
+ /*
+ * Below this should be ONLY defines and other constructs for C code.
+@@ -595,6 +611,38 @@
+ extern int default_entry;
+ extern int current_entryno;
+
++
++/*
++ * graphics menu stuff
++ *
++ * Note: gfx_data and all data referred to in it must lie within a 64k area.
++ */
++typedef struct {
++ unsigned ok; /* set while we're in graphics mode */
++ unsigned code_seg; /* code segment of binary graphics code */
++ unsigned jmp_table[12]; /* link to graphics functions */
++ unsigned char sys_cfg[52]; /* sys_cfg[0]: identifies boot loader (grub == 2) */
++ char *cmdline; /* command line returned by gfx_input() */
++ unsigned cmdline_len; /* length of the above */
++ char *menu_list; /* list of menu entries, each of fixed length (menu_entry_len) */
++ char *menu_default_entry; /* the default entry */
++ unsigned menu_entries; /* number of entries in menu_list */
++ unsigned menu_entry_len; /* one entry */
++ char *args_list; /* same structure as menu_list, menu_entries entries */
++ unsigned args_entry_len; /* one entry */
++ unsigned timeout; /* in seconds (0: no timeout) */
++} __attribute__ ((packed)) gfx_data_t;
++
++extern gfx_data_t *graphics_data;
++
++/* pointer to graphics image data */
++extern char graphics_file[64];
++
++int gfx_init(gfx_data_t *gfx_data);
++int gfx_done(gfx_data_t *gfx_data);
++int gfx_input(gfx_data_t *gfx_data, int *menu_entry);
++int gfx_setup_menu(gfx_data_t *gfx_data);
++
+ /* The constants for password types. */
+ typedef enum
+ {
+--- stage2/stage2.c
++++ stage2/stage2.c
+@@ -22,6 +22,8 @@
+
+ grub_jmp_buf restart_env;
+
++gfx_data_t *graphics_data;
++
+ #if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS)
+
+ # if defined(PRESET_MENU_STRING)
+@@ -310,6 +312,12 @@
+
+ if (! auth && password)
+ {
++ if (*graphics_file)
++ {
++ printf ("\
++ WARNING: graphical menu doesn\'t work\
++ in conjunction with the password feature\n" );
++ }
+ printf ("\
+ Press enter to boot the selected OS or \'p\' to enter a\n\
+ password to unlock the next set of features.");
+@@ -753,6 +761,493 @@
+ }
+
+
++
++#if 0
++/* for debugging */
++static void hexdump(unsigned char *buf, unsigned len)
++{
++ int i, j = 0;
++ char s[17];
++ unsigned addr = (unsigned) buf;
++
++ s[16] = 0;
++ while(len--) {
++ i = buf[j];
++ i = i & 0xff;
++ s[j & 15] = (i >= 0x20 && i <= 0x7e) ? i : '.';
++ if(!(j & 15)) {
++ printf("%x ", j + addr);
++ }
++ if(!(j & 7) && (j & 15)) printf(" ");
++ /* stupid grub_printf */
++ printf("%x", (i >> 4) & 0x0f);
++ printf("%x ", i & 0x0f);
++ if(!(++j & 15)) {
++ printf(" %s\n", s);
++ }
++ }
++
++ if(j & 15) {
++ s[j & 15] = 0;
++ if(!(j & 8)) printf(" ");
++ i = 1 + 3 * (16 - (j & 15));
++ while(i--) printf(" ");
++ printf("%s\n", s);
++ }
++}
++#endif
++
++
++/* kernel + (grub-)module options */
++#define GFX_CMD_BUF_SIZE 512
++
++/* command line separator char */
++#define GFX_CMD_SEP 1
++
++/*
++ * Go through config entry and find kernel args, if any.
++ * Put things into buf and return it.
++ */
++static char *get_kernel_args(char *cfg, char *buf)
++{
++ int i, j;
++ char *s, *t = "", *p, *t2;
++
++ *(p = buf) = 0;
++
++ for(j = 0; ; j++) {
++ s = get_entry(cfg, j, 0);
++ if(!*s) break;
++ if(
++ (!memcmp(s, "kernel", 6) || !memcmp(s, "module", 6)) &&
++ (s[6] == ' ' || s[6] == '\t')
++ ) {
++ t = skip_to(0, s);
++ t2 = s[0] == 'm' ? strstr(t, "initrd") : NULL;
++ if(*t) t = skip_to(0, t);
++ if(t2 && t2 < t) break; /* module is likely a normal initrd -> skip */
++ i = strlen(t);
++ if(p - buf + i > GFX_CMD_BUF_SIZE - 2) break;
++ *p++ = GFX_CMD_SEP;
++ strcpy(p, t);
++ p += i;
++
++ continue;
++ }
++ }
++
++ if(*buf) buf++; /* skip initial separator char */
++
++ return buf;
++}
++
++
++/*
++ * Check header and return code start offset.
++ */
++static unsigned magic_ok(unsigned char *buf)
++{
++ if(
++ *(unsigned *) buf == 0x0b2d97f00 && /* magic id */
++ (buf[4] == 8) /* version 8 */
++ ) {
++ return *(unsigned *) (buf + 8);
++ }
++
++ return 0;
++}
++
++
++/*
++ * Search cpio archive for gfx file.
++ */
++static unsigned find_file(unsigned char *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 && (buf[i] + (buf[i + 1] << 8)) == 0x71c7) {
++ fname_len = *(unsigned short *) (buf + i + 20);
++ *file_len = *(unsigned short *) (buf + i + 24) + (*(unsigned short *) (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;
++}
++
++static inline unsigned char * stack_ptr(void)
++{
++ unsigned char * u;
++
++ asm("movl %%esp, %0" : "=r" (u));
++
++ return u;
++}
++
++static void sleep(int delay)
++{
++ int tick, last_tick = currticks();
++
++ delay *= 18;
++
++ while(delay--) {
++ while((tick = currticks()) == last_tick) { }
++ last_tick = tick;
++ }
++}
++
++static void wait_for_key()
++{
++ printf("Press a key to continue...");
++ getkey();
++ printf("\r \r");
++}
++
++
++/*
++ * Leave that much space on the heap. Everything else goes to the graphics
++ * functions.
++ *
++ * 0x2000 is _not_ enough
++ */
++#define MIN_HEAP_SIZE 0x4000
++#define MIN_GFX_FREE 0x1000
++
++#define SC_BOOTLOADER 0
++#define SC_FAILSAFE 3
++#define SC_SYSCONFIG_SIZE 4
++#define SC_BOOTLOADER_SEG 8
++#define SC_XMEM_0 24
++#define SC_XMEM_1 26
++#define SC_XMEM_2 28
++#define SC_XMEM_3 30
++#define SC_FILE 32
++#define SC_ARCHIVE_START 36
++#define SC_ARCHIVE_END 40
++#define SC_MEM0_START 44
++#define SC_MEM0_END 48
++
++/*
++ * Does normally not return.
++ */
++static void
++run_graphics_menu (char *menu_entries, char *config_entries, int num_entries,
++ char *heap, int entryno)
++{
++ unsigned char *buf, *buf_ext;
++ unsigned buf_size, buf_ext_size, code_start, file_start;
++ char *s, *t, *t2, *cfg, *new_config, *p;
++ char *saved_heap;
++ int i, j, max_len, gfx_file_size, verbose;
++ int selected_entry;
++ gfx_data_t *gfx_data;
++ char *cmd_buf;
++ unsigned mem0_start, mem0_end, file_len;
++
++ /*
++ * check gfx_data_t struct offsets for consistency; gcc will optimize away
++ * the whole block
++ */
++
++ /* dummy function to make ld fail */
++ {
++ extern void wrong_struct_size(void);
++ #define gfx_ofs_check(a) if(gfx_ofs_##a != (char *) &gfx_data->a - (char *) gfx_data) wrong_struct_size();
++ gfx_ofs_check(ok);
++ gfx_ofs_check(code_seg);
++ gfx_ofs_check(jmp_table);
++ gfx_ofs_check(sys_cfg);
++ gfx_ofs_check(cmdline);
++ gfx_ofs_check(cmdline_len);
++ gfx_ofs_check(menu_list);
++ gfx_ofs_check(menu_default_entry);
++ gfx_ofs_check(menu_entries);
++ gfx_ofs_check(menu_entry_len);
++ gfx_ofs_check(args_list);
++ gfx_ofs_check(args_entry_len);
++ gfx_ofs_check(timeout);
++ #undef gfx_ofs_check
++ }
++
++ if(!num_entries) return;
++
++ graphics_data = gfx_data = (gfx_data_t *) heap;
++ heap += sizeof *gfx_data;
++ memset(gfx_data, 0, sizeof *gfx_data);
++
++ gfx_data->sys_cfg[SC_BOOTLOADER] = 2; /* bootloader: grub */
++ gfx_data->sys_cfg[SC_SYSCONFIG_SIZE] = 52; /* config data size */
++ *(unsigned short *) (gfx_data->sys_cfg + SC_BOOTLOADER_SEG) = (unsigned) gfx_data >> 4; /* segment */
++ gfx_data->sys_cfg[SC_XMEM_0] = 0x21; /* 1MB @ 2MB */
++ gfx_data->sys_cfg[SC_XMEM_1] = 0x41; /* 1MB @ 4MB */
++ verbose = (*(unsigned char *) 0x417) & 3 ? 1 : 0; /* SHIFT pressed */
++ gfx_data->sys_cfg[SC_FAILSAFE] = verbose;
++
++ gfx_data->timeout = grub_timeout >= 0 ? grub_timeout : 0;
++
++
++ /* setup command line edit buffer */
++
++ gfx_data->cmdline_len = 256;
++
++ gfx_data->cmdline = heap;
++ heap += gfx_data->cmdline_len;
++ memset(gfx_data->cmdline, 0, gfx_data->cmdline_len);
++
++ cmd_buf = heap;
++ heap += GFX_CMD_BUF_SIZE;
++
++ /* setup menu entries */
++
++ for(i = max_len = 0; i < num_entries; i++) {
++ j = strlen(get_entry(menu_entries, i, 0));
++ if(j > max_len) max_len = j;
++ }
++
++ if(!max_len) return;
++
++ gfx_data->menu_entry_len = max_len + 1;
++ gfx_data->menu_entries = num_entries;
++
++ gfx_data->menu_list = heap;
++ heap += gfx_data->menu_entry_len * gfx_data->menu_entries;
++
++ memset(gfx_data->menu_list, 0, gfx_data->menu_entry_len * gfx_data->menu_entries);
++
++ for(i = 0; i < (int) gfx_data->menu_entries; i++) {
++ strcpy(gfx_data->menu_list + i * gfx_data->menu_entry_len, get_entry(menu_entries, i, 0));
++ }
++
++ gfx_data->menu_default_entry = gfx_data->menu_list + entryno * gfx_data->menu_entry_len;
++
++
++ /* setup list of kernel args */
++
++ for(i = max_len = 0; i < num_entries; i++) {
++ s = get_kernel_args(get_entry(config_entries, i, 1), cmd_buf);
++ j = strlen(s);
++ if(j > max_len) max_len = j;
++ }
++
++ gfx_data->args_entry_len = max_len + 1;
++
++ gfx_data->args_list = heap;
++ heap += gfx_data->args_entry_len * gfx_data->menu_entries;
++
++ memset(gfx_data->args_list, 0, gfx_data->args_entry_len * gfx_data->menu_entries);
++
++ for(i = 0; i < (int) gfx_data->menu_entries; i++) {
++ strcpy(gfx_data->args_list + i* gfx_data->args_entry_len, get_kernel_args(get_entry(config_entries, i, 1), cmd_buf));
++ }
++
++
++ /* go back here when we no longer need the graphics data */
++ saved_heap = heap;
++
++
++ /* get memory area to be used by graphics functions */
++
++ /* use 1MB starting at 2MB as file buffer */
++ buf_ext = (unsigned char *) (2 << 20);
++ buf_ext_size = 1 << 20;
++
++ /* must be 16-byte aligned */
++ buf = (unsigned char *) (((unsigned) heap + 0xf) & ~0xf);
++
++ buf_size = stack_ptr() - buf - MIN_HEAP_SIZE;
++ buf_size &= ~0xf;
++
++ mem0_start = (unsigned) buf;
++ mem0_end = mem0_start + buf_size;
++
++ if(verbose) {
++ printf("low memory 0x%x - 0x%x (%d bytes)\n", mem0_start, mem0_end, buf_size);
++ wait_for_key();
++ }
++
++ heap += buf_size;
++
++ /* read the file */
++
++ if(!grub_open(graphics_file)) {
++ printf("%s: file not found\n", graphics_file);
++ sleep(5);
++ heap = saved_heap;
++ return;
++ }
++
++ gfx_file_size = grub_read(buf_ext, buf_ext_size);
++
++ grub_close();
++
++ if(gfx_file_size <= 0) {
++ printf("%s: read error\n", graphics_file);
++ sleep(5);
++ heap = saved_heap;
++ return;
++ }
++
++ if(verbose) {
++ printf("%s: %d bytes (%d bytes left)\n", graphics_file, gfx_file_size, buf_ext_size - gfx_file_size);
++ wait_for_key();
++ }
++
++ /* locate file inside cpio archive */
++ if(!(code_start = find_file(buf_ext, gfx_file_size, &file_start, &file_len))) {
++ printf("%s: invalid file format\n", graphics_file);
++ sleep(5);
++ heap = saved_heap;
++ return;
++ }
++
++ if(verbose) {
++ printf("init: start 0x%x, len %d; code offset 0x%x\n", file_start, file_len, code_start);
++ wait_for_key();
++ }
++
++ if(file_len - code_start + MIN_GFX_FREE > buf_size) {
++ printf("not enough free memory: %d extra bytes need\n", file_len - code_start + MIN_GFX_FREE - buf_size);
++ sleep(5);
++ heap = saved_heap;
++ return;
++ }
++
++ memcpy((void *) buf, (void *) (buf_ext + file_start + code_start), file_len - code_start);
++
++ mem0_start += file_len - code_start;
++ mem0_start = (mem0_start + 3) & ~3; /* align */
++
++ /* init interface to graphics functions */
++
++ *(unsigned *) (gfx_data->sys_cfg + SC_FILE) = (unsigned) buf_ext + file_start;
++ *(unsigned *) (gfx_data->sys_cfg + SC_ARCHIVE_START) = (unsigned) buf_ext;
++ *(unsigned *) (gfx_data->sys_cfg + SC_ARCHIVE_END) = (unsigned) buf_ext + gfx_file_size;
++ *(unsigned *) (gfx_data->sys_cfg + SC_MEM0_START) = mem0_start;
++ *(unsigned *) (gfx_data->sys_cfg + SC_MEM0_END) = mem0_end;
++
++ gfx_data->code_seg = (unsigned) buf >> 4;
++
++ if(verbose) {
++ printf("init 0x%x, archive 0x%x - 0x%x, low mem 0x%x - 0x%x\ncode seg 0x%x\n",
++ (unsigned) buf_ext + file_start,
++ (unsigned) buf_ext, (unsigned) buf_ext + gfx_file_size,
++ mem0_start, mem0_end, gfx_data->code_seg
++ );
++ wait_for_key();
++ }
++
++ for(i = 0; (unsigned) i < sizeof gfx_data->jmp_table / sizeof *gfx_data->jmp_table; i++) {
++ gfx_data->jmp_table[i] = (gfx_data->code_seg << 16) + ((unsigned short *) buf)[i];
++ }
++
++ if(verbose) {
++ for(i = 0; i < 12; i++) {
++ printf("%d: 0x%x\n", i, gfx_data->jmp_table[i]);
++ }
++
++ for(i = 0; i < gfx_data->menu_entries; i++) {
++ printf("\"%s\" -- \"%s\"\n",
++ gfx_data->menu_list + i * gfx_data->menu_entry_len,
++ gfx_data->args_list + i * gfx_data->args_entry_len
++ );
++ }
++
++ printf("default: \"%s\"\n", gfx_data->menu_default_entry);
++ wait_for_key();
++ }
++
++ /* switch to graphics mode */
++
++ if(gfx_init(gfx_data)) {
++ printf("graphics initialization failed\n");
++ sleep(5);
++ heap = saved_heap;
++ return;
++ }
++
++ gfx_setup_menu(gfx_data);
++
++ i = gfx_input(gfx_data, &selected_entry);
++
++ /* ESC -> show text menu */
++ if(i == 1) {
++ gfx_done(gfx_data);
++ grub_timeout = -1;
++
++ heap = saved_heap;
++ return;
++ }
++
++ gfx_done(gfx_data);
++
++ heap = saved_heap; /* free most of the graphics data */
++
++ // printf("cmdline: >%s<, entry = %d\n", gfx_data->cmdline, selected_entry);
++
++ if(selected_entry < 0 || selected_entry > num_entries) return;
++
++
++ /* create new config with modified kernel option */
++
++ cfg = get_entry(config_entries, selected_entry, 1);
++
++ new_config = heap;
++
++ for(p = gfx_data->cmdline, i = 0; ; i++) {
++ s = get_entry(cfg, i, 0);
++ if(!*s) {
++ if(!i) *heap++ = 0;
++ *heap++ = 0;
++ break;
++ }
++ /* note: must match get_kernel_args() */
++ if(
++ (!memcmp(s, "kernel", 6) || !memcmp(s, "module", 6)) &&
++ (s[6] == ' ' || s[6] == '\t')
++ ) {
++ t = skip_to(0, s);
++ t2 = s[0] == 'm' ? strstr(t, "initrd") : NULL;
++ if(*t) t = skip_to(0, t);
++ if(t2 && t2 < t) { /* module is likely a normal initrd -> skip */
++ strcpy(heap, s);
++ heap += strlen(s) + 1;
++ continue;
++ }
++ memmove(heap, s, t - s);
++ heap += t - s;
++ *heap++ = ' ';
++ while(*p && *p != GFX_CMD_SEP) *heap++ = *p++;
++ *heap++ = 0;
++ if(*p == GFX_CMD_SEP) p++;
++ }
++ else {
++ strcpy(heap, s);
++ heap += strlen(s) + 1;
++ }
++ }
++
++ *heap++ = 0;
++
++ // hexdump(new_config, heap - new_config);
++ // getkey();
++
++ run_script(new_config, heap);
++}
++
++
+ static int
+ get_line_from_config (char *cmdline, int maxlen, int read_from_file)
+ {
+@@ -1062,9 +1557,12 @@
+ }
+ else
+ {
+- /* Run menu interface. */
+- run_menu (menu_entries, config_entries, num_entries,
+- menu_entries + menu_len, default_entry);
++ if (*graphics_file && !password && show_menu && grub_timeout)
++ {
++ run_graphics_menu(menu_entries, config_entries, num_entries,menu_entries + menu_len, default_entry);
++ }
++ /* Run menu interface. */
++ run_menu (menu_entries, config_entries, num_entries, menu_entries + menu_len, default_entry);
+ }
+ }
+ }
+--- stage2/stage2.c
++++ stage2/stage2.c
+@@ -1199,6 +1199,9 @@
+
+ if(selected_entry < 0 || selected_entry > num_entries) return;
+
++ /* for 'savedefault' */
++ current_entryno = selected_entry;
++
+
+ /* create new config with modified kernel option */
+
diff --git a/patches/lilo-22.7.diff b/patches/lilo-22.7.diff
new file mode 100644
index 0000000..a1bdc20
--- /dev/null
+++ b/patches/lilo-22.7.diff
@@ -0,0 +1,1133 @@
+--- Makefile
++++ Makefile
+@@ -330,15 +330,15 @@
+ first.s: first.S lilo.h version.h Makefile
+ $(CPP) $(PCONFIG) -DFIRST=0x199d1f05 -o first.s first.S
+
+-second.s: second.S read.S volume.S mapper.S biosdata.S shs3.S bdata.h lilo.h version.h \
++second.s: second.S gfxlogo.S read.S volume.S mapper.S biosdata.S shs3.S bdata.h lilo.h version.h \
+ graph.S menu.S strlen.S bitmap.S crt.S display4.S Makefile
+ $(CPP) $(PCONFIG) -DTEXT=0x8bd7820b second.S -o second.s
+
+-third.s: second.S read.S volume.S mapper.S biosdata.S shs3.S bdata.h lilo.h version.h \
++third.s: second.S gfxlogo.S read.S volume.S mapper.S biosdata.S shs3.S bdata.h lilo.h version.h \
+ graph.S menu.S strlen.S bitmap.S crt.S display4.S Makefile
+ $(CPP) $(PCONFIG) -DMENU=0x7920a7c2 second.S -o third.s
+
+-bitmap.s: second.S read.S volume.S mapper.S biosdata.S shs3.S bdata.h lilo.h version.h \
++bitmap.s: second.S gfxlogo.S read.S volume.S mapper.S biosdata.S shs3.S bdata.h lilo.h version.h \
+ graph.S menu.S strlen.S bitmap.S crt.S display4.S Makefile
+ $(CPP) $(PCONFIG) -DBITMAP=0xf54f8b9d second.S -o bitmap.s
+
+--- bsect.c
++++ bsect.c
+@@ -774,6 +774,7 @@
+ if (st.st_size > i)
+ die("%s is too big (> %d bytes)",message,i);
+ param2.msg_len = bitmap ? (st.st_size+15)/16 : st.st_size;
++ if(!bitmap) param2.msg_len = st.st_size > 0xffff ? 0xffff : st.st_size;
+ map_begin_section();
+ #ifndef LCF_UNIFY
+ map_add(&geo,0,((st.st_size)+SECTOR_SIZE-1)/SECTOR_SIZE);
+--- gfxlogo.S
++++ gfxlogo.S
+@@ -0,0 +1,764 @@
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; gfx stuff
++;
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++
++; != 0 -> everything is fine
++gfx_ok: .byte 0
++
++; we need it at some point
++gfx_tmp: .word 0
++
++; the memory area we are working with
++gfx_mem: .long 0 ; linear address
++
++; interface to loadable gfx extension (seg:ofs values)
++gfx_bc_jt: .long 0
++
++gfx_bc_init: .long 0
++gfx_bc_done: .long 0
++gfx_bc_input: .long 0
++gfx_bc_menu_init: .long 0
++gfx_bc_infobox_init: .long 0
++gfx_bc_infobox_done: .long 0
++gfx_bc_progress_init: .long 0
++gfx_bc_progress_done: .long 0
++gfx_bc_progress_update: .long 0
++gfx_bc_progress_limit: .long 0
++gfx_bc_password_init: .long 0
++gfx_bc_password_done: .long 0
++
++; system config data (52 bytes)
++gfx_sysconfig:
++gfx_bootloader: .byte 0
++gfx_sector_shift: .byte 0
++gfx_media_type: .byte 0
++gfx_failsafe: .byte 0
++gfx_sysconfig_size: .byte gfx_sysconfig_end-gfx_sysconfig
++gfx_boot_drive: .byte 0
++gfx_callback: .word 0
++gfx_bootloader_seg: .word 0
++gfx_reserved_1: .word 0
++gfx_user_info_0: .long 0
++gfx_user_info_1: .long 0
++gfx_bios_mem_size: .long 0
++gfx_xmem_0: .word 0x21 ; extended mem area 0 (start:size in MB; 12:4 bits)
++gfx_xmem_1: .word 0x41
++gfx_xmem_2: .word 0
++gfx_xmem_3: .word 0
++gfx_file: .long 0
++gfx_archive_start: .long 0
++gfx_archive_end: .long 0
++gfx_mem0_start: .long 0
++gfx_mem0_end: .long 0
++gfx_sysconfig_end:
++
++; menu entry descriptor
++menu_entries equ 0
++menu_default equ 2 ; seg:ofs
++menu_ent_list equ 6 ; seg:ofs
++menu_ent_size equ 10
++menu_arg_list equ 12 ; seg:ofs
++menu_arg_size equ 16
++sizeof_menu_desc equ 18
++
++menu_desc: .blkb sizeof_menu_desc
++
++; 64 dummy entries (all "")
++gfx_args_entry: .blkb 64
++
++gfx_password_buf: .blkb 32
++gfx_msg_wrong_image: .ascii "Could not find kernel image: "
++ .byte 0
++gfx_msg_wrong_password: .ascii "Sorry, incorrect password."
++ .byte 0
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; I really have no idea how to get as86 to do this properly
++
++ macro farcall
++ .byte 0xff
++ .byte 0x1e
++ .word ?1
++ mend
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_get_sysconfig:
++ xor ax,ax
++ ret
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_set_sysconfig:
++ ret
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; Initialize graphics code. Load and display graphics data.
++;
++; edi file length
++;
++; return: [gfx_ok] = 0/1
++;
++gfx_init:
++ push es
++
++ cld
++
++ mov byte gfx_ok,#0
++
++ ; no gfx if we use a serial line
++ cmp byte par2_port,#0
++ jnz near gfx_init_90
++
++ mov gfx_bootloader_seg,cs
++ mov gfx_archive_end,edi
++
++ ; define our memory area
++ ; gfx_mem _must_ be 16-byte aligned
++ mov dword gfx_mem,#0x10000
++ add edi,gfx_mem
++ add edi,#15 ; add space for alignment
++ mov gfx_mem0_start,edi
++ mov dword gfx_mem0_end,#0x80000
++
++ sub edi,gfx_mem0_end
++ neg edi
++ cmp edi,#0x1000 ; we need some minimum space
++ jc near gfx_init_80
++
++ ; align 4
++ mov eax,gfx_mem0_start
++ add eax,#3
++ and eax,#~3
++ mov gfx_mem0_start,eax
++
++ call find_file
++ or eax,eax
++ jz near gfx_init_80
++
++ push edi
++ push eax
++ add eax,edi
++ call align_it
++ pop eax
++ pop edi
++
++ sub edi,gfx_mem
++ mov ecx,gfx_archive_start
++ add edi,ecx
++ mov gfx_file,edi
++ add gfx_archive_end,ecx
++ add eax,edi
++ shr eax,4
++ mov gfx_bc_jt+2,ax
++
++ ; setup jump table
++ les bx,gfx_bc_jt
++
++ seg es
++ mov ax,(bx)
++ mov gfx_bc_init,ax
++ mov gfx_bc_init+2,es
++
++ seg es
++ mov ax,(bx+2)
++ mov gfx_bc_done,ax
++ mov gfx_bc_done+2,es
++
++ seg es
++ mov ax,(bx+4)
++ mov gfx_bc_input,ax
++ mov gfx_bc_input+2,es
++
++ seg es
++ mov ax,(bx+6)
++ mov gfx_bc_menu_init,ax
++ mov gfx_bc_menu_init+2,es
++
++ seg es
++ mov ax,(bx+8)
++ mov gfx_bc_infobox_init,ax
++ mov gfx_bc_infobox_init+2,es
++
++ seg es
++ mov ax,(bx+10)
++ mov gfx_bc_infobox_done,ax
++ mov gfx_bc_infobox_done+2,es
++
++ seg es
++ mov ax,(bx+12)
++ mov gfx_bc_progress_init,ax
++ mov gfx_bc_progress_init+2,es
++
++ seg es
++ mov ax,(bx+14)
++ mov gfx_bc_progress_done,ax
++ mov gfx_bc_progress_done+2,es
++
++ seg es
++ mov ax,(bx+16)
++ mov gfx_bc_progress_update,ax
++ mov gfx_bc_progress_update+2,es
++
++ seg es
++ mov ax,(bx+18)
++ mov gfx_bc_progress_limit,ax
++ mov gfx_bc_progress_limit+2,es
++
++ seg es
++ mov ax,(bx+20)
++ mov gfx_bc_password_init,ax
++ mov gfx_bc_password_init+2,es
++
++ seg es
++ mov ax,(bx+22)
++ mov gfx_bc_password_done,ax
++ mov gfx_bc_password_done+2,es
++
++ ; esi sysconfig data
++ .byte 0x66
++ mov si,ds ; mov esi,ds
++ shl esi,4
++ add esi,#gfx_sysconfig
++
++ farcall(gfx_bc_init)
++
++ jc gfx_init_80
++
++ mov byte gfx_ok,#1
++
++ jmp gfx_init_90
++
++gfx_init_80:
++ mov byte gfx_ok,#0
++gfx_init_90:
++ pop es
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; Back to text mode.
++;
++; return: [gfx_ok] = 0
++;
++gfx_done:
++ cmp byte gfx_ok,#0
++ jz gfx_done_90
++ farcall(gfx_bc_done)
++ mov byte gfx_ok,#0
++ call gfx_set_sysconfig
++gfx_done_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_input:
++ cmp byte gfx_ok,#0
++ jz gfx_input_90
++ call gfx_get_sysconfig
++
++ mov ax,#0xffff
++ xchg ax,par2_timeout
++
++ ; map 0xffff -> 0
++ add ax,#1
++ cmc
++ sbb ax,#0
++
++gfx_input_20:
++ .byte 0x66
++ mov di,ds ; mov edi,ds
++ shl edi,4
++ add edi,#cmdline
++ mov ecx,#CL_LENGTH
++ movzx eax,ax
++
++ ; edi buffer (0: no buffer)
++ ; ecx buffer size
++ ; eax timeout value (0: no timeout)
++
++ farcall(gfx_bc_input)
++
++ pushf
++ call gfx_set_sysconfig
++ popf
++ jnc gfx_input_50
++ mov ax,#1
++gfx_input_50:
++ cmp ax,#1
++ jz gfx_input_80
++
++ call find_boot_image
++ jnc gfx_input_90
++
++ .byte 0x66
++ mov cx,ds ; mov ecx,ds
++ shl ecx,4
++ lea esi,(ecx+gfx_msg_wrong_image)
++ lea edi,(ecx+cmdline)
++
++ mov al,#0
++ call gfx_infobox
++
++ xor ax,ax
++ jmp gfx_input_20
++gfx_input_80:
++ push ax
++ call gfx_done
++ pop ax
++gfx_input_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_setup_menu:
++ cmp byte gfx_ok,#0
++ jz gfx_setup_menu_90
++
++ mov si,#DESCR0
++ mov di,si
++ xor ax,ax
++ mov cx,#IMAGES
++gfx_setup_menu_20:
++ cmp byte (si),#0
++ jz gfx_setup_menu_30
++ inc ax
++ add si,#id_size
++ loop gfx_setup_menu_20
++gfx_setup_menu_30:
++ mov esi,#menu_desc
++
++ xor ah,ah
++ mov (si+menu_entries),ax
++
++ mov (si+menu_default),di
++ mov (si+menu_default+2),ds
++
++ mov (si+menu_ent_list),di
++ mov (si+menu_ent_list+2),ds
++ mov word (si+menu_ent_size),#id_size
++
++ mov word (si+menu_arg_list),#gfx_args_entry
++ mov (si+menu_arg_list+2),ds
++ mov word (si+menu_arg_size),#1
++
++ .byte 0x66
++ mov ax,ds ; mov eax,ds
++ shl eax,4
++ add esi,eax
++
++ farcall(gfx_bc_menu_init)
++gfx_setup_menu_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_infobox:
++ pushad
++ cmp byte gfx_ok,#0
++ jz gfx_infobox_90
++ farcall(gfx_bc_infobox_init)
++ xor edi,edi
++ xor eax,eax
++ farcall(gfx_bc_input)
++ farcall(gfx_bc_infobox_done)
++gfx_infobox_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_progress_init:
++ pushad
++ cmp byte gfx_ok,#0
++ jz gfx_progress_init_90
++ movzx eax,ax
++ farcall(gfx_bc_progress_init)
++gfx_progress_init_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_progress_done:
++ pushad
++ cmp byte gfx_ok,#0
++ jz gfx_progress_done_90
++ farcall(gfx_bc_progress_done)
++gfx_progress_done_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_progress_update:
++ pushad
++ cmp byte gfx_ok,#0
++ jz gfx_progress_update_90
++ movzx eax,cx
++ farcall(gfx_bc_progress_update)
++gfx_progress_update_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_progress_limit:
++ pushad
++ cmp byte gfx_ok,#0
++ jz gfx_progress_limit_90
++ movzx eax,ax
++ farcall(gfx_bc_progress_limit)
++gfx_progress_limit_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; ds:di image descr
++;
++gfx_password:
++ push es
++ pushad
++ cmp byte gfx_ok,#0
++ stc
++ jz gfx_password_90
++ push di
++ .byte 0x66
++ mov cx,ds ; mov ecx,ds
++ shl ecx,4
++ lea esi,(ecx+gfx_password_buf) ; not used
++ movzx edi,di
++ add edi,ecx
++ farcall(gfx_bc_password_init)
++ .byte 0x66
++ mov di,ds ; mov edi,ds
++ shl edi,4
++ add edi,#gfx_password_buf
++ mov ecx,#32
++ xor eax,eax
++ farcall(gfx_bc_input)
++ .byte 0x66
++ mov si,ds ; mov esi,ds
++ shl esi,4
++ add esi,#gfx_password_buf
++ farcall(gfx_bc_password_done)
++ pop di
++ call check_password
++ jnc gfx_password_90
++ .byte 0x66
++ mov si,ds ; mov esi,ds
++ shl esi,4
++ add esi,#gfx_msg_wrong_password
++ xor edi,edi
++ mov al,#0
++ farcall(gfx_bc_infobox_init)
++ xor edi,edi
++ xor eax,eax
++ farcall(gfx_bc_input)
++ farcall(gfx_bc_infobox_done)
++ stc
++gfx_password_90:
++ popad
++ pop es
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; Convert 32bit linear address to seg:ofs.
++;
++; dword [esp + 2]: linear address
++;
++; return:
++; dword [esp + 2]: seg:ofs
++;
++; Notes:
++; - changes no regs
++;
++gfx_l2so:
++ push eax
++ mov eax,(esp + 6)
++ shr eax,4
++ mov (esp + 8),ax
++ and word (esp + 6),#0xf
++ pop eax
++ ret
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; locate the boot image
++;
++; return:
++; bx boot image index
++; CF error (image not found)
++;
++find_boot_image:
++ mov dx,#DESCR0
++ mov cx,#IMAGES
++
++ xor bx,bx
++
++find_boot_image_10:
++ mov si,dx
++ mov di,#cmdline
++find_boot_image_20:
++ mov al,(si)
++ or al,al
++ jz find_boot_image_60
++ cmp al,(di)
++ jnz find_boot_image_30
++ inc si
++ inc di
++ jmp find_boot_image_20
++find_boot_image_30:
++ inc bx
++ add dx,#id_size
++ cmp bx,cx
++ jb find_boot_image_10
++ ; not found, strip options for fancy error message
++
++ mov si,#cmdline
++ mov cx,#MAX_IMAGE_NAME+1
++find_boot_image_40:
++ cmp byte (si),#0x20+1
++ inc si
++ jb find_boot_image_50
++ loop find_boot_image_40
++find_boot_image_50:
++ mov byte (si-1),#0
++ jmp find_boot_image_80
++find_boot_image_60:
++ mov al,(di)
++ cmp al,#0x20
++ ja find_boot_image_30
++ imul bx,bx,#id_size
++ add bx,#DESCR0
++ jmp find_boot_image_90
++find_boot_image_80:
++ stc
++find_boot_image_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; ds:di image descr
++;
++; CF = 0: password valid
++;
++check_password:
++#if defined(SHS_PASSWORDS)
++ mov bp,di
++ mov di,#gfx_password_buf
++ mov si,di
++ push ds
++ pop es
++ mov cx,#32 - 1
++ xor al,al
++ repne
++ scasb
++ sub cx,#32 - 1
++ not cx
++ mov bx,cx
++
++ push ss
++ pop es
++ mov cx,#32
++ sub sp,cx
++ mov di,sp
++ rep
++ movsb
++ mov si,sp
++
++ push bp
++
++ push bx ; length
++ push si ; ss:si password
++ call _shsInit
++ call _shsUpdate
++ call _shsFinal
++ pop si
++ pop bx
++
++ pop di
++
++ add di,#id_password_crc
++ mov si,#shs_digest
++ mov cx,#MAX_PW_CRC*4
++ push ds
++ pop es
++ repe
++ cmpsb
++ je check_pw_50
++ inc cx
++check_pw_50:
++ add sp,#32
++ pushad ; clear password buffer
++ mov di,#gfx_password_buf
++ mov cx,#32
++ xor al,al
++ rep
++ stosb
++ popad
++ cmp cx,#1
++ cmc
++#endif
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; locate graphics file
++;
++; return: eax: code offset (0 -> no file found)
++; edi: gfx file start
++;
++find_file:
++ mov edi,gfx_mem
++ push edi
++ call gfx_l2so
++ pop bx
++ pop es
++ call magic_ok
++ or eax,eax
++ jnz find_file_90
++
++ ; ok, maybe it's a cpio archive
++
++ ; note: edi must be properly aligned (2)!
++
++find_file_20:
++ mov ecx,gfx_mem0_start
++ sub ecx,#26 + 12 ; min cpio header + gfx header
++ cmp edi,ecx
++ jae find_file_90
++
++ push edi
++ call gfx_l2so
++ pop bx
++ pop es
++ seg es
++ cmp word (bx),#0x71c7
++ jnz find_file_90 ; no cpio record
++
++ seg es
++ movzx esi,word (bx+20) ; file name size
++
++ inc si
++ and si,#~1 ; align
++
++ seg es
++ mov ecx,(bx+22) ; data size
++ rol ecx,#16 ; get word order right
++
++ inc ecx
++ and ecx,#~1 ; align
++
++ add si,#26 ; skip header
++
++ add edi,esi
++ add bx,si
++ call magic_ok
++ or eax,eax
++ jnz find_file_90
++
++ add edi,ecx
++ jmp find_file_20
++
++find_file_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; es:bx file start
++;
++; return: eax: offset to code entry
++;
++; Notes:
++; - changes no regs except eax
++;
++magic_ok:
++ xor eax,eax
++ seg es
++ cmp dword (bx),#0x0b2d97f00 ; header.magic_id
++ jnz magic_ok_90
++ ; version 8
++ seg es
++ cmp byte (bx+4),#8 ; header.version
++ jb magic_ok_90
++ seg es
++ cmp byte (bx+4),#8 ; header.version
++ ja magic_ok_90
++ seg es
++ mov eax,(bx+8)
++magic_ok_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; eax address to be aligned
++;
++align_it:
++ push dword gfx_mem
++ pop dword gfx_archive_start
++ neg al
++ and eax,#0x0f
++ jz align_it_90
++ add gfx_archive_start,eax
++ mov esi,gfx_mem
++ mov ebx,gfx_mem0_start
++ sub ebx,esi
++ sub ebx,#0x0f
++ add esi,ebx
++ dec esi
++
++ std
++
++align_it_30:
++ or ebx,ebx
++ jz align_it_60
++ mov ecx,ebx
++ cmp ebx,#0x8000
++ jb align_it_40
++ mov ecx,#0x8000
++align_it_40:
++ push esi
++ sub ebx,ecx
++ sub (esp),ecx
++ push esi
++ call gfx_l2so
++ pop si
++ add si,#0x8000
++ sub word (esp),#0x8000 >> 4
++ pop es
++ mov di,si
++ add di,ax
++ seg es
++ rep
++ movsb
++ pop esi
++ jmp align_it_30
++align_it_60:
++
++ cld
++
++align_it_90:
++ ret
++
++
+--- lilo.h
++++ lilo.h
+@@ -331,7 +331,7 @@
+ #define DC_MAGIC 0xf4f2 /* magic number of default cmd. line sector */
+ #define DC_MGOFF 0x6b6d /* magic number for disabled line */
+
+-#define MAX_MESSAGE 65535 /* maximum message length */
++#define MAX_MESSAGE 8*64*1024-1 /* maximum message length (512k) */
+ #define MAX_MENU_TITLE 37 /* maximum MENU title length */
+
+ #define NEW_HDR_SIG "HdrS" /* setup header signature */
+--- second.S
++++ second.S
+@@ -618,7 +618,8 @@
+ seg es
+ cmp byte ptr (si),#0 ! empty ?
+ je iloop ! yes -> enter interactive mode
+- jmp niloop ! enter non-interactive mode
++niloop0:
++ br niloop ! enter non-interactive mode
+
+ ! No external parameters after timeout -> boot first image
+
+@@ -626,7 +627,7 @@
+ pop es
+ mov si,#DFLCMD+2 ! default command line ?
+ cmp byte ptr (si),#0
+- jne niloop ! yes -> use it
++ jne niloop0 ! yes -> use it
+ mov ax,nodfl ! no idea how to tell as86 to do jmp (addr) :-(
+ jmp ax ! fall through
+
+@@ -634,16 +635,23 @@
+ ! Command input processor
+
+ iloop:
+-#if defined(MENU) || defined(BITMAP)
++#ifdef BITMAP
+ call menu_setup
+ #endif
+
+ #ifndef BITMAP
++ cmp byte gfx_ok,#0
++ jnz iloop_gfx
++
++ ; load message before doing anything else
++ ; the graphics/text stuff is mixed in a rather messy way...
++
+ ;; BEG_FS
+ ;; SEG_FS ! message disabled ?
+ cmp word ptr par2_msg_len,#0 ;MSG_OFF+SSDIFF,#0
+ ;; END_FS
+- je nomsg ! yes -> skip this
++ ; _must_ be 'jz'!
++ jz iloop_20 ! yes -> skip this
+ call crlf
+ ;BEG_FS
+ ;SEG_FS ! load the message file
+@@ -657,13 +665,96 @@
+ call sread
+ call loadfile
+
++ ; es:bx points to file end
++ movzx ebx,bx
++ xor edi,edi
++ mov di,es
++ shl edi,4
++ add edi,ebx
++ sub edi,#SYSSEG * 16
++
++ ; edi -> message file size
++
+ xor bx,bx ! set the terminating NUL and disable further
+ ! messages
+ xchg bx,par2_msg_len ;MSG_OFF+SSDIFF
+
+ push #SYSSEG
+ pop ds
+- mov byte ptr (bx),#0
++
++ cmp word ptr 0,#0x71c7 ; cpio header
++ jz iloop_cpio
++
++ cmp dword ptr 0,#0x0b2d97f00 ; magic header
++ ; _must_ be 'jnz'!
++ jnz iloop_10
++
++iloop_cpio:
++
++ push cs
++ pop ds
++
++ ; graphical message
++
++ call gfx_init
++ push cs
++ pop es
++ cmp byte gfx_ok,#0
++ jz iloop
++ call gfx_setup_menu
++iloop_gfx:
++ push cs
++ pop es
++ call gfx_input
++ cmp ax,#1
++ jz iloop ; text mode
++ mov gfx_tmp,bx
++ jmp near scan_cmdline
++
++ ; normal text message
++iloop_10:
++ ; keep the zero flag!
++ mov byte ptr (bx),#0
++ push cs
++ pop ds
++
++iloop_20:
++#ifdef MENU
++ pushf
++ call menu_setup
++ popf
++#endif
++ jnz totxt
++
++ mov ax,showit
++ mov dx,showit+2
++ mov bx,ax
++ or bx,dx
++ jz iloop_40
++ push ds
++ shl dx,12
++ mov bx,ax
++ and bx,#0xf
++ shr ax,4
++ add ax,dx
++ add ax,#SYSSEG
++ mov ds,ax
++ call say
++ pop ds
++ xor ax,ax
++ mov showit,ax
++ mov showit+2,ax
++iloop_40:
++
++ jmp nomsg
++
++showit:
++ .long 0
++
++totxt:
++ push #SYSSEG
++ pop ds
++
+ xor bx,bx ! display the message
+ call say
+
+@@ -699,12 +790,6 @@
+ mov al,(si)
+ inc si
+ jmp gotinp ! go on
+-
+-tolist:
+-#ifdef BITMAP
+- call menu_exit
+-#endif
+- br list ! ...
+
+ kbinp:
+ mov cx,#brto ! get a key
+@@ -733,7 +818,7 @@
+ cmp al,#8 ! BS ?
+ je todelch ! yes -> erase one character
+ cmp al,#13 ! CR ?
+- je cr ! yes -> go on
++ je near cr ! yes -> go on
+ cmp al,#127 ! DEL ?
+ je todelch ! yes -> erase one character
+ ja input ! non-printable -> ignore it
+@@ -783,6 +868,11 @@
+
+ todelch:br delch ! ...
+ todell: br delline ! ...
++tolist:
++#ifdef BITMAP
++ call menu_exit
++#endif
++ br list ! ...
+
+ ! End of input, process the command line
+
+@@ -825,6 +915,8 @@
+ or al,al ! at end ?
+ jnz cpsav ! no -> go on
+
++
++scan_cmdline:
+ cmp bx,#cmdline ! empty line ?
+ je notrspc ! yes -> boot first image
+ cmp byte ptr (bx-1),#32 ! trailing space ?
+@@ -882,6 +974,11 @@
+ je chkvga ! yes -> look for options again
+ or al,al ! at end ?
+ jnz vsknb ! no -> go on
++
++ mov bx,gfx_tmp
++ cmp byte gfx_ok,#0
++ jnz near boot
++
+ call crlf ! write CR/LF
+ cmp di,#cmdline+1 ! empty line ?
+ emptyl: je bfirst ! yes -> boot first image
+@@ -1073,6 +1170,16 @@
+ ;; SEG_FS
+ mov word ptr par2_timeout,#0xffff ; cancel timeout
+ ;; END_FS
++
++ cmp byte gfx_ok,#0
++ jz dopw_nogfx
++ mov di,bx
++ call gfx_password
++ pop bx
++ jnc toboot
++ jmp near iloop
++dopw_nogfx:
++
+ mov bx,#msg_pw ! display a prompt
+ call say
+
+@@ -1450,7 +1557,9 @@
+
+ ! Boot the image BX points to
+
+-doboot: mov byte ptr prechr,#61 ! switch to equal sign
++doboot:
++ call gfx_done
++ mov byte ptr prechr,#61 ! switch to equal sign
+ push bx ! save image descr
+ mov bx,#msg_l ! say hi
+ call say
+@@ -3274,6 +3383,8 @@
+
+
+
++#include "gfxlogo.S"
++
+ #ifdef SHS_PASSWORDS
+ #include "shs3.S"
+ #endif
+@@ -3421,6 +3532,8 @@
+ jmp vgaput1
+ #else
+ xor cx,cx
++ mov bh,ch
++ mov bl,#10 ; bx: base (_not_ bl!)
+ mov ah,cl
+ test byte ptr (si),#0xff ! no value ?
+ jz vgaerr ! yes -> error
+@@ -3429,17 +3542,47 @@
+ jz vgaput ! yes -> done
+ cmp al,#32
+ je vgaput
+- cmp al,#48 ! is it a digit ? (0x30=48="0")
+- jb vgaerr ! no -> error
+- cmp al,#57 ! 57=0x39="9"
+- ja vgaerr
+- sub al,#48 ! cx = cx*10+al-'0'
+- imul cx,#10
++ cmp al,#0x61
++ jb vgadig_10
++ sub al,#0x20 ; upper case
++vgadig_10:
++ or cx,cx
++ jnz vgadig_20
++ cmp al,#0x58
++ jnz vgadig_20
++ mov bl,#16 ; hex
++ jmp vgadig
++vgadig_20:
++ cmp al,#0x3a
++ jb vgadig_30
++ sub al,#7
++vgadig_30:
++ sub al,#0x30
++ jb vgaerr ; not a digit
++ cmp al,bl
++ jae vgaerr ; larger than current base
++ imul cx,bx
+ add cx,ax
+ jnc vgadig ! next one
+ #endif
+
+-vgaerr: mov bx,#msg_v ! display an error message
++vgaerr:
++ mov bx,#msg_v ! display an error message
++ cmp byte gfx_ok,#0
++ jz vgaerr_txt
++ lea esi,(bx+1) ; skip initial NL
++ .byte 0x66
++ mov di,ds ; mov edi,ds
++ shl edi,4
++ add esi,edi
++ xor edi,edi
++ mov al,#0
++ mov msg_v1,al ; drop final NL
++ call gfx_infobox
++ mov byte msg_v1,#10 ; put it back
++ stc
++ ret
++vgaerr_txt:
+ call say
+ /* ifdef HIGHMEM_MAX */
+ xor eax,eax
+@@ -3583,8 +3726,10 @@
+ xor dx,dx
+ mov cx,#10 ! default radix is decimal
+ cmp byte ptr (si),#0x39
+- ja s2lbad ! error if > '9'
++ /* Those were changed to jna and previously */
++ ja s2lbad ! error if > '9'
+ cmp byte ptr (si),#0x30 ! == '0'?
++ /* Likewise */
+ jb s2lbad ! error if < '0'
+ jne s2lnext
+ inc si
+@@ -3890,8 +4035,8 @@
+
+ msg_v: .byte 10
+ .ascii "Valid vga values are ASK, NORMAL, EXTENDED or a "
+- .ascii "decimal number."
+- .byte 10,0
++ .ascii "number."
++msg_v1: .byte 10,0
+
+ msg_pks:.byte 10
+ .ascii "Invalid hexadecimal number. - Ignoring remaining items."
diff --git a/patches/syslinux-3.63.diff b/patches/syslinux-3.63.diff
new file mode 100644
index 0000000..ab9d657
--- /dev/null
+++ b/patches/syslinux-3.63.diff
@@ -0,0 +1,2352 @@
+--- Makefile
++++ Makefile
+@@ -146,15 +146,16 @@
+ $(PERL) genhash.pl < keywords > kwdhash.gen
+
+ # Standard rule for {isolinux,isolinux-debug}.bin
+-iso%.bin: iso%.asm kwdhash.gen version.gen
++iso%.bin: iso%.asm kwdhash.gen version.gen gfxboot.inc
+ $(NASM) $(NASMOPT) -f bin -DDATE_STR="'$(DATE)'" -DHEXDATE="$(HEXDATE)" \
+ -DMAP=$(@:.bin=.map) -l $(@:.bin=.lsr) -o $@ $<
+ $(PERL) lstadjust.pl $(@:.bin=.lsr) $(@:.bin=.map) $(@:.bin=.lst)
+ $(PERL) checksumiso.pl $@
+ $(PERL) checkov.pl $(@:.bin=.map) $@
++ -./add_crc $@
+
+ # Standard rule for {ldlinux,pxelinux,extlinux}.bin
+-%.bin: %.asm kwdhash.gen version.gen
++%.bin: %.asm kwdhash.gen version.gen gfxboot.inc
+ $(NASM) $(NASMOPT) -f bin -DDATE_STR="'$(DATE)'" -DHEXDATE="$(HEXDATE)" \
+ -DMAP=$(@:.bin=.map) -l $(@:.bin=.lsr) -o $@ $<
+ $(PERL) lstadjust.pl $(@:.bin=.lsr) $(@:.bin=.map) $(@:.bin=.lst)
+--- README.gfxboot
++++ README.gfxboot
+@@ -0,0 +1,41 @@
++Graphical boot screen
++=====================
++
++ syslinux/isolinux support a graphical boot screen using VESA BIOS
++ extensions. (Note that this is different from the graphics support that
++ syslinux comes with).
++
++ To use it you have to prepare a special boot logo file and put a line like
++ this into syslinux.cfg/isolinux.cfg:
++
++ gfxboot foo
++
++ The tools to create 'foo' from the above example are in the gfxboot
++ package. Please _do_ have a look at its documentation before you begin.
++
++ Note that you cannot use comboot images and graphics at the same time as
++ the memory used overlaps the comboot loading area.
++
++ If you encouter problems with the graphics code, hold down SHIFT while
++ syslinux starts. This will put it into 'failsafe' mode that lets you
++ interactively skip critical parts (like monitor detection).
++
++
++
++Spread boot images over several floppy disks (syslinux)
++=======================================================
++
++ You can prepare boot disks with a file system that spans several disks.
++ The 'mkbootdisk' script from the openSUSE project can create a suitable
++ file system.
++
++ syslinux will ask you for disk changes if necessary. To enable this
++ feature, use
++
++ disksize <size_of_single_disk_in_sectors>
++
++ Note that every individual disk must have at least a valid FAT boot
++ sector. syslinux will use the serial number stored there to verify that
++ the correct disk has been inserted (its last hex digit is the zero based
++ disk number).
++
+--- abort.inc
++++ abort.inc
+@@ -21,6 +21,11 @@
+ ; abort_check: let the user abort with <ESC> or <Ctrl-C>
+ ;
+ abort_check:
++%ifdef WITH_GFX
++ ; don't
++ cmp byte [gfx_ok],0
++ jnz .ret1
++%endif
+ call pollchar
+ jz .ret1
+ pusha
+--- add_crc
++++ add_crc
+@@ -0,0 +1,57 @@
++#! /usr/bin/perl
++
++use integer;
++
++# for isolinux
++#
++# Ensure checksum over (first sector - 64 bytes) [internally: FirstSecSum]
++# is 0 by adjusting the variable csum_value.
++#
++# Though isolinux checks the integrity with a separate checksum after all
++# data has been loaded this does not help with BIOSes that don't get even
++# the first 2k right. Hence this additional check. :-(
++#
++
++$file = shift;
++$list = "$file";
++$list =~ s/\.bin$/.lsr/;
++
++open F, $list;
++
++while(<F>) {
++ if(/^\s*\d+\s*(\S+)\s*0+\s*(\<\d+\>\s*)?csum_value\s*dd\s*0/) {
++ $ofs = hex $1;
++ }
++}
++close F;
++
++die "oops 1\n" unless $ofs && !($ofs & 3);
++
++# print "$ofs\n";
++
++open F, $file or die "$file: $!\n";
++
++$file_size = -s $file;
++
++sysread F, $buf, $file_size;
++
++close F;
++
++die "oops 1\n" if $file_size != length($buf);
++
++@x = unpack "V512", $buf;
++
++for ($sum = 0, $i = 16; $i < 512; $i++) {
++ $sum += $x[$i];
++}
++
++# printf "0x%08x\n", $sum;
++
++$ns = pack "V", -$sum;
++
++substr($buf, $ofs, 4) = $ns;
++
++open F, ">$file" or die "$file: $!\n";
++
++syswrite F, $buf;
++
+--- com32/libutil/get_key.c
++++ com32/libutil/get_key.c
+@@ -42,6 +42,10 @@
+ #include <getkey.h>
+ #include <libutil.h>
+
++#ifndef CLK_TCK
++# define CLK_TCK __sysconf(2)
++#endif
++
+ struct keycode {
+ int code;
+ int seqlen;
+--- com32/modules/cpuid.c
++++ com32/modules/cpuid.c
+@@ -254,7 +254,7 @@
+
+ static int smp_scan_config (unsigned long base, unsigned long length)
+ {
+- unsigned long *bp = base;
++ unsigned long *bp = (unsigned long *) base;
+ struct intel_mp_floating *mpf;
+
+ // printf("Scan SMP from %p for %ld bytes.\n", bp,length);
+--- com32/samples/keytest.c
++++ com32/samples/keytest.c
+@@ -25,6 +25,10 @@
+ #include <consoles.h> /* Provided by libutil */
+ #include <getkey.h>
+
++#ifndef CLK_TCK
++# define CLK_TCK __sysconf(2)
++#endif
++
+ static void cooked_keys(void)
+ {
+ int key;
+--- conio.inc
++++ conio.inc
+@@ -49,6 +49,14 @@
+ ; Assumes CS == DS == ES.
+ ;
+ get_msg_file:
++%ifdef WITH_GFX
++ ; don't load if graphics code is active
++ cmp byte [gfx_ok],0
++ jz .nogfx
++ jmp close
++.nogfx:
++%endif
++
+ mov byte [TextAttribute],07h ; Default grey on white
+ mov byte [DisplayMask],07h ; Display text in all modes
+ call msg_initvars
+--- doc/syslinux.txt
++++ doc/syslinux.txt
+@@ -230,7 +230,7 @@
+ Append nothing. APPEND with a single hyphen as argument in a
+ LABEL section can be used to override a global APPEND.
+
+- LOCALBOOT type [ISOLINUX, PXELINUX]
++ LOCALBOOT type [ISOLINUX, SYSLINUX, PXELINUX]
+ On PXELINUX, specifying "LOCALBOOT 0" instead of a "KERNEL"
+ option means invoking this particular label will cause a local
+ disk boot instead of booting a kernel.
+@@ -244,12 +244,11 @@
+ UNDI or PXE stacks are, don't worry -- you don't want them,
+ just specify 0.
+
+- On ISOLINUX, the "type" specifies the local drive number to
+- boot from; 0x00 is the primary floppy drive and 0x80 is the
+- primary hard drive. The special value -1 causes ISOLINUX to
+- report failure to the BIOS, which, on recent BIOSes, should
+- mean that the next boot device in the boot sequence should be
+- activated.
++ On ISOLINUX and SYSLINUX, the "type" specifies the local drive
++ number to boot from; 0x00 is the primary floppy drive and 0x80 is
++ the primary hard drive. The special value -1 causes them to report
++ failure to the BIOS, which, on recent BIOSes, should mean that the
++ next boot device in the boot sequence should be activated.
+
+ IMPLICIT flag_val
+ If flag_val is 0, do not load a kernel image unless it has been
+--- gfxboot.inc
++++ gfxboot.inc
+@@ -0,0 +1,1355 @@
++ section .text
++
++load_gfx_msg db 'Loading...', 0
++no_msg db 0
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; do a reboot
++;
++do_reboot:
++ call gfx_done
++ mov word [472h],1234h
++ push word 0ffffh
++ push word 0
++ retf
++ int 19h
++ jmp $
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; wait for 'enter' key pressed
++;
++wait_for_key:
++ pusha
++wait_for_key_10:
++ mov ah,0
++ int 16h
++ cmp al,13
++ jnz wait_for_key_10
++ popa
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; read gfx data
++get_gfx_file:
++ push si
++ mov si,load_gfx_msg
++ call cwritestr
++ pop si
++ mov word [gfx_mem_start_seg],first_free_seg
++ push ds
++ push 40h
++ pop ds
++ mov bx,[13h] ; mem size in kb
++ pop ds
++ shl bx,6
++ mov word [gfx_mem_end_seg],bx
++
++ call gfx_init ; Load and display file
++ cmp byte [gfx_ok],0
++ jz .done
++ mov si,crlf_msg
++ call cwritestr
++.done:
++ ret
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; read from disk, ask for disk change, if necessary
++
++; EAX - Linear sector number
++; ES:BX - Target buffer
++; BP - Sector count
++;
++; (es:)bx gets updated
++
++%if IS_SYSLINUX
++
++; %define DEBUG_XXX
++
++getlinsec3:
++%ifdef DEBUG_XXX
++ mov si,txt0
++ call dump_params
++ call crlf
++%endif
++ mov cx,[DiskSize]
++ or cx,cx
++ jz getlinsec3_70 ; unlimited
++
++ xor dx,dx
++ div cx
++ xchg ax,dx
++ movzx eax,ax
++
++ mov si,ax
++ add si,bp
++ sub si,cx
++ jbe getlinsec3_40
++ ; split
++
++ sub bp,si
++ movzx ebp,bp
++ push ebp
++ push eax
++ push si
++ push dx
++ call getlinsec3_40
++ pop dx
++ pop bp
++ pop eax
++ pop esi
++ add eax,esi
++ movzx ebp,bp
++ inc dl
++
++getlinsec3_40:
++ push es
++ pushad
++ call disk_change
++ popad
++ pop es
++ jc kaboom
++getlinsec3_70:
++%ifdef DEBUG_XXX
++ mov si,txt1
++ call dump_params
++ mov si,txt3
++ call cwritestr
++ push ax
++ mov al,[CurrentDisk]
++ call writehex2
++ pop ax
++ call crlf
++ pushad
++ call getchar
++ popad
++%endif
++ xor edx,edx
++ call getlinsec.jmp
++ ret
++
++%ifdef DEBUG_XXX
++dump_params:
++ push eax
++ call cwritestr
++ mov ax,es
++ call writehex4
++ mov si,txt2
++ call cwritestr
++ mov ax,bx
++ call writehex4
++ mov si,txt3
++ call cwritestr
++ pop eax
++ push eax
++ call writehex8
++ mov si,txt3
++ call cwritestr
++ mov ax,bp
++ call writehex4
++ pop eax
++ ret
++
++txt0 db 'linsec3: ', 0
++txt1 db 'linsec: ', 0
++txt2 db ':', 0
++txt3 db ', ', 0
++
++%include "writehex.inc"
++
++%endif
++
++
++; dl: new disk
++; return: CF = 1 -> error
++disk_change:
++ cmp dl,[CurrentDisk]
++ jz disk_change_90
++
++ mov [CurrentDisk],dl
++ movzx eax,dl
++ mov [gfx_user_info_0],eax
++ add dl,'1'
++ mov [boot_disk_msg0],dl
++ mov [boot_ndisk_msg0],dl
++
++disk_change_20:
++ cmp byte [gfx_ok],0
++ jz disk_change_40
++ mov al,3
++ xor di,di
++ xor si,si
++ call gfx_infobox
++
++ jmp disk_change_50
++disk_change_40:
++ mov si,clrln_msg
++ call cwritestr
++ mov si,boot_disk_msg
++ call cwritestr
++ call wait_for_key
++ mov si,clrln_msg
++ call cwritestr
++disk_change_50:
++ xor eax,eax
++ mov bp,1
++ push bx
++ xor edx,edx
++ call getlinsec.jmp
++ pop bx
++ mov eax,[es:bx+27h]
++ sub eax,[bsVolumeID]
++ movzx edx,byte [CurrentDisk]
++ cmp eax,edx
++ jz disk_change_90
++ mov [gfx_user_info_1],eax
++
++ cmp byte [gfx_ok],0
++ jz disk_change_70
++ mov al,4
++ xor di,di
++ xor si,si
++ call gfx_infobox
++
++ jmp disk_change_50
++disk_change_70:
++ mov si,clrln_msg
++ call cwritestr
++ mov si,boot_ndisk_msg
++ call cwritestr
++; mov eax,[es:bx+27h]
++; call writehex8
++ call wait_for_key
++ mov si,clrln_msg
++ call cwritestr
++
++ jmp disk_change_20
++disk_change_90:
++ ret
++
++
++bsVolumeID equ bsHugeSectors+7
++
++boot_disk_msg db 'Please insert boot disk '
++boot_disk_msg0 db '0; then press ENTER to continue.', 0
++boot_ndisk_msg db 'This is not boot disk '
++boot_ndisk_msg0 db '0. Press ENTER to continue.', 0
++
++DiskSize dw 0 ; unlimited
++CurrentDisk db 0 ; current disk
++
++clrln_msg db 0dh, ' ', 0dh, 0
++
++
++cache_metadata:
++ cmp word [DiskSize],0
++ jz cache_md_90
++
++ mov eax,[FAT]
++ mov ecx,[bxFATsecs]
++cache_md_20:
++ push eax
++ push cx
++ call getcachesector
++ pop cx
++ pop eax
++ inc eax
++ loop cache_md_20
++
++ mov eax,[RootDir]
++cache_md_40:
++ push eax
++ call getcachesector
++ pop eax
++ call nextsector
++ jnc cache_md_40
++
++cache_md_90:
++ ret
++
++
++%endif
++
++
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; gfx stuff
++;
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++
++; != 0 -> graphics active
++gfx_ok db 0
++gfx_cwritestr_old db 0c3h ; 'ret'
++gfx_cpio_swab db 0
++gfx_cd_changed db 0
++
++gfx_mem_start_seg dw 0
++gfx_mem_end_seg dw 0
++
++ align 4, db 0
++; the memory area we are working with
++gfx_mem dd 0 ; linear address
++
++gfx_save_area1 dd 0 ; 64k
++gfx_save_area1_used db 0 ; != 0 if area1 is in use
++
++; interface to loadable gfx extension (seg:ofs values)
++gfx_bc_jt dd 0
++
++gfx_bc_init dd 0
++gfx_bc_done dd 0
++gfx_bc_input dd 0
++gfx_bc_menu_init dd 0
++gfx_bc_infobox_init dd 0
++gfx_bc_infobox_done dd 0
++gfx_bc_progress_init dd 0
++gfx_bc_progress_done dd 0
++gfx_bc_progress_update dd 0
++gfx_bc_progress_limit dd 0
++gfx_bc_password_init dd 0
++gfx_bc_password_done dd 0
++
++; menu entry descriptor
++menu_entries equ 0
++menu_default equ 2 ; seg:ofs
++menu_ent_list equ 6 ; seg:ofs
++menu_ent_size equ 10
++menu_arg_list equ 12 ; seg:ofs
++menu_arg_size equ 16
++sizeof_menu_desc equ 18
++
++menu_desc zb sizeof_menu_desc
++
++; system config data (52 bytes)
++gfx_sysconfig equ $
++gfx_bootloader db 1 ; 0: boot loader type (0: lilo, 1: syslinux, 2: grub)
++gfx_sector_shift db SECTOR_SHIFT ; 1: sector shift
++gfx_media_type db 0 ; 2: media type (0: disk, 1: floppy, 2: cdrom)
++gfx_failsafe db 0 ; 3: turn on failsafe mode (bitmask)
++ ; 0: SHIFT pressed
++ ; 1: skip gfxboot
++ ; 2: skip monitor detection
++gfx_sysconfig_size db gfx_sysconfig_end-gfx_sysconfig ; 4: size of sysconfig data
++gfx_boot_drive db 0 ; 5: BIOS boot drive
++gfx_callback dw gfx_cb ; 6: offset to callback handler
++gfx_bootloader_seg dw 0 ; 8: code/data segment used by bootloader; must follow gfx_callback
++gfx_reserved_1 dw 0 ; 10
++gfx_user_info_0 dd 0 ; 12: data for info box
++gfx_user_info_1 dd 0 ; 16: data for info box
++gfx_bios_mem_size dd 0 ; 20: BIOS memory size (in bytes)
++gfx_xmem_0 dw 0 ; 24: extended mem area 0 (start:size in MB; 12:4 bits)
++gfx_xmem_1 dw 0 ; 26: extended mem area 1
++gfx_xmem_2 dw 0 ; 28: extended mem area 2
++gfx_xmem_3 dw 0 ; 20: extended mem area 3
++gfx_file dd 0 ; 32: start of gfx file
++gfx_archive_start dd 0 ; 36: start of cpio archive
++gfx_archive_end dd 0 ; 40: end of cpio archive
++gfx_mem0_start dd 0 ; 44: low free memory start
++gfx_mem0_end dd 0 ; 48: low free memory end
++gfx_sysconfig_end equ $
++
++gfx_slash db '/', 0
++
++%macro lin2segofs 3
++ push %1
++ call gfx_l2so
++ pop %3
++ pop %2
++%endmacro
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++; must not change registers!
++;
++gfx_get_sysconfig:
++ push ax
++ mov al,[DriveNumber]
++ mov [gfx_boot_drive],al
++
++%if IS_ISOLINUX
++ mov ah,2
++%else
++ mov ah,0
++%endif
++ cmp al,80h ; floppy ?
++ jae gfx_get_sysconfig_20
++ mov ah,1
++gfx_get_sysconfig_20:
++
++ mov [gfx_media_type],ah
++
++ mov [gfx_bootloader_seg],cs
++
++ pop ax
++ ret
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; Initialize graphics code. Load and display graphics data.
++;
++; dx:ax file length
++; si start cluster
++;
++; return: [gfx_ok] = 0/1
++;
++gfx_init:
++ push es
++
++ test byte [KbdFlags],3
++ jz gfx_init_10
++ mov byte [gfx_failsafe],1
++ call ask_user
++gfx_init_10:
++
++ test byte [gfx_failsafe],2
++ jnz gfx_init_80
++
++ call highmemsize
++
++ ; setup extended memory areas
++ pusha
++ mov eax,[HighMemSize]
++ mov [gfx_bios_mem_size],eax
++ mov eax,[VKernelEnd]
++ shr eax,20
++ cmp ax,16
++ jb gfx_init_40 ; at least 16MB
++ mov word [gfx_xmem_0],81h ; 1MB at 8MB
++ mov word [gfx_xmem_1],0a1h ; 1MB at 10MB
++
++ mov dword [gfx_save_area1],7f0000h ; 8MB-64k
++gfx_init_40:
++ popa
++
++ cld
++
++ movzx ebx,word [gfx_mem_start_seg]
++ shl ebx,4
++ jz gfx_init_80
++
++ movzx ecx,word [gfx_mem_end_seg]
++ shl ecx,4
++ jz gfx_init_80
++
++ cmp ecx,ebx
++ jbe gfx_init_80
++
++ ; define our memory area
++ ; gfx_mem _must_ be 16-byte aligned
++ mov dword [gfx_mem],ebx
++ mov dword [gfx_mem0_start],ebx
++ mov dword [gfx_mem0_end],ecx
++
++ call gfx_read_file
++ cmp byte [gfx_ok],0
++ jz near gfx_init_90
++
++ call gfx_get_sysconfig
++
++ ; align 4
++ mov eax,[gfx_mem0_start]
++ add eax,3
++ and eax,~3
++ mov [gfx_mem0_start],eax
++
++ ; setup jump table
++ les bx,[gfx_bc_jt]
++
++ mov ax,[es:bx]
++ mov [gfx_bc_init],ax
++ mov [gfx_bc_init+2],es
++
++ mov ax,[es:bx+2]
++ mov [gfx_bc_done],ax
++ mov [gfx_bc_done+2],es
++
++ mov ax,[es:bx+4]
++ mov [gfx_bc_input],ax
++ mov [gfx_bc_input+2],es
++
++ mov ax,[es:bx+6]
++ mov [gfx_bc_menu_init],ax
++ mov [gfx_bc_menu_init+2],es
++
++ mov ax,[es:bx+8]
++ mov [gfx_bc_infobox_init],ax
++ mov [gfx_bc_infobox_init+2],es
++
++ mov ax,[es:bx+10]
++ mov [gfx_bc_infobox_done],ax
++ mov [gfx_bc_infobox_done+2],es
++
++ mov ax,[es:bx+12]
++ mov [gfx_bc_progress_init],ax
++ mov [gfx_bc_progress_init+2],es
++
++ mov ax,[es:bx+14]
++ mov [gfx_bc_progress_done],ax
++ mov [gfx_bc_progress_done+2],es
++
++ mov ax,[es:bx+16]
++ mov [gfx_bc_progress_update],ax
++ mov [gfx_bc_progress_update+2],es
++
++ mov ax,[es:bx+18]
++ mov [gfx_bc_progress_limit],ax
++ mov [gfx_bc_progress_limit+2],es
++
++ mov ax,[es:bx+20]
++ mov [gfx_bc_password_init],ax
++ mov [gfx_bc_password_init+2],es
++
++ mov ax,[es:bx+22]
++ mov [gfx_bc_password_done],ax
++ mov [gfx_bc_password_done+2],es
++
++ mov esi,cs
++ shl esi,4
++ add esi,gfx_sysconfig
++ call far [gfx_bc_init]
++ jc gfx_init_80
++
++ mov byte [gfx_ok],1
++
++ ; turn off 'cwritestr'
++ mov al,[cwritestr]
++ cmp al,0c3h
++ jz gfx_init_90
++ mov [gfx_cwritestr_old],al
++ mov byte [cwritestr],0c3h
++
++ jmp gfx_init_90
++
++gfx_init_80:
++ mov byte [gfx_ok],0
++gfx_init_90:
++ pop es
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; Back to text mode.
++;
++; return: [gfx_ok] = 0
++;
++gfx_done:
++ push ax
++ cmp byte [gfx_ok],0
++ jz gfx_done_90
++ call far [gfx_bc_done]
++ mov byte [gfx_ok],0
++
++ ; reactivate 'cwritestr'
++ mov al,[gfx_cwritestr_old]
++ cmp al,0c3h
++ jz gfx_done_90
++ mov [cwritestr],al
++gfx_done_90:
++ pop ax
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_input:
++ cmp byte [gfx_ok],0
++ jz gfx_input_90
++ cmp byte [gfx_save_area1_used],0
++ jz gfx_input_10
++ ; recover saved menu layout, gfxboot has references into it
++ pushad
++ mov esi,[gfx_save_area1]
++ mov edi,gfx_menu_seg << 4
++ mov ecx,10000h
++ call bcopy
++ popad
++gfx_input_10:
++ call far [gfx_bc_input]
++ jnc gfx_input_50
++ mov ax,1
++gfx_input_50:
++ cmp ax,1
++ jnz gfx_input_90
++ push ax
++ call gfx_done
++ pop ax
++gfx_input_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; es:di string
++; return:
++; cx length
++gfx_strlen:
++ mov cx,-1
++ mov al,0
++ repnz scasb
++ not cx
++ dec cx
++ ret
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_setup_menu:
++ push es
++ cmp byte [gfx_ok],0
++ jz gfx_setup_menu_90
++
++ mov dword [menu_desc+menu_default],gfx_menu_seg << 16
++
++ push ds
++ pop es
++ mov si,default_cmd
++ mov di,trackbuf
++ call mangle_name
++ mov si,trackbuf
++ les di,[menu_desc+menu_default]
++ call unmangle_name
++ inc di
++
++ mov [menu_desc+menu_ent_list],di
++ mov [menu_desc+menu_ent_list+2],es
++ mov [menu_desc+menu_arg_list],di
++ mov [menu_desc+menu_arg_list+2],es
++
++ ; first, count entries and find max kernel and append length
++
++ push ds
++ pop es
++
++ mov esi,[HighMemSize]
++ jmp gfx_setup_menu_20
++gfx_setup_menu_10:
++ mov di,VKernelBuf
++ call rllunpack
++
++ ; count only non empty entries
++ cmp byte [VKernelBuf],0
++ jz gfx_setup_menu_20
++
++ inc word [menu_desc+menu_entries]
++
++ push esi
++ mov si,VKernelBuf
++ mov di,KernelCName
++ push di
++ call unmangle_name
++ pop di
++ pop esi
++
++ call gfx_strlen
++
++ cmp cx,[menu_desc+menu_ent_size]
++ jbe gfx_setup_menu_15
++ mov [menu_desc+menu_ent_size],cx
++gfx_setup_menu_15:
++ mov ax,[VKernelBuf+vk_appendlen]
++ cmp ax,[menu_desc+menu_arg_size]
++ jbe gfx_setup_menu_20
++ mov [menu_desc+menu_arg_size],ax
++
++gfx_setup_menu_20:
++ cmp esi,[VKernelEnd]
++ ja gfx_setup_menu_10
++
++ inc word [menu_desc+menu_ent_size]
++ mov ax,[menu_desc+menu_ent_size]
++ inc ax
++ add [menu_desc+menu_arg_size],ax
++
++ ; ...and again, but this time copy entries
++
++ mov word [menu_desc+menu_entries],0
++
++ mov esi,[HighMemSize]
++ jmp gfx_setup_menu_60
++gfx_setup_menu_30:
++ mov di,VKernelBuf
++ call rllunpack
++
++ ; count only non empty entries
++ cmp byte [VKernelBuf],0
++ jz gfx_setup_menu_60
++
++ mov di,[menu_desc+menu_arg_list]
++ add di,[menu_desc+menu_arg_size]
++ jc gfx_setup_menu_60
++
++ inc word [menu_desc+menu_entries]
++
++ push esi
++
++ mov si,VKernelBuf
++ mov di,KernelCName
++ push di
++ call unmangle_name
++ pop si
++ mov cx,[menu_desc+menu_ent_size]
++ les di,[menu_desc+menu_arg_list]
++
++ rep movsb
++
++ mov cx,[VKernelBuf+vk_appendlen]
++ mov si,VKernelBuf+vk_append
++ rep movsb
++ mov byte [es:di],0
++
++ pop esi
++
++ push ds
++ pop es
++
++ mov ax,[menu_desc+menu_arg_size]
++ add [menu_desc+menu_arg_list],ax
++
++gfx_setup_menu_60:
++ cmp esi,[VKernelEnd]
++ ja gfx_setup_menu_30
++
++ mov ax,[menu_desc+menu_ent_size]
++ mov bx,[menu_desc+menu_arg_size]
++ mov [menu_desc+menu_ent_size],bx
++ add ax,[menu_desc+menu_ent_list]
++ mov [menu_desc+menu_arg_list],ax
++
++ mov esi,ds
++ shl esi,4
++ add esi,menu_desc
++
++ call far [gfx_bc_menu_init]
++
++ ; save menu structure, gfxboot uses references into it
++ mov edi,[gfx_save_area1]
++ or edi,edi
++ jz gfx_setup_menu_90
++ mov esi,gfx_menu_seg << 4
++ mov ecx,10000h
++ call bcopy
++ mov byte [gfx_save_area1_used],1
++
++gfx_setup_menu_90:
++ pop es
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_infobox:
++ pushad
++ cmp byte [gfx_ok],0
++ jz gfx_infobox_90
++ mov ecx,ds
++ shl ecx,4
++ movzx esi,si
++ movzx edi,di
++ or si,si
++ jz gfx_infobox_20
++ add esi,ecx
++gfx_infobox_20:
++ or di,di
++ jz gfx_infobox_30
++ add edi,ecx
++gfx_infobox_30:
++ call far [gfx_bc_infobox_init]
++ xor edi,edi
++ xor eax,eax
++ call far [gfx_bc_input]
++ call far [gfx_bc_infobox_done]
++gfx_infobox_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_progress_init:
++ pushad
++ cmp byte [gfx_ok],0
++ jz gfx_progress_init_90
++ call far [gfx_bc_progress_init]
++gfx_progress_init_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_progress_done:
++ pushad
++ cmp byte [gfx_ok],0
++ jz gfx_progress_done_90
++ call far [gfx_bc_progress_done]
++gfx_progress_done_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_progress_update:
++ pushad
++ cmp byte [gfx_ok],0
++ jz gfx_progress_update_90
++ movzx eax,cx
++ call far [gfx_bc_progress_update]
++gfx_progress_update_90:
++ popad
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_progress_limit:
++ pushad
++ cmp byte [gfx_ok],0
++ jz gfx_progress_limit_90
++ movzx eax,ax
++ movzx edx,dx
++ call far [gfx_bc_progress_limit]
++gfx_progress_limit_90:
++ popad
++ ret
++
++
++%if 0
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++gfx_password:
++ pushad
++ cmp byte [gfx_ok],0
++ stc
++ jz gfx_password_90
++ call far [gfx_bc_password_init]
++ mov edi,ds
++ shl edi,4
++ add edi,gfx_password_buf
++ mov ecx,32
++ xor eax,eax
++ call far [gfx_bc_input]
++ mov esi,ds
++ shl esi,4
++ add esi,gfx_password_buf
++ call far [gfx_bc_password_done]
++ jnc gfx_password_90
++ mov esi,ds
++ shl esi,4
++ add esi,gfx_msg_wrong_password
++ xor edi,edi
++ mov al,0
++ call far [gfx_bc_infobox_init]
++ xor edi,edi
++ xor eax,eax
++ call far [gfx_bc_input]
++ call far [gfx_bc_infobox_done]
++ stc
++gfx_password_90:
++ popad
++ ret
++%endif
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; Read graphics data and store them at [gfx_mem].
++;
++; dx:ax file length
++; si start cluster
++;
++; return: [gfx_ok] = 0/1
++;
++gfx_read_file:
++ push es
++ mov byte [gfx_ok],0
++ mov edi,[gfx_mem]
++ push dx ; DX:AX = length of file
++ push ax
++ pop edx
++ mov [gfx_archive_end],edx
++ mov eax,[gfx_mem0_start]
++ lea eax,[eax+edx+0fh] ; add space for alignment
++ cmp eax,[gfx_mem0_end] ; max. length
++ ja near gfx_read_file_90
++ mov [gfx_mem0_start],eax
++
++gfx_read_file_10:
++ mov bx,trackbuf
++ mov cx,[BufSafe]
++ push edi
++ push edx
++ call getfssec
++ pop edx
++ pop edi
++ movzx ecx,word [BufSafeBytes]
++ cmp edx,ecx
++ jae gfx_read_file_20
++ mov ecx,edx
++gfx_read_file_20:
++ push ecx
++ push edi
++ push si ; Save current cluster
++ push es
++ mov si,trackbuf
++ push edi
++ call gfx_l2so
++ pop di
++ pop es
++ rep movsb
++ pop es
++ pop si
++ pop edi
++ pop ecx
++ add edi,ecx
++ sub edx,ecx
++ ja gfx_read_file_10
++
++ call find_file
++ or eax,eax
++ jz gfx_read_file_90
++ push edi
++ push eax
++ add eax,edi
++ call align_it
++ pop eax
++ pop edi
++ sub edi,[gfx_mem]
++ mov ecx,[gfx_archive_start]
++ add edi,ecx
++ mov [gfx_file],edi
++ add [gfx_archive_end],ecx
++ add eax,edi
++ shr eax,4
++ mov [gfx_bc_jt+2],ax
++
++ mov byte [gfx_ok],1
++
++gfx_read_file_90:
++ pop es
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; locate graphics file
++;
++; return: eax: code offset (0 -> no file found)
++; edi: gfx file start
++;
++find_file:
++ mov edi,[gfx_mem]
++ lin2segofs edi,es,bx
++ call magic_ok
++ or eax,eax
++ jnz find_file_90
++
++ ; ok, maybe it's a cpio archive
++
++ ; note: edi must be properly aligned (2)!
++
++find_file_20:
++ mov ecx,[gfx_mem0_start]
++ sub ecx,26 + 12 ; min cpio header + gfx header
++ cmp edi,ecx
++ jae find_file_90
++
++ lin2segofs edi,es,bx
++ cmp word [es:bx],71c7h
++ jz find_file_30 ; normal cpio record
++ cmp word [es:bx],0c771h ; maybe byte-swapped?
++ jnz find_file_90 ; no cpio record
++ mov byte [gfx_cpio_swab],1
++
++find_file_30:
++ mov ax,[es:bx+20] ; file name size
++ call cpio_swab
++ movzx esi,ax
++
++ inc si
++ and si,~1 ; align
++
++ mov eax,[es:bx+22] ; data size
++ call cpio_swab
++ rol eax,16 ; get word order right
++ call cpio_swab
++ mov ecx,eax
++
++ inc ecx
++ and ecx,byte ~1 ; align
++
++ add si,26 ; skip header
++
++ add edi,esi
++ add bx,si
++ call magic_ok
++ or eax,eax
++ jnz find_file_90
++
++ add edi,ecx
++ jmp find_file_20
++
++find_file_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; byte-swap cpio data if appropriate
++;
++; ax: word to swap
++;
++; return: ax: swapped if [gfx_cpio_swab], otherwise same as input
++;
++cpio_swab:
++ cmp byte [gfx_cpio_swab],0
++ jz cpio_swab_90
++ xchg ah,al
++
++cpio_swab_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; es:bx file start
++;
++; return: eax: offset to code entry
++;
++; Notes:
++; - changes no regs except eax
++;
++magic_ok:
++ xor eax,eax
++ cmp dword [es:bx],0b2d97f00h ; header.magic_id
++ jnz magic_ok_90
++ cmp byte [es:bx+4],8 ; header.version
++ jnz magic_ok_90
++ mov eax,[es:bx+8]
++magic_ok_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; eax address to be aligned
++;
++align_it:
++ push dword [gfx_mem]
++ pop dword [gfx_archive_start]
++ neg al
++ and eax,byte 0fh
++ jz align_it_90
++ add [gfx_archive_start],eax
++ mov esi,[gfx_mem]
++ mov ebx,[gfx_mem0_start]
++ sub ebx,esi
++ sub ebx,byte 0fh
++ add esi,ebx
++ dec esi
++
++ std
++
++align_it_30:
++ or ebx,ebx
++ jz align_it_60
++ mov ecx,ebx
++ cmp ebx,8000h
++ jb align_it_40
++ mov ecx,8000h
++align_it_40:
++ push esi
++ sub ebx,ecx
++ sub [esp],ecx
++ push esi
++ call gfx_l2so
++ pop si
++ add si,8000h
++ sub word [esp],(8000h >> 4)
++ pop es
++ mov di,si
++ add di,ax
++ es rep movsb
++ pop esi
++ jmp align_it_30
++align_it_60:
++
++ cld
++
++align_it_90:
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; Ask user whether to continue.
++;
++; don't change regs
++;
++ask_user:
++ pushad
++
++ mov si,failsafe_msg_0
++ call cwritestr
++
++ mov dx,2
++ mov si,failsafe_msg_1
++ call ask_question
++
++ test byte [gfx_failsafe],2
++ jnz ask_user_90
++
++ mov dx,4
++ mov si,failsafe_msg_2
++ call ask_question
++
++ask_user_90:
++
++ popad
++ ret
++
++
++; si: text
++; dh: 'yes'-mask
++; dl: 'no'-mask
++ask_question:
++ push dx
++ call cwritestr
++ mov si,failsafe_msg_q
++ call cwritestr
++ pop dx
++
++ask_question_20:
++ push dx
++ mov ah,0
++ int 16h
++ pop dx
++
++ cmp al,13
++ jnz ask_question_30
++ mov al,'y'
++ask_question_30:
++ or al,20h ; force lower case
++
++ cmp al,'y'
++ jz ask_question_40
++ cmp al,'n'
++ jnz ask_question_20
++ or byte [gfx_failsafe],dl
++ jmp ask_question_50
++ask_question_40:
++ or byte [gfx_failsafe],dh
++ask_question_50:
++
++ mov si,failsafe_key
++ mov [si],al
++ call cwritestr
++
++ ret
++
++failsafe_msg_q db ' (y/n)? y', 8, 0
++
++failsafe_msg_0 db 13, 10, 10, 10, 0
++failsafe_msg_1 db 'Load boot graphics', 0
++failsafe_msg_2 db 'Detect display size', 0
++
++failsafe_key db 0, 13, 10, 0
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++; Convert 32bit linear address to seg:ofs.
++;
++; dword [esp + 2]: linear address
++;
++; return:
++; dword [esp + 2]: seg:ofs
++;
++; Notes:
++; - changes no regs
++;
++gfx_l2so:
++ push eax
++ mov eax,[esp + 6]
++ shr eax,4
++ mov [esp + 8],ax
++ and word [esp + 6],byte 0fh
++ pop eax
++ ret
++
++
++; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
++;
++cb_table dw cb_status
++ dw cb_fopen
++ dw cb_fread
++ dw cb_getcwd
++ dw cb_chdir
++ dw cb_readsector
++cb_len equ ($-cb_table)/2
++
++f_handle dw 0
++f_size dd 0
++fname_buf times 64 db 0
++fname_buf_len equ $ - fname_buf
++
++
++gfx_cb:
++ push cs
++ pop ds
++
++ cmp al,cb_len
++ jae gfx_cb_80
++
++ movzx bx,al
++ add bx,bx
++ call word [bx+cb_table]
++ jmp gfx_cb_90
++
++gfx_cb_80:
++ mov al,0ffh
++gfx_cb_90:
++ retf
++
++
++; Return status info.
++;
++; return:
++; edx filename buffer (64 bytes)
++;
++cb_status:
++ mov edx,cs
++ shl edx,4
++ add edx,fname_buf
++
++ xor al,al
++ ret
++
++; Open file.
++;
++; return:
++; al 0: ok, 1: file not found
++; ecx file length (al = 0)
++;
++cb_fopen:
++ mov si,fname_buf
++ mov di,VGAFileMBuf ; we just need some space
++ push ds
++ pop es
++ push di
++ call mangle_name
++ pop di
++ call searchdir
++ xchg ax,bx
++ mov al,1
++ jz cb_fopen_90
++ mov [f_handle],si
++ mov cx,dx
++ shl ecx,16
++ mov cx,bx
++ mov [f_size],ecx
++cb_fopen_80:
++ xor al,al
++cb_fopen_90:
++ ret
++
++
++; Read next chunk.
++;
++; return:
++; edx buffer address (linear)
++; ecx data length (< 64k)
++;
++cb_fread:
++ cmp dword [f_size],0
++ jz cb_fread_80
++ push cs
++ pop es
++ mov bx,trackbuf
++ mov cx,[BufSafe]
++ mov si,[f_handle]
++ call getfssec
++ mov [f_handle],si
++ mov ecx,[f_size]
++ movzx edx,word [BufSafeBytes]
++ cmp ecx,edx
++ jbe cb_fread_50
++ mov ecx,edx
++cb_fread_50:
++ sub [f_size],ecx
++ mov edx,cs
++ shl edx,4
++ add edx,trackbuf
++
++cb_fread_80:
++ xor al,al
++cb_fread_90:
++ ret
++
++
++; Return current working directory.
++;
++; return:
++; edx filename
++;
++cb_getcwd:
++ mov edx,cs
++ shl edx,4
++%if IS_ISOLINUX
++ add edx,isolinux_dir
++%else
++ add edx,gfx_slash
++%endif
++ xor al,al
++ ret
++
++
++; Set current working directory.
++;
++cb_chdir:
++%if IS_ISOLINUX
++
++ push cs
++ pop es
++ mov si,fname_buf
++ mov di,isolinux_dir
++cb_chdir_20:
++ lodsb
++ stosb
++ or al,al
++ jz cb_chdir_60
++ cmp di,config_name - 1
++ jb cb_chdir_20
++ xor al,al
++ stosb
++cb_chdir_60:
++ call get_fs_structures
++
++%endif
++ xor al,al
++ ret
++
++
++; read sector
++;
++; edx sector
++;
++; return:
++; edx buffer (linear address)
++;
++; Note: does not return on error!
++;
++cb_readsector:
++ mov eax,edx
++ push ds
++ pop es
++ mov bx,trackbuf
++ call getonesec
++ mov edx,ds
++ shl edx,4
++ add edx,trackbuf
++ xor al,al
++ ret
++
+--- isolinux.asm
++++ isolinux.asm
+@@ -19,6 +19,10 @@
+ ; ****************************************************************************
+
+ %define IS_ISOLINUX 1
++
++%define WITH_GFX 1
++; %define DEBUG_DISKIO
++
+ %include "head.inc"
+
+ ;
+@@ -70,6 +74,8 @@
+ real_mode_seg equ 2000h
+ xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
+ comboot_seg equ real_mode_seg ; COMBOOT image loading zone
++gfx_menu_seg equ 3000h ; temporary storage to setup menu
++first_free_seg equ 4000h ; end of isolinux used memory
+
+ ;
+ ; File structure. This holds the information for each currently open file.
+@@ -259,6 +265,21 @@
+ mov [FirstSecSum],edi
+
+ mov [DriveNumber],dl
++
++ ; check whether the BIOS did load us correctly
++ cmp dl,80h ; some BIOSes try to do floppy emulation...
++ jb bios_err
++ cmp dword [FirstSecSum], byte 0
++ jz bios_ok
++bios_err:
++ mov si,broken_bios_msg
++ call writemsg
++ jmp short $
++broken_bios_msg db 13, 10, 'Cannot boot from this CD. Please try a BIOS update.', 13, 10, 0
++ align 4
++csum_value dd 0
++bios_ok:
++
+ %ifdef DEBUG_MESSAGES
+ mov si,startup_msg
+ call writemsg
+@@ -283,6 +304,9 @@
+ ; Other nonzero fields
+ inc word [dsp_sectors]
+
++%if 0
++ ; Some BIOSes don't like that call.
++
+ ; Now figure out what we're actually doing
+ ; Note: use passed-in DL value rather than 7Fh because
+ ; at least some BIOSes will get the wrong value otherwise
+@@ -303,6 +327,8 @@
+ call crlf
+ %endif
+
++%endif
++
+ found_drive:
+ ; Alright, we have found the drive. Now, try to find the
+ ; boot file itself. If we have a boot info table, life is
+@@ -427,6 +453,9 @@
+ %endif
+ jmp all_read ; Jump to main code
+
++%if 0
++ ; doesn't work anyway, see above
++
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+@@ -618,6 +647,7 @@
+ mov si,trysbm_msg
+ call writemsg
+ jmp .found_drive ; Pray that this works...
++%endif
+
+ fatal_error:
+ mov si,nothing_msg
+@@ -694,10 +724,17 @@
+ ;
+ getlinsec:
+ mov si,dapa ; Load up the DAPA
+- mov [si+4],bx
+- mov bx,es
+- mov [si+6],bx
+ mov [si+8],eax
++ ; seems that some BIOSes have problems if the target
++ ; segment is 0 (don't ask); to avoid this, we normalize
++ ; the buffer address here
++ ; -> seen on Acer TravelMate C102Ti
++ mov [si+4],bx
++ and word [si+4],0fh
++ mov ax,es
++ shr bx,4
++ add ax,bx
++ mov [si+6],ax
+ .loop:
+ push bp ; Sectors left
+ cmp bp,[MaxTransfer]
+@@ -724,15 +761,78 @@
+ ; INT 13h with retry
+ xint13: mov byte [RetryCount],retry_count
+ .try: pushad
++%ifdef DEBUG_DISKIO
++ pushad
++ mov cx,16
++.zap:
++ lodsb
++ call writehex2
++ mov al,' '
++ call writechr
++ loop .zap
++ mov ah,0
++ int 16h
++ popad
++%endif
++ ; seen buggy bios that overwrites buffer address on error...
++ push dword [dapa + 4]
+ call int13
++ pop dword [dapa + 4]
++%ifdef DEBUG_DISKIO
++ pushad
++ pushf
++ push ax
++ mov al,':'
++ call writechr
++ mov al,' '
++ call writechr
++ pop ax
++ sbb al,al
++ call writehex4
++ call crlf
++ mov ah,0
++ int 16h
++ popf
++ popad
++%endif
+ jc .error
++.noerror:
+ add sp,byte 8*4 ; Clean up stack
+ ret
+ .error:
++ or ah,ah
++ jz .noerror
+ mov [DiskError],ah ; Save error code
+ popad
+ mov [DiskSys],ax ; Save system call number
+- dec byte [RetryCount]
++
++%if 0
++ ; eject currently not supported - doesn't work anyway with
++ ; most BIOSes
++
++ test byte [gfx_user_note],1
++ jz .noeject
++ cmp byte [RetryCount],4
++ ja .noeject
++ cmp byte [DiskError],0aah ; drive not ready
++ jnz .noeject
++ ; might have been cdrom eject, wait a bit
++ cmp byte [gfx_ok],0
++ jz .noeject
++ push si
++ push di
++ push ax
++ mov si,err_not_ready
++ xor di,di
++ mov al,0
++ call gfx_infobox
++ pop ax
++ pop di
++ pop si
++%endif
++.noeject:
++
++ dec byte [RetryCount]
+ jz .real_error
+ push ax
+ mov al,[RetryCount]
+@@ -774,6 +874,17 @@
+ ;
+ kaboom:
+ RESET_STACK_AND_SEGS AX
++
++ cmp byte [gfx_ok],0
++ jz .nogfx
++ mov si,err_failed_gfx
++ xor di,di
++ mov al,1
++ call gfx_infobox
++ call gfx_done
++ call do_reboot
++.nogfx:
++
+ mov si,err_bootfailed
+ call cwritestr
+ call getchar
+@@ -825,6 +936,9 @@
+ crlf_msg db CR, LF
+ null_msg db 0
+
++err_failed_gfx db 'Error reading boot CD.', 0
++err_not_ready db 'CDROM drive not ready.', 0
++
+ alignb 4, db 0
+ MaxTransfer dw 32 ; Max sectors per transfer
+
+@@ -882,6 +996,9 @@
+ ; (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.
+ ;
++ call get_fs_structures
++ jmp get_fs_struct_done
++
+ get_fs_structures:
+ mov eax,[bi_pvd]
+ mov bx,trackbuf
+@@ -907,10 +1024,15 @@
+ ; Look for an isolinux directory, and if found,
+ ; make it the current directory instead of the root
+ ; directory.
++
++ cmp byte [gfx_ok],0 ; don't look at both
++ jnz .gfx
++
+ mov di,boot_dir ; Search for /boot/isolinux
+ mov al,02h
+ call searchdir_iso
+ jnz .found_dir
++.gfx:
+ mov di,isolinux_dir
+ mov al,02h ; Search for /isolinux
+ call searchdir_iso
+@@ -931,6 +1053,9 @@
+ call crlf
+ %endif
+ .no_isolinux_dir:
++ ret
++
++get_fs_struct_done:
+
+ ;
+ ; Locate the configuration file
+@@ -1096,6 +1221,9 @@
+ ; 0xFFFF in case we should execute INT 18h ("next device.")
+ ;
+ local_boot:
++%ifdef WITH_GFX
++ call gfx_done
++%endif
+ call vgaclearmode
+ lss sp,[cs:Stack] ; Restore stack pointer
+ xor dx,dx
+@@ -1113,7 +1241,7 @@
+ xor dh,dh
+ push dx
+ xor ax,ax ; Reset drive
+- call xint13
++ int 13h ; we don't care about errors here...
+ mov ax,0201h ; Read one sector
+ mov cx,0001h ; C/H/S = 0/0/1 (first sector)
+ mov bx,trackbuf
+@@ -1488,6 +1616,9 @@
+ %include "rawcon.inc" ; Console I/O w/o using the console functions
+ %include "adv.inc" ; Auxillary Data Vector
+
++%include "gfxboot.inc" ; add gfx things
++
++
+ ; -----------------------------------------------------------------------------
+ ; Begin data section
+ ; -----------------------------------------------------------------------------
+@@ -1499,6 +1630,7 @@
+ default_len equ ($-default_str)
+ boot_dir db '/boot' ; /boot/isolinux
+ isolinux_dir db '/isolinux', 0
++zb 64
+ config_name db 'isolinux.cfg', 0
+ err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0
+
+--- keywords
++++ keywords
+@@ -42,3 +42,5 @@
+ f10
+ f11
+ f12
++gfxboot
++disksize
+--- keywords.inc
++++ keywords.inc
+@@ -90,8 +90,15 @@
+ %if IS_PXELINUX
+ keyword ipappend, pc_ipappend
+ %endif
+-%if IS_PXELINUX || IS_ISOLINUX
++%if IS_PXELINUX || IS_ISOLINUX || IS_SYSLINUX
+ keyword localboot, pc_localboot
+ %endif
+
++%ifdef WITH_GFX
++ keyword gfxboot, pc_filecmd, get_gfx_file
++%if IS_SYSLINUX
++ keyword disksize, pc_disksize, DiskSize
++%endif
++%endif
++
+ keywd_count equ ($-keywd_table)/keywd_size
+--- layout.inc
++++ layout.inc
+@@ -88,7 +88,11 @@
+ section .uibss nobits align=16 follows=.adv
+
+ ; Normal bss...
++%if IS_ISOLINUX
++ section .bss1 nobits align=16 follows=.bss2
++%else
+ section .bss1 nobits align=16 follows=.uibss
++%endif
+
+ ; Reserve space for stack
+ section .stack nobits align=16 start=STACK_START
+--- ldlinux.asm
++++ ldlinux.asm
+@@ -24,6 +24,9 @@
+ %ifndef IS_MDSLINUX
+ %define IS_SYSLINUX 1
+ %endif
++
++%define WITH_GFX 1
++
+ %include "head.inc"
+
+ ;
+@@ -82,6 +85,8 @@
+ cache_seg equ 2000h ; 64K area for metadata cache
+ xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
+ comboot_seg equ real_mode_seg ; COMBOOT image loading zone
++gfx_menu_seg equ 4000h ; temporary storage to setup menu
++first_free_seg equ 5000h ; end of syslinux used memory
+
+ ;
+ ; File structure. This holds the information for each currently open file.
+@@ -357,6 +362,8 @@
+ add eax,[bsHidden] ; Add partition offset
+ xor edx,edx ; Zero-extend LBA (eventually allow 64 bits)
+
++.patch: jmp strict near .jmp
++
+ .jmp: jmp strict short getlinsec_cbios
+
+ ;
+@@ -906,6 +913,48 @@
+ ;
+ %include "ui.inc"
+
++; Boot a specified local disk. AX specifies the BIOS disk number; or
++; 0xFFFF in case we should execute INT 18h ("next device.")
++;
++local_boot:
++%ifdef WITH_GFX
++ call gfx_done
++%endif
++ call vgaclearmode
++ lss sp,[cs:Stack] ; Restore stack pointer
++ xor dx,dx
++ mov ds,dx
++ mov es,dx
++ mov fs,dx
++ mov gs,dx
++ mov si,localboot_msg
++ call cwritestr
++ cmp ax,-1
++ je .int18
++
++ ; Load boot sector from the specified BIOS device and jump to it.
++ mov dl,al
++ xor dh,dh
++ push dx
++ xor ax,ax ; Reset drive
++ int 13h
++ mov ax,0201h ; Read one sector
++ mov cx,0001h ; C/H/S = 0/0/1 (first sector)
++ mov bx,trackbuf
++ int 13h
++ pop dx
++ cli ; Abandon hope, ye who enter here
++ mov si,trackbuf
++ mov di,07C00h
++ mov cx,512 ; Probably overkill, but should be safe
++ rep movsd
++ mov ss,cx
++ mov sp,7c00h
++ jmp 0:07C00h ; Jump to new boot sector
++.int18:
++ int 18h ; Hope this does the right thing...
++ jmp kaboom ; If we returned, oh boy...
++
+ ;
+ ; allocate_file: Allocate a file structure
+ ;
+@@ -1126,6 +1175,15 @@
+ ; starting with "kaboom.patch" with this part
+
+ kaboom2:
++ cmp byte [gfx_ok],0
++ jz .nogfx
++ mov si,err_failed_gfx
++ xor di,di
++ mov al,1
++ call gfx_infobox
++ call gfx_done
++ call do_reboot
++.nogfx:
+ mov si,err_bootfailed
+ call cwritestr
+ cmp byte [kaboom.again+1],18h ; INT 18h version?
+@@ -1530,6 +1588,8 @@
+ %include "cache.inc" ; Metadata disk cache
+ %include "adv.inc" ; Auxillary Data Vector
+
++%include "gfxboot.inc" ; add gfx things
++
+ ; -----------------------------------------------------------------------------
+ ; Begin data section
+ ; -----------------------------------------------------------------------------
+@@ -1539,6 +1599,8 @@
+ db CR, LF, 0
+ err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
+ db 'a key to continue.', CR, LF, 0
++err_failed_gfx db 'Error reading from disk.', 0
++localboot_msg db 'Booting from local disk...', CR, LF, 0
+ syslinux_cfg1 db '/boot' ; /boot/syslinux/syslinux.cfg
+ syslinux_cfg2 db '/syslinux' ; /syslinux/syslinux.cfg
+ syslinux_cfg3 db '/' ; /syslinux.cfg
+--- loadhigh.inc
++++ loadhigh.inc
+@@ -47,7 +47,12 @@
+ .read_loop:
+ and si,si ; If SI == 0 then we have end of file
+ jz .eof
++%ifdef WITH_GFX
++ cmp byte [gfx_ok],0
++ jnz .no_message
++%endif
+ call bx
++.no_message:
+ push bx ; <AA> Pausebird function
+
+ push eax ; <A> Total bytes to transfer
+@@ -63,7 +68,15 @@
+ push edi ; <C> Target buffer
+ mov cx,ax
+ xor bx,bx ; ES:0
++
++%ifdef WITH_GFX
++ call gfx_progress_update
++%endif
++
++ push dx
+ call getfssec ; Load the data into xfer_buf_seg
++ ; getfssec destroys dx!
++ pop dx
+ pop edi ; <C> Target buffer
+ pop ecx ; <B> Byte count this round
+ push ecx ; <B> Byte count this round
+--- parsecmd.inc
++++ parsecmd.inc
+@@ -107,6 +107,14 @@
+ FKeyName resb MAX_FKEYS*FILENAME_MAX ; File names for F-key help
+ KernelCNameLen resw 1 ; Length of unmangled kernel name
+ InitRDCNameLen resw 1 ; Length of unmangled initrd name
++
++%if IS_ISOLINUX
++%ifdef DEBUG_MESSAGES
++; we need to rearrange memory a bit to make isolinux-debug.bin fit
++ section .bss
++%endif
++%endif
++
+ %if IS_SYSLINUX
+ KernelName resb FILENAME_MAX+1 ; Mangled name for kernel
+ KernelCName resb FILENAME_MAX+2 ; Unmangled kernel name
+--- parseconfig.inc
++++ parseconfig.inc
+@@ -81,7 +81,7 @@
+ ;
+ ; "localboot" command (PXELINUX, ISOLINUX)
+ ;
+-%if IS_PXELINUX || IS_ISOLINUX
++%if IS_PXELINUX || IS_ISOLINUX || IS_SYSLINUX
+ pc_localboot: call getint
+ cmp byte [VKernel],0 ; ("label" section only)
+ je .err
+@@ -286,6 +286,25 @@
+ mov [SerialPort], word 0
+ ret
+
++%ifdef WITH_GFX
++%if IS_SYSLINUX
++;
++; like pc_setint16, but patch sector read funtion, too
++;
++pc_disksize:
++ push ax
++ call getint
++ pop si
++ jc .err
++ mov [si],bx
++ mov word [getlinsec.patch+1], getlinsec3 - getlinsec.patch - 3
++ or bx,bx
++ jz .err
++ call cache_metadata
++.err: ret
++%endif
++%endif
++
+ ;
+ ; "F"-key command
+ ;
+--- pxelinux.asm
++++ pxelinux.asm
+@@ -846,6 +846,9 @@
+ ; AX contains the appropriate return code.
+ ;
+ local_boot:
++%ifdef WITH_GFX
++ call gfx_done
++%endif
+ push cs
+ pop ds
+ mov [LocalBootType],ax
+--- runkernel.inc
++++ runkernel.inc
+@@ -206,6 +206,15 @@
+ pop ds
+ sub si,cmd_line_here
+ mov [CmdLineLen],si ; Length including final null
++
++%ifdef WITH_GFX
++ mov eax,[KernelSects]
++ mov esi,ds
++ shl esi,4
++ add esi,KernelCName
++ call gfx_progress_init
++%endif
++
+ ;
+ ; Now check if we have a large kernel, which needs to be loaded high
+ ;
+@@ -312,6 +321,19 @@
+ jz nk_noinitrd
+ call parse_load_initrd
+ nk_noinitrd:
++
++%ifdef WITH_GFX
++ call gfx_progress_done
++
++ cmp byte [gfx_cd_changed],0
++ jz .no_cd_change
++ mov al,6
++ xor si,si
++ xor di,di
++ call gfx_infobox
++.no_cd_change:
++%endif
++
+ ;
+ ; Abandon hope, ye that enter here! We do no longer permit aborts.
+ ;
+@@ -320,6 +342,10 @@
+ mov si,ready_msg
+ call cwritestr
+
++%ifdef WITH_GFX
++ call gfx_done
++%endif
++
+ call vgaclearmode ; We can't trust ourselves after this
+
+ UNLOAD_PREP ; Module-specific hook
+@@ -552,6 +578,34 @@
+
+ .got_start:
+ push si
++
++%ifdef WITH_GFX
++ cmp byte [si],'+'
++ jnz .got_start_10
++ mov byte [cs:gfx_cd_changed],1
++ inc si
++ push es
++ push ds
++ push si
++ push cs
++ pop es
++ mov cx,100h
++ mov di,trackbuf
++ push di
++ rep movsb
++ mov byte [es:di],0
++ pop si
++ xor di,di
++ mov al,5 ; ask for cd change
++ push cs
++ pop ds
++ call gfx_infobox
++ pop si
++ pop ds
++ pop es
++.got_start_10:
++%endif
++
+ mov di,InitRD ; Target buffer for mangled name
+ call mangle_name
+ call loadinitrd
+@@ -645,6 +699,20 @@
+ ret
+
+ .notthere:
++
++%ifdef WITH_GFX
++ cmp byte [gfx_ok],0
++ jz .nogfx
++ mov si,InitRDCName
++ xor di,di
++ mov al,2
++ call gfx_infobox
++ call gfx_progress_done
++ mov si,no_msg
++ jmp abort_load
++.nogfx:
++%endif
++
+ mov si,err_noinitrd
+ call cwritestr
+ mov si,InitRDCName
+@@ -653,7 +721,21 @@
+ jmp abort_load
+
+ no_high_mem: ; Error routine
++
++%ifdef WITH_GFX
++ cmp byte [gfx_ok],0
++ jz .nogfx
+ mov si,err_nohighmem
++ xor di,di
++ mov al,0
++ call gfx_infobox
++ call gfx_progress_done
++ mov si,no_msg
++ jmp abort_load
++.nogfx:
++%endif
++
++ mov si,err_nohighmem
+ jmp abort_load
+
+ ret
+@@ -666,7 +748,8 @@
+ ready_msg db 'ready.', CR, LF, 0
+ err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
+ db CR, LF, 0
+-err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
++err_noinitrd db CR, LF
++err_noinitrda db 'Could not find ramdisk image: ', 0
+ err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
+
+ boot_image db 'BOOT_IMAGE='
+--- ui.inc
++++ ui.inc
+@@ -44,6 +44,11 @@
+
+ .no_bootonce:
+
++%ifdef WITH_GFX
++ ; build gfx menu
++ call gfx_setup_menu
++%endif
++
+ ;
+ ; Check whether or not we are supposed to display the boot prompt.
+ ;
+@@ -57,6 +62,21 @@
+ cmp word [NoEscape],0 ; If NOESCAPE, no prompt,
+ jne auto_boot ; always run default cmd
+
++%ifdef WITH_GFX
++ cmp byte [gfx_ok],0
++ jz .nogfx
++ mov edi,ds
++ shl edi,4
++ add edi,command_line
++ mov ecx,max_cmd_len
++ xor eax,eax
++ xchg eax,[KbdTimeout] ; only the first time
++ call gfx_input
++ cmp ax,1
++ jnz load_kernel
++.nogfx:
++%endif
++
+ mov si,boot_prompt
+ call cwritestr
+
+@@ -323,9 +343,21 @@
+ push word real_mode_seg
+ pop es
+ mov di,cmd_line_here
++
++%ifdef WITH_GFX
++ ; gfx code includes them
++ cmp byte [gfx_ok],0
++ jnz .isgfx
++%endif
++
+ mov si,VKernelBuf+vk_append
+ mov cx,[VKernelBuf+vk_appendlen]
+ rep movsb
++
++%ifdef WITH_GFX
++.isgfx:
++%endif
++
+ mov [CmdLinePtr],di ; Where to add rest of cmd
+ pop es
+ mov di,KernelName
+@@ -343,7 +375,7 @@
+ mov al, [VKernelBuf+vk_type]
+ mov [KernelType], al
+
+-%if IS_PXELINUX || IS_ISOLINUX
++%if IS_PXELINUX || IS_ISOLINUX || IS_SYSLINUX
+ ; Is this a "localboot" pseudo-kernel?
+ %if IS_PXELINUX
+ cmp byte [VKernelBuf+vk_rname+4], 0
+@@ -421,6 +453,19 @@
+ push di
+ call unmangle_name ; Get human form
+ mov si,err_notfound ; Complain about missing kernel
++
++%ifdef WITH_GFX
++ cmp byte [gfx_ok],0
++ jz .nogfx
++ pop si
++ xor di,di
++ mov al,2
++ call gfx_infobox
++ mov si,no_msg
++ jmp abort_load
++.nogfx:
++%endif
++
+ call cwritestr
+ pop si ; KernelCName
+ call cwritestr
+--- gfxboot.inc
++++ gfxboot.inc
+@@ -417,8 +417,10 @@
+ shr eax,20
+ cmp ax,16
+ jb gfx_init_40 ; at least 16MB
+- mov word [gfx_xmem_0],81h ; 1MB at 8MB
++ mov word [gfx_xmem_0], 81h ; 1MB at 8MB
+ mov word [gfx_xmem_1],0a1h ; 1MB at 10MB
++ mov word [gfx_xmem_2],0c1h ; 1MB at 12MB
++ mov word [gfx_xmem_3],0e1h ; 1MB at 14MB
+
+ mov dword [gfx_save_area1],7f0000h ; 8MB-64k
+ gfx_init_40:
diff --git a/patches/syslinux-3.82/01_isolinux_mount_chdir.diff b/patches/syslinux-3.82/01_isolinux_mount_chdir.diff
new file mode 100644
index 0000000..3881586
--- /dev/null
+++ b/patches/syslinux-3.82/01_isolinux_mount_chdir.diff
@@ -0,0 +1,175 @@
+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/patches/syslinux-3.82/02_comboot_hooks.diff b/patches/syslinux-3.82/02_comboot_hooks.diff
new file mode 100644
index 0000000..52c9465
--- /dev/null
+++ b/patches/syslinux-3.82/02_comboot_hooks.diff
@@ -0,0 +1,117 @@
+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/patches/syslinux-3.82/03_comboot_ne_real_mode_seg.diff b/patches/syslinux-3.82/03_comboot_ne_real_mode_seg.diff
new file mode 100644
index 0000000..fe319bf
--- /dev/null
+++ b/patches/syslinux-3.82/03_comboot_ne_real_mode_seg.diff
@@ -0,0 +1,44 @@
+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/patches/syslinux-3.82/04_new_quiet_flag.diff b/patches/syslinux-3.82/04_new_quiet_flag.diff
new file mode 100644
index 0000000..b63ed99
--- /dev/null
+++ b/patches/syslinux-3.82/04_new_quiet_flag.diff
@@ -0,0 +1,43 @@
+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/patches/syslinux-3.82/05_variable_kernel_address.diff b/patches/syslinux-3.82/05_variable_kernel_address.diff
new file mode 100644
index 0000000..a494529
--- /dev/null
+++ b/patches/syslinux-3.82/05_variable_kernel_address.diff
@@ -0,0 +1,42 @@
+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/patches/syslinux-3.82/06_comboot_new_api.diff b/patches/syslinux-3.82/06_comboot_new_api.diff
new file mode 100644
index 0000000..2cea657
--- /dev/null
+++ b/patches/syslinux-3.82/06_comboot_new_api.diff
@@ -0,0 +1,189 @@
+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/patches/syslinux-3.82/07_no_append.diff b/patches/syslinux-3.82/07_no_append.diff
new file mode 100644
index 0000000..f06b3b4
--- /dev/null
+++ b/patches/syslinux-3.82/07_no_append.diff
@@ -0,0 +1,45 @@
+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/patches/syslinux-3.82/08_kernel_size.diff b/patches/syslinux-3.82/08_kernel_size.diff
new file mode 100644
index 0000000..e6b868d
--- /dev/null
+++ b/patches/syslinux-3.82/08_kernel_size.diff
@@ -0,0 +1,36 @@
+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/patches/syslinux-3.82/09_doc.diff b/patches/syslinux-3.82/09_doc.diff
new file mode 100644
index 0000000..6f8a8cf
--- /dev/null
+++ b/patches/syslinux-3.82/09_doc.diff
@@ -0,0 +1,43 @@
+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/patches/syslinux-3.82/10_gfxboot_c.diff b/patches/syslinux-3.82/10_gfxboot_c.diff
new file mode 100644
index 0000000..1c0d251
--- /dev/null
+++ b/patches/syslinux-3.82/10_gfxboot_c.diff
@@ -0,0 +1,2099 @@
+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..a59da40
+--- /dev/null
++++ b/modules/gfxboot.c
+@@ -0,0 +1,1040 @@
++/*
++ *
++ * 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);
++ r->eax = (r->eflags & X86_CF) ? 1 : 0;
++ 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 */
++
diff --git a/patches/syslinux-3.83/01_isolinux_mount_chdir.diff b/patches/syslinux-3.83/01_isolinux_mount_chdir.diff
new file mode 100644
index 0000000..3881586
--- /dev/null
+++ b/patches/syslinux-3.83/01_isolinux_mount_chdir.diff
@@ -0,0 +1,175 @@
+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/patches/syslinux-3.83/02_comboot_hooks.diff b/patches/syslinux-3.83/02_comboot_hooks.diff
new file mode 100644
index 0000000..52c9465
--- /dev/null
+++ b/patches/syslinux-3.83/02_comboot_hooks.diff
@@ -0,0 +1,117 @@
+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/patches/syslinux-3.83/03_comboot_ne_real_mode_seg.diff b/patches/syslinux-3.83/03_comboot_ne_real_mode_seg.diff
new file mode 100644
index 0000000..fe319bf
--- /dev/null
+++ b/patches/syslinux-3.83/03_comboot_ne_real_mode_seg.diff
@@ -0,0 +1,44 @@
+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/patches/syslinux-3.83/04_new_quiet_flag.diff b/patches/syslinux-3.83/04_new_quiet_flag.diff
new file mode 100644
index 0000000..b63ed99
--- /dev/null
+++ b/patches/syslinux-3.83/04_new_quiet_flag.diff
@@ -0,0 +1,43 @@
+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/patches/syslinux-3.83/05_variable_kernel_address.diff b/patches/syslinux-3.83/05_variable_kernel_address.diff
new file mode 100644
index 0000000..a494529
--- /dev/null
+++ b/patches/syslinux-3.83/05_variable_kernel_address.diff
@@ -0,0 +1,42 @@
+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/patches/syslinux-3.83/06_comboot_new_api.diff b/patches/syslinux-3.83/06_comboot_new_api.diff
new file mode 100644
index 0000000..2cea657
--- /dev/null
+++ b/patches/syslinux-3.83/06_comboot_new_api.diff
@@ -0,0 +1,189 @@
+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/patches/syslinux-3.83/07_no_append.diff b/patches/syslinux-3.83/07_no_append.diff
new file mode 100644
index 0000000..f06b3b4
--- /dev/null
+++ b/patches/syslinux-3.83/07_no_append.diff
@@ -0,0 +1,45 @@
+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/patches/syslinux-3.83/08_kernel_size.diff b/patches/syslinux-3.83/08_kernel_size.diff
new file mode 100644
index 0000000..e6b868d
--- /dev/null
+++ b/patches/syslinux-3.83/08_kernel_size.diff
@@ -0,0 +1,36 @@
+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/patches/syslinux-3.83/09_doc.diff b/patches/syslinux-3.83/09_doc.diff
new file mode 100644
index 0000000..6f8a8cf
--- /dev/null
+++ b/patches/syslinux-3.83/09_doc.diff
@@ -0,0 +1,43 @@
+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/patches/syslinux-3.83/10_gfxboot_c.diff b/patches/syslinux-3.83/10_gfxboot_c.diff
new file mode 100644
index 0000000..4383445
--- /dev/null
+++ b/patches/syslinux-3.83/10_gfxboot_c.diff
@@ -0,0 +1,2099 @@
+diff --git a/modules/Makefile b/modules/Makefile
+index 77020ea..e6e7905 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 int18.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..a59da40
+--- /dev/null
++++ b/modules/gfxboot.c
+@@ -0,0 +1,1040 @@
++/*
++ *
++ * 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);
++ r->eax = (r->eflags & X86_CF) ? 1 : 0;
++ 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 */
++
diff --git a/patches/syslinux-3.86/01_isolinux_mount_chdir.diff b/patches/syslinux-3.86/01_isolinux_mount_chdir.diff
new file mode 100644
index 0000000..3e11cb3
--- /dev/null
+++ b/patches/syslinux-3.86/01_isolinux_mount_chdir.diff
@@ -0,0 +1,175 @@
+diff --git a/core/isolinux.asm b/core/isolinux.asm
+index 23429bd..54f2e19 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/patches/syslinux-3.86/02_comboot_new_api.diff b/patches/syslinux-3.86/02_comboot_new_api.diff
new file mode 100644
index 0000000..af3c263
--- /dev/null
+++ b/patches/syslinux-3.86/02_comboot_new_api.diff
@@ -0,0 +1,70 @@
+diff --git a/core/comboot.inc b/core/comboot.inc
+index f8a7853..37bd0f2 100644
+--- a/core/comboot.inc
++++ b/core/comboot.inc
+@@ -970,6 +970,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
+@@ -1029,6 +1068,8 @@ 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)
+ int22_count equ ($-int22_table)/2
+
+ APIKeyWait db 0
+@@ -1049,8 +1090,9 @@ 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
+ DOSErrTramp resd 33 ; Error trampolines
++TmpDirName resb FILENAME_MAX
+ ConfigName resb FILENAME_MAX
+ CurrentDirName resb FILENAME_MAX
diff --git a/patches/syslinux-3.86/03_doc.diff b/patches/syslinux-3.86/03_doc.diff
new file mode 100644
index 0000000..ca4d37a
--- /dev/null
+++ b/patches/syslinux-3.86/03_doc.diff
@@ -0,0 +1,25 @@
+diff --git a/doc/comboot.txt b/doc/comboot.txt
+index eb43708..13d18f6 100644
+--- a/doc/comboot.txt
++++ b/doc/comboot.txt
+@@ -949,3 +949,20 @@ 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).
++
diff --git a/patches/syslinux-3.86/04_no_gfxboot_com.diff b/patches/syslinux-3.86/04_no_gfxboot_com.diff
new file mode 100644
index 0000000..032713f
--- /dev/null
+++ b/patches/syslinux-3.86/04_no_gfxboot_com.diff
@@ -0,0 +1,13 @@
+diff --git a/modules/Makefile b/modules/Makefile
+index 77020ea..f318364 100644
+--- a/modules/Makefile
++++ b/modules/Makefile
+@@ -19,7 +19,7 @@ include $(topdir)/MCONFIG.embedded
+
+ INCLUDES = -I$(com32)/include
+
+-BINS = pxechain.com gfxboot.com poweroff.com int18.com
++BINS = pxechain.com poweroff.com int18.com
+
+ all: $(BINS)
+