From 0d2b55cd295930b3b77f326c7237ce11d9ceb06c Mon Sep 17 00:00:00 2001 From: Tee-Kiah Chia Date: Sat, 27 Mar 2021 19:30:38 +0000 Subject: [PATCH] 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; +}