From 4c678ca21000f575095bbd0cc475c091dd709fc5 Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Tue, 23 Mar 2021 16:31:11 +0000 Subject: [PATCH 1/8] Adding MS-DOS .com program support (msdos86). Still incomplete. --- build.lua | 1 + plat/msdos86/boot.s | 145 +++++++++++++++++++++++++++++ plat/msdos86/build-pkg.lua | 25 +++++ plat/msdos86/build-tools.lua | 21 +++++ plat/msdos86/descr | 86 +++++++++++++++++ plat/msdos86/libsys/sys_initmain.c | 112 ++++++++++++++++++++++ 6 files changed, 390 insertions(+) create mode 100644 plat/msdos86/boot.s create mode 100644 plat/msdos86/build-pkg.lua create mode 100644 plat/msdos86/build-tools.lua create mode 100644 plat/msdos86/descr create mode 100644 plat/msdos86/libsys/sys_initmain.c 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; +} From aaf3ef695b12a88ab99986749dc2cf3672d6994e Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Wed, 24 Mar 2021 14:30:18 +0000 Subject: [PATCH 2/8] plat/msdos86: implement close( ) function & proper errno setting --- plat/msdos86/libsys/close.s | 23 +++++++++++++++ plat/msdos86/libsys/errno.s | 28 +++++++++++++++++++ plat/msdos86/libsys/sys_seterrno.c | 45 ++++++++++++++++++++++++++++++ plat/msdos86/libsys/sys_xret.s | 35 +++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 plat/msdos86/libsys/close.s create mode 100644 plat/msdos86/libsys/errno.s create mode 100644 plat/msdos86/libsys/sys_seterrno.c create mode 100644 plat/msdos86/libsys/sys_xret.s diff --git a/plat/msdos86/libsys/close.s b/plat/msdos86/libsys/close.s new file mode 100644 index 000000000..b8c2f8a15 --- /dev/null +++ b/plat/msdos86/libsys/close.s @@ -0,0 +1,23 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Close a file. + +.define _close +_close: + mov bx, sp + mov bx, 2(bx) + movb ah, 0x3E + int 0x21 + jmp .sys_zret diff --git a/plat/msdos86/libsys/errno.s b/plat/msdos86/libsys/errno.s new file mode 100644 index 000000000..9858d2640 --- /dev/null +++ b/plat/msdos86/libsys/errno.s @@ -0,0 +1,28 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +#define D(e) .define e; e + +.sect .data + +! Define various ACK error numbers. Note that these are *not* ANSI C +! errnos, and are used for different purposes. + +D(ERANGE) = 1 +D(ESET) = 2 +D(EIDIVZ) = 6 +D(EHEAP) = 17 +D(EILLINS) = 18 +D(EODDZ) = 19 +D(ECASE) = 20 +D(EBADMON) = 25 + diff --git a/plat/msdos86/libsys/sys_seterrno.c b/plat/msdos86/libsys/sys_seterrno.c new file mode 100644 index 000000000..779e600f5 --- /dev/null +++ b/plat/msdos86/libsys/sys_seterrno.c @@ -0,0 +1,45 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include + +static const signed char err_map[] = + { + 0, /* 0x00 no error */ + EINVAL, /* 0x01 function number invalid */ + ENOENT, /* 0x02 file not found */ + ENOENT, /* 0x03 path not found */ + EMFILE, /* 0x04 too many open files */ + EACCES, /* 0x05 access denied */ + EBADF, /* 0x06 invalid handle */ + EFAULT, /* 0x07 mem. control block destroyed */ + ENOMEM, /* 0x08 insufficient memory */ + EFAULT, /* 0x09 memory block address invalid */ + E2BIG, /* 0x0A environment invalid */ + ENOEXEC, /* 0x0B format invalid */ + EINVAL, /* 0x0C access code invalid */ + EINVAL, /* 0x0D data invalid */ + ENOEXEC, /* 0x0E (?) fixup overflow */ + ENODEV, /* 0x0F invalid drive */ + EBUSY, /* 0x10 attempt to remove curr. dir. */ + EXDEV, /* 0x11 not same device */ + ENOENT, /* 0x12 no more files */ + EIO, /* 0x13 disk write-protected */ + EIO, /* 0x14 unknown unit */ + ENXIO, /* 0x15 drive not ready */ + }; + +/* + * Map an MS-DOS 2+ system error code to an `errno' value and store that in + * `errno'. Return -1. + */ +int _sys_seterrno(unsigned dos_err) +{ + if (dos_err < sizeof(err_map) / sizeof(err_map[0])) + errno = err_map[dos_err]; + else + errno = EIO; + return -1; +} diff --git a/plat/msdos86/libsys/sys_xret.s b/plat/msdos86/libsys/sys_xret.s new file mode 100644 index 000000000..a5b65116a --- /dev/null +++ b/plat/msdos86/libsys/sys_xret.s @@ -0,0 +1,35 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! .sys_zret: if the carry flag is set, then set `errno' from the DOS error +! code in ax, and return -1. If the carry flag is clear, just return zero. +! +! .sys_axret: if the carry flag is set, then set `errno' from the DOS error +! code in ax, and return -1. If the carry flag is clear, just return ax as +! a return value. + +.extern .sys_zret +.extern .sys_axret +.sys_zret: + jc error + xor ax, ax +no_error: + ret +.sys_axret: + jnc no_error +error: + push ax + call __sys_seterrno + pop cx + ret From 2dfddf3fa8fce4172559858f38c77d577118bfaa Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Sat, 27 Mar 2021 06:59:07 +0000 Subject: [PATCH 3/8] libcc.ansi: add support for O_TEXT, O_BINARY file status flags --- lang/cem/libcc.ansi/headers/ack/config.h | 4 ++++ lang/cem/libcc.ansi/headers/ack/emufile.h | 1 + lang/cem/libcc.ansi/headers/unistd.h | 4 ++++ lang/cem/libcc.ansi/sys/stdio/fdopen.c | 18 ++++++++++++++++++ lang/cem/libcc.ansi/sys/stdio/fopen.c | 11 +++++++++-- lang/cem/libcc.ansi/sys/stdio/freopen.c | 10 +++++++++- 6 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lang/cem/libcc.ansi/headers/ack/config.h b/lang/cem/libcc.ansi/headers/ack/config.h index 41e9bc3b6..cf8f5d835 100644 --- a/lang/cem/libcc.ansi/headers/ack/config.h +++ b/lang/cem/libcc.ansi/headers/ack/config.h @@ -15,6 +15,10 @@ #define ACKCONF_WANT_STANDARD_O 1 #endif +#ifndef ACKCONF_WANT_O_TEXT_O_BINARY +#define ACKCONF_WANT_O_TEXT_O_BINARY 0 +#endif + #ifndef ACKCONF_WANT_STANDARD_SIGNALS #define ACKCONF_WANT_STANDARD_SIGNALS 1 #endif diff --git a/lang/cem/libcc.ansi/headers/ack/emufile.h b/lang/cem/libcc.ansi/headers/ack/emufile.h index 06a1ffcec..158ec58a8 100644 --- a/lang/cem/libcc.ansi/headers/ack/emufile.h +++ b/lang/cem/libcc.ansi/headers/ack/emufile.h @@ -24,6 +24,7 @@ struct FILE { #define _IOREADING 0x080 #define _IOWRITING 0x100 #define _IOAPPEND 0x200 +#define _IOBINARY 0x400 #if !defined BUFSIZ #define BUFSIZ 1024 diff --git a/lang/cem/libcc.ansi/headers/unistd.h b/lang/cem/libcc.ansi/headers/unistd.h index bab5ada71..a316132d2 100644 --- a/lang/cem/libcc.ansi/headers/unistd.h +++ b/lang/cem/libcc.ansi/headers/unistd.h @@ -24,6 +24,10 @@ O_WRONLY = 1, O_RDWR = 2, + #if ACKCONF_WANT_O_TEXT_O_BINARY + O_TEXT = 010000, + O_BINARY = 020000, + #endif O_CREAT = 0100, O_TRUNC = 01000, O_APPEND = 02000, diff --git a/lang/cem/libcc.ansi/sys/stdio/fdopen.c b/lang/cem/libcc.ansi/sys/stdio/fdopen.c index aff7d248e..58fcecfcd 100644 --- a/lang/cem/libcc.ansi/sys/stdio/fdopen.c +++ b/lang/cem/libcc.ansi/sys/stdio/fdopen.c @@ -5,6 +5,10 @@ #include #include +#if ACKCONF_WANT_O_TEXT_O_BINARY + #include + #include +#endif #if ACKCONF_WANT_STDIO && ACKCONF_WANT_EMULATED_FILE @@ -38,6 +42,9 @@ FILE* fdopen(int fd, const char* mode) switch (*mode++) { case 'b': +#if ACKCONF_WANT_O_TEXT_O_BINARY + flags |= _IOBINARY; +#endif continue; case '+': flags |= _IOREAD | _IOWRITE; @@ -57,6 +64,17 @@ FILE* fdopen(int fd, const char* mode) if ((flags & _IOREAD) && (flags & _IOWRITE)) flags &= ~(_IOREADING | _IOWRITING); +#if ACKCONF_WANT_O_TEXT_O_BINARY + { + /* + * FIXME: this assumes that any platform that has O_TEXT and + * O_BINARY will also have a _setmode() function. + */ + extern int _setmode(int, int); + _setmode(fd, (flags & _IOBINARY) ? O_BINARY : O_TEXT); + } +#endif + stream->_count = 0; stream->_fd = fd; stream->_flags = flags; diff --git a/lang/cem/libcc.ansi/sys/stdio/fopen.c b/lang/cem/libcc.ansi/sys/stdio/fopen.c index f2189ee8f..cfea44154 100644 --- a/lang/cem/libcc.ansi/sys/stdio/fopen.c +++ b/lang/cem/libcc.ansi/sys/stdio/fopen.c @@ -7,7 +7,6 @@ #include #include #include -#include #if ACKCONF_WANT_STDIO && ACKCONF_WANT_EMULATED_FILE @@ -68,6 +67,9 @@ FILE* fopen(const char* name, const char* mode) switch (*mode++) { case 'b': +#if ACKCONF_WANT_O_TEXT_O_BINARY + flags |= _IOBINARY; +#endif continue; case '+': rwmode = O_RDWR; @@ -80,6 +82,7 @@ FILE* fopen(const char* name, const char* mode) break; } +#if !ACKCONF_WANT_O_TEXT_O_BINARY /* Perform a creat() when the file should be truncated or when * the file is opened for writing and the open() failed. */ @@ -87,12 +90,16 @@ FILE* fopen(const char* name, const char* mode) || (((fd = open(name, rwmode)) < 0) && (rwflags & O_CREAT))) { - if (((fd = creat(name, PMODE)) > 0) && flags | _IOREAD) + if (((fd = creat(name, PMODE)) >= 0) && (flags & _IOREAD)) { (void)close(fd); fd = open(name, rwmode); } } +#else + rwflags |= (flags & _IOBINARY) ? O_BINARY : O_TEXT; + fd = open(name, rwmode | rwflags, PMODE); +#endif if (fd < 0) return (FILE*)NULL; diff --git a/lang/cem/libcc.ansi/sys/stdio/freopen.c b/lang/cem/libcc.ansi/sys/stdio/freopen.c index 846208420..1ae5befe0 100644 --- a/lang/cem/libcc.ansi/sys/stdio/freopen.c +++ b/lang/cem/libcc.ansi/sys/stdio/freopen.c @@ -50,6 +50,9 @@ FILE* freopen(const char* name, const char* mode, FILE* stream) switch (*mode++) { case 'b': +#if ACKCONF_WANT_O_TEXT_O_BINARY + flags |= _IOBINARY; +#endif continue; case '+': rwmode = O_RDWR; @@ -62,16 +65,21 @@ FILE* freopen(const char* name, const char* mode, FILE* stream) break; } +#if !ACKCONF_WANT_O_TEXT_O_BINARY if ((rwflags & O_TRUNC) || (((fd = open(name, rwmode)) < 0) && (rwflags & O_CREAT))) { - if (((fd = creat(name, PMODE)) < 0) && flags | _IOREAD) + if (((fd = creat(name, PMODE)) >= 0) && (flags & _IOREAD)) { (void)close(fd); fd = open(name, rwmode); } } +#else + rwflags |= (flags & _IOBINARY) ? O_BINARY : O_TEXT; + fd = open(name, rwmode | rwflags, PMODE); +#endif if (fd < 0) { From 0d2b55cd295930b3b77f326c7237ce11d9ceb06c Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Sat, 27 Mar 2021 19:30:38 +0000 Subject: [PATCH 4/8] plat/msdos86: add isatty( ), _setmode( ), read( ), write( ) --- plat/msdos86/descr | 2 + plat/msdos86/include/ack/plat.h | 12 +++++ plat/msdos86/include/build.lua | 24 ++++++++++ plat/msdos86/include/sys/types.h | 9 ++++ plat/msdos86/libsys/_hol0.s | 19 ++++++++ plat/msdos86/libsys/brk.c | 59 +++++++++++++++++++++++ plat/msdos86/libsys/build.lua | 15 ++++++ plat/msdos86/libsys/creat.c | 14 ++++++ plat/msdos86/libsys/isatty.s | 37 ++++++++++++++ plat/msdos86/libsys/libsys.h | 53 ++++++++++++++++++++ plat/msdos86/libsys/read.c | 77 ++++++++++++++++++++++++++++++ plat/msdos86/libsys/setmode.c | 23 +++++++++ plat/msdos86/libsys/sys_fdmodes.c | 13 +++++ plat/msdos86/libsys/sys_getmode.c | 27 +++++++++++ plat/msdos86/libsys/sys_initmain.c | 3 +- plat/msdos86/libsys/sys_iseof.c | 27 +++++++++++ plat/msdos86/libsys/sys_isopen.s | 26 ++++++++++ plat/msdos86/libsys/sys_isreadyr.s | 28 +++++++++++ plat/msdos86/libsys/sys_rawrw.s | 33 +++++++++++++ plat/msdos86/libsys/sys_seteof.c | 42 ++++++++++++++++ plat/msdos86/libsys/sys_setmode.c | 55 +++++++++++++++++++++ plat/msdos86/libsys/write.c | 61 +++++++++++++++++++++++ 22 files changed, 657 insertions(+), 2 deletions(-) create mode 100644 plat/msdos86/include/ack/plat.h create mode 100644 plat/msdos86/include/build.lua create mode 100644 plat/msdos86/include/sys/types.h create mode 100644 plat/msdos86/libsys/_hol0.s create mode 100644 plat/msdos86/libsys/brk.c create mode 100644 plat/msdos86/libsys/build.lua create mode 100644 plat/msdos86/libsys/creat.c create mode 100644 plat/msdos86/libsys/isatty.s create mode 100644 plat/msdos86/libsys/libsys.h create mode 100644 plat/msdos86/libsys/read.c create mode 100644 plat/msdos86/libsys/setmode.c create mode 100644 plat/msdos86/libsys/sys_fdmodes.c create mode 100644 plat/msdos86/libsys/sys_getmode.c create mode 100644 plat/msdos86/libsys/sys_iseof.c create mode 100644 plat/msdos86/libsys/sys_isopen.s create mode 100644 plat/msdos86/libsys/sys_isreadyr.s create mode 100644 plat/msdos86/libsys/sys_rawrw.s create mode 100644 plat/msdos86/libsys/sys_seteof.c create mode 100644 plat/msdos86/libsys/sys_setmode.c create mode 100644 plat/msdos86/libsys/write.c diff --git a/plat/msdos86/descr b/plat/msdos86/descr index 97c05751b..333bb671b 100644 --- a/plat/msdos86/descr +++ b/plat/msdos86/descr @@ -73,6 +73,8 @@ name led {FLOATS?} \ (.e:{TAIL}={PLATFORMDIR}/libem.a \ {PLATFORMDIR}/libsys.a \ + {PLATFORMDIR}/libc.a \ + {PLATFORMDIR}/libem.a \ {PLATFORMDIR}/libend.a) linker end diff --git a/plat/msdos86/include/ack/plat.h b/plat/msdos86/include/ack/plat.h new file mode 100644 index 000000000..a1e942cdd --- /dev/null +++ b/plat/msdos86/include/ack/plat.h @@ -0,0 +1,12 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#ifndef _ACK_PLAT_H +#define _ACK_PLAT_H + +#define ACKCONF_WANT_O_TEXT_O_BINARY 1 +#define ACKCONF_WANT_EMULATED_TIME 0 + +#endif diff --git a/plat/msdos86/include/build.lua b/plat/msdos86/include/build.lua new file mode 100644 index 000000000..6ae480041 --- /dev/null +++ b/plat/msdos86/include/build.lua @@ -0,0 +1,24 @@ +include("plat/build.lua") + +headermap = {} +packagemap = {} + +local function addheader(h) + headermap[h] = "./"..h + packagemap["$(PLATIND)/msdos86/include/"..h] = "./"..h +end + +addheader("ack/plat.h") +addheader("sys/types.h") + +acklibrary { + name = "headers", + hdrs = headermap +} + +installable { + name = "pkg", + map = packagemap +} + + diff --git a/plat/msdos86/include/sys/types.h b/plat/msdos86/include/sys/types.h new file mode 100644 index 000000000..6a0c3d3db --- /dev/null +++ b/plat/msdos86/include/sys/types.h @@ -0,0 +1,9 @@ +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H + +typedef int pid_t; +typedef int mode_t; +typedef long time_t; +typedef long suseconds_t; + +#endif diff --git a/plat/msdos86/libsys/_hol0.s b/plat/msdos86/libsys/_hol0.s new file mode 100644 index 000000000..f01566fe8 --- /dev/null +++ b/plat/msdos86/libsys/_hol0.s @@ -0,0 +1,19 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .bss + +! This data block is used to store information about the current line number +! and file. + +.define hol0 +.comm hol0, 8 diff --git a/plat/msdos86/libsys/brk.c b/plat/msdos86/libsys/brk.c new file mode 100644 index 000000000..ab333e550 --- /dev/null +++ b/plat/msdos86/libsys/brk.c @@ -0,0 +1,59 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include "libsys.h" + +#define STACK_BUFFER 128 /* number of bytes to leave for stack */ + +extern char _end[1]; +static char* current = _end; + +int brk(void* newend) +{ + /* This variable is used to figure out the current stack pointer, + * by taking its address. */ + char dummy; + char* p = newend; + + if ((p > (&dummy - STACK_BUFFER)) || + (p < _end)) + { + errno = ENOMEM; + return -1; + } + + current = p; + return 0; +} + +void* sbrk(int increment) +{ + char* old; + char* new; + + if (increment == 0) + return current; + + old = current; + + new = old + increment; + + if ((increment > 0) && (new <= old)) + goto out_of_memory; + else if ((increment < 0) && (new >= old)) + goto out_of_memory; + + if (brk(new) < 0) + goto out_of_memory; + + return old; + +out_of_memory: + errno = ENOMEM; + return OUT_OF_MEMORY; +} diff --git a/plat/msdos86/libsys/build.lua b/plat/msdos86/libsys/build.lua new file mode 100644 index 000000000..dd357082a --- /dev/null +++ b/plat/msdos86/libsys/build.lua @@ -0,0 +1,15 @@ +acklibrary { + name = "lib", + srcs = { + "./*.c", + "./*.s", + }, + deps = { + "lang/cem/libcc.ansi/headers+headers", + "plat/msdos86/include+headers", + }, + vars = { + plat = "msdos86" + } +} + diff --git a/plat/msdos86/libsys/creat.c b/plat/msdos86/libsys/creat.c new file mode 100644 index 000000000..8f18317c5 --- /dev/null +++ b/plat/msdos86/libsys/creat.c @@ -0,0 +1,14 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include "libsys.h" + +int creat(const char* path, int mode) +{ + return open(path, O_CREAT|O_WRONLY|O_TRUNC, mode); +} diff --git a/plat/msdos86/libsys/isatty.s b/plat/msdos86/libsys/isatty.s new file mode 100644 index 000000000..bd2e195e6 --- /dev/null +++ b/plat/msdos86/libsys/isatty.s @@ -0,0 +1,37 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Say whether a given file descriptor number refers to a terminal. + +.define _isatty +_isatty: + mov bx, sp + mov bx, 2(bx) + mov ax, 0x4400 + int 0x21 + jc error + testb dl, dl + jz not_tty + mov ax, 1 + ret +not_tty: + mov (_errno), 25 ! ENOTTY +not_tty_2: + xor ax, ax + ret +error: + push ax + call __sys_seterrno + pop cx + jmp not_tty_2 diff --git a/plat/msdos86/libsys/libsys.h b/plat/msdos86/libsys/libsys.h new file mode 100644 index 000000000..ddba5cda6 --- /dev/null +++ b/plat/msdos86/libsys/libsys.h @@ -0,0 +1,53 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#ifndef LIBSYS_H +#define LIBSYS_H + +#include +#include +#include + +#define OUT_OF_MEMORY ((void *)-1) /* sbrk returns this on failure */ + +/* + * Data structure for remembering whether each file descriptor is open in + * text mode (O_TEXT) or binary mode (O_BINARY). + * + * Currently this is just a simple linked list, where each linked list node + * records the modes for 16 file descriptors, in bit vector form (0: text, + * 1: binary). In addition, each node also remembers, for each of its file + * descriptors, whether an end-of-file (^Z) character was encountered when + * reading from it in text mode. + * + * List nodes are allocated using sbrk() and never freed. + * + * This scheme should be fast and light enough, as the number of open file + * descriptors is expected to be small. + * + * FIXME: the code for updating this structure is not exactly thread-safe. + */ +typedef uint16_t _fdvec_t; + +struct _fdmodes { + struct _fdmodes *next; + int begfd; + _fdvec_t modevec, eofvec; +}; + +extern struct _fdmodes _sys_fdmodes; + +#define _FDVECMASK (sizeof(_fdvec_t) * CHAR_BIT - 1) + +extern int _sys_getmode(int); +extern int _sys_setmode(int, int); +extern int _sys_iseof(int); +extern int _sys_seteof(int, int); +extern ssize_t _sys_rawread(int, char *, size_t); +extern ssize_t _sys_rawwrite(int, const char *, size_t); +extern int _sys_isopen(int); +extern int _sys_isreadyr(int); + +#endif diff --git a/plat/msdos86/libsys/read.c b/plat/msdos86/libsys/read.c new file mode 100644 index 000000000..71711832e --- /dev/null +++ b/plat/msdos86/libsys/read.c @@ -0,0 +1,77 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include +#include +#include "libsys.h" + +ssize_t read(int fd, void* buffer, size_t count) +{ + char *p, *q; + ssize_t r, tot; + size_t left; + int eof = 0; + + if (!count || _sys_getmode(fd) == O_BINARY) + return _sys_rawread(fd, buffer, count); + + if (_sys_iseof(fd)) + return 0; + + /* + * If the file descriptor is an O_TEXT fd, read from it, and then + * remove CRs from the input. After removing CRs, if the buffer is + * not filled and the fd still has bytes left to read, then keep + * reading, and keep removing CRs. + * + * Also handle end-of-file indicators (^Z). If we see one, move the + * file pointer to just before the ^Z. Also set an internal flag + * for the fd so that we do not try to read any further (until e.g. + * a seek happens). + */ + p = buffer; + tot = 0; + + do + { + r = _sys_rawread(fd, p, count - (p - (char *)buffer)); + if (r <= 0) + return tot; + + q = memchr(p, 0x1a, (size_t)r); + if (q) + { + _sys_rawlseek(fd, (off_t)(q - p) - r, SEEK_CUR); + r = q - p; + eof = 1; + _sys_seteof(fd, 1); + } + + q = memchr(p, '\r', (size_t)r); + if (!q) + return tot + r; + + tot += q - p; + left = r - (q + 1 - p); + p = q; + ++q; + + while (left) + { + char c = *q++; + if (c != '\r') + { + *p++ = c; + ++tot; + } + --left; + } + } while (tot < count && !eof && _sys_isreadyr(fd)); + + return tot; +} diff --git a/plat/msdos86/libsys/setmode.c b/plat/msdos86/libsys/setmode.c new file mode 100644 index 000000000..ac086d397 --- /dev/null +++ b/plat/msdos86/libsys/setmode.c @@ -0,0 +1,23 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include "libsys.h" + +int _setmode(int fd, int mode) +{ + if (mode != O_TEXT && mode != O_BINARY) + { + errno = EINVAL; + return -1; + } + if (!_sys_isopen(fd)) + { + errno = EBADF; + return -1; + } + return _sys_setmode(fd, mode); +} diff --git a/plat/msdos86/libsys/sys_fdmodes.c b/plat/msdos86/libsys/sys_fdmodes.c new file mode 100644 index 000000000..c37b5bc7e --- /dev/null +++ b/plat/msdos86/libsys/sys_fdmodes.c @@ -0,0 +1,13 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include "libsys.h" + +/* + * By default, consider all file descriptors, including those for stdin (0), + * stdout (1), and stderr (2), as being open in text mode. + */ +struct _fdmodes _sys_fdmodes = { NULL, 0, 0 }; diff --git a/plat/msdos86/libsys/sys_getmode.c b/plat/msdos86/libsys/sys_getmode.c new file mode 100644 index 000000000..4ae0aba2d --- /dev/null +++ b/plat/msdos86/libsys/sys_getmode.c @@ -0,0 +1,27 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include "libsys.h" + +/* + * Say whether a particular fd is currently open in text or binary mode. + * Assume that the fd is valid. Return O_TEXT or O_BINARY. + */ +int _sys_getmode(int fd) +{ + int reqbegfd = fd & ~_FDVECMASK; + struct _fdmodes *p = &_sys_fdmodes; + _fdvec_t mask; + + while (p->begfd != reqbegfd) + { + p = p->next; + if (!p) + return O_TEXT; + } + mask = (_fdvec_t)1 << (fd & _FDVECMASK); + return (p->modevec & mask) ? O_BINARY : O_TEXT; +} diff --git a/plat/msdos86/libsys/sys_initmain.c b/plat/msdos86/libsys/sys_initmain.c index 11b5a1fb4..5530a0510 100644 --- a/plat/msdos86/libsys/sys_initmain.c +++ b/plat/msdos86/libsys/sys_initmain.c @@ -6,8 +6,7 @@ #include #include #include - -#define OUT_OF_MEMORY (void*)(-1) /* sbrk returns this on failure */ +#include "libsys.h" struct for_main { int argc; diff --git a/plat/msdos86/libsys/sys_iseof.c b/plat/msdos86/libsys/sys_iseof.c new file mode 100644 index 000000000..ab9f6c994 --- /dev/null +++ b/plat/msdos86/libsys/sys_iseof.c @@ -0,0 +1,27 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include "libsys.h" + +/* + * Say whether a particular fd is currently open in text mode and has just + * hit an MS-DOS end-of-file character (^Z). + */ +int _sys_iseof(int fd) +{ + int reqbegfd = fd & ~_FDVECMASK; + struct _fdmodes *p = &_sys_fdmodes; + _fdvec_t mask; + + while (p->begfd != reqbegfd) + { + p = p->next; + if (!p) + return 0; + } + mask = (_fdvec_t)1 << (fd & _FDVECMASK); + return p->eofvec & mask; +} diff --git a/plat/msdos86/libsys/sys_isopen.s b/plat/msdos86/libsys/sys_isopen.s new file mode 100644 index 000000000..5ce49ed45 --- /dev/null +++ b/plat/msdos86/libsys/sys_isopen.s @@ -0,0 +1,26 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Say whether a given file descriptor number refers to a valid open file +! descriptor. + +.define __sys_isopen +__sys_isopen: + mov bx, sp + mov bx, 2(bx) + mov ax, 0x4400 + int 0x21 + sbb ax, ax + inc ax + ret diff --git a/plat/msdos86/libsys/sys_isreadyr.s b/plat/msdos86/libsys/sys_isreadyr.s new file mode 100644 index 000000000..b5d0800ee --- /dev/null +++ b/plat/msdos86/libsys/sys_isreadyr.s @@ -0,0 +1,28 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Say whether a file descriptor is ready for input, i.e. reading from the +! fd will immediately return something. + +.define __sys_isreadyr +__sys_isreadyr: + mov bx, sp + mov ax, 0x4406 + mov bx, 2(bx) + int 0x21 + jnc ok + movb al, 0 +ok: + cbw + ret diff --git a/plat/msdos86/libsys/sys_rawrw.s b/plat/msdos86/libsys/sys_rawrw.s new file mode 100644 index 000000000..a306d6528 --- /dev/null +++ b/plat/msdos86/libsys/sys_rawrw.s @@ -0,0 +1,33 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Read/write bytes to/to a file descriptor. These routines do not do any +! translation between CRLF and LF line endings. +! +! Note that, for MS-DOS, a raw "write" request of zero bytes will truncate +! (or extend) the file to the current file position. + +.define __sys_rawread +__sys_rawread: + movb ah, 0x3f + .data1 0x3d ! eat up the next instruction +.define __sys_rawwrite +__sys_rawwrite: + movb ah, 0x40 + mov bx, sp + mov dx, 4(bx) + mov cx, 6(bx) + mov bx, 2(bx) + int 0x21 + jmp .sys_axret diff --git a/plat/msdos86/libsys/sys_seteof.c b/plat/msdos86/libsys/sys_seteof.c new file mode 100644 index 000000000..643e2167f --- /dev/null +++ b/plat/msdos86/libsys/sys_seteof.c @@ -0,0 +1,42 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include "libsys.h" + +/* + * Set or reset the "end-of-file encountered" indicator for a particular fd. + * Assume that the fd is valid, and is being read from in text mode. + */ +int _sys_seteof(int fd, int eof) +{ + int reqbegfd = fd & ~_FDVECMASK; + struct _fdmodes *p = &_sys_fdmodes; + _fdvec_t mask; + + while (p->begfd != reqbegfd) + { + p = p->next; + if (!p) + { + if (!eof) + return 0; + if ((p = sbrk(sizeof(struct _fdmodes))) + == OUT_OF_MEMORY) + return -1; + p->next = _sys_fdmodes.next; + p->begfd = reqbegfd; + p->modevec = p->eofvec = 0; + _sys_fdmodes.next = p; + break; + } + } + mask = (_fdvec_t)1 << (fd & _FDVECMASK); + if (eof) + p->eofvec |= mask; + else + p->eofvec &= ~mask; + return 0; +} diff --git a/plat/msdos86/libsys/sys_setmode.c b/plat/msdos86/libsys/sys_setmode.c new file mode 100644 index 000000000..6191e9027 --- /dev/null +++ b/plat/msdos86/libsys/sys_setmode.c @@ -0,0 +1,55 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include "libsys.h" + +/* + * Set text/binary mode for a particular fd. Assume that the fd is valid, + * and that the mode is either O_TEXT or O_BINARY. + * + * Return the previous mode (either O_TEXT or O_BINARY) on success, -1 (with + * errno set) on error. + */ +int _sys_setmode(int fd, int mode) +{ + int reqbegfd = fd & ~_FDVECMASK; + struct _fdmodes *p = &_sys_fdmodes; + _fdvec_t mask; + + while (p->begfd != reqbegfd) + { + p = p->next; + if (!p) + { + if (mode == O_TEXT) + return O_TEXT; + if ((p = sbrk(sizeof(struct _fdmodes))) + == OUT_OF_MEMORY) + return -1; + p->next = _sys_fdmodes.next; + p->begfd = reqbegfd; + p->modevec = p->eofvec = 0; + _sys_fdmodes.next = p; + break; + } + } + mask = (_fdvec_t)1 << (fd & _FDVECMASK); + if (mode == O_BINARY) + { + if (p->modevec & mask) + return O_BINARY; + p->modevec |= mask; + p->eofvec &= ~mask; + return O_TEXT; + } + else + { + if (!(p->modevec & mask)) + return O_TEXT; + p->modevec &= ~mask; + return O_BINARY; + } +} diff --git a/plat/msdos86/libsys/write.c b/plat/msdos86/libsys/write.c new file mode 100644 index 000000000..fc744b256 --- /dev/null +++ b/plat/msdos86/libsys/write.c @@ -0,0 +1,61 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include +#include "libsys.h" + +ssize_t write(int fd, void* buffer, size_t count) +{ + static const char crlf[2] = "\r\n"; + int i; + char *p, *q; + size_t left, n; + ssize_t r, tot; + + if (!count) + return 0; + + if (_sys_getmode(fd) == O_BINARY) + return _sys_rawwrite(fd, buffer, count); + + /* If the file descriptor is an O_TEXT fd, translate LFs to CRLFs. */ + p = buffer; + left = count; + tot = 0; + while (left) + { + q = memchr(p, '\n', left); + if (!q) + return _sys_rawwrite(fd, p, left); + + n = q - p; + if (n) + { + r = _sys_rawwrite(fd, p, n); + if (r <= 0) + return tot ? tot : r; + tot += r; + p += r; + left -= r; + if (r != n) + break; + } + + r = _sys_rawwrite(fd, crlf, sizeof crlf); + if (r != 2) + { + if (r > 0) + r = 0; + return tot ? tot : r; + } + ++tot; + ++p; + --left; + } + return tot; +} From 466cf20805a99dc4dbcbf8eb9f69ad2cd61b965c Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Sun, 28 Mar 2021 12:09:52 +0000 Subject: [PATCH 5/8] plat/msdos86: add open( ), lseek( ), and getpid() --- plat/msdos86/include/sys/types.h | 2 +- plat/msdos86/libsys/getpid.s | 27 ++++++++ plat/msdos86/libsys/libsys.h | 5 ++ plat/msdos86/libsys/lseek.c | 26 ++++++++ plat/msdos86/libsys/open.c | 101 +++++++++++++++++++++++++++++ plat/msdos86/libsys/sys_exists.s | 25 +++++++ plat/msdos86/libsys/sys_rawcreat.s | 24 +++++++ plat/msdos86/libsys/sys_rawlseek.s | 26 ++++++++ plat/msdos86/libsys/sys_rawopen.s | 24 +++++++ plat/msdos86/libsys/sys_seterrno.c | 4 +- plat/msdos86/libsys/sys_xret.s | 12 +++- 11 files changed, 270 insertions(+), 6 deletions(-) create mode 100644 plat/msdos86/libsys/getpid.s create mode 100644 plat/msdos86/libsys/lseek.c create mode 100644 plat/msdos86/libsys/open.c create mode 100644 plat/msdos86/libsys/sys_exists.s create mode 100644 plat/msdos86/libsys/sys_rawcreat.s create mode 100644 plat/msdos86/libsys/sys_rawlseek.s create mode 100644 plat/msdos86/libsys/sys_rawopen.s diff --git a/plat/msdos86/include/sys/types.h b/plat/msdos86/include/sys/types.h index 6a0c3d3db..005f48eb0 100644 --- a/plat/msdos86/include/sys/types.h +++ b/plat/msdos86/include/sys/types.h @@ -1,7 +1,7 @@ #ifndef _SYS_TYPES_H #define _SYS_TYPES_H -typedef int pid_t; +typedef long pid_t; typedef int mode_t; typedef long time_t; typedef long suseconds_t; diff --git a/plat/msdos86/libsys/getpid.s b/plat/msdos86/libsys/getpid.s new file mode 100644 index 000000000..009c4f9f4 --- /dev/null +++ b/plat/msdos86/libsys/getpid.s @@ -0,0 +1,27 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Get the current process identifier. For MS-DOS, use the Program Segment +! Prefix (PSP) segment as the PID. +! +! (Note that pid_t is a 32-bit type. This is to allow for PSP addresses +! above 0x7FFF:0.) + +.define _getpid +_getpid: + movb ah, 0x51 + int 0x21 + xchg bx, ax + xor dx, dx + ret diff --git a/plat/msdos86/libsys/libsys.h b/plat/msdos86/libsys/libsys.h index ddba5cda6..3488c332e 100644 --- a/plat/msdos86/libsys/libsys.h +++ b/plat/msdos86/libsys/libsys.h @@ -49,5 +49,10 @@ extern ssize_t _sys_rawread(int, char *, size_t); extern ssize_t _sys_rawwrite(int, const char *, size_t); extern int _sys_isopen(int); extern int _sys_isreadyr(int); +extern int _sys_exists(const char *); +extern int _sys_rawcreat(const char *, unsigned); +extern int _sys_rawopen(const char *, int); +extern off_t _sys_rawlseek(int, off_t, int); + #endif diff --git a/plat/msdos86/libsys/lseek.c b/plat/msdos86/libsys/lseek.c new file mode 100644 index 000000000..db996a1dd --- /dev/null +++ b/plat/msdos86/libsys/lseek.c @@ -0,0 +1,26 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include "libsys.h" + +off_t lseek(int fd, off_t offset, int whence) +{ + off_t newoff; + + if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) + { + errno = EINVAL; + return -1L; + } + + newoff = _sys_rawlseek(fd, offset, whence); + if (newoff != -1L) + _sys_seteof(fd, 0); + + return newoff; +} diff --git a/plat/msdos86/libsys/open.c b/plat/msdos86/libsys/open.c new file mode 100644 index 000000000..038ab004b --- /dev/null +++ b/plat/msdos86/libsys/open.c @@ -0,0 +1,101 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +/* + * Derived from dos-open.c for newlib-ia16 which was written for the + * gcc-ia16 toolchain. + * + * Copyright (c) 2018 Bart Oldeman + * Copyright (c) 2019--2021 TK Chia + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + */ + +#include +#include +#include +#include +#include "libsys.h" + +int open(const char* pathname, int flags, ...) +{ + int fd = -1, binmode; + off_t ret; + + if ((flags & ~(O_ACCMODE | O_CREAT | O_TRUNC | O_APPEND + | O_TEXT | O_BINARY))) + { + /* bail out if there are unknown flags */ + errno = EINVAL; + return -1; + } + + binmode = flags & (O_TEXT | O_BINARY); + if (binmode != O_TEXT && binmode != O_BINARY && binmode != 0) + { + /* bail out if both O_TEXT and O_BINARY are specified (!) */ + errno = EINVAL; + return -1; + } + + if ((flags & O_CREAT)) + { + /* special but common case O_WRONLY | O_CREAT | O_TRUNC + * should be handled by only calling _sys_rawcreat */ + int fileexists = 0; + if (!(flags & O_TRUNC)) + fileexists = _sys_exists(pathname); + if (!fileexists) + { + va_list ap; + mode_t mode; + + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + + fd = _sys_rawcreat(pathname, mode & 0200 ? 0 : 1); + if (fd != -1) + { + if ((flags & O_ACCMODE) == O_WRONLY) + return fd; + close(fd); + fd = -1; + } + } + } + + /* try to open file with mode */ + if (fd == -1) + fd = _sys_rawopen(pathname, flags & O_ACCMODE); + if (fd == -1) + return fd; + ret = 0; + + /* handle O_TRUNC and O_APPEND */ + if ((flags & O_TRUNC)) + ret = _sys_rawwrite(fd, NULL, 0); + else if ((flags & O_APPEND)) + ret = _sys_rawlseek(fd, 0L, SEEK_END); + if (ret == -1) + { + close(fd); + return -1; + } + + /* handle O_TEXT and O_BINARY, and reset "end of file encountered" */ + _sys_setmode(fd, binmode == O_BINARY ? O_BINARY : O_TEXT); + _sys_seteof(fd, 0); + + return fd; +} diff --git a/plat/msdos86/libsys/sys_exists.s b/plat/msdos86/libsys/sys_exists.s new file mode 100644 index 000000000..e282ef687 --- /dev/null +++ b/plat/msdos86/libsys/sys_exists.s @@ -0,0 +1,25 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Say whether a file exists with the given name. + +.define __sys_exists +__sys_exists: + mov bx, sp + mov dx, 2(bx) + mov ax, 0x4300 + int 0x21 + sbb ax, ax + inc ax + ret diff --git a/plat/msdos86/libsys/sys_rawcreat.s b/plat/msdos86/libsys/sys_rawcreat.s new file mode 100644 index 000000000..67bc34e59 --- /dev/null +++ b/plat/msdos86/libsys/sys_rawcreat.s @@ -0,0 +1,24 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Create or truncate a file. + +.define __sys_rawcreat +__sys_rawcreat: + movb ah, 0x3c + mov bx, sp + mov dx, 2(bx) + mov cx, 4(bx) + int 0x21 + jmp .sys_axret diff --git a/plat/msdos86/libsys/sys_rawlseek.s b/plat/msdos86/libsys/sys_rawlseek.s new file mode 100644 index 000000000..992c4c655 --- /dev/null +++ b/plat/msdos86/libsys/sys_rawlseek.s @@ -0,0 +1,26 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Move the current file position for a file descriptor. + +.define __sys_rawlseek +__sys_rawlseek: + movb ah, 0x42 + mov bx, sp + mov dx, 4(bx) + mov cx, 6(bx) + movb al, 8(bx) + mov bx, 2(bx) + int 0x21 + jmp .sys_dxaxret diff --git a/plat/msdos86/libsys/sys_rawopen.s b/plat/msdos86/libsys/sys_rawopen.s new file mode 100644 index 000000000..301e2fded --- /dev/null +++ b/plat/msdos86/libsys/sys_rawopen.s @@ -0,0 +1,24 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Open an existing file. + +.define __sys_rawopen +__sys_rawopen: + movb ah, 0x3d + mov bx, sp + mov dx, 2(bx) + movb al, 4(bx) + int 0x21 + jmp .sys_axret diff --git a/plat/msdos86/libsys/sys_seterrno.c b/plat/msdos86/libsys/sys_seterrno.c index 779e600f5..b135aea33 100644 --- a/plat/msdos86/libsys/sys_seterrno.c +++ b/plat/msdos86/libsys/sys_seterrno.c @@ -33,9 +33,9 @@ static const signed char err_map[] = /* * Map an MS-DOS 2+ system error code to an `errno' value and store that in - * `errno'. Return -1. + * `errno'. Return a longword -1. */ -int _sys_seterrno(unsigned dos_err) +long _sys_seterrno(unsigned dos_err) { if (dos_err < sizeof(err_map) / sizeof(err_map[0])) errno = err_map[dos_err]; diff --git a/plat/msdos86/libsys/sys_xret.s b/plat/msdos86/libsys/sys_xret.s index a5b65116a..57f407ef4 100644 --- a/plat/msdos86/libsys/sys_xret.s +++ b/plat/msdos86/libsys/sys_xret.s @@ -13,20 +13,26 @@ .sect .text ! .sys_zret: if the carry flag is set, then set `errno' from the DOS error -! code in ax, and return -1. If the carry flag is clear, just return zero. +! code in ax, and return a shortword -1. If the carry flag is clear, just +! return a shortword zero. ! ! .sys_axret: if the carry flag is set, then set `errno' from the DOS error -! code in ax, and return -1. If the carry flag is clear, just return ax as -! a return value. +! code in ax, and return a shortword -1. If the carry flag is clear, just +! return ax as a return value. +! +! .sys_dxaxret: same as .sys_axret, but return a longword -1 or dx:ax as a +! return value. .extern .sys_zret .extern .sys_axret +.extern .sys_dxaxret .sys_zret: jc error xor ax, ax no_error: ret .sys_axret: +.sys_dxaxret: jnc no_error error: push ax From f2c8828a4b034eefdeaffa5d1e1c3234d542e74d Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Sun, 28 Mar 2021 12:28:57 +0000 Subject: [PATCH 6/8] plat/msdos86: copy stubs kill( ), signal( ), from plat/pc86 --- plat/msdos86/libsys/kill.c | 14 ++++++++++++++ plat/msdos86/libsys/signal.c | 15 +++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 plat/msdos86/libsys/kill.c create mode 100644 plat/msdos86/libsys/signal.c diff --git a/plat/msdos86/libsys/kill.c b/plat/msdos86/libsys/kill.c new file mode 100644 index 000000000..4a179c47c --- /dev/null +++ b/plat/msdos86/libsys/kill.c @@ -0,0 +1,14 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include + +int kill(pid_t pid, int sig) +{ + errno = EINVAL; + return -1; +} diff --git a/plat/msdos86/libsys/signal.c b/plat/msdos86/libsys/signal.c new file mode 100644 index 000000000..a87b9ced2 --- /dev/null +++ b/plat/msdos86/libsys/signal.c @@ -0,0 +1,15 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include +#include "libsys.h" + +sighandler_t signal(int signum, sighandler_t handler) +{ + return SIG_DFL; +} From a6c7ac2a280b895a7dfb29cda31510c757b50f77 Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Mon, 29 Mar 2021 15:15:13 +0000 Subject: [PATCH 7/8] plat/msdos86: add gettimeofday( ) --- plat/msdos86/include/ack/plat.h | 1 - plat/msdos86/libsys/gettimeofday.c | 92 ++++++++++++++++++++++++++++++ plat/msdos86/libsys/libsys.h | 18 +++++- plat/msdos86/libsys/sys_getdate.s | 25 ++++++++ plat/msdos86/libsys/sys_gettime.s | 25 ++++++++ 5 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 plat/msdos86/libsys/gettimeofday.c create mode 100644 plat/msdos86/libsys/sys_getdate.s create mode 100644 plat/msdos86/libsys/sys_gettime.s diff --git a/plat/msdos86/include/ack/plat.h b/plat/msdos86/include/ack/plat.h index a1e942cdd..4343f1cf8 100644 --- a/plat/msdos86/include/ack/plat.h +++ b/plat/msdos86/include/ack/plat.h @@ -7,6 +7,5 @@ #define _ACK_PLAT_H #define ACKCONF_WANT_O_TEXT_O_BINARY 1 -#define ACKCONF_WANT_EMULATED_TIME 0 #endif diff --git a/plat/msdos86/libsys/gettimeofday.c b/plat/msdos86/libsys/gettimeofday.c new file mode 100644 index 000000000..5cea28f0f --- /dev/null +++ b/plat/msdos86/libsys/gettimeofday.c @@ -0,0 +1,92 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +/* + * Derived from dos-gettimeofday.c for newlib-ia16 which was written for the + * gcc-ia16 toolchain. + * + * Copyright (c) 2017--2021 TK Chia + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + */ + +#include +#include +#include +#include +#include "libsys.h" + +extern long _timezone; +extern int _daylight; + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + static volatile bool tzinited = false; + struct _dosdate dosdt, olddt; + struct _dostime dostm; + suseconds_t usec; + struct tm tm; + time_t tim; + + if (!tzinited) + { + tzset(); + tzinited = true; + } + + memset(&tm, 0, sizeof tm); + + _sys_getdate(&dosdt); + do + { + uint8_t hour; + + _sys_gettime(&dostm); + tm.tm_hour = hour = dostm.hour; + tm.tm_min = dostm.minute; + tm.tm_sec = dostm.second; + usec = dostm.hsecond * 10000UL; + + /* If the time is close to midnight, read the date again to + * check for midnight crossover. If crossover happens, + * repeat until it stops. */ + if (hour) + break; + + olddt = dosdt; + _sys_getdate(&dosdt); + } while (dosdt.day != olddt.day); + + tm.tm_year = dosdt.year - 1900; + tm.tm_mon = dosdt.month - 1; + tm.tm_mday = dosdt.day; + tm.tm_isdst = -1; + + tim = mktime(&tm); + if (tim == -1) + return -1; + + if (tv) + { + tv->tv_sec = tim; + tv->tv_usec = usec; + } + + if (tz) + { + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} diff --git a/plat/msdos86/libsys/libsys.h b/plat/msdos86/libsys/libsys.h index 3488c332e..e6aad45de 100644 --- a/plat/msdos86/libsys/libsys.h +++ b/plat/msdos86/libsys/libsys.h @@ -41,6 +41,21 @@ extern struct _fdmodes _sys_fdmodes; #define _FDVECMASK (sizeof(_fdvec_t) * CHAR_BIT - 1) +/* Structures for getting MS-DOS's idea of the current date and time. */ + +struct _dosdate { + uint8_t day; + uint8_t month; + uint16_t year; +}; + +struct _dostime { + uint8_t minute; + uint8_t hour; + uint8_t hsecond; + uint8_t second; +}; + extern int _sys_getmode(int); extern int _sys_setmode(int, int); extern int _sys_iseof(int); @@ -53,6 +68,7 @@ extern int _sys_exists(const char *); extern int _sys_rawcreat(const char *, unsigned); extern int _sys_rawopen(const char *, int); extern off_t _sys_rawlseek(int, off_t, int); - +extern void _sys_getdate(struct _dosdate *); +extern void _sys_gettime(struct _dostime *); #endif diff --git a/plat/msdos86/libsys/sys_getdate.s b/plat/msdos86/libsys/sys_getdate.s new file mode 100644 index 000000000..41d69fc85 --- /dev/null +++ b/plat/msdos86/libsys/sys_getdate.s @@ -0,0 +1,25 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Get the current system date from MS-DOS. + +.define __sys_getdate +__sys_getdate: + movb ah, 0x2a + int 0x21 + mov bx, sp + mov bx, 2(bx) + mov (bx), dx + mov 2(bx), cx + ret diff --git a/plat/msdos86/libsys/sys_gettime.s b/plat/msdos86/libsys/sys_gettime.s new file mode 100644 index 000000000..d062fad8d --- /dev/null +++ b/plat/msdos86/libsys/sys_gettime.s @@ -0,0 +1,25 @@ +# +! $Source$ +! $State$ +! $Revision$ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +! Get the current system time from MS-DOS. + +.define __sys_gettime +__sys_gettime: + movb ah, 0x2c + int 0x21 + mov bx, sp + mov bx, 2(bx) + mov (bx), cx + mov 2(bx), dx + ret From 85953022b5febac9ab5787032b2883af2e18960e Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Thu, 1 Apr 2021 14:42:02 +0000 Subject: [PATCH 8/8] README: mention msdos86 target platform --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index c27385337..53630e755 100644 --- a/README +++ b/README @@ -37,6 +37,7 @@ linuxmips produces ELF executables for little-endian MIPS32r2 Linux systems cpm produces i80 CP/M .COM files rpi produces Raspberry Pi GPU binaries pdpv7 produces PDP/11 V7 Unix binaries +msdos86 produces i86 MS-DOS .COM files