Convert the DOS installer to C like everything else.
authorhpa <hpa>
Wed, 15 Dec 2004 12:34:42 +0000 (12:34 +0000)
committerhpa <hpa>
Wed, 15 Dec 2004 12:34:42 +0000 (12:34 +0000)
19 files changed:
dos/Makefile
dos/atou.c [new file with mode: 0644]
dos/code16.h [new file with mode: 0644]
dos/com16.ld [new file with mode: 0644]
dos/conio.c [new file with mode: 0644]
dos/crt0.S [new file with mode: 0644]
dos/errno.h [new file with mode: 0644]
dos/free.c [new file with mode: 0644]
dos/malloc.c [new file with mode: 0644]
dos/malloc.h [new file with mode: 0644]
dos/mystuff.h [new file with mode: 0644]
dos/perror.c [new file with mode: 0644]
dos/printf.c [new file with mode: 0644]
dos/skipatou.c [new file with mode: 0644]
dos/stdio.h [new file with mode: 0644]
dos/stdlib.h [new file with mode: 0644]
dos/string.h [new file with mode: 0644]
dos/syslinux.asm [deleted file]
dos/syslinux.c [new file with mode: 0644]

index 5319daf..947c1c8 100644 (file)
@@ -1,21 +1,24 @@
-CC      = gcc
+CC      = gcc -m32
+LD       = ld -m elf_i386
+OBJCOPY  = objcopy
 OPTFLAGS = -g -Os
-INCLUDES = -I./ -I../ -I../libfat/
-CFLAGS  = -W -Wall $(OPTFLAGS) $(INCLUDES)
-LDFLAGS         = -s
-NASM     = nasm
+INCLUDES = -include code16.h -I. -I.. -I../libfat
+CFLAGS  = -W -Wall -ffreestanding $(OPTFLAGS) $(INCLUDES)
+LDFLAGS         = -T com16.ld
 
