diff --git a/build.lua b/build.lua index ccd113f1a..1b459fb52 100644 --- a/build.lua +++ b/build.lua @@ -11,6 +11,7 @@ vars.plats = { "linux68k", "linuxppc", "linuxmips", + "msdos86", "osx386", "osxppc", "pc86", diff --git a/plat/msdos86/boot.s b/plat/msdos86/boot.s new file mode 100644 index 000000000..56d424e1c --- /dev/null +++ b/plat/msdos86/boot.s @@ -0,0 +1,145 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +begtext: + ! Make sure we are running under MS-DOS 2 or above. + ! + ! While at it, also remember the actual DOS version, so that we know + ! whether DOS gives us the program's name in the environment + ! segment. (DOS 3+ does; DOS 2.x does not.) + movb ah, 0x30 + int 0x21 + cmpb al, 2 + xchg bx, ax + jc bad_sys + + ! Clear BSS. + mov di, begbss + mov cx, endbss+1 + sub cx, di + shr cx, 1 + xor ax, ax + cld + rep stosw + + ! Get the size of the environment variables plus (if present) the + ! program name. Also count the number of environment variables. + mov es, (0x002C) + xor di, di + ! ax = 0 from above + cwd ! dx = count of env. vars. + mov cx, -1 + scasb ! handle special case of empty env. + jz is_empty_env +size_env: + inc dx + repnz scasb + scasb + jnz size_env +is_empty_env: + cmpb bl, 2 + jz no_argv0 + scasw + repnz scasb +no_argv0: + + ! Copy out the environment variables and (possibly) program name + ! onto the stack. + mov si, di + dec si + and si, -2 + std +copy_env: + test si, si + eseg lodsw + push ax + jnz copy_env + mov cx, sp + + ! Reset DF and es properly. + cld + push ss + pop es + + ! Reserve space for argc and the argv and envp pointers on the + ! stack. These will be passed to __m_a_i_n later. + sub sp, 6 + mov ax, sp + + ! Build up argc, argv[], and envp[]. + push ax ! output buffer for argc, argv, envp + push bx ! MS-DOS version + push cx ! env. string data + push dx ! count of env. vars. + mov ax, 0x0080 + push ax ! raw command line + call __sys_initmain + add sp, 10 + + ! Bail out if something went wrong. + test ax, ax + jnz no_room + + ! argc, argv, and envp are now at the stack top. Now go. + call __m_a_i_n + add sp, 6 + push ax + call _exit + +bad_sys: + mov dx, bad_sys_msg +dos_msg: + movb ah, 9 + int 0x21 + ret + +no_room: + mov dx, no_room_msg + call dos_msg + movb al, -1 + jmp al_exit + + ! Exit. +.define __exit +.extern __exit +.define EXIT +.extern EXIT +__exit: +EXIT: + pop bx + pop ax +al_exit: + movb ah, 0x4c + int 0x21 + +! Define symbols at the beginning of our various segments, so that we can find +! them. (Except .text, which has already been done.) + +.define begtext, begdata, begbss +.sect .data; begdata: +.sect .rom; begrom: +.sect .bss; begbss: + +.sect .rom + +! Some text messages. +bad_sys_msg: .ascii 'Bad DOS$' +no_room_msg: .ascii 'No room$' + +! Some magic data. All EM systems need these. + +.define .trppc, .ignmask, _errno +.comm .trppc, 4 +.comm .ignmask, 4 +.comm _errno, 4 diff --git a/plat/msdos86/build-pkg.lua b/plat/msdos86/build-pkg.lua new file mode 100644 index 000000000..29e141c4c --- /dev/null +++ b/plat/msdos86/build-pkg.lua @@ -0,0 +1,25 @@ +include("plat/build.lua") + +ackfile { + name = "boot", + srcs = { "./boot.s" }, + vars = { plat = "msdos86" } +} + +build_plat_libs { + name = "libs", + arch = "i86", + plat = "msdos86", +} + +installable { + name = "pkg", + map = { + "+tools", + "+libs", + "./include+pkg", + ["$(PLATIND)/msdos86/boot.o"] = "+boot", + ["$(PLATIND)/msdos86/libsys.a"] = "./libsys+lib", + } +} + diff --git a/plat/msdos86/build-tools.lua b/plat/msdos86/build-tools.lua new file mode 100644 index 000000000..53f1e2706 --- /dev/null +++ b/plat/msdos86/build-tools.lua @@ -0,0 +1,21 @@ +include("plat/build.lua") + +build_as { + name = "as", + arch = "i86", +} + +build_ncg { + name = "ncg", + arch = "i86", +} + +return installable { + name = "tools", + map = { + ["$(PLATDEP)/msdos86/as"] = "+as", + ["$(PLATDEP)/msdos86/ncg"] = "+ncg", + ["$(PLATIND)/descr/msdos86"] = "./descr", + "util/opt+pkg", + } +} diff --git a/plat/msdos86/descr b/plat/msdos86/descr new file mode 100644 index 000000000..97c05751b --- /dev/null +++ b/plat/msdos86/descr @@ -0,0 +1,86 @@ +# $Source$ +# $State$ +# $Revision$ + +var w=2 +var wa=2 +var p=2 +var pa=2 +var s=2 +var sa=2 +var l=4 +var la=2 +var f=4 +var fa=2 +var d=8 +var da=2 +var x=8 +var xa=2 +var ARCH=i86 +var PLATFORM=msdos86 +var PLATFORMDIR={EM}/share/ack/{PLATFORM} +var CPP_F=-D__MSDOS__ -D__DOS__ -D_DOS +var ALIGN=-a0:2 -a1:2 -a2:2 -a3:2 +var MACHOPT_F=-m8 +var EGO_PLAT_FLAGS=-M{EM}/share/ack/ego/{ARCH}.descr + +# Override the setting in fe so that files compiled for this platform can see +# the platform-specific headers. + +var C_INCLUDES=-I{PLATFORMDIR}/include -I{EM}/share/ack/include/ansi + +# These will be overridden under the small (separate I/D) memory model. + +var SEPID=-b0:0x100 +var LOD=aslod + +name be + from .m.g + to .s + program {EM}/lib/ack/{PLATFORM}/ncg + args < + stdout + need .e +end +name as + from .s.so + to .o + program {EM}/lib/ack/{PLATFORM}/as + args - -o > < + prep cond +end +name led + from .o.a + to .out + program {EM}/lib/ack/em_led + mapflag -l* LNAME={PLATFORMDIR}/lib* + mapflag -i SEPID=-b1:0 + mapflag -fp FLOATS={EM}/{ILIB}fp + args {ALIGN} {SEPID} \ + ({RTS}:.b=-u _i_main) \ + (.e:{HEAD}={PLATFORMDIR}/boot.o) \ + ({RTS}:.ocm.bas.b={PLATFORMDIR}/c-ansi.o) \ + ({RTS}:.c={PLATFORMDIR}/c-ansi.o) \ + ({RTS}:.mod={PLATFORMDIR}/modula2.o) \ + ({RTS}:.p={PLATFORMDIR}/pascal.o) \ + -o > < \ + (.p:{TAIL}={PLATFORMDIR}/libpascal.a) \ + (.b:{TAIL}={PLATFORMDIR}/libb.a) \ + (.bas:{TAIL}={PLATFORMDIR}/libbasic.a) \ + (.mod:{TAIL}={PLATFORMDIR}/libmodula2.a) \ + (.ocm:{TAIL}={PLATFORMDIR}/liboccam.a) \ + (.ocm.bas.mod.b.c.p:{TAIL}={PLATFORMDIR}/libc.a) \ + {FLOATS?} \ + (.e:{TAIL}={PLATFORMDIR}/libem.a \ + {PLATFORMDIR}/libsys.a \ + {PLATFORMDIR}/libend.a) + linker +end +name cv + from .out + to .exe + mapflag -i LOD=amzlod + program {EM}/bin/{LOD} + args < > + outfile msdos86.exe +end diff --git a/plat/msdos86/libsys/sys_initmain.c b/plat/msdos86/libsys/sys_initmain.c new file mode 100644 index 000000000..11b5a1fb4 --- /dev/null +++ b/plat/msdos86/libsys/sys_initmain.c @@ -0,0 +1,112 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include + +#define OUT_OF_MEMORY (void*)(-1) /* sbrk returns this on failure */ + +struct for_main { + int argc; + char **argv; + char **envp; +}; + +/* + * Marshal the command line and environment variable data provided by + * MS-DOS, into the standard format expected by a C main(...) routine. The + * environment variable data has been copied to the near data segment. + * + * Return zero if everything went well, non-zero otherwise. + */ +int _sys_initmain(char *arg_data, size_t n_env_vars, char *env_data, + unsigned msdos_ver, struct for_main *out) +{ + char **argv, **envp; + char c, *p, **pp; + unsigned char n; + + /* + * Allocate space for argv[] for the maximum possible number of + * arguments. We can shrink this later. + */ + if ((argv = sbrk(65 * sizeof(char *))) == OUT_OF_MEMORY) + return -1; + out->argv = argv; + + /* Sanity-check the original command line. */ + p = arg_data; + n = (unsigned char)*p; + if (n > 0x7E || p[1 + n] != '\r') + return -1; + + /* Initialize argv[0] to an empty string first. */ + *p = 0; + argv[0] = p; + + /* + * Split the command line string into separate arguments. + * TODO: handle double-quoting, backslashes, etc. + */ + pp = argv + 1; + do + { + do + c = *++p; + while (c == ' ' || c == '\t'); + + if (c != '\r') + { + *pp++ = p; + do + c = *++p; + while (c != ' ' && c != '\t' && c != '\r'); + *p = 0; + } + } while (c != '\r'); + + /* Wrap up argv[]. Compute argc. Free up unneeded space. */ + out->argc = pp - argv; + *pp++ = NULL; + if (brk(pp) != 0) + return -1; + + /* Allocate space for envp[]. */ + if ((envp = sbrk((n_env_vars + 1) * sizeof(char *))) == OUT_OF_MEMORY) + return -1; + out->envp = envp; + + /* + * Populate envp[]. The environment data is simply a series of ASCIIZ + * strings, one after another, terminated by an empty string. + */ + pp = envp; + p = env_data; + while (*p) + { + *pp++ = p; + while (*++p); + ++p; + } + *pp = NULL; + + /* + * If there is a program name (MS-DOS version is 3 or above), point + * argv[0] to it. + * + * p points to the zero byte just before the count of strings (a + * shortword) following the environment variables, so advance p by 3 + * bytes to get at the program name. + */ + if ((msdos_ver & 0x00ff) >= 3) + { + p += 3; + argv[0] = p; + } + + /* We are done. */ + return 0; +}