# $State$
# $Revision$
+
The pc86 platform
=================
to change it to run in SMALL mode, where CS occupies one segment and DS and SS
another, which would give 64kB for nearly all programs; I just haven't done it.
-This port uses a syscall interface, which means you get *all* of the ACK's
-builtin libc with no shortcuts. File descriptors 0, 1 and 2 represent the
-console. All reads block. There's enough TTY emulation to allow \n conversion
-(set RAW to turn off) and local echo (unset ECHO to turn off); this is needed
-to make Basic work.
-
-Language support
-================
+This port only implements a very limited set of syscalls --- and most of those
+are stubs required to make the demo apps link. File descriptors 0, 1 and 2
+represent the console. All reads block. There's enough TTY emulation to allow
+\n conversion and local echo (but it can't be turned off).
-Tested with C (both), Basic, Pascal, Occam, Modula-2.
-
-Basic works, but because Basic programs require access to a data file to work,
-and pc86 doesn't (of course) have a file system, programs won't start unless
-you hack the compiler not to try and open it.
Example command line
====================
-ack -mpc86 -O -ansi -o pc86.img test.c
+ack -mpc86 -O -o pc86.img examples/paranoia.c
The file pc86.img can then be copied onto a floppy and booted, or run via qemu
or somesuch emulator.
+
David Given
dg@cowlark.com
finished:
mov si, running_msg
call write_string
- call pause
! Wipe the bss. (I'm a little suprised that __m_a_i_n doesn't do this.)
! Push standard parameters onto the stack and go.
- mov ax, 1
- push ax ! argc
- mov ax, 2
+ xor ax, ax
push ax ! argc
- mov ax, 3
+ push ax ! argv
push ax ! envp
call __m_a_i_n
! fall through into the exit routine.
.define begtext,begdata,begbss
-.define hol0,.trppc,.ignmask
.define ERANGE,ESET,EHEAP,ECASE,EILLINS,EIDIVZ,EODDZ
.extern _end
-.sect .data
-hol0:
- .data2 0,0
- .data2 0,0
-.ignmask:
- .data2 0
-.trppc:
- .data2 0
-
! Define symbols at the beginning of our various segments, so that we can find
! them. (Except .text, which has already been done.)
.sect .data; begdata:
.sect .rom; begrom:
.sect .bss; begbss:
+
+! Some magic data. All EM systems need these.
+
+.define .trppc, .ignmask
+.comm .trppc, 4
+.comm .ignmask, 4
var PLATFORMDIR={EM}/lib/{PLATFORM}
var CPP_F=-D__unix
var ALIGN=-a0:1 -a1:1 -a2:1 -a3:1
-var C_LIB={PLATFORMDIR}/libc-stdio-knr.a {PLATFORMDIR}/libc-knr.a
-var OLD_C_LIB={C_LIB}
var MACHOPT_F=-m8
+
+# Override the setting in fe so that files compiled for linux386 can see
+# the platform-specific headers.
+
+var C_INCLUDES=-I{PLATFORMDIR}/include -I{EM}/include/ansi
+
name be
from .m.g
to .s
mapflag -l* LNAME={PLATFORMDIR}/lib*
mapflag -i SEPID=-b1:0
mapflag -fp FLOATS={EM}/{ILIB}fp
- mapflag -ansi C_LIB={PLATFORMDIR}/libc-ansi.a
args {ALIGN} {SEPID?} \
(.e:{HEAD}={PLATFORMDIR}/boot.o) \
- ({RTS}:.ocm.b={PLATFORMDIR}/c-knr.o) \
- ({RTS}{ANSI?}:.c={PLATFORMDIR}/c-knr.o) \
- ({RTS}{ANSI?}:.cansi={PLATFORMDIR}/c-ansi.o) \
+ ({RTS}:.ocm.b={PLATFORMDIR}/c-ansi.o) \
+ ({RTS}:.c={PLATFORMDIR}/c-ansi.o) \
({RTS}:.mod={PLATFORMDIR}/modula2.o) \
({RTS}:.p={PLATFORMDIR}/pascal.o) \
-o > < \
(.b:{TAIL}={PLATFORMDIR}/libbasic.a) \
(.mod:{TAIL}={PLATFORMDIR}/libmodula2.a) \
(.ocm:{TAIL}={PLATFORMDIR}/liboccam.a) \
- (.ocm.b:{TAIL}={OLD_C_LIB}) \
- (.c:{TAIL}={C_LIB}) \
+ (.ocm.b.mod.c.p:{TAIL}={PLATFORMDIR}/libc.a) \
{FLOATS?} \
(.e:{TAIL}={PLATFORMDIR}/libsys.a \
- {PLATFORMDIR}/libmon.a \
- {PLATFORMDIR}/libsys.a \
{PLATFORMDIR}/libem.a \
{PLATFORMDIR}/libend.a)
linker
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#ifndef _ACK_CONFIG_H
+#define _ACK_CONFIG_H
+
+/* We're providing a time() system call rather than wanting a wrapper around
+ * gettimeofday() in the libc. */
+
+#define ACKCONF_TIME_IS_A_SYSCALL
+
+#endif
--- /dev/null
+/*
+ * unistd.h - standard system calls
+ */
+/* $Id$ */
+
+#ifndef _UNISTD_H
+#define _UNISTD_H
+
+#include <stddef.h>
+
+/* Types */
+
+typedef int pid_t;
+typedef int mode_t;
+
+/* Constants for file access (open and friends) */
+
+enum
+{
+ O_ACCMODE = 0x3,
+
+ O_RDONLY = 0,
+ O_WRONLY = 1,
+ O_RDWR = 2,
+
+ O_CREAT = 0100,
+ O_TRUNC = 01000,
+ O_APPEND = 02000,
+ O_NONBLOCK = 04000
+};
+
+/* Special variables */
+
+extern char** environ;
+
+/* Implemented system calls */
+
+extern void _exit(int);
+extern pid_t getpid(void);
+extern void* sbrk(intptr_t increment);
+extern int isatty(int d);
+extern off_t lseek(int fildes, off_t offset, int whence);
+extern int close(int d);
+extern int open(const char* path, int access, ...);
+extern int creat(const char* path, mode_t mode);
+extern int read(int fd, void* buffer, size_t count);
+extern int write(int fd, void* buffer, size_t count);
+
+/* Unimplemented system calls (these are just prototypes to let the library
+ * compile). */
+
+extern int fcntl(int fd, int op, ...);
+
+/* Signal handling */
+
+typedef int sig_atomic_t;
+
+#define SIG_ERR ((__sighandler_t) -1) /* Error return. */
+#define SIG_DFL ((__sighandler_t) 0) /* Default action. */
+#define SIG_IGN ((__sighandler_t) 1) /* Ignore signal. */
+
+#define SIGABRT 6 /* Abort (ANSI). */
+
+#define _NSIG 32 /* Biggest signal number + 1
+ (not including real-time signals). */
+typedef void (*sighandler_t)(int);
+extern sighandler_t signal(int signum, sighandler_t handler);
+extern int raise(int signum);
+
+#endif
+++ /dev/null
-#
-! $Source$
-! $State$
-! $Revision$
-
-! Declare segments (the order is important).
-
-.sect .text
-.sect .rom
-.sect .data
-.sect .bss
-
-! This file contains the code necessary to extend the ACK heap. This is called
-! by a i86/libem helper function called .strhp, which takes care of updating
-! some magic global variables --- defined here.
-
-! Pointer to the current top of the heap.
-
-.sect .data
-.define .reghp
-.reghp:
- .data2 endbss
-
-! Pointer to the current top of memory.
-
-.sect .data
-.define .limhp
-.limhp:
- .data2 endbss
-
-! Claims more memory from the system, but does not actually change those
-! global variables (.strhp does that). This does not use the C calling
-! convention!
-!
-! Stack: ( desired_limhp : actual_limhp )
-! Also returns: ax = -1 on failure
-
-.sect .text
-.define BRK
-BRK:
- pop bx ! holds return address
- pop ax ! holds desired limhp
-
- cmp ax, sp ! compare sp with si
- jae fail ! si too big? (Overlaps stack?)
- cmp ax, endbss ! compare with bottom of heap
- jb fail ! si too small? (Overlaps bss?)
-
-return:
- push ax ! success
- jmp bx
-
-fail:
- mov ax, -1
- jmp return
--- /dev/null
+#
+! $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
+++ /dev/null
-#
-! $Source$
-! $State$
-! $Revision$
-
-! Declare segments (the order is important).
-
-.sect .text
-.sect .rom
-.sect .data
-.sect .bss
-
-.sect .bss
-
-.define __sys_params_in
-.comm __sys_params_in, 6
-
-.define __sys_params_out
-.comm __sys_params_out, 2
-
-.comm opcode, 2
-.comm returnto, 2
-
-.sect .text
-
-
-! Called on system call. This does *not* use the C calling convention:
-! ax: syscall number
-! stack: ( param3 param2 param1 - result )
-
-.define .mon
-.mon:
- mov (opcode), ax
- pop (returnto)
-
- cmp ax, 1
- je exit
- cmp ax, 3
- je read
- cmp ax, 4
- je write
- cmp ax, 5
- je open
- cmp ax, 6
- je nop_1 ! close
- cmp ax, 20
- je nop_0 ! getpid
- cmp ax, 35
- je nop_1 ! time
- cmp ax, 48
- je sigtrp
- cmp ax, 54
- je ioctl
-
- ! Syscall not supported --- write out an error message and halt.
-
-unsupported:
- mov si, msg
-1:
- lodsb
- andb al, al
- jz 2f
- movb ah, 0xE ! service
- mov bx, 0x0007 ! page 0, white
- int 0x10
- jmp 1b
-2:
-
- ! Write out the syscall number.
-
- mov dx, (opcode)
- mov cx, 4 ! 4 hex digits
-1:
- rol dx, 1 ! rotate so that highest 4 bits are at the bottom
- rol dx, 1
- rol dx, 1
- rol dx, 1
- mov ax, 0xE0F ! ah = request, al = mask for nybble
- andb al, dl
- addb al, 0x90 ! convert al to ascii hex (four instructions)
- daa
- adcb al, 0x40
- daa
- int 0x10
- loop 1b
-
- ! Exit.
-
- jmp EXIT
-
-.sect .rom
-msg:
- .asciz 'NOSYS'
-
-.sect .text
-
-exit:
- jmp EXIT
-
-read:
- mov ax, __sys_read
- jmp in_3_out_1
-
-write:
- mov ax, __sys_write
- jmp in_3_out_1
-
-open:
- add sp, 2*2
- jmp unimplemented
-
-ioctl:
- mov ax, __sys_ioctl
- jmp in_3_out_0
-
-sigtrp:
- add sp, 4
- jmp unimplemented
-
-in_3_out_0:
- pop (__sys_params_in+0)
- pop (__sys_params_in+2)
- pop (__sys_params_in+4)
- call ax
- jmp out_0
-
-in_3_out_1:
- pop (__sys_params_in+0)
- pop (__sys_params_in+2)
- pop (__sys_params_in+4)
- call ax
- jmp out_1
-
-out_0:
- or ax, ax
- jnz failed
- push ax
- jmp return
-
-out_1:
- or ax, ax
- jnz failed
- push (__sys_params_out)
- push ax
- jmp return
-
-unimplemented:
- mov ax, EBADMON
-failed:
- push ax
- push ax
- jmp return
-
-nop_1:
- add sp, 1*2
- jmp nop_0
-nop_3:
- add sp, 3*2
-nop_0:
- mov ax, 0
- push ax
- jmp return
-
-return:
- jmp (returnto)
-
\ No newline at end of file
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#define OUT_OF_MEMORY (void*)(-1) /* sbrk returns this on failure */
+#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))
+ return -1;
+
+ current = p;
+ return 0;
+}
+
+void* sbrk(intptr_t increment)
+{
+ char* old;
+
+ if (increment == 0)
+ return current;
+
+ old = current;
+ if (brk(old + increment) < 0)
+ return OUT_OF_MEMORY;
+
+ return old;
+}
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+int close(int fd)
+{
+ errno = EBADF;
+ return -1;
+}
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include "libsys.h"
+
+int open(const char* path, int access, ...)
+{
+ errno = EACCES;
+ return -1;
+}
.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
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+pid_t getpid(void)
+{
+ return 0;
+}
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+int isatty(int fd)
+{
+ return 1;
+}
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+int kill(pid_t pid, int sig)
+{
+ errno = EINVAL;
+ return -1;
+}
extern void _sys_write_tty(char c);
-extern int _sys_ttyflags;
+/* extern int _sys_ttyflags; */
#endif
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+off_t lseek(int fd, off_t offset, int whence)
+{
+ errno = EINVAL;
+ return -1;
+}
--- /dev/null
+/* $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);
+}
ACKINCLUDES = {"%BINDIR%include"},
ackfile (d.."errno.s"),
- ackfile (d.."_mon.s"),
- ackfile (d.."_brk.s"),
+ ackfile (d.."_hol0.s"),
ackfile (d.."_sys_rawread.s"),
ackfile (d.."_sys_rawwrite.s"),
- ackfile (d.."_sys_read.c"),
- ackfile (d.."_sys_write.c"),
- ackfile (d.."_sys_ioctl.c"),
+ ackfile (d.."open.c"),
+ ackfile (d.."creat.c"),
+ ackfile (d.."close.c"),
+ ackfile (d.."read.c"),
+ ackfile (d.."write.c"),
+-- ackfile (d.."_sys_ioctl.c"),
+ ackfile (d.."brk.c"),
+ ackfile (d.."getpid.c"),
+ ackfile (d.."kill.c"),
+ ackfile (d.."isatty.c"),
+ ackfile (d.."lseek.c"),
+ ackfile (d.."time.c"),
+ ackfile (d.."signal.c"),
install = pm.install("%BINDIR%lib/%PLATFORM%/libsys.a"),
}
#include <stdlib.h>
#include <errno.h>
-#include <sgtty.h>
+#include <unistd.h>
#include "libsys.h"
-extern struct
-{
- int fd;
- char* buffer;
- size_t count;
-} _sys_params_in;
-
-extern struct
-{
- size_t bytesread;
-} _sys_params_out;
-
-#define P _sys_params_in
-
-int _sys_read(void)
+int read(int fd, void* buffer, size_t count)
{
char i;
/* We're only allowed to read from fd 0, 1 or 2. */
- if ((P.fd < 0) || (P.fd > 2))
- return EBADF;
+ if ((fd < 0) || (fd > 2))
+ {
+ errno = EBADF;
+ return -1;
+ }
/* Empty buffer? */
- if (P.count == 0)
- {
- _sys_params_out.bytesread = 0;
+ if (count == 0)
return 0;
- }
/* Read one byte. */
i = _sys_rawread();
+#if 0
if ((i == '\r') && !(_sys_ttyflags & RAW))
i = '\n';
if (_sys_ttyflags & ECHO)
_sys_write_tty(i);
+#endif
+ if (i == '\r')
+ i = '\n';
+ _sys_write_tty(i);
- *P.buffer = i;
-
- _sys_params_out.bytesread = 1;
- return 0;
+ *(char*)buffer = i;
+ return 1;
}
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include "libsys.h"
+
+sighandler_t signal(int signum, sighandler_t handler)
+{
+ return SIG_DFL;
+}
--- /dev/null
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include "libsys.h"
+
+time_t time(time_t* t)
+{
+ if (t)
+ *t = 0;
+ return 0;
+}
#include <stdlib.h>
#include <errno.h>
-#include <sgtty.h>
+#include <unistd.h>
#include "libsys.h"
-extern struct
-{
- int fd;
- const char* buffer;
- size_t count;
-} _sys_params_in;
-
-extern struct
-{
- size_t byteswritten;
-} _sys_params_out;
-
-#define P _sys_params_in
-
void _sys_write_tty(char c)
{
_sys_rawwrite(c);
+#if 0
if ((c == '\n') && !(_sys_ttyflags & RAW))
_sys_rawwrite('\r');
+#endif
+ if (c == '\n')
+ _sys_rawwrite('\r');
}
-int _sys_write(void)
+int write(int fd, void* buffer, size_t count)
{
int i;
+ char* p = buffer;
/* We're only allowed to write to fd 0, 1 or 2. */
- if ((P.fd < 0) || (P.fd > 2))
- return EBADF;
+ if ((fd < 0) || (fd > 2))
+ {
+ errno = EBADF;
+ return -1;
+ }
/* Write all data. */
i = 0;
- while (i < P.count)
+ while (i < count)
{
- _sys_write_tty(*P.buffer++);
+ _sys_write_tty(*p++);
i++;
}
/* No failures. */
- _sys_params_out.byteswritten = P.count;
- return 0;
+ return count;
}
install = pm.install(d.."descr", "%BINDIR%%PLATIND%/%PLATFORM%/descr")
}
+local headers = group {
+ install = {
+ pm.install(d.."include/ack/config.h", "%BINDIR%%PLATIND%/%PLATFORM%/include/ack/config.h"),
+ pm.install(d.."include/unistd.h", "%BINDIR%%PLATIND%/%PLATFORM%/include/unistd.h"),
+ }
+}
+
platform_pc86 = group {
ARCH = "i86",
PLATFORM = "pc86",
OPTIMISATION = "-O",
- -- Ensure the descr file is installed first because we'll need it
- -- to build the libraries.
+ -- Ensure the descr and headers are installed first because we'll need
+ -- them to build the libraries.
descr,
+ headers,
-- Build the back-end support.