-SRCS     = 
-OBJS    = 
+SRCS     = syslinux.c printf.c conio.c skipatou.c atou.c malloc.c free.c \
+          ../syslxmod.c ../bootsect_bin.c ../ldlinux_bin.c \
+           $(wildcard ../libfat/*.c)
+OBJS    = crt0.o $(patsubst %.c,%.o,$(notdir $(SRCS)))
 
-.SUFFIXES: .c .o .i .s .S .asm .bin .lst .com
+.SUFFIXES: .c .o .i .s .S .elf .com
 
 VPATH = .:..:../libfat
 
-all: syslinux.com
+all: installer
 
 tidy:
-       -rm -f *.o *.i *.s *.a .*.d *.lst
+       -rm -f *.o *.i *.s *.a .*.d *.elf
 
 clean: tidy
        -rm -f syslinux.com
@@ -23,9 +26,24 @@ clean: tidy
 spotless: clean
        -rm -f *~
 
-installer:
+installer: syslinux.com
+
+syslinux.elf: $(OBJS)
+       $(LD) $(LDFLAGS) -o $@ $^
+
+syslinux.com: syslinux.elf
+       $(OBJCOPY) -O binary $< $@
+
+%.o: %.c
+       $(CC) -Wp,-MT,$@,-MD,.$@.d $(CFLAGS) -c -o $@ $<
+%.i: %.c
+       $(CC) $(CFLAGS) -E -o $@ $<
+%.s: %.c
+       $(CC) $(CFLAGS) -S -o $@ $<
+%.s: %.S
+       $(CC) $(CFLAGS) -D__ASSEMBLY__ -S -o $@ $<
+
+-include .*.d
+
 
-syslinux.com: syslinux.asm ../ldlinux.bss ../ldlinux.sys
 
-.asm.com:
-       $(NASM) $(INCLUDES) -f bin -o $@ -l $*.lst $<
diff --git a/dos/atou.c b/dos/atou.c
new file mode 100644 (file)
index 0000000..6d65106
--- /dev/null
@@ -0,0 +1,10 @@
+#include "mystuff.h"
+
+unsigned int atou(const char *s)
+{
+  unsigned int i = 0;
+  while (isdigit(*s))
+    i = i*10 + (*s++ - '0');
+  return i;
+}
+
diff --git a/dos/code16.h b/dos/code16.h
new file mode 100644 (file)
index 0000000..ceb1600
--- /dev/null
@@ -0,0 +1,2 @@
+/* Must be included first of all */
+__asm__ (".code16gcc");
diff --git a/dos/com16.ld b/dos/com16.ld
new file mode 100644 (file)
index 0000000..08a1e95
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Linker script for COM16 binaries
+ */
+
+/* Script for -z combreloc: combine and sort reloc sections */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386",
+             "elf32-i386")
+OUTPUT_ARCH(i386)
+EXTERN(_start)
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x100;
+  PROVIDE (__executable_start = .);
+
+  .init           :
+  {
+    KEEP (*(.init))
+  } =0x90909090
+  .text           :
+  {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+  } =0x90909090
+  .fini           :
+  {
+    KEEP (*(.fini))
+  } =0x90909090
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+
+  /* Ensure the __preinit_array_start label is properly aligned.  We
+     could instead move the label definition inside the section, but
+     the linker would then create the section even if it turns out to
+     be empty, which isn't pretty.  */
+  . = ALIGN(4);
+  PROVIDE (__preinit_array_start = .);
+  .preinit_array     : { *(.preinit_array) }
+  PROVIDE (__preinit_array_end = .);
+  PROVIDE (__init_array_start = .);
+  .init_array     : { *(.init_array) }
+  PROVIDE (__init_array_end = .);
+  PROVIDE (__fini_array_start = .);
+  .fini_array     : { *(.fini_array) }
+  PROVIDE (__fini_array_end = .);
+  PROVIDE (__ctors_start = .);
+  .ctors          :
+  {
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  PROVIDE (__ctors_end = .);
+  PROVIDE (__dtors_start = .);
+  .dtors          :
+  {
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  PROVIDE (__dtors_end = .);
+
+  /* Adjust the address for the data segment.  Avoid mixing code and
+     data within same 128-byte chunk. */
+  . = ALIGN(128);
+
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  _edata = .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.  */
+   . = ALIGN(32 / 8);
+  }
+  . = ALIGN(32 / 8);
+  _end = .;
+  PROVIDE (end = .);
+
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/dos/conio.c b/dos/conio.c
new file mode 100644 (file)
index 0000000..460858f
--- /dev/null
@@ -0,0 +1,41 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2001-2003 H. Peter Anvin - All Rights Reserved
+ *
+ *   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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * conio.c
+ *
+ * Output to the screen
+ */
+
+#include <stdarg.h>
+#include "mystuff.h"
+
+int putchar(int ch)
+{
+  asm("movb $0x02,%%ah ; int $0x21" : : "b" (ch));
+  return ch;
+}
+
+/* Note: doesn't put '\n' like the stdc version does */
+int puts(const char *s)
+{
+  int count = 0;
+
+  while ( *s ) {
+    putchar(*s);
+    count++;
+    s++;
+  }
+
+  return count;
+}
diff --git a/dos/crt0.S b/dos/crt0.S
new file mode 100644 (file)
index 0000000..95a4dc5
--- /dev/null
@@ -0,0 +1,67 @@
+       .code16
+
+       .section ".init","ax"
+       .globl _start
+       .type _start,@function
+_start:
+       # Clear the .bss
+       cld
+       xorl %eax,%eax
+       movw $__bss_start,%di
+       movw $_end+3,%cx
+       subw %di,%cx
+       shrw $2,%cx
+       rep ; stosl
+
+       # Compute argc and argv
+       movzbw 0x80,%cx
+       movl $0x81,%esi
+       movw $argv,%di
+
+       xorl %eax,%eax          # Dummy argv[0]
+       stosl
+
+1:
+       # Skip any space or control character
+       jcxz 2f
+       lodsb
+       decw %cx
+       cmp $0x20,%al
+       ja 3f
+5:
+       movb $0,-1(%si)
+       jmp 1b
+
+3:
+       movl %esi,%eax
+       stosl
+4:
+       # Skip this word
+       jcxz 2f
+       lodsb
+       decw %cx
+       cmp $0x20,%al
+       ja 4b
+       jmp 5b
+2:
+       xorl %eax,%eax          # Final null argv entry
+       stosl   
+
+       # Initialize malloc
+       calll __init_memory_arena
+
+       # Now call main...
+       pushl $argv             # Push argv
+       pushl %eax              # Dummy argc (we dont look at it)
+       calll main
+       
+       pushl %eax
+
+       .globl exit
+exit:
+       popl %eax
+       movb $0x4c,%ah          # Terminate program
+       int $0x21
+
+       .section ".bss","aw"
+argv:  .space 4*128
diff --git a/dos/errno.h b/dos/errno.h
new file mode 100644 (file)
index 0000000..30aa046
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef ERRNO_H
+#define ERRNO_H
+
+int errno;
+void perror(const char *);
+
+#endif /* ERRNO_H */
diff --git a/dos/free.c b/dos/free.c
new file mode 100644 (file)
index 0000000..aa17080
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * free.c
+ *
+ * Very simple linked-list based malloc()/free().
+ */
+
+#include <stdlib.h>
+#include "malloc.h"
+
+static struct free_arena_header *
+__free_block(struct free_arena_header *ah)
+{
+  struct free_arena_header *pah, *nah;
+
+  pah = ah->a.prev;
+  nah = ah->a.next;
+  if ( pah->a.type == ARENA_TYPE_FREE &&
+       (char *)pah+pah->a.size == (char *)ah ) {
+    /* Coalesce into the previous block */
+    pah->a.size += ah->a.size;
+    pah->a.next = nah;
+    nah->a.prev = pah;
+
+#ifdef DEBUG_MALLOC
+    ah->a.type = ARENA_TYPE_DEAD;
+#endif
+
+    ah = pah;
+    pah = ah->a.prev;
+  } else {
+    /* Need to add this block to the free chain */
+    ah->a.type = ARENA_TYPE_FREE;
+
+    ah->next_free = __malloc_head.next_free;
+    ah->prev_free = &__malloc_head;
+    __malloc_head.next_free = ah;
+    ah->next_free->prev_free = ah;
+  }
+
+  /* In either of the previous cases, we might be able to merge
+     with the subsequent block... */
+  if ( nah->a.type == ARENA_TYPE_FREE &&
+       (char *)ah+ah->a.size == (char *)nah ) {
+    ah->a.size += nah->a.size;
+
+    /* Remove the old block from the chains */
+    nah->next_free->prev_free = nah->prev_free;
+    nah->prev_free->next_free = nah->next_free;
+    ah->a.next = nah->a.next;
+    nah->a.next->a.prev = ah;
+
+#ifdef DEBUG_MALLOC
+    nah->a.type = ARENA_TYPE_DEAD;
+#endif
+  }
+
+  /* Return the block that contains the called block */
+  return ah;
+}
+
+void free(void *ptr)
+{
+  struct free_arena_header *ah;
+
+  if ( !ptr )
+    return;
+
+  ah = (struct free_arena_header *)
+    ((struct arena_header *)ptr - 1);
+
+#ifdef DEBUG_MALLOC
+  assert( ah->a.type == ARENA_TYPE_USED );
+#endif
+
+  __free_block(ah);
+
+  /* Here we could insert code to return memory to the system. */
+}
diff --git a/dos/malloc.c b/dos/malloc.c
new file mode 100644 (file)
index 0000000..93a51bb
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * malloc.c
+ *
+ * Very simple linked-list based malloc()/free().
+ */
+
+#include <stdlib.h>
+#include "malloc.h"
+
+struct free_arena_header __malloc_head =
+{
+  {
+    ARENA_TYPE_HEAD,
+    0,
+    &__malloc_head,
+    &__malloc_head,
+  },
+  &__malloc_head,
+  &__malloc_head
+};
+
+/* This is extern so it can be overridden by the user application */
+const size_t __stack_size = 4096;
+
+static inline size_t sp(void)
+{
+  uint16_t sp;
+  asm volatile("movw %%sp,%0" : "=rm" (sp));
+  return sp;
+}
+
+void __init_memory_arena(void)
+{
+  extern char _end[];          /* Symbol created by the linker */
+  struct free_arena_header *fp;
+  size_t start, total_space;
+
+  start = (size_t)ARENA_ALIGN_UP(_end);
+  total_space = sp() - start;
+
+  fp = (struct free_arena_header *)start;
+  fp->a.type = ARENA_TYPE_FREE;
+  fp->a.size = total_space - __stack_size;
+  
+  /* Insert into chains */
+  fp->a.next = fp->a.prev = &__malloc_head;
+  fp->next_free = fp->prev_free = &__malloc_head;
+  __malloc_head.a.next = __malloc_head.a.prev = fp;
+  __malloc_head.next_free = __malloc_head.prev_free = fp;
+}
+
+static void *__malloc_from_block(struct free_arena_header *fp, size_t size)
+{
+  size_t fsize;
+  struct free_arena_header *nfp, *na;
+
+  fsize = fp->a.size;
+  
+  /* We need the 2* to account for the larger requirements of a free block */
+  if ( fsize >= size+2*sizeof(struct arena_header) ) {
+    /* Bigger block than required -- split block */
+    nfp = (struct free_arena_header *)((char *)fp + size);
+    na = fp->a.next;
+
+    nfp->a.type = ARENA_TYPE_FREE;
+    nfp->a.size = fsize-size;
+    fp->a.type  = ARENA_TYPE_USED;
+    fp->a.size  = size;
+
+    /* Insert into all-block chain */
+    nfp->a.prev = fp;
+    nfp->a.next = na;
+    na->a.prev = nfp;
+    fp->a.next = nfp;
+    
+    /* Replace current block on free chain */
+    nfp->next_free = fp->next_free;
+    nfp->prev_free = fp->prev_free;
+    fp->next_free->prev_free = nfp;
+    fp->prev_free->next_free = nfp;
+  } else {
+    /* Allocate the whole block */
+    fp->a.type = ARENA_TYPE_USED;
+
+    /* Remove from free chain */
+    fp->next_free->prev_free = fp->prev_free;
+    fp->prev_free->next_free = fp->next_free;
+  }
+  
+  return (void *)(&fp->a + 1);
+}
+
+void *malloc(size_t size)
+{
+  struct free_arena_header *fp;
+
+  if ( size == 0 )
+    return NULL;
+
+  /* Add the obligatory arena header, and round up */
+  size = (size+2*sizeof(struct arena_header)-1) & ARENA_SIZE_MASK;
+
+  for ( fp = __malloc_head.next_free ; fp->a.type != ARENA_TYPE_HEAD ;
+       fp = fp->next_free ) {
+    if ( fp->a.size >= size ) {
+      /* Found fit -- allocate out of this block */
+      return __malloc_from_block(fp, size);
+    }
+  }
+
+  /* Nothing found... need to request a block from the kernel */
+  return NULL;                 /* No kernel to get stuff from */
+}
diff --git a/dos/malloc.h b/dos/malloc.h
new file mode 100644 (file)
index 0000000..57b7652
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * malloc.h
+ *
+ * Internals for the memory allocator
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * This is the minimum chunk size we will ask the kernel for; this should
+ * be a multiple of the page size on all architectures.
+ */
+#define MALLOC_CHUNK_SIZE      65536
+#define MALLOC_CHUNK_MASK       (MALLOC_CHUNK_SIZE-1)
+
+/*
+ * This structure should be a power of two.  This becomes the
+ * alignment unit.
+ */
+struct free_arena_header;
+
+struct arena_header {
+  size_t type;
+  size_t size;                 /* Also gives the location of the next entry */
+  struct free_arena_header *next, *prev;
+};
+
+#ifdef DEBUG_MALLOC
+#define ARENA_TYPE_USED 0x64e69c70
+#define ARENA_TYPE_FREE 0x012d610a
+#define ARENA_TYPE_HEAD 0x971676b5
+#define ARENA_TYPE_DEAD 0xeeeeeeee
+#else
+#define ARENA_TYPE_USED 0
+#define ARENA_TYPE_FREE 1
+#define ARENA_TYPE_HEAD 2
+#endif
+
+#define ARENA_SIZE_MASK (~(sizeof(struct arena_header)-1))
+
+#define ARENA_ALIGN_UP(p)      ((char *)(((uintptr_t)(p) + ARENA_SIZE_MASK) & ARENA_SIZE_MASK))
+#define ARENA_ALIGN_DOWN(p)    ((char *)((uintptr_t)(p) & ARENA_SIZE_MASK))
+
+/*
+ * This structure should be no more than twice the size of the
+ * previous structure.
+ */
+struct free_arena_header {
+  struct arena_header a;
+  struct free_arena_header *next_free, *prev_free;
+};
+
+extern struct free_arena_header __malloc_head;
diff --git a/dos/mystuff.h b/dos/mystuff.h
new file mode 100644 (file)
index 0000000..83f072f
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef MYSTUFF_H
+#define MYSTUFF_H
+
+#define NULL ((void *)0)
+
+unsigned int skip_atou(const char **s);
+unsigned int atou(const char *s);
+
+static inline int
+isdigit(int ch)
+{
+  return (ch >= '0') && (ch <= '9');
+}
+
+#endif /* MYSTUFF_H */
diff --git a/dos/perror.c b/dos/perror.c
new file mode 100644 (file)
index 0000000..ff8d5f2
--- /dev/null
@@ -0,0 +1,8 @@
+#include <stdio.h>
+#include <errno.h>
+
+void perror(const char *msg)
+{
+  printf("%s: error %s\n", msg, errno);
+}
+
diff --git a/dos/printf.c b/dos/printf.c
new file mode 100644 (file)
index 0000000..efd63cf
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Oh, it's a waste of space, but oh-so-yummy for debugging.  It's just
+ * initialization code anyway, so it doesn't take up space when we're
+ * actually running.  This version of printf() does not include 64-bit
+ * support.  "Live with it."
+ *
+ * Most of this code was shamelessly snarfed from the Linux kernel, then
+ * modified.
+ *
+ * FIX THIS: Replace printf() implementation with BSD/MIT-licensed one
+ * from klibc
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "mystuff.h"
+
+static int strnlen(const char *s, int maxlen)
+{
+  const char *es = s;
+  while ( *es && maxlen ) {
+    es++; maxlen--;
+  }
+
+  return (es-s);
+}
+
+#define ZEROPAD        1               /* pad with zero */
+#define SIGN   2               /* unsigned/signed long */
+#define PLUS   4               /* show plus */
+#define SPACE  8               /* space if plus */
+#define LEFT   16              /* left justified */
+#define SPECIAL        32              /* 0x */
+#define LARGE  64              /* use 'ABCDEF' instead of 'abcdef' */
+
+#define do_div(n,base) ({ \
+int __res; \
+__res = ((unsigned long) n) % (unsigned) base; \
+n = ((unsigned long) n) / (unsigned) base; \
+__res; })
+
+static char * number(char * str, long num, int base, int size, int precision
+       ,int type)
+{
+  char c,sign,tmp[66];
+  const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
+  int i;
+  
+  if (type & LARGE)
+    digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  if (type & LEFT)
+    type &= ~ZEROPAD;
+  if (base < 2 || base > 36)
+    return 0;
+  c = (type & ZEROPAD) ? '0' : ' ';
+  sign = 0;
+  if (type & SIGN) {
+    if (num < 0) {
+      sign = '-';
+      num = -num;
+      size--;
+    } else if (type & PLUS) {
+      sign = '+';
+      size--;
+    } else if (type & SPACE) {
+      sign = ' ';
+      size--;
+    }
+  }
+  if (type & SPECIAL) {
+    if (base == 16)
+      size -= 2;
+    else if (base == 8)
+      size--;
+  }
+  i = 0;
+  if (num == 0)
+    tmp[i++]='0';
+  else while (num != 0)
+    tmp[i++] = digits[do_div(num,base)];
+  if (i > precision)
+    precision = i;
+  size -= precision;
+  if (!(type&(ZEROPAD+LEFT)))
+    while(size-->0)
+      *str++ = ' ';
+  if (sign)
+    *str++ = sign;
+  if (type & SPECIAL) {
+    if (base==8)
+      *str++ = '0';
+    else if (base==16) {
+      *str++ = '0';
+      *str++ = digits[33];
+    }
+  }
+  if (!(type & LEFT))
+    while (size-- > 0)
+      *str++ = c;
+  while (i < precision--)
+    *str++ = '0';
+  while (i-- > 0)
+    *str++ = tmp[i];
+  while (size-- > 0)
+    *str++ = ' ';
+  return str;
+}
+
+/* Forward decl. needed for IP address printing stuff... */
+int sprintf(char * buf, const char *fmt, ...);
+
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+  int len;
+  unsigned long num;
+  int i, base;
+  char * str;
+  const char *s;
+  
+  int flags;           /* flags to number() */
+  
+  int field_width;     /* width of output field */
+  int precision;               /* min. # of digits for integers; max
+                                  number of chars for from string */
+  int qualifier;               /* 'h', 'l', or 'L' for integer fields */
+  
+  for (str=buf ; *fmt ; ++fmt) {
+    if (*fmt != '%') {
+      *str++ = *fmt;
+      continue;
+    }
+    
+    /* process flags */
+    flags = 0;
+  repeat:
+    ++fmt;             /* this also skips first '%' */
+    switch (*fmt) {
+    case '-': flags |= LEFT; goto repeat;
+    case '+': flags |= PLUS; goto repeat;
+    case ' ': flags |= SPACE; goto repeat;
+    case '#': flags |= SPECIAL; goto repeat;
+    case '0': flags |= ZEROPAD; goto repeat;
+    }
+    
+    /* get field width */
+    field_width = -1;
+    if (isdigit(*fmt))
+      field_width = skip_atou(&fmt);
+    else if (*fmt == '*') {
+      ++fmt;
+      /* it's the next argument */
+      field_width = va_arg(args, int);
+      if (field_width < 0) {
+       field_width = -field_width;
+       flags |= LEFT;
+      }
+    }
+    
+    /* get the precision */
+    precision = -1;
+    if (*fmt == '.') {
+      ++fmt;   
+      if (isdigit(*fmt))
+       precision = skip_atou(&fmt);
+      else if (*fmt == '*') {
+       ++fmt;
+       /* it's the next argument */
+       precision = va_arg(args, int);
+      }
+      if (precision < 0)
+       precision = 0;
+    }
+    
+    /* get the conversion qualifier */
+    qualifier = -1;
+    if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
+      qualifier = *fmt;
+      ++fmt;
+    }
+    
+    /* default base */
+    base = 10;
+    
+    switch (*fmt) {
+    case 'c':
+      if (!(flags & LEFT))
+       while (--field_width > 0)
+         *str++ = ' ';
+      *str++ = (unsigned char) va_arg(args, int);
+      while (--field_width > 0)
+       *str++ = ' ';
+      continue;
+      
+    case 's':
+      s = va_arg(args, char *);
+      len = strnlen(s, precision);
+      
+      if (!(flags & LEFT))
+       while (len < field_width--)
+         *str++ = ' ';
+      for (i = 0; i < len; ++i)
+       *str++ = *s++;
+      while (len < field_width--)
+       *str++ = ' ';
+      continue;
+      
+    case 'p':
+      if (field_width == -1) {
+       field_width = 2*sizeof(void *);
+       flags |= ZEROPAD;
+      }
+      str = number(str,
+                  (unsigned long) va_arg(args, void *), 16,
+                  field_width, precision, flags);
+      continue;
+      
+      
+    case 'n':
+      if (qualifier == 'l') {
+       long * ip = va_arg(args, long *);
+       *ip = (str - buf);
+      } else {
+       int * ip = va_arg(args, int *);
+       *ip = (str - buf);
+      }
+      continue;
+      
+    case '%':
+      *str++ = '%';
+      continue;
+      
+      /* integer number formats - set up the flags and "break" */
+    case 'o':
+      base = 8;
+      break;
+      
+    case 'X':
+      flags |= LARGE;
+    case 'x':
+      base = 16;
+      break;
+      
+    case 'd':
+    case 'i':
+      flags |= SIGN;
+    case 'u':
+      break;
+      
+    default:
+      *str++ = '%';
+      if (*fmt)
+       *str++ = *fmt;
+      else
+       --fmt;
+      continue;
+    }
+    if (qualifier == 'l')
+      num = va_arg(args, unsigned long);
+    else if (qualifier == 'h') {
+      num = (unsigned short) va_arg(args, int);
+      if (flags & SIGN)
+       num = (short) num;
+    } else if (flags & SIGN)
+      num = va_arg(args, int);
+    else
+      num = va_arg(args, unsigned int);
+    str = number(str, num, base, field_width, precision, flags);
+  }
+  *str = '\0';
+  return str-buf;
+}
+
+int sprintf(char * buf, const char *fmt, ...)
+{
+  va_list args;
+  int i;
+  
+  va_start(args, fmt);
+  i=vsprintf(buf,fmt,args);
+  va_end(args);
+  return i;
+}
+
+int printf(const char *fmt, ...)
+{
+  char printf_buf[1024];
+  va_list args;
+  int printed;
+
+  va_start(args, fmt);
+  printed = vsprintf(printf_buf, fmt, args);
+  va_end(args);
+
+  puts(printf_buf);
+
+  return printed;
+}
diff --git a/dos/skipatou.c b/dos/skipatou.c
new file mode 100644 (file)
index 0000000..4c663f6
--- /dev/null
@@ -0,0 +1,10 @@
+#include "mystuff.h"
+
+unsigned int skip_atou(const char **s)
+{
+  int i=0;
+  
+  while (isdigit(**s))
+    i = i*10 + *((*s)++) - '0';
+  return i;
+}
diff --git a/dos/stdio.h b/dos/stdio.h
new file mode 100644 (file)
index 0000000..ffc9b3f
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef STDIO_H
+#define STDIO_H
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+typedef unsigned int off_t;
+
+int putchar(int);
+int puts(const char *);
+int sprintf(char * buf, const char *fmt, ...);
+int vsprintf(char *buf, const char *fmt, va_list args);
+int printf(const char *fmt, ...);
+
+#define stdin  0
+#define stdout 1
+#define stderr 2
+
+#define fprintf(x, y, ...) printf(y, ## __VA_ARGS__)
+
+#endif /* STDIO_H */
+
diff --git a/dos/stdlib.h b/dos/stdlib.h
new file mode 100644 (file)
index 0000000..45b76a5
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef STDLIB_H
+#define STDLIB_H
+
+typedef int ssize_t;
+typedef unsigned int size_t;
+
+void __attribute__((noreturn)) exit(int);
+
+void *malloc(size_t);
+void free(void *);
+
+#endif
diff --git a/dos/string.h b/dos/string.h
new file mode 100644 (file)
index 0000000..cee8e2e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * string.h
+ */
+
+#ifndef _STRING_H
+#define _STRING_H
+
+/* Standard routines */
+static inline void *memcpy(void *__d, const void *__s, unsigned int __n)
+{
+  asm volatile("cld ; rep ; movsb"
+              : "+D" (__d), "+S" (__s), "+c" (__n));
+  return __d;
+}
+
+static inline void *memset(void *__d, int __c, unsigned int __n)
+{
+  asm volatile("cld ; rep ; stosb"
+              : "+D" (__d), "+a" (__c), "+c" (__n));
+  return __d;
+}
+
+#define strcpy(a,b)   __builtin_strcpy(a,b)
+#define strlen(a)     __builtin_strlen(a)
+
+/* This only returns true or false */
+static inline int memcmp(const void *__m1, const void *__m2, unsigned int __n)
+{
+  _Bool rv;
+  asm volatile("cld ; repe ; cmpsb ; setne %0"
+              : "=abd" (rv), "+D" (__m1), "+S" (__m2), "+c" (__n));
+  return rv;
+}
+
+#endif /* _STRING_H */
diff --git a/dos/syslinux.asm b/dos/syslinux.asm
deleted file mode 100644 (file)
index bb2df31..0000000
+++ /dev/null
@@ -1,471 +0,0 @@
-; -*- fundamental -*- (asm-mode sucks)
-; $Id$
-; -----------------------------------------------------------------------
-;   
-;   Copyright 1998-2004 H. Peter Anvin - All Rights Reserved
-;
-;   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.
-;
-; -----------------------------------------------------------------------
-
-;
-; syslinux.asm
-;
-;      DOS installer for SYSLINUX
-;
-
-                       absolute 0
-pspInt20:              resw 1
-pspNextParagraph:      resw 1
-                       resb 1          ; reserved
-pspDispatcher:         resb 5
-pspTerminateVector:    resd 1
-pspControlCVector:     resd 1
-pspCritErrorVector:    resd 1
-                       resw 11         ; reserved
-pspEnvironment:                resw 1
-                       resw 23         ; reserved
-pspFCB_1:              resb 16
-pspFCB_2:              resb 16
-                       resd 1          ; reserved
-pspCommandLen:         resb 1
-pspCommandArg:         resb 127
-
-               section .text
-               org 0100h
-_start:                
-               mov ax,3000h                    ; Get DOS version
-               int 21h
-               xchg al,ah
-               mov [DOSVersion],ax
-               cmp ax,0314h                    ; DOS 3.20 minimum
-               jae dosver_ok
-               mov dx,msg_ancient_err
-               jmp die
-
-               section .bss
-               alignb 2
-DOSVersion:    resw 1
-
-               section .text
-;
-; Scan command line for a drive letter followed by a colon
-;
-dosver_ok:
-               xor cx,cx
-               mov si,pspCommandArg
-               mov cl,[pspCommandLen]
-               
-cmdscan1:      jcxz bad_usage                  ; End of command line?
-               lodsb                           ; Load character
-               dec cx
-               cmp al,' '                      ; White space
-               jbe cmdscan1
-               cmp al,'-'
-               je scan_option
-               or al,020h                      ; -> lower case
-               cmp al,'a'                      ; Check for letter
-               jb bad_usage
-               cmp al,'z'
-               ja bad_usage
-               sub al,'a'                      ; Convert to zero-based index
-               mov [DriveNo],al                ; Save away drive index
-
-               section .bss
-DriveNo:       resb 1
-
-               section .text
-;
-; Got the leading letter, now the next character must be a colon
-;
-got_letter:    jcxz bad_usage
-               lodsb
-               dec cx
-               cmp al,':'
-               jne bad_usage
-;
-; Got the colon; the rest better be whitespace
-;
-got_colon:     jcxz got_cmdline
-               lodsb
-               dec cx
-               cmp al,' '
-               jbe got_colon
-;
-; We end up here if the command line doesn't parse
-;
-bad_usage:     mov dx,msg_unfair
-               jmp die
-
-               section .data
-msg_unfair:    db 'Usage: syslinux [-s] <drive>:', 0Dh, 0Ah, '$'
-
-               section .text
-;
-; Scan for options after a - sign.  The only recognized option right now
-; is -s.
-;
-scan_option:   jcxz bad_usage
-               lodsb
-               dec cx
-               cmp al,' '
-               jbe cmdscan1
-               or al,20h
-               cmp al,'s'
-               jne bad_usage
-               push si                 ; make_stupid doesn't save these
-               push cx
-               call make_stupid        ; Enable stupid boot sector
-               pop cx
-               pop si
-               jmp short scan_option
-
-;
-; Parsed the command line OK.  Check that the drive parameters are acceptable
-;
-               struc DPB
-dpbDrive:      resb 1
-dpbUnit:       resb 1
-dpbSectorSize: resw 1
-dpbClusterMask:        resb 1
-dpbClusterShift: resb 1
-dpbFirstFAT:   resw 1
-dpbFATCount:   resb 1
-dpbRootEntries:        resw 1
-dpbFirstSector:        resw 1
-dpbMaxCluster: resw 1
-dpbFATSize:    resw 1
-dpbDirSector:  resw 1
-dpbDriverAddr: resd 1
-dpbMedia:      resb 1
-dpbFirstAccess:        resb 1
-dpbNextDPB:    resd 1
-dpbNextFree:   resw 1
-dpbFreeCnt:    resw 1
-               endstruc
-
-got_cmdline:
-               mov dl,[DriveNo]
-               inc dl                          ; 1-based
-               mov ah,32h
-               int 21h                         ; Get Drive Parameter Block
-               
-               and al,al
-               jnz filesystem_error
-
-               cmp word [bx+dpbSectorSize],512 ; Sector size = 512 required
-               jne sectorsize_error
-
-               cmp byte [bx+dpbClusterShift],5 ; Max size = 16K = 2^5 sectors
-               jna drive_ok
-
-hugeclust_error:
-               mov dx,msg_hugeclust_err
-               jmp die
-filesystem_error:
-               mov dx,msg_filesystem_err
-               jmp doserr
-sectorsize_error:
-               mov dx,msg_sectorsize_err
-               jmp die
-
-drive_ok:
-               push cs
-               pop ds
-
-;
-; Writing LDLINUX.SYS
-;
-               section .data
-ldlinux_sys_str:
-               db 'A:\LDLINUX.SYS', 0
-               section .text
-
-write_file:
-               ; 0. Set the correct filename
-
-               mov al,[DriveNo]
-               add byte [ldlinux_sys_str],al
-
-               ; 1. If the file exists, strip its attributes and delete
-
-               xor cx,cx                       ; Clear attributes
-               mov dx,ldlinux_sys_str
-               mov ax,4301h                    ; Set file attributes
-               int 21h
-
-               mov dx,ldlinux_sys_str
-               mov ah,41h                      ; Delete file
-               int 21h
-
-               ; 2. Create LDLINUX.SYS and write data to it
-
-               mov dx,ldlinux_sys_str
-               xor cx,cx                       ; Normal file
-               mov ah,3Ch                      ; Create file
-               int 21h
-               jc .file_write_error
-               mov [FileHandle],ax
-
-               mov bx,ax
-               mov cx,ldlinux_size
-               mov dx,LDLinuxSYS
-               mov ah,40h                      ; Write data
-               int 21h
-               jc .file_write_error
-               cmp ax,ldlinux_size
-               je .no_file_write_error
-.file_write_error:
-               mov dx, msg_fwrite_err
-               jmp doserr
-.no_file_write_error:
-
-               mov bx,[FileHandle]
-               mov ah,3Eh                      ; Close file
-               int 21h
-
-               section .bss
-FileHandle:    resw 1
-
-               section .text
-
-               ; 3. Set the readonly flag on LDLINUX.SYS
-
-               mov dx,ldlinux_sys_str
-               mov cx,1                        ; Read only
-               mov ax,4301h                    ; Set attributes
-               int 21h
-
-;
-; Now, if we're on a recent Windows system we need to lock the device.
-; This call should have no effect on plain DOS.
-;
-lock_drive:
-               cmp word [DOSVersion], 0700h    ; Win9x/NT?
-               jb .plain_dos                   ; Plain DOS -> no locking
-
-               mov ax,440Dh                    ; Generic IOCTL
-               mov bl,[DriveNo]
-               inc bl                          ; 1-based
-               mov bh,1                        ; Lock level 1
-               mov cx,084Ah                    ; Lock logical volume
-               mov dx,01h                      ; Allow write mappings/allow new mappings
-               pusha
-               int 21h
-               jc .disk_lock_error_nocleanup
-               popa
-
-               xor dx,dx
-               inc bh                          ; Lock level 2
-               pusha
-               int 21h
-               jc .disk_lock_error
-               popa
-
-               inc bh                          ; Lock level 3
-               pusha
-               int 21h
-               jnc .done
-
-.disk_lock_error:
-               xor cx,cx
-               mov cl,bh
-               dec cx
-.lock_cleanup:
-               push cx
-               mov ax, 440Dh
-               mov bl,[DriveNo]
-               inc bl
-               mov cx,086Ah
-               int 21h
-               pop cx
-               loop .lock_cleanup
-
-.disk_lock_error_nocleanup:
-               popa
-               mov dx, msg_lock_err
-               jmp doserr
-
-.done:
-               popa
-
-.plain_dos:    ; Plain DOS -> no locking
-
-;
-; Now read the old boot sector and copy the superblock.
-;
-               section .data
-               align 4, db 0
-DISKIO         equ $
-diStartSector: dd 0                            ; Absolute sector 0
-diSectors:     dw 1                            ; One sector
-diBuffer:      dw SectorBuffer                 ; Buffer offset
-               dw 0                            ; Buffer segment
-
-               section .text
-read_bootsect:
-               mov ax,cs                       ; Set DS <- CS
-               mov ds,ax
-
-               cmp word [DOSVersion],0400h     ; DOS 4.00 has a new interface
-               jae .new
-.old:
-               mov bx,SectorBuffer
-               mov cx,1                        ; One sector
-               jmp short .common
-.new:
-               mov bx,DISKIO
-               mov [bx+8],ax                   ; Buffer segment
-               mov cx,-1
-.common:
-               xor dx,dx                       ; Absolute sector 0
-               mov al,[DriveNo]
-               int 25h                         ; DOS absolute disk read
-               pop ax                          ; Remove flags from stack
-               jc disk_read_error
-
-               mov si,SectorBuffer+3           ; Offset of superblock
-               mov di,BootSector+3
-               mov cx,59                       ; Superblock = 59 bytes
-               rep movsb                       ; Copy the superblock
-               jmp short write_bootsect
-disk_read_error:
-               mov dx,msg_read_err
-               jmp doserr
-
-;
-; Writing boot sector
-;
-write_bootsect:
-               cmp word [DOSVersion],0400h     ; DOS 4.00 has a new interface
-               jae .new
-.old:
-               mov bx,BootSector
-               mov cx,1                        ; One sector
-               jmp short .common
-.new:
-               mov bx,DISKIO
-               mov word [bx+6],BootSector
-               mov cx,-1
-.common:
-               xor dx,dx                       ; Absolute sector 0
-               mov al,[DriveNo]
-               int 26h                         ; DOS absolute disk write
-               pop ax                          ; Remove flags from stack
-               jc disk_write_error
-
-;
-; Unlock the disk if we had to lock it
-;
-unlock_disk:
-               cmp word [DOSVersion], 0700h
-               jb .plain_dos
-
-               mov cx, 3                       ; Need to release lock 3 times
-.loop:
-               push cx
-               mov ax,440Dh                    ; Generic IOCTL
-               mov bl,[DriveNo]
-               inc bl                          ; 1-based drive number
-               mov cx,086Ah                    ; Unlock logical drive
-               int 21h
-               pop cx
-               loop .loop
-
-.plain_dos:    ; Plain DOS -> no locking
-
-all_done:      mov ax,4C00h                    ; Exit good status
-               int 21h
-;
-; Error routine jump
-;
-disk_write_error:
-               mov dx,msg_write_err
-
-doserr:
-               push cs
-               pop ds
-               push dx                         ; Error message
-               push ax                         ; Error code
-               mov dx, msg_error_sp
-               mov ah,09h
-               int 21h
-               pop ax
-               
-               mov cx,4
-               mov bx,hexdigits
-               mov si,ax
-.digit:
-               rol si,1
-               rol si,1
-               rol si,1
-               rol si,1
-               mov ax,si
-               and al,0Fh
-               xlatb
-               mov ah,02h                      ; Display character
-               mov dl,al
-               int 21h
-               loop .digit
-
-               mov dx,msg_colon
-               mov ah,09h
-               int 21h
-
-               jmp short die_common
-
-               section .data
-hexdigits:     db '0123456789ABCDEF'
-
-               section .text
-die:
-               push cs
-               pop ds
-               push dx
-               mov dx, msg_error
-               mov ah,09h
-               int 21h
-
-die_common:
-               pop dx                          ; Error message
-
-               mov ah,09h                      ; Write string
-               int 21h
-
-               mov ax,4C01h                    ; Exit error status
-               int 21h
-
-;
-; Patch the code to make it "stupid"
-;
-make_stupid:
-               ; Only access one sector at a time
-               mov word [BootSector+0x1FC],1
-               ret
-
-                       section .data
-msg_error_sp:          db 'ERROR $'
-msg_colon:             db ': $'
-msg_error:             db 'ERROR: $'
-msg_ancient_err:       db 'DOS version 3.20 or later required', 0Dh, 0Ah, '$'
-msg_filesystem_err:    db 'Filesystem not found on disk', 0Dh, 0Ah, '$'
-msg_sectorsize_err:    db 'Sector sizes other than 512 bytes not supported', 0Dh, 0Ah, '$'
-msg_hugeclust_err:     db 'Clusters larger than 16K not supported', 0Dh, 0Ah, '$'
-msg_read_err:          db 'Boot sector read failed', 0Dh, 0Ah, '$'
-msg_write_err:         db 'Boot sector write failed', 0Dh, 0Ah, '$'
-msg_fwrite_err:                db 'LDLINUX.SYS write failed', 0Dh, 0Ah, '$'
-msg_lock_err:          db 'Unable to lock drive for exclusive access', 0Dh, 0Ah, '$'
-
-               section .data
-               align 16, db 0
-BootSector:    incbin "ldlinux.bss"
-LDLinuxSYS:    incbin "ldlinux.sys"
-ldlinux_size:  equ $-LDLinuxSYS
-
-               section .bss
-               alignb 16
-SectorBuffer:  resb 512
diff --git a/dos/syslinux.c b/dos/syslinux.c
new file mode 100644 (file)
index 0000000..051912b
--- /dev/null
@@ -0,0 +1,250 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 1998-2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * syslinux.c - Linux installer program for SYSLINUX
+ *
+ * Hacked up for DOS.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "mystuff.h"
+
+#include "syslinux.h"
+#include "libfat.h"
+
+const char *program = "syslinux"; /* Name of program */
+
+void __attribute__((noreturn)) usage(void)
+{
+  fprintf(stderr, "Usage: %s [-sf] drive:\n", program);
+  exit(1);
+}
+
+void __attribute__((noreturn)) die(const char *msg)
+{
+  fprintf(stderr, "%s: %s\n", program, msg);
+  exit(1);
+}
+
+/*
+ * read/write wrapper functions
+ */
+int open(const char *filename, int mode)
+{
+  uint16_t rv;
+  uint8_t err;
+
+  rv = 0x3D00 | mode;
+  asm volatile("int $0x21 ; setc %0"
+              : "=rm" (err), "+a" (rv)
+              : "d" (filename));
+  if ( err )
+    die("cannot open ldlinux.sys");
+
+  return rv;
+}
+
+void close(int fd)
+{
+  uint16_t rv = 0x3E00;
+
+  asm volatile("int $0x21"
+              : "+a" (rv)
+              : "b" (fd));
+
+  /* The only error MS-DOS returns for close is EBADF,
+     and we really don't care... */
+}
+
+ssize_t write_file(int fd, const void *buf, size_t count)
+{
+  uint16_t rv;
+  ssize_t done = 0;
+  uint8_t err;
+
+  while ( count ) {
+    rv = 0x4000;
+    asm volatile("int $0x21 ; setc %0"
+                : "=rm" (err), "+a" (rv)
+                : "b" (fd), "c" (count), "d" (buf));
+    if ( err || rv == 0 )
+      die("file write error");
+
+    done += rv;
+    count -= rv;
+  }
+
+  return done;
+}
+
+void write_device(int drive, const void *buf, size_t nsecs, unsigned int sector)
+{
+  uint8_t err;
+
+  asm volatile("int $0x26 ; setc %0 ; popfw"
+              : "=rm" (err)
+              : "a" (drive), "b" (buf), "c" (nsecs), "d" (sector));
+
+  if ( err )
+    die("sector write error");
+}
+
+void read_device(int drive, const void *buf, size_t nsecs, unsigned int sector)
+{
+  uint8_t err;
+
+  asm volatile("int $0x25 ; setc %0 ; popfw"
+              : "=rm" (err)
+              : "a" (drive), "b" (buf), "c" (nsecs), "d" (sector));
+
+  if ( err )
+    die("sector write error");
+}  
+
+void set_attributes(const char *file, int attributes)
+{
+  uint8_t err;
+  uint16_t rv = 0x4301;
+
+  asm volatile("int $0x21 ; setc %0"
+              : "=rm" (err), "+a" (rv)
+              : "c" (attributes), "d" (file));
+
+  if ( err )
+    die("set attribute error");
+}
+
+/*
+ * Version of the read_device function suitable for libfat
+ */
+int libfat_xpread(void *pp, void *buf, size_t secsize, libfat_sector_t sector)
+{
+  read_device((int)pp, buf, 1, sector);
+  return secsize;
+}
+
+int main(int argc, char *argv[])
+{
+  static unsigned char sectbuf[512];
+  int dev_fd, fd;
+  static char ldlinux_name[] = "@:LDLINUX.SYS";
+  char **argp, *opt;
+  int force = 0;               /* -f (force) option */
+  struct libfat_filesystem *fs;
+  libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
+  int32_t ldlinux_cluster;
+  int nsectors;
+  char *device = NULL;
+
+  (void)argc;                  /* Unused */
+
+  for ( argp = argv+1 ; *argp ; argp++ ) {
+    if ( **argp == '-' ) {
+      opt = *argp + 1;
+      if ( !*opt )
+       usage();
+
+      while ( *opt ) {
+       if ( *opt == 's' ) {
+         syslinux_make_stupid();       /* Use "safe, slow and stupid" code */
+       } else if ( *opt == 'f' ) {
+         force = 1;            /* Force install */
+       } else {
+         usage();
+       }
+       opt++;
+      }
+    } else {
+      if ( device )
+       usage();
+      device = *argp;
+    }
+  }
+
+  if ( !device )
+    usage();
+
+  /*
+   * Figure out which drive we're talking to
+   */
+  dev_fd = device[0] & 0x1F;
+  if ( !(device[0] & 0x40) || device[1] != ':' || device[2] )
+    usage();
+
+  read_device(dev_fd, sectbuf, 1, 0);
+
+  /*
+   * Check to see that what we got was indeed an MS-DOS boot sector/superblock
+   */
+  if(!syslinux_check_bootsect(sectbuf,device)) {
+    exit(1);
+  }
+  
+  ldlinux_name[0] = dev_fd | 0x40;
+
+  set_attributes(ldlinux_name, 0);
+  fd = open(ldlinux_name, 2);  /* Open for read/write access */
+  write_file(fd, syslinux_ldlinux, syslinux_ldlinux_len);
+  close(fd);
+  set_attributes(ldlinux_name, 0x27); /* ARCHIVE SYSTEM HIDDEN READONLY */
+
+  /*
+   * Now, use libfat to create a block map.  This probably
+   * should be changed to use ioctl(...,FIBMAP,...) since
+   * this is supposed to be a simple, privileged version
+   * of the installer.
+   */
+  fs = libfat_open(libfat_xpread, (void *)dev_fd);
+  ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
+  secp = sectors;
+  nsectors = 0;
+  s = libfat_clustertosector(fs, ldlinux_cluster);
+  while ( s && nsectors < 65 ) {
+    *secp++ = s;
+    nsectors++;
+    s = libfat_nextsector(fs, s);
+  }
+  libfat_close(fs);
+
+  /*
+   * Patch ldlinux.sys and the boot sector
+   */
+  syslinux_patch(sectors, nsectors);
+
+  /*
+   * Write the now-patched first sector of ldlinux.sys
+   */
+  write_device(dev_fd, syslinux_ldlinux, 1, sectors[0]);
+
+  /*
+   * To finish up, write the boot sector
+   */
+
+  /* Read the superblock again since it might have changed while mounted */
+  read_device(dev_fd, sectbuf, 1, 0);
+
+  /* Copy the syslinux code into the boot sector */
+  syslinux_make_bootsect(sectbuf);
+
+  /* Write new boot sector */
+  write_device(dev_fd, sectbuf, 1, 0);
+
+  /* Done! */
+
+  return 0;
+}
+