plat/msdos86: add isatty( ), _setmode( ), read( ), write( )

This commit is contained in:
Tee-Kiah Chia 2021-03-27 19:30:38 +00:00
parent 2dfddf3fa8
commit 0d2b55cd29
22 changed files with 657 additions and 2 deletions

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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

59
plat/msdos86/libsys/brk.c Normal file
View file

@ -0,0 +1,59 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#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;
}

View file

@ -0,0 +1,15 @@
acklibrary {
name = "lib",
srcs = {
"./*.c",
"./*.s",
},
deps = {
"lang/cem/libcc.ansi/headers+headers",
"plat/msdos86/include+headers",
},
vars = {
plat = "msdos86"
}
}

View file

@ -0,0 +1,14 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include "libsys.h"
int creat(const char* path, int mode)
{
return open(path, O_CREAT|O_WRONLY|O_TRUNC, mode);
}

View file

@ -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

View file

@ -0,0 +1,53 @@
/* $Source$
* $State$
* $Revision$
*/
#ifndef LIBSYS_H
#define LIBSYS_H
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
#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

View file

@ -0,0 +1,77 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#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;
}

View file

@ -0,0 +1,23 @@
/* $Source$
* $State$
* $Revision$
*/
#include <errno.h>
#include <fcntl.h>
#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);
}

View file

@ -0,0 +1,13 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdlib.h>
#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 };

View file

@ -0,0 +1,27 @@
/* $Source$
* $State$
* $Revision$
*/
#include <fcntl.h>
#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;
}

View file

@ -6,8 +6,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define OUT_OF_MEMORY (void*)(-1) /* sbrk returns this on failure */
#include "libsys.h"
struct for_main {
int argc;

View file

@ -0,0 +1,27 @@
/* $Source$
* $State$
* $Revision$
*/
#include <fcntl.h>
#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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,42 @@
/* $Source$
* $State$
* $Revision$
*/
#include <fcntl.h>
#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;
}

View file

@ -0,0 +1,55 @@
/* $Source$
* $State$
* $Revision$
*/
#include <fcntl.h>
#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;
}
}

View file

@ -0,0 +1,61 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#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;
}