ref: 147d51bf7c6cd8798ddb26e33d857be13a66c548
parent: ef6ce918c04fc82bea759fd843cc09e273ec9f6c
author: henesy <devnull@localhost>
date: Sun Nov 4 06:33:14 EST 2018
add utils and such
--- /dev/null
+++ b/emu/DragonFly/README
@@ -1,0 +1,1 @@
+DragonFly BSD changes contributed by fgudin and extrudedaluminiu (see issue 181 at inferno-os.googlecode.com)
--- /dev/null
+++ b/emu/DragonFly/asm-386.S
@@ -1,0 +1,107 @@
+ .file "asm-DragonFly-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+ .type ournewstack,@function
+ .global executeonnewstack
+executeonnewstack:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ movl 8(%ebp), %esi /* get tos */
+ subl $4, %esi
+ movl 16(%ebp), %eax
+ movl %eax, (%esi) /* stash arg on new stack */
+ subl $4, %esi
+ movl 12(%ebp), %eax
+ movl %eax, (%esi) /* stash tramp on new stack */
+ mov %esi, %esp /* swap stacks pronto */
+ popl %eax /* recover the tramp address */
+ call *%eax /* and jump to it (ho ho) */
+
+ /* if we return here, tramp didn't do it's job */
+
+ addl $8, %esp /* clean up for pose value */
+
+ leal SYS_exit, %eax
+ int $0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ * between the unlock and the system call, but this should
+ * not matter since no task is waiting for the result
+ */
+
+ .type unlockandexit,@function
+ .global unlockandexit
+unlockandexit:
+ pushl %ebp
+ movl %esp, %ebp
+
+ movl 8(%ebp), %esi /* get the key address */
+ pushl $0 /* exit status 0 */
+ movl $0, %eax /* unlock the stack allocator */
+ movl %eax, (%esi)
+ leal SYS_exit, %eax /* call exit */
+ int $0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type getcallerpc,@function
+ .global getcallerpc
+getcallerpc:
+ movl 4(%ebp), %eax
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/DragonFly/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_SPEAKER
+#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT
+#define Audio_Lineout_Val SOUND_MIXER_VOLUME
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING "/dev/dsp"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file = -1; /* file in/out */
+static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */
+static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ int i;
+ static ushort flag = 1;
+
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+ for (i = 0; i < 32; i++)
+ mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ char ebuf[ERRMAX];
+
+ if (debug)
+ print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file;
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+ if (debug)
+ print("audio_file_open: success\nin %d out %d both %d\n",
+ audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_out < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ qunlock(&outlock);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_in < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ audio_file_out = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ close(audio_file);
+ audio_file_in = audio_file_out = audio_file = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(!audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ if (debug > 1)
+ print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+ c, va, count, offset);
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+static int
+audio_open(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ if (debug)
+ print("audio_open: failed to set blocking\n");
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if (debug)
+ print("audio_open: blocking set\n");
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(! audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+ int status;
+ osenter();
+ status = ioctl(fd, ctl, info); /* qlock and load general stuff */
+ osleave();
+ if (status < 0)
+ print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+ return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+ int newbits, newenc;
+
+ newbits = i->bits;
+ newenc = i->enc;
+ switch (newenc) {
+ case Audio_Alaw_Val:
+ if (newbits == 8)
+ return AFMT_A_LAW;
+ break;
+ case Audio_Ulaw_Val:
+ if (newbits == 8)
+ return AFMT_MU_LAW;
+ break;
+ case Audio_Pcm_Val:
+ if (newbits == 8)
+ return AFMT_U8;
+ else if (newbits == 16)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+
+ if(fd < 0)
+ return 0;
+
+ /* fmt */
+ if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+ int oldfmt, newfmt;
+ oldfmt = AFMT_QUERY;
+ if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+ return 0;
+ if (debug)
+ print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+ newfmt = choosefmt(i);
+ if (debug)
+ print("audio_set_info: new format 0x%.8lux\n", newfmt);
+ if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG) {
+ int channels = i->chan;
+ if (debug)
+ print("audio_set_info: new channels %d\n", channels);
+ if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+ || channels != i->chan)
+ return 0;
+ }
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG) {
+ int speed = i->rate;
+ if (debug)
+ print("audio_set_info: new speed %d\n", speed);
+ if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+ return 0;
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+ int val;
+ if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+ mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+ if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+ mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+ val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+ doioctl(fd, MIXER_WRITE(i->dev), &val);
+ }
+
+ if (i->flags & AUDIO_DEV_FLAG) {
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ USED(fd);
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ USED(fd);
+ USED(f);
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int x;
+ return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/DragonFly/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/DragonFly/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/cuaa0",
+ "/dev/cuaa1",
+ "/dev/cuaa2",
+ "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/DragonFly/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/DragonFly/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/DragonFly/emu-g
@@ -1,0 +1,97 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif-posix ipaux
+ eia
+ mem
+
+lib
+ interp
+ math
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y) { USED(x); USED(y); }
+ ulong strtochan(char *s) { USED(s); return ~0; }
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/DragonFly/mkfile
@@ -1,0 +1,46 @@
+SYSTARG=DragonFly
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=DragonFly
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/DragonFly/os.c
@@ -1,0 +1,530 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef getwd
+#include <signal.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <sched.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+enum
+{
+ DELETE = 0x7F,
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "DragonFly";
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ void *kstack;
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(up->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", up->text, msg);
+
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ kstack = p->kstack;
+ free(p->prog);
+ free(p);
+ if(kstack != nil)
+ stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+ if(info)
+ print("trapBUS: signo: %d code: %d addr: %lx\n",
+ info->si_signo, info->si_code, info->si_addr);
+ else
+ print("trapBUS: no info\n");
+ disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+ USED(signo);
+ /* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+ char buf[64];
+ USED(signo);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+ disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+ struct sigaction act;
+ sigset_t mask;
+
+ memset(&act, 0 , sizeof(act));
+ sigemptyset(&initmask);
+
+ signal(SIGPIPE, SIG_IGN); /* prevent signal when devcmd child exits */
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+
+ act.sa_handler = trapUSR1;
+ act.sa_mask = initmask;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = trapUSR2;
+ sigaction(SIGUSR2, &act, nil);
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigaddset(&initmask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ /*
+ * prevent Zombies forming when any process terminates
+ */
+ act.sa_sigaction = 0;
+ act.sa_flags |= SA_NOCLDWAIT;
+ if(sigaction(SIGCHLD, &act, nil))
+ panic("sigaction SIGCHLD");
+
+ if(sflag == 0) {
+ act.sa_sigaction = trapBUS;
+ act.sa_flags |= SA_SIGINFO;
+ if(sigaction(SIGBUS, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapILL;
+ if(sigaction(SIGILL, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapSEGV;
+ if(sigaction(SIGSEGV, &act, nil))
+ panic("sigaction SIGSEGV");
+ act.sa_handler = trapFPE;
+ if(sigaction(SIGFPE, &act, nil))
+ panic("sigaction SIGFPE");
+ if(sigaddset(&initmask, SIGINT) == -1)
+ panic("sigaddset");
+ }
+ if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+ panic("sigprocmask");
+}
+
+static int
+tramp(void *arg)
+{
+ Proc *p;
+
+ p = arg;
+ p->pid = p->sigid = getpid();
+ sigprocmask(SIG_BLOCK, &initmask, nil); /* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+ (*p->func)(p->arg);
+ pexit("{Tramp}", 0);
+ _exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ int pid;
+ void *tos;
+
+ p = newproc();
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(flags & KPX11){
+ p->kstack = nil; /* never freed; also up not defined */
+ tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+ }else
+ p->kstack = stackalloc(p, &tos);
+ pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+ if(pid < 0)
+ panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGUSR2);
+ sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+ if(kill(p->sigid, SIGUSR2) < 0)
+ fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+ struct passwd *pwd;
+
+ if(pwd = getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+libinit(char *imod)
+{
+ struct passwd *pw;
+ Proc *p;
+ void *tos;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ if(dflag == 0)
+ termset();
+
+ setsigs();
+
+ p = newproc();
+ p->kstack = stackalloc(p, &tos);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+ if(sec0==0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec / 1000;
+ time.tv_nsec = (milsec % 1000) * 1000000;
+ nanosleep(&time, 0);
+ return 0;
+}
+
+void
+osyield(void)
+{
+ sched_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+ Lock l;
+ void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+ *((void **)stack) = stacklist.free;
+ stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+ lock(&stacklist.l);
+ _stackfree(stack);
+ unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+ void *rv;
+ lock(&stacklist.l);
+ if (stacklist.free == 0) {
+ int x;
+ /*
+ * obtain some more by using sbrk()
+ */
+ void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+ if (more == 0)
+ panic("stackalloc: no more stacks");
+ /*
+ * align to KSTACK
+ */
+ more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+ /*
+ * free all the new stacks onto the freelist
+ */
+ for (x = 0; x < NSTACKSPERALLOC; x++)
+ _stackfree((char *)more + KSTACK * x);
+ }
+ rv = stacklist.free;
+ stacklist.free = *(void **)rv;
+ unlock(&stacklist.l);
+ *tos = rv + KSTACK - sizeof(void*);
+ *(Proc **)rv = p;
+ return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a);
+ USED(n);
+ return 0;
+}
--- /dev/null
+++ b/emu/FreeBSD/asm-386.S
@@ -1,0 +1,107 @@
+ .file "asm-FreeBSD-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+ .type ournewstack,@function
+ .global executeonnewstack
+executeonnewstack:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ movl 8(%ebp), %esi /* get tos */
+ subl $4, %esi
+ movl 16(%ebp), %eax
+ movl %eax, (%esi) /* stash arg on new stack */
+ subl $4, %esi
+ movl 12(%ebp), %eax
+ movl %eax, (%esi) /* stash tramp on new stack */
+ mov %esi, %esp /* swap stacks pronto */
+ popl %eax /* recover the tramp address */
+ call *%eax /* and jump to it (ho ho) */
+
+ /* if we return here, tramp didn't do it's job */
+
+ addl $8, %esp /* clean up for pose value */
+
+ leal SYS_exit, %eax
+ int $0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ * between the unlock and the system call, but this should
+ * not matter since no task is waiting for the result
+ */
+
+ .type unlockandexit,@function
+ .global unlockandexit
+unlockandexit:
+ pushl %ebp
+ movl %esp, %ebp
+
+ movl 8(%ebp), %esi /* get the key address */
+ pushl $0 /* exit status 0 */
+ movl $0, %eax /* unlock the stack allocator */
+ movl %eax, (%esi)
+ leal SYS_exit, %eax /* call exit */
+ int $0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type getcallerpc,@function
+ .global getcallerpc
+getcallerpc:
+ movl 4(%ebp), %eax
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/FreeBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_SPEAKER
+#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT
+#define Audio_Lineout_Val SOUND_MIXER_VOLUME
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING "/dev/dsp"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file = -1; /* file in/out */
+static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */
+static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ int i;
+ static ushort flag = 1;
+
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+ for (i = 0; i < 32; i++)
+ mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ char ebuf[ERRMAX];
+
+ if (debug)
+ print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file;
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+ if (debug)
+ print("audio_file_open: success\nin %d out %d both %d\n",
+ audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_out < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ qunlock(&outlock);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_in < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ audio_file_out = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ close(audio_file);
+ audio_file_in = audio_file_out = audio_file = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(! audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ if (debug > 1)
+ print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+ c, va, count, offset);
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+static int
+audio_open(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ if (debug)
+ print("audio_open: failed to set blocking\n");
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if (debug)
+ print("audio_open: blocking set\n");
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(! audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+ int status;
+ osenter();
+ status = ioctl(fd, ctl, info); /* qlock and load general stuff */
+ osleave();
+ if (status < 0)
+ print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+ return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+ int newbits, newenc;
+
+ newbits = i->bits;
+ newenc = i->enc;
+ switch (newenc) {
+ case Audio_Alaw_Val:
+ if (newbits == 8)
+ return AFMT_A_LAW;
+ break;
+ case Audio_Ulaw_Val:
+ if (newbits == 8)
+ return AFMT_MU_LAW;
+ break;
+ case Audio_Pcm_Val:
+ if (newbits == 8)
+ return AFMT_U8;
+ else if (newbits == 16)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+
+ if(fd < 0)
+ return 0;
+
+ /* fmt */
+ if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+ int oldfmt, newfmt;
+ oldfmt = AFMT_QUERY;
+ if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+ return 0;
+ if (debug)
+ print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+ newfmt = choosefmt(i);
+ if (debug)
+ print("audio_set_info: new format 0x%.8lux\n", newfmt);
+ if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG) {
+ int channels = i->chan;
+ if (debug)
+ print("audio_set_info: new channels %d\n", channels);
+ if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+ || channels != i->chan)
+ return 0;
+ }
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG) {
+ int speed = i->rate;
+ if (debug)
+ print("audio_set_info: new speed %d\n", speed);
+ if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+ return 0;
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+ int val;
+ if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+ mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+ if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+ mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+ val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+ doioctl(fd, MIXER_WRITE(i->dev), &val);
+ }
+
+ if (i->flags & AUDIO_DEV_FLAG) {
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ USED(fd);
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ USED(fd);
+ USED(f);
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int x;
+ return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/FreeBSD/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/FreeBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/cuaa0",
+ "/dev/cuaa1",
+ "/dev/cuaa2",
+ "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/FreeBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/FreeBSD/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/FreeBSD/emu-g
@@ -1,0 +1,102 @@
+env
+ X11LIBS=
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ math
+
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/FreeBSD/mkfile
@@ -1,0 +1,48 @@
+SYSTARG=FreeBSD
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=FreeBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+X11LIBS= -lX11 -lXext
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm $X11LIBS
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/FreeBSD/mkfile-FreeBSD
@@ -1,0 +1,17 @@
+#
+# architecture-dependent files for FreeBSD
+#
+
+LDFLAGS=
+
+TARGFILES=devfs-posix.$O\
+ deveia-FreeBSD.$O\
+ devip.$O\
+ ipif-posix.$O\
+ os-FreeBSD.$O\
+ win-x11.$O\
+ srv.$O\
+ lock.$O\
+ asm-FreeBSD-$OBJTYPE.$O
+
+SYSLIBS=/usr/X11R6/lib/libX11.a -lm
--- /dev/null
+++ b/emu/FreeBSD/os.c
@@ -1,0 +1,531 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef getwd
+#include <signal.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <sched.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+enum
+{
+ DELETE = 0x7F,
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "FreeBSD";
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ void *kstack;
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(up->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", up->text, msg);
+
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ kstack = p->kstack;
+ free(p->prog);
+ free(p);
+ if(kstack != nil)
+ stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+ if(info)
+ print("trapBUS: signo: %d code: %d addr: %lx\n",
+ info->si_signo, info->si_code, info->si_addr);
+ else
+ print("trapBUS: no info\n");
+ disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+ USED(signo);
+ /* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+
+ char buf[64];
+ USED(signo);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+ disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+ struct sigaction act;
+ sigset_t mask;
+
+ memset(&act, 0 , sizeof(act));
+ sigemptyset(&initmask);
+
+ signal(SIGPIPE, SIG_IGN); /* prevent signal when devcmd child exits */
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+
+ act.sa_handler = trapUSR1;
+ act.sa_mask = initmask;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = trapUSR2;
+ sigaction(SIGUSR2, &act, nil);
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigaddset(&initmask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ /*
+ * prevent Zombies forming when any process terminates
+ */
+ act.sa_sigaction = 0;
+ act.sa_flags |= SA_NOCLDWAIT;
+ if(sigaction(SIGCHLD, &act, nil))
+ panic("sigaction SIGCHLD");
+
+ if(sflag == 0) {
+ act.sa_sigaction = trapBUS;
+ act.sa_flags |= SA_SIGINFO;
+ if(sigaction(SIGBUS, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapILL;
+ if(sigaction(SIGILL, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapSEGV;
+ if(sigaction(SIGSEGV, &act, nil))
+ panic("sigaction SIGSEGV");
+ act.sa_handler = trapFPE;
+ if(sigaction(SIGFPE, &act, nil))
+ panic("sigaction SIGFPE");
+ if(sigaddset(&initmask, SIGINT) == -1)
+ panic("sigaddset");
+ }
+ if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+ panic("sigprocmask");
+}
+
+static int
+tramp(void *arg)
+{
+ Proc *p;
+
+ p = arg;
+ p->pid = p->sigid = getpid();
+ sigprocmask(SIG_BLOCK, &initmask, nil); /* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+ (*p->func)(p->arg);
+ pexit("{Tramp}", 0);
+ _exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ int pid;
+ void *tos;
+
+ p = newproc();
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(flags & KPX11){
+ p->kstack = nil; /* never freed; also up not defined */
+ tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+ }else
+ p->kstack = stackalloc(p, &tos);
+ pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+ if(pid < 0)
+ panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGUSR2);
+ sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+ if(kill(p->sigid, SIGUSR2) < 0)
+ fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+ struct passwd *pwd;
+
+ if(pwd = getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+libinit(char *imod)
+{
+ struct passwd *pw;
+ Proc *p;
+ void *tos;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ if(dflag == 0)
+ termset();
+
+ setsigs();
+
+ p = newproc();
+ p->kstack = stackalloc(p, &tos);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+ if(sec0==0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec / 1000;
+ time.tv_nsec = (milsec % 1000) * 1000000;
+ nanosleep(&time, 0);
+ return 0;
+}
+
+void
+osyield(void)
+{
+ sched_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+ Lock l;
+ void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+ *((void **)stack) = stacklist.free;
+ stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+ lock(&stacklist.l);
+ _stackfree(stack);
+ unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+ void *rv;
+ lock(&stacklist.l);
+ if (stacklist.free == 0) {
+ int x;
+ /*
+ * obtain some more by using sbrk()
+ */
+ void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+ if (more == 0)
+ panic("stackalloc: no more stacks");
+ /*
+ * align to KSTACK
+ */
+ more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+ /*
+ * free all the new stacks onto the freelist
+ */
+ for (x = 0; x < NSTACKSPERALLOC; x++)
+ _stackfree((char *)more + KSTACK * x);
+ }
+ rv = stacklist.free;
+ stacklist.free = *(void **)rv;
+ unlock(&stacklist.l);
+ *tos = rv + KSTACK - sizeof(void*);
+ *(Proc **)rv = p;
+ return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a);
+ USED(n);
+ return 0;
+}
--- /dev/null
+++ b/emu/Irix/NOTE
@@ -1,0 +1,1 @@
+for some strange reason, it lacks deveia!
--- /dev/null
+++ b/emu/Irix/asm-mips.s
@@ -1,0 +1,31 @@
+#include <sys/regdef.h>
+#include <sys/asm.h>
+
+LEAF(FPsave)
+ cfc1 t0, $31
+ sw t0, 0(a0) /* a0 is argument */
+ j $31
+ END(FPsave)
+
+LEAF(FPrestore)
+ lw t0, 0(a0) /* a0 is argument */
+ ctc1 t0, $31
+ j $31
+ END(FPrestore)
+
+
+/*
+ * lock from r4000 book
+ */
+LEAF(_tas)
+ .set noreorder
+1:
+ ll v0,0(a0) /* a0 is argument */
+ or t1, v0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ nop
+ .set reorder
+ END(_tas)
--- /dev/null
+++ b/emu/Irix/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Irix/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Irix/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+# eia
+# audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Irix/mkfile
@@ -1,0 +1,49 @@
+SYSTARG=Irix
+OBJTYPE=mips
+<../../mkconfig
+SYSTARG=Irix
+OBJTYPE=mips
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+LIBNAMES=${LIBS:%=lib%.a}
+#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lfpe -lm -lX11 -lXext
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+<../port/portmkfile
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/Irix/mkfile-Irix
@@ -1,0 +1,14 @@
+#
+# architecture-dependent files for IRIX
+#
+
+TARGFILES=asm-Irix.$O\
+ devfs-posix.$O\
+ devip.$O\
+ ipif-posix.$O\
+ os-Irix.$O\
+ win-x11.$O\
+ srv.$O\
+ lock.$O\
+
+SYSLIBS= -lfpe -lm -lX11
--- /dev/null
+++ b/emu/Irix/os.c
@@ -1,0 +1,475 @@
+/* Link with -lfpe. See man pages for fpc
+ * and /usr/include/sigfpe.h, sys/fpu.h.
+ */
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <time.h>
+#include <ulocks.h>
+#include <termios.h>
+#include <sigfpe.h>
+#include <sys/prctl.h>
+#include <sys/fpu.h>
+#include <sys/cachectl.h>
+#undef _POSIX_SOURCE /* SGI incompetence */
+#include <signal.h>
+#define _BSD_TIME
+/* for gettimeofday(), which isn't POSIX,
+ * but is fairly common
+ */
+#include <sys/time.h>
+#define _POSIX_SOURCE
+#include <pwd.h>
+
+extern int rebootargc;
+extern char** rebootargv;
+
+int gidnobody = -1;
+int uidnobody = -1;
+Proc** Xup;
+
+#define MAXSPROC 30000 /* max procs == MAXPID */
+static int sproctbl[MAXSPROC];
+
+enum
+{
+ KSTACK = 64*1024,
+ DELETE = 0x7F
+};
+char *hosttype = "Irix";
+char *cputype = "mips";
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+
+ sproctbl[getpid()] = -1;
+
+ unlock(&procs.l);
+
+/* print("pexit: %s: %s\n", up->text, msg); /**/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(up->prog);
+ free(up);
+ exit(0);
+}
+
+static void
+tramp(void *p, size_t stacksz)
+{
+ up = p;
+ up->sigid = getpid();
+ up->func(up->arg);
+ pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ int pid;
+ int id;
+ int i;
+
+ p = newproc();
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+
+ for(i = 1; i < MAXSPROC; i++) {
+ if(sproctbl[i] == -1) {
+ break;
+ }
+ }
+
+ if(i==MAXSPROC)
+ return -1;
+
+ sproctbl[i] = -i - 1; /* temporary hold of table index outside of lock */
+
+ unlock(&procs.l);
+
+ pid = sprocsp(tramp, PR_SALL, p, 0, KSTACK);
+
+ if(pid >= 0)
+ sproctbl[i] = pid;
+ else
+ sproctbl[i] = -1;
+}
+
+void
+osblock(void)
+{
+ blockproc(up->sigid);
+}
+
+void
+osready(Proc *p)
+{
+ unblockproc(p->sigid);
+}
+
+void
+trapUSR1(void)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+void
+trapILL(void)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+void
+trapBUS(void)
+{
+ disfault(nil, "Bus error");
+}
+
+void
+trapSEGV(void)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+/*
+ * This is not a signal handler but rather a vector from real/FPcontrol-Irix.c
+ */
+void
+trapFPE(unsigned exception[5], int value[2])
+{
+ disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+
+/* if(sproctbl[0] < 0)
+ panic("corrupt sproc tbl");
+
+ kill(sproctbl[0], SIGUSR2);
+ sginap(10000); */
+}
+
+void
+trapUSR2(void)
+{
+ int i;
+
+ for(i = MAXSPROC - 1; i > 0; i--) {
+ if(sproctbl[i] != -1)
+ kill(sproctbl[i], SIGKILL);
+ sproctbl[i] = -1;
+ }
+
+ execvp(rebootargv[0], rebootargv);
+ panic("reboot failure");
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+getnobody(void)
+{
+ struct passwd *pwd;
+
+ pwd = getpwnam("nobody");
+ if(pwd != nil) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct sigaction act;
+ struct passwd *pw;
+ int i;
+ char sys[64];
+
+ setsid();
+
+ for(i=0; i<MAXSPROC; i++)
+ sproctbl[i] = -1;
+
+ sproctbl[0] = getpid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+
+ if(dflag == 0)
+ termset();
+
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+ signal(SIGUSR2, trapUSR2);
+ /* For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGCLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler=trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+ if(sflag == 0) {
+ act.sa_handler=trapBUS;
+ sigaction(SIGBUS, &act, nil);
+ act.sa_handler=trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_handler=trapSEGV;
+ sigaction(SIGSEGV, &act, nil);
+ }
+
+ if(usconfig(CONF_INITUSERS, 1000) < 0)
+ panic("usconfig");
+
+ Xup = (Proc**)PRDA->usr_prda.fill;
+ up = newproc();
+
+ pw = getpwuid(getuid());
+ if(pw != nil) {
+ if (strlen(pw->pw_name) + 1 <= KNAMELEN)
+ strcpy(eve, pw->pw_name);
+ else
+ print("pw_name too long\n");
+ }
+ else
+ print("cannot getpwuid\n");
+
+ /* after setting up, since this takes locks */
+ getnobody();
+ up->env->uid = getuid();
+ up->env->gid = getgid();
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "keyboard read error: %s\n", strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+int
+segflush(void *a, ulong n)
+{
+ cacheflush(a, n, BCACHE);
+ return 0;
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return(0);
+ if(sec0==0){
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ static int tick;
+
+ /*
+ * Posix-conforming CLK_TCK implementations tend to call sysconf,
+ * and we don't need the overhead.
+ */
+ if(tick == 0)
+ tick = CLK_TCK;
+ sginap((tick*milsec)/1000);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ sginap(0);
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ nice(2);
+}
--- /dev/null
+++ b/emu/Linux/arm-tas-v5.S
@@ -1,0 +1,18 @@
+
+ .file "arm-tas-v5.S"
+/*
+ * ulong _tas(ulong*);
+ */
+ .align 2
+ .global _tas
+ .type _tas, %function
+_tas:
+ @ args = 0, pretend = 0, frame = 0
+ @ frame_needed = 0, uses_anonymous_args = 0
+ @ link register save eliminated.
+ @ lr needed for prologue
+ mov r3, #1
+ mov r1, r0
+ swp r0, r3, [r1]
+ bx lr
+ .size _tas, .-_tas
--- /dev/null
+++ b/emu/Linux/arm-tas-v7.S
@@ -1,0 +1,30 @@
+ .file "arm-tas-v7.S"
+#ifndef ARMv7
+#define DMB mcr p15, 0, r0, c7, c10, 5
+#else
+#define DMB dmb
+#endif
+.align 2
+.global _tas
+.type _tas, %function
+_tas:
+ @ args = 0, pretend = 0, frame = 0
+ @ frame_needed = 0, uses_anonymous_args = 0
+ @ link register save eliminated.
+ @ lr needed for prologue
+ DMB
+ mov r1, r0
+ mov r2, #0xaa
+tas1:
+ ldrex r0, [r1]
+ cmp r0, #0
+ bne lockbusy
+ strex r3, r2, [r1]
+ cmp r3, #0
+ bne tas1
+ DMB
+ bx lr
+lockbusy:
+ clrex
+ bx lr
+ .size _tas, .-_tas
--- /dev/null
+++ b/emu/Linux/asm-386.S
@@ -1,0 +1,51 @@
+ .file "asm-Linux-386.S"
+ .text
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/Linux/asm-arm.S
@@ -1,0 +1,62 @@
+ .file "asm-Linux-arm.S"
+ .text
+
+/*
+ * ulong umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .align 2
+ .global umult
+ .type umult, %function
+umult:
+ @ args = 0, pretend = 0, frame = 12
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #12
+ str r0, [fp, #-16]
+ str r1, [fp, #-20]
+ str r2, [fp, #-24]
+ ldr r1, [fp, #-16]
+ ldr r2, [fp, #-20]
+ umull r0, r3, r1, r2
+ ldr r1, [fp, #-24]
+ str r3, [r1]
+ ldmea fp, {fp, sp, pc}
+ .size umult, .-umult
+
+/*
+ * void FPsave(void*);
+ */
+
+ .align 2
+ .global FPsave
+ .type FPsave, %function
+FPsave:
+ @ args = 0, pretend = 0, frame = 4
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #4
+ str r0, [fp, #-16]
+ ldmea fp, {fp, sp, pc}
+ .size FPsave, .-FPsave
+
+/*
+ * void FPrestore(void*);
+ */
+ .align 2
+ .global FPrestore
+ .type FPrestore, %function
+FPrestore:
+ @ args = 0, pretend = 0, frame = 4
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #4
+ str r0, [fp, #-16]
+ ldmea fp, {fp, sp, pc}
+ .size FPrestore, .-FPrestore
--- /dev/null
+++ b/emu/Linux/asm-mips.S
@@ -1,0 +1,28 @@
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+LEAF(FPsave)
+ cfc1 t0, $31
+ sw t0, 0(a0) /* a0 is argument */
+ j $31
+ END(FPsave)
+
+LEAF(FPrestore)
+ lw t0, 0(a0) /* a0 is argument */
+ ctc1 t0, $31
+ j $31
+ END(FPrestore)
+
+LEAF(_tas)
+ .set noreorder
+1:
+ ll v0,0(a0) /* a0 is argument */
+ or t1, v0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ nop
+ .set reorder
+ END(_tas)
--- /dev/null
+++ b/emu/Linux/asm-power.S
@@ -1,0 +1,61 @@
+ .align 2
+ .global FPsave
+FPsave:
+ stfd %f14,0*8(%r3)
+ stfd %f15,1*8(%r3)
+ stfd %f16,2*8(%r3)
+ stfd %f17,3*8(%r3)
+ stfd %f18,4*8(%r3)
+ stfd %f19,5*8(%r3)
+ stfd %f20,6*8(%r3)
+ stfd %f21,7*8(%r3)
+ stfd %f22,8*8(%r3)
+ stfd %f23,9*8(%r3)
+ stfd %f24,10*8(%r3)
+ stfd %f25,11*8(%r3)
+ stfd %f26,12*8(%r3)
+ stfd %f27,13*8(%r3)
+ stfd %f28,14*8(%r3)
+ stfd %f29,15*8(%r3)
+ stfd %f30,16*8(%r3)
+ stfd %f31,17*8(%r3)
+ blr
+
+ .align 2
+ .global FPrestore
+FPrestore:
+ lfd %f14,0*8(%r3)
+ lfd %f15,1*8(%r3)
+ lfd %f16,2*8(%r3)
+ lfd %f17,3*8(%r3)
+ lfd %f18,4*8(%r3)
+ lfd %f19,5*8(%r3)
+ lfd %f20,6*8(%r3)
+ lfd %f21,7*8(%r3)
+ lfd %f22,8*8(%r3)
+ lfd %f23,9*8(%r3)
+ lfd %f24,10*8(%r3)
+ lfd %f25,11*8(%r3)
+ lfd %f26,12*8(%r3)
+ lfd %f27,13*8(%r3)
+ lfd %f28,14*8(%r3)
+ lfd %f29,15*8(%r3)
+ lfd %f30,16*8(%r3)
+ lfd %f31,17*8(%r3)
+ blr
+
+ .align 2
+ .global _tas
+_tas:
+ sync
+ mr %r4, %r3
+ addi %r5,0,0x1
+1:
+ lwarx %r3, 0, %r4
+ cmpwi %r3, 0
+ bne- 2f
+ stwcx. %r5, 0, %r4
+ bne- 1b
+2:
+ sync
+ blr
--- /dev/null
+++ b/emu/Linux/asm-spim.S
@@ -1,0 +1,29 @@
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+
+LEAF(FPsave)
+ cfc1 t0, $31
+ sw t0, 0(a0) /* a0 is argument */
+ j $31
+ END(FPsave)
+
+LEAF(FPrestore)
+ lw t0, 0(a0) /* a0 is argument */
+ ctc1 t0, $31
+ j $31
+ END(FPrestore)
+
+LEAF(_tas)
+ .set noreorder
+1:
+ ll v0,0(a0) /* a0 is argument */
+ or t1, v0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ nop
+ .set reorder
+ END(_tas)
--- /dev/null
+++ b/emu/Linux/audio-oss.c
@@ -1,0 +1,441 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "audio.h"
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_PCM // SOUND_MIXER_VOLUME
+#define Audio_Headphone_Val SOUND_MIXER_ALTPCM
+#define Audio_Lineout_Val SOUND_MIXER_CD
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+#define DEVAUDIO "/dev/dsp"
+#define DEVMIXER "/dev/mixer"
+
+#define DPRINT if(1)print
+
+enum {
+ A_Pause,
+ A_UnPause,
+ A_In,
+ A_Out,
+};
+
+static struct {
+ int data; /* dsp data fd */
+ int ctl; /* mixer fd */
+ int pause;
+ QLock lk;
+} afd = {.data = -1, .ctl = -1, .pause =A_UnPause };
+
+static Audio_t av;
+static QLock inlock;
+static QLock outlock;
+
+static int audio_open(int);
+static int audio_pause(int, int);
+static int audio_set_info(int, Audio_d*, int);
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
+
+void
+audio_file_init(void)
+{
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ USED(c);
+ DPRINT("audio_file_open %d %d %x\n", afd.data, afd.ctl, omode);
+ qlock(&afd.lk);
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ if(afd.data >= 0)
+ error(Einuse);
+ if(afd.ctl < 0){
+ afd.ctl = open(DEVMIXER, ORDWR);
+ if(afd.ctl < 0)
+ oserror();
+ }
+ afd.data = audio_open(omode);
+ if(afd.data < 0)
+ oserror();
+ poperror();
+ qunlock(&afd.lk);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ USED(c);
+ DPRINT("audio_file_close %d %d\n", afd.data, afd.ctl);
+ qlock(&afd.lk);
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ close(afd.data);
+ afd.data = -1;
+ qunlock(&afd.lk);
+ poperror();
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ long ba, status, chunk, total;
+
+ USED(c);
+ USED(offset);
+ DPRINT("audio_file_read %d %d\n", afd.data, afd.ctl);
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(afd.data < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(!audio_pause(afd.data, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count){
+ chunk = count - total;
+ status = read (afd.data, va + total, chunk);
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ USED(c);
+ USED(offset);
+ DPRINT("audio_file_write %d %d\n", afd.data, afd.ctl);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(afd.data < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count){
+ chunk = min(bufsz, count - total);
+ status = write(afd.data, va, chunk);
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ Audio_t tmpav = av;
+ int tfd;
+
+ USED(c);
+ USED(offset);
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ DPRINT ("audio_ctl_write %X %X\n", afd.data, afd.ctl);
+ if(!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if(!canqlock(&inlock))
+ error("device busy");
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(!canqlock(&outlock))
+ error("device busy");
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ /* DEVAUDIO needs to be open to issue an ioctl */
+ tfd = afd.data;
+ if(tfd < 0){
+ tfd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
+ if(tfd < 0)
+ oserror();
+ }
+ if(waserror()){
+ if(tfd != afd.data)
+ close(tfd);
+ nexterror();
+ }
+
+ if(tmpav.in.flags & AUDIO_MOD_FLAG){
+ if(!audio_pause(tfd, A_Pause))
+ error(Ebadarg);
+ if(!audio_set_info(tfd, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+
+ poperror();
+ if(tfd != afd.data)
+ close(tfd);
+
+ tmpav.in.flags = 0;
+ av = tmpav;
+
+ poperror();
+ qunlock(&outlock);
+ poperror();
+ qunlock(&inlock);
+ return count;
+}
+
+/* Linux/OSS specific stuff */
+
+static int
+choosefmt(Audio_d *i)
+{
+ switch(i->bits){
+ case 8:
+ switch(i->enc){
+ case Audio_Alaw_Val:
+ return AFMT_A_LAW;
+ case Audio_Ulaw_Val:
+ return AFMT_MU_LAW;
+ case Audio_Pcm_Val:
+ return AFMT_U8;
+ }
+ break;
+ case 16:
+ if(i->enc == Audio_Pcm_Val)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+setvolume(int fd, int what, int left, int right)
+{
+ int can, v;
+
+ if(ioctl(fd, SOUND_MIXER_READ_DEVMASK, &can) < 0)
+ can = ~0;
+
+ DPRINT("setvolume fd%d %X can mix 0x%X (mask %X)\n", fd, what, (can & (1<<what)), can);
+ if(!(can & (1<<what)))
+ return 0;
+ v = left | (right<<8);
+ if(ioctl(afd.ctl, MIXER_WRITE(what), &v) < 0)
+ return 0;
+ return 1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status, arg;
+ int oldfmt, newfmt;
+
+ USED(d);
+ DPRINT("audio_set_info (%d) %d %d\n", fd, afd.data, afd.ctl);
+ if(fd < 0)
+ return 0;
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG){
+ arg = i->rate;
+ if(ioctl(fd, SNDCTL_DSP_SPEED, &arg) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG){
+ arg = i->chan;
+ if(ioctl(fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
+ return 0;
+ }
+
+ /* precision */
+ if(i->flags & AUDIO_BITS_FLAG){
+ arg = i->bits;
+ if(ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &arg) < 0)
+ return 0;
+ }
+
+ /* encoding */
+ if(i->flags & AUDIO_ENC_FLAG){
+ ioctl(fd, SNDCTL_DSP_GETFMTS, &oldfmt);
+
+ newfmt = choosefmt(i);
+ if(newfmt < 0)
+ return 0;
+ if(newfmt != oldfmt){
+ status = ioctl(fd, SNDCTL_DSP_SETFMT, &arg);
+ DPRINT ("enc oldfmt newfmt %x status %d\n", oldfmt, newfmt, status);
+ }
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG))
+ return setvolume(afd.ctl, i->dev, i->left, i->right);
+
+ return 1;
+}
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+audio_open(int omode)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ switch (omode){
+ case OREAD:
+ fd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
+ break;
+ case OWRITE:
+ fd = open(DEVAUDIO, O_WRONLY|O_NONBLOCK);
+ break;
+ case ORDWR:
+ fd = open(DEVAUDIO, O_RDWR|O_NONBLOCK);
+ break;
+ }
+
+ DPRINT("audio_open %d\n", fd);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)){
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if(!audio_pause(fd, A_Pause)){
+ close(fd);
+ error(Eio);
+ }
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = ~0;
+
+ if(!audio_set_info(fd, &av.in, A_In)){
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+static int
+dspsync(int fd)
+{
+ return ioctl(fd, SNDCTL_DSP_RESET, NULL) >= 0 &&
+ ioctl(fd, SNDCTL_DSP_SYNC, NULL) >= 0;
+}
+
+static int
+audio_pause(int fd, int f)
+{
+ int status;
+
+// DPRINT ("audio_pause (%d) %d %d\n", fd, afd.data, afd.ctl);
+ if(fd < 0)
+ return 0;
+ if(fd != afd.data)
+ return dspsync(fd);
+ qlock(&afd.lk);
+ if(afd.pause == f){
+ qunlock(&afd.lk);
+ return 1;
+ }
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ status = dspsync(afd.data);
+ if(status)
+ afd.pause = f;
+ poperror();
+ qunlock(&afd.lk);
+ return status;
+}
--- /dev/null
+++ b/emu/Linux/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+ if(nice)
+ setpriority(PRIO_PROCESS, 0, 19);
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Linux/deveia.c
@@ -1,0 +1,44 @@
+/*
+ * Linux serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/ttyS0",
+ "/dev/ttyS1",
+ "/dev/ttyS2",
+ "/dev/ttyS3",
+ "/dev/ttyS4",
+ "/dev/ttyS5",
+ "/dev/ttyS6",
+ "/dev/ttyS7",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {460800, B460800},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/Linux/devfs.c
@@ -1,0 +1,26 @@
+#include "devfs-posix.c"
+
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+static vlong
+osdisksize(int fd)
+{
+ uvlong u64;
+ long l;
+ struct hd_geometry geo;
+
+ memset(&geo, 0, sizeof geo);
+ l = 0;
+ u64 = 0;
+#ifdef BLKGETSIZE64
+ if(ioctl(fd, BLKGETSIZE64, &u64) >= 0)
+ return u64;
+#endif
+ if(ioctl(fd, BLKGETSIZE, &l) >= 0)
+ return l*512;
+ if(ioctl(fd, HDIO_GETGEO, &geo) >= 0)
+ return (vlong)geo.heads*geo.sectors*geo.cylinders*512;
+ return 0;
+}
--- /dev/null
+++ b/emu/Linux/emu
@@ -1,0 +1,105 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif6-posix ipaux
+ eia
+# audio audio-oss
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /dis
+# /n
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Linux/emu-g
@@ -1,0 +1,101 @@
+env
+ X11LIBS=
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif6-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ math
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Linux/emu-wrt
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+# draw
+# pointer
+# snarf
+
+ ip ipif6-posix ipaux
+# eia
+# audio audio
+ mem
+
+lib
+ interp
+# tk
+# freetype
+ math
+# draw
+
+# memlayer
+# memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+# draw
+
+# tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+# freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ int dontcompile = 1;
+ unsigned long strtochan(char *s) {return 0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Linux/mk-wrt
@@ -1,0 +1,8 @@
+#!/bin/sh
+
+OPENWRT=$HOME/OpenWrt-SDK-Linux-i686-1
+INFERNO=/usr/inferno
+
+PATH=$OPENWRT/staging_dir_mipsel/bin:$INFERNO/Linux/386/bin:$PATH
+
+mk OBJTYPE=spim CONF=emu-wrt CONFLIST=emu-wrt SYSLIBS=-lm WIN= $*
--- /dev/null
+++ b/emu/Linux/mkfile
@@ -1,0 +1,53 @@
+SYSTARG=Linux
+<../../mkconfig
+SYSTARG=Linux
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+X11LIBS= -lX11 -lXext # can remove or override using env section in config files
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+<mkfile-$OBJTYPE # sets $ARCHFILES
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ $ARCHFILES\
+ os.$O\
+ kproc-pthreads.$O\
+ segflush-$OBJTYPE.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+LIBNAMES=${LIBS:%=lib%.a}
+#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= $X11LIBS -lm -lpthread
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+<../port/portmkfile
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/Linux/mkfile-arm
@@ -1,0 +1,2 @@
+ARCHFILES=\
+ arm-tas-v7.$O\
--- /dev/null
+++ b/emu/Linux/os.c
@@ -1,0 +1,304 @@
+#include <sys/types.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include <stdint.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <semaphore.h>
+
+#include <raise.h>
+
+/* glibc 2.3.3-NTPL messes up getpid() by trying to cache the result, so we'll do it ourselves */
+#include <sys/syscall.h>
+#define getpid() syscall(SYS_getpid)
+
+enum
+{
+ DELETE = 0x7f,
+ CTRLC = 'C'-'@',
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "Linux";
+
+typedef sem_t Sem;
+
+extern int dflag;
+
+int gidnobody = -1;
+int uidnobody = -1;
+static struct termios tinit;
+
+static void
+sysfault(char *what, void *addr)
+{
+ char buf[64];
+
+ snprint(buf, sizeof(buf), "sys: %s%#p", what, addr);
+ disfault(nil, buf);
+}
+
+static void
+trapILL(int signo, siginfo_t *si, void *a)
+{
+ USED(signo);
+ USED(a);
+ sysfault("illegal instruction pc=", si->si_addr);
+}
+
+static int
+isnilref(siginfo_t *si)
+{
+ return si != 0 && (si->si_addr == (void*)~(uintptr_t)0 || (uintptr_t)si->si_addr < 512);
+}
+
+static void
+trapmemref(int signo, siginfo_t *si, void *a)
+{
+ USED(a); /* ucontext_t*, could fetch pc in machine-dependent way */
+ if(isnilref(si))
+ disfault(nil, exNilref);
+ else if(signo == SIGBUS)
+ sysfault("bad address addr=", si->si_addr); /* eg, misaligned */
+ else
+ sysfault("segmentation violation addr=", si->si_addr);
+}
+
+static void
+trapFPE(int signo, siginfo_t *si, void *a)
+{
+ char buf[64];
+
+ USED(signo);
+ USED(a);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux pc=%#p", getfsr(), si->si_addr);
+ disfault(nil, buf);
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ error("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct sigaction act;
+ struct passwd *pw;
+ Proc *p;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ pw = getpwnam("nobody");
+ if(pw != nil) {
+ uidnobody = pw->pw_uid;
+ gidnobody = pw->pw_gid;
+ }
+
+ if(dflag == 0)
+ termset();
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &act, nil);
+
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+
+ if(sflag == 0) {
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_sigaction = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ act.sa_sigaction = trapmemref;
+ sigaction(SIGBUS, &act, nil);
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_flags &= ~SA_SIGINFO;
+ }
+
+ p = newproc();
+ kprocinit(p);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ buf[0] = 'H' - '@';
+ break;
+ case CTRLC:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+
+ if(sec0 == 0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time, NULL);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
--- /dev/null
+++ b/emu/Linux/segflush-386.c
@@ -1,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a); USED(n);
+ return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-arm.c
@@ -1,0 +1,14 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+#define SYS_cacheflush __ARM_NR_cacheflush
+
+int
+segflush(void *a, ulong n)
+{
+ if(n)
+ syscall(SYS_cacheflush, a, (char*)a+n-1, 1);
+ return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-mips.S
@@ -1,0 +1,16 @@
+#include "syscall.h"
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+/*
+ * int segflush(void *p, ulong len)
+ */
+
+LEAF(segflush)
+ li a2,BCACHE
+ li v0,SYS_cacheflush
+ syscall
+ li v0,0
+ j $31
+ END(segflush)
--- /dev/null
+++ b/emu/Linux/segflush-power.c
@@ -1,0 +1,34 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+
+/*
+ * from geoff collyer's port
+ * invalidate instruction cache and write back data cache from a to a+n-1,
+ * at least.
+ */
+int
+segflush(void *a, ulong n)
+{
+ ulong *p;
+
+ // cache blocks are often eight words (32 bytes) long, sometimes 16 bytes.
+ // need to determine it dynamically?
+ for (p = (ulong *)((ulong)a & ~7UL); (char *)p < (char *)a + n; p++)
+ __asm__("dcbst 0,%0\n\t" // not dcbf, which writes back, then invalidates
+ "icbi 0,%0\n\t"
+ : // no output
+ : "ar" (p)
+ );
+ __asm__("sync\n\t"
+ : // no output
+ :
+ );
+ __asm__("isync\n\t"
+ : // no output
+ :
+ );
+ return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-spim.S
@@ -1,0 +1,16 @@
+#include "syscall.h"
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+/*
+ * int segflush(void *p, ulong len)
+ */
+
+LEAF(segflush)
+ li a2,BCACHE
+ li v0,SYS_cacheflush
+ syscall
+ li v0,0
+ j $31
+ END(segflush)
--- /dev/null
+++ b/emu/MacOSX/NOTE
@@ -1,0 +1,4 @@
+- still interp mode only, jit not complete
+- mkfile uses Carbon only; mkfile-x11 uses X11R6 from Apple
+- both powerpc and x86 versions compiled from this source
+- mkfile-g doesn't include graphics (uses emu-g not emu as configuration)
--- /dev/null
+++ b/emu/MacOSX/NOTICE
@@ -1,0 +1,3 @@
+MacOSX port Copyright © 2001-2003 Corpus Callosum Corporation,
+with portions Copyright © 2001-2003 Geoff Collyer
+MacOSX-x86 Copyright © 2006 Corpus Callosum Corporation
--- /dev/null
+++ b/emu/MacOSX/asm-386.s
@@ -1,0 +1,28 @@
+/*
+ * these are the same as on the PC (eg, Linux)
+*/
+
+ .globl _FPsave
+_FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .globl _FPrestore
+_FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .globl __tas
+__tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/MacOSX/asm-power.s
@@ -1,0 +1,36 @@
+/*
+ * File: asm-power.s
+ *
+ * Copyright (c) 2003, Corpus Callosum Corporation. All rights reserved.
+ */
+
+#include <architecture/ppc/asm_help.h>
+
+.text
+
+LEAF(_FPsave)
+ mffs f0
+ stfd f0,0(r3)
+ blr
+END(_FPsave)
+
+LEAF(_FPrestore)
+ lfd f0,0(r3)
+ mtfsf 0xff,f0
+ blr
+END(_FPrestore)
+
+LEAF(__tas)
+ sync
+ mr r4,r3
+ addi r5,0,0x1
+1:
+ lwarx r3,0,r4
+ cmpwi r3,0x0
+ bne- 2f
+ stwcx. r5,0,r4
+ bne- 1b /* Lost reservation, try again */
+2:
+ sync
+ blr
+END(__tas)
--- /dev/null
+++ b/emu/MacOSX/cmd.c
@@ -1,0 +1,214 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/MacOSX/deveia.c
@@ -1,0 +1,123 @@
+/*
+ * Darwin serial port definitions, uses IOKit to build sysdev
+ * Loosely based on FreeBSD/deveia.c
+ * Copyright © 1998, 1999 Lucent Technologies Inc. All rights reserved.
+ * Revisions Copyright © 1999, 2000 Vita Nuova Limited. All rights reserved.
+ * Revisions Copyright © 2003 Corpus Callosum Corporation. All rights reserved.
+*/
+
+#include <termios.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <mach/mach.h>
+
+#include <CoreFoundation/CFNumber.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+#include <IOKit/IOBSD.h>
+
+#include <sys/ioctl.h>
+#include <sys/ttycom.h>
+
+#undef nil
+
+#define B14400 14400
+#define B28800 28800
+#define B57600 57600
+#define B76800 76800
+#define B115200 115200
+#define B230400 230400
+
+extern int vflag;
+
+#define MAXDEV 16
+static char *sysdev[MAXDEV];
+
+static void _buildsysdev(void);
+#define buildsysdev() _buildsysdev() /* for devfs-posix.c */
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {76800, B76800},
+ {115200, B115200},
+ {230400, B230400},
+ {0, -1}
+};
+
+static void
+_buildsysdev(void)
+{
+ mach_port_t port;
+ CFMutableDictionaryRef classesToMatch;
+ io_iterator_t serialPortIterator;
+ io_object_t serialDevice;
+ CFMutableArrayRef paths;
+ CFTypeRef path;
+ char eiapath[MAXPATHLEN];
+ CFIndex i, o, npath;
+
+ if(IOMasterPort(MACH_PORT_NULL, &port) != KERN_SUCCESS)
+ return;
+ classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+ if(classesToMatch == NULL){
+ printf("IOServiceMatching returned a NULL dictionary.\n");
+ goto Failed;
+ }
+ CFDictionarySetValue(classesToMatch,
+ CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDAllTypes));
+
+ if(IOServiceGetMatchingServices(port, classesToMatch, &serialPortIterator) != KERN_SUCCESS)
+ goto Failed;
+
+ paths = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ while((serialDevice = IOIteratorNext(serialPortIterator)) != 0){
+ path = IORegistryEntryCreateCFProperty(serialDevice, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
+ if(path != NULL)
+ CFArrayAppendValue(paths, path);
+ IOObjectRelease(serialDevice);
+ }
+
+ npath = CFArrayGetCount(paths);
+ o = 0;
+ for(i = 0; i < npath && i < nelem(sysdev); i++){
+ if(CFStringGetCString(CFArrayGetValueAtIndex(paths, i), eiapath, sizeof(eiapath), kCFStringEncodingUTF8)){
+ sysdev[o] = strdup(eiapath);
+ if(vflag > 1)
+ print("BSD path: '%s'\n", sysdev[o]);
+ o++;
+ }
+ }
+
+ CFRelease(paths);
+ IOObjectRelease(serialPortIterator);
+
+Failed:
+ mach_port_deallocate(mach_task_self(), port);
+}
+
--- /dev/null
+++ b/emu/MacOSX/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/MacOSX/emu
@@ -1,0 +1,113 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ int dontcompile = 1;
+ int macjit = 1;
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/MacOSX/emu-g
@@ -1,0 +1,101 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ math
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ int dontcompile = 1;
+ int macjit = 1;
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/MacOSX/keycodes.h
@@ -1,0 +1,189 @@
+/* These are the Macintosh key scancode constants -- from Inside Macintosh */
+#define QZ_ESCAPE 0x35
+#define QZ_F1 0x7A
+#define QZ_F2 0x78
+#define QZ_F3 0x63
+#define QZ_F4 0x76
+#define QZ_F5 0x60
+#define QZ_F6 0x61
+#define QZ_F7 0x62
+#define QZ_F8 0x64
+#define QZ_F9 0x65
+#define QZ_F10 0x6D
+#define QZ_F11 0x67
+#define QZ_F12 0x6F
+#define QZ_PRINT 0x69
+#define QZ_SCROLLOCK 0x6B
+#define QZ_PAUSE 0x71
+#define QZ_POWER 0x7F
+#define QZ_BACKQUOTE 0x32
+#define QZ_1 0x12
+#define QZ_2 0x13
+#define QZ_3 0x14
+#define QZ_4 0x15
+#define QZ_5 0x17
+#define QZ_6 0x16
+#define QZ_7 0x1A
+#define QZ_8 0x1C
+#define QZ_9 0x19
+#define QZ_0 0x1D
+#define QZ_MINUS 0x1B
+#define QZ_EQUALS 0x18
+#define QZ_BACKSPACE 0x33
+#define QZ_INSERT 0x72
+#define QZ_HOME 0x73
+#define QZ_PAGEUP 0x74
+#define QZ_NUMLOCK 0x47
+#define QZ_KP_EQUALS 0x51
+#define QZ_KP_DIVIDE 0x4B
+#define QZ_KP_MULTIPLY 0x43
+#define QZ_TAB 0x30
+#define QZ_q 0x0C
+#define QZ_w 0x0D
+#define QZ_e 0x0E
+#define QZ_r 0x0F
+#define QZ_t 0x11
+#define QZ_y 0x10
+#define QZ_u 0x20
+#define QZ_i 0x22
+#define QZ_o 0x1F
+#define QZ_p 0x23
+#define QZ_LEFTBRACKET 0x21
+#define QZ_RIGHTBRACKET 0x1E
+#define QZ_BACKSLASH 0x2A
+#define QZ_DELETE 0x75
+#define QZ_END 0x77
+#define QZ_PAGEDOWN 0x79
+#define QZ_KP7 0x59
+#define QZ_KP8 0x5B
+#define QZ_KP9 0x5C
+#define QZ_KP_MINUS 0x4E
+#define QZ_CAPSLOCK 0x39
+#define QZ_a 0x00
+#define QZ_s 0x01
+#define QZ_d 0x02
+#define QZ_f 0x03
+#define QZ_g 0x05
+#define QZ_h 0x04
+#define QZ_j 0x26
+#define QZ_k 0x28
+#define QZ_l 0x25
+#define QZ_SEMICOLON 0x29
+#define QZ_QUOTE 0x27
+#define QZ_RETURN 0x24
+#define QZ_KP4 0x56
+#define QZ_KP5 0x57
+#define QZ_KP6 0x58
+#define QZ_KP_PLUS 0x45
+#define QZ_LSHIFT 0x38
+#define QZ_z 0x06
+#define QZ_x 0x07
+#define QZ_c 0x08
+#define QZ_v 0x09
+#define QZ_b 0x0B
+#define QZ_n 0x2D
+#define QZ_m 0x2E
+#define QZ_COMMA 0x2B
+#define QZ_PERIOD 0x2F
+#define QZ_SLASH 0x2C
+/* These are the same as the left versions - use left by default */
+#if 0
+#define QZ_RSHIFT 0x38
+#endif
+#define QZ_UP 0x7E
+#define QZ_KP1 0x53
+#define QZ_KP2 0x54
+#define QZ_KP3 0x55
+#define QZ_KP_ENTER 0x4C
+#define QZ_LCTRL 0x3B
+#define QZ_LALT 0x3A
+#define QZ_LMETA 0x37
+#define QZ_SPACE 0x31
+/* These are the same as the left versions - use left by default */
+#if 0
+#define QZ_RMETA 0x37
+#define QZ_RALT 0x3A
+#define QZ_RCTRL 0x3B
+#endif
+#define QZ_LEFT 0x7B
+#define QZ_DOWN 0x7D
+#define QZ_RIGHT 0x7C
+#define QZ_KP0 0x52
+#define QZ_KP_PERIOD 0x41
+
+/* Wierd, these keys are on my iBook under MacOS X */
+#define QZ_IBOOK_ENTER 0x34
+#define QZ_IBOOK_LEFT 0x3B
+#define QZ_IBOOK_RIGHT 0x3C
+#define QZ_IBOOK_DOWN 0x3D
+#define QZ_IBOOK_UP 0x3E
+#define KEY_ENTER 13
+#define KEY_TAB 9
+
+#define KEY_BASE 0x100
+
+/* Function keys */
+#define KEY_F (KEY_BASE+64)
+
+/* Control keys */
+#define KEY_CTRL (KEY_BASE)
+#define KEY_BACKSPACE (KEY_CTRL+0)
+#define KEY_DELETE (KEY_CTRL+1)
+#define KEY_INSERT (KEY_CTRL+2)
+#define KEY_HOME (KEY_CTRL+3)
+#define KEY_END (KEY_CTRL+4)
+#define KEY_PAGE_UP (KEY_CTRL+5)
+#define KEY_PAGE_DOWN (KEY_CTRL+6)
+#define KEY_ESC (KEY_CTRL+7)
+
+/* Control keys short name */
+#define KEY_BS KEY_BACKSPACE
+#define KEY_DEL KEY_DELETE
+#define KEY_INS KEY_INSERT
+#define KEY_PGUP KEY_PAGE_UP
+#define KEY_PGDOWN KEY_PAGE_DOWN
+#define KEY_PGDWN KEY_PAGE_DOWN
+
+/* Cursor movement */
+#define KEY_CRSR (KEY_BASE+16)
+#define KEY_RIGHT (KEY_CRSR+0)
+#define KEY_LEFT (KEY_CRSR+1)
+#define KEY_DOWN (KEY_CRSR+2)
+#define KEY_UP (KEY_CRSR+3)
+
+/* Multimedia keyboard/remote keys */
+#define KEY_MM_BASE (0x100+384)
+#define KEY_POWER (KEY_MM_BASE+0)
+#define KEY_MENU (KEY_MM_BASE+1)
+#define KEY_PLAY (KEY_MM_BASE+2)
+#define KEY_PAUSE (KEY_MM_BASE+3)
+#define KEY_PLAYPAUSE (KEY_MM_BASE+4)
+#define KEY_STOP (KEY_MM_BASE+5)
+#define KEY_FORWARD (KEY_MM_BASE+6)
+#define KEY_REWIND (KEY_MM_BASE+7)
+#define KEY_NEXT (KEY_MM_BASE+8)
+#define KEY_PREV (KEY_MM_BASE+9)
+#define KEY_VOLUME_UP (KEY_MM_BASE+10)
+#define KEY_VOLUME_DOWN (KEY_MM_BASE+11)
+#define KEY_MUTE (KEY_MM_BASE+12)
+
+/* Keypad keys */
+#define KEY_KEYPAD (KEY_BASE+32)
+#define KEY_KP0 (KEY_KEYPAD+0)
+#define KEY_KP1 (KEY_KEYPAD+1)
+#define KEY_KP2 (KEY_KEYPAD+2)
+#define KEY_KP3 (KEY_KEYPAD+3)
+#define KEY_KP4 (KEY_KEYPAD+4)
+#define KEY_KP5 (KEY_KEYPAD+5)
+#define KEY_KP6 (KEY_KEYPAD+6)
+#define KEY_KP7 (KEY_KEYPAD+7)
+#define KEY_KP8 (KEY_KEYPAD+8)
+#define KEY_KP9 (KEY_KEYPAD+9)
+#define KEY_KPDEC (KEY_KEYPAD+10)
+#define KEY_KPINS (KEY_KEYPAD+11)
+#define KEY_KPDEL (KEY_KEYPAD+12)
+#define KEY_KPENTER (KEY_KEYPAD+13)
+
+/* Special keys */
+#define KEY_INTERN (0x1000)
+#define KEY_CLOSE_WIN (KEY_INTERN+0)
--- /dev/null
+++ b/emu/MacOSX/mkfile
@@ -1,0 +1,56 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+ '-DOBJTYPE="'$OBJTYPE'"'\
+ -DEMU -I. -I../port\
+ -I$ROOT/$SYSTARG/$OBJTYPE/include\
+ -I$ROOT/include -I$ROOT/libinterp\
+ $CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+
+KERNDATE=`{$NDATE}
+
+SYSLIBS= \
+ -lm\
+ -framework Carbon -framework QuickTime\
+ -lpthread\
+ -framework CoreFoundation\
+ -framework IOKit\
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/mkfile-g
@@ -1,0 +1,55 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu-g #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+ '-DOBJTYPE="'$OBJTYPE'"'\
+ -DEMU -I. -I../port\
+ -I$ROOT/$SYSTARG/$OBJTYPE/include\
+ -I$ROOT/include -I$ROOT/libinterp\
+ $CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+
+KERNDATE=`{$NDATE}
+
+SYSLIBS= \
+ -lm\
+ -lpthread\
+ -framework CoreFoundation\
+ -framework IOKit\
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/mkfile-x11
@@ -1,0 +1,59 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ win-x11a.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+ '-DOBJTYPE="'$OBJTYPE'"'\
+ -DEMU -I. -I../port\
+ -I$ROOT/$SYSTARG/$OBJTYPE/include\
+ -I$ROOT/include -I$ROOT/libinterp\
+ $CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+ -I/usr/X11R6/include
+
+KERNDATE=`{$NDATE}
+
+LDFLAGS=$LDFLAGS -L/usr/X11R6/lib
+
+SYSLIBS= \
+ -lm -lX11 -lXext\
+ -lpthread\
+ -framework CoreFoundation\
+ -framework IOKit\
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/os.c
@@ -1,0 +1,574 @@
+/*
+ * Loosely based on FreeBSD/os.c and Solaris/os.c
+ * Copyright © 1998, 1999 Lucent Technologies Inc. All rights reserved.
+ * Revisions Copyright © 1999, 2000 Vita Nuova Limited. All rights reserved.
+ * Revisions Copyright © 2002, 2003 Corpus Callosum Corporation. All rights reserved.
+ */
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <raise.h>
+
+#undef _POSIX_C_SOURCE
+#undef getwd
+
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <sched.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <mach/mach_init.h>
+#include <mach/task.h>
+#include <mach/vm_map.h>
+
+#if defined(__ppc__)
+#include <architecture/ppc/cframe.h>
+#endif
+
+enum
+{
+ DELETE = 0x7F
+};
+char *hosttype = "MacOSX";
+char *cputype = OBJTYPE;
+
+typedef struct Sem Sem;
+struct Sem {
+ pthread_cond_t c;
+ pthread_mutex_t m;
+ int v;
+};
+
+static pthread_key_t prdakey;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+ return pthread_getspecific(prdakey);
+}
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ Sem *sem;
+
+ USED(t);
+ USED(msg);
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", p->text, msg);
+
+ e = p->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ free(e->user);
+ }
+ free(p->prog);
+ sem = p->os;
+ if(sem != nil){
+ pthread_cond_destroy(&sem->c);
+ pthread_mutex_destroy(&sem->m);
+ }
+ free(p->os);
+ free(p);
+ pthread_exit(0);
+}
+
+
+
+static void
+sysfault(char *what, void *addr)
+{
+ char buf[64];
+
+ snprint(buf, sizeof(buf), "sys: %s%#p", what, addr);
+ disfault(nil, buf);
+}
+
+static void
+trapILL(int signo, siginfo_t *si, void *a)
+{
+ USED(signo);
+ USED(a);
+ sysfault("illegal instruction pc=", si->si_addr);
+}
+
+static int
+isnilref(siginfo_t *si)
+{
+ return si != 0 && (si->si_addr == (void*)~(uintptr_t)0 || (uintptr_t)si->si_addr < 512);
+}
+
+static void
+trapmemref(int signo, siginfo_t *si, void *a)
+{
+ USED(a); /* ucontext_t*, could fetch pc in machine-dependent way */
+ if(isnilref(si))
+ disfault(nil, exNilref);
+ else if(signo == SIGBUS)
+ sysfault("bad address addr=", si->si_addr); /* eg, misaligned */
+ else
+ sysfault("segmentation violation addr=", si->si_addr);
+}
+
+static void
+trapFPE(int signo, siginfo_t *si, void *a)
+{
+ char buf[64];
+
+ USED(signo);
+ USED(a);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux pc=%#p", getfsr(), si->si_addr);
+ disfault(nil, buf);
+}
+
+void
+trapUSR1(int signo)
+{
+ USED(signo);
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(up->intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+
+ up->intwait = 0; /* Clear it so the proc can continue */
+}
+
+/* from geoff collyer's port */
+void
+printILL(int sig, siginfo_t *si, void *v)
+{
+ USED(sig);
+ USED(v);
+ panic("illegal instruction with code=%d at address=%p, opcode=%#x\n",
+ si->si_code, si->si_addr, *(uchar*)si->si_addr);
+}
+
+static void
+setsigs(void)
+{
+ struct sigaction act;
+
+ memset(&act, 0 , sizeof(act));
+
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+
+ act.sa_handler = trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+
+ if(sflag == 0) {
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_sigaction = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ act.sa_sigaction = trapmemref;
+ sigaction(SIGBUS, &act, nil);
+ sigaction(SIGSEGV, &act, nil);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+ } else {
+ act.sa_sigaction = printILL;
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGILL, &act, nil);
+ }
+}
+
+
+
+
+void *
+tramp(void *arg)
+{
+ Proc *p = arg;
+ p->sigid = (int)pthread_self();
+ if(pthread_setspecific(prdakey, arg)) {
+ print("set specific data failed in tramp\n");
+ pthread_exit(0);
+ }
+ p->func(p->arg);
+ pexit("{Tramp}", 0);
+ return NULL;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ pthread_t thread;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ pthread_attr_t attr;
+ Sem *sem;
+
+ p = newproc();
+ if(p == nil)
+ panic("kproc: no memory");
+ sem = malloc(sizeof(*sem));
+ if(sem == nil)
+ panic("can't allocate semaphore");
+ pthread_cond_init(&sem->c, NULL);
+ pthread_mutex_init(&sem->m, NULL);
+ sem->v = 0;
+ p->os = sem;
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ } else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(pthread_attr_init(&attr) == -1)
+ panic("pthread_attr_init failed");
+
+ pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
+ pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ if(pthread_create(&thread, &attr, tramp, p))
+ panic("thr_create failed\n");
+ pthread_attr_destroy(&attr);
+}
+
+int
+segflush(void *va, ulong len)
+{
+ kern_return_t err;
+ vm_machine_attribute_val_t value = MATTR_VAL_ICACHE_FLUSH;
+
+ err = vm_machine_attribute( (vm_map_t)mach_task_self(),
+ (vm_address_t)va,
+ (vm_size_t)len,
+ MATTR_CACHE,
+ &value);
+ if(err != KERN_SUCCESS)
+ print("segflush: failure (%d) address %lud\n", err, va);
+ return (int)err;
+}
+
+void
+oshostintr(Proc *p)
+{
+ pthread_kill((pthread_t)p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ Sem *sem;
+
+ sem = up->os;
+ pthread_mutex_lock(&sem->m);
+ while(sem->v == 0)
+ pthread_cond_wait(&sem->c, &sem->m);
+ sem->v--;
+ pthread_mutex_unlock(&sem->m);
+}
+
+void
+osready(Proc *p)
+{
+ Sem *sem;
+
+ sem = p->os;
+ pthread_mutex_lock(&sem->m);
+ sem->v++;
+ pthread_cond_signal(&sem->c);
+ pthread_mutex_unlock(&sem->m);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON | ECHO | ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ exit(0);
+}
+
+
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+ struct passwd *pwd;
+
+ if((pwd = getpwnam("nobody"))) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+libinit(char *imod)
+{
+ struct passwd *pw;
+ Proc *p;
+ char sys[64];
+
+ setsid();
+
+ // setup personality
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ if(dflag == 0)
+ termset();
+
+ setsigs();
+
+ if(pthread_key_create(&prdakey, NULL))
+ print("key_create failed\n");
+
+ p = newproc();
+ if(pthread_setspecific(prdakey, p))
+ panic("set specific thread data failed\n");
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ up->env->uid = getuid();
+ up->env->gid = getgid();
+
+ emuinit(imod);
+}
+
+
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t, NULL) < 0)
+ return(0);
+ if(sec0 == 0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return((t.tv_sec - sec0) * 1000 + (t.tv_usec - usec0 + 500) / 1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+ time.tv_sec = milsec / 1000;
+ time.tv_nsec = (milsec % 1000) * 1000000;
+ nanosleep(&time, nil);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ pthread_yield_np();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+// pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+__typeof__(sbrk(0))
+sbrk(int size)
+{
+ void *brk;
+ kern_return_t err;
+
+ err = vm_allocate( (vm_map_t) mach_task_self(),
+ (vm_address_t *)&brk,
+ size,
+ VM_FLAGS_ANYWHERE);
+ if(err != KERN_SUCCESS)
+ brk = (void*)-1;
+ return brk;
+}
--- /dev/null
+++ b/emu/MacOSX/win.c
@@ -1,0 +1,735 @@
+// in this file, _Rect is os x Rect,
+// _Point is os x Point
+#define Point _Point
+#define Rect _Rect
+
+#include <Carbon/Carbon.h>
+//#include <QuickTime/QuickTime.h> // for full screen
+
+#undef Rect
+#undef Point
+
+#undef nil
+
+#include "dat.h"
+#include "fns.h"
+#undef log2
+#include <draw.h>
+#include <memdraw.h>
+#include "cursor.h"
+#include "keyboard.h"
+#include "keycodes.h"
+
+#define Kup Up
+#define Kleft Left
+#define Kdown Down
+#define Kright Right
+#define Kalt LAlt
+#define Kctl LCtrl
+#define Kshift LShift
+#define Kpgup Pgup
+#define Kpgdown Pgdown
+#define Khome Home
+#define Kins Ins
+#define Kend End
+
+#define rWindowResource 128
+
+extern void flushmemscreen(Rectangle);
+
+Memimage *gscreen;
+
+static int readybit;
+static Rendez rend;
+static int triedscreen;
+
+///
+// menu
+//
+static MenuRef windMenu;
+static MenuRef viewMenu;
+
+enum {
+ kQuitCmd = 1,
+ kFullScreenCmd = 2,
+};
+
+static WindowGroupRef winGroup = NULL;
+static WindowRef theWindow = NULL;
+static CGContextRef context;
+static CGDataProviderRef dataProviderRef;
+static CGImageRef fullScreenImage;
+static CGRect devRect;
+static CGRect bounds;
+static PasteboardRef appleclip;
+static _Rect winRect;
+
+static Boolean altPressed = false;
+static Boolean button2 = false;
+static Boolean button3 = false;
+
+static Boolean needflush = false;
+
+
+static int
+isready(void*a)
+{
+ return readybit;
+}
+
+CGContextRef QuartzContext;
+
+static OSStatus MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
+static OSStatus MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
+
+static void winproc(void *a);
+static void flushproc(void *a);
+
+void
+screeninit(void)
+{
+ int fmt;
+ int dx, dy;
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ SetFrontProcess(&psn);
+
+ fmt = XBGR32; //XRGB32;
+
+ devRect = CGDisplayBounds(CGMainDisplayID());
+// devRect.origin.x = 0;
+// devRect.origin.y = 0;
+// devRect.size.width = 1024;
+// devRect.size.height = 768;
+ dx = devRect.size.width;
+ dy = devRect.size.height;
+
+ if(1){ /* TO DO: new dev draw for changing screen size */
+ dx = Xsize;
+ dy = Ysize;
+ }
+
+ gscreen = allocmemimage(Rect(0,0,dx,dy), fmt);
+ dataProviderRef = CGDataProviderCreateWithData(0, gscreen->data->bdata,
+ dx * dy * 4, 0);
+ fullScreenImage = CGImageCreate(dx, dy, 8, 32, dx * 4,
+ CGColorSpaceCreateDeviceRGB(),
+ kCGImageAlphaNoneSkipLast,
+ dataProviderRef, 0, 0, kCGRenderingIntentDefault);
+
+ kproc("osxscreen", winproc, nil, 0);
+ kproc("osxflush", flushproc, nil, 0);
+ Sleep(&rend, isready, nil);
+}
+
+void
+window_resized(void)
+{
+ GetWindowBounds(theWindow, kWindowContentRgn, &winRect);
+
+ bounds = CGRectMake(0, 0, winRect.right-winRect.left, winRect.bottom - winRect.top);
+}
+
+static void
+flushproc(void *a)
+{
+ for(;;) {
+ if(needflush) {
+ drawqlock();
+ needflush = false;
+ QDBeginCGContext(GetWindowPort(theWindow), &context);
+ CGContextFlush(context);
+ QDEndCGContext(GetWindowPort(theWindow), &context);
+ drawqunlock();
+ }
+ usleep(33333);
+ }
+}
+
+static void
+winproc(void *a)
+{
+ MenuItemIndex index;
+ int dx, dy;
+
+ winRect.left = 30;
+ winRect.top = 60;
+ dx = devRect.size.width*0.75; /* devRect is full screen; take only most of it */
+ dy = devRect.size.height*0.75;
+ if(1){ /* TO DO */
+ dx = Xsize;
+ dy = Ysize;
+ }
+ winRect.bottom = winRect.top + dy;
+ winRect.right = winRect.left + dx;
+
+ ClearMenuBar();
+ InitCursor();
+
+ CreateStandardWindowMenu(0, &windMenu);
+ InsertMenu(windMenu, 0);
+
+ CreateNewMenu(1004, 0, &viewMenu);
+ SetMenuTitleWithCFString(viewMenu, CFSTR("View"));
+ AppendMenuItemTextWithCFString(viewMenu, CFSTR("Full Screen"), 0,
+ kFullScreenCmd, &index);
+ SetMenuItemCommandKey(viewMenu, index, 0, 'F');
+ AppendMenuItemTextWithCFString(viewMenu, CFSTR("ctrl-opt to return"),
+ kMenuItemAttrDisabled,
+ kFullScreenCmd, &index);
+ InsertMenu(viewMenu, GetMenuID(windMenu));
+
+ DrawMenuBar();
+ uint32_t windowAttrs = 0
+ | kWindowCloseBoxAttribute
+ | kWindowCollapseBoxAttribute
+// | kWindowResizableAttribute // TO DO
+ | kWindowStandardHandlerAttribute
+// | kWindowFullZoomAttribute // TO DO
+ ;
+
+ CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow);
+ CreateWindowGroup(0, &winGroup);
+ SetWindowGroup(theWindow, winGroup);
+
+ SetWindowTitleWithCFString(theWindow, CFSTR("Inferno"));
+
+ if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr)
+ sysfatal("pasteboard create failed");
+
+ const EventTypeSpec commands[] = {
+ { kEventClassWindow, kEventWindowClosed },
+ { kEventClassWindow, kEventWindowBoundsChanged },
+ { kEventClassCommand, kEventCommandProcess }
+ };
+ const EventTypeSpec events[] = {
+ { kEventClassKeyboard, kEventRawKeyDown },
+ { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+ { kEventClassKeyboard, kEventRawKeyRepeat },
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseMoved },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseWheelMoved },
+ };
+
+ InstallApplicationEventHandler (
+ NewEventHandlerUPP (MainWindowEventHandler),
+ GetEventTypeCount(events),
+ events,
+ NULL,
+ NULL);
+ InstallWindowEventHandler (
+ theWindow,
+ NewEventHandlerUPP (MainWindowCommandHandler),
+ GetEventTypeCount(commands),
+ commands,
+ theWindow,
+ NULL);
+
+ ShowWindow(theWindow);
+ ShowMenuBar();
+ window_resized();
+ SelectWindow(theWindow);
+ // Run the event loop
+ readybit = 1;
+ Wakeup(&rend);
+ RunApplicationEventLoop();
+
+}
+
+static int
+convert_key(UInt32 key, UInt32 charcode)
+{
+ switch(key) {
+ case QZ_IBOOK_ENTER:
+ case QZ_RETURN: return '\n';
+ case QZ_ESCAPE: return 27;
+ case QZ_BACKSPACE: return '\b';
+ case QZ_LALT: return Kalt;
+ case QZ_LCTRL: return Kctl;
+ case QZ_LSHIFT: return Kshift;
+ case QZ_F1: return KF+1;
+ case QZ_F2: return KF+2;
+ case QZ_F3: return KF+3;
+ case QZ_F4: return KF+4;
+ case QZ_F5: return KF+5;
+ case QZ_F6: return KF+6;
+ case QZ_F7: return KF+7;
+ case QZ_F8: return KF+8;
+ case QZ_F9: return KF+9;
+ case QZ_F10: return KF+10;
+ case QZ_F11: return KF+11;
+ case QZ_F12: return KF+12;
+ case QZ_INSERT: return Kins;
+ case QZ_DELETE: return 0x7F;
+ case QZ_HOME: return Khome;
+ case QZ_END: return Kend;
+ case QZ_KP_PLUS: return '+';
+ case QZ_KP_MINUS: return '-';
+ case QZ_TAB: return '\t';
+ case QZ_PAGEUP: return Kpgup;
+ case QZ_PAGEDOWN: return Kpgdown;
+ case QZ_UP: return Kup;
+ case QZ_DOWN: return Kdown;
+ case QZ_LEFT: return Kleft;
+ case QZ_RIGHT: return Kright;
+ case QZ_KP_MULTIPLY: return '*';
+ case QZ_KP_DIVIDE: return '/';
+ case QZ_KP_ENTER: return '\n';
+ case QZ_KP_PERIOD: return '.';
+ case QZ_KP0: return '0';
+ case QZ_KP1: return '1';
+ case QZ_KP2: return '2';
+ case QZ_KP3: return '3';
+ case QZ_KP4: return '4';
+ case QZ_KP5: return '5';
+ case QZ_KP6: return '6';
+ case QZ_KP7: return '7';
+ case QZ_KP8: return '8';
+ case QZ_KP9: return '9';
+ default: return charcode;
+ }
+}
+
+void
+sendbuttons(int b, int x, int y)
+{
+ mousetrack(b, x, y, 0);
+}
+
+static Ptr fullScreenRestore;
+static int amFullScreen = 0;
+static WindowRef oldWindow = NULL;
+
+static void
+leave_full_screen(void)
+{
+ if(amFullScreen){
+ EndFullScreen(fullScreenRestore, 0);
+ theWindow = oldWindow;
+ ShowWindow(theWindow);
+ amFullScreen = 0;
+ window_resized();
+ Rectangle rect = { { 0, 0 }, { bounds.size.width, bounds.size.height} };
+ drawqlock();
+ flushmemscreen(rect);
+ drawqunlock();
+ }
+}
+
+static void
+full_screen(void)
+{
+ if(!amFullScreen){
+ oldWindow = theWindow;
+ HideWindow(theWindow);
+ BeginFullScreen(&fullScreenRestore, 0, 0, 0, &theWindow, 0, 0);
+ amFullScreen = 1;
+ window_resized();
+ Rectangle rect = { { 0, 0 },
+ { bounds.size.width,
+ bounds.size.height} };
+ drawqlock();
+ flushmemscreen(rect);
+ drawqunlock();
+ }
+}
+
+static OSStatus
+MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
+{
+ OSStatus result = noErr;
+ result = CallNextEventHandler(nextHandler, event);
+ UInt32 class = GetEventClass (event);
+ UInt32 kind = GetEventKind (event);
+ static uint32_t mousebuttons = 0; // bitmask of buttons currently down
+ static uint32_t mouseX = 0;
+ static uint32_t mouseY = 0;
+
+ if(class == kEventClassKeyboard) {
+ char macCharCodes;
+ UInt32 macKeyCode;
+ UInt32 macKeyModifiers;
+
+ GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar,
+ NULL, sizeof(macCharCodes), NULL, &macCharCodes);
+ GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL,
+ sizeof(macKeyCode), NULL, &macKeyCode);
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL,
+ sizeof(macKeyModifiers), NULL, &macKeyModifiers);
+ switch(kind) {
+ case kEventRawKeyModifiersChanged:
+ if (macKeyModifiers == (controlKey | optionKey)) leave_full_screen();
+
+ switch(macKeyModifiers & (optionKey | cmdKey)) {
+ case (optionKey | cmdKey):
+ /* due to chording we need to handle the case when both
+ * modifier keys are pressed at the same time.
+ * currently it's only 2-3 snarf and the 3-2 noop
+ */
+ altPressed = true;
+ if(mousebuttons & 1 || mousebuttons & 2 || mousebuttons & 4) {
+ mousebuttons |= 2; /* set button 2 */
+ mousebuttons |= 4; /* set button 3 */
+ button2 = true;
+ button3 = true;
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ }
+ break;
+ case optionKey:
+ altPressed = true;
+ if(mousebuttons & 1 || mousebuttons & 4) {
+ mousebuttons |= 2; /* set button 2 */
+ button2 = true;
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ }
+ break;
+ case cmdKey:
+ if(mousebuttons & 1 || mousebuttons & 2) {
+ mousebuttons |= 4; /* set button 3 */
+ button3 = true;
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ }else
+ gkbdputc(gkbdq, Latin);
+ break;
+ case 0:
+ default:
+ if(button2 || button3) {
+ if(button2) {
+ mousebuttons &= ~2; /* clear button 2 */
+ button2 = false;
+ altPressed = false;
+ }
+ if(button3) {
+ mousebuttons &= ~4; /* clear button 3 */
+ button3 = false;
+ }
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ }
+ if(altPressed) {
+ gkbdputc(gkbdq, Kalt);
+ altPressed = false;
+ }
+ break;
+ }
+ break;
+ case kEventRawKeyDown:
+ case kEventRawKeyRepeat:
+ if(macKeyModifiers != cmdKey) {
+ int key;
+ key = convert_key(macKeyCode, macCharCodes);
+ if(key != -1)
+ gkbdputc(gkbdq, key);
+ }else
+ result = eventNotHandledErr;
+ break;
+ default:
+ break;
+ }
+ }
+ else if(class == kEventClassMouse) {
+ _Point mousePos;
+
+ GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
+ 0, sizeof mousePos, 0, &mousePos);
+
+ switch(kind) {
+ case kEventMouseWheelMoved:
+ {
+ int32_t wheeldelta;
+ GetEventParameter(event,kEventParamMouseWheelDelta,typeSInt32,
+ 0,sizeof(wheeldelta), 0, &wheeldelta);
+ mouseX = mousePos.h - winRect.left;
+ mouseY = mousePos.v - winRect.top;
+ sendbuttons(wheeldelta>0 ? 8 : 16, mouseX, mouseY);
+ break;
+ }
+ case kEventMouseUp:
+ case kEventMouseDown:
+ {
+ uint32_t buttons;
+ uint32_t modifiers;
+ uint32_t clkcnt;
+
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
+ 0, sizeof(modifiers), 0, &modifiers);
+ GetEventParameter(event, kEventParamMouseChord, typeUInt32,
+ 0, sizeof buttons, 0, &buttons);
+ GetEventParameter(event, kEventParamClickCount, typeUInt32,
+ 0, sizeof(clkcnt), 0, &clkcnt);
+
+ /* simulate other buttons via alt/apple key. like x11 */
+ if(modifiers & optionKey) {
+ mousebuttons = ((buttons & 1) ? 2 : 0);
+ altPressed = false;
+ } else if(modifiers & cmdKey)
+ mousebuttons = ((buttons & 1) ? 4 : 0);
+ else
+ mousebuttons = (buttons & 1);
+
+ mousebuttons |= ((buttons & 2)<<1);
+ mousebuttons |= ((buttons & 4)>>1);
+ if(clkcnt > 1)
+ mousebuttons |= 1<<8;
+
+ } /* Fallthrough */
+ case kEventMouseMoved:
+ case kEventMouseDragged:
+ mouseX = mousePos.h - winRect.left;
+ mouseY = mousePos.v - winRect.top;
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ break;
+ default:
+ result = eventNotHandledErr;
+ break;
+ }
+ }
+ return result;
+}
+
+
+//default window command handler (from menus)
+static OSStatus
+MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
+{
+ OSStatus result = noErr;
+ UInt32 class = GetEventClass (event);
+ UInt32 kind = GetEventKind (event);
+
+ result = CallNextEventHandler(nextHandler, event);
+
+ if(class == kEventClassCommand) {
+ HICommand theHICommand;
+ GetEventParameter(event, kEventParamDirectObject, typeHICommand,
+ NULL, sizeof(HICommand), NULL, &theHICommand);
+
+ switch(theHICommand.commandID) {
+ case kHICommandQuit:
+ cleanexit(0);
+ break;
+
+ case kFullScreenCmd:
+ full_screen();
+ break;
+
+ default:
+ result = eventNotHandledErr;
+ break;
+ }
+ } else if(class == kEventClassWindow) {
+ WindowRef window;
+ _Rect rectPort = {0,0,0,0};
+
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef,
+ NULL, sizeof(WindowRef), NULL, &window);
+
+ if(window)
+ GetPortBounds(GetWindowPort(window), &rectPort);
+
+ switch(kind) {
+ case kEventWindowClosed:
+ theWindow = NULL;
+ cleanexit(0); // only one window
+ break;
+
+ //resize window
+ case kEventWindowBoundsChanged:
+ window_resized();
+ Rectangle rect = { { 0, 0 },
+ { bounds.size.width,
+ bounds.size.height} };
+ drawqlock();
+ flushmemscreen(rect);
+ drawqunlock();
+ break;
+
+ default:
+ result = eventNotHandledErr;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ CGRect rbounds;
+
+ // sanity check. Trips from the initial "terminal"
+ if (r.max.x < r.min.x || r.max.y < r.min.y)
+ return;
+
+ rbounds.size.width = r.max.x - r.min.x;
+ rbounds.size.height = r.max.y - r.min.y;
+ rbounds.origin.x = r.min.x;
+ rbounds.origin.y = r.min.y;
+
+ if(rbounds.size.width <= 0 || rbounds.size.height <= 0)
+ return;
+
+ QDBeginCGContext(GetWindowPort(theWindow), &context);
+
+ // The sub-image is relative to our whole screen image.
+ CGImageRef subimg = CGImageCreateWithImageInRect(fullScreenImage, rbounds);
+
+ // Drawing the sub-image is relative to the window.
+ rbounds.origin.y = winRect.bottom - winRect.top - r.min.y - rbounds.size.height;
+ CGContextDrawImage(context, rbounds, subimg);
+ CGImageRelease(subimg);
+ QDEndCGContext(GetWindowPort(theWindow), &context);
+
+ needflush = true;
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
+{
+ if(!triedscreen) {
+ triedscreen = 1;
+ screeninit(); /* TO DO: call this elsewhere? */
+ }
+ *r = gscreen->r;
+ *chan = gscreen->chan;
+ *depth = gscreen->depth;
+ *width = gscreen->width;
+ *softscreen = 1;
+
+ return gscreen->data->bdata;
+}
+
+// PAL - no palette handling. Don't intend to either.
+void
+getcolor(ulong i, ulong *r, ulong *g, ulong *b)
+{
+
+// PAL: Certainly wrong to return a grayscale.
+ *r = i;
+ *g = i;
+ *b = i;
+}
+
+void
+setcolor(ulong index, ulong r, ulong g, ulong b)
+{
+ USED(index); USED(r); USED(g); USED(b);
+}
+
+enum{
+ SnarfSize= 100*1024
+};
+
+static char snarf[3*SnarfSize+1];
+static Rune rsnarf[SnarfSize+1];
+
+char*
+clipread(void)
+{
+ CFDataRef cfdata;
+ OSStatus err = noErr;
+ ItemCount nitems;
+ int i;
+ char *s;
+
+ if(appleclip == NULL)
+ return nil;
+ // Wow. This is ridiculously complicated.
+ PasteboardSynchronize(appleclip);
+ if((err = PasteboardGetItemCount(appleclip, &nitems)) != noErr) {
+ fprint(2, "apple pasteboard GetItemCount failed - Error %d\n", err);
+ return 0;
+ }
+
+ // Yes, based at 1. Silly API.
+ for(i = 1; i <= nitems; i++) {
+ PasteboardItemID itemID;
+ CFArrayRef flavorTypeArray;
+ CFIndex flavorCount;
+
+ if((err = PasteboardGetItemIdentifier(appleclip, i, &itemID)) != noErr){
+ fprint(2, "Can't get pasteboard item identifier: %d\n", err);
+ return 0;
+ }
+
+ if((err = PasteboardCopyItemFlavors(appleclip, itemID, &flavorTypeArray))!=noErr){
+ fprint(2, "Can't copy pasteboard item flavors: %d\n", err);
+ return 0;
+ }
+
+ flavorCount = CFArrayGetCount(flavorTypeArray);
+ CFIndex flavorIndex;
+ for(flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex){
+ CFStringRef flavorType;
+ flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
+ if (UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text"))){
+ if((err = PasteboardCopyItemFlavorData(appleclip, itemID,
+ CFSTR("public.utf16-plain-text"), &cfdata)) != noErr){
+ fprint(2, "apple pasteboard CopyItem failed - Error %d\n", err);
+ return 0;
+ }
+ CFIndex length = CFDataGetLength(cfdata);
+ if (length > sizeof rsnarf) length = sizeof rsnarf;
+ CFDataGetBytes(cfdata, CFRangeMake(0, length), (uint8_t *)rsnarf);
+ snprint(snarf, sizeof snarf, "%.*S", length/sizeof(Rune), rsnarf);
+ for(s = snarf; *s; s++)
+ if(*s == '\r')
+ *s = '\n';
+ CFRelease(cfdata);
+ return strdup(snarf);
+ }
+ }
+ }
+ return 0;
+}
+
+int
+clipwrite(char *snarf)
+{
+ CFDataRef cfdata;
+ PasteboardSyncFlags flags;
+
+ if(appleclip == NULL)
+ return 0;
+ runeseprint(rsnarf, rsnarf+nelem(rsnarf), "%s", snarf);
+ if(PasteboardClear(appleclip) != noErr){
+ fprint(2, "apple pasteboard clear failed\n");
+ return 0;
+ }
+ flags = PasteboardSynchronize(appleclip);
+ if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
+ fprint(2, "apple pasteboard cannot assert ownership\n");
+ return 0;
+ }
+ cfdata = CFDataCreate(kCFAllocatorDefault, (uchar*)rsnarf, runestrlen(rsnarf)*2);
+ if(cfdata == nil){
+ fprint(2, "apple pasteboard cfdatacreate failed\n");
+ return 0;
+ }
+ if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1,
+ CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
+ fprint(2, "apple pasteboard putitem failed\n");
+ CFRelease(cfdata);
+ return 0;
+ }
+ CFRelease(cfdata);
+ return 1;
+}
+
+void
+setpointer(int x, int y)
+{
+ CGPoint pnt;
+
+ pnt.x = x + winRect.left;
+ pnt.y = y + winRect.top;
+ CGWarpMouseCursorPosition(pnt);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ USED(c);
+ /* removed, pending extensive change for newer MacOS X */
+}
--- /dev/null
+++ b/emu/NOTICE
@@ -1,0 +1,32 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+ Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+ Portions Copyright © 1997-1999 Vita Nuova Limited
+ Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+ Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+ Portions Copyright © 2005 Russ Cox, MIT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
--- /dev/null
+++ b/emu/NetBSD/asm-386.S
@@ -1,0 +1,101 @@
+ .file "asm-NetBSD-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+ .type executeonnewstack,@function
+ .global executeonnewstack
+executeonnewstack:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ movl 8(%ebp), %esi /* get tos */
+ subl $4, %esi
+ movl 16(%ebp), %eax
+ movl %eax, (%esi) /* stash arg on new stack */
+ subl $4, %esi
+ movl 12(%ebp), %eax
+ movl %eax, (%esi) /* stash tramp on new stack */
+ mov %esi, %esp /* swap stacks pronto */
+ popl %eax /* recover the tramp address */
+ call *%eax /* and jump to it (ho ho) */
+
+ /* if we return here, tramp didn't do its job */
+
+ addl $8, %esp /* clean up for pose value */
+
+ leal SYS_exit, %eax
+ int $0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ * between the unlock and the system call, but this should
+ * not matter since no task is waiting for the result
+ */
+
+ .type unlockandexit,@function
+ .global unlockandexit
+unlockandexit:
+ pushl %ebp
+ movl %esp, %ebp
+
+ movl 8(%ebp), %esi /* get the key address */
+ pushl $0 /* exit status 0 */
+ movl $0, %eax /* unlock the stack allocator */
+ movl %eax, (%esi)
+ leal SYS_exit, %eax /* call exit */
+ int $0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/NetBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_SPEAKER
+#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT
+#define Audio_Lineout_Val SOUND_MIXER_VOLUME
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING "/dev/dsp"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file = -1; /* file in/out */
+static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */
+static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ int i;
+ static ushort flag = 1;
+
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+ for (i = 0; i < 32; i++)
+ mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ char ebuf[ERRMAX];
+
+ if (debug)
+ print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file;
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+ if (debug)
+ print("audio_file_open: success\nin %d out %d both %d\n",
+ audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_out < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ qunlock(&outlock);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_in < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ audio_file_out = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ close(audio_file);
+ audio_file_in = audio_file_out = audio_file = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(! audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ if (debug > 1)
+ print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+ c, va, count, offset);
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+static int
+audio_open(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ if (debug)
+ print("audio_open: failed to set blocking\n");
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if (debug)
+ print("audio_open: blocking set\n");
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(! audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+ int status;
+ osenter();
+ status = ioctl(fd, ctl, info); /* qlock and load general stuff */
+ osleave();
+ if (status < 0)
+ print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+ return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+ int newbits, newenc;
+
+ newbits = i->bits;
+ newenc = i->enc;
+ switch (newenc) {
+ case Audio_Alaw_Val:
+ if (newbits == 8)
+ return AFMT_A_LAW;
+ break;
+ case Audio_Ulaw_Val:
+ if (newbits == 8)
+ return AFMT_MU_LAW;
+ break;
+ case Audio_Pcm_Val:
+ if (newbits == 8)
+ return AFMT_U8;
+ else if (newbits == 16)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+
+ if(fd < 0)
+ return 0;
+
+ /* fmt */
+ if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+ int oldfmt, newfmt;
+ oldfmt = AFMT_QUERY;
+ if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+ return 0;
+ if (debug)
+ print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+ newfmt = choosefmt(i);
+ if (debug)
+ print("audio_set_info: new format 0x%.8lux\n", newfmt);
+ if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG) {
+ int channels = i->chan;
+ if (debug)
+ print("audio_set_info: new channels %d\n", channels);
+ if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+ || channels != i->chan)
+ return 0;
+ }
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG) {
+ int speed = i->rate;
+ if (debug)
+ print("audio_set_info: new speed %d\n", speed);
+ if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+ return 0;
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+ int val;
+ if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+ mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+ if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+ mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+ val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+ doioctl(fd, MIXER_WRITE(i->dev), &val);
+ }
+
+ if (i->flags & AUDIO_DEV_FLAG) {
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ USED(fd);
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ USED(fd);
+ USED(f);
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int x;
+ return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/NetBSD/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/NetBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * NetBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/dty00",
+ "/dev/dty01",
+ "/dev/dty02",
+ "/dev/dty03",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/NetBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/NetBSD/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/NetBSD/emu-g
@@ -1,0 +1,102 @@
+env
+ X11LIBS=
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ math
+
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/NetBSD/emu.c
@@ -1,0 +1,88 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+
+
+#include "emu.root.h"
+
+ulong ndevs = 29;
+
+extern Dev rootdevtab;
+extern Dev consdevtab;
+extern Dev envdevtab;
+extern Dev mntdevtab;
+extern Dev pipedevtab;
+extern Dev progdevtab;
+extern Dev profdevtab;
+extern Dev srvdevtab;
+extern Dev dupdevtab;
+extern Dev ssldevtab;
+extern Dev capdevtab;
+extern Dev fsdevtab;
+extern Dev cmddevtab;
+extern Dev indirdevtab;
+extern Dev drawdevtab;
+extern Dev pointerdevtab;
+extern Dev snarfdevtab;
+extern Dev ipdevtab;
+extern Dev eiadevtab;
+extern Dev audiodevtab;
+extern Dev memdevtab;
+Dev* devtab[]={
+ &rootdevtab,
+ &consdevtab,
+ &envdevtab,
+ &mntdevtab,
+ &pipedevtab,
+ &progdevtab,
+ &profdevtab,
+ &srvdevtab,
+ &dupdevtab,
+ &ssldevtab,
+ &capdevtab,
+ &fsdevtab,
+ &cmddevtab,
+ &indirdevtab,
+ &drawdevtab,
+ &pointerdevtab,
+ &snarfdevtab,
+ &ipdevtab,
+ &eiadevtab,
+ &audiodevtab,
+ &memdevtab,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+};
+
+void links(void){
+}
+
+extern void sysmodinit(void);
+extern void drawmodinit(void);
+extern void tkmodinit(void);
+extern void mathmodinit(void);
+extern void srvmodinit(void);
+extern void keyringmodinit(void);
+extern void loadermodinit(void);
+extern void freetypemodinit(void);
+void modinit(void){
+ sysmodinit();
+ drawmodinit();
+ tkmodinit();
+ mathmodinit();
+ srvmodinit();
+ keyringmodinit();
+ loadermodinit();
+ freetypemodinit();
+}
+
+char* conffile = "emu";
+ulong kerndate = KERNDATE;
--- /dev/null
+++ b/emu/NetBSD/mkfile
@@ -1,0 +1,49 @@
+SYSTARG=NetBSD
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=NetBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+# can remove or override X11LIBS using env section in config files
+X11LIBS= -L/usr/X11R7/lib -R/usr/X11R7/lib -lXext -lX11
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= ${X11LIBS} -lossaudio -lm
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/NetBSD/os.c
@@ -1,0 +1,537 @@
+#include <sys/types.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+/* For dynamic linking init/fini code that needs malloc */
+void (*coherence)(void) = nofence;
+
+
+enum
+{
+ DELETE = 0x7f,
+ CTRLC = 'C'-'@',
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "NetBSD";
+
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+void executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg);
+void unlockandexit(ulong *key);
+
+extern int dflag;
+
+int gidnobody = -1;
+int uidnobody = -1;
+static struct termios tinit;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ void *kstack;
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", up->text, msg);
+
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ kstack = up->kstack;
+ free(up->prog);
+ free(up);
+ if(kstack != nil)
+ stackfreeandexit(kstack);
+}
+
+int
+tramp(void *arg)
+{
+ Proc *p;
+ p = arg;
+ p->pid = p->sigid = getpid();
+ (*p->func)(p->arg);
+ pexit("{Tramp}", 0);
+ return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ int pid;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ void *tos;
+
+ p = newproc();
+ if(0)
+ print("start %s:%.8lx\n", name, p);
+ if(p == nil) {
+ print("kproc(%s): no memory", name);
+ panic("kproc: no memory");
+ }
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ if(flags & KPX11){
+ p->kstack = nil; /* never freed; also up not defined */
+ tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+ }else
+ p->kstack = stackalloc(p, &tos);
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if (__clone(tramp, tos, /*CLONE_PTRACE|*/CLONE_VM|CLONE_FS|CLONE_FILES|SIGCHLD, p) <= 0) {
+ fprint(2, "emu: clone failed: %s\n", strerror(errno));
+ panic("kproc: clone failed");
+ }
+}
+
+/*
+ * TO DO:
+ * To get pc on trap, use sigaction instead of signal and
+ * examine its siginfo structure
+ */
+
+/*
+static void
+diserr(char *s, int pc)
+{
+ char buf[ERRMAX];
+
+ snprint(buf, sizeof(buf), "%s: pc=0x%lux", s, pc);
+ disfault(nil, buf);
+}
+*/
+
+static void
+trapILL(int signo)
+{
+ USED(signo);
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+trapBUS(int signo)
+{
+ USED(signo);
+ disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(int signo)
+{
+ USED(signo);
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+ char buf[64];
+ USED(signo);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+ disfault(nil, buf);
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+/* called to wake up kproc blocked on a syscall */
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+static void
+trapUSR2(int signo)
+{
+ USED(signo);
+ /* we've done our work of interrupting sigsuspend */
+}
+
+void
+osblock(void)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGUSR2);
+ sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+ if(kill(p->sigid, SIGUSR2) < 0)
+ fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ error("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct termios t;
+ struct sigaction act;
+ sigset_t mask;
+ struct passwd *pw;
+ Proc *p;
+ void *tos;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+
+ pw = getpwnam("nobody");
+ if(pw != nil) {
+ uidnobody = pw->pw_uid;
+ gidnobody = pw->pw_gid;
+ }
+
+ if(dflag == 0)
+ termset();
+
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler = trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler = trapUSR2;
+ sigaction(SIGUSR2, &act, nil);
+
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &act, nil);
+
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+
+ if(sflag == 0) {
+ act.sa_handler = trapBUS;
+ sigaction(SIGBUS, &act, nil);
+ act.sa_handler = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_handler = trapSEGV;
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_handler = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ }
+
+ p = newproc();
+ p->kstack = stackalloc(p, &tos);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ buf[0] = 'H' - '@';
+ break;
+ case CTRLC:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+
+ if(sec0 == 0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time, NULL);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ sched_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+ Lock l;
+ void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+ *((void **)stack) = stacklist.free;
+ stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+ lock(&stacklist.l);
+ _stackfree(stack);
+ unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+ void *rv;
+ lock(&stacklist.l);
+ if (stacklist.free == 0) {
+ int x;
+ /*
+ * obtain some more by using sbrk()
+ */
+ void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+ if (more == 0)
+ panic("stackalloc: no more stacks");
+ /*
+ * align to KSTACK
+ */
+ more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+ /*
+ * free all the new stacks onto the freelist
+ */
+ for (x = 0; x < NSTACKSPERALLOC; x++)
+ _stackfree((char *)more + KSTACK * x);
+ }
+ rv = stacklist.free;
+ stacklist.free = *(void **)rv;
+ unlock(&stacklist.l);
+ *tos = rv + KSTACK - sizeof(void *);
+ *(Proc **)rv = p;
+ return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a);
+ USED(n);
+ return 0;
+}
--- /dev/null
+++ b/emu/Nt/audio.c
@@ -1,0 +1,810 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#include <mmsystem.h>
+#undef Unknown
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#define Audio_Mic_Val 0
+#define Audio_Linein_Val -1
+
+#define Audio_Speaker_Val 0
+#define Audio_Headphone_Val -1
+#define Audio_Lineout_Val -1
+
+#define Audio_Pcm_Val WAVE_FORMAT_PCM
+#define Audio_Ulaw_Val (WAVE_FORMAT_PCM+1)
+#define Audio_Alaw_Val (WAVE_FORMAT_PCM+2)
+
+#define Audio_Max_Queue 8
+
+#define BUFLEN 1000
+
+#define INISOPEN 0x00000002 // the microphone is open
+#define OUTISOPEN 0x00000004 // the speaker is open
+#define INPUTISGOING 0x00000020 // microphone is being recorded/read
+
+#include "audio.h"
+#include "audio-tbls.c"
+
+static int debug = 0;
+
+/* TO DO: sensible expression of double-buffering */
+#define Ping 0
+#define Pong 1
+
+static HWAVEIN audio_file_in;
+static HWAVEOUT audio_file_out;
+
+static long out_buf_count;
+
+typedef struct _awin {
+ WAVEHDR hdr;
+ long sz;
+ char* ptr;
+ char data[Audio_Max_Buf];
+} AWin;
+
+static AWin audio_ping;
+static AWin audio_pong;
+
+static long paddle = Ping;
+static int ping_is_filling;
+static int pong_is_filling;
+
+static long audio_flags = 0;
+static int audio_init = 0;
+
+static QLock flag_lock;
+
+static Audio_t av;
+
+static HANDLE outlock;
+static HANDLE inlock;
+
+static int audio_open_in(HWAVEIN*, Audio_d*);
+static int audio_open_out(HWAVEOUT*, Audio_d*);
+static void audio_close_in(void);
+static void audio_close_out(void);
+static void CALLBACK waveInProc(HWAVEIN, UINT, DWORD, DWORD, DWORD);
+static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD, DWORD, DWORD);
+
+#define AUDIOIN 0
+#define AUDIOOUT 1
+
+/*
+* Error routines
+*/
+static int
+audioerror(unsigned int code, int in_out, char *msg)
+{
+ char errorText[MAXERRORLENGTH];
+
+ if (code != MMSYSERR_NOERROR) {
+ switch(in_out) {
+ case AUDIOIN:
+ waveInGetErrorText(code, errorText, sizeof(errorText));
+ //print("ERROR -- %s: %s\n", msg, errorText);
+ return(-1);
+ case AUDIOOUT:
+ waveOutGetErrorText(code, errorText, sizeof(errorText));
+ //print("ERROR -- %s: %s\n", msg, errorText);
+ return(-1);
+ default:
+ print("%s: Unknown device\n", msg);
+ }
+ }
+ //print("TRACE %s\n", msg);
+ return 0;
+}
+
+void
+audio_file_init(void)
+{
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ int in_is_open = 0;
+
+ switch(omode){
+ case OREAD:
+ qlock(&flag_lock);
+
+ if(waserror()) {
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & INISOPEN)
+ error(Einuse);
+
+ inlock = CreateMutex(NULL, FALSE, NULL);
+ if(inlock == NULL)
+ error(Einuse);
+
+ if(!audio_open_in(&audio_file_in, &av.in) ) {
+ CloseHandle(inlock);
+ error(Ebadarg);
+ }
+
+ ping_is_filling = 0;
+ pong_is_filling = 0;
+ paddle = Ping;
+ audio_flags |= INISOPEN;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ case OWRITE:
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & OUTISOPEN)
+ error(Einuse);
+
+ outlock = CreateMutex(NULL, FALSE, NULL);
+ if(outlock == NULL)
+ error(Einuse);
+
+ if(!audio_open_out(&audio_file_out, &av.out) ) {
+ CloseHandle(outlock);
+ error(Ebadarg);
+ }
+
+ out_buf_count = 0;
+ audio_flags |= OUTISOPEN;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ case ORDWR:
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ if(in_is_open)
+ audio_close_in();
+ nexterror();
+ }
+
+ if((audio_flags & INISOPEN) || (audio_flags & OUTISOPEN))
+ error(Einuse);
+
+ if(!audio_open_in(&audio_file_in, &av.in) )
+ error(Ebadarg);
+
+ in_is_open = 1;
+
+ if(!audio_open_out(&audio_file_out, &av.out)) {
+ CloseHandle(outlock);
+ error(Ebadarg);
+ }
+
+ inlock = CreateMutex(NULL, FALSE, NULL);
+ if(inlock == NULL)
+ error(Einuse);
+
+ outlock = CreateMutex(NULL, FALSE, NULL);
+ if(outlock == NULL) {
+ CloseHandle(inlock);
+ error(Einuse);
+ }
+
+ audio_flags |= INISOPEN;
+ audio_flags |= OUTISOPEN;
+ ping_is_filling = 0;
+ pong_is_filling = 0;
+ paddle = Ping;
+ out_buf_count = 0;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ default:
+ error(Egreg);
+ }
+}
+
+static int
+audio_open_in(HWAVEIN* h, Audio_d* d)
+{
+ HWAVEIN th;
+ WAVEFORMATEX format;
+
+ format.wFormatTag = d->enc;
+ format.nChannels = d->chan;
+ format.nSamplesPerSec = d->rate;
+ format.wBitsPerSample = d->bits;
+ format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if (audioerror(
+ waveInOpen(&th, WAVE_MAPPER, &format, (DWORD)waveInProc, 0, CALLBACK_FUNCTION),
+ AUDIOIN,
+ "cannot open microphone/line-in") == 0) {
+ *h = th;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+audio_open_out(HWAVEOUT* h, Audio_d* d)
+{
+ unsigned int code;
+ HWAVEOUT th;
+ WAVEFORMATEX format;
+
+ format.wFormatTag = d->enc;
+ format.nChannels = d->chan;
+ format.nSamplesPerSec = d->rate;
+ format.wBitsPerSample = d->bits;
+ format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ code = waveOutOpen(&th, WAVE_MAPPER, &format, (DWORD)waveOutProc, 0, CALLBACK_FUNCTION);
+
+ if (audioerror(code, AUDIOOUT, "cannot open speaker/line-out") == 0) {
+ out_buf_count = 0;
+ *h = th;
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&flag_lock);
+ audio_close_in();
+ audio_flags &= ~(INISOPEN|INPUTISGOING);
+ CloseHandle(inlock);
+ qunlock(&flag_lock);
+ break;
+ case OWRITE:
+ qlock(&flag_lock);
+ audio_close_out();
+ audio_flags &= ~OUTISOPEN;
+ CloseHandle(outlock);
+ qunlock(&flag_lock);
+ break;
+ case ORDWR:
+ qlock(&flag_lock);
+ audio_close_in();
+ audio_close_out();
+
+ audio_flags &= ~(INISOPEN|INPUTISGOING|OUTISOPEN);
+
+ CloseHandle(outlock);
+ CloseHandle(inlock);
+ qunlock(&flag_lock);
+ break;
+ }
+}
+
+static void
+audio_close_in()
+{
+ audioerror(waveInStop(audio_file_in), AUDIOIN, "audio_close_in Stop");
+ audioerror(waveInReset(audio_file_in), AUDIOIN, "audio_close_in Reset");
+
+ audioerror(waveInUnprepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR)), AUDIOIN, "in un prepare ping header");
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audioerror(waveInUnprepareHeader(audio_file_in, &audio_pong.hdr,
+ sizeof(WAVEHDR)), AUDIOIN, "in un prepare pong header");
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ audioerror(waveInClose(audio_file_in), AUDIOIN, "in close");
+
+}
+
+static void
+audio_close_out()
+{
+Again:
+ WaitForSingleObject(outlock, INFINITE);
+ while(out_buf_count > 0) {
+ ReleaseMutex(outlock);
+ sleep(0);
+ goto Again;
+ }
+ ReleaseMutex(outlock);
+
+ audioerror(waveOutReset(audio_file_out), AUDIOOUT, "close wave out reset");
+ audioerror(waveOutClose(audio_file_out), AUDIOOUT, "closing out device");
+}
+
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ MMRESULT status;
+ long len = av.in.buf * Audio_Max_Buf / Audio_Max_Val;
+ char *v = (char *) va;
+ char *p;
+ long ba, n, chunk, total;
+
+
+ qlock(&flag_lock);
+ WaitForSingleObject(inlock, INFINITE);
+
+ if(waserror()) {
+ audioerror(waveInStop(audio_file_in), AUDIOIN,
+ "audio_file_read Stop 1");
+ audioerror(waveInReset(audio_file_in), AUDIOIN,
+ "audio_file_read Reset 1");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare ping");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)),
+ AUDIOIN, "in unprepare pong");
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ ping_is_filling = pong_is_filling = 0;
+ paddle = Ping;
+
+ qunlock(&flag_lock);
+ ReleaseMutex(inlock);
+
+ nexterror();
+ }
+
+ if(!(audio_flags & INISOPEN))
+ error(Eperm);
+
+ /* check for block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(len < 1 || count % ba)
+ error(Ebadarg);
+
+ if(!(audio_flags & INPUTISGOING)) {
+ if(audioerror(waveInStart(audio_file_in), AUDIOIN,
+ "in start") == -1)
+ error(Eio);
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_ping.hdr.lpData = audio_ping.ptr;
+ audio_ping.hdr.dwBufferLength = len;
+ audio_ping.hdr.dwUser = Ping;
+ audio_ping.hdr.dwFlags = 0;
+
+ status = waveInPrepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOIN, "in prepare header") == -1)
+ error(Eio);
+
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+ audio_pong.hdr.lpData = audio_pong.ptr;
+ audio_pong.hdr.dwBufferLength = len;
+ audio_pong.hdr.dwUser = Pong;
+ audio_pong.hdr.dwFlags = 0;
+
+ status = waveInPrepareHeader(audio_file_in, &audio_pong.hdr,
+ sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOIN, "in prepare header") == -1)
+ error(Eio);
+
+ status = waveInAddBuffer(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+ if (audioerror(status, AUDIOIN, "file_read Add Buffer")== -1){
+ waveInUnprepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ error(Eio);
+ }
+
+ ping_is_filling = 1;
+ pong_is_filling = 0;
+ paddle = Ping;
+ audio_flags |= INPUTISGOING;
+ }
+ poperror();
+ ReleaseMutex(inlock);
+
+ total = 0;
+
+Draining:
+
+ WaitForSingleObject(inlock, INFINITE);
+ if(waserror()) {
+ audioerror(waveInStop(audio_file_in), AUDIOIN,
+ "audio_file_read Stop 2");
+ audioerror(waveInReset(audio_file_in), AUDIOIN,
+ "audio_file_read Reset 2");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare ping");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare pong");
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ audio_flags &= ~INPUTISGOING;
+
+ ReleaseMutex(inlock);
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ while((total < count) && ((audio_ping.sz > 0) || (audio_pong.sz > 0))) {
+ n = paddle == Ping ? audio_ping.sz : audio_pong.sz;
+ p = paddle == Ping ? audio_ping.ptr : audio_pong.ptr;
+
+ chunk = min(n, count - total);
+
+ memmove(v+total, p , chunk);
+
+ total += chunk;
+
+ if(paddle == Ping) {
+ if(!pong_is_filling) {
+
+ if(audioerror(waveInAddBuffer(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "draining ping calling add buffer pong") == -1)
+ error(Eio);
+
+ pong_is_filling = 1;
+ }
+
+ audio_ping.sz -= chunk;
+ if(audio_ping.sz > 0) {
+ audio_ping.ptr += chunk;
+ } else {
+ audio_ping.ptr = &audio_ping.data[0];
+ ping_is_filling = 0;
+ paddle = Pong;
+ }
+ } else {
+ if(!ping_is_filling) {
+
+ if(audioerror(waveInAddBuffer(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "draining pong calling add buffer ping") == -1)
+ error(Eio);
+
+ ping_is_filling = 1;
+ }
+
+ audio_pong.sz -= chunk;
+ if(audio_pong.sz > 0) {
+ audio_pong.ptr += chunk;
+ } else {
+ audio_pong.ptr = &audio_pong.data[0];
+ pong_is_filling = 0;
+ paddle = Ping;
+ }
+ }
+ }
+
+ poperror();
+
+ ReleaseMutex(inlock);
+
+ if(total == count) {
+ qunlock(&flag_lock);
+ return count;
+ }
+
+Filling:
+ WaitForSingleObject(inlock, INFINITE);
+ while((audio_ping.sz < 1) && (audio_pong.sz < 1)) {
+ ReleaseMutex(inlock);
+ sleep(0);
+ goto Filling;
+ }
+ ReleaseMutex(inlock);
+
+ goto Draining;
+}
+
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ MMRESULT status;
+ WAVEHDR *hHdr = (WAVEHDR *) NULL;
+ char *hData = NULL;
+ char *p = (char *) va;
+ long ba;
+ long bufsz;
+ long chunk;
+ long total;
+
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(!(audio_flags & OUTISOPEN))
+ error(Eperm);
+
+ /* check for block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz < 1)
+ error(Ebadarg);
+
+ total = 0;
+
+ while(total < count) {
+
+Again:
+ chunk = min(bufsz, count - total);
+
+Drain:
+ WaitForSingleObject(outlock, INFINITE);
+ while(out_buf_count > bufsz) {
+ ReleaseMutex(outlock);
+ sleep(0);
+ goto Drain;
+ }
+
+ if(out_buf_count == 0)
+ audioerror(waveOutReset(audio_file_out), AUDIOOUT, "wave out reset");
+ ReleaseMutex(outlock);
+
+ /*
+ * allocate and lock the memory for the wave header
+ * and data blocks
+ */
+ hHdr = (WAVEHDR *) malloc(sizeof(WAVEHDR));
+ if (!hHdr)
+ error(Enomem);
+
+ hData = malloc(chunk);
+ if (!hData) {
+ free(hHdr);
+ error(Enomem);
+ }
+
+ /*
+ * initialize the wave header struct
+ */
+
+ /*
+ * copy user data into write Q
+ */
+ memmove(hData, p+total, chunk);
+
+ hHdr->lpData = hData;
+ hHdr->dwBufferLength = chunk;
+ hHdr->dwBytesRecorded = 0;
+ hHdr->dwUser = chunk;
+ hHdr->dwFlags = 0;
+ hHdr->dwLoops = 0;
+ hHdr->lpNext = 0;
+ hHdr->reserved = 0;
+
+ status = waveOutPrepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOOUT, "out prepare header") == -1) {
+ free(hHdr);
+ free(hData);
+ error(Eio);
+ }
+
+ status =
+ waveOutWrite(audio_file_out, hHdr, sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOOUT, "out write data") == -1) {
+ waveOutUnprepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR));
+ free(hHdr);
+ free(hData);
+ error(Eio);
+ }
+
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count += chunk;
+ ReleaseMutex(outlock);
+
+ total += chunk;
+
+ }
+
+ poperror();
+ qunlock(&flag_lock);
+ osmillisleep(1); /* hack to get around thread scheduler */
+
+ return count;
+}
+
+void CALLBACK
+waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
+{
+ LPWAVEHDR hHdr;
+ long count;
+
+ switch(uMsg) {
+ case WIM_OPEN:
+ break;
+ case WIM_CLOSE:
+ break;
+ case WIM_DATA:
+ hHdr = (LPWAVEHDR)dwParam1;
+ if(hHdr != NULL) {
+ count = hHdr->dwBytesRecorded;
+ if(count > 0) {
+ WaitForSingleObject(inlock, INFINITE);
+ if(hHdr->dwUser == Ping)
+ audio_ping.sz = count;
+ else
+ audio_pong.sz = count;
+ ReleaseMutex(inlock);
+ }
+ }
+ break;
+ }
+ return;
+}
+
+
+void CALLBACK
+waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwOutstance, DWORD dwParam1, DWORD dwParam2)
+{
+ LPWAVEHDR hHdr;
+
+ switch(uMsg) {
+ case WOM_DONE:
+ hHdr = (LPWAVEHDR)dwParam1;
+ if(hHdr != NULL) {
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count -= hHdr->dwUser;
+ ReleaseMutex(outlock);
+ audioerror(
+ waveOutUnprepareHeader(
+ audio_file_out, hHdr, sizeof(WAVEHDR)),
+ AUDIOOUT, "out un prepare header");
+ if(hHdr->lpData != NULL)
+ free(hHdr->lpData);
+ free(hHdr);
+ }
+ break;
+ case WOM_CLOSE:
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count = 0;
+ ReleaseMutex(outlock);
+ break;
+ case WOM_OPEN:
+ break;
+ }
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ WAVEFORMATEX format;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if(!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if((tmpav.in.enc != Audio_Pcm_Val) || (tmpav.out.enc != Audio_Pcm_Val))
+ error(Ebadarg);
+
+ if(tmpav.in.flags & AUDIO_MOD_FLAG) {
+ format.wFormatTag = tmpav.in.enc;
+ format.wBitsPerSample = tmpav.in.bits;
+ format.nChannels = tmpav.in.chan;
+ format.nSamplesPerSec = tmpav.in.rate;
+ format.nBlockAlign =
+ (tmpav.in.chan * tmpav.in.bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if(audioerror(
+ waveInOpen(NULL, WAVE_MAPPER, &format, 0, 0, WAVE_FORMAT_QUERY),
+ AUDIOIN, "cannot open microphone/line-in to test parameters") == -1)
+ error(Ebadarg);
+
+ qlock(&flag_lock);
+
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & INISOPEN) {
+ audio_close_in();
+ audio_flags &= ~INISOPEN;
+ audio_flags &= ~INPUTISGOING;
+ if(!audio_open_in(&audio_file_in, &tmpav.in))
+ error(Eio);
+ audio_flags |= INISOPEN;
+ }
+ poperror();
+ qunlock(&flag_lock);
+ }
+
+ if(tmpav.out.flags & AUDIO_MOD_FLAG) {
+
+ format.wFormatTag = tmpav.out.enc;
+ format.wBitsPerSample = tmpav.out.bits;
+ format.nChannels = tmpav.out.chan;
+ format.nSamplesPerSec = tmpav.out.rate;
+ format.nBlockAlign =
+ (tmpav.out.chan * tmpav.out.bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if (audioerror(waveOutOpen(NULL, WAVE_MAPPER,
+ &format,
+ 0, 0, WAVE_FORMAT_QUERY),
+ AUDIOOUT, "cannot open output to test parameters") == -1)
+ error(Ebadarg);
+
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+ if(audio_flags & OUTISOPEN) {
+ audio_close_out();
+
+ audio_flags &= ~OUTISOPEN;
+ if(!audio_open_out(&audio_file_out, &tmpav.out)) {
+ error(Eio);
+ return -1;
+ }
+ audio_flags |= OUTISOPEN;
+ }
+ poperror();
+ qunlock(&flag_lock);
+ }
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/Nt/cmd.c
@@ -1,0 +1,243 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+extern int nth2fd(HANDLE);
+extern wchar_t *widen(char*);
+
+/*
+ * thanks to rcsh for these.
+ *
+ * windows quoting rules - I think
+ * Words are separated by space or tab
+ * Words containing a space or tab can be quoted using "
+ * 2N backslashes + " ==> N backslashes and end quote
+ * 2N+1 backslashes + " ==> N backslashes + literal "
+ * N backslashes not followed by " ==> N backslashes
+ */
+static char *
+dblquote(char *cmd, char *s)
+{
+ int nb;
+ char *p;
+
+ for(p=s; *p; p++)
+ if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"')
+ break;
+
+ if(*p == 0){ /* easy case */
+ strcpy(cmd, s);
+ return cmd+(p-s);
+ }
+
+ *cmd++ = '"';
+ for(;;) {
+ for(nb=0; *s=='\\'; nb++)
+ *cmd++ = *s++;
+
+ if(*s == 0) { /* trailing backslashes -> 2N */
+ while(nb-- > 0)
+ *cmd++ = '\\';
+ break;
+ }
+
+ if(*s == '"') { /* literal quote -> 2N+1 backslashes */
+ while(nb-- > 0)
+ *cmd++ = '\\';
+ *cmd++ = '\\'; /* escape the quote */
+ }
+ *cmd++ = *s++;
+ }
+
+ *cmd++ = '"';
+ *cmd = 0;
+
+ return cmd;
+}
+
+static char *
+ntquotedcmd(char **argv)
+{
+ int i, n;
+ char *cmd, *p;
+
+ /* conservatively calculate length of command;
+ * backslash expansion can cause growth in dblquote().
+ */
+ for(i=0,n=0; argv[i]; i++)
+ n += 2*strlen(argv[i]);
+ n++;
+
+ cmd = malloc(n);
+ if(cmd == nil)
+ return nil;
+ for(i=0,p=cmd; argv[i]; i++) {
+ p = dblquote(p, argv[i]);
+ *p++ = ' ';
+ }
+ if(p != cmd)
+ p--;
+ *p = 0;
+
+ return cmd;
+}
+
+static HANDLE
+exporthandle(HANDLE h, int close)
+{
+ HANDLE cp, dh;
+ DWORD flags = DUPLICATE_SAME_ACCESS;
+ if (close)
+ flags |= DUPLICATE_CLOSE_SOURCE;
+ cp = GetCurrentProcess();
+ if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags))
+ return nil;
+ return dh;
+}
+
+/* TO DO: check that oserrstr will have the right text on error */
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ STARTUPINFO si;
+ SECURITY_ATTRIBUTES sec;
+ HANDLE rh, wh, eh, srh, swh, seh;
+ PROCESS_INFORMATION pinfo;
+ char *cmd;
+ wchar_t *wcmd, *wdir;
+ int prio;
+
+ wdir = nil;
+ if(dir != nil)
+ wdir = widen(dir);
+
+ cmd = ntquotedcmd(args);
+ if(cmd == nil)
+ error(Enomem);
+
+ wcmd = widen(cmd);
+ sec.nLength = sizeof(sec);
+ sec.lpSecurityDescriptor = 0;
+ sec.bInheritHandle = 0;
+ rh = wh = eh = srh = swh = seh = nil;
+ if(!CreatePipe(&rh, &swh, &sec, 0))
+ goto Error;
+ if(!CreatePipe(&srh, &wh, &sec, 0))
+ goto Error;
+ if(!CreatePipe(&seh, &eh, &sec, 0))
+ goto Error;
+ rh = exporthandle(rh, 1);
+ if(rh == nil)
+ goto Error;
+ wh = exporthandle(wh, 1);
+ if(wh == nil)
+ goto Error;
+ eh = exporthandle(eh, 1);
+ if(eh == nil)
+ goto Error;
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+ si.wShowWindow = SW_SHOW;
+ si.hStdInput = rh;
+ si.hStdOutput = wh;
+ si.hStdError = eh;
+
+ prio = 0;
+ if(nice){
+ prio = IDLE_PRIORITY_CLASS;
+ if(nice > 1)
+ prio |= CREATE_SUSPENDED;
+ }
+
+ /* default of nil for wpath seems to be what we want; nil for env exports our current one */
+ if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1,
+ CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio,
+ 0 /*env*/, wdir, &si, &pinfo)){
+ //print("can't create process '%Q' %d\n", wcmd, GetLastError());
+ goto Error;
+ }
+
+ fd[0] = nth2fd(swh);
+ fd[1] = nth2fd(srh);
+ fd[2] = nth2fd(seh);
+ if(fd[1] == 1 || fd[2] == 2)
+ panic("invalid mapping of handle to fd");
+ CloseHandle(si.hStdInput);
+ CloseHandle(si.hStdOutput);
+ CloseHandle(si.hStdError);
+
+ if(prio & CREATE_SUSPENDED){
+ if(nice > 1)
+ SetThreadPriority(pinfo.hThread,
+ nice>3? THREAD_PRIORITY_IDLE:
+ nice>2? THREAD_PRIORITY_LOWEST:
+ THREAD_PRIORITY_BELOW_NORMAL);
+ ResumeThread(pinfo.hThread);
+ }
+ CloseHandle(pinfo.hThread);
+ /* don't close process handle */
+ free(cmd);
+ free(wcmd);
+ free(wdir);
+ return pinfo.hProcess;
+
+Error:
+ if(rh)
+ CloseHandle(rh);
+ if(wh)
+ CloseHandle(wh);
+ if(eh)
+ CloseHandle(eh);
+ if(srh)
+ CloseHandle(srh);
+ if(swh)
+ CloseHandle(swh);
+ if(seh)
+ CloseHandle(seh);
+ free(cmd);
+ free(wcmd);
+ free(wdir);
+ return nil;
+}
+
+int
+oscmdwait(void *v, char *buf, int n)
+{
+ int status;
+ HANDLE proc = (HANDLE)v;
+
+ /* need not worry about being interrupted */
+ if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED)
+ return -1;
+ if(!GetExitCodeProcess(proc, &status))
+ status = 1;
+ if(status)
+ n = snprint(buf, n, "0 0 0 0 'status %d'", status);
+ else
+ n = snprint(buf, n, "0 0 0 0 ''");
+ return n;
+
+}
+
+int
+oscmdkill(void *v)
+{
+ if(TerminateProcess((HANDLE)v, 666) == FALSE)
+ return -1;
+ return 0;
+}
+
+void
+oscmdfree(void *v)
+{
+ CloseHandle((HANDLE)v);
+}
--- /dev/null
+++ b/emu/Nt/devarch.c
@@ -1,0 +1,442 @@
+/*
+ * platform-specific interface
+ */
+
+#define Unknown win_Unknown
+#define UNICODE
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "r16.h"
+
+enum{
+ Qdir,
+ Qarchctl,
+ Qcputype,
+ Qregquery,
+ Qhostmem
+};
+
+static
+Dirtab archtab[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "archctl", {Qarchctl, 0}, 0, 0444,
+ "cputype", {Qcputype}, 0, 0444,
+ "regquery", {Qregquery}, 0, 0666,
+ "hostmem", {Qhostmem}, 0, 0444,
+};
+
+typedef struct Value Value;
+struct Value {
+ int type;
+ int size;
+ union {
+ ulong w;
+ vlong q;
+ char data[1]; /* utf-8 */
+ };
+};
+
+typedef struct Regroot Regroot;
+struct Regroot {
+ char* name;
+ HKEY root;
+};
+
+static Regroot roots[] = {
+ {"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT},
+ {"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
+ {"HKEY_CURRENT_USER", HKEY_CURRENT_USER},
+ {"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
+ {"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
+ {"HKEY_USERS", HKEY_USERS},
+};
+
+static struct {
+ ulong mhz;
+ int ncpu;
+ char cpu[64];
+} arch;
+
+static QLock reglock;
+
+static Value* getregistry(HKEY, Rune16*, Rune16*);
+static int nprocs(void);
+
+static void
+archinit(void)
+{
+ Value *v;
+ char *p;
+
+ v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"ProcessorNameString");
+ if(v != nil){
+ snprint(arch.cpu, sizeof(arch.cpu), "%s", v->data);
+ if((p = strrchr(arch.cpu, ' ')) != nil)
+ for(; p >= arch.cpu && *p == ' '; p--)
+ *p = '\0';
+ free(v);
+ }else{
+ v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"VendorIdentifier");
+ if(v != nil){
+ snprint(arch.cpu, sizeof(arch.cpu), "%s", v->data);
+ free(v);
+ }else
+ snprint(arch.cpu, sizeof(arch.cpu), "unknown");
+ }
+ v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"~MHz");
+ if(v != nil){
+ arch.mhz = v->w;
+ free(v);
+ }
+ arch.ncpu = nprocs();
+}
+
+static int
+nprocs(void)
+{
+ int n;
+ char *p;
+ Rune16 *r;
+ Value *v;
+ n = 0;
+ for(;;){
+ p = smprint("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", n);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ r = widen(p);
+ free(p);
+ v = getregistry(HKEY_LOCAL_MACHINE, r, L"~MHz");
+ free(r);
+ if(v == nil)
+ break;
+ free(v);
+ n++;
+ }
+ return n;
+}
+
+static Chan*
+archattach(char* spec)
+{
+ return devattach('a', spec);
+}
+
+static Walkqid*
+archwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, archtab, nelem(archtab), devgen);
+}
+
+static int
+archstat(Chan* c, uchar *db, int n)
+{
+ return devstat(c, db, n, archtab, nelem(archtab), devgen);
+}
+
+static Chan*
+archopen(Chan* c, int omode)
+{
+ return devopen(c, omode, archtab, nelem(archtab), devgen);
+}
+
+static void
+archclose(Chan* c)
+{
+ if((ulong)c->qid.path == Qregquery && c->aux != nil)
+ free(c->aux);
+}
+
+static long
+archread(Chan* c, void* a, long n, vlong offset)
+{
+ char *p;
+ Value *v;
+ int i, l;
+ MEMORYSTATUS mem;
+
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, archtab, nelem(archtab), devgen);
+ case Qarchctl:
+ case Qcputype:
+ l = 0;
+ if((ulong)c->qid.path == Qcputype)
+ l = 4;
+ p = smalloc(READSTR);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ snprint(p, READSTR, "cpu %q %lud %d\n", arch.cpu, arch.mhz, arch.ncpu);
+ n = readstr(offset, a, n, p+l);
+ poperror();
+ free(p);
+ break;
+ case Qregquery:
+ v = c->aux;
+ if(v == nil)
+ return 0;
+ p = smalloc(READSTR);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ switch(v->type){
+ case REG_NONE:
+ n = readstr(offset, a, n, "nil");
+ break;
+ case REG_DWORD:
+ snprint(p, READSTR, "int %ld", v->w);
+ n = readstr(offset, a, n, p);
+ break;
+#ifdef REG_QWORD
+ case REG_QWORD:
+ snprint(p, READSTR, "int %lld", v->q);
+ n = readstr(offset, a, n, p);
+ break;
+#endif
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if(v->data[0])
+ snprint(p, READSTR, "str %q", v->data);
+ n = readstr(offset, a, n, p);
+ break;
+ case REG_MULTI_SZ:
+ l = snprint(p, READSTR, "str");
+ for(i=0;;){
+ l += snprint(p+l, READSTR-l, " %q", v->data+i);
+ while(v->data[i++] != 0){
+ /* skip */
+ }
+ if(v->data[i] == 0)
+ break; /* final terminator */
+ }
+ n = readstr(offset, a, n, p);
+ break;
+ case REG_BINARY:
+ l = n;
+ n = readstr(offset, a, l, "bin");
+ if(n >= 3){
+ offset -= 3;
+ if(offset+l > v->size)
+ l = v->size - offset;
+ memmove((char*)a+n, v->data+offset, l);
+ n += l;
+ }
+ break;
+ default:
+ error("unknown registry type");
+ n=0;
+ break;
+ }
+ poperror();
+ free(p);
+ c->aux = nil;
+ free(v);
+ break;
+ case Qhostmem:
+ mem.dwLength = sizeof(mem);
+ GlobalMemoryStatus(&mem); /* GlobalMemoryStatusEx isn't on NT */
+ p = smalloc(READSTR);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ snprint(p, READSTR, "load %ld\nphys %lud %lud\nvirt %lud %lud\nswap %lud %lud\n",
+ mem.dwMemoryLoad,
+ mem.dwAvailPhys, mem.dwTotalPhys, mem.dwAvailVirtual, mem.dwTotalVirtual,
+ mem.dwAvailPageFile, mem.dwTotalPageFile);
+ n = readstr(offset, a, n, p);
+ poperror();
+ free(p);
+ break;
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+archwrite(Chan* c, void* a, long n, vlong offset)
+{
+ Value *v;
+ int i;
+ Cmdbuf *cb;
+ Rune16 *key, *item;
+
+ if((ulong)c->qid.path != Qregquery)
+ error(Eperm);
+ USED(offset);
+ if(c->aux != nil){
+ free(c->aux);
+ c->aux = nil;
+ }
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ if(cb->nf < 3)
+ error(Ebadctl);
+ for(i=0; i<nelem(roots); i++)
+ if(strcmp(cb->f[0], roots[i].name) == 0)
+ break;
+ if(i >= nelem(roots))
+ errorf("unknown root: %s", cb->f[0]);
+ key = widen(cb->f[1]);
+ if(waserror()){
+ free(key);
+ nexterror();
+ }
+ item = widen(cb->f[2]);
+ if(waserror()){
+ free(item);
+ nexterror();
+ }
+ v = getregistry(roots[i].root, key, item);
+ if(v == nil)
+ error(up->env->errstr);
+ c->aux = v;
+ poperror();
+ free(item);
+ poperror();
+ free(key);
+ poperror();
+ free(cb);
+ return n;
+}
+
+Dev archdevtab = {
+ 'a',
+ "arch",
+
+ archinit,
+ archattach,
+ archwalk,
+ archstat,
+ archopen,
+ devcreate,
+ archclose,
+ archread,
+ devbread,
+ archwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+static void
+regerr(int rc)
+{
+ Rune16 err[64];
+ char emsg[sizeof(err)*UTFmax+1];
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ err, sizeof(err), 0);
+ runes16toutf(emsg, err, nelem(err));
+ error(emsg);
+}
+
+static Value*
+getregistry(HKEY root, Rune16 *keyname, Rune16 *name)
+{
+ long res;
+ HKEY key;
+ DWORD dtype, n;
+ int i, l, nb;
+ void* vp;
+ char *p;
+ Value *val;
+ Rune16 *rb;
+
+ qlock(®lock);
+ if(waserror()){
+ qunlock(®lock);
+ return nil;
+ }
+ res = RegOpenKey(root, keyname, &key);
+ if(res != ERROR_SUCCESS)
+ regerr(res);
+ if(waserror()){
+ RegCloseKey(key);
+ nexterror();
+ }
+ n = 0;
+ res = RegQueryValueEx(key, name, NULL, &dtype, NULL, &n);
+ if(res != ERROR_SUCCESS)
+ regerr(res);
+ nb = n;
+ if(dtype == REG_SZ || dtype == REG_EXPAND_SZ || dtype == REG_MULTI_SZ){
+ nb = n*UTFmax + 1;
+ rb = smalloc((n+2)*sizeof(Rune16));
+ memset(rb, 0, (n+2)*sizeof(Rune16));
+ }else
+ rb = nil;
+ if(waserror()){
+ free(rb);
+ nexterror();
+ }
+ val = smalloc(sizeof(Value)+nb);
+ if(waserror()){
+ free(val);
+ nexterror();
+ }
+ val->type = dtype;
+ val->size = n;
+ switch(dtype){
+ case REG_DWORD:
+ vp = &val->w;
+ break;
+#ifdef REG_QWORD
+ case REG_QWORD:
+ vp = &val->q;
+ break;
+#endif
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ case REG_MULTI_SZ:
+ vp = rb;
+ break;
+ case REG_BINARY:
+ case REG_NONE:
+ vp = val->data;
+ break;
+ default:
+ errorf("unsupported registry type: %d", dtype);
+ return nil; /* for compiler */
+ }
+ res = RegQueryValueEx(key, name, NULL, NULL, vp, &n);
+ if(res != ERROR_SUCCESS)
+ regerr(res);
+ poperror();
+ if(rb != nil){
+ if(dtype == REG_MULTI_SZ){
+ p = val->data;
+ for(i=0;;){
+ l = runes16len(rb+i);
+ runes16toutf(p, rb+i, l);
+ i += l+1;
+ if(rb[i] == 0)
+ break;
+ p += strlen(p)+1;
+ }
+ }else
+ runes16toutf(val->data, rb, n);
+ free(rb);
+ }
+ poperror();
+ poperror();
+ RegCloseKey(key);
+ poperror();
+ qunlock(®lock);
+ return val;
+}
--- /dev/null
+++ b/emu/Nt/deveia.c
@@ -1,0 +1,672 @@
+/*
+ * Windows serial driver
+ *
+ * to do:
+ * scan the registry for serial ports?
+ */
+
+#define Unknown win_Unknown
+#include <windows.h>
+#undef Unknown
+#undef Sleep
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <lm.h>
+#include <direct.h>
+
+// local fcts
+static void openport(int);
+static void wrctl(int, char*);
+static long rdstat(int, void*, long, ulong );
+
+enum
+{
+ Devchar = 't',
+
+ Ndataqid = 1,
+ Nctlqid,
+ Nstatqid,
+ Nqid = 3, /* number of QIDs */
+
+ Maxctl = 128,
+
+ // in/out buffer sizes for comm port (NT requires an even number)
+ // set it to x* the max styx message rounded up to the
+ // nearest 4 byte value
+ CommBufSize = ((((8192+128)*2)+3) & ~3)
+};
+
+/*
+ * Macros to manage QIDs
+ */
+#define NETTYPE(x) ((x)&0x0F)
+#define NETID(x) ((x)>>4)
+#define NETQID(i,t) (((i)<<4)|(t))
+
+static Dirtab *eiadir;
+static int ndir;
+
+typedef struct Eia Eia;
+struct Eia {
+ Ref r;
+ HANDLE comfh; //handle to open port
+ int restore; //flag to restore prev. states
+ DCB dcb; //win32 device control block used for restore
+ int id; //index to host port name in sysdev
+};
+
+// the same timeouts are used for all ports
+// currently there is no Inferno interface to
+// change the timeouts.
+static COMMTIMEOUTS timeouts;
+
+// std win32 serial port names are COM1..COM4
+// however there can be more and they can be
+// named anything. we should be more flexible
+// pehaps adding a ctl command to allow you to
+// access any win32 comm port
+static char* sysdev[] = {
+ "COM1:",
+ "COM2:",
+ "COM3:",
+ "COM4:",
+ "COM5:",
+ "COM6:",
+ "COM7:",
+ "COM8:",
+ NULL
+};
+
+static Eia *eia;
+
+typedef struct OptTable OptTable;
+struct OptTable {
+ char *str;
+ DWORD flag;
+};
+
+#define BAD ((DWORD)-1)
+
+// valid bit-per-byte sizes
+static OptTable size[] = {
+ {"5", 5},
+ {"6", 6},
+ {"7", 7},
+ {"8", 8},
+ {NULL, BAD}
+};
+
+// valid stop bits
+static OptTable stopbits[] = {
+ {"1", ONESTOPBIT},
+ {"1.5", ONE5STOPBITS},
+ {"2", TWOSTOPBITS},
+ {NULL, BAD}
+};
+
+// valid parity settings
+static OptTable parity[] = {
+ {"o", ODDPARITY},
+ {"e", EVENPARITY},
+ {"s", SPACEPARITY},
+ {"m", MARKPARITY},
+ {"n", NOPARITY},
+ {NULL, NOPARITY}
+};
+
+
+static char *
+ftos(OptTable *tbl, DWORD flag)
+{
+ while(tbl->str && tbl->flag != flag)
+ tbl++;
+ if(tbl->str == 0)
+ return "unknown";
+ return tbl->str;
+}
+
+static DWORD
+stof(OptTable *tbl, char *str)
+{
+ while(tbl->str && strcmp(tbl->str, str) != 0)
+ tbl++;
+ return tbl->flag;
+}
+
+static void
+eiainit(void)
+{
+ int i,x;
+ byte ports; //bitmask of active host ports
+ int nports; //number of active host ports
+ int max; //number of highest port
+ Dirtab *dp;
+
+ // setup the timeouts; choose carefully
+ timeouts.ReadIntervalTimeout = 2;
+ timeouts.ReadTotalTimeoutMultiplier = 0;
+ timeouts.ReadTotalTimeoutConstant = 200;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 400;
+
+ // check to see which ports exist by trying to open them
+ // keep results in a bitmask
+ ports = nports = max = 0;
+ for(i=0; (sysdev[i] != NULL) && (i<8); i++) {
+ HANDLE comfh = CreateFile(sysdev[i], 0, 0, NULL, /* no security attrs */
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if(comfh != INVALID_HANDLE_VALUE) {
+ ports |= 1<<i;
+ CloseHandle(comfh);
+ nports++;
+ max = i;
+ }
+ }
+
+ if(nports == 0)
+ return; //no ports
+
+ // allocate directory table and eia structure
+ // for each active port.
+ ndir = Nqid*nports+1;
+ dp = eiadir = malloc(ndir*sizeof(Dirtab));
+ if(dp == 0)
+ panic("eiainit");
+ eia = malloc(nports*sizeof(Eia));
+ if(eia == 0) {
+ free(dp);
+ panic("eiainit");
+ }
+
+ // fill in the directory table and initialize
+ // the eia structure. skip inactive ports.
+ sprint(dp->name, ".");
+ dp->qid.path = 0;
+ dp->qid.type = QTDIR;
+ dp->perm = DMDIR|0555;
+ dp++;
+ x = 0; // index in eia[]
+ for(i = 0; i <= max; i++) {
+ if( (ports & (1<<i)) == 0)
+ continue; //port 'i' is not active
+ sprint(dp->name, "eia%d", i);
+ dp->qid.path = NETQID(x, Ndataqid);
+ dp->perm = 0660;
+ dp++;
+ sprint(dp->name, "eia%dctl", i);
+ dp->qid.path = NETQID(x, Nctlqid);
+ dp->perm = 0660;
+ dp++;
+ sprint(dp->name, "eia%dstatus", i);
+ dp->qid.path = NETQID(x, Nstatqid);
+ dp->perm = 0660;
+ dp++;
+ // init the eia structure
+ eia[x].restore = 0;
+ eia[x].id = i;
+ x++;
+ }
+}
+
+static Chan*
+eiaattach(char *spec)
+{
+ if(eiadir == nil)
+ error(Enodev);
+
+ return devattach(Devchar, spec);
+}
+
+static Walkqid*
+eiawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, eiadir, ndir, devgen);
+}
+
+static int
+eiastat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, eiadir, ndir, devgen);
+}
+
+static Chan*
+eiaopen(Chan *c, int mode)
+{
+ int port = NETID(c->qid.path);
+
+ c = devopen(c, mode, eiadir, ndir, devgen);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Nctlqid:
+ case Ndataqid:
+ case Nstatqid:
+ if(incref(&eia[port].r) != 1)
+ break;
+ if(waserror()) {
+ decref(&eia[port].r);
+ nexterror();
+ }
+ openport(port);
+ poperror();
+ break;
+ }
+ return c;
+}
+
+static void
+eiaclose(Chan *c)
+{
+ int port = NETID(c->qid.path);
+
+ if((c->flag & COPEN) == 0)
+ return;
+
+ switch(NETTYPE(c->qid.path)) {
+ case Nctlqid:
+ case Ndataqid:
+ case Nstatqid:
+ if(decref(&eia[port].r) == 0) {
+ osenter();
+ CloseHandle(eia[port].comfh);
+ osleave();
+ }
+ break;
+ }
+
+}
+
+static long
+eiaread(Chan *c, void *buf, long n, vlong offset)
+{
+ DWORD cnt;
+ int port = NETID(c->qid.path);
+ BOOL good;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, eiadir, ndir, devgen);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Ndataqid:
+ cnt = 0;
+ // if ReadFile timeouts and cnt==0 then just re-read
+ // this will give osleave() a chance to detect an
+ // interruption (i.e. killprog)
+ while(cnt==0) {
+ osenter();
+ good = ReadFile(eia[port].comfh, buf, n, &cnt, NULL);
+ SleepEx(0,FALSE); //allow another thread access to port
+ osleave();
+ if(!good)
+ oserror();
+ }
+ return cnt;
+ case Nctlqid:
+ return readnum(offset, buf, n, eia[port].id, NUMSIZE);
+ case Nstatqid:
+ return rdstat(port, buf, n, offset);
+ }
+
+ return 0;
+}
+
+static long
+eiawrite(Chan *c, void *buf, long n, vlong offset)
+{
+ DWORD cnt;
+ char cmd[Maxctl];
+ int port = NETID(c->qid.path);
+ BOOL good;
+ uchar *data;
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Ndataqid:
+ cnt = 0;
+ data = (uchar*)buf;
+ // if WriteFile times out (i.e. return true; cnt<n) then
+ // allow osleave() to check for an interrupt otherwise try
+ // to send the unsent data.
+ while(n>0) {
+ osenter();
+ good = WriteFile(eia[port].comfh, data, n, &cnt, NULL);
+ osleave();
+ if(!good)
+ oserror();
+ data += cnt;
+ n -= cnt;
+ }
+ return (data-(uchar*)buf);
+ case Nctlqid:
+ if(n >= sizeof(cmd))
+ n = sizeof(cmd)-1;
+ memmove(cmd, buf, n);
+ cmd[n] = 0;
+ wrctl(port, cmd);
+ return n;
+ }
+ return 0;
+}
+
+static int
+eiawstat(Chan *c, uchar *dp, int n)
+{
+ Dir d;
+ int i;
+
+ if(!iseve())
+ error(Eperm);
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+ if(NETTYPE(c->qid.path) == Nstatqid)
+ error(Eperm);
+
+ n = convM2D(dp, n, &d, nil);
+ i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid;
+ if(d.mode != ~0UL)
+ eiadir[i+1].perm = d.mode&0666;
+ return n;
+}
+
+Dev eiadevtab = {
+ Devchar,
+ "eia",
+
+ eiainit,
+ eiaattach,
+ eiawalk,
+ eiastat,
+ eiaopen,
+ devcreate,
+ eiaclose,
+ eiaread,
+ devbread,
+ eiawrite,
+ devbwrite,
+ devremove,
+ eiawstat
+};
+
+
+//
+// local functions
+//
+
+/*
+ * open the indicated comm port and then set
+ * the default settings for the port.
+ */
+static void
+openport(int port)
+{
+ Eia* p = &eia[port];
+
+ // open the port
+ p->comfh = CreateFile(sysdev[p->id],
+ GENERIC_READ|GENERIC_WRITE, //open underlying port for rd/wr
+ 0, //comm port can't be shared
+ NULL, //no security attrs
+ OPEN_EXISTING, //a must for comm port
+ FILE_ATTRIBUTE_NORMAL, //nonoverlapped io
+ NULL); //another must for comm port
+
+ if(p->comfh == INVALID_HANDLE_VALUE)
+ oserror();
+ if(waserror()){
+ CloseHandle(p->comfh);
+ p->comfh = INVALID_HANDLE_VALUE;
+ nexterror();
+ }
+
+ // setup in/out buffers (NT requires an even number)
+ if(!SetupComm(p->comfh, CommBufSize, CommBufSize))
+ oserror();
+
+ // either use existing settings or set defaults
+ if(!p->restore) {
+ // set default settings
+ if(!GetCommState(p->comfh, &p->dcb))
+ oserror();
+ p->dcb.BaudRate = 9600;
+ p->dcb.ByteSize = 8;
+ p->dcb.fParity = 0;
+ p->dcb.Parity = NOPARITY;
+ p->dcb.StopBits = ONESTOPBIT;
+ p->dcb.fInX = 0; //default to xoff
+ p->dcb.fOutX = 0;
+ p->dcb.fAbortOnError = 1; //read/write abort on err
+ }
+
+ // set state and timeouts
+ if(!SetCommState(p->comfh, &p->dcb) ||
+ !SetCommTimeouts(p->comfh, &timeouts))
+ oserror();
+ poperror();
+}
+
+/*
+ * Obtain status information on the com port.
+ */
+static long
+rdstat(int port, void *buf, long n, ulong offset)
+{
+ HANDLE comfh = eia[port].comfh;
+ char str[Maxctl];
+ char *s;
+ DCB dcb;
+ DWORD modemstatus;
+ DWORD porterr;
+ COMSTAT portstat;
+ int frame, overrun, i;
+
+ // valid line control ids
+ static enum {
+ L_CTS, L_DSR, L_RING, L_DCD, L_DTR, L_RTS, L_MAX
+ };
+ int status[L_MAX];
+
+ // line control strings (should match above id's)
+ static char* lines[] = {
+ "cts", "dsr", "ring", "dcd", "dtr", "rts", NULL
+ };
+
+
+ // get any error conditions; also clears error flag
+ // and enables io
+ if(!ClearCommError(comfh, &porterr, &portstat))
+ oserror();
+
+ // get comm port state
+ if(!GetCommState(comfh, &dcb))
+ oserror();
+
+ // get modem line information
+ if(!GetCommModemStatus(comfh, &modemstatus))
+ oserror();
+
+ // now set our local flags
+ status[L_CTS] = MS_CTS_ON & modemstatus;
+ status[L_DSR] = MS_DSR_ON & modemstatus;
+ status[L_RING] = MS_RING_ON & modemstatus;
+ status[L_DCD] = MS_RLSD_ON & modemstatus;
+ status[L_DTR] = FALSE; //?? cand this work: dcb.fDtrControl;
+ status[L_RTS] = FALSE; //?? dcb.fRtsControl;
+ frame = porterr & CE_FRAME;
+ overrun = porterr & CE_OVERRUN;
+
+ /* TO DO: mimic native eia driver's first line */
+
+ s = seprint(str, str+sizeof(str), "opens %d ferr %d oerr %d baud %d",
+ eia[port].r.ref-1,
+ frame,
+ overrun,
+ dcb.BaudRate);
+
+ // add line settings
+ for(i=0; i < L_MAX; i++)
+ if(status[i])
+ s = seprint(s, str+sizeof(str), " %s", lines[i]);
+ seprint(s, str+sizeof(str), "\n");
+ return readstr(offset, buf, n, str);
+}
+
+//
+// write on ctl file. modify the settings for
+// the underlying port.
+//
+static void
+wrctl(int port, char *cmd)
+{
+ DCB dcb;
+ int nf, n, i;
+ char *f[16];
+ HANDLE comfh = eia[port].comfh;
+ DWORD flag, opt;
+ BOOL rslt;
+ int chg;
+
+ // get the current settings for the port
+ if(!GetCommState(comfh, &dcb))
+ oserror();
+
+ chg = 0;
+ nf = tokenize(cmd, f, nelem(f));
+ for(i = 0; i < nf; i++){
+ if(strcmp(f[i], "break") == 0){
+ if(!SetCommBreak(comfh))
+ oserror();
+ SleepEx((DWORD)300, FALSE);
+ if(!ClearCommBreak(comfh))
+ oserror();
+ continue;
+ }
+
+ n = atoi(f[i]+1);
+ switch(*f[i]) {
+ case 'B':
+ case 'b': // set the baud rate
+ if(n < 110)
+ error(Ebadarg);
+ dcb.BaudRate = n;
+ chg = 1;
+ break;
+ case 'C':
+ case 'c':
+ /* dcd */
+ break;
+ case 'D':
+ case 'd': // set DTR
+ opt = n ? SETDTR : CLRDTR;
+ if(!EscapeCommFunction(comfh, opt))
+ oserror();
+ break;
+ case 'E':
+ case 'e':
+ /* dsr */
+ break;
+ case 'F':
+ case 'f': // flush any untransmitted data
+ if(!PurgeComm(comfh, PURGE_TXCLEAR))
+ oserror();
+ break;
+ case 'H':
+ case 'h':
+ /* hangup */
+ /* TO DO: close handle */
+ break;
+ case 'I':
+ case 'i':
+ /* fifo: nothing to do */
+ break;
+ case 'K':
+ case 'k':
+ /* send a break */
+ if(!SetCommBreak(comfh))
+ oserror();
+ SleepEx((DWORD)300, FALSE);
+ if(!ClearCommBreak(comfh))
+ oserror();
+ break;
+ case 'L':
+ case 'l': // set bits per byte
+ flag = stof(size, f[0]+1);
+ if(flag == BAD)
+ error(Ebadarg);
+ dcb.ByteSize = (BYTE)flag;
+ chg = 1;
+ break;
+ case 'M':
+ case 'm': // set CTS (modem control)
+ dcb.fOutxCtsFlow = (n!=0);
+ chg = 1;
+ break;
+ case 'N':
+ case 'n':
+ /* don't block on output */
+ break;
+ case 'P':
+ case 'p': // set parity -- even or odd
+ flag = stof(parity, f[0]+1);
+ if(flag==BAD)
+ error(Ebadarg);
+ dcb.Parity = (BYTE)flag;
+ chg = 1;
+ break;
+ case 'Q':
+ case 'q':
+ /* set i/o queue limits */
+ break;
+ case 'R':
+ case 'r': // set RTS
+ opt = n ? SETRTS : CLRRTS;
+ if(!EscapeCommFunction(comfh, opt))
+ oserror();
+ break;
+ case 'S':
+ case 's': // set stop bits -- valid: 1 or 2 (win32 allows 1.5??)
+ flag = stof(stopbits, f[0]+1);
+ if(flag==BAD)
+ error(Ebadarg);
+ dcb.StopBits = flag;
+ chg = 1;
+ break;
+ case 'T':
+ case 't':
+ break;
+ case 'W':
+ case 'w':
+ /* set uart timer */
+ break;
+ case 'X':
+ case 'x': // xon/xoff
+ opt = n ? SETXON : SETXOFF;
+ if(!EscapeCommFunction(comfh, opt))
+ oserror();
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ }
+
+ if(!chg)
+ return;
+ // make the changes on the underlying port, but flush
+ // outgoing chars down the port before
+ osenter();
+ rslt = FlushFileBuffers(comfh);
+ if(rslt)
+ rslt = SetCommState(comfh, &dcb);
+ osleave();
+ if(!rslt)
+ oserror();
+ eia[port].restore = 1;
+ eia[port].dcb = dcb;
+}
--- /dev/null
+++ b/emu/Nt/devfs.c
@@ -1,0 +1,2375 @@
+#define UNICODE
+#define Unknown win_Unknown
+#include <windows.h>
+#include <winbase.h>
+#undef Unknown
+#undef Sleep
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "r16.h"
+#include <lm.h>
+
+/* TODO: try using / in place of \ in path names */
+
+#ifndef SID_MAX_SUB_AUTHORITIES
+#define SID_MAX_SUB_AUTHORITIES 15
+#endif
+
+enum
+{
+ MAX_SID = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD),
+ ACL_ROCK = sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID),
+ SD_ROCK = SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK,
+ MAXCOMP = 128,
+};
+
+typedef struct User User;
+typedef struct Gmem Gmem;
+typedef struct Stat Stat;
+typedef struct Fsinfo Fsinfo;
+typedef WIN32_FIND_DATA Fsdir;
+
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+struct Fsinfo
+{
+ int uid;
+ int gid;
+ int mode;
+ int fd;
+ vlong offset;
+ QLock oq;
+ char* spec;
+ Rune16* srv;
+ Cname* name; /* Windows' idea of the file name */
+ ushort usesec;
+ ushort checksec;
+ Fsdir* de; /* non-nil for saved entry from last dirread at offset */
+};
+#define FS(c) ((Fsinfo*)(c)->aux)
+
+/*
+ * info about a user or group
+ * there are two ways to specify a user:
+ * by sid, a unique identifier
+ * by user and domain names
+ * this structure is used to convert between the two,
+ * as well as figure out which groups a users belongs to.
+ * the user information never gets thrown away,
+ * but the group information gets refreshed with each setid.
+ */
+struct User
+{
+ QLock lk; /* locks the gotgroup and group fields */
+ SID *sid;
+ Rune16 *name;
+ Rune16 *dom;
+ int type; /* the type of sid, ie SidTypeUser, SidTypeAlias, ... */
+ int gotgroup; /* tried to add group */
+ Gmem *group; /* global and local groups to which this user or group belongs. */
+ User *next;
+};
+
+struct Gmem
+{
+ User *user;
+ Gmem *next;
+};
+
+/*
+ * intermediate stat information
+ */
+struct Stat
+{
+ User *owner;
+ User *group;
+ ulong mode;
+};
+
+/*
+ * some "well-known" sids
+ */
+static SID *creatorowner;
+static SID *creatorgroup;
+static SID *everyone;
+static SID *ntignore;
+static SID *ntroot; /* user who is supposed to run emu as a server */
+
+/*
+ * all users we ever see end up in this table
+ * users are never deleted, but we should update
+ * group information for users sometime
+ */
+static struct
+{
+ QLock lk;
+ User *u;
+}users;
+
+/*
+ * conversion from inferno permission modes to nt access masks
+ * is this good enough? this is what nt sets, except for NOMODE
+ */
+#define NOMODE (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define RMODE (READ_CONTROL|SYNCHRONIZE\
+ |FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define XMODE (READ_CONTROL|SYNCHRONIZE\
+ |FILE_EXECUTE|FILE_READ_ATTRIBUTES)
+#define WMODE (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\
+ |FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\
+ |FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES)
+
+static int
+modetomask[] =
+{
+ NOMODE,
+ XMODE,
+ WMODE,
+ WMODE|XMODE,
+ RMODE,
+ RMODE|XMODE,
+ RMODE|WMODE,
+ RMODE|WMODE|XMODE,
+};
+
+extern DWORD PlatformId;
+ char rootdir[MAXROOT] = "\\inferno";
+ Rune16 rootname[] = L"inferno-server";
+static Qid rootqid;
+static User *fsnone;
+static User *fsuser;
+static Rune16 *ntsrv;
+static int usesec;
+static int checksec;
+static int isserver;
+static int file_share_delete;
+static uchar isntfrog[256];
+
+static void fsremove(Chan*);
+
+ wchar_t *widen(char *s);
+ char *narrowen(wchar_t *ws);
+ int widebytes(wchar_t *ws);
+
+static char Etoolong[] = "file name too long";
+
+extern int nth2fd(HANDLE);
+extern HANDLE ntfd2h(int);
+static int cnisroot(Cname*);
+static int fsisroot(Chan*);
+static int okelem(char*, int);
+static int fsexist(char*, Qid*);
+static char* fspath(Cname*, char*, char*, char*);
+static Cname* fswalkpath(Cname*, char*, int);
+static char* fslastelem(Cname*);
+static long fsdirread(Chan*, uchar*, int, vlong);
+static ulong fsqidpath(char*);
+static int fsomode(int);
+static int fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir);
+static int fsdirsize(WIN32_FIND_DATA*, char*, Chan*);
+static void fssettime(char*, long, long);
+static long unixtime(FILETIME);
+static FILETIME wintime(ulong);
+static void secinit(void);
+static int secstat(Dir*, char*, Rune16*);
+static int secsize(char*, Rune16*);
+static void seccheck(char*, ulong, Rune16*);
+static int sechasperm(char*, ulong, Rune16*);
+static SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]);
+static int secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune16*);
+static int secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune16*);
+static SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int);
+static SID *dupsid(SID*);
+static int ismembersid(Rune16*, User*, SID*);
+static int ismember(User*, User*);
+static User *sidtouser(Rune16*, SID*);
+static User *domnametouser(Rune16*, Rune16*, Rune16*);
+static User *nametouser(Rune16*, Rune16*);
+static User *unametouser(Rune16*, char*);
+static void addgroups(User*, int);
+static User *mkuser(SID*, int, Rune16*, Rune16*);
+static Rune16 *domsrv(Rune16 *, Rune16[MAX_PATH]);
+static Rune16 *filesrv(char*);
+static int fsacls(char*);
+static User *secuser(void);
+
+
+int
+winfilematch(char *path, WIN32_FIND_DATA *data)
+{
+ char *p;
+ wchar_t *wpath;
+ int r;
+
+ p = path+strlen(path);
+ while(p > path && p[-1] != '\\')
+ --p;
+ wpath = widen(p);
+ r = (data->cFileName[0] == '.' && runes16len(data->cFileName) == 1)
+ || runes16cmp(data->cFileName, wpath) == 0;
+ free(wpath);
+ return r;
+}
+
+int
+winfileclash(char *path)
+{
+ HANDLE h;
+ WIN32_FIND_DATA data;
+ wchar_t *wpath;
+
+ wpath = widen(path);
+ h = FindFirstFile(wpath, &data);
+ free(wpath);
+ if (h != INVALID_HANDLE_VALUE) {
+ FindClose(h);
+ return !winfilematch(path, &data);
+ }
+ return 0;
+}
+
+
+/*
+ * this gets called to set up the environment when we switch users
+ */
+void
+setid(char *name, int owner)
+{
+ User *u;
+
+ if(owner && !iseve())
+ return;
+
+ kstrdup(&up->env->user, name);
+
+ if(!usesec)
+ return;
+
+ u = unametouser(ntsrv, up->env->user);
+ if(u == nil)
+ u = fsnone;
+ else {
+ qlock(&u->lk);
+ addgroups(u, 1);
+ qunlock(&u->lk);
+ }
+ if(u == nil)
+ panic("setid: user nil\n");
+
+ up->env->ui = u;
+}
+
+static void
+fsfree(Chan *c)
+{
+ cnameclose(FS(c)->name);
+ if(FS(c)->de != nil)
+ free(FS(c)->de);
+ free(FS(c));
+}
+
+void
+fsinit(void)
+{
+ int n, isvol;
+ ulong attr;
+ char *p, tmp[MAXROOT];
+ wchar_t *wp, *wpath, *last;
+ wchar_t wrootdir[MAXROOT];
+
+ isntfrog['/'] = 1;
+ isntfrog['\\'] = 1;
+ isntfrog[':'] = 1;
+ isntfrog['*'] = 1;
+ isntfrog['?'] = 1;
+ isntfrog['"'] = 1;
+ isntfrog['<'] = 1;
+ isntfrog['>'] = 1;
+
+ /*
+ * vet the root
+ */
+ strcpy(tmp, rootdir);
+ for(p = tmp; *p; p++)
+ if(*p == '/')
+ *p = '\\';
+ if(tmp[0] != 0 && tmp[1] == ':') {
+ if(tmp[2] == 0) {
+ tmp[2] = '\\';
+ tmp[3] = 0;
+ }
+ else if(tmp[2] != '\\') {
+ /* don't allow c:foo - only c:\foo */
+ panic("illegal root pathX");
+ }
+ }
+ wrootdir[0] = '\0';
+ wpath = widen(tmp);
+ for(wp = wpath; *wp; wp++) {
+ if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':'))
+ panic("illegal root path");
+ }
+ n = GetFullPathName(wpath, MAXROOT, wrootdir, &last);
+ free(wpath);
+ runes16toutf(rootdir, wrootdir, MAXROOT);
+ if(n >= MAXROOT || n == 0)
+ panic("illegal root path");
+
+ /* get rid of trailing \ */
+ while(rootdir[n-1] == '\\') {
+ if(n <= 2) {
+ panic("illegal root path");
+ }
+ rootdir[--n] = '\0';
+ }
+
+ isvol = 0;
+ if(rootdir[1] == ':' && rootdir[2] == '\0')
+ isvol = 1;
+ else if(rootdir[0] == '\\' && rootdir[1] == '\\') {
+ p = strchr(&rootdir[2], '\\');
+ if(p == nil)
+ panic("inferno root can't be a server");
+ isvol = strchr(p+1, '\\') == nil;
+ }
+
+ if(strchr(rootdir, '\\') == nil)
+ strcat(rootdir, "\\.");
+ attr = GetFileAttributes(wrootdir);
+ if(attr == 0xFFFFFFFF)
+ panic("root path '%s' does not exist", narrowen(wrootdir));
+ rootqid.path = fsqidpath(rootdir);
+ if(attr & FILE_ATTRIBUTE_DIRECTORY)
+ rootqid.type |= QTDIR;
+ rootdir[n] = '\0';
+
+ rootqid.vers = time(0);
+
+ /*
+ * set up for nt file security checking
+ */
+ ntsrv = filesrv(rootdir);
+ usesec = PlatformId == VER_PLATFORM_WIN32_NT; /* true for NT and 2000 */
+ if(usesec){
+ file_share_delete = FILE_SHARE_DELETE; /* sensible handling of shared files by delete and rename */
+ secinit();
+ if(!fsacls(rootdir))
+ usesec = 0;
+ }
+ checksec = usesec && isserver;
+}
+
+Chan*
+fsattach(char *spec)
+{
+ Chan *c;
+ static int devno;
+ static Lock l;
+ char *drive = (char *)spec;
+
+ if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0'))
+ error(Ebadspec);
+
+ c = devattach('U', spec);
+ lock(&l);
+ c->dev = devno++;
+ unlock(&l);
+ c->qid = rootqid;
+ c->aux = smalloc(sizeof(Fsinfo));
+ FS(c)->srv = ntsrv;
+ if(!emptystr(spec)) {
+ char *s = smalloc(strlen(spec)+1);
+ strcpy(s, spec);
+ FS(c)->spec = s;
+ FS(c)->srv = filesrv(spec);
+ if(usesec)
+ FS(c)->usesec = fsacls(spec);
+ FS(c)->checksec = FS(c)->usesec && isserver;
+ c->qid.path = fsqidpath(spec);
+ c->qid.type = QTDIR;
+ c->qid.vers = 0;
+ }else{
+ FS(c)->usesec = usesec;
+ FS(c)->checksec = checksec;
+ }
+ FS(c)->name = newcname("/");
+ return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j, alloc;
+ Walkqid *wq;
+ char path[MAX_PATH], *p;
+ Cname *ph;
+ Cname *current, *next;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ current = nil;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone != nil)
+ cclose(wq->clone);
+ cnameclose(current);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0;
+ alloc = 1;
+ }
+ wq->clone = nc;
+ current = FS(c)->name;
+ if(current != nil)
+ incref(¤t->r);
+ for(j = 0; j < nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ if(!okelem(name[j], 0)){
+ if(j == 0)
+ error(Efilename);
+ break;
+ }
+ p = fspath(current, name[j], path, FS(c)->spec);
+ if(FS(c)->checksec) {
+ *p = '\0';
+ if(!sechasperm(path, XMODE, FS(c)->srv)){
+ if(j == 0)
+ error(Eperm);
+ break;
+ }
+ *p = '\\';
+ }
+
+ if(strcmp(name[j], "..") == 0) {
+ if(fsisroot(c))
+ nc->qid = rootqid;
+ else{
+ ph = fswalkpath(current, "..", 1);
+ if(cnisroot(ph)){
+ nc->qid = rootqid;
+ current = ph;
+ if(current != nil)
+ incref(¤t->r);
+ }
+ else {
+ fspath(ph, 0, path, FS(c)->spec);
+ if(!fsexist(path, &nc->qid)){
+ cnameclose(ph);
+ if(j == 0)
+ error(Enonexist);
+ break;
+ }
+ }
+ next = fswalkpath(current, name[j], 1);
+ cnameclose(current);
+ current = next;
+ cnameclose(ph);
+ }
+ }
+ else{
+ if(!fsexist(path, &nc->qid)){
+ if(j == 0)
+ error(Enonexist);
+ break;
+ }
+ next = fswalkpath(current, name[j], 1);
+ cnameclose(current);
+ current = next;
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+ poperror();
+ if(wq->nqid < nname){
+ cnameclose(current);
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ nc->aux = smalloc(sizeof(Fsinfo));
+ nc->type = c->type;
+ FS(nc)->spec = FS(c)->spec;
+ FS(nc)->srv = FS(c)->srv;
+ FS(nc)->name = current;
+ FS(nc)->usesec = FS(c)->usesec;
+ FS(nc)->checksec = FS(c)->checksec;
+ }
+ return wq;
+}
+
+Chan*
+fsopen(Chan *c, int mode)
+{
+ HANDLE h;
+ int m, isdir, aflag, cflag;
+ char path[MAX_PATH];
+ wchar_t *wpath;
+
+ isdir = c->qid.type & QTDIR;
+ if(isdir && mode != OREAD)
+ error(Eperm);
+ fspath(FS(c)->name, 0, path, FS(c)->spec);
+
+ if(FS(c)->checksec) {
+ switch(mode & (OTRUNC|3)) {
+ case OREAD:
+ seccheck(path, RMODE, FS(c)->srv);
+ break;
+ case OWRITE:
+ case OWRITE|OTRUNC:
+ seccheck(path, WMODE, FS(c)->srv);
+ break;
+ case ORDWR:
+ case ORDWR|OTRUNC:
+ case OREAD|OTRUNC:
+ seccheck(path, RMODE|WMODE, FS(c)->srv);
+ break;
+ case OEXEC:
+ seccheck(path, XMODE, FS(c)->srv);
+ break;
+ default:
+ error(Ebadarg);
+ }
+ }
+
+ c->mode = openmode(mode);
+ if(isdir)
+ FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+ else {
+ m = fsomode(mode & 3);
+ cflag = OPEN_EXISTING;
+ if(mode & OTRUNC)
+ cflag = TRUNCATE_EXISTING;
+ aflag = FILE_FLAG_RANDOM_ACCESS;
+ if(mode & ORCLOSE)
+ aflag |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (winfileclash(path))
+ error(Eexist);
+ wpath = widen(path);
+ h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE)
+ oserror();
+ FS(c)->fd = nth2fd(h);
+ }
+
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+ Stat st;
+ HANDLE h;
+ int m, aflag;
+ SECURITY_ATTRIBUTES sa;
+ SECURITY_DESCRIPTOR *sd;
+ BY_HANDLE_FILE_INFORMATION hi;
+ char *p, path[MAX_PATH], sdrock[SD_ROCK];
+ wchar_t *wpath;
+ ACL *acl;
+
+ if(!okelem(name, 1))
+ error(Efilename);
+
+ m = fsomode(mode & 3);
+ p = fspath(FS(c)->name, name, path, FS(c)->spec);
+ acl = (ACL*)smalloc(ACL_ROCK);
+ sd = nil;
+ if(FS(c)->usesec) {
+ *p = '\0';
+ sd = secsd(path, sdrock);
+ *p = '\\';
+ if(sd == nil){
+ free(acl);
+ oserror();
+ }
+ if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv)
+ || !secsdstat(sd, &st, FS(c)->srv)){
+ if(sd != (void*)sdrock)
+ free(sd);
+ free(acl);
+ error(Eperm);
+ }
+ if(sd != (void*)sdrock)
+ free(sd);
+ if(perm & DMDIR)
+ st.mode = (perm & ~0777) | (st.mode & perm & 0777);
+ else
+ st.mode = (perm & ~0666) | (st.mode & perm & 0666);
+ st.owner = up->env->ui;
+ if(!isserver)
+ st.owner = fsuser;
+ sd = secmksd(sdrock, &st, acl, perm & DMDIR);
+ if(sd == nil){
+ free(acl);
+ oserror();
+ }
+ }
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd;
+ sa.bInheritHandle = 0;
+
+ if(perm & DMDIR) {
+ if(mode != OREAD) {
+ free(acl);
+ error(Eisdir);
+ }
+ wpath = widen(path);
+ if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) {
+ free(wpath);
+ free(acl);
+ oserror();
+ }
+ free(wpath);
+ FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+ }
+ else {
+ aflag = 0;
+ if(mode & ORCLOSE)
+ aflag = FILE_FLAG_DELETE_ON_CLOSE;
+ if (winfileclash(path))
+ error(Eexist);
+ wpath = widen(path);
+ h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE) {
+ free(acl);
+ oserror();
+ }
+ FS(c)->fd = nth2fd(h);
+ c->qid.path = fsqidpath(path);
+ c->qid.type = 0;
+ c->qid.vers = 0;
+ if(GetFileInformationByHandle(h, &hi))
+ c->qid.vers = unixtime(hi.ftLastWriteTime);
+ }
+
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ FS(c)->name = fswalkpath(FS(c)->name, name, 0);
+ free(acl);
+}
+
+void
+fsclose(Chan *c)
+{
+ HANDLE h;
+
+ if(c->flag & COPEN){
+ h = ntfd2h(FS(c)->fd);
+ if(h != INVALID_HANDLE_VALUE){
+ if(c->qid.type & QTDIR)
+ FindClose(h);
+ else
+ CloseHandle(h);
+ }
+ }
+ if(c->flag & CRCLOSE){
+ if(!waserror()){
+ fsremove(c);
+ poperror();
+ }
+ return;
+ }
+ fsfree(c);
+}
+
+/*
+ * 64-bit seeks, using SetFilePointer because SetFilePointerEx
+ * is not supported by NT
+ */
+static void
+fslseek(HANDLE h, vlong offset)
+{
+ LONG hi;
+
+ if(offset <= 0x7fffffff){
+ if(SetFilePointer(h, (LONG)offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ oserror();
+ }else{
+ hi = offset>>32;
+ if(SetFilePointer(h, (LONG)offset, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER &&
+ GetLastError() != NO_ERROR)
+ oserror();
+ }
+}
+
+long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+ DWORD n2;
+ HANDLE h;
+
+ qlock(&FS(c)->oq);
+ if(waserror()){
+ qunlock(&FS(c)->oq);
+ nexterror();
+ }
+ if(c->qid.type & QTDIR) {
+ n2 = fsdirread(c, va, n, offset);
+ }
+ else {
+ h = ntfd2h(FS(c)->fd);
+ if(FS(c)->offset != offset){
+ fslseek(h, offset);
+ FS(c)->offset = offset;
+ }
+ if(!ReadFile(h, va, n, &n2, NULL))
+ oserror();
+ FS(c)->offset += n2;
+ }
+ qunlock(&FS(c)->oq);
+ poperror();
+ return n2;
+}
+
+long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+ DWORD n2;
+ HANDLE h;
+
+ qlock(&FS(c)->oq);
+ if(waserror()){
+ qunlock(&FS(c)->oq);
+ nexterror();
+ }
+ h = ntfd2h(FS(c)->fd);
+ if(FS(c)->offset != offset){
+ fslseek(h, offset);
+ FS(c)->offset = offset;
+ }
+ if(!WriteFile(h, va, n, &n2, NULL))
+ oserror();
+ FS(c)->offset += n2;
+ qunlock(&FS(c)->oq);
+ poperror();
+ return n2;
+}
+
+int
+fsstat(Chan *c, uchar *buf, int n)
+{
+ WIN32_FIND_DATA data;
+ char path[MAX_PATH];
+ wchar_t *wpath;
+
+ /*
+ * have to fake up a data for volumes like
+ * c: and \\server\share since you can't FindFirstFile them
+ */
+ if(fsisroot(c)){
+ strcpy(path, rootdir);
+ if(strchr(path, '\\') == nil)
+ strcat(path, "\\.");
+ wpath = widen(path);
+ data.dwFileAttributes = GetFileAttributes(wpath);
+ free(wpath);
+ if(data.dwFileAttributes == 0xffffffff)
+ oserror();
+ data.ftCreationTime =
+ data.ftLastAccessTime =
+ data.ftLastWriteTime = wintime(time(0));
+ data.nFileSizeHigh = 0;
+ data.nFileSizeLow = 0;
+ utftorunes16(data.cFileName, ".", MAX_PATH);
+ } else {
+ HANDLE h = INVALID_HANDLE_VALUE;
+
+ fspath(FS(c)->name, 0, path, FS(c)->spec);
+ if (c->flag & COPEN)
+ h = ntfd2h(FS(c)->fd);
+
+ if (h != INVALID_HANDLE_VALUE) {
+ BY_HANDLE_FILE_INFORMATION fi;
+ if (c->mode & OWRITE)
+ FlushFileBuffers(h);
+ if (!GetFileInformationByHandle(h, &fi))
+ oserror();
+ data.dwFileAttributes = fi.dwFileAttributes;
+ data.ftCreationTime = fi.ftCreationTime;
+ data.ftLastAccessTime = fi.ftLastAccessTime;
+ data.ftLastWriteTime = fi.ftLastWriteTime;;
+ data.nFileSizeHigh = fi.nFileSizeHigh;
+ data.nFileSizeLow = fi.nFileSizeLow;
+ } else {
+ wpath = widen(path);
+ h = FindFirstFile(wpath, &data);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE)
+ oserror();
+ if (!winfilematch(path, &data)) {
+ FindClose(h);
+ error(Enonexist);
+ }
+ FindClose(h);
+ }
+ utftorunes16(data.cFileName, fslastelem(FS(c)->name), MAX_PATH);
+ }
+
+ return fsdirset(buf, n, &data, path, c, 0);
+}
+
+int
+fswstat(Chan *c, uchar *buf, int n)
+{
+ int wsd;
+ Dir dir;
+ Stat st;
+ Cname * volatile ph;
+ HANDLE h;
+ ulong attr;
+ User *ou, *gu;
+ WIN32_FIND_DATA data;
+ SECURITY_DESCRIPTOR *sd;
+ char *last, sdrock[SD_ROCK], path[MAX_PATH], newpath[MAX_PATH], strs[4*256];
+ wchar_t wspath[MAX_PATH], wsnewpath[MAX_PATH];
+ wchar_t *wpath;
+ int nmatch;
+
+ n = convM2D(buf, n, &dir, strs);
+ if(n == 0)
+ error(Eshortstat);
+
+ last = fspath(FS(c)->name, 0, path, FS(c)->spec);
+ utftorunes16(wspath, path, MAX_PATH);
+
+ if(fsisroot(c)){
+ if(dir.atime != ~0)
+ data.ftLastAccessTime = wintime(dir.atime);
+ if(dir.mtime != ~0)
+ data.ftLastWriteTime = wintime(dir.mtime);
+ utftorunes16(data.cFileName, ".", MAX_PATH);
+ }else{
+ h = FindFirstFile(wspath, &data);
+ if(h == INVALID_HANDLE_VALUE)
+ oserror();
+ if (!winfilematch(path, &data)) {
+ FindClose(h);
+ error(Enonexist);
+ }
+ FindClose(h);
+ }
+
+ wsd = 0;
+ ou = nil;
+ gu = nil;
+ if(FS(c)->usesec) {
+ if(FS(c)->checksec && up->env->ui == fsnone)
+ error(Eperm);
+
+ /*
+ * find new owner and group
+ */
+ if(!emptystr(dir.uid)){
+ ou = unametouser(FS(c)->srv, dir.uid);
+ if(ou == nil)
+ oserror();
+ }
+ if(!emptystr(dir.gid)){
+ gu = unametouser(FS(c)->srv, dir.gid);
+ if(gu == nil){
+ if(strcmp(dir.gid, "unknown") != 0
+ && strcmp(dir.gid, "deleted") != 0)
+ oserror();
+ gu = ou;
+ }
+ }
+
+ /*
+ * find old stat info
+ */
+ sd = secsd(path, sdrock);
+ if(sd == nil || !secsdstat(sd, &st, FS(c)->srv)){
+ if(sd != nil && sd != (void*)sdrock)
+ free(sd);
+ oserror();
+ }
+ if(sd != (void*)sdrock)
+ free(sd);
+
+ /*
+ * permission rules:
+ * if none, can't do anything
+ * chown => no way
+ * chgrp => current owner or group, and in new group
+ * mode/time => owner or in either group
+ * rename => write in parent
+ */
+ if(ou == nil)
+ ou = st.owner;
+ if(FS(c)->checksec && st.owner != ou)
+ error(Eperm);
+
+ if(gu == nil)
+ gu = st.group;
+ if(st.group != gu){
+ if(FS(c)->checksec
+ &&(!ismember(up->env->ui, ou) && !ismember(up->env->ui, gu)
+ || !ismember(up->env->ui, st.group)))
+ error(Eperm);
+ wsd = 1;
+ }
+
+ if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
+ || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime
+ || dir.mode != ~0 && st.mode != dir.mode){
+ if(FS(c)->checksec
+ && !ismember(up->env->ui, ou)
+ && !ismember(up->env->ui, gu)
+ && !ismember(up->env->ui, st.group))
+ error(Eperm);
+ if(dir.mode != ~0 && st.mode != dir.mode)
+ wsd = 1;
+ }
+ }
+ wpath = widen(dir.name);
+ nmatch = runes16cmp(wpath, data.cFileName);
+ free(wpath);
+ if(!emptystr(dir.name) && nmatch != 0){
+ if(!okelem(dir.name, 1))
+ error(Efilename);
+ ph = fswalkpath(FS(c)->name, "..", 1);
+ if(waserror()){
+ cnameclose(ph);
+ nexterror();
+ }
+ ph = fswalkpath(ph, dir.name, 0);
+ fspath(ph, 0, newpath, FS(c)->spec);
+ utftorunes16(wsnewpath, newpath, MAX_PATH);
+ if(GetFileAttributes(wpath) != 0xffffffff && !winfileclash(newpath))
+ error("file already exists");
+ if(fsisroot(c))
+ error(Eperm);
+ if(FS(c)->checksec){
+ *last = '\0';
+ seccheck(path, WMODE, FS(c)->srv);
+ *last = '\\';
+ }
+ poperror();
+ cnameclose(ph);
+ }
+
+ if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
+ || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime)
+ fssettime(path, dir.atime, dir.mtime);
+
+ attr = data.dwFileAttributes;
+ if(dir.mode & 0222)
+ attr &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ attr |= FILE_ATTRIBUTE_READONLY;
+ if(!fsisroot(c)
+ && attr != data.dwFileAttributes
+ && (attr & FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes(wspath, attr);
+ if(FS(c)->usesec && wsd){
+ ACL *acl = (ACL *) smalloc(ACL_ROCK);
+ st.owner = ou;
+ st.group = gu;
+ if(dir.mode != ~0)
+ st.mode = dir.mode;
+ sd = secmksd(sdrock, &st, acl, data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+ if(sd == nil || !SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd)){
+ free(acl);
+ oserror();
+ }
+ free(acl);
+ }
+
+ if(!fsisroot(c)
+ && attr != data.dwFileAttributes
+ && !(attr & FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes(wspath, attr);
+
+ /* do last so path is valid throughout */
+ wpath = widen(dir.name);
+ nmatch = runes16cmp(wpath, data.cFileName);
+ free(wpath);
+ if(!emptystr(dir.name) && nmatch != 0) {
+ ph = fswalkpath(FS(c)->name, "..", 1);
+ if(waserror()){
+ cnameclose(ph);
+ nexterror();
+ }
+ ph = fswalkpath(ph, dir.name, 0);
+ fspath(ph, 0, newpath, FS(c)->spec);
+ utftorunes16(wsnewpath, newpath, MAX_PATH);
+ /*
+ * can't rename if it is open: if this process has it open, close it temporarily.
+ */
+ if(!file_share_delete && c->flag & COPEN){
+ h = ntfd2h(FS(c)->fd);
+ if(h != INVALID_HANDLE_VALUE)
+ CloseHandle(h); /* woe betide it if ORCLOSE */
+ FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+ }
+ if(!MoveFile(wspath, wsnewpath)) {
+ oserror();
+ } else if(!file_share_delete && c->flag & COPEN) {
+ int aflag;
+ SECURITY_ATTRIBUTES sa;
+
+ /* The move succeeded, so open new file to maintain handle */
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd;
+ sa.bInheritHandle = 0;
+ if(c->flag & CRCLOSE)
+ aflag = FILE_FLAG_DELETE_ON_CLOSE;
+ h = CreateFile(wsnewpath, fsomode(c->mode & 0x3), FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, OPEN_EXISTING, aflag, 0);
+ if(h == INVALID_HANDLE_VALUE)
+ oserror();
+ FS(c)->fd = nth2fd(h);
+ }
+ cnameclose(FS(c)->name);
+ poperror();
+ FS(c)->name = ph;
+ }
+ return n;
+}
+
+static void
+fsremove(Chan *c)
+{
+ int n;
+ char *p, path[MAX_PATH];
+ wchar_t wspath[MAX_PATH];
+
+ if(waserror()){
+ fsfree(c);
+ nexterror();
+ }
+ if(fsisroot(c))
+ error(Eperm);
+ p = fspath(FS(c)->name, 0, path, FS(c)->spec);
+ utftorunes16(wspath, path, MAX_PATH);
+ if(FS(c)->checksec){
+ *p = '\0';
+ seccheck(path, WMODE, FS(c)->srv);
+ *p = '\\';
+ }
+ if(c->qid.type & QTDIR)
+ n = RemoveDirectory(wspath);
+ else
+ n = DeleteFile(wspath);
+ if (!n) {
+ ulong attr, mode;
+ SECURITY_DESCRIPTOR *sd = nil;
+ char sdrock[SD_ROCK];
+ Stat st;
+ int secok;
+ attr = GetFileAttributes(wspath);
+ if(attr != 0xFFFFFFFF) {
+ if (FS(c)->usesec) {
+ sd = secsd(path, sdrock);
+ secok = (sd != nil) && secsdstat(sd, &st, FS(c)->srv);
+ if (secok) {
+ ACL *acl = (ACL *) smalloc(ACL_ROCK);
+ mode = st.mode;
+ st.mode |= 0660;
+ sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
+ if(sd != nil) {
+ SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
+ }
+ free(acl);
+ if(sd != nil && sd != (void*)sdrock)
+ free(sd);
+ sd = nil;
+ }
+ }
+ SetFileAttributes(wspath, FILE_ATTRIBUTE_NORMAL);
+ if(c->qid.type & QTDIR)
+ n = RemoveDirectory(wspath);
+ else
+ n = DeleteFile(wspath);
+ if (!n) {
+ if (FS(c)->usesec && secok) {
+ ACL *acl = (ACL *) smalloc(ACL_ROCK);
+ st.mode = mode;
+ sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
+ if(sd != nil) {
+ SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
+ }
+ free(acl);
+ }
+ SetFileAttributes(wspath, attr);
+ if(sd != nil && sd != (void*)sdrock)
+ free(sd);
+ }
+ }
+ }
+ if(!n)
+ oserror();
+ poperror();
+ fsfree(c);
+}
+
+/*
+ * check elem for illegal characters /\:*?"<>
+ * ... and relatives are also disallowed,
+ * since they specify grandparents, which we
+ * are not prepared to handle
+ */
+static int
+okelem(char *elem, int nodots)
+{
+ int c, dots;
+
+ dots = 0;
+ while((c = *(uchar*)elem) != 0){
+ if(isntfrog[c])
+ return 0;
+ if(c == '.' && dots >= 0)
+ dots++;
+ else
+ dots = -1;
+ elem++;
+ }
+ if(nodots)
+ return dots <= 0;
+ return dots <= 2;
+}
+
+static int
+cnisroot(Cname *c)
+{
+ return strcmp(c->s, "/") == 0;
+}
+
+static int
+fsisroot(Chan *c)
+{
+ return strcmp(FS(c)->name->s, "/") == 0;
+}
+
+static char*
+fspath(Cname *c, char *ext, char *path, char *spec)
+{
+ char *p, *last, *rootd;
+ int extlen = 0;
+
+ rootd = spec != nil ? spec : rootdir;
+ if(ext)
+ extlen = strlen(ext) + 1;
+ if(strlen(rootd) + extlen >= MAX_PATH)
+ error(Etoolong);
+ strcpy(path, rootd);
+ if(cnisroot(c)){
+ if(ext) {
+ strcat(path, "\\");
+ strcat(path, ext);
+ }
+ }else{
+ if(*c->s != '/') {
+ if(strlen(path) + 1 >= MAX_PATH)
+ error(Etoolong);
+ strcat(path, "\\");
+ }
+ if(strlen(path) + strlen(c->s) + extlen >= MAX_PATH)
+ error(Etoolong);
+ strcat(path, c->s);
+ if(ext){
+ strcat(path, "\\");
+ strcat(path, ext);
+ }
+ }
+ last = path;
+ for(p = path; *p != '\0'; p++){
+ if(*p == '/' || *p == '\\'){
+ *p = '\\';
+ last = p;
+ }
+ }
+ return last;
+}
+
+extern void cleancname(Cname*);
+
+static Cname *
+fswalkpath(Cname *c, char *name, int dup)
+{
+ if(dup)
+ c = newcname(c->s);
+ c = addelem(c, name);
+ if(isdotdot(name))
+ cleancname(c);
+ return c;
+}
+
+static char *
+fslastelem(Cname *c)
+{
+ char *p;
+
+ p = c->s + c->len;
+ while(p > c->s && p[-1] != '/')
+ p--;
+ return p;
+}
+
+static int
+fsdirbadentry(WIN32_FIND_DATA *data)
+{
+ wchar_t *s;
+
+ s = data->cFileName;
+ if(s[0] == 0)
+ return 1;
+ if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0))
+ return 1;
+
+ return 0;
+}
+
+static Fsdir*
+fsdirent(Chan *c, char *path, Fsdir *data)
+{
+ wchar_t *wpath;
+ HANDLE h;
+
+ h = ntfd2h(FS(c)->fd);
+ if(data == nil)
+ data = smalloc(sizeof(*data));
+ if(FS(c)->offset == 0){
+ if(h != INVALID_HANDLE_VALUE)
+ FindClose(h);
+ wpath = widen(path);
+ h = FindFirstFile(wpath, data);
+ free(wpath);
+ FS(c)->fd = nth2fd(h);
+ if(h == INVALID_HANDLE_VALUE){
+ free(data);
+ return nil;
+ }
+ if(!fsdirbadentry(data))
+ return data;
+ }
+ do{
+ if(!FindNextFile(h, data)){
+ free(data);
+ return nil;
+ }
+ }while(fsdirbadentry(data));
+ return data;
+}
+
+static long
+fsdirread(Chan *c, uchar *va, int count, vlong offset)
+{
+ int i, r;
+ char path[MAX_PATH], *p;
+ Fsdir *de;
+ vlong o;
+
+ if(count == 0 || offset < 0)
+ return 0;
+ p = fspath(FS(c)->name, "*.*", path, FS(c)->spec);
+ p++;
+ de = nil;
+ if(FS(c)->offset != offset){
+ de = FS(c)->de;
+ if(FS(c)->de != nil){
+ free(FS(c)->de);
+ FS(c)->de = nil;
+ }
+ FS(c)->offset = 0;
+ for(o = 0; o < offset;){
+ de = fsdirent(c, path, de);
+ if(de == nil){
+ FS(c)->offset = o;
+ return 0;
+ }
+ runes16toutf(p, de->cFileName, &path[MAX_PATH]-p);
+ path[MAX_PATH-1] = '\0';
+ o += fsdirsize(de, path, c);
+ }
+ FS(c)->offset = offset;
+ }
+ for(i = 0; i < count;){
+ if(FS(c)->de != nil){ /* left over from previous read at offset */
+ de = FS(c)->de;
+ FS(c)->de = nil;
+ }else{
+ de = fsdirent(c, path, de);
+ if(de == nil)
+ break;
+ }
+ runes16toutf(p, de->cFileName, &path[MAX_PATH]-p);
+ path[MAX_PATH-1] = '\0';
+ r = fsdirset(va+i, count-i, de, path, c, 1);
+ if(r <= 0){
+ /* won't fit; save for next read at this offset */
+ FS(c)->de = de;
+ break;
+ }
+ i += r;
+ FS(c)->offset += r;
+ }
+ return i;
+}
+
+static ulong
+fsqidpath(char *p)
+{
+ ulong h;
+ int c;
+
+ h = 0;
+ while(*p != '\0'){
+ /* force case insensitive file names */
+ c = *p++;
+ if(c >= 'A' && c <= 'Z')
+ c += 'a'-'A';
+ h = h * 19 ^ c;
+ }
+ return h;
+}
+
+/* TO DO: substitute fixed, made-up (unlikely) names for these */
+static char* devf[] = { "aux", "com1", "com2", "lpt1", "nul", "prn", nil };
+
+static int
+devfile(char *p)
+{
+ char *s, *t, *u, **ss;
+
+ if((u = strrchr(p, '\\')) != nil)
+ u++;
+ else if((u = strrchr(p, '/')) != nil)
+ u++;
+ else
+ u = p;
+ for(ss = devf; *ss != nil; ss++){
+ for(s = *ss, t = u; *s != '\0' && *t != '\0' && *t != '.'; s++, t++)
+ if(*s != *t && *s != *t+'a'-'A')
+ break;
+ if(*s == '\0' && (*t == '\0' || *t == '.'))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * there are other ways to figure out
+ * the attributes and times for a file.
+ * perhaps they are faster
+ */
+static int
+fsexist(char *p, Qid *q)
+{
+ HANDLE h;
+ WIN32_FIND_DATA data;
+ wchar_t *wpath;
+
+ if(devfile(p))
+ return 0;
+ wpath = widen(p);
+ h = FindFirstFile(wpath, &data);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE)
+ return 0;
+ if (!winfilematch(p, &data)) {
+ FindClose(h);
+ return 0;
+ }
+ FindClose(h);
+
+ q->path = fsqidpath(p);
+ q->type = 0;
+
+ if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ q->type |= QTDIR;
+
+ q->vers = unixtime(data.ftLastWriteTime);
+
+ return 1;
+}
+
+static int
+fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir)
+{
+ Dir dir;
+ static char neveryone[] = "Everyone";
+
+ dir.name = narrowen(data->cFileName);
+ dir.muid = nil;
+ dir.qid.path = fsqidpath(path);
+ dir.qid.vers = 0;
+ dir.qid.type = 0;
+ dir.mode = 0;
+ dir.atime = unixtime(data->ftLastAccessTime);
+ dir.mtime = unixtime(data->ftLastWriteTime);
+ dir.qid.vers = dir.mtime;
+ dir.length = ((uvlong)data->nFileSizeHigh<<32) | ((uvlong)data->nFileSizeLow & ~((uvlong)0xFFFFFFFF<<32));
+ dir.type = 'U';
+ dir.dev = c->dev;
+
+ if(!FS(c)->usesec){
+ /* no NT security so make something up */
+ dir.uid = neveryone;
+ dir.gid = neveryone;
+ dir.mode = 0777;
+ }else if(!secstat(&dir, path, FS(c)->srv))
+ oserror();
+
+ if(data->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ dir.mode &= ~0222;
+ if(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
+ dir.qid.type |= QTDIR;
+ dir.mode |= DMDIR;
+ dir.length = 0;
+ }
+
+ if(isdir && sizeD2M(&dir) > n)
+ n = -1;
+ else
+ n = convD2M(&dir, edir, n);
+ if(dir.uid != neveryone)
+ free(dir.uid);
+ if(dir.gid != neveryone)
+ free(dir.gid);
+ free(dir.name);
+ return n;
+}
+
+static int
+fsdirsize(WIN32_FIND_DATA *data, char *path, Chan *c)
+{
+ int i, n;
+
+ n = widebytes(data->cFileName);
+ if(!FS(c)->usesec)
+ n += 8+8;
+ else{
+ i = secsize(path, FS(c)->srv);
+ if(i < 0)
+ oserror();
+ n += i;
+ }
+ return STATFIXLEN+n;
+}
+
+static void
+fssettime(char *path, long at, long mt)
+{
+ HANDLE h;
+ FILETIME atime, mtime;
+ wchar_t *wpath;
+
+ wpath = widen(path);
+ h = CreateFile(wpath, GENERIC_WRITE,
+ 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE)
+ return;
+ mtime = wintime(mt);
+ atime = wintime(at);
+ if(!SetFileTime(h, 0, &atime, &mtime)){
+ CloseHandle(h);
+ oserror();
+ }
+ CloseHandle(h);
+}
+
+static int
+fsomode(int m)
+{
+ switch(m & 0x3) {
+ case OREAD:
+ case OEXEC:
+ return GENERIC_READ;
+ case OWRITE:
+ return GENERIC_WRITE;
+ case ORDWR:
+ return GENERIC_READ|GENERIC_WRITE;
+ }
+ error(Ebadarg);
+ return 0;
+}
+
+static long
+unixtime(FILETIME ft)
+{
+ vlong t;
+
+ t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32);
+ t -= (vlong)10000000*134774*24*60*60;
+
+ return (long)(t/10000000);
+}
+
+static FILETIME
+wintime(ulong t)
+{
+ FILETIME ft;
+ vlong vt;
+
+ vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60;
+
+ ft.dwLowDateTime = vt;
+ ft.dwHighDateTime = vt>>32;
+
+ return ft;
+}
+
+/*
+ * the sec routines manage file permissions for nt.
+ * nt files have an associated security descriptor,
+ * which has in it an owner, a group,
+ * and a discretionary acces control list, or acl,
+ * which specifies the permissions for the file.
+ *
+ * the strategy for mapping between inferno owner,
+ * group, other, and mode and nt file security is:
+ *
+ * inferno owner == nt file owner
+ * inferno other == nt Everyone
+ * inferno group == first non-owner,
+ * non-Everyone user given in the acl,
+ * or the owner if there is no such user.
+ * we examine the entire acl when check for permissions,
+ * but only report a subset.
+ *
+ * when we write an acl, we also give all permissions to
+ * the special user rootname, who is supposed to run emu in server mode.
+ */
+static void
+secinit(void)
+{
+ HANDLE token;
+ TOKEN_PRIVILEGES *priv;
+ char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)];
+ SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY;
+
+ if(!AllocateAndInitializeSid(&id, 1,
+ SECURITY_CREATOR_OWNER_RID,
+ 1, 2, 3, 4, 5, 6, 7, &creatorowner)
+ || !AllocateAndInitializeSid(&id, 1,
+ SECURITY_CREATOR_GROUP_RID,
+ 1, 2, 3, 4, 5, 6, 7, &creatorgroup)
+ || !AllocateAndInitializeSid(&wid, 1,
+ SECURITY_WORLD_RID,
+ 1, 2, 3, 4, 5, 6, 7, &everyone)
+ || !AllocateAndInitializeSid(&ntid, 1,
+ 0,
+ 1, 2, 3, 4, 5, 6, 7, &ntignore))
+ panic("can't initialize well-known sids");
+
+ fsnone = sidtouser(ntsrv, everyone);
+ if(fsnone == nil)
+ panic("can't make none user");
+
+ /*
+ * see if we are running as the emu server user
+ * if so, set up SE_RESTORE_NAME privilege,
+ * which allows setting the owner field in a security descriptor.
+ * other interesting privileges are SE_TAKE_OWNERSHIP_NAME,
+ * which enables changing the ownership of a file to yourself
+ * regardless of the permissions on the file, SE_BACKUP_NAME,
+ * which enables reading any files regardless of permission,
+ * and SE_CHANGE_NOTIFY_NAME, which enables walking through
+ * directories without X permission.
+ * SE_RESTORE_NAME and SE_BACKUP_NAME together allow writing
+ * and reading any file data, regardless of permission,
+ * if the file is opened with FILE_BACKUP_SEMANTICS.
+ */
+ isserver = 0;
+ fsuser = secuser();
+ if(fsuser == nil)
+ fsuser = fsnone;
+ else if(runes16cmp(fsuser->name, rootname) == 0
+ && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)){
+ priv = (TOKEN_PRIVILEGES*)privrock;
+ priv->PrivilegeCount = 1;
+ priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid)
+ && AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
+ isserver = 1;
+ CloseHandle(token);
+ }
+}
+
+/*
+ * get the User for the executing process
+ */
+static User*
+secuser(void)
+{
+ DWORD need;
+ HANDLE token;
+ TOKEN_USER *tu;
+ char turock[sizeof(TOKEN_USER) + MAX_SID];
+
+ if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
+ return nil;
+
+ tu = (TOKEN_USER*)turock;
+ if(!GetTokenInformation(token, TokenUser, tu, sizeof(turock), &need)){
+ CloseHandle(token);
+ return nil;
+ }
+ CloseHandle(token);
+ return sidtouser(nil, tu->User.Sid);
+}
+
+static int
+secstat(Dir *dir, char *file, Rune16 *srv)
+{
+ int ok, n;
+ Stat st;
+ char sdrock[SD_ROCK];
+ SECURITY_DESCRIPTOR *sd;
+
+ sd = secsd(file, sdrock);
+ if(sd == nil){
+ int e = GetLastError();
+ if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION){
+ dir->uid = strdup("unknown");
+ dir->gid = strdup("unknown");
+ if(dir->uid == nil || dir->gid == nil){
+ free(dir->uid);
+ error(Enomem); /* will change to use kstrdup */
+ }
+ dir->mode = 0;
+ return 1;
+ }
+ return 0;
+ }
+ ok = secsdstat(sd, &st, srv);
+ if(sd != (void*)sdrock)
+ free(sd);
+ if(ok){
+ dir->mode = st.mode;
+ n = rune16nlen(st.owner->name, runes16len(st.owner->name));
+ dir->uid = smalloc(n+1);
+ runes16toutf(dir->uid, st.owner->name, n+1);
+ n = rune16nlen(st.group->name, runes16len(st.group->name));
+ dir->gid = smalloc(n+1);
+ runes16toutf(dir->gid, st.group->name, n+1);
+ }
+ return ok;
+}
+
+static int
+secsize(char *file, Rune16 *srv)
+{
+ int ok;
+ Stat st;
+ char sdrock[SD_ROCK];
+ SECURITY_DESCRIPTOR *sd;
+
+ sd = secsd(file, sdrock);
+ if(sd == nil){
+ int e = GetLastError();
+ if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION)
+ return 7+7;
+ return -1;
+ }
+ ok = secsdstat(sd, &st, srv);
+ if(sd != (void*)sdrock)
+ free(sd);
+ if(ok)
+ return rune16nlen(st.owner->name, runes16len(st.owner->name))+rune16nlen(st.group->name, runes16len(st.group->name));
+ return -1;
+}
+
+/*
+ * verify that u had access to file
+ */
+static void
+seccheck(char *file, ulong access, Rune16 *srv)
+{
+ if(!sechasperm(file, access, srv))
+ error(Eperm);
+}
+
+static int
+sechasperm(char *file, ulong access, Rune16 *srv)
+{
+ int ok;
+ char sdrock[SD_ROCK];
+ SECURITY_DESCRIPTOR *sd;
+
+ /*
+ * only really needs dacl info
+ */
+ sd = secsd(file, sdrock);
+ if(sd == nil)
+ return 0;
+ ok = secsdhasperm(sd, access, srv);
+ if(sd != (void*)sdrock)
+ free(sd);
+ return ok;
+}
+
+static SECURITY_DESCRIPTOR*
+secsd(char *file, char sdrock[SD_ROCK])
+{
+ DWORD need;
+ SECURITY_DESCRIPTOR *sd;
+ char *path, pathrock[6];
+ wchar_t *wpath;
+
+ path = file;
+ if(path[0] != '\0' && path[1] == ':' && path[2] == '\0'){
+ path = pathrock;
+ strcpy(path, "?:\\.");
+ path[0] = file[0];
+ }
+ sd = (SECURITY_DESCRIPTOR*)sdrock;
+ need = 0;
+ wpath = widen(path);
+ if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, SD_ROCK, &need)) {
+ free(wpath);
+ return sd;
+ }
+ if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ free(wpath);
+ return nil;
+ }
+ sd = malloc(need);
+ if(sd == nil) {
+ free(wpath);
+ error(Enomem);
+ }
+ if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, need, &need)) {
+ free(wpath);
+ return sd;
+ }
+ free(wpath);
+ free(sd);
+ return nil;
+}
+
+static int
+secsdstat(SECURITY_DESCRIPTOR *sd, Stat *st, Rune16 *srv)
+{
+ ACL *acl;
+ BOOL hasacl, b;
+ ACE_HEADER *aceh;
+ User *owner, *group;
+ SID *sid, *osid, *gsid;
+ ACCESS_ALLOWED_ACE *ace;
+ int i, allow, deny, *p, m;
+ ACL_SIZE_INFORMATION size;
+
+ st->mode = 0;
+
+ osid = nil;
+ gsid = nil;
+ if(!GetSecurityDescriptorOwner(sd, &osid, &b)
+ || !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
+ return 0;
+
+ if(acl == 0)
+ size.AceCount = 0;
+ else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
+ return 0;
+
+ /*
+ * first pass through acl finds group
+ */
+ for(i = 0; i < size.AceCount; i++){
+ if(!GetAce(acl, i, &aceh))
+ continue;
+ if(aceh->AceFlags & INHERIT_ONLY_ACE)
+ continue;
+
+ if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE
+ && aceh->AceType != ACCESS_DENIED_ACE_TYPE)
+ continue;
+
+ ace = (ACCESS_ALLOWED_ACE*)aceh;
+ sid = (SID*)&ace->SidStart;
+ if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+ continue;
+
+ if(EqualSid(sid, everyone))
+ ;
+ else if(EqualSid(sid, osid))
+ ;
+ else if(EqualPrefixSid(sid, ntignore))
+ continue; /* boring nt accounts */
+ else{
+ gsid = sid;
+ break;
+ }
+ }
+ if(gsid == nil)
+ gsid = osid;
+
+ owner = sidtouser(srv, osid);
+ if(owner == nil)
+ return 0;
+ group = sidtouser(srv, gsid);
+ if(group == nil)
+ return 0;
+
+ /* no acl means full access */
+ allow = 0;
+ if(acl == 0)
+ allow = 0777;
+ deny = 0;
+ for(i = 0; i < size.AceCount; i++){
+ if(!GetAce(acl, i, &aceh))
+ continue;
+ if(aceh->AceFlags & INHERIT_ONLY_ACE)
+ continue;
+
+ if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
+ p = &allow;
+ else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
+ p = &deny;
+ else
+ continue;
+
+ ace = (ACCESS_ALLOWED_ACE*)aceh;
+ sid = (SID*)&ace->SidStart;
+ if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+ continue;
+
+ m = 0;
+ if(ace->Mask & FILE_EXECUTE)
+ m |= 1;
+ if(ace->Mask & FILE_WRITE_DATA)
+ m |= 2;
+ if(ace->Mask & FILE_READ_DATA)
+ m |= 4;
+
+ if(ismembersid(srv, owner, sid))
+ *p |= (m << 6) & ~(allow|deny) & 0700;
+ if(ismembersid(srv, group, sid))
+ *p |= (m << 3) & ~(allow|deny) & 0070;
+ if(EqualSid(everyone, sid))
+ *p |= m & ~(allow|deny) & 0007;
+ }
+
+ st->mode = allow & ~deny;
+ st->owner = owner;
+ st->group = group;
+ return 1;
+}
+
+static int
+secsdhasperm(SECURITY_DESCRIPTOR *sd, ulong access, Rune16 *srv)
+{
+ User *u;
+ ACL *acl;
+ BOOL hasacl, b;
+ ACE_HEADER *aceh;
+ SID *sid, *osid, *gsid;
+ int i, allow, deny, *p, m;
+ ACCESS_ALLOWED_ACE *ace;
+ ACL_SIZE_INFORMATION size;
+
+ u = up->env->ui;
+ allow = 0;
+ deny = 0;
+ osid = nil;
+ gsid = nil;
+ if(!GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
+ return 0;
+
+ /* no acl means full access */
+ if(acl == 0)
+ return 1;
+ if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
+ return 0;
+ for(i = 0; i < size.AceCount; i++){
+ if(!GetAce(acl, i, &aceh))
+ continue;
+ if(aceh->AceFlags & INHERIT_ONLY_ACE)
+ continue;
+
+ if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
+ p = &allow;
+ else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
+ p = &deny;
+ else
+ continue;
+
+ ace = (ACCESS_ALLOWED_ACE*)aceh;
+ sid = (SID*)&ace->SidStart;
+ if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+ continue;
+
+ m = ace->Mask;
+
+ if(ismembersid(srv, u, sid))
+ *p |= m & ~(allow|deny);
+ }
+
+ allow &= ~deny;
+
+ return (allow & access) == access;
+}
+
+static SECURITY_DESCRIPTOR*
+secmksd(char *sdrock, Stat *st, ACL *dacl, int isdir)
+{
+ int m;
+
+ ulong mode;
+ ACE_HEADER *aceh;
+ SECURITY_DESCRIPTOR *sd;
+
+ sd = (SECURITY_DESCRIPTOR*)sdrock;
+ if(!InitializeAcl(dacl, ACL_ROCK, ACL_REVISION))
+ return nil;
+
+ mode = st->mode;
+ if(st->owner == st->group){
+ mode |= (mode >> 3) & 0070;
+ mode |= (mode << 3) & 0700;
+ }
+
+
+ m = modetomask[(mode>>6) & 7];
+ if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->owner->sid))
+ return nil;
+
+ if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION, m, creatorowner))
+ return nil;
+
+ m = modetomask[(mode>>3) & 7];
+ if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->group->sid))
+ return nil;
+
+ m = modetomask[(mode>>0) & 7];
+ if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, everyone))
+ return nil;
+
+ if(isdir){
+ /* hack to add inherit flags */
+ if(!GetAce(dacl, 1, &aceh))
+ return nil;
+ aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+ if(!GetAce(dacl, 2, &aceh))
+ return nil;
+ aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+ if(!GetAce(dacl, 3, &aceh))
+ return nil;
+ aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+ }
+
+ /*
+ * allow server user to access any file
+ */
+ if(isserver){
+ if(!AddAccessAllowedAce(dacl, ACL_REVISION, RMODE|WMODE|XMODE, fsuser->sid))
+ return nil;
+ if(isdir){
+ if(!GetAce(dacl, 4, &aceh))
+ return nil;
+ aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+ }
+ }
+
+ if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
+ return nil;
+ if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0))
+ return nil;
+// if(isserver && !SetSecurityDescriptorOwner(sd, st->owner->sid, 0))
+// return nil;
+ return sd;
+}
+
+/*
+ * the user manipulation routines
+ * just make it easier to deal with user identities
+ */
+static User*
+sidtouser(Rune16 *srv, SID *s)
+{
+ SID_NAME_USE type;
+ Rune16 aname[100], dname[100];
+ DWORD naname, ndname;
+ User *u;
+
+ qlock(&users.lk);
+ for(u = users.u; u != 0; u = u->next)
+ if(EqualSid(s, u->sid))
+ break;
+ qunlock(&users.lk);
+
+ if(u != 0)
+ return u;
+
+ naname = sizeof(aname);
+ ndname = sizeof(dname);
+
+ if(!LookupAccountSidW(srv, s, aname, &naname, dname, &ndname, &type))
+ return mkuser(s, SidTypeUnknown, L"unknown", L"unknown");
+ return mkuser(s, type, aname, dname);
+}
+
+static User*
+domnametouser(Rune16 *srv, Rune16 *name, Rune16 *dom)
+{
+ User *u;
+
+ qlock(&users.lk);
+ for(u = users.u; u != 0; u = u->next)
+ if(runes16cmp(name, u->name) == 0 && runes16cmp(dom, u->dom) == 0)
+ break;
+ qunlock(&users.lk);
+ if(u == 0)
+ u = nametouser(srv, name);
+ return u;
+}
+
+static User*
+nametouser(Rune16 *srv, Rune16 *name)
+{
+ char sidrock[MAX_SID];
+ SID *sid;
+ SID_NAME_USE type;
+ Rune16 dom[MAX_PATH];
+ DWORD nsid, ndom;
+
+ sid = (SID*)sidrock;
+ nsid = sizeof(sidrock);
+ ndom = sizeof(dom);
+ if(!LookupAccountNameW(srv, name, sid, &nsid, dom, &ndom, &type))
+ return nil;
+
+ return mkuser(sid, type, name, dom);
+}
+
+/*
+ * this mapping could be cached
+ */
+static User*
+unametouser(Rune16 *srv, char *name)
+{
+ Rune16 rname[MAX_PATH];
+
+ utftorunes16(rname, name, MAX_PATH);
+ return nametouser(srv, rname);
+}
+
+/*
+ * make a user structure and add it to the global cache.
+ */
+static User*
+mkuser(SID *sid, int type, Rune16 *name, Rune16 *dom)
+{
+ User *u;
+
+ qlock(&users.lk);
+ for(u = users.u; u != 0; u = u->next){
+ if(EqualSid(sid, u->sid)){
+ qunlock(&users.lk);
+ return u;
+ }
+ }
+
+ switch(type) {
+ default:
+ break;
+ case SidTypeDeletedAccount:
+ name = L"deleted";
+ break;
+ case SidTypeInvalid:
+ name = L"invalid";
+ break;
+ case SidTypeUnknown:
+ name = L"unknown";
+ break;
+ }
+
+ u = malloc(sizeof(User));
+ if(u == nil){
+ qunlock(&users.lk);
+ return 0;
+ }
+ u->next = nil;
+ u->group = nil;
+ u->sid = dupsid(sid);
+ u->type = type;
+ u->name = nil;
+ if(name != nil)
+ u->name = runes16dup(name);
+ u->dom = nil;
+ if(dom != nil)
+ u->dom = runes16dup(dom);
+
+ u->next = users.u;
+ users.u = u;
+
+ qunlock(&users.lk);
+ return u;
+}
+
+/*
+ * check if u is a member of gsid,
+ * which might be a group.
+ */
+static int
+ismembersid(Rune16 *srv, User *u, SID *gsid)
+{
+ User *g;
+
+ if(EqualSid(u->sid, gsid))
+ return 1;
+
+ g = sidtouser(srv, gsid);
+ if(g == 0)
+ return 0;
+ return ismember(u, g);
+}
+
+static int
+ismember(User *u, User *g)
+{
+ Gmem *grps;
+
+ if(EqualSid(u->sid, g->sid))
+ return 1;
+
+ if(EqualSid(g->sid, everyone))
+ return 1;
+
+ qlock(&u->lk);
+ addgroups(u, 0);
+ for(grps = u->group; grps != 0; grps = grps->next){
+ if(EqualSid(grps->user->sid, g->sid)){
+ qunlock(&u->lk);
+ return 1;
+ }
+ }
+ qunlock(&u->lk);
+ return 0;
+}
+
+/*
+ * find out what groups a user belongs to.
+ * if force, throw out the old info and do it again.
+ *
+ * note that a global group is also know as a group,
+ * and a local group is also know as an alias.
+ * global groups can only contain users.
+ * local groups can contain global groups or users.
+ * this code finds all global groups to which a user belongs,
+ * and all the local groups to which the user or a global group
+ * containing the user belongs.
+ */
+static void
+addgroups(User *u, int force)
+{
+ LOCALGROUP_USERS_INFO_0 *loc;
+ GROUP_USERS_INFO_0 *grp;
+ DWORD i, n, rem;
+ User *gu;
+ Gmem *g, *next;
+ Rune16 *srv, srvrock[MAX_PATH];
+
+ if(force){
+ u->gotgroup = 0;
+ for(g = u->group; g != nil; g = next){
+ next = g->next;
+ free(g);
+ }
+ u->group = nil;
+ }
+ if(u->gotgroup)
+ return;
+ u->gotgroup = 1;
+
+ n = 0;
+ srv = domsrv(u->dom, srvrock);
+ i = NetUserGetGroups(srv, u->name, 0,
+ (BYTE**)&grp, MAX_PREFERRED_LENGTH, &n, &rem);
+ if(i == NERR_Success || i == ERROR_MORE_DATA){
+ for(i = 0; i < n; i++){
+ gu = domnametouser(srv, grp[i].grui0_name, u->dom);
+ if(gu == 0)
+ continue;
+ g = malloc(sizeof(Gmem));
+ if(g == nil)
+ error(Enomem);
+ g->user = gu;
+ g->next = u->group;
+ u->group = g;
+ }
+ NetApiBufferFree(grp);
+ }
+
+ n = 0;
+ i = NetUserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT,
+ (BYTE**)&loc, MAX_PREFERRED_LENGTH, &n, &rem);
+ if(i == NERR_Success || i == ERROR_MORE_DATA){
+ for(i = 0; i < n; i++){
+ gu = domnametouser(srv, loc[i].lgrui0_name, u->dom);
+ if(gu == NULL)
+ continue;
+ g = malloc(sizeof(Gmem));
+ if(g == nil)
+ error(Enomem);
+ g->user = gu;
+ g->next = u->group;
+ u->group = g;
+ }
+ NetApiBufferFree(loc);
+ }
+}
+
+static SID*
+dupsid(SID *sid)
+{
+ SID *nsid;
+ int n;
+
+ n = GetLengthSid(sid);
+ nsid = malloc(n);
+ if(nsid == nil || !CopySid(n, nsid, sid))
+ panic("can't copy sid");
+ return nsid;
+}
+
+/*
+ * return the name of the server machine for file
+ */
+static Rune16*
+filesrv(char *file)
+{
+ int n;
+ Rune16 *srv;
+ char *p, uni[MAX_PATH], mfile[MAX_PATH];
+ wchar_t vol[3];
+
+ strcpy(mfile, file);
+ /* assume file is a fully qualified name - X: or \\server */
+ if(file[1] == ':') {
+ vol[0] = file[0];
+ vol[1] = file[1];
+ vol[2] = 0;
+ if(GetDriveType(vol) != DRIVE_REMOTE)
+ return 0;
+ n = sizeof(uni);
+ if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR)
+ return nil;
+ runes16toutf(mfile, ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName, MAX_PATH);
+ file = mfile;
+ }
+ file += 2;
+ p = strchr(file, '\\');
+ if(p == 0)
+ n = strlen(file);
+ else
+ n = p - file;
+ if(n >= MAX_PATH)
+ n = MAX_PATH-1;
+
+ memmove(uni, file, n);
+ uni[n] = '\0';
+
+ srv = malloc((n + 1) * sizeof(Rune16));
+ if(srv == nil)
+ panic("filesrv: no memory");
+ utftorunes16(srv, uni, n+1);
+ return srv;
+}
+
+/*
+ * does the file system support acls?
+ */
+static int
+fsacls(char *file)
+{
+ char *p;
+ DWORD flags;
+ char path[MAX_PATH];
+ wchar_t wpath[MAX_PATH];
+
+ /* assume file is a fully qualified name - X: or \\server */
+ if(file[1] == ':') {
+ path[0] = file[0];
+ path[1] = file[1];
+ path[2] = '\\';
+ path[3] = 0;
+ } else {
+ strcpy(path, file);
+ p = strchr(path+2, '\\');
+ if(p == 0)
+ return 0;
+ p = strchr(p+1, '\\');
+ if(p == 0)
+ strcat(path, "\\");
+ else
+ p[1] = 0;
+ }
+ utftorunes16(wpath, path, MAX_PATH);
+ if(!GetVolumeInformation(wpath, NULL, 0, NULL, NULL, &flags, NULL, 0))
+ return 0;
+
+ return flags & FS_PERSISTENT_ACLS;
+}
+
+/*
+ * given a domain, find out the server to ask about its users.
+ * we just ask the local machine to do the translation,
+ * so it might fail sometimes. in those cases, we don't
+ * trust the domain anyway, and vice versa, so it's not
+ * clear what benifit we would gain by getting the answer "right".
+ */
+static Rune16*
+domsrv(Rune16 *dom, Rune16 srv[MAX_PATH])
+{
+ Rune16 *psrv;
+ int n, r;
+
+ if(dom[0] == 0)
+ return nil;
+
+ r = NetGetAnyDCName(NULL, dom, (LPBYTE*)&psrv);
+ if(r == NERR_Success) {
+ n = runes16len(psrv);
+ if(n >= MAX_PATH)
+ n = MAX_PATH-1;
+ memmove(srv, psrv, n*sizeof(Rune16));
+ srv[n] = 0;
+ NetApiBufferFree(psrv);
+ return srv;
+ }
+
+ return nil;
+}
+
+Dev fsdevtab = {
+ 'U',
+ "fs",
+
+ fsinit,
+ fsattach,
+ fswalk,
+ fsstat,
+ fsopen,
+ fscreate,
+ fsclose,
+ fsread,
+ devbread,
+ fswrite,
+ devbwrite,
+ fsremove,
+ fswstat
+};
--- /dev/null
+++ b/emu/Nt/emu
@@ -1,0 +1,112 @@
+env
+ LDFLAGS= -subsystem:console -entry:WinMainCRTStartup $LDFLAGS
+
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win
+
+ ip ipif ipaux
+ eia
+ audio audio
+ mem
+ arch
+ pointer
+ snarf
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+ 9
+
+link
+
+mod
+ sys
+ draw
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Nt/fp.c
@@ -1,0 +1,102 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#include <float.h>
+#include "mathi.h"
+
+#define NANEXP (2047<<20)
+#define NANMASK (2047<<20)
+#define NANSIGN (1<<31)
+
+int isInf(double, int);
+
+double
+NaN(void)
+{
+ union
+ {
+ double d;
+ long x[2];
+ } a;
+
+ a.x[1] = NANEXP;
+ a.x[0] = 1;
+ return a.d;
+}
+
+int
+isNaN(double d)
+{
+ union
+ {
+ double d;
+ long x[2];
+ } a;
+
+ a.d = d;
+ if((a.x[1] & NANMASK) != NANEXP)
+ return 0;
+ return !isInf(d, 0);
+}
+
+double
+Inf(int sign)
+{
+ union
+ {
+ double d;
+ long x[2];
+ } a;
+
+ a.x[1] = NANEXP;
+ a.x[0] = 0;
+ if(sign < 0)
+ a.x[1] |= NANSIGN;
+ return a.d;
+}
+
+int
+isInf(double d, int sign)
+{
+ union
+ {
+ double d;
+ long x[2];
+ } a;
+
+ a.d = d;
+ if(a.x[0] != 0)
+ return 0;
+ if(a.x[1] == NANEXP)
+ return sign >= 0;
+ if(a.x[1] == (NANEXP|NANSIGN))
+ return sign <= 0;
+ return 0;
+}
+
+ulong
+getfcr(void)
+{
+ return getFPcontrol();
+}
+
+void
+setfcr(ulong m)
+{
+ FPcontrol(m, ~0);
+}
+
+ulong
+getfsr(void)
+{
+ return getFPstatus();
+}
+
+void
+setfsr(ulong m)
+{
+ FPstatus(m, ~0);
+}
--- /dev/null
+++ b/emu/Nt/ie
@@ -1,0 +1,109 @@
+env
+ LDFLAGS= -subsystem:windows $LDFLAGS
+ OSX= ie-os
+
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ indir
+
+ draw ie-win
+
+ ip ipif ipaux
+ eia
+ audio audio
+ mem
+ pointer
+# arch
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+ 9
+
+link
+
+mod
+ sys
+ draw
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /root /
+ /root/dev /
+ /root/fd /
+ /root/prog /
+ /root/prof /
+ /root/net /
+ /root/net.alt /
+ /root/chan /
+ /root/nvfs /
+ /root/env /
+ /root/dis /
+ /root/dis/lib /
+ /root/dis/install /
+ /root/dis/install/archfs.dis /dis/install/archfs.dis
+ /root/dis/install/arch.dis /dis/install/arch.dis
+ /root/dis/gunzip.dis /dis/gunzip.dis
+ /root/dis/lib/bufio.dis /dis/lib/bufio.dis
+ /root/dis/lib/inflate.dis /dis/lib/inflate.dis
+ /root/dis/lib/string.dis /dis/lib/string.dis
+ /root/dis/lib/daytime.dis /dis/lib/daytime.dis
+ /root/dis/lib/arg.dis /dis/lib/arg.dis
+ /root/dis/lib/styx.dis /dis/lib/styx.dis
--- /dev/null
+++ b/emu/Nt/ie-os.c
@@ -1,0 +1,791 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+#undef Unknown
+#include <excpt.h>
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "r16.h"
+#include "ieplugin.h"
+
+extern int SYS_SLEEP = 2;
+extern int SOCK_SELECT = 3;
+#define MAXSLEEPERS 1500
+
+extern Plugin* plugin;
+extern void newiop();
+extern int sendiop();
+
+DWORD PlatformId;
+static char* path;
+static HANDLE kbdh = INVALID_HANDLE_VALUE;
+static HANDLE conh = INVALID_HANDLE_VALUE;
+static int sleepers;
+
+static ulong erendezvous(void*, ulong);
+
+__declspec(thread) Proc *up;
+
+HANDLE ntfd2h(int);
+int nth2fd(HANDLE);
+char *hosttype = "Nt";
+
+static void
+pfree(Proc *p)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ e = p->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(e->user);
+ free(p->prog);
+ free(p);
+}
+
+void
+osblock(void)
+{
+ erendezvous(up, 0);
+}
+
+void
+osready(Proc *p)
+{
+ erendezvous(p, 0);
+}
+
+
+void
+pexit(char *msg, int t)
+{
+ pfree(up);
+ ExitThread(0);
+}
+
+LONG TrapHandler(LPEXCEPTION_POINTERS ureg);
+
+__cdecl
+Exhandler(EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, void *dcon)
+{
+ EXCEPTION_POINTERS ep;
+ ep.ExceptionRecord = rec;
+ ep.ContextRecord = context;
+ TrapHandler(&ep);
+ return ExceptionContinueExecution;
+}
+
+DWORD WINAPI
+tramp(LPVOID p)
+{
+ // install our own exception handler
+ // replacing all others installed on this thread
+ DWORD handler = (DWORD)Exhandler;
+ _asm {
+ mov eax,handler
+ push eax
+ mov eax,-1
+ push eax
+ mov fs:[0],esp
+ }
+
+ up = p;
+ up->func(up->arg);
+ pexit("", 0);
+ // should never get here but tidy up anyway
+ _asm {
+ mov fs:[0],-1
+ add esp, 8
+ }
+ return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ DWORD h;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+
+ p = newproc();
+ if(p == nil)
+ panic("out of kernel processes");
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->ui = up->env->ui;
+ kstrdup(&p->env->user, up->env->user);
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ p->pid = (int)CreateThread(0, 16384, tramp, p, 0, &h);
+ if(p->pid <= 0)
+ panic("ran out of kernel processes");
+}
+
+#if(_WIN32_WINNT >= 0x0400)
+void APIENTRY sleepintr(DWORD param)
+{
+}
+#endif
+
+void
+oshostintr(Proc *p)
+{
+ if (p->syscall == SOCK_SELECT)
+ return;
+ p->intwait = 0;
+#if(_WIN32_WINNT >= 0x0400)
+ if(p->syscall == SYS_SLEEP) {
+ QueueUserAPC(sleepintr, (HANDLE) p->pid, (DWORD) p->pid);
+ }
+#endif
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ longjmp(env, val);
+}
+
+int
+readkbd(void)
+{
+ DWORD r;
+ char buf[1];
+
+ if(ReadFile(plugin->conin, buf, sizeof(buf), &r, 0) == FALSE)
+ panic("keyboard fail");
+ if (r == 0)
+ panic("keyboard EOF");
+
+ if(buf[0] == '\r')
+ buf[0] = '\n';
+ return buf[0];
+}
+
+void
+cleanexit(int x)
+{
+ newiop();
+ IOP.op = Iquit;
+ sendiop();
+ ExitProcess(x);
+}
+
+struct ecodes {
+ DWORD code;
+ char* name;
+} ecodes[] = {
+ EXCEPTION_ACCESS_VIOLATION, "Segmentation violation",
+ EXCEPTION_DATATYPE_MISALIGNMENT, "Data Alignment",
+ EXCEPTION_BREAKPOINT, "Breakpoint",
+ EXCEPTION_SINGLE_STEP, "SingleStep",
+ EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array Bounds Check",
+ EXCEPTION_FLT_DENORMAL_OPERAND, "Denormalized Float",
+ EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating Point Divide by Zero",
+ EXCEPTION_FLT_INEXACT_RESULT, "Inexact Floating Point",
+ EXCEPTION_FLT_INVALID_OPERATION, "Invalid Floating Operation",
+ EXCEPTION_FLT_OVERFLOW, "Floating Point Result Overflow",
+ EXCEPTION_FLT_STACK_CHECK, "Floating Point Stack Check",
+ EXCEPTION_FLT_UNDERFLOW, "Floating Point Result Underflow",
+ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by Zero",
+ EXCEPTION_INT_OVERFLOW, "Integer Overflow",
+ EXCEPTION_PRIV_INSTRUCTION, "Privileged Instruction",
+ EXCEPTION_IN_PAGE_ERROR, "Page-in Error",
+ EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal Instruction",
+ EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-Continuable Exception",
+ EXCEPTION_STACK_OVERFLOW, "Stack Overflow",
+ EXCEPTION_INVALID_DISPOSITION, "Invalid Disposition",
+ EXCEPTION_GUARD_PAGE, "Guard Page Violation",
+ 0, nil
+};
+
+void
+dodisfault(void)
+{
+ disfault(nil, up->env->errstr);
+}
+
+typedef struct Ereg Ereg;
+struct Ereg {
+ Ereg *prev;
+ FARPROC handler;
+};
+
+void
+dumpex()
+{
+ Ereg *er;
+ int i;
+ _asm { mov eax,fs:[0] };
+ _asm { mov [er],eax };
+
+ i = 0;
+ while ((unsigned)er != ~0) {
+ print("handler %ux\n", er->handler);
+ i++;
+ er = er->prev;
+ }
+ print("EXCEPTION CHAIN LENGTH = %d\n", i);
+}
+
+LONG
+TrapHandler(LPEXCEPTION_POINTERS ureg)
+{
+ int i;
+ char *name;
+ DWORD code;
+ // WORD pc;
+ char buf[ERRMAX];
+
+ code = ureg->ExceptionRecord->ExceptionCode;
+ // pc = ureg->ContextRecord->Eip;
+
+ name = nil;
+ for(i = 0; i < nelem(ecodes); i++) {
+ if(ecodes[i].code == code) {
+ name = ecodes[i].name;
+ break;
+ }
+ }
+
+ if(name == nil) {
+ snprint(buf, sizeof(buf), "Unrecognized Machine Trap (%.8lux)\n", code);
+ name = buf;
+ }
+/*
+ if(pc != 0) {
+ snprint(buf, sizeof(buf), "%s: pc=0x%lux", name, pc);
+ name = buf;
+ }
+*/
+ /* YUCK! */
+ strncpy(up->env->errstr, name, ERRMAX);
+ switch (code) {
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ /* clear exception flags and register stack */
+ _asm { fnclex };
+ ureg->ContextRecord->FloatSave.StatusWord = 0x0000;
+ ureg->ContextRecord->FloatSave.TagWord = 0xffff;
+ }
+ ureg->ContextRecord->Eip = (DWORD)dodisfault;
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+static int rebootok = 0; /* is shutdown -r supported? */
+
+void
+osreboot(char *file, char **argv)
+{
+ if(rebootok){
+ execvp(file, argv);
+ panic("reboot failure");
+ }else
+ error("reboot option not supported on this system");
+}
+
+void
+libinit(char *imod)
+{
+ WSADATA wasdat;
+// DWORD lasterror, namelen;
+ OSVERSIONINFO os;
+// char sys[64];
+
+ os.dwOSVersionInfoSize = sizeof(os);
+ if(!GetVersionEx(&os))
+ panic("can't get os version");
+ PlatformId = os.dwPlatformId;
+ if (PlatformId == VER_PLATFORM_WIN32_NT) { /* true for NT and 2000 */
+ rebootok = 1;
+ } else {
+ rebootok = 0;
+ }
+
+ if((int)INVALID_HANDLE_VALUE != -1 || sizeof(HANDLE) != sizeof(int))
+ panic("invalid handle value or size");
+
+ if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0)
+ panic("no winsock.dll");
+
+// gethostname(sys, sizeof(sys));
+// kstrdup(&ossysname, sys);
+ kstrdup(&ossysname, "plugin");
+
+// if(sflag == 0)
+// SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TrapHandler);
+
+ path = getenv("PATH");
+ if(path == nil)
+ path = ".";
+
+ up = newproc();
+ if(up == nil)
+ panic("cannot create kernel process");
+
+ kstrdup(&eve, "system");
+ emuinit(imod);
+}
+
+enum
+{
+ NHLOG = 7,
+ NHASH = (1<<NHLOG)
+};
+
+typedef struct Tag Tag;
+struct Tag
+{
+ void* tag;
+ ulong val;
+ HANDLE pid;
+ Tag* next;
+};
+
+static Tag* ht[NHASH];
+static Tag* ft;
+static Lock hlock;
+static int nsema;
+
+ulong
+erendezvous(void *tag, ulong value)
+{
+ int h;
+ ulong rval;
+ Tag *t, **l, *f;
+
+
+ h = (ulong)tag & (NHASH-1);
+
+ lock(&hlock);
+ l = &ht[h];
+ for(t = ht[h]; t; t = t->next) {
+ if(t->tag == tag) {
+ rval = t->val;
+ t->val = value;
+ t->tag = 0;
+ unlock(&hlock);
+ if(SetEvent(t->pid) == FALSE)
+ panic("Release failed\n");
+ return rval;
+ }
+ }
+
+ t = ft;
+ if(t == 0) {
+ t = malloc(sizeof(Tag));
+ if(t == nil)
+ panic("rendezvous: no memory");
+ t->pid = CreateEvent(0, 0, 0, 0);
+ }
+ else
+ ft = t->next;
+
+ t->tag = tag;
+ t->val = value;
+ t->next = *l;
+ *l = t;
+ unlock(&hlock);
+
+ if(WaitForSingleObject(t->pid, INFINITE) != WAIT_OBJECT_0)
+ panic("WaitForSingleObject failed\n");
+
+ lock(&hlock);
+ rval = t->val;
+ for(f = *l; f; f = f->next) {
+ if(f == t) {
+ *l = f->next;
+ break;
+ }
+ l = &f->next;
+ }
+ t->next = ft;
+ ft = t;
+ unlock(&hlock);
+
+ return rval;
+}
+
+void
+FPsave(void *fptr)
+{
+ _asm {
+ mov eax, fptr
+ fstenv [eax]
+ }
+}
+
+void
+FPrestore(void *fptr)
+{
+ _asm {
+ mov eax, fptr
+ fldenv [eax]
+ }
+}
+
+ulong
+umult(ulong a, ulong b, ulong *high)
+{
+ ulong lo, hi;
+
+ _asm {
+ mov eax, a
+ mov ecx, b
+ MUL ecx
+ mov lo, eax
+ mov hi, edx
+ }
+ *high = hi;
+ return lo;
+}
+
+int
+close(int fd)
+{
+ if(fd != -1)
+ CloseHandle(ntfd2h(fd));
+ return 0;
+}
+
+int
+read(int fd, void *buf, uint n)
+{
+ if(!ReadFile(ntfd2h(fd), buf, n, &n, NULL))
+ return -1;
+ return n;
+}
+
+int
+write(int fd, void *buf, uint n)
+{
+ if(fd == 1 || fd == 2){
+ int w;
+ if (plugin->conout == NULL)
+ return n;
+ if (!WriteFile(plugin->conout, buf, n, &w, NULL) || n != w)
+ abort();
+ return n;
+ }
+ if(!WriteFile(ntfd2h(fd), buf, n, &n, NULL))
+ return -1;
+ return n;
+}
+
+/*
+ * map handles and fds.
+ * this code assumes sizeof(HANDLE) == sizeof(int),
+ * that INVALID_HANDLE_VALUE is -1, and assumes
+ * that all tests of invalid fds check only for -1, not < 0
+ */
+int
+nth2fd(HANDLE h)
+{
+ return (int)h;
+}
+
+HANDLE
+ntfd2h(int fd)
+{
+ return (HANDLE)fd;
+}
+
+void
+oslopri(void)
+{
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+}
+
+/* Resolve system header name conflict */
+#undef Sleep
+void
+sleep(int secs)
+{
+ Sleep(secs*1000);
+}
+
+void*
+sbrk(int size)
+{
+ void *brk;
+
+ brk = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ if(brk == 0)
+ return (void*)-1;
+
+ return brk;
+}
+
+ulong
+getcallerpc(void *arg)
+{
+ ulong cpc;
+ _asm {
+ mov eax, dword ptr [ebp]
+ mov eax, dword ptr [eax+4]
+ mov dword ptr cpc, eax
+ }
+ return cpc;
+}
+
+/*
+ulong
+getpc(void *arg, ulong *narg)
+{
+ ulong *a = arg, *fp, pc;
+
+ if(a == nil){
+ *narg = 0;
+ return 0;
+ }
+ fp = a-2;
+ pc = fp[1];
+ fp = *(ulong**)fp;
+ if(fp == nil)
+ *narg = 0;
+ else
+ *narg = (ulong)(fp+2);
+ return pc;
+}
+*/
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ return GetTickCount();
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int*
+yrsize(int yr)
+{
+ /* a leap year is a multiple of 4, excluding centuries
+ * that are not multiples of 400 */
+ if( (yr % 4 == 0) && (yr % 100 != 0 || yr % 400 == 0) )
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+static long
+tm2sec(SYSTEMTIME *tm)
+{
+ long secs;
+ int i, *d2m;
+
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ for(i = 1970; i < tm->wYear; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * seconds per month
+ */
+ d2m = yrsize(tm->wYear);
+ for(i = 1; i < tm->wMonth; i++)
+ secs += d2m[i] * SEC2DAY;
+
+ /*
+ * secs in last month
+ */
+ secs += (tm->wDay-1) * SEC2DAY;
+
+ /*
+ * hours, minutes, seconds
+ */
+ secs += tm->wHour * SEC2HOUR;
+ secs += tm->wMinute * SEC2MIN;
+ secs += tm->wSecond;
+
+ return secs;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+ SYSTEMTIME tm;
+ vlong secs;
+
+ GetSystemTime(&tm);
+ secs = tm2sec(&tm);
+ return secs * 1000000 + tm.wMilliseconds * 1000;
+}
+
+vlong
+osnsec(void)
+{
+ return osusectime()*1000; /* TO DO better */
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ up->syscall = 1;
+ SleepEx(milsec, FALSE);
+ up->syscall = 0;
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ if (sleepers > MAXSLEEPERS)
+ return -1;
+ sleepers++;
+ up->syscall = SYS_SLEEP;
+ SleepEx(milsec, TRUE);
+ up->syscall = 0;
+ sleepers--;
+ return 0;
+}
+
+void
+osyield(void)
+{
+ sleep(0);
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ sleep(1000000);
+}
+
+/*
+ * these should never be called, and are included
+ * as stubs since we are linking against a library which defines them
+ */
+int
+open(const char *path, int how, ...)
+{
+ panic("open");
+ return -1;
+}
+
+int
+creat(const char *path, int how)
+{
+ panic("creat");
+ return -1;
+}
+
+int
+stat(const char *path, struct stat *sp)
+{
+ panic("stat");
+ return -1;
+}
+
+int
+chown(const char *path, int uid, int gid)
+{
+ panic("chown");
+ return -1;
+}
+
+int
+chmod(const char *path, int mode)
+{
+ panic("chmod");
+ return -1;
+}
+
+void
+link(char *path, char *next)
+{
+ panic("link");
+}
+
+int
+segflush(void *a, ulong n)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Nt/ie-win.c
@@ -1,0 +1,224 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include "keyboard.h"
+#include "cursor.h"
+#include "ieplugin.h"
+
+/*
+ * image channel descriptors - copied from draw.h as it clashes with windows.h on many things
+ */
+enum {
+ CRed = 0,
+ CGreen,
+ CBlue,
+ CGrey,
+ CAlpha,
+ CMap,
+ CIgnore,
+ NChan,
+};
+
+#define __DC(type, nbits) ((((type)&15)<<4)|((nbits)&15))
+#define CHAN1(a,b) __DC(a,b)
+#define CHAN2(a,b,c,d) (CHAN1((a),(b))<<8|__DC((c),(d)))
+#define CHAN3(a,b,c,d,e,f) (CHAN2((a),(b),(c),(d))<<8|__DC((e),(f)))
+#define CHAN4(a,b,c,d,e,f,g,h) (CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h)))
+
+#define NBITS(c) ((c)&15)
+#define TYPE(c) (((c)>>4)&15)
+
+enum {
+ GREY1 = CHAN1(CGrey, 1),
+ GREY2 = CHAN1(CGrey, 2),
+ GREY4 = CHAN1(CGrey, 4),
+ GREY8 = CHAN1(CGrey, 8),
+ CMAP8 = CHAN1(CMap, 8),
+ RGB15 = CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5),
+ RGB16 = CHAN3(CRed, 5, CGreen, 6, CBlue, 5),
+ RGB24 = CHAN3(CRed, 8, CGreen, 8, CBlue, 8),
+ RGBA32 = CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8),
+ ARGB32 = CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8), /* stupid VGAs */
+ XRGB32 = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8),
+};
+
+extern ulong displaychan;
+
+extern void drawend(void);
+
+extern int chantodepth(ulong);
+extern int main(int argc, char **argv);
+static void dprint(char*, ...);
+static DWORD WINAPI winproc(LPVOID);
+
+static HINSTANCE inst;
+static HINSTANCE previnst;
+static int attached;
+static ulong *data;
+
+extern DWORD PlatformId;
+char* gkscanid = "emu_win32vk";
+
+extern int cflag;
+Plugin *plugin = NULL;
+
+DWORD WINAPI
+pluginproc(LPVOID p)
+{
+ int x, y, b;
+
+ for (;;) {
+ WaitForSingleObject(plugin->dopop, INFINITE);
+ switch (POP.op) {
+ case Pgfxkey:
+ if(gkbdq != nil)
+ gkbdputc(gkbdq, POP.u.key);
+ break;
+ case Pmouse:
+ x = POP.u.m.x;
+ y = POP.u.m.y;
+ b = POP.u.m.b;
+ mousetrack(b, x, y, 0);
+ break;
+ }
+ SetEvent(plugin->popdone);
+ }
+}
+
+int WINAPI
+WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow)
+{
+ HANDLE sharedmem;
+ uint pid = _getpid();
+ char iname[16];
+ inst = winst;
+ previnst = wprevinst;
+ sprint(iname, "%uX", pid);
+ sharedmem = OpenFileMapping(FILE_MAP_WRITE, FALSE, iname);
+ if (sharedmem != NULL)
+ plugin = MapViewOfFile(sharedmem, FILE_MAP_WRITE, 0, 0, 0);
+ if (plugin != NULL) {
+ DWORD tid;
+ int i;
+ Xsize = plugin->Xsize;
+ Ysize = plugin->Ysize;
+ displaychan = plugin->cdesc;
+ cflag = plugin->cflag;
+ for (i = 0; i < PI_NCLOSE; i++)
+ CloseHandle(plugin->closehandles[i]);
+ CreateThread(0, 0, pluginproc, 0, 0, &tid);
+
+ /* cmdline passed into WinMain does not contain name of executable.
+ * The globals __argc and __argv to include this info - like UNIX
+ */
+ main(__argc, __argv);
+ UnmapViewOfFile(plugin);
+ plugin = NULL;
+ }
+ if (sharedmem != NULL)
+ CloseHandle(sharedmem);
+ return 0;
+}
+
+static Lock ioplock;
+
+void
+newiop()
+{
+ lock(&ioplock);
+}
+
+int
+sendiop()
+{
+ int val;
+ SetEvent(plugin->doiop);
+ WaitForSingleObject(plugin->iopdone, INFINITE);
+ val = plugin->iop.val;
+ unlock(&ioplock);
+ return val;
+}
+
+void
+dprint(char *fmt, ...)
+{
+ va_list arg;
+ char buf[128];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg);
+ va_end(arg);
+ OutputDebugString("inferno: ");
+ OutputDebugString(buf);
+}
+
+uchar*
+attachscreen(IRectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int k;
+
+ if (!attached) {
+ newiop();
+ IOP.op = Iattachscr;
+ if (sendiop() != 0)
+ return nil;
+ data = plugin->screen;
+ attached = 1;
+ }
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+
+ if(displaychan == 0)
+ displaychan = CMAP8;
+ *chan = displaychan;
+
+ k = chantodepth(displaychan);
+ *d = k;
+ *width = (Xsize/4)*(k/8);
+ *softscreen = 1;
+ return (uchar*)data;
+}
+
+void
+flushmemscreen(IRectangle r)
+{
+ if(r.max.x<=r.min.x || r.max.y<=r.min.y)
+ return;
+ newiop();
+ IOP.op = Iflushscr;
+ IOP.u.r = r;
+ sendiop();
+}
+
+void
+setpointer(int x, int y)
+{
+ USED(x); USED(y);
+ // TODO
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ USED(c);
+ // TODO
+}
+
+char*
+clipread(void)
+{
+ return nil;
+}
+
+int
+clipwrite(char *p)
+{
+ USED(p);
+ return -1;
+}
--- /dev/null
+++ b/emu/Nt/ieplugin.h
@@ -1,0 +1,75 @@
+typedef struct Pop Pop;
+typedef struct Iop Iop;
+typedef struct IPoint IPoint;
+typedef struct IRectangle IRectangle;
+typedef struct Plugin Plugin;
+
+enum {
+ // messages from Plugin to Inferno
+ Pgfxkey,
+ Pmouse,
+
+ // message from Inferno to Plugin
+ Iattachscr,
+ Iflushscr,
+ Isetcur,
+ Idrawcur,
+ Iquit,
+};
+
+struct Pop {
+ int op;
+ union {
+ int key; // Pgfxkey
+ struct {
+ int x;
+ int y;
+ int b;
+ int modify;
+ } m; // Pmouse
+ } u;
+};
+
+struct IPoint
+{
+ LONG x;
+ LONG y;
+};
+
+struct IRectangle
+{
+ IPoint min;
+ IPoint max;
+};
+
+struct Iop {
+ int op;
+ int val;
+ union {
+ IRectangle r; // Iflushscr
+ // need additional support for Isetcur & Idrawcur
+ } u;
+};
+#define PI_NCLOSE 2
+
+struct Plugin {
+ LONG sz; // size of this data struct (including screen mem)
+ HANDLE conin; // console input (from plugin) - never NULL
+ HANDLE conout; // console output (to plugin) - can be NULL
+ HANDLE datain; // #C data file for initialisation (HACK!)
+ HANDLE dopop; // new Pop available
+ HANDLE popdone; // acknowledgement of Pop
+ HANDLE doiop; // new Iop available
+ HANDLE iopdone; // acknowledgement of Iop
+ HANDLE closehandles[PI_NCLOSE];
+ Pop pop;
+ Iop iop;
+ int Xsize; // screen dimensions
+ int Ysize;
+ ULONG cdesc; // display chans descriptor
+ int cflag;
+ ULONG screen[1];
+};
+
+#define IOP (plugin->iop)
+#define POP (plugin->pop)
--- /dev/null
+++ b/emu/Nt/ipif.c
@@ -1,0 +1,409 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#include <winbase.h>
+#include <sys/types.h>
+#include <winsock.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+#include "error.h"
+
+typedef int socklen_t; /* Windows is leading edge as always */
+
+
+extern int SOCK_SELECT;
+
+char Enotv4[] = "address not IPv4";
+
+static void
+ipw6(uchar *a, ulong w)
+{
+ memmove(a, v4prefix, IPv4off);
+ memmove(a+IPv4off, &w, IPv4addrlen);
+}
+
+int
+so_socket(int type)
+{
+ int fd, one;
+
+ switch(type) {
+ default:
+ error("bad protocol type");
+ case S_TCP:
+ type = SOCK_STREAM;
+ break;
+ case S_UDP:
+ type = SOCK_DGRAM;
+ break;
+ }
+ fd = socket(AF_INET, type, 0);
+ if(fd < 0)
+ oserror();
+ if(type == SOCK_DGRAM){
+ one = 1;
+ setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+ }else{
+ one = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+ }
+ return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+ uchar *h = hdr;
+
+
+ osenter();
+ if(hdr == 0)
+ r = send(sock, va, len, 0);
+ else {
+ memset(&sa, 0, sizeof(sa));
+ sin = (struct sockaddr_in*)&sa;
+ sin->sin_family = AF_INET;
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(&sin->sin_addr, h, 4);
+ memmove(&sin->sin_port, h+8, 2);
+ break;
+ case OUdphdrlen:
+ v6tov4((uchar*)&sin->sin_addr, h);
+ memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */
+ break;
+ default:
+ v6tov4((uchar*)&sin->sin_addr, h);
+ memmove(&sin->sin_port, h+3*IPaddrlen, 2);
+ break;
+ }
+ r = sendto(sock, va, len, 0, &sa, sizeof(sa));
+ }
+ osleave();
+ return r;
+}
+
+static int
+doselect(int sock)
+{
+ fd_set waitr;
+ struct timeval seltime;
+
+ up->syscall = SOCK_SELECT;
+ FD_ZERO(&waitr);
+ FD_SET(sock, &waitr);
+ for(;;){
+ int nfds;
+ fd_set in, exc;
+
+ in = waitr;
+ exc = waitr;
+ seltime.tv_sec = 1;
+ seltime.tv_usec = 0L;
+ nfds = select(sizeof(fd_set)*8, &in, (fd_set*)0, &exc, &seltime);
+ if(up->intwait) {
+ up->intwait = 0;
+ return -1;
+ }
+ if(nfds < 0) {
+ print("select error\n");
+ return 0;
+ }
+ if(FD_ISSET(sock, &in) || FD_ISSET(sock, &exc)){
+ return 0;
+ }
+ }
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ socklen_t l;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+ uchar h[Udphdrlen];
+
+ osenter();
+ if(doselect(sock) < 0) {
+ osleave();
+ return -1;
+ }
+ if(hdr == 0)
+ r = recv(sock, va, len, 0);
+ else {
+ sin = (struct sockaddr_in*)&sa;
+ l = sizeof(sa);
+ r = recvfrom(sock, va, len, 0, &sa, &l);
+ if(r >= 0) {
+ memset(h, 0, sizeof(h));
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(h, &sin->sin_addr, IPv4addrlen);
+ memmove(h+2*IPv4addrlen, &sin->sin_port, 2);
+ break;
+ case OUdphdrlen:
+ v4tov6(h, (uchar*)&sin->sin_addr);
+ memmove(h+2*IPaddrlen, &sin->sin_port, 2);
+ break;
+ default:
+ v4tov6(h, (uchar*)&sin->sin_addr);
+ memmove(h+3*IPaddrlen, &sin->sin_port, 2);
+ break;
+ }
+
+ /* alas there's no way to get the local addr/port correctly. Pretend. */
+ memset(&sa, 0, sizeof(sa));
+ getsockname(sock, &sa, &l);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen);
+ memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2);
+ break;
+ case OUdphdrlen:
+ v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+ memmove(h+2*IPaddrlen+2, &sin->sin_port, 2);
+ break;
+ default:
+ v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+ v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */
+ memmove(h+3*IPaddrlen+2, &sin->sin_port, 2);
+ break;
+ }
+ memmove(hdr, h, hdrlen);
+ }
+ }
+ osleave();
+ return r;
+}
+
+void
+so_close(int sock)
+{
+ closesocket(sock);
+}
+
+void
+so_connect(int fd, uchar *raddr, ushort rport)
+{
+ int r;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ if(!isv4(raddr))
+ error(Enotv4);
+
+ memset(&sa, 0, sizeof(sa));
+ sin = (struct sockaddr_in*)&sa;
+ sin->sin_family = AF_INET;
+ hnputs(&sin->sin_port, rport);
+ memmove(&sin->sin_addr.s_addr, raddr+IPv4off, IPv4addrlen);
+
+ osenter();
+ r = connect(fd, &sa, sizeof(sa));
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+void
+so_getsockname(int fd, uchar *laddr, ushort *lport)
+{
+ socklen_t len;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ len = sizeof(sa);
+ if(getsockname(fd, &sa, &len) < 0)
+ oserror();
+
+ sin = (struct sockaddr_in*)&sa;
+ if(sin->sin_family != AF_INET || len != sizeof(*sin))
+ error(Enotv4);
+
+ ipw6(laddr, sin->sin_addr.s_addr);
+ *lport = nhgets(&sin->sin_port);
+}
+
+void
+so_listen(int fd)
+{
+ int r;
+
+ osenter();
+ r = listen(fd, 256);
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+int
+so_accept(int fd, uchar *raddr, ushort *rport)
+{
+ int nfd;
+ socklen_t len;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in*)&sa;
+
+ len = sizeof(sa);
+ osenter();
+ if(doselect(fd) < 0) {
+ osleave();
+ return -1;
+ }
+ nfd = accept(fd, &sa, &len);
+ osleave();
+ if(nfd < 0)
+ oserror();
+
+ if(sin->sin_family != AF_INET || len != sizeof(*sin))
+ error(Enotv4);
+
+ ipw6(raddr, sin->sin_addr.s_addr);
+ *rport = nhgets(&sin->sin_port);
+ return nfd;
+}
+
+void
+so_bind(int fd, int su, uchar *addr, ushort port)
+{
+ int i, one;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in*)&sa;
+
+ one = 1;
+// if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+// oserrstr(up->genbuf, sizeof(up->genbuf));
+// print("setsockopt: %s", err);
+// }
+
+ if(su) {
+ for(i = 600; i < 1024; i++) {
+ memset(&sa, 0, sizeof(sa));
+ sin->sin_family = AF_INET;
+ memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+ hnputs(&sin->sin_port, i);
+
+ if(bind(fd, &sa, sizeof(sa)) >= 0)
+ return;
+ }
+ oserror();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sin->sin_family = AF_INET;
+ memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+ hnputs(&sin->sin_port, port);
+
+ if(bind(fd, &sa, sizeof(sa)) < 0)
+ oserror();
+}
+
+int
+so_gethostbyname(char *host, char**hostv, int n)
+{
+ int i;
+ char buf[32];
+ uchar *p;
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if(hp == 0)
+ return 0;
+
+ for(i = 0; hp->h_addr_list[i] && i < n; i++) {
+ p = (uchar*)hp->h_addr_list[i];
+ sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
+ hostv[i] = strdup(buf);
+ if(hostv[i] == 0)
+ break;
+ }
+ return i;
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+ int i;
+ struct hostent *hp;
+ unsigned long straddr;
+
+ straddr = inet_addr(addr);
+ if(straddr == -1)
+ return 0;
+
+ hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET);
+ if(hp == 0)
+ return 0;
+
+ hostv[0] = strdup(hp->h_name);
+ if(hostv[0] == 0)
+ return 0;
+ for(i = 1; hp->h_aliases[i-1] && i < n; i++) {
+ hostv[i] = strdup(hp->h_aliases[i-1]);
+ if(hostv[i] == 0)
+ break;
+ }
+ return i;
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+ ushort p;
+ struct servent *s;
+
+ s = getservbyname(service, net);
+ if(s == 0)
+ return -1;
+ p = s->s_port;
+ sprint(port, "%d", nhgets(&p));
+ return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+ int r;
+ static struct linger l = {1, 0};
+
+ osenter();
+ if(nolinger)
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+ r = closesocket(fd);
+ osleave();
+ return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+ error("arp not implemented");
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+ USED(restricted);
+ USED(port);
+ /* Windows requires bound sockets, even on port 0 */
+ return 1;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+ int on;
+
+ USED(ms);
+ on = 1;
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+}
--- /dev/null
+++ b/emu/Nt/ipif6.c
@@ -1,0 +1,413 @@
+#define Unknown win_Unknown
+
+/* vista (0x0600) was the first to support IPPROTO_IPV6 */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+
+#include <winsock2.h>
+#include <ws2def.h>
+#include <ws2tcpip.h>
+#include <wspiapi.h>
+#undef Unknown
+
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+#include "error.h"
+
+extern int SOCK_SELECT;
+
+int
+so_socket(int type)
+{
+ int fd, one;
+ int v6only;
+
+ switch(type) {
+ default:
+ error("bad protocol type");
+ case S_TCP:
+ type = SOCK_STREAM;
+ break;
+ case S_UDP:
+ type = SOCK_DGRAM;
+ break;
+ }
+
+ fd = socket(AF_INET6, type, 0);
+ if(fd < 0)
+ oserror();
+
+ /* OpenBSD has v6only fixed to 1, so the following will fail */
+ v6only = 0;
+ if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only, sizeof v6only) < 0)
+ oserror();
+
+ if(type == SOCK_DGRAM){
+ one = 1;
+ setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+ }else{
+ one = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+ }
+ return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+ char *h = hdr;
+
+ osenter();
+ if(hdr == 0)
+ r = send(sock, va, len, 0);
+ else {
+ memset(&sa, 0, sizeof(sa));
+ sin6 = (struct sockaddr_in6*)&sa;
+ sin6->sin6_family = AF_INET6;
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ v4tov6((uchar*)&sin6->sin6_addr, h);
+ memmove(&sin6->sin6_port, h+8, 2);
+ break;
+ case OUdphdrlen:
+ memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+ memmove(&sin6->sin6_port, h+2*IPaddrlen, 2); /* rport */
+ break;
+ default:
+ memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+ memmove(&sin6->sin6_port, h+3*IPaddrlen, 2);
+ break;
+ }
+ r = sendto(sock, va, len, 0, (struct sockaddr*)sin6, sizeof(*sin6));
+ }
+ osleave();
+ return r;
+}
+
+static int
+doselect(int sock)
+{
+ fd_set waitr;
+ struct timeval seltime;
+
+ up->syscall = SOCK_SELECT;
+ FD_ZERO(&waitr);
+ FD_SET(sock, &waitr);
+ for(;;){
+ int nfds;
+ fd_set in, exc;
+
+ in = waitr;
+ exc = waitr;
+ seltime.tv_sec = 1;
+ seltime.tv_usec = 0L;
+ nfds = select(sizeof(fd_set)*8, &in, (fd_set*)0, &exc, &seltime);
+ if(up->intwait) {
+ up->intwait = 0;
+ return -1;
+ }
+ if(nfds < 0) {
+ print("select error\n");
+ return 0;
+ }
+ if(FD_ISSET(sock, &in) || FD_ISSET(sock, &exc)){
+ return 0;
+ }
+ }
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r, l;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+ char h[Udphdrlen];
+
+ osenter();
+ if(doselect(sock) < 0) {
+ osleave();
+ return -1;
+ }
+ if(hdr == 0)
+ r = recv(sock, va, len, 0);
+ else {
+ sin6 = (struct sockaddr_in6*)&sa;
+ l = sizeof(sa);
+ r = recvfrom(sock, va, len, 0, (struct sockaddr*)&sa, &l);
+ if(r >= 0) {
+ memset(h, 0, sizeof h);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ if(v6tov4(h, (uchar*)&sin6->sin6_addr) < 0) {
+ osleave();
+ error("OUdphdrlenv4 with IPv6 address");
+ }
+ memmove(h+2*IPv4addrlen, &sin6->sin6_port, 2);
+ break;
+ case OUdphdrlen:
+ memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen, &sin6->sin6_port, 2);
+ break;
+ default:
+ memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+3*IPaddrlen, &sin6->sin6_port, 2);
+ break;
+ }
+
+ /* alas there's no way to get the local addr/port correctly. Pretend. */
+ memset(&sa, 0, sizeof sa);
+ l = sizeof(sa);
+ getsockname(sock, (struct sockaddr*)&sa, &l);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ /*
+ * we get v6Unspecified/noaddr if local address cannot be determined.
+ * that's reasonable for ipv4 too.
+ */
+ if(ipcmp(v6Unspecified, (uchar*)&sin6->sin6_addr) != 0
+ && v6tov4(h+IPv4addrlen, (uchar*)&sin6->sin6_addr) < 0) {
+ osleave();
+ error("OUdphdrlenv4 with IPv6 address");
+ }
+ memmove(h+2*IPv4addrlen+2, &sin6->sin6_port, 2);
+ break;
+ case OUdphdrlen:
+ memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen+2, &sin6->sin6_port, 2);
+ break;
+ default:
+ memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen); /* ifcaddr */
+ memmove(h+3*IPaddrlen+2, &sin6->sin6_port, 2);
+ break;
+ }
+ memmove(hdr, h, hdrlen);
+ }
+ }
+ osleave();
+ return r;
+}
+
+void
+so_close(int sock)
+{
+ closesocket(sock);
+}
+
+void
+so_connect(int fd, unsigned char *raddr, unsigned short rport)
+{
+ int r;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ memset(&sa, 0, sizeof(sa));
+ sin6 = (struct sockaddr_in6*)&sa;
+ sin6->sin6_family = AF_INET6;
+ hnputs(&sin6->sin6_port, rport);
+ memmove((uchar*)&sin6->sin6_addr, raddr, IPaddrlen);
+
+ osenter();
+ r = connect(fd, (struct sockaddr*)sin6, sizeof(*sin6));
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+void
+so_getsockname(int fd, unsigned char *laddr, unsigned short *lport)
+{
+ int len;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ len = sizeof(*sin6);
+ if(getsockname(fd, (struct sockaddr*)&sa, &len) < 0)
+ oserror();
+
+ sin6 = (struct sockaddr_in6*)&sa;
+ if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+ error("not AF_INET6");
+
+ memmove(laddr, &sin6->sin6_addr, IPaddrlen);
+ *lport = nhgets(&sin6->sin6_port);
+}
+
+void
+so_listen(int fd)
+{
+ int r;
+
+ osenter();
+ r = listen(fd, 256);
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+int
+so_accept(int fd, unsigned char *raddr, unsigned short *rport)
+{
+ int nfd, len;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6*)&sa;
+
+ len = sizeof(*sin6);
+ osenter();
+ if(doselect(fd) < 0) {
+ osleave();
+ return -1;
+ }
+ nfd = accept(fd, (struct sockaddr*)&sa, &len);
+ osleave();
+ if(nfd < 0)
+ oserror();
+
+ if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+ error("not AF_INET6");
+
+ memmove(raddr, &sin6->sin6_addr, IPaddrlen);
+ *rport = nhgets(&sin6->sin6_port);
+ return nfd;
+}
+
+void
+so_bind(int fd, int su, unsigned char *addr, unsigned short port)
+{
+ int i, one;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6*)&sa;
+
+ one = 1;
+ if(0 && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ print("setsockopt: %s", up->genbuf);
+ }
+
+ if(su) {
+ for(i = 600; i < 1024; i++) {
+ memset(&sa, 0, sizeof(sa));
+ sin6->sin6_family = AF_INET6;
+ memmove(&sin6->sin6_addr, addr, IPaddrlen);
+ hnputs(&sin6->sin6_port, i);
+
+ if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) >= 0)
+ return;
+ }
+ oserror();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sin6->sin6_family = AF_INET6;
+ memmove(&sin6->sin6_addr, addr, IPaddrlen);
+ hnputs(&sin6->sin6_port, port);
+
+ if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) < 0)
+ oserror();
+}
+
+static int
+resolve(char *name, char **hostv, int n, int isnumeric)
+{
+ int i;
+ struct addrinfo *res0, *r;
+ char buf[5*8];
+ uchar addr[IPaddrlen];
+ struct addrinfo hints;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = isnumeric ? AI_NUMERICHOST : 0;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if(getaddrinfo(name, nil, &hints, &res0) < 0)
+ return 0;
+ i = 0;
+ for(r = res0; r != nil && i < n; r = r->ai_next) {
+ if(r->ai_family == AF_INET)
+ v4tov6(addr, (uchar*)&((struct sockaddr_in*)r->ai_addr)->sin_addr);
+ else if(r->ai_family == AF_INET6)
+ memmove(addr, &((struct sockaddr_in6*)r->ai_addr)->sin6_addr, IPaddrlen);
+ else
+ continue;
+
+ snprint(buf, sizeof buf, "%I", addr);
+ hostv[i++] = strdup(buf);
+ }
+
+ freeaddrinfo(res0);
+ return i;
+}
+
+int
+so_gethostbyname(char *host, char **hostv, int n)
+{
+ return resolve(host, hostv, n, 0);
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+ return resolve(addr, hostv, n, 1);
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+ ushort p;
+ struct servent *s;
+
+ s = getservbyname(service, net);
+ if(s == 0)
+ return -1;
+ p = s->s_port;
+ sprint(port, "%d", nhgets(&p));
+ return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+ int r;
+ static struct linger l = {1, 0};
+
+ osenter();
+ if(nolinger)
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+ r = closesocket(fd);
+ osleave();
+ return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+ error("arp not implemented");
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+ USED(restricted);
+ USED(port);
+ /* Windows requires bound sockets, even on port 0 */
+ return 1;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+ int on;
+
+ USED(ms);
+ on = 1;
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+}
--- /dev/null
+++ b/emu/Nt/mkfile
@@ -1,0 +1,51 @@
+SYSTARG=Nt
+OBJTYPE=386
+#uncomment following line for full Microsoft debug symbols
+#LDEBUG=-debug -debugtype:cv -pdb:none
+<../../mkconfig
+SYSTARG=Nt
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu ie
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+OSX=os
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ $OSX.$O\
+ $CONF.root.$O\
+ lock.$O\
+ fp.$O\
+ r16.$O\
+ vlrt.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+ r16.h\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= $SYSLIBS netapi32.lib wsock32.lib user32.lib gdi32.lib advapi32.lib winmm.lib mpr.lib
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF.exe
+
+<../port/portmkfile
+
+i$CONF.exe: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -out:$target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: i$CONF.exe
+ cp i$CONF.exe $INSTALLDIR/$CONF.exe
--- /dev/null
+++ b/emu/Nt/nt.rc
@@ -1,0 +1,1 @@
+100 ICON inferno.ico
--- /dev/null
+++ b/emu/Nt/os.c
@@ -1,0 +1,715 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+#undef Unknown
+#include <excpt.h>
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include "r16.h"
+
+int SYS_SLEEP = 2;
+int SOCK_SELECT = 3;
+#define MAXSLEEPERS 1500
+
+extern int cflag;
+
+DWORD PlatformId;
+DWORD consolestate;
+static char* path;
+static HANDLE kbdh = INVALID_HANDLE_VALUE;
+static HANDLE conh = INVALID_HANDLE_VALUE;
+static HANDLE errh = INVALID_HANDLE_VALUE;
+static int donetermset = 0;
+static int sleepers = 0;
+
+
+__declspec(thread) Proc *up;
+
+HANDLE ntfd2h(int);
+int nth2fd(HANDLE);
+void termrestore(void);
+char *hosttype = "Nt";
+char *cputype = "386";
+void (*coherence)(void) = nofence;
+
+static void
+pfree(Proc *p)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ e = p->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(e->user);
+ free(p->prog);
+ CloseHandle((HANDLE)p->os);
+ free(p);
+}
+
+void
+osblock(void)
+{
+ if(WaitForSingleObject((HANDLE)up->os, INFINITE) != WAIT_OBJECT_0)
+ panic("osblock failed");
+}
+
+void
+osready(Proc *p)
+{
+ if(SetEvent((HANDLE)p->os) == FALSE)
+ panic("osready failed");
+}
+
+void
+pexit(char *msg, int t)
+{
+ pfree(up);
+ ExitThread(0);
+}
+
+LONG TrapHandler(LPEXCEPTION_POINTERS ureg);
+
+__cdecl
+Exhandler(EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, void *dcon)
+{
+ EXCEPTION_POINTERS ep;
+ ep.ExceptionRecord = rec;
+ ep.ContextRecord = context;
+ TrapHandler(&ep);
+ return ExceptionContinueExecution;
+}
+
+DWORD WINAPI
+tramp(LPVOID p)
+{
+ up = p;
+ up->func(up->arg);
+ pexit("", 0);
+ /* not reached */
+ for(;;)
+ panic("tramp");
+ return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ DWORD h;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+
+ p = newproc();
+ if(p == nil)
+ panic("out of kernel processes");
+ p->os = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if(p->os == NULL)
+ panic("can't allocate os event");
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->ui = up->env->ui;
+ kstrdup(&p->env->user, up->env->user);
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ p->pid = (int)CreateThread(0, 16384, tramp, p, 0, &h);
+ if(p->pid <= 0)
+ panic("ran out of kernel processes");
+}
+
+#if(_WIN32_WINNT >= 0x0400)
+void APIENTRY sleepintr(DWORD param)
+{
+}
+#endif
+
+void
+oshostintr(Proc *p)
+{
+ if (p->syscall == SOCK_SELECT)
+ return;
+ p->intwait = 0;
+#if(_WIN32_WINNT >= 0x0400)
+ if(p->syscall == SYS_SLEEP) {
+ QueueUserAPC(sleepintr, (HANDLE) p->pid, (DWORD) p->pid);
+ }
+#endif
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ longjmp(env, val);
+}
+
+int
+readkbd(void)
+{
+ DWORD r;
+ char buf[1];
+
+ if(ReadFile(kbdh, buf, sizeof(buf), &r, 0) == FALSE)
+ panic("keyboard fail");
+ if (r == 0)
+ panic("keyboard EOF");
+
+ if (buf[0] == 0x03) {
+ // INTR (CTRL+C)
+ termrestore();
+ ExitProcess(0);
+ }
+ if(buf[0] == '\r')
+ buf[0] = '\n';
+ return buf[0];
+}
+
+void
+cleanexit(int x)
+{
+ sleep(2); /* give user a chance to see message */
+ termrestore();
+ ExitProcess(x);
+}
+
+struct ecodes {
+ DWORD code;
+ char* name;
+} ecodes[] = {
+ EXCEPTION_ACCESS_VIOLATION, "segmentation violation",
+ EXCEPTION_DATATYPE_MISALIGNMENT, "data alignment",
+ EXCEPTION_BREAKPOINT, "breakpoint",
+ EXCEPTION_SINGLE_STEP, "single step",
+ EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "array bounds check",
+ EXCEPTION_FLT_DENORMAL_OPERAND, "denormalized float",
+ EXCEPTION_FLT_DIVIDE_BY_ZERO, "floating point divide by zero",
+ EXCEPTION_FLT_INEXACT_RESULT, "inexact floating point",
+ EXCEPTION_FLT_INVALID_OPERATION, "invalid floating operation",
+ EXCEPTION_FLT_OVERFLOW, "floating point result overflow",
+ EXCEPTION_FLT_STACK_CHECK, "floating point stack check",
+ EXCEPTION_FLT_UNDERFLOW, "floating point result underflow",
+ EXCEPTION_INT_DIVIDE_BY_ZERO, "divide by zero",
+ EXCEPTION_INT_OVERFLOW, "integer overflow",
+ EXCEPTION_PRIV_INSTRUCTION, "privileged instruction",
+ EXCEPTION_IN_PAGE_ERROR, "page-in error",
+ EXCEPTION_ILLEGAL_INSTRUCTION, "illegal instruction",
+ EXCEPTION_NONCONTINUABLE_EXCEPTION, "non-continuable exception",
+ EXCEPTION_STACK_OVERFLOW, "stack overflow",
+ EXCEPTION_INVALID_DISPOSITION, "invalid disposition",
+ EXCEPTION_GUARD_PAGE, "guard page violation",
+ 0, nil
+};
+
+LONG
+TrapHandler(LPEXCEPTION_POINTERS ureg)
+{
+ int i;
+ char *name;
+ DWORD code;
+ // WORD pc;
+ char buf[ERRMAX];
+
+ code = ureg->ExceptionRecord->ExceptionCode;
+ // pc = ureg->ContextRecord->Eip;
+
+ name = nil;
+ for(i = 0; i < nelem(ecodes); i++) {
+ if(ecodes[i].code == code) {
+ name = ecodes[i].name;
+ break;
+ }
+ }
+
+ if(name == nil) {
+ snprint(buf, sizeof(buf), "unknown trap type (%#.8lux)\n", code);
+ name = buf;
+ }
+/*
+ if(pc != 0) {
+ snprint(buf, sizeof(buf), "%s: pc=0x%lux", name, pc);
+ name = buf;
+ }
+*/
+ switch (code) {
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ /* clear exception flags and ensure safe empty state */
+ _asm { fnclex };
+ _asm { fninit };
+ }
+ disfault(nil, name);
+ /* not reached */
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+static void
+termset(void)
+{
+ DWORD flag;
+
+ if(donetermset)
+ return;
+ donetermset = 1;
+ conh = GetStdHandle(STD_OUTPUT_HANDLE);
+ kbdh = GetStdHandle(STD_INPUT_HANDLE);
+ errh = GetStdHandle(STD_ERROR_HANDLE);
+ if(errh == INVALID_HANDLE_VALUE)
+ errh = conh;
+
+ // The following will fail if kbdh not from console (e.g. a pipe)
+ // in which case we don't care
+ GetConsoleMode(kbdh, &consolestate);
+ flag = consolestate;
+ flag = flag & ~(ENABLE_PROCESSED_INPUT|ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT);
+ SetConsoleMode(kbdh, flag);
+}
+
+void
+termrestore(void)
+{
+ if(kbdh != INVALID_HANDLE_VALUE)
+ SetConsoleMode(kbdh, consolestate);
+}
+
+static int rebootok = 0; /* is shutdown -r supported? */
+
+void
+osreboot(char *file, char **argv)
+{
+ if(rebootok){
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+ }
+}
+
+void
+libinit(char *imod)
+{
+ WSADATA wasdat;
+ DWORD lasterror, namelen;
+ OSVERSIONINFO os;
+ char sys[64], uname[64];
+ wchar_t wuname[64];
+ char *uns;
+
+ os.dwOSVersionInfoSize = sizeof(os);
+ if(!GetVersionEx(&os))
+ panic("can't get os version");
+ PlatformId = os.dwPlatformId;
+ if (PlatformId == VER_PLATFORM_WIN32_NT) { /* true for NT and 2000 */
+ rebootok = 1;
+ } else {
+ rebootok = 0;
+ }
+ termset();
+
+ if((int)INVALID_HANDLE_VALUE != -1 || sizeof(HANDLE) != sizeof(int))
+ panic("invalid handle value or size");
+
+ if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0)
+ panic("no winsock.dll");
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ if(sflag == 0)
+ SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TrapHandler);
+
+ path = getenv("PATH");
+ if(path == nil)
+ path = ".";
+
+ up = newproc();
+ if(up == nil)
+ panic("cannot create kernel process");
+
+ strcpy(uname, "inferno");
+ namelen = sizeof(wuname);
+ if(GetUserName(wuname, &namelen) != TRUE) {
+ lasterror = GetLastError();
+ if(PlatformId == VER_PLATFORM_WIN32_NT || lasterror != ERROR_NOT_LOGGED_ON)
+ print("cannot GetUserName: %d\n", lasterror);
+ }else{
+ uns = narrowen(wuname);
+ snprint(uname, sizeof(uname), "%s", uns);
+ free(uns);
+ }
+ kstrdup(&eve, uname);
+
+ emuinit(imod);
+}
+
+void
+FPsave(void *fptr)
+{
+ _asm {
+ mov eax, fptr
+ fstenv [eax]
+ }
+}
+
+void
+FPrestore(void *fptr)
+{
+ _asm {
+ mov eax, fptr
+ fldenv [eax]
+ }
+}
+
+ulong
+umult(ulong a, ulong b, ulong *high)
+{
+ ulong lo, hi;
+
+ _asm {
+ mov eax, a
+ mov ecx, b
+ MUL ecx
+ mov lo, eax
+ mov hi, edx
+ }
+ *high = hi;
+ return lo;
+}
+
+int
+close(int fd)
+{
+ if(fd == -1)
+ return 0;
+ CloseHandle(ntfd2h(fd));
+ return 0;
+}
+
+int
+read(int fd, void *buf, uint n)
+{
+ HANDLE h;
+
+ if(fd == 0)
+ h = kbdh;
+ else
+ h = ntfd2h(fd);
+ if(h == INVALID_HANDLE_VALUE)
+ return -1;
+ if(!ReadFile(h, buf, n, &n, NULL))
+ return -1;
+ return n;
+}
+
+int
+write(int fd, void *buf, uint n)
+{
+ HANDLE h;
+
+ if(fd == 1 || fd == 2){
+ if(!donetermset)
+ termset();
+ if(fd == 1)
+ h = conh;
+ else
+ h = errh;
+ if(h == INVALID_HANDLE_VALUE)
+ return -1;
+ if(!WriteFile(h, buf, n, &n, NULL))
+ return -1;
+ return n;
+ }
+ if(!WriteFile(ntfd2h(fd), buf, n, &n, NULL))
+ return -1;
+ return n;
+}
+
+/*
+ * map handles and fds.
+ * this code assumes sizeof(HANDLE) == sizeof(int),
+ * that INVALID_HANDLE_VALUE is -1, and assumes
+ * that all tests of invalid fds check only for -1, not < 0
+ */
+int
+nth2fd(HANDLE h)
+{
+ return (int)h;
+}
+
+HANDLE
+ntfd2h(int fd)
+{
+ return (HANDLE)fd;
+}
+
+void
+oslopri(void)
+{
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+}
+
+/* Resolve system header name conflict */
+#undef Sleep
+void
+sleep(int secs)
+{
+ Sleep(secs*1000);
+}
+
+void*
+sbrk(int size)
+{
+ void *brk;
+
+ brk = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ if(brk == 0)
+ return (void*)-1;
+
+ return brk;
+}
+
+ulong
+getcallerpc(void *arg)
+{
+ ulong cpc;
+ _asm {
+ mov eax, dword ptr [ebp]
+ mov eax, dword ptr [eax+4]
+ mov dword ptr cpc, eax
+ }
+ return cpc;
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ return GetTickCount();
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int*
+yrsize(int yr)
+{
+ /* a leap year is a multiple of 4, excluding centuries
+ * that are not multiples of 400 */
+ if( (yr % 4 == 0) && (yr % 100 != 0 || yr % 400 == 0) )
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+static long
+tm2sec(SYSTEMTIME *tm)
+{
+ long secs;
+ int i, *d2m;
+
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ for(i = 1970; i < tm->wYear; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * seconds per month
+ */
+ d2m = yrsize(tm->wYear);
+ for(i = 1; i < tm->wMonth; i++)
+ secs += d2m[i] * SEC2DAY;
+
+ /*
+ * secs in last month
+ */
+ secs += (tm->wDay-1) * SEC2DAY;
+
+ /*
+ * hours, minutes, seconds
+ */
+ secs += tm->wHour * SEC2HOUR;
+ secs += tm->wMinute * SEC2MIN;
+ secs += tm->wSecond;
+
+ return secs;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+ SYSTEMTIME tm;
+ vlong secs;
+
+ GetSystemTime(&tm);
+ secs = tm2sec(&tm);
+ return secs * 1000000 + tm.wMilliseconds * 1000;
+}
+
+vlong
+osnsec(void)
+{
+ return osusectime()*1000; /* TO DO better */
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ SleepEx(milsec, FALSE);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ if (sleepers > MAXSLEEPERS)
+ return -1;
+ sleepers++;
+ up->syscall = SYS_SLEEP;
+ SleepEx(milsec, TRUE);
+ up->syscall = 0;
+ sleepers--;
+ return 0;
+}
+
+void
+osyield(void)
+{
+ SwitchToThread();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ sleep(1000000);
+}
+
+/*
+ * these should never be called, and are included
+ * as stubs since we are linking against a library which defines them
+ */
+int
+open(const char *path, int how, ...)
+{
+ panic("open");
+ return -1;
+}
+
+int
+creat(const char *path, int how)
+{
+ panic("creat");
+ return -1;
+}
+
+int
+stat(const char *path, struct stat *sp)
+{
+ panic("stat");
+ return -1;
+}
+
+int
+chown(const char *path, int uid, int gid)
+{
+ panic("chown");
+ return -1;
+}
+
+int
+chmod(const char *path, int mode)
+{
+ panic("chmod");
+ return -1;
+}
+
+void
+link(char *path, char *next)
+{
+ panic("link");
+}
+
+int
+segflush(void *a, ulong n)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Nt/r16.c
@@ -1,0 +1,186 @@
+#define UNICODE
+#define Unknown win_Unknown
+#include <windows.h>
+#include <winbase.h>
+#undef Unknown
+#undef Sleep
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "r16.h"
+
+enum
+{
+ Bits10 = 0x03ff, /* 0011 1111 1111 */
+
+ R16self = 0x10000,
+
+ HSurrogateMin = 0xd800,
+ HSurrogateMax = 0xdbff,
+ LSurrogateMin = 0xdc00,
+ LSurrogateMax = 0xdfff,
+};
+
+Rune16*
+runes16dup(Rune16 *r)
+{
+ int n;
+ Rune16 *s;
+
+ n = runes16len(r) + 1;
+ s = malloc(n * sizeof(Rune16));
+ if(s == nil)
+ error(Enomem);
+ memmove(s, r, n * sizeof(Rune16));
+ return s;
+}
+
+int
+runes16len(Rune16 *r)
+{
+ int n;
+
+ n = 0;
+ while(*r++ != 0)
+ n++;
+ return n;
+}
+
+char*
+runes16toutf(char *p, Rune16 *r, int nc)
+{
+ char *op, *ep;
+ int n;
+ Rune c, lc;
+
+ op = p;
+ ep = p + nc;
+ while(c = *r++) {
+ if(c > Runemax)
+ c = Runeerror;
+ if(c >= LSurrogateMin && c <= LSurrogateMax)
+ c = Runeerror;
+ if(c >= HSurrogateMin && c<= HSurrogateMax){
+ lc = *r++;
+ if(lc >= LSurrogateMin || lc <= LSurrogateMax)
+ c = (c&Bits10)<<10 | (lc&Bits10) + R16self;
+ else
+ c = Runeerror;
+ }
+ n = runelen(c);
+ if(p + n >= ep)
+ break;
+ p += runetochar(p, &c);
+ }
+ *p = '\0';
+ return op;
+}
+
+int
+rune16nlen(Rune16 *r, int nrune)
+{
+ int nb;
+ Rune c;
+
+ nb = 0;
+ while(nrune--) {
+ c = *r++;
+ if(c < R16self)
+ nb += runelen(c);
+ else {
+ c -= R16self;
+ nb += runelen(HSurrogateMin | (c>>10));
+ nb += runelen(LSurrogateMin | (c&Bits10));
+ }
+ }
+ return nb;
+}
+
+Rune16*
+utftorunes16(Rune16 *r, char *p, int nc)
+{
+ Rune16 *or, *er;
+ Rune rc;
+
+ or = r;
+ er = r + nc;
+ while(*p != '\0' && r + 1 < er){
+ p += chartorune(&rc, p);
+ if(rc < R16self){
+ *r++ = rc;
+ continue;
+ }
+ if(rc > Runemax || er-r < 2){
+ *r++ = Runeerror;
+ continue;
+ }
+ rc -= R16self;
+ *r++ = HSurrogateMin | (rc>>10);
+ *r++ = LSurrogateMin | (rc&Bits10);
+ }
+ *r = '\0';
+ return or;
+}
+
+int
+runes16cmp(Rune16 *s1, Rune16 *s2)
+{
+ Rune16 r1, r2;
+
+ for(;;) {
+ r1 = *s1++;
+ r2 = *s2++;
+ if(r1 != r2) {
+ if(r1 > r2)
+ return 1;
+ return -1;
+ }
+ if(r1 == 0)
+ return 0;
+ }
+}
+
+wchar_t *
+widen(char *s)
+{
+ int n;
+ wchar_t *ws;
+
+ n = utflen(s) + 1;
+ ws = smalloc(n*sizeof(wchar_t));
+ utftorunes16(ws, s, n);
+ return ws;
+}
+
+
+char *
+narrowen(wchar_t *ws)
+{
+ char *s;
+ int n;
+
+ n = widebytes(ws);
+ s = smalloc(n);
+ runes16toutf(s, ws, n);
+ return s;
+}
+
+
+int
+widebytes(wchar_t *ws)
+{
+ int n = 0;
+ wchar_t c;
+
+ while (*ws){
+ c = *ws++;
+ if(c < R16self)
+ n += runelen(c);
+ else {
+ c -= R16self;
+ n += runelen(HSurrogateMin | (c>>10));
+ n += runelen(LSurrogateMin | (c&Bits10));
+ }
+ }
+ return n+1;
+}
--- /dev/null
+++ b/emu/Nt/r16.h
@@ -1,0 +1,11 @@
+typedef unsigned short Rune16;
+
+wchar_t *widen(char *s);
+char *narrowen(wchar_t *ws);
+int widebytes(wchar_t *ws);
+int runes16len(Rune16*);
+int rune16nlen(Rune16*, int);
+Rune16* runes16dup(Rune16*);
+Rune16* utftorunes16(Rune16*, char*, int);
+char* runes16toutf(char*, Rune16*, int);
+int runes16cmp(Rune16*, Rune16*);
--- /dev/null
+++ b/emu/Nt/vlrt.c
@@ -1,0 +1,751 @@
+#include "dat.h"
+
+/*
+ * typedef unsigned long ulong;
+ * typedef unsigned int uint;
+ * typedef unsigned short ushort;
+ * typedef unsigned char uchar;
+ * typedef signed char schar;
+*/
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ ulong lo;
+ ulong hi;
+};
+
+void abort(void);
+
+void
+_addv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo + b.lo;
+ hi = a.hi + b.hi;
+ if(lo < a.lo)
+ hi++;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_subv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo - b.lo;
+ hi = a.hi - b.hi;
+ if(lo > a.lo)
+ hi--;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_mulv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong ahi, alo, chi, clo, x;
+ int i;
+
+ ahi = a.hi;
+ alo = a.lo;
+ chi = 0;
+ clo = 0;
+
+ x = b.lo;
+ for(i=0; i<32; i++) {
+ if(x & 1) {
+ chi += ahi;
+ clo += alo;
+ if(clo < alo)
+ chi++;
+ }
+ ahi <<= 1;
+ if(alo & SIGN(32))
+ ahi += 1;
+ alo <<= 1;
+ x >>= 1;
+ }
+
+ /*
+ * same, but
+ * alo is known to be 0
+ * can stop when x == 0
+ */
+ for(x=b.hi; x; x>>=1) {
+ if(x & 1)
+ chi += ahi;
+ ahi <<= 1;
+ }
+
+ r->hi = chi;
+ r->lo = clo;
+}
+
+void
+_d2v(Vlong *y, double d)
+{
+ Vlong x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ *(double*)&x = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
+{
+ ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+ int i;
+
+ numhi = num.hi;
+ numlo = num.lo;
+ denhi = den.hi;
+ denlo = den.lo;
+
+ /*
+ * get a divide by zero
+ */
+ if(denlo==0 && denhi==0) {
+ numlo = numlo / denlo;
+ }
+
+ /*
+ * set up the divisor and find the number of iterations needed
+ */
+ if(numhi >= SIGN(32)) {
+ quohi = SIGN(32);
+ quolo = 0;
+ } else {
+ quohi = numhi;
+ quolo = numlo;
+ }
+ i = 0;
+ while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+ denhi = (denhi<<1) | (denlo>>31);
+ denlo <<= 1;
+ i++;
+ }
+
+ quohi = 0;
+ quolo = 0;
+ for(; i >= 0; i--) {
+ quohi = (quohi<<1) | (quolo>>31);
+ quolo <<= 1;
+ if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+ t = numlo;
+ numlo -= denlo;
+ if(numlo > t)
+ numhi--;
+ numhi -= denhi;
+ quolo |= 1;
+ }
+ denlo = (denlo>>1) | (denhi<<31);
+ denhi >>= 1;
+ }
+
+ if(q) {
+ q->lo = quolo;
+ q->hi = quohi;
+ }
+ if(r) {
+ r->lo = numlo;
+ r->hi = numhi;
+ }
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+ long t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = t>>31;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = t>>31;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = 0;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.lo;
+ if(b >= 32) {
+ r->lo = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->hi = 0;
+ return;
+ }
+ r->hi = t << (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->lo = t;
+ r->hi = a.hi;
+ return;
+ }
+ r->lo = t << b;
+ r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi & b.hi;
+ r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi | b.hi;
+ r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi ^ b.hi;
+ r->lo = a.lo ^ b.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u.lo = 0;
+ u.hi = 0;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+ long t;
+
+ t = (ulong)p;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+ long t;
+
+ t = sl;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+ long t;
+
+ t = si;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+ long t;
+
+ t = ui;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+ long t;
+
+ t = (sh << 16) >> 16;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xffff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+ long t;
+
+ t = (uc << 24) >> 24;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xff;
+ return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+ return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xffff;
+ return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+ return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+ return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+ return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+ return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
--- /dev/null
+++ b/emu/Nt/win.c
@@ -1,0 +1,798 @@
+#define Unknown WUnknown
+#define Colormap WColormap
+#define Cursor WCursor
+#define Display WDisplay
+#define Drawable WDrawable
+#define Font WFont
+#define GC WGC
+#define Point WPoint
+#define Rectangle WRectangle
+#define Screen WScreen
+#define Visual WVisual
+#define Window WWindow
+
+#include <windows.h>
+
+#undef Colormap
+#undef Cursor
+#undef Display
+#undef XDrawable
+#undef Font
+#undef GC
+#undef Point
+#undef Rectangle
+#undef Screen
+#undef Visual
+#undef Window
+#undef Unknown
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <draw.h>
+#include "keyboard.h"
+#include "cursor.h"
+#include "r16.h"
+
+extern ulong displaychan;
+
+extern int bytesperline(Rectangle, int);
+extern int main(int argc, char **argv);
+static void dprint(char*, ...);
+static DWORD WINAPI winproc(LPVOID);
+
+static HINSTANCE inst;
+static HINSTANCE previnst;
+static int cmdshow;
+static HWND window;
+static HDC screen;
+static HPALETTE palette;
+static int maxxsize;
+static int maxysize;
+static int attached;
+static int isunicode = 1;
+static HCURSOR hcursor;
+
+char *argv0 = "inferno";
+static ulong *data;
+
+extern DWORD PlatformId;
+char* gkscanid = "emu_win32vk";
+
+int WINAPI
+WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow)
+{
+ inst = winst;
+ previnst = wprevinst;
+ cmdshow = wcmdshow;
+
+ /* cmdline passed into WinMain does not contain name of executable.
+ * The globals __argc and __argv to include this info - like UNIX
+ */
+ main(__argc, __argv);
+ return 0;
+}
+
+static void
+dprint(char *fmt, ...)
+{
+ va_list arg;
+ char buf[128];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg);
+ va_end(arg);
+ OutputDebugString("inferno: ");
+ OutputDebugString(buf);
+}
+
+static void
+graphicscmap(PALETTEENTRY *pal)
+{
+ int r, g, b, cr, cg, cb, v, p;
+ int num, den;
+ int i, j;
+ for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+ for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+ den=r;
+ if(g>den) den=g;
+ if(b>den) den=b;
+ if(den==0) /* divide check -- pick grey shades */
+ cr=cg=cb=v*17;
+ else{
+ num=17*(4*den+v);
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ p = i+(j&15);
+ pal[p].peRed = cr*0x01010101;
+ pal[p].peGreen = cg*0x01010101;
+ pal[p].peBlue = cb*0x01010101;
+ pal[p].peFlags = 0;
+ }
+ }
+}
+
+static void
+graphicsgmap(PALETTEENTRY *pal, int d)
+{
+ int i, j, s, m, p;
+
+ s = 8-d;
+ m = 1;
+ while(--d >= 0)
+ m *= 2;
+ m = 255/(m-1);
+ for(i=0; i < 256; i++){
+ j = (i>>s)*m;
+ p = 255-i;
+ pal[p].peRed = pal[p].peGreen = pal[p].peBlue = (255-j)*0x01010101;
+ pal[p].peFlags = 0;
+ }
+}
+
+static ulong
+autochan(void)
+{
+ HDC dc;
+ int bpp;
+
+ dc = GetDC(NULL);
+ if (dc == NULL)
+ return CMAP8;
+
+ bpp = GetDeviceCaps(dc, BITSPIXEL);
+ if (bpp < 15)
+ return CMAP8;
+ if (bpp < 24)
+ return RGB15;
+ if (bpp < 32)
+ return RGB24;
+ return XRGB32;
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int i, k;
+ ulong c;
+ DWORD h;
+ RECT bs;
+ RGBQUAD *rgb;
+ HBITMAP bits;
+ BITMAPINFO *bmi;
+ LOGPALETTE *logpal;
+ PALETTEENTRY *pal;
+ int bsh, bsw, sx, sy;
+
+ if(attached)
+ goto Return;
+
+ /* Compute bodersizes */
+ memset(&bs, 0, sizeof(bs));
+ AdjustWindowRect(&bs, WS_OVERLAPPEDWINDOW, 0);
+ bsw = bs.right - bs.left;
+ bsh = bs.bottom - bs.top;
+ sx = GetSystemMetrics(SM_CXFULLSCREEN) - bsw;
+ Xsize -= Xsize % 4; /* Round down */
+ if(Xsize > sx)
+ Xsize = sx;
+ sy = GetSystemMetrics(SM_CYFULLSCREEN) - bsh + 20;
+ if(Ysize > sy)
+ Ysize = sy;
+
+ logpal = malloc(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY));
+ if(logpal == nil)
+ return nil;
+ logpal->palVersion = 0x300;
+ logpal->palNumEntries = 256;
+ pal = logpal->palPalEntry;
+
+ c = displaychan;
+ if(c == 0)
+ c = autochan();
+ k = 8;
+ if(TYPE(c) == CGrey){
+ graphicsgmap(pal, NBITS(c));
+ c = GREY8;
+ }else{
+ if(c == RGB15)
+ k = 16;
+ else if(c == RGB24)
+ k = 24;
+ else if(c == XRGB32)
+ k = 32;
+ else
+ c = CMAP8;
+ graphicscmap(pal);
+ }
+
+ palette = CreatePalette(logpal);
+
+ if(k == 8)
+ bmi = malloc(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD));
+ else
+ bmi = malloc(sizeof(BITMAPINFOHEADER));
+ if(bmi == nil)
+ return nil;
+ bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi->bmiHeader.biWidth = Xsize;
+ bmi->bmiHeader.biHeight = -Ysize; /* - => origin upper left */
+ bmi->bmiHeader.biPlanes = 1; /* always 1 */
+ bmi->bmiHeader.biBitCount = k;
+ bmi->bmiHeader.biCompression = BI_RGB;
+ bmi->bmiHeader.biSizeImage = 0; /* Xsize*Ysize*(k/8) */
+ bmi->bmiHeader.biXPelsPerMeter = 0;
+ bmi->bmiHeader.biYPelsPerMeter = 0;
+ bmi->bmiHeader.biClrUsed = 0;
+ bmi->bmiHeader.biClrImportant = 0; /* number of important colors: 0 means all */
+
+ if(k == 8){
+ rgb = bmi->bmiColors;
+ for(i = 0; i < 256; i++){
+ rgb[i].rgbRed = pal[i].peRed;
+ rgb[i].rgbGreen = pal[i].peGreen;
+ rgb[i].rgbBlue = pal[i].peBlue;
+ }
+ }
+
+ screen = CreateCompatibleDC(NULL);
+ if(screen == nil){
+ fprint(2, "screen dc nil\n");
+ return nil;
+ }
+
+ if(SelectPalette(screen, palette, 1) == nil){
+ fprint(2, "select pallete failed\n");
+ }
+ i = RealizePalette(screen);
+ GdiFlush();
+ bits = CreateDIBSection(screen, bmi, DIB_RGB_COLORS, &data, nil, 0);
+ if(bits == nil){
+ fprint(2, "CreateDIBSection failed\n");
+ return nil;
+ }
+
+ SelectObject(screen, bits);
+ GdiFlush();
+ CreateThread(0, 16384, winproc, nil, 0, &h);
+ attached = 1;
+
+ Return:
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+ displaychan = c;
+ *chan = c;
+ *d = k;
+ *width = (Xsize/4)*(k/8);
+ *softscreen = 1;
+ return (uchar*)data;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ RECT wr;
+
+ if(r.max.x<=r.min.x || r.max.y<=r.min.y)
+ return;
+ wr.left = r.min.x;
+ wr.top = r.min.y;
+ wr.right = r.max.x;
+ wr.bottom = r.max.y;
+ InvalidateRect(window, &wr, 0);
+}
+
+static void
+scancode(WPARAM wparam, LPARAM lparam, int keyup)
+{
+ uchar buf[2];
+
+ if(!(lparam & (1<<30))) { /* don't auto-repeat chars */
+ buf[0] = wparam;
+ buf[1] = wparam >> 8;
+ if (keyup)
+ buf[1] |= 0x80;
+ qproduce(gkscanq, buf, sizeof buf);
+ }
+}
+
+LRESULT CALLBACK
+WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ PAINTSTRUCT paint;
+ HDC hdc;
+ LPMINMAXINFO mmi;
+ LONG x, y, w, h, b;
+ HCURSOR dcurs;
+ POINT m;
+
+ switch(msg) {
+ case WM_SETCURSOR:
+ /* User set */
+ if(hcursor != NULL) {
+ SetCursor(hcursor);
+ break;
+ }
+ /* Pick the default */
+ dcurs = LoadCursor(NULL, IDC_ARROW);
+ SetCursor(dcurs);
+ break;
+ case WM_MOUSEWHEEL:
+ if((int)wparam>0)
+ b = 8;
+ else
+ b = 16;
+ m.x = LOWORD(lparam);
+ m.y = HIWORD(lparam);
+ ScreenToClient(hwnd, &m);
+ goto mok;
+ case WM_LBUTTONDBLCLK:
+ b = (1<<8) | 1;
+ goto process;
+ case WM_MBUTTONDBLCLK:
+ b = (1<<8) | 2;
+ goto process;
+ case WM_RBUTTONDBLCLK:
+ b = (1<<8) | 4;
+ goto process;
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ b = 0;
+ process:
+ m.x = LOWORD(lparam);
+ m.y = HIWORD(lparam);
+ mok:
+ if(wparam & MK_LBUTTON)
+ b |= 1;
+ if(wparam & MK_MBUTTON)
+ b |= 2;
+ if(wparam & MK_RBUTTON) {
+ if(wparam & MK_CONTROL)
+ b |= 2; //simulate middle button
+ else
+ b |= 4; //right button
+ }
+ mousetrack(b, m.x, m.y, 0);
+ break;
+ case WM_SYSKEYDOWN:
+ if(gkscanq)
+ scancode(wparam, lparam, 0);
+ break;
+ case WM_SYSKEYUP:
+ if(gkscanq)
+ scancode(wparam, lparam, 1);
+ else if(wparam == VK_MENU)
+ gkbdputc(gkbdq, Latin);
+ break;
+ case WM_KEYDOWN:
+ if(gkscanq) {
+ scancode(wparam, lparam, 0);
+ break;
+ }
+ switch(wparam) {
+ default:
+ return 0;
+ case VK_HOME:
+ wparam = Home;
+ break;
+ case VK_END:
+ wparam = End;
+ break;
+ case VK_UP:
+ wparam = Up;
+ break;
+ case VK_DOWN:
+ wparam = Down;
+ break;
+ case VK_LEFT:
+ wparam = Left;
+ break;
+ case VK_RIGHT:
+ wparam = Right;
+ break;
+ case VK_PRIOR: /* VK_PAGE_UP */
+ wparam = Pgup;
+ break;
+ case VK_NEXT: /* VK_PAGE_DOWN */
+ wparam = Pgdown;
+ break;
+ case VK_PRINT:
+ wparam = Print;
+ break;
+ case VK_SCROLL:
+ wparam = Scroll;
+ break;
+ case VK_PAUSE:
+ wparam = Pause;
+ break;
+ case VK_INSERT:
+ wparam = Ins;
+ break;
+ case VK_DELETE:
+ wparam = Del;
+ break;
+/*
+ case VK_TAB:
+ if(GetKeyState(VK_SHIFT)<0)
+ wparam = BackTab;
+ else
+ wparam = '\t';
+ break;
+*/
+ }
+ gkbdputc(gkbdq, wparam);
+ break;
+ case WM_KEYUP:
+ if(gkscanq)
+ scancode(wparam, lparam, 1);
+ break;
+ case WM_CHAR:
+ if(gkscanq)
+ break;
+ switch(wparam) {
+ case '\n':
+ wparam = '\r';
+ break;
+ case '\r':
+ wparam = '\n';
+ break;
+ case '\t':
+ if(GetKeyState(VK_SHIFT)<0)
+ wparam = BackTab;
+ else
+ wparam = '\t';
+ break;
+ }
+ if(lparam & KF_ALTDOWN)
+ wparam = APP | (wparam & 0xFF);
+ gkbdputc(gkbdq, wparam);
+ break;
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ cleanexit(0);
+ break;
+ case WM_PALETTECHANGED:
+ if((HWND)wparam == hwnd)
+ break;
+ /* fall through */
+ case WM_QUERYNEWPALETTE:
+ hdc = GetDC(hwnd);
+ SelectPalette(hdc, palette, 0);
+ if(RealizePalette(hdc) != 0)
+ InvalidateRect(hwnd, nil, 0);
+ ReleaseDC(hwnd, hdc);
+ break;
+ case WM_PAINT:
+ hdc = BeginPaint(hwnd, &paint);
+ SelectPalette(hdc, palette, 0);
+ RealizePalette(hdc);
+ x = paint.rcPaint.left;
+ y = paint.rcPaint.top;
+ w = paint.rcPaint.right - x;
+ h = paint.rcPaint.bottom - y;
+ BitBlt(hdc, x, y, w, h, screen, x, y, SRCCOPY);
+ EndPaint(hwnd, &paint);
+ break;
+ case WM_GETMINMAXINFO:
+ mmi = (LPMINMAXINFO)lparam;
+ mmi->ptMaxSize.x = maxxsize;
+ mmi->ptMaxSize.y = maxysize;
+ mmi->ptMaxTrackSize.x = maxxsize;
+ mmi->ptMaxTrackSize.y = maxysize;
+ break;
+ case WM_SYSCHAR:
+ case WM_COMMAND:
+ case WM_CREATE:
+ case WM_SETFOCUS:
+ case WM_DEVMODECHANGE:
+ case WM_WININICHANGE:
+ case WM_INITMENU:
+ default:
+ if(isunicode)
+ return DefWindowProcW(hwnd, msg, wparam, lparam);
+ return DefWindowProcA(hwnd, msg, wparam, lparam);
+ }
+ return 0;
+}
+
+static DWORD WINAPI
+winproc(LPVOID x)
+{
+ MSG msg;
+ RECT size;
+ WNDCLASSW wc;
+ WNDCLASSA wca;
+ DWORD ws;
+
+ if(!previnst){
+ wc.style = CS_DBLCLKS;
+ wc.lpfnWndProc = WindowProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = inst;
+ wc.hIcon = LoadIcon(inst, MAKEINTRESOURCE(100));
+ wc.hCursor = NULL;
+ wc.hbrBackground = GetStockObject(WHITE_BRUSH);
+
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = L"inferno";
+
+ if(RegisterClassW(&wc) == 0){
+ wca.style = wc.style;
+ wca.lpfnWndProc = wc.lpfnWndProc;
+ wca.cbClsExtra = wc.cbClsExtra;
+ wca.cbWndExtra = wc.cbWndExtra;
+ wca.hInstance = wc.hInstance;
+ wca.hIcon = wc.hIcon;
+ wca.hCursor = wc.hCursor;
+ wca.hbrBackground = wc.hbrBackground;
+
+ wca.lpszMenuName = 0;
+ wca.lpszClassName = "inferno";
+ isunicode = 0;
+
+ RegisterClassA(&wca);
+ }
+ }
+
+ size.left = 0;
+ size.top = 0;
+ size.right = Xsize;
+ size.bottom = Ysize;
+
+ ws = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX;
+
+ if(AdjustWindowRect(&size, ws, 0)) {
+ maxxsize = size.right - size.left;
+ maxysize = size.bottom - size.top;
+ }else{
+ maxxsize = Xsize + 40;
+ maxysize = Ysize + 40;
+ }
+
+ if(isunicode) {
+ window = CreateWindowExW(
+ 0, /* extended style */
+ L"inferno", /* class */
+ L"Inferno", /* caption */
+ ws, /* style */
+ CW_USEDEFAULT, /* init. x pos */
+ CW_USEDEFAULT, /* init. y pos */
+ maxxsize, /* init. x size */
+ maxysize, /* init. y size */
+ NULL, /* parent window (actually owner window for overlapped) */
+ NULL, /* menu handle */
+ inst, /* program handle */
+ NULL /* create parms */
+ );
+ }else{
+ window = CreateWindowExA(
+ 0, /* extended style */
+ "inferno", /* class */
+ "Inferno", /* caption */
+ ws, /* style */
+ CW_USEDEFAULT, /* init. x pos */
+ CW_USEDEFAULT, /* init. y pos */
+ maxxsize, /* init. x size */
+ maxysize, /* init. y size */
+ NULL, /* parent window (actually owner window for overlapped) */
+ NULL, /* menu handle */
+ inst, /* program handle */
+ NULL /* create parms */
+ );
+ }
+
+ if(window == nil){
+ fprint(2, "can't make window\n");
+ ExitThread(0);
+ }
+
+ SetForegroundWindow(window);
+ ShowWindow(window, cmdshow);
+ UpdateWindow(window);
+ // CloseWindow(window);
+
+ if(isunicode) {
+ while(GetMessageW(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }else{
+ while(GetMessageA(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+ }
+ attached = 0;
+ ExitThread(msg.wParam);
+ return 0;
+}
+
+void
+setpointer(int x, int y)
+{
+ POINT pt;
+
+ pt.x = x; pt.y = y;
+ ClientToScreen(window, &pt);
+ SetCursorPos(pt.x, pt.y);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ HCURSOR nh, oh;
+ Rectangle ir;
+ int i, h, j, bpl, ch, cw;
+ uchar *bs, *bc, *and, *xor, *cand, *cxor;
+
+ /* Set the default system cursor */
+ if(c->data == nil) {
+ oh = hcursor;
+ hcursor = NULL;
+ if(oh != NULL) {
+ SendMessage(window, WM_SETCURSOR, (int)window, 0);
+ DestroyCursor(oh);
+ }
+ return;
+ }
+
+ ir.min.x = c->minx;
+ ir.min.y = c->miny;
+ ir.max.x = c->maxx;
+ ir.max.y = c->maxy;
+ bpl = bytesperline(ir, 1);
+
+ h = (c->maxy-c->miny)/2;
+
+ ch = GetSystemMetrics(SM_CYCURSOR);
+ cw = (GetSystemMetrics(SM_CXCURSOR)+7)/8;
+
+ i = ch*cw;
+ and = malloc(2*i);
+ if(and == nil)
+ return;
+ xor = and + i;
+ memset(and, 0xff, i);
+ memset(xor, 0, i);
+
+ cand = and;
+ cxor = xor;
+ bc = c->data;
+ bs = c->data + h*bpl;
+
+ for(i = 0; i < ch && i < h; i++) {
+ for(j = 0; j < cw && j < bpl; j++) {
+ cand[j] = ~(bs[j] | bc[j]);
+ cxor[j] = ~bs[j] & bc[j];
+ }
+ cand += cw;
+ cxor += cw;
+ bs += bpl;
+ bc += bpl;
+ }
+ nh = CreateCursor(inst, -c->hotx, -c->hoty, 8*cw, ch, and, xor);
+ if(nh != NULL) {
+ oh = hcursor;
+ hcursor = nh;
+ SendMessage(window, WM_SETCURSOR, (int)window, 0);
+ if(oh != NULL)
+ DestroyCursor(oh);
+ }else{
+ print("CreateCursor error %d\n", GetLastError());
+ print("CXCURSOR=%d\n", GetSystemMetrics(SM_CXCURSOR));
+ print("CYCURSOR=%d\n", GetSystemMetrics(SM_CYCURSOR));
+ }
+ free(and);
+}
+
+/*
+ * thanks to drawterm for these
+ */
+
+static char*
+clipreadunicode(HANDLE h)
+{
+ Rune16 *p;
+ int n;
+ char *q;
+
+ p = GlobalLock(h);
+ n = rune16nlen(p, runes16len(p)+1);
+ q = malloc(n);
+ if(q != nil)
+ runes16toutf(q, p, n);
+ GlobalUnlock(h);
+
+ if(q == nil)
+ error(Enovmem);
+ return q;
+}
+
+static char *
+clipreadutf(HANDLE h)
+{
+ uchar *p;
+
+ p = GlobalLock(h);
+ p = strdup(p);
+ GlobalUnlock(h);
+
+ if(p == nil)
+ error(Enovmem);
+ return p;
+}
+
+char*
+clipread(void)
+{
+ HANDLE h;
+ char *p;
+
+ if(!OpenClipboard(window))
+ return strdup("");
+
+ if((h = GetClipboardData(CF_UNICODETEXT)))
+ p = clipreadunicode(h);
+ else if((h = GetClipboardData(CF_TEXT)))
+ p = clipreadutf(h);
+ else
+ p = strdup("");
+
+ CloseClipboard();
+ return p;
+}
+
+int
+clipwrite(char *buf)
+{
+ HANDLE h;
+ char *p;
+ Rune16 *rp;
+ int n;
+
+ n = 0;
+ if(buf != nil)
+ n = strlen(buf);
+ if(!OpenClipboard(window))
+ return -1;
+
+ if(!EmptyClipboard()){
+ CloseClipboard();
+ return -1;
+ }
+
+ h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune16));
+ if(h == NULL)
+ error(Enovmem);
+ rp = GlobalLock(h);
+ utftorunes16(rp, buf, n+1);
+ GlobalUnlock(h);
+
+ SetClipboardData(CF_UNICODETEXT, h);
+
+ h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1);
+ if(h == NULL)
+ error(Enovmem);
+ p = GlobalLock(h);
+ memmove(p, buf, n);
+ p[n] = 0;
+ GlobalUnlock(h);
+
+ SetClipboardData(CF_TEXT, h);
+
+ CloseClipboard();
+ return n;
+}
--- /dev/null
+++ b/emu/OpenBSD/asm-386.S
@@ -1,0 +1,111 @@
+ .file "asm-OpenBSD-386.S"
+
+#include <sys/syscall.h>
+#include <machine/asm.h>
+
+#include "rfork_thread.S"
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+ .type ournewstack,@function
+ .global executeonnewstack
+executeonnewstack:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ movl 8(%ebp), %esi /* get tos */
+ subl $4, %esi
+ movl 16(%ebp), %eax
+ movl %eax, (%esi) /* stash arg on new stack */
+ subl $4, %esi
+ movl 12(%ebp), %eax
+ movl %eax, (%esi) /* stash tramp on new stack */
+ mov %esi, %esp /* swap stacks pronto */
+ popl %eax /* recover the tramp address */
+ call *%eax /* and jump to it (ho ho) */
+
+ /* if we return here, tramp didn't do it's job */
+
+ addl $8, %esp /* clean up for pose value */
+
+ leal SYS_exit, %eax
+ int $0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ * between the unlock and the system call, but this should
+ * not matter since no task is waiting for the result
+ */
+
+ .type unlockandexit,@function
+ .global unlockandexit
+unlockandexit:
+ pushl %ebp
+ movl %esp, %ebp
+
+ movl 8(%ebp), %esi /* get the key address */
+ pushl $0 /* exit status 0 */
+ movl $0, %eax /* unlock the stack allocator */
+ movl %eax, (%esi)
+ leal SYS_exit, %eax /* call exit */
+ int $0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type getcallerpc,@function
+ .global getcallerpc
+getcallerpc:
+ movl 4(%ebp), %eax
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/OpenBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_SPEAKER
+#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT
+#define Audio_Lineout_Val SOUND_MIXER_VOLUME
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING "/dev/dsp"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file = -1; /* file in/out */
+static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */
+static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ int i;
+ static ushort flag = 1;
+
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+ for (i = 0; i < 32; i++)
+ mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ char ebuf[ERRMAX];
+
+ if (debug)
+ print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file;
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+ if (debug)
+ print("audio_file_open: success\nin %d out %d both %d\n",
+ audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_out < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ qunlock(&outlock);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_in < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ audio_file_out = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ close(audio_file);
+ audio_file_in = audio_file_out = audio_file = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(! audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ if (debug > 1)
+ print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+ c, va, count, offset);
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+static int
+audio_open(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ if (debug)
+ print("audio_open: failed to set blocking\n");
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if (debug)
+ print("audio_open: blocking set\n");
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(! audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+ int status;
+ osenter();
+ status = ioctl(fd, ctl, info); /* qlock and load general stuff */
+ osleave();
+ if (status < 0)
+ print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+ return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+ int newbits, newenc;
+
+ newbits = i->bits;
+ newenc = i->enc;
+ switch (newenc) {
+ case Audio_Alaw_Val:
+ if (newbits == 8)
+ return AFMT_A_LAW;
+ break;
+ case Audio_Ulaw_Val:
+ if (newbits == 8)
+ return AFMT_MU_LAW;
+ break;
+ case Audio_Pcm_Val:
+ if (newbits == 8)
+ return AFMT_U8;
+ else if (newbits == 16)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+
+ if(fd < 0)
+ return 0;
+
+ /* fmt */
+ if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+ int oldfmt, newfmt;
+ oldfmt = AFMT_QUERY;
+ if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+ return 0;
+ if (debug)
+ print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+ newfmt = choosefmt(i);
+ if (debug)
+ print("audio_set_info: new format 0x%.8lux\n", newfmt);
+ if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG) {
+ int channels = i->chan;
+ if (debug)
+ print("audio_set_info: new channels %d\n", channels);
+ if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+ || channels != i->chan)
+ return 0;
+ }
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG) {
+ int speed = i->rate;
+ if (debug)
+ print("audio_set_info: new speed %d\n", speed);
+ if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+ return 0;
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+ int val;
+ if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+ mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+ if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+ mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+ val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+ doioctl(fd, MIXER_WRITE(i->dev), &val);
+ }
+
+ if (i->flags & AUDIO_DEV_FLAG) {
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ USED(fd);
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ USED(fd);
+ USED(f);
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int x;
+ return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/OpenBSD/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/OpenBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/cuaa0",
+ "/dev/cuaa1",
+ "/dev/cuaa2",
+ "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/OpenBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/OpenBSD/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/OpenBSD/mkfile
@@ -1,0 +1,45 @@
+<../../mkconfig
+SYSTARG=OpenBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ win-x11a.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm -lX11 -lXext -lossaudio
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/OpenBSD/mkfile-OpenBSD
@@ -1,0 +1,17 @@
+#
+# architecture-dependent files for OpenBSD
+#
+
+LDFLAGS=
+
+TARGFILES=devfs-posix.$O\
+ deveia-OpenBSD.$O\
+ devip.$O\
+ ipif-posix.$O\
+ os-OpenBSD.$O\
+ win-x11.$O\
+ srv.$O\
+ lock.$O\
+ asm-OpenBSD-$OBJTYPE.$O
+
+SYSLIBS=/usr/X11R6/lib/libX11.a -lm
--- /dev/null
+++ b/emu/OpenBSD/os.c
@@ -1,0 +1,533 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef getwd
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <time.h>
+#include <termios.h>
+#include <sched.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+
+enum
+{
+ DELETE = 0x7F,
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "OpenBSD";
+
+int rfork_thread(int, void *, void (*)(void *), void *);
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ void *kstack;
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(up->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", up->text, msg);
+
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ kstack = p->kstack;
+ free(p->prog);
+ free(p);
+ if(kstack != nil)
+ stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+ if(info)
+ print("trapBUS: signo: %d code: %d addr: %lx\n",
+ info->si_signo, info->si_code, info->si_addr);
+ else
+ print("trapBUS: no info\n");
+ disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+ USED(signo);
+ /* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+ char buf[64];
+ USED(signo);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+ disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+ struct sigaction act;
+ sigset_t mask;
+
+ memset(&act, 0 , sizeof(act));
+ sigemptyset(&initmask);
+
+ signal(SIGPIPE, SIG_IGN); /* prevent signal when devcmd child exits */
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+
+ act.sa_handler = trapUSR1;
+ act.sa_mask = initmask;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = trapUSR2;
+ sigaction(SIGUSR2, &act, nil);
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigaddset(&initmask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ /*
+ * prevent Zombies forming when any process terminates
+ */
+ act.sa_sigaction = 0;
+ act.sa_flags |= SA_NOCLDWAIT;
+ if(sigaction(SIGCHLD, &act, nil))
+ panic("sigaction SIGCHLD");
+
+ if(sflag == 0) {
+ act.sa_sigaction = trapBUS;
+ act.sa_flags |= SA_SIGINFO;
+ if(sigaction(SIGBUS, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapILL;
+ if(sigaction(SIGILL, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapSEGV;
+ if(sigaction(SIGSEGV, &act, nil))
+ panic("sigaction SIGSEGV");
+ act.sa_handler = trapFPE;
+ if(sigaction(SIGFPE, &act, nil))
+ panic("sigaction SIGFPE");
+ if(sigaddset(&initmask, SIGINT) == -1)
+ panic("sigaddset");
+ }
+ if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+ panic("sigprocmask");
+}
+
+static void
+tramp(void *arg)
+{
+ Proc *p;
+
+ p = arg;
+ p->pid = p->sigid = getpid();
+ sigprocmask(SIG_BLOCK, &initmask, nil); /* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+ (*p->func)(p->arg);
+ pexit("{Tramp}", 0);
+ _exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ int pid;
+ void *tos;
+
+ p = newproc();
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(flags & KPX11){
+ p->kstack = nil; /* never freed; also up not defined */
+ tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+ }else
+ p->kstack = stackalloc(p, &tos);
+ pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+ if(pid < 0)
+ panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGUSR2);
+ sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+ if(kill(p->sigid, SIGUSR2) < 0)
+ fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+ struct passwd *pwd;
+
+ if(pwd = getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+libinit(char *imod)
+{
+ struct passwd *pw;
+ Proc *p;
+ void *tos;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ if(dflag == 0)
+ termset();
+
+ setsigs();
+
+ p = newproc();
+ p->kstack = stackalloc(p, &tos);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+ if(sec0==0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec / 1000;
+ time.tv_nsec = (milsec % 1000) * 1000000;
+ nanosleep(&time, 0);
+ return 0;
+}
+
+void
+osyield(void)
+{
+ sched_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+ Lock l;
+ void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+ *((void **)stack) = stacklist.free;
+ stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+ lock(&stacklist.l);
+ _stackfree(stack);
+ unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+ void *rv;
+ lock(&stacklist.l);
+ if (stacklist.free == 0) {
+ int x;
+ /*
+ * obtain some more by using sbrk()
+ */
+ void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+ if (more == 0)
+ panic("stackalloc: no more stacks");
+ /*
+ * align to KSTACK
+ */
+ more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+ /*
+ * free all the new stacks onto the freelist
+ */
+ for (x = 0; x < NSTACKSPERALLOC; x++)
+ _stackfree((char *)more + KSTACK * x);
+ }
+ rv = stacklist.free;
+ stacklist.free = *(void **)rv;
+ unlock(&stacklist.l);
+ *tos = rv + KSTACK - sizeof(void*);
+ *(Proc **)rv = p;
+ return rv;
+}
+
+int
+segflush(void *p, ulong n)
+{
+ return mprotect(p, n, PROT_EXEC|PROT_READ|PROT_WRITE);
+}
--- /dev/null
+++ b/emu/OpenBSD/rfork_thread.S
@@ -1,0 +1,104 @@
+/*-
+ * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * 8 12 16 20
+ * rfork_thread(flags, stack_addr, start_fnc, start_arg);
+ *
+ * flags: Flags to rfork system call. See rfork(2).
+ * stack_addr: Top of stack for thread.
+ * start_fnc: Address of thread function to call in child.
+ * start_arg: Argument to pass to the thread function in child.
+ */
+
+ENTRY(rfork_thread)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ /*
+ * Push thread info onto the new thread's stack
+ */
+ movl 12(%ebp), %esi # get stack addr
+
+ subl $4, %esi
+ movl 20(%ebp), %eax # get start argument
+ movl %eax, (%esi)
+
+ subl $4, %esi
+ movl 16(%ebp), %eax # get start thread address
+ movl %eax, (%esi)
+
+ /*
+ * Prepare and execute the thread creation syscall
+ */
+ pushl 8(%ebp)
+ pushl $0
+ movl $SYS_rfork, %eax
+ int $0x80
+ jb 2f
+
+ /*
+ * Check to see if we are in the parent or child
+ */
+ cmpl $0, %edx
+ jnz 1f
+ addl $8, %esp
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+ ret
+ .p2align 2
+
+ /*
+ * If we are in the child (new thread), then
+ * set-up the call to the internal subroutine. If it
+ * returns, then call __exit.
+ */
+1:
+ movl %esi,%esp
+ popl %eax
+ call *%eax
+ addl $4, %esp
+
+ /*
+ * Exit system call
+ */
+ pushl %eax
+ pushl $0
+ movl $SYS_threxit, %eax
+ int $0x80
+
+ /*
+ * Branch here if the thread creation fails:
+ */
+2:
+ addl $8, %esp
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+ PIC_PROLOGUE
+ jmp PIC_PLT(_C_LABEL(__cerror))
--- /dev/null
+++ b/emu/Plan9/asm-386.s
@@ -1,0 +1,33 @@
+
+TEXT tramp(SB),$0
+ MOVL nsp+0(FP), BX /* new stack */
+ MOVL fn+4(FP), CX /* func to exec */
+ MOVL arg+8(FP),DX
+
+ LEAL -8(BX), SP /* new stack */
+ PUSHL DX
+ CALL *CX
+ POPL AX
+
+ PUSHL $0
+ CALL _exits(SB)
+ POPL AX
+ RET
+
+TEXT vstack(SB),$0
+ MOVL arg+0(FP), AX
+ MOVL ustack(SB), SP
+ PUSHL AX
+ CALL exectramp(SB)
+ POPL AX /* dammit ken! */
+ RET
+
+TEXT FPsave(SB), 1, $0
+ MOVL fpu+0(FP), AX
+ FSTENV 0(AX)
+ RET
+
+TEXT FPrestore(SB), 1, $0
+ MOVL fpu+0(FP), AX
+ FLDENV 0(AX)
+ RET
--- /dev/null
+++ b/emu/Plan9/asm-arm.s
@@ -1,0 +1,32 @@
+/* for VFP */
+#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */
+#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */
+
+#define Fpscr 1
+
+ TEXT tramp(SB), 1, $0
+ MOVW fn+4(FP), R1 /* func to exec */
+ MOVW arg+8(FP), R2 /* argument */
+ SUB $8, R0 /* new stack */
+ MOVW R0, SP
+ MOVW R2, R0
+ BL (R1)
+
+ MOVW $0, R0
+ BL _exits(SB)
+ RET
+
+ TEXT vstack(SB), 1, $0
+ MOVW ustack(SB), SP
+ BL exectramp(SB)
+ RET
+
+ TEXT FPsave(SB), 1, $0
+ VMRS(Fpscr, 1)
+ MOVW R1, 0(R0)
+ RET
+
+ TEXT FPrestore(SB), 1, $0
+ MOVW (R0), R0
+ VMSR(0, Fpscr)
+ RET
--- /dev/null
+++ b/emu/Plan9/asm-mips.s
@@ -1,0 +1,24 @@
+
+ TEXT tramp(SB), 1, $0
+ ADDU $-8, R1, R3 /* new stack */
+ MOVW 4(FP), R2 /* func to exec */
+ MOVW 8(FP), R1 /* arg to reg */
+ MOVW R3, R29 /* new stack */
+ JAL (R2)
+ MOVW R0, R1
+ JMP _exits(SB)
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R1 */
+ MOVW ustack(SB), R29
+ JMP exectramp(SB)
+ RET
+
+ TEXT FPsave(SB), 1, $0
+ MOVW FCR31, R2
+ MOVW R2, 0(R1)
+ RET
+
+ TEXT FPrestore(SB), 1, $0
+ MOVW 0(R1), R2
+ MOVW R2, FCR31
+ RET
--- /dev/null
+++ b/emu/Plan9/asm-power.s
@@ -1,0 +1,28 @@
+ TEXT tramp(SB), 1, $0
+ ADD $-8, R3, R4 /* new stack */
+ MOVW 4(FP), R5 /* func to exec */
+ MOVW R5, LR
+ MOVW 8(FP), R3 /* arg to reg */
+ MOVW R4, R1 /* new stack */
+ BL (LR)
+ MOVW R0, R3
+ MOVW $_exits(SB), R4
+ MOVW R4, LR
+ BR (LR)
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R3 */
+ MOVW ustack(SB), R1
+ MOVW $exectramp(SB), R4
+ MOVW R4, CTR
+ BR (CTR)
+ RETURN
+
+ TEXT FPsave(SB), 1, $0
+ MOVFL FPSCR, F0
+ FMOVD F0, 0(R3)
+ RETURN
+
+ TEXT FPrestore(SB), 1, $0
+ FMOVD 0(R3), F0
+ MOVFL F0, FPSCR
+ RETURN
--- /dev/null
+++ b/emu/Plan9/asm-sparc.s
@@ -1,0 +1,22 @@
+ TEXT tramp(SB), 1, $0
+ ADD $-8, R7, R3 /* new stack */
+ MOVW 4(FP), R4 /* func to exec */
+ MOVW 8(FP), R7 /* arg to reg */
+ MOVW R3, R1 /* new stack */
+ JMPL (R4)
+ MOVW R0, R7
+ JMPL _exits(SB) /* Leaks the stack in R29 */
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R7 */
+ MOVW ustack(SB), R1
+ MOVW $exectramp(SB), R3
+ JMP (R3)
+ RETURN
+
+ TEXT FPsave(SB), 1, $0
+ MOVW FSR, 0(R7)
+ RETURN
+
+ TEXT FPrestore(SB), 1, $0
+ MOVW 0(R7), FSR
+ RETURN
--- /dev/null
+++ b/emu/Plan9/cmd.c
@@ -1,0 +1,189 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+extern void vstack(void*);
+
+/*
+ * all this is for the benefit of devcmd.
+ * i hope it's grateful.
+ */
+
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* standard input, output and error */
+ int wfd;
+ int* spin;
+ char** args;
+ char* dir;
+ int pid;
+ int nice;
+};
+
+/*
+ * called by vstack once it has moved to
+ * the unshared stack in the new process.
+ */
+void
+exectramp(Targ *t)
+{
+ int *fd, i, nfd;
+ char filename[128], err[ERRMAX], status[2*ERRMAX];
+
+ t->pid = getpid();
+ *t->spin = 0; /* allow parent to proceed: can't just rendezvous: see below */
+ fd = t->fd;
+
+ snprint(filename, sizeof(filename), "#d/%d", t->wfd);
+ t->wfd = open(filename, OWRITE|OCEXEC);
+ /* if it failed, we'll manage */
+
+ nfd = MAXNFD; /* TO DO: should read from /fd */
+ for(i = 0; i < nfd; i++)
+ if(i != fd[0] && i != fd[1] && i != fd[2] && i != t->wfd)
+ close(i);
+
+ if(fd[0] != 0){
+ dup(fd[0], 0);
+ close(fd[0]);
+ }
+ if(fd[1] != 1){
+ dup(fd[1], 1);
+ close(fd[1]);
+ }
+ if(fd[2] != 2){
+ dup(fd[2], 2);
+ close(fd[2]);
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ if(t->wfd > 0)
+ fprint(t->wfd, "chdir: %s: %r", t->dir);
+ _exits("bad dir");
+ }
+ if(t->nice)
+ oslopri();
+
+ exec(t->args[0], t->args);
+ err[0] = 0;
+ errstr(err, sizeof(err));
+ if(t->args[0][0] != '/' && t->args[0][0] != '#' &&
+ strncmp(t->args[0], "../", 3) != 0 && strncmp(t->args[0], "./", 2) != 0 &&
+ strlen(t->args[0])+5 < sizeof(filename)){
+ snprint(filename, sizeof(filename), "/bin/%s", t->args[0]);
+ exec(filename, t->args);
+ errstr(err, sizeof(err));
+ }
+ snprint(status, sizeof(status), "%s: can't exec: %s", t->args[0], err);
+ if(t->wfd > 0)
+ write(t->wfd, status, strlen(status));
+ _exits(status);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int spin, *spinptr, fd0[2], fd1[2], fd2[2], wfd[2], n;
+ Dir *d;
+
+ up->genbuf[0] = 0;
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+ t->args = args;
+ t->dir = dir;
+ t->nice = nice;
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(dir != nil){
+ d = dirstat(dir);
+ if(d == nil)
+ goto Error;
+ free(d);
+ }
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+
+ spinptr = &spin;
+ spin = 1;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->spin = spinptr;
+ switch(rfork(RFPROC|RFMEM|RFREND|RFNOTEG|RFFDG|RFNAMEG|RFENVG)) {
+ case -1:
+ goto Error;
+ case 0:
+ /* if child returns first from rfork, its call to vstack replaces ... */
+ vstack(t);
+ /* ... parent's return address from rfork and parent returns here */
+ default:
+ /* if parent returns first from rfork, it comes here */
+ /* can't call anything: on shared stack until child releases spin in exectramp */
+ while(*spinptr)
+ ;
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ up->genbuf[n] = 0;
+ errstr(up->genbuf, sizeof(up->genbuf));
+ free(t);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ errstr(up->genbuf, sizeof(up->genbuf)); /* save the message before close */
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ free(t);
+ errstr(up->genbuf, sizeof(up->genbuf));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ return postnote(PNGROUP, t->pid, "kill");
+}
+
+int
+oscmdwait(void*, char *buf, int n)
+{
+ return await(buf, n);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Plan9/devfs.c
@@ -1,0 +1,365 @@
+/*
+ * Plan 9 file system interface
+ */
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+typedef struct Fsinfo Fsinfo;
+struct Fsinfo
+{
+ int fd;
+ QLock; /* serialise access to offset */
+ ulong offset; /* offset used only for directory reads */
+ Cname* name; /* Plan 9's name for file */
+ Qid rootqid; /* Plan 9's qid for Inferno's root */
+ char* root; /* prefix to strip from all names in diagnostics */
+};
+#define FS(c) ((Fsinfo*)((c)->aux))
+
+char rootdir[MAXROOT] = ROOT;
+
+static void
+fserr(Fsinfo *f)
+{
+ int n;
+ char *p;
+
+ oserrstr(up->env->errstr, ERRMAX);
+ if(f != nil && *up->env->errstr == '\'' && (n = strlen(f->root)) > 1){
+ /* don't reveal full names */
+ if(strncmp(up->env->errstr+1, f->root, n-1) == 0){
+ p = up->env->errstr+1+n;
+ memmove(up->env->errstr+1, p, strlen(p)+1);
+ }
+ }
+ error(up->env->errstr);
+}
+
+static void
+fsfree(Chan *c)
+{
+ cnameclose(FS(c)->name);
+ free(c->aux);
+}
+
+Chan*
+fsattach(char *spec)
+{
+ Chan *c;
+ Dir *d;
+ char *root;
+ Qid rootqid;
+ static int devno;
+ static Lock l;
+
+ if(!emptystr(spec)){
+ if(strcmp(spec, "*") != 0)
+ error(Ebadspec);
+ root = "/";
+ }else
+ root = rootdir;
+
+ d = dirstat(root);
+ if(d == nil)
+ fserr(nil);
+ rootqid = d->qid;
+ free(d);
+
+ c = devattach('U', spec);
+ lock(&l);
+ c->dev = devno++;
+ c->qid = rootqid;
+ unlock(&l);
+ c->aux = smalloc(sizeof(Fsinfo));
+ FS(c)->name = newcname(root);
+ FS(c)->rootqid = rootqid;
+ FS(c)->fd = -1;
+ FS(c)->root = root;
+
+ return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j, alloc;
+ Walkqid *wq;
+ Dir *dir;
+ char *n;
+ Cname *current, *next;
+ Qid rootqid;
+
+ if(nname > 0)
+ isdir(c); /* do we need this? */
+
+ alloc = 0;
+ current = nil;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone!=nil)
+ cclose(wq->clone);
+ cnameclose(current);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0;
+ alloc = 1;
+ }
+ wq->clone = nc;
+
+ rootqid = FS(c)->rootqid;
+ current = FS(c)->name;
+ if(current != nil)
+ incref(¤t->r);
+ for(j=0; j<nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ n = name[j];
+ if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){ /* TO DO: underlying qids aliased */
+ //print("** ufs walk '%s' -> %s\n", current->s, n);
+ next = current;
+ incref(&next->r);
+ next = addelem(current, n);
+ dir = dirstat(next->s);
+ if(dir == nil){
+ cnameclose(next);
+ if(j == 0)
+ error(Enonexist);
+ strcpy(up->env->errstr, Enonexist);
+ break;
+ }
+ nc->qid = dir->qid;
+ free(dir);
+ cnameclose(current);
+ current = next;
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+// print("** ufs walk '%s'\n", current->s);
+
+ poperror();
+ if(wq->nqid < nname){
+ cnameclose(current);
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ /* now attach to our device */
+ nc->aux = smalloc(sizeof(Fsinfo));
+ nc->type = c->type;
+ FS(nc)->rootqid = FS(c)->rootqid;
+ FS(nc)->name = current;
+ FS(nc)->fd = -1;
+ FS(nc)->root = FS(c)->root;
+ }else
+ panic("fswalk: can't happen");
+ return wq;
+}
+
+int
+fsstat(Chan *c, uchar *dp, int n)
+{
+ if(FS(c)->fd >= 0)
+ n = fstat(FS(c)->fd, dp, n);
+ else
+ n = stat(FS(c)->name->s, dp, n);
+ if(n < 0)
+ fserr(FS(c));
+ /* TO DO: change name to / if rootqid */
+ return n;
+}
+
+Chan*
+fsopen(Chan *c, int mode)
+{
+ osenter();
+ FS(c)->fd = open(FS(c)->name->s, mode);
+ osleave();
+ if(FS(c)->fd < 0)
+ fserr(FS(c));
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+ Dir *d;
+ Cname *n;
+
+ if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ error(Efilename);
+ n = addelem(newcname(FS(c)->name->s), name);
+ osenter();
+ FS(c)->fd = create(n->s, mode, perm);
+ osleave();
+ if(FS(c)->fd < 0) {
+ cnameclose(n);
+ fserr(FS(c));
+ }
+ d = dirfstat(FS(c)->fd);
+ if(d == nil) {
+ cnameclose(n);
+ close(FS(c)->fd);
+ FS(c)->fd = -1;
+ fserr(FS(c));
+ }
+ c->qid = d->qid;
+ free(d);
+
+ cnameclose(FS(c)->name);
+ FS(c)->name = n;
+
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+}
+
+void
+fsclose(Chan *c)
+{
+ if(c->flag & COPEN){
+ osenter();
+ close(FS(c)->fd);
+ osleave();
+ }
+ /* don't need to check for CRCLOSE, because Plan 9 itself implements ORCLOSE */
+ fsfree(c);
+}
+
+static long
+fsdirread(Chan *c, void *va, long count, vlong offset)
+{
+ long n, r;
+ static char slop[16384];
+
+ if(FS(c)->offset != offset){
+ seek(FS(c)->fd, 0, 0);
+ for(n=0; n<offset;) {
+ r = offset - n;
+ if(r > sizeof(slop))
+ r = sizeof(slop);
+ osenter();
+ r = read(FS(c)->fd, slop, r);
+ osleave();
+ if(r <= 0){
+ FS(c)->offset = n;
+ return 0;
+ }
+ n += r;
+ }
+ FS(c)->offset = offset;
+ }
+ osenter();
+ r = read(FS(c)->fd, va, count);
+ osleave();
+ if(r < 0)
+ return r;
+ FS(c)->offset = offset+r;
+ return r;
+}
+
+long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+ int r;
+
+ if(c->qid.type & QTDIR){ /* need to maintain offset only for directories */
+ qlock(FS(c));
+ if(waserror()){
+ qunlock(FS(c));
+ nexterror();
+ }
+ r = fsdirread(c, va, n, offset);
+ poperror();
+ qunlock(FS(c));
+ }else{
+ osenter();
+ r = pread(FS(c)->fd, va, n, offset);
+ osleave();
+ }
+ if(r < 0)
+ fserr(FS(c));
+ return r;
+}
+
+long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+ int r;
+
+ osenter();
+ r = pwrite(FS(c)->fd, va, n, offset);
+ osleave();
+ if(r < 0)
+ fserr(FS(c));
+ return r;
+}
+
+void
+fsremove(Chan *c)
+{
+ int r;
+
+ if(waserror()){
+ fsfree(c);
+ nexterror();
+ }
+ osenter();
+ r = remove(FS(c)->name->s);
+ osleave();
+ if(r < 0)
+ fserr(FS(c));
+ poperror();
+ fsfree(c);
+}
+
+int
+fswstat(Chan *c, uchar *dp, int n)
+{
+ osenter();
+ if(FS(c)->fd >= 0)
+ n = fwstat(FS(c)->fd, dp, n);
+ else
+ n = wstat(FS(c)->name->s, dp, n);
+ osleave();
+ if(n < 0)
+ fserr(FS(c));
+ return n;
+}
+
+void
+setid(char *name, int owner)
+{
+ if(!owner || iseve())
+ kstrdup(&up->env->user, name);
+}
+
+Dev fsdevtab = {
+ 'U',
+ "fs",
+
+ devinit,
+ fsattach,
+ fswalk,
+ fsstat,
+ fsopen,
+ fscreate,
+ fsclose,
+ fsread,
+ devbread,
+ fswrite,
+ devbwrite,
+ fsremove,
+ fswstat
+};
--- /dev/null
+++ b/emu/Plan9/devsrv9.c
@@ -1,0 +1,395 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+typedef struct Srv Srv;
+struct Srv
+{
+ Ref;
+ int fd; /* fd for opened /srv or /srv/X, or -1 */
+ int sfd; /* fd for created /srv entry or -1 */
+ uvlong path;
+ Srv *next;
+};
+
+static QLock srv9lk;
+static Srv *srv9;
+static Srv *srvroot;
+
+static char*
+srvname(Chan *c)
+{
+ char *p;
+
+ p = strrchr(c->name->s, '/');
+ if(p == nil)
+ return "";
+ return p+1;
+}
+
+static Srv*
+srvget(uvlong path)
+{
+ Srv *sv;
+
+ qlock(&srv9lk);
+ for(sv = srv9; sv != nil; sv = sv->next)
+ if(sv->path == path){
+ incref(sv);
+ qunlock(&srv9lk);
+ return sv;
+ }
+ sv = smalloc(sizeof(*sv));
+ sv->path = path;
+ sv->fd = -1;
+ sv->sfd = -1;
+ sv->ref = 1;
+ sv->next = srv9;
+ srv9 = sv;
+ qunlock(&srv9lk);
+ return sv;
+}
+
+static void
+srvput(Srv *sv)
+{
+ Srv **l;
+ int fd, sfd;
+
+ if(sv != nil && decref(sv) == 0){
+ qlock(&srv9lk);
+ for(l = &srv9; *l != nil; l = &(*l)->next)
+ if(*l == sv){
+ *l = sv->next;
+ break;
+ }
+ qunlock(&srv9lk);
+ fd = sv->fd;
+ sfd = sv->sfd;
+ free(sv);
+ if(sfd >= 0){
+ osenter();
+ close(sfd);
+ osleave();
+ }
+ if(fd >= 0){
+ osenter();
+ close(fd);
+ osleave();
+ }
+ }
+}
+
+static void
+srv9init(void)
+{
+ Srv *sv;
+
+ sv = mallocz(sizeof(*srvroot), 1);
+ sv->path = 0;
+ sv->fd = -1;
+ sv->ref = 1; /* subsequently never reaches zero */
+ srvroot = srv9 = sv;
+}
+
+static Chan*
+srv9attach(char *spec)
+{
+ Chan *c;
+
+ if(*spec)
+ error(Ebadspec);
+ c = devattach(L'₪', spec);
+ if(c != nil){
+ incref(srvroot);
+ c->aux = srvroot;
+ }
+ return c;
+}
+
+static Walkqid*
+srv9walk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j, alloc;
+ Walkqid *wq;
+ char *n;
+ Dir *d;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc)
+ cclose(wq->clone);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0; /* device doesn't know about this channel yet */
+ alloc = 1;
+ }
+ wq->clone = nc;
+
+ for(j=0; j<nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ n = name[j];
+ if(strcmp(n, ".") != 0 && strcmp(n, "..") != 0){
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", n);
+ d = dirstat(up->genbuf);
+ if(d == nil){
+ if(j == 0)
+ error(Enonexist);
+ kstrcpy(up->env->errstr, Enonexist, ERRMAX);
+ break;
+ }
+ nc->qid = d->qid;
+ free(d);
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+ poperror();
+ if(wq->nqid < nname){
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else{
+ /* attach cloned channel to device */
+ wq->clone->type = c->type;
+ if(wq->clone != c)
+ nc->aux = srvget(nc->qid.path);
+ }
+ return wq;
+}
+
+static int
+srv9stat(Chan *c, uchar *db, int n)
+{
+ Srv *sv;
+ Dir d;
+
+ if(c->qid.type & QTDIR){
+ devdir(c, c->qid, "#₪", 0, eve, 0775, &d);
+ n = convD2M(&d, db, n);
+ if(n == 0)
+ error(Eshortstat);
+ return n;
+ }
+ sv = c->aux;
+ if(sv->fd >= 0){
+ osenter();
+ n = fstat(sv->fd, db, n);
+ osleave();
+ }else{
+ osenter();
+ n = stat(srvname(c), db, n);
+ osleave();
+ }
+ return n;
+}
+
+static Chan*
+srv9open(Chan *c, int omode)
+{
+ Srv *sv;
+ char *args[10];
+ int fd[2], i, ifd, is9p;
+ Dir *d;
+
+ sv = c->aux;
+ if(c->qid.type == QTDIR){
+ osenter();
+ sv->fd = open("/srv", omode);
+ osleave();
+ if(sv->fd < 0)
+ oserror();
+ c->mode = omode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ if(omode&OTRUNC || openmode(omode) != ORDWR)
+ error(Eperm);
+ if(sv->fd < 0){
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", srvname(c));
+
+ /* check permission */
+ osenter();
+ ifd = open(up->genbuf, omode);
+ osleave();
+ if(ifd < 0)
+ oserror();
+ osenter();
+ d = dirfstat(ifd);
+ is9p = d != nil && d->qid.type & QTMOUNT;
+ free(d);
+ osleave();
+
+ if(is9p){
+ close(ifd);
+
+ /* spawn exportfs */
+ args[0] = "exportfs";
+ args[1] = "-S";
+ args[2] = up->genbuf;
+ args[3] = nil;
+ if(pipe(fd) < 0)
+ oserror();
+ /* TO DO: without RFMEM there's a copy made of each page touched by any kproc until the exec */
+ switch(rfork(RFPROC|RFNOWAIT|RFREND|RFFDG|RFNAMEG|RFENVG)){ /* no sharing except NOTEG */
+ case -1:
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ close(fd[0]);
+ close(fd[1]);
+ error(up->genbuf);
+ case 0:
+ for(i=3; i<MAXNFD; i++)
+ if(i != fd[1])
+ close(i);
+ dup(fd[1], 0);
+ if(fd[0] != 0)
+ close(fd[0]);
+ dup(0, 1);
+ exec("/bin/exportfs", args);
+ exits("exportfs failed");
+ default:
+ sv->fd = fd[0];
+ close(fd[1]);
+ break;
+ }
+ }else
+ sv->fd = ifd;
+ }
+
+ c->mode = ORDWR;
+ c->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+static void
+srv9close(Chan *c)
+{
+ srvput(c->aux);
+}
+
+static long
+srv9read(Chan *c, void *va, long n, vlong off)
+{
+ Srv *sv;
+
+ sv = c->aux;
+ osenter();
+ n = pread(sv->fd, va, n, off);
+ osleave();
+ if(n < 0)
+ oserror();
+ return n;
+}
+
+static long
+srv9write(Chan *c, void *va, long n, vlong off)
+{
+ Srv *sv;
+
+ sv = c->aux;
+ osenter();
+ n = pwrite(sv->fd, va, n, off);
+ osleave();
+ if(n == 0)
+ error(Ehungup);
+ if(n < 0)
+ oserror();
+ return n;
+}
+
+static void
+srv9create(Chan *c, char *name, int omode, ulong perm)
+{
+ Srv *sv;
+ int sfd, fd[2];
+ vlong path;
+ Dir *d;
+
+ if(openmode(omode) != ORDWR)
+ error(Eperm);
+
+ if(pipe(fd) < 0)
+ oserror();
+ if(waserror()){
+ close(fd[0]);
+ close(fd[1]);
+ nexterror();
+ }
+
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", name);
+ osenter();
+ sfd = create(up->genbuf, OWRITE|ORCLOSE, perm);
+ osleave();
+ if(sfd < 0)
+ oserror();
+ if(waserror()){
+ close(sfd);
+ nexterror();
+ }
+ osenter();
+ if(fprint(sfd, "%d", fd[1]) < 0){
+ osleave();
+ oserror();
+ }
+ d = dirfstat(sfd);
+ osleave();
+ if(d != nil){
+ path = d->qid.path;
+ free(d);
+ }else
+ oserror();
+
+ poperror();
+ poperror();
+ close(fd[1]);
+
+ if(waserror()){
+ close(sfd);
+ close(fd[0]);
+ nexterror();
+ }
+ sv = srvget(path);
+ sv->fd = fd[0];
+ sv->sfd = sfd;
+ poperror();
+
+ srvput((Srv*)c->aux);
+ c->qid.type = QTFILE;
+ c->qid.path = path;
+ c->aux = sv;
+ c->flag |= COPEN;
+ c->mode = ORDWR;
+ c->offset = 0;
+}
+
+Dev srv9devtab = {
+ L'₪',
+ "srv9",
+
+ srv9init,
+ srv9attach,
+ srv9walk,
+ srv9stat,
+ srv9open,
+ srv9create, /* TO DO */
+ srv9close,
+ srv9read,
+ devbread,
+ srv9write,
+ devbwrite,
+ devremove, /* TO DO */
+ devwstat, /* TO DO */
+};
--- /dev/null
+++ b/emu/Plan9/emu
@@ -1,0 +1,114 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+# sign
+
+ draw win
+ pointer
+
+ dynld
+ mem
+ srv9
+
+# ip and eia are simply bound in from Plan 9
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+ dynld
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+# srv not used on Plan 9
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ exptab
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /chan /
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /nvfs /
+ /env /
+ /root /
+ /srv /
+# /tmp /
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Plan9/emusig
@@ -1,0 +1,100 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+ sign
+
+ draw win
+
+# ip and eia are simply bound in from Plan 9
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+ memlayer
+ memdraw
+ keyring
+ crypt
+ 9
+
+link
+
+mod
+ sys
+ draw
+ tk
+ math
+# srv not used on Plan 9
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Plan9/mkfile
@@ -1,0 +1,45 @@
+SYSTARG=Plan9
+OBJTYPE=$objtype
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ $DEVS\
+ $PORT\
+
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+safeinstall:V: $O.$CONF
+ mv $INSTALLDIR/$CONF $INSTALLDIR/$CONF.`{date -n}
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
--- /dev/null
+++ b/emu/Plan9/os.c
@@ -1,0 +1,428 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ KSTACK = 16*1024,
+ DELETE = 0x7F,
+};
+
+Proc **Xup;
+
+extern void killrefresh(void);
+extern void tramp(char*, void (*)(void*), void*);
+
+extern int usenewwin;
+
+int *ustack; /* address on unshared stack: see vstack in asm*.s */
+extern int dflag;
+char *hosttype = "Plan9";
+char *cputype;
+
+void
+osblock(void)
+{
+ rendezvous(up, nil);
+}
+
+void
+osready(Proc *p)
+{
+ rendezvous(p, nil);
+}
+
+void
+pexit(char *msg, int)
+{
+ Osenv *e;
+
+ USED(msg);
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+/* print("pexit: %s: %s\n", up->text, msg); /**/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(e->user);
+ free(up->prog);
+ up->prog = nil;
+ up->type = Moribund;
+ longjmp(up->privstack, 1);
+}
+
+int
+kproc1(char *name, void (*func)(void*), void *arg, int flags)
+{
+ int pid;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+
+ p = newproc();
+ if(p == nil)
+ panic("kproc: no memory");
+ p->kstack = mallocz(KSTACK, 0);
+ if(p->kstack == nil)
+ panic("kproc: no memory");
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ /*
+ * switch back to the unshared stack to do the fork
+ * only the parent returns from kproc
+ */
+ up->kid = p;
+ up->kidsp = p->kstack;
+ pid = setjmp(up->sharestack);
+ if(pid == 0)
+ longjmp(up->privstack, 1);
+ return pid;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ kproc1(name, func, arg, flags);
+}
+
+void
+traphandler(void *reg, char *msg)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0;
+ /* Ignore pipe writes from devcmd */
+ if(strstr(msg, "write on closed pipe") != nil)
+ noted(NCONT);
+
+ if(sflag) {
+ if(intwait && strcmp(msg, Eintr) == 0)
+ noted(NCONT);
+ else
+ noted(NDFLT);
+ }
+ if(intwait == 0)
+ disfault(reg, msg);
+ noted(NCONT);
+}
+
+int
+readfile(char *path, char *buf, int n)
+{
+ int fd;
+
+ fd = open(path, OREAD);
+ if(fd >= 0) {
+ n = read(fd, buf, n-1);
+ if(n > 0) /* both calls to readfile() have a ``default'' */
+ buf[n] = '\0';
+ close(fd);
+ return n;
+ }
+ return 0;
+}
+
+static void
+dobinds(void)
+{
+ char dir[MAXROOT+9];
+
+ snprint(dir, sizeof(dir), "%s/net", rootdir);
+ bind("/net", dir, MREPL);
+
+ snprint(dir, sizeof(dir), "%s/net.alt", rootdir);
+ bind("/net.alt", dir, MREPL);
+
+ snprint(dir, sizeof(dir), "%s/dev", rootdir);
+ bind("#t", dir, MAFTER);
+ bind("#A", dir, MAFTER);
+}
+
+void
+libinit(char *imod)
+{
+ char *sp;
+ Proc *xup, *p;
+ int fd, n, pid;
+ char nbuf[64];
+
+ xup = nil;
+ Xup = &xup;
+
+ /*
+ * setup personality
+ */
+ if(readfile("/dev/user", nbuf, sizeof nbuf))
+ kstrdup(&eve, nbuf);
+ if(readfile("/dev/sysname", nbuf, sizeof nbuf))
+ kstrdup(&ossysname, nbuf);
+ if(readfile("/env/cputype", nbuf, sizeof nbuf))
+ kstrdup(&cputype, nbuf);
+
+ /*
+ * guess at a safe stack for vstack
+ */
+ ustack = &fd;
+
+ rfork(RFNAMEG|RFREND);
+
+ if(!dflag){
+ fd = open("/dev/consctl", OWRITE);
+ if(fd < 0)
+ fprint(2, "libinit: open /dev/consctl: %r\n");
+ n = write(fd, "rawon", 5);
+ if(n != 5)
+ fprint(2, "keyboard rawon (n=%d, %r)\n", n);
+ }
+
+ osmillisec(); /* set the epoch */
+ dobinds();
+
+ notify(traphandler);
+
+ /*
+ * dummy up a up and stack so the first proc
+ * calls emuinit after setting up his private jmp_buf
+ */
+ p = newproc();
+ p->kstack = mallocz(KSTACK, 0);
+ if(p == nil || p->kstack == nil)
+ panic("libinit: no memory");
+ sp = p->kstack;
+ p->func = emuinit;
+ p->arg = imod;
+
+ /*
+ * set up a stack for forking kids on separate stacks.
+ * longjmp back here from kproc.
+ */
+ while(setjmp(p->privstack)){
+ if(up->type == Moribund){
+ free(up->kstack);
+ free(up);
+ _exits("");
+ }
+ p = up->kid;
+ sp = up->kidsp;
+ up->kid = nil;
+ switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
+ case 0:
+ /*
+ * send the kid around the loop to set up his private jmp_buf
+ */
+ break;
+ default:
+ /*
+ * parent just returns to his shared stack in kproc
+ */
+ longjmp(up->sharestack, pid);
+ panic("longjmp failed");
+ }
+ }
+
+ /*
+ * you get here only once per Proc
+ * go to the shared memory stack
+ */
+ up = p;
+ up->pid = up->sigid = getpid();
+ tramp(sp+KSTACK, up->func, up->arg);
+ panic("tramp returned");
+}
+
+void
+oshostintr(Proc *p)
+{
+ postnote(PNPROC, p->sigid, Eintr);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ if(regs != nil)
+ notejmp(regs, env, val);
+ else
+ longjmp(env, val);
+}
+
+void
+osreboot(char*, char**)
+{
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+ killrefresh();
+ postnote(PNGROUP, getpid(), "interrupt");
+ exits("interrupt");
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "emu: keyboard read error: %r\n");
+ if(n <= 0)
+ pexit("keyboard", 0);
+ switch(buf[0]) {
+ case DELETE:
+ cleanexit(0);
+ case '\r':
+ buf[0] = '\n';
+ }
+ return buf[0];
+}
+
+static vlong
+b2v(uchar *p)
+{
+ int i;
+ vlong v;
+
+ v = 0;
+ for(i=0; i<sizeof(uvlong); i++)
+ v = (v<<8)|p[i];
+ return v;
+}
+
+vlong
+nsec(void)
+{
+ int n;
+ static int nsecfd = -1;
+ uchar buf[sizeof(uvlong)];
+
+ if(nsecfd < 0){
+ nsecfd = open("/dev/bintime", OREAD|OCEXEC); /* never closed */
+ if(nsecfd<0){
+ fprint(2,"can't open /dev/bintime: %r\n");
+ return 0;
+ }
+ }
+ n = read(nsecfd, buf, sizeof(buf));
+ if(n!=sizeof(buf)) {
+ fprint(2,"read err on /dev/bintime: %r\n");
+ return 0;
+ }
+ return b2v(buf);
+}
+
+long
+osmillisec(void)
+{
+ static vlong nsec0 = 0;
+
+ if(nsec0 == 0){
+ nsec0 = nsec();
+ return 0;
+ }
+ return (nsec()-nsec0)/1000000;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+ return nsec()/1000;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ sleep(milsec);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ sleep(0);
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ sleep(1000000);
+}
+
+void
+oslopri(void)
+{
+ int fd;
+ char buf[32];
+
+ snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+ if((fd = open(buf, OWRITE)) >= 0){
+ fprint(fd, "pri 8");
+ close(fd);
+ }
+}
--- /dev/null
+++ b/emu/Plan9/win.c
@@ -1,0 +1,566 @@
+#include "dat.h"
+#include "fns.h"
+#include "kernel.h"
+#include "error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "keyboard.h"
+
+enum
+{
+ Margin = 4,
+ Lsize = 100,
+};
+
+extern Memimage *screenimage;
+
+extern int kproc1(char*, void (*)(void*), void*, int);
+
+static ulong* attachwindow(Rectangle*, ulong*, int*, int*);
+
+static void plan9readmouse(void*);
+static void plan9readkeybd(void*);
+static int mapspecials(char *s1, char *s2, int *n);
+
+int usenewwin = 1;
+int kbdiscons;
+static int truedepth;
+
+static int datafd;
+static int ctlfd;
+static int mousefd = -1;
+static int keybdfd;
+static int mousepid = -1;
+static int keybdpid = -1;
+static int cursfd;
+static char winname[64];
+
+/* Following updated by attachwindow() asynchronously */
+static QLock ql;
+static Rectangle tiler;
+static ulong* data;
+static uchar* loadbuf;
+static int cursfd;
+static int imageid;
+static Rectangle imager;
+static uchar *chunk;
+static int chunksize;
+static int dispbufsize;
+
+#define NINFO 12*12
+#define HDR 21
+
+
+void
+killrefresh(void)
+{
+ if(mousepid < 0)
+ return;
+ close(mousefd);
+ close(ctlfd);
+ close(datafd);
+ postnote(PNPROC, mousepid, Eintr);
+ postnote(PNPROC, keybdpid, Eintr);
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int fd;
+ char *p, buf[128], info[NINFO+1];
+
+ if(usenewwin){
+ p = getenv("wsys");
+ if(p == nil)
+ return nil;
+
+ fd = open(p, ORDWR);
+ if(fd < 0) {
+ fprint(2, "attachscreen: can't open window manager: %r\n");
+ return nil;
+ }
+ sprint(buf, "new -dx %d -dy %d", Xsize+2*Margin, Ysize+2*Margin);
+ if(mount(fd, -1, "/mnt/wsys", MREPL, buf) < 0) {
+ fprint(2, "attachscreen: can't mount window manager: %r\n");
+ return nil;
+ }
+ if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
+ fprint(2, "attachscreen: can't bind /mnt/wsys before /dev: %r\n");
+ return nil;
+ }
+ }
+
+ cursfd = open("/dev/cursor", OWRITE);
+ if(cursfd < 0) {
+ fprint(2, "attachscreen: open cursor: %r\n");
+ return nil;
+ }
+
+ /* Set up graphics window console (chars->gkbdq) */
+ keybdfd = open("/dev/cons", OREAD);
+ if(keybdfd < 0) {
+ fprint(2, "attachscreen: open keyboard: %r\n");
+ return nil;
+ }
+ mousefd = open("/dev/mouse", ORDWR);
+ if(mousefd < 0){
+ fprint(2, "attachscreen: can't open mouse: %r\n");
+ return nil;
+ }
+ if(usenewwin || 1){
+ fd = open("/dev/consctl", OWRITE);
+ if(fd < 0)
+ fprint(2, "attachscreen: open /dev/consctl: %r\n");
+ if(write(fd, "rawon", 5) != 5)
+ fprint(2, "attachscreen: write /dev/consctl: %r\n");
+ }
+
+ /* Set up graphics files */
+ ctlfd = open("/dev/draw/new", ORDWR);
+ if(ctlfd < 0){
+ fprint(2, "attachscreen: can't open graphics control file: %r\n");
+ return nil;
+ }
+ if(read(ctlfd, info, sizeof info) < NINFO){
+ close(ctlfd);
+ fprint(2, "attachscreen: can't read graphics control file: %r\n");
+ return nil;
+ }
+ sprint(buf, "/dev/draw/%d/data", atoi(info+0*12));
+ datafd = open(buf, ORDWR|OCEXEC);
+ if(datafd < 0){
+ close(ctlfd);
+ fprint(2, "attachscreen: can't read graphics data file: %r\n");
+ return nil;
+ }
+ dispbufsize = iounit(datafd);
+ if(dispbufsize <= 0)
+ dispbufsize = 8000;
+ if(dispbufsize < 512){
+ close(ctlfd);
+ close(datafd);
+ fprint(2, "attachscreen: iounit %d too small\n", dispbufsize);
+ return nil;
+ }
+ chunksize = dispbufsize - 64;
+
+ if(attachwindow(r, chan, d, width) == nil){
+ close(ctlfd);
+ close(datafd);
+ return nil;
+ }
+
+ mousepid = kproc1("readmouse", plan9readmouse, nil, 0);
+ keybdpid = kproc1("readkbd", plan9readkeybd, nil, 0);
+
+ fd = open("/dev/label", OWRITE);
+ if(fd >= 0){
+ snprint(buf, sizeof(buf), "inferno %d", getpid());
+ write(fd, buf, strlen(buf));
+ close(fd);
+ }
+
+ *softscreen = 1;
+ return (uchar*)data;
+}
+
+static ulong*
+attachwindow(Rectangle *r, ulong *chan, int *d, int *width)
+{
+ int n, fd, nb;
+ char buf[256];
+ uchar ubuf[128];
+ ulong imagechan;
+
+ /*
+ * Discover name of window
+ */
+ fd = open("/mnt/wsys/winname", OREAD);
+ if(fd<0 || (n=read(fd, winname, sizeof winname))<=0){
+ fprint(2, "attachwindow: can only run inferno under rio, not stand-alone\n");
+ return nil;
+ }
+ close(fd);
+ /*
+ * If had previous window, release it
+ */
+ if(imageid > 0){
+ ubuf[0] = 'f';
+ BPLONG(ubuf+1, imageid);
+ if(write(datafd, ubuf, 1+4) != 1+4)
+ fprint(2, "attachwindow: cannot free old window: %r\n");
+ }
+ /*
+ * Allocate image pointing to window, and discover its ID
+ */
+ ubuf[0] = 'n';
+ ++imageid;
+ BPLONG(ubuf+1, imageid);
+ ubuf[5] = n;
+ memmove(ubuf+6, winname, n);
+ if(write(datafd, ubuf, 6+n) != 6+n){
+ fprint(2, "attachwindow: cannot bind %d to window id '%s': %r\n", imageid, winname);
+ return nil;
+ }
+ if(read(ctlfd, buf, sizeof buf) < 12*12){
+ fprint(2, "attachwindow: cannot read window id: %r\n");
+ return nil;
+ }
+ imagechan = strtochan(buf+2*12);
+ truedepth = chantodepth(imagechan);
+ if(truedepth == 0){
+ fprint(2, "attachwindow: cannot handle window depth specifier %.12s\n", buf+2*12);
+ return nil;
+ }
+
+ /*
+ * Report back
+ */
+ if(chan != nil)
+ *chan = imagechan;
+ if(d != nil)
+ *d = chantodepth(imagechan);
+ nb = 0;
+ if(r != nil){
+ Xsize = atoi(buf+6*12)-atoi(buf+4*12)-2*Margin;
+ Ysize = atoi(buf+7*12)-atoi(buf+5*12)-2*Margin;
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+ nb = bytesperline(*r, truedepth);
+ data = malloc(nb*Ysize);
+ loadbuf = malloc(nb*Lsize+1);
+ chunk = malloc(HDR+chunksize+5); /* +5 for flush (1 old, 5 new) */
+ }
+ imager.min.x = atoi(buf+4*12);
+ imager.min.y = atoi(buf+5*12);
+ imager.max.x = atoi(buf+6*12);
+ imager.max.y = atoi(buf+7*12);
+
+ if(width != nil)
+ *width = nb/4;
+
+ tiler.min.x = atoi(buf+4*12)+Margin;
+ tiler.min.y = atoi(buf+5*12)+Margin;
+ tiler.max.x = atoi(buf+6*12)-Margin;
+ tiler.max.y = atoi(buf+7*12)-Margin;
+
+ return data;
+}
+
+static int
+plan9loadimage(Rectangle r, uchar *data, int ndata)
+{
+ long dy;
+ int n, bpl;
+
+ if(!rectinrect(r, imager)){
+ werrstr("loadimage: bad rectangle");
+ return -1;
+ }
+ bpl = bytesperline(r, truedepth);
+ n = bpl*Dy(r);
+ if(n > ndata){
+ werrstr("loadimage: insufficient data");
+ return -1;
+ }
+ ndata = 0;
+ while(r.max.y > r.min.y){
+ dy = r.max.y - r.min.y;
+ if(dy*bpl> chunksize)
+ dy = chunksize/bpl;
+ n = dy*bpl;
+ chunk[0] = 'y';
+ BPLONG(chunk+1, imageid);
+ BPLONG(chunk+5, r.min.x);
+ BPLONG(chunk+9, r.min.y);
+ BPLONG(chunk+13, r.max.x);
+ BPLONG(chunk+17, r.min.y+dy);
+ memmove(chunk+21, data, n);
+ ndata += n;
+ data += n;
+ r.min.y += dy;
+ n += 21;
+ if(r.min.y >= r.max.y) /* flush to screen */
+ chunk[n++] = 'v';
+ if(write(datafd, chunk, n) != n)
+ return -1;
+ }
+ return ndata;
+}
+
+static void
+_flushmemscreen(Rectangle r)
+{
+ int n, dy, l;
+ Rectangle rr;
+
+ if(data == nil || loadbuf == nil || chunk==nil)
+ return;
+ if(!rectclip(&r, Rect(0, 0, Xsize, Ysize)))
+ return;
+ if(!rectclip(&r, Rect(0, 0, Dx(tiler), Dy(tiler))))
+ return;
+ if(Dx(r)<=0 || Dy(r)<=0)
+ return;
+ l = bytesperline(r, truedepth);
+ while(r.min.y < r.max.y){
+ dy = Dy(r);
+ if(dy > Lsize)
+ dy = Lsize;
+ rr = r;
+ rr.max.y = rr.min.y+dy;
+ n = unloadmemimage(screenimage, rr, loadbuf, l*dy);
+ /* offset from (0,0) to window */
+ rr.min.x += tiler.min.x;
+ rr.min.y += tiler.min.y;
+ rr.max.x += tiler.min.x;
+ rr.max.y += tiler.min.y;
+ if(plan9loadimage(rr, loadbuf, n) != n)
+ fprint(2, "flushmemscreen: %d bytes: %r\n", n);
+ r.min.y += dy;
+ }
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ qlock(&ql);
+ _flushmemscreen(r);
+ qunlock(&ql);
+}
+
+void
+drawcursor(Drawcursor *c)
+{
+ int j, i, h, w, bpl;
+ uchar *bc, *bs, *cclr, *cset, curs[2*4+2*2*16];
+
+ /* Set the default system cursor */
+ if(c->data == nil) {
+ write(cursfd, curs, 0);
+ return;
+ }
+
+ BPLONG(curs+0*4, c->hotx);
+ BPLONG(curs+1*4, c->hoty);
+
+ w = (c->maxx-c->minx);
+ h = (c->maxy-c->miny)/2;
+
+ cclr = curs+2*4;
+ cset = curs+2*4+2*16;
+ bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+ bc = c->data;
+ bs = c->data + h*bpl;
+
+ if(h > 16)
+ h = 16;
+ if(w > 16)
+ w = 16;
+ w /= 8;
+ for(i = 0; i < h; i++) {
+ for(j = 0; j < w; j++) {
+ cclr[j] = bc[j];
+ cset[j] = bs[j];
+ }
+ bc += bpl;
+ bs += bpl;
+ cclr += 2;
+ cset += 2;
+ }
+ write(cursfd, curs, sizeof curs);
+}
+
+static int
+checkmouse(char *buf, int n)
+{
+ int x, y, tick, b;
+ static int lastb, lastt, lastx, lasty, lastclick;
+
+ switch(n){
+ default:
+ kwerrstr("atomouse: bad count");
+ return -1;
+
+ case 1+4*12:
+ if(buf[0] == 'r'){
+ qlock(&ql);
+ if(attachwindow(nil, nil, nil, nil) == nil) {
+ qunlock(&ql);
+ return -1;
+ }
+ _flushmemscreen(Rect(0, 0, Xsize, Ysize));
+ qunlock(&ql);
+ }
+ x = atoi(buf+1+0*12) - tiler.min.x;
+ y = atoi(buf+1+1*12) - tiler.min.y;
+ b = atoi(buf+1+2*12);
+ tick = atoi(buf+1+3*12);
+ if(b && lastb == 0){ /* button newly pressed */
+ if(b==lastclick && tick-lastt<400
+ && abs(x-lastx)<10 && abs(y-lasty)<10)
+ b |= (1<<8);
+ lastt = tick;
+ lastclick = b&0xff;
+ lastx = x;
+ lasty = y;
+ }
+ lastb = b&0xff;
+ //mouse.msec = tick;
+ mousetrack(b, x, y, 0);
+ return n;
+ }
+}
+
+static void
+plan9readmouse(void *v)
+{
+ int n;
+ char buf[128];
+
+ USED(v);
+ for(;;){
+ n = read(mousefd, buf, sizeof(buf));
+ if(n < 0) /* probably interrupted */
+ _exits(0);
+ checkmouse(buf, n);
+ }
+}
+
+static void
+plan9readkeybd(void*)
+{
+ int n, partial;
+ char buf[32];
+ char dbuf[32 * 3]; /* overestimate but safe */
+
+ partial = 0;
+ for(;;){
+ n = read(keybdfd, buf + partial, sizeof(buf) - partial);
+ if(n < 0) /* probably interrupted */
+ _exits(0);
+ partial += n;
+ n = mapspecials(dbuf, buf, &partial);
+ qproduce(gkbdq, dbuf, n);
+ }
+}
+
+void
+setpointer(int x, int y)
+{
+ char buf[50];
+ int n;
+
+ if(mousefd < 0)
+ return;
+ x += tiler.min.x;
+ y += tiler.min.y;
+ n = snprint(buf, sizeof buf, "m%11d %11d ", x, y);
+ write(mousefd, buf, n);
+}
+
+/*
+ * plan9 keyboard codes; from /sys/include/keyboard.h; can't include directly
+ * because constant names clash.
+ */
+enum {
+ P9KF= 0xF000, /* Rune: beginning of private Unicode space */
+ P9Spec= 0xF800,
+ /* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
+ Khome= P9KF|0x0D,
+ Kup= P9KF|0x0E,
+ Kpgup= P9KF|0x0F,
+ Kprint= P9KF|0x10,
+ Kleft= P9KF|0x11,
+ Kright= P9KF|0x12,
+ Kdown= P9Spec|0x00,
+ Kview= P9Spec|0x00,
+ Kpgdown= P9KF|0x13,
+ Kins= P9KF|0x14,
+ Kend= KF|0x18,
+
+ Kalt= P9KF|0x15,
+ Kshift= P9KF|0x16,
+ Kctl= P9KF|0x17,
+};
+
+/*
+ * translate plan 9 special characters from s2 (of length *n) into s1;
+ * return number of chars placed into s1.
+ * any trailing incomplete chars are moved to the beginning of s2,
+ * and *n set to the number moved there.
+ */
+static int
+mapspecials(char *s1, char *s2, int *n)
+{
+ char *s, *d, *es2;
+ Rune r;
+ d = s1;
+ s = s2;
+ es2 = s2 + *n;
+ while (fullrune(s, es2 - s)) {
+ s += chartorune(&r, s);
+ switch (r) {
+ case Kshift:
+ r = LShift;
+ break;
+ case Kctl:
+ r = LCtrl;
+ break;
+ case Kalt:
+ r = LAlt;
+ break;
+ case Khome:
+ r = Home;
+ break;
+ case Kend:
+ r = End;
+ break;
+ case Kup:
+ r = Up;
+ break;
+ case Kdown:
+ r = Down;
+ break;
+ case Kleft:
+ r = Left;
+ break;
+ case Kright:
+ r = Right;
+ break;
+ case Kpgup:
+ r = Pgup;
+ break;
+ case Kpgdown:
+ r = Pgdown;
+ break;
+ case Kins:
+ r = Ins;
+ break;
+ /*
+ * function keys
+ */
+ case P9KF|1:
+ case P9KF|2:
+ case P9KF|3:
+ case P9KF|4:
+ case P9KF|5:
+ case P9KF|6:
+ case P9KF|7:
+ case P9KF|8:
+ case P9KF|9:
+ case P9KF|10:
+ case P9KF|11:
+ case P9KF|12:
+ r = (r - P9KF) + KF;
+ }
+ d += runetochar(d, &r);
+ }
+ *n = es2 - s;
+ memmove(s2, s, *n);
+ return d - s1;
+}
--- /dev/null
+++ b/emu/Solaris/asm-386.s
@@ -1,0 +1,72 @@
+ .section .bss
+ .align 4
+.L4_.bss:
+ .align 4
+Solaris_Asm_IntP: / Offset 0
+ .type Solaris_Asm_IntP,@object
+ .size Solaris_Asm_IntP,4
+ .set .,.+4
+Solaris_Asm_VoidP: / Offset 4
+ .type Solaris_Asm_VoidP,@object
+ .size Solaris_Asm_VoidP,4
+ .set .,.+4
+ .section .text
+ .align 4
+.L1_.text:
+
+/====================
+/ FPsave
+/--------------------
+ .align 4
+ .align 4
+ .globl FPsave
+FPsave:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%eax
+ movl %eax,Solaris_Asm_VoidP
+ fstenv (%eax)
+ leave
+ ret
+ .align 4
+ .type FPsave,@function
+ .size FPsave,.-FPsave
+
+/====================
+/ FPrestore
+/--------------------
+ .align 4
+ .globl FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%eax
+ movl %eax,Solaris_Asm_VoidP
+ fldenv (%eax)
+ leave
+ ret
+ .align 4
+ .type FPrestore,@function
+ .size FPrestore,.-FPrestore
+
+
+/====================
+/ getcallerpc
+/--------------------
+ .align 4
+ .globl getcallerpc
+getcallerpc:
+ movl 4(%ebp),%eax
+ ret
+ .align 4
+ .type getcallerpc,@function
+ .size getcallerpc,.-getcallerpc
+
+/ test-and-set
+ .align 4
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/Solaris/asm-sparc.s
@@ -1,0 +1,76 @@
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global segflush
+ .type segflush, #function
+
+ ! The flush instruction works on 8-byte chunks.
+ ! We truncate the pointer and increase the count
+ ! to make sure we flush the right range.
+
+ ! SPARC requires 5 instructions after flush to
+ ! let the caches settle. The loop code supplies
+ ! the delay instructions.
+
+segflush: ! int segflush(void *p, ulong len)
+
+ and %o0,-8,%o0 ! clear low 3 bits of p
+ add %o1, 7, %o1 ! len += 7
+1:
+ flush %o0 ! synchronize cache
+ sub %o1, 8, %o1 ! len -= 8
+ cmp %o1, 0 ! if len > 0, repeat
+ bg 1b
+ add %o0, 8, %o0 ! p += 8 in delay slot
+
+ retl
+ add %g0, %g0, %o0 ! return 0
+ .size segflush,(.-segflush)
+
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global FPsave
+ .type FPsave, #function
+FPsave:
+ retl
+ st %fsr,[%o0]
+ .size FPsave,(.-FPsave)
+
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global FPrestore
+ .type FPrestore, #function
+FPrestore:
+ retl
+ ld [%o0],%fsr
+ .size FPrestore,(.-FPrestore)
+
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global getcallerpc
+ .type getcallerpc, #function
+getcallerpc: ! ignore argument
+ retl
+ add %i7,0,%o0
+
+ .size getcallerpc,(.-getcallerpc)
+
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global _tas
+ .type _tas, #function
+_tas:
+ or %g0,1,%o1
+ swap [%o0],%o1
+ retl
+ or %g0,%o1,%o0
+ .size _tas,(.-_tas)
--- /dev/null
+++ b/emu/Solaris/audio.c
@@ -1,0 +1,593 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#define __EXTENSIONS__
+#include <sys/time.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <sys/audioio.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/audioio.h>
+
+#define Audio_Mic_Val AUDIO_MICROPHONE
+#define Audio_Linein_Val AUDIO_LINE_IN
+
+#define Audio_Speaker_Val AUDIO_SPEAKER
+#define Audio_Headphone_Val AUDIO_HEADPHONE
+#define Audio_Lineout_Val AUDIO_LINE_OUT
+
+#define Audio_Pcm_Val AUDIO_ENCODING_LINEAR
+#define Audio_Ulaw_Val AUDIO_ENCODING_ULAW
+#define Audio_Alaw_Val AUDIO_ENCODING_ALAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug = 0;
+
+extern int nanosleep(const struct timespec *, struct timespec *);
+
+#define AUDIO_FILE_STRING "/dev/audio"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file_in = -1; /* file in */
+static int audio_file_out = -1; /* file out */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+
+static int audio_enforce(Audio_t*);
+static int audio_open_in(void);
+static int audio_open_out(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ static ushort flag = 1;
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if((audio_file_in = audio_open_in()) < 0)
+ oserror();
+
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if((audio_file_out = audio_open_out() ) < 0)
+ oserror();
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&inlock);
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+
+ if((audio_file_in = audio_open_in()) < 0)
+ oserror();
+ if(waserror()){
+ close(audio_file_in);
+ audio_file_in = -1;
+ nexterror();
+ }
+ if((audio_file_out = audio_open_out()) < 0)
+ oserror();
+ poperror();
+
+ poperror();
+ qunlock(&inlock);
+ qunlock(&outlock);
+ break;
+ }
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ close(audio_file_in);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ close(audio_file_out);
+ audio_file_out = -1;
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ close(audio_file_in);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ qlock(&outlock);
+ close(audio_file_out);
+ audio_file_out = -1;
+ qunlock(&outlock);
+ break;
+ }
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(!audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */
+ time.tv_nsec= 1;
+ nanosleep(&time,nil);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */
+ time.tv_nsec= 1;
+ nanosleep(&time,nil);
+
+ return count;
+}
+
+int
+audio_open_in(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDONLY|O_NONBLOCK);
+
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ close(fd);
+ error(Eio);
+ }
+
+ if(!audio_pause_in(fd, A_Pause)) {
+ close(fd);
+ error(Eio);
+ }
+
+ if(!audio_flush(fd, A_In)) {
+ close(fd);
+ error(Eio);
+ }
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(!audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+int
+audio_open_out(void)
+{
+ int fd;
+ struct audio_info hdr;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_WRONLY|O_NONBLOCK);
+
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ /* set audio info */
+ av.in.flags = 0;
+ av.out.flags = ~0;
+
+ if(!audio_set_info(fd, &av.out, A_Out)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.out.flags = 0;
+
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+ audio_info_t info;
+ audio_prinfo_t *dev;
+
+ if(fd < 0)
+ return 0;
+
+ /* devitialize header */
+ AUDIO_INITINFO(&info);
+
+ if(d == A_In)
+ dev = &info.record;
+ else
+ dev = &info.play;
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG)
+ dev->sample_rate = i->rate;
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG)
+ dev->channels = i->chan;
+
+ /* precision */
+ if(i->flags & AUDIO_BITS_FLAG)
+ dev->precision = i->bits;
+
+ /* encoding */
+ if(i->flags & AUDIO_ENC_FLAG)
+ dev->encoding = i->enc;
+
+ /* devices */
+ if(i->flags & AUDIO_DEV_FLAG)
+ dev->port = i->dev;
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG)) {
+ dev->gain = (i->left * AUDIO_MAX_GAIN) / Audio_Max_Val;
+
+ /* do left first then right later */
+ if(i->left == i->right)
+ dev->balance = AUDIO_MID_BALANCE;
+ else {
+ dev->balance = AUDIO_LEFT_BALANCE;
+ if(i->chan != 1)
+ unequal_stereo = 1;
+ }
+ }
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info); /* qlock and load general stuff */
+ osleave();
+
+ if(status == -1) {
+ if(debug) print("audio_set_info 1 failed: fd = %d errno = %d\n", fd, errno);
+ return 0;
+ }
+
+ /* check for different right and left for dev */
+ if(unequal_stereo) {
+
+ /* re-init header */
+ AUDIO_INITINFO(&info);
+
+ dev->gain = (i->right * AUDIO_MAX_GAIN) / Audio_Max_Val;
+ dev->balance == AUDIO_RIGHT_BALANCE;
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info);
+ osleave();
+
+ if(status == -1) {
+ if(debug) print("audio_set_info 2 failed: fd = %d errno = %d\n",fd, errno);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ audio_info_t info;
+ int foo = 0;
+ int status;
+
+ osenter();
+ status = ioctl(fd, AUDIO_DRAIN, &foo);
+ osleave();
+
+ if(status == -1)
+ return 0;
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ audio_info_t info;
+ int status;
+
+ if(fd < 0)
+ return 0;
+
+ if(audio_in_pause == f)
+ return 1;
+
+ /* initialize header */
+ AUDIO_INITINFO(&info);
+
+ /* unpause input */
+ if(f == A_Pause)
+ info.record.pause = 1;
+ else
+ info.record.pause = 0;
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info);
+ osleave();
+
+ if(status == -1)
+ return 0;
+
+ audio_in_pause = f;
+
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int flag = d==A_In? FLUSHR: FLUSHW;
+ int status;
+
+ osenter();
+ status = ioctl(fd, I_FLUSH, flag); /* drain anything already put into buffer */
+ osleave();
+
+ if(status == -1)
+ return 0;
+ return 1;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/Solaris/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Solaris/deveia.c
@@ -1,0 +1,38 @@
+/*
+ * Solaris serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/term/a",
+ "/dev/term/b"
+};
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {76800, B76800},
+ {115200, B115200},
+ {153600, B153600},
+ {230400, B230400},
+ {307200, B307200},
+ {460800, B460800},
+ {-1, -1}
+};
--- /dev/null
+++ b/emu/Solaris/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Solaris/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Solaris/mkfile
@@ -1,0 +1,46 @@
+SYSTARG=Solaris
+OBJTYPE=sparc
+<../../mkconfig
+SYSTARG=Solaris
+OBJTYPE=sparc
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS=$EMULIBS
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/Solaris/os.c
@@ -1,0 +1,436 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef _POSIX_C_SOURCE
+#undef getwd
+#include <unistd.h>
+#include <thread.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+enum
+{
+ DELETE = 0x7F
+};
+char *hosttype = "Solaris";
+
+static thread_key_t prdakey;
+
+static siginfo_t siginfo;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+ void *vp;
+
+ if (thr_getspecific(prdakey, &vp))
+ return nil;
+ return vp;
+}
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+ /*print("pexit: %s: %s\n", up->text, msg);*/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(up->prog);
+ sema_destroy(up->os);
+ free(up->os);
+ free(up);
+ thr_exit(0);
+}
+
+static void *
+tramp(void *v)
+{
+ struct Proc *Up;
+
+ if(thr_setspecific(prdakey, v)) {
+ print("set specific data failed in tramp\n");
+ thr_exit(0);
+ }
+ Up = v;
+ Up->sigid = thr_self();
+ Up->func(Up->arg);
+ pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ thread_t thread;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ sema_t *sem;
+
+ p = newproc();
+
+ sem = malloc(sizeof(*sem));
+ if(sem == nil)
+ panic("can't allocate semaphore");
+ sema_init(sem, 0, USYNC_THREAD, 0);
+ p->os = sem;
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(thr_create(0, 0, &tramp, p, THR_BOUND|THR_DETACHED, &thread))
+ panic("thr_create failed\n");
+ thr_yield();
+}
+
+/* to get pc on trap use siginfo.si_pc field and define all trap handlers
+ as printILL - have to set sa_sigaction, sa_flags not sa_handler
+*/
+
+static void
+trapUSR1(void)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapILL(void)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+printILL(int sig, siginfo_t *siginfo, void *v)
+{
+ panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n",
+ siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr);
+}
+
+static void
+trapBUS(void)
+{
+ disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(void)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(void)
+{
+ disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+ thr_kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ while(sema_wait(up->os))
+ ; /* retry on signals */
+}
+
+void
+osready(Proc *p)
+{
+ sema_post(p->os);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+ exit(0);
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody(void)
+{
+ struct passwd *pwd;
+
+ if (pwd=getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct Proc *Up;
+ struct sigaction act;
+ struct passwd *pw;
+ char sys[64];
+
+ setsid();
+
+ if(dflag == 0)
+ termset();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler=trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(sflag == 0) {
+ act.sa_handler = trapBUS;
+ sigaction(SIGBUS, &act, nil);
+ act.sa_handler = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_handler = trapSEGV;
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_handler = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+ } else{
+ act.sa_sigaction = printILL;
+ act.sa_flags=SA_SIGINFO;
+ sigaction(SIGILL, &act, nil);
+ }
+
+ if(thr_keycreate(&prdakey,NULL))
+ print("keycreate failed\n");
+
+ Up = newproc();
+ if(thr_setspecific(prdakey,Up))
+ panic("set specific thread data failed\n");
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ up->env->uid = getuid();
+ up->env->gid = getgid();
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "keyboard read: %s\n", strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t, NULL)<0)
+ return(0);
+ if(sec0==0){
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time,nil);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ thr_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
--- /dev/null
+++ b/emu/Unixware/asm-386.s
@@ -1,0 +1,71 @@
+ .section .bss
+ .align 4
+.L4_.bss:
+ .align 4
+Unixware_Asm_IntP: / Offset 0
+ .type Unixware_Asm_IntP,@object
+ .size Unixware_Asm_IntP,4
+ .set .,.+4
+Unixware_Asm_VoidP: / Offset 4
+ .type Unixware_Asm_VoidP,@object
+ .size Unixware_Asm_VoidP,4
+ .set .,.+4
+ .section .text
+ .align 4
+.L1_.text:
+/====================
+/ FPsave
+/--------------------
+ .align 4
+ .align 4
+ .globl FPsave
+FPsave:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%eax
+ movl %eax,Unixware_Asm_VoidP
+ fstenv (%eax)
+ leave
+ ret
+ .align 4
+ .type FPsave,@function
+ .size FPsave,.-FPsave
+
+/====================
+/ FPrestore
+/--------------------
+ .align 4
+ .globl FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%eax
+ movl %eax,Unixware_Asm_VoidP
+ fldenv (%eax)
+ leave
+ ret
+ .align 4
+ .type FPrestore,@function
+ .size FPrestore,.-FPrestore
+
+
+/====================
+/ getcallerpc
+/--------------------
+ .align 4
+ .globl getcallerpc
+getcallerpc:
+ movl 4(%ebp),%eax
+ ret
+ .align 4
+ .type getcallerpc,@function
+ .size getcallerpc,.-getcallerpc
+
+/ test-and-set
+ .align 4
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/Unixware/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Unixware/deveia.c
@@ -1,0 +1,11 @@
+/*
+ * Solaris serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/cua/a",
+ "/dev/cua/b"
+};
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
--- /dev/null
+++ b/emu/Unixware/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Unixware/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Unixware/mkfile
@@ -1,0 +1,44 @@
+SYSTARG=Unixware
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=Unixware
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm -lX11 -lsocket -lnsl -lresolv -lxti -Kthread
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
--- /dev/null
+++ b/emu/Unixware/mkfile-Unixware
@@ -1,0 +1,15 @@
+#
+# architecture-dependent files for Unixware
+#
+
+TARGFILES=asm-Unixware-$OBJTYPE.$O\
+ devfs-posix.$O\
+ devip.$O\
+ ipif-posix.$O\
+ os-Unixware.$O\
+ win-x11.$O\
+ deveia-Unixware.$O\
+ srv.$O\
+ lock.$O\
+
+SYSLIBS= -lm -lX11 -lsocket -lnsl -lresolv -lxti -Kthread
--- /dev/null
+++ b/emu/Unixware/os.c
@@ -1,0 +1,437 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef _POSIX_C_SOURCE
+#undef getwd
+#include <unistd.h>
+#include <thread.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+enum
+{
+ DELETE = 0x7F
+};
+char *hosttype = "Unixware";
+
+static thread_key_t prdakey;
+
+static siginfo_t siginfo;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+ void *vp;
+
+ if (thr_getspecific(prdakey, &vp))
+ return nil;
+ return vp;
+}
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+ /*print("pexit: %s: %s\n", up->text, msg);*/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(up->prog);
+ sema_destroy(up->os);
+ free(up->os);
+ free(up);
+ thr_exit(0);
+}
+
+static void *
+tramp(void *v)
+{
+ struct Proc *Up;
+
+ if(thr_setspecific(prdakey, v)) {
+ print("set specific data failed in tramp\n");
+ thr_exit(0);
+ }
+ Up = v;
+ Up->sigid = thr_self();
+ Up->func(Up->arg);
+ pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ thread_t thread;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ sema_t *sem;
+
+ p = newproc();
+
+ sem = malloc(sizeof(*sem));
+ if(sem == nil)
+ panic("can't allocate semaphore");
+ sema_init(sem, 0, USYNC_THREAD, 0);
+ p->os = sem;
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(thr_create(0, 0, &tramp, p, THR_BOUND|THR_DETACHED, &thread))
+ panic("thr_create failed\n");
+ thr_yield();
+}
+
+/* to get pc on trap use siginfo.si_pc field and define all trap handlers
+ as printILL - have to set sa_sigaction, sa_flags not sa_handler
+*/
+
+static void
+trapUSR1(void)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapILL(void)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+printILL(int sig, siginfo_t *siginfo, void *v)
+{
+ panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n",
+ siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr);
+}
+
+static void
+trapBUS(void)
+{
+ disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(void)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(void)
+{
+ disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+ thr_kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ while(sema_wait(up->os))
+ ; /* retry on signals */
+}
+
+void
+osready(Proc *p)
+{
+ sema_post(p->os);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+ exit(0);
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody(void)
+{
+ struct passwd *pwd;
+
+ if (pwd=getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct Proc *Up;
+ struct sigaction act;
+ struct passwd *pw;
+ char sys[64];
+
+ setsid();
+
+ if(dflag == 0)
+ termset();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler=trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(sflag == 0) {
+ act.sa_handler = trapBUS;
+ sigaction(SIGBUS, &act, nil);
+ act.sa_handler = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_handler = trapSEGV;
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_handler = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+ } else{
+ act.sa_sigaction = printILL;
+ act.sa_flags=SA_SIGINFO;
+ sigaction(SIGILL, &act, nil);
+ }
+
+ if(thr_keycreate(&prdakey,NULL))
+ print("keycreate failed\n");
+
+ Up = newproc();
+ if(thr_setspecific(prdakey,Up))
+ panic("set specific thread data failed\n");
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ up->env->uid = getuid();
+ up->env->gid = getgid();
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "keyboard read: %s\n", strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t, NULL)<0)
+ return(0);
+ if(sec0==0){
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time,nil);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ thr_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
--- /dev/null
+++ b/emu/mkfile
@@ -1,0 +1,19 @@
+<../mkconfig
+
+all:V: all-$HOSTMODEL
+install:V: install-$HOSTMODEL
+safeinstall:V: safeinstall-$HOSTMODEL
+clean:V: clean-$HOSTMODEL
+nuke:V: nuke-$HOSTMODEL
+
+&-Posix:QV:
+ echo "(cd $SYSTARG; mk $MKFLAGS $stem)"
+ (cd $SYSTARG; mk $MKFLAGS $stem) || exit 1
+
+&-Nt:QV:
+ echo '@{builtin cd' $SYSTARG '; mk $MKFLAGS $stem}'
+ @{builtin cd $SYSTARG; mk $MKFLAGS $stem }
+
+&-Plan9:QV:
+ echo '@{builtin cd' $SYSTARG '; mk $MKFLAGS $stem}'
+ @{builtin cd $SYSTARG; mk $MKFLAGS $stem }
--- /dev/null
+++ b/emu/port/acme-offset
@@ -1,0 +1,1 @@
+X ,x/b?(read|write)\(.*ulong.*\)$/v/(bread|bwrite)/x/ulong offset/c/vlong offset
--- /dev/null
+++ b/emu/port/alloc.c
@@ -1,0 +1,1021 @@
+#include "dat.h"
+#include "fns.h"
+#include "interp.h"
+#include "error.h"
+
+enum
+{
+ MAXPOOL = 4
+};
+
+#define left u.s.bhl
+#define right u.s.bhr
+#define fwd u.s.bhf
+#define prev u.s.bhv
+#define parent u.s.bhp
+
+#define RESERVED 512*1024
+
+struct Pool
+{
+ char* name;
+ int pnum;
+ ulong maxsize;
+ int quanta;
+ int chunk;
+ int monitor;
+ ulong ressize; /* restricted size */
+ ulong cursize;
+ ulong arenasize;
+ ulong hw;
+ Lock l;
+ Bhdr* root;
+ Bhdr* chain;
+ ulong nalloc;
+ ulong nfree;
+ int nbrk;
+ int lastfree;
+ void (*move)(void*, void*);
+};
+
+void* initbrk(ulong);
+
+struct
+{
+ int n;
+ Pool pool[MAXPOOL];
+ /* Lock l; */
+} table = {
+ 3,
+ {
+ { "main", 0, 32*1024*1024, 31, 512*1024, 0, 31*1024*1024 },
+ { "heap", 1, 32*1024*1024, 31, 512*1024, 0, 31*1024*1024 },
+ { "image", 2, 64*1024*1024+256, 31, 4*1024*1024, 1, 63*1024*1024 },
+ }
+};
+
+Pool* mainmem = &table.pool[0];
+Pool* heapmem = &table.pool[1];
+Pool* imagmem = &table.pool[2];
+
+static void _auditmemloc(char *, void *);
+void (*auditmemloc)(char *, void *) = _auditmemloc;
+static void _poolfault(void *, char *, ulong);
+void (*poolfault)(void *, char *, ulong) = _poolfault;
+
+/* non tracing
+ *
+enum {
+ Npadlong = 0,
+ MallocOffset = 0,
+ ReallocOffset = 0
+};
+ *
+ */
+
+/* tracing */
+enum {
+ Npadlong = 2,
+ MallocOffset = 0,
+ ReallocOffset = 1
+};
+
+enum {
+ Monitor = 1
+};
+
+void (*memmonitor)(int, ulong, ulong, ulong) = nil;
+#define MM(v,pc,base,size) if(!Monitor || memmonitor==nil){} else memmonitor((v),(pc),(base),(size))
+
+#define CKLEAK 0
+int ckleak;
+#define ML(v, sz, pc) if(CKLEAK && ckleak && v){ if(sz) fprint(2, "%lux %lux %lux\n", (ulong)v, (ulong)sz, (ulong)pc); else fprint(2, "%lux\n", (ulong)v); }
+
+int
+memusehigh(void)
+{
+ return mainmem->cursize > mainmem->ressize ||
+ heapmem->cursize > heapmem->ressize ||
+ 0 && imagmem->cursize > imagmem->ressize;
+}
+
+int
+memlow(void)
+{
+ return heapmem->cursize > (heapmem->maxsize)/2;
+}
+
+int
+poolsetsize(char *s, int size)
+{
+ int i;
+
+ for(i = 0; i < table.n; i++) {
+ if(strcmp(table.pool[i].name, s) == 0) {
+ table.pool[i].maxsize = size;
+ table.pool[i].ressize = size-RESERVED;
+ if(size < RESERVED)
+ panic("not enough memory");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+poolimmutable(void *v)
+{
+ Bhdr *b;
+
+ D2B(b, v);
+ b->magic = MAGIC_I;
+}
+
+void
+poolmutable(void *v)
+{
+ Bhdr *b;
+
+ D2B(b, v);
+ b->magic = MAGIC_A;
+ ((Heap*)v)->color = mutator;
+}
+
+char*
+poolname(Pool *p)
+{
+ return p->name;
+}
+
+Bhdr*
+poolchain(Pool *p)
+{
+ return p->chain;
+}
+
+void
+pooldel(Pool *p, Bhdr *t)
+{
+ Bhdr *s, *f, *rp, *q;
+
+ if(t->parent == nil && p->root != t) {
+ t->prev->fwd = t->fwd;
+ t->fwd->prev = t->prev;
+ return;
+ }
+
+ if(t->fwd != t) {
+ f = t->fwd;
+ s = t->parent;
+ f->parent = s;
+ if(s == nil)
+ p->root = f;
+ else {
+ if(s->left == t)
+ s->left = f;
+ else
+ s->right = f;
+ }
+
+ rp = t->left;
+ f->left = rp;
+ if(rp != nil)
+ rp->parent = f;
+ rp = t->right;
+ f->right = rp;
+ if(rp != nil)
+ rp->parent = f;
+
+ t->prev->fwd = t->fwd;
+ t->fwd->prev = t->prev;
+ return;
+ }
+
+ if(t->left == nil)
+ rp = t->right;
+ else {
+ if(t->right == nil)
+ rp = t->left;
+ else {
+ f = t;
+ rp = t->right;
+ s = rp->left;
+ while(s != nil) {
+ f = rp;
+ rp = s;
+ s = rp->left;
+ }
+ if(f != t) {
+ s = rp->right;
+ f->left = s;
+ if(s != nil)
+ s->parent = f;
+ s = t->right;
+ rp->right = s;
+ if(s != nil)
+ s->parent = rp;
+ }
+ s = t->left;
+ rp->left = s;
+ s->parent = rp;
+ }
+ }
+ q = t->parent;
+ if(q == nil)
+ p->root = rp;
+ else {
+ if(t == q->left)
+ q->left = rp;
+ else
+ q->right = rp;
+ }
+ if(rp != nil)
+ rp->parent = q;
+}
+
+void
+pooladd(Pool *p, Bhdr *q)
+{
+ int size;
+ Bhdr *tp, *t;
+
+ q->magic = MAGIC_F;
+
+ q->left = nil;
+ q->right = nil;
+ q->parent = nil;
+ q->fwd = q;
+ q->prev = q;
+
+ t = p->root;
+ if(t == nil) {
+ p->root = q;
+ return;
+ }
+
+ size = q->size;
+
+ tp = nil;
+ while(t != nil) {
+ if(size == t->size) {
+ q->prev = t->prev;
+ q->prev->fwd = q;
+ q->fwd = t;
+ t->prev = q;
+ return;
+ }
+ tp = t;
+ if(size < t->size)
+ t = t->left;
+ else
+ t = t->right;
+ }
+
+ q->parent = tp;
+ if(size < tp->size)
+ tp->left = q;
+ else
+ tp->right = q;
+}
+
+static void*
+dopoolalloc(Pool *p, ulong asize, ulong pc)
+{
+ Bhdr *q, *t;
+ int alloc, ldr, ns, frag;
+ int osize, size;
+
+ if(asize >= 1024*1024*1024) /* for sanity and to avoid overflow */
+ return nil;
+ size = asize;
+ osize = size;
+ size = (size + BHDRSIZE + p->quanta) & ~(p->quanta);
+
+ lock(&p->l);
+ p->nalloc++;
+
+ t = p->root;
+ q = nil;
+ while(t) {
+ if(t->size == size) {
+ t = t->fwd;
+ pooldel(p, t);
+ t->magic = MAGIC_A;
+ p->cursize += t->size;
+ if(p->cursize > p->hw)
+ p->hw = p->cursize;
+ unlock(&p->l);
+ if(p->monitor)
+ MM(p->pnum, pc, (ulong)B2D(t), size);
+ return B2D(t);
+ }
+ if(size < t->size) {
+ q = t;
+ t = t->left;
+ }
+ else
+ t = t->right;
+ }
+ if(q != nil) {
+ pooldel(p, q);
+ q->magic = MAGIC_A;
+ frag = q->size - size;
+ if(frag < (size>>2) && frag < 0x8000) {
+ p->cursize += q->size;
+ if(p->cursize > p->hw)
+ p->hw = p->cursize;
+ unlock(&p->l);
+ if(p->monitor)
+ MM(p->pnum, pc, (ulong)B2D(q), size);
+ return B2D(q);
+ }
+ /* Split */
+ ns = q->size - size;
+ q->size = size;
+ B2T(q)->hdr = q;
+ t = B2NB(q);
+ t->size = ns;
+ B2T(t)->hdr = t;
+ pooladd(p, t);
+ p->cursize += q->size;
+ if(p->cursize > p->hw)
+ p->hw = p->cursize;
+ unlock(&p->l);
+ if(p->monitor)
+ MM(p->pnum, pc, (ulong)B2D(q), size);
+ return B2D(q);
+ }
+
+ ns = p->chunk;
+ if(size > ns)
+ ns = size;
+ ldr = p->quanta+1;
+
+ alloc = ns+ldr+ldr;
+ p->arenasize += alloc;
+ if(p->arenasize > p->maxsize) {
+ p->arenasize -= alloc;
+ ns = p->maxsize-p->arenasize-ldr-ldr;
+ ns &= ~p->quanta;
+ if (ns < size) {
+ if(poolcompact(p)) {
+ unlock(&p->l);
+ return poolalloc(p, osize);
+ }
+
+ unlock(&p->l);
+ print("arena %s too large: size %d cursize %lud arenasize %lud maxsize %lud\n",
+ p->name, size, p->cursize, p->arenasize, p->maxsize);
+ return nil;
+ }
+ alloc = ns+ldr+ldr;
+ p->arenasize += alloc;
+ }
+
+ p->nbrk++;
+ t = (Bhdr *)sbrk(alloc);
+ if(t == (void*)-1) {
+ p->nbrk--;
+ unlock(&p->l);
+ return nil;
+ }
+ /* Double alignment */
+ t = (Bhdr *)(((ulong)t + 7) & ~7);
+
+ if(p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){
+ /* can merge chains */
+ if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name);
+ q = B2LIMIT(p->chain);
+ q->magic = MAGIC_A;
+ q->size = alloc;
+ B2T(q)->hdr = q;
+ t = B2NB(q);
+ t->magic = MAGIC_E;
+ p->chain->csize += alloc;
+ p->cursize += alloc;
+ unlock(&p->l);
+ poolfree(p, B2D(q)); /* for backward merge */
+ return poolalloc(p, osize);
+ }
+
+ t->magic = MAGIC_E; /* Make a leader */
+ t->size = ldr;
+ t->csize = ns+ldr;
+ t->clink = p->chain;
+ p->chain = t;
+ B2T(t)->hdr = t;
+ t = B2NB(t);
+
+ t->magic = MAGIC_A; /* Make the block we are going to return */
+ t->size = size;
+ B2T(t)->hdr = t;
+ q = t;
+
+ ns -= size; /* Free the rest */
+ if(ns > 0) {
+ q = B2NB(t);
+ q->size = ns;
+ B2T(q)->hdr = q;
+ pooladd(p, q);
+ }
+ B2NB(q)->magic = MAGIC_E; /* Mark the end of the chunk */
+
+ p->cursize += t->size;
+ if(p->cursize > p->hw)
+ p->hw = p->cursize;
+ unlock(&p->l);
+ if(p->monitor)
+ MM(p->pnum, pc, (ulong)B2D(t), size);
+ return B2D(t);
+}
+
+void *
+poolalloc(Pool *p, ulong asize)
+{
+ Prog *prog;
+
+ if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted)
+ return nil;
+ return dopoolalloc(p, asize, getcallerpc(&p));
+}
+
+void
+poolfree(Pool *p, void *v)
+{
+ Bhdr *b, *c;
+ extern Bhdr *ptr;
+
+ D2B(b, v);
+ if(p->monitor)
+ MM(p->pnum|(1<<8), getcallerpc(&p), (ulong)v, b->size);
+
+ lock(&p->l);
+ p->nfree++;
+ p->cursize -= b->size;
+ c = B2NB(b);
+ if(c->magic == MAGIC_F) { /* Join forward */
+ if(c == ptr)
+ ptr = b;
+ pooldel(p, c);
+ c->magic = 0;
+ b->size += c->size;
+ B2T(b)->hdr = b;
+ }
+
+ c = B2PT(b)->hdr;
+ if(c->magic == MAGIC_F) { /* Join backward */
+ if(b == ptr)
+ ptr = c;
+ pooldel(p, c);
+ b->magic = 0;
+ c->size += b->size;
+ b = c;
+ B2T(b)->hdr = b;
+ }
+ pooladd(p, b);
+ unlock(&p->l);
+}
+
+void *
+poolrealloc(Pool *p, void *v, ulong size)
+{
+ Bhdr *b;
+ void *nv;
+ int osize;
+
+ if(size >= 1024*1024*1024) /* for sanity and to avoid overflow */
+ return nil;
+ if(size == 0){
+ poolfree(p, v);
+ return nil;
+ }
+ SET(osize);
+ if(v != nil){
+ lock(&p->l);
+ D2B(b, v);
+ osize = b->size - BHDRSIZE;
+ unlock(&p->l);
+ if(osize >= size)
+ return v;
+ }
+ nv = poolalloc(p, size);
+ if(nv != nil && v != nil){
+ memmove(nv, v, osize);
+ poolfree(p, v);
+ }
+ return nv;
+}
+
+ulong
+poolmsize(Pool *p, void *v)
+{
+ Bhdr *b;
+ ulong size;
+
+ if(v == nil)
+ return 0;
+ lock(&p->l);
+ D2B(b, v);
+ size = b->size - BHDRSIZE;
+ unlock(&p->l);
+ return size;
+}
+
+static ulong
+poolmax(Pool *p)
+{
+ Bhdr *t;
+ ulong size;
+
+ lock(&p->l);
+ size = p->maxsize - p->cursize;
+ t = p->root;
+ if(t != nil) {
+ while(t->right != nil)
+ t = t->right;
+ if(size < t->size)
+ size = t->size;
+ }
+ if(size >= BHDRSIZE)
+ size -= BHDRSIZE;
+ unlock(&p->l);
+ return size;
+}
+
+ulong
+poolmaxsize(void)
+{
+ int i;
+ ulong total;
+
+ total = 0;
+ for(i = 0; i < nelem(table.pool); i++)
+ total += table.pool[i].maxsize;
+ return total;
+}
+
+int
+poolread(char *va, int count, ulong offset)
+{
+ Pool *p;
+ int n, i, signed_off;
+
+ n = 0;
+ signed_off = offset;
+ for(i = 0; i < table.n; i++) {
+ p = &table.pool[i];
+ n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n",
+ p->cursize,
+ p->maxsize,
+ p->hw,
+ p->nalloc,
+ p->nfree,
+ p->nbrk,
+ poolmax(p),
+ p->name);
+
+ if(signed_off > 0) {
+ signed_off -= n;
+ if(signed_off < 0) {
+ memmove(va, va+n+signed_off, -signed_off);
+ n = -signed_off;
+ }
+ else
+ n = 0;
+ }
+
+ }
+ return n;
+}
+
+void*
+smalloc(size_t size)
+{
+ void *v;
+
+ for(;;){
+ v = malloc(size);
+ if(v != nil)
+ break;
+ if(0)
+ print("smalloc waiting from %lux\n", getcallerpc(&size));
+ osenter();
+ osmillisleep(100);
+ osleave();
+ }
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ return v;
+}
+
+void*
+kmalloc(size_t size)
+{
+ void *v;
+
+ v = dopoolalloc(mainmem, size+Npadlong*sizeof(ulong), getcallerpc(&size));
+ if(v != nil){
+ ML(v, size, getcallerpc(&size));
+ if(Npadlong){
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ memset(v, 0, size);
+ MM(0, getcallerpc(&size), (ulong)v, size);
+ }
+ return v;
+}
+
+
+
+void*
+malloc(size_t size)
+{
+ void *v;
+
+ v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+ if(v != nil){
+ ML(v, size, getcallerpc(&size));
+ if(Npadlong){
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ memset(v, 0, size);
+ MM(0, getcallerpc(&size), (ulong)v, size);
+ } else
+ print("malloc failed from %lux\n", getcallerpc(&size));
+ return v;
+}
+
+void*
+mallocz(ulong size, int clr)
+{
+ void *v;
+
+ v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+ if(v != nil){
+ ML(v, size, getcallerpc(&size));
+ if(Npadlong){
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ if(clr)
+ memset(v, 0, size);
+ MM(0, getcallerpc(&size), (ulong)v, size);
+ } else
+ print("mallocz failed from %lux\n", getcallerpc(&size));
+ return v;
+}
+
+void
+free(void *v)
+{
+ Bhdr *b;
+
+ if(v != nil) {
+ if(Npadlong)
+ v = (ulong*)v-Npadlong;
+ D2B(b, v);
+ ML(v, 0, 0);
+ MM(1<<8|0, getcallerpc(&v), (ulong)((ulong*)v+Npadlong), b->size);
+ poolfree(mainmem, v);
+ }
+}
+
+void*
+realloc(void *v, size_t size)
+{
+ void *nv;
+
+ if(size == 0)
+ return malloc(size); /* temporary change until realloc calls can be checked */
+ if(v != nil)
+ v = (ulong*)v-Npadlong;
+ if(Npadlong!=0 && size!=0)
+ size += Npadlong*sizeof(ulong);
+ nv = poolrealloc(mainmem, v, size);
+ ML(v, 0, 0);
+ ML(nv, size, getcallerpc(&v));
+ if(nv != nil) {
+ nv = (ulong*)nv+Npadlong;
+ setrealloctag(nv, getcallerpc(&v));
+ if(v == nil)
+ setmalloctag(v, getcallerpc(&v));
+ } else
+ print("realloc failed from %lux\n", getcallerpc(&v));
+ return nv;
+}
+
+void
+setmalloctag(void *v, ulong pc)
+{
+ ulong *u;
+
+ USED(v);
+ USED(pc);
+ if(Npadlong <= MallocOffset || v == nil)
+ return;
+ u = v;
+ u[-Npadlong+MallocOffset] = pc;
+}
+
+ulong
+getmalloctag(void *v)
+{
+ USED(v);
+ if(Npadlong <= MallocOffset)
+ return ~0;
+ return ((ulong*)v)[-Npadlong+MallocOffset];
+}
+
+void
+setrealloctag(void *v, ulong pc)
+{
+ ulong *u;
+
+ USED(v);
+ USED(pc);
+ if(Npadlong <= ReallocOffset || v == nil)
+ return;
+ u = v;
+ u[-Npadlong+ReallocOffset] = pc;
+}
+
+ulong
+getrealloctag(void *v)
+{
+ USED(v);
+ if(Npadlong <= ReallocOffset)
+ return ((ulong*)v)[-Npadlong+ReallocOffset];
+ return ~0;
+}
+
+ulong
+msize(void *v)
+{
+ if(v == nil)
+ return 0;
+ return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
+}
+
+void*
+calloc(size_t n, size_t szelem)
+{
+ return malloc(n*szelem);
+}
+
+/*
+void
+pooldump(Pool *p)
+{
+ Bhdr *b, *base, *limit, *ptr;
+
+ b = p->chain;
+ if(b == nil)
+ return;
+ base = b;
+ ptr = b;
+ limit = B2LIMIT(b);
+
+ while(base != nil) {
+ print("\tbase #%.8lux ptr #%.8lux", base, ptr);
+ if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I)
+ print("\tA%.5d\n", ptr->size);
+ else if(ptr->magic == MAGIC_E)
+ print("\tE\tL#%.8lux\tS#%.8lux\n", ptr->clink, ptr->csize);
+ else
+ print("\tF%.5d\tL#%.8lux\tR#%.8lux\tF#%.8lux\tP#%.8lux\tT#%.8lux\n",
+ ptr->size, ptr->left, ptr->right, ptr->fwd, ptr->prev, ptr->parent);
+ ptr = B2NB(ptr);
+ if(ptr >= limit) {
+ print("link to #%.8lux\n", base->clink);
+ base = base->clink;
+ if(base == nil)
+ break;
+ ptr = base;
+ limit = B2LIMIT(base);
+ }
+ }
+}
+*/
+
+void
+poolsetcompact(Pool *p, void (*move)(void*, void*))
+{
+ p->move = move;
+}
+
+int
+poolcompact(Pool *pool)
+{
+ Bhdr *base, *limit, *ptr, *end, *next;
+ int compacted, nb;
+
+ if(pool->move == nil || pool->lastfree == pool->nfree)
+ return 0;
+
+ pool->lastfree = pool->nfree;
+
+ base = pool->chain;
+ ptr = B2NB(base); /* First Block in arena has clink */
+ limit = B2LIMIT(base);
+ compacted = 0;
+
+ pool->root = nil;
+ end = ptr;
+ while(base != nil) {
+ next = B2NB(ptr);
+ if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) {
+ if(ptr != end) {
+ memmove(end, ptr, ptr->size);
+ pool->move(B2D(ptr), B2D(end));
+ compacted = 1;
+ }
+ end = B2NB(end);
+ }
+ if(next >= limit) {
+ nb = (uchar*)limit - (uchar*)end;
+ if(nb > 0){
+ if(nb < pool->quanta+1){
+ print("poolcompact: leftover too small\n");
+ abort();
+ }
+ end->size = nb;
+ B2T(end)->hdr = end;
+ pooladd(pool, end);
+ }
+ base = base->clink;
+ if(base == nil)
+ break;
+ ptr = B2NB(base);
+ end = ptr; /* could do better by copying between chains */
+ limit = B2LIMIT(base);
+ } else
+ ptr = next;
+ }
+
+ return compacted;
+}
+
+static void
+_poolfault(void *v, char *msg, ulong c)
+{
+ auditmemloc(msg, v);
+ panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c);
+}
+
+static void
+dumpvl(char *msg, ulong *v, int n)
+{
+ int i, l;
+
+ l = print("%s at %p: ", msg, v);
+ for (i = 0; i < n; i++) {
+ if (l >= 60) {
+ print("\n");
+ l = print(" %p: ", v);
+ }
+ l += print(" %lux", *v++);
+ }
+ print("\n");
+}
+
+static void
+corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v)
+{
+ print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n",
+ str, v, p->name, msg, b, b->size, b->magic);
+ dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10);
+}
+
+static void
+_auditmemloc(char *str, void *v)
+{
+ Pool *p;
+ Bhdr *bc, *ec, *b, *nb, *fb = nil;
+ char *fmsg, *msg;
+ ulong fsz;
+
+ SET(fsz);
+ SET(fmsg);
+ for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
+ lock(&p->l);
+ for (bc = p->chain; bc != nil; bc = bc->clink) {
+ if (bc->magic != MAGIC_E) {
+ unlock(&p->l);
+ corrupted(str, "chain hdr!=MAGIC_E", p, bc, v);
+ goto nextpool;
+ }
+ ec = B2LIMIT(bc);
+ if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec))
+ goto found;
+ }
+ unlock(&p->l);
+nextpool: ;
+ }
+ print("%s: %p not in pools\n", str, v);
+ return;
+
+found:
+ for (b = bc; b < ec; b = nb) {
+ switch(b->magic) {
+ case MAGIC_F:
+ msg = "free blk";
+ break;
+ case MAGIC_I:
+ msg = "immutable block";
+ break;
+ case MAGIC_A:
+ msg = "block";
+ break;
+ default:
+ if (b == bc && b->magic == MAGIC_E) {
+ msg = "pool hdr";
+ break;
+ }
+ unlock(&p->l);
+ corrupted(str, "bad magic", p, b, v);
+ goto badchunk;
+ }
+ if (b->size <= 0 || (b->size & p->quanta)) {
+ unlock(&p->l);
+ corrupted(str, "bad size", p, b, v);
+ goto badchunk;
+ }
+ if (fb != nil)
+ break;
+ nb = B2NB(b);
+ if ((Bhdr*)v < nb) {
+ fb = b;
+ fsz = b->size;
+ fmsg = msg;
+ }
+ }
+ unlock(&p->l);
+ if (b >= ec) {
+ if (b > ec)
+ corrupted(str, "chain size mismatch", p, b, v);
+ else if (b->magic != MAGIC_E)
+ corrupted(str, "chain end!=MAGIC_E", p, b, v);
+ }
+badchunk:
+ if (fb != nil) {
+ print("%s: %p in %s:", str, v, p->name);
+ if (fb == v)
+ print(" is %s '%lux\n", fmsg, fsz);
+ else
+ print(" in %s at %p'%lux\n", fmsg, fb, fsz);
+ dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20);
+ }
+}
+
+char *
+poolaudit(char*(*audit)(int, Bhdr *))
+{
+ Pool *p;
+ Bhdr *bc, *ec, *b;
+ char *r = nil;
+
+ for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
+ lock(&p->l);
+ for (bc = p->chain; bc != nil; bc = bc->clink) {
+ if (bc->magic != MAGIC_E) {
+ unlock(&p->l);
+ return "bad chain hdr";
+ }
+ ec = B2LIMIT(bc);
+ for (b = bc; b < ec; b = B2NB(b)) {
+ if (b->size <= 0 || (b->size & p->quanta))
+ r = "bad size in bhdr";
+ else
+ switch(b->magic) {
+ case MAGIC_E:
+ if (b != bc) {
+ r = "unexpected MAGIC_E";
+ break;
+ }
+ case MAGIC_F:
+ case MAGIC_A:
+ case MAGIC_I:
+ r = audit(p->pnum, b);
+ break;
+ default:
+ r = "bad magic";
+ }
+ if (r != nil) {
+ unlock(&p->l);
+ return r;
+ }
+ }
+ if (b != ec || b->magic != MAGIC_E) {
+ unlock(&p->l);
+ return "bad chain ending";
+ }
+ }
+ unlock(&p->l);
+ }
+ return r;
+}
--- /dev/null
+++ b/emu/port/audio-tbls.c
@@ -1,0 +1,53 @@
+svp_t audio_bits_tbl[] = {
+ { "8", 8 } , /* 8 bits per sample */
+ { "16", 16 }, /* 16 bits per sample */
+ {nil},
+};
+
+svp_t audio_chan_tbl[] = {
+ { "1", 1 }, /* 1 channel */
+ { "2", 2 }, /* 2 channels */
+ {nil},
+};
+
+svp_t audio_indev_tbl[] = {
+ { "mic", Audio_Mic_Val }, /* input microphone */
+ { "line", Audio_Linein_Val }, /* line in */
+ {nil},
+};
+
+svp_t audio_outdev_tbl[] = {
+ { "spkr", Audio_Speaker_Val }, /* output speaker */
+ { "hdph", Audio_Headphone_Val },/* head phones */
+ { "line", Audio_Lineout_Val }, /* line out */
+ {nil},
+};
+
+svp_t audio_enc_tbl[] = {
+ { "ulaw", Audio_Ulaw_Val }, /* u-law encoding */
+ { "alaw", Audio_Alaw_Val }, /* A-law encoding */
+ { "pcm", Audio_Pcm_Val }, /* Pulse Code Modulation */
+ {nil},
+};
+
+svp_t audio_rate_tbl[] = {
+ { "8000", 8000 }, /* 8000 samples per second */
+ { "11025", 11025 }, /* 11025 samples per second */
+ { "22050", 22050 }, /* 22050 samples per second */
+ { "44100", 44100 }, /* 44100 samples per second */
+ {nil},
+};
+
+Audio_d Default_Audio_Format = {
+ 0,
+ 16, /* bits per sample */
+ Audio_Max_Val, /* buffer size (as percentage) */
+ 2, /* number of channels */
+ -1, /* device */
+ Audio_Pcm_Val, /* encoding format */
+ 8000, /* samples per second */
+ Audio_Max_Val, /* left channel gain */
+ Audio_Max_Val, /* right channel gain */
+};
+int Default_Audio_Input = Audio_Mic_Val;
+int Default_Audio_Output = Audio_Speaker_Val;
--- /dev/null
+++ b/emu/port/audio.h
@@ -1,0 +1,81 @@
+#define AUDIO_BITS_FLAG 0x00000001
+#define AUDIO_BUF_FLAG 0x00000002
+#define AUDIO_CHAN_FLAG 0x00000004
+#define AUDIO_COUNT_FLAG 0x00000008
+#define AUDIO_DEV_FLAG 0x00000010
+#define AUDIO_ENC_FLAG 0x00000020
+#define AUDIO_RATE_FLAG 0x00000040
+#define AUDIO_VOL_FLAG 0x00000080
+#define AUDIO_LEFT_FLAG 0x00000100
+#define AUDIO_RIGHT_FLAG 0x00000200
+#define AUDIO_IN_FLAG 0x00000400
+#define AUDIO_OUT_FLAG 0x00000800
+#define AUDIO_MOD_FLAG 0x10000000
+
+#define Audio_Min_Val 0
+#define Audio_Max_Val 100
+
+#define Audio_No_Val 0
+#define Audio_In_Val 1
+#define Audio_Out_Val 2
+
+#define Audio_Max_Buf 32768
+#define Bits_Per_Byte 8
+
+typedef struct Audio_d Audio_d;
+struct Audio_d {
+ ulong flags; /* bit flag for fields */
+ ulong bits; /* bits per sample */
+ ulong buf; /* buffer size */
+ ulong chan; /* number of channels */
+ ulong dev; /* device */
+ ulong enc; /* encoding format */
+ ulong rate; /* samples per second */
+ ulong left; /* left channel gain */
+ ulong right; /* right channel gain */
+};
+
+typedef struct Audio_t Audio_t;
+struct Audio_t {
+ Audio_d in; /* input device */
+ Audio_d out; /* output device */
+};
+
+#define AUDIO_CMD_MAXNUM 32
+
+void audio_info_init(Audio_t*);
+int audioparse(char*, int n, Audio_t*);
+
+enum
+{
+ Qdir = 0, /* must start at 0 representing a directory */
+ Qaudio,
+ Qaudioctl
+};
+
+/* required external platform specific functions */
+void audio_file_init(void);
+void audio_file_open(Chan*, int);
+long audio_file_read(Chan*, void*, long, vlong);
+long audio_file_write(Chan*, void*, long, vlong);
+long audio_ctl_write(Chan*, void*, long, vlong);
+void audio_file_close(Chan*);
+
+typedef struct _svp_t {
+ char* s; /* string */
+ unsigned long v; /* value */
+} svp_t;
+
+/* string value pairs for default audio values */
+extern svp_t audio_chan_tbl[];
+extern svp_t audio_indev_tbl[];
+extern svp_t audio_outdev_tbl[];
+extern svp_t audio_enc_tbl[];
+extern svp_t audio_rate_tbl[];
+extern svp_t audio_val_tbl[];
+extern svp_t audio_bits_tbl[];
+
+extern Audio_d Default_Audio_Format;
+extern int Default_Audio_Input, Default_Audio_Output;
+
+extern Audio_t* getaudiodev(void);
--- /dev/null
+++ b/emu/port/cache.c
@@ -1,0 +1,45 @@
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * no cache in hosted mode
+ */
+void
+cinit(void)
+{
+}
+
+void
+copen(Chan *c)
+{
+ c->flag &= ~CCACHE;
+}
+
+int
+cread(Chan *c, uchar *b, int n, vlong off)
+{
+ USED(c);
+ USED(b);
+ USED(n);
+ USED(off);
+ return 0;
+}
+
+void
+cwrite(Chan *c, uchar *buf, int n, vlong off)
+{
+ USED(c);
+ USED(buf);
+ USED(n);
+ USED(off);
+}
+
+void
+cupdate(Chan *c, uchar *buf, int n, vlong off)
+{
+ USED(c);
+ USED(buf);
+ USED(n);
+ USED(off);
+}
+
--- /dev/null
+++ b/emu/port/chan.c
@@ -1,0 +1,1403 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+char*
+c2name(Chan *c) /* DEBUGGING */
+{
+ if(c == nil)
+ return "<nil chan>";
+ if(c->name == nil)
+ return "<nil name>";
+ if(c->name->s == nil)
+ return "<nil name.s>";
+ return c->name->s;
+}
+
+enum
+{
+ CNAMESLOP = 20
+};
+
+struct
+{
+ Lock l;
+ int fid;
+ Chan *free;
+ Chan *list;
+}chanalloc;
+
+typedef struct Elemlist Elemlist;
+
+struct Elemlist
+{
+ char *name; /* copy of name, so '/' can be overwritten */
+ int nelems;
+ char **elems;
+ int *off;
+ int mustbedir;
+};
+
+#define SEP(c) ((c) == 0 || (c) == '/')
+void cleancname(Cname*);
+
+int
+isdotdot(char *p)
+{
+ return p[0]=='.' && p[1]=='.' && p[2]=='\0';
+}
+
+int
+incref(Ref *r)
+{
+ int x;
+
+ lock(&r->lk);
+ x = ++r->ref;
+ unlock(&r->lk);
+ return x;
+}
+
+int
+decref(Ref *r)
+{
+ int x;
+
+ lock(&r->lk);
+ x = --r->ref;
+ unlock(&r->lk);
+ if(x < 0)
+ panic("decref, pc=0x%lux", getcallerpc(&r));
+
+ return x;
+}
+
+/*
+ * Rather than strncpy, which zeros the rest of the buffer, kstrcpy
+ * truncates if necessary, always zero terminates, does not zero fill,
+ * and puts ... at the end of the string if it's too long. Usually used to
+ * save a string in up->genbuf;
+ */
+void
+kstrcpy(char *s, char *t, int ns)
+{
+ int nt;
+
+ nt = strlen(t);
+ if(nt+1 <= ns){
+ memmove(s, t, nt+1);
+ return;
+ }
+ /* too long */
+ if(ns < 4){
+ /* but very short! */
+ strncpy(s, t, ns);
+ return;
+ }
+ /* truncate with ... at character boundary (very rare case) */
+ memmove(s, t, ns-4);
+ ns -= 4;
+ s[ns] = '\0';
+ /* look for first byte of UTF-8 sequence by skipping continuation bytes */
+ while(ns>0 && (s[--ns]&0xC0)==0x80)
+ ;
+ strcpy(s+ns, "...");
+}
+
+int
+emptystr(char *s)
+{
+ return s == nil || s[0] == '\0';
+}
+
+/*
+ * Atomically replace *p with copy of s
+ */
+void
+kstrdup(char **p, char *s)
+{
+ int n;
+ char *t, *prev;
+
+ n = strlen(s)+1;
+ t = kmalloc(n);
+ if(t == nil)
+ panic("kstrdup: no memory");
+ memmove(t, s, n);
+ prev = *p;
+ *p = t;
+ free(prev);
+}
+
+static char isfrog[256]=
+{
+ /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+void
+chandevinit(void)
+{
+ int i;
+
+
+/* isfrog[' '] = 1; */ /* let's see what happens */
+ isfrog['/'] = 1;
+ isfrog[0x7f] = 1;
+
+ for(i=0; devtab[i] != nil; i++)
+ devtab[i]->init();
+}
+
+Chan*
+newchan(void)
+{
+ Chan *c;
+
+ lock(&chanalloc.l);
+ c = chanalloc.free;
+ if(c != 0)
+ chanalloc.free = c->next;
+ unlock(&chanalloc.l);
+
+ if(c == nil) {
+ c = malloc(sizeof(Chan));
+ if(c == nil)
+ error(Enomem);
+ lock(&chanalloc.l);
+ c->fid = ++chanalloc.fid;
+ c->link = chanalloc.list;
+ chanalloc.list = c;
+ unlock(&chanalloc.l);
+ }
+
+ /* if you get an error before associating with a dev,
+ close calls rootclose, a nop */
+ c->type = 0;
+ c->flag = 0;
+ c->r.ref = 1;
+ c->dev = 0;
+ c->offset = 0;
+ c->iounit = 0;
+ c->umh = 0;
+ c->uri = 0;
+ c->dri = 0;
+ c->aux = 0;
+ c->mchan = 0;
+ c->mcp = 0;
+ c->mux = 0;
+ c->mqid.path = 0;
+ c->mqid.vers = 0;
+ c->mqid.type = 0;
+ c->name = 0;
+ return c;
+}
+
+static Ref ncname;
+
+Cname*
+newcname(char *s)
+{
+ Cname *n;
+ int i;
+
+ n = smalloc(sizeof(Cname));
+ i = strlen(s);
+ n->len = i;
+ n->alen = i+CNAMESLOP;
+ n->s = smalloc(n->alen);
+ memmove(n->s, s, i+1);
+ n->r.ref = 1;
+ incref(&ncname);
+ return n;
+}
+
+void
+cnameclose(Cname *n)
+{
+ if(n == nil)
+ return;
+ if(decref(&n->r))
+ return;
+ decref(&ncname);
+ free(n->s);
+ free(n);
+}
+
+Cname*
+addelem(Cname *n, char *s)
+{
+ int i, a;
+ char *t;
+ Cname *new;
+
+ if(s[0]=='.' && s[1]=='\0')
+ return n;
+
+ if(n->r.ref > 1){
+ /* copy on write */
+ new = newcname(n->s);
+ cnameclose(n);
+ n = new;
+ }
+
+ i = strlen(s);
+ if(n->len+1+i+1 > n->alen){
+ a = n->len+1+i+1 + CNAMESLOP;
+ t = smalloc(a);
+ memmove(t, n->s, n->len+1);
+ free(n->s);
+ n->s = t;
+ n->alen = a;
+ }
+ if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/') /* don't insert extra slash if one is present */
+ n->s[n->len++] = '/';
+ memmove(n->s+n->len, s, i+1);
+ n->len += i;
+ if(isdotdot(s))
+ cleancname(n);
+ return n;
+}
+
+void
+chanfree(Chan *c)
+{
+ c->flag = CFREE;
+
+ if(c->umh != nil){
+ putmhead(c->umh);
+ c->umh = nil;
+ }
+ if(c->umc != nil){
+ cclose(c->umc);
+ c->umc = nil;
+ }
+ if(c->mux != nil){
+ muxclose(c->mux);
+ c->mux = nil;
+ }
+ if(c->mchan != nil){
+ cclose(c->mchan);
+ c->mchan = nil;
+ }
+
+ cnameclose(c->name);
+
+ lock(&chanalloc.l);
+ c->next = chanalloc.free;
+ chanalloc.free = c;
+ unlock(&chanalloc.l);
+}
+
+void
+cclose(Chan *c)
+{
+ if(c == 0)
+ return;
+
+ if(c->flag&CFREE)
+ panic("cclose %lux", getcallerpc(&c));
+
+ if(decref(&c->r))
+ return;
+
+ if(!waserror()){
+ devtab[c->type]->close(c);
+ poperror();
+ }
+
+ chanfree(c);
+}
+
+/*
+ * Make sure we have the only copy of c. (Copy on write.)
+ */
+Chan*
+cunique(Chan *c)
+{
+ Chan *nc;
+
+ if(c->r.ref != 1) {
+ nc = cclone(c);
+ cclose(c);
+ c = nc;
+ }
+
+ return c;
+}
+
+int
+eqqid(Qid a, Qid b)
+{
+ return a.path==b.path && a.vers==b.vers;
+}
+
+int
+eqchan(Chan *a, Chan *b, int pathonly)
+{
+ if(a->qid.path != b->qid.path)
+ return 0;
+ if(!pathonly && a->qid.vers!=b->qid.vers)
+ return 0;
+ if(a->type != b->type)
+ return 0;
+ if(a->dev != b->dev)
+ return 0;
+ return 1;
+}
+
+int
+eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly)
+{
+ if(a->qid.path != qid.path)
+ return 0;
+ if(!pathonly && a->qid.vers!=qid.vers)
+ return 0;
+ if(a->type != type)
+ return 0;
+ if(a->dev != dev)
+ return 0;
+ return 1;
+}
+
+Mhead*
+newmhead(Chan *from)
+{
+ Mhead *mh;
+
+ mh = smalloc(sizeof(Mhead));
+ mh->r.ref = 1;
+ mh->from = from;
+ incref(&from->r);
+
+/*
+ n = from->name->len;
+ if(n >= sizeof(mh->fromname))
+ n = sizeof(mh->fromname)-1;
+ memmove(mh->fromname, from->name->s, n);
+ mh->fromname[n] = 0;
+*/
+ return mh;
+}
+
+int
+cmount(Chan *new, Chan *old, int flag, char *spec)
+{
+ Pgrp *pg;
+ int order, flg;
+ Mhead *m, **l, *mh;
+ Mount *nm, *f, *um, **h;
+
+ if(QTDIR & (old->qid.type^new->qid.type))
+ error(Emount);
+
+if(old->umh)
+ print("cmount old extra umh\n");
+
+ order = flag&MORDER;
+
+ if((old->qid.type&QTDIR)==0 && order != MREPL)
+ error(Emount);
+
+ mh = new->umh;
+
+ /*
+ * Not allowed to bind when the old directory
+ * is itself a union. (Maybe it should be allowed, but I don't see
+ * what the semantics would be.)
+ *
+ * We need to check mh->mount->next to tell unions apart from
+ * simple mount points, so that things like
+ * mount -c fd /root
+ * bind -c /root /
+ * work. The check of mount->mflag catches things like
+ * mount fd /root
+ * bind -c /root /
+ *
+ * This is far more complicated than it should be, but I don't
+ * see an easier way at the moment. -rsc
+ */
+ if((flag&MCREATE) && mh && mh->mount
+ && (mh->mount->next || !(mh->mount->mflag&MCREATE)))
+ error(Emount);
+
+ pg = up->env->pgrp;
+ wlock(&pg->ns);
+
+ l = &MOUNTH(pg, old->qid);
+ for(m = *l; m; m = m->hash) {
+ if(eqchan(m->from, old, 1))
+ break;
+ l = &m->hash;
+ }
+
+ if(m == nil) {
+ /*
+ * nothing mounted here yet. create a mount
+ * head and add to the hash table.
+ */
+ m = newmhead(old);
+ *l = m;
+
+ /*
+ * if this is a union mount, add the old
+ * node to the mount chain.
+ */
+ if(order != MREPL)
+ m->mount = newmount(m, old, 0, 0);
+ }
+ wlock(&m->lock);
+ if(waserror()){
+ wunlock(&m->lock);
+ nexterror();
+ }
+ wunlock(&pg->ns);
+
+ nm = newmount(m, new, flag, spec);
+ if(mh != nil && mh->mount != nil) {
+ /*
+ * copy a union when binding it onto a directory
+ */
+ flg = order;
+ if(order == MREPL)
+ flg = MAFTER;
+ h = &nm->next;
+ um = mh->mount;
+ for(um = um->next; um; um = um->next) {
+ f = newmount(m, um->to, flg, um->spec);
+ *h = f;
+ h = &f->next;
+ }
+ }
+
+ if(m->mount && order == MREPL) {
+ mountfree(m->mount);
+ m->mount = 0;
+ }
+
+ if(flag & MCREATE)
+ nm->mflag |= MCREATE;
+
+ if(m->mount && order == MAFTER) {
+ for(f = m->mount; f->next; f = f->next)
+ ;
+ f->next = nm;
+ }
+ else {
+ for(f = nm; f->next; f = f->next)
+ ;
+ f->next = m->mount;
+ m->mount = nm;
+ }
+
+ wunlock(&m->lock);
+ poperror();
+ return nm->mountid;
+}
+
+void
+cunmount(Chan *mnt, Chan *mounted)
+{
+ Pgrp *pg;
+ Mhead *m, **l;
+ Mount *f, **p;
+
+ if(mnt->umh) /* should not happen */
+ print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh);
+
+ /*
+ * It _can_ happen that mounted->umh is non-nil,
+ * because mounted is the result of namec(Aopen)
+ * (see sysfile.c:/^sysunmount).
+ * If we open a union directory, it will have a umh.
+ * Although surprising, this is okay, since the
+ * cclose will take care of freeing the umh.
+ */
+
+ pg = up->env->pgrp;
+ wlock(&pg->ns);
+
+ l = &MOUNTH(pg, mnt->qid);
+ for(m = *l; m; m = m->hash) {
+ if(eqchan(m->from, mnt, 1))
+ break;
+ l = &m->hash;
+ }
+
+ if(m == 0) {
+ wunlock(&pg->ns);
+ error(Eunmount);
+ }
+
+ wlock(&m->lock);
+ if(mounted == 0) {
+ *l = m->hash;
+ wunlock(&pg->ns);
+ mountfree(m->mount);
+ m->mount = nil;
+ cclose(m->from);
+ wunlock(&m->lock);
+ putmhead(m);
+ return;
+ }
+
+ p = &m->mount;
+ for(f = *p; f; f = f->next) {
+ /* BUG: Needs to be 2 pass */
+ if(eqchan(f->to, mounted, 1) ||
+ (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) {
+ *p = f->next;
+ f->next = 0;
+ mountfree(f);
+ if(m->mount == nil) {
+ *l = m->hash;
+ cclose(m->from);
+ wunlock(&m->lock);
+ wunlock(&pg->ns);
+ putmhead(m);
+ return;
+ }
+ wunlock(&m->lock);
+ wunlock(&pg->ns);
+ return;
+ }
+ p = &f->next;
+ }
+ wunlock(&m->lock);
+ wunlock(&pg->ns);
+ error(Eunion);
+}
+
+Chan*
+cclone(Chan *c)
+{
+ Chan *nc;
+ Walkqid *wq;
+
+ wq = devtab[c->type]->walk(c, nil, nil, 0);
+ if(wq == nil)
+ error("clone failed");
+ nc = wq->clone;
+ free(wq);
+ nc->name = c->name;
+ if(c->name)
+ incref(&c->name->r);
+ return nc;
+}
+
+int
+findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid)
+{
+ Pgrp *pg;
+ Mhead *m;
+
+ pg = up->env->pgrp;
+ rlock(&pg->ns);
+ for(m = MOUNTH(pg, qid); m; m = m->hash){
+ rlock(&m->lock);
+if(m->from == nil){
+ print("m %p m->from 0\n", m);
+ runlock(&m->lock);
+ continue;
+}
+ if(eqchantdqid(m->from, type, dev, qid, 1)) {
+ runlock(&pg->ns);
+ if(mp != nil){
+ incref(&m->r);
+ if(*mp != nil)
+ putmhead(*mp);
+ *mp = m;
+ }
+ if(*cp != nil)
+ cclose(*cp);
+ incref(&m->mount->to->r);
+ *cp = m->mount->to;
+ runlock(&m->lock);
+ return 1;
+ }
+ runlock(&m->lock);
+ }
+
+ runlock(&pg->ns);
+ return 0;
+}
+
+int
+domount(Chan **cp, Mhead **mp)
+{
+ return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid);
+}
+
+Chan*
+undomount(Chan *c, Cname *name)
+{
+ Chan *nc;
+ Pgrp *pg;
+ Mount *t;
+ Mhead **h, **he, *f;
+
+ pg = up->env->pgrp;
+ rlock(&pg->ns);
+ if(waserror()) {
+ runlock(&pg->ns);
+ nexterror();
+ }
+
+ he = &pg->mnthash[MNTHASH];
+ for(h = pg->mnthash; h < he; h++) {
+ for(f = *h; f; f = f->hash) {
+ if(strcmp(f->from->name->s, name->s) != 0)
+ continue;
+ for(t = f->mount; t; t = t->next) {
+ if(eqchan(c, t->to, 1)) {
+ /*
+ * We want to come out on the left hand side of the mount
+ * point using the element of the union that we entered on.
+ * To do this, find the element that has a from name of
+ * c->name->s.
+ */
+ if(strcmp(t->head->from->name->s, name->s) != 0)
+ continue;
+ nc = t->head->from;
+ incref(&nc->r);
+ cclose(c);
+ c = nc;
+ break;
+ }
+ }
+ }
+ }
+ poperror();
+ runlock(&pg->ns);
+ return c;
+}
+
+/*
+ * Either walks all the way or not at all. No partial results in *cp.
+ * *nerror is the number of names to display in an error message.
+ */
+static char Edoesnotexist[] = "does not exist";
+int
+walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
+{
+ int dev, dotdot, i, n, nhave, ntry, type;
+ Chan *c, *nc;
+ Cname *cname;
+ Mount *f;
+ Mhead *mh, *nmh;
+ Walkqid *wq;
+
+ c = *cp;
+ incref(&c->r);
+ cname = c->name;
+ incref(&cname->r);
+ mh = nil;
+
+ /*
+ * While we haven't gotten all the way down the path:
+ * 1. step through a mount point, if any
+ * 2. send a walk request for initial dotdot or initial prefix without dotdot
+ * 3. move to the first mountpoint along the way.
+ * 4. repeat.
+ *
+ * An invariant is that each time through the loop, c is on the undomount
+ * side of the mount point, and c's name is cname.
+ */
+ for(nhave=0; nhave<nnames; nhave+=n){
+ if((c->qid.type&QTDIR)==0){
+ if(nerror)
+ *nerror = nhave;
+ cnameclose(cname);
+ cclose(c);
+ strcpy(up->env->errstr, Enotdir);
+ if(mh != nil)
+ putmhead(mh);
+ return -1;
+ }
+ ntry = nnames - nhave;
+ if(ntry > MAXWELEM)
+ ntry = MAXWELEM;
+ dotdot = 0;
+ for(i=0; i<ntry; i++){
+ if(isdotdot(names[nhave+i])){
+ if(i==0) {
+ dotdot = 1;
+ ntry = 1;
+ } else
+ ntry = i;
+ break;
+ }
+ }
+
+ if(!dotdot && !nomount)
+ domount(&c, &mh);
+
+ type = c->type;
+ dev = c->dev;
+
+ if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){
+ /* try a union mount, if any */
+ if(mh && !nomount){
+ /*
+ * mh->mount == c, so start at mh->mount->next
+ */
+ rlock(&mh->lock);
+ for(f = mh->mount->next; f; f = f->next)
+ if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil)
+ break;
+ runlock(&mh->lock);
+ if(f != nil){
+ type = f->to->type;
+ dev = f->to->dev;
+ }
+ }
+ if(wq == nil){
+ cclose(c);
+ cnameclose(cname);
+ if(nerror)
+ *nerror = nhave+1;
+ if(mh != nil)
+ putmhead(mh);
+ return -1;
+ }
+ }
+
+ nmh = nil;
+ if(dotdot) {
+ assert(wq->nqid == 1);
+ assert(wq->clone != nil);
+
+ cname = addelem(cname, "..");
+ nc = undomount(wq->clone, cname);
+ n = 1;
+ } else {
+ nc = nil;
+ if(!nomount)
+ for(i=0; i<wq->nqid && i<ntry-1; i++)
+ if(findmount(&nc, &nmh, type, dev, wq->qid[i]))
+ break;
+ if(nc == nil){ /* no mount points along path */
+ if(wq->clone == nil){
+ cclose(c);
+ cnameclose(cname);
+ if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){
+ if(nerror)
+ *nerror = nhave+wq->nqid+1;
+ strcpy(up->env->errstr, Edoesnotexist);
+ }else{
+ if(nerror)
+ *nerror = nhave+wq->nqid;
+ strcpy(up->env->errstr, Enotdir);
+ }
+ free(wq);
+ if(mh != nil)
+ putmhead(mh);
+ return -1;
+ }
+ n = wq->nqid;
+ nc = wq->clone;
+ }else{ /* stopped early, at a mount point */
+ if(wq->clone != nil){
+ cclose(wq->clone);
+ wq->clone = nil;
+ }
+ n = i+1;
+ }
+ for(i=0; i<n; i++)
+ cname = addelem(cname, names[nhave+i]);
+ }
+ cclose(c);
+ c = nc;
+ putmhead(mh);
+ mh = nmh;
+ free(wq);
+ }
+
+ putmhead(mh);
+
+ c = cunique(c);
+
+ if(c->umh != nil){ /* BUG */
+ print("walk umh\n");
+ putmhead(c->umh);
+ c->umh = nil;
+ }
+
+ cnameclose(c->name);
+ c->name = cname;
+
+ cclose(*cp);
+ *cp = c;
+ if(nerror)
+ *nerror = 0;
+ return 0;
+}
+
+/*
+ * c is a mounted non-creatable directory. find a creatable one.
+ */
+Chan*
+createdir(Chan *c, Mhead *m)
+{
+ Chan *nc;
+ Mount *f;
+
+ rlock(&m->lock);
+ if(waserror()) {
+ runlock(&m->lock);
+ nexterror();
+ }
+ for(f = m->mount; f; f = f->next) {
+ if(f->mflag&MCREATE) {
+ nc = cclone(f->to);
+ runlock(&m->lock);
+ poperror();
+ cclose(c);
+ return nc;
+ }
+ }
+ error(Enocreate);
+ return 0;
+}
+
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ */
+void
+cleancname(Cname *n)
+{
+ char *p;
+
+ if(n->s[0] == '#'){
+ p = strchr(n->s, '/');
+ if(p == nil)
+ return;
+ cleanname(p);
+
+ /*
+ * The correct name is #i rather than #i/,
+ * but the correct name of #/ is #/.
+ */
+ if(strcmp(p, "/")==0 && n->s[1] != '/')
+ *p = '\0';
+ }else
+ cleanname(n->s);
+ n->len = strlen(n->s);
+}
+
+static void
+growparse(Elemlist *e)
+{
+ char **new;
+ int *inew;
+ enum { Delta = 8 };
+
+ if(e->nelems % Delta == 0){
+ new = smalloc((e->nelems+Delta) * sizeof(char*));
+ memmove(new, e->elems, e->nelems*sizeof(char*));
+ free(e->elems);
+ e->elems = new;
+ inew = smalloc((e->nelems+Delta+1) * sizeof(int));
+ memmove(inew, e->off, e->nelems*sizeof(int));
+ free(e->off);
+ e->off = inew;
+ }
+}
+
+/*
+ * The name is known to be valid.
+ * Copy the name so slashes can be overwritten.
+ * An empty string will set nelem=0.
+ * A path ending in / or /. or /.//./ etc. will have
+ * e.mustbedir = 1, so that we correctly
+ * reject, e.g., "/adm/users/." when /adm/users is a file
+ * rather than a directory.
+ */
+static void
+parsename(char *name, Elemlist *e)
+{
+ char *slash;
+
+ kstrdup(&e->name, name);
+ name = e->name;
+ e->nelems = 0;
+ e->elems = nil;
+ e->off = smalloc(sizeof(int));
+ e->off[0] = skipslash(name) - name;
+ for(;;){
+ name = skipslash(name);
+ if(*name=='\0'){
+ e->mustbedir = 1;
+ break;
+ }
+ growparse(e);
+
+ e->elems[e->nelems++] = name;
+ slash = utfrune(name, '/');
+ if(slash == nil){
+ e->off[e->nelems] = name+strlen(name) - e->name;
+ e->mustbedir = 0;
+ break;
+ }
+ e->off[e->nelems] = slash - e->name;
+ *slash++ = '\0';
+ name = slash;
+ }
+}
+
+static void*
+kmemrchr(void *va, int c, long n)
+{
+ uchar *a, *e;
+
+ a = va;
+ for(e=a+n-1; e>a; e--)
+ if(*e == c)
+ return e;
+ return nil;
+}
+
+static void
+saveregisters(void)
+{
+}
+
+/*
+ * Turn a name into a channel.
+ * &name[0] is known to be a valid address. It may be a kernel address.
+ *
+ * Opening with amode Aopen, Acreate, or Aremove guarantees
+ * that the result will be the only reference to that particular fid.
+ * This is necessary since we might pass the result to
+ * devtab[]->remove().
+ *
+ * Opening Atodir, Amount, or Aaccess does not guarantee this.
+ *
+ * Opening Aaccess can, under certain conditions, return a
+ * correct Chan* but with an incorrect Cname attached.
+ * Since the functions that open Aaccess (sysstat, syswstat, sys_stat)
+ * do not use the Cname*, this avoids an unnecessary clone.
+ */
+Chan*
+namec(char *aname, int amode, int omode, ulong perm)
+{
+ int n, prefix, len, t, nomount, npath;
+ Chan *c, *cnew;
+ Cname *cname;
+ Elemlist e;
+ Rune r;
+ Mhead *m;
+ char *createerr, tmperrbuf[ERRMAX];
+ char *name;
+
+ name = aname;
+ if(name[0] == '\0')
+ error("empty file name");
+ validname(name, 1);
+
+ /*
+ * Find the starting off point (the current slash, the root of
+ * a device tree, or the current dot) as well as the name to
+ * evaluate starting there.
+ */
+ nomount = 0;
+ switch(name[0]){
+ case '/':
+ c = up->env->pgrp->slash;
+ incref(&c->r);
+ break;
+
+ case '#':
+ nomount = 1;
+ up->genbuf[0] = '\0';
+ n = 0;
+ while(*name!='\0' && (*name != '/' || n < 2)){
+ if(n >= sizeof(up->genbuf)-1)
+ error(Efilename);
+ up->genbuf[n++] = *name++;
+ }
+ up->genbuf[n] = '\0';
+ n = chartorune(&r, up->genbuf+1)+1;
+ if(r == 'M')
+ error(Enoattach);
+ /*
+ * the nodevs exceptions are
+ * | it only gives access to pipes you create
+ * e this process's environment
+ * s private file2chan creation space
+ * D private secure sockets name space
+ * a private TLS name space
+ */
+ if(up->env->pgrp->nodevs &&
+ (utfrune("|esDa", r) == nil || r == 's' && up->genbuf[n]!='\0'))
+ error(Enoattach);
+ t = devno(r, 1);
+ if(t == -1)
+ error(Ebadsharp);
+ c = devtab[t]->attach(up->genbuf+n);
+ break;
+
+ default:
+ c = up->env->pgrp->dot;
+ incref(&c->r);
+ break;
+ }
+ prefix = name - aname;
+
+ e.name = nil;
+ e.elems = nil;
+ e.off = nil;
+ e.nelems = 0;
+ if(waserror()){
+ cclose(c);
+ free(e.name);
+ free(e.elems);
+ free(e.off);
+//dumpmount();
+ nexterror();
+ }
+
+ /*
+ * Build a list of elements in the path.
+ */
+ parsename(name, &e);
+
+ /*
+ * On create, ....
+ */
+ if(amode == Acreate){
+ /* perm must have DMDIR if last element is / or /. */
+ if(e.mustbedir && !(perm&DMDIR)){
+ npath = e.nelems;
+ strcpy(tmperrbuf, "create without DMDIR");
+ goto NameError;
+ }
+
+ /* don't try to walk the last path element just yet. */
+ if(e.nelems == 0)
+ error(Eexist);
+ e.nelems--;
+ }
+
+ if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){
+ if(npath < 0 || npath > e.nelems){
+ print("namec %s walk error npath=%d\n", aname, npath);
+ nexterror();
+ }
+ strcpy(tmperrbuf, up->env->errstr);
+ NameError:
+ len = prefix+e.off[npath];
+ if(len < ERRMAX/3 || (name=kmemrchr(aname, '/', len))==nil || name==aname)
+ snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname);
+ else
+ snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name);
+ snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf);
+ nexterror();
+ }
+
+ if(e.mustbedir && !(c->qid.type&QTDIR)){
+ npath = e.nelems;
+ strcpy(tmperrbuf, "not a directory");
+ goto NameError;
+ }
+
+ if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){
+ npath = e.nelems;
+ error("cannot exec directory");
+ }
+
+ switch(amode){
+ case Aaccess:
+ if(!nomount)
+ domount(&c, nil);
+ break;
+
+ case Abind:
+ m = nil;
+ if(!nomount)
+ domount(&c, &m);
+ if(c->umh != nil)
+ putmhead(c->umh);
+ c->umh = m;
+ break;
+
+ case Aremove:
+ case Aopen:
+ Open:
+ /* save the name; domount might change c */
+ cname = c->name;
+ incref(&cname->r);
+ m = nil;
+ if(!nomount)
+ domount(&c, &m);
+
+ /* our own copy to open or remove */
+ c = cunique(c);
+
+ /* now it's our copy anyway, we can put the name back */
+ cnameclose(c->name);
+ c->name = cname;
+
+ switch(amode){
+ case Aremove:
+ putmhead(m);
+ break;
+
+ case Aopen:
+ case Acreate:
+if(c->umh != nil){
+ print("cunique umh\n");
+ putmhead(c->umh);
+ c->umh = nil;
+}
+
+ /* only save the mount head if it's a multiple element union */
+ if(m && m->mount && m->mount->next)
+ c->umh = m;
+ else
+ putmhead(m);
+
+ /* save registers else error() in open has wrong value of c saved */
+ saveregisters();
+
+ if(omode == OEXEC)
+ c->flag &= ~CCACHE;
+
+ c = devtab[c->type]->open(c, omode&~OCEXEC);
+
+ if(omode & OCEXEC)
+ c->flag |= CCEXEC;
+ if(omode & ORCLOSE)
+ c->flag |= CRCLOSE;
+ break;
+ }
+ break;
+
+ case Atodir:
+ /*
+ * Directories (e.g. for cd) are left before the mount point,
+ * so one may mount on / or . and see the effect.
+ */
+ if(!(c->qid.type & QTDIR))
+ error(Enotdir);
+ break;
+
+ case Amount:
+ /*
+ * When mounting on an already mounted upon directory,
+ * one wants subsequent mounts to be attached to the
+ * original directory, not the replacement. Don't domount.
+ */
+ break;
+
+ case Acreate:
+ /*
+ * We've already walked all but the last element.
+ * If the last exists, try to open it OTRUNC.
+ * If omode&OEXCL is set, just give up.
+ */
+ e.nelems++;
+ if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){
+ if(omode&OEXCL)
+ error(Eexist);
+ omode |= OTRUNC;
+ goto Open;
+ }
+
+ /*
+ * The semantics of the create(2) system call are that if the
+ * file exists and can be written, it is to be opened with truncation.
+ * On the other hand, the create(5) message fails if the file exists.
+ * If we get two create(2) calls happening simultaneously,
+ * they might both get here and send create(5) messages, but only
+ * one of the messages will succeed. To provide the expected create(2)
+ * semantics, the call with the failed message needs to try the above
+ * walk again, opening for truncation. This correctly solves the
+ * create/create race, in the sense that any observable outcome can
+ * be explained as one happening before the other.
+ * The create/create race is quite common. For example, it happens
+ * when two rc subshells simultaneously update the same
+ * environment variable.
+ *
+ * The implementation still admits a create/create/remove race:
+ * (A) walk to file, fails
+ * (B) walk to file, fails
+ * (A) create file, succeeds, returns
+ * (B) create file, fails
+ * (A) remove file, succeeds, returns
+ * (B) walk to file, return failure.
+ *
+ * This is hardly as common as the create/create race, and is really
+ * not too much worse than what might happen if (B) got a hold of a
+ * file descriptor and then the file was removed -- either way (B) can't do
+ * anything with the result of the create call. So we don't care about this race.
+ *
+ * Applications that care about more fine-grained decision of the races
+ * can use the OEXCL flag to get at the underlying create(5) semantics;
+ * by default we provide the common case.
+ *
+ * We need to stay behind the mount point in case we
+ * need to do the first walk again (should the create fail).
+ *
+ * We also need to cross the mount point and find the directory
+ * in the union in which we should be creating.
+ *
+ * The channel staying behind is c, the one moving forward is cnew.
+ */
+ m = nil;
+ cnew = nil; /* is this assignment necessary? */
+ if(!waserror()){ /* try create */
+ if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid))
+ cnew = createdir(cnew, m);
+ else{
+ cnew = c;
+ incref(&cnew->r);
+ }
+
+ /*
+ * We need our own copy of the Chan because we're
+ * about to send a create, which will move it. Once we have
+ * our own copy, we can fix the name, which might be wrong
+ * if findmount gave us a new Chan.
+ */
+ cnew = cunique(cnew);
+ cnameclose(cnew->name);
+ cnew->name = c->name;
+ incref(&cnew->name->r);
+
+ devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm);
+ poperror();
+ if(omode & OCEXEC)
+ cnew->flag |= CCEXEC;
+ if(omode & ORCLOSE)
+ cnew->flag |= CRCLOSE;
+ if(m)
+ putmhead(m);
+ cclose(c);
+ c = cnew;
+ c->name = addelem(c->name, e.elems[e.nelems-1]);
+ break;
+ }else{ /* create failed */
+ cclose(cnew);
+ if(m)
+ putmhead(m);
+ if(omode & OEXCL)
+ nexterror();
+ /* save error */
+ createerr = up->env->errstr;
+ up->env->errstr = tmperrbuf;
+ /* note: we depend that walk does not error */
+ if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){
+ up->env->errstr = createerr;
+ error(createerr); /* report true error */
+ }
+ up->env->errstr = createerr;
+ omode |= OTRUNC;
+ goto Open;
+ }
+
+ default:
+ panic("unknown namec access %d\n", amode);
+ }
+
+ poperror();
+
+ /* place final element in genbuf for e.g. exec */
+ if(e.nelems > 0)
+ kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf);
+ else
+ kstrcpy(up->genbuf, ".", sizeof up->genbuf);
+ free(e.name);
+ free(e.elems);
+ free(e.off);
+
+ return c;
+}
+
+/*
+ * name is valid. skip leading / and ./ as much as possible
+ */
+char*
+skipslash(char *name)
+{
+ while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/')))
+ name++;
+ return name;
+}
+
+/*
+ * Check that the name
+ * a) is in valid memory.
+ * b) is shorter than 2^16 bytes, so it can fit in a 9P string field.
+ * c) contains no frogs.
+ * The first byte is known to be addressible by the requester, so the
+ * routine works for kernel and user memory both.
+ * The parameter slashok flags whether a slash character is an error
+ * or a valid character.
+ */
+void
+validname(char *aname, int slashok)
+{
+ char *ename, *name;
+ int c;
+ Rune r;
+
+ name = aname;
+ ename = memchr(name, 0, (1<<16));
+
+ if(ename==nil || ename-name>=(1<<16))
+ error("name too long");
+
+ while(*name){
+ /* all characters above '~' are ok */
+ c = *(uchar*)name;
+ if(c >= Runeself)
+ name += chartorune(&r, name);
+ else{
+ if(isfrog[c])
+ if(!slashok || c!='/'){
+ snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname);
+ error(up->genbuf);
+ }
+ name++;
+ }
+ }
+}
+
+void
+isdir(Chan *c)
+{
+ if(c->qid.type & QTDIR)
+ return;
+ error(Enotdir);
+}
+
+/*
+ * This is necessary because there are many
+ * pointers to the top of a given mount list:
+ *
+ * - the mhead in the namespace hash table
+ * - the mhead in chans returned from findmount:
+ * used in namec and then by unionread.
+ * - the mhead in chans returned from createdir:
+ * used in the open/create race protect, which is gone.
+ *
+ * The RWlock in the Mhead protects the mount list it contains.
+ * The mount list is deleted when we cunmount.
+ * The RWlock ensures that nothing is using the mount list at that time.
+ *
+ * It is okay to replace c->mh with whatever you want as
+ * long as you are sure you have a unique reference to it.
+ *
+ * This comment might belong somewhere else.
+ */
+void
+putmhead(Mhead *m)
+{
+ if(m && decref(&m->r) == 0){
+ m->mount = (Mount*)0xCafeBeef;
+ free(m);
+ }
+}
--- /dev/null
+++ b/emu/port/dat.h
@@ -1,0 +1,513 @@
+typedef struct Block Block;
+typedef struct Chan Chan;
+typedef struct Cmdbuf Cmdbuf;
+typedef struct Cmdtab Cmdtab;
+typedef struct Cname Cname;
+typedef struct Dev Dev;
+typedef struct Dirtab Dirtab;
+typedef struct Egrp Egrp;
+typedef struct Evalue Evalue;
+typedef struct Fgrp Fgrp;
+typedef struct Mount Mount;
+typedef struct Mntcache Mntcache;
+typedef struct Mntparam Mntparam;
+typedef struct Mntrpc Mntrpc;
+typedef struct Mntwalk Mntwalk;
+typedef struct Mnt Mnt;
+typedef struct Mhead Mhead;
+typedef struct Osenv Osenv;
+typedef struct Pgrp Pgrp;
+typedef struct Proc Proc;
+typedef struct Queue Queue;
+typedef struct Ref Ref;
+typedef struct Rendez Rendez;
+typedef struct Rept Rept;
+typedef struct Rootdata Rootdata;
+/*typedef struct RWlock RWlock;*/
+typedef struct RWLock RWlock;
+typedef struct Procs Procs;
+typedef struct Signerkey Signerkey;
+typedef struct Skeyset Skeyset;
+typedef struct Uqid Uqid;
+typedef struct Uqidtab Uqidtab;
+typedef struct Walkqid Walkqid;
+
+#include "lib9.h"
+#undef CHDIR
+#undef NAMELEN
+#undef ERRLEN
+
+#include "emu.h"
+
+#pragma incomplete Queue
+#pragma incomplete Mntrpc
+
+#include "fcall.h"
+
+#include "pool.h"
+
+typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*);
+
+enum
+{
+ NERR = 32,
+ KNAMELEN = 28,
+ MAXROOT = 5*KNAMELEN, /* Maximum root pathname len of devfs-* */
+ NUMSIZE = 11,
+ PRINTSIZE = 256,
+ READSTR = 1000 /* temporary buffer size for device reads */
+};
+
+struct Ref
+{
+ Lock lk;
+ long ref;
+};
+
+struct Rendez
+{
+ Lock l;
+ Proc* p;
+};
+
+struct Rept
+{
+ Lock l;
+ Rendez r;
+ void *o;
+ int t;
+ int (*active)(void*);
+ int (*ck)(void*, int);
+ void (*f)(void*); /* called with VM acquire()'d */
+};
+
+/*
+ * Access types in namec & channel flags
+ */
+enum
+{
+ Aaccess, /* as in access, stat */
+ Abind, /* for left-hand-side of bind */
+ Atodir, /* as in chdir */
+ Aopen, /* for i/o */
+ Amount, /* to be mounted upon */
+ Acreate, /* file is to be created */
+ Aremove, /* will be removed by caller */
+
+ COPEN = 0x0001, /* for i/o */
+ CMSG = 0x0002, /* the message channel for a mount */
+/*rsc CCREATE = 0x0004, *//* permits creation if c->mnt */
+ CCEXEC = 0x0008, /* close on exec */
+ CFREE = 0x0010, /* not in use */
+ CRCLOSE = 0x0020, /* remove on close */
+ CCACHE = 0x0080 /* client cache */
+};
+
+struct Chan
+{
+ Lock l;
+ Ref r;
+ Chan* next; /* allocation */
+ Chan* link;
+ vlong offset; /* in file */
+ ushort type;
+ ulong dev;
+ ushort mode; /* read/write */
+ ushort flag;
+ Qid qid;
+ int fid; /* for devmnt */
+ ulong iounit; /* chunk size for i/o; 0==default */
+ Mhead* umh; /* mount point that derived Chan; used in unionread */
+ Chan* umc; /* channel in union; held for union read */
+ QLock umqlock; /* serialize unionreads */
+ int uri; /* union read index */
+ int dri; /* devdirread index */
+ ulong mountid;
+ Mntcache *mcp; /* Mount cache pointer */
+ Mnt *mux; /* Mnt for clients using me for messages */
+ void* aux; /* device specific data */
+ Chan* mchan; /* channel to mounted server */
+ Qid mqid; /* qid of root of mount point */
+ Cname *name;
+};
+
+struct Cname
+{
+ Ref r;
+ int alen; /* allocated length */
+ int len; /* strlen(s) */
+ char *s;
+};
+
+struct Dev
+{
+ int dc;
+ char* name;
+
+ void (*init)(void);
+ Chan* (*attach)(char*);
+ Walkqid* (*walk)(Chan*, Chan*, char**, int);
+ int (*stat)(Chan*, uchar*, int);
+ Chan* (*open)(Chan*, int);
+ void (*create)(Chan*, char*, int, ulong);
+ void (*close)(Chan*);
+ long (*read)(Chan*, void*, long, vlong);
+ Block* (*bread)(Chan*, long, ulong);
+ long (*write)(Chan*, void*, long, vlong);
+ long (*bwrite)(Chan*, Block*, ulong);
+ void (*remove)(Chan*);
+ int (*wstat)(Chan*, uchar*, int);
+};
+
+enum
+{
+ BINTR = (1<<0),
+ BFREE = (1<<1),
+ BMORE = (1<<2) /* continued in next block */
+};
+
+struct Block
+{
+ Block* next;
+ Block* list;
+ uchar* rp; /* first unconsumed byte */
+ uchar* wp; /* first empty byte */
+ uchar* lim; /* 1 past the end of the buffer */
+ uchar* base; /* start of the buffer */
+ void (*free)(Block*);
+ ulong flag;
+};
+#define BLEN(s) ((s)->wp - (s)->rp)
+#define BALLOC(s) ((s)->lim - (s)->base)
+
+struct Dirtab
+{
+ char name[KNAMELEN];
+ Qid qid;
+ vlong length;
+ long perm;
+};
+
+struct Walkqid
+{
+ Chan *clone;
+ int nqid;
+ Qid qid[1];
+};
+
+enum
+{
+ NSMAX = 1000,
+ NSLOG = 7,
+ NSCACHE = (1<<NSLOG)
+};
+
+struct Mntwalk /* state for /proc/#/ns */
+{
+ int cddone;
+ ulong id;
+ Mhead* mh;
+ Mount* cm;
+};
+
+struct Mount
+{
+ ulong mountid;
+ Mount* next;
+ Mhead* head;
+ Mount* copy;
+ Mount* order;
+ Chan* to; /* channel replacing channel */
+ int mflag;
+ char *spec;
+};
+
+struct Mhead
+{
+ Ref r;
+ RWlock lock;
+ Chan* from; /* channel mounted upon */
+ Mount* mount; /* what's mounted upon it */
+ Mhead* hash; /* Hash chain */
+};
+
+struct Mnt
+{
+ Lock l;
+ /* references are counted using c->ref; channels on this mount point incref(c->mchan) == Mnt.c */
+ Chan* c; /* Channel to file service */
+ Proc* rip; /* Reader in progress */
+ Mntrpc* queue; /* Queue of pending requests on this channel */
+ ulong id; /* Multiplexor id for channel check */
+ Mnt* list; /* Free list */
+ int flags; /* cache */
+ int msize; /* data + IOHDRSZ */
+ char *version; /* 9P version */
+ Queue *q; /* input queue */
+};
+
+enum
+{
+ MNTLOG = 5,
+ MNTHASH = 1<<MNTLOG, /* Hash to walk mount table */
+ DELTAFD= 20, /* allocation quantum for process file descriptors */
+ MAXNFD = 4000, /* max per process file descriptors */
+ MAXKEY = 8 /* keys for signed modules */
+};
+#define MOUNTH(p,qid) ((p)->mnthash[(qid).path&((1<<MNTLOG)-1)])
+
+struct Mntparam {
+ Chan* chan;
+ Chan* authchan;
+ char* spec;
+ int flags;
+};
+
+struct Pgrp
+{
+ Ref r; /* also used as a lock when mounting */
+ ulong pgrpid;
+ RWlock ns; /* Namespace n read/one write lock */
+ QLock nsh;
+ Mhead* mnthash[MNTHASH];
+ int progmode;
+ Chan* dot;
+ Chan* slash;
+ int nodevs;
+ int pin;
+};
+
+enum
+{
+ Nopin = -1
+};
+
+struct Fgrp
+{
+ Lock l;
+ Ref r;
+ Chan** fd;
+ int nfd; /* number of fd slots */
+ int maxfd; /* highest fd in use */
+ int minfd; /* lower bound on free fd */
+};
+
+struct Evalue
+{
+ char *var;
+ char *val;
+ int len;
+ Qid qid;
+ Evalue *next;
+};
+
+struct Egrp
+{
+ Ref r;
+ QLock l;
+ ulong path;
+ ulong vers;
+ Evalue *entries;
+};
+
+struct Signerkey
+{
+ Ref r;
+ char* owner;
+ ushort footprint;
+ ulong expires;
+ void* alg;
+ void* pk;
+ void (*pkfree)(void*);
+};
+
+struct Skeyset
+{
+ Ref r;
+ QLock l;
+ ulong flags;
+ char* devs;
+ int nkey;
+ Signerkey *keys[MAXKEY];
+};
+
+struct Uqid
+{
+ Ref r;
+ int type;
+ int dev;
+ vlong oldpath;
+ vlong newpath;
+ Uqid* next;
+};
+
+enum
+{
+ Nqidhash = 32
+};
+
+struct Uqidtab
+{
+ QLock l;
+ Uqid* qids[Nqidhash];
+ ulong pathgen;
+};
+
+struct Osenv
+{
+ char *syserrstr; /* last error from a system call, errbuf0 or 1 */
+ char *errstr; /* reason we're unwinding the error stack, errbuf1 or 0 */
+ char errbuf0[ERRMAX];
+ char errbuf1[ERRMAX];
+ Pgrp* pgrp; /* Ref to namespace, working dir and root */
+ Fgrp* fgrp; /* Ref to file descriptors */
+ Egrp* egrp; /* Environment vars */
+ Skeyset* sigs; /* Signed module keys */
+ Rendez* rend; /* Synchro point */
+ Queue* waitq; /* Info about dead children */
+ Queue* childq; /* Info about children for debuggers */
+ void* debug; /* Debugging master */
+ char* user; /* Inferno user name */
+ FPU fpu; /* Floating point thread state */
+ int uid; /* Numeric user id for host system */
+ int gid; /* Numeric group id for host system */
+ void *ui; /* User info for NT */
+};
+
+enum
+{
+ Unknown = 0xdeadbabe,
+ IdleGC = 0x16,
+ Interp = 0x17,
+ BusyGC = 0x18,
+ Moribund
+};
+
+struct Proc
+{
+ int type; /* interpreter or not */
+ char text[KNAMELEN];
+ Proc* qnext; /* list of processes waiting on a Qlock */
+ long pid;
+ Proc* next; /* list of created processes */
+ Proc* prev;
+ Lock rlock; /* sync between sleep/swiproc for r */
+ Rendez* r; /* rendezvous point slept on */
+ Rendez sleep; /* place to sleep */
+ int killed; /* by swiproc */
+ int swipend; /* software interrupt pending for Prog */
+ int syscall; /* set true under sysio for interruptable syscalls */
+ int intwait; /* spin wait for note to turn up */
+ int sigid; /* handle used for signal/note/exception */
+ Lock sysio; /* note handler lock */
+ char genbuf[128]; /* buffer used e.g. for last name element from namec */
+ int nerr; /* error stack SP */
+ osjmpbuf estack[NERR]; /* vector of error jump labels */
+ char* kstack;
+ void (*func)(void*); /* saved trampoline pointer for kproc */
+ void* arg; /* arg for invoked kproc function */
+ void* iprog; /* work for Prog after release */
+ void* prog; /* fake prog for slaves eg. exportfs */
+ Osenv* env; /* effective operating system environment */
+ Osenv defenv; /* default env for slaves with no prog */
+ osjmpbuf privstack; /* private stack for making new kids */
+ osjmpbuf sharestack;
+ Proc *kid;
+ void *kidsp;
+ void *os; /* host os specific data */
+};
+
+#define poperror() up->nerr--
+#define waserror() (up->nerr++, ossetjmp(up->estack[up->nerr-1]))
+
+enum
+{
+ /* kproc flags */
+ KPDUPPG = (1<<0),
+ KPDUPFDG = (1<<1),
+ KPDUPENVG = (1<<2),
+ KPX11 = (1<<8), /* needs silly amount of stack */
+ KPDUP = (KPDUPPG|KPDUPFDG|KPDUPENVG)
+};
+
+struct Procs
+{
+ Lock l;
+ Proc* head;
+ Proc* tail;
+};
+
+struct Rootdata
+{
+ int dotdot;
+ void *ptr;
+ int size;
+ int *sizep;
+};
+
+extern Dev* devtab[];
+extern char *ossysname;
+extern char *eve;
+extern Queue* kbdq;
+extern Queue* gkbdq;
+extern Queue* gkscanq;
+extern int Xsize;
+extern int Ysize;
+extern Pool* mainmem;
+extern char rootdir[MAXROOT]; /* inferno root */
+extern Procs procs;
+extern int sflag;
+extern int xtblbit;
+extern int globfs;
+extern int greyscale;
+extern uint qiomaxatomic;
+
+/*
+ * floating point control and status register masks
+ */
+enum
+{
+ INVAL = 0x0001,
+ ZDIV = 0x0002,
+ OVFL = 0x0004,
+ UNFL = 0x0008,
+ INEX = 0x0010,
+ RND_NR = 0x0000,
+ RND_NINF = 0x0100,
+ RND_PINF = 0x0200,
+ RND_Z = 0x0300,
+ RND_MASK = 0x0300
+};
+
+struct Cmdbuf
+{
+ char *buf;
+ char **f;
+ int nf;
+};
+
+struct Cmdtab
+{
+ int index; /* used by client to switch on result */
+ char *cmd; /* command name */
+ int narg; /* expected #args; 0 ==> variadic */
+};
+
+/* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */
+enum
+{
+ /* Queue.state */
+ Qstarve = (1<<0), /* consumer starved */
+ Qmsg = (1<<1), /* message stream */
+ Qclosed = (1<<2), /* queue has been closed/hungup */
+ Qflow = (1<<3), /* producer flow controlled */
+ Qcoalesce = (1<<4), /* coallesce packets on read */
+ Qkick = (1<<5), /* always call the kick routine after qwrite */
+};
+
+#define DEVDOTDOT -1
+
+#pragma varargck type "I" uchar*
+#pragma varargck type "E" uchar*
+
+extern void (*mainmonitor)(int, void*, ulong);
--- /dev/null
+++ b/emu/port/dev.c
@@ -1,0 +1,446 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+extern ulong kerndate;
+
+void
+mkqid(Qid *q, vlong path, ulong vers, int type)
+{
+ q->type = type;
+ q->vers = vers;
+ q->path = path;
+}
+
+int
+devno(int c, int user)
+{
+ int i;
+
+ for(i = 0; devtab[i] != nil; i++) {
+ if(devtab[i]->dc == c)
+ return i;
+ }
+ if(user == 0)
+ panic("devno %C 0x%ux", c, c);
+
+ return -1;
+}
+
+void
+devdir(Chan *c, Qid qid, char *n, long length, char *user, long perm, Dir *db)
+{
+ db->name = n;
+ if(c->flag&CMSG)
+ qid.type |= QTMOUNT;
+ db->qid = qid;
+ db->type = devtab[c->type]->dc;
+ db->dev = c->dev;
+ db->mode = perm | (qid.type << 24);
+ db->atime = time(0);
+ db->mtime = kerndate;
+ db->length = length;
+ db->uid = user;
+ db->gid = eve;
+ db->muid = user;
+}
+
+/*
+ * the zeroth element of the table MUST be the directory itself for ..
+ */
+int
+devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+ USED(name);
+ if(tab == 0)
+ return -1;
+ if(i != DEVDOTDOT){
+ /* skip over the first element, that for . itself */
+ i++;
+ if(i >= ntab)
+ return -1;
+ tab += i;
+ }
+ devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+ return 1;
+}
+
+void
+devinit(void)
+{
+}
+
+void
+devshutdown(void)
+{
+}
+
+Chan*
+devattach(int tc, char *spec)
+{
+ Chan *c;
+ char *buf;
+
+ c = newchan();
+ mkqid(&c->qid, 0, 0, QTDIR);
+ c->type = devno(tc, 0);
+ if(spec == nil)
+ spec = "";
+ buf = smalloc(4+strlen(spec)+1);
+ sprint(buf, "#%C%s", tc, spec);
+ c->name = newcname(buf);
+ free(buf);
+ return c;
+}
+
+Chan*
+devclone(Chan *c)
+{
+ Chan *nc;
+
+ if(c->flag & COPEN)
+ panic("clone of open file type %C\n", devtab[c->type]->dc);
+
+ nc = newchan();
+ nc->type = c->type;
+ nc->dev = c->dev;
+ nc->mode = c->mode;
+ nc->qid = c->qid;
+ nc->offset = c->offset;
+ nc->umh = nil;
+ nc->mountid = c->mountid;
+ nc->aux = c->aux;
+ nc->mqid = c->mqid;
+ nc->mcp = c->mcp;
+ return nc;
+}
+
+Walkqid*
+devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen)
+{
+ int i, j;
+ volatile int alloc;
+ Walkqid *wq;
+ char *n;
+ Dir dir;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone!=nil)
+ cclose(wq->clone);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0; /* device doesn't know about this channel yet */
+ alloc = 1;
+ }
+ wq->clone = nc;
+
+ for(j=0; j<nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ goto Done;
+ }
+ n = name[j];
+ if(strcmp(n, ".") == 0){
+ Accept:
+ wq->qid[wq->nqid++] = nc->qid;
+ continue;
+ }
+ if(strcmp(n, "..") == 0){
+ (*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir);
+ nc->qid = dir.qid;
+ goto Accept;
+ }
+ /*
+ * Ugly problem: If we're using devgen, make sure we're
+ * walking the directory itself, represented by the first
+ * entry in the table, and not trying to step into a sub-
+ * directory of the table, e.g. /net/net. Devgen itself
+ * should take care of the problem, but it doesn't have
+ * the necessary information (that we're doing a walk).
+ */
+ if(gen==devgen && nc->qid.path!=tab[0].qid.path)
+ goto Notfound;
+ for(i=0;; i++) {
+ switch((*gen)(nc, n, tab, ntab, i, &dir)){
+ case -1:
+ Notfound:
+ if(j == 0)
+ error(Enonexist);
+ kstrcpy(up->env->errstr, Enonexist, ERRMAX);
+ goto Done;
+ case 0:
+ continue;
+ case 1:
+ if(strcmp(n, dir.name) == 0){
+ nc->qid = dir.qid;
+ goto Accept;
+ }
+ continue;
+ }
+ }
+ }
+ /*
+ * We processed at least one name, so will return some data.
+ * If we didn't process all nname entries succesfully, we drop
+ * the cloned channel and return just the Qids of the walks.
+ */
+Done:
+ poperror();
+ if(wq->nqid < nname){
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ /* attach cloned channel to same device */
+ wq->clone->type = c->type;
+ }
+ return wq;
+}
+
+int
+devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen)
+{
+ int i;
+ Dir dir;
+ char *p, *elem;
+
+ for(i=0;; i++)
+ switch((*gen)(c, nil, tab, ntab, i, &dir)){
+ case -1:
+ if(c->qid.type & QTDIR){
+ if(c->name == nil)
+ elem = "???";
+ else if(strcmp(c->name->s, "/") == 0)
+ elem = "/";
+ else
+ for(elem=p=c->name->s; *p; p++)
+ if(*p == '/')
+ elem = p+1;
+ devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
+ n = convD2M(&dir, db, n);
+ if(n == 0)
+ error(Ebadarg);
+ return n;
+ }
+ print("%s %s: devstat %C %llux\n",
+ up->text, up->env->user,
+ devtab[c->type]->dc, c->qid.path);
+
+ error(Enonexist);
+ case 0:
+ break;
+ case 1:
+ if(c->qid.path == dir.qid.path) {
+ if(c->flag&CMSG)
+ dir.mode |= DMMOUNT;
+ n = convD2M(&dir, db, n);
+ if(n == 0)
+ error(Ebadarg);
+ return n;
+ }
+ break;
+ }
+}
+
+long
+devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen)
+{
+ long m, dsz;
+ struct{
+ Dir d;
+ char slop[100]; /* TO DO */
+ }dir;
+
+ for(m=0; m<n; c->dri++) {
+ switch((*gen)(c, nil, tab, ntab, c->dri, &dir.d)){
+ case -1:
+ return m;
+
+ case 0:
+ break;
+
+ case 1:
+ dsz = convD2M(&dir.d, (uchar*)d, n-m);
+ if(dsz <= BIT16SZ){ /* <= not < because this isn't stat; read is stuck */
+ if(m == 0)
+ error(Eshort);
+ return m;
+ }
+ m += dsz;
+ d += dsz;
+ break;
+ }
+ }
+
+ return m;
+}
+
+/*
+ * error(Eperm) if open permission not granted for up->env->user.
+ */
+void
+devpermcheck(char *fileuid, ulong perm, int omode)
+{
+ ulong t;
+ static int access[] = { 0400, 0200, 0600, 0100 };
+
+ if(strcmp(up->env->user, fileuid) == 0)
+ perm <<= 0;
+ else
+ if(strcmp(up->env->user, eve) == 0)
+ perm <<= 3;
+ else
+ perm <<= 6;
+
+ t = access[omode&3];
+ if((t&perm) != t)
+ error(Eperm);
+}
+
+Chan*
+devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen)
+{
+ int i;
+ Dir dir;
+
+ for(i=0;; i++) {
+ switch((*gen)(c, nil, tab, ntab, i, &dir)){
+ case -1:
+ goto Return;
+ case 0:
+ break;
+ case 1:
+ if(c->qid.path == dir.qid.path) {
+ devpermcheck(dir.uid, dir.mode, omode);
+ goto Return;
+ }
+ break;
+ }
+ }
+Return:
+ c->offset = 0;
+ if((c->qid.type&QTDIR) && omode!=OREAD)
+ error(Eperm);
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ return c;
+}
+
+Block*
+devbread(Chan *c, long n, ulong offset)
+{
+ Block *bp;
+
+ bp = allocb(n);
+ if(waserror()) {
+ freeb(bp);
+ nexterror();
+ }
+ bp->wp += devtab[c->type]->read(c, bp->wp, n, offset);
+ poperror();
+ return bp;
+}
+
+long
+devbwrite(Chan *c, Block *bp, ulong offset)
+{
+ long n;
+
+ if(waserror()) {
+ freeb(bp);
+ nexterror();
+ }
+ n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset);
+ poperror();
+ freeb(bp);
+
+ return n;
+}
+
+void
+devcreate(Chan *c, char *name, int mode, ulong perm)
+{
+ USED(c);
+ USED(name);
+ USED(mode);
+ USED(perm);
+ error(Eperm);
+}
+
+void
+devremove(Chan *c)
+{
+ USED(c);
+ error(Eperm);
+}
+
+int
+devwstat(Chan *c, uchar *dp, int n)
+{
+ USED(c);
+ USED(dp);
+ USED(n);
+ error(Eperm);
+ return 0;
+}
+
+int
+readstr(ulong off, char *buf, ulong n, char *str)
+{
+ int size;
+
+ size = strlen(str);
+ if(off >= size)
+ return 0;
+ if(off+n > size)
+ n = size-off;
+ memmove(buf, str+off, n);
+ return n;
+}
+
+int
+readnum(ulong off, char *buf, ulong n, ulong val, int size)
+{
+ char tmp[64];
+
+ if(size > 64) size = 64;
+
+ snprint(tmp, sizeof(tmp), "%*.0lud ", size, val);
+ if(off >= size)
+ return 0;
+ if(off+n > size)
+ n = size-off;
+ memmove(buf, tmp+off, n);
+ return n;
+}
+
+/*
+ * check that the name in a wstat is plausible
+ */
+void
+validwstatname(char *name)
+{
+ validname(name, 0);
+ if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ error(Efilename);
+}
+
+Dev*
+devbyname(char *name)
+{
+ int i;
+
+ for(i = 0; devtab[i] != nil; i++)
+ if(strcmp(devtab[i]->name, name) == 0)
+ return devtab[i];
+ return nil;
+}
--- /dev/null
+++ b/emu/port/devaudio.c
@@ -1,0 +1,397 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "audio.h"
+
+Dirtab audiotab[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "audio", {Qaudio}, 0, 0666,
+ "audioctl", {Qaudioctl}, 0, 0666,
+};
+
+static void
+audioinit(void)
+{
+ audio_file_init();
+}
+
+static Chan*
+audioattach(char *spec)
+{
+ return devattach('A', spec);
+}
+
+static Walkqid*
+audiowalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, audiotab, nelem(audiotab), devgen);
+}
+
+static int
+audiostat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, audiotab, nelem(audiotab), devgen);
+}
+
+static Chan*
+audioopen(Chan *c, int omode)
+{
+ c = devopen(c, omode, audiotab, nelem(audiotab), devgen);
+ if(waserror()){
+ c->flag &= ~COPEN;
+ nexterror();
+ }
+ switch(c->qid.path) {
+ case Qdir:
+ case Qaudioctl:
+ break;
+ case Qaudio:
+ audio_file_open(c, c->mode);
+ break;
+ default:
+ error(Egreg);
+ }
+ poperror();
+ return c;
+}
+
+static void
+audioclose(Chan *c)
+{
+ if((c->flag & COPEN) == 0)
+ return;
+
+ switch(c->qid.path) {
+ case Qdir:
+ case Qaudioctl:
+ break;
+ case Qaudio:
+ audio_file_close(c);
+ break;
+ default:
+ error(Egreg);
+ }
+}
+
+static int ctlsummary(char*, int, Audio_t*);
+
+static long
+audioread(Chan *c, void *va, long count, vlong offset)
+{
+ char *buf;
+ int n;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, va, count, audiotab, nelem(audiotab), devgen);
+ switch(c->qid.path) {
+ case Qaudio:
+ return audio_file_read(c, va, count, offset);
+ case Qaudioctl:
+ buf = smalloc(READSTR);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+ n = ctlsummary(buf, READSTR, getaudiodev());
+ count = readstr(offset, va, n, buf);
+ poperror();
+ free(buf);
+ return count;
+ }
+ return 0;
+}
+
+static long
+audiowrite(Chan *c, void *va, long count, vlong offset)
+{
+ switch(c->qid.path) {
+ case Qaudio:
+ return audio_file_write(c, va, count, offset);
+ case Qaudioctl:
+ return audio_ctl_write(c, va, count, offset);
+ }
+ return 0;
+}
+
+static int sval(char*, unsigned long*, ulong, ulong);
+static int str2val(svp_t*, char*, ulong*);
+static char* val2str(svp_t*, ulong);
+
+int
+audioparse(char* args, int len, Audio_t *t)
+{
+ int i, n;
+ ulong v;
+ Cmdbuf *cb;
+ ulong tf;
+ Audio_t info = *t;
+
+ cb = parsecmd(args, len);
+ if(waserror()){
+ free(cb);
+ return 0;
+ }
+
+ tf = 0;
+ n = cb->nf;
+ for(i = 0; i < cb->nf-1; i++) {
+ if(strcmp(cb->f[i], "in") == 0){
+ tf |= AUDIO_IN_FLAG;
+ continue;
+ }
+ if(strcmp(cb->f[i], "out") == 0) {
+ tf |= AUDIO_OUT_FLAG;
+ continue;
+ }
+ if(tf == 0)
+ tf = AUDIO_IN_FLAG | AUDIO_OUT_FLAG;
+ if(strcmp(cb->f[i], "bits") == 0) {
+ if(!str2val(audio_bits_tbl, cb->f[i+1], &v))
+ break;
+ i++;
+ if(tf & AUDIO_IN_FLAG) {
+ info.in.flags |= AUDIO_BITS_FLAG | AUDIO_MOD_FLAG;
+ info.in.bits = v;
+ }
+ if(tf & AUDIO_OUT_FLAG) {
+ info.out.flags |= AUDIO_BITS_FLAG | AUDIO_MOD_FLAG;
+ info.out.bits = v;
+ }
+ } else if(strcmp(cb->f[i], "buf") == 0) {
+ if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val))
+ break;
+ i++;
+ if(tf & AUDIO_IN_FLAG) {
+ info.in.flags |= AUDIO_BUF_FLAG | AUDIO_MOD_FLAG;
+ info.in.buf = v;
+ }
+ if(tf & AUDIO_OUT_FLAG) {
+ info.out.flags |= AUDIO_BUF_FLAG | AUDIO_MOD_FLAG;
+ info.out.buf = v;
+ }
+ } else if(strcmp(cb->f[i], "chans") == 0) {
+ if(!str2val(audio_chan_tbl, cb->f[i+1], &v))
+ break;
+ i++;
+ if(tf & AUDIO_IN_FLAG) {
+ info.in.flags |= AUDIO_CHAN_FLAG | AUDIO_MOD_FLAG;
+ info.in.chan = v;
+ }
+ if(tf & AUDIO_OUT_FLAG) {
+ info.out.flags |= AUDIO_CHAN_FLAG | AUDIO_MOD_FLAG;
+ info.out.chan = v;
+ }
+ } else if(strcmp(cb->f[i], "indev") == 0) {
+ if(!str2val(audio_indev_tbl, cb->f[i+1], &v))
+ break;
+ i++;
+ info.in.flags |= AUDIO_DEV_FLAG | AUDIO_MOD_FLAG;
+ info.in.dev = v;
+ } else if(strcmp(cb->f[i], "outdev") == 0) {
+ if(!str2val(audio_outdev_tbl, cb->f[i+1], &v))
+ break;
+ i++;
+ info.out.flags |= AUDIO_DEV_FLAG | AUDIO_MOD_FLAG;
+ info.out.dev = v;
+ } else if(strcmp(cb->f[i], "enc") == 0) {
+ if(!str2val(audio_enc_tbl, cb->f[i+1], &v))
+ break;
+ i++;
+ if(tf & AUDIO_IN_FLAG) {
+ info.in.flags |= AUDIO_ENC_FLAG | AUDIO_MOD_FLAG;
+ info.in.enc = v;
+ }
+ if(tf & AUDIO_OUT_FLAG) {
+ info.out.flags |= AUDIO_ENC_FLAG | AUDIO_MOD_FLAG;
+ info.out.enc = v;
+ }
+ } else if(strcmp(cb->f[i], "rate") == 0) {
+ if(!str2val(audio_rate_tbl, cb->f[i+1], &v))
+ break;
+ i++;
+ if(tf & AUDIO_IN_FLAG) {
+ info.in.flags |= AUDIO_RATE_FLAG | AUDIO_MOD_FLAG;
+ info.in.rate = v;
+ }
+ if(tf & AUDIO_OUT_FLAG) {
+ info.out.flags |= AUDIO_RATE_FLAG | AUDIO_MOD_FLAG;
+ info.out.rate = v;
+ }
+ } else if(strcmp(cb->f[i], "vol") == 0) {
+ if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val))
+ break;
+ i++;
+ if(tf & AUDIO_IN_FLAG) {
+ info.in.flags |= AUDIO_VOL_FLAG | AUDIO_MOD_FLAG;
+ info.in.left = v;
+ info.in.right = v;
+ }
+ if(tf & AUDIO_OUT_FLAG) {
+ info.out.flags |= AUDIO_VOL_FLAG | AUDIO_MOD_FLAG;
+ info.out.left = v;
+ info.out.right = v;
+ }
+ } else if(strcmp(cb->f[i], "left") == 0) {
+ if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val))
+ break;
+ i++;
+ if(tf & AUDIO_IN_FLAG) {
+ info.in.flags |= AUDIO_LEFT_FLAG | AUDIO_MOD_FLAG;
+ info.in.left = v;
+ }
+ if(tf & AUDIO_OUT_FLAG) {
+ info.out.flags |= AUDIO_LEFT_FLAG | AUDIO_MOD_FLAG;
+ info.out.left = v;
+ }
+ } else if(strcmp(cb->f[i], "right") == 0) {
+ if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val))
+ break;
+ i++;
+ if(tf & AUDIO_IN_FLAG) {
+ info.in.flags |= AUDIO_RIGHT_FLAG | AUDIO_MOD_FLAG;
+ info.in.right = v;
+ }
+ if(tf & AUDIO_OUT_FLAG) {
+ info.out.flags |= AUDIO_RIGHT_FLAG | AUDIO_MOD_FLAG;
+ info.out.right = v;
+ }
+ }else
+ break;
+ }
+ poperror();
+ free(cb);
+
+ if(i < n)
+ return 0;
+
+ *t = info; /* set information back */
+ return 1;
+}
+
+static char*
+audioparam(char* p, char* e, char* name, int val, svp_t* tbl)
+{
+ char *s;
+ svp_t *sv;
+
+ if((s = val2str(tbl, val)) != nil){
+ p = seprint(p, e, "%s %s", name, s); /* current setting */
+ for(sv = tbl; sv->s != nil; sv++)
+ if(sv->v != val)
+ p = seprint(p, e, " %s", sv->s); /* other possible values */
+ p = seprint(p, e, "\n");
+ }else
+ p = seprint(p, e, "%s unknown\n", name);
+ return p;
+}
+
+static char*
+audioioparam(char* p, char* e, char* name, int ival, int oval, svp_t* tbl)
+{
+ if(ival == oval)
+ return audioparam(p, e, name, ival, tbl);
+ p = audioparam(seprint(p, e, "in "), e, name, ival, tbl);
+ p = audioparam(seprint(p, e, "out "), e, name, oval, tbl);
+ return p;
+}
+
+static int
+ctlsummary(char *buf, int bsize, Audio_t *adev)
+{
+ Audio_d *in, *out;
+ char *p, *e;
+
+ in = &adev->in;
+ out = &adev->out;
+
+ p = buf;
+ e = p + bsize;
+
+ p = audioparam(p, e, "indev", in->dev, audio_indev_tbl);
+ p = audioparam(p, e, "outdev", out->dev, audio_outdev_tbl);
+ p = audioioparam(p, e, "enc", in->enc, out->enc, audio_enc_tbl);
+ p = audioioparam(p, e, "rate", in->rate, out->rate, audio_rate_tbl);
+ p = audioioparam(p, e, "bits", in->bits, out->bits, audio_bits_tbl); /* this one is silly */
+ p = audioioparam(p, e, "chans", in->chan, out->chan, audio_chan_tbl);
+ /* TO DO: minimise in/out left/right where possible */
+ if(in->left != in->right){
+ p = seprint(p, e, "in left %d 0 100\n", in->left);
+ p = seprint(p, e, "in right %d 0 100\n", in->right);
+ }else
+ p = seprint(p, e, "in %d 0 100\n", in->right);
+ if(out->left != out->right){
+ p = seprint(p, e, "out left %d 0 100\n", out->left);
+ p = seprint(p, e, "out right %d 0 100\n", out->right);
+ }else
+ p = seprint(p, e, "out %d 0 100\n", out->right);
+ p = seprint(p, e, "in buf %d %d %d\n", in->buf, Audio_Min_Val, Audio_Max_Val);
+ p = seprint(p, e, "out buf %d %d %d\n", out->buf, Audio_Min_Val, Audio_Max_Val);
+
+ return p-buf;
+}
+
+void
+audio_info_init(Audio_t *t)
+{
+ t->in = Default_Audio_Format;
+ t->in.dev = Default_Audio_Input;
+ t->out = Default_Audio_Format;
+ t->out.dev = Default_Audio_Output;
+}
+
+static int
+str2val(svp_t* t, char* s, ulong *v)
+{
+ if(t == nil || s == nil)
+ return 0;
+ for(; t->s != nil; t++) {
+ if(strncmp(t->s, s, strlen(t->s)) == 0) {
+ *v = t->v;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static char*
+val2str(svp_t* t, ulong v)
+{
+ if(t == nil)
+ return nil;
+ for(; t->s != nil; t++)
+ if(t->v == v)
+ return t->s;
+ return nil;
+}
+
+static int
+sval(char* buf, ulong* v, ulong max, ulong min)
+{
+ unsigned long val = strtoul(buf, 0, 10);
+
+ if(val > max || val < min)
+ return 0;
+ *v = val;
+ return 1;
+}
+
+Dev audiodevtab = {
+ 'A',
+ "audio",
+
+ audioinit,
+ audioattach,
+ audiowalk,
+ audiostat,
+ audioopen,
+ devcreate,
+ audioclose,
+ audioread,
+ devbread,
+ audiowrite,
+ devbwrite,
+ devremove,
+ devwstat
+};
+
--- /dev/null
+++ b/emu/port/devcap.c
@@ -1,0 +1,243 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "mp.h"
+#include "libsec.h"
+
+/*
+ * Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+ */
+
+enum {
+ Captimeout = 15, /* seconds until expiry */
+ Capidletime = 60 /* idle seconds before capwatch exits */
+};
+
+typedef struct Caps Caps;
+struct Caps
+{
+ uchar hash[SHA1dlen];
+ ulong time;
+ Caps* next;
+};
+
+struct {
+ QLock l;
+ Caps* caps;
+ int kpstarted;
+} allcaps;
+
+enum {
+ Qdir,
+ Qhash,
+ Quse
+};
+
+static Dirtab capdir[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "capuse", {Quse, 0}, 0, 0222,
+ "caphash", {Qhash, 0}, 0, 0200,
+};
+
+static int ncapdir = nelem(capdir);
+
+static void
+capwatch(void *a)
+{
+ Caps *c, **l;
+ int idletime;
+
+ USED(a);
+ idletime = 0;
+ for(;;){
+ osmillisleep(30*1000);
+ qlock(&allcaps.l);
+ for(l = &allcaps.caps; (c = *l) != nil;)
+ if(++c->time > Captimeout){
+ *l = c->next;
+ free(c);
+ }else
+ l = &c->next;
+ if(allcaps.caps == nil){
+ if(++idletime > Capidletime){
+ allcaps.kpstarted = 0;
+ qunlock(&allcaps.l);
+ pexit("", 0);
+ }
+ }else
+ idletime = 0;
+ qunlock(&allcaps.l);
+ }
+}
+
+static Chan *
+capattach(char *spec)
+{
+ return devattach(0x00A4, spec); /* L'¤' */
+}
+
+static Walkqid*
+capwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen);
+}
+
+static int
+capstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, capdir, nelem(capdir), devgen);
+}
+
+static Chan*
+capopen(Chan *c, int omode)
+{
+ if(c->qid.type & QTDIR) {
+ if(omode != OREAD)
+ error(Eisdir);
+ c->mode = omode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ if(c->qid.path == Qhash && !iseve())
+ error(Eperm);
+
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static void
+capclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+capread(Chan *c, void *va, long n, vlong vl)
+{
+ USED(vl);
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, va, n, capdir, ncapdir, devgen);
+
+ default:
+ error(Eperm);
+ break;
+ }
+ return n;
+}
+
+static int
+capwritehash(uchar *a, int l)
+{
+ Caps *c;
+
+ if(l != SHA1dlen)
+ return -1;
+ c = malloc(sizeof(*c));
+ if(c == nil)
+ return -1;
+ memmove(c->hash, a, l);
+ c->time = 0;
+ qlock(&allcaps.l);
+ c->next = allcaps.caps;
+ allcaps.caps = c;
+ if(!allcaps.kpstarted){
+ allcaps.kpstarted = 1;
+ kproc("capwatch", capwatch, 0, 0);
+ }
+ qunlock(&allcaps.l);
+ return 0;
+}
+
+static int
+capwriteuse(uchar *a, int len)
+{
+ int n;
+ uchar digest[SHA1dlen];
+ char buf[128], *p, *users[3];
+ Caps *c, **l;
+
+ if(len >= sizeof(buf)-1)
+ return -1;
+ memmove(buf, a, len);
+ buf[len] = 0;
+ p = strrchr(buf, '@');
+ if(p == nil)
+ return -1;
+ *p++ = 0;
+ len = strlen(p);
+ n = strlen(buf);
+ if(len == 0 || n == 0)
+ return -1;
+ hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil);
+ n = getfields(buf, users, nelem(users), 0, "@");
+ if(n == 1)
+ users[1] = users[0];
+ else if(n != 2)
+ return -1;
+ if(*users[0] == 0 || *users[1] == 0)
+ return -1;
+ qlock(&allcaps.l);
+ for(l = &allcaps.caps; (c = *l) != nil; l = &c->next)
+ if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){
+ *l = c->next;
+ qunlock(&allcaps.l);
+ free(c);
+ if(n == 2 && strcmp(up->env->user, users[0]) != 0)
+ return -1;
+ setid(users[1], 0); /* could use users[2] to mean `host OS user' */
+ return 0;
+ }
+ qunlock(&allcaps.l);
+ return -1;
+}
+
+static long
+capwrite(Chan* c, void* buf, long n, vlong offset)
+{
+ USED(offset);
+ switch((ulong)c->qid.path){
+ case Qhash:
+ if(capwritehash(buf, n) < 0)
+ error(Ebadarg);
+ return n;
+ case Quse:
+ if(capwriteuse(buf, n) < 0)
+ error("invalid capability");
+ return n;
+ }
+ error(Ebadarg);
+ return 0;
+}
+
+static void
+capremove(Chan *c)
+{
+ if(c->qid.path != Qhash || !iseve())
+ error(Eperm);
+ ncapdir = nelem(capdir)-1;
+}
+
+Dev capdevtab = {
+ 0x00A4, /* L'¤' */
+ "cap",
+
+ devinit,
+ capattach,
+ capwalk,
+ capstat,
+ capopen,
+ devcreate,
+ capclose,
+ capread,
+ devbread,
+ capwrite,
+ devbwrite,
+ capremove,
+ devwstat
+};
--- /dev/null
+++ b/emu/port/devcmd.c
@@ -1,0 +1,683 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Qtopdir, /* top level directory */
+ Qcmd,
+ Qclonus,
+ Qconvdir,
+ Qconvbase,
+ Qdata = Qconvbase,
+ Qstderr,
+ Qctl,
+ Qstatus,
+ Qwait,
+
+ Debug=0 /* to help debug os.c */
+};
+#define TYPE(x) ((ulong)(x).path & 0xf)
+#define CONV(x) (((ulong)(x).path >> 4)&0xfff)
+#define QID(c, y) (((c)<<4) | (y))
+
+typedef struct Conv Conv;
+struct Conv
+{
+ int x;
+ int inuse;
+ int fd[3]; /* stdin, stdout, and stderr */
+ int count[3]; /* number of readers on stdin/stdout/stderr */
+ int perm;
+ char* owner;
+ char* state;
+ Cmdbuf* cmd;
+ char* dir;
+ QLock l; /* protects state changes */
+ Queue* waitq;
+ void* child;
+ char* error; /* on start up */
+ int nice;
+ short killonclose;
+ short killed;
+ Rendez startr;
+};
+
+static struct
+{
+ QLock l;
+ int nc;
+ int maxconv;
+ Conv** conv;
+} cmd;
+
+static Conv* cmdclone(char*);
+static void cmdproc(void*);
+
+static int
+cmd3gen(Chan *c, int i, Dir *dp)
+{
+ Qid q;
+ Conv *cv;
+
+ cv = cmd.conv[CONV(c->qid)];
+ switch(i){
+ default:
+ return -1;
+ case Qdata:
+ mkqid(&q, QID(CONV(c->qid), Qdata), 0, QTFILE);
+ devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
+ return 1;
+ case Qstderr:
+ mkqid(&q, QID(CONV(c->qid), Qstderr), 0, QTFILE);
+ devdir(c, q, "stderr", 0, cv->owner, 0444, dp);
+ return 1;
+ case Qctl:
+ mkqid(&q, QID(CONV(c->qid), Qctl), 0, QTFILE);
+ devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
+ return 1;
+ case Qstatus:
+ mkqid(&q, QID(CONV(c->qid), Qstatus), 0, QTFILE);
+ devdir(c, q, "status", 0, cv->owner, 0444, dp);
+ return 1;
+ case Qwait:
+ mkqid(&q, QID(CONV(c->qid), Qwait), 0, QTFILE);
+ devdir(c, q, "wait", 0, cv->owner, 0444, dp);
+ return 1;
+ }
+}
+
+static int
+cmdgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
+{
+ Qid q;
+ Conv *cv;
+
+ USED(name);
+ USED(nd);
+ USED(d);
+
+ if(s == DEVDOTDOT){
+ switch(TYPE(c->qid)){
+ case Qtopdir:
+ case Qcmd:
+ mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
+ devdir(c, q, "#C", 0, eve, DMDIR|0555, dp);
+ break;
+ case Qconvdir:
+ mkqid(&q, QID(0, Qcmd), 0, QTDIR);
+ devdir(c, q, "cmd", 0, eve, DMDIR|0555, dp);
+ break;
+ default:
+ panic("cmdgen %llux", c->qid.path);
+ }
+ return 1;
+ }
+
+ switch(TYPE(c->qid)) {
+ case Qtopdir:
+ if(s >= 1)
+ return -1;
+ mkqid(&q, QID(0, Qcmd), 0, QTDIR);
+ devdir(c, q, "cmd", 0, "cmd", DMDIR|0555, dp);
+ return 1;
+ case Qcmd:
+ if(s < cmd.nc) {
+ cv = cmd.conv[s];
+ mkqid(&q, QID(s, Qconvdir), 0, QTDIR);
+ sprint(up->genbuf, "%d", s);
+ devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp);
+ return 1;
+ }
+ s -= cmd.nc;
+ if(s == 0){
+ mkqid(&q, QID(0, Qclonus), 0, QTFILE);
+ devdir(c, q, "clone", 0, "cmd", 0666, dp);
+ return 1;
+ }
+ return -1;
+ case Qclonus:
+ if(s == 0){
+ mkqid(&q, QID(0, Qclonus), 0, QTFILE);
+ devdir(c, q, "clone", 0, "cmd", 0666, dp);
+ return 1;
+ }
+ return -1;
+ case Qconvdir:
+ return cmd3gen(c, Qconvbase+s, dp);
+ case Qdata:
+ case Qstderr:
+ case Qctl:
+ case Qstatus:
+ case Qwait:
+ return cmd3gen(c, TYPE(c->qid), dp);
+ }
+ return -1;
+}
+
+static void
+cmdinit(void)
+{
+ cmd.maxconv = 1000;
+ cmd.conv = mallocz(sizeof(Conv*)*(cmd.maxconv+1), 1);
+ /* cmd.conv is checked by cmdattach, below */
+}
+
+static Chan *
+cmdattach(char *spec)
+{
+ Chan *c;
+
+ if(cmd.conv == nil)
+ error(Enomem);
+ c = devattach('C', spec);
+ mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
+ return c;
+}
+
+static Walkqid*
+cmdwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, cmdgen);
+}
+
+static int
+cmdstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, cmdgen);
+}
+
+static Chan *
+cmdopen(Chan *c, int omode)
+{
+ int perm;
+ Conv *cv;
+ char *user;
+
+ perm = 0;
+ omode = openmode(omode);
+ switch(omode) {
+ case OREAD:
+ perm = 4;
+ break;
+ case OWRITE:
+ perm = 2;
+ break;
+ case ORDWR:
+ perm = 6;
+ break;
+ }
+
+ switch(TYPE(c->qid)) {
+ default:
+ break;
+ case Qtopdir:
+ case Qcmd:
+ case Qconvdir:
+ case Qstatus:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+ case Qclonus:
+ qlock(&cmd.l);
+ if(waserror()){
+ qunlock(&cmd.l);
+ nexterror();
+ }
+ cv = cmdclone(up->env->user);
+ poperror();
+ qunlock(&cmd.l);
+ if(cv == 0)
+ error(Enodev);
+ mkqid(&c->qid, QID(cv->x, Qctl), 0, QTFILE);
+ break;
+ case Qdata:
+ case Qstderr:
+ case Qctl:
+ case Qwait:
+ qlock(&cmd.l);
+ cv = cmd.conv[CONV(c->qid)];
+ qlock(&cv->l);
+ if(waserror()){
+ qunlock(&cv->l);
+ qunlock(&cmd.l);
+ nexterror();
+ }
+ user = up->env->user;
+ if((perm & (cv->perm>>6)) != perm) {
+ if(strcmp(user, cv->owner) != 0 ||
+ (perm & cv->perm) != perm)
+ error(Eperm);
+ }
+ switch(TYPE(c->qid)){
+ case Qdata:
+ if(omode == OWRITE || omode == ORDWR)
+ cv->count[0]++;
+ if(omode == OREAD || omode == ORDWR)
+ cv->count[1]++;
+ break;
+ case Qstderr:
+ if(omode != OREAD)
+ error(Eperm);
+ cv->count[2]++;
+ break;
+ case Qwait:
+ if(cv->waitq == nil)
+ cv->waitq = qopen(1024, Qmsg, nil, 0);
+ break;
+ }
+ cv->inuse++;
+ if(cv->inuse == 1) {
+ cv->state = "Open";
+ kstrdup(&cv->owner, user);
+ cv->perm = 0660;
+ cv->nice = 0;
+ }
+ poperror();
+ qunlock(&cv->l);
+ qunlock(&cmd.l);
+ break;
+ }
+ c->mode = omode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static void
+closeconv(Conv *c)
+{
+ kstrdup(&c->owner, "cmd");
+ kstrdup(&c->dir, rootdir);
+ c->perm = 0666;
+ c->state = "Closed";
+ c->killonclose = 0;
+ c->killed = 0;
+ c->nice = 0;
+ free(c->cmd);
+ c->cmd = nil;
+ if(c->waitq != nil){
+ qfree(c->waitq);
+ c->waitq = nil;
+ }
+ free(c->error);
+ c->error = nil;
+}
+
+static void
+cmdfdclose(Conv *c, int fd)
+{
+ if(--c->count[fd] == 0 && c->fd[fd] != -1){
+ close(c->fd[fd]);
+ c->fd[fd] = -1;
+ }
+}
+
+static void
+cmdclose(Chan *c)
+{
+ Conv *cc;
+ int r;
+
+ if((c->flag & COPEN) == 0)
+ return;
+
+ switch(TYPE(c->qid)) {
+ case Qctl:
+ case Qdata:
+ case Qstderr:
+ case Qwait:
+ cc = cmd.conv[CONV(c->qid)];
+ qlock(&cc->l);
+ if(TYPE(c->qid) == Qdata){
+ if(c->mode == OWRITE || c->mode == ORDWR)
+ cmdfdclose(cc, 0);
+ if(c->mode == OREAD || c->mode == ORDWR)
+ cmdfdclose(cc, 1);
+ }else if(TYPE(c->qid) == Qstderr)
+ cmdfdclose(cc, 2);
+
+ r = --cc->inuse;
+ if(cc->child != nil){
+ if(!cc->killed)
+ if(r == 0 || (cc->killonclose && TYPE(c->qid) == Qctl)){
+ oscmdkill(cc->child);
+ cc->killed = 1;
+ }
+ }else if(r == 0)
+ closeconv(cc);
+
+ qunlock(&cc->l);
+ break;
+ }
+}
+
+static long
+cmdread(Chan *ch, void *a, long n, vlong offset)
+{
+ Conv *c;
+ char *p, *cmds;
+ int fd;
+
+ USED(offset);
+
+ p = a;
+ switch(TYPE(ch->qid)) {
+ default:
+ error(Eperm);
+ case Qcmd:
+ case Qtopdir:
+ case Qconvdir:
+ return devdirread(ch, a, n, 0, 0, cmdgen);
+ case Qctl:
+ sprint(up->genbuf, "%ld", CONV(ch->qid));
+ return readstr(offset, p, n, up->genbuf);
+ case Qstatus:
+ c = cmd.conv[CONV(ch->qid)];
+ cmds = "";
+ if(c->cmd != nil)
+ cmds = c->cmd->f[1];
+ snprint(up->genbuf, sizeof(up->genbuf), "cmd/%d %d %s %q %q\n",
+ c->x, c->inuse, c->state, c->dir, cmds);
+ return readstr(offset, p, n, up->genbuf);
+ case Qdata:
+ case Qstderr:
+ fd = 1;
+ if(TYPE(ch->qid) == Qstderr)
+ fd = 2;
+ c = cmd.conv[CONV(ch->qid)];
+ qlock(&c->l);
+ if(c->fd[fd] == -1){
+ qunlock(&c->l);
+ return 0;
+ }
+ qunlock(&c->l);
+ osenter();
+ n = read(c->fd[fd], a, n);
+ osleave();
+ if(n < 0)
+ oserror();
+ return n;
+ case Qwait:
+ c = cmd.conv[CONV(ch->qid)];
+ return qread(c->waitq, a, n);
+ }
+}
+
+static int
+cmdstarted(void *a)
+{
+ Conv *c;
+
+ c = a;
+ return c->child != nil || c->error != nil || strcmp(c->state, "Execute") != 0;
+}
+
+enum
+{
+ CMdir,
+ CMexec,
+ CMkill,
+ CMnice,
+ CMkillonclose
+};
+
+static
+Cmdtab cmdtab[] = {
+ CMdir, "dir", 2,
+ CMexec, "exec", 0,
+ CMkill, "kill", 1,
+ CMnice, "nice", 0,
+ CMkillonclose, "killonclose", 0,
+};
+
+static long
+cmdwrite(Chan *ch, void *a, long n, vlong offset)
+{
+ int i, r;
+ Conv *c;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+
+ USED(offset);
+
+ switch(TYPE(ch->qid)) {
+ default:
+ error(Eperm);
+ case Qctl:
+ c = cmd.conv[CONV(ch->qid)];
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, cmdtab, nelem(cmdtab));
+ switch(ct->index){
+ case CMdir:
+ kstrdup(&c->dir, cb->f[1]);
+ break;
+ case CMexec:
+ poperror(); /* cb */
+ qlock(&c->l);
+ if(waserror()){
+ qunlock(&c->l);
+ free(cb);
+ nexterror();
+ }
+ if(c->child != nil || c->cmd != nil)
+ error(Einuse);
+ for(i = 0; i < nelem(c->fd); i++)
+ if(c->fd[i] != -1)
+ error(Einuse);
+ if(cb->nf < 1)
+ error(Etoosmall);
+ kproc("cmdproc", cmdproc, c, 0); /* cmdproc held back until unlock below */
+ free(c->cmd);
+ c->cmd = cb; /* don't free cb */
+ c->state = "Execute";
+ poperror();
+ qunlock(&c->l);
+ while(waserror())
+ ;
+ Sleep(&c->startr, cmdstarted, c);
+ poperror();
+ if(c->error)
+ error(c->error);
+ return n; /* avoid free(cb) below */
+ case CMkill:
+ qlock(&c->l);
+ if(waserror()){
+ qunlock(&c->l);
+ nexterror();
+ }
+ if(c->child == nil)
+ error("not started");
+ if(oscmdkill(c->child) < 0)
+ oserror();
+ poperror();
+ qunlock(&c->l);
+ break;
+ case CMnice:
+ c->nice = cb->nf > 1? atoi(cb->f[1]): 1;
+ break;
+ case CMkillonclose:
+ c->killonclose = 1;
+ break;
+ }
+ poperror();
+ free(cb);
+ break;
+ case Qdata:
+ c = cmd.conv[CONV(ch->qid)];
+ qlock(&c->l);
+ if(c->fd[0] == -1){
+ qunlock(&c->l);
+ error(Ehungup);
+ }
+ qunlock(&c->l);
+ osenter();
+ r = write(c->fd[0], a, n);
+ osleave();
+ if(r == 0)
+ error(Ehungup);
+ if(r < 0) {
+ /* XXX perhaps should kill writer "write on closed pipe" here, 2nd time around? */
+ oserror();
+ }
+ return r;
+ }
+ return n;
+}
+
+static int
+cmdwstat(Chan *c, uchar *dp, int n)
+{
+ Dir *d;
+ Conv *cv;
+
+ switch(TYPE(c->qid)){
+ default:
+ error(Eperm);
+ case Qctl:
+ case Qdata:
+ case Qstderr:
+ d = malloc(sizeof(*d)+n);
+ if(d == nil)
+ error(Enomem);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ n = convM2D(dp, n, d, (char*)&d[1]);
+ if(n == 0)
+ error(Eshortstat);
+ cv = cmd.conv[CONV(c->qid)];
+ if(!iseve() && strcmp(up->env->user, cv->owner) != 0)
+ error(Eperm);
+ if(!emptystr(d->uid))
+ kstrdup(&cv->owner, d->uid);
+ if(d->mode != ~0UL)
+ cv->perm = d->mode & 0777;
+ poperror();
+ free(d);
+ break;
+ }
+ return n;
+}
+
+static Conv*
+cmdclone(char *user)
+{
+ Conv *c, **pp, **ep;
+ int i;
+
+ c = nil;
+ ep = &cmd.conv[cmd.maxconv];
+ for(pp = cmd.conv; pp < ep; pp++) {
+ c = *pp;
+ if(c == nil) {
+ c = malloc(sizeof(Conv));
+ if(c == nil)
+ error(Enomem);
+ qlock(&c->l);
+ c->inuse = 1;
+ c->x = pp - cmd.conv;
+ cmd.nc++;
+ *pp = c;
+ break;
+ }
+ if(canqlock(&c->l)){
+ if(c->inuse == 0 && c->child == nil)
+ break;
+ qunlock(&c->l);
+ }
+ }
+ if(pp >= ep)
+ return nil;
+
+ c->inuse = 1;
+ kstrdup(&c->owner, user);
+ kstrdup(&c->dir, rootdir);
+ c->perm = 0660;
+ c->state = "Closed";
+ for(i=0; i<nelem(c->fd); i++)
+ c->fd[i] = -1;
+
+ qunlock(&c->l);
+ return c;
+}
+
+static void
+cmdproc(void *a)
+{
+ Conv *c;
+ int n;
+ char status[ERRMAX];
+ void *t;
+
+ c = a;
+ qlock(&c->l);
+ if(Debug)
+ print("f[0]=%q f[1]=%q\n", c->cmd->f[0], c->cmd->f[1]);
+ if(waserror()){
+ if(Debug)
+ print("failed: %q\n", up->env->errstr);
+ kstrdup(&c->error, up->env->errstr);
+ c->state = "Done";
+ qunlock(&c->l);
+ Wakeup(&c->startr);
+ pexit("cmdproc", 0);
+ }
+ t = oscmd(c->cmd->f+1, c->nice, c->dir, c->fd);
+ if(t == nil)
+ oserror();
+ c->child = t; /* to allow oscmdkill */
+ poperror();
+ qunlock(&c->l);
+ Wakeup(&c->startr);
+ if(Debug)
+ print("started\n");
+ while(waserror())
+ oscmdkill(t);
+ osenter();
+ n = oscmdwait(t, status, sizeof(status));
+ osleave();
+ if(n < 0){
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ n = snprint(status, sizeof(status), "0 0 0 0 %q", up->genbuf);
+ }
+ qlock(&c->l);
+ c->child = nil;
+ oscmdfree(t);
+ if(Debug){
+ status[n]=0;
+ print("done %d %d %d: %q\n", c->fd[0], c->fd[1], c->fd[2], status);
+ }
+ if(c->inuse > 0){
+ c->state = "Done";
+ if(c->waitq != nil)
+ qproduce(c->waitq, status, n);
+ }else
+ closeconv(c);
+ qunlock(&c->l);
+ pexit("", 0);
+}
+
+Dev cmddevtab = {
+ 'C',
+ "cmd",
+
+ cmdinit,
+ cmdattach,
+ cmdwalk,
+ cmdstat,
+ cmdopen,
+ devcreate,
+ cmdclose,
+ cmdread,
+ devbread,
+ cmdwrite,
+ devbwrite,
+ devremove,
+ cmdwstat
+};
--- /dev/null
+++ b/emu/port/devcons.c
@@ -1,0 +1,643 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "version.h"
+#include "mp.h"
+#include "libsec.h"
+#include "keyboard.h"
+
+extern int cflag;
+int exdebug;
+extern int keepbroken;
+
+enum
+{
+ Qdir,
+ Qcons,
+ Qconsctl,
+ Qdrivers,
+ Qhostowner,
+ Qhoststdin,
+ Qhoststdout,
+ Qhoststderr,
+ Qjit,
+ Qkeyboard,
+ Qkprint,
+ Qmemory,
+ Qmsec,
+ Qnotquiterandom,
+ Qnull,
+ Qrandom,
+ Qscancode,
+ Qsysctl,
+ Qsysname,
+ Qtime,
+ Quser
+};
+
+Dirtab contab[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "cons", {Qcons}, 0, 0666,
+ "consctl", {Qconsctl}, 0, 0222,
+ "drivers", {Qdrivers}, 0, 0444,
+ "hostowner", {Qhostowner}, 0, 0644,
+ "hoststdin", {Qhoststdin}, 0, 0444,
+ "hoststdout", {Qhoststdout}, 0, 0222,
+ "hoststderr", {Qhoststderr}, 0, 0222,
+ "jit", {Qjit}, 0, 0666,
+ "keyboard", {Qkeyboard}, 0, 0666,
+ "kprint", {Qkprint}, 0, 0444,
+ "memory", {Qmemory}, 0, 0444,
+ "msec", {Qmsec}, NUMSIZE, 0444,
+ "notquiterandom", {Qnotquiterandom}, 0, 0444,
+ "null", {Qnull}, 0, 0666,
+ "random", {Qrandom}, 0, 0444,
+ "scancode", {Qscancode}, 0, 0444,
+ "sysctl", {Qsysctl}, 0, 0644,
+ "sysname", {Qsysname}, 0, 0644,
+ "time", {Qtime}, 0, 0644,
+ "user", {Quser}, 0, 0644,
+};
+
+Queue* gkscanq; /* Graphics keyboard raw scancodes */
+char* gkscanid; /* name of raw scan format (if defined) */
+Queue* gkbdq; /* Graphics keyboard unprocessed input */
+Queue* kbdq; /* Console window unprocessed keyboard input */
+Queue* lineq; /* processed console input */
+
+char *ossysname;
+
+static struct
+{
+ RWlock l;
+ Queue* q;
+} kprintq;
+
+vlong timeoffset;
+
+extern int dflag;
+
+static int sysconwrite(void*, ulong);
+extern char** rebootargv;
+
+static struct
+{
+ QLock q;
+ QLock gq; /* separate lock for the graphical input */
+
+ int raw; /* true if we shouldn't process input */
+ Ref ctl; /* number of opens to the control file */
+ Ref ptr; /* number of opens to the ptr file */
+ int scan; /* true if reading raw scancodes */
+ int x; /* index into line */
+ char line[1024]; /* current input line */
+
+ Rune c;
+ int count;
+} kbd;
+
+void
+kbdslave(void *a)
+{
+ char b;
+
+ USED(a);
+ for(;;) {
+ b = readkbd();
+ if(kbd.raw == 0){
+ switch(b){
+ case 0x15:
+ write(1, "^U\n", 3);
+ break;
+ default:
+ write(1, &b, 1);
+ break;
+ }
+ }
+ qproduce(kbdq, &b, 1);
+ }
+ /* pexit("kbdslave", 0); */ /* not reached */
+}
+
+void
+gkbdputc(Queue *q, int ch)
+{
+ int n;
+ Rune r;
+ static uchar kc[5*UTFmax];
+ static int nk, collecting = 0;
+ char buf[UTFmax];
+
+ r = ch;
+ if(r == Latin) {
+ collecting = 1;
+ nk = 0;
+ return;
+ }
+ if(collecting) {
+ int c;
+ nk += runetochar((char*)&kc[nk], &r);
+ c = latin1(kc, nk);
+ if(c < -1) /* need more keystrokes */
+ return;
+ collecting = 0;
+ if(c == -1) { /* invalid sequence */
+ qproduce(q, kc, nk);
+ return;
+ }
+ r = (Rune)c;
+ }
+ n = runetochar(buf, &r);
+ if(n == 0)
+ return;
+ /* if(!isdbgkey(r)) */
+ qproduce(q, buf, n);
+}
+
+void
+consinit(void)
+{
+ kbdq = qopen(512, 0, nil, nil);
+ if(kbdq == 0)
+ panic("no memory");
+ lineq = qopen(2*1024, 0, nil, nil);
+ if(lineq == 0)
+ panic("no memory");
+ gkbdq = qopen(512, 0, nil, nil);
+ if(gkbdq == 0)
+ panic("no memory");
+ randominit();
+}
+
+/*
+ * return true if current user is eve
+ */
+int
+iseve(void)
+{
+ return strcmp(eve, up->env->user) == 0;
+}
+
+static Chan*
+consattach(char *spec)
+{
+ static int kp;
+
+ if(kp == 0 && !dflag) {
+ kp = 1;
+ kproc("kbd", kbdslave, 0, 0);
+ }
+ return devattach('c', spec);
+}
+
+static Walkqid*
+conswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, contab, nelem(contab), devgen);
+}
+
+static int
+consstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, contab, nelem(contab), devgen);
+}
+
+static Chan*
+consopen(Chan *c, int omode)
+{
+ c = devopen(c, omode, contab, nelem(contab), devgen);
+ switch((ulong)c->qid.path) {
+ case Qconsctl:
+ incref(&kbd.ctl);
+ break;
+
+ case Qscancode:
+ qlock(&kbd.gq);
+ if(gkscanq != nil || gkscanid == nil) {
+ qunlock(&kbd.q);
+ c->flag &= ~COPEN;
+ if(gkscanq)
+ error(Einuse);
+ else
+ error("not supported");
+ }
+ gkscanq = qopen(256, 0, nil, nil);
+ qunlock(&kbd.gq);
+ break;
+
+ case Qkprint:
+ wlock(&kprintq.l);
+ if(waserror()){
+ wunlock(&kprintq.l);
+ c->flag &= ~COPEN;
+ nexterror();
+ }
+ if(kprintq.q != nil)
+ error(Einuse);
+ kprintq.q = qopen(32*1024, Qcoalesce, nil, nil);
+ if(kprintq.q == nil)
+ error(Enomem);
+ qnoblock(kprintq.q, 1);
+ poperror();
+ wunlock(&kprintq.l);
+ c->iounit = qiomaxatomic;
+ break;
+ }
+ return c;
+}
+
+static void
+consclose(Chan *c)
+{
+ if((c->flag & COPEN) == 0)
+ return;
+
+ switch((ulong)c->qid.path) {
+ case Qconsctl:
+ /* last close of control file turns off raw */
+ if(decref(&kbd.ctl) == 0)
+ kbd.raw = 0;
+ break;
+
+ case Qscancode:
+ qlock(&kbd.gq);
+ if(gkscanq) {
+ qfree(gkscanq);
+ gkscanq = nil;
+ }
+ qunlock(&kbd.gq);
+ break;
+
+ case Qkprint:
+ wlock(&kprintq.l);
+ qfree(kprintq.q);
+ kprintq.q = nil;
+ wunlock(&kprintq.l);
+ break;
+ }
+}
+
+static long
+consread(Chan *c, void *va, long n, vlong offset)
+{
+ int send;
+ char buf[64], ch;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, va, n, contab, nelem(contab), devgen);
+
+ switch((ulong)c->qid.path) {
+ default:
+ error(Egreg);
+
+ case Qsysctl:
+ return readstr(offset, va, n, VERSION);
+
+ case Qsysname:
+ if(ossysname == nil)
+ return 0;
+ return readstr(offset, va, n, ossysname);
+
+ case Qrandom:
+ return randomread(va, n);
+
+ case Qnotquiterandom:
+ genrandom(va, n);
+ return n;
+
+ case Qhostowner:
+ return readstr(offset, va, n, eve);
+
+ case Qhoststdin:
+ return read(0, va, n); /* should be pread */
+
+ case Quser:
+ return readstr(offset, va, n, up->env->user);
+
+ case Qjit:
+ snprint(buf, sizeof(buf), "%d", cflag);
+ return readstr(offset, va, n, buf);
+
+ case Qtime:
+ snprint(buf, sizeof(buf), "%.lld", timeoffset + osusectime());
+ return readstr(offset, va, n, buf);
+
+ case Qdrivers:
+ return devtabread(c, va, n, offset);
+
+ case Qmemory:
+ return poolread(va, n, offset);
+
+ case Qnull:
+ return 0;
+
+ case Qmsec:
+ return readnum(offset, va, n, osmillisec(), NUMSIZE);
+
+ case Qcons:
+ qlock(&kbd.q);
+ if(waserror()){
+ qunlock(&kbd.q);
+ nexterror();
+ }
+
+ if(dflag)
+ error(Enonexist);
+
+ while(!qcanread(lineq)) {
+ if(qread(kbdq, &ch, 1) == 0)
+ continue;
+ send = 0;
+ if(ch == 0){
+ /* flush output on rawoff -> rawon */
+ if(kbd.x > 0)
+ send = !qcanread(kbdq);
+ }else if(kbd.raw){
+ kbd.line[kbd.x++] = ch;
+ send = !qcanread(kbdq);
+ }else{
+ switch(ch){
+ case '\b':
+ if(kbd.x)
+ kbd.x--;
+ break;
+ case 0x15:
+ kbd.x = 0;
+ break;
+ case 0x04:
+ send = 1;
+ break;
+ case '\n':
+ send = 1;
+ default:
+ kbd.line[kbd.x++] = ch;
+ break;
+ }
+ }
+ if(send || kbd.x == sizeof kbd.line){
+ qwrite(lineq, kbd.line, kbd.x);
+ kbd.x = 0;
+ }
+ }
+ n = qread(lineq, va, n);
+ qunlock(&kbd.q);
+ poperror();
+ return n;
+
+ case Qscancode:
+ if(offset == 0)
+ return readstr(0, va, n, gkscanid);
+ return qread(gkscanq, va, n);
+
+ case Qkeyboard:
+ return qread(gkbdq, va, n);
+
+ case Qkprint:
+ rlock(&kprintq.l);
+ if(waserror()){
+ runlock(&kprintq.l);
+ nexterror();
+ }
+ n = qread(kprintq.q, va, n);
+ poperror();
+ runlock(&kprintq.l);
+ return n;
+ }
+}
+
+static long
+conswrite(Chan *c, void *va, long n, vlong offset)
+{
+ char buf[128], *a, ch;
+ int x;
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ switch((ulong)c->qid.path) {
+ default:
+ error(Egreg);
+
+ case Qcons:
+ if(canrlock(&kprintq.l)){
+ if(kprintq.q != nil){
+ if(waserror()){
+ runlock(&kprintq.l);
+ nexterror();
+ }
+ qwrite(kprintq.q, va, n);
+ poperror();
+ runlock(&kprintq.l);
+ return n;
+ }
+ runlock(&kprintq.l);
+ }
+ return write(1, va, n);
+
+ case Qsysctl:
+ return sysconwrite(va, n);
+
+ case Qconsctl:
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ strncpy(buf, va, n);
+ buf[n] = 0;
+ for(a = buf; a;){
+ if(strncmp(a, "rawon", 5) == 0){
+ kbd.raw = 1;
+ /* clumsy hack - wake up reader */
+ ch = 0;
+ qwrite(kbdq, &ch, 1);
+ } else if(strncmp(buf, "rawoff", 6) == 0){
+ kbd.raw = 0;
+ }
+ if((a = strchr(a, ' ')) != nil)
+ a++;
+ }
+ break;
+
+ case Qkeyboard:
+ for(x=0; x<n; ) {
+ Rune r;
+ x += chartorune(&r, &((char*)va)[x]);
+ gkbdputc(gkbdq, r);
+ }
+ break;
+
+ case Qnull:
+ break;
+
+ case Qtime:
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ strncpy(buf, va, n);
+ buf[n] = '\0';
+ timeoffset = strtoll(buf, 0, 0)-osusectime();
+ break;
+
+ case Qhostowner:
+ if(!iseve())
+ error(Eperm);
+ if(offset != 0 || n >= sizeof(buf))
+ error(Ebadarg);
+ memmove(buf, va, n);
+ buf[n] = '\0';
+ if(n > 0 && buf[n-1] == '\n')
+ buf[--n] = '\0';
+ if(n == 0)
+ error(Ebadarg);
+ /* renameuser(eve, buf); */
+ /* renameproguser(eve, buf); */
+ kstrdup(&eve, buf);
+ kstrdup(&up->env->user, buf);
+ break;
+
+ case Quser:
+ if(!iseve())
+ error(Eperm);
+ if(offset != 0)
+ error(Ebadarg);
+ if(n <= 0 || n >= sizeof(buf))
+ error(Ebadarg);
+ strncpy(buf, va, n);
+ buf[n] = '\0';
+ if(n > 0 && buf[n-1] == '\n')
+ buf[--n] = '\0';
+ if(n == 0)
+ error(Ebadarg);
+ setid(buf, 0);
+ break;
+
+ case Qhoststdout:
+ return write(1, va, n);
+
+ case Qhoststderr:
+ return write(2, va, n);
+
+ case Qjit:
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ strncpy(buf, va, n);
+ buf[n] = '\0';
+ x = atoi(buf);
+ if(x < 0 || x > 9)
+ error(Ebadarg);
+ cflag = x;
+ break;
+
+ case Qsysname:
+ if(offset != 0)
+ error(Ebadarg);
+ if(n < 0 || n >= sizeof(buf))
+ error(Ebadarg);
+ strncpy(buf, va, n);
+ buf[n] = '\0';
+ if(buf[n-1] == '\n')
+ buf[n-1] = 0;
+ kstrdup(&ossysname, buf);
+ break;
+ }
+ return n;
+}
+
+static int
+sysconwrite(void *va, ulong count)
+{
+ Cmdbuf *cb;
+ int e;
+ cb = parsecmd(va, count);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ if(cb->nf == 0)
+ error(Enoctl);
+ if(strcmp(cb->f[0], "reboot") == 0){
+ osreboot(rebootargv[0], rebootargv);
+ error("reboot not supported");
+ }else if(strcmp(cb->f[0], "halt") == 0){
+ if(cb->nf > 1)
+ e = atoi(cb->f[1]);
+ else
+ e = 0;
+ cleanexit(e); /* XXX ignored for the time being (and should be a string anyway) */
+ }else if(strcmp(cb->f[0], "broken") == 0)
+ keepbroken = 1;
+ else if(strcmp(cb->f[0], "nobroken") == 0)
+ keepbroken = 0;
+ else if(strcmp(cb->f[0], "exdebug") == 0)
+ exdebug = !exdebug;
+ else
+ error(Enoctl);
+ poperror();
+ free(cb);
+ return count;
+}
+
+Dev consdevtab = {
+ 'c',
+ "cons",
+
+ consinit,
+ consattach,
+ conswalk,
+ consstat,
+ consopen,
+ devcreate,
+ consclose,
+ consread,
+ devbread,
+ conswrite,
+ devbwrite,
+ devremove,
+ devwstat
+};
+
+static ulong randn;
+
+static void
+seedrand(void)
+{
+ randomread((void*)&randn, sizeof(randn));
+}
+
+int
+nrand(int n)
+{
+ if(randn == 0)
+ seedrand();
+ randn = randn*1103515245 + 12345 + osusectime();
+ return (randn>>16) % n;
+}
+
+int
+rand(void)
+{
+ nrand(1);
+ return randn;
+}
+
+ulong
+truerand(void)
+{
+ ulong x;
+
+ randomread(&x, sizeof(x));
+ return x;
+}
+
+QLock grandomlk;
+
+void
+_genrandomqlock(void)
+{
+ qlock(&grandomlk);
+}
+
+
+void
+_genrandomqunlock(void)
+{
+ qunlock(&grandomlk);
+}
--- /dev/null
+++ b/emu/port/devdraw.c
@@ -1,0 +1,2016 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+#include <cursor.h>
+//#include "screen.h"
+
+enum
+{
+ Qtopdir = 0,
+ Qnew,
+ Q3rd,
+ Q2nd,
+ Qcolormap,
+ Qctl,
+ Qdata,
+ Qrefresh
+};
+
+/*
+ * Qid path is:
+ * 4 bits of file type (qids above)
+ * 24 bits of mux slot number +1; 0 means not attached to client
+ */
+#define QSHIFT 4 /* location in qid of client # */
+
+#define QID(q) ((((ulong)(q).path)&0x0000000F)>>0)
+#define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT)
+#define CLIENT(q) CLIENTPATH((q).path)
+
+#define NHASH (1<<5)
+#define HASHMASK (NHASH-1)
+#define IOUNIT (64*1024)
+
+typedef struct Client Client;
+typedef struct Draw Draw;
+typedef struct DImage DImage;
+typedef struct DScreen DScreen;
+typedef struct CScreen CScreen;
+typedef struct FChar FChar;
+typedef struct Refresh Refresh;
+typedef struct Refx Refx;
+typedef struct DName DName;
+
+ulong blanktime = 30; /* in minutes; a half hour */
+
+struct Draw
+{
+ QLock q;
+ int clientid;
+ int nclient;
+ Client** client;
+ int nname;
+ DName* name;
+ int vers;
+ int softscreen;
+ int blanked; /* screen turned off */
+ ulong blanktime; /* time of last operation */
+ ulong savemap[3*256];
+};
+
+struct Client
+{
+ Ref r;
+ DImage* dimage[NHASH];
+ CScreen* cscreen;
+ Refresh* refresh;
+ Rendez refrend;
+ uchar* readdata;
+ int nreaddata;
+ int busy;
+ int clientid;
+ int slot;
+ int refreshme;
+ int infoid;
+ int op; /* compositing operator - SoverD by default */
+};
+
+struct Refresh
+{
+ DImage* dimage;
+ Rectangle r;
+ Refresh* next;
+};
+
+struct Refx
+{
+ Client* client;
+ DImage* dimage;
+};
+
+struct DName
+{
+ char *name;
+ Client *client;
+ DImage* dimage;
+ int vers;
+};
+
+struct FChar
+{
+ int minx; /* left edge of bits */
+ int maxx; /* right edge of bits */
+ uchar miny; /* first non-zero scan-line */
+ uchar maxy; /* last non-zero scan-line + 1 */
+ schar left; /* offset of baseline */
+ uchar width; /* width of baseline */
+};
+
+/*
+ * Reference counts in DImages:
+ * one per open by original client
+ * one per screen image or fill
+ * one per image derived from this one by name
+ */
+struct DImage
+{
+ int id;
+ int ref;
+ char *name;
+ int vers;
+ Memimage* image;
+ int ascent;
+ int nfchar;
+ FChar* fchar;
+ DScreen* dscreen; /* 0 if not a window */
+ DImage* fromname; /* image this one is derived from, by name */
+ DImage* next;
+};
+
+struct CScreen
+{
+ DScreen* dscreen;
+ CScreen* next;
+};
+
+struct DScreen
+{
+ int id;
+ int public;
+ int ref;
+ DImage *dimage;
+ DImage *dfill;
+ Memscreen* screen;
+ Client* owner;
+ DScreen* next;
+};
+
+static Draw sdraw;
+ Memimage *screenimage; /* accessed by some win-*.c */
+static Memdata screendata;
+static Rectangle flushrect;
+static int waste;
+static DScreen* dscreen;
+extern void flushmemscreen(Rectangle);
+ void drawmesg(Client*, void*, int);
+ void drawuninstall(Client*, int);
+ void drawfreedimage(DImage*);
+ Client* drawclientofpath(ulong);
+static int drawclientop(Client*);
+
+static char Enodrawimage[] = "unknown id for draw image";
+static char Enodrawscreen[] = "unknown id for draw screen";
+static char Eshortdraw[] = "short draw message";
+static char Eshortread[] = "draw read too short";
+static char Eimageexists[] = "image id in use";
+static char Escreenexists[] = "screen id in use";
+static char Edrawmem[] = "out of memory: image";
+static char Ereadoutside[] = "readimage outside image";
+static char Ewriteoutside[] = "writeimage outside image";
+static char Enotfont[] = "image not a font";
+static char Eindex[] = "character index out of range";
+static char Enoclient[] = "no such draw client";
+static char Enameused[] = "image name in use";
+static char Enoname[] = "no image with that name";
+static char Eoldname[] = "named image no longer valid";
+static char Enamed[] = "image already has name";
+static char Ewrongname[] = "wrong name for image";
+
+static int
+drawgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp)
+{
+ int t;
+ Qid q;
+ ulong path;
+ Client *cl;
+
+ USED(name);
+ USED(tab);
+ USED(x);
+ q.vers = 0;
+
+ if(s == DEVDOTDOT){
+ switch(QID(c->qid)){
+ case Qtopdir:
+ case Q2nd:
+ mkqid(&q, Qtopdir, 0, QTDIR);
+ devdir(c, q, "#i", 0, eve, 0500, dp);
+ break;
+ case Q3rd:
+ cl = drawclientofpath(c->qid.path);
+ if(cl == nil)
+ strcpy(up->genbuf, "??");
+ else
+ sprint(up->genbuf, "%d", cl->clientid);
+ mkqid(&q, Q2nd, 0, QTDIR);
+ devdir(c, q, up->genbuf, 0, eve, 0500, dp);
+ break;
+ default:
+ panic("drawwalk %llux", c->qid.path);
+ }
+ return 1;
+ }
+
+ /*
+ * Top level directory contains the name of the device.
+ */
+ t = QID(c->qid);
+ if(t == Qtopdir){
+ switch(s){
+ case 0:
+ mkqid(&q, Q2nd, 0, QTDIR);
+ devdir(c, q, "draw", 0, eve, 0555, dp);
+ break;
+ default:
+ return -1;
+ }
+ return 1;
+ }
+
+ /*
+ * Second level contains "new" plus all the clients.
+ */
+ if(t == Q2nd || t == Qnew){
+ if(s == 0){
+ mkqid(&q, Qnew, 0, QTFILE);
+ devdir(c, q, "new", 0, eve, 0666, dp);
+ }
+ else if(s <= sdraw.nclient){
+ cl = sdraw.client[s-1];
+ if(cl == 0)
+ return 0;
+ sprint(up->genbuf, "%d", cl->clientid);
+ mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+ else
+ return -1;
+ return 1;
+ }
+
+ /*
+ * Third level.
+ */
+ path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */
+ q.vers = c->qid.vers;
+ q.type = QTFILE;
+ switch(s){
+ case 0:
+ q.path = path|Qcolormap;
+ devdir(c, q, "colormap", 0, eve, 0600, dp);
+ break;
+ case 1:
+ q.path = path|Qctl;
+ devdir(c, q, "ctl", 0, eve, 0600, dp);
+ break;
+ case 2:
+ q.path = path|Qdata;
+ devdir(c, q, "data", 0, eve, 0600, dp);
+ break;
+ case 3:
+ q.path = path|Qrefresh;
+ devdir(c, q, "refresh", 0, eve, 0400, dp);
+ break;
+ default:
+ return -1;
+ }
+ return 1;
+}
+
+static
+int
+drawrefactive(void *a)
+{
+ Client *c;
+
+ c = a;
+ return c->refreshme || c->refresh!=0;
+}
+
+static
+void
+drawrefreshscreen(DImage *l, Client *client)
+{
+ while(l != nil && l->dscreen == nil)
+ l = l->fromname;
+ if(l != nil && l->dscreen->owner != client)
+ l->dscreen->owner->refreshme = 1;
+}
+
+static
+void
+drawrefresh(Memimage *l, Rectangle r, void *v)
+{
+ Refx *x;
+ DImage *d;
+ Client *c;
+ Refresh *ref;
+
+ USED(l);
+ if(v == 0)
+ return;
+ x = v;
+ c = x->client;
+ d = x->dimage;
+ for(ref=c->refresh; ref; ref=ref->next)
+ if(ref->dimage == d){
+ combinerect(&ref->r, r);
+ return;
+ }
+ ref = malloc(sizeof(Refresh));
+ if(ref){
+ ref->dimage = d;
+ ref->r = r;
+ ref->next = c->refresh;
+ c->refresh = ref;
+ }
+}
+
+static void
+addflush(Rectangle r)
+{
+ int abb, ar, anbb;
+ Rectangle nbb;
+
+ if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
+ return;
+
+ if(flushrect.min.x >= flushrect.max.x){
+ flushrect = r;
+ waste = 0;
+ return;
+ }
+ nbb = flushrect;
+ combinerect(&nbb, r);
+ ar = Dx(r)*Dy(r);
+ abb = Dx(flushrect)*Dy(flushrect);
+ anbb = Dx(nbb)*Dy(nbb);
+ /*
+ * Area of new waste is area of new bb minus area of old bb,
+ * less the area of the new segment, which we assume is not waste.
+ * This could be negative, but that's OK.
+ */
+ waste += anbb-abb - ar;
+ if(waste < 0)
+ waste = 0;
+ /*
+ * absorb if:
+ * total area is small
+ * waste is less than half total area
+ * rectangles touch
+ */
+ if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
+ flushrect = nbb;
+ return;
+ }
+ /* emit current state */
+ if(flushrect.min.x < flushrect.max.x)
+ flushmemscreen(flushrect);
+ flushrect = r;
+ waste = 0;
+}
+
+static
+void
+dstflush(Memimage *dst, Rectangle r)
+{
+ Memlayer *l;
+
+ if(dst == screenimage){
+ combinerect(&flushrect, r);
+ return;
+ }
+ l = dst->layer;
+ if(l == nil)
+ return;
+ do{
+ if(l->screen->image->data != screenimage->data)
+ return;
+ r = rectaddpt(r, l->delta);
+ l = l->screen->image->layer;
+ }while(l);
+ addflush(r);
+}
+
+static
+void
+drawflush(void)
+{
+ if(flushrect.min.x < flushrect.max.x)
+ flushmemscreen(flushrect);
+ flushrect = Rect(10000, 10000, -10000, -10000);
+}
+
+static
+int
+drawcmp(char *a, char *b, int n)
+{
+ if(strlen(a) != n)
+ return 1;
+ return memcmp(a, b, n);
+}
+
+DName*
+drawlookupname(int n, char *str)
+{
+ DName *name, *ename;
+
+ name = sdraw.name;
+ ename = &name[sdraw.nname];
+ for(; name<ename; name++)
+ if(drawcmp(name->name, str, n) == 0)
+ return name;
+ return 0;
+}
+
+int
+drawgoodname(DImage *d)
+{
+ DName *n;
+
+ /* if window, validate the screen's own images */
+ if(d->dscreen)
+ if(drawgoodname(d->dscreen->dimage) == 0
+ || drawgoodname(d->dscreen->dfill) == 0)
+ return 0;
+ if(d->name == nil)
+ return 1;
+ n = drawlookupname(strlen(d->name), d->name);
+ if(n==nil || n->vers!=d->vers)
+ return 0;
+ return 1;
+}
+
+DImage*
+drawlookup(Client *client, int id, int checkname)
+{
+ DImage *d;
+
+ d = client->dimage[id&HASHMASK];
+ while(d){
+ if(d->id == id){
+ if(checkname && !drawgoodname(d))
+ error(Eoldname);
+ return d;
+ }
+ d = d->next;
+ }
+ return 0;
+}
+
+DScreen*
+drawlookupdscreen(int id)
+{
+ DScreen *s;
+
+ s = dscreen;
+ while(s){
+ if(s->id == id)
+ return s;
+ s = s->next;
+ }
+ return 0;
+}
+
+DScreen*
+drawlookupscreen(Client *client, int id, CScreen **cs)
+{
+ CScreen *s;
+
+ s = client->cscreen;
+ while(s){
+ if(s->dscreen->id == id){
+ *cs = s;
+ return s->dscreen;
+ }
+ s = s->next;
+ }
+ error(Enodrawscreen);
+ return 0;
+}
+
+Memimage*
+drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
+{
+ DImage *d;
+
+ d = malloc(sizeof(DImage));
+ if(d == 0)
+ return 0;
+ d->id = id;
+ d->ref = 1;
+ d->name = 0;
+ d->vers = 0;
+ d->image = i;
+ d->nfchar = 0;
+ d->fchar = 0;
+ d->fromname = 0;
+ d->dscreen = dscreen;
+ d->next = client->dimage[id&HASHMASK];
+ client->dimage[id&HASHMASK] = d;
+ return i;
+}
+
+Memscreen*
+drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
+{
+ Memscreen *s;
+ CScreen *c;
+
+ c = malloc(sizeof(CScreen));
+ if(dimage && dimage->image && dimage->image->chan == 0)
+ panic("bad image %p in drawinstallscreen", dimage->image);
+
+ if(c == 0)
+ return 0;
+ if(d == 0){
+ d = malloc(sizeof(DScreen));
+ if(d == 0){
+ free(c);
+ return 0;
+ }
+ s = malloc(sizeof(Memscreen));
+ if(s == 0){
+ free(c);
+ free(d);
+ return 0;
+ }
+ s->frontmost = 0;
+ s->rearmost = 0;
+ d->dimage = dimage;
+ if(dimage){
+ s->image = dimage->image;
+ dimage->ref++;
+ }
+ d->dfill = dfill;
+ if(dfill){
+ s->fill = dfill->image;
+ dfill->ref++;
+ }
+ d->ref = 0;
+ d->id = id;
+ d->screen = s;
+ d->public = public;
+ d->next = dscreen;
+ d->owner = client;
+ dscreen = d;
+ }
+ c->dscreen = d;
+ d->ref++;
+ c->next = client->cscreen;
+ client->cscreen = c;
+ return d->screen;
+}
+
+void
+drawdelname(DName *name)
+{
+ int i;
+
+ i = name-sdraw.name;
+ memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
+ sdraw.nname--;
+}
+
+void
+drawfreedscreen(DScreen *this)
+{
+ DScreen *ds, *next;
+
+ this->ref--;
+ if(this->ref < 0)
+ print("negative ref in drawfreedscreen\n");
+ if(this->ref > 0)
+ return;
+ ds = dscreen;
+ if(ds == this){
+ dscreen = this->next;
+ goto Found;
+ }
+ while(next = ds->next){ /* assign = */
+ if(next == this){
+ ds->next = this->next;
+ goto Found;
+ }
+ ds = next;
+ }
+ error(Enodrawimage);
+
+ Found:
+ if(this->dimage)
+ drawfreedimage(this->dimage);
+ if(this->dfill)
+ drawfreedimage(this->dfill);
+ free(this->screen);
+ free(this);
+}
+
+void
+drawfreedimage(DImage *dimage)
+{
+ int i;
+ Memimage *l;
+ DScreen *ds;
+
+ dimage->ref--;
+ if(dimage->ref < 0)
+ print("negative ref in drawfreedimage\n");
+ if(dimage->ref > 0)
+ return;
+
+ /* any names? */
+ for(i=0; i<sdraw.nname; )
+ if(sdraw.name[i].dimage == dimage)
+ drawdelname(sdraw.name+i);
+ else
+ i++;
+ if(dimage->fromname){ /* acquired by name; owned by someone else*/
+ drawfreedimage(dimage->fromname);
+ goto Return;
+ }
+ if(dimage->image == screenimage) /* don't free the display */
+ goto Return;
+ ds = dimage->dscreen;
+ if(ds){
+ l = dimage->image;
+ if(l->data == screenimage->data)
+ dstflush(l->layer->screen->image, l->layer->screenr);
+ if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */
+ free(l->layer->refreshptr);
+ l->layer->refreshptr = nil;
+ if(drawgoodname(dimage))
+ memldelete(l);
+ else
+ memlfree(l);
+ drawfreedscreen(ds);
+ }else
+ freememimage(dimage->image);
+ Return:
+ free(dimage->fchar);
+ free(dimage);
+}
+
+void
+drawuninstallscreen(Client *client, CScreen *this)
+{
+ CScreen *cs, *next;
+
+ cs = client->cscreen;
+ if(cs == this){
+ client->cscreen = this->next;
+ drawfreedscreen(this->dscreen);
+ free(this);
+ return;
+ }
+ while(next = cs->next){ /* assign = */
+ if(next == this){
+ cs->next = this->next;
+ drawfreedscreen(this->dscreen);
+ free(this);
+ return;
+ }
+ cs = next;
+ }
+}
+
+void
+drawuninstall(Client *client, int id)
+{
+ DImage *d, *next;
+
+ d = client->dimage[id&HASHMASK];
+ if(d == 0)
+ error(Enodrawimage);
+ if(d->id == id){
+ client->dimage[id&HASHMASK] = d->next;
+ drawfreedimage(d);
+ return;
+ }
+ while(next = d->next){ /* assign = */
+ if(next->id == id){
+ d->next = next->next;
+ drawfreedimage(next);
+ return;
+ }
+ d = next;
+ }
+ error(Enodrawimage);
+}
+
+void
+drawaddname(Client *client, DImage *di, int n, char *str)
+{
+ DName *name, *ename, *new, *t;
+
+ name = sdraw.name;
+ ename = &name[sdraw.nname];
+ for(; name<ename; name++)
+ if(drawcmp(name->name, str, n) == 0)
+ error(Enameused);
+ t = smalloc((sdraw.nname+1)*sizeof(DName));
+ memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
+ free(sdraw.name);
+ sdraw.name = t;
+ new = &sdraw.name[sdraw.nname++];
+ new->name = smalloc(n+1);
+ memmove(new->name, str, n);
+ new->name[n] = 0;
+ new->dimage = di;
+ new->client = client;
+ new->vers = ++sdraw.vers;
+}
+
+Client*
+drawnewclient(void)
+{
+ Client *cl, **cp;
+ int i;
+
+ for(i=0; i<sdraw.nclient; i++){
+ cl = sdraw.client[i];
+ if(cl == 0)
+ break;
+ }
+ if(i == sdraw.nclient){
+ cp = malloc((sdraw.nclient+1)*sizeof(Client*));
+ if(cp == 0)
+ return 0;
+ memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*));
+ free(sdraw.client);
+ sdraw.client = cp;
+ sdraw.nclient++;
+ cp[i] = 0;
+ }
+ cl = malloc(sizeof(Client));
+ if(cl == 0)
+ return 0;
+ memset(cl, 0, sizeof(Client));
+ cl->slot = i;
+ cl->clientid = ++sdraw.clientid;
+ cl->op = SoverD;
+ sdraw.client[i] = cl;
+ return cl;
+}
+
+static int
+drawclientop(Client *cl)
+{
+ int op;
+
+ op = cl->op;
+ cl->op = SoverD;
+ return op;
+}
+
+int
+drawhasclients(void)
+{
+ /*
+ * if draw has ever been used, we can't resize the frame buffer,
+ * even if all clients have exited (nclients is cumulative); it's too
+ * hard to make work.
+ */
+ return sdraw.nclient != 0;
+}
+
+Client*
+drawclientofpath(ulong path)
+{
+ Client *cl;
+ int slot;
+
+ slot = CLIENTPATH(path);
+ if(slot == 0)
+ return nil;
+ cl = sdraw.client[slot-1];
+ if(cl==0 || cl->clientid==0)
+ return nil;
+ return cl;
+}
+
+
+Client*
+drawclient(Chan *c)
+{
+ Client *client;
+
+ client = drawclientofpath(c->qid.path);
+ if(client == nil)
+ error(Enoclient);
+ return client;
+}
+
+Memimage*
+drawimage(Client *client, uchar *a)
+{
+ DImage *d;
+
+ d = drawlookup(client, BGLONG(a), 1);
+ if(d == nil)
+ error(Enodrawimage);
+ return d->image;
+}
+
+void
+drawrectangle(Rectangle *r, uchar *a)
+{
+ r->min.x = BGLONG(a+0*4);
+ r->min.y = BGLONG(a+1*4);
+ r->max.x = BGLONG(a+2*4);
+ r->max.y = BGLONG(a+3*4);
+}
+
+void
+drawpoint(Point *p, uchar *a)
+{
+ p->x = BGLONG(a+0*4);
+ p->y = BGLONG(a+1*4);
+}
+
+Point
+drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
+{
+ FChar *fc;
+ Rectangle r;
+ Point sp1;
+
+ fc = &font->fchar[index];
+ r.min.x = p.x+fc->left;
+ r.min.y = p.y-(font->ascent-fc->miny);
+ r.max.x = r.min.x+(fc->maxx-fc->minx);
+ r.max.y = r.min.y+(fc->maxy-fc->miny);
+ sp1.x = sp->x+fc->left;
+ sp1.y = sp->y+fc->miny;
+ memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
+ p.x += fc->width;
+ sp->x += fc->width;
+ return p;
+}
+
+static int
+initscreenimage(void)
+{
+ int width, depth;
+ ulong chan;
+ Rectangle r;
+
+ if(screenimage != nil)
+ return 1;
+
+ memimageinit();
+ screendata.base = nil;
+ screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen);
+ if(screendata.bdata == nil)
+ return 0;
+ screendata.ref = 1;
+
+ screenimage = allocmemimaged(r, chan, &screendata);
+ if(screenimage == nil){
+ /* RSC: BUG: detach screen */
+ return 0;
+ }
+
+ screenimage->width = width;
+ screenimage->clipr = r;
+ return 1;
+}
+
+void
+deletescreenimage(void)
+{
+ qlock(&sdraw.q);
+ /* RSC: BUG: detach screen */
+ if(screenimage)
+ freememimage(screenimage);
+ screenimage = nil;
+ qunlock(&sdraw.q);
+}
+
+Chan*
+drawattach(char *spec)
+{
+ qlock(&sdraw.q);
+ if(!initscreenimage()){
+ qunlock(&sdraw.q);
+ error("no frame buffer");
+ }
+ qunlock(&sdraw.q);
+ return devattach('i', spec);
+}
+
+static Walkqid*
+drawwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ if(screendata.bdata == nil)
+ error("no frame buffer");
+ return devwalk(c, nc, name, nname, 0, 0, drawgen);
+}
+
+static int
+drawstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, drawgen);
+}
+
+static Chan*
+drawopen(Chan *c, int omode)
+{
+ Client *cl;
+
+ if(c->qid.type & QTDIR)
+ return devopen(c, omode, 0, 0, drawgen);
+
+ qlock(&sdraw.q);
+ if(waserror()){
+ qunlock(&sdraw.q);
+ nexterror();
+ }
+
+ if(QID(c->qid) == Qnew){
+ cl = drawnewclient();
+ if(cl == 0)
+ error(Enodev);
+ c->qid.path = Qctl|((cl->slot+1)<<QSHIFT);
+ }
+
+ switch(QID(c->qid)){
+ case Qnew:
+ break;
+
+ case Qctl:
+ cl = drawclient(c);
+ if(cl->busy)
+ error(Einuse);
+ cl->busy = 1;
+ flushrect = Rect(10000, 10000, -10000, -10000);
+ drawinstall(cl, 0, screenimage, 0);
+ incref(&cl->r);
+ break;
+ case Qcolormap:
+ case Qdata:
+ case Qrefresh:
+ cl = drawclient(c);
+ incref(&cl->r);
+ break;
+ }
+ qunlock(&sdraw.q);
+ poperror();
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ c->iounit = IOUNIT;
+ return c;
+}
+
+static void
+drawclose(Chan *c)
+{
+ int i;
+ DImage *d, **dp;
+ Client *cl;
+ Refresh *r;
+
+ if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */
+ return;
+ qlock(&sdraw.q);
+ if(waserror()){
+ qunlock(&sdraw.q);
+ nexterror();
+ }
+
+ cl = drawclient(c);
+ if(QID(c->qid) == Qctl)
+ cl->busy = 0;
+ if((c->flag&COPEN) && (decref(&cl->r)==0)){
+ while(r = cl->refresh){ /* assign = */
+ cl->refresh = r->next;
+ free(r);
+ }
+ /* free names */
+ for(i=0; i<sdraw.nname; )
+ if(sdraw.name[i].client == cl)
+ drawdelname(sdraw.name+i);
+ else
+ i++;
+ while(cl->cscreen)
+ drawuninstallscreen(cl, cl->cscreen);
+ /* all screens are freed, so now we can free images */
+ dp = cl->dimage;
+ for(i=0; i<NHASH; i++){
+ while((d = *dp) != nil){
+ *dp = d->next;
+ drawfreedimage(d);
+ }
+ dp++;
+ }
+ sdraw.client[cl->slot] = 0;
+ drawflush(); /* to erase visible, now dead windows */
+ free(cl);
+ }
+ qunlock(&sdraw.q);
+ poperror();
+}
+
+long
+drawread(Chan *c, void *a, long n, vlong off)
+{
+ int index, m;
+ ulong red, green, blue;
+ Client *cl;
+ uchar *p;
+ Refresh *r;
+ DImage *di;
+ Memimage *i;
+ ulong offset = off;
+ char buf[16];
+
+ USED(offset);
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, drawgen);
+ cl = drawclient(c);
+ qlock(&sdraw.q);
+ if(waserror()){
+ qunlock(&sdraw.q);
+ nexterror();
+ }
+ switch(QID(c->qid)){
+ case Qctl:
+ if(n < 12*12)
+ error(Eshortread);
+ if(cl->infoid < 0)
+ error(Enodrawimage);
+ if(cl->infoid == 0){
+ i = screenimage;
+ if(i == nil)
+ error(Enodrawimage);
+ }else{
+ di = drawlookup(cl, cl->infoid, 1);
+ if(di == nil)
+ error(Enodrawimage);
+ i = di->image;
+ }
+ n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
+ cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
+ i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
+ i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
+ cl->infoid = -1;
+ break;
+
+ case Qcolormap:
+#ifdef COLORMAP
+ p = malloc(4*12*256+1);
+ if(p == 0)
+ error(Enomem);
+ m = 0;
+ for(index = 0; index < 256; index++){
+ getcolor(index, &red, &green, &blue);
+ m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24);
+ }
+ n = readstr(offset, a, n, (char*)p);
+ free(p);
+#else
+ n = 0;
+#endif
+ break;
+
+ case Qdata:
+ if(cl->readdata == nil)
+ error("no draw data");
+ if(n < cl->nreaddata)
+ error(Eshortread);
+ n = cl->nreaddata;
+ memmove(a, cl->readdata, cl->nreaddata);
+ free(cl->readdata);
+ cl->readdata = nil;
+ break;
+
+ case Qrefresh:
+ if(n < 5*4)
+ error(Ebadarg);
+ for(;;){
+ if(cl->refreshme || cl->refresh)
+ break;
+ qunlock(&sdraw.q);
+ if(waserror()){
+ qlock(&sdraw.q); /* restore lock for waserror() above */
+ nexterror();
+ }
+ Sleep(&cl->refrend, drawrefactive, cl);
+ poperror();
+ qlock(&sdraw.q);
+ }
+ p = a;
+ while(cl->refresh && n>=5*4){
+ r = cl->refresh;
+ BPLONG(p+0*4, r->dimage->id);
+ BPLONG(p+1*4, r->r.min.x);
+ BPLONG(p+2*4, r->r.min.y);
+ BPLONG(p+3*4, r->r.max.x);
+ BPLONG(p+4*4, r->r.max.y);
+ cl->refresh = r->next;
+ free(r);
+ p += 5*4;
+ n -= 5*4;
+ }
+ cl->refreshme = 0;
+ n = p-(uchar*)a;
+ }
+ qunlock(&sdraw.q);
+ poperror();
+ return n;
+}
+
+void
+drawwakeall(void)
+{
+ Client *cl;
+ int i;
+
+ for(i=0; i<sdraw.nclient; i++){
+ cl = sdraw.client[i];
+ if(cl && (cl->refreshme || cl->refresh))
+ Wakeup(&cl->refrend);
+ }
+}
+
+static long
+drawwrite(Chan *c, void *a, long n, vlong off)
+{
+ char buf[128], *fields[4], *q;
+ Client *cl;
+ int i, m, red, green, blue, x;
+ ulong offset = off;
+
+ USED(offset);
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+ cl = drawclient(c);
+ qlock(&sdraw.q);
+ if(waserror()){
+ drawwakeall();
+ qunlock(&sdraw.q);
+ nexterror();
+ }
+ switch(QID(c->qid)){
+ case Qctl:
+ if(n != 4)
+ error("unknown draw control request");
+ cl->infoid = BGLONG((uchar*)a);
+ break;
+
+ case Qcolormap:
+#ifdef COLORMAP
+ m = n;
+ n = 0;
+ while(m > 0){
+ x = m;
+ if(x > sizeof(buf)-1)
+ x = sizeof(buf)-1;
+ q = memccpy(buf, a, '\n', x);
+ if(q == 0)
+ break;
+ i = q-buf;
+ n += i;
+ a = (char*)a + i;
+ m -= i;
+ *q = 0;
+ if(tokenize(buf, fields, nelem(fields)) != 4)
+ error(Ebadarg);
+ i = strtoul(fields[0], 0, 0);
+ red = strtoul(fields[1], 0, 0);
+ green = strtoul(fields[2], 0, 0);
+ blue = strtoul(fields[3], &q, 0);
+ if(fields[3] == q)
+ error(Ebadarg);
+ if(red>255 || green>255 || blue>255 || i<0 || i>255)
+ error(Ebadarg);
+ red |= red<<8;
+ red |= red<<16;
+ green |= green<<8;
+ green |= green<<16;
+ blue |= blue<<8;
+ blue |= blue<<16;
+ setcolor(i, red, green, blue);
+ }
+#else
+ n = 0;
+#endif
+ break;
+
+ case Qdata:
+ drawmesg(cl, a, n);
+ drawwakeall();
+ break;
+
+ default:
+ error(Ebadusefd);
+ }
+ qunlock(&sdraw.q);
+ poperror();
+ return n;
+}
+
+uchar*
+drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
+{
+ int b, x;
+
+ if(p >= maxp)
+ error(Eshortdraw);
+ b = *p++;
+ x = b & 0x7F;
+ if(b & 0x80){
+ if(p+1 >= maxp)
+ error(Eshortdraw);
+ x |= *p++ << 7;
+ x |= *p++ << 15;
+ if(x & (1<<22))
+ x |= ~0<<23;
+ }else{
+ if(b & 0x40)
+ x |= ~0<<7;
+ x += oldx;
+ }
+ *newx = x;
+ return p;
+}
+
+static void
+printmesg(char *fmt, uchar *a, int plsprnt)
+{
+ char buf[256];
+ char *p, *q;
+ int s;
+
+ if(1|| plsprnt==0){
+ SET(s); SET(q); SET(p);
+ USED(fmt); USED(a); USED(buf); USED(p); USED(q); USED(s);
+ return;
+ }
+ q = buf;
+ *q++ = *a++;
+ for(p=fmt; *p; p++){
+ switch(*p){
+ case 'l':
+ q += sprint(q, " %ld", (long)BGLONG(a));
+ a += 4;
+ break;
+ case 'L':
+ q += sprint(q, " %.8lux", (ulong)BGLONG(a));
+ a += 4;
+ break;
+ case 'R':
+ q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
+ a += 16;
+ break;
+ case 'P':
+ q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
+ a += 8;
+ break;
+ case 'b':
+ q += sprint(q, " %d", *a++);
+ break;
+ case 's':
+ q += sprint(q, " %d", BGSHORT(a));
+ a += 2;
+ break;
+ case 'S':
+ q += sprint(q, " %.4ux", BGSHORT(a));
+ a += 2;
+ break;
+ }
+ }
+ *q++ = '\n';
+ *q = 0;
+ iprint("%.*s", (int)(q-buf), buf);
+}
+
+void
+drawmesg(Client *client, void *av, int n)
+{
+ int c, op, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, oesize, doflush;
+ uchar *u, *a, refresh;
+ char *fmt;
+ ulong value, chan;
+ Rectangle r, clipr;
+ Point p, q, *pp, sp;
+ Memimage *i, *dst, *src, *mask;
+ Memimage *l, **lp;
+ Memscreen *scrn;
+ DImage *font, *ll, *di, *ddst, *dsrc;
+ DName *dn;
+ DScreen *dscrn;
+ FChar *fc;
+ Refx *refx;
+ CScreen *cs;
+ Refreshfn reffn;
+
+ a = av;
+ m = 0;
+ fmt = nil;
+ if(waserror()){
+ if(fmt) printmesg(fmt, a, 1);
+ /* iprint("error: %s\n", up->env->errstr); */
+ nexterror();
+ }
+ while((n-=m) > 0){
+ USED(fmt);
+ a += m;
+ switch(*a){
+ default:
+ error("bad draw command");
+ /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */
+ case 'b':
+ printmesg(fmt="LLbLbRRL", a, 0);
+ m = 1+4+4+1+4+1+4*4+4*4+4;
+ if(n < m)
+ error(Eshortdraw);
+ dstid = BGLONG(a+1);
+ scrnid = BGSHORT(a+5);
+ refresh = a[9];
+ chan = BGLONG(a+10);
+ repl = a[14];
+ drawrectangle(&r, a+15);
+ drawrectangle(&clipr, a+31);
+ value = BGLONG(a+47);
+ if(drawlookup(client, dstid, 0))
+ error(Eimageexists);
+ if(scrnid){
+ dscrn = drawlookupscreen(client, scrnid, &cs);
+ scrn = dscrn->screen;
+ if(repl || chan!=scrn->image->chan)
+ error("image parameters incompatible with screen");
+ reffn = nil;
+ switch(refresh){
+ case Refbackup:
+ break;
+ case Refnone:
+ reffn = memlnorefresh;
+ break;
+ case Refmesg:
+ reffn = drawrefresh;
+ break;
+ default:
+ error("unknown refresh method");
+ }
+ l = memlalloc(scrn, r, reffn, 0, value);
+ if(l == 0)
+ error(Edrawmem);
+ dstflush(l->layer->screen->image, l->layer->screenr);
+ l->clipr = clipr;
+ rectclip(&l->clipr, r);
+ if(drawinstall(client, dstid, l, dscrn) == 0){
+ memldelete(l);
+ error(Edrawmem);
+ }
+ dscrn->ref++;
+ if(reffn){
+ refx = nil;
+ if(reffn == drawrefresh){
+ refx = malloc(sizeof(Refx));
+ if(refx == 0){
+ drawuninstall(client, dstid);
+ error(Edrawmem);
+ }
+ refx->client = client;
+ refx->dimage = drawlookup(client, dstid, 1);
+ }
+ memlsetrefresh(l, reffn, refx);
+ }
+ continue;
+ }
+ i = allocmemimage(r, chan);
+ if(i == 0)
+ error(Edrawmem);
+ if(repl)
+ i->flags |= Frepl;
+ i->clipr = clipr;
+ if(!repl)
+ rectclip(&i->clipr, r);
+ if(drawinstall(client, dstid, i, 0) == 0){
+ freememimage(i);
+ error(Edrawmem);
+ }
+ memfillcolor(i, value);
+ continue;
+
+ /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
+ case 'A':
+ printmesg(fmt="LLLb", a, 1);
+ m = 1+4+4+4+1;
+ if(n < m)
+ error(Eshortdraw);
+ dstid = BGLONG(a+1);
+ if(dstid == 0)
+ error(Ebadarg);
+ if(drawlookupdscreen(dstid))
+ error(Escreenexists);
+ ddst = drawlookup(client, BGLONG(a+5), 1);
+ dsrc = drawlookup(client, BGLONG(a+9), 1);
+ if(ddst==0 || dsrc==0)
+ error(Enodrawimage);
+ if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
+ error(Edrawmem);
+ continue;
+
+ /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
+ case 'c':
+ printmesg(fmt="LbR", a, 0);
+ m = 1+4+1+4*4;
+ if(n < m)
+ error(Eshortdraw);
+ ddst = drawlookup(client, BGLONG(a+1), 1);
+ if(ddst == nil)
+ error(Enodrawimage);
+ if(ddst->name)
+ error("cannot change repl/clipr of shared image");
+ dst = ddst->image;
+ if(a[5])
+ dst->flags |= Frepl;
+ drawrectangle(&dst->clipr, a+6);
+ continue;
+
+ /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
+ case 'd':
+ printmesg(fmt="LLLRPP", a, 0);
+ m = 1+4+4+4+4*4+2*4+2*4;
+ if(n < m)
+ error(Eshortdraw);
+ dst = drawimage(client, a+1);
+ src = drawimage(client, a+5);
+ mask = drawimage(client, a+9);
+ drawrectangle(&r, a+13);
+ drawpoint(&p, a+29);
+ drawpoint(&q, a+37);
+ op = drawclientop(client);
+ memdraw(dst, r, src, p, mask, q, op);
+ dstflush(dst, r);
+ continue;
+
+ /* toggle debugging: 'D' val[1] */
+ case 'D':
+ printmesg(fmt="b", a, 0);
+ m = 1+1;
+ if(n < m)
+ error(Eshortdraw);
+ drawdebug = a[1];
+ continue;
+
+ /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
+ case 'e':
+ case 'E':
+ printmesg(fmt="LLPlllPll", a, 0);
+ m = 1+4+4+2*4+4+4+4+2*4+2*4;
+ if(n < m)
+ error(Eshortdraw);
+ dst = drawimage(client, a+1);
+ src = drawimage(client, a+5);
+ drawpoint(&p, a+9);
+ e0 = BGLONG(a+17);
+ e1 = BGLONG(a+21);
+ if(e0<0 || e1<0)
+ error("invalid ellipse semidiameter");
+ j = BGLONG(a+25);
+ if(j < 0)
+ error("negative ellipse thickness");
+ drawpoint(&sp, a+29);
+ c = j;
+ if(*a == 'E')
+ c = -1;
+ ox = BGLONG(a+37);
+ oy = BGLONG(a+41);
+ op = drawclientop(client);
+ /* high bit indicates arc angles are present */
+ if(ox & (1<<31)){
+ if((ox & (1<<30)) == 0)
+ ox &= ~(1<<31);
+ memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
+ }else
+ memellipse(dst, p, e0, e1, c, src, sp, op);
+ dstflush(dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
+ continue;
+
+ /* free: 'f' id[4] */
+ case 'f':
+ printmesg(fmt="L", a, 1);
+ m = 1+4;
+ if(n < m)
+ error(Eshortdraw);
+ ll = drawlookup(client, BGLONG(a+1), 0);
+ if(ll && ll->dscreen && ll->dscreen->owner != client)
+ ll->dscreen->owner->refreshme = 1;
+ drawuninstall(client, BGLONG(a+1));
+ continue;
+
+ /* free screen: 'F' id[4] */
+ case 'F':
+ printmesg(fmt="L", a, 1);
+ m = 1+4;
+ if(n < m)
+ error(Eshortdraw);
+ drawlookupscreen(client, BGLONG(a+1), &cs);
+ drawuninstallscreen(client, cs);
+ continue;
+
+ /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
+ case 'i':
+ printmesg(fmt="Llb", a, 1);
+ m = 1+4+4+1;
+ if(n < m)
+ error(Eshortdraw);
+ dstid = BGLONG(a+1);
+ dst = drawimage(client, a+1);
+ if(dstid == 0 || dst == screenimage)
+ error("cannot use display as font");
+ font = drawlookup(client, dstid, 1);
+ if(font == 0)
+ error(Enodrawimage);
+ if(font->image->layer)
+ error("cannot use window as font");
+ ni = BGLONG(a+5);
+ if(ni<=0 || ni>4096)
+ error("bad font size (4096 chars max)");
+ free(font->fchar); /* should we complain if non-zero? */
+ font->fchar = malloc(ni*sizeof(FChar));
+ if(font->fchar == 0)
+ error(Enomem);
+ memset(font->fchar, 0, ni*sizeof(FChar));
+ font->nfchar = ni;
+ font->ascent = a[9];
+ continue;
+
+ /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
+ case 'l':
+ printmesg(fmt="LLSRPbb", a, 0);
+ m = 1+4+4+2+4*4+2*4+1+1;
+ if(n < m)
+ error(Eshortdraw);
+ font = drawlookup(client, BGLONG(a+1), 1);
+ if(font == 0)
+ error(Enodrawimage);
+ if(font->nfchar == 0)
+ error(Enotfont);
+ src = drawimage(client, a+5);
+ ci = BGSHORT(a+9);
+ if(ci >= font->nfchar)
+ error(Eindex);
+ drawrectangle(&r, a+11);
+ drawpoint(&p, a+27);
+ memdraw(font->image, r, src, p, memopaque, p, SoverD);
+ fc = &font->fchar[ci];
+ fc->minx = r.min.x;
+ fc->maxx = r.max.x;
+ fc->miny = r.min.y;
+ fc->maxy = r.max.y;
+ fc->left = a[35];
+ fc->width = a[36];
+ continue;
+
+ /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
+ case 'L':
+ printmesg(fmt="LPPlllLP", a, 0);
+ m = 1+4+2*4+2*4+4+4+4+4+2*4;
+ if(n < m)
+ error(Eshortdraw);
+ dst = drawimage(client, a+1);
+ drawpoint(&p, a+5);
+ drawpoint(&q, a+13);
+ e0 = BGLONG(a+21);
+ e1 = BGLONG(a+25);
+ j = BGLONG(a+29);
+ if(j < 0)
+ error("negative line width");
+ src = drawimage(client, a+33);
+ drawpoint(&sp, a+37);
+ op = drawclientop(client);
+ memline(dst, p, q, e0, e1, j, src, sp, op);
+ /* avoid memlinebbox if possible */
+ if(dst == screenimage || dst->layer!=nil){
+ /* BUG: this is terribly inefficient: update maximal containing rect*/
+ r = memlinebbox(p, q, e0, e1, j);
+ dstflush(dst, insetrect(r, -(1+1+j)));
+ }
+ continue;
+
+ /* create image mask: 'm' newid[4] id[4] */
+/*
+ *
+ case 'm':
+ printmesg("LL", a, 0);
+ m = 4+4;
+ if(n < m)
+ error(Eshortdraw);
+ break;
+ *
+ */
+
+ /* attach to a named image: 'n' dstid[4] j[1] name[j] */
+ case 'n':
+ printmesg(fmt="Lz", a, 0);
+ m = 1+4+1;
+ if(n < m)
+ error(Eshortdraw);
+ j = a[5];
+ if(j == 0) /* give me a non-empty name please */
+ error(Eshortdraw);
+ m += j;
+ if(n < m)
+ error(Eshortdraw);
+ dstid = BGLONG(a+1);
+ if(drawlookup(client, dstid, 0))
+ error(Eimageexists);
+ dn = drawlookupname(j, (char*)a+6);
+ if(dn == nil)
+ error(Enoname);
+ if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
+ error(Edrawmem);
+ di = drawlookup(client, dstid, 0);
+ if(di == 0)
+ error("draw: cannot happen");
+ di->vers = dn->vers;
+ di->name = smalloc(j+1);
+ di->fromname = dn->dimage;
+ di->fromname->ref++;
+ memmove(di->name, a+6, j);
+ di->name[j] = 0;
+ client->infoid = dstid;
+ continue;
+
+ /* name an image: 'N' dstid[4] in[1] j[1] name[j] */
+ case 'N':
+ printmesg(fmt="Lbz", a, 0);
+ m = 1+4+1+1;
+ if(n < m)
+ error(Eshortdraw);
+ c = a[5];
+ j = a[6];
+ if(j == 0) /* give me a non-empty name please */
+ error(Eshortdraw);
+ m += j;
+ if(n < m)
+ error(Eshortdraw);
+ di = drawlookup(client, BGLONG(a+1), 0);
+ if(di == 0)
+ error(Enodrawimage);
+ if(di->name)
+ error(Enamed);
+ if(c)
+ drawaddname(client, di, j, (char*)a+7);
+ else{
+ dn = drawlookupname(j, (char*)a+7);
+ if(dn == nil)
+ error(Enoname);
+ if(dn->dimage != di)
+ error(Ewrongname);
+ drawdelname(dn);
+ }
+ continue;
+
+ /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
+ case 'o':
+ printmesg(fmt="LPP", a, 0);
+ m = 1+4+2*4+2*4;
+ if(n < m)
+ error(Eshortdraw);
+ dst = drawimage(client, a+1);
+ if(dst->layer){
+ drawpoint(&p, a+5);
+ drawpoint(&q, a+13);
+ r = dst->layer->screenr;
+ ni = memlorigin(dst, p, q);
+ if(ni < 0)
+ error("image origin failed");
+ if(ni > 0){
+ dstflush(dst->layer->screen->image, r);
+ dstflush(dst->layer->screen->image, dst->layer->screenr);
+ ll = drawlookup(client, BGLONG(a+1), 1);
+ drawrefreshscreen(ll, client);
+ }
+ }
+ continue;
+
+ /* set compositing operator for next draw operation: 'O' op */
+ case 'O':
+ printmesg(fmt="b", a, 0);
+ m = 1+1;
+ if(n < m)
+ error(Eshortdraw);
+ client->op = *(a+1);
+ continue;
+
+ /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
+ /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
+ case 'p':
+ case 'P':
+ printmesg(fmt="LslllLPP", a, 0);
+ m = 1+4+2+4+4+4+4+2*4;
+ if(n < m)
+ error(Eshortdraw);
+ dst = drawimage(client, a+1);
+ ni = BGSHORT(a+5);
+ if(ni < 0)
+ error("negative count in polygon");
+ e0 = BGLONG(a+7);
+ e1 = BGLONG(a+11);
+ j = 0;
+ if(*a == 'p'){
+ j = BGLONG(a+15);
+ if(j < 0)
+ error("negative polygon line width");
+ }
+ src = drawimage(client, a+19);
+ drawpoint(&sp, a+23);
+ drawpoint(&p, a+31);
+ ni++;
+ pp = malloc(ni*sizeof(Point));
+ if(pp == nil)
+ error(Enomem);
+ doflush = 0;
+ if(dst == screenimage || (dst->layer && dst->layer->screen->image->data == screenimage->data))
+ doflush = 1; /* simplify test in loop */
+ ox = oy = 0;
+ esize = 0;
+ u = a+m;
+ for(y=0; y<ni; y++){
+ q = p;
+ oesize = esize;
+ u = drawcoord(u, a+n, ox, &p.x);
+ u = drawcoord(u, a+n, oy, &p.y);
+ ox = p.x;
+ oy = p.y;
+ if(doflush){
+ esize = j;
+ if(*a == 'p'){
+ if(y == 0){
+ c = memlineendsize(e0);
+ if(c > esize)
+ esize = c;
+ }
+ if(y == ni-1){
+ c = memlineendsize(e1);
+ if(c > esize)
+ esize = c;
+ }
+ }
+ if(*a=='P' && e0!=1 && e0 !=~0)
+ r = dst->clipr;
+ else if (y>0){
+ r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
+ combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
+ }
+ if (rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */
+ dstflush(dst, r);
+ }
+ pp[y] = p;
+ }
+ if (y == 1)
+ dstflush(dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
+ op = drawclientop(client);
+ if(*a == 'p')
+ mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
+ else
+ memfillpoly(dst, pp, ni, e0, src, sp, op);
+ free(pp);
+ m = u-a;
+ continue;
+
+ /* read: 'r' id[4] R[4*4] */
+ case 'r':
+ printmesg(fmt="LR", a, 0);
+ m = 1+4+4*4;
+ if(n < m)
+ error(Eshortdraw);
+ i = drawimage(client, a+1);
+ drawrectangle(&r, a+5);
+ if(!rectinrect(r, i->r))
+ error(Ereadoutside);
+ c = bytesperline(r, i->depth);
+ c *= Dy(r);
+ free(client->readdata);
+ client->readdata = mallocz(c, 0);
+ if(client->readdata == nil)
+ error("readimage malloc failed");
+ client->nreaddata = memunload(i, r, client->readdata, c);
+ if(client->nreaddata < 0){
+ free(client->readdata);
+ client->readdata = nil;
+ error("bad readimage call");
+ }
+ continue;
+
+ /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
+ /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
+ case 's':
+ case 'x':
+ printmesg(fmt="LLLPRPs", a, 0);
+ m = 1+4+4+4+2*4+4*4+2*4+2;
+ if(*a == 'x')
+ m += 4+2*4;
+ if(n < m)
+ error(Eshortdraw);
+
+ dst = drawimage(client, a+1);
+ src = drawimage(client, a+5);
+ font = drawlookup(client, BGLONG(a+9), 1);
+ if(font == 0)
+ error(Enodrawimage);
+ if(font->nfchar == 0)
+ error(Enotfont);
+ drawpoint(&p, a+13);
+ drawrectangle(&r, a+21);
+ drawpoint(&sp, a+37);
+ ni = BGSHORT(a+45);
+ u = a+m;
+ m += ni*2;
+ if(n < m)
+ error(Eshortdraw);
+ clipr = dst->clipr;
+ dst->clipr = r;
+ op = drawclientop(client);
+ if(*a == 'x'){
+ /* paint background */
+ l = drawimage(client, a+47);
+ drawpoint(&q, a+51);
+ r.min.x = p.x;
+ r.min.y = p.y-font->ascent;
+ r.max.x = p.x;
+ r.max.y = r.min.y+Dy(font->image->r);
+ j = ni;
+ while(--j >= 0){
+ ci = BGSHORT(u);
+ if(ci<0 || ci>=font->nfchar){
+ dst->clipr = clipr;
+ error(Eindex);
+ }
+ r.max.x += font->fchar[ci].width;
+ u += 2;
+ }
+ memdraw(dst, r, l, q, memopaque, ZP, op);
+ u -= 2*ni;
+ }
+ q = p;
+ while(--ni >= 0){
+ ci = BGSHORT(u);
+ if(ci<0 || ci>=font->nfchar){
+ dst->clipr = clipr;
+ error(Eindex);
+ }
+ q = drawchar(dst, q, src, &sp, font, ci, op);
+ u += 2;
+ }
+ dst->clipr = clipr;
+ p.y -= font->ascent;
+ dstflush(dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
+ continue;
+
+ /* use public screen: 'S' id[4] chan[4] */
+ case 'S':
+ printmesg(fmt="Ll", a, 0);
+ m = 1+4+4;
+ if(n < m)
+ error(Eshortdraw);
+ dstid = BGLONG(a+1);
+ if(dstid == 0)
+ error(Ebadarg);
+ dscrn = drawlookupdscreen(dstid);
+ if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
+ error(Enodrawscreen);
+ if(dscrn->screen->image->chan != BGLONG(a+5))
+ error("inconsistent chan");
+ if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
+ error(Edrawmem);
+ continue;
+
+ /* top or bottom windows: 't' top[1] nw[2] n*id[4] */
+ case 't':
+ printmesg(fmt="bsL", a, 0);
+ m = 1+1+2;
+ if(n < m)
+ error(Eshortdraw);
+ nw = BGSHORT(a+2);
+ if(nw < 0)
+ error(Ebadarg);
+ if(nw == 0)
+ continue;
+ m += nw*4;
+ if(n < m)
+ error(Eshortdraw);
+ lp = malloc(nw*sizeof(Memimage*));
+ if(lp == 0)
+ error(Enomem);
+ if(waserror()){
+ free(lp);
+ nexterror();
+ }
+ for(j=0; j<nw; j++)
+ lp[j] = drawimage(client, a+1+1+2+j*4);
+ if(lp[0]->layer == 0)
+ error("images are not windows");
+ for(j=1; j<nw; j++)
+ if(lp[j]->layer->screen != lp[0]->layer->screen)
+ error("images not on same screen");
+ if(a[1])
+ memltofrontn(lp, nw);
+ else
+ memltorearn(lp, nw);
+ if(lp[0]->layer->screen->image->data == screenimage->data)
+ for(j=0; j<nw; j++)
+ dstflush(lp[j]->layer->screen->image, lp[j]->layer->screenr);
+ ll = drawlookup(client, BGLONG(a+1+1+2), 1);
+ drawrefreshscreen(ll, client);
+ poperror();
+ free(lp);
+ continue;
+
+ /* visible: 'v' */
+ case 'v':
+ printmesg(fmt="", a, 0);
+ m = 1;
+ drawflush();
+ continue;
+
+ /* write: 'y' id[4] R[4*4] data[x*1] */
+ /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
+ case 'y':
+ case 'Y':
+ printmesg(fmt="LR", a, 0);
+ // iprint("load %c\n", *a);
+ m = 1+4+4*4;
+ if(n < m)
+ error(Eshortdraw);
+ dst = drawimage(client, a+1);
+ drawrectangle(&r, a+5);
+ if(!rectinrect(r, dst->r))
+ error(Ewriteoutside);
+ y = memload(dst, r, a+m, n-m, *a=='Y');
+ if(y < 0)
+ error("bad writeimage call");
+ dstflush(dst, r);
+ m += y;
+ continue;
+ }
+ }
+ poperror();
+}
+
+int
+drawlsetrefresh(ulong qidpath, int id, void *reffn, void *refx)
+{
+ DImage *d;
+ Memimage *i;
+ Client *client;
+
+ client = drawclientofpath(qidpath);
+ if(client == 0)
+ return 0;
+ d = drawlookup(client, id, 0);
+ if(d == nil)
+ return 0;
+ i = d->image;
+ if(i->layer == nil)
+ return 0;
+ return memlsetrefresh(i, reffn, refx);
+}
+
+void
+drawqlock(void)
+{
+ qlock(&sdraw.q);
+}
+
+void
+drawqunlock(void)
+{
+ qunlock(&sdraw.q);
+}
+
+void
+interf(void)
+{
+ /* force it to load */
+ drawreplxy(0, 0, 0);
+}
+
+Dev drawdevtab = {
+ 'i',
+ "draw",
+
+ devinit,
+ drawattach,
+ drawwalk,
+ drawstat,
+ drawopen,
+ devcreate,
+ drawclose,
+ drawread,
+ devbread,
+ drawwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/emu/port/devdup.c
@@ -1,0 +1,150 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+
+/* Qid is (2*fd + (file is ctl))+1 */
+
+static int
+dupgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+ Fgrp *fgrp = up->env->fgrp;
+ Chan *f;
+ static int perm[] = { 0400, 0200, 0600, 0 };
+ int p;
+ Qid q;
+
+ USED(name); USED(tab); USED(ntab);
+ if(s == DEVDOTDOT){
+ devdir(c, c->qid, ".", 0, eve, DMDIR|0555, dp);
+ return 1;
+ }
+ if(s == 0)
+ return 0;
+ s--;
+ if(s/2 > fgrp->maxfd)
+ return -1;
+ if((f=fgrp->fd[s/2]) == nil)
+ return 0;
+ if(s & 1){
+ p = 0400;
+ sprint(up->genbuf, "%dctl", s/2);
+ }else{
+ p = perm[f->mode&3];
+ sprint(up->genbuf, "%d", s/2);
+ }
+ mkqid(&q, s+1, 0, QTFILE);
+ devdir(c, q, up->genbuf, 0, eve, p, dp);
+ return 1;
+}
+
+static Chan*
+dupattach(char *spec)
+{
+ return devattach('d', spec);
+}
+
+static Walkqid*
+dupwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, nil, 0, dupgen);
+}
+
+static int
+dupstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, nil, 0, dupgen);
+}
+
+static Chan*
+dupopen(Chan *c, int omode)
+{
+ Chan *f;
+ int fd, twicefd;
+
+ if(c->qid.type & QTDIR){
+ if(omode != 0)
+ error(Eisdir);
+ c->mode = 0;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+ if(c->qid.type & QTAUTH)
+ error(Eperm);
+ twicefd = c->qid.path - 1;
+ fd = twicefd/2;
+ if((twicefd & 1)){
+ /* ctl file */
+ f = c;
+ f->mode = openmode(omode);
+ f->flag |= COPEN;
+ f->offset = 0;
+ }else{
+ /* fd file */
+ f = fdtochan(up->env->fgrp, fd, openmode(omode), 0, 1);
+ cclose(c);
+ }
+ if(omode & OCEXEC)
+ f->flag |= CCEXEC;
+ return f;
+}
+
+static void
+dupclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+dupread(Chan *c, void *va, long n, vlong offset)
+{
+ char *a = va;
+ char buf[256];
+ int fd, twicefd;
+
+ if(c->qid.type == QTDIR)
+ return devdirread(c, a, n, nil, 0, dupgen);
+ twicefd = c->qid.path - 1;
+ fd = twicefd/2;
+ if(twicefd & 1){
+ c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+ if(waserror()){
+ cclose(c);
+ nexterror();
+ }
+ progfdprint(c, fd, 0, buf, sizeof buf);
+ poperror();
+ cclose(c);
+ return readstr((ulong)offset, va, n, buf);
+ }
+ panic("dupread");
+ return 0;
+}
+
+static long
+dupwrite(Chan *c, void *a, long n, vlong o)
+{
+ USED(c); USED(a); USED(n); USED(o);
+ panic("dupwrite");
+ return 0; /* not reached */
+}
+
+Dev dupdevtab = {
+ 'd',
+ "dup",
+
+ devinit,
+ dupattach,
+ dupwalk,
+ dupstat,
+ dupopen,
+ devcreate,
+ dupclose,
+ dupread,
+ devbread,
+ dupwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/emu/port/devdynld.c
@@ -1,0 +1,357 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <a.out.h>
+#include <dynld.h>
+
+#define DBG if(1) print
+
+extern ulong ndevs;
+
+enum
+{
+ Qdir,
+ Qdynld,
+ Qdynsyms,
+
+ DEVCHAR = 'L',
+};
+
+static Dirtab dltab[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "dynld", {Qdynld}, 0, 0644,
+ "dynsyms", {Qdynsyms}, 0, 0444,
+};
+
+enum
+{
+ DLdev,
+ DLudev,
+};
+
+static Cmdtab dlcmd[] =
+{
+ DLdev, "dev", 2,
+ DLudev, "udev", 2,
+};
+
+typedef struct Dyndev Dyndev;
+
+struct Dyndev
+{
+ char *path;
+ Dynobj *o;
+ Dev *dev;
+ Dyndev *next;
+};
+
+static Dyndev *loaded;
+static QLock dllock;
+
+typedef struct Fd Fd;
+struct Fd {
+ int fd;
+};
+
+static long
+readfd(void *a, void *buf, long nbytes)
+{
+ return kread(((Fd*)a)->fd, buf, nbytes);
+}
+
+static vlong
+seekfd(void *a, vlong off, int t)
+{
+ return kseek(((Fd*)a)->fd, off, t);
+}
+
+static void
+errfd(char *s)
+{
+ kstrcpy(up->env->errstr, s, ERRMAX);
+}
+
+static void
+dlfree(Dyndev *l)
+{
+ if(l != nil){
+ free(l->path);
+ dynobjfree(l->o);
+ free(l);
+ }
+}
+
+static Dyndev*
+dlload(char *path, Dynsym *tab, int ntab)
+{
+ Fd f;
+ Dyndev *l;
+
+ f.fd = kopen(path, OREAD);
+ if(f.fd < 0)
+ error("cannot open");
+ if(waserror()){
+ kclose(f.fd);
+ nexterror();
+ }
+ l = mallocz(sizeof(Dyndev), 1);
+ if(l == nil)
+ error(Enomem);
+ if(waserror()){
+ dlfree(l);
+ nexterror();
+ }
+ l->path = strdup(path);
+ if(l->path == nil)
+ error(Enomem);
+ l->o = dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0);
+ if(l->o == nil)
+ error(up->env->errstr);
+ poperror();
+ poperror();
+ kclose(f.fd);
+ return l;
+}
+
+static void
+devload(char *path)
+{
+ int i;
+ Dyndev *l;
+ Dev *dev;
+ char devname[32];
+
+ l = dlload(path, _exporttab, dyntabsize(_exporttab));
+ if(waserror()){
+ dlfree(l);
+ nexterror();
+ }
+ snprint(devname, sizeof(devname), "%sdevtab", "XXX"); /* TO DO */
+ dev = dynimport(l->o, devname, signof(*dev));
+ if(dev == nil)
+ error("no devtab");
+ if(devno(dev->dc, 1) >= 0)
+ error("device loaded");
+ for(i = 0; devtab[i] != nil; i++)
+ ;
+ if(i >= ndevs || devtab[i+1] != nil)
+ error("device table full");
+ l->dev = devtab[i] = dev;
+ dev->init();
+ l->next = loaded;
+ loaded = l;
+ poperror();
+}
+
+static void
+devunload(char *path)
+{
+ int i, dc;
+ Dyndev *l, **ll;
+
+ dc = 0;
+ if(strlen(path) == 1)
+ dc = path[0];
+ for(ll = &loaded; *ll != nil; ll = &(*ll)->next){
+ if(path != nil && strcmp(path, (*ll)->path) == 0)
+ break;
+ if(dc != 0 && (*ll)->dev && dc == (*ll)->dev->dc)
+ break;
+ }
+ if((l = *ll) != nil){
+ for(i = 0; i < ndevs; i++)
+ if(l->dev == devtab[i]){
+ devtab[i] = nil;
+ break;
+ }
+/*
+ if(l->dev)
+ l->dev->shutdown();
+*/
+ *ll = l->next;
+ dlfree(l);
+ }
+}
+
+static long
+readdl(void *a, ulong n, ulong offset)
+{
+ char *p;
+ Dyndev *l;
+ int m, len;
+
+ m = 0;
+ for(l = loaded; l != nil; l = l->next)
+ m++;
+ m *= 48;
+ p = malloc(m);
+ if(p == nil)
+ error(Enomem);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ *p = 0;
+ len = 0;
+ for(l = loaded; l != nil; l = l->next)
+ if(l->dev)
+ len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lux\t%s\n",
+ l->dev->dc, l->o->base, l->o->size, l->dev->name);
+ n = readstr(offset, a, n, p);
+ poperror();
+ free(p);
+ return n;
+}
+
+static long
+readsyms(char *a, ulong n, ulong offset)
+{
+ char *p;
+ Dynsym *t;
+ long l, nr;
+
+ p = malloc(READSTR);
+ if(p == nil)
+ error(Enomem);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ nr = 0;
+ for(t = _exporttab; n > 0 && t->name != nil; t++){
+ l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name);
+ if(offset >= l){
+ offset -= l;
+ continue;
+ }
+ l = readstr(offset, a, n, p);
+ offset = 0;
+ n -= l;
+ a += l;
+ nr += l;
+ }
+ poperror();
+ free(p);
+ return nr;
+}
+
+static Chan*
+dlattach(char *spec)
+{
+ return devattach(DEVCHAR, spec);
+}
+
+static Walkqid*
+dlwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen);
+}
+
+static int
+dlstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, dltab, nelem(dltab), devgen);
+}
+
+static Chan*
+dlopen(Chan *c, int omode)
+{
+ return devopen(c, omode, dltab, nelem(dltab), devgen);
+}
+
+static void
+dlclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+dlread(Chan *c, void *a, long n, vlong voffset)
+{
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, dltab, nelem(dltab), devgen);
+ case Qdynld:
+ return readdl(a, n, (ulong)voffset);
+ case Qdynsyms:
+ return readsyms(a, n, (ulong)voffset);
+ default:
+ error(Egreg);
+ }
+ return n;
+}
+
+static long
+dlwrite(Chan *c, void *a, long n, vlong voffset)
+{
+ Cmdbuf *cmd;
+ Cmdtab *ct;
+
+ USED(voffset);
+ switch((ulong)c->qid.path){
+ case Qdynld:
+ cmd = parsecmd(a, n);
+ qlock(&dllock);
+ if(waserror()){
+ qunlock(&dllock);
+ free(cmd);
+ nexterror();
+ }
+ ct = lookupcmd(cmd, dlcmd, nelem(dlcmd));
+ switch(ct->index){
+ case DLdev:
+ devload(cmd->f[1]);
+ break;
+ case DLudev:
+ devunload(cmd->f[1]);
+ break;
+ }
+ poperror();
+ qunlock(&dllock);
+ free(cmd);
+ break;
+ default:
+ error(Egreg);
+ }
+ return n;
+}
+
+Dev dynlddevtab = {
+ DEVCHAR,
+ "dynld",
+
+ devinit,
+ dlattach,
+ dlwalk,
+ dlstat,
+ dlopen,
+ devcreate,
+ dlclose,
+ dlread,
+ devbread,
+ dlwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+/* auxiliary routines for dynamic loading of C modules */
+
+Dynobj*
+dynld(int fd)
+{
+ Fd f;
+
+ f.fd = fd;
+ return dynloadgen(&f, readfd, seekfd, errfd, _exporttab, dyntabsize(_exporttab), 0);
+}
+
+int
+dynldable(int fd)
+{
+ Fd f;
+
+ f.fd = fd;
+ return dynloadable(&f, readfd, seekfd);
+}
--- /dev/null
+++ b/emu/port/devdynldx.c
@@ -1,0 +1,357 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <a.out.h>
+#include <dynld.h>
+
+/*
+ * TO DO
+ * - dynamic allocation of Dev.dc
+ * - inter-module dependencies
+ * - reference count on Dev to allow error("inuse") [or how else to do it]
+ * - is Dev.shutdown the right function to call? Dev.config perhaps?
+ */
+
+#define DBG if(1) print
+
+extern ulong ndevs;
+
+enum
+{
+ Qdir,
+ Qdynld,
+ Qdynsyms,
+
+ DEVCHAR = 'L',
+};
+
+static Dirtab dltab[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "dynld", {Qdynld}, 0, 0644,
+ "dynsyms", {Qdynsyms}, 0, 0444,
+};
+
+typedef struct Dyndev Dyndev;
+
+struct Dyndev
+{
+ char* name; /* device name (eg, "dynld") */
+ char* tag; /* version tag (eg, MD5 or SHA1 hash of content) */
+ char* path; /* file from whence it came */
+ Dynobj* o;
+ Dev* dev;
+ Dyndev* next;
+};
+
+static Dyndev *loaded;
+static QLock dllock;
+
+static Dyndev** finddyndev(char*);
+static int matched(Dyndev*, char*, char*);
+
+extern Dynobj* kdynloadfd(int, Dynsym*, int, ulong);
+
+static void
+dlfree(Dyndev *l)
+{
+ if(l != nil){
+ free(l->tag);
+ free(l->name);
+ free(l->path);
+ dynobjfree(l->o);
+ free(l);
+ }
+}
+
+static Dyndev*
+dlload(char *path, Dynsym *tab, int ntab)
+{
+ Dyndev *l;
+ int fd;
+
+ /* in Plan 9, would probably use Chan* interface here */
+ fd = kopen(path, OREAD);
+ if(fd < 0)
+ error("cannot open");
+ if(waserror()){
+ kclose(fd);
+ nexterror();
+ }
+ l = mallocz(sizeof(Dyndev), 1);
+ if(l == nil)
+ error(Enomem);
+ if(waserror()){
+ dlfree(l);
+ nexterror();
+ }
+ l->path = strdup(path);
+ if(l->path == nil)
+ error(Enomem);
+ l->o = kdynloadfd(fd, tab, ntab, 0);
+ if(l->o == nil)
+ error(up->env->errstr);
+ poperror();
+ poperror();
+ kclose(fd);
+ return l;
+}
+
+static void
+devload(char *name, char *path, char *tag)
+{
+ int i;
+ Dyndev *l, **lp;
+ Dev *dev;
+ char tabname[32];
+
+ lp = finddyndev(name);
+ if(*lp != nil)
+ error("already loaded"); /* i'm assuming the name (eg, "cons") is to be unique */
+ l = dlload(path, _exporttab, dyntabsize(_exporttab));
+ if(waserror()){
+ dlfree(l);
+ nexterror();
+ }
+ snprint(tabname, sizeof(tabname), "%sdevtab", name);
+ dev = dynimport(l->o, tabname, signof(*dev));
+ if(dev == nil)
+ errorf("can't find %sdevtab in module", name);
+ kstrdup(&l->name, name);
+ kstrdup(&l->tag, tag != nil? tag: "");
+ if(dev->name == nil)
+ dev->name = l->name;
+ else if(strcmp(dev->name, l->name) != 0)
+ errorf("module file has device %s", dev->name);
+ /* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */
+ if(devno(dev->dc, 1) >= 0)
+ errorf("devchar %C already used", dev->dc);
+ for(i = 0; devtab[i] != nil; i++)
+ ;
+ if(i >= ndevs || devtab[i+1] != nil)
+ error("device table full");
+#ifdef NATIVE
+ i = splhi();
+ dev->reset();
+ splx(i);
+#endif
+ dev->init();
+ l->dev = devtab[i] = dev;
+ l->next = loaded;
+ loaded = l; /* recently loaded ones first: good unload order? */
+ poperror();
+}
+
+static Dyndev**
+finddyndev(char *name)
+{
+ Dyndev *l, **lp;
+
+ for(lp = &loaded; (l = *lp) != nil; lp = &l->next)
+ if(strcmp(l->name, name) == 0)
+ break;
+ return lp;
+}
+
+static int
+matched(Dyndev *l, char *path, char *tag)
+{
+ if(path != nil && strcmp(l->path, path) != 0)
+ return 0;
+ if(tag != nil && strcmp(l->tag, tag) != 0)
+ return 0;
+ return 1;
+}
+
+static void
+devunload(char *name, char *path, char *tag)
+{
+ int i;
+ Dyndev *l, **lp;
+
+ lp = finddyndev(name);
+ l = *lp;
+ if(l == nil)
+ error("not loaded");
+ if(!matched(l, path, tag))
+ error("path/tag mismatch");
+ for(i = 0; i < ndevs; i++)
+ if(l->dev == devtab[i]){
+ devtab[i] = nil; /* TO DO: ensure driver is not currently in use */
+ break;
+ }
+#ifdef NATIVE
+ l->dev->shutdown();
+#endif
+ *lp = l->next;
+ dlfree(l);
+}
+
+static long
+readdynld(void *a, ulong n, ulong offset)
+{
+ char *p;
+ Dyndev *l;
+ int m, len;
+
+ m = 0;
+ for(l = loaded; l != nil; l = l->next)
+ m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag);
+ p = malloc(m);
+ if(p == nil)
+ error(Enomem);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ *p = 0;
+ len = 0;
+ for(l = loaded; l != nil; l = l->next)
+ if(l->dev)
+ len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n",
+ l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag);
+ n = readstr(offset, a, n, p);
+ poperror();
+ free(p);
+ return n;
+}
+
+static long
+readsyms(char *a, ulong n, ulong offset)
+{
+ char *p;
+ Dynsym *t;
+ long l, nr;
+
+ p = malloc(READSTR);
+ if(p == nil)
+ error(Enomem);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ nr = 0;
+ for(t = _exporttab; n > 0 && t->name != nil; t++){
+ l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name);
+ if(offset >= l){
+ offset -= l;
+ continue;
+ }
+ l = readstr(offset, a, n, p);
+ offset = 0;
+ n -= l;
+ a += l;
+ nr += l;
+ }
+ poperror();
+ free(p);
+ return nr;
+}
+
+static Chan*
+dlattach(char *spec)
+{
+ return devattach(DEVCHAR, spec);
+}
+
+static Walkqid*
+dlwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen);
+}
+
+static int
+dlstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, dltab, nelem(dltab), devgen);
+}
+
+static Chan*
+dlopen(Chan *c, int omode)
+{
+ return devopen(c, omode, dltab, nelem(dltab), devgen);
+}
+
+static void
+dlclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+dlread(Chan *c, void *a, long n, vlong voffset)
+{
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, dltab, nelem(dltab), devgen);
+ case Qdynld:
+ return readdynld(a, n, voffset);
+ case Qdynsyms:
+ return readsyms(a, n, voffset);
+ default:
+ error(Egreg);
+ }
+ return n;
+}
+
+static long
+dlwrite(Chan *c, void *a, long n, vlong voffset)
+{
+ Cmdbuf *cb;
+ char *name, *tag, *path;
+
+ USED(voffset);
+ switch((ulong)c->qid.path){
+ case Qdynld:
+ cb = parsecmd(a, n);
+ qlock(&dllock);
+ if(waserror()){
+ qunlock(&dllock);
+ free(cb);
+ nexterror();
+ }
+ if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0) /* only do devices */
+ cmderror(cb, Ebadctl);
+ name = cb->f[2];
+ path = nil;
+ if(cb->nf > 3)
+ path = cb->f[3];
+ tag = nil;
+ if(cb->nf > 4)
+ tag = cb->f[4];
+ if(strcmp(cb->f[0], "load") == 0){
+ if(path == nil)
+ cmderror(cb, "missing load path");
+ devload(name, path, tag); /* TO DO: remaining parameters might be dependencies? */
+ }else if(strcmp(cb->f[0], "unload") == 0)
+ devunload(name, path, tag);
+ else
+ cmderror(cb, Ebadctl);
+ poperror();
+ qunlock(&dllock);
+ free(cb);
+ break;
+ default:
+ error(Egreg);
+ }
+ return n;
+}
+
+Dev dynlddevtab = {
+ DEVCHAR,
+ "dynld",
+
+ devinit,
+ dlattach,
+ dlwalk,
+ dlstat,
+ dlopen,
+ devcreate,
+ dlclose,
+ dlread,
+ devbread,
+ dlwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/emu/port/deveia-bsd.c
@@ -1,0 +1,95 @@
+/*
+ * BSD serial port control features not found in POSIX
+ * including modem line control and hardware flow control
+ */
+
+static struct flagmap lines[] = {
+ {"cts", TIOCM_CTS},
+ {"dsr", TIOCM_DSR},
+ {"ring", TIOCM_RI},
+ {"dcd", TIOCM_CD},
+ {"dtr", TIOCM_DTR},
+ {"rts", TIOCM_RTS},
+ {0, -1}
+};
+
+static void
+resxtra(int port, struct termios *ts)
+{
+ int fd = eia[port].fd;
+
+ USED(ts);
+
+ if(eia[port].dtr)
+ ioctl(fd, TIOCM_DTR, eia[port].dtr);
+ if(eia[port].rts)
+ ioctl(fd, TIOCM_RTS, eia[port].rts);
+ if(eia[port].cts)
+ ioctl(fd, TIOCM_CTS, eia[port].cts);
+}
+
+static char *
+rdxtra(int port, struct termios *ts, char *str)
+{
+ int fd = eia[port].fd;
+ int line;
+// struct flagmap *lp;
+ char *s = str;
+
+ USED(ts);
+
+ if(ioctl(fd, TIOCMGET, &line) < 0)
+ oserror();
+
+// for(lp = lines; lp->str; lp++)
+// if(line&lp->flag)
+// s += sprint(s, " %s", lp->str);
+
+ return s;
+}
+
+static char *
+wrxtra(int port, struct termios *ts, char *cmd)
+{
+ int fd = eia[port].fd;
+ int n, r, flag, iocmd, *l;
+
+ USED(ts);
+
+ switch(*cmd) {
+ case 'D':
+ case 'd':
+ flag = TIOCM_DTR;
+ l = &eia[port].dtr;
+ break;
+ case 'R':
+ case 'r':
+ flag = TIOCM_RTS;
+ l = &eia[port].rts;
+ break;
+ case 'M':
+ case 'm':
+ flag = TIOCM_CTS;
+ l = &eia[port].cts;
+ break;
+ default:
+ return nil;
+ }
+
+ n = atoi(cmd+1);
+ if(n)
+ iocmd = TIOCMBIS;
+ else
+ iocmd = TIOCMBIC;
+
+ osenter();
+ r = ioctl(fd, iocmd, &flag);
+ osleave();
+ if(r < 0)
+ oserror();
+
+ eia[port].restore = 1;
+ *l = iocmd;
+
+ return nil;
+}
--- /dev/null
+++ b/emu/port/deveia-posix.c
@@ -1,0 +1,463 @@
+/*
+ * Driver for POSIX serial ports
+ */
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef _POSIX_C_SOURCE /* for deveia-bsd.c */
+#include <sys/stat.h>
+#include <termios.h>
+
+enum
+{
+ Devchar = 't',
+
+ Ndataqid = 1,
+ Nctlqid,
+ Nstatqid,
+ Nqid = 3, /* number of QIDs */
+
+ CTLS= 023,
+ CTLQ= 021,
+
+ Maxctl = 128,
+ Maxfield = 32
+};
+
+/*
+ * Macros to manage QIDs
+ */
+#define NETTYPE(x) ((x)&0x0F)
+#define NETID(x) ((x)>>4)
+#define NETQID(i,t) (((i)<<4)|(t))
+
+static Dirtab *eiadir;
+static int ndir;
+
+static char Devname[] = "eia";
+
+typedef struct Eia Eia;
+struct Eia {
+ Ref r;
+ int fd;
+ int overrun;
+ int frame;
+ int restore; /* flag to restore prev. states */
+ struct termios ts;
+ int dtr;
+ int rts;
+ int cts;
+};
+
+static Eia *eia;
+
+struct tcdef_t {
+ int val;
+ tcflag_t flag;
+};
+
+struct flagmap {
+ char* s;
+ tcflag_t flag;
+};
+
+static struct tcdef_t bps[];
+
+static struct tcdef_t size[] = {
+ {5, CS5},
+ {6, CS6},
+ {7, CS7},
+ {8, CS8},
+ {-1, -1}
+};
+
+static char *
+ftos(char *buf, struct tcdef_t *tbl, tcflag_t flag)
+{
+ for(; tbl->val >= 0; tbl++)
+ if(tbl->flag == flag){
+ sprint(buf, "%d", tbl->val);
+ return buf;
+ }
+ return "unknown";
+}
+
+static tcflag_t
+stof(struct tcdef_t *tbl, int val)
+{
+ for(; tbl->val >= 0 && tbl->val != val; tbl++)
+ {}
+ return tbl->flag;
+}
+
+static char *
+rdxtra(int port, struct termios *ts, char *str); /* non-POSIX extensions */
+
+static long
+rdstat(int port, void *buf, long n, ulong offset)
+{
+ int fd = eia[port].fd;
+ struct termios ts;
+ char str[Maxctl];
+ char sbuf[20];
+ char *s;
+
+ if(tcgetattr(fd, &ts) < 0)
+ oserror();
+
+ s = str;
+ s += sprint(s, "opens %d ferr %d oerr %d baud %s",
+ eia[port].r.ref-1, eia[port].frame, eia[port].overrun,
+ ftos(sbuf, bps, (tcflag_t)cfgetospeed(&ts)));
+ s = rdxtra(port, &ts, s);
+ sprint(s, "\n");
+
+ return readstr(offset, buf, n, str);
+}
+
+static char *
+wrxtra(int port, struct termios *ts, char *cmd); /* non-POSIX extensions */
+
+static void
+wrctl(int port, char *cmd)
+{
+ struct termios ts;
+ char *xerr;
+ int r, nf, n, i;
+ char *f[Maxfield];
+ int fd = eia[port].fd;
+ tcflag_t flag;
+
+ if(tcgetattr(fd, &ts) < 0) {
+Error:
+ oserror();
+ }
+
+ nf = tokenize(cmd, f, nelem(f));
+ for(i = 0; i < nf; i++){
+ if(strncmp(f[i], "break", 5) == 0){
+ tcsendbreak(fd, 0);
+ continue;
+ }
+ n = atoi(f[i]+1);
+ switch(*f[i]) {
+ case 'F':
+ case 'f':
+ if(tcflush(fd, TCOFLUSH) < 0)
+ goto Error;
+ break;
+ case 'K':
+ case 'k':
+ if(tcsendbreak(fd, 0) < 0)
+ ; /* ignore it */
+ break;
+ case 'H':
+ case 'h':
+ cfsetospeed(&ts, B0);
+ break;
+ case 'B':
+ case 'b':
+ flag = stof(bps, n);
+ if((int)flag == -1)
+ error(Ebadarg);
+ cfsetispeed(&ts, (speed_t)flag);
+ cfsetospeed(&ts, (speed_t)flag);
+ break;
+ case 'L':
+ case 'l':
+ flag = stof(size, n);
+ if((int)flag == -1)
+ error(Ebadarg);
+ ts.c_cflag &= ~CSIZE;
+ ts.c_cflag |= flag;
+ break;
+ case 'S':
+ case 's':
+ if(n == 1)
+ ts.c_cflag &= ~CSTOPB;
+ else if(n ==2)
+ ts.c_cflag |= CSTOPB;
+ else
+ error(Ebadarg);
+ break;
+ case 'P':
+ case 'p':
+ if(*(f[i]+1) == 'o')
+ ts.c_cflag |= PARENB|PARODD;
+ else if(*(f[i]+1) == 'e') {
+ ts.c_cflag |= PARENB;
+ ts.c_cflag &= ~PARODD;
+ }
+ else
+ ts.c_cflag &= ~PARENB;
+ break;
+ case 'X':
+ case 'x':
+ if(n == 0)
+ ts.c_iflag &= ~(IXON|IXOFF);
+ else
+ ts.c_iflag |= (IXON|IXOFF);
+ break;
+ case 'i':
+ case 'I':
+ /* enable fifo; ignore */
+ break;
+ default:
+ if((xerr = wrxtra(port, &ts, f[i])) != nil)
+ error(xerr);
+ }
+ }
+
+ osenter();
+ r = tcsetattr(fd, TCSADRAIN, &ts);
+ osleave();
+ if(r < 0)
+ goto Error;
+ eia[port].restore = 1;
+ eia[port].ts = ts;
+}
+
+static void
+eiainit(void)
+{
+ int i, nports;
+ Dirtab *dp;
+ struct stat sb;
+
+#ifdef buildsysdev
+ buildsysdev();
+#endif
+
+ /* check to see which ports exist by trying to stat them */
+ nports = 0;
+ for (i=0; i < nelem(sysdev); i++) {
+ if(stat(sysdev[i], &sb) < 0)
+ break;
+
+ nports++;
+ }
+
+ if (!nports)
+ return;
+
+ ndir = Nqid*nports+1;
+ dp = eiadir = malloc(ndir*sizeof(Dirtab));
+ if(dp == 0)
+ panic("eiainit");
+ strcpy(dp->name, ".");
+ dp->qid.path = 0;
+ dp->qid.type = QTDIR;
+ dp->perm = DMDIR|0555;
+ dp++;
+ eia = malloc(nports*sizeof(Eia));
+ if(eia == 0)
+ panic("eiainit");
+ for(i = 0; i < nports; i++) {
+ sprint(dp->name, "%s%d", Devname, i);
+ dp->qid.path = NETQID(i, Ndataqid);
+ dp->perm = 0660;
+ dp++;
+ sprint(dp->name, "%s%dctl", Devname, i);
+ dp->qid.path = NETQID(i, Nctlqid);
+ dp->perm = 0660;
+ dp++;
+ sprint(dp->name, "%s%dstatus", Devname, i);
+ dp->qid.path = NETQID(i, Nstatqid);
+ dp->perm = 0660;
+ dp++;
+ eia[i].frame = eia[i].overrun = 0;
+ eia[i].restore = eia[i].dtr = eia[i].rts = eia[i].cts = 0;
+ }
+}
+
+static Chan*
+eiaattach(char *spec)
+{
+ if(eiadir == nil)
+ error(Enodev);
+
+ return devattach(Devchar, spec);
+}
+
+Walkqid*
+eiawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, eiadir, ndir, devgen);
+}
+
+int
+eiastat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, eiadir, ndir, devgen);
+}
+
+static void
+resxtra(int port, struct termios *ts); /* non-POSIX extensions */
+
+static Chan*
+eiaopen(Chan *c, int mode)
+{
+ int port = NETID(c->qid.path);
+ struct termios ts;
+ int r;
+
+ c = devopen(c, mode, eiadir, ndir, devgen);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Nctlqid:
+ case Ndataqid:
+ case Nstatqid:
+ if(incref(&eia[port].r) != 1)
+ break;
+
+ osenter();
+ eia[port].fd = open(sysdev[port], O_RDWR);
+ osleave();
+ if(eia[port].fd < 0)
+ oserror();
+
+ /* make port settings sane */
+ if(tcgetattr(eia[port].fd, &ts) < 0)
+ oserror();
+ ts.c_iflag = ts.c_oflag = ts.c_lflag = 0;
+ if(eia[port].restore)
+ ts = eia[port].ts;
+ else {
+ cfsetispeed(&ts, B9600);
+ cfsetospeed(&ts, B9600);
+ ts.c_iflag |= IGNPAR;
+ ts.c_cflag &= ~CSIZE;
+ ts.c_cflag |= CS8|CREAD;
+ ts.c_cflag &= ~(PARENB|PARODD);
+ ts.c_cc[VMIN] = 1;
+ ts.c_cc[VTIME] = 0;
+ }
+ osenter();
+ r = tcsetattr(eia[port].fd, TCSANOW, &ts);
+ osleave();
+ if(r < 0)
+ oserror();
+
+ if(eia[port].restore)
+ resxtra(port, &ts);
+ break;
+ }
+ return c;
+}
+
+static void
+eiaclose(Chan *c)
+{
+ int port = NETID(c->qid.path);
+
+ if((c->flag & COPEN) == 0)
+ return;
+
+ switch(NETTYPE(c->qid.path)) {
+ case Nctlqid:
+ case Ndataqid:
+ case Nstatqid:
+ if(decref(&eia[port].r) != 0)
+ break;
+ if(eia[port].fd >= 0) {
+ osenter();
+ close(eia[port].fd);
+ osleave();
+ }
+ break;
+ }
+
+}
+
+static long
+eiaread(Chan *c, void *buf, long n, vlong offset)
+{
+ ssize_t cnt;
+ int port = NETID(c->qid.path);
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, eiadir, ndir, devgen);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Ndataqid:
+ osenter();
+ cnt = read(eia[port].fd, buf, n);
+ osleave();
+ if(cnt == -1)
+ oserror();
+ return cnt;
+ case Nctlqid:
+ return readnum(offset, buf, n, port, NUMSIZE);
+ case Nstatqid:
+ return rdstat(port, buf, n, offset);
+ }
+
+ return 0;
+}
+
+static long
+eiawrite(Chan *c, void *buf, long n, vlong offset)
+{
+ ssize_t cnt;
+ char cmd[Maxctl];
+ int port = NETID(c->qid.path);
+
+ USED(offset);
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Ndataqid:
+ osenter();
+ cnt = write(eia[port].fd, buf, n);
+ osleave();
+ if(cnt == -1)
+ oserror();
+ return cnt;
+ case Nctlqid:
+ if(n >= (long)sizeof(cmd))
+ n = sizeof(cmd)-1;
+ memmove(cmd, buf, n);
+ cmd[n] = 0;
+ wrctl(port, cmd);
+ return n;
+ }
+ return 0;
+}
+
+int
+eiawstat(Chan *c, uchar *dp, int n)
+{
+ Dir d;
+ int i;
+
+ if(strcmp(up->env->user, eve) != 0)
+ error(Eperm);
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ n = convM2D(dp, n, &d, nil);
+ i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid;
+ eiadir[i+1].perm = d.mode&0666;
+ return n;
+}
+
+Dev eiadevtab = {
+ Devchar,
+ Devname,
+
+ eiainit,
+ eiaattach,
+ eiawalk,
+ eiastat,
+ eiaopen,
+ devcreate,
+ eiaclose,
+ eiaread,
+ devbread,
+ eiawrite,
+ devbwrite,
+ devremove,
+ eiawstat
+};
--- /dev/null
+++ b/emu/port/devenv.c
@@ -1,0 +1,253 @@
+/*
+ * devenv - environment
+ */
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Maxenvlen= 16*1024-1,
+};
+
+
+static void envremove(Chan*);
+
+static int
+envgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
+{
+ Egrp *eg;
+ Evalue *e;
+
+ USED(name);
+ USED(d);
+ USED(nd);
+ if(s == DEVDOTDOT){
+ devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp);
+ return 1;
+ }
+ eg = up->env->egrp;
+ qlock(&eg->l);
+ for(e = eg->entries; e != nil && s != 0; e = e->next)
+ s--;
+ if(e == nil) {
+ qunlock(&eg->l);
+ return -1;
+ }
+ /* make sure name string continues to exist after we release lock */
+ kstrcpy(up->genbuf, e->var, sizeof up->genbuf);
+ devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp);
+ qunlock(&eg->l);
+ return 1;
+}
+
+static Chan*
+envattach(char *spec)
+{
+ return devattach('e', spec);
+}
+
+static Walkqid*
+envwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, envgen);
+}
+
+static int
+envstat(Chan *c, uchar *db, int n)
+{
+ if(c->qid.type & QTDIR)
+ c->qid.vers = up->env->egrp->vers;
+ return devstat(c, db, n, 0, 0, envgen);
+}
+
+static Chan *
+envopen(Chan *c, int mode)
+{
+ Egrp *eg;
+ Evalue *e;
+
+ if(c->qid.type & QTDIR) {
+ if(mode != OREAD)
+ error(Eperm);
+ c->mode = mode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+ eg = up->env->egrp;
+ qlock(&eg->l);
+ for(e = eg->entries; e != nil; e = e->next)
+ if(e->qid.path == c->qid.path)
+ break;
+ if(e == nil) {
+ qunlock(&eg->l);
+ error(Enonexist);
+ }
+ if(mode == (OWRITE|OTRUNC) && e->val) {
+ free(e->val);
+ e->val = 0;
+ e->len = 0;
+ e->qid.vers++;
+ }
+ qunlock(&eg->l);
+ c->offset = 0;
+ c->flag |= COPEN;
+ c->mode = mode&~OTRUNC;
+ return c;
+}
+
+static void
+envcreate(Chan *c, char *name, int mode, ulong perm)
+{
+ Egrp *eg;
+ Evalue *e, *le;
+
+ USED(perm);
+ if(c->qid.type != QTDIR)
+ error(Eperm);
+ if(strlen(name) >= sizeof(up->genbuf))
+ error("name too long"); /* needs to fit for stat */
+ eg = up->env->egrp;
+ qlock(&eg->l);
+ for(le = nil, e = eg->entries; e != nil; le = e, e = e->next)
+ if(strcmp(e->var, name) == 0) {
+ qunlock(&eg->l);
+ error(Eexist);
+ }
+ e = smalloc(sizeof(Evalue));
+ e->var = smalloc(strlen(name)+1);
+ strcpy(e->var, name);
+ e->val = 0;
+ e->len = 0;
+ e->qid.path = ++eg->path;
+ if (le == nil)
+ eg->entries = e;
+ else
+ le->next = e;
+ e->qid.vers = 0;
+ c->qid = e->qid;
+ eg->vers++;
+ qunlock(&eg->l);
+ c->offset = 0;
+ c->flag |= COPEN;
+ c->mode = mode;
+}
+
+static void
+envclose(Chan * c)
+{
+ if(c->flag & CRCLOSE)
+ envremove(c);
+}
+
+static long
+envread(Chan *c, void *a, long n, vlong offset)
+{
+ Egrp *eg;
+ Evalue *e;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, envgen);
+ eg = up->env->egrp;
+ qlock(&eg->l);
+ for(e = eg->entries; e != nil; e = e->next)
+ if(e->qid.path == c->qid.path)
+ break;
+ if(e == nil) {
+ qunlock(&eg->l);
+ error(Enonexist);
+ }
+ if(offset > e->len) /* protects against overflow converting vlong to ulong */
+ n = 0;
+ else if(offset + n > e->len)
+ n = e->len - offset;
+ if(n <= 0)
+ n = 0;
+ else
+ memmove(a, e->val+offset, n);
+ qunlock(&eg->l);
+ return n;
+}
+
+static long
+envwrite(Chan *c, void *a, long n, vlong offset)
+{
+ char *s;
+ ulong ve;
+ Egrp *eg;
+ Evalue *e;
+
+ if(n <= 0)
+ return 0;
+ ve = offset+n;
+ if(ve > Maxenvlen)
+ error(Etoobig);
+ eg = up->env->egrp;
+ qlock(&eg->l);
+ for(e = eg->entries; e != nil; e = e->next)
+ if(e->qid.path == c->qid.path)
+ break;
+ if(e == nil) {
+ qunlock(&eg->l);
+ error(Enonexist);
+ }
+ if(ve > e->len) {
+ s = smalloc(ve);
+ memmove(s, e->val, e->len);
+ if(e->val)
+ free(e->val);
+ e->val = s;
+ e->len = ve;
+ }
+ memmove(e->val+offset, a, n);
+ e->qid.vers++;
+ qunlock(&eg->l);
+ return n;
+}
+
+static void
+envremove(Chan *c)
+{
+ Egrp *eg;
+ Evalue *e, **l;
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+ eg = up->env->egrp;
+ qlock(&eg->l);
+ for(l = &eg->entries; *l != nil; l = &(*l)->next)
+ if((*l)->qid.path == c->qid.path)
+ break;
+ e = *l;
+ if(e == nil) {
+ qunlock(&eg->l);
+ error(Enonexist);
+ }
+ *l = e->next;
+ eg->vers++;
+ qunlock(&eg->l);
+ free(e->var);
+ if(e->val != nil)
+ free(e->val);
+ free(e);
+}
+
+Dev envdevtab = {
+ 'e',
+ "env",
+
+ devinit,
+ envattach,
+ envwalk,
+ envstat,
+ envopen,
+ envcreate,
+ envclose,
+ envread,
+ devbread,
+ envwrite,
+ devbwrite,
+ envremove,
+ devwstat
+};
--- /dev/null
+++ b/emu/port/devfs-posix.c
@@ -1,0 +1,1094 @@
+/*
+ * Unix file system interface
+ */
+#define _LARGEFILE64_SOURCE 1
+#define _FILE_OFFSET_BITS 64
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <utime.h>
+#include <dirent.h>
+#include <stdio.h>
+#define __EXTENSIONS__
+#undef getwd
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+typedef struct Fsinfo Fsinfo;
+struct Fsinfo
+{
+ int uid;
+ int gid;
+ int mode; /* Unix mode */
+ DIR* dir; /* open directory */
+ struct dirent* de; /* directory reading */
+ int fd; /* open files */
+ ulong offset; /* offset when reading directory */
+ int eod; /* end of directory */
+ int issocket;
+ QLock oq; /* mutex for offset */
+ char* spec;
+ Cname* name; /* Unix's name for file */
+ Qid rootqid; /* Plan 9's qid for Inferno's root */
+};
+
+#define FS(c) ((Fsinfo*)(c)->aux)
+
+enum
+{
+ IDSHIFT = 8,
+ NID = 1 << IDSHIFT,
+ IDMASK = NID - 1,
+ MAXPATH = 1024 /* TO DO: eliminate this */
+};
+
+typedef struct User User;
+struct User
+{
+ int id; /* might be user or group ID */
+ int gid; /* if it's a user not a group, the group ID (only for setid) */
+ char* name;
+ int nmem;
+ int* mem; /* member array, if nmem != 0 */
+ User* next;
+};
+
+char rootdir[MAXROOT] = ROOT;
+
+static User* uidmap[NID];
+static User* gidmap[NID];
+static QLock idl;
+static User* name2user(User**, char*, User* (*get)(char*));
+static User* id2user(User**, int, User* (*get)(int));
+static User* newuid(int);
+static User* newgid(int);
+static User* newuname(char*);
+static User* newgname(char*);
+
+static Qid fsqid(struct stat *);
+static void fspath(Cname*, char*, char*);
+static int fsdirconv(Chan*, char*, char*, struct stat*, uchar*, int, int);
+static Cname* fswalkpath(Cname*, char*, int);
+static char* fslastelem(Cname*);
+static int ingroup(int id, int gid);
+static void fsperm(Chan*, int);
+static long fsdirread(Chan*, uchar*, int, vlong);
+static int fsomode(int);
+static void fsremove(Chan*);
+static vlong osdisksize(int); /* defined by including file */
+
+/*
+ * make invalid symbolic links visible; less confusing, and at least you can then delete them.
+ */
+static int
+xstat(char *f, struct stat *sb)
+{
+ if(stat(f, sb) >= 0)
+ return 0;
+ /* could possibly generate ->name as rob once suggested */
+ return lstat(f, sb);
+}
+
+static void
+fsfree(Chan *c)
+{
+ cnameclose(FS(c)->name);
+ free(FS(c));
+}
+
+Chan*
+fsattach(char *spec)
+{
+ Chan *c;
+ struct stat st;
+ static int devno;
+ static Lock l;
+
+ if(!emptystr(spec) && strcmp(spec, "*") != 0)
+ error(Ebadspec);
+ if(stat(rootdir, &st) < 0)
+ oserror();
+ if(!S_ISDIR(st.st_mode))
+ error(Enotdir);
+
+ c = devattach('U', spec);
+ c->qid = fsqid(&st);
+ c->aux = smalloc(sizeof(Fsinfo));
+ FS(c)->dir = nil;
+ FS(c)->de = nil;
+ FS(c)->fd = -1;
+ FS(c)->issocket = 0;
+ FS(c)->gid = st.st_gid;
+ FS(c)->uid = st.st_uid;
+ FS(c)->mode = st.st_mode;
+ lock(&l);
+ c->dev = devno++;
+ unlock(&l);
+ if(!emptystr(spec)){
+ FS(c)->spec = "/";
+ FS(c)->name = newcname(FS(c)->spec);
+ }else
+ FS(c)->name = newcname(rootdir);
+ FS(c)->rootqid = c->qid;
+
+ return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j;
+ volatile int alloc;
+ Walkqid *wq;
+ struct stat st;
+ char *n;
+ Cname *next;
+ Cname *volatile current;
+ Qid rootqid;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ current = nil;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone != nil)
+ cclose(wq->clone);
+ cnameclose(current);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0;
+ alloc = 1;
+ }
+ wq->clone = nc;
+ rootqid = FS(c)->rootqid;
+ current = FS(c)->name;
+ if(current != nil)
+ incref(¤t->r);
+ for(j = 0; j < nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ n = name[j];
+ if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){
+ next = current;
+ incref(&next->r);
+ next = addelem(current, n);
+ //print("** ufs walk '%s' -> %s [%s]\n", current->s, n, next->s);
+ if(xstat(next->s, &st) < 0){
+ cnameclose(next);
+ if(j == 0)
+ error(Enonexist);
+ strcpy(up->env->errstr, Enonexist);
+ break;
+ }
+ nc->qid = fsqid(&st);
+ cnameclose(current);
+ current = next;
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+ poperror();
+ if(wq->nqid < nname){
+ cnameclose(current);
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ nc->aux = smalloc(sizeof(Fsinfo));
+ nc->type = c->type;
+ if(nname > 0) {
+ FS(nc)->gid = st.st_gid;
+ FS(nc)->uid = st.st_uid;
+ FS(nc)->mode = st.st_mode;
+ FS(nc)->issocket = S_ISSOCK(st.st_mode);
+ } else {
+ FS(nc)->gid = FS(c)->gid;
+ FS(nc)->uid = FS(c)->uid;
+ FS(nc)->mode = FS(c)->mode;
+ FS(nc)->issocket = FS(c)->issocket;
+ }
+ FS(nc)->name = current;
+ FS(nc)->spec = FS(c)->spec;
+ FS(nc)->rootqid = rootqid;
+ FS(nc)->fd = -1;
+ FS(nc)->dir = nil;
+ FS(nc)->de = nil;
+ }
+ return wq;
+}
+
+static int
+fsstat(Chan *c, uchar *dp, int n)
+{
+ struct stat st;
+ char *p;
+
+ if(FS(c)->fd >= 0){
+ if(fstat(FS(c)->fd, &st) < 0)
+ oserror();
+ }else{
+ if(xstat(FS(c)->name->s, &st) < 0)
+ oserror();
+ }
+ p = fslastelem(FS(c)->name);
+ if(*p == 0)
+ p = "/";
+ qlock(&idl);
+ n = fsdirconv(c, FS(c)->name->s, p, &st, dp, n, 0);
+ qunlock(&idl);
+ return n;
+}
+
+static int
+opensocket(char *path)
+{
+ int fd;
+ struct sockaddr_un su;
+
+ memset(&su, 0, sizeof su);
+ su.sun_family = AF_UNIX;
+ if(strlen(path)+1 > sizeof su.sun_path)
+ error("unix socket name too long");
+ strcpy(su.sun_path, path);
+ if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -1;
+ if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
+ return fd;
+ close(fd);
+ if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
+ return -1;
+ if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
+ return fd;
+ close(fd);
+ return -1;
+}
+
+static Chan*
+fsopen(Chan *c, int mode)
+{
+ int m, isdir;
+
+ m = mode & (OTRUNC|3);
+ switch(m) {
+ case 0:
+ fsperm(c, 4);
+ break;
+ case 1:
+ case 1|16:
+ fsperm(c, 2);
+ break;
+ case 2:
+ case 0|16:
+ case 2|16:
+ fsperm(c, 4);
+ fsperm(c, 2);
+ break;
+ case 3:
+ fsperm(c, 1);
+ break;
+ default:
+ error(Ebadarg);
+ }
+
+ isdir = c->qid.type & QTDIR;
+
+ if(isdir && mode != OREAD)
+ error(Eperm);
+
+ m = fsomode(m & 3);
+ c->mode = openmode(mode);
+
+ if(isdir) {
+ FS(c)->dir = opendir(FS(c)->name->s);
+ if(FS(c)->dir == nil)
+ oserror();
+ FS(c)->eod = 0;
+ }
+ else {
+ if(!FS(c)->issocket){
+ if(mode & OTRUNC)
+ m |= O_TRUNC;
+ FS(c)->fd = open(FS(c)->name->s, m, 0666);
+ }else
+ FS(c)->fd = opensocket(FS(c)->name->s);
+ if(FS(c)->fd < 0)
+ oserror();
+ }
+
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+static void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+ int fd, m, o;
+ struct stat st;
+ Cname *n;
+
+ fsperm(c, 2);
+
+ m = fsomode(mode&3);
+ openmode(mode); /* get the errors out of the way */
+
+ if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ error(Efilename);
+ n = fswalkpath(FS(c)->name, name, 1);
+ if(waserror()){
+ cnameclose(n);
+ nexterror();
+ }
+ if(perm & DMDIR) {
+ if(m)
+ error(Eperm);
+
+ perm &= ~0777 | (FS(c)->mode & 0777);
+ if(mkdir(n->s, perm) < 0)
+ oserror();
+
+ fd = open(n->s, 0);
+ if(fd < 0)
+ oserror();
+ fchmod(fd, perm);
+ fchown(fd, up->env->uid, FS(c)->gid);
+ if(fstat(fd, &st) <0){
+ close(fd);
+ oserror();
+ }
+ close(fd);
+ FS(c)->dir = opendir(n->s);
+ if(FS(c)->dir == nil)
+ oserror();
+ FS(c)->eod = 0;
+ } else {
+ o = (O_CREAT | O_EXCL) | (mode&3);
+ if(mode & OTRUNC)
+ o |= O_TRUNC;
+ perm &= ~0666 | (FS(c)->mode & 0666);
+ fd = open(n->s, o, perm);
+ if(fd < 0)
+ oserror();
+ fchmod(fd, perm);
+ fchown(fd, up->env->uid, FS(c)->gid);
+ if(fstat(fd, &st) < 0){
+ close(fd);
+ oserror();
+ }
+ FS(c)->fd = fd;
+ }
+ cnameclose(FS(c)->name);
+ FS(c)->name = n;
+ poperror();
+
+ c->qid = fsqid(&st);
+ FS(c)->gid = st.st_gid;
+ FS(c)->uid = st.st_uid;
+ FS(c)->mode = st.st_mode;
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ FS(c)->issocket = 0;
+ c->flag |= COPEN;
+}
+
+static void
+fsclose(Chan *c)
+{
+ if((c->flag & COPEN) != 0){
+ if(c->qid.type & QTDIR)
+ closedir(FS(c)->dir);
+ else
+ close(FS(c)->fd);
+ }
+ if(c->flag & CRCLOSE) {
+ if(!waserror()) {
+ fsremove(c);
+ poperror();
+ }
+ return;
+ }
+ fsfree(c);
+}
+
+static long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+ long r;
+
+ if(c->qid.type & QTDIR){
+ qlock(&FS(c)->oq);
+ if(waserror()) {
+ qunlock(&FS(c)->oq);
+ nexterror();
+ }
+ r = fsdirread(c, va, n, offset);
+ poperror();
+ qunlock(&FS(c)->oq);
+ }else{
+ if(!FS(c)->issocket){
+ r = pread(FS(c)->fd, va, n, offset);
+ if(r >= 0)
+ return r;
+ if(errno != ESPIPE && errno != EPIPE)
+ oserror();
+ }
+ r = read(FS(c)->fd, va, n);
+ if(r < 0)
+ oserror();
+ }
+ return r;
+}
+
+static long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+ long r;
+
+ if(!FS(c)->issocket){
+ r = pwrite(FS(c)->fd, va, n, offset);
+ if(r >= 0)
+ return r;
+ if(errno != ESPIPE && errno != EPIPE)
+ oserror();
+ }
+ r = write(FS(c)->fd, va, n);
+ if(r < 0)
+ oserror();
+ return r;
+}
+
+static void
+fswchk(Cname *c)
+{
+ struct stat st;
+
+ if(stat(c->s, &st) < 0)
+ oserror();
+
+ if(st.st_uid == up->env->uid)
+ st.st_mode >>= 6;
+ else if(st.st_gid == up->env->gid || ingroup(up->env->uid, st.st_gid))
+ st.st_mode >>= 3;
+
+ if(st.st_mode & S_IWOTH)
+ return;
+
+ error(Eperm);
+}
+
+static void
+fsremove(Chan *c)
+{
+ int n;
+ Cname *volatile dir;
+
+ if(waserror()){
+ fsfree(c);
+ nexterror();
+ }
+ dir = fswalkpath(FS(c)->name, "..", 1);
+ if(waserror()){
+ cnameclose(dir);
+ nexterror();
+ }
+ fswchk(dir);
+ cnameclose(dir);
+ poperror();
+ if(c->qid.type & QTDIR)
+ n = rmdir(FS(c)->name->s);
+ else
+ n = remove(FS(c)->name->s);
+ if(n < 0)
+ oserror();
+ poperror();
+ fsfree(c);
+}
+
+static int
+fswstat(Chan *c, uchar *buf, int nb)
+{
+ Dir *d;
+ User *p;
+ Cname *volatile ph;
+ struct stat st;
+ struct utimbuf utbuf;
+ int tsync;
+
+ if(FS(c)->fd >= 0){
+ if(fstat(FS(c)->fd, &st) < 0)
+ oserror();
+ }else{
+ if(stat(FS(c)->name->s, &st) < 0)
+ oserror();
+ }
+ d = malloc(sizeof(*d)+nb);
+ if(d == nil)
+ error(Enomem);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ tsync = 1;
+ nb = convM2D(buf, nb, d, (char*)&d[1]);
+ if(nb == 0)
+ error(Eshortstat);
+ if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) {
+ tsync = 0;
+ validname(d->name, 0);
+ ph = fswalkpath(FS(c)->name, "..", 1);
+ if(waserror()){
+ cnameclose(ph);
+ nexterror();
+ }
+ fswchk(ph);
+ ph = fswalkpath(ph, d->name, 0);
+ if(rename(FS(c)->name->s, ph->s) < 0)
+ oserror();
+ cnameclose(FS(c)->name);
+ poperror();
+ FS(c)->name = ph;
+ }
+
+ if(d->mode != ~0 && (d->mode&0777) != (st.st_mode&0777)) {
+ tsync = 0;
+ if(up->env->uid != st.st_uid)
+ error(Eowner);
+ if(FS(c)->fd >= 0){
+ if(fchmod(FS(c)->fd, d->mode&0777) < 0)
+ oserror();
+ }else{
+ if(chmod(FS(c)->name->s, d->mode&0777) < 0)
+ oserror();
+ }
+ FS(c)->mode &= ~0777;
+ FS(c)->mode |= d->mode&0777;
+ }
+
+ if(d->atime != ~0 && d->atime != st.st_atime ||
+ d->mtime != ~0 && d->mtime != st.st_mtime) {
+ tsync = 0;
+ if(up->env->uid != st.st_uid)
+ error(Eowner);
+ if(d->mtime != ~0)
+ utbuf.modtime = d->mtime;
+ else
+ utbuf.modtime = st.st_mtime;
+ if(d->atime != ~0)
+ utbuf.actime = d->atime;
+ else
+ utbuf.actime = st.st_atime;
+ if(utime(FS(c)->name->s, &utbuf) < 0) /* TO DO: futimes isn't portable */
+ oserror();
+ }
+
+ if(*d->gid){
+ tsync = 0;
+ qlock(&idl);
+ if(waserror()){
+ qunlock(&idl);
+ nexterror();
+ }
+ p = name2user(gidmap, d->gid, newgname);
+ if(p == 0)
+ error(Eunknown);
+ if(p->id != st.st_gid) {
+ if(up->env->uid != st.st_uid)
+ error(Eowner);
+ if(FS(c)->fd >= 0){
+ if(fchown(FS(c)->fd, st.st_uid, p->id) < 0)
+ oserror();
+ }else{
+ if(chown(FS(c)->name->s, st.st_uid, p->id) < 0)
+ oserror();
+ }
+ FS(c)->gid = p->id;
+ }
+ poperror();
+ qunlock(&idl);
+ }
+
+ if(d->length != ~(uvlong)0){
+ tsync = 0;
+ if(FS(c)->fd >= 0){
+ fsperm(c, 2);
+ if(ftruncate(FS(c)->fd, d->length) < 0)
+ oserror();
+ }else{
+ fswchk(FS(c)->name);
+ if(truncate(FS(c)->name->s, d->length) < 0)
+ oserror();
+ }
+ }
+
+ poperror();
+ free(d);
+ if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0)
+ oserror();
+ return nb;
+}
+
+static Qid
+fsqid(struct stat *st)
+{
+ Qid q;
+ u16int dev;
+
+ q.type = QTFILE;
+ if(S_ISDIR(st->st_mode))
+ q.type = QTDIR;
+
+ dev = (u16int)st->st_dev;
+ if(dev & 0x8000){
+ static int aware = 1;
+ if(aware==0){
+ aware = 1;
+ fprint(2, "fs: fsqid: top-bit dev: %#4.4ux\n", dev);
+ }
+ dev ^= 0x8080;
+ }
+
+ q.path = (uvlong)dev<<48;
+ q.path ^= st->st_ino;
+ q.vers = st->st_mtime;
+
+ return q;
+}
+
+static void
+fspath(Cname *c, char *name, char *path)
+{
+ int n;
+
+ if(c->len+strlen(name) >= MAXPATH)
+ panic("fspath: name too long");
+ memmove(path, c->s, c->len);
+ n = c->len;
+ if(path[n-1] != '/')
+ path[n++] = '/';
+ strcpy(path+n, name);
+ if(isdotdot(name))
+ cleanname(path);
+/*print("->%s\n", path);*/
+}
+
+static Cname *
+fswalkpath(Cname *c, char *name, int dup)
+{
+ if(dup)
+ c = newcname(c->s);
+ c = addelem(c, name);
+ if(isdotdot(name))
+ cleancname(c);
+ return c;
+}
+
+static char *
+fslastelem(Cname *c)
+{
+ char *p;
+
+ p = c->s + c->len;
+ while(p > c->s && p[-1] != '/')
+ p--;
+ return p;
+}
+
+static void
+fsperm(Chan *c, int mask)
+{
+ int m;
+
+ m = FS(c)->mode;
+/*
+ print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n",
+ m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid);
+*/
+ if(FS(c)->uid == up->env->uid)
+ m >>= 6;
+ else if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid))
+ m >>= 3;
+
+ m &= mask;
+ if(m == 0)
+ error(Eperm);
+}
+
+static int
+isdots(char *name)
+{
+ return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
+}
+
+static int
+fsdirconv(Chan *c, char *path, char *name, struct stat *s, uchar *va, int nb, int indir)
+{
+ Dir d;
+ char uidbuf[NUMSIZE], gidbuf[NUMSIZE];
+ User *u;
+ int fd;
+
+ memset(&d, 0, sizeof(d));
+ d.name = name;
+ u = id2user(uidmap, s->st_uid, newuid);
+ if(u == nil){
+ snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid);
+ d.uid = uidbuf;
+ }else
+ d.uid = u->name;
+ u = id2user(gidmap, s->st_gid, newgid);
+ if(u == nil){
+ snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid);
+ d.gid = gidbuf;
+ }else
+ d.gid = u->name;
+ d.muid = "";
+ d.qid = fsqid(s);
+ d.mode = (d.qid.type<<24)|(s->st_mode&0777);
+ d.atime = s->st_atime;
+ d.mtime = s->st_mtime;
+ d.length = s->st_size;
+ if(d.mode&DMDIR)
+ d.length = 0;
+ else if(S_ISBLK(s->st_mode) && s->st_size == 0){
+ fd = open(path, O_RDONLY);
+ if(fd >= 0){
+ d.length = osdisksize(fd);
+ close(fd);
+ }
+ }
+ d.type = 'U';
+ d.dev = c->dev;
+ if(indir && sizeD2M(&d) > nb)
+ return -1; /* directory reader needs to know it didn't fit */
+ return convD2M(&d, va, nb);
+}
+
+static long
+fsdirread(Chan *c, uchar *va, int count, vlong offset)
+{
+ int i;
+ long n, r;
+ struct stat st;
+ char path[MAXPATH], *ep;
+ struct dirent *de;
+ static uchar slop[8192];
+
+ i = 0;
+ fspath(FS(c)->name, "", path);
+ ep = path+strlen(path);
+ if(FS(c)->offset != offset) {
+ seekdir(FS(c)->dir, 0);
+ FS(c)->de = nil;
+ FS(c)->eod = 0;
+ for(n=0; n<offset; ) {
+ de = readdir(FS(c)->dir);
+ if(de == 0) {
+ /* EOF, so stash offset and return 0 */
+ FS(c)->offset = n;
+ FS(c)->eod = 1;
+ return 0;
+ }
+ if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
+ continue;
+ strecpy(ep, path+sizeof(path), de->d_name);
+ if(xstat(path, &st) < 0) {
+ fprint(2, "dir: bad path %s\n", path);
+ continue;
+ }
+ qlock(&idl);
+ if(waserror()){
+ qunlock(&idl);
+ nexterror();
+ }
+ r = fsdirconv(c, path, de->d_name, &st, slop, sizeof(slop), 1);
+ poperror();
+ qunlock(&idl);
+ if(r <= 0) {
+ FS(c)->offset = n;
+ return 0;
+ }
+ n += r;
+ }
+ FS(c)->offset = offset;
+ }
+
+ if(FS(c)->eod)
+ return 0;
+
+ /*
+ * Take idl on behalf of id2name. Stalling attach, which is a
+ * rare operation, until the readdir completes is probably
+ * preferable to adding lock round-trips.
+ */
+ qlock(&idl);
+ while(i < count){
+ de = FS(c)->de;
+ FS(c)->de = nil;
+ if(de == nil)
+ de = readdir(FS(c)->dir);
+ if(de == nil){
+ FS(c)->eod = 1;
+ break;
+ }
+
+ if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
+ continue;
+
+ strecpy(ep, path+sizeof(path), de->d_name);
+ if(xstat(path, &st) < 0) {
+ fprint(2, "dir: bad path %s\n", path);
+ continue;
+ }
+ r = fsdirconv(c, path, de->d_name, &st, va+i, count-i, 1);
+ if(r <= 0){
+ FS(c)->de = de;
+ break;
+ }
+ i += r;
+ FS(c)->offset += r;
+ }
+ qunlock(&idl);
+ return i;
+}
+
+static int
+fsomode(int m)
+{
+ if(m < 0 || m > 3)
+ error(Ebadarg);
+ return m == 3? 0: m;
+}
+
+void
+setid(char *name, int owner)
+{
+ User *u;
+
+ if(owner && !iseve())
+ return;
+ kstrdup(&up->env->user, name);
+
+ qlock(&idl);
+ u = name2user(uidmap, name, newuname);
+ if(u == nil){
+ qunlock(&idl);
+ up->env->uid = -1;
+ up->env->gid = -1;
+ return;
+ }
+
+ up->env->uid = u->id;
+ up->env->gid = u->gid;
+ qunlock(&idl);
+}
+
+static User**
+hashuser(User** tab, int id)
+{
+ int i;
+
+ i = (id>>IDSHIFT) ^ id;
+ return &tab[i & IDMASK];
+}
+
+/*
+ * the caller of the following functions must hold QLock idl.
+ */
+
+/*
+ * we could keep separate maps of user and group names to Users to
+ * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid)
+ */
+static User*
+name2user(User **tab, char *name, User* (*get)(char*))
+{
+ int i;
+ User *u, **h;
+ static User *prevu;
+ static User **prevtab;
+
+ if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0)
+ return prevu; /* it's often the one we've just seen */
+
+ for(i=0; i<NID; i++)
+ for(u = tab[i]; u != nil; u = u->next)
+ if(strcmp(name, u->name) == 0) {
+ prevtab = tab;
+ prevu = u;
+ return u;
+ }
+
+ u = get(name);
+ if(u == nil)
+ return nil;
+ h = hashuser(tab, u->id);
+ u->next = *h;
+ *h = u;
+ prevtab = tab;
+ prevu = u;
+ return u;
+}
+
+static void
+freeuser(User *u)
+{
+ if(u != nil){
+ free(u->name);
+ free(u->mem);
+ free(u);
+ }
+}
+
+static User*
+newuser(int id, int gid, char *name, int nmem)
+{
+ User *u;
+
+ u = malloc(sizeof(*u));
+ if(u == nil)
+ return nil;
+ u->name = strdup(name);
+ if(u->name == nil){
+ free(u);
+ return nil;
+ }
+ u->nmem = nmem;
+ if(nmem){
+ u->mem = malloc(nmem*sizeof(*u->mem));
+ if(u->mem == nil){
+ free(u->name);
+ free(u);
+ return nil;
+ }
+ }else
+ u->mem = nil;
+ u->id = id;
+ u->gid = gid;
+ u->next = nil;
+ return u;
+}
+
+static User*
+newuname(char *name)
+{
+ struct passwd *p;
+
+ p = getpwnam(name);
+ if(p == nil)
+ return nil;
+ return newuser(p->pw_uid, p->pw_gid, name, 0);
+}
+
+static User*
+newuid(int id)
+{
+ struct passwd *p;
+
+ p = getpwuid(id);
+ if(p == nil)
+ return nil;
+ return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0);
+}
+
+static User*
+newgroup(struct group *g)
+{
+ User *u, *gm;
+ int n, o;
+
+ if(g == nil)
+ return nil;
+ for(n=0; g->gr_mem[n] != nil; n++)
+ ;
+ u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n);
+ if(u == nil)
+ return nil;
+ o = 0;
+ for(n=0; g->gr_mem[n] != nil; n++){
+ gm = name2user(uidmap, g->gr_mem[n], newuname);
+ if(gm != nil)
+ u->mem[o++] = gm->id;
+ /* ignore names that don't map to IDs */
+ }
+ u->nmem = o;
+ return u;
+}
+
+static User*
+newgid(int id)
+{
+ return newgroup(getgrgid(id));
+}
+
+static User*
+newgname(char *name)
+{
+ return newgroup(getgrnam(name));
+}
+
+static User*
+id2user(User **tab, int id, User* (*get)(int))
+{
+ User *u, **h;
+
+ h = hashuser(tab, id);
+ for(u = *h; u != nil; u = u->next)
+ if(u->id == id)
+ return u;
+ u = get(id);
+ if(u == nil)
+ return nil;
+ u->next = *h;
+ *h = u;
+ return u;
+}
+
+static int
+ingroup(int id, int gid)
+{
+ int i;
+ User *g;
+
+ g = id2user(gidmap, gid, newgid);
+ if(g == nil || g->mem == nil)
+ return 0;
+ for(i = 0; i < g->nmem; i++)
+ if(g->mem[i] == id)
+ return 1;
+ return 0;
+}
+
+Dev fsdevtab = {
+ 'U',
+ "fs",
+
+ devinit,
+ fsattach,
+ fswalk,
+ fsstat,
+ fsopen,
+ fscreate,
+ fsclose,
+ fsread,
+ devbread,
+ fswrite,
+ devbwrite,
+ fsremove,
+ fswstat
+};
--- /dev/null
+++ b/emu/port/devindir.c
@@ -1,0 +1,35 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+static Chan *
+indirattach(char *spec)
+{
+ char *p;
+ Dev *d;
+
+ if(*spec == 0)
+ error(Ebadspec);
+ p = strrchr(spec, '!');
+ if(p == nil)
+ p = "";
+ else
+ *p++ = 0;
+ d = devbyname(spec);
+ if(d == nil || d->dc == '*'){
+ snprint(up->env->errstr, ERRMAX, "unknown device: %s", spec);
+ error(up->env->errstr);
+ }
+ if(up->env->pgrp->nodevs &&
+ (utfrune("|esDa", d->dc) == nil || d->dc == 's' && *p!='\0'))
+ error(Enoattach);
+ return d->attach(p);
+}
+
+Dev indirdevtab = {
+ '*',
+ "indir",
+
+ devinit,
+ indirattach,
+};
--- /dev/null
+++ b/emu/port/devip.c
@@ -1,0 +1,1119 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "ip.h"
+
+enum
+{
+ Qtopdir = 1, /* top level directory */
+ Qtopbase,
+ Qarp= Qtopbase,
+/* Qiproute, */
+/* Qipselftab, */
+ Qndb,
+
+ Qprotodir, /* directory for a protocol */
+ Qprotobase,
+ Qclone= Qprotobase,
+ Qstats,
+
+ Qconvdir, /* directory for a conversation */
+ Qconvbase,
+ Qctl= Qconvbase,
+ Qdata,
+ Qlisten,
+ Qlocal,
+ Qremote,
+ Qstatus,
+
+ Logtype= 5,
+ Masktype= (1<<Logtype)-1,
+ Logconv= 12,
+ Maskconv= (1<<Logconv)-1,
+ Shiftconv= Logtype,
+ Logproto= 8,
+ Maskproto= (1<<Logproto)-1,
+ Shiftproto= Logtype + Logconv,
+
+ Statelen = 256,
+
+ Nfs= 1,
+
+ Maxproto = 4,
+ MAXCONV = 4096
+};
+#define TYPE(x) ( ((ulong)(x).path) & Masktype )
+#define CONV(x) ( (((ulong)(x).path) >> Shiftconv) & Maskconv )
+#define PROTO(x) ( (((ulong)(x).path) >> Shiftproto) & Maskproto )
+#define QID(p, c, y) ( ((p)<<(Shiftproto)) | ((c)<<Shiftconv) | (y) )
+
+enum
+{
+ Idle= 0,
+ Announcing= 1,
+ Announced= 2,
+ Connecting= 3,
+ Connected= 4,
+ Hungup= 5,
+};
+
+struct Conv
+{
+ QLock l;
+
+ int x; /* conversation index */
+ Proto* p;
+
+ uchar laddr[IPaddrlen]; /* local IP address */
+ uchar raddr[IPaddrlen]; /* remote IP address */
+ int restricted; /* remote port is restricted */
+ ushort lport; /* local port number */
+ ushort rport; /* remote port number */
+
+ char* owner; /* protections */
+ int perm;
+ int inuse; /* opens of listen/data/ctl */
+ int state;
+
+ /* udp specific */
+ int headers; /* data src/dst headers in udp */
+
+ char cerr[ERRMAX];
+
+ QLock listenq;
+
+ void* ptcl; /* protocol specific stuff */
+
+ int sfd;
+ QLock wlock; /* prevent data from being split by concurrent writes */
+};
+
+struct Proto
+{
+ QLock l;
+ int x;
+ int ipproto;
+ int stype;
+ char* name;
+ int maxconv;
+ Fs* f; /* file system this proto is part of */
+ Conv** conv; /* array of conversations */
+ int pctlsize; /* size of per protocol ctl block */
+ int nc; /* number of conversations */
+ int ac;
+ Qid qid; /* qid for protocol directory */
+ /* port allocation isn't done here when hosted */
+
+ void* priv;
+};
+
+/*
+ * one per IP protocol stack
+ */
+struct Fs
+{
+ RWlock l;
+ int dev;
+
+ int np;
+ Proto* p[Maxproto+1]; /* list of supported protocols */
+ Proto* t2p[256]; /* vector of all protocols */
+
+ char ndb[1024]; /* an ndb entry for this interface */
+ int ndbvers;
+ long ndbmtime;
+};
+
+static Fs *ipfs[Nfs]; /* attached fs's */
+static char network[] = "network";
+static char* ipstates[] = {
+ "Closed", /* Idle */
+ "Announcing",
+ "Announced",
+ "Connecting",
+ "Established", /* Connected */
+ "Closed", /* Hungup */
+};
+
+static Conv* protoclone(Proto*, char*, int);
+static Conv* newconv(Proto*, Conv **);
+static void setladdr(Conv*);
+
+static int
+ip3gen(Chan *c, int i, Dir *dp)
+{
+ Qid q;
+ Conv *cv;
+ char *p;
+
+ cv = ipfs[c->dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)];
+ if(cv->owner == nil)
+ kstrdup(&cv->owner, eve);
+ mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE);
+
+ switch(i) {
+ default:
+ return -1;
+ case Qctl:
+ devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
+ return 1;
+ case Qdata:
+ devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
+ return 1;
+ case Qlisten:
+ devdir(c, q, "listen", 0, cv->owner, cv->perm, dp);
+ return 1;
+ case Qlocal:
+ p = "local";
+ break;
+ case Qremote:
+ p = "remote";
+ break;
+ case Qstatus:
+ p = "status";
+ break;
+ }
+ devdir(c, q, p, 0, cv->owner, 0444, dp);
+ return 1;
+}
+
+static int
+ip2gen(Chan *c, int i, Dir *dp)
+{
+ Qid q;
+
+ switch(i) {
+ case Qclone:
+ mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE);
+ devdir(c, q, "clone", 0, network, 0666, dp);
+ return 1;
+ case Qstats:
+ mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE);
+ devdir(c, q, "stats", 0, network, 0444, dp);
+ return 1;
+ }
+ return -1;
+}
+
+static int
+ip1gen(Chan *c, int i, Dir *dp)
+{
+ Qid q;
+ char *p;
+ int prot;
+ int len = 0;
+ Fs *f;
+ extern ulong kerndate;
+
+ f = ipfs[c->dev];
+
+ prot = 0664;
+ mkqid(&q, QID(0, 0, i), 0, QTFILE);
+ switch(i) {
+ default:
+ return -1;
+ case Qarp:
+ p = "arp";
+ break;
+ case Qndb:
+ p = "ndb";
+ len = strlen(ipfs[c->dev]->ndb);
+ break;
+/* case Qiproute:
+ p = "iproute";
+ break;
+ case Qipselftab:
+ p = "ipselftab";
+ prot = 0444;
+ break;
+ case Qiprouter:
+ p = "iprouter";
+ break;
+ case Qlog:
+ p = "log";
+ break;
+*/
+ }
+ devdir(c, q, p, len, network, prot, dp);
+ if(i == Qndb && f->ndbmtime > kerndate)
+ dp->mtime = f->ndbmtime;
+ return 1;
+}
+
+static int
+ipgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp)
+{
+ Qid q;
+ Conv *cv;
+ Fs *f;
+
+ USED(name);
+ USED(tab);
+ USED(x);
+ f = ipfs[c->dev];
+
+ switch(TYPE(c->qid)) {
+ case Qtopdir:
+ if(s == DEVDOTDOT){
+ mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
+ sprint(up->genbuf, "#I%lud", c->dev);
+ devdir(c, q, up->genbuf, 0, network, 0555, dp);
+ return 1;
+ }
+ if(s < f->np) {
+/* if(f->p[s]->connect == nil)
+ return 0; /* protocol with no user interface */
+ mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
+ devdir(c, q, f->p[s]->name, 0, network, 0555, dp);
+ return 1;
+ }
+ s -= f->np;
+ return ip1gen(c, s+Qtopbase, dp);
+ case Qarp:
+ case Qndb:
+/* case Qiproute:
+ case Qiprouter:
+ case Qipselftab: */
+ return ip1gen(c, TYPE(c->qid), dp);
+ case Qprotodir:
+ if(s == DEVDOTDOT){
+ mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
+ sprint(up->genbuf, "#I%lud", c->dev);
+ devdir(c, q, up->genbuf, 0, network, 0555, dp);
+ return 1;
+ }
+ if(s < f->p[PROTO(c->qid)]->ac) {
+ cv = f->p[PROTO(c->qid)]->conv[s];
+ sprint(up->genbuf, "%d", s);
+ mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR);
+ devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp);
+ return 1;
+ }
+ s -= f->p[PROTO(c->qid)]->ac;
+ return ip2gen(c, s+Qprotobase, dp);
+ case Qclone:
+ case Qstats:
+ return ip2gen(c, TYPE(c->qid), dp);
+ case Qconvdir:
+ if(s == DEVDOTDOT){
+ s = PROTO(c->qid);
+ mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
+ devdir(c, q, f->p[s]->name, 0, network, 0555, dp);
+ return 1;
+ }
+ return ip3gen(c, s+Qconvbase, dp);
+ case Qctl:
+ case Qdata:
+ case Qlisten:
+ case Qlocal:
+ case Qremote:
+ case Qstatus:
+ return ip3gen(c, TYPE(c->qid), dp);
+ }
+ return -1;
+}
+
+static void
+newproto(char *name, int type, int maxconv)
+{
+ Proto *p;
+
+ p = smalloc(sizeof(*p));
+ p->name = name;
+ p->stype = type;
+ p->ipproto = type+1; /* temporary */
+ p->nc = maxconv;
+ if(Fsproto(ipfs[0], p))
+ panic("can't newproto %s", name);
+}
+
+void
+ipinit(void)
+{
+ ipfs[0] = malloc(sizeof(Fs));
+ if(ipfs[0] == nil)
+ panic("no memory for IP stack");
+
+ newproto("udp", S_UDP, 64);
+ newproto("tcp", S_TCP, 2048);
+
+ fmtinstall('i', eipfmt);
+ fmtinstall('I', eipfmt);
+ fmtinstall('E', eipfmt);
+ fmtinstall('V', eipfmt);
+ fmtinstall('M', eipfmt);
+}
+
+Chan *
+ipattach(char *spec)
+{
+ Chan *c;
+
+ if(atoi(spec) != 0)
+ error("bad specification");
+
+ c = devattach('I', spec);
+ mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR);
+ c->dev = 0;
+
+ return c;
+}
+
+static Walkqid*
+ipwalk(Chan* c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, nil, 0, ipgen);
+}
+
+static int
+ipstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, ipgen);
+}
+
+static int m2p[] = {
+ 4,
+ 2,
+ 6,
+};
+
+static Chan *
+ipopen(Chan *c, int omode)
+{
+ Conv *cv, *nc;
+ Proto *p;
+ uchar raddr[IPaddrlen];
+ ushort rport;
+ int perm, sfd;
+ Fs *f;
+
+ perm = m2p[omode&3];
+
+ f = ipfs[c->dev];
+
+ switch(TYPE(c->qid)) {
+ default:
+ break;
+ case Qtopdir:
+ case Qprotodir:
+ case Qconvdir:
+ case Qstatus:
+ case Qremote:
+ case Qlocal:
+ case Qstats:
+ /* case Qipselftab: */
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+ case Qndb:
+ if(omode & (OWRITE|OTRUNC) && !iseve())
+ error(Eperm);
+ if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)){
+ f->ndb[0] = 0;
+ f->ndbvers++;
+ }
+ break;
+ case Qclone:
+ p = f->p[PROTO(c->qid)];
+ cv = protoclone(p, up->env->user, -1);
+ if(cv == 0)
+ error(Enodev);
+ mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE);
+ break;
+ case Qdata:
+ case Qctl:
+ p = f->p[PROTO(c->qid)];
+ qlock(&p->l);
+ cv = p->conv[CONV(c->qid)];
+ qlock(&cv->l);
+ if(waserror()){
+ qunlock(&cv->l);
+ qunlock(&p->l);
+ nexterror();
+ }
+ if((perm & (cv->perm>>6)) != perm) {
+ if(strcmp(up->env->user, cv->owner) != 0)
+ error(Eperm);
+ if((perm & cv->perm) != perm)
+ error(Eperm);
+ }
+ cv->inuse++;
+ if(cv->inuse == 1) {
+ kstrdup(&cv->owner, up->env->user);
+ cv->perm = 0660;
+ if(cv->sfd < 0)
+ cv->sfd = so_socket(p->stype);
+ }
+ poperror();
+ qunlock(&cv->l);
+ qunlock(&p->l);
+ break;
+ case Qlisten:
+ p = f->p[PROTO(c->qid)];
+ cv = p->conv[CONV(c->qid)];
+ if((perm & (cv->perm>>6)) != perm){
+ if(strcmp(up->env->user, cv->owner) != 0)
+ error(Eperm);
+ if((perm & cv->perm) != perm)
+ error(Eperm);
+ }
+
+ if(cv->state != Announced)
+ error("not announced");
+
+ qlock(&cv->listenq);
+ if(waserror()){
+ qunlock(&cv->listenq);
+ nexterror();
+ }
+
+ sfd = so_accept(cv->sfd, raddr, &rport);
+
+ nc = protoclone(p, up->env->user, sfd);
+ if(nc == 0) {
+ so_close(sfd);
+ error(Enodev);
+ }
+ memmove(nc->raddr, raddr, IPaddrlen);
+ nc->rport = rport;
+ setladdr(nc);
+ nc->state = Connected;
+ mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE);
+
+ poperror();
+ qunlock(&cv->listenq);
+ break;
+ }
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static void
+closeconv(Conv *cv)
+{
+ int fd;
+
+ qlock(&cv->l);
+
+ if(--cv->inuse > 0) {
+ qunlock(&cv->l);
+ return;
+ }
+
+ if(waserror()){
+ qunlock(&cv->l);
+ return;
+ }
+ kstrdup(&cv->owner, network);
+ cv->perm = 0660;
+ /* cv->p->close(cv); */
+ cv->state = Idle;
+ cv->restricted = 0;
+ fd = cv->sfd;
+ cv->sfd = -1;
+ if(fd >= 0)
+ so_close(fd);
+ poperror();
+ qunlock(&cv->l);
+}
+
+static void
+ipclose(Chan *c)
+{
+ Fs *f;
+
+ f = ipfs[c->dev];
+ switch(TYPE(c->qid)) {
+ case Qdata:
+ case Qctl:
+ if(c->flag & COPEN)
+ closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]);
+ break;
+ }
+}
+
+static long
+ipread(Chan *ch, void *a, long n, vlong off)
+{
+ int r;
+ Conv *c;
+ Proto *x;
+ char *p, *s;
+ Fs *f;
+ ulong offset = off;
+
+ f = ipfs[ch->dev];
+
+ p = a;
+ switch(TYPE(ch->qid)) {
+ default:
+ error(Eperm);
+ case Qprotodir:
+ case Qtopdir:
+ case Qconvdir:
+ return devdirread(ch, a, n, 0, 0, ipgen);
+ case Qarp:
+ error(Eperm); /* TO DO */
+ case Qndb:
+ return readstr(off, a, n, f->ndb);
+ case Qctl:
+ sprint(up->genbuf, "%lud", CONV(ch->qid));
+ return readstr(offset, p, n, up->genbuf);
+ case Qremote:
+ x = f->p[PROTO(ch->qid)];
+ c = x->conv[CONV(ch->qid)];
+ sprint(up->genbuf, "%I!%d\n", c->raddr, c->rport);
+ return readstr(offset, p, n, up->genbuf);
+ case Qlocal:
+ x = f->p[PROTO(ch->qid)];
+ c = x->conv[CONV(ch->qid)];
+ sprint(up->genbuf, "%I!%d\n", c->laddr, c->lport);
+ return readstr(offset, p, n, up->genbuf);
+ case Qstatus:
+ x = f->p[PROTO(ch->qid)];
+ c = x->conv[CONV(ch->qid)];
+ s = smalloc(Statelen);
+ if(waserror()){
+ free(s);
+ nexterror();
+ }
+ snprint(s, Statelen, "%s\n", ipstates[c->state]);
+ n = readstr(offset, p, n, s);
+ poperror();
+ free(s);
+ return n;
+ case Qdata:
+ x = f->p[PROTO(ch->qid)];
+ c = x->conv[CONV(ch->qid)];
+ if(c->sfd < 0)
+ error(Ehungup);
+ if(c->headers) {
+ if(n < c->headers)
+ error(Ebadarg);
+ p = a;
+ r = so_recv(c->sfd, p + c->headers, n - c->headers, p, c->headers);
+ if(r >= 0)
+ r += c->headers;
+ } else
+ r = so_recv(c->sfd, a, n, nil, 0);
+ if(r < 0)
+ oserror();
+ return r;
+ case Qstats:
+ error("stats not implemented");
+ return n;
+ }
+}
+
+static void
+setladdr(Conv *c)
+{
+ /* TO DO: this can't be right for hosts with several addresses before connect/accept */
+ so_getsockname(c->sfd, c->laddr, &c->lport);
+}
+
+/*
+ * pick a local port and set it
+ */
+static void
+setlport(Conv *c)
+{
+ uchar laddr[IPaddrlen];
+ ushort p;
+
+ so_bind(c->sfd, c->restricted, c->laddr, c->lport);
+ if(c->lport == 0 || ipcmp(c->laddr, IPnoaddr) == 0){
+ so_getsockname(c->sfd, laddr, &p);
+ if(c->lport == 0)
+ c->lport = p;
+ if(ipcmp(c->laddr, IPnoaddr) == 0)
+ memmove(c->laddr, laddr, sizeof laddr);
+ }
+}
+
+static int
+portno(char *p)
+{
+ long n;
+ char *e;
+
+ n = strtoul(p, &e, 0);
+ if(p == e)
+ error("non-numeric port number");
+ return n;
+}
+
+/*
+ * set a local address and port from a string of the form
+ * [address!]port[!r]
+ */
+static void
+setladdrport(Conv *c, char *str, int announcing)
+{
+ char *p;
+ int lport;
+
+ /*
+ * ignore restricted part if it exists. it's
+ * meaningless on local ports.
+ */
+ p = strchr(str, '!');
+ if(p != nil){
+ *p++ = 0;
+ if(strcmp(p, "r") == 0)
+ p = nil;
+ }
+
+ c->lport = 0;
+ if(p == nil){
+ if(announcing)
+ ipmove(c->laddr, IPnoaddr);
+ else if(0)
+ setladdr(c);
+ p = str;
+ } else {
+ if(strcmp(str, "*") == 0)
+ ipmove(c->laddr, IPnoaddr);
+ else if(parseip(c->laddr, str) == 0)
+ error("invalid IP address");
+ }
+
+ if(announcing && strcmp(p, "*") == 0){
+ if(!iseve())
+ error(Eperm);
+ c->lport = 0;
+ setlport(c);
+ return;
+ }
+
+ lport = portno(p);
+ if(lport <= 0)
+ c->lport = 0;
+ else
+ c->lport = lport;
+
+ setlport(c);
+}
+
+static char*
+setraddrport(Conv *c, char *str)
+{
+ char *p;
+
+ p = strchr(str, '!');
+ if(p == nil)
+ return "malformed address";
+ *p++ = 0;
+ if(parseip(c->raddr, str) == 0)
+ return "invalid IP address";
+ c->rport = portno(p);
+ p = strchr(p, '!');
+ if(p){
+ if(strstr(p, "!r") != nil)
+ c->restricted = 1;
+ }
+ return nil;
+}
+
+static void
+connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+ char *p;
+
+ USED(x);
+ if(c->state != Idle)
+ error(Econinuse);
+ c->state = Connecting;
+ c->cerr[0] = '\0';
+ switch(cb->nf) {
+ default:
+ error("bad args to connect");
+ case 2:
+ p = setraddrport(c, cb->f[1]);
+ if(p != nil)
+ error(p);
+ break;
+ case 3:
+ p = setraddrport(c, cb->f[1]);
+ if(p != nil)
+ error(p);
+ c->lport = portno(cb->f[2]);
+ setlport(c);
+ break;
+ }
+ qunlock(&c->l);
+ if(waserror()){
+ qlock(&c->l);
+ c->state = Connected; /* sic */
+ nexterror();
+ }
+ /* p = x->connect(c, cb->f, cb->nf); */
+ so_connect(c->sfd, c->raddr, c->rport);
+ qlock(&c->l);
+ poperror();
+ setladdr(c);
+ c->state = Connected;
+}
+
+static void
+announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+ if(c->state != Idle)
+ error(Econinuse);
+ c->state = Announcing;
+ c->cerr[0] = '\0';
+ ipmove(c->raddr, IPnoaddr);
+ c->rport = 0;
+ switch(cb->nf){
+ default:
+ error("bad args to announce");
+ case 2:
+ setladdrport(c, cb->f[1], 1);
+ break;
+ }
+ USED(x);
+ /* p = x->announce(c, cb->f, cb->nf); */
+ if(c->p->stype != S_UDP){
+ qunlock(&c->l);
+ if(waserror()){
+ c->state = Announced; /* sic */
+ qlock(&c->l);
+ nexterror();
+ }
+ so_listen(c->sfd);
+ qlock(&c->l);
+ poperror();
+ }
+ c->state = Announced;
+}
+
+static void
+bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+ USED(x);
+ switch(cb->nf){
+ default:
+ error("bad args to bind");
+ case 2:
+ setladdrport(c, cb->f[1], 0);
+ break;
+ }
+}
+
+static long
+ipwrite(Chan *ch, void *a, long n, vlong off)
+{
+ Conv *c;
+ Proto *x;
+ char *p;
+ Cmdbuf *cb;
+ Fs *f;
+
+ f = ipfs[ch->dev];
+
+ switch(TYPE(ch->qid)) {
+ default:
+ error(Eperm);
+ case Qdata:
+ x = f->p[PROTO(ch->qid)];
+ c = x->conv[CONV(ch->qid)];
+ if(c->sfd < 0)
+ error(Ehungup);
+ qlock(&c->wlock);
+ if(waserror()){
+ qunlock(&c->wlock);
+ nexterror();
+ }
+ if(c->headers) {
+ if(n < c->headers)
+ error(Eshort);
+ p = a;
+ n = so_send(c->sfd, p + c->headers, n - c->headers, p, c->headers);
+ if(n >= 0)
+ n += c->headers;
+ } else
+ n = so_send(c->sfd, a, n, nil, 0);
+ poperror();
+ qunlock(&c->wlock);
+ if(n < 0)
+ oserror();
+ break;
+ case Qarp:
+ return arpwrite(a, n);
+ case Qndb:
+ if(off > strlen(f->ndb))
+ error(Eio);
+ if(off+n >= sizeof(f->ndb)-1)
+ error(Eio);
+ memmove(f->ndb+off, a, n);
+ f->ndb[off+n] = 0;
+ f->ndbvers++;
+ f->ndbmtime = seconds();
+ break;
+ case Qctl:
+ x = f->p[PROTO(ch->qid)];
+ c = x->conv[CONV(ch->qid)];
+ cb = parsecmd(a, n);
+ qlock(&c->l);
+ if(waserror()){
+ qunlock(&c->l);
+ free(cb);
+ nexterror();
+ }
+ if(cb->nf < 1)
+ error("short control request");
+ if(strcmp(cb->f[0], "connect") == 0)
+ connectctlmsg(x, c, cb);
+ else if(strcmp(cb->f[0], "announce") == 0)
+ announcectlmsg(x, c, cb);
+ else if(strcmp(cb->f[0], "bind") == 0)
+ bindctlmsg(x, c, cb);
+ else if(strcmp(cb->f[0], "ttl") == 0){
+ /* ignored */
+ } else if(strcmp(cb->f[0], "tos") == 0){
+ /* ignored */
+ } else if(strcmp(cb->f[0], "ignoreadvice") == 0){
+ /* ignored */
+ } else if(strcmp(cb->f[0], "headers4") == 0){
+ if(c->p->stype != S_UDP)
+ error(Enoctl);
+ c->headers = OUdphdrlenv4;
+ } else if(strcmp(cb->f[0], "oldheaders") == 0){
+ if(c->p->stype != S_UDP)
+ error(Enoctl);
+ c->headers = OUdphdrlen;
+ } else if(strcmp(cb->f[0], "headers") == 0){
+ if(c->p->stype != S_UDP)
+ error(Enoctl);
+ c->headers = Udphdrlen;
+ } else if(strcmp(cb->f[0], "hangup") == 0){
+ if(c->p->stype != S_TCP)
+ error(Enoctl);
+ qunlock(&c->l);
+ if(waserror()){
+ qlock(&c->l);
+ nexterror();
+ }
+ /* TO DO: check fd status if socket close/hangup interrupted */
+ if(c->sfd >= 0 && so_hangup(c->sfd, 1) < 0)
+ oserror();
+ qlock(&c->l);
+ poperror();
+ c->sfd = -1;
+ c->state = Hungup;
+ } else if(strcmp(cb->f[0], "keepalive") == 0){
+ if(c->p->stype != S_TCP)
+ error(Enoctl);
+ if(c->sfd < 0)
+ error("not connected");
+ so_keepalive(c->sfd, cb->nf>1? atoi(cb->f[1]): 0);
+ } else
+ error(Enoctl);
+ poperror();
+ qunlock(&c->l);
+ free(cb);
+ break;
+ }
+ return n;
+}
+
+static int
+ipwstat(Chan *c, uchar *dp, int n)
+{
+ Dir *d;
+ Conv *cv;
+ Proto *p;
+ Fs *f;
+
+ f = ipfs[c->dev];
+ switch(TYPE(c->qid)) {
+ default:
+ error(Eperm);
+ break;
+ case Qctl:
+ case Qdata:
+ break;
+ }
+
+ d = smalloc(sizeof(*d)+n);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ n = convM2D(dp, n, d, (char*)&d[1]);
+ if(n == 0)
+ error(Eshortstat);
+ p = f->p[PROTO(c->qid)];
+ cv = p->conv[CONV(c->qid)];
+ if(!iseve() && strcmp(up->env->user, cv->owner) != 0)
+ error(Eperm);
+ if(!emptystr(d->uid))
+ kstrdup(&cv->owner, d->uid);
+ if(d->mode != ~0UL)
+ cv->perm = d->mode & 0777;
+ poperror();
+ free(d);
+ return n;
+}
+
+static Conv*
+protoclone(Proto *p, char *user, int nfd)
+{
+ Conv *c, **pp, **ep, **np;
+ int maxconv;
+
+ c = 0;
+ qlock(&p->l);
+ if(waserror()) {
+ qunlock(&p->l);
+ nexterror();
+ }
+ ep = &p->conv[p->nc];
+ for(pp = p->conv; pp < ep; pp++) {
+ c = *pp;
+ if(c == 0) {
+ c = newconv(p, pp);
+ break;
+ }
+ if(canqlock(&c->l)){
+ if(c->inuse == 0)
+ break;
+ qunlock(&c->l);
+ }
+ }
+ if(pp >= ep) {
+ if(p->nc >= MAXCONV) {
+ qunlock(&p->l);
+ poperror();
+ return 0;
+ }
+ maxconv = 2 * p->nc;
+ if(maxconv > MAXCONV)
+ maxconv = MAXCONV;
+ np = realloc(p->conv, sizeof(Conv*) * maxconv);
+ if(np == nil)
+ error(Enomem);
+ p->conv = np;
+ pp = &p->conv[p->nc];
+ memset(pp, 0, sizeof(Conv*)*(maxconv - p->nc));
+ p->nc = maxconv;
+ c = newconv(p, pp);
+ }
+
+ c->inuse = 1;
+ kstrdup(&c->owner, user);
+ c->perm = 0660;
+ c->state = Idle;
+ ipmove(c->laddr, IPnoaddr);
+ ipmove(c->raddr, IPnoaddr);
+ c->lport = 0;
+ c->rport = 0;
+ c->restricted = 0;
+ c->headers = 0;
+ c->sfd = nfd;
+ if(nfd == -1)
+ c->sfd = so_socket(p->stype);
+
+ qunlock(&c->l);
+ qunlock(&p->l);
+ poperror();
+ return c;
+}
+
+static Conv*
+newconv(Proto *p, Conv **pp)
+{
+ Conv *c;
+
+ *pp = c = malloc(sizeof(Conv));
+ if(c == 0)
+ error(Enomem);
+ qlock(&c->l);
+ c->inuse = 1;
+ c->p = p;
+ c->x = pp - p->conv;
+ p->ac++;
+ return c;
+}
+
+int
+arpwrite(char *s, int len)
+{
+ int n;
+ char *f[4], buf[256];
+
+ if(len >= sizeof(buf))
+ len = sizeof(buf)-1;
+ memmove(buf, s, len);
+ buf[len] = 0;
+ if(len > 0 && buf[len-1] == '\n')
+ buf[len-1] = 0;
+
+ n = getfields(buf, f, 4, 1, " ");
+ if(strcmp(f[0], "add") == 0) {
+ if(n == 3) {
+ arpadd(f[1], f[2], n);
+ return len;
+ }
+ }
+ error("bad arp request");
+
+ return len;
+}
+
+Dev ipdevtab = {
+ 'I',
+ "ip",
+
+ ipinit,
+ ipattach,
+ ipwalk,
+ ipstat,
+ ipopen,
+ devcreate,
+ ipclose,
+ ipread,
+ devbread,
+ ipwrite,
+ devbwrite,
+ devremove,
+ ipwstat
+};
+
+int
+Fsproto(Fs *f, Proto *p)
+{
+ if(f->np >= Maxproto)
+ return -1;
+
+ p->f = f;
+
+ if(p->ipproto > 0){
+ if(f->t2p[p->ipproto] != nil)
+ return -1;
+ f->t2p[p->ipproto] = p;
+ }
+
+ p->qid.type = QTDIR;
+ p->qid.path = QID(f->np, 0, Qprotodir);
+ p->conv = malloc(sizeof(Conv*)*(p->nc+1));
+ if(p->conv == nil)
+ panic("Fsproto");
+
+ p->x = f->np;
+ f->p[f->np++] = p;
+
+ return 0;
+}
+
+/*
+ * return true if this protocol is
+ * built in
+ */
+int
+Fsbuiltinproto(Fs* f, uchar proto)
+{
+ return f->t2p[proto] != nil;
+}
--- /dev/null
+++ b/emu/port/devlogfs.c
@@ -1,0 +1,1522 @@
+#ifndef EMU
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#else
+#include "error.h"
+#endif
+#include <dat.h>
+#include <fns.h>
+#include <kernel.h>
+#include <logfs.h>
+#include <nandfs.h>
+
+#ifndef EMU
+#define Sleep sleep
+#define Wakeup wakeup
+#endif
+
+#ifndef offsetof
+#define offsetof(T,X) ((ulong)&(((T*)0)->X))
+#endif
+
+typedef struct Devlogfs Devlogfs;
+typedef struct DevlogfsSession DevlogfsSession;
+
+//#define CALLTRACE
+
+enum {
+ DEVLOGFSDEBUG = 0,
+ DEVLOGFSIODEBUG = 0,
+ DEVLOGFSBAD = 1,
+};
+
+enum {
+ Qdir,
+ Qctl,
+ Qusers,
+ Qdump,
+ Qfs,
+ Qfsboot,
+ Qend,
+};
+
+typedef enum DevlogfsServerState { Closed, BootOpen, NeedVersion, NeedAttach, Attached, Hungup } DevlogfsServerState;
+
+struct Devlogfs {
+ QLock qlock;
+ Ref ref;
+ int instance;
+ int trace; /* (debugging) trace of read/write actions */
+ int nand;
+ char *name;
+ char *device;
+ char *filename[Qend - Qfs];
+ LogfsLowLevel *ll;
+ Chan *flash, *flashctl;
+ QLock bootqlock;
+ int logfstrace;
+ LogfsBoot *lb;
+ /* stuff for server */
+ ulong openflags;
+ Fcall in;
+ Fcall out;
+ int reading;
+ DevlogfsServerState state;
+ Rendez readrendez;
+ Rendez writerendez;
+ uint readcount;
+ ulong readbufsize;
+ uchar *readbuf;
+ uchar *readp;
+ LogfsServer *server;
+ Devlogfs *next;
+};
+
+#define MAXMSIZE 8192
+
+static struct {
+ RWlock rwlock; /* rlock when walking, wlock when changing */
+ QLock configqlock; /* serialises addition of new configurations */
+ Devlogfs *head;
+ char *defname;
+} devlogfslist;
+
+static LogfsIdentityStore *is;
+
+#ifndef EMU
+char Eunknown[] = "unknown user or group id";
+#endif
+
+static void devlogfsfree(Devlogfs*);
+
+#define SPLITPATH(path, qtype, instance, qid, qt) { instance = path >> 4; qid = path & 0xf; qt = qtype & QTDIR; }
+#define DATAQID(q, qt) (!(qt) && (q) >= Qfs && (q) < Qend)
+#define MKPATH(instance, qid) ((instance << 4) | qid)
+
+#define PREFIX "logfs"
+
+static char *devlogfsprefix = PREFIX;
+static char *devlogfsctlname = PREFIX "ctl";
+static char *devlogfsusersname = PREFIX "users";
+static char *devlogfsdumpname = PREFIX "dump";
+static char *devlogfsbootsuffix = "boot";
+static char *devlogfs9pversion = "9P2000";
+
+enum {
+ Toshiba = 0x98,
+ Samsung = 0xec,
+};
+
+static struct {
+ uchar manufacturer;
+ uchar device;
+} nandtab[] = {
+ { 0, 0xe6 },
+ { 0, 0xea },
+ { 0, 0xe3 },
+ { 0, 0xe5 },
+ { 0, 0x73 },
+ { 0, 0x75 },
+ { 0, 0x76 },
+};
+
+static void
+errorany(char *errmsg)
+{
+ if (errmsg)
+ error(errmsg);
+}
+
+static void *
+emalloc(ulong size)
+{
+ void *p;
+ p = logfsrealloc(nil, size);
+ if (p == nil)
+ error(Enomem);
+ return p;
+}
+
+static char *
+estrdup(char *q)
+{
+ void *p;
+ if (q == nil)
+ return nil;
+ p = logfsrealloc(nil, strlen(q) + 1);
+ if (p == nil)
+ error(Enomem);
+ return strcpy(p, q);
+}
+
+static char *
+estrconcat(char *a, ...)
+{
+ va_list l;
+ char *p, *r;
+ int t;
+
+ t = strlen(a);
+ va_start(l, a);
+ while ((p = va_arg(l, char *)) != nil)
+ t += strlen(p);
+
+ r = logfsrealloc(nil, t + 1);
+ if (r == nil)
+ error(Enomem);
+
+ strcpy(r, a);
+ va_start(l, a);
+ while ((p = va_arg(l, char *)) != nil)
+ strcat(r, p);
+
+ va_end(l);
+
+ return r;
+}
+
+static int
+gen(Chan *c, int i, Dir *dp, int lockit)
+{
+ Devlogfs *l;
+ long size;
+ Qid qid;
+ qid.vers = 0;
+ qid.type = 0;
+
+ if (i + Qctl < Qfs) {
+ switch (i + Qctl) {
+ case Qctl:
+ qid.path = Qctl;
+ devdir(c, qid, devlogfsctlname, 0, eve, 0666, dp);
+ return 1;
+ case Qusers:
+ qid.path = Qusers;
+ devdir(c, qid, devlogfsusersname, 0, eve, 0444, dp);
+ return 1;
+ case Qdump:
+ qid.path = Qdump;
+ devdir(c, qid, devlogfsdumpname, 0, eve, 0444, dp);
+ return 1;
+ }
+ }
+
+ i -= Qfs - Qctl;
+
+ if (lockit)
+ rlock(&devlogfslist.rwlock);
+
+ if (waserror()) {
+ if (lockit)
+ runlock(&devlogfslist.rwlock);
+ nexterror();
+ }
+
+ for (l = devlogfslist.head; l; l = l->next) {
+ if (i < Qend - Qfs)
+ break;
+ i -= Qend - Qfs;
+ }
+
+ if (l == nil) {
+ poperror();
+ if (lockit)
+ runlock(&devlogfslist.rwlock);
+ return -1;
+ }
+
+ switch (Qfs + i) {
+ case Qfsboot:
+ size = l->lb ? logfsbootgetsize(l->lb) : 0;
+ break;
+ default:
+ size = 0;
+ break;
+ }
+ /* perhaps the user id should come from the underlying file */
+ qid.path = MKPATH(l->instance, Qfs + i);
+ devdir(c, qid, l->filename[i], size, eve, 0666, dp);
+
+ poperror();
+ if (lockit)
+ runlock(&devlogfslist.rwlock);
+
+ return 1;
+}
+
+static int
+devlogfsgen(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+ USED(n);
+ USED(tab);
+ USED(ntab);
+ return gen(c, i, dp, 1);
+}
+
+static int
+devlogfsgennolock(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+ USED(n);
+ USED(tab);
+ USED(ntab);
+ return gen(c, i, dp, 0);
+}
+
+/* called under lock */
+static Devlogfs *
+devlogfsfind(int instance)
+{
+ Devlogfs *l;
+
+ for (l = devlogfslist.head; l; l = l->next)
+ if (l->instance == instance)
+ break;
+ return l;
+}
+
+static Devlogfs *
+devlogfsget(int instance)
+{
+ Devlogfs *l;
+ rlock(&devlogfslist.rwlock);
+ for (l = devlogfslist.head; l; l = l->next)
+ if (l->instance == instance)
+ break;
+ if (l)
+ incref(&l->ref);
+ runlock(&devlogfslist.rwlock);
+ return l;
+}
+
+static Devlogfs *
+devlogfsfindbyname(char *name)
+{
+ Devlogfs *l;
+
+ rlock(&devlogfslist.rwlock);
+ for (l = devlogfslist.head; l; l = l->next)
+ if (strcmp(l->name, name) == 0)
+ break;
+ runlock(&devlogfslist.rwlock);
+ return l;
+}
+
+static Devlogfs *
+devlogfssetdefname(char *name)
+{
+ Devlogfs *l;
+ char *searchname;
+ wlock(&devlogfslist.rwlock);
+ if (waserror()) {
+ wunlock(&devlogfslist.rwlock);
+ nexterror();
+ }
+ if (name == nil)
+ searchname = devlogfslist.defname;
+ else
+ searchname = name;
+ for (l = devlogfslist.head; l; l = l->next)
+ if (strcmp(l->name, searchname) == 0)
+ break;
+ if (l == nil) {
+ logfsfreemem(devlogfslist.defname);
+ devlogfslist.defname = nil;
+ }
+ else if (name) {
+ if (devlogfslist.defname) {
+ logfsfreemem(devlogfslist.defname);
+ devlogfslist.defname = nil;
+ }
+ devlogfslist.defname = estrdup(name);
+ }
+ poperror();
+ wunlock(&devlogfslist.rwlock);
+ return l;
+}
+
+static Chan *
+devlogfskopen(char *name, char *suffix, int mode)
+{
+ Chan *c;
+ char *fn;
+ int fd;
+
+ fn = estrconcat(name, suffix, 0);
+ fd = kopen(fn, mode);
+ logfsfreemem(fn);
+ if (fd < 0)
+ error(up->env->errstr);
+ c = fdtochan(up->env->fgrp, fd, mode, 0, 1);
+ kclose(fd);
+ return c;
+}
+
+static char *
+xread(void *a, void *buf, long nbytes, ulong offset)
+{
+ Devlogfs *l = a;
+ long rv;
+
+ if (DEVLOGFSIODEBUG || l->trace)
+ print("devlogfs: %s: read(0x%lux, %ld)\n", l->device, offset, nbytes);
+ l->flash->offset = offset;
+ rv = kchanio(l->flash, buf, nbytes, OREAD);
+ if (rv < 0) {
+ print("devlogfs: %s: flash read error: %s\n", l->device, up->env->errstr);
+ return up->env->errstr;
+ }
+ if (rv != nbytes) {
+ print("devlogfs: %s: short flash read: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes);
+ return "short read";
+ }
+ return nil;
+}
+
+static char *
+xwrite(void *a, void *buf, long nbytes, ulong offset)
+{
+ Devlogfs *l = a;
+ long rv;
+
+ if (DEVLOGFSIODEBUG || l->trace)
+ print("devlogfs: %s: write(0x%lux, %ld)\n", l->device, offset, nbytes);
+ l->flash->offset = offset;
+ rv = kchanio(l->flash, buf, nbytes, OWRITE);
+ if (rv < 0) {
+ print("devlogfs: %s: flash write error: %s\n", l->device, up->env->errstr);
+ return up->env->errstr;
+ }
+ if (rv != nbytes) {
+ print("devlogfs: %s: short flash write: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes);
+ return "short write";
+ }
+ return nil;
+}
+
+static char *
+xerase(void *a, long address)
+{
+ Devlogfs *l = a;
+ char cmd[40];
+
+ if (DEVLOGFSIODEBUG || l->trace)
+ print("devlogfs: %s: erase(0x%lux)\n", l->device, address);
+ snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", address);
+ if (kchanio(l->flashctl, cmd, strlen(cmd), OWRITE) <= 0) {
+ print("devlogfs: %s: flash erase error: %s\n", l->device, up->env->errstr);
+ return up->env->errstr;
+ }
+ return nil;
+}
+
+static char *
+xsync(void *a)
+{
+ Devlogfs *l = a;
+ uchar statbuf[STATFIXLEN];
+
+ if (DEVLOGFSIODEBUG || l->trace)
+ print("devlogfs: %s: sync()\n", l->device);
+ memset(statbuf, 0xff, sizeof(statbuf));
+ memset(statbuf + STATFIXLEN - 8, 0x00, 8);
+ PBIT16(statbuf, sizeof(statbuf) - BIT16SZ);
+ if (kwstat(l->device, statbuf, sizeof(statbuf)) < 0)
+ return up->env->errstr;
+ return nil;
+}
+
+//#define LEAKHUNT
+#ifdef LEAKHUNT
+#define MAXLIVE 2000
+typedef struct Live {
+ void *p;
+ int freed;
+ ulong callerpc;
+} Live;
+
+static Live livemem[MAXLIVE];
+
+static void
+leakalloc(void *p, ulong callerpc)
+{
+ int x;
+ int use = -1;
+ for (x = 0; x < MAXLIVE; x++) {
+ if (livemem[x].p == p) {
+ if (!livemem[x].freed)
+ print("leakalloc: unexpected realloc of 0x%.8lux from 0x%.8lux\n", p, callerpc);
+// else
+// print("leakalloc: reusing address 0x%.8lux from 0x%.8lux\n", p, callerpc);
+ livemem[x].freed = 0;
+ livemem[x].callerpc = callerpc;
+ return;
+ }
+ else if (use < 0 && livemem[x].p == 0)
+ use = x;
+ }
+ if (use < 0)
+ panic("leakalloc: too many live entries");
+ livemem[use].p = p;
+ livemem[use].freed = 0;
+ livemem[use].callerpc = callerpc;
+}
+
+static void
+leakaudit(void)
+{
+ int x;
+ for (x = 0; x < MAXLIVE; x++) {
+ if (livemem[x].p && !livemem[x].freed)
+ print("leakaudit: 0x%.8lux from 0x%.8lux\n", livemem[x].p, livemem[x].callerpc);
+ }
+}
+
+static void
+leakfree(void *p, ulong callerpc)
+{
+ int x;
+ if (p == nil)
+ return;
+ for (x = 0; x < MAXLIVE; x++) {
+ if (livemem[x].p == p) {
+ if (livemem[x].freed)
+ print("leakfree: double free of 0x%.8lux from 0x%.8lux, originally by 0x%.8lux\n",
+ p, callerpc, livemem[x].callerpc);
+ livemem[x].freed = 1;
+ livemem[x].callerpc = callerpc;
+ return;
+ }
+ }
+ print("leakfree: free of unalloced address 0x%.8lux from 0x%.8lux\n", p, callerpc);
+ leakaudit();
+}
+
+static void
+leakrealloc(void *newp, void *oldp, ulong callerpc)
+{
+ leakfree(oldp, callerpc);
+ leakalloc(newp, callerpc);
+}
+#endif
+
+
+#ifdef LEAKHUNT
+static void *_realloc(void *p, ulong size, ulong callerpc)
+#else
+void *
+logfsrealloc(void *p, ulong size)
+#endif
+{
+ void *q;
+ ulong osize;
+ if (waserror()) {
+ print("wobbly thrown in memory allocator: %s\n", up->env->errstr);
+ nexterror();
+ }
+ if (p == nil) {
+ q = smalloc(size);
+ poperror();
+#ifdef LEAKHUNT
+ leakrealloc(q, nil, callerpc);
+#endif
+ return q;
+ }
+ q = realloc(p, size);
+ if (q) {
+ poperror();
+#ifdef LEAKHUNT
+ leakrealloc(q, p, callerpc);
+#endif
+ return q;
+ }
+ q = smalloc(size);
+ osize = msize(p);
+ if (osize > size)
+ osize = size;
+ memmove(q, p, osize);
+ free(p);
+ poperror();
+#ifdef LEAKHUNT
+ leakrealloc(q, p, callerpc);
+#endif
+ return q;
+}
+
+#ifdef LEAKHUNT
+void *
+logfsrealloc(void *p, ulong size)
+{
+ return _realloc(p, size, getcallerpc(&p));
+}
+
+void *
+nandfsrealloc(void *p, ulong size)
+{
+ return _realloc(p, size, getcallerpc(&p));
+}
+#else
+void *
+nandfsrealloc(void *p, ulong size)
+{
+ return logfsrealloc(p, size);
+}
+#endif
+
+void
+logfsfreemem(void *p)
+{
+#ifdef LEAKHUNT
+ leakfree(p, getcallerpc(&p));
+#endif
+ free(p);
+}
+
+void
+nandfsfreemem(void *p)
+{
+#ifdef LEAKHUNT
+ leakfree(p, getcallerpc(&p));
+#endif
+ free(p);
+}
+
+static Devlogfs *
+devlogfsconfig(char *name, char *device)
+{
+ Devlogfs *newl, *l;
+ int i;
+ int n;
+ char buf[100], *fields[8];
+ long rawblocksize, rawsize;
+
+ newl = nil;
+
+ qlock(&devlogfslist.configqlock);
+
+ if (waserror()) {
+ qunlock(&devlogfslist.configqlock);
+ devlogfsfree(newl);
+ nexterror();
+ }
+
+ rlock(&devlogfslist.rwlock);
+ for (l = devlogfslist.head; l; l = l->next)
+ if (strcmp(l->name, name) == 0) {
+ runlock(&devlogfslist.rwlock);
+ error(Einuse);
+ }
+
+ /* horrid n^2 solution to finding a unique instance number */
+
+ for (i = 0;; i++) {
+ for (l = devlogfslist.head; l; l = l->next)
+ if (l->instance == i)
+ break;
+ if (l == nil)
+ break;
+ }
+ runlock(&devlogfslist.rwlock);
+
+ newl = emalloc(sizeof(Devlogfs));
+ newl->instance = i;
+ newl->name = estrdup(name);
+ newl->device = estrdup(device);
+ newl->filename[Qfs - Qfs] = estrconcat(devlogfsprefix, name, nil);
+ newl->filename[Qfsboot - Qfs] = estrconcat(devlogfsprefix, name, devlogfsbootsuffix, nil);
+ newl->flash = devlogfskopen(device, nil, ORDWR);
+ newl->flashctl = devlogfskopen(device, "ctl", ORDWR);
+ newl->flashctl->offset = 0;
+ if ((n = kchanio(newl->flashctl, buf, sizeof(buf), OREAD)) <= 0) {
+ print("devlogfsconfig: read ctl failed: %s\n", up->env->errstr);
+ error(up->env->errstr);
+ }
+
+ if (n >= sizeof(buf))
+ n = sizeof(buf) - 1;
+ buf[n] = 0;
+ n = getfields(buf, fields, nelem(fields), 1, " \t\n");
+ newl->nand = 0;
+ if (n >= 2) {
+ /* detect NAND devices, and learn parameters from there */
+ ulong manufacturer = strtoul(fields[0], nil, 16);
+ ulong device = strtoul(fields[1], nil, 16);
+ int d;
+
+ for (d = 0; d < sizeof(nandtab) / sizeof(nandtab[0]); d++) {
+ if ((nandtab[d].manufacturer == manufacturer
+ && nandtab[d].device == device)
+ || (nandtab[d].manufacturer == 0
+ && (manufacturer == Toshiba || manufacturer == Samsung)
+ && nandtab[d].device == device))
+ {
+ if (DEVLOGFSDEBUG)
+ print("devlogfsconfig: nand device detected\n");
+ newl->nand = 1;
+ break;
+ }
+ }
+ }
+ if (n < 4)
+ error("unknown erase size");
+ rawblocksize = strtol(fields[5], nil, 0);
+ rawsize = strtol(fields[4], nil, 0)-strtol(fields[3], nil, 0);
+ if (newl->nand == 0)
+ error("only NAND supported at the moment");
+ errorany(nandfsinit(newl, rawsize, rawblocksize, xread, xwrite, xerase, xsync, &newl->ll));
+ wlock(&devlogfslist.rwlock);
+ newl->next = devlogfslist.head;
+ devlogfslist.head = newl;
+ logfsfreemem(devlogfslist.defname);
+ devlogfslist.defname = nil;
+ if (waserror()) {
+ }
+ else {
+ devlogfslist.defname = estrdup(name);
+ poperror();
+ }
+ wunlock(&devlogfslist.rwlock);
+ poperror();
+ qunlock(&devlogfslist.configqlock);
+ return newl;
+}
+
+void
+devlogfsunconfig(Devlogfs *devlogfs)
+{
+ Devlogfs **lp;
+
+ qlock(&devlogfslist.configqlock);
+
+ if (waserror()) {
+ qunlock(&devlogfslist.configqlock);
+ nexterror();
+ }
+
+ wlock(&devlogfslist.rwlock);
+
+ if (waserror()) {
+ wunlock(&devlogfslist.rwlock);
+ nexterror();
+ }
+
+ for (lp = &devlogfslist.head; *lp && (*lp) != devlogfs; lp = &(*lp)->next)
+ ;
+ if (*lp == nil) {
+ if (DEVLOGFSBAD)
+ print("devlogfsunconfig: not in list\n");
+ }
+ else
+ *lp = devlogfs->next;
+
+ poperror();
+ wunlock(&devlogfslist.rwlock);
+
+ /* now invisible to the naked eye */
+ devlogfsfree(devlogfs);
+ poperror();
+ qunlock(&devlogfslist.configqlock);
+}
+
+static void
+devlogfsllopen(Devlogfs *l)
+{
+ qlock(&l->qlock);
+ if (waserror()) {
+ qunlock(&l->qlock);
+ nexterror();
+ }
+ if (l->lb == nil)
+ errorany(logfsbootopen(l->ll, 0, 0, l->logfstrace, 1, &l->lb));
+ l->state = BootOpen;
+ poperror();
+ qunlock(&l->qlock);
+}
+
+static void
+devlogfsllformat(Devlogfs *l, long bootsize)
+{
+ qlock(&l->qlock);
+ if (waserror()) {
+ qunlock(&l->qlock);
+ nexterror();
+ }
+ if (l->lb == nil)
+ errorany(logfsformat(l->ll, 0, 0, bootsize, l->logfstrace));
+ poperror();
+ qunlock(&l->qlock);
+}
+
+static Chan *
+devlogfsattach(char *spec)
+{
+ Chan *c;
+#ifdef CALLTRACE
+ print("devlogfsattach(spec = %s) - start\n", spec);
+#endif
+ /* create the identity store on first attach */
+ if (is == nil)
+ errorany(logfsisnew(&is));
+ c = devattach(0x29f, spec);
+// c = devattach(L'ʟ', spec);
+#ifdef CALLTRACE
+ print("devlogfsattach(spec = %s) - return %.8lux\n", spec, (ulong)c);
+#endif
+ return c;
+}
+
+static Walkqid*
+devlogfswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int instance, qid, qt, clone;
+ Walkqid *wq;
+
+#ifdef CALLTRACE
+ print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - start\n",
+ (ulong)c, (ulong)nc, (ulong)name, nname);
+#endif
+ clone = 0;
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0;
+ SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+ if(DATAQID(qid, qt))
+ nc->aux = devlogfsget(instance);
+ clone = 1;
+ }
+ wq = devwalk(c, nc, name, nname, 0, 0, devlogfsgen);
+ if (wq == nil || wq->nqid < nname) {
+ if(clone)
+ cclose(nc);
+ }
+ else if (clone) {
+ wq->clone = nc;
+ nc->type = c->type;
+ }
+#ifdef CALLTRACE
+ print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - return\n",
+ (ulong)c, (ulong)nc, (ulong)name, nname);
+#endif
+ return wq;
+}
+
+static int
+devlogfsstat(Chan *c, uchar *dp, int n)
+{
+#ifdef CALLTRACE
+ print("devlogfsstat(c = 0x%.8lux, dp = 0x%.8lux n= %d)\n",
+ (ulong)c, (ulong)dp, n);
+#endif
+ return devstat(c, dp, n, 0, 0, devlogfsgen);
+}
+
+static Chan*
+devlogfsopen(Chan *c, int omode)
+{
+ int instance, qid, qt;
+
+ omode = openmode(omode);
+ SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+#ifdef CALLTRACE
+ print("devlogfsopen(c = 0x%.8lux, omode = %o, instance = %d, qid = %d, qt = %d)\n",
+ (ulong)c, omode, instance, qid, qt);
+#endif
+
+
+ rlock(&devlogfslist.rwlock);
+ if (waserror()) {
+ runlock(&devlogfslist.rwlock);
+#ifdef CALLTRACE
+ print("devlogfsopen(c = 0x%.8lux, omode = %o) - error %s\n", (ulong)c, omode, up->env->errstr);
+#endif
+ nexterror();
+ }
+
+ if (DATAQID(qid, qt)) {
+ Devlogfs *d;
+ d = devlogfsfind(instance);
+ if (d == nil)
+ error(Enodev);
+ if (strcmp(up->env->user, eve) != 0)
+ error(Eperm);
+ if (qid == Qfs && d->state != BootOpen)
+ error(Eperm);
+ if (d->server == nil) {
+ errorany(logfsservernew(d->lb, d->ll, is, d->openflags, d->logfstrace, &d->server));
+ d->state = NeedVersion;
+ }
+ c = devopen(c, omode, 0, 0, devlogfsgennolock);
+ incref(&d->ref);
+ c->aux = d;
+ }
+ else if (qid == Qctl || qid == Qusers) {
+ if (strcmp(up->env->user, eve) != 0)
+ error(Eperm);
+ c = devopen(c, omode, 0, 0, devlogfsgennolock);
+ }
+ else
+ c = devopen(c, omode, 0, 0, devlogfsgennolock);
+ poperror();
+ runlock(&devlogfslist.rwlock);
+#ifdef CALLTRACE
+ print("devlogfsopen(c = 0x%.8lux, omode = %o) - return\n", (ulong)c, omode);
+#endif
+ return c;
+}
+
+static void
+devlogfsclose(Chan *c)
+{
+ int instance, qid, qt;
+#ifdef CALLTRACE
+ print("devlogfsclose(c = 0x%.8lux)\n", (ulong)c);
+#endif
+ SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+ USED(instance);
+ if(DATAQID(qid, qt) && (c->flag & COPEN) != 0) {
+ Devlogfs *d;
+ d = c->aux;
+ qlock(&d->qlock);
+ if (qid == Qfs && d->state == Attached) {
+ logfsserverflush(d->server);
+ logfsserverfree(&d->server);
+ d->state = BootOpen;
+ }
+ qunlock(&d->qlock);
+ decref(&d->ref);
+ }
+#ifdef CALLTRACE
+ print("devlogfsclose(c = 0x%.8lux) - return\n", (ulong)c);
+#endif
+}
+
+typedef char *(SMARTIOFN)(void *magic, void *buf, long n, ulong offset, int write);
+
+void
+smartio(SMARTIOFN *io, void *magic, void *buf, long n, ulong offset, long blocksize, int write)
+{
+ void *tmp = nil;
+ ulong blocks, toread;
+
+ if (waserror()) {
+ logfsfreemem(tmp);
+ nexterror();
+ }
+ if (offset % blocksize) {
+ ulong aoffset;
+ int tmpoffset;
+ int tocopy;
+
+ if (tmp == nil)
+ tmp = emalloc(blocksize);
+ aoffset = offset / blocksize;
+ aoffset *= blocksize;
+ errorany((*io)(magic, tmp, blocksize, aoffset, 0));
+ tmpoffset = offset - aoffset;
+ tocopy = blocksize - tmpoffset;
+ if (tocopy > n)
+ tocopy = n;
+ if (write) {
+ memmove((uchar *)tmp + tmpoffset, buf, tocopy);
+ errorany((*io)(magic, tmp, blocksize, aoffset, 1));
+ }
+ else
+ memmove(buf, (uchar *)tmp + tmpoffset, tocopy);
+ buf = (uchar *)buf + tocopy;
+ n -= tocopy;
+ offset = aoffset + blocksize;
+ }
+ blocks = n / blocksize;
+ toread = blocks * blocksize;
+ errorany((*io)(magic, buf, toread, offset, write));
+ buf = (uchar *)buf + toread;
+ n -= toread;
+ offset += toread;
+ if (n) {
+ if (tmp == nil)
+ tmp = emalloc(blocksize);
+ errorany((*io)(magic, tmp, blocksize, offset, 0));
+ if (write) {
+ memmove(tmp, buf, n);
+ errorany((*io)(magic, tmp, blocksize, offset, 1));
+ }
+ memmove(buf, tmp, n);
+ }
+ poperror();
+ logfsfreemem(tmp);
+}
+
+static int
+readok(void *a)
+{
+ Devlogfs *d = a;
+ return d->reading;
+}
+
+static int
+writeok(void *a)
+{
+ Devlogfs *d = a;
+ return !d->reading;
+}
+
+long
+devlogfsserverread(Devlogfs *d, void *buf, long n)
+{
+ if (d->state == Hungup)
+ error(Ehungup);
+ Sleep(&d->readrendez, readok, d);
+ if (n > d->readcount)
+ n = d->readcount;
+ memmove(buf, d->readp, n);
+ d->readp += n;
+ d->readcount -= n;
+ if (d->readcount == 0) {
+ d->reading = 0;
+ Wakeup(&d->writerendez);
+ }
+ return n;
+}
+
+static void
+reply(Devlogfs *d)
+{
+ d->readp = d->readbuf;
+ d->readcount = convS2M(&d->out, d->readp, d->readbufsize);
+//print("reply is %d bytes\n", d->readcount);
+ if (d->readcount == 0)
+ panic("logfs: reply: did not fit\n");
+ d->reading = 1;
+ Wakeup(&d->readrendez);
+}
+
+static void
+rerror(Devlogfs *d, char *ename)
+{
+ d->out.type = Rerror;
+ d->out.ename = ename;
+ reply(d);
+}
+
+static struct {
+ QLock qlock;
+ int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen);
+ void *magic;
+ Devlogfs *d;
+ int line;
+} dump;
+
+static void *
+extentdumpinit(Devlogfs *d, int argc, char **argv)
+{
+ int *p;
+ ulong path;
+ ulong flashaddr, length;
+ long block;
+ int page, offset;
+ if (argc != 1)
+ error(Ebadarg);
+ path = strtoul(argv[0], 0, 0);
+ errorany(logfsserverreadpathextent(d->server, path, 0, &flashaddr, &length, &block, &page, &offset));
+ p = emalloc(sizeof(ulong));
+ *p = path;
+ return p;
+}
+
+static int
+extentdumpread(void *magic, Devlogfs *d, int line, char *buf, int buflen)
+{
+ ulong *p = magic;
+ ulong flashaddr, length;
+ long block;
+ int page, offset;
+ USED(d);
+ errorany(logfsserverreadpathextent(d->server, *p, line, &flashaddr, &length, &block, &page, &offset));
+ if (length == 0)
+ return 0;
+ return snprint(buf, buflen, "%.8ux %ud %ld %d %d\n", flashaddr, length, block, page, offset);
+}
+
+void
+devlogfsdumpinit(Devlogfs *d,
+ void *(*init)(Devlogfs *d, int argc, char **argv),
+ int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen), int argc, char **argv)
+{
+ qlock(&dump.qlock);
+ if (waserror()) {
+ qunlock(&dump.qlock);
+ nexterror();
+ }
+ if (d) {
+ if (d->state < NeedVersion)
+ error("not mounted");
+ qlock(&d->qlock);
+ if (waserror()) {
+ qunlock(&d->qlock);
+ nexterror();
+ }
+ }
+ if (dump.magic) {
+ logfsfreemem(dump.magic);
+ dump.magic = nil;
+ }
+ dump.d = d;
+ dump.magic = (*init)(d, argc, argv);
+ dump.read = read;
+ dump.line = 0;
+ if (d) {
+ poperror();
+ qunlock(&d->qlock);
+ }
+ poperror();
+ qunlock(&dump.qlock);
+}
+
+long
+devlogfsdumpread(char *buf, int buflen)
+{
+ char *tmp = nil;
+ long n;
+ qlock(&dump.qlock);
+ if (waserror()) {
+ logfsfreemem(tmp);
+ qunlock(&dump.qlock);
+ nexterror();
+ }
+ if (dump.magic == nil)
+ error(Eio);
+ tmp = emalloc(READSTR);
+ if (dump.d) {
+ if (dump.d->state < NeedVersion)
+ error("not mounted");
+ qlock(&dump.d->qlock);
+ if (waserror()) {
+ qunlock(&dump.d->qlock);
+ nexterror();
+ }
+ }
+ n = (*dump.read)(dump.magic, dump.d, dump.line, tmp, READSTR);
+ if (n) {
+ dump.line++;
+ n = readstr(0, buf, buflen, tmp);
+ }
+ if (dump.d) {
+ poperror();
+ qunlock(&dump.d->qlock);
+ }
+ logfsfreemem(tmp);
+ poperror();
+ qunlock(&dump.qlock);
+ return n;
+}
+
+void
+devlogfsserverlogsweep(Devlogfs *d, int justone)
+{
+ int didsomething;
+ if (d->state < NeedVersion)
+ error("not mounted");
+ qlock(&d->qlock);
+ if (waserror()) {
+ qunlock(&d->qlock);
+ nexterror();
+ }
+ errorany(logfsserverlogsweep(d->server, justone, &didsomething));
+ poperror();
+ qunlock(&d->qlock);
+}
+
+void
+devlogfsserverwrite(Devlogfs *d, void *buf, long n)
+{
+ int locked = 0;
+ if (d->state == Hungup)
+ error(Ehungup);
+ Sleep(&d->writerendez, writeok, d);
+ if (convM2S(buf, n, &d->in) != n) {
+ /*
+ * someone is writing drivel; have nothing to do with them anymore
+ * most common cause; trying to mount authenticated
+ */
+ d->state = Hungup;
+ error(Ehungup);
+ }
+ d->out.tag = d->in.tag;
+ d->out.fid = d->in.fid;
+ d->out.type = d->in.type + 1;
+ if (waserror()) {
+ if (locked)
+ qunlock(&d->qlock);
+ rerror(d, up->env->errstr);
+ return;
+ }
+ if (d->in.type != Tversion && d->in.type != Tattach) {
+ if (d->state != Attached)
+ error("must be attached");
+ qlock(&d->qlock);
+ locked = 1;
+ }
+ switch (d->in.type) {
+ case Tauth:
+ error("no authentication needed");
+ case Tversion: {
+ char *rversion;
+ if (d->state != NeedVersion)
+ error("unexpected Tversion");
+ if (d->in.tag != NOTAG)
+ error("protocol botch");
+ /*
+ * check the version string
+ */
+ if (strcmp(d->in.version, devlogfs9pversion) != 0)
+ rversion = "unknown";
+ else
+ rversion = devlogfs9pversion;
+ /*
+ * allocate the reply buffer
+ */
+ d->readbufsize = d->in.msize;
+ if (d->readbufsize > MAXMSIZE)
+ d->readbufsize = MAXMSIZE;
+ d->readbuf = emalloc(d->readbufsize);
+ /*
+ * compose the Rversion
+ */
+ d->out.msize = d->readbufsize;
+ d->out.version = rversion;
+ d->state = NeedAttach;
+ break;
+ }
+ case Tattach:
+ if (d->state != NeedAttach)
+ error("unexpected attach");
+ if (d->in.afid != NOFID)
+ error("unexpected afid");
+ errorany(logfsserverattach(d->server, d->in.fid, d->in.uname, &d->out.qid));
+ d->state = Attached;
+ break;
+ case Tclunk:
+ errorany(logfsserverclunk(d->server, d->in.fid));
+ break;
+ case Tcreate:
+ errorany(logfsservercreate(d->server, d->in.fid, d->in.name, d->in.perm, d->in.mode, &d->out.qid));
+ d->out.iounit = d->readbufsize - 11;
+ break;
+ case Tflush:
+ break;
+ case Topen:
+ errorany(logfsserveropen(d->server, d->in.fid, d->in.mode, &d->out.qid));
+ d->out.iounit = d->readbufsize - 11;
+ break;
+ case Tread:
+ d->out.data = (char *)d->readbuf + 11;
+ /* TODO - avoid memmove */
+ errorany(logfsserverread(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->out.data,
+ d->readbufsize - 11, &d->out.count));
+ break;
+ case Tremove:
+ errorany(logfsserverremove(d->server, d->in.fid));
+ break;
+ case Tstat:
+ d->out.stat = d->readbuf + 9;
+ /* TODO - avoid memmove */
+ errorany(logfsserverstat(d->server, d->in.fid, d->out.stat, d->readbufsize - 9, &d->out.nstat));
+// print("nstat %d\n", d->out.nstat);
+ break;
+ case Twalk:
+ errorany(logfsserverwalk(d->server, d->in.fid, d->in.newfid,
+ d->in.nwname, d->in.wname, &d->out.nwqid, d->out.wqid));
+ break;
+ case Twrite:
+ errorany(logfsserverwrite(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->in.data,
+ &d->out.count));
+ break;
+ case Twstat:
+ errorany(logfsserverwstat(d->server, d->in.fid, d->in.stat, d->in.nstat));
+ break;
+ default:
+ print("devlogfsserverwrite: msg %d unimplemented\n", d->in.type);
+ error("unimplemented");
+ }
+ poperror();
+ if (locked)
+ qunlock(&d->qlock);
+ reply(d);
+}
+
+static long
+devlogfsread(Chan *c, void *buf, long n, vlong off)
+{
+ int instance, qid, qt;
+
+ SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+ USED(instance);
+#ifdef CALLTRACE
+ print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n",
+ (ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+ if(qt & QTDIR) {
+#ifdef CALLTRACE
+ print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - calling devdirread\n",
+ (ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+ return devdirread(c, buf, n, 0, 0, devlogfsgen);
+ }
+
+ if(DATAQID(qid, qt)) {
+ if (qid == Qfsboot) {
+ Devlogfs *l = c->aux;
+ qlock(&l->bootqlock);
+ if (waserror()) {
+ qunlock(&l->bootqlock);
+ nexterror();
+ }
+ smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 0);
+ poperror();
+ qunlock(&l->bootqlock);
+ return n;
+ }
+ else if (qid == Qfs) {
+ Devlogfs *d = c->aux;
+ return devlogfsserverread(d, buf, n);
+ }
+ error(Eio);
+ }
+
+ if (qid == Qusers) {
+ long nr;
+ errorany(logfsisusersread(is, buf, n, (ulong)off, &nr));
+ return nr;
+ }
+ else if (qid == Qdump)
+ return devlogfsdumpread(buf, n);
+
+ if (qid != Qctl)
+ error(Egreg);
+
+ return 0;
+}
+
+static long
+devlogfswrite(Chan *c, void *buf, long n, vlong off)
+{
+ char cmd[64], *realfields[6];
+ int i;
+ int instance, qid, qt;
+
+ if(n <= 0)
+ return 0;
+ SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+#ifdef CALLTRACE
+ print("devlogfswrite(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n",
+ (ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+ USED(instance);
+ if(DATAQID(qid, qt)){
+ if (qid == Qfsboot) {
+ Devlogfs *l = c->aux;
+ qlock(&l->bootqlock);
+ if (waserror()) {
+ qunlock(&l->bootqlock);
+ nexterror();
+ }
+ smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 1);
+ poperror();
+ qunlock(&l->bootqlock);
+ return n;
+ }
+ else if (qid == Qfs) {
+ Devlogfs *d = c->aux;
+ devlogfsserverwrite(d, buf, n);
+ return n;
+ }
+ error(Eio);
+ }
+ else if (qid == Qctl) {
+ Devlogfs *l = nil;
+ char **fields;
+
+ if(n > sizeof(cmd)-1)
+ n = sizeof(cmd)-1;
+ memmove(cmd, buf, n);
+ cmd[n] = 0;
+ i = getfields(cmd, realfields, 6, 1, " \t\n");
+//print("i = %d\n", i);
+ if (i <= 0)
+ error(Ebadarg);
+ fields = realfields;
+ if (i == 3 && strcmp(fields[0], "uname") == 0) {
+ switch (fields[2][0]) {
+ default:
+ errorany(logfsisgroupcreate(is, fields[1], fields[2]));
+ break;
+ case ':':
+ errorany(logfsisgroupcreate(is, fields[1], fields[2] + 1));
+ break;
+ case '%':
+ errorany(logfsisgrouprename(is, fields[1], fields[2] + 1));
+ break;
+ case '=':
+ errorany(logfsisgroupsetleader(is, fields[1], fields[2] + 1));
+ break;
+ case '+':
+ errorany(logfsisgroupaddmember(is, fields[1], fields[2] + 1));
+ break;
+ case '-':
+ errorany(logfsisgroupremovemember(is, fields[1], fields[2] + 1));
+ break;
+ }
+ i = 0;
+ }
+ if (i == 4 && strcmp(fields[0], "fsys") == 0 && strcmp(fields[2], "config") == 0) {
+ l = devlogfsconfig(fields[1], fields[3]);
+ i = 0;
+ }
+ else if (i >= 2 && strcmp(fields[0], "fsys") == 0) {
+ l = devlogfssetdefname(fields[1]);
+ if (l == nil)
+ error(Ebadarg);
+ i -= 2;
+ fields += 2;
+ }
+ if (i != 0) {
+ if (l == nil)
+ l = devlogfssetdefname(nil);
+ if (i >= 1 && strcmp(fields[0], "open") == 0) {
+ int a;
+ if (l == nil)
+ error(Ebadarg);
+ for (a = 1; a < i; a++)
+ if (fields[a][0] == '-')
+ switch (fields[a][1]) {
+ case 'P':
+ l->openflags |= LogfsOpenFlagNoPerm;
+ break;
+ case 'W':
+ l->openflags |= LogfsOpenFlagWstatAllow;
+ break;
+ default:
+ error(Ebadarg);
+ }
+ devlogfsllopen(l);
+ i = 0;
+ }
+ else if (i == 2 && strcmp(fields[0], "format") == 0) {
+ if (l == nil)
+ error(Ebadarg);
+ devlogfsllformat(l, strtol(fields[1], nil, 0));
+ i = 0;
+ }
+ else if (i >= 1 && strcmp(fields[0], "sweep") == 0) {
+ if (l == nil)
+ error(Ebadarg);
+ devlogfsserverlogsweep(l, 0);
+ i = 0;
+ }
+ else if (i >= 1 && strcmp(fields[0], "sweepone") == 0) {
+ if (l == nil)
+ error(Ebadarg);
+ devlogfsserverlogsweep(l, 1);
+ i = 0;
+ }
+ else if (i <= 2&& strcmp(fields[0], "trace") == 0) {
+ if (l == nil)
+ error(Ebadarg);
+ l->logfstrace = i > 1 ? strtol(fields[1], nil, 0) : 0;
+ if (l->server)
+ logfsservertrace(l->server, l->logfstrace);
+ if (l->lb)
+ logfsboottrace(l->lb, l->logfstrace);
+ i = 0;
+ }
+ else if (i == 1 && strcmp(fields[0], "unconfig") == 0) {
+ if (l == nil)
+ error(Ebadarg);
+ if (l->ref.ref > 0)
+ error(Einuse);
+ devlogfsunconfig(l);
+ i = 0;
+ }
+ else if (i == 2 && strcmp(fields[0], "extent") == 0) {
+ if (l == nil)
+ error(Ebadarg);
+ devlogfsdumpinit(l, extentdumpinit, extentdumpread, i - 1, fields + 1);
+ i = 0;
+ }
+ else if (i >= 2 && strcmp(fields[0], "test") == 0) {
+ if (l == nil)
+ error(Ebadarg);
+ errorany(logfsservertestcmd(l->server, i - 1, fields + 1));
+ i = 0;
+ }
+#ifdef LEAKHUNT
+ else if (i == 1 && strcmp(fields[0], "leakaudit") == 0) {
+ leakaudit();
+ i = 0;
+ }
+#endif
+ }
+ if (i != 0)
+ error(Ebadarg);
+ return n;
+ }
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+static void
+devlogfsfree(Devlogfs *devlogfs)
+{
+ if (devlogfs != nil) {
+ int i;
+ logfsfreemem(devlogfs->device);
+ logfsfreemem(devlogfs->name);
+ for (i = 0; i < Qend - Qfs; i++)
+ logfsfreemem(devlogfs->filename[i]);
+ cclose(devlogfs->flash);
+ cclose(devlogfs->flashctl);
+ qlock(&devlogfs->qlock);
+ logfsserverfree(&devlogfs->server);
+ logfsbootfree(devlogfs->lb);
+ if (devlogfs->ll)
+ (*devlogfs->ll->free)(devlogfs->ll);
+ logfsfreemem(devlogfs->readbuf);
+ qunlock(&devlogfs->qlock);
+ logfsfreemem(devlogfs);
+ }
+}
+
+#ifdef EMU
+ulong
+logfsnow(void)
+{
+ extern vlong timeoffset;
+ return (timeoffset + osusectime()) / 1000000;
+}
+#endif
+
+Dev logfsdevtab = {
+ 0x29f,
+// L'ʟ',
+ "logfs",
+
+#ifndef EMU
+ devreset,
+#endif
+ devinit,
+#ifndef EMU
+ devshutdown,
+#endif
+ devlogfsattach,
+ devlogfswalk,
+ devlogfsstat,
+ devlogfsopen,
+ devcreate,
+ devlogfsclose,
+ devlogfsread,
+ devbread,
+ devlogfswrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/emu/port/devmem.c
@@ -1,0 +1,484 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+
+enum
+{
+ Qdir,
+ Qctl,
+ Qstate,
+ Qsum,
+ Qevent,
+ Qprof,
+ Qheap,
+ Qgc
+};
+
+static
+Dirtab memdir[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "memctl", {Qctl}, 0, 0666,
+ "memstate", {Qstate}, 0, 0444,
+ "memsum", {Qsum}, 0, 0444,
+ "memevent", {Qevent}, 0, 0444,
+ "memprof", {Qprof}, 0, 0444,
+ "memheap", {Qheap}, 0, 0444,
+ "memgc", {Qgc}, 0, 0444,
+};
+
+enum
+{
+ /* these are the top two bits of size */
+ Pflags= 3<<30,
+ Pfree= 0<<30,
+ Palloc= 1<<30,
+ Paend= 2<<30,
+ Pimmutable= 3<<30,
+
+ Npool = 3,
+ Nevent = 10000,
+ Nstate = 12000
+};
+
+/*
+ * pool snapshots
+ */
+typedef struct Pstate Pstate;
+struct Pstate
+{
+ ulong base;
+ ulong size;
+};
+
+static struct
+{
+ Pstate state[3+Nstate];
+ Pstate* lim;
+ Pstate* ptr;
+ int summary;
+} poolstate[Npool];
+static Ref stateopen;
+
+/*
+ * pool/heap allocation events
+ */
+typedef struct Pevent Pevent;
+struct Pevent
+{
+ int pool;
+ ulong pc;
+ ulong base;
+ ulong size;
+};
+
+static struct
+{
+ Lock l;
+ Ref inuse;
+ Rendez r;
+ int open;
+ Pevent events[Nevent];
+ int rd;
+ int wr;
+ int full;
+ int want;
+ ulong lost;
+} poolevents;
+
+/*
+ * allocation profiles
+ */
+typedef struct Pprof Pprof;
+typedef struct Pbucket Pbucket;
+
+struct Pbucket
+{
+ ulong val;
+ ulong pool;
+ ulong count;
+ ulong size;
+ Pbucket* next;
+};
+
+static struct {
+ Ref inuse; /* only one of these things */
+ Lock l;
+ Pbucket buckets[1000];
+ Pbucket snap[1000];
+ int used;
+ int snapped;
+ Pbucket* hash[128];
+ ulong lost;
+} memprof;
+
+extern void (*memmonitor)(int, ulong, ulong, ulong);
+extern ulong gcnruns;
+extern ulong gcsweeps;
+extern ulong gcbroken;
+extern ulong gchalted;
+extern ulong gcepochs;
+extern uvlong gcdestroys;
+extern uvlong gcinspects;
+extern uvlong gcbusy;
+extern uvlong gcidle;
+extern uvlong gcidlepass;
+extern uvlong gcpartial;
+
+
+static void
+mprofreset(void)
+{
+ lock(&memprof.l); /* need ilock in kernel */
+ memset(memprof.hash, 0, sizeof(memprof.hash));
+ memprof.used = 0;
+ memprof.lost = 0;
+ unlock(&memprof.l);
+}
+
+static void
+mprofmonitor(int pool, ulong pc, ulong base, ulong size)
+{
+ Pbucket **h0, **h, *p;
+
+ if((pool&7) == 1)
+ return; /* ignore heap */
+ USED(base);
+ h0 = &memprof.hash[((pc>>16)^(pc>>4))&(nelem(memprof.hash)-1)];
+ lock(&memprof.l);
+ for(h = h0; (p = *h) != nil; h = &p->next)
+ if(p->val == pc && p->pool == pool){
+ p->count++;
+ p->size += size;
+ *h = p->next;
+ p->next = *h0;
+ *h0 = p;
+ unlock(&memprof.l);
+ return;
+ }
+ if(memprof.used >= nelem(memprof.buckets)){
+ memprof.lost++;
+ unlock(&memprof.l);
+ return;
+ }
+ p = &memprof.buckets[memprof.used++];
+ p->val = pc;
+ p->pool = pool;
+ p->count = 1;
+ p->size = size;
+ p->next = *h0;
+ *h0 = p;
+ unlock(&memprof.l);
+}
+
+static void
+_memmonitor(int pool, ulong pc, ulong base, ulong size)
+{
+ Pevent e;
+
+ e.pool = pool;
+ e.pc = pc;
+ e.base = base;
+ e.size = size;
+ lock(&poolevents.l);
+ if(!poolevents.full){
+ poolevents.events[poolevents.wr] = e;
+ if(++poolevents.wr == nelem(poolevents.events))
+ poolevents.wr = 0;
+ if(poolevents.wr == poolevents.rd)
+ poolevents.full = 1;
+ }else
+ poolevents.lost++;
+ if(poolevents.want){
+ poolevents.want = 0;
+ Wakeup(&poolevents.r);
+ }
+ unlock(&poolevents.l);
+}
+
+static int
+ismemdata(void *v)
+{
+ USED(v);
+ return poolevents.full || poolevents.rd != poolevents.wr;
+}
+
+static char*
+memaudit(int pno, Bhdr *b)
+{
+ Pstate *p;
+
+ if(pno >= Npool)
+ return "too many pools for memaudit";
+ if((p = poolstate[pno].ptr) == poolstate[pno].lim){
+ if(b->magic == MAGIC_E)
+ return nil;
+ p = &poolstate[pno].state[1];
+ if(b->magic == MAGIC_F)
+ p++;
+ p->base++;
+ p->size += b->size;
+ return nil;
+ }
+ poolstate[pno].ptr++;
+ p->base = (ulong)b;
+ p->size = b->size;
+ switch(b->magic){
+ case MAGIC_A:
+ p->size |= Palloc;
+ break;
+ case MAGIC_F:
+ p->size |= Pfree;
+ break;
+ case MAGIC_E:
+ p->size = b->csize | Paend;
+ break;
+ case MAGIC_I:
+ p->size |= Pimmutable;
+ break;
+ default:
+ return "bad magic number in block";
+ }
+ return nil;
+}
+
+static void
+mput4(uchar *m, ulong v)
+{
+ m[0] = v>>24;
+ m[1] = v>>16;
+ m[2] = v>>8;
+ m[3] = v;
+}
+
+static Chan*
+memattach(char *spec)
+{
+ return devattach('%', spec);
+}
+
+static Walkqid*
+memwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, memdir, nelem(memdir), devgen);
+}
+
+static int
+memstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, memdir, nelem(memdir), devgen);
+}
+
+static Chan*
+memopen(Chan *c, int omode)
+{
+ if(memmonitor != nil && c->qid.path != Qgc)
+ error(Einuse);
+ c = devopen(c, omode, memdir, nelem(memdir), devgen);
+ switch((ulong)c->qid.path){
+ case Qevent:
+ if(incref(&poolevents.inuse) != 1){
+ decref(&poolevents.inuse);
+ c->flag &= ~COPEN;
+ error(Einuse);
+ }
+ poolevents.rd = poolevents.wr = 0;
+ poolevents.full = 0;
+ poolevents.want = 0;
+ poolevents.lost = 0;
+ memmonitor = _memmonitor;
+ poolevents.open = 1;
+ break;
+ case Qstate:
+ if(incref(&stateopen) != 1){
+ decref(&stateopen);
+ c->flag &= ~COPEN;
+ error(Einuse);
+ }
+ break;
+ case Qprof:
+ if(incref(&memprof.inuse) != 1){
+ decref(&memprof.inuse);
+ c->flag &= ~COPEN;
+ error(Einuse);
+ }
+ memmonitor = mprofmonitor;
+ break;
+ }
+ return c;
+}
+
+static void
+memclose(Chan *c)
+{
+ if((c->flag & COPEN) == 0)
+ return;
+ switch((ulong)c->qid.path) {
+ case Qevent:
+ memmonitor = nil;
+ poolevents.open = 0;
+ decref(&poolevents.inuse);
+ break;
+ case Qstate:
+ decref(&stateopen);
+ break;
+ case Qprof:
+ decref(&memprof.inuse);
+ memmonitor = nil;
+ break;
+ }
+
+}
+
+static long
+memread(Chan *c, void *va, long count, vlong offset)
+{
+ uchar *m;
+ char *e, *s;
+ int i, summary;
+ long n, nr;
+ Pstate *p;
+ Pevent pe;
+ Pbucket *b;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, va, count, memdir, nelem(memdir), devgen);
+
+ summary = 0;
+ switch((ulong)c->qid.path) {
+ default:
+ error(Egreg);
+ case Qctl:
+ return 0;
+ case Qsum:
+ summary = 1;
+ /* fall through */
+ case Qstate:
+ if(offset == 0){
+ for(i=0; i<Npool; i++){
+ poolstate[i].ptr = &poolstate[i].state[3];
+ poolstate[i].lim = poolstate[i].ptr + Nstate;
+ memset(poolstate[i].state, 0, sizeof(poolstate[i].state));
+ poolstate[i].summary = summary;
+ }
+ e = poolaudit(memaudit);
+ if(e != nil){
+ print("mem: %s\n", e);
+ error(e);
+ }
+ }
+ m = va;
+ nr = offset/8;
+ for(i=0; i<Npool && count >= 8; i++){
+ n = poolstate[i].ptr - poolstate[i].state;
+ poolstate[i].state[0].base = i;
+ poolstate[i].state[0].size = n;
+ if(nr >= n){
+ nr -= n;
+ continue;
+ }
+ n -= nr;
+ p = &poolstate[i].state[nr];
+ for(; --n >= 0 && (count -= 8) >= 0; m += 8, p++){
+ mput4(m, p->base);
+ mput4(m+4, p->size);
+ }
+ }
+ return m-(uchar*)va;
+ case Qevent:
+ while(!ismemdata(nil)){
+ poolevents.want = 1;
+ Sleep(&poolevents.r, ismemdata, nil);
+ }
+ m = va;
+ do{
+ if((count -= 4*4) < 0)
+ return m-(uchar*)va;
+ pe = poolevents.events[poolevents.rd];
+ mput4(m, pe.pool);
+ mput4(m+4, pe.pc);
+ mput4(m+8, pe.base);
+ mput4(m+12, pe.size);
+ m += 4*4;
+ if(++poolevents.rd >= nelem(poolevents.events))
+ poolevents.rd = 0;
+ }while(poolevents.rd != poolevents.wr);
+ poolevents.full = 0;
+ return m-(uchar*)va;
+ case Qprof:
+ if(offset == 0){
+ lock(&memprof.l);
+ memmove(memprof.snap, memprof.buckets, memprof.used*sizeof(memprof.buckets[0]));
+ memprof.snapped = memprof.used;
+ unlock(&memprof.l);
+ }
+ m = va;
+ for(i = offset/(4*4); i < memprof.snapped && (count -= 4*4) >= 0; i++){
+ b = &memprof.snap[i];
+ mput4(m, b->pool);
+ mput4(m+4, b->val);
+ mput4(m+8, b->count);
+ mput4(m+12, b->size);
+ m += 4*4;
+ }
+ return m-(uchar*)va;
+ case Qgc:
+ s = malloc(READSTR);
+ if(s == nil)
+ error(Enomem);
+ if(waserror()){
+ free(s);
+ nexterror();
+ }
+ snprint(s, READSTR, "runs: %lud\nsweeps: %lud\nbchain: %lud\nhalted: %lud\nepochs: %lud\ndestroy: %llud\ninspects: %llud\nbusy: %llud\nidle: %llud\nidlepass: %llud\npartial: %llud\n",
+ gcnruns, gcsweeps, gcbroken, gchalted, gcepochs, gcdestroys, gcinspects, gcbusy, gcidle, gcidlepass, gcpartial);
+ count = readstr(offset, va, count, s);
+ poperror();
+ free(s);
+ return count;
+ }
+}
+
+static long
+memwrite(Chan *c, void *va, long count, vlong offset)
+{
+ USED(offset);
+ USED(count);
+ USED(va);
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ switch((ulong)c->qid.path) {
+ default:
+ error(Egreg);
+ case Qctl:
+ error(Ebadarg);
+ case Qstate:
+ error(Eperm);
+ case Qprof:
+ mprofreset();
+ break;
+ }
+ return 0;
+}
+
+Dev memdevtab = {
+ '%',
+ "mem",
+
+ devinit,
+ memattach,
+ memwalk,
+ memstat,
+ memopen,
+ devcreate,
+ memclose,
+ memread,
+ devbread,
+ memwrite,
+ devbwrite,
+ devremove,
+ devwstat
+};
--- /dev/null
+++ b/emu/port/devmnt.c
@@ -1,0 +1,1203 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+/*
+ * References are managed as follows:
+ * The channel to the server - a network connection or pipe - has one
+ * reference for every Chan open on the server. The server channel has
+ * c->mux set to the Mnt used for muxing control to that server. Mnts
+ * have no reference count; they go away when c goes away.
+ * Each channel derived from the mount point has mchan set to c,
+ * and increfs/decrefs mchan to manage references on the server
+ * connection.
+ */
+
+#define MAXRPC (IOHDRSZ+8192)
+
+struct Mntrpc
+{
+ Chan* c; /* Channel for whom we are working */
+ Mntrpc* list; /* Free/pending list */
+ Fcall request; /* Outgoing file system protocol message */
+ Fcall reply; /* Incoming reply */
+ Mnt* m; /* Mount device during rpc */
+ Rendez r; /* Place to hang out */
+ uchar* rpc; /* I/O Data buffer */
+ uint rpclen; /* len of buffer */
+ Block *b; /* reply blocks */
+ char done; /* Rpc completed */
+ uvlong stime; /* start time for mnt statistics */
+ ulong reqlen; /* request length for mnt statistics */
+ ulong replen; /* reply length for mnt statistics */
+ Mntrpc* flushed; /* message this one flushes */
+};
+
+enum
+{
+ TAGSHIFT = 5, /* ulong has to be 32 bits */
+ TAGMASK = (1<<TAGSHIFT)-1,
+ NMASK = (64*1024)>>TAGSHIFT,
+};
+
+struct Mntalloc
+{
+ Lock l;
+ Mnt* list; /* Mount devices in use */
+ Mnt* mntfree; /* Free list */
+ Mntrpc* rpcfree;
+ int nrpcfree;
+ int nrpcused;
+ ulong id;
+ ulong tagmask[NMASK];
+}mntalloc;
+
+void mattach(Mnt*, Chan*, char*);
+Mnt* mntchk(Chan*);
+void mntdirfix(uchar*, Chan*);
+Mntrpc* mntflushalloc(Mntrpc*, ulong);
+void mntflushfree(Mnt*, Mntrpc*);
+void mntfree(Mntrpc*);
+void mntgate(Mnt*);
+void mntpntfree(Mnt*);
+void mntqrm(Mnt*, Mntrpc*);
+Mntrpc* mntralloc(Chan*, ulong);
+long mntrdwr(int, Chan*, void*, long, vlong);
+int mntrpcread(Mnt*, Mntrpc*);
+void mountio(Mnt*, Mntrpc*);
+void mountmux(Mnt*, Mntrpc*);
+void mountrpc(Mnt*, Mntrpc*);
+int rpcattn(void*);
+Chan* mntchan(void);
+
+char Esbadstat[] = "invalid directory entry received from server";
+char Enoversion[] = "version not established for mount channel";
+
+
+void (*mntstats)(int, Chan*, uvlong, ulong);
+
+static void
+mntinit(void)
+{
+ mntalloc.id = 1;
+ mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */
+ mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */
+ fmtinstall('F', fcallfmt);
+/* fmtinstall('D', dirfmt); */
+/* fmtinstall('M', dirmodefmt); */
+
+ cinit();
+}
+
+/*
+ * Version is not multiplexed: message sent only once per connection.
+ */
+long
+mntversion(Chan *c, char *version, int msize, int returnlen)
+{
+ Fcall f;
+ uchar *msg;
+ Mnt *m;
+ char *v;
+ long k, l;
+ uvlong oo;
+ char buf[128];
+
+ qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */
+ if(waserror()){
+ qunlock(&c->umqlock);
+ nexterror();
+ }
+
+ /* defaults */
+ if(msize == 0)
+ msize = MAXRPC;
+ if(msize > c->iounit && c->iounit != 0)
+ msize = c->iounit;
+ v = version;
+ if(v == nil || v[0] == '\0')
+ v = VERSION9P;
+
+ /* validity */
+ if(msize < 0)
+ error("bad iounit in version call");
+ if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
+ error("bad 9P version specification");
+
+ m = c->mux;
+
+ if(m != nil){
+ qunlock(&c->umqlock);
+ poperror();
+
+ strecpy(buf, buf+sizeof buf, m->version);
+ k = strlen(buf);
+ if(strncmp(buf, v, k) != 0){
+ snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v);
+ error(buf);
+ }
+ if(returnlen > 0){
+ if(returnlen < k)
+ error(Eshort);
+ memmove(version, buf, k);
+ }
+ return k;
+ }
+
+ f.type = Tversion;
+ f.tag = NOTAG;
+ f.msize = msize;
+ f.version = v;
+ msg = malloc(8192+IOHDRSZ);
+ if(msg == nil)
+ exhausted("version memory");
+ if(waserror()){
+ free(msg);
+ nexterror();
+ }
+ k = convS2M(&f, msg, 8192+IOHDRSZ);
+ if(k == 0)
+ error("bad fversion conversion on send");
+
+ lock(&c->l);
+ oo = c->offset;
+ c->offset += k;
+ unlock(&c->l);
+
+ l = devtab[c->type]->write(c, msg, k, oo);
+
+ if(l < k){
+ lock(&c->l);
+ c->offset -= k - l;
+ unlock(&c->l);
+ error("short write in fversion");
+ }
+
+ /* message sent; receive and decode reply */
+ k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
+ if(k <= 0)
+ error("EOF receiving fversion reply");
+
+ lock(&c->l);
+ c->offset += k;
+ unlock(&c->l);
+
+ l = convM2S(msg, k, &f);
+ if(l != k)
+ error("bad fversion conversion on reply");
+ if(f.type != Rversion){
+ if(f.type == Rerror)
+ error(f.ename);
+ error("unexpected reply type in fversion");
+ }
+ if(f.msize > msize)
+ error("server tries to increase msize in fversion");
+ if(f.msize<256 || f.msize>1024*1024)
+ error("nonsense value of msize in fversion");
+ if(strncmp(f.version, v, strlen(f.version)) != 0)
+ error("bad 9P version returned from server");
+
+ /* now build Mnt associated with this connection */
+ lock(&mntalloc.l);
+ m = mntalloc.mntfree;
+ if(m != 0)
+ mntalloc.mntfree = m->list;
+ else {
+ m = malloc(sizeof(Mnt));
+ if(m == 0) {
+ unlock(&mntalloc.l);
+ exhausted("mount devices");
+ }
+ }
+ m->list = mntalloc.list;
+ mntalloc.list = m;
+ m->version = nil;
+ kstrdup(&m->version, f.version);
+ m->id = mntalloc.id++;
+ m->q = qopen(10*MAXRPC, 0, nil, nil);
+ m->msize = f.msize;
+ unlock(&mntalloc.l);
+
+ poperror(); /* msg */
+ free(msg);
+
+ lock(&m->l);
+ m->queue = 0;
+ m->rip = 0;
+
+ c->flag |= CMSG;
+ c->mux = m;
+ m->c = c;
+ unlock(&m->l);
+
+ poperror(); /* c */
+ qunlock(&c->umqlock);
+
+ k = strlen(f.version);
+ if(returnlen > 0){
+ if(returnlen < k)
+ error(Eshort);
+ memmove(version, f.version, k);
+ }
+
+ return k;
+}
+
+Chan*
+mntauth(Chan *c, char *spec)
+{
+ Mnt *m;
+ Mntrpc *r;
+
+ m = c->mux;
+
+ if(m == nil){
+ mntversion(c, VERSION9P, MAXRPC, 0);
+ m = c->mux;
+ if(m == nil)
+ error(Enoversion);
+ }
+
+ c = mntchan();
+ if(waserror()) {
+ /* Close must not be called since it will
+ * call mnt recursively
+ */
+ chanfree(c);
+ nexterror();
+ }
+
+ r = mntralloc(0, m->msize);
+
+ if(waserror()) {
+ mntfree(r);
+ nexterror();
+ }
+
+ r->request.type = Tauth;
+ r->request.afid = c->fid;
+ r->request.uname = up->env->user;
+ r->request.aname = spec;
+ mountrpc(m, r);
+
+ c->qid = r->reply.aqid;
+ c->mchan = m->c;
+ incref(&m->c->r);
+ c->mqid = c->qid;
+ c->mode = ORDWR;
+
+ poperror(); /* r */
+ mntfree(r);
+
+ poperror(); /* c */
+
+ return c;
+
+}
+
+static Chan*
+mntattach(char *muxattach)
+{
+ Mnt *m;
+ Chan *c;
+ Mntrpc *r;
+ struct bogus{
+ Chan *chan;
+ Chan *authchan;
+ char *spec;
+ int flags;
+ }bogus;
+
+ bogus = *((struct bogus *)muxattach);
+ c = bogus.chan;
+
+ m = c->mux;
+
+ if(m == nil){
+ mntversion(c, nil, 0, 0);
+ m = c->mux;
+ if(m == nil)
+ error(Enoversion);
+ }
+
+ c = mntchan();
+ if(waserror()) {
+ /* Close must not be called since it will
+ * call mnt recursively
+ */
+ chanfree(c);
+ nexterror();
+ }
+
+ r = mntralloc(0, m->msize);
+
+ if(waserror()) {
+ mntfree(r);
+ nexterror();
+ }
+
+ r->request.type = Tattach;
+ r->request.fid = c->fid;
+ if(bogus.authchan == nil)
+ r->request.afid = NOFID;
+ else
+ r->request.afid = bogus.authchan->fid;
+ r->request.uname = up->env->user;
+ r->request.aname = bogus.spec;
+ mountrpc(m, r);
+
+ c->qid = r->reply.qid;
+ c->mchan = m->c;
+ incref(&m->c->r);
+ c->mqid = c->qid;
+
+ poperror(); /* r */
+ mntfree(r);
+
+ poperror(); /* c */
+
+ if(bogus.flags&MCACHE)
+ c->flag |= CCACHE;
+ return c;
+}
+
+Chan*
+mntchan(void)
+{
+ Chan *c;
+
+ c = devattach('M', 0);
+ lock(&mntalloc.l);
+ c->dev = mntalloc.id++;
+ unlock(&mntalloc.l);
+
+ if(c->mchan)
+ panic("mntchan non-zero %p", c->mchan);
+ return c;
+}
+
+static Walkqid*
+mntwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ volatile int alloc;
+ int i;
+ Mnt *m;
+ Mntrpc *r;
+ Walkqid *wq;
+
+ if(nc != nil)
+ print("mntwalk: nc != nil\n");
+ if(nname > MAXWELEM)
+ error("devmnt: too many name elements");
+ alloc = 0;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone!=nil)
+ cclose(wq->clone);
+ free(wq);
+ return nil;
+ }
+
+ alloc = 0;
+ m = mntchk(c);
+ r = mntralloc(c, m->msize);
+ if(nc == nil){
+ nc = devclone(c);
+ /*
+ * Until the other side accepts this fid, we can't mntclose it.
+ * Therefore set type to 0 for now; rootclose is known to be safe.
+ */
+ nc->type = 0;
+ alloc = 1;
+ }
+ wq->clone = nc;
+
+ if(waserror()) {
+ mntfree(r);
+ nexterror();
+ }
+ r->request.type = Twalk;
+ r->request.fid = c->fid;
+ r->request.newfid = nc->fid;
+ r->request.nwname = nname;
+ memmove(r->request.wname, name, nname*sizeof(char*));
+
+ mountrpc(m, r);
+
+ if(r->reply.nwqid > nname)
+ error("too many QIDs returned by walk");
+ if(r->reply.nwqid < nname){
+ if(alloc)
+ cclose(nc);
+ wq->clone = nil;
+ if(r->reply.nwqid == 0){
+ free(wq);
+ wq = nil;
+ goto Return;
+ }
+ }
+
+ /* move new fid onto mnt device and update its qid */
+ if(wq->clone != nil){
+ if(wq->clone != c){
+ wq->clone->type = c->type;
+ wq->clone->mchan = c->mchan;
+ incref(&c->mchan->r);
+ }
+ if(r->reply.nwqid > 0)
+ wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
+ }
+ wq->nqid = r->reply.nwqid;
+ for(i=0; i<wq->nqid; i++)
+ wq->qid[i] = r->reply.wqid[i];
+
+ Return:
+ poperror();
+ mntfree(r);
+ poperror();
+ return wq;
+}
+
+static int
+mntstat(Chan *c, uchar *dp, int n)
+{
+ Mnt *m;
+ Mntrpc *r;
+
+ if(n < BIT16SZ)
+ error(Eshortstat);
+ m = mntchk(c);
+ r = mntralloc(c, m->msize);
+ if(waserror()) {
+ mntfree(r);
+ nexterror();
+ }
+ r->request.type = Tstat;
+ r->request.fid = c->fid;
+ mountrpc(m, r);
+
+ if(r->reply.nstat > n){
+ /* doesn't fit; just patch the count and return */
+ PBIT16((uchar*)dp, r->reply.nstat);
+ n = BIT16SZ;
+ }else{
+ n = r->reply.nstat;
+ memmove(dp, r->reply.stat, n);
+ validstat(dp, n);
+ mntdirfix(dp, c);
+ }
+ poperror();
+ mntfree(r);
+ return n;
+}
+
+static Chan*
+mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
+{
+ Mnt *m;
+ Mntrpc *r;
+
+ m = mntchk(c);
+ r = mntralloc(c, m->msize);
+ if(waserror()) {
+ mntfree(r);
+ nexterror();
+ }
+ r->request.type = type;
+ r->request.fid = c->fid;
+ r->request.mode = omode;
+ if(type == Tcreate){
+ r->request.perm = perm;
+ r->request.name = name;
+ }
+ mountrpc(m, r);
+
+ c->qid = r->reply.qid;
+ c->offset = 0;
+ c->mode = openmode(omode);
+ c->iounit = r->reply.iounit;
+ if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
+ c->iounit = m->msize-IOHDRSZ;
+ c->flag |= COPEN;
+ poperror();
+ mntfree(r);
+
+ if(c->flag & CCACHE)
+ copen(c);
+
+ return c;
+}
+
+static Chan*
+mntopen(Chan *c, int omode)
+{
+ return mntopencreate(Topen, c, nil, omode, 0);
+}
+
+static void
+mntcreate(Chan *c, char *name, int omode, ulong perm)
+{
+ mntopencreate(Tcreate, c, name, omode, perm);
+}
+
+static void
+mntclunk(Chan *c, int t)
+{
+ Mnt *m;
+ Mntrpc *r;
+
+ m = mntchk(c);
+ r = mntralloc(c, m->msize);
+ if(waserror()){
+ mntfree(r);
+ nexterror();
+ }
+
+ r->request.type = t;
+ r->request.fid = c->fid;
+ mountrpc(m, r);
+ mntfree(r);
+ poperror();
+}
+
+void
+muxclose(Mnt *m)
+{
+ Mntrpc *q, *r;
+
+ for(q = m->queue; q; q = r) {
+ r = q->list;
+ mntfree(q);
+ }
+ m->id = 0;
+ free(m->version);
+ m->version = nil;
+ mntpntfree(m);
+}
+
+void
+mntpntfree(Mnt *m)
+{
+ Mnt *f, **l;
+ Queue *q;
+
+ lock(&mntalloc.l);
+ l = &mntalloc.list;
+ for(f = *l; f; f = f->list) {
+ if(f == m) {
+ *l = m->list;
+ break;
+ }
+ l = &f->list;
+ }
+ m->list = mntalloc.mntfree;
+ mntalloc.mntfree = m;
+ q = m->q;
+ unlock(&mntalloc.l);
+
+ qfree(q);
+}
+
+static void
+mntclose(Chan *c)
+{
+ mntclunk(c, Tclunk);
+}
+
+static void
+mntremove(Chan *c)
+{
+ mntclunk(c, Tremove);
+}
+
+static int
+mntwstat(Chan *c, uchar *dp, int n)
+{
+ Mnt *m;
+ Mntrpc *r;
+
+ m = mntchk(c);
+ r = mntralloc(c, m->msize);
+ if(waserror()) {
+ mntfree(r);
+ nexterror();
+ }
+ r->request.type = Twstat;
+ r->request.fid = c->fid;
+ r->request.nstat = n;
+ r->request.stat = dp;
+ mountrpc(m, r);
+ poperror();
+ mntfree(r);
+ return n;
+}
+
+static long
+mntread(Chan *c, void *buf, long n, vlong off)
+{
+ uchar *p, *e;
+ int nc, cache, isdir, dirlen;
+
+ isdir = 0;
+ cache = c->flag & CCACHE;
+ if(c->qid.type & QTDIR) {
+ cache = 0;
+ isdir = 1;
+ }
+
+ p = buf;
+ if(cache) {
+ nc = cread(c, buf, n, off);
+ if(nc > 0) {
+ n -= nc;
+ if(n == 0)
+ return nc;
+ p += nc;
+ off += nc;
+ }
+ n = mntrdwr(Tread, c, p, n, off);
+ cupdate(c, p, n, off);
+ return n + nc;
+ }
+
+ n = mntrdwr(Tread, c, buf, n, off);
+ if(isdir) {
+ for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
+ dirlen = BIT16SZ+GBIT16(p);
+ if(p+dirlen > e)
+ break;
+ validstat(p, dirlen);
+ mntdirfix(p, c);
+ }
+ if(p != e)
+ error(Esbadstat);
+ }
+ return n;
+}
+
+static long
+mntwrite(Chan *c, void *buf, long n, vlong off)
+{
+ return mntrdwr(Twrite, c, buf, n, off);
+}
+
+long
+mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
+{
+ Mnt *m;
+ Mntrpc *r; /* TO DO: volatile struct { Mntrpc *r; } r; */
+ char *uba;
+ int cache;
+ ulong cnt, nr, nreq;
+
+ m = mntchk(c);
+ uba = buf;
+ cnt = 0;
+ cache = c->flag & CCACHE;
+ if(c->qid.type & QTDIR)
+ cache = 0;
+ for(;;) {
+ r = mntralloc(c, m->msize);
+ if(waserror()) {
+ mntfree(r);
+ nexterror();
+ }
+ r->request.type = type;
+ r->request.fid = c->fid;
+ r->request.offset = off;
+ r->request.data = uba;
+ nr = n;
+ if(nr > m->msize-IOHDRSZ)
+ nr = m->msize-IOHDRSZ;
+ r->request.count = nr;
+ mountrpc(m, r);
+ nreq = r->request.count;
+ nr = r->reply.count;
+ if(nr > nreq)
+ nr = nreq;
+
+ if(type == Tread)
+ r->b = bl2mem((uchar*)uba, r->b, nr);
+ else if(cache)
+ cwrite(c, (uchar*)uba, nr, off);
+
+ poperror();
+ mntfree(r);
+ off += nr;
+ uba += nr;
+ cnt += nr;
+ n -= nr;
+ if(nr != nreq || n == 0 || up->killed)
+ break;
+ }
+ return cnt;
+}
+
+void
+mountrpc(Mnt *m, Mntrpc *r)
+{
+ char *sn, *cn;
+ int t;
+
+ r->reply.tag = 0;
+ r->reply.type = Tmax; /* can't ever be a valid message type */
+
+ mountio(m, r);
+
+ t = r->reply.type;
+ switch(t) {
+ case Rerror:
+ error(r->reply.ename);
+ case Rflush:
+ error(Eintr);
+ default:
+ if(t == r->request.type+1)
+ break;
+ sn = "?";
+ if(m->c->name != nil)
+ sn = m->c->name->s;
+ cn = "?";
+ if(r->c != nil && r->c->name != nil)
+ cn = r->c->name->s;
+ print("mnt: proc %s %lud: mismatch from %s %s rep 0x%p tag %d fid %d T%d R%d rp %d\n",
+ up->text, up->pid, sn, cn,
+ r, r->request.tag, r->request.fid, r->request.type,
+ r->reply.type, r->reply.tag);
+ error(Emountrpc);
+ }
+}
+
+void
+mountio(Mnt *m, Mntrpc *r)
+{
+ int n;
+
+ while(waserror()) {
+ if(m->rip == up)
+ mntgate(m);
+ if(strcmp(up->env->errstr, Eintr) != 0){
+ mntflushfree(m, r);
+ nexterror();
+ }
+ r = mntflushalloc(r, m->msize);
+ }
+
+ lock(&m->l);
+ r->m = m;
+ r->list = m->queue;
+ m->queue = r;
+ unlock(&m->l);
+
+ /* Transmit a file system rpc */
+ if(m->msize == 0)
+ panic("msize");
+ n = convS2M(&r->request, r->rpc, m->msize);
+ if(n < 0)
+ panic("bad message type in mountio");
+ if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
+ error(Emountrpc);
+/* r->stime = fastticks(nil); */
+ r->reqlen = n;
+
+ /* Gate readers onto the mount point one at a time */
+ for(;;) {
+ lock(&m->l);
+ if(m->rip == 0)
+ break;
+ unlock(&m->l);
+ Sleep(&r->r, rpcattn, r);
+ if(r->done){
+ poperror();
+ mntflushfree(m, r);
+ return;
+ }
+ }
+ m->rip = up;
+ unlock(&m->l);
+ while(r->done == 0) {
+ if(mntrpcread(m, r) < 0)
+ error(Emountrpc);
+ mountmux(m, r);
+ }
+ mntgate(m);
+ poperror();
+ mntflushfree(m, r);
+}
+
+static int
+doread(Mnt *m, int len)
+{
+ Block *b;
+
+ while(qlen(m->q) < len){
+ b = devtab[m->c->type]->bread(m->c, m->msize, 0);
+ if(b == nil)
+ return -1;
+ if(blocklen(b) == 0){
+ freeblist(b);
+ return -1;
+ }
+ qaddlist(m->q, b);
+ }
+ return 0;
+}
+
+int
+mntrpcread(Mnt *m, Mntrpc *r)
+{
+ int i, t, len, hlen;
+ Block *b, **l, *nb;
+
+ r->reply.type = 0;
+ r->reply.tag = 0;
+
+ /* read at least length, type, and tag and pullup to a single block */
+ if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
+ return -1;
+ nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
+
+ /* read in the rest of the message, avoid ridiculous (for now) message sizes */
+ len = GBIT32(nb->rp);
+ if(len > m->msize){
+ qdiscard(m->q, qlen(m->q));
+ return -1;
+ }
+ if(doread(m, len) < 0)
+ return -1;
+
+ /* pullup the header (i.e. everything except data) */
+ t = nb->rp[BIT32SZ];
+ switch(t){
+ case Rread:
+ hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
+ break;
+ default:
+ hlen = len;
+ break;
+ }
+ nb = pullupqueue(m->q, hlen);
+
+ if(convM2S(nb->rp, len, &r->reply) <= 0){
+ /* bad message, dump it */
+ print("mntrpcread: convM2S failed\n");
+ qdiscard(m->q, len);
+ return -1;
+ }
+
+ /* hang the data off of the fcall struct */
+ l = &r->b;
+ *l = nil;
+ do {
+ b = qremove(m->q);
+ if(hlen > 0){
+ b->rp += hlen;
+ len -= hlen;
+ hlen = 0;
+ }
+ i = BLEN(b);
+ if(i <= len){
+ len -= i;
+ *l = b;
+ l = &(b->next);
+ } else {
+ /* split block and put unused bit back */
+ nb = allocb(i-len);
+ memmove(nb->wp, b->rp+len, i-len);
+ b->wp = b->rp+len;
+ nb->wp += i-len;
+ qputback(m->q, nb);
+ *l = b;
+ return 0;
+ }
+ }while(len > 0);
+
+ return 0;
+}
+
+void
+mntgate(Mnt *m)
+{
+ Mntrpc *q;
+
+ lock(&m->l);
+ m->rip = 0;
+ for(q = m->queue; q; q = q->list) {
+ if(q->done == 0)
+ if(Wakeup(&q->r))
+ break;
+ }
+ unlock(&m->l);
+}
+
+void
+mountmux(Mnt *m, Mntrpc *r)
+{
+ Mntrpc **l, *q;
+
+ lock(&m->l);
+ l = &m->queue;
+ for(q = *l; q; q = q->list) {
+ /* look for a reply to a message */
+ if(q->request.tag == r->reply.tag) {
+ *l = q->list;
+ if(q != r) {
+ /*
+ * Completed someone else.
+ * Trade pointers to receive buffer.
+ */
+ q->reply = r->reply;
+ q->b = r->b;
+ r->b = nil;
+ }
+ q->done = 1;
+ unlock(&m->l);
+ if(mntstats != nil)
+ (*mntstats)(q->request.type,
+ m->c, q->stime,
+ q->reqlen + r->replen);
+ if(q != r)
+ Wakeup(&q->r);
+ return;
+ }
+ l = &q->list;
+ }
+ unlock(&m->l);
+ if(r->reply.type == Rerror)
+ print("unexpected reply tag %ud; type %d (error %q)\n", r->reply.tag, r->reply.type, r->reply.ename);
+ else
+ print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
+}
+
+/*
+ * Create a new flush request and chain the previous
+ * requests from it
+ */
+Mntrpc*
+mntflushalloc(Mntrpc *r, ulong iounit)
+{
+ Mntrpc *fr;
+
+ fr = mntralloc(0, iounit);
+
+ fr->request.type = Tflush;
+ if(r->request.type == Tflush)
+ fr->request.oldtag = r->request.oldtag;
+ else
+ fr->request.oldtag = r->request.tag;
+ fr->flushed = r;
+
+ return fr;
+}
+
+/*
+ * Free a chain of flushes. Remove each unanswered
+ * flush and the original message from the unanswered
+ * request queue. Mark the original message as done
+ * and if it hasn't been answered set the reply to to
+ * Rflush.
+ */
+void
+mntflushfree(Mnt *m, Mntrpc *r)
+{
+ Mntrpc *fr;
+
+ while(r){
+ fr = r->flushed;
+ if(!r->done){
+ r->reply.type = Rflush;
+ mntqrm(m, r);
+ }
+ if(fr)
+ mntfree(r);
+ r = fr;
+ }
+}
+
+static int
+alloctag(void)
+{
+ int i, j;
+ ulong v;
+
+ for(i = 0; i < NMASK; i++){
+ v = mntalloc.tagmask[i];
+ if(v == ~0UL)
+ continue;
+ for(j = 0; j < 1<<TAGSHIFT; j++)
+ if((v & (1<<j)) == 0){
+ mntalloc.tagmask[i] |= 1<<j;
+ return (i<<TAGSHIFT) + j;
+ }
+ }
+ /* panic("no devmnt tags left"); */
+ return NOTAG;
+}
+
+static void
+freetag(int t)
+{
+ mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
+}
+
+Mntrpc*
+mntralloc(Chan *c, ulong msize)
+{
+ Mntrpc *new;
+
+ lock(&mntalloc.l);
+ new = mntalloc.rpcfree;
+ if(new == nil){
+ new = malloc(sizeof(Mntrpc));
+ if(new == nil) {
+ unlock(&mntalloc.l);
+ exhausted("mount rpc header");
+ }
+ /*
+ * The header is split from the data buffer as
+ * mountmux may swap the buffer with another header.
+ */
+ new->rpc = mallocz(msize, 0);
+ if(new->rpc == nil){
+ free(new);
+ unlock(&mntalloc.l);
+ exhausted("mount rpc buffer");
+ }
+ new->rpclen = msize;
+ new->request.tag = alloctag();
+ if(new->request.tag == NOTAG){
+ free(new);
+ unlock(&mntalloc.l);
+ exhausted("rpc tags");
+ }
+ }
+ else {
+ mntalloc.rpcfree = new->list;
+ mntalloc.nrpcfree--;
+ if(new->rpclen < msize){
+ free(new->rpc);
+ new->rpc = mallocz(msize, 0);
+ if(new->rpc == nil){
+ free(new);
+ mntalloc.nrpcused--;
+ unlock(&mntalloc.l);
+ exhausted("mount rpc buffer");
+ }
+ new->rpclen = msize;
+ }
+ }
+ mntalloc.nrpcused++;
+ unlock(&mntalloc.l);
+ new->c = c;
+ new->done = 0;
+ new->flushed = nil;
+ new->b = nil;
+ return new;
+}
+
+void
+mntfree(Mntrpc *r)
+{
+ if(r->b != nil)
+ freeblist(r->b);
+ lock(&mntalloc.l);
+ if(mntalloc.nrpcfree >= 10){
+ free(r->rpc);
+ freetag(r->request.tag);
+ free(r);
+ }
+ else{
+ r->list = mntalloc.rpcfree;
+ mntalloc.rpcfree = r;
+ mntalloc.nrpcfree++;
+ }
+ mntalloc.nrpcused--;
+ unlock(&mntalloc.l);
+}
+
+void
+mntqrm(Mnt *m, Mntrpc *r)
+{
+ Mntrpc **l, *f;
+
+ lock(&m->l);
+ r->done = 1;
+
+ l = &m->queue;
+ for(f = *l; f; f = f->list) {
+ if(f == r) {
+ *l = r->list;
+ break;
+ }
+ l = &f->list;
+ }
+ unlock(&m->l);
+}
+
+Mnt*
+mntchk(Chan *c)
+{
+ Mnt *m;
+
+ /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
+
+ if(c->mchan == nil)
+ panic("mntchk 1: nil mchan c %s\n", c2name(c));
+
+ m = c->mchan->mux;
+
+ if(m == nil)
+ print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan));
+
+ /*
+ * Was it closed and reused (was error(Eshutdown); now, it can't happen)
+ */
+ if(m->id == 0 || m->id >= c->dev)
+ panic("mntchk 3: can't happen");
+
+ return m;
+}
+
+/*
+ * Rewrite channel type and dev for in-flight data to
+ * reflect local values. These entries are known to be
+ * the first two in the Dir encoding after the count.
+ */
+void
+mntdirfix(uchar *dirbuf, Chan *c)
+{
+ uint r;
+
+ r = devtab[c->type]->dc;
+ dirbuf += BIT16SZ; /* skip count */
+ PBIT16(dirbuf, r);
+ dirbuf += BIT16SZ;
+ PBIT32(dirbuf, c->dev);
+}
+
+int
+rpcattn(void *v)
+{
+ Mntrpc *r;
+
+ r = v;
+ return r->done || r->m->rip == 0;
+}
+
+Dev mntdevtab = {
+ 'M',
+ "mnt",
+
+ mntinit,
+ mntattach,
+ mntwalk,
+ mntstat,
+ mntopen,
+ mntcreate,
+ mntclose,
+ mntread,
+ devbread,
+ mntwrite,
+ devbwrite,
+ mntremove,
+ mntwstat,
+};
--- /dev/null
+++ b/emu/port/devpipe.c
@@ -1,0 +1,460 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <interp.h>
+
+#define NETTYPE(x) ((ulong)(x)&0x1f)
+#define NETID(x) (((ulong)(x))>>5)
+#define NETQID(i,t) (((i)<<5)|(t))
+
+typedef struct Pipe Pipe;
+struct Pipe
+{
+ QLock l;
+ Pipe* next;
+ int ref;
+ ulong path;
+ Queue* q[2];
+ int qref[2];
+ Dirtab* pipedir;
+ char* user;
+};
+
+static struct
+{
+ Lock l;
+ ulong path;
+ int pipeqsize;
+} pipealloc;
+
+enum
+{
+ Qdir,
+ Qdata0,
+ Qdata1
+};
+
+Dirtab pipedir[] =
+{
+ ".", {Qdir,0,QTDIR}, 0, DMDIR|0500,
+ "data", {Qdata0}, 0, 0660,
+ "data1", {Qdata1}, 0, 0660,
+};
+
+static void
+freepipe(Pipe *p)
+{
+ if(p != nil){
+ free(p->user);
+ free(p->q[0]);
+ free(p->q[1]);
+ free(p->pipedir);
+ free(p);
+ }
+}
+
+static void
+pipeinit(void)
+{
+ pipealloc.pipeqsize = 32*1024;
+}
+
+/*
+ * create a pipe, no streams are created until an open
+ */
+static Chan*
+pipeattach(char *spec)
+{
+ Pipe *p;
+ Chan *c;
+
+ c = devattach('|', spec);
+ p = malloc(sizeof(Pipe));
+ if(p == 0)
+ error(Enomem);
+ if(waserror()){
+ freepipe(p);
+ nexterror();
+ }
+ p->pipedir = malloc(sizeof(pipedir));
+ if (p->pipedir == 0)
+ error(Enomem);
+ memmove(p->pipedir, pipedir, sizeof(pipedir));
+ kstrdup(&p->user, up->env->user);
+ p->ref = 1;
+
+ p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0);
+ if(p->q[0] == 0)
+ error(Enomem);
+ p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0);
+ if(p->q[1] == 0)
+ error(Enomem);
+ poperror();
+
+ lock(&pipealloc.l);
+ p->path = ++pipealloc.path;
+ unlock(&pipealloc.l);
+
+ c->qid.path = NETQID(2*p->path, Qdir);
+ c->qid.vers = 0;
+ c->qid.type = QTDIR;
+ c->aux = p;
+ c->dev = 0;
+ return c;
+}
+
+static int
+pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+ int id, len;
+ Qid qid;
+ Pipe *p;
+
+ USED(name);
+ if(i == DEVDOTDOT){
+ devdir(c, c->qid, "#|", 0, eve, 0555, dp);
+ return 1;
+ }
+ i++; /* skip . */
+ if(tab==0 || i>=ntab)
+ return -1;
+ tab += i;
+ p = c->aux;
+ switch(NETTYPE(tab->qid.path)){
+ case Qdata0:
+ len = qlen(p->q[0]);
+ break;
+ case Qdata1:
+ len = qlen(p->q[1]);
+ break;
+ default:
+ len = tab->length;
+ break;
+ }
+ id = NETID(c->qid.path);
+ qid.path = NETQID(id, tab->qid.path);
+ qid.vers = 0;
+ qid.type = QTFILE;
+ devdir(c, qid, tab->name, len, eve, tab->perm, dp);
+ return 1;
+}
+
+static Walkqid*
+pipewalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ Walkqid *wq;
+ Pipe *p;
+
+ p = c->aux;
+ wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen);
+ if(wq != nil && wq->clone != nil && wq->clone != c){
+ qlock(&p->l);
+ p->ref++;
+ if(c->flag & COPEN){
+ switch(NETTYPE(c->qid.path)){
+ case Qdata0:
+ p->qref[0]++;
+ break;
+ case Qdata1:
+ p->qref[1]++;
+ break;
+ }
+ }
+ qunlock(&p->l);
+ }
+ return wq;
+}
+
+static int
+pipestat(Chan *c, uchar *db, int n)
+{
+ Pipe *p;
+ Dir dir;
+ Dirtab *tab;
+
+ p = c->aux;
+ tab = p->pipedir;
+
+ switch(NETTYPE(c->qid.path)){
+ case Qdir:
+ devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir);
+ break;
+ case Qdata0:
+ devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir);
+ break;
+ case Qdata1:
+ devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir);
+ break;
+ default:
+ panic("pipestat");
+ }
+ n = convD2M(&dir, db, n);
+ if(n < BIT16SZ)
+ error(Eshortstat);
+ return n;
+}
+
+/*
+ * if the stream doesn't exist, create it
+ */
+static Chan*
+pipeopen(Chan *c, int omode)
+{
+ Pipe *p;
+
+ if(c->qid.type & QTDIR){
+ if(omode != OREAD)
+ error(Ebadarg);
+ c->mode = omode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ openmode(omode); /* check it */
+
+ p = c->aux;
+ qlock(&p->l);
+ if(waserror()){
+ qunlock(&p->l);
+ nexterror();
+ }
+ switch(NETTYPE(c->qid.path)){
+ case Qdata0:
+ devpermcheck(p->user, p->pipedir[1].perm, omode);
+ p->qref[0]++;
+ break;
+ case Qdata1:
+ devpermcheck(p->user, p->pipedir[2].perm, omode);
+ p->qref[1]++;
+ break;
+ }
+ poperror();
+ qunlock(&p->l);
+
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ c->iounit = qiomaxatomic;
+ return c;
+}
+
+static void
+pipeclose(Chan *c)
+{
+ Pipe *p;
+
+ p = c->aux;
+ qlock(&p->l);
+
+ if(c->flag & COPEN){
+ /*
+ * closing either side hangs up the stream
+ */
+ switch(NETTYPE(c->qid.path)){
+ case Qdata0:
+ p->qref[0]--;
+ if(p->qref[0] == 0){
+ qhangup(p->q[1], 0);
+ qclose(p->q[0]);
+ }
+ break;
+ case Qdata1:
+ p->qref[1]--;
+ if(p->qref[1] == 0){
+ qhangup(p->q[0], 0);
+ qclose(p->q[1]);
+ }
+ break;
+ }
+ }
+
+
+ /*
+ * if both sides are closed, they are reusable
+ */
+ if(p->qref[0] == 0 && p->qref[1] == 0){
+ qreopen(p->q[0]);
+ qreopen(p->q[1]);
+ }
+
+ /*
+ * free the structure on last close
+ */
+ p->ref--;
+ if(p->ref == 0){
+ qunlock(&p->l);
+ freepipe(p);
+ } else
+ qunlock(&p->l);
+}
+
+static long
+piperead(Chan *c, void *va, long n, vlong junk)
+{
+ Pipe *p;
+
+ p = c->aux;
+
+ USED(junk);
+ switch(NETTYPE(c->qid.path)){
+ case Qdir:
+ return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen);
+ case Qdata0:
+ return qread(p->q[0], va, n);
+ case Qdata1:
+ return qread(p->q[1], va, n);
+ default:
+ panic("piperead");
+ }
+ return -1; /* not reached */
+}
+
+static Block*
+pipebread(Chan *c, long n, ulong offset)
+{
+ Pipe *p;
+
+ p = c->aux;
+
+ switch(NETTYPE(c->qid.path)){
+ case Qdata0:
+ return qbread(p->q[0], n);
+ case Qdata1:
+ return qbread(p->q[1], n);
+ }
+
+ return devbread(c, n, offset);
+}
+
+/*
+ * a write to a closed pipe causes an exception to be sent to
+ * the prog.
+ */
+static long
+pipewrite(Chan *c, void *va, long n, vlong junk)
+{
+ Pipe *p;
+ Prog *r;
+
+ USED(junk);
+ if(waserror()) {
+ /* avoid exceptions when pipe is a mounted queue */
+ if((c->flag & CMSG) == 0) {
+ r = up->iprog;
+ if(r != nil && r->kill == nil)
+ r->kill = "write on closed pipe";
+ }
+ nexterror();
+ }
+
+ p = c->aux;
+
+ switch(NETTYPE(c->qid.path)){
+ case Qdata0:
+ n = qwrite(p->q[1], va, n);
+ break;
+
+ case Qdata1:
+ n = qwrite(p->q[0], va, n);
+ break;
+
+ default:
+ panic("pipewrite");
+ }
+
+ poperror();
+ return n;
+}
+
+static long
+pipebwrite(Chan *c, Block *bp, ulong junk)
+{
+ long n;
+ Pipe *p;
+ Prog *r;
+
+ USED(junk);
+ if(waserror()) {
+ /* avoid exceptions when pipe is a mounted queue */
+ if((c->flag & CMSG) == 0) {
+ r = up->iprog;
+ if(r != nil && r->kill == nil)
+ r->kill = "write on closed pipe";
+ }
+ nexterror();
+ }
+
+ p = c->aux;
+ switch(NETTYPE(c->qid.path)){
+ case Qdata0:
+ n = qbwrite(p->q[1], bp);
+ break;
+
+ case Qdata1:
+ n = qbwrite(p->q[0], bp);
+ break;
+
+ default:
+ n = 0;
+ panic("pipebwrite");
+ }
+
+ poperror();
+ return n;
+}
+
+static int
+pipewstat(Chan *c, uchar *dp, int n)
+{
+ Dir *d;
+ Pipe *p;
+ int d1;
+
+ if (c->qid.type&QTDIR)
+ error(Eperm);
+ p = c->aux;
+ if(strcmp(up->env->user, p->user) != 0)
+ error(Eperm);
+ d = smalloc(sizeof(*d)+n);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ n = convM2D(dp, n, d, (char*)&d[1]);
+ if(n == 0)
+ error(Eshortstat);
+ d1 = NETTYPE(c->qid.path) == Qdata1;
+ if(!emptystr(d->name)){
+ validwstatname(d->name);
+ if(strlen(d->name) >= KNAMELEN)
+ error(Efilename);
+ if(strcmp(p->pipedir[1+!d1].name, d->name) == 0)
+ error(Eexist);
+ kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN);
+ }
+ if(d->mode != ~0UL)
+ p->pipedir[d1 + 1].perm = d->mode & 0777;
+ poperror();
+ free(d);
+ return n;
+}
+
+Dev pipedevtab = {
+ '|',
+ "pipe",
+
+ pipeinit,
+ pipeattach,
+ pipewalk,
+ pipestat,
+ pipeopen,
+ devcreate,
+ pipeclose,
+ piperead,
+ pipebread,
+ pipewrite,
+ pipebwrite,
+ devremove,
+ pipewstat,
+};
--- /dev/null
+++ b/emu/port/devpointer.c
@@ -1,0 +1,295 @@
+/*
+ * mouse or stylus
+ */
+
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#define cursorenable()
+#define cursordisable()
+
+enum{
+ Qdir,
+ Qpointer,
+ Qcursor
+};
+
+typedef struct Pointer Pointer;
+
+struct Pointer {
+ int x;
+ int y;
+ int b;
+ ulong msec;
+};
+
+static struct
+{
+ Pointer v;
+ int modify;
+ int lastb;
+ Rendez r;
+ Ref ref;
+ QLock q;
+} mouse;
+
+static
+Dirtab pointertab[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "pointer", {Qpointer}, 0, 0666,
+ "cursor", {Qcursor}, 0, 0222,
+};
+
+enum {
+ Nevent = 16 /* enough for some */
+};
+
+static struct {
+ int rd;
+ int wr;
+ Pointer clicks[Nevent];
+ Rendez r;
+ int full;
+ int put;
+ int get;
+} ptrq;
+
+/*
+ * called by any source of pointer data
+ */
+void
+mousetrack(int b, int x, int y, int isdelta)
+{
+ int lastb;
+ ulong msec;
+ Pointer e;
+
+ if(isdelta){
+ x += mouse.v.x;
+ y += mouse.v.y;
+ }
+ msec = osmillisec();
+ lastb = mouse.v.b;
+ mouse.v.x = x;
+ mouse.v.y = y;
+ mouse.v.b = b;
+ mouse.v.msec = msec;
+ if(!ptrq.full && lastb != b){
+ e = mouse.v;
+ ptrq.clicks[ptrq.wr] = e;
+ if(++ptrq.wr >= Nevent)
+ ptrq.wr = 0;
+ if(ptrq.wr == ptrq.rd)
+ ptrq.full = 1;
+ }
+ mouse.modify = 1;
+ ptrq.put++;
+ Wakeup(&ptrq.r);
+/* drawactive(1); */
+/* setpointer(x, y); */
+}
+
+static int
+ptrqnotempty(void *x)
+{
+ USED(x);
+ return ptrq.full || ptrq.put != ptrq.get;
+}
+
+static Pointer
+mouseconsume(void)
+{
+ Pointer e;
+
+ Sleep(&ptrq.r, ptrqnotempty, 0);
+ ptrq.full = 0;
+ ptrq.get = ptrq.put;
+ if(ptrq.rd != ptrq.wr){
+ e = ptrq.clicks[ptrq.rd];
+ if(++ptrq.rd >= Nevent)
+ ptrq.rd = 0;
+ }else
+ e = mouse.v;
+ return e;
+}
+
+Point
+mousexy(void)
+{
+ return Pt(mouse.v.x, mouse.v.y);
+}
+
+
+static Chan*
+pointerattach(char* spec)
+{
+ return devattach('m', spec);
+}
+
+static Walkqid*
+pointerwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ Walkqid *wq;
+
+ wq = devwalk(c, nc, name, nname, pointertab, nelem(pointertab), devgen);
+ if(wq != nil && wq->clone != c && wq->clone != nil && (ulong)c->qid.path == Qpointer)
+ incref(&mouse.ref); /* can this happen? */
+ return wq;
+}
+
+static int
+pointerstat(Chan* c, uchar *db, int n)
+{
+ return devstat(c, db, n, pointertab, nelem(pointertab), devgen);
+}
+
+static Chan*
+pointeropen(Chan* c, int omode)
+{
+ c = devopen(c, omode, pointertab, nelem(pointertab), devgen);
+ if((ulong)c->qid.path == Qpointer){
+ if(waserror()){
+ c->flag &= ~COPEN;
+ nexterror();
+ }
+ if(!canqlock(&mouse.q))
+ error(Einuse);
+ if(incref(&mouse.ref) != 1){
+ qunlock(&mouse.q);
+ error(Einuse);
+ }
+ cursorenable();
+ qunlock(&mouse.q);
+ poperror();
+ }
+ return c;
+}
+
+static void
+pointerclose(Chan* c)
+{
+ if((c->flag & COPEN) == 0)
+ return;
+ switch((ulong)c->qid.path){
+ case Qpointer:
+ qlock(&mouse.q);
+ if(decref(&mouse.ref) == 0){
+ cursordisable();
+ }
+ qunlock(&mouse.q);
+ break;
+ }
+}
+
+static long
+pointerread(Chan* c, void* a, long n, vlong off)
+{
+ Pointer mt;
+ char buf[1+4*12+1];
+ int l;
+
+ USED(&off);
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, pointertab, nelem(pointertab), devgen);
+ case Qpointer:
+ qlock(&mouse.q);
+ if(waserror()) {
+ qunlock(&mouse.q);
+ nexterror();
+ }
+ mt = mouseconsume();
+ poperror();
+ qunlock(&mouse.q);
+ l = snprint(buf, sizeof(buf), "m%11d %11d %11d %11lud ", mt.x, mt.y, mt.b, mt.msec);
+ if(l < n)
+ n = l;
+ memmove(a, buf, n);
+ break;
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+pointerwrite(Chan* c, void* va, long n, vlong off)
+{
+ char *a = va;
+ char buf[128];
+ int b, x, y;
+ Drawcursor cur;
+
+ USED(&off);
+ switch((ulong)c->qid.path){
+ case Qpointer:
+ if(n > sizeof buf-1)
+ n = sizeof buf -1;
+ memmove(buf, va, n);
+ buf[n] = 0;
+ x = strtoul(buf+1, &a, 0);
+ if(*a == 0)
+ error(Eshort);
+ y = strtoul(a, &a, 0);
+ if(*a != 0)
+ b = strtoul(a, 0, 0);
+ else
+ b = mouse.v.b;
+ /*mousetrack(b, x, y, msec);*/
+ setpointer(x, y);
+ USED(b);
+ break;
+ case Qcursor:
+ /* TO DO: perhaps interpret data as an Image */
+ /*
+ * hotx[4] hoty[4] dx[4] dy[4] clr[dx/8 * dy/2] set[dx/8 * dy/2]
+ * dx must be a multiple of 8; dy must be a multiple of 2.
+ */
+ if(n == 0){
+ cur.data = nil;
+ drawcursor(&cur);
+ break;
+ }
+ if(n < 8)
+ error(Eshort);
+ cur.hotx = BGLONG((uchar*)va+0*4);
+ cur.hoty = BGLONG((uchar*)va+1*4);
+ cur.minx = 0;
+ cur.miny = 0;
+ cur.maxx = BGLONG((uchar*)va+2*4);
+ cur.maxy = BGLONG((uchar*)va+3*4);
+ if(cur.maxx%8 != 0 || cur.maxy%2 != 0 || n-4*4 != (cur.maxx/8 * cur.maxy))
+ error(Ebadarg);
+ cur.data = (uchar*)va + 4*4;
+ drawcursor(&cur);
+ break;
+ default:
+ error(Ebadusefd);
+ }
+ return n;
+}
+
+Dev pointerdevtab = {
+ 'm',
+ "pointer",
+
+ devinit,
+ pointerattach,
+ pointerwalk,
+ pointerstat,
+ pointeropen,
+ devcreate,
+ pointerclose,
+ pointerread,
+ devbread,
+ pointerwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/emu/port/devprof.c
@@ -1,0 +1,817 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include <isa.h>
+#include "runt.h"
+
+extern Pool* imagmem;
+extern void (*memmonitor)(int, ulong, ulong, ulong);
+
+static void cpxec(Prog *);
+static void memprof(int, void*, ulong);
+static void memprofmi(int, ulong, ulong, ulong);
+
+extern Inst* pc2dispc(Inst*, Module*);
+
+static int interval = 100; /* Sampling interval in milliseconds */
+
+enum
+{
+ HSIZE = 32,
+};
+
+#define HASH(m) ((m)%HSIZE)
+
+/* cope with multiple profilers some day */
+
+typedef struct Record Record;
+struct Record
+{
+ int id;
+ char* name;
+ char* path;
+ Inst* base;
+ int size;
+ /*Module* m; */
+ ulong mtime;
+ Qid qid;
+ Record* hash;
+ Record* link;
+ ulong bucket[1];
+};
+
+struct
+{
+ Lock l;
+ vlong time;
+ Record* hash[HSIZE];
+ Record* list;
+} profile;
+
+typedef struct Pmod Pmod;
+struct Pmod
+{
+ char* name;
+ Pmod* link;
+} *pmods;
+
+#define QSHIFT 4
+#define QID(q) ((ulong)(q).path&0xf)
+#define QPID(pid) ((pid)<<QSHIFT)
+#define PID(q) ((q).vers)
+#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1))
+
+enum
+{
+ Qdir,
+ Qname,
+ Qpath,
+ Qhist,
+ Qpctl,
+ Qctl,
+};
+
+Dirtab profdir[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "name", {Qname}, 0, 0444,
+ "path", {Qpath}, 0, 0444,
+ "histogram", {Qhist}, 0, 0444,
+ "pctl", {Qpctl}, 0, 0222,
+ "ctl", {Qctl}, 0, 0222,
+};
+
+enum{
+ Pnil, /* null profiler */
+ Psam, /* sampling profiler */
+ Pcov, /* coverage profiler */
+ Pmem, /* heap memory profiler */
+};
+
+enum{
+ Mnone = 0,
+ Mmain = 1,
+ Mheap = 2,
+ Mimage = 4,
+};
+
+static int profiler = Pnil;
+static int mprofiler = Mnone;
+
+static int ids;
+static int samplefn;
+
+static void sampler(void*);
+
+static Record*
+getrec(int id)
+{
+ Record *r;
+
+ for(r = profile.list; r != nil; r = r->link)
+ if(r->id == id)
+ break;
+ return r;
+}
+
+static void
+addpmod(char *m)
+{
+ Pmod *p = malloc(sizeof(Pmod));
+
+ if(p == nil)
+ return;
+ p->name = malloc(strlen(m)+1);
+ if(p->name == nil){
+ free(p);
+ return;
+ }
+ strcpy(p->name, m);
+ p->link = pmods;
+ pmods = p;
+}
+
+static void
+freepmods(void)
+{
+ Pmod *p, *np;
+
+ for(p = pmods; p != nil; p = np){
+ free(p->name);
+ np = p->link;
+ free(p);
+ }
+ pmods = nil;
+}
+
+static int
+inpmods(char *m)
+{
+ Pmod *p;
+
+ for(p = pmods; p != nil; p = p->link)
+ if(strcmp(p->name, m) == 0)
+ return 1;
+ return 0;
+}
+
+static void
+freeprof(void)
+{
+ int i;
+ Record *r, *nr;
+
+ ids = 0;
+ profiler = Pnil;
+ mprofiler = Mnone;
+ freepmods();
+ for(r = profile.list; r != nil; r = nr){
+ free(r->name);
+ free(r->path);
+ nr = r->link;
+ free(r);
+ }
+ profile.list = nil;
+ profile.time = 0;
+ for(i = 0; i < HSIZE; i++)
+ profile.hash[i] = nil;
+}
+
+static int
+profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
+{
+ Qid qid;
+ Record *r;
+ ulong path, perm, len;
+ Dirtab *tab;
+
+ USED(name);
+ USED(d);
+ USED(nd);
+
+ if(s == DEVDOTDOT) {
+ mkqid(&qid, Qdir, 0, QTDIR);
+ devdir(c, qid, "#P", 0, eve, 0555, dp);
+ return 1;
+ }
+
+ if(c->qid.path == Qdir && c->qid.type & QTDIR) {
+ acquire();
+ if(s-- == 0){
+ tab = &profdir[Qctl];
+ mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE);
+ devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
+ release();
+ return 1;
+ }
+ r = profile.list;
+ while(s-- && r != nil)
+ r = r->link;
+ if(r == nil) {
+ release();
+ return -1;
+ }
+ sprint(up->genbuf, "%.8lux", (ulong)r->id);
+ mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR);
+ devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp);
+ release();
+ return 1;
+ }
+ if(s >= nelem(profdir)-1)
+ error(Enonexist); /* was return -1; */
+ tab = &profdir[s];
+ path = PATH(c->qid);
+
+ acquire();
+ r = getrec(PID(c->qid));
+ if(r == nil) {
+ release();
+ error(Enonexist); /* was return -1; */
+ }
+
+ perm = tab->perm;
+ len = tab->length;
+ mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+ devdir(c, qid, tab->name, len, eve, perm, dp);
+ release();
+ return 1;
+}
+
+static Chan*
+profattach(char *spec)
+{
+ return devattach('P', spec);
+}
+
+static Walkqid*
+profwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, profgen);
+}
+
+static int
+profstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, profgen);
+}
+
+static Chan*
+profopen(Chan *c, int omode)
+{
+ int qid;
+ Record *r;
+
+ if(c->qid.type & QTDIR) {
+ if(omode != OREAD)
+ error(Eisdir);
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ if(omode&OTRUNC)
+ error(Eperm);
+
+ qid = QID(c->qid);
+ if(qid == Qctl || qid == Qpctl){
+ if (omode != OWRITE)
+ error(Eperm);
+ }
+ else{
+ if(omode != OREAD)
+ error(Eperm);
+ }
+
+ if(qid != Qctl){
+ acquire();
+ r = getrec(PID(c->qid));
+ release();
+ if(r == nil)
+ error(Ethread);
+ }
+
+ c->offset = 0;
+ c->flag |= COPEN;
+ c->mode = openmode(omode);
+ if(QID(c->qid) == Qhist)
+ c->aux = nil;
+ return c;
+}
+
+static int
+profwstat(Chan *c, uchar *dp, int n)
+{
+ Dir d;
+ Record *r;
+
+ if(strcmp(up->env->user, eve))
+ error(Eperm);
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+ acquire();
+ r = getrec(PID(c->qid));
+ release();
+ if(r == nil)
+ error(Ethread);
+ n = convM2D(dp, n, &d, nil);
+ if(n == 0)
+ error(Eshortstat);
+ d.mode &= 0777;
+ /* TO DO: copy to c->aux->perm, once that exists */
+ return n;
+}
+
+static void
+profclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+profread(Chan *c, void *va, long n, vlong offset)
+{
+ int i;
+ Record *r;
+ char *a = va;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, profgen);
+ acquire();
+ r = getrec(PID(c->qid));
+ release();
+ if(r == nil)
+ error(Ethread);
+ switch(QID(c->qid)){
+ case Qname:
+ return readstr(offset, va, n, r->name);
+ case Qpath:
+ return readstr(offset, va, n, r->path);
+ case Qhist:
+ i = (int)c->aux;
+ while(i < r->size && r->bucket[i] == 0)
+ i++;
+ if(i >= r->size)
+ return 0;
+ c->aux = (void*)(i+1);
+ if(n < 20)
+ error(Etoosmall);
+ return sprint(a, "%d %lud", i, r->bucket[i]);
+ case Qctl:
+ error(Eperm);
+ }
+ return 0;
+}
+
+static long
+profwrite(Chan *c, void *va, long n, vlong offset)
+{
+ int i;
+ char *a = va;
+ char buf[128], *fields[128];
+ void (*f)(int, ulong, ulong, ulong);
+
+ USED(va);
+ USED(n);
+ USED(offset);
+
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+ switch(QID(c->qid)){
+ case Qctl:
+ if(n > sizeof(buf)-1)
+ n = sizeof(buf)-1;
+ memmove(buf, a, n);
+ buf[n] = 0;
+ i = getfields(buf, fields, nelem(fields), 1, " \t\n");
+ if(i > 0 && strcmp(fields[0], "module") == 0){
+ f = memmonitor;
+ memmonitor = nil;
+ freepmods();
+ while(--i > 0)
+ addpmod(fields[i]);
+ memmonitor = f;
+ return n;
+ }
+ if(i == 1){
+ if(strcmp(fields[0], "start") == 0){
+ if(profiler == Pnil) {
+ profiler = Psam;
+ if(!samplefn){
+ samplefn = 1;
+ kproc("prof", sampler, 0, 0);
+ }
+ }
+ }
+ else if(strncmp(fields[0], "startmp", 7) == 0){
+ if(profiler == Pnil){
+ profiler = Pmem;
+ for(a = &fields[0][7]; *a != '\0'; a++){
+ if(*a == '1'){
+ memmonitor = memprofmi;
+ mprofiler |= Mmain;
+ }
+ else if(*a == '2'){
+ heapmonitor = memprof;
+ mprofiler |= Mheap;
+ }
+ else if(*a == '3'){
+ memmonitor = memprofmi;
+ mprofiler |= Mimage;
+ }
+ };
+ }
+ }
+ else if(strcmp(fields[0], "stop") == 0){
+ profiler = Pnil;
+ mprofiler = Mnone;
+ }
+ else if(strcmp(fields[0], "end") == 0){
+ profiler = Pnil;
+ mprofiler = Mnone;
+ memmonitor = nil;
+ freeprof();
+ interval = 100;
+ }
+ else
+ error(Ebadarg);
+ }
+ else if (i == 2){
+ if(strcmp(fields[0], "interval") == 0)
+ interval = strtoul(fields[1], nil, 0);
+ else if(strcmp(fields[0], "startcp") == 0){
+ Prog *p;
+
+ acquire();
+ p = progpid(strtoul(fields[1], nil, 0));
+ if(p == nil){
+ release();
+ return -1;
+ }
+ if(profiler == Pnil){
+ profiler = Pcov;
+ p->xec = cpxec;
+ }
+ release();
+ }
+ else
+ error(Ebadarg);
+ }
+ else
+ error(Ebadarg);
+ return n;
+ default:
+ error(Eperm);
+ }
+ return 0;
+}
+
+static Record*
+newmodule(Module *m, int vm, int scale, int origin)
+{
+ int dsize;
+ Record *r, **l;
+
+ if(!vm)
+ acquire();
+ if((m->compiled && m->pctab == nil) || m->prog == nil) {
+ if(!vm)
+ release();
+ return nil;
+ }
+ if(m->compiled)
+ dsize = m->nprog * sizeof(r->bucket[0]);
+ else
+ dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]);
+ dsize *= scale;
+ dsize += origin;
+ r = malloc(sizeof(Record)+dsize);
+ if(r == nil) {
+ if(!vm)
+ release();
+ return nil;
+ }
+
+ r->id = ++ids;
+ if(ids == (1<<8)-1)
+ ids = 0;
+ kstrdup(&r->name, m->name);
+ kstrdup(&r->path, m->path);
+ r->base = m->prog;
+ r->size = dsize/sizeof(r->bucket[0]);
+ /* r->m = m; */
+ r->mtime = m->mtime;
+ r->qid.path = m->qid.path;
+ r->qid.vers = m->qid.vers;
+ memset(r->bucket, 0, dsize);
+ r->link = profile.list;
+ profile.list = r;
+
+ l = &profile.hash[HASH(m->mtime)];
+ r->hash = *l;
+ *l = r;
+
+ if(!vm)
+ release();
+ return r;
+}
+
+#define LIMBO(m) ((m)->path[0] != '$')
+
+Module*
+limbomodule(void)
+{
+ Frame *f;
+ uchar *fp;
+ Module *m;
+
+ m = R.M->m;
+ if(LIMBO(m))
+ return m;
+ for(fp = R.FP ; fp != nil; fp = f->fp){
+ f = (Frame*)fp;
+ if(f->mr != nil){
+ m = f->mr->m;
+ if(LIMBO(m))
+ return m;
+ }
+ }
+ return nil;
+}
+
+static Record*
+mlook(Module *m, int limbo, int vm, int scale, int origin)
+{
+ Record *r;
+ void (*f)(int, ulong, ulong, ulong);
+
+ if(limbo)
+ m = limbomodule();
+ if(m == nil)
+ return nil;
+ for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){
+ if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){
+ r->base = m->prog;
+ return r;
+ }
+ }
+ if(pmods == nil || inpmods(m->name) || inpmods(m->path)){
+ f = memmonitor;
+ memmonitor = nil; /* prevent monitoring of our memory usage */
+ r = newmodule(m, vm, scale, origin);
+ memmonitor = f;
+ return r;
+ }
+ return nil;
+}
+
+static void
+sampler(void* a)
+{
+ int i;
+ Module *m;
+ Record *r;
+ Inst *p;
+
+ USED(a);
+ for(;;) {
+ osmillisleep(interval);
+ if(profiler != Psam)
+ break;
+ lock(&profile.l);
+ profile.time += interval;
+ if(R.M == H || (m = R.M->m) == nil){
+ unlock(&profile.l);
+ continue;
+ }
+ p = R.PC;
+ r = mlook(m, 0, 0, 1, 0);
+ if(r == nil){
+ unlock(&profile.l);
+ continue;
+ }
+ if(m->compiled && m->pctab != nil)
+ p = pc2dispc(p, m);
+ if((i = p-r->base) >= 0 && i < r->size)
+ r->bucket[i]++;
+ unlock(&profile.l);
+ }
+ samplefn = 0;
+ pexit("", 0);
+}
+
+/*
+ * coverage profiling
+ */
+
+static void
+cpxec(Prog *p)
+{
+ int op, i;
+ Module *m;
+ Record *r;
+ Prog *n;
+
+ R = p->R;
+ R.MP = R.M->MP;
+ R.IC = p->quanta;
+
+ if(p->kill != nil){
+ char *m;
+ m = p->kill;
+ p->kill = nil;
+ error(m);
+ }
+
+ if(R.M->compiled)
+ comvec();
+ else{
+ m = R.M->m;
+ r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil;
+ do{
+ dec[R.PC->add]();
+ op = R.PC->op;
+ if(r != nil){
+ i = R.PC-r->base;
+ if(i >= 0 && i < r->size)
+ r->bucket[i]++;
+ }
+ R.PC++;
+ optab[op]();
+ if(op == ISPAWN || op == IMSPAWN){
+ n = delruntail(Pdebug); /* any state will do */
+ n->xec = cpxec;
+ addrun(n);
+ }
+ if(m != R.M->m){
+ m = R.M->m;
+ r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil;
+ }
+ }while(--R.IC != 0);
+ }
+
+ p->R = R;
+}
+
+/* memory profiling */
+
+enum{
+ Mhalloc,
+ Mhfree,
+ Mgcfree,
+ Mmfree,
+ Mmalloc,
+ Mifree,
+ Mialloc,
+};
+
+static void
+memprof(int c, void *v, ulong n)
+{
+ int i, j, k;
+ ulong kk, *b;
+ Module *m;
+ Record *r;
+ Inst *p;
+ Heap *h;
+
+ USED(v);
+ USED(n);
+ if(profiler != Pmem){
+ memmonitor = nil;
+ heapmonitor = nil;
+ return;
+ }
+ lock(&profile.l);
+ m = nil;
+ if(c != Mgcfree && (R.M == H || (m = R.M->m) == nil)){
+ unlock(&profile.l);
+ return;
+ }
+ h = v;
+ if(c == Mhalloc || c == Mmalloc || c == Mialloc){
+ p = R.PC;
+ if(m->compiled && m->pctab != nil)
+ p = pc2dispc(p, m);
+ if((r = mlook(m, 1, 1, 2, 2)) == nil){
+ unlock(&profile.l);
+ return;
+ }
+ i = p-r->base;
+ k = (r->id<<24) | i;
+ if(c == Mhalloc){
+ h->hprof = k;
+ j = hmsize(h)-sizeof(Heap);
+ }
+ else if(c == Mmalloc){
+ setmalloctag(v, k);
+ j = msize(v);
+ }
+ else{
+ ((ulong*)v)[1] = k;
+ j = poolmsize(imagmem, v)-sizeof(ulong);
+ }
+ }
+ else{
+ if(c == Mmfree)
+ k = getmalloctag(v);
+ else if(c == Mifree)
+ k = ((ulong*)v)[1];
+ else
+ k = h->hprof;
+ if((r = getrec(k>>24)) == nil){
+ unlock(&profile.l);
+ return;
+ }
+ i = k&0xffffff;
+ if(c == Mmfree)
+ j = msize(v);
+ else if(c == Mifree)
+ j = poolmsize(imagmem, v)-sizeof(ulong);
+ else
+ j = hmsize(h)-sizeof(Heap);
+ j = -j;
+ }
+ i = 2*(i+1);
+ b = r->bucket;
+ if(i >= 0 && i < r->size){
+ if(0){
+ if(c == 1){
+ b[0] -= j;
+ b[i] -= j;
+ }
+ else if(c == 2){
+ b[1] -= j;
+ b[i+1] -= j;
+ }
+ }
+ else{
+ b[0] += j;
+ if((int)b[0] < 0)
+ b[0] = 0;
+ b[i] += j;
+ if((int)b[i] < 0)
+ b[i] = 0;
+ if(j > 0){
+ if((kk = b[0]) > b[1])
+ b[1] = kk;
+ if((kk = b[i]) > b[i+1])
+ b[i+1] = kk;
+ }
+ }
+ }
+ unlock(&profile.l);
+}
+
+/* main and image memory */
+static void
+memprofmi(int c, ulong pc, ulong v, ulong n)
+{
+ USED(pc);
+
+ if(c&2){
+ if(!(mprofiler&Mimage))
+ return;
+ }
+ else{
+ if(!(mprofiler&Mmain))
+ return;
+ }
+ switch(c){
+ case 0:
+ c = Mmalloc;
+ break;
+ case 2:
+ c = Mialloc;
+ break;
+ case 0 | 1<<8:
+ c = Mmfree;
+ break;
+ case 2 | 1<<8:
+ c = Mifree;
+ break;
+ default:
+ print("bad profile code %d\n", c);
+ }
+ memprof(c, (void*)v, n);
+}
+
+Dev profdevtab = {
+ 'P',
+ "prof",
+
+ devinit,
+ profattach,
+ profwalk,
+ profstat,
+ profopen,
+ devcreate,
+ profclose,
+ profread,
+ devbread,
+ profwrite,
+ devbwrite,
+ devremove,
+ profwstat
+};
--- /dev/null
+++ b/emu/port/devprog.c
@@ -1,0 +1,1505 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <interp.h>
+#include <isa.h>
+#include "runt.h"
+
+/*
+ * Enable the heap device for environments that allow debugging =>
+ * Must be 1 for a production environment.
+ */
+int SECURE = 0;
+
+enum
+{
+ Qdir,
+ Qctl,
+ Qdbgctl,
+ Qheap,
+ Qns,
+ Qnsgrp,
+ Qpgrp,
+ Qstack,
+ Qstatus,
+ Qtext,
+ Qwait,
+ Qfd,
+ Qexception,
+};
+
+/*
+ * must be in same order as enum
+ */
+Dirtab progdir[] =
+{
+ "ctl", {Qctl}, 0, 0200,
+ "dbgctl", {Qdbgctl}, 0, 0600,
+ "heap", {Qheap}, 0, 0600,
+ "ns", {Qns}, 0, 0400,
+ "nsgrp", {Qnsgrp}, 0, 0444,
+ "pgrp", {Qpgrp}, 0, 0444,
+ "stack", {Qstack}, 0, 0400,
+ "status", {Qstatus}, 0, 0444,
+ "text", {Qtext}, 0, 0000,
+ "wait", {Qwait}, 0, 0400,
+ "fd", {Qfd}, 0, 0400,
+ "exception", {Qexception}, 0, 0400,
+};
+
+enum
+{
+ CMkill,
+ CMkillgrp,
+ CMrestricted,
+ CMexceptions,
+ CMprivate
+};
+
+static
+Cmdtab progcmd[] = {
+ CMkill, "kill", 1,
+ CMkillgrp, "killgrp", 1,
+ CMrestricted, "restricted", 1,
+ CMexceptions, "exceptions", 2,
+ CMprivate, "private", 1,
+};
+
+enum
+{
+ CDstep,
+ CDtoret,
+ CDcont,
+ CDstart,
+ CDstop,
+ CDunstop,
+ CDmaim,
+ CDbpt
+};
+
+static
+Cmdtab progdbgcmd[] = {
+ CDstep, "step", 0, /* known below to be first, to cope with stepN */
+ CDtoret, "toret", 1,
+ CDcont, "cont", 1,
+ CDstart, "start", 1,
+ CDstop, "stop", 1,
+ CDunstop, "unstop", 1,
+ CDmaim, "maim", 1,
+ CDbpt, "bpt", 4,
+};
+
+typedef struct Heapqry Heapqry;
+struct Heapqry
+{
+ char fmt;
+ ulong addr;
+ ulong module;
+ int count;
+};
+
+typedef struct Bpt Bpt;
+
+struct Bpt
+{
+ Bpt *next;
+ int pc;
+ char *file;
+ char path[1];
+};
+
+typedef struct Progctl Progctl;
+struct Progctl
+{
+ Rendez r;
+ int ref;
+ Proc *debugger; /* waiting for dbgxec */
+ char *msg; /* reply from dbgxec */
+ int step; /* instructions to try */
+ int stop; /* stop running the program */
+ Bpt* bpts; /* active breakpoints */
+ Queue* q; /* status queue */
+};
+
+#define QSHIFT 4 /* location in qid of pid */
+#define QID(q) (((ulong)(q).path&0x0000000F)>>0)
+#define QPID(pid) (((pid)<<QSHIFT))
+#define PID(q) ((q).vers)
+#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1))
+
+static char *progstate[] = /* must correspond to include/interp.h */
+{
+ "alt", /* blocked in alt instruction */
+ "send", /* waiting to send */
+ "recv", /* waiting to recv */
+ "debug", /* debugged */
+ "ready", /* ready to be scheduled */
+ "release", /* interpreter released */
+ "exiting", /* exit because of kill or error */
+ "broken", /* thread crashed */
+};
+
+static void dbgstep(Progctl*, Prog*, int);
+static void dbgstart(Prog*);
+static void freebpts(Bpt*);
+static Bpt* delbpt(Bpt*, char*, int);
+static Bpt* setbpt(Bpt*, char*, int);
+static void mntscan(Mntwalk*, Pgrp*);
+extern Type *Trdchan;
+extern Type *Twrchan;
+extern Module* modules;
+static char Emisalign[] = "misaligned address";
+
+static int
+proggen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+ Qid qid;
+ Prog *p;
+ char *e;
+ Osenv *o;
+ ulong pid, path, perm, len;
+
+ USED(ntab);
+
+ if(s == DEVDOTDOT){
+ mkqid(&qid, Qdir, 0, QTDIR);
+ devdir(c, qid, "#p", 0, eve, DMDIR|0555, dp);
+ return 1;
+ }
+
+ if((ulong)c->qid.path == Qdir) {
+ if(name != nil){
+ /* ignore s and use name to find pid */
+ pid = strtoul(name, &e, 0);
+ if(pid == 0 || *e != '\0')
+ return -1;
+ acquire();
+ p = progpid(pid);
+ if(p == nil){
+ release();
+ return -1;
+ }
+ }else{
+ acquire();
+ p = progn(s);
+ if(p == nil) {
+ release();
+ return -1;
+ }
+ pid = p->pid;
+ }
+ o = p->osenv;
+ sprint(up->genbuf, "%lud", pid);
+ if(name != nil && strcmp(name, up->genbuf) != 0){
+ release();
+ return -1;
+ }
+ mkqid(&qid, pid<<QSHIFT, pid, QTDIR);
+ devdir(c, qid, up->genbuf, 0, o->user, DMDIR|0555, dp);
+ release();
+ return 1;
+ }
+
+ if(s >= nelem(progdir))
+ return -1;
+ tab = &progdir[s];
+ path = PATH(c->qid);
+
+ acquire();
+ p = progpid(PID(c->qid));
+ if(p == nil) {
+ release();
+ return -1;
+ }
+
+ o = p->osenv;
+
+ perm = tab->perm;
+ if((perm & 7) == 0)
+ perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode;
+
+ len = tab->length;
+ mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+ devdir(c, qid, tab->name, len, o->user, perm, dp);
+ release();
+ return 1;
+}
+
+static Chan*
+progattach(char *spec)
+{
+ return devattach('p', spec);
+}
+
+static Walkqid*
+progwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, proggen);
+}
+
+static int
+progstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, proggen);
+}
+
+static Chan*
+progopen(Chan *c, int omode)
+{
+ Prog *p;
+ Osenv *o;
+ Progctl *ctl;
+ int perm;
+
+ if(c->qid.type & QTDIR)
+ return devopen(c, omode, 0, 0, proggen);
+
+ acquire();
+ if (waserror()) {
+ release();
+ nexterror();
+ }
+ p = progpid(PID(c->qid));
+ if(p == nil)
+ error(Ethread);
+ o = p->osenv;
+ perm = progdir[QID(c->qid)-1].perm;
+ if((perm & 7) == 0)
+ perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode;
+ devpermcheck(o->user, perm, omode);
+ omode = openmode(omode);
+
+ switch(QID(c->qid)){
+ default:
+ error(Egreg);
+ case Qnsgrp:
+ case Qpgrp:
+ case Qtext:
+ case Qstatus:
+ case Qstack:
+ case Qctl:
+ case Qfd:
+ case Qexception:
+ break;
+ case Qwait:
+ c->aux = qopen(1024, Qmsg, nil, nil);
+ if(c->aux == nil)
+ error(Enomem);
+ o->childq = c->aux;
+ break;
+ case Qns:
+ c->aux = malloc(sizeof(Mntwalk));
+ if(c->aux == nil)
+ error(Enomem);
+ break;
+ case Qheap:
+ if(SECURE || p->group->flags&Pprivatemem || omode != ORDWR)
+ error(Eperm);
+ c->aux = malloc(sizeof(Heapqry));
+ if(c->aux == nil)
+ error(Enomem);
+ break;
+ case Qdbgctl:
+ if(SECURE || p->group->flags&Pprivatemem || omode != ORDWR)
+ error(Eperm);
+ ctl = malloc(sizeof(Progctl));
+ if(ctl == nil)
+ error(Enomem);
+ ctl->q = qopen(1024, Qmsg, nil, nil);
+ if(ctl->q == nil) {
+ free(ctl);
+ error(Enomem);
+ }
+ ctl->bpts = nil;
+ ctl->ref = 1;
+ c->aux = ctl;
+ break;
+ }
+ if(p->state != Pexiting)
+ c->qid.vers = p->pid;
+
+ poperror();
+ release();
+ c->offset = 0;
+ c->mode = omode;
+ c->flag |= COPEN;
+ return c;
+}
+
+static int
+progwstat(Chan *c, uchar *db, int n)
+{
+ Dir d;
+ Prog *p;
+ char *u;
+ Osenv *o;
+
+ if(c->qid.type&QTDIR)
+ error(Eperm);
+ acquire();
+ p = progpid(PID(c->qid));
+ if(p == nil) {
+ release();
+ error(Ethread);
+ }
+
+ u = up->env->user;
+ o = p->osenv;
+ if(strcmp(u, o->user) != 0 && strcmp(u, eve) != 0) {
+ release();
+ error(Eperm);
+ }
+
+ n = convM2D(db, n, &d, nil);
+ if(n == 0){
+ release();
+ error(Eshortstat);
+ }
+ if(d.mode != ~0UL)
+ o->pgrp->progmode = d.mode&0777;
+ release();
+ return n;
+}
+
+static void
+closedbgctl(Progctl *ctl, Prog *p)
+{
+ Osenv *o;
+
+ if(ctl->ref-- > 1)
+ return;
+ freebpts(ctl->bpts);
+ if(p != nil){
+ o = p->osenv;
+ if(o->debug == ctl){
+ o->debug = nil;
+ p->xec = xec;
+ }
+ }
+ qfree(ctl->q);
+ free(ctl);
+}
+
+static void
+progclose(Chan *c)
+{
+ int i;
+ Prog *f;
+ Osenv *o;
+ Progctl *ctl;
+
+ switch(QID(c->qid)) {
+ case Qns:
+ case Qheap:
+ free(c->aux);
+ break;
+ case Qdbgctl:
+ if((c->flag & COPEN) == 0)
+ return;
+ ctl = c->aux;
+ acquire();
+ closedbgctl(ctl, progpid(PID(c->qid)));
+ release();
+ break;
+ case Qwait:
+ acquire();
+ i = 0;
+ for(;;) {
+ f = progn(i++);
+ if(f == nil)
+ break;
+ o = f->osenv;
+ if(o->waitq == c->aux)
+ o->waitq = nil;
+ if(o->childq == c->aux)
+ o->childq = nil;
+ }
+ release();
+ qfree(c->aux);
+ }
+}
+
+static int
+progsize(Prog *p)
+{
+ int size;
+ Frame *f;
+ uchar *fp;
+ Modlink *m;
+
+ m = p->R.M;
+ size = 0;
+ if(m->MP != H)
+ size += hmsize(D2H(m->MP));
+ if(m->prog != nil)
+ size += msize(m->prog);
+
+ fp = p->R.FP;
+ while(fp != nil) {
+ f = (Frame*)fp;
+ fp = f->fp;
+ if(f->mr != nil) {
+ if(f->mr->MP != H)
+ size += hmsize(D2H(f->mr->MP));
+ if(f->mr->prog != nil)
+ size += msize(f->mr->prog);
+ }
+ if(f->t == nil)
+ size += msize(SEXTYPE(f));
+ }
+ return size/1024;
+}
+
+static long
+progoffset(long offset, char *va, int *np)
+{
+ if(offset > 0) {
+ offset -= *np;
+ if(offset < 0) {
+ memmove(va, va+*np+offset, -offset);
+ *np = -offset;
+ }
+ else
+ *np = 0;
+ }
+ return offset;
+}
+
+static int
+progqidwidth(Chan *c)
+{
+ char buf[32];
+
+ return sprint(buf, "%lud", c->qid.vers);
+}
+
+int
+progfdprint(Chan *c, int fd, int w, char *s, int ns)
+{
+ int n;
+
+ if(w == 0)
+ w = progqidwidth(c);
+ n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
+ fd,
+ &"r w rw"[(c->mode&3)<<1],
+ devtab[c->type]->dc, c->dev,
+ c->qid.path, w, c->qid.vers, c->qid.type,
+ c->iounit, c->offset, c->name->s);
+ return n;
+}
+
+static int
+progfds(Osenv *o, char *va, int count, long offset)
+{
+ Fgrp *f;
+ Chan *c;
+ int n, i, w, ww;
+
+ f = o->fgrp; /* f is not locked because we've acquired */
+ n = readstr(0, va, count, o->pgrp->dot->name->s);
+ n += snprint(va+n, count-n, "\n");
+ offset = progoffset(offset, va, &n);
+ /* compute width of qid.path */
+ w = 0;
+ for(i = 0; i <= f->maxfd; i++) {
+ c = f->fd[i];
+ if(c == nil)
+ continue;
+ ww = progqidwidth(c);
+ if(ww > w)
+ w = ww;
+ }
+ for(i = 0; i <= f->maxfd; i++) {
+ c = f->fd[i];
+ if(c == nil)
+ continue;
+ n += progfdprint(c, i, w, va+n, count-n);
+ offset = progoffset(offset, va, &n);
+ }
+ return n;
+}
+
+Inst *
+pc2dispc(Inst *pc, Module *mod)
+{
+ ulong l, u, m, v;
+ ulong *tab = mod->pctab;
+
+ v = (ulong)pc - (ulong)mod->prog;
+ l = 0;
+ u = mod->nprog-1;
+ while(l < u){
+ m = (l+u+1)/2;
+ if(tab[m] < v)
+ l = m;
+ else if(tab[m] > v)
+ u = m-1;
+ else
+ l = u = m;
+ }
+ if(l == u && tab[u] <= v && u != mod->nprog-1 && tab[u+1] > v)
+ return &mod->prog[u];
+ return 0;
+}
+
+static int
+progstack(REG *reg, int state, char *va, int count, long offset)
+{
+ int n;
+ Frame *f;
+ Inst *pc;
+ uchar *fp;
+ Modlink *m;
+
+ n = 0;
+ m = reg->M;
+ fp = reg->FP;
+ pc = reg->PC;
+
+ /*
+ * all states other than debug and ready block,
+ * but interp has already advanced the PC
+ */
+ if(!m->compiled && state != Pready && state != Pdebug && pc > m->prog)
+ pc--;
+ if(m->compiled && m->m->pctab != nil)
+ pc = pc2dispc(pc, m->m);
+
+ while(fp != nil) {
+ f = (Frame*)fp;
+ n += snprint(va+n, count-n, "%.8lux %.8lux %.8lux %.8lux %d %s\n",
+ (ulong)f, /* FP */
+ (ulong)(pc - m->prog), /* PC in dis instructions */
+ (ulong)m->MP, /* MP */
+ (ulong)m->prog, /* Code for module */
+ m->compiled && m->m->pctab == nil, /* True if native assembler: fool stack utility for now */
+ m->m->path); /* File system path */
+
+ if(offset > 0) {
+ offset -= n;
+ if(offset < 0) {
+ memmove(va, va+n+offset, -offset);
+ n = -offset;
+ }
+ else
+ n = 0;
+ }
+
+ pc = f->lr;
+ fp = f->fp;
+ if(f->mr != nil)
+ m = f->mr;
+ if(!m->compiled)
+ pc--;
+ else if(m->m->pctab != nil)
+ pc = pc2dispc(pc, m->m)-1;
+ }
+ return n;
+}
+
+static int
+calldepth(REG *reg)
+{
+ int n;
+ uchar *fp;
+
+ n = 0;
+ for(fp = reg->FP; fp != nil; fp = ((Frame*)fp)->fp)
+ n++;
+ return n;
+}
+
+static int
+progheap(Heapqry *hq, char *va, int count, ulong offset)
+{
+ WORD *w;
+ void *p;
+ List *hd;
+ Array *a;
+ char *fmt, *str;
+ Module *m;
+ Modlink *ml;
+ Channel *c;
+ ulong addr;
+ String *ss;
+ union { REAL r; LONG l; WORD w[2]; } rock;
+ int i, s, n, len, signed_off;
+ Type *t;
+
+ n = 0;
+ s = 0;
+ signed_off = offset;
+ addr = hq->addr;
+ for(i = 0; i < hq->count; i++) {
+ switch(hq->fmt) {
+ case 'W':
+ if(addr & 3)
+ return -1;
+ n += snprint(va+n, count-n, "%d\n", *(WORD*)addr);
+ s = sizeof(WORD);
+ break;
+ case 'B':
+ n += snprint(va+n, count-n, "%d\n", *(BYTE*)addr);
+ s = sizeof(BYTE);
+ break;
+ case 'V':
+ if(addr & 3)
+ return -1;
+ w = (WORD*)addr;
+ rock.w[0] = w[0];
+ rock.w[1] = w[1];
+ n += snprint(va+n, count-n, "%lld\n", rock.l);
+ s = sizeof(LONG);
+ break;
+ case 'R':
+ if(addr & 3)
+ return -1;
+ w = (WORD*)addr;
+ rock.w[0] = w[0];
+ rock.w[1] = w[1];
+ n += snprint(va+n, count-n, "%g\n", rock.r);
+ s = sizeof(REAL);
+ break;
+ case 'I':
+ if(addr & 3)
+ return -1;
+ for(m = modules; m != nil; m = m->link)
+ if(m == (Module*)hq->module)
+ break;
+ if(m == nil)
+ error(Ebadctl);
+ addr = (ulong)(m->prog+addr);
+ n += snprint(va+n, count-n, "%D\n", (Inst*)addr);
+ s = sizeof(Inst);
+ break;
+ case 'P':
+ if(addr & 3)
+ return -1;
+ p = *(void**)addr;
+ fmt = "nil\n";
+ if(p != H)
+ fmt = "%lux\n";
+ n += snprint(va+n, count-n, fmt, p);
+ s = sizeof(WORD);
+ break;
+ case 'L':
+ if(addr & 3)
+ return -1;
+ hd = *(List**)addr;
+ if(hd == H || D2H(hd)->t != &Tlist)
+ return -1;
+ n += snprint(va+n, count-n, "%lux.%lux\n", (ulong)&hd->tail, (ulong)hd->data);
+ s = sizeof(WORD);
+ break;
+ case 'A':
+ if(addr & 3)
+ return -1;
+ a = *(Array**)addr;
+ if(a == H)
+ n += snprint(va+n, count-n, "nil\n");
+ else {
+ if(D2H(a)->t != &Tarray)
+ return -1;
+ n += snprint(va+n, count-n, "%d.%lux\n", a->len, (ulong)a->data);
+ }
+ s = sizeof(WORD);
+ break;
+ case 'C':
+ if(addr & 3)
+ return -1;
+ ss = *(String**)addr;
+ if(ss == H)
+ ss = &snil;
+ else
+ if(D2H(ss)->t != &Tstring)
+ return -1;
+ n += snprint(va+n, count-n, "%d.", abs(ss->len));
+ str = string2c(ss);
+ len = strlen(str);
+ if(count-n < len)
+ len = count-n;
+ if(len > 0) {
+ memmove(va+n, str, len);
+ n += len;
+ }
+ break;
+ case 'M':
+ if(addr & 3)
+ return -1;
+ ml = *(Modlink**)addr;
+ fmt = ml == H ? "nil\n" : "%lux\n";
+ n += snprint(va+n, count-n, fmt, ml->MP);
+ s = sizeof(WORD);
+ break;
+ case 'c':
+ if(addr & 3)
+ return -1;
+ c = *(Channel**)addr;
+ if(c == H)
+ n += snprint(va+n, count-n, "nil\n");
+ else{
+ t = D2H(c)->t;
+ if(t != &Tchannel && t != Trdchan && t != Twrchan)
+ return -1;
+ if(c->buf == H)
+ n += snprint(va+n, count-n, "0.%lux\n", (ulong)c);
+ else
+ n += snprint(va+n, count-n, "%d.%lux.%d.%d\n", c->buf->len, (ulong)c->buf->data, c->front, c->size);
+ }
+ break;
+
+ }
+ addr += s;
+ if(signed_off > 0) {
+ signed_off -= n;
+ if(signed_off < 0) {
+ memmove(va, va+n+signed_off, -signed_off);
+ n = -signed_off;
+ }
+ else
+ n = 0;
+ }
+ }
+ return n;
+}
+
+WORD
+modstatus(REG *r, char *ptr, int len)
+{
+ Inst *PC;
+ Frame *f;
+
+ if(r->M->m->name[0] == '$') {
+ f = (Frame*)r->FP;
+ snprint(ptr, len, "%s[%s]", f->mr->m->name, r->M->m->name);
+ if(f->mr->compiled)
+ return (WORD)f->lr;
+ return f->lr - f->mr->prog;
+ }
+ memmove(ptr, r->M->m->name, len);
+ if(r->M->compiled)
+ return (WORD)r->PC;
+ PC = r->PC;
+ /* should really check for blocked states */
+ if(PC > r->M->prog)
+ PC--;
+ return PC - r->M->prog;
+}
+
+static void
+int2flag(int flag, char *s)
+{
+ if(flag == 0){
+ *s = '\0';
+ return;
+ }
+ *s++ = '-';
+ if(flag & MAFTER)
+ *s++ = 'a';
+ if(flag & MBEFORE)
+ *s++ = 'b';
+ if(flag & MCREATE)
+ *s++ = 'c';
+ if(flag & MCACHE)
+ *s++ = 'C';
+ *s = '\0';
+}
+
+static char*
+progtime(ulong msec, char *buf, char *ebuf)
+{
+ int tenths, sec;
+
+ tenths = msec/100;
+ sec = tenths/10;
+ seprint(buf, ebuf, "%4d:%2.2d.%d", sec/60, sec%60, tenths%10);
+ return buf;
+}
+
+static long
+progread(Chan *c, void *va, long n, vlong offset)
+{
+ int i;
+ Prog *p;
+ Osenv *o;
+ Mntwalk *mw;
+ ulong grpid;
+ char *a = va;
+ Progctl *ctl;
+ char mbuf[64], timebuf[12];
+ char flag[10];
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, proggen);
+
+ switch(QID(c->qid)){
+ case Qdbgctl:
+ ctl = c->aux;
+ return qread(ctl->q, va, n);
+ case Qstatus:
+ acquire();
+ p = progpid(PID(c->qid));
+ if(p == nil || p->state == Pexiting || p->R.M == H) {
+ release();
+ snprint(up->genbuf, sizeof(up->genbuf), "%8lud %8d %10s %s %10s %5dK %s",
+ PID(c->qid),
+ 0,
+ eve,
+ progtime(0, timebuf, timebuf+sizeof(timebuf)),
+ progstate[Pexiting],
+ 0,
+ "[$Sys]");
+ return readstr(offset, va, n, up->genbuf);
+ }
+ modstatus(&p->R, mbuf, sizeof(mbuf));
+ o = p->osenv;
+ snprint(up->genbuf, sizeof(up->genbuf), "%8d %8d %10s %s %10s %5dK %s",
+ p->pid,
+ p->group!=nil? p->group->id: 0,
+ o->user,
+ progtime(p->ticks, timebuf, timebuf+sizeof(timebuf)),
+ progstate[p->state],
+ progsize(p),
+ mbuf);
+ release();
+ return readstr(offset, va, n, up->genbuf);
+ case Qwait:
+ return qread(c->aux, va, n);
+ case Qns:
+ acquire();
+ if(waserror()){
+ release();
+ nexterror();
+ }
+ p = progpid(PID(c->qid));
+ if(p == nil)
+ error(Ethread);
+ mw = c->aux;
+ if(mw->cddone){
+ poperror();
+ release();
+ return 0;
+ }
+ o = p->osenv;
+ mntscan(mw, o->pgrp);
+ if(mw->mh == 0) {
+ mw->cddone = 1;
+ i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s);
+ poperror();
+ release();
+ return i;
+ }
+ int2flag(mw->cm->mflag, flag);
+ if(strcmp(mw->cm->to->name->s, "#M") == 0){
+ i = snprint(a, n, "mount %s %s %s %s\n", flag,
+ mw->cm->to->mchan->name->s,
+ mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : "");
+ }else
+ i = snprint(a, n, "bind %s %s %s\n", flag,
+ mw->cm->to->name->s, mw->mh->from->name->s);
+ poperror();
+ release();
+ return i;
+ case Qnsgrp:
+ acquire();
+ p = progpid(PID(c->qid));
+ if(p == nil) {
+ release();
+ error(Ethread);
+ }
+ grpid = ((Osenv *)p->osenv)->pgrp->pgrpid;
+ release();
+ return readnum(offset, va, n, grpid, NUMSIZE);
+ case Qpgrp:
+ acquire();
+ p = progpid(PID(c->qid));
+ if(p == nil) {
+ release();
+ error(Ethread);
+ }
+ grpid = p->group!=nil? p->group->id: 0;
+ release();
+ return readnum(offset, va, n, grpid, NUMSIZE);
+ case Qstack:
+ acquire();
+ p = progpid(PID(c->qid));
+ if(p == nil || p->state == Pexiting) {
+ release();
+ error(Ethread);
+ }
+ if(p->state == Pready) {
+ release();
+ error(Estopped);
+ }
+ n = progstack(&p->R, p->state, va, n, offset);
+ release();
+ return n;
+ case Qheap:
+ acquire();
+ if(waserror()){
+ release();
+ nexterror();
+ }
+ n = progheap(c->aux, va, n, offset);
+ if(n == -1)
+ error(Emisalign);
+ poperror();
+ release();
+ return n;
+ case Qfd:
+ acquire();
+ if(waserror()) {
+ release();
+ nexterror();
+ }
+ p = progpid(PID(c->qid));
+ if(p == nil)
+ error(Ethread);
+ o = p->osenv;
+ n = progfds(o, va, n, offset);
+ poperror();
+ release();
+ return n;
+ case Qexception:
+ acquire();
+ p = progpid(PID(c->qid));
+ if(p == nil) {
+ release();
+ error(Ethread);
+ }
+ if(p->exstr == nil)
+ up->genbuf[0] = 0;
+ else
+ snprint(up->genbuf, sizeof(up->genbuf), p->exstr);
+ release();
+ return readstr(offset, va, n, up->genbuf);
+ }
+ error(Egreg);
+ return 0;
+}
+
+static void
+mntscan(Mntwalk *mw, Pgrp *pg)
+{
+ Mount *t;
+ Mhead *f;
+ int nxt, i;
+ ulong last, bestmid;
+
+ rlock(&pg->ns);
+
+ nxt = 0;
+ bestmid = ~0;
+
+ last = 0;
+ if(mw->mh)
+ last = mw->cm->mountid;
+
+ for(i = 0; i < MNTHASH; i++) {
+ for(f = pg->mnthash[i]; f; f = f->hash) {
+ for(t = f->mount; t; t = t->next) {
+ if(mw->mh == 0 ||
+ (t->mountid > last && t->mountid < bestmid)) {
+ mw->cm = t;
+ mw->mh = f;
+ bestmid = mw->cm->mountid;
+ nxt = 1;
+ }
+ }
+ }
+ }
+ if(nxt == 0)
+ mw->mh = 0;
+
+ runlock(&pg->ns);
+}
+
+static long
+progwrite(Chan *c, void *va, long n, vlong offset)
+{
+ Prog *p, *f;
+ Heapqry *hq;
+ char buf[512];
+ Progctl *ctl;
+ char *b;
+ int i, pc;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+
+ USED(offset);
+ USED(va);
+
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+
+ acquire();
+ if(waserror()) {
+ release();
+ nexterror();
+ }
+ p = progpid(PID(c->qid));
+ if(p == nil)
+ error(Ethread);
+
+ switch(QID(c->qid)){
+ case Qctl:
+ cb = parsecmd(va, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, progcmd, nelem(progcmd));
+ switch(ct->index){
+ case CMkillgrp:
+ killgrp(p, "killed");
+ break;
+ case CMkill:
+ killprog(p, "killed");
+ break;
+ case CMrestricted:
+ p->flags |= Prestrict;
+ break;
+ case CMexceptions:
+ if(p->group->id != p->pid)
+ error(Eperm);
+ if(strcmp(cb->f[1], "propagate") == 0)
+ p->flags |= Ppropagate;
+ else if(strcmp(cb->f[1], "notifyleader") == 0)
+ p->flags |= Pnotifyleader;
+ else
+ error(Ebadctl);
+ break;
+ case CMprivate:
+ p->group->flags |= Pprivatemem;
+ break;
+ }
+ poperror();
+ free(cb);
+ break;
+ case Qdbgctl:
+ cb = parsecmd(va, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0)
+ ct = progdbgcmd;
+ else
+ ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd));
+ switch(ct->index){
+ case CDstep:
+ if(cb->nf == 1)
+ i = strtoul(cb->f[0]+4, nil, 0);
+ else
+ i = strtoul(cb->f[1], nil, 0);
+ dbgstep(c->aux, p, i);
+ break;
+ case CDtoret:
+ f = currun();
+ i = calldepth(&p->R);
+ while(f->kill == nil) {
+ dbgstep(c->aux, p, 1024);
+ if(i > calldepth(&p->R))
+ break;
+ }
+ break;
+ case CDcont:
+ f = currun();
+ while(f->kill == nil)
+ dbgstep(c->aux, p, 1024);
+ break;
+ case CDstart:
+ dbgstart(p);
+ break;
+ case CDstop:
+ ctl = c->aux;
+ ctl->stop = 1;
+ break;
+ case CDunstop:
+ ctl = c->aux;
+ ctl->stop = 0;
+ break;
+ case CDbpt:
+ pc = strtoul(cb->f[3], nil, 10);
+ ctl = c->aux;
+ if(strcmp(cb->f[1], "set") == 0)
+ ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc);
+ else if(strcmp(cb->f[1], "del") == 0)
+ ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc);
+ else
+ error(Ebadctl);
+ break;
+ case CDmaim:
+ p->kill = "maim";
+ break;
+ }
+ poperror();
+ free(cb);
+ break;
+ case Qheap:
+ /*
+ * Heap query:
+ * addr.Fn
+ * pc+module.In
+ */
+ i = n;
+ if(i > sizeof(buf)-1)
+ i = sizeof(buf)-1;
+ memmove(buf, va, i);
+ buf[i] = '\0';
+ hq = c->aux;
+ hq->addr = strtoul(buf, &b, 0);
+ if(*b == '+')
+ hq->module = strtoul(b, &b, 0);
+ if(*b++ != '.')
+ error(Ebadctl);
+ hq->fmt = *b++;
+ hq->count = strtoul(b, nil, 0);
+ break;
+ default:
+ print("unknown qid in procwrite\n");
+ error(Egreg);
+ }
+ poperror();
+ release();
+ return n;
+}
+
+static Bpt*
+setbpt(Bpt *bpts, char *path, int pc)
+{
+ int n;
+ Bpt *b;
+
+ n = strlen(path);
+ b = mallocz(sizeof *b + n, 0);
+ if(b == nil)
+ return bpts;
+ b->pc = pc;
+ memmove(b->path, path, n+1);
+ b->file = b->path;
+ path = strrchr(b->path, '/');
+ if(path != nil)
+ b->file = path + 1;
+ b->next = bpts;
+ return b;
+}
+
+static Bpt*
+delbpt(Bpt *bpts, char *path, int pc)
+{
+ Bpt *b, **last;
+
+ last = &bpts;
+ for(b = bpts; b != nil; b = b->next){
+ if(b->pc == pc && strcmp(b->path, path) == 0) {
+ *last = b->next;
+ free(b);
+ break;
+ }
+ last = &b->next;
+ }
+ return bpts;
+}
+
+static void
+freebpts(Bpt *b)
+{
+ Bpt *next;
+
+ for(; b != nil; b = next){
+ next = b->next;
+ free(b);
+ }
+}
+
+static void
+telldbg(Progctl *ctl, char *msg)
+{
+ kstrcpy(ctl->msg, msg, ERRMAX);
+ ctl->debugger = nil;
+ Wakeup(&ctl->r);
+}
+
+static void
+dbgstart(Prog *p)
+{
+ Osenv *o;
+ Progctl *ctl;
+
+ o = p->osenv;
+ ctl = o->debug;
+ if(ctl != nil && ctl->debugger != nil)
+ error("prog debugged");
+ if(p->state == Pdebug)
+ addrun(p);
+ o->debug = nil;
+ p->xec = xec;
+}
+
+static int
+xecdone(void *vc)
+{
+ Progctl *ctl = vc;
+
+ return ctl->debugger == nil;
+}
+
+static void
+dbgstep(Progctl *vctl, Prog *p, int n)
+{
+ Osenv * volatile o;
+ Progctl * volatile ctl;
+ char buf[ERRMAX+20], *msg;
+
+ if(p == currun())
+ error("cannot step yourself");
+
+ if(p->state == Pbroken)
+ error("prog broken");
+
+ ctl = vctl;
+ if(ctl->debugger != nil)
+ error("prog already debugged");
+
+ o = p->osenv;
+ if(o->debug == nil) {
+ o->debug = ctl;
+ p->xec = dbgxec;
+ }else if(o->debug != ctl)
+ error("prog already debugged");
+
+ ctl->step = n;
+ if(p->state == Pdebug)
+ addrun(p);
+ ctl->debugger = up;
+ strcpy(buf, "child: ");
+ msg = buf+7;
+ ctl->msg = msg;
+
+ /*
+ * wait for reply from dbgxec; release is okay because prog is now
+ * debugged, cannot exit.
+ */
+ if(waserror()){
+ acquire();
+ ctl->debugger = nil;
+ ctl->msg = nil;
+ o->debug = nil;
+ p->xec = xec;
+ nexterror();
+ }
+ release();
+ Sleep(&ctl->r, xecdone, ctl);
+ poperror();
+ acquire();
+ if(msg[0] != '\0')
+ error(buf);
+}
+
+void
+dbgexit(Prog *kid, int broken, char *estr)
+{
+ int n;
+ Osenv *e;
+ Progctl *ctl;
+ char buf[ERRMAX+20];
+
+ e = kid->osenv;
+ ctl = e->debug;
+ e->debug = nil;
+ kid->xec = xec;
+
+ if(broken)
+ n = snprint(buf, sizeof(buf), "broken: %s", estr);
+ else
+ n = snprint(buf, sizeof(buf), "exited");
+ if(ctl->debugger)
+ telldbg(ctl, buf);
+ qproduce(ctl->q, buf, n);
+}
+
+static void
+dbgaddrun(Prog *p)
+{
+ Osenv *o;
+
+ p->state = Pdebug;
+ p->addrun = nil;
+ o = p->osenv;
+ if(o->rend != nil)
+ Wakeup(o->rend);
+ o->rend = nil;
+}
+
+static int
+bdone(void *vp)
+{
+ Prog *p = vp;
+
+ return p->addrun == nil;
+}
+
+static void
+dbgblock(Prog *p)
+{
+ Osenv *o;
+ Progctl *ctl;
+
+ o = p->osenv;
+ ctl = o->debug;
+ qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state]));
+ pushrun(p);
+ p->addrun = dbgaddrun;
+ o->rend = &up->sleep;
+
+ /*
+ * bdone(p) is safe after release because p is being debugged,
+ * so cannot exit.
+ */
+ if(waserror()){
+ acquire();
+ nexterror();
+ }
+ release();
+ if(o->rend != nil)
+ Sleep(o->rend, bdone, p);
+ poperror();
+ acquire();
+ if(p->kill != nil)
+ error(p->kill);
+ ctl = o->debug;
+ if(ctl != nil)
+ qproduce(ctl->q, "run", 3);
+}
+
+void
+dbgxec(Prog *p)
+{
+ Bpt *b;
+ Prog *kid;
+ Osenv *env;
+ Progctl *ctl;
+ int op, pc, n;
+ char buf[ERRMAX+10];
+ extern void (*dec[])(void);
+
+ env = p->osenv;
+ ctl = env->debug;
+ ctl->ref++;
+ if(waserror()){
+ closedbgctl(ctl, p);
+ nexterror();
+ }
+
+ R = p->R;
+ R.MP = R.M->MP;
+
+ R.IC = p->quanta;
+ if((ulong)R.IC > ctl->step)
+ R.IC = ctl->step;
+ if(ctl->stop)
+ R.IC = 0;
+
+
+ buf[0] = '\0';
+
+ if(R.IC != 0 && R.M->compiled) {
+ comvec();
+ if(p != currun())
+ dbgblock(p);
+ goto save;
+ }
+
+ while(R.IC != 0) {
+ if(0)
+ print("step: %lux: %s %4ld %D\n",
+ (ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC);
+
+ dec[R.PC->add]();
+ op = R.PC->op;
+ R.PC++;
+ optab[op]();
+
+ /*
+ * check notification about new progs
+ */
+ if(op == ISPAWN || op == IMSPAWN) {
+ /* pick up the kid from the end of the run queue */
+ kid = delruntail(Pdebug);
+ n = snprint(buf, sizeof buf, "new %d", kid->pid);
+ qproduce(ctl->q, buf, n);
+ buf[0] = '\0';
+ }
+ if(op == ILOAD) {
+ n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s));
+ qproduce(ctl->q, buf, n);
+ buf[0] = '\0';
+ }
+
+ /*
+ * check for returns at big steps
+ */
+ if(op == IRET)
+ R.IC = 1;
+
+ /*
+ * check for blocked progs
+ */
+ if(R.IC == 1 && p != currun())
+ dbgblock(p);
+ if(ctl->stop)
+ R.IC = 1;
+ R.IC--;
+
+ if(ctl->bpts == nil)
+ continue;
+
+ pc = R.PC - R.M->prog;
+ for(b = ctl->bpts; b != nil; b = b->next) {
+ if(pc == b->pc &&
+ (strcmp(R.M->m->path, b->path) == 0 ||
+ strcmp(R.M->m->path, b->file) == 0)) {
+ strcpy(buf, "breakpoint");
+ goto save;
+ }
+ }
+ }
+save:
+ if(ctl->stop)
+ strcpy(buf, "stopped");
+
+ p->R = R;
+
+ if(env->debug == nil) {
+ poperror();
+ return;
+ }
+
+ if(p == currun())
+ delrun(Pdebug);
+
+ telldbg(env->debug, buf);
+ poperror();
+ closedbgctl(env->debug, p);
+}
+
+Dev progdevtab = {
+ 'p',
+ "prog",
+
+ devinit,
+ progattach,
+ progwalk,
+ progstat,
+ progopen,
+ devcreate,
+ progclose,
+ progread,
+ devbread,
+ progwrite,
+ devbwrite,
+ devremove,
+ progwstat
+};
--- /dev/null
+++ b/emu/port/devroot.c
@@ -1,0 +1,150 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+extern Rootdata rootdata[];
+extern Dirtab roottab[];
+extern int rootmaxq;
+
+static Chan*
+rootattach(char *spec)
+{
+ int i;
+ ulong len;
+ Rootdata *r;
+
+ if(*spec)
+ error(Ebadspec);
+ for (i = 0; i < rootmaxq; i++){
+ r = &rootdata[i];
+ if (r->sizep){
+ len = *r->sizep;
+ r->size = len;
+ roottab[i].length = len;
+ }
+ }
+ return devattach('/', spec);
+}
+
+static int
+rootgen(Chan *c, char *name, Dirtab *tab, int nd, int s, Dir *dp)
+{
+ int p, i;
+ Rootdata *r;
+
+ if(s == DEVDOTDOT){
+ p = rootdata[c->qid.path].dotdot;
+ c->qid.path = p;
+ c->qid.type = QTDIR;
+ name = "#/";
+ if(p != 0){
+ for(i = 0; i < rootmaxq; i++)
+ if(roottab[i].qid.path == c->qid.path){
+ name = roottab[i].name;
+ break;
+ }
+ }
+ devdir(c, c->qid, name, 0, eve, 0555, dp);
+ return 1;
+ }
+ if(name != nil){
+ isdir(c);
+ r = &rootdata[(int)c->qid.path];
+ tab = r->ptr;
+ for(i=0; i<r->size; i++, tab++)
+ if(strcmp(tab->name, name) == 0){
+ devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+ return 1;
+ }
+ return -1;
+ }
+ if(s >= nd)
+ return -1;
+ tab += s;
+ devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+ return 1;
+}
+
+static Walkqid*
+rootwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ ulong p;
+
+ p = c->qid.path;
+ if(nname == 0)
+ p = rootdata[p].dotdot;
+ return devwalk(c, nc, name, nname, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+static int
+rootstat(Chan *c, uchar *dp, int n)
+{
+ int p;
+
+ p = rootdata[c->qid.path].dotdot;
+ return devstat(c, dp, n, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+static Chan*
+rootopen(Chan *c, int omode)
+{
+ int p;
+
+ p = rootdata[c->qid.path].dotdot;
+ return devopen(c, omode, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+/*
+ * sysremove() knows this is a nop
+ */
+static void
+rootclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+rootread(Chan *c, void *buf, long n, vlong offset)
+{
+ ulong p, len;
+ uchar *data;
+
+ p = c->qid.path;
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, rootdata[p].ptr, rootdata[p].size, rootgen);
+ len = rootdata[p].size;
+ if(offset < 0 || offset >= len)
+ return 0;
+ if(offset+n > len)
+ n = len - offset;
+ data = rootdata[p].ptr;
+ memmove(buf, data+offset, n);
+ return n;
+}
+
+static long
+rootwrite(Chan *c, void *a, long n, vlong off)
+{
+ USED(c); USED(a); USED(n); USED(off);
+ error(Eperm);
+ return 0;
+}
+
+Dev rootdevtab = {
+ '/',
+ "root",
+
+ devinit,
+ rootattach,
+ rootwalk,
+ rootstat,
+ rootopen,
+ devcreate,
+ rootclose,
+ rootread,
+ devbread,
+ rootwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/emu/port/devsign.c
@@ -1,0 +1,440 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include <isa.h>
+#include "runt.h"
+#include "mp.h"
+#include "libsec.h"
+#include "../../libkeyring/keys.h"
+
+/*
+ * experimental version of signed modules
+ */
+
+enum
+{
+ Qdir,
+ Qkey,
+ Qctl,
+
+ Maxkey = 2048
+};
+
+static Dirtab signdir[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "signerkey", {Qkey}, 0, 0644,
+ "signerctl", {Qctl}, 0, 0600,
+};
+
+typedef struct Get Get;
+struct Get {
+ uchar* p;
+ uchar* ep;
+};
+
+#define G32(b) ((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3])
+
+static int vc(Get*);
+static int vs(void*, int, Get*, int);
+static Signerkey* findsignerkey(Skeyset*, char*, int, char*);
+extern vlong osusectime(void);
+
+int
+verifysigner(uchar *sign, int len, uchar *data, ulong ndata)
+{
+ Get sig;
+ int alg;
+ ulong issued, expires, now;
+ int footprint, r, n;
+ uchar buf[128], digest[SHA1dlen];
+ DigestState *ds;
+ volatile struct {mpint* b;} b;
+ volatile struct {mpint* s;} s;
+ SigAlgVec *sa;
+ Signerkey *key;
+ Skeyset *sigs;
+
+ /* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */
+ sigs = up->env->sigs;
+ if(sigs == nil)
+ return 1; /* not enforcing signed modules */
+ sig.p = sign;
+ sig.ep = sign+len;
+ alg = vc(&sig);
+ if(alg != 2)
+ return 0; /* we do only SHA1/RSA */
+ sa = findsigalg("rsa");
+ if(sa == nil)
+ return 0;
+ if(vs(buf, sizeof(buf), &sig, 4) < 0)
+ return 0;
+ now = osusectime()/1000000;
+ issued = G32(buf);
+ if(vs(buf, sizeof(buf), &sig, 4) < 0)
+ return 0;
+ if(issued != 0 && now < issued)
+ return 0;
+ expires = G32(buf);
+ if(expires != 0 && now >= expires)
+ return 0;
+ footprint = vc(&sig) << 8;
+ footprint |= vc(&sig);
+ if(footprint < 0)
+ return 0;
+ r = 0;
+ b.b = nil;
+ s.s = nil;
+ qlock(&sigs->l);
+ if(waserror())
+ goto out;
+ if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0) /* owner */
+ goto out;
+ buf[n] = 0;
+ key = findsignerkey(sigs, sa->name, footprint, (char*)buf);
+ if(key == nil)
+ goto out;
+ n += snprint((char*)buf+n, NUMSIZE, " %lud", expires);
+ ds = sha1(buf, n, nil, nil);
+ sha1(data, ndata, digest, ds);
+ b.b = betomp(digest, SHA1dlen, nil);
+ if(b.b == nil)
+ goto out;
+ s.s = betomp(sig.p, sig.ep-sig.p, nil);
+ if(s.s == nil)
+ goto out;
+ r = (*sa->verify)(b.b, s.s, key->pk);
+out:
+ qunlock(&sigs->l);
+ if(b.b != nil)
+ mpfree(b.b);
+ if(s.s != nil)
+ mpfree(s.s);
+ return r;
+}
+
+int
+mustbesigned(char *path, uchar *code, ulong length, Dir *dir)
+{
+ USED(code); USED(length);
+if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type);
+ /* allow only signed modules and those in #/; already loaded modules are reloaded from cache */
+ return up->env->sigs != nil && (dir == nil || dir->type != '/');
+}
+
+static int
+vc(Get *g)
+{
+ return g->p < g->ep? *g->p++: -1;
+}
+
+static int
+vs(void *s, int lim, Get *g, int n)
+{
+ int nr;
+
+ if(n < 0){
+ if(g->p >= g->ep)
+ return -1;
+ n = *g->p++;
+ lim--;
+ }
+ if(n > lim)
+ return -1;
+ nr = g->ep - g->p;
+ if(n > nr)
+ return -1;
+ if(s != nil)
+ memmove(s, g->p, n);
+ g->p += n;
+ return n;
+}
+
+static char*
+cstring(char *str, char **strp)
+{
+ char *p, *s;
+ int n;
+
+ p = strchr(str, '\n');
+ if(p == 0)
+ p = str + strlen(str);
+ n = p - str;
+ s = malloc(n+1);
+ if(s == nil)
+ return nil;
+ memmove(s, str, n);
+ s[n] = 0;
+
+ if(strp){
+ if(*p)
+ p++;
+ *strp = p;
+ }
+
+ return s;
+}
+
+static SigAlgVec*
+cstrtoalg(char *str, char **strp)
+{
+ int n;
+ char *p, name[KNAMELEN];
+
+ p = strchr(str, '\n');
+ if(p == 0){
+ p = str + strlen(str);
+ if(strp)
+ *strp = p;
+ } else {
+ if(strp)
+ *strp = p+1;
+ }
+
+ n = p - str;
+ if(n >= sizeof(name))
+ return nil;
+ strncpy(name, str, n);
+ name[n] = 0;
+ return findsigalg(name);
+}
+
+static Signerkey*
+strtopk(char *buf)
+{
+ SigAlgVec *sa;
+ char *p;
+ Signerkey *key;
+
+ key = malloc(sizeof(*key));
+ if(key == nil)
+ return nil;
+ key->r.ref = 1;
+ sa = cstrtoalg(buf, &p);
+ if(sa == nil){
+ free(key);
+ return nil;
+ }
+ key->alg = sa;
+ key->pkfree = sa->pkfree;
+ key->owner = cstring(p, &p);
+ if(key->owner == nil){
+ free(key);
+ return nil;
+ }
+ key->pk = (*sa->str2pk)(p, &p);
+ if(key->pk == nil){
+ free(key->owner);
+ free(key);
+ return nil;
+ }
+ return key;
+}
+
+static Signerkey*
+findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner)
+{
+ int i;
+ Signerkey *key;
+
+ for(i=0; i<sigs->nkey; i++){
+ key = sigs->keys[i];
+ if(key->footprint == footprint &&
+ strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 &&
+ strcmp(key->owner, owner) == 0)
+ return key;
+ }
+ return nil;
+}
+
+static Chan*
+signattach(char *spec)
+{
+ return devattach(0x03A3, spec); /* L'Σ' */
+}
+
+static Walkqid*
+signwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen);
+}
+
+static int
+signstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, signdir, nelem(signdir), devgen);
+}
+
+static Chan*
+signopen(Chan *c, int omode)
+{
+ if(c->qid.type & QTDIR) {
+ if(omode != OREAD)
+ error(Eisdir);
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ switch((ulong)c->qid.path){
+ case Qctl:
+ if(!iseve())
+ error(Eperm);
+ break;
+
+ case Qkey:
+ if(omode != OREAD && !iseve())
+ error(Eperm);
+ break;
+ }
+
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static void
+signclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+signread(Chan *c, void *va, long n, vlong offset)
+{
+ char *buf, *p;
+ SigAlgVec *sa;
+ Skeyset *sigs;
+ Signerkey *key;
+ int i;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, va, n, signdir, nelem(signdir), devgen);
+ sigs = up->env->sigs;
+ if(sigs == nil)
+ return 0;
+ switch((ulong)c->qid.path){
+ case Qkey:
+ buf = smalloc(Maxkey);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+ qlock(&sigs->l);
+ if(waserror()){
+ qunlock(&sigs->l);
+ nexterror();
+ }
+ p = buf;
+ for(i=0; i<sigs->nkey; i++){
+ key = sigs->keys[i];
+ sa = key->alg;
+ p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n",
+ key->owner, sa->name, key->footprint, key->expires);
+ }
+ poperror();
+ qunlock(&sigs->l);
+ n = readstr(offset, va, n, buf);
+ poperror();
+ free(buf);
+ return n;
+
+ case Qctl:
+ return readnum(offset, va, n, sigs->nkey, NUMSIZE);
+ }
+ return 0;
+}
+
+static long
+signwrite(Chan *c, void *va, long n, vlong offset)
+{
+ char *buf;
+ Skeyset *sigs;
+ Signerkey *okey, *key;
+ int i;
+
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+ USED(offset);
+ switch((ulong)c->qid.path){
+ case Qkey:
+ if(n >= Maxkey)
+ error(Etoobig);
+ buf = smalloc(Maxkey);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+ memmove(buf, va, n);
+ buf[n] = 0;
+
+ key = strtopk(buf);
+ if(key == nil)
+ error("bad key syntax");
+ poperror();
+ free(buf);
+
+ if(waserror()){
+ freeskey(key);
+ nexterror();
+ }
+ sigs = up->env->sigs;
+ if(sigs == nil){
+ sigs = malloc(sizeof(*sigs));
+ if(sigs == nil)
+ error(Enomem);
+ sigs->r.ref = 1;
+ up->env->sigs = sigs;
+ }
+ qlock(&sigs->l);
+ if(waserror()){
+ qunlock(&sigs->l);
+ nexterror();
+ }
+ for(i=0; i<sigs->nkey; i++){
+ okey = sigs->keys[i];
+ if(strcmp(okey->owner, key->owner) == 0){
+ /* replace existing key */
+ sigs->keys[i] = key;
+ freeskey(okey);
+ break;
+ }
+ }
+ if(i >= sigs->nkey){
+ if(sigs->nkey >= nelem(sigs->keys))
+ error("too many keys");
+ sigs->keys[sigs->nkey++] = key;
+ }
+ poperror();
+ qunlock(&sigs->l);
+ poperror(); /* key */
+
+ return n;
+ case Qctl:
+ error(Ebadctl);
+ break;
+ }
+ return 0;
+}
+
+Dev signdevtab = {
+ 0x03A3, /* L'Σ', /* U+03A3 */
+ "sign",
+
+ devinit,
+ signattach,
+ signwalk,
+ signstat,
+ signopen,
+ devcreate,
+ signclose,
+ signread,
+ devbread,
+ signwrite,
+ devbwrite,
+ devremove,
+ devwstat
+};
--- /dev/null
+++ b/emu/port/devsnarf.c
@@ -1,0 +1,162 @@
+/*
+ * host's snarf buffer
+ */
+
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+enum{
+ Qdir,
+ Qsnarf,
+
+ Maxsnarf= 100*1024
+};
+
+static
+Dirtab snarftab[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "snarf", {Qsnarf}, 0, 0666,
+};
+
+static QLock snarflock; /* easiest to synchronise all access */
+
+static Chan*
+snarfattach(char *spec)
+{
+ return devattach('^', spec);
+}
+
+static Walkqid*
+snarfwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, snarftab, nelem(snarftab), devgen);
+}
+
+static int
+snarfstat(Chan* c, uchar *db, int n)
+{
+ return devstat(c, db, n, snarftab, nelem(snarftab), devgen);
+}
+
+static Chan*
+snarfopen(Chan* c, int omode)
+{
+ c = devopen(c, omode, snarftab, nelem(snarftab), devgen);
+ if(c->qid.path == Qsnarf){
+ if(c->mode == ORDWR || c->mode == OWRITE){
+ qlock(&snarflock);
+ free(c->aux);
+ c->aux = nil;
+ qunlock(&snarflock);
+ }
+ }
+ return c;
+}
+
+static void
+snarfclose(Chan* c)
+{
+ if((c->flag & COPEN) == 0)
+ return;
+ if(c->qid.path == Qsnarf){
+ /* this must be the last reference: no need to lock */
+ if(c->mode == ORDWR || c->mode == OWRITE){
+ if(!waserror()){
+ if(c->aux != nil)
+ clipwrite(c->aux);
+ poperror();
+ }
+ }
+ free(c->aux);
+ }
+}
+
+static long
+snarfread(Chan* c, void* a, long n, vlong offset)
+{
+ void *p;
+
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, snarftab, nelem(snarftab), devgen);
+ case Qsnarf:
+ qlock(&snarflock);
+ if(waserror()){
+ qunlock(&snarflock);
+ nexterror();
+ }
+ if(offset == 0){
+ p = c->aux;
+ c->aux = nil;
+ free(p);
+ c->aux = clipread();
+ }
+ if(c->aux != nil)
+ n = readstr(offset, a, n, c->aux);
+ else
+ n = 0;
+ poperror();
+ qunlock(&snarflock);
+ break;
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+snarfwrite(Chan* c, void* va, long n, vlong offset)
+{
+ ulong l;
+ char *p;
+
+ switch((ulong)c->qid.path){
+ case Qsnarf:
+ /* append only */
+ USED(offset); /* not */
+ qlock(&snarflock);
+ if(waserror()){
+ qunlock(&snarflock);
+ nexterror();
+ }
+ if(c->aux != nil)
+ l = strlen(c->aux);
+ else
+ l = 0;
+ if(l+n > Maxsnarf)
+ error(Etoobig);
+ c->aux = realloc(c->aux, l+n+1);
+ if((p = c->aux) == nil)
+ error(Enovmem);
+ memmove(p+l, va, n);
+ p[l+n] = 0;
+ snarftab[1].qid.vers++;
+ poperror();
+ qunlock(&snarflock);
+ break;
+ default:
+ error(Ebadusefd);
+ }
+ return n;
+}
+
+Dev snarfdevtab = {
+ '^',
+ "snarf",
+
+ devinit,
+ snarfattach,
+ snarfwalk,
+ snarfstat,
+ snarfopen,
+ devcreate,
+ snarfclose,
+ snarfread,
+ devbread,
+ snarfwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/emu/port/devsrv.c
@@ -1,0 +1,829 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include <isa.h>
+#include "runt.h"
+
+typedef struct SrvFile SrvFile;
+typedef struct Pending Pending;
+
+/* request pending to a server, in case a server vanishes */
+struct Pending
+{
+ Pending* next;
+ Pending* prev;
+ int fid;
+ Channel* rc;
+ Channel* wc;
+};
+
+struct SrvFile
+{
+ char* name;
+ char* user;
+ ulong perm;
+ Qid qid;
+ int ref;
+
+ /* root directory */
+ char* spec;
+ SrvFile* devlist;
+ SrvFile* entry;
+
+ /* file */
+ int opens;
+ int flags;
+ vlong length;
+ Channel* read;
+ Channel* write;
+ SrvFile* dir; /* parent directory */
+ Pending waitlist; /* pending requests from client opens */
+};
+
+enum
+{
+ SORCLOSE = (1<<0),
+ SRDCLOSE = (1<<1),
+ SWRCLOSE = (1<<2),
+ SREMOVED = (1<<3),
+};
+
+typedef struct SrvDev SrvDev;
+struct SrvDev
+{
+ Type* Rread;
+ Type* Rwrite;
+ QLock l;
+ ulong pathgen;
+ SrvFile* devices;
+};
+
+static SrvDev dev;
+
+void freechan(Heap*, int);
+static void freerdchan(Heap*, int);
+static void freewrchan(Heap*, int);
+static void delwaiting(Pending*);
+
+Type *Trdchan;
+Type *Twrchan;
+
+static int
+srvgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+ SrvFile *f;
+
+ USED(name);
+ USED(tab);
+ USED(ntab);
+
+ if(s == DEVDOTDOT){
+ devdir(c, c->qid, "#s", 0, eve, 0555, dp);
+ return 1;
+ }
+ f = c->aux;
+ if((c->qid.type & QTDIR) == 0){
+ if(s > 0)
+ return -1;
+ devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
+ return 1;
+ }
+
+ for(f = f->entry; f != nil; f = f->entry){
+ if(s-- == 0)
+ break;
+ }
+ if(f == nil)
+ return -1;
+
+ devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
+ return 1;
+}
+
+static void
+srvinit(void)
+{
+ static uchar rmap[] = Sys_Rread_map;
+ static uchar wmap[] = Sys_Rwrite_map;
+
+ Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np);
+ Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np);
+
+ dev.pathgen = 1;
+ dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap));
+ dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap));
+}
+
+static int
+srvcanattach(SrvFile *d)
+{
+ if(strcmp(d->user, up->env->user) == 0)
+ return 1;
+
+ /*
+ * Need write permission in other to allow attaches if
+ * we are not the owner
+ */
+ if(d->perm & 2)
+ return 1;
+
+ return 0;
+}
+
+static Chan*
+srvattach(char *spec)
+{
+ Chan *c;
+ SrvFile *d;
+ char srvname[16];
+
+ qlock(&dev.l);
+ if(waserror()){
+ qunlock(&dev.l);
+ nexterror();
+ }
+
+ if(spec[0] != '\0'){
+ for(d = dev.devices; d != nil; d = d->devlist){
+ if(strcmp(spec, d->spec) == 0){
+ if(!srvcanattach(d))
+ error(Eperm);
+ c = devattach('s', spec);
+ c->aux = d;
+ c->qid = d->qid;
+ d->ref++;
+ poperror();
+ qunlock(&dev.l);
+ return c;
+ }
+ }
+ }
+
+ d = malloc(sizeof(SrvFile));
+ if(d == nil)
+ error(Enomem);
+
+ d->ref = 1;
+ kstrdup(&d->spec, spec);
+ kstrdup(&d->user, up->env->user);
+ snprint(srvname, sizeof(srvname), "srv%ld", up->env->pgrp->pgrpid);
+ kstrdup(&d->name, srvname);
+ d->perm = DMDIR|0770;
+ mkqid(&d->qid, dev.pathgen++, 0, QTDIR);
+
+ d->devlist = dev.devices;
+ dev.devices = d;
+
+ poperror();
+ qunlock(&dev.l);
+
+ c = devattach('s', spec);
+ c->aux = d;
+ c->qid = d->qid;
+
+ return c;
+}
+
+static Walkqid*
+srvwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ SrvFile *d, *pd;
+ Walkqid *w;
+
+ pd = c->aux;
+ qlock(&dev.l);
+ if(waserror()){
+ qunlock(&dev.l);
+ nexterror();
+ }
+
+ w = devwalk(c, nc, name, nname, nil, 0, srvgen);
+ if(w != nil && w->clone != nil){
+ if(nname != 0){
+ for(d = pd->entry; d != nil; d = d->entry)
+ if(d->qid.path == w->clone->qid.path)
+ break;
+ if(d == nil)
+ panic("srvwalk");
+ if(w->clone == c)
+ pd->ref--;
+ }else
+ d = pd;
+ w->clone->aux = d;
+ d->ref++;
+ }
+
+ poperror();
+ qunlock(&dev.l);
+ return w;
+}
+
+static int
+srvstat(Chan *c, uchar *db, int n)
+{
+ qlock(&dev.l);
+ if(waserror()){
+ qunlock(&dev.l);
+ nexterror();
+ }
+ n = devstat(c, db, n, 0, 0, srvgen);
+ poperror();
+ qunlock(&dev.l);
+ return n;
+}
+
+static Chan*
+srvopen(Chan *c, int omode)
+{
+ SrvFile *sf;
+
+ openmode(omode); /* check it */
+ if(c->qid.type & QTDIR){
+ if(omode != OREAD)
+ error(Eisdir);
+ c->mode = omode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ sf = c->aux;
+
+ qlock(&dev.l);
+ if(waserror()){
+ qunlock(&dev.l);
+ nexterror();
+ }
+ devpermcheck(sf->user, sf->perm, omode);
+ if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0)
+ error(Eperm);
+ if(sf->perm & DMEXCL && sf->opens != 0)
+ error(Einuse);
+ sf->opens++;
+ if(omode&ORCLOSE)
+ sf->flags |= SORCLOSE;
+ poperror();
+ qunlock(&dev.l);
+
+ c->offset = 0;
+ c->flag |= COPEN;
+ c->mode = openmode(omode);
+
+ return c;
+}
+
+static int
+srvwstat(Chan *c, uchar *dp, int n)
+{
+ Dir *d;
+ SrvFile *sf, *f;
+
+ sf = c->aux;
+ if(strcmp(up->env->user, sf->user) != 0)
+ error(Eperm);
+
+ d = smalloc(sizeof(*d)+n);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ n = convM2D(dp, n, d, (char*)&d[1]);
+ if(n == 0)
+ error(Eshortstat);
+ if(!emptystr(d->name)){
+ if(sf->dir == nil)
+ error(Eperm);
+ validwstatname(d->name);
+ qlock(&dev.l);
+ for(f = sf->dir; f != nil; f = f->entry)
+ if(strcmp(f->name, d->name) == 0){
+ qunlock(&dev.l);
+ error(Eexist);
+ }
+ kstrdup(&sf->name, d->name);
+ qunlock(&dev.l);
+ }
+ if(d->mode != ~0UL)
+ sf->perm = d->mode & (DMEXCL|DMAPPEND|0777);
+ if(d->length != (vlong)-1)
+ sf->length = d->length;
+ poperror();
+ free(d);
+ return n;
+}
+
+static void
+srvputdir(SrvFile *dir)
+{
+ SrvFile **l, *d;
+
+ dir->ref--;
+ if(dir->ref != 0)
+ return;
+
+ for(l = &dev.devices; (d = *l) != nil; l = &d->devlist)
+ if(d == dir){
+ *l = d->devlist;
+ break;
+ }
+ free(dir->spec);
+ free(dir->user);
+ free(dir->name);
+ free(dir);
+}
+
+static void
+srvunblock(SrvFile *sf, int fid)
+{
+ Channel *d;
+ Sys_FileIO_read rreq;
+ Sys_FileIO_write wreq;
+
+ acquire();
+ if(waserror()){
+ release();
+ nexterror();
+ }
+ d = sf->read;
+ if(d != H){
+ rreq.t0 = 0;
+ rreq.t1 = 0;
+ rreq.t2 = fid;
+ rreq.t3 = H;
+ csendalt(d, &rreq, d->mid.t, -1);
+ }
+
+ d = sf->write;
+ if(d != H){
+ wreq.t0 = 0;
+ wreq.t1 = H;
+ wreq.t2 = fid;
+ wreq.t3 = H;
+ csendalt(d, &wreq, d->mid.t, -1);
+ }
+ poperror();
+ release();
+}
+
+static void
+srvcancelreqs(SrvFile *sf)
+{
+ Pending *w, *ws;
+ Sys_Rread rreply;
+ Sys_Rwrite wreply;
+
+ acquire();
+ ws = &sf->waitlist;
+ while((w = ws->next) != ws){
+ delwaiting(w);
+ if(waserror() == 0){
+ if(w->rc != nil){
+ rreply.t0 = H;
+ rreply.t1 = c2string(Ehungup, strlen(Ehungup));
+ csend(w->rc, &rreply);
+ }
+ if(w->wc != nil){
+ wreply.t0 = 0;
+ wreply.t1 = c2string(Ehungup, strlen(Ehungup));
+ csend(w->wc, &wreply);
+ }
+ poperror();
+ }
+ }
+ release();
+}
+
+static void
+srvdelete(SrvFile *sf)
+{
+ SrvFile *f, **l;
+
+ if((sf->flags & SREMOVED) == 0){
+ for(l = &sf->dir->entry; (f = *l) != nil; l = &f->entry){
+ if(sf == f){
+ *l = f->entry;
+ break;
+ }
+ }
+ sf->ref--;
+ sf->flags |= SREMOVED;
+ }
+}
+
+static void
+srvchkref(SrvFile *sf)
+{
+ if(sf->ref != 0)
+ return;
+
+ if(sf->dir != nil)
+ srvputdir(sf->dir);
+
+ free(sf->user);
+ free(sf->name);
+ free(sf);
+}
+
+static void
+srvfree(SrvFile *sf, int flag)
+{
+ sf->flags |= flag;
+ if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){
+ sf->ref--;
+ srvdelete(sf);
+ /* no further requests can arrive; return error to pending requests */
+ srvcancelreqs(sf);
+ srvchkref(sf);
+ }
+}
+
+static void
+freerdchan(Heap *h, int swept)
+{
+ SrvFile *sf;
+
+ release();
+ qlock(&dev.l);
+ sf = H2D(Channel*, h)->aux;
+ sf->read = H;
+ srvfree(sf, SRDCLOSE);
+ qunlock(&dev.l);
+ acquire();
+
+ freechan(h, swept);
+}
+
+static void
+freewrchan(Heap *h, int swept)
+{
+ SrvFile *sf;
+
+ release();
+ qlock(&dev.l);
+ sf = H2D(Channel*, h)->aux;
+ sf->write = H;
+ srvfree(sf, SWRCLOSE);
+ qunlock(&dev.l);
+ acquire();
+
+ freechan(h, swept);
+}
+
+static void
+srvclunk(Chan *c, int remove)
+{
+ int opens, noperm;
+ SrvFile *sf;
+
+ sf = c->aux;
+ qlock(&dev.l);
+ if(c->qid.type & QTDIR){
+ srvputdir(sf);
+ qunlock(&dev.l);
+ if(remove)
+ error(Eperm);
+ return;
+ }
+ opens = 0;
+ if(c->flag & COPEN){
+ opens = sf->opens--;
+ if(sf->read != H || sf->write != H)
+ srvunblock(sf, c->fid);
+ }
+
+ sf->ref--;
+ if(opens == 1){
+ if(sf->flags & SORCLOSE)
+ remove = 1;
+ }
+
+ noperm = 0;
+ if(remove && strcmp(sf->dir->user, up->env->user) != 0){
+ noperm = 1;
+ remove = 0;
+ }
+ if(remove)
+ srvdelete(sf);
+ srvchkref(sf);
+ qunlock(&dev.l);
+
+ if(noperm)
+ error(Eperm);
+}
+
+static void
+srvclose(Chan *c)
+{
+ srvclunk(c, 0);
+}
+
+static void
+srvremove(Chan *c)
+{
+ srvclunk(c, 1);
+}
+
+static void
+addwaiting(SrvFile *sp, Pending *w)
+{
+ Pending *sw;
+
+ sw = &sp->waitlist;
+ w->next = sw;
+ w->prev = sw->prev;
+ sw->prev->next = w;
+ sw->prev = w;
+}
+
+static void
+delwaiting(Pending *w)
+{
+ w->next->prev = w->prev;
+ w->prev->next = w->next;
+}
+
+static long
+srvread(Chan *c, void *va, long count, vlong offset)
+{
+ int l;
+ Heap * volatile h;
+ Array *a;
+ SrvFile *sp;
+ Channel *rc;
+ Channel *rd;
+ Pending wait;
+ Sys_Rread * volatile r;
+ Sys_FileIO_read req;
+
+ if(c->qid.type & QTDIR){
+ qlock(&dev.l);
+ if(waserror()){
+ qunlock(&dev.l);
+ nexterror();
+ }
+ l = devdirread(c, va, count, 0, 0, srvgen);
+ poperror();
+ qunlock(&dev.l);
+ return l;
+ }
+
+ sp = c->aux;
+
+ acquire();
+ if(waserror()){
+ release();
+ nexterror();
+ }
+
+ rd = sp->read;
+ if(rd == H)
+ error(Ehungup);
+
+ rc = cnewc(dev.Rread, movtmp, 1);
+ ptradd(D2H(rc));
+ if(waserror()){
+ ptrdel(D2H(rc));
+ destroy(rc);
+ nexterror();
+ }
+
+ req.t0 = offset;
+ req.t1 = count;
+ req.t2 = c->fid;
+ req.t3 = rc;
+ csend(rd, &req);
+
+ h = heap(dev.Rread);
+ r = H2D(Sys_Rread *, h);
+ ptradd(h);
+ if(waserror()){
+ ptrdel(h);
+ destroy(r);
+ nexterror();
+ }
+
+ wait.fid = c->fid;
+ wait.rc = rc;
+ wait.wc = nil;
+ addwaiting(sp, &wait);
+ if(waserror()){
+ delwaiting(&wait);
+ nexterror();
+ }
+ crecv(rc, r);
+ poperror();
+ delwaiting(&wait);
+
+ if(r->t1 != H)
+ error(string2c(r->t1));
+
+ a = r->t0;
+ l = 0;
+ if(a != H){
+ l = a->len;
+ if(l > count)
+ l = count;
+ memmove(va, a->data, l);
+ }
+
+ poperror();
+ ptrdel(h);
+ destroy(r);
+
+ poperror();
+ ptrdel(D2H(rc));
+ destroy(rc);
+
+ poperror();
+ release();
+
+ return l;
+}
+
+static long
+srvwrite(Chan *c, void *va, long count, vlong offset)
+{
+ long l;
+ Heap * volatile h;
+ SrvFile *sp;
+ Channel *wc;
+ Channel *wr;
+ Pending wait;
+ Sys_Rwrite * volatile w;
+ Sys_FileIO_write req;
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ acquire();
+ if(waserror()){
+ release();
+ nexterror();
+ }
+
+ sp = c->aux;
+ wr = sp->write;
+ if(wr == H)
+ error(Ehungup);
+
+ wc = cnewc(dev.Rwrite, movtmp, 1);
+ ptradd(D2H(wc));
+ if(waserror()){
+ ptrdel(D2H(wc));
+ destroy(wc);
+ nexterror();
+ }
+
+ req.t0 = offset;
+ req.t1 = mem2array(va, count);
+ req.t2 = c->fid;
+ req.t3 = wc;
+
+ ptradd(D2H(req.t1));
+
+ if(waserror()){
+ ptrdel(D2H(req.t1));
+ destroy(req.t1);
+ nexterror();
+ }
+
+ csend(wr, &req);
+
+ poperror();
+ ptrdel(D2H(req.t1));
+ destroy(req.t1);
+
+ h = heap(dev.Rwrite);
+ w = H2D(Sys_Rwrite *, h);
+ ptradd(h);
+
+ if(waserror()){
+ ptrdel(h);
+ destroy(w);
+ nexterror();
+ }
+
+ wait.fid = c->fid;
+ wait.rc = nil;
+ wait.wc = wc;
+ addwaiting(sp, &wait);
+ if(waserror()){
+ delwaiting(&wait);
+ nexterror();
+ }
+ crecv(wc, w);
+ poperror();
+ delwaiting(&wait);
+
+ if(w->t1 != H)
+ error(string2c(w->t1));
+ poperror();
+ ptrdel(h);
+ l = w->t0;
+ destroy(w);
+
+ poperror();
+ ptrdel(D2H(wc));
+ destroy(wc);
+
+ poperror();
+ release();
+ if(l < 0)
+ l = 0;
+ return l;
+}
+
+static void
+srvretype(Channel *c, SrvFile *f, Type *t)
+{
+ Heap *h;
+
+ h = D2H(c);
+ freetype(h->t);
+ h->t = t;
+ t->ref++;
+ c->aux = f;
+}
+
+int
+srvf2c(char *dir, char *file, Sys_FileIO *io)
+{
+ SrvFile *s, *f;
+ volatile struct { Chan *c; } c;
+
+ c.c = nil;
+ if(waserror()){
+ cclose(c.c);
+ return -1;
+ }
+
+ if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
+ error(Efilename);
+
+ c.c = namec(dir, Aaccess, 0, 0);
+ if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's')
+ error("directory not a srv device");
+
+ s = c.c->aux;
+
+ qlock(&dev.l);
+ if(waserror()){
+ qunlock(&dev.l);
+ nexterror();
+ }
+ for(f = s->entry; f != nil; f = f->entry){
+ if(strcmp(f->name, file) == 0)
+ error(Eexist);
+ }
+
+ f = malloc(sizeof(SrvFile));
+ if(f == nil)
+ error(Enomem);
+
+ srvretype(io->read, f, Trdchan);
+ srvretype(io->write, f, Twrchan);
+ f->read = io->read;
+ f->write = io->write;
+
+ f->waitlist.next = &f->waitlist;
+ f->waitlist.prev = &f->waitlist;
+
+ kstrdup(&f->name, file);
+ kstrdup(&f->user, up->env->user);
+ f->perm = 0666 & (~0666 | (s->perm & 0666));
+ f->length = 0;
+ f->ref = 2;
+ mkqid(&f->qid, dev.pathgen++, 0, QTFILE);
+
+ f->entry = s->entry;
+ s->entry = f;
+ s->ref++;
+ f->dir = s;
+ poperror();
+ qunlock(&dev.l);
+
+ cclose(c.c);
+ poperror();
+
+ return 0;
+}
+
+Dev srvdevtab = {
+ 's',
+ "srv",
+
+ srvinit,
+ srvattach,
+ srvwalk,
+ srvstat,
+ srvopen,
+ devcreate,
+ srvclose,
+ srvread,
+ devbread,
+ srvwrite,
+ devbwrite,
+ srvremove,
+ srvwstat
+};
--- /dev/null
+++ b/emu/port/devssl.c
@@ -1,0 +1,1441 @@
+/*
+ * devssl - secure sockets layer
+ */
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include "mp.h"
+#include "libsec.h"
+
+typedef struct OneWay OneWay;
+struct OneWay
+{
+ QLock q;
+ QLock ctlq;
+
+ void *state; /* encryption state */
+ int slen; /* secret data length */
+ uchar *secret; /* secret */
+ ulong mid; /* message id */
+};
+
+enum
+{
+ /* connection states */
+ Sincomplete= 0,
+ Sclear= 1,
+ Sencrypting= 2,
+ Sdigesting= 4,
+ Sdigenc= Sencrypting|Sdigesting,
+
+ /* encryption algorithms */
+ Noencryption= 0,
+ DESCBC= 1,
+ DESECB= 2,
+ RC4= 3,
+ IDEACBC= 4,
+ IDEAECB= 5
+};
+
+typedef struct Dstate Dstate;
+struct Dstate
+{
+ Chan *c; /* io channel */
+ uchar state; /* state of connection */
+ int ref; /* serialized by dslock for atomic destroy */
+
+ uchar encryptalg; /* encryption algorithm */
+ ushort blocklen; /* blocking length */
+
+ ushort diglen; /* length of digest */
+ DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */
+
+ /* for SSL format */
+ int max; /* maximum unpadded data per msg */
+ int maxpad; /* maximum padded data per msg */
+
+ /* input side */
+ OneWay in;
+ Block *processed;
+ Block *unprocessed;
+
+ /* output side */
+ OneWay out;
+
+ /* protections */
+ char* user;
+ int perm;
+};
+
+enum
+{
+ Maxdmsg= 1<<16,
+ Maxdstate= 1<<10,
+};
+
+Lock dslock;
+int dshiwat;
+int maxdstate = 20;
+Dstate** dstate;
+
+enum{
+ Qtopdir = 1, /* top level directory */
+ Qclonus,
+ Qconvdir, /* directory for a conversation */
+ Qdata,
+ Qctl,
+ Qsecretin,
+ Qsecretout,
+ Qencalgs,
+ Qhashalgs
+};
+
+#define TYPE(x) ((ulong)(x).path & 0xf)
+#define CONV(x) (((ulong)(x).path >> 4)&(Maxdstate-1))
+#define QID(c, y) (((c)<<4) | (y))
+
+static char* encalgs;
+static char* hashalgs;
+
+void producerand(void);
+
+static void alglistinit(void);
+static void ensure(Dstate*, Block**, int);
+static void consume(Block**, uchar*, int);
+static void setsecret(OneWay*, uchar*, int);
+static Block* encryptb(Dstate*, Block*, int);
+static Block* decryptb(Dstate*, Block*);
+static Block* digestb(Dstate*, Block*, int);
+static void checkdigestb(Dstate*, Block*);
+static Chan* buftochan(char*);
+static void sslhangup(Dstate*);
+static void dsclone(Chan *c);
+static void dsnew(Chan *c, Dstate **);
+
+static int
+sslgen(Chan *c, char *dname, Dirtab *d, int nd, int s, Dir *dp)
+{
+ Qid q;
+ Dstate *ds;
+ char *p, *nm;
+
+ USED(dname);
+ USED(nd);
+ USED(d);
+ q.type = QTFILE;
+ q.vers = 0;
+ if(s == DEVDOTDOT){
+ q.path = QID(0, Qtopdir);
+ q.type = QTDIR;
+ devdir(c, q, "#D", 0, eve, 0555, dp);
+ return 1;
+ }
+ switch(TYPE(c->qid)) {
+ case Qtopdir:
+ if(s < dshiwat) {
+ q.path = QID(s, Qconvdir);
+ q.type = QTDIR;
+ ds = dstate[s];
+ if(ds != 0)
+ nm = ds->user;
+ else
+ nm = eve;
+ snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
+ devdir(c, q, up->genbuf, 0, nm, DMDIR|0555, dp);
+ return 1;
+ }
+ if(s > dshiwat)
+ return -1;
+ /* fall through */
+ case Qclonus:
+ q.path = QID(0, Qclonus);
+ devdir(c, q, "clone", 0, eve, 0666, dp);
+ return 1;
+ case Qconvdir:
+ ds = dstate[CONV(c->qid)];
+ if(ds != 0)
+ nm = ds->user;
+ else
+ nm = eve;
+ switch(s) {
+ default:
+ return -1;
+ case 0:
+ q.path = QID(CONV(c->qid), Qctl);
+ p = "ctl";
+ break;
+ case 1:
+ q.path = QID(CONV(c->qid), Qdata);
+ p = "data";
+ break;
+ case 2:
+ q.path = QID(CONV(c->qid), Qsecretin);
+ p = "secretin";
+ break;
+ case 3:
+ q.path = QID(CONV(c->qid), Qsecretout);
+ p = "secretout";
+ break;
+ case 4:
+ q.path = QID(CONV(c->qid), Qencalgs);
+ p = "encalgs";
+ break;
+ case 5:
+ q.path = QID(CONV(c->qid), Qhashalgs);
+ p = "hashalgs";
+ break;
+ }
+ devdir(c, q, p, 0, nm, 0660, dp);
+ return 1;
+ }
+ return -1;
+}
+
+static void
+sslinit(void)
+{
+ if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0)
+ panic("sslinit");
+ alglistinit();
+}
+
+static Chan *
+sslattach(char *spec)
+{
+ Chan *c;
+
+ c = devattach('D', spec);
+ c->qid.path = QID(0, Qtopdir);
+ c->qid.vers = 0;
+ c->qid.type = QTDIR;
+ return c;
+}
+
+static Walkqid*
+sslwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, sslgen);
+}
+
+static int
+sslstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, sslgen);
+}
+
+static Chan*
+sslopen(Chan *c, int omode)
+{
+ Dstate *s, **pp;
+ int perm;
+
+ perm = 0;
+ omode &= 3;
+ switch(omode) {
+ case OREAD:
+ perm = 4;
+ break;
+ case OWRITE:
+ perm = 2;
+ break;
+ case ORDWR:
+ perm = 6;
+ break;
+ }
+
+ switch(TYPE(c->qid)) {
+ default:
+ panic("sslopen");
+ case Qtopdir:
+ case Qconvdir:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+ case Qclonus:
+ dsclone(c);
+ break;
+ case Qctl:
+ case Qdata:
+ case Qsecretin:
+ case Qsecretout:
+ if(waserror()) {
+ unlock(&dslock);
+ nexterror();
+ }
+ lock(&dslock);
+ pp = &dstate[CONV(c->qid)];
+ s = *pp;
+ if(s == 0)
+ dsnew(c, pp);
+ else {
+ if((perm & (s->perm>>6)) != perm
+ && (strcmp(up->env->user, s->user) != 0
+ || (perm & s->perm) != perm))
+ error(Eperm);
+
+ s->ref++;
+ }
+ unlock(&dslock);
+ poperror();
+ break;
+ case Qencalgs:
+ case Qhashalgs:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+ }
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static int
+sslwstat(Chan *c, uchar *db, int n)
+{
+ Dir *dir;
+ Dstate *s;
+ int m;
+
+ s = dstate[CONV(c->qid)];
+ if(s == 0)
+ error(Ebadusefd);
+ if(strcmp(s->user, up->env->user) != 0)
+ error(Eperm);
+
+ dir = smalloc(sizeof(Dir)+n);
+ m = convM2D(db, n, &dir[0], (char*)&dir[1]);
+ if(m == 0){
+ free(dir);
+ error(Eshortstat);
+ }
+
+ if(!emptystr(dir->uid))
+ kstrdup(&s->user, dir->uid);
+ if(dir->mode != ~0UL)
+ s->perm = dir->mode;
+
+ free(dir);
+ return m;
+}
+
+static void
+sslclose(Chan *c)
+{
+ Dstate *s;
+
+ switch(TYPE(c->qid)) {
+ case Qctl:
+ case Qdata:
+ case Qsecretin:
+ case Qsecretout:
+ if((c->flag & COPEN) == 0)
+ break;
+
+ s = dstate[CONV(c->qid)];
+ if(s == 0)
+ break;
+
+ lock(&dslock);
+ if(--s->ref > 0) {
+ unlock(&dslock);
+ break;
+ }
+ dstate[CONV(c->qid)] = 0;
+ unlock(&dslock);
+
+ sslhangup(s);
+ if(s->c)
+ cclose(s->c);
+ free(s->user);
+ free(s->in.secret);
+ free(s->out.secret);
+ free(s->in.state);
+ free(s->out.state);
+ free(s);
+ }
+}
+
+/*
+ * make sure we have at least 'n' bytes in list 'l'
+ */
+static void
+ensure(Dstate *s, Block **l, int n)
+{
+ int sofar, i;
+ Block *b, *bl;
+
+ sofar = 0;
+ for(b = *l; b; b = b->next){
+ sofar += BLEN(b);
+ if(sofar >= n)
+ return;
+ l = &b->next;
+ }
+
+ while(sofar < n){
+ bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0);
+ if(bl == 0)
+ error(Ehungup);
+ *l = bl;
+ i = 0;
+ for(b = bl; b; b = b->next){
+ i += BLEN(b);
+ l = &b->next;
+ }
+ if(i == 0)
+ error(Ehungup);
+
+ sofar += i;
+ }
+}
+
+/*
+ * copy 'n' bytes from 'l' into 'p' and free
+ * the bytes in 'l'
+ */
+static void
+consume(Block **l, uchar *p, int n)
+{
+ Block *b;
+ int i;
+
+ for(; *l && n > 0; n -= i){
+ b = *l;
+ i = BLEN(b);
+ if(i > n)
+ i = n;
+ memmove(p, b->rp, i);
+ b->rp += i;
+ p += i;
+ if(BLEN(b) < 0)
+ panic("consume");
+ if(BLEN(b))
+ break;
+ *l = b->next;
+ freeb(b);
+ }
+}
+
+/*
+ * remove at most n bytes from the queue, if discard is set
+ * dump the remainder
+ */
+static Block*
+qtake(Block **l, int n, int discard)
+{
+ Block *nb, *b, *first;
+ int i;
+
+ first = *l;
+ for(b = first; b; b = b->next){
+ i = BLEN(b);
+ if(i == n){
+ if(discard){
+ freeblist(b->next);
+ *l = 0;
+ } else
+ *l = b->next;
+ b->next = 0;
+ return first;
+ } else if(i > n){
+ i -= n;
+ if(discard){
+ freeblist(b->next);
+ *l = 0;
+ } else {
+ nb = allocb(i);
+ memmove(nb->wp, b->rp+n, i);
+ nb->wp += i;
+ nb->next = b->next;
+ *l = nb;
+ }
+ b->wp -= i;
+ b->next = 0;
+ if(BLEN(b) < 0)
+ panic("qtake");
+ return first;
+ } else
+ n -= i;
+ if(BLEN(b) < 0)
+ panic("qtake");
+ }
+ *l = 0;
+ return first;
+}
+
+static Block*
+sslbread(Chan *c, long n, ulong offset)
+{
+ volatile struct { Dstate *s; } s;
+ volatile struct { int nc; } nc;
+ Block *b;
+ uchar count[3];
+ int len, pad;
+
+ USED(offset);
+ s.s = dstate[CONV(c->qid)];
+ if(s.s == 0)
+ panic("sslbread");
+ if(s.s->state == Sincomplete)
+ error(Ebadusefd);
+
+ nc.nc = 0;
+ if(waserror()){
+ qunlock(&s.s->in.q);
+ if(strcmp(up->env->errstr, "interrupted") == 0){
+ if(nc.nc > 0){
+ b = allocb(nc.nc);
+ memmove(b->wp, count, nc.nc);
+ b->wp += nc.nc;
+ b->next = s.s->unprocessed;
+ s.s->unprocessed = b;
+ }
+ } else
+ sslhangup(s.s);
+ nexterror();
+ }
+ qlock(&s.s->in.q);
+
+ if(s.s->processed == 0){
+ /* read in the whole message */
+ ensure(s.s, &s.s->unprocessed, 2);
+
+ consume(&s.s->unprocessed, count, 2);
+ nc.nc += 2;
+ if(count[0] & 0x80){
+ len = ((count[0] & 0x7f)<<8) | count[1];
+ ensure(s.s, &s.s->unprocessed, len);
+ pad = 0;
+ } else {
+ len = ((count[0] & 0x3f)<<8) | count[1];
+ ensure(s.s, &s.s->unprocessed, len+1);
+ consume(&s.s->unprocessed, count + nc.nc, 1);
+ pad = count[nc.nc];
+ nc.nc++;
+ if(pad > len){
+ print("pad %d buf len %d\n", pad, len);
+ error("bad pad in ssl message");
+ }
+ }
+ nc.nc = 0;
+
+ /* put extra on unprocessed queue */
+ s.s->processed = qtake(&s.s->unprocessed, len, 0);
+
+ if(waserror()){
+ qunlock(&s.s->in.ctlq);
+ nexterror();
+ }
+ qlock(&s.s->in.ctlq);
+ switch(s.s->state){
+ case Sencrypting:
+ s.s->processed = decryptb(s.s, s.s->processed);
+ break;
+ case Sdigesting:
+ s.s->processed = pullupblock(s.s->processed, s.s->diglen);
+ if(s.s->processed == 0)
+ error("ssl message too short");
+ checkdigestb(s.s, s.s->processed);
+ s.s->processed->rp += s.s->diglen;
+ break;
+ case Sdigenc:
+ s.s->processed = decryptb(s.s, s.s->processed);
+ s.s->processed = pullupblock(s.s->processed, s.s->diglen);
+ if(s.s->processed == 0)
+ error("ssl message too short");
+ checkdigestb(s.s, s.s->processed);
+ s.s->processed->rp += s.s->diglen;
+ len -= s.s->diglen;
+ break;
+ }
+ s.s->in.mid++;
+ qunlock(&s.s->in.ctlq);
+ poperror();
+
+ /* remove pad */
+ if(pad)
+ s.s->processed = qtake(&s.s->processed, len - pad, 1);
+ }
+
+ /* return at most what was asked for */
+ b = qtake(&s.s->processed, n, 0);
+
+ qunlock(&s.s->in.q);
+ poperror();
+
+ return b;
+}
+
+static long
+sslread(Chan *c, void *a, long n, vlong offset)
+{
+ volatile struct { Block *b; } b;
+ Block *nb;
+ uchar *va;
+ int i;
+ char buf[128];
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, sslgen);
+
+ switch(TYPE(c->qid)) {
+ default:
+ error(Ebadusefd);
+ case Qctl:
+ sprint(buf, "%ld", CONV(c->qid));
+ return readstr(offset, a, n, buf);
+ case Qdata:
+ b.b = sslbread(c, n, offset);
+ break;
+ case Qencalgs:
+ return readstr(offset, a, n, encalgs);
+ case Qhashalgs:
+ return readstr(offset, a, n, hashalgs);
+ }
+
+ n = 0;
+ va = a;
+ for(nb = b.b; nb; nb = nb->next){
+ i = BLEN(nb);
+ memmove(va+n, nb->rp, i);
+ n += i;
+ }
+
+ freeblist(b.b);
+
+ return n;
+}
+
+/*
+ * this algorithm doesn't have to be great since we're just
+ * trying to obscure the block fill
+ */
+static void
+randfill(uchar *buf, int len)
+{
+ while(len-- > 0)
+ *buf++ = nrand(256);
+}
+
+/*
+ * use SSL record format, add in count and digest or encrypt
+ */
+static long
+sslbwrite(Chan *c, Block *b, ulong offset)
+{
+ volatile struct { Dstate *s; } s;
+ volatile struct { Block *b; } bb;
+ Block *nb;
+ int h, n, m, pad, rv;
+ uchar *p;
+
+ bb.b = b;
+ s.s = dstate[CONV(c->qid)];
+ if(s.s == 0)
+ panic("sslbwrite");
+ if(s.s->state == Sincomplete){
+ freeb(b);
+ error(Ebadusefd);
+ }
+
+ if(waserror()){
+ qunlock(&s.s->out.q);
+ if(bb.b)
+ freeb(bb.b);
+ sslhangup(s.s);
+ nexterror();
+ }
+ qlock(&s.s->out.q);
+
+ rv = 0;
+ while(bb.b){
+ m = n = BLEN(bb.b);
+ h = s.s->diglen + 2;
+
+ /* trim to maximum block size */
+ pad = 0;
+ if(m > s.s->max){
+ m = s.s->max;
+ } else if(s.s->blocklen != 1){
+ pad = (m + s.s->diglen)%s.s->blocklen;
+ if(pad){
+ if(m > s.s->maxpad){
+ pad = 0;
+ m = s.s->maxpad;
+ } else {
+ pad = s.s->blocklen - pad;
+ h++;
+ }
+ }
+ }
+
+ rv += m;
+ if(m != n){
+ nb = allocb(m + h + pad);
+ memmove(nb->wp + h, bb.b->rp, m);
+ nb->wp += m + h;
+ bb.b->rp += m;
+ } else {
+ /* add header space */
+ nb = padblock(bb.b, h);
+ bb.b = 0;
+ }
+ m += s.s->diglen;
+
+ /* SSLv2 style count */
+ if(pad){
+ nb = padblock(nb, -pad);
+ randfill(nb->wp, pad);
+ nb->wp += pad;
+ m += pad;
+
+ p = nb->rp;
+ p[0] = (m>>8);
+ p[1] = m;
+ p[2] = pad;
+ offset = 3;
+ } else {
+ p = nb->rp;
+ p[0] = (m>>8) | 0x80;
+ p[1] = m;
+ offset = 2;
+ }
+
+ switch(s.s->state){
+ case Sencrypting:
+ nb = encryptb(s.s, nb, offset);
+ break;
+ case Sdigesting:
+ nb = digestb(s.s, nb, offset);
+ break;
+ case Sdigenc:
+ nb = digestb(s.s, nb, offset);
+ nb = encryptb(s.s, nb, offset);
+ break;
+ }
+
+ s.s->out.mid++;
+
+ m = BLEN(nb);
+ devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset);
+ s.s->c->offset += m;
+ }
+ qunlock(&s.s->out.q);
+ poperror();
+
+ return rv;
+}
+
+static void
+setsecret(OneWay *w, uchar *secret, int n)
+{
+ free(w->secret);
+ w->secret = mallocz(n, 0);
+ if(w->secret == nil)
+ error(Enomem);
+ memmove(w->secret, secret, n);
+ w->slen = n;
+}
+
+static void
+initIDEAkey(OneWay *w)
+{
+
+ free(w->state);
+ w->state = malloc(sizeof(IDEAstate));
+ if(w->state == nil)
+ error(Enomem);
+ if(w->slen >= 24)
+ setupIDEAstate(w->state, w->secret, w->secret+16);
+ else if(w->slen >= 16)
+ setupIDEAstate(w->state, w->secret, 0);
+ else
+ error("secret too short");
+}
+
+static void
+initDESkey(OneWay *w)
+{
+
+ free(w->state);
+ w->state = malloc(sizeof(DESstate));
+ if (!w->state)
+ error(Enomem);
+ if(w->slen >= 16)
+ setupDESstate(w->state, w->secret, w->secret+8);
+ else if(w->slen >= 8)
+ setupDESstate(w->state, w->secret, 0);
+ else
+ error("secret too short");
+}
+
+/*
+ * 40 bit DES is the same as 56 bit DES. However,
+ * 16 bits of the key are masked to zero.
+ */
+static void
+initDESkey_40(OneWay *w)
+{
+ uchar key[8];
+
+
+ if(w->slen >= 8) {
+ memmove(key, w->secret, 8);
+ key[0] &= 0x0f;
+ key[2] &= 0x0f;
+ key[4] &= 0x0f;
+ key[6] &= 0x0f;
+ }
+
+ free(w->state);
+ w->state = malloc(sizeof(DESstate));
+ if (!w->state)
+ error(Enomem);
+ if(w->slen >= 16)
+ setupDESstate(w->state, key, w->secret+8);
+ else if(w->slen >= 8)
+ setupDESstate(w->state, key, 0);
+ else
+ error("secret too short");
+}
+
+static void
+initRC4key(OneWay *w)
+{
+ free(w->state);
+ w->state = malloc(sizeof(RC4state));
+ if (!w->state)
+ error(Enomem);
+ setupRC4state(w->state, w->secret, w->slen);
+}
+
+/*
+ * 40 bit RC4 is the same as n-bit RC4. However,
+ * we ignore all but the first 40 bits of the key.
+ */
+static void
+initRC4key_40(OneWay *w)
+{
+ int slen = w->slen;
+
+ if(slen > 5)
+ slen = 5;
+
+ free(w->state);
+ w->state = malloc(sizeof(RC4state));
+ if (!w->state)
+ error(Enomem);
+ setupRC4state(w->state, w->secret, slen);
+}
+
+/*
+ * 128 bit RC4 is the same as n-bit RC4. However,
+ * we ignore all but the first 128 bits of the key.
+ */
+static void
+initRC4key_128(OneWay *w)
+{
+ int slen = w->slen;
+
+ if(slen > 16)
+ slen = 16;
+
+ free(w->state);
+ w->state = malloc(sizeof(RC4state));
+ if (!w->state)
+ error(Enomem);
+ setupRC4state(w->state, w->secret, slen);
+}
+
+typedef struct Hashalg Hashalg;
+struct Hashalg
+{
+ char *name;
+ int diglen;
+ DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);
+};
+
+Hashalg hashtab[] =
+{
+ { "md4", MD4dlen, md4, },
+ { "md5", MD5dlen, md5, },
+ { "sha1", SHA1dlen, sha1, },
+ { "sha", SHA1dlen, sha1, },
+ { 0 }
+};
+
+static int
+parsehashalg(char *p, Dstate *s)
+{
+ Hashalg *ha;
+
+ for(ha = hashtab; ha->name; ha++){
+ if(strcmp(p, ha->name) == 0){
+ s->hf = ha->hf;
+ s->diglen = ha->diglen;
+ s->state &= ~Sclear;
+ s->state |= Sdigesting;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+typedef struct Encalg Encalg;
+struct Encalg
+{
+ char *name;
+ int blocklen;
+ int alg;
+ void (*keyinit)(OneWay*);
+};
+
+Encalg encrypttab[] =
+{
+ { "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */
+ { "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */
+ { "des_56_cbc", 8, DESCBC, initDESkey, },
+ { "des_56_ecb", 8, DESECB, initDESkey, },
+ { "des_40_cbc", 8, DESCBC, initDESkey_40, },
+ { "des_40_ecb", 8, DESECB, initDESkey_40, },
+ { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */
+ { "rc4_256", 1, RC4, initRC4key, },
+ { "rc4_128", 1, RC4, initRC4key_128, },
+ { "rc4_40", 1, RC4, initRC4key_40, },
+ { "ideacbc", 8, IDEACBC, initIDEAkey, },
+ { "ideaecb", 8, IDEAECB, initIDEAkey, },
+ { 0 }
+};
+
+static int
+parseencryptalg(char *p, Dstate *s)
+{
+ Encalg *ea;
+
+ for(ea = encrypttab; ea->name; ea++){
+ if(strcmp(p, ea->name) == 0){
+ s->encryptalg = ea->alg;
+ s->blocklen = ea->blocklen;
+ (*ea->keyinit)(&s->in);
+ (*ea->keyinit)(&s->out);
+ s->state &= ~Sclear;
+ s->state |= Sencrypting;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void
+alglistinit(void)
+{
+ Hashalg *h;
+ Encalg *e;
+ int n;
+
+ n = 1;
+ for(e = encrypttab; e->name != nil; e++)
+ n += strlen(e->name) + 1;
+ encalgs = malloc(n);
+ if(encalgs == nil)
+ panic("sslinit");
+ n = 0;
+ for(e = encrypttab; e->name != nil; e++){
+ strcpy(encalgs+n, e->name);
+ n += strlen(e->name);
+ if(e[1].name == nil)
+ break;
+ encalgs[n++] = ' ';
+ }
+ encalgs[n] = 0;
+
+ n = 1;
+ for(h = hashtab; h->name != nil; h++)
+ n += strlen(h->name) + 1;
+ hashalgs = malloc(n);
+ if(hashalgs == nil)
+ panic("sslinit");
+ n = 0;
+ for(h = hashtab; h->name != nil; h++){
+ strcpy(hashalgs+n, h->name);
+ n += strlen(h->name);
+ if(h[1].name == nil)
+ break;
+ hashalgs[n++] = ' ';
+ }
+ hashalgs[n] = 0;
+}
+
+static long
+sslwrite(Chan *c, void *a, long n, vlong offset)
+{
+ volatile struct { Dstate *s; } s;
+ volatile struct { Block *b; } b;
+ int m, t;
+ char *p, *np, *e, buf[32];
+ uchar *x;
+
+ s.s = dstate[CONV(c->qid)];
+ if(s.s == 0)
+ panic("sslwrite");
+
+ t = TYPE(c->qid);
+ if(t == Qdata){
+ if(s.s->state == Sincomplete)
+ error(Ebadusefd);
+
+ p = a;
+ e = p + n;
+ do {
+ m = e - p;
+ if(m > s.s->max)
+ m = s.s->max;
+
+ b.b = allocb(m);
+ memmove(b.b->wp, p, m);
+ b.b->wp += m;
+
+ sslbwrite(c, b.b, offset);
+
+ p += m;
+ } while(p < e);
+ return n;
+ }
+
+ /* mutex with operations using what we're about to change */
+ if(waserror()){
+ qunlock(&s.s->in.ctlq);
+ qunlock(&s.s->out.q);
+ nexterror();
+ }
+ qlock(&s.s->in.ctlq);
+ qlock(&s.s->out.q);
+
+ switch(t){
+ default:
+ panic("sslwrite");
+ case Qsecretin:
+ setsecret(&s.s->in, a, n);
+ goto out;
+ case Qsecretout:
+ setsecret(&s.s->out, a, n);
+ goto out;
+ case Qctl:
+ break;
+ }
+
+ if(n >= sizeof(buf))
+ error(Ebadarg);
+ strncpy(buf, a, n);
+ buf[n] = 0;
+ p = strchr(buf, '\n');
+ if(p)
+ *p = 0;
+ p = strchr(buf, ' ');
+ if(p)
+ *p++ = 0;
+
+ if(strcmp(buf, "fd") == 0){
+ s.s->c = buftochan(p);
+
+ /* default is clear (msg delimiters only) */
+ s.s->state = Sclear;
+ s.s->blocklen = 1;
+ s.s->diglen = 0;
+ s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+ s.s->in.mid = 0;
+ s.s->out.mid = 0;
+ } else if(strcmp(buf, "alg") == 0 && p != 0){
+ s.s->blocklen = 1;
+ s.s->diglen = 0;
+
+ if(s.s->c == 0)
+ error("must set fd before algorithm");
+
+ if(strcmp(p, "clear") == 0){
+ s.s->state = Sclear;
+ s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+ goto out;
+ }
+
+ if(s.s->in.secret && s.s->out.secret == 0)
+ setsecret(&s.s->out, s.s->in.secret, s.s->in.slen);
+ if(s.s->out.secret && s.s->in.secret == 0)
+ setsecret(&s.s->in, s.s->out.secret, s.s->out.slen);
+ if(s.s->in.secret == 0 || s.s->out.secret == 0)
+ error("algorithm but no secret");
+
+ s.s->hf = 0;
+ s.s->encryptalg = Noencryption;
+ s.s->blocklen = 1;
+
+ for(;;){
+ np = strchr(p, ' ');
+ if(np)
+ *np++ = 0;
+ else{
+ np = strchr(p, '/');
+ if(np)
+ *np++ = 0;
+ }
+ if(parsehashalg(p, s.s) < 0)
+ if(parseencryptalg(p, s.s) < 0)
+ error(Ebadarg);
+
+ if(np == 0)
+ break;
+ p = np;
+ }
+
+ if(s.s->hf == 0 && s.s->encryptalg == Noencryption)
+ error(Ebadarg);
+
+ if(s.s->blocklen != 1){
+ /* make multiple of blocklen */
+ s.s->max = (1<<15) - s.s->diglen - 1;
+ s.s->max -= s.s->max % s.s->blocklen;
+ s.s->maxpad = (1<<14) - s.s->diglen - 1;
+ s.s->maxpad -= s.s->maxpad % s.s->blocklen;
+ } else
+ s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+ } else if(strcmp(buf, "secretin") == 0 && p != 0) {
+ m = (strlen(p)*3)/2;
+ x = smalloc(m);
+ if(waserror()){
+ free(x);
+ nexterror();
+ }
+ t = dec64(x, m, p, strlen(p));
+ setsecret(&s.s->in, x, t);
+ poperror();
+ free(x);
+ } else if(strcmp(buf, "secretout") == 0 && p != 0) {
+ m = (strlen(p)*3)/2;
+ x = smalloc(m);
+ if(waserror()){
+ free(x);
+ nexterror();
+ }
+ t = dec64(x, m, p, strlen(p));
+ setsecret(&s.s->out, x, t);
+ poperror();
+ free(x);
+ } else
+ error(Ebadarg);
+
+out:
+ qunlock(&s.s->in.ctlq);
+ qunlock(&s.s->out.q);
+ poperror();
+ return n;
+}
+
+
+static Block*
+encryptb(Dstate *s, Block *b, int offset)
+{
+ uchar *p, *ep, *p2, *ip, *eip;
+ DESstate *ds;
+ IDEAstate *is;
+
+ switch(s->encryptalg){
+ case DESECB:
+ ds = s->out.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp + offset; p < ep; p += 8)
+ block_cipher(ds->expanded, p, 0);
+ break;
+ case DESCBC:
+ ds = s->out.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp + offset; p < ep; p += 8){
+ p2 = p;
+ ip = ds->ivec;
+ for(eip = ip+8; ip < eip; )
+ *p2++ ^= *ip++;
+ block_cipher(ds->expanded, p, 0);
+ memmove(ds->ivec, p, 8);
+ }
+ break;
+ case IDEAECB:
+ is = s->out.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp + offset; p < ep; p += 8)
+ idea_cipher(is->edkey, p, 0);
+ break;
+ case IDEACBC:
+ is = s->out.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp + offset; p < ep; p += 8){
+ p2 = p;
+ ip = is->ivec;
+ for(eip = ip+8; ip < eip; )
+ *p2++ ^= *ip++;
+ idea_cipher(is->edkey, p, 0);
+ memmove(is->ivec, p, 8);
+ }
+ break;
+ case RC4:
+ rc4(s->out.state, b->rp + offset, BLEN(b) - offset);
+ break;
+ }
+ return b;
+}
+
+static Block*
+decryptb(Dstate *s, Block *inb)
+{
+ Block *b, **l;
+ uchar *p, *ep, *tp, *ip, *eip;
+ DESstate *ds;
+ IDEAstate *is;
+ uchar tmp[8];
+ int i;
+
+ l = &inb;
+ for(b = inb; b; b = b->next){
+ /* make sure we have a multiple of s->blocklen */
+ if(s->blocklen > 1){
+ i = BLEN(b);
+ if(i % s->blocklen){
+ *l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen));
+ if(b == 0)
+ error("ssl encrypted message too short");
+ }
+ }
+ l = &b->next;
+
+ /* decrypt */
+ switch(s->encryptalg){
+ case DESECB:
+ ds = s->in.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp; p < ep; p += 8)
+ block_cipher(ds->expanded, p, 1);
+ break;
+ case DESCBC:
+ ds = s->in.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp; p < ep;){
+ memmove(tmp, p, 8);
+ block_cipher(ds->expanded, p, 1);
+ tp = tmp;
+ ip = ds->ivec;
+ for(eip = ip+8; ip < eip; ){
+ *p++ ^= *ip;
+ *ip++ = *tp++;
+ }
+ }
+ break;
+ case IDEAECB:
+ is = s->in.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp; p < ep; p += 8)
+ idea_cipher(is->edkey, p, 1);
+ break;
+ case IDEACBC:
+ is = s->in.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp; p < ep;){
+ memmove(tmp, p, 8);
+ idea_cipher(is->edkey, p, 1);
+ tp = tmp;
+ ip = is->ivec;
+ for(eip = ip+8; ip < eip; ){
+ *p++ ^= *ip;
+ *ip++ = *tp++;
+ }
+ }
+ break;
+ case RC4:
+ rc4(s->in.state, b->rp, BLEN(b));
+ break;
+ }
+ }
+ return inb;
+}
+
+static Block*
+digestb(Dstate *s, Block *b, int offset)
+{
+ uchar *p;
+ DigestState ss;
+ uchar msgid[4];
+ ulong n, h;
+ OneWay *w;
+
+ w = &s->out;
+
+ memset(&ss, 0, sizeof(ss));
+ h = s->diglen + offset;
+ n = BLEN(b) - h;
+
+ /* hash secret + message */
+ (*s->hf)(w->secret, w->slen, 0, &ss);
+ (*s->hf)(b->rp + h, n, 0, &ss);
+
+ /* hash message id */
+ p = msgid;
+ n = w->mid;
+ *p++ = n>>24;
+ *p++ = n>>16;
+ *p++ = n>>8;
+ *p = n;
+ (*s->hf)(msgid, 4, b->rp + offset, &ss);
+
+ return b;
+}
+
+static void
+checkdigestb(Dstate *s, Block *inb)
+{
+ uchar *p;
+ DigestState ss;
+ uchar msgid[4];
+ int n, h;
+ OneWay *w;
+ uchar digest[128];
+ Block *b;
+
+ w = &s->in;
+
+ memset(&ss, 0, sizeof(ss));
+
+ /* hash secret */
+ (*s->hf)(w->secret, w->slen, 0, &ss);
+
+ /* hash message */
+ h = s->diglen;
+ for(b = inb; b; b = b->next){
+ n = BLEN(b) - h;
+ if(n < 0)
+ panic("checkdigestb");
+ (*s->hf)(b->rp + h, n, 0, &ss);
+ h = 0;
+ }
+
+ /* hash message id */
+ p = msgid;
+ n = w->mid;
+ *p++ = n>>24;
+ *p++ = n>>16;
+ *p++ = n>>8;
+ *p = n;
+ (*s->hf)(msgid, 4, digest, &ss);
+
+ /* requires pullupblock */
+ if(memcmp(digest, inb->rp, s->diglen) != 0)
+ error("bad digest");
+}
+
+/* get channel associated with an fd */
+static Chan*
+buftochan(char *p)
+{
+ Chan *c;
+ int fd;
+
+ if(p == 0)
+ error(Ebadarg);
+ fd = strtoul(p, 0, 0);
+ if(fd < 0)
+ error(Ebadarg);
+ c = fdtochan(up->env->fgrp, fd, -1, 0, 1); /* error check and inc ref */
+ return c;
+}
+
+/* hang up a digest connection */
+static void
+sslhangup(Dstate *s)
+{
+ qlock(&s->in.q);
+ freeblist(s->processed);
+ s->processed = 0;
+ freeblist(s->unprocessed);
+ s->unprocessed = 0;
+ s->state = Sincomplete;
+ qunlock(&s->in.q);
+}
+
+static void
+dsclone(Chan *ch)
+{
+ Dstate **pp, **ep, **np;
+ int newmax;
+
+ lock(&dslock);
+ if(waserror()) {
+ unlock(&dslock);
+ nexterror();
+ }
+ ep = &dstate[maxdstate];
+ for(pp = dstate; pp < ep; pp++) {
+ if(*pp == 0) {
+ dsnew(ch, pp);
+ break;
+ }
+ }
+ if(pp >= ep) {
+ if(maxdstate >= Maxdstate)
+ error(Enodev);
+ newmax = 2 * maxdstate;
+ if(newmax > Maxdstate)
+ newmax = Maxdstate;
+
+ np = realloc(dstate, sizeof(Dstate*) * newmax);
+ if(np == 0)
+ error(Enomem);
+ dstate = np;
+ pp = &dstate[maxdstate];
+ memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate));
+
+ maxdstate = newmax;
+ dsnew(ch, pp);
+ }
+ poperror();
+ unlock(&dslock);
+}
+
+static void
+dsnew(Chan *ch, Dstate **pp)
+{
+ Dstate *s;
+ int t;
+
+ *pp = s = mallocz(sizeof(*s), 1);
+ if(s == nil)
+ error(Enomem);
+ if(pp - dstate >= dshiwat)
+ dshiwat++;
+ s->state = Sincomplete;
+ s->ref = 1;
+ kstrdup(&s->user, up->env->user);
+ s->perm = 0660;
+ t = TYPE(ch->qid);
+ if(t == Qclonus)
+ t = Qctl;
+ ch->qid.path = QID(pp - dstate, t);
+ ch->qid.vers = 0;
+ ch->qid.type = QTFILE;
+}
+
+Dev ssldevtab = {
+ 'D',
+ "ssl",
+
+ sslinit,
+ sslattach,
+ sslwalk,
+ sslstat,
+ sslopen,
+ devcreate,
+ sslclose,
+ sslread,
+ sslbread,
+ sslwrite,
+ sslbwrite,
+ devremove,
+ sslwstat
+};
--- /dev/null
+++ b/emu/port/devtab.c
@@ -1,0 +1,35 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+extern Dev* devtab[];
+
+long
+devtabread(Chan *c, void* buf, long n, vlong off)
+{
+ int i;
+ Dev *dev;
+ char *alloc, *e, *p;
+
+ USED(c);
+ alloc = malloc(READSTR);
+ if(alloc == nil)
+ error(Enomem);
+
+ p = alloc;
+ e = p + READSTR;
+ for(i = 0; devtab[i] != nil; i++){
+ dev = devtab[i];
+ p = seprint(p, e, "#%C %s\n", dev->dc, dev->name);
+ }
+
+ if(waserror()){
+ free(alloc);
+ nexterror();
+ }
+ n = readstr(off, buf, n, alloc);
+ free(alloc);
+ poperror();
+
+ return n;
+}
--- /dev/null
+++ b/emu/port/devtinyfs.c
@@ -1,0 +1,897 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+
+enum{
+ Qdir,
+ Qmedium,
+
+ Maxfs= 10, /* max file systems */
+
+ Blen= 48, /* block length */
+ Nlen= 28, /* name length */
+ Dlen= Blen - 4,
+
+ Tagdir= 'd',
+ Tagdata= 'D',
+ Tagend= 'e',
+ Tagfree= 'f',
+
+ Notapin= 0xffff,
+ Notabno= 0xffff,
+
+ Fcreating= 1,
+ Frmonclose= 2
+};
+
+/* representation of a Tdir on medium */
+typedef struct Mdir Mdir;
+struct Mdir {
+ uchar type;
+ uchar bno[2];
+ uchar pin[2];
+ char name[Nlen];
+ char pad[Blen - Nlen - 6];
+ uchar sum;
+};
+
+/* representation of a Tdata/Tend on medium */
+typedef struct Mdata Mdata;
+struct Mdata {
+ uchar type;
+ uchar bno[2];
+ uchar data[Dlen];
+ uchar sum;
+};
+
+typedef struct Tfile Tfile;
+struct Tfile {
+ int r;
+ char name[Nlen];
+ ushort bno;
+ ushort dbno;
+ ushort pin;
+ uchar flag;
+ ulong length;
+
+ /* hint to avoid egregious reading */
+ ushort fbno;
+ ulong finger;
+};
+
+typedef struct Tfs Tfs;
+struct Tfs {
+ QLock ql;
+ int r;
+ Chan *c;
+ uchar *map;
+ int nblocks;
+ Tfile *f;
+ int nf;
+ int fsize;
+};
+
+static struct {
+ Tfs fs[Maxfs];
+} tinyfs;
+
+#define GETS(x) ((x)[0]|((x)[1]<<8))
+#define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);}
+
+#define GETL(x) (GETS(x)|(GETS(x+2)<<16))
+#define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)};
+
+static uchar
+checksum(uchar *p)
+{
+ uchar *e;
+ uchar s;
+
+ s = 0;
+ for(e = p + Blen; p < e; p++)
+ s += *p;
+ return s;
+}
+
+static void
+mapclr(Tfs *fs, ulong bno)
+{
+ fs->map[bno>>3] &= ~(1<<(bno&7));
+}
+
+static void
+mapset(Tfs *fs, ulong bno)
+{
+ fs->map[bno>>3] |= 1<<(bno&7);
+}
+
+static int
+isalloced(Tfs *fs, ulong bno)
+{
+ return fs->map[bno>>3] & (1<<(bno&7));
+}
+
+static int
+mapalloc(Tfs *fs)
+{
+ int i, j, lim;
+ uchar x;
+
+ lim = (fs->nblocks + 8 - 1)/8;
+ for(i = 0; i < lim; i++){
+ x = fs->map[i];
+ if(x == 0xff)
+ continue;
+ for(j = 0; j < 8; j++)
+ if((x & (1<<j)) == 0){
+ fs->map[i] = x|(1<<j);
+ return i*8 + j;
+ }
+ }
+
+ return Notabno;
+}
+
+static Mdir*
+validdir(Tfs *fs, uchar *p)
+{
+ Mdir *md;
+ ulong x;
+
+ if(checksum(p) != 0)
+ return 0;
+ if(p[0] != Tagdir)
+ return 0;
+ md = (Mdir*)p;
+ x = GETS(md->bno);
+ if(x >= fs->nblocks)
+ return 0;
+ return md;
+}
+
+static Mdata*
+validdata(Tfs *fs, uchar *p, int *lenp)
+{
+ Mdata *md;
+ ulong x;
+
+ if(checksum(p) != 0)
+ return 0;
+ md = (Mdata*)p;
+ switch(md->type){
+ case Tagdata:
+ x = GETS(md->bno);
+ if(x >= fs->nblocks)
+ return 0;
+ if(lenp)
+ *lenp = Dlen;
+ break;
+ case Tagend:
+ x = GETS(md->bno);
+ if(x > Dlen)
+ return 0;
+ if(lenp)
+ *lenp = x;
+ break;
+ default:
+ return 0;
+ }
+ return md;
+}
+
+static Mdata*
+readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp)
+{
+ if(bno >= fs->nblocks)
+ return 0;
+ if(fs->c->dev->read(fs->c, buf, Blen, Blen*bno) != Blen)
+ error(Eio);
+ return validdata(fs, buf, lenp);
+}
+
+static void
+writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last)
+{
+ Mdata md;
+
+ if(bno >= fs->nblocks)
+ error(Eio);
+ if(len > Dlen)
+ len = Dlen;
+ if(len < 0)
+ error(Eio);
+ memset(&md, 0, sizeof(md));
+ if(last){
+ md.type = Tagend;
+ PUTS(md.bno, len);
+ } else {
+ md.type = Tagdata;
+ PUTS(md.bno, next);
+ }
+ memmove(md.data, buf, len);
+ md.sum = 0 - checksum((uchar*)&md);
+
+ if(fs->c->dev->write(fs->c, &md, Blen, Blen*bno) != Blen)
+ error(Eio);
+}
+
+static void
+writedir(Tfs *fs, Tfile *f)
+{
+ Mdir *md;
+ uchar buf[Blen];
+
+ if(f->bno == Notabno)
+ return;
+
+ md = (Mdir*)buf;
+ memset(buf, 0, Blen);
+ md->type = Tagdir;
+ strncpy(md->name, f->name, sizeof(md->name)-1);
+ PUTS(md->bno, f->dbno);
+ PUTS(md->pin, f->pin);
+ md->sum = 0 - checksum(buf);
+
+ if(fs->c->dev->write(fs->c, buf, Blen, Blen*f->bno) != Blen)
+ error(Eio);
+}
+
+static void
+freeblocks(Tfs *fs, ulong bno, ulong bend)
+{
+ uchar buf[Blen];
+ Mdata *md;
+
+ if(waserror())
+ return;
+
+ while(bno != bend && bno != Notabno){
+ mapclr(fs, bno);
+ if(fs->c->dev->read(fs->c, buf, Blen, Blen*bno) != Blen)
+ break;
+ md = validdata(fs, buf, 0);
+ if(md == 0)
+ break;
+ if(md->type == Tagend)
+ break;
+ bno = GETS(md->bno);
+ }
+
+ poperror();
+}
+
+static void
+freefile(Tfs *fs, Tfile *f, ulong bend)
+{
+ uchar buf[Blen];
+
+ /* remove blocks from map */
+ freeblocks(fs, f->dbno, bend);
+
+ /* change file type to free on medium */
+ if(f->bno != Notabno){
+ memset(buf, 0x55, Blen);
+ fs->c->dev->write(fs->c, buf, Blen, Blen*f->bno);
+ mapclr(fs, f->bno);
+ }
+
+ /* forget we ever knew about it */
+ memset(f, 0, sizeof(*f));
+}
+
+static void
+expand(Tfs *fs)
+{
+ Tfile *f;
+
+ fs->fsize += 8;
+ f = malloc(fs->fsize*sizeof(*f));
+ if(f == nil)
+ error(Enomem);
+
+ if(fs->f){
+ memmove(f, fs->f, fs->nf*sizeof(*f));
+ free(fs->f);
+ }
+ fs->f = f;
+}
+
+static Tfile*
+newfile(Tfs *fs, char *name)
+{
+ int i;
+ volatile struct {
+ Tfile *f;
+ Tfs *fs;
+ } rock;
+
+ /* find free entry in file table */
+ rock.f = 0;
+ rock.fs = fs;
+ for(;;) {
+ for(i = 0; i < rock.fs->fsize; i++){
+ rock.f = &rock.fs->f[i];
+ if(rock.f->name[0] == 0){
+ strncpy(rock.f->name, name, sizeof(rock.f->name)-1);
+ break;
+ }
+ }
+
+ if(i < rock.fs->fsize){
+ if(i >= rock.fs->nf)
+ rock.fs->nf = i+1;
+ break;
+ }
+
+ expand(rock.fs);
+ }
+
+ rock.f->flag = Fcreating;
+ rock.f->dbno = Notabno;
+ rock.f->bno = mapalloc(rock.fs);
+ rock.f->fbno = Notabno;
+ rock.f->r = 1;
+ rock.f->pin = Notapin;
+
+ /* write directory block */
+ if(waserror()){
+ freefile(rock.fs, rock.f, Notabno);
+ nexterror();
+ }
+ if(rock.f->bno == Notabno)
+ error("out of space");
+ writedir(rock.fs, rock.f);
+ poperror();
+
+ return rock.f;
+}
+
+/*
+ * Read the whole medium and build a file table and used
+ * block bitmap. Inconsistent files are purged. The medium
+ * had better be small or this could take a while.
+ */
+static void
+tfsinit(Tfs *fs)
+{
+ uchar dbuf[STATFIXLEN+32*4];
+ Dir d;
+ uchar buf[Blen];
+ ulong x, bno;
+ int n, done;
+ Tfile *f;
+ Mdir *mdir;
+ Mdata *mdata;
+
+ n = fs->c->dev->stat(fs->c, dbuf, sizeof(dbuf));
+ n = convM2D(dbuf, n, &d, nil);
+ if(n <= 0)
+ error("cannot stat tinyfs medium");
+ fs->nblocks = d.length/Blen;
+ if(fs->nblocks < 3)
+ error("tinyfs medium too small");
+
+ /* bitmap for block usage */
+ x = (fs->nblocks + 8 - 1)/8;
+ fs->map = malloc(x);
+ if(fs->map == nil)
+ error(Enomem);
+ for(bno = fs->nblocks; bno < x*8; bno++)
+ mapset(fs, bno);
+
+ /* find files */
+ for(bno = 0; bno < fs->nblocks; bno++){
+ n = fs->c->dev->read(fs->c, buf, Blen, Blen*bno);
+ if(n != Blen)
+ break;
+
+ mdir = validdir(fs, buf);
+ if(mdir == 0)
+ continue;
+
+ if(fs->nf >= fs->fsize)
+ expand(fs);
+
+ f = &fs->f[fs->nf++];
+
+ x = GETS(mdir->bno);
+ mapset(fs, bno);
+ strncpy(f->name, mdir->name, sizeof(f->name));
+ f->pin = GETS(mdir->pin);
+ f->bno = bno;
+ f->dbno = x;
+ f->fbno = Notabno;
+ }
+
+ /* follow files */
+ for(f = fs->f; f < &(fs->f[fs->nf]); f++){
+ bno = f->dbno;
+ for(done = 0; !done;) {
+ if(isalloced(fs, bno)){
+ freefile(fs, f, bno);
+ break;
+ }
+ n = fs->c->dev->read(fs->c, buf, Blen, Blen*bno);
+ if(n != Blen){
+ freefile(fs, f, bno);
+ break;
+ }
+ mdata = validdata(fs, buf, 0);
+ if(mdata == 0){
+ freefile(fs, f, bno);
+ break;
+ }
+ mapset(fs, bno);
+ switch(mdata->type){
+ case Tagdata:
+ bno = GETS(mdata->bno);
+ f->length += Dlen;
+ break;
+ case Tagend:
+ f->length += GETS(mdata->bno);
+ done = 1;
+ break;
+ }
+ if(done)
+ f->flag &= ~Fcreating;
+ }
+ }
+}
+
+/*
+ * single directory
+ */
+static int
+tinyfsgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+ Tfs *fs;
+ Tfile *f;
+ Qid qid;
+
+ USED(name);
+ USED(ntab);
+ USED(tab);
+
+ fs = &tinyfs.fs[c->devno];
+ if(i >= fs->nf)
+ return -1;
+ qid.vers = 0;
+ if(i == DEVDOTDOT){
+ qid.type = QTDIR;
+ devdir(c, qid, ".", 0, eve, DMDIR|0555, dp);
+ return 1;
+ }
+ f = &fs->f[i];
+ if(f->name[0] == 0)
+ return 0;
+ qid.path = i+1;
+ qid.type = QTFILE;
+ devdir(c, qid, f->name, f->length, eve, 0664, dp);
+ return 1;
+}
+
+/*
+ * specifier is the name of a device in /dev
+ */
+static Chan *
+tinyfsattach(char *spec)
+{
+ Tfs *fs;
+ Chan *c;
+ volatile struct { Chan *cc; } rock;
+ int i;
+ char buf[KNAMELEN*3];
+
+ if(*spec == 0 || strchr(spec, '/') != nil)
+ error("bad specifier");
+
+ snprint(buf, sizeof(buf), "/dev/%s", spec);
+ rock.cc = namec(buf, Aopen, ORDWR, 0);
+ if(waserror()){
+ cclose(rock.cc);
+ nexterror();
+ }
+
+ fs = 0;
+ for(i = 0; i < Maxfs; i++){
+ fs = &tinyfs.fs[i];
+ qlock(&fs->ql);
+ if(fs->r && eqchan(rock.cc, fs->c, 1))
+ break;
+ qunlock(&fs->ql);
+ }
+ if(i < Maxfs){
+ fs->r++;
+ qunlock(&fs->ql);
+ cclose(rock.cc);
+ } else {
+ for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){
+ qlock(&fs->ql);
+ if(fs->r == 0)
+ break;
+ qunlock(&fs->ql);
+ }
+ if(fs == &tinyfs.fs[Maxfs])
+ error("too many tinyfs's");
+ fs->c = rock.cc;
+ fs->r = 1;
+ fs->f = 0;
+ fs->nf = 0;
+ fs->fsize = 0;
+ tfsinit(fs);
+ qunlock(&fs->ql);
+ }
+ poperror();
+
+ c = devattach('F', spec);
+ c->devno = fs - tinyfs.fs;
+ c->qid.type = QTDIR;
+ c->qid.vers = 0;
+
+ return c;
+}
+
+static Walkqid*
+tinyfswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ Tfs *fs;
+ Walkqid *wq;
+
+ fs = &tinyfs.fs[c->devno];
+
+ qlock(&fs->ql);
+ wq = devwalk(c, nc, name, nname, 0, 0, tinyfsgen);
+ if(wq != nil && (nc = wq->clone) != nil && nc->qid.path != Qdir){
+ fs = &tinyfs.fs[nc->devno];
+ fs->f[nc->qid.path-1].r++;
+ }
+ qunlock(&fs->ql);
+ return wq;
+}
+
+static int
+tinyfsstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, tinyfsgen);
+}
+
+static Chan *
+tinyfsopen(Chan *c, int omode)
+{
+ Tfile *f;
+ volatile struct { Tfs *fs; } rock;
+
+ rock.fs = &tinyfs.fs[c->devno];
+
+ if(c->qid.type & QTDIR){
+ if(omode != OREAD)
+ error(Eperm);
+ } else {
+ qlock(&rock.fs->ql);
+ if(waserror()){
+ qunlock(&rock.fs->ql);
+ nexterror();
+ }
+ switch(omode){
+ case OTRUNC|ORDWR:
+ case OTRUNC|OWRITE:
+ f = newfile(rock.fs, rock.fs->f[c->qid.path-1].name);
+ rock.fs->f[c->qid.path-1].r--;
+ c->qid.path = f - rock.fs->f;
+ break;
+ case OREAD:
+ break;
+ default:
+ error(Eperm);
+ }
+ qunlock(&rock.fs->ql);
+ poperror();
+ }
+
+ return devopen(c, omode, 0, 0, tinyfsgen);
+}
+
+static void
+tinyfscreate(Chan *c, char *name, int omode, ulong perm)
+{
+ volatile struct { Tfs *fs; } rock;
+ Tfile *f;
+
+ USED(perm);
+
+ rock.fs = &tinyfs.fs[c->devno];
+
+ qlock(&rock.fs->ql);
+ if(waserror()){
+ qunlock(&rock.fs->ql);
+ nexterror();
+ }
+ f = newfile(rock.fs, name);
+ qunlock(&rock.fs->ql);
+ poperror();
+
+ c->qid.path = (f - rock.fs->f)+1;
+ c->qid.vers = 0;
+ c->qid.type = QTFILE;
+ c->mode = openmode(omode);
+}
+
+static void
+tinyfsremove(Chan *c)
+{
+ Tfs *fs;
+ Tfile *f;
+
+ if(c->qid.path == Qdir)
+ error(Eperm);
+ fs = &tinyfs.fs[c->devno];
+ f = &fs->f[c->qid.path-1];
+ qlock(&fs->ql);
+ freefile(fs, f, Notabno);
+ qunlock(&fs->ql);
+}
+
+static void
+tinyfsclose(Chan *c)
+{
+ volatile struct { Tfs *fs; } rock;
+ Tfile *f, *nf;
+ int i;
+
+ rock.fs = &tinyfs.fs[c->devno];
+
+ qlock(&rock.fs->ql);
+
+ /* dereference file and remove old versions */
+ if(!waserror()){
+ if(c->qid.path != Qdir){
+ f = &rock.fs->f[c->qid.path-1];
+ f->r--;
+ if(f->r == 0){
+ if(f->flag & Frmonclose)
+ freefile(rock.fs, f, Notabno);
+ else if(f->flag & Fcreating){
+ /* remove all other files with this name */
+ for(i = 0; i < rock.fs->fsize; i++){
+ nf = &rock.fs->f[i];
+ if(f == nf)
+ continue;
+ if(strcmp(nf->name, f->name) == 0){
+ if(nf->r)
+ nf->flag |= Frmonclose;
+ else
+ freefile(rock.fs, nf, Notabno);
+ }
+ }
+ f->flag &= ~Fcreating;
+ }
+ }
+ }
+ poperror();
+ }
+
+ /* dereference rock.fs and remove on zero refs */
+ rock.fs->r--;
+ if(rock.fs->r == 0){
+ if(rock.fs->f)
+ free(rock.fs->f);
+ rock.fs->f = 0;
+ rock.fs->nf = 0;
+ rock.fs->fsize = 0;
+ if(rock.fs->map)
+ free(rock.fs->map);
+ rock.fs->map = 0;
+ cclose(rock.fs->c);
+ rock.fs->c = 0;
+ }
+ qunlock(&rock.fs->ql);
+}
+
+static long
+tinyfsread(Chan *c, void *a, long n, vlong offset)
+{
+ volatile struct { Tfs *fs; } rock;
+ Tfile *f;
+ int sofar, i, off;
+ ulong bno;
+ Mdata *md;
+ uchar buf[Blen];
+ uchar *p;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, tinyfsgen);
+
+ p = a;
+ rock.fs = &tinyfs.fs[c->devno];
+ f = &rock.fs->f[c->qid.path-1];
+ if(offset >= f->length)
+ return 0;
+
+ qlock(&rock.fs->ql);
+ if(waserror()){
+ qunlock(&rock.fs->ql);
+ nexterror();
+ }
+ if(n + offset >= f->length)
+ n = f->length - offset;
+
+ /* walk to starting data block */
+ if(0 && f->finger <= offset && f->fbno != Notabno){
+ sofar = f->finger;
+ bno = f->fbno;
+ } else {
+ sofar = 0;
+ bno = f->dbno;
+ }
+ for(; sofar + Dlen <= offset; sofar += Dlen){
+ md = readdata(rock.fs, bno, buf, 0);
+ if(md == 0)
+ error(Eio);
+ bno = GETS(md->bno);
+ }
+
+ /* read data */
+ off = offset%Dlen;
+ offset -= off;
+ for(sofar = 0; sofar < n; sofar += i){
+ md = readdata(rock.fs, bno, buf, &i);
+ if(md == 0)
+ error(Eio);
+
+ /* update finger for successful read */
+ f->finger = offset;
+ f->fbno = bno;
+ offset += Dlen;
+
+ i -= off;
+ if(i > n - sofar)
+ i = n - sofar;
+ memmove(p, md->data+off, i);
+ p += i;
+ bno = GETS(md->bno);
+ off = 0;
+ }
+ qunlock(&rock.fs->ql);
+ poperror();
+
+ return sofar;
+}
+
+/*
+ * if we get a write error in this routine, blocks will
+ * be lost. They should be recovered next fsinit.
+ */
+static long
+tinyfswrite(Chan *c, void *a, long n, vlong offset)
+{
+ Tfile *f;
+ int last, next, i, finger, off, used;
+ ulong bno, fbno;
+ Mdata *md;
+ uchar buf[Blen];
+ uchar *p;
+ volatile struct {
+ Tfs *fs;
+ ulong dbno;
+ } rock;
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ if(n == 0)
+ return 0;
+
+ p = a;
+ rock.fs = &tinyfs.fs[c->devno];
+ f = &rock.fs->f[c->qid.path-1];
+
+ qlock(&rock.fs->ql);
+ rock.dbno = Notabno;
+ if(waserror()){
+ freeblocks(rock.fs, rock.dbno, Notabno);
+ qunlock(&rock.fs->ql);
+ nexterror();
+ }
+
+ /* files are append only, anything else is illegal */
+ if(offset != f->length)
+ error("append only");
+
+ /* write blocks backwards */
+ p += n;
+ last = offset + n;
+ fbno = Notabno;
+ finger = 0;
+ off = offset; /* so we have something signed to compare against */
+ for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){
+ bno = mapalloc(rock.fs);
+ if(bno == Notabno)
+ error("out of space");
+ i = last - next;
+ p -= i;
+ if(last == n+offset){
+ writedata(rock.fs, bno, rock.dbno, p, i, 1);
+ finger = next; /* remember for later */
+ fbno = bno;
+ } else {
+ writedata(rock.fs, bno, rock.dbno, p, i, 0);
+ }
+ rock.dbno = bno;
+ last = next;
+ }
+
+ /* walk to last data block */
+ md = (Mdata*)buf;
+ if(0 && f->finger < offset && f->fbno != Notabno){
+ next = f->finger;
+ bno = f->fbno;
+ } else {
+ next = 0;
+ bno = f->dbno;
+ }
+
+ used = 0;
+ while(bno != Notabno){
+ md = readdata(rock.fs, bno, buf, &used);
+ if(md == 0)
+ error(Eio);
+ if(md->type == Tagend){
+ if(next + Dlen < offset)
+ panic("devtinyfs1");
+ break;
+ }
+ next += Dlen;
+ if(next > offset)
+ panic("devtinyfs1");
+ bno = GETS(md->bno);
+ }
+
+ /* point to new blocks */
+ if(offset == 0){
+ /* first block in a file */
+ f->dbno = rock.dbno;
+ writedir(rock.fs, f);
+ } else {
+ /* updating a current block */
+ i = last - offset;
+ if(i > 0){
+ p -= i;
+ memmove(md->data + used, p, i);
+ used += i;
+ }
+ writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset);
+ }
+ f->length += n;
+
+ /* update finger */
+ if(fbno != Notabno){
+ f->finger = finger;
+ f->fbno = fbno;
+ }
+ poperror();
+ qunlock(&rock.fs->ql);
+
+ return n;
+}
+
+Dev tinyfsdevtab = {
+ 'F',
+ "tinyfs",
+
+ devreset,
+ devinit,
+ devshutdown,
+ tinyfsattach,
+ tinyfswalk,
+ tinyfsstat,
+ tinyfsopen,
+ tinyfscreate,
+ tinyfsclose,
+ tinyfsread,
+ devbread,
+ tinyfswrite,
+ devbwrite,
+ tinyfsremove,
+ devwstat
+};
--- /dev/null
+++ b/emu/port/devtk.c
@@ -1,0 +1,185 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <interp.h>
+
+#include "draw.h"
+#include <memdraw.h>
+#include <memlayer.h>
+#include <cursor.h>
+
+enum{
+ Qdir,
+ Qtkevents
+};
+
+static
+Dirtab tkdirtab[]=
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "tkevents", {Qtkevents, 0}, 0, 0600,
+};
+
+static struct {
+ QLock l;
+ Queue* eq;
+ Ref inuse;
+} tkevents;
+
+static void
+tkwiretapper(void *top, char *cmd, char *result, void *image, Rectangle *rp)
+{
+ Block *b;
+ int n;
+ char *s, *e;
+
+//fprint(2, "wiretap %p %q %q\n", top, cmd, result);
+ if(tkevents.eq == nil)
+ return;
+ n = 12;
+ if(cmd != nil)
+ n += strlen(cmd)+2+1;
+ if(result != nil)
+ n += strlen(result)+2+1;
+ if(image != nil)
+ n += 12;
+ if(rp != nil)
+ n += 4*20;
+ n++;
+ b = allocb(n);
+ if(b != nil){
+ s = (char*)b->wp;
+ e = s+n;
+ s += snprint(s, e-s, "%p", top);
+ if(cmd != nil){
+ *s++ = ' ';
+ *s++ = '[';
+ n = strlen(cmd);
+ memmove(s, cmd, n);
+ s += n;
+ *s++ = ']';
+ }
+ /* ignore result for now */
+ if(image != nil)
+ s += snprint(s, e-s, " %p", image);
+ if(rp != nil)
+ s += snprint(s, e-s, " %d %d %d %d", rp->min.x, rp->min.y, rp->max.x, rp->max.y);
+ b->wp = (uchar*)s;
+ release();
+ qlock(&tkevents.l);
+ if(waserror()){
+ freeb(b);
+ qunlock(&tkevents.l);
+ acquire();
+ return;
+ }
+ if(tkevents.eq != nil)
+ qbwrite(tkevents.eq, b);
+ else
+ freeb(b);
+ poperror();
+ qunlock(&tkevents.l);
+ acquire();
+ }
+}
+
+void (*tkwiretap)(void*, char*, char*, void*, Rectangle*) = tkwiretapper;
+
+static Chan*
+tkattach(char* spec)
+{
+ return devattach(L'τ', spec);
+}
+
+static Walkqid*
+tkwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, tkdirtab, nelem(tkdirtab), devgen);
+}
+
+static int
+tkstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, tkdirtab, nelem(tkdirtab), devgen);
+}
+
+static Chan*
+tkopen(Chan* c, int omode)
+{
+ if(c->qid.type & QTDIR)
+ return devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen);
+ switch(c->qid.path){
+ case Qtkevents:
+ c = devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen);
+ qlock(&tkevents.l);
+ if(incref(&tkevents.inuse) != 1){
+ qunlock(&tkevents.l);
+ error(Einuse);
+ }
+ if(tkevents.eq == nil)
+ tkevents.eq = qopen(256*1024, 0, nil, nil);
+ else
+ qreopen(tkevents.eq);
+ qunlock(&tkevents.l);
+ break;
+ }
+ return c;
+}
+
+static void
+tkclose(Chan* c)
+{
+ if(c->qid.type & QTDIR || (c->flag & COPEN) == 0)
+ return;
+ qlock(&tkevents.l);
+ if(decref(&tkevents.inuse) == 0)
+ qclose(tkevents.eq);
+ qunlock(&tkevents.l);
+}
+
+static long
+tkread(Chan* c, void* a, long n, vlong offset)
+{
+ USED(offset);
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, tkdirtab, nelem(tkdirtab), devgen);
+
+ switch((ulong)c->qid.path){
+ case Qtkevents:
+ return qread(tkevents.eq, a, n);
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+tkwrite(Chan *c, void* a, long n, vlong offset)
+{
+ USED(c); USED(a); USED(n); USED(offset);
+ error(Ebadusefd);
+ return 0;
+}
+
+Dev tkdevtab = {
+ L'τ',
+ "tk",
+
+// devreset,
+ devinit,
+ tkattach,
+// devdetach,
+ tkwalk,
+ tkstat,
+ tkopen,
+ devcreate,
+ tkclose,
+ tkread,
+ devbread,
+ tkwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/emu/port/dial.c
@@ -1,0 +1,421 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "kernel.h"
+
+typedef struct DS DS;
+
+static int call(char*, char*, DS*);
+static int csdial(DS*);
+static void _dial_string_parse(char*, DS*);
+static int nettrans(char*, char*, int na, char*, int);
+
+enum
+{
+ Maxstring= 128,
+ Maxpath= 100
+};
+
+struct DS
+{
+ char buf[Maxstring]; /* dist string */
+ char *netdir;
+ char *proto;
+ char *rem;
+ char *local; /* other args */
+ char *dir;
+ int *cfdp;
+};
+
+/*
+ * the dialstring is of the form '[/net/]proto!dest'
+ */
+int
+kdial(char *dest, char *local, char *dir, int *cfdp)
+{
+ DS ds;
+ int rv;
+ char err[ERRMAX], alterr[ERRMAX];
+
+ ds.local = local;
+ ds.dir = dir;
+ ds.cfdp = cfdp;
+
+ _dial_string_parse(dest, &ds);
+ if(ds.netdir)
+ return csdial(&ds);
+
+ ds.netdir = "/net";
+ rv = csdial(&ds);
+ if(rv >= 0)
+ return rv;
+
+ err[0] = 0;
+ kerrstr(err, sizeof err);
+ if(strstr(err, "refused") != 0){
+ kerrstr(err, sizeof err);
+ return rv;
+ }
+
+ ds.netdir = "/net.alt";
+ rv = csdial(&ds);
+ if(rv >= 0)
+ return rv;
+
+ alterr[0] = 0;
+ kerrstr(alterr, sizeof err);
+
+ if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
+ kerrstr(err, sizeof err);
+ else
+ kerrstr(alterr, sizeof alterr);
+ return rv;
+}
+
+static int
+csdial(DS *ds)
+{
+ int n, fd, rv;
+ char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
+
+ /*
+ * open connection server
+ */
+ snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
+ fd = kopen(buf, ORDWR);
+ if(fd < 0){
+ /* no connection server, don't translate */
+ snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
+ return call(clone, ds->rem, ds);
+ }
+
+ /*
+ * ask connection server to translate
+ */
+ sprint(buf, "%s!%s", ds->proto, ds->rem);
+ if(kwrite(fd, buf, strlen(buf)) < 0){
+ kerrstr(err, sizeof err);
+ kclose(fd);
+ kwerrstr("%s (%s)", err, buf);
+ return -1;
+ }
+
+ /*
+ * loop through each address from the connection server till
+ * we get one that works.
+ */
+ *besterr = 0;
+ strcpy(err, Egreg);
+ rv = -1;
+ kseek(fd, 0, 0);
+ while((n = kread(fd, buf, sizeof(buf) - 1)) > 0){
+ buf[n] = 0;
+ p = strchr(buf, ' ');
+ if(p == 0)
+ continue;
+ *p++ = 0;
+ rv = call(buf, p, ds);
+ if(rv >= 0)
+ break;
+ err[0] = 0;
+ kerrstr(err, sizeof err);
+ if(strstr(err, "does not exist") == 0)
+ memmove(besterr, err, sizeof besterr);
+ }
+ kclose(fd);
+
+ if(rv < 0 && *besterr)
+ kerrstr(besterr, sizeof besterr);
+ else
+ kerrstr(err, sizeof err);
+ return rv;
+}
+
+static int
+call(char *clone, char *dest, DS *ds)
+{
+ int fd, cfd, n;
+ char name[Maxpath], data[Maxpath], err[ERRMAX], *p;
+
+ cfd = kopen(clone, ORDWR);
+ if(cfd < 0){
+ kerrstr(err, sizeof err);
+ kwerrstr("%s (%s)", err, clone);
+ return -1;
+ }
+
+ /* get directory name */
+ n = kread(cfd, name, sizeof(name)-1);
+ if(n < 0){
+ kerrstr(err, sizeof err);
+ kclose(cfd);
+ kwerrstr("read %s: %s", clone, err);
+ return -1;
+ }
+ name[n] = 0;
+ for(p = name; *p == ' '; p++)
+ ;
+ sprint(name, "%ld", strtoul(p, 0, 0));
+ p = strrchr(clone, '/');
+ *p = 0;
+ if(ds->dir)
+ snprint(ds->dir, NETPATHLEN, "%s/%s", clone, name);
+ snprint(data, sizeof(data), "%s/%s/data", clone, name);
+
+ /* connect */
+ if(ds->local)
+ snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
+ else
+ snprint(name, sizeof(name), "connect %s", dest);
+ if(kwrite(cfd, name, strlen(name)) < 0){
+ err[0] = 0;
+ kerrstr(err, sizeof err);
+ kclose(cfd);
+ kwerrstr("%s (%s)", err, name);
+ return -1;
+ }
+
+ /* open data connection */
+ fd = kopen(data, ORDWR);
+ if(fd < 0){
+ err[0] = 0;
+ kerrstr(err, sizeof err);
+ kwerrstr("%s (%s)", err, data);
+ kclose(cfd);
+ return -1;
+ }
+ if(ds->cfdp)
+ *ds->cfdp = cfd;
+ else
+ kclose(cfd);
+
+ return fd;
+}
+
+/*
+ * parse a dial string
+ */
+static void
+_dial_string_parse(char *str, DS *ds)
+{
+ char *p, *p2;
+
+ strncpy(ds->buf, str, Maxstring);
+ ds->buf[Maxstring-1] = 0;
+
+ p = strchr(ds->buf, '!');
+ if(p == 0) {
+ ds->netdir = 0;
+ ds->proto = "net";
+ ds->rem = ds->buf;
+ } else {
+ if(*ds->buf != '/' && *ds->buf != '#'){
+ ds->netdir = 0;
+ ds->proto = ds->buf;
+ } else {
+ for(p2 = p; *p2 != '/'; p2--)
+ ;
+ *p2++ = 0;
+ ds->netdir = ds->buf;
+ ds->proto = p2;
+ }
+ *p = 0;
+ ds->rem = p + 1;
+ }
+}
+
+/*
+ * announce a network service.
+ */
+int
+kannounce(char *addr, char *dir)
+{
+ int ctl, n, m;
+ char buf[NETPATHLEN];
+ char buf2[Maxpath];
+ char netdir[NETPATHLEN];
+ char naddr[Maxpath];
+ char *cp;
+
+ /*
+ * translate the address
+ */
+ if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
+ return -1;
+
+ /*
+ * get a control channel
+ */
+ ctl = kopen(netdir, ORDWR);
+ if(ctl<0)
+ return -1;
+ cp = strrchr(netdir, '/');
+ *cp = 0;
+
+ /*
+ * find out which line we have
+ */
+ n = sprint(buf, "%.*s/", sizeof buf, netdir);
+ m = kread(ctl, &buf[n], sizeof(buf)-n-1);
+ if(m <= 0){
+ kclose(ctl);
+ return -1;
+ }
+ buf[n+m] = 0;
+
+ /*
+ * make the call
+ */
+ n = snprint(buf2, sizeof buf2, "announce %s", naddr);
+ if(kwrite(ctl, buf2, n)!=n){
+ kclose(ctl);
+ return -1;
+ }
+
+ /*
+ * return directory etc.
+ */
+ if(dir)
+ strcpy(dir, buf);
+ return ctl;
+}
+
+/*
+ * listen for an incoming call
+ */
+int
+klisten(char *dir, char *newdir)
+{
+ int ctl, n, m;
+ char buf[NETPATHLEN];
+ char *cp;
+
+ /*
+ * open listen, wait for a call
+ */
+ snprint(buf, sizeof buf, "%s/listen", dir);
+ ctl = kopen(buf, ORDWR);
+ if(ctl < 0)
+ return -1;
+
+ /*
+ * find out which line we have
+ */
+ strcpy(buf, dir);
+ cp = strrchr(buf, '/');
+ *++cp = 0;
+ n = cp-buf;
+ m = kread(ctl, cp, sizeof(buf) - n - 1);
+ if(m <= 0){
+ kclose(ctl);
+ return -1;
+ }
+ buf[n+m] = 0;
+
+ /*
+ * return directory etc.
+ */
+ if(newdir)
+ strcpy(newdir, buf);
+ return ctl;
+
+}
+
+/*
+ * perform the identity translation (in case we can't reach cs)
+ */
+static int
+identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf)
+{
+ char proto[Maxpath];
+ char *p;
+
+ USED(nf);
+
+ /* parse the protocol */
+ strncpy(proto, addr, sizeof(proto));
+ proto[sizeof(proto)-1] = 0;
+ p = strchr(proto, '!');
+ if(p)
+ *p++ = 0;
+
+ snprint(file, nf, "%s/%s/clone", netdir, proto);
+ strncpy(naddr, p, na);
+ naddr[na-1] = 0;
+
+ return 1;
+}
+
+/*
+ * call up the connection server and get a translation
+ */
+static int
+nettrans(char *addr, char *naddr, int na, char *file, int nf)
+{
+ int i, fd;
+ char buf[Maxpath];
+ char netdir[NETPATHLEN];
+ char *p, *p2;
+ long n;
+
+ /*
+ * parse, get network directory
+ */
+ p = strchr(addr, '!');
+ if(p == 0){
+ kwerrstr("bad dial string: %s", addr);
+ return -1;
+ }
+ if(*addr != '/'){
+ strcpy(netdir, "/net");
+ } else {
+ for(p2 = p; *p2 != '/'; p2--)
+ ;
+ i = p2 - addr;
+ if(i == 0 || i >= sizeof(netdir)){
+ kwerrstr("bad dial string: %s", addr);
+ return -1;
+ }
+ strncpy(netdir, addr, i);
+ netdir[i] = 0;
+ addr = p2 + 1;
+ }
+
+ /*
+ * ask the connection server
+ */
+ sprint(buf, "%s/cs", netdir);
+ fd = kopen(buf, ORDWR);
+ if(fd < 0)
+ return identtrans(netdir, addr, naddr, na, file, nf);
+ if(kwrite(fd, addr, strlen(addr)) < 0){
+ kclose(fd);
+ return -1;
+ }
+ kseek(fd, 0, 0);
+ n = kread(fd, buf, sizeof(buf)-1);
+ kclose(fd);
+ if(n <= 0)
+ return -1;
+ buf[n] = 0;
+
+ /*
+ * parse the reply
+ */
+ p = strchr(buf, ' ');
+ if(p == 0)
+ return -1;
+ *p++ = 0;
+ strncpy(naddr, p, na);
+ naddr[na-1] = 0;
+
+ if(buf[0] == '/'){
+ p = strchr(buf+1, '/');
+ if(p == nil)
+ p = buf;
+ else
+ p++;
+ }
+ snprint(file, nf, "%s/%s", netdir, p);
+ return 0;
+}
--- /dev/null
+++ b/emu/port/dis.c
@@ -1,0 +1,1141 @@
+#include "dat.h"
+#include "fns.h"
+#include <isa.h>
+#include <interp.h>
+#include <kernel.h>
+#include "error.h"
+#include "raise.h"
+
+struct
+{
+ Lock l;
+ Prog* runhd;
+ Prog* runtl;
+ Prog* head;
+ Prog* tail;
+ Rendez irend;
+ int idle;
+ int nyield;
+ int creating;
+ Proc* vmq; /* queue of procs wanting vm */
+ Proc* vmqt;
+ Proc* idlevmq; /* queue of procs wanting work */
+ Atidle* idletasks;
+} isched;
+
+int bflag;
+int cflag;
+uvlong gcbusy;
+uvlong gcidle;
+uvlong gcidlepass;
+uvlong gcpartial;
+int keepbroken = 1;
+extern int vflag;
+static Prog* proghash[64];
+
+static Progs* delgrp(Prog*);
+static void addgrp(Prog*, Prog*);
+void printgrp(Prog*, char*);
+
+static Prog**
+pidlook(int pid)
+{
+ ulong h;
+ Prog **l;
+
+ h = (ulong)pid % nelem(proghash);
+ for(l = &proghash[h]; *l != nil && (*l)->pid != pid; l = &(*l)->pidlink)
+ ;
+ return l;
+}
+
+int
+tready(void *a)
+{
+ USED(a);
+ return isched.runhd != nil || isched.vmq != nil;
+}
+
+Prog*
+progpid(int pid)
+{
+ return *pidlook(pid);
+}
+
+Prog*
+progn(int n)
+{
+ Prog *p;
+
+ for(p = isched.head; p && n--; p = p->next)
+ ;
+ return p;
+}
+
+int
+nprog(void)
+{
+ int n;
+ Prog *p;
+
+ n = 0;
+ for(p = isched.head; p; p = p->next)
+ n++;
+ return n;
+}
+
+static void
+execatidle(void)
+{
+ int done;
+
+ if(tready(nil))
+ return;
+
+ gcidle++;
+ up->type = IdleGC;
+ up->iprog = nil;
+ addrun(up->prog);
+ done = gccolor+3;
+ while(gccolor < done && gcruns()) {
+ if(isched.vmq != nil || isched.runhd != isched.runtl) {
+ gcpartial++;
+ break;
+ }
+ rungc(isched.head);
+ gcidlepass++;
+ if(((ulong)gcidlepass&0xFF) == 0)
+ osyield();
+ }
+ up->type = Interp;
+ delrunq(up->prog);
+}
+
+Prog*
+newprog(Prog *p, Modlink *m)
+{
+ Heap *h;
+ Prog *n, **ph;
+ Osenv *on, *op;
+ static int pidnum;
+
+ if(p != nil){
+ if(p->group != nil)
+ p->flags |= p->group->flags & Pkilled;
+ if(p->kill != nil)
+ error(p->kill);
+ if(p->flags & Pkilled)
+ error("");
+ }
+ n = malloc(sizeof(Prog)+sizeof(Osenv));
+ if(n == 0){
+ if(p == nil)
+ panic("no memory");
+ else
+ error(exNomem);
+ }
+
+ n->pid = ++pidnum;
+ if(n->pid <= 0)
+ panic("no pids");
+ n->group = nil;
+
+ if(isched.tail != nil) {
+ n->prev = isched.tail;
+ isched.tail->next = n;
+ }
+ else {
+ isched.head = n;
+ n->prev = nil;
+ }
+ isched.tail = n;
+
+ ph = pidlook(n->pid);
+ if(*ph != nil)
+ panic("dup pid");
+ n->pidlink = nil;
+ *ph = n;
+
+ n->osenv = (Osenv*)((uchar*)n + sizeof(Prog));
+ n->xec = xec;
+ n->quanta = PQUANTA;
+ n->flags = 0;
+ n->exval = H;
+
+ h = D2H(m);
+ h->ref++;
+ Setmark(h);
+ n->R.M = m;
+ n->R.MP = m->MP;
+ if(m->MP != H)
+ Setmark(D2H(m->MP));
+ addrun(n);
+
+ if(p == nil){
+ newgrp(n);
+ return n;
+ }
+
+ addgrp(n, p);
+ n->flags = p->flags;
+ if(p->flags & Prestrict)
+ n->flags |= Prestricted;
+ memmove(n->osenv, p->osenv, sizeof(Osenv));
+ op = p->osenv;
+ on = n->osenv;
+ on->waitq = op->childq;
+ on->childq = nil;
+ on->debug = nil;
+ incref(&on->pgrp->r);
+ incref(&on->fgrp->r);
+ incref(&on->egrp->r);
+ if(on->sigs != nil)
+ incref(&on->sigs->r);
+ on->user = nil;
+ kstrdup(&on->user, op->user);
+ on->errstr = on->errbuf0;
+ on->syserrstr = on->errbuf1;
+
+ return n;
+}
+
+void
+delprog(Prog *p, char *msg)
+{
+ Osenv *o;
+ Prog **ph;
+
+ tellsomeone(p, msg); /* call before being removed from prog list */
+
+ o = p->osenv;
+ release();
+ closepgrp(o->pgrp);
+ closefgrp(o->fgrp);
+ closeegrp(o->egrp);
+ closesigs(o->sigs);
+ acquire();
+
+ delgrp(p);
+
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ isched.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ isched.tail = p->prev;
+
+ ph = pidlook(p->pid);
+ if(*ph == nil)
+ panic("lost pid");
+ *ph = p->pidlink;
+
+ if(p == isched.runhd) {
+ isched.runhd = p->link;
+ if(p->link == nil)
+ isched.runtl = nil;
+ }
+ p->state = 0xdeadbeef;
+ free(o->user);
+ free(p->killstr);
+ free(p->exstr);
+ free(p);
+}
+
+void
+renameproguser(char *old, char *new)
+{
+ Prog *p;
+ Osenv *o;
+
+ acquire();
+ for(p = isched.head; p; p = p->next){
+ o = p->osenv;
+ if(o->user != nil && strcmp(o->user, old) == 0)
+ kstrdup(&o->user, new);
+ }
+ release();
+}
+
+void
+tellsomeone(Prog *p, char *buf)
+{
+ Osenv *o;
+
+ if(waserror())
+ return;
+ o = p->osenv;
+ if(o->childq != nil)
+ qproduce(o->childq, buf, strlen(buf));
+ if(o->waitq != nil)
+ qproduce(o->waitq, buf, strlen(buf));
+ poperror();
+}
+
+static void
+swiprog(Prog *p)
+{
+ Proc *q;
+
+ lock(&procs.l);
+ for(q = procs.head; q; q = q->next) {
+ if(q->iprog == p) {
+ unlock(&procs.l);
+ swiproc(q, 1);
+ return;
+ }
+ }
+ unlock(&procs.l);
+ /*print("didn't find\n");*/
+}
+
+static Prog*
+grpleader(Prog *p)
+{
+ Progs *g;
+ Prog *l;
+
+ g = p->group;
+ if(g != nil && (l = g->head) != nil && l->pid == g->id)
+ return l;
+ return nil;
+}
+
+int
+exprog(Prog *p, char *exc)
+{
+ /* similar code to killprog but not quite */
+ switch(p->state) {
+ case Palt:
+ altdone(p->R.s, p, nil, -1);
+ break;
+ case Psend:
+ cqdelp(&p->chan->send, p);
+ break;
+ case Precv:
+ cqdelp(&p->chan->recv, p);
+ break;
+ case Pready:
+ break;
+ case Prelease:
+ swiprog(p);
+ break;
+ case Pexiting:
+ case Pbroken:
+ case Pdebug:
+ return 0;
+ default:
+ panic("exprog - bad state 0x%x\n", p->state);
+ }
+ if(p->state != Pready && p->state != Prelease)
+ addrun(p);
+ if(p->kill == nil){
+ if(p->killstr == nil){
+ p->killstr = malloc(ERRMAX);
+ if(p->killstr == nil){
+ p->kill = Enomem;
+ return 1;
+ }
+ }
+ kstrcpy(p->killstr, exc, ERRMAX);
+ p->kill = p->killstr;
+ }
+ return 1;
+}
+
+static void
+propex(Prog *p, char *estr)
+{
+ Prog *f, *nf, *pgl;
+
+ if(!(p->flags & (Ppropagate|Pnotifyleader)) || p->group == nil)
+ return;
+ if(*estr == 0){
+ if((p->flags & Pkilled) == 0)
+ return;
+ estr = "killed";
+ }
+ pgl = grpleader(p);
+ if(pgl == nil)
+ pgl = p;
+ if(!(pgl->flags & (Ppropagate|Pnotifyleader)))
+ return; /* exceptions are local; don't propagate */
+ for(f = p->group->head; f != nil; f = nf){
+ nf = f->grpnext;
+ if(f != p && f != pgl){
+ if(pgl->flags & Ppropagate)
+ exprog(f, estr);
+ else{
+ f->flags &= ~(Ppropagate|Pnotifyleader); /* prevent recursion */
+ killprog(f, "killed");
+ }
+ }
+ }
+ if(p != pgl)
+ exprog(pgl, estr);
+}
+
+int
+killprog(Prog *p, char *cause)
+{
+ Osenv *env;
+ char msg[ERRMAX+2*KNAMELEN];
+
+ if(p == isched.runhd) {
+ p->kill = "";
+ p->flags |= Pkilled;
+ p->state = Pexiting;
+ return 0;
+ }
+
+ switch(p->state) {
+ case Palt:
+ altdone(p->R.s, p, nil, -1);
+ break;
+ case Psend:
+ cqdelp(&p->chan->send, p);
+ break;
+ case Precv:
+ cqdelp(&p->chan->recv, p);
+ break;
+ case Pready:
+ delrunq(p);
+ break;
+ case Prelease:
+ p->kill = "";
+ p->flags |= Pkilled;
+ p->state = Pexiting;
+ swiprog(p);
+ /* No break */
+ case Pexiting:
+ return 0;
+ case Pbroken:
+ case Pdebug:
+ break;
+ default:
+ panic("killprog - bad state 0x%x\n", p->state);
+ }
+
+ if(p->addrun != nil) {
+ p->kill = "";
+ p->flags |= Pkilled;
+ p->addrun(p);
+ p->addrun = nil;
+ return 0;
+ }
+
+ env = p->osenv;
+ if(env->debug != nil) {
+ p->state = Pbroken;
+ dbgexit(p, 0, cause);
+ return 0;
+ }
+
+ propex(p, "killed");
+
+ snprint(msg, sizeof(msg), "%d \"%s\":%s", p->pid, p->R.M->m->name, cause);
+
+ p->state = Pexiting;
+ gclock();
+ destroystack(&p->R);
+ delprog(p, msg);
+ gcunlock();
+
+ return 1;
+}
+
+void
+newgrp(Prog *p)
+{
+ Progs *pg, *g;
+
+ if(p->group != nil && p->group->id == p->pid)
+ return;
+ g = malloc(sizeof(*g));
+ if(g == nil)
+ error(Enomem);
+ p->flags &= ~(Ppropagate|Pnotifyleader);
+ g->id = p->pid;
+ g->flags = 0;
+ if(p->group != nil)
+ g->flags |= p->group->flags&Pprivatemem;
+ g->child = nil;
+ pg = delgrp(p);
+ g->head = g->tail = p;
+ p->group = g;
+ if(pg != nil){
+ g->sib = pg->child;
+ pg->child = g;
+ }
+ g->parent = pg;
+}
+
+static void
+addgrp(Prog *n, Prog *p)
+{
+ Progs *g;
+
+ n->group = p->group;
+ if((g = n->group) != nil){
+ n->grpnext = nil;
+ if(g->head != nil){
+ n->grpprev = g->tail;
+ g->tail->grpnext = n;
+ }else{
+ n->grpprev = nil;
+ g->head = n;
+ }
+ g->tail = n;
+ }
+}
+
+static Progs*
+delgrp(Prog *p)
+{
+ Progs *g, *pg, *cg, **l;
+
+ g = p->group;
+ if(g == nil)
+ return nil;
+ if(p->grpprev)
+ p->grpprev->grpnext = p->grpnext;
+ else
+ g->head = p->grpnext;
+ if(p->grpnext)
+ p->grpnext->grpprev = p->grpprev;
+ else
+ g->tail = p->grpprev;
+ p->grpprev = p->grpnext = nil;
+ p->group = nil;
+
+ if(g->head == nil){
+ /* move up, giving subgroups of groups with no Progs to their parents */
+ do{
+ if((pg = g->parent) != nil){
+ pg = g->parent;
+ for(l = &pg->child; *l != nil && *l != g; l = &(*l)->sib)
+ ;
+ *l = g->sib;
+ }
+ /* put subgroups in new parent group */
+ while((cg = g->child) != nil){
+ g->child = cg->sib;
+ cg->parent = pg;
+ if(pg != nil){
+ cg->sib = pg->child;
+ pg->child = cg;
+ }
+ }
+ free(g);
+ }while((g = pg) != nil && g->head == nil);
+ }
+ return g;
+}
+
+void
+printgrp(Prog *p, char *v)
+{
+ Progs *g;
+ Prog *q;
+
+ g = p->group;
+ print("%s pid %d grp %d pgrp %d: [pid", v, p->pid, g->id, g->parent!=nil?g->parent->id:0);
+ for(q = g->head; q != nil; q = q->grpnext)
+ print(" %d", q->pid);
+ print(" subgrp");
+ for(g = g->child; g != nil; g = g->sib)
+ print(" %d", g->id);
+ print("]\n");
+}
+
+int
+killgrp(Prog *p, char *msg)
+{
+ int i, npid, *pids;
+ Prog *f;
+ Progs *g;
+
+ /* interpreter has been acquired */
+ g = p->group;
+ if(g == nil || g->head == nil)
+ return 0;
+ while(g->flags & Pkilled){
+ release();
+ acquire();
+ }
+ npid = 0;
+ for(f = g->head; f != nil; f = f->grpnext)
+ if(f->group != g)
+ panic("killgrp");
+ else
+ npid++;
+ /* use pids not Prog* because state can change during killprog (eg, in delprog) */
+ pids = malloc(npid*sizeof(int));
+ if(pids == nil)
+ error(Enomem);
+ npid = 0;
+ for(f = g->head; f != nil; f = f->grpnext)
+ pids[npid++] = f->pid;
+ g->flags |= Pkilled;
+ if(waserror()) {
+ g->flags &= ~Pkilled;
+ free(pids);
+ nexterror();
+ }
+ for(i = 0; i < npid; i++) {
+ f = progpid(pids[i]);
+ if(f != nil && f != currun())
+ killprog(f, msg);
+ }
+ poperror();
+ g->flags &= ~Pkilled;
+ free(pids);
+ return 1;
+}
+
+char changup[] = "channel hangup";
+
+void
+killcomm(Progq **q)
+{
+ Prog *p;
+ Progq *f;
+
+ for (f = *q; f != nil; f = *q) {
+ *q = f->next;
+ p = f->prog;
+ free(f);
+ if(p == nil)
+ return;
+ p->ptr = nil;
+ switch(p->state) {
+ case Prelease:
+ swiprog(p);
+ break;
+ case Psend:
+ case Precv:
+ p->kill = changup;
+ addrun(p);
+ break;
+ case Palt:
+ altgone(p);
+ break;
+ }
+ }
+}
+
+void
+addprog(Proc *p)
+{
+ Prog *n;
+
+ n = malloc(sizeof(Prog));
+ if(n == nil)
+ panic("no memory");
+ p->prog = n;
+ n->osenv = p->env;
+}
+
+static void
+cwakeme(Prog *p)
+{
+ Osenv *o;
+
+ p->addrun = nil;
+ o = p->osenv;
+ Wakeup(o->rend);
+}
+
+static int
+cdone(void *vp)
+{
+ Prog *p = vp;
+
+ return p->addrun == nil || p->kill != nil;
+}
+
+void
+cblock(Prog *p)
+{
+ Osenv *o;
+
+ p->addrun = cwakeme;
+ o = p->osenv;
+ o->rend = &up->sleep;
+ release();
+
+ /*
+ * To allow cdone(p) safely after release,
+ * p must be currun before the release.
+ * Exits in the error case with the vm acquired.
+ */
+ if(waserror()) {
+ acquire();
+ p->addrun = nil;
+ nexterror();
+ }
+ Sleep(o->rend, cdone, p);
+ if (p->kill != nil)
+ error(Eintr);
+ poperror();
+ acquire();
+}
+
+void
+addrun(Prog *p)
+{
+ if(p->addrun != 0) {
+ p->addrun(p);
+ return;
+ }
+ p->state = Pready;
+ p->link = nil;
+ if(isched.runhd == nil)
+ isched.runhd = p;
+ else
+ isched.runtl->link = p;
+
+ isched.runtl = p;
+}
+
+Prog*
+delrun(int state)
+{
+ Prog *p;
+
+ p = isched.runhd;
+ p->state = state;
+ isched.runhd = p->link;
+ if(p->link == nil)
+ isched.runtl = nil;
+
+ return p;
+}
+
+void
+delrunq(Prog *p)
+{
+ Prog *prev, *f;
+
+ prev = nil;
+ for(f = isched.runhd; f; f = f->link) {
+ if(f == p)
+ break;
+ prev = f;
+ }
+ if(f == nil)
+ return;
+ if(prev == nil)
+ isched.runhd = p->link;
+ else
+ prev->link = p->link;
+ if(p == isched.runtl)
+ isched.runtl = prev;
+}
+
+Prog*
+delruntail(int state)
+{
+ Prog *p;
+
+ p = isched.runtl;
+ delrunq(p);
+ p->state = state;
+ return p;
+}
+
+Prog*
+currun(void)
+{
+ return isched.runhd;
+}
+
+Prog*
+schedmod(Module *m)
+{
+ Heap *h;
+ Type *t;
+ Prog *p;
+ Modlink *ml;
+ Frame f, *fp;
+
+ ml = mklinkmod(m, 0);
+
+ if(m->origmp != H && m->ntype > 0) {
+ t = m->type[0];
+ h = nheap(t->size);
+ h->t = t;
+ t->ref++;
+ ml->MP = H2D(uchar*, h);
+ newmp(ml->MP, m->origmp, t);
+ }
+
+ p = newprog(nil, ml);
+ h = D2H(ml);
+ h->ref--;
+ p->R.PC = m->entry;
+ fp = &f;
+ R.s = &fp;
+ f.t = m->entryt;
+ newstack(p);
+ initmem(m->entryt, p->R.FP);
+
+ return p;
+}
+
+/*
+static char*
+m(Prog *p)
+{
+ if(p)
+ if(p->R.M)
+ if(p->R.M->m)
+ return p->R.M->m->name;
+ return "nil";
+}
+*/
+
+void
+acquire(void)
+{
+ int empty;
+ Prog *p;
+
+ lock(&isched.l);
+ if(isched.idle) {
+ isched.idle = 0;
+ unlock(&isched.l);
+ }
+ else {
+ up->qnext = nil;
+ if(isched.vmq != nil){
+ empty = 0;
+ isched.vmqt->qnext = up;
+ }else{
+ isched.vmq = up;
+ empty = 1;
+ }
+ isched.vmqt = up;
+
+ unlock(&isched.l);
+ strcpy(up->text, "acquire");
+ if(empty)
+ Wakeup(&isched.irend);
+ osblock();
+ }
+
+ if(up->type == Interp) {
+ p = up->iprog;
+ up->iprog = nil;
+ irestore(p);
+ }
+ else
+ p = up->prog;
+
+ p->state = Pready;
+ p->link = isched.runhd;
+ isched.runhd = p;
+ if(p->link == nil)
+ isched.runtl = p;
+
+ strcpy(up->text, "dis");
+}
+
+void
+release(void)
+{
+ Proc *p, **pq;
+ int f;
+
+ if(up->type == Interp)
+ up->iprog = isave();
+ else
+ delrun(Prelease);
+
+ lock(&isched.l);
+ if(*(pq = &isched.vmq) == nil && *(pq = &isched.idlevmq) == nil) {
+ isched.idle = 1;
+ f = isched.creating;
+ isched.creating = 1;
+ unlock(&isched.l);
+ if(f == 0)
+ kproc("dis", vmachine, nil, 0);
+ return;
+ }
+ p = *pq;
+ *pq = p->qnext;
+ unlock(&isched.l);
+
+ osready(p); /* wake up thread to run VM */
+ strcpy(up->text, "released");
+}
+
+void
+iyield(void)
+{
+ Proc *p;
+
+ lock(&isched.l);
+ p = isched.vmq;
+ if(p == nil) {
+ unlock(&isched.l);
+ return;
+ }
+ isched.nyield++;
+ isched.vmq = p->qnext;
+
+ if(up->iprog != nil)
+ panic("iyield but iprog, type %d", up->type);
+ if(up->type != Interp){
+ static int once;
+ if(!once++)
+ print("tell charles: #%p->type==%d\n", up, up->type);
+ }
+ up->qnext = isched.idlevmq;
+ isched.idlevmq = up;
+
+ unlock(&isched.l);
+ osready(p); /* wake up acquiring kproc */
+ strcpy(up->text, "yield");
+ osblock(); /* sleep */
+ strcpy(up->text, "dis");
+}
+
+void
+startup(void)
+{
+
+ up->type = Interp;
+ up->iprog = nil;
+
+ lock(&isched.l);
+ isched.creating = 0;
+ if(isched.idle) {
+ isched.idle = 0;
+ unlock(&isched.l);
+ return;
+ }
+ up->qnext = isched.idlevmq;
+ isched.idlevmq = up;
+ unlock(&isched.l);
+
+ osblock();
+}
+
+void
+progexit(void)
+{
+ Prog *r;
+ Module *m;
+ int broken;
+ char *estr, msg[ERRMAX+2*KNAMELEN];
+
+ estr = up->env->errstr;
+ broken = 0;
+ if(estr[0] != '\0' && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0)
+ broken = 1;
+
+ r = up->iprog;
+ if(r != nil)
+ acquire();
+ else
+ r = currun();
+
+ if(*estr == '\0' && r->flags & Pkilled)
+ estr = "killed";
+
+ m = R.M->m;
+ if(broken){
+ if(cflag){ /* only works on Plan9 for now */
+ char *pc = strstr(estr, "pc=");
+
+ if(pc != nil)
+ R.PC = r->R.PC = (Inst*)strtol(pc+3, nil, 0); /* for debugging */
+ }
+ print("[%s] Broken: \"%s\"\n", m->name, estr);
+ }
+
+ snprint(msg, sizeof(msg), "%d \"%s\":%s", r->pid, m->name, estr);
+
+ if(up->env->debug != nil) {
+ dbgexit(r, broken, estr);
+ broken = 1;
+ /* must force it to break if in debug */
+ }else if(broken && (!keepbroken || strncmp(estr, "out of memory", 13)==0 || memusehigh()))
+ broken = 0; /* don't want them or short of memory */
+
+ if(broken){
+ tellsomeone(r, msg);
+ r = isave();
+ r->state = Pbroken;
+ return;
+ }
+
+ gclock();
+ destroystack(&R);
+ delprog(r, msg);
+ gcunlock();
+
+ if(isched.head == nil)
+ cleanexit(0);
+}
+
+void
+disfault(void *reg, char *msg)
+{
+ Prog *p;
+
+ USED(reg);
+
+ if(strncmp(msg, Eintr, 6) == 0)
+ exits(0);
+
+ if(up == nil) {
+ print("EMU: faults: %s\n", msg);
+ cleanexit(0);
+ }
+ if(up->type != Interp) {
+ print("SYS: process %s faults: %s\n", up->text, msg);
+ cleanexit(0);
+ }
+
+ if(up->iprog != nil)
+ acquire();
+
+ p = currun();
+ if(p == nil)
+ panic("Interp faults with no dis prog");
+
+ /* cause an exception in the dis prog. As for error(), but Plan 9 needs reg*/
+ kstrcpy(up->env->errstr, msg, ERRMAX);
+ oslongjmp(reg, up->estack[--up->nerr], 1);
+}
+
+void
+vmachine(void *a)
+{
+ Prog *r;
+ Osenv *o;
+ int cycles;
+ static int gccounter;
+
+ USED(a);
+
+ startup();
+
+ while(waserror()) {
+ if(up->iprog != nil)
+ acquire();
+ if(handler(up->env->errstr) == 0) {
+ propex(currun(), up->env->errstr);
+ progexit();
+ }
+ up->env = &up->defenv;
+ }
+
+ cycles = 0;
+ for(;;) {
+ if(tready(nil) == 0) {
+ execatidle();
+ strcpy(up->text, "idle");
+ Sleep(&isched.irend, tready, 0);
+ strcpy(up->text, "dis");
+ }
+
+ if(isched.vmq != nil && (isched.runhd == nil || ++cycles > 2)){
+ iyield();
+ cycles = 0;
+ }
+
+ r = isched.runhd;
+ if(r != nil) {
+ o = r->osenv;
+ up->env = o;
+
+ FPrestore(&o->fpu);
+ r->xec(r);
+ FPsave(&o->fpu);
+
+ if(isched.runhd != nil)
+ if(r == isched.runhd)
+ if(isched.runhd != isched.runtl) {
+ isched.runhd = r->link;
+ r->link = nil;
+ isched.runtl->link = r;
+ isched.runtl = r;
+ }
+ up->env = &up->defenv;
+ }
+ if(isched.runhd != nil)
+ if((++gccounter&0xFF) == 0 || memlow()) {
+ gcbusy++;
+ up->type = BusyGC;
+ pushrun(up->prog);
+ rungc(isched.head);
+ up->type = Interp;
+ delrunq(up->prog);
+ }
+ }
+}
+
+void
+disinit(void *a)
+{
+ Prog *p;
+ Osenv *o;
+ Module *root;
+ char *initmod = a;
+
+ if(waserror())
+ panic("disinit error: %r");
+
+ if(vflag)
+ print("Initial Dis: \"%s\"\n", initmod);
+
+ fmtinstall('D', Dconv);
+
+ FPinit();
+ FPsave(&up->env->fpu);
+
+ opinit();
+ modinit();
+ excinit();
+
+ root = load(initmod);
+ if(root == 0) {
+ kgerrstr(up->genbuf, sizeof up->genbuf);
+ panic("loading \"%s\": %s", initmod, up->genbuf);
+ }
+
+ p = schedmod(root);
+
+ memmove(p->osenv, up->env, sizeof(Osenv));
+ o = p->osenv;
+ incref(&o->pgrp->r);
+ incref(&o->fgrp->r);
+ incref(&o->egrp->r);
+ if(o->sigs != nil)
+ incref(&o->sigs->r);
+ o->user = nil;
+ kstrdup(&o->user, up->env->user);
+ o->errstr = o->errbuf0;
+ o->syserrstr = o->errbuf1;
+
+ isched.idle = 1;
+ poperror();
+ vmachine(nil);
+}
+
+void
+pushrun(Prog *p)
+{
+ if(p->addrun != nil)
+ panic("pushrun addrun");
+ p->state = Pready;
+ p->link = isched.runhd;
+ isched.runhd = p;
+ if(p->link == nil)
+ isched.runtl = p;
+}
--- /dev/null
+++ b/emu/port/discall.c
@@ -1,0 +1,179 @@
+#include "dat.h"
+#include "fns.h"
+#include <interp.h>
+
+#define QP(l) (Prog**)((char*)(l)+sizeof(QLock))
+
+void*
+libqlowner(void *l)
+{
+ return *QP(l);
+}
+
+void
+libqlock(void *l)
+{
+ Prog *p;
+ QLock *q;
+
+ q = l;
+ p = currun();
+ if(p == nil)
+ abort();
+
+ if(!canqlock(q)) {
+ release();
+ qlock(q);
+ acquire();
+ }
+ *QP(l) = p;
+}
+
+void
+libqunlock(void *l)
+{
+ Prog *p;
+ QLock *q;
+
+ q = l;
+ p = currun();
+ if(p == nil)
+ abort();
+ if(*QP(l) != p)
+ abort();
+
+ *QP(l) = nil;
+ qunlock(q);
+}
+
+void*
+libqlalloc(void)
+{
+ QLock *q;
+
+ q = mallocz(sizeof(QLock)+sizeof(Prog*), 1);
+ return q;
+}
+
+void
+libqlfree(void *l)
+{
+ free(l);
+}
+
+vlong
+libseek(int fd, vlong off, int whence)
+{
+ release();
+ off = kseek(fd, off, whence);
+ acquire();
+ return off;
+}
+
+int
+libread(int fd, void *buf, int n)
+{
+ release();
+ n = kread(fd, buf, n);
+ acquire();
+ return n;
+}
+
+int
+libreadn(int fd, void *av, long n)
+{
+ char *a;
+ long m, t;
+
+ a = av;
+ t = 0;
+ release();
+ while(t < n){
+ m = kread(fd, a+t, n-t);
+ if(m <= 0){
+ if(t == 0){
+ acquire();
+ return m;
+ }
+ break;
+ }
+ t += m;
+ }
+ acquire();
+ return t;
+}
+
+int
+libwrite(int fd, void *buf, int n)
+{
+ release();
+ n = kwrite(fd, buf, n);
+ acquire();
+ return n;
+}
+
+int
+libopen(char *name, int omode)
+{
+ int fd;
+
+ release();
+ fd = kopen(name, omode);
+ acquire();
+ return fd;
+}
+
+int
+libclose(int fd)
+{
+ release();
+ fd = kclose(fd);
+ acquire();
+ return fd;
+}
+
+Dir*
+libdirfstat(int fd)
+{
+ Dir *d;
+
+ release();
+ d = kdirfstat(fd);
+ acquire();
+ return d;
+}
+
+int
+libbind(char *s, char *t, int f)
+{
+ int n;
+
+ release();
+ n = kbind(s, t, f);
+ acquire();
+ return n;
+}
+
+void
+libchanclose(void *chan)
+{
+ release();
+ cclose(chan);
+ acquire();
+}
+
+void*
+libfdtochan(int fd, int mode)
+{
+ Chan *c;
+
+ release();
+ if(waserror()) {
+ acquire();
+ return nil;
+ }
+ c = fdtochan(up->env->fgrp, fd, mode, 0, 1);
+ poperror();
+ acquire();
+ return c;
+}
--- /dev/null
+++ b/emu/port/dynld.c
@@ -1,0 +1,52 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <a.out.h>
+#include <dynld.h>
+
+/*
+ * kernel interface to dynld, for use by devdynld.c,
+ * libinterp/dlm.c, and possibly others
+ */
+
+typedef struct Fd Fd;
+struct Fd {
+ int fd;
+};
+
+static long
+readfd(void *a, void *buf, long nbytes)
+{
+ return kread(((Fd*)a)->fd, buf, nbytes);
+}
+
+static vlong
+seekfd(void *a, vlong off, int t)
+{
+ return kseek(((Fd*)a)->fd, off, t);
+}
+
+static void
+errfd(char *s)
+{
+ kstrcpy(up->env->errstr, s, ERRMAX);
+}
+
+Dynobj*
+kdynloadfd(int fd, Dynsym *tab, int ntab)
+{
+ Dynobj *o;
+ Fd f;
+
+ f.fd = fd;
+ return dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0);
+}
+
+int
+kdynloadable(int fd)
+{
+ Fd f;
+
+ f.fd = fd;
+ return dynloadable(&f, readfd, seekfd);
+}
--- /dev/null
+++ b/emu/port/dynldc.c
@@ -1,0 +1,65 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <a.out.h>
+#include <dynld.h>
+
+/*
+ * channel-based kernel interface to dynld, for use by devdynld.c,
+ * libinterp/dlm.c, and possibly others
+ */
+
+static long
+readfc(void *a, void *buf, long nbytes)
+{
+ Chan *c = a;
+
+ if(waserror())
+ return -1;
+ nbytes = devtab[c->type]->read(c, buf, nbytes, c->offset);
+ poperror();
+ return nbytes;
+}
+
+static vlong
+seekfc(void *a, vlong off, int t)
+{
+ Chan *c = a;
+
+ if(c->qid.type & QTDIR || off < 0)
+ return -1; /* won't happen */
+ switch(t){
+ case 0:
+ lock(c);
+ c->offset = off;
+ unlock(c);
+ break;
+ case 1:
+ lock(c);
+ off += c->offset;
+ c->offset = off;
+ unlock(c);
+ break;
+ case 2:
+ return -1; /* not needed */
+ }
+ return off;
+}
+
+static void
+errfc(char *s)
+{
+ kstrcpy(up->env->errstr, s, ERRMAX);
+}
+
+Dynobj*
+kdynloadchan(Chan *c, Dynsym *tab, int ntab)
+{
+ return dynloadgen(c, readfc, seekfc, errfc, tab, ntab, 0);
+}
+
+int
+kdynloadable(Chan *c)
+{
+ return dynloadable(c, readfc, seekfc);
+}
--- /dev/null
+++ b/emu/port/env.c
@@ -1,0 +1,77 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+Egrp*
+newegrp(void)
+{
+ Egrp *e;
+
+ e = smalloc(sizeof(Egrp));
+ if (e == nil)
+ error(Enomem);
+ e->r.ref = 1;
+ return e;
+}
+
+void
+closeegrp(Egrp *e)
+{
+ Evalue *el, *nl;
+
+ if(e == nil || decref(&e->r) != 0)
+ return;
+ for (el = e->entries; el != nil; el = nl) {
+ free(el->var);
+ if (el->val)
+ free(el->val);
+ nl = el->next;
+ free(el);
+ }
+ free(e);
+}
+
+void
+egrpcpy(Egrp *to, Egrp *from)
+{
+ Evalue *e, *ne, **last;
+
+ last = &to->entries;
+ qlock(&from->l);
+ for (e = from->entries; e != nil; e = e->next) {
+ ne = smalloc(sizeof(Evalue));
+ ne->var = smalloc(strlen(e->var)+1);
+ strcpy(ne->var, e->var);
+ if (e->val) {
+ ne->val = smalloc(e->len);
+ memmove(ne->val, e->val, e->len);
+ ne->len = e->len;
+ }
+ ne->qid.path = ++to->path;
+ *last = ne;
+ last = &ne->next;
+ }
+ qunlock(&from->l);
+}
+
+void
+ksetenv(char *var, char *val, int conf)
+{
+ Chan *c;
+ char buf[2*KNAMELEN];
+
+ USED(conf);
+ snprint(buf, sizeof(buf), "#e/%s", var);
+ if(waserror())
+ return;
+ c = namec(buf, Acreate, OWRITE, 0600);
+ poperror();
+ if(!waserror()){
+ if(!waserror()){
+ devtab[c->type]->write(c, val, strlen(val), 0);
+ poperror();
+ }
+ poperror();
+ }
+ cclose(c);
+}
--- /dev/null
+++ b/emu/port/error.c
@@ -1,0 +1,66 @@
+char Enoerror[] = "no error";
+char Emount[] = "inconsistent mount";
+char Eunmount[] = "not mounted";
+char Eunion[] = "not in union";
+char Emountrpc[] = "mount rpc error";
+char Eshutdown[] = "mounted device shut down";
+char Eowner[] = "not owner";
+char Eunknown[] = "unknown user or group id";
+char Enocreate[] = "mounted directory forbids creation";
+char Enonexist[] = "file does not exist";
+char Eexist[] = "file already exists";
+char Ebadsharp[] = "unknown device in # filename";
+char Enotdir[] = "not a directory";
+char Eisdir[] = "file is a directory";
+char Ebadchar[] = "bad character in file name";
+char Efilename[] = "file name syntax";
+char Eperm[] = "permission denied";
+char Ebadusefd[] = "inappropriate use of fd";
+char Ebadarg[] = "bad arg in system call";
+char Einuse[] = "device or object already in use";
+char Eio[] = "i/o error";
+char Etoobig[] = "read or write too large";
+char Etoosmall[] = "read or write too small";
+char Enetaddr[] = "bad network address";
+char Emsgsize[] = "message is too big for protocol";
+char Enetbusy[] = "network device is busy or allocated";
+char Enoproto[] = "network protocol not supported";
+char Enoport[] = "network port not available";
+char Enoifc[] = "bad interface or no free interface slots";
+char Enolisten[] = "not announced";
+char Ehungup[] = "i/o on hungup channel";
+char Ebadctl[] = "bad process or channel control request";
+char Enodev[] = "no free devices";
+char Enoenv[] = "no free environment resources";
+char Emuxshutdown[] = "mux server shut down";
+char Emuxbusy[] = "all mux channels busy";
+char Emuxmsg[] = "bad mux message format or mismatch";
+char Ethread[] = "thread exited";
+char Enochild[] = "no living children";
+char Eioload[] = "i/o error in demand load";
+char Enovmem[] = "out of memory: virtual memory";
+char Ebadld[] = "illegal line discipline";
+char Ebadfd[] = "fd out of range or not open";
+char Eisstream[] = "seek on a stream";
+char Ebadexec[] = "exec header invalid";
+char Etimedout[] = "connection timed out";
+char Econrefused[] = "connection refused";
+char Econinuse[] = "connection in use";
+char Enetunreach[] = "network unreachable";
+char Eintr[] = "interrupted";
+char Enomem[] = "out of memory: kernel";
+char Esfnotcached[] = "subfont not cached";
+char Esoverlap[] = "segments overlap";
+char Emouseset[] = "mouse type already set";
+char Eshort[] = "i/o count too small";
+/* char Enobitstore[] = "out of screen memory"; */
+char Egreg[] = "jim'll fix it";
+char Ebadspec[] = "bad attach specifier";
+char Estopped[] = "thread must be stopped";
+char Enoattach[] = "mount/attach disallowed";
+char Eshortstat[] = "stat buffer too small";
+char Enegoff[] = "negative i/o offset";
+char Ebadstat[] = "malformed stat buffer";
+char Ecmdargs[] = "wrong #args in control message";
+char Enofd[] = "no free file descriptors";
+char Enoctl[] = "unknown control request";
--- /dev/null
+++ b/emu/port/error.h
@@ -1,0 +1,65 @@
+extern char Enoerror[]; /* no error */
+extern char Emount[]; /* inconsistent mount */
+extern char Eunmount[]; /* not mounted */
+extern char Eunion[]; /* not in union */
+extern char Emountrpc[]; /* mount rpc error */
+extern char Eshutdown[]; /* mounted device shut down */
+extern char Eowner[]; /* not owner */
+extern char Eunknown[]; /* unknown user or group id */
+extern char Enocreate[]; /* mounted directory forbids creation */
+extern char Enonexist[]; /* file does not exist */
+extern char Eexist[]; /* file already exists */
+extern char Ebadsharp[]; /* unknown device in # filename */
+extern char Enotdir[]; /* not a directory */
+extern char Eisdir[]; /* file is a directory */
+extern char Ebadchar[]; /* bad character in file name */
+extern char Efilename[]; /* file name syntax */
+extern char Eperm[]; /* permission denied */
+extern char Ebadusefd[]; /* inappropriate use of fd */
+extern char Ebadarg[]; /* bad arg in system call */
+extern char Einuse[]; /* device or object already in use */
+extern char Eio[]; /* i/o error */
+extern char Etoobig[]; /* read or write too large */
+extern char Etoosmall[]; /* read or write too small */
+extern char Enetaddr[]; /* bad network address */
+extern char Emsgsize[]; /* message is too big for protocol */
+extern char Enetbusy[]; /* network device is busy or allocated */
+extern char Enoproto[]; /* network protocol not supported */
+extern char Enoport[]; /* network port not available */
+extern char Enoifc[]; /* bad interface or no free interface slots */
+extern char Enolisten[]; /* not announced */
+extern char Ehungup[]; /* i/o on hungup channel */
+extern char Ebadctl[]; /* bad process or channel control request */
+extern char Enodev[]; /* no free devices */
+extern char Enoenv[]; /* no free environment resources */
+extern char Ethread[]; /* thread exited */
+extern char Enochild[]; /* no living children */
+extern char Eioload[]; /* i/o error in demand load */
+extern char Enovmem[]; /* out of memory: virtual memory */
+extern char Ebadld[]; /* illegal line discipline */
+extern char Ebadfd[]; /* fd out of range or not open */
+extern char Eisstream[]; /* seek on a stream */
+extern char Ebadexec[]; /* exec header invalid */
+extern char Etimedout[]; /* connection timed out */
+extern char Econrefused[]; /* connection refused */
+extern char Econinuse[]; /* connection in use */
+extern char Enetunreach[]; /* network unreachable */
+extern char Eintr[]; /* interrupted */
+extern char Enomem[]; /* out of memory: kernel */
+extern char Esfnotcached[]; /* subfont not cached */
+extern char Esoverlap[]; /* segments overlap */
+extern char Emouseset[]; /* mouse type already set */
+extern char Eshort[]; /* i/o count too small */
+extern char Enobitstore[]; /* out of screen memory */
+extern char Egreg[]; /* jim'll fix it */
+extern char Ebadspec[]; /* bad attach specifier */
+extern char Estopped[]; /* thread must be stopped */
+extern char Enoattach[]; /* mount/attach disallowed */
+extern char Eshortstat[]; /* stat buffer too small */
+extern char Enegoff[]; /* negative i/o offset */
+extern char Ebadstat[]; /* malformed stat buffer */
+extern char Ecmdargs[]; /* wrong #args in control message */
+extern char Enofd[]; /* no free file descriptors */
+extern char Enoctl[]; /* unknown control request */
+
+extern void error(char*);
--- /dev/null
+++ b/emu/port/errstr.c
@@ -1,0 +1,38 @@
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * General OS interface to errors
+ */
+void
+kwerrstr(char *fmt, ...)
+{
+ va_list arg;
+ char buf[ERRMAX];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ kstrcpy(up->env->errstr, buf, ERRMAX);
+}
+
+void
+kerrstr(char *err, uint size)
+{
+ char tmp[ERRMAX];
+
+ kstrcpy(tmp, up->env->errstr, sizeof(tmp));
+ kstrcpy(up->env->errstr, err, ERRMAX);
+ kstrcpy(err, tmp, size);
+}
+
+void
+kgerrstr(char *err, uint size)
+{
+ char *s;
+
+ s = "<no-up>";
+ if(up != nil)
+ s = up->env->errstr;
+ kstrcpy(err, s, size); /* TO DO */
+}
--- /dev/null
+++ b/emu/port/exception.c
@@ -1,0 +1,214 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include "isa.h"
+#include "runt.h"
+#include "kernel.h"
+#include "raise.h"
+
+static int
+ematch(char *pat, char *exp)
+{
+ int l;
+
+ if(strcmp(pat, exp) == 0)
+ return 1;
+
+ l = strlen(pat);
+ if(l == 0)
+ return 0;
+ if(pat[l-1] == '*') {
+ if(l == 1)
+ return 1;
+ if(strncmp(pat, exp, l-1) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static void
+setstr(String *s, char *p)
+{
+ if(s == H)
+ return;
+ if(s->len < 0 || s->max < 4)
+ return;
+ kstrcpy(s->Sascii, p, s->max); /* TO DO: we are assuming they aren't runes */
+ s->len = strlen(s->Sascii);
+}
+
+static String *exstr;
+
+void
+excinit(void)
+{
+ exstr = newstring(ERRMAX);
+ poolimmutable(D2H(exstr));
+}
+
+static String*
+newestring(char *estr)
+{
+ String *s;
+
+ if(waserror()){
+ setstr(exstr, estr);
+ D2H(exstr)->ref++;
+ return exstr;
+ }
+ s = c2string(estr, strlen(estr));
+ poperror();
+ return s;
+}
+
+#define NOPC 0xffffffff
+
+#define FRTYPE(f) ((f)->t == nil ? SEXTYPE(f)->reg.TR : (f)->t)
+
+/*
+ * clear up an uncalled frame
+ */
+static void
+freeframe(uchar *fp, int setsp)
+{
+ Frame *f;
+
+ f = (Frame*)fp;
+ if(f->t == nil)
+ unextend(f);
+ else if(f->t->np)
+ freeptrs(f, f->t);
+ if(setsp)
+ R.SP = fp;
+}
+
+int
+handler(char *estr)
+{
+ Prog *p;
+ Modlink *m, *mr;
+ int str, ne;
+ ulong pc, newpc;
+ long eoff;
+ uchar *fp, **eadr;
+ Frame *f;
+ Type *t, *zt;
+ Handler *h;
+ Except *e;
+ void *v;
+
+ p = currun();
+ if(*estr == 0 || p == nil)
+ return 0;
+ str = p->exval == H || D2H(p->exval)->t == &Tstring;
+ m = R.M;
+ if(m->compiled)
+ pc = (ulong)R.PC-(ulong)m->prog;
+ else
+ pc = R.PC-m->prog;
+ pc--;
+ fp = R.FP;
+
+ while(fp != nil){ /* look for a handler */
+ if((h = m->m->htab) != nil){
+ for( ; h->etab != nil; h++){
+ if(pc < h->pc1 || pc >= h->pc2)
+ continue;
+ eoff = h->eoff;
+ zt = h->t;
+ for(e = h->etab, ne = h->ne; e->s != nil; e++, ne--){
+ if(ematch(e->s, estr) && (str && ne <= 0 || !str && ne > 0)){
+ newpc = e->pc;
+ goto found;
+ }
+ }
+ newpc = e->pc;
+ if(newpc != NOPC)
+ goto found;
+ }
+ }
+ if(!str && fp != R.FP){ /* becomes a string exception in immediate caller */
+ v = p->exval;
+ p->exval = *(String**)v;
+ D2H(p->exval)->ref++;
+ destroy(v);
+ str = 1;
+ continue;
+ }
+ f = (Frame*)fp;
+ if(f->mr != nil)
+ m = f->mr;
+ if(m->compiled)
+ pc = (ulong)f->lr-(ulong)m->prog;
+ else
+ pc = f->lr-m->prog;
+ pc--;
+ fp = f->fp;
+ }
+ destroy(p->exval);
+ p->exval = H;
+ return 0;
+found:
+ {
+ int n;
+ char name[3*KNAMELEN];
+
+ pc = modstatus(&R, name, sizeof(name));
+ n = 10+1+strlen(name)+1+strlen(estr)+1;
+ p->exstr = realloc(p->exstr, n);
+ if(p->exstr != nil)
+ snprint(p->exstr, n, "%lud %s %s", pc, name, estr);
+ }
+
+ /*
+ * there may be an uncalled frame at the top of the stack
+ */
+ f = (Frame*)R.FP;
+ t = FRTYPE(f);
+ if(R.FP < R.EX || R.FP >= R.TS)
+ freeframe(R.EX+OA(Stkext, reg.tos.fr), 0);
+ else if(R.FP+t->size < R.SP)
+ freeframe(R.FP+t->size, 1);
+
+ m = R.M;
+ while(R.FP != fp){
+ f = (Frame*)R.FP;
+ R.PC = f->lr;
+ R.FP = f->fp;
+ R.SP = (uchar*)f;
+ mr = f->mr;
+ if(f->t == nil)
+ unextend(f);
+ else if(f->t->np)
+ freeptrs(f, f->t);
+ if(mr != nil){
+ m = mr;
+ destroy(R.M);
+ R.M = m;
+ R.MP = m->MP;
+ }
+ }
+ if(zt != nil){
+ freeptrs(fp, zt);
+ initmem(zt, fp);
+ }
+ eadr = (uchar**)(fp+eoff);
+ destroy(*eadr);
+ *eadr = H;
+ if(p->exval == H)
+ *eadr = (uchar*)newestring(estr); /* might fail */
+ else{
+ D2H(p->exval)->ref++;
+ *eadr = p->exval;
+ }
+ if(m->compiled)
+ R.PC = (Inst*)((ulong)m->prog+newpc);
+ else
+ R.PC = m->prog+newpc;
+ memmove(&p->R, &R, sizeof(R));
+ p->kill = nil;
+ destroy(p->exval);
+ p->exval = H;
+ return 1;
+}
--- /dev/null
+++ b/emu/port/exportfs.c
@@ -1,0 +1,1219 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "kernel.h"
+
+typedef struct Fid Fid;
+typedef struct Export Export;
+typedef struct Exq Exq;
+
+enum
+{
+ Nfidhash = 32,
+ MAXFDATA = 8192,
+ MAXRPCDEF = IOHDRSZ+MAXFDATA, /* initial/default */
+ MAXRPCMAX = IOHDRSZ+64*1024, /* most every allowed */
+ MSGHDRSZ = BIT32SZ+BIT8SZ+BIT16SZ
+};
+
+struct Export
+{
+ Lock l;
+ Ref r;
+ Exq* work;
+ Lock fidlock;
+ Fid* fid[Nfidhash];
+ Uqidtab uqids;
+ Chan* io;
+ Chan* root;
+ Pgrp* pgrp;
+ Egrp* egrp;
+ Fgrp* fgrp;
+ int async;
+ int readonly;
+ int uid;
+ int gid;
+ int msize;
+ char* user;
+};
+
+struct Fid
+{
+ Fid* next;
+ Fid** last;
+ Chan* chan;
+ int fid;
+ int ref; /* fcalls using the fid; locked by Export.Lock */
+ vlong offset; /* last offset used (within directory) */
+ int attached; /* fid attached or cloned but not clunked */
+ Uqid* qid; /* generated qid */
+};
+
+struct Exq
+{
+ Lock l;
+ int busy; /* fcall in progress */
+ int finished; /* will do no more work on this request or flushes */
+ Exq* next;
+ int shut; /* has been noted for shutdown */
+ Exq* flush; /* queued flush requests */
+ Exq* flusht; /* tail of flush queue */
+ Export* export;
+ Proc* slave;
+ Fcall in, out;
+ uchar* buf;
+ int bsize;
+};
+
+struct
+{
+ Lock l;
+ QLock qwait;
+ Rendez rwait;
+ Exq *head; /* work waiting for a slave */
+ Exq *tail;
+}exq;
+
+static void exshutdown(Export*);
+static int exflushed(Export*, Exq*);
+static void exslave(void*);
+static void exfree(Export*);
+static void exfreeq(Exq*);
+static void exportproc(void*);
+static void exreply(Exq*, char*);
+static int exisroot(Export*, Chan*);
+
+static char* Exversion(Export*, Fcall*, Fcall*);
+static char* Exauth(Export*, Fcall*, Fcall*);
+static char* Exattach(Export*, Fcall*, Fcall*);
+static char* Exclunk(Export*, Fcall*, Fcall*);
+static char* Excreate(Export*, Fcall*, Fcall*);
+static char* Exopen(Export*, Fcall*, Fcall*);
+static char* Exread(Export*, Fcall*, Fcall*);
+static char* Exremove(Export*, Fcall*, Fcall*);
+static char* Exstat(Export*, Fcall*, Fcall*);
+static char* Exwalk(Export*, Fcall*, Fcall*);
+static char* Exwrite(Export*, Fcall*, Fcall*);
+static char* Exwstat(Export*, Fcall*, Fcall*);
+
+static char *(*fcalls[Tmax])(Export*, Fcall*, Fcall*);
+
+static char Enofid[] = "no such fid";
+static char Eseekdir[] = "can't seek on a directory";
+static char Eopen[] = "walk of open fid";
+static char Emode[] = "open/create -- unknown mode";
+static char Edupfid[] = "fid in use";
+static char Eaccess[] = "read/write -- not open in suitable mode";
+static char Ecount[] = "read/write -- count too big";
+int exdebug = 0;
+
+int
+export(int fd, char *dir, int async)
+{
+ Chan *c, *dc;
+ Pgrp *pg;
+ Egrp *eg;
+ Export *fs;
+
+ if(waserror())
+ return -1;
+ c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1);
+ poperror();
+
+ if(waserror()){
+ cclose(c);
+ return -1;
+ }
+ dc = namec(dir, Atodir, 0, 0);
+ poperror();
+
+ fs = malloc(sizeof(Export));
+ if(fs == nil){
+ cclose(c);
+ cclose(dc);
+ error(Enomem);
+ }
+
+ fs->r.ref = 1;
+ pg = up->env->pgrp;
+ fs->pgrp = pg;
+ incref(&pg->r);
+ eg = up->env->egrp;
+ fs->egrp = eg;
+ incref(&eg->r);
+ fs->fgrp = newfgrp(nil);
+ fs->uid = up->env->uid;
+ fs->gid = up->env->gid;
+ kstrdup(&fs->user, up->env->user);
+ fs->root = dc;
+ fs->io = c;
+ uqidinit(&fs->uqids);
+ fs->msize = 0;
+ c->flag |= CMSG;
+ fs->async = async;
+
+ if(async){
+ if(waserror())
+ return -1;
+ kproc("exportfs", exportproc, fs, 0);
+ poperror();
+ }else
+ exportproc(fs);
+
+ return 0;
+}
+
+static void
+exportinit(void)
+{
+ lock(&exq.l);
+ if(fcalls[Tversion] != nil) {
+ unlock(&exq.l);
+ return;
+ }
+ fcalls[Tversion] = Exversion;
+ fcalls[Tauth] = Exauth;
+ fcalls[Tattach] = Exattach;
+ fcalls[Twalk] = Exwalk;
+ fcalls[Topen] = Exopen;
+ fcalls[Tcreate] = Excreate;
+ fcalls[Tread] = Exread;
+ fcalls[Twrite] = Exwrite;
+ fcalls[Tclunk] = Exclunk;
+ fcalls[Tremove] = Exremove;
+ fcalls[Tstat] = Exstat;
+ fcalls[Twstat] = Exwstat;
+ unlock(&exq.l);
+}
+
+static int
+exisroot(Export *fs, Chan *c)
+{
+ return eqchan(fs->root, c, 1);
+}
+
+static int
+exreadn(Chan *c, void *buf, int n)
+{
+ int nr, t;
+
+ if(waserror())
+ return -1;
+ for(nr = 0; nr < n;){
+ t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0);
+ if(t <= 0)
+ break;
+ nr += t;
+ }
+ poperror();
+ return nr;
+}
+
+static int
+exreadmsg(Chan *c, void *a, uint n)
+{
+ int m, len;
+ uchar *buf;
+
+ buf = a;
+ m = exreadn(c, buf, BIT32SZ);
+ if(m < BIT32SZ){
+ if(m < 0)
+ return -1;
+ return 0;
+ }
+ len = GBIT32(buf);
+ if(len <= BIT32SZ || len > n){
+ kwerrstr("bad length in Styx message header");
+ return -1;
+ }
+ len -= BIT32SZ;
+ m = exreadn(c, buf+BIT32SZ, len);
+ if(m < len){
+ if(m < 0)
+ return -1;
+ return 0;
+ }
+ return BIT32SZ+m;
+}
+
+static void
+exportproc(void *a)
+{
+ Exq *q;
+ int async, msize;
+ int n, type;
+ Export *fs = a;
+
+ exportinit();
+
+ for(;;){
+
+ msize = fs->msize;
+ if(msize == 0)
+ msize = MAXRPCDEF;
+ for(n=0;; n++){ /* we don't use smalloc, to avoid memset */
+ q = mallocz(sizeof(*q)+msize, 0);
+ if(q != nil || n > 6000)
+ break;
+ if(n%600 == 0)
+ print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize);
+ osenter();
+ osmillisleep(100);
+ osleave();
+ }
+ if(q == nil){
+ kwerrstr("out of memory: read request");
+ n = -1;
+ break;
+ }
+ memset(q, 0, sizeof(*q));
+ q->buf = (uchar*)q + sizeof(*q);
+ q->bsize = msize;
+
+ n = exreadmsg(fs->io, q->buf, msize); /* TO DO: avoid copy */
+ if(n <= 0)
+ break;
+ if(convM2S(q->buf, n, &q->in) != n){
+ kwerrstr("bad T-message");
+ n = -1;
+ break;
+ }
+ type = q->in.type;
+ if(type < Tversion || type >= Tmax || type&1 || type == Terror){
+ kwerrstr("invalid T-message type %d", type);
+ n = -1;
+ break;
+ }
+
+ if(exdebug)
+ print("export %ld <- %F\n", up->pid, &q->in);
+
+ q->out.type = type+1;
+ q->out.tag = q->in.tag;
+
+ q->export = fs;
+ incref(&fs->r);
+
+ if(fs->readonly){
+ switch(type){
+ case Topen:
+ if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD)
+ break;
+ /* FALL THROUGH */
+ case Tcreate:
+ case Twrite:
+ case Tremove:
+ case Twstat:
+ q->out.type = Rerror;
+ q->out.ename = "file system read only";
+ exreply(q, "exportproc");
+ exfreeq(q);
+ continue;
+ }
+ }
+
+ if(q->in.type == Tflush){
+ if(exflushed(fs, q)){
+ /* not yet started or not found (flush arrived after reply); we reply */
+ if(exdebug)
+ print("export: flush %d\n", q->in.oldtag);
+ exreply(q, "exportproc");
+ exfreeq(q);
+ }
+ continue;
+ }
+
+ lock(&exq.l);
+ if(exq.head == nil)
+ exq.head = q;
+ else
+ exq.tail->next = q;
+ q->next = nil;
+ exq.tail = q;
+ unlock(&exq.l);
+ if(exq.qwait.head == nil)
+ kproc("exslave", exslave, nil, 0);
+ Wakeup(&exq.rwait);
+ }
+
+ if(exdebug){
+ if(n < 0)
+ print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr);
+ else
+ print("exportproc %ld shut down\n", up->pid);
+ }
+
+ free(q);
+ exshutdown(fs);
+ async = fs->async;
+ exfree(fs);
+
+ if(async)
+ pexit("mount shut down", 0);
+}
+
+static int
+exflushed(Export *fs, Exq *fq)
+{
+ Exq *q, **last;
+ ulong pid;
+
+ /* not yet started? */
+ lock(&exq.l);
+ for(last = &exq.head; (q = *last) != nil; last = &q->next)
+ if(q->export == fs && q->in.tag == fq->in.oldtag){
+ *last = q->next;
+ unlock(&exq.l);
+ /* not yet started: discard, and Rflush */
+ exfreeq(q);
+ return 1;
+ }
+ unlock(&exq.l);
+
+ /* tricky case: in progress */
+ lock(&fs->l);
+ for(q = fs->work; q != nil; q = q->next)
+ if(q->in.tag == fq->in.oldtag){
+ pid = 0;
+ lock(&q->l);
+ if(q->finished){
+ /* slave replied and emptied its flush queue; we can Rflush now */
+ unlock(&q->l);
+ return 1;
+ }
+ /* append to slave's flush queue */
+ fq->next = nil;
+ if(q->flush != nil)
+ q->flusht->next = fq;
+ else
+ q->flush = fq;
+ q->flusht = fq;
+ if(q->busy){
+ pid = q->slave->pid;
+ swiproc(q->slave, 0);
+ }
+ unlock(&q->l);
+ unlock(&fs->l);
+ if(exdebug && pid)
+ print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag);
+ return 0;
+ }
+ unlock(&fs->l);
+
+ /* not found */
+ return 1;
+}
+
+static void
+exfreeq(Exq *q)
+{
+ Exq *fq;
+
+ while((fq = q->flush) != nil){
+ q->flush = fq->next;
+ exfree(fq->export);
+ free(fq);
+ }
+ exfree(q->export);
+ free(q);
+}
+
+static void
+exshutdown(Export *fs)
+{
+ Exq *q, **last;
+
+ /* work not started */
+ lock(&exq.l);
+ for(last = &exq.head; (q = *last) != nil;)
+ if(q->export == fs){
+ *last = q->next;
+ exfreeq(q);
+ }else
+ last = &q->next;
+ unlock(&exq.l);
+
+ /* tell slaves to abandon work in progress */
+ lock(&fs->l);
+ while((q = fs->work) != nil){
+ fs->work = q->next;
+ lock(&q->l);
+ q->shut = 1;
+ swiproc(q->slave, 0); /* whether busy or not */
+ unlock(&q->l);
+ }
+ unlock(&fs->l);
+}
+
+static void
+exfreefids(Export *fs)
+{
+ Fid *f, *n;
+ int i;
+
+ for(i = 0; i < Nfidhash; i++){
+ for(f = fs->fid[i]; f != nil; f = n){
+ n = f->next;
+ f->attached = 0;
+ if(f->ref == 0) {
+ if(f->chan != nil)
+ cclose(f->chan);
+ freeuqid(&fs->uqids, f->qid);
+ free(f);
+ } else
+ print("exfreefids: busy fid\n");
+ }
+ }
+}
+
+static void
+exfree(Export *fs)
+{
+ if(exdebug)
+ print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref);
+ if(decref(&fs->r) != 0)
+ return;
+ closepgrp(fs->pgrp);
+ closeegrp(fs->egrp);
+ closefgrp(fs->fgrp);
+ cclose(fs->root);
+ cclose(fs->io);
+ exfreefids(fs);
+ free(fs->user);
+ free(fs);
+}
+
+static int
+exwork(void *a)
+{
+ USED(a);
+ return exq.head != nil;
+}
+
+static void
+exslave(void *a)
+{
+ Export *fs;
+ Exq *q, *t, *fq, **last;
+ char *err;
+
+ USED(a);
+
+ for(;;){
+ qlock(&exq.qwait);
+ if(waserror()){
+ qunlock(&exq.qwait);
+ continue;
+ }
+ Sleep(&exq.rwait, exwork, nil);
+ poperror();
+
+ lock(&exq.l);
+ q = exq.head;
+ if(q == nil) {
+ unlock(&exq.l);
+ qunlock(&exq.qwait);
+ continue;
+ }
+ exq.head = q->next;
+
+ qunlock(&exq.qwait);
+
+ /*
+ * put the job on the work queue before it's
+ * visible as off of the head queue, so it's always
+ * findable for flushes and shutdown
+ */
+ notkilled();
+ q->slave = up;
+ q->busy = 1; /* fcall in progress: interruptible */
+ fs = q->export;
+ lock(&fs->l);
+ q->next = fs->work;
+ fs->work = q;
+ unlock(&fs->l);
+ unlock(&exq.l);
+
+ up->env->pgrp = q->export->pgrp;
+ up->env->egrp = q->export->egrp;
+ up->env->fgrp = q->export->fgrp;
+ up->env->uid = q->export->uid;
+ up->env->gid = q->export->gid;
+ kstrdup(&up->env->user, q->export->user);
+
+ if(exdebug > 1)
+ print("exslave %ld dispatch %F\n", up->pid, &q->in);
+
+ if(waserror()){
+ print("exslave %ld err %s\n", up->pid, up->env->errstr); /* shouldn't happen */
+ err = up->env->errstr;
+ }else{
+ if(q->in.type >= Tmax || !fcalls[q->in.type]){
+ snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in);
+ err = up->genbuf;
+ }else{
+ switch(q->in.type){
+ case Tread:
+ q->out.data = (char*)q->buf + IOHDRSZ;
+ break;
+ case Tstat:
+ q->out.stat = q->buf + MSGHDRSZ + BIT16SZ; /* leaves it just where we want it */
+ q->out.nstat = q->bsize-(MSGHDRSZ+BIT16SZ);
+ break;
+ }
+ err = (*fcalls[q->in.type])(fs, &q->in, &q->out);
+ }
+ poperror();
+ }
+
+ /*
+ * if the fcall completed without error we must reply,
+ * even if a flush is pending (because the underlying server
+ * might have changed state), unless the export has shut down completely.
+ * must also reply to each flush in order, and only after the original reply (if sent).
+ */
+ lock(&q->l);
+ notkilled();
+ q->busy = 0; /* operation complete */
+ if(!q->shut){
+ if(q->flush == nil || err == nil){
+ unlock(&q->l);
+ q->out.type = q->in.type+1;
+ q->out.tag = q->in.tag;
+ if(err){
+ q->out.type = Rerror;
+ q->out.ename = err;
+ }
+ exreply(q, "exslave");
+ lock(&q->l);
+ }
+ while((fq = q->flush) != nil && !q->shut){
+ q->flush = fq->next;
+ unlock(&q->l);
+ exreply(fq, "exslave");
+ exfreeq(fq);
+ lock(&q->l);
+ }
+ }
+ q->finished = 1; /* promise not to send any more */
+ unlock(&q->l);
+
+ lock(&fs->l);
+ for(last = &fs->work; (t = *last) != nil; last = &t->next)
+ if(t == q){
+ *last = q->next;
+ break;
+ }
+ unlock(&fs->l);
+
+ notkilled();
+ exfreeq(q);
+ }
+}
+
+static void
+exreply(Exq *q, char *who)
+{
+ Export *fs;
+ Fcall *r;
+ int n;
+
+ fs = q->export;
+ r = &q->out;
+
+ n = convS2M(r, q->buf, q->bsize);
+ if(n == 0){
+ r->type = Rerror;
+ if(fs->msize == 0)
+ r->ename = "Tversion not seen";
+ else
+ r->ename = "failed to convert R-message";
+ n = convS2M(r, q->buf, q->bsize);
+ }
+
+ if(exdebug)
+ print("%s %ld -> %F\n", who, up->pid, r);
+
+ if(!waserror()){
+ devtab[fs->io->type]->write(fs->io, q->buf, n, 0);
+ poperror();
+ }
+}
+
+static int
+exiounit(Export *fs, Chan *c)
+{
+ int iounit;
+
+ iounit = fs->msize-IOHDRSZ;
+ if(c->iounit != 0 && c->iounit < fs->msize)
+ iounit = c->iounit;
+ return iounit;
+}
+
+static Fid*
+Exmkfid(Export *fs, ulong fid)
+{
+ ulong h;
+ Fid *f, *nf;
+
+ nf = malloc(sizeof(Fid));
+ if(nf == nil)
+ return nil;
+ lock(&fs->fidlock);
+ h = fid % Nfidhash;
+ for(f = fs->fid[h]; f != nil; f = f->next){
+ if(f->fid == fid){
+ unlock(&fs->fidlock);
+ free(nf);
+ return nil;
+ }
+ }
+
+ nf->next = fs->fid[h];
+ if(nf->next != nil)
+ nf->next->last = &nf->next;
+ nf->last = &fs->fid[h];
+ fs->fid[h] = nf;
+
+ nf->fid = fid;
+ nf->ref = 1;
+ nf->attached = 1;
+ nf->offset = 0;
+ nf->chan = nil;
+ nf->qid = nil;
+ unlock(&fs->fidlock);
+ return nf;
+}
+
+static Fid*
+Exgetfid(Export *fs, ulong fid)
+{
+ Fid *f;
+ ulong h;
+
+ lock(&fs->fidlock);
+ h = fid % Nfidhash;
+ for(f = fs->fid[h]; f; f = f->next) {
+ if(f->fid == fid){
+ if(f->attached == 0)
+ break;
+ f->ref++;
+ unlock(&fs->fidlock);
+ return f;
+ }
+ }
+ unlock(&fs->fidlock);
+ return nil;
+}
+
+static void
+Exputfid(Export *fs, Fid *f)
+{
+ Chan *c;
+
+ lock(&fs->fidlock);
+ f->ref--;
+ if(f->ref == 0 && f->attached == 0){
+ c = f->chan;
+ f->chan = nil;
+ *f->last = f->next;
+ if(f->next != nil)
+ f->next->last = f->last;
+ unlock(&fs->fidlock);
+ freeuqid(&fs->uqids, f->qid);
+ free(f);
+ if(c != nil)
+ cclose(c);
+ return;
+ }
+ unlock(&fs->fidlock);
+}
+
+static Chan*
+exmount(Chan *c, Mhead **mp, int doname)
+{
+ struct {Chan *nc;} nc;
+ Cname *oname;
+
+ nc.nc = nil;
+ if((c->flag & COPEN) == 0 && findmount(&nc.nc, mp, c->type, c->dev, c->qid)){
+ if(waserror()){
+ cclose(nc.nc);
+ nexterror();
+ }
+ nc.nc = cunique(nc.nc);
+ poperror();
+ if(doname){
+ oname = c->name;
+ incref(&oname->r);
+ cnameclose(nc.nc->name);
+ nc.nc->name = oname;
+ }
+ return nc.nc;
+ }
+ incref(&c->r);
+ return c;
+}
+
+static char*
+Exversion(Export *fs, Fcall *t, Fcall *r)
+{
+ char *p;
+ static char version[] = VERSION9P;
+ int iounit;
+
+ r->msize = t->msize;
+ if(r->msize > MAXRPCMAX)
+ r->msize = MAXRPCMAX;
+ iounit = fs->io->iounit;
+ if(iounit != 0 && iounit > 64 && iounit < r->msize)
+ r->msize = iounit;
+ if(r->msize < 64)
+ return "message size too small";
+ if(0)
+ print("msgsize=%d\n", r->msize);
+ if((p = strchr(t->version, '.')) != nil)
+ *p = 0;
+ if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){
+ r->version = version;
+ fs->msize = r->msize;
+ }else
+ r->version = "unknown";
+ return nil;
+}
+
+static char*
+Exauth(Export *fs, Fcall *t, Fcall *r)
+{
+ USED(fs);
+ USED(t);
+ USED(r);
+ return "authentication not required";
+}
+
+static char*
+Exattach(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+
+ f = Exmkfid(fs, t->fid);
+ if(f == nil)
+ return Edupfid;
+ if(waserror()){
+ f->attached = 0;
+ Exputfid(fs, f);
+ return up->env->errstr;
+ }
+ f->chan = cclone(fs->root);
+ f->qid = uqidalloc(&fs->uqids, f->chan);
+ poperror();
+ r->qid = mkuqid(f->chan, f->qid);
+ Exputfid(fs, f);
+ return nil;
+}
+
+static char*
+Exclunk(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+
+ USED(r);
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+ f->attached = 0;
+ Exputfid(fs, f);
+ return nil;
+}
+
+static int
+safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
+{
+ int r;
+
+ /* walk can raise error */
+ if(waserror())
+ return -1;
+ r = walk(cp, names, nnames, nomount, nerror);
+ poperror();
+ return r;
+}
+
+static char*
+Exwalk(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f, *nf;
+ Chan *c;
+ char *name;
+ Uqid *qid;
+ int i;
+
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+ if(f->chan->flag & COPEN){
+ Exputfid(fs, f);
+ return Eopen;
+ }
+ if(waserror())
+ return up->env->errstr;
+ c = cclone(f->chan);
+ poperror();
+ qid = f->qid;
+ incref(&qid->r);
+ r->nwqid = 0;
+ if(t->nwname > 0){
+ for(i=0; i<t->nwname; i++){
+ name = t->wname[i];
+ if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){
+ if(safewalk(&c, &name, 1, 0, nil) < 0){
+ /* leave the original state on error */
+ cclose(c);
+ freeuqid(&fs->uqids, qid);
+ Exputfid(fs, f);
+ if(i == 0)
+ return up->env->errstr;
+ return nil;
+ }
+ freeuqid(&fs->uqids, qid);
+ qid = uqidalloc(&fs->uqids, c);
+ }
+ r->wqid[r->nwqid++] = mkuqid(c, qid);
+ }
+ }
+
+ if(t->newfid != t->fid){
+ nf = Exmkfid(fs, t->newfid);
+ if(nf == nil){
+ cclose(c);
+ freeuqid(&fs->uqids, qid);
+ Exputfid(fs, f);
+ return Edupfid;
+ }
+ nf->chan = c;
+ nf->qid = qid;
+ Exputfid(fs, nf);
+ }else{
+ cclose(f->chan);
+ f->chan = c;
+ freeuqid(&fs->uqids, f->qid);
+ f->qid = qid;
+ }
+ Exputfid(fs, f);
+ return nil;
+}
+
+static char*
+Exopen(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+ Chan *c;
+ Uqid *qid;
+ Mhead *m;
+
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+ if(f->chan->flag & COPEN){
+ Exputfid(fs, f);
+ return Emode;
+ }
+ m = nil;
+ c = exmount(f->chan, &m, 1);
+ if(waserror()){
+ cclose(c);
+ Exputfid(fs, f);
+ return up->env->errstr;
+ }
+
+ /* only save the mount head if it's a multiple element union */
+ if(m && m->mount && m->mount->next)
+ c->umh = m;
+ else
+ putmhead(m);
+
+ c = devtab[c->type]->open(c, t->mode);
+ if(t->mode & ORCLOSE)
+ c->flag |= CRCLOSE;
+
+ qid = uqidalloc(&fs->uqids, c);
+ poperror();
+ freeuqid(&fs->uqids, f->qid);
+ cclose(f->chan);
+ f->chan = c;
+ f->qid = qid;
+ f->offset = 0;
+ r->qid = mkuqid(c, f->qid);
+ r->iounit = exiounit(fs, c);
+ Exputfid(fs, f);
+ return nil;
+}
+
+static char*
+Excreate(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+ volatile struct {Chan *c;} c, dc;
+ Cname *oname;
+ Uqid *qid;
+ Mhead *m;
+
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+ if(f->chan->flag & COPEN){
+ Exputfid(fs, f);
+ return Emode;
+ }
+ if(waserror()){
+ Exputfid(fs, f);
+ return up->env->errstr;
+ }
+ validname(t->name, 0);
+ if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0'))
+ error(Efilename); /* underlying server should check, but stop it here */
+
+ m = nil;
+ c.c = exmount(f->chan, &m, 1);
+ if(waserror()){
+ cclose(c.c);
+ if(m != nil)
+ putmhead(m);
+ nexterror();
+ }
+ if(m != nil){
+ oname = c.c->name;
+ incref(&oname->r);
+ if(waserror()){
+ cnameclose(oname);
+ nexterror();
+ }
+ dc.c = createdir(c.c, m);
+ if(waserror()){
+ cclose(dc.c);
+ nexterror();
+ }
+ c.c = cunique(dc.c);
+ poperror();
+ cnameclose(c.c->name);
+ poperror();
+ c.c->name = oname;
+ }
+ devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm);
+ c.c->name = addelem(c.c->name, t->name);
+ if(t->mode & ORCLOSE)
+ c.c->flag |= CRCLOSE;
+ qid = uqidalloc(&fs->uqids, c.c);
+ poperror();
+ if(m != nil)
+ putmhead(m);
+
+ poperror();
+ cclose(f->chan);
+ f->chan = c.c;
+ freeuqid(&fs->uqids, f->qid);
+ f->qid = qid;
+ r->qid = mkuqid(c.c, f->qid);
+ r->iounit = exiounit(fs, c.c);
+ Exputfid(fs, f);
+ return nil;
+}
+
+static char*
+Exread(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+ Chan *c;
+ Lock *cl;
+ vlong off;
+ int dir, n, seek;
+
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+
+ if(waserror()) {
+ Exputfid(fs, f);
+ return up->env->errstr;
+ }
+ c = f->chan;
+ if((c->flag & COPEN) == 0)
+ error(Emode);
+ if(c->mode != OREAD && c->mode != ORDWR)
+ error(Eaccess);
+ if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
+ error(Ecount);
+ if(t->offset < 0)
+ error(Enegoff);
+ dir = c->qid.type & QTDIR;
+ if(dir && t->offset != f->offset){
+ if(t->offset != 0)
+ error(Eseekdir);
+ f->offset = 0;
+ c->uri = 0;
+ c->dri = 0;
+ }
+
+ for(;;){
+ n = t->count;
+ seek = 0;
+ off = t->offset;
+ if(dir && f->offset != off){
+ off = f->offset;
+ n = t->offset - off;
+ if(n > MAXFDATA)
+ n = MAXFDATA;
+ seek = 1;
+ }
+ if(dir && c->umh != nil){
+ if(0)
+ print("union read %d uri %d dri %d\n", seek, c->uri, c->dri);
+ n = unionread(c, r->data, n);
+ }
+ else{
+ cl = &c->l;
+ lock(cl);
+ c->offset = off;
+ unlock(cl);
+ n = devtab[c->type]->read(c, r->data, n, off);
+ lock(cl);
+ c->offset += n;
+ unlock(cl);
+ }
+ f->offset = off + n;
+ if(n == 0 || !seek)
+ break;
+ }
+ r->count = n;
+
+ poperror();
+ Exputfid(fs, f);
+ return nil;
+}
+
+static char*
+Exwrite(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+ Chan *c;
+
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+ if(waserror()){
+ Exputfid(fs, f);
+ return up->env->errstr;
+ }
+ c = f->chan;
+ if((c->flag & COPEN) == 0)
+ error(Emode);
+ if(c->mode != OWRITE && c->mode != ORDWR)
+ error(Eaccess);
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+ if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
+ error(Ecount);
+ if(t->offset < 0)
+ error(Enegoff);
+ r->count = devtab[c->type]->write(c, t->data, t->count, t->offset);
+ poperror();
+ Exputfid(fs, f);
+ return nil;
+}
+
+static char*
+Exstat(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+ Chan *c;
+ int n;
+
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+ c = exmount(f->chan, nil, 1);
+ if(waserror()){
+ cclose(c);
+ Exputfid(fs, f);
+ return up->env->errstr;
+ }
+ n = devtab[c->type]->stat(c, r->stat, r->nstat);
+ if(n <= BIT16SZ)
+ error(Eshortstat);
+ r->nstat = n;
+ poperror();
+ /* TO DO: need to change qid */
+ cclose(c);
+ Exputfid(fs, f);
+ return nil;
+}
+
+static char*
+Exwstat(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+ Chan *c;
+
+ USED(r);
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+ if(waserror()){
+ Exputfid(fs, f);
+ return up->env->errstr;
+ }
+ validstat(t->stat, t->nstat); /* check name */
+
+ c = exmount(f->chan, nil, 0);
+ if(waserror()){
+ cclose(c);
+ nexterror();
+ }
+ devtab[c->type]->wstat(c, t->stat, t->nstat);
+ poperror();
+
+ cclose(c);
+ poperror();
+ Exputfid(fs, f);
+ return nil;
+}
+
+static char*
+Exremove(Export *fs, Fcall *t, Fcall *r)
+{
+ Fid *f;
+ Chan *c;
+
+ USED(r);
+ f = Exgetfid(fs, t->fid);
+ if(f == nil)
+ return Enofid;
+ if(waserror()){
+ f->attached = 0;
+ Exputfid(fs, f);
+ return up->env->errstr;
+ }
+ c = exmount(f->chan, nil, 0);
+ if(waserror()){
+ c->type = 0; /* see below */
+ cclose(c);
+ nexterror();
+ }
+ devtab[c->type]->remove(c);
+ poperror();
+ poperror();
+
+ /*
+ * chan is already clunked by remove.
+ * however, we need to recover the chan,
+ * and follow sysremove's lead in making it point to root.
+ */
+ c->type = 0;
+ cclose(c);
+
+ f->attached = 0;
+ Exputfid(fs, f);
+ return nil;
+}
--- /dev/null
+++ b/emu/port/exptab.c
@@ -1,0 +1,6 @@
+#include "dat.h"
+#include <dynld.h>
+
+/* dummy export table */
+
+Dynsym _exporttab[] = { 0, 0, nil };
--- /dev/null
+++ b/emu/port/file.c
@@ -1,0 +1,16 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+int
+openmode(ulong o)
+{
+ if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
+ error(Ebadarg);
+ o &= ~(OTRUNC|OCEXEC|ORCLOSE);
+ if(o > OEXEC)
+ error(Ebadarg);
+ if(o == OEXEC)
+ return OREAD;
+ return o;
+}
--- /dev/null
+++ b/emu/port/fns.h
@@ -1,0 +1,251 @@
+ulong FPcontrol(ulong,ulong);
+ulong FPstatus(ulong,ulong);
+void FPsave(void*);
+void FPrestore(void*);
+void Sleep(Rendez*, int (*)(void*), void*);
+int Wakeup(Rendez*);
+void FPinit(void);
+void addprog(Proc*);
+Block* adjustblock(Block*, int);
+Block* allocb(int);
+Block* bl2mem(uchar*, Block*, int);
+int blocklen(Block*);
+char* c2name(Chan*);
+int canlock(Lock*);
+int canqlock(QLock*);
+void cclose(Chan*);
+void chandevinit(void);
+void chanfree(Chan*);
+Dir* chandirstat(Chan*);
+void cinit(void);
+char* clipread(void);
+int clipwrite(char*);
+void (*coherence)(void);
+void copen(Chan*);
+void cmderror(Cmdbuf*, char*);
+Block* concatblock(Block*);
+int cread(Chan*, uchar*, int, vlong);
+void cwrite(Chan*, uchar*, int, vlong);
+Chan* cunique(Chan*);
+void cupdate(Chan*, uchar*, int, vlong);
+char* cleanname(char*);
+Chan* cclone(Chan*);
+void closeegrp(Egrp*);
+void closefgrp(Fgrp*);
+void closepgrp(Pgrp*);
+void closesigs(Skeyset*);
+int cmount(Chan*, Chan*, int, char*);
+Chan* createdir(Chan*, Mhead*);
+void cunmount(Chan*, Chan*);
+int decref(Ref*);
+long devbwrite(Chan*, Block*, ulong);
+void devcreate(Chan*, char*, int, ulong);
+void devdir(Chan*, Qid, char*, long, char*, long, Dir*);
+long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*);
+void devinit(void);
+int devno(int, int);
+Dev* devbyname(char*);
+void devpermcheck(char*, ulong, int);
+void devremove(Chan*);
+int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*);
+long devtabread(Chan*, void*, long, vlong);
+int devwstat(Chan*, uchar*, int);
+Chan* devattach(int, char*);
+Block* devbread(Chan*, long, ulong);
+Chan* devclone(Chan*);
+Devgen devgen;
+Chan* devopen(Chan*, int, Dirtab*, int, Devgen*);
+Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*);
+void disfault(void*, char*);
+void disinit(void*);
+int domount(Chan**, Mhead**);
+void drawqlock(void);
+void drawqunlock(void);
+Fgrp* dupfgrp(Fgrp*);
+void egrpcpy(Egrp*, Egrp*);
+int emptystr(char*);
+void emuinit(void*);
+int eqchan(Chan*, Chan*, int);
+int eqqid(Qid, Qid);
+void error(char*);
+void errorf(char*, ...);
+#pragma varargck argpos errorf 1
+void excinit(void);
+void exhausted(char*);
+int export(int, char*, int);
+Chan* fdtochan(Fgrp*, int, int, int, int);
+int findmount(Chan**, Mhead**, int, int, Qid);
+void freeb(Block*);
+void freeblist(Block*);
+void freeskey(Signerkey*);
+ulong getcallerpc(void*);
+ulong getFPcontrol(void);
+ulong getFPstatus(void);
+void gkbdputc(Queue*, int);
+int incref(Ref*);
+int iprint(char*, ...);
+void isdir(Chan*);
+int isdotdot(char*);
+int iseve(void);
+int kannounce(char*, char*);
+int kdial(char*, char*, char*, int*);
+void kproc(char*, void (*)(void*), void*, int);
+void kprocinit(Proc*);
+int kfgrpclose(Fgrp*, int);
+void ksetenv(char*, char*, int);
+void kstrcpy(char*, char*, int);
+void kstrdup(char**, char*);
+long latin1(uchar*, int);
+void libinit(char*);
+void links(void);
+void lock(Lock*);
+Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int);
+Block* mem2bl(uchar*, int);
+int memusehigh(void);
+int memlow(void);
+void mkqid(Qid*, vlong, ulong, int);
+Qid mkuqid(Chan*, Uqid*);
+Chan* mntauth(Chan*, char*);
+long mntversion(Chan*, char*, int, int);
+void mountfree(Mount*);
+void mousetrack(int, int, int, int);
+void muxclose(Mnt*);
+Chan* namec(char*, int, int, ulong);
+Chan* newchan(void);
+Cname* newcname(char*);
+Egrp* newegrp(void);
+Fgrp* newfgrp(Fgrp*);
+Mount* newmount(Mhead*, Chan*, int, char*);
+Pgrp* newpgrp(void);
+Proc* newproc(void);
+void nexterror(void);
+void nofence(void);
+void notkilled(void);
+int openmode(ulong);
+void osblock(void);
+void* oscmd(char**, int, char*, int*);
+int oscmdwait(void*, char*, int);
+int oscmdkill(void*);
+void oscmdfree(void*);
+void oserror(void);
+void oserrstr(char*, uint);
+void oslongjmp(void*, osjmpbuf, int);
+long osmillisec(void);
+int osmillisleep(ulong);
+void osready(Proc*);
+int limbosleep(ulong);
+vlong osusectime(void);
+Block* packblock(Block*);
+Block* padblock(Block*, int);
+void panic(char*, ...);
+Cmdbuf* parsecmd(char*, int);
+void pexit(char*, int);
+void pgrpcpy(Pgrp*, Pgrp*);
+int progfdprint(Chan*, int, int, char*, int);
+void putenvq(char*, char*, int);
+void putenvqv(char*, char**, int, int);
+void putmhead(Mhead*);
+Block* pullupblock(Block*, int);
+Block* pullupqueue(Queue*, int);
+void qaddlist(Queue*, Block*);
+Block* qbread(Queue*, int);
+long qbwrite(Queue*, Block*);
+Queue* qbypass(void (*)(void*, Block*), void*);
+int qcanread(Queue*);
+void qclose(Queue*);
+int qisclosed(Queue*);
+int qconsume(Queue*, void*, int);
+Block* qcopy(Queue*, int, ulong);
+int qdiscard(Queue*, int);
+void qflush(Queue*);
+void qfree(Queue*);
+int qfull(Queue*);
+Block* qget(Queue*);
+void qhangup(Queue*, char*);
+int qiwrite(Queue*, void*, int);
+int qlen(Queue*);
+void qlock(QLock*);
+void qnoblock(Queue*, int);
+Queue* qopen(int, int, void (*)(void*), void*);
+int qpass(Queue*, Block*);
+int qproduce(Queue*, void*, int);
+void qputback(Queue*, Block*);
+long qread(Queue*, void*, int);
+Block* qremove(Queue*);
+void qreopen(Queue*);
+void qsetlimit(Queue*, int);
+int qstate(Queue*);
+void qunlock(QLock*);
+int qwindow(Queue*);
+int qwrite(Queue*, void*, int);
+ulong randomread(void *xp, ulong n);
+void randominit(void);
+int readkbd(void);
+int readnum(ulong, char*, ulong, ulong, int);
+int readnum_vlong(ulong, char*, ulong, vlong, int);
+int readstr(ulong, char*, ulong, char*);
+#define seconds() (osusectime()/1000000)
+void seterror(char*, ...);
+void setid(char*, int);
+void setpointer(int, int);
+char* skipslash(char*);
+void srvrtinit(void);
+void swiproc(Proc*, int);
+Block* trimblock(Block*, int, int);
+long unionread(Chan*, void*, long);
+void unlock(Lock*);
+Uqid* uqidalloc(Uqidtab*, Chan*);
+void uqidinit(Uqidtab*);
+void freeuqid(Uqidtab*, Uqid*);
+void validname(char*, int);
+void validstat(uchar*, int);
+void validwstatname(char*);
+void vmachine(void*);
+int walk(Chan**, char**, int, int, int*);
+void cleanexit(int);
+void oshostintr(Proc*);
+void osenter(void);
+void osleave(void);
+void oslopri(void);
+void ospause(void);
+void osyield(void);
+void osreboot(char*, char**);
+ulong poolmaxsize(void);
+Pool* poolmk(char*, int, int, int);
+void hnputv(void*, vlong);
+void hnputl(void*, ulong);
+void hnputs(void*, ushort);
+vlong nhgetv(void*);
+ulong nhgetl(void*);
+ushort nhgets(void*);
+void* smalloc(size_t);
+void* kmalloc(size_t);
+
+/* Namespace Emulation */
+int kbind(char*, char*, int);
+int kclose(int);
+int kcreate(char*, int, ulong);
+int kdup(int, int);
+int kfstat(int, uchar*, int);
+int kfwstat(int, uchar*, int);
+int kmount(int, int, char*, int, char*);
+int kunmount(char*, char*);
+int kopen(char*, int);
+long kread(int, void*, long);
+int kremove(char*);
+vlong kseek(int, vlong, int);
+int kstat(char*, uchar*, int);
+long kwrite(int, void*, long);
+int kwstat(char*, uchar*, int);
+Dir* kdirstat(char*);
+Dir* kdirfstat(int);
+int kdirwstat(char*, Dir*);
+int kdirfwstat(int, Dir*);
+long kdirread(int, Dir**);
+int klisten(char*, char*);
+
+Cname* addelem(Cname*, char*);
+void cleancname(Cname*);
+void cnameclose(Cname*);
+
+#pragma varargck argpos iprint 1
--- /dev/null
+++ b/emu/port/inferno.c
@@ -1,0 +1,1060 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include "isa.h"
+#include "runt.h"
+#include "kernel.h"
+
+/*
+ * here because Sys_FileIO is not public
+ */
+extern int srvf2c(char*, char*, Sys_FileIO*);
+
+/*
+ * System types connected to gc
+ */
+uchar FDmap[] = Sys_FD_map;
+uchar FileIOmap[] = Sys_FileIO_map;
+void freeFD(Heap*, int);
+void freeFileIO(Heap*, int);
+Type* TFD;
+Type* TFileIO;
+
+static uchar rmap[] = Sys_FileIO_read_map;
+static uchar wmap[] = Sys_FileIO_write_map;
+static Type* FioTread;
+static Type* FioTwrite;
+static uchar dmap[] = Sys_Dir_map;
+static Type* Tdir;
+
+typedef struct FD FD;
+struct FD
+{
+ Sys_FD fd;
+ Fgrp* grp;
+};
+
+void
+sysinit(void)
+{
+ TFD = dtype(freeFD, sizeof(FD), FDmap, sizeof(FDmap));
+ TFileIO = dtype(freeFileIO, Sys_FileIO_size, FileIOmap, sizeof(FileIOmap));
+
+ /* Support for devsrv.c */
+ FioTread = dtype(freeheap, Sys_FileIO_read_size, rmap, sizeof(rmap));
+ FioTwrite = dtype(freeheap, Sys_FileIO_write_size, wmap, sizeof(wmap));
+
+ /* Support for dirread */
+ Tdir = dtype(freeheap, Sys_Dir_size, dmap, sizeof(dmap));
+}
+
+void
+freeFD(Heap *h, int swept)
+{
+ FD *handle;
+
+ USED(swept);
+
+ handle = H2D(FD*, h);
+
+ release();
+ if(handle->fd.fd >= 0)
+ kfgrpclose(handle->grp, handle->fd.fd);
+ closefgrp(handle->grp);
+ acquire();
+}
+
+void
+freeFileIO(Heap *h, int swept)
+{
+ Sys_FileIO *fio;
+
+ if(swept)
+ return;
+
+ fio = H2D(Sys_FileIO*, h);
+ destroy(fio->read);
+ destroy(fio->write);
+}
+
+Sys_FD*
+mkfd(int fd)
+{
+ Heap *h;
+ Fgrp *fg;
+ FD *handle;
+
+ h = heap(TFD);
+ handle = H2D(FD*, h);
+ handle->fd.fd = fd;
+ fg = up->env->fgrp;
+ handle->grp = fg;
+ incref(&fg->r);
+ return (Sys_FD*)handle;
+}
+#define fdchk(x) ((x) == (Sys_FD*)H ? -1 : (x)->fd)
+
+void
+seterror(char *err, ...)
+{
+ char *estr;
+ va_list arg;
+
+ estr = up->env->errstr;
+ va_start(arg, err);
+ vseprint(estr, estr+ERRMAX, err, arg);
+ va_end(arg);
+}
+
+char*
+syserr(char *s, char *es, Prog *p)
+{
+ Osenv *o;
+
+ o = p->osenv;
+ kstrcpy(s, o->errstr, es - s);
+ return s + strlen(s);
+}
+
+void
+Sys_millisec(void *fp)
+{
+ F_Sys_millisec *f;
+
+ f = fp;
+ *f->ret = osmillisec();
+}
+
+void
+Sys_open(void *fp)
+{
+ int fd;
+ F_Sys_open *f;
+
+ f = fp;
+ destroy(*f->ret);
+ *f->ret = H;
+ release();
+ fd = kopen(string2c(f->s), f->mode);
+ acquire();
+ if(fd == -1)
+ return;
+
+ *f->ret = mkfd(fd);
+}
+
+void
+Sys_pipe(void *fp)
+{
+ Array *a;
+ int fd[2];
+ Sys_FD **sfd;
+ F_Sys_pipe *f;
+
+ f = fp;
+ *f->ret = -1;
+
+ a = f->fds;
+ if(a->len < 2)
+ return;
+ if(kpipe(fd) < 0)
+ return;
+
+ sfd = (Sys_FD**)a->data;
+ destroy(sfd[0]);
+ destroy(sfd[1]);
+ sfd[0] = H;
+ sfd[1] = H;
+ sfd[0] = mkfd(fd[0]);
+ sfd[1] = mkfd(fd[1]);
+ *f->ret = 0;
+}
+
+void
+Sys_fildes(void *fp)
+{
+ F_Sys_fildes *f;
+ int fd;
+
+ f = fp;
+ destroy(*f->ret);
+ *f->ret = H;
+ release();
+ fd = kdup(f->fd, -1);
+ acquire();
+ if(fd == -1)
+ return;
+ *f->ret = mkfd(fd);
+}
+
+void
+Sys_dup(void *fp)
+{
+ F_Sys_dup *f;
+
+ f = fp;
+ release();
+ *f->ret = kdup(f->old, f->new);
+ acquire();
+}
+
+void
+Sys_create(void *fp)
+{
+ int fd;
+ F_Sys_create *f;
+
+ f = fp;
+ destroy(*f->ret);
+ *f->ret = H;
+ release();
+ fd = kcreate(string2c(f->s), f->mode, f->perm);
+ acquire();
+ if(fd == -1)
+ return;
+
+ *f->ret = mkfd(fd);
+}
+
+void
+Sys_remove(void *fp)
+{
+ F_Sys_remove *f;
+
+ f = fp;
+ release();
+ *f->ret = kremove(string2c(f->s));
+ acquire();
+}
+
+void
+Sys_seek(void *fp)
+{
+ F_Sys_seek *f;
+
+ f = fp;
+ release();
+ *f->ret = kseek(fdchk(f->fd), f->off, f->start);
+ acquire();
+}
+
+void
+Sys_unmount(void *fp)
+{
+ F_Sys_unmount *f;
+
+ f = fp;
+ release();
+ *f->ret = kunmount(string2c(f->s1), string2c(f->s2));
+ acquire();
+}
+
+void
+Sys_read(void *fp)
+{
+ int n;
+ F_Sys_read *f;
+
+ f = fp;
+ n = f->n;
+ if(f->buf == (Array*)H || n < 0) {
+ *f->ret = 0;
+ return;
+ }
+ if(n > f->buf->len)
+ n = f->buf->len;
+
+ release();
+ *f->ret = kread(fdchk(f->fd), f->buf->data, n);
+ acquire();
+}
+
+void
+Sys_readn(void *fp)
+{
+ int fd, m, n, t;
+ F_Sys_readn *f;
+
+ f = fp;
+ n = f->n;
+ if(f->buf == (Array*)H || n < 0) {
+ *f->ret = 0;
+ return;
+ }
+ if(n > f->buf->len)
+ n = f->buf->len;
+ fd = fdchk(f->fd);
+
+ release();
+ for(t = 0; t < n; t += m){
+ m = kread(fd, (char*)f->buf->data+t, n-t);
+ if(m <= 0){
+ if(t == 0)
+ t = m;
+ break;
+ }
+ }
+ *f->ret = t;
+ acquire();
+}
+
+void
+Sys_pread(void *fp)
+{
+ int n;
+ F_Sys_pread *f;
+
+ f = fp;
+ n = f->n;
+ if(f->buf == (Array*)H || n < 0) {
+ *f->ret = 0;
+ return;
+ }
+ if(n > f->buf->len)
+ n = f->buf->len;
+
+ release();
+ *f->ret = kpread(fdchk(f->fd), f->buf->data, n, f->off);
+ acquire();
+}
+
+void
+Sys_chdir(void *fp)
+{
+ F_Sys_chdir *f;
+
+ f = fp;
+ release();
+ *f->ret = kchdir(string2c(f->path));
+ acquire();
+}
+
+void
+Sys_write(void *fp)
+{
+ int n;
+ F_Sys_write *f;
+
+ f = fp;
+ n = f->n;
+ if(f->buf == (Array*)H || n < 0) {
+ *f->ret = 0;
+ return;
+ }
+ if(n > f->buf->len)
+ n = f->buf->len;
+
+ release();
+ *f->ret = kwrite(fdchk(f->fd), f->buf->data, n);
+ acquire();
+}
+
+void
+Sys_pwrite(void *fp)
+{
+ int n;
+ F_Sys_pwrite *f;
+
+ f = fp;
+ n = f->n;
+ if(f->buf == (Array*)H || n < 0) {
+ *f->ret = 0;
+ return;
+ }
+ if(n > f->buf->len)
+ n = f->buf->len;
+
+ release();
+ *f->ret = kpwrite(fdchk(f->fd), f->buf->data, n, f->off);
+ acquire();
+}
+
+static void
+unpackdir(Dir *d, Sys_Dir *sd)
+{
+ retstr(d->name, &sd->name);
+ retstr(d->uid, &sd->uid);
+ retstr(d->gid, &sd->gid);
+ retstr(d->muid, &sd->muid);
+ sd->qid.path = d->qid.path;
+ sd->qid.vers = d->qid.vers;
+ sd->qid.qtype = d->qid.type;
+ sd->mode = d->mode;
+ sd->atime = d->atime;
+ sd->mtime = d->mtime;
+ sd->length = d->length;
+ sd->dtype = d->type;
+ sd->dev = d->dev;
+}
+
+static Dir*
+packdir(Sys_Dir *sd)
+{
+ char *nm[4], *p;
+ int i, n;
+ Dir *d;
+
+ nm[0] = string2c(sd->name);
+ nm[1] = string2c(sd->uid);
+ nm[2] = string2c(sd->gid);
+ nm[3] = string2c(sd->muid);
+ n = 0;
+ for(i=0; i<4; i++)
+ n += strlen(nm[i])+1;
+ d = smalloc(sizeof(*d)+n);
+ p = (char*)d+sizeof(*d);
+ for(i=0; i<4; i++){
+ n = strlen(nm[i])+1;
+ memmove(p, nm[i], n);
+ nm[i] = p;
+ p += n;
+ }
+ d->name = nm[0];
+ d->uid = nm[1];
+ d->gid = nm[2];
+ d->muid = nm[3];
+ d->qid.path = sd->qid.path;
+ d->qid.vers = sd->qid.vers;
+ d->qid.type = sd->qid.qtype;
+ d->mode = sd->mode;
+ d->atime = sd->atime;
+ d->mtime = sd->mtime;
+ d->length = sd->length;
+ d->type = sd->dtype;
+ d->dev = sd->dev;
+ return d;
+}
+
+void
+Sys_fstat(void *fp)
+{
+ Dir *d;
+ F_Sys_fstat *f;
+
+ f = fp;
+ f->ret->t0 = -1;
+ release();
+ d = kdirfstat(fdchk(f->fd));
+ acquire();
+ if(d == nil)
+ return;
+ if(waserror() == 0){
+ unpackdir(d, &f->ret->t1);
+ f->ret->t0 = 0;
+ poperror();
+ }
+ free(d);
+}
+
+void
+Sys_stat(void *fp)
+{
+ Dir *d;
+ F_Sys_stat *f;
+
+ f = fp;
+ f->ret->t0 = -1;
+ release();
+ d = kdirstat(string2c(f->s));
+ acquire();
+ if(d == nil)
+ return;
+ if(waserror() == 0){
+ unpackdir(d, &f->ret->t1);
+ f->ret->t0 = 0;
+ poperror();
+ }
+ free(d);
+}
+
+void
+Sys_fd2path(void *fp)
+{
+ F_Sys_fd2path *f;
+ char *s;
+ void *r;
+
+ f = fp;
+ r = *f->ret;
+ *f->ret = H;
+ destroy(r);
+ release();
+ s = kfd2path(fdchk(f->fd));
+ acquire();
+ if(waserror() == 0){
+ retstr(s, f->ret);
+ poperror();
+ }
+ free(s);
+}
+
+void
+Sys_mount(void *fp)
+{
+ F_Sys_mount *f;
+
+ f = fp;
+ release();
+ *f->ret = kmount(fdchk(f->fd), fdchk(f->afd), string2c(f->on), f->flags, string2c(f->spec));
+ acquire();
+}
+
+void
+Sys_bind(void *fp)
+{
+ F_Sys_bind *f;
+
+ f = fp;
+ release();
+ *f->ret = kbind(string2c(f->s), string2c(f->on), f->flags);
+ acquire();
+}
+
+void
+Sys_wstat(void *fp)
+{
+ Dir *d;
+ F_Sys_wstat *f;
+
+ f = fp;
+ d = packdir(&f->d);
+ release();
+ *f->ret = kdirwstat(string2c(f->s), d);
+ acquire();
+ free(d);
+}
+
+void
+Sys_fwstat(void *fp)
+{
+ Dir *d;
+ F_Sys_fwstat *f;
+
+ f = fp;
+ d = packdir(&f->d);
+ release();
+ *f->ret = kdirfwstat(fdchk(f->fd), d);
+ acquire();
+ free(d);
+}
+
+void
+Sys_print(void *fp)
+{
+ int n;
+ Prog *p;
+ Chan *c;
+ char buf[1024], *b = buf;
+ F_Sys_print *f;
+ f = fp;
+ c = up->env->fgrp->fd[1];
+ if(c == nil)
+ return;
+ p = currun();
+
+ release();
+ n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf));
+ if (n >= sizeof(buf)-UTFmax-2)
+ n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf));
+ *f->ret = kwrite(1, b, n);
+ if (b != buf)
+ free(b);
+ acquire();
+}
+
+void
+Sys_fprint(void *fp)
+{
+ int n;
+ Prog *p;
+ char buf[1024], *b = buf;
+ F_Sys_fprint *f;
+
+ f = fp;
+ p = currun();
+ release();
+ n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf));
+ if (n >= sizeof(buf)-UTFmax-2)
+ n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf));
+ *f->ret = kwrite(fdchk(f->fd), b, n);
+ if (b != buf)
+ free(b);
+ acquire();
+}
+
+void
+Sys_werrstr(void *fp)
+{
+ F_Sys_werrstr *f;
+
+ f = fp;
+ *f->ret = 0;
+ kstrcpy(up->env->errstr, string2c(f->s), ERRMAX);
+}
+
+void
+Sys_dial(void *fp)
+{
+ int cfd;
+ char dir[NETPATHLEN], *a, *l;
+ F_Sys_dial *f;
+
+ f = fp;
+ a = string2c(f->addr);
+ l = string2c(f->local);
+ release();
+ f->ret->t0 = kdial(a, l, dir, &cfd);
+ acquire();
+ destroy(f->ret->t1.dfd);
+ f->ret->t1.dfd = H;
+ destroy(f->ret->t1.cfd);
+ f->ret->t1.cfd = H;
+ if(f->ret->t0 == -1)
+ return;
+
+ f->ret->t1.dfd = mkfd(f->ret->t0);
+ f->ret->t0 = 0;
+ f->ret->t1.cfd = mkfd(cfd);
+ retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_announce(void *fp)
+{
+ char dir[NETPATHLEN], *a;
+ F_Sys_announce *f;
+
+ f = fp;
+ a = string2c(f->addr);
+ release();
+ f->ret->t0 = kannounce(a, dir);
+ acquire();
+ destroy(f->ret->t1.dfd);
+ f->ret->t1.dfd = H;
+ destroy(f->ret->t1.cfd);
+ f->ret->t1.cfd = H;
+ if(f->ret->t0 == -1)
+ return;
+
+ f->ret->t1.cfd = mkfd(f->ret->t0);
+ f->ret->t0 = 0;
+ retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_listen(void *fp)
+{
+ F_Sys_listen *f;
+ char dir[NETPATHLEN], *d;
+
+ f = fp;
+ d = string2c(f->c.dir);
+ release();
+ f->ret->t0 = klisten(d, dir);
+ acquire();
+
+ destroy(f->ret->t1.dfd);
+ f->ret->t1.dfd = H;
+ destroy(f->ret->t1.cfd);
+ f->ret->t1.cfd = H;
+ if(f->ret->t0 == -1)
+ return;
+
+ f->ret->t1.cfd = mkfd(f->ret->t0);
+ f->ret->t0 = 0;
+ retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_sleep(void *fp)
+{
+ F_Sys_sleep *f;
+
+ f = fp;
+ release();
+ if(f->period > 0){
+ if(waserror()){
+ acquire();
+ error("");
+ }
+ osenter();
+ *f->ret = limbosleep(f->period);
+ osleave();
+ poperror();
+ }
+ acquire();
+}
+
+void
+Sys_stream(void *fp)
+{
+ Prog *p;
+ uchar *buf;
+ int src, dst;
+ F_Sys_stream *f;
+ int nbytes, t, n;
+
+ f = fp;
+ buf = malloc(f->bufsiz);
+ if(buf == nil) {
+ kwerrstr(Enomem);
+ *f->ret = -1;
+ return;
+ }
+
+ src = fdchk(f->src);
+ dst = fdchk(f->dst);
+
+ p = currun();
+
+ release();
+ t = 0;
+ nbytes = 0;
+ while(p->kill == nil) {
+ n = kread(src, buf+t, f->bufsiz-t);
+ if(n <= 0)
+ break;
+ t += n;
+ if(t >= f->bufsiz) {
+ if(kwrite(dst, buf, t) != t) {
+ t = 0;
+ break;
+ }
+
+ nbytes += t;
+ t = 0;
+ }
+ }
+ if(t != 0) {
+ kwrite(dst, buf, t);
+ nbytes += t;
+ }
+ acquire();
+ free(buf);
+ *f->ret = nbytes;
+}
+
+void
+Sys_export(void *fp)
+{
+ F_Sys_export *f;
+
+ f = fp;
+ release();
+ *f->ret = export(fdchk(f->c), string2c(f->dir), f->flag&Sys_EXPASYNC);
+ acquire();
+}
+
+void
+Sys_file2chan(void *fp)
+{
+ int r;
+ Heap *h;
+ Channel *c;
+ Sys_FileIO *fio;
+ F_Sys_file2chan *f;
+ void *sv;
+
+ h = heap(TFileIO);
+
+ fio = H2D(Sys_FileIO*, h);
+
+ c = cnewc(FioTread, movtmp, 16);
+ fio->read = c;
+
+ c = cnewc(FioTwrite, movtmp, 16);
+ fio->write = c;
+
+ f = fp;
+ sv = *f->ret;
+ *f->ret = fio;
+ destroy(sv);
+
+ release();
+ r = srvf2c(string2c(f->dir), string2c(f->file), fio);
+ acquire();
+ if(r == -1) {
+ *f->ret = H;
+ destroy(fio);
+ }
+}
+
+enum
+{
+ /* the following pctl calls can block and must release the virtual machine */
+ BlockingPctl= Sys_NEWFD|Sys_FORKFD|Sys_NEWNS|Sys_FORKNS|Sys_NEWENV|Sys_FORKENV
+};
+
+void
+Sys_pctl(void *fp)
+{
+ int fd;
+ Prog *p;
+ List *l;
+ Chan *c;
+ volatile struct {Pgrp *np;} np;
+ Pgrp *opg;
+ Chan *dot;
+ Osenv *o;
+ F_Sys_pctl *f;
+ Fgrp *fg, *ofg, *nfg;
+ volatile struct {Egrp *ne;} ne;
+ Egrp *oe;
+
+ f = fp;
+
+ p = currun();
+ if(f->flags & BlockingPctl)
+ release();
+
+ np.np = nil;
+ ne.ne = nil;
+ if(waserror()) {
+ closepgrp(np.np);
+ closeegrp(ne.ne);
+ if(f->flags & BlockingPctl)
+ acquire();
+ *f->ret = -1;
+ return;
+ }
+
+ o = p->osenv;
+ if(f->flags & Sys_NEWFD) {
+ ofg = o->fgrp;
+ nfg = newfgrp(ofg);
+ lock(&ofg->l);
+ /* file descriptors to preserve */
+ for(l = f->movefd; l != H; l = l->tail) {
+ fd = *(int*)l->data;
+ if(fd >= 0 && fd <= ofg->maxfd) {
+ c = ofg->fd[fd];
+ if(c != nil && fd < nfg->nfd && nfg->fd[fd] == nil) {
+ incref(&c->r);
+ nfg->fd[fd] = c;
+ if(nfg->maxfd < fd)
+ nfg->maxfd = fd;
+ }
+ }
+ }
+ unlock(&ofg->l);
+ o->fgrp = nfg;
+ closefgrp(ofg);
+ }
+ else
+ if(f->flags & Sys_FORKFD) {
+ ofg = o->fgrp;
+ fg = dupfgrp(ofg);
+ /* file descriptors to close */
+ for(l = f->movefd; l != H; l = l->tail)
+ kclose(*(int*)l->data);
+ o->fgrp = fg;
+ closefgrp(ofg);
+ }
+
+ if(f->flags & Sys_NEWNS) {
+ np.np = newpgrp();
+ dot = o->pgrp->dot;
+ np.np->dot = cclone(dot);
+ np.np->slash = cclone(dot);
+ cnameclose(np.np->slash->name);
+ np.np->slash->name = newcname("/");
+ np.np->nodevs = o->pgrp->nodevs;
+ opg = o->pgrp;
+ o->pgrp = np.np;
+ np.np = nil;
+ closepgrp(opg);
+ }
+ else
+ if(f->flags & Sys_FORKNS) {
+ np.np = newpgrp();
+ pgrpcpy(np.np, o->pgrp);
+ opg = o->pgrp;
+ o->pgrp = np.np;
+ np.np = nil;
+ closepgrp(opg);
+ }
+
+ if(f->flags & Sys_NEWENV) {
+ oe = o->egrp;
+ o->egrp = newegrp();
+ closeegrp(oe);
+ }
+ else
+ if(f->flags & Sys_FORKENV) {
+ ne.ne = newegrp();
+ egrpcpy(ne.ne, o->egrp);
+ oe = o->egrp;
+ o->egrp = ne.ne;
+ ne.ne = nil;
+ closeegrp(oe);
+ }
+
+ if(f->flags & Sys_NEWPGRP)
+ newgrp(p);
+
+ if(f->flags & Sys_NODEVS)
+ o->pgrp->nodevs = 1;
+
+ poperror();
+
+ if(f->flags & BlockingPctl)
+ acquire();
+
+ *f->ret = p->pid;
+}
+
+void
+Sys_dirread(void *fp)
+{
+
+ Dir *b;
+ int i, n;
+ Heap *h;
+ uchar *d;
+ void *r;
+ F_Sys_dirread *f;
+
+ f = fp;
+ f->ret->t0 = -1;
+ r = f->ret->t1;
+ f->ret->t1 = H;
+ destroy(r);
+ release();
+ n = kdirread(fdchk(f->fd), &b);
+ acquire();
+ if(n <= 0) {
+ f->ret->t0 = n;
+ free(b);
+ return;
+ }
+ if(waserror()){
+ free(b);
+ return;
+ }
+ h = heaparray(Tdir, n);
+ poperror();
+ d = H2D(Array*, h)->data;
+ for(i = 0; i < n; i++) {
+ unpackdir(b+i, (Sys_Dir*)d);
+ d += Sys_Dir_size;
+ }
+ f->ret->t0 = n;
+ f->ret->t1 = H2D(Array*, h);
+ free(b);
+}
+
+void
+Sys_fauth(void *fp)
+{
+ int fd;
+ F_Sys_fauth *f;
+ void *r;
+
+ f = fp;
+ r = *f->ret;
+ *f->ret = H;
+ destroy(r);
+ release();
+ fd = kfauth(fdchk(f->fd), string2c(f->aname));
+ acquire();
+ if(fd >= 0)
+ *f->ret = mkfd(fd);
+}
+
+void
+Sys_fversion(void *fp)
+{
+ void *r;
+ F_Sys_fversion *f;
+ int n;
+ char buf[20], *s;
+
+ f = fp;
+ f->ret->t0 = -1;
+ r = f->ret->t1;
+ f->ret->t1 = H;
+ destroy(r);
+ s = string2c(f->version);
+ n = strlen(s);
+ if(n >= sizeof(buf)-1)
+ n = sizeof(buf)-1;
+ memmove(buf, s, n);
+ buf[n] = 0;
+ release();
+ n = kfversion(fdchk(f->fd), f->msize, buf, sizeof(buf));
+ acquire();
+ if(n >= 0){
+ f->ret->t0 = f->msize;
+ retnstr(buf, n, &f->ret->t1);
+ }
+}
+
+void
+Sys_iounit(void *fp)
+{
+ F_Sys_iounit *f;
+
+ f = fp;
+ release();
+ *f->ret = kiounit(fdchk(f->fd));
+ acquire();
+}
+
+void
+ccom(Progq **cl, Prog *p)
+{
+ volatile struct {Progq **cl;} vcl;
+
+ cqadd(cl, p);
+ vcl.cl = cl;
+ if(waserror()) {
+ if(p->ptr != nil) { /* no killcomm */
+ cqdelp(vcl.cl, p);
+ p->ptr = nil;
+ }
+ nexterror();
+ }
+ cblock(p);
+ poperror();
+}
+
+void
+crecv(Channel *c, void *ip)
+{
+ Prog *p;
+ REG rsav;
+
+ if(c->send->prog == nil && c->size == 0) {
+ p = currun();
+ p->ptr = ip;
+ ccom(&c->recv, p);
+ return;
+ }
+
+ rsav = R;
+ R.s = &c;
+ R.d = ip;
+ irecv();
+ R = rsav;
+}
+
+void
+csend(Channel *c, void *ip)
+{
+ Prog *p;
+ REG rsav;
+
+ if(c->recv->prog == nil && (c->buf == H || c->size == c->buf->len)) {
+ p = currun();
+ p->ptr = ip;
+ ccom(&c->send, p);
+ return;
+ }
+
+ rsav = R;
+ R.s = ip;
+ R.d = &c;
+ isend();
+ R = rsav;
+}
--- /dev/null
+++ b/emu/port/ip.h
@@ -1,0 +1,65 @@
+enum
+{
+ IPaddrlen = 16, /* IPv6 */
+ IPv4addrlen = 4, /* IPv4 */
+ IPv4off = 12, /* length of IPv6 prefix for IPv4 addresses */
+ Udphdrlen = 3*IPaddrlen+2*2,
+ OUdphdrlen = 2*IPaddrlen+2*2,
+ OUdphdrlenv4 = 2*IPv4addrlen+2*2,
+
+ S_TCP = 0,
+ S_UDP
+};
+
+typedef struct Fs Fs;
+typedef struct Proto Proto;
+typedef struct Conv Conv;
+
+extern int so_socket(int type);
+extern void so_connect(int, uchar*, ushort);
+extern void so_getsockname(int, uchar*, ushort*);
+extern void so_bind(int, int, uchar*, ushort);
+extern void so_listen(int);
+extern int so_accept(int, uchar*, ushort*);
+extern int so_getservbyname(char*, char*, char*);
+extern int so_gethostbyname(char*, char**, int);
+extern int so_gethostbyaddr(char*, char**, int);
+extern int so_recv(int, void*, int, void*, int);
+extern int so_send(int, void*, int, void*, int);
+extern void so_close(int);
+extern int so_hangup(int, int);
+extern void so_setsockopt(int, int, int);
+extern int so_mustbind(int, int);
+extern void so_keepalive(int, int);
+
+
+extern void hnputl(void *p, unsigned long v);
+extern void hnputs(void *p, ushort v);
+extern unsigned long nhgetl(void *p);
+extern ushort nhgets(void *p);
+extern unsigned long parseip(uchar *to, char *from);
+extern int parsemac(uchar *to, char *from, int len);
+extern char* v4parseip(uchar*, char*);
+extern int bipipe(int[]);
+
+extern int isv4(uchar*);
+extern void v4tov6(uchar *v6, uchar *v4);
+extern int v6tov4(uchar *v4, uchar *v6);
+extern int eipfmt(Fmt*);
+
+#define ipmove(x, y) memmove(x, y, IPaddrlen)
+#define ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) )
+
+extern uchar IPv4bcast[IPaddrlen];
+extern uchar IPv4bcastobs[IPaddrlen];
+extern uchar IPv4allsys[IPaddrlen];
+extern uchar IPv4allrouter[IPaddrlen];
+extern uchar IPnoaddr[IPaddrlen];
+extern uchar v4prefix[IPaddrlen];
+extern uchar IPallbits[IPaddrlen];
+extern uchar v6Unspecified[IPaddrlen];
+
+extern void arpadd(char*, char*, int);
+extern int arpwrite(char*, int);
+
+extern int Fsproto(Fs*, Proto*);
--- /dev/null
+++ b/emu/port/ipaux.c
@@ -1,0 +1,525 @@
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+#include "error.h"
+
+/*
+ * well known IP addresses
+ */
+uchar IPv4bcast[IPaddrlen] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+uchar IPv4allsys[IPaddrlen] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0xff, 0xff,
+ 0xe0, 0, 0, 0x01
+};
+uchar IPv4allrouter[IPaddrlen] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0xff, 0xff,
+ 0xe0, 0, 0, 0x02
+};
+uchar IPallbits[IPaddrlen] = {
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+
+uchar IPnoaddr[IPaddrlen];
+
+/*
+ * prefix of all v4 addresses
+ */
+uchar v4prefix[IPaddrlen] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0xff, 0xff,
+ 0, 0, 0, 0
+};
+
+/*
+ * well known IPv6 addresses
+ */
+uchar v6Unspecified[IPaddrlen] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+uchar v6loopback[IPaddrlen] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x01
+};
+uchar v6linklocal[IPaddrlen] = {
+ 0xfe, 0x80, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+uchar v6linklocalmask[IPaddrlen] = {
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+int v6linklocalprefix = 8;
+uchar v6sitelocal[IPaddrlen] = {
+ 0xfe, 0xc0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+uchar v6sitelocalmask[IPaddrlen] = {
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+int v6sitelocalprefix = 6;
+uchar v6glunicast[IPaddrlen] = {
+ 0x08, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+uchar v6multicast[IPaddrlen] = {
+ 0xff, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+uchar v6multicastmask[IPaddrlen] = {
+ 0xff, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+int v6multicastprefix = 1;
+uchar v6allnodesN[IPaddrlen] = {
+ 0xff, 0x01, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x01
+};
+uchar v6allnodesNmask[IPaddrlen] = {
+ 0xff, 0xff, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+int v6allnodesprefix = 2;
+uchar v6allnodesL[IPaddrlen] = {
+ 0xff, 0x02, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x01
+};
+uchar v6allnodesLmask[IPaddrlen] = {
+ 0xff, 0xff, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+int v6allnodesLprefix = 2;
+uchar v6allroutersN[IPaddrlen] = {
+ 0xff, 0x01, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x02
+};
+uchar v6allroutersL[IPaddrlen] = {
+ 0xff, 0x02, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x02
+};
+uchar v6allroutersS[IPaddrlen] = {
+ 0xff, 0x05, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x02
+};
+uchar v6solicitednode[IPaddrlen] = {
+ 0xff, 0x02, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x01,
+ 0xff, 0, 0, 0
+};
+uchar v6solicitednodemask[IPaddrlen] = {
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0, 0x0, 0x0
+};
+int v6solicitednodeprefix = 13;
+
+enum
+{
+ Isprefix= 16,
+};
+
+int
+eipfmt(Fmt *f)
+{
+ char buf[5*8];
+ static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
+ static char *ifmt = "%d.%d.%d.%d";
+ uchar *p, ip[16];
+ ulong *lp;
+ ushort s;
+ int i, j, n, eln, eli, m, v;
+
+ switch(f->r) {
+ case 'E': /* Ethernet address */
+ p = va_arg(f->args, uchar*);
+ return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
+ case 'I': /* Ip address */
+ p = va_arg(f->args, uchar*);
+common:
+ if(memcmp(p, v4prefix, 12) == 0)
+ return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]);
+
+ /* find longest elision */
+ eln = eli = -1;
+ for(i = 0; i < 16; i += 2){
+ for(j = i; j < 16; j += 2)
+ if(p[j] != 0 || p[j+1] != 0)
+ break;
+ if(j > i && j - i > eln){
+ eli = i;
+ eln = j - i;
+ }
+ }
+
+ /* print with possible elision */
+ n = 0;
+ for(i = 0; i < 16; i += 2){
+ if(i == eli){
+ n += sprint(buf+n, "::");
+ i += eln;
+ if(i >= 16)
+ break;
+ } else if(i != 0)
+ n += sprint(buf+n, ":");
+ s = (p[i]<<8) + p[i+1];
+ n += sprint(buf+n, "%ux", s);
+ }
+ return fmtstrcpy(f, buf);
+
+ case 'i': /* v6 address as 4 longs */
+ lp = va_arg(f->args, ulong*);
+ for(i = 0; i < 4; i++)
+ hnputl(ip+4*i, *lp++);
+ p = ip;
+ goto common;
+
+ case 'V': /* v4 ip address */
+ p = va_arg(f->args, uchar*);
+ return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]);
+
+ case 'M': /* ip mask */
+ p = va_arg(f->args, uchar*);
+
+ /* look for a prefix mask */
+ for(i = 0; i < 16; i++)
+ if(p[i] != 0xff)
+ break;
+ for(j = i+1; j < 16; j++)
+ if(p[j] != 0)
+ goto common;
+ n = 8*i;
+ if(i < IPaddrlen){
+ v = p[i];
+ for(m = 0x80; m != 0; m >>= 1){
+ if((v & m) == 0)
+ break;
+ v &= ~m;
+ n++;
+ }
+ if(v != 0)
+ goto common;
+ }
+
+ /* got one, use /xx format */
+ return fmtprint(f, "/%d", n);
+
+ }
+ return fmtstrcpy(f, "(eipfmt)");
+}
+
+#define CLASS(p) ((*(uchar*)(p))>>6)
+
+char*
+v4parseip(uchar *to, char *from)
+{
+ int i;
+ char *p;
+
+ p = from;
+ for(i = 0; i < 4 && *p; i++){
+ to[i] = strtoul(p, &p, 0);
+ if(*p == '.')
+ p++;
+ }
+ switch(CLASS(to)){
+ case 0: /* class A - 1 uchar net */
+ case 1:
+ if(i == 3){
+ to[3] = to[2];
+ to[2] = to[1];
+ to[1] = 0;
+ } else if(i == 2){
+ to[3] = to[1];
+ to[1] = 0;
+ }
+ break;
+ case 2: /* class B - 2 uchar net */
+ if(i == 3){
+ to[3] = to[2];
+ to[2] = 0;
+ }
+ break;
+ }
+ return p;
+}
+
+int
+isv4(uchar *ip)
+{
+ return memcmp(ip, v4prefix, IPv4off) == 0;
+}
+
+
+/*
+ * the following routines are unrolled with no memset's to speed
+ * up the usual case
+ */
+void
+v4tov6(uchar *v6, uchar *v4)
+{
+ v6[0] = 0;
+ v6[1] = 0;
+ v6[2] = 0;
+ v6[3] = 0;
+ v6[4] = 0;
+ v6[5] = 0;
+ v6[6] = 0;
+ v6[7] = 0;
+ v6[8] = 0;
+ v6[9] = 0;
+ v6[10] = 0xff;
+ v6[11] = 0xff;
+ v6[12] = v4[0];
+ v6[13] = v4[1];
+ v6[14] = v4[2];
+ v6[15] = v4[3];
+}
+
+int
+v6tov4(uchar *v4, uchar *v6)
+{
+ if(v6[0] == 0
+ && v6[1] == 0
+ && v6[2] == 0
+ && v6[3] == 0
+ && v6[4] == 0
+ && v6[5] == 0
+ && v6[6] == 0
+ && v6[7] == 0
+ && v6[8] == 0
+ && v6[9] == 0
+ && v6[10] == 0xff
+ && v6[11] == 0xff)
+ {
+ v4[0] = v6[12];
+ v4[1] = v6[13];
+ v4[2] = v6[14];
+ v4[3] = v6[15];
+ return 0;
+ } else {
+ memset(v4, 0, 4);
+ return -1;
+ }
+}
+
+ulong
+parseip(uchar *to, char *from)
+{
+ int i, elipsis = 0, v4 = 1;
+ ulong x;
+ char *p, *op;
+
+ memset(to, 0, IPaddrlen);
+ p = from;
+ for(i = 0; i < 16 && *p; i+=2){
+ op = p;
+ x = strtoul(p, &p, 16);
+ if(*p == '.' || (*p == 0 && i == 0)){
+ p = v4parseip(to+i, op);
+ i += 4;
+ break;
+ } else {
+ to[i] = x>>8;
+ to[i+1] = x;
+ }
+ if(*p == ':'){
+ v4 = 0;
+ if(*++p == ':'){
+ elipsis = i+2;
+ p++;
+ }
+ }
+ }
+ if(i < 16){
+ memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis);
+ memset(&to[elipsis], 0, 16-i);
+ }
+ if(v4){
+ to[10] = to[11] = 0xff;
+ return nhgetl(to+12);
+ } else
+ return 6;
+}
+
+/*
+ * hack to allow ip v4 masks to be entered in the old
+ * style
+ */
+ulong
+parseipmask(uchar *to, char *from)
+{
+ ulong x;
+ int i;
+ uchar *p;
+
+ if(*from == '/'){
+ /* as a number of prefix bits */
+ i = atoi(from+1);
+ if(i < 0)
+ i = 0;
+ if(i > 128)
+ i = 128;
+ memset(to, 0, IPaddrlen);
+ for(p = to; i >= 8; i -= 8)
+ *p++ = 0xff;
+ if(i > 0)
+ *p = ~((1<<(8-i))-1);
+ x = nhgetl(to+IPv4off);
+ } else {
+ /* as a straight bit mask */
+ x = parseip(to, from);
+ if(memcmp(to, v4prefix, IPv4off) == 0)
+ memset(to, 0xff, IPv4off);
+ }
+ return x;
+}
+
+void
+maskip(uchar *from, uchar *mask, uchar *to)
+{
+ int i;
+
+ for(i = 0; i < IPaddrlen; i++)
+ to[i] = from[i] & mask[i];
+}
+
+uchar classmask[4][16] = {
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00,
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00,
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x00,0x00,
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0x00,
+};
+
+uchar*
+defmask(uchar *ip)
+{
+ if(isv4(ip))
+ return classmask[ip[IPv4off]>>6];
+ else {
+ if(ipcmp(ip, v6loopback) == 0)
+ return IPallbits;
+ else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0)
+ return v6linklocalmask;
+ else if(memcmp(ip, v6sitelocal, v6sitelocalprefix) == 0)
+ return v6sitelocalmask;
+ else if(memcmp(ip, v6solicitednode, v6solicitednodeprefix) == 0)
+ return v6solicitednodemask;
+ else if(memcmp(ip, v6multicast, v6multicastprefix) == 0)
+ return v6multicastmask;
+ return IPallbits;
+ }
+}
+
+/*
+ * parse a hex mac address
+ */
+int
+parsemac(uchar *to, char *from, int len)
+{
+ char nip[4];
+ char *p;
+ int i;
+
+ p = from;
+ memset(to, 0, len);
+ for(i = 0; i < len; i++){
+ if(p[0] == '\0' || p[1] == '\0')
+ break;
+
+ nip[0] = p[0];
+ nip[1] = p[1];
+ nip[2] = '\0';
+ p += 2;
+
+ to[i] = strtoul(nip, 0, 16);
+ if(*p == ':')
+ p++;
+ }
+ return i;
+}
+
+void
+hnputl(void *p, unsigned long v)
+{
+ unsigned char *a;
+
+ a = p;
+ a[0] = v>>24;
+ a[1] = v>>16;
+ a[2] = v>>8;
+ a[3] = v;
+}
+
+void
+hnputs(void *p, unsigned short v)
+{
+ unsigned char *a;
+
+ a = p;
+ a[0] = v>>8;
+ a[1] = v;
+}
+
+unsigned long
+nhgetl(void *p)
+{
+ unsigned char *a;
+ a = p;
+ return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
+}
+
+unsigned short
+nhgets(void *p)
+{
+ unsigned char *a;
+ a = p;
+ return (a[0]<<8)|(a[1]<<0);
+}
--- /dev/null
+++ b/emu/port/ipif-posix.c
@@ -1,0 +1,414 @@
+#ifdef sun
+#define uint uxuint
+#define ulong uxulong
+#define ushort uxushort
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#undef ulong
+#undef ushort
+#undef uint
+
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+#include "error.h"
+
+char Enotv4[] = "address not IPv4";
+
+static void
+ipw6(uchar *a, ulong w)
+{
+ memmove(a, v4prefix, IPv4off);
+ memmove(a+IPv4off, &w, IPv4addrlen);
+}
+
+int
+so_socket(int type)
+{
+ int fd, one;
+
+ switch(type) {
+ default:
+ error("bad protocol type");
+ case S_TCP:
+ type = SOCK_STREAM;
+ break;
+ case S_UDP:
+ type = SOCK_DGRAM;
+ break;
+ }
+
+ fd = socket(AF_INET, type, 0);
+ if(fd < 0)
+ oserror();
+ if(type == SOCK_DGRAM){
+ one = 1;
+ setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+ }else{
+ one = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+ }
+ return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+ uchar *h = hdr;
+
+ osenter();
+ if(hdr == 0)
+ r = write(sock, va, len);
+ else {
+ memset(&sa, 0, sizeof(sa));
+ sin = (struct sockaddr_in*)&sa;
+ sin->sin_family = AF_INET;
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(&sin->sin_addr, h, 4);
+ memmove(&sin->sin_port, h+8, 2);
+ break;
+ case OUdphdrlen:
+ v6tov4((uchar*)&sin->sin_addr, h);
+ memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */
+ break;
+ default:
+ v6tov4((uchar*)&sin->sin_addr, h);
+ memmove(&sin->sin_port, h+3*IPaddrlen, 2);
+ break;
+ }
+ r = sendto(sock, va, len, 0, &sa, sizeof(sa));
+ }
+ osleave();
+ return r;
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ socklen_t l;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+ uchar h[Udphdrlen];
+
+ osenter();
+ if(hdr == 0)
+ r = read(sock, va, len);
+ else {
+ sin = (struct sockaddr_in*)&sa;
+ l = sizeof(sa);
+ r = recvfrom(sock, va, len, 0, &sa, &l);
+ if(r >= 0) {
+ memset(h, 0, sizeof(h));
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(h, &sin->sin_addr, IPv4addrlen);
+ memmove(h+2*IPv4addrlen, &sin->sin_port, 2);
+ break;
+ case OUdphdrlen:
+ v4tov6(h, (uchar*)&sin->sin_addr);
+ memmove(h+2*IPaddrlen, &sin->sin_port, 2);
+ break;
+ default:
+ v4tov6(h, (uchar*)&sin->sin_addr);
+ memmove(h+3*IPaddrlen, &sin->sin_port, 2);
+ break;
+ }
+
+ /* alas there's no way to get the local addr/port correctly. Pretend. */
+ memset(&sa, 0, sizeof(sa));
+ getsockname(sock, &sa, &l);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen);
+ memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2);
+ break;
+ case OUdphdrlen:
+ v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+ memmove(h+2*IPaddrlen+2, &sin->sin_port, 2);
+ break;
+ default:
+ v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+ v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */
+ memmove(h+3*IPaddrlen+2, &sin->sin_port, 2);
+ break;
+ }
+ memmove(hdr, h, hdrlen);
+ }
+ }
+ osleave();
+ return r;
+}
+
+void
+so_close(int sock)
+{
+ close(sock);
+}
+
+void
+so_connect(int fd, uchar *raddr, ushort rport)
+{
+ int r;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ if(!isv4(raddr))
+ error(Enotv4);
+
+ memset(&sa, 0, sizeof(sa));
+ sin = (struct sockaddr_in*)&sa;
+ sin->sin_family = AF_INET;
+ hnputs(&sin->sin_port, rport);
+ memmove(&sin->sin_addr.s_addr, raddr+IPv4off, IPv4addrlen);
+
+ osenter();
+ r = connect(fd, &sa, sizeof(sa));
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+void
+so_getsockname(int fd, uchar *laddr, ushort *lport)
+{
+ socklen_t len;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ len = sizeof(sa);
+ if(getsockname(fd, &sa, &len) < 0)
+ oserror();
+
+ sin = (struct sockaddr_in*)&sa;
+ if(sin->sin_family != AF_INET || len != sizeof(*sin))
+ error(Enotv4);
+
+ ipw6(laddr, sin->sin_addr.s_addr);
+ *lport = nhgets(&sin->sin_port);
+}
+
+void
+so_listen(int fd)
+{
+ int r;
+
+ osenter();
+ r = listen(fd, 256);
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+int
+so_accept(int fd, uchar *raddr, ushort *rport)
+{
+ int nfd;
+ socklen_t len;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in*)&sa;
+
+ len = sizeof(sa);
+ osenter();
+ nfd = accept(fd, &sa, &len);
+ osleave();
+ if(nfd < 0)
+ oserror();
+
+ if(sin->sin_family != AF_INET || len != sizeof(*sin))
+ error(Enotv4);
+
+ ipw6(raddr, sin->sin_addr.s_addr);
+ *rport = nhgets(&sin->sin_port);
+ return nfd;
+}
+
+void
+so_bind(int fd, int su, uchar *addr, ushort port)
+{
+ int i, one;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in*)&sa;
+
+ one = 1;
+ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ print("setsockopt: %s", up->genbuf);
+ }
+
+ if(su) {
+ for(i = 600; i < 1024; i++) {
+ memset(&sa, 0, sizeof(sa));
+ sin->sin_family = AF_INET;
+ memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+ hnputs(&sin->sin_port, i);
+
+ if(bind(fd, &sa, sizeof(sa)) >= 0)
+ return;
+ }
+ oserror();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sin->sin_family = AF_INET;
+ memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+ hnputs(&sin->sin_port, port);
+
+ if(bind(fd, &sa, sizeof(sa)) < 0)
+ oserror();
+}
+
+int
+so_gethostbyname(char *host, char**hostv, int n)
+{
+ int i;
+ char buf[32];
+ uchar *p;
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if(hp == 0)
+ return 0;
+
+ for(i = 0; hp->h_addr_list[i] && i < n; i++) {
+ p = (uchar*)hp->h_addr_list[i];
+ sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
+ hostv[i] = strdup(buf);
+ if(hostv[i] == 0)
+ break;
+ }
+ return i;
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+ int i;
+ struct hostent *hp;
+ unsigned long straddr;
+
+ straddr = inet_addr(addr);
+ if(straddr == -1)
+ return 0;
+
+ hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET);
+ if(hp == 0)
+ return 0;
+
+ hostv[0] = strdup(hp->h_name);
+ if(hostv[0] == 0)
+ return 0;
+ for(i = 1; hp->h_aliases[i-1] && i < n; i++) {
+ hostv[i] = strdup(hp->h_aliases[i-1]);
+ if(hostv[i] == 0)
+ break;
+ }
+ return i;
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+ ushort p;
+ struct servent *s;
+
+ s = getservbyname(service, net);
+ if(s == 0)
+ return -1;
+ p = s->s_port;
+ sprint(port, "%d", nhgets(&p));
+ return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+ int r;
+ static struct linger l = {1, 0};
+
+ osenter();
+ if(nolinger)
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+ r = shutdown(fd, 2);
+ if(r >= 0)
+ r = close(fd);
+ osleave();
+ return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+#ifdef SIOCGARP
+ struct arpreq a;
+ struct sockaddr_in pa;
+ int s;
+ uchar addr[IPaddrlen];
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ memset(&a, 0, sizeof(a));
+ memset(&pa, 0, sizeof(pa));
+ pa.sin_family = AF_INET;
+ pa.sin_port = 0;
+ parseip(addr, ipaddr);
+ if(!isv4(addr)){
+ close(s);
+ error(Ebadarg);
+ }
+ memmove(&pa.sin_addr, ipaddr+IPv4off, IPv4addrlen);
+ memmove(&a.arp_pa, &pa, sizeof(pa));
+ while(ioctl(s, SIOCGARP, &a) != -1) {
+ ioctl(s, SIOCDARP, &a);
+ memset(&a.arp_ha, 0, sizeof(a.arp_ha));
+ }
+ a.arp_ha.sa_family = AF_UNSPEC;
+ parsemac((uchar*)a.arp_ha.sa_data, eaddr, 6);
+ a.arp_flags = ATF_PERM;
+ if(ioctl(s, SIOCSARP, &a) == -1) {
+ oserrstr(up->env->errstr, ERRMAX);
+ close(s);
+ error(up->env->errstr);
+ }
+ close(s);
+#else
+ error("arp not implemented");
+#endif
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+ return restricted || port != 0;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+ int on;
+
+ on = 1;
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+#ifdef TCP_KEEPIDLE
+ if(ms <= 120000)
+ ms = 120000;
+ ms /= 1000;
+ setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&ms, sizeof(ms));
+#endif
+}
--- /dev/null
+++ b/emu/port/ipif6-posix.c
@@ -1,0 +1,415 @@
+#ifdef sun
+#define uint uxuint
+#define ulong uxulong
+#define ushort uxushort
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#undef ulong
+#undef ushort
+#undef uint
+
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+#include "error.h"
+
+int
+so_socket(int type)
+{
+ int fd, one;
+ int v6only;
+
+ switch(type) {
+ default:
+ error("bad protocol type");
+ case S_TCP:
+ type = SOCK_STREAM;
+ break;
+ case S_UDP:
+ type = SOCK_DGRAM;
+ break;
+ }
+
+ fd = socket(AF_INET6, type, 0);
+ if(fd < 0)
+ oserror();
+
+ /* OpenBSD has v6only fixed to 1, so the following will fail */
+ v6only = 0;
+ if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only, sizeof v6only) < 0)
+ oserror();
+
+ if(type == SOCK_DGRAM){
+ one = 1;
+ setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+ }else{
+ one = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+ }
+ return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+ char *h = hdr;
+
+ osenter();
+ if(hdr == 0)
+ r = write(sock, va, len);
+ else {
+ memset(&sa, 0, sizeof(sa));
+ sin6 = (struct sockaddr_in6*)&sa;
+ sin6->sin6_family = AF_INET6;
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ v4tov6((uchar*)&sin6->sin6_addr, h);
+ memmove(&sin6->sin6_port, h+8, 2);
+ break;
+ case OUdphdrlen:
+ memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+ memmove(&sin6->sin6_port, h+2*IPaddrlen, 2); /* rport */
+ break;
+ default:
+ memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+ memmove(&sin6->sin6_port, h+3*IPaddrlen, 2);
+ break;
+ }
+ r = sendto(sock, va, len, 0, (struct sockaddr*)sin6, sizeof(*sin6));
+ }
+ osleave();
+ return r;
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r, l;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+ char h[Udphdrlen];
+
+ osenter();
+ if(hdr == 0)
+ r = read(sock, va, len);
+ else {
+ sin6 = (struct sockaddr_in6*)&sa;
+ l = sizeof(sa);
+ r = recvfrom(sock, va, len, 0, (struct sockaddr*)&sa, &l);
+ if(r >= 0) {
+ memset(h, 0, sizeof(h));
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ if(v6tov4(h, (uchar*)&sin6->sin6_addr) < 0) {
+ osleave();
+ error("OUdphdrlenv4 with IPv6 address");
+ }
+ memmove(h+2*IPv4addrlen, &sin6->sin6_port, 2);
+ break;
+ case OUdphdrlen:
+ memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen, &sin6->sin6_port, 2);
+ break;
+ default:
+ memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+3*IPaddrlen, &sin6->sin6_port, 2);
+ break;
+ }
+
+ /* alas there's no way to get the local addr/port correctly. Pretend. */
+ memset(&sa, 0, sizeof(sa));
+ l = sizeof(sa);
+ getsockname(sock, (struct sockaddr*)&sa, &l);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ /*
+ * we get v6Unspecified/noaddr if local address cannot be determined.
+ * that's reasonable for ipv4 too.
+ */
+ if(ipcmp(v6Unspecified, (uchar*)&sin6->sin6_addr) != 0
+ && v6tov4(h+IPv4addrlen, (uchar*)&sin6->sin6_addr) < 0) {
+ osleave();
+ error("OUdphdrlenv4 with IPv6 address");
+ }
+ memmove(h+2*IPv4addrlen+2, &sin6->sin6_port, 2);
+ break;
+ case OUdphdrlen:
+ memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen+2, &sin6->sin6_port, 2);
+ break;
+ default:
+ memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen); /* ifcaddr */
+ memmove(h+3*IPaddrlen+2, &sin6->sin6_port, 2);
+ break;
+ }
+ memmove(hdr, h, hdrlen);
+ }
+ }
+ osleave();
+ return r;
+}
+
+void
+so_close(int sock)
+{
+ close(sock);
+}
+
+void
+so_connect(int fd, uchar *raddr, ushort rport)
+{
+ int r;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ memset(&sa, 0, sizeof(sa));
+ sin6 = (struct sockaddr_in6*)&sa;
+ sin6->sin6_family = AF_INET6;
+ hnputs(&sin6->sin6_port, rport);
+ memmove((uchar*)&sin6->sin6_addr, raddr, IPaddrlen);
+
+ osenter();
+ r = connect(fd, (struct sockaddr*)sin6, sizeof(*sin6));
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+void
+so_getsockname(int fd, uchar *laddr, ushort *lport)
+{
+ int len;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ len = sizeof(*sin6);
+ if(getsockname(fd, (struct sockaddr*)&sa, &len) < 0)
+ oserror();
+
+ sin6 = (struct sockaddr_in6*)&sa;
+ if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+ error("not AF_INET6");
+
+ memmove(laddr, &sin6->sin6_addr, IPaddrlen);
+ *lport = nhgets(&sin6->sin6_port);
+}
+
+void
+so_listen(int fd)
+{
+ int r;
+
+ osenter();
+ r = listen(fd, 256);
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+int
+so_accept(int fd, uchar *raddr, ushort *rport)
+{
+ int nfd, len;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6*)&sa;
+
+ len = sizeof(*sin6);
+ osenter();
+ nfd = accept(fd, (struct sockaddr*)&sa, &len);
+ osleave();
+ if(nfd < 0)
+ oserror();
+
+ if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+ error("not AF_INET6");
+
+ memmove(raddr, &sin6->sin6_addr, IPaddrlen);
+ *rport = nhgets(&sin6->sin6_port);
+ return nfd;
+}
+
+void
+so_bind(int fd, int su, uchar *addr, ushort port)
+{
+ int i, one;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6*)&sa;
+
+ one = 1;
+ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ print("setsockopt: %s", up->genbuf);
+ }
+
+ if(su) {
+ for(i = 600; i < 1024; i++) {
+ memset(&sa, 0, sizeof(sa));
+ sin6->sin6_family = AF_INET6;
+ memmove(&sin6->sin6_addr, addr, IPaddrlen);
+ hnputs(&sin6->sin6_port, i);
+
+ if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) >= 0)
+ return;
+ }
+ oserror();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sin6->sin6_family = AF_INET6;
+ memmove(&sin6->sin6_addr, addr, IPaddrlen);
+ hnputs(&sin6->sin6_port, port);
+
+ if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) < 0)
+ oserror();
+}
+
+static int
+resolve(char *name, char **hostv, int n, int isnumeric)
+{
+ int i;
+ struct addrinfo *res0, *r;
+ char buf[5*8];
+ uchar addr[IPaddrlen];
+ struct addrinfo hints;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = isnumeric? AI_NUMERICHOST: 0;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if(getaddrinfo(name, nil, &hints, &res0) < 0)
+ return 0;
+ i = 0;
+ for(r = res0; r != nil && i < n; r = r->ai_next) {
+ if(r->ai_family == AF_INET)
+ v4tov6(addr, (uchar*)&((struct sockaddr_in*)r->ai_addr)->sin_addr);
+ else if(r->ai_family == AF_INET6)
+ memmove(addr, &((struct sockaddr_in6*)r->ai_addr)->sin6_addr, IPaddrlen);
+ else
+ continue;
+
+ snprint(buf, sizeof buf, "%I", addr);
+ hostv[i++] = strdup(buf);
+ }
+
+ freeaddrinfo(res0);
+ return i;
+}
+
+int
+so_gethostbyname(char *host, char **hostv, int n)
+{
+ return resolve(host, hostv, n, 0);
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+ return resolve(addr, hostv, n, 1);
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+ ushort p;
+ struct servent *s;
+
+ s = getservbyname(service, net);
+ if(s == 0)
+ return -1;
+ p = s->s_port;
+ sprint(port, "%d", nhgets(&p));
+ return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+ int r;
+ static struct linger l = {1, 0};
+
+ osenter();
+ if(nolinger)
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+ r = shutdown(fd, 2);
+ if(r >= 0)
+ r = close(fd);
+ osleave();
+ return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+#ifdef SIOCGARP
+ struct arpreq a;
+ struct sockaddr_in pa;
+ int s;
+ uchar addr[IPaddrlen];
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ memset(&a, 0, sizeof(a));
+ memset(&pa, 0, sizeof(pa));
+ pa.sin_family = AF_INET;
+ pa.sin_port = 0;
+ parseip(addr, ipaddr);
+ if(!isv4(addr)){
+ close(s);
+ error(Ebadarg);
+ }
+ memmove(&pa.sin_addr, ipaddr+IPv4off, IPv4addrlen);
+ memmove(&a.arp_pa, &pa, sizeof(pa));
+ while(ioctl(s, SIOCGARP, &a) != -1) {
+ ioctl(s, SIOCDARP, &a);
+ memset(&a.arp_ha, 0, sizeof(a.arp_ha));
+ }
+ a.arp_ha.sa_family = AF_UNSPEC;
+ parsemac((uchar*)a.arp_ha.sa_data, eaddr, 6);
+ a.arp_flags = ATF_PERM;
+ if(ioctl(s, SIOCSARP, &a) == -1) {
+ oserrstr(up->env->errstr, ERRMAX);
+ close(s);
+ error(up->env->errstr);
+ }
+ close(s);
+#else
+ error("arp not implemented");
+#endif
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+ return restricted || port != 0;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+ int on;
+
+ on = 1;
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+#ifdef TCP_KEEPIDLE
+ if(ms <= 120000)
+ ms = 120000;
+ ms /= 1000;
+ setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&ms, sizeof(ms));
+#endif
+}
--- /dev/null
+++ b/emu/port/keysym2ucs.h
@@ -1,0 +1,9 @@
+/* $XFree86: xc/programs/xterm/keysym2ucs.h,v 1.1 1999/06/12 15:37:18 dawes Exp $ */
+/*
+ * This module converts keysym values into the corresponding ISO 10646-1
+ * (UCS, Unicode) values.
+ */
+
+#include <X11/X.h>
+
+long keysym2ucs(KeySym keysym);
--- /dev/null
+++ b/emu/port/kproc-pthreads.c
@@ -1,0 +1,235 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#undef _POSIX_C_SOURCE
+#undef getwd
+
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+#include <limits.h>
+#include <errno.h>
+#include <semaphore.h>
+
+typedef struct Osdep Osdep;
+struct Osdep {
+ sem_t sem;
+ pthread_t self;
+};
+
+static pthread_key_t prdakey;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+ return pthread_getspecific(prdakey);
+}
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ Osdep *os;
+
+ USED(t);
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", p->text, msg);
+
+ e = p->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ free(e->user);
+ }
+ free(p->prog);
+ os = p->os;
+ if(os != nil){
+ sem_destroy(&os->sem);
+ free(os);
+ }
+ free(p);
+ pthread_exit(0);
+}
+
+static void*
+tramp(void *arg)
+{
+ Proc *p;
+ Osdep *os;
+
+ p = arg;
+ os = p->os;
+ os->self = pthread_self();
+ if(pthread_setspecific(prdakey, arg))
+ panic("set specific data failed in tramp\n");
+ if(0){
+ pthread_attr_t attr;
+ memset(&attr, 0, sizeof(attr));
+ pthread_getattr_np(pthread_self(), &attr);
+ size_t s;
+ pthread_attr_getstacksize(&attr, &s);
+ print("stack size = %d\n", s);
+ }
+ p->func(p->arg);
+ pexit("{Tramp}", 0);
+ return nil;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ pthread_t thread;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ pthread_attr_t attr;
+ Osdep *os;
+
+ p = newproc();
+ if(p == nil)
+ panic("kproc: no memory");
+
+ os = malloc(sizeof(*os));
+ if(os == nil)
+ panic("kproc: no memory");
+ os->self = 0; /* set by tramp */
+ sem_init(&os->sem, 0, 0);
+ p->os = os;
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ } else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ memset(&attr, 0, sizeof(attr));
+ if(pthread_attr_init(&attr) == -1)
+ panic("pthread_attr_init failed");
+ if(flags & KPX11)
+ pthread_attr_setstacksize(&attr, 512*1024); /* could be a parameter */
+ else if(KSTACK > 0)
+ pthread_attr_setstacksize(&attr, (KSTACK < PTHREAD_STACK_MIN? PTHREAD_STACK_MIN: KSTACK)+1024);
+ pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if(pthread_create(&thread, &attr, tramp, p))
+ panic("thr_create failed\n");
+ pthread_attr_destroy(&attr);
+}
+
+/* called to wake up kproc blocked on a syscall */
+void
+oshostintr(Proc *p)
+{
+ Osdep *os;
+
+ os = p->os;
+ if(os != nil && os->self != 0)
+ pthread_kill(os->self, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ Osdep *os;
+
+ os = up->os;
+ while(sem_wait(&os->sem))
+ {} /* retry on signals (which shouldn't happen) */
+}
+
+void
+osready(Proc *p)
+{
+ Osdep *os;
+
+ os = p->os;
+ sem_post(&os->sem);
+}
+
+void
+kprocinit(Proc *p)
+{
+ if(pthread_key_create(&prdakey, NULL))
+ panic("key_create failed");
+ if(pthread_setspecific(prdakey, p))
+ panic("set specific thread data failed");
+}
+
+void
+osyield(void)
+{
+// pthread_yield_np();
+ /* define pthread_yield to be sched_yield or pthread_yield_np if required */
+ pthread_yield();
+}
+
+void
+ospause(void)
+{
+ /* main just wants this thread to go away */
+ pthread_exit(0);
+}
+
+void
+oslopri(void)
+{
+ struct sched_param param;
+ int policy;
+ pthread_t self;
+
+ self = pthread_self();
+ pthread_getschedparam(self, &policy, ¶m);
+ param.sched_priority = sched_get_priority_min(policy);
+ pthread_setschedparam(self, policy, ¶m);
+}
--- /dev/null
+++ b/emu/port/latin1.c
@@ -1,0 +1,83 @@
+#include "dat.h"
+
+/*
+ * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a
+ * prefix of latintab[j].ld only when j<i.
+ */
+static
+struct cvlist
+{
+ char *ld; /* must be seen before using this conversion */
+ char *si; /* options for last input characters */
+ char *so; /* the corresponding Rune for each si entry */
+} latintab[] = {
+#include "latin1.h"
+ 0, 0, 0
+};
+
+/*
+ * Given 5 characters k[0]..k[4], find the rune or return -1 for failure.
+ */
+static long
+unicode(uchar *k)
+{
+ long i, c;
+
+ k++; /* skip 'X' */
+ c = 0;
+ for(i=0; i<4; i++,k++){
+ c <<= 4;
+ if('0'<=*k && *k<='9')
+ c += *k-'0';
+ else if('a'<=*k && *k<='f')
+ c += 10 + *k-'a';
+ else if('A'<=*k && *k<='F')
+ c += 10 + *k-'A';
+ else
+ return -1;
+ }
+ return c;
+}
+
+/*
+ * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for
+ * failure, or something < -1 if n is too small. In the latter case, the result
+ * is minus the required n.
+ */
+long
+latin1(uchar *k, int n)
+{
+ struct cvlist *l;
+ int c;
+ char* p;
+
+ if(k[0] == 'X')
+ if(n>=5)
+ return unicode(k);
+ else
+ return -5;
+ for(l=latintab; l->ld!=0; l++)
+ if(k[0] == l->ld[0]){
+ if(n == 1)
+ return -2;
+ if(l->ld[1] == 0)
+ c = k[1];
+ else if(l->ld[1] != k[1])
+ continue;
+ else if(n == 2)
+ return -3;
+ else
+ c = k[2];
+ for(p=l->si; *p!=0; p++)
+ if(*p == c) {
+ Rune r;
+ int i = p - l->si;
+ p = l->so;
+ for(; i >= 0; i--)
+ p += chartorune(&r, p);
+ return r;
+ }
+ return -1;
+ }
+ return -1;
+}
--- /dev/null
+++ b/emu/port/latin1.h
@@ -1,0 +1,99 @@
+ " ", " i", "␣ı",
+ "!~", "-=~", "≄≇≉",
+ "!", "!<=>?bmp", "¡≮≠≯‽⊄∉⊅",
+ "\"*", "IUiu", "ΪΫϊϋ",
+ "\"", "\"AEIOUYaeiouy", "¨ÄËÏÖÜŸäëïöüÿ",
+ "$*", "fhk", "ϕϑϰ",
+ "$", "BEFHILMRVaefglopv", "ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ",
+ "\'\"", "Uu", "Ǘǘ",
+ "\'", "\'ACEILNORSUYZacegilnorsuyz", "´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź",
+ "*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz", "∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ",
+ "+", "-O", "±⊕",
+ ",", ",ACEGIKLNORSTUacegiklnorstu", "¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų",
+ "-*", "l", "ƛ",
+ "-", "+-2:>DGHILOTZbdghiltuz~", "∓ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂",
+ ".", ".CEGILOZceglz", "·ĊĖĠİĿ⊙Żċėġŀż",
+ "/", "Oo", "Øø",
+ "1", "234568", "½⅓¼⅕⅙⅛",
+ "2", "-35", "ƻ⅔⅖",
+ "3", "458", "¾⅗⅜",
+ "4", "5", "⅘",
+ "5", "68", "⅚⅝",
+ "7", "8", "⅞",
+ ":", ")-=", "☺÷≔",
+ "<!", "=~", "≨⋦",
+ "<", "-<=>~", "←«≤≶≲",
+ "=", ":<=>OV", "≕⋜≡⋝⊜⇒",
+ ">!", "=~", "≩⋧",
+ ">", "<=>~", "≷≥»≳",
+ "?", "!?", "‽¿",
+ "@\'", "\'", "ъ",
+ "@@", "\'EKSTYZekstyz", "ьЕКСТЫЗекстыз",
+ "@C", "Hh", "ЧЧ",
+ "@E", "Hh", "ЭЭ",
+ "@K", "Hh", "ХХ",
+ "@S", "CHch", "ЩШЩШ",
+ "@T", "Ss", "ЦЦ",
+ "@Y", "AEOUaeou", "ЯЕЁЮЯЕЁЮ",
+ "@Z", "Hh", "ЖЖ",
+ "@c", "h", "ч",
+ "@e", "h", "э",
+ "@k", "h", "х",
+ "@s", "ch", "щш",
+ "@t", "s", "ц",
+ "@y", "aeou", "яеёю",
+ "@z", "h", "ж",
+ "@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx", "АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх",
+ "A", "E", "Æ",
+ "C", "ACU", "⋂ℂ⋃",
+ "Dv", "Zz", "DŽDž",
+ "D", "-e", "Ð∆",
+ "G", "-", "Ǥ",
+ "H", "-H", "Ħℍ",
+ "I", "-J", "ƗIJ",
+ "L", "&-Jj|", "⋀ŁLJLj⋁",
+ "N", "JNj", "NJℕNj",
+ "O", "*+-./=EIcoprx", "⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗",
+ "P", "P", "ℙ",
+ "Q", "Q", "ℚ",
+ "R", "R", "ℝ",
+ "S", "123S", "¹²³§",
+ "T", "-u", "Ŧ⊨",
+ "V", "=", "⇐",
+ "Y", "R", "Ʀ",
+ "Z", "-ACSZ", "Ƶℤ",
+ "^", "ACEGHIJOSUWYaceghijosuwy", "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ",
+ "_\"", "AUau", "ǞǕǟǖ",
+ "_,", "Oo", "Ǭǭ",
+ "_.", "Aa", "Ǡǡ",
+ "_", "AEIOU_aeiou", "ĀĒĪŌŪ¯āēīōū",
+ "`\"", "Uu", "Ǜǜ",
+ "`", "AEIOUaeiou", "ÀÈÌÒÙàèìòù",
+ "a", "ben", "↔æ∠",
+ "b", "()+-0123456789=bknpqru", "₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•",
+ "c", "$Oagu", "¢©∩≅∪",
+ "dv", "z", "dž",
+ "d", "-adegz", "ð↓‡°†ʣ",
+ "e", "$lmns", "€⋯—–∅",
+ "f", "a", "∀",
+ "g", "$-r", "¤ǥ∇",
+ "h", "-v", "ℏƕ",
+ "i", "-bfjps", "ɨ⊆∞ij⊇∫",
+ "l", "\"$&\'-jz|", "“£∧‘łlj⋄∨",
+ "m", "iou", "µ∈×",
+ "n", "jo", "nj¬",
+ "o", "AOUaeiu", "Å⊚Ůåœƣů",
+ "p", "Odgrt", "℗∂¶∏∝",
+ "r", "\"\'O", "”’®",
+ "s", "()+-0123456789=abnoprstu", "⁽⁾⁺⁻⁰ⁱ⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑",
+ "t", "-efmsu", "ŧ∃∴™ς⊢",
+ "u", "-AEGIOUaegiou", "ʉĂĔĞĬŎŬ↑ĕğĭŏŭ",
+ "v\"", "Uu", "Ǚǚ",
+ "v", "ACDEGIKLNORSTUZacdegijklnorstuz", "ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž",
+ "w", "bknpqr", "♗♔♘♙♕♖",
+ "x", "O", "⊗",
+ "y", "$", "¥",
+ "z", "-", "ƶ",
+ "|", "Pp|", "Þþ¦",
+ "~!", "=", "≆",
+ "~", "-=AINOUainou~", "≃≅ÃĨÑÕŨãĩñõũ≈",
--- /dev/null
+++ b/emu/port/lock.c
@@ -1,0 +1,142 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+void
+lock(Lock *l)
+{
+ int i;
+
+ if(_tas(&l->val) == 0)
+ return;
+ for(i=0; i<100; i++){
+ if(_tas(&l->val) == 0)
+ return;
+ osyield();
+ }
+ for(i=1;; i++){
+ if(_tas(&l->val) == 0)
+ return;
+ osmillisleep(i*10);
+ if(i > 100){
+ osyield();
+ i = 1;
+ }
+ }
+}
+
+int
+canlock(Lock *l)
+{
+ return _tas(&l->val) == 0;
+}
+
+void
+unlock(Lock *l)
+{
+ coherence();
+ l->val = 0;
+}
+
+void
+qlock(QLock *q)
+{
+ Proc *p;
+
+ lock(&q->use);
+ if(!q->locked) {
+ q->locked = 1;
+ unlock(&q->use);
+ return;
+ }
+ p = q->tail;
+ if(p == 0)
+ q->head = up;
+ else
+ p->qnext = up;
+ q->tail = up;
+ up->qnext = 0;
+ unlock(&q->use);
+ osblock();
+}
+
+int
+canqlock(QLock *q)
+{
+ if(!canlock(&q->use))
+ return 0;
+ if(q->locked){
+ unlock(&q->use);
+ return 0;
+ }
+ q->locked = 1;
+ unlock(&q->use);
+ return 1;
+}
+
+void
+qunlock(QLock *q)
+{
+ Proc *p;
+
+ lock(&q->use);
+ p = q->head;
+ if(p) {
+ q->head = p->qnext;
+ if(q->head == 0)
+ q->tail = 0;
+ unlock(&q->use);
+ osready(p);
+ return;
+ }
+ q->locked = 0;
+ unlock(&q->use);
+}
+
+void
+rlock(RWlock *l)
+{
+ qlock(&l->x); /* wait here for writers and exclusion */
+ lock(&l->l);
+ l->readers++;
+ canqlock(&l->k); /* block writers if we are the first reader */
+ unlock(&l->l);
+ qunlock(&l->x);
+}
+
+/* same as rlock but punts if there are any writers waiting */
+int
+canrlock(RWlock *l)
+{
+ if (!canqlock(&l->x))
+ return 0;
+ lock(&l->l);
+ l->readers++;
+ canqlock(&l->k); /* block writers if we are the first reader */
+ unlock(&l->l);
+ qunlock(&l->x);
+ return 1;
+}
+
+void
+runlock(RWlock *l)
+{
+ lock(&l->l);
+ if(--l->readers == 0) /* last reader out allows writers */
+ qunlock(&l->k);
+ unlock(&l->l);
+}
+
+void
+wlock(RWlock *l)
+{
+ qlock(&l->x); /* wait here for writers and exclusion */
+ qlock(&l->k); /* wait here for last reader */
+}
+
+void
+wunlock(RWlock *l)
+{
+ qunlock(&l->k);
+ qunlock(&l->x);
+}
--- /dev/null
+++ b/emu/port/main.c
@@ -1,0 +1,454 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include "kernel.h"
+#include "draw.h"
+#include "version.h"
+
+int rebootargc = 0;
+char** rebootargv;
+static char *imod = "/dis/emuinit.dis";
+extern char* hosttype;
+char* tkfont; /* for libtk/utils.c */
+int tkstylus; /* libinterp/tk.c */
+extern int mflag;
+ int dflag;
+ int vflag;
+ int vflag;
+ Procs procs;
+ char *eve;
+ int Xsize = 640;
+ int Ysize = 480;
+ int bflag = 1;
+ int sflag;
+ int qflag;
+ int xtblbit;
+ ulong displaychan;
+char *cputype;
+
+static void
+usage(void)
+{
+ fprint(2, "Usage: emu [options...] [file.dis [args...]]\n"
+ "\t-gXxY\n"
+ "\t-c[0-9]\n"
+ "\t-d file.dis\n"
+ "\t-s\n"
+ "\t-v\n"
+ "\t-p<poolname>=maxsize\n"
+ "\t-f<fontpath>\n"
+ "\t-r<rootpath>\n"
+ "\t-7\n"
+ "\t-B\n"
+ "\t-C<channel string>\n"
+ "\t-S\n");
+
+ exits("usage");
+}
+
+static void
+envusage(void)
+{
+ fprint(2, "emu: bad option in EMU environment variable (%s)\n", getenv("EMU"));
+ usage();
+}
+
+static int
+isnum(char *p)
+{
+ if (*p == 0) return 0;
+ while (*p) {
+ if (*p < '0' || *p > '9') return 0;
+ p++;
+ }
+ return 1;
+}
+
+static int
+geom(char *val)
+{
+ char *p;
+ int x, y;
+ if (val == '\0' || (*val < '0' || *val > '9'))
+ return 0;
+ x = strtoul(val, &p, 0);
+ if(x >= 64)
+ Xsize = x;
+ if (*p++ != 'x' || !isnum(p))
+ return 0;
+ y = strtoul(p, &p, 0);
+ if(y >= 48)
+ Ysize = y;
+ if (*p != '\0') return 0;
+ return 1;
+}
+
+static void
+poolopt(char *str)
+{
+ char *var;
+ int n;
+ ulong x;
+
+ var = str;
+ while(*str && *str != '=')
+ str++;
+ if(*str != '=' || str[1] == '\0')
+ usage();
+ *str++ = '\0';
+ n = strlen(str);
+ x = atoi(str);
+ switch(str[n - 1]){
+ case 'k':
+ case 'K':
+ x *= 1024;
+ break;
+ case 'm':
+ case 'M':
+ x *= 1024*1024;
+ break;
+ }
+ if(poolsetsize(var, x) == 0)
+ usage();
+}
+
+static void
+option(int argc, char *argv[], void (*badusage)(void))
+{
+ char *cp;
+
+ ARGBEGIN {
+ default:
+ badusage();
+ case 'g': /* Window geometry */
+ if (geom(EARGF(badusage())) == 0)
+ badusage();
+ break;
+ case 'b': /* jit array bounds checking (obsolete, now on by default) */
+ break;
+ case 'B': /* suppress jit array bounds checks */
+ bflag = 0;
+ break;
+ case 'c': /* Compile on the fly */
+ cp = EARGF(badusage());
+ if (!isnum(cp))
+ badusage();
+ cflag = atoi(cp);
+ if(cflag < 0|| cflag > 9)
+ usage();
+ break;
+ case 'I': /* (temporary option) run without cons */
+ dflag++;
+ break;
+ case 'd': /* run as a daemon */
+ dflag++;
+ imod = EARGF(badusage());
+ break;
+ case 's': /* No trap handling */
+ sflag++;
+ break;
+ case 'm': /* gc mark and sweep */
+ cp = EARGF(badusage());
+ if (!isnum(cp))
+ badusage();
+ mflag = atoi(cp);
+ if(mflag < 0|| mflag > 9)
+ usage();
+ break;
+ case 'p': /* pool option */
+ poolopt(EARGF(badusage()));
+ break;
+ case 'f': /* Set font path */
+ tkfont = EARGF(badusage());
+ break;
+ case 'r': /* Set inferno root */
+ strecpy(rootdir, rootdir+sizeof(rootdir), EARGF(badusage()));
+ break;
+ case '7': /* use 7 bit colormap in X */
+ xtblbit = 1;
+ break;
+ case 'G': /* allow global access to file system (obsolete) */
+ break;
+ case 'C': /* channel specification for display */
+ cp = EARGF(badusage());
+ displaychan = strtochan(cp);
+ if(displaychan == 0){
+ fprint(2, "emu: invalid channel specifier (-C): %q\n", cp);
+ exits("usage");
+ }
+ break;
+ case 'S':
+ tkstylus = 1;
+ break;
+ case 'v':
+ vflag = 1; /* print startup messages */
+ break;
+ } ARGEND
+}
+
+static void
+savestartup(int argc, char *argv[])
+{
+ int i;
+
+ rebootargc = argc;
+ rebootargv = malloc((argc+1)*sizeof(char*));
+ if(rebootargv == nil)
+ panic("can't save startup args");
+ for(i = 0; i < argc; i++) {
+ rebootargv[i] = strdup(argv[i]);
+ if(rebootargv[i] == nil)
+ panic("can't save startup args");
+ }
+ rebootargv[i] = nil;
+}
+
+void
+putenvq(char *name, char *val, int conf)
+{
+ val = smprint("%q", val);
+ ksetenv(name, val, conf);
+ free(val);
+}
+
+void
+putenvqv(char *name, char **v, int n, int conf)
+{
+ Fmt f;
+ int i;
+ char *val;
+
+ fmtstrinit(&f);
+ for(i=0; i<n; i++)
+ fmtprint(&f, "%s%q", i?" ":"", v[i]);
+ val = fmtstrflush(&f);
+ ksetenv(name, val, conf);
+ free(val);
+}
+
+void
+nofence(void)
+{
+}
+
+void
+main(int argc, char *argv[])
+{
+ char *opt, *p;
+ char *enva[20];
+ int envc;
+
+ if(coherence == nil)
+ coherence = nofence;
+ quotefmtinstall();
+ savestartup(argc, argv);
+ /* set default root now, so either $EMU or -r can override it later */
+ if((p = getenv("INFERNO")) != nil || (p = getenv("ROOT")) != nil)
+ strecpy(rootdir, rootdir+sizeof(rootdir), p);
+ opt = getenv("EMU");
+ if(opt != nil && *opt != '\0') {
+ enva[0] = "emu";
+ envc = tokenize(opt, &enva[1], sizeof(enva)-1) + 1;
+ enva[envc] = 0;
+ option(envc, enva, envusage);
+ }
+ option(argc, argv, usage);
+ eve = strdup("inferno");
+
+ opt = "interp";
+ if(cflag)
+ opt = "compile";
+
+ if(vflag)
+ print("Inferno %s main (pid=%d) %s\n", VERSION, getpid(), opt);
+
+ libinit(imod);
+}
+
+void
+emuinit(void *imod)
+{
+ Osenv *e;
+ char *wdir;
+
+ e = up->env;
+ e->pgrp = newpgrp();
+ e->fgrp = newfgrp(nil);
+ e->egrp = newegrp();
+ e->errstr = e->errbuf0;
+ e->syserrstr = e->errbuf1;
+ e->user = strdup("");
+
+ links();
+ chandevinit();
+
+ if(waserror())
+ panic("setting root and dot");
+
+ e->pgrp->slash = namec("#/", Atodir, 0, 0);
+ cnameclose(e->pgrp->slash->name);
+ e->pgrp->slash->name = newcname("/");
+ e->pgrp->dot = cclone(e->pgrp->slash);
+ poperror();
+
+ strcpy(up->text, "main");
+
+ if(kopen("#c/cons", OREAD) != 0)
+ fprint(2, "failed to make fd0 from #c/cons: %r\n");
+ kopen("#c/cons", OWRITE);
+ kopen("#c/cons", OWRITE);
+
+ /* the setid cannot precede the bind of #U */
+ kbind("#U", "/", MAFTER|MCREATE);
+ setid(eve, 0);
+ kbind("#^", "/dev", MBEFORE); /* snarf */
+ kbind("#^", "/chan", MBEFORE);
+ kbind("#m", "/dev", MBEFORE); /* pointer */
+ kbind("#c", "/dev", MBEFORE);
+ kbind("#p", "/prog", MREPL);
+ kbind("#d", "/fd", MREPL);
+ kbind("#I", "/net", MAFTER); /* will fail on Plan 9 */
+
+ /* BUG: we actually only need to do these on Plan 9 */
+ kbind("#U/dev", "/dev", MAFTER);
+ kbind("#U/net", "/net", MAFTER);
+ kbind("#U/net.alt", "/net.alt", MAFTER);
+
+ if(cputype != nil)
+ ksetenv("cputype", cputype, 1);
+ putenvqv("emuargs", rebootargv, rebootargc, 1);
+ putenvq("emuroot", rootdir, 1);
+ ksetenv("emuhost", hosttype, 1);
+ wdir = malloc(1024);
+ if(wdir != nil){
+ if(getwd(wdir, 1024) != nil)
+ putenvq("emuwdir", wdir, 1);
+ free(wdir);
+ }
+
+ kproc("main", disinit, imod, KPDUPFDG|KPDUPPG|KPDUPENVG);
+
+ for(;;)
+ ospause();
+}
+
+void
+errorf(char *fmt, ...)
+{
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ error(buf);
+}
+
+void
+error(char *err)
+{
+ if(err != up->env->errstr && up->env->errstr != nil)
+ kstrcpy(up->env->errstr, err, ERRMAX);
+// ossetjmp(up->estack[NERR-1]);
+ nexterror();
+}
+
+void
+exhausted(char *resource)
+{
+ char buf[64];
+ int n;
+
+ n = snprint(buf, sizeof(buf), "no free %s\n", resource);
+ iprint(buf);
+ buf[n-1] = 0;
+ error(buf);
+}
+
+void
+nexterror(void)
+{
+ oslongjmp(nil, up->estack[--up->nerr], 1);
+}
+
+/* for dynamic modules - functions not macros */
+
+void*
+waserr(void)
+{
+ up->nerr++;
+ return up->estack[up->nerr-1];
+}
+
+void
+poperr(void)
+{
+ up->nerr--;
+}
+
+char*
+enverror(void)
+{
+ return up->env->errstr;
+}
+
+void
+panic(char *fmt, ...)
+{
+ va_list arg;
+ char buf[512];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "panic: %s\n", buf);
+ if(sflag)
+ abort();
+
+ cleanexit(0);
+}
+
+int
+iprint(char *fmt, ...)
+{
+
+ int n;
+ va_list va;
+ char buf[1024];
+
+ va_start(va, fmt);
+ n = vseprint(buf, buf+sizeof buf, fmt, va) - buf;
+ va_end(va);
+
+ write(1, buf, n);
+ return 1;
+}
+
+void
+_assert(char *fmt)
+{
+ panic("assert failed: %s", fmt);
+}
+
+/*
+ * mainly for libmp
+ */
+void
+sysfatal(char *fmt, ...)
+{
+ va_list arg;
+ char buf[64];
+
+ va_start(arg, fmt);
+ vsnprint(buf, sizeof(buf), fmt, arg);
+ va_end(arg);
+ error(buf);
+}
+
+void
+oserror(void)
+{
+ oserrstr(up->env->errstr, ERRMAX);
+ error(up->env->errstr);
+}
--- /dev/null
+++ b/emu/port/master
@@ -1,0 +1,36 @@
+# do not edit; automatically generated
+
+% mem
+* indir
+/ root
+A audio
+C cmd
+D ssl
+F tinyfs
+I ip
+M mnt
+P prof
+U fs
+^ snarf
+a arch
+c cons
+d dup
+e env
+i draw
+m pointer
+p prog
+s srv
+| pipe
+τ tk
+₪ srv9
+
+α local use
+β local use
+γ local use
+δ local use
+ε local use
+ζ local use
+η local use
+θ local use
+ι local use
+κ local use
--- /dev/null
+++ b/emu/port/master.local
@@ -1,0 +1,10 @@
+α local use
+β local use
+γ local use
+δ local use
+ε local use
+ζ local use
+η local use
+θ local use
+ι local use
+κ local use
--- /dev/null
+++ b/emu/port/mkdevc
@@ -1,0 +1,99 @@
+$AWK '
+BEGIN{
+ if(ARGC < 2)
+ exit
+}
+
+/^$/{
+ next;
+}
+/^#/{
+ next;
+}
+collect && /^[^ \t]/{
+ collect = 0;
+}
+collect && section ~ "dev"{
+ dev[ndev++] = $1;
+}
+collect && section ~ "ip"{
+ ip[nip++] = $1;
+}
+collect && section ~ "link"{
+ link[nlink++] = $1;
+}
+collect && section ~ "mod"{
+ mod[nmod++] = $1;
+}
+collect && section ~ "misc"{
+ misc[nmisc++] = $1;
+}
+collect && section ~ "port"{
+ port[nport++] = $0;
+}
+collect && section ~ "code"{
+ code[ncode++] = $0;
+}
+$0 ~ /^[^ \t]/{
+ if($0 ~ "(code|dev|ip|lib|link|mod|misc|port|root)"){
+ section = $0;
+ collect = 1;
+ }
+ next;
+}
+
+END{
+ if(ARGC < 2)
+ exit "usage"
+
+ printf "#include \"dat.h\"\n"
+ printf "#include \"fns.h\"\n"
+ printf "#include \"error.h\"\n"
+ printf "#include \"interp.h\"\n\n\n"
+ printf "#include \"%s.root.h\"\n\n", ARGV[1];
+
+ nildev = 8;
+ printf "ulong ndevs = %s;\n\n", ndev+nildev
+ for(i = 0; i < ndev; i++)
+ printf "extern Dev %sdevtab;\n", dev[i];
+ printf "Dev* devtab[]={\n"
+ for(i = 0; i < ndev; i++)
+ printf "\t&%sdevtab,\n", dev[i];
+ for(i = 0; i < nildev; i++)
+ printf("\tnil,\n");
+ printf "\tnil,\n};\n\n";
+
+
+ for(i = 0; i < nlink; i++)
+ printf "extern void %slink(void);\n", link[i];
+
+ printf "void links(void){\n";
+ for(i = 0; i < nlink; i++)
+ printf "\t%slink();\n", link[i];
+ printf "}\n\n";
+
+ for(i = 0; i < nmod; i++)
+ printf "extern void %smodinit(void);\n", mod[i];
+ printf "void modinit(void){\n";
+ for(i = 0; i < nmod; i++)
+ printf "\t%smodinit();\n",mod[i];
+ printf "}\n\n";
+
+ if(nip){
+ printf "#include \"../ip/ip.h\"\n";
+ for(i = 0; i < nip; i++)
+ printf "extern void %sinit(Fs*);\n", ip[i];
+ printf "void (*ipprotoinit[])(Fs*) = {\n";
+ for(i = 0; i < nip; i++)
+ printf "\t%sinit,\n", ip[i];
+ printf "\tnil,\n};\n\n";
+ }
+
+ for(i = 0; i < ncode; i++)
+ printf "%s\n", code[i];
+
+ printf "char* conffile = \"%s\";\n", ARGV[1];
+ printf "ulong kerndate = KERNDATE;\n";
+
+ exit
+}' $*
--- /dev/null
+++ b/emu/port/mkdevlist
@@ -1,0 +1,75 @@
+$AWK '
+BEGIN{
+ var["init"] = "INIT=";
+ var["ip"] = "IP=";
+ var["lib"] = "LIBS=";
+ var["root"] = "ROOTFILES=";
+ infernoroot = ENVIRON["ROOT"];
+}
+/^$/{ next;
+}
+/^#/{ next;
+}
+/^env/{
+ inenv = 1;
+ next;
+}
+inenv != 0 && /^[ ]/{
+ sub("^[ ]*", "", $0)
+ printf "%s\n", $0
+ next
+}
+
+/^(code|dev|init|ip|lib|link|mod|misc|port|root)/{
+ inenv = 0;
+ type = $1;
+ next;
+}
+/^[^ ]/ {
+ inenv = 0;
+}
+type && /^[ ]/{
+ if(type == "code")
+ next;
+ if(type == "root"){
+ if (NF > 1)
+ file = $2;
+ else if ($1 == "/osinit.dis")
+ next; # handled via explicit dependency
+ else
+ file = $1;
+ if(rootfile[file] == 0){
+ var[type] = var[type] " " infernoroot file;
+ rootfile[file]++;
+ }
+ next;
+ }
+ if(type == "init" || type == "lib"){
+ var[type] = var[type] " " $1;
+ next;
+ }
+ file = $1 "'.$O'"
+ if(type == "port")
+ port[file]++;
+ else if(type == "dev")
+ obj["dev" file]++;
+ else if(type != "mod")
+ obj[file]++;
+ for(i = 2; i <= NF; i++){
+ if($i !~ "^[+=-].*")
+ obj[$i "'.$O'"]++;
+ }
+ next;
+}
+END{
+ x = ""
+ for(i in obj)
+ x = x " " i
+ printf "DEVS=%s\n", x;
+ x = ""
+ for(i in port)
+ x = x " " i
+ printf "PORT=%s\n", x
+ for(v in var)
+ printf "%s\n", var[v]
+}' $*
--- /dev/null
+++ b/emu/port/mkfile
@@ -1,0 +1,15 @@
+# If the existence of this mkfile screws something up, rename it. -rsc
+
+master:DV:
+ {
+ echo '# do not edit; automatically generated'
+ echo
+ echo '
+ X , s/Dev (.*)devtab.*{.*\n L?''(.*)''/DEV \1 \2\n/
+ X ,x g/^DEV/ p
+ ' | sam -d ../*/dev*.c >[2]/dev/null |
+ awk '/^DEV/ { printf("%s\t%s\n", $3, $2); }' |
+ sort -u
+ echo
+ cat master.local
+ } >master
--- /dev/null
+++ b/emu/port/mkroot
@@ -1,0 +1,121 @@
+$AWK '
+BEGIN{
+ if (ARGC < 2)
+ exit "usage";
+
+ conf = ARGV[1];
+ infernoroot = ENVIRON["ROOT"];
+ init = ENVIRON["INIT"];
+ data2c = ENVIRON["DATA2C"];
+ if(data2c == "")
+ data2c = "data2c"
+ nroot = 0;
+}
+/^$/{
+ next;
+}
+/^#/{
+ next;
+}
+collect && /^[^ \t]/{
+ collect = 0;
+}
+collect && section ~ "root"{
+ dst[nroot] = $1;
+ if (NF > 1)
+ src[nroot] = infernoroot $2;
+ else if (dst[nroot] == "/osinit.dis")
+ src[nroot] = infernoroot "/dis/" init ".dis";
+ else
+ src[nroot] = infernoroot $1;
+ for(i=0; i<nroot; i++)
+ if(dst[i] == dst[nroot])
+ break;
+ if(i == nroot)
+ nroot++;
+}
+$0 ~ /^[^ \t]/{
+ if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){
+ section = $0;
+ collect = 1;
+ }
+ next;
+}
+END{
+ rootdata = conf ".root.c";
+ system("rm -f " rootdata);
+ print("/* Generated by mkroot */") >rootdata;
+ close(rootdata);
+ isdir[0] = 1;
+ dotdot[0] = 0;
+ qid = 1;
+ for (i = 0; i < nroot; i++) {
+ ncomp = split(dst[i], comp, "/");
+ if (comp[1] != "" || ncomp < 2)
+ continue;
+ q = 0;
+ for (j = 2; j <= ncomp; j++) {
+ key = q "/" comp[j];
+ if (walk[key] == 0) {
+ walk[key] = qid;
+ dotdot[qid] = q;
+ q = qid++;
+ name[q] = comp[j];
+ if (j < ncomp)
+ isdir[q] = 1;
+ }
+ else
+ q = walk[key];
+ }
+ if (system("test -d " src[i]) == 0)
+ isdir[q] = 1;
+ else {
+ if (system(data2c " root" q " <" src[i] " >>" rootdata) != 0)
+ exit 1;
+ print("extern unsigned char root" q "code[];");
+ print("extern int root" q "len;");
+ }
+ }
+
+ x = 1;
+ sort[0] = 0;
+ unsort[0] = 0;
+ for (q = 0; q < qid; q++) {
+ if (isdir[q]) {
+ nchild[q] = 0;
+ for (q2 = 1; q2 < qid; q2++) {
+ if (dotdot[q2] == q) {
+ if (nchild[q]++ == 0)
+ child0[q] = x;
+ sort[q2] = x++;
+ unsort[sort[q2]] = q2;
+ }
+ }
+ }
+ }
+
+ print("int rootmaxq = " qid ";");
+
+ print("Dirtab roottab[" qid "] = {");
+ for (oq = 0; oq < qid; oq++) {
+ q = unsort[oq];
+ if (!isdir[q])
+ print("\t\"" name[q] "\",\t{" oq ", 0, QTFILE},\t", "0,\t0444,");
+ else
+ print("\t\"" name[q] "\",\t{" oq ", 0, QTDIR},\t", "0,\t0555,");
+ }
+ print("};");
+
+ print("Rootdata rootdata[" qid "] = {");
+ for (oq = 0; oq < qid; oq++) {
+ q = unsort[oq];
+ if (!isdir[q])
+ print("\t" sort[dotdot[q]] ",\t", "root" q "code,\t", "0,\t", "&root" q "len,");
+ else if (nchild[q])
+ print("\t" sort[dotdot[q]] ",\t", "&roottab[" child0[q] "],\t", nchild[q] ",\tnil,");
+ else
+ print("\t" sort[dotdot[q]] ",\t", "nil,\t", "0,\t", "nil,");
+ }
+ print("};");
+}
+' $1 >$1.root.h
--- /dev/null
+++ b/emu/port/parse.c
@@ -1,0 +1,111 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+/*
+ * Generous estimate of number of fields, including terminal nil pointer
+ */
+static int
+ncmdfield(char *p, int n)
+{
+ int white, nwhite;
+ char *ep;
+ int nf;
+
+ if(p == nil)
+ return 1;
+
+ nf = 0;
+ ep = p+n;
+ white = 1; /* first text will start field */
+ while(p < ep){
+ nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */
+ if(white && !nwhite) /* beginning of field */
+ nf++;
+ white = nwhite;
+ }
+ return nf+1; /* +1 for nil */
+}
+
+/*
+ * parse a command written to a device
+ */
+Cmdbuf*
+parsecmd(char *p, int n)
+{
+ Cmdbuf *volatile cb;
+ int nf;
+ char *sp;
+
+ nf = ncmdfield(p, n);
+
+ /* allocate Cmdbuf plus string pointers plus copy of string including \0 */
+ sp = smalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1);
+ cb = (Cmdbuf*)sp;
+ cb->f = (char**)(&cb[1]);
+ cb->buf = (char*)(&cb->f[nf]);
+
+ if(up!=nil && waserror()){
+ free(cb);
+ nexterror();
+ }
+ memmove(cb->buf, p, n);
+ if(up != nil)
+ poperror();
+
+ /* dump new line and null terminate */
+ if(n > 0 && cb->buf[n-1] == '\n')
+ n--;
+ cb->buf[n] = '\0';
+
+ cb->nf = tokenize(cb->buf, cb->f, nf-1);
+ cb->f[cb->nf] = nil;
+
+ return cb;
+}
+
+/*
+ * Reconstruct original message, for error diagnostic
+ */
+void
+cmderror(Cmdbuf *cb, char *s)
+{
+ int i;
+ char *p, *e;
+
+ p = up->genbuf;
+ e = p+ERRMAX-10;
+ p = seprint(p, e, "%s \"", s);
+ for(i=0; i<cb->nf; i++){
+ if(i > 0)
+ p = seprint(p, e, " ");
+ p = seprint(p, e, "%q", cb->f[i]);
+ }
+ strcpy(p, "\"");
+ error(up->genbuf);
+}
+
+/*
+ * Look up entry in table
+ */
+Cmdtab*
+lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab)
+{
+ int i;
+ Cmdtab *ct;
+
+ if(cb->nf == 0)
+ error("empty control message");
+
+ for(ct = ctab, i=0; i<nctab; i++, ct++){
+ if(strcmp(ct->cmd, "*") !=0) /* wildcard always matches */
+ if(strcmp(ct->cmd, cb->f[0]) != 0)
+ continue;
+ if(ct->narg != 0 && ct->narg != cb->nf)
+ cmderror(cb, Ecmdargs);
+ return ct;
+ }
+
+ cmderror(cb, "unknown control message");
+ return nil;
+}
--- /dev/null
+++ b/emu/port/pgrp.c
@@ -1,0 +1,264 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+static Ref pgrpid;
+static Ref mountid;
+
+Pgrp*
+newpgrp(void)
+{
+ Pgrp *p;
+
+ p = malloc(sizeof(Pgrp));
+ if(p == nil)
+ error(Enomem);
+ p->r.ref = 1;
+ p->pgrpid = incref(&pgrpid);
+ p->progmode = 0644;
+ return p;
+}
+
+void
+closepgrp(Pgrp *p)
+{
+ Mhead **h, **e, *f, *next;
+
+ if(p == nil || decref(&p->r) != 0)
+ return;
+
+ wlock(&p->ns);
+ p->pgrpid = -1;
+ e = &p->mnthash[MNTHASH];
+ for(h = p->mnthash; h < e; h++) {
+ for(f = *h; f; f = next) {
+ wlock(&f->lock);
+ cclose(f->from);
+ mountfree(f->mount);
+ f->mount = nil;
+ next = f->hash;
+ wunlock(&f->lock);
+ putmhead(f);
+ }
+ }
+ wunlock(&p->ns);
+ cclose(p->dot);
+ cclose(p->slash);
+ free(p);
+}
+
+void
+pgrpinsert(Mount **order, Mount *m)
+{
+ Mount *f;
+
+ m->order = 0;
+ if(*order == 0) {
+ *order = m;
+ return;
+ }
+ for(f = *order; f; f = f->order) {
+ if(m->mountid < f->mountid) {
+ m->order = f;
+ *order = m;
+ return;
+ }
+ order = &f->order;
+ }
+ *order = m;
+}
+
+/*
+ * pgrpcpy MUST preserve the mountid allocation order of the parent group
+ */
+void
+pgrpcpy(Pgrp *to, Pgrp *from)
+{
+ int i;
+ Mount *n, *m, **link, *order;
+ Mhead *f, **tom, **l, *mh;
+
+ wlock(&from->ns);
+ if(waserror()){
+ wunlock(&from->ns);
+ nexterror();
+ }
+ order = 0;
+ tom = to->mnthash;
+ for(i = 0; i < MNTHASH; i++) {
+ l = tom++;
+ for(f = from->mnthash[i]; f; f = f->hash) {
+ rlock(&f->lock);
+ if(waserror()){
+ runlock(&f->lock);
+ nexterror();
+ }
+ mh = malloc(sizeof(Mhead));
+ if(mh == nil)
+ error(Enomem);
+ mh->from = f->from;
+ mh->r.ref = 1;
+ incref(&mh->from->r);
+ *l = mh;
+ l = &mh->hash;
+ link = &mh->mount;
+ for(m = f->mount; m; m = m->next) {
+ n = newmount(mh, m->to, m->mflag, m->spec);
+ m->copy = n;
+ pgrpinsert(&order, m);
+ *link = n;
+ link = &n->next;
+ }
+ poperror();
+ runlock(&f->lock);
+ }
+ }
+ /*
+ * Allocate mount ids in the same sequence as the parent group
+ */
+ lock(&mountid.lk);
+ for(m = order; m; m = m->order)
+ m->copy->mountid = mountid.ref++;
+ unlock(&mountid.lk);
+
+ to->progmode = from->progmode;
+ to->slash = cclone(from->slash);
+ to->dot = cclone(from->dot);
+ to->nodevs = from->nodevs;
+ poperror();
+ wunlock(&from->ns);
+}
+
+Fgrp*
+newfgrp(Fgrp *old)
+{
+ Fgrp *new;
+ int n;
+
+ new = malloc(sizeof(Fgrp));
+ if(new == nil)
+ error(Enomem);
+ new->r.ref = 1;
+ n = DELTAFD;
+ if(old != nil){
+ lock(&old->l);
+ if(old->maxfd >= n)
+ n = (old->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD;
+ new->maxfd = old->maxfd;
+ unlock(&old->l);
+ }
+ new->nfd = n;
+ new->fd = malloc(n*sizeof(Chan*));
+ if(new->fd == nil){
+ free(new);
+ error(Enomem);
+ }
+ return new;
+}
+
+Fgrp*
+dupfgrp(Fgrp *f)
+{
+ int i;
+ Chan *c;
+ Fgrp *new;
+ int n;
+
+ new = malloc(sizeof(Fgrp));
+ if(new == nil)
+ error(Enomem);
+ new->r.ref = 1;
+ lock(&f->l);
+ n = DELTAFD;
+ if(f->maxfd >= n)
+ n = (f->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD;
+ new->nfd = n;
+ new->fd = malloc(n*sizeof(Chan*));
+ if(new->fd == nil){
+ unlock(&f->l);
+ free(new);
+ error(Enomem);
+ }
+ new->maxfd = f->maxfd;
+ new->minfd = f->minfd;
+ for(i = 0; i <= f->maxfd; i++) {
+ if(c = f->fd[i]){
+ incref(&c->r);
+ new->fd[i] = c;
+ }
+ }
+ unlock(&f->l);
+
+ return new;
+}
+
+void
+closefgrp(Fgrp *f)
+{
+ int i;
+ Chan *c;
+
+ if(f != nil && decref(&f->r) == 0) {
+ for(i = 0; i <= f->maxfd; i++)
+ if(c = f->fd[i])
+ cclose(c);
+ free(f->fd);
+ free(f);
+ }
+}
+
+Mount*
+newmount(Mhead *mh, Chan *to, int flag, char *spec)
+{
+ Mount *m;
+
+ m = malloc(sizeof(Mount));
+ if(m == nil)
+ error(Enomem);
+ m->to = to;
+ m->head = mh;
+ incref(&to->r);
+ m->mountid = incref(&mountid);
+ m->mflag = flag;
+ if(spec != 0)
+ kstrdup(&m->spec, spec);
+
+ return m;
+}
+
+void
+mountfree(Mount *m)
+{
+ Mount *f;
+
+ while(m) {
+ f = m->next;
+ cclose(m->to);
+ m->mountid = 0;
+ free(m->spec);
+ free(m);
+ m = f;
+ }
+}
+
+void
+closesigs(Skeyset *s)
+{
+ int i;
+
+ if(s == nil || decref(&s->r) != 0)
+ return;
+ for(i=0; i<s->nkey; i++)
+ freeskey(s->keys[i]);
+ free(s);
+}
+
+void
+freeskey(Signerkey *key)
+{
+ if(key == nil || decref(&key->r) != 0)
+ return;
+ free(key->owner);
+ (*key->pkfree)(key->pk);
+ free(key);
+}
--- /dev/null
+++ b/emu/port/portmkfile
@@ -1,0 +1,170 @@
+PORTHFILES=\
+ $ROOT/$OBJDIR/include/lib9.h\
+ $ROOT/$OBJDIR/include/emu.h\
+ $ROOT/include/fcall.h\
+ $ROOT/include/interp.h\
+ $ROOT/include/draw.h\
+ ../port/error.h\
+ ../port/dat.h\
+ ../port/fns.h\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBFILES=${LIBS:%=$ROOT/$SYSTARG/$OBJTYPE/lib/lib%.a}
+
+$ROOT/$SYSTARG/$OBJTYPE/lib/lib%.a:N: lib%.a
+
+%.$O: %.s
+ $AS $ASFLAGS $stem.s
+
+%.$O: %.S$MACOSINF
+ $AS $ASFLAGS $stem.S
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
+
+%.$O: ../port/%.c
+ $CC $CFLAGS -I. ../port/$stem.c
+
+%.$O: ../ip/%.c
+ $CC $CFLAGS -I. ../ip/$stem.c
+
+&.$O: $HFILES $PORTHFILES
+
+$INSTALLDIR/%: %
+ cp $stem $INSTALLDIR/$stem
+
+installall:V: install-$SHELLTYPE
+all:V: default-$SHELLTYPE
+
+# Plan 9 only (requires the compiler)
+acid:V: i$CONF.acid
+
+i$CONF.acid: i$CONF
+ {
+ for (i in `{srclist -ec -r $ROOT/ i$CONF}) {
+ echo '//FILE: ' $i
+ $CC -I. -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp '-DKERNDATE='$KERNDATE -a $i
+ }
+ echo 'include ("inferno");'
+ } >i$CONF.acid
+
+lib%.a:V: $SHELLTYPE-lib%.a
+
+rc-lib%.a nt-lib%.a:VQ:
+ echo '@{builtin cd' $ROOT/lib$stem '; mk $MKFLAGS SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}'
+ @{builtin cd $ROOT/lib$stem; mk $MKFLAGS 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install}
+
+sh-lib%.a:VQ:
+ echo "(cd $ROOT/lib$stem ; mk $MKFLAGS SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)"
+ (cd $ROOT/lib$stem; mk $MKFLAGS 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install)
+
+%-rc %-nt:V:
+ for(i in $CONFLIST)
+ mk 'CONF='$i $MKFLAGS $stem
+
+%-sh:V:
+ for i in $CONFLIST
+ do
+ mk 'CONF='$i $MKFLAGS $stem
+ done
+
+clean:V: cleanconf-$SHELLTYPE
+ rm -f *.[$OS] *.root.[shc] errstr.h *.out *.obj
+
+cleanconf-sh:V:
+ for i in $CONFLIST $CLEANCONFLIST
+ do
+ rm -f $i.c [$OS].$i i$i i$i.* $i.ver
+ done
+
+cleanconf-rc:V:
+ for(i in $CONFLIST $CLEANCONFLIST)
+ rm -f $i.c [$OS].$i i$i i$i.* $i.ver
+
+cleanconf-nt:V:
+ for(i in $CONFLIST $CLEANCONFLIST)
+ rm -f $i.c [$OS].$i i$i i$i.* $i.ver $i.exe
+
+nuke-sh:QV:
+ for i in $LIBDIRS
+ do
+ echo "(cd $ROOT/lib$i ; mk $MKFLAGS SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)"
+ (cd $ROOT/lib$i; mk $MKFLAGS 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke)
+ done
+
+nuke-rc nuke-nt:QV:
+ for (i in $LIBDIRS)
+ {
+ echo '@{cd $ROOT/lib$i ; mk $MKFLAGS SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}'
+ @{cd $ROOT/lib$i; mk $MKFLAGS 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke}
+ }
+
+nuke:V: clean nuke-$SHELLTYPE
+ rm -f srv.h srvm.h
+
+$CONF.c: ../port/mkdevc $CONF
+ $SHELLNAME ../port/mkdevc $CONF > $CONF.c
+
+errstr.h: ../port/error.h
+ sed 's/extern //;s,;.*/\* , = ",;s, \*/,";,' < ../port/error.h > errstr.h
+
+../init/%.dis: ../init/%.b
+ cd ../init; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem.dis
+
+$ROOT/libinterp/runt.h:
+ cd $ROOT/libinterp
+ mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE runt.h
+
+INTERP=$ROOT/include/interp.h
+RUNT=$ROOT/libinterp/runt.h # for culling dependencies
+
+alloc.$O: ../../include/pool.h\
+ $INTERP
+devcons.$O: $ROOT/include/version.h
+devdraw.$O: $ROOT/include/draw.h\
+ $ROOT/include/memdraw.h\
+ $ROOT/include/memlayer.h
+devmem.$O: $INTERP
+devpipe.$O: $INTERP
+devprof.$O: $INTERP
+devprog.$O: $ROOT/libinterp/runt.h\
+ $INTERP
+devsign.$O: $INTERP
+devsign.$O: $ROOT/libkeyring/keys.h
+devsrv.$O: $ROOT/libinterp/runt.h\
+ $INTERP
+devtk.$O: $INTERP
+dis.$O: $INTERP
+discall.$O: $INTERP
+exception.$O: $INTERP
+inferno.$O: $ROOT/libinterp/runt.h\
+ $INTERP
+devmnt.$O: ../../include/fcall.h
+main.$O: $ROOT/include/version.h
+main.$O: ../port/error.h\
+ $INTERP
+proc.$O: errstr.h\
+ $INTERP
+srv.$O: $INTERP
+devroot.$O: errstr.h
+latin1.$O: ../port/latin1.h
+netif.$O: ../port/netif.h
+devprog.$O: $RUNT
+devsrv.$O: $RUNT
+exception.$O: $RUNT
+inferno.$O: $RUNT
+ipif-$SYSTARG.$O devip.$O: ../port/ip.h
+
+devroot.$O: $CONF.root.h
+$CONF.$O: $CONF.root.h
+$CONF.root.c $CONF.root.h: $CONF ../port/mkroot $ROOTFILES
+ $SHELLNAME ../port/mkroot $CONF
+
+audio.c:N: ../port/audio-tbls.c
+audio.c:N: ../port/audio.h
+
+srv.$O: srv.h srvm.h
+srv.h srvm.h:D: $ROOT/module/srvrunt.b $ROOT/module/srv.m
+ rm -f $alltarget
+ limbo -a -I$ROOT/module $ROOT/module/srvrunt.b >srv.h
+ limbo -t Srv -I$ROOT/module $ROOT/module/srvrunt.b >srvm.h
--- /dev/null
+++ b/emu/port/print.c
@@ -1,0 +1,23 @@
+#include "dat.h"
+#include "fns.h"
+
+static Lock fmtl;
+
+void
+_fmtlock(void)
+{
+ lock(&fmtl);
+}
+
+void
+_fmtunlock(void)
+{
+ unlock(&fmtl);
+}
+
+int
+_efgfmt(Fmt *f)
+{
+ USED(f);
+ return -1;
+}
--- /dev/null
+++ b/emu/port/proc.c
@@ -1,0 +1,239 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+
+Proc*
+newproc(void)
+{
+ Proc *p;
+
+ p = malloc(sizeof(Proc));
+ if(p == nil)
+ return nil;
+
+ p->type = Unknown;
+ p->killed = 0;
+ p->swipend = 0;
+ p->env = &p->defenv;
+ kstrdup(&p->env->user, "*nouser");
+ p->env->errstr = p->env->errbuf0;
+ p->env->syserrstr = p->env->errbuf1;
+ addprog(p);
+
+ return p;
+}
+
+void
+Sleep(Rendez *r, int (*f)(void*), void *arg)
+{
+ lock(&up->rlock);
+ lock(&r->l);
+
+ /*
+ * if interrupted or condition happened, never mind
+ */
+ if(up->killed || f(arg)) {
+ unlock(&r->l);
+ }else{
+ if(r->p != nil)
+ panic("double sleep pc=0x%lux %s[%lud] %s[%lud] r=0x%lux\n", getcallerpc(&r), r->p->text, r->p->pid, up->text, up->pid, r);
+
+ r->p = up;
+ unlock(&r->l);
+ up->swipend = 0;
+ up->r = r; /* for swiproc */
+ unlock(&up->rlock);
+
+ osblock();
+
+ lock(&up->rlock);
+ up->r = nil;
+ }
+
+ if(up->killed || up->swipend) {
+ up->killed = 0;
+ up->swipend = 0;
+ unlock(&up->rlock);
+ error(Eintr);
+ }
+ unlock(&up->rlock);
+}
+
+int
+Wakeup(Rendez *r)
+{
+ Proc *p;
+
+ lock(&r->l);
+ p = r->p;
+ if(p != nil) {
+ r->p = nil;
+ osready(p);
+ }
+ unlock(&r->l);
+ return p != nil;
+}
+
+void
+swiproc(Proc *p, int interp)
+{
+ Rendez *r;
+
+ if(p == nil)
+ return;
+
+ /*
+ * Pull out of emu Sleep
+ */
+ lock(&p->rlock);
+ if(!interp)
+ p->killed = 1;
+ r = p->r;
+ if(r != nil) {
+ lock(&r->l);
+ if(r->p == p){
+ p->swipend = 1;
+ r->p = nil;
+ osready(p);
+ }
+ unlock(&r->l);
+ unlock(&p->rlock);
+ return;
+ }
+ unlock(&p->rlock);
+
+ /*
+ * Maybe pull out of Host OS
+ */
+ lock(&p->sysio);
+ if(p->syscall && p->intwait == 0) {
+ p->swipend = 1;
+ p->intwait = 1;
+ unlock(&p->sysio);
+ oshostintr(p);
+ return;
+ }
+ unlock(&p->sysio);
+}
+
+void
+notkilled(void)
+{
+ lock(&up->rlock);
+ up->killed = 0;
+ unlock(&up->rlock);
+}
+
+void
+osenter(void)
+{
+ up->syscall = 1;
+}
+
+void
+osleave(void)
+{
+ int r;
+
+ lock(&up->rlock);
+ r = up->swipend;
+ up->swipend = 0;
+ unlock(&up->rlock);
+
+ lock(&up->sysio);
+ up->syscall = 0;
+ unlock(&up->sysio);
+
+ /* Cleared by the signal/note/exception handler */
+ while(up->intwait)
+ osyield();
+
+ if(r != 0)
+ error(Eintr);
+}
+
+void
+rptwakeup(void *o, void *ar)
+{
+ Rept *r;
+
+ r = ar;
+ if(r == nil)
+ return;
+ lock(&r->l);
+ r->o = o;
+ unlock(&r->l);
+ Wakeup(&r->r);
+}
+
+static int
+rptactive(void *a)
+{
+ Rept *r = a;
+ int i;
+ lock(&r->l);
+ i = r->active(r->o);
+ unlock(&r->l);
+ return i;
+}
+
+static void
+rproc(void *a)
+{
+ long now, then;
+ ulong t;
+ int i;
+ void *o;
+ Rept *r;
+
+ up->env->fgrp = newfgrp(nil);
+ r = a;
+ t = (ulong)r->t;
+
+Wait:
+ Sleep(&r->r, rptactive, r);
+ lock(&r->l);
+ o = r->o;
+ unlock(&r->l);
+ then = osmillisec();
+ for(;;){
+ osmillisleep(t);
+ now = osmillisec();
+ if(waserror())
+ break;
+ i = r->ck(o, now-then);
+ poperror();
+ if(i == -1)
+ goto Wait;
+ if(i == 0)
+ continue;
+ then = now;
+ acquire();
+ if(waserror()) {
+ release();
+ break;
+ }
+ r->f(o);
+ poperror();
+ release();
+ }
+ pexit("", 0);
+}
+
+void*
+rptproc(char *s, int t, void *o, int (*active)(void*), int (*ck)(void*, int), void (*f)(void*))
+{
+ Rept *r;
+
+ r = mallocz(sizeof(Rept), 1);
+ if(r == nil)
+ return nil;
+ r->t = t;
+ r->active = active;
+ r->ck = ck;
+ r->f = f;
+ r->o = o;
+ kproc(s, rproc, r, KPDUPPG);
+ return r;
+}
--- /dev/null
+++ b/emu/port/qio.c
@@ -1,0 +1,1554 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#define QDEBUG if(0)
+
+/*
+ * IO queues
+ */
+
+struct Queue
+{
+ Lock l;
+
+ Block* bfirst; /* buffer */
+ Block* blast;
+
+ int len; /* bytes allocated to queue */
+ int dlen; /* data bytes in queue */
+ int limit; /* max bytes in queue */
+ int inilim; /* initial limit */
+ int state;
+ int noblock; /* true if writes return immediately when q full */
+ int eof; /* number of eofs read by user */
+
+ void (*kick)(void*); /* restart output */
+ void (*bypass)(void*, Block*); /* bypass queue altogether */
+ void* arg; /* argument to kick */
+
+ QLock rlock; /* mutex for reading processes */
+ Rendez rr; /* process waiting to read */
+ QLock wlock; /* mutex for writing processes */
+ Rendez wr; /* process waiting to write */
+
+ char err[ERRMAX];
+ int want;
+};
+
+enum
+{
+ Maxatomic = 32*1024
+};
+
+uint qiomaxatomic = Maxatomic;
+
+void
+checkb(Block *b, char *msg)
+{
+ if(b->base > b->lim)
+ panic("checkb 0 %s %lux %lux", msg, b->base, b->lim);
+ if(b->rp < b->base)
+ panic("checkb 1 %s %lux %lux", msg, b->base, b->rp);
+ if(b->wp < b->base)
+ panic("checkb 2 %s %lux %lux", msg, b->base, b->wp);
+ if(b->rp > b->lim)
+ panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim);
+ if(b->wp > b->lim)
+ panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim);
+}
+
+void
+freeb(Block *b)
+{
+ if(b == nil)
+ return;
+
+ /*
+ * drivers which perform non cache coherent DMA manage their own buffer
+ * pool of uncached buffers and provide their own free routine.
+ */
+ if(b->free) {
+ b->free(b);
+ return;
+ }
+
+ /* poison the block in case someone is still holding onto it */
+ b->next = (void*)0xdeadbabe;
+ b->rp = (void*)0xdeadbabe;
+ b->wp = (void*)0xdeadbabe;
+ b->lim = (void*)0xdeadbabe;
+ b->base = (void*)0xdeadbabe;
+
+ free(b);
+}
+
+/*
+ * free a list of blocks
+ */
+void
+freeblist(Block *b)
+{
+ Block *next;
+
+ for(; b != 0; b = next){
+ next = b->next;
+ b->next = 0;
+ freeb(b);
+ }
+}
+
+ulong padblockoverhead;
+
+/*
+ * pad a block to the front (or the back if size is negative)
+ */
+Block*
+padblock(Block *bp, int size)
+{
+ int n;
+ Block *nbp;
+
+ if(size >= 0){
+ if(bp->rp - bp->base >= size){
+ bp->rp -= size;
+ return bp;
+ }
+
+ if(bp->next)
+ panic("padblock 0x%luX", getcallerpc(&bp));
+ n = BLEN(bp);
+ padblockoverhead += n;
+ nbp = allocb(size+n);
+ nbp->rp += size;
+ nbp->wp = nbp->rp;
+ memmove(nbp->wp, bp->rp, n);
+ nbp->wp += n;
+ freeb(bp);
+ nbp->rp -= size;
+ } else {
+ size = -size;
+
+ if(bp->next)
+ panic("padblock 0x%luX", getcallerpc(&bp));
+
+ if(bp->lim - bp->wp >= size)
+ return bp;
+
+ n = BLEN(bp);
+ padblockoverhead += n;
+ nbp = allocb(size+n);
+ memmove(nbp->wp, bp->rp, n);
+ nbp->wp += n;
+ freeb(bp);
+ }
+ return nbp;
+}
+
+/*
+ * return count of bytes in a string of blocks
+ */
+int
+blocklen(Block *bp)
+{
+ int len;
+
+ len = 0;
+ while(bp) {
+ len += BLEN(bp);
+ bp = bp->next;
+ }
+ return len;
+}
+
+/*
+ * return count of space in blocks
+ */
+int
+blockalloclen(Block *bp)
+{
+ int len;
+
+ len = 0;
+ while(bp) {
+ len += BALLOC(bp);
+ bp = bp->next;
+ }
+ return len;
+}
+
+/*
+ * copy the string of blocks into
+ * a single block and free the string
+ */
+Block*
+concatblock(Block *bp)
+{
+ int len;
+ Block *nb, *f;
+
+ if(bp->next == 0)
+ return bp;
+
+ nb = allocb(blocklen(bp));
+ for(f = bp; f; f = f->next) {
+ len = BLEN(f);
+ memmove(nb->wp, f->rp, len);
+ nb->wp += len;
+ }
+ freeblist(bp);
+ return nb;
+}
+
+/*
+ * make sure the first block has at least n bytes. If we started with
+ * less than n bytes, make sure we have exactly n bytes. devssl.c depends
+ * on this.
+ */
+Block*
+pullupblock(Block *bp, int n)
+{
+ int i;
+ Block *nbp;
+
+ /*
+ * this should almost always be true, the rest it
+ * just to avoid every caller checking.
+ */
+ if(BLEN(bp) >= n)
+ return bp;
+
+ /*
+ * if not enough room in the first block,
+ * add another to the front of the list.
+ */
+ if(bp->lim - bp->rp < n){
+ nbp = allocb(n);
+ nbp->next = bp;
+ bp = nbp;
+ }
+
+ /*
+ * copy bytes from the trailing blocks into the first
+ */
+ n -= BLEN(bp);
+ while(nbp = bp->next){
+ i = BLEN(nbp);
+ if(i > n) {
+ memmove(bp->wp, nbp->rp, n);
+ bp->wp += n;
+ nbp->rp += n;
+ return bp;
+ }
+ else {
+ memmove(bp->wp, nbp->rp, i);
+ bp->wp += i;
+ bp->next = nbp->next;
+ nbp->next = 0;
+ freeb(nbp);
+ n -= i;
+ if(n == 0)
+ return bp;
+ }
+ }
+ freeblist(bp);
+ return 0;
+}
+
+/*
+ * make sure the first block has at least n bytes
+ */
+Block*
+pullupqueue(Queue *q, int n)
+{
+ Block *b;
+
+ if(BLEN(q->bfirst) >= n)
+ return q->bfirst;
+ q->bfirst = pullupblock(q->bfirst, n);
+ for(b = q->bfirst; b != nil && b->next != nil; b = b->next)
+ ;
+ q->blast = b;
+ return q->bfirst;
+}
+
+/*
+ * trim to len bytes starting at offset
+ */
+Block *
+trimblock(Block *bp, int offset, int len)
+{
+ ulong l;
+ Block *nb, *startb;
+
+ if(blocklen(bp) < offset+len) {
+ freeblist(bp);
+ return nil;
+ }
+
+ while((l = BLEN(bp)) < offset) {
+ offset -= l;
+ nb = bp->next;
+ bp->next = nil;
+ freeb(bp);
+ bp = nb;
+ }
+
+ startb = bp;
+ bp->rp += offset;
+
+ while((l = BLEN(bp)) < len) {
+ len -= l;
+ bp = bp->next;
+ }
+
+ bp->wp -= (BLEN(bp) - len);
+
+ if(bp->next) {
+ freeblist(bp->next);
+ bp->next = nil;
+ }
+
+ return startb;
+}
+
+/*
+ * copy 'count' bytes into a new block
+ */
+Block*
+copyblock(Block *bp, int count)
+{
+ int l;
+ Block *nbp;
+
+ nbp = allocb(count);
+ for(; count > 0 && bp != 0; bp = bp->next){
+ l = BLEN(bp);
+ if(l > count)
+ l = count;
+ memmove(nbp->wp, bp->rp, l);
+ nbp->wp += l;
+ count -= l;
+ }
+ if(count > 0){
+ memset(nbp->wp, 0, count);
+ nbp->wp += count;
+ }
+
+ return nbp;
+}
+
+Block*
+adjustblock(Block* bp, int len)
+{
+ int n;
+ Block *nbp;
+
+ if(len < 0){
+ freeb(bp);
+ return nil;
+ }
+
+ if(bp->rp+len > bp->lim){
+ nbp = copyblock(bp, len);
+ freeblist(bp);
+ QDEBUG checkb(nbp, "adjustblock 1");
+
+ return nbp;
+ }
+
+ n = BLEN(bp);
+ if(len > n)
+ memset(bp->wp, 0, len-n);
+ bp->wp = bp->rp+len;
+ QDEBUG checkb(bp, "adjustblock 2");
+
+ return bp;
+}
+
+/*
+ * throw away up to count bytes from a
+ * list of blocks. Return count of bytes
+ * thrown away.
+ */
+int
+pullblock(Block **bph, int count)
+{
+ Block *bp;
+ int n, bytes;
+
+ bytes = 0;
+ if(bph == nil)
+ return 0;
+
+ while(*bph != nil && count != 0) {
+ bp = *bph;
+ n = BLEN(bp);
+ if(count < n)
+ n = count;
+ bytes += n;
+ count -= n;
+ bp->rp += n;
+ if(BLEN(bp) == 0) {
+ *bph = bp->next;
+ bp->next = nil;
+ freeb(bp);
+ }
+ }
+ return bytes;
+}
+
+/*
+ * allocate queues and blocks (round data base address to 64 bit boundary)
+ */
+Block*
+iallocb(int size)
+{
+ Block *b;
+ ulong addr;
+
+ b = kmalloc(sizeof(Block)+size);
+ if(b == 0)
+ return 0;
+ memset(b, 0, sizeof(Block));
+
+ addr = (ulong)b + sizeof(Block);
+ b->base = (uchar*)addr;
+ b->lim = b->base + size;
+ b->rp = b->base;
+ b->wp = b->rp;
+
+ return b;
+}
+
+
+/*
+ * call error if iallocb fails
+ */
+Block*
+allocb(int size)
+{
+ Block *b;
+
+ b = iallocb(size);
+ if(b == 0)
+ exhausted("allocb");
+ return b;
+}
+
+
+/*
+ * get next block from a queue, return null if nothing there
+ */
+Block*
+qget(Queue *q)
+{
+ int dowakeup;
+ Block *b;
+
+ /* sync with qwrite */
+ lock(&q->l);
+
+ b = q->bfirst;
+ if(b == 0){
+ q->state |= Qstarve;
+ unlock(&q->l);
+ return 0;
+ }
+ q->bfirst = b->next;
+ b->next = 0;
+ q->len -= BALLOC(b);
+ q->dlen -= BLEN(b);
+ QDEBUG checkb(b, "qget");
+
+ /* if writer flow controlled, restart */
+ if((q->state & Qflow) && q->len < q->limit/2){
+ q->state &= ~Qflow;
+ dowakeup = 1;
+ } else
+ dowakeup = 0;
+
+ unlock(&q->l);
+
+ if(dowakeup)
+ Wakeup(&q->wr);
+
+ return b;
+}
+
+/*
+ * throw away the next 'len' bytes in the queue
+ */
+int
+qdiscard(Queue *q, int len)
+{
+ Block *b;
+ int dowakeup, n, sofar;
+
+ lock(&q->l);
+ for(sofar = 0; sofar < len; sofar += n){
+ b = q->bfirst;
+ if(b == nil)
+ break;
+ QDEBUG checkb(b, "qdiscard");
+ n = BLEN(b);
+ if(n <= len - sofar){
+ q->bfirst = b->next;
+ b->next = 0;
+ q->len -= BALLOC(b);
+ q->dlen -= BLEN(b);
+ freeb(b);
+ } else {
+ n = len - sofar;
+ b->rp += n;
+ q->dlen -= n;
+ }
+ }
+
+ /* if writer flow controlled, restart */
+ if((q->state & Qflow) && q->len < q->limit){
+ q->state &= ~Qflow;
+ dowakeup = 1;
+ } else
+ dowakeup = 0;
+
+ unlock(&q->l);
+
+ if(dowakeup)
+ Wakeup(&q->wr);
+
+ return sofar;
+}
+
+/*
+ * Interrupt level copy out of a queue, return # bytes copied.
+ */
+int
+qconsume(Queue *q, void *vp, int len)
+{
+ Block *b;
+ int n, dowakeup;
+ uchar *p = vp;
+ Block *tofree = nil;
+
+ /* sync with qwrite */
+ lock(&q->l);
+
+ for(;;) {
+ b = q->bfirst;
+ if(b == 0){
+ q->state |= Qstarve;
+ unlock(&q->l);
+ return -1;
+ }
+ QDEBUG checkb(b, "qconsume 1");
+
+ n = BLEN(b);
+ if(n > 0)
+ break;
+ q->bfirst = b->next;
+ q->len -= BALLOC(b);
+
+ /* remember to free this */
+ b->next = tofree;
+ tofree = b;
+ }
+
+ if(n < len)
+ len = n;
+ memmove(p, b->rp, len);
+ if((q->state & Qmsg) || len == n)
+ q->bfirst = b->next;
+ b->rp += len;
+ q->dlen -= len;
+
+ /* discard the block if we're done with it */
+ if((q->state & Qmsg) || len == n){
+ b->next = 0;
+ q->len -= BALLOC(b);
+ q->dlen -= BLEN(b);
+
+ /* remember to free this */
+ b->next = tofree;
+ tofree = b;
+ }
+
+ /* if writer flow controlled, restart */
+ if((q->state & Qflow) && q->len < q->limit/2){
+ q->state &= ~Qflow;
+ dowakeup = 1;
+ } else
+ dowakeup = 0;
+
+ unlock(&q->l);
+
+ if(dowakeup)
+ Wakeup(&q->wr);
+
+ if(tofree != nil)
+ freeblist(tofree);
+
+ return len;
+}
+
+int
+qpass(Queue *q, Block *b)
+{
+ int dlen, len, dowakeup;
+
+ /* sync with qread */
+ dowakeup = 0;
+ lock(&q->l);
+ if(q->len >= q->limit){
+ unlock(&q->l);
+ freeblist(b);
+ return -1;
+ }
+ if(q->state & Qclosed){
+ unlock(&q->l);
+ len = blocklen(b);
+ freeblist(b);
+ return len;
+ }
+
+ /* add buffer to queue */
+ if(q->bfirst)
+ q->blast->next = b;
+ else
+ q->bfirst = b;
+ len = BALLOC(b);
+ dlen = BLEN(b);
+ QDEBUG checkb(b, "qpass");
+ while(b->next){
+ b = b->next;
+ QDEBUG checkb(b, "qpass");
+ len += BALLOC(b);
+ dlen += BLEN(b);
+ }
+ q->blast = b;
+ q->len += len;
+ q->dlen += dlen;
+
+ if(q->len >= q->limit/2)
+ q->state |= Qflow;
+
+ if(q->state & Qstarve){
+ q->state &= ~Qstarve;
+ dowakeup = 1;
+ }
+ unlock(&q->l);
+
+ if(dowakeup)
+ Wakeup(&q->rr);
+
+ return len;
+}
+
+int
+qpassnolim(Queue *q, Block *b)
+{
+ int dlen, len, dowakeup;
+
+ /* sync with qread */
+ dowakeup = 0;
+ lock(&q->l);
+
+ len = BALLOC(b);
+ if(q->state & Qclosed){
+ unlock(&q->l);
+ freeblist(b);
+ return len;
+ }
+
+ /* add buffer to queue */
+ if(q->bfirst)
+ q->blast->next = b;
+ else
+ q->bfirst = b;
+ dlen = BLEN(b);
+ QDEBUG checkb(b, "qpass");
+ while(b->next){
+ b = b->next;
+ QDEBUG checkb(b, "qpass");
+ len += BALLOC(b);
+ dlen += BLEN(b);
+ }
+ q->blast = b;
+ q->len += len;
+ q->dlen += dlen;
+
+ if(q->len >= q->limit/2)
+ q->state |= Qflow;
+
+ if(q->state & Qstarve){
+ q->state &= ~Qstarve;
+ dowakeup = 1;
+ }
+ unlock(&q->l);
+
+ if(dowakeup)
+ Wakeup(&q->rr);
+
+ return len;
+}
+
+/*
+ * if the allocated space is way out of line with the used
+ * space, reallocate to a smaller block
+ */
+Block*
+packblock(Block *bp)
+{
+ Block **l, *nbp;
+ int n;
+
+ for(l = &bp; *l; l = &(*l)->next){
+ nbp = *l;
+ n = BLEN(nbp);
+ if((n<<2) < BALLOC(nbp)){
+ *l = allocb(n);
+ memmove((*l)->wp, nbp->rp, n);
+ (*l)->wp += n;
+ (*l)->next = nbp->next;
+ freeb(nbp);
+ }
+ }
+
+ return bp;
+}
+
+int
+qproduce(Queue *q, void *vp, int len)
+{
+ Block *b;
+ int dowakeup;
+ uchar *p = vp;
+
+ /* sync with qread */
+ dowakeup = 0;
+ lock(&q->l);
+
+ if(q->state & Qclosed){
+ unlock(&q->l);
+ return -1;
+ }
+
+ /* no waiting receivers, room in buffer? */
+ if(q->len >= q->limit){
+ q->state |= Qflow;
+ unlock(&q->l);
+ return -1;
+ }
+
+ /* save in buffer */
+ b = iallocb(len);
+ if(b == 0){
+ unlock(&q->l);
+ print("qproduce: iallocb failed\n");
+ return -1;
+ }
+ memmove(b->wp, p, len);
+ b->wp += len;
+ if(q->bfirst)
+ q->blast->next = b;
+ else
+ q->bfirst = b;
+ q->blast = b;
+ /* b->next = 0; done by allocb() */
+ q->len += BALLOC(b);
+ q->dlen += BLEN(b);
+ QDEBUG checkb(b, "qproduce");
+
+ if(q->state & Qstarve){
+ q->state &= ~Qstarve;
+ dowakeup = 1;
+ }
+
+ if(q->len >= q->limit)
+ q->state |= Qflow;
+ unlock(&q->l);
+
+
+ if(dowakeup)
+ Wakeup(&q->rr);
+
+ return len;
+}
+
+/*
+ * copy from offset in the queue
+ */
+Block*
+qcopy(Queue *q, int len, ulong offset)
+{
+ int sofar;
+ int n;
+ Block *b, *nb;
+ uchar *p;
+
+ nb = allocb(len);
+
+ lock(&q->l);
+
+ /* go to offset */
+ b = q->bfirst;
+ for(sofar = 0; ; sofar += n){
+ if(b == nil){
+ unlock(&q->l);
+ return nb;
+ }
+ n = BLEN(b);
+ if(sofar + n > offset){
+ p = b->rp + offset - sofar;
+ n -= offset - sofar;
+ break;
+ }
+ b = b->next;
+ }
+
+ /* copy bytes from there */
+ for(sofar = 0; sofar < len;){
+ if(n > len - sofar)
+ n = len - sofar;
+ memmove(nb->wp, p, n);
+ sofar += n;
+ nb->wp += n;
+ b = b->next;
+ if(b == nil)
+ break;
+ n = BLEN(b);
+ p = b->rp;
+ }
+ unlock(&q->l);
+
+ return nb;
+}
+
+/*
+ * called by non-interrupt code
+ */
+Queue*
+qopen(int limit, int msg, void (*kick)(void*), void *arg)
+{
+ Queue *q;
+
+ q = kmalloc(sizeof(Queue));
+ if(q == 0)
+ return 0;
+
+ q->limit = q->inilim = limit;
+ q->kick = kick;
+ q->arg = arg;
+ q->state = msg;
+ q->state |= Qstarve;
+ q->eof = 0;
+ q->noblock = 0;
+
+ return q;
+}
+
+/* open a queue to be bypassed */
+Queue*
+qbypass(void (*bypass)(void*, Block*), void *arg)
+{
+ Queue *q;
+
+ q = malloc(sizeof(Queue));
+ if(q == 0)
+ return 0;
+
+ q->limit = 0;
+ q->arg = arg;
+ q->bypass = bypass;
+ q->state = 0;
+
+ return q;
+}
+
+static int
+notempty(void *a)
+{
+ Queue *q = a;
+
+ return (q->state & Qclosed) || q->bfirst != 0;
+}
+
+/*
+ * wait for the queue to be non-empty or closed.
+ * called with q ilocked.
+ */
+static int
+qwait(Queue *q)
+{
+ /* wait for data */
+ for(;;){
+ if(q->bfirst != nil)
+ break;
+
+ if(q->state & Qclosed){
+ if(++q->eof > 3)
+ return -1;
+ if(*q->err && strcmp(q->err, Ehungup) != 0)
+ return -1;
+ return 0;
+ }
+
+ q->state |= Qstarve; /* flag requesting producer to wake me */
+ unlock(&q->l);
+ Sleep(&q->rr, notempty, q);
+ lock(&q->l);
+ }
+ return 1;
+}
+
+/*
+ * add a block list to a queue
+ */
+void
+qaddlist(Queue *q, Block *b)
+{
+ /* queue the block */
+ if(q->bfirst)
+ q->blast->next = b;
+ else
+ q->bfirst = b;
+ q->len += blockalloclen(b);
+ q->dlen += blocklen(b);
+ while(b->next)
+ b = b->next;
+ q->blast = b;
+}
+
+/*
+ * called with q locked
+ */
+Block*
+qremove(Queue *q)
+{
+ Block *b;
+
+ b = q->bfirst;
+ if(b == nil)
+ return nil;
+ q->bfirst = b->next;
+ b->next = nil;
+ q->dlen -= BLEN(b);
+ q->len -= BALLOC(b);
+ QDEBUG checkb(b, "qremove");
+ return b;
+}
+
+/*
+ * copy the contents of a string of blocks into
+ * memory. emptied blocks are freed. return
+ * pointer to first unconsumed block.
+ */
+Block*
+bl2mem(uchar *p, Block *b, int n)
+{
+ int i;
+ Block *next;
+
+ for(; b != nil; b = next){
+ i = BLEN(b);
+ if(i > n){
+ memmove(p, b->rp, n);
+ b->rp += n;
+ return b;
+ }
+ memmove(p, b->rp, i);
+ n -= i;
+ p += i;
+ b->rp += i;
+ next = b->next;
+ freeb(b);
+ }
+ return nil;
+}
+
+/*
+ * copy the contents of memory into a string of blocks.
+ * return nil on error.
+ */
+Block*
+mem2bl(uchar *p, int len)
+{
+ int n;
+ Block *b, *first, **l;
+
+ first = nil;
+ l = &first;
+ if(waserror()){
+ freeblist(first);
+ nexterror();
+ }
+ do {
+ n = len;
+ if(n > Maxatomic)
+ n = Maxatomic;
+
+ *l = b = allocb(n);
+ setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]);
+ memmove(b->wp, p, n);
+ b->wp += n;
+ p += n;
+ len -= n;
+ l = &b->next;
+ } while(len > 0);
+ poperror();
+
+ return first;
+}
+
+/*
+ * put a block back to the front of the queue
+ * called with q ilocked
+ */
+void
+qputback(Queue *q, Block *b)
+{
+ b->next = q->bfirst;
+ if(q->bfirst == nil)
+ q->blast = b;
+ q->bfirst = b;
+ q->len += BALLOC(b);
+ q->dlen += BLEN(b);
+}
+
+/*
+ * flow control, get producer going again
+ * called with q locked
+ */
+static void
+qwakeup_unlock(Queue *q)
+{
+ int dowakeup = 0;
+
+ /* if writer flow controlled, restart */
+ if((q->state & Qflow) && q->len < q->limit/2){
+ q->state &= ~Qflow;
+ dowakeup = 1;
+ }
+
+ unlock(&q->l);
+
+ /* wakeup flow controlled writers */
+ if(dowakeup){
+ if(q->kick)
+ q->kick(q->arg);
+ Wakeup(&q->wr);
+ }
+}
+
+/*
+ * get next block from a queue (up to a limit)
+ */
+Block*
+qbread(Queue *q, int len)
+{
+ Block *b, *nb;
+ int n;
+
+ qlock(&q->rlock);
+ if(waserror()){
+ qunlock(&q->rlock);
+ nexterror();
+ }
+
+ lock(&q->l);
+ switch(qwait(q)){
+ case 0:
+ /* queue closed */
+ unlock(&q->l);
+ poperror();
+ qunlock(&q->rlock);
+ return nil;
+ case -1:
+ /* multiple reads on a closed queue */
+ unlock(&q->l);
+ error(q->err);
+ }
+
+ /* if we get here, there's at least one block in the queue */
+ b = qremove(q);
+ n = BLEN(b);
+
+ /* split block if it's too big and this is not a message oriented queue */
+ nb = b;
+ if(n > len){
+ if((q->state&Qmsg) == 0){
+ n -= len;
+ b = allocb(n);
+ memmove(b->wp, nb->rp+len, n);
+ b->wp += n;
+ qputback(q, b);
+ }
+ nb->wp = nb->rp + len;
+ }
+
+ /* restart producer */
+ qwakeup_unlock(q);
+
+ poperror();
+ qunlock(&q->rlock);
+ return nb;
+}
+
+/*
+ * read a queue. if no data is queued, wait on its Rendez
+ */
+long
+qread(Queue *q, void *vp, int len)
+{
+ Block *b, *first, **l;
+ int m, n;
+
+ qlock(&q->rlock);
+ if(waserror()){
+ qunlock(&q->rlock);
+ nexterror();
+ }
+
+ lock(&q->l);
+again:
+ switch(qwait(q)){
+ case 0:
+ /* queue closed */
+ unlock(&q->l);
+ poperror();
+ qunlock(&q->rlock);
+ return 0;
+ case -1:
+ /* multiple reads on a closed queue */
+ unlock(&q->l);
+ error(q->err);
+ }
+
+ /* if we get here, there's at least one block in the queue */
+ if(q->state & Qcoalesce){
+ /* when coalescing, 0 length blocks just go away */
+ b = q->bfirst;
+ if(BLEN(b) <= 0){
+ freeb(qremove(q));
+ goto again;
+ }
+
+ /* grab the first block plus as many
+ * following blocks as will completely
+ * fit in the read.
+ */
+ n = 0;
+ l = &first;
+ m = BLEN(b);
+ for(;;) {
+ *l = qremove(q);
+ l = &b->next;
+ n += m;
+
+ b = q->bfirst;
+ if(b == nil)
+ break;
+ m = BLEN(b);
+ if(n+m > len)
+ break;
+ }
+ } else {
+ first = qremove(q);
+ n = BLEN(first);
+ }
+
+ /* copy to user space outside of the ilock */
+ unlock(&q->l);
+ b = bl2mem(vp, first, len);
+ lock(&q->l);
+
+ /* take care of any left over partial block */
+ if(b != nil){
+ n -= BLEN(b);
+ if(q->state & Qmsg)
+ freeb(b);
+ else
+ qputback(q, b);
+ }
+
+ /* restart producer */
+ qwakeup_unlock(q);
+
+ poperror();
+ qunlock(&q->rlock);
+ return n;
+}
+
+static int
+qnotfull(void *a)
+{
+ Queue *q = a;
+
+ return q->len < q->limit || (q->state & Qclosed);
+}
+
+/*
+ * add a block to a queue obeying flow control
+ */
+long
+qbwrite(Queue *q, Block *b)
+{
+ int n, dowakeup;
+ volatile struct {Block *b;} cb;
+
+ dowakeup = 0;
+ n = BLEN(b);
+ if(q->bypass){
+ (*q->bypass)(q->arg, b);
+ return n;
+ }
+ cb.b = b;
+
+ qlock(&q->wlock);
+ if(waserror()){
+ if(cb.b != nil)
+ freeb(cb.b);
+ qunlock(&q->wlock);
+ nexterror();
+ }
+
+ lock(&q->l);
+
+ /* give up if the queue is closed */
+ if(q->state & Qclosed){
+ unlock(&q->l);
+ error(q->err);
+ }
+
+ /* if nonblocking, don't queue over the limit */
+ if(q->len >= q->limit){
+ if(q->noblock){
+ unlock(&q->l);
+ freeb(b);
+ poperror();
+ qunlock(&q->wlock);
+ return n;
+ }
+ }
+
+ /* queue the block */
+ if(q->bfirst)
+ q->blast->next = b;
+ else
+ q->bfirst = b;
+ q->blast = b;
+ b->next = 0;
+ q->len += BALLOC(b);
+ q->dlen += n;
+ QDEBUG checkb(b, "qbwrite");
+ cb.b = nil;
+
+ if(q->state & Qstarve){
+ q->state &= ~Qstarve;
+ dowakeup = 1;
+ }
+
+ unlock(&q->l);
+
+ /* get output going again */
+ if(q->kick && (dowakeup || (q->state&Qkick)))
+ q->kick(q->arg);
+
+ if(dowakeup)
+ Wakeup(&q->rr);
+
+ /*
+ * flow control, wait for queue to get below the limit
+ * before allowing the process to continue and queue
+ * more. We do this here so that postnote can only
+ * interrupt us after the data has been queued. This
+ * means that things like 9p flushes and ssl messages
+ * will not be disrupted by software interrupts.
+ *
+ * Note - this is moderately dangerous since a process
+ * that keeps getting interrupted and rewriting will
+ * queue infinite crud.
+ */
+ for(;;){
+ if(q->noblock || qnotfull(q))
+ break;
+
+ lock(&q->l);
+ q->state |= Qflow;
+ unlock(&q->l);
+ Sleep(&q->wr, qnotfull, q);
+ }
+
+ qunlock(&q->wlock);
+ poperror();
+ return n;
+}
+
+/*
+ * write to a queue. only Maxatomic bytes at a time is atomic.
+ */
+int
+qwrite(Queue *q, void *vp, int len)
+{
+ int n, sofar;
+ Block *b;
+ uchar *p = vp;
+
+ sofar = 0;
+ do {
+ n = len-sofar;
+ if(n > Maxatomic)
+ n = Maxatomic;
+
+ b = allocb(n);
+ setmalloctag(b, getcallerpc(&q));
+ if(waserror()){
+ freeb(b);
+ nexterror();
+ }
+ memmove(b->wp, p+sofar, n);
+ poperror();
+ b->wp += n;
+
+ qbwrite(q, b);
+
+ sofar += n;
+ } while(sofar < len && (q->state & Qmsg) == 0);
+
+ return len;
+}
+
+/*
+ * used by print() to write to a queue. Since we may be splhi or not in
+ * a process, don't qlock.
+ */
+int
+qiwrite(Queue *q, void *vp, int len)
+{
+ int n, sofar, dowakeup;
+ Block *b;
+ uchar *p = vp;
+
+ dowakeup = 0;
+
+ sofar = 0;
+ do {
+ n = len-sofar;
+ if(n > Maxatomic)
+ n = Maxatomic;
+
+ b = iallocb(n);
+ if (b == 0) {
+ print("qiwrite: iallocb failed\n");
+ break;
+ }
+ memmove(b->wp, p+sofar, n);
+ b->wp += n;
+
+ lock(&q->l);
+
+ QDEBUG checkb(b, "qiwrite");
+ if(q->bfirst)
+ q->blast->next = b;
+ else
+ q->bfirst = b;
+ q->blast = b;
+ q->len += BALLOC(b);
+ q->dlen += n;
+
+ if(q->state & Qstarve){
+ q->state &= ~Qstarve;
+ dowakeup = 1;
+ }
+
+ unlock(&q->l);
+
+ if(dowakeup){
+ if(q->kick)
+ q->kick(q->arg);
+ Wakeup(&q->rr);
+ }
+
+ sofar += n;
+ } while(sofar < len && (q->state & Qmsg) == 0);
+
+ return sofar;
+}
+
+/*
+ * be extremely careful when calling this,
+ * as there is no reference accounting
+ */
+void
+qfree(Queue *q)
+{
+ qclose(q);
+ free(q);
+}
+
+/*
+ * Mark a queue as closed. No further IO is permitted.
+ * All blocks are released.
+ */
+void
+qclose(Queue *q)
+{
+ Block *bfirst;
+
+ if(q == nil)
+ return;
+
+ /* mark it */
+ lock(&q->l);
+ q->state |= Qclosed;
+ q->state &= ~(Qflow|Qstarve);
+ strcpy(q->err, Ehungup);
+ bfirst = q->bfirst;
+ q->bfirst = 0;
+ q->len = 0;
+ q->dlen = 0;
+ q->noblock = 0;
+ unlock(&q->l);
+
+ /* free queued blocks */
+ freeblist(bfirst);
+
+ /* wake up readers/writers */
+ Wakeup(&q->rr);
+ Wakeup(&q->wr);
+}
+
+/*
+ * Mark a queue as closed. Wakeup any readers. Don't remove queued
+ * blocks.
+ */
+void
+qhangup(Queue *q, char *msg)
+{
+ /* mark it */
+ lock(&q->l);
+ q->state |= Qclosed;
+ if(msg == 0 || *msg == 0)
+ strcpy(q->err, Ehungup);
+ else
+ kstrcpy(q->err, msg, sizeof q->err);
+ unlock(&q->l);
+
+ /* wake up readers/writers */
+ Wakeup(&q->rr);
+ Wakeup(&q->wr);
+}
+
+/*
+ * return non-zero if the q is hungup
+ */
+int
+qisclosed(Queue *q)
+{
+ return q->state & Qclosed;
+}
+
+/*
+ * mark a queue as no longer hung up
+ */
+void
+qreopen(Queue *q)
+{
+ lock(&q->l);
+ q->state &= ~Qclosed;
+ q->state |= Qstarve;
+ q->eof = 0;
+ q->limit = q->inilim;
+ unlock(&q->l);
+}
+
+/*
+ * return bytes queued
+ */
+int
+qlen(Queue *q)
+{
+ return q->dlen;
+}
+
+/*
+ * return space remaining before flow control
+ */
+int
+qwindow(Queue *q)
+{
+ int l;
+
+ l = q->limit - q->len;
+ if(l < 0)
+ l = 0;
+ return l;
+}
+
+/*
+ * return true if we can read without blocking
+ */
+int
+qcanread(Queue *q)
+{
+ return q->bfirst!=0;
+}
+
+/*
+ * change queue limit
+ */
+void
+qsetlimit(Queue *q, int limit)
+{
+ q->limit = limit;
+}
+
+/*
+ * set blocking/nonblocking
+ */
+void
+qnoblock(Queue *q, int onoff)
+{
+ q->noblock = onoff;
+}
+
+/*
+ * flush the output queue
+ */
+void
+qflush(Queue *q)
+{
+ Block *bfirst;
+
+ /* mark it */
+ lock(&q->l);
+ bfirst = q->bfirst;
+ q->bfirst = 0;
+ q->len = 0;
+ q->dlen = 0;
+ unlock(&q->l);
+
+ /* free queued blocks */
+ freeblist(bfirst);
+
+ /* wake up readers/writers */
+ Wakeup(&q->wr);
+}
+
+int
+qfull(Queue *q)
+{
+ return q->state & Qflow;
+}
+
+int
+qstate(Queue *q)
+{
+ return q->state;
+}
+
+int
+qclosed(Queue *q)
+{
+ return q->state & Qclosed;
+}
--- /dev/null
+++ b/emu/port/random.c
@@ -1,0 +1,167 @@
+#include "dat.h"
+#include "fns.h"
+
+static struct
+{
+ QLock l;
+ Rendez producer;
+ Rendez consumer;
+ Rendez clock;
+ ulong randomcount;
+ uchar buf[1024];
+ uchar *ep;
+ uchar *rp;
+ uchar *wp;
+ uchar next;
+ uchar bits;
+ uchar wakeme;
+ uchar filled;
+ int kprocstarted;
+ ulong randn;
+ int target;
+} rb;
+
+static int
+rbnotfull(void *v)
+{
+ int i;
+
+ USED(v);
+ i = rb.wp - rb.rp;
+ if(i < 0)
+ i += sizeof(rb.buf);
+ return i < rb.target;
+}
+
+static int
+rbnotempty(void *v)
+{
+ USED(v);
+ return rb.wp != rb.rp;
+}
+
+/*
+ * spin counting up
+ */
+static void
+genrandom(void *v)
+{
+ USED(v);
+ oslopri();
+ for(;;){
+ for(;;)
+ if(++rb.randomcount > 65535)
+ break;
+ if(rb.filled || !rbnotfull(0))
+ Sleep(&rb.producer, rbnotfull, 0);
+ }
+}
+
+/*
+ * produce random bits in a circular buffer
+ */
+static void
+randomclock(void *v)
+{
+ uchar *p;
+
+ USED(v);
+ for(;; osmillisleep(20)){
+ while(!rbnotfull(0)){
+ rb.filled = 1;
+ Sleep(&rb.clock, rbnotfull, 0);
+ }
+ if(rb.randomcount == 0)
+ continue;
+
+ rb.bits = (rb.bits<<2) ^ rb.randomcount;
+ rb.randomcount = 0;
+
+ rb.next++;
+ if(rb.next != 8/2)
+ continue;
+ rb.next = 0;
+
+ p = rb.wp;
+ *p ^= rb.bits;
+ if(++p == rb.ep)
+ p = rb.buf;
+ rb.wp = p;
+
+ if(rb.wakeme)
+ Wakeup(&rb.consumer);
+ }
+}
+
+void
+randominit(void)
+{
+ rb.target = 16;
+ rb.ep = rb.buf + sizeof(rb.buf);
+ rb.rp = rb.wp = rb.buf;
+}
+
+/*
+ * consume random bytes from a circular buffer
+ */
+ulong
+randomread(void *xp, ulong n)
+{
+ uchar *e, *p, *r;
+ ulong x;
+ int i;
+
+ p = xp;
+
+if(0)print("A%ld.%d.%lux|", n, rb.target, getcallerpc(&xp));
+ if(waserror()){
+ qunlock(&rb.l);
+ nexterror();
+ }
+
+ qlock(&rb.l);
+ if(!rb.kprocstarted){
+ rb.kprocstarted = 1;
+ kproc("genrand", genrandom, 0, 0);
+ kproc("randomclock", randomclock, 0, 0);
+ }
+
+ for(e = p + n; p < e; ){
+ r = rb.rp;
+ if(r == rb.wp){
+ rb.wakeme = 1;
+ Wakeup(&rb.clock);
+ Wakeup(&rb.producer);
+ Sleep(&rb.consumer, rbnotempty, 0);
+ rb.wakeme = 0;
+ continue;
+ }
+
+ /*
+ * beating clocks will be predictable if
+ * they are synchronized. Use a cheap pseudo
+ * random number generator to obscure any cycles.
+ */
+ x = rb.randn*1103515245 ^ *r;
+ *p++ = rb.randn = x;
+
+ if(++r == rb.ep)
+ r = rb.buf;
+ rb.rp = r;
+ }
+ if(rb.filled && rb.wp == rb.rp){
+ i = 2*rb.target;
+ if(i > sizeof(rb.buf) - 1)
+ i = sizeof(rb.buf) - 1;
+ rb.target = i;
+ rb.filled = 0;
+ }
+ qunlock(&rb.l);
+ poperror();
+
+ Wakeup(&rb.clock);
+ Wakeup(&rb.producer);
+
+if(0)print("B");
+ return n;
+}
--- /dev/null
+++ b/emu/port/srv.c
@@ -1,0 +1,153 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <interp.h>
+#include <isa.h>
+#include "ip.h"
+#include "srv.h"
+#include "srvm.h"
+
+static QLock dbq;
+
+void
+Srv_init(void *fp)
+{
+ USED(fp);
+}
+
+void
+Srv_iph2a(void *fp)
+{
+ Heap *hpt;
+ String *ss;
+ F_Srv_iph2a *f;
+ int i, n, nhost;
+ List **h, *l, *nl;
+ char *hostv[10];
+ void *r;
+
+ f = fp;
+ r = *f->ret;
+ *f->ret = H;
+ destroy(r);
+ release();
+ qlock(&dbq);
+ if(waserror()){
+ qunlock(&dbq);
+ acquire();
+ return;
+ }
+ nhost = so_gethostbyname(string2c(f->host), hostv, nelem(hostv));
+ poperror();
+ qunlock(&dbq);
+ acquire();
+ if(nhost == 0)
+ return;
+
+ l = (List*)H;
+ h = &l;
+ for(i = 0; i < nhost; i++) {
+ n = strlen(hostv[i]);
+ ss = newstring(n);
+ memmove(ss->Sascii, hostv[i], n);
+ free(hostv[i]);
+
+ hpt = nheap(sizeof(List) + IBY2WD);
+ hpt->t = &Tlist;
+ hpt->t->ref++;
+ nl = H2D(List*, hpt);
+ nl->t = &Tptr;
+ Tptr.ref++;
+ nl->tail = (List*)H;
+ *(String**)nl->data = ss;
+
+ *h = nl;
+ h = &nl->tail;
+ }
+ *f->ret = l;
+}
+
+void
+Srv_ipa2h(void *fp)
+{
+ Heap *hpt;
+ String *ss;
+ F_Srv_ipa2h *f;
+ int i, n, naliases;
+ List **h, *l, *nl;
+ char *hostv[10];
+ void *r;
+
+ f = fp;
+ r = *f->ret;
+ *f->ret = H;
+ destroy(r);
+ release();
+ qlock(&dbq);
+ if(waserror()){
+ qunlock(&dbq);
+ acquire();
+ return;
+ }
+ naliases = so_gethostbyaddr(string2c(f->addr), hostv, nelem(hostv));
+ poperror();
+ qunlock(&dbq);
+ acquire();
+ if(naliases == 0)
+ return;
+
+ l = (List*)H;
+ h = &l;
+ for(i = 0; i < naliases; i++) {
+ n = strlen(hostv[i]);
+ ss = newstring(n);
+ memmove(ss->Sascii, hostv[i], n);
+ free(hostv[i]);
+
+ hpt = nheap(sizeof(List) + IBY2WD);
+ hpt->t = &Tlist;
+ hpt->t->ref++;
+ nl = H2D(List*, hpt);
+ nl->t = &Tptr;
+ Tptr.ref++;
+ nl->tail = (List*)H;
+ *(String**)nl->data = ss;
+
+ *h = nl;
+ h = &nl->tail;
+ }
+ *f->ret = l;
+}
+
+void
+Srv_ipn2p(void *fp)
+{
+ int n;
+ char buf[16];
+ F_Srv_ipn2p *f;
+ void *r;
+
+ f = fp;
+ r = *f->ret;
+ *f->ret = H;
+ destroy(r);
+ release();
+ qlock(&dbq);
+ if(waserror()){
+ qunlock(&dbq);
+ acquire();
+ return;
+ }
+ n = so_getservbyname(string2c(f->service), string2c(f->net), buf);
+ poperror();
+ qunlock(&dbq);
+ acquire();
+ if(n >= 0)
+ retstr(buf, f->ret);
+}
+
+void
+srvmodinit(void)
+{
+ builtinmod("$Srv", Srvmodtab, Srvmodlen);
+}
--- /dev/null
+++ b/emu/port/styx.c
@@ -1,0 +1,701 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+static
+uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{
+ uint n;
+
+ if(p+BIT16SZ > ep)
+ return nil;
+ n = GBIT16(p);
+ p += BIT16SZ - 1;
+ if(p+n+1 > ep)
+ return nil;
+ /* move it down, on top of count, to make room for '\0' */
+ memmove(p, p + 1, n);
+ p[n] = '\0';
+ *s = (char*)p;
+ p += n+1;
+ return p;
+}
+
+static
+uchar*
+gqid(uchar *p, uchar *ep, Qid *q)
+{
+ if(p+QIDSZ > ep)
+ return nil;
+ q->type = GBIT8(p);
+ p += BIT8SZ;
+ q->vers = GBIT32(p);
+ p += BIT32SZ;
+ q->path = GBIT64(p);
+ p += BIT64SZ;
+ return p;
+}
+
+/*
+ * no syntactic checks.
+ * three causes for error:
+ * 1. message size field is incorrect
+ * 2. input buffer too short for its own data (counts too long, etc.)
+ * 3. too many names or qids
+ * gqid() and gstring() return nil if they would reach beyond buffer.
+ * main switch statement checks range and also can fall through
+ * to test at end of routine.
+ */
+uint
+convM2S(uchar *ap, uint nap, Fcall *f)
+{
+ uchar *p, *ep;
+ uint i, size;
+
+ p = ap;
+ ep = p + nap;
+
+ if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
+ return 0;
+ size = GBIT32(p);
+ p += BIT32SZ;
+
+ if(size < BIT32SZ+BIT8SZ+BIT16SZ)
+ return 0;
+
+ f->type = GBIT8(p);
+ p += BIT8SZ;
+ f->tag = GBIT16(p);
+ p += BIT16SZ;
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->msize = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->version);
+ break;
+
+ case Tflush:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->oldtag = GBIT16(p);
+ p += BIT16SZ;
+ break;
+
+ case Tauth:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->afid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ break;
+
+ case Tattach:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->afid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ break;
+
+ case Twalk:
+ if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->newfid = GBIT32(p);
+ p += BIT32SZ;
+ f->nwname = GBIT16(p);
+ p += BIT16SZ;
+ if(f->nwname > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwname; i++){
+ p = gstring(p, ep, &f->wname[i]);
+ if(p == nil)
+ break;
+ }
+ break;
+
+ case Topen:
+ if(p+BIT32SZ+BIT8SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->mode = GBIT8(p);
+ p += BIT8SZ;
+ break;
+
+ case Tcreate:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->name);
+ if(p == nil)
+ break;
+ if(p+BIT32SZ+BIT8SZ > ep)
+ return 0;
+ f->perm = GBIT32(p);
+ p += BIT32SZ;
+ f->mode = GBIT8(p);
+ p += BIT8SZ;
+ break;
+
+ case Tread:
+ if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->offset = GBIT64(p);
+ p += BIT64SZ;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Twrite:
+ if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->offset = GBIT64(p);
+ p += BIT64SZ;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Tstat:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Twstat:
+ if(p+BIT32SZ+BIT16SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->nstat = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nstat > ep)
+ return 0;
+ f->stat = p;
+ p += f->nstat;
+ break;
+
+/*
+ */
+ case Rversion:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->msize = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->version);
+ break;
+
+ case Rerror:
+ p = gstring(p, ep, &f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ p = gqid(p, ep, &f->aqid);
+ if(p == nil)
+ break;
+ break;
+
+ case Rattach:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ break;
+
+ case Rwalk:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nwqid = GBIT16(p);
+ p += BIT16SZ;
+ if(f->nwqid > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwqid; i++){
+ p = gqid(p, ep, &f->wqid[i]);
+ if(p == nil)
+ break;
+ }
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->iounit = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Rread:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case Rwrite:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Rclunk:
+ case Rremove:
+ break;
+
+ case Rstat:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nstat = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nstat > ep)
+ return 0;
+ f->stat = p;
+ p += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+
+ if(p==nil || p>ep)
+ return 0;
+ if(ap+size == p)
+ return size;
+ return 0;
+}
+
+
+
+
+static
+uchar*
+pstring(uchar *p, char *s)
+{
+ uint n;
+
+ if(s == nil){
+ PBIT16(p, 0);
+ p += BIT16SZ;
+ return p;
+ }
+
+ n = strlen(s);
+ PBIT16(p, n);
+ p += BIT16SZ;
+ memmove(p, s, n);
+ p += n;
+ return p;
+}
+
+static
+uchar*
+pqid(uchar *p, Qid *q)
+{
+ PBIT8(p, q->type);
+ p += BIT8SZ;
+ PBIT32(p, q->vers);
+ p += BIT32SZ;
+ PBIT64(p, q->path);
+ p += BIT64SZ;
+ return p;
+}
+
+static
+uint
+stringsz(char *s)
+{
+ if(s == nil)
+ return BIT16SZ;
+
+ return BIT16SZ+strlen(s);
+}
+
+uint
+sizeS2M(Fcall *f)
+{
+ uint n;
+ int i;
+
+ n = 0;
+ n += BIT32SZ; /* size */
+ n += BIT8SZ; /* type */
+ n += BIT16SZ; /* tag */
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ n += BIT32SZ;
+ n += stringsz(f->version);
+ break;
+
+ case Tflush:
+ n += BIT16SZ;
+ break;
+
+ case Tauth:
+ n += BIT32SZ;
+ n += stringsz(f->uname);
+ n += stringsz(f->aname);
+ break;
+
+ case Tattach:
+ n += BIT32SZ;
+ n += BIT32SZ;
+ n += stringsz(f->uname);
+ n += stringsz(f->aname);
+ break;
+
+ case Twalk:
+ n += BIT32SZ;
+ n += BIT32SZ;
+ n += BIT16SZ;
+ for(i=0; i<f->nwname; i++)
+ n += stringsz(f->wname[i]);
+ break;
+
+ case Topen:
+ n += BIT32SZ;
+ n += BIT8SZ;
+ break;
+
+ case Tcreate:
+ n += BIT32SZ;
+ n += stringsz(f->name);
+ n += BIT32SZ;
+ n += BIT8SZ;
+ break;
+
+ case Tread:
+ n += BIT32SZ;
+ n += BIT64SZ;
+ n += BIT32SZ;
+ break;
+
+ case Twrite:
+ n += BIT32SZ;
+ n += BIT64SZ;
+ n += BIT32SZ;
+ n += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ n += BIT32SZ;
+ break;
+
+ case Tstat:
+ n += BIT32SZ;
+ break;
+
+ case Twstat:
+ n += BIT32SZ;
+ n += BIT16SZ;
+ n += f->nstat;
+ break;
+/*
+ */
+
+ case Rversion:
+ n += BIT32SZ;
+ n += stringsz(f->version);
+ break;
+
+ case Rerror:
+ n += stringsz(f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ n += QIDSZ;
+ break;
+
+ case Rattach:
+ n += QIDSZ;
+ break;
+
+ case Rwalk:
+ n += BIT16SZ;
+ n += f->nwqid*QIDSZ;
+ break;
+
+ case Ropen:
+ case Rcreate:
+ n += QIDSZ;
+ n += BIT32SZ;
+ break;
+
+ case Rread:
+ n += BIT32SZ;
+ n += f->count;
+ break;
+
+ case Rwrite:
+ n += BIT32SZ;
+ break;
+
+ case Rclunk:
+ break;
+
+ case Rremove:
+ break;
+
+ case Rstat:
+ n += BIT16SZ;
+ n += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+ return n;
+}
+
+uint
+convS2M(Fcall *f, uchar *ap, uint nap)
+{
+ uchar *p;
+ uint i, size;
+
+ size = sizeS2M(f);
+ if(size == 0)
+ return 0;
+ if(size > nap)
+ return 0;
+
+ p = (uchar*)ap;
+
+ PBIT32(p, size);
+ p += BIT32SZ;
+ PBIT8(p, f->type);
+ p += BIT8SZ;
+ PBIT16(p, f->tag);
+ p += BIT16SZ;
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ PBIT32(p, f->msize);
+ p += BIT32SZ;
+ p = pstring(p, f->version);
+ break;
+
+ case Tflush:
+ PBIT16(p, f->oldtag);
+ p += BIT16SZ;
+ break;
+
+ case Tauth:
+ PBIT32(p, f->afid);
+ p += BIT32SZ;
+ p = pstring(p, f->uname);
+ p = pstring(p, f->aname);
+ break;
+
+ case Tattach:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT32(p, f->afid);
+ p += BIT32SZ;
+ p = pstring(p, f->uname);
+ p = pstring(p, f->aname);
+ break;
+
+ case Twalk:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT32(p, f->newfid);
+ p += BIT32SZ;
+ PBIT16(p, f->nwname);
+ p += BIT16SZ;
+ if(f->nwname > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwname; i++)
+ p = pstring(p, f->wname[i]);
+ break;
+
+ case Topen:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT8(p, f->mode);
+ p += BIT8SZ;
+ break;
+
+ case Tcreate:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ p = pstring(p, f->name);
+ PBIT32(p, f->perm);
+ p += BIT32SZ;
+ PBIT8(p, f->mode);
+ p += BIT8SZ;
+ break;
+
+ case Tread:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT64(p, f->offset);
+ p += BIT64SZ;
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ break;
+
+ case Twrite:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT64(p, f->offset);
+ p += BIT64SZ;
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ break;
+
+ case Tstat:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ break;
+
+ case Twstat:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT16(p, f->nstat);
+ p += BIT16SZ;
+ memmove(p, f->stat, f->nstat);
+ p += f->nstat;
+ break;
+/*
+ */
+
+ case Rversion:
+ PBIT32(p, f->msize);
+ p += BIT32SZ;
+ p = pstring(p, f->version);
+ break;
+
+ case Rerror:
+ p = pstring(p, f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ p = pqid(p, &f->aqid);
+ break;
+
+ case Rattach:
+ p = pqid(p, &f->qid);
+ break;
+
+ case Rwalk:
+ PBIT16(p, f->nwqid);
+ p += BIT16SZ;
+ if(f->nwqid > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwqid; i++)
+ p = pqid(p, &f->wqid[i]);
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = pqid(p, &f->qid);
+ PBIT32(p, f->iounit);
+ p += BIT32SZ;
+ break;
+
+ case Rread:
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Rwrite:
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ break;
+
+ case Rclunk:
+ break;
+
+ case Rremove:
+ break;
+
+ case Rstat:
+ PBIT16(p, f->nstat);
+ p += BIT16SZ;
+ memmove(p, f->stat, f->nstat);
+ p += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+ if(size != p-ap)
+ return 0;
+ return size;
+}
--- /dev/null
+++ b/emu/port/sysfile.c
@@ -1,0 +1,1089 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "kernel.h"
+
+static int
+growfd(Fgrp *f, int fd)
+{
+ int n;
+ Chan **nfd, **ofd;
+
+ if(fd < f->nfd)
+ return 0;
+ n = f->nfd+DELTAFD;
+ if(n > MAXNFD)
+ n = MAXNFD;
+ if(fd >= n)
+ return -1;
+ nfd = malloc(n*sizeof(Chan*));
+ if(nfd == nil)
+ return -1;
+ ofd = f->fd;
+ memmove(nfd, ofd, f->nfd*sizeof(Chan *));
+ f->fd = nfd;
+ f->nfd = n;
+ free(ofd);
+ return 0;
+}
+
+static int
+newfd(Chan *c)
+{
+ int i;
+ Fgrp *f = up->env->fgrp;
+
+ lock(&f->l);
+ for(i=f->minfd; i<f->nfd; i++)
+ if(f->fd[i] == 0)
+ break;
+ if(i >= f->nfd && growfd(f, i) < 0){
+ unlock(&f->l);
+ exhausted("file descriptors");
+ return -1;
+ }
+ f->minfd = i + 1;
+ if(i > f->maxfd)
+ f->maxfd = i;
+ f->fd[i] = c;
+ unlock(&f->l);
+ return i;
+}
+
+Chan*
+fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref)
+{
+ Chan *c;
+
+ c = 0;
+ lock(&f->l);
+ if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) {
+ unlock(&f->l);
+ error(Ebadfd);
+ }
+ if(iref)
+ incref(&c->r);
+ unlock(&f->l);
+
+ if(chkmnt && (c->flag&CMSG))
+ goto bad;
+ if(mode<0 || c->mode==ORDWR)
+ return c;
+ if((mode&OTRUNC) && c->mode==OREAD)
+ goto bad;
+ if((mode&~OTRUNC) != c->mode)
+ goto bad;
+ return c;
+bad:
+ if(iref)
+ cclose(c);
+ error(Ebadusefd);
+ return nil;
+}
+
+long
+kchanio(void *vc, void *buf, int n, int mode)
+{
+ int r;
+ Chan *c;
+
+ c = vc;
+ if(waserror())
+ return -1;
+
+ if(mode == OREAD)
+ r = devtab[c->type]->read(c, buf, n, c->offset);
+ else
+ r = devtab[c->type]->write(c, buf, n, c->offset);
+
+ lock(&c->l);
+ c->offset += r;
+ unlock(&c->l);
+ poperror();
+ return r;
+}
+
+int
+openmode(ulong o)
+{
+ if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
+ error(Ebadarg);
+ o &= ~(OTRUNC|OCEXEC|ORCLOSE);
+ if(o > OEXEC)
+ error(Ebadarg);
+ if(o == OEXEC)
+ return OREAD;
+ return o;
+}
+
+static void
+fdclose(Fgrp *f, int fd)
+{
+ int i;
+ Chan *c;
+
+ lock(&f->l);
+ c = f->fd[fd];
+ if(c == 0) {
+ /* can happen for users with shared fd tables */
+ unlock(&f->l);
+ return;
+ }
+ f->fd[fd] = 0;
+ if(fd == f->maxfd)
+ for(i=fd; --i>=0 && f->fd[i]==0; )
+ f->maxfd = i;
+ if(fd < f->minfd)
+ f->minfd = fd;
+ unlock(&f->l);
+ cclose(c);
+}
+
+int
+kchdir(char *path)
+{
+ Chan *c;
+ Pgrp *pg;
+
+ if(waserror())
+ return -1;
+
+ c = namec(path, Atodir, 0, 0);
+ pg = up->env->pgrp;
+ cclose(pg->dot);
+ pg->dot = c;
+ poperror();
+ return 0;
+}
+
+int
+kfgrpclose(Fgrp *f, int fd)
+{
+ if(waserror())
+ return -1;
+
+ /*
+ * Take no reference on the chan because we don't really need the
+ * data structure, and are calling fdtochan only for error checks.
+ * fdclose takes care of processes racing through here.
+ */
+ fdtochan(f, fd, -1, 0, 0);
+ fdclose(f, fd);
+ poperror();
+ return 0;
+}
+
+int
+kclose(int fd)
+{
+ return kfgrpclose(up->env->fgrp, fd);
+}
+
+int
+kcreate(char *path, int mode, ulong perm)
+{
+ int fd;
+ volatile struct { Chan *c; } c;
+
+ c.c = nil;
+ if(waserror()) {
+ cclose(c.c);
+ return -1;
+ }
+
+ openmode(mode&~OEXCL); /* error check only; OEXCL okay here */
+ c.c = namec(path, Acreate, mode, perm);
+ fd = newfd(c.c);
+ if(fd < 0)
+ error(Enofd);
+ poperror();
+ return fd;
+}
+
+int
+kdup(int old, int new)
+{
+ int fd;
+ Chan *oc;
+ Fgrp *f = up->env->fgrp;
+ volatile struct { Chan *c; } c;
+
+ if(waserror())
+ return -1;
+
+ c.c = fdtochan(up->env->fgrp, old, -1, 0, 1);
+ if(c.c->qid.type & QTAUTH)
+ error(Eperm);
+ fd = new;
+ if(fd != -1) {
+ lock(&f->l);
+ if(fd < 0 || growfd(f, fd) < 0) {
+ unlock(&f->l);
+ cclose(c.c);
+ error(Ebadfd);
+ }
+ if(fd > f->maxfd)
+ f->maxfd = fd;
+ oc = f->fd[fd];
+ f->fd[fd] = c.c;
+ unlock(&f->l);
+ if(oc != 0)
+ cclose(oc);
+ }
+ else {
+ if(waserror()) {
+ cclose(c.c);
+ nexterror();
+ }
+ fd = newfd(c.c);
+ if(fd < 0)
+ error(Enofd);
+ poperror();
+ }
+ poperror();
+ return fd;
+}
+
+int
+kfstat(int fd, uchar *buf, int n)
+{
+ volatile struct { Chan *c; } c;
+
+ c.c = nil;
+ if(waserror()) {
+ cclose(c.c);
+ return -1;
+ }
+ c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+ devtab[c.c->type]->stat(c.c, buf, n);
+ poperror();
+ cclose(c.c);
+ return n;
+}
+
+char*
+kfd2path(int fd)
+{
+ Chan *c;
+ char *s;
+
+ if(waserror())
+ return nil;
+ c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+ s = nil;
+ if(c->name != nil){
+ s = malloc(c->name->len+1);
+ if(s == nil){
+ cclose(c);
+ error(Enomem);
+ }
+ memmove(s, c->name->s, c->name->len+1);
+ cclose(c);
+ }
+ poperror();
+ return s;
+}
+
+int
+kfauth(int fd, char *aname)
+{
+ Chan *c, *ac;
+
+ if(waserror())
+ return -1;
+
+ validname(aname, 1);
+ c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+ if(waserror()){
+ cclose(c);
+ nexterror();
+ }
+
+ ac = mntauth(c, aname);
+
+ /* at this point ac is responsible for keeping c alive */
+ poperror(); /* c */
+ cclose(c);
+
+ if(waserror()){
+ cclose(ac);
+ nexterror();
+ }
+
+ fd = newfd(ac);
+ if(fd < 0)
+ error(Enofd);
+ poperror(); /* ac */
+
+ poperror();
+
+ return fd;
+}
+
+int
+kfversion(int fd, uint msize, char *vers, uint arglen)
+{
+ int m;
+ Chan *c;
+
+ if(waserror())
+ return -1;
+
+ /* check there's a NUL in the version string */
+ if(arglen==0 || memchr(vers, 0, arglen)==0)
+ error(Ebadarg);
+
+ c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+ if(waserror()){
+ cclose(c);
+ nexterror();
+ }
+
+ m = mntversion(c, vers, msize, arglen);
+
+ poperror();
+ cclose(c);
+
+ poperror();
+ return m;
+}
+
+int
+kpipe(int fd[2])
+{
+ Dev *d;
+ Fgrp *f;
+ Chan *c[2];
+ static char *names[] = {"data", "data1"};
+
+ f = up->env->fgrp;
+
+ d = devtab[devno('|', 0)];
+ c[0] = namec("#|", Atodir, 0, 0);
+ c[1] = 0;
+ fd[0] = -1;
+ fd[1] = -1;
+ if(waserror()) {
+ if(c[0] != 0)
+ cclose(c[0]);
+ if(c[1] != 0)
+ cclose(c[1]);
+ if(fd[0] >= 0)
+ f->fd[fd[0]]=0;
+ if(fd[1] >= 0)
+ f->fd[fd[1]]=0;
+ return -1;
+ }
+ c[1] = cclone(c[0]);
+ if(walk(&c[0], &names[0], 1, 1, nil) < 0)
+ error(Egreg);
+ if(walk(&c[1], &names[1], 1, 1, nil) < 0)
+ error(Egreg);
+ c[0] = d->open(c[0], ORDWR);
+ c[1] = d->open(c[1], ORDWR);
+ fd[0] = newfd(c[0]);
+ if(fd[0] < 0)
+ error(Enofd);
+ fd[1] = newfd(c[1]);
+ if(fd[1] < 0)
+ error(Enofd);
+ poperror();
+ return 0;
+}
+
+int
+kfwstat(int fd, uchar *buf, int n)
+{
+ volatile struct { Chan *c; } c;
+
+ c.c = nil;
+ if(waserror()) {
+ cclose(c.c);
+ return -1;
+ }
+ validstat(buf, n);
+ c.c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
+ n = devtab[c.c->type]->wstat(c.c, buf, n);
+ poperror();
+ cclose(c.c);
+ return n;
+}
+
+long
+bindmount(Chan *c, char *old, int flag, char *spec)
+{
+ int ret;
+ volatile struct { Chan *c; } c1;
+
+ if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER))
+ error(Ebadarg);
+
+ c1.c = namec(old, Amount, 0, 0);
+ if(waserror()){
+ cclose(c1.c);
+ nexterror();
+ }
+ ret = cmount(c, c1.c, flag, spec);
+
+ poperror();
+ cclose(c1.c);
+ return ret;
+}
+
+int
+kbind(char *new, char *old, int flags)
+{
+ long r;
+ volatile struct { Chan *c; } c0;
+
+ c0.c = nil;
+ if(waserror()) {
+ cclose(c0.c);
+ return -1;
+ }
+ c0.c = namec(new, Abind, 0, 0);
+ r = bindmount(c0.c, old, flags, "");
+ poperror();
+ cclose(c0.c);
+ return r;
+}
+
+int
+kmount(int fd, int afd, char *old, int flags, char *spec)
+{
+ long r;
+ volatile struct { Chan *c; } c0;
+ volatile struct { Chan *c; } bc;
+ volatile struct { Chan *c; } ac;
+ Mntparam mntparam;
+
+ ac.c = nil;
+ bc.c = nil;
+ c0.c = nil;
+ if(waserror()) {
+ cclose(ac.c);
+ cclose(bc.c);
+ cclose(c0.c);
+ return -1;
+ }
+ bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+ if(afd >= 0)
+ ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1);
+ mntparam.chan = bc.c;
+ mntparam.authchan = ac.c;
+ mntparam.spec = spec;
+ mntparam.flags = flags;
+ c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam);
+
+ r = bindmount(c0.c, old, flags, spec);
+ poperror();
+ cclose(ac.c);
+ cclose(bc.c);
+ cclose(c0.c);
+
+ return r;
+}
+
+int
+kunmount(char *old, char *new)
+{
+ volatile struct { Chan *c; } cmount;
+ volatile struct { Chan *c; } cmounted;
+
+ cmount.c = nil;
+ cmounted.c = nil;
+ if(waserror()) {
+ cclose(cmount.c);
+ cclose(cmounted.c);
+ return -1;
+ }
+
+ cmount.c = namec(new, Amount, 0, 0);
+ if(old != nil && old[0] != '\0') {
+ /*
+ * This has to be namec(..., Aopen, ...) because
+ * if arg[0] is something like /srv/cs or /fd/0,
+ * opening it is the only way to get at the real
+ * Chan underneath.
+ */
+ cmounted.c = namec(old, Aopen, OREAD, 0);
+ }
+
+ cunmount(cmount.c, cmounted.c);
+ poperror();
+ cclose(cmount.c);
+ cclose(cmounted.c);
+ return 0;
+}
+
+int
+kopen(char *path, int mode)
+{
+ int fd;
+ volatile struct { Chan *c; } c;
+
+ if(waserror())
+ return -1;
+
+ openmode(mode); /* error check only */
+ c.c = namec(path, Aopen, mode, 0);
+ if(waserror()){
+ cclose(c.c);
+ nexterror();
+ }
+ fd = newfd(c.c);
+ if(fd < 0)
+ error(Enofd);
+ poperror();
+
+ poperror();
+ return fd;
+}
+
+long
+unionread(Chan *c, void *va, long n)
+{
+ int i;
+ long nr;
+ Mhead *m;
+ Mount *mount;
+
+ qlock(&c->umqlock);
+ m = c->umh;
+ rlock(&m->lock);
+ mount = m->mount;
+ /* bring mount in sync with c->uri and c->umc */
+ for(i = 0; mount != nil && i < c->uri; i++)
+ mount = mount->next;
+
+ nr = 0;
+ while(mount != nil) {
+ /* Error causes component of union to be skipped */
+ if(mount->to && !waserror()) {
+ if(c->umc == nil){
+ c->umc = cclone(mount->to);
+ c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
+ }
+
+ nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
+ if(nr < 0)
+ nr = 0; /* dev.c can return -1 */
+ c->umc->offset += nr;
+ poperror();
+ }
+ if(nr > 0)
+ break;
+
+ /* Advance to next element */
+ c->uri++;
+ if(c->umc) {
+ cclose(c->umc);
+ c->umc = nil;
+ }
+ mount = mount->next;
+ }
+ runlock(&m->lock);
+ qunlock(&c->umqlock);
+ return nr;
+}
+
+static void
+unionrewind(Chan *c)
+{
+ qlock(&c->umqlock);
+ c->uri = 0;
+ if(c->umc){
+ cclose(c->umc);
+ c->umc = nil;
+ }
+ qunlock(&c->umqlock);
+}
+
+static long
+rread(int fd, void *va, long n, vlong *offp)
+{
+ int dir;
+ Lock *cl;
+ volatile struct { Chan *c; } c;
+ vlong off;
+
+ if(waserror())
+ return -1;
+
+ c.c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1);
+ if(waserror()){
+ cclose(c.c);
+ nexterror();
+ }
+
+ if(n < 0)
+ error(Etoosmall);
+
+ dir = c.c->qid.type & QTDIR;
+ if(dir && c.c->umh)
+ n = unionread(c.c, va, n);
+ else{
+ cl = &c.c->l;
+ if(offp == nil){
+ lock(cl); /* lock for vlong assignment */
+ off = c.c->offset;
+ unlock(cl);
+ }else
+ off = *offp;
+ if(off < 0)
+ error(Enegoff);
+ if(off == 0){
+ if(offp == nil){
+ lock(cl);
+ c.c->offset = 0;
+ c.c->dri = 0;
+ unlock(cl);
+ }
+ unionrewind(c.c);
+ }
+ n = devtab[c.c->type]->read(c.c, va, n, off);
+ lock(cl);
+ c.c->offset += n;
+ unlock(cl);
+ }
+
+ poperror();
+ cclose(c.c);
+
+ poperror();
+ return n;
+}
+
+long
+kread(int fd, void *va, long n)
+{
+ return rread(fd, va, n, nil);
+}
+
+long
+kpread(int fd, void *va, long n, vlong off)
+{
+ return rread(fd, va, n, &off);
+}
+
+int
+kremove(char *path)
+{
+ volatile struct { Chan *c; } c;
+
+ if(waserror())
+ return -1;
+
+ c.c = namec(path, Aremove, 0, 0);
+ if(waserror()){
+ c.c->type = 0; /* see below */
+ cclose(c.c);
+ nexterror();
+ }
+ devtab[c.c->type]->remove(c.c);
+ /*
+ * Remove clunks the fid, but we need to recover the Chan
+ * so fake it up. rootclose() is known to be a nop.
+ */
+ c.c->type = 0;
+ poperror();
+ cclose(c.c);
+
+ poperror();
+ return 0;
+}
+
+vlong
+kseek(int fd, vlong off, int whence)
+{
+ Dir *dir;
+ Chan *c;
+
+ if(waserror())
+ return -1;
+
+ c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
+ if(waserror()) {
+ cclose(c);
+ nexterror();
+ }
+
+ if(devtab[c->type]->dc == '|')
+ error(Eisstream);
+
+ switch(whence) {
+ case 0:
+ if(c->qid.type & QTDIR){
+ if(off != 0)
+ error(Eisdir);
+ unionrewind(c);
+ }else if(off < 0)
+ error(Enegoff);
+ lock(&c->l); /* lock for vlong assignment */
+ c->offset = off;
+ unlock(&c->l);
+ break;
+
+ case 1:
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+ lock(&c->l); /* lock for read/write update */
+ off += c->offset;
+ if(off < 0){
+ unlock(&c->l);
+ error(Enegoff);
+ }
+ c->offset = off;
+ unlock(&c->l);
+ break;
+
+ case 2:
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+ dir = chandirstat(c);
+ if(dir == nil)
+ error("internal error: stat error in seek");
+ off += dir->length;
+ free(dir);
+ if(off < 0)
+ error(Enegoff);
+ lock(&c->l); /* lock for read/write update */
+ c->offset = off;
+ unlock(&c->l);
+ break;
+
+ default:
+ error(Ebadarg);
+ break;
+ }
+ poperror();
+ c->dri = 0;
+ cclose(c);
+ poperror();
+ return off;
+}
+
+void
+validstat(uchar *s, int n)
+{
+ int m;
+ char buf[64];
+
+ if(statcheck(s, n) < 0)
+ error(Ebadstat);
+ /* verify that name entry is acceptable */
+ s += STATFIXLEN - 4*BIT16SZ; /* location of first string */
+ /*
+ * s now points at count for first string.
+ * if it's too long, let the server decide; this is
+ * only for his protection anyway. otherwise
+ * we'd have to allocate and waserror.
+ */
+ m = GBIT16(s);
+ s += BIT16SZ;
+ if(m+1 > sizeof buf)
+ return;
+ memmove(buf, s, m);
+ buf[m] = '\0';
+ /* name could be '/' */
+ if(strcmp(buf, "/") != 0)
+ validname(buf, 0);
+}
+
+int
+kstat(char *path, uchar *buf, int n)
+{
+ volatile struct { Chan *c; } c;
+
+ c.c = nil;
+ if(waserror()){
+ cclose(c.c);
+ return -1;
+ }
+ c.c = namec(path, Aaccess, 0, 0);
+ devtab[c.c->type]->stat(c.c, buf, n);
+ poperror();
+ cclose(c.c);
+ return 0;
+}
+
+static long
+rwrite(int fd, void *va, long n, vlong *offp)
+{
+ Lock *cl;
+ volatile struct { Chan *c; } c;
+ vlong off;
+ long m;
+
+ if(waserror())
+ return -1;
+ c.c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1);
+ if(waserror()){
+ cclose(c.c);
+ nexterror();
+ }
+ if(c.c->qid.type & QTDIR)
+ error(Eisdir);
+
+ if(n < 0)
+ error(Etoosmall);
+
+ cl = &c.c->l;
+ if(offp == nil){
+ lock(cl);
+ off = c.c->offset;
+ c.c->offset += n;
+ unlock(cl);
+ }else
+ off = *offp;
+
+ if(waserror()){
+ if(offp == nil){
+ lock(cl);
+ c.c->offset -= n;
+ unlock(cl);
+ }
+ nexterror();
+ }
+ if(off < 0)
+ error(Enegoff);
+ m = devtab[c.c->type]->write(c.c, va, n, off);
+ poperror();
+
+ if(offp == nil && m < n){
+ lock(cl);
+ c.c->offset -= n - m;
+ unlock(cl);
+ }
+
+ poperror();
+ cclose(c.c);
+
+ poperror();
+ return m;
+}
+
+long
+kwrite(int fd, void *va, long n)
+{
+ return rwrite(fd, va, n, nil);
+}
+
+long
+kpwrite(int fd, void *va, long n, vlong off)
+{
+ return rwrite(fd, va, n, &off);
+}
+
+int
+kwstat(char *path, uchar *buf, int n)
+{
+ volatile struct { Chan *c; } c;
+
+ c.c = nil;
+ if(waserror()){
+ cclose(c.c);
+ return -1;
+ }
+ validstat(buf, n);
+ c.c = namec(path, Aaccess, 0, 0);
+ n = devtab[c.c->type]->wstat(c.c, buf, n);
+ poperror();
+ cclose(c.c);
+ return n;
+}
+
+enum
+{
+ DIRSIZE = STATFIXLEN + 32 * 4,
+ DIRREADLIM = 2048, /* should handle the largest reasonable directory entry */
+};
+
+Dir*
+chandirstat(Chan *c)
+{
+ Dir *d;
+ uchar *buf;
+ int n, nd, i;
+
+ nd = DIRSIZE;
+ for(i=0; i<2; i++){ /* should work by the second try */
+ d = smalloc(sizeof(Dir) + nd);
+ buf = (uchar*)&d[1];
+ if(waserror()){
+ free(d);
+ return nil;
+ }
+ n = devtab[c->type]->stat(c, buf, nd);
+ poperror();
+ if(n < BIT16SZ){
+ free(d);
+ return nil;
+ }
+ nd = GBIT16((uchar*)buf) + BIT16SZ; /* size needed to store whole stat buffer including count */
+ if(nd <= n){
+ convM2D(buf, n, d, (char*)&d[1]);
+ return d;
+ }
+ /* else sizeof(Dir)+nd is plenty */
+ free(d);
+ }
+ return nil;
+
+}
+
+Dir*
+kdirstat(char *name)
+{
+ volatile struct {Chan *c;} c;
+ Dir *d;
+
+ c.c = nil;
+ if(waserror()){
+ cclose(c.c);
+ return nil;
+ }
+ c.c = namec(name, Aaccess, 0, 0);
+ d = chandirstat(c.c);
+ poperror();
+ cclose(c.c);
+ return d;
+}
+
+Dir*
+kdirfstat(int fd)
+{
+ volatile struct { Chan *c; } c;
+ Dir *d;
+
+ c.c = nil;
+ if(waserror()) {
+ cclose(c.c);
+ return nil;
+ }
+ c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+ d = chandirstat(c.c);
+ poperror();
+ cclose(c.c);
+ return d;
+}
+
+int
+kdirwstat(char *name, Dir *dir)
+{
+ uchar *buf;
+ int r;
+
+ r = sizeD2M(dir);
+ buf = smalloc(r);
+ convD2M(dir, buf, r);
+ r = kwstat(name, buf, r);
+ free(buf);
+ return r < 0? r: 0;
+}
+
+int
+kdirfwstat(int fd, Dir *dir)
+{
+ uchar *buf;
+ int r;
+
+ r = sizeD2M(dir);
+ buf = smalloc(r);
+ convD2M(dir, buf, r);
+ r = kfwstat(fd, buf, r);
+ free(buf);
+ return r < 0? r: 0;
+}
+
+static long
+dirpackage(uchar *buf, long ts, Dir **d)
+{
+ char *s;
+ long ss, i, n, nn, m;
+
+ *d = nil;
+ if(ts <= 0)
+ return ts;
+
+ /*
+ * first find number of all stats, check they look like stats, & size all associated strings
+ */
+ ss = 0;
+ n = 0;
+ for(i = 0; i < ts; i += m){
+ m = BIT16SZ + GBIT16(&buf[i]);
+ if(statcheck(&buf[i], m) < 0)
+ break;
+ ss += m;
+ n++;
+ }
+
+ if(i != ts)
+ error("bad directory format");
+
+ *d = malloc(n * sizeof(Dir) + ss);
+ if(*d == nil)
+ error(Enomem);
+
+ /*
+ * then convert all buffers
+ */
+ s = (char*)*d + n * sizeof(Dir);
+ nn = 0;
+ for(i = 0; i < ts; i += m){
+ m = BIT16SZ + GBIT16((uchar*)&buf[i]);
+ if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
+ free(*d);
+ *d = nil;
+ error("bad directory entry");
+ }
+ nn++;
+ s += m;
+ }
+
+ return nn;
+}
+
+long
+kdirread(int fd, Dir **d)
+{
+ uchar *buf;
+ long ts;
+
+ *d = nil;
+ if(waserror())
+ return -1;
+ buf = malloc(DIRREADLIM);
+ if(buf == nil)
+ error(Enomem);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+ ts = kread(fd, buf, DIRREADLIM);
+ if(ts > 0)
+ ts = dirpackage(buf, ts, d);
+
+ poperror();
+ free(buf);
+ poperror();
+ return ts;
+}
+
+int
+kiounit(int fd)
+{
+ Chan *c;
+ int n;
+
+ c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+ if(waserror()){
+ cclose(c);
+ return 0; /* n.b. */
+ }
+ n = c->iounit;
+ poperror();
+ cclose(c);
+ return n;
+}
--- /dev/null
+++ b/emu/port/uqid.c
@@ -1,0 +1,114 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+/*
+ * unique Qid path generation,
+ * for exportfs.c and various devfs-*
+ */
+
+#define QIDMASK (((vlong)1<<48)-1)
+
+void
+uqidinit(Uqidtab *tab)
+{
+ memset(tab, 0, sizeof(*tab));
+}
+
+static int
+uqidhash(vlong path)
+{
+ ulong p;
+ p = (ulong)path;
+ return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1);
+}
+
+static Uqid **
+uqidlook(Uqid **tab, Chan *c, vlong path)
+{
+ Uqid **hp, *q;
+
+ for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next)
+ if(q->type == c->type && q->dev == c->dev && q->oldpath == path)
+ break;
+ return hp;
+}
+
+static int
+uqidexists(Uqid **tab, vlong path)
+{
+ int i;
+ Uqid *q;
+
+ for(i=0; i<Nqidhash; i++)
+ for(q = tab[i]; q != nil; q = q->next)
+ if(q->newpath == path)
+ return 1;
+ return 0;
+}
+
+Uqid *
+uqidalloc(Uqidtab *tab, Chan *c)
+{
+ Uqid **hp, *q;
+
+ qlock(&tab->l);
+ hp = uqidlook(tab->qids, c, c->qid.path);
+ if((q = *hp) != nil){
+ incref(&q->r);
+ qunlock(&tab->l);
+ return q;
+ }
+ q = mallocz(sizeof(*q), 1);
+ if(q == nil){
+ qunlock(&tab->l);
+ error(Enomem);
+ }
+ q->r.ref = 1;
+ q->type = c->type;
+ q->dev = c->dev;
+ q->oldpath = c->qid.path;
+ q->newpath = c->qid.path;
+ while(uqidexists(tab->qids, q->newpath)){
+ if(++tab->pathgen >= (1<<16))
+ tab->pathgen = 1;
+ q->newpath = ((vlong)tab->pathgen<<48) | (q->newpath & QIDMASK);
+ }
+ q->next = nil;
+ *hp = q;
+ qunlock(&tab->l);
+ return q;
+}
+
+void
+freeuqid(Uqidtab *tab, Uqid *q)
+{
+ Uqid **hp;
+
+ if(q == nil)
+ return;
+ qlock(&tab->l);
+ if(decref(&q->r) == 0){
+ hp = &tab->qids[uqidhash(q->oldpath)];
+ for(; *hp != nil; hp = &(*hp)->next)
+ if(*hp == q){
+ *hp = q->next;
+ free(q);
+ break;
+ }
+ }
+ qunlock(&tab->l);
+}
+
+Qid
+mkuqid(Chan *c, Uqid *qid)
+{
+ Qid q;
+
+ if(qid == nil)
+ return c->qid;
+ q.path = qid->newpath;
+ q.vers = c->qid.vers;
+ q.type = c->qid.type;
+ return q;
+}
--- /dev/null
+++ b/emu/port/win-x11-old.c
@@ -1,0 +1,847 @@
+#include "dat.h"
+#include "fns.h"
+#include "cursor.h"
+#include "keyboard.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+
+/*
+ * alias defs for image types to overcome name conflicts
+ */
+typedef struct ICursor ICursor;
+typedef struct IPoint IPoint;
+typedef struct IRectangle IRectangle;
+typedef struct CRemapTbl CRemapTbl;
+struct ICursor
+{
+ int w;
+ int h;
+ int hotx;
+ int hoty;
+ char *src;
+ char *mask;
+};
+
+struct IPoint
+{
+ int x;
+ int y;
+};
+
+struct IRectangle
+{
+ IPoint min;
+ IPoint max;
+};
+
+struct CRemapTbl
+{
+ ulong inferno[256]; /* The corresponding inferno colormap vals */
+ ulong openslot[256];
+ Bool cellused[256];
+ int cnt;
+ int opencnt;
+};
+
+enum
+{
+ DblTime = 300 /* double click time in msec */
+};
+
+XColor map[256]; /* Inferno colormap array */
+XColor map7[128]; /* Inferno colormap array */
+uchar map7to8[128][2];
+Colormap xcmap; /* Default shared colormap */
+int infernotox11[256]; /* Values for mapping between */
+int x11toinferno[256]; /* X11 and inferno */
+int x24bitswap = 0; /* swap endian for 24bit RGB */
+
+static int triedscreen;
+static XModifierKeymap *modmap;
+static int keypermod;
+static Drawable xdrawable;
+static Atom wm_take_focus;
+static void xexpose(XEvent*);
+static void xmouse(XEvent*);
+static void xkeyboard(XEvent*);
+static void xmapping(XEvent*);
+static void xproc(void*);
+static void xinitscreen(int, int);
+static void initmap(Window);
+static GC creategc(Drawable);
+static CRemapTbl crtbl;
+static void graphicscmap(XColor*);
+ int xscreendepth;
+ Drawable xscreenid;
+ Display* xdisplay;
+ Display* xkmcon;
+ Visual *xvis;
+ GC xgcfill, xgccopy, xgcsimplesrc, xgczero, xgcreplsrc;
+ GC xgcfill0, xgccopy0, xgcsimplesrc0, xgczero0, xgcreplsrc0;
+
+char *gkscanid = "emu_x11";
+
+
+ulong*
+attachscreen(IRectangle *r, int *ld, int *width, int *softscreen)
+{
+ extern ulong* makememones();
+
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+ *ld = 3;
+ *width = Xsize/4;
+ *softscreen = 1;
+ if(!triedscreen){
+ triedscreen = 1;
+ xinitscreen(Xsize, Ysize);
+ if(kproc("xproc", xproc, nil, 0) < 0) {
+ fprint(2, "emu: win-x11 can't make X proc\n");
+ return 0;
+ }
+ }
+ return makememones();
+}
+
+void
+flushmemscreen(IRectangle r)
+{
+ if(r.min.x > r.max.x)
+ return;
+ XCopyArea(xdisplay, xscreenid, xdrawable, xgccopy,
+ r.min.x, r.min.y,
+ r.max.x-r.min.x, r.max.y-r.min.y, r.min.x, r.min.y);
+ XFlush(xdisplay);
+}
+
+static int
+revbyte(int b)
+{
+ int r;
+
+ r = 0;
+ r |= (b&0x01) << 7;
+ r |= (b&0x02) << 5;
+ r |= (b&0x04) << 3;
+ r |= (b&0x08) << 1;
+ r |= (b&0x10) >> 1;
+ r |= (b&0x20) >> 3;
+ r |= (b&0x40) >> 5;
+ r |= (b&0x80) >> 7;
+ return r;
+}
+
+static void
+gotcursor(ICursor c)
+{
+ Cursor xc;
+ XColor fg, bg;
+ Pixmap xsrc, xmask;
+ static Cursor xcursor;
+
+ if(c.src == nil){
+ if(xcursor != 0) {
+ XFreeCursor(xdisplay, xcursor);
+ xcursor = 0;
+ }
+ XUndefineCursor(xdisplay, xdrawable);
+ XFlush(xdisplay);
+ return;
+ }
+ xsrc = XCreateBitmapFromData(xdisplay, xdrawable, c.src, c.w, c.h);
+ xmask = XCreateBitmapFromData(xdisplay, xdrawable, c.mask, c.w, c.h);
+
+ fg = map[255];
+ bg = map[0];
+ fg.pixel = infernotox11[255];
+ bg.pixel = infernotox11[0];
+ xc = XCreatePixmapCursor(xdisplay, xsrc, xmask, &fg, &bg, -c.hotx, -c.hoty);
+ if(xc != 0) {
+ XDefineCursor(xdisplay, xdrawable, xc);
+ if(xcursor != 0)
+ XFreeCursor(xdisplay, xcursor);
+ xcursor = xc;
+ }
+ XFreePixmap(xdisplay, xsrc);
+ XFreePixmap(xdisplay, xmask);
+ XFlush(xdisplay);
+ free(c.src);
+}
+
+void
+setcursor(IPoint p)
+{
+ XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, p.x, p.y);
+ XFlush(xdisplay);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ ICursor ic;
+ IRectangle ir;
+ uchar *bs, *bc;
+ int i, h, j, bpl;
+ char *src, *mask, *csrc, *cmask;
+
+ /* Set the default system cursor */
+ src = nil;
+ mask = nil;
+ if(c->data != nil){
+ h = (c->maxy-c->miny)/2;
+ ir.min.x = c->minx;
+ ir.min.y = c->miny;
+ ir.max.x = c->maxx;
+ ir.max.y = c->maxy;
+ /* passing IRectangle to Rectangle is safe */
+ bpl = bytesperline(ir, 1);
+
+ i = h*bpl;
+ src = malloc(2*i);
+ if(src == nil)
+ return;
+ mask = src + i;
+
+ csrc = src;
+ cmask = mask;
+ bc = c->data;
+ bs = c->data + h*bpl;
+ for(i = 0; i < h; i++){
+ for(j = 0; j < bpl; j++) {
+ *csrc++ = revbyte(bs[j]);
+ *cmask++ = revbyte(bs[j] | bc[j]);
+ }
+ bs += bpl;
+ bc += bpl;
+ }
+ }
+ ic.w = 8*bpl;
+ ic.h = h;
+ ic.hotx = c->hotx;
+ ic.hoty = c->hoty;
+ ic.src = src;
+ ic.mask = mask;
+
+ gotcursor(ic);
+}
+
+static void
+xproc(void *arg)
+{
+ ulong mask;
+ XEvent event;
+
+ closepgrp(up->env->pgrp);
+ closefgrp(up->env->fgrp);
+ closeegrp(up->env->egrp);
+ closesigs(up->env->sigs);
+
+ mask = KeyPressMask|
+ KeyReleaseMask|
+ ButtonPressMask|
+ ButtonReleaseMask|
+ PointerMotionMask|
+ Button1MotionMask|
+ Button2MotionMask|
+ Button3MotionMask|
+ ExposureMask;
+
+ XSelectInput(xkmcon, xdrawable, mask);
+ for(;;) {
+ XWindowEvent(xkmcon, xdrawable, mask, &event);
+ switch(event.type) {
+ case KeyPress:
+ case KeyRelease:
+ xkeyboard(&event);
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ xmouse(&event);
+ break;
+ case Expose:
+ xexpose(&event);
+ break;
+ case MappingNotify:
+ xmapping(&event);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int
+shutup(Display *d, XErrorEvent *e)
+{
+ char buf[200];
+ print("X error: error code=%d, request_code=%d, minor=%d\n", e->error_code, e->request_code, e->minor_code);
+ XGetErrorText(d, e->error_code, buf, sizeof(buf));
+ print("%s\n", buf);
+ USED(d);
+ USED(e);
+ return 0;
+}
+
+static void
+xinitscreen(int xsize, int ysize)
+{
+ int i, pmid;
+ char *argv[2];
+ char *disp_val;
+ Window rootwin;
+ XWMHints hints;
+ Screen *screen;
+ XVisualInfo xvi;
+ int rootscreennum;
+ XTextProperty name;
+ XClassHint classhints;
+ XSizeHints normalhints;
+ XSetWindowAttributes attrs;
+
+ xscreenid = 0;
+ xdrawable = 0;
+
+ xdisplay = XOpenDisplay(NULL);
+ if(xdisplay == 0){
+ disp_val = getenv("DISPLAY");
+ if(disp_val == 0)
+ disp_val = "not set";
+ fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", disp_val);
+ cleanexit(0);
+ }
+
+ rootscreennum = DefaultScreen(xdisplay);
+ rootwin = DefaultRootWindow(xdisplay);
+
+ xscreendepth = DefaultDepth(xdisplay, rootscreennum);
+ if(XMatchVisualInfo(xdisplay, rootscreennum, 24, TrueColor, &xvi)
+ || XMatchVisualInfo(xdisplay, rootscreennum, 24, DirectColor, &xvi)){
+ xvis = xvi.visual;
+ xscreendepth = 24;
+ xtblbit = 1;
+ }
+ else if(XMatchVisualInfo(xdisplay, rootscreennum, 8, PseudoColor, &xvi)
+ || XMatchVisualInfo(xdisplay, rootscreennum, 8, StaticColor, &xvi)){
+ if(xscreendepth > 8) {
+ fprint(2, "emu: win-x11 can't deal with depth %d screens\n", xscreendepth);
+ cleanexit(0);
+ }
+ xvis = xvi.visual;
+ xscreendepth = 8;
+ }
+ else{
+ if(xscreendepth != 8){
+ fprint(2, "emu: win-x11 can't deal with depth %d screens\n", xscreendepth);
+ cleanexit(0);
+ }
+ xvis = DefaultVisual(xdisplay, rootscreennum);
+ }
+
+ screen = DefaultScreenOfDisplay(xdisplay);
+ xcmap = DefaultColormapOfScreen(screen);
+
+ if(xvis->class != StaticColor){
+ graphicscmap(map);
+ initmap(rootwin);
+ }
+
+ if(modmap = XGetModifierMapping(xdisplay))
+ keypermod = modmap->max_keypermod;
+
+ attrs.colormap = xcmap;
+ attrs.background_pixel = 0;
+ attrs.border_pixel = 0;
+ /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */
+ xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs);
+
+ /*
+ * set up property as required by ICCCM
+ */
+ name.value = "inferno";
+ name.encoding = XA_STRING;
+ name.format = 8;
+ name.nitems = strlen(name.value);
+ normalhints.flags = USSize|PMaxSize;
+ normalhints.max_width = normalhints.width = xsize;
+ normalhints.max_height = normalhints.height = ysize;
+ hints.flags = InputHint|StateHint;
+ hints.input = 1;
+ hints.initial_state = NormalState;
+ classhints.res_name = "inferno";
+ classhints.res_class = "Inferno";
+ argv[0] = "inferno";
+ argv[1] = nil;
+ XSetWMProperties(xdisplay, xdrawable,
+ &name, /* XA_WM_NAME property for ICCCM */
+ &name, /* XA_WM_ICON_NAME */
+ argv, /* XA_WM_COMMAND */
+ 1, /* argc */
+ &normalhints, /* XA_WM_NORMAL_HINTS */
+ &hints, /* XA_WM_HINTS */
+ &classhints); /* XA_WM_CLASS */
+
+ /*
+ * put the window on the screen
+ */
+ XMapWindow(xdisplay, xdrawable);
+
+ xscreenid = XCreatePixmap(xdisplay, xdrawable, xsize, ysize, xscreendepth);
+ XFlush(xdisplay);
+
+ xgcfill = creategc(xscreenid);
+ XSetFillStyle(xdisplay, xgcfill, FillSolid);
+ xgccopy = creategc(xscreenid);
+ xgcsimplesrc = creategc(xscreenid);
+ XSetFillStyle(xdisplay, xgcsimplesrc, FillStippled);
+ xgczero = creategc(xscreenid);
+ xgcreplsrc = creategc(xscreenid);
+ XSetFillStyle(xdisplay, xgcreplsrc, FillTiled);
+
+ pmid = XCreatePixmap(xdisplay, xdrawable, 1, 1, 1);
+ xgcfill0 = creategc(pmid);
+ XSetFillStyle(xdisplay, xgcfill0, FillSolid);
+ xgccopy0 = creategc(pmid);
+ xgcsimplesrc0 = creategc(pmid);
+ XSetFillStyle(xdisplay, xgcsimplesrc0, FillStippled);
+ xgczero0 = creategc(pmid);
+ xgcreplsrc0 = creategc(pmid);
+ XSetFillStyle(xdisplay, xgcreplsrc0, FillTiled);
+ XFreePixmap(xdisplay, pmid);
+
+ XSetForeground(xdisplay, xgccopy, infernotox11[0]);
+ XFillRectangle(xdisplay, xscreenid, xgccopy, 0, 0, xsize, ysize);
+
+ xkmcon = XOpenDisplay(NULL);
+ if(xkmcon == 0){
+ disp_val = getenv("DISPLAY");
+ if(disp_val == 0)
+ disp_val = "not set";
+ fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", disp_val);
+ cleanexit(0);
+ }
+}
+
+static void
+graphicscmap(XColor *map)
+{
+ int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
+
+ for(r=0; r!=4; r++) {
+ for(g = 0; g != 4; g++) {
+ for(b = 0; b!=4; b++) {
+ for(v = 0; v!=4; v++) {
+ den=r;
+ if(g > den)
+ den=g;
+ if(b > den)
+ den=b;
+ /* divide check -- pick grey shades */
+ if(den==0)
+ cr=cg=cb=v*17;
+ else {
+ num=17*(4*den+v);
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
+ idx = 255 - idx;
+ map[idx].red = cr*0x0101;
+ map[idx].green = cg*0x0101;
+ map[idx].blue = cb*0x0101;
+ map[idx].pixel = idx;
+ map[idx].flags = DoRed|DoGreen|DoBlue;
+
+ v7 = v >> 1;
+ idx7 = r*32 + v7*16 + g*4 + b;
+ if((v & 1) == v7){
+ map7to8[idx7][0] = idx;
+ if(den == 0) { /* divide check -- pick grey shades */
+ cr = ((255.0/7.0)*v7)+0.5;
+ cg = cr;
+ cb = cr;
+ }
+ else {
+ num=17*15*(4*den+v7*2)/14;
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ map7[idx7].red = cr*0x0101;
+ map7[idx7].green = cg*0x0101;
+ map7[idx7].blue = cb*0x0101;
+ map7[idx7].pixel = idx7;
+ map7[idx7].flags = DoRed|DoGreen|DoBlue;
+ }
+ else
+ map7to8[idx7][1] = idx;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Initialize and install the Inferno colormap as a private colormap for this
+ * application. Inferno gets the best colors here when it has the cursor focus.
+ */
+static void
+initmap(Window w)
+{
+ XColor c;
+ XColor xcol;
+ ulong v;
+ int i, pix, ncmaps;
+
+ if(xscreendepth <= 1)
+ return;
+
+ if(xvis->class == TrueColor || xvis->class == DirectColor) {
+ int color_order_init = 0;
+ uint pp, p;
+
+ for(i = 0; i < 256; i++) {
+ c = map[i];
+ /* find out index into colormap for our RGB */
+ if(!XAllocColor(xdisplay, xcmap, &c)) {
+ fprint(2, "emu: win-x11 can't alloc color\n");
+ cleanexit(0);
+ }
+
+ /* The pixel value returned from XGetPixel needs to
+ * be converted to RGB so we can call rgb2cmap()
+ * to translate between 24 bit X and our color. Unfortunately,
+ * the return value appears to be display server endian
+ * dependant. Therefore, we run some heuristics to later
+ * determine how to mask the int value correctly.
+ * Yeah, I know we can look at xvis->byte_order but
+ * some displays say MSB even though they run on LSB.
+ * Besides, this is more anal.
+ */
+
+ p = c.pixel;
+ pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff);
+ if(!color_order_init && (pp!=map[i].pixel)) {
+ /* check if endian is other way */
+ pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
+ if(pp!=map[i].pixel) {
+ fprint(2, "emu: win-x11 can't convert 24bit colors\n");
+ cleanexit(0);
+ }
+ color_order_init = 1;
+ x24bitswap = 1;
+ }
+
+ if(color_order_init) {
+ pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
+ if(pp!=map[i].pixel) {
+ fprint(2, "emu: win-x11 can't convert 24bit colors\n");
+ cleanexit(0);
+ }
+ /* no x11toinferno; later use rgb2cmap() */
+ infernotox11[map[i].pixel] = c.pixel;
+ }
+ else if(pp!=map[i].pixel) {
+ fprint(2, "emu: win-x11 can't convert 24bit colors\n");
+ cleanexit(0);
+ }
+ else {
+ /* no x11toinferno; later use rgb2cmap() */
+ infernotox11[map[i].pixel] = c.pixel;
+ }
+ }
+ }
+ else if(xvis->class == PseudoColor) {
+ if(xtblbit == 0){
+ xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll);
+ XStoreColors(xdisplay, xcmap, map, 256);
+ for(i = 0; i < 256; i++) {
+ infernotox11[i] = i;
+ x11toinferno[i] = i;
+ }
+ }
+ else {
+ for(i = 0; i < 128; i++) {
+ c = map7[i];
+ if(!XAllocColor(xdisplay, xcmap, &c)) {
+ fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n");
+ cleanexit(0);
+ }
+ infernotox11[map7to8[i][0]] = c.pixel;
+ infernotox11[map7to8[i][1]] = c.pixel;
+ x11toinferno[c.pixel] = map7to8[i][0];
+ }
+ }
+ }
+ else {
+ xtblbit = 0;
+ fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class);
+ }
+ return;
+}
+
+static void
+xmapping(XEvent *e)
+{
+ XMappingEvent *xe;
+
+ if(e->type != MappingNotify)
+ return;
+ xe = (XMappingEvent*)e;
+ if(modmap)
+ XFreeModifiermap(modmap);
+ modmap = XGetModifierMapping(xe->display);
+ if(modmap)
+ keypermod = modmap->max_keypermod;
+}
+
+
+/*
+ * Disable generation of GraphicsExpose/NoExpose events in the GC.
+ */
+static GC
+creategc(Drawable d)
+{
+ XGCValues gcv;
+
+ gcv.function = GXcopy;
+ gcv.graphics_exposures = False;
+ return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv);
+}
+
+static void
+xexpose(XEvent *e)
+{
+ IRectangle r;
+ XExposeEvent *xe;
+
+ if(e->type != Expose)
+ return;
+ xe = (XExposeEvent*)e;
+ r.min.x = xe->x;
+ r.min.y = xe->y;
+ r.max.x = xe->x + xe->width;
+ r.max.y = xe->y + xe->height;
+ drawqlock();
+ flushmemscreen(r);
+ drawqunlock();
+}
+
+
+static void
+xkeyboard(XEvent *e)
+{
+ int ind, n;
+ KeySym k;
+ Rune r;
+ unsigned int md;
+ char buf[1];
+
+ if(gkscanq) {
+ uchar ch = (KeyCode)e->xkey.keycode;
+ if(e->xany.type == KeyRelease)
+ ch |= 0x80;
+ qproduce(gkscanq, &ch, 1);
+ return;
+ }
+
+ /*
+ * I tried using XtGetActionKeysym, but it didn't seem to
+ * do case conversion properly
+ * (at least, with Xterminal servers and R4 intrinsics)
+ */
+ if(e->xany.type != KeyPress)
+ return;
+
+ md = e->xkey.state;
+ ind = 0;
+ if(md & ShiftMask)
+ ind = 1;
+
+ k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind);
+
+ /* May have to try unshifted version */
+ if(k == NoSymbol && ind == 1)
+ k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0);
+
+ if(k == XK_Multi_key || k == NoSymbol)
+ return;
+ if(k&0xFF00){
+ switch(k){
+ case XK_BackSpace:
+ case XK_Tab:
+ case XK_Escape:
+ case XK_Delete:
+ case XK_KP_0:
+ case XK_KP_1:
+ case XK_KP_2:
+ case XK_KP_3:
+ case XK_KP_4:
+ case XK_KP_5:
+ case XK_KP_6:
+ case XK_KP_7:
+ case XK_KP_8:
+ case XK_KP_9:
+ case XK_KP_Divide:
+ case XK_KP_Multiply:
+ case XK_KP_Subtract:
+ case XK_KP_Add:
+ case XK_KP_Decimal:
+ k &= 0x7F;
+ break;
+ case XK_Linefeed:
+ k = '\r';
+ break;
+ case XK_KP_Enter:
+ case XK_Return:
+ k = '\n';
+ break;
+ case XK_Alt_L:
+ case XK_Alt_R:
+ case XK_Meta_L:
+ case XK_Meta_R:
+ k = Latin;
+ break;
+ case XK_Left:
+ case XK_KP_Left:
+ k = Left;
+ break;
+ case XK_Down:
+ case XK_KP_Down:
+ k = Down;
+ break;
+ case XK_Right:
+ case XK_KP_Right:
+ k = Right;
+ break;
+ case XK_Up:
+ case XK_KP_Up:
+ k = Up;
+ break;
+ case XK_Home:
+ case XK_KP_Home:
+ k = Home;
+ break;
+ case XK_End:
+ case XK_KP_End:
+ k = End;
+ break;
+ case XK_Page_Up:
+ case XK_KP_Page_Up:
+ k = Pgup;
+ break;
+ case XK_Page_Down:
+ case XK_KP_Page_Down:
+ k = Pgdown;
+ break;
+ default: /* not ISO-1 or tty control */
+ return;
+ }
+ }
+ /* Compensate for servers that call a minus a hyphen */
+ if(k == XK_hyphen)
+ k = XK_minus;
+ /* Do control mapping ourselves if translator doesn't */
+ if(md & ControlMask)
+ k &= 0x9f;
+ if(k == '\t' && ind)
+ k = BackTab;
+
+ if(md & Mod1Mask)
+ k = APP|(k&0xff);
+ if(k == NoSymbol)
+ return;
+
+ gkbdputc(gkbdq, k);
+}
+
+static void
+xmouse(XEvent *e)
+{
+ int s, dbl;
+ XButtonEvent *be;
+ XMotionEvent *me;
+ XEvent motion;
+ Pointer m;
+ static ulong lastb, lastt;
+
+ dbl = 0;
+ switch(e->type){
+ case ButtonPress:
+ be = (XButtonEvent *)e;
+ m.x = be->x;
+ m.y = be->y;
+ s = be->state;
+ if(be->button == lastb && be->time - lastt < DblTime)
+ dbl = 1;
+ lastb = be->button;
+ lastt = be->time;
+ switch(be->button){
+ case 1:
+ s |= Button1Mask;
+ break;
+ case 2:
+ s |= Button2Mask;
+ break;
+ case 3:
+ s |= Button3Mask;
+ break;
+ }
+ break;
+ case ButtonRelease:
+ be = (XButtonEvent *)e;
+ m.x = be->x;
+ m.y = be->y;
+ s = be->state;
+ switch(be->button){
+ case 1:
+ s &= ~Button1Mask;
+ break;
+ case 2:
+ s &= ~Button2Mask;
+ break;
+ case 3:
+ s &= ~Button3Mask;
+ break;
+ }
+ break;
+ case MotionNotify:
+ me = (XMotionEvent *) e;
+
+ /* remove excess MotionNotify events from queue and keep last one */
+ while(XCheckTypedWindowEvent(xkmcon, xdrawable, MotionNotify, &motion) == True)
+ me = (XMotionEvent *) &motion;
+
+ s = me->state;
+ m.x = me->x;
+ m.y = me->y;
+ break;
+ default:
+ return;
+ }
+
+ m.b = 0;
+ if(s & Button1Mask)
+ m.b |= 1;
+ if(s & Button2Mask)
+ m.b |= 2;
+ if(s & Button3Mask)
+ m.b |= 4;
+ if(dbl)
+ m.b |= 1<<4;
+
+ m.modify = 1;
+ mouseproduce(m);
+}
+
--- /dev/null
+++ b/emu/port/win-x11a.c
@@ -1,0 +1,1621 @@
+/*
+ * This implementation of the screen functions for X11 uses the
+ * portable implementation of the Inferno drawing operations (libmemdraw)
+ * to do the work, then has flushmemscreen copy the result to the X11 display.
+ * Thus it potentially supports all colour depths but with a possible
+ * performance penalty (although it tries to use the X11 shared memory extension
+ * to copy the result to the screen, which might reduce the latter).
+ *
+ * CraigN
+ */
+
+#define _GNU_SOURCE 1
+#define XTHREADS
+#include "dat.h"
+#include "fns.h"
+#undef log2
+#include <draw.h>
+#include "cursor.h"
+#include "keyboard.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define Colormap XColormap
+#define Cursor XCursor
+#define Display XDisplay
+#define Drawable XDrawable
+#define Font XFont
+#define GC XGC
+#define Point XPoint
+#define Rectangle XRectangle
+#define Screen XScreen
+#define Visual XVisual
+#define Window XWindow
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/extensions/XShm.h>
+
+#include "keysym2ucs.h"
+
+#undef Colormap
+#undef Cursor
+#undef Display
+#undef XDrawable
+#undef Font
+#undef GC
+#undef Point
+#undef Rectangle
+#undef Screen
+#undef Visual
+#undef Window
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+static int displaydepth;
+extern ulong displaychan;
+
+enum
+{
+ DblTime = 300 /* double click time in msec */
+};
+
+/* screen data .... */
+static uchar* gscreendata;
+static uchar* xscreendata;
+
+XColor map[256]; /* Inferno colormap array */
+XColor mapr[256]; /* Inferno red colormap array */
+XColor mapg[256]; /* Inferno green colormap array */
+XColor mapb[256]; /* Inferno blue colormap array */
+XColor map7[128]; /* Inferno colormap array */
+uchar map7to8[128][2];
+
+/* for copy/paste, lifted from plan9ports via drawterm */
+static Atom clipboard;
+static Atom utf8string;
+static Atom targets;
+static Atom text;
+static Atom compoundtext;
+
+static Atom cursorchange;
+
+static XColormap xcmap; /* Default shared colormap */
+static int infernotox11[256]; /* Values for mapping between */
+static int infernortox11[256]; /* Values for mapping between */
+static int infernogtox11[256]; /* Values for mapping between */
+static int infernobtox11[256]; /* Values for mapping between */
+static int triedscreen;
+static XDrawable xdrawable;
+static void xexpose(XEvent*);
+static void xmouse(XEvent*);
+static void xkeyboard(XEvent*);
+static void xsetcursor(XEvent*);
+static void xkbdproc(void*);
+static void xdestroy(XEvent*);
+static void xselect(XEvent*, XDisplay*);
+static void xproc(void*);
+static void xinitscreen(int, int, ulong, ulong*, int*);
+static void initxcmap(XWindow);
+static XGC creategc(XDrawable);
+static void graphicsgmap(XColor*, int);
+static void graphicscmap(XColor*);
+static void graphicsrgbmap(XColor*, XColor*, XColor*);
+
+static int xscreendepth;
+static XDisplay* xdisplay; /* used holding draw lock */
+static XDisplay* xmcon; /* used only in xproc */
+static XDisplay* xkbdcon; /* used only in xkbdproc */
+static XDisplay* xsnarfcon; /* used holding clip.lk */
+static XVisual *xvis;
+static XGC xgc;
+static XImage *img;
+static int is_shm;
+
+static int putsnarf, assertsnarf;
+char *gkscanid = "emu_x11";
+
+/*
+ * The documentation for the XSHM extension implies that if the server
+ * supports XSHM but is not the local machine, the XShm calls will
+ * return False; but this turns out not to be the case. Instead, the
+ * server throws a BadAccess error. So, we need to catch X errors
+ * around all of our XSHM calls, sigh.
+ */
+static int shm_got_x_error = 0;
+static XErrorHandler old_handler = 0;
+static XErrorHandler old_io_handler = 0;
+
+static int
+shm_ehandler(XDisplay *dpy, XErrorEvent *error)
+{
+ shm_got_x_error = 1;
+ return 0;
+}
+
+static void
+clean_errhandlers(void)
+{
+ /* remove X11 error handler(s) */
+ if(old_handler)
+ XSetErrorHandler(old_handler);
+ old_handler = 0;
+ if(old_io_handler)
+ XSetErrorHandler(old_io_handler);
+ old_io_handler = 0;
+}
+
+static int
+makesharedfb(void)
+{
+ XShmSegmentInfo *shminfo;
+
+ shminfo = malloc(sizeof(XShmSegmentInfo));
+ if(shminfo == nil) {
+ fprint(2, "emu: cannot allocate XShmSegmentInfo\n");
+ cleanexit(0);
+ }
+
+ /* setup to catch X11 error(s) */
+ XSync(xdisplay, 0);
+ shm_got_x_error = 0;
+ if(old_handler != shm_ehandler)
+ old_handler = XSetErrorHandler(shm_ehandler);
+ if(old_io_handler != shm_ehandler)
+ old_io_handler = XSetErrorHandler(shm_ehandler);
+
+ img = XShmCreateImage(xdisplay, xvis, xscreendepth, ZPixmap,
+ NULL, shminfo, Xsize, Ysize);
+ XSync(xdisplay, 0);
+
+ /* did we get an X11 error? if so then try without shm */
+ if(shm_got_x_error) {
+ free(shminfo);
+ shminfo = NULL;
+ clean_errhandlers();
+ return 0;
+ }
+
+ if(img == nil) {
+ fprint(2, "emu: cannot allocate virtual screen buffer\n");
+ cleanexit(0);
+ }
+
+ shminfo->shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height, IPC_CREAT|0777);
+ shminfo->shmaddr = img->data = shmat(shminfo->shmid, 0, 0);
+ shminfo->readOnly = True;
+
+ if(!XShmAttach(xdisplay, shminfo)) {
+ fprint(2, "emu: cannot allocate virtual screen buffer\n");
+ cleanexit(0);
+ }
+ XSync(xdisplay, 0);
+
+ /*
+ * Delete the shared segment right now; the segment
+ * won't actually go away until both the client and
+ * server have deleted it. The server will delete it
+ * as soon as the client disconnects, so we might as
+ * well delete our side now as later.
+ */
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+
+ /* did we get an X11 error? if so then try without shm */
+ if(shm_got_x_error) {
+ XDestroyImage(img);
+ XSync(xdisplay, 0);
+ free(shminfo);
+ shminfo = NULL;
+ clean_errhandlers();
+ return 0;
+ }
+
+ gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3));
+ if(gscreendata == nil) {
+ fprint(2, "emu: cannot allocate screen buffer (%dx%dx%d)\n", Xsize, Ysize, displaydepth);
+ cleanexit(0);
+ }
+ xscreendata = (uchar*)img->data;
+
+ clean_errhandlers();
+ return 1;
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int depth;
+
+ Xsize &= ~0x3; /* ensure multiple of 4 */
+
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+
+ if(!triedscreen){
+ xinitscreen(Xsize, Ysize, displaychan, chan, d);
+ /*
+ * moved xproc from here to end since it could cause an expose event and
+ * hence a flushmemscreen before xscreendata is initialized
+ */
+ }
+ else{
+ *chan = displaychan;
+ *d = displaydepth;
+ }
+
+ *width = (Xsize/4)*(*d/8);
+ *softscreen = 1;
+ displaychan = *chan;
+ displaydepth = *d;
+
+ /* check for X Shared Memory Extension */
+ is_shm = XShmQueryExtension(xdisplay);
+
+ if(!is_shm || !makesharedfb()){
+ is_shm = 0;
+ depth = xscreendepth;
+ if(depth == 24)
+ depth = 32;
+
+ /* allocate virtual screen */
+ gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3));
+ xscreendata = malloc(Xsize * Ysize * (depth >> 3));
+ if(gscreendata == nil || xscreendata == nil) {
+ fprint(2, "emu: can not allocate virtual screen buffer (%dx%dx%d[%d])\n", Xsize, Ysize, displaydepth, depth);
+ return 0;
+ }
+ img = XCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, 0,
+ (char*)xscreendata, Xsize, Ysize, 8, Xsize * (depth >> 3));
+ if(img == nil) {
+ fprint(2, "emu: can not allocate virtual screen buffer (%dx%dx%d)\n", Xsize, Ysize, depth);
+ return 0;
+ }
+
+ }
+
+ if(!triedscreen){
+ triedscreen = 1;
+ kproc("xproc", xproc, xmcon, 0);
+ kproc("xkbdproc", xkbdproc, xkbdcon, KPX11); /* silly stack size for bloated X11 */
+ }
+
+ return gscreendata;
+}
+
+static void
+copy32to32(Rectangle r)
+{
+ int dx, width;
+ uchar *p, *ep, *cp;
+ u32int v, w, *dp, *wp, *edp, *lp;
+
+ width = Dx(r);
+ dx = Xsize - width;
+ dp = (u32int*)(gscreendata + (r.min.y * Xsize + r.min.x) * 4);
+ wp = (u32int*)(xscreendata + (r.min.y * Xsize + r.min.x) * 4);
+ edp = (u32int*)(gscreendata + (r.max.y * Xsize + r.max.x) * 4);
+ while(dp < edp) {
+ lp = dp + width;
+ while(dp < lp){
+ v = *dp++;
+ w = infernortox11[(v>>16)&0xff]<<16|infernogtox11[(v>>8)&0xff]<<8|infernobtox11[(v>>0)&0xff]<<0;
+ *wp++ = w;
+ }
+ dp += dx;
+ wp += dx;
+ }
+}
+
+static void
+copy8to32(Rectangle r)
+{
+ int dx, width;
+ uchar *p, *ep, *lp;
+ u32int *wp;
+
+ width = Dx(r);
+ dx = Xsize - width;
+ p = gscreendata + r.min.y * Xsize + r.min.x;
+ wp = (u32int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4);
+ ep = gscreendata + r.max.y * Xsize + r.max.x;
+ while(p < ep) {
+ lp = p + width;
+ while(p < lp)
+ *wp++ = infernotox11[*p++];
+ p += dx;
+ wp += dx;
+ }
+}
+
+static void
+copy8to24(Rectangle r)
+{
+ int dx, width, v;
+ uchar *p, *cp, *ep, *lp;
+
+ width = Dx(r);
+ dx = Xsize - width;
+ p = gscreendata + r.min.y * Xsize + r.min.x;
+ cp = xscreendata + (r.min.y * Xsize + r.min.x) * 3;
+ ep = gscreendata + r.max.y * Xsize + r.max.x;
+ while(p < ep) {
+ lp = p + width;
+ while(p < lp){
+ v = infernotox11[*p++];
+ cp[0] = (v>>16)&0xff;
+ cp[1] = (v>>8)&0xff;
+ cp[2] = (v>>0)&0xff;
+ cp += 3;
+ }
+ p += dx;
+ cp += 3*dx;
+ }
+}
+
+static void
+copy8to16(Rectangle r)
+{
+ int dx, width;
+ uchar *p, *ep, *lp;
+ u16int *sp;
+
+ width = Dx(r);
+ dx = Xsize - width;
+ p = gscreendata + r.min.y * Xsize + r.min.x;
+ sp = (unsigned short *)(xscreendata + (r.min.y * Xsize + r.min.x) * 2);
+ ep = gscreendata + r.max.y * Xsize + r.max.x;
+ while(p < ep) {
+ lp = p + width;
+ while(p < lp)
+ *sp++ = infernotox11[*p++];
+ p += dx;
+ sp += dx;
+ }
+}
+
+static void
+copy8to8(Rectangle r)
+{
+ int dx, width;
+ uchar *p, *cp, *ep, *lp;
+
+ width = Dx(r);
+ dx = Xsize - width;
+ p = gscreendata + r.min.y * Xsize + r.min.x;
+ cp = xscreendata + r.min.y * Xsize + r.min.x;
+ ep = gscreendata + r.max.y * Xsize + r.max.x;
+ while(p < ep) {
+ lp = p + width;
+ while(p < lp)
+ *cp++ = infernotox11[*p++];
+ p += dx;
+ cp += dx;
+ }
+}
+
+static void
+copy8topixel(Rectangle r)
+{
+ int x, y;
+ uchar *p;
+
+ /* mainly for 4-bit greyscale */
+ for (y = r.min.y; y < r.max.y; y++) {
+ x = r.min.x;
+ p = gscreendata + y * Xsize + x;
+ while (x < r.max.x)
+ XPutPixel(img, x++, y, infernotox11[*p++]);
+ }
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ char chanbuf[16];
+
+ // Clip to screen
+ if(r.min.x < 0)
+ r.min.x = 0;
+ if(r.min.y < 0)
+ r.min.y = 0;
+ if(r.max.x >= Xsize)
+ r.max.x = Xsize - 1;
+ if(r.max.y >= Ysize)
+ r.max.y = Ysize - 1;
+ if(r.max.x <= r.min.x || r.max.y <= r.min.y)
+ return;
+
+ switch(displaydepth){
+ case 32:
+ copy32to32(r);
+ break;
+ case 8:
+ switch(xscreendepth){
+ case 24:
+ /* copy8to24(r); */ /* doesn't happen? */
+ /* break */
+ case 32:
+ copy8to32(r);
+ break;
+ case 16:
+ copy8to16(r);
+ break;
+ case 8:
+ copy8to8(r);
+ break;
+ default:
+ copy8topixel(r);
+ break;
+ }
+ break;
+ default:
+ fprint(2, "emu: bad display depth %d chan %s xscreendepth %d\n", displaydepth,
+ chantostr(chanbuf, displaychan), xscreendepth);
+ cleanexit(0);
+ }
+
+ XLockDisplay(xdisplay);
+ /* Display image on X11 */
+ if(is_shm)
+ XShmPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, Dx(r), Dy(r), 0);
+ else
+ XPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, Dx(r), Dy(r));
+ XSync(xdisplay, 0);
+ XUnlockDisplay(xdisplay);
+}
+
+static int
+revbyte(int b)
+{
+ int r;
+
+ r = 0;
+ r |= (b&0x01) << 7;
+ r |= (b&0x02) << 5;
+ r |= (b&0x04) << 3;
+ r |= (b&0x08) << 1;
+ r |= (b&0x10) >> 1;
+ r |= (b&0x20) >> 3;
+ r |= (b&0x40) >> 5;
+ r |= (b&0x80) >> 7;
+ return r;
+}
+
+void
+setpointer(int x, int y)
+{
+ drawqlock();
+ XLockDisplay(xdisplay);
+ XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, x, y);
+ XFlush(xdisplay);
+ XUnlockDisplay(xdisplay);
+ drawqunlock();
+}
+
+static void
+xkbdproc(void *arg)
+{
+ XEvent event;
+ XDisplay *xd;
+
+ xd = arg;
+
+ /* BEWARE: the value of up is not defined for this proc on some systems */
+
+ XLockDisplay(xd); /* should be ours alone */
+ XSelectInput(xd, xdrawable, KeyPressMask | KeyReleaseMask);
+ for(;;){
+ XNextEvent(xd, &event);
+ xkeyboard(&event);
+ xsetcursor(&event);
+ }
+}
+
+static void
+xproc(void *arg)
+{
+ ulong mask;
+ XEvent event;
+ XDisplay *xd;
+
+ closepgrp(up->env->pgrp);
+ closefgrp(up->env->fgrp);
+ closeegrp(up->env->egrp);
+ closesigs(up->env->sigs);
+
+ xd = arg;
+ mask = ButtonPressMask|
+ ButtonReleaseMask|
+ PointerMotionMask|
+ Button1MotionMask|
+ Button2MotionMask|
+ Button3MotionMask|
+ Button4MotionMask|
+ Button5MotionMask|
+ ExposureMask|
+ StructureNotifyMask;
+
+ XLockDisplay(xd); /* should be ours alone */
+ XSelectInput(xd, xdrawable, mask);
+ for(;;){
+ XNextEvent(xd, &event);
+ xselect(&event, xd);
+ xmouse(&event);
+ xexpose(&event);
+ xdestroy(&event);
+ }
+}
+
+/*
+ * this crud is here because X11 can put huge amount of data
+ * on the stack during keyboard translation and cursor changing(!).
+ * we do both in a dedicated process with lots of stack, perhaps even enough.
+ */
+
+enum {
+ CursorSize= 32 /* biggest cursor size */
+};
+
+typedef struct ICursor ICursor;
+struct ICursor {
+ int inuse;
+ int modify;
+ int hotx;
+ int hoty;
+ int w;
+ int h;
+ uchar src[(CursorSize/8)*CursorSize]; /* image and mask bitmaps */
+ uchar mask[(CursorSize/8)*CursorSize];
+};
+static ICursor icursor;
+
+static void
+xcurslock(void)
+{
+ while(_tas(&icursor.inuse) != 0)
+ osyield();
+}
+
+static void
+xcursunlock(void)
+{
+ coherence();
+ icursor.inuse = 0;
+}
+
+static void
+xcursnotify(void)
+{
+ XClientMessageEvent e;
+
+ memset(&e, 0, sizeof e);
+ e.type = ClientMessage;
+ e.window = xdrawable;
+ e.message_type = cursorchange;
+ e.format = 8;
+ XLockDisplay(xdisplay);
+ XSendEvent(xdisplay, xdrawable, True, KeyPressMask, (XEvent*)&e);
+ XFlush(xdisplay);
+ XUnlockDisplay(xdisplay);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ uchar *bs, *bc, *ps, *pm;
+ int i, j, w, h, bpl;
+
+ if(c->data == nil){
+ drawqlock();
+ if(icursor.h != 0){
+ xcurslock();
+ icursor.h = 0;
+ icursor.modify = 1;
+ xcursunlock();
+ }
+ xcursnotify();
+ drawqunlock();
+ return;
+ }
+
+ drawqlock();
+ xcurslock();
+ icursor.modify = 0; /* xsetcursor will now ignore it */
+ xcursunlock();
+
+ h = (c->maxy-c->miny)/2; /* image, then mask */
+ if(h > CursorSize)
+ h = CursorSize;
+ bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+ w = bpl;
+ if(w > CursorSize/8)
+ w = CursorSize/8;
+
+ ps = icursor.src;
+ pm = icursor.mask;
+ bc = c->data;
+ bs = c->data + h*bpl;
+ for(i = 0; i < h; i++){
+ for(j = 0; j < bpl && j < w; j++) {
+ *ps++ = revbyte(bs[j]);
+ *pm++ = revbyte(bs[j] | bc[j]);
+ }
+ bs += bpl;
+ bc += bpl;
+ }
+ icursor.h = h;
+ icursor.w = w*8;
+ icursor.hotx = c->hotx;
+ icursor.hoty = c->hoty;
+ icursor.modify = 1;
+ xcursnotify();
+ drawqunlock();
+}
+
+static void
+xsetcursor(XEvent *e)
+{
+ ICursor ic;
+ XCursor xc;
+ XColor fg, bg;
+ Pixmap xsrc, xmask;
+ static XCursor xcursor;
+
+ if(e->type != ClientMessage || !e->xclient.send_event || e->xclient.message_type != cursorchange)
+ return;
+
+ xcurslock();
+ if(icursor.modify == 0){
+ xcursunlock();
+ return;
+ }
+ icursor.modify = 0;
+ if(icursor.h == 0){
+ xcursunlock();
+ /* set the default system cursor */
+ if(xcursor != 0) {
+ XFreeCursor(xkbdcon, xcursor);
+ xcursor = 0;
+ }
+ XUndefineCursor(xkbdcon, xdrawable);
+ XFlush(xkbdcon);
+ return;
+ }
+ ic = icursor;
+ xcursunlock();
+
+ xsrc = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.src, ic.w, ic.h);
+ xmask = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.mask, ic.w, ic.h);
+
+ fg = map[0];
+ bg = map[255];
+ fg.pixel = infernotox11[0];
+ bg.pixel = infernotox11[255];
+ xc = XCreatePixmapCursor(xkbdcon, xsrc, xmask, &fg, &bg, -ic.hotx, -ic.hoty);
+ if(xc != 0) {
+ XDefineCursor(xkbdcon, xdrawable, xc);
+ if(xcursor != 0)
+ XFreeCursor(xkbdcon, xcursor);
+ xcursor = xc;
+ }
+ XFreePixmap(xkbdcon, xsrc);
+ XFreePixmap(xkbdcon, xmask);
+ XFlush(xkbdcon);
+}
+
+typedef struct Mg Mg;
+struct Mg
+{
+ int code;
+ int bit;
+ int len;
+ ulong mask;
+};
+
+static int
+maskx(Mg* g, int code, ulong mask)
+{
+ int i;
+
+ for(i=0; i<32; i++)
+ if(mask & (1<<i))
+ break;
+ if(i == 32)
+ return 0;
+ g->code = code;
+ g->bit = i;
+ g->mask = mask;
+ for(g->len = 0; i<32 && (mask & (1<<i))!=0; i++)
+ g->len++;
+ return 1;
+}
+
+/*
+ * for a given depth, we need to check the available formats
+ * to find how many actual bits are used per pixel.
+ */
+static int
+xactualdepth(int screenno, int depth)
+{
+ XPixmapFormatValues *pfmt;
+ int i, n;
+
+ pfmt = XListPixmapFormats(xdisplay, &n);
+ for(i=0; i<n; i++)
+ if(pfmt[i].depth == depth)
+ return pfmt[i].bits_per_pixel;
+ return -1;
+}
+
+static int
+xtruevisual(int screenno, int reqdepth, XVisualInfo *vi, ulong *chan)
+{
+ XVisual *xv;
+ Mg r, g, b;
+ int pad, d;
+ ulong c;
+ char buf[30];
+
+ if(XMatchVisualInfo(xdisplay, screenno, reqdepth, TrueColor, vi) ||
+ XMatchVisualInfo(xdisplay, screenno, reqdepth, DirectColor, vi)){
+ xv = vi->visual;
+ if(maskx(&r, CRed, xv->red_mask) &&
+ maskx(&g, CGreen, xv->green_mask) &&
+ maskx(&b, CBlue, xv->blue_mask)){
+ d = xactualdepth(screenno, reqdepth);
+ if(d < 0)
+ return 0;
+ pad = d - (r.len + g.len + b.len);
+ if(0){
+ fprint(2, "r: %8.8lux %d %d\ng: %8.8lux %d %d\nb: %8.8lux %d %d\n",
+ xv->red_mask, r.bit, r.len, xv->green_mask, g.bit, g.len, xv->blue_mask, b.bit, b.len);
+ }
+ if(r.bit > b.bit)
+ c = CHAN3(CRed, r.len, CGreen, g.len, CBlue, b.len);
+ else
+ c = CHAN3(CBlue, b.len, CGreen, g.len, CRed, r.len);
+ if(pad > 0)
+ c |= CHAN1(CIgnore, pad) << 24;
+ *chan = c;
+ xscreendepth = reqdepth;
+ if(0)
+ fprint(2, "chan=%s reqdepth=%d bits=%d\n", chantostr(buf, c), reqdepth, d);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+xmapvisual(int screenno, XVisualInfo *vi, ulong *chan)
+{
+ if(XMatchVisualInfo(xdisplay, screenno, 8, PseudoColor, vi) ||
+ XMatchVisualInfo(xdisplay, screenno, 8, StaticColor, vi)){
+ *chan = CMAP8;
+ xscreendepth = 8;
+ return 1;
+ }
+ return 0;
+}
+
+static void
+xinitscreen(int xsize, int ysize, ulong reqchan, ulong *chan, int *d)
+{
+ char *argv[2];
+ char *dispname;
+ XWindow rootwin;
+ XWMHints hints;
+ XVisualInfo xvi;
+ XScreen *screen;
+ int rootscreennum;
+ XTextProperty name;
+ XClassHint classhints;
+ XSizeHints normalhints;
+ XSetWindowAttributes attrs;
+ char buf[30];
+ int i;
+
+ xdrawable = 0;
+
+ dispname = getenv("DISPLAY");
+ if(dispname == nil)
+ dispname = "not set";
+ XInitThreads();
+ xdisplay = XOpenDisplay(NULL);
+ if(xdisplay == 0){
+ fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname);
+ cleanexit(0);
+ }
+
+ rootscreennum = DefaultScreen(xdisplay);
+ rootwin = DefaultRootWindow(xdisplay);
+ xscreendepth = DefaultDepth(xdisplay, rootscreennum);
+ xvis = DefaultVisual(xdisplay, rootscreennum);
+ screen = DefaultScreenOfDisplay(xdisplay);
+ xcmap = DefaultColormapOfScreen(screen);
+
+ if(reqchan == 0){
+ *chan = 0;
+ if(xscreendepth <= 16){ /* try for better colour */
+ xtruevisual(rootscreennum, 16, &xvi, chan) ||
+ xtruevisual(rootscreennum, 15, &xvi, chan) ||
+ xtruevisual(rootscreennum, 24, &xvi, chan) ||
+ xmapvisual(rootscreennum, &xvi, chan);
+ }else{
+ xtruevisual(rootscreennum, xscreendepth, &xvi, chan) ||
+ xtruevisual(rootscreennum, 24, &xvi, chan);
+ }
+ if(*chan == 0){
+ fprint(2, "emu: could not find suitable x11 pixel format for depth %d on this display\n", xscreendepth);
+ cleanexit(0);
+ }
+ reqchan = *chan;
+ *d = chantodepth(reqchan);
+ xvis = xvi.visual;
+ }else{
+ *chan = reqchan; /* not every channel description will work */
+ *d = chantodepth(reqchan);
+ if(*d != xactualdepth(rootscreennum, *d)){
+ fprint(2, "emu: current x11 display configuration does not support %s (depth %d) directly\n",
+ chantostr(buf, reqchan), *d);
+ cleanexit(0);
+ }
+ }
+
+ if(xvis->class != StaticColor) {
+ if(TYPE(*chan) == CGrey)
+ graphicsgmap(map, NBITS(reqchan));
+ else{
+ graphicscmap(map);
+ graphicsrgbmap(mapr, mapg, mapb);
+ }
+ initxcmap(rootwin);
+ }
+
+ memset(&attrs, 0, sizeof(attrs));
+ attrs.colormap = xcmap;
+ attrs.background_pixel = 0;
+ attrs.border_pixel = 0;
+ /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */
+ xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth,
+ InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs);
+
+ /*
+ * set up property as required by ICCCM
+ */
+ memset(&name, 0, sizeof(name));
+ name.value = (uchar*)"inferno";
+ name.encoding = XA_STRING;
+ name.format = 8;
+ name.nitems = strlen((char*)name.value);
+
+ memset(&normalhints, 0, sizeof(normalhints));
+ normalhints.flags = USSize|PMaxSize;
+ normalhints.max_width = normalhints.width = xsize;
+ normalhints.max_height = normalhints.height = ysize;
+ hints.flags = InputHint|StateHint;
+ hints.input = 1;
+ hints.initial_state = NormalState;
+
+ memset(&classhints, 0, sizeof(classhints));
+ classhints.res_name = "inferno";
+ classhints.res_class = "Inferno";
+ argv[0] = "inferno";
+ argv[1] = nil;
+ XSetWMProperties(xdisplay, xdrawable,
+ &name, /* XA_WM_NAME property for ICCCM */
+ &name, /* XA_WM_ICON_NAME */
+ argv, /* XA_WM_COMMAND */
+ 1, /* argc */
+ &normalhints, /* XA_WM_NORMAL_HINTS */
+ &hints, /* XA_WM_HINTS */
+ &classhints); /* XA_WM_CLASS */
+
+ XMapWindow(xdisplay, xdrawable);
+ XFlush(xdisplay);
+
+ xgc = creategc(xdrawable);
+
+ xmcon = XOpenDisplay(NULL);
+ xsnarfcon = XOpenDisplay(NULL);
+ xkbdcon = XOpenDisplay(NULL);
+ if(xmcon == 0 || xsnarfcon == 0 || xkbdcon == 0){
+ fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname);
+ cleanexit(0);
+ }
+
+ clipboard = XInternAtom(xmcon, "CLIPBOARD", False);
+ utf8string = XInternAtom(xmcon, "UTF8_STRING", False);
+ targets = XInternAtom(xmcon, "TARGETS", False);
+ text = XInternAtom(xmcon, "TEXT", False);
+ compoundtext = XInternAtom(xmcon, "COMPOUND_TEXT", False);
+
+ cursorchange = XInternAtom(xkbdcon, "TheCursorHasChanged", False);
+
+}
+
+static void
+graphicsgmap(XColor *map, int d)
+{
+ int i, j, s, m, p;
+
+ s = 8-d;
+ m = 1;
+ while(--d >= 0)
+ m *= 2;
+ m = 255/(m-1);
+ for(i=0; i < 256; i++){
+ j = (i>>s)*m;
+ p = 255-i;
+ map[p].red = map[p].green = map[p].blue = (255-j)*0x0101;
+ map[p].pixel = p;
+ map[p].flags = DoRed|DoGreen|DoBlue;
+ }
+}
+
+static void
+graphicscmap(XColor *map)
+{
+ int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
+
+ for(r=0; r!=4; r++) {
+ for(g = 0; g != 4; g++) {
+ for(b = 0; b!=4; b++) {
+ for(v = 0; v!=4; v++) {
+ den=r;
+ if(g > den)
+ den=g;
+ if(b > den)
+ den=b;
+ /* divide check -- pick grey shades */
+ if(den==0)
+ cr=cg=cb=v*17;
+ else {
+ num=17*(4*den+v);
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
+ /* was idx = 255 - idx; */
+ map[idx].red = cr*0x0101;
+ map[idx].green = cg*0x0101;
+ map[idx].blue = cb*0x0101;
+ map[idx].pixel = idx;
+ map[idx].flags = DoRed|DoGreen|DoBlue;
+
+ v7 = v >> 1;
+ idx7 = r*32 + v7*16 + g*4 + b;
+ if((v & 1) == v7){
+ map7to8[idx7][0] = idx;
+ if(den == 0) { /* divide check -- pick grey shades */
+ cr = ((255.0/7.0)*v7)+0.5;
+ cg = cr;
+ cb = cr;
+ }
+ else {
+ num=17*15*(4*den+v7*2)/14;
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ map7[idx7].red = cr*0x0101;
+ map7[idx7].green = cg*0x0101;
+ map7[idx7].blue = cb*0x0101;
+ map7[idx7].pixel = idx7;
+ map7[idx7].flags = DoRed|DoGreen|DoBlue;
+ }
+ else
+ map7to8[idx7][1] = idx;
+ }
+ }
+ }
+ }
+}
+
+static void
+graphicsrgbmap(XColor *mapr, XColor *mapg, XColor *mapb)
+{
+ int i;
+
+ memset(mapr, 0, 256*sizeof(XColor));
+ memset(mapg, 0, 256*sizeof(XColor));
+ memset(mapb, 0, 256*sizeof(XColor));
+ for(i=0; i < 256; i++){
+ mapr[i].red = mapg[i].green = mapb[i].blue = i*0x0101;
+ mapr[i].pixel = mapg[i].pixel = mapb[i].pixel = i;
+ mapr[i].flags = mapg[i].flags = mapb[i].flags = DoRed|DoGreen|DoBlue;
+ }
+}
+
+/*
+ * Initialize and install the Inferno colormap as a private colormap for this
+ * application. Inferno gets the best colors here when it has the cursor focus.
+ */
+static void
+initxcmap(XWindow w)
+{
+ XColor c;
+ int i;
+
+ if(xscreendepth <= 1)
+ return;
+
+ switch(xvis->class){
+ case TrueColor:
+ case DirectColor:
+ for(i = 0; i < 256; i++) {
+ c = map[i];
+ /* find index into colormap for our RGB */
+ if(!XAllocColor(xdisplay, xcmap, &c)) {
+ fprint(2, "emu: win-x11 can't alloc color\n");
+ cleanexit(0);
+ }
+ infernotox11[map[i].pixel] = c.pixel;
+ if(xscreendepth >= 24){
+ c = mapr[i];
+ XAllocColor(xdisplay, xcmap, &c);
+ infernortox11[i] = (c.pixel>>16)&0xff;
+ c = mapg[i];
+ XAllocColor(xdisplay, xcmap, &c);
+ infernogtox11[i] = (c.pixel>>8)&0xff;
+ c = mapb[i];
+ XAllocColor(xdisplay, xcmap, &c);
+ infernobtox11[i] = (c.pixel>>0)&0xff;
+ }
+ }
+if(0){int i, j; for(i=0;i<256; i+=16){print("%3d", i); for(j=i; j<i+16; j++)print(" %2.2ux/%2.2ux/%2.2ux", infernortox11[j], infernogtox11[j],infernobtox11[j]); print("\n");}}
+ /* TO DO: if the map(s) used give the identity map, don't use the map during copy */
+ break;
+
+ case PseudoColor:
+ if(xtblbit == 0){
+ xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll);
+ XStoreColors(xdisplay, xcmap, map, 256);
+ for(i = 0; i < 256; i++)
+ infernotox11[i] = i;
+ /* TO DO: the map is the identity, so don't need the map in copy */
+ } else {
+ for(i = 0; i < 128; i++) {
+ c = map7[i];
+ if(!XAllocColor(xdisplay, xcmap, &c)) {
+ fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n");
+ cleanexit(0);
+ }
+ infernotox11[map7to8[i][0]] = c.pixel;
+ infernotox11[map7to8[i][1]] = c.pixel;
+ }
+ }
+ break;
+
+ default:
+ xtblbit = 0;
+ fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class);
+ break;
+ }
+}
+
+static void
+xdestroy(XEvent *e)
+{
+ XDestroyWindowEvent *xe;
+ if(e->type != DestroyNotify)
+ return;
+ xe = (XDestroyWindowEvent*)e;
+ if(xe->window == xdrawable)
+ cleanexit(0);
+}
+
+/*
+ * Disable generation of GraphicsExpose/NoExpose events in the XGC.
+ */
+static XGC
+creategc(XDrawable d)
+{
+ XGCValues gcv;
+
+ gcv.function = GXcopy;
+ gcv.graphics_exposures = False;
+ return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv);
+}
+
+static void
+xexpose(XEvent *e)
+{
+ Rectangle r;
+ XExposeEvent *xe;
+
+ if(e->type != Expose)
+ return;
+ xe = (XExposeEvent*)e;
+ r.min.x = xe->x;
+ r.min.y = xe->y;
+ r.max.x = xe->x + xe->width;
+ r.max.y = xe->y + xe->height;
+ drawqlock();
+ flushmemscreen(r);
+ drawqunlock();
+}
+
+static void
+xkeyboard(XEvent *e)
+{
+ int ind, md;
+ KeySym k;
+
+ if(gkscanq != nil && (e->type == KeyPress || e->type == KeyRelease)){
+ uchar ch = e->xkey.keycode;
+ if(e->xany.type == KeyRelease)
+ ch |= 0x80;
+ qproduce(gkscanq, &ch, 1);
+ return;
+ }
+
+ /*
+ * I tried using XtGetActionKeysym, but it didn't seem to
+ * do case conversion properly
+ * (at least, with Xterminal servers and R4 intrinsics)
+ */
+ if(e->xany.type != KeyPress)
+ return;
+
+ md = e->xkey.state;
+ ind = 0;
+ if(md & ShiftMask)
+ ind = 1;
+ if(0){
+ k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind);
+
+ /* May have to try unshifted version */
+ if(k == NoSymbol && ind == 1)
+ k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0);
+ }else
+ XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL);
+
+ if(k == XK_Multi_key || k == NoSymbol)
+ return;
+ if(k&0xFF00){
+ switch(k){
+ case XK_BackSpace:
+ case XK_Tab:
+ case XK_Escape:
+ case XK_Delete:
+ case XK_KP_0:
+ case XK_KP_1:
+ case XK_KP_2:
+ case XK_KP_3:
+ case XK_KP_4:
+ case XK_KP_5:
+ case XK_KP_6:
+ case XK_KP_7:
+ case XK_KP_8:
+ case XK_KP_9:
+ case XK_KP_Divide:
+ case XK_KP_Multiply:
+ case XK_KP_Subtract:
+ case XK_KP_Add:
+ case XK_KP_Decimal:
+ k &= 0x7F;
+ break;
+ case XK_Linefeed:
+ k = '\r';
+ break;
+ case XK_KP_Space:
+ k = ' ';
+ break;
+ case XK_Home:
+ case XK_KP_Home:
+ k = Home;
+ break;
+ case XK_Left:
+ case XK_KP_Left:
+ k = Left;
+ break;
+ case XK_Up:
+ case XK_KP_Up:
+ k = Up;
+ break;
+ case XK_Down:
+ case XK_KP_Down:
+ k = Down;
+ break;
+ case XK_Right:
+ case XK_KP_Right:
+ k = Right;
+ break;
+ case XK_Page_Down:
+ case XK_KP_Page_Down:
+ k = Pgdown;
+ break;
+ case XK_End:
+ case XK_KP_End:
+ k = End;
+ break;
+ case XK_Page_Up:
+ case XK_KP_Page_Up:
+ k = Pgup;
+ break;
+ case XK_Insert:
+ case XK_KP_Insert:
+ k = Ins;
+ break;
+ case XK_KP_Enter:
+ case XK_Return:
+ k = '\n';
+ break;
+ case XK_Alt_L:
+ case XK_Alt_R:
+ k = Latin;
+ break;
+ case XK_Shift_L:
+ case XK_Shift_R:
+ case XK_Control_L:
+ case XK_Control_R:
+ case XK_Caps_Lock:
+ case XK_Shift_Lock:
+
+ case XK_Meta_L:
+ case XK_Meta_R:
+ case XK_Super_L:
+ case XK_Super_R:
+ case XK_Hyper_L:
+ case XK_Hyper_R:
+ return;
+ default: /* not ISO-1 or tty control */
+ if(k>0xff){
+ k = keysym2ucs(k); /* supplied by X */
+ if(k == -1)
+ return;
+ }
+ break;
+ }
+ }
+
+ /* Compensate for servers that call a minus a hyphen */
+ if(k == XK_hyphen)
+ k = XK_minus;
+ /* Do control mapping ourselves if translator doesn't */
+ if(md & ControlMask)
+ k &= 0x9f;
+ if(0){
+ if(k == '\t' && ind)
+ k = BackTab;
+
+ if(md & Mod1Mask)
+ k = APP|(k&0xff);
+ }
+ if(k == NoSymbol)
+ return;
+
+ gkbdputc(gkbdq, k);
+}
+
+static void
+xmouse(XEvent *e)
+{
+ int s, dbl;
+ XButtonEvent *be;
+ XMotionEvent *me;
+ XEvent motion;
+ int x, y, b;
+ static ulong lastb, lastt;
+
+ if(putsnarf != assertsnarf){
+ assertsnarf = putsnarf;
+ XSetSelectionOwner(xmcon, XA_PRIMARY, xdrawable, CurrentTime);
+ if(clipboard != None)
+ XSetSelectionOwner(xmcon, clipboard, xdrawable, CurrentTime);
+ XFlush(xmcon);
+ }
+
+ dbl = 0;
+ switch(e->type){
+ case ButtonPress:
+ be = (XButtonEvent *)e;
+ /*
+ * Fake message, just sent to make us announce snarf.
+ * Apparently state and button are 16 and 8 bits on
+ * the wire, since they are truncated by the time they
+ * get to us.
+ */
+ if(be->send_event
+ && (~be->state&0xFFFF)==0
+ && (~be->button&0xFF)==0)
+ return;
+ x = be->x;
+ y = be->y;
+ s = be->state;
+ if(be->button == lastb && be->time - lastt < DblTime)
+ dbl = 1;
+ lastb = be->button;
+ lastt = be->time;
+ switch(be->button){
+ case 1:
+ s |= Button1Mask;
+ break;
+ case 2:
+ s |= Button2Mask;
+ break;
+ case 3:
+ s |= Button3Mask;
+ break;
+ case 4:
+ s |= Button4Mask;
+ break;
+ case 5:
+ s |= Button5Mask;
+ break;
+ }
+ break;
+ case ButtonRelease:
+ be = (XButtonEvent *)e;
+ x = be->x;
+ y = be->y;
+ s = be->state;
+ switch(be->button){
+ case 1:
+ s &= ~Button1Mask;
+ break;
+ case 2:
+ s &= ~Button2Mask;
+ break;
+ case 3:
+ s &= ~Button3Mask;
+ break;
+ case 4:
+ s &= ~Button4Mask;
+ break;
+ case 5:
+ s &= ~Button5Mask;
+ break;
+ }
+ break;
+ case MotionNotify:
+ me = (XMotionEvent *) e;
+
+ /* remove excess MotionNotify events from queue and keep last one */
+ while(XCheckTypedWindowEvent(xmcon, xdrawable, MotionNotify, &motion) == True)
+ me = (XMotionEvent *) &motion;
+
+ s = me->state;
+ x = me->x;
+ y = me->y;
+ break;
+ default:
+ return;
+ }
+
+ b = 0;
+ if(s & Button1Mask)
+ b |= 1;
+ if(s & Button2Mask)
+ b |= 2;
+ if(s & Button3Mask)
+ b |= 4;
+ if(s & Button4Mask)
+ b |= 8;
+ if(s & Button5Mask)
+ b |= 16;
+ if(dbl)
+ b |= 1<<8;
+
+ mousetrack(b, x, y, 0);
+}
+
+#include "x11-keysym2ucs.c"
+
+/*
+ * Cut and paste. Just couldn't stand to make this simple...
+ */
+
+enum{
+ SnarfSize= 100*1024
+};
+
+typedef struct Clip Clip;
+struct Clip
+{
+ char buf[SnarfSize];
+ QLock lk;
+};
+Clip clip;
+
+#undef long /* sic */
+#undef ulong
+
+static char*
+_xgetsnarf(XDisplay *xd)
+{
+ uchar *data, *xdata;
+ Atom clipboard, type, prop;
+ unsigned long len, lastlen, dummy;
+ int fmt, i;
+ XWindow w;
+
+ qlock(&clip.lk);
+ /*
+ * Have we snarfed recently and the X server hasn't caught up?
+ */
+ if(putsnarf != assertsnarf)
+ goto mine;
+
+ /*
+ * Is there a primary selection (highlighted text in an xterm)?
+ */
+ clipboard = XA_PRIMARY;
+ w = XGetSelectionOwner(xd, XA_PRIMARY);
+ if(w == xdrawable){
+ mine:
+ data = (uchar*)strdup(clip.buf);
+ goto out;
+ }
+
+ /*
+ * If not, is there a clipboard selection?
+ */
+ if(w == None && clipboard != None){
+ clipboard = clipboard;
+ w = XGetSelectionOwner(xd, clipboard);
+ if(w == xdrawable)
+ goto mine;
+ }
+
+ /*
+ * If not, give up.
+ */
+ if(w == None){
+ data = nil;
+ goto out;
+ }
+
+ /*
+ * We should be waiting for SelectionNotify here, but it might never
+ * come, and we have no way to time out. Instead, we will clear
+ * local property #1, request our buddy to fill it in for us, and poll
+ * until he's done or we get tired of waiting.
+ *
+ * We should try to go for utf8string instead of XA_STRING,
+ * but that would add to the polling.
+ */
+ prop = 1;
+ XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0);
+ XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime);
+ XFlush(xd);
+ lastlen = 0;
+ for(i=0; i<10 || (lastlen!=0 && i<30); i++){
+ osmillisleep(100);
+ XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType,
+ &type, &fmt, &dummy, &len, &data);
+ if(lastlen == len && len > 0)
+ break;
+ lastlen = len;
+ }
+ if(i == 10){
+ data = nil;
+ goto out;
+ }
+ /* get the property */
+ data = nil;
+ XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0,
+ AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
+ if((type != XA_STRING && type != utf8string) || len == 0){
+ if(xdata)
+ XFree(xdata);
+ data = nil;
+ }else{
+ if(xdata){
+ data = (uchar*)strdup((char*)xdata);
+ XFree(xdata);
+ }else
+ data = nil;
+ }
+out:
+ qunlock(&clip.lk);
+ return (char*)data;
+}
+
+static void
+_xputsnarf(XDisplay *xd, char *data)
+{
+ XButtonEvent e;
+
+ if(strlen(data) >= SnarfSize)
+ return;
+ qlock(&clip.lk);
+ strcpy(clip.buf, data);
+
+ /* leave note for mouse proc to assert selection ownership */
+ putsnarf++;
+
+ /* send mouse a fake event so snarf is announced */
+ memset(&e, 0, sizeof e);
+ e.type = ButtonPress;
+ e.window = xdrawable;
+ e.state = ~0;
+ e.button = ~0;
+ XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e);
+ XFlush(xd);
+ qunlock(&clip.lk);
+}
+
+static void
+xselect(XEvent *e, XDisplay *xd)
+{
+ char *name;
+ XEvent r;
+ XSelectionRequestEvent *xe;
+ Atom a[4];
+
+ if(e->xany.type != SelectionRequest)
+ return;
+
+ memset(&r, 0, sizeof r);
+ xe = (XSelectionRequestEvent*)e;
+if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n",
+ xe->target, xe->requestor, xe->property, xe->selection);
+ r.xselection.property = xe->property;
+ if(xe->target == targets){
+ a[0] = XA_STRING;
+ a[1] = utf8string;
+ a[2] = text;
+ a[3] = compoundtext;
+
+ XChangeProperty(xd, xe->requestor, xe->property, xe->target,
+ 8, PropModeReplace, (uchar*)a, sizeof a);
+ }else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){
+ /* if the target is STRING we're supposed to reply with Latin1 XXX */
+ qlock(&clip.lk);
+ XChangeProperty(xd, xe->requestor, xe->property, xe->target,
+ 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf));
+ qunlock(&clip.lk);
+ }else{
+ iprint("get %d\n", xe->target);
+ name = XGetAtomName(xd, xe->target);
+ if(name == nil)
+ iprint("XGetAtomName failed\n");
+ else if(strcmp(name, "TIMESTAMP") != 0)
+ iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
+ r.xselection.property = None;
+ }
+
+ r.xselection.display = xe->display;
+ /* r.xselection.property filled above */
+ r.xselection.target = xe->target;
+ r.xselection.type = SelectionNotify;
+ r.xselection.requestor = xe->requestor;
+ r.xselection.time = xe->time;
+ r.xselection.send_event = True;
+ r.xselection.selection = xe->selection;
+ XSendEvent(xd, xe->requestor, False, 0, &r);
+ XFlush(xd);
+}
+
+char*
+clipread(void)
+{
+ char *p;
+
+ if(xsnarfcon == nil)
+ return nil;
+ XLockDisplay(xsnarfcon);
+ p = _xgetsnarf(xsnarfcon);
+ XUnlockDisplay(xsnarfcon);
+ return p;
+}
+
+int
+clipwrite(char *buf)
+{
+ if(xsnarfcon == nil)
+ return 0;
+ XLockDisplay(xsnarfcon);
+ _xputsnarf(xsnarfcon, buf);
+ XUnlockDisplay(xsnarfcon);
+ return 0;
+}
--- /dev/null
+++ b/emu/port/x11-keysym2ucs.c
@@ -1,0 +1,857 @@
+/* $XFree86: xc/programs/xterm/keysym2ucs.c,v 1.5 2001/06/18 19:09:26 dickey Exp $
+ * This module converts keysym values into the corresponding ISO 10646
+ * (UCS, Unicode) values.
+ *
+ * The array keysymtab[] contains pairs of X11 keysym values for graphical
+ * characters and the corresponding Unicode value. The function
+ * keysym2ucs() maps a keysym onto a Unicode value using a binary search,
+ * therefore keysymtab[] must remain SORTED by keysym value.
+ *
+ * The keysym -> UTF-8 conversion will hopefully one day be provided
+ * by Xlib via XmbLookupString() and should ideally not have to be
+ * done in X applications. But we are not there yet.
+ *
+ * We allow to represent any UCS character in the range U-00000000 to
+ * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff.
+ * This admittedly does not cover the entire 31-bit space of UCS, but
+ * it does cover all of the characters up to U-10FFFF, which can be
+ * represented by UTF-16, and more, and it is very unlikely that higher
+ * UCS codes will ever be assigned by ISO. So to get Unicode character
+ * U+ABCD you can directly use keysym 0x0100abcd.
+ *
+ * NOTE: The comments in the table below contain the actual character
+ * encoded in UTF-8, so for viewing and editing best use an editor in
+ * UTF-8 mode.
+ *
+ * Author: Markus G. Kuhn <mkuhn@acm.org>, University of Cambridge, April 2001
+ *
+ * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing
+ * an initial draft of the mapping table.
+ *
+ * This software is in the public domain. Share and enjoy!
+ *
+ * AUTOMATICALLY GENERATED FILE, DO NOT EDIT !!! (unicode/convmap.pl)
+ */
+
+#ifndef KEYSYM2UCS_INCLUDED
+
+#include "keysym2ucs.h"
+#define VISIBLE /* */
+
+#else
+
+#define VISIBLE static
+
+#endif
+
+static struct codepair {
+ unsigned short keysym;
+ unsigned short ucs;
+} keysymtab[] = {
+ { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */
+ { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */
+ { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */
+ { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */
+ { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */
+ { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */
+ { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */
+ { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */
+ { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */
+ { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */
+ { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */
+ { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */
+ { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */
+ { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */
+ { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */
+ { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */
+ { 0x01b7, 0x02c7 }, /* caron ˇ CARON */
+ { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */
+ { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */
+ { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */
+ { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */
+ { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */
+ { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */
+ { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */
+ { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */
+ { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */
+ { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */
+ { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */
+ { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */
+ { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */
+ { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */
+ { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */
+ { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */
+ { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */
+ { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */
+ { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
+ { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */
+ { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */
+ { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
+ { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */
+ { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */
+ { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */
+ { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */
+ { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */
+ { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */
+ { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */
+ { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */
+ { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */
+ { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */
+ { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */
+ { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */
+ { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */
+ { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */
+ { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */
+ { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */
+ { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */
+ { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */
+ { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */
+ { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
+ { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */
+ { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */
+ { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
+ { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */
+ { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */
+ { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */
+ { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */
+ { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */
+ { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */
+ { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
+ { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */
+ { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
+ { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */
+ { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
+ { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */
+ { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */
+ { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */
+ { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */
+ { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */
+ { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */
+ { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */
+ { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */
+ { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */
+ { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */
+ { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */
+ { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */
+ { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */
+ { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */
+ { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */
+ { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */
+ { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */
+ { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */
+ { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */
+ { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */
+ { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */
+ { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */
+ { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */
+ { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */
+ { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */
+ { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */
+ { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */
+ { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */
+ { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */
+ { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */
+ { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */
+ { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */
+ { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */
+ { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */
+ { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */
+ { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */
+ { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */
+ { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */
+ { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */
+ { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */
+ { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */
+ { 0x047e, 0x203e }, /* overline ‾ OVERLINE */
+ { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */
+ { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */
+ { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */
+ { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */
+ { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */
+ { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */
+ { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */
+ { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */
+ { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */
+ { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */
+ { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */
+ { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */
+ { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */
+ { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */
+ { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */
+ { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */
+ { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */
+ { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */
+ { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */
+ { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */
+ { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */
+ { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */
+ { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */
+ { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */
+ { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */
+ { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */
+ { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */
+ { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */
+ { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */
+ { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */
+ { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */
+ { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */
+ { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */
+ { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */
+ { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */
+ { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */
+ { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */
+ { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */
+ { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */
+ { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */
+ { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */
+ { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */
+ { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */
+ { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */
+ { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */
+ { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */
+ { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */
+ { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */
+ { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */
+ { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */
+ { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */
+ { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */
+ { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */
+ { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */
+ { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */
+ { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */
+ { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */
+ { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */
+ { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */
+ { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */
+ { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */
+ { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */
+ { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
+ { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */
+ { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */
+ { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */
+ { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */
+ { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */
+ { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */
+ { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */
+ { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */
+ { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */
+ { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */
+ { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */
+ { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */
+ { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */
+ { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */
+ { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */
+ { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */
+ { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */
+ { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */
+ { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */
+ { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */
+ { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */
+ { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */
+ { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */
+ { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */
+ { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */
+ { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */
+ { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */
+ { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */
+ { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */
+ { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */
+ { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */
+ { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */
+ { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */
+ { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */
+ { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */
+ { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */
+ { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */
+ { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */
+ { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */
+ { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */
+ { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */
+ { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */
+ { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */
+ { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */
+ { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */
+ { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */
+ { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */
+ { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */
+ { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */
+ { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */
+ { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */
+ { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */
+ { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */
+ { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
+ { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */
+ { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */
+ { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */
+ { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */
+ { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */
+ { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */
+ { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */
+ { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */
+ { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */
+ { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */
+ { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */
+ { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */
+ { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */
+ { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */
+ { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
+ { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */
+ { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */
+ { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */
+ { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */
+ { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */
+ { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */
+ { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */
+ { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */
+ { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */
+ { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */
+ { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */
+ { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */
+ { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */
+ { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */
+ { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */
+ { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */
+ { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */
+ { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */
+ { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */
+ { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */
+ { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */
+ { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */
+ { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */
+ { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */
+ { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */
+ { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */
+ { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */
+ { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */
+ { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */
+ { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */
+ { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */
+ { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */
+ { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */
+ { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */
+ { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */
+ { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */
+ { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */
+ { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */
+ { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */
+ { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */
+ { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */
+ { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */
+ { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */
+ { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */
+ { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */
+ { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */
+ { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */
+ { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */
+ { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */
+ { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */
+ { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */
+ { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */
+ { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */
+ { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */
+ { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */
+ { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */
+ { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */
+ { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */
+ { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */
+ { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */
+ { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */
+ { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */
+ { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */
+ { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */
+ { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */
+ { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */
+ { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */
+ { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */
+ { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */
+ { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */
+ { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */
+ { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */
+ { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */
+ { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */
+ { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */
+ { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */
+ { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
+ { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */
+ { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */
+ { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
+ { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */
+ { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */
+ { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */
+ { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */
+ { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */
+ { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */
+ { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */
+ { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */
+ { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
+ { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */
+ { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */
+ { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */
+ { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
+ { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */
+ { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */
+ { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */
+ { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */
+ { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */
+ { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */
+ { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */
+ { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */
+ { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */
+ { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */
+ { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */
+ { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */
+ { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */
+ { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */
+ { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */
+ { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */
+ { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */
+ { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */
+ { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */
+ { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */
+ { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */
+ { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */
+ { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */
+ { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */
+ { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */
+ { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */
+ { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */
+ { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */
+ { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */
+ { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */
+ { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */
+ { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */
+ { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */
+ { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */
+ { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */
+ { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */
+ { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */
+ { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */
+ { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */
+ { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */
+ { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */
+ { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */
+ { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */
+ { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */
+ { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */
+ { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */
+ { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */
+ { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */
+ { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */
+ { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */
+ { 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */
+ { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
+ { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */
+ { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */
+ { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */
+ { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */
+ { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */
+ { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */
+ { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */
+ { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */
+ { 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */
+ { 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */
+ { 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */
+ { 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */
+ { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */
+ { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */
+/* 0x08b1 topleftsummation ? ??? */
+/* 0x08b2 botleftsummation ? ??? */
+/* 0x08b3 topvertsummationconnector ? ??? */
+/* 0x08b4 botvertsummationconnector ? ??? */
+/* 0x08b5 toprightsummation ? ??? */
+/* 0x08b6 botrightsummation ? ??? */
+/* 0x08b7 rightmiddlesummation ? ??? */
+ { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */
+ { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */
+ { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */
+ { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */
+ { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */
+ { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */
+ { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */
+ { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */
+ { 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */
+ { 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */
+ { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */
+ { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */
+ { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */
+ { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */
+ { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */
+ { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */
+ { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */
+ { 0x08dd, 0x222a }, /* union ∪ UNION */
+ { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */
+ { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */
+ { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */
+ { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */
+ { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */
+ { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */
+ { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */
+ { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */
+/* 0x09df blank ? ??? */
+ { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */
+ { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */
+ { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */
+ { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */
+ { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */
+ { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */
+ { 0x09e8, 0x2424 }, /* nl  SYMBOL FOR NEWLINE */
+ { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */
+ { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */
+ { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */
+ { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
+ { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */
+ { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
+ { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
+ { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
+ { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */
+ { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
+ { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
+ { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+ { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+ { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+ { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+ { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */
+ { 0x0aa1, 0x2003 }, /* emspace EM SPACE */
+ { 0x0aa2, 0x2002 }, /* enspace EN SPACE */
+ { 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */
+ { 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */
+ { 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */
+ { 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */
+ { 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */
+ { 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */
+ { 0x0aa9, 0x2014 }, /* emdash — EM DASH */
+ { 0x0aaa, 0x2013 }, /* endash – EN DASH */
+/* 0x0aac signifblank ? ??? */
+ { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */
+ { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */
+ { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */
+ { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */
+ { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */
+ { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */
+ { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */
+ { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */
+ { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */
+ { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */
+ { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */
+ { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */
+ { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */
+/* 0x0abd decimalpoint ? ??? */
+ { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */
+/* 0x0abf marker ? ??? */
+ { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */
+ { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */
+ { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */
+ { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */
+ { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */
+ { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */
+/* 0x0acb trademarkincircle ? ??? */
+ { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */
+ { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */
+ { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */
+ { 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */
+ { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */
+ { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */
+ { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */
+ { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */
+ { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */
+ { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */
+ { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */
+ { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */
+/* 0x0ada hexagram ? ??? */
+ { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */
+ { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */
+ { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */
+ { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */
+ { 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */
+ { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */
+ { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */
+ { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */
+ { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */
+ { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */
+ { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */
+ { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */
+ { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */
+ { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */
+ { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */
+ { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */
+ { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */
+ { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */
+ { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */
+ { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */
+ { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */
+ { 0x0af1, 0x2020 }, /* dagger † DAGGER */
+ { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */
+ { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */
+ { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */
+ { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */
+ { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */
+ { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */
+ { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */
+ { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */
+ { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */
+ { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */
+ { 0x0afc, 0x2038 }, /* caret ‸ CARET */
+ { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */
+ { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */
+/* 0x0aff cursor ? ??? */
+ { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */
+ { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */
+ { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */
+ { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */
+ { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */
+ { 0x0bc2, 0x22a5 }, /* downtack ⊥ UP TACK */
+ { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */
+ { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */
+ { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */
+ { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */
+ { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */
+ { 0x0bce, 0x22a4 }, /* uptack ⊤ DOWN TACK */
+ { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */
+ { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */
+ { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */
+ { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */
+ { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */
+ { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */
+ { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */
+ { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */
+ { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */
+ { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */
+ { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */
+ { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */
+ { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */
+ { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */
+ { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */
+ { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */
+ { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */
+ { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */
+ { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */
+ { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */
+ { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */
+ { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */
+ { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */
+ { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */
+ { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */
+ { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */
+ { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */
+ { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */
+ { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */
+ { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */
+ { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */
+ { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */
+ { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */
+ { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */
+ { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */
+ { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */
+ { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */
+ { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */
+ { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */
+ { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */
+ { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */
+ { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */
+ { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */
+ { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */
+ { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */
+ { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */
+ { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */
+ { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */
+ { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */
+ { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */
+ { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */
+ { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */
+ { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */
+ { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */
+ { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */
+ { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */
+ { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */
+ { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */
+ { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */
+ { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */
+ { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */
+ { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */
+ { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */
+ { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */
+ { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */
+ { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */
+ { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */
+ { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */
+ { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */
+ { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */
+ { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */
+ { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */
+ { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */
+ { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */
+ { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */
+ { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */
+ { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */
+ { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */
+ { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */
+ { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */
+ { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */
+ { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */
+ { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */
+ { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */
+ { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */
+ { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */
+ { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */
+ { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */
+ { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */
+ { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */
+ { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */
+ { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */
+ { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */
+/* 0x0dde Thai_maihanakat_maitho ? ??? */
+ { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */
+ { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */
+ { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */
+ { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */
+ { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */
+ { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */
+ { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */
+ { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */
+ { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */
+ { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */
+ { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */
+ { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */
+ { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */
+ { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */
+ { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */
+ { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */
+ { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */
+ { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */
+ { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */
+ { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */
+ { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */
+ { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */
+ { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */
+ { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */
+ { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */
+ { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */
+ { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */
+ { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */
+ { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */
+ { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */
+ { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */
+ { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */
+ { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */
+ { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */
+ { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */
+ { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */
+ { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */
+ { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */
+ { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */
+ { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */
+ { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */
+ { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */
+ { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */
+ { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */
+ { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */
+ { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */
+ { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */
+ { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */
+ { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */
+ { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */
+ { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */
+ { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */
+ { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */
+ { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */
+ { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */
+ { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */
+ { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */
+ { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */
+ { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */
+ { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */
+ { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */
+ { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */
+ { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */
+ { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */
+ { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */
+ { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */
+ { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */
+ { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */
+ { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */
+ { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */
+ { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */
+ { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */
+ { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */
+ { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */
+ { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */
+ { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */
+ { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */
+ { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */
+ { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */
+ { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */
+ { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */
+ { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */
+ { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */
+ { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */
+ { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */
+ { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */
+ { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */
+ { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */
+ { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */
+ { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */
+ { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */
+ { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */
+ { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */
+ { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */
+ { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */
+ { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */
+ { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */
+ { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */
+ { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */
+ { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */
+ { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */
+ { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */
+ { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */
+ { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */
+ { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */
+ { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */
+ { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */
+ { 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */
+ { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */
+ { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */
+ { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */
+ { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */
+ { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */
+ { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */
+ { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */
+ { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */
+ { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */
+ { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */
+ { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */
+ { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */
+ { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */
+};
+
+VISIBLE
+long keysym2ucs(KeySym keysym)
+{
+ int min = 0;
+ int max = sizeof(keysymtab) / sizeof(struct codepair) - 1;
+ int mid;
+
+ /* first check for Latin-1 characters (1:1 mapping) */
+ if ((keysym >= 0x0020 && keysym <= 0x007e) ||
+ (keysym >= 0x00a0 && keysym <= 0x00ff))
+ return keysym;
+
+ /* also check for directly encoded 24-bit UCS characters */
+ if ((keysym & 0xff000000) == 0x01000000)
+ return keysym & 0x00ffffff;
+
+ /* binary search in table */
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (keysymtab[mid].keysym < keysym)
+ min = mid + 1;
+ else if (keysymtab[mid].keysym > keysym)
+ max = mid - 1;
+ else {
+ /* found it */
+ return keysymtab[mid].ucs;
+ }
+ }
+
+ /* no matching Unicode value found */
+ return -1;
+}
--- /dev/null
+++ b/limbo/NOTICE
@@ -1,0 +1,25 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+Copyright © 1995-1999 Lucent Technologies Inc.
+Portions Copyright © 1997-2000 Vita Nuova Limited
+Portions Copyright © 2000-2009 Vita Nuova Holdings Limited
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--- /dev/null
+++ b/limbo/asm.c
@@ -1,0 +1,289 @@
+#include "limbo.h"
+
+void
+asmentry(Decl *e)
+{
+ if(e == nil)
+ return;
+ Bprint(bout, "\tentry\t%ld, %d\n", e->pc->pc, e->desc->id);
+}
+
+void
+asmmod(Decl *m)
+{
+ Bprint(bout, "\tmodule\t");
+ Bprint(bout, "%s\n", m->sym->name);
+ for(m = m->ty->tof->ids; m != nil; m = m->next){
+ switch(m->store){
+ case Dglobal:
+ Bprint(bout, "\tlink\t-1,-1,0x%lux,\".mp\"\n", sign(m));
+ break;
+ case Dfn:
+ Bprint(bout, "\tlink\t%d,%ld,0x%lux,\"",
+ m->desc->id, m->pc->pc, sign(m));
+ if(m->dot->ty->kind == Tadt)
+ Bprint(bout, "%s.", m->dot->sym->name);
+ Bprint(bout, "%s\"\n", m->sym->name);
+ break;
+ }
+ }
+}
+
+#define NAMELEN 64
+
+void
+asmpath(void)
+{
+ char name[8*NAMELEN], *sp;
+
+ sp = srcpath(name, 8*NAMELEN);
+ Bprint(bout, "\tsource\t\"%s\"\n", sp);
+}
+
+void
+asmdesc(Desc *d)
+{
+ uchar *m, *e;
+
+ for(; d != nil; d = d->next){
+ Bprint(bout, "\tdesc\t$%d,%lud,\"", d->id, d->size);
+ e = d->map + d->nmap;
+ for(m = d->map; m < e; m++)
+ Bprint(bout, "%.2x", *m);
+ Bprint(bout, "\"\n");
+ }
+}
+
+void
+asmvar(long size, Decl *d)
+{
+ Bprint(bout, "\tvar\t@mp,%ld\n", size);
+
+ for(; d != nil; d = d->next)
+ if(d->store == Dglobal && d->init != nil)
+ asminitializer(d->offset, d->init);
+}
+
+void
+asmldt(long size, Decl *d)
+{
+ Bprint(bout, "\tldts\t@ldt,%ld\n", size);
+
+ for(; d != nil; d = d->next)
+ if(d->store == Dglobal && d->init != nil)
+ asminitializer(d->offset, d->init);
+}
+
+void
+asminitializer(long offset, Node *n)
+{
+ Node *elem, *wild;
+ Case *c;
+ Label *lab;
+ Decl *id;
+ ulong dv[2];
+ long e, last, esz, dotlen, idlen;
+ int i;
+
+ switch(n->ty->kind){
+ case Tbyte:
+ Bprint(bout, "\tbyte\t@mp+%ld,%ld\n", offset, (long)n->val & 0xff);
+ break;
+ case Tint:
+ case Tfix:
+ Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, (long)n->val);
+ break;
+ case Tbig:
+ Bprint(bout, "\tlong\t@mp+%ld,%lld # %.16llux\n", offset, n->val, n->val);
+ break;
+ case Tstring:
+ asmstring(offset, n->decl->sym);
+ break;
+ case Treal:
+ dtocanon(n->rval, dv);
+ Bprint(bout, "\treal\t@mp+%ld,%g # %.8lux%.8lux\n", offset, n->rval, dv[0], dv[1]);
+ break;
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ id = n->ty->ids;
+ for(n = n->left; n != nil; n = n->right){
+ asminitializer(offset + id->offset, n->left);
+ id = id->next;
+ }
+ break;
+ case Tcase:
+ c = n->ty->cse;
+ Bprint(bout, "\tword\t@mp+%ld,%d", offset, c->nlab);
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ Bprint(bout, ",%ld,%ld,%ld", (long)lab->start->val, (long)lab->stop->val+1, lab->inst->pc);
+ }
+ Bprint(bout, ",%ld\n", c->iwild ? c->iwild->pc : -1);
+ break;
+ case Tcasel:
+ c = n->ty->cse;
+ Bprint(bout, "\tword\t@mp+%ld,%d", offset, c->nlab);
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ Bprint(bout, ",%lld,%lld,%ld", lab->start->val, lab->stop->val+1, lab->inst->pc);
+ }
+ Bprint(bout, ",%ld\n", c->iwild ? c->iwild->pc : -1);
+ break;
+ case Tcasec:
+ c = n->ty->cse;
+ Bprint(bout, "\tword\t@mp+%ld,%d\n", offset, c->nlab);
+ offset += IBY2WD;
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ asmstring(offset, lab->start->decl->sym);
+ offset += IBY2WD;
+ if(lab->stop != lab->start)
+ asmstring(offset, lab->stop->decl->sym);
+ offset += IBY2WD;
+ Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, lab->inst->pc);
+ offset += IBY2WD;
+ }
+ Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, c->iwild ? c->iwild->pc : -1);
+ break;
+ case Tgoto:
+ c = n->ty->cse;
+ Bprint(bout, "\tword\t@mp+%ld", offset);
+ Bprint(bout, ",%ld", n->ty->size/IBY2WD-1);
+ for(i = 0; i < c->nlab; i++)
+ Bprint(bout, ",%ld", c->labs[i].inst->pc);
+ if(c->iwild != nil)
+ Bprint(bout, ",%ld", c->iwild->pc);
+ Bprint(bout, "\n");
+ break;
+ case Tany:
+ break;
+ case Tarray:
+ Bprint(bout, "\tarray\t@mp+%ld,$%d,%ld\n", offset, n->ty->tof->decl->desc->id, (long)n->left->val);
+ if(n->right == nil)
+ break;
+ Bprint(bout, "\tindir\t@mp+%ld,0\n", offset);
+ c = n->right->ty->cse;
+ wild = nil;
+ if(c->wild != nil)
+ wild = c->wild->right;
+ last = 0;
+ esz = n->ty->tof->size;
+ for(i = 0; i < c->nlab; i++){
+ e = c->labs[i].start->val;
+ if(wild != nil){
+ for(; last < e; last++)
+ asminitializer(esz * last, wild);
+ }
+ last = e;
+ e = c->labs[i].stop->val;
+ elem = c->labs[i].node->right;
+ for(; last <= e; last++)
+ asminitializer(esz * last, elem);
+ }
+ if(wild != nil)
+ for(e = n->left->val; last < e; last++)
+ asminitializer(esz * last, wild);
+ Bprint(bout, "\tapop\n");
+ break;
+ case Tiface:
+ if(LDT)
+ Bprint(bout, "\tword\t@ldt+%ld,%ld\n", offset, (long)n->val);
+ else
+ Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, (long)n->val);
+ offset += IBY2WD;
+ for(id = n->decl->ty->ids; id != nil; id = id->next){
+ offset = align(offset, IBY2WD);
+ if(LDT)
+ Bprint(bout, "\text\t@ldt+%ld,0x%lux,\"", offset, sign(id));
+ else
+ Bprint(bout, "\text\t@mp+%ld,0x%lux,\"", offset, sign(id));
+ dotlen = 0;
+ idlen = id->sym->len + 1;
+ if(id->dot->ty->kind == Tadt){
+ dotlen = id->dot->sym->len + 1;
+ Bprint(bout, "%s.", id->dot->sym->name);
+ }
+ Bprint(bout, "%s\"\n", id->sym->name);
+ offset += idlen + dotlen + IBY2WD;
+ }
+ break;
+ default:
+ nerror(n, "can't asm global %n", n);
+ break;
+ }
+}
+
+void
+asmexc(Except *es)
+{
+ int i, o, n, id;
+ Decl *d;
+ Except *e;
+ Case *c;
+ Label *lab;
+
+ n = 0;
+ for(e = es; e != nil; e = e->next)
+ n++;
+ Bprint(bout, "\texceptions\t%d\n", n);
+ for(e = es; e != nil; e = e->next){
+ if(!e->p1->reach && !e->p2->reach)
+ continue;
+ c = e->c;
+ o = e->d->offset;
+ if(e->desc != nil)
+ id = e->desc->id;
+ else
+ id = -1;
+ Bprint(bout, "\texception\t%ld, %ld, %d, %d, %d, %d\n", getpc(e->p1), getpc(e->p2), o, id, c->nlab, e->ne);
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ d = lab->start->decl;
+ if(lab->start->ty->kind == Texception)
+ d = d->init->decl;
+ Bprint(bout, "\texctab\t\"%s\", %ld\n", d->sym->name, lab->inst->pc);
+ }
+ if(c->iwild == nil)
+ Bprint(bout, "\texctab\t*, %d\n", -1);
+ else
+ Bprint(bout, "\texctab\t*, %ld\n", c->iwild->pc);
+ }
+}
+
+void
+asmstring(long offset, Sym *sym)
+{
+ char *s, *se;
+ int c;
+
+ Bprint(bout, "\tstring\t@mp+%ld,\"", offset);
+ s = sym->name;
+ se = s + sym->len;
+ for(; s < se; s++){
+ c = *s;
+ if(c == '\n')
+ Bwrite(bout, "\\n", 2);
+ else if(c == '\0')
+ Bwrite(bout, "\\z", 2);
+ else if(c == '"')
+ Bwrite(bout, "\\\"", 2);
+ else if(c == '\\')
+ Bwrite(bout, "\\\\", 2);
+ else
+ Bputc(bout, c);
+ }
+ Bprint(bout, "\"\n");
+}
+
+void
+asminst(Inst *in)
+{
+ for(; in != nil; in = in->next){
+ if(in->op == INOOP)
+ continue;
+ if(in->pc % 10 == 0)
+ Bprint(bout, "#%ld\n", in->pc);
+ Bprint(bout, "%I\n", in);
+ }
+}
--- /dev/null
+++ b/limbo/com.c
@@ -1,0 +1,1510 @@
+#include "limbo.h"
+
+static Inst **breaks;
+static Inst **conts;
+static Decl **labels;
+static Node **bcscps;
+static int labdep;
+static Inst nocont;
+
+static int scp;
+static Node *scps[MaxScope];
+
+static int trcom(Node*, Node*, int);
+
+static void
+pushscp(Node *n)
+{
+ if (scp >= MaxScope)
+ fatal("scope too deep");
+ scps[scp++] = n;
+}
+
+static void
+popscp(void)
+{
+ scp--;
+}
+
+static Node *
+curscp(void)
+{
+ if (scp == 0)
+ return nil;
+ return scps[scp-1];
+}
+
+static void
+zeroscopes(Node *stop)
+{
+ int i;
+ Node *cs;
+
+ for (i = scp-1; i >= 0; i--) {
+ cs = scps[i];
+ if (cs == stop)
+ break;
+ zcom(cs->left, nil);
+ }
+}
+
+static void
+zeroallscopes(Node *n, Node **nn)
+{
+ if(n == nil)
+ return;
+ for(; n != nil; n = n->right){
+ switch(n->op){
+ case Oscope:
+ zeroallscopes(n->right, nn);
+ zcom(n->left, nn);
+ return;
+ case Olabel:
+ case Odo:
+ zeroallscopes(n->right, nn);
+ return;
+ case Oif:
+ case Ofor:
+ zeroallscopes(n->right->left, nn);
+ zeroallscopes(n->right->right, nn);
+ return;
+ case Oalt:
+ case Ocase:
+ case Opick:
+ case Oexcept:
+ for(n = n->right; n != nil; n = n->right)
+ zeroallscopes(n->left->right, nn);
+ return;
+ case Oseq:
+ zeroallscopes(n->left, nn);
+ break;
+ case Oexstmt:
+ zeroallscopes(n->left, nn);
+ zeroallscopes(n->right, nn);
+ return;
+ default:
+ return;
+ }
+ }
+}
+
+static Except *excs;
+
+static void
+installexc(Node *en, Inst *p1, Inst *p2, Node *zn)
+{
+ int i, ne;
+ Except *e;
+ Case *c;
+ Label *lab;
+
+ e = allocmem(sizeof(Except));
+ e->p1 = p1;
+ e->p2 = p2;
+ e->c = en->ty->cse;
+ e->d = en->left->decl;
+ e->zn = zn;
+ e->desc = nil;
+ e->next = excs;
+ excs = e;
+
+ ne = 0;
+ c = e->c;
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ if(lab->start->ty->kind == Texception)
+ ne++;
+ }
+ e->ne = ne;
+}
+
+static int
+inlist(Decl *d, Decl *dd)
+{
+ for( ; dd != nil; dd = dd->next)
+ if(d == dd)
+ return 1;
+ return 0;
+}
+
+static void
+excdesc(void)
+{
+ ulong o, maxo;
+ Except *e;
+ Node *n;
+ Decl *d, *dd, *nd;
+
+ for(e = excs; e != nil; e = e->next){
+ if(e->zn != nil){
+ /* set up a decl list for gendesc */
+ dd = nil;
+ maxo = 0;
+ for(n = e->zn ; n != nil; n = n->right){
+ d = n->decl;
+ d->locals = d->next;
+ if(!inlist(d, dd)){
+ d->next = dd;
+ dd = d;
+ o = d->offset+d->ty->size;
+ if(o > maxo)
+ maxo = o;
+ }
+ }
+ e->desc = gendesc(e->d, align(maxo, MaxAlign), dd);
+ for(d = dd; d != nil; d = nd){
+ nd = d->next;
+ d->next = d->locals;
+ d->locals = nil;
+ }
+ e->zn = nil;
+ }
+ }
+}
+
+static Except*
+reve(Except *e)
+{
+ Except *l, *n;
+
+ l = nil;
+ for( ; e != nil; e = n){
+ n = e->next;
+ e->next = l;
+ l = e;
+ }
+ return l;
+}
+
+static int
+ckinline0(Node *n, Decl *d)
+{
+ Decl *dd;
+
+ if(n == nil)
+ return 1;
+ if(n->op == Oname){
+ dd = n->decl;
+ if(d == dd)
+ return 0;
+ if(dd->caninline == 1)
+ return ckinline0(dd->init->right, d);
+ return 1;
+ }
+ return ckinline0(n->left, d) && ckinline0(n->right, d);
+}
+
+static void
+ckinline(Decl *d)
+{
+ d->caninline = ckinline0(d->init->right, d);
+}
+
+void
+modcom(Decl *entry)
+{
+ Decl *globals, *m, *nils, *d, *ldts;
+ long ninst, ndata, ndesc, nlink, offset, ldtoff;
+ int ok, i, hints;
+ Dlist *dl;
+
+ if(errors)
+ return;
+
+ if(emitcode || emitstub || emittab != nil){
+ emit(curscope());
+ popscope();
+ return;
+ }
+
+ /*
+ * scom introduces global variables for case statements
+ * and unaddressable constants, so it must be done before
+ * popping the global scope
+ */
+ nlabel = 0;
+ maxstack = MaxTemp;
+ genstart();
+
+ for(i = 0; i < nfns; i++)
+ if(fns[i]->caninline == 1)
+ ckinline(fns[i]);
+
+ ok = 0;
+ for(i = 0; i < nfns; i++){
+ d = fns[i];
+if(debug['v']) print("fncom: %s %d %p\n", d->sym->name, d->refs, d);
+ if(d->refs > 1 && !(d->caninline == 1 && local(d) && d->iface == nil)){
+ fns[ok++] = d;
+ fncom(d);
+ }
+ }
+ nfns = ok;
+ if(blocks != -1)
+ fatal("blocks not nested correctly");
+ firstinst = firstinst->next;
+ if(errors)
+ return;
+
+ globals = popscope();
+ checkrefs(globals);
+ if(errors)
+ return;
+ globals = vars(globals);
+ moddataref();
+
+ nils = popscope();
+ m = nil;
+ for(d = nils; d != nil; d = d->next){
+ if(debug['n'])
+ print("nil '%s' ref %d\n", d->sym->name, d->refs);
+ if(d->refs && m == nil)
+ m = dupdecl(d);
+ d->offset = 0;
+ }
+ globals = appdecls(m, globals);
+ globals = namesort(globals);
+ globals = modglobals(impdecls->d, globals);
+ vcom(globals);
+ narrowmods();
+ ldts = nil;
+ if(LDT)
+ globals = resolveldts(globals, &ldts);
+ offset = idoffsets(globals, 0, IBY2WD);
+ if(LDT)
+ ldtoff = idindices(ldts); /* ldtoff = idoffsets(ldts, 0, IBY2WD); */
+ for(d = nils; d != nil; d = d->next){
+ if(debug['n'])
+ print("nil '%s' ref %d\n", d->sym->name, d->refs);
+ if(d->refs)
+ d->offset = m->offset;
+ }
+
+ if(debug['g']){
+ print("globals:\n");
+ printdecls(globals);
+ }
+
+ ndata = 0;
+ for(d = globals; d != nil; d = d->next)
+ ndata++;
+ ndesc = resolvedesc(impdecls->d, offset, globals);
+ ninst = resolvepcs(firstinst);
+ modresolve();
+ if(impdecls->next != nil)
+ for(dl = impdecls; dl != nil; dl = dl->next)
+ resolvemod(dl->d);
+ nlink = resolvemod(impdecl);
+
+ maxstack *= 10;
+ if(fixss != 0)
+ maxstack = fixss;
+
+ if(debug['s'])
+ print("%ld instructions\n%ld data elements\n%ld type descriptors\n%ld functions exported\n%ld stack size\n",
+ ninst, ndata, ndesc, nlink, maxstack);
+
+ excs = reve(excs);
+
+ if(gendis){
+ discon(XMAGIC);
+ hints = 0;
+ if(mustcompile)
+ hints |= MUSTCOMPILE;
+ if(dontcompile)
+ hints |= DONTCOMPILE;
+ if(LDT)
+ hints |= HASLDT;
+ if(excs != nil)
+ hints |= HASEXCEPT;
+ discon(hints); /* runtime hints */
+ discon(maxstack); /* minimum stack extent size */
+ discon(ninst);
+ discon(offset);
+ discon(ndesc);
+ discon(nlink);
+ disentry(entry);
+ disinst(firstinst);
+ disdesc(descriptors);
+ disvar(offset, globals);
+ dismod(impdecl);
+ if(LDT)
+ disldt(ldtoff, ldts);
+ if(excs != nil)
+ disexc(excs);
+ dispath();
+ }else{
+ asminst(firstinst);
+ asmentry(entry);
+ asmdesc(descriptors);
+ asmvar(offset, globals);
+ asmmod(impdecl);
+ if(LDT)
+ asmldt(ldtoff, ldts);
+ if(excs != nil)
+ asmexc(excs);
+ asmpath();
+ }
+ if(bsym != nil){
+ sblmod(impdecl);
+
+ sblfiles();
+ sblinst(firstinst, ninst);
+ sblty(adts, nadts);
+ sblfn(fns, nfns);
+ sblvar(globals);
+ }
+
+ firstinst = nil;
+ lastinst = nil;
+
+ excs = nil;
+}
+
+void
+fncom(Decl *decl)
+{
+ Src src;
+ Node *n;
+ Decl *loc, *last;
+ Inst *in;
+ int valued;
+
+ curfn = decl;
+ if(ispoly(decl))
+ addfnptrs(decl, 1);
+
+ /*
+ * pick up the function body and compile it
+ * this code tries to clean up the parse nodes as fast as possible
+ * function is Ofunc(name, body)
+ */
+ decl->pc = nextinst();
+ tinit();
+ labdep = 0;
+ scp = 0;
+ breaks = allocmem(maxlabdep * sizeof breaks[0]);
+ conts = allocmem(maxlabdep * sizeof conts[0]);
+ labels = allocmem(maxlabdep * sizeof labels[0]);
+ bcscps = allocmem(maxlabdep * sizeof bcscps[0]);
+ n = decl->init;
+ if(decl->caninline == 1)
+ decl->init = dupn(0, nil, n);
+ else
+ decl->init = n->left;
+ src = n->right->src;
+ src.start.line = src.stop.line;
+ src.start.pos = src.stop.pos - 1;
+ for(n = n->right; n != nil; n = n->right){
+ if(n->op != Oseq){
+ if(n->op == Ocall && trcom(n, nil, 1))
+ break;
+ scom(n);
+ break;
+ }
+ if(n->left->op == Ocall && trcom(n->left, n->right, 1)){
+ n = n->right;
+ if(n == nil || n->op != Oseq)
+ break;
+ }
+ else
+ scom(n->left);
+ }
+ pushblock();
+ valued = decl->ty->tof != tnone;
+ in = genrawop(&src, valued? IRAISE: IRET, nil, nil, nil);
+ popblock();
+ reach(decl->pc);
+ if(valued && in->reach)
+ error(src.start, "no return at end of function %D", decl);
+ /* decl->endpc = lastinst; */
+ if(labdep != 0)
+ fatal("unbalanced label stack");
+ free(breaks);
+ free(conts);
+ free(labels);
+ free(bcscps);
+
+ loc = declsort(appdecls(vars(decl->locals), tdecls()));
+ decl->offset = idoffsets(loc, decl->offset, MaxAlign);
+ for(last = decl->ty->ids; last != nil && last->next != nil; last = last->next)
+ ;
+ if(last != nil)
+ last->next = loc;
+ else
+ decl->ty->ids = loc;
+
+ if(debug['f']){
+ print("fn: %s\n", decl->sym->name);
+ printdecls(decl->ty->ids);
+ }
+
+ decl->desc = gendesc(decl, decl->offset, decl->ty->ids);
+ decl->locals = loc;
+ excdesc();
+ if(decl->offset > maxstack)
+ maxstack = decl->offset;
+ if(optims)
+ optim(decl->pc, decl);
+ if(last != nil)
+ last->next = nil;
+ else
+ decl->ty->ids = nil;
+}
+
+/*
+ * statement compiler
+ */
+void
+scom(Node *n)
+{
+ Inst *p, *pp, *p1, *p2, *p3;
+ Node tret, *left, *zn;
+
+ for(; n != nil; n = n->right){
+ switch(n->op){
+ case Ocondecl:
+ case Otypedecl:
+ case Ovardecl:
+ case Oimport:
+ case Oexdecl:
+ return;
+ case Ovardecli:
+ break;
+ case Oscope:
+ pushscp(n);
+ scom(n->right);
+ popscp();
+ zcom(n->left, nil);
+ return;
+ case Olabel:
+ scom(n->right);
+ return;
+ case Oif:
+ pushblock();
+ left = simplify(n->left);
+ if(left->op == Oconst && left->ty == tint){
+ if(left->val != 0)
+ scom(n->right->left);
+ else
+ scom(n->right->right);
+ popblock();
+ return;
+ }
+ sumark(left);
+ pushblock();
+ p = bcom(left, 1, nil);
+ tfreenow();
+ popblock();
+ scom(n->right->left);
+ if(n->right->right != nil){
+ pp = p;
+ p = genrawop(&lastinst->src, IJMP, nil, nil, nil);
+ patch(pp, nextinst());
+ scom(n->right->right);
+ }
+ patch(p, nextinst());
+ popblock();
+ return;
+ case Ofor:
+ n->left = left = simplify(n->left);
+ if(left->op == Oconst && left->ty == tint){
+ if(left->val == 0)
+ return;
+ left->op = Onothing;
+ left->ty = tnone;
+ left->decl = nil;
+ }
+ pp = nextinst();
+ pushblock();
+ /* b = pushblock(); */
+ sumark(left);
+ p = bcom(left, 1, nil);
+ tfreenow();
+ popblock();
+
+ if(labdep >= maxlabdep)
+ fatal("label stack overflow");
+ breaks[labdep] = nil;
+ conts[labdep] = nil;
+ labels[labdep] = n->decl;
+ bcscps[labdep] = curscp();
+ labdep++;
+ scom(n->right->left);
+ labdep--;
+
+ patch(conts[labdep], nextinst());
+ if(n->right->right != nil){
+ pushblock();
+ scom(n->right->right);
+ popblock();
+ }
+ repushblock(lastinst->block); /* was b */
+ patch(genrawop(&lastinst->src, IJMP, nil, nil, nil), pp); /* for cprof: was &left->src */
+ popblock();
+ patch(p, nextinst());
+ patch(breaks[labdep], nextinst());
+ return;
+ case Odo:
+ pp = nextinst();
+
+ if(labdep >= maxlabdep)
+ fatal("label stack overflow");
+ breaks[labdep] = nil;
+ conts[labdep] = nil;
+ labels[labdep] = n->decl;
+ bcscps[labdep] = curscp();
+ labdep++;
+ scom(n->right);
+ labdep--;
+
+ patch(conts[labdep], nextinst());
+
+ left = simplify(n->left);
+ if(left->op == Onothing
+ || left->op == Oconst && left->ty == tint){
+ if(left->op == Onothing || left->val != 0){
+ pushblock();
+ p = genrawop(&left->src, IJMP, nil, nil, nil);
+ popblock();
+ }else
+ p = nil;
+ }else{
+ pushblock();
+ p = bcom(sumark(left), 0, nil);
+ tfreenow();
+ popblock();
+ }
+ patch(p, pp);
+ patch(breaks[labdep], nextinst());
+ return;
+ case Oalt:
+ case Ocase:
+ case Opick:
+ case Oexcept:
+/* need push/pop blocks for alt guards */
+ pushblock();
+ if(labdep >= maxlabdep)
+ fatal("label stack overflow");
+ breaks[labdep] = nil;
+ conts[labdep] = &nocont;
+ labels[labdep] = n->decl;
+ bcscps[labdep] = curscp();
+ labdep++;
+ switch(n->op){
+ case Oalt:
+ altcom(n);
+ break;
+ case Ocase:
+ case Opick:
+ casecom(n);
+ break;
+ case Oexcept:
+ excom(n);
+ break;
+ }
+ labdep--;
+ patch(breaks[labdep], nextinst());
+ popblock();
+ return;
+ case Obreak:
+ pushblock();
+ bccom(n, breaks);
+ popblock();
+ break;
+ case Ocont:
+ pushblock();
+ bccom(n, conts);
+ popblock();
+ break;
+ case Oseq:
+ if(n->left->op == Ocall && trcom(n->left, n->right, 0)){
+ n = n->right;
+ if(n == nil || n->op != Oseq)
+ return;
+ }
+ else
+ scom(n->left);
+ break;
+ case Oret:
+ if(n->left != nil && n->left->op == Ocall && trcom(n->left, nil, 1))
+ return;
+ pushblock();
+ if(n->left != nil){
+ n->left = simplify(n->left);
+ sumark(n->left);
+ ecom(&n->left->src, retalloc(&tret, n->left), n->left);
+ tfreenow();
+ }
+ genrawop(&n->src, IRET, nil, nil, nil);
+ popblock();
+ return;
+ case Oexit:
+ pushblock();
+ genrawop(&n->src, IEXIT, nil, nil, nil);
+ popblock();
+ return;
+ case Onothing:
+ return;
+ case Ofunc:
+ fatal("Ofunc");
+ return;
+ case Oexstmt:
+ pushblock();
+ pp = genrawop(&n->right->src, IEXC0, nil, nil, nil); /* marker */
+ p1 = nextinst();
+ scom(n->left);
+ p2 = nextinst();
+ p3 = genrawop(&n->right->src, IJMP, nil, nil, nil);
+ p = genrawop(&n->right->src, IEXC, nil, nil, nil); /* marker */
+ p->d.decl = mkdecl(&n->src, 0, n->right->ty);
+ zn = nil;
+ zeroallscopes(n->left, &zn);
+ scom(n->right);
+ patch(p3, nextinst());
+ installexc(n->right, p1, p2, zn);
+ patch(pp, p);
+ popblock();
+ return;
+ default:
+ pushblock();
+ n = simplify(n);
+ sumark(n);
+ ecom(&n->src, nil, n);
+ tfreenow();
+ popblock();
+ return;
+ }
+ }
+}
+
+/*
+ * compile a break, continue
+ */
+void
+bccom(Node *n, Inst **bs)
+{
+ Sym *s;
+ Inst *p;
+ int i, ok;
+
+ s = nil;
+ if(n->decl != nil)
+ s = n->decl->sym;
+ ok = -1;
+ for(i = 0; i < labdep; i++){
+ if(bs[i] == &nocont)
+ continue;
+ if(s == nil || labels[i] != nil && labels[i]->sym == s)
+ ok = i;
+ }
+ if(ok < 0){
+ nerror(n, "no appropriate target for %V", n);
+ return;
+ }
+ zeroscopes(bcscps[ok]);
+ p = genrawop(&n->src, IJMP, nil, nil, nil);
+ p->branch = bs[ok];
+ bs[ok] = p;
+}
+
+static int
+dogoto(Case *c)
+{
+ int i, j, k, n, r, q, v;
+ Label *l, *nl;
+ Src *src;
+
+ l = c->labs;
+ n = c->nlab;
+ if(n == 0)
+ return 0;
+ r = l[n-1].stop->val - l[0].start->val+1;
+ if(r >= 3 && r <= 3*n){
+ if(r != n){
+ /* remove ranges, fill in gaps */
+ c->nlab = r;
+ nl = c->labs = allocmem(r*sizeof(*nl));
+ k = 0;
+ v = l[0].start->val-1;
+ for(i = 0; i < n; i++){
+ /* p = l[i].start->val; */
+ q = l[i].stop->val;
+ src = &l[i].start->src;
+ for(j = v+1; j <= q; j++){
+ nl[k] = l[i];
+ nl[k].start = nl[k].stop = mkconst(src, j);
+ k++;
+ }
+ v = q;
+ }
+ if(k != r)
+ fatal("bad case expansion");
+ }
+ l = c->labs;
+ for(i = 0; i < r; i++)
+ l[i].inst = nil;
+ return 1;
+ }
+ return 0;
+}
+
+static void
+fillrange(Case *c, Node *nn, Inst *in)
+{
+ int i, j, n, p, q;
+ Label *l;
+
+ l = c->labs;
+ n = c->nlab;
+ p = nn->left->val;
+ q = nn->right->val;
+ for(i = 0; i < n; i++)
+ if(l[i].start->val == p)
+ break;
+ if(i == n)
+ fatal("fillrange fails");
+ for(j = p; j <= q; j++)
+ l[i++].inst = in;
+}
+
+static int
+nconstqual(Node *s1)
+{
+ Node *s2;
+ int n;
+
+ n = 0;
+ for(; s1 != nil; s1 = s1->right){
+ for(s2 = s1->left->left; s2 != nil; s2 = s2->right)
+ if(s2->left->op == Oconst)
+ n++;
+ }
+ return n;
+}
+
+void
+casecom(Node *cn)
+{
+ Src *src;
+ Case *c;
+ Decl *d;
+ Type *ctype;
+ Inst *j, *jmps, *wild, *k, *j1, *j2;
+ Node *n, *p, *left, tmp, nto, tmpc;
+ Label *labs;
+ char buf[32];
+ int i, nlab, op, needwild, igoto;
+
+ c = cn->ty->cse;
+
+ needwild = cn->op != Opick || nconstqual(cn->right) != cn->left->right->ty->tof->decl->tag;
+ igoto = cn->left->ty == tint && dogoto(c);
+ j1 = j2 = nil;
+
+ /*
+ * generate global which has case labels
+ */
+ if(igoto){
+ seprint(buf, buf+sizeof(buf), ".g%d", nlabel++);
+ cn->ty->kind = Tgoto;
+ }
+ else
+ seprint(buf, buf+sizeof(buf), ".c%d", nlabel++);
+ d = mkids(&cn->src, enter(buf, 0), cn->ty, nil);
+ d->init = mkdeclname(&cn->src, d);
+
+ nto.addable = Rmreg;
+ nto.left = nil;
+ nto.right = nil;
+ nto.op = Oname;
+ nto.ty = d->ty;
+ nto.decl = d;
+
+ tmp.decl = tmpc.decl = nil;
+ left = cn->left;
+ left = simplify(left);
+ cn->left = left;
+ sumark(left);
+ if(debug['c'])
+ print("case %n\n", left);
+ ctype = cn->left->ty;
+ if(left->addable >= Rcant){
+ if(cn->op == Opick){
+ ecom(&left->src, nil, left);
+ tfreenow();
+ left = mkunary(Oind, dupn(1, &left->src, left->left));
+ left->ty = tint;
+ sumark(left);
+ ctype = tint;
+ }else{
+ left = eacom(left, &tmp, nil);
+ tfreenow();
+ }
+ }
+
+ labs = c->labs;
+ nlab = c->nlab;
+
+ if(igoto){
+ if(labs[0].start->val != 0){
+ talloc(&tmpc, left->ty, nil);
+ if(left->addable == Radr || left->addable == Rmadr){
+ genrawop(&left->src, IMOVW, left, nil, &tmpc);
+ left = &tmpc;
+ }
+ genrawop(&left->src, ISUBW, sumark(labs[0].start), left, &tmpc);
+ left = &tmpc;
+ }
+ if(needwild){
+ j1 = genrawop(&left->src, IBLTW, left, sumark(mkconst(&left->src, 0)), nil);
+ j2 = genrawop(&left->src, IBGTW, left, sumark(mkconst(&left->src, labs[nlab-1].start->val-labs[0].start->val)), nil);
+ }
+ j = nextinst();
+ genrawop(&left->src, IGOTO, left, nil, &nto);
+ j->d.reg = IBY2WD;
+ }
+ else{
+ op = ICASE;
+ if(ctype == tbig)
+ op = ICASEL;
+ else if(ctype == tstring)
+ op = ICASEC;
+ genrawop(&left->src, op, left, nil, &nto);
+ }
+ tfree(&tmp);
+ tfree(&tmpc);
+
+ jmps = nil;
+ wild = nil;
+ for(n = cn->right; n != nil; n = n->right){
+ j = nextinst();
+ for(p = n->left->left; p != nil; p = p->right){
+ if(debug['c'])
+ print("case qualifier %n\n", p->left);
+ switch(p->left->op){
+ case Oconst:
+ labs[findlab(ctype, p->left, labs, nlab)].inst = j;
+ break;
+ case Orange:
+ labs[findlab(ctype, p->left->left, labs, nlab)].inst = j;
+ if(igoto)
+ fillrange(c, p->left, j);
+ break;
+ case Owild:
+ if(needwild)
+ wild = j;
+/*
+ else
+ nwarn(p->left, "default case redundant");
+*/
+ break;
+ }
+ }
+
+ if(debug['c'])
+ print("case body for %V: %n\n", n->left->left, n->left->right);
+
+ k = nextinst();
+ scom(n->left->right);
+
+ src = &lastinst->src;
+ // if(n->left->right == nil || n->left->right->op == Onothing)
+ if(k == nextinst())
+ src = &n->left->left->src;
+ j = genrawop(src, IJMP, nil, nil, nil);
+ j->branch = jmps;
+ jmps = j;
+ }
+ patch(jmps, nextinst());
+ if(wild == nil && needwild)
+ wild = nextinst();
+
+ if(igoto){
+ if(needwild){
+ patch(j1, wild);
+ patch(j2, wild);
+ }
+ for(i = 0; i < nlab; i++)
+ if(labs[i].inst == nil)
+ labs[i].inst = wild;
+ }
+
+ c->iwild = wild;
+
+ d->ty->cse = c;
+ usetype(d->ty);
+ installids(Dglobal, d);
+}
+
+void
+altcom(Node *nalt)
+{
+ Src altsrc;
+ Case *c;
+ Decl *d;
+ Type *talt;
+ Node *n, *p, *left, tab, slot, off, add, which, nto, adr;
+ Node **comm, *op, *tmps;
+ Inst *j, *tj, *jmps, *me, *wild;
+ Label *labs;
+ char buf[32];
+ int i, is, ir, nlab, nsnd, altop, isptr;
+ Inst *pp;
+
+ talt = nalt->ty;
+ c = talt->cse;
+ nlab = c->nlab;
+ nsnd = c->nsnd;
+ comm = allocmem(nlab * sizeof *comm);
+ labs = allocmem(nlab * sizeof *labs);
+ tmps = allocmem(nlab * sizeof *tmps);
+ c->labs = labs;
+
+ /*
+ * built the type of the alt channel table
+ * note that we lie to the garbage collector
+ * if we know that another reference exists for the channel
+ */
+ is = 0;
+ ir = nsnd;
+ i = 0;
+ for(n = nalt->left; n != nil; n = n->right){
+ for(p = n->left->right->left; p != nil; p = p->right){
+ left = simplify(p->left);
+ p->left = left;
+ if(left->op == Owild)
+ continue;
+ comm[i] = hascomm(left);
+ left = comm[i]->left;
+ sumark(left);
+ isptr = left->addable >= Rcant;
+ if(comm[i]->op == Osnd)
+ labs[is++].isptr = isptr;
+ else
+ labs[ir++].isptr = isptr;
+ i++;
+ }
+ }
+
+ talloc(&which, tint, nil);
+ talloc(&tab, talt, nil);
+
+ /*
+ * build the node for the address of each channel,
+ * the values to send, and the storage fro values received
+ */
+ off = znode;
+ off.op = Oconst;
+ off.ty = tint;
+ off.addable = Rconst;
+ adr = znode;
+ adr.op = Oadr;
+ adr.left = &tab;
+ adr.ty = tint;
+ add = znode;
+ add.op = Oadd;
+ add.left = &adr;
+ add.right = &off;
+ add.ty = tint;
+ slot = znode;
+ slot.op = Oind;
+ slot.left = &add;
+ sumark(&slot);
+
+ /*
+ * compile the sending and receiving channels and values
+ */
+ is = 2*IBY2WD;
+ ir = is + nsnd*2*IBY2WD;
+ i = 0;
+ for(n = nalt->left; n != nil; n = n->right){
+ for(p = n->left->right->left; p != nil; p = p->right){
+ if(p->left->op == Owild)
+ continue;
+
+ /*
+ * gen channel
+ */
+ op = comm[i];
+ if(op->op == Osnd){
+ off.val = is;
+ is += 2*IBY2WD;
+ }else{
+ off.val = ir;
+ ir += 2*IBY2WD;
+ }
+ left = op->left;
+
+ /*
+ * this sleaze is lying to the garbage collector
+ */
+ if(left->addable < Rcant)
+ genmove(&left->src, Mas, tint, left, &slot);
+ else{
+ slot.ty = left->ty;
+ ecom(&left->src, &slot, left);
+ tfreenow();
+ slot.ty = nil;
+ }
+
+ /*
+ * gen value
+ */
+ off.val += IBY2WD;
+ tmps[i].decl = nil;
+ p->left = rewritecomm(p->left, comm[i], &tmps[i], &slot);
+
+ i++;
+ }
+ }
+
+ /*
+ * stuff the number of send & receive channels into the table
+ */
+ altsrc = nalt->src;
+ altsrc.stop.pos += 3;
+ off.val = 0;
+ genmove(&altsrc, Mas, tint, sumark(mkconst(&altsrc, nsnd)), &slot);
+ off.val += IBY2WD;
+ genmove(&altsrc, Mas, tint, sumark(mkconst(&altsrc, nlab-nsnd)), &slot);
+ off.val += IBY2WD;
+
+ altop = IALT;
+ if(c->wild != nil)
+ altop = INBALT;
+ pp = genrawop(&altsrc, altop, &tab, nil, &which);
+ pp->m.offset = talt->size; /* for optimizer */
+
+ seprint(buf, buf+sizeof(buf), ".g%d", nlabel++);
+ d = mkids(&nalt->src, enter(buf, 0), mktype(&nalt->src.start, &nalt->src.stop, Tgoto, nil, nil), nil);
+ d->ty->cse = c;
+ d->init = mkdeclname(&nalt->src, d);
+
+ nto.addable = Rmreg;
+ nto.left = nil;
+ nto.right = nil;
+ nto.op = Oname;
+ nto.decl = d;
+ nto.ty = d->ty;
+
+ me = nextinst();
+ genrawop(&altsrc, IGOTO, &which, nil, &nto);
+ me->d.reg = IBY2WD; /* skip the number of cases field */
+ tfree(&tab);
+ tfree(&which);
+
+ /*
+ * compile the guard expressions and bodies
+ */
+ i = 0;
+ is = 0;
+ ir = nsnd;
+ jmps = nil;
+ wild = nil;
+ for(n = nalt->left; n != nil; n = n->right){
+ j = nil;
+ for(p = n->left->right->left; p != nil; p = p->right){
+ tj = nextinst();
+ if(p->left->op == Owild){
+ wild = nextinst();
+ }else{
+ if(comm[i]->op == Osnd)
+ labs[is++].inst = tj;
+ else{
+ labs[ir++].inst = tj;
+ tacquire(&tmps[i]);
+ }
+ sumark(p->left);
+ if(debug['a'])
+ print("alt guard %n\n", p->left);
+ ecom(&p->left->src, nil, p->left);
+ tfree(&tmps[i]);
+ tfreenow();
+ i++;
+ }
+ if(p->right != nil){
+ tj = genrawop(&lastinst->src, IJMP, nil, nil, nil);
+ tj->branch = j;
+ j = tj;
+ }
+ }
+
+ patch(j, nextinst());
+ if(debug['a'])
+ print("alt body %n\n", n->left->right);
+ scom(n->left);
+
+ j = genrawop(&lastinst->src, IJMP, nil, nil, nil);
+ j->branch = jmps;
+ jmps = j;
+ }
+ patch(jmps, nextinst());
+ free(comm);
+
+ c->iwild = wild;
+
+ usetype(d->ty);
+ installids(Dglobal, d);
+}
+
+void
+excom(Node *en)
+{
+ Src *src;
+ Decl *ed;
+ Type *qt;
+ Case *c;
+ Inst *j, *jmps, *wild, *k;
+ Node *n, *p;
+ Label *labs;
+ int nlab;
+
+ ed = en->left->decl;
+ ed->ty = rtexception;
+ c = en->ty->cse;
+ labs = c->labs;
+ nlab = c->nlab;
+ jmps = nil;
+ wild = nil;
+ for(n = en->right; n != nil; n = n->right){
+ qt = nil;
+ j = nextinst();
+ for(p = n->left->left; p != nil; p = p->right){
+ switch(p->left->op){
+ case Oconst:
+ labs[findlab(texception, p->left, labs, nlab)].inst = j;
+ break;
+ case Owild:
+ wild = j;
+ break;
+ }
+ if(qt == nil)
+ qt = p->left->ty;
+ else if(!tequal(qt, p->left->ty))
+ qt = texception;
+ }
+ if(qt != nil)
+ ed->ty = qt;
+ k = nextinst();
+ scom(n->left->right);
+ src = &lastinst->src;
+ if(k == nextinst())
+ src = &n->left->left->src;
+ j = genrawop(src, IJMP, nil, nil, nil);
+ j->branch = jmps;
+ jmps = j;
+ }
+ ed->ty = rtexception;
+ patch(jmps, nextinst());
+ c->iwild = wild;
+}
+
+/*
+ * rewrite the communication operand
+ * allocate any temps needed for holding value to send or receive
+ */
+Node*
+rewritecomm(Node *n, Node *comm, Node *tmp, Node *slot)
+{
+ Node *adr;
+ Inst *p;
+
+ if(n == nil)
+ return nil;
+ adr = nil;
+ if(n == comm){
+ if(comm->op == Osnd && sumark(n->right)->addable < Rcant)
+ adr = n->right;
+ else{
+ adr = talloc(tmp, n->ty, nil);
+ tmp->src = n->src;
+ if(comm->op == Osnd){
+ ecom(&n->right->src, tmp, n->right);
+ tfreenow();
+ }
+ else
+ trelease(tmp);
+ }
+ }
+ if(n->right == comm && n->op == Oas && comm->op == Orcv
+ && sumark(n->left)->addable < Rcant && (n->left->op != Oname || n->left->decl != nildecl))
+ adr = n->left;
+ if(adr != nil){
+ p = genrawop(&comm->left->src, ILEA, adr, nil, slot);
+ p->m.offset = adr->ty->size; /* for optimizer */
+ if(comm->op == Osnd)
+ p->m.reg = 1; /* for optimizer */
+ return adr;
+ }
+ n->left = rewritecomm(n->left, comm, tmp, slot);
+ n->right = rewritecomm(n->right, comm, tmp, slot);
+ return n;
+}
+
+/*
+ * merge together two sorted lists, yielding a sorted list
+ */
+static Decl*
+declmerge(Decl *e, Decl *f)
+{
+ Decl rock, *d;
+ int es, fs, v;
+
+ d = &rock;
+ while(e != nil && f != nil){
+ fs = f->ty->size;
+ es = e->ty->size;
+ /* v = 0; */
+ v = (e->link == nil) - (f->link == nil);
+ if(v == 0 && (es <= IBY2WD || fs <= IBY2WD))
+ v = fs - es;
+ if(v == 0)
+ v = e->refs - f->refs;
+ if(v == 0)
+ v = fs - es;
+ if(v == 0)
+ v = -strcmp(e->sym->name, f->sym->name);
+ if(v >= 0){
+ d->next = e;
+ d = e;
+ e = e->next;
+ while(e != nil && e->nid == 0){
+ d = e;
+ e = e->next;
+ }
+ }else{
+ d->next = f;
+ d = f;
+ f = f->next;
+ while(f != nil && f->nid == 0){
+ d = f;
+ f = f->next;
+ }
+ }
+ /* d = d->next; */
+ }
+ if(e != nil)
+ d->next = e;
+ else
+ d->next = f;
+ return rock.next;
+}
+
+/*
+ * recursively split lists and remerge them after they are sorted
+ */
+static Decl*
+recdeclsort(Decl *d, int n)
+{
+ Decl *r, *dd;
+ int i, m;
+
+ if(n <= 1)
+ return d;
+ m = n / 2 - 1;
+ dd = d;
+ for(i = 0; i < m; i++){
+ dd = dd->next;
+ while(dd->nid == 0)
+ dd = dd->next;
+ }
+ r = dd->next;
+ while(r->nid == 0){
+ dd = r;
+ r = r->next;
+ }
+ dd->next = nil;
+ return declmerge(recdeclsort(d, n / 2),
+ recdeclsort(r, (n + 1) / 2));
+}
+
+/*
+ * sort the ids by size and number of references
+ */
+Decl*
+declsort(Decl *d)
+{
+ Decl *dd;
+ int n;
+
+ n = 0;
+ for(dd = d; dd != nil; dd = dd->next)
+ if(dd->nid > 0)
+ n++;
+ return recdeclsort(d, n);
+}
+
+Src nilsrc;
+
+/* Do we finally
+ * (a) pick off pointers as in the code below
+ * (b) generate a block move from zeroed memory as in tfree() in gen.b in limbo version
+ * (c) add a new block zero instruction to dis
+ * (d) reorganize the locals/temps in a frame
+ */
+void
+zcom1(Node *n, Node **nn)
+{
+ Type *ty;
+ Decl *d;
+ Node *e, *dn;
+ Src src;
+
+ ty = n->ty;
+ if (!tmustzero(ty))
+ return;
+ if (n->op == Oname && n->decl->refs == 0)
+ return;
+ if (nn) {
+ if(n->op != Oname)
+ nerror(n, "fatal: bad op in zcom1 map");
+ n->right = *nn;
+ *nn = n;
+ return;
+ }
+ if (debug['Z'])
+ print("zcom1 : %n\n", n);
+ if (ty->kind == Tadtpick)
+ ty = ty->tof;
+ if (ty->kind == Ttuple || ty->kind == Tadt) {
+ for (d = ty->ids; d != nil; d = d->next) {
+ if (tmustzero(d->ty)) {
+ if (d->next != nil)
+ dn = dupn(0, nil, n);
+ else
+ dn = n;
+ e = mkbin(Odot, dn, mkname(&nilsrc, d->sym));
+ e->right->decl = d;
+ e->ty = e->right->ty = d->ty;
+ zcom1(e, nn);
+ }
+ }
+ }
+ else {
+ src = n->src;
+ n->src = nilsrc;
+ e = mkbin(Oas, n, mknil(&nilsrc));
+ e->ty = e->right->ty = ty;
+/*
+ if (debug['Z'])
+ print("ecom %n\n", e);
+*/
+ pushblock();
+ e = simplify(e);
+ sumark(e);
+ ecom(&e->src, nil, e);
+ popblock();
+ n->src = src;
+ }
+}
+
+void
+zcom0(Decl *id, Node **nn)
+{
+ Node *e;
+
+ e = mkname(&nilsrc, id->sym);
+ e->decl = id;
+ e->ty = id->ty;
+ zcom1(e, nn);
+}
+
+/* end of scope */
+void
+zcom(Node *n, Node **nn)
+{
+ Decl *ids, *last;
+ Node *r, *nt;
+
+ for ( ; n != nil; n = r) {
+ r = n->right;
+ n->right = nil;
+ switch (n->op) {
+ case Ovardecl :
+ last = n->left->decl;
+ for (ids = n->decl; ids != last->next; ids = ids->next)
+ zcom0(ids, nn);
+ break;
+ case Oname :
+ if (n->decl != nildecl)
+ zcom1(dupn(0, nil, n), nn);
+ break;
+ case Otuple :
+ for (nt = n->left; nt != nil; nt = nt->right)
+ zcom(nt->left, nn);
+ break;
+ default :
+ fatal("bad node in zcom()");
+ break;
+ }
+ n->right = r;
+ }
+}
+
+static int
+ret(Node *n, int nilret)
+{
+ if(n == nil)
+ return nilret;
+ if(n->op == Oseq)
+ n = n->left;
+ return n->op == Oret && n->left == nil;
+}
+
+/*
+ * tail-recursive call
+ */
+static int
+trcom(Node *e, Node *ne, int nilret)
+{
+ Decl *d, *id;
+ Node *as, *a, *f, *n;
+ Inst *p;
+
+ if(1)
+ return 0; /* TO DO: should we enable this? */
+ if(e->op != Ocall || e->left->op != Oname)
+ return 0;
+ d = e->left->decl;
+ if(d != curfn || d->handler || ispoly(d))
+ return 0;
+ if(!ret(ne, nilret))
+ return 0;
+ pushblock();
+ id = d->ty->ids;
+ /* evaluate args in same order as normal calls */
+ for(as = e->right; as != nil; as = as->right){
+ a = as->left;
+ if(!(a->op == Oname && id == a->decl)){
+ if(occurs(id, as->right)){
+ f = talloc(mkn(0, nil, nil), id->ty, nil);
+ f->flags |= TEMP;
+ }
+ else
+ f = mkdeclname(&as->src, id);
+ n = mkbin(Oas, f, a);
+ n->ty = id->ty;
+ scom(n);
+ if(f->flags&TEMP)
+ as->left = f;
+ }
+ id = id->next;
+ }
+ id = d->ty->ids;
+ for(as = e->right; as != nil; as = as->right){
+ a = as->left;
+ if(a->flags&TEMP){
+ f = mkdeclname(&as->src, id);
+ n = mkbin(Oas, f, a);
+ n->ty = id->ty;
+ scom(n);
+ tfree(a);
+ }
+ id = id->next;
+ }
+ p = genrawop(&e->src, IJMP, nil, nil, nil);
+ patch(p, d->pc);
+ popblock();
+ return 1;
+}
--- /dev/null
+++ b/limbo/decls.c
@@ -1,0 +1,1366 @@
+#include "limbo.h"
+
+char *storename[Dend]=
+{
+ /* Dtype */ "type",
+ /* Dfn */ "function",
+ /* Dglobal */ "global",
+ /* Darg */ "argument",
+ /* Dlocal */ "local",
+ /* Dconst */ "con",
+ /* Dfield */ "field",
+ /* Dtag */ "pick tag",
+ /* Dimport */ "import",
+ /* Dunbound */ "unbound",
+ /* Dundef */ "undefined",
+ /* Dwundef */ "undefined",
+};
+
+char *storeart[Dend] =
+{
+ /* Dtype */ "a ",
+ /* Dfn */ "a ",
+ /* Dglobal */ "a ",
+ /* Darg */ "an ",
+ /* Dlocal */ "a ",
+ /* Dconst */ "a ",
+ /* Dfield */ "a ",
+ /* Dtag */ "a",
+ /* Dimport */ "an ",
+ /* Dunbound */ "",
+ /* Dundef */ "",
+ /* Dwundef */ "",
+};
+
+int storespace[Dend] =
+{
+ /* Dtype */ 0,
+ /* Dfn */ 0,
+ /* Dglobal */ 1,
+ /* Darg */ 1,
+ /* Dlocal */ 1,
+ /* Dconst */ 0,
+ /* Dfield */ 1,
+ /* Dtag */ 0,
+ /* Dimport */ 0,
+ /* Dunbound */ 0,
+ /* Dundef */ 0,
+ /* Dwundef */ 0,
+};
+
+static Decl *scopes[MaxScope];
+static Decl *tails[MaxScope];
+static Node *scopenode[MaxScope];
+static uchar scopekind[MaxScope];
+
+static void freeloc(Decl*);
+
+void
+popscopes(void)
+{
+ Decl *d;
+ Dlist *id;
+
+ /*
+ * clear out any decls left in syms
+ */
+ while(scope >= ScopeBuiltin){
+ for(d = scopes[scope--]; d != nil; d = d->next){
+ if(d->sym != nil){
+ d->sym->decl = d->old;
+ d->old = nil;
+ }
+ }
+ }
+
+ for(id = impdecls; id != nil; id = id->next){
+ for(d = id->d->ty->ids; d != nil; d = d->next){
+ d->sym->decl = nil;
+ d->old = nil;
+ }
+ }
+ impdecls = nil;
+
+ scope = ScopeBuiltin;
+ scopes[ScopeBuiltin] = nil;
+ tails[ScopeBuiltin] = nil;
+}
+
+void
+declstart(void)
+{
+ Decl *d;
+
+ iota = mkids(&nosrc, enter("iota", 0), tint, nil);
+ iota->init = mkconst(&nosrc, 0);
+
+ scope = ScopeNils;
+ scopes[ScopeNils] = nil;
+ tails[ScopeNils] = nil;
+
+ nildecl = mkdecl(&nosrc, Dglobal, tany);
+ nildecl->sym = enter("nil", 0);
+ installids(Dglobal, nildecl);
+ d = mkdecl(&nosrc, Dglobal, tstring);
+ d->sym = enter("", 0);
+ installids(Dglobal, d);
+
+ scope = ScopeGlobal;
+ scopes[ScopeGlobal] = nil;
+ tails[ScopeGlobal] = nil;
+}
+
+void
+redecl(Decl *d)
+{
+ Decl *old;
+
+ old = d->sym->decl;
+ if(old->store == Dwundef)
+ return;
+ error(d->src.start, "redeclaration of %K, previously declared as %k on line %L",
+ d, old, old->src.start);
+}
+
+void
+checkrefs(Decl *d)
+{
+ Decl *id, *m;
+ long refs;
+
+ for(; d != nil; d = d->next){
+ if(d->das)
+ d->refs--;
+ switch(d->store){
+ case Dtype:
+ refs = d->refs;
+ if(d->ty->kind == Tadt){
+ for(id = d->ty->ids; id != nil; id = id->next){
+ d->refs += id->refs;
+ if(id->store != Dfn)
+ continue;
+ if(id->init == nil && id->link == nil && d->importid == nil)
+ error(d->src.start, "function %s.%s not defined", d->sym->name, id->sym->name);
+ if(superwarn && !id->refs && d->importid == nil)
+ warn(d->src.start, "function %s.%s not referenced", d->sym->name, id->sym->name);
+ }
+ }
+ if(d->ty->kind == Tmodule){
+ for(id = d->ty->ids; id != nil; id = id->next){
+ refs += id->refs;
+ if(id->iface != nil)
+ id->iface->refs += id->refs;
+ if(id->store == Dtype){
+ for(m = id->ty->ids; m != nil; m = m->next){
+ refs += m->refs;
+ if(m->iface != nil)
+ m->iface->refs += m->refs;
+ }
+ }
+ }
+ d->refs = refs;
+ }
+ if(superwarn && !refs && d->importid == nil)
+ warn(d->src.start, "%K not referenced", d);
+ break;
+ case Dglobal:
+ if(!superwarn)
+ break;
+ case Dlocal:
+ case Darg:
+ if(!d->refs && d->sym != nil
+ && d->sym->name != nil && d->sym->name[0] != '.')
+ warn(d->src.start, "%K not referenced", d);
+ break;
+ case Dconst:
+ if(superwarn && !d->refs && d->sym != nil)
+ warn(d->src.start, "%K not referenced", d);
+ if(d->ty == tstring && d->init != nil)
+ d->init->decl->refs += d->refs;
+ break;
+ case Dfn:
+ if(d->init == nil && d->importid == nil)
+ error(d->src.start, "%K not defined", d);
+ if(superwarn && !d->refs)
+ warn(d->src.start, "%K not referenced", d);
+ break;
+ case Dimport:
+ if(superwarn && !d->refs)
+ warn(d->src.start, "%K not referenced", d);
+ break;
+ }
+ if(d->das)
+ d->refs++;
+ }
+}
+
+Node*
+vardecl(Decl *ids, Type *t)
+{
+ Node *n;
+
+ n = mkn(Ovardecl, mkn(Oseq, nil, nil), nil);
+ n->decl = ids;
+ n->ty = t;
+ return n;
+}
+
+void
+vardecled(Node *n)
+{
+ Decl *ids, *last;
+ Type *t;
+ int store;
+
+ store = Dlocal;
+ if(scope == ScopeGlobal)
+ store = Dglobal;
+ if(n->ty->kind == Texception && n->ty->cons){
+ store = Dconst;
+ fatal("Texception in vardecled");
+ }
+ ids = n->decl;
+ installids(store, ids);
+ t = n->ty;
+ for(last = ids; ids != nil; ids = ids->next){
+ ids->ty = t;
+ last = ids;
+ }
+ n->left->decl = last;
+}
+
+Node*
+condecl(Decl *ids, Node *init)
+{
+ Node *n;
+
+ n = mkn(Ocondecl, mkn(Oseq, nil, nil), init);
+ n->decl = ids;
+ return n;
+}
+
+void
+condecled(Node *n)
+{
+ Decl *ids, *last;
+
+ ids = n->decl;
+ installids(Dconst, ids);
+ for(last = ids; ids != nil; ids = ids->next){
+ ids->ty = tunknown;
+ last = ids;
+ }
+ n->left->decl = last;
+}
+
+Node*
+exdecl(Decl *ids, Decl *tids)
+{
+ Node *n;
+ Type *t;
+
+ t = mktype(&ids->src.start, &ids->src.stop, Texception, nil, tids);
+ t->cons = 1;
+ n = mkn(Oexdecl, mkn(Oseq, nil, nil), nil);
+ n->decl = ids;
+ n->ty = t;
+ return n;
+}
+
+void
+exdecled(Node *n)
+{
+ Decl *ids, *last;
+ Type *t;
+
+ ids = n->decl;
+ installids(Dconst, ids);
+ t = n->ty;
+ for(last = ids; ids != nil; ids = ids->next){
+ ids->ty = t;
+ last = ids;
+ }
+ n->left->decl = last;
+}
+
+Node*
+importdecl(Node *m, Decl *ids)
+{
+ Node *n;
+
+ n = mkn(Oimport, mkn(Oseq, nil, nil), m);
+ n->decl = ids;
+ return n;
+}
+
+void
+importdecled(Node *n)
+{
+ Decl *ids, *last;
+
+ ids = n->decl;
+ installids(Dimport, ids);
+ for(last = ids; ids != nil; ids = ids->next){
+ ids->ty = tunknown;
+ last = ids;
+ }
+ n->left->decl = last;
+}
+
+Node*
+mkscope(Node *body)
+{
+ Node *n;
+
+ n = mkn(Oscope, nil, body);
+ if(body != nil)
+ n->src = body->src;
+ return n;
+}
+
+Node*
+fndecl(Node *n, Type *t, Node *body)
+{
+ n = mkbin(Ofunc, n, body);
+ n->ty = t;
+ return n;
+}
+
+void
+fndecled(Node *n)
+{
+ Decl *d;
+ Node *left;
+
+ left = n->left;
+ if(left->op == Oname){
+ d = left->decl->sym->decl;
+ if(d == nil || d->store == Dimport){
+ d = mkids(&left->src, left->decl->sym, n->ty, nil);
+ installids(Dfn, d);
+ }
+ left->decl = d;
+ d->refs++;
+ }
+ if(left->op == Odot)
+ pushscope(nil, Sother);
+ if(n->ty->polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, n->ty->polys);
+ }
+ pushscope(nil, Sother);
+ installids(Darg, n->ty->ids);
+ n->ty->ids = popscope();
+ if(n->ty->val != nil)
+ mergepolydecs(n->ty);
+ if(n->ty->polys != nil)
+ n->ty->polys = popscope();
+ if(left->op == Odot)
+ popscope();
+}
+
+/*
+ * check the function declaration only
+ * the body will be type checked later by fncheck
+ */
+Decl *
+fnchk(Node *n)
+{
+ int bad;
+ Decl *d, *inadt, *adtp;
+ Type *t;
+
+ bad = 0;
+ d = n->left->decl;
+ if(n->left->op == Odot)
+ d = n->left->right->decl;
+ if(d == nil)
+ fatal("decl() fnchk nil");
+ n->left->decl = d;
+ if(d->store == Dglobal || d->store == Dfield)
+ d->store = Dfn;
+ if(d->store != Dfn || d->init != nil){
+ nerror(n, "redeclaration of function %D, previously declared as %k on line %L",
+ d, d, d->src.start);
+ if(d->store == Dfn && d->init != nil)
+ bad = 1;
+ }
+ d->init = n;
+
+ t = n->ty;
+ inadt = d->dot;
+ if(inadt != nil && (inadt->store != Dtype || inadt->ty->kind != Tadt))
+ inadt = nil;
+ if(n->left->op == Odot){
+ pushscope(nil, Sother);
+ adtp = outerpolys(n->left);
+ if(adtp != nil)
+ installids(Dtype, adtp);
+ if(!polyequal(adtp, n->decl))
+ nerror(n, "adt polymorphic type mismatch");
+ n->decl = nil;
+ }
+ t = validtype(t, inadt);
+ if(n->left->op == Odot)
+ popscope();
+ if(debug['d'])
+ print("declare function %D ty %T newty %T\n", d, d->ty, t);
+ t = usetype(t);
+
+ if(!polyequal(d->ty->polys, t->polys))
+ nerror(n, "function polymorphic type mismatch");
+ if(!tcompat(d->ty, t, 0))
+ nerror(n, "type mismatch: %D defined as %T declared as %T on line %L",
+ d, t, d->ty, d->src.start);
+ else if(!raisescompat(d->ty->u.eraises, t->u.eraises))
+ nerror(n, "raises mismatch: %D", d);
+ if(t->varargs != 0)
+ nerror(n, "cannot define functions with a '*' argument, such as %D", d);
+
+ t->u.eraises = d->ty->u.eraises;
+
+ d->ty = t;
+ d->offset = idoffsets(t->ids, MaxTemp, IBY2WD);
+ d->src = n->src;
+
+ d->locals = nil;
+
+ n->ty = t;
+
+ return bad ? nil: d;
+}
+
+Node*
+globalas(Node *dst, Node *v, int valok)
+{
+ Node *tv;
+
+ if(v == nil)
+ return nil;
+ if(v->op == Oas || v->op == Odas){
+ v = globalas(v->left, v->right, valok);
+ if(v == nil)
+ return nil;
+ }else if(valok && !initable(dst, v, 0))
+ return nil;
+ switch(dst->op){
+ case Oname:
+ if(dst->decl->init != nil)
+ nerror(dst, "duplicate assignment to %V, previously assigned on line %L",
+ dst, dst->decl->init->src.start);
+ if(valok)
+ dst->decl->init = v;
+ return v;
+ case Otuple:
+ if(valok && v->op != Otuple)
+ fatal("can't deal with %n in tuple case of globalas", v);
+ tv = v->left;
+ for(dst = dst->left; dst != nil; dst = dst->right){
+ globalas(dst->left, tv->left, valok);
+ if(valok)
+ tv = tv->right;
+ }
+ return v;
+ }
+ fatal("can't deal with %n in globalas", dst);
+ return nil;
+}
+
+int
+needsstore(Decl *d)
+{
+ if(!d->refs)
+ return 0;
+ if(d->importid != nil)
+ return 0;
+ if(storespace[d->store])
+ return 1;
+ return 0;
+}
+
+/*
+ * return the list of all referenced storage variables
+ */
+Decl*
+vars(Decl *d)
+{
+ Decl *v, *n;
+
+ while(d != nil && !needsstore(d))
+ d = d->next;
+ for(v = d; v != nil; v = v->next){
+ while(v->next != nil){
+ n = v->next;
+ if(needsstore(n))
+ break;
+ v->next = n->next;
+ }
+ }
+ return d;
+}
+
+/*
+ * declare variables from the left side of a := statement
+ */
+static int
+recdasdecl(Node *n, int store, int *nid)
+{
+ Decl *d, *old;
+ int ok;
+
+ switch(n->op){
+ case Otuple:
+ ok = 1;
+ for(n = n->left; n != nil; n = n->right)
+ ok &= recdasdecl(n->left, store, nid);
+ return ok;
+ case Oname:
+ if(n->decl == nildecl){
+ *nid = -1;
+ return 1;
+ }
+ d = mkids(&n->src, n->decl->sym, nil, nil);
+ installids(store, d);
+ old = d->old;
+ if(old != nil
+ && old->store != Dfn
+ && old->store != Dwundef
+ && old->store != Dundef)
+ warn(d->src.start, "redeclaration of %K, previously declared as %k on line %L",
+ d, old, old->src.start);
+ n->decl = d;
+ d->refs++;
+ d->das = 1;
+ if(*nid >= 0)
+ (*nid)++;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+recmark(Node *n, int nid)
+{
+ switch(n->op){
+ case Otuple:
+ for(n = n->left; n != nil; n = n->right)
+ nid = recmark(n->left, nid);
+ break;
+ case Oname:
+ n->decl->nid = nid;
+ nid = 0;
+ break;
+ }
+ return nid;
+}
+
+int
+dasdecl(Node *n)
+{
+ int store, ok, nid;
+
+ nid = 0;
+ if(scope == ScopeGlobal)
+ store = Dglobal;
+ else
+ store = Dlocal;
+
+ ok = recdasdecl(n, store, &nid);
+ if(!ok)
+ nerror(n, "illegal declaration expression %V", n);
+ if(ok && store == Dlocal && nid > 1)
+ recmark(n, nid);
+ return ok;
+}
+
+/*
+ * declare global variables in nested := expressions
+ */
+void
+gdasdecl(Node *n)
+{
+ if(n == nil)
+ return;
+
+ if(n->op == Odas){
+ gdasdecl(n->right);
+ dasdecl(n->left);
+ }else{
+ gdasdecl(n->left);
+ gdasdecl(n->right);
+ }
+}
+
+Decl*
+undefed(Src *src, Sym *s)
+{
+ Decl *d;
+
+ d = mkids(src, s, tnone, nil);
+ error(src->start, "%s is not declared", s->name);
+ installids(Dwundef, d);
+ return d;
+}
+
+/*
+int
+inloop()
+{
+ int i;
+
+ for (i = scope; i > 0; i--)
+ if (scopekind[i] == Sloop)
+ return 1;
+ return 0;
+}
+*/
+
+int
+nested()
+{
+ int i;
+
+ for (i = scope; i > 0; i--)
+ if (scopekind[i] == Sscope || scopekind[i] == Sloop)
+ return 1;
+ return 0;
+}
+
+void
+decltozero(Node *n)
+{
+ Node *scp;
+
+ if ((scp = scopenode[scope]) != nil) {
+ /* can happen if we do
+ * x[i] := ......
+ * which is an error
+ */
+ if (n->right != nil && errors == 0)
+ fatal("Ovardecl/Oname/Otuple has right field\n");
+ n->right = scp->left;
+ scp->left = n;
+ }
+}
+
+void
+pushscope(Node *scp, int kind)
+{
+ if(scope >= MaxScope)
+ fatal("scope too deep");
+ scope++;
+ scopes[scope] = nil;
+ tails[scope] = nil;
+ scopenode[scope] = scp;
+ scopekind[scope] = kind;
+}
+
+Decl*
+curscope(void)
+{
+ return scopes[scope];
+}
+
+/*
+ * revert to old declarations for each symbol in the currect scope.
+ * remove the effects of any imported adt types
+ * whenever the adt is imported from a module,
+ * we record in the type's decl the module to use
+ * when calling members. the process is reversed here.
+ */
+Decl*
+popscope(void)
+{
+ Decl *id;
+ Type *t;
+
+if (debug['X'])
+ print("popscope\n");
+ for(id = scopes[scope]; id != nil; id = id->next){
+ if(id->sym != nil){
+if (debug['X'])
+ print("%s : %s %d\n", id->sym->name, kindname[id->ty->kind], id->init != nil ? id->init->op : 0);
+ id->sym->decl = id->old;
+ id->old = nil;
+ }
+ if(id->importid != nil)
+ id->importid->refs += id->refs;
+ t = id->ty;
+ if(id->store == Dtype
+ && t->decl != nil
+ && t->decl->timport == id)
+ t->decl->timport = id->timport;
+ if(id->store == Dlocal)
+ freeloc(id);
+ }
+ return scopes[scope--];
+}
+
+/*
+ * make a new scope,
+ * preinstalled with some previously installed identifiers
+ * don't add the identifiers to the scope chain,
+ * so they remain separate from any newly installed ids
+ *
+ * these routines assume no ids are imports
+ */
+void
+repushids(Decl *ids)
+{
+ Sym *s;
+
+ if(scope >= MaxScope)
+ fatal("scope too deep");
+ scope++;
+ scopes[scope] = nil;
+ tails[scope] = nil;
+ scopenode[scope] = nil;
+ scopekind[scope] = Sother;
+
+ for(; ids != nil; ids = ids->next){
+ if(ids->scope != scope
+ && (ids->dot == nil || !isimpmod(ids->dot->sym)
+ || ids->scope != ScopeGlobal || scope != ScopeGlobal + 1))
+ fatal("repushids scope mismatch");
+ s = ids->sym;
+ if(s != nil && ids->store != Dtag){
+ if(s->decl != nil && s->decl->scope >= scope)
+ ids->old = s->decl->old;
+ else
+ ids->old = s->decl;
+ s->decl = ids;
+ }
+ }
+}
+
+/*
+ * pop a scope which was started with repushids
+ * return any newly installed ids
+ */
+Decl*
+popids(Decl *ids)
+{
+ for(; ids != nil; ids = ids->next){
+ if(ids->sym != nil && ids->store != Dtag){
+ ids->sym->decl = ids->old;
+ ids->old = nil;
+ }
+ }
+ return popscope();
+}
+
+void
+installids(int store, Decl *ids)
+{
+ Decl *d, *last;
+ Sym *s;
+
+ last = nil;
+ for(d = ids; d != nil; d = d->next){
+ d->scope = scope;
+ if(d->store == Dundef)
+ d->store = store;
+ s = d->sym;
+ if(s != nil){
+ if(s->decl != nil && s->decl->scope >= scope){
+ redecl(d);
+ d->old = s->decl->old;
+ }else
+ d->old = s->decl;
+ s->decl = d;
+ }
+ last = d;
+ }
+ if(ids != nil){
+ d = tails[scope];
+ if(d == nil)
+ scopes[scope] = ids;
+ else
+ d->next = ids;
+ tails[scope] = last;
+ }
+}
+
+Decl*
+lookup(Sym *sym)
+{
+ int s;
+ Decl *d;
+
+ for(s = scope; s >= ScopeBuiltin; s--){
+ for(d = scopes[s]; d != nil; d = d->next){
+ if(d->sym == sym)
+ return d;
+ }
+ }
+ return nil;
+}
+
+Decl*
+mkids(Src *src, Sym *s, Type *t, Decl *next)
+{
+ Decl *d;
+
+ d = mkdecl(src, Dundef, t);
+ d->next = next;
+ d->sym = s;
+ return d;
+}
+
+Decl*
+mkdecl(Src *src, int store, Type *t)
+{
+ Decl *d;
+ static Decl z;
+
+ d = allocmem(sizeof *d);
+ *d = z;
+ d->src = *src;
+ d->store = store;
+ d->ty = t;
+ d->nid = 1;
+ return d;
+}
+
+Decl*
+dupdecl(Decl *old)
+{
+ Decl *d;
+
+ d = allocmem(sizeof *d);
+ *d = *old;
+ d->next = nil;
+ return d;
+}
+
+Decl*
+dupdecls(Decl *old)
+{
+ Decl *d, *nd, *first, *last;
+
+ first = last = nil;
+ for(d = old; d != nil; d = d->next){
+ nd = dupdecl(d);
+ if(first == nil)
+ first = nd;
+ else
+ last->next = nd;
+ last = nd;
+ }
+ return first;
+}
+
+Decl*
+appdecls(Decl *d, Decl *dd)
+{
+ Decl *t;
+
+ if(d == nil)
+ return dd;
+ for(t = d; t->next != nil; t = t->next)
+ ;
+ t->next = dd;
+ return d;
+}
+
+Decl*
+revids(Decl *id)
+{
+ Decl *d, *next;
+
+ d = nil;
+ for(; id != nil; id = next){
+ next = id->next;
+ id->next = d;
+ d = id;
+ }
+ return d;
+}
+
+long
+idoffsets(Decl *id, long offset, int al)
+{
+ int a, algn;
+ Decl *d;
+
+ algn = 1;
+ for(; id != nil; id = id->next){
+ if(storespace[id->store]){
+usedty(id->ty);
+ if(id->store == Dlocal && id->link != nil){
+ /* id->nid always 1 */
+ id->offset = id->link->offset;
+ continue;
+ }
+ a = id->ty->align;
+ if(id->nid > 1){
+ for(d = id->next; d != nil && d->nid == 0; d = d->next)
+ if(d->ty->align > a)
+ a = d->ty->align;
+ algn = a;
+ }
+ offset = align(offset, a);
+ id->offset = offset;
+ offset += id->ty->size;
+ if(id->nid == 0 && (id->next == nil || id->next->nid != 0))
+ offset = align(offset, algn);
+ }
+ }
+ return align(offset, al);
+}
+
+long
+idindices(Decl *id)
+{
+ int i;
+
+ i = 0;
+ for(; id != nil; id = id->next){
+ if(storespace[id->store]){
+ usedty(id->ty);
+ id->offset = i++;
+ }
+ }
+ return i;
+}
+
+int
+declconv(Fmt *f)
+{
+ Decl *d;
+ char buf[4096], *s;
+
+ d = va_arg(f->args, Decl*);
+ if(d->sym == nil)
+ s = "<nil>";
+ else
+ s = d->sym->name;
+ seprint(buf, buf+sizeof(buf), "%s %s", storename[d->store], s);
+ return fmtstrcpy(f, buf);
+}
+
+int
+storeconv(Fmt *f)
+{
+ Decl *d;
+ char buf[4096];
+
+ d = va_arg(f->args, Decl*);
+ seprint(buf, buf+sizeof(buf), "%s%s", storeart[d->store], storename[d->store]);
+ return fmtstrcpy(f, buf);
+}
+
+int
+dotconv(Fmt *f)
+{
+ Decl *d;
+ char buf[4096], *p, *s;
+
+ d = va_arg(f->args, Decl*);
+ buf[0] = 0;
+ p = buf;
+ if(d->dot != nil && !isimpmod(d->dot->sym)){
+ s = ".";
+ if(d->dot->ty != nil && d->dot->ty->kind == Tmodule)
+ s = "->";
+ p = seprint(buf, buf+sizeof(buf), "%D%s", d->dot, s);
+ }
+ seprint(p, buf+sizeof(buf), "%s", d->sym->name);
+ return fmtstrcpy(f, buf);
+}
+
+/*
+ * merge together two sorted lists, yielding a sorted list
+ */
+static Decl*
+namemerge(Decl *e, Decl *f)
+{
+ Decl rock, *d;
+
+ d = &rock;
+ while(e != nil && f != nil){
+ if(strcmp(e->sym->name, f->sym->name) <= 0){
+ d->next = e;
+ e = e->next;
+ }else{
+ d->next = f;
+ f = f->next;
+ }
+ d = d->next;
+ }
+ if(e != nil)
+ d->next = e;
+ else
+ d->next = f;
+ return rock.next;
+}
+
+/*
+ * recursively split lists and remerge them after they are sorted
+ */
+static Decl*
+recnamesort(Decl *d, int n)
+{
+ Decl *r, *dd;
+ int i, m;
+
+ if(n <= 1)
+ return d;
+ m = n / 2 - 1;
+ dd = d;
+ for(i = 0; i < m; i++)
+ dd = dd->next;
+ r = dd->next;
+ dd->next = nil;
+ return namemerge(recnamesort(d, n / 2),
+ recnamesort(r, (n + 1) / 2));
+}
+
+/*
+ * sort the ids by name
+ */
+Decl*
+namesort(Decl *d)
+{
+ Decl *dd;
+ int n;
+
+ n = 0;
+ for(dd = d; dd != nil; dd = dd->next)
+ n++;
+ return recnamesort(d, n);
+}
+
+void
+printdecls(Decl *d)
+{
+ for(; d != nil; d = d->next)
+ print("%ld: %K %T ref %d\n", d->offset, d, d->ty, d->refs);
+}
+
+void
+mergepolydecs(Type *t)
+{
+ Node *n, *nn;
+ Decl *id, *ids, *ids1;
+
+ for(n = t->val; n != nil; n = n->right){
+ nn = n->left;
+ for(ids = nn->decl; ids != nil; ids = ids->next){
+ id = ids->sym->decl;
+ if(id == nil){
+ undefed(&ids->src, ids->sym);
+ break;
+ }
+ if(id->store != Dtype){
+ error(ids->src.start, "%K is not a type", id);
+ break;
+ }
+ if(id->ty->kind != Tpoly){
+ error(ids->src.start, "%K is not a polymorphic type", id);
+ break;
+ }
+ if(id->ty->ids != nil)
+ error(ids->src.start, "%K redefined", id);
+ pushscope(nil, Sother);
+ fielddecled(nn->left);
+ id->ty->ids = popscope();
+ for(ids1 = id->ty->ids; ids1 != nil; ids1 = ids1->next){
+ ids1->dot = id;
+ bindtypes(ids1->ty);
+ if(ids1->ty->kind != Tfn){
+ error(ids1->src.start, "only function types expected");
+ id->ty->ids = nil;
+ }
+ }
+ }
+ }
+ t->val = nil;
+}
+
+static void
+adjfnptrs(Decl *d, Decl *polys1, Decl *polys2)
+{
+ int n;
+ Decl *id, *idt, *idf, *arg;
+
+ if(debug['U'])
+ print("adjnptrs %s\n", d->sym->name);
+ n = 0;
+ for(id = d->ty->ids; id != nil; id = id->next)
+ n++;
+ for(idt = polys1; idt != nil; idt = idt->next)
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next)
+ n -= 2;
+ for(idt = polys2; idt != nil; idt = idt->next)
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next)
+ n -= 2;
+ for(arg = d->ty->ids; --n >= 0; arg = arg->next)
+ ;
+ for(idt = polys1; idt != nil; idt = idt->next){
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+ idf->link = arg;
+ arg = arg->next->next;
+ }
+ }
+ for(idt = polys2; idt != nil; idt = idt->next){
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+ idf->link = arg;
+ arg = arg->next->next;
+ }
+ }
+}
+
+static void
+addptrs(Decl *polys, Decl** fps, Decl **last, int link, Src *src)
+{
+ Decl *idt, *idf, *fp;
+
+ if(debug['U'])
+ print("addptrs\n");
+ for(idt = polys; idt != nil; idt = idt->next){
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+ fp = mkdecl(src, Darg, tany);
+ fp->sym = idf->sym;
+ if(link)
+ idf->link = fp;
+ if(*fps == nil)
+ *fps = fp;
+ else
+ (*last)->next = fp;
+ *last = fp;
+ fp = mkdecl(src, Darg, tint);
+ fp->sym = idf->sym;
+ (*last)->next = fp;
+ *last = fp;
+ }
+ }
+}
+
+void
+addfnptrs(Decl *d, int link)
+{
+ Decl *fps, *last, *polys;
+
+ if(debug['U'])
+ print("addfnptrs %s %d\n", d->sym->name, link);
+ polys = encpolys(d);
+ if(d->ty->flags&FULLARGS){
+ if(link)
+ adjfnptrs(d, d->ty->polys, polys);
+ if(0 && debug['U']){
+ for(d = d->ty->ids; d != nil; d = d->next)
+ print("%s=%ld(%d) ", d->sym->name, d->offset, tattr[d->ty->kind].isptr);
+ print("\n");
+ }
+ return;
+ }
+ d->ty->flags |= FULLARGS;
+ fps = last = nil;
+ addptrs(d->ty->polys, &fps, &last, link, &d->src);
+ addptrs(polys, &fps, &last, link, &d->src);
+ for(last = d->ty->ids; last != nil && last->next != nil; last = last->next)
+ ;
+ if(last != nil)
+ last->next = fps;
+ else
+ d->ty->ids = fps;
+ d->offset = idoffsets(d->ty->ids, MaxTemp, IBY2WD);
+ if(0 && debug['U']){
+ for(d = d->ty->ids; d != nil; d = d->next)
+ print("%s=%ld(%d) ", d->sym->name, d->offset, tattr[d->ty->kind].isptr);
+ print("\n");
+ }
+}
+
+void
+rmfnptrs(Decl *d)
+{
+ int n;
+ Decl *id, *idt, *idf;
+
+ if(debug['U'])
+ print("rmfnptrs %s\n", d->sym->name);
+ if(!(d->ty->flags&FULLARGS))
+ return;
+ d->ty->flags &= ~FULLARGS;
+ n = 0;
+ for(id = d->ty->ids; id != nil; id = id->next)
+ n++;
+ for(idt = d->ty->polys; idt != nil; idt = idt->next)
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next)
+ n -= 2;
+ for(idt = encpolys(d); idt != nil; idt = idt->next)
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next)
+ n -= 2;
+ if(n == 0){
+ d->ty->ids = nil;
+ return;
+ }
+ for(id = d->ty->ids; --n > 0; id = id->next)
+ ;
+ id->next = nil;
+ d->offset = idoffsets(d->ty->ids, MaxTemp, IBY2WD);
+}
+
+int
+local(Decl *d)
+{
+ for(d = d->dot; d != nil; d = d->dot)
+ if(d->store == Dtype && d->ty->kind == Tmodule)
+ return 0;
+ return 1;
+}
+
+Decl*
+module(Decl *d)
+{
+ for(d = d->dot; d != nil; d = d->dot)
+ if(d->store == Dtype && d->ty->kind == Tmodule)
+ return d;
+ return nil;
+}
+
+Decl*
+outerpolys(Node *n)
+{
+ Decl *d;
+
+ if(n->op == Odot){
+ d = n->right->decl;
+ if(d == nil)
+ fatal("decl() outeradt nil");
+ d = d->dot;
+ if(d != nil && d->store == Dtype && d->ty->kind == Tadt)
+ return d->ty->polys;
+ }
+ return nil;
+}
+
+Decl*
+encpolys(Decl *d)
+{
+ if((d = d->dot) == nil)
+ return nil;
+ return d->ty->polys;
+}
+
+Decl*
+fnlookup(Sym *s, Type *t, Node **m)
+{
+ Decl *id;
+ Node *mod;
+
+ id = nil;
+ mod = nil;
+ if(t->kind == Tpoly || t->kind == Tmodule)
+ id = namedot(t->ids, s);
+ else if(t->kind == Tref){
+ t = t->tof;
+ if(t->kind == Tadt){
+ id = namedot(t->ids, s);
+ if(t->decl != nil && t->decl->timport != nil)
+ mod = t->decl->timport->eimport;
+ }
+ else if(t->kind == Tadtpick){
+ id = namedot(t->ids, s);
+ if(t->decl != nil && t->decl->timport != nil)
+ mod = t->decl->timport->eimport;
+ t = t->decl->dot->ty;
+ if(id == nil)
+ id = namedot(t->ids, s);
+ if(t->decl != nil && t->decl->timport != nil)
+ mod = t->decl->timport->eimport;
+ }
+ }
+ if(id == nil){
+ id = lookup(s);
+ if(id != nil)
+ mod = id->eimport;
+ }
+ if(m != nil)
+ *m = mod;
+ return id;
+}
+
+int
+isimpmod(Sym *s)
+{
+ Decl *d;
+
+ for(d = impmods; d != nil; d = d->next)
+ if(d->sym == s)
+ return 1;
+ return 0;
+}
+
+int
+dequal(Decl *d1, Decl *d2, int full)
+{
+ return d1->sym == d2->sym &&
+ d1->store == d2->store &&
+ d1->implicit == d2->implicit &&
+ d1->cyc == d2->cyc &&
+ (!full || tequal(d1->ty, d2->ty)) &&
+ (!full || d1->store == Dfn || sametree(d1->init, d2->init));
+}
+
+static int
+tzero(Type *t)
+{
+ return t->kind == Texception || tmustzero(t);
+}
+
+static int
+isptr(Type *t)
+{
+ return t->kind == Texception || tattr[t->kind].isptr;
+}
+
+/* can d share the same stack location as another local ? */
+void
+shareloc(Decl *d)
+{
+ int z;
+ Type *t, *tt;
+ Decl *dd, *res;
+
+ if(d->store != Dlocal || d->nid != 1)
+ return;
+ t = d->ty;
+ res = nil;
+ for(dd = fndecls; dd != nil; dd = dd->next){
+ if(d == dd)
+ fatal("d==dd in shareloc");
+ if(dd->store != Dlocal || dd->nid != 1 || dd->link != nil || dd->tref != 0)
+ continue;
+ tt = dd->ty;
+ if(t->size != tt->size || t->align != tt->align)
+ continue;
+ z = tzero(t)+tzero(tt);
+ if(z > 0)
+ continue; /* for now */
+ if(t == tt || tequal(t, tt))
+ res = dd;
+ else{
+ if(z == 1)
+ continue;
+ if(z == 0 || isptr(t) || isptr(tt) || mktdesc(t) == mktdesc(tt))
+ res = dd;
+ }
+ if(res != nil){
+ /* print("%L %K share %L %K\n", d->src.start, d, res->src.start, res); */
+ d->link = res;
+ res->tref = 1;
+ return;
+ }
+ }
+ return;
+}
+
+static void
+freeloc(Decl *d)
+{
+ if(d->link != nil)
+ d->link->tref = 0;
+}
--- /dev/null
+++ b/limbo/dis.c
@@ -1,0 +1,638 @@
+#include "limbo.h"
+
+static void disbig(long, Long);
+static void disbyte(long, int);
+static void disbytes(long, void*, int);
+static void disdatum(long, Node*);
+static void disflush(int, long, long);
+static void disint(long, long);
+static void disreal(long, Real);
+static void disstring(long, Sym*);
+
+static uchar *cache;
+static int ncached;
+static int ndatum;
+static int startoff;
+static int lastoff;
+static int lastkind;
+static int lencache;
+
+void
+discon(long val)
+{
+ if(val >= -64 && val <= 63) {
+ Bputc(bout, val & ~0x80);
+ return;
+ }
+ if(val >= -8192 && val <= 8191) {
+ Bputc(bout, ((val>>8) & ~0xC0) | 0x80);
+ Bputc(bout, val);
+ return;
+ }
+ if(val < 0 && ((val >> 29) & 0x7) != 7
+ || val > 0 && (val >> 29) != 0)
+ fatal("overflow in constant 0x%lux\n", val);
+ Bputc(bout, (val>>24) | 0xC0);
+ Bputc(bout, val>>16);
+ Bputc(bout, val>>8);
+ Bputc(bout, val);
+}
+
+void
+disword(long w)
+{
+ Bputc(bout, w >> 24);
+ Bputc(bout, w >> 16);
+ Bputc(bout, w >> 8);
+ Bputc(bout, w);
+}
+
+void
+disdata(int kind, long n)
+{
+ if(n < DMAX && n != 0)
+ Bputc(bout, DBYTE(kind, n));
+ else{
+ Bputc(bout, DBYTE(kind, 0));
+ discon(n);
+ }
+}
+
+#define NAMELEN 64
+
+void
+dismod(Decl *m)
+{
+ char name[8*NAMELEN];
+ vlong fileoff;
+
+ fileoff = Boffset(bout);
+ strncpy(name, m->sym->name, NAMELEN);
+ name[NAMELEN-1] = '\0';
+ Bwrite(bout, name, strlen(name)+1);
+ for(m = m->ty->tof->ids; m != nil; m = m->next){
+ switch(m->store){
+ case Dglobal:
+ discon(-1);
+ discon(-1);
+ disword(sign(m));
+ Bprint(bout, ".mp");
+ Bputc(bout, '\0');
+ break;
+ case Dfn:
+if(debug['v']) print("Dfn: %s %d %p\n", m->sym->name, m->refs, m);
+ discon(m->pc->pc);
+ discon(m->desc->id);
+ disword(sign(m));
+ if(m->dot->ty->kind == Tadt)
+ Bprint(bout, "%s.", m->dot->sym->name);
+ Bprint(bout, "%s", m->sym->name);
+ Bputc(bout, '\0');
+ break;
+ default:
+ fatal("unknown kind %K in dismod", m);
+ break;
+ }
+ }
+ if(debug['s'])
+ print("%lld linkage bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+dispath(void)
+{
+ char name[8*NAMELEN], *sp;
+
+ sp = srcpath(name, 8*NAMELEN);
+ Bwrite(bout, sp, strlen(sp)+1);
+}
+
+void
+disentry(Decl *e)
+{
+ if(e == nil){
+ discon(-1);
+ discon(-1);
+ return;
+ }
+ discon(e->pc->pc);
+ discon(e->desc->id);
+}
+
+void
+disdesc(Desc *d)
+{
+ vlong fileoff;
+
+ fileoff = Boffset(bout);
+ for(; d != nil; d = d->next){
+ discon(d->id);
+ discon(d->size);
+ discon(d->nmap);
+ Bwrite(bout, d->map, d->nmap);
+ }
+ if(debug['s'])
+ print("%lld type descriptor bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+disvar(long size, Decl *d)
+{
+ vlong fileoff;
+
+ fileoff = Boffset(bout);
+ USED(size);
+
+ lastkind = -1;
+ ncached = 0;
+ ndatum = 0;
+
+ for(; d != nil; d = d->next)
+ if(d->store == Dglobal && d->init != nil)
+ disdatum(d->offset, d->init);
+
+ disflush(-1, -1, 0);
+
+ Bputc(bout, 0);
+
+ if(debug['s'])
+ print("%lld data bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+disldt(long size, Decl *ds)
+{
+ int m;
+ Decl *d, *id;
+ Sym *s;
+ Node *n;
+
+ if(0){
+ discon(size);
+ disvar(size, ds);
+ return;
+ }
+
+ m = 0;
+ for(d = ds; d != nil; d = d->next)
+ if(d->store == Dglobal && d->init != nil)
+ m++;
+ discon(m);
+ for(d = ds; d != nil; d = d->next){
+ if(d->store == Dglobal && d->init != nil){
+ n = d->init;
+ if(n->ty->kind != Tiface)
+ nerror(n, "disldt: not Tiface");
+ discon(n->val);
+ for(id = n->decl->ty->ids; id != nil; id = id->next){
+ disword(sign(id));
+ if(id->dot->ty->kind == Tadt){
+ s = id->dot->sym;
+ Bprint(bout, "%s", s->name);
+ Bputc(bout, '.');
+ }
+ s = id->sym;
+ Bprint(bout, "%s", s->name);
+ Bputc(bout, 0);
+ }
+ }
+ }
+ discon(0);
+}
+
+static void
+disdatum(long offset, Node *n)
+{
+ Node *elem, *wild;
+ Case *c;
+ Label *lab;
+ Decl *id;
+ Sym *s;
+ long e, last, esz;
+ int i;
+
+ switch(n->ty->kind){
+ case Tbyte:
+ disbyte(offset, n->val);
+ break;
+ case Tint:
+ case Tfix:
+ disint(offset, n->val);
+ break;
+ case Tbig:
+ disbig(offset, n->val);
+ break;
+ case Tstring:
+ disstring(offset, n->decl->sym);
+ break;
+ case Treal:
+ disreal(offset, n->rval);
+ break;
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ id = n->ty->ids;
+ for(n = n->left; n != nil; n = n->right){
+ disdatum(offset + id->offset, n->left);
+ id = id->next;
+ }
+ break;
+ case Tany:
+ break;
+ case Tcase:
+ c = n->ty->cse;
+ disint(offset, c->nlab);
+ offset += IBY2WD;
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ disint(offset, lab->start->val);
+ offset += IBY2WD;
+ disint(offset, lab->stop->val+1);
+ offset += IBY2WD;
+ disint(offset, lab->inst->pc);
+ offset += IBY2WD;
+ }
+ disint(offset, c->iwild ? c->iwild->pc : -1);
+ break;
+ case Tcasel:
+ c = n->ty->cse;
+ disint(offset, c->nlab);
+ offset += 2*IBY2WD;
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ disbig(offset, lab->start->val);
+ offset += IBY2LG;
+ disbig(offset, lab->stop->val+1);
+ offset += IBY2LG;
+ disint(offset, lab->inst->pc);
+ offset += 2*IBY2WD;
+ }
+ disint(offset, c->iwild ? c->iwild->pc : -1);
+ break;
+ case Tcasec:
+ c = n->ty->cse;
+ disint(offset, c->nlab);
+ offset += IBY2WD;
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ disstring(offset, lab->start->decl->sym);
+ offset += IBY2WD;
+ if(lab->stop != lab->start)
+ disstring(offset, lab->stop->decl->sym);
+ offset += IBY2WD;
+ disint(offset, lab->inst->pc);
+ offset += IBY2WD;
+ }
+ disint(offset, c->iwild ? c->iwild->pc : -1);
+ break;
+ case Tgoto:
+ c = n->ty->cse;
+ disint(offset, n->ty->size/IBY2WD-1);
+ offset += IBY2WD;
+ for(i = 0; i < c->nlab; i++){
+ disint(offset, c->labs[i].inst->pc);
+ offset += IBY2WD;
+ }
+ if(c->iwild != nil)
+ disint(offset, c->iwild->pc);
+ break;
+ case Tarray:
+ disflush(-1, -1, 0);
+ disdata(DEFA, 1); /* 1 is ignored */
+ discon(offset);
+ disword(n->ty->tof->decl->desc->id);
+ disword(n->left->val);
+
+ if(n->right == nil)
+ break;
+
+ disdata(DIND, 1); /* 1 is ignored */
+ discon(offset);
+ disword(0);
+
+ c = n->right->ty->cse;
+ wild = nil;
+ if(c->wild != nil)
+ wild = c->wild->right;
+ last = 0;
+ esz = n->ty->tof->size;
+ for(i = 0; i < c->nlab; i++){
+ e = c->labs[i].start->val;
+ if(wild != nil){
+ for(; last < e; last++)
+ disdatum(esz * last, wild);
+ }
+ last = e;
+ e = c->labs[i].stop->val;
+ elem = c->labs[i].node->right;
+ for(; last <= e; last++)
+ disdatum(esz * last, elem);
+ }
+ if(wild != nil)
+ for(e = n->left->val; last < e; last++)
+ disdatum(esz * last, wild);
+
+ disflush(-1, -1, 0);
+ disdata(DAPOP, 1); /* 1 is ignored */
+ discon(0);
+
+ break;
+ case Tiface:
+ disint(offset, n->val);
+ offset += IBY2WD;
+ for(id = n->decl->ty->ids; id != nil; id = id->next){
+ offset = align(offset, IBY2WD);
+ disint(offset, sign(id));
+ offset += IBY2WD;
+
+ if(id->dot->ty->kind == Tadt){
+ s = id->dot->sym;
+ disbytes(offset, s->name, s->len);
+ offset += s->len;
+ disbyte(offset, '.');
+ offset++;
+ }
+ s = id->sym;
+ disbytes(offset, s->name, s->len);
+ offset += s->len;
+ disbyte(offset, 0);
+ offset++;
+ }
+ break;
+ default:
+ nerror(n, "can't dis global %n", n);
+ break;
+ }
+}
+
+void
+disexc(Except *es)
+{
+ int i, n;
+ Decl *d;
+ Except *e;
+ Case *c;
+ Label *lab;
+
+ n = 0;
+ for(e = es; e != nil; e = e->next)
+ if(e->p1->reach || e->p2->reach)
+ n++;
+ discon(n);
+ for(e = es; e != nil; e = e->next){
+ if(!e->p1->reach && !e->p2->reach)
+ continue;
+ c = e->c;
+ discon(e->d->offset);
+ discon(getpc(e->p1));
+ discon(getpc(e->p2));
+ if(e->desc)
+ discon(e->desc->id);
+ else
+ discon(-1);
+ discon(c->nlab|(e->ne<<16));
+ for(i = 0; i < c->nlab; i++){
+ lab = &c->labs[i];
+ d = lab->start->decl;
+ if(lab->start->ty->kind == Texception)
+ d = d->init->decl;
+ Bprint(bout, "%s", d->sym->name);
+ Bputc(bout, '\0');
+ discon(lab->inst->pc);
+ }
+ if(c->iwild == nil)
+ discon(-1);
+ else
+ discon(c->iwild->pc);
+ }
+ discon(0);
+}
+
+static void
+disbyte(long off, int v)
+{
+ disflush(DEFB, off, 1);
+ cache[ncached++] = v;
+ ndatum++;
+}
+
+static void
+disbytes(long off, void *v, int n)
+{
+ disflush(DEFB, off, n);
+ memmove(&cache[ncached], v, n);
+ ncached += n;
+ ndatum += n;
+}
+
+static void
+disint(long off, long v)
+{
+ disflush(DEFW, off, IBY2WD);
+ cache[ncached++] = v >> 24;
+ cache[ncached++] = v >> 16;
+ cache[ncached++] = v >> 8;
+ cache[ncached++] = v;
+ ndatum++;
+}
+
+static void
+disbig(long off, Long v)
+{
+ ulong iv;
+
+ disflush(DEFL, off, IBY2LG);
+ iv = v >> 32;
+ cache[ncached++] = iv >> 24;
+ cache[ncached++] = iv >> 16;
+ cache[ncached++] = iv >> 8;
+ cache[ncached++] = iv;
+ iv = v;
+ cache[ncached++] = iv >> 24;
+ cache[ncached++] = iv >> 16;
+ cache[ncached++] = iv >> 8;
+ cache[ncached++] = iv;
+ ndatum++;
+}
+
+static void
+disreal(long off, Real v)
+{
+ ulong bv[2];
+ ulong iv;
+
+ disflush(DEFF, off, IBY2LG);
+ dtocanon(v, bv);
+ iv = bv[0];
+ cache[ncached++] = iv >> 24;
+ cache[ncached++] = iv >> 16;
+ cache[ncached++] = iv >> 8;
+ cache[ncached++] = iv;
+ iv = bv[1];
+ cache[ncached++] = iv >> 24;
+ cache[ncached++] = iv >> 16;
+ cache[ncached++] = iv >> 8;
+ cache[ncached++] = iv;
+ ndatum++;
+}
+
+static void
+disstring(long offset, Sym *sym)
+{
+ disflush(-1, -1, 0);
+ disdata(DEFS, sym->len);
+ discon(offset);
+ Bwrite(bout, sym->name, sym->len);
+}
+
+static void
+disflush(int kind, long off, long size)
+{
+ if(kind != lastkind || off != lastoff){
+ if(lastkind != -1 && ncached){
+ disdata(lastkind, ndatum);
+ discon(startoff);
+ Bwrite(bout, cache, ncached);
+ }
+ startoff = off;
+ lastkind = kind;
+ ncached = 0;
+ ndatum = 0;
+ }
+ lastoff = off + size;
+ while(kind >= 0 && ncached + size >= lencache){
+ lencache = ncached+1024;
+ cache = reallocmem(cache, lencache);
+ }
+}
+
+static int dismode[Aend] = {
+ /* Aimm */ AIMM,
+ /* Amp */ AMP,
+ /* Ampind */ AMP|AIND,
+ /* Afp */ AFP,
+ /* Afpind */ AFP|AIND,
+ /* Apc */ AIMM,
+ /* Adesc */ AIMM,
+ /* Aoff */ AIMM,
+ /* Anoff */ AIMM,
+ /* Aerr */ AXXX,
+ /* Anone */ AXXX,
+ /* Aldt */ AIMM,
+};
+
+static int disregmode[Aend] = {
+ /* Aimm */ AXIMM,
+ /* Amp */ AXINM,
+ /* Ampind */ AXNON,
+ /* Afp */ AXINF,
+ /* Afpind */ AXNON,
+ /* Apc */ AXIMM,
+ /* Adesc */ AXIMM,
+ /* Aoff */ AXIMM,
+ /* Anoff */ AXIMM,
+ /* Aerr */ AXNON,
+ /* Anone */ AXNON,
+ /* Aldt */ AXIMM,
+};
+
+enum
+{
+ MAXCON = 4,
+ MAXADDR = 2*MAXCON,
+ MAXINST = 3*MAXADDR+2,
+ NIBUF = 1024
+};
+
+static uchar *ibuf;
+static int nibuf;
+
+void
+disinst(Inst *in)
+{
+ vlong fileoff;
+
+ fileoff = Boffset(bout);
+ ibuf = allocmem(NIBUF);
+ nibuf = 0;
+ for(; in != nil; in = in->next){
+ if(in->op == INOOP)
+ continue;
+ if(nibuf >= NIBUF-MAXINST){
+ Bwrite(bout, ibuf, nibuf);
+ nibuf = 0;
+ }
+ ibuf[nibuf++] = in->op;
+ ibuf[nibuf++] = SRC(dismode[in->sm]) | DST(dismode[in->dm]) | disregmode[in->mm];
+ if(in->mm != Anone)
+ disaddr(in->mm, &in->m);
+ if(in->sm != Anone)
+ disaddr(in->sm, &in->s);
+ if(in->dm != Anone)
+ disaddr(in->dm, &in->d);
+ }
+ if(nibuf > 0)
+ Bwrite(bout, ibuf, nibuf);
+ free(ibuf);
+ ibuf = nil;
+
+ if(debug['s'])
+ print("%lld instruction bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+disaddr(int m, Addr *a)
+{
+ long val;
+
+ val = 0;
+ switch(m){
+ case Anone:
+ case Aerr:
+ default:
+ break;
+ case Aimm:
+ case Apc:
+ case Adesc:
+ val = a->offset;
+ break;
+ case Aoff:
+ val = a->decl->iface->offset;
+ break;
+ case Anoff:
+ val = -(a->decl->iface->offset+1);
+ break;
+ case Afp:
+ case Amp:
+ case Aldt:
+ val = a->reg;
+ break;
+ case Afpind:
+ case Ampind:
+ disbcon(a->reg);
+ val = a->offset;
+ break;
+ }
+ disbcon(val);
+}
+
+void
+disbcon(long val)
+{
+ if(val >= -64 && val <= 63){
+ ibuf[nibuf++] = val & ~0x80;
+ return;
+ }
+ if(val >= -8192 && val <= 8191){
+ ibuf[nibuf++] = val>>8 & ~0xC0 | 0x80;
+ ibuf[nibuf++] = val;
+ return;
+ }
+ if(val < 0 && ((val >> 29) & 7) != 7
+ || val > 0 && (val >> 29) != 0)
+ fatal("overflow in constant 16r%lux", val);
+ ibuf[nibuf++] = val>>24 | 0xC0;
+ ibuf[nibuf++] = val>>16;
+ ibuf[nibuf++] = val>>8;
+ ibuf[nibuf++] = val;
+}
--- /dev/null
+++ b/limbo/dtocanon.c
@@ -1,0 +1,35 @@
+#include "limbo.h"
+
+void
+dtocanon(double f, ulong v[])
+{
+ union { double d; ulong ul[2]; } a;
+
+ a.d = 1.;
+ if(a.ul[0]){
+ a.d = f;
+ v[0] = a.ul[0];
+ v[1] = a.ul[1];
+ }else{
+ a.d = f;
+ v[0] = a.ul[1];
+ v[1] = a.ul[0];
+ }
+}
+
+double
+canontod(ulong v[2])
+{
+ union { double d; unsigned long ul[2]; } a;
+
+ a.d = 1.;
+ if(a.ul[0]) {
+ a.ul[0] = v[0];
+ a.ul[1] = v[1];
+ }
+ else {
+ a.ul[1] = v[0];
+ a.ul[0] = v[1];
+ }
+ return a.d;
+}
--- /dev/null
+++ b/limbo/ecom.c
@@ -1,0 +1,2558 @@
+#include "limbo.h"
+
+static Node* putinline(Node*);
+static void fpcall(Src*, int, Node*, Node*);
+
+void
+optabinit(void)
+{
+ int i;
+
+ for(i = 0; setisbyteinst[i] >= 0; i++)
+ isbyteinst[setisbyteinst[i]] = 1;
+
+ for(i = 0; setisused[i] >= 0; i++)
+ isused[setisused[i]] = 1;
+
+ for(i = 0; setsideeffect[i] >= 0; i++)
+ sideeffect[setsideeffect[i]] = 1;
+
+ opind[Tbyte] = 1;
+ opind[Tint] = 2;
+ opind[Tbig] = 3;
+ opind[Treal] = 4;
+ opind[Tstring] = 5;
+ opind[Tfix] = 6;
+
+ opcommute[Oeq] = Oeq;
+ opcommute[Oneq] = Oneq;
+ opcommute[Olt] = Ogt;
+ opcommute[Ogt] = Olt;
+ opcommute[Ogeq] = Oleq;
+ opcommute[Oleq] = Ogeq;
+ opcommute[Oadd] = Oadd;
+ opcommute[Omul] = Omul;
+ opcommute[Oxor] = Oxor;
+ opcommute[Oor] = Oor;
+ opcommute[Oand] = Oand;
+
+ oprelinvert[Oeq] = Oneq;
+ oprelinvert[Oneq] = Oeq;
+ oprelinvert[Olt] = Ogeq;
+ oprelinvert[Ogt] = Oleq;
+ oprelinvert[Ogeq] = Olt;
+ oprelinvert[Oleq] = Ogt;
+
+ isrelop[Oeq] = 1;
+ isrelop[Oneq] = 1;
+ isrelop[Olt] = 1;
+ isrelop[Oleq] = 1;
+ isrelop[Ogt] = 1;
+ isrelop[Ogeq] = 1;
+ isrelop[Oandand] = 1;
+ isrelop[Ooror] = 1;
+ isrelop[Onot] = 1;
+
+ precasttab[Tstring][Tbyte] = tint;
+ precasttab[Tbyte][Tstring] = tint;
+ precasttab[Treal][Tbyte] = tint;
+ precasttab[Tbyte][Treal] = tint;
+ precasttab[Tbig][Tbyte] = tint;
+ precasttab[Tbyte][Tbig] = tint;
+ precasttab[Tfix][Tbyte] = tint;
+ precasttab[Tbyte][Tfix] = tint;
+ precasttab[Tbig][Tfix] = treal;
+ precasttab[Tfix][Tbig] = treal;
+ precasttab[Tstring][Tfix] = treal;
+ precasttab[Tfix][Tstring] = treal;
+
+ casttab[Tint][Tint] = IMOVW;
+ casttab[Tbig][Tbig] = IMOVL;
+ casttab[Treal][Treal] = IMOVF;
+ casttab[Tbyte][Tbyte] = IMOVB;
+ casttab[Tstring][Tstring] = IMOVP;
+ casttab[Tfix][Tfix] = ICVTXX; /* never same type */
+
+ casttab[Tint][Tbyte] = ICVTWB;
+ casttab[Tint][Treal] = ICVTWF;
+ casttab[Tint][Tstring] = ICVTWC;
+ casttab[Tint][Tfix] = ICVTXX;
+ casttab[Tbyte][Tint] = ICVTBW;
+ casttab[Treal][Tint] = ICVTFW;
+ casttab[Tstring][Tint] = ICVTCW;
+ casttab[Tfix][Tint] = ICVTXX;
+
+ casttab[Tint][Tbig] = ICVTWL;
+ casttab[Treal][Tbig] = ICVTFL;
+ casttab[Tstring][Tbig] = ICVTCL;
+ casttab[Tbig][Tint] = ICVTLW;
+ casttab[Tbig][Treal] = ICVTLF;
+ casttab[Tbig][Tstring] = ICVTLC;
+
+ casttab[Treal][Tstring] = ICVTFC;
+ casttab[Tstring][Treal] = ICVTCF;
+
+ casttab[Treal][Tfix] = ICVTFX;
+ casttab[Tfix][Treal] = ICVTXF;
+
+ casttab[Tstring][Tarray] = ICVTCA;
+ casttab[Tarray][Tstring] = ICVTAC;
+
+ /*
+ * placeholders; fixed in precasttab
+ */
+ casttab[Tbyte][Tstring] = 0xff;
+ casttab[Tstring][Tbyte] = 0xff;
+ casttab[Tbyte][Treal] = 0xff;
+ casttab[Treal][Tbyte] = 0xff;
+ casttab[Tbyte][Tbig] = 0xff;
+ casttab[Tbig][Tbyte] = 0xff;
+ casttab[Tfix][Tbyte] = 0xff;
+ casttab[Tbyte][Tfix] = 0xff;
+ casttab[Tfix][Tbig] = 0xff;
+ casttab[Tbig][Tfix] = 0xff;
+ casttab[Tfix][Tstring] = 0xff;
+ casttab[Tstring][Tfix] = 0xff;
+}
+
+/*
+ * global variable and constant initialization checking
+ */
+int
+vcom(Decl *ids)
+{
+ Decl *v;
+ int ok;
+
+ ok = 1;
+ for(v = ids; v != nil; v = v->next)
+ ok &= varcom(v);
+ for(v = ids; v != nil; v = v->next)
+ v->init = simplify(v->init);
+ return ok;
+}
+
+Node*
+simplify(Node *n)
+{
+ if(n == nil)
+ return nil;
+ if(debug['F'])
+ print("simplify %n\n", n);
+ n = efold(rewrite(n));
+ if(debug['F'])
+ print("simplified %n\n", n);
+ return n;
+}
+
+static int
+isfix(Node *n)
+{
+ if(n->ty->kind == Tint || n->ty->kind == Tfix){
+ if(n->op == Ocast)
+ return n->left->ty->kind == Tint || n->left->ty->kind == Tfix;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * rewrite an expression to make it easiser to compile,
+ * or give the correct results
+ */
+Node*
+rewrite(Node *n)
+{
+ Long v;
+ Type *t;
+ Decl *d;
+ Node *nn, *left, *right;
+
+ if(n == nil)
+ return nil;
+
+ left = n->left;
+ right = n->right;
+
+ /*
+ * rewrites
+ */
+ switch(n->op){
+ case Oname:
+ d = n->decl;
+ if(d->importid != nil){
+ left = mkbin(Omdot, dupn(1, &n->src, d->eimport), mkdeclname(&n->src, d->importid));
+ left->ty = n->ty;
+ return rewrite(left);
+ }
+ if((t = n->ty)->kind == Texception){
+ if(t->cons)
+ fatal("cons in rewrite Oname");
+ n = mkbin(Oadd, n, mkconst(&n->src, 2*IBY2WD));
+ n = mkunary(Oind, n);
+ n->ty = t;
+ n->left->ty = n->left->left->ty = tint;
+ return rewrite(n);
+ }
+ break;
+ case Odas:
+ n->op = Oas;
+ return rewrite(n);
+ case Oneg:
+ n->left = rewrite(left);
+ if(n->ty == treal)
+ break;
+ left = n->left;
+ n->right = left;
+ n->left = mkconst(&n->src, 0);
+ n->left->ty = n->ty;
+ n->op = Osub;
+ break;
+ case Ocomp:
+ v = 0;
+ v = ~v;
+ n->right = mkconst(&n->src, v);
+ n->right->ty = n->ty;
+ n->left = rewrite(left);
+ n->op = Oxor;
+ break;
+ case Oinc:
+ case Odec:
+ case Opreinc:
+ case Opredec:
+ n->left = rewrite(left);
+ switch(n->ty->kind){
+ case Treal:
+ n->right = mkrconst(&n->src, 1.0);
+ break;
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Tfix:
+ n->right = mkconst(&n->src, 1);
+ n->right->ty = n->ty;
+ break;
+ default:
+ fatal("can't rewrite inc/dec %n", n);
+ break;
+ }
+ if(n->op == Opreinc)
+ n->op = Oaddas;
+ else if(n->op == Opredec)
+ n->op = Osubas;
+ break;
+ case Oslice:
+ if(right->left->op == Onothing)
+ right->left = mkconst(&right->left->src, 0);
+ n->left = rewrite(left);
+ n->right = rewrite(right);
+ break;
+ case Oindex:
+ n->op = Oindx;
+ n->left = rewrite(left);
+ n->right = rewrite(right);
+ n = mkunary(Oind, n);
+ n->ty = n->left->ty;
+ n->left->ty = tint;
+ break;
+ case Oload:
+ n->right = mkn(Oname, nil, nil);
+ n->right->src = n->left->src;
+ n->right->decl = n->ty->tof->decl;
+ n->right->ty = n->ty;
+ n->left = rewrite(left);
+ break;
+ case Ocast:
+ if(left->ty->kind == Texception){
+ n = rewrite(left);
+ break;
+ }
+ n->op = Ocast;
+ t = precasttab[left->ty->kind][n->ty->kind];
+ if(t != nil){
+ n->left = mkunary(Ocast, left);
+ n->left->ty = t;
+ return rewrite(n);
+ }
+ n->left = rewrite(left);
+ break;
+ case Oraise:
+ if(left->ty == tstring)
+ {}
+ else if(!left->ty->cons)
+ break;
+ else if(left->op != Ocall || left->left->ty->kind == Tfn){
+ left = mkunary(Ocall, left);
+ left->ty = left->left->ty;
+ }
+ n->left = rewrite(left);
+ break;
+ case Ocall:
+ t = left->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ if(t->kind == Tfn){
+if(debug['U']) print("call %n\n", left);
+ if(left->ty->kind == Tref){ /* call by function reference */
+ n->left = mkunary(Oind, left);
+ n->left->ty = t;
+ return rewrite(n);
+ }
+ d = nil;
+ if(left->op == Oname)
+ d = left->decl;
+ else if(left->op == Omdot && left->right->op == Odot)
+ d = left->right->right->decl;
+ else if(left->op == Omdot || left->op == Odot)
+ d = left->right->decl;
+ else if(left->op != Oind)
+ fatal("cannot deal with call %n in rewrite", n);
+ if(ispoly(d))
+ addfnptrs(d, 0);
+ n->left = rewrite(left);
+ if(right != nil)
+ n->right = rewrite(right);
+ if(d != nil && d->caninline == 1)
+ n = simplify(putinline(n));
+ break;
+ }
+ switch(n->ty->kind){
+ case Tref:
+ n = mkunary(Oref, n);
+ n->ty = n->left->ty;
+ n->left->ty = n->left->ty->tof;
+ n->left->left->ty = n->left->ty;
+ return rewrite(n);
+ case Tadt:
+ n->op = Otuple;
+ n->right = nil;
+ if(n->ty->tags != nil){
+ n->left = nn = mkunary(Oseq, mkconst(&n->src, left->right->decl->tag));
+ if(right != nil){
+ nn->right = right;
+ nn->src.stop = right->src.stop;
+ }
+ n->ty = left->right->decl->ty->tof;
+ }else
+ n->left = right;
+ return rewrite(n);
+ case Tadtpick:
+ n->op = Otuple;
+ n->right = nil;
+ n->left = nn = mkunary(Oseq, mkconst(&n->src, left->right->decl->tag));
+ if(right != nil){
+ nn->right = right;
+ nn->src.stop = right->src.stop;
+ }
+ n->ty = left->right->decl->ty->tof;
+ return rewrite(n);
+ case Texception:
+ if(!n->ty->cons)
+ return n->left;
+ if(left->op == Omdot){
+ left->right->ty = left->ty;
+ left = left->right;
+ }
+ n->op = Otuple;
+ n->right = nil;
+ n->left = nn = mkunary(Oseq, left->decl->init);
+ nn->right = mkunary(Oseq, mkconst(&n->src, 0));
+ nn->right->right = right;
+ n->ty = mkexbasetype(n->ty);
+ n = mkunary(Oref, n);
+ n->ty = internaltype(mktype(&n->src.start, &n->src.stop, Tref, t, nil));
+ return rewrite(n);
+ default:
+ fatal("can't deal with %n in rewrite/Ocall", n);
+ break;
+ }
+ break;
+ case Omdot:
+ /*
+ * what about side effects from left?
+ */
+ d = right->decl;
+ switch(d->store){
+ case Dfn:
+ n->left = rewrite(left);
+ if(right->op == Odot){
+ n->right = dupn(1, &left->src, right->right);
+ n->right->ty = d->ty;
+ }
+ break;
+ case Dconst:
+ case Dtag:
+ case Dtype:
+ /* handled by fold */
+ return n;
+ case Dglobal:
+ right->op = Oconst;
+ right->val = d->offset;
+ right->ty = tint;
+
+ n->left = left = mkunary(Oind, left);
+ left->ty = tint;
+ n->op = Oadd;
+ n = mkunary(Oind, n);
+ n->ty = n->left->ty;
+ n->left->ty = tint;
+ n->left = rewrite(n->left);
+ return n;
+ case Darg:
+ return n;
+ default:
+ fatal("can't deal with %n in rewrite/Omdot", n);
+ break;
+ }
+ break;
+ case Odot:
+ /*
+ * what about side effects from left?
+ */
+ d = right->decl;
+ switch(d->store){
+ case Dfn:
+ if(right->left != nil){
+ n = mkbin(Omdot, dupn(1, &left->src, right->left), right);
+ right->left = nil;
+ n->ty = d->ty;
+ return rewrite(n);
+ }
+ if(left->ty->kind == Tpoly){
+ n = mkbin(Omdot, mkdeclname(&left->src, d->link), mkdeclname(&left->src, d->link->next));
+ n->ty = d->ty;
+ return rewrite(n);
+ }
+ n->op = Oname;
+ n->decl = d;
+ n->right = nil;
+ n->left = nil;
+ return n;
+ case Dconst:
+ case Dtag:
+ case Dtype:
+ /* handled by fold */
+ return n;
+ }
+ if(istuple(left))
+ return n; /* handled by fold */
+ right->op = Oconst;
+ right->val = d->offset;
+ right->ty = tint;
+
+ if(left->ty->kind != Tref){
+ n->left = mkunary(Oadr, left);
+ n->left->ty = tint;
+ }
+ n->op = Oadd;
+ n = mkunary(Oind, n);
+ n->ty = n->left->ty;
+ n->left->ty = tint;
+ n->left = rewrite(n->left);
+ return n;
+ case Oadr:
+ left = rewrite(left);
+ n->left = left;
+ if(left->op == Oind)
+ return left->left;
+ break;
+ case Otagof:
+ if(n->decl == nil){
+ n->op = Oind;
+ return rewrite(n);
+ }
+ return n;
+ case Omul:
+ case Odiv:
+ left = n->left = rewrite(left);
+ right = n->right = rewrite(right);
+ if(n->ty->kind == Tfix && isfix(left) && isfix(right)){
+ if(left->op == Ocast && tequal(left->ty, n->ty))
+ n->left = left->left;
+ if(right->op == Ocast && tequal(right->ty, n->ty))
+ n->right = right->left;
+ }
+ break;
+ case Oself:
+ if(newfnptr)
+ return n;
+ if(selfdecl == nil){
+ d = selfdecl = mkids(&n->src, enter(strdup(".self"), 5), tany, nil);
+ installids(Dglobal, d);
+ d->refs++;
+ }
+ nn = mkn(Oload, nil, nil);
+ nn->src = n->src;
+ nn->left = mksconst(&n->src, enterstring(strdup("$self"), 5));
+ nn->ty = impdecl->ty;
+ usetype(nn->ty);
+ usetype(nn->ty->tof);
+ nn = rewrite(nn);
+ nn->op = Oself;
+ return nn;
+ case Ofnptr:
+ if(n->flags == 0){
+ /* module */
+ if(left == nil)
+ left = mkn(Oself, nil, nil);
+ return rewrite(left);
+ }
+ right->flags = n->flags;
+ n = right;
+ d = n->decl;
+ if(n->flags == FNPTR2){
+ if(left != nil && left->op != Oname)
+ fatal("not Oname for addiface");
+ if(left == nil){
+ addiface(nil, d);
+ if(newfnptr)
+ n->flags |= FNPTRN;
+ }
+ else
+ addiface(left->decl, d); /* is this necessary ? */
+ n->ty = tint;
+ return n;
+ }
+ if(n->flags == FNPTRA){
+ n = mkdeclname(&n->src, d->link);
+ n->ty = tany;
+ return n;
+ }
+ if(n->flags == (FNPTRA|FNPTR2)){
+ n = mkdeclname(&n->src, d->link->next);
+ n->ty = tint;
+ return n;
+ }
+ break;
+ case Ochan:
+ if(left == nil)
+ left = n->left = mkconst(&n->src, 0);
+ n->left = rewrite(left);
+ break;
+ default:
+ n->left = rewrite(left);
+ n->right = rewrite(right);
+ break;
+ }
+
+ return n;
+}
+
+/*
+ * label a node with sethi-ullman numbers and addressablity
+ * genaddr interprets addable to generate operands,
+ * so a change here mandates a change there.
+ *
+ * addressable:
+ * const Rconst $value may also be Roff or Rdesc or Rnoff
+ * Asmall(local) Rreg value(FP)
+ * Asmall(global) Rmreg value(MP)
+ * ind(Rareg) Rreg value(FP)
+ * ind(Ramreg) Rmreg value(MP)
+ * ind(Rreg) Radr *value(FP)
+ * ind(Rmreg) Rmadr *value(MP)
+ * ind(Raadr) Radr value(value(FP))
+ * ind(Ramadr) Rmadr value(value(MP))
+ *
+ * almost addressable:
+ * adr(Rreg) Rareg
+ * adr(Rmreg) Ramreg
+ * add(const, Rareg) Rareg
+ * add(const, Ramreg) Ramreg
+ * add(const, Rreg) Raadr
+ * add(const, Rmreg) Ramadr
+ * add(const, Raadr) Raadr
+ * add(const, Ramadr) Ramadr
+ * adr(Radr) Raadr
+ * adr(Rmadr) Ramadr
+ *
+ * strangely addressable:
+ * fn Rpc
+ * mdot(module,exp) Rmpc
+ */
+Node*
+sumark(Node *n)
+{
+ Node *left, *right;
+ long v;
+
+ if(n == nil)
+ return nil;
+
+ n->temps = 0;
+ n->addable = Rcant;
+
+ left = n->left;
+ right = n->right;
+ if(left != nil){
+ sumark(left);
+ n->temps = left->temps;
+ }
+ if(right != nil){
+ sumark(right);
+ if(right->temps == n->temps)
+ n->temps++;
+ else if(right->temps > n->temps)
+ n->temps = right->temps;
+ }
+
+ switch(n->op){
+ case Oadr:
+ switch(left->addable){
+ case Rreg:
+ n->addable = Rareg;
+ break;
+ case Rmreg:
+ n->addable = Ramreg;
+ break;
+ case Radr:
+ n->addable = Raadr;
+ break;
+ case Rmadr:
+ n->addable = Ramadr;
+ break;
+ }
+ break;
+ case Oind:
+ switch(left->addable){
+ case Rreg:
+ n->addable = Radr;
+ break;
+ case Rmreg:
+ n->addable = Rmadr;
+ break;
+ case Rareg:
+ n->addable = Rreg;
+ break;
+ case Ramreg:
+ n->addable = Rmreg;
+ break;
+ case Raadr:
+ n->addable = Radr;
+ break;
+ case Ramadr:
+ n->addable = Rmadr;
+ break;
+ }
+ break;
+ case Oname:
+ switch(n->decl->store){
+ case Darg:
+ case Dlocal:
+ n->addable = Rreg;
+ break;
+ case Dglobal:
+ n->addable = Rmreg;
+ if(LDT && n->decl->ty->kind == Tiface)
+ n->addable = Rldt;
+ break;
+ case Dtype:
+ /*
+ * check for inferface to load
+ */
+ if(n->decl->ty->kind == Tmodule)
+ n->addable = Rmreg;
+ break;
+ case Dfn:
+ if(n->flags & FNPTR){
+ if(n->flags == FNPTR2)
+ n->addable = Roff;
+ else if(n->flags == (FNPTR2|FNPTRN))
+ n->addable = Rnoff;
+ }
+ else
+ n->addable = Rpc;
+ break;
+ default:
+ fatal("cannot deal with %K in Oname in %n", n->decl, n);
+ break;
+ }
+ break;
+ case Omdot:
+ n->addable = Rmpc;
+ break;
+ case Oconst:
+ switch(n->ty->kind){
+ case Tint:
+ case Tfix:
+ v = n->val;
+ if(v < 0 && ((v >> 29) & 0x7) != 7
+ || v > 0 && (v >> 29) != 0){
+ n->decl = globalconst(n);
+ n->addable = Rmreg;
+ }else
+ n->addable = Rconst;
+ break;
+ case Tbig:
+ n->decl = globalBconst(n);
+ n->addable = Rmreg;
+ break;
+ case Tbyte:
+ n->decl = globalbconst(n);
+ n->addable = Rmreg;
+ break;
+ case Treal:
+ n->decl = globalfconst(n);
+ n->addable = Rmreg;
+ break;
+ case Tstring:
+ n->decl = globalsconst(n);
+ n->addable = Rmreg;
+ break;
+ default:
+ fatal("cannot %T const in sumark", n->ty);
+ break;
+ }
+ break;
+ case Oadd:
+ if(right->addable == Rconst){
+ switch(left->addable){
+ case Rareg:
+ n->addable = Rareg;
+ break;
+ case Ramreg:
+ n->addable = Ramreg;
+ break;
+ case Rreg:
+ case Raadr:
+ n->addable = Raadr;
+ break;
+ case Rmreg:
+ case Ramadr:
+ n->addable = Ramadr;
+ break;
+ }
+ }
+ break;
+ }
+ if(n->addable < Rcant)
+ n->temps = 0;
+ else if(n->temps == 0)
+ n->temps = 1;
+ return n;
+}
+
+Node*
+mktn(Type *t)
+{
+ Node *n;
+
+ n = mkn(Oname, nil, nil);
+ usedesc(mktdesc(t));
+ n->ty = t;
+ n->decl = t->decl;
+ if(n->decl == nil)
+ fatal("mktn t %T nil decl", t);
+ n->addable = Rdesc;
+ return n;
+}
+
+/* does a tuple of the form (a, b, ...) form a contiguous block
+ * of memory on the stack when offsets are assigned later
+ * - only when (a, b, ...) := rhs and none of the names nil
+ * can we guarantee this
+ */
+static int
+tupblk0(Node *n, Decl **dd)
+{
+ Decl *d;
+ int nid;
+
+ switch(n->op){
+ case Otuple:
+ for(n = n->left; n != nil; n = n->right)
+ if(!tupblk0(n->left, dd))
+ return 0;
+ return 1;
+ case Oname:
+ if(n->decl == nildecl)
+ return 0;
+ d = *dd;
+ if(d != nil && d->next != n->decl)
+ return 0;
+ nid = n->decl->nid;
+ if(d == nil && nid == 1)
+ return 0;
+ if(d != nil && nid != 0)
+ return 0;
+ *dd = n->decl;
+ return 1;
+ }
+ return 0;
+}
+
+/* could force locals to be next to each other
+ * - need to shuffle locals list
+ * - later
+ */
+static Node*
+tupblk(Node *n)
+{
+ Decl *d;
+
+ if(n->op != Otuple)
+ return nil;
+ d = nil;
+ if(!tupblk0(n, &d))
+ return nil;
+ while(n->op == Otuple)
+ n = n->left->left;
+ if(n->op != Oname || n->decl->nid == 1)
+ fatal("bad tupblk");
+ return n;
+}
+
+/* for cprof */
+#define esrc(src, osrc, nto) (src != nil && nto != nil ? src : osrc)
+
+/*
+ * compile an expression with an implicit assignment
+ * note: you are not allowed to use to->src
+ *
+ * need to think carefully about the types used in moves
+ * it particular, it would be nice to gen movp rather than movc sometimes.
+ */
+Node*
+ecom(Src *src, Node *nto, Node *n)
+{
+ Node *left, *right, *tn;
+ Node tl, tr, tto, ttn;
+ Type *t, *tt;
+ Inst *p, *pp;
+ int op;
+
+ if(debug['e']){
+ print("ecom: %n\n", n);
+ if(nto != nil)
+ print("ecom to: %n\n", nto);
+ }
+
+ if(n->addable < Rcant){
+ /*
+ * think carefully about the type used here
+ */
+ if(nto != nil)
+ genmove(src, Mas, n->ty, n, nto);
+ return nto;
+ }
+
+ tl.decl = nil;
+ tr.decl = nil;
+ tto.decl = nil;
+ ttn.decl = nil;
+
+ left = n->left;
+ right = n->right;
+ op = n->op;
+ switch(op){
+ default:
+ case Oadr:
+ fatal("can't %n in ecom", n);
+ return nto;
+ case Oif:
+ p = bcom(left, 1, nil);
+ ecom(&right->left->src, nto, right->left);
+ if(right->right != nil){
+ pp = p;
+ p = genrawop(&right->left->src, IJMP, nil, nil, nil);
+ patch(pp, nextinst());
+ ecom(&right->right->src, nto, right->right);
+ }
+ patch(p, nextinst());
+ break;
+ case Ocomma:
+ tn = left->left;
+ ecom(&left->src, nil, left);
+ ecom(&right->src, nto, right);
+ tfree(tn);
+ break;
+ case Oname:
+ if(n->addable == Rpc){
+ if(nto != nil)
+ genmove(src, Mas, n->ty, n, nto);
+ return nto;
+ }
+ fatal("can't %n in ecom", n);
+ break;
+ case Onothing:
+ break;
+ case Oused:
+ if(nto != nil)
+ fatal("superfluous used %n to %n", left, nto);
+ talloc(&tto, left->ty, nil);
+ ecom(&left->src, &tto, left);
+ tfree(&tto);
+ break;
+ case Oas:
+ if(right->ty == tany)
+ right->ty = n->ty;
+ if(left->op == Oname && left->decl->ty == tany){
+ if(nto == nil)
+ nto = talloc(&tto, right->ty, nil);
+ left = nto;
+ nto = nil;
+ }
+ if(left->op == Oinds){
+ indsascom(src, nto, n);
+ tfree(&tto);
+ break;
+ }
+ if(left->op == Oslice){
+ slicelcom(src, nto, n);
+ tfree(&tto);
+ break;
+ }
+
+ if(left->op == Otuple){
+ if(!tupsaliased(right, left)){
+ if((tn = tupblk(left)) != nil){
+ tn->ty = n->ty;
+ ecom(&n->right->src, tn, right);
+ if(nto != nil)
+ genmove(src, Mas, n->ty, tn, nto);
+ tfree(&tto);
+ break;
+ }
+ if((tn = tupblk(right)) != nil){
+ tn->ty = n->ty;
+ tuplcom(tn, left);
+ if(nto != nil)
+ genmove(src, Mas, n->ty, tn, nto);
+ tfree(&tto);
+ break;
+ }
+ if(nto == nil && right->op == Otuple && left->ty->kind != Tadtpick){
+ tuplrcom(right, left);
+ tfree(&tto);
+ break;
+ }
+ }
+ if(right->addable >= Ralways
+ || right->op != Oname
+ || tupaliased(right, left)){
+ talloc(&tr, n->ty, nil);
+ ecom(&n->right->src, &tr, right);
+ right = &tr;
+ }
+ tuplcom(right, n->left);
+ if(nto != nil)
+ genmove(src, Mas, n->ty, right, nto);
+ tfree(&tr);
+ tfree(&tto);
+ break;
+ }
+
+ /*
+ * check for left/right aliasing and build right into temporary
+ */
+ if(right->op == Otuple){
+ if(!tupsaliased(left, right) && (tn = tupblk(right)) != nil){
+ tn->ty = n->ty;
+ right = tn;
+ }
+ else if(left->op != Oname || tupaliased(left, right))
+ right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+ }
+
+ /*
+ * think carefully about types here
+ */
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ ecom(&n->src, left, right);
+ if(nto != nil)
+ genmove(src, Mas, nto->ty, left, nto);
+ tfree(&tl);
+ tfree(&tr);
+ tfree(&tto);
+ break;
+ case Ochan:
+ if(left && left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ genchan(src, left, n->ty->tof, nto);
+ tfree(&tl);
+ break;
+ case Oinds:
+ if(right->addable < Ralways){
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else if(left->temps <= right->temps){
+ right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else{
+ left = eacom(left, &tl, nil);
+ right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+ }
+ genop(&n->src, op, left, right, nto);
+ tfree(&tl);
+ tfree(&tr);
+ break;
+ case Osnd:
+ if(right->addable < Rcant){
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ }else if(left->temps < right->temps){
+ right = eacom(right, &tr, nto);
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else{
+ left = eacom(left, &tl, nto);
+ right = eacom(right, &tr, nil);
+ }
+ p = genrawop(&n->src, ISEND, right, nil, left);
+ p->m.offset = n->ty->size; /* for optimizer */
+ if(nto != nil)
+ genmove(src, Mas, right->ty, right, nto);
+ tfree(&tl);
+ tfree(&tr);
+ break;
+ case Orcv:
+ if(nto == nil){
+ ecom(&n->src, talloc(&tto, n->ty, nil), n);
+ tfree(&tto);
+ return nil;
+ }
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ if(left->ty->kind == Tchan){
+ p = genrawop(src, IRECV, left, nil, nto);
+ p->m.offset = n->ty->size; /* for optimizer */
+ }else{
+ recvacom(src, nto, n);
+ }
+ tfree(&tl);
+ break;
+ case Ocons:
+ /*
+ * another temp which can go with analysis
+ */
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ if(!sameaddr(right, nto)){
+ ecom(&right->src, talloc(&tto, n->ty, nto), right);
+ genmove(src, Mcons, left->ty, left, &tto);
+ if(!sameaddr(&tto, nto))
+ genmove(src, Mas, nto->ty, &tto, nto);
+ }else
+ genmove(src, Mcons, left->ty, left, nto);
+ tfree(&tl);
+ tfree(&tto);
+ break;
+ case Ohd:
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ genmove(src, Mhd, nto->ty, left, nto);
+ tfree(&tl);
+ break;
+ case Otl:
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ genmove(src, Mtl, left->ty, left, nto);
+ tfree(&tl);
+ break;
+ case Otuple:
+ if((tn = tupblk(n)) != nil){
+ tn->ty = n->ty;
+ genmove(src, Mas, n->ty, tn, nto);
+ break;
+ }
+ tupcom(nto, n);
+ break;
+ case Oadd:
+ case Osub:
+ case Omul:
+ case Odiv:
+ case Omod:
+ case Oand:
+ case Oor:
+ case Oxor:
+ case Olsh:
+ case Orsh:
+ case Oexp:
+ /*
+ * check for 2 operand forms
+ */
+ if(sameaddr(nto, left)){
+ if(right->addable >= Rcant)
+ right = eacom(right, &tr, nto);
+ genop(src, op, right, nil, nto);
+ tfree(&tr);
+ break;
+ }
+
+ if(opcommute[op] && sameaddr(nto, right) && n->ty != tstring){
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ genop(src, opcommute[op], left, nil, nto);
+ tfree(&tl);
+ break;
+ }
+
+ if(right->addable < left->addable
+ && opcommute[op]
+ && n->ty != tstring){
+ op = opcommute[op];
+ left = right;
+ right = n->left;
+ }
+ if(left->addable < Ralways){
+ if(right->addable >= Rcant)
+ right = eacom(right, &tr, nto);
+ }else if(right->temps <= left->temps){
+ left = ecom(&left->src, talloc(&tl, left->ty, nto), left);
+ if(right->addable >= Rcant)
+ right = eacom(right, &tr, nil);
+ }else{
+ right = eacom(right, &tr, nto);
+ left = ecom(&left->src, talloc(&tl, left->ty, nil), left);
+ }
+
+ /*
+ * check for 2 operand forms
+ */
+ if(sameaddr(nto, left))
+ genop(src, op, right, nil, nto);
+ else if(opcommute[op] && sameaddr(nto, right) && n->ty != tstring)
+ genop(src, opcommute[op], left, nil, nto);
+ else
+ genop(src, op, right, left, nto);
+ tfree(&tl);
+ tfree(&tr);
+ break;
+ case Oaddas:
+ case Osubas:
+ case Omulas:
+ case Odivas:
+ case Omodas:
+ case Oexpas:
+ case Oandas:
+ case Ooras:
+ case Oxoras:
+ case Olshas:
+ case Orshas:
+ if(left->op == Oinds){
+ indsascom(src, nto, n);
+ break;
+ }
+ if(right->addable < Rcant){
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ }else if(left->temps < right->temps){
+ right = eacom(right, &tr, nto);
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else{
+ left = eacom(left, &tl, nto);
+ right = eacom(right, &tr, nil);
+ }
+ genop(&n->src, op, right, nil, left);
+ if(nto != nil)
+ genmove(src, Mas, left->ty, left, nto);
+ tfree(&tl);
+ tfree(&tr);
+ break;
+ case Olen:
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ op = -1;
+ t = left->ty;
+ if(t == tstring)
+ op = ILENC;
+ else if(t->kind == Tarray)
+ op = ILENA;
+ else if(t->kind == Tlist)
+ op = ILENL;
+ else
+ fatal("can't len %n", n);
+ genrawop(src, op, left, nil, nto);
+ tfree(&tl);
+ break;
+ case Oneg:
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ genop(&n->src, op, left, nil, nto);
+ tfree(&tl);
+ break;
+ case Oinc:
+ case Odec:
+ if(left->op == Oinds){
+ indsascom(src, nto, n);
+ break;
+ }
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ if(nto != nil)
+ genmove(src, Mas, left->ty, left, nto);
+ if(right->addable >= Rcant)
+ fatal("inc/dec amount not addressable: %n", n);
+ genop(&n->src, op, right, nil, left);
+ tfree(&tl);
+ break;
+ case Ospawn:
+ if(left->left->op == Oind)
+ fpcall(&n->src, op, left, nto);
+ else
+ callcom(&n->src, op, left, nto);
+ break;
+ case Oraise:
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ genrawop(&n->src, IRAISE, left, nil, nil);
+ tfree(&tl);
+ break;
+ case Ocall:
+ if(left->op == Oind)
+ fpcall(esrc(src, &n->src, nto), op, n, nto);
+ else
+ callcom(esrc(src, &n->src, nto), op, n, nto);
+ break;
+ case Oref:
+ t = left->ty;
+ if(left->op == Oname && left->decl->store == Dfn || left->op == Omdot && left->right->op == Oname && left->right->decl->store == Dfn){ /* create a function reference */
+ Decl *d;
+ Node *mod, *ind;
+
+ d = left->decl;
+ if(left->op == Omdot){
+ d = left->right->decl;
+ mod = left->left;
+ }
+ else if(d->eimport != nil)
+ mod = d->eimport;
+ else{
+ mod = rewrite(mkn(Oself, nil, nil));
+ addiface(nil, d);
+ }
+ sumark(mod);
+ talloc(&tto, n->ty, nto);
+ genrawop(src, INEW, mktn(usetype(tfnptr)), nil, &tto);
+ tr.src = *src;
+ tr.op = Oind;
+ tr.left = &tto;
+ tr.right = nil;
+ tr.ty = tany;
+ sumark(&tr);
+ ecom(src, &tr, mod);
+ ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, &tto), mkconst(src, IBY2WD)));
+ ind->ty = ind->left->ty = ind->left->right->ty = tint;
+ tr.op = Oas;
+ tr.left = ind;
+ tr.right = mkdeclname(src, d);
+ tr.ty = tr.right->ty = tint;
+ sumark(&tr);
+ tr.right->addable = mod->op == Oself && newfnptr ? Rnoff : Roff;
+ ecom(src, nil, &tr);
+ if(!sameaddr(&tto, nto))
+ genmove(src, Mas, n->ty, &tto, nto);
+ tfree(&tto);
+ break;
+ }
+ if(left->op == Oname && left->decl->store == Dtype){
+ genrawop(src, INEW, mktn(t), nil, nto);
+ break;
+ }
+ if(t->kind == Tadt && t->tags != nil){
+ pickdupcom(src, nto, left);
+ break;
+ }
+
+ tt = t;
+ if(left->op == Oconst && left->decl->store == Dtag)
+ t = left->decl->ty->tof;
+ /*
+ * could eliminate temp if to does not occur
+ * in tuple initializer
+ */
+ talloc(&tto, n->ty, nto);
+ genrawop(src, INEW, mktn(t), nil, &tto);
+ tr.op = Oind;
+ tr.left = &tto;
+ tr.right = nil;
+ tr.ty = tt;
+ sumark(&tr);
+ ecom(src, &tr, left);
+ if(!sameaddr(&tto, nto))
+ genmove(src, Mas, n->ty, &tto, nto);
+ tfree(&tto);
+ break;
+ case Oload:
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ talloc(&tr, tint, nil);
+ if(LDT)
+ genrawop(src, ILOAD, left, right, nto);
+ else{
+ genrawop(src, ILEA, right, nil, &tr);
+ genrawop(src, ILOAD, left, &tr, nto);
+ }
+ tfree(&tl);
+ tfree(&tr);
+ break;
+ case Ocast:
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ t = left->ty;
+ if(t->kind == Tfix || n->ty->kind == Tfix){
+ op = casttab[t->kind][n->ty->kind];
+ if(op == ICVTXX)
+ genfixcastop(src, op, left, nto);
+ else{
+ tn = sumark(mkrconst(src, scale2(t, n->ty)));
+ genrawop(src, op, left, tn, nto);
+ }
+ }
+ else
+ genrawop(src, casttab[t->kind][n->ty->kind], left, nil, nto);
+ tfree(&tl);
+ break;
+ case Oarray:
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ genrawop(esrc(src, &left->src, nto), arrayz ? INEWAZ : INEWA, left, mktn(n->ty->tof), nto);
+ if(right != nil)
+ arraycom(nto, right);
+ tfree(&tl);
+ break;
+ case Oslice:
+ tn = right->right;
+ right = right->left;
+
+ /*
+ * make the left node of the slice directly addressable
+ * therefore, if it's len is taken (via tn),
+ * left's tree won't be rewritten
+ */
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+
+ if(tn->op == Onothing){
+ tn = mkn(Olen, left, nil);
+ tn->src = *src;
+ tn->ty = tint;
+ sumark(tn);
+ }
+ if(tn->addable < Ralways){
+ if(right->addable >= Rcant)
+ right = eacom(right, &tr, nil);
+ }else if(right->temps <= tn->temps){
+ tn = ecom(&tn->src, talloc(&ttn, tn->ty, nil), tn);
+ if(right->addable >= Rcant)
+ right = eacom(right, &tr, nil);
+ }else{
+ right = eacom(right, &tr, nil);
+ tn = ecom(&tn->src, talloc(&ttn, tn->ty, nil), tn);
+ }
+ op = ISLICEA;
+ if(nto->ty == tstring)
+ op = ISLICEC;
+
+ /*
+ * overwrite the destination last,
+ * since it might be used in computing the slice bounds
+ */
+ if(!sameaddr(left, nto))
+ ecom(&left->src, nto, left);
+
+ genrawop(src, op, right, tn, nto);
+ tfree(&tl);
+ tfree(&tr);
+ tfree(&ttn);
+ break;
+ case Oindx:
+ if(right->addable < Rcant){
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ }else if(left->temps < right->temps){
+ right = eacom(right, &tr, nto);
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else{
+ left = eacom(left, &tl, nto);
+ right = eacom(right, &tr, nil);
+ }
+ if(nto->addable >= Ralways)
+ nto = ecom(src, talloc(&tto, nto->ty, nil), nto);
+ op = IINDX;
+ switch(left->ty->tof->size){
+ case IBY2LG:
+ op = IINDL;
+ if(left->ty->tof == treal)
+ op = IINDF;
+ break;
+ case IBY2WD:
+ op = IINDW;
+ break;
+ case 1:
+ op = IINDB;
+ break;
+ }
+ genrawop(src, op, left, nto, right);
+ // array[] of {....} [index] frees array too early (before index value used)
+ // function(...) [index] frees array too early (before index value used)
+ if(tl.decl != nil)
+ tfreelater(&tl);
+ else
+ tfree(&tl);
+ tfree(&tr);
+ tfree(&tto);
+ break;
+ case Oind:
+ n = eacom(n, &tl, nto);
+ genmove(src, Mas, n->ty, n, nto);
+ tfree(&tl);
+ break;
+ case Onot:
+ case Oandand:
+ case Ooror:
+ case Oeq:
+ case Oneq:
+ case Olt:
+ case Oleq:
+ case Ogt:
+ case Ogeq:
+ p = bcom(n, 1, nil);
+ genmove(src, Mas, tint, sumark(mkconst(src, 1)), nto);
+ pp = genrawop(src, IJMP, nil, nil, nil);
+ patch(p, nextinst());
+ genmove(src, Mas, tint, sumark(mkconst(src, 0)), nto);
+ patch(pp, nextinst());
+ break;
+ case Oself:
+ if(newfnptr){
+ if(nto != nil)
+ genrawop(src, ISELF, nil, nil, nto);
+ break;
+ }
+ tn = sumark(mkdeclname(src, selfdecl));
+ p = genbra(src, Oneq, tn, sumark(mkdeclname(src, nildecl)));
+ n->op = Oload;
+ ecom(src, tn, n);
+ patch(p, nextinst());
+ genmove(src, Mas, n->ty, tn, nto);
+ break;
+ }
+ return nto;
+}
+
+/*
+ * compile exp n to yield an addressable expression
+ * use reg to build a temporary; if t is a temp, it is usable
+ * if dangle leaves the address dangling, generate into a temporary
+ * this should only happen with arrays
+ *
+ * note that 0adr's are strange as they are only used
+ * for calculating the addresses of fields within adt's.
+ * therefore an Oind is the parent or grandparent of the Oadr,
+ * and we pick off all of the cases where Oadr's argument is not
+ * addressable by looking from the Oind.
+ */
+Node*
+eacom(Node *n, Node *reg, Node *t)
+{
+ Node *left, *tn;
+
+ if(n->op == Ocomma){
+ tn = n->left->left;
+ ecom(&n->left->src, nil, n->left);
+ n = eacom(n->right, reg, t);
+ tfree(tn);
+ return n;
+ }
+
+ if(debug['e'] || debug['E'])
+ print("eacom: %n\n", n);
+
+ left = n->left;
+ if(n->op != Oind){
+ ecom(&n->src, talloc(reg, n->ty, t), n);
+ reg->src = n->src;
+ return reg;
+ }
+
+ if(left->op == Oadd && left->right->op == Oconst){
+ if(left->left->op == Oadr){
+ left->left->left = eacom(left->left->left, reg, t);
+ sumark(n);
+ if(n->addable >= Rcant)
+ fatal("eacom can't make node addressable: %n", n);
+ return n;
+ }
+ talloc(reg, left->left->ty, t);
+ ecom(&left->left->src, reg, left->left);
+ left->left->decl = reg->decl;
+ left->left->addable = Rreg;
+ left->left = reg;
+ left->addable = Raadr;
+ n->addable = Radr;
+ }else if(left->op == Oadr){
+ talloc(reg, left->left->ty, t);
+ ecom(&left->left->src, reg, left->left);
+
+ /*
+ * sleaze: treat the temp as the type of the field, not the enclosing structure
+ */
+ reg->ty = n->ty;
+ reg->src = n->src;
+ return reg;
+ }else{
+ talloc(reg, left->ty, t);
+ ecom(&left->src, reg, left);
+ n->left = reg;
+ n->addable = Radr;
+ }
+ return n;
+}
+
+/*
+ * compile an assignment to an array slice
+ */
+Node*
+slicelcom(Src *src, Node *nto, Node *n)
+{
+ Node *left, *right, *v;
+ Node tl, tr, tv, tu;
+
+ tl.decl = nil;
+ tr.decl = nil;
+ tv.decl = nil;
+ tu.decl = nil;
+
+ left = n->left->left;
+ right = n->left->right->left;
+ v = n->right;
+ if(right->addable < Ralways){
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ }else if(left->temps <= right->temps){
+ right = ecom(&right->src, talloc(&tr, right->ty, nto), right);
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else{
+ left = eacom(left, &tl, nil); /* dangle on right and v */
+ right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+ }
+
+ switch(n->op){
+ case Oas:
+ if(v->addable >= Rcant)
+ v = eacom(v, &tv, nil);
+ break;
+ }
+
+ genrawop(&n->src, ISLICELA, v, right, left);
+ if(nto != nil)
+ genmove(src, Mas, n->ty, left, nto);
+ tfree(&tl);
+ tfree(&tv);
+ tfree(&tr);
+ tfree(&tu);
+ return nto;
+}
+
+/*
+ * compile an assignment to a string location
+ */
+Node*
+indsascom(Src *src, Node *nto, Node *n)
+{
+ Node *left, *right, *u, *v;
+ Node tl, tr, tv, tu;
+
+ tl.decl = nil;
+ tr.decl = nil;
+ tv.decl = nil;
+ tu.decl = nil;
+
+ left = n->left->left;
+ right = n->left->right;
+ v = n->right;
+ if(right->addable < Ralways){
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nto);
+ }else if(left->temps <= right->temps){
+ right = ecom(&right->src, talloc(&tr, right->ty, nto), right);
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else{
+ left = eacom(left, &tl, nil); /* dangle on right and v */
+ right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+ }
+
+ switch(n->op){
+ case Oas:
+ if(v->addable >= Rcant)
+ v = eacom(v, &tv, nil);
+ break;
+ case Oinc:
+ case Odec:
+ if(v->addable >= Rcant)
+ fatal("inc/dec amount not addable");
+ u = talloc(&tu, tint, nil);
+ genop(&n->left->src, Oinds, left, right, u);
+ if(nto != nil)
+ genmove(src, Mas, n->ty, u, nto);
+ nto = nil;
+ genop(&n->src, n->op, v, nil, u);
+ v = u;
+ break;
+ case Oaddas:
+ case Osubas:
+ case Omulas:
+ case Odivas:
+ case Omodas:
+ case Oexpas:
+ case Oandas:
+ case Ooras:
+ case Oxoras:
+ case Olshas:
+ case Orshas:
+ if(v->addable >= Rcant)
+ v = eacom(v, &tv, nil);
+ u = talloc(&tu, tint, nil);
+ genop(&n->left->src, Oinds, left, right, u);
+ genop(&n->src, n->op, v, nil, u);
+ v = u;
+ break;
+ }
+
+ genrawop(&n->src, IINSC, v, right, left);
+ tfree(&tl);
+ tfree(&tv);
+ tfree(&tr);
+ tfree(&tu);
+ if(nto != nil)
+ genmove(src, Mas, n->ty, v, nto);
+ return nto;
+}
+
+void
+callcom(Src *src, int op, Node *n, Node *ret)
+{
+ Node frame, tadd, toff, pass, *a, *mod, *ind, *nfn, *args, tmod, tind, *tn;
+ Inst *in,*p;
+ Decl *d, *callee;
+ long off;
+ int iop;
+
+ args = n->right;
+ nfn = n->left;
+ switch(nfn->op){
+ case Odot:
+ callee = nfn->right->decl;
+ nfn->addable = Rpc;
+ break;
+ case Omdot:
+ callee = nfn->right->decl;
+ break;
+ case Oname:
+ callee = nfn->decl;
+ break;
+ default:
+ callee = nil;
+ fatal("bad call op in callcom");
+ }
+ if(nfn->addable != Rpc && nfn->addable != Rmpc)
+ fatal("can't gen call addresses");
+ if(nfn->ty->tof != tnone && ret == nil){
+ ecom(src, talloc(&tmod, nfn->ty->tof, nil), n);
+ tfree(&tmod);
+ return;
+ }
+ if(ispoly(callee))
+ addfnptrs(callee, 0);
+ if(nfn->ty->varargs){
+ nfn->decl = dupdecl(nfn->right->decl);
+ nfn->decl->desc = gendesc(nfn->right->decl, idoffsets(nfn->ty->ids, MaxTemp, MaxAlign), nfn->ty->ids);
+ }
+
+ talloc(&frame, tint, nil);
+
+ mod = nfn->left;
+ ind = nfn->right;
+ tmod.decl = tind.decl = nil;
+ if(nfn->addable == Rmpc){
+ if(mod->addable >= Rcant)
+ mod = eacom(mod, &tmod, nil); /* dangle always */
+ if(ind->op != Oname && ind->addable >= Ralways){
+ talloc(&tind, ind->ty, nil);
+ ecom(&ind->src, &tind, ind);
+ ind = &tind;
+ }
+ else if(ind->decl != nil && ind->decl->store != Darg)
+ ind->addable = Roff;
+ }
+
+ /*
+ * stop nested uncalled frames
+ * otherwise exception handling very complicated
+ */
+ for(a = args; a != nil; a = a->right){
+ if(hascall(a->left)){
+ tn = mkn(0, nil, nil);
+ talloc(tn, a->left->ty, nil);
+ ecom(&a->left->src, tn, a->left);
+ a->left = tn;
+ tn->flags |= TEMP;
+ }
+ }
+
+ /*
+ * allocate the frame
+ */
+ if(nfn->addable == Rmpc && !nfn->ty->varargs){
+ genrawop(src, IMFRAME, mod, ind, &frame);
+ }else if(nfn->op == Odot){
+ genrawop(src, IFRAME, nfn->left, nil, &frame);
+ }else{
+ in = genrawop(src, IFRAME, nil, nil, &frame);
+ in->sm = Adesc;
+ in->s.decl = nfn->decl;
+ }
+
+ /*
+ * build a fake node for the argument area
+ */
+ toff = znode;
+ tadd = znode;
+ pass = znode;
+ toff.op = Oconst;
+ toff.addable = Rconst;
+ toff.ty = tint;
+ tadd.op = Oadd;
+ tadd.addable = Raadr;
+ tadd.left = &frame;
+ tadd.right = &toff;
+ tadd.ty = tint;
+ pass.op = Oind;
+ pass.addable = Radr;
+ pass.left = &tadd;
+
+ /*
+ * compile all the args
+ */
+ d = nfn->ty->ids;
+ off = 0;
+ for(a = args; a != nil; a = a->right){
+ off = d->offset;
+ toff.val = off;
+ if(d->ty->kind == Tpoly)
+ pass.ty = a->left->ty;
+ else
+ pass.ty = d->ty;
+ ecom(&a->left->src, &pass, a->left);
+ d = d->next;
+ if(a->left->flags & TEMP)
+ tfree(a->left);
+ }
+ if(off > maxstack)
+ maxstack = off;
+
+ /*
+ * pass return value
+ */
+ if(ret != nil){
+ toff.val = REGRET*IBY2WD;
+ pass.ty = nfn->ty->tof;
+ p = genrawop(src, ILEA, ret, nil, &pass);
+ p->m.offset = ret->ty->size; /* for optimizer */
+ }
+
+ /*
+ * call it
+ */
+ if(nfn->addable == Rmpc){
+ iop = IMCALL;
+ if(op == Ospawn)
+ iop = IMSPAWN;
+ genrawop(src, iop, &frame, ind, mod);
+ tfree(&tmod);
+ tfree(&tind);
+ }else if(nfn->op == Odot){
+ iop = ICALL;
+ if(op == Ospawn)
+ iop = ISPAWN;
+ genrawop(src, iop, &frame, nil, nfn->right);
+ }else{
+ iop = ICALL;
+ if(op == Ospawn)
+ iop = ISPAWN;
+ in = genrawop(src, iop, &frame, nil, nil);
+ in->d.decl = nfn->decl;
+ in->dm = Apc;
+ }
+ tfree(&frame);
+}
+
+/*
+ * initialization code for arrays
+ * a must be addressable (< Rcant)
+ */
+void
+arraycom(Node *a, Node *elems)
+{
+ Node tindex, fake, tmp, ri, *e, *n, *q, *body, *wild;
+ Inst *top, *out;
+ /* Case *c; */
+
+ if(debug['A'])
+ print("arraycom: %n %n\n", a, elems);
+
+ /* c = elems->ty->cse; */
+ /* don't use c->wild in case we've been inlined */
+ wild = nil;
+ for(e = elems; e != nil; e = e->right)
+ for(q = e->left->left; q != nil; q = q->right)
+ if(q->left->op == Owild)
+ wild = e->left;
+ if(wild != nil)
+ arraydefault(a, wild->right);
+
+ tindex = znode;
+ fake = znode;
+ talloc(&tmp, tint, nil);
+ tindex.op = Oindx;
+ tindex.addable = Rcant;
+ tindex.left = a;
+ tindex.right = nil;
+ tindex.ty = tint;
+ fake.op = Oind;
+ fake.addable = Radr;
+ fake.left = &tmp;
+ fake.ty = a->ty->tof;
+
+ for(e = elems; e != nil; e = e->right){
+ /*
+ * just duplicate the initializer for Oor
+ */
+ for(q = e->left->left; q != nil; q = q->right){
+ if(q->left->op == Owild)
+ continue;
+
+ body = e->left->right;
+ if(q->right != nil)
+ body = dupn(0, &nosrc, body);
+ top = nil;
+ out = nil;
+ ri.decl = nil;
+ if(q->left->op == Orange){
+ /*
+ * for(i := q.left.left; i <= q.left.right; i++)
+ */
+ talloc(&ri, tint, nil);
+ ri.src = q->left->src;
+ ecom(&q->left->src, &ri, q->left->left);
+
+ /* i <= q.left.right; */
+ n = mkn(Oleq, &ri, q->left->right);
+ n->src = q->left->src;
+ n->ty = tint;
+ top = nextinst();
+ out = bcom(n, 1, nil);
+
+ tindex.right = &ri;
+ }else{
+ tindex.right = q->left;
+ }
+
+ tindex.addable = Rcant;
+ tindex.src = q->left->src;
+ ecom(&tindex.src, &tmp, &tindex);
+
+ ecom(&body->src, &fake, body);
+
+ if(q->left->op == Orange){
+ /* i++ */
+ n = mkbin(Oinc, &ri, sumark(mkconst(&ri.src, 1)));
+ n->ty = tint;
+ n->addable = Rcant;
+ ecom(&n->src, nil, n);
+
+ /* jump to test */
+ patch(genrawop(&q->left->src, IJMP, nil, nil, nil), top);
+ patch(out, nextinst());
+ tfree(&ri);
+ }
+ }
+ }
+ tfree(&tmp);
+}
+
+/*
+ * default initialization code for arrays.
+ * compiles to
+ * n = len a;
+ * while(n){
+ * n--;
+ * a[n] = elem;
+ * }
+ */
+void
+arraydefault(Node *a, Node *elem)
+{
+ Inst *out, *top;
+ Node n, e, *t;
+
+ if(debug['A'])
+ print("arraydefault: %n %n\n", a, elem);
+
+ t = mkn(Olen, a, nil);
+ t->src = elem->src;
+ t->ty = tint;
+ t->addable = Rcant;
+ talloc(&n, tint, nil);
+ n.src = elem->src;
+ ecom(&t->src, &n, t);
+
+ top = nextinst();
+ out = bcom(&n, 1, nil);
+
+ t = mkbin(Odec, &n, sumark(mkconst(&elem->src, 1)));
+ t->ty = tint;
+ t->addable = Rcant;
+ ecom(&t->src, nil, t);
+
+ e.decl = nil;
+ if(elem->addable >= Rcant)
+ elem = eacom(elem, &e, nil);
+
+ t = mkn(Oindx, a, &n);
+ t->src = elem->src;
+ t = mkbin(Oas, mkunary(Oind, t), elem);
+ t->ty = elem->ty;
+ t->left->ty = elem->ty;
+ t->left->left->ty = tint;
+ sumark(t);
+ ecom(&t->src, nil, t);
+
+ patch(genrawop(&t->src, IJMP, nil, nil, nil), top);
+
+ tfree(&n);
+ tfree(&e);
+ patch(out, nextinst());
+}
+
+void
+tupcom(Node *nto, Node *n)
+{
+ Node tadr, tadd, toff, fake, *e;
+ Decl *d;
+
+ if(debug['Y'])
+ print("tupcom %n\nto %n\n", n, nto);
+
+ /*
+ * build a fake node for the tuple
+ */
+ toff = znode;
+ tadd = znode;
+ fake = znode;
+ tadr = znode;
+ toff.op = Oconst;
+ toff.ty = tint;
+ tadr.op = Oadr;
+ tadr.left = nto;
+ tadr.ty = tint;
+ tadd.op = Oadd;
+ tadd.left = &tadr;
+ tadd.right = &toff;
+ tadd.ty = tint;
+ fake.op = Oind;
+ fake.left = &tadd;
+ sumark(&fake);
+ if(fake.addable >= Rcant)
+ fatal("tupcom: bad value exp %n", &fake);
+
+ /*
+ * compile all the exps
+ */
+ d = n->ty->ids;
+ for(e = n->left; e != nil; e = e->right){
+ toff.val = d->offset;
+ fake.ty = d->ty;
+ ecom(&e->left->src, &fake, e->left);
+ d = d->next;
+ }
+}
+
+void
+tuplcom(Node *n, Node *nto)
+{
+ Node tadr, tadd, toff, fake, tas, *e, *as;
+ Decl *d;
+
+ if(debug['Y'])
+ print("tuplcom %n\nto %n\n", n, nto);
+
+ /*
+ * build a fake node for the tuple
+ */
+ toff = znode;
+ tadd = znode;
+ fake = znode;
+ tadr = znode;
+ toff.op = Oconst;
+ toff.ty = tint;
+ tadr.op = Oadr;
+ tadr.left = n;
+ tadr.ty = tint;
+ tadd.op = Oadd;
+ tadd.left = &tadr;
+ tadd.right = &toff;
+ tadd.ty = tint;
+ fake.op = Oind;
+ fake.left = &tadd;
+ sumark(&fake);
+ if(fake.addable >= Rcant)
+ fatal("tuplcom: bad value exp for %n", &fake);
+
+ /*
+ * compile all the exps
+ */
+ d = nto->ty->ids;
+ if(nto->ty->kind == Tadtpick)
+ d = nto->ty->tof->ids->next;
+ for(e = nto->left; e != nil; e = e->right){
+ as = e->left;
+ if(as->op != Oname || as->decl != nildecl){
+ toff.val = d->offset;
+ fake.ty = d->ty;
+ fake.src = as->src;
+ if(as->addable < Rcant)
+ genmove(&as->src, Mas, d->ty, &fake, as);
+ else{
+ tas.op = Oas;
+ tas.ty = d->ty;
+ tas.src = as->src;
+ tas.left = as;
+ tas.right = &fake;
+ tas.addable = Rcant;
+ ecom(&tas.src, nil, &tas);
+ }
+ }
+ d = d->next;
+ }
+}
+
+void
+tuplrcom(Node *n, Node *nto)
+{
+ Node *s, *d, tas;
+ Decl *de;
+
+ de = nto->ty->ids;
+ for(s = n->left, d = nto->left; s != nil && d != nil; s = s->right, d = d->right){
+ if(d->left->op != Oname || d->left->decl != nildecl){
+ tas.op = Oas;
+ tas.ty = de->ty;
+ tas.src = s->left->src;
+ tas.left = d->left;
+ tas.right = s->left;
+ sumark(&tas);
+ ecom(&tas.src, nil, &tas);
+ }
+ de = de->next;
+ }
+ if(s != nil || d != nil)
+ fatal("tuplrcom");
+}
+
+/*
+ * boolean compiler
+ * fall through when condition == true
+ */
+Inst*
+bcom(Node *n, int iftrue, Inst *b)
+{
+ Inst *bb;
+ Node tl, tr, *t, *left, *right, *tn;
+ int op;
+
+ if(n->op == Ocomma){
+ tn = n->left->left;
+ ecom(&n->left->src, nil, n->left);
+ bb = bcom(n->right, iftrue, b);
+ tfree(tn);
+ return bb;
+ }
+
+ if(debug['b'])
+ print("bcom %n %d\n", n, iftrue);
+
+ left = n->left;
+ right = n->right;
+ op = n->op;
+
+ switch(op){
+ case Onothing:
+ return b;
+ case Onot:
+ return bcom(n->left, !iftrue, b);
+ case Oandand:
+ if(!iftrue)
+ return oror(n, iftrue, b);
+ return andand(n, iftrue, b);
+ case Ooror:
+ if(!iftrue)
+ return andand(n, iftrue, b);
+ return oror(n, iftrue, b);
+ case Ogt:
+ case Ogeq:
+ case Oneq:
+ case Oeq:
+ case Olt:
+ case Oleq:
+ break;
+ default:
+ if(n->ty->kind == Tint){
+ right = mkconst(&n->src, 0);
+ right->addable = Rconst;
+ left = n;
+ op = Oneq;
+ break;
+ }
+ fatal("can't bcom %n", n);
+ return b;
+ }
+
+ if(iftrue)
+ op = oprelinvert[op];
+
+ if(left->addable < right->addable){
+ t = left;
+ left = right;
+ right = t;
+ op = opcommute[op];
+ }
+
+ tl.decl = nil;
+ tr.decl = nil;
+ if(right->addable < Ralways){
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else if(left->temps <= right->temps){
+ right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+ if(left->addable >= Rcant)
+ left = eacom(left, &tl, nil);
+ }else{
+ left = eacom(left, &tl, nil);
+ right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+ }
+ bb = genbra(&n->src, op, left, right);
+ bb->branch = b;
+ tfree(&tl);
+ tfree(&tr);
+ return bb;
+}
+
+Inst*
+andand(Node *n, int iftrue, Inst *b)
+{
+ if(debug['b'])
+ print("andand %n\n", n);
+ b = bcom(n->left, iftrue, b);
+ b = bcom(n->right, iftrue, b);
+ return b;
+}
+
+Inst*
+oror(Node *n, int iftrue, Inst *b)
+{
+ Inst *bb;
+
+ if(debug['b'])
+ print("oror %n\n", n);
+ bb = bcom(n->left, !iftrue, nil);
+ b = bcom(n->right, iftrue, b);
+ patch(bb, nextinst());
+ return b;
+}
+
+/*
+ * generate code for a recva expression
+ * this is just a hacked up small alt
+ */
+void
+recvacom(Src *src, Node *nto, Node *n)
+{
+ Label *labs;
+ Case *c;
+ Node which, tab, off, add, adr, slot, *left;
+ Type *talt;
+ Inst *p;
+
+ left = n->left;
+
+ labs = allocmem(1 * sizeof *labs);
+ labs[0].isptr = left->addable >= Rcant;
+ c = allocmem(sizeof *c);
+ c->nlab = 1;
+ c->labs = labs;
+ talt = mktalt(c);
+
+ talloc(&which, tint, nil);
+ talloc(&tab, talt, nil);
+
+ /*
+ * build the node for the address of each channel,
+ * the values to send, and the storage fro values received
+ */
+ off = znode;
+ off.op = Oconst;
+ off.ty = tint;
+ off.addable = Rconst;
+ adr = znode;
+ adr.op = Oadr;
+ adr.left = &tab;
+ adr.ty = tint;
+ add = znode;
+ add.op = Oadd;
+ add.left = &adr;
+ add.right = &off;
+ add.ty = tint;
+ slot = znode;
+ slot.op = Oind;
+ slot.left = &add;
+ sumark(&slot);
+
+ /*
+ * gen the channel
+ * this sleaze is lying to the garbage collector
+ */
+ off.val = 2*IBY2WD;
+ if(left->addable < Rcant)
+ genmove(src, Mas, tint, left, &slot);
+ else{
+ slot.ty = left->ty;
+ ecom(src, &slot, left);
+ slot.ty = nil;
+ }
+
+ /*
+ * gen the value
+ */
+ off.val += IBY2WD;
+ p = genrawop(&left->src, ILEA, nto, nil, &slot);
+ p->m.offset = nto->ty->size; /* for optimizer */
+
+ /*
+ * number of senders and receivers
+ */
+ off.val = 0;
+ genmove(src, Mas, tint, sumark(mkconst(src, 0)), &slot);
+ off.val += IBY2WD;
+ genmove(src, Mas, tint, sumark(mkconst(src, 1)), &slot);
+ off.val += IBY2WD;
+
+ p = genrawop(src, IALT, &tab, nil, &which);
+ p->m.offset = talt->size; /* for optimizer */
+ tfree(&which);
+ tfree(&tab);
+}
+
+/*
+ * generate code to duplicate an adt with pick fields
+ * this is just a hacked up small pick
+ * n is Oind(exp)
+ */
+void
+pickdupcom(Src *src, Node *nto, Node *n)
+{
+ Node *start, *stop, *node, *orig, *dest, tmp, clab;
+ Case *c;
+ Inst *j, *jmps, *wild;
+ Label *labs;
+ Decl *d, *tg, *stg;
+ Type *t;
+ int i, nlab;
+ char buf[32];
+
+ if(n->op != Oind)
+ fatal("pickdupcom not Oind: %n" ,n);
+
+ t = n->ty;
+ nlab = t->decl->tag;
+
+ /*
+ * generate global which has case labels
+ */
+ seprint(buf, buf+sizeof(buf), ".c%d", nlabel++);
+ d = mkids(src, enter(buf, 0), mktype(&src->start, &src->stop, Tcase, nil, nil), nil);
+ d->init = mkdeclname(src, d);
+
+ clab.addable = Rmreg;
+ clab.left = nil;
+ clab.right = nil;
+ clab.op = Oname;
+ clab.ty = d->ty;
+ clab.decl = d;
+
+ /*
+ * generate a temp to hold the real value
+ * then generate a case on the tag
+ */
+ orig = n->left;
+ talloc(&tmp, orig->ty, nil);
+ ecom(src, &tmp, orig);
+ orig = mkunary(Oind, &tmp);
+ orig->ty = tint;
+ sumark(orig);
+
+ dest = mkunary(Oind, nto);
+ dest->ty = nto->ty->tof;
+ sumark(dest);
+
+ genrawop(src, ICASE, orig, nil, &clab);
+
+ labs = allocmem(nlab * sizeof *labs);
+
+ i = 0;
+ jmps = nil;
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ stg = tg;
+ for(; tg->next != nil; tg = tg->next)
+ if(stg->ty != tg->next->ty)
+ break;
+ start = sumark(simplify(mkdeclname(src, stg)));
+ stop = start;
+ node = start;
+ if(stg != tg){
+ stop = sumark(simplify(mkdeclname(src, tg)));
+ node = mkbin(Orange, start, stop);
+ }
+
+ labs[i].start = start;
+ labs[i].stop = stop;
+ labs[i].node = node;
+ labs[i++].inst = nextinst();
+
+ genrawop(src, INEW, mktn(tg->ty->tof), nil, nto);
+ genmove(src, Mas, tg->ty->tof, orig, dest);
+
+ j = genrawop(src, IJMP, nil, nil, nil);
+ j->branch = jmps;
+ jmps = j;
+ }
+
+ /*
+ * this should really be a runtime error
+ */
+ wild = genrawop(src, IJMP, nil, nil, nil);
+ patch(wild, wild);
+
+ patch(jmps, nextinst());
+ tfree(&tmp);
+
+ if(i > nlab)
+ fatal("overflowed label tab for pickdupcom");
+
+ c = allocmem(sizeof *c);
+ c->nlab = i;
+ c->nsnd = 0;
+ c->labs = labs;
+ c->iwild = wild;
+
+ d->ty->cse = c;
+ usetype(d->ty);
+ installids(Dglobal, d);
+}
+
+/*
+ * see if name n occurs anywhere in e
+ */
+int
+tupaliased(Node *n, Node *e)
+{
+ for(;;){
+ if(e == nil)
+ return 0;
+ if(e->op == Oname && e->decl == n->decl)
+ return 1;
+ if(tupaliased(n, e->left))
+ return 1;
+ e = e->right;
+ }
+}
+
+/*
+ * see if any name in n occurs anywere in e
+ */
+int
+tupsaliased(Node *n, Node *e)
+{
+ for(;;){
+ if(n == nil)
+ return 0;
+ if(n->op == Oname && tupaliased(n, e))
+ return 1;
+ if(tupsaliased(n->left, e))
+ return 1;
+ n = n->right;
+ }
+}
+
+/*
+ * put unaddressable constants in the global data area
+ */
+Decl*
+globalconst(Node *n)
+{
+ Decl *d;
+ Sym *s;
+ char buf[32];
+
+ seprint(buf, buf+sizeof(buf), ".i.%.8lux", (long)n->val);
+ s = enter(buf, 0);
+ d = s->decl;
+ if(d == nil){
+ d = mkids(&n->src, s, tint, nil);
+ installids(Dglobal, d);
+ d->init = n;
+ d->refs++;
+ }
+ return d;
+}
+
+Decl*
+globalBconst(Node *n)
+{
+ Decl *d;
+ Sym *s;
+ char buf[32];
+
+ seprint(buf, buf+sizeof(buf), ".B.%.8lux.%8lux", (long)(n->val>>32), (long)n->val);
+
+ s = enter(buf, 0);
+ d = s->decl;
+ if(d == nil){
+ d = mkids(&n->src, s, tbig, nil);
+ installids(Dglobal, d);
+ d->init = n;
+ d->refs++;
+ }
+ return d;
+}
+
+Decl*
+globalbconst(Node *n)
+{
+ Decl *d;
+ Sym *s;
+ char buf[32];
+
+ seprint(buf, buf+sizeof(buf), ".b.%.2lux", (long)n->val & 0xff);
+ s = enter(buf, 0);
+ d = s->decl;
+ if(d == nil){
+ d = mkids(&n->src, s, tbyte, nil);
+ installids(Dglobal, d);
+ d->init = n;
+ d->refs++;
+ }
+ return d;
+}
+
+Decl*
+globalfconst(Node *n)
+{
+ Decl *d;
+ Sym *s;
+ char buf[32];
+ ulong dv[2];
+
+ dtocanon(n->rval, dv);
+ seprint(buf, buf+sizeof(buf), ".f.%.8lux.%8lux", dv[0], dv[1]);
+ s = enter(buf, 0);
+ d = s->decl;
+ if(d == nil){
+ d = mkids(&n->src, s, treal, nil);
+ installids(Dglobal, d);
+ d->init = n;
+ d->refs++;
+ }
+ return d;
+}
+
+Decl*
+globalsconst(Node *n)
+{
+ Decl *d;
+ Sym *s;
+
+ s = n->decl->sym;
+ d = s->decl;
+ if(d == nil){
+ d = mkids(&n->src, s, tstring, nil);
+ installids(Dglobal, d);
+ d->init = n;
+ }
+ d->refs++;
+ return d;
+}
+
+static Node*
+subst(Decl *d, Node *e, Node *n)
+{
+ if(n == nil)
+ return nil;
+ if(n->op == Oname){
+ if(d == n->decl){
+ n = dupn(0, nil, e);
+ n->ty = d->ty;
+ }
+ return n;
+ }
+ n->left = subst(d, e, n->left);
+ n->right = subst(d, e, n->right);
+ return n;
+}
+
+static Node*
+putinline(Node *n)
+{
+ Node *e, *tn;
+ Type *t;
+ Decl *d;
+
+if(debug['z']) print("inline1: %n\n", n);
+ if(n->left->op == Oname)
+ d = n->left->decl;
+ else
+ d = n->left->right->decl;
+ e = d->init;
+ t = e->ty;
+ e = dupn(1, &n->src, e->right->left->left);
+ for(d = t->ids, n = n->right; d != nil && n != nil; d = d->next, n = n->right){
+ if(hasside(n->left, 0) && occurs(d, e) != 1){
+ tn = talloc(mkn(0, nil, nil), d->ty, nil);
+ e = mkbin(Ocomma, mkbin(Oas, tn, n->left), subst(d, tn, e));
+ e->ty = e->right->ty;
+ e->left->ty = d->ty;
+ }
+ else
+ e = subst(d, n->left, e);
+ }
+ if(d != nil || n != nil)
+ fatal("bad arg match in putinline()");
+if(debug['z']) print("inline2: %n\n", e);
+ return e;
+}
+
+static void
+fpcall(Src *src, int op, Node *n, Node *ret)
+{
+ Node tp, *e, *mod, *ind;
+
+ tp.decl = nil;
+ e = n->left->left;
+ if(e->addable >= Rcant)
+ e = eacom(e, &tp, nil);
+ mod = mkunary(Oind, e);
+ ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, e), mkconst(src, IBY2WD)));
+ n->left = mkbin(Omdot, mod, ind);
+ n->left->ty = e->ty->tof;
+ mod->ty = ind->ty = ind->left->ty = ind->left->right->ty = tint;
+ sumark(n);
+ callcom(src, op, n, ret);
+ tfree(&tp);
+}
--- /dev/null
+++ b/limbo/fns.h
@@ -1,0 +1,391 @@
+int addfile(File*);
+void addfnptrs(Decl*, int);
+void addiface(Decl*, Decl*);
+void addinclude(char*);
+char *addrprint(char*, char*, int, Addr*);
+Typelist *addtype(Type*, Typelist*);
+Node *adtdecl(Decl *ids, Node *fields);
+void adtdecled(Node *n);
+void adtdefd(Type*);
+Decl *adtmeths(Type*);
+void adtstub(Decl*);
+long align(long, int);
+void *allocmem(ulong);
+void altcheck(Node *an, Type *ret);
+void altcom(Node*);
+Inst *andand(Node*, int, Inst*);
+Decl *appdecls(Decl*, Decl*);
+int argcompat(Node*, Decl*, Node*);
+void arraycom(Node*, Node*);
+void arraydefault(Node*, Node*);
+Type *arrowtype(Type*, Decl*);
+void asmdesc(Desc*);
+void asmentry(Decl*);
+void asmexc(Except*);
+void asminitializer(long, Node*);
+void asminst(Inst*);
+void asmldt(long, Decl*);
+void asmmod(Decl*);
+void asmpath(void);
+void asmstring(long, Sym*);
+void asmvar(long, Decl*);
+int assignindices(Node*);
+void bccom(Node*, Inst**);
+Inst *bcom(Node*, int, Inst*);
+void bindnames(Node*);
+void bindtypes(Type *t);
+Ok callcast(Node*, int, int);
+void callcom(Src*, int, Node*, Node*);
+Type* calltype(Type*, Node*, Type*);
+double canontod(ulong v[2]);
+void casecheck(Node *cn, Type *ret);
+int casecmp(Type*, Node*, Node*);
+void casecom(Node*);
+Node *caselist(Node*, Node*);
+void casesort(Type*, Label*, Label*, int, int);
+Case *checklabels(Node *inits, Type *ctype, int nlab, char *title);
+void checkrefs(Decl*);
+Node *checkused(Node*);
+int circlval(Node*, Node*);
+void concheck(Node *n, int isglobal);
+Node *condecl(Decl*, Node*);
+void condecled(Node *n);
+void constub(Decl*);
+Type *copytypeids(Type*);
+char *ctprint(char*, char*, Type*);
+int ctypeconv(Fmt*);
+Line curline(void);
+Decl *curscope(void);
+int cycarc(Type*, Type*);
+void cycfield(Type*, Decl*);
+void cycsizetype(Type*);
+void cyctype(Type*);
+int dasdecl(Node *n);
+void declaserr(Node*);
+int declasinfer(Node*, Type*);
+int declconv(Fmt*);
+Decl *declsort(Decl*);
+void declstart(void);
+void decltozero(Node *n);
+void deldecl(Decl*);
+int dequal(Decl*, Decl*, int);
+long descmap(Decl*, uchar*, long);
+void disaddr(int, Addr*);
+void disbcon(long);
+void discon(long);
+void disdata(int, long);
+void disdesc(Desc*);
+void disentry(Decl*);
+void disexc(Except*);
+void disinst(Inst*);
+void disldt(long, Decl*);
+void dismod(Decl*);
+void dispath(void);
+void disvar(long, Decl*);
+void disword(long);
+int dotconv(Fmt*);
+char *dotprint(char*, char*, Decl*, int);
+Type *dottype(Type*, Decl*);
+void dtocanon(double, ulong[2]);
+Decl *dupdecl(Decl*);
+Decl *dupdecls(Decl*);
+Node *dupn(int, Src*, Node*);
+Node *eacom(Node*, Node*, Node*);
+Ok echeck(Node *n, int typeok, int isglobal, Node* par);
+Node *ecom(Src*, Node*, Node*);
+Node *efold(Node *n);
+Node *elemsort(Node*);
+void emit(Decl*);
+Decl *encpolys(Decl*);
+Sym *enter(char*, int);
+Desc *enterdesc(uchar*, long, long);
+Sym *enterstring(char*, int);
+char *eprint(char*, char*, Node*);
+char *eprintlist(char*, char*, Node*, char*);
+void error(Line, char*, ...);
+#pragma varargck argpos error 2
+int etconv(Fmt*);
+Node *etolist(Node*);
+void excheck(Node *n, int isglobal);
+void exccheck(Node *cn, Type *ret);
+void excom(Node*);
+Node *exdecl(Decl*, Decl*);
+void exdecled(Node *n);
+Type *expandtype(Type*, Type*, Decl*, Tpair**);
+Type *expandtypes(Type*, Decl*);
+int expconv(Fmt*);
+Type *exptotype(Node*);
+void fatal(char*, ...);
+#pragma varargck argpos fatal 1
+void fielddecled(Node *n);
+Node *fielddecl(int store, Decl *ids);
+int findlab(Type *ty, Node *v, Label *labs, int nlab);
+int fixop(int, Type*, Type*, Type*, int*, int*);
+Fline fline(int);
+void fmtcheck(Node*, Node*, Node*);
+void fncheck(Decl *d);
+Decl *fnchk(Node *n);
+void fncom(Decl*);
+Node *fndecl(Node *n, Type *t, Node *body);
+void fndecled(Node *n);
+Decl* fnlookup(Sym*, Type*, Node**);
+Node *fold(Node*);
+void foldbranch(Inst*);
+Node *foldc(Node*);
+Node *foldcast(Node*, Node*);
+Node *foldcasti(Node*, Node*);
+Node *foldr(Node*);
+Node *foldvc(Node*);
+void gbind(Node *n);
+int gcheck(Node*, Decl**, int);
+void gdasdecl(Node *n);
+void gdecl(Node *n);
+Addr genaddr(Node*);
+Inst *genbra(Src*, int, Node*, Node*);
+Inst *genchan(Src*, Node*, Type*, Node*);
+Desc *gendesc(Decl*, long, Decl*);
+Inst *genfixcastop(Src*, int, Node*, Node*);
+Inst *genmove(Src*, int, Type*, Node*, Node*);
+Inst *genop(Src*, int, Node*, Node*, Node*);
+Inst *genrawop(Src*, int, Node*, Node*, Node*);
+void genstart(void);
+long getpc(Inst*);
+int gfltconv(Fmt*);
+Decl *globalBconst(Node*);
+Node *globalas(Node*, Node*, int);
+Decl *globalbconst(Node*);
+Decl *globalconst(Node*);
+Decl *globalfconst(Node*);
+Decl *globalsconst(Node*);
+void gsort(Node*);
+int hasasgns(Node*);
+int hascall(Node*);
+Node *hascomm(Node*);
+int hasside(Node*, int);
+long idindices(Decl*);
+long idoffsets(Decl*, long, int);
+Type *idtype(Type*);
+void importcheck(Node *n, int isglobal);
+void importchk(Node*);
+Node *importdecl(Node *m, Decl *ids);
+void importdecled(Node *n);
+void includef(Sym*);
+Node *indsascom(Src*, Node*, Node*);
+int initable(Node*, Node*, int);
+int inloop(void);
+void installids(int store, Decl *ids);
+int instconv(Fmt*);
+Type *insttype(Type*, Decl*, Tpair**);
+Type *internaltype(Type*);
+int isimpmod(Sym*);
+int islval(Node*);
+int ispoly(Decl*);
+int ispolyadt(Type*);
+int istuple(Node*);
+void joiniface(Type*, Type*);
+void lexinit(void);
+void lexstart(char*);
+int lineconv(Fmt*);
+int local(Decl*);
+Decl *lookdot(Decl*, Sym*);
+Decl *lookup(Sym*);
+int mapconv(Fmt*);
+int marklval(Node*);
+int mathchk(Node*, int);
+void mergepolydecs(Type*);
+Type *mkadtcon(Type*);
+Type *mkadtpickcon(Type*, Type*);
+Type *mkarrowtype(Line*, Line*, Type*, Sym*);
+Node *mkbin(int, Node*, Node*);
+Node *mkconst(Src*, Long);
+Decl *mkdecl(Src*, int, Type*);
+Node *mkdeclname(Src*, Decl*);
+Desc *mkdesc(long, Decl*);
+Type *mkdottype(Line*, Line*, Type*, Sym*);
+Type *mkexbasetype(Type*);
+Type *mkextuptype(Type*);
+Type *mkextype(Type*);
+File *mkfile(char*, int, int, int, char*, int, int);
+Decl *mkids(Src*, Sym*, Type*, Decl*);
+Type *mkidtype(Src*, Sym*);
+Type *mkiface(Decl*);
+Inst *mkinst(void);
+Type *mkinsttype(Src*, Type*, Typelist*);
+Node *mkname(Src*, Sym*);
+Node *mknil(Src*);
+Node *mkn(int, Node*, Node*);
+Node *mkrconst(Src*, Real);
+Node *mksconst(Src*, Sym*);
+Node *mkscope(Node *body);
+Type *mktalt(Case*);
+Desc *mktdesc(Type*);
+Node *mktn(Type*);
+Type *mktype(Line*, Line*, int, Type*, Decl*);
+Node *mkunary(int, Node*);
+Type *mkvarargs(Node*, Node*);
+Teq *modclass(void);
+void modcode(Decl*);
+void modcom(Decl*);
+void moddataref(void);
+Node *moddecl(Decl *ids, Node *fields);
+void moddecled(Node *n);
+Decl *modglobals(Decl*, Decl*);
+Decl *modimp(Dlist*, Decl*);
+void modrefable(Type*);
+void modresolve(void);
+void modstub(Decl*);
+void modtab(Decl*);
+Decl *module(Decl*);
+int mustzero(Decl *);
+int mpatof(char*, double*);
+Decl *namedot(Decl*, Sym*);
+Decl *namesort(Decl*);
+void narrowmods(void);
+void nerror(Node*, char*, ...);
+#pragma varargck argpos nerror 2
+int nested(void);
+Inst *nextinst(void);
+int nodeconv(Fmt*);
+int nodes(Node*);
+char *nprint(char*, char*, Node*, int);
+void nwarn(Node*, char*, ...);
+#pragma varargck argpos nwarn 2
+int occurs(Decl*, Node*);
+int opconv(Fmt*);
+void optabinit(void);
+void optim(Inst*, Decl*);
+Inst *oror(Node*, int, Inst*);
+Decl *outerpolys(Node*);
+Node *passfns(Src*, Decl*, Node*, Node*, Type*, Tpair*);
+Node *passimplicit(Node*, Node*);
+void patch(Inst*, Inst*);
+void pickcheck(Node*, Type*);
+int pickdecled(Node *n);
+Decl *pickdefd(Type*, Decl*);
+void pickdupcom(Src*, Node*, Node*);
+Decl* polydecl(Decl*);
+int polyequal(Decl*, Decl*);
+void popblock(void);
+Decl *popids(Decl*);
+void popscopes(void);
+Decl *popscope(void);
+void printdecls(Decl*);
+int pushblock(void);
+void pushlabel(Node*);
+void pushscope(Node *, int);
+void raisescheck(Type*);
+int raisescompat(Node*, Node*);
+void reach(Inst*);
+void *reallocmem(void*, ulong);
+void recvacom(Src*, Node*, Node*);
+void redecl(Decl *d);
+void reftype(Type*);
+void repushblock(int);
+void repushids(Decl*);
+void resizetype(Type*);
+long resolvedesc(Decl*, long, Decl*);
+Decl* resolveldts(Decl*, Decl**);
+int resolvemod(Decl*);
+long resolvepcs(Inst*);
+Node *retalloc(Node*, Node*);
+Decl *revids(Decl*);
+Node *rewrite(Node *n);
+Node *rewritecomm(Node*, Node*, Node*, Node*);
+Inst *rewritedestreg(Inst*, int, int);
+Inst *rewritesrcreg(Inst*, int, int, int);
+void rmfnptrs(Decl*);
+Node *rotater(Node*);
+double rpow(double, int);
+int sameaddr(Node*, Node*);
+int sametree(Node*, Node*);
+void sblfiles(void);
+void sblfn(Decl**, int);
+void sblinst(Inst*, long);
+void sblmod(Decl*);
+void sblty(Decl**, int);
+void sblvar(Decl*);
+double scale(Type*);
+double scale2(Type*, Type*);
+Node* scheck(Node*, Type*, int);
+void scom(Node*);
+char *secpy(char*, char*, char*);
+char *seprint(char*, char*, char*, ...);
+#pragma varargck argpos seprint 3
+void shareloc(Decl*);
+int shiftchk(Node*);
+ulong sign(Decl*);
+Node *simplify(Node*);
+Szal sizeids(Decl*, long);
+void sizetype(Type*);
+Node *slicelcom(Src*, Node*, Node*);
+int specific(Type*);
+int srcconv(Fmt*);
+char* srcpath(char*, int);
+int storeconv(Fmt*);
+char *stprint(char*, char*, Type*);
+Sym *stringcat(Sym*, Sym*);
+char *stringpr(char*, char*, Sym*);
+Long strtoi(char*, int);
+int stypeconv(Fmt*);
+Node *sumark(Node*);
+int symcmp(Sym*, Sym*);
+Node *tacquire(Node*);
+Ok tagcast(Node*, Node*, Node*, Decl*, int, int);
+Node *talloc(Node*, Type*, Node*);
+int tcompat(Type*, Type*, int);
+void tcycle(Type*);
+Decl *tdecls(void);
+long tdescmap(Type*, uchar*, long);
+void teqclass(Type*);
+int tequal(Type*, Type*);
+void tfree(Node*);
+void tfreelater(Node*);
+void tfreenow(void);
+void tinit(void);
+int tmustzero(Type *);
+Type *toptype(Src*, Type*);
+Type *topvartype(Type *t, Decl *id, int tyok, int polyok);
+Type* tparent(Type*, Type*);
+char *tprint(char*, char*, Type*);
+void translate(char*, char*, char*);
+void trelease(Node*);
+int tunify(Type*, Type*, Tpair**);
+int tupaliased(Node*, Node*);
+int tupsaliased(Node*, Node*);
+void tupcom(Node*, Node*);
+void tuplcom(Node*, Node*);
+void tuplrcom(Node*, Node*);
+Decl *tuplefields(Node*);
+void typebuiltin(Decl*, Type*);
+Decl *typecheck(int);
+int typeconv(Fmt*);
+Node *typedecl(Decl *ids, Type *t);
+void typedecled(Node *n);
+Decl *typeids(Decl*, Type*);
+void typeinit(void);
+void typestart(void);
+Decl *undefed(Src *src, Sym *s);
+Desc *usedesc(Desc*);
+void usedty(Type*);
+Type *usetype(Type*);
+Type *validtype(Type*, Decl*);
+int valistype(Node*);
+Type *valtmap(Type*, Tpair*);
+void varcheck(Node *n, int isglobal);
+int varcom(Decl*);
+Node *vardecl(Decl*, Type*);
+void vardecled(Node *n);
+Node *varinit(Decl*, Node*);
+Decl *varlistdecl(Decl*, Node*);
+Decl *vars(Decl*);
+int vcom(Decl*);
+Type *verifytypes(Type*, Decl*, Decl*);
+void warn(Line, char*, ...);
+#pragma varargck argpos warn 2
+void yyerror(char*, ...);
+#pragma varargck argpos yyerror 1
+int yylex(void);
+int yyparse(void);
+void zcom(Node *, Node**);
+void zcom0(Decl *, Node**);
+void zcom1(Node *, Node**);
--- /dev/null
+++ b/limbo/gen.c
@@ -1,0 +1,1096 @@
+#include "limbo.h"
+
+static int addrmode[Rend] =
+{
+ /* Rreg */ Afp,
+ /* Rmreg */ Amp,
+ /* Roff */ Aoff,
+ /* Rnoff */ Anoff,
+ /* Rdesc */ Adesc,
+ /* Rdescp */ Adesc,
+ /* Rconst */ Aimm,
+ /* Ralways */ Aerr,
+ /* Radr */ Afpind,
+ /* Rmadr */ Ampind,
+ /* Rcant */ Aerr,
+ /* Rpc */ Apc,
+ /* Rmpc */ Aerr,
+ /* Rareg */ Aerr,
+ /* Ramreg */ Aerr,
+ /* Raadr */ Aerr,
+ /* Ramadr */ Aerr,
+ /* Rldt */ Aldt,
+};
+
+static Decl *wtemp;
+static Decl *bigtemp;
+static int ntemp;
+static Node retnode;
+static Inst zinst;
+
+ int *blockstack;
+ int blockdep;
+ int nblocks;
+static int lenblockstack;
+static Node *ntoz;
+
+static Inst* genfixop(Src *src, int op, Node *s, Node *m, Node *d);
+
+void
+genstart(void)
+{
+ Decl *d;
+
+ d = mkdecl(&nosrc, Dlocal, tint);
+ d->sym = enter(".ret", 0);
+ d->offset = IBY2WD * REGRET;
+
+ retnode = znode;
+ retnode.op = Oname;
+ retnode.addable = Rreg;
+ retnode.decl = d;
+ retnode.ty = tint;
+
+ zinst.op = INOP;
+ zinst.sm = Anone;
+ zinst.dm = Anone;
+ zinst.mm = Anone;
+
+ firstinst = allocmem(sizeof *firstinst);
+ *firstinst = zinst;
+ lastinst = firstinst;
+
+ blocks = -1;
+ blockdep = 0;
+ nblocks = 0;
+}
+
+/*
+ * manage nested control flow blocks
+ */
+int
+pushblock(void)
+{
+ if(blockdep >= lenblockstack){
+ lenblockstack = blockdep + 32;
+ blockstack = reallocmem(blockstack, lenblockstack * sizeof *blockstack);
+ }
+ blockstack[blockdep++] = blocks;
+ return blocks = nblocks++;
+}
+
+void
+repushblock(int b)
+{
+ blockstack[blockdep++] = blocks;
+ blocks = b;
+}
+
+void
+popblock(void)
+{
+ blocks = blockstack[blockdep -= 1];
+}
+
+void
+tinit(void)
+{
+ wtemp = nil;
+ bigtemp = nil;
+}
+
+Decl*
+tdecls(void)
+{
+ Decl *d;
+
+ for(d = wtemp; d != nil; d = d->next){
+ if(d->tref != 1)
+ fatal("temporary %s has %d references", d->sym->name, d->tref-1);
+ }
+
+ for(d = bigtemp; d != nil; d = d->next){
+ if(d->tref != 1)
+ fatal("temporary %s has %d references", d->sym->name, d->tref-1);
+ }
+
+ return appdecls(wtemp, bigtemp);
+}
+
+Node*
+talloc(Node *n, Type *t, Node *nok)
+{
+ Decl *d, *ok;
+ Desc *desc;
+ char buf[StrSize];
+
+ ok = nil;
+ if(nok != nil)
+ ok = nok->decl;
+ if(ok == nil || ok->tref == 0 || tattr[ok->ty->kind].big != tattr[t->kind].big || ok->ty->align != t->align)
+ ok = nil;
+ *n = znode;
+ n->op = Oname;
+ n->addable = Rreg;
+ n->ty = t;
+ if(tattr[t->kind].big){
+ desc = mktdesc(t);
+ if(ok != nil && ok->desc == desc){
+ ok->tref++;
+ ok->refs++;
+ n->decl = ok;
+ return n;
+ }
+ for(d = bigtemp; d != nil; d = d->next){
+ if(d->tref == 1 && d->desc == desc && d->ty->align == t->align){
+ d->tref++;
+ d->refs++;
+ n->decl = d;
+ return n;
+ }
+ }
+ d = mkdecl(&nosrc, Dlocal, t);
+ d->desc = desc;
+ d->tref = 2;
+ d->refs = 1;
+ n->decl = d;
+ seprint(buf, buf+sizeof(buf), ".b%d", ntemp++);
+ d->sym = enter(buf, 0);
+ d->next = bigtemp;
+ bigtemp = d;
+ return n;
+ }
+ if(ok != nil
+ && tattr[ok->ty->kind].isptr == tattr[t->kind].isptr
+ && ok->ty->size == t->size){
+ ok->tref++;
+ n->decl = ok;
+ return n;
+ }
+ for(d = wtemp; d != nil; d = d->next){
+ if(d->tref == 1
+ && tattr[d->ty->kind].isptr == tattr[t->kind].isptr
+ && d->ty->size == t->size
+ && d->ty->align == t->align){
+ d->tref++;
+ n->decl = d;
+ return n;
+ }
+ }
+ d = mkdecl(&nosrc, Dlocal, t);
+ d->tref = 2;
+ d->refs = 1;
+ n->decl = d;
+ seprint(buf, buf+sizeof(buf), ".t%d", ntemp++);
+ d->sym = enter(buf, 0);
+ d->next = wtemp;
+ wtemp = d;
+ return n;
+}
+
+void
+tfree(Node *n)
+{
+ if(n == nil || n->decl == nil || n->decl->tref == 0)
+ return;
+ if(n->decl->tref == 1)
+ fatal("double free of temporary %s", n->decl->sym->name);
+ if (--n->decl->tref == 1)
+ zcom1(n, nil);
+}
+
+void
+tfreelater(Node *n)
+{
+ if(n == nil || n->decl == nil || n->decl->tref == 0)
+ return;
+ if(n->decl->tref == 1)
+ fatal("double free of temporary %s", n->decl->sym->name);
+ if(--n->decl->tref == 1){
+ Node *nn = mkn(Oname, nil, nil);
+
+ *nn = *n;
+ nn->left = ntoz;
+ ntoz = nn;
+ n->decl->tref++;
+ }
+}
+
+void
+tfreenow()
+{
+ Node *n, *nn;
+
+ for(n = ntoz; n != nil; n = nn){
+ nn = n->left;
+ n->left = nil;
+ if(n->decl->tref != 2)
+ fatal("bad free of temporary %s", n->decl->sym->name);
+ --n->decl->tref;
+ zcom1(n, nil);
+ }
+ ntoz = nil;
+}
+
+/*
+ * realloc a temporary after it's been freed
+ */
+Node*
+tacquire(Node *n)
+{
+ if(n == nil || n->decl == nil || n->decl->tref == 0)
+ return n;
+/*
+ if(n->decl->tref != 1)
+ fatal("tacquire ref != 1: %d", n->decl->tref);
+*/
+ n->decl->tref++;
+ return n;
+}
+
+void
+trelease(Node *n)
+{
+ if(n == nil || n->decl == nil || n->decl->tref == 0)
+ return;
+ if(n->decl->tref == 1)
+ fatal("double release of temporary %s", n->decl->sym->name);
+ n->decl->tref--;
+}
+
+Inst*
+mkinst(void)
+{
+ Inst *in;
+
+ in = lastinst->next;
+ if(in == nil){
+ in = allocmem(sizeof *in);
+ *in = zinst;
+ lastinst->next = in;
+ }
+ lastinst = in;
+ in->block = blocks;
+ if(blocks < 0)
+ fatal("mkinst no block");
+ return in;
+}
+
+Inst*
+nextinst(void)
+{
+ Inst *in;
+
+ in = lastinst->next;
+ if(in != nil)
+ return in;
+ in = allocmem(sizeof(*in));
+ *in = zinst;
+ lastinst->next = in;
+ return in;
+}
+
+/*
+ * allocate a node for returning
+ */
+Node*
+retalloc(Node *n, Node *nn)
+{
+ if(nn->ty == tnone)
+ return nil;
+ *n = znode;
+ n->op = Oind;
+ n->addable = Radr;
+ n->left = dupn(1, &n->src, &retnode);
+ n->ty = nn->ty;
+ return n;
+}
+
+Inst*
+genrawop(Src *src, int op, Node *s, Node *m, Node *d)
+{
+ Inst *in;
+
+ in = mkinst();
+ in->op = op;
+ in->src = *src;
+if(in->sm != Anone || in->mm != Anone || in->dm != Anone)
+fatal("bogus mkinst in genrawop: %I\n", in);
+ if(s != nil){
+ in->s = genaddr(s);
+ in->sm = addrmode[s->addable];
+ }
+ if(m != nil){
+ in->m = genaddr(m);
+ in->mm = addrmode[m->addable];
+ if(in->mm == Ampind || in->mm == Afpind)
+ fatal("illegal addressing mode in register %n", m);
+ }
+ if(d != nil){
+ in->d = genaddr(d);
+ in->dm = addrmode[d->addable];
+ }
+ return in;
+}
+
+Inst*
+genop(Src *src, int op, Node *s, Node *m, Node *d)
+{
+ Inst *in;
+ int iop;
+
+ iop = disoptab[op][opind[d->ty->kind]];
+ if(iop == 0)
+ fatal("can't deal with op %s on %n %n %n in genop", opname[op], s, m, d);
+ if(iop == IMULX || iop == IDIVX)
+ return genfixop(src, iop, s, m, d);
+ in = mkinst();
+ in->op = iop;
+ in->src = *src;
+ if(s != nil){
+ in->s = genaddr(s);
+ in->sm = addrmode[s->addable];
+ }
+ if(m != nil){
+ in->m = genaddr(m);
+ in->mm = addrmode[m->addable];
+ if(in->mm == Ampind || in->mm == Afpind)
+ fatal("illegal addressing mode in register %n", m);
+ }
+ if(d != nil){
+ in->d = genaddr(d);
+ in->dm = addrmode[d->addable];
+ }
+ return in;
+}
+
+Inst*
+genbra(Src *src, int op, Node *s, Node *m)
+{
+ Type *t;
+ Inst *in;
+ int iop;
+
+ t = s->ty;
+ if(t == tany)
+ t = m->ty;
+ iop = disoptab[op][opind[t->kind]];
+ if(iop == 0)
+ fatal("can't deal with op %s on %n %n in genbra", opname[op], s, m);
+ in = mkinst();
+ in->op = iop;
+ in->src = *src;
+ if(s != nil){
+ in->s = genaddr(s);
+ in->sm = addrmode[s->addable];
+ }
+ if(m != nil){
+ in->m = genaddr(m);
+ in->mm = addrmode[m->addable];
+ if(in->mm == Ampind || in->mm == Afpind)
+ fatal("illegal addressing mode in register %n", m);
+ }
+ return in;
+}
+
+Inst*
+genchan(Src *src, Node *sz, Type *mt, Node *d)
+{
+ Inst *in;
+ Desc *td;
+ Addr reg;
+ int op, regm;
+
+ regm = Anone;
+ reg.decl = nil;
+ reg.reg = 0;
+ reg.offset = 0;
+ op = chantab[mt->kind];
+ if(op == 0)
+ fatal("can't deal with op %d in genchan", mt->kind);
+
+ switch(mt->kind){
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ td = mktdesc(mt);
+ if(td->nmap != 0){
+ op++; /* sleazy */
+ usedesc(td);
+ regm = Adesc;
+ reg.decl = mt->decl;
+ }else{
+ regm = Aimm;
+ reg.offset = mt->size;
+ }
+ break;
+ }
+ in = mkinst();
+ in->op = op;
+ in->src = *src;
+ in->s = reg;
+ in->sm = regm;
+ if(sz != nil){
+ in->m = genaddr(sz);
+ in->mm = addrmode[sz->addable];
+ }
+ if(d != nil){
+ in->d = genaddr(d);
+ in->dm = addrmode[d->addable];
+ }
+ return in;
+}
+
+Inst*
+genmove(Src *src, int how, Type *mt, Node *s, Node *d)
+{
+ Inst *in;
+ Desc *td;
+ Addr reg;
+ int op, regm;
+
+ regm = Anone;
+ reg.decl = nil;
+ reg.reg = 0;
+ reg.offset = 0;
+ op = movetab[how][mt->kind];
+ if(op == 0)
+ fatal("can't deal with op %d on %n %n in genmove", how, s, d);
+
+ switch(mt->kind){
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ case Texception:
+ if(mt->size == 0 && how == Mas)
+ return nil;
+ td = mktdesc(mt);
+ if(td->nmap != 0){
+ op++; /* sleazy */
+ usedesc(td);
+ regm = Adesc;
+ reg.decl = mt->decl;
+ }else{
+ regm = Aimm;
+ reg.offset = mt->size;
+ }
+ break;
+ }
+ in = mkinst();
+ in->op = op;
+ in->src = *src;
+ if(s != nil){
+ in->s = genaddr(s);
+ in->sm = addrmode[s->addable];
+ }
+ in->m = reg;
+ in->mm = regm;
+ if(d != nil){
+ in->d = genaddr(d);
+ in->dm = addrmode[d->addable];
+ }
+ if(s->addable == Rpc)
+ in->op = IMOVPC;
+ return in;
+}
+
+void
+patch(Inst *b, Inst *dst)
+{
+ Inst *n;
+
+ for(; b != nil; b = n){
+ n = b->branch;
+ b->branch = dst;
+ }
+}
+
+long
+getpc(Inst *i)
+{
+ if(i->pc == 0 && i != firstinst && (firstinst->op != INOOP || i != firstinst->next)){
+ do
+ i = i->next;
+ while(i != nil && i->pc == 0);
+ if(i == nil || i->pc == 0)
+ fatal("bad instruction in getpc");
+ }
+ return i->pc;
+}
+
+/*
+ * follow all possible paths from n,
+ * marking reached code, compressing branches, and reclaiming unreached insts
+ */
+void
+reach(Inst *in)
+{
+ Inst *last;
+
+ foldbranch(in);
+ last = in;
+ for(in = in->next; in != nil; in = in->next){
+ if(!in->reach)
+ last->next = in->next;
+ else
+ last = in;
+ }
+ lastinst = last;
+}
+
+/*
+ * follow all possible paths from n,
+ * marking reached code, compressing branches, and eliminating tail recursion
+ */
+void
+foldbranch(Inst *in)
+{
+ Inst *b, *next;
+ Label *lab;
+ int i, n;
+
+ while(in != nil && !in->reach){
+ in->reach = 1;
+ if(in->branch != nil)
+ while(in->branch->op == IJMP){
+ if(in == in->branch || in->branch == in->branch->branch)
+ break;
+ in->branch = in->branch->branch;
+ }
+ switch(in->op){
+ case IGOTO:
+ case ICASE:
+ case ICASEL:
+ case ICASEC:
+ case IEXC:
+ foldbranch(in->d.decl->ty->cse->iwild);
+ lab = in->d.decl->ty->cse->labs;
+ n = in->d.decl->ty->cse->nlab;
+ for(i = 0; i < n; i++)
+ foldbranch(lab[i].inst);
+ if(in->op == IEXC)
+ in->op = INOOP;
+ return;
+ case IEXC0:
+ foldbranch(in->branch);
+ in->op = INOOP;
+ break;
+ case IRET:
+ case IEXIT:
+ case IRAISE:
+ return;
+ case IJMP:
+ b = in->branch;
+ switch(b->op){
+ case ICASE:
+ case ICASEL:
+ case ICASEC:
+ case IRET:
+ case IEXIT:
+ next = in->next;
+ *in = *b;
+ in->next = next;
+ continue;
+ }
+ foldbranch(b);
+ return;
+ default:
+ if(in->branch != nil)
+ foldbranch(in->branch);
+ break;
+ }
+
+ in = in->next;
+ }
+}
+
+/*
+ * convert the addressable node into an operand
+ * see the comment for sumark
+ */
+Addr
+genaddr(Node *n)
+{
+ Addr a;
+
+ a.reg = 0;
+ a.offset = 0;
+ a.decl = nil;
+ if(n == nil)
+ return a;
+ switch(n->addable){
+ case Rreg:
+ if(n->decl != nil)
+ a.decl = n->decl;
+ else
+ a = genaddr(n->left);
+ break;
+ case Rmreg:
+ if(n->decl != nil)
+ a.decl = n->decl;
+ else
+ a = genaddr(n->left);
+ break;
+ case Rdesc:
+ a.decl = n->ty->decl;
+ break;
+ case Roff:
+ case Rnoff:
+ a.decl = n->decl;
+ break;
+ case Rconst:
+ a.offset = n->val;
+ break;
+ case Radr:
+ a = genaddr(n->left);
+ break;
+ case Rmadr:
+ a = genaddr(n->left);
+ break;
+ case Rareg:
+ case Ramreg:
+ a = genaddr(n->left);
+ if(n->op == Oadd)
+ a.reg += n->right->val;
+ break;
+ case Raadr:
+ case Ramadr:
+ a = genaddr(n->left);
+ if(n->op == Oadd)
+ a.offset += n->right->val;
+ break;
+ case Rldt:
+ a.decl = n->decl;
+ break;
+ case Rdescp:
+ case Rpc:
+ a.decl = n->decl;
+ break;
+ default:
+ fatal("can't deal with %n in genaddr", n);
+ break;
+ }
+ return a;
+}
+
+int
+sameaddr(Node *n, Node *m)
+{
+ Addr a, b;
+
+ if(n->addable != m->addable)
+ return 0;
+ a = genaddr(n);
+ b = genaddr(m);
+ return a.offset == b.offset && a.reg == b.reg && a.decl == b.decl;
+}
+
+long
+resolvedesc(Decl *mod, long length, Decl *decls)
+{
+ Desc *g, *d, *last;
+ int descid;
+
+ g = gendesc(mod, length, decls);
+ g->used = 0;
+ last = nil;
+ for(d = descriptors; d != nil; d = d->next){
+ if(!d->used){
+ if(last != nil)
+ last->next = d->next;
+ else
+ descriptors = d->next;
+ continue;
+ }
+ last = d;
+ }
+
+ g->next = descriptors;
+ descriptors = g;
+
+ descid = 0;
+ for(d = descriptors; d != nil; d = d->next)
+ d->id = descid++;
+ if(g->id != 0)
+ fatal("bad global descriptor id");
+
+ return descid;
+}
+
+int
+resolvemod(Decl *m)
+{
+ Decl *id, *d;
+
+ for(id = m->ty->ids; id != nil; id = id->next){
+ switch(id->store){
+ case Dfn:
+ id->iface->pc = id->pc;
+ id->iface->desc = id->desc;
+if(debug['v']) print("R1: %s %p %p %p\n", id->sym->name, id, id->iface, id->pc);
+ break;
+ case Dtype:
+ if(id->ty->kind != Tadt)
+ break;
+ for(d = id->ty->ids; d != nil; d = d->next){
+ if(d->store == Dfn){
+ d->iface->pc = d->pc;
+ d->iface->desc = d->desc;
+if(debug['v']) print("R2: %s %p %p %p\n", d->sym->name, d, d->iface, d->pc);
+ }
+ }
+ break;
+ }
+ }
+ /* for addiface */
+ for(id = m->ty->tof->ids; id != nil; id = id->next){
+ if(id->store == Dfn){
+ if(id->pc == nil)
+ id->pc = id->iface->pc;
+ if(id->desc == nil)
+ id->desc = id->iface->desc;
+if(debug['v']) print("R3: %s %p %p %p\n", id->sym->name, id, id->iface, id->pc);
+ }
+ }
+ return m->ty->tof->decl->init->val;
+}
+
+/*
+ * place the Tiface decs in another list
+ */
+Decl*
+resolveldts(Decl *d, Decl **dd)
+{
+ Decl *d1, *ld1, *d2, *ld2, *n;
+
+ d1 = d2 = nil;
+ ld1 = ld2 = nil;
+ for( ; d != nil; d = n){
+ n = d->next;
+ d->next = nil;
+ if(d->ty->kind == Tiface){
+ if(d2 == nil)
+ d2 = d;
+ else
+ ld2->next = d;
+ ld2 = d;
+ }
+ else{
+ if(d1 == nil)
+ d1 = d;
+ else
+ ld1->next = d;
+ ld1 = d;
+ }
+ }
+ *dd = d2;
+ return d1;
+}
+
+/*
+ * fix up all pc's
+ * finalize all data offsets
+ * fix up instructions with offsets too large
+ */
+long
+resolvepcs(Inst *inst)
+{
+ Decl *d;
+ Inst *in;
+ int op;
+ ulong r, off;
+ long v, pc;
+
+ pc = 0;
+ for(in = inst; in != nil; in = in->next){
+ if(!in->reach || in->op == INOP)
+ fatal("unreachable pc: %I %ld", in, pc);
+ if(in->op == INOOP){
+ in->pc = pc;
+ continue;
+ }
+ d = in->s.decl;
+ if(d != nil){
+ if(in->sm == Adesc){
+ if(d->desc != nil)
+ in->s.offset = d->desc->id;
+ }else
+ in->s.reg += d->offset;
+ }
+ r = in->s.reg;
+ off = in->s.offset;
+ if((in->sm == Afpind || in->sm == Ampind)
+ && (r >= MaxReg || off >= MaxReg))
+ fatal("big offset in %I\n", in);
+
+ d = in->m.decl;
+ if(d != nil){
+ if(in->mm == Adesc){
+ if(d->desc != nil)
+ in->m.offset = d->desc->id;
+ }else
+ in->m.reg += d->offset;
+ }
+ v = 0;
+ switch(in->mm){
+ case Anone:
+ break;
+ case Aimm:
+ case Apc:
+ case Adesc:
+ v = in->m.offset;
+ break;
+ case Aoff:
+ case Anoff:
+ v = in->m.decl->iface->offset;
+ break;
+ case Afp:
+ case Amp:
+ case Aldt:
+ v = in->m.reg;
+ if(v < 0)
+ v = 0x8000;
+ break;
+
+ default:
+ fatal("can't deal with %I's m mode\n", in);
+ break;
+ }
+ if(v > 0x7fff || v < -0x8000){
+ switch(in->op){
+ case IALT:
+ case IINDX:
+warn(in->src.start, "possible bug: temp m too big in %I: %ld %ld %d\n", in, in->m.reg, in->m.reg, MaxReg);
+ rewritedestreg(in, IMOVW, RTemp);
+ break;
+ default:
+ op = IMOVW;
+ if(isbyteinst[in->op])
+ op = IMOVB;
+ in = rewritesrcreg(in, op, RTemp, pc++);
+ break;
+ }
+ }
+
+ d = in->d.decl;
+ if(d != nil){
+ if(in->dm == Apc)
+ in->d.offset = d->pc->pc;
+ else
+ in->d.reg += d->offset;
+ }
+ r = in->d.reg;
+ off = in->d.offset;
+ if((in->dm == Afpind || in->dm == Ampind)
+ && (r >= MaxReg || off >= MaxReg))
+ fatal("big offset in %I\n", in);
+
+ in->pc = pc;
+ pc++;
+ }
+ for(in = inst; in != nil; in = in->next){
+ d = in->s.decl;
+ if(d != nil && in->sm == Apc)
+ in->s.offset = d->pc->pc;
+ d = in->d.decl;
+ if(d != nil && in->dm == Apc)
+ in->d.offset = d->pc->pc;
+ if(in->branch != nil){
+ in->dm = Apc;
+ in->d.offset = in->branch->pc;
+ }
+ }
+ return pc;
+}
+
+/*
+ * fixp up a big register constant uses as a source
+ * ugly: smashes the instruction
+ */
+Inst*
+rewritesrcreg(Inst *in, int op, int treg, int pc)
+{
+ Inst *new;
+ Addr a;
+ int am;
+
+ a = in->m;
+ am = in->mm;
+ in->mm = Afp;
+ in->m.reg = treg;
+ in->m.decl = nil;
+
+ new = allocmem(sizeof(*in));
+ *new = *in;
+
+ *in = zinst;
+ in->src = new->src;
+ in->next = new;
+ in->op = op;
+ in->s = a;
+ in->sm = am;
+ in->dm = Afp;
+ in->d.reg = treg;
+ in->pc = pc;
+ in->reach = 1;
+ in->block = new->block;
+ return new;
+}
+
+/*
+ * fix up a big register constant by moving to the destination
+ * after the instruction completes
+ */
+Inst*
+rewritedestreg(Inst *in, int op, int treg)
+{
+ Inst *n;
+
+ n = allocmem(sizeof(*n));
+ *n = zinst;
+ n->next = in->next;
+ in->next = n;
+ n->src = in->src;
+ n->op = op;
+ n->sm = Afp;
+ n->s.reg = treg;
+ n->d = in->m;
+ n->dm = in->mm;
+ n->reach = 1;
+ n->block = in->block;
+
+ in->mm = Afp;
+ in->m.reg = treg;
+ in->m.decl = nil;
+
+ return n;
+}
+
+int
+instconv(Fmt *f)
+{
+ Inst *in;
+ char buf[512], *p;
+ char *op, *comma;
+
+ in = va_arg(f->args, Inst*);
+ op = nil;
+ if(in->op < MAXDIS)
+ op = instname[in->op];
+ if(op == nil)
+ op = "??";
+ buf[0] = '\0';
+ if(in->op == INOP)
+ return fmtstrcpy(f, "\tnop");
+ p = seprint(buf, buf + sizeof(buf), "\t%s\t", op);
+ comma = "";
+ if(in->sm != Anone){
+ p = addrprint(p, buf + sizeof(buf), in->sm, &in->s);
+ comma = ",";
+ }
+ if(in->mm != Anone){
+ p = seprint(p, buf + sizeof(buf), "%s", comma);
+ p = addrprint(p, buf + sizeof(buf), in->mm, &in->m);
+ comma = ",";
+ }
+ if(in->dm != Anone){
+ p = seprint(p, buf + sizeof(buf), "%s", comma);
+ p = addrprint(p, buf + sizeof(buf), in->dm, &in->d);
+ }
+
+ if(asmsym && in->s.decl != nil && in->sm == Adesc)
+ p = seprint(p, buf+sizeof(buf), " #%D", in->s.decl);
+ if(0 && asmsym && in->m.decl != nil)
+ p = seprint(p, buf+sizeof(buf), " #%D", in->m.decl);
+ if(asmsym && in->d.decl != nil && in->dm == Apc)
+ p = seprint(p, buf+sizeof(buf), " #%D", in->d.decl);
+ if(asmsym)
+ p = seprint(p, buf+sizeof(buf), " #%U", in->src);
+ USED(p);
+ return fmtstrcpy(f, buf);
+}
+
+char*
+addrprint(char *buf, char *end, int am, Addr *a)
+{
+ switch(am){
+ case Anone:
+ return buf;
+ case Aimm:
+ case Apc:
+ case Adesc:
+ return seprint(buf, end, "$%ld", a->offset);
+ case Aoff:
+ return seprint(buf, end, "$%ld", a->decl->iface->offset);
+ case Anoff:
+ return seprint(buf, end, "-$%ld", a->decl->iface->offset);
+ case Afp:
+ return seprint(buf, end, "%ld(fp)", a->reg);
+ case Afpind:
+ return seprint(buf, end, "%ld(%ld(fp))", a->offset, a->reg);
+ case Amp:
+ return seprint(buf, end, "%ld(mp)", a->reg);
+ case Ampind:
+ return seprint(buf, end, "%ld(%ld(mp))", a->offset, a->reg);
+ case Aldt:
+ return seprint(buf, end, "$%ld", a->reg);
+ case Aerr:
+ default:
+ return seprint(buf, end, "%ld(%ld(?%d?))", a->offset, a->reg, am);
+ }
+}
+
+static void
+genstore(Src *src, Node *n, int offset)
+{
+ Decl *de;
+ Node d;
+
+ de = mkdecl(&nosrc, Dlocal, tint);
+ de->sym = nil;
+ de->offset = offset;
+
+ d = znode;
+ d.op = Oname;
+ d.addable = Rreg;
+ d.decl = de;
+ d.ty = tint;
+ genrawop(src, IMOVW, n, nil, &d);
+}
+
+static Inst*
+genfixop(Src *src, int op, Node *s, Node *m, Node *d)
+{
+ int p, a;
+ Node *mm;
+ Inst *i;
+
+ mm = m ? m: d;
+ op = fixop(op, mm->ty, s->ty, d->ty, &p, &a);
+ if(op == IMOVW){ /* just zero d */
+ s = sumark(mkconst(src, 0));
+ return genrawop(src, op, s, nil, d);
+ }
+ if(op != IMULX && op != IDIVX)
+ genstore(src, sumark(mkconst(src, a)), STemp);
+ genstore(src, sumark(mkconst(src, p)), DTemp);
+ i = genrawop(src, op, s, m, d);
+ return i;
+}
+
+Inst*
+genfixcastop(Src *src, int op, Node *s, Node *d)
+{
+ int p, a;
+ Node *m;
+
+ op = fixop(op, s->ty, tint, d->ty, &p, &a);
+ if(op == IMOVW){ /* just zero d */
+ s = sumark(mkconst(src, 0));
+ return genrawop(src, op, s, nil, d);
+ }
+ m = sumark(mkconst(src, p));
+ if(op != ICVTXX)
+ genstore(src, sumark(mkconst(src, a)), STemp);
+ return genrawop(src, op, s, m, d);
+}
--- /dev/null
+++ b/limbo/lex.c
@@ -1,0 +1,1453 @@
+#define Extern
+#include "limbo.h"
+#include "y.tab.h"
+
+enum
+{
+ Leof = -1,
+ Linestart = 0,
+
+ Mlower = 1,
+ Mupper = 2,
+ Munder = 4,
+ Malpha = Mupper|Mlower|Munder,
+ Mdigit = 8,
+ Msign = 16,
+ Mexp = 32,
+ Mhex = 64,
+ Mradix = 128,
+
+ HashSize = 1024,
+ MaxPath = 4096
+};
+
+typedef struct Keywd Keywd;
+struct Keywd
+{
+ char *name;
+ int token;
+};
+
+ File **files; /* files making up the module, sorted by absolute line */
+ int nfiles;
+static int lenfiles;
+static int lastfile; /* index of last file looked up */
+
+static char *incpath[MaxIncPath];
+static Sym *symbols[HashSize];
+static Sym *strings[HashSize];
+static char map[256];
+static Biobuf *bin;
+static Line linestack[MaxInclude];
+static int lineno;
+static int linepos;
+static int bstack;
+static int ineof;
+static int lasttok;
+static YYSTYPE lastyylval;
+static char srcdir[MaxPath];
+
+static Keywd keywords[] =
+{
+ "adt", Ladt,
+ "alt", Lalt,
+ "array", Larray,
+ "big", Ltid,
+ "break", Lbreak,
+ "byte", Ltid,
+ "case", Lcase,
+ "chan", Lchan,
+ "con", Lcon,
+ "continue", Lcont,
+ "cyclic", Lcyclic,
+ "do", Ldo,
+ "dynamic", Ldynamic,
+ "else", Lelse,
+ "exception", Lexcept,
+ "exit", Lexit,
+ "fixed", Lfix,
+ "fn", Lfn,
+ "for", Lfor,
+ "hd", Lhd,
+ "if", Lif,
+ "implement", Limplement,
+ "import", Limport,
+ "include", Linclude,
+ "int", Ltid,
+ "len", Llen,
+ "list", Llist,
+ "load", Lload,
+ "module", Lmodule,
+ "nil", Lnil,
+ "of", Lof,
+ "or", Lor,
+ "pick", Lpick,
+ "raise", Lraise,
+ "raises", Lraises,
+ "real", Ltid,
+ "ref", Lref,
+ "return", Lreturn,
+ "self", Lself,
+ "spawn", Lspawn,
+ "string", Ltid,
+ "tagof", Ltagof,
+ "tl", Ltl,
+ "to", Lto,
+ "type", Ltype,
+ "while", Lwhile,
+ 0,
+};
+
+static Keywd tokwords[] =
+{
+ "&=", Landeq,
+ "|=", Loreq,
+ "^=", Lxoreq,
+ "<<=", Llsheq,
+ ">>=", Lrsheq,
+ "+=", Laddeq,
+ "-=", Lsubeq,
+ "*=", Lmuleq,
+ "/=", Ldiveq,
+ "%=", Lmodeq,
+ "**=", Lexpeq,
+ ":=", Ldeclas,
+ "||", Loror,
+ "&&", Landand,
+ "::", Lcons,
+ "==", Leq,
+ "!=", Lneq,
+ "<=", Lleq,
+ ">=", Lgeq,
+ "<<", Llsh,
+ ">>", Lrsh,
+ "<-", Lcomm,
+ "++", Linc,
+ "--", Ldec,
+ "->", Lmdot,
+ "=>", Llabs,
+ "**", Lexp,
+ "EOF", Leof,
+ "eof", Beof,
+ 0,
+};
+
+void
+lexinit(void)
+{
+ Keywd *k;
+ int i;
+
+ for(i = 0; i < 256; i++){
+ if(i == '_' || i > 0xa0)
+ map[i] |= Munder;
+ if(i >= 'A' && i <= 'Z')
+ map[i] |= Mupper;
+ if(i >= 'a' && i <= 'z')
+ map[i] |= Mlower;
+ if(i >= 'A' && i <= 'F' || i >= 'a' && i <= 'f')
+ map[i] |= Mhex;
+ if(i == 'e' || i == 'E')
+ map[i] |= Mexp;
+ if(i == 'r' || i == 'R')
+ map[i] |= Mradix;
+ if(i == '-' || i == '+')
+ map[i] |= Msign;
+ if(i >= '0' && i <= '9')
+ map[i] |= Mdigit;
+ }
+
+ memset(escmap, -1, sizeof(escmap));
+ escmap['\''] = '\'';
+ unescmap['\''] = '\'';
+ escmap['"'] = '"';
+ unescmap['"'] = '"';
+ escmap['\\'] = '\\';
+ unescmap['\\'] = '\\';
+ escmap['a'] = '\a';
+ unescmap['\a'] = 'a';
+ escmap['b'] = '\b';
+ unescmap['\b'] = 'b';
+ escmap['f'] = '\f';
+ unescmap['\f'] = 'f';
+ escmap['n'] = '\n';
+ unescmap['\n'] = 'n';
+ escmap['r'] = '\r';
+ unescmap['\r'] = 'r';
+ escmap['t'] = '\t';
+ unescmap['\t'] = 't';
+ escmap['v'] = '\v';
+ unescmap['\v'] = 'v';
+ escmap['0'] = '\0';
+ unescmap['\0'] = '0';
+
+ for(k = keywords; k->name != nil; k++)
+ enter(k->name, k->token);
+}
+
+int
+cmap(int c)
+{
+ if(c<0)
+ return 0;
+ if(c<256)
+ return map[c];
+ return Mlower;
+}
+
+void
+lexstart(char *in)
+{
+ char *p;
+
+ ineof = 0;
+ bstack = 0;
+ nfiles = 0;
+ lastfile = 0;
+ addfile(mkfile(strdup(in), 1, 0, -1, nil, 0, -1));
+ bin = bins[bstack];
+ lineno = 1;
+ linepos = Linestart;
+
+ secpy(srcdir, srcdir+MaxPath, in);
+ p = strrchr(srcdir, '/');
+ if(p == nil)
+ srcdir[0] = '\0';
+ else
+ p[1] = '\0';
+}
+
+static int
+Getc(void)
+{
+ int c;
+
+ if(ineof)
+ return Beof;
+ c = BGETC(bin);
+ if(c == Beof)
+ ineof = 1;
+ linepos++;
+ return c;
+}
+
+static void
+unGetc(void)
+{
+ if(ineof)
+ return;
+ Bungetc(bin);
+ linepos--;
+}
+
+static int
+getrune(void)
+{
+ int c;
+
+ if(ineof)
+ return Beof;
+ c = Bgetrune(bin);
+ if(c == Beof)
+ ineof = 1;
+ linepos++;
+ return c;
+}
+
+static void
+ungetrune(void)
+{
+ if(ineof)
+ return;
+ Bungetrune(bin);
+ linepos--;
+}
+
+void
+addinclude(char *s)
+{
+ int i;
+
+ for(i = 0; i < MaxIncPath; i++){
+ if(incpath[i] == 0){
+ incpath[i] = s;
+ return;
+ }
+ }
+ fatal("out of include path space");
+}
+
+File*
+mkfile(char *name, int abs, int off, int in, char *act, int actoff, int sbl)
+{
+ File *f;
+
+ f = allocmem(sizeof *f);
+ f->name = name;
+ f->abs = abs;
+ f->off = off;
+ f->in = in;
+ f->act = act;
+ f->actoff = actoff;
+ f->sbl = sbl;
+ return f;
+}
+
+int
+addfile(File *f)
+{
+ if(nfiles >= lenfiles){
+ lenfiles = nfiles+32;
+ files = reallocmem(files, lenfiles*sizeof(File*));
+ }
+ files[nfiles] = f;
+ return nfiles++;
+}
+
+void
+includef(Sym *file)
+{
+ Biobuf *b;
+ char *p, buf[MaxPath];
+ int i;
+
+ linestack[bstack].line = lineno;
+ linestack[bstack].pos = linepos;
+ bstack++;
+ if(bstack >= MaxInclude)
+ fatal("%L: include file depth too great", curline());
+ p = "";
+ if(file->name[0] != '/')
+ p = srcdir;
+ seprint(buf, buf+sizeof(buf), "%s%s", p, file->name);
+ b = Bopen(buf, OREAD);
+ for(i = 0; b == nil && i < MaxIncPath && incpath[i] != nil && file->name[0] != '/'; i++){
+ seprint(buf, buf+sizeof(buf), "%s/%s", incpath[i], file->name);
+ b = Bopen(buf, OREAD);
+ }
+ bins[bstack] = b;
+ if(bins[bstack] == nil){
+ yyerror("can't include %s: %r", file->name);
+ bstack--;
+ }else{
+ addfile(mkfile(strdup(buf), lineno+1, -lineno, lineno, nil, 0, -1));
+ lineno++;
+ linepos = Linestart;
+ }
+ bin = bins[bstack];
+}
+
+/*
+ * we hit eof in the current file
+ * revert to the file which included it.
+ */
+static void
+popinclude(void)
+{
+ Fline fl;
+ File *f;
+ int oline, opos, ln;
+
+ ineof = 0;
+ bstack--;
+ bin = bins[bstack];
+ oline = linestack[bstack].line;
+ opos = linestack[bstack].pos;
+ fl = fline(oline);
+ f = fl.file;
+ ln = fl.line;
+ lineno++;
+ linepos = opos;
+ addfile(mkfile(f->name, lineno, ln-lineno, f->in, f->act, f->actoff, -1));
+}
+
+/*
+ * convert an absolute Line into a file and line within the file
+ */
+Fline
+fline(int absline)
+{
+ Fline fl;
+ int l, r, m, s;
+
+ if(absline < files[lastfile]->abs
+ || lastfile+1 < nfiles && absline >= files[lastfile+1]->abs){
+ lastfile = 0;
+ l = 0;
+ r = nfiles - 1;
+ while(l <= r){
+ m = (r + l) / 2;
+ s = files[m]->abs;
+ if(s <= absline){
+ l = m + 1;
+ lastfile = m;
+ }else
+ r = m - 1;
+ }
+ }
+
+ fl.file = files[lastfile];
+ fl.line = absline + files[lastfile]->off;
+ return fl;
+}
+
+/*
+ * read a comment
+ */
+static int
+lexcom(void)
+{
+ File *f;
+ char buf[StrSize], *s, *t, *act;
+ int i, n, c, actline;
+
+ i = 0;
+ while((c = Getc()) != '\n'){
+ if(c == Beof)
+ return -1;
+ if(i < sizeof(buf)-1)
+ buf[i++] = c;
+ }
+ buf[i] = 0;
+
+ lineno++;
+ linepos = Linestart;
+
+ if(strncmp(buf, "line ", 5) != 0 && strncmp(buf, "line\t", 5) != 0)
+ return 0;
+ for(s = buf+5; *s == ' ' || *s == '\t'; s++)
+ ;
+ if(!(cmap(*s) & Mdigit))
+ return 0;
+ n = 0;
+ for(; cmap(c = *s) & Mdigit; s++)
+ n = n * 10 + c - '0';
+ for(; *s == ' ' || *s == '\t'; s++)
+ ;
+ if(*s != '"')
+ return 0;
+ s++;
+ t = strchr(s, '"');
+ if(t == nil || t[1] != '\0')
+ return 0;
+ *t = '\0';
+
+ f = files[nfiles - 1];
+ if(n == f->off+lineno && strcmp(s, f->name) == 0)
+ return 1;
+ act = f->name;
+ actline = lineno + f->off;
+ if(f->act != nil){
+ actline += f->actoff;
+ act = f->act;
+ }
+ addfile(mkfile(strdup(s), lineno, n-lineno, f->in, act, actline - n, -1));
+
+ return 1;
+}
+
+Line
+curline(void)
+{
+ Line line;
+
+ line.line = lineno;
+ line.pos = linepos;
+ return line;
+}
+
+int
+lineconv(Fmt *f)
+{
+ Fline fl;
+ File *file;
+ Line inl, line;
+ char buf[StrSize], *s;
+
+ line = va_arg(f->args, Line);
+
+ if(line.line < 0)
+ return fmtstrcpy(f, "<noline>");
+ fl = fline(line.line);
+ file = fl.file;
+
+ s = seprint(buf, buf+sizeof(buf), "%s:%d", file->name, fl.line);
+ if(file->act != nil)
+ s = seprint(s, buf+sizeof(buf), " [ %s:%d ]", file->act, file->actoff+fl.line);
+ if(file->in >= 0){
+ inl.line = file->in;
+ inl.pos = 0;
+ seprint(s, buf+sizeof(buf), ": %L", inl);
+ }
+ return fmtstrcpy(f, buf);
+}
+
+static char*
+posconv(char *s, char *e, Line line)
+{
+ Fline fl;
+
+ if(line.line < 0)
+ return secpy(s, e, "nopos");
+
+ fl = fline(line.line);
+ return seprint(s, e, "%s:%d.%d", fl.file->name, fl.line, line.pos);
+}
+
+int
+srcconv(Fmt *f)
+{
+ Src src;
+ char buf[StrSize], *s;
+
+ src = va_arg(f->args, Src);
+ s = posconv(buf, buf+sizeof(buf), src.start);
+ s = secpy(s, buf+sizeof(buf), ",");
+ posconv(s, buf+sizeof(buf), src.stop);
+
+ return fmtstrcpy(f, buf);
+}
+
+int
+lexid(int c)
+{
+ Sym *sym;
+ char id[StrSize*UTFmax+1], *p;
+ Rune r;
+ int i, t;
+
+ p = id;
+ i = 0;
+ for(;;){
+ if(i < StrSize){
+ if(c < Runeself)
+ *p++ = c;
+ else{
+ r = c;
+ p += runetochar(p, &r);
+ }
+ i++;
+ }
+ c = getrune();
+ if(c == Beof
+ || !(cmap(c) & (Malpha|Mdigit))){
+ ungetrune();
+ break;
+ }
+ }
+ *p = '\0';
+ sym = enter(id, Lid);
+ t = sym->token;
+ if(t == Lid || t == Ltid)
+ yylval.tok.v.idval = sym;
+ return t;
+}
+
+Long
+strtoi(char *t, int base)
+{
+ char *s;
+ Long v;
+ int c, neg, ck;
+
+ neg = 0;
+ if(t[0] == '-'){
+ neg = 1;
+ t++;
+ }else if(t[0] == '+')
+ t++;
+ v = 0;
+ for(s = t; c = *s; s++){
+ ck = cmap(c);
+ if(ck & Mdigit)
+ c -= '0';
+ else if(ck & Mlower)
+ c = c - 'a' + 10;
+ else if(ck & Mupper)
+ c = c - 'A' + 10;
+ if(c >= base){
+ yyerror("digit '%c' not radix %d", *s, base);
+ return -1;
+ }
+ v = v * base + c;
+ }
+ if(neg)
+ return -v;
+ return v;
+}
+
+static int
+digit(int c, int base)
+{
+ int cc, ck;
+
+ cc = c;
+ ck = cmap(c);
+ if(ck & Mdigit)
+ c -= '0';
+ else if(ck & Mlower)
+ c = c - 'a' + 10;
+ else if(ck & Mupper)
+ c = c - 'A' + 10;
+ else if(ck & Munder)
+ {}
+ else
+ return -1;
+ if(c >= base)
+ yyerror("digit '%c' not radix %d", cc, base);
+ return c;
+}
+
+double
+strtodb(char *t, int base)
+{
+ double num, dem;
+ int neg, eneg, dig, exp, c, d;
+
+ num = 0;
+ neg = 0;
+ dig = 0;
+ exp = 0;
+ eneg = 0;
+
+ c = *t++;
+ if(c == '-' || c == '+'){
+ if(c == '-')
+ neg = 1;
+ c = *t++;
+ }
+ while((d = digit(c, base)) >= 0){
+ num = num*base + d;
+ c = *t++;
+ }
+ if(c == '.')
+ c = *t++;
+ while((d = digit(c, base)) >= 0){
+ num = num*base + d;
+ dig++;
+ c = *t++;
+ }
+ if(c == 'e' || c == 'E'){
+ c = *t++;
+ if(c == '-' || c == '+'){
+ if(c == '-'){
+ dig = -dig;
+ eneg = 1;
+ }
+ c = *t++;
+ }
+ while((d = digit(c, base)) >= 0){
+ exp = exp*base + d;
+ c = *t++;
+ }
+ }
+ exp -= dig;
+ if(exp < 0){
+ exp = -exp;
+ eneg = !eneg;
+ }
+ dem = rpow(base, exp);
+ if(eneg)
+ num /= dem;
+ else
+ num *= dem;
+ if(neg)
+ return -num;
+ return num;
+}
+
+/*
+ * parse a numeric identifier
+ * format [0-9]+(r[0-9A-Za-z]+)?
+ * or ([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)?
+ */
+int
+lexnum(int c)
+{
+ char buf[StrSize], *base;
+ enum { Int, Radix, RadixSeen, Frac, ExpSeen, ExpSignSeen, Exp, FracB } state;
+ double d;
+ Long v;
+ int i, ck;
+
+ i = 0;
+ buf[i++] = c;
+ state = Int;
+ if(c == '.')
+ state = Frac;
+ base = nil;
+ for(;;){
+ c = Getc();
+ if(c == Beof){
+ yyerror("end of file in numeric constant");
+ return Leof;
+ }
+
+ ck = cmap(c);
+ switch(state){
+ case Int:
+ if(ck & Mdigit)
+ break;
+ if(ck & Mexp){
+ state = ExpSeen;
+ break;
+ }
+ if(ck & Mradix){
+ base = &buf[i];
+ state = RadixSeen;
+ break;
+ }
+ if(c == '.'){
+ state = Frac;
+ break;
+ }
+ goto done;
+ case RadixSeen:
+ case Radix:
+ if(ck & (Mdigit|Malpha)){
+ state = Radix;
+ break;
+ }
+ if(c == '.'){
+ state = FracB;
+ break;
+ }
+ goto done;
+ case Frac:
+ if(ck & Mdigit)
+ break;
+ if(ck & Mexp)
+ state = ExpSeen;
+ else
+ goto done;
+ break;
+ case FracB:
+ if(ck & (Mdigit|Malpha))
+ break;
+ goto done;
+ case ExpSeen:
+ if(ck & Msign){
+ state = ExpSignSeen;
+ break;
+ }
+ /* fall through */
+ case ExpSignSeen:
+ case Exp:
+ if(ck & Mdigit){
+ state = Exp;
+ break;
+ }
+ goto done;
+ }
+ if(i < StrSize-1)
+ buf[i++] = c;
+ }
+done:
+ buf[i] = 0;
+ unGetc();
+ switch(state){
+ default:
+ yyerror("malformed numerical constant '%s'", buf);
+ yylval.tok.v.ival = 0;
+ return Lconst;
+ case Radix:
+ *base++ = '\0';
+ v = strtoi(buf, 10);
+ if(v < 0)
+ break;
+ if(v < 2 || v > 36){
+ yyerror("radix '%s' must be between 2 and 36", buf);
+ break;
+ }
+ v = strtoi(base, v);
+ break;
+ case Int:
+ v = strtoi(buf, 10);
+ break;
+ case Frac:
+ case Exp:
+ d = strtod(buf, nil);
+ yylval.tok.v.rval = d;
+ return Lrconst;
+ case FracB:
+ *base++ = '\0';
+ v = strtoi(buf, 10);
+ if(v < 0)
+ break;
+ if(v < 2 || v > 36){
+ yyerror("radix '%s' must be between 2 and 36", buf);
+ break;
+ }
+ d = strtodb(base, v);
+ yylval.tok.v.rval = d;
+ return Lrconst;
+ }
+ yylval.tok.v.ival = v;
+ return Lconst;
+}
+
+int
+escchar(void)
+{
+ char buf[4+1];
+ int c, i;
+
+ c = getrune();
+ if(c == Beof)
+ return Beof;
+ if(c == 'u'){
+ for(i = 0; i < 4; i++){
+ c = getrune();
+ if(c == Beof || !(cmap(c) & (Mdigit|Mhex))){
+ yyerror("malformed \\u escape sequence");
+ ungetrune();
+ break;
+ }
+ buf[i] = c;
+ }
+ buf[i] = 0;
+ return strtoul(buf, 0, 16);
+ }
+ if(c < 256 && (i = escmap[c]) >= 0)
+ return i;
+ yyerror("unrecognized escape \\%C", c);
+ return c;
+}
+
+void
+lexstring(int israw)
+{
+ char *str;
+ int c, t, startlno;
+ Rune r;
+ int len, alloc;
+
+ alloc = 32;
+ len = 0;
+ str = allocmem(alloc * sizeof(str));
+ startlno = lineno;
+ for(;;){
+ c = getrune();
+ if(israw){
+ switch(c){
+ case '`':
+ yylval.tok.v.idval = enterstring(str, len);
+ return;
+ case '\n':
+ lineno++;
+ linepos = Linestart;
+ break;
+ case Beof:
+ t = lineno;
+ lineno = startlno;
+ yyerror("end of file in raw string constant");
+ lineno = t;
+ yylval.tok.v.idval = enterstring(str, len);
+ return;
+ }
+ }else{
+ switch(c){
+ case '\\':
+ c = escchar();
+ if(c != Beof)
+ break;
+ /* fall through */
+ case Beof:
+ yyerror("end of file in string constant");
+ yylval.tok.v.idval = enterstring(str, len);
+ return;
+ case '\n':
+ yyerror("newline in string constant");
+ lineno++;
+ linepos = Linestart;
+ yylval.tok.v.idval = enterstring(str, len);
+ return;
+ case '"':
+ yylval.tok.v.idval = enterstring(str, len);
+ return;
+ }
+ }
+ while(len+UTFmax+1 >= alloc){
+ alloc += 32;
+ str = reallocmem(str, alloc * sizeof(str));
+ }
+ r = c;
+ len += runetochar(&str[len], &r);
+ str[len] = '\0';
+ }
+}
+
+static int
+lex(void)
+{
+ int c;
+
+loop:
+ yylval.tok.src.start.line = lineno;
+ yylval.tok.src.start.pos = linepos;
+ c = getrune(); /* ehg: outside switch() to avoid bug in VisualC++5.0 */
+ switch(c){
+ case Beof:
+ Bterm(bin);
+ if(bstack == 0)
+ return Leof;
+ popinclude();
+ break;
+ case '#':
+ if(lexcom() < 0){
+ Bterm(bin);
+ if(bstack == 0)
+ return Leof;
+ popinclude();
+ }
+ break;
+
+ case '\n':
+ lineno++;
+ linepos = Linestart;
+ goto loop;
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\v':
+ case '\f':
+ goto loop;
+ case '"':
+ lexstring(0);
+ return Lsconst;
+ case '`':
+ lexstring(1);
+ return Lsconst;
+ case '\'':
+ c = getrune();
+ if(c == '\\')
+ c = escchar();
+ if(c == Beof){
+ yyerror("end of file in character constant");
+ return Beof;
+ }else
+ yylval.tok.v.ival = c;
+ c = Getc();
+ if(c != '\'') {
+ yyerror("missing closing '");
+ unGetc();
+ }
+ return Lconst;
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case ',':
+ case ';':
+ case '~':
+ return c;
+
+ case ':':
+ c = Getc();
+ if(c == ':')
+ return Lcons;
+ if(c == '=')
+ return Ldeclas;
+ unGetc();
+ return ':';
+
+ case '.':
+ c = Getc();
+ unGetc();
+ if(c != Beof && (cmap(c) & Mdigit))
+ return lexnum('.');
+ return '.';
+
+ case '|':
+ c = Getc();
+ if(c == '=')
+ return Loreq;
+ if(c == '|')
+ return Loror;
+ unGetc();
+ return '|';
+
+ case '&':
+ c = Getc();
+ if(c == '=')
+ return Landeq;
+ if(c == '&')
+ return Landand;
+ unGetc();
+ return '&';
+
+ case '^':
+ c = Getc();
+ if(c == '=')
+ return Lxoreq;
+ unGetc();
+ return '^';
+
+ case '*':
+ c = Getc();
+ if(c == '=')
+ return Lmuleq;
+ if(c == '*'){
+ c = Getc();
+ if(c == '=')
+ return Lexpeq;
+ unGetc();
+ return Lexp;
+ }
+ unGetc();
+ return '*';
+ case '/':
+ c = Getc();
+ if(c == '=')
+ return Ldiveq;
+ unGetc();
+ return '/';
+ case '%':
+ c = Getc();
+ if(c == '=')
+ return Lmodeq;
+ unGetc();
+ return '%';
+ case '=':
+ c = Getc();
+ if(c == '=')
+ return Leq;
+ if(c == '>')
+ return Llabs;
+ unGetc();
+ return '=';
+ case '!':
+ c = Getc();
+ if(c == '=')
+ return Lneq;
+ unGetc();
+ return '!';
+ case '>':
+ c = Getc();
+ if(c == '=')
+ return Lgeq;
+ if(c == '>'){
+ c = Getc();
+ if(c == '=')
+ return Lrsheq;
+ unGetc();
+ return Lrsh;
+ }
+ unGetc();
+ return '>';
+
+ case '<':
+ c = Getc();
+ if(c == '=')
+ return Lleq;
+ if(c == '-')
+ return Lcomm;
+ if(c == '<'){
+ c = Getc();
+ if(c == '=')
+ return Llsheq;
+ unGetc();
+ return Llsh;
+ }
+ unGetc();
+ return '<';
+
+ case '+':
+ c = Getc();
+ if(c == '=')
+ return Laddeq;
+ if(c == '+')
+ return Linc;
+ unGetc();
+ return '+';
+
+ case '-':
+ c = Getc();
+ if(c == '=')
+ return Lsubeq;
+ if(c == '-')
+ return Ldec;
+ if(c == '>')
+ return Lmdot;
+ unGetc();
+ return '-';
+
+ case '1': case '2': case '3': case '4': case '5':
+ case '0': case '6': case '7': case '8': case '9':
+ return lexnum(c);
+
+ default:
+ if(cmap(c) & Malpha)
+ return lexid(c);
+ yyerror("unknown character %c", c);
+ break;
+ }
+ goto loop;
+}
+
+int
+yylex(void)
+{
+ int t;
+
+ t = lex();
+ yylval.tok.src.stop.line = lineno;
+ yylval.tok.src.stop.pos = linepos;
+ lasttok = t;
+ lastyylval = yylval;
+ return t;
+}
+
+static char*
+toksp(int t)
+{
+ Keywd *k;
+ static char buf[256];
+
+ switch(t){
+ case Lconst:
+ snprint(buf, sizeof(buf), "%lld", lastyylval.tok.v.ival);
+ return buf;
+ case Lrconst:
+ snprint(buf, sizeof(buf), "%f", lastyylval.tok.v.rval);
+ return buf;
+ case Lsconst:
+ snprint(buf, sizeof(buf), "\"%s\"", lastyylval.tok.v.idval->name);
+ return buf;
+ case Ltid:
+ case Lid:
+ return lastyylval.tok.v.idval->name;
+ }
+ for(k = keywords; k->name != nil; k++)
+ if(t == k->token)
+ return k->name;
+ for(k = tokwords; k->name != nil; k++)
+ if(t == k->token)
+ return k->name;
+ if(t < 0 || t > 255)
+ fatal("bad token %d in toksp()", t);
+ buf[0] = t;
+ buf[1] = '\0';
+ return buf;
+}
+
+Sym*
+enterstring(char *str, int n)
+{
+ Sym *s;
+ char *p, *e;
+ ulong h;
+ int c, c0;
+
+ e = str + n;
+ h = 0;
+ for(p = str; p < e; p++){
+ c = *p;
+ c ^= c << 6;
+ h += (c << 11) ^ (c >> 1);
+ c = *p;
+ h ^= (c << 14) + (c << 7) + (c << 4) + c;
+ }
+
+ c0 = str[0];
+ h %= HashSize;
+ for(s = strings[h]; s != nil; s = s->next){
+ if(s->name[0] == c0 && s->len == n && memcmp(s->name, str, n) == 0){
+ free(str);
+ return s;
+ }
+ }
+
+ if(n == 0)
+ return enter("", 0);
+
+ s = allocmem(sizeof(Sym));
+ memset(s, 0, sizeof(Sym));
+ s->name = str;
+ s->len = n;
+ s->next = strings[h];
+ strings[h] = s;
+ return s;
+}
+
+int
+symcmp(Sym *s, Sym *t)
+{
+ int n, c;
+
+ n = s->len;
+ if(n > t->len)
+ n = t->len;
+ c = memcmp(s->name, t->name, n);
+ if(c == 0)
+ return s->len - t->len;
+ return c;
+}
+
+Sym*
+stringcat(Sym *s, Sym *t)
+{
+ char *str;
+ int n;
+
+ n = s->len + t->len;
+ str = allocmem(n+1);
+ memmove(str, s->name, s->len);
+ memmove(str+s->len, t->name, t->len);
+ str[n] = '\0';
+ return enterstring(str, n);
+}
+
+Sym*
+enter(char *name, int token)
+{
+ Sym *s;
+ char *p;
+ ulong h;
+ int c0, c, n;
+
+ c0 = name[0];
+ h = 0;
+ for(p = name; c = *p; p++){
+ c ^= c << 6;
+ h += (c << 11) ^ (c >> 1);
+ c = *p;
+ h ^= (c << 14) + (c << 7) + (c << 4) + c;
+ }
+ n = p - name;
+
+ h %= HashSize;
+ for(s = symbols[h]; s != nil; s = s->next)
+ if(s->name[0] == c0 && strcmp(s->name, name) == 0)
+ return s;
+
+ s = allocmem(sizeof(Sym));
+ memset(s, 0, sizeof(Sym));
+ s->hash = h;
+ s->name = allocmem(n+1);
+ memmove(s->name, name, n+1);
+ if(token == 0)
+ token = Lid;
+ s->token = token;
+ s->next = symbols[h];
+ s->len = n;
+ symbols[h] = s;
+ return s;
+}
+
+char*
+stringpr(char *buf, char *end, Sym *sym)
+{
+ char sb[30], *s, *p;
+ int i, c, n;
+
+ s = sym->name;
+ n = sym->len;
+ if(n > 10)
+ n = 10;
+ p = sb;
+ *p++ = '"';
+ for(i = 0; i < n; i++){
+ c = s[i];
+ switch(c){
+ case '\\':
+ case '"':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\b':
+ case '\a':
+ case '\v':
+ case '\0':
+ *p++ = '\\';
+ *p++ = unescmap[c];
+ break;
+ default:
+ *p++ = c;
+ break;
+ }
+ }
+ if(n != sym->len){
+ *p++ = '.';
+ *p++ = '.';
+ *p++ = '.';
+ }
+ *p++ = '"';
+ *p = 0;
+ return secpy(buf, end, sb);
+}
+
+void
+warn(Line line, char *fmt, ...)
+{
+ char buf[4096];
+ va_list arg;
+
+ if(errors || !dowarn)
+ return;
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%L: warning: %s\n", line, buf);
+}
+
+void
+nwarn(Node *n, char *fmt, ...)
+{
+ char buf[4096];
+ va_list arg;
+
+ if(errors || !dowarn)
+ return;
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%L: warning: %s\n", n->src.start, buf);
+}
+
+void
+error(Line line, char *fmt, ...)
+{
+ char buf[4096];
+ va_list arg;
+
+ errors++;
+ if(errors >= maxerr){
+ if(errors == maxerr)
+ fprint(2, "too many errors, stopping\n");
+ return;
+ }
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%L: %s\n", line, buf);
+}
+
+void
+nerror(Node *n, char *fmt, ...)
+{
+ char buf[4096];
+ va_list arg;
+
+ errors++;
+ if(errors >= maxerr){
+ if(errors == maxerr)
+ fprint(2, "too many errors, stopping\n");
+ return;
+ }
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%L: %s\n", n->src.start, buf);
+}
+
+void
+yyerror(char *fmt, ...)
+{
+ char buf[4096];
+ va_list arg;
+
+ errors++;
+ if(errors >= maxerr){
+ if(errors == maxerr)
+ fprint(2, "too many errors, stopping\n");
+ return;
+ }
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ if(lasttok != 0)
+ fprint(2, "%L: near ` %s ` : %s\n", curline(), toksp(lasttok), buf);
+ else
+ fprint(2, "%L: %s\n", curline(), buf);
+}
+
+void
+fatal(char *fmt, ...)
+{
+ char buf[4096];
+ va_list arg;
+
+ if(errors == 0 || isfatal){
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "fatal limbo compiler error: %s\n", buf);
+ }
+ if(bout != nil)
+ remove(outfile);
+ if(bsym != nil)
+ remove(symfile);
+ if(isfatal)
+ abort();
+ exits(buf);
+}
+
+int
+gfltconv(Fmt *f)
+{
+ double d;
+ char buf[32];
+
+ d = va_arg(f->args, double);
+ g_fmt(buf, d, 'e');
+ return fmtstrcpy(f, buf);
+}
+
+char*
+secpy(char *p, char *e, char *s)
+{
+ int c;
+
+ if(p == e){
+ p[-1] = '\0';
+ return p;
+ }
+ for(; c = *s; s++){
+ *p++ = c;
+ if(p == e){
+ p[-1] = '\0';
+ return p;
+ }
+ }
+ *p = '\0';
+ return p;
+}
+
+char*
+seprint(char *buf, char *end, char *fmt, ...)
+{
+ va_list arg;
+
+ if(buf == end)
+ return buf;
+ va_start(arg, fmt);
+ buf = vseprint(buf, end, fmt, arg);
+ va_end(arg);
+ return buf;
+}
+
+void*
+allocmem(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == nil)
+ fatal("out of memory");
+ return p;
+}
+
+void*
+reallocmem(void *p, ulong n)
+{
+ if(p == nil)
+ p = malloc(n);
+ else
+ p = realloc(p, n);
+ if(p == nil)
+ fatal("out of memory");
+ return p;
+}
--- /dev/null
+++ b/limbo/limbo.h
@@ -1,0 +1,702 @@
+#include "lib9.h"
+#include "bio.h"
+#include "isa.h"
+#include "mathi.h"
+
+/* internal dis ops */
+#define IEXC MAXDIS
+#define IEXC0 (MAXDIS+1)
+#define INOOP (MAXDIS+2)
+
+/* temporary */
+#define LDT 1
+
+#ifndef Extern
+#define Extern extern
+#endif
+
+#define YYMAXDEPTH 200
+
+typedef struct Addr Addr;
+typedef struct Case Case;
+typedef struct Decl Decl;
+typedef struct Desc Desc;
+typedef struct Dlist Dlist;
+typedef struct Except Except;
+typedef struct File File;
+typedef struct Fline Fline;
+typedef struct Inst Inst;
+typedef struct Label Label;
+typedef struct Line Line;
+typedef struct Node Node;
+typedef struct Ok Ok;
+typedef struct Src Src;
+typedef struct Sym Sym;
+typedef struct Szal Szal;
+typedef struct Tattr Tattr;
+typedef struct Teq Teq;
+typedef struct Tpair Tpair;
+typedef struct Type Type;
+typedef struct Typelist Typelist;
+
+typedef double Real;
+typedef vlong Long;
+
+enum
+{
+ STemp = NREG * IBY2WD,
+ RTemp = STemp+IBY2WD,
+ DTemp = RTemp+IBY2WD,
+ MaxTemp = DTemp+IBY2WD,
+ MaxReg = 1<<16,
+ MaxAlign = IBY2LG,
+ StrSize = 256,
+ NumSize = 32, /* max length of printed */
+ MaxIncPath = 32, /* max directories in include path */
+ MaxScope = 64, /* max nested {} */
+ MaxInclude = 32, /* max nested include "" */
+ ScopeBuiltin = 0,
+ ScopeNils = 1,
+ ScopeGlobal = 2
+};
+
+/*
+ * return tuple from expression type checking
+ */
+struct Ok
+{
+ int ok;
+ int allok;
+};
+
+/*
+ * return tuple from type sizing
+ */
+struct Szal
+{
+ int size;
+ int align;
+};
+
+/*
+ * return tuple for file/line numbering
+ */
+struct Fline
+{
+ File *file;
+ int line;
+};
+
+struct File
+{
+ char *name;
+ int abs; /* absolute line of start of the part of file */
+ int off; /* offset to line in the file */
+ int in; /* absolute line where included */
+ char *act; /* name of real file with #line fake file */
+ int actoff; /* offset from fake line to real line */
+ int sbl; /* symbol file number */
+};
+
+struct Line
+{
+ int line;
+ int pos; /* character within the line */
+};
+
+struct Src
+{
+ Line start;
+ Line stop;
+};
+
+enum
+{
+ Aimm, /* immediate */
+ Amp, /* global */
+ Ampind, /* global indirect */
+ Afp, /* activation frame */
+ Afpind, /* frame indirect */
+ Apc, /* branch */
+ Adesc, /* type descriptor immediate */
+ Aoff, /* offset in module description table */
+ Anoff, /* above encoded as -ve */
+ Aerr, /* error */
+ Anone, /* no operand */
+ Aldt, /* linkage descriptor table immediate */
+ Aend
+};
+
+struct Addr
+{
+ long reg;
+ long offset;
+ Decl *decl;
+};
+
+struct Inst
+{
+ Src src;
+ ushort op;
+ long pc;
+ uchar reach; /* could a control path reach this instruction? */
+ uchar sm; /* operand addressing modes */
+ uchar mm;
+ uchar dm;
+ Addr s; /* operands */
+ Addr m;
+ Addr d;
+ Inst *branch; /* branch destination */
+ Inst *next;
+ int block; /* blocks nested inside */
+};
+
+struct Case
+{
+ int nlab;
+ int nsnd;
+ long offset; /* offset in mp */
+ Label *labs;
+ Node *wild; /* if nothing matches */
+ Inst *iwild;
+};
+
+struct Label
+{
+ Node *node;
+ char isptr; /* true if the labelled alt channel is a pointer */
+ Node *start; /* value in range [start, stop) => code */
+ Node *stop;
+ Inst *inst;
+};
+
+enum
+{
+ Dtype,
+ Dfn,
+ Dglobal,
+ Darg,
+ Dlocal,
+ Dconst,
+ Dfield,
+ Dtag, /* pick tags */
+ Dimport, /* imported identifier */
+ Dunbound, /* unbound identified */
+ Dundef,
+ Dwundef, /* undefined, but don't whine */
+
+ Dend
+};
+
+struct Decl
+{
+ Src src; /* where declaration */
+ Sym *sym;
+ uchar store; /* storage class */
+ uchar nid; /* block grouping for locals */
+ schar caninline; /* inline function */
+ uchar das; /* declared with := */
+ Decl *dot; /* parent adt or module */
+ Type *ty;
+ int refs; /* number of references */
+ long offset;
+ int tag; /* union tag */
+
+ uchar scope; /* in which it was declared */
+ uchar handler; /* fn has exception handler in body */
+ Decl *next; /* list in same scope, field or argument list, etc. */
+ Decl *old; /* declaration of the symbol in enclosing scope */
+
+ Node *eimport; /* expr from which imported */
+ Decl *importid; /* identifier imported */
+ Decl *timport; /* stack of identifiers importing a type */
+
+ Node *init; /* data initialization */
+ int tref; /* 1 => is a tmp; >=2 => tmp in use */
+ char cycle; /* can create a cycle */
+ char cyc; /* so labelled in source */
+ char cycerr; /* delivered an error message for cycle? */
+ char implicit; /* implicit first argument in an adt? */
+
+ Decl *iface; /* used external declarations in a module */
+
+ Decl *locals; /* locals for a function */
+ Decl *link; /* pointer to parent function or function argument or local share or parent type dec */
+ Inst *pc; /* start of function */
+ /* Inst *endpc; */ /* limit of function - unused */
+
+ Desc *desc; /* heap descriptor */
+};
+
+struct Desc
+{
+ int id; /* dis type identifier */
+ uchar used; /* actually used in output? */
+ uchar *map; /* byte map of pointers */
+ long size; /* length of the object */
+ long nmap; /* length of good bytes in map */
+ Desc *next;
+};
+
+struct Dlist
+{
+ Decl *d;
+ Dlist *next;
+};
+
+struct Except
+{
+ Inst *p1; /* first pc covered */
+ Inst *p2; /* last pc not covered */
+ Case *c; /* exception case instructions */
+ Decl *d; /* exception definition if any */
+ Node *zn; /* list of nodes to zero in handler */
+ Desc *desc; /* descriptor map for above */
+ int ne; /* number of exceptions (ie not strings) in case */
+ Except *next;
+};
+
+struct Sym
+{
+ ushort token;
+ char *name;
+ int len;
+ int hash;
+ Sym *next;
+ Decl *decl;
+ Decl *unbound; /* place holder for unbound symbols */
+};
+
+/*
+ * ops for nodes
+ */
+enum
+{
+ Oadd = 1,
+ Oaddas,
+ Oadr,
+ Oadtdecl,
+ Oalt,
+ Oand,
+ Oandand,
+ Oandas,
+ Oarray,
+ Oas,
+ Obreak,
+ Ocall,
+ Ocase,
+ Ocast,
+ Ochan,
+ Ocomma,
+ Ocomp,
+ Ocondecl,
+ Ocons,
+ Oconst,
+ Ocont,
+ Odas,
+ Odec,
+ Odiv,
+ Odivas,
+ Odo,
+ Odot,
+ Oelem,
+ Oeq,
+ Oexcept,
+ Oexdecl,
+ Oexit,
+ Oexp,
+ Oexpas,
+ Oexstmt,
+ Ofielddecl,
+ Ofnptr,
+ Ofor,
+ Ofunc,
+ Ogeq,
+ Ogt,
+ Ohd,
+ Oif,
+ Oimport,
+ Oinc,
+ Oind,
+ Oindex,
+ Oinds,
+ Oindx,
+ Oinv,
+ Ojmp,
+ Olabel,
+ Olen,
+ Oleq,
+ Oload,
+ Olsh,
+ Olshas,
+ Olt,
+ Omdot,
+ Omod,
+ Omodas,
+ Omoddecl,
+ Omul,
+ Omulas,
+ Oname,
+ Oneg,
+ Oneq,
+ Onot,
+ Onothing,
+ Oor,
+ Ooras,
+ Ooror,
+ Opick,
+ Opickdecl,
+ Opredec,
+ Opreinc,
+ Oraise,
+ Orange,
+ Orcv,
+ Oref,
+ Oret,
+ Orsh,
+ Orshas,
+ Oscope,
+ Oself,
+ Oseq,
+ Oslice,
+ Osnd,
+ Ospawn,
+ Osub,
+ Osubas,
+ Otagof,
+ Otl,
+ Otuple,
+ Otype,
+ Otypedecl,
+ Oused,
+ Ovardecl,
+ Ovardecli,
+ Owild,
+ Oxor,
+ Oxoras,
+
+ Oend
+};
+
+/*
+ * moves
+ */
+enum
+{
+ Mas,
+ Mcons,
+ Mhd,
+ Mtl,
+
+ Mend
+};
+
+/*
+ * addressability
+ */
+enum
+{
+ Rreg, /* v(fp) */
+ Rmreg, /* v(mp) */
+ Roff, /* $v */
+ Rnoff, /* $v encoded as -ve */
+ Rdesc, /* $v */
+ Rdescp, /* $v */
+ Rconst, /* $v */
+ Ralways, /* preceeding are always addressable */
+ Radr, /* v(v(fp)) */
+ Rmadr, /* v(v(mp)) */
+ Rcant, /* following are not quite addressable */
+ Rpc, /* branch address */
+ Rmpc, /* cross module branch address */
+ Rareg, /* $v(fp) */
+ Ramreg, /* $v(mp) */
+ Raadr, /* $v(v(fp)) */
+ Ramadr, /* $v(v(mp)) */
+ Rldt, /* $v */
+
+ Rend
+};
+
+#define PARENS 1
+#define TEMP 2
+#define FNPTRA 4 /* argument */
+#define FNPTR2 8 /* 2nd parameter */
+#define FNPTRN 16 /* use -ve offset */
+#define FNPTR (FNPTRA|FNPTR2|FNPTRN)
+
+struct Node
+{
+ Src src;
+ uchar op;
+ uchar addable;
+ uchar flags;
+ uchar temps;
+ Node *left;
+ Node *right;
+ Type *ty;
+ Decl *decl;
+ Long val; /* for Oconst */
+ Real rval; /* for Oconst */
+};
+
+enum
+{
+ /*
+ * types visible to limbo
+ */
+ Tnone = 0,
+ Tadt,
+ Tadtpick, /* pick case of an adt */
+ Tarray,
+ Tbig, /* 64 bit int */
+ Tbyte, /* 8 bit unsigned int */
+ Tchan,
+ Treal,
+ Tfn,
+ Tint, /* 32 bit int */
+ Tlist,
+ Tmodule,
+ Tref,
+ Tstring,
+ Ttuple,
+ Texception,
+ Tfix,
+ Tpoly,
+
+ /*
+ * internal use types
+ */
+ Tainit, /* array initializers */
+ Talt, /* alt channels */
+ Tany, /* type of nil */
+ Tarrow, /* unresolved ty->id types */
+ Tcase, /* case labels */
+ Tcasel, /* case big labels */
+ Tcasec, /* case string labels */
+ Tdot, /* unresolved ty.id types */
+ Terror,
+ Tgoto, /* goto labels */
+ Tid, /* id with unknown type */
+ Tiface, /* module interface */
+ Texcept, /* exception handler tables */
+ Tinst, /* instantiated adt */
+
+ Tend
+};
+
+enum
+{
+ OKbind = 1 << 0, /* type decls are bound */
+ OKverify = 1 << 1, /* type looks ok */
+ OKsized = 1 << 2, /* started figuring size */
+ OKref = 1 << 3, /* recorded use of type */
+ OKclass = 1 << 4, /* equivalence class found */
+ OKcyc = 1 << 5, /* checked for cycles */
+ OKcycsize = 1 << 6, /* checked for cycles and size */
+ OKmodref = 1 << 7, /* started checking for a module handle */
+
+ OKmask = 0xff,
+
+ /*
+ * recursive marks
+ */
+ TReq = 1 << 0,
+ TRcom = 1 << 1,
+ TRcyc = 1 << 2,
+ TRvis = 1 << 3,
+};
+
+/* type flags */
+#define FULLARGS 1 /* all hidden args added */
+#define INST 2 /* instantiated adt */
+#define CYCLIC 4 /* cyclic type */
+#define POLY 8 /* polymorphic types inside */
+#define NOPOLY 16 /* no polymorphic types inside */
+
+struct Type
+{
+ Src src;
+ uchar kind;
+ uchar varargs; /* if a function, ends with vargs? */
+ uchar ok; /* set when type is verified */
+ uchar linkall; /* put all iface fns in external linkage? */
+ uchar rec; /* in the middle of recursive type */
+ uchar cons; /* exception constant */
+ uchar align; /* alignment in bytes */
+ uchar flags;
+ int sbl; /* slot in .sbl adt table */
+ long sig; /* signature for dynamic type check */
+ long size; /* storage required, in bytes */
+ Decl *decl;
+ Type *tof;
+ Decl *ids;
+ Decl *tags; /* tagged fields in an adt */
+ Decl *polys; /* polymorphic fields in fn or adt */
+ Case *cse; /* case or goto labels */
+ Type *teq; /* temporary equiv class for equiv checking */
+ Type *tcom; /* temporary equiv class for compat checking */
+ Teq *eq; /* real equiv class */
+ Node *val; /* for Tfix, Tfn, Tadt only */
+ union {
+ Node *eraises; /* for Tfn only */
+ Typelist *tlist; /* for Tinst only */
+ Tpair *tmap; /* for Tadt only */
+ } u;
+};
+
+/*
+ * type equivalence classes
+ */
+struct Teq
+{
+ int id; /* for signing */
+ Type *ty; /* an instance of the class */
+ Teq *eq; /* used to link eq sets */
+};
+
+struct Tattr
+{
+ char isptr;
+ char refable;
+ char conable;
+ char big;
+ char vis; /* type visible to users */
+};
+
+enum {
+ Sother,
+ Sloop,
+ Sscope
+};
+
+struct Tpair
+{
+ Type *t1;
+ Type *t2;
+ Tpair *nxt;
+};
+
+struct Typelist
+{
+ Type *t;
+ Typelist *nxt;
+};
+
+Extern Decl **adts;
+Extern Sym *anontupsym; /* name assigned to all anonymouse tuples */
+Extern int arrayz;
+Extern int asmsym; /* generate symbols in assembly language? */
+Extern Biobuf *bins[MaxInclude];
+Extern int blocks;
+Extern Biobuf *bout; /* output file */
+Extern Biobuf *bsym; /* symbol output file; nil => no sym out */
+Extern double canonnan; /* standard nan */
+Extern uchar casttab[Tend][Tend]; /* instruction to cast from [1] to [2] */
+Extern long constval;
+Extern Decl *curfn;
+Extern char debug[256];
+Extern Desc *descriptors; /* list of all possible descriptors */
+Extern int dontcompile; /* dis header flag */
+Extern int dowarn;
+Extern char *emitcode; /* emit stub routines for system module functions */
+Extern int emitdyn; /* emit stub routines as above but for dynamic modules */
+Extern int emitstub; /* emit type and call frames for system modules */
+Extern char *emittab; /* emit table of runtime functions for this module */
+Extern int errors;
+Extern char escmap[256];
+Extern Inst *firstinst;
+Extern long fixss; /* set extent from command line */
+Extern Decl *fndecls;
+Extern Decl **fns;
+Extern int gendis; /* generate dis or asm? */
+Extern Decl *impdecl; /* id of implementation module or union if many */
+Extern Dlist *impdecls; /* id(s) of implementation module(s) */
+/* Extern Sym *impmod; */ /* name of implementation module */
+Extern Decl *impmods; /* name of implementation module(s) */
+Extern Decl *iota;
+Extern uchar isbyteinst[256];
+Extern int isfatal;
+Extern int isrelop[Oend];
+Extern uchar isused[Oend];
+Extern Inst *lastinst;
+Extern int lenadts;
+Extern int maxerr;
+Extern int maxlabdep; /* maximum nesting of breakable/continuable statements */
+Extern long maxstack; /* max size of a stack frame called */
+Extern int mustcompile; /* dis header flag */
+Extern int oldcycles;
+Extern int nadts;
+Extern int newfnptr; /* ISELF and -ve indices */
+Extern int nfns;
+Extern Decl *nildecl; /* declaration for limbo's nil */
+Extern int nlabel;
+Extern int dontinline;
+Extern Line noline;
+Extern Src nosrc;
+Extern uchar opcommute[Oend];
+Extern int opind[Tend];
+Extern uchar oprelinvert[Oend];
+Extern int optims;
+Extern char *outfile;
+Extern Type *precasttab[Tend][Tend];
+Extern int scope;
+Extern Decl *selfdecl; /* declaration for limbo's self */
+Extern uchar sideeffect[Oend];
+Extern char *signdump; /* dump sig for this fn */
+Extern int superwarn;
+Extern char *symfile;
+Extern Type *tany;
+Extern Type *tbig;
+Extern Type *tbyte;
+Extern Type *terror;
+Extern Type *tint;
+Extern Type *tnone;
+Extern Type *treal;
+Extern Node *tree;
+Extern Type *tstring;
+Extern Type *texception;
+Extern Type *tunknown;
+Extern Type *tfnptr;
+Extern Type *rtexception;
+Extern char unescmap[256];
+Extern Src unifysrc;
+Extern Node znode;
+
+extern int *blockstack;
+extern int blockdep;
+extern int nblocks;
+extern File **files;
+extern int nfiles;
+extern uchar chantab[Tend];
+extern uchar disoptab[Oend+1][7];
+extern char *instname[];
+extern char *kindname[Tend];
+extern uchar movetab[Mend][Tend];
+extern char *opname[];
+extern int setisbyteinst[];
+extern int setisused[];
+extern int setsideeffect[];
+extern char *storename[Dend];
+extern int storespace[Dend];
+extern Tattr tattr[Tend];
+
+#include "fns.h"
+
+#pragma varargck type "D" Decl*
+#pragma varargck type "I" Inst*
+#pragma varargck type "K" Decl*
+#pragma varargck type "k" Decl*
+#pragma varargck type "L" Line
+#pragma varargck type "M" Desc*
+#pragma varargck type "n" Node*
+#pragma varargck type "O" int
+#pragma varargck type "O" uint
+#pragma varargck type "g" double
+#pragma varargck type "Q" Node*
+#pragma varargck type "R" Type*
+#pragma varargck type "T" Type*
+#pragma varargck type "t" Type*
+#pragma varargck type "U" Src
+#pragma varargck type "v" Node*
+#pragma varargck type "V" Node*
--- /dev/null
+++ b/limbo/limbo.y
@@ -1,0 +1,1995 @@
+%{
+#include "limbo.h"
+%}
+
+%union
+{
+ struct{
+ Src src;
+ union{
+ Sym *idval;
+ Long ival;
+ Real rval;
+ }v;
+ }tok;
+ Decl *ids;
+ Node *node;
+ Type *type;
+ Typelist *types;
+}
+
+%type <type> type fnarg fnargret fnargretp adtk fixtype iditype dotiditype
+%type <ids> ids rids nids nrids tuplist forms ftypes ftype
+ bclab bctarg ptags rptags polydec
+%type <node> zexp exp monexp term elist zelist celist
+ idatom idterms idterm idlist
+ initlist elemlist elem qual
+ decl topdecls topdecl fndef fbody stmt stmts qstmts qbodies cqstmts cqbodies
+ mdecl adtdecl mfield mfields field fields fnname
+ pstmts pbodies pqual pfields pfbody pdecl dfield dfields
+ eqstmts eqbodies idexc edecl raises tpoly tpolys texp export exportlist forpoly
+%type <types> types
+
+%right <tok.src> '=' Landeq Loreq Lxoreq Llsheq Lrsheq
+ Laddeq Lsubeq Lmuleq Ldiveq Lmodeq Lexpeq Ldeclas
+%left <tok.src> Lload
+%left <tok.src> Loror
+%left <tok.src> Landand
+%right <tok.src> Lcons
+%left <tok.src> '|'
+%left <tok.src> '^'
+%left <tok.src> '&'
+%left <tok.src> Leq Lneq
+%left <tok.src> '<' '>' Lleq Lgeq
+%left <tok.src> Llsh Lrsh
+%left <tok.src> '+' '-'
+%left <tok.src> '*' '/' '%'
+%right <tok.src> Lexp
+%right <tok.src> Lcomm
+
+%left <tok.src> '(' ')' '[' ']' Linc Ldec Lof Lref
+%right <tok.src> Lif Lelse Lfn ':' Lexcept Lraises
+%left <tok.src> Lmdot
+%left <tok.src> '.'
+
+%left <tok.src> Lto
+%left <tok.src> Lor
+
+
+%nonassoc <tok.v.rval> Lrconst
+%nonassoc <tok.v.ival> Lconst
+%nonassoc <tok.v.idval> Lid Ltid Lsconst
+%nonassoc <tok.src> Llabs Lnil
+ '!' '~' Llen Lhd Ltl Ltagof
+ '{' '}' ';'
+ Limplement Limport Linclude
+ Lcon Ltype Lmodule Lcyclic
+ Ladt Larray Llist Lchan Lself
+ Ldo Lwhile Lfor Lbreak
+ Lalt Lcase Lpick Lcont
+ Lreturn Lexit Lspawn Lraise Lfix
+ Ldynamic
+%%
+prog : Limplement ids ';'
+ {
+ impmods = $2;
+ } topdecls
+ {
+ tree = rotater($5);
+ }
+ | topdecls
+ {
+ impmods = nil;
+ tree = rotater($1);
+ }
+ ;
+
+topdecls: topdecl
+ | topdecls topdecl
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkbin(Oseq, $1, $2);
+ }
+ ;
+
+topdecl : error ';'
+ {
+ $$ = nil;
+ }
+ | decl
+ | fndef
+ | adtdecl ';'
+ | mdecl ';'
+ | idatom '=' exp ';'
+ {
+ $$ = mkbin(Oas, $1, $3);
+ }
+ | idterm '=' exp ';'
+ {
+ $$ = mkbin(Oas, $1, $3);
+ }
+ | idatom Ldeclas exp ';'
+ {
+ $$ = mkbin(Odas, $1, $3);
+ }
+ | idterm Ldeclas exp ';'
+ {
+ $$ = mkbin(Odas, $1, $3);
+ }
+ | idterms ':' type ';'
+ {
+ yyerror("illegal declaration");
+ $$ = nil;
+ }
+ | idterms ':' type '=' exp ';'
+ {
+ yyerror("illegal declaration");
+ $$ = nil;
+ }
+ ;
+
+idterms : idterm
+ | idterms ',' idterm
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+decl : Linclude Lsconst ';'
+ {
+ includef($2);
+ $$ = nil;
+ }
+ | ids ':' Ltype type ';'
+ {
+ $$ = typedecl($1, $4);
+ }
+ | ids ':' Limport exp ';'
+ {
+ $$ = importdecl($4, $1);
+ $$->src.start = $1->src.start;
+ $$->src.stop = $5.stop;
+ }
+ | ids ':' type ';'
+ {
+ $$ = vardecl($1, $3);
+ }
+ | ids ':' type '=' exp ';'
+ {
+ $$ = mkbin(Ovardecli, vardecl($1, $3), varinit($1, $5));
+ }
+ | ids ':' Lcon exp ';'
+ {
+ $$ = condecl($1, $4);
+ }
+ | edecl
+ ;
+
+edecl : ids ':' Lexcept ';'
+ {
+ $$ = exdecl($1, nil);
+ }
+ | ids ':' Lexcept '(' tuplist ')' ';'
+ {
+ $$ = exdecl($1, revids($5));
+ }
+ ;
+
+mdecl : ids ':' Lmodule '{' mfields '}'
+ {
+ $1->src.stop = $6.stop;
+ $$ = moddecl($1, rotater($5));
+ }
+ ;
+
+mfields :
+ {
+ $$ = nil;
+ }
+ | mfields mfield
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkn(Oseq, $1, $2);
+ }
+ | error
+ {
+ $$ = nil;
+ }
+ ;
+
+mfield : ids ':' type ';'
+ {
+ $$ = fielddecl(Dglobal, typeids($1, $3));
+ }
+ | adtdecl ';'
+ | ids ':' Ltype type ';'
+ {
+ $$ = typedecl($1, $4);
+ }
+ | ids ':' Lcon exp ';'
+ {
+ $$ = condecl($1, $4);
+ }
+ | edecl
+ ;
+
+adtdecl : ids ':' Ladt polydec '{' fields '}' forpoly
+ {
+ $1->src.stop = $7.stop;
+ $$ = adtdecl($1, rotater($6));
+ $$->ty->polys = $4;
+ $$->ty->val = rotater($8);
+ }
+ | ids ':' Ladt polydec Lfor '{' tpolys '}' '{' fields '}'
+ {
+ $1->src.stop = $11.stop;
+ $$ = adtdecl($1, rotater($10));
+ $$->ty->polys = $4;
+ $$->ty->val = rotater($7);
+ }
+ ;
+
+forpoly :
+ {
+ $$ = nil;
+ }
+ | Lfor '{' tpolys '}'
+ {
+ $$ = $3;
+ }
+ ;
+
+fields :
+ {
+ $$ = nil;
+ }
+ | fields field
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkn(Oseq, $1, $2);
+ }
+ | error
+ {
+ $$ = nil;
+ }
+ ;
+
+field : dfield
+ | pdecl
+ | ids ':' Lcon exp ';'
+ {
+ $$ = condecl($1, $4);
+ }
+ ;
+
+dfields :
+ {
+ $$ = nil;
+ }
+ | dfields dfield
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkn(Oseq, $1, $2);
+ }
+ ;
+
+dfield : ids ':' Lcyclic type ';'
+ {
+ Decl *d;
+
+ for(d = $1; d != nil; d = d->next)
+ d->cyc = 1;
+ $$ = fielddecl(Dfield, typeids($1, $4));
+ }
+ | ids ':' type ';'
+ {
+ $$ = fielddecl(Dfield, typeids($1, $3));
+ }
+ ;
+
+pdecl : Lpick '{' pfields '}'
+ {
+ $$ = $3;
+ }
+ ;
+
+pfields : pfbody dfields
+ {
+ $1->right->right = $2;
+ $$ = $1;
+ }
+ | pfbody error
+ {
+ $$ = nil;
+ }
+ | error
+ {
+ $$ = nil;
+ }
+ ;
+
+pfbody : ptags Llabs
+ {
+ $$ = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, $1), nil));
+ typeids($1, mktype(&$1->src.start, &$1->src.stop, Tadtpick, nil, nil));
+ }
+ | pfbody dfields ptags Llabs
+ {
+ $1->right->right = $2;
+ $$ = mkn(Opickdecl, $1, mkn(Oseq, fielddecl(Dtag, $3), nil));
+ typeids($3, mktype(&$3->src.start, &$3->src.stop, Tadtpick, nil, nil));
+ }
+ | pfbody error ptags Llabs
+ {
+ $$ = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, $3), nil));
+ typeids($3, mktype(&$3->src.start, &$3->src.stop, Tadtpick, nil, nil));
+ }
+ ;
+
+ptags : rptags
+ {
+ $$ = revids($1);
+ }
+ ;
+
+rptags : Lid
+ {
+ $$ = mkids(&$<tok.src>1, $1, nil, nil);
+ }
+ | rptags Lor Lid
+ {
+ $$ = mkids(&$<tok.src>3, $3, nil, $1);
+ }
+ ;
+
+ids : rids
+ {
+ $$ = revids($1);
+ }
+ ;
+
+rids : Lid
+ {
+ $$ = mkids(&$<tok.src>1, $1, nil, nil);
+ }
+ | rids ',' Lid
+ {
+ $$ = mkids(&$<tok.src>3, $3, nil, $1);
+ }
+ ;
+
+fixtype : Lfix '(' exp ',' exp ')'
+ {
+ $$ = mktype(&$1.start, &$6.stop, Tfix, nil, nil);
+ $$->val = mkbin(Oseq, $3, $5);
+ }
+ | Lfix '(' exp ')'
+ {
+ $$ = mktype(&$1.start, &$4.stop, Tfix, nil, nil);
+ $$->val = $3;
+ }
+ ;
+
+types : type
+ {
+ $$ = addtype($1, nil);
+ }
+ | Lcyclic type
+ {
+ $$ = addtype($2, nil);
+ $2->flags |= CYCLIC;
+ }
+ | types ',' type
+ {
+ $$ = addtype($3, $1);
+ }
+ | types ',' Lcyclic type
+ {
+ $$ = addtype($4, $1);
+ $4->flags |= CYCLIC;
+ }
+ ;
+
+type : Ltid
+ {
+ $$ = mkidtype(&$<tok.src>1, $1);
+ }
+ | iditype
+ {
+ $$ = $1;
+ }
+ | dotiditype
+ {
+ $$ = $1;
+ }
+ | type Lmdot Lid
+ {
+ $$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+ }
+ | type Lmdot Lid '[' types ']'
+ {
+ $$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+ $$ = mkinsttype(&$1->src, $$, $5);
+ }
+ | Lref type
+ {
+ $$ = mktype(&$1.start, &$2->src.stop, Tref, $2, nil);
+ }
+ | Lchan Lof type
+ {
+ $$ = mktype(&$1.start, &$3->src.stop, Tchan, $3, nil);
+ }
+ | '(' tuplist ')'
+ {
+ if($2->next == nil)
+ $$ = $2->ty;
+ else
+ $$ = mktype(&$1.start, &$3.stop, Ttuple, nil, revids($2));
+ }
+ | Larray Lof type
+ {
+ $$ = mktype(&$1.start, &$3->src.stop, Tarray, $3, nil);
+ }
+ | Llist Lof type
+ {
+ $$ = mktype(&$1.start, &$3->src.stop, Tlist, $3, nil);
+ }
+ | Lfn polydec fnargretp raises
+ {
+ $3->src.start = $1.start;
+ $3->polys = $2;
+ $3->u.eraises = $4;
+ $$ = $3;
+ }
+ | fixtype
+/*
+ | Lexcept
+ {
+ $$ = mktype(&$1.start, &$1.stop, Texception, nil, nil);
+ $$->cons = 1;
+ }
+ | Lexcept '(' tuplist ')'
+ {
+ $$ = mktype(&$1.start, &$4.stop, Texception, nil, revids($3));
+ $$->cons = 1;
+ }
+*/
+ ;
+
+iditype : Lid
+ {
+ $$ = mkidtype(&$<tok.src>1, $1);
+ }
+ | Lid '[' types ']'
+ {
+ $$ = mkinsttype(&$<tok.src>1, mkidtype(&$<tok.src>1, $1), $3);
+ }
+ ;
+
+dotiditype : type '.' Lid
+ {
+ $$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+ }
+ | type '.' Lid '[' types ']'
+ {
+ $$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+ $$ = mkinsttype(&$1->src, $$, $5);
+ }
+ ;
+
+tuplist : type
+ {
+ $$ = mkids(&$1->src, nil, $1, nil);
+ }
+ | tuplist ',' type
+ {
+ $$ = mkids(&$1->src, nil, $3, $1);
+ }
+ ;
+
+polydec :
+ {
+ $$ = nil;
+ }
+ | '[' ids ']'
+ {
+ $$ = polydecl($2);
+ }
+ ;
+
+fnarg : '(' forms ')'
+ {
+ $$ = mktype(&$1.start, &$3.stop, Tfn, tnone, $2);
+ }
+ | '(' '*' ')'
+ {
+ $$ = mktype(&$1.start, &$3.stop, Tfn, tnone, nil);
+ $$->varargs = 1;
+ }
+ | '(' ftypes ',' '*' ')'
+ {
+ $$ = mktype(&$1.start, &$5.stop, Tfn, tnone, $2);
+ $$->varargs = 1;
+ }
+ ;
+
+fnargret: fnarg %prec ':'
+ {
+ $$ = $1;
+ }
+ | fnarg ':' type
+ {
+ $1->tof = $3;
+ $1->src.stop = $3->src.stop;
+ $$ = $1;
+ }
+ ;
+
+fnargretp: fnargret %prec '='
+ {
+ $$ = $1;
+ }
+ | fnargret Lfor '{' tpolys '}'
+ {
+ $$ = $1;
+ $$->val = rotater($4);
+ }
+ ;
+
+forms :
+ {
+ $$ = nil;
+ }
+ | ftypes
+ ;
+
+ftypes : ftype
+ | ftypes ',' ftype
+ {
+ $$ = appdecls($1, $3);
+ }
+ ;
+
+ftype : nids ':' type
+ {
+ $$ = typeids($1, $3);
+ }
+ | nids ':' adtk
+ {
+ Decl *d;
+
+ $$ = typeids($1, $3);
+ for(d = $$; d != nil; d = d->next)
+ d->implicit = 1;
+ }
+ | idterms ':' type
+ {
+ $$ = mkids(&$1->src, enter("junk", 0), $3, nil);
+ $$->store = Darg;
+ yyerror("illegal argument declaraion");
+ }
+ | idterms ':' adtk
+ {
+ $$ = mkids(&$1->src, enter("junk", 0), $3, nil);
+ $$->store = Darg;
+ yyerror("illegal argument declaraion");
+ }
+ ;
+
+nids : nrids
+ {
+ $$ = revids($1);
+ }
+ ;
+
+nrids : Lid
+ {
+ $$ = mkids(&$<tok.src>1, $1, nil, nil);
+ $$->store = Darg;
+ }
+ | Lnil
+ {
+ $$ = mkids(&$1, nil, nil, nil);
+ $$->store = Darg;
+ }
+ | nrids ',' Lid
+ {
+ $$ = mkids(&$<tok.src>3, $3, nil, $1);
+ $$->store = Darg;
+ }
+ | nrids ',' Lnil
+ {
+ $$ = mkids(&$3, nil, nil, $1);
+ $$->store = Darg;
+ }
+ ;
+
+/*
+adttype : Lid
+ {
+ $$ = mkidtype(&$<tok.src>1, $1);
+ }
+ | adttype '.' Lid
+ {
+ $$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+ }
+ | adttype Lmdot Lid
+ {
+ $$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+ }
+ | Lref adttype
+ {
+ $$ = mktype(&$1.start, &$2->src.stop, Tref, $2, nil);
+ }
+ ;
+
+adtk : Lself adttype
+ {
+ $$ = $2;
+ }
+ ;
+*/
+
+adtk : Lself iditype
+ {
+ $$ = $2;
+ }
+ | Lself Lref iditype
+ {
+ $$ = mktype(&$<tok.src>2.start, &$<tok.src>3.stop, Tref, $3, nil);
+ }
+ | Lself dotiditype
+ {
+ $$ = $2;
+ }
+ | Lself Lref dotiditype
+ {
+ $$ = mktype(&$<tok.src>2.start, &$<tok.src>3.stop, Tref, $3, nil);
+ }
+ ;
+
+fndef : fnname fnargretp raises fbody
+ {
+ $$ = fndecl($1, $2, $4);
+ nfns++;
+ /* patch up polydecs */
+ if($1->op == Odot){
+ if($1->right->left != nil){
+ $2->polys = $1->right->left->decl;
+ $1->right->left = nil;
+ }
+ if($1->left->op == Oname && $1->left->left != nil){
+ $$->decl = $1->left->left->decl;
+ $1->left->left = nil;
+ }
+ }
+ else{
+ if($1->left != nil){
+ $2->polys = $1->left->decl;
+ $1->left = nil;
+ }
+ }
+ $2->u.eraises = $3;
+ $$->src = $1->src;
+ }
+ ;
+
+raises : Lraises '(' idlist ')'
+ {
+ $$ = mkn(Otuple, rotater($3), nil);
+ $$->src.start = $1.start;
+ $$->src.stop = $4.stop;
+ }
+ | Lraises idatom
+ {
+ $$ = mkn(Otuple, mkunary(Oseq, $2), nil);
+ $$->src.start = $1.start;
+ $$->src.stop = $2->src.stop;
+ }
+ | /* empty */ %prec Lraises
+ {
+ $$ = nil;
+ }
+ ;
+
+fbody : '{' stmts '}'
+ {
+ if($2 == nil){
+ $2 = mkn(Onothing, nil, nil);
+ $2->src.start = curline();
+ $2->src.stop = $2->src.start;
+ }
+ $$ = rotater($2);
+ $$->src.start = $1.start;
+ $$->src.stop = $3.stop;
+ }
+ | error '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ }
+ | error '{' stmts '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ }
+ ;
+
+fnname : Lid polydec
+ {
+ $$ = mkname(&$<tok.src>1, $1);
+ if($2 != nil){
+ $$->left = mkn(Onothing, nil ,nil);
+ $$->left->decl = $2;
+ }
+ }
+ | fnname '.' Lid polydec
+ {
+ $$ = mkbin(Odot, $1, mkname(&$<tok.src>3, $3));
+ if($4 != nil){
+ $$->right->left = mkn(Onothing, nil ,nil);
+ $$->right->left->decl = $4;
+ }
+ }
+ ;
+
+stmts :
+ {
+ $$ = nil;
+ }
+ | stmts decl
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkbin(Oseq, $1, $2);
+ }
+ | stmts stmt
+ {
+ if($1 == nil)
+ $$ = $2;
+ else
+ $$ = mkbin(Oseq, $1, $2);
+ }
+ ;
+
+elists : '(' elist ')'
+ | elists ',' '(' elist ')'
+ ;
+
+stmt : error ';'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = curline();
+ $$->src.stop = $$->src.start;
+ }
+ | error '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = curline();
+ $$->src.stop = $$->src.start;
+ }
+ | error '{' stmts '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = curline();
+ $$->src.stop = $$->src.start;
+ }
+ | '{' stmts '}'
+ {
+ if($2 == nil){
+ $2 = mkn(Onothing, nil, nil);
+ $2->src.start = curline();
+ $2->src.stop = $2->src.start;
+ }
+ $$ = mkscope(rotater($2));
+ }
+ | elists ':' type ';'
+ {
+ yyerror("illegal declaration");
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = curline();
+ $$->src.stop = $$->src.start;
+ }
+ | elists ':' type '=' exp';'
+ {
+ yyerror("illegal declaration");
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = curline();
+ $$->src.stop = $$->src.start;
+ }
+ | zexp ';'
+ {
+ $$ = $1;
+ }
+ | Lif '(' exp ')' stmt
+ {
+ $$ = mkn(Oif, $3, mkunary(Oseq, $5));
+ $$->src.start = $1.start;
+ $$->src.stop = $5->src.stop;
+ }
+ | Lif '(' exp ')' stmt Lelse stmt
+ {
+ $$ = mkn(Oif, $3, mkbin(Oseq, $5, $7));
+ $$->src.start = $1.start;
+ $$->src.stop = $7->src.stop;
+ }
+ | bclab Lfor '(' zexp ';' zexp ';' zexp ')' stmt
+ {
+ $$ = mkunary(Oseq, $10);
+ if($8->op != Onothing)
+ $$->right = $8;
+ $$ = mkbin(Ofor, $6, $$);
+ $$->decl = $1;
+ if($4->op != Onothing)
+ $$ = mkbin(Oseq, $4, $$);
+ }
+ | bclab Lwhile '(' zexp ')' stmt
+ {
+ $$ = mkn(Ofor, $4, mkunary(Oseq, $6));
+ $$->src.start = $2.start;
+ $$->src.stop = $6->src.stop;
+ $$->decl = $1;
+ }
+ | bclab Ldo stmt Lwhile '(' zexp ')' ';'
+ {
+ $$ = mkn(Odo, $6, $3);
+ $$->src.start = $2.start;
+ $$->src.stop = $7.stop;
+ $$->decl = $1;
+ }
+ | Lbreak bctarg ';'
+ {
+ $$ = mkn(Obreak, nil, nil);
+ $$->decl = $2;
+ $$->src = $1;
+ }
+ | Lcont bctarg ';'
+ {
+ $$ = mkn(Ocont, nil, nil);
+ $$->decl = $2;
+ $$->src = $1;
+ }
+ | Lreturn zexp ';'
+ {
+ $$ = mkn(Oret, $2, nil);
+ $$->src = $1;
+ if($2->op == Onothing)
+ $$->left = nil;
+ else
+ $$->src.stop = $2->src.stop;
+ }
+ | Lspawn exp ';'
+ {
+ $$ = mkn(Ospawn, $2, nil);
+ $$->src.start = $1.start;
+ $$->src.stop = $2->src.stop;
+ }
+ | Lraise zexp ';'
+ {
+ $$ = mkn(Oraise, $2, nil);
+ $$->src.start = $1.start;
+ $$->src.stop = $2->src.stop;
+ }
+ | bclab Lcase exp '{' cqstmts '}'
+ {
+ $$ = mkn(Ocase, $3, caselist($5, nil));
+ $$->src = $3->src;
+ $$->decl = $1;
+ }
+ | bclab Lalt '{' qstmts '}'
+ {
+ $$ = mkn(Oalt, caselist($4, nil), nil);
+ $$->src = $2;
+ $$->decl = $1;
+ }
+ | bclab Lpick Lid Ldeclas exp '{' pstmts '}'
+ {
+ $$ = mkn(Opick, mkbin(Odas, mkname(&$<tok.src>3, $3), $5), caselist($7, nil));
+ $$->src.start = $<tok.src>3.start;
+ $$->src.stop = $5->src.stop;
+ $$->decl = $1;
+ }
+ | Lexit ';'
+ {
+ $$ = mkn(Oexit, nil, nil);
+ $$->src = $1;
+ }
+ | '{' stmts '}' Lexcept idexc '{' eqstmts '}'
+ {
+ if($2 == nil){
+ $2 = mkn(Onothing, nil, nil);
+ $2->src.start = curline();
+ $2->src.stop = curline();
+ }
+ $2 = mkscope(rotater($2));
+ $$ = mkbin(Oexstmt, $2, mkn(Oexcept, $5, caselist($7, nil)));
+ }
+/*
+ | stmt Lexcept idexc '{' eqstmts '}'
+ {
+ $$ = mkbin(Oexstmt, $1, mkn(Oexcept, $3, caselist($5, nil)));
+ }
+*/
+ ;
+
+bclab :
+ {
+ $$ = nil;
+ }
+ | ids ':'
+ {
+ if($1->next != nil)
+ yyerror("only one identifier allowed in a label");
+ $$ = $1;
+ }
+ ;
+
+bctarg :
+ {
+ $$ = nil;
+ }
+ | Lid
+ {
+ $$ = mkids(&$<tok.src>1, $1, nil, nil);
+ }
+ ;
+
+qstmts : qbodies stmts
+ {
+ $1->left->right->right = $2;
+ $$ = $1;
+ }
+ ;
+
+qbodies : qual Llabs
+ {
+ $$ = mkunary(Oseq, mkscope(mkunary(Olabel, rotater($1))));
+ }
+ | qbodies stmts qual Llabs
+ {
+ $1->left->right->right = $2;
+ $$ = mkbin(Oseq, mkscope(mkunary(Olabel, rotater($3))), $1);
+ }
+ ;
+
+cqstmts : cqbodies stmts
+ {
+ $1->left->right = mkscope($2);
+ $$ = $1;
+ }
+ ;
+
+cqbodies : qual Llabs
+ {
+ $$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+ }
+ | cqbodies stmts qual Llabs
+ {
+ $1->left->right = mkscope($2);
+ $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+ }
+ ;
+
+eqstmts : eqbodies stmts
+ {
+ $1->left->right = mkscope($2);
+ $$ = $1;
+ }
+ ;
+
+eqbodies : qual Llabs
+ {
+ $$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+ }
+ | eqbodies stmts qual Llabs
+ {
+ $1->left->right = mkscope($2);
+ $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+ }
+ ;
+
+qual : exp
+ | exp Lto exp
+ {
+ $$ = mkbin(Orange, $1, $3);
+ }
+ | '*'
+ {
+ $$ = mkn(Owild, nil, nil);
+ $$->src = $1;
+ }
+ | qual Lor qual
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ | error
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = curline();
+ $$->src.stop = $$->src.start;
+ }
+ ;
+
+pstmts : pbodies stmts
+ {
+ $1->left->right = mkscope($2);
+ $$ = $1;
+ }
+ ;
+
+pbodies : pqual Llabs
+ {
+ $$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+ }
+ | pbodies stmts pqual Llabs
+ {
+ $1->left->right = mkscope($2);
+ $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+ }
+ ;
+
+pqual : Lid
+ {
+ $$ = mkname(&$<tok>1.src, $1);
+ }
+ | '*'
+ {
+ $$ = mkn(Owild, nil, nil);
+ $$->src = $1;
+ }
+ | pqual Lor pqual
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ | error
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = curline();
+ $$->src.stop = $$->src.start;
+ }
+ ;
+
+zexp :
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = curline();
+ $$->src.stop = $$->src.start;
+ }
+ | exp
+ ;
+
+exp : monexp
+ | exp '=' exp
+ {
+ $$ = mkbin(Oas, $1, $3);
+ }
+ | exp Landeq exp
+ {
+ $$ = mkbin(Oandas, $1, $3);
+ }
+ | exp Loreq exp
+ {
+ $$ = mkbin(Ooras, $1, $3);
+ }
+ | exp Lxoreq exp
+ {
+ $$ = mkbin(Oxoras, $1, $3);
+ }
+ | exp Llsheq exp
+ {
+ $$ = mkbin(Olshas, $1, $3);
+ }
+ | exp Lrsheq exp
+ {
+ $$ = mkbin(Orshas, $1, $3);
+ }
+ | exp Laddeq exp
+ {
+ $$ = mkbin(Oaddas, $1, $3);
+ }
+ | exp Lsubeq exp
+ {
+ $$ = mkbin(Osubas, $1, $3);
+ }
+ | exp Lmuleq exp
+ {
+ $$ = mkbin(Omulas, $1, $3);
+ }
+ | exp Ldiveq exp
+ {
+ $$ = mkbin(Odivas, $1, $3);
+ }
+ | exp Lmodeq exp
+ {
+ $$ = mkbin(Omodas, $1, $3);
+ }
+ | exp Lexpeq exp
+ {
+ $$ = mkbin(Oexpas, $1, $3);
+ }
+ | exp Lcomm '=' exp
+ {
+ $$ = mkbin(Osnd, $1, $4);
+ }
+ | exp Ldeclas exp
+ {
+ $$ = mkbin(Odas, $1, $3);
+ }
+ | Lload Lid exp %prec Lload
+ {
+ $$ = mkn(Oload, $3, nil);
+ $$->src.start = $<tok.src.start>1;
+ $$->src.stop = $3->src.stop;
+ $$->ty = mkidtype(&$<tok.src>2, $2);
+ }
+ | exp Lexp exp
+ {
+ $$ = mkbin(Oexp, $1, $3);
+ }
+ | exp '*' exp
+ {
+ $$ = mkbin(Omul, $1, $3);
+ }
+ | exp '/' exp
+ {
+ $$ = mkbin(Odiv, $1, $3);
+ }
+ | exp '%' exp
+ {
+ $$ = mkbin(Omod, $1, $3);
+ }
+ | exp '+' exp
+ {
+ $$ = mkbin(Oadd, $1, $3);
+ }
+ | exp '-' exp
+ {
+ $$ = mkbin(Osub, $1, $3);
+ }
+ | exp Lrsh exp
+ {
+ $$ = mkbin(Orsh, $1, $3);
+ }
+ | exp Llsh exp
+ {
+ $$ = mkbin(Olsh, $1, $3);
+ }
+ | exp '<' exp
+ {
+ $$ = mkbin(Olt, $1, $3);
+ }
+ | exp '>' exp
+ {
+ $$ = mkbin(Ogt, $1, $3);
+ }
+ | exp Lleq exp
+ {
+ $$ = mkbin(Oleq, $1, $3);
+ }
+ | exp Lgeq exp
+ {
+ $$ = mkbin(Ogeq, $1, $3);
+ }
+ | exp Leq exp
+ {
+ $$ = mkbin(Oeq, $1, $3);
+ }
+ | exp Lneq exp
+ {
+ $$ = mkbin(Oneq, $1, $3);
+ }
+ | exp '&' exp
+ {
+ $$ = mkbin(Oand, $1, $3);
+ }
+ | exp '^' exp
+ {
+ $$ = mkbin(Oxor, $1, $3);
+ }
+ | exp '|' exp
+ {
+ $$ = mkbin(Oor, $1, $3);
+ }
+ | exp Lcons exp
+ {
+ $$ = mkbin(Ocons, $1, $3);
+ }
+ | exp Landand exp
+ {
+ $$ = mkbin(Oandand, $1, $3);
+ }
+ | exp Loror exp
+ {
+ $$ = mkbin(Ooror, $1, $3);
+ }
+ ;
+
+monexp : term
+ | '+' monexp
+ {
+ $2->src.start = $1.start;
+ $$ = $2;
+ }
+ | '-' monexp
+ {
+ $$ = mkunary(Oneg, $2);
+ $$->src.start = $1.start;
+ }
+ | '!' monexp
+ {
+ $$ = mkunary(Onot, $2);
+ $$->src.start = $1.start;
+ }
+ | '~' monexp
+ {
+ $$ = mkunary(Ocomp, $2);
+ $$->src.start = $1.start;
+ }
+ | '*' monexp
+ {
+ $$ = mkunary(Oind, $2);
+ $$->src.start = $1.start;
+ }
+ | Linc monexp
+ {
+ $$ = mkunary(Opreinc, $2);
+ $$->src.start = $1.start;
+ }
+ | Ldec monexp
+ {
+ $$ = mkunary(Opredec, $2);
+ $$->src.start = $1.start;
+ }
+ | Lcomm monexp
+ {
+ $$ = mkunary(Orcv, $2);
+ $$->src.start = $1.start;
+ }
+ | Lhd monexp
+ {
+ $$ = mkunary(Ohd, $2);
+ $$->src.start = $1.start;
+ }
+ | Ltl monexp
+ {
+ $$ = mkunary(Otl, $2);
+ $$->src.start = $1.start;
+ }
+ | Llen monexp
+ {
+ $$ = mkunary(Olen, $2);
+ $$->src.start = $1.start;
+ }
+ | Lref monexp
+ {
+ $$ = mkunary(Oref, $2);
+ $$->src.start = $1.start;
+ }
+ | Ltagof monexp
+ {
+ $$ = mkunary(Otagof, $2);
+ $$->src.start = $1.start;
+ }
+ | Larray '[' exp ']' Lof type
+ {
+ $$ = mkn(Oarray, $3, nil);
+ $$->ty = mktype(&$1.start, &$6->src.stop, Tarray, $6, nil);
+ $$->src = $$->ty->src;
+ }
+ | Larray '[' exp ']' Lof '{' initlist '}'
+ {
+ $$ = mkn(Oarray, $3, $7);
+ $$->src.start = $1.start;
+ $$->src.stop = $8.stop;
+ }
+ | Larray '[' ']' Lof '{' initlist '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$->src.start = $2.start;
+ $$->src.stop = $3.stop;
+ $$ = mkn(Oarray, $$, $6);
+ $$->src.start = $1.start;
+ $$->src.stop = $7.stop;
+ }
+ | Llist Lof '{' celist '}'
+ {
+ $$ = etolist($4);
+ $$->src.start = $1.start;
+ $$->src.stop = $5.stop;
+ }
+ | Lchan Lof type
+ {
+ $$ = mkn(Ochan, nil, nil);
+ $$->ty = mktype(&$1.start, &$3->src.stop, Tchan, $3, nil);
+ $$->src = $$->ty->src;
+ }
+ | Lchan '[' exp ']' Lof type
+ {
+ $$ = mkn(Ochan, $3, nil);
+ $$->ty = mktype(&$1.start, &$6->src.stop, Tchan, $6, nil);
+ $$->src = $$->ty->src;
+ }
+ | Larray Lof Ltid monexp
+ {
+ $$ = mkunary(Ocast, $4);
+ $$->ty = mktype(&$1.start, &$4->src.stop, Tarray, mkidtype(&$<tok.src>3, $3), nil);
+ $$->src = $$->ty->src;
+ }
+ | Ltid monexp
+ {
+ $$ = mkunary(Ocast, $2);
+ $$->src.start = $<tok.src>1.start;
+ $$->ty = mkidtype(&$$->src, $1);
+ }
+ | Lid monexp
+ {
+ $$ = mkunary(Ocast, $2);
+ $$->src.start = $<tok.src>1.start;
+ $$->ty = mkidtype(&$$->src, $1);
+ }
+ | fixtype monexp
+ {
+ $$ = mkunary(Ocast, $2);
+ $$->src.start = $<tok.src>1.start;
+ $$->ty = $1;
+ }
+ ;
+
+term : idatom
+ | term '(' zelist ')'
+ {
+ $$ = mkn(Ocall, $1, $3);
+ $$->src.start = $1->src.start;
+ $$->src.stop = $4.stop;
+ }
+ | '(' elist ')'
+ {
+ $$ = $2;
+ if($2->op == Oseq)
+ $$ = mkn(Otuple, rotater($2), nil);
+ else
+ $$->flags |= PARENS;
+ $$->src.start = $1.start;
+ $$->src.stop = $3.stop;
+ }
+ | term '.' Lid
+ {
+ $$ = mkbin(Odot, $1, mkname(&$<tok.src>3, $3));
+ }
+ | term Lmdot term
+ {
+ $$ = mkbin(Omdot, $1, $3);
+ }
+ | term '[' export ']'
+ {
+ $$ = mkbin(Oindex, $1, $3);
+ $$->src.stop = $4.stop;
+ }
+ | term '[' zexp ':' zexp ']'
+ {
+ if($3->op == Onothing)
+ $3->src = $4;
+ if($5->op == Onothing)
+ $5->src = $4;
+ $$ = mkbin(Oslice, $1, mkbin(Oseq, $3, $5));
+ $$->src.stop = $6.stop;
+ }
+ | term Linc
+ {
+ $$ = mkunary(Oinc, $1);
+ $$->src.stop = $2.stop;
+ }
+ | term Ldec
+ {
+ $$ = mkunary(Odec, $1);
+ $$->src.stop = $2.stop;
+ }
+ | Lsconst
+ {
+ $$ = mksconst(&$<tok.src>1, $1);
+ }
+ | Lconst
+ {
+ $$ = mkconst(&$<tok.src>1, $1);
+ if($1 > 0x7fffffff || $1 < -0x7fffffff)
+ $$->ty = tbig;
+ $$ = $$;
+ }
+ | Lrconst
+ {
+ $$ = mkrconst(&$<tok.src>1, $1);
+ }
+ | term '[' exportlist ',' export ']'
+ {
+ $$ = mkbin(Oindex, $1, rotater(mkbin(Oseq, $3, $5)));
+ $$->src.stop = $6.stop;
+ }
+ ;
+
+idatom : Lid
+ {
+ $$ = mkname(&$<tok.src>1, $1);
+ }
+ | Lnil
+ {
+ $$ = mknil(&$<tok.src>1);
+ }
+ ;
+
+idterm : '(' idlist ')'
+ {
+ $$ = mkn(Otuple, rotater($2), nil);
+ $$->src.start = $1.start;
+ $$->src.stop = $3.stop;
+ }
+ ;
+
+exportlist : export
+ | exportlist ',' export
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+export : exp
+ | texp
+ ;
+
+texp : Ltid
+ {
+ $$ = mkn(Otype, nil, nil);
+ $$->ty = mkidtype(&$<tok.src>1, $1);
+ $$->src = $$->ty->src;
+ }
+ | Larray Lof type
+ {
+ $$ = mkn(Otype, nil, nil);
+ $$->ty = mktype(&$1.start, &$3->src.stop, Tarray, $3, nil);
+ $$->src = $$->ty->src;
+ }
+ | Llist Lof type
+ {
+ $$ = mkn(Otype, nil, nil);
+ $$->ty = mktype(&$1.start, &$3->src.stop, Tlist, $3, nil);
+ $$->src = $$->ty->src;
+ }
+ | Lcyclic type
+ {
+ $$ = mkn(Otype, nil ,nil);
+ $$->ty = $2;
+ $$->ty->flags |= CYCLIC;
+ $$->src = $$->ty->src;
+ }
+ ;
+
+idexc : Lid
+ {
+ $$ = mkname(&$<tok.src>1, $1);
+ }
+ | /* empty */
+ {
+ $$ = nil;
+ }
+ ;
+
+idlist : idterm
+ | idatom
+ | idlist ',' idterm
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ | idlist ',' idatom
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+zelist :
+ {
+ $$ = nil;
+ }
+ | elist
+ {
+ $$ = rotater($1);
+ }
+ ;
+
+celist : elist
+ | elist ','
+ ;
+
+elist : exp
+ | elist ',' exp
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+initlist : elemlist
+ {
+ $$ = rotater($1);
+ }
+ | elemlist ','
+ {
+ $$ = rotater($1);
+ }
+ ;
+
+elemlist : elem
+ | elemlist ',' elem
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+elem : exp
+ {
+ $$ = mkn(Oelem, nil, $1);
+ $$->src = $1->src;
+ }
+ | qual Llabs exp
+ {
+ $$ = mkbin(Oelem, rotater($1), $3);
+ }
+ ;
+
+/*
+tpoly : ids Llabs '{' dfields '}'
+ {
+ $$ = typedecl($1, mktype(&$1->src.start, &$5.stop, Tpoly, nil, nil));
+ $$->left = rotater($4);
+ }
+ ;
+
+tpolys : tpoly
+ {
+ $$ = $1;
+ }
+ | tpolys tpoly
+ {
+ $$ = mkbin(Oseq, $1, $2);
+ }
+ ;
+*/
+
+tpolys : tpoly dfields
+ {
+ if($1->op == Oseq)
+ $1->right->left = rotater($2);
+ else
+ $1->left = rotater($2);
+ $$ = $1;
+ }
+ ;
+
+tpoly : ids Llabs
+ {
+ $$ = typedecl($1, mktype(&$1->src.start, &$2.stop, Tpoly, nil, nil));
+ }
+ | tpoly dfields ids Llabs
+ {
+ if($1->op == Oseq)
+ $1->right->left = rotater($2);
+ else
+ $1->left = rotater($2);
+ $$ = mkbin(Oseq, $1, typedecl($3, mktype(&$3->src.start, &$4.stop, Tpoly, nil, nil)));
+ }
+ ;
+
+%%
+
+static char *mkfileext(char*, char*, char*);
+static void usage(void);
+
+static int dosym;
+static int toterrors;
+static ulong canonnanbits[] = { 0x7fffffff, 0xffffffff};
+static char* infile;
+
+#define SLASHMOD "/module"
+
+static char*
+getroot(void)
+{
+ int n;
+ char *e, *l, *s;
+
+ if((e = getenv("EMU")) != nil){
+ for(s = e; *e != '\0'; e++){
+ if(*e == '-' && *(e+1) == 'r' && (e == s || *(e-1) == ' ' || *(e-1) == '\t')){
+ e += 2;
+ l = strchr(e, ' ');
+ if(l != nil)
+ *l = '\0';
+ if((n = strlen(e)) > 0){
+ s = malloc(n+1);
+ strcpy(s, e);
+ return s;
+ }
+ }
+ }
+ }
+ if((e = getenv("ROOT")) != nil)
+ return strdup(e);
+ return nil;
+}
+
+void
+main(int argc, char *argv[])
+{
+ char *s, *ofile, *ext, *root;
+ int i;
+
+ FPinit();
+ FPcontrol(0, INVAL|ZDIV|OVFL|UNFL|INEX);
+ canonnan = canontod(canonnanbits);
+
+ fmtinstall('D', dotconv);
+ fmtinstall('I', instconv);
+ fmtinstall('K', declconv);
+ fmtinstall('k', storeconv);
+ fmtinstall('L', lineconv);
+ fmtinstall('M', mapconv);
+ fmtinstall('n', nodeconv); /* exp structure */
+ fmtinstall('O', opconv);
+ fmtinstall('g', gfltconv);
+ fmtinstall('Q', etconv); /* src expression with type */
+ fmtinstall('R', ctypeconv); /* c equivalent type */
+ fmtinstall('P', ctypeconv); /* c equivalent type - pointer type */
+ fmtinstall('T', typeconv); /* source style types */
+ fmtinstall('t', stypeconv); /* structurally descriptive type */
+ fmtinstall('U', srcconv);
+ fmtinstall('v', expconv); /* src expression */
+ fmtinstall('V', expconv); /* src expression in '' */
+ lexinit();
+ typeinit();
+ optabinit();
+
+ gendis = 1;
+ asmsym = 0;
+ maxerr = 20;
+ ofile = nil;
+ ext = nil;
+ ARGBEGIN{
+ case 'D':
+ /*
+ * debug flags:
+ *
+ * a alt compilation
+ * A array constructor compilation
+ * b boolean and branch compilation
+ * c case compilation
+ * d function declaration
+ * D descriptor generation
+ * e expression compilation
+ * E addressable expression compilation
+ * f print arguments for compiled functions
+ * F constant folding
+ * g print out globals
+ * m module declaration and type checking
+ * n nil references
+ * s print sizes of output file sections
+ * S type signing
+ * t type checking function bodies
+ * T timing
+ * v global var and constant compilation
+ * x adt verification
+ * Y tuple compilation
+ * z Z bug fixes
+ */
+ s = ARGF();
+ while(s && *s)
+ debug[*s++] = 1;
+ break;
+ case 'I':
+ s = ARGF();
+ if(s == nil)
+ usage();
+ addinclude(s);
+ break;
+ case 'G':
+ asmsym = 1;
+ break;
+ case 'S':
+ gendis = 0;
+ break;
+ case 'a':
+ emitstub = 1;
+ break;
+ case 'A':
+ emitstub = emitdyn = 1;
+ break;
+ case 'c':
+ mustcompile = 1;
+ break;
+ case 'C':
+ dontcompile = 1;
+ break;
+ case 'e':
+ maxerr = 1000;
+ break;
+ case 'f':
+ isfatal = 1;
+ break;
+ case 'F':
+ newfnptr = 1;
+ break;
+ case 'g':
+ dosym = 1;
+ break;
+ case 'i':
+ dontinline = 1;
+ break;
+ case 'o':
+ ofile = ARGF();
+ break;
+ case 'O':
+ optims = 1;
+ break;
+ case 's':
+ s = ARGF();
+ if(s != nil)
+ fixss = atoi(s);
+ break;
+ case 't':
+ emittab = ARGF();
+ if(emittab == nil)
+ usage();
+ break;
+ case 'T':
+ emitcode = ARGF();
+ if(emitcode == nil)
+ usage();
+ break;
+ case 'd':
+ emitcode = ARGF();
+ if(emitcode == nil)
+ usage();
+ emitdyn = 1;
+ break;
+ case 'w':
+ superwarn = dowarn;
+ dowarn = 1;
+ break;
+ case 'x':
+ ext = ARGF();
+ break;
+ case 'X':
+ signdump = ARGF();
+ break;
+ case 'y':
+ oldcycles = 1;
+ break;
+ case 'z':
+ arrayz = 1;
+ break;
+ default:
+ usage();
+ break;
+ }ARGEND
+
+ if((root = getroot()) != nil){
+ char *r;
+
+ r = malloc(strlen(root)+strlen(SLASHMOD)+1);
+ strcpy(r, root);
+ strcat(r, SLASHMOD);
+ addinclude(r);
+ free(root);
+ }
+ else
+ addinclude(INCPATH);
+
+ if(argc == 0){
+ usage();
+ }else if(ofile != nil){
+ if(argc != 1)
+ usage();
+ translate(argv[0], ofile, mkfileext(ofile, ".dis", ".sbl"));
+ }else{
+ if(ext == nil){
+ ext = ".s";
+ if(gendis)
+ ext = ".dis";
+ }
+ for(i = 0; i < argc; i++){
+ s = strrchr(argv[i], '/');
+ if(s == nil)
+ s = argv[i];
+ else
+ s++;
+ if(argc > 1)
+ print("%s:\n", argv[i]);
+ ofile = mkfileext(s, ".b", ext);
+ translate(argv[i], ofile, mkfileext(ofile, ext, ".sbl"));
+ }
+ }
+ if(toterrors)
+ exits("errors");
+ exits(0);
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: limbo [-CGSacgwe] [-I incdir] [-o outfile] [-{T|t|d} module] [-D debug] file ...\n");
+ exits("usage");
+}
+
+static char*
+mkfileext(char *file, char *oldext, char *ext)
+{
+ char *ofile;
+ int n, n2;
+
+ n = strlen(file);
+ n2 = strlen(oldext);
+ if(n >= n2 && strcmp(&file[n-n2], oldext) == 0)
+ n -= n2;
+ ofile = malloc(n + strlen(ext) + 1);
+ memmove(ofile, file, n);
+ strcpy(ofile+n, ext);
+ return ofile;
+}
+
+void
+translate(char *in, char *out, char *dbg)
+{
+ Decl *entry;
+ int doemit;
+
+ infile = in;
+ outfile = out;
+ symfile = dbg;
+ errors = 0;
+ bins[0] = Bopen(in, OREAD);
+ if(bins[0] == nil){
+ fprint(2, "can't open %s: %r\n", in);
+ toterrors++;
+ return;
+ }
+ doemit = emitstub || emittab || emitcode;
+ if(!doemit){
+ bout = Bopen(out, OWRITE);
+ if(bout == nil){
+ fprint(2, "can't open %s: %r\n", out);
+ toterrors++;
+ Bterm(bins[0]);
+ return;
+ }
+ if(dosym){
+ bsym = Bopen(dbg, OWRITE);
+ if(bsym == nil)
+ fprint(2, "can't open %s: %r\n", dbg);
+ }
+ }
+
+ lexstart(in);
+
+ popscopes();
+ typestart();
+ declstart();
+
+ yyparse();
+
+ entry = typecheck(!doemit);
+
+ modcom(entry);
+
+ fns = nil;
+ nfns = 0;
+ descriptors = nil;
+
+ if(bout != nil)
+ Bterm(bout);
+ if(bsym != nil)
+ Bterm(bsym);
+ toterrors += errors;
+ if(errors && bout != nil)
+ remove(out);
+ if(errors && bsym != nil)
+ remove(dbg);
+}
+
+void
+trapFPE(unsigned exception[5], int value[2])
+{
+ /* can't happen; it's just here to keep FPinit happy. */
+ USED(exception);
+ USED(value);
+}
+
+static char *
+win2inf(char *s)
+{
+ int nt = 0;
+ char *t;
+
+ if(strlen(s) > 1 && s[1] == ':'){
+ s[1] = '/';
+ s++;
+ nt = 1;
+ }
+ for(t = s; *t != '\0'; t++){
+ if(*t == '\\')
+ *t = '/';
+ if(nt)
+ *t = tolower(*t);
+ }
+ return s;
+}
+
+static char *
+cleann(char *s)
+{
+ char *p, *r, *t;
+ char buf[256];
+
+ r = t = malloc(strlen(s)+1);
+ strcpy(t, s);
+ t = win2inf(t);
+ if(*t != '/'){
+ p = win2inf(getwd(buf, sizeof(buf)));
+ s = malloc(strlen(p)+strlen(t)+2);
+ strcpy(s, p);
+ strcat(s, "/");
+ strcat(s, t);
+ }
+ else{
+ s = malloc(strlen(t)+1);
+ strcpy(s, t);
+ }
+ free(r);
+ /* print("cleann: %s\n", p); */
+ return cleanname(s);
+}
+
+char *
+srcpath(char *name, int nlen)
+{
+ int l1, l2;
+ char *r, *srcp, *t;
+
+ srcp = cleann(infile);
+ r = getroot();
+ if(r == nil){
+ l1 = strlen(INCPATH);
+ r = malloc(l1+1);
+ strcpy(r, INCPATH);
+ if(l1 >= strlen(SLASHMOD) && strcmp(r+l1-strlen(SLASHMOD), SLASHMOD) == 0)
+ r[l1-strlen(SLASHMOD)] = '\0';
+ }
+ t = cleann(r);
+ free(r);
+ r = t;
+ /* srcp relative to r */
+ l1 = strlen(srcp);
+ l2 = strlen(r);
+ if(l1 >= l2 && strncmp(srcp, r, l2) == 0){
+ /* nothing to do */
+ }else
+ l2 = 0;
+ strncpy(name, srcp+l2, nlen);
+ name[nlen-1] = '\0';
+ free(r);
+ free(srcp);
+ /* print("srcpath: %s\n", name); */
+ return name;
+}
--- /dev/null
+++ b/limbo/mkfile
@@ -1,0 +1,41 @@
+<../mkconfig
+
+TARG=limbo
+
+OFILES= asm.$O\
+ com.$O\
+ decls.$O\
+ dis.$O\
+ dtocanon.$O\
+ ecom.$O\
+ gen.$O\
+ lex.$O\
+ nodes.$O\
+ optab.$O\
+ optim.$O\
+ sbl.$O\
+ stubs.$O\
+ typecheck.$O\
+ types.$O\
+ y.tab.$O\
+
+HFILES= limbo.h\
+ fns.h\
+ y.tab.h\
+ $ROOT/include/interp.h\
+ $ROOT/include/isa.h\
+
+LIBS= bio\
+ math\
+ sec\
+ mp\
+ 9\
+
+YFILES= limbo.y
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS='-DINCPATH="'$ROOT/module'"' $CFLAGS
+YFLAGS=-d
--- /dev/null
+++ b/limbo/nodes.c
@@ -1,0 +1,1538 @@
+#include "limbo.h"
+
+static vlong
+ipow(vlong x, int n)
+{
+ int inv;
+ vlong r;
+
+ inv = 0;
+ if(n < 0){
+ n = -n;
+ inv = 1;
+ }
+ r = 1;
+ for(;;){
+ if(n&1)
+ r *= x;
+ if((n >>= 1) == 0)
+ break;
+ x *= x;
+ }
+ if(inv)
+ r = 1/r;
+ return r;
+}
+
+double
+rpow(double x, int n)
+{
+ int inv;
+ double r;
+
+ inv = 0;
+ if(n < 0){
+ n = -n;
+ inv = 1;
+ }
+ r = 1;
+ for(;;){
+ if(n&1)
+ r *= x;
+ if((n >>= 1) == 0)
+ break;
+ x *= x;
+ }
+ if(inv)
+ r = 1/r;
+ return r;
+}
+
+Long
+real2fix(double v, Type *t)
+{
+ v /= scale(t);
+ v = v < 0 ? v-0.5: v+0.5;
+ return v;
+}
+
+Long
+fix2fix(Long v, Type *f, Type *t)
+{
+ double r;
+
+ r = (double)v * (scale(f)/scale(t));
+ r = r < 0 ? r-0.5: r+0.5;
+ return r;
+}
+
+double
+fix2real(Long v, Type *f)
+{
+ return (double)v * scale(f);
+}
+
+int
+istuple(Node *n)
+{
+ Decl *d;
+
+ switch(n->op){
+ case Otuple:
+ return 1;
+ case Oname:
+ d = n->decl;
+ if(d->importid != nil)
+ d = d->importid;
+ return d->store == Dconst && (n->ty->kind == Ttuple || n->ty->kind == Tadt);
+ case Odot:
+ return 0; /* istuple(n->left); */
+ }
+ return 0;
+}
+
+static Node*
+tuplemem(Node *n, Decl *d)
+{
+ Type *ty;
+ Decl *ids;
+
+ ty = n->ty;
+ n = n->left;
+ for(ids = ty->ids; ids != nil; ids = ids->next){
+ if(ids->sym == d->sym)
+ break;
+ else
+ n = n->right;
+ }
+ if(n == nil)
+ fatal("tuplemem cannot cope !\n");
+ return n->left;
+}
+
+int
+varcom(Decl *v)
+{
+ Node *n, tn;
+
+ n = v->init;
+ n = fold(n);
+ v->init = n;
+ if(debug['v'])
+ print("variable '%D' val %V\n", v, n);
+ if(n == nil)
+ return 1;
+
+ tn = znode;
+ tn.op = Oname;
+ tn.decl = v;
+ tn.src = v->src;
+ tn.ty = v->ty;
+ return initable(&tn, n, 0);
+}
+
+int
+initable(Node *v, Node *n, int allocdep)
+{
+ Node *e;
+
+ switch(n->ty->kind){
+ case Tiface:
+ case Tgoto:
+ case Tcase:
+ case Tcasel:
+ case Tcasec:
+ case Talt:
+ case Texcept:
+ return 1;
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ case Tstring:
+ case Tfix:
+ if(n->op != Oconst)
+ break;
+ return 1;
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ if(n->op == Otuple)
+ n = n->left;
+ else if(n->op == Ocall)
+ n = n->right;
+ else
+ break;
+ for(; n != nil; n = n->right)
+ if(!initable(v, n->left, allocdep))
+ return 0;
+ return 1;
+ case Tarray:
+ if(n->op != Oarray)
+ break;
+ if(allocdep >= DADEPTH){
+ nerror(v, "%Vs initializer has arrays nested more than %d deep", v, allocdep);
+ return 0;
+ }
+ allocdep++;
+ usedesc(mktdesc(n->ty->tof));
+ if(n->left->op != Oconst){
+ nerror(v, "%Vs size is not a constant", v);
+ return 0;
+ }
+ for(e = n->right; e != nil; e = e->right)
+ if(!initable(v, e->left->right, allocdep))
+ return 0;
+ return 1;
+ case Tany:
+ return 1;
+ case Tref:
+ case Tlist:
+ case Tpoly:
+ default:
+ nerror(v, "can't initialize %Q", v);
+ return 0;
+ }
+ nerror(v, "%Vs initializer, %V, is not a constant expression", v, n);
+ return 0;
+}
+
+/*
+ * merge together two sorted lists, yielding a sorted list
+ */
+static Node*
+elemmerge(Node *e, Node *f)
+{
+ Node rock, *r;
+
+ r = &rock;
+ while(e != nil && f != nil){
+ if(e->left->left->val <= f->left->left->val){
+ r->right = e;
+ e = e->right;
+ }else{
+ r->right = f;
+ f = f->right;
+ }
+ r = r->right;
+ }
+ if(e != nil)
+ r->right = e;
+ else
+ r->right = f;
+ return rock.right;
+}
+
+/*
+ * recursively split lists and remerge them after they are sorted
+ */
+static Node*
+recelemsort(Node *e, int n)
+{
+ Node *r, *ee;
+ int i, m;
+
+ if(n <= 1)
+ return e;
+ m = n / 2 - 1;
+ ee = e;
+ for(i = 0; i < m; i++)
+ ee = ee->right;
+ r = ee->right;
+ ee->right = nil;
+ return elemmerge(recelemsort(e, n / 2),
+ recelemsort(r, (n + 1) / 2));
+}
+
+/*
+ * sort the elems by index; wild card is first
+ */
+Node*
+elemsort(Node *e)
+{
+ Node *ee;
+ int n;
+
+ n = 0;
+ for(ee = e; ee != nil; ee = ee->right){
+ if(ee->left->left->op == Owild)
+ ee->left->left->val = -1;
+ n++;
+ }
+ return recelemsort(e, n);
+}
+
+int
+sametree(Node *n1, Node *n2)
+{
+ if(n1 == n2)
+ return 1;
+ if(n1 == nil || n2 == nil)
+ return 0;
+ if(n1->op != n2->op || n1->ty != n2->ty)
+ return 0;
+ if(n1->op == Oconst){
+ switch(n1->ty->kind){
+ case Tbig:
+ case Tbyte:
+ case Tint:
+ return n1->val == n2->val;
+ case Treal:
+ return n1->rval == n2->rval;
+ case Tfix:
+ return n1->val == n2->val && tequal(n1->ty, n2->ty);
+ case Tstring:
+ return n1->decl->sym == n2->decl->sym;
+ }
+ return 0;
+ }
+ return n1->decl == n2->decl && sametree(n1->left, n2->left) && sametree(n1->right, n2->right);
+}
+
+int
+occurs(Decl *d, Node *n)
+{
+ if(n == nil)
+ return 0;
+ if(n->op == Oname){
+ if(d == n->decl)
+ return 1;
+ return 0;
+ }
+ return occurs(d, n->left) + occurs(d, n->right);
+}
+
+/*
+ * left and right subtrees the same
+ */
+Node*
+folds(Node *n)
+{
+ if(hasside(n, 1))
+ return n;
+ switch(n->op){
+ case Oeq:
+ case Oleq:
+ case Ogeq:
+ n->val = 1;
+ break;
+ case Osub:
+ n->val = 0;
+ n->rval = 0.0;
+ break;
+ case Oxor:
+ case Oneq:
+ case Olt:
+ case Ogt:
+ n->val = 0;
+ break;
+ case Oand:
+ case Oor:
+ case Oandand:
+ case Ooror:
+ return n->left;
+ default:
+ return n;
+ }
+ n->op = Oconst;
+ n->left = n->right = nil;
+ n->decl = nil;
+ return n;
+}
+
+/*
+ * constant folding for typechecked expressions,
+ */
+Node*
+fold(Node *n)
+{
+ if(n == nil)
+ return nil;
+ if(debug['F'])
+ print("fold %n\n", n);
+ n = efold(n);
+ if(debug['F'])
+ print("folded %n\n", n);
+ return n;
+}
+
+Node*
+efold(Node *n)
+{
+ Decl *d;
+ Node *left, *right;
+
+ if(n == nil)
+ return nil;
+
+ left = n->left;
+ right = n->right;
+ switch(n->op){
+ case Oname:
+ d = n->decl;
+ if(d->importid != nil)
+ d = d->importid;
+ if(d->store != Dconst){
+ if(d->store == Dtag){
+ n->op = Oconst;
+ n->ty = tint;
+ n->val = d->tag;
+ }
+ break;
+ }
+ switch(n->ty->kind){
+ case Tbig:
+ n->op = Oconst;
+ n->val = d->init->val;
+ break;
+ case Tbyte:
+ n->op = Oconst;
+ n->val = d->init->val & 0xff;
+ break;
+ case Tint:
+ case Tfix:
+ n->op = Oconst;
+ n->val = d->init->val;
+ break;
+ case Treal:
+ n->op = Oconst;
+ n->rval = d->init->rval;
+ break;
+ case Tstring:
+ n->op = Oconst;
+ n->decl = d->init->decl;
+ break;
+ case Ttuple:
+ *n = *d->init;
+ break;
+ case Tadt:
+ *n = *d->init;
+ n = rewrite(n); /* was call */
+ break;
+ case Texception:
+ if(!n->ty->cons)
+ fatal("non-const exception type in efold");
+ n->op = Oconst;
+ break;
+ default:
+ fatal("unknown const type %T in efold", n->ty);
+ break;
+ }
+ break;
+ case Oadd:
+ left = efold(left);
+ right = efold(right);
+ n->left = left;
+ n->right = right;
+ if(n->ty == tstring && right->op == Oconst){
+ if(left->op == Oconst)
+ n = mksconst(&n->src, stringcat(left->decl->sym, right->decl->sym));
+ else if(left->op == Oadd && left->ty == tstring && left->right->op == Oconst){
+ left->right = mksconst(&n->src, stringcat(left->right->decl->sym, right->decl->sym));
+ n = left;
+ }
+ }
+ break;
+ case Olen:
+ left = efold(left);
+ n->left = left;
+ if(left->ty == tstring && left->op == Oconst)
+ n = mkconst(&n->src, utflen(left->decl->sym->name));
+ break;
+ case Oslice:
+ if(right->left->op == Onothing)
+ right->left = mkconst(&right->left->src, 0);
+ n->left = efold(left);
+ n->right = efold(right);
+ break;
+ case Oinds:
+ n->left = left = efold(left);
+ n->right = right = efold(right);
+ if(right->op == Oconst && left->op == Oconst){
+ ;
+ }
+ break;
+ case Ocast:
+ n->op = Ocast;
+ left = efold(left);
+ n->left = left;
+ if(n->ty == left->ty || n->ty->kind == Tfix && tequal(n->ty, left->ty))
+ return left;
+ if(left->op == Oconst)
+ return foldcast(n, left);
+ break;
+ case Odot:
+ case Omdot:
+ /*
+ * what about side effects from left?
+ */
+ d = right->decl;
+ switch(d->store){
+ case Dconst:
+ case Dtag:
+ case Dtype:
+ /*
+ * set it up as a name and let that case do the hard work
+ */
+ n->op = Oname;
+ n->decl = d;
+ n->left = nil;
+ n->right = nil;
+ return efold(n);
+ }
+ n->left = efold(left);
+ if(n->left->op == Otuple)
+ n = tuplemem(n->left, d);
+ else
+ n->right = efold(right);
+ break;
+ case Otagof:
+ if(n->decl != nil){
+ n->op = Oconst;
+ n->left = nil;
+ n->right = nil;
+ n->val = n->decl->tag;
+ return efold(n);
+ }
+ n->left = efold(left);
+ break;
+ case Oif:
+ n->left = left = efold(left);
+ n->right = right = efold(right);
+ if(left->op == Oconst){
+ if(left->val)
+ return right->left;
+ else
+ return right->right;
+ }
+ break;
+ default:
+ n->left = efold(left);
+ n->right = efold(right);
+ break;
+ }
+
+ left = n->left;
+ right = n->right;
+ if(left == nil)
+ return n;
+
+ if(right == nil){
+ if(left->op == Oconst){
+ if(left->ty == tint || left->ty == tbyte || left->ty == tbig)
+ return foldc(n);
+ if(left->ty == treal)
+ return foldr(n);
+ }
+ return n;
+ }
+
+ if(left->op == Oconst){
+ switch(n->op){
+ case Olsh:
+ case Orsh:
+ if(left->val == 0 && !hasside(right, 1))
+ return left;
+ break;
+ case Ooror:
+ if(left->ty == tint || left->ty == tbyte || left->ty == tbig){
+ if(left->val == 0){
+ n = mkbin(Oneq, right, mkconst(&right->src, 0));
+ n->ty = right->ty;
+ n->left->ty = right->ty;
+ return efold(n);
+ }
+ left->val = 1;
+ return left;
+ }
+ break;
+ case Oandand:
+ if(left->ty == tint || left->ty == tbyte || left->ty == tbig){
+ if(left->val == 0)
+ return left;
+ n = mkbin(Oneq, right, mkconst(&right->src, 0));
+ n->ty = right->ty;
+ n->left->ty = right->ty;
+ return efold(n);
+ }
+ break;
+ }
+ }
+ if(left->op == Oconst && right->op != Oconst
+ && opcommute[n->op]
+ && n->ty != tstring){
+ n->op = opcommute[n->op];
+ n->left = right;
+ n->right = left;
+ left = right;
+ right = n->right;
+ }
+ if(right->op == Oconst && left->op == n->op && left->right->op == Oconst
+ && (n->op == Oadd || n->op == Omul || n->op == Oor || n->op == Oxor || n->op == Oand)
+ && n->ty != tstring){
+ n->left = left->left;
+ left->left = right;
+ right = efold(left);
+ n->right = right;
+ left = n->left;
+ }
+ if(right->op == Oconst){
+ if(n->op == Oexp && left->ty == treal){
+ if(left->op == Oconst)
+ return foldr(n);
+ return n;
+ }
+ if(right->ty == tint || right->ty == tbyte || left->ty == tbig){
+ if(left->op == Oconst)
+ return foldc(n);
+ return foldvc(n);
+ }
+ if(right->ty == treal && left->op == Oconst)
+ return foldr(n);
+ }
+ if(sametree(left, right))
+ return folds(n);
+ return n;
+}
+
+/*
+ * does evaluating the node have any side effects?
+ */
+int
+hasside(Node *n, int strict)
+{
+ for(; n != nil; n = n->right){
+ if(sideeffect[n->op] && (strict || n->op != Oadr && n->op != Oind))
+ return 1;
+ if(hasside(n->left, strict))
+ return 1;
+ }
+ return 0;
+}
+
+int
+hascall(Node *n)
+{
+ for(; n != nil; n = n->right){
+ if(n->op == Ocall || n->op == Ospawn)
+ return 1;
+ if(hascall(n->left))
+ return 1;
+ }
+ return 0;
+}
+
+int
+hasasgns(Node *n)
+{
+ if(n == nil)
+ return 0;
+ if(n->op != Ocall && isused[n->op] && n->op != Onothing)
+ return 1;
+ return hasasgns(n->left) || hasasgns(n->right);
+}
+
+int
+nodes(Node *n)
+{
+ if(n == nil)
+ return 0;
+ return 1+nodes(n->left)+nodes(n->right);
+}
+
+Node*
+foldcast(Node *n, Node *left)
+{
+ Real r;
+ char *buf, *e;
+
+ switch(left->ty->kind){
+ case Tint:
+ left->val &= 0xffffffff;
+ if(left->val & 0x80000000)
+ left->val |= (Long)0xffffffff << 32;
+ return foldcasti(n, left);
+ case Tbyte:
+ left->val &= 0xff;
+ return foldcasti(n, left);
+ case Tbig:
+ return foldcasti(n, left);
+ case Treal:
+ switch(n->ty->kind){
+ case Tint:
+ case Tbyte:
+ case Tbig:
+ r = left->rval;
+ left->val = r < 0 ? r - .5 : r + .5;
+ break;
+ case Tfix:
+ left->val = real2fix(left->rval, n->ty);
+ break;
+ case Tstring:
+ buf = allocmem(NumSize);
+ e = seprint(buf, buf+NumSize, "%g", left->rval);
+ return mksconst(&n->src, enterstring(buf, e-buf));
+ default:
+ return n;
+ }
+ break;
+ case Tfix:
+ switch(n->ty->kind){
+ case Tint:
+ case Tbyte:
+ case Tbig:
+ left->val = fix2real(left->val, left->ty);
+ break;
+ case Treal:
+ left->rval = fix2real(left->val, left->ty);
+ break;
+ case Tfix:
+ if(tequal(left->ty, n->ty))
+ return left;
+ left->val = fix2fix(left->val, left->ty, n->ty);
+ break;
+ case Tstring:
+ buf = allocmem(NumSize);
+ e = seprint(buf, buf+NumSize, "%g", fix2real(left->val, left->ty));
+ return mksconst(&n->src, enterstring(buf, e-buf));
+ default:
+ return n;
+ }
+ break;
+ case Tstring:
+ switch(n->ty->kind){
+ case Tint:
+ case Tbyte:
+ case Tbig:
+ left->val = strtoi(left->decl->sym->name, 10);
+ break;
+ case Treal:
+ left->rval = strtod(left->decl->sym->name, nil);
+ break;
+ case Tfix:
+ left->val = real2fix(strtod(left->decl->sym->name, nil), n->ty);
+ break;
+ default:
+ return n;
+ }
+ break;
+ default:
+ return n;
+ }
+ left->ty = n->ty;
+ left->src = n->src;
+ return left;
+}
+
+/*
+ * left is some kind of int type
+ */
+Node*
+foldcasti(Node *n, Node *left)
+{
+ char *buf, *e;
+
+ switch(n->ty->kind){
+ case Tint:
+ left->val &= 0xffffffff;
+ if(left->val & 0x80000000)
+ left->val |= (Long)0xffffffff << 32;
+ break;
+ case Tbyte:
+ left->val &= 0xff;
+ break;
+ case Tbig:
+ break;
+ case Treal:
+ left->rval = left->val;
+ break;
+ case Tfix:
+ left->val = real2fix(left->val, n->ty);
+ break;
+ case Tstring:
+ buf = allocmem(NumSize);
+ e = seprint(buf, buf+NumSize, "%lld", left->val);
+ return mksconst(&n->src, enterstring(buf, e-buf));
+ default:
+ return n;
+ }
+ left->ty = n->ty;
+ left->src = n->src;
+ return left;
+}
+
+/*
+ * right is a const int
+ */
+Node*
+foldvc(Node *n)
+{
+ Node *left, *right;
+
+ left = n->left;
+ right = n->right;
+ switch(n->op){
+ case Oadd:
+ case Osub:
+ case Oor:
+ case Oxor:
+ case Olsh:
+ case Orsh:
+ case Ooror:
+ if(right->val == 0)
+ return left;
+ if(n->op == Ooror && !hasside(left, 1))
+ return right;
+ break;
+ case Oand:
+ if(right->val == 0 && !hasside(left, 1))
+ return right;
+ break;
+ case Omul:
+ if(right->val == 1)
+ return left;
+ if(right->val == 0 && !hasside(left, 1))
+ return right;
+ break;
+ case Odiv:
+ if(right->val == 1)
+ return left;
+ break;
+ case Omod:
+ if(right->val == 1 && !hasside(left, 1)){
+ right->val = 0;
+ return right;
+ }
+ break;
+ case Oexp:
+ if(right->val == 0){
+ right->val = 1;
+ return right;
+ }
+ if(right->val == 1)
+ return left;
+ break;
+ case Oandand:
+ if(right->val != 0)
+ return left;
+ if(!hasside(left, 1))
+ return right;
+ break;
+ case Oneq:
+ if(!isrelop[left->op])
+ return n;
+ if(right->val == 0)
+ return left;
+ n->op = Onot;
+ n->right = nil;
+ break;
+ case Oeq:
+ if(!isrelop[left->op])
+ return n;
+ if(right->val != 0)
+ return left;
+ n->op = Onot;
+ n->right = nil;
+ break;
+ }
+ return n;
+}
+
+/*
+ * left and right are const ints
+ */
+Node*
+foldc(Node *n)
+{
+ Node *left, *right;
+ Long lv, v;
+ int rv, nb;
+
+ left = n->left;
+ right = n->right;
+ switch(n->op){
+ case Oadd:
+ v = left->val + right->val;
+ break;
+ case Osub:
+ v = left->val - right->val;
+ break;
+ case Omul:
+ v = left->val * right->val;
+ break;
+ case Odiv:
+ if(right->val == 0){
+ nerror(n, "divide by 0 in constant expression");
+ return n;
+ }
+ v = left->val / right->val;
+ break;
+ case Omod:
+ if(right->val == 0){
+ nerror(n, "mod by 0 in constant expression");
+ return n;
+ }
+ v = left->val % right->val;
+ break;
+ case Oexp:
+ if(left->val == 0 && right->val < 0){
+ nerror(n, "0 to negative power in constant expression");
+ return n;
+ }
+ v = ipow(left->val, right->val);
+ break;
+ case Oand:
+ v = left->val & right->val;
+ break;
+ case Oor:
+ v = left->val | right->val;
+ break;
+ case Oxor:
+ v = left->val ^ right->val;
+ break;
+ case Olsh:
+ lv = left->val;
+ rv = right->val;
+ if(rv < 0 || rv >= n->ty->size * 8){
+ nwarn(n, "shift amount %d out of range", rv);
+ rv = 0;
+ }
+ if(rv == 0){
+ v = lv;
+ break;
+ }
+ v = lv << rv;
+ break;
+ case Orsh:
+ lv = left->val;
+ rv = right->val;
+ nb = n->ty->size * 8;
+ if(rv < 0 || rv >= nb){
+ nwarn(n, "shift amount %d out of range", rv);
+ rv = 0;
+ }
+ if(rv == 0){
+ v = lv;
+ break;
+ }
+ v = lv >> rv;
+
+ /*
+ * properly sign extend c right shifts
+ */
+ if((n->ty == tint || n->ty == tbig)
+ && rv != 0
+ && (lv & (1<<(nb-1)))){
+ lv = 0;
+ lv = ~lv;
+ v |= lv << (nb - rv);
+ }
+ break;
+ case Oneg:
+ v = -left->val;
+ break;
+ case Ocomp:
+ v = ~left->val;
+ break;
+ case Oeq:
+ v = left->val == right->val;
+ break;
+ case Oneq:
+ v = left->val != right->val;
+ break;
+ case Ogt:
+ v = left->val > right->val;
+ break;
+ case Ogeq:
+ v = left->val >= right->val;
+ break;
+ case Olt:
+ v = left->val < right->val;
+ break;
+ case Oleq:
+ v = left->val <= right->val;
+ break;
+ case Oandand:
+ v = left->val && right->val;
+ break;
+ case Ooror:
+ v = left->val || right->val;
+ break;
+ case Onot:
+ v = !left->val;
+ break;
+ default:
+ return n;
+ }
+ if(n->ty == tint){
+ v &= 0xffffffff;
+ if(v & 0x80000000)
+ v |= (Long)0xffffffff << 32;
+ }else if(n->ty == tbyte)
+ v &= 0xff;
+ n->left = nil;
+ n->right = nil;
+ n->decl = nil;
+ n->op = Oconst;
+ n->val = v;
+ return n;
+}
+
+/*
+ * left and right are const reals
+ */
+Node*
+foldr(Node *n)
+{
+ Node *left, *right;
+ double rv;
+ Long v;
+
+ rv = 0.;
+ v = 0;
+
+ left = n->left;
+ right = n->right;
+ switch(n->op){
+ case Ocast:
+ return n;
+ case Oadd:
+ rv = left->rval + right->rval;
+ break;
+ case Osub:
+ rv = left->rval - right->rval;
+ break;
+ case Omul:
+ rv = left->rval * right->rval;
+ break;
+ case Odiv:
+ rv = left->rval / right->rval;
+ break;
+ case Oexp:
+ rv = rpow(left->rval, right->val);
+ break;
+ case Oneg:
+ rv = -left->rval;
+ break;
+ case Oinv:
+ if(left->rval == 0.0){
+ error(n->src.start, "divide by 0 in fixed point type");
+ return n;
+ }
+ rv = 1/left->rval;
+ break;
+ case Oeq:
+ v = left->rval == right->rval;
+ break;
+ case Oneq:
+ v = left->rval != right->rval;
+ break;
+ case Ogt:
+ v = left->rval > right->rval;
+ break;
+ case Ogeq:
+ v = left->rval >= right->rval;
+ break;
+ case Olt:
+ v = left->rval < right->rval;
+ break;
+ case Oleq:
+ v = left->rval <= right->rval;
+ break;
+ default:
+ return n;
+ }
+ n->left = nil;
+ n->right = nil;
+
+ if(isNaN(rv))
+ rv = canonnan;
+
+ n->rval = rv;
+ n->val = v;
+
+ n->op = Oconst;
+ return n;
+}
+
+Node*
+varinit(Decl *d, Node *e)
+{
+ Node *n;
+
+ n = mkdeclname(&e->src, d);
+ if(d->next == nil)
+ return mkbin(Oas, n, e);
+ return mkbin(Oas, n, varinit(d->next, e));
+}
+
+/*
+ * given: an Oseq list with left == next or the last child
+ * make a list with the right == next
+ * ie: Oseq(Oseq(a, b),c) ==> Oseq(a, Oseq(b, Oseq(c, nil))))
+ */
+Node*
+rotater(Node *e)
+{
+ Node *left;
+
+ if(e == nil)
+ return e;
+ if(e->op != Oseq)
+ return mkunary(Oseq, e);
+ e->right = mkunary(Oseq, e->right);
+ while(e->left->op == Oseq){
+ left = e->left;
+ e->left = left->right;
+ left->right = e;
+ e = left;
+ }
+ return e;
+}
+
+/*
+ * reverse the case labels list
+ */
+Node*
+caselist(Node *s, Node *nr)
+{
+ Node *r;
+
+ r = s->right;
+ s->right = nr;
+ if(r == nil)
+ return s;
+ return caselist(r, s);
+}
+
+/*
+ * e is a seq of expressions; make into cons's to build a list
+ */
+Node*
+etolist(Node *e)
+{
+ Node *left, *n;
+
+ if(e == nil)
+ return nil;
+ n = mknil(&e->src);
+ n->src.start = n->src.stop;
+ if(e->op != Oseq)
+ return mkbin(Ocons, e, n);
+ e->right = mkbin(Ocons, e->right, n);
+ while(e->left->op == Oseq){
+ e->op = Ocons;
+ left = e->left;
+ e->left = left->right;
+ left->right = e;
+ e = left;
+ }
+ e->op = Ocons;
+ return e;
+}
+
+Node*
+dupn(int resrc, Src *src, Node *n)
+{
+ Node *nn;
+
+ nn = allocmem(sizeof *nn);
+ *nn = *n;
+ if(resrc)
+ nn->src = *src;
+ if(nn->left != nil)
+ nn->left = dupn(resrc, src, nn->left);
+ if(nn->right != nil)
+ nn->right = dupn(resrc, src, nn->right);
+ return nn;
+}
+
+Node*
+mkn(int op, Node *left, Node *right)
+{
+ Node *n;
+
+ n = allocmem(sizeof *n);
+ *n = znode;
+ n->op = op;
+ n->left = left;
+ n->right = right;
+ return n;
+}
+
+Node*
+mkunary(int op, Node *left)
+{
+ Node *n;
+
+ n = mkn(op, left, nil);
+ n->src = left->src;
+ return n;
+}
+
+Node*
+mkbin(int op, Node *left, Node *right)
+{
+ Node *n;
+
+ n = mkn(op, left, right);
+ n->src.start = left->src.start;
+ n->src.stop = right->src.stop;
+ return n;
+}
+
+Node*
+mkdeclname(Src *src, Decl *d)
+{
+ Node *n;
+
+ n = mkn(Oname, nil, nil);
+ n->src = *src;
+ n->decl = d;
+ n->ty = d->ty;
+ d->refs++;
+ return n;
+}
+
+Node*
+mknil(Src *src)
+{
+ return mkdeclname(src, nildecl);
+}
+
+Node*
+mkname(Src *src, Sym *s)
+{
+ Node *n;
+
+ n = mkn(Oname, nil, nil);
+ n->src = *src;
+ if(s->unbound == nil){
+ s->unbound = mkdecl(src, Dunbound, nil);
+ s->unbound->sym = s;
+ }
+ n->decl = s->unbound;
+ return n;
+}
+
+Node*
+mkconst(Src *src, Long v)
+{
+ Node *n;
+
+ n = mkn(Oconst, nil, nil);
+ n->ty = tint;
+ n->val = v;
+ n->src = *src;
+ return n;
+}
+
+Node*
+mkrconst(Src *src, Real v)
+{
+ Node *n;
+
+ n = mkn(Oconst, nil, nil);
+ n->ty = treal;
+ n->rval = v;
+ n->src = *src;
+ return n;
+}
+
+Node*
+mksconst(Src *src, Sym *s)
+{
+ Node *n;
+
+ n = mkn(Oconst, nil, nil);
+ n->ty = tstring;
+ n->decl = mkdecl(src, Dconst, tstring);
+ n->decl->sym = s;
+ n->src = *src;
+ return n;
+}
+
+int
+opconv(Fmt *f)
+{
+ int op;
+ char buf[32];
+
+ op = va_arg(f->args, int);
+ if(op < 0 || op > Oend) {
+ seprint(buf, buf+sizeof(buf), "op %d", op);
+ return fmtstrcpy(f, buf);
+ }
+ return fmtstrcpy(f, opname[op]);
+}
+
+int
+etconv(Fmt *f)
+{
+ Node *n;
+ char buf[1024];
+
+ n = va_arg(f->args, Node*);
+ if(n->ty == tany || n->ty == tnone || n->ty == terror)
+ seprint(buf, buf+sizeof(buf), "%V", n);
+ else
+ seprint(buf, buf+sizeof(buf), "%V of type %T", n, n->ty);
+ return fmtstrcpy(f, buf);
+}
+
+int
+expconv(Fmt *f)
+{
+ Node *n;
+ char buf[4096], *p;
+
+ n = va_arg(f->args, Node*);
+ p = buf;
+ *p = 0;
+ if(f->r == 'V')
+ *p++ = '\'';
+ p = eprint(p, buf+sizeof(buf)-1, n);
+ if(f->r == 'V')
+ *p++ = '\'';
+ *p = 0;
+ return fmtstrcpy(f, buf);
+}
+
+char*
+eprint(char *buf, char *end, Node *n)
+{
+ if(n == nil)
+ return buf;
+ if(n->flags & PARENS)
+ buf = secpy(buf, end, "(");
+ switch(n->op){
+ case Obreak:
+ case Ocont:
+ buf = secpy(buf, end, opname[n->op]);
+ if(n->decl != nil){
+ buf = seprint(buf, end, " %s", n->decl->sym->name);
+ }
+ break;
+ case Oexit:
+ case Owild:
+ buf = secpy(buf, end, opname[n->op]);
+ break;
+ case Onothing:
+ break;
+ case Oadr:
+ case Oused:
+ buf = eprint(buf, end, n->left);
+ break;
+ case Oseq:
+ buf = eprintlist(buf, end, n, ", ");
+ break;
+ case Oname:
+ if(n->decl == nil)
+ buf = secpy(buf, end, "<nil>");
+ else
+ buf = seprint(buf, end, "%s", n->decl->sym->name);
+ break;
+ case Oconst:
+ if(n->ty->kind == Tstring){
+ buf = stringpr(buf, end, n->decl->sym);
+ break;
+ }
+ if(n->decl != nil && n->decl->sym != nil){
+ buf = seprint(buf, end, "%s", n->decl->sym->name);
+ break;
+ }
+ switch(n->ty->kind){
+ case Tint:
+ case Tbyte:
+ buf = seprint(buf, end, "%ld", (long)n->val);
+ break;
+ case Tbig:
+ buf = seprint(buf, end, "%lld", n->val);
+ break;
+ case Treal:
+ buf = seprint(buf, end, "%g", n->rval);
+ break;
+ case Tfix:
+ buf = seprint(buf, end, "%ld(%g)", (long)n->val, n->ty->val->rval);
+ break;
+ default:
+ buf = secpy(buf, end, opname[n->op]);
+ break;
+ }
+ break;
+ case Ocast:
+ buf = seprint(buf, end, "%T ", n->ty);
+ buf = eprint(buf, end, n->left);
+ break;
+ case Otuple:
+ if(n->ty != nil && n->ty->kind == Tadt)
+ buf = seprint(buf, end, "%s", n->ty->decl->sym->name);
+ buf = seprint(buf, end, "(");
+ buf = eprintlist(buf, end, n->left, ", ");
+ buf = secpy(buf, end, ")");
+ break;
+ case Ochan:
+ if(n->left){
+ buf = secpy(buf, end, "chan [");
+ buf = eprint(buf, end, n->left);
+ buf = secpy(buf, end, "] of ");
+ buf = seprint(buf, end, "%T", n->ty->tof);
+ }else
+ buf = seprint(buf, end, "chan of %T", n->ty->tof);
+ break;
+ case Oarray:
+ buf = secpy(buf, end, "array [");
+ if(n->left != nil)
+ buf = eprint(buf, end, n->left);
+ buf = secpy(buf, end, "] of ");
+ if(n->right != nil){
+ buf = secpy(buf, end, "{");
+ buf = eprintlist(buf, end, n->right, ", ");
+ buf = secpy(buf, end, "}");
+ }else{
+ buf = seprint(buf, end, "%T", n->ty->tof);
+ }
+ break;
+ case Oelem:
+ case Olabel:
+ if(n->left != nil){
+ buf = eprintlist(buf, end, n->left, " or ");
+ buf = secpy(buf, end, " =>");
+ }
+ buf = eprint(buf, end, n->right);
+ break;
+ case Orange:
+ buf = eprint(buf, end, n->left);
+ buf = secpy(buf, end, " to ");
+ buf = eprint(buf, end, n->right);
+ break;
+ case Ospawn:
+ buf = secpy(buf, end, "spawn ");
+ buf = eprint(buf, end, n->left);
+ break;
+ case Oraise:
+ buf = secpy(buf, end, "raise ");
+ buf = eprint(buf, end, n->left);
+ break;
+ case Ocall:
+ buf = eprint(buf, end, n->left);
+ buf = secpy(buf, end, "(");
+ buf = eprintlist(buf, end, n->right, ", ");
+ buf = secpy(buf, end, ")");
+ break;
+ case Oinc:
+ case Odec:
+ buf = eprint(buf, end, n->left);
+ buf = secpy(buf, end, opname[n->op]);
+ break;
+ case Oindex:
+ case Oindx:
+ case Oinds:
+ buf = eprint(buf, end, n->left);
+ buf = secpy(buf, end, "[");
+ buf = eprint(buf, end, n->right);
+ buf = secpy(buf, end, "]");
+ break;
+ case Oslice:
+ buf = eprint(buf, end, n->left);
+ buf = secpy(buf, end, "[");
+ buf = eprint(buf, end, n->right->left);
+ buf = secpy(buf, end, ":");
+ buf = eprint(buf, end, n->right->right);
+ buf = secpy(buf, end, "]");
+ break;
+ case Oload:
+ buf = seprint(buf, end, "load %T ", n->ty);
+ buf = eprint(buf, end, n->left);
+ break;
+ case Oref:
+ case Olen:
+ case Ohd:
+ case Otl:
+ case Otagof:
+ buf = secpy(buf, end, opname[n->op]);
+ buf = secpy(buf, end, " ");
+ buf = eprint(buf, end, n->left);
+ break;
+ default:
+ if(n->right == nil){
+ buf = secpy(buf, end, opname[n->op]);
+ buf = eprint(buf, end, n->left);
+ }else{
+ buf = eprint(buf, end, n->left);
+ buf = secpy(buf, end, opname[n->op]);
+ buf = eprint(buf, end, n->right);
+ }
+ break;
+ }
+ if(n->flags & PARENS)
+ buf = secpy(buf, end, ")");
+ return buf;
+}
+
+char*
+eprintlist(char *buf, char *end, Node *elist, char *sep)
+{
+ if(elist == nil)
+ return buf;
+ for(; elist->right != nil; elist = elist->right){
+ if(elist->op == Onothing)
+ continue;
+ if(elist->left->op == Ofnptr)
+ return buf;
+ buf = eprint(buf, end, elist->left);
+ if(elist->right->left->op != Ofnptr)
+ buf = secpy(buf, end, sep);
+ }
+ buf = eprint(buf, end, elist->left);
+ return buf;
+}
+
+int
+nodeconv(Fmt *f)
+{
+ Node *n;
+ char buf[4096];
+
+ n = va_arg(f->args, Node*);
+ buf[0] = 0;
+ nprint(buf, buf+sizeof(buf), n, 0);
+ return fmtstrcpy(f, buf);
+}
+
+char*
+nprint(char *buf, char *end, Node *n, int indent)
+{
+ int i;
+
+ if(n == nil)
+ return buf;
+ buf = seprint(buf, end, "\n");
+ for(i = 0; i < indent; i++)
+ if(buf < end-1)
+ *buf++ = ' ';
+ switch(n->op){
+ case Oname:
+ if(n->decl == nil)
+ buf = secpy(buf, end, "name <nil>");
+ else
+ buf = seprint(buf, end, "name %s", n->decl->sym->name);
+ break;
+ case Oconst:
+ if(n->decl != nil && n->decl->sym != nil)
+ buf = seprint(buf, end, "const %s", n->decl->sym->name);
+ else
+ buf = seprint(buf, end, "%O", n->op);
+ if(n->ty == tint || n->ty == tbyte || n->ty == tbig)
+ buf = seprint(buf, end, " (%ld)", (long)n->val);
+ break;
+ default:
+ buf = seprint(buf, end, "%O", n->op);
+ break;
+ }
+ buf = seprint(buf, end, " %T %d %d", n->ty, n->addable, n->temps);
+ indent += 2;
+ buf = nprint(buf, end, n->left, indent);
+ buf = nprint(buf, end, n->right, indent);
+ return buf;
+}
--- /dev/null
+++ b/limbo/optab.c
@@ -1,0 +1,658 @@
+#include "limbo.h"
+
+uchar movetab[Mend][Tend] =
+{
+ /* Mas */
+ {
+ /* Tnone */ 0,
+ /* Tadt */ IMOVM,
+ /* Tadtpick */ IMOVM,
+ /* Tarray */ IMOVP,
+ /* Tbig */ IMOVL,
+ /* Tbyte */ IMOVB,
+ /* Tchan */ IMOVP,
+ /* Treal */ IMOVF,
+ /* Tfn */ 0,
+ /* Tint */ IMOVW,
+ /* Tlist */ IMOVP,
+ /* Tmodule */ IMOVP,
+ /* Tref */ IMOVP,
+ /* Tstring */ IMOVP,
+ /* Ttuple */ IMOVM,
+ /* Texception */ IMOVM,
+ /* Tfix */ IMOVW,
+ /* Tpoly */ IMOVP,
+
+ /* Tainit */ 0,
+ /* Talt */ 0,
+ /* Tany */ IMOVP,
+ /* Tarrow */ 0,
+ /* Tcase */ 0,
+ /* Tcasel */ 0,
+ /* Tcasec */ 0,
+ /* Tdot */ 0,
+ /* Terror */ 0,
+ /* Tgoto */ 0,
+ /* Tid */ 0,
+ },
+ /* Mcons */
+ {
+ /* Tnone */ 0,
+ /* Tadt */ ICONSM,
+ /* Tadtpick */ 0,
+ /* Tarray */ ICONSP,
+ /* Tbig */ ICONSL,
+ /* Tbyte */ ICONSB,
+ /* Tchan */ ICONSP,
+ /* Treal */ ICONSF,
+ /* Tfn */ 0,
+ /* Tint */ ICONSW,
+ /* Tlist */ ICONSP,
+ /* Tmodule */ ICONSP,
+ /* Tref */ ICONSP,
+ /* Tstring */ ICONSP,
+ /* Ttuple */ ICONSM,
+ /* Texception */ ICONSM,
+ /* Tfix */ ICONSW,
+ /* Tpoly */ ICONSP,
+
+ /* Tainit */ 0,
+ /* Talt */ 0,
+ /* Tany */ ICONSP,
+ /* Tarrow */ 0,
+ /* Tcase */ 0,
+ /* Tcasel */ 0,
+ /* Tcasec */ 0,
+ /* Tdot */ 0,
+ /* Terror */ 0,
+ /* Tgoto */ 0,
+ /* Tid */ 0,
+ },
+ /* Mhd */
+ {
+ /* Tnone */ 0,
+ /* Tadt */ IHEADM,
+ /* Tadtpick */ 0,
+ /* Tarray */ IHEADP,
+ /* Tbig */ IHEADL,
+ /* Tbyte */ IHEADB,
+ /* Tchan */ IHEADP,
+ /* Treal */ IHEADF,
+ /* Tfn */ 0,
+ /* Tint */ IHEADW,
+ /* Tlist */ IHEADP,
+ /* Tmodule */ IHEADP,
+ /* Tref */ IHEADP,
+ /* Tstring */ IHEADP,
+ /* Ttuple */ IHEADM,
+ /* Texception */ IHEADM,
+ /* Tfix */ IHEADW,
+ /* Tpoly */ IHEADP,
+
+ /* Tainit */ 0,
+ /* Talt */ 0,
+ /* Tany */ IHEADP,
+ /* Tarrow */ 0,
+ /* Tcase */ 0,
+ /* Tcasel */ 0,
+ /* Tcasec */ 0,
+ /* Tdot*/ 0,
+ /* Terror */ 0,
+ /* Tgoto */ 0,
+ /* Tid */ 0,
+ },
+ /* Mtl */
+ {
+ /* Tnone */ 0,
+ /* Tadt */ 0,
+ /* Tadtpick */ 0,
+ /* Tarray */ 0,
+ /* Tbig */ 0,
+ /* Tbyte */ 0,
+ /* Tchan */ 0,
+ /* Treal */ 0,
+ /* Tfn */ 0,
+ /* Tint */ 0,
+ /* Tlist */ ITAIL,
+ /* Tmodule */ 0,
+ /* Tref */ 0,
+ /* Tstring */ 0,
+ /* Ttuple */ 0,
+ /* Texception */ 0,
+ /* Tfix */ 0,
+ /* Tpoly */ 0,
+
+ /* Tainit */ 0,
+ /* Talt */ 0,
+ /* Tany */ 0,
+ /* Tarrow */ 0,
+ /* Tcase */ 0,
+ /* Tcasel */ 0,
+ /* Tcasec */ 0,
+ /* Tdot */ 0,
+ /* Terror */ 0,
+ /* Tgoto */ 0,
+ /* Tid */ 0,
+ },
+};
+
+uchar chantab[Tend] =
+{
+ /* Tnone */ 0,
+ /* Tadt */ INEWCM,
+ /* Tadtpick */ 0,
+ /* Tarray */ INEWCP,
+ /* Tbig */ INEWCL,
+ /* Tbyte */ INEWCB,
+ /* Tchan */ INEWCP,
+ /* Treal */ INEWCF,
+ /* Tfn */ 0,
+ /* Tint */ INEWCW,
+ /* Tlist */ INEWCP,
+ /* Tmodule */ INEWCP,
+ /* Tref */ INEWCP,
+ /* Tstring */ INEWCP,
+ /* Ttuple */ INEWCM,
+ /* Texception */ INEWCM,
+ /* Tfix */ INEWCW,
+ /* Tpoly */ INEWCP,
+
+ /* Tainit */ 0,
+ /* Talt */ 0,
+ /* Tany */ INEWCP,
+ /* Tarrow */ 0,
+ /* Tcase */ 0,
+ /* Tcasel */ 0,
+ /* Tcasec */ 0,
+ /* Tdot */ 0,
+ /* Terror */ 0,
+ /* Tgoto */ 0,
+ /* Tid */ 0,
+};
+
+uchar disoptab[Oend+1][7] = {
+ /* opcode default byte word big real string fixed */
+ {0},
+ /* Oadd */ {0, IADDB, IADDW, IADDL, IADDF, IADDC, IADDW,},
+ /* Oaddas */ {0, IADDB, IADDW, IADDL, IADDF, IADDC, IADDW,},
+ /* Oadr */ {0},
+ /* Oadtdecl */ {0},
+ /* Oalt */ {0},
+ /* Oand */ {0, IANDB, IANDW, IANDL, 0, 0, 0,},
+ /* Oandand */ {0},
+ /* Oandas */ {0, IANDB, IANDW, IANDL, 0, 0, 0,},
+ /* Oarray */ {0},
+ /* Oas */ {0},
+ /* Obreak */ {0},
+ /* Ocall */ {0},
+ /* Ocase */ {0},
+ /* Ocast */ {0},
+ /* Ochan */ {0},
+ /* Ocomma */ {0},
+ /* Ocomp */ {0},
+ /* Ocondecl */ {0},
+ /* Ocons */ {0},
+ /* Oconst */ {0},
+ /* Ocont */ {0},
+ /* Odas */ {0},
+ /* Odec */ {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,},
+ /* Odiv */ {0, IDIVB, IDIVW, IDIVL, IDIVF, 0, IDIVX,},
+ /* Odivas */ {0, IDIVB, IDIVW, IDIVL, IDIVF, 0, IDIVX,},
+ /* Odo */ {0},
+ /* Odot */ {0},
+ /* Oelem */ {0},
+ /* Oeq */ {IBEQW, IBEQB, IBEQW, IBEQL, IBEQF, IBEQC, IBEQW,},
+ /* Oexcept */ {0},
+ /* Oexdecl */ {0},
+ /* Oexit */ {0},
+ /* Oexp */ {0, 0, IEXPW, IEXPL, IEXPF, 0, 0,},
+ /* Oexpas */ {0, 0, IEXPW, IEXPL, IEXPF, 0, 0,},
+ /* Oexstmt */ {0},
+ /* Ofielddecl */{0},
+ /* Ofnptr */ {0},
+ /* Ofor */ {0},
+ /* Ofunc */ {0},
+ /* Ogeq */ {0, IBGEB, IBGEW, IBGEL, IBGEF, IBGEC, IBGEW,},
+ /* Ogt */ {0, IBGTB, IBGTW, IBGTL, IBGTF, IBGTC, IBGTW,},
+ /* Ohd */ {0},
+ /* Oif */ {0},
+ /* Oimport */ {0},
+ /* Oinc */ {0, IADDB, IADDW, IADDL, IADDF, 0, IADDW,},
+ /* Oind */ {0},
+ /* Oindex */ {0,},
+ /* Oinds */ {0, 0, IINDC, 0, 0, 0, 0,},
+ /* Oindx */ {0, 0, IINDX, 0, 0, 0, 0,},
+ /* Oinv */ {0},
+ /* Ojmp */ {0},
+ /* Olabel */ {0},
+ /* Olen */ {ILENA, 0, 0, 0, 0, ILENC, 0,},
+ /* Oleq */ {0, IBLEB, IBLEW, IBLEL, IBLEF, IBLEC, IBLEW,},
+ /* Oload */ {0},
+ /* Olsh */ {0, ISHLB, ISHLW, ISHLL, 0, 0, 0,},
+ /* Olshas */ {0, ISHLB, ISHLW, ISHLL, 0, 0, 0,},
+ /* Olt */ {0, IBLTB, IBLTW, IBLTL, IBLTF, IBLTC, IBLTW,},
+ /* Omdot */ {0},
+ /* Omod */ {0, IMODB, IMODW, IMODL, 0, 0, 0,},
+ /* Omodas */ {0, IMODB, IMODW, IMODL, 0, 0, 0,},
+ /* Omoddecl */ {0},
+ /* Omul */ {0, IMULB, IMULW, IMULL, IMULF, 0, IMULX,},
+ /* Omulas */ {0, IMULB, IMULW, IMULL, IMULF, 0, IMULX,},
+ /* Oname */ {0},
+ /* Oneg */ {0, 0, 0, 0, INEGF, 0, 0,},
+ /* Oneq */ {IBNEW, IBNEB, IBNEW, IBNEL, IBNEF, IBNEC, IBNEW,},
+ /* Onot */ {0},
+ /* Onothing */ {0},
+ /* Oor */ {0, IORB, IORW, IORL, 0, 0, 0,},
+ /* Ooras */ {0, IORB, IORW, IORL, 0, 0, 0,},
+ /* Ooror */ {0},
+ /* Opick */ {0},
+ /* Opickdecl */ {0},
+ /* Opredec */ {0},
+ /* Opreinc */ {0},
+ /* Oraise */ {0},
+ /* Orange */ {0},
+ /* Orcv */ {0},
+ /* Oref */ {0},
+ /* Oret */ {0},
+ /* Orsh */ {0, ISHRB, ISHRW, ISHRL, 0, 0, 0,},
+ /* Orshas */ {0, ISHRB, ISHRW, ISHRL, 0, 0, 0,},
+ /* Oscope */ {0},
+ /* Oself */ {0},
+ /* Oseq */ {0},
+ /* Oslice */ {ISLICEA,0, 0, 0, 0, ISLICEC, 0,},
+ /* Osnd */ {0},
+ /* Ospawn */ {0},
+ /* Osub */ {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,},
+ /* Osubas */ {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,},
+ /* Otagof */ {0},
+ /* Otl */ {0},
+ /* Otuple */ {0},
+ /* Otype */ {0},
+ /* Otypedecl */ {0},
+ /* Oused */ {0},
+ /* Ovardecl */ {0},
+ /* Ovardecli */ {0},
+ /* Owild */ {0},
+ /* Oxor */ {0, IXORB, IXORW, IXORL, 0, 0, 0,},
+ /* Oxoras */ {0, IXORB, IXORW, IXORL, 0, 0, 0,},
+
+ /* Oend */ {0}
+};
+
+int setisused[] = {
+ Oas,
+ Odas,
+ Oaddas,
+ Osubas,
+ Omulas,
+ Odivas,
+ Omodas,
+ Oexpas,
+ Oandas,
+ Ooras,
+ Oxoras,
+ Olshas,
+ Onothing,
+ Orshas,
+ Oinc,
+ Odec,
+ Opreinc,
+ Opredec,
+ Ocall,
+ Oraise,
+ Ospawn,
+ Osnd,
+ Orcv,
+
+ -1
+};
+
+int setsideeffect[] = {
+ Oas,
+ Odas,
+ Oaddas,
+ Osubas,
+ Omulas,
+ Odivas,
+ Omodas,
+ Oexpas,
+ Oandas,
+ Ooras,
+ Oxoras,
+ Olshas,
+ Orshas,
+ Oinc,
+ Odec,
+ Opreinc,
+ Opredec,
+ Ocall,
+ Oraise,
+ Ospawn,
+ Osnd,
+ Orcv,
+
+ Oadr,
+ Oarray,
+ Ocast,
+ Ochan,
+ Ocons,
+ Odiv,
+ Odot,
+ Oind,
+ Oindex,
+ Oinds,
+ Oindx,
+ Olen,
+ Oload,
+ Omod,
+ Oref,
+
+ -1
+};
+
+char *opname[Oend+1] = {
+ "unknown",
+ /* Oadd */ "+",
+ /* Oaddas */ "+=",
+ /* Oadr */ "adr",
+ /* Oadtdecl */ "adtdecl",
+ /* Oalt */ "alt",
+ /* Oand */ "&",
+ /* Oandand */ "&&",
+ /* Oandas */ "&=",
+ /* Oarray */ "array",
+ /* Oas */ "=",
+ /* Obreak */ "break",
+ /* Ocall */ "call",
+ /* Ocase */ "case",
+ /* Ocast */ "cast",
+ /* Ochan */ "chan",
+ /* Ocomma */ ",",
+ /* Ocomp */ "~",
+ /* Ocondecl */ "condecl",
+ /* Ocons */ "::",
+ /* Oconst */ "const",
+ /* Ocont */ "continue",
+ /* Odas */ ":=",
+ /* Odec */ "--",
+ /* Odiv */ "/",
+ /* Odivas */ "/=",
+ /* Odo */ "do",
+ /* Odot */ ".",
+ /* Oelem */ "elem",
+ /* Oeq */ "==",
+ /* Oexcept */ "except",
+ /* Oexdecl */ "exdecl",
+ /* Oexit */ "exit",
+ /* Oexp */ "**",
+ /* Oexpas */ "**=",
+ /* Oexstmt */ "exstat",
+ /* Ofielddecl */"fielddecl",
+ /* Ofnptr */ "fnptr",
+ /* Ofor */ "for",
+ /* Ofunc */ "fn(){}",
+ /* Ogeq */ ">=",
+ /* Ogt */ ">",
+ /* Ohd */ "hd",
+ /* Oif */ "if",
+ /* Oimport */ "import",
+ /* Oinc */ "++",
+ /* Oind */ "*",
+ /* Oindex */ "index",
+ /* Oinds */ "inds",
+ /* Oindx */ "indx",
+ /* Oinv */ "inv",
+ /* Ojmp */ "jmp",
+ /* Olabel */ "label",
+ /* Olen */ "len",
+ /* Oleq */ "<=",
+ /* Oload */ "load",
+ /* Olsh */ "<<",
+ /* Olshas */ "<<=",
+ /* Olt */ "<",
+ /* Omdot */ "->",
+ /* Omod */ "%",
+ /* Omodas */ "%=",
+ /* Omoddecl */ "moddecl",
+ /* Omul */ "*",
+ /* Omulas */ "*=",
+ /* Oname */ "name",
+ /* Oneg */ "-",
+ /* Oneq */ "!=",
+ /* Onot */ "!",
+ /* Onothing */ "nothing",
+ /* Oor */ "|",
+ /* Ooras */ "|=",
+ /* Ooror */ "||",
+ /* Opick */ "pick",
+ /* Opickdecl */ "pickdecl",
+ /* Opredec */ "--",
+ /* Opreinc */ "++",
+ /* Oraise */ "raise",
+ /* Orange */ "range",
+ /* Orcv */ "<-",
+ /* Oref */ "ref",
+ /* Oret */ "return",
+ /* Orsh */ ">>",
+ /* Orshas */ ">>=",
+ /* Oscope */ "scope",
+ /* Oself */ "self",
+ /* Oseq */ "seq",
+ /* Oslice */ "slice",
+ /* Osnd */ "<-=",
+ /* Ospawn */ "spawn",
+ /* Osub */ "-",
+ /* Osubas */ "-=",
+ /* Otl */ "tagof",
+ /* Otl */ "tl",
+ /* Otuple */ "tuple",
+ /* Otype */ "type",
+ /* Otypedecl */ "typedecl",
+ /* Oused */ "used",
+ /* Ovardecl */ "vardecl",
+ /* Ovardecli */ "vardecli",
+ /* Owild */ "*",
+ /* Oxor */ "^",
+ /* Oxoras */ "^=",
+
+ /* Oend */ "unknown"
+};
+
+int setisbyteinst[] = {
+ IMULB,
+ ISUBB,
+ IADDB,
+ IDIVB,
+ IORB,
+ IXORB,
+ ISHLB,
+ ISHRB,
+ IMODB,
+ IANDB,
+ IBEQB,
+ IBNEB,
+ IBLTB,
+ IBLEB,
+ IBGTB,
+ IBGEB,
+
+ -1
+};
+
+char *instname[256] = {
+ "nop",
+ "alt",
+ "nbalt",
+ "goto",
+ "call",
+ "frame",
+ "spawn",
+ "runt",
+ "load",
+ "mcall",
+ "mspawn",
+ "mframe",
+ "ret",
+ "jmp",
+ "case",
+ "exit",
+ "new",
+ "newa",
+ "newcb",
+ "newcw",
+ "newcf",
+ "newcp",
+ "newcm",
+ "newcmp",
+ "send",
+ "recv",
+ "consb",
+ "consw",
+ "consp",
+ "consf",
+ "consm",
+ "consmp",
+ "headb",
+ "headw",
+ "headp",
+ "headf",
+ "headm",
+ "headmp",
+ "tail",
+ "lea",
+ "indx",
+ "movp",
+ "movm",
+ "movmp",
+ "movb",
+ "movw",
+ "movf",
+ "cvtbw",
+ "cvtwb",
+ "cvtfw",
+ "cvtwf",
+ "cvtca",
+ "cvtac",
+ "cvtwc",
+ "cvtcw",
+ "cvtfc",
+ "cvtcf",
+ "addb",
+ "addw",
+ "addf",
+ "subb",
+ "subw",
+ "subf",
+ "mulb",
+ "mulw",
+ "mulf",
+ "divb",
+ "divw",
+ "divf",
+ "modw",
+ "modb",
+ "andb",
+ "andw",
+ "orb",
+ "orw",
+ "xorb",
+ "xorw",
+ "shlb",
+ "shlw",
+ "shrb",
+ "shrw",
+ "insc",
+ "indc",
+ "addc",
+ "lenc",
+ "lena",
+ "lenl",
+ "beqb",
+ "bneb",
+ "bltb",
+ "bleb",
+ "bgtb",
+ "bgeb",
+ "beqw",
+ "bnew",
+ "bltw",
+ "blew",
+ "bgtw",
+ "bgew",
+ "beqf",
+ "bnef",
+ "bltf",
+ "blef",
+ "bgtf",
+ "bgef",
+ "beqc",
+ "bnec",
+ "bltc",
+ "blec",
+ "bgtc",
+ "bgec",
+ "slicea",
+ "slicela",
+ "slicec",
+ "indw",
+ "indf",
+ "indb",
+ "negf",
+ "movl",
+ "addl",
+ "subl",
+ "divl",
+ "modl",
+ "mull",
+ "andl",
+ "orl",
+ "xorl",
+ "shll",
+ "shrl",
+ "bnel",
+ "bltl",
+ "blel",
+ "bgtl",
+ "bgel",
+ "beql",
+ "cvtlf",
+ "cvtfl",
+ "cvtlw",
+ "cvtwl",
+ "cvtlc",
+ "cvtcl",
+ "headl",
+ "consl",
+ "newcl",
+ "casec",
+ "indl",
+ "movpc",
+ "tcmp",
+ "mnewz",
+ "cvtrf",
+ "cvtfr",
+ "cvtws",
+ "cvtsw",
+ "lsrw",
+ "lsrl",
+ "eclr",
+ "newz",
+ "newaz",
+ "raise",
+ "casel",
+ "mulx",
+ "divx",
+ "cvtxx",
+ "mulx0",
+ "divx0",
+ "cvtxx0",
+ "mulx1",
+ "divx1",
+ "cvtxx1",
+ "cvtfx",
+ "cvtxf",
+ "expw",
+ "expl",
+ "expf",
+ "self",
+};
--- /dev/null
+++ b/limbo/optim.c
@@ -1,0 +1,1803 @@
+#include "limbo.h"
+
+#define bzero bbzero /* bsd name space pollution */
+/*
+ (r, s) := f(); => r, s have def on same pc
+ s = g(); => this def kills previous r def (and s def)
+ solution: r has def pc, s has def pc+1 and next instruction has pc pc+2
+*/
+
+#define BLEN (8*sizeof(ulong))
+#define BSHIFT 5 /* assumes ulong 4 */
+#define BMASK (BLEN-1)
+
+#define SIGN(n) (1<<(n-1))
+#define MSK(n) (SIGN(n)|(SIGN(n)-1))
+#define MASK(a, b) (MSK((b)-(a)+1)<<(a))
+
+#define isnilsrc(s) ((s)->start.line == 0 && (s)->stop.line == 0 && (s)->start.pos == 0 && (s)->stop.pos == 0)
+
+#define limbovar(d) ((d)->sym->name[0] != '.')
+#define structure(t) ((t)->kind == Tadt || (t)->kind == Ttuple)
+
+enum
+{
+ Bclr,
+ Band,
+ Bandinv,
+ Bstore,
+ Bandrev,
+ Bnoop,
+ Bxor,
+ Bor,
+ Bnor,
+ Bequiv,
+ Binv,
+ Bimpby,
+ Brev,
+ Bimp,
+ Bnand,
+ Bset,
+};
+
+enum
+{
+ Suse = 1,
+ Muse = 2,
+ Duse = 4,
+ Sdef = 8,
+ Mdef = 16,
+ Ddef = 32,
+ Tuse1 = 64, /* fixed point temporary */
+ Tuse2 = 128, /* fixed point temporary */
+ Mduse = 256, /* D used if M nil */
+
+ None = 0,
+ Unop = Suse|Ddef,
+ Cunop = Muse|Ddef,
+ Threop = Suse|Muse|Ddef,
+ Binop = Suse|Muse|Ddef|Mduse,
+ Mbinop = Suse|Mdef|Duse, /* strange */
+ Abinop=Suse|Duse|Ddef,
+ Mabinop = Suse|Muse|Duse|Ddef,
+ Use1 = Suse,
+ Use2 = Suse|Duse,
+ Use3 = Suse|Muse|Duse,
+};
+
+enum
+{
+ Sshift = 10,
+ Mshift = 5,
+ Dshift = 0,
+};
+
+#define S(x) ((x)<<Sshift)
+#define M(x) ((x)<<Mshift)
+#define D(x) ((x)<<Dshift)
+
+#define SS(x) (((x)>>Sshift)&0x1f)
+#define SM(x) (((x)>>Mshift)&0x1f)
+#define SD(x) (((x)>>Dshift)&0x1f)
+
+enum
+{
+ I = 0, /* ignore */
+ B = 1, /* byte */
+ W = 4, /* int */
+ P = 4, /* pointer */
+ A = 4, /* array */
+ C = 4, /* string */
+ X = 4, /* fixed */
+ R = 4, /* float */
+ L = 8, /* big */
+ F = 8, /* real */
+ Sh = 2, /* short */
+ Pc = 4, /* pc */
+ Mp = 16, /* memory */
+
+ Bop2 = S(B)|D(B),
+ Bop = S(B)|M(B)|D(B),
+ Bopb = S(B)|M(B)|D(Pc),
+ Wop2 = S(W)|D(W),
+ Wop = S(W)|M(W)|D(W),
+ Wopb = S(W)|M(W)|D(Pc),
+ Lop2 = S(L)|D(L),
+ Lop = S(L)|M(L)|D(L),
+ Lopb = S(L)|M(L)|D(Pc),
+ Cop2 = Wop2,
+ Cop = Wop,
+ Copb = Wopb,
+ Fop2 = Lop2,
+ Fop = Lop,
+ Fopb = Lopb,
+ Xop = Wop,
+};
+
+typedef struct Array Array;
+typedef struct Bits Bits;
+typedef struct Blist Blist;
+typedef struct Block Block;
+typedef struct Idlist Idlist;
+typedef struct Optab Optab;
+
+struct Array
+{
+ int n;
+ int m;
+ Block **a;
+};
+
+struct Bits
+{
+ int n;
+ ulong *b;
+};
+
+struct Blist
+{
+ Block *block;
+ Blist *next;
+};
+
+struct Block
+{
+ int dfn;
+ int flags;
+ Inst *first;
+ Inst *last;
+ Block *prev;
+ Block *next;
+ Blist *pred;
+ Blist *succ;
+ Bits kill;
+ Bits gen;
+ Bits in;
+ Bits out;
+};
+
+struct Idlist
+{
+ int id;
+ Idlist *next;
+};
+
+struct Optab
+{
+ short flags;
+ short size;
+};
+
+Block zblock;
+Decl *regdecls;
+Idlist *frelist;
+Idlist *deflist;
+Idlist *uselist;
+
+static void
+addlist(Idlist **hd, int id)
+{
+ Idlist *il;
+
+ if(frelist == nil)
+ il = (Idlist*)malloc(sizeof(Idlist));
+ else{
+ il = frelist;
+ frelist = frelist->next;
+ }
+ il->id = id;
+ il->next = *hd;
+ *hd = il;
+}
+
+static void
+freelist(Idlist **hd)
+{
+ Idlist *il;
+
+ for(il = *hd; il != nil && il->next != nil; il = il->next)
+ ;
+ if(il != nil){
+ il->next = frelist;
+ frelist = *hd;
+ *hd = nil;
+ }
+}
+
+Optab opflags[] = {
+ /* INOP */ None, 0,
+ /* IALT */ Unop, S(Mp)|D(W),
+ /* INBALT */ Unop, S(Mp)|D(W),
+ /* IGOTO */ Use2, S(W)|D(I),
+ /* ICALL */ Use2, S(P)|D(Pc),
+ /* IFRAME */ Unop, S(W)|D(P),
+ /* ISPAWN */ Use2, S(P)|D(Pc),
+ /* IRUNT */ None, 0,
+ /* ILOAD */ Threop, S(C)|M(P)|D(P),
+ /* IMCALL */ Use3, S(P)|M(W)|D(P),
+ /* IMSPAWN */ Use3, S(P)|M(W)|D(P),
+ /* IMFRAME */ Threop, S(P)|M(W)|D(P),
+ /* IRET */ None, 0,
+ /* IJMP */ Duse, D(Pc),
+ /* ICASE */ Use2, S(W)|D(I),
+ /* IEXIT */ None, 0,
+ /* INEW */ Unop, S(W)|D(P),
+ /* INEWA */ Threop, S(W)|M(W)|D(P),
+ /* INEWCB */ Cunop, M(W)|D(P),
+ /* INEWCW */ Cunop, M(W)|D(P),
+ /* INEWCF */ Cunop, M(W)|D(P),
+ /* INEWCP */ Cunop, M(W)|D(P),
+ /* INEWCM */ Threop, S(W)|M(W)|D(P),
+ /* INEWCMP */ Threop, S(W)|M(W)|D(P),
+ /* ISEND */ Use2, S(Mp)|D(P),
+ /* IRECV */ Unop, S(P)|D(Mp),
+ /* ICONSB */ Abinop, S(B)|D(P),
+ /* ICONSW */ Abinop, S(W)|D(P),
+ /* ICONSP */ Abinop, S(P)|D(P),
+ /* ICONSF */ Abinop, S(F)|D(P),
+ /* ICONSM */ Mabinop, S(Mp)|M(W)|D(P),
+ /* ICONSMP */ Mabinop, S(Mp)|M(W)|D(P),
+ /* IHEADB */ Unop, S(P)|D(B),
+ /* IHEADW */ Unop, S(P)|D(W),
+ /* IHEADP */ Unop, S(P)|D(P),
+ /* IHEADF */ Unop, S(P)|D(F),
+ /* IHEADM */ Threop, S(P)|M(W)|D(Mp),
+ /* IHEADMP */ Threop, S(P)|M(W)|D(Mp),
+ /* ITAIL */ Unop, S(P)|D(P),
+ /* ILEA */ Ddef, S(Mp)|D(P), /* S done specially cos of ALT */
+ /* IINDX */ Mbinop, S(P)|M(P)|D(W),
+ /* IMOVP */ Unop, S(P)|D(P),
+ /* IMOVM */ Threop, S(Mp)|M(W)|D(Mp),
+ /* IMOVMP */ Threop, S(Mp)|M(W)|D(Mp),
+ /* IMOVB */ Unop, Bop2,
+ /* IMOVW */ Unop, Wop2,
+ /* IMOVF */ Unop, Fop2,
+ /* ICVTBW */ Unop, S(B)|D(W),
+ /* ICVTWB */ Unop, S(W)|D(B),
+ /* ICVTFW */ Unop, S(F)|D(W),
+ /* ICVTWF */ Unop, S(W)|D(F),
+ /* ICVTCA */ Unop, S(C)|D(A),
+ /* ICVTAC */ Unop, S(A)|D(C),
+ /* ICVTWC */ Unop, S(W)|D(C),
+ /* ICVTCW */ Unop, S(C)|D(W),
+ /* ICVTFC */ Unop, S(F)|D(C),
+ /* ICVTCF */ Unop, S(C)|D(F),
+ /* IADDB */ Binop, Bop,
+ /* IADDW */ Binop, Wop,
+ /* IADDF */ Binop, Fop,
+ /* ISUBB */ Binop, Bop,
+ /* ISUBW */ Binop, Wop,
+ /* ISUBF */ Binop, Fop,
+ /* IMULB */ Binop, Bop,
+ /* IMULW */ Binop, Wop,
+ /* IMULF */ Binop, Fop,
+ /* IDIVB */ Binop, Bop,
+ /* IDIVW */ Binop, Wop,
+ /* IDIVF */ Binop, Fop,
+ /* IMODW */ Binop, Wop,
+ /* IMODB */ Binop, Bop,
+ /* IANDB */ Binop, Bop,
+ /* IANDW */ Binop, Wop,
+ /* IORB */ Binop, Bop,
+ /* IORW */ Binop, Wop,
+ /* IXORB */ Binop, Bop,
+ /* IXORW */ Binop, Wop,
+ /* ISHLB */ Binop, S(W)|M(B)|D(B),
+ /* ISHLW */ Binop, Wop,
+ /* ISHRB */ Binop, S(W)|M(B)|D(B),
+ /* ISHRW */ Binop, Wop,
+ /* IINSC */ Mabinop, S(W)|M(W)|D(C),
+ /* IINDC */ Threop, S(C)|M(W)|D(W),
+ /* IADDC */ Binop, Cop,
+ /* ILENC */ Unop, S(C)|D(W),
+ /* ILENA */ Unop, S(A)|D(W),
+ /* ILENL */ Unop, S(P)|D(W),
+ /* IBEQB */ Use3, Bopb,
+ /* IBNEB */ Use3, Bopb,
+ /* IBLTB */ Use3, Bopb,
+ /* IBLEB */ Use3, Bopb,
+ /* IBGTB */ Use3, Bopb,
+ /* IBGEB */ Use3, Bopb,
+ /* IBEQW */ Use3, Wopb,
+ /* IBNEW */ Use3, Wopb,
+ /* IBLTW */ Use3, Wopb,
+ /* IBLEW */ Use3, Wopb,
+ /* IBGTW */ Use3, Wopb,
+ /* IBGEW */ Use3, Wopb,
+ /* IBEQF */ Use3, Fopb,
+ /* IBNEF */ Use3, Fopb,
+ /* IBLTF */ Use3, Fopb,
+ /* IBLEF */ Use3, Fopb,
+ /* IBGTF */ Use3, Fopb,
+ /* IBGEF */ Use3, Fopb,
+ /* IBEQC */ Use3, Copb,
+ /* IBNEC */ Use3, Copb,
+ /* IBLTC */ Use3, Copb,
+ /* IBLEC */ Use3, Copb,
+ /* IBGTC */ Use3, Copb,
+ /* IBGEC */ Use3, Copb,
+ /* ISLICEA */ Mabinop, S(W)|M(W)|D(P),
+ /* ISLICELA */ Use3, S(P)|M(W)|D(P),
+ /* ISLICEC */ Mabinop, S(W)|M(W)|D(C),
+ /* IINDW */ Mbinop, S(P)|M(P)|D(W),
+ /* IINDF */ Mbinop, S(P)|M(P)|D(W),
+ /* IINDB */ Mbinop, S(P)|M(P)|D(W),
+ /* INEGF */ Unop, Fop2,
+ /* IMOVL */ Unop, Lop2,
+ /* IADDL */ Binop, Lop,
+ /* ISUBL */ Binop, Lop,
+ /* IDIVL */ Binop, Lop,
+ /* IMODL */ Binop, Lop,
+ /* IMULL */ Binop, Lop,
+ /* IANDL */ Binop, Lop,
+ /* IORL */ Binop, Lop,
+ /* IXORL */ Binop, Lop,
+ /* ISHLL */ Binop, S(W)|M(L)|D(L),
+ /* ISHRL */ Binop, S(W)|M(L)|D(L),
+ /* IBNEL */ Use3, Lopb,
+ /* IBLTL */ Use3, Lopb,
+ /* IBLEL */ Use3, Lopb,
+ /* IBGTL */ Use3, Lopb,
+ /* IBGEL */ Use3, Lopb,
+ /* IBEQL */ Use3, Lopb,
+ /* ICVTLF */ Unop, S(L)|D(F),
+ /* ICVTFL */ Unop, S(F)|D(L),
+ /* ICVTLW */ Unop, S(L)|D(W),
+ /* ICVTWL */ Unop, S(W)|D(L),
+ /* ICVTLC */ Unop, S(L)|D(C),
+ /* ICVTCL */ Unop, S(C)|D(L),
+ /* IHEADL */ Unop, S(P)|D(L),
+ /* ICONSL */ Abinop, S(L)|D(P),
+ /* INEWCL */ Cunop, M(W)|D(P),
+ /* ICASEC */ Use2, S(C)|D(I),
+ /* IINDL */ Mbinop, S(P)|M(P)|D(W),
+ /* IMOVPC */ Unop, S(W)|D(P),
+ /* ITCMP */ Use2, S(P)|D(P),
+ /* IMNEWZ */ Threop, S(P)|M(W)|D(P),
+ /* ICVTRF */ Unop, S(R)|D(F),
+ /* ICVTFR */ Unop, S(F)|D(R),
+ /* ICVTWS */ Unop, S(W)|D(Sh),
+ /* ICVTSW */ Unop, S(Sh)|D(W),
+ /* ILSRW */ Binop, Wop,
+ /* ILSRL */ Binop, S(W)|M(L)|D(L),
+ /* IECLR */ None, 0,
+ /* INEWZ */ Unop, S(W)|D(P),
+ /* INEWAZ */ Threop, S(W)|M(W)|D(P),
+ /* IRAISE */ Use1, S(P),
+ /* ICASEL */ Use2, S(L)|D(I),
+ /* IMULX */ Binop|Tuse2, Xop,
+ /* IDIVX */ Binop|Tuse2, Xop,
+ /* ICVTXX */ Threop, Xop,
+ /* IMULX0 */ Binop|Tuse1|Tuse2, Xop,
+ /* IDIVX0 */ Binop|Tuse1|Tuse2, Xop,
+ /* ICVTXX0 */ Threop|Tuse1, Xop,
+ /* IMULX1 */ Binop|Tuse1|Tuse2, Xop,
+ /* IDIVX1 */ Binop|Tuse1|Tuse2, Xop,
+ /* ICVTXX1 */ Threop|Tuse1, Xop,
+ /* ICVTFX */ Threop, S(F)|M(F)|D(X),
+ /* ICVTXF */ Threop, S(X)|M(F)|D(F),
+ /* IEXPW */ Binop, S(W)|M(W)|D(W),
+ /* IEXPL */ Binop, S(W)|M(L)|D(L),
+ /* IEXPF */ Binop, S(W)|M(F)|D(F),
+ /* ISELF */ Ddef, D(P),
+ /* IEXC */ None, 0,
+ /* IEXC0 */ None, 0,
+ /* INOOP */ None, 0,
+};
+
+/*
+static int
+pop(int i)
+{
+ i = (i & 0x55555555) + ((i>>1) & 0x55555555);
+ i = (i & 0x33333333) + ((i>>2) & 0x33333333);
+ i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
+ i = (i & 0x00FF00FF) + ((i>>8) & 0x00FF00FF);
+ i = (i & 0x0000FFFF) + ((i>>16) & 0x0000FFFF);
+ return i;
+}
+*/
+
+static int
+bitc(uint x)
+{
+ uint n;
+
+ n = (x>>1)&0x77777777;
+ x -= n;
+ n = (n>>1)&0x77777777;
+ x -= n;
+ n = (n>>1)&0x77777777;
+ x -= n;
+ x = (x+(x>>4))&0x0f0f0f0f;
+ x *= 0x01010101;
+ return x>>24;
+}
+
+/*
+static int
+top(uint x)
+{
+ int i;
+
+ for(i = -1; x; i++)
+ x >>= 1;
+ return i;
+}
+*/
+
+static int
+topb(uint x)
+{
+ int i;
+
+ if(x == 0)
+ return -1;
+ i = 0;
+ if(x&0xffff0000){
+ i |= 16;
+ x >>= 16;
+ }
+ if(x&0xff00){
+ i |= 8;
+ x >>= 8;
+ }
+ if(x&0xf0){
+ i |= 4;
+ x >>= 4;
+ }
+ if(x&0xc){
+ i |= 2;
+ x >>= 2;
+ }
+ if(x&0x2)
+ i |= 1;
+ return i;
+}
+
+/*
+static int
+lowb(uint x)
+{
+ int i;
+
+ if(x == 0)
+ return -1;
+ for(i = BLEN; x; i--)
+ x <<= 1;
+ return i;
+}
+*/
+
+static int
+lowb(uint x)
+{
+ int i;
+
+ if(x == 0)
+ return -1;
+ i = 0;
+ if((x&0xffff) == 0){
+ i |= 16;
+ x >>= 16;
+ }
+ if((x&0xff) == 0){
+ i |= 8;
+ x >>= 8;
+ }
+ if((x&0xf) == 0){
+ i |= 4;
+ x >>= 4;
+ }
+ if((x&0x3) == 0){
+ i |= 2;
+ x >>= 2;
+ }
+ return i+1-(x&1);
+}
+
+static void
+pbit(int x, int n)
+{
+ int i, m;
+
+ m = 1;
+ for(i = 0; i < BLEN; i++){
+ if(x&m)
+ print("%d ", i+n);
+ m <<= 1;
+ }
+}
+
+static ulong
+bop(int o, ulong s, ulong d)
+{
+ switch(o){
+ case Bclr: return 0;
+ case Band: return s & d;
+ case Bandinv: return s & ~d;
+ case Bstore: return s;
+ case Bandrev: return ~s & d;
+ case Bnoop: return d;
+ case Bxor: return s ^ d;
+ case Bor: return s | d;
+ case Bnor: return ~(s | d);
+ case Bequiv: return ~(s ^ d);
+ case Binv: return ~d;
+ case Bimpby: return s | ~d;
+ case Brev: return ~s;
+ case Bimp: return ~s | d;
+ case Bnand: return ~(s & d);
+ case Bset: return 0xffffffff;
+ }
+ return 0;
+}
+
+static Bits
+bnew(int n, int bits)
+{
+ Bits b;
+
+ if(bits)
+ b.n = (n+BLEN-1)>>BSHIFT;
+ else
+ b.n = n;
+ b.b = allocmem(b.n*sizeof(ulong));
+ memset(b.b, 0, b.n*sizeof(ulong));
+ return b;
+}
+
+static void
+bfree(Bits b)
+{
+ free(b.b);
+}
+
+static void
+bset(Bits b, int n)
+{
+ b.b[n>>BSHIFT] |= 1<<(n&BMASK);
+}
+
+static void
+bclr(Bits b, int n)
+{
+ b.b[n>>BSHIFT] &= ~(1<<(n&BMASK));
+}
+
+static int
+bmem(Bits b, int n)
+{
+ return b.b[n>>BSHIFT] & (1<<(n&BMASK));
+}
+
+static void
+bsets(Bits b, int m, int n)
+{
+ int i, c1, c2;
+
+ c1 = m>>BSHIFT;
+ c2 = n>>BSHIFT;
+ m &= BMASK;
+ n &= BMASK;
+ if(c1 == c2){
+ b.b[c1] |= MASK(m, n);
+ return;
+ }
+ for(i = c1+1; i < c2; i++)
+ b.b[i] = 0xffffffff;
+ b.b[c1] |= MASK(m, BLEN-1);
+ b.b[c2] |= MASK(0, n);
+}
+
+static void
+bclrs(Bits b, int m, int n)
+{
+ int i, c1, c2;
+
+ if(n < 0)
+ n = (b.n<<BSHIFT)-1;
+ c1 = m>>BSHIFT;
+ c2 = n>>BSHIFT;
+ m &= BMASK;
+ n &= BMASK;
+ if(c1 == c2){
+ b.b[c1] &= ~MASK(m, n);
+ return;
+ }
+ for(i = c1+1; i < c2; i++)
+ b.b[i] = 0;
+ b.b[c1] &= ~MASK(m, BLEN-1);
+ b.b[c2] &= ~MASK(0, n);
+}
+
+/* b = a op b */
+static Bits
+boper(int o, Bits a, Bits b)
+{
+ int i, n;
+
+ n = a.n;
+ if(b.n != n)
+ fatal("boper %d %d %d", o, a.n, b.n);
+ for(i = 0; i < n; i++)
+ b.b[i] = bop(o, a.b[i], b.b[i]);
+ return b;
+}
+
+static int
+beq(Bits a, Bits b)
+{
+ int i, n;
+
+ n = a.n;
+ for(i = 0; i < n; i++)
+ if(a.b[i] != b.b[i])
+ return 0;
+ return 1;
+}
+
+static int
+bzero(Bits b)
+{
+ int i, n;
+
+ n = b.n;
+ for(i = 0; i < n; i++)
+ if(b.b[i] != 0)
+ return 0;
+ return 1;
+}
+
+static int
+bitcnt(Bits b)
+{
+ int i, m, n;
+
+ m = b.n;
+ n = 0;
+ for(i = 0; i < m; i++)
+ n += bitc(b.b[i]);
+ return n;
+}
+
+static int
+topbit(Bits b)
+{
+ int i, n;
+
+ n = b.n;
+ for(i = n-1; i >= 0; i--)
+ if(b.b[i] != 0)
+ return (i<<BSHIFT)+topb(b.b[i]);
+ return -1;
+}
+
+static int
+lowbit(Bits b)
+{
+ int i, n;
+
+ n = b.n;
+ for(i = 0; i < n; i++)
+ if(b.b[i] != 0)
+ return (i<<BSHIFT)+lowb(b.b[i]);
+ return -1;
+}
+
+static void
+pbits(Bits b)
+{
+ int i, n;
+
+ n = b.n;
+ for(i = 0; i < n; i++)
+ pbit(b.b[i], i<<BSHIFT);
+}
+
+static char*
+decname(Decl *d)
+{
+ if(d->sym == nil)
+ return "<nil>";
+ return d->sym->name;
+}
+
+static void
+warning(Inst *i, char *s, Decl *d, Decl *sd)
+{
+ int n;
+ char *f;
+ Decl *ds;
+
+ n = 0;
+ for(ds = sd; ds != nil; ds = ds->next)
+ if(ds->link == d)
+ n += strlen(ds->sym->name)+1;
+ if(n == 0){
+ warn(i->src.start, "%s: %s", d->sym->name, s);
+ return;
+ }
+ n += strlen(d->sym->name);
+ f = malloc(n+1);
+ strcpy(f, d->sym->name);
+ for(ds = sd; ds != nil; ds = ds->next){
+ if(ds->link == d){
+ strcat(f, "/");
+ strcat(f, ds->sym->name);
+ }
+ }
+ warn(i->src.start, "%s: %s", f, s);
+ free(f);
+}
+
+static int
+inspc(Inst *in)
+{
+ int n;
+ Inst *i;
+
+ n = 0;
+ for(i = in; i != nil; i = i->next)
+ i->pc = n++;
+ return n;
+}
+
+static Inst*
+pc2i(Block *b, int pc)
+{
+ Inst *i;
+
+ for( ; b != nil; b = b->next){
+ if(pc > b->last->pc)
+ continue;
+ for(i = b->first; ; i = i->next){
+ if(i->pc == pc)
+ return i;
+ if(i == b->last)
+ fatal("pc2i a");
+ }
+ }
+ fatal("pc2i b");
+ return nil;
+}
+
+static void
+padr(int am, Addr *a, Inst *br)
+{
+ long reg;
+
+ if(br != nil){
+ print("$%ld", br->pc);
+ return;
+ }
+ reg = a->reg;
+ if(a->decl != nil && am != Adesc)
+ reg += a->decl->offset;
+ switch(am){
+ case Anone:
+ print("-");
+ break;
+ case Aimm:
+ case Apc:
+ case Adesc:
+ print("$%ld", a->offset);
+ break;
+ case Aoff:
+ print("$%ld", a->decl->iface->offset);
+ break;
+ case Anoff:
+ print("-$%ld", a->decl->iface->offset);
+ break;
+ case Afp:
+ print("%ld(fp)", reg);
+ break;
+ case Afpind:
+ print("%ld(%ld(fp))", a->offset, reg);
+ break;
+ case Amp:
+ print("%ld(mp)", reg);
+ break;
+ case Ampind:
+ print("%ld(%ld(mp))", a->offset, reg);
+ break;
+ case Aldt:
+ print("$%ld", reg);
+ break;
+ case Aerr:
+ default:
+ print("%ld(%ld(?%d?))", a->offset, reg, am);
+ break;
+ }
+}
+
+static void
+pins(Inst *i)
+{
+ /* print("%L %ld ", i->src.start, i->pc); */
+ print(" %ld ", i->pc);
+ if(i->op < MAXDIS)
+ print("%s", instname[i->op]);
+ else
+ print("noop");
+ print(" ");
+ padr(i->sm, &i->s, nil);
+ print(", ");
+ padr(i->mm, &i->m, nil);
+ print(", ");
+ padr(i->dm, &i->d, i->branch);
+ print("\n");
+}
+
+static void
+blfree(Blist *bl)
+{
+ Blist *nbl;
+
+ for( ; bl != nil; bl = nbl){
+ nbl = bl->next;
+ free(bl);
+ }
+}
+
+static void
+freebits(Bits *bs, int nv)
+{
+ int i;
+
+ for(i = 0; i < nv; i++)
+ bfree(bs[i]);
+ free(bs);
+}
+
+static void
+freeblks(Block *b)
+{
+ Block *nb;
+
+ for( ; b != nil; b = nb){
+ blfree(b->pred);
+ blfree(b->succ);
+ bfree(b->kill);
+ bfree(b->gen);
+ bfree(b->in);
+ bfree(b->out);
+ nb = b->next;
+ free(b);
+ }
+}
+
+static int
+len(Decl *d)
+{
+ int n;
+
+ n = 0;
+ for( ; d != nil; d = d->next)
+ n++;
+ return n;
+}
+
+static Bits*
+allocbits(int nv, int npc)
+{
+ int i;
+ Bits *defs;
+
+ defs = (Bits*)allocmem(nv*sizeof(Bits));
+ for(i = 0; i < nv; i++)
+ defs[i] = bnew(npc, 1);
+ return defs;
+}
+
+static int
+bitcount(Bits *bs, int nv)
+{
+ int i, n;
+
+ n = 0;
+ for(i = 0; i < nv; i++)
+ n += bitcnt(bs[i]);
+ return n;
+}
+
+static Block*
+mkblock(Inst *i)
+{
+ Block *b;
+
+ b = allocmem(sizeof(Block));
+ *b = zblock;
+ b->first = b->last = i;
+ return b;
+}
+
+static Blist*
+mkblist(Block *b, Blist *nbl)
+{
+ Blist *bl;
+
+ bl = allocmem(sizeof(Blist));
+ bl->block = b;
+ bl->next = nbl;
+ return bl;
+}
+
+static void
+leader(Inst *i, Array *ab)
+{
+ int m, n;
+ Block *b, **a;
+
+ if(i != nil && i->pc == 0){
+ if((n = ab->n) == (m = ab->m)){
+ a = ab->a;
+ ab->a = allocmem(2*m*sizeof(Block*));
+ memcpy(ab->a, a, m*sizeof(Block*));
+ ab->m = 2*m;
+ free(a);
+ }
+ b = mkblock(i);
+ b->dfn = n;
+ ab->a[n] = b;
+ i->pc = ab->n = n+1;
+ }
+}
+
+static Block*
+findb(Inst *i, Array *ab)
+{
+ if(i == nil)
+ return nil;
+ if(i->pc <= 0)
+ fatal("pc <= 0 in findb");
+ return ab->a[i->pc-1];
+}
+
+static int
+memb(Block *b, Blist *bl)
+{
+ for( ; bl != nil; bl = bl->next)
+ if(bl->block == b)
+ return 1;
+ return 0;
+}
+
+static int
+canfallthrough(Inst *i)
+{
+ if(i == nil)
+ return 0;
+ switch(i->op){
+ case IGOTO:
+ case ICASE:
+ case ICASEL:
+ case ICASEC:
+ case IRET:
+ case IEXIT:
+ case IRAISE:
+ case IJMP:
+ return 0;
+ case INOOP:
+ return i->branch != nil;
+ }
+ return 1;
+}
+
+static void
+predsucc(Block *b1, Block *b2)
+{
+ if(b1 == nil || b2 == nil)
+ return;
+ if(!memb(b1, b2->pred))
+ b2->pred = mkblist(b1, b2->pred);
+ if(!memb(b2, b1->succ))
+ b1->succ = mkblist(b2, b1->succ);
+}
+
+static Block*
+mkblocks(Inst *in, int *nb)
+{
+ Inst *i;
+ Block *b, *firstb, *lastb;
+ Label *lab;
+ Array *ab;
+ int j, n;
+
+ ab = allocmem(sizeof(Array));
+ ab->n = 0;
+ ab->m = 16;
+ ab->a = allocmem(ab->m*sizeof(Block*));
+ leader(in, ab);
+ for(i = in; i != nil; i = i->next){
+ switch(i->op){
+ case IGOTO:
+ case ICASE:
+ case ICASEL:
+ case ICASEC:
+ case INOOP:
+ if(i->op == INOOP && i->branch != nil){
+ leader(i->branch, ab);
+ leader(i->next, ab);
+ break;
+ }
+ leader(i->d.decl->ty->cse->iwild, ab);
+ lab = i->d.decl->ty->cse->labs;
+ n = i->d.decl->ty->cse->nlab;
+ for(j = 0; j < n; j++)
+ leader(lab[j].inst, ab);
+ leader(i->next, ab);
+ break;
+ case IRET:
+ case IEXIT:
+ case IRAISE:
+ leader(i->next, ab);
+ break;
+ case IJMP:
+ leader(i->branch, ab);
+ leader(i->next, ab);
+ break;
+ default:
+ if(i->branch != nil){
+ leader(i->branch, ab);
+ leader(i->next, ab);
+ }
+ break;
+ }
+ }
+ firstb = lastb = mkblock(nil);
+ for(i = in; i != nil; i = i->next){
+ if(i->pc != 0){
+ b = findb(i, ab);
+ b->prev = lastb;
+ lastb->next = b;
+ if(canfallthrough(lastb->last))
+ predsucc(lastb, b);
+ lastb = b;
+ }
+ else
+ lastb->last = i;
+ switch(i->op){
+ case IGOTO:
+ case ICASE:
+ case ICASEL:
+ case ICASEC:
+ case INOOP:
+ if(i->op == INOOP && i->branch != nil){
+ b = findb(i->next, ab);
+ predsucc(lastb, b);
+ b = findb(i->branch, ab);
+ predsucc(lastb, b);
+ break;
+ }
+ b = findb(i->d.decl->ty->cse->iwild, ab);
+ predsucc(lastb, b);
+ lab = i->d.decl->ty->cse->labs;
+ n = i->d.decl->ty->cse->nlab;
+ for(j = 0; j < n; j++){
+ b = findb(lab[j].inst, ab);
+ predsucc(lastb, b);
+ }
+ break;
+ case IRET:
+ case IEXIT:
+ case IRAISE:
+ break;
+ case IJMP:
+ b = findb(i->branch, ab);
+ predsucc(lastb, b);
+ break;
+ default:
+ if(i->branch != nil){
+ b = findb(i->next, ab);
+ predsucc(lastb, b);
+ b = findb(i->branch, ab);
+ predsucc(lastb, b);
+ }
+ break;
+ }
+ }
+ *nb = ab->n;
+ free(ab->a);
+ free(ab);
+ b = firstb->next;
+ b->prev = nil;
+ return b;
+}
+
+static int
+back(Block *b1, Block *b2)
+{
+ return b1->dfn >= b2->dfn;
+}
+
+static void
+pblocks(Block *b, int nb)
+{
+ Inst *i;
+ Blist *bl;
+
+ print("--------------------%d blocks--------------------\n", nb);
+ print("------------------------------------------------\n");
+ for( ; b != nil; b = b->next){
+ print("dfn=%d\n", b->dfn);
+ print(" pred ");
+ for(bl = b->pred; bl != nil; bl = bl->next)
+ print("%d%s ", bl->block->dfn, back(bl->block, b) ? "*" : "");
+ print("\n");
+ print(" succ ");
+ for(bl = b->succ; bl != nil; bl = bl->next)
+ print("%d%s ", bl->block->dfn, back(b, bl->block) ? "*" : "");
+ print("\n");
+ for(i = b->first; i != nil; i = i->next){
+ // print(" %I\n", i);
+ pins(i);
+ if(i == b->last)
+ break;
+ }
+ }
+ print("------------------------------------------------\n");
+}
+
+static void
+ckblocks(Inst *in, Block *b, int nb)
+{
+ int n;
+ Block *lastb;
+
+ if(b->first != in)
+ fatal("A - %d", b->dfn);
+ n = 0;
+ lastb = nil;
+ for( ; b != nil; b = b->next){
+ n++;
+ if(b->prev != lastb)
+ fatal("a - %d\n", b->dfn);
+ if(b->prev != nil && b->prev->next != b)
+ fatal("b - %d\n", b->dfn);
+ if(b->next != nil && b->next->prev != b)
+ fatal("c - %d\n", b->dfn);
+
+ if(b->prev != nil && b->prev->last->next != b->first)
+ fatal("B - %d\n", b->dfn);
+ if(b->next != nil && b->last->next != b->next->first)
+ fatal("C - %d\n", b->dfn);
+ if(b->next == nil && b->last->next != nil)
+ fatal("D - %d\n", b->dfn);
+
+ if(b->last->branch != nil && b->succ->block->first != b->last->branch)
+ fatal("0 - %d\n", b->dfn);
+
+ lastb = b;
+ }
+ if(n != nb)
+ fatal("N - %d %d\n", n, nb);
+}
+
+static void
+dfs0(Block *b, int *n)
+{
+ Block *s;
+ Blist *bl;
+
+ b->flags = 1;
+ for(bl = b->succ; bl != nil; bl = bl->next){
+ s = bl->block;
+ if(s->flags == 0)
+ dfs0(s, n);
+ }
+ b->dfn = --(*n);
+}
+
+static int
+dfs(Block *b, int nb)
+{
+ int n, u;
+ Block *b0;
+
+ b0 = b;
+ n = nb;
+ dfs0(b0, &n);
+ u = 0;
+ for(b = b0; b != nil; b = b->next){
+ if(b->flags == 0){ /* unreachable: see foldbranch */
+ fatal("found unreachable code");
+ u++;
+ b->prev->next = b->next;
+ if(b->next){
+ b->next->prev = b->prev;
+ b->prev->last->next = b->next->first;
+ }
+ else
+ b->prev->last->next = nil;
+ }
+ b->flags = 0;
+ }
+ if(u){
+ for(b = b0; b != nil; b = b->next)
+ b->dfn -= u;
+ }
+ return nb-u;
+}
+
+static void
+loop0(Block *b)
+{
+ Block *p;
+ Blist *bl;
+
+ b->flags = 1;
+ for(bl = b->pred; bl != nil; bl = bl->next){
+ p = bl->block;
+ if(p->flags == 0)
+ loop0(p);
+ }
+}
+
+/* b1->b2 a back edge */
+static void
+loop(Block *b, Block *b1, Block *b2)
+{
+ if(0 && debug['o'])
+ print("back edge %d->%d\n", b1->dfn, b2->dfn);
+ b2->flags = 1;
+ if(b1->flags == 0)
+ loop0(b1);
+ if(0 && debug['o'])
+ print(" loop ");
+ for( ; b != nil; b = b->next){
+ if(b->flags && 0 && debug['o'])
+ print("%d ", b->dfn);
+ b->flags = 0;
+ }
+ if(0 && debug['o'])
+ print("\n");
+}
+
+static void
+loops(Block *b)
+{
+ Block *b0;
+ Blist *bl;
+
+ b0 = b;
+ for( ; b != nil; b = b->next){
+ for(bl = b->succ; bl != nil; bl = bl->next){
+ if(back(b, bl->block))
+ loop(b0, b, bl->block);
+ }
+ }
+}
+
+static int
+imm(int m, Addr *a)
+{
+ if(m == Aimm)
+ return a->offset;
+ fatal("bad immediate value");
+ return -1;
+}
+
+static int
+desc(int m, Addr *a)
+{
+ if(m == Adesc)
+ return a->decl->desc->size;
+ fatal("bad descriptor value");
+ return -1;
+}
+
+static int
+fpoff(int m, Addr *a)
+{
+ int off;
+ Decl *d;
+
+ if(m == Afp || m == Afpind){
+ off = a->reg;
+ if((d = a->decl) != nil)
+ off += d->offset;
+ return off;
+ }
+ return -1;
+}
+
+static int
+size(Inst *i)
+{
+ switch(i->op){
+ case ISEND:
+ case IRECV:
+ case IALT:
+ case INBALT:
+ case ILEA:
+ return i->m.offset;
+ case IMOVM:
+ case IHEADM:
+ case ICONSM:
+ return imm(i->mm, &i->m);
+ case IMOVMP:
+ case IHEADMP:
+ case ICONSMP:
+ return desc(i->mm, &i->m);
+ break;
+ }
+ fatal("bad op in size");
+ return -1;
+}
+
+static Decl*
+mkdec(int o)
+{
+ Decl *d;
+
+ d = mkdecl(&nosrc, Dlocal, tint);
+ d->offset = o;
+ return d;
+}
+
+static void
+mkdecls(void)
+{
+ regdecls = mkdec(REGRET*IBY2WD);
+ regdecls->next = mkdec(STemp);
+ regdecls->next->next = mkdec(DTemp);
+}
+
+static Decl*
+sharedecls(Decl *d)
+{
+ Decl *ld;
+
+ ld = d;
+ for(d = d->next ; d != nil; d = d->next){
+ if(d->offset <= ld->offset)
+ break;
+ ld = d;
+ }
+ return d;
+}
+
+static int
+finddec(int o, int s, Decl *vars, int *nv, Inst *i)
+{
+ int m, n;
+ Decl *d;
+
+ n = 0;
+ for(d = vars; d != nil; d = d->next){
+ if(o >= d->offset && o < d->offset+d->ty->size){
+ m = 1;
+ while(o+s > d->offset+d->ty->size){
+ m++;
+ d = d->next;
+ }
+ *nv = m;
+ return n;
+ }
+ n++;
+ }
+ // print("%d %d missing\n", o, s);
+ pins(i);
+ fatal("missing decl");
+ return -1;
+}
+
+static void
+setud(Bits *b, int id, int n, int pc)
+{
+ if(id < 0)
+ return;
+ while(--n >= 0)
+ bset(b[id++], pc);
+}
+
+static void
+ud(Inst *i, Decl *vars, Bits *uses, Bits *defs)
+{
+ ushort f;
+ int id, j, nv, pc, sz, s, m, d, ss, sm, sd;
+ Optab *t;
+ Idlist *l;
+
+ pc = i->pc;
+ ss = 0;
+ t = &opflags[i->op];
+ f = t->flags;
+ sz = t->size;
+ s = fpoff(i->sm, &i->s);
+ m = fpoff(i->mm, &i->m);
+ d = fpoff(i->dm, &i->d);
+ if(f&Mduse && i->mm == Anone)
+ f |= Duse;
+ if(s >= 0){
+ if(i->sm == Afp){
+ ss = SS(sz);
+ if(ss == Mp)
+ ss = size(i);
+ }
+ else
+ ss = IBY2WD;
+ id = finddec(s, ss, vars, &nv, i);
+ if(f&Suse)
+ setud(uses, id, nv, pc);
+ if(f&Sdef){
+ if(i->sm == Afp)
+ setud(defs, id, nv, pc);
+ else
+ setud(uses, id, nv, pc);
+ }
+ }
+ if(m >= 0){
+ if(i->mm == Afp){
+ sm = SM(sz);
+ if(sm == Mp)
+ sm = size(i);
+ }
+ else
+ sm = IBY2WD;
+ id = finddec(m, sm, vars, &nv, i);
+ if(f&Muse)
+ setud(uses, id, nv, pc);
+ if(f&Mdef){
+ if(i->mm == Afp)
+ setud(defs, id, nv, pc);
+ else
+ setud(uses, id, nv, pc);
+ }
+ }
+ if(d >= 0){
+ if(i->dm == Afp){
+ sd = SD(sz);
+ if(sd == Mp)
+ sd = size(i);
+ }
+ else
+ sd = IBY2WD;
+ id = finddec(d, sd, vars, &nv, i);
+ if(f&Duse)
+ setud(uses, id, nv, pc);
+ if(f&Ddef){
+ if(i->dm == Afp)
+ setud(defs, id, nv, pc);
+ else
+ setud(uses, id, nv, pc);
+ }
+ }
+ if(f&Tuse1){
+ id = finddec(STemp, IBY2WD, vars, &nv, i);
+ setud(uses, id, nv, pc);
+ }
+ if(f&Tuse2){
+ id = finddec(DTemp, IBY2WD, vars, &nv, i);
+ setud(uses, id, nv, pc);
+ }
+ if(i->op == ILEA){
+ if(s >= 0){
+ id = finddec(s, ss, vars, &nv, i);
+ if(i->sm == Afp && i->m.reg == 0)
+ setud(defs, id, nv, pc);
+ else
+ setud(uses, id, nv, pc);
+ }
+ }
+ if(0)
+ switch(i->op){
+ case ILEA:
+ if(s >= 0){
+ id = finddec(s, ss, vars, &nv, i);
+ if(id < 0)
+ break;
+ for(j = 0; j < nv; j++){
+ if(i->sm == Afp && i->m.reg == 0)
+ addlist(&deflist, id++);
+ else
+ addlist(&uselist, id++);
+ }
+ }
+ break;
+ case IALT:
+ case INBALT:
+ case ICALL:
+ case IMCALL:
+ for(l = deflist; l != nil; l = l->next){
+ id = l->id;
+ bset(defs[id], pc);
+ }
+ for(l = uselist; l != nil; l = l->next){
+ id = l->id;
+ bset(uses[id], pc);
+ }
+ freelist(&deflist);
+ freelist(&uselist);
+ break;
+ }
+}
+
+static void
+usedef(Inst *in, Decl *vars, Bits *uses, Bits *defs)
+{
+ Inst *i;
+
+ for(i = in; i != nil; i = i->next)
+ ud(i, vars, uses, defs);
+}
+
+static void
+pusedef(Bits *ud, int nv, Decl *d, char *s)
+{
+ int i;
+
+ print("%s\n", s);
+ for(i = 0; i < nv; i++){
+ if(!bzero(ud[i])){
+ print("\t%s(%ld): ", decname(d), d->offset);
+ pbits(ud[i]);
+ print("\n");
+ }
+ d = d->next;
+ }
+}
+
+static void
+dummydefs(Bits *defs, int nv, int npc)
+{
+ int i;
+
+ for(i = 0; i < nv; i++)
+ bset(defs[i], npc++);
+}
+
+static void
+dogenkill(Block *b, Bits *defs, int nv)
+{
+ int i, n, t;
+ Bits v;
+
+ n = defs[0].n;
+ v = bnew(n, 0);
+ for( ; b != nil; b = b->next){
+ b->gen = bnew(n, 0);
+ b->kill = bnew(n, 0);
+ b->in = bnew(n, 0);
+ b->out = bnew(n, 0);
+ for(i = 0; i < nv; i++){
+ boper(Bclr, v, v);
+ bsets(v, b->first->pc, b->last->pc);
+ boper(Band, defs[i], v);
+ t = topbit(v);
+ if(t >= 0)
+ bset(b->gen, t);
+ else
+ continue;
+ boper(Bclr, v, v);
+ bsets(v, b->first->pc, b->last->pc);
+ boper(Binv, v, v);
+ boper(Band, defs[i], v);
+ boper(Bor, v, b->kill);
+ }
+ }
+ bfree(v);
+}
+
+static void
+udflow(Block *b, int nv, int npc)
+{
+ int iter;
+ Block *b0, *p;
+ Blist *bl;
+ Bits newin;
+
+ b0 = b;
+ for(b = b0; b != nil; b = b->next)
+ boper(Bstore, b->gen, b->out);
+ newin = bnew(b0->in.n, 0);
+ iter = 1;
+ while(iter){
+ iter = 0;
+ for(b = b0; b != nil; b = b->next){
+ boper(Bclr, newin, newin);
+ for(bl = b->pred; bl != nil; bl = bl->next){
+ p = bl->block;
+ boper(Bor, p->out, newin);
+ }
+ if(b == b0)
+ bsets(newin, npc, npc+nv-1);
+ if(!beq(b->in, newin))
+ iter = 1;
+ boper(Bstore, newin, b->in);
+ boper(Bstore, b->in, b->out);
+ boper(Bandrev, b->kill, b->out);
+ boper(Bor, b->gen, b->out);
+ }
+ }
+ bfree(newin);
+}
+
+static void
+pflows(Block *b)
+{
+ for( ; b != nil; b = b->next){
+ print("block %d\n", b->dfn);
+ print(" gen: "); pbits(b->gen); print("\n");
+ print(" kill: "); pbits(b->kill); print("\n");
+ print(" in: "); pbits(b->in); print("\n");
+ print(" out: "); pbits(b->out); print("\n");
+ }
+}
+
+static int
+set(Decl *d)
+{
+ if(d->store == Darg)
+ return 1;
+ if(d->sym == nil) /* || d->sym->name[0] == '.') */
+ return 1;
+ if(tattr[d->ty->kind].isptr || d->ty->kind == Texception)
+ return 1;
+ return 0;
+}
+
+static int
+used(Decl *d)
+{
+ if(d->sym == nil ) /* || d->sym->name[0] == '.') */
+ return 1;
+ return 0;
+}
+
+static void
+udchain(Block *b, Decl *ds, int nv, int npc, Bits *defs, Bits *uses, Decl *sd)
+{
+ int i, n, p, q;
+ Bits d, u, dd, ud;
+ Block *b0;
+ Inst *in;
+
+ b0 = b;
+ n = defs[0].n;
+ u = bnew(n, 0);
+ d = bnew(n, 0);
+ dd = bnew(n, 0);
+ ud = bnew(n, 0);
+ for(i = 0; i < nv; i++){
+ boper(Bstore, defs[i], ud);
+ bclr(ud, npc+i);
+ for(b = b0 ; b != nil; b = b->next){
+ boper(Bclr, u, u);
+ bsets(u, b->first->pc, b->last->pc);
+ boper(Band, uses[i], u);
+ boper(Bclr, d, d);
+ bsets(d, b->first->pc, b->last->pc);
+ boper(Band, defs[i], d);
+ for(;;){
+ p = topbit(u);
+ if(p < 0)
+ break;
+ bclr(u, p);
+ bclrs(d, p, -1);
+ q = topbit(d);
+ if(q >= 0){
+ bclr(ud, q);
+ if(debug['o'])
+ print("udc b=%d v=%d(%s/%ld) u=%d d=%d\n", b->dfn, i, decname(ds), ds->offset, p, q);
+ }
+ else{
+ boper(Bstore, defs[i], dd);
+ boper(Band, b->in, dd);
+ boper(Bandrev, dd, ud);
+ if(!bzero(dd)){
+ if(debug['o']){
+ print("udc b=%d v=%d(%s/%ld) u=%d d=", b->dfn, i, decname(ds), ds->offset, p);
+ pbits(dd);
+ print("\n");
+ }
+ if(bmem(dd, npc+i) && !set(ds))
+ warning(pc2i(b0, p), "used and not set", ds, sd);
+ }
+ else
+ fatal("no defs in udchain");
+ }
+ }
+ }
+ for(;;){
+ p = topbit(ud);
+ if(p < 0)
+ break;
+ bclr(ud, p);
+ if(!used(ds)){
+ in = pc2i(b0, p);
+ if(isnilsrc(&in->src)) /* nilling code */
+ in->op = INOOP; /* elim p from bitmaps ? */
+ else if(limbovar(ds) && !structure(ds->ty))
+ warning(in, "set and not used", ds, sd);
+ }
+ }
+ ds = ds->next;
+ }
+ bfree(u);
+ bfree(d);
+ bfree(dd);
+ bfree(ud);
+}
+
+static void
+ckflags(void)
+{
+ int i, j, k, n;
+ Optab *o;
+
+ n = nelem(opflags);
+ o = opflags;
+ for(i = 0; i < n; i++){
+ j = (o->flags&(Suse|Sdef)) != 0;
+ k = SS(o->size) != 0;
+ if(j != k){
+ if(!(j == 0 && k == 1 && i == ILEA))
+ fatal("S %ld %s\n", o-opflags, instname[i]);
+ }
+ j = (o->flags&(Muse|Mdef)) != 0;
+ k = SM(o->size) != 0;
+ if(j != k)
+ fatal("M %ld %s\n", o-opflags, instname[i]);
+ j = (o->flags&(Duse|Ddef)) != 0;
+ k = SD(o->size) != 0;
+ if(j != k){
+ if(!(j == 1 && k == 0 && (i == IGOTO || i == ICASE || i == ICASEC || i == ICASEL)))
+ fatal("D %ld %s\n", o-opflags, instname[i]);
+ }
+ o++;
+ }
+}
+
+void
+optim(Inst *in, Decl *d)
+{
+ int nb, npc, nv, nd, nu;
+ Block *b;
+ Bits *uses, *defs;
+ Decl *sd;
+
+ ckflags();
+ if(debug['o'])
+ print("************************************************\nfunction %s\n************************************************\n", d->sym->name);
+ if(in == nil || errors > 0)
+ return;
+ d = d->ty->ids;
+ if(regdecls == nil)
+ mkdecls();
+ regdecls->next->next->next = d;
+ d = regdecls;
+ sd = sharedecls(d);
+ if(debug['o'])
+ printdecls(d);
+ b = mkblocks(in, &nb);
+ ckblocks(in, b, nb);
+ npc = inspc(in);
+ nb = dfs(b, nb);
+ if(debug['o'])
+ pblocks(b, nb);
+ loops(b);
+ nv = len(d);
+ uses = allocbits(nv, npc+nv);
+ defs = allocbits(nv, npc+nv);
+ dummydefs(defs, nv, npc);
+ usedef(in, d, uses, defs);
+ if(debug['o']){
+ pusedef(uses, nv, d, "uses");
+ pusedef(defs, nv, d, "defs");
+ }
+ nu = bitcount(uses, nv);
+ nd = bitcount(defs, nv);
+ dogenkill(b, defs, nv);
+ udflow(b, nv, npc);
+ if(debug['o'])
+ pflows(b);
+ udchain(b, d, nv, npc, defs, uses, sd);
+ freeblks(b);
+ freebits(uses, nv);
+ freebits(defs, nv);
+ if(debug['o'])
+ print("nb=%d npc=%d nv=%d nd=%d nu=%d\n", nb, npc, nv, nd, nu);
+}
+
--- /dev/null
+++ b/limbo/sbl.c
@@ -1,0 +1,352 @@
+#include "limbo.h"
+
+static char sbltname[Tend] =
+{
+ /* Tnone */ 'n',
+ /* Tadt */ 'a',
+ /* Tadtpick */ 'a',
+ /* Tarray */ 'A',
+ /* Tbig */ 'B',
+ /* Tbyte */ 'b',
+ /* Tchan */ 'C',
+ /* Treal */ 'f',
+ /* Tfn */ 'F',
+ /* Tint */ 'i',
+ /* Tlist */ 'L',
+ /* Tmodule */ 'm',
+ /* Tref */ 'R',
+ /* Tstring */ 's',
+ /* Ttuple */ 't',
+ /* Texception */ 't',
+ /* Tfix */ 'i',
+ /* Tpoly */ 'P',
+
+ /* Tainit */ '?',
+ /* Talt */ '?',
+ /* Tany */ 'N',
+ /* Tarrow */ '?',
+ /* Tcase */ '?',
+ /* Tcasel */ '?',
+ /* Tcasec */ '?',
+ /* Tdot */ '?',
+ /* Terror */ '?',
+ /* Tgoto */ '?',
+ /* Tid */ '?',
+ /* Tiface */ '?',
+ /* Texcept */ '?',
+ /* Tinst */ '?',
+};
+int sbltadtpick = 'p';
+
+static Sym *sfiles;
+static Sym *ftail;
+static int nsfiles;
+static int blockid;
+static int lastf;
+static int lastline;
+
+static void sbltype(Type*, int);
+static void sbldecl(Decl*, int);
+static void sblftype(Type*);
+static void sblfdecl(Decl*, int);
+
+void
+sblmod(Decl *m)
+{
+ Bprint(bsym, "limbo .sbl 2.1\n");
+ Bprint(bsym, "%s\n", m->sym->name);
+
+ blockid = 0;
+ nsfiles = 0;
+ sfiles = ftail = nil;
+ lastf = 0;
+ lastline = 0;
+}
+
+static int
+sblfile(char *name)
+{
+ Sym *s;
+ int i;
+
+ i = 0;
+ for(s = sfiles; s != nil; s = s->next){
+ if(strcmp(s->name, name) == 0)
+ return i;
+ i++;
+ }
+ s = allocmem(sizeof(Sym));
+ s->name = name;
+ s->next = nil;
+ if(sfiles == nil)
+ sfiles = s;
+ else
+ ftail->next = s;
+ ftail = s;
+ nsfiles = i + 1;
+ return i;
+}
+
+static char *
+filename(char *s)
+{
+ char *t;
+
+ t = strrchr(s, '/');
+ if(t != nil)
+ s = t + 1;
+ t = strrchr(s, '\\');
+ if(t != nil)
+ s = t+1;
+ t = strrchr(s, ' ');
+ if(t != nil)
+ s = t + 1;
+ return s;
+}
+
+void
+sblfiles(void)
+{
+ Sym *s;
+ int i;
+
+ for(i = 0; i < nfiles; i++)
+ files[i]->sbl = sblfile(files[i]->name);
+ Bprint(bsym, "%d\n", nsfiles);
+ for(s = sfiles; s != nil; s = s->next)
+ Bprint(bsym, "%s\n", filename(s->name));
+}
+
+static char*
+sblsrcconv(char *buf, char *end, Src *src)
+{
+ Fline fl;
+ File *startf, *stopf;
+ char *s;
+ int startl, stopl;
+
+ s = buf;
+
+ fl = fline(src->start.line);
+ startf = fl.file;
+ startl = fl.line;
+ fl = fline(src->stop.line);
+ stopf = fl.file;
+ stopl = fl.line;
+ if(lastf != startf->sbl)
+ s = seprint(s, end, "%d:", startf->sbl);
+ if(lastline != startl)
+ s = seprint(s, end, "%d.", startl);
+ s = seprint(s, end, "%d,", src->start.pos);
+ if(startf->sbl != stopf->sbl)
+ s = seprint(s, end, "%d:", stopf->sbl);
+ if(startl != stopl)
+ s = seprint(s, end, "%d.", stopl);
+ seprint(s, end, "%d ", src->stop.pos);
+ lastf = stopf->sbl;
+ lastline = stopl;
+ return buf;
+}
+
+#define isnilsrc(s) ((s)->start.line == 0 && (s)->stop.line == 0 && (s)->start.pos == 0 && (s)->stop.pos == 0)
+#define isnilstopsrc(s) ((s)->stop.line == 0 && (s)->stop.pos == 0)
+
+void
+sblinst(Inst *inst, long ninst)
+{
+ Inst *in;
+ char buf[StrSize];
+ int *sblblocks, i, b;
+ Src src;
+
+ Bprint(bsym, "%ld\n", ninst);
+ sblblocks = allocmem(nblocks * sizeof *sblblocks);
+ for(i = 0; i < nblocks; i++)
+ sblblocks[i] = -1;
+ src = nosrc;
+ for(in = inst; in != nil; in = in->next){
+ if(in->op == INOOP)
+ continue;
+ if(in->src.start.line < 0)
+ fatal("no file specified for %I", in);
+ b = sblblocks[in->block];
+ if(b < 0)
+ sblblocks[in->block] = b = blockid++;
+ if(isnilsrc(&in->src))
+ in->src = src;
+ else if(isnilstopsrc(&in->src)){ /* how does this happen ? */
+ in->src.stop = in->src.start;
+ in->src.stop.pos++;
+ }
+ Bprint(bsym, "%s%d\n", sblsrcconv(buf, buf+sizeof(buf), &in->src), b);
+ src = in->src;
+ }
+ free(sblblocks);
+}
+
+void
+sblty(Decl **tys, int ntys)
+{
+ Decl *d;
+ int i;
+
+ Bprint(bsym, "%d\n", ntys);
+ for(i = 0; i < ntys; i++){
+ d = tys[i];
+ d->ty->sbl = i;
+ }
+ for(i = 0; i < ntys; i++){
+ d = tys[i];
+ sbltype(d->ty, 1);
+ }
+}
+
+void
+sblfn(Decl **fns, int nfns)
+{
+ Decl *f;
+ int i;
+
+ Bprint(bsym, "%d\n", nfns);
+ for(i = 0; i < nfns; i++){
+ f = fns[i];
+ if(ispoly(f))
+ rmfnptrs(f);
+ if(f->dot != nil && f->dot->ty->kind == Tadt)
+ Bprint(bsym, "%ld:%s.%s\n", f->pc->pc, f->dot->sym->name, f->sym->name);
+ else
+ Bprint(bsym, "%ld:%s\n", f->pc->pc, f->sym->name);
+ sbldecl(f->ty->ids, Darg);
+ sbldecl(f->locals, Dlocal);
+ sbltype(f->ty->tof, 0);
+ }
+}
+
+void
+sblvar(Decl *vars)
+{
+ sbldecl(vars, Dglobal);
+}
+
+static int
+isvis(Decl *id)
+{
+ if(!tattr[id->ty->kind].vis
+ || id->sym == nil
+ || id->sym->name == nil /*????*/
+ || id->sym->name[0] == '.')
+ return 0;
+ if(id->ty == tstring && id->init != nil && id->init->op == Oconst)
+ return 0;
+ if(id->src.start.line < 0 || id->src.stop.line < 0)
+ return 0;
+ return 1;
+}
+
+static void
+sbldecl(Decl *ids, int store)
+{
+ Decl *id;
+ char buf[StrSize];
+ int n;
+
+ n = 0;
+ for(id = ids; id != nil; id = id->next){
+ if(id->store != store || !isvis(id))
+ continue;
+ n++;
+ }
+ Bprint(bsym, "%d\n", n);
+ for(id = ids; id != nil; id = id->next){
+ if(id->store != store || !isvis(id))
+ continue;
+ Bprint(bsym, "%ld:%s:%s", id->offset, id->sym->name, sblsrcconv(buf, buf+sizeof(buf), &id->src));
+ sbltype(id->ty, 0);
+ Bprint(bsym, "\n");
+ }
+}
+
+static void
+sbltype(Type *t, int force)
+{
+ Type *lastt;
+ Decl *tg, *d;
+ char buf[StrSize];
+
+ if(t->kind == Tadtpick)
+ t = t->decl->dot->ty;
+
+ d = t->decl;
+ if(!force && d != nil && d->ty->sbl >= 0){
+ Bprint(bsym, "@%d\n", d->ty->sbl);
+ return;
+ }
+
+ switch(t->kind){
+ default:
+ fatal("bad type %T in sbltype", t);
+ break;
+ case Tnone:
+ case Tany:
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ case Tstring:
+ case Tfix:
+ case Tpoly:
+ Bprint(bsym, "%c", sbltname[t->kind]);
+ break;
+ case Tfn:
+ Bprint(bsym, "%c", sbltname[t->kind]);
+ sbldecl(t->ids, Darg);
+ sbltype(t->tof, 0);
+ break;
+ case Tarray:
+ case Tlist:
+ case Tchan:
+ case Tref:
+ Bprint(bsym, "%c", sbltname[t->kind]);
+ if(t->kind == Tref && t->tof->kind == Tfn){
+ tattr[Tany].vis = 1;
+ sbltype(tfnptr, 0);
+ tattr[Tany].vis = 0;
+ }
+ else
+ sbltype(t->tof, 0);
+ break;
+ case Ttuple:
+ case Texception:
+ Bprint(bsym, "%c%ld.", sbltname[t->kind], t->size);
+ sbldecl(t->ids, Dfield);
+ break;
+ case Tadt:
+ if(t->tags != nil)
+ Bputc(bsym, sbltadtpick);
+ else
+ Bputc(bsym, sbltname[t->kind]);
+ if(d->dot != nil && !isimpmod(d->dot->sym))
+ Bprint(bsym, "%s->", d->dot->sym->name);
+ Bprint(bsym, "%s %s%ld\n", d->sym->name, sblsrcconv(buf, buf+sizeof(buf), &d->src), d->ty->size);
+ sbldecl(t->ids, Dfield);
+ if(t->tags != nil){
+ Bprint(bsym, "%d\n", t->decl->tag);
+ lastt = nil;
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ Bprint(bsym, "%s:%s", tg->sym->name, sblsrcconv(buf, buf+sizeof(buf), &tg->src));
+ if(lastt == tg->ty){
+ Bputc(bsym, '\n');
+ }else{
+ Bprint(bsym, "%ld\n", tg->ty->size);
+ sbldecl(tg->ty->ids, Dfield);
+ }
+ lastt = tg->ty;
+ }
+ }
+ break;
+ case Tmodule:
+ Bprint(bsym, "%c%s\n%s", sbltname[t->kind], d->sym->name, sblsrcconv(buf, buf+sizeof(buf), &d->src));
+ sbldecl(t->ids, Dglobal);
+ break;
+ }
+}
--- /dev/null
+++ b/limbo/stubs.c
@@ -1,0 +1,590 @@
+#include "limbo.h"
+
+static long stubalign(long offset, int a, char** b, char *e);
+static void pickadtstub(Type *t);
+
+void
+emit(Decl *globals)
+{
+ Decl *m, *d, *id;
+
+ for(m = globals; m != nil; m = m->next){
+ if(m->store != Dtype || m->ty->kind != Tmodule)
+ continue;
+ m->ty = usetype(m->ty);
+ for(d = m->ty->ids; d != nil; d = d->next){
+ d->ty = usetype(d->ty);
+ if(d->store == Dglobal || d->store == Dfn)
+ modrefable(d->ty);
+ if(d->store == Dtype && d->ty->kind == Tadt){
+ for(id = d->ty->ids; id != nil; id = id->next){
+ id->ty = usetype(id->ty);
+ modrefable(d->ty);
+ }
+ }
+ }
+ }
+ if(emitstub){
+ adtstub(globals);
+ modstub(globals);
+ }
+ if(emittab != nil)
+ modtab(globals);
+ if(emitcode)
+ modcode(globals);
+}
+
+static char*
+lowercase(char *f)
+{
+ char *s = f;
+
+ for( ; *s != 0; s++)
+ if(*s >= 'A' && *s <= 'Z')
+ *s += 'a' - 'A';
+ return f;
+}
+
+void
+modcode(Decl *globals)
+{
+ Decl *d, *id;
+ char buf[32];
+
+ if(emitdyn){
+ strcpy(buf, emitcode);
+ lowercase(buf);
+ print("#include \"%s.h\"\n", buf);
+ }
+ else{
+ print("#include <lib9.h>\n");
+ print("#include <isa.h>\n");
+ print("#include <interp.h>\n");
+ print("#include \"%smod.h\"\n", emitcode);
+ }
+ print("\n");
+
+ for(d = globals; d != nil; d = d->next)
+ if(d->store == Dtype && d->ty->kind == Tmodule && strcmp(d->sym->name, emitcode) == 0)
+ break;
+
+ if(d == nil)
+ return;
+
+ /*
+ * stub types
+ */
+ for(id = d->ty->ids; id != nil; id = id->next){
+ if(id->store == Dtype && id->ty->kind == Tadt){
+ id->ty = usetype(id->ty);
+ print("Type*\tT_%s;\n", id->sym->name);
+ }
+ }
+
+ /*
+ * type maps
+ */
+ if(emitdyn){
+ for(id = d->ty->ids; id != nil; id = id->next)
+ if(id->store == Dtype && id->ty->kind == Tadt)
+ print("uchar %s_map[] = %s_%s_map;\n",
+ id->sym->name, emitcode, id->sym->name);
+ }
+
+ /*
+ * heap allocation and garbage collection for a type
+ */
+ if(emitdyn){
+ for(id = d->ty->ids; id != nil; id = id->next)
+ if(id->store == Dtype && id->ty->kind == Tadt){
+ print("\n%s_%s*\n%salloc%s(void)\n{\n\tHeap *h;\n\n\th = heap(T_%s);\n\treturn H2D(%s_%s*, h);\n}\n", emitcode, id->sym->name, emitcode, id->sym->name, id->sym->name, emitcode, id->sym->name);
+ print("\nvoid\n%sfree%s(Heap *h, int swept)\n{\n\t%s_%s *d;\n\n\td = H2D(%s_%s*, h);\n\tfreeheap(h, swept);\n}\n", emitcode, id->sym->name, emitcode, id->sym->name, emitcode, id->sym->name);
+ }
+ }
+
+ /*
+ * initialization function
+ */
+ if(emitdyn)
+ print("\nvoid\n%sinit(void)\n{\n", emitcode);
+ else{
+ print("\nvoid\n%smodinit(void)\n{\n", emitcode);
+ print("\tbuiltinmod(\"$%s\", %smodtab, %smodlen);\n", emitcode, emitcode, emitcode);
+ }
+ for(id = d->ty->ids; id != nil; id = id->next){
+ if(id->store == Dtype && id->ty->kind == Tadt){
+ if(emitdyn)
+ print("\tT_%s = dtype(%sfree%s, %s_%s_size, %s_map, sizeof(%s_map));\n",
+ id->sym->name, emitcode, id->sym->name, emitcode, id->sym->name, id->sym->name, id->sym->name);
+ else
+ print("\tT_%s = dtype(freeheap, sizeof(%s), %smap, sizeof(%smap));\n",
+ id->sym->name, id->sym->name, id->sym->name, id->sym->name);
+ }
+ }
+ print("}\n");
+
+ /*
+ * end function
+ */
+ if(emitdyn){
+ print("\nvoid\n%send(void)\n{\n", emitcode);
+ for(id = d->ty->ids; id != nil; id = id->next)
+ if(id->store == Dtype && id->ty->kind == Tadt)
+ print("\tfreetype(T_%s);\n", id->sym->name);
+ print("}\n");
+ }
+
+ /*
+ * stub functions
+ */
+ for(id = d->ty->tof->ids; id != nil; id = id->next){
+ print("\nvoid\n%s_%s(void *fp)\n{\n\tF_%s_%s *f = fp;\n",
+ id->dot->sym->name, id->sym->name,
+ id->dot->sym->name, id->sym->name);
+ if(id->ty->tof != tnone && tattr[id->ty->tof->kind].isptr){
+ print("\tvoid *r;\n");
+ print("\n\tr = *f->ret;\n\t*f->ret = H;\n\tdestroy(r);\n");
+ }
+ print("}\n");
+ }
+
+ if(emitdyn)
+ print("\n#include \"%smod.h\"\n", buf);
+}
+
+void
+modtab(Decl *globals)
+{
+ int n;
+ Desc *md;
+ Decl *d, *id;
+
+ print("typedef struct{char *name; long sig; void (*fn)(void*); int size; int np; uchar map[16];} Runtab;\n");
+ for(d = globals; d != nil; d = d->next){
+ if(d->store == Dtype && d->ty->kind == Tmodule && strcmp(d->sym->name, emittab) == 0){
+ n = 0;
+ print("Runtab %smodtab[]={\n", d->sym->name);
+ for(id = d->ty->tof->ids; id != nil; id = id->next){
+ n++;
+ print("\t\"");
+ if(id->dot != d)
+ print("%s.", id->dot->sym->name);
+ print("%s\",0x%lux,%s_%s,", id->sym->name, sign(id),
+ id->dot->sym->name, id->sym->name);
+ if(id->ty->varargs)
+ print("0,0,{0},");
+ else{
+ md = mkdesc(idoffsets(id->ty->ids, MaxTemp, MaxAlign), id->ty->ids);
+ print("%ld,%ld,%M,", md->size, md->nmap, md);
+ }
+ print("\n");
+ }
+ print("\t0\n};\n");
+ print("#define %smodlen %d\n", d->sym->name, n);
+ }
+ }
+}
+
+/*
+ * produce activation records for all the functions in modules
+ */
+void
+modstub(Decl *globals)
+{
+ Type *t;
+ Decl *d, *id, *m;
+ char buf[StrSize*2], *p;
+ long offset;
+ int arg;
+
+ for(d = globals; d != nil; d = d->next){
+ if(d->store != Dtype || d->ty->kind != Tmodule)
+ continue;
+ arg = 0;
+ for(id = d->ty->tof->ids; id != nil; id = id->next){
+ if(emitdyn && id->dot->dot != nil)
+ seprint(buf, buf+sizeof(buf), "%s_%s_%s", id->dot->dot->sym->name, id->dot->sym->name, id->sym->name);
+ else
+ seprint(buf, buf+sizeof(buf), "%s_%s", id->dot->sym->name, id->sym->name);
+ print("void %s(void*);\ntypedef struct F_%s F_%s;\nstruct F_%s\n{\n",
+ buf, buf, buf, buf);
+ print(" WORD regs[NREG-1];\n");
+ if(id->ty->tof != tnone)
+ print(" %R* ret;\n", id->ty->tof);
+ else
+ print(" WORD noret;\n");
+ print(" uchar temps[%d];\n", MaxTemp-NREG*IBY2WD);
+ offset = MaxTemp;
+ for(m = id->ty->ids; m != nil; m = m->next){
+ if(m->sym != nil)
+ p = m->sym->name;
+ else{
+ seprint(buf, buf+sizeof(buf), "arg%d", arg);
+ p = buf;
+ }
+
+ /*
+ * explicit pads for structure alignment
+ */
+ t = m->ty;
+ offset = stubalign(offset, t->align, nil, nil);
+ if(offset != m->offset)
+ yyerror("module stub must not contain data objects");
+ // fatal("modstub bad offset");
+ print(" %R %s;\n", t, p);
+ arg++;
+ offset += t->size;
+ }
+ if(id->ty->varargs)
+ print(" WORD vargs;\n");
+ print("};\n");
+ }
+ for(id = d->ty->ids; id != nil; id = id->next)
+ if(id->store == Dconst)
+ constub(id);
+ }
+}
+
+static void
+chanstub(char *in, Decl *id)
+{
+ Desc *desc;
+
+ print("typedef %R %s_%s;\n", id->ty->tof, in, id->sym->name);
+ desc = mktdesc(id->ty->tof);
+ print("#define %s_%s_size %ld\n", in, id->sym->name, desc->size);
+ print("#define %s_%s_map %M\n", in, id->sym->name, desc);
+}
+
+/*
+ * produce c structs for all adts
+ */
+void
+adtstub(Decl *globals)
+{
+ Type *t, *tt;
+ Desc *desc;
+ Decl *m, *d, *id;
+ char buf[2*StrSize];
+ long offset;
+
+ for(m = globals; m != nil; m = m->next){
+ if(m->store != Dtype || m->ty->kind != Tmodule)
+ continue;
+ for(d = m->ty->ids; d != nil; d = d->next){
+ if(d->store != Dtype)
+ continue;
+ t = usetype(d->ty);
+ d->ty = t;
+ dotprint(buf, buf+sizeof(buf), d->ty->decl, '_');
+ switch(d->ty->kind){
+ case Tadt:
+ print("typedef struct %s %s;\n", buf, buf);
+ break;
+ case Tint:
+ case Tbyte:
+ case Treal:
+ case Tbig:
+ case Tfix:
+ print("typedef %T %s;\n", t, buf);
+ break;
+ }
+ }
+ }
+ for(m = globals; m != nil; m = m->next){
+ if(m->store != Dtype || m->ty->kind != Tmodule)
+ continue;
+ for(d = m->ty->ids; d != nil; d = d->next){
+ if(d->store != Dtype)
+ continue;
+ t = d->ty;
+ if(t->kind == Tadt || t->kind == Ttuple && t->decl->sym != anontupsym){
+ if(t->tags != nil){
+ pickadtstub(t);
+ continue;
+ }
+ dotprint(buf, buf+sizeof(buf), t->decl, '_');
+ print("struct %s\n{\n", buf);
+ offset = 0;
+ for(id = t->ids; id != nil; id = id->next){
+ if(id->store == Dfield){
+ tt = id->ty;
+ offset = stubalign(offset, tt->align, nil, nil);
+ if(offset != id->offset)
+ fatal("adtstub bad offset");
+ print(" %R %s;\n", tt, id->sym->name);
+ offset += tt->size;
+ }
+ }
+ if(t->ids == nil){
+ print(" char dummy[1];\n");
+ offset = 1;
+ }
+ offset = stubalign(offset, t->align, nil ,nil);
+ offset = stubalign(offset, IBY2WD, nil , nil);
+ if(offset != t->size && t->ids != nil)
+ fatal("adtstub: bad size");
+ print("};\n");
+
+ for(id = t->ids; id != nil; id = id->next)
+ if(id->store == Dconst)
+ constub(id);
+
+ for(id = t->ids; id != nil; id = id->next)
+ if(id->ty->kind == Tchan)
+ chanstub(buf, id);
+
+ desc = mktdesc(t);
+ if(offset != desc->size && t->ids != nil)
+ fatal("adtstub: bad desc size");
+ print("#define %s_size %ld\n", buf, offset);
+ print("#define %s_map %M\n", buf, desc);
+if(0)
+ print("struct %s_check {int s[2*(sizeof(%s)==%s_size)-1];};\n", buf, buf, buf);
+ }else if(t->kind == Tchan)
+ chanstub(m->sym->name, d);
+ }
+ }
+}
+
+/*
+ * emit an expicit pad field for aligning emitted c structs
+ * according to limbo's definition
+ */
+static long
+stubalign(long offset, int a, char **buf, char *end)
+{
+ long x;
+
+ x = offset & (a-1);
+ if(x == 0)
+ return offset;
+ x = a - x;
+ if(buf == nil)
+ print("\tuchar\t_pad%ld[%ld];\n", offset, x);
+ else
+ *buf = seprint(*buf, end, "uchar\t_pad%ld[%ld]; ", offset, x);
+ offset += x;
+ if((offset & (a-1)) || x >= a)
+ fatal("compiler stub misalign");
+ return offset;
+}
+
+void
+constub(Decl *id)
+{
+ char buf[StrSize*2];
+
+ seprint(buf, buf+sizeof(buf), "%s_%s", id->dot->sym->name, id->sym->name);
+ switch(id->ty->kind){
+ case Tbyte:
+ print("#define %s %d\n", buf, (int)id->init->val & 0xff);
+ break;
+ case Tint:
+ case Tfix:
+ print("#define %s %ld\n", buf, (long)id->init->val);
+ break;
+ case Tbig:
+ print("#define %s %ld\n", buf, (long)id->init->val);
+ break;
+ case Treal:
+ print("#define %s %g\n", buf, id->init->rval);
+ break;
+ case Tstring:
+ print("#define %s \"%s\"\n", buf, id->init->decl->sym->name);
+ break;
+ }
+}
+
+int
+mapconv(Fmt *f)
+{
+ Desc *d;
+ char *s, *e, buf[1024];
+ int i;
+
+ d = va_arg(f->args, Desc*);
+ e = buf+sizeof(buf);
+ s = buf;
+ s = secpy(s, e, "{");
+ for(i = 0; i < d->nmap; i++)
+ s = seprint(s, e, "0x%x,", d->map[i]);
+ if(i == 0)
+ s = seprint(s, e, "0");
+ seprint(s, e, "}");
+ return fmtstrcpy(f, buf);
+}
+
+char*
+dotprint(char *buf, char *end, Decl *d, int dot)
+{
+ if(d->dot != nil){
+ buf = dotprint(buf, end, d->dot, dot);
+ if(buf < end)
+ *buf++ = dot;
+ }
+ if(d->sym == nil)
+ return buf;
+ return seprint(buf, end, "%s", d->sym->name);
+}
+
+char *ckindname[Tend] =
+{
+ /* Tnone */ "void",
+ /* Tadt */ "struct",
+ /* Tadtpick */ "?adtpick?",
+ /* Tarray */ "Array*",
+ /* Tbig */ "LONG",
+ /* Tbyte */ "BYTE",
+ /* Tchan */ "Channel*",
+ /* Treal */ "REAL",
+ /* Tfn */ "?fn?",
+ /* Tint */ "WORD",
+ /* Tlist */ "List*",
+ /* Tmodule */ "Modlink*",
+ /* Tref */ "?ref?",
+ /* Tstring */ "String*",
+ /* Ttuple */ "?tuple?",
+ /* Texception */ "?exception",
+ /* Tfix */ "WORD",
+ /* Tpoly */ "void*",
+
+ /* Tainit */ "?ainit?",
+ /* Talt */ "?alt?",
+ /* Tany */ "void*",
+ /* Tarrow */ "?arrow?",
+ /* Tcase */ "?case?",
+ /* Tcasel */ "?casel",
+ /* Tcasec */ "?casec?",
+ /* Tdot */ "?dot?",
+ /* Terror */ "?error?",
+ /* Tgoto */ "?goto?",
+ /* Tid */ "?id?",
+ /* Tiface */ "?iface?",
+ /* Texcept */ "?except?",
+ /* Tinst */ "?inst?",
+};
+
+char*
+ctprint(char *buf, char *end, Type *t)
+{
+ Decl *id;
+ Type *tt;
+ long offset;
+
+ if(t == nil)
+ return secpy(buf, end, "void");
+ switch(t->kind){
+ case Tref:
+ return seprint(buf, end, "%R*", t->tof);
+ case Tarray:
+ case Tlist:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tchan:
+ case Tmodule:
+ case Tfix:
+ case Tpoly:
+ return seprint(buf, end, "%s", ckindname[t->kind]);
+ case Tadtpick:
+ return ctprint(buf, end, t->decl->dot->ty);
+ case Tadt:
+ case Ttuple:
+ if(t->decl->sym != anontupsym)
+ return dotprint(buf, end, t->decl, '_');
+ offset = 0;
+ buf = secpy(buf, end, "struct{ ");
+ for(id = t->ids; id != nil; id = id->next){
+ tt = id->ty;
+ offset = stubalign(offset, tt->align, &buf, end);
+ if(offset != id->offset)
+ fatal("ctypeconv tuple bad offset");
+ buf = seprint(buf, end, "%R %s; ", tt, id->sym->name);
+ offset += tt->size;
+ }
+ offset = stubalign(offset, t->align, &buf, end);
+ if(offset != t->size)
+ fatal("ctypeconv tuple bad t=%T size=%ld offset=%ld", t, t->size, offset);
+ return secpy(buf, end, "}");
+ default:
+ if(t->kind >= Tend)
+ yyerror("no C equivalent for type %d", t->kind);
+ else
+ yyerror("no C equivalent for type %s", kindname[t->kind]);
+ break;
+ }
+ return buf;
+}
+
+static void
+pickadtstub(Type *t)
+{
+ Type *tt;
+ Desc *desc;
+ Decl *id, *tg;
+ char buf[2*StrSize];
+ int ok;
+ long offset, tgoffset;
+
+ dotprint(buf, buf+sizeof(buf), t->decl, '_');
+ offset = 0;
+ for(tg = t->tags; tg != nil; tg = tg->next)
+ print("#define %s_%s %ld\n", buf, tg->sym->name, offset++);
+ print("struct %s\n{\n", buf);
+ print(" int pick;\n");
+ offset = IBY2WD;
+ for(id = t->ids; id != nil; id = id->next){
+ if(id->store == Dfield){
+ tt = id->ty;
+ offset = stubalign(offset, tt->align, nil, nil);
+ if(offset != id->offset)
+ fatal("pickadtstub bad offset");
+ print(" %R %s;\n", tt, id->sym->name);
+ offset += tt->size;
+ }
+ }
+ print(" union{\n");
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ tgoffset = offset;
+ print(" struct{\n");
+ for(id = tg->ty->ids; id != nil; id = id->next){
+ if(id->store == Dfield){
+ tt = id->ty;
+ tgoffset = stubalign(tgoffset, tt->align, nil, nil);
+ if(tgoffset != id->offset)
+ fatal("pickadtstub bad offset");
+ print(" %R %s;\n", tt, id->sym->name);
+ tgoffset += tt->size;
+ }
+ }
+ if(tg->ty->ids == nil)
+ print(" char dummy[1];\n");
+ print(" } %s;\n", tg->sym->name);
+ }
+ print(" } u;\n");
+ print("};\n");
+
+ for(id = t->ids; id != nil; id = id->next)
+ if(id->store == Dconst)
+ constub(id);
+
+ for(id = t->ids; id != nil; id = id->next)
+ if(id->ty->kind == Tchan)
+ chanstub(buf, id);
+
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ ok = tg->ty->tof->ok;
+ tg->ty->tof->ok = OKverify;
+ sizetype(tg->ty->tof);
+ tg->ty->tof->ok = OKmask;
+ desc = mktdesc(tg->ty->tof);
+ tg->ty->tof->ok = ok;
+ print("#define %s_%s_size %ld\n", buf, tg->sym->name, tg->ty->size);
+ print("#define %s_%s_map %M\n", buf, tg->sym->name, desc);
+ }
+}
--- /dev/null
+++ b/limbo/typecheck.c
@@ -1,0 +1,3627 @@
+#include "limbo.h"
+#include "y.tab.h"
+
+Node **labstack;
+int labdep;
+static Node* inexcept;
+static Decl* fndec;
+
+void checkraises(Node *n);
+
+static void
+increfs(Decl *id)
+{
+ for( ; id != nil; id = id->link)
+ id->refs++;
+}
+
+static int
+fninline(Decl *d)
+{
+ Node *n, *left, *right;
+
+ left = right = nil;
+ n = d->init;
+ if(dontinline || d->caninline < 0 || d->locals != nil || ispoly(d) || n->ty->tof->kind == Tnone || nodes(n) >= 100)
+ return 0;
+ n = n->right;
+ if(n->op == Oseq && n->right == nil)
+ n = n->left;
+ /*
+ * inline
+ * (a) return e;
+ * (b) if(c) return e1; else return e2;
+ * (c) if(c) return e1; return e2;
+ */
+ switch(n->op){
+ case Oret:
+ break;
+ case Oif:
+ right = n->right;
+ if(right->right == nil || right->left->op != Oret || right->right->op != Oret || !tequal(right->left->left->ty, right->right->left->ty))
+ return 0;
+ break;
+ case Oseq:
+ left = n->left;
+ right = n->right;
+ if(left->op != Oif || left->right->right != nil || left->right->left->op != Oret || right->op != Oseq || right->right != nil || right->left->op != Oret || !tequal(left->right->left->left->ty, right->left->left->ty))
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ if(occurs(d, n) || hasasgns(n))
+ return 0;
+ if(n->op == Oseq){
+ left->right->right = right->left;
+ n = left;
+ right = n->right;
+ d->init->right->right = nil;
+ }
+ if(n->op == Oif){
+ n->ty = right->ty = right->left->left->ty;
+ right->left = right->left->left;
+ right->right = right->right->left;
+ d->init->right->left = mkunary(Oret, n);
+ }
+ return 1;
+}
+
+static int
+isfnrefty(Type *t)
+{
+ return t->kind == Tref && t->tof->kind == Tfn;
+}
+
+static int
+isfnref(Decl *d)
+{
+ switch(d->store){
+ case Dglobal:
+ case Darg:
+ case Dlocal:
+ case Dfield:
+ case Dimport:
+ return isfnrefty(d->ty);
+ }
+ return 0;
+}
+
+int
+argncompat(Node *n, Decl *f, Node *a)
+{
+ for(; a != nil; a = a->right){
+ if(f == nil){
+ nerror(n, "%V: too many function arguments", n->left);
+ return 0;
+ }
+ f = f->next;
+ }
+ if(f != nil){
+ nerror(n, "%V: too few function arguments", n->left);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+rewind(Node *n)
+{
+ Node *r, *nn;
+
+ r = n;
+ nn = n->left;
+ for(n = n->right; n != nil; n = n->right){
+ if(n->right == nil){
+ r->left = nn;
+ r->right = n->left;
+ }
+ else
+ nn = mkbin(Oindex, nn, n->left);
+ }
+}
+
+static void
+ckmod(Node *n, Decl *id)
+{
+ Type *t;
+ Decl *d, *idc;
+ Node *mod;
+
+ if(id == nil)
+ fatal("can't find function: %n", n);
+ idc = nil;
+ mod = nil;
+ if(n->op == Oname){
+ idc = id;
+ mod = id->eimport;
+ }
+ else if(n->op == Omdot)
+ mod = n->left;
+ else if(n->op == Odot){
+ idc = id->dot;
+ t = n->left->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ if(t->kind == Tadtpick)
+ t = t->decl->dot->ty;
+ d = t->decl;
+ while(d != nil && d->link != nil)
+ d = d->link;
+ if(d != nil && d->timport != nil)
+ mod = d->timport->eimport;
+ n->right->left = mod;
+ }
+ if(mod != nil && mod->ty->kind != Tmodule){
+ nerror(n, "cannot use %V as a function reference", n);
+ return;
+ }
+ if(mod != nil){
+ if(valistype(mod)){
+ nerror(n, "cannot use %V as a function reference because %V is a module interface", n, mod);
+ return;
+ }
+ }else if(idc != nil && idc->dot != nil && !isimpmod(idc->dot->sym)){
+ nerror(n, "cannot use %V without importing %s from a variable", n, idc->sym->name);
+ return;
+ }
+ if(mod != nil)
+ modrefable(n->ty);
+}
+
+static void
+addref(Node *n)
+{
+ Node *nn;
+
+ nn = mkn(0, nil, nil);
+ *nn = *n;
+ n->op = Oref;
+ n->left = nn;
+ n->right = nil;
+ n->decl = nil;
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tref, nn->ty, nil));
+}
+
+static void
+fnref(Node *n, Decl *id)
+{
+ id->caninline = -1;
+ ckmod(n, id);
+ addref(n);
+ while(id->link != nil)
+ id = id->link;
+ if(ispoly(id) && encpolys(id) != nil)
+ nerror(n, "cannot have a polymorphic adt function reference %s", id->sym->name);
+}
+
+Decl*
+typecheck(int checkimp)
+{
+ Decl *entry, *m, *d;
+ Sym *s;
+ int i;
+
+ if(errors)
+ return nil;
+
+ /*
+ * generate the set of all functions
+ * compile one function at a time
+ */
+ gdecl(tree);
+ gbind(tree);
+ fns = allocmem(nfns * sizeof(Decl));
+ i = gcheck(tree, fns, 0);
+ if(i != nfns)
+ fatal("wrong number of functions found in gcheck");
+
+ maxlabdep = 0;
+ for(i = 0; i < nfns; i++){
+ d = fns[i];
+ if(d != nil)
+ fndec = d;
+ if(d != nil)
+ fncheck(d);
+ fndec = nil;
+ }
+
+ if(errors)
+ return nil;
+
+ entry = nil;
+ if(checkimp){
+ Decl *im;
+ Dlist *dm;
+
+ if(impmods == nil){
+ yyerror("no implementation module");
+ return nil;
+ }
+ for(im = impmods; im != nil; im = im->next){
+ for(dm = impdecls; dm != nil; dm = dm->next)
+ if(dm->d->sym == im->sym)
+ break;
+ if(dm == nil || dm->d->ty == nil){
+ yyerror("no definition for implementation module %s", im->sym->name);
+ return nil;
+ }
+ }
+
+ /*
+ * can't check the module spec until all types and imports are determined,
+ * which happens in scheck
+ */
+ for(dm = impdecls; dm != nil; dm = dm->next){
+ im = dm->d;
+ im->refs++;
+ im->ty = usetype(im->ty);
+ if(im->store != Dtype || im->ty->kind != Tmodule){
+ error(im->src.start, "cannot implement %K", im);
+ return nil;
+ }
+ }
+
+ /* now check any multiple implementations */
+ impdecl = modimp(impdecls, impmods);
+
+ s = enter("init", 0);
+ entry = nil;
+ for(dm = impdecls; dm != nil; dm = dm->next){
+ im = dm->d;
+ for(m = im->ty->ids; m != nil; m = m->next){
+ m->ty = usetype(m->ty);
+ m->refs++;
+
+ if(m->sym == s && m->ty->kind == Tfn && entry == nil)
+ entry = m;
+
+ if(m->store == Dglobal || m->store == Dfn)
+ modrefable(m->ty);
+
+ if(m->store == Dtype && m->ty->kind == Tadt){
+ for(d = m->ty->ids; d != nil; d = d->next){
+ d->ty = usetype(d->ty);
+ modrefable(d->ty);
+ d->refs++;
+ }
+ }
+ }
+ checkrefs(im->ty->ids);
+ }
+ }
+
+ if(errors)
+ return nil;
+ gsort(tree);
+ tree = nil;
+ return entry;
+}
+
+/*
+ * introduce all global declarations
+ * also adds all fields to adts and modules
+ * note the complications due to nested Odas expressions
+ */
+void
+gdecl(Node *n)
+{
+ for(;;){
+ if(n == nil)
+ return;
+ if(n->op != Oseq)
+ break;
+ gdecl(n->left);
+ n = n->right;
+ }
+ switch(n->op){
+ case Oimport:
+ importdecled(n);
+ gdasdecl(n->right);
+ break;
+ case Oadtdecl:
+ adtdecled(n);
+ break;
+ case Ocondecl:
+ condecled(n);
+ gdasdecl(n->right);
+ break;
+ case Oexdecl:
+ exdecled(n);
+ break;
+ case Omoddecl:
+ moddecled(n);
+ break;
+ case Otypedecl:
+ typedecled(n);
+ break;
+ case Ovardecl:
+ vardecled(n);
+ break;
+ case Ovardecli:
+ vardecled(n->left);
+ gdasdecl(n->right);
+ break;
+ case Ofunc:
+ fndecled(n);
+ break;
+ case Oas:
+ case Odas:
+ case Onothing:
+ gdasdecl(n);
+ break;
+ default:
+ fatal("can't deal with %O in gdecl", n->op);
+ }
+}
+
+/*
+ * bind all global type ids,
+ * including those nested inside modules
+ * this needs to be done, since we may use such
+ * a type later in a nested scope, so if we bound
+ * the type ids then, the type could get bound
+ * to a nested declaration
+ */
+void
+gbind(Node *n)
+{
+ Decl *d, *ids;
+
+ for(;;){
+ if(n == nil)
+ return;
+ if(n->op != Oseq)
+ break;
+ gbind(n->left);
+ n = n->right;
+ }
+ switch(n->op){
+ case Oas:
+ case Ocondecl:
+ case Odas:
+ case Oexdecl:
+ case Ofunc:
+ case Oimport:
+ case Onothing:
+ case Ovardecl:
+ case Ovardecli:
+ break;
+ case Ofielddecl:
+ bindtypes(n->decl->ty);
+ break;
+ case Otypedecl:
+ bindtypes(n->decl->ty);
+ if(n->left != nil)
+ gbind(n->left);
+ break;
+ case Opickdecl:
+ gbind(n->left);
+ d = n->right->left->decl;
+ bindtypes(d->ty);
+ repushids(d->ty->ids);
+ gbind(n->right->right);
+ /* get new ids for undefined types; propagate outwards */
+ ids = popids(d->ty->ids);
+ if(ids != nil)
+ installids(Dundef, ids);
+ break;
+ case Oadtdecl:
+ case Omoddecl:
+ bindtypes(n->ty);
+ if(n->ty->polys != nil)
+ repushids(n->ty->polys);
+ repushids(n->ty->ids);
+ gbind(n->left);
+ /* get new ids for undefined types; propagate outwards */
+ ids = popids(n->ty->ids);
+ if(ids != nil)
+ installids(Dundef, ids);
+ if(n->ty->polys != nil)
+ popids(n->ty->polys);
+ break;
+ default:
+ fatal("can't deal with %O in gbind", n->op);
+ }
+}
+
+/*
+ * check all of the > declarations
+ * bind all type ids referred to within types at the global level
+ * record decls for defined functions
+ */
+int
+gcheck(Node *n, Decl **fns, int nfns)
+{
+ Ok rok;
+ Decl *d;
+
+ for(;;){
+ if(n == nil)
+ return nfns;
+ if(n->op != Oseq)
+ break;
+ nfns = gcheck(n->left, fns, nfns);
+ n = n->right;
+ }
+
+ switch(n->op){
+ case Ofielddecl:
+ if(n->decl->ty->u.eraises)
+ raisescheck(n->decl->ty);
+ break;
+ case Onothing:
+ case Opickdecl:
+ break;
+ case Otypedecl:
+ tcycle(n->ty);
+ break;
+ case Oadtdecl:
+ case Omoddecl:
+ if(n->ty->polys != nil)
+ repushids(n->ty->polys);
+ repushids(n->ty->ids);
+ if(gcheck(n->left, nil, 0))
+ fatal("gcheck fn decls nested in modules or adts");
+ if(popids(n->ty->ids) != nil)
+ fatal("gcheck installs new ids in a module or adt");
+ if(n->ty->polys != nil)
+ popids(n->ty->polys);
+ break;
+ case Ovardecl:
+ varcheck(n, 1);
+ break;
+ case Ocondecl:
+ concheck(n, 1);
+ break;
+ case Oexdecl:
+ excheck(n, 1);
+ break;
+ case Oimport:
+ importcheck(n, 1);
+ break;
+ case Ovardecli:
+ varcheck(n->left, 1);
+ rok = echeck(n->right, 0, 1, nil);
+ if(rok.ok){
+ if(rok.allok)
+ n->right = fold(n->right);
+ globalas(n->right->left, n->right->right, rok.allok);
+ }
+ break;
+ case Oas:
+ case Odas:
+ rok = echeck(n, 0, 1, nil);
+ if(rok.ok){
+ if(rok.allok)
+ n = fold(n);
+ globalas(n->left, n->right, rok.allok);
+ }
+ break;
+ case Ofunc:
+ rok = echeck(n->left, 0, 1, n);
+ if(rok.ok && n->ty->u.eraises)
+ raisescheck(n->ty);
+ d = nil;
+ if(rok.ok)
+ d = fnchk(n);
+ fns[nfns++] = d;
+ break;
+ default:
+ fatal("can't deal with %O in gcheck", n->op);
+ }
+ return nfns;
+}
+
+/*
+ * check for unused expression results
+ * make sure the any calculated expression has
+ * a destination
+ */
+Node*
+checkused(Node *n)
+{
+ Type *t;
+ Node *nn;
+
+ /*
+ * only nil; and nil = nil; should have type tany
+ */
+ if(n->ty == tany){
+ if(n->op == Oname)
+ return n;
+ if(n->op == Oas)
+ return checkused(n->right);
+ fatal("line %L checkused %n", n->src.start, n);
+ }
+
+ if(n->op == Ocall && n->left->ty->kind == Tfn && n->left->ty->tof != tnone){
+ n = mkunary(Oused, n);
+ n->ty = n->left->ty;
+ return n;
+ }
+ if(n->op == Ocall && isfnrefty(n->left->ty)){
+ if(n->left->ty->tof->tof != tnone){
+ n = mkunary(Oused, n);
+ n->ty = n->left->ty;
+ }
+ return n;
+ }
+ if(isused[n->op] && (n->op != Ocall || n->left->ty->kind == Tfn))
+ return n;
+ t = n->ty;
+ if(t->kind == Tfn)
+ nerror(n, "function %V not called", n);
+ else if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+ nerror(n, "expressions cannot have type %T", t);
+ else if(n->op == Otuple){
+ for(nn = n->left; nn != nil; nn = nn->right)
+ checkused(nn->left);
+ }
+ else
+ nwarn(n, "result of expression %V not used", n);
+ n = mkunary(Oused, n);
+ n->ty = n->left->ty;
+ return n;
+}
+
+void
+fncheck(Decl *d)
+{
+ Node *n;
+ Decl *adtp;
+
+ n = d->init;
+ if(debug['t'])
+ print("typecheck tree: %n\n", n);
+
+ fndecls = nil;
+ adtp = outerpolys(n->left);
+ if(n->left->op == Odot)
+ repushids(adtp);
+ if(d->ty->polys != nil)
+ repushids(d->ty->polys);
+ repushids(d->ty->ids);
+
+ labdep = 0;
+ labstack = allocmem(maxlabdep * sizeof *labstack);
+ n->right = scheck(n->right, d->ty->tof, Sother);
+ if(labdep != 0)
+ fatal("unbalanced label stack in fncheck");
+ free(labstack);
+
+ d->locals = appdecls(popids(d->ty->ids), fndecls);
+ if(d->ty->polys != nil)
+ popids(d->ty->polys);
+ if(n->left->op == Odot)
+ popids(adtp);
+ fndecls = nil;
+
+ checkrefs(d->ty->ids);
+ checkrefs(d->ty->polys);
+ checkrefs(d->locals);
+
+ checkraises(n);
+
+ d->caninline = fninline(d);
+}
+
+Node*
+scheck(Node *n, Type *ret, int kind)
+{
+ Node *left, *right, *last, *top;
+ Decl *d;
+ Sym *s;
+ Ok rok;
+ int i;
+
+ top = n;
+ last = nil;
+ for(; n != nil; n = n->right){
+ left = n->left;
+ right = n->right;
+ switch(n->op){
+ case Ovardecl:
+ vardecled(n);
+ varcheck(n, 0);
+ if (nested() && tmustzero(n->decl->ty))
+ decltozero(n);
+/*
+ else if (inloop() && tmustzero(n->decl->ty))
+ decltozero(n);
+*/
+ return top;
+ case Ovardecli:
+ vardecled(left);
+ varcheck(left, 0);
+ echeck(right, 0, 0, nil);
+ if (nested() && tmustzero(left->decl->ty))
+ decltozero(left);
+ return top;
+ case Otypedecl:
+ typedecled(n);
+ bindtypes(n->ty);
+ tcycle(n->ty);
+ return top;
+ case Ocondecl:
+ condecled(n);
+ concheck(n, 0);
+ return top;
+ case Oexdecl:
+ exdecled(n);
+ excheck(n, 0);
+ return top;
+ case Oimport:
+ importdecled(n);
+ importcheck(n, 0);
+ return top;
+ case Ofunc:
+ fatal("scheck func");
+ case Oscope:
+ pushscope(n, kind == Sother ? Sscope : kind);
+ if (left != nil)
+ fatal("Oscope has left field");
+ echeck(left, 0, 0, nil);
+ n->right = scheck(right, ret, Sother);
+ d = popscope();
+ fndecls = appdecls(fndecls, d);
+ return top;
+ case Olabel:
+ echeck(left, 0, 0, nil);
+ n->right = scheck(right, ret, Sother);
+ return top;
+ case Oseq:
+ n->left = scheck(left, ret, Sother);
+ /* next time will check n->right */
+ break;
+ case Oif:
+ rok = echeck(left, 0, 0, nil);
+ if(rok.ok && left->op != Onothing && left->ty != tint)
+ nerror(n, "if conditional must be an int, not %Q", left);
+ right->left = scheck(right->left, ret, Sother);
+ /* next time will check n->right->right */
+ n = right;
+ break;
+ case Ofor:
+ rok = echeck(left, 0, 0, nil);
+ if(rok.ok && left->op != Onothing && left->ty != tint)
+ nerror(n, "for conditional must be an int, not %Q", left);
+ /*
+ * do the continue clause before the body
+ * this reflects the ordering of declarations
+ */
+ pushlabel(n);
+ right->right = scheck(right->right, ret, Sother);
+ right->left = scheck(right->left, ret, Sloop);
+ labdep--;
+ if(n->decl != nil && !n->decl->refs)
+ nwarn(n, "label %s never referenced", n->decl->sym->name);
+ return top;
+ case Odo:
+ rok = echeck(left, 0, 0, nil);
+ if(rok.ok && left->op != Onothing && left->ty != tint)
+ nerror(n, "do conditional must be an int, not %Q", left);
+ pushlabel(n);
+ n->right = scheck(n->right, ret, Sloop);
+ labdep--;
+ if(n->decl != nil && !n->decl->refs)
+ nwarn(n, "label %s never referenced", n->decl->sym->name);
+ return top;
+ case Oalt:
+ case Ocase:
+ case Opick:
+ case Oexcept:
+ pushlabel(n);
+ switch(n->op){
+ case Oalt:
+ altcheck(n, ret);
+ break;
+ case Ocase:
+ casecheck(n, ret);
+ break;
+ case Opick:
+ pickcheck(n, ret);
+ break;
+ case Oexcept:
+ exccheck(n, ret);
+ break;
+ }
+ labdep--;
+ if(n->decl != nil && !n->decl->refs)
+ nwarn(n, "label %s never referenced", n->decl->sym->name);
+ return top;
+ case Oret:
+ rok = echeck(left, 0, 0, nil);
+ if(!rok.ok)
+ return top;
+ if(left == nil){
+ if(ret != tnone)
+ nerror(n, "return of nothing from a fn of %T", ret);
+ }else if(ret == tnone){
+ if(left->ty != tnone)
+ nerror(n, "return %Q from a fn with no return type", left);
+ }else if(!tcompat(ret, left->ty, 0))
+ nerror(n, "return %Q from a fn of %T", left, ret);
+ return top;
+ case Obreak:
+ case Ocont:
+ s = nil;
+ if(n->decl != nil)
+ s = n->decl->sym;
+ for(i = 0; i < labdep; i++){
+ if(s == nil || labstack[i]->decl != nil && labstack[i]->decl->sym == s){
+ if(n->op == Ocont
+ && labstack[i]->op != Ofor && labstack[i]->op != Odo)
+ continue;
+ if(s != nil)
+ labstack[i]->decl->refs++;
+ return top;
+ }
+ }
+ nerror(n, "no appropriate target for %V", n);
+ return top;
+ case Oexit:
+ case Onothing:
+ return top;
+ case Oexstmt:
+ fndec->handler = 1;
+ n->left = scheck(left, ret, Sother);
+ n->right = scheck(right, ret, Sother);
+ return top;
+ default:
+ rok = echeck(n, 0, 0, nil);
+ if(rok.allok)
+ n = checkused(n);
+ if(last == nil)
+ return n;
+ last->right = n;
+ return top;
+ }
+ last = n;
+ }
+ return top;
+}
+
+void
+pushlabel(Node *n)
+{
+ Sym *s;
+ int i;
+
+ if(labdep >= maxlabdep){
+ maxlabdep += MaxScope;
+ labstack = reallocmem(labstack, maxlabdep * sizeof *labstack);
+ }
+ if(n->decl != nil){
+ s = n->decl->sym;
+ n->decl->refs = 0;
+ for(i = 0; i < labdep; i++)
+ if(labstack[i]->decl != nil && labstack[i]->decl->sym == s)
+ nerror(n, "label %s duplicated on line %L", s->name, labstack[i]->decl->src.start);
+ }
+ labstack[labdep++] = n;
+}
+
+void
+varcheck(Node *n, int isglobal)
+{
+ Type *t;
+ Decl *ids, *last;
+
+ t = validtype(n->ty, nil);
+ t = topvartype(t, n->decl, isglobal, 0);
+ last = n->left->decl;
+ for(ids = n->decl; ids != last->next; ids = ids->next){
+ ids->ty = t;
+ shareloc(ids);
+ }
+ if(t->u.eraises)
+ raisescheck(t);
+}
+
+void
+concheck(Node *n, int isglobal)
+{
+ Decl *ids, *last;
+ Type *t;
+ Node *init;
+ Ok rok;
+ int i;
+
+ pushscope(nil, Sother);
+ installids(Dconst, iota);
+ rok = echeck(n->right, 0, isglobal, nil);
+ popscope();
+
+ init = n->right;
+ if(!rok.ok){
+ t = terror;
+ }else{
+ t = init->ty;
+ if(!tattr[t->kind].conable){
+ nerror(init, "cannot have a %T constant", t);
+ rok.allok = 0;
+ }
+ }
+
+ last = n->left->decl;
+ for(ids = n->decl; ids != last->next; ids = ids->next)
+ ids->ty = t;
+
+ if(!rok.allok)
+ return;
+
+ i = 0;
+ for(ids = n->decl; ids != last->next; ids = ids->next){
+ if(rok.ok){
+ iota->init->val = i;
+ ids->init = dupn(0, &nosrc, init);
+ if(!varcom(ids))
+ rok.ok = 0;
+ }
+ i++;
+ }
+}
+
+static char*
+exname(Decl *d)
+{
+ int n;
+ Sym *m;
+ char *s;
+ char buf[16];
+
+ n = 0;
+ sprint(buf, "%d", scope-ScopeGlobal);
+ m = nil;
+ if(d->dot)
+ m = d->dot->sym;
+ else if(impmods)
+ m = impmods->sym;
+ if(m)
+ n += strlen(m->name)+1;
+ if(fndec)
+ n += strlen(fndec->sym->name)+1;
+ n += strlen(buf)+1+strlen(d->sym->name)+1;
+ s = malloc(n);
+ strcpy(s, "");
+ if(m){
+ strcat(s, m->name);
+ strcat(s, ".");
+ }
+ if(fndec){
+ strcat(s, fndec->sym->name);
+ strcat(s, ".");
+ }
+ strcat(s, buf);
+ strcat(s, ".");
+ strcat(s, d->sym->name);
+ return s;
+}
+
+void
+excheck(Node *n, int isglobal)
+{
+ char *nm;
+ Type *t;
+ Decl *ids, *last;
+
+ t = validtype(n->ty, nil);
+ t = topvartype(t, n->decl, isglobal, 0);
+ last = n->left->decl;
+ for(ids = n->decl; ids != last->next; ids = ids->next){
+ ids->ty = t;
+ nm = exname(ids);
+ ids->init = mksconst(&n->src, enterstring(nm, strlen(nm)));
+ /* ids->init = mksconst(&n->src, enterstring(strdup(ids->sym->name), strlen(ids->sym->name))); */
+ }
+}
+
+void
+importcheck(Node *n, int isglobal)
+{
+ Node *m;
+ Decl *id, *last, *v;
+ Type *t;
+ Ok rok;
+
+ rok = echeck(n->right, 1, isglobal, nil);
+ if(!rok.ok)
+ return;
+
+ m = n->right;
+ if(m->ty->kind != Tmodule || m->op != Oname){
+ nerror(n, "cannot import from %Q", m);
+ return;
+ }
+
+ last = n->left->decl;
+ for(id = n->decl; id != last->next; id = id->next){
+ v = namedot(m->ty->ids, id->sym);
+ if(v == nil){
+ error(id->src.start, "%s is not a member of %V", id->sym->name, m);
+ id->store = Dwundef;
+ continue;
+ }
+ id->store = v->store;
+ v->ty = validtype(v->ty, nil);
+ id->ty = t = v->ty;
+ if(id->store == Dtype && t->decl != nil){
+ id->timport = t->decl->timport;
+ t->decl->timport = id;
+ }
+ id->init = v->init;
+ id->importid = v;
+ id->eimport = m;
+ }
+}
+
+static Decl*
+rewcall(Node *n, Decl *d)
+{
+ /* put original function back now we're type checked */
+ while(d->link != nil)
+ d = d->link;
+ if(n->op == Odot)
+ n->right->decl = d;
+ else if(n->op == Omdot){
+ n->right->right->decl = d;
+ n->right->right->ty = d->ty;
+ }
+ else
+ fatal("bad op in Ocall rewcall");
+ n->ty = n->right->ty = d->ty;
+ d->refs++;
+ usetype(d->ty);
+ return d;
+}
+
+/*
+ * annotate the expression with types
+ */
+Ok
+echeck(Node *n, int typeok, int isglobal, Node *par)
+{
+ Type *t, *tt;
+ Node *left, *right, *mod, *nn;
+ Decl *tg, *id, *callee;
+ Sym *s;
+ int max, nocheck;
+ Ok ok, rok, kidsok;
+ static int tagopt;
+
+ ok.ok = ok.allok = 1;
+ if(n == nil)
+ return ok;
+
+ /* avoid deep recursions */
+ if(n->op == Oseq){
+ for( ; n != nil && n->op == Oseq; n = n->right){
+ rok = echeck(n->left, typeok == 2, isglobal, n);
+ ok.ok &= rok.ok;
+ ok.allok &= rok.allok;
+ n->ty = tnone;
+ }
+ if(n == nil)
+ return ok;
+ }
+
+ left = n->left;
+ right = n->right;
+
+ nocheck = 0;
+ if(n->op == Odot || n->op == Omdot || n->op == Ocall || n->op == Oref || n->op == Otagof || n->op == Oindex)
+ nocheck = 1;
+ if(n->op != Odas /* special case */
+ && n->op != Oload) /* can have better error recovery */
+ ok = echeck(left, nocheck, isglobal, n);
+ if(n->op != Odas /* special case */
+ && n->op != Odot /* special check */
+ && n->op != Omdot /* special check */
+ && n->op != Ocall /* can have better error recovery */
+ && n->op != Oindex){
+ rok = echeck(right, 0, isglobal, n);
+ ok.ok &= rok.ok;
+ ok.allok &= rok.allok;
+ }
+ if(!ok.ok){
+ n->ty = terror;
+ ok.allok = 0;
+ return ok;
+ }
+
+ switch(n->op){
+ case Odas:
+ kidsok = echeck(right, 0, isglobal, n);
+ if(!kidsok.ok)
+ right->ty = terror;
+ if(!isglobal && !dasdecl(left)){
+ kidsok.ok = 0;
+ }else if(!specific(right->ty) || !declasinfer(left, right->ty)){
+ nerror(n, "cannot declare %V from %Q", left, right);
+ declaserr(left);
+ kidsok.ok = 0;
+ }
+ if(right->ty->kind == Texception)
+ left->ty = n->ty = mkextuptype(right->ty);
+ else{
+ left->ty = n->ty = right->ty;
+ usedty(n->ty);
+ }
+ kidsok.allok &= kidsok.ok;
+ if (nested() && tmustzero(left->ty))
+ decltozero(left);
+ return kidsok;
+ case Oseq:
+ case Onothing:
+ n->ty = tnone;
+ break;
+ case Owild:
+ n->ty = tint;
+ break;
+ case Ocast:
+ t = usetype(n->ty);
+ n->ty = t;
+ tt = left->ty;
+ if(tcompat(t, tt, 0)){
+ left->ty = t;
+ break;
+ }
+ if(tt->kind == Tarray){
+ if(tt->tof == tbyte && t == tstring)
+ break;
+ }else if(t->kind == Tarray){
+ if(t->tof == tbyte && tt == tstring)
+ break;
+ }else if(casttab[tt->kind][t->kind]){
+ break;
+ }
+ nerror(n, "cannot make a %T from %Q", n->ty, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Ochan:
+ n->ty = usetype(n->ty);
+ if(left && left->ty->kind != Tint){
+ nerror(n, "channel size %Q is not an int", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oload:
+ n->ty = usetype(n->ty);
+ kidsok = echeck(left, 0, isglobal, n);
+ if(n->ty->kind != Tmodule){
+ nerror(n, "cannot load a %T, ", n->ty);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!kidsok.allok){
+ ok.allok = 0;
+ break;
+ }
+ if(left->ty != tstring){
+ nerror(n, "cannot load a module from %Q", left);
+ ok.allok = 0;
+ break;
+ }
+if(n->ty->tof->decl->refs != 0)
+n->ty->tof->decl->refs++;
+n->ty->decl->refs++;
+ usetype(n->ty->tof);
+ break;
+ case Oref:
+ t = left->ty;
+ if(t->kind != Tadt && t->kind != Tadtpick && t->kind != Tfn && t->kind != Ttuple){
+ nerror(n, "cannot make a ref from %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!tagopt && t->kind == Tadt && t->tags != nil && valistype(left)){
+ nerror(n, "instances of ref %V must be qualified with a pick tag", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(t->kind == Tadtpick)
+ t->tof = usetype(t->tof);
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tref, t, nil));
+ break;
+ case Oarray:
+ max = 0;
+ if(right != nil){
+ max = assignindices(n);
+ if(max < 0){
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!specific(right->left->ty)){
+ nerror(n, "type for array not specific");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = mktype(&n->src.start, &n->src.stop, Tarray, right->left->ty, nil);
+ }
+ n->ty = usetype(n->ty);
+
+ if(left->op == Onothing)
+ n->left = left = mkconst(&n->left->src, max);
+
+ if(left->ty->kind != Tint){
+ nerror(n, "array size %Q is not an int", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oelem:
+ n->ty = right->ty;
+ break;
+ case Orange:
+ if(left->ty != right->ty
+ || left->ty != tint && left->ty != tstring){
+ nerror(left, "range %Q to %Q is not an int or string range", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = left->ty;
+ break;
+ case Oname:
+ id = n->decl;
+ if(id == nil){
+ nerror(n, "name with no declaration");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->store == Dunbound){
+ s = id->sym;
+ id = s->decl;
+ if(id == nil)
+ id = undefed(&n->src, s);
+ /* save a little space */
+ s->unbound = nil;
+ n->decl = id;
+ id->refs++;
+ }
+ n->ty = id->ty = usetype(id->ty);
+ switch(id->store){
+ case Dfn:
+ case Dglobal:
+ case Darg:
+ case Dlocal:
+ case Dimport:
+ case Dfield:
+ case Dtag:
+ break;
+ case Dundef:
+ nerror(n, "%s is not declared", id->sym->name);
+ id->store = Dwundef;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Dwundef:
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Dconst:
+ if(id->init == nil){
+ nerror(n, "%s's value cannot be determined", id->sym->name);
+ id->store = Dwundef;
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Dtype:
+ if(typeok)
+ break;
+ nerror(n, "%K is not a variable", id);
+ ok.ok = ok.allok = 0;
+ return ok;
+ default:
+ fatal("echeck: unknown symbol storage");
+ }
+
+ if(n->ty == nil){
+ nerror(n, "%K's type is not fully defined", id);
+ id->store = Dwundef;
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->importid != nil && valistype(id->eimport)
+ && id->store != Dconst && id->store != Dtype && id->store != Dfn){
+ nerror(n, "cannot use %V because %V is a module interface", n, id->eimport);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(n->ty->kind == Texception && !n->ty->cons && par != nil && par->op != Oraise && par->op != Odot){
+ nn = mkn(0, nil, nil);
+ *nn = *n;
+ n->op = Ocast;
+ n->left = nn;
+ n->decl = nil;
+ n->ty = usetype(mkextuptype(n->ty));
+ }
+ /* function name as function reference */
+ if(id->store == Dfn && (par == nil || (par->op != Odot && par->op != Omdot && par->op != Ocall && par->op != Ofunc)))
+ fnref(n, id);
+ break;
+ case Oconst:
+ if(n->ty == nil){
+ nerror(n, "no type in %V", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oas:
+ t = right->ty;
+ if(t->kind == Texception)
+ t = mkextuptype(t);
+ if(!tcompat(left->ty, t, 1)){
+ nerror(n, "type clash in %Q = %Q", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(t == tany)
+ t = left->ty;
+ n->ty = t;
+ left->ty = t;
+ if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+ if(left->ty->kind != Tadtpick || right->ty->kind != Tadtpick)
+ nerror(n, "expressions cannot have type %T", t);
+ if(left->ty->kind == Texception){
+ nerror(n, "cannot assign to an exception");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Osnd:
+ if(left->ty->kind != Tchan){
+ nerror(n, "cannot send on %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!tcompat(left->ty->tof, right->ty, 0)){
+ nerror(n, "type clash in %Q <-= %Q", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ t = right->ty;
+ if(t == tany)
+ t = left->ty->tof;
+ n->ty = t;
+ break;
+ case Orcv:
+ t = left->ty;
+ if(t->kind == Tarray)
+ t = t->tof;
+ if(t->kind != Tchan){
+ nerror(n, "cannot receive on %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(left->ty->kind == Tarray)
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Ttuple, nil,
+ mkids(&n->src, nil, tint, mkids(&n->src, nil, t->tof, nil))));
+ else
+ n->ty = t->tof;
+ break;
+ case Ocons:
+ if(right->ty->kind != Tlist && right->ty != tany){
+ nerror(n, "cannot :: to %Q", right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = right->ty;
+ if(right->ty == tany)
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tlist, left->ty, nil));
+ else if(!tcompat(right->ty->tof, left->ty, 0)){
+ t = tparent(right->ty->tof, left->ty);
+ if(!tcompat(t, left->ty, 0)){
+ nerror(n, "type clash in %Q :: %Q", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ else
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tlist, t, nil));
+ }
+ break;
+ case Ohd:
+ case Otl:
+ if(left->ty->kind != Tlist || left->ty->tof == nil){
+ nerror(n, "cannot %O %Q", n->op, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(n->op == Ohd)
+ n->ty = left->ty->tof;
+ else
+ n->ty = left->ty;
+ break;
+ case Otuple:
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Ttuple, nil, tuplefields(left)));
+ break;
+ case Ospawn:
+ if(left->op != Ocall || left->left->ty->kind != Tfn && !isfnrefty(left->left->ty)){
+ nerror(left, "cannot spawn %V", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(left->ty != tnone){
+ nerror(left, "cannot spawn functions which return values, such as %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oraise:
+ if(left->op == Onothing){
+ if(inexcept == nil){
+ nerror(n, "%V: empty raise not in exception handler", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->left = dupn(1, &n->src, inexcept);
+ break;
+ }
+ if(left->ty != tstring && left->ty->kind != Texception){
+ nerror(n, "%V: raise argument %Q is not a string or exception", n, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if((left->op != Ocall || left->left->ty->kind == Tfn) && left->ty->ids != nil && left->ty->cons){
+ nerror(n, "too few exception arguments");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Ocall:{
+ int pure;
+
+ kidsok = echeck(right, 0, isglobal, nil);
+ t = left->ty;
+ usedty(t);
+ pure = 1;
+ if(t->kind == Tref){
+ pure = 0;
+ t = t->tof;
+ }
+ if(t->kind != Tfn)
+ return callcast(n, kidsok.allok, ok.allok);
+ n->ty = t->tof;
+ if(!kidsok.allok){
+ ok.allok = 0;
+ break;
+ }
+
+ /*
+ * get the name to call and any associated module
+ */
+ mod = nil;
+ callee = nil;
+ id = nil;
+ tt = nil;
+ if(left->op == Odot){
+ Decl *dd;
+ Type *ttt;
+
+ callee = left->right->decl;
+ id = callee->dot;
+ right = passimplicit(left, right);
+ n->right = right;
+ tt = left->left->ty;
+ if(tt->kind == Tref)
+ tt = tt->tof;
+ ttt = tt;
+ if(tt->kind == Tadtpick)
+ ttt = tt->decl->dot->ty;
+ dd = ttt->decl;
+ while(dd != nil && dd->link != nil)
+ dd = dd->link;
+ if(dd != nil && dd->timport != nil)
+ mod = dd->timport->eimport;
+ /*
+ * stash the import module under a rock,
+ * because we won't be able to get it later
+ * after scopes are popped
+ */
+ left->right->left = mod;
+ }else if(left->op == Omdot){
+ if(left->right->op == Odot){
+ callee = left->right->right->decl;
+ right = passimplicit(left->right, right);
+ n->right = right;
+ tt = left->right->left->ty;
+ if(tt->kind == Tref)
+ tt = tt->tof;
+ }else
+ callee = left->right->decl;
+ mod = left->left;
+ }else if(left->op == Oname){
+ callee = left->decl;
+ id = callee;
+ mod = id->eimport;
+ }else if(pure){
+ nerror(left, "%V is not a function name", left);
+ ok.allok = 0;
+ break;
+ }
+ if(pure && callee == nil)
+ fatal("can't find called function: %n", left);
+ if(callee != nil && callee->store != Dfn && !isfnref(callee)){
+ nerror(left, "%V is not a function or function reference", left);
+ ok.allok = 0;
+ break;
+ }
+ if(mod != nil && mod->ty->kind != Tmodule){
+ nerror(left, "cannot call %V", left);
+ ok.allok = 0;
+ break;
+ }
+ if(mod != nil){
+ if(valistype(mod)){
+ nerror(left, "cannot call %V because %V is a module interface", left, mod);
+ ok.allok = 0;
+ break;
+ }
+ }else if(id != nil && id->dot != nil && !isimpmod(id->dot->sym)){
+ nerror(left, "cannot call %V without importing %s from a variable", left, id->sym->name);
+ ok.allok = 0;
+ break;
+ }
+ if(mod != nil)
+ modrefable(left->ty);
+ if(callee != nil && callee->store != Dfn)
+ callee = nil;
+ if(t->varargs != 0){
+ t = mkvarargs(left, right);
+ if(left->ty->kind == Tref)
+ left->ty = usetype(mktype(&t->src.start, &t->src.stop, Tref, t, nil));
+ else
+ left->ty = t;
+ }
+ else if(ispoly(callee) || isfnrefty(left->ty) && left->ty->tof->polys != nil){
+ Tpair *tp;
+
+ unifysrc = n->src;
+ if(!argncompat(n, t->ids, right))
+ ok.allok = 0;
+ else if(!tunify(left->ty, calltype(left->ty, right, n->ty), &tp)){
+ nerror(n, "function call type mismatch");
+ ok.allok = 0;
+ }
+ else{
+ n->ty = usetype(expandtype(n->ty, nil, nil, &tp));
+ if(ispoly(callee) && tt != nil && (tt->kind == Tadt || tt->kind == Tadtpick) && (tt->flags&INST))
+ callee = rewcall(left, callee);
+ n->right = passfns(&n->src, callee, left, right, tt, tp);
+ }
+ }
+ else if(!argcompat(n, t->ids, right))
+ ok.allok = 0;
+ break;
+ }
+ case Odot:
+ t = left->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ switch(t->kind){
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ case Texception:
+ case Tpoly:
+ id = namedot(t->ids, right->decl->sym);
+ if(id == nil){
+ id = namedot(t->tags, right->decl->sym);
+ if(id != nil && !valistype(left)){
+ nerror(n, "%V is not a type", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ }
+ if(id == nil){
+ id = namedot(t->polys, right->decl->sym);
+ if(id != nil && !valistype(left)){
+ nerror(n, "%V is not a type", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ }
+ if(id == nil && t->kind == Tadtpick)
+ id = namedot(t->decl->dot->ty->ids, right->decl->sym);
+ if(id == nil){
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ id = namedot(tg->ty->ids, right->decl->sym);
+ if(id != nil)
+ break;
+ }
+ if(id != nil){
+ nerror(n, "cannot yet index field %s of %Q", right->decl->sym->name, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ }
+ if(id == nil)
+ break;
+ if(id->store == Dfield && valistype(left)){
+ nerror(n, "%V is not a value", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ id->ty = validtype(id->ty, t->decl);
+ id->ty = usetype(id->ty);
+ break;
+ default:
+ nerror(left, "%Q cannot be qualified with .", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id == nil){
+ nerror(n, "%V is not a member of %Q", right, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->ty == tunknown){
+ nerror(n, "illegal forward reference to %V", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+
+ increfs(id);
+ right->decl = id;
+ n->ty = id->ty;
+ if((id->store == Dconst || id->store == Dtag) && hasside(left, 1))
+ nwarn(left, "result of expression %Q ignored", left);
+ /* function name as function reference */
+ if(id->store == Dfn && (par == nil || (par->op != Omdot && par->op != Ocall && par->op != Ofunc)))
+ fnref(n, id);
+ break;
+ case Omdot:
+ t = left->ty;
+ if(t->kind != Tmodule){
+ nerror(left, "%Q cannot be qualified with ->", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ id = nil;
+ if(right->op == Oname){
+ id = namedot(t->ids, right->decl->sym);
+ }else if(right->op == Odot){
+ kidsok = echeck(right, 0, isglobal, n);
+ ok.ok = kidsok.ok;
+ ok.allok &= kidsok.allok;
+ if(!ok.ok){
+ ok.allok = 0;
+ return ok;
+ }
+ tt = right->left->ty;
+ if(tt->kind == Tref)
+ tt = tt->tof;
+ if(right->ty->kind == Tfn
+ && tt->kind == Tadt
+ && tt->decl->dot == t->decl)
+ id = right->right->decl;
+ }
+ if(id == nil){
+ nerror(n, "%V is not a member of %Q", right, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->store != Dconst && id->store != Dtype && id->store != Dtag){
+ if(valistype(left)){
+ nerror(n, "%V is not a value", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ }else if(hasside(left, 1))
+ nwarn(left, "result of expression %Q ignored", left);
+ if(!typeok && id->store == Dtype){
+ nerror(n, "%V is a type, not a value", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->ty == tunknown){
+ nerror(n, "illegal forward reference to %V", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ id->refs++;
+ right->decl = id;
+ n->ty = id->ty = usetype(id->ty);
+ if(id->store == Dglobal)
+ modrefable(id->ty);
+ /* function name as function reference */
+ if(id->store == Dfn && (par == nil || (par->op != Ocall && par->op != Ofunc)))
+ fnref(n, id);
+ break;
+ case Otagof:
+ n->ty = tint;
+ t = left->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ id = nil;
+ switch(left->op){
+ case Oname:
+ id = left->decl;
+ break;
+ case Odot:
+ id = left->right->decl;
+ break;
+ case Omdot:
+ if(left->right->op == Odot)
+ id = left->right->right->decl;
+ break;
+ }
+ if(id != nil && id->store == Dtag
+ || id != nil && id->store == Dtype && t->kind == Tadt && t->tags != nil)
+ n->decl = id;
+ else if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+ n->decl = nil;
+ else{
+ nerror(n, "cannot get the tag value for %Q", left);
+ ok.ok = 1;
+ ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oind:
+ t = left->ty;
+ if(t->kind != Tref || (t->tof->kind != Tadt && t->tof->kind != Tadtpick && t->tof->kind != Ttuple)){
+ nerror(n, "cannot * %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = t->tof;
+ for(tg = t->tof->tags; tg != nil; tg = tg->next)
+ tg->ty->tof = usetype(tg->ty->tof);
+ break;
+ case Oindex:
+ if(valistype(left)){
+ tagopt = 1;
+ kidsok = echeck(right, 2, isglobal, n);
+ tagopt = 0;
+ if(!kidsok.allok){
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if((t = exptotype(n)) == nil){
+ nerror(n, "%V is not a type list", right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!typeok){
+ nerror(n, "%Q is not a variable", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ *n = *(n->left);
+ n->ty = usetype(t);
+ break;
+ }
+ if(0 && right->op == Oseq){ /* a[e1, e2, ...] */
+ /* array creation to do before we allow this */
+ rewind(n);
+ return echeck(n, typeok, isglobal, par);
+ }
+ t = left->ty;
+ kidsok = echeck(right, 0, isglobal, n);
+ if(t->kind != Tarray && t != tstring){
+ nerror(n, "cannot index %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(t == tstring){
+ n->op = Oinds;
+ n->ty = tint;
+ }else{
+ n->ty = t->tof;
+ }
+ if(!kidsok.allok){
+ ok.allok = 0;
+ break;
+ }
+ if(right->ty != tint){
+ nerror(n, "cannot index %Q with %Q", left, right);
+ ok.allok = 0;
+ break;
+ }
+ break;
+ case Oslice:
+ t = n->ty = left->ty;
+ if(t->kind != Tarray && t != tstring){
+ nerror(n, "cannot slice %Q with '%v:%v'", left, right->left, right->right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(right->left->ty != tint && right->left->op != Onothing
+ || right->right->ty != tint && right->right->op != Onothing){
+ nerror(n, "cannot slice %Q with '%v:%v'", left, right->left, right->right);
+ ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Olen:
+ t = left->ty;
+ n->ty = tint;
+ if(t->kind != Tarray && t->kind != Tlist && t != tstring){
+ nerror(n, "len requires an array, string or list in %Q", left);
+ ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Ocomp:
+ case Onot:
+ case Oneg:
+ n->ty = left->ty;
+ usedty(n->ty);
+ switch(left->ty->kind){
+ case Tint:
+ return ok;
+ case Treal:
+ case Tfix:
+ if(n->op == Oneg)
+ return ok;
+ break;
+ case Tbig:
+ case Tbyte:
+ if(n->op == Oneg || n->op == Ocomp)
+ return ok;
+ break;
+ }
+ nerror(n, "cannot apply %O to %Q", n->op, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oinc:
+ case Odec:
+ case Opreinc:
+ case Opredec:
+ n->ty = left->ty;
+ switch(left->ty->kind){
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ break;
+ default:
+ nerror(n, "cannot apply %O to %Q", n->op, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oadd:
+ case Odiv:
+ case Omul:
+ case Osub:
+ if(mathchk(n, 1))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oexp:
+ case Oexpas:
+ n->ty = left->ty;
+ if(n->ty != tint && n->ty != tbig && n->ty != treal){
+ nerror(n, "exponend %Q is not int, big or real", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(right->ty != tint){
+ nerror(n, "exponent %Q is not int", right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(n->op == Oexpas && !islval(left)){
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Olsh:
+ case Orsh:
+ if(shiftchk(n))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oandand:
+ case Ooror:
+ if(left->ty != tint){
+ nerror(n, "%O's left operand is not an int: %Q", n->op, left);
+ ok.allok = 0;
+ }
+ if(right->ty != tint){
+ nerror(n, "%O's right operand is not an int: %Q", n->op, right);
+ ok.allok = 0;
+ }
+ n->ty = tint;
+ break;
+ case Oand:
+ case Omod:
+ case Oor:
+ case Oxor:
+ if(mathchk(n, 0))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oaddas:
+ case Odivas:
+ case Omulas:
+ case Osubas:
+ if(mathchk(n, 1) && islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Olshas:
+ case Orshas:
+ if(shiftchk(n) && islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oandas:
+ case Omodas:
+ case Oxoras:
+ case Ooras:
+ if(mathchk(n, 0) && islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Olt:
+ case Oleq:
+ case Ogt:
+ case Ogeq:
+ if(!mathchk(n, 1)){
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = tint;
+ break;
+ case Oeq:
+ case Oneq:
+ switch(left->ty->kind){
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ case Tstring:
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ case Tany:
+ case Tmodule:
+ case Tfix:
+ case Tpoly:
+ if(!tcompat(left->ty, right->ty, 0) && !tcompat(right->ty, left->ty, 0))
+ break;
+ t = left->ty;
+ if(t == tany)
+ t = right->ty;
+ if(t == tany)
+ t = tint;
+ if(left->ty == tany)
+ left->ty = t;
+ if(right->ty == tany)
+ right->ty = t;
+ n->ty = tint;
+ return ok;
+ }
+ nerror(n, "cannot compare %Q to %Q", left, right);
+ usedty(n->ty);
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Otype:
+ if(!typeok){
+ nerror(n, "%Q is not a variable", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = usetype(n->ty);
+ break;
+ default:
+ fatal("unknown op in typecheck: %O", n->op);
+ }
+ usedty(n->ty);
+ return ok;
+}
+
+/*
+ * n is syntactically a call, but n->left is not a fn
+ * check if it's the contructor for an adt
+ */
+Ok
+callcast(Node *n, int kidsok, int allok)
+{
+ Node *left, *right;
+ Decl *id;
+ Type *t, *tt;
+ Ok ok;
+
+ left = n->left;
+ right = n->right;
+ id = nil;
+ switch(left->op){
+ case Oname:
+ id = left->decl;
+ break;
+ case Omdot:
+ if(left->right->op == Odot)
+ id = left->right->right->decl;
+ else
+ id = left->right->decl;
+ break;
+ case Odot:
+ id = left->right->decl;
+ break;
+ }
+/*
+ (chan of int)(nil) looks awkward since both sets of brackets needed
+ if(id == nil && right != nil && right->right == nil && (t = exptotype(left)) != nil){
+ n->op = Ocast;
+ n->left = right->left;
+ n->right = nil;
+ n->ty = t;
+ return echeck(n, 0, 0, nil);
+ }
+*/
+ if(id == nil || (id->store != Dtype && id->store != Dtag && id->ty->kind != Texception)){
+ nerror(left, "%V is not a function or type name", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->store == Dtag)
+ return tagcast(n, left, right, id, kidsok, allok);
+ t = left->ty;
+ n->ty = t;
+ if(!kidsok){
+ ok.ok = 1;
+ ok.allok = 0;
+ return ok;
+ }
+
+ if(t->kind == Tref)
+ t = t->tof;
+ tt = mktype(&n->src.start, &n->src.stop, Ttuple, nil, tuplefields(right));
+ if(t->kind == Tadt && tcompat(t, tt, 1)){
+ if(right == nil)
+ *n = *n->left;
+ ok.ok = 1;
+ ok.allok = allok;
+ return ok;
+ }
+
+ /* try an exception with args */
+ tt = mktype(&n->src.start, &n->src.stop, Texception, nil, tuplefields(right));
+ tt->cons = 1;
+ if(t->kind == Texception && t->cons && tcompat(t, tt, 1)){
+ if(right == nil)
+ *n = *n->left;
+ ok.ok = 1;
+ ok.allok = allok;
+ return ok;
+ }
+
+ /* try a cast */
+ if(t->kind != Texception && right != nil && right->right == nil){ /* Oseq but single expression */
+ right = right->left;
+ n->op = Ocast;
+ n->left = right;
+ n->right = nil;
+ n->ty = mkidtype(&n->src, id->sym);
+ return echeck(n, 0, 0, nil);
+ }
+
+ nerror(left, "cannot make a %V from '(%v)'", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+}
+
+Ok
+tagcast(Node *n, Node *left, Node *right, Decl *id, int kidsok, int allok)
+{
+ Type *tt;
+ Ok ok;
+
+ left->ty = id->ty;
+ if(left->op == Omdot)
+ left->right->ty = id->ty;
+ n->ty = id->ty;
+ if(!kidsok){
+ ok.ok = 1;
+ ok.allok = 0;
+ return ok;
+ }
+ id->ty->tof = usetype(id->ty->tof);
+ if(right != nil)
+ right->ty = id->ty->tof;
+ tt = mktype(&n->src.start, &n->src.stop, Ttuple, nil, mkids(&nosrc, nil, tint, tuplefields(right)));
+ tt->ids->store = Dfield;
+ if(tcompat(id->ty->tof, tt, 1)){
+ ok.ok = 1;
+ ok.allok = allok;
+ return ok;
+ }
+
+ nerror(left, "cannot make a %V from '(%v)'", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+}
+
+int
+valistype(Node *n)
+{
+ switch(n->op){
+ case Oname:
+ if(n->decl->store == Dtype)
+ return 1;
+ break;
+ case Omdot:
+ return valistype(n->right);
+ }
+ return 0;
+}
+
+int
+islval(Node *n)
+{
+ int s;
+
+ s = marklval(n);
+ if(s == 1)
+ return 1;
+ if(s == 0)
+ nerror(n, "cannot assign to %V", n);
+ else
+ circlval(n, n);
+ return 0;
+}
+
+/*
+ * check to see if n is an lval
+ * mark the lval name as set
+ */
+int
+marklval(Node *n)
+{
+ Decl *id;
+ Node *nn;
+ int s;
+
+ if(n == nil)
+ return 0;
+ switch(n->op){
+ case Oname:
+ return storespace[n->decl->store] && n->ty->kind != Texception; /*ZZZZ && n->decl->tagged == nil;*/
+ case Odot:
+ if(n->right->decl->store != Dfield)
+ return 0;
+ if(n->right->decl->cycle && !n->right->decl->cyc)
+ return -1;
+ if(n->left->ty->kind != Tref && marklval(n->left) == 0)
+ nwarn(n, "assignment to %Q ignored", n);
+ return 1;
+ case Omdot:
+ if(n->right->decl->store == Dglobal)
+ return 1;
+ return 0;
+ case Oind:
+ for(id = n->ty->ids; id != nil; id = id->next)
+ if(id->cycle && !id->cyc)
+ return -1;
+ return 1;
+ case Oslice:
+ if(n->right->right->op != Onothing || n->ty == tstring)
+ return 0;
+ return 1;
+ case Oinds:
+ /*
+ * make sure we don't change a string constant
+ */
+ switch(n->left->op){
+ case Oconst:
+ return 0;
+ case Oname:
+ return storespace[n->left->decl->store];
+ case Odot:
+ case Omdot:
+ if(n->left->right->decl != nil)
+ return storespace[n->left->right->decl->store];
+ break;
+ }
+ return 1;
+ case Oindex:
+ case Oindx:
+ return 1;
+ case Otuple:
+ for(nn = n->left; nn != nil; nn = nn->right){
+ s = marklval(nn->left);
+ if(s != 1)
+ return s;
+ }
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * n has a circular field assignment.
+ * find it and print an error message.
+ */
+int
+circlval(Node *n, Node *lval)
+{
+ Decl *id;
+ Node *nn;
+ int s;
+
+ if(n == nil)
+ return 0;
+ switch(n->op){
+ case Oname:
+ break;
+ case Odot:
+ if(oldcycles && n->right->decl->cycle && !n->right->decl->cyc){
+ nerror(lval, "cannot assign to %V because field '%s' of %V could complete a cycle to %V",
+ lval, n->right->decl->sym->name, n->left, n->left);
+ return -1;
+ }
+ return 1;
+ case Oind:
+ if(!oldcycles)
+ return 1;
+ for(id = n->ty->ids; id != nil; id = id->next){
+ if(id->cycle && !id->cyc){
+ nerror(lval, "cannot assign to %V because field '%s' of %V could complete a cycle to %V",
+ lval, id->sym->name, n, n);
+ return -1;
+ }
+ }
+ return 1;
+ case Oslice:
+ if(n->right->right->op != Onothing || n->ty == tstring)
+ return 0;
+ return 1;
+ case Oindex:
+ case Oinds:
+ case Oindx:
+ return 1;
+ case Otuple:
+ for(nn = n->left; nn != nil; nn = nn->right){
+ s = circlval(nn->left, lval);
+ if(s != 1)
+ return s;
+ }
+ return 1;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+int
+mathchk(Node *n, int realok)
+{
+ Type *tr, *tl;
+
+ tl = n->left->ty;
+ tr = n->right->ty;
+ if(tr != tl && !tequal(tl, tr)){
+ nerror(n, "type clash in %Q %O %Q", n->left, n->op, n->right);
+ return 0;
+ }
+ n->ty = tr;
+ switch(tr->kind){
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ return 1;
+ case Tstring:
+ switch(n->op){
+ case Oadd:
+ case Oaddas:
+ case Ogt:
+ case Ogeq:
+ case Olt:
+ case Oleq:
+ return 1;
+ }
+ break;
+ case Treal:
+ case Tfix:
+ if(realok)
+ return 1;
+ break;
+ }
+ nerror(n, "cannot %O %Q and %Q", n->op, n->left, n->right);
+ return 0;
+}
+
+int
+shiftchk(Node *n)
+{
+ Node *left, *right;
+
+ right = n->right;
+ left = n->left;
+ n->ty = left->ty;
+ switch(n->ty->kind){
+ case Tint:
+ case Tbyte:
+ case Tbig:
+ if(right->ty->kind != Tint){
+ nerror(n, "shift %Q is not an int", right);
+ return 0;
+ }
+ return 1;
+ }
+ nerror(n, "cannot %Q %O %Q", left, n->op, right);
+ return 0;
+}
+
+/*
+ * check for any tany's in t
+ */
+int
+specific(Type *t)
+{
+ Decl *d;
+
+ if(t == nil)
+ return 0;
+ switch(t->kind){
+ case Terror:
+ case Tnone:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Tbyte:
+ case Treal:
+ case Tfn:
+ case Tadt:
+ case Tadtpick:
+ case Tmodule:
+ case Tfix:
+ return 1;
+ case Tany:
+ return 0;
+ case Tpoly:
+ return 1;
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ return specific(t->tof);
+ case Ttuple:
+ case Texception:
+ for(d = t->ids; d != nil; d = d->next)
+ if(!specific(d->ty))
+ return 0;
+ return 1;
+ }
+ fatal("unknown type %T in specific", t);
+ return 0;
+}
+
+/*
+ * infer the type of all variable in n from t
+ * n is the left-hand exp of a := exp
+ */
+int
+declasinfer(Node *n, Type *t)
+{
+ Decl *ids;
+ int ok;
+
+ if(t->kind == Texception){
+ if(t->cons)
+ return 0;
+ t = mkextuptype(t);
+ }
+ switch(n->op){
+ case Otuple:
+ if(t->kind != Ttuple && t->kind != Tadt && t->kind != Tadtpick)
+ return 0;
+ ok = 1;
+ n->ty = t;
+ n = n->left;
+ ids = t->ids;
+ if(t->kind == Tadtpick)
+ ids = t->tof->ids->next;
+ for(; n != nil && ids != nil; ids = ids->next){
+ if(ids->store != Dfield)
+ continue;
+ ok &= declasinfer(n->left, ids->ty);
+ n = n->right;
+ }
+ for(; ids != nil; ids = ids->next)
+ if(ids->store == Dfield)
+ break;
+ if(n != nil || ids != nil)
+ return 0;
+ return ok;
+ case Oname:
+ topvartype(t, n->decl, 0, 0);
+ if(n->decl == nildecl)
+ return 1;
+ n->decl->ty = t;
+ n->ty = t;
+ shareloc(n->decl);
+ return 1;
+ }
+ fatal("unknown op %n in declasinfer", n);
+ return 0;
+}
+
+/*
+ * an error occured in declaring n;
+ * set all decl identifiers to Dwundef
+ * so further errors are squashed.
+ */
+void
+declaserr(Node *n)
+{
+ switch(n->op){
+ case Otuple:
+ for(n = n->left; n != nil; n = n->right)
+ declaserr(n->left);
+ return;
+ case Oname:
+ if(n->decl != nildecl)
+ n->decl->store = Dwundef;
+ return;
+ }
+ fatal("unknown op %n in declaserr", n);
+}
+
+int
+argcompat(Node *n, Decl *f, Node *a)
+{
+ for(; a != nil; a = a->right){
+ if(f == nil){
+ nerror(n, "%V: too many function arguments", n->left);
+ return 0;
+ }
+ if(!tcompat(f->ty, a->left->ty, 0)){
+ nerror(n, "%V: argument type mismatch: expected %T saw %Q",
+ n->left, f->ty, a->left);
+ return 0;
+ }
+ if(a->left->ty == tany)
+ a->left->ty = f->ty;
+ f = f->next;
+ }
+ if(f != nil){
+ nerror(n, "%V: too few function arguments", n->left);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * fn is Odot(adt, methid)
+ * pass adt implicitly if needed
+ * if not, any side effect of adt will be ignored
+ */
+Node*
+passimplicit(Node *fn, Node *args)
+{
+ Node *n;
+ Type *t;
+
+ t = fn->ty;
+ n = fn->left;
+ if(t->ids == nil || !t->ids->implicit){
+ if(!isfnrefty(t) && hasside(n, 1))
+ nwarn(fn, "result of expression %V ignored", n);
+ return args;
+ }
+ if(n->op == Oname && n->decl->store == Dtype){
+ nerror(n, "%V is a type and cannot be a self argument", n);
+ n = mkn(Onothing, nil, nil);
+ n->src = fn->src;
+ n->ty = t->ids->ty;
+ }
+ args = mkn(Oseq, n, args);
+ args->src = n->src;
+ return args;
+}
+
+static int
+mem(Type *t, Decl *d)
+{
+ for( ; d != nil; d = d->next)
+ if(d->ty == t) /* was if(d->ty == t || tequal(d->ty, t)) */
+ return 1;
+ return 0;
+}
+
+static int
+memp(Type *t, Decl *f)
+{
+ return mem(t, f->ty->polys) || mem(t, encpolys(f));
+}
+
+static void
+passfns0(Src *src, Decl *fn, Node *args0, Node **args, Node **a, Tpair *tp, Decl *polys)
+{
+ Decl *id, *idt, *idf, *dot;
+ Type *tt;
+ Sym *sym;
+ Node *n, *na, *mod;
+ Tpair *p;
+
+if(debug['w']){
+ print("polys: ");
+ for(id=polys; id!=nil; id=id->next) print("%s ", id->sym->name);
+ print("\nmap: ");
+ for(p=tp; p!=nil; p=p->nxt) print("%T -> %T ", p->t1, p->t2);
+ print("\n");
+}
+ for(idt = polys; idt != nil; idt = idt->next){
+ tt = valtmap(idt->ty, tp);
+ if(tt->kind == Tpoly && fndec != nil && !memp(tt, fndec))
+ error(src->start, "cannot determine the instantiated type of %T", tt);
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+ sym = idf->sym;
+ id = fnlookup(sym, tt, &mod);
+ while(id != nil && id->link != nil)
+ id = id->link;
+if(debug['v']) print("fnlookup: %p\n", id);
+ if(id == nil) /* error flagged already */
+ continue;
+ id->refs++;
+ id->caninline = -1;
+ if(tt->kind == Tmodule){ /* mod an actual parameter */
+ for(;;){
+ if(args0 != nil && tequal(tt, args0->left->ty)){
+ mod = args0->left;
+ break;
+ }
+ if(args0 == nil)
+ break;
+ args0 = args0->right;
+ }
+ }
+ if(mod == nil && (dot = module(id)) != nil && !isimpmod(dot->sym))
+ error(src->start, "cannot use %s without importing %s from a variable", id->sym->name, id->dot->sym->name);
+
+if(debug['U']) print("fp: %s %s %s\n", fn->sym->name, mod ? mod->decl->sym->name : "nil", id->sym->name);
+ n = mkn(Ofnptr, mod, mkdeclname(src, id));
+ n->src = *src;
+ n->decl = fn;
+ if(tt->kind == Tpoly)
+ n->flags = FNPTRA;
+ else
+ n->flags = 0;
+ na = mkn(Oseq, n, nil);
+ if(*a == nil)
+ *args = na;
+ else
+ (*a)->right = na;
+
+ n = mkn(Ofnptr, mod, mkdeclname(src, id));
+ n->src = *src;
+ n->decl = fn;
+ if(tt->kind == Tpoly)
+ n->flags = FNPTRA|FNPTR2;
+ else
+ n->flags = FNPTR2;
+ *a = na->right = mkn(Oseq, n, nil);
+ }
+ if(args0 != nil)
+ args0 = args0->right;
+ }
+}
+
+Node*
+passfns(Src *src, Decl *fn, Node *left, Node *args, Type *adt, Tpair *tp)
+{
+ Node *a, *args0;
+
+ a = nil;
+ args0 = args;
+ if(args != nil)
+ for(a = args; a->right != nil; a = a->right)
+ ;
+ passfns0(src, fn, args0, &args, &a, tp, ispoly(fn) ? fn->ty->polys : left->ty->tof->polys);
+ if(adt != nil)
+ passfns0(src, fn, args0, &args, &a, adt->u.tmap, ispoly(fn) ? encpolys(fn) : nil);
+ return args;
+}
+
+/*
+ * check the types for a function with a variable number of arguments
+ * last typed argument must be a constant string, and must use the
+ * print format for describing arguments.
+ */
+Type*
+mkvarargs(Node *n, Node *args)
+{
+ Node *s, *a;
+ Decl *f, *last, *va;
+ Type *nt;
+
+ nt = copytypeids(n->ty);
+ n->ty = nt;
+ f = n->ty->ids;
+ last = nil;
+ if(f == nil){
+ nerror(n, "%V's type is illegal", n);
+ return nt;
+ }
+ s = args;
+ for(a = args; a != nil; a = a->right){
+ if(f == nil)
+ break;
+ if(!tcompat(f->ty, a->left->ty, 0)){
+ nerror(n, "%V: argument type mismatch: expected %T saw %Q",
+ n, f->ty, a->left);
+ return nt;
+ }
+ if(a->left->ty == tany)
+ a->left->ty = f->ty;
+ last = f;
+ f = f->next;
+ s = a;
+ }
+ if(f != nil){
+ nerror(n, "%V: too few function arguments", n);
+ return nt;
+ }
+
+ s->left = fold(s->left);
+ s = s->left;
+ if(s->ty != tstring || s->op != Oconst){
+ nerror(args, "%V: format argument %Q is not a string constant", n, s);
+ return nt;
+ }
+ fmtcheck(n, s, a);
+ va = tuplefields(a);
+ if(last == nil)
+ nt->ids = va;
+ else
+ last->next = va;
+ return nt;
+}
+
+/*
+ * check that a print style format string matches its arguments
+ */
+void
+fmtcheck(Node *f, Node *fmtarg, Node *va)
+{
+ Sym *fmt;
+ Rune r;
+ char *s, flags[10];
+ int i, c, n1, n2, dot, verb, flag, ns, lens, fmtstart;
+ Type *ty;
+
+ fmt = fmtarg->decl->sym;
+ s = fmt->name;
+ lens = fmt->len;
+ ns = 0;
+ while(ns < lens){
+ c = s[ns++];
+ if(c != '%')
+ continue;
+
+ verb = -1;
+ n1 = 0;
+ n2 = 0;
+ dot = 0;
+ flag = 0;
+ fmtstart = ns - 1;
+ while(ns < lens && verb < 0){
+ c = s[ns++];
+ switch(c){
+ default:
+ chartorune(&r, &s[ns-1]);
+ nerror(f, "%V: invalid character %C in format '%.*s'", f, r, ns-fmtstart, &s[fmtstart]);
+ return;
+ case '.':
+ if(dot){
+ nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ n1 = 1;
+ dot = 1;
+ continue;
+ case '*':
+ if(!n1)
+ n1 = 1;
+ else if(!n2 && dot)
+ n2 = 1;
+ else{
+ nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ if(va == nil){
+ nerror(f, "%V: too few arguments for format '%.*s'",
+ f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ if(va->left->ty->kind != Tint){
+ nerror(f, "%V: format '%.*s' incompatible with argument %Q",
+ f, ns-fmtstart, &s[fmtstart], va->left);
+ return;
+ }
+ va = va->right;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ while(ns < lens && s[ns] >= '0' && s[ns] <= '9')
+ ns++;
+ if(!n1)
+ n1 = 1;
+ else if(!n2 && dot)
+ n2 = 1;
+ else{
+ nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ break;
+ case '+':
+ case '-':
+ case '#':
+ case ',':
+ case 'b':
+ case 'u':
+ for(i = 0; i < flag; i++){
+ if(flags[i] == c){
+ nerror(f, "%V: duplicate flag %c in format '%.*s'",
+ f, c, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ }
+ flags[flag++] = c;
+ if(flag >= sizeof flags){
+ nerror(f, "too many flags in format '%.*s'", ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ break;
+ case '%':
+ case 'r':
+ verb = Tnone;
+ break;
+ case 'H':
+ verb = Tany;
+ break;
+ case 'c':
+ verb = Tint;
+ break;
+ case 'd':
+ case 'o':
+ case 'x':
+ case 'X':
+ verb = Tint;
+ for(i = 0; i < flag; i++){
+ if(flags[i] == 'b'){
+ verb = Tbig;
+ break;
+ }
+ }
+ break;
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'E':
+ case 'G':
+ verb = Treal;
+ break;
+ case 's':
+ case 'q':
+ verb = Tstring;
+ break;
+ }
+ }
+ if(verb != Tnone){
+ if(verb < 0){
+ nerror(f, "%V: incomplete format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ if(va == nil){
+ nerror(f, "%V: too few arguments for format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ ty = va->left->ty;
+ if(ty->kind == Texception)
+ ty = mkextuptype(ty);
+ switch(verb){
+ case Tint:
+ switch(ty->kind){
+ case Tstring:
+ case Tarray:
+ case Tref:
+ case Tchan:
+ case Tlist:
+ case Tmodule:
+ if(c == 'x' || c == 'X')
+ verb = ty->kind;
+ break;
+ }
+ break;
+ case Tany:
+ if(tattr[ty->kind].isptr)
+ verb = ty->kind;
+ break;
+ }
+ if(verb != ty->kind){
+ nerror(f, "%V: format '%.*s' incompatible with argument %Q", f, ns-fmtstart, &s[fmtstart], va->left);
+ return;
+ }
+ va = va->right;
+ }
+ }
+ if(va != nil)
+ nerror(f, "%V: more arguments than formats", f);
+}
+
+Decl*
+tuplefields(Node *n)
+{
+ Decl *d, *h, **last;
+
+ h = nil;
+ last = &h;
+ for(; n != nil; n = n->right){
+ d = mkdecl(&n->left->src, Dfield, n->left->ty);
+ *last = d;
+ last = &d->next;
+ }
+ return h;
+}
+
+/*
+ * make explicit indices for every element in an array initializer
+ * return the maximum index
+ * sort the indices and check for duplicates
+ */
+int
+assignindices(Node *ar)
+{
+ Node *wild, *off, *size, *inits, *n, *q;
+ Type *t;
+ Case *c;
+ int amax, max, last, nlab, ok;
+
+ amax = 0x7fffffff;
+ size = dupn(0, &nosrc, ar->left);
+ if(size->ty == tint){
+ size = fold(size);
+ if(size->op == Oconst)
+ amax = size->val;
+ }
+
+ inits = ar->right;
+ max = -1;
+ last = -1;
+ t = inits->left->ty;
+ wild = nil;
+ nlab = 0;
+ ok = 1;
+ for(n = inits; n != nil; n = n->right){
+ if(!tcompat(t, n->left->ty, 0)){
+ t = tparent(t, n->left->ty);
+ if(!tcompat(t, n->left->ty, 0)){
+ nerror(n->left, "inconsistent types %T and %T and in array initializer", t, n->left->ty);
+ return -1;
+ }
+ else
+ inits->left->ty = t;
+ }
+ if(t == tany)
+ t = n->left->ty;
+
+ /*
+ * make up an index if there isn't one
+ */
+ if(n->left->left == nil)
+ n->left->left = mkn(Oseq, mkconst(&n->left->right->src, last + 1), nil);
+
+ for(q = n->left->left; q != nil; q = q->right){
+ off = q->left;
+ if(off->ty != tint){
+ nerror(off, "array index %Q is not an int", off);
+ ok = 0;
+ continue;
+ }
+ off = fold(off);
+ switch(off->op){
+ case Owild:
+ if(wild != nil)
+ nerror(off, "array index * duplicated on line %L", wild->src.start);
+ wild = off;
+ continue;
+ case Orange:
+ if(off->left->op != Oconst || off->right->op != Oconst){
+ nerror(off, "range %V is not constant", off);
+ off = nil;
+ }else if(off->left->val < 0 || off->right->val >= amax){
+ nerror(off, "array index %V out of bounds", off);
+ off = nil;
+ }else
+ last = off->right->val;
+ break;
+ case Oconst:
+ last = off->val;
+ if(off->val < 0 || off->val >= amax){
+ nerror(off, "array index %V out of bounds", off);
+ off = nil;
+ }
+ break;
+ case Onothing:
+ /* get here from a syntax error */
+ off = nil;
+ break;
+ default:
+ nerror(off, "array index %V is not constant", off);
+ off = nil;
+ break;
+ }
+
+ nlab++;
+ if(off == nil){
+ off = mkconst(&n->left->right->src, last);
+ ok = 0;
+ }
+ if(last > max)
+ max = last;
+ q->left = off;
+ }
+ }
+
+ /*
+ * fix up types of nil elements
+ */
+ for(n = inits; n != nil; n = n->right)
+ if(n->left->ty == tany)
+ n->left->ty = t;
+
+ if(!ok)
+ return -1;
+
+
+ c = checklabels(inits, tint, nlab, "array index");
+ t = mktype(&inits->src.start, &inits->src.stop, Tainit, nil, nil);
+ inits->ty = t;
+ t->cse = c;
+
+ return max + 1;
+}
+
+/*
+ * check the labels of a case statment
+ */
+void
+casecheck(Node *cn, Type *ret)
+{
+ Node *n, *q, *wild, *left, *arg;
+ Type *t;
+ Case *c;
+ Ok rok;
+ int nlab, ok, op;
+
+ rok = echeck(cn->left, 0, 0, nil);
+ cn->right = scheck(cn->right, ret, Sother);
+ if(!rok.ok)
+ return;
+ arg = cn->left;
+
+ t = arg->ty;
+ if(t != tint && t != tbig && t != tstring){
+ nerror(cn, "case argument %Q is not an int or big or string", arg);
+ return;
+ }
+
+ wild = nil;
+ nlab= 0;
+ ok = 1;
+ for(n = cn->right; n != nil; n = n->right){
+ q = n->left->left;
+ if(n->left->right->right == nil)
+ nwarn(q, "no body for case qualifier %V", q);
+ for(; q != nil; q = q->right){
+ left = fold(q->left);
+ q->left = left;
+ switch(left->op){
+ case Owild:
+ if(wild != nil)
+ nerror(left, "case qualifier * duplicated on line %L", wild->src.start);
+ wild = left;
+ break;
+ case Orange:
+ if(left->ty != t)
+ nerror(left, "case qualifier %Q clashes with %Q", left, arg);
+ else if(left->left->op != Oconst || left->right->op != Oconst){
+ nerror(left, "case range %V is not constant", left);
+ ok = 0;
+ }
+ nlab++;
+ break;
+ default:
+ if(left->ty != t){
+ nerror(left, "case qualifier %Q clashes with %Q", left, arg);
+ ok = 0;
+ }else if(left->op != Oconst){
+ nerror(left, "case qualifier %V is not constant", left);
+ ok = 0;
+ }
+ nlab++;
+ break;
+ }
+ }
+ }
+
+ if(!ok)
+ return;
+
+ c = checklabels(cn->right, t, nlab, "case qualifier");
+ op = Tcase;
+ if(t == tbig)
+ op = Tcasel;
+ else if(t == tstring)
+ op = Tcasec;
+ t = mktype(&cn->src.start, &cn->src.stop, op, nil, nil);
+ cn->ty = t;
+ t->cse = c;
+}
+
+/*
+ * check the labels and bodies of a pick statment
+ */
+void
+pickcheck(Node *n, Type *ret)
+{
+ Node *w, *arg, *qs, *q, *qt, *left, **tags;
+ Decl *id, *d;
+ Type *t, *argty;
+ Case *c;
+ Ok rok;
+ int ok, nlab;
+
+ arg = n->left->right;
+ rok = echeck(arg, 0, 0, nil);
+ if(!rok.allok)
+ return;
+ t = arg->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ if(arg->ty->kind != Tref || t->kind != Tadt || t->tags == nil){
+ nerror(arg, "pick argument %Q is not a ref adt with pick tags", arg);
+ return;
+ }
+ argty = usetype(mktype(&arg->ty->src.start, &arg->ty->src.stop, Tref, t, nil));
+
+ arg = n->left->left;
+ pushscope(nil, Sother);
+ dasdecl(arg);
+ arg->decl->ty = argty;
+ arg->ty = argty;
+
+ tags = allocmem(t->decl->tag * sizeof *tags);
+ memset(tags, 0, t->decl->tag * sizeof *tags);
+ w = nil;
+ ok = 1;
+ nlab = 0;
+ for(qs = n->right; qs != nil; qs = qs->right){
+ qt = nil;
+ for(q = qs->left->left; q != nil; q = q->right){
+ left = q->left;
+ switch(left->op){
+ case Owild:
+ /* left->ty = tnone; */
+ left->ty = t;
+ if(w != nil)
+ nerror(left, "pick qualifier * duplicated on line %L", w->src.start);
+ w = left;
+ break;
+ case Oname:
+ id = namedot(t->tags, left->decl->sym);
+ if(id == nil){
+ nerror(left, "pick qualifier %V is not a member of %Q", left, arg);
+ ok = 0;
+ continue;
+ }
+
+ left->decl = id;
+ left->ty = id->ty;
+
+ if(tags[id->tag] != nil){
+ nerror(left, "pick qualifier %V duplicated on line %L",
+ left, tags[id->tag]->src.start);
+ ok = 0;
+ }
+ tags[id->tag] = left;
+ nlab++;
+ break;
+ default:
+ fatal("pickcheck can't handle %n", q);
+ break;
+ }
+
+ if(qt == nil)
+ qt = left;
+ else if(!tequal(qt->ty, left->ty))
+ nerror(left, "type clash in pick qualifiers %Q and %Q", qt, left);
+ }
+
+ argty->tof = t;
+ if(qt != nil)
+ argty->tof = qt->ty;
+ qs->left->right = scheck(qs->left->right, ret, Sother);
+ if(qs->left->right == nil)
+ nwarn(qs->left->left, "no body for pick qualifier %V", qs->left->left);
+ }
+ argty->tof = t;
+ for(qs = n->right; qs != nil; qs = qs->right)
+ for(q = qs->left->left; q != nil; q = q->right)
+ q->left = fold(q->left);
+
+ d = popscope();
+ d->refs++;
+ if(d->next != nil)
+ fatal("pickcheck: installing more than one id");
+ fndecls = appdecls(fndecls, d);
+
+ if(!ok)
+ return;
+
+ c = checklabels(n->right, tint, nlab, "pick qualifier");
+ t = mktype(&n->src.start, &n->src.stop, Tcase, nil, nil);
+ n->ty = t;
+ t->cse = c;
+}
+
+void
+exccheck(Node *en, Type *ret)
+{
+ Decl *ed;
+ Node *n, *q, *wild, *left, *oinexcept;
+ Type *t, *qt;
+ Case *c;
+ int nlab, ok;
+ Ok rok;
+ char buf[32];
+ static int nexc;
+
+ pushscope(nil, Sother);
+ if(en->left == nil){
+ seprint(buf, buf+sizeof(buf), ".ex%d", nexc++);
+ en->left = mkdeclname(&en->src, mkids(&en->src, enter(buf, 0), texception, nil));
+ }
+ oinexcept = inexcept;
+ inexcept = en->left;
+ dasdecl(en->left);
+ en->left->ty = en->left->decl->ty = texception;
+ ed = en->left->decl;
+ /* en->right = scheck(en->right, ret, Sother); */
+ t = tstring;
+ wild = nil;
+ nlab = 0;
+ ok = 1;
+ for(n = en->right; n != nil; n = n->right){
+ qt = nil;
+ for(q = n->left->left; q != nil; q = q->right){
+ left = q->left;
+ switch(left->op){
+ case Owild:
+ left->ty = texception;
+ if(wild != nil)
+ nerror(left, "exception qualifier * duplicated on line %L", wild->src.start);
+ wild = left;
+ break;
+ case Orange:
+ left->ty = tnone;
+ nerror(left, "exception qualifier %V is illegal", left);
+ ok = 0;
+ break;
+ default:
+ rok = echeck(left, 0, 0, nil);
+ if(!rok.ok){
+ ok = 0;
+ break;
+ }
+ left = q->left = fold(left);
+ if(left->ty != t && left->ty->kind != Texception){
+ nerror(left, "exception qualifier %Q is not a string or exception", left);
+ ok = 0;
+ }else if(left->op != Oconst){
+ nerror(left, "exception qualifier %V is not constant", left);
+ ok = 0;
+ }
+ else if(left->ty != t)
+ left->ty = mkextype(left->ty);
+ nlab++;
+ break;
+ }
+
+ if(qt == nil)
+ qt = left->ty;
+ else if(!tequal(qt, left->ty))
+ qt = texception;
+ }
+
+ if(qt != nil)
+ ed->ty = qt;
+ n->left->right = scheck(n->left->right, ret, Sother);
+ if(n->left->right->right == nil)
+ nwarn(n->left->left, "no body for exception qualifier %V", n->left->left);
+ }
+ ed->ty = texception;
+ inexcept = oinexcept;
+ if(!ok)
+ return;
+ c = checklabels(en->right, texception, nlab, "exception qualifier");
+ t = mktype(&en->src.start, &en->src.stop, Texcept, nil, nil);
+ en->ty = t;
+ t->cse = c;
+ ed = popscope();
+ fndecls = appdecls(fndecls, ed);
+}
+
+/*
+ * check array and case labels for validity
+ */
+Case *
+checklabels(Node *inits, Type *ctype, int nlab, char *title)
+{
+ Node *n, *p, *q, *wild;
+ Label *labs, *aux;
+ Case *c;
+ char buf[256], buf1[256];
+ int i, e;
+
+ labs = allocmem(nlab * sizeof *labs);
+ i = 0;
+ wild = nil;
+ for(n = inits; n != nil; n = n->right){
+ for(q = n->left->left; q != nil; q = q->right){
+ switch(q->left->op){
+ case Oconst:
+ labs[i].start = q->left;
+ labs[i].stop = q->left;
+ labs[i++].node = n->left;
+ break;
+ case Orange:
+ labs[i].start = q->left->left;
+ labs[i].stop = q->left->right;
+ labs[i++].node = n->left;
+ break;
+ case Owild:
+ wild = n->left;
+ break;
+ default:
+ fatal("bogus index in checklabels");
+ break;
+ }
+ }
+ }
+
+ if(i != nlab)
+ fatal("bad label count: %d then %d", nlab, i);
+
+ aux = allocmem(nlab * sizeof *aux);
+ casesort(ctype, aux, labs, 0, nlab);
+ for(i = 0; i < nlab; i++){
+ p = labs[i].stop;
+ if(casecmp(ctype, labs[i].start, p) > 0)
+ nerror(labs[i].start, "unmatchable %s %V", title, labs[i].node);
+ for(e = i + 1; e < nlab; e++){
+ if(casecmp(ctype, labs[e].start, p) <= 0){
+ eprintlist(buf, buf+sizeof(buf), labs[e].node->left, " or ");
+ eprintlist(buf1, buf1+sizeof(buf1), labs[e-1].node->left, " or ");
+ nerror(labs[e].start,"%s '%s' overlaps with '%s' on line %L",
+ title, buf, buf1, p->src.start);
+ }
+
+ /*
+ * check for merging case labels
+ */
+ if(ctype != tint
+ || labs[e].start->val != p->val+1
+ || labs[e].node != labs[i].node)
+ break;
+ p = labs[e].stop;
+ }
+ if(e != i + 1){
+ labs[i].stop = p;
+ memmove(&labs[i+1], &labs[e], (nlab-e) * sizeof *labs);
+ nlab -= e - (i + 1);
+ }
+ }
+ free(aux);
+
+ c = allocmem(sizeof *c);
+ c->nlab = nlab;
+ c->nsnd = 0;
+ c->labs = labs;
+ c->wild = wild;
+
+ return c;
+}
+
+static int
+matchcmp(Node *na, Node *nb)
+{
+ Sym *a, *b;
+ int sa, sb;
+
+ a = na->decl->sym;
+ b = nb->decl->sym;
+ sa = a->len > 0 && a->name[a->len-1] == '*';
+ sb = b->len > 0 && b->name[b->len-1] == '*';
+ if(sa){
+ if(sb){
+ if(a->len == b->len)
+ return symcmp(a, b);
+ return b->len-a->len;
+ }
+ else
+ return 1;
+ }
+ else{
+ if(sb)
+ return -1;
+ else{
+ if(na->ty == tstring){
+ if(nb->ty == tstring)
+ return symcmp(a, b);
+ else
+ return 1;
+ }
+ else{
+ if(nb->ty == tstring)
+ return -1;
+ else
+ return symcmp(a, b);
+ }
+ }
+ }
+}
+
+int
+casecmp(Type *ty, Node *a, Node *b)
+{
+ if(ty == tint || ty == tbig){
+ if(a->val < b->val)
+ return -1;
+ if(a->val > b->val)
+ return 1;
+ return 0;
+ }
+ if(ty == texception)
+ return matchcmp(a, b);
+ return symcmp(a->decl->sym, b->decl->sym);
+}
+
+void
+casesort(Type *t, Label *aux, Label *labs, int start, int stop)
+{
+ int n, top, mid, base;
+
+ n = stop - start;
+ if(n <= 1)
+ return;
+ top = mid = start + n / 2;
+
+ casesort(t, aux, labs, start, top);
+ casesort(t, aux, labs, mid, stop);
+
+ /*
+ * merge together two sorted label arrays, yielding a sorted array
+ */
+ n = 0;
+ base = start;
+ while(base < top && mid < stop){
+ if(casecmp(t, labs[base].start, labs[mid].start) <= 0)
+ aux[n++] = labs[base++];
+ else
+ aux[n++] = labs[mid++];
+ }
+ if(base < top)
+ memmove(&aux[n], &labs[base], (top-base) * sizeof *aux);
+ else if(mid < stop)
+ memmove(&aux[n], &labs[mid], (stop-mid) * sizeof *aux);
+ memmove(&labs[start], &aux[0], (stop-start) * sizeof *labs);
+}
+
+/*
+ * binary search for the label corresponding to a given value
+ */
+int
+findlab(Type *ty, Node *v, Label *labs, int nlab)
+{
+ int l, r, m;
+
+ if(nlab <= 1)
+ return 0;
+ l = 1;
+ r = nlab - 1;
+ while(l <= r){
+ m = (r + l) / 2;
+ if(casecmp(ty, labs[m].start, v) <= 0)
+ l = m + 1;
+ else
+ r = m - 1;
+ }
+ m = l - 1;
+ if(casecmp(ty, labs[m].start, v) > 0
+ || casecmp(ty, labs[m].stop, v) < 0)
+ fatal("findlab out of range");
+ return m;
+}
+
+void
+altcheck(Node *an, Type *ret)
+{
+ Node *n, *q, *left, *op, *wild;
+ Case *c;
+ int ok, nsnd, nrcv;
+
+ an->left = scheck(an->left, ret, Sother);
+
+ ok = 1;
+ nsnd = 0;
+ nrcv = 0;
+ wild = nil;
+ for(n = an->left; n != nil; n = n->right){
+ q = n->left->right->left;
+ if(n->left->right->right == nil)
+ nwarn(q, "no body for alt guard %V", q);
+ for(; q != nil; q = q->right){
+ left = q->left;
+ switch(left->op){
+ case Owild:
+ if(wild != nil)
+ nerror(left, "alt guard * duplicated on line %L", wild->src.start);
+ wild = left;
+ break;
+ case Orange:
+ nerror(left, "alt guard %V is illegal", left);
+ ok = 0;
+ break;
+ default:
+ op = hascomm(left);
+ if(op == nil){
+ nerror(left, "alt guard %V has no communication", left);
+ ok = 0;
+ break;
+ }
+ if(op->op == Osnd)
+ nsnd++;
+ else
+ nrcv++;
+ break;
+ }
+ }
+ }
+
+ if(!ok)
+ return;
+
+ c = allocmem(sizeof *c);
+ c->nlab = nsnd + nrcv;
+ c->nsnd = nsnd;
+ c->wild = wild;
+
+ an->ty = mktalt(c);
+}
+
+Node*
+hascomm(Node *n)
+{
+ Node *r;
+
+ if(n == nil)
+ return nil;
+ if(n->op == Osnd || n->op == Orcv)
+ return n;
+ r = hascomm(n->left);
+ if(r != nil)
+ return r;
+ return hascomm(n->right);
+}
+
+void
+raisescheck(Type *t)
+{
+ Node *n, *nn;
+ Ok ok;
+
+ if(t->kind != Tfn)
+ return;
+ n = t->u.eraises;
+ for(nn = n->left; nn != nil; nn = nn->right){
+ ok = echeck(nn->left, 0, 0, nil);
+ if(ok.ok && nn->left->ty->kind != Texception)
+ nerror(n, "%V: illegal raises expression", nn->left);
+ }
+}
+
+typedef struct Elist Elist;
+
+struct Elist{
+ Decl *d;
+ Elist *nxt;
+};
+
+static Elist*
+emerge(Elist *el1, Elist *el2)
+{
+ int f;
+ Elist *el, *nxt;
+
+ for( ; el1 != nil; el1 = nxt){
+ f = 0;
+ for(el = el2; el != nil; el = el->nxt){
+ if(el1->d == el->d){
+ f = 1;
+ break;
+ }
+ }
+ nxt = el1->nxt;
+ if(!f){
+ el1->nxt = el2;
+ el2 = el1;
+ }
+ }
+ return el2;
+}
+
+static Elist*
+equals(Node *n)
+{
+ Node *q, *nn;
+ Elist *e, *el;
+
+ el = nil;
+ for(q = n->left->left; q != nil; q = q->right){
+ nn = q->left;
+ if(nn->op == Owild)
+ return nil;
+ if(nn->ty->kind != Texception)
+ continue;
+ e = (Elist*)malloc(sizeof(Elist));
+ e->d = nn->decl;
+ e->nxt = el;
+ el = e;
+ }
+ return el;
+}
+
+static int
+caught(Decl *d, Node *n)
+{
+ Node *q, *nn;
+
+ for(n = n->right; n != nil; n = n->right){
+ for(q = n->left->left; q != nil; q = q->right){
+ nn = q->left;
+ if(nn->op == Owild)
+ return 1;
+ if(nn->ty->kind != Texception)
+ continue;
+ if(d == nn->decl)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static Elist*
+raisecheck(Node *n, Elist *ql)
+{
+ int exc;
+ Node *e;
+ Elist *el, *nel, *nxt;
+
+ if(n == nil)
+ return nil;
+ el = nil;
+ for(; n != nil; n = n->right){
+ switch(n->op){
+ case Oscope:
+ return raisecheck(n->right, ql);
+ case Olabel:
+ case Odo:
+ return raisecheck(n->right, ql);
+ case Oif:
+ case Ofor:
+ return emerge(raisecheck(n->right->left, ql),
+ raisecheck(n->right->right, ql));
+ case Oalt:
+ case Ocase:
+ case Opick:
+ case Oexcept:
+ exc = n->op == Oexcept;
+ for(n = n->right; n != nil; n = n->right){
+ ql = nil;
+ if(exc)
+ ql = equals(n);
+ el = emerge(raisecheck(n->left->right, ql), el);
+ }
+ return el;
+ case Oseq:
+ el = emerge(raisecheck(n->left, ql), el);
+ break;
+ case Oexstmt:
+ el = raisecheck(n->left, ql);
+ nel = nil;
+ for( ; el != nil; el = nxt){
+ nxt = el->nxt;
+ if(!caught(el->d, n->right)){
+ el->nxt = nel;
+ nel = el;
+ }
+ }
+ return emerge(nel, raisecheck(n->right, ql));
+ case Oraise:
+ e = n->left;
+ if(e->ty && e->ty->kind == Texception){
+ if(!e->ty->cons)
+ return ql;
+ if(e->op == Ocall)
+ e = e->left;
+ if(e->op == Omdot)
+ e = e->right;
+ if(e->op != Oname)
+ fatal("exception %n not a name", e);
+ el = (Elist*)malloc(sizeof(Elist));
+ el->d = e->decl;
+ el->nxt = nil;
+ return el;
+ }
+ return nil;
+ default:
+ return nil;
+ }
+ }
+ return el;
+}
+
+void
+checkraises(Node *n)
+{
+ int f;
+ Decl *d;
+ Elist *e, *el;
+ Node *es, *nn;
+
+ el = raisecheck(n->right, nil);
+ es = n->ty->u.eraises;
+ if(es != nil){
+ for(nn = es->left; nn != nil; nn = nn->right){
+ d = nn->left->decl;
+ f = 0;
+ for(e = el; e != nil; e = e->nxt){
+ if(d == e->d){
+ f = 1;
+ e->d = nil;
+ break;
+ }
+ }
+ if(!f)
+ nwarn(n, "function %V does not raise %s but declared", n->left, d->sym->name);
+ }
+ }
+ for(e = el; e != nil; e = e->nxt)
+ if(e->d != nil)
+ nwarn(n, "function %V raises %s but not declared", n->left, e->d->sym->name);
+}
+
+/* sort all globals in modules now that we've finished with 'last' pointers
+ * and before any code generation
+ */
+void
+gsort(Node *n)
+{
+ for(;;){
+ if(n == nil)
+ return;
+ if(n->op != Oseq)
+ break;
+ gsort(n->left);
+ n = n->right;
+ }
+ if(n->op == Omoddecl && n->ty->ok & OKverify){
+ n->ty->ids = namesort(n->ty->ids);
+ sizeids(n->ty->ids, 0);
+ }
+}
--- /dev/null
+++ b/limbo/types.c
@@ -1,0 +1,4745 @@
+#include "limbo.h"
+#include "mp.h"
+#include "libsec.h"
+
+char *kindname[Tend] =
+{
+ /* Tnone */ "no type",
+ /* Tadt */ "adt",
+ /* Tadtpick */ "adt",
+ /* Tarray */ "array",
+ /* Tbig */ "big",
+ /* Tbyte */ "byte",
+ /* Tchan */ "chan",
+ /* Treal */ "real",
+ /* Tfn */ "fn",
+ /* Tint */ "int",
+ /* Tlist */ "list",
+ /* Tmodule */ "module",
+ /* Tref */ "ref",
+ /* Tstring */ "string",
+ /* Ttuple */ "tuple",
+ /* Texception */ "exception",
+ /* Tfix */ "fixed point",
+ /* Tpoly */ "polymorphic",
+
+ /* Tainit */ "array initializers",
+ /* Talt */ "alt channels",
+ /* Tany */ "polymorphic type",
+ /* Tarrow */ "->",
+ /* Tcase */ "case int labels",
+ /* Tcasel */ "case big labels",
+ /* Tcasec */ "case string labels",
+ /* Tdot */ ".",
+ /* Terror */ "type error",
+ /* Tgoto */ "goto labels",
+ /* Tid */ "id",
+ /* Tiface */ "module interface",
+ /* Texcept */ "exception handler table",
+ /* Tinst */ "instantiated type",
+};
+
+Tattr tattr[Tend] =
+{
+ /* isptr refable conable big vis */
+ /* Tnone */ { 0, 0, 0, 0, 0, },
+ /* Tadt */ { 0, 1, 1, 1, 1, },
+ /* Tadtpick */ { 0, 1, 0, 1, 1, },
+ /* Tarray */ { 1, 0, 0, 0, 1, },
+ /* Tbig */ { 0, 0, 1, 1, 1, },
+ /* Tbyte */ { 0, 0, 1, 0, 1, },
+ /* Tchan */ { 1, 0, 0, 0, 1, },
+ /* Treal */ { 0, 0, 1, 1, 1, },
+ /* Tfn */ { 0, 1, 0, 0, 1, },
+ /* Tint */ { 0, 0, 1, 0, 1, },
+ /* Tlist */ { 1, 0, 0, 0, 1, },
+ /* Tmodule */ { 1, 0, 0, 0, 1, },
+ /* Tref */ { 1, 0, 0, 0, 1, },
+ /* Tstring */ { 1, 0, 1, 0, 1, },
+ /* Ttuple */ { 0, 1, 1, 1, 1, },
+ /* Texception */ { 0, 0, 0, 1, 1, },
+ /* Tfix */ { 0, 0, 1, 0, 1, },
+ /* Tpoly */ { 1, 0, 0, 0, 1, },
+
+ /* Tainit */ { 0, 0, 0, 1, 0, },
+ /* Talt */ { 0, 0, 0, 1, 0, },
+ /* Tany */ { 1, 0, 0, 0, 0, },
+ /* Tarrow */ { 0, 0, 0, 0, 1, },
+ /* Tcase */ { 0, 0, 0, 1, 0, },
+ /* Tcasel */ { 0, 0, 0, 1, 0, },
+ /* Tcasec */ { 0, 0, 0, 1, 0, },
+ /* Tdot */ { 0, 0, 0, 0, 1, },
+ /* Terror */ { 0, 1, 1, 0, 0, },
+ /* Tgoto */ { 0, 0, 0, 1, 0, },
+ /* Tid */ { 0, 0, 0, 0, 1, },
+ /* Tiface */ { 0, 0, 0, 1, 0, },
+ /* Texcept */ { 0, 0, 0, 1, 0, },
+ /* Tinst */ { 0, 1, 1, 1, 1, },
+};
+
+static Teq *eqclass[Tend];
+
+static Type ztype;
+static int eqrec;
+static int eqset;
+static int tcomset;
+
+static int idcompat(Decl*, Decl*, int, int);
+static int rtcompat(Type *t1, Type *t2, int any, int);
+static int assumeteq(Type *t1, Type *t2);
+static int assumetcom(Type *t1, Type *t2);
+static int cleartcomrec(Type *t);
+static int rtequal(Type*, Type*);
+static int cleareqrec(Type*);
+static int idequal(Decl*, Decl*, int, int*);
+static int pyequal(Type*, Type*);
+static int rtsign(Type*, uchar*, int, int);
+static int clearrec(Type*);
+static int idsign(Decl*, int, uchar*, int, int);
+static int idsign1(Decl*, int, uchar*, int, int);
+static int raisessign(Node *n, uchar *sig, int lensig, int spos);
+static void ckfix(Type*, double);
+static int fnunify(Type*, Type*, Tpair**, int);
+static int rtunify(Type*, Type*, Tpair**, int);
+static int idunify(Decl*, Decl*, Tpair**, int);
+static int toccurs(Type*, Tpair**);
+static int fncleareqrec(Type*, Type*);
+static Type* comtype(Src*, Type*, Decl*);
+static Type* duptype(Type*);
+static int tpolys(Type*);
+
+static void
+addtmap(Type *t1, Type *t2, Tpair **tpp)
+{
+ Tpair *tp;
+
+ tp = allocmem(sizeof *tp);
+ tp->t1 = t1;
+ tp->t2 = t2;
+ tp->nxt = *tpp;
+ *tpp = tp;
+}
+
+Type*
+valtmap(Type *t, Tpair *tp)
+{
+ for( ; tp != nil; tp = tp->nxt)
+ if(tp->t1 == t)
+ return tp->t2;
+ return t;
+}
+
+Typelist*
+addtype(Type *t, Typelist *hd)
+{
+ Typelist *tl, *p;
+
+ tl = allocmem(sizeof(*tl));
+ tl->t = t;
+ tl->nxt = nil;
+ if(hd == nil)
+ return tl;
+ for(p = hd; p->nxt != nil; p = p->nxt)
+ ;
+ p->nxt = tl;
+ return hd;
+}
+
+void
+typeinit(void)
+{
+ Decl *id;
+
+ anontupsym = enter(".tuple", 0);
+
+ ztype.sbl = -1;
+ ztype.ok = 0;
+ ztype.rec = 0;
+
+ tbig = mktype(&noline, &noline, Tbig, nil, nil);
+ tbig->size = IBY2LG;
+ tbig->align = IBY2LG;
+ tbig->ok = OKmask;
+
+ tbyte = mktype(&noline, &noline, Tbyte, nil, nil);
+ tbyte->size = 1;
+ tbyte->align = 1;
+ tbyte->ok = OKmask;
+
+ tint = mktype(&noline, &noline, Tint, nil, nil);
+ tint->size = IBY2WD;
+ tint->align = IBY2WD;
+ tint->ok = OKmask;
+
+ treal = mktype(&noline, &noline, Treal, nil, nil);
+ treal->size = IBY2FT;
+ treal->align = IBY2FT;
+ treal->ok = OKmask;
+
+ tstring = mktype(&noline, &noline, Tstring, nil, nil);
+ tstring->size = IBY2WD;
+ tstring->align = IBY2WD;
+ tstring->ok = OKmask;
+
+ texception = mktype(&noline, &noline, Texception, nil, nil);
+ texception->size = IBY2WD;
+ texception->align = IBY2WD;
+ texception->ok = OKmask;
+
+ tany = mktype(&noline, &noline, Tany, nil, nil);
+ tany->size = IBY2WD;
+ tany->align = IBY2WD;
+ tany->ok = OKmask;
+
+ tnone = mktype(&noline, &noline, Tnone, nil, nil);
+ tnone->size = 0;
+ tnone->align = 1;
+ tnone->ok = OKmask;
+
+ terror = mktype(&noline, &noline, Terror, nil, nil);
+ terror->size = 0;
+ terror->align = 1;
+ terror->ok = OKmask;
+
+ tunknown = mktype(&noline, &noline, Terror, nil, nil);
+ tunknown->size = 0;
+ tunknown->align = 1;
+ tunknown->ok = OKmask;
+
+ tfnptr = mktype(&noline, &noline, Ttuple, nil, nil);
+ id = tfnptr->ids = mkids(&nosrc, nil, tany, nil);
+ id->store = Dfield;
+ id->offset = 0;
+ id->sym = enter("t0", 0);
+ id->src = nosrc;
+ id = tfnptr->ids->next = mkids(&nosrc, nil, tint, nil);
+ id->store = Dfield;
+ id->offset = IBY2WD;
+ id->sym = enter("t1", 0);
+ id->src = nosrc;
+
+ rtexception = mktype(&noline, &noline, Tref, texception, nil);
+ rtexception->size = IBY2WD;
+ rtexception->align = IBY2WD;
+ rtexception->ok = OKmask;
+}
+
+void
+typestart(void)
+{
+ descriptors = nil;
+ nfns = 0;
+ nadts = 0;
+ selfdecl = nil;
+ if(tfnptr->decl != nil)
+ tfnptr->decl->desc = nil;
+
+ memset(eqclass, 0, sizeof eqclass);
+
+ typebuiltin(mkids(&nosrc, enter("int", 0), nil, nil), tint);
+ typebuiltin(mkids(&nosrc, enter("big", 0), nil, nil), tbig);
+ typebuiltin(mkids(&nosrc, enter("byte", 0), nil, nil), tbyte);
+ typebuiltin(mkids(&nosrc, enter("string", 0), nil, nil), tstring);
+ typebuiltin(mkids(&nosrc, enter("real", 0), nil, nil), treal);
+}
+
+Teq*
+modclass(void)
+{
+ return eqclass[Tmodule];
+}
+
+Type*
+mktype(Line *start, Line *stop, int kind, Type *tof, Decl *args)
+{
+ Type *t;
+
+ t = allocmem(sizeof *t);
+ *t = ztype;
+ t->src.start = *start;
+ t->src.stop = *stop;
+ t->kind = kind;
+ t->tof = tof;
+ t->ids = args;
+ return t;
+}
+
+Type*
+mktalt(Case *c)
+{
+ Type *t;
+ char buf[32];
+ static int nalt;
+
+ t = mktype(&noline, &noline, Talt, nil, nil);
+ t->decl = mkdecl(&nosrc, Dtype, t);
+ seprint(buf, buf+sizeof(buf), ".a%d", nalt++);
+ t->decl->sym = enter(buf, 0);
+ t->cse = c;
+ return usetype(t);
+}
+
+/*
+ * copy t and the top level of ids
+ */
+Type*
+copytypeids(Type *t)
+{
+ Type *nt;
+ Decl *id, *new, *last;
+
+ nt = allocmem(sizeof *nt);
+ *nt = *t;
+ last = nil;
+ for(id = t->ids; id != nil; id = id->next){
+ new = allocmem(sizeof *id);
+ *new = *id;
+ if(last == nil)
+ nt->ids = new;
+ else
+ last->next = new;
+ last = new;
+ }
+ return nt;
+}
+
+/*
+ * make each of the ids have type t
+ */
+Decl*
+typeids(Decl *ids, Type *t)
+{
+ Decl *id;
+
+ if(ids == nil)
+ return nil;
+
+ ids->ty = t;
+ for(id = ids->next; id != nil; id = id->next){
+ id->ty = t;
+ }
+ return ids;
+}
+
+void
+typebuiltin(Decl *d, Type *t)
+{
+ d->ty = t;
+ t->decl = d;
+ installids(Dtype, d);
+}
+
+Node *
+fielddecl(int store, Decl *ids)
+{
+ Node *n;
+
+ n = mkn(Ofielddecl, nil, nil);
+ n->decl = ids;
+ for(; ids != nil; ids = ids->next)
+ ids->store = store;
+ return n;
+}
+
+Node *
+typedecl(Decl *ids, Type *t)
+{
+ Node *n;
+
+ if(t->decl == nil)
+ t->decl = ids;
+ n = mkn(Otypedecl, nil, nil);
+ n->decl = ids;
+ n->ty = t;
+ for(; ids != nil; ids = ids->next)
+ ids->ty = t;
+ return n;
+}
+
+void
+typedecled(Node *n)
+{
+ installids(Dtype, n->decl);
+}
+
+Node *
+adtdecl(Decl *ids, Node *fields)
+{
+ Node *n;
+ Type *t;
+
+ n = mkn(Oadtdecl, nil, nil);
+ t = mktype(&ids->src.start, &ids->src.stop, Tadt, nil, nil);
+ n->decl = ids;
+ n->left = fields;
+ n->ty = t;
+ t->decl = ids;
+ for(; ids != nil; ids = ids->next)
+ ids->ty = t;
+ return n;
+}
+
+void
+adtdecled(Node *n)
+{
+ Decl *d, *ids;
+
+ d = n->ty->decl;
+ installids(Dtype, d);
+ if(n->ty->polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, n->ty->polys);
+ }
+ pushscope(nil, Sother);
+ fielddecled(n->left);
+ n->ty->ids = popscope();
+ if(n->ty->polys != nil)
+ n->ty->polys = popscope();
+ for(ids = n->ty->ids; ids != nil; ids = ids->next)
+ ids->dot = d;
+}
+
+void
+fielddecled(Node *n)
+{
+ for(; n != nil; n = n->right){
+ switch(n->op){
+ case Oseq:
+ fielddecled(n->left);
+ break;
+ case Oadtdecl:
+ adtdecled(n);
+ return;
+ case Otypedecl:
+ typedecled(n);
+ return;
+ case Ofielddecl:
+ installids(Dfield, n->decl);
+ return;
+ case Ocondecl:
+ condecled(n);
+ gdasdecl(n->right);
+ return;
+ case Oexdecl:
+ exdecled(n);
+ return;
+ case Opickdecl:
+ pickdecled(n);
+ return;
+ default:
+ fatal("can't deal with %O in fielddecled", n->op);
+ }
+ }
+}
+
+int
+pickdecled(Node *n)
+{
+ Decl *d;
+ int tag;
+
+ if(n == nil)
+ return 0;
+ tag = pickdecled(n->left);
+ pushscope(nil, Sother);
+ fielddecled(n->right->right);
+ d = n->right->left->decl;
+ d->ty->ids = popscope();
+ installids(Dtag, d);
+ for(; d != nil; d = d->next)
+ d->tag = tag++;
+ return tag;
+}
+
+/*
+ * make the tuple type used to initialize adt t
+ */
+Type*
+mkadtcon(Type *t)
+{
+ Decl *id, *new, *last;
+ Type *nt;
+
+ nt = allocmem(sizeof *nt);
+ *nt = *t;
+ last = nil;
+ nt->ids = nil;
+ nt->kind = Ttuple;
+ for(id = t->ids; id != nil; id = id->next){
+ if(id->store != Dfield)
+ continue;
+ new = allocmem(sizeof *id);
+ *new = *id;
+ new->cyc = 0;
+ if(last == nil)
+ nt->ids = new;
+ else
+ last->next = new;
+ last = new;
+ }
+ last->next = nil;
+ return nt;
+}
+
+/*
+ * make the tuple type used to initialize t,
+ * an adt with pick fields tagged by tg
+ */
+Type*
+mkadtpickcon(Type *t, Type *tgt)
+{
+ Decl *id, *new, *last;
+ Type *nt;
+
+ last = mkids(&tgt->decl->src, nil, tint, nil);
+ last->store = Dfield;
+ nt = mktype(&t->src.start, &t->src.stop, Ttuple, nil, last);
+ for(id = t->ids; id != nil; id = id->next){
+ if(id->store != Dfield)
+ continue;
+ new = allocmem(sizeof *id);
+ *new = *id;
+ new->cyc = 0;
+ last->next = new;
+ last = new;
+ }
+ for(id = tgt->ids; id != nil; id = id->next){
+ if(id->store != Dfield)
+ continue;
+ new = allocmem(sizeof *id);
+ *new = *id;
+ new->cyc = 0;
+ last->next = new;
+ last = new;
+ }
+ last->next = nil;
+ return nt;
+}
+
+/*
+ * make an identifier type
+ */
+Type*
+mkidtype(Src *src, Sym *s)
+{
+ Type *t;
+
+ t = mktype(&src->start, &src->stop, Tid, nil, nil);
+ if(s->unbound == nil){
+ s->unbound = mkdecl(src, Dunbound, nil);
+ s->unbound->sym = s;
+ }
+ t->decl = s->unbound;
+ return t;
+}
+
+/*
+ * make a qualified type for t->s
+ */
+Type*
+mkarrowtype(Line *start, Line *stop, Type *t, Sym *s)
+{
+ Src src;
+
+ src.start = *start;
+ src.stop = *stop;
+ t = mktype(start, stop, Tarrow, t, nil);
+ if(s->unbound == nil){
+ s->unbound = mkdecl(&src, Dunbound, nil);
+ s->unbound->sym = s;
+ }
+ t->decl = s->unbound;
+ return t;
+}
+
+/*
+ * make a qualified type for t.s
+ */
+Type*
+mkdottype(Line *start, Line *stop, Type *t, Sym *s)
+{
+ Src src;
+
+ src.start = *start;
+ src.stop = *stop;
+ t = mktype(start, stop, Tdot, t, nil);
+ if(s->unbound == nil){
+ s->unbound = mkdecl(&src, Dunbound, nil);
+ s->unbound->sym = s;
+ }
+ t->decl = s->unbound;
+ return t;
+}
+
+Type*
+mkinsttype(Src* src, Type *tt, Typelist *tl)
+{
+ Type *t;
+
+ t = mktype(&src->start, &src->stop, Tinst, tt, nil);
+ t->u.tlist = tl;
+ return t;
+}
+
+/*
+ * look up the name f in the fields of a module, adt, or tuple
+ */
+Decl*
+namedot(Decl *ids, Sym *s)
+{
+ for(; ids != nil; ids = ids->next)
+ if(ids->sym == s)
+ return ids;
+ return nil;
+}
+
+/*
+ * complete the declaration of an adt
+ * methods frames get sized in module definition or during function definition
+ * place the methods at the end of the field list
+ */
+void
+adtdefd(Type *t)
+{
+ Decl *d, *id, *next, *aux, *store, *auxhd, *tagnext;
+ int seentags;
+
+ if(debug['x'])
+ print("adt %T defd\n", t);
+ d = t->decl;
+ tagnext = nil;
+ store = nil;
+ for(id = t->polys; id != nil; id = id->next){
+ id->store = Dtype;
+ id->ty = verifytypes(id->ty, d, nil);
+ }
+ for(id = t->ids; id != nil; id = next){
+ if(id->store == Dtag){
+ if(t->tags != nil)
+ error(id->src.start, "only one set of pick fields allowed");
+ tagnext = pickdefd(t, id);
+ next = tagnext;
+ if(store != nil)
+ store->next = next;
+ else
+ t->ids = next;
+ continue;
+ }else{
+ id->dot = d;
+ next = id->next;
+ store = id;
+ }
+ }
+ aux = nil;
+ store = nil;
+ auxhd = nil;
+ seentags = 0;
+ for(id = t->ids; id != nil; id = next){
+ if(id == tagnext)
+ seentags = 1;
+
+ next = id->next;
+ id->dot = d;
+ id->ty = topvartype(verifytypes(id->ty, d, nil), id, 1, 1);
+ if(id->store == Dfield && id->ty->kind == Tfn)
+ id->store = Dfn;
+ if(id->store == Dfn || id->store == Dconst){
+ if(store != nil)
+ store->next = next;
+ else
+ t->ids = next;
+ if(aux != nil)
+ aux->next = id;
+ else
+ auxhd = id;
+ aux = id;
+ }else{
+ if(seentags)
+ error(id->src.start, "pick fields must be the last data fields in an adt");
+ store = id;
+ }
+ }
+ if(aux != nil)
+ aux->next = nil;
+ if(store != nil)
+ store->next = auxhd;
+ else
+ t->ids = auxhd;
+
+ for(id = t->tags; id != nil; id = id->next){
+ id->ty = verifytypes(id->ty, d, nil);
+ if(id->ty->tof == nil)
+ id->ty->tof = mkadtpickcon(t, id->ty);
+ }
+}
+
+/*
+ * assemble the data structure for an adt with a pick clause.
+ * since the scoping rules for adt pick fields are strange,
+ * we have a customized check for overlapping definitions.
+ */
+Decl*
+pickdefd(Type *t, Decl *tg)
+{
+ Decl *id, *xid, *lasttg, *d;
+ Type *tt;
+ int tag;
+
+ lasttg = nil;
+ d = t->decl;
+ t->tags = tg;
+ tag = 0;
+ while(tg != nil){
+ tt = tg->ty;
+ if(tt->kind != Tadtpick || tg->tag != tag)
+ break;
+ tt->decl = tg;
+ lasttg = tg;
+ for(; tg != nil; tg = tg->next){
+ if(tg->ty != tt)
+ break;
+ tag++;
+ lasttg = tg;
+ tg->dot = d;
+ }
+ for(id = tt->ids; id != nil; id = id->next){
+ xid = namedot(t->ids, id->sym);
+ if(xid != nil)
+ error(id->src.start, "redeclaration of %K, previously declared as %k on line %L",
+ id, xid, xid->src.start);
+ id->dot = d;
+ }
+ }
+ if(lasttg == nil){
+ error(t->src.start, "empty pick field declaration in %T", t);
+ t->tags = nil;
+ }else
+ lasttg->next = nil;
+ d->tag = tag;
+ return tg;
+}
+
+Node*
+moddecl(Decl *ids, Node *fields)
+{
+ Node *n;
+ Type *t;
+
+ n = mkn(Omoddecl, mkn(Oseq, nil, nil), nil);
+ t = mktype(&ids->src.start, &ids->src.stop, Tmodule, nil, nil);
+ n->decl = ids;
+ n->left = fields;
+ n->ty = t;
+ return n;
+}
+
+void
+moddecled(Node *n)
+{
+ Decl *d, *ids, *im, *dot;
+ Type *t;
+ Sym *s;
+ char buf[StrSize];
+ int isimp;
+ Dlist *dm, *dl;
+
+ d = n->decl;
+ installids(Dtype, d);
+ isimp = 0;
+ for(ids = d; ids != nil; ids = ids->next){
+ for(im = impmods; im != nil; im = im->next){
+ if(ids->sym == im->sym){
+ isimp = 1;
+ d = ids;
+ dm = malloc(sizeof(Dlist));
+ dm->d = ids;
+ dm->next = nil;
+ if(impdecls == nil)
+ impdecls = dm;
+ else{
+ for(dl = impdecls; dl->next != nil; dl = dl->next)
+ ;
+ dl->next = dm;
+ }
+ }
+ }
+ ids->ty = n->ty;
+ }
+ pushscope(nil, Sother);
+ fielddecled(n->left);
+
+ d->ty->ids = popscope();
+
+ /*
+ * make the current module the -> parent of all contained decls->
+ */
+ for(ids = d->ty->ids; ids != nil; ids = ids->next)
+ ids->dot = d;
+
+ t = d->ty;
+ t->decl = d;
+ if(debug['m'])
+ print("declare module %s\n", d->sym->name);
+
+ /*
+ * add the iface declaration in case it's needed later
+ */
+ seprint(buf, buf+sizeof(buf), ".m.%s", d->sym->name);
+ installids(Dglobal, mkids(&d->src, enter(buf, 0), tnone, nil));
+
+ if(isimp){
+ for(ids = d->ty->ids; ids != nil; ids = ids->next){
+ s = ids->sym;
+ if(s->decl != nil && s->decl->scope >= scope){
+ dot = s->decl->dot;
+ if(s->decl->store != Dwundef && dot != nil && dot != d && isimpmod(dot->sym) && dequal(ids, s->decl, 0))
+ continue;
+ redecl(ids);
+ ids->old = s->decl->old;
+ }else
+ ids->old = s->decl;
+ s->decl = ids;
+ ids->scope = scope;
+ }
+ }
+}
+
+/*
+ * for each module in id,
+ * link by field ext all of the decls for
+ * functions needed in external linkage table
+ * collect globals and make a tuple for all of them
+ */
+Type*
+mkiface(Decl *m)
+{
+ Decl *iface, *last, *globals, *glast, *id, *d;
+ Type *t;
+ char buf[StrSize];
+
+ iface = last = allocmem(sizeof(Decl));
+ globals = glast = mkdecl(&m->src, Dglobal, mktype(&m->src.start, &m->src.stop, Tadt, nil, nil));
+ for(id = m->ty->ids; id != nil; id = id->next){
+ switch(id->store){
+ case Dglobal:
+ glast = glast->next = dupdecl(id);
+ id->iface = globals;
+ glast->iface = id;
+ break;
+ case Dfn:
+ id->iface = last = last->next = dupdecl(id);
+ last->iface = id;
+ break;
+ case Dtype:
+ if(id->ty->kind != Tadt)
+ break;
+ for(d = id->ty->ids; d != nil; d = d->next){
+ if(d->store == Dfn){
+ d->iface = last = last->next = dupdecl(d);
+ last->iface = d;
+ }
+ }
+ break;
+ }
+ }
+ last->next = nil;
+ iface = namesort(iface->next);
+
+ if(globals->next != nil){
+ glast->next = nil;
+ globals->ty->ids = namesort(globals->next);
+ globals->ty->decl = globals;
+ globals->sym = enter(".mp", 0);
+ globals->dot = m;
+ globals->next = iface;
+ iface = globals;
+ }
+
+ /*
+ * make the interface type and install an identifier for it
+ * the iface has a ref count if it is loaded
+ */
+ t = mktype(&m->src.start, &m->src.stop, Tiface, nil, iface);
+ seprint(buf, buf+sizeof(buf), ".m.%s", m->sym->name);
+ id = enter(buf, 0)->decl;
+ t->decl = id;
+ id->ty = t;
+
+ /*
+ * dummy node so the interface is initialized
+ */
+ id->init = mkn(Onothing, nil, nil);
+ id->init->ty = t;
+ id->init->decl = id;
+ return t;
+}
+
+void
+joiniface(Type *mt, Type *t)
+{
+ Decl *id, *d, *iface, *globals;
+
+ iface = t->ids;
+ globals = iface;
+ if(iface != nil && iface->store == Dglobal)
+ iface = iface->next;
+ for(id = mt->tof->ids; id != nil; id = id->next){
+ switch(id->store){
+ case Dglobal:
+ for(d = id->ty->ids; d != nil; d = d->next)
+ d->iface->iface = globals;
+ break;
+ case Dfn:
+ id->iface->iface = iface;
+ iface = iface->next;
+ break;
+ default:
+ fatal("unknown store %k in joiniface", id);
+ break;
+ }
+ }
+ if(iface != nil)
+ fatal("join iface not matched");
+ mt->tof = t;
+}
+
+void
+addiface(Decl *m, Decl *d)
+{
+ Type *t;
+ Decl *id, *last, *dd, *lastorig;
+ Dlist *dl;
+
+ if(d == nil || !local(d))
+ return;
+ modrefable(d->ty);
+ if(m == nil){
+ if(impdecls->next != nil)
+ for(dl = impdecls; dl != nil; dl = dl->next)
+ if(dl->d->ty->tof != impdecl->ty->tof) /* impdecl last */
+ addiface(dl->d, d);
+ addiface(impdecl, d);
+ return;
+ }
+ t = m->ty->tof;
+ last = nil;
+ lastorig = nil;
+ for(id = t->ids; id != nil; id = id->next){
+ if(d == id || d == id->iface)
+ return;
+ last = id;
+ if(id->tag == 0)
+ lastorig = id;
+ }
+ dd = dupdecl(d);
+ if(d->dot == nil)
+ d->dot = dd->dot = m;
+ d->iface = dd;
+ dd->iface = d;
+if(debug['v']) print("addiface %p %p\n", d, dd);
+ if(last == nil)
+ t->ids = dd;
+ else
+ last->next = dd;
+ dd->tag = 1; /* mark so not signed */
+ if(lastorig == nil)
+ t->ids = namesort(t->ids);
+ else
+ lastorig->next = namesort(lastorig->next);
+}
+
+/*
+ * eliminate unused declarations from interfaces
+ * label offset within interface
+ */
+void
+narrowmods(void)
+{
+ Teq *eq;
+ Decl *id, *last;
+ Type *t;
+ long offset;
+
+ for(eq = modclass(); eq != nil; eq = eq->eq){
+ t = eq->ty->tof;
+
+ if(t->linkall == 0){
+ last = nil;
+ for(id = t->ids; id != nil; id = id->next){
+ if(id->refs == 0){
+ if(last == nil)
+ t->ids = id->next;
+ else
+ last->next = id->next;
+ }else
+ last = id;
+ }
+
+ /*
+ * need to resize smaller interfaces
+ */
+ resizetype(t);
+ }
+
+ offset = 0;
+ for(id = t->ids; id != nil; id = id->next)
+ id->offset = offset++;
+
+ /*
+ * rathole to stuff number of entries in interface
+ */
+ t->decl->init->val = offset;
+ }
+}
+
+/*
+ * check to see if any data field of module m if referenced.
+ * if so, mark all data in m
+ */
+void
+moddataref(void)
+{
+ Teq *eq;
+ Decl *id;
+
+ for(eq = modclass(); eq != nil; eq = eq->eq){
+ id = eq->ty->tof->ids;
+ if(id != nil && id->store == Dglobal && id->refs)
+ for(id = eq->ty->ids; id != nil; id = id->next)
+ if(id->store == Dglobal)
+ modrefable(id->ty);
+ }
+}
+
+/*
+ * move the global declarations in interface to the front
+ */
+Decl*
+modglobals(Decl *mod, Decl *globals)
+{
+ Decl *id, *head, *last;
+
+ /*
+ * make a copy of all the global declarations
+ * used for making a type descriptor for globals ONLY
+ * note we now have two declarations for the same variables,
+ * which is apt to cause problems if code changes
+ *
+ * here we fix up the offsets for the real declarations
+ */
+ idoffsets(mod->ty->ids, 0, 1);
+
+ last = head = allocmem(sizeof(Decl));
+ for(id = mod->ty->ids; id != nil; id = id->next)
+ if(id->store == Dglobal)
+ last = last->next = dupdecl(id);
+
+ last->next = globals;
+ return head->next;
+}
+
+/*
+ * snap all id type names to the actual type
+ * check that all types are completely defined
+ * verify that the types look ok
+ */
+Type*
+validtype(Type *t, Decl *inadt)
+{
+ if(t == nil)
+ return t;
+ bindtypes(t);
+ t = verifytypes(t, inadt, nil);
+ cycsizetype(t);
+ teqclass(t);
+ return t;
+}
+
+Type*
+usetype(Type *t)
+{
+ if(t == nil)
+ return t;
+ t = validtype(t, nil);
+ reftype(t);
+ return t;
+}
+
+Type*
+internaltype(Type *t)
+{
+ bindtypes(t);
+ t->ok = OKverify;
+ sizetype(t);
+ t->ok = OKmask;
+ return t;
+}
+
+/*
+ * checks that t is a valid top-level type
+ */
+Type*
+topvartype(Type *t, Decl *id, int tyok, int polyok)
+{
+ if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+ error(id->src.start, "cannot declare %s with type %T", id->sym->name, t);
+ if(!tyok && t->kind == Tfn)
+ error(id->src.start, "cannot declare %s to be a function", id->sym->name);
+ if(!polyok && (t->kind == Tadt || t->kind == Tadtpick) && ispolyadt(t))
+ error(id->src.start, "cannot declare %s of a polymorphic type", id->sym->name);
+ return t;
+}
+
+Type*
+toptype(Src *src, Type *t)
+{
+ if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+ error(src->start, "%T, an adt with pick fields, must be used with ref", t);
+ if(t->kind == Tfn)
+ error(src->start, "data cannot have a fn type like %T", t);
+ return t;
+}
+
+static Type*
+comtype(Src *src, Type *t, Decl* adtd)
+{
+ if(adtd == nil && (t->kind == Tadt || t->kind == Tadtpick) && ispolyadt(t))
+ error(src->start, "polymorphic type %T illegal here", t);
+ return t;
+}
+
+void
+usedty(Type *t)
+{
+ if(t != nil && (t->ok | OKmodref) != OKmask)
+ fatal("used ty %t %2.2ux", t, t->ok);
+}
+
+void
+bindtypes(Type *t)
+{
+ Decl *id;
+ Typelist *tl;
+
+ if(t == nil)
+ return;
+ if((t->ok & OKbind) == OKbind)
+ return;
+ t->ok |= OKbind;
+ switch(t->kind){
+ case Tadt:
+ if(t->polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, t->polys);
+ }
+ if(t->val != nil)
+ mergepolydecs(t);
+ if(t->polys != nil){
+ popscope();
+ for(id = t->polys; id != nil; id = id->next)
+ bindtypes(id->ty);
+ }
+ break;
+ case Tadtpick:
+ case Tmodule:
+ case Terror:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tiface:
+ case Tainit:
+ case Talt:
+ case Tcase:
+ case Tcasel:
+ case Tcasec:
+ case Tgoto:
+ case Texcept:
+ case Tfix:
+ case Tpoly:
+ break;
+ case Tarray:
+ case Tarrow:
+ case Tchan:
+ case Tdot:
+ case Tlist:
+ case Tref:
+ bindtypes(t->tof);
+ break;
+ case Tid:
+ id = t->decl->sym->decl;
+ if(id == nil)
+ id = undefed(&t->src, t->decl->sym);
+ /* save a little space */
+ id->sym->unbound = nil;
+ t->decl = id;
+ break;
+ case Ttuple:
+ case Texception:
+ for(id = t->ids; id != nil; id = id->next)
+ bindtypes(id->ty);
+ break;
+ case Tfn:
+ if(t->polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, t->polys);
+ }
+ for(id = t->ids; id != nil; id = id->next)
+ bindtypes(id->ty);
+ bindtypes(t->tof);
+ if(t->val != nil)
+ mergepolydecs(t);
+ if(t->polys != nil){
+ popscope();
+ for(id = t->polys; id != nil; id = id->next)
+ bindtypes(id->ty);
+ }
+ break;
+ case Tinst:
+ bindtypes(t->tof);
+ for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+ bindtypes(tl->t);
+ break;
+ default:
+ fatal("bindtypes: unknown type kind %d", t->kind);
+ }
+}
+
+/*
+ * walk the type checking for validity
+ */
+Type*
+verifytypes(Type *t, Decl *adtt, Decl *poly)
+{
+ Node *n;
+ Decl *id, *id1, *last;
+ char buf[32];
+ int i, cyc;
+ Ok ok, ok1;
+ double max;
+ Typelist *tl;
+
+ if(t == nil)
+ return nil;
+ if((t->ok & OKverify) == OKverify)
+ return t;
+ t->ok |= OKverify;
+if((t->ok & (OKverify|OKbind)) != (OKverify|OKbind))
+fatal("verifytypes bogus ok for %t", t);
+ cyc = t->flags&CYCLIC;
+ switch(t->kind){
+ case Terror:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tiface:
+ case Tainit:
+ case Talt:
+ case Tcase:
+ case Tcasel:
+ case Tcasec:
+ case Tgoto:
+ case Texcept:
+ break;
+ case Tfix:
+ n = t->val;
+ max = 0.0;
+ if(n->op == Oseq){
+ ok = echeck(n->left, 0, 0, n);
+ ok1 = echeck(n->right, 0, 0, n);
+ if(!ok.ok || !ok1.ok)
+ return terror;
+ if(n->left->ty != treal || n->right->ty != treal){
+ error(t->src.start, "fixed point scale/maximum not real");
+ return terror;
+ }
+ n->right = fold(n->right);
+ if(n->right->op != Oconst){
+ error(t->src.start, "fixed point maximum not constant");
+ return terror;
+ }
+ if((max = n->right->rval) <= 0){
+ error(t->src.start, "non-positive fixed point maximum");
+ return terror;
+ }
+ n = n->left;
+ }
+ else{
+ ok = echeck(n, 0, 0, nil);
+ if(!ok.ok)
+ return terror;
+ if(n->ty != treal){
+ error(t->src.start, "fixed point scale not real");
+ return terror;
+ }
+ }
+ n = t->val = fold(n);
+ if(n->op != Oconst){
+ error(t->src.start, "fixed point scale not constant");
+ return terror;
+ }
+ if(n->rval <= 0){
+ error(t->src.start, "non-positive fixed point scale");
+ return terror;
+ }
+ ckfix(t, max);
+ break;
+ case Tref:
+ t->tof = comtype(&t->src, verifytypes(t->tof, adtt, nil), adtt);
+ if(t->tof != nil && !tattr[t->tof->kind].refable){
+ error(t->src.start, "cannot have a ref %T", t->tof);
+ return terror;
+ }
+ if(0 && t->tof->kind == Tfn && t->tof->ids != nil && t->tof->ids->implicit)
+ error(t->src.start, "function references cannot have a self argument");
+ if(0 && t->tof->kind == Tfn && t->polys != nil)
+ error(t->src.start, "function references cannot be polymorphic");
+ break;
+ case Tchan:
+ case Tarray:
+ case Tlist:
+ t->tof = comtype(&t->src, toptype(&t->src, verifytypes(t->tof, adtt, nil)), adtt);
+ break;
+ case Tid:
+ t->ok &= ~OKverify;
+ t = verifytypes(idtype(t), adtt, nil);
+ break;
+ case Tarrow:
+ t->ok &= ~OKverify;
+ t = verifytypes(arrowtype(t, adtt), adtt, nil);
+ break;
+ case Tdot:
+ /*
+ * verify the parent adt & lookup the tag fields
+ */
+ t->ok &= ~OKverify;
+ t = verifytypes(dottype(t, adtt), adtt, nil);
+ break;
+ case Tadt:
+ /*
+ * this is where Tadt may get tag fields added
+ */
+ adtdefd(t);
+ break;
+ case Tadtpick:
+ for(id = t->ids; id != nil; id = id->next){
+ id->ty = topvartype(verifytypes(id->ty, id->dot, nil), id, 0, 1);
+ if(id->store == Dconst)
+ error(t->src.start, "pick fields cannot be a con like %s", id->sym->name);
+ }
+ verifytypes(t->decl->dot->ty, nil, nil);
+ break;
+ case Tmodule:
+ for(id = t->ids; id != nil; id = id->next){
+ id->ty = verifytypes(id->ty, nil, nil);
+ if(id->store == Dglobal && id->ty->kind == Tfn)
+ id->store = Dfn;
+ if(id->store != Dtype && id->store != Dfn)
+ topvartype(id->ty, id, 0, 0);
+ }
+ break;
+ case Ttuple:
+ case Texception:
+ if(t->decl == nil){
+ t->decl = mkdecl(&t->src, Dtype, t);
+ t->decl->sym = enter(".tuple", 0);
+ }
+ i = 0;
+ for(id = t->ids; id != nil; id = id->next){
+ id->store = Dfield;
+ if(id->sym == nil){
+ seprint(buf, buf+sizeof(buf), "t%d", i);
+ id->sym = enter(buf, 0);
+ }
+ i++;
+ id->ty = toptype(&id->src, verifytypes(id->ty, adtt, nil));
+ /* id->ty = comtype(&id->src, toptype(&id->src, verifytypes(id->ty, adtt, nil)), adtt); */
+ }
+ break;
+ case Tfn:
+ last = nil;
+ for(id = t->ids; id != nil; id = id->next){
+ id->store = Darg;
+ id->ty = topvartype(verifytypes(id->ty, adtt, nil), id, 0, 1);
+ if(id->implicit){
+ Decl *selfd;
+
+ selfd = poly ? poly : adtt;
+ if(selfd == nil)
+ error(t->src.start, "function is not a member of an adt, so can't use self");
+ else if(id != t->ids)
+ error(id->src.start, "only the first argument can use self");
+ else if(id->ty != selfd->ty && (id->ty->kind != Tref || id->ty->tof != selfd->ty))
+ error(id->src.start, "self argument's type must be %s or ref %s",
+ selfd->sym->name, selfd->sym->name);
+ }
+ last = id;
+ }
+ for(id = t->polys; id != nil; id = id->next){
+ if(adtt != nil){
+ for(id1 = adtt->ty->polys; id1 != nil; id1 = id1->next){
+ if(id1->sym == id->sym)
+ id->ty = id1->ty;
+ }
+ }
+ id->store = Dtype;
+ id->ty = verifytypes(id->ty, adtt, nil);
+ }
+ t->tof = comtype(&t->src, toptype(&t->src, verifytypes(t->tof, adtt, nil)), adtt);
+ if(t->varargs && (last == nil || last->ty != tstring))
+ error(t->src.start, "variable arguments must be preceded by a string");
+ if(t->varargs && t->polys != nil)
+ error(t->src.start, "polymorphic functions must not have variable arguments");
+ break;
+ case Tpoly:
+ for(id = t->ids; id != nil; id = id->next){
+ id->store = Dfn;
+ id->ty = verifytypes(id->ty, adtt, t->decl);
+ }
+ break;
+ case Tinst:
+ t->ok &= ~OKverify;
+ t->tof = verifytypes(t->tof, adtt, nil);
+ for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+ tl->t = verifytypes(tl->t, adtt, nil);
+ t = verifytypes(insttype(t, adtt, nil), adtt, nil);
+ break;
+ default:
+ fatal("verifytypes: unknown type kind %d", t->kind);
+ }
+ if(cyc)
+ t->flags |= CYCLIC;
+ return t;
+}
+
+/*
+ * resolve an id type
+ */
+Type*
+idtype(Type *t)
+{
+ Decl *id;
+ Type *tt;
+
+ id = t->decl;
+ if(id->store == Dunbound)
+ fatal("idtype: unbound decl");
+ tt = id->ty;
+ if(id->store != Dtype && id->store != Dtag){
+ if(id->store == Dundef){
+ id->store = Dwundef;
+ error(t->src.start, "%s is not declared", id->sym->name);
+ }else if(id->store == Dimport){
+ id->store = Dwundef;
+ error(t->src.start, "%s's type cannot be determined", id->sym->name);
+ }else if(id->store != Dwundef)
+ error(t->src.start, "%s is not a type", id->sym->name);
+ return terror;
+ }
+ if(tt == nil){
+ error(t->src.start, "%t not fully defined", t);
+ return terror;
+ }
+ return tt;
+}
+
+/*
+ * resolve a -> qualified type
+ */
+Type*
+arrowtype(Type *t, Decl *adtt)
+{
+ Type *tt;
+ Decl *id;
+
+ id = t->decl;
+ if(id->ty != nil){
+ if(id->store == Dunbound)
+ fatal("arrowtype: unbound decl has a type");
+ return id->ty;
+ }
+
+ /*
+ * special hack to allow module variables to derive other types
+ */
+ tt = t->tof;
+ if(tt->kind == Tid){
+ id = tt->decl;
+ if(id->store == Dunbound)
+ fatal("arrowtype: Tid's decl unbound");
+ if(id->store == Dimport){
+ id->store = Dwundef;
+ error(t->src.start, "%s's type cannot be determined", id->sym->name);
+ return terror;
+ }
+
+ /*
+ * forward references to module variables can't be resolved
+ */
+ if(id->store != Dtype && !(id->ty->ok & OKbind)){
+ error(t->src.start, "%s's type cannot be determined", id->sym->name);
+ return terror;
+ }
+
+ if(id->store == Dwundef)
+ return terror;
+ tt = id->ty = verifytypes(id->ty, adtt, nil);
+ if(tt == nil){
+ error(t->tof->src.start, "%T is not a module", t->tof);
+ return terror;
+ }
+ }else
+ tt = verifytypes(t->tof, adtt, nil);
+ t->tof = tt;
+ if(tt == terror)
+ return terror;
+ if(tt->kind != Tmodule){
+ error(t->src.start, "%T is not a module", tt);
+ return terror;
+ }
+ id = namedot(tt->ids, t->decl->sym);
+ if(id == nil){
+ error(t->src.start, "%s is not a member of %T", t->decl->sym->name, tt);
+ return terror;
+ }
+ if(id->store == Dtype && id->ty != nil){
+ t->decl = id;
+ return id->ty;
+ }
+ error(t->src.start, "%T is not a type", t);
+ return terror;
+}
+
+/*
+ * resolve a . qualified type
+ */
+Type*
+dottype(Type *t, Decl *adtt)
+{
+ Type *tt;
+ Decl *id;
+
+ if(t->decl->ty != nil){
+ if(t->decl->store == Dunbound)
+ fatal("dottype: unbound decl has a type");
+ return t->decl->ty;
+ }
+ t->tof = tt = verifytypes(t->tof, adtt, nil);
+ if(tt == terror)
+ return terror;
+ if(tt->kind != Tadt){
+ error(t->src.start, "%T is not an adt", tt);
+ return terror;
+ }
+ id = namedot(tt->tags, t->decl->sym);
+ if(id != nil && id->ty != nil){
+ t->decl = id;
+ return id->ty;
+ }
+ error(t->src.start, "%s is not a pick tag of %T", t->decl->sym->name, tt);
+ return terror;
+}
+
+Type*
+insttype(Type *t, Decl *adtt, Tpair **tp)
+{
+ Type *tt;
+ Typelist *tl;
+ Decl *ids;
+ Tpair *tp1, *tp2;
+ Src src;
+
+ src = t->src;
+ if(tp == nil){
+ tp2 = nil;
+ tp = &tp2;
+ }
+ if(t->tof->kind != Tadt && t->tof->kind != Tadtpick){
+ error(src.start, "%T is not an adt", t->tof);
+ return terror;
+ }
+ if(t->tof->kind == Tadt)
+ ids = t->tof->polys;
+ else
+ ids = t->tof->decl->dot->ty->polys;
+ if(ids == nil){
+ error(src.start, "%T is not a polymorphic adt", t->tof);
+ return terror;
+ }
+ for(tl = t->u.tlist; tl != nil && ids != nil; tl = tl->nxt, ids = ids->next){
+ tt = tl->t;
+ if(!tattr[tt->kind].isptr){
+ error(src.start, "%T is not a pointer type", tt);
+ return terror;
+ }
+ unifysrc = src;
+ if(!tunify(ids->ty, tt, &tp1)){
+ error(src.start, "type %T does not match %T", tt, ids->ty);
+ return terror;
+ }
+ /* usetype(tt); */
+ tt = verifytypes(tt, adtt, nil);
+ addtmap(ids->ty, tt, tp);
+ }
+ if(tl != nil){
+ error(src.start, "too many actual types in instantiation");
+ return terror;
+ }
+ if(ids != nil){
+ error(src.start, "too few actual types in instantiation");
+ return terror;
+ }
+ tp1 = *tp;
+ tt = t->tof;
+ t = expandtype(tt, t, adtt, tp);
+ if(t == tt && adtt == nil)
+ t = duptype(t);
+ if(t != tt){
+ t->u.tmap = tp1;
+ if(debug['w']){
+ print("tmap for %T: ", t);
+ for( ; tp1!=nil; tp1=tp1->nxt)
+ print("%T -> %T ", tp1->t1, tp1->t2);
+ print("\n");
+ }
+ }
+ t->src = src;
+ return t;
+}
+
+/*
+ * walk a type, putting all adts, modules, and tuples into equivalence classes
+ */
+void
+teqclass(Type *t)
+{
+ Decl *id, *tg;
+ Teq *teq;
+
+ if(t == nil || (t->ok & OKclass) == OKclass)
+ return;
+ t->ok |= OKclass;
+ switch(t->kind){
+ case Terror:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tiface:
+ case Tainit:
+ case Talt:
+ case Tcase:
+ case Tcasel:
+ case Tcasec:
+ case Tgoto:
+ case Texcept:
+ case Tfix:
+ case Tpoly:
+ return;
+ case Tref:
+ teqclass(t->tof);
+ return;
+ case Tchan:
+ case Tarray:
+ case Tlist:
+ teqclass(t->tof);
+ if(!debug['Z'])
+ return;
+ break;
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ case Texception:
+ for(id = t->ids; id != nil; id = id->next)
+ teqclass(id->ty);
+ for(tg = t->tags; tg != nil; tg = tg->next)
+ teqclass(tg->ty);
+ for(id = t->polys; id != nil; id = id->next)
+ teqclass(id->ty);
+ break;
+ case Tmodule:
+ t->tof = mkiface(t->decl);
+ for(id = t->ids; id != nil; id = id->next)
+ teqclass(id->ty);
+ break;
+ case Tfn:
+ for(id = t->ids; id != nil; id = id->next)
+ teqclass(id->ty);
+ for(id = t->polys; id != nil; id = id->next)
+ teqclass(id->ty);
+ teqclass(t->tof);
+ return;
+ default:
+ fatal("teqclass: unknown type kind %d", t->kind);
+ return;
+ }
+
+ /*
+ * find an equivalent type
+ * stupid linear lookup could be made faster
+ */
+ if((t->ok & OKsized) != OKsized)
+ fatal("eqclass type not sized: %t", t);
+
+ for(teq = eqclass[t->kind]; teq != nil; teq = teq->eq){
+ if(t->size == teq->ty->size && tequal(t, teq->ty)){
+ t->eq = teq;
+ if(t->kind == Tmodule)
+ joiniface(t, t->eq->ty->tof);
+ return;
+ }
+ }
+
+ /*
+ * if no equiv type, make one
+ */
+ t->eq = allocmem(sizeof(Teq));
+ t->eq->id = 0;
+ t->eq->ty = t;
+ t->eq->eq = eqclass[t->kind];
+ eqclass[t->kind] = t->eq;
+}
+
+/*
+ * record that we've used the type
+ * using a type uses all types reachable from that type
+ */
+void
+reftype(Type *t)
+{
+ Decl *id, *tg;
+
+ if(t == nil || (t->ok & OKref) == OKref)
+ return;
+ t->ok |= OKref;
+ if(t->decl != nil && t->decl->refs == 0)
+ t->decl->refs++;
+ switch(t->kind){
+ case Terror:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tiface:
+ case Tainit:
+ case Talt:
+ case Tcase:
+ case Tcasel:
+ case Tcasec:
+ case Tgoto:
+ case Texcept:
+ case Tfix:
+ case Tpoly:
+ break;
+ case Tref:
+ case Tchan:
+ case Tarray:
+ case Tlist:
+ if(t->decl != nil){
+ if(nadts >= lenadts){
+ lenadts = nadts + 32;
+ adts = reallocmem(adts, lenadts * sizeof *adts);
+ }
+ adts[nadts++] = t->decl;
+ }
+ reftype(t->tof);
+ break;
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ case Texception:
+ if(t->kind == Tadt || t->kind == Ttuple && t->decl->sym != anontupsym){
+ if(nadts >= lenadts){
+ lenadts = nadts + 32;
+ adts = reallocmem(adts, lenadts * sizeof *adts);
+ }
+ adts[nadts++] = t->decl;
+ }
+ for(id = t->ids; id != nil; id = id->next)
+ if(id->store != Dfn)
+ reftype(id->ty);
+ for(tg = t->tags; tg != nil; tg = tg->next)
+ reftype(tg->ty);
+ for(id = t->polys; id != nil; id = id->next)
+ reftype(id->ty);
+ if(t->kind == Tadtpick)
+ reftype(t->decl->dot->ty);
+ break;
+ case Tmodule:
+ /*
+ * a module's elements should get used individually
+ * but do the globals for any sbl file
+ */
+ if(bsym != nil)
+ for(id = t->ids; id != nil; id = id->next)
+ if(id->store == Dglobal)
+ reftype(id->ty);
+ break;
+ case Tfn:
+ for(id = t->ids; id != nil; id = id->next)
+ reftype(id->ty);
+ for(id = t->polys; id != nil; id = id->next)
+ reftype(id->ty);
+ reftype(t->tof);
+ break;
+ default:
+ fatal("reftype: unknown type kind %d", t->kind);
+ break;
+ }
+}
+
+/*
+ * check all reachable types for cycles and illegal forward references
+ * find the size of all the types
+ */
+void
+cycsizetype(Type *t)
+{
+ Decl *id, *tg;
+
+ if(t == nil || (t->ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
+ return;
+ t->ok |= OKcycsize;
+ switch(t->kind){
+ case Terror:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tiface:
+ case Tainit:
+ case Talt:
+ case Tcase:
+ case Tcasel:
+ case Tcasec:
+ case Tgoto:
+ case Texcept:
+ case Tfix:
+ case Tpoly:
+ t->ok |= OKcyc;
+ sizetype(t);
+ break;
+ case Tref:
+ case Tchan:
+ case Tarray:
+ case Tlist:
+ cyctype(t);
+ sizetype(t);
+ cycsizetype(t->tof);
+ break;
+ case Tadt:
+ case Ttuple:
+ case Texception:
+ cyctype(t);
+ sizetype(t);
+ for(id = t->ids; id != nil; id = id->next)
+ cycsizetype(id->ty);
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ if((tg->ty->ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
+ continue;
+ tg->ty->ok |= (OKcycsize|OKcyc|OKsized);
+ for(id = tg->ty->ids; id != nil; id = id->next)
+ cycsizetype(id->ty);
+ }
+ for(id = t->polys; id != nil; id = id->next)
+ cycsizetype(id->ty);
+ break;
+ case Tadtpick:
+ t->ok &= ~OKcycsize;
+ cycsizetype(t->decl->dot->ty);
+ break;
+ case Tmodule:
+ cyctype(t);
+ sizetype(t);
+ for(id = t->ids; id != nil; id = id->next)
+ cycsizetype(id->ty);
+ sizeids(t->ids, 0);
+ break;
+ case Tfn:
+ cyctype(t);
+ sizetype(t);
+ for(id = t->ids; id != nil; id = id->next)
+ cycsizetype(id->ty);
+ for(id = t->polys; id != nil; id = id->next)
+ cycsizetype(id->ty);
+ cycsizetype(t->tof);
+ sizeids(t->ids, MaxTemp);
+ break;
+ default:
+ fatal("cycsizetype: unknown type kind %d", t->kind);
+ break;
+ }
+}
+
+/* check for circularity in type declarations
+ * - has to be called before verifytypes
+ */
+void
+tcycle(Type *t)
+{
+ Decl *id;
+ Type *tt;
+ Typelist *tl;
+
+ if(t == nil)
+ return;
+ switch(t->kind){
+ default:
+ break;
+ case Tchan:
+ case Tarray:
+ case Tref:
+ case Tlist:
+ case Tdot:
+ tcycle(t->tof);
+ break;
+ case Tfn:
+ case Ttuple:
+ tcycle(t->tof);
+ for(id = t->ids; id != nil; id = id->next)
+ tcycle(id->ty);
+ break;
+ case Tarrow:
+ if(t->rec&TRvis){
+ error(t->src.start, "circularity in definition of %T", t);
+ *t = *terror; /* break the cycle */
+ return;
+ }
+ tt = t->tof;
+ t->rec |= TRvis;
+ tcycle(tt);
+ if(tt->kind == Tid)
+ tt = tt->decl->ty;
+ id = namedot(tt->ids, t->decl->sym);
+ if(id != nil)
+ tcycle(id->ty);
+ t->rec &= ~TRvis;
+ break;
+ case Tid:
+ if(t->rec&TRvis){
+ error(t->src.start, "circularity in definition of %T", t);
+ *t = *terror; /* break the cycle */
+ return;
+ }
+ t->rec |= TRvis;
+ tcycle(t->decl->ty);
+ t->rec &= ~TRvis;
+ break;
+ case Tinst:
+ tcycle(t->tof);
+ for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+ tcycle(tl->t);
+ break;
+ }
+}
+
+/*
+ * marks for checking for arcs
+ */
+enum
+{
+ ArcValue = 1 << 0,
+ ArcList = 1 << 1,
+ ArcArray = 1 << 2,
+ ArcRef = 1 << 3,
+ ArcCyc = 1 << 4, /* cycle found */
+ ArcPolycyc = 1 << 5,
+};
+
+void
+cyctype(Type *t)
+{
+ Decl *id, *tg;
+
+ if((t->ok & OKcyc) == OKcyc)
+ return;
+ t->ok |= OKcyc;
+ t->rec |= TRcyc;
+ switch(t->kind){
+ case Terror:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tfn:
+ case Tchan:
+ case Tarray:
+ case Tref:
+ case Tlist:
+ case Tfix:
+ case Tpoly:
+ break;
+ case Tadt:
+ case Tmodule:
+ case Ttuple:
+ case Texception:
+ for(id = t->ids; id != nil; id = id->next)
+ cycfield(t, id);
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ if((tg->ty->ok & OKcyc) == OKcyc)
+ continue;
+ tg->ty->ok |= OKcyc;
+ for(id = tg->ty->ids; id != nil; id = id->next)
+ cycfield(t, id);
+ }
+ break;
+ default:
+ fatal("checktype: unknown type kind %d", t->kind);
+ break;
+ }
+ t->rec &= ~TRcyc;
+}
+
+void
+cycfield(Type *base, Decl *id)
+{
+ int arc;
+
+ if(!storespace[id->store])
+ return;
+ arc = cycarc(base, id->ty);
+
+ if((arc & (ArcCyc|ArcValue)) == (ArcCyc|ArcValue)){
+ if(id->cycerr == 0)
+ error(base->src.start, "illegal type cycle without a reference in field %s of %t",
+ id->sym->name, base);
+ id->cycerr = 1;
+ }else if(arc & ArcCyc){
+ if((arc & ArcArray) && id->cyc == 0 && !(arc & ArcPolycyc)){
+ if(id->cycerr == 0)
+ error(base->src.start, "illegal circular reference to type %T in field %s of %t",
+ id->ty, id->sym->name, base);
+ id->cycerr = 1;
+ }
+ id->cycle = 1;
+ }else if(id->cyc != 0){
+ if(id->cycerr == 0)
+ error(id->src.start, "spurious cyclic qualifier for field %s of %t", id->sym->name, base);
+ id->cycerr = 1;
+ }
+}
+
+int
+cycarc(Type *base, Type *t)
+{
+ Decl *id, *tg;
+ int me, arc;
+
+ if(t == nil)
+ return 0;
+ if(t->rec & TRcyc){
+ if(tequal(t, base)){
+ if(t->kind == Tmodule)
+ return ArcCyc | ArcRef;
+ else
+ return ArcCyc | ArcValue;
+ }
+ return 0;
+ }
+ t->rec |= TRcyc;
+ me = 0;
+ switch(t->kind){
+ case Terror:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tchan:
+ case Tfn:
+ case Tfix:
+ case Tpoly:
+ break;
+ case Tarray:
+ me = cycarc(base, t->tof) & ~ArcValue | ArcArray;
+ break;
+ case Tref:
+ me = cycarc(base, t->tof) & ~ArcValue | ArcRef;
+ break;
+ case Tlist:
+ me = cycarc(base, t->tof) & ~ArcValue | ArcList;
+ break;
+ case Tadt:
+ case Tadtpick:
+ case Tmodule:
+ case Ttuple:
+ case Texception:
+ me = 0;
+ for(id = t->ids; id != nil; id = id->next){
+ if(!storespace[id->store])
+ continue;
+ arc = cycarc(base, id->ty);
+ if((arc & ArcCyc) && id->cycerr == 0)
+ me |= arc;
+ }
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ arc = cycarc(base, tg->ty);
+ if((arc & ArcCyc) && tg->cycerr == 0)
+ me |= arc;
+ }
+
+ if(t->kind == Tmodule)
+ me = me & ArcCyc | ArcRef | ArcPolycyc;
+ else
+ me &= ArcCyc | ArcValue | ArcPolycyc;
+ break;
+ default:
+ fatal("cycarc: unknown type kind %d", t->kind);
+ break;
+ }
+ t->rec &= ~TRcyc;
+ if(t->flags&CYCLIC)
+ me |= ArcPolycyc;
+ return me;
+}
+
+/*
+ * set the sizes and field offsets for t
+ * look only as deeply as needed to size this type.
+ * cycsize type will clean up the rest.
+ */
+void
+sizetype(Type *t)
+{
+ Decl *id, *tg;
+ Szal szal;
+ long sz, al, a;
+
+ if(t == nil)
+ return;
+ if((t->ok & OKsized) == OKsized)
+ return;
+ t->ok |= OKsized;
+if((t->ok & (OKverify|OKsized)) != (OKverify|OKsized))
+fatal("sizetype bogus ok for %t", t);
+ switch(t->kind){
+ default:
+ fatal("sizetype: unknown type kind %d", t->kind);
+ break;
+ case Terror:
+ case Tnone:
+ case Tbyte:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Tany:
+ case Treal:
+ fatal("%T should have a size", t);
+ break;
+ case Tref:
+ case Tchan:
+ case Tarray:
+ case Tlist:
+ case Tmodule:
+ case Tfix:
+ case Tpoly:
+ t->size = t->align = IBY2WD;
+ break;
+ case Ttuple:
+ case Tadt:
+ case Texception:
+ if(t->tags == nil){
+ if(!debug['z']){
+ szal = sizeids(t->ids, 0);
+ t->size = align(szal.size, szal.align);
+ t->align = szal.align;
+ }else{
+ szal = sizeids(t->ids, 0);
+ t->align = IBY2LG;
+ t->size = align(szal.size, IBY2LG);
+ }
+ return;
+ }
+ if(!debug['z']){
+ szal = sizeids(t->ids, IBY2WD);
+ sz = szal.size;
+ al = szal.align;
+ if(al < IBY2WD)
+ al = IBY2WD;
+ }else{
+ szal = sizeids(t->ids, IBY2WD);
+ sz = szal.size;
+ al = IBY2LG;
+ }
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ if((tg->ty->ok & OKsized) == OKsized)
+ continue;
+ tg->ty->ok |= OKsized;
+ if(!debug['z']){
+ szal = sizeids(tg->ty->ids, sz);
+ a = szal.align;
+ if(a < al)
+ a = al;
+ tg->ty->size = align(szal.size, a);
+ tg->ty->align = a;
+ }else{
+ szal = sizeids(tg->ty->ids, sz);
+ tg->ty->size = align(szal.size, IBY2LG);
+ tg->ty->align = IBY2LG;
+ }
+ }
+ break;
+ case Tfn:
+ t->size = 0;
+ t->align = 1;
+ break;
+ case Tainit:
+ t->size = 0;
+ t->align = 1;
+ break;
+ case Talt:
+ t->size = t->cse->nlab * 2*IBY2WD + 2*IBY2WD;
+ t->align = IBY2WD;
+ break;
+ case Tcase:
+ case Tcasec:
+ t->size = t->cse->nlab * 3*IBY2WD + 2*IBY2WD;
+ t->align = IBY2WD;
+ break;
+ case Tcasel:
+ t->size = t->cse->nlab * 6*IBY2WD + 3*IBY2WD;
+ t->align = IBY2LG;
+ break;
+ case Tgoto:
+ t->size = t->cse->nlab * IBY2WD + IBY2WD;
+ if(t->cse->iwild != nil)
+ t->size += IBY2WD;
+ t->align = IBY2WD;
+ break;
+ case Tiface:
+ sz = IBY2WD;
+ for(id = t->ids; id != nil; id = id->next){
+ sz = align(sz, IBY2WD) + IBY2WD;
+ sz += id->sym->len + 1;
+ if(id->dot->ty->kind == Tadt)
+ sz += id->dot->sym->len + 1;
+ }
+ t->size = sz;
+ t->align = IBY2WD;
+ break;
+ case Texcept:
+ t->size = 0;
+ t->align = IBY2WD;
+ break;
+ }
+}
+
+Szal
+sizeids(Decl *id, long off)
+{
+ Szal szal;
+ int a, al;
+
+ al = 1;
+ for(; id != nil; id = id->next){
+ if(storespace[id->store]){
+ sizetype(id->ty);
+ /*
+ * alignment can be 0 if we have
+ * illegal forward declarations.
+ * just patch a; other code will flag an error
+ */
+ a = id->ty->align;
+ if(a == 0)
+ a = 1;
+
+ if(a > al)
+ al = a;
+
+ off = align(off, a);
+ id->offset = off;
+ off += id->ty->size;
+ }
+ }
+ szal.size = off;
+ szal.align = al;
+ return szal;
+}
+
+long
+align(long off, int align)
+{
+ if(align == 0)
+ fatal("align 0");
+ while(off % align)
+ off++;
+ return off;
+}
+
+/*
+ * recalculate a type's size
+ */
+void
+resizetype(Type *t)
+{
+ if((t->ok & OKsized) == OKsized){
+ t->ok &= ~OKsized;
+ cycsizetype(t);
+ }
+}
+
+/*
+ * check if a module is accessable from t
+ * if so, mark that module interface
+ */
+void
+modrefable(Type *t)
+{
+ Decl *id, *m, *tg;
+
+ if(t == nil || (t->ok & OKmodref) == OKmodref)
+ return;
+ if((t->ok & OKverify) != OKverify)
+ fatal("modrefable unused type %t", t);
+ t->ok |= OKmodref;
+ switch(t->kind){
+ case Terror:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tnone:
+ case Tany:
+ case Tfix:
+ case Tpoly:
+ break;
+ case Tchan:
+ case Tref:
+ case Tarray:
+ case Tlist:
+ modrefable(t->tof);
+ break;
+ case Tmodule:
+ t->tof->linkall = 1;
+ t->decl->refs++;
+ for(id = t->ids; id != nil; id = id->next){
+ switch(id->store){
+ case Dglobal:
+ case Dfn:
+ modrefable(id->ty);
+ break;
+ case Dtype:
+ if(id->ty->kind != Tadt)
+ break;
+ for(m = id->ty->ids; m != nil; m = m->next)
+ if(m->store == Dfn)
+ modrefable(m->ty);
+ break;
+ }
+ }
+ break;
+ case Tfn:
+ case Tadt:
+ case Ttuple:
+ case Texception:
+ for(id = t->ids; id != nil; id = id->next)
+ if(id->store != Dfn)
+ modrefable(id->ty);
+ for(tg = t->tags; tg != nil; tg = tg->next){
+/*
+ if((tg->ty->ok & OKmodref) == OKmodref)
+ continue;
+*/
+ tg->ty->ok |= OKmodref;
+ for(id = tg->ty->ids; id != nil; id = id->next)
+ modrefable(id->ty);
+ }
+ for(id = t->polys; id != nil; id = id->next)
+ modrefable(id->ty);
+ modrefable(t->tof);
+ break;
+ case Tadtpick:
+ modrefable(t->decl->dot->ty);
+ break;
+ default:
+ fatal("unknown type kind %d", t->kind);
+ break;
+ }
+}
+
+Desc*
+gendesc(Decl *d, long size, Decl *decls)
+{
+ Desc *desc;
+
+ if(debug['D'])
+ print("generate desc for %D\n", d);
+ if(ispoly(d))
+ addfnptrs(d, 0);
+ desc = usedesc(mkdesc(size, decls));
+ return desc;
+}
+
+Desc*
+mkdesc(long size, Decl *d)
+{
+ uchar *pmap;
+ long len, n;
+
+ len = (size+8*IBY2WD-1) / (8*IBY2WD);
+ pmap = allocmem(len);
+ memset(pmap, 0, len);
+ n = descmap(d, pmap, 0);
+ if(n >= 0)
+ n = n / (8*IBY2WD) + 1;
+ else
+ n = 0;
+ if(n > len)
+ fatal("wrote off end of decl map: %ld %ld", n, len);
+ return enterdesc(pmap, size, n);
+}
+
+Desc*
+mktdesc(Type *t)
+{
+ Desc *d;
+ uchar *pmap;
+ long len, n;
+
+usedty(t);
+ if(debug['D'])
+ print("generate desc for %T\n", t);
+ if(t->decl == nil){
+ t->decl = mkdecl(&t->src, Dtype, t);
+ t->decl->sym = enter("_mktdesc_", 0);
+ }
+ if(t->decl->desc != nil)
+ return t->decl->desc;
+ len = (t->size+8*IBY2WD-1) / (8*IBY2WD);
+ pmap = allocmem(len);
+ memset(pmap, 0, len);
+ n = tdescmap(t, pmap, 0);
+ if(n >= 0)
+ n = n / (8*IBY2WD) + 1;
+ else
+ n = 0;
+ if(n > len)
+ fatal("wrote off end of type map for %T: %ld %ld 0x%2.2ux", t, n, len, t->ok);
+ d = enterdesc(pmap, t->size, n);
+ t->decl->desc = d;
+ if(debug['j']){
+ uchar *m, *e;
+
+ print("generate desc for %T\n", t);
+ print("\tdesc\t$%d,%lud,\"", d->id, d->size);
+ e = d->map + d->nmap;
+ for(m = d->map; m < e; m++)
+ print("%.2x", *m);
+ print("\"\n");
+ }
+ return d;
+}
+
+Desc*
+enterdesc(uchar *map, long size, long nmap)
+{
+ Desc *d, *last;
+ int c;
+
+ last = nil;
+ for(d = descriptors; d != nil; d = d->next){
+ if(d->size > size || d->size == size && d->nmap > nmap)
+ break;
+ if(d->size == size && d->nmap == nmap){
+ c = memcmp(d->map, map, nmap);
+ if(c == 0){
+ free(map);
+ return d;
+ }
+ if(c > 0)
+ break;
+ }
+ last = d;
+ }
+ d = allocmem(sizeof *d);
+ d->id = -1;
+ d->used = 0;
+ d->map = map;
+ d->size = size;
+ d->nmap = nmap;
+ if(last == nil){
+ d->next = descriptors;
+ descriptors = d;
+ }else{
+ d->next = last->next;
+ last->next = d;
+ }
+ return d;
+}
+
+Desc*
+usedesc(Desc *d)
+{
+ d->used = 1;
+ return d;
+}
+
+/*
+ * create the pointer description byte map for every type in decls
+ * each bit corresponds to a word, and is 1 if occupied by a pointer
+ * the high bit in the byte maps the first word
+ */
+long
+descmap(Decl *decls, uchar *map, long start)
+{
+ Decl *d;
+ long last, m;
+
+ if(debug['D'])
+ print("descmap offset %ld\n", start);
+ last = -1;
+ for(d = decls; d != nil; d = d->next){
+ if(d->store == Dtype && d->ty->kind == Tmodule
+ || d->store == Dfn
+ || d->store == Dconst)
+ continue;
+ if(d->store == Dlocal && d->link != nil)
+ continue;
+ m = tdescmap(d->ty, map, d->offset + start);
+ if(debug['D']){
+ if(d->sym != nil)
+ print("descmap %s type %T offset %ld returns %ld\n",
+ d->sym->name, d->ty, d->offset+start, m);
+ else
+ print("descmap type %T offset %ld returns %ld\n", d->ty, d->offset+start, m);
+ }
+ if(m >= 0)
+ last = m;
+ }
+ return last;
+}
+
+long
+tdescmap(Type *t, uchar *map, long offset)
+{
+ Label *lab;
+ long i, e, m;
+ int bit;
+
+ if(t == nil)
+ return -1;
+
+ m = -1;
+ if(t->kind == Talt){
+ lab = t->cse->labs;
+ e = t->cse->nlab;
+ offset += IBY2WD * 2;
+ for(i = 0; i < e; i++){
+ if(lab[i].isptr){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
+ m = offset;
+ }
+ offset += 2*IBY2WD;
+ }
+ return m;
+ }
+ if(t->kind == Tcasec){
+ e = t->cse->nlab;
+ offset += IBY2WD;
+ for(i = 0; i < e; i++){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
+ offset += IBY2WD;
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
+ m = offset;
+ offset += 2*IBY2WD;
+ }
+ return m;
+ }
+
+ if(tattr[t->kind].isptr){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
+ return offset;
+ }
+ if(t->kind == Tadtpick)
+ t = t->tof;
+ if(t->kind == Ttuple || t->kind == Tadt || t->kind == Texception){
+ if(debug['D'])
+ print("descmap adt offset %ld\n", offset);
+ if(t->rec != 0)
+ fatal("illegal cyclic type %t in tdescmap", t);
+ t->rec = 1;
+ offset = descmap(t->ids, map, offset);
+ t->rec = 0;
+ return offset;
+ }
+
+ return -1;
+}
+
+/*
+ * can a t2 be assigned to a t1?
+ * any means Tany matches all types,
+ * not just references
+ */
+int
+tcompat(Type *t1, Type *t2, int any)
+{
+ int ok, v;
+
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t2->kind == Texception && t1->kind != Texception)
+ t2 = mkextuptype(t2);
+ tcomset = 0;
+ ok = rtcompat(t1, t2, any, 0);
+ v = cleartcomrec(t1) + cleartcomrec(t2);
+ if(v != tcomset)
+ fatal("recid t1 %t and t2 %t not balanced in tcompat: %d v %d", t1, t2, v, tcomset);
+ return ok;
+}
+
+static int
+rtcompat(Type *t1, Type *t2, int any, int inaorc)
+{
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1->kind == Terror || t2->kind == Terror)
+ return 1;
+ if(t2->kind == Texception && t1->kind != Texception)
+ t2 = mkextuptype(t2);
+
+ if(debug['x'])
+ print("rtcompat: %t and %t\n", t1, t2);
+
+ t1->rec |= TRcom;
+ t2->rec |= TRcom;
+ switch(t1->kind){
+ default:
+ fatal("unknown type %t v %t in rtcompat", t1, t2);
+ case Tstring:
+ return t2->kind == Tstring || t2->kind == Tany;
+ case Texception:
+ if(t2->kind == Texception && t1->cons == t2->cons){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1->ids, t2->ids, 0, inaorc);
+ }
+ return 0;
+ case Tnone:
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ return t1->kind == t2->kind;
+ case Tfix:
+ return t1->kind == t2->kind && sametree(t1->val, t2->val);
+ case Tany:
+ if(tattr[t2->kind].isptr)
+ return 1;
+ return any;
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ if(t1->kind != t2->kind){
+ if(t2->kind == Tany)
+ return 1;
+ return 0;
+ }
+ if(t1->kind != Tref && assumetcom(t1, t2))
+ return 1;
+ return rtcompat(t1->tof, t2->tof, 0, t1->kind == Tarray || t1->kind == Tchan || inaorc);
+ case Tfn:
+ break;
+ case Ttuple:
+ if(t2->kind == Tadt && t2->tags == nil
+ || t2->kind == Ttuple){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1->ids, t2->ids, any, inaorc);
+ }
+ if(t2->kind == Tadtpick){
+ t2->tof->rec |= TRcom;
+ if(assumetcom(t1, t2->tof))
+ return 1;
+ return idcompat(t1->ids, t2->tof->ids->next, any, inaorc);
+ }
+ return 0;
+ case Tadt:
+ if(t2->kind == Ttuple && t1->tags == nil){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1->ids, t2->ids, any, inaorc);
+ }
+ if(t1->tags != nil && t2->kind == Tadtpick && !inaorc)
+ t2 = t2->decl->dot->ty;
+ break;
+ case Tadtpick:
+/*
+ if(t2->kind == Ttuple)
+ return idcompat(t1->tof->ids->next, t2->ids, any, inaorc);
+*/
+ break;
+ case Tmodule:
+ if(t2->kind == Tany)
+ return 1;
+ break;
+ case Tpoly:
+ if(t2->kind == Tany)
+ return 1;
+ break;
+ }
+ return tequal(t1, t2);
+}
+
+/*
+ * add the assumption that t1 and t2 are compatable
+ */
+static int
+assumetcom(Type *t1, Type *t2)
+{
+ Type *r1, *r2;
+
+ if(t1->tcom == nil && t2->tcom == nil){
+ tcomset += 2;
+ t1->tcom = t2->tcom = t1;
+ }else{
+ if(t1->tcom == nil){
+ r1 = t1;
+ t1 = t2;
+ t2 = r1;
+ }
+ for(r1 = t1->tcom; r1 != r1->tcom; r1 = r1->tcom)
+ ;
+ for(r2 = t2->tcom; r2 != nil && r2 != r2->tcom; r2 = r2->tcom)
+ ;
+ if(r1 == r2)
+ return 1;
+ if(r2 == nil)
+ tcomset++;
+ t2->tcom = t1;
+ for(; t2 != r1; t2 = r2){
+ r2 = t2->tcom;
+ t2->tcom = r1;
+ }
+ }
+ return 0;
+}
+
+static int
+cleartcomrec(Type *t)
+{
+ Decl *id;
+ int n;
+
+ n = 0;
+ for(; t != nil && (t->rec & TRcom) == TRcom; t = t->tof){
+ t->rec &= ~TRcom;
+ if(t->tcom != nil){
+ t->tcom = nil;
+ n++;
+ }
+ if(t->kind == Tadtpick)
+ n += cleartcomrec(t->tof);
+ if(t->kind == Tmodule)
+ t = t->tof;
+ for(id = t->ids; id != nil; id = id->next)
+ n += cleartcomrec(id->ty);
+ for(id = t->tags; id != nil; id = id->next)
+ n += cleartcomrec(id->ty);
+ for(id = t->polys; id != nil; id = id->next)
+ n += cleartcomrec(id->ty);
+ }
+ return n;
+}
+
+/*
+ * id1 and id2 are the fields in an adt or tuple
+ * simple structural check; ignore names
+ */
+static int
+idcompat(Decl *id1, Decl *id2, int any, int inaorc)
+{
+ for(; id1 != nil; id1 = id1->next){
+ if(id1->store != Dfield)
+ continue;
+ while(id2 != nil && id2->store != Dfield)
+ id2 = id2->next;
+ if(id2 == nil
+ || id1->store != id2->store
+ || !rtcompat(id1->ty, id2->ty, any, inaorc))
+ return 0;
+ id2 = id2->next;
+ }
+ while(id2 != nil && id2->store != Dfield)
+ id2 = id2->next;
+ return id2 == nil;
+}
+
+int
+tequal(Type *t1, Type *t2)
+{
+ int ok, v;
+
+ eqrec = 0;
+ eqset = 0;
+ ok = rtequal(t1, t2);
+ v = cleareqrec(t1) + cleareqrec(t2);
+ if(v != eqset && 0)
+ fatal("recid t1 %t and t2 %t not balanced in tequal: %d %d", t1, t2, v, eqset);
+ eqset = 0;
+ return ok;
+}
+
+/*
+ * structural equality on types
+ */
+static int
+rtequal(Type *t1, Type *t2)
+{
+ /*
+ * this is just a shortcut
+ */
+ if(t1 == t2)
+ return 1;
+
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1->kind == Terror || t2->kind == Terror)
+ return 1;
+
+ if(t1->kind != t2->kind)
+ return 0;
+
+ if(t1->eq != nil && t2->eq != nil)
+ return t1->eq == t2->eq;
+
+ if(debug['x'])
+ print("rtequal: %t and %t\n", t1, t2);
+
+ t1->rec |= TReq;
+ t2->rec |= TReq;
+ switch(t1->kind){
+ default:
+ fatal("unknown type %t v %t in rtequal", t1, t2);
+ case Tnone:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ case Tint:
+ case Tstring:
+ /*
+ * this should always be caught by t1 == t2 check
+ */
+ fatal("bogus value type %t vs %t in rtequal", t1, t2);
+ return 1;
+ case Tfix:
+ return sametree(t1->val, t2->val);
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ if(t1->kind != Tref && assumeteq(t1, t2))
+ return 1;
+ return rtequal(t1->tof, t2->tof);
+ case Tfn:
+ if(t1->varargs != t2->varargs)
+ return 0;
+ if(!idequal(t1->ids, t2->ids, 0, storespace))
+ return 0;
+ /* if(!idequal(t1->polys, t2->polys, 1, nil)) */
+ if(!pyequal(t1, t2))
+ return 0;
+ return rtequal(t1->tof, t2->tof);
+ case Ttuple:
+ case Texception:
+ if(t1->kind != t2->kind || t1->cons != t2->cons)
+ return 0;
+ if(assumeteq(t1, t2))
+ return 1;
+ return idequal(t1->ids, t2->ids, 0, storespace);
+ case Tadt:
+ case Tadtpick:
+ case Tmodule:
+ if(assumeteq(t1, t2))
+ return 1;
+ /*
+ * compare interfaces when comparing modules
+ */
+ if(t1->kind == Tmodule)
+ return idequal(t1->tof->ids, t2->tof->ids, 1, nil);
+
+ /*
+ * picked adts; check parent,
+ * assuming equiv picked fields,
+ * then check picked fields are equiv
+ */
+ if(t1->kind == Tadtpick && !rtequal(t1->decl->dot->ty, t2->decl->dot->ty))
+ return 0;
+
+ /*
+ * adts with pick tags: check picked fields for equality
+ */
+ if(!idequal(t1->tags, t2->tags, 1, nil))
+ return 0;
+
+ /* if(!idequal(t1->polys, t2->polys, 1, nil)) */
+ if(!pyequal(t1, t2))
+ return 0;
+ return idequal(t1->ids, t2->ids, 1, storespace);
+ case Tpoly:
+ if(assumeteq(t1, t2))
+ return 1;
+ if(t1->decl->sym != t2->decl->sym)
+ return 0;
+ return idequal(t1->ids, t2->ids, 1, nil);
+ }
+}
+
+static int
+assumeteq(Type *t1, Type *t2)
+{
+ Type *r1, *r2;
+
+ if(t1->teq == nil && t2->teq == nil){
+ eqrec++;
+ eqset += 2;
+ t1->teq = t2->teq = t1;
+ }else{
+ if(t1->teq == nil){
+ r1 = t1;
+ t1 = t2;
+ t2 = r1;
+ }
+ for(r1 = t1->teq; r1 != r1->teq; r1 = r1->teq)
+ ;
+ for(r2 = t2->teq; r2 != nil && r2 != r2->teq; r2 = r2->teq)
+ ;
+ if(r1 == r2)
+ return 1;
+ if(r2 == nil)
+ eqset++;
+ t2->teq = t1;
+ for(; t2 != r1; t2 = r2){
+ r2 = t2->teq;
+ t2->teq = r1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * checking structural equality for adts, tuples, and fns
+ */
+static int
+idequal(Decl *id1, Decl *id2, int usenames, int *storeok)
+{
+ /*
+ * this is just a shortcut
+ */
+ if(id1 == id2)
+ return 1;
+
+ for(; id1 != nil; id1 = id1->next){
+ if(storeok != nil && !storeok[id1->store])
+ continue;
+ while(id2 != nil && storeok != nil && !storeok[id2->store])
+ id2 = id2->next;
+ if(id2 == nil
+ || usenames && id1->sym != id2->sym
+ || id1->store != id2->store
+ || id1->implicit != id2->implicit
+ || id1->cyc != id2->cyc
+ || (id1->dot == nil) != (id2->dot == nil)
+ || id1->dot != nil && id2->dot != nil && id1->dot->ty->kind != id2->dot->ty->kind
+ || !rtequal(id1->ty, id2->ty))
+ return 0;
+ id2 = id2->next;
+ }
+ while(id2 != nil && storeok != nil && !storeok[id2->store])
+ id2 = id2->next;
+ return id1 == nil && id2 == nil;
+}
+
+static int
+pyequal(Type *t1, Type *t2)
+{
+ Type *pt1, *pt2;
+ Decl *id1, *id2;
+
+ if(t1 == t2)
+ return 1;
+ id1 = t1->polys;
+ id2 = t2->polys;
+ for(; id1 != nil; id1 = id1->next){
+ if(id2 == nil)
+ return 0;
+ pt1 = id1->ty;
+ pt2 = id2->ty;
+ if(!rtequal(pt1, pt2)){
+ if(t1->u.tmap != nil)
+ pt1 = valtmap(pt1, t1->u.tmap);
+ if(t2->u.tmap != nil)
+ pt2 = valtmap(pt2, t2->u.tmap);
+ if(!rtequal(pt1, pt2))
+ return 0;
+ }
+ id2 = id2->next;
+ }
+ return id1 == nil && id2 == nil;
+}
+
+static int
+cleareqrec(Type *t)
+{
+ Decl *id;
+ int n;
+
+ n = 0;
+ for(; t != nil && (t->rec & TReq) == TReq; t = t->tof){
+ t->rec &= ~TReq;
+ if(t->teq != nil){
+ t->teq = nil;
+ n++;
+ }
+ if(t->kind == Tadtpick)
+ n += cleareqrec(t->decl->dot->ty);
+ if(t->kind == Tmodule)
+ t = t->tof;
+ for(id = t->ids; id != nil; id = id->next)
+ n += cleareqrec(id->ty);
+ for(id = t->tags; id != nil; id = id->next)
+ n += cleareqrec(id->ty);
+ for(id = t->polys; id != nil; id = id->next)
+ n += cleareqrec(id->ty);
+ }
+ return n;
+}
+
+int
+raisescompat(Node *n1, Node *n2)
+{
+ if(n1 == n2)
+ return 1;
+ if(n2 == nil)
+ return 1; /* no need to repeat in definition if given in declaration */
+ if(n1 == nil)
+ return 0;
+ for(n1 = n1->left, n2 = n2->left; n1 != nil && n2 != nil; n1 = n1->right, n2 = n2->right){
+ if(n1->left->decl != n2->left->decl)
+ return 0;
+ }
+ return n1 == n2;
+}
+
+/* t1 a polymorphic type */
+static int
+fnunify(Type *t1, Type *t2, Tpair **tp, int swapped)
+{
+ Decl *id, *ids;
+ Sym *sym;
+
+ for(ids = t1->ids; ids != nil; ids = ids->next){
+ sym = ids->sym;
+ id = fnlookup(sym, t2, nil);
+ if(id != nil)
+ usetype(id->ty);
+ if(id == nil){
+ if(dowarn)
+ error(unifysrc.start, "type %T does not have a '%s' function", t2, sym->name);
+ return 0;
+ }
+ else if(id->ty->kind != Tfn){
+ if(dowarn)
+ error(unifysrc.start, "%T is not a function", id->ty);
+ return 0;
+ }
+ else if(!rtunify(ids->ty, id->ty, tp, !swapped)){
+ if(dowarn)
+ error(unifysrc.start, "%T and %T are not compatible wrt %s", ids->ty, id->ty, sym->name);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+fncleareqrec(Type *t1, Type *t2)
+{
+ Decl *id, *ids;
+ int n;
+
+ n = 0;
+ n += cleareqrec(t1);
+ n += cleareqrec(t2);
+ for(ids = t1->ids; ids != nil; ids = ids->next){
+ id = fnlookup(ids->sym, t2, nil);
+ if(id == nil)
+ continue;
+ else{
+ n += cleareqrec(ids->ty);
+ n += cleareqrec(id->ty);
+ }
+ }
+ return n;
+}
+int
+tunify(Type *t1, Type *t2, Tpair **tp)
+{
+ int ok, v;
+ Tpair *p;
+
+ *tp = nil;
+ eqrec = 0;
+ eqset = 0;
+ ok = rtunify(t1, t2, tp, 0);
+ v = cleareqrec(t1) + cleareqrec(t2);
+ for(p = *tp; p != nil; p = p->nxt)
+ v += fncleareqrec(p->t1, p->t2);
+ if(0 && v != eqset)
+ fatal("recid t1 %t and t2 %t not balanced in tunify: %d %d", t1, t2, v, eqset);
+ return ok;
+}
+
+static int
+rtunify(Type *t1, Type *t2, Tpair **tp, int swapped)
+{
+ Type *tmp;
+
+if(debug['w']) print("rtunifya - %T %T\n", t1, t2);
+ t1 = valtmap(t1, *tp);
+ t2 = valtmap(t2, *tp);
+if(debug['w']) print("rtunifyb - %T %T\n", t1, t2);
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1->kind == Terror || t2->kind == Terror)
+ return 1;
+ if(t1->kind != Tpoly && t2->kind == Tpoly){
+ tmp = t1;
+ t1 = t2;
+ t2 = tmp;
+ swapped = !swapped;
+ }
+ if(t1->kind == Tpoly){
+/*
+ if(typein(t1, t2))
+ return 0;
+*/
+ if(!tattr[t2->kind].isptr)
+ return 0;
+ if(t2->kind != Tany)
+ addtmap(t1, t2, tp);
+ return fnunify(t1, t2, tp, swapped);
+ }
+ if(t1->kind != Tany && t2->kind == Tany){
+ tmp = t1;
+ t1 = t2;
+ t2 = tmp;
+ swapped = !swapped;
+ }
+ if(t1->kind == Tadt && t1->tags != nil && t2->kind == Tadtpick && !swapped)
+ t2 = t2->decl->dot->ty;
+ if(t2->kind == Tadt && t2->tags != nil && t1->kind == Tadtpick && swapped)
+ t1 = t1->decl->dot->ty;
+ if(t1->kind != Tany && t1->kind != t2->kind)
+ return 0;
+ t1->rec |= TReq;
+ t2->rec |= TReq;
+ switch(t1->kind){
+ default:
+ return tequal(t1, t2);
+ case Tany:
+ return tattr[t2->kind].isptr;
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ if(t1->kind != Tref && assumeteq(t1, t2))
+ return 1;
+ return rtunify(t1->tof, t2->tof, tp, swapped);
+ case Tfn:
+ if(!idunify(t1->ids, t2->ids, tp, swapped))
+ return 0;
+ if(!idunify(t1->polys, t2->polys, tp, swapped))
+ return 0;
+ return rtunify(t1->tof, t2->tof, tp, swapped);
+ case Ttuple:
+ if(assumeteq(t1, t2))
+ return 1;
+ return idunify(t1->ids, t2->ids, tp, swapped);
+ case Tadt:
+ case Tadtpick:
+ if(assumeteq(t1, t2))
+ return 1;
+ if(!idunify(t1->polys, t2->polys, tp, swapped))
+ return 0;
+ if(!idunify(t1->tags, t2->tags, tp, swapped))
+ return 0;
+ return idunify(t1->ids, t2->ids, tp, swapped);
+ case Tmodule:
+ if(assumeteq(t1, t2))
+ return 1;
+ return idunify(t1->tof->ids, t2->tof->ids, tp, swapped);
+ case Tpoly:
+ return t1 == t2;
+ }
+}
+
+static int
+idunify(Decl *id1, Decl *id2, Tpair **tp, int swapped)
+{
+ if(id1 == id2)
+ return 1;
+ for(; id1 != nil; id1 = id1->next){
+ if(id2 == nil || !rtunify(id1->ty, id2->ty, tp, swapped))
+ return 0;
+ id2 = id2->next;
+ }
+ return id1 == nil && id2 == nil;
+}
+
+int
+polyequal(Decl *id1, Decl *id2)
+{
+ int ck2;
+ Decl *d;
+
+ /* allow id2 list to have an optional for clause */
+ ck2 = 0;
+ for(d = id2; d != nil; d = d->next)
+ if(d->ty->ids != nil)
+ ck2 = 1;
+ for( ; id1 != nil; id1 = id1->next){
+ if(id2 == nil
+ || id1->sym != id2->sym
+ || id1->ty->decl != nil && id2->ty->decl != nil && id1->ty->decl->sym != id2->ty->decl->sym)
+ return 0;
+ if(ck2 && !idequal(id1->ty->ids, id2->ty->ids, 1, nil))
+ return 0;
+ id2 = id2->next;
+ }
+ return id1 == nil && id2 == nil;
+}
+
+Type*
+calltype(Type *f, Node *a, Type *rt)
+{
+ Type *t;
+ Decl *id, *first, *last;
+
+ first = last = nil;
+ t = mktype(&f->src.start, &f->src.stop, Tfn, rt, nil);
+ t->polys = f->kind == Tref ? f->tof->polys : f->polys;
+ for( ; a != nil; a = a->right){
+ id = mkdecl(&f->src, Darg, a->left->ty);
+ if(last == nil)
+ first = id;
+ else
+ last->next = id;
+ last = id;
+ }
+ t->ids = first;
+ if(f->kind == Tref)
+ t = mktype(&f->src.start, &f->src.stop, Tref, t, nil);
+ return t;
+}
+
+static Type*
+duptype(Type *t)
+{
+ Type *nt;
+
+ nt = allocmem(sizeof(*nt));
+ *nt = *t;
+ nt->ok &= ~(OKverify|OKref|OKclass|OKsized|OKcycsize|OKcyc);
+ nt->flags |= INST;
+ nt->eq = nil;
+ nt->sbl = -1;
+ if(t->decl != nil && (nt->kind == Tadt || nt->kind == Tadtpick || nt->kind == Ttuple)){
+ nt->decl = dupdecl(t->decl);
+ nt->decl->ty = nt;
+ nt->decl->link = t->decl;
+ if(t->decl->dot != nil){
+ nt->decl->dot = dupdecl(t->decl->dot);
+ nt->decl->dot->link = t->decl->dot;
+ }
+ }
+ else
+ nt->decl = nil;
+ return nt;
+}
+
+static int
+dpolys(Decl *ids)
+{
+ Decl *p;
+
+ for(p = ids; p != nil; p = p->next)
+ if(tpolys(p->ty))
+ return 1;
+ return 0;
+}
+
+static int
+tpolys(Type *t)
+{
+ int v;
+ Typelist *tl;
+
+ if(t == nil)
+ return 0;
+ if(t->flags&(POLY|NOPOLY))
+ return t->flags&POLY;
+ switch(t->kind){
+ default:
+ v = 0;
+ break;
+ case Tarrow:
+ case Tdot:
+ case Tpoly:
+ v = 1;
+ break;
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ v = tpolys(t->tof);
+ break;
+ case Tid:
+ v = tpolys(t->decl->ty);
+ break;
+ case Tinst:
+ v = 0;
+ for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+ if(tpolys(tl->t)){
+ v = 1;
+ break;
+ }
+ if(v == 0)
+ v = tpolys(t->tof);
+ break;
+ case Tfn:
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ case Texception:
+ if(t->polys != nil){
+ v = 1;
+ break;
+ }
+ if(t->rec&TRvis)
+ return 0;
+ t->rec |= TRvis;
+ v = tpolys(t->tof) || dpolys(t->polys) || dpolys(t->ids) || dpolys(t->tags);
+ t->rec &= ~TRvis;
+ if(t->kind == Tadtpick && v == 0)
+ v = tpolys(t->decl->dot->ty);
+ break;
+ }
+ if(v)
+ t->flags |= POLY;
+ else
+ t->flags |= NOPOLY;
+ return v;
+}
+
+static int
+doccurs(Decl *ids, Tpair **tp)
+{
+ Decl *p;
+
+ for(p = ids; p != nil; p = p->next)
+ if(toccurs(p->ty, tp))
+ return 1;
+ return 0;
+}
+
+static int
+toccurs(Type *t, Tpair **tp)
+{
+ int o;
+ Typelist *tl;
+
+ if(t == nil)
+ return 0;
+ if(!(t->flags&(POLY|NOPOLY)))
+ tpolys(t);
+ if(t->flags&NOPOLY)
+ return 0;
+ switch(t->kind){
+ default:
+ fatal("unknown type %t in toccurs", t);
+ case Tnone:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ case Tint:
+ case Tstring:
+ case Tfix:
+ case Tmodule:
+ case Terror:
+ return 0;
+ case Tarrow:
+ case Tdot:
+ return 1;
+ case Tpoly:
+ return valtmap(t, *tp) != t;
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ return toccurs(t->tof, tp);
+ case Tid:
+ return toccurs(t->decl->ty, tp);
+ case Tinst:
+ for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+ if(toccurs(tl->t, tp))
+ return 1;
+ return toccurs(t->tof, tp);
+ case Tfn:
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ case Texception:
+ if(t->rec&TRvis)
+ return 0;
+ t->rec |= TRvis;
+ o = toccurs(t->tof, tp) || doccurs(t->polys, tp) || doccurs(t->ids, tp) || doccurs(t->tags, tp);
+ t->rec &= ~TRvis;
+ if(t->kind == Tadtpick && o == 0)
+ o = toccurs(t->decl->dot->ty, tp);
+ return o;
+ }
+}
+
+static Decl*
+expandids(Decl *ids, Decl *adtt, Tpair **tp, int sym)
+{
+ Decl *p, *q, *nids, *last;
+
+ nids = last = nil;
+ for(p = ids; p != nil; p = p->next){
+ q = dupdecl(p);
+ q->ty = expandtype(p->ty, nil, adtt, tp);
+ if(sym && q->ty->decl != nil)
+ q->sym = q->ty->decl->sym;
+ if(q->store == Dfn){
+if(debug['v']) print("%p->link = %p\n", q, p);
+ q->link = p;
+ }
+ if(nids == nil)
+ nids = q;
+ else
+ last->next = q;
+ last = q;
+ }
+ return nids;
+}
+
+Type*
+expandtype(Type *t, Type *instt, Decl *adtt, Tpair **tp)
+{
+ Type *nt;
+ Decl *ids;
+
+ if(t == nil)
+ return nil;
+if(debug['w']) print("expandtype %d %lux %T\n", t->kind, (ulong)t, t);
+ if(!toccurs(t, tp))
+ return t;
+if(debug['w']) print("\texpanding\n");
+ switch(t->kind){
+ default:
+ fatal("unknown type %t in expandtype", t);
+ case Tpoly:
+ return valtmap(t, *tp);
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ nt = duptype(t);
+ nt->tof = expandtype(t->tof, nil, adtt, tp);
+ return nt;
+ case Tid:
+ return expandtype(idtype(t), nil, adtt, tp);
+ case Tdot:
+ return expandtype(dottype(t, adtt), nil, adtt, tp);
+ case Tarrow:
+ return expandtype(arrowtype(t, adtt), nil, adtt, tp);
+ case Tinst:
+ if((nt = valtmap(t, *tp)) != t)
+ return nt;
+ return expandtype(insttype(t, adtt, tp), nil, adtt, tp);
+ case Tfn:
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ case Texception:
+ if((nt = valtmap(t, *tp)) != t)
+ return nt;
+ if(t->kind == Tadt)
+ adtt = t->decl;
+ nt = duptype(t);
+ addtmap(t, nt, tp);
+ if(instt != nil)
+ addtmap(instt, nt, tp);
+ nt->tof = expandtype(t->tof, nil, adtt, tp);
+ nt->polys = expandids(t->polys, adtt, tp, 1);
+ nt->ids = expandids(t->ids, adtt, tp, 0);
+ nt->tags = expandids(t->tags, adtt, tp, 0);
+ if(t->kind == Tadt){
+ for(ids = nt->tags; ids != nil; ids = ids->next)
+ ids->ty->decl->dot = nt->decl;
+ }
+ if(t->kind == Tadtpick){
+ nt->decl->dot->ty = expandtype(t->decl->dot->ty, nil, adtt, tp);
+ }
+ if((t->kind == Tadt || t->kind == Tadtpick) && t->u.tmap != nil){
+ Tpair *p;
+
+ nt->u.tmap = nil;
+ for(p = t->u.tmap; p != nil; p = p->nxt)
+ addtmap(valtmap(p->t1, *tp), valtmap(p->t2, *tp), &nt->u.tmap);
+ if(debug['w']){
+ print("new tmap for %T->%T: ", t, nt);
+ for(p=nt->u.tmap;p!=nil;p=p->nxt)print("%T -> %T ", p->t1, p->t2);
+ print("\n");
+ }
+ }
+ return nt;
+ }
+}
+
+/*
+ * create type signatures
+ * sign the same information used
+ * for testing type equality
+ */
+ulong
+sign(Decl *d)
+{
+ Type *t;
+ uchar *sig, md5sig[MD5dlen];
+ char buf[StrSize];
+ int i, sigend, sigalloc, v;
+
+ t = d->ty;
+ if(t->sig != 0)
+ return t->sig;
+
+ if(ispoly(d))
+ rmfnptrs(d);
+
+ sig = 0;
+ sigend = -1;
+ sigalloc = 1024;
+ while(sigend < 0 || sigend >= sigalloc){
+ sigalloc *= 2;
+ sig = reallocmem(sig, sigalloc);
+ eqrec = 0;
+ sigend = rtsign(t, sig, sigalloc, 0);
+ v = clearrec(t);
+ if(v != eqrec)
+ fatal("recid not balanced in sign: %d %d", v, eqrec);
+ eqrec = 0;
+ }
+ sig[sigend] = '\0';
+
+ if(signdump != nil){
+ seprint(buf, buf+sizeof(buf), "%D", d);
+ if(strcmp(buf, signdump) == 0){
+ print("sign %D len %d\n", d, sigend);
+ print("%s\n", (char*)sig);
+ }
+ }
+
+ md5(sig, sigend, md5sig, nil);
+ for(i = 0; i < MD5dlen; i += 4)
+ t->sig ^= md5sig[i+0] | (md5sig[i+1]<<8) | (md5sig[i+2]<<16) | (md5sig[i+3]<<24);
+ if(debug['S'])
+ print("signed %D type %T len %d sig %#lux\n", d, t, sigend, t->sig);
+ free(sig);
+ return t->sig;
+}
+
+enum
+{
+ SIGSELF = 'S',
+ SIGVARARGS = '*',
+ SIGCYC = 'y',
+ SIGREC = '@'
+};
+
+static int sigkind[Tend] =
+{
+ /* Tnone */ 'n',
+ /* Tadt */ 'a',
+ /* Tadtpick */ 'p',
+ /* Tarray */ 'A',
+ /* Tbig */ 'B',
+ /* Tbyte */ 'b',
+ /* Tchan */ 'C',
+ /* Treal */ 'r',
+ /* Tfn */ 'f',
+ /* Tint */ 'i',
+ /* Tlist */ 'L',
+ /* Tmodule */ 'm',
+ /* Tref */ 'R',
+ /* Tstring */ 's',
+ /* Ttuple */ 't',
+ /* Texception */ 'e',
+ /* Tfix */ 'x',
+ /* Tpoly */ 'P',
+};
+
+static int
+rtsign(Type *t, uchar *sig, int lensig, int spos)
+{
+ Decl *id, *tg;
+ char name[32];
+ int kind, lenname;
+
+ if(t == nil)
+ return spos;
+
+ if(spos < 0 || spos + 8 >= lensig)
+ return -1;
+
+ if(t->eq != nil && t->eq->id){
+ if(t->eq->id < 0 || t->eq->id > eqrec)
+ fatal("sign rec %T %d %d", t, t->eq->id, eqrec);
+
+ sig[spos++] = SIGREC;
+ seprint(name, name+sizeof(name), "%d", t->eq->id);
+ lenname = strlen(name);
+ if(spos + lenname > lensig)
+ return -1;
+ strcpy((char*)&sig[spos], name);
+ spos += lenname;
+ return spos;
+ }
+ if(t->eq != nil){
+ eqrec++;
+ t->eq->id = eqrec;
+ }
+
+ kind = sigkind[t->kind];
+ sig[spos++] = kind;
+ if(kind == 0)
+ fatal("no sigkind for %t", t);
+
+ t->rec = 1;
+ switch(t->kind){
+ default:
+ fatal("bogus type %t in rtsign", t);
+ return -1;
+ case Tnone:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ case Tint:
+ case Tstring:
+ case Tpoly:
+ return spos;
+ case Tfix:
+ seprint(name, name+sizeof(name), "%g", t->val->rval);
+ lenname = strlen(name);
+ if(spos+lenname-1 >= lensig)
+ return -1;
+ strcpy((char*)&sig[spos], name);
+ spos += lenname;
+ return spos;
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ return rtsign(t->tof, sig, lensig, spos);
+ case Tfn:
+ if(t->varargs != 0)
+ sig[spos++] = SIGVARARGS;
+ if(t->polys != nil)
+ spos = idsign(t->polys, 0, sig, lensig, spos);
+ spos = idsign(t->ids, 0, sig, lensig, spos);
+ if(t->u.eraises)
+ spos = raisessign(t->u.eraises, sig, lensig, spos);
+ return rtsign(t->tof, sig, lensig, spos);
+ case Ttuple:
+ return idsign(t->ids, 0, sig, lensig, spos);
+ case Tadt:
+ /*
+ * this is a little different than in rtequal,
+ * since we flatten the adt we used to represent the globals
+ */
+ if(t->eq == nil){
+ if(strcmp(t->decl->sym->name, ".mp") != 0)
+ fatal("no t->eq field for %t", t);
+ spos--;
+ for(id = t->ids; id != nil; id = id->next){
+ spos = idsign1(id, 1, sig, lensig, spos);
+ if(spos < 0 || spos >= lensig)
+ return -1;
+ sig[spos++] = ';';
+ }
+ return spos;
+ }
+ if(t->polys != nil)
+ spos = idsign(t->polys, 0, sig, lensig, spos);
+ spos = idsign(t->ids, 1, sig, lensig, spos);
+ if(spos < 0 || t->tags == nil)
+ return spos;
+
+ /*
+ * convert closing ')' to a ',', then sign any tags
+ */
+ sig[spos-1] = ',';
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ lenname = tg->sym->len;
+ if(spos + lenname + 2 >= lensig)
+ return -1;
+ strcpy((char*)&sig[spos], tg->sym->name);
+ spos += lenname;
+ sig[spos++] = '=';
+ sig[spos++] = '>';
+
+ spos = rtsign(tg->ty, sig, lensig, spos);
+ if(spos < 0 || spos >= lensig)
+ return -1;
+
+ if(tg->next != nil)
+ sig[spos++] = ',';
+ }
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = ')';
+ return spos;
+ case Tadtpick:
+ spos = idsign(t->ids, 1, sig, lensig, spos);
+ if(spos < 0)
+ return spos;
+ return rtsign(t->decl->dot->ty, sig, lensig, spos);
+ case Tmodule:
+ if(t->tof->linkall == 0)
+ fatal("signing a narrowed module");
+
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = '{';
+ for(id = t->tof->ids; id != nil; id = id->next){
+ if(id->tag)
+ continue;
+ if(strcmp(id->sym->name, ".mp") == 0){
+ spos = rtsign(id->ty, sig, lensig, spos);
+ if(spos < 0)
+ return -1;
+ continue;
+ }
+ spos = idsign1(id, 1, sig, lensig, spos);
+ if(spos < 0 || spos >= lensig)
+ return -1;
+ sig[spos++] = ';';
+ }
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = '}';
+ return spos;
+ }
+}
+
+static int
+idsign(Decl *id, int usenames, uchar *sig, int lensig, int spos)
+{
+ int first;
+
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = '(';
+ first = 1;
+ for(; id != nil; id = id->next){
+ if(id->store == Dlocal)
+ fatal("local %s in idsign", id->sym->name);
+
+ if(!storespace[id->store])
+ continue;
+
+ if(!first){
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = ',';
+ }
+
+ spos = idsign1(id, usenames, sig, lensig, spos);
+ if(spos < 0)
+ return -1;
+ first = 0;
+ }
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = ')';
+ return spos;
+}
+
+static int
+idsign1(Decl *id, int usenames, uchar *sig, int lensig, int spos)
+{
+ char *name;
+ int lenname;
+
+ if(usenames){
+ name = id->sym->name;
+ lenname = id->sym->len;
+ if(spos + lenname + 1 >= lensig)
+ return -1;
+ strcpy((char*)&sig[spos], name);
+ spos += lenname;
+ sig[spos++] = ':';
+ }
+
+ if(spos + 2 >= lensig)
+ return -1;
+
+ if(id->implicit != 0)
+ sig[spos++] = SIGSELF;
+
+ if(id->cyc != 0)
+ sig[spos++] = SIGCYC;
+
+ return rtsign(id->ty, sig, lensig, spos);
+}
+
+static int
+raisessign(Node *n, uchar *sig, int lensig, int spos)
+{
+ int m;
+ char *s;
+ Node *nn;
+
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = '(';
+ for(nn = n->left; nn != nil; nn = nn->right){
+ s = nn->left->decl->sym->name;
+ m = nn->left->decl->sym->len;
+ if(spos+m-1 >= lensig)
+ return -1;
+ strcpy((char*)&sig[spos], s);
+ spos += m;
+ if(nn->right != nil){
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = ',';
+ }
+ }
+ if(spos >= lensig)
+ return -1;
+ sig[spos++] = ')';
+ return spos;
+}
+
+static int
+clearrec(Type *t)
+{
+ Decl *id;
+ int n;
+
+ n = 0;
+ for(; t != nil && t->rec; t = t->tof){
+ t->rec = 0;
+ if(t->eq != nil && t->eq->id != 0){
+ t->eq->id = 0;
+ n++;
+ }
+ if(t->kind == Tmodule){
+ for(id = t->tof->ids; id != nil; id = id->next)
+ n += clearrec(id->ty);
+ return n;
+ }
+ if(t->kind == Tadtpick)
+ n += clearrec(t->decl->dot->ty);
+ for(id = t->ids; id != nil; id = id->next)
+ n += clearrec(id->ty);
+ for(id = t->tags; id != nil; id = id->next)
+ n += clearrec(id->ty);
+ for(id = t->polys; id != nil; id = id->next)
+ n += clearrec(id->ty);
+ }
+ return n;
+}
+
+/* must a variable of the given type be zeroed ? (for uninitialized declarations inside loops) */
+int
+tmustzero(Type *t)
+{
+ if(t==nil)
+ return 0;
+ if(tattr[t->kind].isptr)
+ return 1;
+ if(t->kind == Tadtpick)
+ t = t->tof;
+ if(t->kind == Ttuple || t->kind == Tadt)
+ return mustzero(t->ids);
+ return 0;
+}
+
+int
+mustzero(Decl *decls)
+{
+ Decl *d;
+
+ for (d = decls; d != nil; d = d->next)
+ if (tmustzero(d->ty))
+ return 1;
+ return 0;
+}
+
+int
+typeconv(Fmt *f)
+{
+ Type *t;
+ char *p, buf[1024];
+
+ t = va_arg(f->args, Type*);
+ if(t == nil){
+ p = "nothing";
+ }else{
+ p = buf;
+ buf[0] = 0;
+ tprint(buf, buf+sizeof(buf), t);
+ }
+ return fmtstrcpy(f, p);
+}
+
+int
+stypeconv(Fmt *f)
+{
+ Type *t;
+ char *p, buf[1024];
+
+ t = va_arg(f->args, Type*);
+ if(t == nil){
+ p = "nothing";
+ }else{
+ p = buf;
+ buf[0] = 0;
+ stprint(buf, buf+sizeof(buf), t);
+ }
+ return fmtstrcpy(f, p);
+}
+
+int
+ctypeconv(Fmt *f)
+{
+ Type *t;
+ char buf[1024];
+
+ t = va_arg(f->args, Type*);
+ buf[0] = 0;
+ ctprint(buf, buf+sizeof(buf), t);
+ return fmtstrcpy(f, buf);
+}
+
+char*
+tprint(char *buf, char *end, Type *t)
+{
+ Decl *id;
+ Typelist *tl;
+
+ if(t == nil)
+ return buf;
+ if(t->kind >= Tend)
+ return seprint(buf, end, "kind %d", t->kind);
+ switch(t->kind){
+ case Tarrow:
+ buf = seprint(buf, end, "%T->%s", t->tof, t->decl->sym->name);
+ break;
+ case Tdot:
+ buf = seprint(buf, end, "%T.%s", t->tof, t->decl->sym->name);
+ break;
+ case Tid:
+ case Tpoly:
+ buf = seprint(buf, end, "%s", t->decl->sym->name);
+ break;
+ case Tinst:
+ buf = tprint(buf, end, t->tof);
+ buf = secpy(buf ,end, "[");
+ for(tl = t->u.tlist; tl != nil; tl = tl->nxt){
+ buf = tprint(buf, end, tl->t);
+ if(tl->nxt != nil)
+ buf = secpy(buf, end, ", ");
+ }
+ buf = secpy(buf, end, "]");
+ break;
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Treal:
+ case Tbyte:
+ case Tany:
+ case Tnone:
+ case Terror:
+ case Tainit:
+ case Talt:
+ case Tcase:
+ case Tcasel:
+ case Tcasec:
+ case Tgoto:
+ case Tiface:
+ case Texception:
+ case Texcept:
+ buf = secpy(buf, end, kindname[t->kind]);
+ break;
+ case Tfix:
+ buf = seprint(buf, end, "%s(%v)", kindname[t->kind], t->val);
+ break;
+ case Tref:
+ buf = secpy(buf, end, "ref ");
+ buf = tprint(buf, end, t->tof);
+ break;
+ case Tchan:
+ case Tarray:
+ case Tlist:
+ buf = seprint(buf, end, "%s of ", kindname[t->kind]);
+ buf = tprint(buf, end, t->tof);
+ break;
+ case Tadtpick:
+ buf = seprint(buf, end, "%s.%s", t->decl->dot->sym->name, t->decl->sym->name);
+ break;
+ case Tadt:
+ if(t->decl->dot != nil && !isimpmod(t->decl->dot->sym))
+ buf = seprint(buf, end, "%s->%s", t->decl->dot->sym->name, t->decl->sym->name);
+ else
+ buf = seprint(buf, end, "%s", t->decl->sym->name);
+ if(t->polys != nil){
+ buf = secpy(buf ,end, "[");
+ for(id = t->polys; id != nil; id = id->next){
+ if(t->u.tmap != nil)
+ buf = tprint(buf, end, valtmap(id->ty, t->u.tmap));
+ else
+ buf = seprint(buf, end, "%s", id->sym->name);
+ if(id->next != nil)
+ buf = secpy(buf, end, ", ");
+ }
+ buf = secpy(buf, end, "]");
+ }
+ break;
+ case Tmodule:
+ buf = seprint(buf, end, "%s", t->decl->sym->name);
+ break;
+ case Ttuple:
+ buf = secpy(buf, end, "(");
+ for(id = t->ids; id != nil; id = id->next){
+ buf = tprint(buf, end, id->ty);
+ if(id->next != nil)
+ buf = secpy(buf, end, ", ");
+ }
+ buf = secpy(buf, end, ")");
+ break;
+ case Tfn:
+ buf = secpy(buf, end, "fn");
+ if(t->polys != nil){
+ buf = secpy(buf, end, "[");
+ for(id = t->polys; id != nil; id = id->next){
+ buf = seprint(buf, end, "%s", id->sym->name);
+ if(id->next != nil)
+ buf = secpy(buf, end, ", ");
+ }
+ buf = secpy(buf, end, "]");
+ }
+ buf = secpy(buf, end, "(");
+ for(id = t->ids; id != nil; id = id->next){
+ if(id->sym == nil)
+ buf = secpy(buf, end, "nil: ");
+ else
+ buf = seprint(buf, end, "%s: ", id->sym->name);
+ if(id->implicit)
+ buf = secpy(buf, end, "self ");
+ buf = tprint(buf, end, id->ty);
+ if(id->next != nil)
+ buf = secpy(buf, end, ", ");
+ }
+ if(t->varargs && t->ids != nil)
+ buf = secpy(buf, end, ", *");
+ else if(t->varargs)
+ buf = secpy(buf, end, "*");
+ if(t->tof != nil && t->tof->kind != Tnone){
+ buf = secpy(buf, end, "): ");
+ buf = tprint(buf, end, t->tof);
+ break;
+ }
+ buf = secpy(buf, end, ")");
+ break;
+ default:
+ yyerror("tprint: unknown type kind %d", t->kind);
+ break;
+ }
+ return buf;
+}
+
+char*
+stprint(char *buf, char *end, Type *t)
+{
+ if(t == nil)
+ return buf;
+ switch(t->kind){
+ case Tid:
+ return seprint(buf, end, "id %s", t->decl->sym->name);
+ case Tadt:
+ case Tadtpick:
+ case Tmodule:
+ buf = secpy(buf, end, kindname[t->kind]);
+ buf = secpy(buf, end, " ");
+ return tprint(buf, end, t);
+ }
+ return tprint(buf, end, t);
+}
+
+/* generalize ref P.A, ref P.B to ref P */
+
+/*
+Type*
+tparentx(Type *t1, Type* t2)
+{
+ if(t1 == nil || t2 == nil || t1->kind != Tref || t2->kind != Tref)
+ return t1;
+ t1 = t1->tof;
+ t2 = t2->tof;
+ if(t1 == nil || t2 == nil || t1->kind != Tadtpick || t2->kind != Tadtpick)
+ return t1;
+ t1 = t1->decl->dot->ty;
+ t2 = t2->decl->dot->ty;
+ if(tequal(t1, t2))
+ return mktype(&t1->src.start, &t1->src.stop, Tref, t1, nil);
+ return t1;
+}
+*/
+
+static int
+tparent0(Type *t1, Type *t2)
+{
+ Decl *id1, *id2;
+
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1->kind == Tadt && t2->kind == Tadtpick)
+ t2 = t2->decl->dot->ty;
+ if(t1->kind == Tadtpick && t2->kind == Tadt)
+ t1 = t1->decl->dot->ty;
+ if(t1->kind != t2->kind)
+ return 0;
+ switch(t1->kind){
+ default:
+ fatal("unknown type %t v %t in tparent", t1, t2);
+ break;
+ case Terror:
+ case Tstring:
+ case Tnone:
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ case Tany:
+ return 1;
+ case Texception:
+ case Tfix:
+ case Tfn:
+ case Tadt:
+ case Tmodule:
+ case Tpoly:
+ return tcompat(t1, t2, 0);
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ return tparent0(t1->tof, t2->tof);
+ case Ttuple:
+ for(id1 = t1->ids, id2 = t2->ids; id1 != nil && id2 != nil; id1 = id1->next, id2 = id2->next)
+ if(!tparent0(id1->ty, id2->ty))
+ return 0;
+ return id1 == nil && id2 == nil;
+ case Tadtpick:
+ return tequal(t1->decl->dot->ty, t2->decl->dot->ty);
+ }
+ return 0;
+}
+
+static Type*
+tparent1(Type *t1, Type *t2)
+{
+ Type *t, *nt;
+ Decl *id, *id1, *id2, *idt;
+
+ if(t1->kind == Tadt && t2->kind == Tadtpick)
+ t2 = t2->decl->dot->ty;
+ if(t1->kind == Tadtpick && t2->kind == Tadt)
+ t1 = t1->decl->dot->ty;
+ switch(t1->kind){
+ default:
+ return t1;
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ t = tparent1(t1->tof, t2->tof);
+ if(t == t1->tof)
+ return t1;
+ return mktype(&t1->src.start, &t1->src.stop, t1->kind, t, nil);
+ case Ttuple:
+ nt = nil;
+ id = nil;
+ for(id1 = t1->ids, id2 = t2->ids; id1 != nil && id2 != nil; id1 = id1->next, id2 = id2->next){
+ t = tparent1(id1->ty, id2->ty);
+ if(t != id1->ty){
+ if(nt == nil){
+ nt = mktype(&t1->src.start, &t1->src.stop, Ttuple, nil, dupdecls(t1->ids));
+ for(id = nt->ids, idt = t1->ids; idt != id1; id = id->next, idt = idt->next)
+ ;
+ }
+ id->ty = t;
+ }
+ if(id != nil)
+ id = id->next;
+ }
+ if(nt == nil)
+ return t1;
+ return nt;
+ case Tadtpick:
+ if(tequal(t1, t2))
+ return t1;
+ return t1->decl->dot->ty;
+ }
+}
+
+Type*
+tparent(Type *t1, Type *t2)
+{
+ if(tparent0(t1, t2))
+ return tparent1(t1, t2);
+ return t1;
+}
+
+/*
+ * make the tuple type used to initialize an exception type
+ */
+Type*
+mkexbasetype(Type *t)
+{
+ Decl *id, *new, *last;
+ Type *nt;
+
+ if(!t->cons)
+ fatal("mkexbasetype on non-constant");
+ last = mkids(&t->decl->src, nil, tstring, nil);
+ last->store = Dfield;
+ nt = mktype(&t->src.start, &t->src.stop, Texception, nil, last);
+ nt->cons = 0;
+ new = mkids(&t->decl->src, nil, tint, nil);
+ new->store = Dfield;
+ last->next = new;
+ last = new;
+ for(id = t->ids; id != nil; id = id->next){
+ new = allocmem(sizeof *id);
+ *new = *id;
+ new->cyc = 0;
+ last->next = new;
+ last = new;
+ }
+ last->next = nil;
+ return usetype(nt);
+}
+
+/*
+ * make an instantiated exception type
+ */
+Type*
+mkextype(Type *t)
+{
+ Type *nt;
+
+ if(!t->cons)
+ fatal("mkextype on non-constant");
+ if(t->tof != nil)
+ return t->tof;
+ nt = copytypeids(t);
+ nt->cons = 0;
+ t->tof = usetype(nt);
+ return t->tof;
+}
+
+/*
+ * convert an instantiated exception type to its underlying type
+ */
+Type*
+mkextuptype(Type *t)
+{
+ Decl *id;
+ Type *nt;
+
+ if(t->cons)
+ return t;
+ if(t->tof != nil)
+ return t->tof;
+ id = t->ids;
+ if(id == nil)
+ nt = t;
+ else if(id->next == nil)
+ nt = id->ty;
+ else{
+ nt = copytypeids(t);
+ nt->cons = 0;
+ nt->kind = Ttuple;
+ }
+ t->tof = usetype(nt);
+ return t->tof;
+}
+
+static void
+ckfix(Type *t, double max)
+{
+ int p;
+ vlong k, x;
+ double s;
+
+ s = t->val->rval;
+ if(max == 0.0)
+ k = ((vlong)1<<32)-1;
+ else
+ k = 2*(vlong)(max/s+0.5)+1;
+ x = 1;
+ for(p = 0; k > x; p++)
+ x *= 2;
+ if(p == 0 || p > 32){
+ error(t->src.start, "cannot fit fixed type into an int");
+ return;
+ }
+ if(p < 32)
+ t->val->rval /= (double)(1<<(32-p));
+}
+
+double
+scale(Type *t)
+{
+ Node *n;
+
+ if(t->kind == Tint || t->kind == Treal)
+ return 1.0;
+ if(t->kind != Tfix)
+ fatal("scale() on non fixed point type");
+ n = t->val;
+ if(n->op != Oconst)
+ fatal("non constant scale");
+ if(n->ty != treal)
+ fatal("non real scale");
+ return n->rval;
+}
+
+double
+scale2(Type *f, Type *t)
+{
+ return scale(f)/scale(t);
+}
+
+#define I(x) ((int)(x))
+#define V(x) ((Long)(x))
+#define D(x) ((double)(x))
+
+/* put x in normal form */
+static int
+nf(double x, int *mant)
+{
+ int p;
+ double m;
+
+ p = 0;
+ m = x;
+ while(m >= 1){
+ p++;
+ m /= 2;
+ }
+ while(m < 0.5){
+ p--;
+ m *= 2;
+ }
+ m *= D(1<<16)*D(1<<15);
+ if(m >= D(0x7fffffff) - 0.5){
+ *mant = 0x7fffffff;
+ return p;
+ }
+ *mant = I(m+0.5);
+ return p;
+}
+
+static int
+ispow2(double x)
+{
+ int m;
+
+ nf(x, &m);
+ if(m != 1<<30)
+ return 0;
+ return 1;
+}
+
+static int
+fround(double x, int n, int *m)
+{
+ if(n != 31)
+ fatal("not 31 in fround");
+ return nf(x, m);
+}
+
+static int
+fixmul2(double sx, double sy, double sr, int *rp, int *ra)
+{
+ int k, n, a;
+ double alpha;
+
+ alpha = (sx*sy)/sr;
+ n = 31;
+ k = fround(1/alpha, n, &a);
+ *rp = 1-k;
+ *ra = 0;
+ return IMULX;
+}
+
+static int
+fixdiv2(double sx, double sy, double sr, int *rp, int *ra)
+{
+ int k, n, b;
+ double beta;
+
+ beta = sx/(sy*sr);
+ n = 31;
+ k = fround(beta, n, &b);
+ *rp = k-1;
+ *ra = 0;
+ return IDIVX;
+}
+
+static int
+fixmul(double sx, double sy, double sr, int *rp, int *ra)
+{
+ int k, m, n, a, v;
+ vlong W;
+ double alpha, eps;
+
+ alpha = (sx*sy)/sr;
+ if(ispow2(alpha))
+ return fixmul2(sx, sy, sr, rp, ra);
+ n = 31;
+ k = fround(1/alpha, n, &a);
+ m = n-k;
+ if(m < -n-1)
+ return IMOVW; /* result is zero whatever the values */
+ v = 0;
+ W = 0;
+ eps = D(1<<m)/(alpha*D(a)) - 1;
+ if(eps < 0){
+ v = a-1;
+ eps = -eps;
+ }
+ if(m < 0 && D(1<<n)*eps*D(a) >= D(a)-1+D(1<<m))
+ W = (V(1)<<(-m)) - 1;
+ if(v != 0 || W != 0)
+ m = m<<2|(v != 0)<<1|(W != 0);
+ *rp = m;
+ *ra = a;
+ return v == 0 && W == 0 ? IMULX0: IMULX1;
+}
+
+static int
+fixdiv(double sx, double sy, double sr, int *rp, int *ra)
+{
+ int k, m, n, b, v;
+ vlong W;
+ double beta, eps;
+
+ beta = sx/(sy*sr);
+ if(ispow2(beta))
+ return fixdiv2(sx, sy, sr, rp, ra);
+ n = 31;
+ k = fround(beta, n, &b);
+ m = k-n;
+ if(m <= -2*n)
+ return IMOVW; /* result is zero whatever the values */
+ v = 0;
+ W = 0;
+ eps = (D(1<<m)*D(b))/beta - 1;
+ if(eps < 0)
+ v = 1;
+ if(m < 0)
+ W = (V(1)<<(-m)) - 1;
+ if(v != 0 || W != 0)
+ m = m<<2|(v != 0)<<1|(W != 0);
+ *rp = m;
+ *ra = b;
+ return v == 0 && W == 0 ? IDIVX0: IDIVX1;
+}
+
+static int
+fixcast(double sx, double sr, int *rp, int *ra)
+{
+ int op;
+
+ op = fixmul(sx, 1.0, sr, rp, ra);
+ return op-IMULX+ICVTXX;
+}
+
+int
+fixop(int op, Type *tx, Type *ty, Type *tr, int *rp, int *ra)
+{
+ double sx, sy, sr;
+
+ sx = scale(tx);
+ sy = scale(ty);
+ sr = scale(tr);
+ if(op == IMULX)
+ op = fixmul(sx, sy, sr, rp, ra);
+ else if(op == IDIVX)
+ op = fixdiv(sx, sy, sr, rp, ra);
+ else
+ op = fixcast(sx, sr, rp, ra);
+ return op;
+}
+
+int
+ispoly(Decl *d)
+{
+ Type *t;
+
+ if(d == nil)
+ return 0;
+ t = d->ty;
+ if(t->kind == Tfn){
+ if(t->polys != nil)
+ return 1;
+ if((d = d->dot) == nil)
+ return 0;
+ t = d->ty;
+ return t->kind == Tadt && t->polys != nil;
+ }
+ return 0;
+}
+
+int
+ispolyadt(Type *t)
+{
+ return (t->kind == Tadt || t->kind == Tadtpick) && t->polys != nil && !(t->flags & INST);
+}
+
+Decl*
+polydecl(Decl *ids)
+{
+ Decl *id;
+ Type *t;
+
+ for(id = ids; id != nil; id = id->next){
+ t = mktype(&id->src.start, &id->src.stop, Tpoly, nil, nil);
+ id->ty = t;
+ t->decl = id;
+ }
+ return ids;
+}
+
+/* try to convert an expression tree to a type */
+Type*
+exptotype(Node *n)
+{
+ Type *t, *tt;
+ Decl *d;
+ Typelist *tl;
+ Src *src;
+
+ if(n == nil)
+ return nil;
+ t = nil;
+ switch(n->op){
+ case Oname:
+ if((d = n->decl) != nil && d->store == Dtype)
+ t = d->ty;
+ break;
+ case Otype:
+ case Ochan:
+ t = n->ty;
+ break;
+ case Oref:
+ t = exptotype(n->left);
+ if(t != nil)
+ t = mktype(&n->src.start, &n->src.stop, Tref, t, nil);
+ break;
+ case Odot:
+ t = exptotype(n->left);
+ if(t != nil){
+ d = namedot(t->tags, n->right->decl->sym);
+ if(d == nil)
+ t = nil;
+ else
+ t = d->ty;
+ }
+ if(t == nil)
+ t = exptotype(n->right);
+ break;
+ case Omdot:
+ t = exptotype(n->right);
+ break;
+ case Oindex:
+ t = exptotype(n->left);
+ if(t != nil){
+ src = &n->src;
+ tl = nil;
+ for(n = n->right; n != nil; n = n->right){
+ if(n->op == Oseq)
+ tt = exptotype(n->left);
+ else
+ tt = exptotype(n);
+ if(tt == nil)
+ return nil;
+ tl = addtype(tt, tl);
+ if(n->op != Oseq)
+ break;
+ }
+ t = mkinsttype(src, t, tl);
+ }
+ break;
+ }
+ return t;
+}
+
+static char*
+uname(Decl *im)
+{
+ Decl *p;
+ int n;
+ char *s;
+
+ n = 0;
+ for(p = im; p != nil; p = p->next)
+ n += strlen(p->sym->name)+1;
+ s = allocmem(n);
+ strcpy(s, "");
+ for(p = im; p != nil; p = p->next){
+ strcat(s, p->sym->name);
+ if(p->next != nil)
+ strcat(s, "+");
+ }
+ return s;
+}
+
+/* check all implementation modules have consistent declarations
+ * and create their union if needed
+ */
+Decl*
+modimp(Dlist *dl, Decl *im)
+{
+ Decl *u, *d, *dd, *ids, *dot, *last;
+ Sym *s;
+ Dlist *dl0;
+ long sg, sg0;
+ char buf[StrSize], *un;
+
+ if(dl->next == nil)
+ return dl->d;
+ dl0 = dl;
+ sg0 = 0;
+ un = uname(im);
+ seprint(buf, buf+sizeof(buf), ".m.%s", un);
+ installids(Dglobal, mkids(&dl->d->src, enter(buf, 0), tnone, nil));
+ u = dupdecl(dl->d);
+ u->sym = enter(un, 0);
+ u->sym->decl = u;
+ u->ty = mktype(&u->src.start, &u->src.stop, Tmodule, nil, nil);
+ u->ty->decl = u;
+ last = nil;
+ for( ; dl != nil; dl = dl->next){
+ d = dl->d;
+ ids = d->ty->tof->ids; /* iface */
+ if(ids != nil && ids->store == Dglobal) /* .mp */
+ sg = sign(ids);
+ else
+ sg = 0;
+ if(dl == dl0)
+ sg0 = sg;
+ else if(sg != sg0)
+ error(d->src.start, "%s's module data not consistent with that of %s\n", d->sym->name, dl0->d->sym->name);
+ for(ids = d->ty->ids; ids != nil; ids = ids->next){
+ s = ids->sym;
+ if(s->decl != nil && s->decl->scope >= scope){
+ if(ids == s->decl){
+ dd = dupdecl(ids);
+ if(u->ty->ids == nil)
+ u->ty->ids = dd;
+ else
+ last->next = dd;
+ last = dd;
+ continue;
+ }
+ dot = s->decl->dot;
+ if(s->decl->store != Dwundef && dot != nil && dot != d && isimpmod(dot->sym) && dequal(ids, s->decl, 1))
+ ids->refs = s->decl->refs;
+ else
+ redecl(ids);
+ ids->init = s->decl->init;
+ }
+ }
+ }
+ u->ty = usetype(u->ty);
+ return u;
+}
+
+static void
+modres(Decl *d)
+{
+ Decl *ids, *id, *n, *i;
+ Type *t;
+
+ for(ids = d->ty->ids; ids != nil; ids = ids->next){
+ id = ids->sym->decl;
+ if(ids != id){
+ n = ids->next;
+ i = ids->iface;
+ t = ids->ty;
+ *ids = *id;
+ ids->next = n;
+ ids->iface = i;
+ ids->ty = t;
+ }
+ }
+}
+
+/* update the fields of duplicate declarations in other implementation modules
+ * and their union
+ */
+void
+modresolve(void)
+{
+ Dlist *dl;
+
+ dl = impdecls;
+ if(dl->next == nil)
+ return;
+ for( ; dl != nil; dl = dl->next)
+ modres(dl->d);
+ modres(impdecl);
+}
--- /dev/null
+++ b/scripts/startinferno
@@ -1,0 +1,3 @@
+#!/usr/bin/env sh
+emu -g1280x1024 wm/wm
+
--- /dev/null
+++ b/services/httpd/httpd.rewrite
@@ -1,0 +1,10 @@
+# syntax: pattern replacement
+# The most specific pattern wins, and is applied once (no rescanning).
+# Leave off trailing slash if pattern is a directory.
+# Replacements starting with http:// return "Permanently moved"
+# message.
+# e.g the following line aliases requests for / to requests for
+# /usr/mig/html
+
+# / /usr/mig/html
+
--- /dev/null
+++ b/services/httpd/httpd.suff
@@ -1,0 +1,110 @@
+#suffix generic type specific type encoding
+.C text plain - # C++ program
+.Z - - x-compress
+.a application octet-stream - # [Mosaic]
+.ada text plain - # ada program
+.ai application postscript - # [Mosaic]
+.aif audio x-aiff -
+.aifc audio x-aiff -
+.aiff audio x-aiff -
+.au audio basic - # sun audio
+.avi video x-msvideo - # [Mosaic]
+.awk text plain - # awk program
+.bas text plain - # basic program
+.bbl text plain - # BibTex output
+.bcpio application x-bcpio - # [Mosaic]
+.bib text plain - # BibTex input
+.c text plain - # C program
+.c++ text plain - # C++ program
+.cc text plain - # [Mosaic]
+.cdf application x-netcdf -
+.cpio application x-cpio -
+.cpp text plain - # DOS C++ program
+.dat text plain - # AMPL et al.
+.diff text plain -
+.dvi application x-dvi - # TeX output
+.enc application octet-stream - # encrypted file
+.eps application postscript -
+.etx text x-setext - # [Mosaic]
+.exe application octet-stream - # DOS executable
+.executable application octet-stream - # DOS executable
+.exz application octet-stream x-gzip # gziped DOS executable
+.f text plain - # fortran-77 program
+.flc video x-flc -
+.fli video x-fli -
+.gif image gif -
+.gtar application x-gtar - # [Mosaic]
+.gz - - x-gzip # gziped file
+.h text plain - # C header file
+.hdf application x-hdf -
+.hqx application octet-stream - # Mac BinHex
+.htm text html -
+.html text html -
+.ief image ief - # [Mosaic]
+.jfif image jpeg - # [Mosaic]
+.jfif-tbnl image jpeg - # [Mosaic]
+.jpe image jpeg - # [Mosaic]
+.jpeg image jpeg -
+.jpg image jpeg -
+.latex application x-latex - # [Mosaic]
+.ltx application x-latex -
+.man application x-troff-man - # [Mosaic]
+.me application x-troff-me - # [Mosaic]
+.mime message rfc822 - # [Mosaic]
+.mod text plain - # AMPL et al.
+.mov video quicktime - # [Mosaic]
+.movie video x-sgi-movie - # [Mosaic]
+.mpe video mpeg - # [Mosaic]
+.mpeg video mpeg -
+.mpg video mpeg -
+.ms application x-troff-ms - # [Mosaic]
+.mv video x-sgi-movie - # [Mosaic]
+.nc application x-netcdf - # [Mosaic]
+.o application octet-stream - # [Mosaic]
+.oda application oda - # [Mosaic]
+.pbm image x-portable-bitmap - # [Mosaic]
+.pdf application pdf - # Adobe Portable Document Format
+.pgm image x-portable-graymap - # [Mosaic]
+.pl text plain - # [Mosaic]
+.pnm image x-portable-anymap - # [Mosaic]
+.ppm image x-portable-pixmap - # [Mosaic]
+.ps application postscript -
+.qt video quicktime - # [Mosaic]
+.r text plain - # ratfor program
+.ras image x-cmu-rast - # [Mosaic]
+.rc text plain - # rc
+.rfr text plain - # refer
+.rgb image x-rgb - # [Mosaic]
+.roff application x-troff - # [Mosaic]
+.rtf application rtf - # [Mosaic]
+.rtx text richtext - # MIME richtext [Mosaic]
+.sh application x-shar -
+.shar application x-shar -
+.snd audio basic -
+.sv4cpio application x-sv4cpio - # [Mosaic]
+.sv4crc application x-sv4crc - # [Mosaic]
+.t application x-troff - # [Mosaic]
+.tar application x-tar - # [Mosaic]
+.taz application x-tar x-compress
+.tcl application x-tcl -
+.tex application x-tex - # Tex input
+.texi application x-texinfo - # [Mosaic]
+.texinfo application x-texinfo - # [Mosaic]
+.text text plain - # [Mosaic]
+.tgz application x-tar x-gzip
+.tif image tiff -
+.tiff image tiff -
+.toc text plain - # table of contents
+.tr application x-troff - # [Mosaic]
+.trz application x-tar x-compress
+.tsv text tab-separated-values - # [Mosaic]
+.txt text plain -
+.ustar application x-ustar - # [Mosaic]
+.wav audio x-wav -
+.wsrc application x-wais-source - # [Mosaic]
+.xbm image x-xbitmap - # X bitmap
+.xpm image x-xpixmap - # [Mosaic]
+.xwd image x-xwindowdump - # [Mosaic]
+.z - - x-compress
+.Z - - x-compress
+.zip application zip -
--- /dev/null
+++ b/services/webget/help.html
@@ -1,0 +1,58 @@
+<HTML>
+<HEAD>
+<TITLE>Browser Help</TITLE>
+</HEAD>
+<BODY BGCOLOR="#90EE90">
+
+<P>
+
+Click on the buttons to travel around during a browsing session.
+<P>
+
+<IMG SRC="file:///icons/charon/redleft.bit"> Goes back to the previous page.
+<P>
+<IMG SRC="file:///icons/charon/redright.bit"> Goes forward one page beyond the page that you've gone back to.
+<P>
+<IMG SRC="file:///icons/charon/circarrow.bit"> Reloads the current page.
+<P>
+<IMG SRC="file:///icons/charon/stop.bit"> Stops loading the current page.
+<P>
+<IMG SRC="file:///icons/charon/home.bit"> Returns to your home page.
+<P>
+The location field displays your current location on the internet. When you are accessing a web site, the location field
+will also include a status bar that tells you what's happening with your web site connection.
+<P>
+The <b>History</b> is a list of web sites that you have visited since you opened the browser.
+<P>
+To view the History touch
+<IMG SRC="file:///icons/charon/history.bit">
+and then touch the desired web page to return to that page.
+<P>
+<b>Bookmarks</b> are saved URLs (web site addresses).
+<P>
+To save a bookmark, access the desired web page and touch
+<IMG SRC="file:///icons/charon/edit.bit"> .
+<P>
+To view saved bookmarks, touch
+<IMG SRC="file:///icons/charon/bookmark.bit"> .
+<P>
+To access a bookmark, touch
+<IMG SRC="file:///icons/charon/bookmark.bit">
+and then touch the desired bookmark to go to that page.
+<P>
+<b>
+User Preferences</b> allow you to make changes in how your web browser performs.
+<P>
+Touch
+<IMG SRC="file:///icons/charon/conf.bit">
+to change the User Preferences.
+<P>
+Check Automatically Load Images if you want images on web sites to be
+loaded. Enter the URL you want for your home page. Touch Ok to save the information.
+<P>
+<IMG SRC="file:///icons/charon/help.bit"> Displays this help file.
+<P>
+Touch a down arrow to scroll down a line in a web site. Touch an up arrow to scroll up in a web site.
+You may also use the PgUp and PgDn keys on your keyboard to scroll a full page at a time. The Home and End keys allow you to jump to the beginning and end of a web page.
+</BODY>
+</HTML>
binary files /dev/null b/services/webget/inferno.gif differ
--- /dev/null
+++ b/services/webget/start.html
@@ -1,0 +1,17 @@
+<head><title>Welcome to Inferno</title></head>
+<body bgcolor=#ff5500 link=#000000>
+<font color=#ffffff>
+<br><br>
+<center>
+<table bgcolor=#000000>
+<tr>
+<td valign=middle><img src="vnlogo.gif">
+<td valign=middle><img src="inferno.gif">
+</table>
+<br><br>
+<h2>brought to you by
+<a href="http://www.vitanuova.com">
+Vita Nuova
+</a></h2>
+</center>
+</body>
binary files /dev/null b/services/webget/vnlogo.gif differ
--- /dev/null
+++ b/tools/NOTICE
@@ -1,0 +1,23 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+Copyright © 2000-2007 Vita Nuova Holdings Limited
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License (`LGPL') as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--- /dev/null
+++ b/tools/db/infdb.c
@@ -1,0 +1,998 @@
+//
+// infdb - NT data base daemon for Inferno
+//
+// Copyright 1997 Lucent Technologies
+//
+// May 1997
+//
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#ifdef WIN32
+#include <winsock.h>
+#include <SQL.h>
+#include <SQLEXT.h>
+#else
+#include <unistd.h>
+#include <sql.h>
+#include <sqlext.h>
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define strnicmp strncasecmp
+#endif
+
+#define MAXCOLS 100
+#define BUFSIZE 8192
+#define REQ_HEADER_SIZE 18
+#define RES_HEADER_SIZE 22
+#define OFFSET_LENGTH 2
+#define OFFSET_STREAM 14
+#define OFFSET_REQ_DATA 18
+#define OFFSET_RETURN 18
+#define OFFSET_RES_DATA 22
+
+#define CONN_ALLOC_FAIL 1
+#define STREAM_ALLOC_FAIL 2
+#define STREAM_BAD_ID 3
+#define LAST_ERROR_NO 4
+
+
+//
+// Deal with one connection. Use stdin and stdout to read and write messages.
+// Each incoming message is answered before reading the next incoming message.
+//
+
+typedef int STATUS;
+#define OK 0
+#define WARN -1
+#define ERR -2
+
+typedef struct {
+ int state;
+#define SQLC_FREE 0
+#define SQLC_INUSE 1
+ int connid;
+ int refcount;
+ UCHAR user[48];
+ UCHAR passwd[48];
+ UCHAR dbname[48];
+ UCHAR errmsg[256];
+ HDBC hdbc;
+} SQLConn;
+
+
+typedef struct {
+ int state;
+#define SQLS_FREE 0
+#define SQLS_INUSE 1
+ int streamid;
+ int connid;
+ HSTMT hstmt;
+ UCHAR errmsg[256];
+ UCHAR colname[MAXCOLS][32];
+ SWORD coltype[MAXCOLS];
+ SWORD colnamelen;
+ SWORD nullable;
+ UDWORD collen[MAXCOLS];
+ SWORD scale;
+ SDWORD outlen[MAXCOLS];
+ UCHAR *data[MAXCOLS];
+ SWORD nresultcols;
+ SDWORD rowcount;
+ SWORD rownum;
+ RETCODE rc;
+ UCHAR *setdata[MAXCOLS];
+ SDWORD setdatalen[MAXCOLS];
+} SQLStream;
+
+
+typedef struct {
+ HENV henv;
+ int maxconn;
+ int numconn;
+ SQLConn **scarray;
+ int maxstream;
+ int numstream;
+ SQLStream **ssarray;
+} SQLEnv;
+
+
+typedef struct {
+ char mtype;
+ char version;
+ int nbytes;
+ int sstream;
+ int retcode;
+ int bytesNotRead;
+ char *data;
+} DBMSG, *DBMSGP;
+
+
+int getCommand (DBMSGP msgp, UCHAR *buf, int bufsiz);
+void sendResponse (char type, int lendata, int sstream, int retcode, char *data);
+void sendError (char *errmsg, int sstream);
+void print_err (SQLEnv *sqle, int connid, int streamid, UCHAR * buf, int bufsiz);
+UDWORD display_size (SWORD coltype, UDWORD collen, UCHAR *colname);
+
+STATUS newSqlEnv (SQLEnv **sqle);
+STATUS freeSqlEnv (SQLEnv **sqle);
+
+STATUS newSqlConn (SQLEnv *sqle, char *info, int *connid);
+STATUS mapSqlConn (SQLEnv *sqle, int connid, SQLConn **sqlc);
+STATUS freeSqlConn (SQLEnv *sqle, int connid);
+
+STATUS newSqlStream (SQLEnv *sqle, int connid, int *streamid);
+STATUS mapSqlStream (SQLEnv *sqle, int streamid, SQLStream **sqls);
+STATUS freeSqlStream (SQLEnv *sqle, int streamid);
+
+STATUS parseConnInfo (SQLConn *sqlc, char *info);
+
+char *iError[] = {
+ "INFDB: DB connection allocation failed",
+ "INFDB: couldn't allocate SQL stream",
+ "INFDB: bad SQL stream identifier"
+};
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ int notdone = 1;
+ int infErrno;
+ DBMSG msg;
+ SQLEnv *sqle = NULL;
+ SQLStream *sqls;
+ char buf[BUFSIZE];
+ char outbuf[BUFSIZE];
+ char errmsg[256];
+ STATUS rc;
+
+ // We just have to talk to stdin and stdout. However, stdout may be open
+ // in text mode, which is bad for data. Set it to binary mode.
+
+#ifdef WIN32
+ _setmode(0, _O_BINARY);
+ _setmode(1, _O_BINARY);
+#endif
+
+ rc = newSqlEnv(&sqle);
+ if ( rc != OK ) {
+ sendError("INFDB: Failed to allocate SQL environment.", -1);
+ return -1;
+ }
+
+ while ( notdone ) {
+ int bytesRead;
+
+ bytesRead = 0;
+ if ( (bytesRead = getCommand(&msg, buf, sizeof(buf))) <= 0 ) {
+ continue;
+ }
+ msg.retcode = 0;
+ infErrno = 0;
+
+ switch ( msg.mtype ) {
+ // Initiate a new connection.
+ case 'I':
+ {
+ int connid;
+
+ rc = newSqlConn(sqle, msg.data, &connid);
+ if ( rc != OK ) {
+ infErrno = CONN_ALLOC_FAIL;
+ break;
+ }
+ /*
+ // Need a new SQLStream to make subsequent requests.
+ rc = newSqlStream(sqle, connid, &streamid);
+ if ( rc != OK ) {
+ infErrno = STREAM_ALLOC_FAIL;
+ break;
+ }
+
+ sprintf(outbuf, "%d", streamid);
+ */
+ sprintf(outbuf, "%d", connid);
+ sendResponse('i', strlen(outbuf), msg.sstream, 0, outbuf);
+ break;
+ }
+
+
+ case 'O':
+ {
+ // open an SQL stream.
+ int connid, streamid;
+
+ connid = atoi(msg.data);
+ rc = newSqlStream(sqle, connid, &streamid);
+ if (rc != OK) {
+ infErrno = STREAM_ALLOC_FAIL;
+ break;
+ }
+
+ sprintf(outbuf, "%d", streamid);
+ sendResponse('o', strlen(outbuf), msg.sstream, 0, outbuf);
+ break;
+ }
+
+
+ case 'K':
+ // klose an SQL stream
+ rc = freeSqlStream(sqle, msg.sstream);
+ sendResponse('k', 0, msg.sstream, 0, "");
+ break;
+
+
+ case 'C':
+ // request number of columns
+ rc = mapSqlStream(sqle, msg.sstream, &sqls);
+ if ( rc != OK ) {
+ infErrno = STREAM_BAD_ID;
+ break;
+ }
+
+ sprintf(outbuf, "%d", sqls->nresultcols);
+ sendResponse('c', strlen(outbuf), msg.sstream, 0, outbuf);
+ break;
+
+
+ case 'N':
+ // fetch next row
+ rc = mapSqlStream(sqle, msg.sstream, &sqls);
+ if ( rc != OK ) {
+ infErrno = STREAM_BAD_ID;
+ break;
+ }
+
+ sqls->errmsg[0] = '\0';
+ rc = SQLFetch(sqls->hstmt);
+ if ( rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO ) {
+ sqls->rownum++;
+ // if ( rc == SQL_SUCCESS_WITH_INFO ) {
+ // fprintf(stderr, "SQLFetch got SQL_SUCCESS_WITH_INFO\n");
+ // }
+ /* Get the data for all columns */
+ for ( i = 0; i < sqls->nresultcols; i++ ) {
+ rc = SQLGetData(sqls->hstmt, (UWORD)(i+1),
+ (sqls->coltype[i] == SQL_LONGVARBINARY ||
+ sqls->coltype[i] == SQL_LONGVARCHAR) ? SQL_C_DEFAULT :
+ SQL_C_CHAR,
+ sqls->data[i], sqls->collen[i], &sqls->outlen[i]);
+ if ( rc == SQL_SUCCESS_WITH_INFO &&
+ (UDWORD) sqls->outlen[i] > sqls->collen[i] ) {
+ UCHAR *tmp;
+
+ tmp = (UCHAR *) realloc(sqls->data[i], sqls->outlen[i]+1);
+ if ( tmp != NULL ) {
+ SDWORD dummy;
+ sqls->data[i] = tmp;
+ rc = SQLGetData(sqls->hstmt, (UWORD)(i+1), SQL_C_DEFAULT,
+ &tmp[sqls->collen[i]], sqls->outlen[i], &dummy);
+ sqls->collen[i] = sqls->outlen[i];
+ sqls->data[i][sqls->outlen[i]] = 0;
+ }
+ }
+ else if ( rc != SQL_SUCCESS ) {
+ sprintf(sqls->errmsg, "Problem retrieving data from data base, col %d", i+1);
+ msg.retcode = 2;
+ }
+ }
+ }
+ else if ( rc == SQL_NO_DATA_FOUND ) {
+ sqls->rownum = 0;
+ msg.retcode = 1;
+ }
+ else {
+ sqls->rownum = -1;
+ // Probably should get some status from ODBC for message
+ sprintf(sqls->errmsg, "Error occurred in fetching data");
+ }
+ if ( sqls->rownum < 0 ) {
+ sendError(errmsg, msg.sstream);
+ }
+ else {
+ sprintf(outbuf, "%d", sqls->rownum);
+ // rownum should be <= rowcount
+ sendResponse('n', strlen(outbuf), msg.sstream, msg.retcode, outbuf);
+ }
+ break;
+
+
+ case 'H':
+ // request an error message, if any
+ rc = mapSqlStream(sqle, msg.sstream, &sqls);
+ if ( rc != OK ) {
+ infErrno = STREAM_BAD_ID;
+ break;
+ }
+
+ sendError(sqls->errmsg, msg.sstream);
+ sqls->errmsg[0] = 0;
+ break;
+
+
+ case 'P':
+ // request write data
+ // in Inferno, param nums start at 0; in ODBC/SQL, they start at 1
+ // This leaves outdatalen[0] unused, hence available as final SQLBindParameter arg.
+ rc = mapSqlStream(sqle, msg.sstream, &sqls);
+ if ( rc != OK ) {
+ infErrno = STREAM_BAD_ID;
+ break;
+ }
+
+ sqls->errmsg[0] = 0;
+ if ( (i = atoi(msg.data) + 1) < 1 || i >= MAXCOLS ) {
+ sendError("Illegal param number", msg.sstream);
+ }
+ else {
+ int len;
+ char *p = msg.data + 4; // data points to param number
+
+ len = msg.nbytes - 4; // number of data chars
+ if ( len < 0 ) {
+ sendError("Write phase error II", msg.sstream);
+ break;
+ }
+ if ( sqls->setdata[i] != NULL ) {
+ free(sqls->setdata[i]);
+ }
+ sqls->setdata[i] = (char *) malloc(len + 1);
+ if ( sqls->setdata[i] == NULL ) {
+ sendError("Allocation error in server", msg.sstream);
+ break;
+ }
+ // Copy data we have into buffer, and if we don't have it all yet,
+ // try to get the rest.
+ sqls->setdatalen[i] = len++; // adjust len for trailing \n
+ bytesRead = &buf[bytesRead] - p; // number data bytes we have read
+ memcpy(sqls->setdata[i], p, bytesRead);
+ len -= bytesRead; // number bytes still to read,
+ while ( len > 0 ) {
+ int n;
+
+ if ( (n = read(0, sqls->setdata[i] + bytesRead, len)) <= 0 ) {
+ break;
+ }
+ bytesRead += n;
+ len -= n;
+ }
+ if ( len > 0 ) {
+ sendError("Couldn't read all of parameter", msg.sstream);
+ break;
+ }
+ rc = SQLBindParameter(sqls->hstmt, (UWORD)i, SQL_PARAM_INPUT,
+ SQL_C_BINARY, SQL_LONGVARBINARY,
+ sqls->setdatalen[i], 0, (PTR) i, 0, sqls->setdatalen);
+ if ( rc != SQL_SUCCESS ) {
+ sendError("BindParameter failed: maybe not supported", msg.sstream);
+ break;
+ }
+ sqls->setdatalen[0] = SQL_LEN_DATA_AT_EXEC(0);
+ sprintf(outbuf, "%d", bytesRead - 1);
+ sendResponse('p', strlen(outbuf), msg.sstream, 0, outbuf);
+ }
+ break;
+
+
+ case 'R':
+ // request read data
+ rc = mapSqlStream(sqle, msg.sstream, &sqls);
+ if ( rc != OK ) {
+ infErrno = STREAM_BAD_ID;
+ break;
+ }
+
+ if ( (i = atoi(msg.data)) < 0 || i >= sqls->nresultcols || sqls->rownum <= 0 ) {
+ sendError(sqls->rownum <= 0 ? "No current row" : "Illegal column number", msg.sstream);
+ }
+ else if ( sqls->outlen[i] == SQL_NULL_DATA || sqls->outlen[i] == SQL_NO_TOTAL ) {
+ sendResponse('r', 0, msg.sstream, 0, "");
+ }
+ else {
+ if ( sqls->coltype[i] == SQL_VARCHAR ) {
+ sqls->outlen[i] = strlen(sqls->data[i]);
+ }
+ sendResponse('r', sqls->outlen[i], msg.sstream, 0, sqls->data[i]);
+ }
+ break;
+
+
+ case 'T':
+ // request column title
+ rc = mapSqlStream(sqle, msg.sstream, &sqls);
+ if ( rc != OK ) {
+ infErrno = STREAM_BAD_ID;
+ break;
+ }
+
+ if ( (i = atoi(msg.data)) < 0 || i >= sqls->nresultcols ) {
+ sendError("Illegal column number", msg.sstream);
+ }
+ else {
+ sendResponse('t', strlen(sqls->colname[i]), msg.sstream, 0, sqls->colname[i]);
+ }
+ break;
+
+
+ case 'W':
+ // execute command
+ rc = mapSqlStream(sqle, msg.sstream, &sqls);
+ if ( rc != OK ) {
+ infErrno = STREAM_BAD_ID;
+ break;
+ }
+
+ if ( sqls->hstmt ) {
+ SQLFreeStmt(sqls->hstmt, SQL_CLOSE);
+ SQLFreeStmt(sqls->hstmt, SQL_UNBIND);
+ }
+ // Look for special extensions
+ if ( strnicmp(msg.data, "commit", 6) == 0 ) {
+ SQLConn *sqlc;
+
+ rc = mapSqlConn(sqle, sqls->connid, &sqlc);
+ rc = SQLTransact(SQL_NULL_HENV, sqlc->hdbc, SQL_COMMIT);
+ }
+ else if ( strnicmp(msg.data, "rollback", 8) == 0 ) {
+ SQLConn *sqlc;
+
+ rc = mapSqlConn(sqle, sqls->connid, &sqlc);
+ rc = SQLTransact(SQL_NULL_HENV, sqlc->hdbc, SQL_ROLLBACK);
+ }
+
+ else if ( strnicmp(msg.data, "tables", 6) == 0 ) {
+ rc = SQLTables(sqls->hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
+ }
+ else if ( strnicmp(msg.data, "columns", 7) == 0 ) {
+ UCHAR *tbl;
+
+ for ( tbl = msg.data+8; *tbl == ' ' || *tbl == '\t'; tbl++ ) { }
+
+ rc = SQLColumns(sqls->hstmt, NULL, 0, NULL, 0, tbl, SQL_NTS, NULL, 0);
+ }
+ else {
+ rc = SQLExecDirect(sqls->hstmt, msg.data, SQL_NTS);
+ }
+ outbuf[0] = '\0';
+ while ( rc == SQL_NEED_DATA ) {
+ PTR pToken;
+// SDWORD pnum;
+
+ rc = SQLParamData(sqls->hstmt, &pToken);
+// pnum = (SDWORD) pToken;
+#define pnum (int)pToken
+ if ( rc == SQL_NEED_DATA ) {
+ int retcode;
+
+ if ( sqls->setdata[pnum] == NULL || sqls->setdatalen[pnum] <= 0 ) {
+ sprintf(outbuf, "Parameter %d not set\n", pnum);
+ break;
+ }
+ for ( i = 0; i < sqls->setdatalen[pnum]; ) {
+ int togo;
+
+ togo = 1024;
+ if ( sqls->setdatalen[pnum] - i < 1024 ) {
+ togo = sqls->setdatalen[pnum] - i;
+ }
+ retcode = SQLPutData(sqls->hstmt,
+ sqls->setdata[pnum] + i, togo);
+ i += togo;
+ if ( retcode != SQL_SUCCESS ) {
+ print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf));
+ break;
+ }
+ }
+ if ( retcode != SQL_SUCCESS /* && retcode != SQL_SUCCESS_WITH_INFO */) {
+ break;
+ }
+ }
+ }
+ if ( rc != SQL_SUCCESS ) {
+ strcat(outbuf, "Command execution failed\n");
+ switch ( rc ) {
+ case SQL_SUCCESS_WITH_INFO:
+ strcat(outbuf, ": SQL_SUCCESS_WITH_INFO");
+ print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf));
+ break;
+ case SQL_ERROR:
+ strcat(outbuf, ": SQL_ERROR");
+ print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf));
+ break;
+ case SQL_NEED_DATA:
+ strcat(outbuf, ": SQL_NEED_DATA");
+ break;
+ case SQL_STILL_EXECUTING:
+ strcat(outbuf, ": SQL_STILL_EXECUTING");
+ break;
+ case SQL_INVALID_HANDLE:
+ strcat(outbuf, ": SQL_INVALID_HANDLE");
+ break;
+ }
+ sendError(outbuf, msg.sstream);
+ break;
+ }
+ SQLNumResultCols(sqls->hstmt, &sqls->nresultcols);
+ if ( sqls->nresultcols == 0 ) { // was not 'select' command
+ SQLRowCount(sqls->hstmt, &sqls->rowcount); // we don't use this, do we?
+ }
+ else { // get the column labels, save for later
+ for ( i = 0; i < sqls->nresultcols; i++ ) {
+ int newlen;
+
+ SQLDescribeCol(sqls->hstmt, (UWORD) (i+1), sqls->colname[i],
+ (SWORD)sizeof(sqls->colname[i]),
+ &sqls->colnamelen, &sqls->coltype[i], &sqls->collen[i],
+ &sqls->scale, &sqls->nullable);
+ sqls->colname[i][sqls->colnamelen] = 0;
+ // Adjust the length, since we are converting everything to strings.
+ if ( (newlen = display_size(sqls->coltype[i], sqls->collen[i],
+ sqls->colname[i])) != 0 ) {
+ sqls->collen[i] = newlen;
+ }
+ if ( sqls->collen[i] == 0 ) {
+ sqls->collen[i] = BUFSIZE;
+ }
+ sqls->data[i] = (UCHAR *) malloc(sqls->collen[i] + 1);
+ /*
+ SQLBindCol(sqls->hstmt, (UWORD) (i+1), newlen > 0 ? SQL_C_CHAR : SQL_C_DEFAULT,
+ sqls->data[i], sqls->collen[i], &sqls->outlen[i]);
+ */
+ }
+ sqls->rownum = 0;
+ }
+ sendResponse('w', 0, msg.sstream, 0, "");
+ break;
+
+
+ case 'X':
+ notdone = 0;
+ break;
+
+
+ default:
+ sprintf(sqls->errmsg, "Unknown command: %c", msg.mtype);
+ sendError(sqls->errmsg, msg.sstream);
+ sqls->errmsg[0] = '\0';
+ break;
+ } // end of switch (msg.mtype)
+ if ( infErrno > 0 && infErrno < LAST_ERROR_NO ) {
+ sendError(iError[infErrno - 1], msg.sstream);
+ }
+ } // end of while (notdone)
+ rc = freeSqlEnv(&sqle);
+
+ return 0;
+}
+
+//
+// All the incoming commands should end with a newline character.
+// We read until we get one. Then we verify that we have read as
+// many bytes as the count in message says we should.
+//
+int
+getCommand(DBMSGP msgp, UCHAR *buf, int bufsiz)
+{
+ int bytesRead = 0;
+ int rc = 0;
+
+ msgp->mtype = '\0';
+ while ( bufsiz > 0 && (rc = read(0, &buf[bytesRead], bufsiz)) > 0 ) {
+ bytesRead += rc;
+ bufsiz -= rc;
+ msgp->bytesNotRead -= rc;
+ if ( msgp->mtype == '\0' && bytesRead >= REQ_HEADER_SIZE ) {
+ if ( (msgp->version = buf[1]) != '1' ) { // wrong version, give up
+ char *wrong_version = "Message has wrong version number";
+ sendResponse('h', strlen(wrong_version), 0, 0, wrong_version);
+ return -1;
+ }
+ msgp->mtype = buf[0];
+ msgp->nbytes = atoi(buf+OFFSET_LENGTH);
+ msgp->sstream = atoi(buf+OFFSET_STREAM);
+ msgp->data = buf+OFFSET_REQ_DATA;
+ msgp->bytesNotRead = REQ_HEADER_SIZE + msgp->nbytes + 1 - bytesRead;
+ if ( bufsiz > msgp->bytesNotRead ) {
+ bufsiz = msgp->bytesNotRead;
+ }
+ }
+ }
+ if ( rc < 0 ) {
+ // log a problem
+ // fprintf(stderr, "Problem reading from client\n");
+ return rc;
+ }
+ if ( msgp->bytesNotRead == 0 ) {
+ msgp->data[msgp->nbytes] = 0; // discard final newline
+ }
+ return bytesRead;
+}
+
+
+void
+sendResponse(char type, int lendata, int sstream, int retcode, char *data)
+{
+ char hdr[RES_HEADER_SIZE+2];
+
+ sprintf(hdr, "%c1%11d %3d %3d ", type, lendata, sstream, retcode);
+ write(1, hdr, RES_HEADER_SIZE);
+ write(1, data, lendata);
+ write(1, "\n", 1);
+}
+
+
+void
+sendError(char *errmsg, int sstream)
+{
+ sendResponse('h', strlen(errmsg), sstream, 0, errmsg);
+}
+
+
+void
+print_err(SQLEnv *sqle, int connid, int streamid, UCHAR * buf, int bufsiz)
+{
+ RETCODE rc;
+ UCHAR stateString[40];
+ SDWORD native;
+ SWORD msglen;
+ SQLConn *sqlc;
+ SQLStream *sqls;
+ HENV *henv;
+ HDBC *hdbc;
+ HSTMT *hstmt;
+
+ henv = sqle->henv;
+
+ rc = mapSqlConn(sqle, connid, &sqlc);
+ hdbc = rc == OK ? sqlc->hdbc : SQL_NULL_HDBC;
+
+ rc = mapSqlStream(sqle, streamid, &sqls);
+ hstmt = rc == OK ? sqls->hstmt : SQL_NULL_HSTMT;
+
+ rc = SQLError(henv, hdbc, hstmt, stateString, &native, buf, (SWORD) bufsiz, &msglen);
+}
+
+
+#define MAX_NUM_PRECISION 15
+
+/* Define max length of char string representation of number as: */
+/* = max(precision) + leading sign + E + exp sign + max exp length */
+/* = 15 + 1 + 1 + 1 + 2 */
+/* = 15 + 5 */
+
+#define MAX_NUM_STRING_SIZE (MAX_NUM_PRECISION + 5)
+
+UDWORD
+display_size(SWORD coltype, UDWORD collen, UCHAR *colname)
+{
+switch (coltype) {
+
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ return max(collen, strlen(colname));
+
+ case SQL_SMALLINT:
+ case SQL_TINYINT:
+ case SQL_BIT:
+ return max(6, strlen(colname));
+
+ case SQL_INTEGER:
+ return max(11, strlen(colname));
+
+ case SQL_BIGINT:
+ return max(30, strlen(colname));
+
+ case SQL_DATE:
+ case SQL_TIME:
+ case SQL_TIMESTAMP:
+ return max(50, strlen(colname));
+
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_REAL:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ return(max(MAX_NUM_STRING_SIZE, strlen(colname)));
+
+ case SQL_LONGVARBINARY:
+ case SQL_LONGVARCHAR:
+ return BUFSIZE;
+
+ /* Note that this function only supports the core data types. */
+ /* For unknown data types, the caller should assume binary data */
+ default:
+ /* fprintf(stderr, "Unknown datatype, %d\n", coltype); */
+ return 0;
+ }
+}
+
+
+STATUS
+newSqlEnv(SQLEnv **sqle)
+{
+ SQLEnv *newenv;
+ STATUS rc;
+
+ newenv = (SQLEnv *) calloc(1, sizeof(SQLEnv));
+ if (newenv == NULL) {
+ return ERR;
+ }
+
+ rc = SQLAllocEnv(&newenv->henv);
+ if ( rc != SQL_SUCCESS) {
+ free (newenv);
+ return ERR;
+ }
+
+ *sqle = newenv;
+ return OK;
+}
+
+
+STATUS
+freeSqlEnv(SQLEnv **sqle)
+{
+ int i;
+ STATUS rc;
+
+ for (i = 0; i < (*sqle)->maxstream; i++) {
+ // Free this stream.
+ // Connection will be freed automatically.
+ rc = freeSqlStream(*sqle, i);
+ }
+ // dealloc the stream structures
+ // dealloc the connect structures
+ SQLFreeEnv((*sqle)->henv);
+ // dealloc the env structure
+
+ return OK;
+}
+
+
+STATUS
+mapSqlConn(SQLEnv *sqle, int connid, SQLConn **sqlc)
+{
+ if ( connid >= 0 && connid < sqle->maxconn ) {
+ *sqlc = sqle->scarray[connid];
+ if ( (*sqlc)->state == SQLC_INUSE )
+ return OK;
+ }
+ return ERR;
+}
+
+
+STATUS
+newSqlConn(SQLEnv *sqle, char *info, int *connid)
+{
+ SQLConn **newarray, *sqlc;
+ int newid = -1, i;
+ STATUS rc;
+
+ *connid = -1;
+
+ // Connect to the database.
+ // Search for an available connection structure to reuse
+ for ( i = 0; i < sqle->maxconn; i++ ) {
+ sqlc = sqle->scarray[i];
+ if ( sqlc != NULL && sqlc->state == SQLC_FREE ) {
+ newid = i;
+ break;
+ }
+ }
+
+ if ( newid == -1 ) {
+ // Assign a new connection id
+ newid = sqle->maxconn++;
+
+ // Extend the connection pointer array
+ newarray = (SQLConn **) realloc((char *) sqle->scarray,
+ sqle->maxconn * sizeof(SQLConn*));
+ if ( newarray == NULL ) {
+ return ERR;
+ }
+ sqle->scarray = newarray;
+
+ // Allocate a new connection structure
+ sqlc = (SQLConn *) calloc(1, sizeof(SQLConn));
+ if ( sqlc == NULL ) {
+ return ERR;
+ }
+ sqle->scarray[newid] = sqlc;
+ }
+
+ // Ask ODBC for a new connection handle
+ rc = SQLAllocConnect(sqle->henv, &sqlc->hdbc);
+ if (rc == SQL_ERROR) {
+ return ERR;
+ }
+
+ sqlc->refcount = 0;
+ sqlc->state = SQLC_INUSE;
+
+ // Extract the username, password, and database name
+ rc = parseConnInfo(sqlc, info);
+ if ( rc != OK ) {
+ return ERR;
+ }
+
+ // Request an ODBC connection to the database
+ rc = SQLConnect(sqlc->hdbc, sqlc->dbname, SQL_NTS, sqlc->user, SQL_NTS,
+ sqlc->passwd, SQL_NTS);
+ if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) {
+ // log error?
+ // Should try to get something more specific from ODBC
+ sprintf(sqlc->errmsg, "Connect failed: user = %s, passwd = %s, dbname = %s",
+ sqlc->user, sqlc->passwd, sqlc->dbname);
+
+ SQLDisconnect(sqlc->hdbc);
+ SQLFreeConnect(sqlc->hdbc);
+ return ERR;
+ }
+ *connid = newid;
+
+ // Set connect option to disable auto commit
+ rc = SQLSetConnectOption(sqlc->hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
+ if ( rc != SQL_SUCCESS ) {
+ return WARN;
+ }
+
+ return OK;
+}
+
+
+STATUS
+freeSqlConn(SQLEnv *sqle, int connid)
+{
+ SQLConn *sqlc;
+ STATUS rc;
+
+ rc = mapSqlConn(sqle, connid, &sqlc);
+ if ( rc != OK ) {
+ return WARN;
+ }
+
+ SQLDisconnect(sqlc->hdbc);
+ SQLFreeConnect(sqlc->hdbc);
+ sqlc->state = SQLC_FREE;
+ return OK;
+}
+
+
+STATUS
+mapSqlStream(SQLEnv *sqle, int streamid, SQLStream **sqls)
+{
+ if ( streamid >= 0 && streamid < sqle->maxstream ) {
+ *sqls = sqle->ssarray[streamid];
+ if ( (*sqls)->state == SQLS_INUSE )
+ return OK;
+ }
+ return ERR;
+}
+
+
+STATUS
+newSqlStream(SQLEnv *sqle, int connid, int *streamid)
+{
+ HSTMT hstmt;
+ SQLConn *sqlc;
+ SQLStream **newarray, *sqls;
+ int newid = -1, i;
+ STATUS rc;
+
+ rc = mapSqlConn(sqle, connid, &sqlc);
+ if (rc != OK) {
+ return ERR;
+ }
+
+ // Search for an available stream structure to reuse
+ for ( i = 0; i < sqle->maxstream; i++ ) {
+ sqls = sqle->ssarray[i];
+ if ( sqls != NULL && sqls->state == SQLS_FREE ) {
+ newid = i;
+ break;
+ }
+ }
+
+ if ( newid == -1 ) {
+ // Assign a new stream id
+ newid = sqle->maxstream++;
+
+ // Extend the stream pointer array
+ newarray = (SQLStream **) realloc((char *) sqle->ssarray,
+ sqle->maxstream * sizeof(SQLStream*));
+ if ( newarray == NULL ) {
+ return ERR;
+ }
+ sqle->ssarray = newarray;
+
+ // Allocate a new stream structure
+ sqls = (SQLStream *) calloc(1, sizeof(SQLStream));
+ if ( sqls == NULL ) {
+ return ERR;
+ }
+ sqle->ssarray[newid] = sqls;
+ }
+
+ // Associate new stream with specified connection
+ sqls->connid = connid;
+ sqlc->refcount++;
+
+ // Ask ODBC to allocate a new statement handle
+ rc = SQLAllocStmt(sqlc->hdbc, &hstmt);
+ if (rc == SQL_ERROR) {
+ return ERR;
+ }
+ sqls->hstmt = hstmt;
+ sqls->state = SQLS_INUSE;
+
+ *streamid = newid;
+ return OK;
+}
+
+
+STATUS
+freeSqlStream(SQLEnv *sqle, int streamid)
+{
+ SQLConn *sqlc;
+ SQLStream *sqls;
+ STATUS rc;
+
+ rc = mapSqlStream(sqle, streamid, &sqls);
+ if ( rc != OK ) {
+ return WARN;
+ }
+
+ sqls->state = SQLS_FREE;
+
+ rc = SQLFreeStmt(sqls->hstmt, SQL_DROP);
+
+ rc = mapSqlConn(sqle, sqls->connid, &sqlc);
+ if ( rc != OK ) {
+ return WARN;
+ }
+
+ if ( --sqlc->refcount == 0 )
+ {
+ rc = freeSqlConn(sqle, sqls->connid);
+ if (rc != OK) {
+ return WARN;
+ }
+ }
+ return OK;
+}
+
+
+STATUS
+parseConnInfo(SQLConn *sqlc, char *info)
+{
+ UCHAR *temp;
+
+ // The argument 'info' points to a buffer containing a string
+ // of the form "username/password/dbname\n". We will use 'strtok'
+ // to tokenize the string into the parts we need, keeping
+ // copies in the 'sqlc' structure.
+
+ temp = strtok(info, "/\n");
+ if ( temp == NULL ) {
+ return ERR;
+ }
+ strncpy(sqlc->user, temp, 48);
+
+ temp = strtok(NULL, "/\n");
+ if ( temp == NULL ) {
+ return ERR;
+ }
+ strncpy(sqlc->passwd, temp, 48);
+
+ temp = strtok(NULL, "/\n");
+ if ( temp == NULL ) {
+ return ERR;
+ }
+ strncpy(sqlc->dbname, temp, 48);
+
+ return OK;
+}
+
+
+
--- /dev/null
+++ b/tools/db/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=infdb
+
+OFILES= infdb.$O
+
+LIBS= 9\
+
+BIN=$ROOT/$OBJDIR/bin
+
+SYSLIBS= -liodbc
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/tools/libstyx/Nt.c
@@ -1,0 +1,172 @@
+#include <windows.h>
+#include <lib9.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Fdset Fdset;
+
+struct Fdset
+{
+ fd_set infds, outfds, excfds, r_infds, r_outfds, r_excfds;
+};
+
+int
+styxinitsocket(void)
+{
+ WSADATA wsaData;
+ WORD wVersionRequired=MAKEWORD(1,1);
+
+ int rv = WSAStartup(wVersionRequired, &wsaData);
+
+ if(rv != 0){
+ fprint(2, "Unable to Find winsock.dll");
+ return -1;
+ }
+ if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1 ){
+ fprint(2, "Unable to find winsock.dll V1.1 or later");
+ return -1;
+ }
+ return 0;
+}
+
+void
+styxendsocket(void)
+{
+ WSACleanup( );
+}
+
+void
+styxclosesocket(int fd)
+{
+ closesocket(fd);
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+ struct sockaddr_in sin;
+ int s, one;
+
+ USED(server);
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if(s < 0)
+ return s;
+ one = 1;
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0)
+ fprint(2, "setsockopt failed\n");
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = 0;
+ sin.sin_port = htons(atoi(port));
+ if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
+ close(s);
+ return -1;
+ }
+ if(listen(s, 20) < 0){
+ close(s);
+ return -1;
+ }
+ return s;
+}
+
+int
+styxaccept(Styxserver *server)
+{
+ struct sockaddr_in sin;
+ int len, s;
+
+ len = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ s = accept(server->connfd, (struct sockaddr *)&sin, &len);
+ if(s < 0){
+ if(errno != EINTR)
+ fprint(2, "error in accept: %s\n", strerror(errno));
+ }
+ return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+ Fdset *fs;
+
+ server->priv = fs = malloc(sizeof(Fdset));
+ FD_ZERO(&fs->infds);
+ FD_ZERO(&fs->outfds);
+ FD_ZERO(&fs->excfds);
+ FD_SET(server->connfd, &fs->infds);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ return FD_ISSET(server->connfd, &fs->r_infds);
+}
+
+void
+styxnewclient(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ FD_SET(s, &fs->infds);
+}
+
+void
+styxfreeclient(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ FD_CLR(s, &fs->infds);
+}
+
+int
+styxnewmsg(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ return FD_ISSET(s, &fs->r_infds) || FD_ISSET(s, &fs->r_excfds);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+ struct timeval seltime;
+ int nfds;
+ Fdset *fs;
+
+ fs = server->priv;
+ fs->r_infds = fs->infds;
+ fs->r_outfds = fs->outfds;
+ fs->r_excfds = fs->excfds;
+ seltime.tv_sec = 10;
+ seltime.tv_usec = 0L;
+ nfds = select(sizeof(fd_set)*8, &fs->r_infds, &fs->r_outfds, &fs->r_excfds, &seltime);
+ if(nfds < 0 && errno != EINTR)
+ return"error in select";
+ return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ return recv(fd, buf, n, m);
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ return send(fd, buf, n, m);
+}
+
+void
+styxexit(int n)
+{
+ exit(n);
+}
--- /dev/null
+++ b/tools/libstyx/Plan9.c
@@ -1,0 +1,314 @@
+#include <lib9.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Listener Listener;
+typedef struct Reader Reader;
+typedef struct Union Union;
+
+struct Listener
+{
+ int fd;
+ char *dir;
+ Listener *next;
+};
+
+struct Reader
+{
+ int pid;
+ int fd;
+ int n;
+ char buf[MSGMAX];
+ char rbuf[MSGMAX];
+ Reader *next;
+};
+
+struct Union
+{
+ int pid;
+ Lock lock;
+ Listener *lr;
+ Reader *rr;
+};
+
+void xlock(Lock *l){ lock(l); }
+void xunlock(Lock *l){ unlock(l); }
+
+static Reader*
+findr(Styxserver *server, int fd)
+{
+ Reader *r;
+ Union *u;
+
+ u = server->priv;
+ xlock(&u->lock);
+ for(r = u->rr; r != nil; r = r->next)
+ if(r->fd == fd)
+ break;
+ xunlock(&u->lock);
+ return r;
+}
+
+int
+styxinitsocket(void)
+{
+ return 0;
+}
+
+void
+styxendsocket(void)
+{
+}
+
+void
+styxclosesocket(int fd)
+{
+ close(fd);
+}
+
+static void
+listener(Styxserver *server, int afd, char *adir)
+{
+ int s;
+ Listener *l;
+ Union *u;
+ char ld[40];
+
+ USED(afd);
+ u = server->priv;
+ for(;;){
+ s = listen(adir, ld);
+ /* fprint(2, "listen %d %s %s\n", s, adir, ld); */
+ if(s < 0){
+ u->pid = -1;
+ break;
+ }
+ l = malloc(sizeof(Listener));
+ l->fd = s;
+ l->dir = strdup(ld);
+ xlock(&u->lock);
+ l->next = u->lr;
+ u->lr = l;
+ xunlock(&u->lock);
+ }
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+ int s, pid;
+ Union *u;
+ char adr[32], adir[40];
+
+ server->priv = u = malloc(sizeof(Union));
+ u->lock.val = 0;
+ u->lr = nil;
+ u->rr = nil;
+ sprint(adr, "tcp!*!%s", port);
+ s = announce(adr, adir);
+ /* fprint(2, "announce %d %s %s\n", s, adr, adir); */
+ if(s < 0)
+ return s;
+ switch((pid = rfork(RFPROC|RFREND|RFMEM))){
+ case 0:
+ listener(server, s, strdup(adir));
+ break;
+ default:
+ u->pid = pid;
+ break;
+ }
+ return s;
+}
+
+static void
+reader(Styxserver *server, Reader *r)
+{
+ int m, n, s;
+ Union *u;
+
+ u = server->priv;
+ s = r->fd;
+ for(;;){
+ n = r->n;
+ if(n < 0){
+ r->pid = -1;
+ exits(nil);
+ }
+ m = read(s, r->rbuf, MSGMAX-n);
+ xlock(&u->lock);
+ n = r->n;
+ if(m < 0)
+ r->n = n == 0 ? m : n;
+ else{
+ memmove(r->buf+n, r->rbuf, m);
+ r->n = m+n;
+ }
+ xunlock(&u->lock);
+ }
+}
+
+int
+styxaccept(Styxserver *server)
+{
+ int s, fd, pid;
+ Reader *r;
+ Listener *l;
+ Union *u;
+ char *dir;
+
+ u = server->priv;
+ xlock(&u->lock);
+ if((l = u->lr) == nil){
+ xunlock(&u->lock);
+ return -1;
+ }
+ u->lr = l->next;
+ xunlock(&u->lock);
+ fd = l->fd;
+ dir = l->dir;
+ free(l);
+ s = accept(fd, dir);
+ /* fprint(2, "accept %d\n", s); */
+ free(dir);
+ if(s < 0)
+ return s;
+ r = malloc(sizeof(struct Reader));
+ r->fd = s;
+ r->n = 0;
+ xlock(&u->lock);
+ r->next = u->rr;
+ u->rr = r;
+ xunlock(&u->lock);
+ switch((pid = rfork(RFPROC|RFREND|RFMEM))){
+ case 0:
+ reader(server, r);
+ break;
+ case 1:
+ r->pid = pid;
+ break;
+ }
+ return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+ USED(server);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+ Union *u;
+
+ u = server->priv;
+ return u->lr != nil;
+}
+
+int
+styxnewmsg(Styxserver *server, int fd)
+{
+ Reader *r;
+
+ r = findr(server, fd);
+ return r != nil && r->n != 0;
+}
+
+void
+styxnewclient(Styxserver *server, int fd)
+{
+ USED(server);
+ USED(fd);
+}
+
+void
+styxfreeclient(Styxserver *server, int fd)
+{
+ Reader *r, **rp;
+ Union *u;
+
+ u = server->priv;
+ r = findr(server, fd);
+ if(r == nil)
+ return;
+ xlock(&u->lock);
+ for(rp = &u->rr; *rp != nil; rp = &(*rp)->next)
+ if(r == *rp){
+ *rp = r->next;
+ break;
+ }
+ xunlock(&u->lock);
+ if(r->pid >= 0)
+ postnote(PNPROC, r->pid, "kill");
+ free(r);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+ int i;
+ Reader *r;
+ Union *u;
+
+ u = server->priv;
+ for(i = 0; i < 100; i++){
+ if(u->lr != nil)
+ return nil;
+ xlock(&u->lock);
+ for(r = u->rr; r != nil; r = r->next)
+ if(r->n != 0){
+ xunlock(&u->lock);
+ return nil;
+ }
+ xunlock(&u->lock);
+ sleep(100);
+ }
+ return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ Reader *r;
+ Union *u;
+ int rn;
+ char *rbuf;
+
+ USED(m);
+ r = findr(server, fd);
+ if(r == nil)
+ return -1;
+ u = server->priv;
+ xlock(&u->lock);
+ rn = r->n;
+ rbuf = r->buf;
+ if(rn < 0){
+ xunlock(&u->lock);
+ return rn;
+ }
+ if(n > rn)
+ n = rn;
+ memmove(buf, rbuf, n);
+ rn -= n;
+ memmove(rbuf, rbuf+n, rn);
+ r->n = rn;
+ xunlock(&u->lock);
+ return n;
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ USED(server);
+ USED(m);
+ return write(fd, buf, n);
+}
+
+void
+styxexit(int n)
+{
+ if(n)
+ exits("error");
+ else
+ exits(nil);
+}
--- /dev/null
+++ b/tools/libstyx/Posix.c
@@ -1,0 +1,165 @@
+#define __EXTENSIONS__
+#define _BSD_COMPAT
+#include <lib9.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Fdset Fdset;
+
+struct Fdset
+{
+ fd_set infds, outfds, excfds, r_infds, r_outfds, r_excfds;
+};
+
+int
+styxinitsocket(void)
+{
+ return 0;
+}
+
+void
+styxendsocket(void)
+{
+}
+
+void
+styxclosesocket(int fd)
+{
+ close(fd);
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+ struct sockaddr_in sin;
+ int s, one;
+
+ USED(server);
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if(s < 0)
+ return s;
+ one = 1;
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0)
+ fprint(2, "setsockopt failed\n");
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = 0;
+ sin.sin_port = htons(atoi(port));
+ if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
+ close(s);
+ return -1;
+ }
+ if(listen(s, 20) < 0){
+ close(s);
+ return -1;
+ }
+ return s;
+}
+
+int
+styxaccept(Styxserver *server)
+{
+ struct sockaddr_in sin;
+ int s;
+ socklen_t len;
+
+ len = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ s = accept(server->connfd, (struct sockaddr *)&sin, &len);
+ if(s < 0){
+ if(errno != EINTR)
+ fprint(2, "error in accept: %s\n", strerror(errno));
+ }
+ return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+ Fdset *fs;
+
+ server->priv = fs = malloc(sizeof(Fdset));
+ FD_ZERO(&fs->infds);
+ FD_ZERO(&fs->outfds);
+ FD_ZERO(&fs->excfds);
+ FD_SET(server->connfd, &fs->infds);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ return FD_ISSET(server->connfd, &fs->r_infds);
+}
+
+void
+styxnewclient(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ FD_SET(s, &fs->infds);
+}
+
+void
+styxfreeclient(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ FD_CLR(s, &fs->infds);
+}
+
+int
+styxnewmsg(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ return FD_ISSET(s, &fs->r_infds) || FD_ISSET(s, &fs->r_excfds);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+ struct timeval seltime;
+ int nfds;
+ Fdset *fs;
+
+ fs = server->priv;
+ fs->r_infds = fs->infds;
+ fs->r_outfds = fs->outfds;
+ fs->r_excfds = fs->excfds;
+ seltime.tv_sec = 10;
+ seltime.tv_usec = 0L;
+ nfds = select(sizeof(fd_set)*8, &fs->r_infds, &fs->r_outfds, &fs->r_excfds, &seltime);
+ if(nfds < 0 && errno != EINTR)
+ return"error in select";
+ return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ return recv(fd, buf, n, m);
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ return send(fd, buf, n, m);
+}
+
+void
+styxexit(int n)
+{
+ exit(n);
+}
--- /dev/null
+++ b/tools/libstyx/mkfile
@@ -1,0 +1,11 @@
+<../../mkconfig
+
+LIB=libstyx.a
+
+OFILES=\
+ styxserver.$O\
+ $TARGMODEL.$O\
+
+HFILES=\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
--- /dev/null
+++ b/tools/libstyx/styxaux.h
@@ -1,0 +1,14 @@
+int styxinitsocket(void);
+void styxendsocket(void);
+void styxclosesocket(int);
+int styxannounce(Styxserver*, char *);
+void styxinitwait(Styxserver*);
+int styxnewcall(Styxserver*);
+int styxnewmsg(Styxserver*, int);
+int styxaccept(Styxserver *server);
+void styxnewclient(Styxserver*, int);
+void styxfreeclient(Styxserver*, int);
+char* styxwaitmsg(Styxserver*);
+int styxrecv(Styxserver*, int, char*, int, int);
+int styxsend(Styxserver*, int, char*, int, int);
+void styxexit(int);
--- /dev/null
+++ b/tools/libstyx/styxserver.c
@@ -1,0 +1,1073 @@
+#include <lib9.h>
+#include <styx.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+#define MAXSTAT 512
+#define EMSGLEN 256 /* %r */
+
+#define TABSZ 32 /* power of 2 */
+
+static unsigned long boottime;
+static char* eve = "inferno";
+static int Debug = 0;
+
+char Enomem[] = "out of memory";
+char Eperm[] = "permission denied";
+char Enodev[] = "no free devices";
+char Ehungup[] = "write to hungup channel";
+char Eexist[] = "file exists";
+char Enonexist[] = "file does not exist";
+char Ebadcmd[] = "bad command";
+char Ebadarg[] = "bad arg in system call";
+char Enofid[] = "no such fid";
+char Enotdir[] = "not a directory";
+char Eopen[] = "already open";
+char Ebadfid[] = "bad fid";
+
+/* client state */
+enum{
+ CDISC = 01,
+ CNREAD = 02,
+ CRECV = 04,
+};
+
+typedef struct Walkqid Walkqid;
+
+struct Fid
+{
+ Client *client;
+ Fid *next;
+ short fid;
+ ushort open;
+ ushort mode; /* read/write */
+ ulong offset; /* in file */
+ int dri; /* dirread index */
+ Qid qid;
+};
+
+struct Walkqid
+{
+ Fid *clone;
+ int nqid;
+ Qid qid[1];
+};
+
+#define ASSERT(A,B) styxassert((int)A,B)
+
+static int hash(Path);
+static void deletefids(Client *);
+
+static void
+styxfatal(char *fmt, ...)
+{
+ char buf[1024], *out;
+ va_list arg;
+ out = seprint(buf, buf+sizeof(buf), "Fatal error: ");
+ va_start(arg, fmt);
+ out = vseprint(out, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ write(2, buf, out-buf);
+ styxexit(1);
+}
+
+static void
+styxassert(int vtrue, char *reason)
+{
+ if(!vtrue)
+ styxfatal("assertion failed: %s\n", reason);
+}
+
+void *
+styxmalloc(int bytes)
+{
+ char *m = malloc(bytes);
+ if(m == nil)
+ styxfatal(Enomem);
+ memset(m, 0, bytes);
+ return m;
+}
+
+void
+styxfree(void *p)
+{
+ free(p);
+}
+
+void
+styxdebug()
+{
+ Debug = 1;
+}
+
+static Client *
+newclient(Styxserver *server, int fd)
+{
+ Client *c = (Client *)styxmalloc(sizeof(Client));
+
+ if(Debug)
+ fprint(2, "New client at %lux\n", (ulong)c);
+ c->server = server;
+ c->fd = fd;
+ c->nread = 0;
+ c->nc = 0;
+ c->state = 0;
+ c->fids = nil;
+ c->uname = strdup(eve);
+ c->aname = strdup("");
+ c->next = server->clients;
+ server->clients = c;
+ if(server->ops->newclient)
+ server->ops->newclient(c);
+ return c;
+}
+
+static void
+freeclient(Client *c)
+{
+ Client **p;
+ Styxserver *server;
+
+ if(Debug)
+ fprint(2, "Freeing client at %lux\n", (ulong)c);
+ server = c->server;
+ if(server->ops->freeclient)
+ server->ops->freeclient(c);
+ for(p = &server->clients; *p; p = &(*p)->next)
+ if(*p == c){
+ styxclosesocket(c->fd);
+ *p = c->next;
+ deletefids(c);
+ free(c->uname);
+ free(c->aname);
+ styxfree(c);
+ return;
+ }
+}
+
+static int
+nbread(Client *c, int nr)
+{
+ int nb;
+
+ if(c->state&CDISC)
+ return -1;
+ nb = styxrecv(c->server, c->fd, c->msg + c->nread, nr, 0);
+ if(nb <= 0){
+ c->nread = 0;
+ c->state |= CDISC;
+ return -1;
+ }
+ c->nread += nb;
+ return 0;
+}
+
+static int
+rd(Client *c, Fcall *r)
+{
+ if(c->nc > 0){ /* last convM2S consumed nc bytes */
+ c->nread -= c->nc;
+ if((int)c->nread < 0){
+ r->ename = "negative size in rd";
+ return -1;
+ }
+ memmove(c->msg, c->msg+c->nc, c->nread);
+ c->nc = 0;
+ }
+ if(c->state&CRECV){
+ if(nbread(c, MSGMAX - c->nread) != 0){
+ r->ename = "unexpected EOF";
+ return -1;
+ }
+ c->state &= ~CRECV;
+ }
+ c->nc = convM2S((uchar*)(c->msg), c->nread, r);
+ if(c->nc < 0){
+ r->ename = "bad message format";
+ return -1;
+ }
+ if(c->nc == 0 && c->nread > 0){
+ c->nread = 0;
+ c->state &= ~CNREAD;
+ return 0;
+ }
+ if(c->nread > c->nc)
+ c->state |= CNREAD;
+ else
+ c->state &= ~CNREAD;
+ if(c->nc == 0)
+ return 0;
+ /* fprint(2, "rd: %F\n", r); */
+ return 1;
+}
+
+static int
+wr(Client *c, Fcall *r)
+{
+ int n;
+ char buf[MSGMAX];
+
+ n = convS2M(r, (uchar*)buf, sizeof(buf));
+ if(n < 0){
+ r->ename = "bad message type in wr";
+ return -1;
+ }
+ /* fprint(2, "wr: %F\n", r); */
+ return styxsend(c->server, c->fd, buf, n, 0);
+}
+
+static void
+sremove(Styxserver *server, Styxfile *f)
+{
+ Styxfile *s, *next, **p;
+
+ if(f == nil)
+ return;
+ if(Debug)
+ fprint(2, "Remove file %s Qid=%llx\n", f->d.name, f->d.qid.path);
+ if(f->d.qid.type&QTDIR)
+ for(s = f->child; s != nil; s = next){
+ next = s->sibling;
+ sremove(server, s);
+ }
+ for(p = &server->ftab[hash(f->d.qid.path)]; *p; p = &(*p)->next)
+ if(*p == f){
+ *p = f->next;
+ break;
+ }
+ for(p = &f->parent->child; *p; p = &(*p)->sibling)
+ if(*p == f){
+ *p = f->sibling;
+ break;
+ }
+ styxfree(f->d.name);
+ styxfree(f->d.uid);
+ styxfree(f->d.gid);
+ styxfree(f);
+}
+
+int
+styxrmfile(Styxserver *server, Path qid)
+{
+ Styxfile *f;
+
+ f = styxfindfile(server, qid);
+ if(f != nil){
+ if(f->parent == nil)
+ return -1;
+ sremove(server, f);
+ return 0;
+ }
+ return -1;
+}
+
+static void
+incref(Styxfile *f)
+{
+ if(f != nil)
+ f->ref++;
+}
+
+static void
+decref(Styxfile *f)
+{
+ if(f != nil)
+ --f->ref;
+}
+
+static void
+increff(Fid *f)
+{
+ incref(styxfindfile(f->client->server, f->qid.path));
+}
+
+static void
+decreff(Fid *f)
+{
+ decref(styxfindfile(f->client->server, f->qid.path));
+}
+
+static void
+incopen(Fid *f)
+{
+ Styxfile *file;
+
+ if(f->open && (file = styxfindfile(f->client->server, f->qid.path)) != nil)
+ file->open++;
+}
+
+static void
+decopen(Fid *f)
+{
+ Styxfile *file;
+
+ if(f->open && (file = styxfindfile(f->client->server, f->qid.path)) != nil)
+ file->open--;
+}
+
+int
+styxperm(Styxfile *f, char *uid, int mode)
+{
+ int m, p;
+
+ p = 0;
+ switch(mode&3){
+ case OREAD: p = AREAD; break;
+ case OWRITE: p = AWRITE; break;
+ case ORDWR: p = AREAD+AWRITE; break;
+ case OEXEC: p = AEXEC; break;
+ }
+ if(mode&OTRUNC)
+ p |= AWRITE;
+ m = f->d.mode&7;
+ if((p&m) == p)
+ return 1;
+ if(strcmp(f->d.uid, uid) == 0){
+ m |= (f->d.mode>>6)&7;
+ if((p&m) == p)
+ return 1;
+ }
+ if(strcmp(f->d.gid, uid) == 0){
+ m |= (f->d.mode>>3)&7;
+ if((p&m) == p)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+hash(Path path)
+{
+ return path&(TABSZ-1);
+}
+
+Styxfile *
+styxfindfile(Styxserver *server, Path path)
+{
+ Styxfile *f;
+
+ for(f = server->ftab[hash(path)]; f != nil; f = f->next){
+ if(f->d.qid.path == path)
+ return f;
+ }
+ return nil;
+}
+
+static Fid *
+findfid(Client *c, short fid)
+{
+ Fid *f;
+ for(f = c->fids; f && f->fid != fid; f = f->next)
+ ;
+ return f;
+}
+
+static void
+deletefid(Client *c, Fid *d)
+{
+ /* TODO: end any outstanding reads on this fid */
+ Fid **f;
+
+ for(f = &c->fids; *f; f = &(*f)->next)
+ if(*f == d){
+ decreff(d);
+ decopen(d);
+ *f = d->next;
+ styxfree(d);
+ return;
+ }
+}
+
+static void
+deletefids(Client *c)
+{
+ Fid *f, *g;
+
+ for(f = c->fids; f; f = g){
+ decreff(f);
+ decopen(f);
+ g = f->next;
+ styxfree(f);
+ }
+}
+
+Fid *
+newfid(Client *c, short fid, Qid qid){
+ Fid *f;
+
+ f = styxmalloc(sizeof(Fid));
+ ASSERT(f, "newfid");
+ f->client = c;
+ f->fid = fid;
+ f->open = 0;
+ f->dri = 0;
+ f->qid = qid;
+ f->next = c->fids;
+ c->fids = f;
+ increff(f);
+ return f;
+}
+
+static void
+flushtag(int oldtag)
+{
+ USED(oldtag);
+}
+
+int
+eqqid(Qid a, Qid b)
+{
+ return a.path == b.path && a.vers == b.vers;
+}
+
+static Fid *
+fidclone(Fid *old, short fid)
+{
+ Fid *new;
+
+ new = newfid(old->client, fid, old->qid);
+ return new;
+}
+
+static Walkqid*
+devwalk(Client *c, Styxfile *file, Fid *fp, Fid *nfp, char **name, int nname, char **err)
+{
+ Styxserver *server;
+ long j;
+ Walkqid *wq;
+ char *n;
+ Styxfile *p, *f;
+ Styxops *ops;
+ Qid qid;
+
+ *err = nil;
+ server = c->server;
+ ops = server->ops;
+
+ wq = styxmalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ wq->nqid = 0;
+
+ p = file;
+ qid = p != nil ? p->d.qid : fp->qid;
+ for(j = 0; j < nname; j++){
+ if(!(qid.type&QTDIR)){
+ if(j == 0)
+ styxfatal("devwalk error");
+ *err = Enotdir;
+ goto Done;
+ }
+ if(p != nil && !styxperm(p, c->uname, OEXEC)){
+ *err = Eperm;
+ goto Done;
+ }
+ n = name[j];
+ if(strcmp(n, ".") == 0){
+ Accept:
+ wq->qid[wq->nqid++] = nfp->qid;
+ continue;
+ }
+ if(p != nil && strcmp(n, "..") == 0 && p->parent){
+ decref(p);
+ nfp->qid.path = p->parent->d.qid.path;
+ nfp->qid.type = p->parent->d.qid.type;
+ nfp->qid.vers = 0;
+ incref(p->parent);
+ p = p->parent;
+ qid = p->d.qid;
+ goto Accept;
+ }
+
+ if(ops->walk != nil){
+ char *e;
+
+ e = ops->walk(&qid, n);
+ if(e == nil){
+ decreff(nfp);
+ nfp->qid = qid;
+ increff(nfp);
+ p = styxfindfile(server, qid.path);
+ if(server->needfile && p == nil)
+ goto Done;
+ qid = p != nil ? p->d.qid : nfp->qid;
+ goto Accept;
+ }
+ }
+
+ if(p != nil)
+ for(f = p->child; f != nil; f = f->sibling){
+ if(strcmp(n, f->d.name) == 0){
+ decref(p);
+ nfp->qid.path = f->d.qid.path;
+ nfp->qid.type = f->d.qid.type;
+ nfp->qid.vers = 0;
+ incref(f);
+ p = f;
+ qid = p->d.qid;
+ goto Accept;
+ }
+ }
+ if(j == 0 && *err == nil)
+ *err = Enonexist;
+ goto Done;
+ }
+Done:
+ if(*err != nil){
+ styxfree(wq);
+ return nil;
+ }
+ return wq;
+}
+
+static long
+devdirread(Fid *fp, Styxfile *file, char *d, long n)
+{
+ long dsz, m;
+ Styxfile *f;
+ int i;
+
+ struct{
+ Dir d;
+ char slop[100]; /* TO DO */
+ }dir;
+
+ f = file->child;
+ for(i = 0; i < fp->dri; i++)
+ if(f == 0)
+ return 0;
+ else
+ f = f->sibling;
+ for(m = 0; m < n; fp->dri++){
+ if(f == nil)
+ break;
+ dir.d = f->d;
+ dsz = convD2M(&dir.d, (uchar*)d, n-m);
+ m += dsz;
+ d += dsz;
+ f = f->sibling;
+ }
+
+ return m;
+}
+
+static char*
+nilconv(char *s)
+{
+ if(s != nil && s[0] == '\0')
+ return nil;
+ return s;
+}
+
+static Styxfile *
+newfile(Styxserver *server, Styxfile *parent, int isdir, Path qid, char *name, int mode, char *owner)
+{
+ Styxfile *file;
+ Dir d;
+ int h;
+
+ if(qid == -1)
+ qid = server->qidgen++;
+ file = styxfindfile(server, qid);
+ if(file != nil)
+ return nil;
+ if(parent != nil){
+ for(file = parent->child; file != nil; file = file->sibling)
+ if(strcmp(name, file->d.name) == 0)
+ return nil;
+ }
+ file = (Styxfile *)styxmalloc(sizeof(Styxfile));
+ file->parent = parent;
+ file->child = nil;
+ h = hash(qid);
+ file->next = server->ftab[h];
+ server->ftab[h] = file;
+ if(parent){
+ file->sibling = parent->child;
+ parent->child = file;
+ }else
+ file->sibling = nil;
+
+ d.type = 'X';
+ d.dev = 'x';
+ d.qid.path = qid;
+ d.qid.type = 0;
+ d.qid.vers = 0;
+ d.mode = mode;
+ d.atime = time(0);
+ d.mtime = boottime;
+ d.length = 0;
+ d.name = strdup(name);
+ d.uid = strdup(owner);
+ d.gid = strdup(eve);
+ d.muid = "";
+
+ if(isdir){
+ d.qid.type |= QTDIR;
+ d.mode |= DMDIR;
+ }
+ else{
+ d.qid.type &= ~QTDIR;
+ d.mode &= ~DMDIR;
+ }
+
+ file->d = d;
+ file->ref = 0;
+ file->open = 0;
+ if(Debug)
+ fprint(2, "New file %s Qid=%llx\n", name, qid);
+ return file;
+}
+
+static void
+run(Client *c)
+{
+ Fcall f;
+ Fid *fp, *nfp;
+ int i, open, mode;
+ char ebuf[EMSGLEN];
+ Walkqid *wq;
+ Styxfile *file;
+ Dir dir;
+ Qid qid;
+ Styxops *ops;
+ char strs[128];
+
+ ebuf[0] = 0;
+ if(rd(c, &f) <= 0)
+ return;
+ if(f.type == Tflush){
+ flushtag(f.oldtag);
+ f.type = Rflush;
+ wr(c, &f);
+ return;
+ }
+ ops = c->server->ops;
+ file = nil;
+ fp = findfid(c, f.fid);
+ if(f.type != Tversion && f.type != Tauth && f.type != Tattach){
+ if(fp == nil){
+ f.type = Rerror;
+ f.ename = Enofid;
+ wr(c, &f);
+ return;
+ }
+ else{
+ file = styxfindfile(c->server, fp->qid.path);
+ if(c->server->needfile && file == nil){
+ f.type = Rerror;
+ f.ename = Enonexist;
+ wr(c, &f);
+ return;
+ }
+ }
+ }
+ /* if(fp == nil) fprint(2, "fid not found for %d\n", f.fid); */
+ switch(f.type){
+ case Twalk:
+ if(Debug){
+ fprint(2, "Twalk %d %d", f.fid, f.newfid);
+ for(i = 0; i < f.nwname; i++)
+ fprint(2, " %s", f.wname[i]);
+ fprint(2, "\n");
+ }
+ nfp = findfid(c, f.newfid);
+ f.type = Rerror;
+ if(nfp){
+ deletefid(c, nfp);
+ nfp = nil;
+ }
+ if(nfp){
+ f.ename = "fid in use";
+ if(Debug) fprint(2, "walk: %s\n", f.ename);
+ wr(c, &f);
+ break;
+ }else if(fp->open){
+ f.ename = "can't clone";
+ wr(c, &f);
+ break;
+ }
+ if(f.newfid != f.fid)
+ nfp = fidclone(fp, f.newfid);
+ else
+ nfp = fp;
+ if((wq = devwalk(c, file, fp, nfp, f.wname, f.nwname, &f.ename)) == nil){
+ if(nfp != fp)
+ deletefid(c, nfp);
+ f.type = Rerror;
+ }else{
+ if(nfp != fp){
+ if(wq->nqid != f.nwname)
+ deletefid(c, nfp);
+ }
+ f.type = Rwalk;
+ f.nwqid = wq->nqid;
+ for(i = 0; i < wq->nqid; i++)
+ f.wqid[i] = wq->qid[i];
+ styxfree(wq);
+ }
+ wr(c, &f);
+ break;
+ case Topen:
+ if(Debug)
+ fprint(2, "Topen %d\n", f.fid);
+ f.ename = nil;
+ if(fp->open)
+ f.ename = Eopen;
+ else if((fp->qid.type&QTDIR) && (f.mode&(OWRITE|OTRUNC|ORCLOSE)))
+ f.ename = Eperm;
+ else if(file != nil && !styxperm(file, c->uname, f.mode))
+ f.ename = Eperm;
+ else if((f.mode&ORCLOSE) && file != nil && file->parent != nil && !styxperm(file->parent, c->uname, OWRITE))
+ f.ename = Eperm;
+ if(f.ename != nil){
+ f.type = Rerror;
+ wr(c, &f);
+ break;
+ }
+ f.ename = Enonexist;
+ decreff(fp);
+ if(ops->open == nil || (f.ename = ops->open(&fp->qid, f.mode)) == nil){
+ f.type = Ropen;
+ f.qid = fp->qid;
+ fp->mode = f.mode;
+ fp->open = 1;
+ fp->offset = 0;
+ incopen(fp);
+ }
+ else
+ f.type = Rerror;
+ increff(fp);
+ wr(c, &f);
+ break;
+ case Tcreate:
+ if(Debug)
+ fprint(2, "Tcreate %d %s\n", f.fid, f.name);
+ f.ename = nil;
+ if(fp->open)
+ f.ename = Eopen;
+ else if(!(fp->qid.type&QTDIR))
+ f.ename = Enotdir;
+ else if((f.perm&DMDIR) && (f.mode&(OWRITE|OTRUNC|ORCLOSE)))
+ f.ename = Eperm;
+ else if(file != nil && !styxperm(file, c->uname, OWRITE))
+ f.ename = Eperm;
+ if(f.ename != nil){
+ f.type = Rerror;
+ wr(c, &f);
+ break;
+ }
+ f.ename = Eperm;
+ decreff(fp);
+ if(file != nil){
+ if(f.perm&DMDIR)
+ f.perm = (f.perm&~0777) | (file->d.mode&f.perm&0777) | DMDIR;
+ else
+ f.perm = (f.perm&(~0777|0111)) | (file->d.mode&f.perm&0666);
+ }
+ if(ops->create && (f.ename = ops->create(&fp->qid, f.name, f.perm, f.mode)) == nil){
+ f.type = Rcreate;
+ f.qid = fp->qid;
+ fp->mode = f.mode;
+ fp->open = 1;
+ fp->offset = 0;
+ incopen(fp);
+ }
+ else
+ f.type = Rerror;
+ increff(fp);
+ wr(c, &f);
+ break;
+ case Tread:
+ if(Debug)
+ fprint(2, "Tread %d\n", f.fid);
+ if(!fp->open){
+ f.type = Rerror;
+ f.ename = Ebadfid;
+ wr(c, &f);
+ break;
+ }
+ if(fp->qid.type&QTDIR || (file != nil && file->d.qid.type&QTDIR)){
+ f.type = Rread;
+ if(file == nil){
+ f.ename = Eperm;
+ if(ops->read && (f.ename = ops->read(fp->qid, c->data, (ulong*)(&f.count), fp->dri)) == nil){
+ f.data = c->data;
+ }
+ else
+ f.type = Rerror;
+ }
+ else{
+ f.count = devdirread(fp, file, c->data, f.count);
+ f.data = c->data;
+ }
+ }else{
+ f.ename = Eperm;
+ f.type = Rerror;
+ if(ops->read && (f.ename = ops->read(fp->qid, c->data, (ulong*)(&f.count), f.offset)) == nil){
+ f.type = Rread;
+ f.data = c->data;
+ }
+ }
+ wr(c, &f);
+ break;
+ case Twrite:
+ if(Debug)
+ fprint(2, "Twrite %d\n", f.fid);
+ if(!fp->open){
+ f.type = Rerror;
+ f.ename = Ebadfid;
+ wr(c, &f);
+ break;
+ }
+ f.ename = Eperm;
+ f.type = Rerror;
+ if(ops->write && (f.ename = ops->write(fp->qid, f.data, (ulong*)(&f.count), f.offset)) == nil){
+ f.type = Rwrite;
+ }
+ wr(c, &f);
+ break;
+ case Tclunk:
+ if(Debug)
+ fprint(2, "Tclunk %d\n", f.fid);
+ open = fp->open;
+ mode = fp->mode;
+ qid = fp->qid;
+ deletefid(c, fp);
+ f.type = Rclunk;
+ if(open && ops->close && (f.ename = ops->close(qid, mode)) != nil)
+ f.type = Rerror;
+ wr(c, &f);
+ break;
+ case Tremove:
+ if(Debug)
+ fprint(2, "Tremove %d\n", f.fid);
+ if(file != nil && file->parent != nil && !styxperm(file->parent, c->uname, OWRITE)){
+ f.type = Rerror;
+ f.ename = Eperm;
+ deletefid(c, fp);
+ wr(c, &f);
+ break;
+ }
+ f.ename = Eperm;
+ if(ops->remove && (f.ename = ops->remove(fp->qid)) == nil)
+ f.type = Rremove;
+ else
+ f.type = Rerror;
+ deletefid(c, fp);
+ wr(c, &f);
+ break;
+ case Tstat:
+ if(Debug)
+ fprint(2, "Tstat %d qid=%llx\n", f.fid, fp->qid.path);
+ f.stat = styxmalloc(MAXSTAT);
+ f.ename = "stat error";
+ if(ops->stat == nil && file != nil){
+ f.type = Rstat;
+ f.nstat = convD2M(&file->d, f.stat, MAXSTAT);
+ }
+ else if(ops->stat && (f.ename = ops->stat(fp->qid, &dir)) == nil){
+ f.type = Rstat;
+ f.nstat = convD2M(&dir, f.stat, MAXSTAT);
+ }
+ else
+ f.type = Rerror;
+ wr(c, &f);
+ styxfree(f.stat);
+ break;
+ case Twstat:
+ if(Debug)
+ fprint(2, "Twstat %d\n", f.fid);
+ f.ename = Eperm;
+ convM2D(f.stat, f.nstat, &dir, strs);
+ dir.name = nilconv(dir.name);
+ dir.uid = nilconv(dir.uid);
+ dir.gid = nilconv(dir.gid);
+ dir.muid = nilconv(dir.muid);
+ if(ops->wstat && (f.ename = ops->wstat(fp->qid, &dir)) == nil)
+ f.type = Rwstat;
+ else
+ f.type = Rerror;
+ wr(c, &f);
+ break;
+ case Tversion:
+ if(Debug)
+ fprint(2, "Tversion\n");
+ f.type = Rversion;
+ f.tag = NOTAG;
+ wr(c, &f);
+ break;
+ case Tauth:
+ if(Debug)
+ fprint(2, "Tauth\n");
+ f.type = Rauth;
+ wr(c, &f);
+ break;
+ case Tattach:
+ if(Debug)
+ fprint(2, "Tattach %d %s %s\n", f.fid, f.uname[0] ? f.uname : c->uname, f.aname[0]? f.aname: c->aname);
+ if(fp){
+ f.type = Rerror;
+ f.ename = "fid in use";
+ }else{
+ Qid q;
+
+ if(f.uname[0]){
+ free(c->uname);
+ c->uname = strdup(f.uname);
+ }
+ if(f.aname[0]){
+ free(c->aname);
+ c->aname = strdup(f.aname);
+ }
+ q.path = Qroot;
+ q.type = QTDIR;
+ q.vers = 0;
+ fp = newfid(c, f.fid, q);
+ f.type = Rattach;
+ f.fid = fp->fid;
+ f.qid = q;
+ if(ops->attach && (f.ename = ops->attach(c->uname, c->aname)) != nil)
+ f.type = Rerror;
+ }
+ wr(c, &f);
+ break;
+ }
+}
+
+char *
+styxinit(Styxserver *server, Styxops *ops, char *port, int perm, int needfile)
+{
+ int i;
+
+ if(Debug)
+ fprint(2, "Initialising Styx server on port %s\n", port);
+ if(perm == -1)
+ perm = 0555;
+ server->ops = ops;
+ server->clients = nil;
+ server->root = nil;
+ server->ftab = (Styxfile**)malloc(TABSZ*sizeof(Styxfile*));
+ for(i = 0; i < TABSZ; i++)
+ server->ftab[i] = nil;
+ server->qidgen = Qroot+1;
+ if(styxinitsocket() < 0)
+ return "styxinitsocket failed";
+ server->connfd = styxannounce(server, port);
+ if(server->connfd < 0)
+ return "can't announce on network port";
+ styxinitwait(server);
+ server->root = newfile(server, nil, 1, Qroot, "/", perm|DMDIR, eve);
+ server->needfile = needfile;
+ return nil;
+}
+
+char*
+styxend(Styxserver *server)
+{
+ USED(server);
+ styxendsocket();
+ return nil;
+}
+
+char *
+styxwait(Styxserver *server)
+{
+ return styxwaitmsg(server);
+}
+
+char *
+styxprocess(Styxserver *server)
+{
+ Client *c;
+ int s;
+
+ if(styxnewcall(server)){
+ s = styxaccept(server);
+ if(s >= 0){
+ newclient(server, s);
+ styxnewclient(server, s);
+ }
+ }
+ for(c = server->clients; c != nil; ){
+ Client *next = c->next;
+
+ server->curc = c;
+ if(c->fd >= 0 && styxnewmsg(server, c->fd))
+ c->state |= CRECV;
+ if(c->state&(CNREAD|CRECV)){
+ if(c->state&CDISC){
+ styxfreeclient(server, c->fd);
+ freeclient(c);
+ }else
+ do
+ run(c);
+ while(c->state&CNREAD);
+ }
+ c = next;
+ }
+
+ return nil;
+}
+
+Client*
+styxclient(Styxserver *server)
+{
+ return server->curc;
+}
+
+Styxfile*
+styxaddfile(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner)
+{
+ Styxfile *f, *parent;
+
+ parent = styxfindfile(server, pqid);
+ if(parent == nil || (parent->d.qid.type&QTDIR) == 0)
+ return nil;
+ f = newfile(server, parent, 0, qid, name, mode, owner);
+ return f;
+}
+
+Styxfile*
+styxadddir(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner)
+{
+ Styxfile *f, *parent;
+
+ parent = styxfindfile(server, pqid);
+ if(parent == nil || (parent->d.qid.type&QTDIR) == 0)
+ return nil;
+ f = newfile(server, parent, 1, qid, name, mode|DMDIR, owner);
+ return f;
+}
+
+long
+styxreadstr(ulong off, char *buf, ulong n, char *str)
+{
+ int size;
+
+ size = strlen(str);
+ if(off >= size)
+ return 0;
+ if(off+n > size)
+ n = size-off;
+ memmove(buf, str+off, n);
+ return n;
+}
+
+Qid
+styxqid(int path, int isdir)
+{
+ Qid q;
+
+ q.path = path;
+ q.vers = 0;
+ if(isdir)
+ q.type = QTDIR;
+ else
+ q.type = 0;
+ return q;
+}
+
+void
+styxsetowner(char *name)
+{
+ eve = name;
+}
--- /dev/null
+++ b/tools/mkfile
@@ -1,0 +1,7 @@
+<../mkconfig
+
+DIRS=\
+ libstyx\
+ styxtest\
+
+<$ROOT/mkfiles/mksubdirs
--- /dev/null
+++ b/tools/odbc/mkfile
@@ -1,0 +1,19 @@
+<../../mkconfig
+
+TARG=odbc
+
+OFILES=\
+ odbc.$O\
+
+HFILES=\
+ ../libstyx/styxserver.h\
+
+LIBS=styx 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<mkfile-$SYSTARG
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS= $CFLAGS -I../libstyx
--- /dev/null
+++ b/tools/odbc/mkfile-Linux
@@ -1,0 +1,1 @@
+SYSLIBS=-lodbc
--- /dev/null
+++ b/tools/odbc/mkfile-MacOSX
@@ -1,0 +1,1 @@
+SYSLIBS= -liodbc
--- /dev/null
+++ b/tools/odbc/mkfile-Nt
@@ -1,0 +1,2 @@
+SYSLIBS= odbc32.lib wsock32.lib $SYSLIBS
+CFLAGS= $CFLAGS -DWINDOWSNT
--- /dev/null
+++ b/tools/odbc/mkfile-Solaris
@@ -1,0 +1,1 @@
+SYSLIBS=$EMULIBS
--- /dev/null
+++ b/tools/odbc/odbc.c
@@ -1,0 +1,1147 @@
+#ifdef WINDOWSNT
+#include <windows.h>
+#endif
+#include <lib9.h>
+#include <styx.h>
+#include "styxserver.h"
+/* #include <winsock.h> */
+
+#define DEFCOLSIZE 10000
+
+static int ODebug;
+
+char Eodbcalloc[] = "no free ODBC handles";
+char Enoconnect[] = "no ODBC connection";
+
+static char *netport = "6700";
+static char *inferno = "inferno";
+
+Styxserver *iserver;
+
+/* ----- */
+#include <sql.h>
+#include <sqlext.h>
+
+int nclients = 0;
+
+typedef struct Env Env;
+struct Env
+{
+ SQLHENV h; /* ODBC environment handle */
+};
+
+typedef struct Conn Conn;
+struct Conn
+{
+ SQLHDBC h; /* ODBC connection handle */
+ int connected;
+};
+
+typedef struct Coltype Coltype;
+struct Coltype
+{
+ char name[255];
+ ushort type;
+ SQLUINTEGER size;
+ ushort digits;
+ ushort nulls;
+};
+
+typedef struct Column Column;
+struct Column
+{
+ char *data;
+ SQLINTEGER len;
+};
+
+typedef struct Stmt Stmt;
+struct Stmt
+{
+ SQLHSTMT h; /* ODBC statement handle */
+ ushort ncols; /* number of columns in result */
+ ulong nrows; /* number of rows affected by update, insert, delete */
+ Coltype *cols; /* column descriptions */
+ Column *rec; /* data record */
+ char *headstr; /* column headings if requested */
+};
+
+/* ----- */
+enum
+{
+ Qtopdir = 0, /* top level directory */
+ Qnclients,
+ Qprotodir,
+ Qclonus,
+ Qconvdir,
+ Qdata,
+ Qcmd,
+ Qctl,
+ Qstatus,
+ Qformat,
+ Qsources,
+ Qerror,
+
+ MAXPROTO = 1
+};
+#define TYPE(x) ((x).path & 0xf)
+#define CONV(x) (((x).path >> 4)&0xfff)
+#define PROTO(x) (((x).path >> 16)&0xff)
+#define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y))
+
+typedef struct Proto Proto;
+typedef struct Conv Conv;
+typedef struct Output Output;
+
+struct Output {
+ enum {Fixed, Float} style; /* output style */
+ uchar fs; /* float: field separator */
+ uchar rs; /* float: record separator */
+};
+Output defoutput = {Float, '|', '\n'};
+
+struct Conv
+{
+ int x;
+ int ref;
+ int perm;
+ char *owner;
+ char* state;
+ Proto* p;
+
+ /*-----*/
+ Conn c;
+ Stmt s;
+ Output out;
+ int headings;
+ char errmsg[400]; /* odbc error messages can be big */
+};
+
+struct Proto
+{
+ int x;
+ char *name;
+ uint nc;
+ int maxconv;
+ Conv** conv;
+ Qid qid;
+};
+
+typedef struct Dirtab Dirtab;
+struct Dirtab
+{
+ char name[255];
+ Qid qid;
+ long length;
+ long perm;
+};
+
+static int np;
+static Proto proto[MAXPROTO];
+static Conv* protoclone(Proto*, char*);
+
+typedef int Devgen(Fid*, char *, Dirtab*, int, int, Dir*);
+
+struct xClient {
+ /* ---- */
+ Env e;
+};
+
+#define H(c) ((Env*)(c->u))->h
+
+void
+fatal(char *fmt, ...)
+{
+ char buf[1024], *out;
+ va_list arg;
+ out = vseprint(buf, buf+sizeof(buf), "Fatal error: ", 0);
+ va_start(arg, fmt);
+ out = vseprint(out, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ write(2, buf, out-buf);
+ exit(1);
+}
+
+#define ASSERT(A,B) xassert((int)A,B)
+
+void
+xassert(int true, char *reason)
+{
+ if(!true)
+ fatal("assertion failed: %s\n", reason);
+}
+
+void *
+xmalloc(int bytes)
+{
+ char *m = malloc(bytes);
+ if(m)
+ memset(m, 0, bytes);
+// print("xmalloc: %lux (%d)\n", m, bytes);
+ return m;
+}
+
+void
+xfree(void *p, char *from)
+{
+// print("xfree: %lux [%s]\n", p, from);
+ free(p);
+}
+
+char *
+odbcerror(Conv *cv, int lasterr)
+{
+ char sqlstate[6];
+ long native;
+ char *mp;
+ short msglen;
+
+ if(cv == 0)
+ return "";
+ if(lasterr)
+ return cv->errmsg;
+ if(cv->c.connected)
+ SQLGetDiagRec(SQL_HANDLE_STMT, cv->s.h, 1, sqlstate, &native, cv->errmsg, sizeof(cv->errmsg), &msglen);
+ else
+ SQLGetDiagRec(SQL_HANDLE_DBC, cv->c.h, 1, sqlstate, &native, cv->errmsg, sizeof(cv->errmsg), &msglen);
+ cv->errmsg[msglen]=0;
+ /* fprint(2, "c: sqlstate: %s, msg %s\n", sqlstate, cv->errmsg); */
+ if((mp=strrchr(cv->errmsg, ']')) != 0)
+ return mp+1;
+ return cv->errmsg;
+}
+
+char*
+odbcsources(Client *c)
+{
+ int ss, i;
+ char server[SQL_MAX_DSN_LENGTH+1];
+ char source[1024];
+ char buff[1024+SQL_MAX_DSN_LENGTH+1];
+ short serverlen, sourcelen;
+ char *all = nil, *p;
+
+ for (i=0;; i++) {
+ ss = SQLDataSources(H(c), (i==0 ? SQL_FETCH_FIRST : SQL_FETCH_NEXT),
+ server, sizeof(server), &serverlen,
+ source, sizeof(source), &sourcelen);
+ if (ss != SQL_SUCCESS)
+ break;
+ snprint(buff, sizeof(buff), "%s:%s\n", server, source);
+ if (i == 0)
+ all = strdup(buff);
+ else {
+ p = all;
+ all = malloc(strlen(all)+strlen(buff)+1);
+ strcpy(all, p);
+ strcat(all, buff);
+ free(p);
+ }
+ }
+ return all;
+}
+
+
+int
+sqlerr(Conv *c, int sqlstatus, char *errp, char *func, char *sqlcall)
+{
+ char *e;
+
+ errp[0] = 0;
+ e = "failed";
+ if (sqlstatus == SQL_ERROR || sqlstatus == SQL_SUCCESS_WITH_INFO)
+ strecpy(errp, errp+ERRMAX, odbcerror(c, 0));
+ if (sqlstatus == SQL_SUCCESS_WITH_INFO)
+ e = "info";
+ if (sqlstatus != SQL_SUCCESS)
+ fprint(2, "%s: %s %s - %s\n", func, sqlcall, e, errp);
+ if (sqlstatus != SQL_SUCCESS && sqlstatus != SQL_SUCCESS_WITH_INFO)
+ return 1;
+ return 0;
+}
+
+char*
+odbcnewclient(Client *c)
+{
+ int ss;
+
+ /* ---- */
+ c->u = styxmalloc(sizeof(Env));
+ ss = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H(c));
+ if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
+ fprint(2, "newclient: SQLAllocHandle failed\n");
+ return "SQLAllocHandle failed";
+ }
+ ss = SQLSetEnvAttr(H(c), SQL_ATTR_ODBC_VERSION, (char*)SQL_OV_ODBC2, 0);
+ if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
+ fprint(2, "newclient: SQLSetEnvAttr failed\n");
+ return "SQLSetEnvAttr failed";
+ }
+ nclients++;
+ return nil;
+}
+
+char*
+odbcfreeclient(Client *c)
+{
+ int ss;
+
+ ss = SQLFreeHandle(SQL_HANDLE_ENV, H(c));
+ if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
+ fprint(2, "freeclient: SQLFreeHandle failed\n");
+ styxfree(c->u);
+ nclients--;
+ return nil;
+}
+
+int
+parsefields(char *lp, char **fields, int n, char *sep)
+{
+ int i;
+
+ for(i=0; lp && *lp && i<n; i++){
+ while(*lp && strchr(sep, *lp) != 0)
+ *lp++=0;
+ if(*lp == 0)
+ break;
+ fields[i]=lp;
+ while(*lp && strchr(sep, *lp) == 0)
+ lp++;
+ }
+ return i;
+}
+
+void
+odbcdisconnect(Conv *c)
+{
+ int ss;
+
+ if(c->c.connected){
+ ss = SQLFreeHandle(SQL_HANDLE_STMT, c->s.h);
+ if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
+ fprint(2, "odbcdisconnect: SQLFreeHandle failed\n");
+ ss = SQLDisconnect(c->c.h);
+ if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
+ fprint(2, "odbcdisconnect: SQLDisconnect failed\n");
+ c->c.connected = 0;
+ }
+}
+
+int
+odbcconnect(Conv *c, char *server, char *user, char *auth, char *ename)
+{
+ int ss;
+
+ odbcdisconnect(c);
+ ss = SQLConnect(c->c.h, server, SQL_NTS, user, strlen(user), auth, strlen(auth));
+ if (sqlerr(c, ss, ename, "odbcconnect", "SQLConnect"))
+ return -1;
+ c->c.connected = 1;
+ ss = SQLAllocHandle(SQL_HANDLE_STMT, c->c.h, &c->s.h);
+ if (sqlerr(c, ss, ename, "odbcconnect", "SQLAllocHandle"))
+ return -1;
+ return 0;
+}
+
+int
+odbcnewconv(Client *c, Conv *cv)
+{
+ int ss;
+
+ ss = SQLAllocHandle(SQL_HANDLE_DBC, H(c), &cv->c.h);
+ if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
+ fprint(2, "odbcnewconv: SQLAllocHandle failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+void
+odbcfreeconv(Conv *c)
+{
+ int ss;
+
+ odbcdisconnect(c);
+ ss = SQLFreeHandle(SQL_HANDLE_DBC, c->c.h);
+ if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
+ fprint(2, "odbcfreeconv: SQLFreeHandle failed\n");
+}
+
+/* Free up all memory used in the last query */
+void
+freestat(Stmt *s)
+{
+ int i;
+ if (s->ncols == 0)
+ return;
+ for(i=0; i<s->ncols; i++){
+ Column *d = &s->rec[i];
+ xfree(d->data, "freestat - data");
+ }
+ xfree(s->cols, "freestat - cols");
+ s->cols = 0;
+ xfree(s->rec, "freestat - rec");
+ s->rec = 0;
+ xfree(s->headstr, "freestat - headstr");
+ s->headstr = 0;
+ s->ncols = 0;
+}
+
+
+/* build an array describing the columns */
+int
+mkcols(Conv *c, char *ename)
+{
+ Stmt *s;
+ int rv, i, err, hsize;
+ ushort ignore;
+ char *p;
+
+ s = &c->s;
+ s->ncols = 0;
+ rv = SQLNumResultCols(s->h, &s->ncols);
+ if (sqlerr(c, rv, ename, "mkcols", "SQLNumResultCols"))
+ return -1;
+ s->cols = xmalloc(s->ncols*sizeof(Coltype));
+ err = 0;
+ hsize = 0;
+ for(i=0; i<s->ncols; i++){
+ Coltype *t = &s->cols[i];
+ rv = SQLDescribeCol(s->h, i+1, t->name, sizeof(t->name), &ignore, &t->type, &t->size, &t->digits, &t->nulls);
+ if (sqlerr(c, rv, ename, "mkcols", "SQLDescribeCol"))
+ err++;
+ if(t->size == 0 || t->size > MSGMAX) /* odbc should return 0 if size not available, not -1 */
+ t->size = DEFCOLSIZE;
+ hsize += strlen(t->name) + 1;
+ }
+ if (c->headings) {
+ hsize += 2;
+ s->headstr = xmalloc(hsize);
+ p = s->headstr;
+ for(i=0; i<s->ncols; i++) {
+ Coltype *t = &s->cols[i];
+ p += sprint(p, "%s%c", t->name, c->out.fs);
+ }
+ p[-1] = c->out.rs;
+ } else
+ s->headstr = 0;
+ return (err ? -1 : 0);
+}
+
+/* build a record to hold `fetched' results */
+int
+mkrec(Conv *c, char *ename)
+{
+ Stmt *s;
+ int rv, i;
+
+ s = &c->s;
+ s->rec = xmalloc(s->ncols*sizeof(Column));
+ for(i=0; i<s->ncols; i++){
+ Coltype *t = &s->cols[i];
+ Column *d = &s->rec[i];
+ if (ODebug)
+ print("Column %d size=%ud type=%hd\n", i, t->size, t->type);
+ d->data = xmalloc(t->size+1); /* expects to zero terminate */
+ rv = SQLBindCol(s->h, i+1, SQL_C_CHAR, d->data, t->size+1, &d->len);
+ if (sqlerr(c, rv, ename, "mkrec", "SQLBindCol"))
+ return -1;
+ }
+ return 0;
+}
+
+int
+rowcount(Conv *c, char *ename)
+{
+ Stmt *s;
+ int rv;
+
+ s = &c->s;
+ s->nrows = 0;
+ rv = SQLRowCount(s->h, &s->nrows);
+ if (sqlerr(c, rv, ename, "rowcount", "SQLRowCount"))
+ return -1;
+ return 0;
+}
+
+int
+odbcfetch(Conv *c, char *ename)
+{
+ Stmt *s = &c->s;
+ int rv;
+
+ rv = SQLFetch(s->h);
+ if(rv == SQL_NO_DATA) {
+ freestat(s);
+ return 0;
+ }
+ if (sqlerr(c, rv, ename, "odbcfetch", "SQLFetch")) {
+ freestat(s);
+ return -1;
+ }
+ return 1;
+}
+
+int
+odbcresults(Conv *c, char *ename)
+{
+ if(mkcols(c, ename))
+ return -1;
+ if(mkrec(c, ename))
+ return -1;
+ if(rowcount(c, ename))
+ return -1;
+ return 0;
+}
+
+void
+struncate(char *s, long *len)
+{
+ long i;
+ for (i=0; i<*len; i++)
+ if (s[i] == 0) {
+ *len = i;
+ return;
+ }
+}
+
+void
+fix_delim(char *p, int len, char delim)
+{
+ int i;
+ for (i=0; i<len; i++)
+ if (p[i] == delim)
+ p[i] = '\\';
+}
+
+long
+odbcdataread(Conv *c, void *a, long n, ulong offset, char *ename)
+{
+ Stmt *s = &c->s;
+ int i, r;
+ long left;
+ char *p, *lastp;
+
+ if(c->c.connected == 0){
+ strcpy(ename, Enoconnect);
+ return -1;
+ }
+ if (s->cols == 0 || s->rec == 0)
+ return 0;
+ p = a;
+ left = n;
+ if (c->headings) {
+ r = strlen(s->headstr);
+ if (r && offset < r) {
+ memcpy(p, s->headstr+offset, r-offset);
+ p += r-offset;
+ left -= r-offset;
+ return n-left;
+ }
+ }
+ if((r=odbcfetch(c, ename)) < 0)
+ return -1;
+ if(r == 0)
+ return 0;
+ for(i=0; i<s->ncols; i++){
+ Coltype *t = &s->cols[i];
+ Column *d = &s->rec[i];
+ if (ODebug)
+ fprint(2, "Col %d Returned data len=%d\n", i, d->len);
+ if(d->len <= 0) /* SQL_NULL_DATA or strange error! */
+ d->len = 0;
+ if (d->len > t->size+1)
+ d->len = t->size+1;
+ if(left <= d->len+1) /* whole fields */
+ break;
+ struncate(d->data, &d->len); /* assume string data and stop on an embedded null */
+ memcpy(p, d->data, d->len);
+ lastp = p;
+ left -= d->len;
+ p += d->len;
+ fix_delim(lastp, d->len, '\n');
+ switch(c->out.style){
+ case Float:
+ fix_delim(lastp, d->len, c->out.fs);
+ *p++ = (i==s->ncols-1)? c->out.rs: c->out.fs;
+ left--;
+ break;
+ case Fixed:
+ r = t->size - d->len;
+ if(r < 0)
+ r = 0;
+ if(left < r)
+ r = left;
+ memset(p, ' ', r);
+ left -= r;
+ p += r;
+ break;
+ }
+ }
+ if (left < 0)
+ fprint(2, "*** left<0 n=%d left=%d\n", n, left);
+ return n-left;
+}
+
+/*
+ * Returns a description of the format of a fixed width output
+ * record. `start' is the offset of the first character in the field.
+ * `end' is one greater than the offset of the last character of the field.
+ * `name' is the column name (which may contain spaces).
+ * `start' and `end' are terminated with a space, `name' with a newline.
+ * return 1 record containing one line for each field in the output:
+ * start1 end1 name1\n
+ * start2 end2 name2\n
+ * ....
+ */
+long
+odbcfmtread(Conv *c, void *a, long n, ulong offset, char *ename)
+{
+ Stmt *s = &c->s;
+ int i, len;
+ long left, off;
+ char *p;
+ char buf[100];
+
+ if(offset > 0)
+ return 0;
+ p = a;
+ left = n;
+ off = 0;
+ for(i=0; i<s->ncols; i++){
+ Coltype *t = &s->cols[i];
+
+ len = snprint(buf, sizeof(buf), "%ld %ld %s\n", off, off+t->size, t->name);
+ off += t->size;
+ if(left < len)
+ break;
+ memcpy(p, buf, len);
+ left -= len;
+ p += len;
+
+ }
+ return n-left;
+}
+
+int
+odbctables(Conv *c, char *ename)
+{
+ int rv;
+
+ if(c->c.connected == 0){
+ strcpy(ename, Enoconnect);
+ return -1;
+ }
+ rv = SQLCloseCursor(c->s.h);
+ rv = SQLTables(c->s.h, 0, 0, 0, 0, 0, 0, 0, 0);
+ if (sqlerr(c, rv, ename, "odbctables", "SQLTables"))
+ return -1;
+ if(odbcresults(c, ename))
+ return -1;
+ return 0;
+}
+
+int
+odbccolumns(Conv *c, char *table, char *ename)
+{
+ int rv;
+
+ if(c->c.connected == 0){
+ strcpy(ename, Enoconnect);
+ return -1;
+ }
+ rv = SQLCloseCursor(c->s.h);
+ rv = SQLColumns(c->s.h, 0, 0, 0, 0, table, strlen(table), 0, 0);
+ if (sqlerr(c, rv, ename, "odbccolumns", "SQLColumns"))
+ return -1;
+ if(odbcresults(c, ename))
+ return -1;
+ return 0;
+}
+
+int
+odbcexec(Conv *c, char *cmd, int cmdlen, char *ename)
+{
+ int rv;
+
+ if(c->c.connected == 0){
+ strcpy(ename, Enoconnect);
+ return -1;
+ }
+ SQLCloseCursor(c->s.h);
+ rv = SQLExecDirect(c->s.h, cmd, cmdlen);
+ if (sqlerr(c, rv, ename, "odbcexec", "SQLExecDirect"))
+ return -1;
+ if(odbcresults(c, ename))
+ return -1;
+ return 0;
+}
+
+int
+odbctrans(Conv *c, char *cmd, char *ename)
+{
+ int rv;
+
+ if(strcmp(cmd, "auto") == 0){
+ rv = SQLSetConnectAttr(c->c.h, SQL_ATTR_AUTOCOMMIT, (char*)SQL_AUTOCOMMIT_ON, 0);
+ } else if(strcmp(cmd, "begin") == 0){
+ rv = SQLSetConnectAttr(c->c.h, SQL_ATTR_AUTOCOMMIT, (char*)SQL_AUTOCOMMIT_OFF, 0);
+ } else if(strcmp(cmd, "commit") == 0){
+ rv = SQLEndTran(SQL_HANDLE_DBC, c->c.h, SQL_COMMIT);
+ } else if(strcmp(cmd, "rollback") == 0){
+ rv = SQLEndTran(SQL_HANDLE_DBC, c->c.h, SQL_ROLLBACK);
+ } else {
+ strcpy(ename, Ebadarg);
+ return -1;
+ }
+ if (sqlerr(c, rv, ename, "odbctrans", "SQLSetConnectAttr/SQLEndTran"))
+ return -1;
+ return 0;
+}
+
+int
+readstr(ulong off, char *buf, ulong n, char *str)
+{
+ int size;
+
+ size = strlen(str);
+ if(off >= size)
+ return 0;
+ if(off+n > size)
+ n = size-off;
+ memmove(buf, str+off, n);
+ return n;
+}
+
+static void
+newproto(char *name, int maxconv)
+{
+ int l;
+ Proto *p;
+
+ if(np >= MAXPROTO) {
+ print("no %s: increase MAXPROTO", name);
+ return;
+ }
+
+ p = &proto[np];
+ p->name = strdup(name);
+ p->qid.path = QID(np, 0, Qprotodir);
+ p->qid.type = QTDIR;
+ p->x = np++;
+ p->maxconv = maxconv;
+ l = sizeof(Conv*)*(p->maxconv+1);
+ p->conv = xmalloc(l);
+ if(p->conv == 0)
+ fatal("no memory");
+ memset(p->conv, 0, l);
+}
+
+char*
+openmode(int *o)
+{
+ if(*o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)){
+ return Ebadarg;
+ }
+ *o &= ~(OTRUNC|OCEXEC|ORCLOSE);
+ if(*o > OEXEC){
+ return Ebadarg;
+ }
+ if(*o == OEXEC)
+ *o = OREAD;
+ return nil;
+}
+
+static Conv*
+protoclone(Proto *p, char *user)
+{
+ Conv *c, **pp, **ep;
+ uvlong nr;
+ char buf[16];
+
+ c = 0;
+ ep = &p->conv[p->maxconv];
+ for(pp = p->conv; pp < ep; pp++) {
+ c = *pp;
+ if(c == 0) {
+ c = xmalloc(sizeof(Conv));
+ if(c == 0)
+ return 0;
+ c->ref = 1;
+ c->p = p;
+ c->x = pp - p->conv;
+ p->nc++;
+ *pp = c;
+ break;
+ }
+ if(c->ref == 0) {
+ c->ref++;
+ break;
+ }
+ }
+ if(pp >= ep)
+ return 0;
+
+ c->owner = strdup(user);
+ c->perm = 0660;
+ c->state = "Open";
+ c->out = defoutput;
+ c->headings = 0;
+ c->errmsg[0] = 0;
+
+ nr = QID(0, c->x, Qconvdir);
+ snprint(buf, sizeof(buf), "%d", c->x);
+ styxadddir(iserver, Qprotodir, nr, buf, 0555, c->owner);
+ styxaddfile(iserver, nr, QID(0, c->x, Qcmd), "cmd", c->perm, c->owner);
+ styxaddfile(iserver, nr, QID(0, c->x, Qctl), "ctl", c->perm, c->owner);
+ styxaddfile(iserver, nr, QID(0, c->x, Qdata), "data", c->perm, c->owner);
+ styxaddfile(iserver, nr, QID(0, c->x, Qerror), "error", c->perm, c->owner);
+ styxaddfile(iserver, nr, QID(0, c->x, Qformat), "format", c->perm, c->owner);
+ styxaddfile(iserver, nr, QID(0, c->x, Qsources), "sources", c->perm, c->owner);
+ styxaddfile(iserver, nr, QID(0, c->x, Qstatus), "status", 0444, c->owner);
+
+ return c;
+}
+
+char*
+dbopen(Qid *qid, int omode)
+{
+ Proto *p;
+ int perm;
+ Conv *cv;
+ char *user;
+ Qid q;
+ Client *c;
+
+ q = *qid;
+ c = styxclient(iserver);
+
+ perm = 0;
+ omode &= 3;
+ switch(omode) {
+ case OREAD:
+ perm = 4;
+ break;
+ case OWRITE:
+ perm = 2;
+ break;
+ case ORDWR:
+ perm = 6;
+ break;
+ }
+
+ switch(TYPE(q)) {
+ default:
+ break;
+ case Qtopdir:
+ case Qprotodir:
+ case Qconvdir:
+ case Qstatus:
+ case Qformat:
+ case Qsources:
+ case Qerror:
+ if(omode != OREAD){
+ return Eperm;
+ }
+ break;
+ case Qclonus:
+ p = &proto[PROTO(q)];
+ cv = protoclone(p, c->uname);
+ if(cv == 0){
+ return Enodev;
+ }
+ qid->path = QID(p->x, cv->x, Qctl);
+ qid->type = 0;
+ qid->vers = 0;
+ if(odbcnewconv(c, cv) != 0){
+ return Eodbcalloc;
+ }
+ break;
+ case Qdata:
+ case Qcmd:
+ case Qctl:
+ p = &proto[PROTO(q)];
+ cv = p->conv[CONV(q)];
+ user = c->uname;
+ if((perm & (cv->perm>>6)) != perm) {
+ if(strcmp(user, cv->owner) != 0 ||
+ (perm & cv->perm) != perm) {
+ return Eperm;
+ }
+ }
+ cv->ref++;
+ if(cv->ref == 1) {
+ cv->state = "Open";
+ cv->owner = strdup(user);
+ cv->perm = 0660;
+ if(odbcnewconv(c, cv) != 0){
+ return Eodbcalloc;
+ }
+ }
+ break;
+ }
+ return openmode(&omode);
+}
+
+char*
+dbclose(Qid qid, int mode)
+{
+ Conv *cc;
+
+ USED(mode);
+ switch(TYPE(qid)) {
+ case Qctl:
+ case Qcmd:
+ case Qdata:
+ cc = proto[PROTO(qid)].conv[CONV(qid)];
+ if(--cc->ref != 0)
+ break;
+ cc->owner = inferno;
+ cc->perm = 0666;
+ cc->state = "Closed";
+ odbcfreeconv(cc);
+ styxrmfile(iserver, QID(0, cc->x, Qconvdir));
+ break;
+ }
+ return nil;
+}
+
+static char ebuf[ERRMAX];
+
+char*
+dbread(Qid qid, char *ba, ulong *n, vlong offset)
+{
+ uchar *a = ba;
+ Conv *c;
+ Proto *x;
+ char buf[128], *p, *s;
+ long r;
+ ulong m;
+
+ m = *n;
+ ebuf[0] = 0;
+ p = a;
+ switch(TYPE(qid)) {
+ default:
+ return Eperm;
+ case Qnclients:
+ snprint(buf, sizeof(buf), "%d\n", nclients);
+ *n = readstr(offset, p, m, buf);
+ return nil;
+ case Qprotodir:
+ case Qtopdir:
+ case Qconvdir:
+ return "bad read of directory";
+ case Qctl:
+ snprint(buf, sizeof(buf), "%ld", CONV(qid));
+ *n = readstr(offset, p, m, buf);
+ return nil;
+ case Qstatus:
+ x = &proto[PROTO(qid)];
+ c = x->conv[CONV(qid)];
+ snprint(buf, sizeof(buf), "%s/%d %ld %s %s\n",
+ c->p->name, c->x, c->ref, c->state, "");
+ *n = readstr(offset, p, m, buf);
+ return nil;
+ case Qdata:
+ c = proto[PROTO(qid)].conv[CONV(qid)];
+ *n = odbcdataread(c, a, m, offset, ebuf);
+ if(ebuf[0] != 0)
+ return ebuf;
+ return nil;
+ case Qformat:
+ c = proto[PROTO(qid)].conv[CONV(qid)];
+ *n = odbcfmtread(c, a, m, offset, ebuf);
+ if(ebuf[0] != 0)
+ return ebuf;
+ return nil;
+ case Qerror:
+ c = proto[PROTO(qid)].conv[CONV(qid)];
+ *n = readstr(offset, p, m, odbcerror(c, 1));
+ return nil;
+ case Qsources:
+ c = proto[PROTO(qid)].conv[CONV(qid)];
+ s = odbcsources(styxclient(iserver));
+ r = readstr(offset, p, m, s);
+ free(s);
+ *n = r;
+ return nil;
+ }
+ return nil;
+}
+
+char*
+dbwrite(Qid qid, char *ba, ulong *n, vlong offset)
+{
+ uchar *a = ba;
+ int nf;
+ Conv *c;
+ Proto *x;
+ char *fields[10], buf[512], safebuf[512];
+ ulong m;
+
+ m = *n;
+ ebuf[0] = 0;
+ switch(TYPE(qid)) {
+ default:
+ return Eperm;
+ case Qctl:
+ x = &proto[PROTO(qid)];
+ c = x->conv[CONV(qid)];
+ //
+ if(m > sizeof(buf)-1)
+ m = sizeof(buf)-1;
+ memmove(buf, a, m);
+ buf[m] = '\0';
+ if (ODebug)
+ fprint(2, "write Qctl: <%s>\n", buf);
+ fields[0] = 0;
+ nf = parsefields(buf, fields, sizeof(fields)/sizeof(*fields), " \n\t");
+ if (nf == 0) {
+ return Ebadarg;
+ }
+ if(strcmp(fields[0], "connect") == 0){ /* connect database [user!auth] */
+ char *afields[2];
+ char *user = "";
+ char *auth = "";
+ switch(nf){
+ default:
+ return Ebadarg;
+ case 2:
+ break;
+ case 3:
+ nf = parsefields(fields[2], afields, 2, "!");
+ switch(nf){
+ case 2:
+ user = afields[0];
+ auth = afields[1];
+ break;
+ case 1:
+ if(fields[2][0] == 0)
+ auth = afields[0];
+ else
+ user = afields[0];
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ if(odbcconnect(c, fields[1], user, auth, ebuf) < 0)
+ return ebuf;
+ c->state = "Connected";
+ } else if(strcmp(fields[0], "disconnect") == 0){
+ odbcdisconnect(c);
+ c->state = "Disconnected";
+ } else if(strcmp(fields[0], "fixed") == 0){
+ c->out = defoutput;
+ c->out.style = Fixed;
+ } else if(strcmp(fields[0], "float") == 0){
+ c->out = defoutput;
+ c->out.style = Float;
+ if(nf > 1)
+ c->out.fs = fields[1][0];
+ if(nf > 2)
+ c->out.rs = fields[2][0];
+ } else if(strcmp(fields[0], "headings") == 0){
+ c->headings = 1;
+ } else if(strcmp(fields[0], "noheadings") == 0){
+ c->headings = 0;
+ } else if(strcmp(fields[0], "trans") == 0){ /* begin, auto, commit, rollback */
+ if(nf < 2){
+ return Ebadarg;
+ }
+ if(odbctrans(c, fields[1], ebuf) < 0)
+ return ebuf;
+ } else {
+ return Ebadcmd;
+ }
+ *n = m;
+ return nil;
+ case Qcmd:
+ x = &proto[PROTO(qid)];
+ c = x->conv[CONV(qid)];
+ if(m > sizeof(buf)-1)
+ m = sizeof(buf)-1;
+ memmove(buf, a, m);
+ buf[m] = '\0';
+ if (ODebug)
+ fprint(2, "write Qcmd: <%s>\n", buf);
+ memmove(safebuf, a, m);
+ safebuf[m] = '\0';
+ fields[0] = 0;
+ nf = parsefields(buf, fields, 3, " \n\t");
+ if (nf == 0) {
+ return Ebadarg;
+ }
+ if(strcmp(fields[0], "tables") == 0){
+ if(odbctables(c, ebuf))
+ return ebuf;
+ }else if(strcmp(fields[0], "columns") == 0){
+ if(nf < 2){
+ return Ebadarg;
+ }
+ if(odbccolumns(c, &safebuf[strlen(fields[0])+1], ebuf)) /* allow for spaces in table name */
+ return ebuf;
+ } else
+ if (odbcexec(c, a, m, ebuf))
+ return ebuf;
+ *n = m;
+ return nil;
+ case Qdata:
+ return Eperm;
+ }
+ return nil;
+}
+
+void
+badusage(void)
+{
+ fprint(2, "Usage: odbc [-d] [-p port]\n");
+ exit(1);
+}
+
+Styxops ops = {
+ odbcnewclient, /* newclient */
+ odbcfreeclient, /* freeclient */
+
+ nil, /* attach */
+ nil, /* walk */
+ dbopen, /* open */
+ nil, /* create */
+ dbread, /* read */
+ dbwrite, /* write */
+ dbclose, /* close */
+ nil, /* remove */
+ nil, /* stat */
+ nil, /* wstat */
+};
+
+void
+main(int argc, char *argv[])
+{
+ Styxserver s;
+
+ ARGBEGIN {
+ default:
+ badusage();
+ case 'd': /* Debug */
+ ODebug = 1;
+ styxdebug();
+ break;
+ case 'p': /* Debug */
+ netport = EARGF(badusage());
+ break;
+ } ARGEND
+
+ iserver = &s;
+ styxinit(&s, &ops, netport, -1, 1);
+ styxaddfile(&s, Qroot, Qnclients, "nclients", 0444, inferno);
+ styxadddir(&s, Qroot, Qprotodir, "db", 0555, inferno);
+ styxaddfile(&s, Qprotodir, Qclonus, "new", 0666, inferno);
+ newproto("db", 100);
+ for (;;) {
+ styxwait(&s);
+ styxprocess(&s);
+ }
+ styxend(&s);
+}
--- /dev/null
+++ b/tools/styxtest/mkfile
@@ -1,0 +1,23 @@
+<../../mkconfig
+
+TARG=styxtest
+
+OFILES=\
+ styxtest.$O\
+
+HFILES=\
+ ../libstyx/styxserver.h\
+
+LIBS=styx 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<mkfile-$SYSTARG
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS= $CFLAGS -I../libstyx
+
+
+
+
--- /dev/null
+++ b/tools/styxtest/mkfile-Nt
@@ -1,0 +1,1 @@
+SYSLIBS= wsock32.lib $SYSLIBS
--- /dev/null
+++ b/tools/styxtest/mkfile-Solaris
@@ -1,0 +1,1 @@
+SYSLIBS=$EMULIBS
--- /dev/null
+++ b/tools/styxtest/styxtest.c
@@ -1,0 +1,198 @@
+#include <lib9.h>
+#include "styxserver.h"
+
+/*
+ * An in-memory file server
+ * allowing truncation, removal on closure, wstat and
+ * all other file operations
+ */
+
+char *fsremove(Qid);
+
+Styxserver *server;
+
+char*
+fsopen(Qid *qid, int mode)
+{
+ Styxfile *f;
+
+ f = styxfindfile(server, qid->path);
+ if(mode&OTRUNC){ /* truncate on open */
+ styxfree(f->u);
+ f->u = nil;
+ f->d.length = 0;
+ }
+ return nil;
+}
+
+char*
+fsclose(Qid qid, int mode)
+{
+ if(mode&ORCLOSE) /* remove on close */
+ return fsremove(qid);
+ return nil;
+}
+
+char *
+fscreate(Qid *qid, char *name, int perm, int mode)
+{
+ int isdir;
+ Styxfile *f;
+
+ USED(mode);
+ isdir = perm&DMDIR;
+ if(isdir)
+ f = styxadddir(server, qid->path, -1, name, perm, "inferno");
+ else
+ f = styxaddfile(server, qid->path, -1, name, perm, "inferno");
+ if(f == nil)
+ return Eexist;
+ f->u = nil;
+ f->d.length = 0;
+ *qid = f->d.qid;
+ return nil;
+}
+
+char *
+fsremove(Qid qid)
+{
+ Styxfile *f;
+
+ f = styxfindfile(server, qid.path);
+ if((f->d.qid.type&QTDIR) && f->child != nil)
+ return "directory not empty";
+ styxfree(f->u);
+ styxrmfile(server, qid.path);
+ return nil;
+}
+
+char *
+fsread(Qid qid, char *buf, ulong *n, vlong off)
+{
+ int m;
+ Styxfile *f;
+
+ f = styxfindfile(server, qid.path);
+ m = f->d.length;
+ if(off >= m)
+ *n = 0;
+ else{
+ if(off + *n > m)
+ *n = m-off;
+ memmove(buf, (char*)f->u+off, *n);
+ }
+ return nil;
+}
+
+char*
+fswrite(Qid qid, char *buf, ulong *n, vlong off)
+{
+ Styxfile *f;
+ vlong m, p;
+ char *u;
+
+ f = styxfindfile(server, qid.path);
+ m = f->d.length;
+ p = off + *n;
+ if(p > m){ /* just grab a larger piece of memory */
+ u = styxmalloc(p);
+ if(u == nil)
+ return "out of memory";
+ memset(u, 0, p);
+ memmove(u, f->u, m);
+ styxfree(f->u);
+ f->u = u;
+ f->d.length = p;
+ }
+ memmove((char*)f->u+off, buf, *n);
+ return nil;
+}
+
+char*
+fswstat(Qid qid, Dir *d)
+{
+ Styxfile *f, *tf;
+ Client *c;
+ int owner;
+
+ /* the most complicated operation when fully allowed */
+
+ c = styxclient(server);
+ f = styxfindfile(server, qid.path);
+ owner = strcmp(c->uname, f->d.uid) == 0;
+ if(d->name != nil && strcmp(d->name, f->d.name) != 0){
+ /* need write permission in parent directory */
+ if(!styxperm(f->parent, c->uname, OWRITE))
+ return Eperm;
+ if((tf = styxaddfile(server, f->parent->d.qid.path, -1, d->name, 0, "")) == nil){
+ /* file with same name exists */
+ return Eexist;
+ }
+ else{
+ /* undo above addfile */
+ styxrmfile(server, tf->d.qid.path);
+ }
+ /* ok to change name now */
+ styxfree(f->d.name);
+ f->d.name = strdup(d->name);
+ }
+ if(d->uid != nil && strcmp(d->uid, f->d.uid) != 0){
+ if(!owner)
+ return Eperm;
+ styxfree(f->d.uid);
+ f->d.uid = strdup(d->uid);
+ }
+ if(d->gid != nil && strcmp(d->gid, f->d.gid) != 0){
+ if(!owner)
+ return Eperm;
+ styxfree(f->d.gid);
+ f->d.gid = strdup(d->gid);
+ }
+ if(d->mode != ~0 && d->mode != f->d.mode){
+ if(!owner)
+ return Eperm;
+ if((d->mode&DMDIR) != (f->d.mode&DMDIR))
+ return Eperm; /* cannot change file->directory or vice-verse */
+ f->d.mode = d->mode;
+ }
+ if(d->mtime != ~0 && d->mtime != f->d.mtime){
+ if(!owner)
+ return Eperm;
+ f->d.mtime = d->mtime;
+ }
+ /* all other file attributes cannot be changed by wstat */
+ return nil;
+}
+
+Styxops ops = {
+ nil, /* newclient */
+ nil, /* freeclient */
+
+ nil, /* attach */
+ nil, /* walk */
+ fsopen, /* open */
+ fscreate, /* create */
+ fsread, /* read */
+ fswrite, /* write */
+ fsclose, /* close */
+ fsremove, /* remove */
+ nil, /* stat */
+ fswstat, /* wstat */
+};
+
+void
+main(int argc, char **argv)
+{
+ Styxserver s;
+
+ USED(argc);
+ USED(argv);
+ server = &s;
+ styxdebug();
+ styxinit(&s, &ops, "6701", 0777, 1);
+ for(;;){
+ styxwait(&s);
+ styxprocess(&s);
+ }
+ exits(nil);
+}
--- /dev/null
+++ b/tools/styxtest/styxtest0.c
@@ -1,0 +1,95 @@
+#include <lib9.h>
+#include "styxserver.h"
+
+int nq;
+Styxserver *server;
+
+void
+myinit(Styxserver *s)
+{
+ styxaddfile(s, Qroot, 1, "fred", 0664, "inferno");
+ styxaddfile(s, Qroot, 2, "joe", 0664, "inferno");
+ styxadddir(s, Qroot, 3, "adir", 0775, "inferno");
+ styxaddfile(s, 3, 4, "bill", 0664, "inferno");
+ styxadddir(s, Qroot, 5, "new", 0775, "inferno");
+ styxadddir(s, 5, 6, "cdir", 0775, "inferno");
+ styxaddfile(s, 6, 7, "cfile", 0664, "inferno");
+ nq = 8;
+}
+
+char *
+mycreate(Qid *qid, char *name, int perm, int mode)
+{
+ int isdir;
+ Styxfile *f;
+
+ USED(mode);
+ isdir = perm&DMDIR;
+ if(isdir)
+ f = styxadddir(server, qid->path, nq++, name , perm, "inferno");
+ else
+ f = styxaddfile(server, qid->path, nq++, name, perm, "inferno");
+ if(f == nil)
+ return Eexist;
+ *qid = f->d.qid;
+ return nil;
+}
+
+char *
+myremove(Qid qid)
+{
+ Styxfile *f;
+
+ f = styxfindfile(server, qid.path);
+ if(f != nil && (f->d.qid.type&QTDIR) && f->child != nil)
+ return "directory not empty";
+
+ if(styxrmfile(server, qid.path) < 0)
+ return Enonexist;
+ return nil;
+}
+
+char *
+myread(Qid qid, char *d, ulong *n, vlong offset)
+{
+ if(qid.path != 1){
+ *n = 0;
+ return nil;
+ }
+ *n = styxreadstr(offset, d, *n, "abcdefghijklmn");
+ return nil;
+}
+
+Styxops ops = {
+ nil, /* newclient */
+ nil, /* freeclient */
+
+ nil, /* attach */
+ nil, /* walk */
+ nil, /* open */
+ mycreate, /* create */
+ myread, /* read */
+ nil, /* write */
+ nil, /* close */
+ myremove, /* remove */
+ nil, /* stat */
+ nil, /* wstat */
+};
+
+main(int argc, char **argv)
+{
+ Styxserver s;
+
+ USED(argc);
+ USED(argv);
+ server = &s;
+ styxdebug();
+ styxinit(&s, &ops, "6701", 0555, 0);
+ myinit(&s);
+ for(;;){
+ styxwait(&s);
+ styxprocess(&s);
+ }
+ return 0;
+}
+
--- /dev/null
+++ b/utils/0a/a.h
@@ -1,0 +1,183 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../vc/v.out.h"
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+
+#define MAXALIGN 7
+
+typedef struct Sym Sym;
+typedef struct Gen Gen;
+typedef struct Io Io;
+typedef struct Hist Hist;
+
+#define FPCHIP 1
+#define NSYMB 8192
+#define BUFSIZ 8192
+#define HISTSZ 20
+#define NINCLUDE 10
+#define NHUNK 10000
+#define EOF (-1)
+#define IGN (-2)
+#define GETC() ((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define NHASH 503
+#define STRINGSZ 200
+#define NMACRO 10
+
+struct Sym
+{
+ Sym* link;
+ char* macro;
+ long value;
+ ushort type;
+ char *name;
+ char sym;
+};
+#define S ((Sym*)0)
+
+EXTERN struct
+{
+ char* p;
+ int c;
+} fi;
+
+struct Io
+{
+ Io* link;
+ char b[BUFSIZ];
+ char* p;
+ short c;
+ short f;
+};
+#define I ((Io*)0)
+
+EXTERN struct
+{
+ Sym* sym;
+ short type;
+} h[NSYM];
+
+struct Gen
+{
+ Sym* sym;
+ long offset;
+ short type;
+ short reg;
+ short name;
+ double dval;
+ char sval[8];
+};
+
+struct Hist
+{
+ Hist* link;
+ char* name;
+ long line;
+ long offset;
+};
+#define H ((Hist*)0)
+
+enum /* keep in synch with ../cc/cc.h */
+{
+ Plan9 = 1<<0,
+ Unix = 1<<1,
+ Windows = 1<<2,
+};
+
+enum
+{
+ CLAST,
+ CMACARG,
+ CMACRO,
+ CPREPROC,
+};
+
+EXTERN char debug[256];
+EXTERN Sym* hash[NHASH];
+EXTERN char* Dlist[30];
+EXTERN int nDlist;
+EXTERN Hist* ehist;
+EXTERN int newflag;
+EXTERN Hist* hist;
+EXTERN char* hunk;
+EXTERN char* include[NINCLUDE];
+EXTERN Io* iofree;
+EXTERN Io* ionext;
+EXTERN Io* iostack;
+EXTERN long lineno;
+EXTERN int nerrors;
+EXTERN long nhunk;
+EXTERN int nosched;
+EXTERN int ninclude;
+EXTERN Gen nullgen;
+EXTERN char* outfile;
+EXTERN int pass;
+EXTERN char* pathname;
+EXTERN long pc;
+EXTERN int peekc;
+EXTERN int sym;
+EXTERN char symb[NSYMB];
+EXTERN int thechar;
+EXTERN char* thestring;
+EXTERN long thunk;
+EXTERN Biobuf obuf;
+
+int assemble(char*);
+void* alloc(long);
+void* allocn(void*, long, long);
+void errorexit(void);
+void pushio(void);
+void newio(void);
+void newfile(char*, int);
+Sym* slookup(char*);
+Sym* lookup(void);
+void syminit(Sym*);
+long yylex(void);
+int getc(void);
+int getnsc(void);
+void unget(int);
+int escchar(int);
+void cinit(void);
+void pinit(char*);
+void cclean(void);
+int isreg(Gen*);
+void outcode(int, Gen*, int, Gen*);
+void zname(char*, int, int);
+void zaddr(Gen*, int);
+void ieeedtod(Ieee*, double);
+int filbuf(void);
+Sym* getsym(void);
+void domacro(void);
+void macund(void);
+void macdef(void);
+void macexpand(Sym*, char*);
+void macinc(void);
+void maclin(void);
+void macprag(void);
+void macif(int);
+void macend(void);
+void outhist(void);
+void dodefine(char*);
+void prfile(long);
+void linehist(char*, int);
+void gethunk(void);
+void yyerror(char*, ...);
+int yyparse(void);
+void setinclude(char*);
+
+/*
+ * Posix.c/Inferno.c/Nt.c
+ */
+int mywait(int*);
+int mycreat(char*, int);
+int systemtype(int);
+int pathchar(void);
+char* mygetwd(char*, int);
+int myexec(char*, char*[]);
+int mydup(int, int);
+int myfork(void);
+int mypipe(int*);
+void* mysbrk(ulong);
--- /dev/null
+++ b/utils/0a/a.y
@@ -1,0 +1,588 @@
+%{
+#include "a.h"
+%}
+%union
+{
+ Sym *sym;
+ long lval;
+ double dval;
+ char sval[8];
+ Gen gen;
+}
+%left '|'
+%left '^'
+%left '&'
+%left '<' '>'
+%left '+' '-'
+%left '*' '/' '%'
+%token <lval> LTYPE1 LTYPE2 LTYPE3 LTYPE4 LTYPE5
+%token <lval> LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA
+%token <lval> LTYPEB LTYPEC LTYPED LTYPEE LTYPEF
+%token <lval> LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK
+%token <lval> LCONST LSP LSB LFP LPC LHI LLO LMREG
+%token <lval> LTYPEX LREG LFREG LFCREG LR LM LF
+%token <lval> LFCR LSCHED
+%token <dval> LFCONST
+%token <sval> LSCONST
+%token <sym> LNAME LLAB LVAR
+%type <lval> con expr pointer offset sreg
+%type <gen> gen vgen lgen vlgen rel reg freg mreg fcreg
+%type <gen> imm ximm ireg name oreg imr nireg fgen
+%%
+prog:
+| prog line
+
+line:
+ LLAB ':'
+ {
+ if($1->value != pc)
+ yyerror("redeclaration of %s", $1->name);
+ $1->value = pc;
+ }
+ line
+| LNAME ':'
+ {
+ $1->type = LLAB;
+ $1->value = pc;
+ }
+ line
+| LNAME '=' expr ';'
+ {
+ $1->type = LVAR;
+ $1->value = $3;
+ }
+| LVAR '=' expr ';'
+ {
+ if($1->value != $3)
+ yyerror("redeclaration of %s", $1->name);
+ $1->value = $3;
+ }
+| LSCHED ';'
+ {
+ nosched = $1;
+ }
+| ';'
+| inst ';'
+| error ';'
+
+inst:
+/*
+ * Immed-type
+ */
+ LTYPE1 imr ',' sreg ',' reg
+ {
+ outcode($1, &$2, $4, &$6);
+ }
+| LTYPE1 imr ',' reg
+ {
+ outcode($1, &$2, NREG, &$4);
+ }
+/*
+ * NOR
+ */
+| LTYPE2 imr ',' sreg ',' imr
+ {
+ outcode($1, &$2, $4, &$6);
+ }
+| LTYPE2 imr ',' imr
+ {
+ outcode($1, &$2, NREG, &$4);
+ }
+/*
+ * LOAD/STORE, but not MOVW
+ */
+| LTYPE3 lgen ',' gen
+ {
+ if(!isreg(&$2) && !isreg(&$4))
+ print("one side must be register\n");
+ outcode($1, &$2, NREG, &$4);
+ }
+/*
+ * SPECIAL
+ */
+| LTYPE4 comma
+ {
+ outcode($1, &nullgen, NREG, &nullgen);
+ }
+/*
+ * MOVW
+ */
+| LTYPE5 vlgen ',' vgen
+ {
+ if(!isreg(&$2) && !isreg(&$4))
+ print("one side must be register\n");
+ outcode($1, &$2, NREG, &$4);
+ }
+/*
+ * MUL/DIV
+ */
+| LTYPE6 reg ',' sreg comma
+ {
+ outcode($1, &$2, $4, &nullgen);
+ }
+| LTYPE6 reg ',' sreg ',' reg
+ {
+ outcode($1, &$2, $4, &$6);
+ }
+/*
+ * JMP/JAL
+ */
+| LTYPE7 comma rel
+ {
+ outcode($1, &nullgen, NREG, &$3);
+ }
+| LTYPE7 comma nireg
+ {
+ outcode($1, &nullgen, NREG, &$3);
+ }
+| LTYPE8 comma rel
+ {
+ outcode($1, &nullgen, NREG, &$3);
+ }
+| LTYPE8 comma nireg
+ {
+ outcode($1, &nullgen, NREG, &$3);
+ }
+| LTYPE8 sreg ',' nireg
+ {
+ outcode($1, &nullgen, $2, &$4);
+ }
+/*
+ * BEQ/BNE
+ */
+| LTYPE9 gen ',' rel
+ {
+ if(!isreg(&$2))
+ print("left side must be register\n");
+ outcode($1, &$2, NREG, &$4);
+ }
+| LTYPE9 gen ',' sreg ',' rel
+ {
+ if(!isreg(&$2))
+ print("left side must be register\n");
+ outcode($1, &$2, $4, &$6);
+ }
+/*
+ * B-other
+ */
+| LTYPEA gen ',' rel
+ {
+ if(!isreg(&$2))
+ print("left side must be register\n");
+ outcode($1, &$2, NREG, &$4);
+ }
+/*
+ * TEXT/GLOBL
+ */
+| LTYPEB name ',' imm
+ {
+ outcode($1, &$2, NREG, &$4);
+ }
+| LTYPEB name ',' con ',' imm
+ {
+ outcode($1, &$2, $4, &$6);
+ }
+/*
+ * DATA
+ */
+| LTYPEC name '/' con ',' ximm
+ {
+ outcode($1, &$2, $4, &$6);
+ }
+/*
+ * floating-type
+ */
+| LTYPED freg ',' freg
+ {
+ outcode($1, &$2, NREG, &$4);
+ }
+| LTYPEE freg ',' freg
+ {
+ outcode($1, &$2, NREG, &$4);
+ }
+| LTYPEE freg ',' LFREG ',' freg
+ {
+ outcode($1, &$2, $4, &$6);
+ }
+| LTYPEF freg ',' LFREG comma
+ {
+ outcode($1, &$2, $4, &nullgen);
+ }
+/*
+ * coprocessor branch
+ */
+| LTYPEG comma rel
+ {
+ outcode($1, &nullgen, NREG, &$3);
+ }
+/*
+ * word
+ */
+| LTYPEH comma ximm
+ {
+ outcode($1, &nullgen, NREG, &$3);
+ }
+/*
+ * NOP
+ */
+| LTYPEI comma
+ {
+ outcode($1, &nullgen, NREG, &nullgen);
+ }
+| LTYPEI ',' vgen
+ {
+ outcode($1, &nullgen, NREG, &$3);
+ }
+| LTYPEI vgen comma
+ {
+ outcode($1, &$2, NREG, &nullgen);
+ }
+/*
+ * BREAK -- overloaded with CACHE opcode
+ */
+| LTYPEJ comma
+ {
+ outcode($1, &nullgen, NREG, &nullgen);
+ }
+| LTYPEJ vgen ',' vgen
+ {
+ outcode($1, &$2, NREG, &$4);
+ }
+
+comma:
+| ','
+
+rel:
+ con '(' LPC ')'
+ {
+ $$ = nullgen;
+ $$.type = D_BRANCH;
+ $$.offset = $1 + pc;
+ }
+| LNAME offset
+ {
+ $$ = nullgen;
+ if(pass == 2)
+ yyerror("undefined label: %s", $1->name);
+ $$.type = D_BRANCH;
+ $$.sym = $1;
+ $$.offset = $2;
+ }
+| LLAB offset
+ {
+ $$ = nullgen;
+ $$.type = D_BRANCH;
+ $$.sym = $1;
+ $$.offset = $1->value + $2;
+ }
+
+vlgen:
+ lgen
+| fgen
+| mreg
+| fcreg
+| LHI
+ {
+ $$ = nullgen;
+ $$.type = D_HI;
+ }
+| LLO
+ {
+ $$ = nullgen;
+ $$.type = D_LO;
+ }
+
+vgen:
+ gen
+| fgen
+| mreg
+| fcreg
+| LHI
+ {
+ $$ = nullgen;
+ $$.type = D_HI;
+ }
+| LLO
+ {
+ $$ = nullgen;
+ $$.type = D_LO;
+ }
+
+lgen:
+ gen
+| ximm
+
+fgen:
+ freg
+
+mreg:
+ LMREG
+ {
+ $$ = nullgen;
+ $$.type = D_MREG;
+ $$.reg = $1;
+ }
+| LM '(' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_MREG;
+ $$.reg = $3;
+ }
+
+fcreg:
+ LFCREG
+ {
+ $$ = nullgen;
+ $$.type = D_FCREG;
+ $$.reg = $1;
+ }
+| LFCR '(' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_FCREG;
+ $$.reg = $3;
+ }
+
+freg:
+ LFREG
+ {
+ $$ = nullgen;
+ $$.type = D_FREG;
+ $$.reg = $1;
+ }
+| LF '(' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_FREG;
+ $$.reg = $3;
+ }
+
+ximm: '$' con
+ {
+ $$ = nullgen;
+ $$.type = D_CONST;
+ $$.offset = $2;
+ }
+| '$' oreg
+ {
+ $$ = $2;
+ $$.type = D_CONST;
+ }
+| '$' '*' '$' oreg
+ {
+ $$ = $4;
+ $$.type = D_OCONST;
+ }
+| '$' LSCONST
+ {
+ $$ = nullgen;
+ $$.type = D_SCONST;
+ memcpy($$.sval, $2, sizeof($$.sval));
+ }
+| '$' LFCONST
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = $2;
+ }
+| '$' '-' LFCONST
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = -$3;
+ }
+
+nireg:
+ ireg
+| con ireg
+ {
+ if($1 != 0)
+ yyerror("offset must be zero");
+ $$ = $2;
+ }
+| name
+ {
+ $$ = $1;
+ if($1.name != D_EXTERN && $1.name != D_STATIC) {
+ }
+ }
+
+ireg:
+ '(' sreg ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.reg = $2;
+ $$.offset = 0;
+ }
+
+gen:
+ reg
+| con
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.offset = $1;
+ }
+| oreg
+
+oreg:
+ name
+| name '(' sreg ')'
+ {
+ $$ = $1;
+ $$.type = D_OREG;
+ $$.reg = $3;
+ }
+| '(' sreg ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.reg = $2;
+ $$.offset = 0;
+ }
+| con '(' sreg ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.reg = $3;
+ $$.offset = $1;
+ }
+
+imr:
+ reg
+| imm
+
+imm: '$' con
+ {
+ $$ = nullgen;
+ $$.type = D_CONST;
+ $$.offset = $2;
+ }
+
+reg:
+ sreg
+ {
+ $$ = nullgen;
+ $$.type = D_REG;
+ $$.reg = $1;
+ }
+
+sreg:
+ LREG
+| LR '(' expr ')'
+ {
+ if($$ < 0 || $$ >= NREG)
+ print("register value out of range\n");
+ $$ = $3;
+ }
+
+name:
+ con '(' pointer ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.name = $3;
+ $$.sym = S;
+ $$.offset = $1;
+ }
+| LNAME offset '(' pointer ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.name = $4;
+ $$.sym = $1;
+ $$.offset = $2;
+ }
+| LNAME '<' '>' offset '(' LSB ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.name = D_STATIC;
+ $$.sym = $1;
+ $$.offset = $4;
+ }
+
+offset:
+ {
+ $$ = 0;
+ }
+| '+' con
+ {
+ $$ = $2;
+ }
+| '-' con
+ {
+ $$ = -$2;
+ }
+
+pointer:
+ LSB
+| LSP
+| LFP
+
+con:
+ LCONST
+| LVAR
+ {
+ $$ = $1->value;
+ }
+| '-' con
+ {
+ $$ = -$2;
+ }
+| '+' con
+ {
+ $$ = $2;
+ }
+| '~' con
+ {
+ $$ = ~$2;
+ }
+| '(' expr ')'
+ {
+ $$ = $2;
+ }
+
+expr:
+ con
+| expr '+' expr
+ {
+ $$ = $1 + $3;
+ }
+| expr '-' expr
+ {
+ $$ = $1 - $3;
+ }
+| expr '*' expr
+ {
+ $$ = $1 * $3;
+ }
+| expr '/' expr
+ {
+ $$ = $1 / $3;
+ }
+| expr '%' expr
+ {
+ $$ = $1 % $3;
+ }
+| expr '<' '<' expr
+ {
+ $$ = $1 << $4;
+ }
+| expr '>' '>' expr
+ {
+ $$ = $1 >> $4;
+ }
+| expr '&' expr
+ {
+ $$ = $1 & $3;
+ }
+| expr '^' expr
+ {
+ $$ = $1 ^ $3;
+ }
+| expr '|' expr
+ {
+ $$ = $1 | $3;
+ }
--- /dev/null
+++ b/utils/0a/l.s
@@ -1,0 +1,703 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define BI2BY 8 /* bits per byte */
+#define BI2WD 32 /* bits per word */
+#define BY2WD 4 /* bytes per word */
+#define BY2PG 4096 /* bytes per page */
+#define WD2PG (BY2PG/BY2WD) /* words per page */
+#define PGSHIFT 12 /* log(BY2PG) */
+
+#define MAXMACH 4 /* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define MS2HZ 50 /* millisec per clock tick */
+#define TK2SEC(t) ((t)/20) /* ticks to seconds */
+#define TK2MS(t) ((t)*MS2HZ) /* ticks to milliseconds */
+#define MS2TK(t) ((t)/MS2HZ) /* milliseconds to ticks */
+
+/*
+ * CP0 registers
+ */
+
+#define INDEX 0
+#define RANDOM 1
+#define TLBPHYS 2
+#define CONTEXT 4
+#define BADVADDR 8
+#define TLBVIRT 10
+#define STATUS 12
+#define CAUSE 13
+#define EPC 14
+#define PRID 15
+
+/*
+ * M(STATUS) bits
+ */
+#define IEC 0x00000001
+#define KUC 0x00000002
+#define IEP 0x00000004
+#define KUP 0x00000008
+#define INTMASK 0x0000ff00
+#define SW0 0x00000100
+#define SW1 0x00000200
+#define INTR0 0x00000400
+#define INTR1 0x00000800
+#define INTR2 0x00001000
+#define INTR3 0x00002000
+#define INTR4 0x00004000
+#define INTR5 0x00008000
+#define ISC 0x00010000
+#define SWC 0x00020000
+#define CU1 0x20000000
+
+/*
+ * Traps
+ */
+
+#define UTLBMISS (KSEG0+0x00)
+#define EXCEPTION (KSEG0+0x80)
+
+/*
+ * Magic registers
+ */
+
+#define MACH 25 /* R25 is m-> */
+#define USER 24 /* R24 is u-> */
+#define MPID 0xBF000000 /* long; low 3 bits identify mp bus slot */
+#define WBFLUSH 0xBC000000 /* D-CACHE data; used for write buffer flush */
+
+/*
+ * Fundamental addresses
+ */
+
+#define MACHADDR 0x80014000
+#define USERADDR 0xC0000000
+#define UREGADDR (USERADDR+BY2PG-4-0xA0)
+/*
+ * MMU
+ */
+
+#define KUSEG 0x00000000
+#define KSEG0 0x80000000
+#define KSEG1 0xA0000000
+#define KSEG2 0xC0000000
+#define KSEGM 0xE0000000 /* mask to check which seg */
+
+#define PTEGLOBL (1<<8)
+#define PTEVALID (1<<9)
+#define PTEWRITE (1<<10)
+#define PTEPID(n) ((n)<<6)
+
+#define NTLBPID 64 /* number of pids */
+#define NTLB 64 /* number of entries */
+#define TLBROFF 8 /* offset of first randomly indexed entry */
+
+/*
+ * Address spaces
+ */
+
+#define UZERO KUSEG /* base of user address space */
+#define UTZERO (UZERO+BY2PG) /* first address in user text */
+#define USTKTOP KZERO /* byte just beyond user stack */
+#define TSTKTOP (USERADDR+100*BY2PG) /* top of temporary stack */
+#define KZERO KSEG0 /* base of kernel address space */
+#define KTZERO (KSEG0+0x20000) /* first address in kernel text */
+#define USTACKSIZE (4*1024*1024) /* size of user stack */
+/*
+ * Exception codes
+ */
+#define CINT 0 /* external interrupt */
+#define CTLBM 1 /* TLB modification */
+#define CTLBL 2 /* TLB miss (load or fetch) */
+#define CTLBS 3 /* TLB miss (store) */
+#define CADREL 4 /* address error (load or fetch) */
+#define CADRES 5 /* address error (store) */
+#define CBUSI 6 /* bus error (fetch) */
+#define CBUSD 7 /* bus error (data load or store) */
+#define CSYS 8 /* system call */
+#define CBRK 9 /* breakpoint */
+#define CRES 10 /* reserved instruction */
+#define CCPU 11 /* coprocessor unusable */
+#define COVF 12 /* arithmetic overflow */
+#define CUNK13 13 /* undefined 13 */
+#define CUNK14 14 /* undefined 14 */
+#define CUNK15 15 /* undefined 15 */
+
+#define NSEG 5
+
+#define SP R29
+
+#define PROM (KSEG1+0x1FC00000)
+#define NOOP NOR R0,R0
+#define WAIT NOOP; NOOP
+
+/*
+ * Boot first processor
+ * - why is the processor number loaded from R0 ?????
+ */
+TEXT start(SB), $-4
+
+ MOVW $setR30(SB), R30
+ MOVW $(CU1|INTR5|INTR4|INTR3|INTR2|INTR1|SW1|SW0), R1
+ MOVW R1, M(STATUS)
+ WAIT
+
+ MOVW $(0x1C<<7), R1
+ MOVW R1, FCR31 /* permit only inexact and underflow */
+ NOOP
+ MOVD $0.5, F26
+ SUBD F26, F26, F24
+ ADDD F26, F26, F28
+ ADDD F28, F28, F30
+
+ MOVD F24, F0
+ MOVD F24, F2
+ MOVD F24, F4
+ MOVD F24, F6
+ MOVD F24, F8
+ MOVD F24, F10
+ MOVD F24, F12
+ MOVD F24, F14
+ MOVD F24, F16
+ MOVD F24, F18
+ MOVD F24, F20
+ MOVD F24, F22
+
+ MOVW $MACHADDR, R(MACH)
+ ADDU $(BY2PG-4), R(MACH), SP
+ MOVW $0, R(USER)
+ MOVW R0, 0(R(MACH))
+
+ MOVW $edata(SB), R1
+ MOVW $end(SB), R2
+
+clrbss:
+ MOVB $0, (R1)
+ ADDU $1, R1
+ BNE R1, R2, clrbss
+
+ MOVW R4, _argc(SB)
+ MOVW R5, _argv(SB)
+ MOVW R6, _env(SB)
+ JAL main(SB)
+ JMP (R0)
+
+/*
+ * Take first processor into user mode
+ * - argument is stack pointer to user
+ */
+
+TEXT touser(SB), $-4
+
+ MOVW M(STATUS), R1
+ OR $(KUP|IEP), R1
+ MOVW R1, M(STATUS)
+ NOOP
+ MOVW 0(FP), SP
+ MOVW $(UTZERO+32), R26 /* header appears in text */
+ RFE (R26)
+
+/*
+ * Bring subsequent processors on line
+ */
+TEXT newstart(SB), $0
+
+ MOVW $setR30(SB), R30
+ MOVW $(INTR5|INTR4|INTR3|INTR2|INTR1|SW1|SW0), R1
+ MOVW R1, M(STATUS)
+ NOOP
+ MOVW $MACHADDR, R(MACH)
+ MOVB (MPID+3), R1
+ AND $7, R1
+ SLL $PGSHIFT, R1, R2
+ ADDU R2, R(MACH)
+ ADDU $(BY2PG-4), R(MACH), SP
+ MOVW $0, R(USER)
+ MOVW R1, 0(R(MACH))
+ JAL online(SB)
+ JMP (R0)
+
+TEXT firmware(SB), $0
+
+ MOVW $(PROM+0x18), R1 /**/
+/* MOVW $(PROM+0x00), R1 /**/
+ JMP (R1)
+
+TEXT splhi(SB), $0
+
+ MOVW M(STATUS), R1
+ AND $~IEC, R1, R2
+ MOVW R2, M(STATUS)
+ NOOP
+ RET
+
+TEXT spllo(SB), $0
+
+ MOVW M(STATUS), R1
+ OR $IEC, R1, R2
+ MOVW R2, M(STATUS)
+ NOOP
+ RET
+
+TEXT splx(SB), $0
+
+ MOVW 0(FP), R1
+ MOVW M(STATUS), R2
+ AND $IEC, R1
+ AND $~IEC, R2
+ OR R2, R1
+ MOVW R1, M(STATUS)
+ NOOP
+ RET
+
+TEXT wbflush(SB), $-4
+
+ MOVW $WBFLUSH, R1
+ MOVW 0(R1), R1
+ RET
+
+TEXT setlabel(SB), $0
+
+ MOVW 0(FP), R2
+ MOVW $0, R1
+ MOVW R31, 0(R2)
+ MOVW R29, 4(R2)
+ RET
+
+TEXT gotolabel(SB), $0
+
+ MOVW 0(FP), R2
+ MOVW $1, R1
+ MOVW 0(R2), R31
+ MOVW 4(R2), R29
+ RET
+
+TEXT gotopc(SB), $8
+
+ MOVW 0(FP), R7 /* save arguments for later */
+ MOVW _argc(SB), R4
+ MOVW _argv(SB), R5
+ MOVW _env(SB), R6
+ MOVW R0, 4(SP)
+ MOVW $(64*1024), R1
+ MOVW R1, 8(SP)
+ JAL icflush(SB)
+ JMP (R7)
+
+TEXT puttlb(SB), $4
+
+ JAL splhi(SB)
+ MOVW 0(FP), R2
+ MOVW 4(FP), R3
+ MOVW R1, 4(SP)
+ MOVW R2, M(TLBVIRT)
+ MOVW R3, M(TLBPHYS)
+ NOOP
+ TLBP
+ NOOP
+ MOVW M(INDEX), R4
+ BGEZ R4, index
+ TLBWR
+ NOOP
+ JAL splx(SB)
+ RET
+index:
+ TLBWI
+ NOOP
+ JAL splx(SB)
+ RET
+
+TEXT puttlbx(SB), $0
+
+ MOVW 0(FP), R4
+ MOVW 4(FP), R2
+ MOVW 8(FP), R3
+ SLL $8, R4
+ MOVW R2, M(TLBVIRT)
+ MOVW R3, M(TLBPHYS)
+ MOVW R4, M(INDEX)
+ NOOP
+ TLBWI
+ NOOP
+ RET
+
+TEXT tlbp(SB), $0
+ TLBP
+ NOOP
+ MOVW M(INDEX), R1
+ RET
+
+TEXT tlbvirt(SB), $0
+ TLBP
+ NOOP
+ MOVW M(TLBVIRT), R1
+ RET
+
+
+TEXT gettlb(SB), $0
+
+ MOVW 0(FP), R3
+ MOVW 4(FP), R4
+ SLL $8, R3
+ MOVW R3, M(INDEX)
+ NOOP
+ TLBR
+ NOOP
+ MOVW M(TLBVIRT), R1
+ MOVW M(TLBPHYS), R2
+ NOOP
+ MOVW R1, 0(R4)
+ MOVW R2, 4(R4)
+ RET
+
+TEXT gettlbvirt(SB), $0
+
+ MOVW 0(FP), R3
+ SLL $8, R3
+ MOVW R3, M(INDEX)
+ NOOP
+ TLBR
+ NOOP
+ MOVW M(TLBVIRT), R1
+ NOOP
+ RET
+
+TEXT vector80(SB), $-4
+
+ MOVW $exception(SB), R26
+ JMP (R26)
+
+TEXT exception(SB), $-4
+
+ MOVW M(STATUS), R26
+ AND $KUP, R26
+ BEQ R26, waskernel
+
+wasuser:
+ MOVW SP, R26
+ /*
+ * set kernel sp: ureg - ureg* - pc
+ * done in 2 steps because R30 is not set
+ * and the loader will make a literal
+ */
+ MOVW $((UREGADDR-2*BY2WD) & 0xffff0000), SP
+ OR $((UREGADDR-2*BY2WD) & 0xffff), SP
+ MOVW R26, 0x10(SP) /* user SP */
+ MOVW R31, 0x28(SP)
+ MOVW R30, 0x2C(SP)
+ MOVW M(CAUSE), R26
+ MOVW R(MACH), 0x3C(SP)
+ MOVW R(USER), 0x40(SP)
+ AND $(0xF<<2), R26
+ SUB $(CSYS<<2), R26
+
+ JAL saveregs(SB)
+
+ MOVW $setR30(SB), R30
+ SUBU $(UREGADDR-2*BY2WD-USERADDR), SP, R(USER)
+ MOVW $MPID, R1
+ MOVB 3(R1), R1
+ MOVW $MACHADDR, R(MACH) /* locn of mach 0 */
+ AND $7, R1
+ SLL $PGSHIFT, R1
+ ADDU R1, R(MACH) /* add offset for mach # */
+
+ BNE R26, notsys
+
+ JAL syscall(SB)
+
+ MOVW 0x28(SP), R31
+ MOVW 0x08(SP), R26
+ MOVW 0x2C(SP), R30
+ MOVW R26, M(STATUS)
+ NOOP
+ MOVW 0x0C(SP), R26 /* old pc */
+ MOVW 0x10(SP), SP
+ RFE (R26)
+
+notsys:
+ JAL trap(SB)
+
+restore:
+ JAL restregs(SB)
+ MOVW 0x28(SP), R31
+ MOVW 0x2C(SP), R30
+ MOVW 0x3C(SP), R(MACH)
+ MOVW 0x40(SP), R(USER)
+ MOVW 0x10(SP), SP
+ RFE (R26)
+
+waskernel:
+ MOVW $1, R26 /* not sys call */
+ MOVW SP, -0x90(SP) /* drop this if possible */
+ SUB $0xA0, SP
+ MOVW R31, 0x28(SP)
+ JAL saveregs(SB)
+ JAL trap(SB)
+ JAL restregs(SB)
+ MOVW 0x28(SP), R31
+ ADD $0xA0, SP
+ RFE (R26)
+
+TEXT saveregs(SB), $-4
+ MOVW R1, 0x9C(SP)
+ MOVW R2, 0x98(SP)
+ ADDU $8, SP, R1
+ MOVW R1, 0x04(SP) /* arg to base of regs */
+ MOVW M(STATUS), R1
+ MOVW M(EPC), R2
+ MOVW R1, 0x08(SP)
+ MOVW R2, 0x0C(SP)
+
+ BEQ R26, return /* sys call, don't save */
+
+ MOVW M(CAUSE), R1
+ MOVW M(BADVADDR), R2
+ MOVW R1, 0x14(SP)
+ MOVW M(TLBVIRT), R1
+ MOVW R2, 0x18(SP)
+ MOVW R1, 0x1C(SP)
+ MOVW HI, R1
+ MOVW LO, R2
+ MOVW R1, 0x20(SP)
+ MOVW R2, 0x24(SP)
+ /* LINK,SB,SP missing */
+ MOVW R28, 0x30(SP)
+ /* R27, R26 not saved */
+ /* R25, R24 missing */
+ MOVW R23, 0x44(SP)
+ MOVW R22, 0x48(SP)
+ MOVW R21, 0x4C(SP)
+ MOVW R20, 0x50(SP)
+ MOVW R19, 0x54(SP)
+ MOVW R18, 0x58(SP)
+ MOVW R17, 0x5C(SP)
+ MOVW R16, 0x60(SP)
+ MOVW R15, 0x64(SP)
+ MOVW R14, 0x68(SP)
+ MOVW R13, 0x6C(SP)
+ MOVW R12, 0x70(SP)
+ MOVW R11, 0x74(SP)
+ MOVW R10, 0x78(SP)
+ MOVW R9, 0x7C(SP)
+ MOVW R8, 0x80(SP)
+ MOVW R7, 0x84(SP)
+ MOVW R6, 0x88(SP)
+ MOVW R5, 0x8C(SP)
+ MOVW R4, 0x90(SP)
+ MOVW R3, 0x94(SP)
+return:
+ RET
+
+TEXT restregs(SB), $-4
+ /* LINK,SB,SP missing */
+ MOVW 0x30(SP), R28
+ /* R27, R26 not saved */
+ /* R25, R24 missing */
+ MOVW 0x44(SP), R23
+ MOVW 0x48(SP), R22
+ MOVW 0x4C(SP), R21
+ MOVW 0x50(SP), R20
+ MOVW 0x54(SP), R19
+ MOVW 0x58(SP), R18
+ MOVW 0x5C(SP), R17
+ MOVW 0x60(SP), R16
+ MOVW 0x64(SP), R15
+ MOVW 0x68(SP), R14
+ MOVW 0x6C(SP), R13
+ MOVW 0x70(SP), R12
+ MOVW 0x74(SP), R11
+ MOVW 0x78(SP), R10
+ MOVW 0x7C(SP), R9
+ MOVW 0x80(SP), R8
+ MOVW 0x84(SP), R7
+ MOVW 0x88(SP), R6
+ MOVW 0x8C(SP), R5
+ MOVW 0x90(SP), R4
+ MOVW 0x94(SP), R3
+ MOVW 0x24(SP), R2
+ MOVW 0x20(SP), R1
+ MOVW R2, LO
+ MOVW R1, HI
+ MOVW 0x08(SP), R1
+ MOVW 0x98(SP), R2
+ MOVW R1, M(STATUS)
+ NOOP
+ MOVW 0x9C(SP), R1
+ MOVW 0x0C(SP), R26 /* old pc */
+ RET
+
+TEXT rfnote(SB), $0
+ MOVW 0(FP), R26 /* 1st arg is &uregpointer */
+ SUBU $(BY2WD), R26, SP /* pc hole */
+ BNE R26, restore
+
+
+TEXT clrfpintr(SB), $0
+ MOVW FCR31, R1
+ MOVW R1, R2
+ AND $~(0x3F<<12), R2
+ MOVW R2, FCR31
+ RET
+
+TEXT savefpregs(SB), $0
+ MOVW M(STATUS), R3
+ MOVW 0(FP), R1
+ MOVW FCR31, R2
+
+ MOVD F0, 0x00(R1)
+ MOVD F2, 0x08(R1)
+ MOVD F4, 0x10(R1)
+ MOVD F6, 0x18(R1)
+ MOVD F8, 0x20(R1)
+ MOVD F10, 0x28(R1)
+ MOVD F12, 0x30(R1)
+ MOVD F14, 0x38(R1)
+ MOVD F16, 0x40(R1)
+ MOVD F18, 0x48(R1)
+ MOVD F20, 0x50(R1)
+ MOVD F22, 0x58(R1)
+ MOVD F24, 0x60(R1)
+ MOVD F26, 0x68(R1)
+ MOVD F28, 0x70(R1)
+ MOVD F30, 0x78(R1)
+
+ MOVW R2, 0x80(R1)
+ AND $~CU1, R3
+ MOVW R3, M(STATUS)
+ RET
+
+TEXT restfpregs(SB), $0
+
+ MOVW M(STATUS), R3
+ MOVW 0(FP), R1
+ OR $CU1, R3
+ MOVW R3, M(STATUS)
+ MOVW 0x80(R1), R2
+
+ MOVD 0x00(R1), F0
+ MOVD 0x08(R1), F2
+ MOVD 0x10(R1), F4
+ MOVD 0x18(R1), F6
+ MOVD 0x20(R1), F8
+ MOVD 0x28(R1), F10
+ MOVD 0x30(R1), F12
+ MOVD 0x38(R1), F14
+ MOVD 0x40(R1), F16
+ MOVD 0x48(R1), F18
+ MOVD 0x50(R1), F20
+ MOVD 0x58(R1), F22
+ MOVD 0x60(R1), F24
+ MOVD 0x68(R1), F26
+ MOVD 0x70(R1), F28
+ MOVD 0x78(R1), F30
+
+ MOVW R2, FCR31
+ AND $~CU1, R3
+ MOVW R3, M(STATUS)
+ RET
+
+/*
+ * we avoid using R4, R5, R6, and R7 so gotopc can call us without saving them
+ */
+TEXT icflush(SB), $-4 /* icflush(physaddr, nbytes) */
+
+ MOVW M(STATUS), R10
+ MOVW 0(FP), R8
+ MOVW 4(FP), R9
+ MOVW $KSEG0, R3
+ OR R3, R8
+ MOVW $0, M(STATUS)
+ MOVW $WBFLUSH, R1 /* wbflush */
+ MOVW 0(R1), R1
+ NOOP
+ MOVW $KSEG1, R3
+ MOVW $icflush0(SB), R2 /* make sure PC is in uncached address space */
+ MOVW $(SWC|ISC), R1
+ OR R3, R2
+ JMP (R2)
+
+TEXT icflush0(SB), $-4
+
+ MOVW R1, M(STATUS) /* swap and isolate cache, splhi */
+ MOVW $icflush1(SB), R2
+ JMP (R2)
+
+TEXT icflush1(SB), $-4
+
+_icflush1:
+ MOVBU R0, 0x00(R8)
+ MOVBU R0, 0x04(R8)
+ MOVBU R0, 0x08(R8)
+ MOVBU R0, 0x0C(R8)
+ MOVBU R0, 0x10(R8)
+ MOVBU R0, 0x14(R8)
+ MOVBU R0, 0x18(R8)
+ MOVBU R0, 0x1C(R8)
+ MOVBU R0, 0x20(R8)
+ MOVBU R0, 0x24(R8)
+ MOVBU R0, 0x28(R8)
+ MOVBU R0, 0x2C(R8)
+ MOVBU R0, 0x30(R8)
+ MOVBU R0, 0x34(R8)
+ MOVBU R0, 0x38(R8)
+ MOVBU R0, 0x3C(R8)
+ SUB $0x40, R9
+ ADD $0x40, R8
+ BGTZ R9, _icflush1
+ MOVW $icflush2(SB), R2 /* make sure PC is in uncached address space */
+ OR R3, R2
+ JMP (R2)
+
+TEXT icflush2(SB), $-4
+
+ MOVW $0, M(STATUS) /* swap back caches, de-isolate them, and stay splhi */
+ NOOP /* +++ */
+ MOVW R10, M(STATUS)
+ RET
+
+TEXT dcflush(SB), $-4 /* dcflush(physaddr, nbytes) */
+
+ MOVW M(STATUS), R6
+ MOVW 0(FP), R4
+ MOVW 4(FP), R5
+ MOVW $KSEG0, R3
+ OR R3, R4
+ MOVW $0, M(STATUS)
+ MOVW $WBFLUSH, R1
+ MOVW 0(R1), R1
+ NOOP
+ MOVW $ISC, R1
+ MOVW R1, M(STATUS)
+_dcflush0:
+ MOVBU R0, 0x00(R4)
+ MOVBU R0, 0x04(R4)
+ MOVBU R0, 0x08(R4)
+ MOVBU R0, 0x0C(R4)
+ MOVBU R0, 0x10(R4)
+ MOVBU R0, 0x14(R4)
+ MOVBU R0, 0x18(R4)
+ MOVBU R0, 0x1C(R4)
+ MOVBU R0, 0x20(R4)
+ MOVBU R0, 0x24(R4)
+ MOVBU R0, 0x28(R4)
+ MOVBU R0, 0x2C(R4)
+ MOVBU R0, 0x30(R4)
+ MOVBU R0, 0x34(R4)
+ MOVBU R0, 0x38(R4)
+ MOVBU R0, 0x3C(R4)
+ SUB $0x40, R5
+ ADD $0x40, R4
+ BGTZ R5, _dcflush0
+ MOVW $0, M(STATUS)
+ NOOP /* +++ */
+ MOVW R6, M(STATUS)
+ RET
--- /dev/null
+++ b/utils/0a/lex.c
@@ -1,0 +1,697 @@
+#define EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+ char *p;
+ int nout, nproc, status, i, c;
+
+ thechar = '0';
+ thestring = "spim";
+ memset(debug, 0, sizeof(debug));
+ cinit();
+ outfile = 0;
+ include[ninclude++] = ".";
+ ARGBEGIN {
+ default:
+ c = ARGC();
+ if(c >= 0 || c < sizeof(debug))
+ debug[c] = 1;
+ break;
+
+ case 'o':
+ outfile = ARGF();
+ break;
+
+ case 'D':
+ p = ARGF();
+ if(p)
+ Dlist[nDlist++] = p;
+ break;
+
+ case 'I':
+ p = ARGF();
+ setinclude(p);
+ break;
+ } ARGEND
+ if(*argv == 0) {
+ print("usage: %ca [-options] file.s\n", thechar);
+ errorexit();
+ }
+ if(argc > 1 && systemtype(Windows)) {
+ print("can't assemble multiple files on windows\n");
+ errorexit();
+ }
+ if(argc > 1 && !systemtype(Windows)) {
+ nproc = 1;
+ if(p = getenv("NPROC"))
+ nproc = atol(p); /* */
+ c = 0;
+ nout = 0;
+ for(;;) {
+ while(nout < nproc && argc > 0) {
+ i = myfork();
+ if(i < 0) {
+ i = mywait(&status);
+ if(i < 0)
+ errorexit();
+ if(status)
+ c++;
+ nout--;
+ continue;
+ }
+ if(i == 0) {
+ print("%s:\n", *argv);
+ if(assemble(*argv))
+ errorexit();
+ exits(0);
+ }
+ nout++;
+ argc--;
+ argv++;
+ }
+ i = mywait(&status);
+ if(i < 0) {
+ if(c)
+ errorexit();
+ exits(0);
+ }
+ if(status)
+ c++;
+ nout--;
+ }
+ }
+ if(assemble(argv[0]))
+ errorexit();
+ exits(0);
+}
+
+int
+assemble(char *file)
+{
+ char ofile[100], incfile[20], *p;
+ int i, of;
+
+ strcpy(ofile, file);
+ p = utfrrune(ofile, '/');
+ if(p) {
+ include[0] = ofile;
+ *p++ = 0;
+ } else
+ p = ofile;
+ if(outfile == 0) {
+ outfile = p;
+ if(outfile){
+ p = utfrrune(outfile, '.');
+ if(p)
+ if(p[1] == 's' && p[2] == 0)
+ p[0] = 0;
+ p = utfrune(outfile, 0);
+ p[0] = '.';
+ p[1] = thechar;
+ p[2] = 0;
+ } else
+ outfile = "/dev/null";
+ }
+ p = getenv("INCLUDE");
+ if(p) {
+ setinclude(p);
+ } else {
+ if(systemtype(Plan9)) {
+ sprint(incfile,"/%s/include", thestring);
+ setinclude(strdup(incfile));
+ }
+ }
+
+ of = mycreat(outfile, 0664);
+ if(of < 0) {
+ yyerror("%ca: cannot create %s", thechar, outfile);
+ errorexit();
+ }
+ Binit(&obuf, of, OWRITE);
+
+ pass = 1;
+ pinit(file);
+ for(i=0; i<nDlist; i++)
+ dodefine(Dlist[i]);
+ yyparse();
+ if(nerrors) {
+ cclean();
+ return nerrors;
+ }
+
+ pass = 2;
+ outhist();
+ pinit(file);
+ for(i=0; i<nDlist; i++)
+ dodefine(Dlist[i]);
+ yyparse();
+ cclean();
+ return nerrors;
+}
+
+struct
+{
+ char *name;
+ ushort type;
+ ushort value;
+} itab[] =
+{
+ "SP", LSP, D_AUTO,
+ "SB", LSB, D_EXTERN,
+ "FP", LFP, D_PARAM,
+ "PC", LPC, D_BRANCH,
+ "HI", LHI, D_HI,
+ "LO", LLO, D_LO,
+
+ "R", LR, 0,
+ "R0", LREG, 0,
+ "R1", LREG, 1,
+ "R2", LREG, 2,
+ "R3", LREG, 3,
+ "R4", LREG, 4,
+ "R5", LREG, 5,
+ "R6", LREG, 6,
+ "R7", LREG, 7,
+ "R8", LREG, 8,
+ "R9", LREG, 9,
+ "R10", LREG, 10,
+ "R11", LREG, 11,
+ "R12", LREG, 12,
+ "R13", LREG, 13,
+ "R14", LREG, 14,
+ "R15", LREG, 15,
+ "R16", LREG, 16,
+ "R17", LREG, 17,
+ "R18", LREG, 18,
+ "R19", LREG, 19,
+ "R20", LREG, 20,
+ "R21", LREG, 21,
+ "R22", LREG, 22,
+ "R23", LREG, 23,
+ "R24", LREG, 24,
+ "R25", LREG, 25,
+ "R26", LREG, 26,
+ "R27", LREG, 27,
+ "R28", LREG, 28,
+ "R29", LREG, 29,
+ "R30", LREG, 30,
+ "R31", LREG, 31,
+
+ "M", LM, 0,
+ "M0", LMREG, 0,
+ "M1", LMREG, 1,
+ "M2", LMREG, 2,
+ "M3", LMREG, 3,
+ "M4", LMREG, 4,
+ "M5", LMREG, 5,
+ "M6", LMREG, 6,
+ "M7", LMREG, 7,
+ "M8", LMREG, 8,
+ "M9", LMREG, 9,
+ "M10", LMREG, 10,
+ "M11", LMREG, 11,
+ "M12", LMREG, 12,
+ "M13", LMREG, 13,
+ "M14", LMREG, 14,
+ "M15", LMREG, 15,
+ "M16", LMREG, 16,
+ "M17", LMREG, 17,
+ "M18", LMREG, 18,
+ "M19", LMREG, 19,
+ "M20", LMREG, 20,
+ "M21", LMREG, 21,
+ "M22", LMREG, 22,
+ "M23", LMREG, 23,
+ "M24", LMREG, 24,
+ "M25", LMREG, 25,
+ "M26", LMREG, 26,
+ "M27", LMREG, 27,
+ "M28", LMREG, 28,
+ "M29", LMREG, 29,
+ "M30", LMREG, 30,
+ "M31", LMREG, 31,
+
+ "F", LF, 0,
+
+ "F0", LFREG, 0,
+ "F1", LFREG, 1,
+ "F2", LFREG, 2,
+ "F3", LFREG, 3,
+ "F4", LFREG, 4,
+ "F5", LFREG, 5,
+ "F6", LFREG, 6,
+ "F7", LFREG, 7,
+ "F8", LFREG, 8,
+ "F9", LFREG, 9,
+ "F10", LFREG, 10,
+ "F11", LFREG, 11,
+ "F12", LFREG, 12,
+ "F13", LFREG, 13,
+ "F14", LFREG, 14,
+ "F15", LFREG, 15,
+ "F16", LFREG, 16,
+ "F17", LFREG, 17,
+ "F18", LFREG, 18,
+ "F19", LFREG, 19,
+ "F20", LFREG, 20,
+ "F21", LFREG, 21,
+ "F22", LFREG, 22,
+ "F23", LFREG, 23,
+ "F24", LFREG, 24,
+ "F25", LFREG, 25,
+ "F26", LFREG, 26,
+ "F27", LFREG, 27,
+ "F28", LFREG, 28,
+ "F29", LFREG, 29,
+ "F30", LFREG, 30,
+ "F31", LFREG, 31,
+
+ "FCR", LFCR, 0,
+ "FCR0", LFCREG, 0,
+ "FCR1", LFCREG, 1,
+ "FCR2", LFCREG, 2,
+ "FCR3", LFCREG, 3,
+ "FCR4", LFCREG, 4,
+ "FCR5", LFCREG, 5,
+ "FCR6", LFCREG, 6,
+ "FCR7", LFCREG, 7,
+ "FCR8", LFCREG, 8,
+ "FCR9", LFCREG, 9,
+ "FCR10", LFCREG, 10,
+ "FCR11", LFCREG, 11,
+ "FCR12", LFCREG, 12,
+ "FCR13", LFCREG, 13,
+ "FCR14", LFCREG, 14,
+ "FCR15", LFCREG, 15,
+ "FCR16", LFCREG, 16,
+ "FCR17", LFCREG, 17,
+ "FCR18", LFCREG, 18,
+ "FCR19", LFCREG, 19,
+ "FCR20", LFCREG, 20,
+ "FCR21", LFCREG, 21,
+ "FCR22", LFCREG, 22,
+ "FCR23", LFCREG, 23,
+ "FCR24", LFCREG, 24,
+ "FCR25", LFCREG, 25,
+ "FCR26", LFCREG, 26,
+ "FCR27", LFCREG, 27,
+ "FCR28", LFCREG, 28,
+ "FCR29", LFCREG, 29,
+ "FCR30", LFCREG, 30,
+ "FCR31", LFCREG, 31,
+
+ "ADD", LTYPE1, AADD,
+ "ADDU", LTYPE1, AADDU,
+ "SUB", LTYPE1, ASUB, /* converted to ADD(-) in loader */
+ "SUBU", LTYPE1, ASUBU,
+ "SGT", LTYPE1, ASGT,
+ "SGTU", LTYPE1, ASGTU,
+ "AND", LTYPE1, AAND,
+ "OR", LTYPE1, AOR,
+ "XOR", LTYPE1, AXOR,
+ "SLL", LTYPE1, ASLL,
+ "SRL", LTYPE1, ASRL,
+ "SRA", LTYPE1, ASRA,
+
+ "ADDV", LTYPE1, AADDV,
+ "ADDVU", LTYPE1, AADDVU,
+ "SUBV", LTYPE1, ASUBV, /* converted to ADD(-) in loader */
+ "SUBVU", LTYPE1, ASUBVU,
+ "SLLV", LTYPE1, ASLLV,
+ "SRLV", LTYPE1, ASRLV,
+ "SRAV", LTYPE1, ASRAV,
+
+ "NOR", LTYPE2, ANOR,
+
+ "MOVB", LTYPE3, AMOVB,
+ "MOVBU", LTYPE3, AMOVBU,
+ "MOVH", LTYPE3, AMOVH,
+ "MOVHU", LTYPE3, AMOVHU,
+ "MOVWL", LTYPE3, AMOVWL,
+ "MOVWR", LTYPE3, AMOVWR,
+ "MOVVL", LTYPE3, AMOVVL,
+ "MOVVR", LTYPE3, AMOVVR,
+
+ "BREAK", LTYPEJ, ABREAK, /* overloaded CACHE opcode */
+ "END", LTYPE4, AEND,
+ "REM", LTYPE6, AREM,
+ "REMU", LTYPE6, AREMU,
+ "RET", LTYPE4, ARET,
+ "SYSCALL", LTYPE4, ASYSCALL,
+ "TLBP", LTYPE4, ATLBP,
+ "TLBR", LTYPE4, ATLBR,
+ "TLBWI", LTYPE4, ATLBWI,
+ "TLBWR", LTYPE4, ATLBWR,
+
+ "MOVW", LTYPE5, AMOVW,
+ "MOVV", LTYPE5, AMOVV,
+ "MOVD", LTYPE5, AMOVD,
+ "MOVF", LTYPE5, AMOVF,
+
+ "DIV", LTYPE6, ADIV,
+ "DIVU", LTYPE6, ADIVU,
+ "MUL", LTYPE6, AMUL,
+ "MULU", LTYPE6, AMULU,
+ "DIVV", LTYPE6, ADIVV,
+ "DIVVU", LTYPE6, ADIVVU,
+ "MULV", LTYPE6, AMULV,
+ "MULVU", LTYPE6, AMULVU,
+
+ "RFE", LTYPE7, ARFE,
+ "JMP", LTYPE7, AJMP,
+
+ "JAL", LTYPE8, AJAL,
+
+ "BEQ", LTYPE9, ABEQ,
+ "BNE", LTYPE9, ABNE,
+
+ "BGEZ", LTYPEA, ABGEZ,
+ "BGEZAL", LTYPEA, ABGEZAL,
+ "BGTZ", LTYPEA, ABGTZ,
+ "BLEZ", LTYPEA, ABLEZ,
+ "BLTZ", LTYPEA, ABLTZ,
+ "BLTZAL", LTYPEA, ABLTZAL,
+
+ "TEXT", LTYPEB, ATEXT,
+ "GLOBL", LTYPEB, AGLOBL,
+
+ "DATA", LTYPEC, ADATA,
+
+ "MOVDF", LTYPE5, AMOVDF,
+ "MOVDW", LTYPE5, AMOVDW,
+ "MOVFD", LTYPE5, AMOVFD,
+ "MOVFW", LTYPE5, AMOVFW,
+ "MOVWD", LTYPE5, AMOVWD,
+ "MOVWF", LTYPE5, AMOVWF,
+
+ "ABSD", LTYPED, AABSD,
+ "ABSF", LTYPED, AABSF,
+ "ABSW", LTYPED, AABSW,
+ "NEGD", LTYPED, ANEGD,
+ "NEGF", LTYPED, ANEGF,
+ "NEGW", LTYPED, ANEGW,
+
+ "CMPEQD", LTYPEF, ACMPEQD,
+ "CMPEQF", LTYPEF, ACMPEQF,
+ "CMPGED", LTYPEF, ACMPGED,
+ "CMPGEF", LTYPEF, ACMPGEF,
+ "CMPGTD", LTYPEF, ACMPGTD,
+ "CMPGTF", LTYPEF, ACMPGTF,
+
+ "ADDD", LTYPEE, AADDD,
+ "ADDF", LTYPEE, AADDF,
+ "ADDW", LTYPEE, AADDW,
+ "DIVD", LTYPEE, ADIVD,
+ "DIVF", LTYPEE, ADIVF,
+ "DIVW", LTYPEE, ADIVW,
+ "MULD", LTYPEE, AMULD,
+ "MULF", LTYPEE, AMULF,
+ "MULW", LTYPEE, AMULW,
+ "SUBD", LTYPEE, ASUBD,
+ "SUBF", LTYPEE, ASUBF,
+ "SUBW", LTYPEE, ASUBW,
+
+ "BFPT", LTYPEG, ABFPT,
+ "BFPF", LTYPEG, ABFPF,
+
+ "WORD", LTYPEH, AWORD,
+ "NOP", LTYPEI, ANOP,
+ "SCHED", LSCHED, 0,
+ "NOSCHED", LSCHED, 0x80,
+ 0
+};
+
+void
+cinit(void)
+{
+ Sym *s;
+ int i;
+
+ nullgen.sym = S;
+ nullgen.offset = 0;
+ nullgen.type = D_NONE;
+ nullgen.name = D_NONE;
+ nullgen.reg = NREG;
+ if(FPCHIP)
+ nullgen.dval = 0;
+ for(i=0; i<sizeof(nullgen.sval); i++)
+ nullgen.sval[i] = 0;
+
+ nerrors = 0;
+ iostack = I;
+ iofree = I;
+ peekc = IGN;
+ nhunk = 0;
+ for(i=0; i<NHASH; i++)
+ hash[i] = S;
+ for(i=0; itab[i].name; i++) {
+ s = slookup(itab[i].name);
+ s->type = itab[i].type;
+ s->value = itab[i].value;
+ }
+
+ pathname = alloc(100);
+ if(mygetwd(pathname, 99) == 0) {
+ pathname = allocn(pathname, 100, 900);
+ if(mygetwd(pathname, 999) == 0)
+ strcpy(pathname, "/?");
+ }
+}
+
+void
+syminit(Sym *s)
+{
+
+ s->type = LNAME;
+ s->value = 0;
+}
+
+int
+isreg(Gen *g)
+{
+
+ USED(g);
+ return 1;
+}
+
+void
+cclean(void)
+{
+
+ outcode(AEND, &nullgen, NREG, &nullgen);
+ Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+ Bputc(&obuf, ANAME);
+ Bputc(&obuf, t); /* type */
+ Bputc(&obuf, s); /* sym */
+ while(*n) {
+ Bputc(&obuf, *n);
+ n++;
+ }
+ Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+ long l;
+ int i;
+ char *n;
+ Ieee e;
+
+ Bputc(&obuf, a->type);
+ Bputc(&obuf, a->reg);
+ Bputc(&obuf, s);
+ Bputc(&obuf, a->name);
+ switch(a->type) {
+ default:
+ print("unknown type %d\n", a->type);
+ exits("arg");
+
+ case D_NONE:
+ case D_REG:
+ case D_FREG:
+ case D_MREG:
+ case D_FCREG:
+ case D_LO:
+ case D_HI:
+ break;
+
+ case D_OREG:
+ case D_CONST:
+ case D_OCONST:
+ case D_BRANCH:
+ l = a->offset;
+ Bputc(&obuf, l);
+ Bputc(&obuf, l>>8);
+ Bputc(&obuf, l>>16);
+ Bputc(&obuf, l>>24);
+ break;
+
+ case D_SCONST:
+ n = a->sval;
+ for(i=0; i<NSNAME; i++) {
+ Bputc(&obuf, *n);
+ n++;
+ }
+ break;
+
+ case D_FCONST:
+ ieeedtod(&e, a->dval);
+ Bputc(&obuf, e.l);
+ Bputc(&obuf, e.l>>8);
+ Bputc(&obuf, e.l>>16);
+ Bputc(&obuf, e.l>>24);
+ Bputc(&obuf, e.h);
+ Bputc(&obuf, e.h>>8);
+ Bputc(&obuf, e.h>>16);
+ Bputc(&obuf, e.h>>24);
+ break;
+ }
+}
+
+void
+outcode(int a, Gen *g1, int reg, Gen *g2)
+{
+ int sf, st, t;
+ Sym *s;
+
+ if(pass == 1)
+ goto out;
+jackpot:
+ sf = 0;
+ s = g1->sym;
+ while(s != S) {
+ sf = s->sym;
+ if(sf < 0 || sf >= NSYM)
+ sf = 0;
+ t = g1->name;
+ if(h[sf].type == t)
+ if(h[sf].sym == s)
+ break;
+ zname(s->name, t, sym);
+ s->sym = sym;
+ h[sym].sym = s;
+ h[sym].type = t;
+ sf = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ break;
+ }
+ st = 0;
+ s = g2->sym;
+ while(s != S) {
+ st = s->sym;
+ if(st < 0 || st >= NSYM)
+ st = 0;
+ t = g2->name;
+ if(h[st].type == t)
+ if(h[st].sym == s)
+ break;
+ zname(s->name, t, sym);
+ s->sym = sym;
+ h[sym].sym = s;
+ h[sym].type = t;
+ st = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ if(st == sf)
+ goto jackpot;
+ break;
+ }
+ Bputc(&obuf, a);
+ Bputc(&obuf, reg|nosched);
+ Bputc(&obuf, lineno);
+ Bputc(&obuf, lineno>>8);
+ Bputc(&obuf, lineno>>16);
+ Bputc(&obuf, lineno>>24);
+ zaddr(g1, sf);
+ zaddr(g2, st);
+
+out:
+ if(a != AGLOBL && a != ADATA)
+ pc++;
+}
+
+void
+outhist(void)
+{
+ Gen g;
+ Hist *h;
+ char *p, *q, *op, c;
+ int n;
+
+ g = nullgen;
+ c = pathchar();
+ for(h = hist; h != H; h = h->link) {
+ p = h->name;
+ op = 0;
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && p && p[1] == ':'){
+ p += 2;
+ c = *p;
+ }
+ if(p && p[0] != c && h->offset == 0 && pathname){
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && pathname[1] == ':') {
+ op = p;
+ p = pathname+2;
+ c = *p;
+ } else if(pathname[0] == c){
+ op = p;
+ p = pathname;
+ }
+ }
+ while(p) {
+ q = strchr(p, c);
+ if(q) {
+ n = q-p;
+ if(n == 0){
+ n = 1; /* leading "/" */
+ *p = '/'; /* don't emit "\" on windows */
+ }
+ q++;
+ } else {
+ n = strlen(p);
+ q = 0;
+ }
+ if(n) {
+ Bputc(&obuf, ANAME);
+ Bputc(&obuf, D_FILE); /* type */
+ Bputc(&obuf, 1); /* sym */
+ Bputc(&obuf, '<');
+ Bwrite(&obuf, p, n);
+ Bputc(&obuf, 0);
+ }
+ p = q;
+ if(p == 0 && op) {
+ p = op;
+ op = 0;
+ }
+ }
+ g.offset = h->offset;
+
+ Bputc(&obuf, AHISTORY);
+ Bputc(&obuf, 0);
+ Bputc(&obuf, h->line);
+ Bputc(&obuf, h->line>>8);
+ Bputc(&obuf, h->line>>16);
+ Bputc(&obuf, h->line>>24);
+ zaddr(&nullgen, 0);
+ zaddr(&g, 0);
+ }
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/0a/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=0a
+
+OFILES=\
+ y.tab.$O\
+ lex.$O\
+
+HFILES=\
+ ../vc/v.out.h\
+ y.tab.h\
+ a.h\
+
+YFILES= a.y\
+
+LIBS=cc bio 9 # order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS= $CFLAGS -I../include
+
+lex.$O: ../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+ cd ../cc
+ mk $MKFLAGS install
+ mk $MKFLAGS clean
--- /dev/null
+++ b/utils/0c/cgen.c
@@ -1,0 +1,1148 @@
+#include "gc.h"
+
+void
+cgen(Node *n, Node *nn)
+{
+ Node *l, *r;
+ Prog *p1;
+ Node nod, nod1, nod2, nod3, nod4;
+ int o;
+ long v, curs;
+
+ if(debug['g']) {
+ prtree(nn, "cgen lhs");
+ prtree(n, "cgen");
+ }
+ if(n == Z || n->type == T)
+ return;
+ if(typesu[n->type->etype]) {
+ sugen(n, nn, n->type->width);
+ return;
+ }
+ if(n->addable > INDEXED) {
+ if(nn != Z)
+ gmove(n, nn);
+ return;
+ }
+ curs = cursafe;
+ l = n->left;
+ r = n->right;
+ o = n->op;
+
+ if(n->complex >= FNX)
+ if(l->complex >= FNX)
+ if(r != Z && r->complex >= FNX)
+ switch(o) {
+ default:
+ regret(&nod, r);
+ cgen(r, &nod);
+
+ regsalloc(&nod1, r);
+ gopcode(OAS, &nod, Z, &nod1);
+
+ regfree(&nod);
+ nod = *n;
+ nod.right = &nod1;
+ cgen(&nod, nn);
+ return;
+
+ case OFUNC:
+ case OCOMMA:
+ case OANDAND:
+ case OOROR:
+ case OCOND:
+ case ODOT:
+ break;
+ }
+
+ switch(o) {
+ default:
+ diag(n, "unknown op in cgen: %O", o);
+ break;
+
+ case OAS:
+ if(l->op == OBIT)
+ goto bitas;
+ if(l->addable >= INDEXED && l->complex < FNX) {
+ if(nn != Z || r->addable < INDEXED) {
+ if(r->complex >= FNX && nn == Z)
+ regret(&nod, r);
+ else
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ gmove(&nod, l);
+ if(nn != Z)
+ gmove(&nod, nn);
+ regfree(&nod);
+ } else
+ gmove(r, l);
+ break;
+ }
+ if(l->complex >= r->complex) {
+ reglcgen(&nod1, l, Z);
+ if(r->addable >= INDEXED) {
+ gmove(r, &nod1);
+ if(nn != Z)
+ gmove(r, nn);
+ regfree(&nod1);
+ break;
+ }
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ reglcgen(&nod1, l, Z);
+ }
+ gmove(&nod, &nod1);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ bitas:
+ n = l->left;
+ regalloc(&nod, r, nn);
+ if(l->complex >= r->complex) {
+ reglcgen(&nod1, n, Z);
+ cgen(r, &nod);
+ } else {
+ cgen(r, &nod);
+ reglcgen(&nod1, n, Z);
+ }
+ regalloc(&nod2, n, Z);
+ gopcode(OAS, &nod1, Z, &nod2);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+
+ case OBIT:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ bitload(n, &nod, Z, Z, nn);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ break;
+
+ case OADD:
+ case OSUB:
+ case OAND:
+ case OOR:
+ case OXOR:
+ case OLSHR:
+ case OASHL:
+ case OASHR:
+ /*
+ * immediate operands
+ */
+ if(nn != Z)
+ if(r->op == OCONST)
+ if(!typefd[n->type->etype]) {
+ cgen(l, nn);
+ if(r->vconst == 0)
+ if(o != OAND)
+ break;
+ if(nn != Z)
+ gopcode(o, r, Z, nn);
+ break;
+ }
+
+ case OLMUL:
+ case OLDIV:
+ case OLMOD:
+ case OMUL:
+ case ODIV:
+ case OMOD:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ if(o == OMUL || o == OLMUL) {
+ if(mulcon(n, nn))
+ break;
+ }
+ if(l->complex >= r->complex) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ gopcode(o, &nod1, Z, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ regalloc(&nod1, l, Z);
+ cgen(l, &nod1);
+ gopcode(o, &nod, &nod1, &nod);
+ }
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ case OASLSHR:
+ case OASASHL:
+ case OASASHR:
+ case OASAND:
+ case OASADD:
+ case OASSUB:
+ case OASXOR:
+ case OASOR:
+ if(l->op == OBIT)
+ goto asbitop;
+ if(r->op == OCONST)
+ if(!typefd[n->type->etype]) {
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+
+ regalloc(&nod, n, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ gopcode(o, r, Z, &nod);
+ gopcode(OAS, &nod, Z, &nod2);
+
+ regfree(&nod);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+ }
+
+ case OASLMUL:
+ case OASLDIV:
+ case OASLMOD:
+ case OASMUL:
+ case OASDIV:
+ case OASMOD:
+ if(l->op == OBIT)
+ goto asbitop;
+ if(l->complex >= r->complex) {
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ } else {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ }
+
+ regalloc(&nod, n, nn);
+ gmove(&nod2, &nod);
+ gopcode(o, &nod1, Z, &nod);
+ gmove(&nod, &nod2);
+ if(nn != Z)
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ regfree(&nod1);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ asbitop:
+ regalloc(&nod4, n, nn);
+ if(l->complex >= r->complex) {
+ bitload(l, &nod, &nod1, &nod2, &nod4);
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ } else {
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ bitload(l, &nod, &nod1, &nod2, &nod4);
+ }
+ gmove(&nod, &nod4);
+ gopcode(o, &nod3, Z, &nod4);
+ regfree(&nod3);
+ gmove(&nod4, &nod);
+ regfree(&nod4);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+
+ case OADDR:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ lcgen(l, nn);
+ break;
+
+ case OFUNC:
+ if(l->complex >= FNX) {
+ if(l->op != OIND)
+ diag(n, "bad function call");
+
+ regret(&nod, l->left);
+ cgen(l->left, &nod);
+ regsalloc(&nod1, l->left);
+ gopcode(OAS, &nod, Z, &nod1);
+ regfree(&nod);
+
+ nod = *n;
+ nod.left = &nod2;
+ nod2 = *l;
+ nod2.left = &nod1;
+ nod2.complex = 1;
+ cgen(&nod, nn);
+
+ return;
+ }
+ o = reg[REGARG];
+ gargs(r, &nod, &nod1);
+ if(l->addable < INDEXED) {
+ reglcgen(&nod, l, Z);
+ gopcode(OFUNC, Z, Z, &nod);
+ regfree(&nod);
+ } else
+ gopcode(OFUNC, Z, Z, l);
+ if(REGARG)
+ if(o != reg[REGARG])
+ reg[REGARG]--;
+ if(nn != Z) {
+ regret(&nod, n);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ }
+ break;
+
+ case OIND:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ regialloc(&nod, n, nn);
+ r = l;
+ while(r->op == OADD)
+ r = r->right;
+ if(sconst(r)) {
+ v = r->vconst;
+ r->vconst = 0;
+ cgen(l, &nod);
+ nod.xoffset += v;
+ r->vconst = v;
+ } else
+ cgen(l, &nod);
+ regind(&nod, n);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ break;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OLO:
+ case OLS:
+ case OHI:
+ case OHS:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ boolgen(n, 1, nn);
+ break;
+
+ case OANDAND:
+ case OOROR:
+ boolgen(n, 1, nn);
+ if(nn == Z)
+ patch(p, pc);
+ break;
+
+ case ONOT:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ boolgen(n, 1, nn);
+ break;
+
+ case OCOMMA:
+ cgen(l, Z);
+ cgen(r, nn);
+ break;
+
+ case OCAST:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ /*
+ * convert from types l->n->nn
+ */
+ if(nocast(l->type, n->type)) {
+ if(nocast(n->type, nn->type)) {
+ cgen(l, nn);
+ break;
+ }
+ }
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ regalloc(&nod1, n, &nod);
+ gopcode(OAS, &nod, Z, &nod1);
+ gopcode(OAS, &nod1, Z, nn);
+ regfree(&nod1);
+ regfree(&nod);
+ break;
+
+ case ODOT:
+ sugen(l, nodrat, l->type->width);
+ if(nn != Z) {
+ warn(n, "non-interruptable temporary");
+ nod = *nodrat;
+ if(!r || r->op != OCONST) {
+ diag(n, "DOT and no offset");
+ break;
+ }
+ nod.xoffset += (long)r->vconst;
+ nod.type = n->type;
+ cgen(&nod, nn);
+ }
+ break;
+
+ case OCOND:
+ bcgen(l, 1);
+ p1 = p;
+ cgen(r->left, nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ cgen(r->right, nn);
+ patch(p1, pc);
+ break;
+
+ case OPOSTINC:
+ case OPOSTDEC:
+ v = 1;
+ if(l->type->etype == TIND)
+ v = l->type->link->width;
+ if(o == OPOSTDEC)
+ v = -v;
+ if(l->op == OBIT)
+ goto bitinc;
+ if(nn == Z)
+ goto pre;
+
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+
+ regalloc(&nod, l, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ regalloc(&nod1, l, Z);
+ if(typefd[l->type->etype]) {
+ regalloc(&nod3, l, Z);
+ if(v < 0) {
+ gopcode(OAS, nodfconst(-v), Z, &nod3);
+ gopcode(OSUB, &nod3, &nod, &nod1);
+ } else {
+ gopcode(OAS, nodfconst(v), Z, &nod3);
+ gopcode(OADD, &nod3, &nod, &nod1);
+ }
+ regfree(&nod3);
+ } else
+ gopcode(OADD, nodconst(v), &nod, &nod1);
+ gopcode(OAS, &nod1, Z, &nod2);
+
+ regfree(&nod);
+ regfree(&nod1);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ case OPREINC:
+ case OPREDEC:
+ v = 1;
+ if(l->type->etype == TIND)
+ v = l->type->link->width;
+ if(o == OPREDEC)
+ v = -v;
+ if(l->op == OBIT)
+ goto bitinc;
+
+ pre:
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+
+ regalloc(&nod, l, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ if(typefd[l->type->etype]) {
+ regalloc(&nod3, l, Z);
+ if(v < 0) {
+ gopcode(OAS, nodfconst(-v), Z, &nod3);
+ gopcode(OSUB, &nod3, Z, &nod);
+ } else {
+ gopcode(OAS, nodfconst(v), Z, &nod3);
+ gopcode(OADD, &nod3, Z, &nod);
+ }
+ regfree(&nod3);
+ } else
+ gopcode(OADD, nodconst(v), Z, &nod);
+ gopcode(OAS, &nod, Z, &nod2);
+
+ regfree(&nod);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ bitinc:
+ if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+ bitload(l, &nod, &nod1, &nod2, Z);
+ gopcode(OAS, &nod, Z, nn);
+ gopcode(OADD, nodconst(v), Z, &nod);
+ bitstore(l, &nod, &nod1, &nod2, Z);
+ break;
+ }
+ bitload(l, &nod, &nod1, &nod2, nn);
+ gopcode(OADD, nodconst(v), Z, &nod);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+ }
+ cursafe = curs;
+ return;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+ Node *r;
+ long v;
+
+ regialloc(t, n, nn);
+ if(n->op == OIND) {
+ r = n->left;
+ while(r->op == OADD)
+ r = r->right;
+ if(sconst(r)) {
+ v = r->vconst;
+ r->vconst = 0;
+ lcgen(n, t);
+ t->xoffset += v;
+ r->vconst = v;
+ regind(t, n);
+ return;
+ }
+ }
+ lcgen(n, t);
+ regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+ Prog *p1;
+ Node nod;
+
+ if(debug['g']) {
+ prtree(nn, "lcgen lhs");
+ prtree(n, "lcgen");
+ }
+ if(n == Z || n->type == T)
+ return;
+ if(nn == Z) {
+ nn = &nod;
+ regalloc(&nod, n, Z);
+ }
+ switch(n->op) {
+ default:
+ if(n->addable < INDEXED) {
+ diag(n, "unknown op in lcgen: %O", n->op);
+ break;
+ }
+ nod = *n;
+ nod.op = OADDR;
+ nod.left = n;
+ nod.right = Z;
+ nod.type = types[TIND];
+ gopcode(OAS, &nod, Z, nn);
+ break;
+
+ case OCOMMA:
+ cgen(n->left, n->left);
+ lcgen(n->right, nn);
+ break;
+
+ case OIND:
+ cgen(n->left, nn);
+ break;
+
+ case OCOND:
+ bcgen(n->left, 1);
+ p1 = p;
+ lcgen(n->right->left, nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ lcgen(n->right->right, nn);
+ patch(p1, pc);
+ break;
+ }
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+ if(n->type == T)
+ gbranch(OGOTO);
+ else
+ boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+ int o;
+ Prog *p1, *p2;
+ Node *l, *r, nod, nod1;
+ long curs;
+
+ if(debug['g']) {
+ prtree(nn, "boolgen lhs");
+ prtree(n, "boolgen");
+ }
+ curs = cursafe;
+ l = n->left;
+ r = n->right;
+ switch(n->op) {
+
+ default:
+ regalloc(&nod, n, nn);
+ cgen(n, &nod);
+ if(nn == Z || typefd[n->type->etype]) {
+ o = ONE;
+ if(true)
+ o = comrel[relindex(o)];
+ if(typefd[n->type->etype]) {
+ nodreg(&nod1, n, NREG+FREGZERO);
+ gopcode(o, &nod, &nod1, Z);
+ } else
+ gopcode(o, &nod, Z, Z);
+ regfree(&nod);
+ goto com;
+ }
+ if(true)
+ gopcode(OCOND, &nod, nodconst(0), &nod);
+ else
+ gopcode(OCOND, nodconst(1), &nod, &nod);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ break;
+
+ case OCONST:
+ o = vconst(n);
+ if(!true)
+ o = !o;
+ gbranch(OGOTO);
+ if(o) {
+ p1 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ }
+ goto com;
+
+ case OCOMMA:
+ cgen(l, Z);
+ boolgen(r, true, nn);
+ break;
+
+ case ONOT:
+ boolgen(l, !true, nn);
+ break;
+
+ case OCOND:
+ bcgen(l, 1);
+ p1 = p;
+ bcgen(r->left, true);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ bcgen(r->right, !true);
+ patch(p2, pc);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ goto com;
+
+ case OANDAND:
+ if(!true)
+ goto caseor;
+
+ caseand:
+ bcgen(l, true);
+ p1 = p;
+ bcgen(r, !true);
+ p2 = p;
+ patch(p1, pc);
+ gbranch(OGOTO);
+ patch(p2, pc);
+ goto com;
+
+ case OOROR:
+ if(!true)
+ goto caseand;
+
+ caseor:
+ bcgen(l, !true);
+ p1 = p;
+ bcgen(r, !true);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ goto com;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ o = n->op;
+ if(true)
+ o = comrel[relindex(o)];
+ if(l->complex >= FNX && r->complex >= FNX) {
+ regret(&nod, r);
+ cgen(r, &nod);
+ regsalloc(&nod1, r);
+ gopcode(OAS, &nod, Z, &nod1);
+ regfree(&nod);
+ nod = *n;
+ nod.right = &nod1;
+ boolgen(&nod, true, nn);
+ break;
+ }
+ if(nn != Z && !typefd[l->type->etype]) {
+ if(l->complex >= r->complex) {
+ regalloc(&nod1, l, nn);
+ cgen(l, &nod1);
+ regalloc(&nod, r, Z);
+ cgen(r, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ regalloc(&nod1, l, Z);
+ cgen(l, &nod1);
+ }
+ switch(o) {
+ case OEQ:
+ gopcode(OSUB, &nod1, &nod, &nod);
+ gopcode(OCOND, &nod, nodconst(0), &nod);
+ break;
+ case ONE:
+ gopcode(OSUB, &nod1, &nod, &nod);
+ gopcode(OCOND, nodconst(1), &nod, &nod);
+ break;
+ case OLE:
+ gopcode(OCOMMA, &nod1, &nod, &nod);
+ break;
+ case OGT:
+ gopcode(OCOMMA, &nod1, &nod, &nod);
+ gopcode(OXOR, nodconst(1), &nod, &nod);
+ break;
+ case OLT:
+ gopcode(OCOMMA, &nod, &nod1, &nod);
+ gopcode(OXOR, nodconst(1), &nod, &nod);
+ break;
+ case OGE:
+ gopcode(OCOMMA, &nod, &nod1, &nod);
+ break;
+ case OLS:
+ gopcode(OCOND, &nod1, &nod, &nod);
+ break;
+ case OHI:
+ gopcode(OCOND, &nod1, &nod, &nod);
+ gopcode(OXOR, nodconst(1), &nod, &nod);
+ break;
+ case OLO:
+ gopcode(OCOND, &nod, &nod1, &nod);
+ gopcode(OXOR, nodconst(1), &nod, &nod);
+ break;
+ case OHS:
+ gopcode(OCOND, &nod, &nod1, &nod);
+ break;
+ }
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+ }
+ if(sconst(l)) {
+ switch(o) {
+ default:
+ if(l->vconst != 0)
+ break;
+
+ case OGT:
+ case OHI:
+ case OLE:
+ case OLS:
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ gopcode(o, l, &nod, Z);
+ regfree(&nod);
+ goto com;
+ }
+ }
+ if(sconst(r)) {
+ switch(o) {
+ default:
+ if(r->vconst != 0)
+ break;
+
+ case OGE:
+ case OHS:
+ case OLT:
+ case OLO:
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ gopcode(o, &nod, r, Z);
+ regfree(&nod);
+ goto com;
+ }
+ }
+ if(l->complex >= r->complex) {
+ regalloc(&nod1, l, nn);
+ cgen(l, &nod1);
+ regalloc(&nod, r, Z);
+ cgen(r, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ regalloc(&nod1, l, Z);
+ cgen(l, &nod1);
+ }
+ gopcode(o, &nod1, &nod, Z);
+ regfree(&nod);
+ regfree(&nod1);
+
+ com:
+ if(nn != Z) {
+ p1 = p;
+ gopcode(OAS, nodconst(1), Z, nn);
+ gbranch(OGOTO);
+ p2 = p;
+ patch(p1, pc);
+ gopcode(OAS, nodconst(0), Z, nn);
+ patch(p2, pc);
+ }
+ break;
+ }
+ cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+ Prog *p1;
+ Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+ Type *t;
+ long pc1;
+ int i, m, c;
+
+ if(n == Z || n->type == T)
+ return;
+ if(debug['g']) {
+ prtree(nn, "sugen lhs");
+ prtree(n, "sugen");
+ }
+ if(nn == nodrat)
+ if(w > nrathole)
+ nrathole = w;
+ switch(n->op) {
+ case OIND:
+ if(nn == Z) {
+ nullwarn(n->left, Z);
+ break;
+ }
+
+ default:
+ goto copy;
+
+ case ODOT:
+ l = n->left;
+ sugen(l, nodrat, l->type->width);
+ if(nn != Z) {
+ warn(n, "non-interruptable temporary");
+ nod1 = *nodrat;
+ r = n->right;
+ if(!r || r->op != OCONST) {
+ diag(n, "DOT and no offset");
+ break;
+ }
+ nod1.xoffset += (long)r->vconst;
+ nod1.type = n->type;
+ sugen(&nod1, nn, w);
+ }
+ break;
+
+ case OSTRUCT:
+ /*
+ * rewrite so lhs has no fn call
+ */
+ if(nn != Z && nn->complex >= FNX) {
+ nod1 = *n;
+ nod1.type = typ(TIND, n->type);
+ regret(&nod2, &nod1);
+ lcgen(nn, &nod2);
+ regsalloc(&nod0, &nod1);
+ gopcode(OAS, &nod2, Z, &nod0);
+ regfree(&nod2);
+
+ nod1 = *n;
+ nod1.op = OIND;
+ nod1.left = &nod0;
+ nod1.right = Z;
+ nod1.complex = 1;
+
+ sugen(n, &nod1, w);
+ return;
+ }
+
+ r = n->left;
+ for(t = n->type->link; t != T; t = t->down) {
+ l = r;
+ if(r->op == OLIST) {
+ l = r->left;
+ r = r->right;
+ }
+ if(nn == Z) {
+ cgen(l, nn);
+ continue;
+ }
+ /*
+ * hand craft *(&nn + o) = l
+ */
+ nod0 = znode;
+ nod0.op = OAS;
+ nod0.type = t;
+ nod0.left = &nod1;
+ nod0.right = l;
+
+ nod1 = znode;
+ nod1.op = OIND;
+ nod1.type = t;
+ nod1.left = &nod2;
+
+ nod2 = znode;
+ nod2.op = OADD;
+ nod2.type = typ(TIND, t);
+ nod2.left = &nod3;
+ nod2.right = &nod4;
+
+ nod3 = znode;
+ nod3.op = OADDR;
+ nod3.type = nod2.type;
+ nod3.left = nn;
+
+ nod4 = znode;
+ nod4.op = OCONST;
+ nod4.type = nod2.type;
+ nod4.vconst = t->offset;
+
+ ccom(&nod0);
+ acom(&nod0);
+ xcom(&nod0);
+ nod0.addable = 0;
+
+ cgen(&nod0, Z);
+ }
+ break;
+
+ case OAS:
+ if(nn == Z) {
+ if(n->addable < INDEXED)
+ sugen(n->right, n->left, w);
+ break;
+ }
+ sugen(n->right, nodrat, w);
+ warn(n, "non-interruptable temporary");
+ sugen(nodrat, n->left, w);
+ sugen(nodrat, nn, w);
+ break;
+
+ case OFUNC:
+ if(nn == Z) {
+ sugen(n, nodrat, w);
+ break;
+ }
+ if(nn->op != OIND) {
+ nn = new1(OADDR, nn, Z);
+ nn->type = types[TIND];
+ nn->addable = 0;
+ } else
+ nn = nn->left;
+ n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+ n->type = types[TVOID];
+ n->left->type = types[TVOID];
+ cgen(n, Z);
+ break;
+
+ case OCOND:
+ bcgen(n->left, 1);
+ p1 = p;
+ sugen(n->right->left, nn, w);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ sugen(n->right->right, nn, w);
+ patch(p1, pc);
+ break;
+
+ case OCOMMA:
+ cgen(n->left, Z);
+ sugen(n->right, nn, w);
+ break;
+ }
+ return;
+
+copy:
+ if(nn == Z)
+ return;
+ if(n->complex >= FNX && nn->complex >= FNX) {
+ t = nn->type;
+ nn->type = types[TLONG];
+ regialloc(&nod1, nn, Z);
+ lcgen(nn, &nod1);
+ regsalloc(&nod2, nn);
+ nn->type = t;
+
+ gopcode(OAS, &nod1, Z, &nod2);
+ regfree(&nod1);
+
+ nod2.type = typ(TIND, t);
+
+ nod1 = nod2;
+ nod1.op = OIND;
+ nod1.left = &nod2;
+ nod1.right = Z;
+ nod1.complex = 1;
+ nod1.type = t;
+
+ sugen(n, &nod1, w);
+ return;
+ }
+
+ if(n->complex > nn->complex) {
+ t = n->type;
+ n->type = types[TVLONG];
+ reglcgen(&nod1, n, Z);
+ n->type = t;
+
+ t = nn->type;
+ nn->type = types[TVLONG];
+ reglcgen(&nod2, nn, Z);
+ nn->type = t;
+ } else {
+ t = nn->type;
+ nn->type = types[TVLONG];
+ reglcgen(&nod2, nn, Z);
+ nn->type = t;
+
+ t = n->type;
+ n->type = types[TVLONG];
+ reglcgen(&nod1, n, Z);
+ n->type = t;
+ }
+
+ w /= SZ_VLONG;
+ if(w <= 5) {
+ layout(&nod1, &nod2, w, 0, Z);
+ goto out;
+ }
+
+ /*
+ * minimize space for unrolling loop
+ * 3,4,5 times. (6 or more is never minimum)
+ * if small structure, try 2 also.
+ */
+ c = 0; /* set */
+ m = 100;
+ i = 3;
+ if(w <= 15)
+ i = 2;
+ for(; i<=5; i++)
+ if(i + w%i <= m) {
+ c = i;
+ m = c + w%c;
+ }
+
+ regalloc(&nod3, ®node, Z);
+ layout(&nod1, &nod2, w%c, w/c, &nod3);
+
+ pc1 = pc;
+ layout(&nod1, &nod2, c, 0, Z);
+
+ gopcode(OSUB, nodconst(1), Z, &nod3);
+ nod1.op = OREGISTER;
+ gopcode(OADD, nodconst(c*SZ_VLONG), Z, &nod1);
+ nod2.op = OREGISTER;
+ gopcode(OADD, nodconst(c*SZ_VLONG), Z, &nod2);
+
+ gopcode(OEQ, &nod3, Z, Z);
+ p->as = ABGTZ;
+ patch(p, pc1);
+
+ regfree(&nod3);
+out:
+ regfree(&nod1);
+ regfree(&nod2);
+}
+
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+ Node t1, t2;
+
+ while(c > 3) {
+ layout(f, t, 2, 0, Z);
+ c -= 2;
+ }
+
+ regalloc(&t1, ®node, Z);
+ regalloc(&t2, ®node, Z);
+ t1.type = types[TVLONG];
+ t2.type = types[TVLONG];
+ if(c > 0) {
+ gopcode(OAS, f, Z, &t1);
+ f->xoffset += SZ_VLONG;
+ }
+ if(cn != Z)
+ gopcode(OAS, nodconst(cv), Z, cn);
+ if(c > 1) {
+ gopcode(OAS, f, Z, &t2);
+ f->xoffset += SZ_VLONG;
+ }
+ if(c > 0) {
+ gopcode(OAS, &t1, Z, t);
+ t->xoffset += SZ_VLONG;
+ }
+ if(c > 2) {
+ gopcode(OAS, f, Z, &t1);
+ f->xoffset += SZ_VLONG;
+ }
+ if(c > 1) {
+ gopcode(OAS, &t2, Z, t);
+ t->xoffset += SZ_VLONG;
+ }
+ if(c > 2) {
+ gopcode(OAS, &t1, Z, t);
+ t->xoffset += SZ_VLONG;
+ }
+ regfree(&t1);
+ regfree(&t2);
+}
--- /dev/null
+++ b/utils/0c/enam.c
@@ -1,0 +1,118 @@
+char* anames[] =
+{
+ "XXX",
+ "ABSD",
+ "ABSF",
+ "ABSW",
+ "ADD",
+ "ADDD",
+ "ADDF",
+ "ADDU",
+ "ADDW",
+ "AND",
+ "BEQ",
+ "BFPF",
+ "BFPT",
+ "BGEZ",
+ "BGEZAL",
+ "BGTZ",
+ "BLEZ",
+ "BLTZ",
+ "BLTZAL",
+ "BNE",
+ "BREAK",
+ "CMPEQD",
+ "CMPEQF",
+ "CMPGED",
+ "CMPGEF",
+ "CMPGTD",
+ "CMPGTF",
+ "DATA",
+ "DIV",
+ "DIVD",
+ "DIVF",
+ "DIVU",
+ "DIVW",
+ "GLOBL",
+ "GOK",
+ "HISTORY",
+ "JAL",
+ "JMP",
+ "MOVB",
+ "MOVBU",
+ "MOVD",
+ "MOVDF",
+ "MOVDW",
+ "MOVF",
+ "MOVFD",
+ "MOVFW",
+ "MOVH",
+ "MOVHU",
+ "MOVW",
+ "MOVWD",
+ "MOVWF",
+ "MOVWL",
+ "MOVWR",
+ "MUL",
+ "MULD",
+ "MULF",
+ "MULU",
+ "MULW",
+ "NAME",
+ "NEGD",
+ "NEGF",
+ "NEGW",
+ "NOP",
+ "NOR",
+ "OR",
+ "REM",
+ "REMU",
+ "RET",
+ "RFE",
+ "SGT",
+ "SGTU",
+ "SLL",
+ "SRA",
+ "SRL",
+ "SUB",
+ "SUBD",
+ "SUBF",
+ "SUBU",
+ "SUBW",
+ "SYSCALL",
+ "TEXT",
+ "TLBP",
+ "TLBR",
+ "TLBWI",
+ "TLBWR",
+ "WORD",
+ "XOR",
+ "END",
+ "MOVV",
+ "MOVVL",
+ "MOVVR",
+ "SLLV",
+ "SRAV",
+ "SRLV",
+ "DIVV",
+ "DIVVU",
+ "REMV",
+ "REMVU",
+ "MULV",
+ "MULVU",
+ "ADDV",
+ "ADDVU",
+ "SUBV",
+ "SUBVU",
+ "DYNT",
+ "INIT",
+ "BCASE",
+ "CASE",
+ "TRUNCFV",
+ "TRUNCDV",
+ "TRUNCFW",
+ "TRUNCDW",
+ "MOVWU",
+ "SIGNAME",
+ "LAST",
+};
--- /dev/null
+++ b/utils/0c/gc.h
@@ -1,0 +1,332 @@
+#include "../cc/cc.h"
+#include "../vc/v.out.h"
+
+/*
+ * 0c/spim
+ * Mips 4000 little endian
+ */
+#define SZ_CHAR 1
+#define SZ_SHORT 2
+#define SZ_INT 4
+#define SZ_LONG 4
+#define SZ_IND 4
+#define SZ_FLOAT 4
+#define SZ_VLONG 8
+#define SZ_DOUBLE 8
+#define FNX 100
+
+typedef struct Adr Adr;
+typedef struct Prog Prog;
+typedef struct Case Case;
+typedef struct C1 C1;
+typedef struct Multab Multab;
+typedef struct Hintab Hintab;
+typedef struct Var Var;
+typedef struct Reg Reg;
+typedef struct Rgn Rgn;
+
+struct Adr
+{
+ long offset;
+ double dval;
+ vlong vval;
+ char sval[NSNAME];
+ Ieee ieee;
+
+ Sym* sym;
+ char type;
+ char reg;
+ char name;
+ char etype;
+};
+#define A ((Adr*)0)
+
+#define INDEXED 9
+struct Prog
+{
+ Adr from;
+ Adr to;
+ Prog* link;
+ long lineno;
+ char as;
+ char reg;
+};
+#define P ((Prog*)0)
+
+struct Case
+{
+ Case* link;
+ long val;
+ long label;
+ char def;
+};
+#define C ((Case*)0)
+
+struct C1
+{
+ long val;
+ long label;
+};
+
+struct Multab
+{
+ long val;
+ char code[20];
+};
+
+struct Hintab
+{
+ ushort val;
+ char hint[10];
+};
+
+struct Var
+{
+ long offset;
+ Sym* sym;
+ char name;
+ char etype;
+};
+
+struct Reg
+{
+ long pc;
+ long rpo; /* reverse post ordering */
+
+ Bits set;
+ Bits use1;
+ Bits use2;
+
+ Bits refbehind;
+ Bits refahead;
+ Bits calbehind;
+ Bits calahead;
+ Bits regdiff;
+ Bits act;
+
+ long regu;
+ long loop; /* could be shorter */
+
+ Reg* log5;
+ long active;
+
+ Reg* p1;
+ Reg* p2;
+ Reg* p2link;
+ Reg* s1;
+ Reg* s2;
+ Reg* link;
+ Prog* prog;
+};
+#define R ((Reg*)0)
+
+#define NRGN 600
+struct Rgn
+{
+ Reg* enter;
+ short cost;
+ short varno;
+ short regno;
+};
+
+EXTERN long breakpc;
+EXTERN Case* cases;
+EXTERN Node constnode;
+EXTERN Node fconstnode;
+EXTERN long continpc;
+EXTERN long curarg;
+EXTERN long cursafe;
+EXTERN Prog* firstp;
+EXTERN Prog* lastp;
+EXTERN long maxargsafe;
+EXTERN int mnstring;
+EXTERN Multab multab[20];
+EXTERN int retok;
+EXTERN int hintabsize;
+EXTERN Node* nodrat;
+EXTERN Node* nodret;
+EXTERN Node* nodsafe;
+EXTERN long nrathole;
+EXTERN long nstring;
+EXTERN Prog* p;
+EXTERN long pc;
+EXTERN Node regnode;
+EXTERN char string[NSNAME];
+EXTERN Sym* symrathole;
+EXTERN Node znode;
+EXTERN Prog zprog;
+EXTERN int reg[NREG+NREG];
+EXTERN long exregoffset;
+EXTERN long exfregoffset;
+
+#define BLOAD(r) band(bnot(r->refbehind), r->refahead)
+#define BSTORE(r) band(bnot(r->calbehind), r->calahead)
+#define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z])
+#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z])
+
+#define bset(a,n) ((a).b[(n)/32]&(1L<<(n)%32))
+
+#define CLOAD 4
+#define CREF 5
+#define CINF 1000
+#define LOOP 3
+
+EXTERN Rgn region[NRGN];
+EXTERN Rgn* rgp;
+EXTERN int nregion;
+EXTERN int nvar;
+
+EXTERN Bits externs;
+EXTERN Bits params;
+EXTERN Bits consts;
+EXTERN Bits addrs;
+
+EXTERN long regbits;
+EXTERN long exregbits;
+
+EXTERN int change;
+
+EXTERN Reg* firstr;
+EXTERN Reg* lastr;
+EXTERN Reg zreg;
+EXTERN Reg* freer;
+EXTERN Var var[NVAR];
+EXTERN long* idom;
+EXTERN Reg** rpo2r;
+EXTERN long maxnr;
+
+extern char* anames[];
+extern Hintab hintab[];
+
+/*
+ * sgen.c
+ */
+void codgen(Node*, Node*);
+void gen(Node*);
+void noretval(int);
+void xcom(Node*);
+void bcomplex(Node*);
+void usedset(Node*, int);
+
+/*
+ * cgen.c
+ */
+void cgen(Node*, Node*);
+void reglcgen(Node*, Node*, Node*);
+void lcgen(Node*, Node*);
+void bcgen(Node*, int);
+void boolgen(Node*, int, Node*);
+void sugen(Node*, Node*, long);
+void layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void ginit(void);
+void gclean(void);
+void nextpc(void);
+void gargs(Node*, Node*, Node*);
+void garg1(Node*, Node*, Node*, int, Node**);
+Node* nodconst(long);
+Node* nodfconst(double);
+void nodreg(Node*, Node*, int);
+void regret(Node*, Node*);
+void regalloc(Node*, Node*, Node*);
+void regfree(Node*);
+void regialloc(Node*, Node*, Node*);
+void regsalloc(Node*, Node*);
+void regaalloc1(Node*, Node*);
+void regaalloc(Node*, Node*);
+void regind(Node*, Node*);
+void gprep(Node*, Node*);
+void raddr(Node*, Prog*);
+void naddr(Node*, Adr*);
+void gmove(Node*, Node*);
+void gins(int a, Node*, Node*);
+void gopcode(int, Node*, Node*, Node*);
+int samaddr(Node*, Node*);
+void gbranch(int);
+void patch(Prog*, long);
+int sconst(Node*);
+int llconst(Node*);
+int sval(long);
+void gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int swcmp(void*, void*);
+void doswit(Node*);
+void swit1(C1*, int, long, Node*, Node*);
+void casf(void);
+void bitload(Node*, Node*, Node*, Node*, Node*);
+void bitstore(Node*, Node*, Node*, Node*, Node*);
+long outstring(char*, long);
+int mulcon(Node*, Node*);
+Multab* mulcon0(long);
+void nullwarn(Node*, Node*);
+void sextern(Sym*, Node*, long, long);
+void gextern(Sym*, Node*, long, long);
+void outcode(void);
+void ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void listinit(void);
+int Pconv(Fmt*);
+int Aconv(Fmt*);
+int Dconv(Fmt*);
+int Sconv(Fmt*);
+int Nconv(Fmt*);
+int Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg* rega(void);
+int rcmp(void*, void*);
+void regopt(Prog*);
+void addmove(Reg*, int, int, int);
+Bits mkvar(Adr*, int);
+void prop(Reg*, Bits, Bits);
+void loopit(Reg*, long);
+void synch(Reg*, Bits);
+ulong allreg(ulong, Rgn*);
+void paint1(Reg*, int);
+ulong paint2(Reg*, int);
+void paint3(Reg*, int, long, int);
+void addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void peep(void);
+void excise(Reg*);
+Reg* uniqp(Reg*);
+Reg* uniqs(Reg*);
+int regtyp(Adr*);
+int regzer(Adr*);
+int anyvar(Adr*);
+int subprop(Reg*);
+int copyprop(Reg*);
+int copy1(Adr*, Adr*, Reg*, int);
+int copyu(Prog*, Adr*, Adr*);
+
+int copyas(Adr*, Adr*);
+int copyau(Adr*, Adr*);
+int copyau1(Prog*, Adr*);
+int copysub(Adr*, Adr*, Adr*, int);
+int copysub1(Prog*, Adr*, Adr*, int);
+
+long RtoB(int);
+long FtoB(int);
+int BtoR(long);
+int BtoF(long);
+
+#pragma varargck type "A" int
+#pragma varargck type "B" Bits
+#pragma varargck type "D" Adr*
+#pragma varargck type "N" Adr*
+#pragma varargck type "P" Prog*
+#pragma varargck type "S" char*
--- /dev/null
+++ b/utils/0c/list.c
@@ -1,0 +1,244 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+ fmtinstall('A', Aconv);
+ fmtinstall('P', Pconv);
+ fmtinstall('S', Sconv);
+ fmtinstall('N', Nconv);
+ fmtinstall('B', Bconv);
+ fmtinstall('D', Dconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+ char str[STRINGSZ], ss[STRINGSZ], *s;
+ Bits bits;
+ int i;
+
+ str[0] = 0;
+ bits = va_arg(fp->args, Bits);
+ while(bany(&bits)) {
+ i = bnum(bits);
+ if(str[0])
+ strcat(str, " ");
+ if(var[i].sym == S) {
+ sprint(ss, "$%ld", var[i].offset);
+ s = ss;
+ } else
+ s = var[i].sym->name;
+ if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+ break;
+ strcat(str, s);
+ bits.b[i/32] &= ~(1L << (i%32));
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Prog *p;
+ int a;
+
+ p = va_arg(fp->args, Prog*);
+ a = p->as;
+ if(a == ADATA)
+ sprint(str, " %A %D/%d,%D", a, &p->from, p->reg, &p->to);
+ else
+ if(p->as == ATEXT)
+ sprint(str, " %A %D,%d,%D", a, &p->from, p->reg, &p->to);
+ else
+ if(p->reg == NREG)
+ sprint(str, " %A %D,%D", a, &p->from, &p->to);
+ else
+ if(p->from.type != D_FREG)
+ sprint(str, " %A %D,R%d,%D", a, &p->from, p->reg, &p->to);
+ else
+ sprint(str, " %A %D,F%d,%D", a, &p->from, p->reg, &p->to);
+ return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+ char *s;
+ int a;
+
+ a = va_arg(fp->args, int);
+ s = "?";
+ if(a >= AXXX && a < ALAST)
+ s = anames[a];
+ return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+
+ a = va_arg(fp->args, Adr*);
+ switch(a->type) {
+
+ default:
+ sprint(str, "GOK-type(%d)", a->type);
+ break;
+
+ case D_NONE:
+ str[0] = 0;
+ if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+ sprint(str, "%N(R%d)(NONE)", a, a->reg);
+ break;
+
+ case D_CONST:
+ if(a->reg != NREG)
+ sprint(str, "$%N(R%d)", a, a->reg);
+ else
+ sprint(str, "$%N", a);
+ break;
+
+ case D_OREG:
+ if(a->reg != NREG)
+ sprint(str, "%N(R%d)", a, a->reg);
+ else
+ sprint(str, "%N", a);
+ break;
+
+ case D_REG:
+ sprint(str, "R%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_FREG:
+ sprint(str, "F%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_FCREG:
+ sprint(str, "FCR%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_LO:
+ sprint(str, "LO");
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(LO)(REG)", a);
+ break;
+
+ case D_HI:
+ sprint(str, "HI");
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(HI)(REG)", a);
+ break;
+
+ case D_BRANCH:
+ sprint(str, "%ld(PC)", a->offset-pc);
+ break;
+
+ case D_FCONST:
+ sprint(str, "$%.17e", a->dval);
+ break;
+
+ case D_SCONST:
+ sprint(str, "$\"%S\"", a->sval);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+ int i, c;
+ char str[STRINGSZ], *p, *a;
+
+ a = va_arg(fp->args, char*);
+ p = str;
+ for(i=0; i<NSNAME; i++) {
+ c = a[i] & 0xff;
+ if(c >= 'a' && c <= 'z' ||
+ c >= 'A' && c <= 'Z' ||
+ c >= '0' && c <= '9' ||
+ c == ' ' || c == '%') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch(c) {
+ case 0:
+ *p++ = 'z';
+ continue;
+ case '\\':
+ case '"':
+ *p++ = c;
+ continue;
+ case '\n':
+ *p++ = 'n';
+ continue;
+ case '\t':
+ *p++ = 't';
+ continue;
+ case '\r':
+ *p++ = 'r';
+ continue;
+ case '\f':
+ *p++ = 'f';
+ continue;
+ }
+ *p++ = (c>>6) + '0';
+ *p++ = ((c>>3) & 7) + '0';
+ *p++ = (c & 7) + '0';
+ }
+ *p = 0;
+ return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+ Sym *s;
+
+ a = va_arg(fp->args, Adr*);
+ s = a->sym;
+ if(s == S) {
+ sprint(str, "%ld", a->offset);
+ goto out;
+ }
+ switch(a->name) {
+ default:
+ sprint(str, "GOK-name(%d)", a->name);
+ break;
+
+ case D_NONE:
+ sprint(str, "%ld", a->offset);
+ break;
+
+ case D_EXTERN:
+ sprint(str, "%s+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_STATIC:
+ sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_AUTO:
+ sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+ break;
+
+ case D_PARAM:
+ sprint(str, "%s+%ld(FP)", s->name, a->offset);
+ break;
+ }
+out:
+ return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/0c/mkenam
@@ -1,0 +1,15 @@
+ed - ../vc/v.out.h <<'!'
+v/^ A/d
+,s/^ A/ "/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char* anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/0c/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=0c
+
+OFILES= cgen.$O\
+ enam.$O\
+ list.$O\
+ peep.$O\
+ reg.$O\
+ sgen.$O\
+ swt.$O\
+ txt.$O\
+ mul.$O\
+
+HFILES= gc.h\
+ v.out.h\
+ ../cc/cc.h\
+
+LIBS=cc bio 9 # order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS= $CFLAGS -I../include
+
+$ROOT/$OBJDIR/lib/libcc.a:
+ cd ../cc
+ mk $MKFLAGS install
+ mk $MKFLAGS clean
--- /dev/null
+++ b/utils/0c/mul.c
@@ -1,0 +1,608 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ * lsl $(A-'a'),r0,r1
+ * [+][0-7]
+ * add r0,r1,r2
+ * [-][0-7]
+ * sub r0,r1,r2
+ */
+
+static int multabp;
+static long mulval;
+static char* mulcp;
+static long valmax;
+static int shmax;
+
+static int docode(char *hp, char *cp, int r0, int r1);
+static int gen1(int len);
+static int gen2(int len, long r1);
+static int gen3(int len, long r0, long r1, int flag);
+enum
+{
+ SR1 = 1<<0, /* r1 has been shifted */
+ SR0 = 1<<1, /* r0 has been shifted */
+ UR1 = 1<<2, /* r1 has not been used */
+ UR0 = 1<<3, /* r0 has not been used */
+};
+
+Multab*
+mulcon0(long v)
+{
+ int a1, a2, g;
+ Multab *m, *m1;
+ char hint[10];
+
+ if(v < 0)
+ v = -v;
+
+ /*
+ * look in cache
+ */
+ m = multab;
+ for(g=0; g<nelem(multab); g++) {
+ if(m->val == v) {
+ if(m->code[0] == 0)
+ return 0;
+ return m;
+ }
+ m++;
+ }
+
+ /*
+ * select a spot in cache to overwrite
+ */
+ multabp++;
+ if(multabp < 0 || multabp >= nelem(multab))
+ multabp = 0;
+ m = multab+multabp;
+ m->val = v;
+ mulval = v;
+
+ /*
+ * look in execption hint table
+ */
+ a1 = 0;
+ a2 = hintabsize;
+ for(;;) {
+ if(a1 >= a2)
+ goto no;
+ g = (a2 + a1)/2;
+ if(v < hintab[g].val) {
+ a2 = g;
+ continue;
+ }
+ if(v > hintab[g].val) {
+ a1 = g+1;
+ continue;
+ }
+ break;
+ }
+
+ if(docode(hintab[g].hint, m->code, 1, 0))
+ return m;
+ print("multiply table failure %ld\n", v);
+ m->code[0] = 0;
+ return 0;
+
+no:
+ /*
+ * try to search
+ */
+ hint[0] = 0;
+ for(g=1; g<=6; g++) {
+ if(g >= 6 && v >= 65535)
+ break;
+ mulcp = hint+g;
+ *mulcp = 0;
+ if(gen1(g)) {
+ if(docode(hint, m->code, 1, 0))
+ return m;
+ print("multiply table failure %ld\n", v);
+ break;
+ }
+ }
+
+ /*
+ * try a recur followed by a shift
+ */
+ g = 0;
+ while(!(v & 1)) {
+ g++;
+ v >>= 1;
+ }
+ if(g) {
+ m1 = mulcon0(v);
+ if(m1) {
+ strcpy(m->code, m1->code);
+ sprint(strchr(m->code, 0), "%c0", g+'a');
+ return m;
+ }
+ }
+ m->code[0] = 0;
+ return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+ int c, i;
+
+ c = *hp++;
+ *cp = c;
+ cp += 2;
+ switch(c) {
+ default:
+ c -= 'a';
+ if(c < 1 || c >= 30)
+ break;
+ for(i=0; i<4; i++) {
+ switch(i) {
+ case 0:
+ if(docode(hp, cp, r0<<c, r1))
+ goto out;
+ break;
+ case 1:
+ if(docode(hp, cp, r1<<c, r1))
+ goto out;
+ break;
+ case 2:
+ if(docode(hp, cp, r0, r0<<c))
+ goto out;
+ break;
+ case 3:
+ if(docode(hp, cp, r0, r1<<c))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case '+':
+ for(i=0; i<8; i++) {
+ cp[-1] = i+'0';
+ switch(i) {
+ case 1:
+ if(docode(hp, cp, r0+r1, r1))
+ goto out;
+ break;
+ case 5:
+ if(docode(hp, cp, r0, r0+r1))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case '-':
+ for(i=0; i<8; i++) {
+ cp[-1] = i+'0';
+ switch(i) {
+ case 1:
+ if(docode(hp, cp, r0-r1, r1))
+ goto out;
+ break;
+ case 2:
+ if(docode(hp, cp, r1-r0, r1))
+ goto out;
+ break;
+ case 5:
+ if(docode(hp, cp, r0, r0-r1))
+ goto out;
+ break;
+ case 6:
+ if(docode(hp, cp, r0, r1-r0))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case 0:
+ if(r0 == mulval)
+ return 1;
+ }
+ return 0;
+
+out:
+ cp[-1] = i+'0';
+ return 1;
+}
+
+static int
+gen1(int len)
+{
+ int i;
+
+ for(shmax=1; shmax<30; shmax++) {
+ valmax = 1<<shmax;
+ if(valmax >= mulval)
+ break;
+ }
+ if(mulval == 1)
+ return 1;
+
+ len--;
+ for(i=1; i<=shmax; i++)
+ if(gen2(len, 1<<i)) {
+ *--mulcp = 'a'+i;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+ int i;
+
+ if(len <= 0) {
+ if(r1 == mulval)
+ return 1;
+ return 0;
+ }
+
+ len--;
+ if(len == 0)
+ goto calcr0;
+
+ if(gen3(len, r1, r1+1, UR1)) {
+ i = '+';
+ goto out;
+ }
+ if(gen3(len, r1-1, r1, UR0)) {
+ i = '-';
+ goto out;
+ }
+ if(gen3(len, 1, r1+1, UR1)) {
+ i = '+';
+ goto out;
+ }
+ if(gen3(len, 1, r1-1, UR1)) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+calcr0:
+ if(mulval == r1+1) {
+ i = '+';
+ goto out;
+ }
+ if(mulval == r1-1) {
+ i = '-';
+ goto out;
+ }
+ return 0;
+
+out:
+ *--mulcp = i;
+ return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+ int i, f1, f2;
+ long x;
+
+ if(r0 <= 0 ||
+ r0 >= r1 ||
+ r1 > valmax)
+ return 0;
+
+ len--;
+ if(len == 0)
+ goto calcr0;
+
+ if(!(flag & UR1)) {
+ f1 = UR1|SR1;
+ for(i=1; i<=shmax; i++) {
+ x = r0<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r0, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & UR0)) {
+ f1 = UR1|SR1;
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r1, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & SR1)) {
+ f1 = UR1|SR1|(flag&UR0);
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r0, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & SR0)) {
+ f1 = UR0|SR0|(flag&(SR1|UR1));
+
+ f2 = UR1|SR1;
+ if(flag & UR1)
+ f2 |= UR0;
+ if(flag & SR1)
+ f2 |= SR0;
+
+ for(i=1; i<=shmax; i++) {
+ x = r0<<i;
+ if(x > valmax)
+ break;
+ if(x > r1) {
+ if(gen3(len, r1, x, f2)) {
+ i += 'a';
+ goto out;
+ }
+ } else
+ if(gen3(len, x, r1, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ x = r1+r0;
+ if(gen3(len, r0, x, UR1)) {
+ i = '+';
+ goto out;
+ }
+
+ if(gen3(len, r1, x, UR1)) {
+ i = '+';
+ goto out;
+ }
+
+ x = r1-r0;
+ if(gen3(len, x, r1, UR0)) {
+ i = '-';
+ goto out;
+ }
+
+ if(x > r0) {
+ if(gen3(len, r0, x, UR1)) {
+ i = '-';
+ goto out;
+ }
+ } else
+ if(gen3(len, x, r0, UR0)) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+calcr0:
+ f1 = flag & (UR0|UR1);
+ if(f1 == UR1) {
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x >= mulval) {
+ if(x == mulval) {
+ i += 'a';
+ goto out;
+ }
+ break;
+ }
+ }
+ }
+
+ if(mulval == r1+r0) {
+ i = '+';
+ goto out;
+ }
+ if(mulval == r1-r0) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+out:
+ *--mulcp = i;
+ return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ * all numbers
+ * <5000:
+ * ÷ by 5
+ * <10000:
+ * ÷ by 50
+ * <65536:
+ * ÷ by 250
+ */
+Hintab hintab[] =
+{
+ 683, "b++d+e+",
+ 687, "b+e++e-",
+ 691, "b++d+e+",
+ 731, "b++d+e+",
+ 811, "b++d+i+",
+ 821, "b++e+e+",
+ 843, "b+d++e+",
+ 851, "b+f-+e-",
+ 853, "b++e+e+",
+ 877, "c++++g-",
+ 933, "b+c++g-",
+ 981, "c-+e-d+",
+ 1375, "b+c+b+h-",
+ 1675, "d+b++h+",
+ 2425, "c++f-e+",
+ 2675, "c+d++f-",
+ 2750, "b+d-b+h-",
+ 2775, "c-+g-e-",
+ 3125, "b++e+g+",
+ 3275, "b+c+g+e+",
+ 3350, "c++++i+",
+ 3475, "c-+e-f-",
+ 3525, "c-+d+g-",
+ 3625, "c-+e-j+",
+ 3675, "b+d+d+e+",
+ 3725, "b+d-+h+",
+ 3925, "b+d+f-d-",
+ 4275, "b+g++e+",
+ 4325, "b+h-+d+",
+ 4425, "b+b+g-j-",
+ 4525, "b+d-d+f+",
+ 4675, "c++d-g+",
+ 4775, "b+d+b+g-",
+ 4825, "c+c-+i-",
+ 4850, "c++++i-",
+ 4925, "b++e-g-",
+ 4975, "c+f++e-",
+ 5500, "b+g-c+d+",
+ 6700, "d+b++i+",
+ 9700, "d++++j-",
+ 11000, "b+f-c-h-",
+ 11750, "b+d+g+j-",
+ 12500, "b+c+e-k+",
+ 13250, "b+d+e-f+",
+ 13750, "b+h-c-d+",
+ 14250, "b+g-c+e-",
+ 14500, "c+f+j-d-",
+ 14750, "d-g--f+",
+ 16750, "b+e-d-n+",
+ 17750, "c+h-b+e+",
+ 18250, "d+b+h-d+",
+ 18750, "b+g-++f+",
+ 19250, "b+e+b+h+",
+ 19750, "b++h--f-",
+ 20250, "b+e-l-c+",
+ 20750, "c++bi+e-",
+ 21250, "b+i+l+c+",
+ 22000, "b+e+d-g-",
+ 22250, "b+d-h+k-",
+ 22750, "b+d-e-g+",
+ 23250, "b+c+h+e-",
+ 23500, "b+g-c-g-",
+ 23750, "b+g-b+h-",
+ 24250, "c++g+m-",
+ 24750, "b+e+e+j-",
+ 25000, "b++dh+g+",
+ 25250, "b+e+d-g-",
+ 25750, "b+e+b+j+",
+ 26250, "b+h+c+e+",
+ 26500, "b+h+c+g+",
+ 26750, "b+d+e+g-",
+ 27250, "b+e+e+f+",
+ 27500, "c-i-c-d+",
+ 27750, "b+bd++j+",
+ 28250, "d-d-++i-",
+ 28500, "c+c-h-e-",
+ 29000, "b+g-d-f+",
+ 29500, "c+h+++e-",
+ 29750, "b+g+f-c+",
+ 30250, "b+f-g-c+",
+ 33500, "c-f-d-n+",
+ 33750, "b+d-b+j-",
+ 34250, "c+e+++i+",
+ 35250, "e+b+d+k+",
+ 35500, "c+e+d-g-",
+ 35750, "c+i-++e+",
+ 36250, "b+bh-d+e+",
+ 36500, "c+c-h-e-",
+ 36750, "d+e--i+",
+ 37250, "b+g+g+b+",
+ 37500, "b+h-b+f+",
+ 37750, "c+be++j-",
+ 38500, "b+e+b+i+",
+ 38750, "d+i-b+d+",
+ 39250, "b+g-l-+d+",
+ 39500, "b+g-c+g-",
+ 39750, "b+bh-c+f-",
+ 40250, "b+bf+d+g-",
+ 40500, "b+g-c+g+",
+ 40750, "c+b+i-e+",
+ 41250, "d++bf+h+",
+ 41500, "b+j+c+d-",
+ 41750, "c+f+b+h-",
+ 42500, "c+h++g+",
+ 42750, "b+g+d-f-",
+ 43250, "b+l-e+d-",
+ 43750, "c+bd+h+f-",
+ 44000, "b+f+g-d-",
+ 44250, "b+d-g--f+",
+ 44500, "c+e+c+h+",
+ 44750, "b+e+d-h-",
+ 45250, "b++g+j-g+",
+ 45500, "c+d+e-g+",
+ 45750, "b+d-h-e-",
+ 46250, "c+bd++j+",
+ 46500, "b+d-c-j-",
+ 46750, "e-e-b+g-",
+ 47000, "b+c+d-j-",
+ 47250, "b+e+e-g-",
+ 47500, "b+g-c-h-",
+ 47750, "b+f-c+h-",
+ 48250, "d--h+n-",
+ 48500, "b+c-g+m-",
+ 48750, "b+e+e-g+",
+ 49500, "c-f+e+j-",
+ 49750, "c+c+g++f-",
+ 50000, "b+e+e+k+",
+ 50250, "b++i++g+",
+ 50500, "c+g+f-i+",
+ 50750, "b+e+d+k-",
+ 51500, "b+i+c-f+",
+ 51750, "b+bd+g-e-",
+ 52250, "b+d+g-j+",
+ 52500, "c+c+f+g+",
+ 52750, "b+c+e+i+",
+ 53000, "b+i+c+g+",
+ 53500, "c+g+g-n+",
+ 53750, "b+j+d-c+",
+ 54250, "b+d-g-j-",
+ 54500, "c-f+e+f+",
+ 54750, "b+f-+c+g+",
+ 55000, "b+g-d-g-",
+ 55250, "b+e+e+g+",
+ 55500, "b+cd++j+",
+ 55750, "b+bh-d-f-",
+ 56250, "c+d-b+j-",
+ 56500, "c+d+c+i+",
+ 56750, "b+e+d++h-",
+ 57000, "b+d+g-f+",
+ 57250, "b+f-m+d-",
+ 57750, "b+i+c+e-",
+ 58000, "b+e+d+h+",
+ 58250, "c+b+g+g+",
+ 58750, "d-e-j--e+",
+ 59000, "d-i-+e+",
+ 59250, "e--h-m+",
+ 59500, "c+c-h+f-",
+ 59750, "b+bh-e+i-",
+ 60250, "b+bh-e-e-",
+ 60500, "c+c-g-g-",
+ 60750, "b+e-l-e-",
+ 61250, "b+g-g-c+",
+ 61750, "b+g-c+g+",
+ 62250, "f--+c-i-",
+ 62750, "e+f--+g+",
+ 64750, "b+f+d+p-",
+};
+int hintabsize = nelem(hintab);
--- /dev/null
+++ b/utils/0c/peep.c
@@ -1,0 +1,695 @@
+#include "gc.h"
+
+void
+peep(void)
+{
+ Reg *r, *r1, *r2;
+ Prog *p, *p1;
+ int t;
+/*
+ * complete R structure
+ */
+ t = 0;
+ for(r=firstr; r!=R; r=r1) {
+ r1 = r->link;
+ if(r1 == R)
+ break;
+ p = r->prog->link;
+ while(p != r1->prog)
+ switch(p->as) {
+ default:
+ r2 = rega();
+ r->link = r2;
+ r2->link = r1;
+
+ r2->prog = p;
+ r2->p1 = r;
+ r->s1 = r2;
+ r2->s1 = r1;
+ r1->p1 = r2;
+
+ r = r2;
+ t++;
+
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ p = p->link;
+ }
+ }
+
+loop1:
+ t = 0;
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ if(p->as == AMOVW || p->as == AMOVF || p->as == AMOVD)
+ if(regtyp(&p->to)) {
+ if(regtyp(&p->from))
+ if(p->from.type == p->to.type) {
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ } else
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ if(regzer(&p->from))
+ if(p->to.type == D_REG) {
+ p->from.type = D_REG;
+ p->from.reg = 0;
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ } else
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ }
+ }
+ if(t)
+ goto loop1;
+ /*
+ * look for MOVB x,R; MOVB R,R
+ */
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ default:
+ continue;
+ case AMOVH:
+ case AMOVHU:
+ case AMOVB:
+ case AMOVBU:
+ if(p->to.type != D_REG)
+ continue;
+ break;
+ }
+ r1 = r->link;
+ if(r1 == R)
+ continue;
+ p1 = r1->prog;
+ if(p1->as != p->as)
+ continue;
+ if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+ continue;
+ if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+ continue;
+ excise(r1);
+ }
+}
+
+void
+excise(Reg *r)
+{
+ Prog *p;
+
+ p = r->prog;
+ p->as = ANOP;
+ p->from = zprog.from;
+ p->to = zprog.to;
+ p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+ Reg *r1;
+
+ r1 = r->p1;
+ if(r1 == R) {
+ r1 = r->p2;
+ if(r1 == R || r1->p2link != R)
+ return R;
+ } else
+ if(r->p2 != R)
+ return R;
+ return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+ Reg *r1;
+
+ r1 = r->s1;
+ if(r1 == R) {
+ r1 = r->s2;
+ if(r1 == R)
+ return R;
+ } else
+ if(r->s2 != R)
+ return R;
+ return r1;
+}
+
+int
+regzer(Adr *a)
+{
+
+ if(a->type == D_CONST)
+ if(a->sym == S)
+ if(a->offset == 0)
+ return 1;
+ if(a->type == D_REG)
+ if(a->reg == 0)
+ return 1;
+ return 0;
+}
+
+int
+regtyp(Adr *a)
+{
+
+ if(a->type == D_REG) {
+ if(a->reg != 0)
+ return 1;
+ return 0;
+ }
+ if(a->type == D_FREG)
+ return 1;
+ return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ * MOV a, R0
+ * ADD b, R0 / no use of R1
+ * MOV R0, R1
+ * would be converted to
+ * MOV a, R1
+ * ADD b, R1
+ * MOV R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+ Prog *p;
+ Adr *v1, *v2;
+ Reg *r;
+ int t;
+
+ p = r0->prog;
+ v1 = &p->from;
+ if(!regtyp(v1))
+ return 0;
+ v2 = &p->to;
+ if(!regtyp(v2))
+ return 0;
+ for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+ if(uniqs(r) == R)
+ break;
+ p = r->prog;
+ switch(p->as) {
+ case AJAL:
+ return 0;
+
+ case ASGT:
+ case ASGTU:
+
+ case AADD:
+ case AADDU:
+ case ASUB:
+ case ASUBU:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AOR:
+ case AAND:
+ case AXOR:
+ case AMUL:
+ case AMULU:
+ case ADIV:
+ case ADIVU:
+
+ case AADDD:
+ case AADDF:
+ case ASUBD:
+ case ASUBF:
+ case AMULD:
+ case AMULF:
+ case ADIVD:
+ case ADIVF:
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg) {
+ if(p->reg == NREG)
+ p->reg = p->to.reg;
+ goto gotit;
+ }
+ break;
+
+ case AMOVF:
+ case AMOVD:
+ case AMOVW:
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg)
+ goto gotit;
+ break;
+ }
+ if(copyau(&p->from, v2) ||
+ copyau1(p, v2) ||
+ copyau(&p->to, v2))
+ break;
+ if(copysub(&p->from, v1, v2, 0) ||
+ copysub1(p, v1, v2, 0) ||
+ copysub(&p->to, v1, v2, 0))
+ break;
+ }
+ return 0;
+
+gotit:
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P']) {
+ print("gotit: %D->%D\n%P", v1, v2, r->prog);
+ if(p->from.type == v2->type)
+ print(" excise");
+ print("\n");
+ }
+ for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+ p = r->prog;
+ copysub(&p->from, v1, v2, 1);
+ copysub1(p, v1, v2, 1);
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P'])
+ print("%P\n", r->prog);
+ }
+ t = v1->reg;
+ v1->reg = v2->reg;
+ v2->reg = t;
+ if(debug['P'])
+ print("%P last\n", r->prog);
+ return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * use v2 return fail
+ * -----------------
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * set v2 return success
+ */
+int
+copyprop(Reg *r0)
+{
+ Prog *p;
+ Adr *v1, *v2;
+ Reg *r;
+
+ p = r0->prog;
+ v1 = &p->from;
+ v2 = &p->to;
+ if(copyas(v1, v2))
+ return 1;
+ for(r=firstr; r!=R; r=r->link)
+ r->active = 0;
+ return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+ int t;
+ Prog *p;
+
+ if(r->active) {
+ if(debug['P'])
+ print("act set; return 1\n");
+ return 1;
+ }
+ r->active = 1;
+ if(debug['P'])
+ print("copy %D->%D f=%d\n", v1, v2, f);
+ for(; r != R; r = r->s1) {
+ p = r->prog;
+ if(debug['P'])
+ print("%P", p);
+ if(!f && uniqp(r) == R) {
+ f = 1;
+ if(debug['P'])
+ print("; merge; f=%d", f);
+ }
+ t = copyu(p, v2, A);
+ switch(t) {
+ case 2: /* rar, cant split */
+ if(debug['P'])
+ print("; %Drar; return 0\n", v2);
+ return 0;
+
+ case 3: /* set */
+ if(debug['P'])
+ print("; %Dset; return 1\n", v2);
+ return 1;
+
+ case 1: /* used, substitute */
+ case 4: /* use and set */
+ if(f) {
+ if(!debug['P'])
+ return 0;
+ if(t == 4)
+ print("; %Dused+set and f=%d; return 0\n", v2, f);
+ else
+ print("; %Dused and f=%d; return 0\n", v2, f);
+ return 0;
+ }
+ if(copyu(p, v2, v1)) {
+ if(debug['P'])
+ print("; sub fail; return 0\n");
+ return 0;
+ }
+ if(debug['P'])
+ print("; sub%D/%D", v2, v1);
+ if(t == 4) {
+ if(debug['P'])
+ print("; %Dused+set; return 1\n", v2);
+ return 1;
+ }
+ break;
+ }
+ if(!f) {
+ t = copyu(p, v1, A);
+ if(!f && (t == 2 || t == 3 || t == 4)) {
+ f = 1;
+ if(debug['P'])
+ print("; %Dset and !f; f=%d", v1, f);
+ }
+ }
+ if(debug['P'])
+ print("\n");
+ if(r->s2)
+ if(!copy1(v1, v2, r->s2, f))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+ switch(p->as) {
+
+ default:
+ if(debug['P'])
+ print(" (?)");
+ return 2;
+
+
+ case ANOP: /* read, write */
+ case AMOVW:
+ case AMOVF:
+ case AMOVD:
+ case AMOVH:
+ case AMOVHU:
+ case AMOVB:
+ case AMOVBU:
+ case AMOVDW:
+ case AMOVWD:
+ case AMOVFD:
+ case AMOVDF:
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ if(!copyas(&p->to, v))
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v)) {
+ if(copyau(&p->from, v))
+ return 4;
+ return 3;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ASGT: /* read, read, write */
+ case ASGTU:
+
+ case AADD:
+ case AADDU:
+ case ASUB:
+ case ASUBU:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AOR:
+ case ANOR:
+ case AAND:
+ case AXOR:
+ case AMUL:
+ case AMULU:
+ case ADIV:
+ case ADIVU:
+
+ case AADDF:
+ case AADDD:
+ case ASUBF:
+ case ASUBD:
+ case AMULF:
+ case AMULD:
+ case ADIVF:
+ case ADIVD:
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ if(copysub1(p, v, s, 1))
+ return 1;
+ if(!copyas(&p->to, v))
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v)) {
+ if(p->reg == NREG)
+ p->reg = p->to.reg;
+ if(copyau(&p->from, v))
+ return 4;
+ if(copyau1(p, v))
+ return 4;
+ return 3;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau1(p, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ABEQ: /* read, read */
+ case ABNE:
+ case ABGTZ:
+ case ABGEZ:
+ case ABLTZ:
+ case ABLEZ:
+
+ case ACMPEQD:
+ case ACMPEQF:
+ case ACMPGED:
+ case ACMPGEF:
+ case ACMPGTD:
+ case ACMPGTF:
+ case ABFPF:
+ case ABFPT:
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return copysub1(p, v, s, 1);
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau1(p, v))
+ return 1;
+ return 0;
+
+ case AJMP: /* funny */
+ if(s != A) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ARET: /* funny */
+ if(v->type == D_REG)
+ if(v->reg == REGRET)
+ return 2;
+ if(v->type == D_FREG)
+ if(v->reg == FREGRET)
+ return 2;
+
+ case AJAL: /* funny */
+ if(v->type == D_REG) {
+ if(v->reg <= REGEXT && v->reg > exregoffset)
+ return 2;
+ if(REGARG && v->reg == REGARG)
+ return 2;
+ }
+ if(v->type == D_FREG)
+ if(v->reg <= FREGEXT && v->reg > exfregoffset)
+ return 2;
+
+ if(s != A) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 4;
+ return 3;
+
+ case ATEXT: /* funny */
+ if(v->type == D_REG)
+ if(v->reg == REGARG)
+ return 3;
+ return 0;
+ }
+ /* not reached */
+}
+
+int
+a2type(Prog *p)
+{
+
+ switch(p->as) {
+ case ABEQ:
+ case ABNE:
+ case ABGTZ:
+ case ABGEZ:
+ case ABLTZ:
+ case ABLEZ:
+
+ case ASGT:
+ case ASGTU:
+
+ case AADD:
+ case AADDU:
+ case ASUB:
+ case ASUBU:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AOR:
+ case AAND:
+ case AXOR:
+ case AMUL:
+ case AMULU:
+ case ADIV:
+ case ADIVU:
+ return D_REG;
+
+ case ACMPEQD:
+ case ACMPEQF:
+ case ACMPGED:
+ case ACMPGEF:
+ case ACMPGTD:
+ case ACMPGTF:
+
+ case AADDF:
+ case AADDD:
+ case ASUBF:
+ case ASUBD:
+ case AMULF:
+ case AMULD:
+ case ADIVF:
+ case ADIVD:
+ return D_FREG;
+ }
+ return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+ if(regtyp(v))
+ if(a->type == v->type)
+ if(a->reg == v->reg)
+ return 1;
+ return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+ if(copyas(a, v))
+ return 1;
+ if(v->type == D_REG)
+ if(a->type == D_OREG)
+ if(v->reg == a->reg)
+ return 1;
+ return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+ if(regtyp(v))
+ if(p->from.type == v->type || p->to.type == v->type)
+ if(p->reg == v->reg) {
+ if(a2type(p) != v->type)
+ print("botch a2type %P\n", p);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+ if(f)
+ if(copyau(a, v))
+ a->reg = s->reg;
+ return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+ if(f)
+ if(copyau1(p1, v))
+ p1->reg = s->reg;
+ return 0;
+}
--- /dev/null
+++ b/utils/0c/reg.c
@@ -1,0 +1,1152 @@
+#include "gc.h"
+
+void addsplits(void);
+
+Reg*
+rega(void)
+{
+ Reg *r;
+
+ r = freer;
+ if(r == R) {
+ r = alloc(sizeof(*r));
+ } else
+ freer = r->link;
+
+ *r = zreg;
+ return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+ Rgn *p1, *p2;
+ int c1, c2;
+
+ p1 = (Rgn*)a1;
+ p2 = (Rgn*)a2;
+ c1 = p2->cost;
+ c2 = p1->cost;
+ if(c1 -= c2)
+ return c1;
+ return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+ Reg *r, *r1, *r2;
+ Prog *p1;
+ int i, z;
+ long initpc, val, npc;
+ ulong vreg;
+ Bits bit;
+ struct
+ {
+ long m;
+ long c;
+ Reg* p;
+ } log5[6], *lp;
+
+ firstr = R;
+ lastr = R;
+ nvar = 0;
+ regbits = 0;
+ for(z=0; z<BITS; z++) {
+ externs.b[z] = 0;
+ params.b[z] = 0;
+ consts.b[z] = 0;
+ addrs.b[z] = 0;
+ }
+
+ /*
+ * pass 1
+ * build aux data structure
+ * allocate pcs
+ * find use and set of variables
+ */
+ val = 5L * 5L * 5L * 5L * 5L;
+ lp = log5;
+ for(i=0; i<5; i++) {
+ lp->m = val;
+ lp->c = 0;
+ lp->p = R;
+ val /= 5L;
+ lp++;
+ }
+ val = 0;
+ for(; p != P; p = p->link) {
+ switch(p->as) {
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ continue;
+ }
+ r = rega();
+ if(firstr == R) {
+ firstr = r;
+ lastr = r;
+ } else {
+ lastr->link = r;
+ r->p1 = lastr;
+ lastr->s1 = r;
+ lastr = r;
+ }
+ r->prog = p;
+ r->pc = val;
+ val++;
+
+ lp = log5;
+ for(i=0; i<5; i++) {
+ lp->c--;
+ if(lp->c <= 0) {
+ lp->c = lp->m;
+ if(lp->p != R)
+ lp->p->log5 = r;
+ lp->p = r;
+ (lp+1)->c = 0;
+ break;
+ }
+ lp++;
+ }
+
+ r1 = r->p1;
+ if(r1 != R)
+ switch(r1->prog->as) {
+ case ARET:
+ case AJMP:
+ case ARFE:
+ r->p1 = R;
+ r1->s1 = R;
+ }
+
+ /*
+ * left side always read
+ */
+ bit = mkvar(&p->from, p->as==AMOVW);
+ for(z=0; z<BITS; z++)
+ r->use1.b[z] |= bit.b[z];
+
+ /*
+ * right side depends on opcode
+ */
+ bit = mkvar(&p->to, 0);
+ if(bany(&bit))
+ switch(p->as) {
+ default:
+ diag(Z, "reg: unknown asop: %A", p->as);
+ break;
+
+ /*
+ * right side write
+ */
+ case ANOP:
+ case AMOVB:
+ case AMOVBU:
+ case AMOVH:
+ case AMOVHU:
+ case AMOVW:
+ case AMOVV:
+ case AMOVF:
+ case AMOVD:
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
+ break;
+
+ /*
+ * funny
+ */
+ case AJAL:
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ break;
+ }
+ }
+ if(firstr == R)
+ return;
+ initpc = pc - val;
+ npc = val;
+
+ /*
+ * pass 2
+ * turn branch references to pointers
+ * build back pointers
+ */
+ for(r = firstr; r != R; r = r->link) {
+ p = r->prog;
+ if(p->to.type == D_BRANCH) {
+ val = p->to.offset - initpc;
+ r1 = firstr;
+ while(r1 != R) {
+ r2 = r1->log5;
+ if(r2 != R && val >= r2->pc) {
+ r1 = r2;
+ continue;
+ }
+ if(r1->pc == val)
+ break;
+ r1 = r1->link;
+ }
+ if(r1 == R) {
+ nearln = p->lineno;
+ diag(Z, "ref not found\n%P", p);
+ continue;
+ }
+ if(r1 == r) {
+ nearln = p->lineno;
+ diag(Z, "ref to self\n%P", p);
+ continue;
+ }
+ r->s2 = r1;
+ r->p2link = r1->p2;
+ r1->p2 = r;
+ }
+ }
+ if(debug['R']) {
+ p = firstr->prog;
+ print("\n%L %D\n", p->lineno, &p->from);
+ }
+
+ /*
+ * pass 2.5
+ * find looping structure
+ */
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ change = 0;
+ loopit(firstr, npc);
+
+ /*
+ * pass 3
+ * iterate propagating usage
+ * back until flow graph is complete
+ */
+loop1:
+ change = 0;
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ for(r = firstr; r != R; r = r->link)
+ if(r->prog->as == ARET)
+ prop(r, zbits, zbits);
+loop11:
+ /* pick up unreachable code */
+ i = 0;
+ for(r = firstr; r != R; r = r1) {
+ r1 = r->link;
+ if(r1 && r1->active && !r->active) {
+ prop(r, zbits, zbits);
+ i = 1;
+ }
+ }
+ if(i)
+ goto loop11;
+ if(change)
+ goto loop1;
+
+
+ /*
+ * pass 4
+ * iterate propagating register/variable synchrony
+ * forward until graph is complete
+ */
+loop2:
+ change = 0;
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ synch(firstr, zbits);
+ if(change)
+ goto loop2;
+
+ addsplits();
+
+ if(debug['R'] && debug['v']) {
+ print("\nprop structure:\n");
+ for(r = firstr; r != R; r = r->link) {
+ print("%ld:%P", r->loop, r->prog);
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r->set.b[z] |
+ r->refahead.b[z] | r->calahead.b[z] |
+ r->refbehind.b[z] | r->calbehind.b[z] |
+ r->use1.b[z] | r->use2.b[z];
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->use1))
+ print(" u1=%B", r->use1);
+ if(bany(&r->use2))
+ print(" u2=%B", r->use2);
+ if(bany(&r->set))
+ print(" st=%B", r->set);
+ if(bany(&r->refahead))
+ print(" ra=%B", r->refahead);
+ if(bany(&r->calahead))
+ print(" ca=%B", r->calahead);
+ if(bany(&r->refbehind))
+ print(" rb=%B", r->refbehind);
+ if(bany(&r->calbehind))
+ print(" cb=%B", r->calbehind);
+ }
+ print("\n");
+ }
+ }
+
+ /*
+ * pass 5
+ * isolate regions
+ * calculate costs (paint1)
+ */
+ r = firstr;
+ if(r) {
+ for(z=0; z<BITS; z++)
+ bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+ ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+ if(bany(&bit)) {
+ nearln = r->prog->lineno;
+ warn(Z, "used and not set: %B", bit);
+ if(debug['R'] && !debug['w'])
+ print("used and not set: %B\n", bit);
+ }
+ }
+
+ for(r = firstr; r != R; r = r->link)
+ r->act = zbits;
+ rgp = region;
+ nregion = 0;
+ for(r = firstr; r != R; r = r->link) {
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r->set.b[z] &
+ ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+ if(bany(&bit)) {
+ nearln = r->prog->lineno;
+ warn(Z, "set and not used: %B", bit);
+ if(debug['R'])
+ print("set and not used: %B\n", bit);
+ excise(r);
+ }
+ for(z=0; z<BITS; z++)
+ bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+ while(bany(&bit)) {
+ i = bnum(bit);
+ rgp->enter = r;
+ rgp->varno = i;
+ change = 0;
+ if(debug['R'] && debug['v'])
+ print("\n");
+ paint1(r, i);
+ bit.b[i/32] &= ~(1L<<(i%32));
+ if(change <= 0) {
+ if(debug['R'])
+ print("%L $%d: %B\n",
+ r->prog->lineno, change, blsh(i));
+ continue;
+ }
+ rgp->cost = change;
+ nregion++;
+ if(nregion >= NRGN) {
+ warn(Z, "too many regions");
+ goto brk;
+ }
+ rgp++;
+ }
+ }
+brk:
+ qsort(region, nregion, sizeof(region[0]), rcmp);
+
+ /*
+ * pass 6
+ * determine used registers (paint2)
+ * replace code (paint3)
+ */
+ rgp = region;
+ for(i=0; i<nregion; i++) {
+ bit = blsh(rgp->varno);
+ vreg = paint2(rgp->enter, rgp->varno);
+ vreg = allreg(vreg, rgp);
+ if(debug['R']) {
+ if(rgp->regno >= NREG)
+ print("%L $%d F%d: %B\n",
+ rgp->enter->prog->lineno,
+ rgp->cost,
+ rgp->regno-NREG,
+ bit);
+ else
+ print("%L $%d R%d: %B\n",
+ rgp->enter->prog->lineno,
+ rgp->cost,
+ rgp->regno,
+ bit);
+ }
+ if(rgp->regno != 0)
+ paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+ rgp++;
+ }
+ /*
+ * pass 7
+ * peep-hole on basic block
+ */
+ if(!debug['R'] || debug['P'])
+ peep();
+
+ /*
+ * pass 8
+ * recalculate pc
+ */
+ val = initpc;
+ for(r = firstr; r != R; r = r1) {
+ r->pc = val;
+ p = r->prog;
+ p1 = P;
+ r1 = r->link;
+ if(r1 != R)
+ p1 = r1->prog;
+ for(; p != p1; p = p->link) {
+ switch(p->as) {
+ default:
+ val++;
+ break;
+
+ case ANOP:
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ break;
+ }
+ }
+ }
+ pc = val;
+
+ /*
+ * fix up branches
+ */
+ if(debug['R'])
+ if(bany(&addrs))
+ print("addrs: %B\n", addrs);
+
+ r1 = 0; /* set */
+ for(r = firstr; r != R; r = r->link) {
+ p = r->prog;
+ if(p->to.type == D_BRANCH)
+ p->to.offset = r->s2->pc;
+ r1 = r;
+ }
+
+ /*
+ * last pass
+ * eliminate nops
+ * free aux structures
+ */
+ for(p = firstr->prog; p != P; p = p->link){
+ while(p->link && p->link->as == ANOP)
+ p->link = p->link->link;
+ }
+ if(r1 != R) {
+ r1->link = freer;
+ freer = firstr;
+ }
+}
+
+void
+addsplits(void)
+{
+ Reg *r, *r1;
+ int z, i;
+ Bits bit;
+
+ for(r = firstr; r != R; r = r->link) {
+ if(r->loop > 1)
+ continue;
+ if(r->prog->as == AJAL)
+ continue;
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
+ if(r1->loop <= 1)
+ continue;
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r1->calbehind.b[z] &
+ (r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) &
+ ~(r->calahead.b[z] & addrs.b[z]);
+ while(bany(&bit)) {
+ i = bnum(bit);
+ bit.b[i/32] &= ~(1L << (i%32));
+ }
+ }
+ }
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+ Prog *p, *p1;
+ Adr *a;
+ Var *v;
+
+ p1 = alloc(sizeof(*p1));
+ *p1 = zprog;
+ p = r->prog;
+
+ p1->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+
+ v = var + bn;
+
+ a = &p1->to;
+ a->sym = v->sym;
+ a->name = v->name;
+ a->offset = v->offset;
+ a->etype = v->etype;
+ a->type = D_OREG;
+ if(a->etype == TARRAY || a->sym == S)
+ a->type = D_CONST;
+
+ p1->as = AMOVW;
+ if(v->etype == TCHAR || v->etype == TUCHAR)
+ p1->as = AMOVB;
+ if(v->etype == TSHORT || v->etype == TUSHORT)
+ p1->as = AMOVH;
+ if(v->etype == TVLONG || v->etype == TUVLONG)
+ p1->as = AMOVV;
+ if(v->etype == TFLOAT)
+ p1->as = AMOVF;
+ if(v->etype == TDOUBLE)
+ p1->as = AMOVD;
+
+ p1->from.type = D_REG;
+ p1->from.reg = rn;
+ if(rn >= NREG) {
+ p1->from.type = D_FREG;
+ p1->from.reg = rn-NREG;
+ }
+ if(!f) {
+ p1->from = *a;
+ *a = zprog.from;
+ a->type = D_REG;
+ a->reg = rn;
+ if(rn >= NREG) {
+ a->type = D_FREG;
+ a->reg = rn-NREG;
+ }
+ if(v->etype == TUCHAR)
+ p1->as = AMOVBU;
+ if(v->etype == TUSHORT)
+ p1->as = AMOVHU;
+ }
+ if(debug['R'])
+ print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+ Var *v;
+ int i, t, n, et, z;
+ long o;
+ Bits bit;
+ Sym *s;
+
+ t = a->type;
+ if(t == D_REG && a->reg != NREG)
+ regbits |= RtoB(a->reg);
+ if(t == D_FREG && a->reg != NREG)
+ regbits |= FtoB(a->reg);
+ s = a->sym;
+ o = a->offset;
+ et = a->etype;
+ if(s == S) {
+ if(t != D_CONST || !docon || a->reg != NREG)
+ goto none;
+ et = TLONG;
+ }
+ if(t == D_CONST) {
+ if(s == S && sval(o))
+ goto none;
+ }
+
+ n = a->name;
+ v = var;
+ for(i=0; i<nvar; i++) {
+ if(s == v->sym)
+ if(n == v->name)
+ if(o == v->offset)
+ goto out;
+ v++;
+ }
+ if(s)
+ if(s->name[0] == '.')
+ goto none;
+ if(nvar >= NVAR) {
+ if(debug['w'] > 1 && s)
+ warn(Z, "variable not optimized: %s", s->name);
+ goto none;
+ }
+ i = nvar;
+ nvar++;
+ v = &var[i];
+ v->sym = s;
+ v->offset = o;
+ v->etype = et;
+ v->name = n;
+ if(debug['R'])
+ print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+ bit = blsh(i);
+ if(n == D_EXTERN || n == D_STATIC)
+ for(z=0; z<BITS; z++)
+ externs.b[z] |= bit.b[z];
+ if(n == D_PARAM)
+ for(z=0; z<BITS; z++)
+ params.b[z] |= bit.b[z];
+ if(v->etype != et || !(typechlpfd[et]/* || typev[et]*/)) /* funny punning */
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ if(t == D_CONST) {
+ if(s == S) {
+ for(z=0; z<BITS; z++)
+ consts.b[z] |= bit.b[z];
+ return bit;
+ }
+ if(et != TARRAY)
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ for(z=0; z<BITS; z++)
+ params.b[z] |= bit.b[z];
+ return bit;
+ }
+ if(t == D_OREG)
+ return bit;
+
+none:
+ return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+ Reg *r1, *r2;
+ int z;
+
+ for(r1 = r; r1 != R; r1 = r1->p1) {
+ for(z=0; z<BITS; z++) {
+ ref.b[z] |= r1->refahead.b[z];
+ if(ref.b[z] != r1->refahead.b[z]) {
+ r1->refahead.b[z] = ref.b[z];
+ change++;
+ }
+ cal.b[z] |= r1->calahead.b[z];
+ if(cal.b[z] != r1->calahead.b[z]) {
+ r1->calahead.b[z] = cal.b[z];
+ change++;
+ }
+ }
+ switch(r1->prog->as) {
+ case AJAL:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] |= ref.b[z] | externs.b[z];
+ ref.b[z] = 0;
+ }
+ break;
+
+ case ATEXT:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = 0;
+ ref.b[z] = 0;
+ }
+ break;
+
+ case ARET:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = externs.b[z];
+ ref.b[z] = 0;
+ }
+ }
+ for(z=0; z<BITS; z++) {
+ ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+ r1->use1.b[z] | r1->use2.b[z];
+ cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+ r1->refbehind.b[z] = ref.b[z];
+ r1->calbehind.b[z] = cal.b[z];
+ }
+ if(r1->active)
+ break;
+ r1->active = 1;
+ }
+ for(; r != r1; r = r->p1)
+ for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+ prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ * the actual dominators if the flow graph is reducible
+ * otherwise, dominators plus some other non-dominators.
+ * See Matthew S. Hecht and Jeffrey D. Ullman,
+ * "Analysis of a Simple Algorithm for Global Data Flow Problems",
+ * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ * Oct. 1-3, 1973, pp. 207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ * such a node is a loop head.
+ * recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+ Reg *r1;
+
+ r->rpo = 1;
+ r1 = r->s1;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ r1 = r->s2;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ rpo2r[n] = r;
+ n++;
+ return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+ long t;
+
+ if(rpo1 == -1)
+ return rpo2;
+ while(rpo1 != rpo2){
+ if(rpo1 > rpo2){
+ t = rpo2;
+ rpo2 = rpo1;
+ rpo1 = t;
+ }
+ while(rpo1 < rpo2){
+ t = idom[rpo2];
+ if(t >= rpo2)
+ fatal(Z, "bad idom");
+ rpo2 = t;
+ }
+ }
+ return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+ while(s > r)
+ s = idom[s];
+ return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+ long src;
+
+ src = r->rpo;
+ if(r->p1 != R && doms(idom, src, r->p1->rpo))
+ return 1;
+ for(r = r->p2; r != R; r = r->p2link)
+ if(doms(idom, src, r->rpo))
+ return 1;
+ return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+ if(r->rpo < head || r->active == head)
+ return;
+ r->active = head;
+ r->loop += LOOP;
+ if(r->p1 != R)
+ loopmark(rpo2r, head, r->p1);
+ for(r = r->p2; r != R; r = r->p2link)
+ loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+ Reg *r1;
+ long i, d, me;
+
+ if(nr > maxnr) {
+ rpo2r = alloc(nr * sizeof(Reg*));
+ idom = alloc(nr * sizeof(long));
+ maxnr = nr;
+ }
+
+ d = postorder(r, rpo2r, 0);
+ if(d > nr)
+ fatal(Z, "too many reg nodes");
+ nr = d;
+ for(i = 0; i < nr / 2; i++){
+ r1 = rpo2r[i];
+ rpo2r[i] = rpo2r[nr - 1 - i];
+ rpo2r[nr - 1 - i] = r1;
+ }
+ for(i = 0; i < nr; i++)
+ rpo2r[i]->rpo = i;
+
+ idom[0] = 0;
+ for(i = 0; i < nr; i++){
+ r1 = rpo2r[i];
+ me = r1->rpo;
+ d = -1;
+ if(r1->p1 != R && r1->p1->rpo < me)
+ d = r1->p1->rpo;
+ for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+ if(r1->rpo < me)
+ d = rpolca(idom, d, r1->rpo);
+ idom[i] = d;
+ }
+
+ for(i = 0; i < nr; i++){
+ r1 = rpo2r[i];
+ r1->loop++;
+ if(r1->p2 != R && loophead(idom, r1))
+ loopmark(rpo2r, i, r1);
+ }
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+ Reg *r1;
+ int z;
+
+ for(r1 = r; r1 != R; r1 = r1->s1) {
+ for(z=0; z<BITS; z++) {
+ dif.b[z] = (dif.b[z] &
+ ~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+ r1->set.b[z] | r1->regdiff.b[z];
+ if(dif.b[z] != r1->regdiff.b[z]) {
+ r1->regdiff.b[z] = dif.b[z];
+ change++;
+ }
+ }
+ if(r1->active)
+ break;
+ r1->active = 1;
+ for(z=0; z<BITS; z++)
+ dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+ if(r1->s2 != R)
+ synch(r1->s2, dif);
+ }
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+ Var *v;
+ int i;
+
+ v = var + r->varno;
+ r->regno = 0;
+ switch(v->etype) {
+
+ default:
+ diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+ break;
+
+ case TCHAR:
+ case TUCHAR:
+ case TSHORT:
+ case TUSHORT:
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TVLONG:
+ case TUVLONG:
+ case TIND:
+ case TARRAY:
+ i = BtoR(~b);
+ if(i && r->cost >= 0) {
+ r->regno = i;
+ return RtoB(i);
+ }
+ break;
+
+ case TDOUBLE:
+ case TFLOAT:
+ i = BtoF(~b);
+ if(i && r->cost >= 0) {
+ r->regno = i+NREG;
+ return FtoB(i);
+ }
+ break;
+ }
+ return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ ulong bb;
+
+ z = bn/32;
+ bb = 1L<<(bn%32);
+ if(r->act.b[z] & bb)
+ return;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(r1->act.b[z] & bb)
+ break;
+ r = r1;
+ }
+
+ if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tld %B $%d\n", r->loop,
+ r->prog, blsh(bn), change);
+ }
+ for(;;) {
+ r->act.b[z] |= bb;
+ p = r->prog;
+
+ if(r->use1.b[z] & bb) {
+ change += CREF * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tu1 %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ change += CREF * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tu2 %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tst %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ paint1(r1, bn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ paint1(r1, bn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(r->act.b[z] & bb)
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+ Reg *r1;
+ int z;
+ ulong bb, vreg;
+
+ z = bn/32;
+ bb = 1L << (bn%32);
+ vreg = regbits;
+ if(!(r->act.b[z] & bb))
+ return vreg;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(!(r1->act.b[z] & bb))
+ break;
+ r = r1;
+ }
+ for(;;) {
+ r->act.b[z] &= ~bb;
+
+ vreg |= r->regu;
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ vreg |= paint2(r1, bn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ vreg |= paint2(r1, bn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(!(r->act.b[z] & bb))
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+ return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ ulong bb;
+
+ z = bn/32;
+ bb = 1L << (bn%32);
+ if(r->act.b[z] & bb)
+ return;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(r1->act.b[z] & bb)
+ break;
+ r = r1;
+ }
+
+ if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+ addmove(r, bn, rn, 0);
+ for(;;) {
+ r->act.b[z] |= bb;
+ p = r->prog;
+
+ if(r->use1.b[z] & bb) {
+ if(debug['R'])
+ print("%P", p);
+ addreg(&p->from, rn);
+ if(debug['R'])
+ print("\t.c%P\n", p);
+ }
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ if(debug['R'])
+ print("%P", p);
+ addreg(&p->to, rn);
+ if(debug['R'])
+ print("\t.c%P\n", p);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb)
+ addmove(r, bn, rn, 1);
+ r->regu |= rb;
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ paint3(r1, bn, rb, rn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ paint3(r1, bn, rb, rn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(r->act.b[z] & bb)
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+ a->sym = 0;
+ a->name = D_NONE;
+ a->type = D_REG;
+ a->reg = rn;
+ if(rn >= NREG) {
+ a->type = D_FREG;
+ a->reg = rn-NREG;
+ }
+}
+
+/*
+ * bit reg
+ * 0 R3
+ * 1 R4
+ * ... ...
+ * 19 R22
+ * 20 R23
+ */
+long
+RtoB(int r)
+{
+
+ if(r < 3 || r > 23)
+ return 0;
+ return 1L << (r-3);
+}
+
+int
+BtoR(long b)
+{
+
+ b &= 0x001fffffL;
+ if(b == 0)
+ return 0;
+ return bitno(b) + 3;
+}
+
+/*
+ * bit reg
+ * 22 F4
+ * 23 F6
+ * ... ...
+ * 31 F22
+ */
+long
+FtoB(int f)
+{
+
+ if(f < 4 || f > 22 || (f&1))
+ return 0;
+ return 1L << (f/2 + 20);
+}
+
+int
+BtoF(long b)
+{
+
+ b &= 0xffc00000L;
+ if(b == 0)
+ return 0;
+ return bitno(b)*2 - 40;
+}
--- /dev/null
+++ b/utils/0c/sgen.c
@@ -1,0 +1,569 @@
+#include "gc.h"
+
+void
+codgen(Node *n, Node *nn)
+{
+ Prog *sp;
+ Node *n1, nod, nod1;
+
+ cursafe = 0;
+ curarg = 0;
+ maxargsafe = 0;
+
+ /*
+ * isolate name
+ */
+ for(n1 = nn;; n1 = n1->left) {
+ if(n1 == Z) {
+ diag(nn, "cant find function name");
+ return;
+ }
+ if(n1->op == ONAME)
+ break;
+ }
+ nearln = nn->lineno;
+ gpseudo(ATEXT, n1->sym, nodconst(stkoff));
+ sp = p;
+
+ /*
+ * isolate first argument
+ */
+ if(REGARG) {
+ if(typesu[thisfn->link->etype]) {
+ nod1 = *nodret->left;
+ nodreg(&nod, &nod1, REGARG);
+ gopcode(OAS, &nod, Z, &nod1);
+ } else
+ if(firstarg && typechlp[firstargtype->etype]) {
+ nod1 = *nodret->left;
+ nod1.sym = firstarg;
+ nod1.type = firstargtype;
+ nod1.xoffset = align(0, firstargtype, Aarg1);
+ nod1.etype = firstargtype->etype;
+ nodreg(&nod, &nod1, REGARG);
+ gopcode(OAS, &nod, Z, &nod1);
+ }
+ }
+
+ retok = 0;
+ gen(n);
+ if(!retok)
+ if(thisfn->link->etype != TVOID)
+ warn(Z, "no return at end of function: %s", n1->sym->name);
+ noretval(3);
+ gbranch(ORETURN);
+
+ if(!debug['N'] || debug['R'] || debug['P'])
+ regopt(sp);
+
+ sp->to.offset += maxargsafe;
+}
+
+void
+gen(Node *n)
+{
+ Node *l, nod;
+ Prog *sp, *spc, *spb;
+ Case *cn;
+ long sbc, scc;
+ int o;
+
+loop:
+ if(n == Z)
+ return;
+ nearln = n->lineno;
+ o = n->op;
+ if(debug['G'])
+ if(o != OLIST)
+ print("%L %O\n", nearln, o);
+
+ retok = 0;
+ switch(o) {
+
+ default:
+ complex(n);
+ cgen(n, Z);
+ break;
+
+ case OLIST:
+ gen(n->left);
+
+ rloop:
+ n = n->right;
+ goto loop;
+
+ case ORETURN:
+ retok = 1;
+ complex(n);
+ if(n->type == T)
+ break;
+ l = n->left;
+ if(l == Z) {
+ noretval(3);
+ gbranch(ORETURN);
+ break;
+ }
+ if(typesu[n->type->etype]) {
+ sugen(l, nodret, n->type->width);
+ noretval(3);
+ gbranch(ORETURN);
+ break;
+ }
+ regret(&nod, n);
+ cgen(l, &nod);
+ regfree(&nod);
+ if(typefd[n->type->etype])
+ noretval(1);
+ else
+ noretval(2);
+ gbranch(ORETURN);
+ break;
+
+ case OLABEL:
+ l = n->left;
+ if(l) {
+ l->pc = pc;
+ if(l->label)
+ patch(l->label, pc);
+ }
+ gbranch(OGOTO); /* prevent self reference in reg */
+ patch(p, pc);
+ goto rloop;
+
+ case OGOTO:
+ retok = 1;
+ n = n->left;
+ if(n == Z)
+ return;
+ if(n->complex == 0) {
+ diag(Z, "label undefined: %s", n->sym->name);
+ return;
+ }
+ gbranch(OGOTO);
+ if(n->pc) {
+ patch(p, n->pc);
+ return;
+ }
+ if(n->label)
+ patch(n->label, pc-1);
+ n->label = p;
+ return;
+
+ case OCASE:
+ l = n->left;
+ if(cases == C)
+ diag(n, "case/default outside a switch");
+ if(l == Z) {
+ casf();
+ cases->val = 0;
+ cases->def = 1;
+ cases->label = pc;
+ goto rloop;
+ }
+ complex(l);
+ if(l->type == T)
+ goto rloop;
+ if(l->op == OCONST)
+ if(typechl[l->type->etype]) {
+ casf();
+ cases->val = l->vconst;
+ cases->def = 0;
+ cases->label = pc;
+ goto rloop;
+ }
+ diag(n, "case expression must be integer constant");
+ goto rloop;
+
+ case OSWITCH:
+ l = n->left;
+ complex(l);
+ if(l->type == T)
+ break;
+ if(!typechl[l->type->etype]) {
+ diag(n, "switch expression must be integer");
+ break;
+ }
+
+ gbranch(OGOTO); /* entry */
+ sp = p;
+
+ cn = cases;
+ cases = C;
+ casf();
+
+ sbc = breakpc;
+ breakpc = pc;
+ gbranch(OGOTO);
+ spb = p;
+
+ gen(n->right);
+ gbranch(OGOTO);
+ patch(p, breakpc);
+
+ patch(sp, pc);
+ regalloc(&nod, l, Z);
+ nod.type = types[TLONG];
+ cgen(l, &nod);
+ doswit(&nod);
+ regfree(&nod);
+ patch(spb, pc);
+
+ cases = cn;
+ breakpc = sbc;
+ break;
+
+ case OWHILE:
+ case ODWHILE:
+ l = n->left;
+ gbranch(OGOTO); /* entry */
+ sp = p;
+
+ scc = continpc;
+ continpc = pc;
+ gbranch(OGOTO);
+ spc = p;
+
+ sbc = breakpc;
+ breakpc = pc;
+ gbranch(OGOTO);
+ spb = p;
+
+ patch(spc, pc);
+ if(n->op == OWHILE)
+ patch(sp, pc);
+ bcomplex(l); /* test */
+ patch(p, breakpc);
+
+ if(n->op == ODWHILE)
+ patch(sp, pc);
+ gen(n->right); /* body */
+ gbranch(OGOTO);
+ patch(p, continpc);
+
+ patch(spb, pc);
+ continpc = scc;
+ breakpc = sbc;
+ break;
+
+ case OFOR:
+ l = n->left;
+ gen(l->right->left); /* init */
+ gbranch(OGOTO); /* entry */
+ sp = p;
+
+ scc = continpc;
+ continpc = pc;
+ gbranch(OGOTO);
+ spc = p;
+
+ sbc = breakpc;
+ breakpc = pc;
+ gbranch(OGOTO);
+ spb = p;
+
+ patch(spc, pc);
+ gen(l->right->right); /* inc */
+ patch(sp, pc);
+ if(l->left != Z) { /* test */
+ bcomplex(l->left);
+ patch(p, breakpc);
+ }
+ gen(n->right); /* body */
+ gbranch(OGOTO);
+ patch(p, continpc);
+
+ patch(spb, pc);
+ continpc = scc;
+ breakpc = sbc;
+ break;
+
+ case OCONTINUE:
+ if(continpc < 0) {
+ diag(n, "continue not in a loop");
+ break;
+ }
+ gbranch(OGOTO);
+ patch(p, continpc);
+ break;
+
+ case OBREAK:
+ if(breakpc < 0) {
+ diag(n, "break not in a loop");
+ break;
+ }
+ gbranch(OGOTO);
+ patch(p, breakpc);
+ break;
+
+ case OIF:
+ l = n->left;
+ bcomplex(l);
+ sp = p;
+ if(n->right->left != Z)
+ gen(n->right->left);
+ if(n->right->right != Z) {
+ gbranch(OGOTO);
+ patch(sp, pc);
+ sp = p;
+ gen(n->right->right);
+ }
+ patch(sp, pc);
+ break;
+
+ case OSET:
+ case OUSED:
+ usedset(n->left, o);
+ break;
+ }
+}
+
+void
+usedset(Node *n, int o)
+{
+ if(n->op == OLIST) {
+ usedset(n->left, o);
+ usedset(n->right, o);
+ return;
+ }
+ complex(n);
+ switch(n->op) {
+ case OADDR: /* volatile */
+ gins(ANOP, n, Z);
+ break;
+ case ONAME:
+ if(o == OSET)
+ gins(ANOP, Z, n);
+ else
+ gins(ANOP, n, Z);
+ break;
+ }
+}
+
+void
+noretval(int n)
+{
+
+ if(n & 1) {
+ gins(ANOP, Z, Z);
+ p->to.type = D_REG;
+ p->to.reg = REGRET;
+ }
+ if(n & 2) {
+ gins(ANOP, Z, Z);
+ p->to.type = D_FREG;
+ p->to.reg = FREGRET;
+ }
+}
+
+/*
+ * calculate addressability as follows
+ * CONST ==> 20 $value
+ * NAME ==> 10 name
+ * REGISTER ==> 11 register
+ * INDREG ==> 12 *[(reg)+offset]
+ * &10 ==> 2 $name
+ * ADD(2, 20) ==> 2 $name+offset
+ * ADD(3, 20) ==> 3 $(reg)+offset
+ * &12 ==> 3 $(reg)+offset
+ * *11 ==> 11 ??
+ * *2 ==> 10 name
+ * *3 ==> 12 *(reg)+offset
+ * calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+ Node *l, *r;
+ int t;
+
+ if(n == Z)
+ return;
+ l = n->left;
+ r = n->right;
+ n->addable = 0;
+ n->complex = 0;
+ switch(n->op) {
+ case OCONST:
+ n->addable = 20;
+ return;
+
+ case OREGISTER:
+ n->addable = 11;
+ return;
+
+ case OINDREG:
+ n->addable = 12;
+ return;
+
+ case ONAME:
+ n->addable = 10;
+ return;
+
+ case OADDR:
+ xcom(l);
+ if(l->addable == 10)
+ n->addable = 2;
+ if(l->addable == 12)
+ n->addable = 3;
+ break;
+
+ case OIND:
+ xcom(l);
+ if(l->addable == 11)
+ n->addable = 12;
+ if(l->addable == 3)
+ n->addable = 12;
+ if(l->addable == 2)
+ n->addable = 10;
+ break;
+
+ case OADD:
+ xcom(l);
+ xcom(r);
+ if(l->addable == 20) {
+ if(r->addable == 2)
+ n->addable = 2;
+ if(r->addable == 3)
+ n->addable = 3;
+ }
+ if(r->addable == 20) {
+ if(l->addable == 2)
+ n->addable = 2;
+ if(l->addable == 3)
+ n->addable = 3;
+ }
+ break;
+
+ case OASLMUL:
+ case OASMUL:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OASASHL;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OMUL:
+ case OLMUL:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OASHL;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ t = vlog(l);
+ if(t >= 0) {
+ n->op = OASHL;
+ n->left = r;
+ n->right = l;
+ r = l;
+ l = n->left;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OASLDIV:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OASLSHR;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OLDIV:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OLSHR;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OASLMOD:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OASAND;
+ r->vconst--;
+ }
+ break;
+
+ case OLMOD:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OAND;
+ r->vconst--;
+ }
+ break;
+
+ default:
+ if(l != Z)
+ xcom(l);
+ if(r != Z)
+ xcom(r);
+ break;
+ }
+ if(n->addable >= 10)
+ return;
+
+ if(l != Z)
+ n->complex = l->complex;
+ if(r != Z) {
+ if(r->complex == n->complex)
+ n->complex = r->complex+1;
+ else
+ if(r->complex > n->complex)
+ n->complex = r->complex;
+ }
+ if(n->complex == 0)
+ n->complex++;
+
+ switch(n->op) {
+ case OFUNC:
+ n->complex = FNX;
+ break;
+
+ case OADD:
+ case OXOR:
+ case OAND:
+ case OOR:
+ case OEQ:
+ case ONE:
+ /*
+ * immediate operators, make const on right
+ */
+ if(l->op == OCONST) {
+ n->left = r;
+ n->right = l;
+ }
+ break;
+ }
+}
+
+void
+bcomplex(Node *n)
+{
+
+ complex(n);
+ if(n->type != T)
+ if(tcompat(n, T, n->type, tnot))
+ n->type = T;
+ if(n->type != T)
+ boolgen(n, 1, Z);
+ else
+ gbranch(OGOTO);
+}
--- /dev/null
+++ b/utils/0c/swt.c
@@ -1,0 +1,711 @@
+#include "gc.h"
+
+int
+swcmp(void *a1, void *a2)
+{
+ C1 *p1, *p2;
+
+ p1 = (C1*)a1;
+ p2 = (C1*)a2;
+ if(p1->val < p2->val)
+ return -1;
+ return p1->val > p2->val;
+}
+
+void
+doswit(Node *n)
+{
+ Case *c;
+ C1 *q, *iq;
+ long def, nc, i;
+ Node tn;
+
+ def = 0;
+ nc = 0;
+ for(c = cases; c->link != C; c = c->link) {
+ if(c->def) {
+ if(def)
+ diag(n, "more than one default in switch");
+ def = c->label;
+ continue;
+ }
+ nc++;
+ }
+
+ iq = alloc(nc*sizeof(C1));
+ q = iq;
+ for(c = cases; c->link != C; c = c->link) {
+ if(c->def)
+ continue;
+ q->label = c->label;
+ q->val = c->val;
+ q++;
+ }
+ qsort(iq, nc, sizeof(C1), swcmp);
+ if(debug['W'])
+ for(i=0; i<nc; i++)
+ print("case %2ld: = %.8lux\n", i, iq[i].val);
+ if(def == 0)
+ def = breakpc;
+ for(i=0; i<nc-1; i++)
+ if(iq[i].val == iq[i+1].val)
+ diag(n, "duplicate cases in switch %ld", iq[i].val);
+ regalloc(&tn, ®node, Z);
+ swit1(iq, nc, def, n, &tn);
+ regfree(&tn);
+}
+
+void
+swit1(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+ C1 *r;
+ int i;
+ Prog *sp;
+
+ if(nc < 5) {
+ for(i=0; i<nc; i++) {
+ if(debug['W'])
+ print("case = %.8lux\n", q->val);
+ gmove(nodconst(q->val), tn);
+ gopcode(OEQ, n, tn, Z);
+ patch(p, q->label);
+ q++;
+ }
+ gbranch(OGOTO);
+ patch(p, def);
+ return;
+ }
+ i = nc / 2;
+ r = q+i;
+ if(debug['W'])
+ print("case > %.8lux\n", r->val);
+ gmove(nodconst(r->val), tn);
+ gopcode(OLT, tn, n, Z);
+ sp = p;
+ gopcode(OEQ, n, tn, Z);
+ patch(p, r->label);
+ swit1(q, i, def, n, tn);
+
+ if(debug['W'])
+ print("case < %.8lux\n", r->val);
+ patch(sp, pc);
+ swit1(r+1, nc-i-1, def, n, tn);
+}
+
+void
+casf(void)
+{
+ Case *c;
+
+ c = alloc(sizeof(*c));
+ c->link = cases;
+ cases = c;
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+ int sh;
+ long v;
+ Node *l;
+
+ /*
+ * n1 gets adjusted/masked value
+ * n2 gets address of cell
+ * n3 gets contents of cell
+ */
+ l = b->left;
+ if(n2 != Z) {
+ regalloc(n1, l, nn);
+ reglcgen(n2, l, Z);
+ regalloc(n3, l, Z);
+ gopcode(OAS, n2, Z, n3);
+ gopcode(OAS, n3, Z, n1);
+ } else {
+ regalloc(n1, l, nn);
+ cgen(l, n1);
+ }
+ if(b->type->shift == 0 && typeu[b->type->etype]) {
+ v = ~0 + (1L << b->type->nbits);
+ gopcode(OAND, nodconst(v), Z, n1);
+ } else {
+ sh = 32 - b->type->shift - b->type->nbits;
+ if(sh > 0)
+ gopcode(OASHL, nodconst(sh), Z, n1);
+ sh += b->type->shift;
+ if(sh > 0)
+ if(typeu[b->type->etype])
+ gopcode(OLSHR, nodconst(sh), Z, n1);
+ else
+ gopcode(OASHR, nodconst(sh), Z, n1);
+ }
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+ long v;
+ Node nod, *l;
+ int sh;
+
+ /*
+ * n1 has adjusted/masked value
+ * n2 has address of cell
+ * n3 has contents of cell
+ */
+ l = b->left;
+ regalloc(&nod, l, Z);
+ v = ~0 + (1L << b->type->nbits);
+ gopcode(OAND, nodconst(v), Z, n1);
+ gopcode(OAS, n1, Z, &nod);
+ if(nn != Z)
+ gopcode(OAS, n1, Z, nn);
+ sh = b->type->shift;
+ if(sh > 0)
+ gopcode(OASHL, nodconst(sh), Z, &nod);
+ v <<= sh;
+ gopcode(OAND, nodconst(~v), Z, n3);
+ gopcode(OOR, n3, Z, &nod);
+ gopcode(OAS, &nod, Z, n2);
+
+ regfree(&nod);
+ regfree(n1);
+ regfree(n2);
+ regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+ long r;
+
+ r = nstring;
+ while(n) {
+ string[mnstring] = *s++;
+ mnstring++;
+ nstring++;
+ if(mnstring >= NSNAME) {
+ gpseudo(ADATA, symstring, nodconst(0L));
+ p->from.offset += nstring - NSNAME;
+ p->reg = NSNAME;
+ p->to.type = D_SCONST;
+ memmove(p->to.sval, string, NSNAME);
+ mnstring = 0;
+ }
+ n--;
+ }
+ return r;
+}
+
+long
+outlstring(TRune *s, long n)
+{
+ char buf[2];
+ int c;
+ long r;
+
+ while(nstring & 1)
+ outstring("", 1);
+ r = nstring;
+ while(n > 0) {
+ c = *s++;
+ if(align(0, types[TCHAR], Aarg1)) {
+ buf[0] = c>>8;
+ buf[1] = c;
+ } else {
+ buf[0] = c;
+ buf[1] = c>>8;
+ }
+ outstring(buf, 2);
+ n -= sizeof(ushort);
+ }
+ return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+ Node *l, *r, nod1, nod2;
+ Multab *m;
+ long v;
+ int o;
+ char code[sizeof(m->code)+2], *p;
+
+ if(typefd[n->type->etype])
+ return 0;
+ l = n->left;
+ r = n->right;
+ if(l->op == OCONST) {
+ l = r;
+ r = n->left;
+ }
+ if(r->op != OCONST)
+ return 0;
+ v = convvtox(r->vconst, n->type->etype);
+ if(v != r->vconst) {
+ if(debug['M'])
+ print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+ return 0;
+ }
+ m = mulcon0(v);
+ if(!m) {
+ if(debug['M'])
+ print("%L multiply table: %lld\n", n->lineno, r->vconst);
+ return 0;
+ }
+ if(debug['M'] && debug['v'])
+ print("%L multiply: %ld\n", n->lineno, v);
+
+ memmove(code, m->code, sizeof(m->code));
+ code[sizeof(m->code)] = 0;
+
+ p = code;
+ if(p[1] == 'i')
+ p += 2;
+ regalloc(&nod1, n, nn);
+ cgen(l, &nod1);
+ if(v < 0)
+ gopcode(OSUB, &nod1, nodconst(0), &nod1);
+ regalloc(&nod2, n, Z);
+
+loop:
+ switch(*p) {
+ case 0:
+ regfree(&nod2);
+ gopcode(OAS, &nod1, Z, nn);
+ regfree(&nod1);
+ return 1;
+ case '+':
+ o = OADD;
+ goto addsub;
+ case '-':
+ o = OSUB;
+ addsub: /* number is r,n,l */
+ v = p[1] - '0';
+ r = &nod1;
+ if(v&4)
+ r = &nod2;
+ n = &nod1;
+ if(v&2)
+ n = &nod2;
+ l = &nod1;
+ if(v&1)
+ l = &nod2;
+ gopcode(o, l, n, r);
+ break;
+ default: /* op is shiftcount, number is r,l */
+ v = p[1] - '0';
+ r = &nod1;
+ if(v&2)
+ r = &nod2;
+ l = &nod1;
+ if(v&1)
+ l = &nod2;
+ v = *p - 'a';
+ if(v < 0 || v >= 32) {
+ diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+ break;
+ }
+ gopcode(OASHL, nodconst(v), l, r);
+ break;
+ }
+ p += 2;
+ goto loop;
+}
+
+void
+nullwarn(Node *l, Node *r)
+{
+ warn(Z, "result of operation not used");
+ if(l != Z)
+ cgen(l, Z);
+ if(r != Z)
+ cgen(r, Z);
+}
+
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+
+ if(a->op == OCONST && typev[a->type->etype]) {
+ gpseudo(ADATA, s, nodconst(a->vconst));
+ p->from.offset += o;
+ p->reg = 4;
+ gpseudo(ADATA, s, nodconst(a->vconst>>32));
+ p->from.offset += o + 4;
+ p->reg = 4;
+ return;
+ }
+ gpseudo(ADATA, s, a);
+ p->from.offset += o;
+ p->reg = w;
+ if(p->to.type == D_OREG)
+ p->to.type = D_CONST;
+}
+
+void zname(Biobuf*, Sym*, int);
+char* zaddr(char*, Adr*, int);
+void zwrite(Biobuf*, Prog*, int, int);
+void outhist(Biobuf*);
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+ char bf[100], *bp;
+
+ bf[0] = p->as;
+ bf[1] = p->reg;
+ bf[2] = p->lineno;
+ bf[3] = p->lineno>>8;
+ bf[4] = p->lineno>>16;
+ bf[5] = p->lineno>>24;
+ bp = zaddr(bf+6, &p->from, sf);
+ bp = zaddr(bp, &p->to, st);
+ Bwrite(b, bf, bp-bf);
+}
+
+void
+outcode(void)
+{
+ struct { Sym *sym; short type; } h[NSYM];
+ Prog *p;
+ Sym *s;
+ int sf, st, t, sym;
+
+ if(debug['S']) {
+ for(p = firstp; p != P; p = p->link)
+ if(p->as != ADATA && p->as != AGLOBL)
+ pc--;
+ for(p = firstp; p != P; p = p->link) {
+ print("%P\n", p);
+ if(p->as != ADATA && p->as != AGLOBL)
+ pc++;
+ }
+ }
+ outhist(&outbuf);
+ for(sym=0; sym<NSYM; sym++) {
+ h[sym].sym = S;
+ h[sym].type = 0;
+ }
+ sym = 1;
+ for(p = firstp; p != P; p = p->link) {
+ jackpot:
+ sf = 0;
+ s = p->from.sym;
+ while(s != S) {
+ sf = s->sym;
+ if(sf < 0 || sf >= NSYM)
+ sf = 0;
+ t = p->from.name;
+ if(h[sf].type == t)
+ if(h[sf].sym == s)
+ break;
+ s->sym = sym;
+ zname(&outbuf, s, t);
+ h[sym].sym = s;
+ h[sym].type = t;
+ sf = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ break;
+ }
+ st = 0;
+ s = p->to.sym;
+ while(s != S) {
+ st = s->sym;
+ if(st < 0 || st >= NSYM)
+ st = 0;
+ t = p->to.name;
+ if(h[st].type == t)
+ if(h[st].sym == s)
+ break;
+ s->sym = sym;
+ zname(&outbuf, s, t);
+ h[sym].sym = s;
+ h[sym].type = t;
+ st = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ if(st == sf)
+ goto jackpot;
+ break;
+ }
+ zwrite(&outbuf, p, sf, st);
+ }
+ firstp = P;
+ lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+ Hist *h;
+ char *p, *q, *op, c;
+ Prog pg;
+ int n;
+
+ pg = zprog;
+ pg.as = AHISTORY;
+ c = pathchar();
+ for(h = hist; h != H; h = h->link) {
+ p = h->name;
+ op = 0;
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && p && p[1] == ':'){
+ p += 2;
+ c = *p;
+ }
+ if(p && p[0] != c && h->offset == 0 && pathname){
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && pathname[1] == ':') {
+ op = p;
+ p = pathname+2;
+ c = *p;
+ } else if(pathname[0] == c){
+ op = p;
+ p = pathname;
+ }
+ }
+ while(p) {
+ q = utfrune(p, c);
+ if(q) {
+ n = q-p;
+ if(n == 0){
+ n = 1; /* leading "/" */
+ *p = '/'; /* don't emit "\" on windows */
+ }
+ q++;
+ } else {
+ n = strlen(p);
+ q = 0;
+ }
+ if(n) {
+ Bputc(b, ANAME);
+ Bputc(b, D_FILE);
+ Bputc(b, 1);
+ Bputc(b, '<');
+ Bwrite(b, p, n);
+ Bputc(b, 0);
+ }
+ p = q;
+ if(p == 0 && op) {
+ p = op;
+ op = 0;
+ }
+ }
+ pg.lineno = h->line;
+ pg.to.type = zprog.to.type;
+ pg.to.offset = h->offset;
+ if(h->offset)
+ pg.to.type = D_CONST;
+
+ zwrite(b, &pg, 0, 0);
+ }
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+ char *n, bf[7];
+ ulong sig;
+
+ n = s->name;
+ if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+ sig = sign(s);
+ bf[0] = ASIGNAME;
+ bf[1] = sig;
+ bf[2] = sig>>8;
+ bf[3] = sig>>16;
+ bf[4] = sig>>24;
+ bf[5] = t;
+ bf[6] = s->sym;
+ Bwrite(b, bf, 7);
+ s->sig = SIGDONE;
+ }
+ else{
+ bf[0] = ANAME;
+ bf[1] = t; /* type */
+ bf[2] = s->sym; /* sym */
+ Bwrite(b, bf, 3);
+ }
+ Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+ vlong v;
+ long l;
+ Ieee e;
+
+ bp[0] = a->type;
+ bp[1] = a->reg;
+ bp[2] = s;
+ bp[3] = a->name;
+ bp += 4;
+ switch(a->type) {
+ default:
+ diag(Z, "unknown type %d in zaddr", a->type);
+
+ case D_NONE:
+ case D_REG:
+ case D_FREG:
+ case D_MREG:
+ case D_FCREG:
+ case D_LO:
+ case D_HI:
+ break;
+
+ case D_CONST:
+ case D_OREG:
+ case D_BRANCH:
+ l = a->offset;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ break;
+
+ case D_SCONST:
+ memmove(bp, a->sval, NSNAME);
+ bp += NSNAME;
+ break;
+
+ case D_FCONST:
+ ieeedtod(&e, a->dval);
+ l = e.l;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ l = e.h;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ break;
+ case D_VCONST:
+ v = a->vval;
+ bp[0] = v;
+ bp[1] = v>>8;
+ bp[2] = v>>16;
+ bp[3] = v>>24;
+ bp[4] = v>>32;
+ bp[5] = v>>40;
+ bp[6] = v>>48;
+ bp[7] = v>>56;
+ bp += 8;
+ break;
+ }
+ return bp;
+}
+
+void
+ieeedtod(Ieee *ieee, double native)
+{
+ double fr, ho, f;
+ int exp;
+
+ if(native < 0) {
+ ieeedtod(ieee, -native);
+ ieee->h |= 0x80000000L;
+ return;
+ }
+ if(native == 0) {
+ ieee->l = 0;
+ ieee->h = 0;
+ return;
+ }
+ fr = frexp(native, &exp);
+ f = 2097152L; /* shouldnt use fp constants here */
+ fr = modf(fr*f, &ho);
+ ieee->h = ho;
+ ieee->h &= 0xfffffL;
+ ieee->h |= (exp+1022L) << 20;
+ f = 65536L;
+ fr = modf(fr*f, &ho);
+ ieee->l = ho;
+ ieee->l <<= 16;
+ ieee->l |= (long)(fr*f);
+}
+
+long
+align(long i, Type *t, int op)
+{
+ long o;
+ Type *v;
+ int w;
+
+ o = i;
+ w = 1;
+ switch(op) {
+ default:
+ diag(Z, "unknown align opcode %d", op);
+ break;
+
+ case Asu2: /* padding at end of a struct */
+ w = SZ_VLONG;
+ if(packflg)
+ w = packflg;
+ break;
+
+ case Ael1: /* initial allign of struct element */
+ for(v=t; v->etype==TARRAY; v=v->link)
+ ;
+ w = ewidth[v->etype];
+ if(w <= 0 || w >= SZ_VLONG)
+ w = SZ_VLONG;
+ if(packflg)
+ w = packflg;
+ break;
+
+ case Ael2: /* width of a struct element */
+ o += t->width;
+ break;
+
+ case Aarg0: /* initial passbyptr argument in arg list */
+ if(typesu[t->etype]) {
+ o = align(o, types[TIND], Aarg1);
+ o = align(o, types[TIND], Aarg2);
+ }
+ break;
+
+ case Aarg1: /* initial allign of parameter */
+ w = ewidth[t->etype];
+ if(w <= 0 || w >= SZ_VLONG) {
+ w = SZ_VLONG;
+ break;
+ }
+ w = 1; /* little endian no adjustment */
+ break;
+
+ case Aarg2: /* width of a parameter */
+ o += t->width;
+ w = SZ_LONG;
+ break;
+
+ case Aaut3: /* total allign of automatic */
+ o = align(o, t, Ael1);
+ o = align(o, t, Ael2);
+ break;
+ }
+ o = round(o, w);
+ if(debug['A'])
+ print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+ return o;
+}
+
+long
+maxround(long max, long v)
+{
+ v += SZ_VLONG-1;
+ if(v > max)
+ max = round(v, SZ_VLONG);
+ return max;
+}
--- /dev/null
+++ b/utils/0c/txt.c
@@ -1,0 +1,1483 @@
+#include "gc.h"
+
+void
+ginit(void)
+{
+ int i;
+ Type *t;
+
+ thechar = '0';
+ thestring = "spim";
+ exregoffset = REGEXT;
+ exfregoffset = FREGEXT;
+ listinit();
+ nstring = 0;
+ mnstring = 0;
+ nrathole = 0;
+ pc = 0;
+ breakpc = -1;
+ continpc = -1;
+ cases = C;
+ firstp = P;
+ lastp = P;
+ tfield = types[TLONG];
+
+ zprog.link = P;
+ zprog.as = AGOK;
+ zprog.reg = NREG;
+ zprog.from.type = D_NONE;
+ zprog.from.name = D_NONE;
+ zprog.from.reg = NREG;
+ zprog.to = zprog.from;
+
+ regnode.op = OREGISTER;
+ regnode.class = CEXREG;
+ regnode.reg = REGTMP;
+ regnode.complex = 0;
+ regnode.addable = 11;
+ regnode.type = types[TLONG];
+
+ constnode.op = OCONST;
+ constnode.class = CXXX;
+ constnode.complex = 0;
+ constnode.addable = 20;
+ constnode.type = types[TLONG];
+
+ fconstnode.op = OCONST;
+ fconstnode.class = CXXX;
+ fconstnode.complex = 0;
+ fconstnode.addable = 20;
+ fconstnode.type = types[TDOUBLE];
+
+ nodsafe = new(ONAME, Z, Z);
+ nodsafe->sym = slookup(".safe");
+ nodsafe->type = types[TINT];
+ nodsafe->etype = types[TINT]->etype;
+ nodsafe->class = CAUTO;
+ complex(nodsafe);
+
+ t = typ(TARRAY, types[TCHAR]);
+ symrathole = slookup(".rathole");
+ symrathole->class = CGLOBL;
+ symrathole->type = t;
+
+ nodrat = new(ONAME, Z, Z);
+ nodrat->sym = symrathole;
+ nodrat->type = types[TIND];
+ nodrat->etype = TVOID;
+ nodrat->class = CGLOBL;
+ complex(nodrat);
+ nodrat->type = t;
+
+ nodret = new(ONAME, Z, Z);
+ nodret->sym = slookup(".ret");
+ nodret->type = types[TIND];
+ nodret->etype = TIND;
+ nodret->class = CPARAM;
+ nodret = new(OIND, nodret, Z);
+ complex(nodret);
+
+ for(i=0; i<nelem(reg); i++) {
+ reg[i] = 0;
+ if(i == REGZERO ||
+ (i >= NREG && ((i-NREG)&1)))
+ reg[i] = 1;
+ }
+}
+
+void
+gclean(void)
+{
+ int i;
+ Sym *s;
+
+ for(i=0; i<NREG; i++)
+ if(i != REGZERO)
+ if(reg[i])
+ diag(Z, "reg %d left allocated", i);
+ for(i=NREG; i<NREG+NREG; i+=2)
+ if(reg[i])
+ diag(Z, "freg %d left allocated", i-NREG);
+ while(mnstring)
+ outstring("", 1L);
+ symstring->type->width = nstring;
+ symrathole->type->width = nrathole;
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ if(s->type == T)
+ continue;
+ if(s->type->width == 0)
+ continue;
+ if(s->class != CGLOBL && s->class != CSTATIC)
+ continue;
+ if(s->type == types[TENUM])
+ continue;
+ gpseudo(AGLOBL, s, nodconst(s->type->width));
+ }
+ nextpc();
+ p->as = AEND;
+ outcode();
+}
+
+void
+nextpc(void)
+{
+
+ p = alloc(sizeof(*p));
+ *p = zprog;
+ p->lineno = nearln;
+ pc++;
+ if(firstp == P) {
+ firstp = p;
+ lastp = p;
+ return;
+ }
+ lastp->link = p;
+ lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+ long regs;
+ Node fnxargs[20], *fnxp;
+
+ regs = cursafe;
+
+ fnxp = fnxargs;
+ garg1(n, tn1, tn2, 0, &fnxp); /* compile fns to temps */
+
+ curarg = 0;
+ fnxp = fnxargs;
+ garg1(n, tn1, tn2, 1, &fnxp); /* compile normal args and temps */
+
+ cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+ Node nod;
+
+ if(n == Z)
+ return;
+ if(n->op == OLIST) {
+ garg1(n->left, tn1, tn2, f, fnxp);
+ garg1(n->right, tn1, tn2, f, fnxp);
+ return;
+ }
+ if(f == 0) {
+ if(n->complex >= FNX) {
+ regsalloc(*fnxp, n);
+ nod = znode;
+ nod.op = OAS;
+ nod.left = *fnxp;
+ nod.right = n;
+ nod.type = n->type;
+ cgen(&nod, Z);
+ (*fnxp)++;
+ }
+ return;
+ }
+ if(typesu[n->type->etype]) {
+ regaalloc(tn2, n);
+ if(n->complex >= FNX) {
+ sugen(*fnxp, tn2, n->type->width);
+ (*fnxp)++;
+ } else
+ sugen(n, tn2, n->type->width);
+ return;
+ }
+ if(REGARG && curarg == 0 && typechlp[n->type->etype]) {
+ regaalloc1(tn1, n);
+ if(n->complex >= FNX) {
+ cgen(*fnxp, tn1);
+ (*fnxp)++;
+ } else
+ cgen(n, tn1);
+ return;
+ }
+ if(vconst(n) == 0) {
+ regaalloc(tn2, n);
+ gopcode(OAS, n, Z, tn2);
+ return;
+ }
+ regalloc(tn1, n, Z);
+ if(n->complex >= FNX) {
+ cgen(*fnxp, tn1);
+ (*fnxp)++;
+ } else
+ cgen(n, tn1);
+ regaalloc(tn2, n);
+ gopcode(OAS, tn1, Z, tn2);
+ regfree(tn1);
+}
+
+Node*
+nodconst(long v)
+{
+ constnode.vconst = v;
+ return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+ fconstnode.fconst = d;
+ return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+ *n = regnode;
+ n->reg = reg;
+ n->type = nn->type;
+ n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+ int r;
+
+ r = REGRET;
+ if(typefd[nn->type->etype])
+ r = FREGRET+NREG;
+ nodreg(n, nn, r);
+ reg[r]++;
+}
+
+int
+tmpreg(void)
+{
+ int i;
+
+ for(i=REGRET+1; i<NREG; i++)
+ if(reg[i] == 0)
+ return i;
+ diag(Z, "out of fixed registers");
+ return 0;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+ int i, j;
+ static int lasti;
+
+ switch(tn->type->etype) {
+ case TCHAR:
+ case TUCHAR:
+ case TSHORT:
+ case TUSHORT:
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TUVLONG:
+ case TVLONG:
+ if(o != Z && o->op == OREGISTER) {
+ i = o->reg;
+ if(i > 0 && i < NREG)
+ goto out;
+ }
+ j = lasti + REGRET+1;
+ for(i=REGRET+1; i<NREG; i++) {
+ if(j >= NREG)
+ j = REGRET+1;
+ if(reg[j] == 0) {
+ i = j;
+ goto out;
+ }
+ j++;
+ }
+ diag(tn, "out of fixed registers");
+ goto err;
+
+ case TFLOAT:
+ case TDOUBLE:
+ if(o != Z && o->op == OREGISTER) {
+ i = o->reg;
+ if(i >= NREG && i < NREG+NREG)
+ goto out;
+ }
+ j = 0*2 + NREG;
+ for(i=NREG; i<NREG+NREG; i+=2) {
+ if(j >= NREG+NREG)
+ j = NREG;
+ if(reg[j] == 0) {
+ i = j;
+ goto out;
+ }
+ j += 2;
+ }
+ diag(tn, "out of float registers");
+ goto err;
+ }
+ diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+ i = 0;
+out:
+ if(i)
+ reg[i]++;
+ lasti++;
+ if(lasti >= 5)
+ lasti = 0;
+ nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+ Node nod;
+
+ nod = *tn;
+ nod.type = types[TIND];
+ regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+ int i;
+
+ i = 0;
+ if(n->op != OREGISTER && n->op != OINDREG)
+ goto err;
+ i = n->reg;
+ if(i < 0 || i >= sizeof(reg))
+ goto err;
+ if(reg[i] <= 0)
+ goto err;
+ reg[i]--;
+ return;
+err:
+ diag(n, "error in regfree: %d", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+ cursafe = align(cursafe, nn->type, Aaut3);
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+ *n = *nodsafe;
+ n->xoffset = -(stkoff + cursafe);
+ n->type = nn->type;
+ n->etype = nn->type->etype;
+ n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+ nodreg(n, nn, REGARG);
+ reg[REGARG]++;
+ curarg = align(curarg, nn->type, Aarg1);
+ curarg = align(curarg, nn->type, Aarg2);
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+ curarg = align(curarg, nn->type, Aarg1);
+ *n = *nn;
+ n->op = OINDREG;
+ n->reg = REGSP;
+ n->xoffset = curarg + SZ_VLONG;
+ n->complex = 0;
+ n->addable = 20;
+ curarg = align(curarg, nn->type, Aarg2);
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+ if(n->op != OREGISTER) {
+ diag(n, "regind not OREGISTER");
+ return;
+ }
+ n->op = OINDREG;
+ n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+ Adr a;
+
+ naddr(n, &a);
+ if(a.type == D_CONST && a.offset == 0) {
+ a.type = D_REG;
+ a.reg = 0;
+ }
+ if(a.type != D_REG && a.type != D_FREG) {
+ if(n)
+ diag(n, "bad in raddr: %O", n->op);
+ else
+ diag(n, "bad in raddr: <null>");
+ p->reg = NREG;
+ } else
+ p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+ long v;
+
+ a->type = D_NONE;
+ if(n == Z)
+ return;
+ switch(n->op) {
+ default:
+ bad:
+ diag(n, "bad in naddr: %O", n->op);
+ break;
+
+ case OREGISTER:
+ a->type = D_REG;
+ a->sym = S;
+ a->reg = n->reg;
+ if(a->reg >= NREG) {
+ a->type = D_FREG;
+ a->reg -= NREG;
+ }
+ break;
+
+ case OIND:
+ naddr(n->left, a);
+ if(a->type == D_REG) {
+ a->type = D_OREG;
+ break;
+ }
+ if(a->type == D_CONST) {
+ a->type = D_OREG;
+ break;
+ }
+ goto bad;
+
+ case OINDREG:
+ a->type = D_OREG;
+ a->sym = S;
+ a->offset = n->xoffset;
+ a->reg = n->reg;
+ break;
+
+ case ONAME:
+ a->etype = n->etype;
+ a->type = D_OREG;
+ a->name = D_STATIC;
+ a->sym = n->sym;
+ a->offset = n->xoffset;
+ if(n->class == CSTATIC)
+ break;
+ if(n->class == CEXTERN || n->class == CGLOBL) {
+ a->name = D_EXTERN;
+ break;
+ }
+ if(n->class == CAUTO) {
+ a->name = D_AUTO;
+ break;
+ }
+ if(n->class == CPARAM) {
+ a->name = D_PARAM;
+ break;
+ }
+ goto bad;
+
+ case OCONST:
+ a->sym = S;
+ a->reg = NREG;
+ if(typefd[n->type->etype]) {
+ a->type = D_FCONST;
+ a->dval = n->fconst;
+ } else
+ if(llconst(n)) {
+ a->type = D_VCONST;
+ a->vval = n->vconst;
+ } else {
+ a->type = D_CONST;
+ a->offset = n->vconst;
+ }
+ break;
+
+ case OADDR:
+ naddr(n->left, a);
+ if(a->type == D_OREG) {
+ a->type = D_CONST;
+ break;
+ }
+ goto bad;
+
+ case OADD:
+ if(n->left->op == OCONST) {
+ naddr(n->left, a);
+ v = a->offset;
+ naddr(n->right, a);
+ } else {
+ naddr(n->right, a);
+ v = a->offset;
+ naddr(n->left, a);
+ }
+ a->offset += v;
+ break;
+
+ }
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+ Node nod1, nod2, nod3;
+
+ nodreg(&nod1, t, NREG+f1);
+ nodreg(&nod2, t, NREG+f2);
+ regalloc(&nod3, t, t);
+ gopcode(as, &nod1, &nod2, &nod3);
+ gmove(&nod3, t);
+ regfree(&nod3);
+}
+
+void
+gmove(Node *f, Node *t)
+{
+ int ft, tt, a;
+ Node nod;
+ Prog *p1;
+ double d;
+
+ ft = f->type->etype;
+ tt = t->type->etype;
+
+ if(ft == TDOUBLE && f->op == OCONST) {
+ d = f->fconst;
+ if(d == 0.0) {
+ a = FREGZERO;
+ goto ffreg;
+ }
+ if(d == 0.5) {
+ a = FREGHALF;
+ goto ffreg;
+ }
+ if(d == 1.0) {
+ a = FREGONE;
+ goto ffreg;
+ }
+ if(d == 2.0) {
+ a = FREGTWO;
+ goto ffreg;
+ }
+ if(d == -.5) {
+ fop(OSUB, FREGHALF, FREGZERO, t);
+ return;
+ }
+ if(d == -1.0) {
+ fop(OSUB, FREGONE, FREGZERO, t);
+ return;
+ }
+ if(d == -2.0) {
+ fop(OSUB, FREGTWO, FREGZERO, t);
+ return;
+ }
+ if(d == 1.5) {
+ fop(OADD, FREGONE, FREGHALF, t);
+ return;
+ }
+ if(d == 2.5) {
+ fop(OADD, FREGTWO, FREGHALF, t);
+ return;
+ }
+ if(d == 3.0) {
+ fop(OADD, FREGTWO, FREGONE, t);
+ return;
+ }
+ }
+ if(ft == TFLOAT && f->op == OCONST) {
+ d = f->fconst;
+ if(d == 0) {
+ a = FREGZERO;
+ ffreg:
+ nodreg(&nod, f, NREG+a);
+ gmove(&nod, t);
+ return;
+ }
+ }
+ /*
+ * a load --
+ * put it into a register then
+ * worry what to do with it.
+ */
+ if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+ switch(ft) {
+ default:
+ if(typefd[tt]) {
+ /* special case can load mem to Freg */
+ regalloc(&nod, t, t);
+ gins(AMOVW, f, &nod);
+ a = AMOVWD;
+ if(tt == TFLOAT)
+ a = AMOVWF;
+ gins(a, &nod, &nod);
+ gmove(&nod, t);
+ regfree(&nod);
+ return;
+ }
+ a = AMOVW;
+ break;
+ case TCHAR:
+ a = AMOVB;
+ break;
+ case TUCHAR:
+ a = AMOVBU;
+ break;
+ case TSHORT:
+ a = AMOVH;
+ break;
+ case TUSHORT:
+ a = AMOVHU;
+ break;
+ case TFLOAT:
+ a = AMOVF;
+ break;
+ case TDOUBLE:
+ a = AMOVD;
+ break;
+ case TUVLONG:
+ case TVLONG:
+ a = AMOVV;
+ break;
+ }
+ if(typechlp[ft] && typeilp[tt])
+ regalloc(&nod, t, t);
+ else
+ regalloc(&nod, f, t);
+ gins(a, f, &nod);
+ gmove(&nod, t);
+ regfree(&nod);
+ return;
+ }
+
+ /*
+ * a store --
+ * put it into a register then
+ * store it.
+ */
+ if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+ switch(tt) {
+ default:
+ a = AMOVW;
+ break;
+ case TUCHAR:
+ a = AMOVBU;
+ break;
+ case TCHAR:
+ a = AMOVB;
+ break;
+ case TUSHORT:
+ a = AMOVHU;
+ break;
+ case TSHORT:
+ a = AMOVH;
+ break;
+ case TFLOAT:
+ a = AMOVF;
+ break;
+ case TDOUBLE:
+ a = AMOVD;
+ break;
+ case TUVLONG:
+ case TVLONG:
+ a = AMOVV;
+ break;
+ }
+ if(!typefd[ft] && vconst(f) == 0) {
+ gins(a, f, t);
+ return;
+ }
+ if(ft == tt)
+ regalloc(&nod, t, f);
+ else
+ regalloc(&nod, t, Z);
+ gmove(f, &nod);
+ gins(a, &nod, t);
+ regfree(&nod);
+ return;
+ }
+
+ /*
+ * type x type cross table
+ */
+ a = AGOK;
+ switch(ft) {
+ case TUVLONG:
+ case TVLONG:
+ switch(tt) {
+ case TUVLONG:
+ case TVLONG:
+ a = AMOVV;
+ break;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ case TDOUBLE:
+ gins(AMOVW, f, t);
+ gins(AMOVWD, t, t);
+ if(ft == TULONG || ft == TUINT) {
+ regalloc(&nod, t, Z);
+ gins(ACMPGED, t, Z);
+ p->reg = FREGZERO;
+ gins(ABFPT, Z, Z);
+ p1 = p;
+ gins(AMOVD, nodfconst(4294967296.), &nod);
+ gins(AADDD, &nod, t);
+ patch(p1, pc);
+ regfree(&nod);
+ }
+ return;
+ case TFLOAT:
+ gins(AMOVW, f, t);
+ gins(AMOVWF, t, t);
+ if(ft == TULONG || ft == TUINT) {
+ regalloc(&nod, t, Z);
+ gins(ACMPGEF, t, Z);
+ p->reg = FREGZERO;
+ gins(ABFPT, Z, Z);
+ p1 = p;
+ gins(AMOVF, nodfconst(4294967296.), &nod);
+ gins(AADDF, &nod, t);
+ patch(p1, pc);
+ regfree(&nod);
+ }
+ return;
+ }
+ break;
+ case TDOUBLE:
+ case TFLOAT:
+ switch(tt) {
+ case TDOUBLE:
+ a = AMOVD;
+ if(ft == TFLOAT)
+ a = AMOVFD;
+ break;
+ case TFLOAT:
+ a = AMOVDF;
+ if(ft == TFLOAT)
+ a = AMOVF;
+ break;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ regalloc(&nod, f, Z);
+ gins(ATRUNCDW, f, &nod);
+ if(ft == TFLOAT)
+ p->as = ATRUNCFW;
+ gins(AMOVW, &nod, t);
+ regfree(&nod);
+ return;
+ case TUVLONG:
+ case TVLONG:
+ regalloc(&nod, f, Z);
+ gins(ATRUNCDV, f, &nod);
+ if(ft == TFLOAT)
+ p->as = ATRUNCFV;
+ gins(AMOVV, &nod, t);
+ regfree(&nod);
+ return;
+ }
+ break;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ switch(tt) {
+ case TDOUBLE:
+ gins(AMOVW, f, t);
+ gins(AMOVWD, t, t);
+ if(ft == TULONG || ft == TUINT) {
+ regalloc(&nod, t, Z);
+ gins(ACMPGED, t, Z);
+ p->reg = FREGZERO;
+ gins(ABFPT, Z, Z);
+ p1 = p;
+ gins(AMOVD, nodfconst(4294967296.), &nod);
+ gins(AADDD, &nod, t);
+ patch(p1, pc);
+ regfree(&nod);
+ }
+ return;
+ case TFLOAT:
+ gins(AMOVW, f, t);
+ gins(AMOVWF, t, t);
+ if(ft == TULONG || ft == TUINT) {
+ regalloc(&nod, t, Z);
+ gins(ACMPGEF, t, Z);
+ p->reg = FREGZERO;
+ gins(ABFPT, Z, Z);
+ p1 = p;
+ gins(AMOVF, nodfconst(4294967296.), &nod);
+ gins(AADDF, &nod, t);
+ patch(p1, pc);
+ regfree(&nod);
+ }
+ return;
+ case TUVLONG:
+ case TVLONG:
+ if(ft == TULONG || ft == TUINT) {
+ a = AMOVWU;
+ break;
+ }
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TSHORT:
+ switch(tt) {
+ case TDOUBLE:
+ regalloc(&nod, f, Z);
+ gins(AMOVH, f, &nod);
+ gins(AMOVW, &nod, t);
+ gins(AMOVWD, t, t);
+ regfree(&nod);
+ return;
+ case TFLOAT:
+ regalloc(&nod, f, Z);
+ gins(AMOVH, f, &nod);
+ gins(AMOVW, &nod, t);
+ gins(AMOVWF, t, t);
+ regfree(&nod);
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TVLONG:
+ case TUVLONG:
+ case TIND:
+ a = AMOVH;
+ break;
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TUSHORT:
+ switch(tt) {
+ case TDOUBLE:
+ regalloc(&nod, f, Z);
+ gins(AMOVHU, f, &nod);
+ gins(AMOVW, &nod, t);
+ gins(AMOVWD, t, t);
+ regfree(&nod);
+ return;
+ case TFLOAT:
+ regalloc(&nod, f, Z);
+ gins(AMOVHU, f, &nod);
+ gins(AMOVW, &nod, t);
+ gins(AMOVWF, t, t);
+ regfree(&nod);
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TVLONG:
+ case TUVLONG:
+ case TIND:
+ a = AMOVHU;
+ break;
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TCHAR:
+ switch(tt) {
+ case TDOUBLE:
+ regalloc(&nod, f, Z);
+ gins(AMOVB, f, &nod);
+ gins(AMOVW, &nod, t);
+ gins(AMOVWD, t, t);
+ regfree(&nod);
+ return;
+ case TFLOAT:
+ regalloc(&nod, f, Z);
+ gins(AMOVB, f, &nod);
+ gins(AMOVW, &nod, t);
+ gins(AMOVWF, t, t);
+ regfree(&nod);
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TVLONG:
+ case TUVLONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ a = AMOVB;
+ break;
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TUCHAR:
+ switch(tt) {
+ case TDOUBLE:
+ regalloc(&nod, f, Z);
+ gins(AMOVBU, f, &nod);
+ gins(AMOVW, &nod, t);
+ gins(AMOVWD, t, t);
+ regfree(&nod);
+ return;
+ case TFLOAT:
+ regalloc(&nod, f, Z);
+ gins(AMOVBU, f, &nod);
+ gins(AMOVW, &nod, t);
+ gins(AMOVWF, t, t);
+ regfree(&nod);
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TVLONG:
+ case TUVLONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ a = AMOVBU;
+ break;
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ }
+ if(a == AGOK)
+ diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+ if(a == AMOVW || a == AMOVF || a == AMOVD || a == AMOVV)
+ if(samaddr(f, t))
+ return;
+ gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+ nextpc();
+ p->as = a;
+ if(f != Z)
+ naddr(f, &p->from);
+ if(t != Z)
+ naddr(t, &p->to);
+ if(debug['g'])
+ print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+ int a, et, ett;
+ Adr ta;
+ Node nod;
+
+ et = TLONG;
+ if(f1 != Z && f1->type != T)
+ et = f1->type->etype;
+ ett = TLONG;
+ if(t != Z && t->type != T)
+ ett = t->type->etype;
+ if(llconst(f1) && o != OAS) {
+ regalloc(&nod, f1, Z);
+ gmove(f1, &nod);
+ gopcode(o, &nod, f2, t);
+ regfree(&nod);
+ return;
+ }
+ a = AGOK;
+ switch(o) {
+ case OAS:
+ gmove(f1, t);
+ return;
+
+ case OASADD:
+ case OADD:
+ a = AADDU;
+ if(et == TVLONG || et == TUVLONG)
+ a = AADDVU;
+ else
+ if(et == TFLOAT)
+ a = AADDF;
+ else
+ if(et == TDOUBLE)
+ a = AADDD;
+ break;
+
+ case OASSUB:
+ case OSUB:
+ a = ASUBU;
+ if(et == TVLONG || et == TUVLONG)
+ a = ASUBVU;
+ else
+ if(et == TFLOAT)
+ a = ASUBF;
+ else
+ if(et == TDOUBLE)
+ a = ASUBD;
+ break;
+
+ case OASOR:
+ case OOR:
+ a = AOR;
+ break;
+
+ case OASAND:
+ case OAND:
+ a = AAND;
+ break;
+
+ case OASXOR:
+ case OXOR:
+ a = AXOR;
+ break;
+
+ case OASLSHR:
+ case OLSHR:
+ a = ASRL;
+ if(ett == TVLONG || ett == TUVLONG)
+ a = ASRLV;
+ break;
+
+ case OASASHR:
+ case OASHR:
+ a = ASRA;
+ if(ett == TVLONG || ett == TUVLONG)
+ a = ASRAV;
+ break;
+
+ case OASASHL:
+ case OASHL:
+ a = ASLL;
+ if(ett == TVLONG || ett == TUVLONG)
+ a = ASLLV;
+ break;
+
+ case OFUNC:
+ a = AJAL;
+ break;
+
+ case OCOND:
+ a = ASGTU;
+ break;
+
+ case OCOMMA:
+ a = ASGT;
+ break;
+
+ case OASMUL:
+ case OMUL:
+ if(et == TFLOAT) {
+ a = AMULF;
+ break;
+ } else
+ if(et == TDOUBLE) {
+ a = AMULD;
+ break;
+ }
+ a = AMUL;
+ if(et == TVLONG || et == TUVLONG)
+ a = AMULV;
+ goto muldiv;
+
+ case OASDIV:
+ case ODIV:
+ if(et == TFLOAT) {
+ a = ADIVF;
+ break;
+ } else
+ if(et == TDOUBLE) {
+ a = ADIVD;
+ break;
+ }
+ a = ADIV;
+ if(et == TVLONG || et == TUVLONG)
+ a = ADIVV;
+ goto muldiv;
+
+ case OASMOD:
+ case OMOD:
+ a = ADIV;
+ o = OMOD;
+ if(et == TVLONG || et == TUVLONG)
+ a = ADIVV;
+ goto muldiv;
+
+ case OASLMUL:
+ case OLMUL:
+ a = AMULU;
+ if(et == TVLONG || et == TUVLONG)
+ a = AMULVU;
+ goto muldiv;
+
+ case OASLMOD:
+ case OLMOD:
+ o = OMOD;
+
+ case OASLDIV:
+ case OLDIV:
+ a = ADIVU;
+ if(et == TVLONG || et == TUVLONG)
+ a = ADIVVU;
+ goto muldiv;
+
+ muldiv:
+ nextpc();
+ naddr(f1, &p->from);
+ if(f2 == Z)
+ raddr(t, p);
+ else
+ raddr(f2, p);
+ p->as = a;
+ if(debug['g'])
+ print("%P\n", p);
+ nextpc();
+ p->as = AMOVW;
+ if(et == TVLONG || et == TUVLONG)
+ p->as = AMOVV;
+ a = D_LO;
+ if(o == OMOD)
+ a = D_HI;
+ p->from.type = a;
+ naddr(t, &p->to);
+ if(debug['g'])
+ print("%P\n", p);
+ return;
+
+ case OEQ:
+ if(!typefd[et]) {
+ a = ABEQ;
+ break;
+ }
+
+ case ONE:
+ if(!typefd[et]) {
+ a = ABNE;
+ break;
+ }
+
+ case OLT:
+ case OLE:
+ case OGE:
+ case OGT:
+ if(typefd[et]) {
+ nextpc();
+ if(et == TFLOAT) {
+ a = ACMPGTF;
+ if(o == OEQ || o == ONE)
+ a = ACMPEQF;
+ else
+ if(o == OLT || o == OGE)
+ a = ACMPGEF;
+ } else {
+ a = ACMPGTD;
+ if(o == OEQ || o == ONE)
+ a = ACMPEQD;
+ else
+ if(o == OLT || o == OGE)
+ a = ACMPGED;
+ }
+ p->as = a;
+ naddr(f1, &p->from);
+ raddr(f2, p);
+ if(debug['g'])
+ print("%P\n", p);
+ nextpc();
+ a = ABFPF;
+ if(o == OEQ || o == OGE || o == OGT)
+ a = ABFPT;
+ p->as = a;
+ if(debug['g'])
+ print("%P\n", p);
+ return;
+ }
+ if(vconst(f1) == 0 || vconst(f2) == 0) {
+ if(vconst(f1) == 0) {
+ o = invrel[relindex(o)];
+ f1 = f2;
+ }
+ switch(o) {
+ case OLT:
+ a = ABLTZ;
+ break;
+ case OLE:
+ a = ABLEZ;
+ break;
+ case OGE:
+ a = ABGEZ;
+ break;
+ case OGT:
+ a = ABGTZ;
+ break;
+ }
+ f2 = Z;
+ break;
+ }
+
+ case OLO:
+ case OLS:
+ case OHS:
+ case OHI:
+ nextpc();
+ if(o == OLE || o == OGT || o == OLS || o == OHI) {
+ naddr(f1, &p->from);
+ raddr(f2, p);
+ } else {
+ naddr(f2, &p->from);
+ raddr(f1, p);
+ }
+ naddr(®node, &p->to);
+ p->to.reg = tmpreg();
+ a = ASGT;
+ if(o == OLO || o == OLS || o == OHS || o == OHI)
+ a = ASGTU;
+ p->as = a;
+ if(debug['g'])
+ print("%P\n", p);
+
+ nextpc();
+ naddr(®node, &p->from);
+ p->from.reg = tmpreg();
+ a = ABEQ;
+ if(o == OLT || o == OGT || o == OLO || o == OHI)
+ a = ABNE;
+ p->as = a;
+ if(debug['g'])
+ print("%P\n", p);
+ return;
+ }
+ if(a == AGOK)
+ diag(Z, "bad in gopcode %O", o);
+ nextpc();
+ p->as = a;
+ if(f1 != Z)
+ naddr(f1, &p->from);
+ if(f2 != Z) {
+ naddr(f2, &ta);
+ p->reg = ta.reg;
+ if(ta.type == D_CONST && ta.offset == 0)
+ p->reg = REGZERO;
+ }
+ if(t != Z)
+ naddr(t, &p->to);
+ if(debug['g'])
+ print("%P\n", p);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+ if(f->op != t->op)
+ return 0;
+ switch(f->op) {
+
+ case OREGISTER:
+ if(f->reg != t->reg)
+ break;
+ return 1;
+ }
+ return 0;
+}
+
+void
+gbranch(int o)
+{
+ int a;
+
+ a = AGOK;
+ switch(o) {
+ case ORETURN:
+ a = ARET;
+ break;
+ case OGOTO:
+ a = AJMP;
+ break;
+ }
+ nextpc();
+ if(a == AGOK) {
+ diag(Z, "bad in gbranch %O", o);
+ nextpc();
+ }
+ p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+ op->to.offset = pc;
+ op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+ nextpc();
+ p->as = a;
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ p->from.name = D_EXTERN;
+ if(s->class == CSTATIC)
+ p->from.name = D_STATIC;
+ naddr(n, &p->to);
+ if(a == ADATA || a == AGLOBL)
+ pc--;
+}
+
+int
+sconst(Node *n)
+{
+ vlong vv;
+
+ if(n->op == OCONST) {
+ if(!typefd[n->type->etype]) {
+ vv = n->vconst;
+ if(vv >= (vlong)-32766 && vv < (vlong)32766)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+llconst(Node *n)
+{
+ vlong vv;
+
+ if(n != Z && n->op == OCONST) {
+ if(typev[n->type->etype]) {
+ vv = n->vconst >> 32;
+ if(vv != 0 && vv != -1)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+sval(long v)
+{
+ if(v >= -32766L && v < 32766L)
+ return 1;
+ return 0;
+}
+
+long
+exreg(Type *t)
+{
+ long o;
+
+ if(typechlp[t->etype]) {
+ if(exregoffset <= 16)
+ return 0;
+ o = exregoffset;
+ exregoffset--;
+ return o;
+ }
+ if(typefd[t->etype]) {
+ if(exfregoffset <= 16)
+ return 0;
+ o = exfregoffset + NREG;
+ exfregoffset--;
+ return o;
+ }
+ return 0;
+}
+
+schar ewidth[NTYPE] =
+{
+ -1, /*[TXXX]*/
+ SZ_CHAR, /*[TCHAR]*/
+ SZ_CHAR, /*[TUCHAR]*/
+ SZ_SHORT, /*[TSHORT]*/
+ SZ_SHORT, /*[TUSHORT]*/
+ SZ_INT, /*[TINT]*/
+ SZ_INT, /*[TUINT]*/
+ SZ_LONG, /*[TLONG]*/
+ SZ_LONG, /*[TULONG]*/
+ SZ_VLONG, /*[TVLONG]*/
+ SZ_VLONG, /*[TUVLONG]*/
+ SZ_FLOAT, /*[TFLOAT]*/
+ SZ_DOUBLE, /*[TDOUBLE]*/
+ SZ_IND, /*[TIND]*/
+ 0, /*[TFUNC]*/
+ -1, /*[TARRAY]*/
+ 0, /*[TVOID]*/
+ -1, /*[TSTRUCT]*/
+ -1, /*[TUNION]*/
+ SZ_INT, /*[TENUM]*/
+};
+long ncast[NTYPE] =
+{
+ 0, /*[TXXX]*/
+ BCHAR|BUCHAR, /*[TCHAR]*/
+ BCHAR|BUCHAR, /*[TUCHAR]*/
+ BSHORT|BUSHORT, /*[TSHORT]*/
+ BSHORT|BUSHORT, /*[TUSHORT]*/
+ BINT|BUINT|BLONG|BULONG|BIND, /*[TINT]*/
+ BINT|BUINT|BLONG|BULONG|BIND, /*[TUINT]*/
+ BINT|BUINT|BLONG|BULONG|BIND, /*[TLONG]*/
+ BINT|BUINT|BLONG|BULONG|BIND, /*[TULONG]*/
+ BVLONG|BUVLONG, /*[TVLONG]*/
+ BVLONG|BUVLONG, /*[TUVLONG]*/
+ BFLOAT, /*[TFLOAT]*/
+ BDOUBLE, /*[TDOUBLE]*/
+ BLONG|BULONG|BIND, /*[TIND]*/
+ 0, /*[TFUNC]*/
+ 0, /*[TARRAY]*/
+ 0, /*[TVOID]*/
+ BSTRUCT, /*[TSTRUCT]*/
+ BUNION, /*[TUNION]*/
+ 0, /*[TENUM]*/
+};
--- /dev/null
+++ b/utils/0c/v.out.h
@@ -1,0 +1,201 @@
+#define NSNAME 8
+#define NSYM 50
+#define NREG 32
+
+#define NOPROF (1<<0)
+#define DUPOK (1<<1)
+
+#define REGZERO 0
+#define REGRET 1
+#define REGARG 1
+/* compiler allocates R1 up as temps */
+/* compiler allocates register variables R3-R23 */
+#define REGEXT 25
+/* compiler allocates external registers R25 down */
+/* dont use R26 R27 */
+#define REGTMP 28
+#define REGSP 29
+#define REGSB 30
+#define REGLINK 31
+
+#define FREGRET 0
+/* compiler allocates register variables F4-F22 */
+/* compiler allocates external registers F22 down */
+#define FREGEXT 22
+#define FREGZERO 24 /* both float and double */
+#define FREGHALF 26 /* double */
+#define FREGONE 28 /* double */
+#define FREGTWO 30 /* double */
+
+enum as
+{
+ AXXX,
+
+ AABSD,
+ AABSF,
+ AABSW,
+ AADD,
+ AADDD,
+ AADDF,
+ AADDU,
+ AADDW,
+ AAND,
+ ABEQ,
+ ABFPF,
+ ABFPT,
+ ABGEZ,
+ ABGEZAL,
+ ABGTZ,
+ ABLEZ,
+ ABLTZ,
+ ABLTZAL,
+ ABNE,
+ ABREAK,
+ ACMPEQD,
+ ACMPEQF,
+ ACMPGED,
+ ACMPGEF,
+ ACMPGTD,
+ ACMPGTF,
+ ADATA,
+ ADIV,
+ ADIVD,
+ ADIVF,
+ ADIVU,
+ ADIVW,
+ AGLOBL,
+ AGOK,
+ AHISTORY,
+ AJAL,
+ AJMP,
+ AMOVB,
+ AMOVBU,
+ AMOVD,
+ AMOVDF,
+ AMOVDW,
+ AMOVF,
+ AMOVFD,
+ AMOVFW,
+ AMOVH,
+ AMOVHU,
+ AMOVW,
+ AMOVWD,
+ AMOVWF,
+ AMOVWL,
+ AMOVWR,
+ AMUL,
+ AMULD,
+ AMULF,
+ AMULU,
+ AMULW,
+ ANAME,
+ ANEGD,
+ ANEGF,
+ ANEGW,
+ ANOP,
+ ANOR,
+ AOR,
+ AREM,
+ AREMU,
+ ARET,
+ ARFE,
+ ASGT,
+ ASGTU,
+ ASLL,
+ ASRA,
+ ASRL,
+ ASUB,
+ ASUBD,
+ ASUBF,
+ ASUBU,
+ ASUBW,
+ ASYSCALL,
+ ATEXT,
+ ATLBP,
+ ATLBR,
+ ATLBWI,
+ ATLBWR,
+ AWORD,
+ AXOR,
+
+ AEND,
+
+ AMOVV,
+ AMOVVL,
+ AMOVVR,
+ ASLLV,
+ ASRAV,
+ ASRLV,
+ ADIVV,
+ ADIVVU,
+ AREMV,
+ AREMVU,
+ AMULV,
+ AMULVU,
+ AADDV,
+ AADDVU,
+ ASUBV,
+ ASUBVU,
+
+ ADYNT,
+ AINIT,
+
+ ABCASE,
+ ACASE,
+
+ ATRUNCFV,
+ ATRUNCDV,
+ ATRUNCFW,
+ ATRUNCDW,
+ AMOVWU,
+ AMOVFV,
+ AMOVDV,
+ AMOVVF,
+ AMOVVD,
+
+ ASIGNAME,
+
+ ALAST,
+};
+
+/* type/name */
+#define D_GOK 0
+#define D_NONE 1
+
+/* type */
+#define D_BRANCH (D_NONE+1)
+#define D_OREG (D_NONE+2)
+#define D_EXTERN (D_NONE+3) /* name */
+#define D_STATIC (D_NONE+4) /* name */
+#define D_AUTO (D_NONE+5) /* name */
+#define D_PARAM (D_NONE+6) /* name */
+#define D_CONST (D_NONE+7)
+#define D_FCONST (D_NONE+8)
+#define D_SCONST (D_NONE+9)
+#define D_HI (D_NONE+10)
+#define D_LO (D_NONE+11)
+#define D_REG (D_NONE+12)
+#define D_FREG (D_NONE+13)
+#define D_FCREG (D_NONE+14)
+#define D_MREG (D_NONE+15)
+#define D_FILE (D_NONE+16)
+#define D_OCONST (D_NONE+17)
+#define D_FILE1 (D_NONE+18)
+#define D_VCONST (D_NONE+19)
+
+/*
+ * this is the ranlib header
+ */
+#define SYMDEF "__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef struct ieee Ieee;
+struct ieee
+{
+ long l; /* contains ls-man 0xffffffff */
+ long h; /* contains sign 0x80000000
+ exp 0x7ff00000
+ ms-man 0x000fffff */
+};
--- /dev/null
+++ b/utils/0l/asm.c
@@ -1,0 +1,1433 @@
+#include "l.h"
+
+long OFFSET;
+/*
+long BADOFFSET = -1;
+
+ if(OFFSET <= BADOFFSET && OFFSET+4 > BADOFFSET)\
+ abort();\
+ OFFSET += 4;\
+
+ if(OFFSET == BADOFFSET)\
+ abort();\
+ OFFSET++;\
+*/
+
+void
+cput(int c)
+{
+ cbp[0] = c;
+ cbp++;
+ cbc--;
+ if(cbc <= 0)
+ cflush();
+}
+
+void
+bput(long l)
+{
+ cbp[0] = l>>24;
+ cbp[1] = l>>16;
+ cbp[2] = l>>8;
+ cbp[3] = l;
+ cbp += 4;
+ cbc -= 4;
+ if(cbc <= 0)
+ cflush();
+}
+
+void
+lput(long l)
+{
+
+ cbp[0] = l;
+ cbp[1] = l>>8;
+ cbp[2] = l>>16;
+ cbp[3] = l>>24;
+ cbp += 4;
+ cbc -= 4;
+ if(cbc <= 0)
+ cflush();
+}
+
+long
+entryvalue(void)
+{
+ char *a;
+ Sym *s;
+
+ a = INITENTRY;
+ if(*a >= '0' && *a <= '9')
+ return atolwhex(a);
+ s = lookup(a, 0);
+ if(s->type == 0)
+ return INITTEXT;
+ if(s->type != STEXT && s->type != SLEAF)
+ diag("entry not text: %s", s->name);
+ return s->value;
+}
+
+void
+asmb(void)
+{
+ Prog *p;
+ long t;
+ Optab *o;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f asm\n", cputime());
+ Bflush(&bso);
+ OFFSET = HEADR;
+ seek(cout, OFFSET, 0);
+ pc = INITTEXT;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT) {
+ curtext = p;
+ autosize = p->to.offset + 8;
+ }
+ if(p->pc != pc) {
+ diag("phase error %lux sb %lux\n",
+ p->pc, pc);
+ if(!debug['a'])
+ prasm(curp);
+ pc = p->pc;
+ }
+ curp = p;
+ o = oplook(p); /* could probably avoid this call */
+ if(asmout(p, o, 0)) {
+ p = p->link;
+ pc += 4;
+ }
+ pc += o->size;
+ }
+ if(debug['a'])
+ Bprint(&bso, "\n");
+ Bflush(&bso);
+ cflush();
+
+ curtext = P;
+ switch(HEADTYPE) {
+ case 0:
+ case 4:
+ OFFSET = rnd(HEADR+textsize, 4096);
+ seek(cout, OFFSET, 0);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ case 6:
+ OFFSET = HEADR+textsize;
+ seek(cout, OFFSET, 0);
+ break;
+ }
+ for(t = 0; t < datsize; t += sizeof(buf)-100) {
+ if(datsize-t > sizeof(buf)-100)
+ datblk(t, sizeof(buf)-100);
+ else
+ datblk(t, datsize-t);
+ }
+
+ symsize = 0;
+ lcsize = 0;
+ if(!debug['s']) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f sym\n", cputime());
+ Bflush(&bso);
+ switch(HEADTYPE) {
+ case 0:
+ case 4:
+ OFFSET = rnd(HEADR+textsize, 4096)+datsize;
+ seek(cout, OFFSET, 0);
+ break;
+ case 3:
+ case 2:
+ case 1:
+ case 5:
+ case 6:
+ OFFSET = HEADR+textsize+datsize;
+ seek(cout, OFFSET, 0);
+ break;
+ }
+ if(!debug['s'])
+ asmsym();
+ if(debug['v'])
+ Bprint(&bso, "%5.2f pc\n", cputime());
+ Bflush(&bso);
+ if(!debug['s'])
+ asmlc();
+ cflush();
+ }
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f header\n", cputime());
+ Bflush(&bso);
+ OFFSET = 0;
+ seek(cout, OFFSET, 0);
+ switch(HEADTYPE) {
+ case 0:
+ bput(0x160L<<16); /* magic and sections */
+ bput(0L); /* time and date */
+ bput(rnd(HEADR+textsize, 4096)+datsize);
+ bput(symsize); /* nsyms */
+ bput((0x38L<<16)|7L); /* size of optional hdr and flags */
+ bput((0413<<16)|0437L); /* magic and version */
+ bput(rnd(HEADR+textsize, 4096)); /* sizes */
+ bput(datsize);
+ bput(bsssize);
+ bput(entryvalue()); /* va of entry */
+ bput(INITTEXT-HEADR); /* va of base of text */
+ bput(INITDAT); /* va of base of data */
+ bput(INITDAT+datsize); /* va of base of bss */
+ bput(~0L); /* gp reg mask */
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(~0L); /* gp value ?? */
+ break;
+ case 1:
+ bput(0x160L<<16); /* magic and sections */
+ bput(0L); /* time and date */
+ bput(HEADR+textsize+datsize);
+ bput(symsize); /* nsyms */
+ bput((0x38L<<16)|7L); /* size of optional hdr and flags */
+
+ bput((0407<<16)|0437L); /* magic and version */
+ bput(textsize); /* sizes */
+ bput(datsize);
+ bput(bsssize);
+ bput(entryvalue()); /* va of entry */
+ bput(INITTEXT); /* va of base of text */
+ bput(INITDAT); /* va of base of data */
+ bput(INITDAT+datsize); /* va of base of bss */
+ bput(~0L); /* gp reg mask */
+ bput(lcsize);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(~0L); /* gp value ?? */
+ bput(0L); /* complete mystery */
+ break;
+ case 2:
+ t = 22;
+ bput(((((4*t)+0)*t)+7)); /* magic */
+ bput(textsize); /* sizes */
+ bput(datsize);
+ bput(bsssize);
+ bput(symsize); /* nsyms */
+ bput(entryvalue()); /* va of entry */
+ bput(0L);
+ bput(lcsize);
+ break;
+ case 3:
+ bput((0x160L<<16)|3L); /* magic and sections */
+ bput(time(0)); /* time and date */
+ bput(HEADR+textsize+datsize);
+ bput(symsize); /* nsyms */
+ bput((0x38L<<16)|7L); /* size of optional hdr and flags */
+
+ bput((0407<<16)|0437L); /* magic and version */
+ bput(textsize); /* sizes */
+ bput(datsize);
+ bput(bsssize);
+ bput(entryvalue()); /* va of entry */
+ bput(INITTEXT); /* va of base of text */
+ bput(INITDAT); /* va of base of data */
+ bput(INITDAT+datsize); /* va of base of bss */
+ bput(~0L); /* gp reg mask */
+ bput(lcsize);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(~0L); /* gp value ?? */
+
+ strnput(".text", 8); /* text segment */
+ bput(INITTEXT); /* address */
+ bput(INITTEXT);
+ bput(textsize);
+ bput(HEADR);
+ bput(0L);
+ bput(HEADR+textsize+datsize+symsize);
+ bput(lcsize); /* line number size */
+ bput(0x20L); /* flags */
+
+ strnput(".data", 8); /* data segment */
+ bput(INITDAT); /* address */
+ bput(INITDAT);
+ bput(datsize);
+ bput(HEADR+textsize);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(0x40L); /* flags */
+
+ strnput(".bss", 8); /* bss segment */
+ bput(INITDAT+datsize); /* address */
+ bput(INITDAT+datsize);
+ bput(bsssize);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(0x80L); /* flags */
+ break;
+ case 4:
+
+ bput((0x160L<<16)|3L); /* magic and sections */
+ bput(time(0)); /* time and date */
+ bput(rnd(HEADR+textsize, 4096)+datsize);
+ bput(symsize); /* nsyms */
+ bput((0x38L<<16)|7L); /* size of optional hdr and flags */
+
+ bput((0413<<16)|01012L); /* magic and version */
+ bput(textsize); /* sizes */
+ bput(datsize);
+ bput(bsssize);
+ bput(entryvalue()); /* va of entry */
+ bput(INITTEXT); /* va of base of text */
+ bput(INITDAT); /* va of base of data */
+ bput(INITDAT+datsize); /* va of base of bss */
+ bput(~0L); /* gp reg mask */
+ bput(lcsize);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(~0L); /* gp value ?? */
+
+ strnput(".text", 8); /* text segment */
+ bput(INITTEXT); /* address */
+ bput(INITTEXT);
+ bput(textsize);
+ bput(HEADR);
+ bput(0L);
+ bput(HEADR+textsize+datsize+symsize);
+ bput(lcsize); /* line number size */
+ bput(0x20L); /* flags */
+
+ strnput(".data", 8); /* data segment */
+ bput(INITDAT); /* address */
+ bput(INITDAT);
+ bput(datsize);
+ bput(rnd(HEADR+textsize, 4096)); /* sizes */
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(0x40L); /* flags */
+
+ strnput(".bss", 8); /* bss segment */
+ bput(INITDAT+datsize); /* address */
+ bput(INITDAT+datsize);
+ bput(bsssize);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(0L);
+ bput(0x80L); /* flags */
+ break;
+ case 5:
+ strnput("\177ELF", 4); /* e_ident */
+ cput(1); /* class = 32 bit */
+ cput(2); /* data = MSB */
+ cput(1); /* version = CURRENT */
+ strnput("", 9);
+ bput((2L<<16)|8L); /* type = EXEC; machine = MIPS */
+ bput(1L); /* version = CURRENT */
+ bput(entryvalue()); /* entry vaddr */
+ bput(52L); /* offset to first phdr */
+ bput(0L); /* offset to first shdr */
+ bput(0L); /* flags = MIPS */
+ bput((52L<<16)|32L); /* Ehdr & Phdr sizes*/
+ bput((3L<<16)|0L); /* # Phdrs & Shdr size */
+ bput((0L<<16)|0L); /* # Shdrs & shdr string size */
+
+ bput(1L); /* text - type = PT_LOAD */
+ bput(0L); /* file offset */
+ bput(INITTEXT-HEADR); /* vaddr */
+ bput(INITTEXT-HEADR); /* paddr */
+ bput(HEADR+textsize); /* file size */
+ bput(HEADR+textsize); /* memory size */
+ bput(0x05L); /* protections = RX */
+ bput(0x10000L); /* alignment code?? */
+
+ bput(1L); /* data - type = PT_LOAD */
+ bput(HEADR+textsize); /* file offset */
+ bput(INITDAT); /* vaddr */
+ bput(INITDAT); /* paddr */
+ bput(datsize); /* file size */
+ bput(datsize+bsssize); /* memory size */
+ bput(0x06L); /* protections = RW */
+ bput(0x10000L); /* alignment code?? */
+
+ bput(0L); /* data - type = PT_NULL */
+ bput(HEADR+textsize+datsize); /* file offset */
+ bput(0L);
+ bput(0L);
+ bput(symsize); /* symbol table size */
+ bput(lcsize); /* line number size */
+ bput(0x04L); /* protections = R */
+ bput(0x04L); /* alignment code?? */
+ break;
+ case 6:
+ t = 22;
+ bput(((((4*t)+0)*t)+7)); /* magic */
+ bput(textsize); /* sizes */
+ bput(datsize);
+ bput(bsssize);
+ bput(symsize); /* nsyms */
+ bput(entryvalue()); /* va of entry */
+ bput(0L);
+ bput(lcsize);
+ break;
+ }
+ cflush();
+}
+
+void
+strnput(char *s, int n)
+{
+ for(; *s; s++){
+ cput(*s);
+ n--;
+ }
+ for(; n > 0; n--)
+ cput(0);
+}
+
+void
+cflush(void)
+{
+ int n;
+
+ n = sizeof(buf.cbuf) - cbc;
+ if(n)
+ write(cout, buf.cbuf, n);
+ cbp = (uchar*)buf.cbuf;
+ cbc = sizeof(buf.cbuf);
+}
+
+void
+nopstat(char *f, Count *c)
+{
+ if(c->outof)
+ Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f,
+ c->outof - c->count, c->outof,
+ (double)(c->outof - c->count)/c->outof);
+}
+
+void
+asmsym(void)
+{
+ Prog *p;
+ Auto *a;
+ Sym *s;
+ int h;
+
+ s = lookup("etext", 0);
+ if(s->type == STEXT)
+ putsymb(s->name, 'T', s->value, s->version);
+
+ for(h=0; h<NHASH; h++)
+ for(s=hash[h]; s!=S; s=s->link)
+ switch(s->type) {
+ case SCONST:
+ putsymb(s->name, 'D', s->value, s->version);
+ continue;
+
+ case SDATA:
+ putsymb(s->name, 'D', s->value+INITDAT, s->version);
+ continue;
+
+ case SBSS:
+ putsymb(s->name, 'B', s->value+INITDAT, s->version);
+ continue;
+
+ case SFILE:
+ putsymb(s->name, 'f', s->value, s->version);
+ continue;
+ }
+
+ for(p=textp; p!=P; p=p->cond) {
+ s = p->from.sym;
+ if(s->type != STEXT && s->type != SLEAF)
+ continue;
+
+ /* filenames first */
+ for(a=p->to.autom; a; a=a->link)
+ if(a->type == D_FILE)
+ putsymb(a->asym->name, 'z', a->aoffset, 0);
+ else
+ if(a->type == D_FILE1)
+ putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+ if(s->type == STEXT)
+ putsymb(s->name, 'T', s->value, s->version);
+ else
+ putsymb(s->name, 'L', s->value, s->version);
+
+ /* frame, auto and param after */
+ putsymb(".frame", 'm', p->to.offset+8, 0);
+ for(a=p->to.autom; a; a=a->link)
+ if(a->type == D_AUTO)
+ putsymb(a->asym->name, 'a', -a->aoffset, 0);
+ else
+ if(a->type == D_PARAM)
+ putsymb(a->asym->name, 'p', a->aoffset, 0);
+ }
+ if(debug['v'] || debug['n'])
+ Bprint(&bso, "symsize = %lud\n", symsize);
+ Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+ int i, f;
+
+ if(t == 'f')
+ s++;
+ bput(v);
+ if(ver)
+ t += 'a' - 'A';
+ cput(t+0x80); /* 0x80 is variable length */
+
+ if(t == 'Z' || t == 'z') {
+ cput(s[0]);
+ for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+ cput(s[i]);
+ cput(s[i+1]);
+ }
+ cput(0);
+ cput(0);
+ i++;
+ }
+ else {
+ for(i=0; s[i]; i++)
+ cput(s[i]);
+ cput(0);
+ }
+ symsize += 4 + 1 + i + 1;
+
+ if(debug['n']) {
+ if(t == 'z' || t == 'Z') {
+ Bprint(&bso, "%c %.8lux ", t, v);
+ for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+ f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+ Bprint(&bso, "/%x", f);
+ }
+ Bprint(&bso, "\n");
+ return;
+ }
+ if(ver)
+ Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+ else
+ Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+ }
+}
+
+#define MINLC 4
+void
+asmlc(void)
+{
+ long oldpc, oldlc;
+ Prog *p;
+ long v, s;
+
+ oldpc = INITTEXT;
+ oldlc = 0;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+ if(p->as == ATEXT)
+ curtext = p;
+ if(debug['L'])
+ Bprint(&bso, "%6lux %P\n",
+ p->pc, p);
+ continue;
+ }
+ if(debug['L'])
+ Bprint(&bso, "\t\t%6ld", lcsize);
+ v = (p->pc - oldpc) / MINLC;
+ while(v) {
+ s = 127;
+ if(v < 127)
+ s = v;
+ cput(s+128); /* 129-255 +pc */
+ if(debug['L'])
+ Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+ v -= s;
+ lcsize++;
+ }
+ s = p->line - oldlc;
+ oldlc = p->line;
+ oldpc = p->pc + MINLC;
+ if(s > 64 || s < -64) {
+ cput(0); /* 0 vv +lc */
+ cput(s>>24);
+ cput(s>>16);
+ cput(s>>8);
+ cput(s);
+ if(debug['L']) {
+ if(s > 0)
+ Bprint(&bso, " lc+%ld(%d,%ld)\n",
+ s, 0, s);
+ else
+ Bprint(&bso, " lc%ld(%d,%ld)\n",
+ s, 0, s);
+ Bprint(&bso, "%6lux %P\n",
+ p->pc, p);
+ }
+ lcsize += 5;
+ continue;
+ }
+ if(s > 0) {
+ cput(0+s); /* 1-64 +lc */
+ if(debug['L']) {
+ Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+ Bprint(&bso, "%6lux %P\n",
+ p->pc, p);
+ }
+ } else {
+ cput(64-s); /* 65-128 -lc */
+ if(debug['L']) {
+ Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+ Bprint(&bso, "%6lux %P\n",
+ p->pc, p);
+ }
+ }
+ lcsize++;
+ }
+ while(lcsize & 1) {
+ s = 129;
+ cput(s);
+ lcsize++;
+ }
+ if(debug['v'] || debug['L'])
+ Bprint(&bso, "lcsize = %ld\n", lcsize);
+ Bflush(&bso);
+}
+
+void
+datblk(long s, long n)
+{
+ Prog *p;
+ char *cast;
+ long l, fl, j, d;
+ int i, c;
+
+ memset(buf.dbuf, 0, n+100);
+ for(p = datap; p != P; p = p->link) {
+ curp = p;
+ l = p->from.sym->value + p->from.offset - s;
+ c = p->reg;
+ i = 0;
+ if(l < 0) {
+ if(l+c <= 0)
+ continue;
+ while(l < 0) {
+ l++;
+ i++;
+ }
+ }
+ if(l >= n)
+ continue;
+ if(p->as != AINIT && p->as != ADYNT) {
+ for(j=l+(c-i)-1; j>=l; j--)
+ if(buf.dbuf[j]) {
+ print("%P\n", p);
+ diag("multiple initialization\n");
+ break;
+ }
+ }
+ switch(p->to.type) {
+ default:
+ diag("unknown mode in initialization\n%P\n", p);
+ break;
+
+ case D_VCONST:
+ cast = (char*)p->to.ieee;
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[fnuxi8[i]];
+ l++;
+ }
+ break;
+
+ case D_FCONST:
+ switch(c) {
+ default:
+ case 4:
+ fl = ieeedtof(p->to.ieee);
+ cast = (char*)&fl;
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[fnuxi4[i]];
+ l++;
+ }
+ break;
+ case 8:
+ cast = (char*)p->to.ieee;
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[fnuxi8[i]];
+ l++;
+ }
+ break;
+ }
+ break;
+
+ case D_SCONST:
+ for(; i<c; i++) {
+ buf.dbuf[l] = p->to.sval[i];
+ l++;
+ }
+ break;
+
+ case D_CONST:
+ d = p->to.offset;
+ if(p->to.sym) {
+ if(p->to.sym->type == STEXT ||
+ p->to.sym->type == SLEAF)
+ d += p->to.sym->value;
+ if(p->to.sym->type == SDATA)
+ d += p->to.sym->value + INITDAT;
+ if(p->to.sym->type == SBSS)
+ d += p->to.sym->value + INITDAT;
+ }
+ cast = (char*)&d;
+ switch(c) {
+ default:
+ diag("bad nuxi %d %d\n%P\n", c, i, curp);
+ break;
+ case 1:
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[inuxi1[i]];
+ l++;
+ }
+ break;
+ case 2:
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[inuxi2[i]];
+ l++;
+ }
+ break;
+ case 4:
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[inuxi4[i]];
+ l++;
+ }
+ break;
+ }
+ break;
+ }
+ }
+ write(cout, buf.dbuf, n);
+}
+
+#define OP_RRR(op,r1,r2,r3)\
+ (op|(((r1)&31L)<<16)|(((r2)&31L)<<21)|(((r3)&31L)<<11))
+#define OP_IRR(op,i,r2,r3)\
+ (op|((i)&0xffffL)|(((r2)&31L)<<21)|(((r3)&31L)<<16))
+#define OP_SRR(op,s,r2,r3)\
+ (op|(((s)&31L)<<6)|(((r2)&31L)<<16)|(((r3)&31L)<<11))
+#define OP_FRRR(op,r1,r2,r3)\
+ (op|(((r1)&31L)<<16)|(((r2)&31L)<<11)|(((r3)&31L)<<6))
+#define OP_JMP(op,i)\
+ ((op)|((i)&0x3ffffffL))
+
+#define OP(x,y)\
+ (((x)<<3)|((y)<<0))
+#define SP(x,y)\
+ (((x)<<29)|((y)<<26))
+#define BCOND(x,y)\
+ (((x)<<19)|((y)<<16))
+#define MMU(x,y)\
+ (SP(2,0)|(16<<21)|((x)<<3)|((y)<<0))
+#define FPF(x,y)\
+ (SP(2,1)|(16<<21)|((x)<<3)|((y)<<0))
+#define FPD(x,y)\
+ (SP(2,1)|(17<<21)|((x)<<3)|((y)<<0))
+#define FPW(x,y)\
+ (SP(2,1)|(20<<21)|((x)<<3)|((y)<<0))
+#define FPV(x,y)\
+ (SP(2,1)|(21<<21)|((x)<<3)|((y)<<0))
+
+int
+asmout(Prog *p, Optab *o, int aflag)
+{
+ long o1, o2, o3, o4, o5, o6, o7, v;
+ Prog *ct;
+ int r, a;
+
+ o1 = 0;
+ o2 = 0;
+ o3 = 0;
+ o4 = 0;
+ o5 = 0;
+ o6 = 0;
+ o7 = 0;
+ switch(o->type) {
+ default:
+ diag("unknown type %d\n", o->type);
+ if(!debug['a'])
+ prasm(p);
+ break;
+
+ case 0: /* pseudo ops */
+ if(aflag) {
+ if(p->link) {
+ if(p->as == ATEXT) {
+ ct = curtext;
+ o2 = autosize;
+ curtext = p;
+ autosize = p->to.offset + 8;
+ o1 = asmout(p->link, oplook(p->link), aflag);
+ curtext = ct;
+ autosize = o2;
+ } else
+ o1 = asmout(p->link, oplook(p->link), aflag);
+ }
+ return o1;
+ }
+ break;
+
+ case 1: /* mov[v] r1,r2 ==> OR r1,r0,r2 */
+ o1 = OP_RRR(oprrr(AOR), p->from.reg, REGZERO, p->to.reg);
+ break;
+
+ case 2: /* add/sub r1,[r2],r3 */
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ o1 = OP_RRR(oprrr(p->as), p->from.reg, r, p->to.reg);
+ break;
+
+ case 3: /* mov $soreg, r ==> or/add $i,o,r */
+ v = regoff(&p->from);
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ a = AADDU;
+ if(o->a1 == C_ANDCON)
+ a = AOR;
+ o1 = OP_IRR(opirr(a), v, r, p->to.reg);
+ break;
+
+ case 4: /* add $scon,[r1],r2 */
+ v = regoff(&p->from);
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ o1 = OP_IRR(opirr(p->as), v, r, p->to.reg);
+ break;
+
+ case 5: /* syscall */
+ if(aflag)
+ return 0;
+ o1 = oprrr(p->as);
+ break;
+
+ case 6: /* beq r1,[r2],sbra */
+ if(aflag)
+ return 0;
+ if(!debug['Y'] && p->link && p->cond && isnop(p->link)) {
+ nop.branch.count--;
+ nop.branch.outof--;
+ nop.jump.outof++;
+ o2 = asmout(p->cond, oplook(p->cond), 1);
+ if(o2) {
+ if(p->cond == P)
+ v = -4 >> 2;
+ else
+ v = (p->cond->pc+4 - pc-4) >> 2;
+ if(((v << 16) >> 16) != v)
+ diag("short branch too far: %d\n%P\n", v, p);
+ o1 = OP_IRR(opirr(p->as+ALAST), v, p->from.reg, p->reg);
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n",
+ p->pc, o1, o2, p);
+ lput(o1);
+ lput(o2);
+ return 1;
+ }
+ }
+ if(p->cond == P)
+ v = -4 >> 2;
+ else
+ v = (p->cond->pc - pc-4) >> 2;
+ if(((v << 16) >> 16) != v)
+ diag("short branch too far: %d\n%P\n", v, p);
+ o1 = OP_IRR(opirr(p->as), v, p->from.reg, p->reg);
+ break;
+
+ case 7: /* mov r, soreg ==> sw o(r) */
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ v = regoff(&p->to);
+ o1 = OP_IRR(opirr(p->as), v, r, p->from.reg);
+ break;
+
+ case 8: /* mov soreg, r ==> lw o(r) */
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ v = regoff(&p->from);
+ o1 = OP_IRR(opirr(p->as+ALAST), v, r, p->to.reg);
+ break;
+
+ case 9: /* asl r1,[r2],r3 */
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ o1 = OP_RRR(oprrr(p->as), r, p->from.reg, p->to.reg);
+ break;
+
+ case 10: /* add $con,[r1],r2 ==> mov $con,t; add t,[r1],r2 */
+ v = regoff(&p->from);
+ r = AOR;
+ if(v < 0)
+ r = AADDU;
+ o1 = OP_IRR(opirr(r), v, 0, REGTMP);
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+ break;
+
+ case 11: /* jmp lbra */
+ if(aflag)
+ return 0;
+ if(p->cond == P)
+ v = p->pc >> 2;
+ else
+ v = p->cond->pc >> 2;
+ o1 = OP_JMP(opirr(p->as), v);
+ if(!debug['Y'] && p->link && p->cond && isnop(p->link)) {
+ nop.branch.count--;
+ nop.branch.outof--;
+ nop.jump.outof++;
+ o2 = asmout(p->cond, oplook(p->cond), 1);
+ if(o2) {
+ o1 += 1;
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n",
+ p->pc, o1, o2, p);
+ lput(o1);
+ lput(o2);
+ return 1;
+ }
+ }
+ break;
+
+ case 12: /* movbs r,r */
+ v = 16;
+ if(p->as == AMOVB)
+ v = 24;
+ o1 = OP_SRR(opirr(ASLL), v, p->from.reg, p->to.reg);
+ o2 = OP_SRR(opirr(ASRA), v, p->to.reg, p->to.reg);
+ break;
+
+ case 13: /* movbu r,r */
+ if(p->as == AMOVBU)
+ o1 = OP_IRR(opirr(AAND), 0xffL, p->from.reg, p->to.reg);
+ else
+ o1 = OP_IRR(opirr(AAND), 0xffffL, p->from.reg, p->to.reg);
+ break;
+
+ case 14: /* movwu r,r */
+ v = 32-32;
+ o1 = OP_SRR(opirr(ASLLV+ALAST), v, p->from.reg, p->to.reg);
+ o2 = OP_SRR(opirr(ASRLV+ALAST), v, p->to.reg, p->to.reg);
+ break;
+
+ case 16: /* sll $c,[r1],r2 */
+ v = regoff(&p->from);
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ if(v >= 32)
+ o1 = OP_SRR(opirr(p->as+ALAST), v-32, r, p->to.reg);
+ else
+ o1 = OP_SRR(opirr(p->as), v, r, p->to.reg);
+ break;
+
+ case 18: /* jmp [r1],0(r2) */
+ if(aflag)
+ return 0;
+ r = p->reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = OP_RRR(oprrr(p->as), 0, p->to.reg, r);
+ break;
+
+ case 19: /* mov $lcon,r ==> lu+or */
+ v = regoff(&p->from);
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, p->to.reg);
+ o2 = OP_IRR(opirr(AOR), v, p->to.reg, p->to.reg);
+ break;
+
+ case 20: /* mov lohi,r */
+ r = OP(2,0); /* mfhi */
+ if(p->from.type == D_LO)
+ r = OP(2,2); /* mflo */
+ o1 = OP_RRR(r, REGZERO, REGZERO, p->to.reg);
+ break;
+
+ case 21: /* mov r,lohi */
+ r = OP(2,1); /* mthi */
+ if(p->to.type == D_LO)
+ r = OP(2,3); /* mtlo */
+ o1 = OP_RRR(r, REGZERO, p->from.reg, REGZERO);
+ break;
+
+ case 22: /* mul r1,r2 */
+ o1 = OP_RRR(oprrr(p->as), p->from.reg, p->reg, REGZERO);
+ break;
+
+ case 23: /* add $lcon,r1,r2 ==> lu+or+add */
+ v = regoff(&p->from);
+ if(p->to.reg == REGTMP || p->reg == REGTMP)
+ diag("cant synthesize large constant\n%P\n", p);
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+ o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ o3 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+ break;
+
+ case 24: /* mov $ucon,,r ==> lu r */
+ v = regoff(&p->from);
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, p->to.reg);
+ break;
+
+ case 25: /* add/and $ucon,[r1],r2 ==> lu $con,t; add t,[r1],r2 */
+ v = regoff(&p->from);
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+ break;
+
+ case 26: /* mov $lsext/auto/oreg,,r2 ==> lu+or+add */
+ v = regoff(&p->from);
+ if(p->to.reg == REGTMP)
+ diag("cant synthesize large constant\n%P\n", p);
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+ o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o3 = OP_RRR(oprrr(AADDU), REGTMP, r, p->to.reg);
+ break;
+
+ case 27: /* mov [sl]ext/auto/oreg,fr ==> lwc1 o(r) */
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ v = regoff(&p->from);
+ if(p->as == AMOVD)
+ o4 = opirr(AMOVD+ALAST);
+ else
+ o4 = opirr(AMOVF+ALAST);
+ switch(o->size) {
+ case 16:
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+ o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+ o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+ o4 = OP_IRR(o4, 0, REGTMP, p->to.reg);
+ break;
+ case 4:
+ o1 = OP_IRR(o4, v, r, p->to.reg);
+ break;
+ }
+ break;
+
+ case 28: /* mov fr,[sl]ext/auto/oreg ==> swc1 o(r) */
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ v = regoff(&p->to);
+ if(p->as == AMOVD)
+ o4 = opirr(AMOVD);
+ else
+ o4 = opirr(AMOVF);
+ switch(o->size) {
+ case 16:
+ if(r == REGTMP)
+ diag("cant synthesize large constant\n%P\n", p);
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+ o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+ o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+ o4 = OP_IRR(o4, 0, REGTMP, p->from.reg);
+ break;
+ case 4:
+ o1 = OP_IRR(o4, v, r, p->from.reg);
+ break;
+ }
+ break;
+
+ case 30: /* movw r,fr */
+ r = SP(2,1)|(4<<21); /* mtc1 */
+ o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+ break;
+
+ case 31: /* movw fr,r */
+ r = SP(2,1)|(0<<21); /* mfc1 */
+ o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+ break;
+
+ case 32: /* fadd fr1,[fr2],fr3 */
+ r = p->reg;
+ if(r == NREG)
+ o1 = OP_FRRR(oprrr(p->as), p->from.reg, p->to.reg, p->to.reg);
+ else
+ o1 = OP_FRRR(oprrr(p->as), p->from.reg, r, p->to.reg);
+ break;
+
+ case 33: /* fabs fr1,fr3 */
+ o1 = OP_FRRR(oprrr(p->as), 0, p->from.reg, p->to.reg);
+ break;
+
+ case 34: /* mov $con,fr ==> or/add $i,r,r2 */
+ v = regoff(&p->from);
+ r = AADDU;
+ if(o->a1 == C_ANDCON)
+ r = AOR;
+ o1 = OP_IRR(opirr(r), v, 0, REGTMP);
+ o2 = OP_RRR(SP(2,1)|(4<<21), REGTMP, 0, p->to.reg); /* mtc1 */
+ break;
+
+ case 35: /* mov r,lext/luto/oreg ==> sw o(r) */
+ /*
+ * the lowbits of the constant cannot
+ * be moved into the offset of the load
+ * because the mips 4000 in 64-bit mode
+ * does a 64-bit add and it will screw up.
+ */
+ v = regoff(&p->to);
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ if(r == REGTMP)
+ diag("cant synthesize large constant\n%P\n", p);
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+ o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+ o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+ o4 = OP_IRR(opirr(p->as), 0, REGTMP, p->from.reg);
+ break;
+
+ case 36: /* mov lext/lauto/lreg,r ==> lw o(r30) */
+ v = regoff(&p->from);
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ if(r == REGTMP)
+ diag("cant synthesize large constant\n%P\n", p);
+ o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+ o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+ o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+ o4 = OP_IRR(opirr(p->as+ALAST), 0, REGTMP, p->to.reg);
+ break;
+
+ case 37: /* movw r,mr */
+ r = SP(2,0)|(4<<21); /* mtc0 */
+ if(p->as == AMOVV)
+ r = SP(2,0)|(5<<21); /* dmtc0 */
+ o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+ break;
+
+ case 38: /* movw mr,r */
+ r = SP(2,0)|(0<<21); /* mfc0 */
+ if(p->as == AMOVV)
+ r = SP(2,0)|(1<<21); /* dmfc0 */
+ o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+ break;
+
+ case 39: /* rfe ==> jmp+rfe */
+ if(aflag)
+ return 0;
+ o1 = OP_RRR(oprrr(AJMP), 0, p->to.reg, REGZERO);
+ o2 = oprrr(p->as);
+ break;
+
+ case 40: /* word */
+ if(aflag)
+ return 0;
+ o1 = regoff(&p->to);
+ break;
+
+ case 41: /* movw r,fcr */
+ o1 = OP_RRR(SP(2,1)|(2<<21), REGZERO, 0, p->to.reg); /* mfcc1 */
+ o2 = OP_RRR(SP(2,1)|(6<<21), p->from.reg, 0, p->to.reg);/* mtcc1 */
+ break;
+
+ case 42: /* movw fcr,r */
+ o1 = OP_RRR(SP(2,1)|(2<<21), p->to.reg, 0, p->from.reg);/* mfcc1 */
+ break;
+
+ case 47: /* movv r,fr */
+ r = SP(2,1)|(5<<21); /* dmtc1 */
+ o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+ break;
+
+ case 48: /* movv fr,r */
+ r = SP(2,1)|(1<<21); /* dmfc1 */
+ o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+ break;
+ }
+ if(aflag)
+ return o1;
+ v = p->pc;
+ switch(o->size) {
+ default:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+ break;
+ case 4:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+ lput(o1);
+ break;
+ case 8:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
+ lput(o1);
+ lput(o2);
+ break;
+ case 12:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
+ lput(o1);
+ lput(o2);
+ lput(o3);
+ break;
+ case 16:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
+ v, o1, o2, o3, o4, p);
+ lput(o1);
+ lput(o2);
+ lput(o3);
+ lput(o4);
+ break;
+ case 20:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+ v, o1, o2, o3, o4, o5, p);
+ lput(o1);
+ lput(o2);
+ lput(o3);
+ lput(o4);
+ lput(o5);
+ break;
+
+ case 28:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+ v, o1, o2, o3, o4, o5, o6, o7, p);
+ lput(o1);
+ lput(o2);
+ lput(o3);
+ lput(o4);
+ lput(o5);
+ lput(o6);
+ lput(o7);
+ break;
+ }
+ return 0;
+}
+
+int
+isnop(Prog *p)
+{
+ if(p->as != ANOR)
+ return 0;
+ if(p->reg != REGZERO && p->reg != NREG)
+ return 0;
+ if(p->from.type != D_REG || p->from.reg != REGZERO)
+ return 0;
+ if(p->to.type != D_REG || p->to.reg != REGZERO)
+ return 0;
+ return 1;
+}
+
+long
+oprrr(int a)
+{
+ switch(a) {
+ case AADD: return OP(4,0);
+ case AADDU: return OP(4,1);
+ case ASGT: return OP(5,2);
+ case ASGTU: return OP(5,3);
+ case AAND: return OP(4,4);
+ case AOR: return OP(4,5);
+ case AXOR: return OP(4,6);
+ case ASUB: return OP(4,2);
+ case ASUBU: return OP(4,3);
+ case ANOR: return OP(4,7);
+ case ASLL: return OP(0,4);
+ case ASRL: return OP(0,6);
+ case ASRA: return OP(0,7);
+
+ case ASLLV: return OP(2,4);
+ case ASRLV: return OP(2,6);
+ case ASRAV: return OP(2,7);
+
+ case AADDV: return OP(5,4);
+ case AADDVU: return OP(5,5);
+ case ASUBV: return OP(5,6);
+ case ASUBVU: return OP(5,7);
+ case AREM:
+ case ADIV: return OP(3,2);
+ case AREMU:
+ case ADIVU: return OP(3,3);
+ case AMUL: return OP(3,0);
+ case AMULU: return OP(3,1);
+
+ case AREMV:
+ case ADIVV: return OP(3,6);
+ case AREMVU:
+ case ADIVVU: return OP(3,7);
+ case AMULV: return OP(3,4);
+ case AMULVU: return OP(3,5);
+
+ case AJMP: return OP(1,0);
+ case AJAL: return OP(1,1);
+
+ case ABREAK: return OP(1,5);
+ case ASYSCALL: return OP(1,4);
+ case ATLBP: return MMU(1,0);
+ case ATLBR: return MMU(0,1);
+ case ATLBWI: return MMU(0,2);
+ case ATLBWR: return MMU(0,6);
+ case ARFE: return MMU(2,0);
+
+ case ADIVF: return FPF(0,3);
+ case ADIVD: return FPD(0,3);
+ case AMULF: return FPF(0,2);
+ case AMULD: return FPD(0,2);
+ case ASUBF: return FPF(0,1);
+ case ASUBD: return FPD(0,1);
+ case AADDF: return FPF(0,0);
+ case AADDD: return FPD(0,0);
+
+ case ATRUNCFV: return FPF(1,1);
+ case ATRUNCDV: return FPD(1,1);
+ case ATRUNCFW: return FPF(1,5);
+ case ATRUNCDW: return FPD(1,5);
+ case AMOVFV: return FPF(4,5);
+ case AMOVDV: return FPD(4,5);
+ case AMOVVF: return FPV(4,0);
+ case AMOVVD: return FPV(4,1);
+
+ case AMOVFW: return FPF(4,4);
+ case AMOVDW: return FPD(4,4);
+ case AMOVWF: return FPW(4,0);
+ case AMOVDF: return FPD(4,0);
+ case AMOVWD: return FPW(4,1);
+ case AMOVFD: return FPF(4,1);
+ case AABSF: return FPF(0,5);
+ case AABSD: return FPD(0,5);
+ case AMOVF: return FPF(0,6);
+ case AMOVD: return FPD(0,6);
+ case ANEGF: return FPF(0,7);
+ case ANEGD: return FPD(0,7);
+
+ case ACMPEQF: return FPF(6,2);
+ case ACMPEQD: return FPD(6,2);
+ case ACMPGTF: return FPF(7,4);
+ case ACMPGTD: return FPD(7,4);
+ case ACMPGEF: return FPF(7,6);
+ case ACMPGED: return FPD(7,6);
+ }
+ if(a >= ALAST)
+ diag("bad rrr %A+ALAST", a-ALAST);
+ else
+ diag("bad rrr %A", a);
+ return 0;
+}
+
+long
+opirr(int a)
+{
+ switch(a) {
+ case AADD: return SP(1,0);
+ case AADDU: return SP(1,1);
+ case ASGT: return SP(1,2);
+ case ASGTU: return SP(1,3);
+ case AAND: return SP(1,4);
+ case AOR: return SP(1,5);
+ case AXOR: return SP(1,6);
+ case ALAST: return SP(1,7); /* lui */
+ case ASLL: return OP(0,0);
+ case ASRL: return OP(0,2);
+ case ASRA: return OP(0,3);
+
+ case AADDV: return SP(3,0);
+ case AADDVU: return SP(3,1);
+
+ case AJMP: return SP(0,2);
+ case AJAL: return SP(0,3);
+ case ABEQ: return SP(0,4);
+ case ABEQ+ALAST: return SP(2,4); /* likely */
+ case ABNE: return SP(0,5);
+ case ABNE+ALAST: return SP(2,5); /* likely */
+
+ case ABGEZ: return SP(0,1)|BCOND(0,1);
+ case ABGEZ+ALAST: return SP(0,1)|BCOND(0,3); /* likely */
+ case ABGEZAL: return SP(0,1)|BCOND(2,1);
+ case ABGEZAL+ALAST: return SP(0,1)|BCOND(2,3); /* likely */
+ case ABGTZ: return SP(0,7);
+ case ABGTZ+ALAST: return SP(2,7); /* likely */
+ case ABLEZ: return SP(0,6);
+ case ABLEZ+ALAST: return SP(2,6); /* likely */
+ case ABLTZ: return SP(0,1)|BCOND(0,0);
+ case ABLTZ+ALAST: return SP(0,1)|BCOND(0,2); /* likely */
+ case ABLTZAL: return SP(0,1)|BCOND(2,0);
+ case ABLTZAL+ALAST: return SP(0,1)|BCOND(2,2); /* likely */
+
+ case ABFPT: return SP(2,1)|(257<<16);
+ case ABFPT+ALAST: return SP(2,1)|(259<<16); /* likely */
+ case ABFPF: return SP(2,1)|(256<<16);
+ case ABFPF+ALAST: return SP(2,1)|(258<<16); /* likely */
+
+ case AMOVB:
+ case AMOVBU: return SP(5,0);
+ case AMOVH:
+ case AMOVHU: return SP(5,1);
+ case AMOVW: return SP(5,3);
+ case AMOVV: return SP(7,7);
+ case AMOVF: return SP(7,1);
+ case AMOVD: return SP(7,5);
+ case AMOVWL: return SP(5,2);
+ case AMOVWR: return SP(5,6);
+ case AMOVVL: return SP(5,4);
+ case AMOVVR: return SP(5,5);
+
+ case ABREAK: return SP(5,7);
+
+ case AMOVWL+ALAST: return SP(4,2);
+ case AMOVWR+ALAST: return SP(4,6);
+ case AMOVVL+ALAST: return SP(3,2);
+ case AMOVVR+ALAST: return SP(3,3);
+ case AMOVB+ALAST: return SP(4,0);
+ case AMOVBU+ALAST: return SP(4,4);
+ case AMOVH+ALAST: return SP(4,1);
+ case AMOVHU+ALAST: return SP(4,5);
+ case AMOVW+ALAST: return SP(4,3);
+ case AMOVV+ALAST: return SP(6,7);
+ case AMOVF+ALAST: return SP(6,1);
+ case AMOVD+ALAST: return SP(6,5);
+
+ case ASLLV: return OP(7,0);
+ case ASRLV: return OP(7,2);
+ case ASRAV: return OP(7,3);
+ case ASLLV+ALAST: return OP(7,4);
+ case ASRLV+ALAST: return OP(7,6);
+ case ASRAV+ALAST: return OP(7,7);
+ }
+ if(a >= ALAST)
+ diag("bad irr %A+ALAST", a-ALAST);
+ else
+ diag("bad irr %A", a);
+ return 0;
+}
--- /dev/null
+++ b/utils/0l/enam.c
@@ -1,0 +1,122 @@
+char* anames[] =
+{
+ "XXX",
+ "ABSD",
+ "ABSF",
+ "ABSW",
+ "ADD",
+ "ADDD",
+ "ADDF",
+ "ADDU",
+ "ADDW",
+ "AND",
+ "BEQ",
+ "BFPF",
+ "BFPT",
+ "BGEZ",
+ "BGEZAL",
+ "BGTZ",
+ "BLEZ",
+ "BLTZ",
+ "BLTZAL",
+ "BNE",
+ "BREAK",
+ "CMPEQD",
+ "CMPEQF",
+ "CMPGED",
+ "CMPGEF",
+ "CMPGTD",
+ "CMPGTF",
+ "DATA",
+ "DIV",
+ "DIVD",
+ "DIVF",
+ "DIVU",
+ "DIVW",
+ "GLOBL",
+ "GOK",
+ "HISTORY",
+ "JAL",
+ "JMP",
+ "MOVB",
+ "MOVBU",
+ "MOVD",
+ "MOVDF",
+ "MOVDW",
+ "MOVF",
+ "MOVFD",
+ "MOVFW",
+ "MOVH",
+ "MOVHU",
+ "MOVW",
+ "MOVWD",
+ "MOVWF",
+ "MOVWL",
+ "MOVWR",
+ "MUL",
+ "MULD",
+ "MULF",
+ "MULU",
+ "MULW",
+ "NAME",
+ "NEGD",
+ "NEGF",
+ "NEGW",
+ "NOP",
+ "NOR",
+ "OR",
+ "REM",
+ "REMU",
+ "RET",
+ "RFE",
+ "SGT",
+ "SGTU",
+ "SLL",
+ "SRA",
+ "SRL",
+ "SUB",
+ "SUBD",
+ "SUBF",
+ "SUBU",
+ "SUBW",
+ "SYSCALL",
+ "TEXT",
+ "TLBP",
+ "TLBR",
+ "TLBWI",
+ "TLBWR",
+ "WORD",
+ "XOR",
+ "END",
+ "MOVV",
+ "MOVVL",
+ "MOVVR",
+ "SLLV",
+ "SRAV",
+ "SRLV",
+ "DIVV",
+ "DIVVU",
+ "REMV",
+ "REMVU",
+ "MULV",
+ "MULVU",
+ "ADDV",
+ "ADDVU",
+ "SUBV",
+ "SUBVU",
+ "DYNT",
+ "INIT",
+ "BCASE",
+ "CASE",
+ "TRUNCFV",
+ "TRUNCDV",
+ "TRUNCFW",
+ "TRUNCDW",
+ "MOVWU",
+ "MOVFV",
+ "MOVDV",
+ "MOVVF",
+ "MOVVD",
+ "SIGNAME",
+ "LAST",
+};
--- /dev/null
+++ b/utils/0l/l.h
@@ -1,0 +1,329 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../vc/v.out.h"
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+typedef struct Adr Adr;
+typedef struct Sym Sym;
+typedef struct Autom Auto;
+typedef struct Prog Prog;
+typedef struct Optab Optab;
+typedef struct Oprang Oprang;
+typedef uchar Opcross[32][2][32];
+typedef struct Count Count;
+
+#define P ((Prog*)0)
+#define S ((Sym*)0)
+#define TNAME (curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct Adr
+{
+ union
+ {
+ long u0offset;
+ char* u0sval;
+ Ieee* u0ieee;
+ } u0;
+ union
+ {
+ Auto* u1autom;
+ Sym* u1sym;
+ } u1;
+ char type;
+ char reg;
+ char name;
+ char class;
+};
+
+#define offset u0.u0offset
+#define sval u0.u0sval
+#define ieee u0.u0ieee
+
+#define autom u1.u1autom
+#define sym u1.u1sym
+
+struct Prog
+{
+ Adr from;
+ Adr to;
+ union
+ {
+ long u0regused;
+ Prog* u0forwd;
+ } u0;
+ Prog* cond;
+ Prog* link;
+ long pc;
+ long line;
+ uchar mark;
+ uchar optab;
+ char as;
+ char reg;
+};
+
+#define regused u0.u0regused
+#define forwd u0.u0forwd
+
+struct Sym
+{
+ char *name;
+ short type;
+ short version;
+ short become;
+ short frame;
+ long value;
+ Sym* link;
+};
+struct Autom
+{
+ Sym* asym;
+ Auto* link;
+ long aoffset;
+ short type;
+};
+struct Optab
+{
+ char as;
+ char a1;
+ char a2;
+ char a3;
+ char type;
+ char size;
+ char param;
+};
+struct Oprang
+{
+ Optab* start;
+ Optab* stop;
+};
+struct Count
+{
+ long count;
+ long outof;
+};
+
+enum
+{
+ STEXT = 1,
+ SDATA,
+ SBSS,
+ SDATA1,
+ SXREF,
+ SLEAF,
+ SFILE,
+ SCONST,
+
+ C_NONE = 0,
+ C_REG,
+ C_FREG,
+ C_FCREG,
+ C_MREG,
+ C_HI,
+ C_LO,
+ C_ZCON,
+ C_SCON,
+ C_ADD0CON,
+ C_AND0CON,
+ C_ADDCON,
+ C_ANDCON,
+ C_UCON,
+ C_LCON,
+ C_SACON,
+ C_SECON,
+ C_LACON,
+ C_LECON,
+ C_SBRA,
+ C_LBRA,
+ C_SAUTO,
+ C_SEXT,
+ C_LAUTO,
+ C_LEXT,
+ C_ZOREG,
+ C_SOREG,
+ C_LOREG,
+ C_GOK,
+
+ NSCHED = 20,
+
+/* mark flags */
+ FOLL = 1<<0,
+ LABEL = 1<<1,
+ LEAF = 1<<2,
+ SYNC = 1<<3,
+ BRANCH = 1<<4,
+ LOAD = 1<<5,
+ FCMP = 1<<6,
+ NOSCHED = 1<<7,
+
+ BIG = 32766,
+ STRINGSZ = 200,
+ NHASH = 10007,
+ NHUNK = 100000,
+ MINSIZ = 64,
+ NENT = 100,
+ MAXIO = 8192,
+ MAXHIST = 20, /* limit of path elements for history symbols */
+};
+
+EXTERN union
+{
+ struct
+ {
+ char obuf[MAXIO]; /* output buffer */
+ uchar ibuf[MAXIO]; /* input buffer */
+ } u;
+ char dbuf[1];
+} buf;
+
+#define cbuf u.obuf
+#define xbuf u.ibuf
+
+EXTERN long HEADR; /* length of header */
+EXTERN int HEADTYPE; /* type of header */
+EXTERN long INITDAT; /* data location */
+EXTERN long INITRND; /* data round above text location */
+EXTERN long INITTEXT; /* text location */
+EXTERN char* INITENTRY; /* entry point */
+EXTERN long autosize;
+EXTERN Biobuf bso;
+EXTERN long bsssize;
+EXTERN int cbc;
+EXTERN uchar* cbp;
+EXTERN int cout;
+EXTERN Auto* curauto;
+EXTERN Auto* curhist;
+EXTERN Prog* curp;
+EXTERN Prog* curtext;
+EXTERN Prog* datap;
+EXTERN long datsize;
+EXTERN char debug[128];
+EXTERN Prog* etextp;
+EXTERN Prog* firstp;
+EXTERN char fnuxi4[4];
+EXTERN char fnuxi8[8];
+EXTERN char* noname;
+EXTERN Sym* hash[NHASH];
+EXTERN Sym* histfrog[MAXHIST];
+EXTERN int histfrogp;
+EXTERN int histgen;
+EXTERN char* library[50];
+EXTERN char* libraryobj[50];
+EXTERN int libraryp;
+EXTERN int xrefresolv;
+EXTERN char* hunk;
+EXTERN char inuxi1[1];
+EXTERN char inuxi2[2];
+EXTERN char inuxi4[4];
+EXTERN Prog* lastp;
+EXTERN long lcsize;
+EXTERN char literal[32];
+EXTERN int nerrors;
+EXTERN long nhunk;
+EXTERN long instoffset;
+EXTERN Opcross opcross[10];
+EXTERN Oprang oprange[ALAST];
+EXTERN char* outfile;
+EXTERN long pc;
+EXTERN uchar repop[ALAST];
+EXTERN long symsize;
+EXTERN Prog* textp;
+EXTERN long textsize;
+EXTERN long thunk;
+EXTERN int version;
+EXTERN char xcmp[32][32];
+EXTERN Prog zprg;
+EXTERN int dtype;
+
+EXTERN struct
+{
+ Count branch;
+ Count fcmp;
+ Count load;
+ Count mfrom;
+ Count page;
+ Count jump;
+} nop;
+
+extern char* anames[];
+extern Optab optab[];
+
+#pragma varargck type "A" int
+#pragma varargck type "D" Adr*
+#pragma varargck type "N" Adr*
+#pragma varargck type "P" Prog*
+#pragma varargck type "S" char*
+
+
+int Aconv(Fmt*);
+int Dconv(Fmt*);
+int Nconv(Fmt*);
+int Pconv(Fmt*);
+int Sconv(Fmt*);
+int aclass(Adr*);
+void addhist(long, int);
+void addnop(Prog*);
+void append(Prog*, Prog*);
+void asmb(void);
+void asmlc(void);
+int asmout(Prog*, Optab*, int);
+void asmsym(void);
+long atolwhex(char*);
+Prog* brloop(Prog*);
+void buildop(void);
+void buildrep(int, int);
+void cflush(void);
+int cmp(int, int);
+int compound(Prog*);
+double cputime(void);
+void datblk(long, long);
+void diag(char*, ...);
+void dodata(void);
+void doprof1(void);
+void doprof2(void);
+long entryvalue(void);
+void errorexit(void);
+void exchange(Prog*);
+int find1(long, int);
+void follow(void);
+void gethunk(void);
+void histtoauto(void);
+vlong ieeedtov(Ieee*);
+double ieeedtod(Ieee*);
+long ieeedtof(Ieee*);
+int isnop(Prog*);
+void ldobj(int, long, char*);
+void loadlib(void);
+void listinit(void);
+Sym* lookup(char*, int);
+void lput(long);
+void mkfwd(void);
+void* mysbrk(ulong);
+void names(void);
+void nocache(Prog*);
+void noops(void);
+void nuxiinit(void);
+void objfile(char*);
+int ocmp(void*, void*);
+long opirr(int);
+Optab* oplook(Prog*);
+long oprrr(int);
+void patch(void);
+void prasm(Prog*);
+void prepend(Prog*, Prog*);
+Prog* prg(void);
+int pseudo(Prog*);
+void putsymb(char*, int, long, int);
+long regoff(Adr*);
+int relinv(int);
+long rnd(long, long);
+void sched(Prog*, Prog*);
+void span(void);
+void strnput(char*, int);
+void undef(void);
+void xdefine(char*, int, long);
+void xfol(Prog*);
+void xfol(Prog*);
+void nopstat(char*, Count*);
--- /dev/null
+++ b/utils/0l/list.c
@@ -1,0 +1,277 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+ fmtinstall('A', Aconv);
+ fmtinstall('D', Dconv);
+ fmtinstall('P', Pconv);
+ fmtinstall('S', Sconv);
+ fmtinstall('N', Nconv);
+}
+
+void
+prasm(Prog *p)
+{
+ print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+ char str[STRINGSZ], *s;
+ Prog *p;
+ int a;
+
+ p = va_arg(fp->args, Prog*);
+ curp = p;
+ a = p->as;
+ if(a == ADATA || a == ADYNT || a == AINIT)
+ sprint(str, "(%ld) %A %D/%d,%D",
+ p->line, a, &p->from, p->reg, &p->to);
+ else{
+ s = str;
+ s += sprint(s, "(%ld)", p->line);
+ if(p->mark & NOSCHED)
+ s += sprint(s, "*");
+ if(p->reg == NREG)
+ sprint(s, " %A %D,%D",
+ a, &p->from, &p->to);
+ else
+ if(p->from.type != D_FREG)
+ sprint(s, " %A %D,R%d,%D",
+ a, &p->from, p->reg, &p->to);
+ else
+ sprint(s, " %A %D,F%d,%D",
+ a, &p->from, p->reg, &p->to);
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+ char *s;
+ int a;
+
+ a = va_arg(fp->args, int);
+ s = "?";
+ if(a >= AXXX && a < ALAST)
+ s = anames[a];
+ return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+ long v;
+
+ a = va_arg(fp->args, Adr*);
+ switch(a->type) {
+
+ default:
+ sprint(str, "GOK-type(%d)", a->type);
+ break;
+
+ case D_NONE:
+ str[0] = 0;
+ if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+ sprint(str, "%N(R%d)(NONE)", a, a->reg);
+ break;
+
+ case D_CONST:
+ sprint(str, "$%N", a);
+ if(a->reg != NREG)
+ sprint(str, "%N(R%d)(CONST)", a, a->reg);
+ break;
+
+ case D_OCONST:
+ sprint(str, "$*$%N", a);
+ if(a->reg != NREG)
+ sprint(str, "%N(R%d)(CONST)", a, a->reg);
+ break;
+
+ case D_OREG:
+ if(a->reg != NREG)
+ sprint(str, "%N(R%d)", a, a->reg);
+ else
+ sprint(str, "%N", a);
+ break;
+
+ case D_REG:
+ sprint(str, "R%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_MREG:
+ sprint(str, "M%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_FREG:
+ sprint(str, "F%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_FCREG:
+ sprint(str, "FC%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_LO:
+ sprint(str, "LO");
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(LO)(REG)", a);
+ break;
+
+ case D_HI:
+ sprint(str, "HI");
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(HI)(REG)", a);
+ break;
+
+ case D_BRANCH: /* botch */
+ if(curp->cond != P) {
+ v = curp->cond->pc;
+ if(v >= INITTEXT)
+ v -= INITTEXT-HEADR;
+ if(a->sym != S)
+ sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+ else
+ sprint(str, "%.5lux(BRANCH)", v);
+ } else
+ if(a->sym != S)
+ sprint(str, "%s+%ld(APC)", a->sym->name, a->offset);
+ else
+ sprint(str, "%ld(APC)", a->offset);
+ break;
+
+ case D_FCONST:
+ sprint(str, "$%e", ieeedtod(a->ieee));
+ break;
+
+ case D_SCONST:
+ sprint(str, "$\"%S\"", a->sval);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+ Sym *s;
+
+ a = va_arg(fp->args, Adr*);
+ s = a->sym;
+ switch(a->name) {
+ default:
+ sprint(str, "GOK-name(%d)", a->name);
+ break;
+
+ case D_NONE:
+ sprint(str, "%ld", a->offset);
+ break;
+
+ case D_EXTERN:
+ if(s == S)
+ sprint(str, "%ld(SB)", a->offset);
+ else
+ sprint(str, "%s+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_STATIC:
+ if(s == S)
+ sprint(str, "<>+%ld(SB)", a->offset);
+ else
+ sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_AUTO:
+ if(s == S)
+ sprint(str, "%ld(SP)", a->offset);
+ else
+ sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+ break;
+
+ case D_PARAM:
+ if(s == S)
+ sprint(str, "%ld(FP)", a->offset);
+ else
+ sprint(str, "%s+%ld(FP)", s->name, a->offset);
+ break;
+ }
+
+ return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+ int i, c;
+ char str[STRINGSZ], *p, *a;
+
+ a = va_arg(fp->args, char*);
+ p = str;
+ for(i=0; i<sizeof(long); i++) {
+ c = a[i] & 0xff;
+ if(c >= 'a' && c <= 'z' ||
+ c >= 'A' && c <= 'Z' ||
+ c >= '0' && c <= '9' ||
+ c == ' ' || c == '%') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch(c) {
+ case 0:
+ *p++ = 'z';
+ continue;
+ case '\\':
+ case '"':
+ *p++ = c;
+ continue;
+ case '\n':
+ *p++ = 'n';
+ continue;
+ case '\t':
+ *p++ = 't';
+ continue;
+ }
+ *p++ = (c>>6) + '0';
+ *p++ = ((c>>3) & 7) + '0';
+ *p++ = (c & 7) + '0';
+ }
+ *p = 0;
+ return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+ char buf[STRINGSZ], *tn;
+ va_list arg;
+
+ tn = "??none??";
+ if(curtext != P && curtext->from.sym != S)
+ tn = curtext->from.sym->name;
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ print("%s: %s\n", tn, buf);
+
+ nerrors++;
+ if(nerrors > 10) {
+ print("too many errors\n");
+ errorexit();
+ }
+}
--- /dev/null
+++ b/utils/0l/mkfile
@@ -1,0 +1,33 @@
+<../../mkconfig
+
+TARG=0l
+OFILES=\
+ asm.$O\
+ list.$O\
+ noop.$O\
+ sched.$O\
+ obj.$O\
+ optab.$O\
+ pass.$O\
+ span.$O\
+ enam.$O\
+ $TARGMODEL.$O\
+
+HFILES=\
+ l.h\
+ ../vc/v.out.h\
+ ../include/ar.h\
+
+LIBS=bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS= $CFLAGS -I ../include -I.
+
+enam.$O: ../0c/enam.c
+ $CC $CFLAGS ../0c/enam.c
+
+$TARGMODEL.$O: ../ld/$TARGMODEL.c
+ $CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/0l/noop.c
@@ -1,0 +1,420 @@
+#include "l.h"
+
+void
+noops(void)
+{
+ Prog *p, *p1, *q, *q1;
+ int o, curframe, curbecome, maxbecome;
+
+ /*
+ * find leaf subroutines
+ * become sizes
+ * frame sizes
+ * strip NOPs
+ * expand RET
+ * expand BECOME pseudo
+ */
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f noops\n", cputime());
+ Bflush(&bso);
+
+ curframe = 0;
+ curbecome = 0;
+ maxbecome = 0;
+ curtext = 0;
+
+ q = P;
+ for(p = firstp; p != P; p = p->link) {
+
+ /* find out how much arg space is used in this TEXT */
+ if(p->to.type == D_OREG && p->to.reg == REGSP)
+ if(p->to.offset > curframe)
+ curframe = p->to.offset;
+
+ switch(p->as) {
+ case ATEXT:
+ if(curtext && curtext->from.sym) {
+ curtext->from.sym->frame = curframe;
+ curtext->from.sym->become = curbecome;
+ if(curbecome > maxbecome)
+ maxbecome = curbecome;
+ }
+ curframe = 0;
+ curbecome = 0;
+
+ p->mark |= LABEL|LEAF|SYNC;
+ if(p->link)
+ p->link->mark |= LABEL;
+ curtext = p;
+ break;
+
+ /* too hard, just leave alone */
+ case AMOVW:
+ case AMOVV:
+ if(p->to.type == D_FCREG ||
+ p->to.type == D_MREG) {
+ p->mark |= LABEL|SYNC;
+ break;
+ }
+ if(p->from.type == D_FCREG ||
+ p->from.type == D_MREG) {
+ p->mark |= LABEL|SYNC;
+ addnop(p);
+ addnop(p);
+ nop.mfrom.count += 2;
+ nop.mfrom.outof += 2;
+ break;
+ }
+ break;
+
+ /* too hard, just leave alone */
+ case ACASE:
+ case ASYSCALL:
+ case AWORD:
+ case ATLBWR:
+ case ATLBWI:
+ case ATLBP:
+ case ATLBR:
+ p->mark |= LABEL|SYNC;
+ break;
+
+ case ANOR:
+ if(p->to.type == D_REG && p->to.reg == REGZERO)
+ p->mark |= LABEL|SYNC;
+ break;
+
+ case ARET:
+ /* special form of RET is BECOME */
+ if(p->from.type == D_CONST)
+ if(p->from.offset > curbecome)
+ curbecome = p->from.offset;
+
+ if(p->link != P)
+ p->link->mark |= LABEL;
+ break;
+
+ case ANOP:
+ q1 = p->link;
+ q->link = q1; /* q is non-nop */
+ q1->mark |= p->mark;
+ continue;
+
+ case ABCASE:
+ p->mark |= LABEL|SYNC;
+ goto dstlab;
+
+ case ABGEZAL:
+ case ABLTZAL:
+ case AJAL:
+ if(curtext != P)
+ curtext->mark &= ~LEAF;
+
+ case AJMP:
+ case ABEQ:
+ case ABGEZ:
+ case ABGTZ:
+ case ABLEZ:
+ case ABLTZ:
+ case ABNE:
+ case ABFPT:
+ case ABFPF:
+ p->mark |= BRANCH;
+
+ dstlab:
+ q1 = p->cond;
+ if(q1 != P) {
+ while(q1->as == ANOP) {
+ q1 = q1->link;
+ p->cond = q1;
+ }
+ if(!(q1->mark & LEAF))
+ q1->mark |= LABEL;
+ } else
+ p->mark |= LABEL;
+ q1 = p->link;
+ if(q1 != P)
+ q1->mark |= LABEL;
+ break;
+ }
+ q = p;
+ }
+
+ if(curtext && curtext->from.sym) {
+ curtext->from.sym->frame = curframe;
+ curtext->from.sym->become = curbecome;
+ if(curbecome > maxbecome)
+ maxbecome = curbecome;
+ }
+
+ if(debug['b'])
+ print("max become = %d\n", maxbecome);
+ xdefine("ALEFbecome", STEXT, maxbecome);
+
+ curtext = 0;
+ for(p = firstp; p != P; p = p->link) {
+ switch(p->as) {
+ case ATEXT:
+ curtext = p;
+ break;
+ case AJAL:
+ if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+ o = maxbecome - curtext->from.sym->frame;
+ if(o <= 0)
+ break;
+ /* calling a become or calling a variable */
+ if(p->to.sym == S || p->to.sym->become) {
+ curtext->to.offset += o;
+ if(debug['b']) {
+ curp = p;
+ print("%D calling %D increase %d\n",
+ &curtext->from, &p->to, o);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ for(p = firstp; p != P; p = p->link) {
+ o = p->as;
+ switch(o) {
+ case ATEXT:
+ curtext = p;
+ autosize = p->to.offset + 8;
+ if(autosize <= 8)
+ if(curtext->mark & LEAF) {
+ p->to.offset = -8;
+ autosize = 0;
+ }
+
+ q = p;
+ if(autosize) {
+ if(autosize & 7)
+ Bprint(&bso, "odd stack in: %s\n",
+ curtext->from.sym->name);
+ q = prg();
+ q->as = AADD;
+ q->line = p->line;
+ q->from.type = D_CONST;
+ q->from.offset = -autosize;
+ q->to.type = D_REG;
+ q->to.reg = REGSP;
+
+ q->link = p->link;
+ p->link = q;
+ } else
+ if(!(curtext->mark & LEAF)) {
+ if(debug['v'])
+ Bprint(&bso, "save suppressed in: %s\n",
+ curtext->from.sym->name);
+ Bflush(&bso);
+ curtext->mark |= LEAF;
+ }
+
+ if(curtext->mark & LEAF) {
+ if(curtext->from.sym)
+ curtext->from.sym->type = SLEAF;
+ break;
+ }
+
+ q1 = prg();
+ q1->as = AMOVW;
+ q1->line = p->line;
+ q1->from.type = D_REG;
+ q1->from.reg = REGLINK;
+ q1->to.type = D_OREG;
+ q1->from.offset = 0;
+ q1->to.reg = REGSP;
+
+ q1->link = q->link;
+ q->link = q1;
+ break;
+
+ case ARET:
+ nocache(p);
+ if(p->from.type == D_CONST)
+ goto become;
+ if(curtext->mark & LEAF) {
+ if(!autosize) {
+ p->as = AJMP;
+ p->from = zprg.from;
+ p->to.type = D_OREG;
+ p->to.offset = 0;
+ p->to.reg = REGLINK;
+ p->mark |= BRANCH;
+ break;
+ }
+
+ p->as = AADD;
+ p->from.type = D_CONST;
+ p->from.offset = autosize;
+ p->to.type = D_REG;
+ p->to.reg = REGSP;
+
+ q = prg();
+ q->as = AJMP;
+ q->line = p->line;
+ q->to.type = D_OREG;
+ q->to.offset = 0;
+ q->to.reg = REGLINK;
+ q->mark |= BRANCH;
+
+ q->link = p->link;
+ p->link = q;
+ break;
+ }
+ p->as = AMOVW;
+ p->from.type = D_OREG;
+ p->from.offset = 0;
+ p->from.reg = REGSP;
+ p->to.type = D_REG;
+ p->to.reg = 2;
+
+ q = p;
+ if(autosize) {
+ q = prg();
+ q->as = AADD;
+ q->line = p->line;
+ q->from.type = D_CONST;
+ q->from.offset = autosize;
+ q->to.type = D_REG;
+ q->to.reg = REGSP;
+
+ q->link = p->link;
+ p->link = q;
+ }
+
+ q1 = prg();
+ q1->as = AJMP;
+ q1->line = p->line;
+ q1->to.type = D_OREG;
+ q1->to.offset = 0;
+ q1->to.reg = 2;
+ q1->mark |= BRANCH;
+
+ q1->link = q->link;
+ q->link = q1;
+ break;
+
+ become:
+ if(curtext->mark & LEAF) {
+
+ q = prg();
+ q->line = p->line;
+ q->as = AJMP;
+ q->from = zprg.from;
+ q->to = p->to;
+ q->cond = p->cond;
+ q->link = p->link;
+ q->mark |= BRANCH;
+ p->link = q;
+
+ p->as = AADD;
+ p->from = zprg.from;
+ p->from.type = D_CONST;
+ p->from.offset = autosize;
+ p->to = zprg.to;
+ p->to.type = D_REG;
+ p->to.reg = REGSP;
+
+ break;
+ }
+ q = prg();
+ q->line = p->line;
+ q->as = AJMP;
+ q->from = zprg.from;
+ q->to = p->to;
+ q->cond = p->cond;
+ q->link = p->link;
+ q->mark |= BRANCH;
+ p->link = q;
+
+ q = prg();
+ q->line = p->line;
+ q->as = AADD;
+ q->from.type = D_CONST;
+ q->from.offset = autosize;
+ q->to.type = D_REG;
+ q->to.reg = REGSP;
+ q->link = p->link;
+ p->link = q;
+
+ p->as = AMOVW;
+ p->from = zprg.from;
+ p->from.type = D_OREG;
+ p->from.offset = 0;
+ p->from.reg = REGSP;
+ p->to = zprg.to;
+ p->to.type = D_REG;
+ p->to.reg = REGLINK;
+
+ break;
+ }
+ }
+
+ curtext = P;
+ q = P; /* p - 1 */
+ q1 = firstp; /* top of block */
+ o = 0; /* count of instructions */
+ for(p = firstp; p != P; p = p1) {
+ p1 = p->link;
+ o++;
+ if(p->mark & NOSCHED){
+ if(q1 != p){
+ sched(q1, q);
+ }
+ for(; p != P; p = p->link){
+ if(!(p->mark & NOSCHED))
+ break;
+ q = p;
+ }
+ p1 = p;
+ q1 = p;
+ o = 0;
+ continue;
+ }
+ if(p->mark & (LABEL|SYNC)) {
+ if(q1 != p)
+ sched(q1, q);
+ q1 = p;
+ o = 1;
+ }
+ if(p->mark & (BRANCH|SYNC)) {
+ sched(q1, p);
+ q1 = p1;
+ o = 0;
+ }
+ if(o >= NSCHED) {
+ sched(q1, p);
+ q1 = p1;
+ o = 0;
+ }
+ q = p;
+ }
+}
+
+void
+addnop(Prog *p)
+{
+ Prog *q;
+
+ q = prg();
+ q->as = ANOR;
+ q->line = p->line;
+ q->from.type = D_REG;
+ q->from.reg = REGZERO;
+ q->to.type = D_REG;
+ q->to.reg = REGZERO;
+
+ q->link = p->link;
+ p->link = q;
+}
+
+void
+nocache(Prog *p)
+{
+ p->optab = 0;
+ p->from.class = 0;
+ p->to.class = 0;
+}
--- /dev/null
+++ b/utils/0l/obj.c
@@ -1,0 +1,1384 @@
+#define EXTERN
+#include "l.h"
+#include <ar.h>
+
+#ifndef DEFAULT
+#define DEFAULT '9'
+#endif
+
+char *noname = "<none>";
+char symname[] = SYMDEF;
+char thechar = '0';
+char *thestring = "spim";
+
+/*
+ * -H0 -T0x40004C -D0x10000000 is abbrev unix
+ * -H1 -T0x80020000 -R4 is bootp() format for 3k
+ * -H2 -T4128 -R4096 is plan9 spim format
+ * -H3 -T0x80020000 -R8 is bootp() format for 4k
+ * -H4 -T0x400000 -R4 is sgi unix coff executable
+ * -H5 -T0x4000A0 -R4 is sgi unix elf executable
+ * -H6 -T0x4000A0 -R4 is plan9 spim format
+ */
+
+void
+main(int argc, char *argv[])
+{
+ int c;
+ char *a;
+
+ Binit(&bso, 1, OWRITE);
+ cout = -1;
+ listinit();
+ outfile = 0;
+ nerrors = 0;
+ curtext = P;
+ HEADTYPE = -1;
+ INITTEXT = -1;
+ INITDAT = -1;
+ INITRND = -1;
+ INITENTRY = 0;
+
+ ARGBEGIN {
+ default:
+ c = ARGC();
+ if(c >= 0 && c < sizeof(debug))
+ debug[c]++;
+ break;
+ case 'o':
+ outfile = ARGF();
+ break;
+ case 'E':
+ a = ARGF();
+ if(a)
+ INITENTRY = a;
+ break;
+ case 'T':
+ a = ARGF();
+ if(a)
+ INITTEXT = atolwhex(a);
+ break;
+ case 'D':
+ a = ARGF();
+ if(a)
+ INITDAT = atolwhex(a);
+ break;
+ case 'R':
+ a = ARGF();
+ if(a)
+ INITRND = atolwhex(a);
+ break;
+ case 'H':
+ a = ARGF();
+ if(a)
+ HEADTYPE = atolwhex(a);
+ /* do something about setting INITTEXT */
+ break;
+ } ARGEND
+
+ USED(argc);
+
+ if(*argv == 0) {
+ diag("usage: vl [-options] objects\n");
+ errorexit();
+ }
+ if(!debug['9'] && !debug['U'] && !debug['B'])
+ debug[DEFAULT] = 1;
+ if(HEADTYPE == -1) {
+ if(debug['U'])
+ HEADTYPE = 0;
+ if(debug['B'])
+ HEADTYPE = 1;
+ if(debug['9'])
+ HEADTYPE = 2;
+ }
+ switch(HEADTYPE) {
+ default:
+ diag("unknown -H option");
+ errorexit();
+
+ case 0: /* unix simple */
+ HEADR = 20L+56L;
+ if(INITTEXT == -1)
+ INITTEXT = 0x40004CL;
+ if(INITDAT == -1)
+ INITDAT = 0x10000000L;
+ if(INITRND == -1)
+ INITRND = 0;
+ break;
+ case 1: /* boot for 3k */
+ HEADR = 20L+60L;
+ if(INITTEXT == -1)
+ INITTEXT = 0x80020000L;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4;
+ break;
+ case 2: /* plan 9 spim */
+ HEADR = 32L;
+ if(INITTEXT == -1)
+ INITTEXT = 4128;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4096;
+ break;
+ case 3: /* boot for 4k */
+ HEADR = 20L+56L+3*40L;
+ if(INITTEXT == -1)
+ INITTEXT = 0x80020000L;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 8;
+ break;
+ case 4: /* sgi unix coff executable */
+ HEADR = 20L+56L+3*40L;
+ if(INITTEXT == -1)
+ INITTEXT = 0x00400000L+HEADR;
+ if(INITDAT == -1)
+ INITDAT = 0x10000000;
+ if(INITRND == -1)
+ INITRND = 0;
+ break;
+ case 5: /* sgi unix elf executable */
+ HEADR = rnd(52L+3*32L, 16);
+ if(INITTEXT == -1)
+ INITTEXT = 0x00400000L+HEADR;
+ if(INITDAT == -1)
+ INITDAT = 0x10000000;
+ if(INITRND == -1)
+ INITRND = 0;
+ break;
+ case 6: /* plan 9 spim */
+ HEADR = 32L;
+ if(INITTEXT == -1)
+ INITTEXT = 4128;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4096;
+ break;
+ }
+ if(INITDAT != 0 && INITRND != 0)
+ print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+ INITDAT, INITRND);
+ if(debug['v'])
+ Bprint(&bso, "HEADER = -H0x%x -T0x%lux -D0x%lux -R0x%lux\n",
+ HEADTYPE, INITTEXT, INITDAT, INITRND);
+ Bflush(&bso);
+ zprg.as = AGOK;
+ zprg.reg = NREG;
+ zprg.from.name = D_NONE;
+ zprg.from.type = D_NONE;
+ zprg.from.reg = NREG;
+ zprg.to = zprg.from;
+ buildop();
+ histgen = 0;
+ textp = P;
+ datap = P;
+ pc = 0;
+ dtype = 4;
+ if(outfile == 0)
+ outfile = "0.out";
+ cout = create(outfile, 1, 0775);
+ if(cout < 0) {
+ diag("%s: cannot create\n", outfile);
+ errorexit();
+ }
+ nuxiinit();
+
+ version = 0;
+ cbp = (uchar*)buf.cbuf;
+ cbc = sizeof(buf.cbuf);
+ firstp = prg();
+ lastp = firstp;
+
+ if(INITENTRY == 0) {
+ INITENTRY = "_main";
+ if(debug['p'])
+ INITENTRY = "_mainp";
+ if(!debug['l'])
+ lookup(INITENTRY, 0)->type = SXREF;
+ } else
+ lookup(INITENTRY, 0)->type = SXREF;
+
+ while(*argv)
+ objfile(*argv++);
+ if(!debug['l'])
+ loadlib();
+ firstp = firstp->link;
+ if(firstp == P)
+ goto out;
+ patch();
+ if(debug['p'])
+ if(debug['1'])
+ doprof1();
+ else
+ doprof2();
+ dodata();
+ follow();
+ if(firstp == P)
+ goto out;
+ noops();
+ span();
+ asmb();
+ undef();
+
+out:
+ if(debug['v']) {
+ Bprint(&bso, "%5.2f cpu time\n", cputime());
+ Bprint(&bso, "%ld memory used\n", thunk);
+ Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+ Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+ }
+ Bflush(&bso);
+ errorexit();
+}
+
+void
+loadlib(void)
+{
+ int i;
+ long h;
+ Sym *s;
+
+loop:
+ xrefresolv = 0;
+ for(i=0; i<libraryp; i++) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+ objfile(library[i]);
+ }
+ if(xrefresolv)
+ for(h=0; h<nelem(hash); h++)
+ for(s = hash[h]; s != S; s = s->link)
+ if(s->type == SXREF)
+ goto loop;
+}
+
+void
+errorexit(void)
+{
+
+ Bflush(&bso);
+ if(nerrors) {
+ if(cout >= 0)
+ remove(outfile);
+ exits("error");
+ }
+ exits(0);
+}
+
+void
+objfile(char *file)
+{
+ long off, esym, cnt, l;
+ int f, work;
+ Sym *s;
+ char magbuf[SARMAG];
+ char name[100], pname[150];
+ struct ar_hdr arhdr;
+ char *e, *start, *stop;
+
+ if(file[0] == '-' && file[1] == 'l') {
+ if(debug['9'])
+ sprint(name, "/%s/lib/lib", thestring);
+ else
+ sprint(name, "/usr/%clib/lib", thechar);
+ strcat(name, file+2);
+ strcat(name, ".a");
+ file = name;
+ }
+ if(debug['v'])
+ Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+ Bflush(&bso);
+ f = open(file, 0);
+ if(f < 0) {
+ diag("cannot open file: %s\n", file);
+ errorexit();
+ }
+ l = read(f, magbuf, SARMAG);
+ if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+ /* load it as a regular file */
+ l = seek(f, 0L, 2);
+ seek(f, 0L, 0);
+ ldobj(f, l, file);
+ close(f);
+ return;
+ }
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f ldlib: %s\n", cputime(), file);
+ l = read(f, &arhdr, SAR_HDR);
+ if(l != SAR_HDR) {
+ diag("%s: short read on archive file symbol header\n", file);
+ goto out;
+ }
+ if(strncmp(arhdr.name, symname, strlen(symname))) {
+ diag("%s: first entry not symbol header\n", file);
+ goto out;
+ }
+
+ esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+ off = SARMAG + SAR_HDR;
+
+ /*
+ * just bang the whole symbol file into memory
+ */
+ seek(f, off, 0);
+ cnt = esym - off;
+ start = malloc(cnt + 10);
+ cnt = read(f, start, cnt);
+ if(cnt <= 0){
+ close(f);
+ return;
+ }
+ stop = &start[cnt];
+ memset(stop, 0, 10);
+
+ work = 1;
+ while(work){
+ if(debug['v'])
+ Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+ Bflush(&bso);
+ work = 0;
+ for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+ s = lookup(e+5, 0);
+ if(s->type != SXREF)
+ continue;
+ sprint(pname, "%s(%s)", file, s->name);
+ if(debug['v'])
+ Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+ Bflush(&bso);
+ l = e[1] & 0xff;
+ l |= (e[2] & 0xff) << 8;
+ l |= (e[3] & 0xff) << 16;
+ l |= (e[4] & 0xff) << 24;
+ seek(f, l, 0);
+ l = read(f, &arhdr, SAR_HDR);
+ if(l != SAR_HDR)
+ goto bad;
+ if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+ goto bad;
+ l = atolwhex(arhdr.size);
+ ldobj(f, l, pname);
+ if(s->type == SXREF) {
+ diag("%s: failed to load: %s\n", file, s->name);
+ errorexit();
+ }
+ work = 1;
+ xrefresolv = 1;
+ }
+ }
+ return;
+
+bad:
+ diag("%s: bad or out of date archive\n", file);
+out:
+ close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+ int i, c;
+ long l;
+ Sym *s;
+ Auto *u;
+
+ c = p[2];
+ if(c < 0 || c > NSYM){
+ print("sym out of range: %d\n", c);
+ p[0] = ALAST+1;
+ return 0;
+ }
+ a->type = p[0];
+ a->reg = p[1];
+ a->sym = h[c];
+ a->name = p[3];
+ c = 4;
+
+ if(a->reg < 0 || a->reg > NREG) {
+ print("register out of range %d\n", a->reg);
+ p[0] = ALAST+1;
+ return 0; /* force real diagnostic */
+ }
+
+ switch(a->type) {
+ default:
+ print("unknown type %d\n", a->type);
+ p[0] = ALAST+1;
+ return 0; /* force real diagnostic */
+
+ case D_NONE:
+ case D_REG:
+ case D_FREG:
+ case D_MREG:
+ case D_FCREG:
+ case D_LO:
+ case D_HI:
+ break;
+
+ case D_BRANCH:
+ case D_OREG:
+ case D_CONST:
+ case D_OCONST:
+ a->offset = p[4] | (p[5]<<8) |
+ (p[6]<<16) | (p[7]<<24);
+ c += 4;
+ break;
+
+ case D_SCONST:
+ a->sval = malloc(NSNAME);
+ memmove(a->sval, p+4, NSNAME);
+ c += NSNAME;
+ break;
+
+ case D_VCONST:
+ case D_FCONST:
+ a->ieee = malloc(sizeof(Ieee));
+ a->ieee->l = p[4] | (p[5]<<8) |
+ (p[6]<<16) | (p[7]<<24);
+ a->ieee->h = p[8] | (p[9]<<8) |
+ (p[10]<<16) | (p[11]<<24);
+ c += 8;
+ break;
+ }
+ s = a->sym;
+ if(s == S)
+ return c;
+ i = a->name;
+ if(i != D_AUTO && i != D_PARAM)
+ return c;
+
+ l = a->offset;
+ for(u=curauto; u; u=u->link)
+ if(u->asym == s)
+ if(u->type == i) {
+ if(u->aoffset > l)
+ u->aoffset = l;
+ return c;
+ }
+
+ while(nhunk < sizeof(Auto))
+ gethunk();
+ u = (Auto*)hunk;
+ nhunk -= sizeof(Auto);
+ hunk += sizeof(Auto);
+
+ u->link = curauto;
+ curauto = u;
+ u->asym = s;
+ u->aoffset = l;
+ u->type = i;
+ return c;
+}
+
+void
+addlib(char *obj)
+{
+ char name[1024], comp[256], *p;
+ int i;
+
+ if(histfrogp <= 0)
+ return;
+
+ if(histfrog[0]->name[1] == '/') {
+ sprint(name, "");
+ i = 1;
+ } else
+ if(histfrog[0]->name[1] == '.') {
+ sprint(name, ".");
+ i = 0;
+ } else {
+ if(debug['9'])
+ sprint(name, "/%s/lib", thestring);
+ else
+ sprint(name, "/usr/%clib", thechar);
+ i = 0;
+ }
+
+ for(; i<histfrogp; i++) {
+ snprint(comp, sizeof comp, histfrog[i]->name+1);
+ for(;;) {
+ p = strstr(comp, "$O");
+ if(p == 0)
+ break;
+ memmove(p+1, p+2, strlen(p+2)+1);
+ p[0] = thechar;
+ }
+ for(;;) {
+ p = strstr(comp, "$M");
+ if(p == 0)
+ break;
+ if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+ diag("library component too long");
+ return;
+ }
+ memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+ memmove(p, thestring, strlen(thestring));
+ }
+ if(strlen(name) + strlen(comp) + 3 >= sizeof(name)) {
+ diag("library component too long");
+ return;
+ }
+ strcat(name, "/");
+ strcat(name, comp);
+ }
+ for(i=0; i<libraryp; i++)
+ if(strcmp(name, library[i]) == 0)
+ return;
+ if(libraryp == nelem(library)){
+ diag("too many autolibs; skipping %s", name);
+ return;
+ }
+
+ p = malloc(strlen(name) + 1);
+ strcpy(p, name);
+ library[libraryp] = p;
+ p = malloc(strlen(obj) + 1);
+ strcpy(p, obj);
+ libraryobj[libraryp] = p;
+ libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+ Auto *u;
+ Sym *s;
+ int i, j, k;
+
+ u = malloc(sizeof(Auto));
+ s = malloc(sizeof(Sym));
+ s->name = malloc(2*(histfrogp+1) + 1);
+
+ u->asym = s;
+ u->type = type;
+ u->aoffset = line;
+ u->link = curhist;
+ curhist = u;
+
+ j = 1;
+ for(i=0; i<histfrogp; i++) {
+ k = histfrog[i]->value;
+ s->name[j+0] = k>>8;
+ s->name[j+1] = k;
+ j += 2;
+ }
+}
+
+void
+histtoauto(void)
+{
+ Auto *l;
+
+ while(l = curhist) {
+ curhist = l->link;
+ l->link = curauto;
+ curauto = l;
+ }
+}
+
+void
+collapsefrog(Sym *s)
+{
+ int i;
+
+ /*
+ * bad encoding of path components only allows
+ * MAXHIST components. if there is an overflow,
+ * first try to collapse xxx/..
+ */
+ for(i=1; i<histfrogp; i++)
+ if(strcmp(histfrog[i]->name+1, "..") == 0) {
+ memmove(histfrog+i-1, histfrog+i+1,
+ (histfrogp-i-1)*sizeof(histfrog[0]));
+ histfrogp--;
+ goto out;
+ }
+
+ /*
+ * next try to collapse .
+ */
+ for(i=0; i<histfrogp; i++)
+ if(strcmp(histfrog[i]->name+1, ".") == 0) {
+ memmove(histfrog+i, histfrog+i+1,
+ (histfrogp-i-1)*sizeof(histfrog[0]));
+ goto out;
+ }
+
+ /*
+ * last chance, just truncate from front
+ */
+ memmove(histfrog+0, histfrog+1,
+ (histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+ histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+ p->as = ANOP;
+ p->from.type = D_NONE;
+ p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+ int n;
+
+ n = stop - good;
+ memmove(buf, good, stop - good);
+ stop = buf + n;
+ n = MAXIO - n;
+ if(n > max)
+ n = max;
+ n = read(f, stop, n);
+ if(n <= 0)
+ return 0;
+ return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+ long ipc;
+ Prog *p, *t;
+ uchar *bloc, *bsize, *stop;
+ Sym *h[NSYM], *s, *di;
+ int v, o, r, skip;
+
+ bsize = buf.xbuf;
+ bloc = buf.xbuf;
+ di = S;
+
+newloop:
+ memset(h, 0, sizeof(h));
+ version++;
+ histfrogp = 0;
+ ipc = pc;
+ skip = 0;
+
+loop:
+ if(c <= 0)
+ goto eof;
+ r = bsize - bloc;
+ if(r < 100 && r < c) { /* enough for largest prog */
+ bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+ if(bsize == 0)
+ goto eof;
+ bloc = buf.xbuf;
+ goto loop;
+ }
+ o = bloc[0]; /* as */
+ if(o <= AXXX || o >= ALAST) {
+ diag("%s: line %ld: opcode out of range %d\n", pn, pc-ipc, o);
+ print(" probably not a .v file\n");
+ errorexit();
+ }
+ if(o == ANAME || o == ASIGNAME) {
+ if(o == ASIGNAME) {
+ bloc += 4;
+ c -= 4;
+ }
+ stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+ if(stop == 0){
+ bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+ if(bsize == 0)
+ goto eof;
+ bloc = buf.xbuf;
+ stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+ if(stop == 0){
+ fprint(2, "%s: name too long\n", pn);
+ errorexit();
+ }
+ }
+ v = bloc[1]; /* type */
+ o = bloc[2]; /* sym */
+ bloc += 3;
+ c -= 3;
+
+ r = 0;
+ if(v == D_STATIC)
+ r = version;
+ s = lookup((char*)bloc, r);
+ c -= &stop[1] - bloc;
+ bloc = stop + 1;
+
+ if(debug['W'])
+ print(" ANAME %s\n", s->name);
+ h[o] = s;
+ if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+ s->type = SXREF;
+ if(v == D_FILE) {
+ if(s->type != SFILE) {
+ histgen++;
+ s->type = SFILE;
+ s->value = histgen;
+ }
+ if(histfrogp < MAXHIST) {
+ histfrog[histfrogp] = s;
+ histfrogp++;
+ } else
+ collapsefrog(s);
+ }
+ goto loop;
+ }
+
+ if(nhunk < sizeof(Prog))
+ gethunk();
+ p = (Prog*)hunk;
+ nhunk -= sizeof(Prog);
+ hunk += sizeof(Prog);
+
+ p->as = o;
+ p->reg = bloc[1] & 0x7f;
+ if(bloc[1] & 0x80)
+ p->mark = NOSCHED;
+ p->line = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
+
+ r = zaddr(bloc+6, &p->from, h) + 6;
+ r += zaddr(bloc+r, &p->to, h);
+ bloc += r;
+ c -= r;
+
+ if(p->reg < 0 || p->reg > NREG)
+ diag("register out of range %d\n", p->reg);
+
+ p->link = P;
+ p->cond = P;
+
+ if(debug['W'])
+ print("%P\n", p);
+
+ switch(o) {
+ case AHISTORY:
+ if(p->to.offset == -1) {
+ addlib(pn);
+ histfrogp = 0;
+ goto loop;
+ }
+ addhist(p->line, D_FILE); /* 'z' */
+ if(p->to.offset)
+ addhist(p->to.offset, D_FILE1); /* 'Z' */
+ histfrogp = 0;
+ goto loop;
+
+ case AEND:
+ histtoauto();
+ if(curtext != P)
+ curtext->to.autom = curauto;
+ curauto = 0;
+ curtext = P;
+ if(c)
+ goto newloop;
+ return;
+
+ case AGLOBL:
+ s = p->from.sym;
+ if(s == S) {
+ diag("GLOBL must have a name\n%P\n", p);
+ errorexit();
+ }
+ if(s->type == 0 || s->type == SXREF) {
+ s->type = SBSS;
+ s->value = 0;
+ }
+ if(s->type != SBSS) {
+ diag("redefinition: %s\n%P\n", s->name, p);
+ s->type = SBSS;
+ s->value = 0;
+ }
+ if(p->to.offset && s->value && p->to.offset != s->value)
+ print("warning: resize of a global: %s\n%P\n", s->name, p);
+ if(p->to.offset > s->value)
+ s->value = p->to.offset;
+ break;
+
+ case ADYNT:
+ if(p->to.sym == S) {
+ diag("DYNT without a sym\n%P\n", p);
+ break;
+ }
+ di = p->to.sym;
+ p->reg = 4;
+ if(di->type == SXREF) {
+ if(debug['z'])
+ Bprint(&bso, "%P set to %d\n", p, dtype);
+ di->type = SCONST;
+ di->value = dtype;
+ dtype += 4;
+ }
+ if(p->from.sym == S)
+ break;
+
+ p->from.offset = di->value;
+ p->from.sym->type = SDATA;
+ if(curtext == P) {
+ diag("DYNT not in text: %P\n", p);
+ break;
+ }
+ p->to.sym = curtext->from.sym;
+ p->to.type = D_CONST;
+ p->link = datap;
+ datap = p;
+ break;
+
+ case AINIT:
+ if(p->from.sym == S) {
+ diag("INIT without a sym\n%P\n", p);
+ break;
+ }
+ if(di == S) {
+ diag("INIT without previous DYNT\n%P\n", p);
+ break;
+ }
+ p->from.offset = di->value;
+ p->from.sym->type = SDATA;
+ p->link = datap;
+ datap = p;
+ break;
+
+ case ADATA:
+ if(p->from.sym == S) {
+ diag("DATA without a sym\n%P\n", p);
+ break;
+ }
+ p->link = datap;
+ datap = p;
+ break;
+
+ case AGOK:
+ diag("unknown opcode\n%P\n", p);
+ p->pc = pc;
+ pc++;
+ break;
+
+ case ATEXT:
+ if(curtext != P) {
+ histtoauto();
+ curtext->to.autom = curauto;
+ curauto = 0;
+ }
+ skip = 0;
+ curtext = p;
+ s = p->from.sym;
+ if(s == S) {
+ diag("TEXT must have a name\n%P\n", p);
+ errorexit();
+ }
+ autosize = p->to.offset;
+ if(autosize & 7) {
+ diag("stack frame not 8 multiple: %s\n%P\n", s->name, p);
+ autosize = autosize + 7 & ~7;
+ p->to.offset = autosize;
+ }
+ autosize += 8;
+ if(s->type != 0 && s->type != SXREF) {
+ if(p->reg & DUPOK) {
+ skip = 1;
+ goto casedef;
+ }
+ diag("redefinition: %s\n%P\n", s->name, p);
+ }
+ s->type = STEXT;
+ s->value = pc;
+ lastp->link = p;
+ lastp = p;
+ p->pc = pc;
+ pc++;
+ if(textp == P) {
+ textp = p;
+ etextp = p;
+ goto loop;
+ }
+ etextp->cond = p;
+ etextp = p;
+ break;
+
+ case ASUB:
+ case ASUBU:
+ if(p->from.type == D_CONST)
+ if(p->from.name == D_NONE) {
+ p->from.offset = -p->from.offset;
+ if(p->as == ASUB)
+ p->as = AADD;
+ else
+ p->as = AADDU;
+ }
+ goto casedef;
+
+ case AMOVF:
+ if(skip)
+ goto casedef;
+
+ if(p->from.type == D_FCONST) {
+ /* size sb 9 max */
+ sprint(literal, "$%lux", ieeedtof(p->from.ieee));
+ s = lookup(literal, 0);
+ if(s->type == 0) {
+ s->type = SBSS;
+ s->value = 4;
+ t = prg();
+ t->as = ADATA;
+ t->line = p->line;
+ t->from.type = D_OREG;
+ t->from.sym = s;
+ t->from.name = D_EXTERN;
+ t->reg = 4;
+ t->to = p->from;
+ t->link = datap;
+ datap = t;
+ }
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ p->from.name = D_EXTERN;
+ p->from.offset = 0;
+ }
+ goto casedef;
+
+ case AMOVD:
+ if(skip)
+ goto casedef;
+
+ if(p->from.type == D_FCONST) {
+ /* size sb 18 max */
+ sprint(literal, "$%lux.%lux",
+ p->from.ieee->h, p->from.ieee->l);
+ s = lookup(literal, 0);
+ if(s->type == 0) {
+ s->type = SBSS;
+ s->value = 8;
+ t = prg();
+ t->as = ADATA;
+ t->line = p->line;
+ t->from.type = D_OREG;
+ t->from.sym = s;
+ t->from.name = D_EXTERN;
+ t->reg = 8;
+ t->to = p->from;
+ t->link = datap;
+ datap = t;
+ }
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ p->from.name = D_EXTERN;
+ p->from.offset = 0;
+ }
+ goto casedef;
+
+ case AMOVV:
+ if(skip)
+ goto casedef;
+
+ if(p->from.type == D_VCONST) {
+ /* size sb 18 max */
+ sprint(literal, "$%lux.%lux",
+ p->from.ieee->h, p->from.ieee->l);
+ s = lookup(literal, 0);
+ if(s->type == 0) {
+ s->type = SBSS;
+ s->value = 8;
+ t = prg();
+ t->as = ADATA;
+ t->line = p->line;
+ t->from.type = D_OREG;
+ t->from.sym = s;
+ t->from.name = D_EXTERN;
+ t->reg = 8;
+ t->to = p->from;
+ t->link = datap;
+ datap = t;
+ }
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ p->from.name = D_EXTERN;
+ p->from.offset = 0;
+ }
+ goto casedef;
+
+ default:
+ casedef:
+ if(skip)
+ nopout(p);
+
+ if(p->to.type == D_BRANCH)
+ p->to.offset += ipc;
+ lastp->link = p;
+ lastp = p;
+ p->pc = pc;
+ pc++;
+ break;
+ }
+ goto loop;
+
+eof:
+ diag("truncated object file: %s\n", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+ Sym *s;
+ char *p;
+ long h;
+ int c, l;
+
+ h = v;
+ for(p=symb; c = *p; p++)
+ h = h+h+h + c;
+ l = (p - symb) + 1;
+ if(h < 0)
+ h = ~h;
+ h %= NHASH;
+ for(s = hash[h]; s != S; s = s->link)
+ if(s->version == v)
+ if(memcmp(s->name, symb, l) == 0)
+ return s;
+
+ while(nhunk < sizeof(Sym))
+ gethunk();
+ s = (Sym*)hunk;
+ nhunk -= sizeof(Sym);
+ hunk += sizeof(Sym);
+
+ s->name = malloc(l);
+ memmove(s->name, symb, l);
+
+ s->link = hash[h];
+ s->type = 0;
+ s->version = v;
+ s->value = 0;
+ hash[h] = s;
+ return s;
+}
+
+Prog*
+prg(void)
+{
+ Prog *p;
+
+ while(nhunk < sizeof(Prog))
+ gethunk();
+ p = (Prog*)hunk;
+ nhunk -= sizeof(Prog);
+ hunk += sizeof(Prog);
+
+ *p = zprg;
+ return p;
+}
+
+void
+gethunk(void)
+{
+ char *h;
+ long nh;
+
+ nh = NHUNK;
+ if(thunk >= 5L*NHUNK) {
+ nh = 5L*NHUNK;
+ if(thunk >= 25L*NHUNK)
+ nh = 25L*NHUNK;
+ }
+ h = mysbrk(nh);
+ if(h == (char*)-1) {
+ diag("out of memory\n");
+ errorexit();
+ }
+ hunk = h;
+ nhunk = nh;
+ thunk += nh;
+}
+
+void
+doprof1(void)
+{
+ Sym *s;
+ long n;
+ Prog *p, *q;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f profile 1\n", cputime());
+ Bflush(&bso);
+ s = lookup("__mcount", 0);
+ n = 1;
+ for(p = firstp->link; p != P; p = p->link) {
+ if(p->as == ATEXT) {
+ q = prg();
+ q->line = p->line;
+ q->link = datap;
+ datap = q;
+ q->as = ADATA;
+ q->from.type = D_OREG;
+ q->from.name = D_EXTERN;
+ q->from.offset = n*4;
+ q->from.sym = s;
+ q->reg = 4;
+ q->to = p->from;
+ q->to.type = D_CONST;
+
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ p->link = q;
+ p = q;
+ p->as = AMOVW;
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.sym = s;
+ p->from.offset = n*4 + 4;
+ p->to.type = D_REG;
+ p->to.reg = REGTMP;
+
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ p->link = q;
+ p = q;
+ p->as = AADDU;
+ p->from.type = D_CONST;
+ p->from.offset = 1;
+ p->to.type = D_REG;
+ p->to.reg = REGTMP;
+
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ p->link = q;
+ p = q;
+ p->as = AMOVW;
+ p->from.type = D_REG;
+ p->from.reg = REGTMP;
+ p->to.type = D_OREG;
+ p->to.name = D_EXTERN;
+ p->to.sym = s;
+ p->to.offset = n*4 + 4;
+
+ n += 2;
+ continue;
+ }
+ }
+ q = prg();
+ q->line = 0;
+ q->link = datap;
+ datap = q;
+
+ q->as = ADATA;
+ q->from.type = D_OREG;
+ q->from.name = D_EXTERN;
+ q->from.sym = s;
+ q->reg = 4;
+ q->to.type = D_CONST;
+ q->to.offset = n;
+
+ s->type = SBSS;
+ s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+ Sym *s2, *s4;
+ Prog *p, *q, *ps2, *ps4;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f profile 2\n", cputime());
+ Bflush(&bso);
+ s2 = lookup("_profin", 0);
+ s4 = lookup("_profout", 0);
+ if(s2->type != STEXT || s4->type != STEXT) {
+ diag("_profin/_profout not defined\n");
+ return;
+ }
+
+ ps2 = P;
+ ps4 = P;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT) {
+ if(p->from.sym == s2) {
+ ps2 = p;
+ p->reg = 1;
+ }
+ if(p->from.sym == s4) {
+ ps4 = p;
+ p->reg = 1;
+ }
+ }
+ }
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT) {
+ if(p->reg & NOPROF) {
+ for(;;) {
+ q = p->link;
+ if(q == P)
+ break;
+ if(q->as == ATEXT)
+ break;
+ p = q;
+ }
+ continue;
+ }
+
+ /*
+ * JAL profin, R2
+ */
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ p->link = q;
+ p = q;
+ p->as = AJAL;
+ p->to.type = D_BRANCH;
+ p->cond = ps2;
+ p->to.sym = s2;
+
+ continue;
+ }
+ if(p->as == ARET) {
+ /*
+ * RET
+ */
+ q = prg();
+ q->as = ARET;
+ q->from = p->from;
+ q->to = p->to;
+ q->link = p->link;
+ p->link = q;
+
+ /*
+ * JAL profout
+ */
+ p->as = AJAL;
+ p->from = zprg.from;
+ p->to = zprg.to;
+ p->to.type = D_BRANCH;
+ p->cond = ps4;
+ p->to.sym = s4;
+
+ p = q;
+
+ continue;
+ }
+ }
+}
+
+void
+nuxiinit(void)
+{
+ int i, c;
+
+ for(i=0; i<4; i++) {
+ c = find1(0x04030201L, i+1);
+ if(i < 2)
+ inuxi2[i] = c;
+ if(i < 1)
+ inuxi1[i] = c;
+ inuxi4[i] = c;
+ fnuxi4[i] = c;
+ fnuxi8[i] = c;
+ fnuxi8[i+4] = c+4;
+ }
+ if(debug['v']) {
+ Bprint(&bso, "inuxi = ");
+ for(i=0; i<1; i++)
+ Bprint(&bso, "%d", inuxi1[i]);
+ Bprint(&bso, " ");
+ for(i=0; i<2; i++)
+ Bprint(&bso, "%d", inuxi2[i]);
+ Bprint(&bso, " ");
+ for(i=0; i<4; i++)
+ Bprint(&bso, "%d", inuxi4[i]);
+ Bprint(&bso, "\nfnuxi = ");
+ for(i=0; i<4; i++)
+ Bprint(&bso, "%d", fnuxi4[i]);
+ Bprint(&bso, " ");
+ for(i=0; i<8; i++)
+ Bprint(&bso, "%d", fnuxi8[i]);
+ Bprint(&bso, "\n");
+ }
+ Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+ char *p;
+ int i;
+
+ p = (char*)&l;
+ for(i=0; i<4; i++)
+ if(*p++ == c)
+ return i;
+ return 0;
+}
+
+vlong
+ieeedtov(Ieee *ieeep)
+{
+ vlong v;
+
+ v = (vlong)ieeep->l & (vlong)0xffffffff;
+ v |= (vlong)ieeep->h << 32;
+ return v;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+ int exp;
+ long v;
+
+ if(ieeep->h == 0)
+ return 0;
+ exp = (ieeep->h>>20) & ((1L<<11)-1L);
+ exp -= (1L<<10) - 2L;
+ v = (ieeep->h & 0xfffffL) << 3;
+ v |= (ieeep->l >> 29) & 0x7L;
+ if((ieeep->l >> 28) & 1) {
+ v++;
+ if(v & 0x800000L) {
+ v = (v & 0x7fffffL) >> 1;
+ exp++;
+ }
+ }
+ if(exp <= -126 || exp >= 130)
+ diag("double fp to single fp overflow\n");
+ v |= ((exp + 126) & 0xffL) << 23;
+ v |= ieeep->h & 0x80000000L;
+ return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+ Ieee e;
+ double fr;
+ int exp;
+
+ if(ieeep->h & (1L<<31)) {
+ e.h = ieeep->h & ~(1L<<31);
+ e.l = ieeep->l;
+ return -ieeedtod(&e);
+ }
+ if(ieeep->l == 0 && ieeep->h == 0)
+ return 0;
+ fr = ieeep->l & ((1L<<16)-1L);
+ fr /= 1L<<16;
+ fr += (ieeep->l>>16) & ((1L<<16)-1L);
+ fr /= 1L<<16;
+ fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+ fr /= 1L<<21;
+ exp = (ieeep->h>>20) & ((1L<<11)-1L);
+ exp -= (1L<<10) - 2L;
+ return ldexp(fr, exp);
+}
--- /dev/null
+++ b/utils/0l/optab.c
@@ -1,0 +1,221 @@
+#include "l.h"
+
+#define X 99
+
+Optab optab[] =
+{
+ { ATEXT, C_LEXT, C_NONE, C_LCON, 0, 0, 0 },
+ { ATEXT, C_LEXT, C_REG, C_LCON, 0, 0, 0 },
+
+ { AMOVW, C_REG, C_NONE, C_REG, 1, 4, 0 },
+ { AMOVV, C_REG, C_NONE, C_REG, 1, 4, 0 },
+ { AMOVB, C_REG, C_NONE, C_REG, 12, 8, 0 },
+ { AMOVBU, C_REG, C_NONE, C_REG, 13, 4, 0 },
+ { AMOVWU, C_REG, C_NONE, C_REG, 14, 8, 0 },
+
+ { ASUB, C_REG, C_REG, C_REG, 2, 4, 0 },
+ { AADD, C_REG, C_REG, C_REG, 2, 4, 0 },
+ { AAND, C_REG, C_REG, C_REG, 2, 4, 0 },
+ { ASUB, C_REG, C_NONE, C_REG, 2, 4, 0 },
+ { AADD, C_REG, C_NONE, C_REG, 2, 4, 0 },
+ { AAND, C_REG, C_NONE, C_REG, 2, 4, 0 },
+
+ { ASLL, C_REG, C_NONE, C_REG, 9, 4, 0 },
+ { ASLL, C_REG, C_REG, C_REG, 9, 4, 0 },
+
+ { AADDF, C_FREG, C_NONE, C_FREG, 32, 4, 0 },
+ { AADDF, C_FREG, C_REG, C_FREG, 32, 4, 0 },
+ { ACMPEQF, C_FREG, C_REG, C_NONE, 32, 4, 0 },
+ { AABSF, C_FREG, C_NONE, C_FREG, 33, 4, 0 },
+ { AMOVF, C_FREG, C_NONE, C_FREG, 33, 4, 0 },
+ { AMOVD, C_FREG, C_NONE, C_FREG, 33, 4, 0 },
+
+ { AMOVW, C_REG, C_NONE, C_SEXT, 7, 4, REGSB },
+ { AMOVV, C_REG, C_NONE, C_SEXT, 7, 4, REGSB },
+ { AMOVB, C_REG, C_NONE, C_SEXT, 7, 4, REGSB },
+ { AMOVBU, C_REG, C_NONE, C_SEXT, 7, 4, REGSB },
+ { AMOVWL, C_REG, C_NONE, C_SEXT, 7, 4, REGSB },
+ { AMOVW, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP },
+ { AMOVV, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP },
+ { AMOVB, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP },
+ { AMOVBU, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP },
+ { AMOVWL, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP },
+ { AMOVW, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO },
+ { AMOVV, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO },
+ { AMOVB, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO },
+ { AMOVBU, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO },
+ { AMOVWL, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO },
+
+ { AMOVW, C_SEXT, C_NONE, C_REG, 8, 4, REGSB },
+ { AMOVV, C_SEXT, C_NONE, C_REG, 8, 4, REGSB },
+ { AMOVB, C_SEXT, C_NONE, C_REG, 8, 4, REGSB },
+ { AMOVBU, C_SEXT, C_NONE, C_REG, 8, 4, REGSB },
+ { AMOVWL, C_SEXT, C_NONE, C_REG, 8, 4, REGSB },
+ { AMOVW, C_SAUTO,C_NONE, C_REG, 8, 4, REGSP },
+ { AMOVV, C_SAUTO,C_NONE, C_REG, 8, 4, REGSP },
+ { AMOVB, C_SAUTO,C_NONE, C_REG, 8, 4, REGSP },
+ { AMOVBU, C_SAUTO,C_NONE, C_REG, 8, 4, REGSP },
+ { AMOVWL, C_SAUTO,C_NONE, C_REG, 8, 4, REGSP },
+ { AMOVW, C_SOREG,C_NONE, C_REG, 8, 4, REGZERO },
+ { AMOVV, C_SOREG,C_NONE, C_REG, 8, 4, REGZERO },
+ { AMOVB, C_SOREG,C_NONE, C_REG, 8, 4, REGZERO },
+ { AMOVBU, C_SOREG,C_NONE, C_REG, 8, 4, REGZERO },
+ { AMOVWL, C_SOREG,C_NONE, C_REG, 8, 4, REGZERO },
+
+ { AMOVW, C_REG, C_NONE, C_LEXT, 35, 16, REGSB },
+ { AMOVV, C_REG, C_NONE, C_LEXT, 35, 16, REGSB },
+ { AMOVB, C_REG, C_NONE, C_LEXT, 35, 16, REGSB },
+ { AMOVBU, C_REG, C_NONE, C_LEXT, 35, 16, REGSB },
+ { AMOVW, C_REG, C_NONE, C_LAUTO, 35, 16, REGSP },
+ { AMOVV, C_REG, C_NONE, C_LAUTO, 35, 16, REGSP },
+ { AMOVB, C_REG, C_NONE, C_LAUTO, 35, 16, REGSP },
+ { AMOVBU, C_REG, C_NONE, C_LAUTO, 35, 16, REGSP },
+ { AMOVW, C_REG, C_NONE, C_LOREG, 35, 16, REGZERO },
+ { AMOVV, C_REG, C_NONE, C_LOREG, 35, 16, REGZERO },
+ { AMOVB, C_REG, C_NONE, C_LOREG, 35, 16, REGZERO },
+ { AMOVBU, C_REG, C_NONE, C_LOREG, 35, 16, REGZERO },
+
+ { AMOVW, C_LEXT, C_NONE, C_REG, 36, 16, REGSB },
+ { AMOVV, C_LEXT, C_NONE, C_REG, 36, 16, REGSB },
+ { AMOVB, C_LEXT, C_NONE, C_REG, 36, 16, REGSB },
+ { AMOVBU, C_LEXT, C_NONE, C_REG, 36, 16, REGSB },
+ { AMOVW, C_LAUTO,C_NONE, C_REG, 36, 16, REGSP },
+ { AMOVV, C_LAUTO,C_NONE, C_REG, 36, 16, REGSP },
+ { AMOVB, C_LAUTO,C_NONE, C_REG, 36, 16, REGSP },
+ { AMOVBU, C_LAUTO,C_NONE, C_REG, 36, 16, REGSP },
+ { AMOVW, C_LOREG,C_NONE, C_REG, 36, 16, REGZERO },
+ { AMOVV, C_LOREG,C_NONE, C_REG, 36, 16, REGZERO },
+ { AMOVB, C_LOREG,C_NONE, C_REG, 36, 16, REGZERO },
+ { AMOVBU, C_LOREG,C_NONE, C_REG, 36, 16, REGZERO },
+
+ { AMOVW, C_SECON,C_NONE, C_REG, 3, 4, REGSB },
+ { AMOVW, C_SACON,C_NONE, C_REG, 3, 4, REGSP },
+ { AMOVW, C_LECON,C_NONE, C_REG, 26, 12, REGSB },
+ { AMOVW, C_LACON,C_NONE, C_REG, 26, 12, REGSP },
+ { AMOVW, C_ADDCON,C_NONE,C_REG, 3, 4, REGZERO },
+ { AMOVV, C_ADDCON,C_NONE,C_REG, 3, 4, REGZERO },
+ { AMOVW, C_ANDCON,C_NONE,C_REG, 3, 4, REGZERO },
+ { AMOVV, C_ANDCON,C_NONE,C_REG, 3, 4, REGZERO },
+
+ { AMOVW, C_UCON, C_NONE, C_REG, 24, 4, 0 },
+ { AMOVV, C_UCON, C_NONE, C_REG, 24, 4, 0 },
+ { AMOVW, C_LCON, C_NONE, C_REG, 19, 8, 0 },
+ { AMOVV, C_LCON, C_NONE, C_REG, 19, 8, 0 },
+
+ { AMOVW, C_HI, C_NONE, C_REG, 20, 4, 0 },
+ { AMOVV, C_HI, C_NONE, C_REG, 20, 4, 0 },
+ { AMOVW, C_LO, C_NONE, C_REG, 20, 4, 0 },
+ { AMOVV, C_LO, C_NONE, C_REG, 20, 4, 0 },
+ { AMOVW, C_REG, C_NONE, C_HI, 21, 4, 0 },
+ { AMOVV, C_REG, C_NONE, C_HI, 21, 4, 0 },
+ { AMOVW, C_REG, C_NONE, C_LO, 21, 4, 0 },
+ { AMOVV, C_REG, C_NONE, C_LO, 21, 4, 0 },
+
+ { AMUL, C_REG, C_REG, C_NONE, 22, 4, 0 },
+
+ { AADD, C_ADD0CON,C_REG,C_REG, 4, 4, 0 },
+ { AADD, C_ADD0CON,C_NONE,C_REG, 4, 4, 0 },
+ { AADD, C_ANDCON,C_REG, C_REG, 10, 8, 0 },
+ { AADD, C_ANDCON,C_NONE,C_REG, 10, 8, 0 },
+
+ { AAND, C_AND0CON,C_REG,C_REG, 4, 4, 0 },
+ { AAND, C_AND0CON,C_NONE,C_REG, 4, 4, 0 },
+ { AAND, C_ADDCON,C_REG, C_REG, 10, 8, 0 },
+ { AAND, C_ADDCON,C_NONE,C_REG, 10, 8, 0 },
+
+ { AADD, C_UCON, C_REG, C_REG, 25, 8, 0 },
+ { AADD, C_UCON, C_NONE, C_REG, 25, 8, 0 },
+ { AAND, C_UCON, C_REG, C_REG, 25, 8, 0 },
+ { AAND, C_UCON, C_NONE, C_REG, 25, 8, 0 },
+
+ { AADD, C_LCON, C_NONE, C_REG, 23, 12, 0 },
+ { AAND, C_LCON, C_NONE, C_REG, 23, 12, 0 },
+ { AADD, C_LCON, C_REG, C_REG, 23, 12, 0 },
+ { AAND, C_LCON, C_REG, C_REG, 23, 12, 0 },
+
+ { ASLL, C_SCON, C_REG, C_REG, 16, 4, 0 },
+ { ASLL, C_SCON, C_NONE, C_REG, 16, 4, 0 },
+
+ { ASYSCALL, C_NONE, C_NONE, C_NONE, 5, 4, 0 },
+
+ { ABEQ, C_REG, C_REG, C_SBRA, 6, 4, 0 },
+ { ABEQ, C_REG, C_NONE, C_SBRA, 6, 4, 0 },
+ { ABLEZ, C_REG, C_NONE, C_SBRA, 6, 4, 0 },
+ { ABFPT, C_NONE, C_NONE, C_SBRA, 6, 4, 0 },
+
+ { AJMP, C_NONE, C_NONE, C_LBRA, 11, 4, 0 },
+ { AJAL, C_NONE, C_NONE, C_LBRA, 11, 4, 0 },
+
+ { AJMP, C_NONE, C_NONE, C_ZOREG, 18, 4, REGZERO },
+ { AJAL, C_NONE, C_NONE, C_ZOREG, 18, 4, REGLINK },
+
+ { AMOVW, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB },
+ { AMOVF, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB },
+ { AMOVD, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB },
+ { AMOVW, C_SAUTO,C_NONE, C_FREG, 27, 4, REGSP },
+ { AMOVF, C_SAUTO,C_NONE, C_FREG, 27, 4, REGSP },
+ { AMOVD, C_SAUTO,C_NONE, C_FREG, 27, 4, REGSP },
+ { AMOVW, C_SOREG,C_NONE, C_FREG, 27, 4, REGZERO },
+ { AMOVF, C_SOREG,C_NONE, C_FREG, 27, 4, REGZERO },
+ { AMOVD, C_SOREG,C_NONE, C_FREG, 27, 4, REGZERO },
+
+ { AMOVW, C_LEXT, C_NONE, C_FREG, 27, 16, REGSB },
+ { AMOVF, C_LEXT, C_NONE, C_FREG, 27, 16, REGSB },
+ { AMOVD, C_LEXT, C_NONE, C_FREG, 27, 16, REGSB },
+ { AMOVW, C_LAUTO,C_NONE, C_FREG, 27, 16, REGSP },
+ { AMOVF, C_LAUTO,C_NONE, C_FREG, 27, 16, REGSP },
+ { AMOVD, C_LAUTO,C_NONE, C_FREG, 27, 16, REGSP },
+ { AMOVW, C_LOREG,C_NONE, C_FREG, 27, 16, REGZERO },
+ { AMOVF, C_LOREG,C_NONE, C_FREG, 27, 16, REGZERO },
+ { AMOVD, C_LOREG,C_NONE, C_FREG, 27, 16, REGZERO },
+
+ { AMOVW, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB },
+ { AMOVF, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB },
+ { AMOVD, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB },
+ { AMOVW, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP },
+ { AMOVF, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP },
+ { AMOVD, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP },
+ { AMOVW, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO },
+ { AMOVF, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO },
+ { AMOVD, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO },
+
+ { AMOVW, C_FREG, C_NONE, C_LEXT, 28, 16, REGSB },
+ { AMOVF, C_FREG, C_NONE, C_LEXT, 28, 16, REGSB },
+ { AMOVD, C_FREG, C_NONE, C_LEXT, 28, 16, REGSB },
+ { AMOVW, C_FREG, C_NONE, C_LAUTO, 28, 16, REGSP },
+ { AMOVF, C_FREG, C_NONE, C_LAUTO, 28, 16, REGSP },
+ { AMOVD, C_FREG, C_NONE, C_LAUTO, 28, 16, REGSP },
+ { AMOVW, C_FREG, C_NONE, C_LOREG, 28, 16, REGZERO },
+ { AMOVF, C_FREG, C_NONE, C_LOREG, 28, 16, REGZERO },
+ { AMOVD, C_FREG, C_NONE, C_LOREG, 28, 16, REGZERO },
+
+ { AMOVW, C_REG, C_NONE, C_FREG, 30, 4, 0 },
+ { AMOVW, C_FREG, C_NONE, C_REG, 31, 4, 0 },
+ { AMOVV, C_REG, C_NONE, C_FREG, 47, 4, 0 },
+ { AMOVV, C_FREG, C_NONE, C_REG, 48, 4, 0 },
+
+ { AMOVW, C_ADDCON,C_NONE,C_FREG, 34, 8, 0 },
+ { AMOVW, C_ANDCON,C_NONE,C_FREG, 34, 8, 0 },
+ { AMOVW, C_UCON, C_NONE, C_FREG, 35, 8, 0 },
+ { AMOVW, C_LCON, C_NONE, C_FREG, 36, 12, 0 },
+
+ { AMOVW, C_REG, C_NONE, C_MREG, 37, 4, 0 },
+ { AMOVV, C_REG, C_NONE, C_MREG, 37, 4, 0 },
+ { AMOVW, C_MREG, C_NONE, C_REG, 38, 4, 0 },
+ { AMOVV, C_MREG, C_NONE, C_REG, 38, 4, 0 },
+
+ { ARFE, C_NONE, C_NONE, C_ZOREG, 39, 8, 0 },
+ { AWORD, C_NONE, C_NONE, C_LCON, 40, 4, 0 },
+
+ { AMOVW, C_REG, C_NONE, C_FCREG, 41, 8, 0 },
+ { AMOVV, C_REG, C_NONE, C_FCREG, 41, 8, 0 },
+ { AMOVW, C_FCREG,C_NONE, C_REG, 42, 4, 0 },
+ { AMOVV, C_FCREG,C_NONE, C_REG, 42, 4, 0 },
+
+ { ABREAK, C_REG, C_NONE, C_SEXT, 7, 4, REGSB }, /* really CACHE instruction */
+ { ABREAK, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP },
+ { ABREAK, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO },
+ { ABREAK, C_NONE, C_NONE, C_NONE, 5, 4, 0 },
+
+ { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 },
+};
--- /dev/null
+++ b/utils/0l/pass.c
@@ -1,0 +1,498 @@
+#include "l.h"
+
+void
+dodata(void)
+{
+ int i, t;
+ Sym *s;
+ Prog *p, *p1;
+ long orig, orig1, v;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f dodata\n", cputime());
+ Bflush(&bso);
+ for(p = datap; p != P; p = p->link) {
+ s = p->from.sym;
+ if(p->as == ADYNT || p->as == AINIT)
+ s->value = dtype;
+ if(s->type == SBSS)
+ s->type = SDATA;
+ if(s->type != SDATA)
+ diag("initialize non-data (%d): %s\n%P\n",
+ s->type, s->name, p);
+ v = p->from.offset + p->reg;
+ if(v > s->value)
+ diag("initialize bounds (%ld): %s\n%P\n",
+ s->value, s->name, p);
+ }
+
+ /*
+ * pass 1
+ * assign 'small' variables to data segment
+ * (rational is that data segment is more easily
+ * addressed through offset on R30)
+ */
+ orig = 0;
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ t = s->type;
+ if(t != SDATA && t != SBSS)
+ continue;
+ v = s->value;
+ if(v == 0) {
+ diag("%s: no size\n", s->name);
+ v = 1;
+ }
+ while(v & 7)
+ v++;
+ s->value = v;
+ if(v > MINSIZ)
+ continue;
+ s->value = orig;
+ orig += v;
+ s->type = SDATA1;
+ }
+ orig1 = orig;
+
+ /*
+ * pass 2
+ * assign 'data' variables to data segment
+ */
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ t = s->type;
+ if(t != SDATA) {
+ if(t == SDATA1)
+ s->type = SDATA;
+ continue;
+ }
+ v = s->value;
+ while(v & 7)
+ v++;
+ s->value = orig;
+ orig += v;
+ s->type = SDATA1;
+ }
+
+ while(orig & 7)
+ orig++;
+ datsize = orig;
+
+ /*
+ * pass 3
+ * everything else to bss segment
+ */
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ if(s->type != SBSS)
+ continue;
+ v = s->value;
+ while(v & 7)
+ v++;
+ s->value = orig;
+ orig += v;
+ }
+ while(orig & 7)
+ orig++;
+ bsssize = orig-datsize;
+
+ /*
+ * pass 4
+ * add literals to all large values.
+ * at this time:
+ * small data is allocated DATA
+ * large data is allocated DATA1
+ * large bss is allocated BSS
+ * the new literals are loaded between
+ * small data and large data.
+ */
+ orig = 0;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as != AMOVW)
+ continue;
+ if(p->from.type != D_CONST)
+ continue;
+ if(s = p->from.sym) {
+ t = s->type;
+ if(t != SDATA && t != SDATA1 && t != SBSS)
+ continue;
+ t = p->from.name;
+ if(t != D_EXTERN && t != D_STATIC)
+ continue;
+ v = s->value + p->from.offset;
+ if(v >= 0 && v <= 0xffff)
+ continue;
+ if(!strcmp(s->name, "setR30"))
+ continue;
+ /* size should be 19 max */
+ if(strlen(s->name) >= 10) /* has loader address */
+ sprint(literal, "$%lux.%lux", (long)s, p->from.offset);
+ else
+ sprint(literal, "$%s.%d.%lux", s->name, s->version, p->from.offset);
+ } else {
+ if(p->from.name != D_NONE)
+ continue;
+ if(p->from.reg != NREG)
+ continue;
+ v = p->from.offset;
+ if(v >= -0x7fff && v <= 0xffff)
+ continue;
+ if(!(v & 0xffff))
+ continue;
+ /* size should be 9 max */
+ sprint(literal, "$%lux", v);
+ }
+ s = lookup(literal, 0);
+ if(s->type == 0) {
+ s->type = SDATA;
+ s->value = orig1+orig;
+ orig += 8;
+ p1 = prg();
+ p1->line = p->line;
+ p1->as = ADATA;
+ p1->from.type = D_OREG;
+ p1->from.sym = s;
+ p1->from.name = D_EXTERN;
+ p1->reg = 4;
+ p1->to = p->from;
+ p1->link = datap;
+ datap = p1;
+ }
+ if(s->type != SDATA)
+ diag("literal not data: %s", s->name);
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ p->from.name = D_EXTERN;
+ p->from.offset = 0;
+ nocache(p);
+ continue;
+ }
+ while(orig & 7)
+ orig++;
+ /*
+ * pass 5
+ * re-adjust offsets
+ */
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ t = s->type;
+ if(t == SBSS) {
+ s->value += orig;
+ continue;
+ }
+ if(t == SDATA1) {
+ s->type = SDATA;
+ s->value += orig;
+ continue;
+ }
+ }
+ datsize += orig;
+ xdefine("setR30", SDATA, 0L+BIG);
+ xdefine("bdata", SDATA, 0L);
+ xdefine("edata", SDATA, datsize);
+ xdefine("end", SBSS, datsize+bsssize);
+ xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+ int i;
+ Sym *s;
+
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link)
+ if(s->type == SXREF)
+ diag("%s: not defined\n", s->name);
+}
+
+void
+follow(void)
+{
+ if(debug['v'])
+ Bprint(&bso, "%5.2f follow\n", cputime());
+ Bflush(&bso);
+
+ firstp = prg();
+ lastp = firstp;
+ xfol(textp);
+
+ firstp = firstp->link;
+ lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+ Prog *q, *r;
+ int a, i;
+
+loop:
+ if(p == P)
+ return;
+ a = p->as;
+ if(a == ATEXT)
+ curtext = p;
+ if(a == AJMP) {
+ q = p->cond;
+ if((p->mark&NOSCHED) || q && (q->mark&NOSCHED)){
+ p->mark |= FOLL;
+ lastp->link = p;
+ lastp = p;
+ p = p->link;
+ xfol(p);
+ p = q;
+ if(p && !(p->mark & FOLL))
+ goto loop;
+ return;
+ }
+ if(q != P) {
+ p->mark |= FOLL;
+ p = q;
+ if(!(p->mark & FOLL))
+ goto loop;
+ }
+ }
+ if(p->mark & FOLL) {
+ for(i=0,q=p; i<4; i++,q=q->link) {
+ if(q == lastp || (q->mark&NOSCHED))
+ break;
+ a = q->as;
+ if(a == ANOP) {
+ i--;
+ continue;
+ }
+ if(a == AJMP || a == ARET || a == ARFE)
+ goto copy;
+ if(!q->cond || (q->cond->mark&FOLL))
+ continue;
+ if(a != ABEQ && a != ABNE)
+ continue;
+ copy:
+ for(;;) {
+ r = prg();
+ *r = *p;
+ if(!(r->mark&FOLL))
+ print("cant happen 1\n");
+ r->mark |= FOLL;
+ if(p != q) {
+ p = p->link;
+ lastp->link = r;
+ lastp = r;
+ continue;
+ }
+ lastp->link = r;
+ lastp = r;
+ if(a == AJMP || a == ARET || a == ARFE)
+ return;
+ r->as = ABNE;
+ if(a == ABNE)
+ r->as = ABEQ;
+ r->cond = p->link;
+ r->link = p->cond;
+ if(!(r->link->mark&FOLL))
+ xfol(r->link);
+ if(!(r->cond->mark&FOLL))
+ print("cant happen 2\n");
+ return;
+ }
+ }
+ a = AJMP;
+ q = prg();
+ q->as = a;
+ q->line = p->line;
+ q->to.type = D_BRANCH;
+ q->to.offset = p->pc;
+ q->cond = p;
+ p = q;
+ }
+ p->mark |= FOLL;
+ lastp->link = p;
+ lastp = p;
+ if(a == AJMP || a == ARET || a == ARFE){
+ if(p->mark & NOSCHED){
+ p = p->link;
+ goto loop;
+ }
+ return;
+ }
+ if(p->cond != P)
+ if(a != AJAL && p->link != P) {
+ xfol(p->link);
+ p = p->cond;
+ if(p == P || (p->mark&FOLL))
+ return;
+ goto loop;
+ }
+ p = p->link;
+ goto loop;
+}
+
+void
+patch(void)
+{
+ long c, vexit;
+ Prog *p, *q;
+ Sym *s;
+ int a;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f patch\n", cputime());
+ Bflush(&bso);
+ mkfwd();
+ s = lookup("exit", 0);
+ vexit = s->value;
+ for(p = firstp; p != P; p = p->link) {
+ a = p->as;
+ if(a == ATEXT)
+ curtext = p;
+ if((a == AJAL || a == AJMP || a == ARET) &&
+ p->to.type != D_BRANCH && p->to.sym != S) {
+ s = p->to.sym;
+ if(s->type != STEXT) {
+ diag("undefined: %s\n%P\n", s->name, p);
+ s->type = STEXT;
+ s->value = vexit;
+ }
+ p->to.offset = s->value;
+ p->to.type = D_BRANCH;
+ }
+ if(p->to.type != D_BRANCH)
+ continue;
+ c = p->to.offset;
+ for(q = firstp; q != P;) {
+ if(q->forwd != P)
+ if(c >= q->forwd->pc) {
+ q = q->forwd;
+ continue;
+ }
+ if(c == q->pc)
+ break;
+ q = q->link;
+ }
+ if(q == P) {
+ diag("branch out of range %ld\n%P\n", c, p);
+ p->to.type = D_NONE;
+ }
+ p->cond = q;
+ }
+
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT)
+ curtext = p;
+ if(p->cond != P) {
+ p->cond = brloop(p->cond);
+ if(p->cond != P)
+ if(p->to.type == D_BRANCH)
+ p->to.offset = p->cond->pc;
+ }
+ }
+}
+
+#define LOG 5
+void
+mkfwd(void)
+{
+ Prog *p;
+ long dwn[LOG], cnt[LOG], i;
+ Prog *lst[LOG];
+
+ for(i=0; i<LOG; i++) {
+ if(i == 0)
+ cnt[i] = 1; else
+ cnt[i] = LOG * cnt[i-1];
+ dwn[i] = 1;
+ lst[i] = P;
+ }
+ i = 0;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT)
+ curtext = p;
+ i--;
+ if(i < 0)
+ i = LOG-1;
+ p->forwd = P;
+ dwn[i]--;
+ if(dwn[i] <= 0) {
+ dwn[i] = cnt[i];
+ if(lst[i] != P)
+ lst[i]->forwd = p;
+ lst[i] = p;
+ }
+ }
+}
+
+Prog*
+brloop(Prog *p)
+{
+ Prog *q;
+ int c;
+
+ for(c=0; p!=P;) {
+ if(p->as != AJMP || (p->mark&NOSCHED))
+ return p;
+ q = p->cond;
+ if(q <= p) {
+ c++;
+ if(q == p || c > 5000)
+ break;
+ }
+ p = q;
+ }
+ return P;
+}
+
+long
+atolwhex(char *s)
+{
+ long n;
+ int f;
+
+ n = 0;
+ f = 0;
+ while(*s == ' ' || *s == '\t')
+ s++;
+ if(*s == '-' || *s == '+') {
+ if(*s++ == '-')
+ f = 1;
+ while(*s == ' ' || *s == '\t')
+ s++;
+ }
+ if(s[0]=='0' && s[1]){
+ if(s[1]=='x' || s[1]=='X'){
+ s += 2;
+ for(;;){
+ if(*s >= '0' && *s <= '9')
+ n = n*16 + *s++ - '0';
+ else if(*s >= 'a' && *s <= 'f')
+ n = n*16 + *s++ - 'a' + 10;
+ else if(*s >= 'A' && *s <= 'F')
+ n = n*16 + *s++ - 'A' + 10;
+ else
+ break;
+ }
+ } else
+ while(*s >= '0' && *s <= '7')
+ n = n*8 + *s++ - '0';
+ } else
+ while(*s >= '0' && *s <= '9')
+ n = n*10 + *s++ - '0';
+ if(f)
+ n = -n;
+ return n;
+}
+
+long
+rnd(long v, long r)
+{
+ long c;
+
+ if(r <= 0)
+ return v;
+ v += r - 1;
+ c = v % r;
+ if(c < 0)
+ c += r;
+ v -= c;
+ return v;
+}
--- /dev/null
+++ b/utils/0l/sched.c
@@ -1,0 +1,709 @@
+#include "l.h"
+
+enum
+{
+ E_HILO = 1<<0,
+ E_FCR = 1<<1,
+ E_MCR = 1<<2,
+ E_MEM = 1<<3,
+ E_MEMSP = 1<<4, /* uses offset and size */
+ E_MEMSB = 1<<5, /* uses offset and size */
+ ANYMEM = E_MEM|E_MEMSP|E_MEMSB,
+ DELAY = BRANCH|LOAD|FCMP,
+};
+
+typedef struct Sch Sch;
+typedef struct Dep Dep;
+
+struct Dep
+{
+ ulong ireg;
+ ulong freg;
+ ulong cc;
+};
+struct Sch
+{
+ Prog p;
+ Dep set;
+ Dep used;
+ long soffset;
+ char size;
+ char nop;
+ char comp;
+};
+
+void markregused(Sch*, Prog*);
+int depend(Sch*, Sch*);
+int conflict(Sch*, Sch*);
+int offoverlap(Sch*, Sch*);
+void dumpbits(Sch*, Dep*);
+
+void
+sched(Prog *p0, Prog *pe)
+{
+ Prog *p, *q;
+ Sch sch[NSCHED], *s, *t, *u, *se, stmp;
+
+ /*
+ * build side structure
+ */
+ s = sch;
+ for(p=p0;; p=p->link) {
+ memset(s, 0, sizeof(*s));
+ s->p = *p;
+ markregused(s, p);
+ if(debug['X']) {
+ Bprint(&bso, "%P\t\tset", &s->p);
+ dumpbits(s, &s->set);
+ Bprint(&bso, "; used");
+ dumpbits(s, &s->used);
+ if(s->comp)
+ Bprint(&bso, "; compound");
+ if(s->p.mark & LOAD)
+ Bprint(&bso, "; load");
+ if(s->p.mark & BRANCH)
+ Bprint(&bso, "; branch");
+ if(s->p.mark & FCMP)
+ Bprint(&bso, "; fcmp");
+ Bprint(&bso, "\n");
+ }
+ if(p == pe)
+ break;
+ s++;
+ }
+ se = s;
+
+ /*
+ * prepass to move things around
+ * does nothing, but tries to make
+ * the actual scheduler work better
+ */
+ for(s=sch; s<=se; s++) {
+ if(!(s->p.mark & LOAD))
+ continue;
+ /* always good to put nonconflict loads together */
+ for(t=s+1; t<=se; t++) {
+ if(!(t->p.mark & LOAD))
+ continue;
+ if(t->p.mark & BRANCH)
+ break;
+ if(conflict(s, t))
+ break;
+ for(u=t-1; u>s; u--)
+ if(depend(u, t))
+ goto no11;
+ u = s+1;
+ stmp = *t;
+ memmove(s+2, u, (uchar*)t - (uchar*)u);
+ *u = stmp;
+ break;
+ }
+ no11:
+
+ /* put schedule fodder above load */
+ for(t=s+1; t<=se; t++) {
+ if(t->p.mark & BRANCH)
+ break;
+ if(s > sch && conflict(s-1, t))
+ continue;
+ for(u=t-1; u>=s; u--)
+ if(depend(t, u))
+ goto no1;
+ stmp = *t;
+ memmove(s+1, s, (uchar*)t - (uchar*)s);
+ *s = stmp;
+ if(!(s->p.mark & LOAD))
+ break;
+ no1:;
+ }
+ }
+
+ for(s=se; s>=sch; s--) {
+ if(!(s->p.mark & DELAY))
+ continue;
+ if(s < se)
+ if(!conflict(s, s+1))
+ goto out3;
+ /*
+ * s is load, s+1 is immediate use of result or end of block
+ * t is the trial instruction to insert between s and s+1
+ */
+ if(!debug['Y'])
+ for(t=s-1; t>=sch; t--) {
+ if(t->comp)
+ if(s->p.mark & BRANCH)
+ goto no2;
+ if(t->p.mark & DELAY)
+ if(s >= se || conflict(t, s+1))
+ goto no2;
+ for(u=t+1; u<=s; u++)
+ if(depend(u, t))
+ goto no2;
+ goto out2;
+ no2:;
+ }
+ if(debug['X'])
+ Bprint(&bso, "?l%P\n", &s->p);
+ if(s->p.mark & BRANCH)
+ s->nop = 1;
+ if(debug['v']) {
+ if(s->p.mark & LOAD) {
+ nop.load.count++;
+ nop.load.outof++;
+ }
+ if(s->p.mark & BRANCH) {
+ nop.branch.count++;
+ nop.branch.outof++;
+ }
+ if(s->p.mark & FCMP) {
+ nop.fcmp.count++;
+ nop.fcmp.outof++;
+ }
+ }
+ continue;
+
+ out2:
+ if(debug['X']) {
+ Bprint(&bso, "!l%P\n", &t->p);
+ Bprint(&bso, "%P\n", &s->p);
+ }
+ stmp = *t;
+ memmove(t, t+1, (uchar*)s - (uchar*)t);
+ *s = stmp;
+ s--;
+
+ out3:
+ if(debug['v']) {
+ if(s->p.mark & LOAD)
+ nop.load.outof++;
+ if(s->p.mark & BRANCH)
+ nop.branch.outof++;
+ if(s->p.mark & FCMP)
+ nop.fcmp.outof++;
+ }
+ }
+
+ /* Avoid HI/LO use->set */
+ t = sch+1;
+ for(s=sch; s<se-1; s++, t++) {
+ if((s->used.cc & E_HILO) == 0)
+ continue;
+ if(t->set.cc & E_HILO)
+ s->nop = 2;
+ }
+
+ /*
+ * put it all back
+ */
+ for(s=sch, p=p0; s<=se; s++, p=q) {
+ q = p->link;
+ if(q != s->p.link) {
+ *p = s->p;
+ p->link = q;
+ }
+ while(s->nop--)
+ addnop(p);
+ }
+ if(debug['X']) {
+ Bprint(&bso, "\n");
+ Bflush(&bso);
+ }
+}
+
+void
+markregused(Sch *s, Prog *realp)
+{
+ int c, ar, ad, ld, sz;
+ ulong m;
+ Prog *p;
+
+ p = &s->p;
+ s->comp = compound(p);
+ s->nop = 0;
+ if(s->comp) {
+ s->set.ireg |= 1<<REGTMP;
+ s->used.ireg |= 1<<REGTMP;
+ }
+
+ ar = 0; /* dest is really reference */
+ ad = 0; /* source/dest is really address */
+ ld = 0; /* opcode is load instruction */
+ sz = 20; /* size of load/store for overlap computation */
+
+/*
+ * flags based on opcode
+ */
+ switch(p->as) {
+ case ATEXT:
+ curtext = realp;
+ autosize = p->to.offset + 8;
+ ad = 1;
+ break;
+ case AJAL:
+ c = p->reg;
+ if(c == NREG)
+ c = REGLINK;
+ s->set.ireg |= 1<<c;
+ ar = 1;
+ ad = 1;
+ break;
+ case ABGEZAL:
+ case ABLTZAL:
+ s->set.ireg |= 1<<REGLINK;
+ case ABEQ:
+ case ABGEZ:
+ case ABGTZ:
+ case ABLEZ:
+ case ABLTZ:
+ case ABNE:
+ ar = 1;
+ ad = 1;
+ break;
+ case ABFPT:
+ case ABFPF:
+ ad = 1;
+ s->used.cc |= E_FCR;
+ break;
+ case ACMPEQD:
+ case ACMPEQF:
+ case ACMPGED:
+ case ACMPGEF:
+ case ACMPGTD:
+ case ACMPGTF:
+ ar = 1;
+ s->set.cc |= E_FCR;
+ p->mark |= FCMP;
+ break;
+ case AJMP:
+ ar = 1;
+ ad = 1;
+ break;
+ case AMOVB:
+ case AMOVBU:
+ sz = 1;
+ ld = 1;
+ break;
+ case AMOVH:
+ case AMOVHU:
+ sz = 2;
+ ld = 1;
+ break;
+ case AMOVF:
+ case AMOVW:
+ case AMOVWL:
+ case AMOVWR:
+ sz = 4;
+ ld = 1;
+ break;
+ case AMOVD:
+ case AMOVV:
+ case AMOVVL:
+ case AMOVVR:
+ sz = 8;
+ ld = 1;
+ break;
+ case ADIV:
+ case ADIVU:
+ case AMUL:
+ case AMULU:
+ case AREM:
+ case AREMU:
+ case ADIVV:
+ case ADIVVU:
+ case AMULV:
+ case AMULVU:
+ case AREMV:
+ case AREMVU:
+ s->set.cc = E_HILO;
+ case AADD:
+ case AADDU:
+ case AADDV:
+ case AADDVU:
+ case AAND:
+ case ANOR:
+ case AOR:
+ case ASGT:
+ case ASGTU:
+ case ASLL:
+ case ASRA:
+ case ASRL:
+ case ASLLV:
+ case ASRAV:
+ case ASRLV:
+ case ASUB:
+ case ASUBU:
+ case ASUBV:
+ case ASUBVU:
+ case AXOR:
+
+ case AADDD:
+ case AADDF:
+ case AADDW:
+ case ASUBD:
+ case ASUBF:
+ case ASUBW:
+ case AMULF:
+ case AMULD:
+ case AMULW:
+ case ADIVF:
+ case ADIVD:
+ case ADIVW:
+ if(p->reg == NREG) {
+ if(p->to.type == D_REG || p->to.type == D_FREG)
+ p->reg = p->to.reg;
+ if(p->reg == NREG)
+ print("botch %P\n", p);
+ }
+ break;
+ }
+
+/*
+ * flags based on 'to' field
+ */
+ c = p->to.class;
+ if(c == 0) {
+ c = aclass(&p->to) + 1;
+ p->to.class = c;
+ }
+ c--;
+ switch(c) {
+ default:
+ print("unknown class %d %D\n", c, &p->to);
+
+ case C_ZCON:
+ case C_SCON:
+ case C_ADD0CON:
+ case C_AND0CON:
+ case C_ADDCON:
+ case C_ANDCON:
+ case C_UCON:
+ case C_LCON:
+ case C_NONE:
+ case C_SBRA:
+ case C_LBRA:
+ break;
+
+ case C_HI:
+ case C_LO:
+ s->set.cc |= E_HILO;
+ break;
+ case C_FCREG:
+ s->set.cc |= E_FCR;
+ break;
+ case C_MREG:
+ s->set.cc |= E_MCR;
+ break;
+ case C_ZOREG:
+ case C_SOREG:
+ case C_LOREG:
+ c = p->to.reg;
+ s->used.ireg |= 1<<c;
+ if(ad)
+ break;
+ s->size = sz;
+ s->soffset = regoff(&p->to);
+
+ m = ANYMEM;
+ if(c == REGSB)
+ m = E_MEMSB;
+ if(c == REGSP)
+ m = E_MEMSP;
+
+ if(ar)
+ s->used.cc |= m;
+ else
+ s->set.cc |= m;
+ break;
+ case C_SACON:
+ case C_LACON:
+ s->used.ireg |= 1<<REGSP;
+ break;
+ case C_SECON:
+ case C_LECON:
+ s->used.ireg |= 1<<REGSB;
+ break;
+ case C_REG:
+ if(ar)
+ s->used.ireg |= 1<<p->to.reg;
+ else
+ s->set.ireg |= 1<<p->to.reg;
+ break;
+ case C_FREG:
+ /* do better -- determine double prec */
+ if(ar) {
+ s->used.freg |= 1<<p->to.reg;
+ s->used.freg |= 1<<(p->to.reg|1);
+ } else {
+ s->set.freg |= 1<<p->to.reg;
+ s->set.freg |= 1<<(p->to.reg|1);
+ }
+ if(ld && p->from.type == D_REG)
+ p->mark |= LOAD;
+ break;
+ case C_SAUTO:
+ case C_LAUTO:
+ s->used.ireg |= 1<<REGSP;
+ if(ad)
+ break;
+ s->size = sz;
+ s->soffset = regoff(&p->to);
+
+ if(ar)
+ s->used.cc |= E_MEMSP;
+ else
+ s->set.cc |= E_MEMSP;
+ break;
+ case C_SEXT:
+ case C_LEXT:
+ s->used.ireg |= 1<<REGSB;
+ if(ad)
+ break;
+ s->size = sz;
+ s->soffset = regoff(&p->to);
+
+ if(ar)
+ s->used.cc |= E_MEMSB;
+ else
+ s->set.cc |= E_MEMSB;
+ break;
+ }
+
+/*
+ * flags based on 'from' field
+ */
+ c = p->from.class;
+ if(c == 0) {
+ c = aclass(&p->from) + 1;
+ p->from.class = c;
+ }
+ c--;
+ switch(c) {
+ default:
+ print("unknown class %d %D\n", c, &p->from);
+
+ case C_ZCON:
+ case C_SCON:
+ case C_ADD0CON:
+ case C_AND0CON:
+ case C_ADDCON:
+ case C_ANDCON:
+ case C_UCON:
+ case C_LCON:
+ case C_NONE:
+ case C_SBRA:
+ case C_LBRA:
+ break;
+ case C_HI:
+ case C_LO:
+ s->used.cc |= E_HILO;
+ break;
+ case C_FCREG:
+ s->used.cc |= E_FCR;
+ break;
+ case C_MREG:
+ s->used.cc |= E_MCR;
+ break;
+ case C_ZOREG:
+ case C_SOREG:
+ case C_LOREG:
+ c = p->from.reg;
+ s->used.ireg |= 1<<c;
+ if(ld)
+ p->mark |= LOAD;
+ s->size = sz;
+ s->soffset = regoff(&p->from);
+
+ m = ANYMEM;
+ if(c == REGSB)
+ m = E_MEMSB;
+ if(c == REGSP)
+ m = E_MEMSP;
+
+ s->used.cc |= m;
+ break;
+ case C_SACON:
+ case C_LACON:
+ s->used.ireg |= 1<<REGSP;
+ break;
+ case C_SECON:
+ case C_LECON:
+ s->used.ireg |= 1<<REGSB;
+ break;
+ case C_REG:
+ s->used.ireg |= 1<<p->from.reg;
+ break;
+ case C_FREG:
+ /* do better -- determine double prec */
+ s->used.freg |= 1<<p->from.reg;
+ s->used.freg |= 1<<(p->from.reg|1);
+ if(ld && p->to.type == D_REG)
+ p->mark |= LOAD;
+ break;
+ case C_SAUTO:
+ case C_LAUTO:
+ s->used.ireg |= 1<<REGSP;
+ if(ld)
+ p->mark |= LOAD;
+ if(ad)
+ break;
+ s->size = sz;
+ s->soffset = regoff(&p->from);
+
+ s->used.cc |= E_MEMSP;
+ break;
+ case C_SEXT:
+ case C_LEXT:
+ s->used.ireg |= 1<<REGSB;
+ if(ld)
+ p->mark |= LOAD;
+ if(ad)
+ break;
+ s->size = sz;
+ s->soffset = regoff(&p->from);
+
+ s->used.cc |= E_MEMSB;
+ break;
+ }
+
+ c = p->reg;
+ if(c != NREG) {
+ if(p->from.type == D_FREG || p->to.type == D_FREG) {
+ s->used.freg |= 1<<c;
+ s->used.freg |= 1<<(c|1);
+ } else
+ s->used.ireg |= 1<<c;
+ }
+ s->set.ireg &= ~(1<<REGZERO); /* R0 cant be set */
+}
+
+/*
+ * test to see if 2 instrictions can be
+ * interchanged without changing semantics
+ */
+int
+depend(Sch *sa, Sch *sb)
+{
+ ulong x;
+
+ if(sa->set.ireg & (sb->set.ireg|sb->used.ireg))
+ return 1;
+ if(sb->set.ireg & sa->used.ireg)
+ return 1;
+
+ if(sa->set.freg & (sb->set.freg|sb->used.freg))
+ return 1;
+ if(sb->set.freg & sa->used.freg)
+ return 1;
+
+ /*
+ * special case.
+ * loads from same address cannot pass.
+ * this is for hardware fifo's and the like
+ */
+ if(sa->used.cc & sb->used.cc & E_MEM)
+ if(sa->p.reg == sb->p.reg)
+ if(regoff(&sa->p.from) == regoff(&sb->p.from))
+ return 1;
+
+ x = (sa->set.cc & (sb->set.cc|sb->used.cc)) |
+ (sb->set.cc & sa->used.cc);
+ if(x) {
+ /*
+ * allow SB and SP to pass each other.
+ * allow SB to pass SB iff doffsets are ok
+ * anything else conflicts
+ */
+ if(x != E_MEMSP && x != E_MEMSB)
+ return 1;
+ x = sa->set.cc | sb->set.cc |
+ sa->used.cc | sb->used.cc;
+ if(x & E_MEM)
+ return 1;
+ if(offoverlap(sa, sb))
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+offoverlap(Sch *sa, Sch *sb)
+{
+
+ if(sa->soffset < sb->soffset) {
+ if(sa->soffset+sa->size > sb->soffset)
+ return 1;
+ return 0;
+ }
+ if(sb->soffset+sb->size > sa->soffset)
+ return 1;
+ return 0;
+}
+
+/*
+ * test 2 adjacent instructions
+ * and find out if inserted instructions
+ * are desired to prevent stalls.
+ */
+int
+conflict(Sch *sa, Sch *sb)
+{
+
+ if(sa->set.ireg & sb->used.ireg)
+ return 1;
+ if(sa->set.freg & sb->used.freg)
+ return 1;
+ if(sa->set.cc & sb->used.cc)
+ return 1;
+
+ return 0;
+}
+
+int
+compound(Prog *p)
+{
+ Optab *o;
+
+ o = oplook(p);
+ if(o->size != 4)
+ return 1;
+ if(p->to.type == D_REG && p->to.reg == REGSB)
+ return 1;
+ return 0;
+}
+
+void
+dumpbits(Sch *s, Dep *d)
+{
+ int i;
+
+ for(i=0; i<32; i++)
+ if(d->ireg & (1<<i))
+ Bprint(&bso, " R%d", i);
+ for(i=0; i<32; i++)
+ if(d->freg & (1<<i))
+ Bprint(&bso, " F%d", i);
+ for(i=0; i<32; i++)
+ switch(d->cc & (1<<i)) {
+ default:
+ break;
+ case E_HILO:
+ Bprint(&bso, " HILO");
+ break;
+ case E_FCR:
+ Bprint(&bso, " FCR");
+ break;
+ case E_MCR:
+ Bprint(&bso, " MCR");
+ break;
+ case E_MEM:
+ Bprint(&bso, " MEM%d", s->size);
+ break;
+ case E_MEMSB:
+ Bprint(&bso, " SB%d", s->size);
+ break;
+ case E_MEMSP:
+ Bprint(&bso, " SP%d", s->size);
+ break;
+ }
+}
--- /dev/null
+++ b/utils/0l/span.c
@@ -1,0 +1,628 @@
+#include "l.h"
+
+void
+span(void)
+{
+ Prog *p, *q;
+ Sym *setext;
+ Optab *o;
+ int m, bflag;
+ long c, otxt;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f span\n", cputime());
+ Bflush(&bso);
+
+ bflag = 0;
+ c = INITTEXT;
+ otxt = c;
+ for(p = firstp; p != P; p = p->link) {
+ p->pc = c;
+ o = oplook(p);
+ m = o->size;
+ if(m == 0) {
+ if(p->as == ATEXT) {
+ curtext = p;
+ autosize = p->to.offset + 8;
+ if(p->from.sym != S)
+ p->from.sym->value = c;
+ /* need passes to resolve branches */
+ if(c-otxt >= 1L<<17)
+ bflag = 1;
+ otxt = c;
+ continue;
+ }
+ diag("zero-width instruction\n%P\n", p);
+ continue;
+ }
+ c += m;
+ }
+
+ /*
+ * if any procedure is large enough to
+ * generate a large SBRA branch, then
+ * generate extra passes putting branches
+ * around jmps to fix. this is rare.
+ */
+ while(bflag) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f span1\n", cputime());
+ bflag = 0;
+ c = INITTEXT;
+ for(p = firstp; p != P; p = p->link) {
+ p->pc = c;
+ o = oplook(p);
+ if(o->type == 6 && p->cond) {
+ otxt = p->cond->pc - c;
+ if(otxt < 0)
+ otxt = -otxt;
+ if(otxt >= (1L<<17) - 10) {
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ q->as = AJMP;
+ q->to.type = D_BRANCH;
+ q->cond = p->cond;
+ p->cond = q;
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ q->as = AJMP;
+ q->to.type = D_BRANCH;
+ q->cond = q->link->link;
+ addnop(p->link);
+ addnop(p);
+ bflag = 1;
+ }
+ }
+ m = o->size;
+ if(m == 0) {
+ if(p->as == ATEXT) {
+ curtext = p;
+ autosize = p->to.offset + 8;
+ if(p->from.sym != S)
+ p->from.sym->value = c;
+ continue;
+ }
+ diag("zero-width instruction\n%P\n", p);
+ continue;
+ }
+ c += m;
+ }
+ }
+ c = rnd(c, 8);
+
+ setext = lookup("etext", 0);
+ if(setext != S) {
+ setext->value = c;
+ textsize = c - INITTEXT;
+ }
+ if(INITRND)
+ INITDAT = rnd(c, INITRND);
+ if(debug['v'])
+ Bprint(&bso, "tsize = %lux\n", textsize);
+ Bflush(&bso);
+}
+
+void
+xdefine(char *p, int t, long v)
+{
+ Sym *s;
+
+ s = lookup(p, 0);
+ if(s->type == 0 || s->type == SXREF) {
+ s->type = t;
+ s->value = v;
+ }
+}
+
+long
+regoff(Adr *a)
+{
+
+ instoffset = 0;
+ aclass(a);
+ return instoffset;
+}
+
+int
+aclass(Adr *a)
+{
+ Sym *s;
+ int t;
+
+ switch(a->type) {
+ case D_NONE:
+ return C_NONE;
+
+ case D_REG:
+ return C_REG;
+
+ case D_FREG:
+ return C_FREG;
+
+ case D_FCREG:
+ return C_FCREG;
+
+ case D_MREG:
+ return C_MREG;
+
+ case D_OREG:
+ switch(a->name) {
+ case D_EXTERN:
+ case D_STATIC:
+ if(a->sym == 0 || a->sym->name == 0) {
+ print("null sym external\n");
+ print("%D\n", a);
+ return C_GOK;
+ }
+ t = a->sym->type;
+ if(t == 0 || t == SXREF) {
+ diag("undefined external: %s in %s\n",
+ a->sym->name, TNAME);
+ a->sym->type = SDATA;
+ }
+ instoffset = a->sym->value + a->offset - BIG;
+ if(instoffset >= -BIG && instoffset < BIG)
+ return C_SEXT;
+ return C_LEXT;
+ case D_AUTO:
+ instoffset = autosize + a->offset;
+ if(instoffset >= -BIG && instoffset < BIG)
+ return C_SAUTO;
+ return C_LAUTO;
+
+ case D_PARAM:
+ instoffset = autosize + a->offset + 8L;
+ if(instoffset >= -BIG && instoffset < BIG)
+ return C_SAUTO;
+ return C_LAUTO;
+ case D_NONE:
+ instoffset = a->offset;
+ if(instoffset == 0)
+ return C_ZOREG;
+ if(instoffset >= -BIG && instoffset < BIG)
+ return C_SOREG;
+ return C_LOREG;
+ }
+ return C_GOK;
+
+ case D_HI:
+ return C_LO;
+ case D_LO:
+ return C_HI;
+
+ case D_OCONST:
+ switch(a->name) {
+ case D_EXTERN:
+ case D_STATIC:
+ s = a->sym;
+ t = s->type;
+ if(t == 0 || t == SXREF) {
+ diag("undefined external: %s in %s\n",
+ s->name, TNAME);
+ s->type = SDATA;
+ }
+ instoffset = s->value + a->offset + INITDAT;
+ if(s->type == STEXT || s->type == SLEAF)
+ instoffset = s->value + a->offset;
+ return C_LCON;
+ }
+ return C_GOK;
+
+ case D_CONST:
+ switch(a->name) {
+
+ case D_NONE:
+ instoffset = a->offset;
+ consize:
+ if(instoffset > 0) {
+ if(instoffset <= 0x7fff)
+ return C_SCON;
+ if(instoffset <= 0xffff)
+ return C_ANDCON;
+ if((instoffset & 0xffff) == 0)
+ return C_UCON;
+ return C_LCON;
+ }
+ if(instoffset == 0)
+ return C_ZCON;
+ if(instoffset >= -0x8000)
+ return C_ADDCON;
+ if((instoffset & 0xffff) == 0)
+ return C_UCON;
+ return C_LCON;
+
+ case D_EXTERN:
+ case D_STATIC:
+ s = a->sym;
+ if(s == S)
+ break;
+ t = s->type;
+ switch(t) {
+ case 0:
+ case SXREF:
+ diag("undefined external: %s in %s\n",
+ s->name, TNAME);
+ s->type = SDATA;
+ break;
+ case SCONST:
+ instoffset = s->value + a->offset;
+ goto consize;
+ case STEXT:
+ case SLEAF:
+ instoffset = s->value + a->offset;
+ return C_LCON;
+ }
+ instoffset = s->value + a->offset - BIG;
+ if(instoffset >= -BIG && instoffset < BIG && instoffset != 0L)
+ return C_SECON;
+ instoffset = s->value + a->offset + INITDAT;
+ return C_LCON;
+
+ case D_AUTO:
+ instoffset = autosize + a->offset;
+ if(instoffset >= -BIG && instoffset < BIG)
+ return C_SACON;
+ return C_LACON;
+
+ case D_PARAM:
+ instoffset = autosize + a->offset + 8L;
+ if(instoffset >= -BIG && instoffset < BIG)
+ return C_SACON;
+ return C_LACON;
+ }
+ return C_GOK;
+
+ case D_BRANCH:
+ return C_SBRA;
+ }
+ return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+ int a1, a2, a3, r, t;
+ char *c1, *c3;
+ Optab *o, *e;
+
+ a1 = p->optab;
+ if(a1)
+ return optab+(a1-1);
+ a1 = p->from.class;
+ if(a1 == 0) {
+ a1 = aclass(&p->from) + 1;
+ p->from.class = a1;
+ }
+ a1--;
+ a3 = p->to.class;
+ if(a3 == 0) {
+ a3 = aclass(&p->to) + 1;
+ p->to.class = a3;
+ }
+ a3--;
+ a2 = C_NONE;
+ if(p->reg != NREG)
+ a2 = C_REG;
+ r = p->as;
+ o = oprange[r].start;
+ if(o == 0) {
+ t = opcross[repop[r]][a1][a2][a3];
+ if(t) {
+ p->optab = t+1;
+ return optab+t;
+ }
+ o = oprange[r].stop; /* just generate an error */
+ }
+ e = oprange[r].stop;
+ c1 = xcmp[a1];
+ c3 = xcmp[a3];
+ for(; o<e; o++)
+ if(o->a2 == a2)
+ if(c1[o->a1])
+ if(c3[o->a3]) {
+ p->optab = (o-optab)+1;
+ return o;
+ }
+ diag("illegal combination %A %d %d %d",
+ p->as, a1, a2, a3);
+ if(!debug['a'])
+ prasm(p);
+ o = optab;
+ p->optab = (o-optab)+1;
+ return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+ if(a == b)
+ return 1;
+ switch(a) {
+ case C_LCON:
+ if(b == C_ZCON || b == C_SCON || b == C_UCON ||
+ b == C_ADDCON || b == C_ANDCON)
+ return 1;
+ break;
+ case C_ADD0CON:
+ if(b == C_ADDCON)
+ return 1;
+ case C_ADDCON:
+ if(b == C_ZCON || b == C_SCON)
+ return 1;
+ break;
+ case C_AND0CON:
+ if(b == C_ANDCON)
+ return 1;
+ case C_ANDCON:
+ if(b == C_ZCON || b == C_SCON)
+ return 1;
+ break;
+ case C_UCON:
+ if(b == C_ZCON)
+ return 1;
+ break;
+ case C_SCON:
+ if(b == C_ZCON)
+ return 1;
+ break;
+ case C_LACON:
+ if(b == C_SACON)
+ return 1;
+ break;
+ case C_LBRA:
+ if(b == C_SBRA)
+ return 1;
+ break;
+ case C_LEXT:
+ if(b == C_SEXT)
+ return 1;
+ break;
+ case C_LAUTO:
+ if(b == C_SAUTO)
+ return 1;
+ break;
+ case C_REG:
+ if(b == C_ZCON)
+ return 1;
+ break;
+ case C_LOREG:
+ if(b == C_ZOREG || b == C_SOREG)
+ return 1;
+ break;
+ case C_SOREG:
+ if(b == C_ZOREG)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+int
+ocmp(void *a1, void *a2)
+{
+ Optab *p1, *p2;
+ int n;
+
+ p1 = (Optab*)a1;
+ p2 = (Optab*)a2;
+ n = p1->as - p2->as;
+ if(n)
+ return n;
+ n = p1->a1 - p2->a1;
+ if(n)
+ return n;
+ n = p1->a2 - p2->a2;
+ if(n)
+ return n;
+ n = p1->a3 - p2->a3;
+ if(n)
+ return n;
+ return 0;
+}
+
+void
+buildop(void)
+{
+ int i, n, r;
+
+ for(i=0; i<32; i++)
+ for(n=0; n<32; n++)
+ xcmp[i][n] = cmp(n, i);
+ for(n=0; optab[n].as != AXXX; n++)
+ ;
+ qsort(optab, n, sizeof(optab[0]), ocmp);
+ for(i=0; i<n; i++) {
+ r = optab[i].as;
+ oprange[r].start = optab+i;
+ while(optab[i].as == r)
+ i++;
+ oprange[r].stop = optab+i;
+ i--;
+
+ switch(r)
+ {
+ default:
+ diag("unknown op in build: %A\n", r);
+ errorexit();
+ case AABSF:
+ oprange[AMOVFD] = oprange[r];
+ oprange[AMOVDF] = oprange[r];
+ oprange[AMOVWF] = oprange[r];
+ oprange[AMOVFW] = oprange[r];
+ oprange[AMOVWD] = oprange[r];
+ oprange[AMOVDW] = oprange[r];
+ oprange[ANEGF] = oprange[r];
+ oprange[ANEGD] = oprange[r];
+ oprange[AABSD] = oprange[r];
+ oprange[ATRUNCDW] = oprange[r];
+ oprange[ATRUNCFW] = oprange[r];
+ oprange[ATRUNCDV] = oprange[r];
+ oprange[ATRUNCFV] = oprange[r];
+ oprange[AMOVDV] = oprange[r];
+ oprange[AMOVFV] = oprange[r];
+ oprange[AMOVVD] = oprange[r];
+ oprange[AMOVVF] = oprange[r];
+ break;
+ case AADD:
+ buildrep(1, AADD);
+ oprange[ASGT] = oprange[r];
+ repop[ASGT] = 1;
+ oprange[ASGTU] = oprange[r];
+ repop[ASGTU] = 1;
+ oprange[AADDU] = oprange[r];
+ repop[AADDU] = 1;
+ oprange[AADDVU] = oprange[r];
+ repop[AADDVU] = 1;
+ oprange[AADDV] = oprange[r];
+ repop[AADDV] = 1;
+ break;
+ case AADDF:
+ oprange[ADIVF] = oprange[r];
+ oprange[ADIVD] = oprange[r];
+ oprange[AMULF] = oprange[r];
+ oprange[AMULD] = oprange[r];
+ oprange[ASUBF] = oprange[r];
+ oprange[ASUBD] = oprange[r];
+ oprange[AADDD] = oprange[r];
+ break;
+ case AAND:
+ buildrep(2, AAND);
+ oprange[AXOR] = oprange[r];
+ repop[AXOR] = 2;
+ oprange[AOR] = oprange[r];
+ repop[AOR] = 2;
+ break;
+ case ABEQ:
+ oprange[ABNE] = oprange[r];
+ break;
+ case ABLEZ:
+ oprange[ABGEZ] = oprange[r];
+ oprange[ABGEZAL] = oprange[r];
+ oprange[ABLTZ] = oprange[r];
+ oprange[ABLTZAL] = oprange[r];
+ oprange[ABGTZ] = oprange[r];
+ break;
+ case AMOVB:
+ buildrep(3, AMOVB);
+ oprange[AMOVH] = oprange[r];
+ repop[AMOVH] = 3;
+ break;
+ case AMOVBU:
+ buildrep(4, AMOVBU);
+ oprange[AMOVHU] = oprange[r];
+ repop[AMOVHU] = 4;
+ break;
+ case AMUL:
+ oprange[AREM] = oprange[r];
+ oprange[AREMU] = oprange[r];
+ oprange[ADIVU] = oprange[r];
+ oprange[AMULU] = oprange[r];
+ oprange[ADIV] = oprange[r];
+ oprange[ADIVV] = oprange[r];
+ oprange[ADIVVU] = oprange[r];
+ oprange[AMULV] = oprange[r];
+ oprange[AMULVU] = oprange[r];
+ oprange[AREMV] = oprange[r];
+ oprange[AREMVU] = oprange[r];
+ break;
+ case ASLL:
+ oprange[ASRL] = oprange[r];
+ oprange[ASRA] = oprange[r];
+ oprange[ASLLV] = oprange[r];
+ oprange[ASRAV] = oprange[r];
+ oprange[ASRLV] = oprange[r];
+ break;
+ case ASUB:
+ oprange[ASUBU] = oprange[r];
+ oprange[ASUBV] = oprange[r];
+ oprange[ASUBVU] = oprange[r];
+ oprange[ANOR] = oprange[r];
+ break;
+ case ASYSCALL:
+ oprange[ATLBP] = oprange[r];
+ oprange[ATLBR] = oprange[r];
+ oprange[ATLBWI] = oprange[r];
+ oprange[ATLBWR] = oprange[r];
+ break;
+ case ACMPEQF:
+ oprange[ACMPGTF] = oprange[r];
+ oprange[ACMPGTD] = oprange[r];
+ oprange[ACMPGEF] = oprange[r];
+ oprange[ACMPGED] = oprange[r];
+ oprange[ACMPEQD] = oprange[r];
+ break;
+ case ABFPT:
+ oprange[ABFPF] = oprange[r];
+ break;
+ case AMOVWL:
+ oprange[AMOVWR] = oprange[r];
+ oprange[AMOVVR] = oprange[r];
+ oprange[AMOVVL] = oprange[r];
+ break;
+ case AMOVW:
+ buildrep(5, AMOVW);
+ break;
+ case AMOVD:
+ buildrep(6, AMOVD);
+ break;
+ case AMOVF:
+ buildrep(7, AMOVF);
+ break;
+ case AMOVV:
+ buildrep(8, AMOVV);
+ break;
+ case ABREAK:
+ case AWORD:
+ case ARFE:
+ case AJAL:
+ case AJMP:
+ case ATEXT:
+ case ACASE:
+ case ABCASE:
+ case AMOVWU:
+ break;
+ }
+ }
+}
+
+void
+buildrep(int x, int as)
+{
+ Opcross *p;
+ Optab *e, *s, *o;
+ int a1, a2, a3, n;
+
+ if(C_NONE != 0 ||
+ C_REG != 1 ||
+ C_GOK >= 32 ||
+ x >= nelem(opcross)) {
+ diag("assumptions fail in buildrep");
+ errorexit();
+ }
+ repop[as] = x;
+ p = (opcross + x);
+ s = oprange[as].start;
+ e = oprange[as].stop;
+ for(o=e-1; o>=s; o--) {
+ n = o-optab;
+ for(a2=0; a2<2; a2++) {
+ if(a2) {
+ if(o->a2 == C_NONE)
+ continue;
+ } else
+ if(o->a2 != C_NONE)
+ continue;
+ for(a1=0; a1<32; a1++) {
+ if(!xcmp[a1][o->a1])
+ continue;
+ for(a3=0; a3<32; a3++)
+ if(xcmp[a3][o->a3])
+ (*p)[a1][a2][a3] = n;
+ }
+ }
+ }
+ oprange[as].start = 0;
+}
--- /dev/null
+++ b/utils/5a/a.h
@@ -1,0 +1,182 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../5c/5.out.h"
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+typedef struct Sym Sym;
+typedef struct Gen Gen;
+typedef struct Io Io;
+typedef struct Hist Hist;
+
+#define MAXALIGN 7
+#define FPCHIP 1
+#define NSYMB 8192
+#define BUFSIZ 8192
+#define HISTSZ 20
+#define NINCLUDE 10
+#define NHUNK 10000
+#define EOF (-1)
+#define IGN (-2)
+#define GETC() ((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define NHASH 503
+#define STRINGSZ 200
+#define NMACRO 10
+
+struct Sym
+{
+ Sym* link;
+ char* macro;
+ long value;
+ ushort type;
+ char *name;
+ char sym;
+};
+#define S ((Sym*)0)
+
+EXTERN struct
+{
+ char* p;
+ int c;
+} fi;
+
+struct Io
+{
+ Io* link;
+ char b[BUFSIZ];
+ char* p;
+ short c;
+ short f;
+};
+#define I ((Io*)0)
+
+EXTERN struct
+{
+ Sym* sym;
+ short type;
+} h[NSYM];
+
+struct Gen
+{
+ Sym* sym;
+ long offset;
+ short type;
+ short reg;
+ short name;
+ double dval;
+ char sval[8];
+};
+
+struct Hist
+{
+ Hist* link;
+ char* name;
+ long line;
+ long offset;
+};
+#define H ((Hist*)0)
+
+enum
+{
+ CLAST,
+ CMACARG,
+ CMACRO,
+ CPREPROC,
+
+ Always = 14,
+};
+
+EXTERN char debug[256];
+EXTERN Sym* hash[NHASH];
+EXTERN char* Dlist[30];
+EXTERN int nDlist;
+EXTERN Hist* ehist;
+EXTERN int newflag;
+EXTERN Hist* hist;
+EXTERN char* hunk;
+EXTERN char* include[NINCLUDE];
+EXTERN Io* iofree;
+EXTERN Io* ionext;
+EXTERN Io* iostack;
+EXTERN long lineno;
+EXTERN int nerrors;
+EXTERN long nhunk;
+EXTERN int ninclude;
+EXTERN Gen nullgen;
+EXTERN char* outfile;
+EXTERN int pass;
+EXTERN char* pathname;
+EXTERN long pc;
+EXTERN int peekc;
+EXTERN int sym;
+EXTERN char symb[NSYMB];
+EXTERN int thechar;
+EXTERN char* thestring;
+EXTERN long thunk;
+EXTERN Biobuf obuf;
+
+void* alloc(long);
+void* allocn(void*, long, long);
+void errorexit(void);
+void pushio(void);
+void newio(void);
+void newfile(char*, int);
+Sym* slookup(char*);
+Sym* lookup(void);
+void syminit(Sym*);
+long yylex(void);
+int getc(void);
+int getnsc(void);
+void unget(int);
+int escchar(int);
+void cinit(void);
+void pinit(char*);
+void cclean(void);
+int isreg(Gen*);
+void outcode(int, int, Gen*, int, Gen*);
+void zname(char*, int, int);
+void zaddr(Gen*, int);
+void ieeedtod(Ieee*, double);
+int filbuf(void);
+Sym* getsym(void);
+void domacro(void);
+void macund(void);
+void macdef(void);
+void macexpand(Sym*, char*);
+void macinc(void);
+void maclin(void);
+void macprag(void);
+void macif(int);
+void macend(void);
+void outhist(void);
+void dodefine(char*);
+void prfile(long);
+void linehist(char*, int);
+void gethunk(void);
+void yyerror(char*, ...);
+int yyparse(void);
+void setinclude(char*);
+int assemble(char*);
+
+/*
+ * system-dependent stuff from ../cc/compat.c
+ */
+
+enum /* keep in synch with ../cc/cc.h */
+{
+ Plan9 = 1<<0,
+ Unix = 1<<1,
+ Windows = 1<<2,
+};
+int mywait(int*);
+int mycreat(char*, int);
+int systemtype(int);
+int pathchar(void);
+int myfork(void);
+char* mygetwd(char*, int);
+int myexec(char*, char*[]);
+int mydup(int, int);
+int mypipe(int*);
+void* mysbrk(ulong);
--- /dev/null
+++ b/utils/5a/a.y
@@ -1,0 +1,673 @@
+%{
+#include "a.h"
+%}
+%union
+{
+ Sym *sym;
+ long lval;
+ double dval;
+ char sval[8];
+ Gen gen;
+}
+%left '|'
+%left '^'
+%left '&'
+%left '<' '>'
+%left '+' '-'
+%left '*' '/' '%'
+%token <lval> LTYPE1 LTYPE2 LTYPE3 LTYPE4 LTYPE5
+%token <lval> LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA
+%token <lval> LTYPEB LTYPEC LTYPED LTYPEE LTYPEF
+%token <lval> LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK
+%token <lval> LTYPEL LTYPEM LTYPEN LTYPEBX
+%token <lval> LCONST LSP LSB LFP LPC
+%token <lval> LTYPEX LR LREG LF LFREG LC LCREG LPSR LFCR
+%token <lval> LCOND LS LAT
+%token <dval> LFCONST
+%token <sval> LSCONST
+%token <sym> LNAME LLAB LVAR
+%type <lval> con expr oexpr pointer offset sreg spreg creg
+%type <lval> rcon cond reglist
+%type <gen> gen rel reg regreg freg shift fcon frcon
+%type <gen> imm ximm name oreg ireg nireg ioreg imsr
+%%
+prog:
+| prog line
+
+line:
+ LLAB ':'
+ {
+ if($1->value != pc)
+ yyerror("redeclaration of %s", $1->name);
+ $1->value = pc;
+ }
+ line
+| LNAME ':'
+ {
+ $1->type = LLAB;
+ $1->value = pc;
+ }
+ line
+| LNAME '=' expr ';'
+ {
+ $1->type = LVAR;
+ $1->value = $3;
+ }
+| LVAR '=' expr ';'
+ {
+ if($1->value != $3)
+ yyerror("redeclaration of %s", $1->name);
+ $1->value = $3;
+ }
+| ';'
+| inst ';'
+| error ';'
+
+inst:
+/*
+ * ADD
+ */
+ LTYPE1 cond imsr ',' spreg ',' reg
+ {
+ outcode($1, $2, &$3, $5, &$7);
+ }
+| LTYPE1 cond imsr ',' spreg ','
+ {
+ outcode($1, $2, &$3, $5, &nullgen);
+ }
+| LTYPE1 cond imsr ',' reg
+ {
+ outcode($1, $2, &$3, NREG, &$5);
+ }
+/*
+ * MVN
+ */
+| LTYPE2 cond imsr ',' reg
+ {
+ outcode($1, $2, &$3, NREG, &$5);
+ }
+/*
+ * MOVW
+ */
+| LTYPE3 cond gen ',' gen
+ {
+ outcode($1, $2, &$3, NREG, &$5);
+ }
+/*
+ * B/BL
+ */
+| LTYPE4 cond comma rel
+ {
+ outcode($1, $2, &nullgen, NREG, &$4);
+ }
+| LTYPE4 cond comma nireg
+ {
+ outcode($1, $2, &nullgen, NREG, &$4);
+ }
+/*
+ * BX
+ */
+| LTYPEBX comma ireg
+ {
+ outcode($1, Always, &nullgen, NREG, &$3);
+ }
+/*
+ * BEQ
+ */
+| LTYPE5 comma rel
+ {
+ outcode($1, Always, &nullgen, NREG, &$3);
+ }
+/*
+ * SWI
+ */
+| LTYPE6 cond comma gen
+ {
+ outcode($1, $2, &nullgen, NREG, &$4);
+ }
+/*
+ * CMP
+ */
+| LTYPE7 cond imsr ',' spreg comma
+ {
+ outcode($1, $2, &$3, $5, &nullgen);
+ }
+/*
+ * MOVM
+ */
+| LTYPE8 cond ioreg ',' '[' reglist ']'
+ {
+ Gen g;
+
+ g = nullgen;
+ g.type = D_CONST;
+ g.offset = $6;
+ outcode($1, $2, &$3, NREG, &g);
+ }
+| LTYPE8 cond '[' reglist ']' ',' ioreg
+ {
+ Gen g;
+
+ g = nullgen;
+ g.type = D_CONST;
+ g.offset = $4;
+ outcode($1, $2, &g, NREG, &$7);
+ }
+/*
+ * SWAP
+ */
+| LTYPE9 cond reg ',' ireg ',' reg
+ {
+ outcode($1, $2, &$5, $3.reg, &$7);
+ }
+| LTYPE9 cond reg ',' ireg comma
+ {
+ outcode($1, $2, &$5, $3.reg, &$3);
+ }
+| LTYPE9 cond comma ireg ',' reg
+ {
+ outcode($1, $2, &$4, $6.reg, &$6);
+ }
+/*
+ * RET
+ */
+| LTYPEA cond comma
+ {
+ outcode($1, $2, &nullgen, NREG, &nullgen);
+ }
+/*
+ * TEXT/GLOBL
+ */
+| LTYPEB name ',' imm
+ {
+ outcode($1, Always, &$2, NREG, &$4);
+ }
+| LTYPEB name ',' con ',' imm
+ {
+ outcode($1, Always, &$2, $4, &$6);
+ }
+/*
+ * DATA
+ */
+| LTYPEC name '/' con ',' ximm
+ {
+ outcode($1, Always, &$2, $4, &$6);
+ }
+/*
+ * CASE
+ */
+| LTYPED cond reg comma
+ {
+ outcode($1, $2, &$3, NREG, &nullgen);
+ }
+/*
+ * word
+ */
+| LTYPEH comma ximm
+ {
+ outcode($1, Always, &nullgen, NREG, &$3);
+ }
+/*
+ * floating-point coprocessor
+ */
+| LTYPEI cond freg ',' freg
+ {
+ outcode($1, $2, &$3, NREG, &$5);
+ }
+| LTYPEK cond frcon ',' freg
+ {
+ outcode($1, $2, &$3, NREG, &$5);
+ }
+| LTYPEK cond frcon ',' LFREG ',' freg
+ {
+ outcode($1, $2, &$3, $5, &$7);
+ }
+| LTYPEL cond freg ',' freg comma
+ {
+ outcode($1, $2, &$3, $5.reg, &nullgen);
+ }
+/*
+ * MCR MRC
+ */
+| LTYPEJ cond con ',' expr ',' spreg ',' creg ',' creg oexpr
+ {
+ Gen g;
+
+ g = nullgen;
+ g.type = D_CONST;
+ g.offset =
+ (0xe << 24) | /* opcode */
+ ($1 << 20) | /* MCR/MRC */
+ ($2 << 28) | /* scond */
+ (($3 & 15) << 8) | /* coprocessor number */
+ (($5 & 7) << 21) | /* coprocessor operation */
+ (($7 & 15) << 12) | /* arm register */
+ (($9 & 15) << 16) | /* Crn */
+ (($11 & 15) << 0) | /* Crm */
+ (($12 & 7) << 5) | /* coprocessor information */
+ (1<<4); /* must be set */
+ outcode(AWORD, Always, &nullgen, NREG, &g);
+ }
+/*
+ * MULL hi,lo,r1,r2
+ */
+| LTYPEM cond reg ',' reg ',' regreg
+ {
+ outcode($1, $2, &$3, $5.reg, &$7);
+ }
+/*
+ * MULA hi,lo,r1,r2
+ */
+| LTYPEN cond reg ',' reg ',' reg ',' spreg
+ {
+ $7.type = D_REGREG;
+ $7.offset = $9;
+ outcode($1, $2, &$3, $5.reg, &$7);
+ }
+/*
+ * END
+ */
+| LTYPEE comma
+ {
+ outcode($1, Always, &nullgen, NREG, &nullgen);
+ }
+
+cond:
+ {
+ $$ = Always;
+ }
+| cond LCOND
+ {
+ $$ = ($1 & ~C_SCOND) | $2;
+ }
+| cond LS
+ {
+ $$ = $1 | $2;
+ }
+
+comma:
+| ',' comma
+
+rel:
+ con '(' LPC ')'
+ {
+ $$ = nullgen;
+ $$.type = D_BRANCH;
+ $$.offset = $1 + pc;
+ }
+| LNAME offset
+ {
+ $$ = nullgen;
+ if(pass == 2)
+ yyerror("undefined label: %s", $1->name);
+ $$.type = D_BRANCH;
+ $$.sym = $1;
+ $$.offset = $2;
+ }
+| LLAB offset
+ {
+ $$ = nullgen;
+ $$.type = D_BRANCH;
+ $$.sym = $1;
+ $$.offset = $1->value + $2;
+ }
+
+ximm: '$' con
+ {
+ $$ = nullgen;
+ $$.type = D_CONST;
+ $$.offset = $2;
+ }
+| '$' oreg
+ {
+ $$ = $2;
+ $$.type = D_CONST;
+ }
+| '$' '*' '$' oreg
+ {
+ $$ = $4;
+ $$.type = D_OCONST;
+ }
+| '$' LSCONST
+ {
+ $$ = nullgen;
+ $$.type = D_SCONST;
+ memcpy($$.sval, $2, sizeof($$.sval));
+ }
+| fcon
+
+fcon:
+ '$' LFCONST
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = $2;
+ }
+| '$' '-' LFCONST
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = -$3;
+ }
+
+reglist:
+ spreg
+ {
+ $$ = 1 << $1;
+ }
+| spreg '-' spreg
+ {
+ int i;
+ $$=0;
+ for(i=$1; i<=$3; i++)
+ $$ |= 1<<i;
+ for(i=$3; i<=$1; i++)
+ $$ |= 1<<i;
+ }
+| spreg comma reglist
+ {
+ $$ = (1<<$1) | $3;
+ }
+
+gen:
+ reg
+| ximm
+| shift
+| shift '(' spreg ')'
+ {
+ $$ = $1;
+ $$.reg = $3;
+ }
+| LPSR
+ {
+ $$ = nullgen;
+ $$.type = D_PSR;
+ $$.reg = $1;
+ }
+| LFCR
+ {
+ $$ = nullgen;
+ $$.type = D_FPCR;
+ $$.reg = $1;
+ }
+| con
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.offset = $1;
+ }
+| oreg
+| freg
+
+nireg:
+ ireg
+| name
+ {
+ $$ = $1;
+ if($1.name != D_EXTERN && $1.name != D_STATIC) {
+ }
+ }
+
+ireg:
+ '(' spreg ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.reg = $2;
+ $$.offset = 0;
+ }
+
+ioreg:
+ ireg
+| con '(' sreg ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.reg = $3;
+ $$.offset = $1;
+ }
+
+oreg:
+ name
+| name '(' sreg ')'
+ {
+ $$ = $1;
+ $$.type = D_OREG;
+ $$.reg = $3;
+ }
+| ioreg
+
+imsr:
+ reg
+| imm
+| shift
+
+imm: '$' con
+ {
+ $$ = nullgen;
+ $$.type = D_CONST;
+ $$.offset = $2;
+ }
+
+reg:
+ spreg
+ {
+ $$ = nullgen;
+ $$.type = D_REG;
+ $$.reg = $1;
+ }
+
+regreg:
+ '(' spreg ',' spreg ')'
+ {
+ $$ = nullgen;
+ $$.type = D_REGREG;
+ $$.reg = $2;
+ $$.offset = $4;
+ }
+
+shift:
+ spreg '<' '<' rcon
+ {
+ $$ = nullgen;
+ $$.type = D_SHIFT;
+ $$.offset = $1 | $4 | (0 << 5);
+ }
+| spreg '>' '>' rcon
+ {
+ $$ = nullgen;
+ $$.type = D_SHIFT;
+ $$.offset = $1 | $4 | (1 << 5);
+ }
+| spreg '-' '>' rcon
+ {
+ $$ = nullgen;
+ $$.type = D_SHIFT;
+ $$.offset = $1 | $4 | (2 << 5);
+ }
+| spreg LAT '>' rcon
+ {
+ $$ = nullgen;
+ $$.type = D_SHIFT;
+ $$.offset = $1 | $4 | (3 << 5);
+ }
+
+rcon:
+ spreg
+ {
+ if($$ < 0 || $$ >= 16)
+ print("register value out of range\n");
+ $$ = (($1&15) << 8) | (1 << 4);
+ }
+| con
+ {
+ if($$ < 0 || $$ >= 32)
+ print("shift value out of range\n");
+ $$ = ($1&31) << 7;
+ }
+
+sreg:
+ LREG
+| LPC
+ {
+ $$ = REGPC;
+ }
+| LR '(' expr ')'
+ {
+ if($3 < 0 || $3 >= NREG)
+ print("register value out of range\n");
+ $$ = $3;
+ }
+
+spreg:
+ sreg
+| LSP
+ {
+ $$ = REGSP;
+ }
+
+creg:
+ LCREG
+| LC '(' expr ')'
+ {
+ if($3 < 0 || $3 >= NREG)
+ print("register value out of range\n");
+ $$ = $3;
+ }
+
+frcon:
+ freg
+| fcon
+
+freg:
+ LFREG
+ {
+ $$ = nullgen;
+ $$.type = D_FREG;
+ $$.reg = $1;
+ }
+| LF '(' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_FREG;
+ $$.reg = $3;
+ }
+
+name:
+ con '(' pointer ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.name = $3;
+ $$.sym = S;
+ $$.offset = $1;
+ }
+| LNAME offset '(' pointer ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.name = $4;
+ $$.sym = $1;
+ $$.offset = $2;
+ }
+| LNAME '<' '>' offset '(' LSB ')'
+ {
+ $$ = nullgen;
+ $$.type = D_OREG;
+ $$.name = D_STATIC;
+ $$.sym = $1;
+ $$.offset = $4;
+ }
+
+offset:
+ {
+ $$ = 0;
+ }
+| '+' con
+ {
+ $$ = $2;
+ }
+| '-' con
+ {
+ $$ = -$2;
+ }
+
+pointer:
+ LSB
+| LSP
+| LFP
+
+con:
+ LCONST
+| LVAR
+ {
+ $$ = $1->value;
+ }
+| '-' con
+ {
+ $$ = -$2;
+ }
+| '+' con
+ {
+ $$ = $2;
+ }
+| '~' con
+ {
+ $$ = ~$2;
+ }
+| '(' expr ')'
+ {
+ $$ = $2;
+ }
+
+oexpr:
+ {
+ $$ = 0;
+ }
+| ',' expr
+ {
+ $$ = $2;
+ }
+
+expr:
+ con
+| expr '+' expr
+ {
+ $$ = $1 + $3;
+ }
+| expr '-' expr
+ {
+ $$ = $1 - $3;
+ }
+| expr '*' expr
+ {
+ $$ = $1 * $3;
+ }
+| expr '/' expr
+ {
+ $$ = $1 / $3;
+ }
+| expr '%' expr
+ {
+ $$ = $1 % $3;
+ }
+| expr '<' '<' expr
+ {
+ $$ = $1 << $4;
+ }
+| expr '>' '>' expr
+ {
+ $$ = $1 >> $4;
+ }
+| expr '&' expr
+ {
+ $$ = $1 & $3;
+ }
+| expr '^' expr
+ {
+ $$ = $1 ^ $3;
+ }
+| expr '|' expr
+ {
+ $$ = $1 | $3;
+ }
--- /dev/null
+++ b/utils/5a/lex.c
@@ -1,0 +1,696 @@
+#define EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+ char *p;
+ int nout, nproc, status, i, c;
+
+ thechar = '5';
+ thestring = "arm";
+ memset(debug, 0, sizeof(debug));
+ cinit();
+ outfile = 0;
+ include[ninclude++] = ".";
+ ARGBEGIN {
+ default:
+ c = ARGC();
+ if(c >= 0 || c < sizeof(debug))
+ debug[c] = 1;
+ break;
+
+ case 'o':
+ outfile = ARGF();
+ break;
+
+ case 'D':
+ p = ARGF();
+ if(p)
+ Dlist[nDlist++] = p;
+ break;
+
+ case 'I':
+ p = ARGF();
+ setinclude(p);
+ break;
+ case 't':
+ thechar = 't';
+ thestring = "thumb";
+ break;
+ } ARGEND
+ if(*argv == 0) {
+ print("usage: %ca [-options] file.s\n", thechar);
+ errorexit();
+ }
+ if(argc > 1 && systemtype(Windows)){
+ print("can't assemble multiple files on windows\n");
+ errorexit();
+ }
+ if(argc > 1 && !systemtype(Windows)) {
+ nproc = 1;
+ if(p = getenv("NPROC"))
+ nproc = atol(p); /* */
+ c = 0;
+ nout = 0;
+ for(;;) {
+ while(nout < nproc && argc > 0) {
+ i = myfork();
+ if(i < 0) {
+ i = mywait(&status);
+ if(i < 0)
+ errorexit();
+ if(status)
+ c++;
+ nout--;
+ continue;
+ }
+ if(i == 0) {
+ print("%s:\n", *argv);
+ if(assemble(*argv))
+ errorexit();
+ exits(0);
+ }
+ nout++;
+ argc--;
+ argv++;
+ }
+ i = mywait(&status);
+ if(i < 0) {
+ if(c)
+ errorexit();
+ exits(0);
+ }
+ if(status)
+ c++;
+ nout--;
+ }
+ }
+ if(assemble(argv[0]))
+ errorexit();
+ exits(0);
+}
+
+int
+assemble(char *file)
+{
+ char ofile[100], incfile[20], *p;
+ int i, of;
+
+ strcpy(ofile, file);
+ p = utfrrune(ofile, pathchar());
+ if(p) {
+ include[0] = ofile;
+ *p++ = 0;
+ } else
+ p = ofile;
+ if(outfile == 0) {
+ outfile = p;
+ if(outfile){
+ p = utfrrune(outfile, '.');
+ if(p)
+ if(p[1] == 's' && p[2] == 0)
+ p[0] = 0;
+ p = utfrune(outfile, 0);
+ p[0] = '.';
+ p[1] = thechar;
+ p[2] = 0;
+ } else
+ outfile = "/dev/null";
+ }
+ p = getenv("INCLUDE");
+ if(p) {
+ setinclude(p);
+ } else {
+ if(systemtype(Plan9)) {
+ sprint(incfile,"/%s/include", thestring);
+ setinclude(strdup(incfile));
+ }
+ }
+
+ of = mycreat(outfile, 0664);
+ if(of < 0) {
+ yyerror("%ca: cannot create %s", thechar, outfile);
+ errorexit();
+ }
+ Binit(&obuf, of, OWRITE);
+
+ pass = 1;
+ pinit(file);
+ for(i=0; i<nDlist; i++)
+ dodefine(Dlist[i]);
+ yyparse();
+ if(nerrors) {
+ cclean();
+ return nerrors;
+ }
+
+ pass = 2;
+ outhist();
+ pinit(file);
+ for(i=0; i<nDlist; i++)
+ dodefine(Dlist[i]);
+ yyparse();
+ cclean();
+ return nerrors;
+}
+
+struct
+{
+ char *name;
+ ushort type;
+ ushort value;
+} itab[] =
+{
+ "SP", LSP, D_AUTO,
+ "SB", LSB, D_EXTERN,
+ "FP", LFP, D_PARAM,
+ "PC", LPC, D_BRANCH,
+
+ "R", LR, 0,
+ "R0", LREG, 0,
+ "R1", LREG, 1,
+ "R2", LREG, 2,
+ "R3", LREG, 3,
+ "R4", LREG, 4,
+ "R5", LREG, 5,
+ "R6", LREG, 6,
+ "R7", LREG, 7,
+ "R8", LREG, 8,
+ "R9", LREG, 9,
+ "R10", LREG, 10,
+ "R11", LREG, 11,
+ "R12", LREG, 12,
+ "R13", LREG, 13,
+ "R14", LREG, 14,
+ "R15", LREG, 15,
+
+ "F", LF, 0,
+
+ "F0", LFREG, 0,
+ "F1", LFREG, 1,
+ "F2", LFREG, 2,
+ "F3", LFREG, 3,
+ "F4", LFREG, 4,
+ "F5", LFREG, 5,
+ "F6", LFREG, 6,
+ "F7", LFREG, 7,
+ "F8", LFREG, 8,
+ "F9", LFREG, 9,
+ "F10", LFREG, 10,
+ "F11", LFREG, 11,
+ "F12", LFREG, 12,
+ "F13", LFREG, 13,
+ "F14", LFREG, 14,
+ "F15", LFREG, 15,
+
+ "C", LC, 0,
+
+ "C0", LCREG, 0,
+ "C1", LCREG, 1,
+ "C2", LCREG, 2,
+ "C3", LCREG, 3,
+ "C4", LCREG, 4,
+ "C5", LCREG, 5,
+ "C6", LCREG, 6,
+ "C7", LCREG, 7,
+ "C8", LCREG, 8,
+ "C9", LCREG, 9,
+ "C10", LCREG, 10,
+ "C11", LCREG, 11,
+ "C12", LCREG, 12,
+ "C13", LCREG, 13,
+ "C14", LCREG, 14,
+ "C15", LCREG, 15,
+
+ "CPSR", LPSR, 0,
+ "SPSR", LPSR, 1,
+
+ "FPSR", LFCR, 0,
+ "FPCR", LFCR, 1,
+
+ ".EQ", LCOND, 0,
+ ".NE", LCOND, 1,
+ ".CS", LCOND, 2,
+ ".HS", LCOND, 2,
+ ".CC", LCOND, 3,
+ ".LO", LCOND, 3,
+ ".MI", LCOND, 4,
+ ".PL", LCOND, 5,
+ ".VS", LCOND, 6,
+ ".VC", LCOND, 7,
+ ".HI", LCOND, 8,
+ ".LS", LCOND, 9,
+ ".GE", LCOND, 10,
+ ".LT", LCOND, 11,
+ ".GT", LCOND, 12,
+ ".LE", LCOND, 13,
+ ".AL", LCOND, Always,
+
+ ".U", LS, C_UBIT,
+ ".S", LS, C_SBIT,
+ ".W", LS, C_WBIT,
+ ".P", LS, C_PBIT,
+ ".PW", LS, C_WBIT|C_PBIT,
+ ".WP", LS, C_WBIT|C_PBIT,
+
+ ".F", LS, C_FBIT,
+
+ ".IBW", LS, C_WBIT|C_PBIT|C_UBIT,
+ ".IAW", LS, C_WBIT|C_UBIT,
+ ".DBW", LS, C_WBIT|C_PBIT,
+ ".DAW", LS, C_WBIT,
+ ".IB", LS, C_PBIT|C_UBIT,
+ ".IA", LS, C_UBIT,
+ ".DB", LS, C_PBIT,
+ ".DA", LS, 0,
+
+ "@", LAT, 0,
+
+ "AND", LTYPE1, AAND,
+ "EOR", LTYPE1, AEOR,
+ "SUB", LTYPE1, ASUB,
+ "RSB", LTYPE1, ARSB,
+ "ADD", LTYPE1, AADD,
+ "ADC", LTYPE1, AADC,
+ "SBC", LTYPE1, ASBC,
+ "RSC", LTYPE1, ARSC,
+ "ORR", LTYPE1, AORR,
+ "BIC", LTYPE1, ABIC,
+
+ "SLL", LTYPE1, ASLL,
+ "SRL", LTYPE1, ASRL,
+ "SRA", LTYPE1, ASRA,
+
+ "MUL", LTYPE1, AMUL,
+ "MULA", LTYPEN, AMULA,
+ "DIV", LTYPE1, ADIV,
+ "MOD", LTYPE1, AMOD,
+
+ "MULL", LTYPEM, AMULL,
+ "MULAL", LTYPEM, AMULAL,
+ "MULLU", LTYPEM, AMULLU,
+ "MULALU", LTYPEM, AMULALU,
+
+ "MVN", LTYPE2, AMVN, /* op2 ignored */
+
+ "MOVB", LTYPE3, AMOVB,
+ "MOVBU", LTYPE3, AMOVBU,
+ "MOVH", LTYPE3, AMOVH,
+ "MOVHU", LTYPE3, AMOVHU,
+ "MOVW", LTYPE3, AMOVW,
+
+ "MOVD", LTYPE3, AMOVD,
+ "MOVDF", LTYPE3, AMOVDF,
+ "MOVDW", LTYPE3, AMOVDW,
+ "MOVF", LTYPE3, AMOVF,
+ "MOVFD", LTYPE3, AMOVFD,
+ "MOVFW", LTYPE3, AMOVFW,
+ "MOVWD", LTYPE3, AMOVWD,
+ "MOVWF", LTYPE3, AMOVWF,
+
+ "LDREX", LTYPE3, ALDREX,
+ "LDREXD", LTYPE3, ALDREXD,
+ "STREX", LTYPE9, ASTREX,
+ "STREXD", LTYPE9, ASTREXD,
+
+/*
+ "ABSF", LTYPEI, AABSF,
+ "ABSD", LTYPEI, AABSD,
+ "NEGF", LTYPEI, ANEGF,
+ "NEGD", LTYPEI, ANEGD,
+ "SQTF", LTYPEI, ASQTF,
+ "SQTD", LTYPEI, ASQTD,
+ "RNDF", LTYPEI, ARNDF,
+ "RNDD", LTYPEI, ARNDD,
+ "URDF", LTYPEI, AURDF,
+ "URDD", LTYPEI, AURDD,
+ "NRMF", LTYPEI, ANRMF,
+ "NRMD", LTYPEI, ANRMD,
+*/
+
+ "SQRTF", LTYPEI, ASQRTF,
+ "SQRTD", LTYPEI, ASQRTD,
+ "CMPF", LTYPEL, ACMPF,
+ "CMPD", LTYPEL, ACMPD,
+ "ADDF", LTYPEK, AADDF,
+ "ADDD", LTYPEK, AADDD,
+ "SUBF", LTYPEK, ASUBF,
+ "SUBD", LTYPEK, ASUBD,
+ "MULF", LTYPEK, AMULF,
+ "MULD", LTYPEK, AMULD,
+ "DIVF", LTYPEK, ADIVF,
+ "DIVD", LTYPEK, ADIVD,
+
+ "B", LTYPE4, AB,
+ "BL", LTYPE4, ABL,
+ "BX", LTYPEBX, ABX,
+
+ "BEQ", LTYPE5, ABEQ,
+ "BNE", LTYPE5, ABNE,
+ "BCS", LTYPE5, ABCS,
+ "BHS", LTYPE5, ABHS,
+ "BCC", LTYPE5, ABCC,
+ "BLO", LTYPE5, ABLO,
+ "BMI", LTYPE5, ABMI,
+ "BPL", LTYPE5, ABPL,
+ "BVS", LTYPE5, ABVS,
+ "BVC", LTYPE5, ABVC,
+ "BHI", LTYPE5, ABHI,
+ "BLS", LTYPE5, ABLS,
+ "BGE", LTYPE5, ABGE,
+ "BLT", LTYPE5, ABLT,
+ "BGT", LTYPE5, ABGT,
+ "BLE", LTYPE5, ABLE,
+ "BCASE", LTYPE5, ABCASE,
+
+ "SWI", LTYPE6, ASWI,
+
+ "CMP", LTYPE7, ACMP,
+ "TST", LTYPE7, ATST,
+ "TEQ", LTYPE7, ATEQ,
+ "CMN", LTYPE7, ACMN,
+
+ "MOVM", LTYPE8, AMOVM,
+
+ "SWPBU", LTYPE9, ASWPBU,
+ "SWPW", LTYPE9, ASWPW,
+
+ "RET", LTYPEA, ARET,
+ "RFE", LTYPEA, ARFE,
+
+ "TEXT", LTYPEB, ATEXT,
+ "GLOBL", LTYPEB, AGLOBL,
+ "DATA", LTYPEC, ADATA,
+ "CASE", LTYPED, ACASE,
+ "END", LTYPEE, AEND,
+ "WORD", LTYPEH, AWORD,
+ "NOP", LTYPEI, ANOP,
+
+ "MCR", LTYPEJ, 0,
+ "MRC", LTYPEJ, 1,
+ 0
+};
+
+void
+cinit(void)
+{
+ Sym *s;
+ int i;
+
+ nullgen.sym = S;
+ nullgen.offset = 0;
+ nullgen.type = D_NONE;
+ nullgen.name = D_NONE;
+ nullgen.reg = NREG;
+ if(FPCHIP)
+ nullgen.dval = 0;
+ for(i=0; i<sizeof(nullgen.sval); i++)
+ nullgen.sval[i] = 0;
+
+ nerrors = 0;
+ iostack = I;
+ iofree = I;
+ peekc = IGN;
+ nhunk = 0;
+ for(i=0; i<NHASH; i++)
+ hash[i] = S;
+ for(i=0; itab[i].name; i++) {
+ s = slookup(itab[i].name);
+ s->type = itab[i].type;
+ s->value = itab[i].value;
+ }
+
+ pathname = allocn(pathname, 0, 100);
+ if(getwd(pathname, 99) == 0) {
+ pathname = allocn(pathname, 100, 900);
+ if(getwd(pathname, 999) == 0)
+ strcpy(pathname, "/?");
+ }
+}
+
+void
+syminit(Sym *s)
+{
+
+ s->type = LNAME;
+ s->value = 0;
+}
+
+int
+isreg(Gen *g)
+{
+
+ USED(g);
+ return 1;
+}
+
+void
+cclean(void)
+{
+
+ outcode(AEND, Always, &nullgen, NREG, &nullgen);
+ Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+ Bputc(&obuf, ANAME);
+ Bputc(&obuf, t); /* type */
+ Bputc(&obuf, s); /* sym */
+ while(*n) {
+ Bputc(&obuf, *n);
+ n++;
+ }
+ Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+ long l;
+ int i;
+ char *n;
+ Ieee e;
+
+ Bputc(&obuf, a->type);
+ Bputc(&obuf, a->reg);
+ Bputc(&obuf, s);
+ Bputc(&obuf, a->name);
+ switch(a->type) {
+ default:
+ print("unknown type %d\n", a->type);
+ exits("arg");
+
+ case D_NONE:
+ case D_REG:
+ case D_FREG:
+ case D_PSR:
+ case D_FPCR:
+ break;
+
+ case D_REGREG:
+ Bputc(&obuf, a->offset);
+ break;
+
+ case D_OREG:
+ case D_CONST:
+ case D_BRANCH:
+ case D_SHIFT:
+ l = a->offset;
+ Bputc(&obuf, l);
+ Bputc(&obuf, l>>8);
+ Bputc(&obuf, l>>16);
+ Bputc(&obuf, l>>24);
+ break;
+
+ case D_SCONST:
+ n = a->sval;
+ for(i=0; i<NSNAME; i++) {
+ Bputc(&obuf, *n);
+ n++;
+ }
+ break;
+
+ case D_FCONST:
+ ieeedtod(&e, a->dval);
+ Bputc(&obuf, e.l);
+ Bputc(&obuf, e.l>>8);
+ Bputc(&obuf, e.l>>16);
+ Bputc(&obuf, e.l>>24);
+ Bputc(&obuf, e.h);
+ Bputc(&obuf, e.h>>8);
+ Bputc(&obuf, e.h>>16);
+ Bputc(&obuf, e.h>>24);
+ break;
+ }
+}
+
+static int bcode[] =
+{
+ ABEQ,
+ ABNE,
+ ABCS,
+ ABCC,
+ ABMI,
+ ABPL,
+ ABVS,
+ ABVC,
+ ABHI,
+ ABLS,
+ ABGE,
+ ABLT,
+ ABGT,
+ ABLE,
+ AB,
+ ANOP,
+};
+
+void
+outcode(int a, int scond, Gen *g1, int reg, Gen *g2)
+{
+ int sf, st, t;
+ Sym *s;
+
+ /* hack to make B.NE etc. work: turn it into the corresponding conditional */
+ if(a == AB){
+ a = bcode[scond&0xf];
+ scond = (scond & ~0xf) | Always;
+ }
+
+ if(pass == 1)
+ goto out;
+jackpot:
+ sf = 0;
+ s = g1->sym;
+ while(s != S) {
+ sf = s->sym;
+ if(sf < 0 || sf >= NSYM)
+ sf = 0;
+ t = g1->name;
+ if(h[sf].type == t)
+ if(h[sf].sym == s)
+ break;
+ zname(s->name, t, sym);
+ s->sym = sym;
+ h[sym].sym = s;
+ h[sym].type = t;
+ sf = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ break;
+ }
+ st = 0;
+ s = g2->sym;
+ while(s != S) {
+ st = s->sym;
+ if(st < 0 || st >= NSYM)
+ st = 0;
+ t = g2->name;
+ if(h[st].type == t)
+ if(h[st].sym == s)
+ break;
+ zname(s->name, t, sym);
+ s->sym = sym;
+ h[sym].sym = s;
+ h[sym].type = t;
+ st = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ if(st == sf)
+ goto jackpot;
+ break;
+ }
+ Bputc(&obuf, a);
+ Bputc(&obuf, scond);
+ Bputc(&obuf, reg);
+ Bputc(&obuf, lineno);
+ Bputc(&obuf, lineno>>8);
+ Bputc(&obuf, lineno>>16);
+ Bputc(&obuf, lineno>>24);
+ zaddr(g1, sf);
+ zaddr(g2, st);
+
+out:
+ if(a != AGLOBL && a != ADATA)
+ pc++;
+}
+
+void
+outhist(void)
+{
+ Gen g;
+ Hist *h;
+ char *p, *q, *op, c;
+ int n;
+
+ g = nullgen;
+ c = pathchar();
+ for(h = hist; h != H; h = h->link) {
+ p = h->name;
+ op = 0;
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && p && p[1] == ':'){
+ p += 2;
+ c = *p;
+ }
+ if(p && p[0] != c && h->offset == 0 && pathname){
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && pathname[1] == ':') {
+ op = p;
+ p = pathname+2;
+ c = *p;
+ } else if(pathname[0] == c){
+ op = p;
+ p = pathname;
+ }
+ }
+ while(p) {
+ q = strchr(p, c);
+ if(q) {
+ n = q-p;
+ if(n == 0){
+ n = 1; /* leading "/" */
+ *p = '/'; /* don't emit "\" on windows */
+ }
+ q++;
+ } else {
+ n = strlen(p);
+ q = 0;
+ }
+ if(n) {
+ Bputc(&obuf, ANAME);
+ Bputc(&obuf, D_FILE); /* type */
+ Bputc(&obuf, 1); /* sym */
+ Bputc(&obuf, '<');
+ Bwrite(&obuf, p, n);
+ Bputc(&obuf, 0);
+ }
+ p = q;
+ if(p == 0 && op) {
+ p = op;
+ op = 0;
+ }
+ }
+ g.offset = h->offset;
+
+ Bputc(&obuf, AHISTORY);
+ Bputc(&obuf, Always);
+ Bputc(&obuf, 0);
+ Bputc(&obuf, h->line);
+ Bputc(&obuf, h->line>>8);
+ Bputc(&obuf, h->line>>16);
+ Bputc(&obuf, h->line>>24);
+ zaddr(&nullgen, 0);
+ zaddr(&g, 0);
+ }
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/5a/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=5a
+
+OFILES=\
+ y.tab.$O\
+ lex.$O\
+
+HFILES=\
+ ../5c/5.out.h\
+ y.tab.h\
+ a.h\
+
+YFILES=a.y\
+
+LIBS=cc bio 9 # order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS= $CFLAGS -I../include
+
+lex.$O: ../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+ cd ../cc
+ mk $MKFLAGS install
+ mk $MKFLAGS clean
--- /dev/null
+++ b/utils/5c/5.out.h
@@ -1,0 +1,213 @@
+#define NSNAME 8
+#define NSYM 50
+#define NREG 16
+
+#define NOPROF (1<<0)
+#define DUPOK (1<<1)
+#define ALLTHUMBS (1<<2)
+
+#define REGRET 0
+#define REGARG 0
+/* compiler allocates R1 up as temps */
+/* compiler allocates register variables R2 up */
+#define REGMIN 2
+#define REGMAX 8
+#define REGEXT 10
+/* compiler allocates external registers R10 down */
+#define REGTMP 11
+#define REGSB 12
+#define REGSP 13
+#define REGLINK 14
+#define REGPC 15
+
+#define REGTMPT 7 /* used by the loader for thumb code */
+
+#define NFREG 8
+#define FREGRET 0
+#define FREGEXT 7
+#define FREGTMP 15
+/* compiler allocates register variables F0 up */
+/* compiler allocates external registers F7 down */
+
+enum as
+{
+ AXXX,
+
+ AAND,
+ AEOR,
+ ASUB,
+ ARSB,
+ AADD,
+ AADC,
+ ASBC,
+ ARSC,
+ ATST,
+ ATEQ,
+ ACMP,
+ ACMN,
+ AORR,
+ ABIC,
+
+ AMVN,
+
+ AB,
+ ABL,
+
+/*
+ * Do not reorder or fragment the conditional branch
+ * opcodes, or the predication code will break
+ */
+ ABEQ,
+ ABNE,
+ ABCS,
+ ABHS,
+ ABCC,
+ ABLO,
+ ABMI,
+ ABPL,
+ ABVS,
+ ABVC,
+ ABHI,
+ ABLS,
+ ABGE,
+ ABLT,
+ ABGT,
+ ABLE,
+
+ AMOVWD,
+ AMOVWF,
+ AMOVDW,
+ AMOVFW,
+ AMOVFD,
+ AMOVDF,
+ AMOVF,
+ AMOVD,
+
+ ACMPF,
+ ACMPD,
+ AADDF,
+ AADDD,
+ ASUBF,
+ ASUBD,
+ AMULF,
+ AMULD,
+ ADIVF,
+ ADIVD,
+// ASQRTF,
+// ASQRTD,
+
+ ASRL,
+ ASRA,
+ ASLL,
+ AMULU,
+ ADIVU,
+ AMUL,
+ ADIV,
+ AMOD,
+ AMODU,
+
+ AMOVB,
+ AMOVBU,
+ AMOVH,
+ AMOVHU,
+ AMOVW,
+ AMOVM,
+ ASWPBU,
+ ASWPW,
+
+ ANOP,
+ ARFE,
+ ASWI,
+ AMULA,
+
+ ADATA,
+ AGLOBL,
+ AGOK,
+ AHISTORY,
+ ANAME,
+ ARET,
+ ATEXT,
+ AWORD,
+ ADYNT,
+ AINIT,
+ ABCASE,
+ ACASE,
+
+ AEND,
+
+ AMULL,
+ AMULAL,
+ AMULLU,
+ AMULALU,
+
+ ABX,
+ ABXRET,
+ ADWORD,
+
+ ASIGNAME,
+
+ /* moved here to preserve values of older identifiers */
+ ASQRTF,
+ ASQRTD,
+
+ ALDREX,
+ ASTREX,
+
+ ALDREXD,
+ ASTREXD,
+
+ ALAST,
+};
+
+/* scond byte */
+#define C_SCOND ((1<<4)-1)
+#define C_SBIT (1<<4)
+#define C_PBIT (1<<5)
+#define C_WBIT (1<<6)
+#define C_FBIT (1<<7) /* psr flags-only */
+#define C_UBIT (1<<7) /* up bit */
+
+/* type/name */
+#define D_GOK 0
+#define D_NONE 1
+
+/* type */
+#define D_BRANCH (D_NONE+1)
+#define D_OREG (D_NONE+2)
+#define D_CONST (D_NONE+7)
+#define D_FCONST (D_NONE+8)
+#define D_SCONST (D_NONE+9)
+#define D_PSR (D_NONE+10)
+#define D_REG (D_NONE+12)
+#define D_FREG (D_NONE+13)
+#define D_FILE (D_NONE+16)
+#define D_OCONST (D_NONE+17)
+#define D_FILE1 (D_NONE+18)
+
+#define D_SHIFT (D_NONE+19)
+#define D_FPCR (D_NONE+20)
+#define D_REGREG (D_NONE+21)
+#define D_ADDR (D_NONE+22)
+
+/* name */
+#define D_EXTERN (D_NONE+3)
+#define D_STATIC (D_NONE+4)
+#define D_AUTO (D_NONE+5)
+#define D_PARAM (D_NONE+6)
+
+/*
+ * this is the ranlib header
+ */
+#define SYMDEF "__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef struct ieee Ieee;
+struct ieee
+{
+ long l; /* contains ls-man 0xffffffff */
+ long h; /* contains sign 0x80000000
+ exp 0x7ff00000
+ ms-man 0x000fffff */
+};
--- /dev/null
+++ b/utils/5c/cgen.c
@@ -1,0 +1,1168 @@
+#include "gc.h"
+
+void
+cgen(Node *n, Node *nn)
+{
+ cgenrel(n, nn, 0);
+}
+
+void
+cgenrel(Node *n, Node *nn, int inrel)
+{
+ Node *l, *r;
+ Prog *p1;
+ Node nod, nod1, nod2, nod3, nod4;
+ int o, t;
+ long v, curs;
+
+ if(debug['g']) {
+ prtree(nn, "cgen lhs");
+ prtree(n, "cgen");
+ }
+ if(n == Z || n->type == T)
+ return;
+ if(typesuv[n->type->etype]) {
+ sugen(n, nn, n->type->width);
+ return;
+ }
+ l = n->left;
+ r = n->right;
+ o = n->op;
+ if(n->addable >= INDEXED) {
+ if(nn == Z) {
+ switch(o) {
+ default:
+ nullwarn(Z, Z);
+ break;
+ case OINDEX:
+ nullwarn(l, r);
+ break;
+ }
+ return;
+ }
+ gmove(n, nn);
+ return;
+ }
+ curs = cursafe;
+
+ if(n->complex >= FNX)
+ if(l->complex >= FNX)
+ if(r != Z && r->complex >= FNX)
+ switch(o) {
+ default:
+ regret(&nod, r);
+ cgen(r, &nod);
+
+ regsalloc(&nod1, r);
+ gopcode(OAS, &nod, Z, &nod1);
+
+ regfree(&nod);
+ nod = *n;
+ nod.right = &nod1;
+ cgen(&nod, nn);
+ return;
+
+ case OFUNC:
+ case OCOMMA:
+ case OANDAND:
+ case OOROR:
+ case OCOND:
+ case ODOT:
+ break;
+ }
+
+ switch(o) {
+ default:
+ diag(n, "unknown op in cgen: %O", o);
+ break;
+
+ case OAS:
+ if(l->op == OBIT)
+ goto bitas;
+ if(l->addable >= INDEXED && l->complex < FNX) {
+ if(nn != Z || r->addable < INDEXED) {
+ if(r->complex >= FNX && nn == Z)
+ regret(&nod, r);
+ else
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ gmove(&nod, l);
+ if(nn != Z)
+ gmove(&nod, nn);
+ regfree(&nod);
+ } else
+ gmove(r, l);
+ break;
+ }
+ if(l->complex >= r->complex) {
+ reglcgen(&nod1, l, Z);
+ if(r->addable >= INDEXED) {
+ gmove(r, &nod1);
+ if(nn != Z)
+ gmove(r, nn);
+ regfree(&nod1);
+ break;
+ }
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ reglcgen(&nod1, l, Z);
+ }
+ gmove(&nod, &nod1);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ bitas:
+ n = l->left;
+ regalloc(&nod, r, nn);
+ if(l->complex >= r->complex) {
+ reglcgen(&nod1, n, Z);
+ cgen(r, &nod);
+ } else {
+ cgen(r, &nod);
+ reglcgen(&nod1, n, Z);
+ }
+ regalloc(&nod2, n, Z);
+ gopcode(OAS, &nod1, Z, &nod2);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+
+ case OBIT:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ bitload(n, &nod, Z, Z, nn);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ break;
+
+ case ODIV:
+ case OMOD:
+ if(nn != Z)
+ if((t = vlog(r)) >= 0) {
+ /* signed div/mod by constant power of 2 */
+ cgen(l, nn);
+ gopcode(OGE, nodconst(0), nn, Z);
+ p1 = p;
+ if(o == ODIV) {
+ gopcode(OADD, nodconst((1<<t)-1), Z, nn);
+ patch(p1, pc);
+ gopcode(OASHR, nodconst(t), Z, nn);
+ } else {
+ gopcode(OSUB, nn, nodconst(0), nn);
+ gopcode(OAND, nodconst((1<<t)-1), Z, nn);
+ gopcode(OSUB, nn, nodconst(0), nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ gopcode(OAND, nodconst((1<<t)-1), Z, nn);
+ patch(p1, pc);
+ }
+ break;
+ }
+ goto muldiv;
+
+ case OSUB:
+ if(nn != Z)
+ if(l->op == OCONST)
+ if(!typefd[n->type->etype]) {
+ cgen(r, nn);
+ gopcode(o, Z, l, nn);
+ break;
+ }
+ case OADD:
+ case OAND:
+ case OOR:
+ case OXOR:
+ case OLSHR:
+ case OASHL:
+ case OASHR:
+ /*
+ * immediate operands
+ */
+ if(nn != Z)
+ if(r->op == OCONST)
+ if(!typefd[n->type->etype]) {
+ cgen(l, nn);
+ if(r->vconst == 0)
+ if(o != OAND)
+ break;
+ if(nn != Z)
+ gopcode(o, r, Z, nn);
+ break;
+ }
+
+ case OLMUL:
+ case OLDIV:
+ case OLMOD:
+ case OMUL:
+ muldiv:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ if(o == OMUL || o == OLMUL) {
+ if(mulcon(n, nn))
+ break;
+ }
+ if(l->complex >= r->complex) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ gopcode(o, &nod1, Z, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ regalloc(&nod1, l, Z);
+ cgen(l, &nod1);
+ gopcode(o, &nod, &nod1, &nod);
+ }
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ case OASLSHR:
+ case OASASHL:
+ case OASASHR:
+ case OASAND:
+ case OASADD:
+ case OASSUB:
+ case OASXOR:
+ case OASOR:
+ if(l->op == OBIT)
+ goto asbitop;
+ if(r->op == OCONST)
+ if(!typefd[r->type->etype])
+ if(!typefd[n->type->etype]) {
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ regalloc(&nod, r, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ gopcode(o, r, Z, &nod);
+ gopcode(OAS, &nod, Z, &nod2);
+
+ regfree(&nod);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+ }
+
+ case OASLMUL:
+ case OASLDIV:
+ case OASLMOD:
+ case OASMUL:
+ case OASDIV:
+ case OASMOD:
+ if(l->op == OBIT)
+ goto asbitop;
+ if(l->complex >= r->complex) {
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ } else {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ }
+
+ regalloc(&nod, n, nn);
+ gmove(&nod2, &nod);
+ gopcode(o, &nod1, Z, &nod);
+ gmove(&nod, &nod2);
+ if(nn != Z)
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ regfree(&nod1);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ asbitop:
+ regalloc(&nod4, n, nn);
+ if(l->complex >= r->complex) {
+ bitload(l, &nod, &nod1, &nod2, &nod4);
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ } else {
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ bitload(l, &nod, &nod1, &nod2, &nod4);
+ }
+ gmove(&nod, &nod4);
+ gopcode(o, &nod3, Z, &nod4);
+ regfree(&nod3);
+ gmove(&nod4, &nod);
+ regfree(&nod4);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+
+ case OADDR:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ lcgen(l, nn);
+ break;
+
+ case OFUNC:
+ if(l->complex >= FNX) {
+ if(l->op != OIND)
+ diag(n, "bad function call");
+
+ regret(&nod, l->left);
+ cgen(l->left, &nod);
+ regsalloc(&nod1, l->left);
+ gopcode(OAS, &nod, Z, &nod1);
+ regfree(&nod);
+
+ nod = *n;
+ nod.left = &nod2;
+ nod2 = *l;
+ nod2.left = &nod1;
+ nod2.complex = 1;
+ cgen(&nod, nn);
+
+ return;
+ }
+ if(REGARG >= 0)
+ o = reg[REGARG];
+ gargs(r, &nod, &nod1);
+ if(l->addable < INDEXED) {
+ reglcgen(&nod, l, Z);
+ gopcode(OFUNC, Z, Z, &nod);
+ regfree(&nod);
+ } else
+ gopcode(OFUNC, Z, Z, l);
+ if(REGARG >= 0)
+ if(o != reg[REGARG])
+ reg[REGARG]--;
+ if(nn != Z) {
+ regret(&nod, n);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ }
+ break;
+
+ case OIND:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ regialloc(&nod, n, nn);
+ r = l;
+ while(r->op == OADD)
+ r = r->right;
+ if(sconst(r) && (v = r->vconst+nod.xoffset) > -4096 && v < 4096) {
+ v = r->vconst;
+ r->vconst = 0;
+ cgen(l, &nod);
+ nod.xoffset += v;
+ r->vconst = v;
+ } else
+ cgen(l, &nod);
+ regind(&nod, n);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ break;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OLO:
+ case OLS:
+ case OHI:
+ case OHS:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ boolgen(n, 1, nn);
+ break;
+
+ case OANDAND:
+ case OOROR:
+ boolgen(n, 1, nn);
+ if(nn == Z)
+ patch(p, pc);
+ break;
+
+ case ONOT:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ boolgen(n, 1, nn);
+ break;
+
+ case OCOMMA:
+ cgen(l, Z);
+ cgen(r, nn);
+ break;
+
+ case OCAST:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ /*
+ * convert from types l->n->nn
+ */
+ if(nocast(l->type, n->type)) {
+ if(nocast(n->type, nn->type)) {
+ cgen(l, nn);
+ break;
+ }
+ }
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ regalloc(&nod1, n, &nod);
+ if(inrel)
+ gmover(&nod, &nod1);
+ else
+ gopcode(OAS, &nod, Z, &nod1);
+ gopcode(OAS, &nod1, Z, nn);
+ regfree(&nod1);
+ regfree(&nod);
+ break;
+
+ case ODOT:
+ sugen(l, nodrat, l->type->width);
+ if(nn != Z) {
+ warn(n, "non-interruptable temporary");
+ nod = *nodrat;
+ if(!r || r->op != OCONST) {
+ diag(n, "DOT and no offset");
+ break;
+ }
+ nod.xoffset += (long)r->vconst;
+ nod.type = n->type;
+ cgen(&nod, nn);
+ }
+ break;
+
+ case OCOND:
+ bcgen(l, 1);
+ p1 = p;
+ cgen(r->left, nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ cgen(r->right, nn);
+ patch(p1, pc);
+ break;
+
+ case OPOSTINC:
+ case OPOSTDEC:
+ v = 1;
+ if(l->type->etype == TIND)
+ v = l->type->link->width;
+ if(o == OPOSTDEC)
+ v = -v;
+ if(l->op == OBIT)
+ goto bitinc;
+ if(nn == Z)
+ goto pre;
+
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+
+ regalloc(&nod, l, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ regalloc(&nod1, l, Z);
+ if(typefd[l->type->etype]) {
+ regalloc(&nod3, l, Z);
+ if(v < 0) {
+ gopcode(OAS, nodfconst(-v), Z, &nod3);
+ gopcode(OSUB, &nod3, &nod, &nod1);
+ } else {
+ gopcode(OAS, nodfconst(v), Z, &nod3);
+ gopcode(OADD, &nod3, &nod, &nod1);
+ }
+ regfree(&nod3);
+ } else
+ gopcode(OADD, nodconst(v), &nod, &nod1);
+ gopcode(OAS, &nod1, Z, &nod2);
+
+ regfree(&nod);
+ regfree(&nod1);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ case OPREINC:
+ case OPREDEC:
+ v = 1;
+ if(l->type->etype == TIND)
+ v = l->type->link->width;
+ if(o == OPREDEC)
+ v = -v;
+ if(l->op == OBIT)
+ goto bitinc;
+
+ pre:
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+
+ regalloc(&nod, l, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ if(typefd[l->type->etype]) {
+ regalloc(&nod3, l, Z);
+ if(v < 0) {
+ gopcode(OAS, nodfconst(-v), Z, &nod3);
+ gopcode(OSUB, &nod3, Z, &nod);
+ } else {
+ gopcode(OAS, nodfconst(v), Z, &nod3);
+ gopcode(OADD, &nod3, Z, &nod);
+ }
+ regfree(&nod3);
+ } else
+ gopcode(OADD, nodconst(v), Z, &nod);
+ gopcode(OAS, &nod, Z, &nod2);
+ if(nn && l->op == ONAME) /* in x=++i, emit USED(i) */
+ gins(ANOP, l, Z);
+
+ regfree(&nod);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ bitinc:
+ if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+ bitload(l, &nod, &nod1, &nod2, Z);
+ gopcode(OAS, &nod, Z, nn);
+ gopcode(OADD, nodconst(v), Z, &nod);
+ bitstore(l, &nod, &nod1, &nod2, Z);
+ break;
+ }
+ bitload(l, &nod, &nod1, &nod2, nn);
+ gopcode(OADD, nodconst(v), Z, &nod);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+ }
+ cursafe = curs;
+ return;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+ Node *r;
+ long v;
+
+ regialloc(t, n, nn);
+ if(n->op == OIND) {
+ r = n->left;
+ while(r->op == OADD)
+ r = r->right;
+ if(sconst(r) && (v = r->vconst+t->xoffset) > -4096 && v < 4096) {
+ v = r->vconst;
+ r->vconst = 0;
+ lcgen(n, t);
+ t->xoffset += v;
+ r->vconst = v;
+ regind(t, n);
+ return;
+ }
+ } else if(n->op == OINDREG) {
+ if((v = n->xoffset) > -4096 && v < 4096) {
+ n->op = OREGISTER;
+ cgen(n, t);
+ t->xoffset += v;
+ n->op = OINDREG;
+ regind(t, n);
+ return;
+ }
+ }
+ lcgen(n, t);
+ regind(t, n);
+}
+
+void
+reglpcgen(Node *n, Node *nn, int f)
+{
+ Type *t;
+
+ t = nn->type;
+ nn->type = types[TLONG];
+ if(f)
+ reglcgen(n, nn, Z);
+ else {
+ regialloc(n, nn, Z);
+ lcgen(nn, n);
+ regind(n, nn);
+ }
+ nn->type = t;
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+ Prog *p1;
+ Node nod;
+
+ if(debug['g']) {
+ prtree(nn, "lcgen lhs");
+ prtree(n, "lcgen");
+ }
+ if(n == Z || n->type == T)
+ return;
+ if(nn == Z) {
+ nn = &nod;
+ regalloc(&nod, n, Z);
+ }
+ switch(n->op) {
+ default:
+ if(n->addable < INDEXED) {
+ diag(n, "unknown op in lcgen: %O", n->op);
+ break;
+ }
+ nod = *n;
+ nod.op = OADDR;
+ nod.left = n;
+ nod.right = Z;
+ nod.type = types[TIND];
+ gopcode(OAS, &nod, Z, nn);
+ break;
+
+ case OCOMMA:
+ cgen(n->left, n->left);
+ lcgen(n->right, nn);
+ break;
+
+ case OIND:
+ cgen(n->left, nn);
+ break;
+
+ case OCOND:
+ bcgen(n->left, 1);
+ p1 = p;
+ lcgen(n->right->left, nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ lcgen(n->right->right, nn);
+ patch(p1, pc);
+ break;
+ }
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+ if(n->type == T)
+ gbranch(OGOTO);
+ else
+ boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+ int o;
+ Prog *p1, *p2;
+ Node *l, *r, nod, nod1;
+ long curs;
+
+ if(debug['g']) {
+ prtree(nn, "boolgen lhs");
+ prtree(n, "boolgen");
+ }
+ curs = cursafe;
+ l = n->left;
+ r = n->right;
+ switch(n->op) {
+
+ default:
+ regalloc(&nod, n, nn);
+ cgen(n, &nod);
+ o = ONE;
+ if(true)
+ o = comrel[relindex(o)];
+ if(typefd[n->type->etype]) {
+ gopcode(o, nodfconst(0), &nod, Z);
+ } else
+ gopcode(o, nodconst(0), &nod, Z);
+ regfree(&nod);
+ goto com;
+
+ case OCONST:
+ o = vconst(n);
+ if(!true)
+ o = !o;
+ gbranch(OGOTO);
+ if(o) {
+ p1 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ }
+ goto com;
+
+ case OCOMMA:
+ cgen(l, Z);
+ boolgen(r, true, nn);
+ break;
+
+ case ONOT:
+ boolgen(l, !true, nn);
+ break;
+
+ case OCOND:
+ bcgen(l, 1);
+ p1 = p;
+ bcgen(r->left, true);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ bcgen(r->right, !true);
+ patch(p2, pc);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ goto com;
+
+ case OANDAND:
+ if(!true)
+ goto caseor;
+
+ caseand:
+ bcgen(l, true);
+ p1 = p;
+ bcgen(r, !true);
+ p2 = p;
+ patch(p1, pc);
+ gbranch(OGOTO);
+ patch(p2, pc);
+ goto com;
+
+ case OOROR:
+ if(!true)
+ goto caseand;
+
+ caseor:
+ bcgen(l, !true);
+ p1 = p;
+ bcgen(r, !true);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ goto com;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ o = n->op;
+ if(true)
+ o = comrel[relindex(o)];
+ if(l->complex >= FNX && r->complex >= FNX) {
+ regret(&nod, r);
+ cgenrel(r, &nod, 1);
+ regsalloc(&nod1, r);
+ gopcode(OAS, &nod, Z, &nod1);
+ regfree(&nod);
+ nod = *n;
+ nod.right = &nod1;
+ boolgen(&nod, true, nn);
+ break;
+ }
+ if(sconst(l)) {
+ regalloc(&nod, r, nn);
+ cgenrel(r, &nod, 1);
+ o = invrel[relindex(o)];
+ gopcode(o, l, &nod, Z);
+ regfree(&nod);
+ goto com;
+ }
+ if(sconst(r)) {
+ regalloc(&nod, l, nn);
+ cgenrel(l, &nod, 1);
+ gopcode(o, r, &nod, Z);
+ regfree(&nod);
+ goto com;
+ }
+ if(l->complex >= r->complex) {
+ regalloc(&nod1, l, nn);
+ cgenrel(l, &nod1, 1);
+ regalloc(&nod, r, Z);
+ cgenrel(r, &nod, 1);
+ } else {
+ regalloc(&nod, r, nn);
+ cgenrel(r, &nod, 1);
+ regalloc(&nod1, l, Z);
+ cgenrel(l, &nod1, 1);
+ }
+ gopcode(o, &nod, &nod1, Z);
+ regfree(&nod);
+ regfree(&nod1);
+
+ com:
+ if(nn != Z) {
+ p1 = p;
+ gopcode(OAS, nodconst(1), Z, nn);
+ gbranch(OGOTO);
+ p2 = p;
+ patch(p1, pc);
+ gopcode(OAS, nodconst(0), Z, nn);
+ patch(p2, pc);
+ }
+ break;
+ }
+ cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+ Prog *p1;
+ Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+ Type *t;
+ long pc1;
+ int i, m, c;
+
+ if(n == Z || n->type == T)
+ return;
+ if(debug['g']) {
+ prtree(nn, "sugen lhs");
+ prtree(n, "sugen");
+ }
+ if(nn == nodrat)
+ if(w > nrathole)
+ nrathole = w;
+ switch(n->op) {
+ case OIND:
+ if(nn == Z) {
+ nullwarn(n->left, Z);
+ break;
+ }
+
+ default:
+ goto copy;
+
+ case OCONST:
+ if(n->type && typev[n->type->etype]) {
+ if(nn == Z) {
+ nullwarn(n->left, Z);
+ break;
+ }
+
+ t = nn->type;
+ nn->type = types[TLONG];
+ reglcgen(&nod1, nn, Z);
+ nn->type = t;
+
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+ else
+ gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+ nod1.xoffset += SZ_LONG;
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+ else
+ gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+
+ regfree(&nod1);
+ break;
+ }
+ goto copy;
+
+ case ODOT:
+ l = n->left;
+ sugen(l, nodrat, l->type->width);
+ if(nn != Z) {
+ warn(n, "non-interruptable temporary");
+ nod1 = *nodrat;
+ r = n->right;
+ if(!r || r->op != OCONST) {
+ diag(n, "DOT and no offset");
+ break;
+ }
+ nod1.xoffset += (long)r->vconst;
+ nod1.type = n->type;
+ sugen(&nod1, nn, w);
+ }
+ break;
+
+ case OSTRUCT:
+ /*
+ * rewrite so lhs has no fn call
+ */
+ if(nn != Z && nn->complex >= FNX) {
+ nod1 = *n;
+ nod1.type = typ(TIND, n->type);
+ regret(&nod2, &nod1);
+ lcgen(nn, &nod2);
+ regsalloc(&nod0, &nod1);
+ gopcode(OAS, &nod2, Z, &nod0);
+ regfree(&nod2);
+
+ nod1 = *n;
+ nod1.op = OIND;
+ nod1.left = &nod0;
+ nod1.right = Z;
+ nod1.complex = 1;
+
+ sugen(n, &nod1, w);
+ return;
+ }
+
+ r = n->left;
+ for(t = n->type->link; t != T; t = t->down) {
+ l = r;
+ if(r->op == OLIST) {
+ l = r->left;
+ r = r->right;
+ }
+ if(nn == Z) {
+ cgen(l, nn);
+ continue;
+ }
+ /*
+ * hand craft *(&nn + o) = l
+ */
+ nod0 = znode;
+ nod0.op = OAS;
+ nod0.type = t;
+ nod0.left = &nod1;
+ nod0.right = l;
+
+ nod1 = znode;
+ nod1.op = OIND;
+ nod1.type = t;
+ nod1.left = &nod2;
+
+ nod2 = znode;
+ nod2.op = OADD;
+ nod2.type = typ(TIND, t);
+ nod2.left = &nod3;
+ nod2.right = &nod4;
+
+ nod3 = znode;
+ nod3.op = OADDR;
+ nod3.type = nod2.type;
+ nod3.left = nn;
+
+ nod4 = znode;
+ nod4.op = OCONST;
+ nod4.type = nod2.type;
+ nod4.vconst = t->offset;
+
+ ccom(&nod0);
+ acom(&nod0);
+ xcom(&nod0);
+ nod0.addable = 0;
+
+ cgen(&nod0, Z);
+ }
+ break;
+
+ case OAS:
+ if(nn == Z) {
+ if(n->addable < INDEXED)
+ sugen(n->right, n->left, w);
+ break;
+ }
+ sugen(n->right, nodrat, w);
+ warn(n, "non-interruptable temporary");
+ sugen(nodrat, n->left, w);
+ sugen(nodrat, nn, w);
+ break;
+
+ case OFUNC:
+ if(nn == Z) {
+ sugen(n, nodrat, w);
+ break;
+ }
+ if(nn->op != OIND) {
+ nn = new1(OADDR, nn, Z);
+ nn->type = types[TIND];
+ nn->addable = 0;
+ } else
+ nn = nn->left;
+ n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+ n->type = types[TVOID];
+ n->left->type = types[TVOID];
+ cgen(n, Z);
+ break;
+
+ case OCOND:
+ bcgen(n->left, 1);
+ p1 = p;
+ sugen(n->right->left, nn, w);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ sugen(n->right->right, nn, w);
+ patch(p1, pc);
+ break;
+
+ case OCOMMA:
+ cgen(n->left, Z);
+ sugen(n->right, nn, w);
+ break;
+ }
+ return;
+
+copy:
+ if(nn == Z)
+ return;
+ if(n->complex >= FNX && nn->complex >= FNX) {
+ t = nn->type;
+ nn->type = types[TLONG];
+ regialloc(&nod1, nn, Z);
+ lcgen(nn, &nod1);
+ regsalloc(&nod2, nn);
+ nn->type = t;
+
+ gopcode(OAS, &nod1, Z, &nod2);
+ regfree(&nod1);
+
+ nod2.type = typ(TIND, t);
+
+ nod1 = nod2;
+ nod1.op = OIND;
+ nod1.left = &nod2;
+ nod1.right = Z;
+ nod1.complex = 1;
+ nod1.type = t;
+
+ sugen(n, &nod1, w);
+ return;
+ }
+
+ w /= SZ_LONG;
+ if(w <= 2) {
+ if(n->complex > nn->complex) {
+ reglpcgen(&nod1, n, 1);
+ reglpcgen(&nod2, nn, 1);
+ } else {
+ reglpcgen(&nod2, nn, 1);
+ reglpcgen(&nod1, n, 1);
+ }
+ regalloc(&nod3, ®node, Z);
+ regalloc(&nod4, ®node, Z);
+ if(nod3.reg > nod4.reg){
+ /* code below assumes nod3 loaded first */
+ Node t = nod3; nod3 = nod4; nod4 = t;
+ }
+ nod0 = *nodconst((1<<nod3.reg)|(1<<nod4.reg));
+ if(w == 2 && nod1.xoffset == 0)
+ gmovm(&nod1, &nod0, 0);
+ else {
+ gmove(&nod1, &nod3);
+ if(w == 2) {
+ nod1.xoffset += SZ_LONG;
+ gmove(&nod1, &nod4);
+ }
+ }
+ if(w == 2 && nod2.xoffset == 0)
+ gmovm(&nod0, &nod2, 0);
+ else {
+ gmove(&nod3, &nod2);
+ if(w == 2) {
+ nod2.xoffset += SZ_LONG;
+ gmove(&nod4, &nod2);
+ }
+ }
+ regfree(&nod1);
+ regfree(&nod2);
+ regfree(&nod3);
+ regfree(&nod4);
+ return;
+ }
+
+ if(n->complex > nn->complex) {
+ reglpcgen(&nod1, n, 0);
+ reglpcgen(&nod2, nn, 0);
+ } else {
+ reglpcgen(&nod2, nn, 0);
+ reglpcgen(&nod1, n, 0);
+ }
+
+ m = 0;
+ for(c = 0; c < w && c < 4; c++) {
+ i = tmpreg();
+ if (i == 0)
+ break;
+ reg[i]++;
+ m |= 1<<i;
+ }
+ nod4 = *(nodconst(m));
+ if(w < 3*c) {
+ for (; w>c; w-=c) {
+ gmovm(&nod1, &nod4, 1);
+ gmovm(&nod4, &nod2, 1);
+ }
+ goto out;
+ }
+
+ regalloc(&nod3, ®node, Z);
+ gopcode(OAS, nodconst(w/c), Z, &nod3);
+ w %= c;
+
+ pc1 = pc;
+ gmovm(&nod1, &nod4, 1);
+ gmovm(&nod4, &nod2, 1);
+
+ gopcode(OSUB, nodconst(1), Z, &nod3);
+ gopcode(OEQ, nodconst(0), &nod3, Z);
+ p->as = ABGT;
+ patch(p, pc1);
+ regfree(&nod3);
+
+out:
+ if (w) {
+ i = 0;
+ while (c>w) {
+ while ((m&(1<<i)) == 0)
+ i++;
+ m &= ~(1<<i);
+ reg[i] = 0;
+ c--;
+ i++;
+ }
+ nod4.vconst = m;
+ gmovm(&nod1, &nod4, 0);
+ gmovm(&nod4, &nod2, 0);
+ }
+ i = 0;
+ do {
+ while ((m&(1<<i)) == 0)
+ i++;
+ reg[i] = 0;
+ c--;
+ i++;
+ } while (c>0);
+ regfree(&nod1);
+ regfree(&nod2);
+}
--- /dev/null
+++ b/utils/5c/enam.c
@@ -1,0 +1,104 @@
+char* anames[] =
+{
+ "XXX",
+ "AND",
+ "EOR",
+ "SUB",
+ "RSB",
+ "ADD",
+ "ADC",
+ "SBC",
+ "RSC",
+ "TST",
+ "TEQ",
+ "CMP",
+ "CMN",
+ "ORR",
+ "BIC",
+ "MVN",
+ "B",
+ "BL",
+ "BEQ",
+ "BNE",
+ "BCS",
+ "BHS",
+ "BCC",
+ "BLO",
+ "BMI",
+ "BPL",
+ "BVS",
+ "BVC",
+ "BHI",
+ "BLS",
+ "BGE",
+ "BLT",
+ "BGT",
+ "BLE",
+ "MOVWD",
+ "MOVWF",
+ "MOVDW",
+ "MOVFW",
+ "MOVFD",
+ "MOVDF",
+ "MOVF",
+ "MOVD",
+ "CMPF",
+ "CMPD",
+ "ADDF",
+ "ADDD",
+ "SUBF",
+ "SUBD",
+ "MULF",
+ "MULD",
+ "DIVF",
+ "DIVD",
+ "SRL",
+ "SRA",
+ "SLL",
+ "MULU",
+ "DIVU",
+ "MUL",
+ "DIV",
+ "MOD",
+ "MODU",
+ "MOVB",
+ "MOVBU",
+ "MOVH",
+ "MOVHU",
+ "MOVW",
+ "MOVM",
+ "SWPBU",
+ "SWPW",
+ "NOP",
+ "RFE",
+ "SWI",
+ "MULA",
+ "DATA",
+ "GLOBL",
+ "GOK",
+ "HISTORY",
+ "NAME",
+ "RET",
+ "TEXT",
+ "WORD",
+ "DYNT",
+ "INIT",
+ "BCASE",
+ "CASE",
+ "END",
+ "MULL",
+ "MULAL",
+ "MULLU",
+ "MULALU",
+ "BX",
+ "BXRET",
+ "DWORD",
+ "SIGNAME",
+ "SQRTF",
+ "SQRTD",
+ "LDREX",
+ "STREX",
+ "LDREXD",
+ "STREXD",
+ "LAST",
+};
--- /dev/null
+++ b/utils/5c/gc.h
@@ -1,0 +1,351 @@
+#include "../cc/cc.h"
+#include "../5c/5.out.h"
+
+/*
+ * 5c/arm
+ * Arm
+ */
+#define SZ_CHAR 1
+#define SZ_SHORT 2
+#define SZ_INT 4
+#define SZ_LONG 4
+#define SZ_IND 4
+#define SZ_FLOAT 4
+#define SZ_VLONG 8
+#define SZ_DOUBLE 8
+#define FNX 100
+
+typedef struct Adr Adr;
+typedef struct Prog Prog;
+typedef struct Case Case;
+typedef struct C1 C1;
+typedef struct Multab Multab;
+typedef struct Hintab Hintab;
+typedef struct Var Var;
+typedef struct Reg Reg;
+typedef struct Rgn Rgn;
+
+
+#define R0ISZERO 0
+
+struct Adr
+{
+ long offset;
+ double dval;
+ char sval[NSNAME];
+ Ieee ieee;
+
+ Sym* sym;
+ char type;
+ char reg;
+ char name;
+ char etype;
+};
+#define A ((Adr*)0)
+
+#define INDEXED 9
+struct Prog
+{
+ Adr from;
+ Adr to;
+ Prog* link;
+ long lineno;
+ char as;
+ char reg;
+ uchar scond;
+};
+#define P ((Prog*)0)
+
+struct Case
+{
+ Case* link;
+ vlong val;
+ long label;
+ char def;
+ char isv;
+};
+#define C ((Case*)0)
+
+struct C1
+{
+ vlong val;
+ long label;
+};
+
+struct Multab
+{
+ long val;
+ char code[20];
+};
+
+struct Hintab
+{
+ ushort val;
+ char hint[10];
+};
+
+struct Var
+{
+ long offset;
+ Sym* sym;
+ char name;
+ char etype;
+};
+
+struct Reg
+{
+ long pc;
+ long rpo; /* reverse post ordering */
+
+ Bits set;
+ Bits use1;
+ Bits use2;
+
+ Bits refbehind;
+ Bits refahead;
+ Bits calbehind;
+ Bits calahead;
+ Bits regdiff;
+ Bits act;
+
+ long regu;
+ long loop; /* could be shorter */
+
+
+ Reg* log5;
+ long active;
+
+ Reg* p1;
+ Reg* p2;
+ Reg* p2link;
+ Reg* s1;
+ Reg* s2;
+ Reg* link;
+ Prog* prog;
+};
+#define R ((Reg*)0)
+
+#define NRGN 600
+struct Rgn
+{
+ Reg* enter;
+ short cost;
+ short varno;
+ short regno;
+};
+
+EXTERN long breakpc;
+EXTERN long nbreak;
+EXTERN Case* cases;
+EXTERN Node constnode;
+EXTERN Node fconstnode;
+EXTERN long continpc;
+EXTERN long curarg;
+EXTERN long cursafe;
+EXTERN Prog* firstp;
+EXTERN Prog* lastp;
+EXTERN long maxargsafe;
+EXTERN int mnstring;
+EXTERN Multab multab[20];
+EXTERN int hintabsize;
+EXTERN Node* nodrat;
+EXTERN Node* nodret;
+EXTERN Node* nodsafe;
+EXTERN long nrathole;
+EXTERN long nstring;
+EXTERN Prog* p;
+EXTERN long pc;
+EXTERN Node regnode;
+EXTERN char string[NSNAME];
+EXTERN Sym* symrathole;
+EXTERN Node znode;
+EXTERN Prog zprog;
+EXTERN char reg[NREG+NFREG];
+EXTERN long exregoffset;
+EXTERN long exfregoffset;
+EXTERN int suppress;
+
+#define BLOAD(r) band(bnot(r->refbehind), r->refahead)
+#define BSTORE(r) band(bnot(r->calbehind), r->calahead)
+#define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z])
+#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z])
+
+#define bset(a,n) ((a).b[(n)/32]&(1L<<(n)%32))
+
+#define CLOAD 4
+#define CREF 5
+#define CINF 1000
+#define LOOP 3
+
+EXTERN Rgn region[NRGN];
+EXTERN Rgn* rgp;
+EXTERN int nregion;
+EXTERN int nvar;
+
+EXTERN Bits externs;
+EXTERN Bits params;
+EXTERN Bits consts;
+EXTERN Bits addrs;
+
+EXTERN long regbits;
+EXTERN long exregbits;
+
+EXTERN int change;
+
+EXTERN Reg* firstr;
+EXTERN Reg* lastr;
+EXTERN Reg zreg;
+EXTERN Reg* freer;
+EXTERN Var var[NVAR];
+EXTERN long* idom;
+EXTERN Reg** rpo2r;
+EXTERN long maxnr;
+
+extern char* anames[];
+extern Hintab hintab[];
+
+/*
+ * sgen.c
+ */
+void codgen(Node*, Node*);
+void gen(Node*);
+void noretval(int);
+void usedset(Node*, int);
+void xcom(Node*);
+int bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void cgen(Node*, Node*);
+void cgenrel(Node*, Node*, int);
+void reglcgen(Node*, Node*, Node*);
+void lcgen(Node*, Node*);
+void bcgen(Node*, int);
+void boolgen(Node*, int, Node*);
+void sugen(Node*, Node*, long);
+void layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void ginit(void);
+void gclean(void);
+void nextpc(void);
+void gargs(Node*, Node*, Node*);
+void garg1(Node*, Node*, Node*, int, Node**);
+Node* nodconst(long);
+Node* nod32const(vlong);
+Node* nodfconst(double);
+void nodreg(Node*, Node*, int);
+void regret(Node*, Node*);
+int tmpreg(void);
+void regalloc(Node*, Node*, Node*);
+void regfree(Node*);
+void regialloc(Node*, Node*, Node*);
+void regsalloc(Node*, Node*);
+void regaalloc1(Node*, Node*);
+void regaalloc(Node*, Node*);
+void regind(Node*, Node*);
+void gprep(Node*, Node*);
+void raddr(Node*, Prog*);
+void naddr(Node*, Adr*);
+void gmovm(Node*, Node*, int);
+void gmove(Node*, Node*);
+void gmover(Node*, Node*);
+void gins(int a, Node*, Node*);
+void gopcode(int, Node*, Node*, Node*);
+int samaddr(Node*, Node*);
+void gbranch(int);
+void patch(Prog*, long);
+int sconst(Node*);
+int sval(long);
+void gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int swcmp(void*, void*);
+void doswit(Node*);
+void swit1(C1*, int, long, Node*);
+void swit2(C1*, int, long, Node*, Node*);
+void casf(void);
+void bitload(Node*, Node*, Node*, Node*, Node*);
+void bitstore(Node*, Node*, Node*, Node*, Node*);
+long outstring(char*, long);
+int mulcon(Node*, Node*);
+Multab* mulcon0(long);
+void nullwarn(Node*, Node*);
+void gextern(Sym*, Node*, long, long);
+void outcode(void);
+void ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void listinit(void);
+int Pconv(Fmt*);
+int Aconv(Fmt*);
+int Dconv(Fmt*);
+int Sconv(Fmt*);
+int Nconv(Fmt*);
+int Bconv(Fmt*);
+int Rconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg* rega(void);
+int rcmp(void*, void*);
+void regopt(Prog*);
+void addmove(Reg*, int, int, int);
+Bits mkvar(Adr*, int);
+void prop(Reg*, Bits, Bits);
+void loopit(Reg*, long);
+void synch(Reg*, Bits);
+ulong allreg(ulong, Rgn*);
+void paint1(Reg*, int);
+ulong paint2(Reg*, int);
+void paint3(Reg*, int, long, int);
+void addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void peep(void);
+void excise(Reg*);
+Reg* uniqp(Reg*);
+Reg* uniqs(Reg*);
+int regtyp(Adr*);
+int regzer(Adr*);
+int anyvar(Adr*);
+int subprop(Reg*);
+int copyprop(Reg*);
+int shiftprop(Reg*);
+void constprop(Adr*, Adr*, Reg*);
+int copy1(Adr*, Adr*, Reg*, int);
+int copyu(Prog*, Adr*, Adr*);
+
+int copyas(Adr*, Adr*);
+int copyau(Adr*, Adr*);
+int copyau1(Prog*, Adr*);
+int copysub(Adr*, Adr*, Adr*, int);
+int copysub1(Prog*, Adr*, Adr*, int);
+
+long RtoB(int);
+long FtoB(int);
+int BtoR(long);
+int BtoF(long);
+
+void predicate(void);
+int isbranch(Prog *);
+int predicable(Prog *p);
+int modifiescpsr(Prog *p);
+
+#pragma varargck type "A" int
+#pragma varargck type "B" Bits
+#pragma varargck type "D" Adr*
+#pragma varargck type "N" Adr*
+#pragma varargck type "R" Adr*
+#pragma varargck type "P" Prog*
+#pragma varargck type "S" char*
--- /dev/null
+++ b/utils/5c/list.c
@@ -1,0 +1,304 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+ fmtinstall('A', Aconv);
+ fmtinstall('P', Pconv);
+ fmtinstall('S', Sconv);
+ fmtinstall('N', Nconv);
+ fmtinstall('B', Bconv);
+ fmtinstall('D', Dconv);
+ fmtinstall('R', Rconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+ char str[STRINGSZ], ss[STRINGSZ], *s;
+ Bits bits;
+ int i;
+
+ str[0] = 0;
+ bits = va_arg(fp->args, Bits);
+ while(bany(&bits)) {
+ i = bnum(bits);
+ if(str[0])
+ strcat(str, " ");
+ if(var[i].sym == S) {
+ sprint(ss, "$%ld", var[i].offset);
+ s = ss;
+ } else
+ s = var[i].sym->name;
+ if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+ break;
+ strcat(str, s);
+ bits.b[i/32] &= ~(1L << (i%32));
+ }
+ return fmtstrcpy(fp, str);
+}
+
+char *extra [] = {
+ ".EQ", ".NE", ".CS", ".CC",
+ ".MI", ".PL", ".VS", ".VC",
+ ".HI", ".LS", ".GE", ".LT",
+ ".GT", ".LE", "", ".NV",
+};
+
+int
+Pconv(Fmt *fp)
+{
+ char str[STRINGSZ], sc[20];
+ Prog *p;
+ int a, s;
+
+ p = va_arg(fp->args, Prog*);
+ a = p->as;
+ s = p->scond;
+ strcpy(sc, extra[s & C_SCOND]);
+ if(s & C_SBIT)
+ strcat(sc, ".S");
+ if(s & C_PBIT)
+ strcat(sc, ".P");
+ if(s & C_WBIT)
+ strcat(sc, ".W");
+ if(s & C_UBIT) /* ambiguous with FBIT */
+ strcat(sc, ".U");
+ if(a == AMOVM) {
+ if(p->from.type == D_CONST)
+ sprint(str, " %A%s %R,%D", a, sc, &p->from, &p->to);
+ else
+ if(p->to.type == D_CONST)
+ sprint(str, " %A%s %D,%R", a, sc, &p->from, &p->to);
+ else
+ sprint(str, " %A%s %D,%D", a, sc, &p->from, &p->to);
+ } else
+ if(a == ADATA)
+ sprint(str, " %A %D/%d,%D", a, &p->from, p->reg, &p->to);
+ else
+ if(p->as == ATEXT)
+ sprint(str, " %A %D,%d,%D", a, &p->from, p->reg, &p->to);
+ else
+ if(p->reg == NREG)
+ sprint(str, " %A%s %D,%D", a, sc, &p->from, &p->to);
+ else
+ if(p->from.type != D_FREG)
+ sprint(str, " %A%s %D,R%d,%D", a, sc, &p->from, p->reg, &p->to);
+ else
+ sprint(str, " %A%s %D,F%d,%D", a, sc, &p->from, p->reg, &p->to);
+ return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+ char *s;
+ int a;
+
+ a = va_arg(fp->args, int);
+ s = "?";
+ if(a >= AXXX && a < ALAST)
+ s = anames[a];
+ return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+ char *op;
+ int v;
+
+ a = va_arg(fp->args, Adr*);
+ switch(a->type) {
+
+ default:
+ sprint(str, "GOK-type(%d)", a->type);
+ break;
+
+ case D_NONE:
+ str[0] = 0;
+ if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+ sprint(str, "%N(R%d)(NONE)", a, a->reg);
+ break;
+
+ case D_CONST:
+ if(a->reg != NREG)
+ sprint(str, "$%N(R%d)", a, a->reg);
+ else
+ sprint(str, "$%N", a);
+ break;
+
+ case D_SHIFT:
+ v = a->offset;
+ op = "<<>>->@>" + (((v>>5) & 3) << 1);
+ if(v & (1<<4))
+ sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15);
+ else
+ sprint(str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31);
+ if(a->reg != NREG)
+ sprint(str+strlen(str), "(R%d)", a->reg);
+ break;
+
+ case D_OREG:
+ if(a->reg != NREG)
+ sprint(str, "%N(R%d)", a, a->reg);
+ else
+ sprint(str, "%N", a);
+ break;
+
+ case D_REG:
+ sprint(str, "R%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_FREG:
+ sprint(str, "F%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_PSR:
+ sprint(str, "PSR");
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(PSR)(REG)", a);
+ break;
+
+ case D_BRANCH:
+ sprint(str, "%ld(PC)", a->offset-pc);
+ break;
+
+ case D_FCONST:
+ sprint(str, "$%.17e", a->dval);
+ break;
+
+ case D_SCONST:
+ sprint(str, "$\"%S\"", a->sval);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Rconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+ int i, v;
+
+ a = va_arg(fp->args, Adr*);
+ sprint(str, "GOK-reglist");
+ switch(a->type) {
+ case D_CONST:
+ if(a->reg != NREG)
+ break;
+ if(a->sym != S)
+ break;
+ v = a->offset;
+ strcpy(str, "");
+ for(i=0; i<NREG; i++) {
+ if(v & (1<<i)) {
+ if(str[0] == 0)
+ strcat(str, "[R");
+ else
+ strcat(str, ",R");
+ sprint(strchr(str, 0), "%d", i);
+ }
+ }
+ strcat(str, "]");
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+ int i, c;
+ char str[STRINGSZ], *p, *a;
+
+ a = va_arg(fp->args, char*);
+ p = str;
+ for(i=0; i<NSNAME; i++) {
+ c = a[i] & 0xff;
+ if(c >= 'a' && c <= 'z' ||
+ c >= 'A' && c <= 'Z' ||
+ c >= '0' && c <= '9' ||
+ c == ' ' || c == '%') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch(c) {
+ case 0:
+ *p++ = 'z';
+ continue;
+ case '\\':
+ case '"':
+ *p++ = c;
+ continue;
+ case '\n':
+ *p++ = 'n';
+ continue;
+ case '\t':
+ *p++ = 't';
+ continue;
+ case '\r':
+ *p++ = 'r';
+ continue;
+ case '\f':
+ *p++ = 'f';
+ continue;
+ }
+ *p++ = (c>>6) + '0';
+ *p++ = ((c>>3) & 7) + '0';
+ *p++ = (c & 7) + '0';
+ }
+ *p = 0;
+ return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+ Sym *s;
+
+ a = va_arg(fp->args, Adr*);
+ s = a->sym;
+ if(s == S) {
+ sprint(str, "%ld", a->offset);
+ goto out;
+ }
+ switch(a->name) {
+ default:
+ sprint(str, "GOK-name(%d)", a->name);
+ break;
+
+ case D_NONE:
+ sprint(str, "%ld", a->offset);
+ break;
+
+ case D_EXTERN:
+ sprint(str, "%s+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_STATIC:
+ sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_AUTO:
+ sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+ break;
+
+ case D_PARAM:
+ sprint(str, "%s+%ld(FP)", s->name, a->offset);
+ break;
+ }
+out:
+ return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/5c/mkenam
@@ -1,0 +1,15 @@
+ed - ../5c/5.out.h <<'!'
+v/^ A/d
+,s/^ A/ "/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char* anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/5c/mkfile
@@ -1,0 +1,38 @@
+<../../mkconfig
+
+TARG=5c
+
+OFILES=\
+ cgen.$O\
+ enam.$O\
+ list.$O\
+ mul.$O\
+ peep.$O\
+ pgen.$O\
+ pswt.$O\
+ reg.$O\
+ sgen.$O\
+ swt.$O\
+ txt.$O\
+
+HFILES=\
+ gc.h\
+ 5.out.h\
+ ../cc/cc.h\
+
+LIBS=cc bio 9 # order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+$ROOT/$OBJDIR/lib/libcc.a:
+ cd ../cc
+ mk $MKFLAGS install
+ mk $MKFLAGS clean
+
+%.$O: ../cc/%.c
+ $CC -I. $CFLAGS ../cc/$stem.c
+
+#enam.c: 5.out.h
+# rc mkenam
--- /dev/null
+++ b/utils/5c/mul.c
@@ -1,0 +1,609 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ * lsl $(A-'a'),r0,r1
+ * [+][0-7]
+ * add r0,r1,r2
+ * [-][0-7]
+ * sub r0,r1,r2
+ */
+
+static int maxmulops = 3; /* max # of ops to replace mul with */
+static int multabp;
+static long mulval;
+static char* mulcp;
+static long valmax;
+static int shmax;
+
+static int docode(char *hp, char *cp, int r0, int r1);
+static int gen1(int len);
+static int gen2(int len, long r1);
+static int gen3(int len, long r0, long r1, int flag);
+enum
+{
+ SR1 = 1<<0, /* r1 has been shifted */
+ SR0 = 1<<1, /* r0 has been shifted */
+ UR1 = 1<<2, /* r1 has not been used */
+ UR0 = 1<<3, /* r0 has not been used */
+};
+
+Multab*
+mulcon0(long v)
+{
+ int a1, a2, g;
+ Multab *m, *m1;
+ char hint[10];
+
+ if(v < 0)
+ v = -v;
+
+ /*
+ * look in cache
+ */
+ m = multab;
+ for(g=0; g<nelem(multab); g++) {
+ if(m->val == v) {
+ if(m->code[0] == 0)
+ return 0;
+ return m;
+ }
+ m++;
+ }
+
+ /*
+ * select a spot in cache to overwrite
+ */
+ multabp++;
+ if(multabp < 0 || multabp >= nelem(multab))
+ multabp = 0;
+ m = multab+multabp;
+ m->val = v;
+ mulval = v;
+
+ /*
+ * look in execption hint table
+ */
+ a1 = 0;
+ a2 = hintabsize;
+ for(;;) {
+ if(a1 >= a2)
+ goto no;
+ g = (a2 + a1)/2;
+ if(v < hintab[g].val) {
+ a2 = g;
+ continue;
+ }
+ if(v > hintab[g].val) {
+ a1 = g+1;
+ continue;
+ }
+ break;
+ }
+
+ if(docode(hintab[g].hint, m->code, 1, 0))
+ return m;
+ print("multiply table failure %ld\n", v);
+ m->code[0] = 0;
+ return 0;
+
+no:
+ /*
+ * try to search
+ */
+ hint[0] = 0;
+ for(g=1; g<=maxmulops; g++) {
+ if(g >= maxmulops && v >= 65535)
+ break;
+ mulcp = hint+g;
+ *mulcp = 0;
+ if(gen1(g)) {
+ if(docode(hint, m->code, 1, 0))
+ return m;
+ print("multiply table failure %ld\n", v);
+ break;
+ }
+ }
+
+ /*
+ * try a recur followed by a shift
+ */
+ g = 0;
+ while(!(v & 1)) {
+ g++;
+ v >>= 1;
+ }
+ if(g) {
+ m1 = mulcon0(v);
+ if(m1) {
+ strcpy(m->code, m1->code);
+ sprint(strchr(m->code, 0), "%c0", g+'a');
+ return m;
+ }
+ }
+ m->code[0] = 0;
+ return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+ int c, i;
+
+ c = *hp++;
+ *cp = c;
+ cp += 2;
+ switch(c) {
+ default:
+ c -= 'a';
+ if(c < 1 || c >= 30)
+ break;
+ for(i=0; i<4; i++) {
+ switch(i) {
+ case 0:
+ if(docode(hp, cp, r0<<c, r1))
+ goto out;
+ break;
+ case 1:
+ if(docode(hp, cp, r1<<c, r1))
+ goto out;
+ break;
+ case 2:
+ if(docode(hp, cp, r0, r0<<c))
+ goto out;
+ break;
+ case 3:
+ if(docode(hp, cp, r0, r1<<c))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case '+':
+ for(i=0; i<8; i++) {
+ cp[-1] = i+'0';
+ switch(i) {
+ case 1:
+ if(docode(hp, cp, r0+r1, r1))
+ goto out;
+ break;
+ case 5:
+ if(docode(hp, cp, r0, r0+r1))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case '-':
+ for(i=0; i<8; i++) {
+ cp[-1] = i+'0';
+ switch(i) {
+ case 1:
+ if(docode(hp, cp, r0-r1, r1))
+ goto out;
+ break;
+ case 2:
+ if(docode(hp, cp, r1-r0, r1))
+ goto out;
+ break;
+ case 5:
+ if(docode(hp, cp, r0, r0-r1))
+ goto out;
+ break;
+ case 6:
+ if(docode(hp, cp, r0, r1-r0))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case 0:
+ if(r0 == mulval)
+ return 1;
+ }
+ return 0;
+
+out:
+ cp[-1] = i+'0';
+ return 1;
+}
+
+static int
+gen1(int len)
+{
+ int i;
+
+ for(shmax=1; shmax<30; shmax++) {
+ valmax = 1<<shmax;
+ if(valmax >= mulval)
+ break;
+ }
+ if(mulval == 1)
+ return 1;
+
+ len--;
+ for(i=1; i<=shmax; i++)
+ if(gen2(len, 1<<i)) {
+ *--mulcp = 'a'+i;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+ int i;
+
+ if(len <= 0) {
+ if(r1 == mulval)
+ return 1;
+ return 0;
+ }
+
+ len--;
+ if(len == 0)
+ goto calcr0;
+
+ if(gen3(len, r1, r1+1, UR1)) {
+ i = '+';
+ goto out;
+ }
+ if(gen3(len, r1-1, r1, UR0)) {
+ i = '-';
+ goto out;
+ }
+ if(gen3(len, 1, r1+1, UR1)) {
+ i = '+';
+ goto out;
+ }
+ if(gen3(len, 1, r1-1, UR1)) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+calcr0:
+ if(mulval == r1+1) {
+ i = '+';
+ goto out;
+ }
+ if(mulval == r1-1) {
+ i = '-';
+ goto out;
+ }
+ return 0;
+
+out:
+ *--mulcp = i;
+ return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+ int i, f1, f2;
+ long x;
+
+ if(r0 <= 0 ||
+ r0 >= r1 ||
+ r1 > valmax)
+ return 0;
+
+ len--;
+ if(len == 0)
+ goto calcr0;
+
+ if(!(flag & UR1)) {
+ f1 = UR1|SR1;
+ for(i=1; i<=shmax; i++) {
+ x = r0<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r0, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & UR0)) {
+ f1 = UR1|SR1;
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r1, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & SR1)) {
+ f1 = UR1|SR1|(flag&UR0);
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r0, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & SR0)) {
+ f1 = UR0|SR0|(flag&(SR1|UR1));
+
+ f2 = UR1|SR1;
+ if(flag & UR1)
+ f2 |= UR0;
+ if(flag & SR1)
+ f2 |= SR0;
+
+ for(i=1; i<=shmax; i++) {
+ x = r0<<i;
+ if(x > valmax)
+ break;
+ if(x > r1) {
+ if(gen3(len, r1, x, f2)) {
+ i += 'a';
+ goto out;
+ }
+ } else
+ if(gen3(len, x, r1, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ x = r1+r0;
+ if(gen3(len, r0, x, UR1)) {
+ i = '+';
+ goto out;
+ }
+
+ if(gen3(len, r1, x, UR1)) {
+ i = '+';
+ goto out;
+ }
+
+ x = r1-r0;
+ if(gen3(len, x, r1, UR0)) {
+ i = '-';
+ goto out;
+ }
+
+ if(x > r0) {
+ if(gen3(len, r0, x, UR1)) {
+ i = '-';
+ goto out;
+ }
+ } else
+ if(gen3(len, x, r0, UR0)) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+calcr0:
+ f1 = flag & (UR0|UR1);
+ if(f1 == UR1) {
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x >= mulval) {
+ if(x == mulval) {
+ i += 'a';
+ goto out;
+ }
+ break;
+ }
+ }
+ }
+
+ if(mulval == r1+r0) {
+ i = '+';
+ goto out;
+ }
+ if(mulval == r1-r0) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+out:
+ *--mulcp = i;
+ return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ * all numbers
+ * <5000:
+ * ÷ by 5
+ * <10000:
+ * ÷ by 50
+ * <65536:
+ * ÷ by 250
+ */
+Hintab hintab[] =
+{
+ 683, "b++d+e+",
+ 687, "b+e++e-",
+ 691, "b++d+e+",
+ 731, "b++d+e+",
+ 811, "b++d+i+",
+ 821, "b++e+e+",
+ 843, "b+d++e+",
+ 851, "b+f-+e-",
+ 853, "b++e+e+",
+ 877, "c++++g-",
+ 933, "b+c++g-",
+ 981, "c-+e-d+",
+ 1375, "b+c+b+h-",
+ 1675, "d+b++h+",
+ 2425, "c++f-e+",
+ 2675, "c+d++f-",
+ 2750, "b+d-b+h-",
+ 2775, "c-+g-e-",
+ 3125, "b++e+g+",
+ 3275, "b+c+g+e+",
+ 3350, "c++++i+",
+ 3475, "c-+e-f-",
+ 3525, "c-+d+g-",
+ 3625, "c-+e-j+",
+ 3675, "b+d+d+e+",
+ 3725, "b+d-+h+",
+ 3925, "b+d+f-d-",
+ 4275, "b+g++e+",
+ 4325, "b+h-+d+",
+ 4425, "b+b+g-j-",
+ 4525, "b+d-d+f+",
+ 4675, "c++d-g+",
+ 4775, "b+d+b+g-",
+ 4825, "c+c-+i-",
+ 4850, "c++++i-",
+ 4925, "b++e-g-",
+ 4975, "c+f++e-",
+ 5500, "b+g-c+d+",
+ 6700, "d+b++i+",
+ 9700, "d++++j-",
+ 11000, "b+f-c-h-",
+ 11750, "b+d+g+j-",
+ 12500, "b+c+e-k+",
+ 13250, "b+d+e-f+",
+ 13750, "b+h-c-d+",
+ 14250, "b+g-c+e-",
+ 14500, "c+f+j-d-",
+ 14750, "d-g--f+",
+ 16750, "b+e-d-n+",
+ 17750, "c+h-b+e+",
+ 18250, "d+b+h-d+",
+ 18750, "b+g-++f+",
+ 19250, "b+e+b+h+",
+ 19750, "b++h--f-",
+ 20250, "b+e-l-c+",
+ 20750, "c++bi+e-",
+ 21250, "b+i+l+c+",
+ 22000, "b+e+d-g-",
+ 22250, "b+d-h+k-",
+ 22750, "b+d-e-g+",
+ 23250, "b+c+h+e-",
+ 23500, "b+g-c-g-",
+ 23750, "b+g-b+h-",
+ 24250, "c++g+m-",
+ 24750, "b+e+e+j-",
+ 25000, "b++dh+g+",
+ 25250, "b+e+d-g-",
+ 25750, "b+e+b+j+",
+ 26250, "b+h+c+e+",
+ 26500, "b+h+c+g+",
+ 26750, "b+d+e+g-",
+ 27250, "b+e+e+f+",
+ 27500, "c-i-c-d+",
+ 27750, "b+bd++j+",
+ 28250, "d-d-++i-",
+ 28500, "c+c-h-e-",
+ 29000, "b+g-d-f+",
+ 29500, "c+h+++e-",
+ 29750, "b+g+f-c+",
+ 30250, "b+f-g-c+",
+ 33500, "c-f-d-n+",
+ 33750, "b+d-b+j-",
+ 34250, "c+e+++i+",
+ 35250, "e+b+d+k+",
+ 35500, "c+e+d-g-",
+ 35750, "c+i-++e+",
+ 36250, "b+bh-d+e+",
+ 36500, "c+c-h-e-",
+ 36750, "d+e--i+",
+ 37250, "b+g+g+b+",
+ 37500, "b+h-b+f+",
+ 37750, "c+be++j-",
+ 38500, "b+e+b+i+",
+ 38750, "d+i-b+d+",
+ 39250, "b+g-l-+d+",
+ 39500, "b+g-c+g-",
+ 39750, "b+bh-c+f-",
+ 40250, "b+bf+d+g-",
+ 40500, "b+g-c+g+",
+ 40750, "c+b+i-e+",
+ 41250, "d++bf+h+",
+ 41500, "b+j+c+d-",
+ 41750, "c+f+b+h-",
+ 42500, "c+h++g+",
+ 42750, "b+g+d-f-",
+ 43250, "b+l-e+d-",
+ 43750, "c+bd+h+f-",
+ 44000, "b+f+g-d-",
+ 44250, "b+d-g--f+",
+ 44500, "c+e+c+h+",
+ 44750, "b+e+d-h-",
+ 45250, "b++g+j-g+",
+ 45500, "c+d+e-g+",
+ 45750, "b+d-h-e-",
+ 46250, "c+bd++j+",
+ 46500, "b+d-c-j-",
+ 46750, "e-e-b+g-",
+ 47000, "b+c+d-j-",
+ 47250, "b+e+e-g-",
+ 47500, "b+g-c-h-",
+ 47750, "b+f-c+h-",
+ 48250, "d--h+n-",
+ 48500, "b+c-g+m-",
+ 48750, "b+e+e-g+",
+ 49500, "c-f+e+j-",
+ 49750, "c+c+g++f-",
+ 50000, "b+e+e+k+",
+ 50250, "b++i++g+",
+ 50500, "c+g+f-i+",
+ 50750, "b+e+d+k-",
+ 51500, "b+i+c-f+",
+ 51750, "b+bd+g-e-",
+ 52250, "b+d+g-j+",
+ 52500, "c+c+f+g+",
+ 52750, "b+c+e+i+",
+ 53000, "b+i+c+g+",
+ 53500, "c+g+g-n+",
+ 53750, "b+j+d-c+",
+ 54250, "b+d-g-j-",
+ 54500, "c-f+e+f+",
+ 54750, "b+f-+c+g+",
+ 55000, "b+g-d-g-",
+ 55250, "b+e+e+g+",
+ 55500, "b+cd++j+",
+ 55750, "b+bh-d-f-",
+ 56250, "c+d-b+j-",
+ 56500, "c+d+c+i+",
+ 56750, "b+e+d++h-",
+ 57000, "b+d+g-f+",
+ 57250, "b+f-m+d-",
+ 57750, "b+i+c+e-",
+ 58000, "b+e+d+h+",
+ 58250, "c+b+g+g+",
+ 58750, "d-e-j--e+",
+ 59000, "d-i-+e+",
+ 59250, "e--h-m+",
+ 59500, "c+c-h+f-",
+ 59750, "b+bh-e+i-",
+ 60250, "b+bh-e-e-",
+ 60500, "c+c-g-g-",
+ 60750, "b+e-l-e-",
+ 61250, "b+g-g-c+",
+ 61750, "b+g-c+g+",
+ 62250, "f--+c-i-",
+ 62750, "e+f--+g+",
+ 64750, "b+f+d+p-",
+};
+int hintabsize = nelem(hintab);
--- /dev/null
+++ b/utils/5c/peep.c
@@ -1,0 +1,1439 @@
+#include "gc.h"
+
+int xtramodes(Reg*, Adr*);
+
+void
+peep(void)
+{
+ Reg *r, *r1, *r2;
+ Prog *p, *p1;
+ int t;
+/*
+ * complete R structure
+ */
+ t = 0;
+ for(r=firstr; r!=R; r=r1) {
+ r1 = r->link;
+ if(r1 == R)
+ break;
+ p = r->prog->link;
+ while(p != r1->prog)
+ switch(p->as) {
+ default:
+ r2 = rega();
+ r->link = r2;
+ r2->link = r1;
+
+ r2->prog = p;
+ r2->p1 = r;
+ r->s1 = r2;
+ r2->s1 = r1;
+ r1->p1 = r2;
+
+ r = r2;
+ t++;
+
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ p = p->link;
+ }
+ }
+
+loop1:
+ t = 0;
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ if(p->as == ASLL || p->as == ASRL || p->as == ASRA) {
+ /*
+ * elide shift into D_SHIFT operand of subsequent instruction
+ */
+ if(shiftprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ if(p->as == AMOVW || p->as == AMOVF || p->as == AMOVD)
+ if(regtyp(&p->to)) {
+ if(p->from.type == D_CONST)
+ constprop(&p->from, &p->to, r->s1);
+ else if(regtyp(&p->from))
+ if(p->from.type == p->to.type) {
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ } else
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ }
+ }
+ if(t)
+ goto loop1;
+ /*
+ * look for MOVB x,R; MOVB R,R
+ */
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ default:
+ continue;
+ case AEOR:
+ /*
+ * EOR -1,x,y => MVN x,y
+ */
+ if(p->from.type == D_CONST && p->from.offset == -1) {
+ p->as = AMVN;
+ p->from.type = D_REG;
+ if(p->reg != NREG)
+ p->from.reg = p->reg;
+ else
+ p->from.reg = p->to.reg;
+ p->reg = NREG;
+ }
+ continue;
+ case AMOVH:
+ case AMOVHU:
+ case AMOVB:
+ case AMOVBU:
+ if(p->to.type != D_REG)
+ continue;
+ break;
+ }
+ r1 = r->link;
+ if(r1 == R)
+ continue;
+ p1 = r1->prog;
+ if(p1->as != p->as)
+ continue;
+ if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+ continue;
+ if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+ continue;
+ excise(r1);
+ }
+
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ case AMOVW:
+ case AMOVB:
+ case AMOVBU:
+ if(p->from.type == D_OREG && p->from.offset == 0)
+ xtramodes(r, &p->from);
+ else if(p->to.type == D_OREG && p->to.offset == 0)
+ xtramodes(r, &p->to);
+ else
+ continue;
+ break;
+ case ACMP:
+ /*
+ * elide CMP $0,x if calculation of x can set condition codes
+ */
+ if(p->from.type != D_CONST || p->from.offset != 0)
+ continue;
+ r2 = r->s1;
+ if(r2 == R)
+ continue;
+ t = r2->prog->as;
+ switch(t) {
+ default:
+ continue;
+ case ABEQ:
+ case ABNE:
+ case ABMI:
+ case ABPL:
+ break;
+ case ABGE:
+ t = ABPL;
+ break;
+ case ABLT:
+ t = ABMI;
+ break;
+ case ABHI:
+ t = ABNE;
+ break;
+ case ABLS:
+ t = ABEQ;
+ break;
+ }
+ r1 = r;
+ do
+ r1 = uniqp(r1);
+ while (r1 != R && r1->prog->as == ANOP);
+ if(r1 == R)
+ continue;
+ p1 = r1->prog;
+ if(p1->to.type != D_REG)
+ continue;
+ if(p1->to.reg != p->reg)
+ if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg))
+ continue;
+ switch(p1->as) {
+ default:
+ continue;
+ case AMOVW:
+ if(p1->from.type != D_REG)
+ continue;
+ case AAND:
+ case AEOR:
+ case AORR:
+ case ABIC:
+ case AMVN:
+ case ASUB:
+ case ARSB:
+ case AADD:
+ case AADC:
+ case ASBC:
+ case ARSC:
+ break;
+ }
+ p1->scond |= C_SBIT;
+ r2->prog->as = t;
+ excise(r);
+ continue;
+ }
+ }
+
+ predicate();
+}
+
+void
+excise(Reg *r)
+{
+ Prog *p;
+
+ p = r->prog;
+ p->as = ANOP;
+ p->scond = zprog.scond;
+ p->from = zprog.from;
+ p->to = zprog.to;
+ p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+ Reg *r1;
+
+ r1 = r->p1;
+ if(r1 == R) {
+ r1 = r->p2;
+ if(r1 == R || r1->p2link != R)
+ return R;
+ } else
+ if(r->p2 != R)
+ return R;
+ return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+ Reg *r1;
+
+ r1 = r->s1;
+ if(r1 == R) {
+ r1 = r->s2;
+ if(r1 == R)
+ return R;
+ } else
+ if(r->s2 != R)
+ return R;
+ return r1;
+}
+
+int
+regtyp(Adr *a)
+{
+
+ if(a->type == D_REG)
+ return 1;
+ if(a->type == D_FREG)
+ return 1;
+ return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ * MOV a, R0
+ * ADD b, R0 / no use of R1
+ * MOV R0, R1
+ * would be converted to
+ * MOV a, R1
+ * ADD b, R1
+ * MOV R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+ Prog *p;
+ Adr *v1, *v2;
+ Reg *r;
+ int t;
+
+ p = r0->prog;
+ v1 = &p->from;
+ if(!regtyp(v1))
+ return 0;
+ v2 = &p->to;
+ if(!regtyp(v2))
+ return 0;
+ for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+ if(uniqs(r) == R)
+ break;
+ p = r->prog;
+ switch(p->as) {
+ case ABL:
+ return 0;
+
+ case ACMP:
+ case ACMN:
+ case AADD:
+ case ASUB:
+ case ARSB:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AORR:
+ case AAND:
+ case AEOR:
+ case AMUL:
+ case ADIV:
+ case ADIVU:
+
+ case ACMPF:
+ case ACMPD:
+ case AADDD:
+ case AADDF:
+ case ASUBD:
+ case ASUBF:
+ case AMULD:
+ case AMULF:
+ case ADIVD:
+ case ADIVF:
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg) {
+ if(p->reg == NREG)
+ p->reg = p->to.reg;
+ goto gotit;
+ }
+ break;
+
+ case AMOVF:
+ case AMOVD:
+ case AMOVW:
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg)
+ goto gotit;
+ break;
+
+ case AMOVM:
+ t = 1<<v2->reg;
+ if((p->from.type == D_CONST && (p->from.offset&t)) ||
+ (p->to.type == D_CONST && (p->to.offset&t)))
+ return 0;
+ break;
+ }
+ if(copyau(&p->from, v2) ||
+ copyau1(p, v2) ||
+ copyau(&p->to, v2))
+ break;
+ if(copysub(&p->from, v1, v2, 0) ||
+ copysub1(p, v1, v2, 0) ||
+ copysub(&p->to, v1, v2, 0))
+ break;
+ }
+ return 0;
+
+gotit:
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P']) {
+ print("gotit: %D->%D\n%P", v1, v2, r->prog);
+ if(p->from.type == v2->type)
+ print(" excise");
+ print("\n");
+ }
+ for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+ p = r->prog;
+ copysub(&p->from, v1, v2, 1);
+ copysub1(p, v1, v2, 1);
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P'])
+ print("%P\n", r->prog);
+ }
+ t = v1->reg;
+ v1->reg = v2->reg;
+ v2->reg = t;
+ if(debug['P'])
+ print("%P last\n", r->prog);
+ return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * use v2 return fail
+ * -----------------
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * set v2 return success
+ */
+int
+copyprop(Reg *r0)
+{
+ Prog *p;
+ Adr *v1, *v2;
+ Reg *r;
+
+ p = r0->prog;
+ v1 = &p->from;
+ v2 = &p->to;
+ if(copyas(v1, v2))
+ return 1;
+ for(r=firstr; r!=R; r=r->link)
+ r->active = 0;
+ return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+ int t;
+ Prog *p;
+
+ if(r->active) {
+ if(debug['P'])
+ print("act set; return 1\n");
+ return 1;
+ }
+ r->active = 1;
+ if(debug['P'])
+ print("copy %D->%D f=%d\n", v1, v2, f);
+ for(; r != R; r = r->s1) {
+ p = r->prog;
+ if(debug['P'])
+ print("%P", p);
+ if(!f && uniqp(r) == R) {
+ f = 1;
+ if(debug['P'])
+ print("; merge; f=%d", f);
+ }
+ t = copyu(p, v2, A);
+ switch(t) {
+ case 2: /* rar, cant split */
+ if(debug['P'])
+ print("; %Drar; return 0\n", v2);
+ return 0;
+
+ case 3: /* set */
+ if(debug['P'])
+ print("; %Dset; return 1\n", v2);
+ return 1;
+
+ case 1: /* used, substitute */
+ case 4: /* use and set */
+ if(f) {
+ if(!debug['P'])
+ return 0;
+ if(t == 4)
+ print("; %Dused+set and f=%d; return 0\n", v2, f);
+ else
+ print("; %Dused and f=%d; return 0\n", v2, f);
+ return 0;
+ }
+ if(copyu(p, v2, v1)) {
+ if(debug['P'])
+ print("; sub fail; return 0\n");
+ return 0;
+ }
+ if(debug['P'])
+ print("; sub%D/%D", v2, v1);
+ if(t == 4) {
+ if(debug['P'])
+ print("; %Dused+set; return 1\n", v2);
+ return 1;
+ }
+ break;
+ }
+ if(!f) {
+ t = copyu(p, v1, A);
+ if(!f && (t == 2 || t == 3 || t == 4)) {
+ f = 1;
+ if(debug['P'])
+ print("; %Dset and !f; f=%d", v1, f);
+ }
+ }
+ if(debug['P'])
+ print("\n");
+ if(r->s2)
+ if(!copy1(v1, v2, r->s2, f))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * The idea is to remove redundant constants.
+ * $c1->v1
+ * ($c1->v2 s/$c1/v1)*
+ * set v1 return
+ * The v1->v2 should be eliminated by copy propagation.
+ */
+void
+constprop(Adr *c1, Adr *v1, Reg *r)
+{
+ Prog *p;
+
+ if(debug['C'])
+ print("constprop %D->%D\n", c1, v1);
+ for(; r != R; r = r->s1) {
+ p = r->prog;
+ if(debug['C'])
+ print("%P", p);
+ if(uniqp(r) == R) {
+ if(debug['C'])
+ print("; merge; return\n");
+ return;
+ }
+ if(p->as == AMOVW && copyas(&p->from, c1)) {
+ if(debug['C'])
+ print("; sub%D/%D", &p->from, v1);
+ p->from = *v1;
+ } else if(copyu(p, v1, A) > 1) {
+ if(debug['C'])
+ print("; %Dset; return\n", v1);
+ return;
+ }
+ if(debug['C'])
+ print("\n");
+ if(r->s2)
+ constprop(c1, v1, r->s2);
+ }
+}
+
+/*
+ * ASLL x,y,w
+ * .. (not use w, not set x y w)
+ * AXXX w,a,b (a != w)
+ * .. (not use w)
+ * (set w)
+ * ----------- changed to
+ * ..
+ * AXXX (x<<y),a,b
+ * ..
+ */
+#define FAIL(msg) { if(debug['H']) print("\t%s; FAILURE\n", msg); return 0; }
+int
+shiftprop(Reg *r)
+{
+ Reg *r1;
+ Prog *p, *p1, *p2;
+ int n, o;
+ Adr a;
+
+ p = r->prog;
+ if(p->to.type != D_REG)
+ FAIL("BOTCH: result not reg");
+ n = p->to.reg;
+ a = zprog.from;
+ if(p->reg != NREG && p->reg != p->to.reg) {
+ a.type = D_REG;
+ a.reg = p->reg;
+ }
+ if(debug['H'])
+ print("shiftprop\n%P", p);
+ r1 = r;
+ for(;;) {
+ /* find first use of shift result; abort if shift operands or result are changed */
+ r1 = uniqs(r1);
+ if(r1 == R)
+ FAIL("branch");
+ if(uniqp(r1) == R)
+ FAIL("merge");
+ p1 = r1->prog;
+ if(debug['H'])
+ print("\n%P", p1);
+ switch(copyu(p1, &p->to, A)) {
+ case 0: /* not used or set */
+ if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) ||
+ (a.type == D_REG && copyu(p1, &a, A) > 1))
+ FAIL("args modified");
+ continue;
+ case 3: /* set, not used */
+ FAIL("BOTCH: noref");
+ }
+ break;
+ }
+ /* check whether substitution can be done */
+ switch(p1->as) {
+ default:
+ FAIL("non-dpi");
+ case AAND:
+ case AEOR:
+ case AADD:
+ case AADC:
+ case AORR:
+ case ASUB:
+ case ARSB:
+ case ASBC:
+ case ARSC:
+ if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) {
+ if(p1->from.type != D_REG)
+ FAIL("can't swap");
+ p1->reg = p1->from.reg;
+ p1->from.reg = n;
+ switch(p1->as) {
+ case ASUB:
+ p1->as = ARSB;
+ break;
+ case ARSB:
+ p1->as = ASUB;
+ break;
+ case ASBC:
+ p1->as = ARSC;
+ break;
+ case ARSC:
+ p1->as = ASBC;
+ break;
+ }
+ if(debug['H'])
+ print("\t=>%P", p1);
+ }
+ case ABIC:
+ case ACMP:
+ case ACMN:
+ if(p1->reg == n)
+ FAIL("can't swap");
+ if(p1->reg == NREG && p1->to.reg == n)
+ FAIL("shift result used twice");
+ case AMVN:
+ if(p1->from.type == D_SHIFT)
+ FAIL("shift result used in shift");
+ if(p1->from.type != D_REG || p1->from.reg != n)
+ FAIL("BOTCH: where is it used?");
+ break;
+ }
+ /* check whether shift result is used subsequently */
+ p2 = p1;
+ if(p1->to.reg != n)
+ for (;;) {
+ r1 = uniqs(r1);
+ if(r1 == R)
+ FAIL("inconclusive");
+ p1 = r1->prog;
+ if(debug['H'])
+ print("\n%P", p1);
+ switch(copyu(p1, &p->to, A)) {
+ case 0: /* not used or set */
+ continue;
+ case 3: /* set, not used */
+ break;
+ default:/* used */
+ FAIL("reused");
+ }
+ break;
+ }
+ /* make the substitution */
+ p2->from.type = D_SHIFT;
+ p2->from.reg = NREG;
+ o = p->reg;
+ if(o == NREG)
+ o = p->to.reg;
+ switch(p->from.type){
+ case D_CONST:
+ o |= (p->from.offset&0x1f)<<7;
+ break;
+ case D_REG:
+ o |= (1<<4) | (p->from.reg<<8);
+ break;
+ }
+ switch(p->as){
+ case ASLL:
+ o |= 0<<5;
+ break;
+ case ASRL:
+ o |= 1<<5;
+ break;
+ case ASRA:
+ o |= 2<<5;
+ break;
+ }
+ p2->from.offset = o;
+ if(debug['H'])
+ print("\t=>%P\tSUCCEED\n", p2);
+ return 1;
+}
+
+Reg*
+findpre(Reg *r, Adr *v)
+{
+ Reg *r1;
+
+ for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) {
+ if(uniqs(r1) != r)
+ return R;
+ switch(copyu(r1->prog, v, A)) {
+ case 1: /* used */
+ case 2: /* read-alter-rewrite */
+ return R;
+ case 3: /* set */
+ case 4: /* set and used */
+ return r1;
+ }
+ }
+ return R;
+}
+
+Reg*
+findinc(Reg *r, Reg *r2, Adr *v)
+{
+ Reg *r1;
+ Prog *p;
+
+
+ for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) {
+ if(uniqp(r1) != r)
+ return R;
+ switch(copyu(r1->prog, v, A)) {
+ case 0: /* not touched */
+ continue;
+ case 4: /* set and used */
+ p = r1->prog;
+ if(p->as == AADD)
+ if(p->from.type == D_CONST)
+ if(p->from.offset > -4096 && p->from.offset < 4096)
+ return r1;
+ default:
+ return R;
+ }
+ }
+ return R;
+}
+
+int
+nochange(Reg *r, Reg *r2, Prog *p)
+{
+ Adr a[3];
+ int i, n;
+
+ if(r == r2)
+ return 1;
+ n = 0;
+ if(p->reg != NREG && p->reg != p->to.reg) {
+ a[n].type = D_REG;
+ a[n++].reg = p->reg;
+ }
+ switch(p->from.type) {
+ case D_SHIFT:
+ a[n].type = D_REG;
+ a[n++].reg = p->from.offset&0xf;
+ case D_REG:
+ a[n].type = D_REG;
+ a[n++].reg = p->from.reg;
+ }
+ if(n == 0)
+ return 1;
+ for(; r!=R && r!=r2; r=uniqs(r)) {
+ p = r->prog;
+ for(i=0; i<n; i++)
+ if(copyu(p, &a[i], A) > 1)
+ return 0;
+ }
+ return 1;
+}
+
+int
+findu1(Reg *r, Adr *v)
+{
+ for(; r != R; r = r->s1) {
+ if(r->active)
+ return 0;
+ r->active = 1;
+ switch(copyu(r->prog, v, A)) {
+ case 1: /* used */
+ case 2: /* read-alter-rewrite */
+ case 4: /* set and used */
+ return 1;
+ case 3: /* set */
+ return 0;
+ }
+ if(r->s2)
+ if (findu1(r->s2, v))
+ return 1;
+ }
+ return 0;
+}
+
+int
+finduse(Reg *r, Adr *v)
+{
+ Reg *r1;
+
+ for(r1=firstr; r1!=R; r1=r1->link)
+ r1->active = 0;
+ return findu1(r, v);
+}
+
+int
+xtramodes(Reg *r, Adr *a)
+{
+ Reg *r1, *r2, *r3;
+ Prog *p, *p1;
+ Adr v;
+
+ p = r->prog;
+ if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */
+ return 0;
+ v = *a;
+ v.type = D_REG;
+ r1 = findpre(r, &v);
+ if(r1 != R) {
+ p1 = r1->prog;
+ if(p1->to.type == D_REG && p1->to.reg == v.reg)
+ switch(p1->as) {
+ case AADD:
+ if(p1->from.type == D_REG ||
+ (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 &&
+ (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
+ (p1->from.type == D_CONST &&
+ p1->from.offset > -4096 && p1->from.offset < 4096))
+ if(nochange(uniqs(r1), r, p1)) {
+ if(a != &p->from || v.reg != p->to.reg)
+ if (finduse(r->s1, &v)) {
+ if(p1->reg == NREG || p1->reg == v.reg)
+ /* pre-indexing */
+ p->scond |= C_WBIT;
+ else return 0;
+ }
+ switch (p1->from.type) {
+ case D_REG:
+ /* register offset */
+ a->type = D_SHIFT;
+ a->offset = p1->from.reg;
+ break;
+ case D_SHIFT:
+ /* scaled register offset */
+ a->type = D_SHIFT;
+ case D_CONST:
+ /* immediate offset */
+ a->offset = p1->from.offset;
+ break;
+ }
+ if(p1->reg != NREG)
+ a->reg = p1->reg;
+ excise(r1);
+ return 1;
+ }
+ break;
+ case AMOVW:
+ if(p1->from.type == D_REG)
+ if((r2 = findinc(r1, r, &p1->from)) != R) {
+ for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3))
+ ;
+ if(r3 == r) {
+ /* post-indexing */
+ p1 = r2->prog;
+ a->reg = p1->to.reg;
+ a->offset = p1->from.offset;
+ p->scond |= C_PBIT;
+ if(!finduse(r, &r1->prog->to))
+ excise(r1);
+ excise(r2);
+ return 1;
+ }
+ }
+ break;
+ }
+ }
+ if(a != &p->from || a->reg != p->to.reg)
+ if((r1 = findinc(r, R, &v)) != R) {
+ /* post-indexing */
+ p1 = r1->prog;
+ a->offset = p1->from.offset;
+ p->scond |= C_PBIT;
+ excise(r1);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+ switch(p->as) {
+
+ default:
+ if(debug['P'])
+ print(" (?)");
+ return 2;
+
+ case AMOVM:
+ if(v->type != D_REG)
+ return 0;
+ if(p->from.type == D_CONST) { /* read reglist, read/rar */
+ if(s != A) {
+ if(p->from.offset&(1<<v->reg))
+ return 1;
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v)) {
+ if(p->scond&C_WBIT)
+ return 2;
+ return 1;
+ }
+ if(p->from.offset&(1<<v->reg))
+ return 1;
+ } else { /* read/rar, write reglist */
+ if(s != A) {
+ if(p->to.offset&(1<<v->reg))
+ return 1;
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->from, v)) {
+ if(p->scond&C_WBIT)
+ return 2;
+ if(p->to.offset&(1<<v->reg))
+ return 4;
+ return 1;
+ }
+ if(p->to.offset&(1<<v->reg))
+ return 3;
+ }
+ return 0;
+
+ case ANOP: /* read, write */
+ case AMOVW:
+ case AMOVF:
+ case AMOVD:
+ case AMOVH:
+ case AMOVHU:
+ case AMOVB:
+ case AMOVBU:
+ case AMOVDW:
+ case AMOVWD:
+ case AMOVFD:
+ case AMOVDF:
+ if(p->scond&(C_WBIT|C_PBIT))
+ if(v->type == D_REG) {
+ if(p->from.type == D_OREG || p->from.type == D_SHIFT) {
+ if(p->from.reg == v->reg)
+ return 2;
+ } else {
+ if(p->to.reg == v->reg)
+ return 2;
+ }
+ }
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ if(!copyas(&p->to, v))
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v)) {
+ if(copyau(&p->from, v))
+ return 4;
+ return 3;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+
+ case AADD: /* read, read, write */
+ case ASUB:
+ case ARSB:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AORR:
+ case AAND:
+ case AEOR:
+ case AMUL:
+ case ADIV:
+ case ADIVU:
+ case AADDF:
+ case AADDD:
+ case ASUBF:
+ case ASUBD:
+ case AMULF:
+ case AMULD:
+ case ADIVF:
+ case ADIVD:
+
+ case ACMPF:
+ case ACMPD:
+ case ACMP:
+ case ACMN:
+ case ACASE:
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ if(copysub1(p, v, s, 1))
+ return 1;
+ if(!copyas(&p->to, v))
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v)) {
+ if(p->reg == NREG)
+ p->reg = p->to.reg;
+ if(copyau(&p->from, v))
+ return 4;
+ if(copyau1(p, v))
+ return 4;
+ return 3;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau1(p, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ABEQ: /* read, read */
+ case ABNE:
+ case ABCS:
+ case ABHS:
+ case ABCC:
+ case ABLO:
+ case ABMI:
+ case ABPL:
+ case ABVS:
+ case ABVC:
+ case ABHI:
+ case ABLS:
+ case ABGE:
+ case ABLT:
+ case ABGT:
+ case ABLE:
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return copysub1(p, v, s, 1);
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau1(p, v))
+ return 1;
+ return 0;
+
+ case AB: /* funny */
+ if(s != A) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ARET: /* funny */
+ if(v->type == D_REG)
+ if(v->reg == REGRET)
+ return 2;
+ if(v->type == D_FREG)
+ if(v->reg == FREGRET)
+ return 2;
+
+ case ABL: /* funny */
+ if(v->type == D_REG) {
+ if(v->reg <= REGEXT && v->reg > exregoffset)
+ return 2;
+ if(v->reg == (uchar)REGARG)
+ return 2;
+ }
+ if(v->type == D_FREG)
+ if(v->reg <= FREGEXT && v->reg > exfregoffset)
+ return 2;
+
+ if(s != A) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 4;
+ return 3;
+
+ case ATEXT: /* funny */
+ if(v->type == D_REG)
+ if(v->reg == (uchar)REGARG)
+ return 3;
+ return 0;
+ }
+}
+
+int
+a2type(Prog *p)
+{
+
+ switch(p->as) {
+
+ case ACMP:
+ case ACMN:
+
+ case AADD:
+ case ASUB:
+ case ARSB:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AORR:
+ case AAND:
+ case AEOR:
+ case AMUL:
+ case ADIV:
+ case ADIVU:
+ return D_REG;
+
+ case ACMPF:
+ case ACMPD:
+
+ case AADDF:
+ case AADDD:
+ case ASUBF:
+ case ASUBD:
+ case AMULF:
+ case AMULD:
+ case ADIVF:
+ case ADIVD:
+ return D_FREG;
+ }
+ return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+ if(regtyp(v)) {
+ if(a->type == v->type)
+ if(a->reg == v->reg)
+ return 1;
+ } else if(v->type == D_CONST) { /* for constprop */
+ if(a->type == v->type)
+ if(a->name == v->name)
+ if(a->sym == v->sym)
+ if(a->reg == v->reg)
+ if(a->offset == v->offset)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+ if(copyas(a, v))
+ return 1;
+ if(v->type == D_REG) {
+ if(a->type == D_OREG) {
+ if(v->reg == a->reg)
+ return 1;
+ } else if(a->type == D_SHIFT) {
+ if((a->offset&0xf) == v->reg)
+ return 1;
+ if((a->offset&(1<<4)) && (a->offset>>8) == v->reg)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+ if(regtyp(v)) {
+ if(a2type(p) == v->type)
+ if(p->reg == v->reg) {
+ if(a2type(p) != v->type)
+ print("botch a2type %P\n", p);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+ if(f)
+ if(copyau(a, v)) {
+ if(a->type == D_SHIFT) {
+ if((a->offset&0xf) == v->reg)
+ a->offset = (a->offset&~0xf)|s->reg;
+ if((a->offset&(1<<4)) && (a->offset>>8) == v->reg)
+ a->offset = (a->offset&~(0xf<<8))|(s->reg<<8);
+ } else
+ a->reg = s->reg;
+ }
+ return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+ if(f)
+ if(copyau1(p1, v))
+ p1->reg = s->reg;
+ return 0;
+}
+
+struct {
+ int opcode;
+ int notopcode;
+ int scond;
+ int notscond;
+} predinfo[] = {
+ { ABEQ, ABNE, 0x0, 0x1, },
+ { ABNE, ABEQ, 0x1, 0x0, },
+ { ABCS, ABCC, 0x2, 0x3, },
+ { ABHS, ABLO, 0x2, 0x3, },
+ { ABCC, ABCS, 0x3, 0x2, },
+ { ABLO, ABHS, 0x3, 0x2, },
+ { ABMI, ABPL, 0x4, 0x5, },
+ { ABPL, ABMI, 0x5, 0x4, },
+ { ABVS, ABVC, 0x6, 0x7, },
+ { ABVC, ABVS, 0x7, 0x6, },
+ { ABHI, ABLS, 0x8, 0x9, },
+ { ABLS, ABHI, 0x9, 0x8, },
+ { ABGE, ABLT, 0xA, 0xB, },
+ { ABLT, ABGE, 0xB, 0xA, },
+ { ABGT, ABLE, 0xC, 0xD, },
+ { ABLE, ABGT, 0xD, 0xC, },
+};
+
+typedef struct {
+ Reg *start;
+ Reg *last;
+ Reg *end;
+ int len;
+} Joininfo;
+
+enum {
+ Join,
+ Split,
+ End,
+ Branch,
+ Setcond,
+ Toolong
+};
+
+enum {
+ Falsecond,
+ Truecond,
+ Delbranch,
+ Keepbranch
+};
+
+int
+isbranch(Prog *p)
+{
+ return (ABEQ <= p->as) && (p->as <= ABLE);
+}
+
+int
+predicable(Prog *p)
+{
+ if (isbranch(p)
+ || p->as == ANOP
+ || p->as == AXXX
+ || p->as == ADATA
+ || p->as == AGLOBL
+ || p->as == AGOK
+ || p->as == AHISTORY
+ || p->as == ANAME
+ || p->as == ASIGNAME
+ || p->as == ATEXT
+ || p->as == AWORD
+ || p->as == ADYNT
+ || p->as == AINIT
+ || p->as == ABCASE
+ || p->as == ACASE)
+ return 0;
+ return 1;
+}
+
+/*
+ * Depends on an analysis of the encodings performed by 5l.
+ * These seem to be all of the opcodes that lead to the "S" bit
+ * being set in the instruction encodings.
+ *
+ * C_SBIT may also have been set explicitly in p->scond.
+ */
+int
+modifiescpsr(Prog *p)
+{
+ return (p->scond&C_SBIT)
+ || p->as == ATST
+ || p->as == ATEQ
+ || p->as == ACMN
+ || p->as == ACMP
+ || p->as == AMULU
+ || p->as == ADIVU
+ || p->as == AMUL
+ || p->as == ADIV
+ || p->as == AMOD
+ || p->as == AMODU
+ || p->as == ABL;
+}
+
+/*
+ * Find the maximal chain of instructions starting with r which could
+ * be executed conditionally
+ */
+int
+joinsplit(Reg *r, Joininfo *j)
+{
+ j->start = r;
+ j->last = r;
+ j->len = 0;
+ do {
+ if (r->p2 && (r->p1 || r->p2->p2link)) {
+ j->end = r;
+ return Join;
+ }
+ if (r->s1 && r->s2) {
+ j->end = r;
+ return Split;
+ }
+ j->last = r;
+ if (r->prog->as != ANOP)
+ j->len++;
+ if (!r->s1 && !r->s2) {
+ j->end = r->link;
+ return End;
+ }
+ if (r->s2) {
+ j->end = r->s2;
+ return Branch;
+ }
+ if (modifiescpsr(r->prog)) {
+ j->end = r->s1;
+ return Setcond;
+ }
+ r = r->s1;
+ } while (j->len < 4);
+ j->end = r;
+ return Toolong;
+}
+
+Reg *
+successor(Reg *r)
+{
+ if (r->s1)
+ return r->s1;
+ else
+ return r->s2;
+}
+
+void
+applypred(Reg *rstart, Joininfo *j, int cond, int branch)
+{
+ int pred;
+ Reg *r;
+
+ if(j->len == 0)
+ return;
+ if (cond == Truecond)
+ pred = predinfo[rstart->prog->as - ABEQ].scond;
+ else
+ pred = predinfo[rstart->prog->as - ABEQ].notscond;
+
+ for (r = j->start; ; r = successor(r)) {
+ if (r->prog->as == AB) {
+ if (r != j->last || branch == Delbranch)
+ excise(r);
+ else {
+ if (cond == Truecond)
+ r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode;
+ else
+ r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode;
+ }
+ }
+ else if (predicable(r->prog))
+ r->prog->scond = (r->prog->scond&~C_SCOND)|pred;
+ if (r->s1 != r->link) {
+ r->s1 = r->link;
+ r->link->p1 = r;
+ }
+ if (r == j->last)
+ break;
+ }
+}
+
+void
+predicate(void)
+{
+ Reg *r;
+ int t1, t2;
+ Joininfo j1, j2;
+
+ for(r=firstr; r!=R; r=r->link) {
+ if (isbranch(r->prog)) {
+ t1 = joinsplit(r->s1, &j1);
+ t2 = joinsplit(r->s2, &j2);
+ if(j1.last->link != j2.start)
+ continue;
+ if(j1.end == j2.end)
+ if((t1 == Branch && (t2 == Join || t2 == Setcond)) ||
+ (t2 == Join && (t1 == Join || t1 == Setcond))) {
+ applypred(r, &j1, Falsecond, Delbranch);
+ applypred(r, &j2, Truecond, Delbranch);
+ excise(r);
+ continue;
+ }
+ if(t1 == End || t1 == Branch) {
+ applypred(r, &j1, Falsecond, Keepbranch);
+ excise(r);
+ continue;
+ }
+ }
+ }
+}
--- /dev/null
+++ b/utils/5c/reg.c
@@ -1,0 +1,1158 @@
+#include "gc.h"
+
+void addsplits(void);
+
+Reg*
+rega(void)
+{
+ Reg *r;
+
+ r = freer;
+ if(r == R) {
+ r = alloc(sizeof(*r));
+ } else
+ freer = r->link;
+
+ *r = zreg;
+ return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+ Rgn *p1, *p2;
+ int c1, c2;
+
+ p1 = (Rgn*)a1;
+ p2 = (Rgn*)a2;
+ c1 = p2->cost;
+ c2 = p1->cost;
+ if(c1 -= c2)
+ return c1;
+ return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+ Reg *r, *r1, *r2;
+ Prog *p1;
+ int i, z;
+ long initpc, val, npc;
+ ulong vreg;
+ Bits bit;
+ struct
+ {
+ long m;
+ long c;
+ Reg* p;
+ } log5[6], *lp;
+
+ firstr = R;
+ lastr = R;
+ nvar = 0;
+ regbits = 0;
+ for(z=0; z<BITS; z++) {
+ externs.b[z] = 0;
+ params.b[z] = 0;
+ consts.b[z] = 0;
+ addrs.b[z] = 0;
+ }
+
+ /*
+ * pass 1
+ * build aux data structure
+ * allocate pcs
+ * find use and set of variables
+ */
+ val = 5L * 5L * 5L * 5L * 5L;
+ lp = log5;
+ for(i=0; i<5; i++) {
+ lp->m = val;
+ lp->c = 0;
+ lp->p = R;
+ val /= 5L;
+ lp++;
+ }
+ val = 0;
+ for(; p != P; p = p->link) {
+ switch(p->as) {
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ continue;
+ }
+ r = rega();
+ if(firstr == R) {
+ firstr = r;
+ lastr = r;
+ } else {
+ lastr->link = r;
+ r->p1 = lastr;
+ lastr->s1 = r;
+ lastr = r;
+ }
+ r->prog = p;
+ r->pc = val;
+ val++;
+
+ lp = log5;
+ for(i=0; i<5; i++) {
+ lp->c--;
+ if(lp->c <= 0) {
+ lp->c = lp->m;
+ if(lp->p != R)
+ lp->p->log5 = r;
+ lp->p = r;
+ (lp+1)->c = 0;
+ break;
+ }
+ lp++;
+ }
+
+ r1 = r->p1;
+ if(r1 != R)
+ switch(r1->prog->as) {
+ case ARET:
+ case AB:
+ case ARFE:
+ r->p1 = R;
+ r1->s1 = R;
+ }
+
+ /*
+ * left side always read
+ */
+ bit = mkvar(&p->from, p->as==AMOVW);
+ for(z=0; z<BITS; z++)
+ r->use1.b[z] |= bit.b[z];
+
+ /*
+ * right side depends on opcode
+ */
+ bit = mkvar(&p->to, 0);
+ if(bany(&bit))
+ switch(p->as) {
+ default:
+ diag(Z, "reg: unknown asop: %A", p->as);
+ break;
+
+ /*
+ * right side write
+ */
+ case ANOP:
+ case AMOVB:
+ case AMOVBU:
+ case AMOVH:
+ case AMOVHU:
+ case AMOVW:
+ case AMOVF:
+ case AMOVD:
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
+ break;
+
+ /*
+ * funny
+ */
+ case ABL:
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ break;
+ }
+
+ if(p->as == AMOVM) {
+ if(p->from.type == D_CONST)
+ z = p->from.offset;
+ else
+ z = p->to.offset;
+ for(i=0; z; i++) {
+ if(z&1)
+ regbits |= RtoB(i);
+ z >>= 1;
+ }
+ }
+ }
+ if(firstr == R)
+ return;
+ initpc = pc - val;
+ npc = val;
+
+ /*
+ * pass 2
+ * turn branch references to pointers
+ * build back pointers
+ */
+ for(r = firstr; r != R; r = r->link) {
+ p = r->prog;
+ if(p->to.type == D_BRANCH) {
+ val = p->to.offset - initpc;
+ r1 = firstr;
+ while(r1 != R) {
+ r2 = r1->log5;
+ if(r2 != R && val >= r2->pc) {
+ r1 = r2;
+ continue;
+ }
+ if(r1->pc == val)
+ break;
+ r1 = r1->link;
+ }
+ if(r1 == R) {
+ nearln = p->lineno;
+ diag(Z, "ref not found\n%P", p);
+ continue;
+ }
+ if(r1 == r) {
+ nearln = p->lineno;
+ diag(Z, "ref to self\n%P", p);
+ continue;
+ }
+ r->s2 = r1;
+ r->p2link = r1->p2;
+ r1->p2 = r;
+ }
+ }
+ if(debug['R']) {
+ p = firstr->prog;
+ print("\n%L %D\n", p->lineno, &p->from);
+ }
+
+ /*
+ * pass 2.5
+ * find looping structure
+ */
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ change = 0;
+ loopit(firstr, npc);
+
+ /*
+ * pass 3
+ * iterate propagating usage
+ * back until flow graph is complete
+ */
+loop1:
+ change = 0;
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ for(r = firstr; r != R; r = r->link)
+ if(r->prog->as == ARET)
+ prop(r, zbits, zbits);
+loop11:
+ /* pick up unreachable code */
+ i = 0;
+ for(r = firstr; r != R; r = r1) {
+ r1 = r->link;
+ if(r1 && r1->active && !r->active) {
+ prop(r, zbits, zbits);
+ i = 1;
+ }
+ }
+ if(i)
+ goto loop11;
+ if(change)
+ goto loop1;
+
+
+ /*
+ * pass 4
+ * iterate propagating register/variable synchrony
+ * forward until graph is complete
+ */
+loop2:
+ change = 0;
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ synch(firstr, zbits);
+ if(change)
+ goto loop2;
+
+ addsplits();
+
+ if(debug['R'] && debug['v']) {
+ print("\nprop structure:\n");
+ for(r = firstr; r != R; r = r->link) {
+ print("%ld:%P", r->loop, r->prog);
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r->set.b[z] |
+ r->refahead.b[z] | r->calahead.b[z] |
+ r->refbehind.b[z] | r->calbehind.b[z] |
+ r->use1.b[z] | r->use2.b[z];
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->use1))
+ print(" u1=%B", r->use1);
+ if(bany(&r->use2))
+ print(" u2=%B", r->use2);
+ if(bany(&r->set))
+ print(" st=%B", r->set);
+ if(bany(&r->refahead))
+ print(" ra=%B", r->refahead);
+ if(bany(&r->calahead))
+ print(" ca=%B", r->calahead);
+ if(bany(&r->refbehind))
+ print(" rb=%B", r->refbehind);
+ if(bany(&r->calbehind))
+ print(" cb=%B", r->calbehind);
+ }
+ print("\n");
+ }
+ }
+
+ /*
+ * pass 5
+ * isolate regions
+ * calculate costs (paint1)
+ */
+ r = firstr;
+ if(r) {
+ for(z=0; z<BITS; z++)
+ bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+ ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+ if(bany(&bit)) {
+ nearln = r->prog->lineno;
+ warn(Z, "used and not set: %B", bit);
+ if(debug['R'] && !debug['w'])
+ print("used and not set: %B\n", bit);
+ }
+ }
+
+ for(r = firstr; r != R; r = r->link)
+ r->act = zbits;
+ rgp = region;
+ nregion = 0;
+ for(r = firstr; r != R; r = r->link) {
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r->set.b[z] &
+ ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+ if(bany(&bit)) {
+ nearln = r->prog->lineno;
+ warn(Z, "set and not used: %B", bit);
+ if(debug['R'])
+ print("set and not used: %B\n", bit);
+ excise(r);
+ }
+ for(z=0; z<BITS; z++)
+ bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+ while(bany(&bit)) {
+ i = bnum(bit);
+ rgp->enter = r;
+ rgp->varno = i;
+ change = 0;
+ if(debug['R'] && debug['v'])
+ print("\n");
+ paint1(r, i);
+ bit.b[i/32] &= ~(1L<<(i%32));
+ if(change <= 0) {
+ if(debug['R'])
+ print("%L $%d: %B\n",
+ r->prog->lineno, change, blsh(i));
+ continue;
+ }
+ rgp->cost = change;
+ nregion++;
+ if(nregion >= NRGN) {
+ warn(Z, "too many regions");
+ goto brk;
+ }
+ rgp++;
+ }
+ }
+brk:
+ qsort(region, nregion, sizeof(region[0]), rcmp);
+
+ /*
+ * pass 6
+ * determine used registers (paint2)
+ * replace code (paint3)
+ */
+ rgp = region;
+ for(i=0; i<nregion; i++) {
+ bit = blsh(rgp->varno);
+ vreg = paint2(rgp->enter, rgp->varno);
+ vreg = allreg(vreg, rgp);
+ if(debug['R']) {
+ if(rgp->regno >= NREG)
+ print("%L $%d F%d: %B\n",
+ rgp->enter->prog->lineno,
+ rgp->cost,
+ rgp->regno-NREG,
+ bit);
+ else
+ print("%L $%d R%d: %B\n",
+ rgp->enter->prog->lineno,
+ rgp->cost,
+ rgp->regno,
+ bit);
+ }
+ if(rgp->regno != 0)
+ paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+ rgp++;
+ }
+ /*
+ * pass 7
+ * peep-hole on basic block
+ */
+ if(!debug['R'] || debug['P'])
+ peep();
+
+ /*
+ * pass 8
+ * recalculate pc
+ */
+ val = initpc;
+ for(r = firstr; r != R; r = r1) {
+ r->pc = val;
+ p = r->prog;
+ p1 = P;
+ r1 = r->link;
+ if(r1 != R)
+ p1 = r1->prog;
+ for(; p != p1; p = p->link) {
+ switch(p->as) {
+ default:
+ val++;
+ break;
+
+ case ANOP:
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ break;
+ }
+ }
+ }
+ pc = val;
+
+ /*
+ * fix up branches
+ */
+ if(debug['R'])
+ if(bany(&addrs))
+ print("addrs: %B\n", addrs);
+
+ r1 = 0; /* set */
+ for(r = firstr; r != R; r = r->link) {
+ p = r->prog;
+ if(p->to.type == D_BRANCH)
+ p->to.offset = r->s2->pc;
+ r1 = r;
+ }
+
+ /*
+ * last pass
+ * eliminate nops
+ * free aux structures
+ */
+ for(p = firstr->prog; p != P; p = p->link){
+ while(p->link && p->link->as == ANOP)
+ p->link = p->link->link;
+ }
+ if(r1 != R) {
+ r1->link = freer;
+ freer = firstr;
+ }
+}
+
+void
+addsplits(void)
+{
+ Reg *r, *r1;
+ int z, i;
+ Bits bit;
+
+ for(r = firstr; r != R; r = r->link) {
+ if(r->loop > 1)
+ continue;
+ if(r->prog->as == ABL)
+ continue;
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
+ if(r1->loop <= 1)
+ continue;
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r1->calbehind.b[z] &
+ (r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) &
+ ~(r->calahead.b[z] & addrs.b[z]);
+ while(bany(&bit)) {
+ i = bnum(bit);
+ bit.b[i/32] &= ~(1L << (i%32));
+ }
+ }
+ }
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+ Prog *p, *p1;
+ Adr *a;
+ Var *v;
+
+ p1 = alloc(sizeof(*p1));
+ *p1 = zprog;
+ p = r->prog;
+
+ p1->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+
+ v = var + bn;
+
+ a = &p1->to;
+ a->sym = v->sym;
+ a->name = v->name;
+ a->offset = v->offset;
+ a->etype = v->etype;
+ a->type = D_OREG;
+ if(a->etype == TARRAY || a->sym == S)
+ a->type = D_CONST;
+
+ p1->as = AMOVW;
+ if(v->etype == TCHAR || v->etype == TUCHAR)
+ p1->as = AMOVB;
+ if(v->etype == TSHORT || v->etype == TUSHORT)
+ p1->as = AMOVH;
+ if(v->etype == TFLOAT)
+ p1->as = AMOVF;
+ if(v->etype == TDOUBLE)
+ p1->as = AMOVD;
+
+ p1->from.type = D_REG;
+ p1->from.reg = rn;
+ if(rn >= NREG) {
+ p1->from.type = D_FREG;
+ p1->from.reg = rn-NREG;
+ }
+ if(!f) {
+ p1->from = *a;
+ *a = zprog.from;
+ a->type = D_REG;
+ a->reg = rn;
+ if(rn >= NREG) {
+ a->type = D_FREG;
+ a->reg = rn-NREG;
+ }
+ if(v->etype == TUCHAR)
+ p1->as = AMOVBU;
+ if(v->etype == TUSHORT)
+ p1->as = AMOVHU;
+ }
+ if(debug['R'])
+ print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+ Var *v;
+ int i, t, n, et, z;
+ long o;
+ Bits bit;
+ Sym *s;
+
+ t = a->type;
+ if(t == D_REG && a->reg != NREG)
+ regbits |= RtoB(a->reg);
+ if(t == D_FREG && a->reg != NREG)
+ regbits |= FtoB(a->reg);
+ s = a->sym;
+ o = a->offset;
+ et = a->etype;
+ if(s == S) {
+ if(t != D_CONST || !docon || a->reg != NREG)
+ goto none;
+ et = TLONG;
+ }
+ if(t == D_CONST) {
+ if(s == S && sval(o))
+ goto none;
+ }
+
+ n = a->name;
+ v = var;
+ for(i=0; i<nvar; i++) {
+ if(s == v->sym)
+ if(n == v->name)
+ if(o == v->offset)
+ goto out;
+ v++;
+ }
+ if(s)
+ if(s->name[0] == '.')
+ goto none;
+ if(nvar >= NVAR) {
+ if(debug['w'] > 1 && s)
+ warn(Z, "variable not optimized: %s", s->name);
+ goto none;
+ }
+ i = nvar;
+ nvar++;
+ v = &var[i];
+ v->sym = s;
+ v->offset = o;
+ v->etype = et;
+ v->name = n;
+ if(debug['R'])
+ print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+ bit = blsh(i);
+ if(n == D_EXTERN || n == D_STATIC)
+ for(z=0; z<BITS; z++)
+ externs.b[z] |= bit.b[z];
+ if(n == D_PARAM)
+ for(z=0; z<BITS; z++)
+ params.b[z] |= bit.b[z];
+ if(v->etype != et || !typechlpfd[et]) /* funny punning */
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ if(t == D_CONST) {
+ if(s == S) {
+ for(z=0; z<BITS; z++)
+ consts.b[z] |= bit.b[z];
+ return bit;
+ }
+ if(et != TARRAY)
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ for(z=0; z<BITS; z++)
+ params.b[z] |= bit.b[z];
+ return bit;
+ }
+ if(t == D_OREG)
+ return bit;
+
+none:
+ return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+ Reg *r1, *r2;
+ int z;
+
+ for(r1 = r; r1 != R; r1 = r1->p1) {
+ for(z=0; z<BITS; z++) {
+ ref.b[z] |= r1->refahead.b[z];
+ if(ref.b[z] != r1->refahead.b[z]) {
+ r1->refahead.b[z] = ref.b[z];
+ change++;
+ }
+ cal.b[z] |= r1->calahead.b[z];
+ if(cal.b[z] != r1->calahead.b[z]) {
+ r1->calahead.b[z] = cal.b[z];
+ change++;
+ }
+ }
+ switch(r1->prog->as) {
+ case ABL:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] |= ref.b[z] | externs.b[z];
+ ref.b[z] = 0;
+ }
+ break;
+
+ case ATEXT:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = 0;
+ ref.b[z] = 0;
+ }
+ break;
+
+ case ARET:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = externs.b[z];
+ ref.b[z] = 0;
+ }
+ }
+ for(z=0; z<BITS; z++) {
+ ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+ r1->use1.b[z] | r1->use2.b[z];
+ cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+ r1->refbehind.b[z] = ref.b[z];
+ r1->calbehind.b[z] = cal.b[z];
+ }
+ if(r1->active)
+ break;
+ r1->active = 1;
+ }
+ for(; r != r1; r = r->p1)
+ for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+ prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ * the actual dominators if the flow graph is reducible
+ * otherwise, dominators plus some other non-dominators.
+ * See Matthew S. Hecht and Jeffrey D. Ullman,
+ * "Analysis of a Simple Algorithm for Global Data Flow Problems",
+ * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ * Oct. 1-3, 1973, pp. 207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ * such a node is a loop head.
+ * recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+ Reg *r1;
+
+ r->rpo = 1;
+ r1 = r->s1;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ r1 = r->s2;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ rpo2r[n] = r;
+ n++;
+ return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+ long t;
+
+ if(rpo1 == -1)
+ return rpo2;
+ while(rpo1 != rpo2){
+ if(rpo1 > rpo2){
+ t = rpo2;
+ rpo2 = rpo1;
+ rpo1 = t;
+ }
+ while(rpo1 < rpo2){
+ t = idom[rpo2];
+ if(t >= rpo2)
+ fatal(Z, "bad idom");
+ rpo2 = t;
+ }
+ }
+ return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+ while(s > r)
+ s = idom[s];
+ return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+ long src;
+
+ src = r->rpo;
+ if(r->p1 != R && doms(idom, src, r->p1->rpo))
+ return 1;
+ for(r = r->p2; r != R; r = r->p2link)
+ if(doms(idom, src, r->rpo))
+ return 1;
+ return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+ if(r->rpo < head || r->active == head)
+ return;
+ r->active = head;
+ r->loop += LOOP;
+ if(r->p1 != R)
+ loopmark(rpo2r, head, r->p1);
+ for(r = r->p2; r != R; r = r->p2link)
+ loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+ Reg *r1;
+ long i, d, me;
+
+ if(nr > maxnr) {
+ rpo2r = alloc(nr * sizeof(Reg*));
+ idom = alloc(nr * sizeof(long));
+ maxnr = nr;
+ }
+
+ d = postorder(r, rpo2r, 0);
+ if(d > nr)
+ fatal(Z, "too many reg nodes");
+ nr = d;
+ for(i = 0; i < nr / 2; i++){
+ r1 = rpo2r[i];
+ rpo2r[i] = rpo2r[nr - 1 - i];
+ rpo2r[nr - 1 - i] = r1;
+ }
+ for(i = 0; i < nr; i++)
+ rpo2r[i]->rpo = i;
+
+ idom[0] = 0;
+ for(i = 0; i < nr; i++){
+ r1 = rpo2r[i];
+ me = r1->rpo;
+ d = -1;
+ if(r1->p1 != R && r1->p1->rpo < me)
+ d = r1->p1->rpo;
+ for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+ if(r1->rpo < me)
+ d = rpolca(idom, d, r1->rpo);
+ idom[i] = d;
+ }
+
+ for(i = 0; i < nr; i++){
+ r1 = rpo2r[i];
+ r1->loop++;
+ if(r1->p2 != R && loophead(idom, r1))
+ loopmark(rpo2r, i, r1);
+ }
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+ Reg *r1;
+ int z;
+
+ for(r1 = r; r1 != R; r1 = r1->s1) {
+ for(z=0; z<BITS; z++) {
+ dif.b[z] = (dif.b[z] &
+ ~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+ r1->set.b[z] | r1->regdiff.b[z];
+ if(dif.b[z] != r1->regdiff.b[z]) {
+ r1->regdiff.b[z] = dif.b[z];
+ change++;
+ }
+ }
+ if(r1->active)
+ break;
+ r1->active = 1;
+ for(z=0; z<BITS; z++)
+ dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+ if(r1->s2 != R)
+ synch(r1->s2, dif);
+ }
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+ Var *v;
+ int i;
+
+ v = var + r->varno;
+ r->regno = 0;
+ switch(v->etype) {
+
+ default:
+ diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+ break;
+
+ case TCHAR:
+ case TUCHAR:
+ case TSHORT:
+ case TUSHORT:
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TARRAY:
+ i = BtoR(~b);
+ if(i && r->cost >= 0) {
+ r->regno = i;
+ return RtoB(i);
+ }
+ break;
+
+ case TVLONG:
+ case TDOUBLE:
+ case TFLOAT:
+ i = BtoF(~b);
+ if(i && r->cost >= 0) {
+ r->regno = i+NREG;
+ return FtoB(i);
+ }
+ break;
+ }
+ return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ ulong bb;
+
+ z = bn/32;
+ bb = 1L<<(bn%32);
+ if(r->act.b[z] & bb)
+ return;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(r1->act.b[z] & bb)
+ break;
+ r = r1;
+ }
+
+ if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tld %B $%d\n", r->loop,
+ r->prog, blsh(bn), change);
+ }
+ for(;;) {
+ r->act.b[z] |= bb;
+ p = r->prog;
+
+ if(r->use1.b[z] & bb) {
+ change += CREF * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tu1 %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ change += CREF * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tu2 %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tst %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ paint1(r1, bn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ paint1(r1, bn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(r->act.b[z] & bb)
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+ Reg *r1;
+ int z;
+ ulong bb, vreg;
+
+ z = bn/32;
+ bb = 1L << (bn%32);
+ vreg = regbits;
+ if(!(r->act.b[z] & bb))
+ return vreg;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(!(r1->act.b[z] & bb))
+ break;
+ r = r1;
+ }
+ for(;;) {
+ r->act.b[z] &= ~bb;
+
+ vreg |= r->regu;
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ vreg |= paint2(r1, bn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ vreg |= paint2(r1, bn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(!(r->act.b[z] & bb))
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+ return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ ulong bb;
+
+ z = bn/32;
+ bb = 1L << (bn%32);
+ if(r->act.b[z] & bb)
+ return;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(r1->act.b[z] & bb)
+ break;
+ r = r1;
+ }
+
+ if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+ addmove(r, bn, rn, 0);
+ for(;;) {
+ r->act.b[z] |= bb;
+ p = r->prog;
+
+ if(r->use1.b[z] & bb) {
+ if(debug['R'])
+ print("%P", p);
+ addreg(&p->from, rn);
+ if(debug['R'])
+ print("\t.c%P\n", p);
+ }
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ if(debug['R'])
+ print("%P", p);
+ addreg(&p->to, rn);
+ if(debug['R'])
+ print("\t.c%P\n", p);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb)
+ addmove(r, bn, rn, 1);
+ r->regu |= rb;
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ paint3(r1, bn, rb, rn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ paint3(r1, bn, rb, rn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(r->act.b[z] & bb)
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+ a->sym = 0;
+ a->name = D_NONE;
+ a->type = D_REG;
+ a->reg = rn;
+ if(rn >= NREG) {
+ a->type = D_FREG;
+ a->reg = rn-NREG;
+ }
+}
+
+/*
+ * bit reg
+ * 0 R0
+ * 1 R1
+ * ... ...
+ * 10 R10
+ */
+long
+RtoB(int r)
+{
+
+ if(r >= REGMIN && r <= REGMAX)
+ return 1L << r;
+ return 0;
+}
+
+int
+BtoR(long b)
+{
+ b &= 0x01fcL; // excluded R9 and R10 for extern registers
+ if(b == 0)
+ return 0;
+ return bitno(b);
+}
+
+/*
+ * bit reg
+ * 18 F2
+ * 19 F3
+ * ... ...
+ * 23 F7
+ */
+long
+FtoB(int f)
+{
+
+ if(f < 2 || f > NFREG-1)
+ return 0;
+ return 1L << (f + 16);
+}
+
+int
+BtoF(long b)
+{
+
+ b &= 0xfc0000L;
+ if(b == 0)
+ return 0;
+ return bitno(b) - 16;
+}
--- /dev/null
+++ b/utils/5c/sgen.c
@@ -1,0 +1,219 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+ if(n & 1) {
+ gins(ANOP, Z, Z);
+ p->to.type = D_REG;
+ p->to.reg = REGRET;
+ }
+ if(n & 2) {
+ gins(ANOP, Z, Z);
+ p->to.type = D_FREG;
+ p->to.reg = FREGRET;
+ }
+}
+
+/*
+ * calculate addressability as follows
+ * CONST ==> 20 $value
+ * NAME ==> 10 name
+ * REGISTER ==> 11 register
+ * INDREG ==> 12 *[(reg)+offset]
+ * &10 ==> 2 $name
+ * ADD(2, 20) ==> 2 $name+offset
+ * ADD(3, 20) ==> 3 $(reg)+offset
+ * &12 ==> 3 $(reg)+offset
+ * *11 ==> 11 ??
+ * *2 ==> 10 name
+ * *3 ==> 12 *(reg)+offset
+ * calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+ Node *l, *r;
+ int t;
+
+ if(n == Z)
+ return;
+ l = n->left;
+ r = n->right;
+ n->addable = 0;
+ n->complex = 0;
+ switch(n->op) {
+ case OCONST:
+ n->addable = 20;
+ return;
+
+ case OREGISTER:
+ n->addable = 11;
+ return;
+
+ case OINDREG:
+ n->addable = 12;
+ return;
+
+ case ONAME:
+ n->addable = 10;
+ return;
+
+ case OADDR:
+ xcom(l);
+ if(l->addable == 10)
+ n->addable = 2;
+ if(l->addable == 12)
+ n->addable = 3;
+ break;
+
+ case OIND:
+ xcom(l);
+ if(l->addable == 11)
+ n->addable = 12;
+ if(l->addable == 3)
+ n->addable = 12;
+ if(l->addable == 2)
+ n->addable = 10;
+ break;
+
+ case OADD:
+ xcom(l);
+ xcom(r);
+ if(l->addable == 20) {
+ if(r->addable == 2)
+ n->addable = 2;
+ if(r->addable == 3)
+ n->addable = 3;
+ }
+ if(r->addable == 20) {
+ if(l->addable == 2)
+ n->addable = 2;
+ if(l->addable == 3)
+ n->addable = 3;
+ }
+ break;
+
+ case OASLMUL:
+ case OASMUL:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OASASHL;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OMUL:
+ case OLMUL:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OASHL;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ t = vlog(l);
+ if(t >= 0) {
+ n->op = OASHL;
+ n->left = r;
+ n->right = l;
+ r = l;
+ l = n->left;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OASLDIV:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OASLSHR;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OLDIV:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OLSHR;
+ r->vconst = t;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OASLMOD:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OASAND;
+ r->vconst--;
+ }
+ break;
+
+ case OLMOD:
+ xcom(l);
+ xcom(r);
+ t = vlog(r);
+ if(t >= 0) {
+ n->op = OAND;
+ r->vconst--;
+ }
+ break;
+
+ default:
+ if(l != Z)
+ xcom(l);
+ if(r != Z)
+ xcom(r);
+ break;
+ }
+ if(n->addable >= 10)
+ return;
+
+ if(l != Z)
+ n->complex = l->complex;
+ if(r != Z) {
+ if(r->complex == n->complex)
+ n->complex = r->complex+1;
+ else
+ if(r->complex > n->complex)
+ n->complex = r->complex;
+ }
+ if(n->complex == 0)
+ n->complex++;
+
+ if(com64(n))
+ return;
+
+ switch(n->op) {
+ case OFUNC:
+ n->complex = FNX;
+ break;
+
+ case OADD:
+ case OXOR:
+ case OAND:
+ case OOR:
+ case OEQ:
+ case ONE:
+ /*
+ * immediate operators, make const on right
+ */
+ if(l->op == OCONST) {
+ n->left = r;
+ n->right = l;
+ }
+ break;
+ }
+}
--- /dev/null
+++ b/utils/5c/swt.c
@@ -1,0 +1,619 @@
+#include "gc.h"
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+ Node tn;
+
+ regalloc(&tn, ®node, Z);
+ swit2(q, nc, def, n, &tn);
+ regfree(&tn);
+}
+
+void
+swit2(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+ C1 *r;
+ int i;
+ long v;
+ Prog *sp;
+
+ if(nc >= 3) {
+ i = (q+nc-1)->val - (q+0)->val;
+ if(i > 0 && i < nc*2)
+ goto direct;
+ }
+ if(nc < 5) {
+ for(i=0; i<nc; i++) {
+ if(debug['K'])
+ print("case = %.8llux\n", q->val);
+ gopcode(OEQ, nodconst(q->val), n, Z);
+ patch(p, q->label);
+ q++;
+ }
+ gbranch(OGOTO);
+ patch(p, def);
+ return;
+ }
+
+ i = nc / 2;
+ r = q+i;
+ if(debug['K'])
+ print("case > %.8llux\n", r->val);
+ gopcode(OGT, nodconst(r->val), n, Z);
+ sp = p;
+ gopcode(OEQ, nodconst(r->val), n, Z); /* just gen the B.EQ */
+ patch(p, r->label);
+ swit2(q, i, def, n, tn);
+
+ if(debug['K'])
+ print("case < %.8llux\n", r->val);
+ patch(sp, pc);
+ swit2(r+1, nc-i-1, def, n, tn);
+ return;
+
+direct:
+ v = q->val;
+ if(v != 0)
+ gopcode(OSUB, nodconst(v), Z, n);
+ gopcode(OCASE, nodconst((q+nc-1)->val - v), n, Z);
+ patch(p, def);
+ for(i=0; i<nc; i++) {
+ if(debug['K'])
+ print("case = %.8llux\n", q->val);
+ while(q->val != v) {
+ nextpc();
+ p->as = ABCASE;
+ patch(p, def);
+ v++;
+ }
+ nextpc();
+ p->as = ABCASE;
+ patch(p, q->label);
+ q++;
+ v++;
+ }
+ gbranch(OGOTO); /* so that regopt() won't be confused */
+ patch(p, def);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+ int sh;
+ long v;
+ Node *l;
+
+ /*
+ * n1 gets adjusted/masked value
+ * n2 gets address of cell
+ * n3 gets contents of cell
+ */
+ l = b->left;
+ if(n2 != Z) {
+ regalloc(n1, l, nn);
+ reglcgen(n2, l, Z);
+ regalloc(n3, l, Z);
+ gopcode(OAS, n2, Z, n3);
+ gopcode(OAS, n3, Z, n1);
+ } else {
+ regalloc(n1, l, nn);
+ cgen(l, n1);
+ }
+ if(b->type->shift == 0 && typeu[b->type->etype]) {
+ v = ~0 + (1L << b->type->nbits);
+ gopcode(OAND, nodconst(v), Z, n1);
+ } else {
+ sh = 32 - b->type->shift - b->type->nbits;
+ if(sh > 0)
+ gopcode(OASHL, nodconst(sh), Z, n1);
+ sh += b->type->shift;
+ if(sh > 0)
+ if(typeu[b->type->etype])
+ gopcode(OLSHR, nodconst(sh), Z, n1);
+ else
+ gopcode(OASHR, nodconst(sh), Z, n1);
+ }
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+ long v;
+ Node nod, *l;
+ int sh;
+
+ /*
+ * n1 has adjusted/masked value
+ * n2 has address of cell
+ * n3 has contents of cell
+ */
+ l = b->left;
+ regalloc(&nod, l, Z);
+ v = ~0 + (1L << b->type->nbits);
+ gopcode(OAND, nodconst(v), Z, n1);
+ gopcode(OAS, n1, Z, &nod);
+ if(nn != Z)
+ gopcode(OAS, n1, Z, nn);
+ sh = b->type->shift;
+ if(sh > 0)
+ gopcode(OASHL, nodconst(sh), Z, &nod);
+ v <<= sh;
+ gopcode(OAND, nodconst(~v), Z, n3);
+ gopcode(OOR, n3, Z, &nod);
+ gopcode(OAS, &nod, Z, n2);
+
+ regfree(&nod);
+ regfree(n1);
+ regfree(n2);
+ regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+ long r;
+
+ if(suppress)
+ return nstring;
+ r = nstring;
+ while(n) {
+ string[mnstring] = *s++;
+ mnstring++;
+ nstring++;
+ if(mnstring >= NSNAME) {
+ gpseudo(ADATA, symstring, nodconst(0L));
+ p->from.offset += nstring - NSNAME;
+ p->reg = NSNAME;
+ p->to.type = D_SCONST;
+ memmove(p->to.sval, string, NSNAME);
+ mnstring = 0;
+ }
+ n--;
+ }
+ return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+ Node *l, *r, nod1, nod2;
+ Multab *m;
+ long v, vs;
+ int o;
+ char code[sizeof(m->code)+2], *p;
+
+ if(typefd[n->type->etype])
+ return 0;
+ l = n->left;
+ r = n->right;
+ if(l->op == OCONST) {
+ l = r;
+ r = n->left;
+ }
+ if(r->op != OCONST)
+ return 0;
+ v = convvtox(r->vconst, n->type->etype);
+ if(v != r->vconst) {
+ if(debug['M'])
+ print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+ return 0;
+ }
+ m = mulcon0(v);
+ if(!m) {
+ if(debug['M'])
+ print("%L multiply table: %lld\n", n->lineno, r->vconst);
+ return 0;
+ }
+ if(debug['M'] && debug['v'])
+ print("%L multiply: %ld\n", n->lineno, v);
+
+ memmove(code, m->code, sizeof(m->code));
+ code[sizeof(m->code)] = 0;
+
+ p = code;
+ if(p[1] == 'i')
+ p += 2;
+ regalloc(&nod1, n, nn);
+ cgen(l, &nod1);
+ vs = v;
+ regalloc(&nod2, n, Z);
+
+loop:
+ switch(*p) {
+ case 0:
+ regfree(&nod2);
+ if(vs < 0) {
+ gopcode(OAS, &nod1, Z, &nod1);
+ gopcode(OSUB, &nod1, nodconst(0), nn);
+ } else
+ gopcode(OAS, &nod1, Z, nn);
+ regfree(&nod1);
+ return 1;
+ case '+':
+ o = OADD;
+ goto addsub;
+ case '-':
+ o = OSUB;
+ addsub: /* number is r,n,l */
+ v = p[1] - '0';
+ r = &nod1;
+ if(v&4)
+ r = &nod2;
+ n = &nod1;
+ if(v&2)
+ n = &nod2;
+ l = &nod1;
+ if(v&1)
+ l = &nod2;
+ gopcode(o, l, n, r);
+ break;
+ default: /* op is shiftcount, number is r,l */
+ v = p[1] - '0';
+ r = &nod1;
+ if(v&2)
+ r = &nod2;
+ l = &nod1;
+ if(v&1)
+ l = &nod2;
+ v = *p - 'a';
+ if(v < 0 || v >= 32) {
+ diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+ break;
+ }
+ gopcode(OASHL, nodconst(v), l, r);
+ break;
+ }
+ p += 2;
+ goto loop;
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+
+ if(a->op == OCONST && typev[a->type->etype]) {
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gpseudo(ADATA, s, nod32const(a->vconst>>32));
+ else
+ gpseudo(ADATA, s, nod32const(a->vconst));
+ p->from.offset += o;
+ p->reg = 4;
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gpseudo(ADATA, s, nod32const(a->vconst));
+ else
+ gpseudo(ADATA, s, nod32const(a->vconst>>32));
+ p->from.offset += o + 4;
+ p->reg = 4;
+ return;
+ }
+ gpseudo(ADATA, s, a);
+ p->from.offset += o;
+ p->reg = w;
+ if(p->to.type == D_OREG)
+ p->to.type = D_CONST;
+}
+
+void zname(Biobuf*, Sym*, int);
+char* zaddr(char*, Adr*, int);
+void zwrite(Biobuf*, Prog*, int, int);
+void outhist(Biobuf*);
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+ char bf[100], *bp;
+
+ bf[0] = p->as;
+ bf[1] = p->scond;
+ bf[2] = p->reg;
+ bf[3] = p->lineno;
+ bf[4] = p->lineno>>8;
+ bf[5] = p->lineno>>16;
+ bf[6] = p->lineno>>24;
+ bp = zaddr(bf+7, &p->from, sf);
+ bp = zaddr(bp, &p->to, st);
+ Bwrite(b, bf, bp-bf);
+}
+
+void
+outcode(void)
+{
+ struct { Sym *sym; short type; } h[NSYM];
+ Prog *p;
+ Sym *s;
+ int sf, st, t, sym;
+
+ if(debug['S']) {
+ for(p = firstp; p != P; p = p->link)
+ if(p->as != ADATA && p->as != AGLOBL)
+ pc--;
+ for(p = firstp; p != P; p = p->link) {
+ print("%P\n", p);
+ if(p->as != ADATA && p->as != AGLOBL)
+ pc++;
+ }
+ }
+ outhist(&outbuf);
+ for(sym=0; sym<NSYM; sym++) {
+ h[sym].sym = S;
+ h[sym].type = 0;
+ }
+ sym = 1;
+ for(p = firstp; p != P; p = p->link) {
+ jackpot:
+ sf = 0;
+ s = p->from.sym;
+ while(s != S) {
+ sf = s->sym;
+ if(sf < 0 || sf >= NSYM)
+ sf = 0;
+ t = p->from.name;
+ if(h[sf].type == t)
+ if(h[sf].sym == s)
+ break;
+ s->sym = sym;
+ zname(&outbuf, s, t);
+ h[sym].sym = s;
+ h[sym].type = t;
+ sf = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ break;
+ }
+ st = 0;
+ s = p->to.sym;
+ while(s != S) {
+ st = s->sym;
+ if(st < 0 || st >= NSYM)
+ st = 0;
+ t = p->to.name;
+ if(h[st].type == t)
+ if(h[st].sym == s)
+ break;
+ s->sym = sym;
+ zname(&outbuf, s, t);
+ h[sym].sym = s;
+ h[sym].type = t;
+ st = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ if(st == sf)
+ goto jackpot;
+ break;
+ }
+ zwrite(&outbuf, p, sf, st);
+ }
+ firstp = P;
+ lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+ Hist *h;
+ char *p, *q, *op, c;
+ Prog pg;
+ int n;
+
+ pg = zprog;
+ pg.as = AHISTORY;
+ c = pathchar();
+ for(h = hist; h != H; h = h->link) {
+ p = h->name;
+ op = 0;
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && p && p[1] == ':'){
+ p += 2;
+ c = *p;
+ }
+ if(p && p[0] != c && h->offset == 0 && pathname){
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && pathname[1] == ':') {
+ op = p;
+ p = pathname+2;
+ c = *p;
+ } else if(pathname[0] == c){
+ op = p;
+ p = pathname;
+ }
+ }
+ while(p) {
+ q = utfrune(p, c);
+ if(q) {
+ n = q-p;
+ if(n == 0){
+ n = 1; /* leading "/" */
+ *p = '/'; /* don't emit "\" on windows */
+ }
+ q++;
+ } else {
+ n = strlen(p);
+ q = 0;
+ }
+ if(n) {
+ Bputc(b, ANAME);
+ Bputc(b, D_FILE);
+ Bputc(b, 1);
+ Bputc(b, '<');
+ Bwrite(b, p, n);
+ Bputc(b, 0);
+ }
+ p = q;
+ if(p == 0 && op) {
+ p = op;
+ op = 0;
+ }
+ }
+ pg.lineno = h->line;
+ pg.to.type = zprog.to.type;
+ pg.to.offset = h->offset;
+ if(h->offset)
+ pg.to.type = D_CONST;
+
+ zwrite(b, &pg, 0, 0);
+ }
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+ char *n, bf[7];
+ ulong sig;
+
+ n = s->name;
+ if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+ sig = sign(s);
+ bf[0] = ASIGNAME;
+ bf[1] = sig;
+ bf[2] = sig>>8;
+ bf[3] = sig>>16;
+ bf[4] = sig>>24;
+ bf[5] = t;
+ bf[6] = s->sym;
+ Bwrite(b, bf, 7);
+ s->sig = SIGDONE;
+ }
+ else{
+ bf[0] = ANAME;
+ bf[1] = t; /* type */
+ bf[2] = s->sym; /* sym */
+ Bwrite(b, bf, 3);
+ }
+ Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+ long l;
+ Ieee e;
+
+ bp[0] = a->type;
+ bp[1] = a->reg;
+ bp[2] = s;
+ bp[3] = a->name;
+ bp += 4;
+ switch(a->type) {
+ default:
+ diag(Z, "unknown type %d in zaddr", a->type);
+
+ case D_NONE:
+ case D_REG:
+ case D_FREG:
+ case D_PSR:
+ break;
+
+ case D_OREG:
+ case D_CONST:
+ case D_BRANCH:
+ case D_SHIFT:
+ l = a->offset;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ break;
+
+ case D_SCONST:
+ memmove(bp, a->sval, NSNAME);
+ bp += NSNAME;
+ break;
+
+ case D_FCONST:
+ ieeedtod(&e, a->dval);
+ l = e.l;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ l = e.h;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ break;
+ }
+ return bp;
+}
+
+long
+align(long i, Type *t, int op)
+{
+ long o;
+ Type *v;
+ int w;
+
+ o = i;
+ w = 1;
+ switch(op) {
+ default:
+ diag(Z, "unknown align opcode %d", op);
+ break;
+
+ case Asu2: /* padding at end of a struct */
+ w = SZ_LONG;
+ if(packflg)
+ w = packflg;
+ break;
+
+ case Ael1: /* initial align of struct element */
+ for(v=t; v->etype==TARRAY; v=v->link)
+ ;
+ w = ewidth[v->etype];
+ if(w <= 0 || w >= SZ_LONG)
+ w = SZ_LONG;
+ if(packflg)
+ w = packflg;
+ break;
+
+ case Ael2: /* width of a struct element */
+ o += t->width;
+ break;
+
+ case Aarg0: /* initial passbyptr argument in arg list */
+ if(typesuv[t->etype]) {
+ o = align(o, types[TIND], Aarg1);
+ o = align(o, types[TIND], Aarg2);
+ }
+ break;
+
+ case Aarg1: /* initial align of parameter */
+ w = ewidth[t->etype];
+ if(w <= 0 || w >= SZ_LONG) {
+ w = SZ_LONG;
+ break;
+ }
+ w = 1; /* little endian no adjustment */
+ break;
+
+ case Aarg2: /* width of a parameter */
+ o += t->width;
+ w = SZ_LONG;
+ break;
+
+ case Aaut3: /* total align of automatic */
+ o = align(o, t, Ael2);
+ o = align(o, t, Ael1);
+ w = SZ_LONG; /* because of a pun in cc/dcl.c:contig() */
+ break;
+ }
+ o = round(o, w);
+ if(debug['A'])
+ print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+ return o;
+}
+
+long
+maxround(long max, long v)
+{
+ v = round(v, SZ_LONG);
+ if(v > max)
+ return v;
+ return max;
+}
--- /dev/null
+++ b/utils/5c/txt.c
@@ -1,0 +1,1303 @@
+#include "gc.h"
+
+static char resvreg[nelem(reg)];
+
+void
+ginit(void)
+{
+ Type *t;
+
+ thechar = '5';
+ thestring = "arm";
+ exregoffset = REGEXT;
+ exfregoffset = FREGEXT;
+ listinit();
+ nstring = 0;
+ mnstring = 0;
+ nrathole = 0;
+ pc = 0;
+ breakpc = -1;
+ continpc = -1;
+ cases = C;
+ firstp = P;
+ lastp = P;
+ tfield = types[TLONG];
+
+ zprog.link = P;
+ zprog.as = AGOK;
+ zprog.reg = NREG;
+ zprog.from.type = D_NONE;
+ zprog.from.name = D_NONE;
+ zprog.from.reg = NREG;
+ zprog.to = zprog.from;
+ zprog.scond = 0xE;
+
+ regnode.op = OREGISTER;
+ regnode.class = CEXREG;
+ regnode.reg = REGTMP;
+ regnode.complex = 0;
+ regnode.addable = 11;
+ regnode.type = types[TLONG];
+
+ constnode.op = OCONST;
+ constnode.class = CXXX;
+ constnode.complex = 0;
+ constnode.addable = 20;
+ constnode.type = types[TLONG];
+
+ fconstnode.op = OCONST;
+ fconstnode.class = CXXX;
+ fconstnode.complex = 0;
+ fconstnode.addable = 20;
+ fconstnode.type = types[TDOUBLE];
+
+ nodsafe = new(ONAME, Z, Z);
+ nodsafe->sym = slookup(".safe");
+ nodsafe->type = types[TINT];
+ nodsafe->etype = types[TINT]->etype;
+ nodsafe->class = CAUTO;
+ complex(nodsafe);
+
+ t = typ(TARRAY, types[TCHAR]);
+ symrathole = slookup(".rathole");
+ symrathole->class = CGLOBL;
+ symrathole->type = t;
+
+ nodrat = new(ONAME, Z, Z);
+ nodrat->sym = symrathole;
+ nodrat->type = types[TIND];
+ nodrat->etype = TVOID;
+ nodrat->class = CGLOBL;
+ complex(nodrat);
+ nodrat->type = t;
+
+ nodret = new(ONAME, Z, Z);
+ nodret->sym = slookup(".ret");
+ nodret->type = types[TIND];
+ nodret->etype = TIND;
+ nodret->class = CPARAM;
+ nodret = new(OIND, nodret, Z);
+ complex(nodret);
+
+ com64init();
+
+ memset(reg, 0, sizeof(reg));
+ /* don't allocate */
+ reg[REGTMP] = 1;
+ reg[REGSB] = 1;
+ reg[REGSP] = 1;
+ reg[REGLINK] = 1;
+ reg[REGPC] = 1;
+ /* keep two external registers */
+ reg[REGEXT] = 1;
+ reg[REGEXT-1] = 1;
+ memmove(resvreg, reg, sizeof(reg));
+}
+
+void
+gclean(void)
+{
+ int i;
+ Sym *s;
+
+ for(i=0; i<NREG; i++)
+ if(reg[i] && !resvreg[i])
+ diag(Z, "reg %d left allocated", i);
+ for(i=NREG; i<NREG+NFREG; i++)
+ if(reg[i] && !resvreg[i])
+ diag(Z, "freg %d left allocated", i-NREG);
+ while(mnstring)
+ outstring("", 1L);
+ symstring->type->width = nstring;
+ symrathole->type->width = nrathole;
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ if(s->type == T)
+ continue;
+ if(s->type->width == 0)
+ continue;
+ if(s->class != CGLOBL && s->class != CSTATIC)
+ continue;
+ if(s->type == types[TENUM])
+ continue;
+ gpseudo(AGLOBL, s, nodconst(s->type->width));
+ }
+ nextpc();
+ p->as = AEND;
+ outcode();
+}
+
+void
+nextpc(void)
+{
+
+ p = alloc(sizeof(*p));
+ *p = zprog;
+ p->lineno = nearln;
+ pc++;
+ if(firstp == P) {
+ firstp = p;
+ lastp = p;
+ return;
+ }
+ lastp->link = p;
+ lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+ long regs;
+ Node fnxargs[20], *fnxp;
+
+ regs = cursafe;
+
+ fnxp = fnxargs;
+ garg1(n, tn1, tn2, 0, &fnxp); /* compile fns to temps */
+
+ curarg = 0;
+ fnxp = fnxargs;
+ garg1(n, tn1, tn2, 1, &fnxp); /* compile normal args and temps */
+
+ cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+ Node nod;
+
+ if(n == Z)
+ return;
+ if(n->op == OLIST) {
+ garg1(n->left, tn1, tn2, f, fnxp);
+ garg1(n->right, tn1, tn2, f, fnxp);
+ return;
+ }
+ if(f == 0) {
+ if(n->complex >= FNX) {
+ regsalloc(*fnxp, n);
+ nod = znode;
+ nod.op = OAS;
+ nod.left = *fnxp;
+ nod.right = n;
+ nod.type = n->type;
+ cgen(&nod, Z);
+ (*fnxp)++;
+ }
+ return;
+ }
+ if(typesuv[n->type->etype]) {
+ regaalloc(tn2, n);
+ if(n->complex >= FNX) {
+ sugen(*fnxp, tn2, n->type->width);
+ (*fnxp)++;
+ } else
+ sugen(n, tn2, n->type->width);
+ return;
+ }
+ if(REGARG >= 0 && curarg == 0 && typechlp[n->type->etype]) {
+ regaalloc1(tn1, n);
+ if(n->complex >= FNX) {
+ cgen(*fnxp, tn1);
+ (*fnxp)++;
+ } else
+ cgen(n, tn1);
+ return;
+ }
+ regalloc(tn1, n, Z);
+ if(n->complex >= FNX) {
+ cgen(*fnxp, tn1);
+ (*fnxp)++;
+ } else
+ cgen(n, tn1);
+ regaalloc(tn2, n);
+ gopcode(OAS, tn1, Z, tn2);
+ regfree(tn1);
+}
+
+Node*
+nodconst(long v)
+{
+ constnode.vconst = v;
+ return &constnode;
+}
+
+Node*
+nod32const(vlong v)
+{
+ constnode.vconst = v & MASK(32);
+ return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+ fconstnode.fconst = d;
+ return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+ *n = regnode;
+ n->reg = reg;
+ n->type = nn->type;
+ n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+ int r;
+
+ r = REGRET;
+ if(typefd[nn->type->etype])
+ r = FREGRET+NREG;
+ nodreg(n, nn, r);
+ reg[r]++;
+}
+
+int
+tmpreg(void)
+{
+ int i;
+
+ for(i=REGRET+1; i<NREG; i++)
+ if(reg[i] == 0)
+ return i;
+ diag(Z, "out of fixed registers");
+ return 0;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+ int i, j;
+ static int lasti;
+
+ switch(tn->type->etype) {
+ case TCHAR:
+ case TUCHAR:
+ case TSHORT:
+ case TUSHORT:
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ if(o != Z && o->op == OREGISTER) {
+ i = o->reg;
+ if(i >= 0 && i < NREG)
+ goto out;
+ }
+ j = lasti + REGRET+1;
+ for(i=REGRET+1; i<NREG; i++) {
+ if(j >= NREG)
+ j = REGRET+1;
+ if(reg[j] == 0 && resvreg[j] == 0) {
+ i = j;
+ goto out;
+ }
+ j++;
+ }
+ diag(tn, "out of fixed registers");
+ goto err;
+
+ case TFLOAT:
+ case TDOUBLE:
+ case TVLONG:
+ if(o != Z && o->op == OREGISTER) {
+ i = o->reg;
+ if(i >= NREG && i < NREG+NFREG)
+ goto out;
+ }
+ j = 0*2 + NREG;
+ for(i=NREG; i<NREG+NFREG; i++) {
+ if(j >= NREG+NFREG)
+ j = NREG;
+ if(reg[j] == 0) {
+ i = j;
+ goto out;
+ }
+ j++;
+ }
+ diag(tn, "out of float registers");
+ goto err;
+ }
+ diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+ nodreg(n, tn, 0);
+ return;
+out:
+ reg[i]++;
+ lasti++;
+ if(lasti >= 5)
+ lasti = 0;
+ nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+ Node nod;
+
+ nod = *tn;
+ nod.type = types[TIND];
+ regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+ int i;
+
+ i = 0;
+ if(n->op != OREGISTER && n->op != OINDREG)
+ goto err;
+ i = n->reg;
+ if(i < 0 || i >= sizeof(reg))
+ goto err;
+ if(reg[i] <= 0)
+ goto err;
+ reg[i]--;
+ return;
+err:
+ diag(n, "error in regfree: %d", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+ cursafe = align(cursafe, nn->type, Aaut3);
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+ *n = *nodsafe;
+ n->xoffset = -(stkoff + cursafe);
+ n->type = nn->type;
+ n->etype = nn->type->etype;
+ n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+ nodreg(n, nn, REGARG);
+ reg[REGARG]++;
+ curarg = align(curarg, nn->type, Aarg1);
+ curarg = align(curarg, nn->type, Aarg2);
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+ curarg = align(curarg, nn->type, Aarg1);
+ *n = *nn;
+ n->op = OINDREG;
+ n->reg = REGSP;
+ n->xoffset = curarg + SZ_LONG;
+ n->complex = 0;
+ n->addable = 20;
+ curarg = align(curarg, nn->type, Aarg2);
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+ if(n->op != OREGISTER) {
+ diag(n, "regind not OREGISTER");
+ return;
+ }
+ n->op = OINDREG;
+ n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+ Adr a;
+
+ naddr(n, &a);
+ if(R0ISZERO && a.type == D_CONST && a.offset == 0) {
+ a.type = D_REG;
+ a.reg = 0;
+ }
+ if(a.type != D_REG && a.type != D_FREG) {
+ if(n)
+ diag(n, "bad in raddr: %O", n->op);
+ else
+ diag(n, "bad in raddr: <null>");
+ p->reg = NREG;
+ } else
+ p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+ long v;
+
+ a->type = D_NONE;
+ if(n == Z)
+ return;
+ switch(n->op) {
+ default:
+ bad:
+ diag(n, "bad in naddr: %O", n->op);
+ break;
+
+ case OREGISTER:
+ a->type = D_REG;
+ a->sym = S;
+ a->reg = n->reg;
+ if(a->reg >= NREG) {
+ a->type = D_FREG;
+ a->reg -= NREG;
+ }
+ break;
+
+ case OIND:
+ naddr(n->left, a);
+ if(a->type == D_REG) {
+ a->type = D_OREG;
+ break;
+ }
+ if(a->type == D_CONST) {
+ a->type = D_OREG;
+ break;
+ }
+ goto bad;
+
+ case OINDREG:
+ a->type = D_OREG;
+ a->sym = S;
+ a->offset = n->xoffset;
+ a->reg = n->reg;
+ break;
+
+ case ONAME:
+ a->etype = n->etype;
+ a->type = D_OREG;
+ a->name = D_STATIC;
+ a->sym = n->sym;
+ a->offset = n->xoffset;
+ if(n->class == CSTATIC)
+ break;
+ if(n->class == CEXTERN || n->class == CGLOBL) {
+ a->name = D_EXTERN;
+ break;
+ }
+ if(n->class == CAUTO) {
+ a->name = D_AUTO;
+ break;
+ }
+ if(n->class == CPARAM) {
+ a->name = D_PARAM;
+ break;
+ }
+ goto bad;
+
+ case OCONST:
+ a->sym = S;
+ a->reg = NREG;
+ if(typefd[n->type->etype]) {
+ a->type = D_FCONST;
+ a->dval = n->fconst;
+ } else {
+ a->type = D_CONST;
+ a->offset = n->vconst;
+ }
+ break;
+
+ case OADDR:
+ naddr(n->left, a);
+ if(a->type == D_OREG) {
+ a->type = D_CONST;
+ break;
+ }
+ goto bad;
+
+ case OADD:
+ if(n->left->op == OCONST) {
+ naddr(n->left, a);
+ v = a->offset;
+ naddr(n->right, a);
+ } else {
+ naddr(n->right, a);
+ v = a->offset;
+ naddr(n->left, a);
+ }
+ a->offset += v;
+ break;
+
+ }
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+ Node nod1, nod2, nod3;
+
+ nodreg(&nod1, t, NREG+f1);
+ nodreg(&nod2, t, NREG+f2);
+ regalloc(&nod3, t, t);
+ gopcode(as, &nod1, &nod2, &nod3);
+ gmove(&nod3, t);
+ regfree(&nod3);
+}
+
+void
+gmovm(Node *f, Node *t, int w)
+{
+ gins(AMOVM, f, t);
+ p->scond |= C_UBIT;
+ if(w)
+ p->scond |= C_WBIT;
+}
+
+void
+gmove(Node *f, Node *t)
+{
+ int ft, tt, a;
+ Node nod, nod1;
+ Prog *p1;
+
+ ft = f->type->etype;
+ tt = t->type->etype;
+
+ if(ft == TDOUBLE && f->op == OCONST) {
+ }
+ if(ft == TFLOAT && f->op == OCONST) {
+ }
+
+ /*
+ * a load --
+ * put it into a register then
+ * worry what to do with it.
+ */
+ if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+ switch(ft) {
+ default:
+ a = AMOVW;
+ break;
+ case TFLOAT:
+ a = AMOVF;
+ break;
+ case TDOUBLE:
+ a = AMOVD;
+ break;
+ case TCHAR:
+ a = AMOVB;
+ break;
+ case TUCHAR:
+ a = AMOVBU;
+ break;
+ case TSHORT:
+ a = AMOVH;
+ break;
+ case TUSHORT:
+ a = AMOVHU;
+ break;
+ }
+ if(typechlp[ft] && typeilp[tt])
+ regalloc(&nod, t, t);
+ else
+ regalloc(&nod, f, t);
+ gins(a, f, &nod);
+ gmove(&nod, t);
+ regfree(&nod);
+ return;
+ }
+
+ /*
+ * a store --
+ * put it into a register then
+ * store it.
+ */
+ if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+ switch(tt) {
+ default:
+ a = AMOVW;
+ break;
+ case TUCHAR:
+ a = AMOVBU;
+ break;
+ case TCHAR:
+ a = AMOVB;
+ break;
+ case TUSHORT:
+ a = AMOVHU;
+ break;
+ case TSHORT:
+ a = AMOVH;
+ break;
+ case TFLOAT:
+ a = AMOVF;
+ break;
+ case TVLONG:
+ case TDOUBLE:
+ a = AMOVD;
+ break;
+ }
+ if(ft == tt)
+ regalloc(&nod, t, f);
+ else
+ regalloc(&nod, t, Z);
+ gmove(f, &nod);
+ gins(a, &nod, t);
+ regfree(&nod);
+ return;
+ }
+
+ /*
+ * type x type cross table
+ */
+ a = AGOK;
+ switch(ft) {
+ case TDOUBLE:
+ case TVLONG:
+ case TFLOAT:
+ switch(tt) {
+ case TDOUBLE:
+ case TVLONG:
+ a = AMOVD;
+ if(ft == TFLOAT)
+ a = AMOVFD;
+ break;
+ case TFLOAT:
+ a = AMOVDF;
+ if(ft == TFLOAT)
+ a = AMOVF;
+ break;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ a = AMOVDW;
+ if(ft == TFLOAT)
+ a = AMOVFW;
+ break;
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVDW;
+ if(ft == TFLOAT)
+ a = AMOVFW;
+ break;
+ }
+ break;
+ case TUINT:
+ case TULONG:
+ if(tt == TFLOAT || tt == TDOUBLE) {
+ // ugly and probably longer than necessary,
+ // but vfp has a single instruction for this,
+ // so hopefully it won't last long.
+ //
+ // tmp = f
+ // tmp1 = tmp & 0x80000000
+ // tmp ^= tmp1
+ // t = float(int32(tmp))
+ // if(tmp1)
+ // t += 2147483648.
+ //
+ regalloc(&nod, f, Z);
+ regalloc(&nod1, f, Z);
+ gins(AMOVW, f, &nod);
+ gins(AMOVW, &nod, &nod1);
+ gins(AAND, nodconst(0x80000000), &nod1);
+ gins(AEOR, &nod1, &nod);
+ if(tt == TFLOAT)
+ gins(AMOVWF, &nod, t);
+ else
+ gins(AMOVWD, &nod, t);
+ gins(ACMP, nodconst(0), Z);
+ raddr(&nod1, p);
+ gins(ABEQ, Z, Z);
+ regfree(&nod);
+ regfree(&nod1);
+ p1 = p;
+ regalloc(&nod, t, Z);
+ if(tt == TFLOAT) {
+ gins(AMOVF, nodfconst(2147483648.), &nod);
+ gins(AADDF, &nod, t);
+ } else {
+ gins(AMOVD, nodfconst(2147483648.), &nod);
+ gins(AADDD, &nod, t);
+ }
+ regfree(&nod);
+ patch(p1, pc);
+ return;
+ }
+ // fall through
+
+ case TINT:
+ case TLONG:
+ case TIND:
+ switch(tt) {
+ case TDOUBLE:
+ gins(AMOVWD, f, t);
+ return;
+ case TFLOAT:
+ gins(AMOVWF, f, t);
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TSHORT:
+ switch(tt) {
+ case TDOUBLE:
+ regalloc(&nod, f, Z);
+ gins(AMOVH, f, &nod);
+ gins(AMOVWD, &nod, t);
+ regfree(&nod);
+ return;
+ case TFLOAT:
+ regalloc(&nod, f, Z);
+ gins(AMOVH, f, &nod);
+ gins(AMOVWF, &nod, t);
+ regfree(&nod);
+ return;
+ case TUINT:
+ case TINT:
+ case TULONG:
+ case TLONG:
+ case TIND:
+ a = AMOVH;
+ break;
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TUSHORT:
+ switch(tt) {
+ case TDOUBLE:
+ regalloc(&nod, f, Z);
+ gins(AMOVHU, f, &nod);
+ gins(AMOVWD, &nod, t);
+ regfree(&nod);
+ return;
+ case TFLOAT:
+ regalloc(&nod, f, Z);
+ gins(AMOVHU, f, &nod);
+ gins(AMOVWF, &nod, t);
+ regfree(&nod);
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ a = AMOVHU;
+ break;
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TCHAR:
+ switch(tt) {
+ case TDOUBLE:
+ regalloc(&nod, f, Z);
+ gins(AMOVB, f, &nod);
+ gins(AMOVWD, &nod, t);
+ regfree(&nod);
+ return;
+ case TFLOAT:
+ regalloc(&nod, f, Z);
+ gins(AMOVB, f, &nod);
+ gins(AMOVWF, &nod, t);
+ regfree(&nod);
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ a = AMOVB;
+ break;
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TUCHAR:
+ switch(tt) {
+ case TDOUBLE:
+ regalloc(&nod, f, Z);
+ gins(AMOVBU, f, &nod);
+ gins(AMOVWD, &nod, t);
+ regfree(&nod);
+ return;
+ case TFLOAT:
+ regalloc(&nod, f, Z);
+ gins(AMOVBU, f, &nod);
+ gins(AMOVWF, &nod, t);
+ regfree(&nod);
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ a = AMOVBU;
+ break;
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ }
+ if(a == AGOK)
+ diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+ if(a == AMOVW || a == AMOVF || a == AMOVD)
+ if(samaddr(f, t))
+ return;
+ gins(a, f, t);
+}
+
+void
+gmover(Node *f, Node *t)
+{
+ int ft, tt, a;
+
+ ft = f->type->etype;
+ tt = t->type->etype;
+ a = AGOK;
+ if(typechlp[ft] && typechlp[tt] && ewidth[ft] >= ewidth[tt]){
+ switch(tt){
+ case TSHORT:
+ a = AMOVH;
+ break;
+ case TUSHORT:
+ a = AMOVHU;
+ break;
+ case TCHAR:
+ a = AMOVB;
+ break;
+ case TUCHAR:
+ a = AMOVBU;
+ break;
+ }
+ }
+ if(a == AGOK)
+ gmove(f, t);
+ else
+ gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+ nextpc();
+ p->as = a;
+ if(f != Z)
+ naddr(f, &p->from);
+ if(t != Z)
+ naddr(t, &p->to);
+ if(debug['g'])
+ print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+ int a, et;
+ Adr ta;
+
+ et = TLONG;
+ if(f1 != Z && f1->type != T)
+ et = f1->type->etype;
+ a = AGOK;
+ switch(o) {
+ case OAS:
+ gmove(f1, t);
+ return;
+
+ case OASADD:
+ case OADD:
+ a = AADD;
+ if(et == TFLOAT)
+ a = AADDF;
+ else
+ if(et == TDOUBLE || et == TVLONG)
+ a = AADDD;
+ break;
+
+ case OASSUB:
+ case OSUB:
+ if(f2 && f2->op == OCONST) {
+ Node *t = f1;
+ f1 = f2;
+ f2 = t;
+ a = ARSB;
+ } else
+ a = ASUB;
+ if(et == TFLOAT)
+ a = ASUBF;
+ else
+ if(et == TDOUBLE || et == TVLONG)
+ a = ASUBD;
+ break;
+
+ case OASOR:
+ case OOR:
+ a = AORR;
+ break;
+
+ case OASAND:
+ case OAND:
+ a = AAND;
+ break;
+
+ case OASXOR:
+ case OXOR:
+ a = AEOR;
+ break;
+
+ case OASLSHR:
+ case OLSHR:
+ a = ASRL;
+ break;
+
+ case OASASHR:
+ case OASHR:
+ a = ASRA;
+ break;
+
+ case OASASHL:
+ case OASHL:
+ a = ASLL;
+ break;
+
+ case OFUNC:
+ a = ABL;
+ break;
+
+ case OASMUL:
+ case OMUL:
+ a = AMUL;
+ if(et == TFLOAT)
+ a = AMULF;
+ else
+ if(et == TDOUBLE || et == TVLONG)
+ a = AMULD;
+ break;
+
+ case OASDIV:
+ case ODIV:
+ a = ADIV;
+ if(et == TFLOAT)
+ a = ADIVF;
+ else
+ if(et == TDOUBLE || et == TVLONG)
+ a = ADIVD;
+ break;
+
+ case OASMOD:
+ case OMOD:
+ a = AMOD;
+ break;
+
+ case OASLMUL:
+ case OLMUL:
+ a = AMULU;
+ break;
+
+ case OASLMOD:
+ case OLMOD:
+ a = AMODU;
+ break;
+
+ case OASLDIV:
+ case OLDIV:
+ a = ADIVU;
+ break;
+
+ case OCASE:
+ case OEQ:
+ case ONE:
+ case OLT:
+ case OLE:
+ case OGE:
+ case OGT:
+ case OLO:
+ case OLS:
+ case OHS:
+ case OHI:
+ a = ACMP;
+ if(et == TFLOAT)
+ a = ACMPF;
+ else
+ if(et == TDOUBLE || et == TVLONG)
+ a = ACMPD;
+ nextpc();
+ p->as = a;
+ naddr(f1, &p->from);
+ if(a == ACMP && f1->op == OCONST && p->from.offset < 0 &&
+ p->from.offset != 0x80000000) {
+ p->as = ACMN;
+ p->from.offset = -p->from.offset;
+ }
+ raddr(f2, p);
+ switch(o) {
+ case OEQ:
+ a = ABEQ;
+ break;
+ case ONE:
+ a = ABNE;
+ break;
+ case OLT:
+ a = ABLT;
+ break;
+ case OLE:
+ a = ABLE;
+ break;
+ case OGE:
+ a = ABGE;
+ break;
+ case OGT:
+ a = ABGT;
+ break;
+ case OLO:
+ a = ABLO;
+ break;
+ case OLS:
+ a = ABLS;
+ break;
+ case OHS:
+ a = ABHS;
+ break;
+ case OHI:
+ a = ABHI;
+ break;
+ case OCASE:
+ nextpc();
+ p->as = ACASE;
+ p->scond = 0x9;
+ naddr(f2, &p->from);
+ a = ABHI;
+ break;
+ }
+ f1 = Z;
+ f2 = Z;
+ break;
+ }
+ if(a == AGOK)
+ diag(Z, "bad in gopcode %O", o);
+ nextpc();
+ p->as = a;
+ if(f1 != Z)
+ naddr(f1, &p->from);
+ if(f2 != Z) {
+ naddr(f2, &ta);
+ p->reg = ta.reg;
+ }
+ if(t != Z)
+ naddr(t, &p->to);
+ if(debug['g'])
+ print("%P\n", p);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+ if(f->op != t->op)
+ return 0;
+ switch(f->op) {
+
+ case OREGISTER:
+ if(f->reg != t->reg)
+ break;
+ return 1;
+ }
+ return 0;
+}
+
+void
+gbranch(int o)
+{
+ int a;
+
+ a = AGOK;
+ switch(o) {
+ case ORETURN:
+ a = ARET;
+ break;
+ case OGOTO:
+ a = AB;
+ break;
+ }
+ nextpc();
+ if(a == AGOK) {
+ diag(Z, "bad in gbranch %O", o);
+ nextpc();
+ }
+ p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+ op->to.offset = pc;
+ op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+ nextpc();
+ p->as = a;
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ p->from.name = D_EXTERN;
+ if(a == ATEXT)
+ p->reg = (profileflg ? 0 : NOPROF);
+ if(s->class == CSTATIC)
+ p->from.name = D_STATIC;
+ naddr(n, &p->to);
+ if(a == ADATA || a == AGLOBL)
+ pc--;
+}
+
+int
+sconst(Node *n)
+{
+ vlong vv;
+
+ if(n->op == OCONST) {
+ if(!typefd[n->type->etype]) {
+ vv = n->vconst;
+ if(vv >= (vlong)(-32766) && vv < (vlong)32766)
+ return 1;
+ /*
+ * should be specialised for constant values which will
+ * fit in different instructionsl; for now, let 5l
+ * sort it out
+ */
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+sval(long v)
+{
+ int i;
+
+ for(i=0; i<16; i++) {
+ if((v & ~0xff) == 0)
+ return 1;
+ if((~v & ~0xff) == 0)
+ return 1;
+ v = (v<<2) | ((ulong)v>>30);
+ }
+ return 0;
+}
+
+long
+exreg(Type *t)
+{
+ long o;
+
+ if(typechlp[t->etype]) {
+ if(exregoffset <= REGEXT-2)
+ return 0;
+ o = exregoffset;
+ if(reg[o] && !resvreg[o])
+ return 0;
+ resvreg[o] = reg[o] = 1;
+ exregoffset--;
+ return o;
+ }
+ if(typefd[t->etype]) {
+ if(exfregoffset <= NFREG-1)
+ return 0;
+ o = exfregoffset + NREG;
+ if(reg[o] && !resvreg[o])
+ return 0;
+ resvreg[o] = reg[o] = 1;
+ exfregoffset--;
+ return o;
+ }
+ return 0;
+}
+
+schar ewidth[NTYPE] =
+{
+ -1, /* [TXXX] */
+ SZ_CHAR, /* [TCHAR] */
+ SZ_CHAR, /* [TUCHAR] */
+ SZ_SHORT, /* [TSHORT] */
+ SZ_SHORT, /* [TUSHORT] */
+ SZ_INT, /* [TINT] */
+ SZ_INT, /* [TUINT] */
+ SZ_LONG, /* [TLONG] */
+ SZ_LONG, /* [TULONG] */
+ SZ_VLONG, /* [TVLONG] */
+ SZ_VLONG, /* [TUVLONG] */
+ SZ_FLOAT, /* [TFLOAT] */
+ SZ_DOUBLE, /* [TDOUBLE] */
+ SZ_IND, /* [TIND] */
+ 0, /* [TFUNC] */
+ -1, /* [TARRAY] */
+ 0, /* [TVOID] */
+ -1, /* [TSTRUCT] */
+ -1, /* [TUNION] */
+ SZ_INT, /* [TENUM] */
+};
+
+long ncast[NTYPE] =
+{
+ 0, /* [TXXX] */
+ BCHAR|BUCHAR, /* [TCHAR] */
+ BCHAR|BUCHAR, /* [TUCHAR] */
+ BSHORT|BUSHORT, /* [TSHORT] */
+ BSHORT|BUSHORT, /* [TUSHORT] */
+ BINT|BUINT|BLONG|BULONG|BIND, /* [TINT] */
+ BINT|BUINT|BLONG|BULONG|BIND, /* [TUINT] */
+ BINT|BUINT|BLONG|BULONG|BIND, /* [TLONG] */
+ BINT|BUINT|BLONG|BULONG|BIND, /* [TULONG] */
+ BVLONG|BUVLONG, /* [TVLONG] */
+ BVLONG|BUVLONG, /* [TUVLONG] */
+ BFLOAT, /* [TFLOAT] */
+ BDOUBLE, /* [TDOUBLE] */
+ BLONG|BULONG|BIND, /* [TIND] */
+ 0, /* [TFUNC] */
+ 0, /* [TARRAY] */
+ 0, /* [TVOID] */
+ BSTRUCT, /* [TSTRUCT] */
+ BUNION, /* [TUNION] */
+ 0, /* [TENUM] */
+};
--- /dev/null
+++ b/utils/5coff/5coff.c
@@ -1,0 +1,316 @@
+#include "auxi.h"
+
+#define RND(x, y) ((((x)+(y)-1)/(y))*(y))
+
+char *cmd;
+int sflag, dflag;
+
+int ifd, ofd;
+Fhdr ihdr;
+
+long HEADR, INITTEXT, INITDAT, INITRND, INITENTRY;
+long textsize, datsize, bsssize;
+
+int cout;
+int thumb;
+
+static void get_file(char *);
+static void put_file(char *);
+static void usage(char *);
+static long strxtol(char *);
+static void readsyms(void);
+
+char *fail = "error";
+
+void
+main(int argc, char *argv[])
+{
+ char *a, *ifile, *ofile;
+
+ cmd = argv[0];
+
+ INITTEXT = -1;
+ INITDAT = -1;
+ INITRND = -1;
+ INITENTRY = -1;
+
+ ARGBEGIN {
+ /*
+ * Options without args
+ */
+ case 's':
+ sflag = 1;
+ break;
+ /*
+ * Options with args
+ */
+ case 'T':
+ a = ARGF();
+ if(a)
+ INITTEXT = strxtol(a);
+ break;
+ case 'D':
+ a = ARGF();
+ if(a)
+ INITDAT = strxtol(a);
+ break;
+ case 'R':
+ a = ARGF();
+ if(a)
+ INITRND = strxtol(a);
+ break;
+ case 'E':
+ a = ARGF();
+ if(a)
+ INITENTRY = strxtol(a);
+ break;
+ case 'd':
+ dflag |= strxtol(ARGF());
+ break;
+ default:
+ usage("Invalid option");
+ } ARGEND
+
+ if (argc != 2)
+ usage("Wrong number of arguments");
+
+ ifile = argv[0];
+ ofile = argv[1];
+
+ get_file(ifile);
+ put_file(ofile);
+ exits(0);
+}
+
+char usagemsg[] =
+"usage: %s options infile outfile\n\t options (for outfile): -H[1234] -s -T<text> -D<data> -R<rnd> -E<entry>\n";
+
+static void
+usage(char *msg)
+{
+ fprint(2, "***Error: %s\n", msg);
+ fprint(2, usagemsg, cmd);
+ exits("usage");
+}
+
+static long
+strxtol(char *s)
+{
+ char *es;
+ int base = 0;
+ long r;
+
+ if (*s == '0')
+ if (*++s == 'x'){
+ base = 16;
+ s++;
+ }
+ else
+ base = 8;
+ r = strtol(s, &es, base);
+ if (*es)
+ usage("bad number");
+ return(r);
+}
+
+static void
+get_file(char *ifile)
+{
+ int h;
+
+ ifd = open(ifile, OREAD);
+ if (ifd < 0) {
+ fprint(2, "5coff: open %s: %r\n", ifile);
+ exits("open");
+ }
+ h = crackhdr(ifd, &ihdr);
+ if (!h || dflag){
+ fprint(2, "Crackhdr: %d, type: %d, name: %s\n", h, ihdr.type, ihdr.name);
+ fprint(2, "txt %llux, ent %llux, txtsz %lux, dataddr %llux\n",
+ ihdr.txtaddr, ihdr.entry, ihdr.txtsz, ihdr.dataddr);
+ }
+ if (!h)
+ usage("File type not recognized");
+ machbytype(ihdr.type);
+ if (dflag)
+ fprint(2, "name: <%s> pgsize:%ux\n", mach->name, mach->pgsize);
+
+ HEADR = 22+28+3*48;
+ if(INITTEXT == -1)
+ INITTEXT = ihdr.txtaddr;
+ else
+ ihdr.txtaddr = INITTEXT;
+ if(INITDAT == -1)
+ INITDAT = ihdr.dataddr;
+ else
+ ihdr.dataddr = INITDAT;
+ if(INITENTRY == -1)
+ INITENTRY = ihdr.entry;
+ else
+ ihdr.entry = INITENTRY;
+ textsize = ihdr.txtsz;
+ datsize = ihdr.datsz;
+ bsssize = ihdr.bsssz;
+ if(INITRND > 0)
+ ihdr.dataddr = INITDAT = RND(INITTEXT+textsize, INITRND);
+ if(0){
+ INITTEXT = INITENTRY;
+ INITDAT = RND(INITTEXT+textsize, 4);
+ }
+ if(0){
+ print("H=%lux T=%lux D=%lux t=%lux d=%lux b=%lux e=%lux\n", HEADR, INITTEXT, INITDAT, textsize, datsize, bsssize, INITENTRY);
+ print("%llux %llux %llux %lux %lux %lux\n", ihdr.txtaddr, ihdr.dataddr, ihdr.entry, ihdr.txtsz, ihdr.datsz, ihdr.bsssz);
+ }
+
+ readsyms();
+}
+
+#define WB 128
+#define WSAFE (WB-4)
+char Wbuf[WB];
+char *wp = Wbuf;
+
+void
+cflush(void)
+{
+ if(wp > Wbuf)
+ write(ofd, Wbuf, wp-Wbuf);
+ wp = Wbuf;
+}
+
+void
+lput(long l)
+{
+ wp[0] = l>>24;
+ wp[1] = l>>16;
+ wp[2] = l>>8;
+ wp[3] = l;
+ wp += 4;
+ if(wp >= Wbuf+WSAFE)
+ cflush();
+}
+
+void
+cput(int l)
+{
+ wp[0] = l;
+ wp += 1;
+ if(wp >= Wbuf+WSAFE)
+ cflush();
+}
+
+void
+hputl(int l)
+{
+ wp[1] = l>>8;
+ wp[0] = l;
+ wp += 2;
+ if(wp >= Wbuf+WSAFE)
+ cflush();
+}
+
+void
+lputl(long l)
+{
+ wp[3] = l>>24;
+ wp[2] = l>>16;
+ wp[1] = l>>8;
+ wp[0] = l;
+ wp += 4;
+ if(wp >= Wbuf+WSAFE)
+ cflush();
+}
+
+static void
+copyseg(long sz)
+{
+ char buf[1024];
+
+ cflush();
+ while (sz > 0){
+ long n;
+ long r;
+
+ n = sz;
+ if (n > sizeof buf)
+ n = sizeof buf;
+ sz -= n;
+
+ if ((r = read(ifd, buf, n)) != n){
+ fprint(2, "%ld = read(...%ld) at %ld\n", r, n, (long)seek(ifd, 0, 1));
+ perror("Premature eof");
+ exits(fail);
+ }
+ if ((r = write(ofd, buf, n)) != n){
+ fprint(2, "%ld = write(...%ld)\n", r, n);
+ perror("Write error!");
+ exits(fail);
+ }
+ }
+ cflush();
+}
+
+static void
+put_file(char *ofile)
+{
+ ofd = create(ofile, OWRITE, 0666);
+ if (ofd < 0) {
+ fprint(2, "5coff: create %s: %r\n", ofile);
+ exits("create");
+ }
+ cout = ofd;
+
+ /* TBS lput for Plan9 header before ? */
+
+ seek(ifd, ihdr.txtoff, 0);
+ seek(ofd, HEADR, 0);
+ copyseg(ihdr.txtsz);
+
+ seek(ifd, ihdr.datoff, 0);
+ seek(ofd, HEADR+textsize, 0);
+ copyseg(ihdr.datsz);
+
+ seek(ofd, HEADR+textsize+datsize, 0);
+ coffsym();
+ cflush();
+ cofflc();
+ cflush();
+
+ seek(ofd, 0, 0);
+ coffhdr();
+ cflush();
+
+ close(ifd);
+ close(ofd);
+}
+
+long
+entryvalue(void)
+{
+ return INITENTRY;
+}
+
+void
+diag(char *s, ...)
+{
+ fprint(2, "%s\n", s);
+ exits("error");
+}
+
+static void
+readsyms(void)
+{
+ int i;
+ long n;
+ Sym *s;
+
+ if(sflag)
+ return;
+ n = syminit(ifd, &ihdr);
+ beginsym();
+ for(i = 0; i < n; i++){
+ s = getsym(i);
+ newsym(i, s->name, s->value, s->type);
+ }
+ endsym();
+}
--- /dev/null
+++ b/utils/5coff/NOTICE
@@ -1,0 +1,27 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+ Copyright © 2001-2003 Vita Nuova Holdings Limited.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
--- /dev/null
+++ b/utils/5coff/auxi.c
@@ -1,0 +1,251 @@
+#include "auxi.h"
+
+Prog *firstp, *textp, *curtext, *lastp, *etextp;
+Symx *hash[NHASH];
+Auto *lasta;
+long autosize;
+int version = 0;
+
+static int
+private(char *s)
+{
+ return strcmp(s, "safe") == 0 || strcmp(s, "ret") == 0 || strcmp(s, "string") == 0;
+}
+
+static int
+zlen(char *s)
+{
+ int i;
+
+ for(i=1; s[i] != 0 || s[i+1] != 0; i += 2)
+ ;
+ i++;
+ return i+1;
+}
+
+static Symx*
+allocsym(char *symb, int l, int v)
+{
+ Symx *s;
+
+ s = malloc(sizeof(Symx));
+ s->name = malloc(l);
+ memmove(s->name, symb, l);
+ s->name[l-1] = '\0';
+ s->type = 0;
+ s->version = v;
+ s->value = 0;
+ s->link = nil;
+ return s;
+}
+
+Symx*
+lookupsym(char *symb, int v)
+{
+ Symx *s, **as;
+ char *p;
+ long h;
+ int c, l;
+
+ h = v;
+ for(p=symb; c = *p; p++)
+ h = h+h+h + c;
+ l = (p - symb) + 1;
+ if(h < 0)
+ h = ~h;
+ h %= NHASH;
+ for(s = hash[h]; s != nil; s = s->link)
+ if(s->version == v)
+ if(memcmp(s->name, symb, l) == 0)
+ return s;
+ s = allocsym(symb, l, v);
+ for(as = &hash[h]; *as != nil; as = &((*as)->link))
+ ;
+ *as = s;
+ // s->link = hash[h];
+ // hash[h] = s;
+ return s;
+}
+
+static void
+addauto(Auto **aut, Symx *s, int t, long v)
+{
+ Auto *a, **aa;
+
+ a = (Auto*)malloc(sizeof(Auto));
+ a->asym = s;
+ a->link = nil;
+ a->aoffset = v;
+ a->type = t;
+ for(aa = aut; *aa != nil; aa = &((*aa)->link))
+ ;
+ *aa = a;
+}
+
+static Prog*
+newprog(int as, long pc, long ln)
+{
+ Prog *p;
+
+ p = (Prog *)malloc(sizeof(Prog));
+ p->as = as;
+ p->pc = pc;
+ p->line = ln;
+ p->link = p->cond = P;
+ if(firstp == P)
+ firstp = p;
+ else
+ lastp->link = p;
+ lastp = p;
+ if(as == ATEXT){
+ if(textp == P)
+ textp = p;
+ else
+ etextp->cond = p;
+ etextp = p;
+ }
+ return p;
+}
+
+static int
+line(long pc)
+{
+ char buf[1024], *s;
+
+ // return pc2line(pc);
+ if(fileline(buf, sizeof(buf), pc)){
+ for(s = buf; *s != ':' && *s != '\0'; s++)
+ ;
+ if(*s != ':')
+ return -1;
+ return atoi(s+1);
+ }
+ return -1;
+}
+
+static void
+lines(long v)
+{
+ long ll, nl, pc;
+ if(etextp != P){
+ ll = 0;
+ for(pc = etextp->pc; pc < v; pc += 4){
+ nl = line(pc);
+ if(nl != -1 && nl != ll){
+ newprog(ATEXT-1, pc, nl);
+ ll = nl;
+ }
+ }
+ pc -= 4;
+ if(lastp->pc != pc){
+ nl = line(pc);
+ if(nl != -1)
+ newprog(ATEXT-1, pc, nl);
+ }
+ }
+}
+
+void
+beginsym(void)
+{
+}
+
+/* create the same structures as in 5l so we can use same coff.c source file */
+void
+newsym(int i, char *nm, long v, int t)
+{
+ long l, ver;
+ char *os;
+ Symx *s;
+ Prog *p;
+
+ if(i == 0 && (t == 't' || t == 'T') && strcmp(nm, "etext") == 0)
+ return;
+ if(nm[0] == '.' && private(nm+1))
+ return;
+// print("%s %ld %c\n", nm, v, t);
+ ver = 0;
+ if(t == 't' || t == 'l' || t == 'd' || t == 'b'){
+ ver = ++version;
+ if(ver == 0)
+ diag("0 version for static");
+ }
+ if(t == 'a' || t == 'p')
+ s = allocsym(nm, strlen(nm)+1, 0);
+ else if(t == 'z' || t == 'Z')
+ s = allocsym(nm, zlen(nm), 0);
+ else if(t != 'm'){
+ s = lookupsym(nm, ver);
+ if(s->type != 0)
+ diag("seen sym before in newsym");
+ s->value = v;
+ }
+ else
+ s = nil;
+ switch(t){
+ case 'T':
+ case 'L':
+ case 't':
+ case 'l':
+ lines(v);
+ if(t == 'l' || t == 'L')
+ s->type = SLEAF;
+ else
+ s->type = STEXT;
+ p = newprog(ATEXT, v, line(v));
+ p->from.sym = s;
+ p->to.autom = lasta;
+ lasta = nil;
+ break;
+ case 'D':
+ case 'd':
+ s->type = SDATA;
+ s->value -= INITDAT;
+ break;
+ case 'B':
+ case 'b':
+ s->type = SBSS;
+ s->value -= INITDAT;
+ break;
+ case 'f':
+ // version++;
+ s->type = SFILE;
+ os = s->name;
+ l = strlen(os)+1;
+ s->name = malloc(l+1);
+ s->name[0] = '>';
+ memmove(s->name+1, os, l);
+ free(os);
+ break;
+/*
+ case 'f'+'a'-'A':
+ s->type = SFILE;
+ break;
+*/
+ case 'z':
+ addauto(&lasta, s, D_FILE, v);
+ break;
+ case 'Z':
+ addauto(&lasta, s, D_FILE1, v);
+ break;
+ case 'a':
+ addauto(&(etextp->to.autom), s, D_AUTO, -v);
+ break;
+ case 'p':
+ addauto(&(etextp->to.autom), s, D_PARAM, v);
+ break;
+ case 'm':
+ etextp->to.offset = v-4;
+ autosize = v;
+ break;
+ default:
+ diag("bad case in newsym");
+ break;
+ }
+}
+
+void
+endsym(void)
+{
+ lines(INITTEXT+textsize);
+}
--- /dev/null
+++ b/utils/5coff/auxi.h
@@ -1,0 +1,46 @@
+#define COFFCVT
+#define Sym Symx
+#include "../5l/l.h"
+#undef Sym
+#include <mach.h>
+
+/*
+ * auxi.c
+ */
+extern Symx *hash[NHASH];
+Symx *lookupsym(char*, int);
+void beginsym(void);
+void endsym(void);
+void newsym(int, char*, long, int);
+
+extern long autosize;
+extern Prog *firstp, *textp, *curtext, *lastp, *etextp;
+
+/*
+ * coff.c
+ */
+void coffhdr(void);
+void coffsym(void);
+void cofflc(void);
+void endsym(void);
+
+/*
+ * 5coff.c
+ */
+void cflush(void);
+void lput(long);
+void cput(int);
+void hputl(int);
+void lputl(long);
+long entryvalue(void);
+void diag(char*, ...);
+extern long HEADR; /* length of header */
+extern long INITDAT; /* data location */
+extern long INITRND; /* data round above text location */
+extern long INITTEXT; /* text location */
+extern long INITENTRY; /* entry point */
+extern long textsize;
+extern long datsize;
+extern long bsssize;
+extern int cout;
+extern int thumb;
--- /dev/null
+++ b/utils/5coff/coff.c
@@ -1,0 +1,640 @@
+#include "auxi.h"
+
+/*
+ * in some embedded coff files, edata and end have type 0 not 4,
+ * and file value is pointer to next file sym (as here), but the last one
+ * points to an external symbol, not 0 as here.
+ */
+
+#define C_NULL 0
+#define C_AUTO 1
+#define C_EXT 2
+#define C_STAT 3
+#define C_ARG 9
+#define C_FCN 101
+#define C_FILE 103
+
+#define T_VOID 0
+#define T_CHAR 2
+#define T_SHORT 3
+#define T_INT 4
+#define T_LONG 5
+
+#define DT_NON 0
+#define DT_PTR 1
+#define DT_FCN 2
+#define DT_ARY 3
+
+#define T(a, b) (((a)<<4)|b)
+
+#define DOTTEXT ".text"
+#define DOTDATA ".data"
+#define DOTBSS ".bss"
+#define DOTBF ".bf"
+#define DOTEF ".ef"
+
+#define SINDEX(s) (*((long*)(&s->become)))
+#define LINDEX(s) (*((long*)(&s->used)))
+
+typedef struct Hist Hist;
+
+struct Hist{
+ Auto *a;
+ Hist *n;
+};
+
+static int nsym, nlc, lines;
+
+static void cofflcsz(void);
+
+static Hist *freeh, *curh;
+
+static void
+dohist(Auto *a)
+{
+ Hist *h, **ha;
+
+ if(a->aoffset == 1){ /* new file */
+ for(ha = &curh; *ha != nil; ha = &((*ha)->n))
+ ;
+ *ha = freeh;
+ freeh = curh;
+ curh = nil;
+ }
+ if(freeh != nil){
+ h = freeh;
+ freeh = freeh->n;
+ }
+ else
+ h = malloc(sizeof(Hist));
+ h->a = a;
+ h->n = nil;
+ for(ha = &curh; *ha != nil; ha = &((*ha)->n))
+ ;
+ *ha = h;
+}
+
+static long
+lineno(long n)
+{
+ long o, d;
+ Hist *h;
+
+ if(1)
+ return n; /* now using fileline() not pc2line() */
+
+ if(curh == nil)
+ return 0;
+ o = curh->a->aoffset-1;
+ d = 1;
+ for(h = curh->n; d && h != nil; h = h->n){
+ if(h->a->asym->name[1] || h->a->asym->name[2]){
+ if(h->a->type == D_FILE1) {
+ ;
+ }
+ else if(d == 1 && n < h->a->aoffset)
+ break;
+ else if(d++ == 1)
+ o -= h->a->aoffset;
+ }
+ else if(--d == 1)
+ o += h->a->aoffset;
+ }
+ return n-o;
+}
+
+static char *
+filelookup(int k)
+{
+ int i;
+ Symx *s;
+
+ for(i = 0; i < NHASH; i++){
+ for(s = hash[i]; s != nil; s = s->link){
+ if(s->type == SFILE && k == s->value)
+ return s->name+1;
+ }
+ }
+ return "";
+}
+
+static char*
+filename(char *s)
+{
+ int j, k, l;
+ static char buf[256];
+
+ buf[0] = '\0';
+ if(s[0] != 0)
+ diag("bad filename");
+ for(j = 1; ; j += 2){
+ k = (s[j]<<8)|s[j+1];
+ if(k == 0)
+ break;
+ l = strlen(buf);
+ if(l != 0 && buf[l-1] != '/')
+ strcat(buf, "/");
+ strcat(buf, filelookup(k));
+ }
+ return buf;
+}
+
+static void
+sput(char *s, int n)
+{
+ int i;
+
+ for(i = 0; i < n && s != nil && *s != '\0'; i++, s++)
+ cput(*s);
+ for( ; i < n; i++)
+ cput(0);
+}
+
+static void
+coffsect(char *s, long a, long sz, long o, long lp, long nl, long f)
+{
+ if(0)
+ print("sect %s pa=%lux va=%lux sz=%lux\n", s, a, a, sz);
+ sput(s, 8); /* name <= 8 chars in len */
+ lputl(a); /* pa */
+ lputl(a); /* va */
+ lputl(sz); /* size */
+ lputl(o); /* file offset */
+ lputl(0); /* reloc */
+ lputl(lp); /* line nos */
+ lputl(0); /* no reloc entries */
+ lputl(nl); /* no line no entries */
+ lputl(f); /* flags */
+ hputl(0); /* reserved */
+ hputl(0); /* mem page no */
+}
+
+void
+coffhdr(void)
+{
+ if(0){
+ print("H=%lux t=%lux d=%lux b=%lux\n", HEADR, textsize, datsize, bsssize);
+ print("e=%lux ts=%lux ds=%lux\n", entryvalue(), INITTEXT, INITDAT);
+ }
+
+ /*
+ * file header
+ */
+ hputl(0xc2); /* version ID */
+ hputl(3); /* no section hdrs */
+ lputl(0); /* date stamp */
+ lputl(HEADR+textsize+datsize+6*nlc); /* sym table */
+ lputl(nsym); /* no sym table entries */
+ hputl(28); /* size optional hdr */
+ hputl(0x0103); /* flags */
+ hputl(0x97); /* target ID */
+ /*
+ * optional file header
+ */
+ hputl(0x108); /* magic */
+ hputl(0); /* version stamp */
+ lputl(textsize); /* text size */
+ lputl(datsize); /* data size */
+ lputl(bsssize); /* bss size */
+ lputl(entryvalue()); /* entry pt */
+ lputl(INITTEXT); /* text start */
+ lputl(INITDAT); /* data start */
+ /*
+ * sections
+ */
+ coffsect(DOTTEXT, INITTEXT, textsize, HEADR, HEADR+textsize+datsize, nlc, 0x20);
+ coffsect(DOTDATA, INITDAT, datsize, HEADR+textsize, 0, 0, 0x40);
+ coffsect(DOTBSS, INITDAT+datsize, bsssize, 0, 0, 0, 0x80);
+}
+
+static int
+private(char *s)
+{
+ return strcmp(s, "safe") == 0 || strcmp(s, "ret") == 0 || strcmp(s, "string") == 0;
+}
+
+static long stoff = 4;
+
+static long
+stput(char *s)
+{
+ long r;
+
+ r = stoff;
+ stoff += strlen(s)+1;
+ return r;
+}
+
+static long
+strput(char *s)
+{
+ int l;
+
+ if((l = strlen(s)) > 8){
+ if(*s == '.' && private(s+1))
+ return 0;
+ while(*s)
+ cput(*s++);
+ cput(*s);
+ return l+1;
+ }
+ return 0;
+}
+
+static void
+stflush(void)
+{
+ int i;
+ long o;
+ Prog *p;
+ Auto *a, *f;
+ Symx *s;
+ char *fn, file[256];
+
+ lputl(stoff);
+ o = 4;
+ for(p = firstp; p != P; p = p->link){
+ if(p->as == ATEXT){
+ f = nil;
+ fn = nil;
+ for(a = p->to.autom; a != nil; a = a->link){
+ if(a->type == D_FILE){
+ f = a;
+ break;
+ }
+ }
+ if(f != nil)
+ fn = filename(f->asym->name);
+ if(fn != nil && *fn != '\0' && strcmp(fn, file) != 0){
+ strcpy(file, fn);
+ o += strput(file);
+ }
+ o += strput(p->from.sym->name);
+ for(a = p->to.autom; a != nil; a = a->link){
+ if(a->type == D_AUTO || a->type == D_PARAM)
+ o += strput(a->asym->name);
+ }
+ }
+ }
+ for(i = 0; i < NHASH; i++){
+ for(s = hash[i]; s != nil; s = s->link){
+ if(s->version > 0 && (s->type == SDATA || s->type == SBSS))
+ o += strput(s->name);
+ }
+ }
+ for(i = 0; i < NHASH; i++){
+ for(s = hash[i]; s != nil; s = s->link){
+ if(s->version == 0 && (s->type == SDATA || s->type == SBSS))
+ o += strput(s->name);
+ }
+ }
+ if(o != stoff)
+ diag("bad stflush offset");
+}
+
+static int
+putsect(Symx *s)
+{
+ int sz, ln;
+
+ sz = ln = 0;
+ // isn't this repetition ?
+ if(strcmp(s->name, DOTTEXT) == 0){
+ sz = textsize;
+ ln = nlc;
+ }
+ else if(strcmp(s->name, DOTDATA) == 0)
+ sz = datsize;
+ else if(strcmp(s->name, DOTBSS) == 0)
+ sz = bsssize;
+ else
+ diag("bad putsect sym");
+ lputl(sz);
+ hputl(0);
+ hputl(ln);
+ sput(nil, 10);
+ return 1;
+}
+
+static int
+putfun(Symx *s)
+{
+ /* lputl(SINDEX(s)+2); */
+ lputl(0);
+ lputl(0); /* patched later */
+ lputl(HEADR+textsize+datsize+LINDEX(s));
+ lputl(0); /* patched later */
+ sput(nil, 2);
+ return 1;
+}
+
+static int
+putbf(int lno)
+{
+ lputl(0);
+ hputl(lno);
+ hputl(lines);
+ lputl(autosize);
+ lputl(0); /* patched later */
+ sput(nil, 2);
+ return 1;
+}
+
+static int
+putef(int lno)
+{
+ sput(nil, 4);
+ hputl(lno);
+ sput(nil, 12);
+ return 1;
+}
+
+static int
+putsym(Symx *s, int sc, int t, int lno)
+{
+ long v;
+
+ if(s == nil || s->name == nil || s->name[0] == '\0' || (s->name[0] == '.' && private(s->name+1)))
+ return 0;
+ if(0)
+ print("putsym %s %d %ld %d %d\n", s->name, s->type, s->value, sc, t);
+ if(strlen(s->name) <= 8)
+ sput(s->name, 8);
+ else{
+ lputl(0);
+ lputl(stput(s->name));
+ }
+ /* value */
+ v = s->value;
+ if(s->type == SDATA || s->type == SDATA1 || s->type == SBSS)
+ lputl(INITDAT+v);
+ else if(sc == C_AUTO)
+ lputl(autosize+v);
+ else if(sc == C_ARG)
+ lputl(autosize+v+4);
+ else
+ lputl(v);
+ switch(s->type){ /* section number */
+ case STEXT:
+ case SLEAF:
+ hputl(1);
+ break;
+ case SDATA:
+ case SDATA1:
+ hputl(2);
+ break;
+ case SBSS:
+ hputl(3);
+ break;
+ case SFILE:
+ hputl(-2);
+ break;
+ default:
+ diag("type %d in putsym", s->type);
+ break;
+ }
+ hputl(t); /* type */
+ cput(sc); /* storage class */
+ /* aux entries */
+ if(sc == C_STAT && t == T_VOID && s->name[0] == '.'){ /* section */
+ cput(1);
+ return 1+putsect(s);
+ }
+ else if((t>>4) == DT_FCN){ /* function */
+ cput(1);
+ return 1+putfun(s);
+ }
+ else if(sc == C_FCN && strcmp(s->name, DOTBF) == 0){ /* bf */
+ cput(1);
+ return 1+putbf(lno);
+ }
+ else if(sc == C_FCN && strcmp(s->name, DOTEF) == 0){ /* ef */
+ cput(1);
+ return 1+putef(lno);
+ }
+ cput(0); /* 0 aux entry */
+ return 1;
+}
+
+static Symx*
+defsym(char *p, int t, long v)
+{
+ Symx *s;
+
+ s = lookupsym(p, 0);
+ if(s->type == SDATA || s->type == SBSS)
+ return nil; /* already output */
+ if(s->type == 0 || s->type == SXREF){
+ s->type = t;
+ s->value = v;
+ }
+ return s;
+}
+
+static int
+specsym(char *p, int t, long v, int c)
+{
+ return putsym(defsym(p, t, v), c, T_VOID, 0);
+}
+
+static int
+cclass(Symx *s)
+{
+/*
+ if(s->version > 0 && dclass == D_EXTERN)
+ diag("%s: version %d dclass EXTERN", s->name, s->version);
+ if(s->version == 0 && dclass == D_STATIC)
+ diag("%s: version %d dclass STATIC", s->name, s->version);
+*/
+ return s->version > 0 ? C_STAT : C_EXT;
+}
+
+static void
+patchsym(long i, long o, long v)
+{
+ long oo;
+
+ cflush();
+ oo = seek(cout, 0, 1);
+ seek(cout, HEADR+textsize+datsize+6*nlc+18*i+o, 0);
+ lputl(v);
+ cflush();
+ seek(cout, oo, 0);
+}
+
+void
+coffsym(void)
+{
+ int i;
+ long ns, lno, lpc, v, vs, lastf;
+ Prog *p;
+ Auto *a, *f;
+ Symx *s, *bf, *ef, ts;
+ char *fn, file[256];
+
+ file[0] = '\0';
+ cofflcsz();
+ seek(cout, 6*nlc, 1); /* advance over line table */
+ ns = 0;
+ lpc = -1;
+ lno = -1;
+ lastf = -1;
+ bf = defsym(DOTBF, STEXT, 0);
+ ef = defsym(DOTEF, STEXT, 0);
+ for(p = firstp; p != P; p = p->link){
+ if(p->as != ATEXT){
+ if(p->line != 0)
+ lno = lineno(p->line);
+ }
+ if(p->as == ATEXT){
+ curtext = p;
+ autosize = p->to.offset+4;
+ if(lpc >= 0){
+ ef->value = lpc;
+ ns += putsym(ef, C_FCN, T_VOID, lno);
+ }
+ f = nil;
+ fn = nil;
+ for(a = p->to.autom; a != nil; a = a->link){
+ if(a->type == D_FILE || a->type == D_FILE1)
+ dohist(a);
+ if(f == nil && a->type == D_FILE)
+ f = a; /* main filename */
+ }
+ if(f != nil)
+ fn = filename(f->asym->name);
+ if(fn != nil && *fn != '\0' && strcmp(fn, file) != 0){
+ strcpy(file, fn);
+ ts.name = file;
+ ts.type = SFILE;
+ ts.value = 0;
+ if(lastf >= 0)
+ patchsym(lastf, 8, ns);
+ lastf = ns;
+ ns += putsym(&ts, C_FILE, T_VOID, 0);
+ }
+ if(p->link != P && p->link->line != 0)
+ lno = lineno(p->link->line);
+ else if(p->line != 0)
+ lno = lineno(p->line);
+ s = p->from.sym;
+ SINDEX(s) = ns;
+ ns += putsym(s, cclass(s), T(DT_FCN, T_INT), 0);
+ if(p->cond != P)
+ lines = LINDEX(p->cond->from.sym)-LINDEX(s)-1;
+ else
+ lines = 0;
+ bf->value = p->pc;
+ ns += putsym(bf, C_FCN, T_VOID, lno);
+ for(a = p->to.autom; a != nil; a = a->link){
+ if(a->type == D_AUTO || a->type == D_PARAM){
+ ts.name = a->asym->name;
+ ts.type = STEXT;
+ ts.value = a->aoffset;
+ ns += putsym(&ts, a->type == D_AUTO ? C_AUTO : C_ARG, T_INT, 0);
+ }
+ }
+ }
+ lpc = p->pc;
+ }
+ if(lpc >= 0){
+ ef->value = lpc;
+ ns += putsym(ef, C_FCN, T_VOID, lno);
+ }
+ /* patch up */
+ for(p = textp; p != P; p = p->cond){
+ s = p->from.sym;
+ if(p->cond != P){
+ v = SINDEX(p->cond->from.sym);
+ vs = p->cond->pc - p->pc;
+ }
+ else{
+ v = 0;
+ vs = INITTEXT+textsize-p->pc;
+ }
+ patchsym(SINDEX(s)+1, 4, 8*vs);
+ patchsym(SINDEX(s)+1, 12, v);
+ patchsym(SINDEX(s)+3, 12, v);
+ }
+ for(i = 0; i < NHASH; i++){
+ for(s = hash[i]; s != nil; s = s->link){
+ if(s->version > 0 && (s->type == SDATA || s->type == SBSS))
+ ns += putsym(s, cclass(s), T_INT, 0);
+ }
+ }
+ for(i = 0; i < NHASH; i++){
+ for(s = hash[i]; s != nil; s = s->link){
+ if(s->version == 0 && (s->type == SDATA || s->type == SBSS))
+ ns += putsym(s, cclass(s), T_INT, 0);
+ }
+ }
+ ns += specsym(DOTTEXT, STEXT, INITTEXT, C_STAT);
+ ns += specsym(DOTDATA, SDATA, 0, C_STAT);
+ ns += specsym(DOTBSS, SBSS, datsize, C_STAT);
+ ns += specsym("etext", STEXT, INITTEXT+textsize, C_EXT);
+ ns += specsym("edata", SDATA, datsize, C_EXT);
+ ns += specsym("end", SBSS, datsize+bsssize, C_EXT);
+ nsym = ns;
+ stflush();
+}
+
+void
+cofflc(void)
+{
+ long olc, nl;
+ Symx *s;
+ Prog *p;
+ Auto *a;
+
+ cflush();
+ seek(cout, HEADR+textsize+datsize, 0);
+ nl = 0;
+ /* opc = INITTEXT; */
+ olc = 0;
+ for(p = firstp; p != P; p = p->link){
+ if(p->as == ATEXT){
+ curtext = p;
+ s = p->from.sym;
+ /* opc = p->pc; */
+ for(a = p->to.autom; a != nil; a = a->link){
+ if(a->type == D_FILE || a->type == D_FILE1)
+ dohist(a);
+ }
+ lputl(SINDEX(s));
+ hputl(0);
+ nl++;
+ continue;
+ }
+ if(p->line == 0 || p->line == olc || p->as == ANOP)
+ continue;
+ lputl(p->pc);
+ hputl(lineno(p->line));
+ nl++;
+ olc = p->line;
+ }
+ if(nl != nlc)
+ diag("bad line count in cofflc()");
+ nlc = nl;
+}
+
+static void
+cofflcsz(void)
+{
+ long olc, nl;
+ Prog *p;
+
+ nl = 0;
+ olc = 0;
+ for(p = firstp; p != P; p = p->link){
+ if(p->as == ATEXT){
+ LINDEX(p->from.sym) = nl;
+ nl++;
+ continue;
+ }
+ if(p->line == 0 || p->line == olc || p->as == ANOP)
+ continue;
+ nl++;
+ olc = p->line;
+ }
+ nlc = nl;
+}
--- /dev/null
+++ b/utils/5coff/mkfile
@@ -1,0 +1,23 @@
+<../../mkconfig
+
+TARG=5coff
+
+OFILES= 5coff.$O\
+ coff.$O\
+ auxi.$O\
+
+HFILES=\
+ a.out.h\
+ bio.h\
+ mach.h\
+
+LIBS=mach bio 9 # order matters.
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+$O.readcoff: readcoff.$O
+ $LD $LDFLAGS -o $target readcoff.$O $libs $SYSLIBS
--- /dev/null
+++ b/utils/5coff/readcoff.c
@@ -1,0 +1,298 @@
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+
+int fd;
+static void readf(void);
+
+static void
+usage(char *msg)
+{
+ fprint(2, "***Error: %s\n", msg);
+ exits("usage");
+}
+
+static int
+cget(void)
+{
+ uchar b[1];
+
+ if(read(fd, b, 1) != 1){
+ fprint(2, "bad cget\n");
+ exits("cget");
+ }
+ return b[0];
+}
+
+static int
+hget(void)
+{
+ uchar b[2];
+
+ if(read(fd, b, 2) != 2){
+ fprint(2, "bad hget\n");
+ exits("hget");
+ }
+ return b[1]<<8 | b[0];
+}
+
+static int
+lget(void)
+{
+ uchar b[4];
+
+ if(read(fd, b, 4) != 4){
+ fprint(2, "bad lget\n");
+ exits("lget");
+ }
+ return b[3]<<24 | b[2]<<16 | b[1]<<8 | b[0];
+}
+
+static char *
+sget(char *st)
+{
+ int i;
+ static uchar buf[8+1];
+
+ for(i = 0; i < 8+1; i++)
+ buf[i] = 0;
+ if(read(fd, buf, 8) != 8){
+ fprint(2, "bad sget\n");
+ exits("sget");
+ }
+ if(buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
+ return st+(buf[7]<<24|buf[6]<<16|buf[5]<<8|buf[4]);
+ return (char*)buf;
+}
+
+void
+main(int argc, char *argv[])
+{
+ if (argc != 2)
+ usage("Wrong number of arguments");
+
+ fd = open(argv[1], OREAD);
+ if (fd < 0) {
+ fprint(2, "5coff: open %s: %r\n", argv[1]);
+ exits("open");
+ }
+ readf();
+ exits(0);
+}
+
+static void
+section(int i, char *st, int *linoff, int *linn)
+{
+ int pa, va, sz, off, rel, lin, nrel, nlin, f, res, pno;
+ char *nm;
+
+ nm = sget(st);
+ pa = lget();
+ va = lget();
+ sz = lget();
+ off = lget();
+ rel = lget();
+ lin = lget();
+ nrel = lget();
+ nlin = lget();
+ f = lget();
+ res = hget();
+ pno = hget();
+ print("sect %d %s: pa=0x%x va=0x%x sz=%d off=%d rel=%d lin=%d nrel=%d nlin=%d f=0x%x res=%d pno=%d\n", i, nm, pa, va, sz, off, rel, lin, nrel, nlin, f, res, pno);
+ *linoff = lin;
+ *linn = nlin;
+}
+
+static void
+opthdr(void)
+{
+ int mag, ver, textsz, datasz, bsssz, entry, text, data;
+
+ mag = hget();
+ ver = hget();
+ textsz = lget();
+ datasz = lget();
+ bsssz = lget();
+ entry = lget();
+ text = lget();
+ data = lget();
+ print("opt: mag=0x%x ver=%d txtsz=%d datsz=%d bsssz=%d ent=0x%x txt=0x%x dat=0x%x\n", mag, ver, textsz, datasz, bsssz, entry, text, data);
+}
+
+static void
+readhdr(int *o, int *ns, int *sy, int *nsy)
+{
+ int vid, nsec, date, sym, nsym, opt, f, tid;
+
+ vid = hget();
+ nsec = hget();
+ date = lget();
+ sym = lget();
+ nsym = lget();
+ opt = hget();
+ f = hget();
+ tid = hget();
+ print("hdr: vid=0x%x nsect=%d date=%d sym=%d nsym=%d opt=%d f=0x%x tid=0x%x\n", vid, nsec, date, sym, nsym, opt, f, tid);
+ *o = opt;
+ *ns = nsec;
+ *sy = sym;
+ *nsy = nsym;
+}
+
+static void
+readauxsect(int i)
+{
+ int sz, nrel, ln;
+
+ sz = lget();
+ nrel = hget();
+ ln = hget();
+ lget();
+ hget();
+ lget();
+ print("sym auxsect %d: sz=%d nrel=%d ln=%d\n", i, sz, nrel, ln);
+}
+
+static void
+readauxfun(int i)
+{
+ int ind, sz, fpln, nind;
+
+ ind = lget();
+ sz = lget();
+ fpln = lget();
+ nind = lget();
+ hget();
+ print("sym auxfun %d: ind=%d sz=%d fpln=%d nind=%d\n", i, ind, sz, fpln, nind);
+}
+
+static void
+readauxbf(int i)
+{
+ int rsav, lno, lns, fsz, nind;
+
+ rsav = lget();
+ lno = hget();
+ lns = hget();
+ fsz = lget();
+ nind = lget();
+ hget();
+ print("sym auxbf %d: rsav=%x lno=%d lns=%d fsz=%d nind=%d\n", i, rsav, lno, lns, fsz, nind);
+}
+
+static void
+readauxef(int i)
+{
+ int lno;
+
+ lget();
+ lno = hget();
+ lget();
+ lget();
+ lget();
+ print("sym auxef %d: lno=%d\n", i, lno);
+}
+
+static void
+readauxother(int i)
+{
+ lget();
+ lget();
+ hget();
+ lget();
+ lget();
+ print("sym auxother %d\n", i);
+}
+
+static int
+readsym(int i, char *st)
+{
+ int v, s, t, c, aux;
+ char *nm;
+
+ nm = sget(st);
+ v = lget();
+ s = hget();
+ t = hget();
+ c = cget();
+ aux = cget();
+ print("sym %d %s: val=%d sec=%d type=%d class=%d aux=%d\n", i, nm, v, s, t, c, aux);
+ if(aux){
+ i++;
+ if(strcmp(nm, ".text") == 0 || strcmp(nm, ".data") == 0 || strcmp(nm, ".bss") == 0)
+ readauxsect(i);
+ else if(strcmp(nm, ".bf") == 0)
+ readauxbf(i);
+ else if(strcmp(nm, ".ef") == 0)
+ readauxef(i);
+ else if((t&0x30) == 0x20) // will do
+ readauxfun(i);
+ else
+ readauxother(i);
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+readstr(int n)
+{
+ char *s = malloc(n);
+
+ if(read(fd, s+4, n-4) != n-4){
+ fprint(2, "bad readstr\n");
+ exits("sget");
+ }
+ return s;
+}
+
+static void
+readln(int i)
+{
+ int a, l;
+
+ a = lget();
+ l = hget();
+ if(l == 0)
+ print("line %d: sym=%d\n", i, a);
+ else
+ print("line %d: addr=0x%x line=%d\n", i, a, l);
+}
+
+static void
+readf()
+{
+ int i, opt, nsec, sym, nsym, stoff, strsz, linoff, nlin, lino, linn;
+ char *st;
+
+ seek(fd, 0, 0);
+ readhdr(&opt, &nsec, &sym, &nsym);
+ if(opt)
+ opthdr();
+ stoff = sym+18*nsym;
+ seek(fd, stoff, 0);
+ strsz = lget();
+ st = readstr(strsz);
+ linoff = nlin = 0;
+ seek(fd, 22+28, 0);
+ for(i = 0; i < nsec; i++){
+ section(i, st, &lino, &linn);
+ if(linn != 0){
+ if(nlin == 0){
+ nlin = linn;
+ linoff = lino;
+ }
+ else
+ print("multiple line no. tables\n");
+ }
+ }
+ seek(fd, sym, 0);
+ for(i = 0; i < nsym; i++)
+ i += readsym(i, st);
+ print("strsz = %d\n", strsz);
+ if(nlin != 0){
+ seek(fd, linoff, 0);
+ for(i = 0; i < nlin; i++)
+ readln(i);
+ }
+}
--- /dev/null
+++ b/utils/5cv/5cv.c
@@ -1,0 +1,353 @@
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+
+char *Cmd;
+int Hdrtype;
+int Strip;
+long Txtaddr = -1;
+
+int Ofd;
+int Ifd;
+Fhdr Ihdr;
+
+int Debug;
+
+static void get_file(char *);
+static void put_file(char *);
+static void Usage(char *);
+static long strxtol(char *);
+
+char *fail = "error";
+
+void
+main(int argc, char *argv[])
+{
+ char *ifile, *ofile;
+
+ Cmd = argv[0];
+ Hdrtype = 2;
+
+ ARGBEGIN {
+ /*
+ * Options without args
+ */
+ case 's':
+ Strip = 1;
+ break;
+ /*
+ * Options with args
+ */
+ case 'T':
+ Txtaddr = strxtol(ARGF());
+ break;
+ case 'H':
+ Hdrtype = strxtol(ARGF());
+ break;
+ case 'D':
+ Debug |= strxtol(ARGF());
+ break;
+ default:
+ Usage("Invalid option");
+ } ARGEND
+
+ if (argc != 2)
+ Usage("Wrong number of arguments");
+
+ ifile = argv[0];
+ ofile = argv[1];
+
+ get_file(ifile);
+ put_file(ofile);
+ exits(0);
+}
+
+char usagemsg[] =
+"Usage: %s options infile outfile\n\t options (for outfile): -H[123456] -s -T<text> \n";
+
+static void
+Usage(char *msg)
+{
+ fprint(2, "***Error: %s\n", msg);
+ fprint(2, usagemsg, Cmd);
+ exits("usage");
+}
+
+static long
+strxtol(char *s)
+{
+ char *es;
+ int base = 0;
+ long r;
+
+ if (*s == '0')
+ if (*++s == 'x'){
+ base = 16;
+ s++;
+ }
+ else
+ base = 8;
+ r = strtol(s, &es, base);
+ if (*es)
+ Usage("bad number");
+ return(r);
+}
+
+static void
+get_file(char *ifile)
+{
+ int h;
+ int d;
+
+ Ifd = open(ifile, OREAD);
+ if (Ifd < 0) {
+ fprint(2, "5cv: open %s: %r\n", ifile);
+ exits("open");
+ }
+ h = crackhdr(Ifd, &Ihdr);
+ if (!h || Debug){
+ fprint(2, "Crackhdr: %d, type: %d, name: %s\n", h, Ihdr.type, Ihdr.name);
+ fprint(2, "txt %llux, ent %llux, txtsz %lux, dataddr %llux\n",
+ Ihdr.txtaddr, Ihdr.entry, Ihdr.txtsz, Ihdr.dataddr);
+ }
+ if (!h)
+ Usage("File type not recognized");
+ machbytype(Ihdr.type);
+ if (Debug)
+ fprint(2, "name: <%s> pgsize:%ux\n", mach->name, mach->pgsize);
+
+ if (Txtaddr != -1){
+ d = Txtaddr - Ihdr.txtaddr;
+ Ihdr.txtaddr += d;
+ Ihdr.dataddr = Ihdr.txtaddr + Ihdr.txtsz;
+ }
+}
+
+char Wbuf[128];
+char *wp = Wbuf;
+
+void
+lput(long l)
+{
+ wp[0] = l>>24;
+ wp[1] = l>>16;
+ wp[2] = l>>8;
+ wp[3] = l;
+ wp += 4;
+}
+
+void
+lputl(long l)
+{
+ wp[3] = l>>24;
+ wp[2] = l>>16;
+ wp[1] = l>>8;
+ wp[0] = l;
+ wp += 4;
+}
+
+static void
+copyseg(long sz)
+{
+ char buf[1024];
+
+ while (sz > 0){
+ long n;
+ long r;
+
+ n = sz;
+ if (n > sizeof buf)
+ n = sizeof buf;
+ sz -= n;
+
+ if ((r = read(Ifd, buf, n)) != n){
+ fprint(2, "%ld = read(...%ld) at %ld\n", r, n, (long)seek(Ifd, 0, 1));
+ perror("Premature eof");
+ exits(fail);
+ }
+ if ((r = write(Ofd, buf, n)) != n){
+ fprint(2, "%ld = write(...%ld)\n", r, n);
+ perror("Write error!");
+ exits(fail);
+ }
+ }
+}
+
+static void
+zero(long sz)
+{
+ char buf[1024];
+
+ memset(buf, 0, sizeof buf);
+ while (sz > 0){
+ long n;
+ long r;
+
+ n = sz;
+ if (n > sizeof buf)
+ n = sizeof buf;
+ sz -= n;
+
+ if ((r = write(Ofd, buf, n)) != n){
+ fprint(2, "%ld = write(...%ld)\n", r, n);
+ perror("Write error!");
+ exits(fail);
+ }
+ }
+}
+
+static long
+rnd(long v, long r)
+{
+ long c;
+
+ if(r <= 0)
+ return v;
+ v += r - 1;
+ c = v % r;
+ if(c < 0)
+ c += r;
+ v -= c;
+ return v;
+}
+
+static void
+put_file(char *ofile)
+{
+ int ii;
+ long doff;
+ long dsize;
+ long hlen;
+ long pad;
+
+ Ofd = create(ofile, OWRITE, 0666);
+ if (Ofd < 0) {
+ fprint(2, "5cv: create %s: %r\n", ofile);
+ exits("create");
+ }
+
+ pad = 0;
+
+ switch(Hdrtype) {
+ case 1: /* aif for risc os */
+ Strip = 1;
+ hlen = 128;
+ lputl(0xe1a00000); /* NOP - decompress code */
+ lputl(0xe1a00000); /* NOP - relocation code */
+ lputl(0xeb000000 + 12); /* BL - zero init code */
+ lputl(0xeb000000 +
+ (Ihdr.entry
+ - Ihdr.txtaddr
+ + hlen
+ - 12
+ - 8) / 4); /* BL - entry code */
+
+ lputl(0xef000011); /* SWI - exit code */
+ doff = Ihdr.txtsz+hlen;
+ lputl(doff); /* text size */
+ dsize = Ihdr.datsz;
+ lputl(dsize); /* data size */
+ lputl(0); /* sym size */
+
+ lputl(Ihdr.bsssz); /* bss size */
+ lputl(0); /* sym type */
+ lputl(Ihdr.txtaddr-hlen); /* text addr */
+ lputl(0); /* workspace - ignored */
+
+ lputl(32); /* addr mode / data addr flag */
+ lputl(0); /* data addr */
+ for(ii=0; ii<2; ii++)
+ lputl(0); /* reserved */
+
+ for(ii=0; ii<15; ii++)
+ lputl(0xe1a00000); /* NOP - zero init code */
+ lputl(0xe1a0f00e); /* B (R14) - zero init return */
+ break;
+
+ case 2: /* plan 9 */
+ hlen = 32;
+ doff = hlen + Ihdr.txtsz;
+ dsize = Ihdr.datsz;
+ lput(0x647); /* magic */
+ lput(Ihdr.txtsz); /* sizes */
+ lput(Ihdr.datsz);
+ lput(Ihdr.bsssz);
+ if (Strip) /* nsyms */
+ lput(0);
+ else
+ lput(Ihdr.symsz);
+ lput(Ihdr.entry); /* va of entry */
+ lput(0L);
+ lput(Ihdr.lnpcsz);
+ break;
+
+ case 3: /* boot for NetBSD */
+ hlen = 32;
+ doff = rnd(hlen+Ihdr.txtsz, 4096);
+ dsize = rnd(Ihdr.datsz, 4096);
+ lput((143<<16)|0413); /* magic */
+ lputl(doff);
+ lputl(dsize);
+ lputl(Ihdr.bsssz);
+ if (Strip) /* nsyms */
+ lputl(0);
+ else
+ lputl(Ihdr.symsz);
+ lputl(Ihdr.entry); /* va of entry */
+ lputl(0L);
+ lputl(0L);
+ break;
+ case 4: /* no header, stripped, padded to 2K, for serial bootstrap */
+ hlen = 0;
+ Strip = 1;
+ doff = hlen + Ihdr.txtsz;
+ dsize = Ihdr.datsz;
+ pad = 2048;
+ break;
+ case 5: /* no header, stripped, for all sorts */
+ hlen = 0;
+ Strip = 1;
+ doff = hlen + Ihdr.txtsz;
+ dsize = Ihdr.datsz;
+ break;
+ case 6: /* fake EPOC IMG format header */
+ hlen = 256;
+ *wp++ = 'E';
+ *wp++ = 'P';
+ Strip = 1;
+ doff = hlen + Ihdr.txtsz;
+ dsize = Ihdr.datsz;
+ break;
+ default:
+ Usage("Bad -Htype");
+ return;
+ }
+ write(Ofd, Wbuf, hlen);
+
+ seek(Ifd, Ihdr.txtoff, 0);
+ copyseg(Ihdr.txtsz);
+
+ seek(Ifd, Ihdr.datoff, 0);
+ seek(Ofd, doff, 0);
+ copyseg(Ihdr.datsz);
+
+ if (!Strip) {
+ /* Write symbols */
+ seek(Ofd, doff + dsize, 0);
+ if (Ihdr.symsz){
+ seek(Ifd, Ihdr.symoff, 0);
+ copyseg(Ihdr.symsz);
+ }
+ if (Hdrtype == 2)
+ copyseg(Ihdr.lnpcsz);
+ }
+
+ if (pad) {
+ if (doff + Ihdr.datsz > pad) {
+ perror("Too big!");
+ exits(fail);
+ }
+ else if (doff + Ihdr.datsz < pad)
+ zero(pad - (doff + Ihdr.datsz));
+ }
+}
--- /dev/null
+++ b/utils/5cv/mkfile
@@ -1,0 +1,18 @@
+<../../mkconfig
+
+TARG=5cv
+
+OFILES= 5cv.$O\
+
+HFILES=\
+ a.out.h\
+ bio.h\
+ mach.h\
+
+LIBS=mach bio 9 # order matters.
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/5l/asm.c
@@ -1,0 +1,1806 @@
+#include "l.h"
+
+long OFFSET;
+
+static Prog *PP;
+
+long
+entryvalue(void)
+{
+ char *a;
+ Sym *s;
+
+ a = INITENTRY;
+ if(*a >= '0' && *a <= '9')
+ return atolwhex(a);
+ s = lookup(a, 0);
+ if(s->type == 0)
+ return INITTEXT;
+ switch(s->type) {
+ case STEXT:
+ case SLEAF:
+ break;
+ case SDATA:
+ if(dlm)
+ return s->value+INITDAT;
+ default:
+ diag("entry not text: %s", s->name);
+ }
+ return s->value;
+}
+
+void
+asmb(void)
+{
+ Prog *p;
+ long t, etext;
+ Optab *o;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f asm\n", cputime());
+ Bflush(&bso);
+ OFFSET = HEADR;
+ seek(cout, OFFSET, 0);
+ pc = INITTEXT;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT) {
+ curtext = p;
+ autosize = p->to.offset + 4;
+ }
+ if(p->pc != pc) {
+ diag("phase error %lux sb %lux",
+ p->pc, pc);
+ if(!debug['a'])
+ prasm(curp);
+ pc = p->pc;
+ }
+ curp = p;
+ o = oplook(p); /* could probably avoid this call */
+ asmout(p, o);
+ pc += o->size;
+ }
+
+ if(debug['a'])
+ Bprint(&bso, "\n");
+ Bflush(&bso);
+ cflush();
+
+ /* output strings in text segment */
+ etext = INITTEXT + textsize;
+ for(t = pc; t < etext; t += sizeof(buf)-100) {
+ if(etext-t > sizeof(buf)-100)
+ datblk(t, sizeof(buf)-100, 1);
+ else
+ datblk(t, etext-t, 1);
+ }
+
+ curtext = P;
+ switch(HEADTYPE) {
+ case 0:
+ case 1:
+ case 2:
+ case 5:
+ case 7:
+ OFFSET = HEADR+textsize;
+ seek(cout, OFFSET, 0);
+ break;
+ case 3:
+ case 6: /* no header, padded segments */
+ OFFSET = rnd(HEADR+textsize, 4096);
+ seek(cout, OFFSET, 0);
+ break;
+ }
+ if(dlm){
+ char buf[8];
+
+ write(cout, buf, INITDAT-textsize);
+ textsize = INITDAT;
+ }
+ for(t = 0; t < datsize; t += sizeof(buf)-100) {
+ if(datsize-t > sizeof(buf)-100)
+ datblk(t, sizeof(buf)-100, 0);
+ else
+ datblk(t, datsize-t, 0);
+ }
+
+ symsize = 0;
+ lcsize = 0;
+ if(!debug['s']) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f sym\n", cputime());
+ Bflush(&bso);
+ switch(HEADTYPE) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ debug['s'] = 1;
+ break;
+ case 2:
+ OFFSET = HEADR+textsize+datsize;
+ seek(cout, OFFSET, 0);
+ break;
+ case 3:
+ case 6: /* no header, padded segments */
+ OFFSET += rnd(datsize, 4096);
+ seek(cout, OFFSET, 0);
+ break;
+ case 7:
+ break;
+ }
+ if(!debug['s'])
+ asmsym();
+ if(debug['v'])
+ Bprint(&bso, "%5.2f pc\n", cputime());
+ Bflush(&bso);
+ if(!debug['s'])
+ asmlc();
+ if(dlm)
+ asmdyn();
+ cflush();
+ }
+ else if(dlm){
+ seek(cout, HEADR+textsize+datsize, 0);
+ asmdyn();
+ cflush();
+ }
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f header\n", cputime());
+ Bflush(&bso);
+ OFFSET = 0;
+ seek(cout, OFFSET, 0);
+ switch(HEADTYPE) {
+ case 0: /* no header */
+ case 6: /* no header, padded segments */
+ break;
+ case 1: /* aif for risc os */
+ lputl(0xe1a00000); /* NOP - decompress code */
+ lputl(0xe1a00000); /* NOP - relocation code */
+ lputl(0xeb000000 + 12); /* BL - zero init code */
+ lputl(0xeb000000 +
+ (entryvalue()
+ - INITTEXT
+ + HEADR
+ - 12
+ - 8) / 4); /* BL - entry code */
+
+ lputl(0xef000011); /* SWI - exit code */
+ lputl(textsize+HEADR); /* text size */
+ lputl(datsize); /* data size */
+ lputl(0); /* sym size */
+
+ lputl(bsssize); /* bss size */
+ lputl(0); /* sym type */
+ lputl(INITTEXT-HEADR); /* text addr */
+ lputl(0); /* workspace - ignored */
+
+ lputl(32); /* addr mode / data addr flag */
+ lputl(0); /* data addr */
+ for(t=0; t<2; t++)
+ lputl(0); /* reserved */
+
+ for(t=0; t<15; t++)
+ lputl(0xe1a00000); /* NOP - zero init code */
+ lputl(0xe1a0f00e); /* B (R14) - zero init return */
+ break;
+ case 2: /* plan 9 */
+ if(dlm)
+ lput(0x80000000|0x647); /* magic */
+ else
+ lput(0x647); /* magic */
+ lput(textsize); /* sizes */
+ lput(datsize);
+ lput(bsssize);
+ lput(symsize); /* nsyms */
+ lput(entryvalue()); /* va of entry */
+ lput(0L);
+ lput(lcsize);
+ break;
+ case 3: /* boot for NetBSD */
+ lput((143<<16)|0413); /* magic */
+ lputl(rnd(HEADR+textsize, 4096));
+ lputl(rnd(datsize, 4096));
+ lputl(bsssize);
+ lputl(symsize); /* nsyms */
+ lputl(entryvalue()); /* va of entry */
+ lputl(0L);
+ lputl(0L);
+ break;
+ case 4: /* boot for IXP1200 */
+ break;
+ case 5: /* boot for ipaq */
+ lputl(0xe3300000); /* nop */
+ lputl(0xe3300000); /* nop */
+ lputl(0xe3300000); /* nop */
+ lputl(0xe3300000); /* nop */
+ break;
+ case 7: /* elf */
+ debug['S'] = 1; /* symbol table */
+ elf32(ARM, ELFDATA2LSB, 0, nil);
+ break;
+ }
+ cflush();
+}
+
+void
+strnput(char *s, int n)
+{
+ for(; *s; s++){
+ cput(*s);
+ n--;
+ }
+ for(; n > 0; n--)
+ cput(0);
+}
+
+void
+cput(int c)
+{
+ cbp[0] = c;
+ cbp++;
+ cbc--;
+ if(cbc <= 0)
+ cflush();
+}
+
+void
+wput(long l)
+{
+
+ cbp[0] = l>>8;
+ cbp[1] = l;
+ cbp += 2;
+ cbc -= 2;
+ if(cbc <= 0)
+ cflush();
+}
+
+void
+wputl(long l)
+{
+
+ cbp[0] = l;
+ cbp[1] = l>>8;
+ cbp += 2;
+ cbc -= 2;
+ if(cbc <= 0)
+ cflush();
+}
+
+void
+lput(long l)
+{
+
+ cbp[0] = l>>24;
+ cbp[1] = l>>16;
+ cbp[2] = l>>8;
+ cbp[3] = l;
+ cbp += 4;
+ cbc -= 4;
+ if(cbc <= 0)
+ cflush();
+}
+
+void
+lputl(long l)
+{
+
+ cbp[3] = l>>24;
+ cbp[2] = l>>16;
+ cbp[1] = l>>8;
+ cbp[0] = l;
+ cbp += 4;
+ cbc -= 4;
+ if(cbc <= 0)
+ cflush();
+}
+
+void
+llput(vlong v)
+{
+ lput(v>>32);
+ lput(v);
+}
+
+void
+llputl(vlong v)
+{
+ lputl(v);
+ lputl(v>>32);
+}
+
+void
+cflush(void)
+{
+ int n;
+
+ n = sizeof(buf.cbuf) - cbc;
+ if(n)
+ write(cout, buf.cbuf, n);
+ cbp = buf.cbuf;
+ cbc = sizeof(buf.cbuf);
+}
+
+void
+nopstat(char *f, Count *c)
+{
+ if(c->outof)
+ Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f,
+ c->outof - c->count, c->outof,
+ (double)(c->outof - c->count)/c->outof);
+}
+
+void
+asmsym(void)
+{
+ Prog *p;
+ Auto *a;
+ Sym *s;
+ int h;
+
+ s = lookup("etext", 0);
+ if(s->type == STEXT)
+ putsymb(s->name, 'T', s->value, s->version);
+
+ for(h=0; h<NHASH; h++)
+ for(s=hash[h]; s!=S; s=s->link)
+ switch(s->type) {
+ case SCONST:
+ putsymb(s->name, 'D', s->value, s->version);
+ continue;
+
+ case SDATA:
+ putsymb(s->name, 'D', s->value+INITDAT, s->version);
+ continue;
+
+ case SBSS:
+ putsymb(s->name, 'B', s->value+INITDAT, s->version);
+ continue;
+
+ case SSTRING:
+ putsymb(s->name, 'T', s->value, s->version);
+ continue;
+
+ case SFILE:
+ putsymb(s->name, 'f', s->value, s->version);
+ continue;
+ }
+
+ for(p=textp; p!=P; p=p->cond) {
+ s = p->from.sym;
+ if(s->type != STEXT && s->type != SLEAF)
+ continue;
+
+ /* filenames first */
+ for(a=p->to.autom; a; a=a->link)
+ if(a->type == D_FILE)
+ putsymb(a->asym->name, 'z', a->aoffset, 0);
+ else
+ if(a->type == D_FILE1)
+ putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+ if(s->type == STEXT)
+ putsymb(s->name, 'T', s->value, s->version);
+ else
+ putsymb(s->name, 'L', s->value, s->version);
+
+ /* frame, auto and param after */
+ putsymb(".frame", 'm', p->to.offset+4, 0);
+ for(a=p->to.autom; a; a=a->link)
+ if(a->type == D_AUTO)
+ putsymb(a->asym->name, 'a', -a->aoffset, 0);
+ else
+ if(a->type == D_PARAM)
+ putsymb(a->asym->name, 'p', a->aoffset, 0);
+ }
+ if(debug['v'] || debug['n'])
+ Bprint(&bso, "symsize = %lud\n", symsize);
+ Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+ int i, f;
+
+ if(t == 'f')
+ s++;
+ lput(v);
+ if(ver)
+ t += 'a' - 'A';
+ cput(t+0x80); /* 0x80 is variable length */
+
+ if(t == 'Z' || t == 'z') {
+ cput(s[0]);
+ for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+ cput(s[i]);
+ cput(s[i+1]);
+ }
+ cput(0);
+ cput(0);
+ i++;
+ }
+ else {
+ for(i=0; s[i]; i++)
+ cput(s[i]);
+ cput(0);
+ }
+ symsize += 4 + 1 + i + 1;
+
+ if(debug['n']) {
+ if(t == 'z' || t == 'Z') {
+ Bprint(&bso, "%c %.8lux ", t, v);
+ for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+ f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+ Bprint(&bso, "/%x", f);
+ }
+ Bprint(&bso, "\n");
+ return;
+ }
+ if(ver)
+ Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+ else
+ Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+ }
+}
+
+#define MINLC 4
+void
+asmlc(void)
+{
+ long oldpc, oldlc;
+ Prog *p;
+ long v, s;
+
+ oldpc = INITTEXT;
+ oldlc = 0;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+ if(p->as == ATEXT)
+ curtext = p;
+ if(debug['V'])
+ Bprint(&bso, "%6lux %P\n",
+ p->pc, p);
+ continue;
+ }
+ if(debug['V'])
+ Bprint(&bso, "\t\t%6ld", lcsize);
+ v = (p->pc - oldpc) / MINLC;
+ while(v) {
+ s = 127;
+ if(v < 127)
+ s = v;
+ cput(s+128); /* 129-255 +pc */
+ if(debug['V'])
+ Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+ v -= s;
+ lcsize++;
+ }
+ s = p->line - oldlc;
+ oldlc = p->line;
+ oldpc = p->pc + MINLC;
+ if(s > 64 || s < -64) {
+ cput(0); /* 0 vv +lc */
+ cput(s>>24);
+ cput(s>>16);
+ cput(s>>8);
+ cput(s);
+ if(debug['V']) {
+ if(s > 0)
+ Bprint(&bso, " lc+%ld(%d,%ld)\n",
+ s, 0, s);
+ else
+ Bprint(&bso, " lc%ld(%d,%ld)\n",
+ s, 0, s);
+ Bprint(&bso, "%6lux %P\n",
+ p->pc, p);
+ }
+ lcsize += 5;
+ continue;
+ }
+ if(s > 0) {
+ cput(0+s); /* 1-64 +lc */
+ if(debug['V']) {
+ Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+ Bprint(&bso, "%6lux %P\n",
+ p->pc, p);
+ }
+ } else {
+ cput(64-s); /* 65-128 -lc */
+ if(debug['V']) {
+ Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+ Bprint(&bso, "%6lux %P\n",
+ p->pc, p);
+ }
+ }
+ lcsize++;
+ }
+ while(lcsize & 1) {
+ s = 129;
+ cput(s);
+ lcsize++;
+ }
+ if(debug['v'] || debug['V'])
+ Bprint(&bso, "lcsize = %ld\n", lcsize);
+ Bflush(&bso);
+}
+
+void
+datblk(long s, long n, int str)
+{
+ Sym *v;
+ Prog *p;
+ char *cast;
+ long a, l, fl, j, d;
+ int i, c;
+
+ memset(buf.dbuf, 0, n+100);
+ for(p = datap; p != P; p = p->link) {
+ if(str != (p->from.sym->type == SSTRING))
+ continue;
+ curp = p;
+ a = p->from.sym->value + p->from.offset;
+ l = a - s;
+ c = p->reg;
+ i = 0;
+ if(l < 0) {
+ if(l+c <= 0)
+ continue;
+ while(l < 0) {
+ l++;
+ i++;
+ }
+ }
+ if(l >= n)
+ continue;
+ if(p->as != AINIT && p->as != ADYNT) {
+ for(j=l+(c-i)-1; j>=l; j--)
+ if(buf.dbuf[j]) {
+ print("%P\n", p);
+ diag("multiple initialization");
+ break;
+ }
+ }
+ switch(p->to.type) {
+ default:
+ diag("unknown mode in initialization%P", p);
+ break;
+
+ case D_FCONST:
+ switch(c) {
+ default:
+ case 4:
+ fl = ieeedtof(p->to.ieee);
+ cast = (char*)&fl;
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[fnuxi4[i]];
+ l++;
+ }
+ break;
+ case 8:
+ cast = (char*)p->to.ieee;
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[fnuxi8[i]];
+ l++;
+ }
+ break;
+ }
+ break;
+
+ case D_SCONST:
+ for(; i<c; i++) {
+ buf.dbuf[l] = p->to.sval[i];
+ l++;
+ }
+ break;
+
+ case D_CONST:
+ d = p->to.offset;
+ v = p->to.sym;
+ if(v) {
+ switch(v->type) {
+ case SUNDEF:
+ ckoff(v, d);
+ case STEXT:
+ case SLEAF:
+ case SSTRING:
+ d += p->to.sym->value;
+ break;
+ case SDATA:
+ case SBSS:
+ d += p->to.sym->value + INITDAT;
+ }
+ if(dlm)
+ dynreloc(v, a+INITDAT, 1);
+ }
+ cast = (char*)&d;
+ switch(c) {
+ default:
+ diag("bad nuxi %d %d%P", c, i, curp);
+ break;
+ case 1:
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[inuxi1[i]];
+ l++;
+ }
+ break;
+ case 2:
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[inuxi2[i]];
+ l++;
+ }
+ break;
+ case 4:
+ for(; i<c; i++) {
+ buf.dbuf[l] = cast[inuxi4[i]];
+ l++;
+ }
+ break;
+ }
+ break;
+ }
+ }
+ write(cout, buf.dbuf, n);
+}
+
+void
+asmout(Prog *p, Optab *o)
+{
+ long o1, o2, o3, o4, o5, o6, v;
+ int r, rf, rt, rt2;
+ Sym *s;
+
+PP = p;
+ o1 = 0;
+ o2 = 0;
+ o3 = 0;
+ o4 = 0;
+ o5 = 0;
+ o6 = 0;
+ switch(o->type) {
+ default:
+ diag("unknown asm %d", o->type);
+ prasm(p);
+ break;
+
+ case 0: /* pseudo ops */
+ break;
+
+ case 1: /* op R,[R],R */
+ o1 = oprrr(p->as, p->scond);
+ rf = p->from.reg;
+ rt = p->to.reg;
+ r = p->reg;
+ if(p->to.type == D_NONE)
+ rt = 0;
+ if(p->as == AMOVW || p->as == AMVN)
+ r = 0;
+ else if(r == NREG)
+ r = rt;
+ o1 |= rf | (r<<16) | (rt<<12);
+ break;
+
+ case 2: /* movbu $I,[R],R */
+ aclass(&p->from);
+ o1 = oprrr(p->as, p->scond);
+ o1 |= immrot(instoffset);
+ rt = p->to.reg;
+ r = p->reg;
+ if(p->to.type == D_NONE)
+ rt = 0;
+ if(p->as == AMOVW || p->as == AMVN)
+ r = 0;
+ else if(r == NREG)
+ r = rt;
+ o1 |= (r<<16) | (rt<<12);
+ break;
+
+ case 3: /* add R<<[IR],[R],R */
+ mov:
+ aclass(&p->from);
+ o1 = oprrr(p->as, p->scond);
+ o1 |= p->from.offset;
+ rt = p->to.reg;
+ r = p->reg;
+ if(p->to.type == D_NONE)
+ rt = 0;
+ if(p->as == AMOVW || p->as == AMVN)
+ r = 0;
+ else if(r == NREG)
+ r = rt;
+ o1 |= (r<<16) | (rt<<12);
+ break;
+
+ case 4: /* add $I,[R],R */
+ aclass(&p->from);
+ o1 = oprrr(AADD, p->scond);
+ o1 |= immrot(instoffset);
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 |= r << 16;
+ o1 |= p->to.reg << 12;
+ break;
+
+ case 5: /* bra s */
+ v = -8;
+ if(p->cond == UP) {
+ s = p->to.sym;
+ if(s->type != SUNDEF)
+ diag("bad branch sym type");
+ v = (ulong)s->value >> (Roffset-2);
+ dynreloc(s, p->pc, 0);
+ }
+ else if(p->cond != P)
+ v = (p->cond->pc - pc) - 8;
+ o1 = opbra(p->as, p->scond);
+ o1 |= (v >> 2) & 0xffffff;
+ break;
+
+ case 6: /* b ,O(R) -> add $O,R,PC */
+ aclass(&p->to);
+ o1 = oprrr(AADD, p->scond);
+ o1 |= immrot(instoffset);
+ o1 |= p->to.reg << 16;
+ o1 |= REGPC << 12;
+ break;
+
+ case 7: /* bl ,O(R) -> mov PC,link; add $O,R,PC */
+ aclass(&p->to);
+ o1 = oprrr(AADD, p->scond);
+ o1 |= immrot(0);
+ o1 |= REGPC << 16;
+ o1 |= REGLINK << 12;
+
+ o2 = oprrr(AADD, p->scond);
+ o2 |= immrot(instoffset);
+ o2 |= p->to.reg << 16;
+ o2 |= REGPC << 12;
+ break;
+
+ case 8: /* sll $c,[R],R -> mov (R<<$c),R */
+ aclass(&p->from);
+ o1 = oprrr(p->as, p->scond);
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ o1 |= r;
+ o1 |= (instoffset&31) << 7;
+ o1 |= p->to.reg << 12;
+ break;
+
+ case 9: /* sll R,[R],R -> mov (R<<R),R */
+ o1 = oprrr(p->as, p->scond);
+ r = p->reg;
+ if(r == NREG)
+ r = p->to.reg;
+ o1 |= r;
+ o1 |= (p->from.reg << 8) | (1<<4);
+ o1 |= p->to.reg << 12;
+ break;
+
+ case 10: /* swi [$con] */
+ o1 = oprrr(p->as, p->scond);
+ if(p->to.type != D_NONE) {
+ aclass(&p->to);
+ o1 |= instoffset & 0xffffff;
+ }
+ break;
+
+ case 11: /* word */
+ switch(aclass(&p->to)) {
+ case C_LCON:
+ if(!dlm)
+ break;
+ if(p->to.name != D_EXTERN && p->to.name != D_STATIC)
+ break;
+ case C_ADDR:
+ if(p->to.sym->type == SUNDEF)
+ ckoff(p->to.sym, p->to.offset);
+ dynreloc(p->to.sym, p->pc, 1);
+ }
+ o1 = instoffset;
+ break;
+
+ case 12: /* movw $lcon, reg */
+ o1 = omvl(p, &p->from, p->to.reg);
+ break;
+
+ case 13: /* op $lcon, [R], R */
+ o1 = omvl(p, &p->from, REGTMP);
+ if(!o1)
+ break;
+ o2 = oprrr(p->as, p->scond);
+ o2 |= REGTMP;
+ r = p->reg;
+ if(p->as == AMOVW || p->as == AMVN)
+ r = 0;
+ else if(r == NREG)
+ r = p->to.reg;
+ o2 |= r << 16;
+ if(p->to.type != D_NONE)
+ o2 |= p->to.reg << 12;
+ break;
+
+ case 14: /* movb/movbu/movh/movhu R,R */
+ o1 = oprrr(ASLL, p->scond);
+
+ if(p->as == AMOVBU || p->as == AMOVHU)
+ o2 = oprrr(ASRL, p->scond);
+ else
+ o2 = oprrr(ASRA, p->scond);
+
+ r = p->to.reg;
+ o1 |= (p->from.reg)|(r<<12);
+ o2 |= (r)|(r<<12);
+ if(p->as == AMOVB || p->as == AMOVBU) {
+ o1 |= (24<<7);
+ o2 |= (24<<7);
+ } else {
+ o1 |= (16<<7);
+ o2 |= (16<<7);
+ }
+ break;
+
+ case 15: /* mul r,[r,]r */
+ o1 = oprrr(p->as, p->scond);
+ rf = p->from.reg;
+ rt = p->to.reg;
+ r = p->reg;
+ if(r == NREG)
+ r = rt;
+ if(rt == r) {
+ r = rf;
+ rf = rt;
+ }
+ if(0)
+ if(rt == r || rf == REGPC || r == REGPC || rt == REGPC) {
+ diag("bad registers in MUL");
+ prasm(p);
+ }
+ o1 |= (rf<<8) | r | (rt<<16);
+ break;
+
+
+ case 16: /* div r,[r,]r */
+ o1 = 0xf << 28;
+ o2 = 0;
+ break;
+
+ case 17:
+ o1 = oprrr(p->as, p->scond);
+ rf = p->from.reg;
+ rt = p->to.reg;
+ rt2 = p->to.offset;
+ r = p->reg;
+ o1 |= (rf<<8) | r | (rt<<16) | (rt2<<12);
+ break;
+
+ case 20: /* mov/movb/movbu R,O(R) */
+ aclass(&p->to);
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = osr(p->as, p->from.reg, instoffset, r, p->scond);
+ break;
+
+ case 21: /* mov/movbu O(R),R -> lr */
+ aclass(&p->from);
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = olr(instoffset, r, p->to.reg, p->scond);
+ if(p->as != AMOVW)
+ o1 |= 1<<22;
+ break;
+
+ case 22: /* movb/movh/movhu O(R),R -> lr,shl,shr */
+ aclass(&p->from);
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = olr(instoffset, r, p->to.reg, p->scond);
+
+ o2 = oprrr(ASLL, p->scond);
+ o3 = oprrr(ASRA, p->scond);
+ r = p->to.reg;
+ if(p->as == AMOVB) {
+ o2 |= (24<<7)|(r)|(r<<12);
+ o3 |= (24<<7)|(r)|(r<<12);
+ } else {
+ o2 |= (16<<7)|(r)|(r<<12);
+ if(p->as == AMOVHU)
+ o3 = oprrr(ASRL, p->scond);
+ o3 |= (16<<7)|(r)|(r<<12);
+ }
+ break;
+
+ case 23: /* movh/movhu R,O(R) -> sb,sb */
+ aclass(&p->to);
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = osr(AMOVH, p->from.reg, instoffset, r, p->scond);
+
+ o2 = oprrr(ASRL, p->scond);
+ o2 |= (8<<7)|(p->from.reg)|(REGTMP<<12);
+
+ o3 = osr(AMOVH, REGTMP, instoffset+1, r, p->scond);
+ break;
+
+ case 30: /* mov/movb/movbu R,L(R) */
+ o1 = omvl(p, &p->to, REGTMP);
+ if(!o1)
+ break;
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ o2 = osrr(p->from.reg, REGTMP,r, p->scond);
+ if(p->as != AMOVW)
+ o2 |= 1<<22;
+ break;
+
+ case 31: /* mov/movbu L(R),R -> lr[b] */
+ case 32: /* movh/movb L(R),R -> lr[b] */
+ o1 = omvl(p, &p->from, REGTMP);
+ if(!o1)
+ break;
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o2 = olrr(REGTMP,r, p->to.reg, p->scond);
+ if(p->as == AMOVBU || p->as == AMOVB)
+ o2 |= 1<<22;
+ if(o->type == 31)
+ break;
+
+ o3 = oprrr(ASLL, p->scond);
+
+ if(p->as == AMOVBU || p->as == AMOVHU)
+ o4 = oprrr(ASRL, p->scond);
+ else
+ o4 = oprrr(ASRA, p->scond);
+
+ r = p->to.reg;
+ o3 |= (r)|(r<<12);
+ o4 |= (r)|(r<<12);
+ if(p->as == AMOVB || p->as == AMOVBU) {
+ o3 |= (24<<7);
+ o4 |= (24<<7);
+ } else {
+ o3 |= (16<<7);
+ o4 |= (16<<7);
+ }
+ break;
+
+ case 33: /* movh/movhu R,L(R) -> sb, sb */
+ o1 = omvl(p, &p->to, REGTMP);
+ if(!o1)
+ break;
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ o2 = osrr(p->from.reg, REGTMP, r, p->scond);
+ o2 |= (1<<22) ;
+
+ o3 = oprrr(ASRL, p->scond);
+ o3 |= (8<<7)|(p->from.reg)|(p->from.reg<<12);
+ o3 |= (1<<6); /* ROR 8 */
+
+ o4 = oprrr(AADD, p->scond);
+ o4 |= (REGTMP << 12) | (REGTMP << 16);
+ o4 |= immrot(1);
+
+ o5 = osrr(p->from.reg, REGTMP,r,p->scond);
+ o5 |= (1<<22);
+
+ o6 = oprrr(ASRL, p->scond);
+ o6 |= (24<<7)|(p->from.reg)|(p->from.reg<<12);
+ o6 |= (1<<6); /* ROL 8 */
+
+ break;
+
+ case 34: /* mov $lacon,R */
+ o1 = omvl(p, &p->from, REGTMP);
+ if(!o1)
+ break;
+
+ o2 = oprrr(AADD, p->scond);
+ o2 |= REGTMP;
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o2 |= r << 16;
+ if(p->to.type != D_NONE)
+ o2 |= p->to.reg << 12;
+ break;
+
+ case 35: /* mov PSR,R */
+ o1 = (2<<23) | (0xf<<16) | (0<<0);
+ o1 |= (p->scond & C_SCOND) << 28;
+ o1 |= (p->from.reg & 1) << 22;
+ o1 |= p->to.reg << 12;
+ break;
+
+ case 36: /* mov R,PSR */
+ o1 = (2<<23) | (0x29f<<12) | (0<<4);
+ if(p->scond & C_FBIT)
+ o1 ^= 0x010 << 12;
+ o1 |= (p->scond & C_SCOND) << 28;
+ o1 |= (p->to.reg & 1) << 22;
+ o1 |= p->from.reg << 0;
+ break;
+
+ case 37: /* mov $con,PSR */
+ aclass(&p->from);
+ o1 = (2<<23) | (0x29f<<12) | (0<<4);
+ if(p->scond & C_FBIT)
+ o1 ^= 0x010 << 12;
+ o1 |= (p->scond & C_SCOND) << 28;
+ o1 |= immrot(instoffset);
+ o1 |= (p->to.reg & 1) << 22;
+ o1 |= p->from.reg << 0;
+ break;
+
+ case 38: /* movm $con,oreg -> stm */
+ o1 = (0x4 << 25);
+ o1 |= p->from.offset & 0xffff;
+ o1 |= p->to.reg << 16;
+ aclass(&p->to);
+ goto movm;
+
+ case 39: /* movm oreg,$con -> ldm */
+ o1 = (0x4 << 25) | (1 << 20);
+ o1 |= p->to.offset & 0xffff;
+ o1 |= p->from.reg << 16;
+ aclass(&p->from);
+ movm:
+ if(instoffset != 0)
+ diag("offset must be zero in MOVM");
+ o1 |= (p->scond & C_SCOND) << 28;
+ if(p->scond & C_PBIT)
+ o1 |= 1 << 24;
+ if(p->scond & C_UBIT)
+ o1 |= 1 << 23;
+ if(p->scond & C_SBIT)
+ o1 |= 1 << 22;
+ if(p->scond & C_WBIT)
+ o1 |= 1 << 21;
+ break;
+
+ case 40: /* swp oreg,reg,reg */
+ aclass(&p->from);
+ if(instoffset != 0)
+ diag("offset must be zero in SWP");
+ o1 = (0x2<<23) | (0x9<<4);
+ if(p->as != ASWPW)
+ o1 |= 1 << 22;
+ o1 |= p->from.reg << 16;
+ o1 |= p->reg << 0;
+ o1 |= p->to.reg << 12;
+ o1 |= (p->scond & C_SCOND) << 28;
+ break;
+
+ case 41: /* rfe -> movm.s.w.u 0(r13),[r15] */
+ o1 = 0xe8fd8000;
+ break;
+
+ case 50: /* floating point store */
+ v = regoff(&p->to);
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = ofsr(p->as, p->from.reg, v, r, p->scond, p);
+ break;
+
+ case 51: /* floating point load */
+ v = regoff(&p->from);
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = ofsr(p->as, p->to.reg, v, r, p->scond, p) | (1<<20);
+ break;
+
+ case 52: /* floating point store, long offset UGLY */
+ o1 = omvl(p, &p->to, REGTMP);
+ if(!o1)
+ break;
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r;
+ o3 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p);
+ break;
+
+ case 53: /* floating point load, long offset UGLY */
+ o1 = omvl(p, &p->from, REGTMP);
+ if(!o1)
+ break;
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r;
+ o3 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20);
+ break;
+
+ case 54: /* floating point arith */
+ o1 = oprrr(p->as, p->scond);
+ if(p->from.type == D_FCONST) {
+ rf = chipfloat(p->from.ieee);
+ if(rf < 0){
+ diag("invalid floating-point immediate\n%P", p);
+ rf = 0;
+ }
+ rf |= (1<<3);
+ } else
+ rf = p->from.reg;
+ rt = p->to.reg;
+ r = p->reg;
+ if(p->to.type == D_NONE)
+ rt = 0; /* CMP[FD] */
+ else if(o1 & (1<<15))
+ r = 0; /* monadic */
+ else if(r == NREG)
+ r = rt;
+ o1 |= rf | (r<<16) | (rt<<12);
+ break;
+
+ case 55: /* floating point fix and float */
+ o1 = oprrr(p->as, p->scond);
+ rf = p->from.reg;
+ rt = p->to.reg;
+ if(p->to.type == D_NONE){
+ rt = 0;
+ diag("to.type==D_NONE (asm/fp)");
+ }
+ if(p->from.type == D_REG)
+ o1 |= (rf<<12) | (rt<<16);
+ else
+ o1 |= rf | (rt<<12);
+ break;
+
+ /* old arm 7500 fp using coproc 1 (1<<8) */
+ case 56: /* move to FP[CS]R */
+ o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4);
+ o1 |= ((p->to.reg+1)<<21) | (p->from.reg << 12);
+ break;
+
+ case 57: /* move from FP[CS]R */
+ o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4);
+ o1 |= ((p->from.reg+1)<<21) | (p->to.reg<<12) | (1<<20);
+ break;
+ case 58: /* movbu R,R */
+ o1 = oprrr(AAND, p->scond);
+ o1 |= immrot(0xff);
+ rt = p->to.reg;
+ r = p->from.reg;
+ if(p->to.type == D_NONE)
+ rt = 0;
+ if(r == NREG)
+ r = rt;
+ o1 |= (r<<16) | (rt<<12);
+ break;
+
+ case 59: /* movw/bu R<<I(R),R -> ldr indexed */
+ if(p->from.reg == NREG) {
+ if(p->as != AMOVW)
+ diag("byte MOV from shifter operand");
+ goto mov;
+ }
+ if(p->from.offset&(1<<4))
+ diag("bad shift in LDR");
+ o1 = olrr(p->from.offset, p->from.reg, p->to.reg, p->scond);
+ if(p->as == AMOVBU)
+ o1 |= 1<<22;
+ break;
+
+ case 60: /* movb R(R),R -> ldrsb indexed */
+ if(p->from.reg == NREG) {
+ diag("byte MOV from shifter operand");
+ goto mov;
+ }
+ if(p->from.offset&(~0xf))
+ diag("bad shift in LDRSB");
+ o1 = olhrr(p->from.offset, p->from.reg, p->to.reg, p->scond);
+ o1 ^= (1<<5)|(1<<6);
+ break;
+
+ case 61: /* movw/b/bu R,R<<[IR](R) -> str indexed */
+ if(p->to.reg == NREG)
+ diag("MOV to shifter operand");
+ o1 = osrr(p->from.reg, p->to.offset, p->to.reg, p->scond);
+ if(p->as == AMOVB || p->as == AMOVBU)
+ o1 |= 1<<22;
+ break;
+
+ case 62: /* case R -> movw R<<2(PC),PC */
+ o1 = olrr(p->from.reg, REGPC, REGPC, p->scond);
+ o1 |= 2<<7;
+ break;
+
+ case 63: /* bcase */
+ if(p->cond != P) {
+ o1 = p->cond->pc;
+ if(dlm)
+ dynreloc(S, p->pc, 1);
+ }
+ break;
+
+ /* reloc ops */
+ case 64: /* mov/movb/movbu R,addr */
+ o1 = omvl(p, &p->to, REGTMP);
+ if(!o1)
+ break;
+ o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+ break;
+
+ case 65: /* mov/movbu addr,R */
+ case 66: /* movh/movhu/movb addr,R */
+ o1 = omvl(p, &p->from, REGTMP);
+ if(!o1)
+ break;
+ o2 = olr(0, REGTMP, p->to.reg, p->scond);
+ if(p->as == AMOVBU || p->as == AMOVB)
+ o2 |= 1<<22;
+ if(o->type == 65)
+ break;
+
+ o3 = oprrr(ASLL, p->scond);
+
+ if(p->as == AMOVBU || p->as == AMOVHU)
+ o4 = oprrr(ASRL, p->scond);
+ else
+ o4 = oprrr(ASRA, p->scond);
+
+ r = p->to.reg;
+ o3 |= (r)|(r<<12);
+ o4 |= (r)|(r<<12);
+ if(p->as == AMOVB || p->as == AMOVBU) {
+ o3 |= (24<<7);
+ o4 |= (24<<7);
+ } else {
+ o3 |= (16<<7);
+ o4 |= (16<<7);
+ }
+ break;
+
+ case 67: /* movh/movhu R,addr -> sb, sb */
+ o1 = omvl(p, &p->to, REGTMP);
+ if(!o1)
+ break;
+ o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+
+ o3 = oprrr(ASRL, p->scond);
+ o3 |= (8<<7)|(p->from.reg)|(p->from.reg<<12);
+ o3 |= (1<<6); /* ROR 8 */
+
+ o4 = oprrr(AADD, p->scond);
+ o4 |= (REGTMP << 12) | (REGTMP << 16);
+ o4 |= immrot(1);
+
+ o5 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+
+ o6 = oprrr(ASRL, p->scond);
+ o6 |= (24<<7)|(p->from.reg)|(p->from.reg<<12);
+ o6 |= (1<<6); /* ROL 8 */
+ break;
+
+ case 68: /* floating point store -> ADDR */
+ o1 = omvl(p, &p->to, REGTMP);
+ if(!o1)
+ break;
+ o2 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p);
+ break;
+
+ case 69: /* floating point load <- ADDR */
+ o1 = omvl(p, &p->from, REGTMP);
+ if(!o1)
+ break;
+ o2 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20);
+ break;
+
+ /* ArmV4 ops: */
+ case 70: /* movh/movhu R,O(R) -> strh */
+ aclass(&p->to);
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = oshr(p->from.reg, instoffset, r, p->scond);
+ break;
+ case 71: /* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */
+ aclass(&p->from);
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o1 = olhr(instoffset, r, p->to.reg, p->scond);
+ if(p->as == AMOVB)
+ o1 ^= (1<<5)|(1<<6);
+ else if(p->as == AMOVH)
+ o1 ^= (1<<6);
+ break;
+ case 72: /* movh/movhu R,L(R) -> strh */
+ o1 = omvl(p, &p->to, REGTMP);
+ if(!o1)
+ break;
+ r = p->to.reg;
+ if(r == NREG)
+ r = o->param;
+ o2 = oshrr(p->from.reg, REGTMP,r, p->scond);
+ break;
+ case 73: /* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */
+ o1 = omvl(p, &p->from, REGTMP);
+ if(!o1)
+ break;
+ r = p->from.reg;
+ if(r == NREG)
+ r = o->param;
+ o2 = olhrr(REGTMP, r, p->to.reg, p->scond);
+ if(p->as == AMOVB)
+ o2 ^= (1<<5)|(1<<6);
+ else if(p->as == AMOVH)
+ o2 ^= (1<<6);
+ break;
+
+ /* VFP ops: */
+ case 74: /* vfp floating point arith */
+ o1 = opvfprrr(p->as, p->scond);
+ rf = p->from.reg;
+ if(p->from.type == D_FCONST) {
+ diag("invalid floating-point immediate\n%P", p);
+ rf = 0;
+ }
+ rt = p->to.reg;
+ r = p->reg;
+ if(r == NREG)
+ r = rt;
+ o1 |= rt<<12;
+ if(((o1>>20)&0xf) == 0xb)
+ o1 |= rf<<0;
+ else
+ o1 |= r<<16 | rf<<0;
+ break;
+ case 75: /* vfp floating point compare */
+ o1 = opvfprrr(p->as, p->scond);
+ rf = p->from.reg;
+ if(p->from.type == D_FCONST) {
+ if(p->from.ieee->h != 0 || p->from.ieee->l != 0)
+ diag("invalid floating-point immediate\n%P", p);
+ o1 |= 1<<16;
+ rf = 0;
+ }
+ rt = p->reg;
+ o1 |= rt<<12 | rf<<0;
+ o2 = 0x0ef1fa10; /* MRS APSR_nzcv, FPSCR */
+ o2 |= (p->scond & C_SCOND) << 28;
+ break;
+ case 76: /* vfp floating point fix and float */
+ o1 = opvfprrr(p->as, p->scond);
+ rf = p->from.reg;
+ rt = p->to.reg;
+ if(p->from.type == D_REG) {
+ o2 = o1 | rt<<12 | rt<<0;
+ o1 = 0x0e000a10; /* VMOV F,R */
+ o1 |= (p->scond & C_SCOND) << 28 | rt<<16 | rf<<12;
+ } else {
+ o1 |= FREGTMP<<12 | rf<<0;
+ o2 = 0x0e100a10; /* VMOV R,F */
+ o2 |= (p->scond & C_SCOND) << 28 | FREGTMP<<16 | rt<<12;
+ }
+ break;
+ }
+
+ if(debug['a'] > 1)
+ Bprint(&bso, "%2d ", o->type);
+
+ v = p->pc;
+ switch(o->size) {
+ default:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+ break;
+ case 4:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+ lputl(o1);
+ break;
+ case 8:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
+ lputl(o1);
+ lputl(o2);
+ break;
+ case 12:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
+ lputl(o1);
+ lputl(o2);
+ lputl(o3);
+ break;
+ case 16:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
+ v, o1, o2, o3, o4, p);
+ lputl(o1);
+ lputl(o2);
+ lputl(o3);
+ lputl(o4);
+ break;
+ case 20:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+ v, o1, o2, o3, o4, o5, p);
+ lputl(o1);
+ lputl(o2);
+ lputl(o3);
+ lputl(o4);
+ lputl(o5);
+ break;
+ case 24:
+ if(debug['a'])
+ Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+ v, o1, o2, o3, o4, o5, o6, p);
+ lputl(o1);
+ lputl(o2);
+ lputl(o3);
+ lputl(o4);
+ lputl(o5);
+ lputl(o6);
+ break;
+ }
+}
+
+long
+oprrr(int a, int sc)
+{
+ long o;
+
+ o = (sc & C_SCOND) << 28;
+ if(sc & C_SBIT)
+ o |= 1 << 20;
+ if(sc & (C_PBIT|C_WBIT))
+ diag(".P/.W on dp instruction");
+ switch(a) {
+ case AMULU:
+ case AMUL: return o | (0x0<<21) | (0x9<<4);
+ case AMULA: return o | (0x1<<21) | (0x9<<4);
+ case AMULLU: return o | (0x4<<21) | (0x9<<4);
+ case AMULL: return o | (0x6<<21) | (0x9<<4);
+ case AMULALU: return o | (0x5<<21) | (0x9<<4);
+ case AMULAL: return o | (0x7<<21) | (0x9<<4);
+ case AAND: return o | (0x0<<21);
+ case AEOR: return o | (0x1<<21);
+ case ASUB: return o | (0x2<<21);
+ case ARSB: return o | (0x3<<21);
+ case AADD: return o | (0x4<<21);
+ case AADC: return o | (0x5<<21);
+ case ASBC: return o | (0x6<<21);
+ case ARSC: return o | (0x7<<21);
+ case ATST: return o | (0x8<<21) | (1<<20);
+ case ATEQ: return o | (0x9<<21) | (1<<20);
+ case ACMP: return o | (0xa<<21) | (1<<20);
+ case ACMN: return o | (0xb<<21) | (1<<20);
+ case AORR: return o | (0xc<<21);
+ case AMOVW: return o | (0xd<<21);
+ case ABIC: return o | (0xe<<21);
+ case AMVN: return o | (0xf<<21);
+ case ASLL: return o | (0xd<<21) | (0<<5);
+ case ASRL: return o | (0xd<<21) | (1<<5);
+ case ASRA: return o | (0xd<<21) | (2<<5);
+ case ASWI: return o | (0xf<<24);
+
+ /* old arm 7500 fp using coproc 1 (1<<8) */
+ case AADDD: return o | (0xe<<24) | (0x0<<20) | (1<<8) | (1<<7);
+ case AADDF: return o | (0xe<<24) | (0x0<<20) | (1<<8);
+ case AMULD: return o | (0xe<<24) | (0x1<<20) | (1<<8) | (1<<7);
+ case AMULF: return o | (0xe<<24) | (0x1<<20) | (1<<8);
+ case ASUBD: return o | (0xe<<24) | (0x2<<20) | (1<<8) | (1<<7);
+ case ASUBF: return o | (0xe<<24) | (0x2<<20) | (1<<8);
+ case ADIVD: return o | (0xe<<24) | (0x4<<20) | (1<<8) | (1<<7);
+ case ADIVF: return o | (0xe<<24) | (0x4<<20) | (1<<8);
+ case ACMPD:
+ case ACMPF: return o | (0xe<<24) | (0x9<<20) | (0xF<<12) | (1<<8) | (1<<4); /* arguably, ACMPF should expand to RNDF, CMPD */
+
+ case AMOVF:
+ case AMOVDF: return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8);
+ case AMOVD:
+ case AMOVFD: return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8) | (1<<7);
+
+ case AMOVWF: return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4);
+ case AMOVWD: return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4) | (1<<7);
+ case AMOVFW: return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4);
+ case AMOVDW: return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4) | (1<<7);
+ }
+ diag("bad rrr %d", a);
+ prasm(curp);
+ return 0;
+}
+
+long
+opvfprrr(int a, int sc)
+{
+ long o;
+
+ o = (sc & C_SCOND) << 28;
+ if(sc & (C_SBIT|C_PBIT|C_WBIT))
+ diag(".S/.P/.W on vfp instruction");
+ o |= 0xe<<24;
+ switch(a) {
+ case AMOVWD: return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0x8<<16 | 1<<7;
+ case AMOVWF: return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0x8<<16 | 1<<7;
+ case AMOVDW: return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0xD<<16 | 1<<7;
+ case AMOVFW: return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0xD<<16 | 1<<7;
+ case AMOVFD: return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0x7<<16 | 1<<7;
+ case AMOVDF: return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0x7<<16 | 1<<7;
+ case AMOVF: return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0x0<<16 | 0<<7;
+ case AMOVD: return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0x0<<16 | 0<<7;
+ case ACMPF: return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0x4<<16 | 0<<7;
+ case ACMPD: return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0x4<<16 | 0<<7;
+ case AADDF: return o | 0xa<<8 | 0x3<<20;
+ case AADDD: return o | 0xb<<8 | 0x3<<20;
+ case ASUBF: return o | 0xa<<8 | 0x3<<20 | 1<<6;
+ case ASUBD: return o | 0xb<<8 | 0x3<<20 | 1<<6;
+ case AMULF: return o | 0xa<<8 | 0x2<<20;
+ case AMULD: return o | 0xb<<8 | 0x2<<20;
+ case ADIVF: return o | 0xa<<8 | 0x8<<20;
+ case ADIVD: return o | 0xb<<8 | 0x8<<20;
+ }
+ diag("bad vfp rrr %d", a);
+ prasm(curp);
+ return 0;
+}
+
+long
+opbra(int a, int sc)
+{
+
+ if(sc & (C_SBIT|C_PBIT|C_WBIT))
+ diag(".S/.P/.W on bra instruction");
+ sc &= C_SCOND;
+ if(a == ABL)
+ return (sc<<28)|(0x5<<25)|(0x1<<24);
+ if(sc != 0xe)
+ diag(".COND on bcond instruction");
+ switch(a) {
+ case ABEQ: return (0x0<<28)|(0x5<<25);
+ case ABNE: return (0x1<<28)|(0x5<<25);
+ case ABCS: return (0x2<<28)|(0x5<<25);
+ case ABHS: return (0x2<<28)|(0x5<<25);
+ case ABCC: return (0x3<<28)|(0x5<<25);
+ case ABLO: return (0x3<<28)|(0x5<<25);
+ case ABMI: return (0x4<<28)|(0x5<<25);
+ case ABPL: return (0x5<<28)|(0x5<<25);
+ case ABVS: return (0x6<<28)|(0x5<<25);
+ case ABVC: return (0x7<<28)|(0x5<<25);
+ case ABHI: return (0x8<<28)|(0x5<<25);
+ case ABLS: return (0x9<<28)|(0x5<<25);
+ case ABGE: return (0xa<<28)|(0x5<<25);
+ case ABLT: return (0xb<<28)|(0x5<<25);
+ case ABGT: return (0xc<<28)|(0x5<<25);
+ case ABLE: return (0xd<<28)|(0x5<<25);
+ case AB: return (0xe<<28)|(0x5<<25);
+ }
+ diag("bad bra %A", a);
+ prasm(curp);
+ return 0;
+}
+
+long
+olr(long v, int b, int r, int sc)
+{
+ long o;
+
+ if(sc & C_SBIT)
+ diag(".S on LDR/STR instruction");
+ o = (sc & C_SCOND) << 28;
+ if(!(sc & C_PBIT))
+ o |= 1 << 24;
+ if(!(sc & C_UBIT))
+ o |= 1 << 23;
+ if(sc & C_WBIT)
+ o |= 1 << 21;
+ o |= (0x1<<26) | (1<<20);
+ if(v < 0) {
+ v = -v;
+ o ^= 1 << 23;
+ }
+ if(v >= (1<<12))
+ diag("literal span too large: %ld (R%d)\n%P", v, b, PP);
+ o |= v;
+ o |= b << 16;
+ o |= r << 12;
+ return o;
+}
+
+long
+olhr(long v, int b, int r, int sc)
+{
+ long o;
+
+ if(sc & C_SBIT)
+ diag(".S on LDRH/STRH instruction");
+ o = (sc & C_SCOND) << 28;
+ if(!(sc & C_PBIT))
+ o |= 1 << 24;
+ if(sc & C_WBIT)
+ o |= 1 << 21;
+ o |= (1<<23) | (1<<20)|(0xb<<4);
+ if(v < 0) {
+ v = -v;
+ o ^= 1 << 23;
+ }
+ if(v >= (1<<8))
+ diag("literal span too large: %ld (R%d)\n%P", v, b, PP);
+ o |= (v&0xf)|((v>>4)<<8)|(1<<22);
+ o |= b << 16;
+ o |= r << 12;
+ return o;
+}
+
+long
+osr(int a, int r, long v, int b, int sc)
+{
+ long o;
+
+ o = olr(v, b, r, sc) ^ (1<<20);
+ if(a != AMOVW)
+ o |= 1<<22;
+ return o;
+}
+
+long
+oshr(int r, long v, int b, int sc)
+{
+ long o;
+
+ o = olhr(v, b, r, sc) ^ (1<<20);
+ return o;
+}
+
+
+long
+osrr(int r, int i, int b, int sc)
+{
+
+ return olr(i, b, r, sc) ^ ((1<<25) | (1<<20));
+}
+
+long
+oshrr(int r, int i, int b, int sc)
+{
+ return olhr(i, b, r, sc) ^ ((1<<22) | (1<<20));
+}
+
+long
+olrr(int i, int b, int r, int sc)
+{
+
+ return olr(i, b, r, sc) ^ (1<<25);
+}
+
+long
+olhrr(int i, int b, int r, int sc)
+{
+ return olhr(i, b, r, sc) ^ (1<<22);
+}
+
+long
+ovfpmem(int a, int r, long v, int b, int sc, Prog *p)
+{
+ long o;
+
+ if(sc & (C_SBIT|C_PBIT|C_WBIT))
+ diag(".S/.P/.W on VLDR/VSTR instruction");
+ o = (sc & C_SCOND) << 28;
+ o |= 0xd<<24 | (1<<23);
+ if(v < 0) {
+ v = -v;
+ o ^= 1 << 23;
+ }
+ if(v & 3)
+ diag("odd offset for floating point op: %ld\n%P", v, p);
+ else if(v >= (1<<10))
+ diag("literal span too large: %ld\n%P", v, p);
+ o |= (v>>2) & 0xFF;
+ o |= b << 16;
+ o |= r << 12;
+ switch(a) {
+ default:
+ diag("bad fst %A", a);
+ case AMOVD:
+ o |= 0xb<<8;
+ break;
+ case AMOVF:
+ o |= 0xa<<8;
+ break;
+ }
+ return o;
+}
+
+long
+ofsr(int a, int r, long v, int b, int sc, Prog *p)
+{
+ long o;
+
+ if(vfp)
+ return ovfpmem(a, r, v, b, sc, p);
+ if(sc & C_SBIT)
+ diag(".S on FLDR/FSTR instruction");
+ o = (sc & C_SCOND) << 28;
+ if(!(sc & C_PBIT))
+ o |= 1 << 24;
+ if(sc & C_WBIT)
+ o |= 1 << 21;
+ o |= (6<<25) | (1<<24) | (1<<23);
+ if(v < 0) {
+ v = -v;
+ o ^= 1 << 23;
+ }
+ if(v & 3)
+ diag("odd offset for floating point op: %ld\n%P", v, p);
+ else if(v >= (1<<10))
+ diag("literal span too large: %ld\n%P", v, p);
+ o |= (v>>2) & 0xFF;
+ o |= b << 16;
+ o |= r << 12;
+ o |= 1 << 8;
+
+ switch(a) {
+ default:
+ diag("bad fst %A", a);
+ case AMOVD:
+ o |= 1<<15;
+ case AMOVF:
+ break;
+ }
+ return o;
+}
+
+long
+omvl(Prog *p, Adr *a, int dr)
+{
+ long v, o1;
+ if(!p->cond) {
+ aclass(a);
+ v = immrot(~instoffset);
+ if(v == 0) {
+ diag("missing literal");
+ prasm(p);
+ return 0;
+ }
+ o1 = oprrr(AMVN, p->scond&C_SCOND);
+ o1 |= v;
+ o1 |= dr << 12;
+ } else {
+ v = p->cond->pc - p->pc - 8;
+ o1 = olr(v, REGPC, dr, p->scond&C_SCOND);
+ }
+ return o1;
+}
+
+static Ieee chipfloats[] = {
+ {0x00000000, 0x00000000}, /* 0 */
+ {0x00000000, 0x3ff00000}, /* 1 */
+ {0x00000000, 0x40000000}, /* 2 */
+ {0x00000000, 0x40080000}, /* 3 */
+ {0x00000000, 0x40100000}, /* 4 */
+ {0x00000000, 0x40140000}, /* 5 */
+ {0x00000000, 0x3fe00000}, /* .5 */
+ {0x00000000, 0x40240000}, /* 10 */
+};
+
+int
+chipfloat(Ieee *e)
+{
+ Ieee *p;
+ int n;
+
+ if(vfp)
+ return -1;
+ for(n = sizeof(chipfloats)/sizeof(chipfloats[0]); --n >= 0;){
+ p = &chipfloats[n];
+ if(p->l == e->l && p->h == e->h)
+ return n;
+ }
+ return -1;
+}
--- /dev/null
+++ b/utils/5l/l.h
@@ -1,0 +1,415 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../5c/5.out.h"
+#include "../ld/elf.h"
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#define LIBNAMELEN 300
+
+void addlibpath(char*);
+int fileexists(char*);
+char* findlib(char*);
+
+typedef struct Adr Adr;
+typedef struct Sym Sym;
+typedef struct Autom Auto;
+typedef struct Prog Prog;
+typedef struct Optab Optab;
+typedef struct Oprang Oprang;
+typedef uchar Opcross[32][2][32];
+typedef struct Count Count;
+
+#define P ((Prog*)0)
+#define S ((Sym*)0)
+#define TNAME (curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct Adr
+{
+ union
+ {
+ long u0offset;
+ char* u0sval;
+ Ieee* u0ieee;
+ } u0;
+ union
+ {
+ Auto* u1autom;
+ Sym* u1sym;
+ } u1;
+ char type;
+ char reg;
+ char name;
+ char class;
+};
+
+#define offset u0.u0offset
+#define sval u0.u0sval
+#define ieee u0.u0ieee
+
+#define autom u1.u1autom
+#define sym u1.u1sym
+
+struct Prog
+{
+ Adr from;
+ Adr to;
+ union
+ {
+ long u0regused;
+ Prog* u0forwd;
+ } u0;
+ Prog* cond;
+ Prog* link;
+ long pc;
+ long line;
+ uchar mark;
+ uchar optab;
+ uchar as;
+ uchar scond;
+ uchar reg;
+};
+#define regused u0.u0regused
+#define forwd u0.u0forwd
+
+struct Sym
+{
+ char *name;
+ short type;
+ short version;
+ short become;
+ short frame;
+ uchar subtype;
+ uchar used;
+ ushort file;
+ long value;
+ long sig;
+ Sym* link;
+};
+
+#define SIGNINTERN (1729*325*1729)
+
+struct Autom
+{
+ Sym* asym;
+ Auto* link;
+ long aoffset;
+ short type;
+};
+struct Optab
+{
+ char as;
+ char a1;
+ char a2;
+ char a3;
+ char type;
+ char size;
+ char param;
+ char flag;
+};
+struct Oprang
+{
+ Optab* start;
+ Optab* stop;
+};
+struct Count
+{
+ long count;
+ long outof;
+};
+
+enum
+{
+ STEXT = 1,
+ SDATA,
+ SBSS,
+ SDATA1,
+ SXREF,
+ SLEAF,
+ SFILE,
+ SCONST,
+ SSTRING,
+ SUNDEF,
+
+ SIMPORT,
+ SEXPORT,
+
+ LFROM = 1<<0,
+ LTO = 1<<1,
+ LPOOL = 1<<2,
+ V4 = 1<<3, /* arm v4 arch */
+ VFP = 1<<4, /* arm vfpv3 floating point */
+
+ C_NONE = 0,
+ C_REG,
+ C_REGREG,
+ C_SHIFT,
+ C_FREG,
+ C_PSR,
+ C_FCR,
+
+ C_RCON, /* 0xff rotated */
+ C_NCON, /* ~RCON */
+ C_SCON, /* 0xffff */
+ C_LCON,
+ C_FCON,
+
+ C_RACON,
+ C_LACON,
+
+ C_RECON,
+ C_LECON,
+
+ C_SBRA,
+ C_LBRA,
+
+ C_HAUTO, /* halfword insn offset (-0xff to 0xff) */
+ C_FAUTO, /* float insn offset (0 to 0x3fc, word aligned) */
+ C_HFAUTO, /* both H and F */
+ C_SAUTO, /* -0xfff to 0xfff */
+ C_LAUTO,
+
+ C_HEXT,
+ C_FEXT,
+ C_HFEXT,
+ C_SEXT,
+ C_LEXT,
+
+ C_HOREG,
+ C_FOREG,
+ C_HFOREG,
+ C_SOREG,
+ C_ROREG,
+ C_SROREG, /* both S and R */
+ C_LOREG,
+
+ C_ADDR, /* relocatable address */
+
+ C_GOK,
+
+/* mark flags */
+ FOLL = 1<<0,
+ LABEL = 1<<1,
+ LEAF = 1<<2,
+
+ BIG = (1<<12)-4,
+ STRINGSZ = 200,
+ NHASH = 10007,
+ NHUNK = 100000,
+ MINSIZ = 64,
+ NENT = 100,
+ MAXIO = 8192,
+ MAXHIST = 20, /* limit of path elements for history symbols */
+
+ Roffset = 22, /* no. bits for offset in relocation address */
+ Rindex = 10, /* no. bits for index in relocation address */
+};
+
+EXTERN union
+{
+ struct
+ {
+ uchar obuf[MAXIO]; /* output buffer */
+ uchar ibuf[MAXIO]; /* input buffer */
+ } u;
+ char dbuf[1];
+} buf;
+
+#define cbuf u.obuf
+#define xbuf u.ibuf
+
+#ifndef COFFCVT
+
+EXTERN long HEADR; /* length of header */
+EXTERN int HEADTYPE; /* type of header */
+EXTERN long INITDAT; /* data location */
+EXTERN long INITRND; /* data round above text location */
+EXTERN long INITTEXT; /* text location */
+EXTERN long INITTEXTP; /* text location (physical) */
+EXTERN char* INITENTRY; /* entry point */
+EXTERN long autosize;
+EXTERN Biobuf bso;
+EXTERN long bsssize;
+EXTERN int cbc;
+EXTERN uchar* cbp;
+EXTERN int cout;
+EXTERN Auto* curauto;
+EXTERN Auto* curhist;
+EXTERN Prog* curp;
+EXTERN Prog* curtext;
+EXTERN Prog* datap;
+EXTERN long datsize;
+EXTERN char debug[128];
+EXTERN Prog* etextp;
+EXTERN Prog* firstp;
+EXTERN char fnuxi4[4];
+EXTERN char fnuxi8[8];
+EXTERN char* noname;
+EXTERN Sym* hash[NHASH];
+EXTERN Sym* histfrog[MAXHIST];
+EXTERN int histfrogp;
+EXTERN int histgen;
+EXTERN char* library[50];
+EXTERN char* libraryobj[50];
+EXTERN int libraryp;
+EXTERN int xrefresolv;
+EXTERN char* hunk;
+EXTERN char inuxi1[1];
+EXTERN char inuxi2[2];
+EXTERN char inuxi4[4];
+EXTERN Prog* lastp;
+EXTERN long lcsize;
+EXTERN char literal[32];
+EXTERN int nerrors;
+EXTERN long nhunk;
+EXTERN long instoffset;
+EXTERN Opcross opcross[8];
+EXTERN Oprang oprange[ALAST];
+EXTERN char* outfile;
+EXTERN long pc;
+EXTERN uchar repop[ALAST];
+EXTERN long symsize;
+EXTERN Prog* textp;
+EXTERN long textsize;
+EXTERN long thunk;
+EXTERN int version;
+EXTERN char xcmp[C_GOK+1][C_GOK+1];
+EXTERN Prog zprg;
+EXTERN int dtype;
+EXTERN int armv4;
+EXTERN int vfp;
+
+EXTERN int doexp, dlm;
+EXTERN int imports, nimports;
+EXTERN int exports, nexports;
+EXTERN char* EXPTAB;
+EXTERN Prog undefp;
+
+#define UP (&undefp)
+
+extern char* anames[];
+extern Optab optab[];
+
+void addpool(Prog*, Adr*);
+EXTERN Prog* blitrl;
+EXTERN Prog* elitrl;
+
+void initdiv(void);
+EXTERN Prog* prog_div;
+EXTERN Prog* prog_divu;
+EXTERN Prog* prog_mod;
+EXTERN Prog* prog_modu;
+
+#pragma varargck type "A" int
+#pragma varargck type "A" uint
+#pragma varargck type "C" int
+#pragma varargck type "D" Adr*
+#pragma varargck type "N" Adr*
+#pragma varargck type "P" Prog*
+#pragma varargck type "S" char*
+
+#pragma varargck argpos diag 1
+
+int Aconv(Fmt*);
+int Cconv(Fmt*);
+int Dconv(Fmt*);
+int Nconv(Fmt*);
+int Pconv(Fmt*);
+int Sconv(Fmt*);
+int aclass(Adr*);
+void addhist(long, int);
+void addlibpath(char*);
+void append(Prog*, Prog*);
+void asmb(void);
+void asmdyn(void);
+void asmlc(void);
+void asmout(Prog*, Optab*);
+void asmsym(void);
+long atolwhex(char*);
+Prog* brloop(Prog*);
+void buildop(void);
+void buildrep(int, int);
+void cflush(void);
+void ckoff(Sym*, long);
+int chipfloat(Ieee*);
+int cmp(int, int);
+int compound(Prog*);
+double cputime(void);
+void datblk(long, long, int);
+void diag(char*, ...);
+void divsig(void);
+void dodata(void);
+void doprof1(void);
+void doprof2(void);
+void dynreloc(Sym*, long, int);
+long entryvalue(void);
+void errorexit(void);
+void exchange(Prog*);
+void export(void);
+int fileexists(char*);
+int find1(long, int);
+char* findlib(char*);
+void follow(void);
+void gethunk(void);
+void histtoauto(void);
+double ieeedtod(Ieee*);
+long ieeedtof(Ieee*);
+void import(void);
+int isnop(Prog*);
+void ldobj(int, long, char*);
+void loadlib(void);
+void listinit(void);
+Sym* lookup(char*, int);
+void cput(int);
+void llput(vlong);
+void llputl(vlong);
+void lput(long);
+void lputl(long);
+void mkfwd(void);
+void* mysbrk(ulong);
+void names(void);
+void nocache(Prog*);
+void nuxiinit(void);
+void objfile(char*);
+int ocmp(void*, void*);
+long opirr(int);
+Optab* oplook(Prog*);
+long oprrr(int, int);
+long opvfprrr(int, int);
+long olr(long, int, int, int);
+long olhr(long, int, int, int);
+long olrr(int, int, int, int);
+long olhrr(int, int, int, int);
+long osr(int, int, long, int, int);
+long oshr(int, long, int, int);
+long ofsr(int, int, long, int, int, Prog*);
+long osrr(int, int, int, int);
+long oshrr(int, int, int, int);
+long omvl(Prog*, Adr*, int);
+void patch(void);
+void prasm(Prog*);
+void prepend(Prog*, Prog*);
+Prog* prg(void);
+int pseudo(Prog*);
+void putsymb(char*, int, long, int);
+void readundefs(char*, int);
+long regoff(Adr*);
+int relinv(int);
+long rnd(long, long);
+void span(void);
+void strnput(char*, int);
+void undef(void);
+void undefsym(Sym*);
+void wput(long);
+void wputl(long);
+void xdefine(char*, int, long);
+void xfol(Prog*);
+void zerosig(char*);
+void noops(void);
+long immrot(ulong);
+long immaddr(long);
+long opbra(int, int);
+
+#endif
--- /dev/null
+++ b/utils/5l/list.c
@@ -1,0 +1,352 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+ fmtinstall('A', Aconv);
+ fmtinstall('C', Cconv);
+ fmtinstall('D', Dconv);
+ fmtinstall('P', Pconv);
+ fmtinstall('S', Sconv);
+ fmtinstall('N', Nconv);
+}
+
+void
+prasm(Prog *p)
+{
+ print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+ char str[STRINGSZ], *s;
+ Prog *p;
+ int a;
+
+ p = va_arg(fp->args, Prog*);
+ curp = p;
+ a = p->as;
+ switch(a) {
+ default:
+ s = str;
+ s += sprint(s, "(%ld)", p->line);
+ if(p->reg == NREG)
+ sprint(s, " %A%C %D,%D",
+ a, p->scond, &p->from, &p->to);
+ else
+ if(p->from.type != D_FREG)
+ sprint(s, " %A%C %D,R%d,%D",
+ a, p->scond, &p->from, p->reg, &p->to);
+ else
+ sprint(s, " %A%C %D,F%d,%D",
+ a, p->scond, &p->from, p->reg, &p->to);
+ break;
+
+ case ASWPW:
+ case ASWPBU:
+ sprint(str, "(%ld) %A%C R%d,%D,%D",
+ p->line, a, p->scond, p->reg, &p->from, &p->to);
+ break;
+
+ case ADATA:
+ case AINIT:
+ case ADYNT:
+ sprint(str, "(%ld) %A%C %D/%d,%D",
+ p->line, a, p->scond, &p->from, p->reg, &p->to);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+ char *s;
+ int a;
+
+ a = va_arg(fp->args, int);
+ s = "?";
+ if(a >= AXXX && a < ALAST)
+ s = anames[a];
+ return fmtstrcpy(fp, s);
+}
+
+char* strcond[16] =
+{
+ ".EQ",
+ ".NE",
+ ".HS",
+ ".LO",
+ ".MI",
+ ".PL",
+ ".VS",
+ ".VC",
+ ".HI",
+ ".LS",
+ ".GE",
+ ".LT",
+ ".GT",
+ ".LE",
+ "",
+ ".NV"
+};
+
+int
+Cconv(Fmt *fp)
+{
+ char s[20];
+ int c;
+
+ c = va_arg(fp->args, int);
+ strcpy(s, strcond[c & C_SCOND]);
+ if(c & C_SBIT)
+ strcat(s, ".S");
+ if(c & C_PBIT)
+ strcat(s, ".P");
+ if(c & C_WBIT)
+ strcat(s, ".W");
+ if(c & C_UBIT) /* ambiguous with FBIT */
+ strcat(s, ".U");
+ return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ char *op;
+ Adr *a;
+ long v;
+
+ a = va_arg(fp->args, Adr*);
+ switch(a->type) {
+
+ default:
+ sprint(str, "GOK-type(%d)", a->type);
+ break;
+
+ case D_NONE:
+ str[0] = 0;
+ if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+ sprint(str, "%N(R%d)(NONE)", a, a->reg);
+ break;
+
+ case D_CONST:
+ if(a->reg == NREG)
+ sprint(str, "$%N", a);
+ else
+ sprint(str, "$%N(R%d)", a, a->reg);
+ break;
+
+ case D_SHIFT:
+ v = a->offset;
+ op = "<<>>->@>" + (((v>>5) & 3) << 1);
+ if(v & (1<<4))
+ sprint(str, "R%ld%c%cR%ld", v&15, op[0], op[1], (v>>8)&15);
+ else
+ sprint(str, "R%ld%c%c%ld", v&15, op[0], op[1], (v>>7)&31);
+ if(a->reg != NREG)
+ sprint(str+strlen(str), "(R%d)", a->reg);
+ break;
+
+ case D_OCONST:
+ sprint(str, "$*$%N", a);
+ if(a->reg != NREG)
+ sprint(str, "%N(R%d)(CONST)", a, a->reg);
+ break;
+
+ case D_OREG:
+ if(a->reg != NREG)
+ sprint(str, "%N(R%d)", a, a->reg);
+ else
+ sprint(str, "%N", a);
+ break;
+
+ case D_REG:
+ sprint(str, "R%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_REGREG:
+ sprint(str, "(R%d,R%d)", a->reg, (int)a->offset);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_FREG:
+ sprint(str, "F%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_PSR:
+ switch(a->reg) {
+ case 0:
+ sprint(str, "CPSR");
+ break;
+ case 1:
+ sprint(str, "SPSR");
+ break;
+ default:
+ sprint(str, "PSR%d", a->reg);
+ break;
+ }
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(PSR%d)(REG)", a, a->reg);
+ break;
+
+ case D_FPCR:
+ switch(a->reg){
+ case 0:
+ sprint(str, "FPSR");
+ break;
+ case 1:
+ sprint(str, "FPCR");
+ break;
+ default:
+ sprint(str, "FCR%d", a->reg);
+ break;
+ }
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(FCR%d)(REG)", a, a->reg);
+
+ break;
+
+ case D_BRANCH: /* botch */
+ if(curp->cond != P) {
+ v = curp->cond->pc;
+ if(a->sym != S)
+ sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+ else
+ sprint(str, "%.5lux(BRANCH)", v);
+ } else
+ if(a->sym != S)
+ sprint(str, "%s+%ld(APC)", a->sym->name, a->offset);
+ else
+ sprint(str, "%ld(APC)", a->offset);
+ break;
+
+ case D_FCONST:
+ sprint(str, "$%e", ieeedtod(a->ieee));
+ break;
+
+ case D_SCONST:
+ sprint(str, "$\"%S\"", a->sval);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+ Sym *s;
+
+ a = va_arg(fp->args, Adr*);
+ s = a->sym;
+ switch(a->name) {
+ default:
+ sprint(str, "GOK-name(%d)", a->name);
+ break;
+
+ case D_NONE:
+ sprint(str, "%ld", a->offset);
+ break;
+
+ case D_EXTERN:
+ if(s == S)
+ sprint(str, "%ld(SB)", a->offset);
+ else
+ sprint(str, "%s+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_STATIC:
+ if(s == S)
+ sprint(str, "<>+%ld(SB)", a->offset);
+ else
+ sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_AUTO:
+ if(s == S)
+ sprint(str, "%ld(SP)", a->offset);
+ else
+ sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+ break;
+
+ case D_PARAM:
+ if(s == S)
+ sprint(str, "%ld(FP)", a->offset);
+ else
+ sprint(str, "%s+%ld(FP)", s->name, a->offset);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+ int i, c;
+ char str[STRINGSZ], *p, *a;
+
+ a = va_arg(fp->args, char*);
+ p = str;
+ for(i=0; i<sizeof(long); i++) {
+ c = a[i] & 0xff;
+ if(c >= 'a' && c <= 'z' ||
+ c >= 'A' && c <= 'Z' ||
+ c >= '0' && c <= '9' ||
+ c == ' ' || c == '%') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch(c) {
+ case 0:
+ *p++ = 'z';
+ continue;
+ case '\\':
+ case '"':
+ *p++ = c;
+ continue;
+ case '\n':
+ *p++ = 'n';
+ continue;
+ case '\t':
+ *p++ = 't';
+ continue;
+ }
+ *p++ = (c>>6) + '0';
+ *p++ = ((c>>3) & 7) + '0';
+ *p++ = (c & 7) + '0';
+ }
+ *p = 0;
+ return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+ char buf[STRINGSZ], *tn;
+ va_list arg;
+
+ tn = "??none??";
+ if(curtext != P && curtext->from.sym != S)
+ tn = curtext->from.sym->name;
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ print("%s: %s\n", tn, buf);
+
+ nerrors++;
+ if(nerrors > 10) {
+ print("too many errors\n");
+ errorexit();
+ }
+}
--- /dev/null
+++ b/utils/5l/mkfile
@@ -1,0 +1,37 @@
+<../../mkconfig
+
+TARG=5l
+
+OFILES=\
+ asm.$O\
+ list.$O\
+ noop.$O\
+ obj.$O\
+ optab.$O\
+ pass.$O\
+ span.$O\
+ enam.$O\
+ $TARGMODEL.$O\
+ elf.$O\
+
+HFILES=\
+ l.h\
+ ../5c/5.out.h\
+ ../include/ar.h\
+
+LIBS=bio 9 # order is important
+
+CFLAGS=$CFLAGS -I../include -I.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+enam.$O: ../5c/enam.c
+ $CC $CFLAGS ../5c/enam.c
+
+elf.$O: ../ld/elf.c
+ $CC $CFLAGS ../ld/elf.c
+
+$TARGMODEL.$O: ../ld/$TARGMODEL.c
+ $CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/5l/noop.c
@@ -1,0 +1,531 @@
+#include "l.h"
+
+static Sym* sym_div;
+static Sym* sym_divu;
+static Sym* sym_mod;
+static Sym* sym_modu;
+
+void
+noops(void)
+{
+ Prog *p, *q, *q1;
+ int o, curframe, curbecome, maxbecome;
+
+ /*
+ * find leaf subroutines
+ * become sizes
+ * frame sizes
+ * strip NOPs
+ * expand RET
+ * expand BECOME pseudo
+ */
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f noops\n", cputime());
+ Bflush(&bso);
+
+ curframe = 0;
+ curbecome = 0;
+ maxbecome = 0;
+ curtext = 0;
+
+ q = P;
+ for(p = firstp; p != P; p = p->link) {
+
+ /* find out how much arg space is used in this TEXT */
+ if(p->to.type == D_OREG && p->to.reg == REGSP)
+ if(p->to.offset > curframe)
+ curframe = p->to.offset;
+
+ switch(p->as) {
+ case ATEXT:
+ if(curtext && curtext->from.sym) {
+ curtext->from.sym->frame = curframe;
+ curtext->from.sym->become = curbecome;
+ if(curbecome > maxbecome)
+ maxbecome = curbecome;
+ }
+ curframe = 0;
+ curbecome = 0;
+
+ p->mark |= LEAF;
+ curtext = p;
+ break;
+
+ case ARET:
+ /* special form of RET is BECOME */
+ if(p->from.type == D_CONST)
+ if(p->from.offset > curbecome)
+ curbecome = p->from.offset;
+ break;
+
+ case ADIV:
+ case ADIVU:
+ case AMOD:
+ case AMODU:
+ q = p;
+ if(prog_div == P)
+ initdiv();
+ if(curtext != P)
+ curtext->mark &= ~LEAF;
+ continue;
+
+ case ANOP:
+ q1 = p->link;
+ q->link = q1; /* q is non-nop */
+ q1->mark |= p->mark;
+ continue;
+
+ case ABL:
+ if(curtext != P)
+ curtext->mark &= ~LEAF;
+
+ case ABCASE:
+ case AB:
+
+ case ABEQ:
+ case ABNE:
+ case ABCS:
+ case ABHS:
+ case ABCC:
+ case ABLO:
+ case ABMI:
+ case ABPL:
+ case ABVS:
+ case ABVC:
+ case ABHI:
+ case ABLS:
+ case ABGE:
+ case ABLT:
+ case ABGT:
+ case ABLE:
+
+ q1 = p->cond;
+ if(q1 != P) {
+ while(q1->as == ANOP) {
+ q1 = q1->link;
+ p->cond = q1;
+ }
+ }
+ break;
+ }
+ q = p;
+ }
+
+ if(curtext && curtext->from.sym) {
+ curtext->from.sym->frame = curframe;
+ curtext->from.sym->become = curbecome;
+ if(curbecome > maxbecome)
+ maxbecome = curbecome;
+ }
+
+ if(debug['b'])
+ print("max become = %d\n", maxbecome);
+ xdefine("ALEFbecome", STEXT, maxbecome);
+
+ curtext = 0;
+ for(p = firstp; p != P; p = p->link) {
+ switch(p->as) {
+ case ATEXT:
+ curtext = p;
+ break;
+ case ABL:
+ if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+ o = maxbecome - curtext->from.sym->frame;
+ if(o <= 0)
+ break;
+ /* calling a become or calling a variable */
+ if(p->to.sym == S || p->to.sym->become) {
+ curtext->to.offset += o;
+ if(debug['b']) {
+ curp = p;
+ print("%D calling %D increase %d\n",
+ &curtext->from, &p->to, o);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ for(p = firstp; p != P; p = p->link) {
+ o = p->as;
+ switch(o) {
+ case ATEXT:
+ curtext = p;
+ autosize = p->to.offset + 4;
+ if(autosize <= 4)
+ if(curtext->mark & LEAF) {
+ p->to.offset = -4;
+ autosize = 0;
+ }
+
+ if(!autosize && !(curtext->mark & LEAF)) {
+ if(debug['v'])
+ Bprint(&bso, "save suppressed in: %s\n",
+ curtext->from.sym->name);
+ Bflush(&bso);
+ curtext->mark |= LEAF;
+ }
+
+ if(curtext->mark & LEAF) {
+ if(curtext->from.sym)
+ curtext->from.sym->type = SLEAF;
+#ifdef optimise_time
+ if(autosize) {
+ q = prg();
+ q->as = ASUB;
+ q->line = p->line;
+ q->from.type = D_CONST;
+ q->from.offset = autosize;
+ q->to.type = D_REG;
+ q->to.reg = REGSP;
+
+ q->link = p->link;
+ p->link = q;
+ }
+ break;
+#else
+ if(!autosize)
+ break;
+#endif
+ }
+
+ q1 = prg();
+ q1->as = AMOVW;
+ q1->scond |= C_WBIT;
+ q1->line = p->line;
+ q1->from.type = D_REG;
+ q1->from.reg = REGLINK;
+ q1->to.type = D_OREG;
+ q1->to.offset = -autosize;
+ q1->to.reg = REGSP;
+
+ q1->link = p->link;
+ p->link = q1;
+ break;
+
+ case ARET:
+ nocache(p);
+ if(p->from.type == D_CONST)
+ goto become;
+ if(curtext->mark & LEAF) {
+ if(!autosize) {
+ p->as = AB;
+ p->from = zprg.from;
+ p->to.type = D_OREG;
+ p->to.offset = 0;
+ p->to.reg = REGLINK;
+ break;
+ }
+
+#ifdef optimise_time
+ p->as = AADD;
+ p->from.type = D_CONST;
+ p->from.offset = autosize;
+ p->to.type = D_REG;
+ p->to.reg = REGSP;
+
+ q = prg();
+ q->as = AB;
+ q->scond = p->scond;
+ q->line = p->line;
+ q->to.type = D_OREG;
+ q->to.offset = 0;
+ q->to.reg = REGLINK;
+
+ q->link = p->link;
+ p->link = q;
+
+ break;
+#endif
+ }
+ p->as = AMOVW;
+ p->scond |= C_PBIT;
+ p->from.type = D_OREG;
+ p->from.offset = autosize;
+ p->from.reg = REGSP;
+ p->to.type = D_REG;
+ p->to.reg = REGPC;
+ break;
+
+ become:
+ if(curtext->mark & LEAF) {
+
+ if(!autosize) {
+ p->as = AB;
+ p->from = zprg.from;
+ break;
+ }
+
+#ifdef optimise_time
+ q = prg();
+ q->scond = p->scond;
+ q->line = p->line;
+ q->as = AB;
+ q->from = zprg.from;
+ q->to = p->to;
+ q->cond = p->cond;
+ q->link = p->link;
+ p->link = q;
+
+ p->as = AADD;
+ p->from = zprg.from;
+ p->from.type = D_CONST;
+ p->from.offset = autosize;
+ p->to = zprg.to;
+ p->to.type = D_REG;
+ p->to.reg = REGSP;
+
+ break;
+#endif
+ }
+ q = prg();
+ q->scond = p->scond;
+ q->line = p->line;
+ q->as = AB;
+ q->from = zprg.from;
+ q->to = p->to;
+ q->cond = p->cond;
+ q->link = p->link;
+ p->link = q;
+
+ p->as = AMOVW;
+ p->scond |= C_PBIT;
+ p->from = zprg.from;
+ p->from.type = D_OREG;
+ p->from.offset = autosize;
+ p->from.reg = REGSP;
+ p->to = zprg.to;
+ p->to.type = D_REG;
+ p->to.reg = REGLINK;
+
+ break;
+
+ /*
+ * 5c code generation for unsigned -> double made the
+ * unfortunate assumption that single and double floating
+ * point registers are aliased - true for emulated 7500
+ * but not for vfp. Now corrected, but this test is
+ * insurance against old 5c compiled code in libraries.
+ */
+ case AMOVWD:
+ if((q = p->link) != P && q->as == ACMP)
+ if((q = q->link) != P && q->as == AMOVF)
+ if((q1 = q->link) != P && q1->as == AADDF)
+ if(q1->to.type == D_FREG && q1->to.reg == p->to.reg) {
+ q1->as = AADDD;
+ q1 = prg();
+ q1->scond = q->scond;
+ q1->line = q->line;
+ q1->as = AMOVFD;
+ q1->from = q->to;
+ q1->to = q1->from;
+ q1->link = q->link;
+ q->link = q1;
+ }
+ break;
+
+ case ADIV:
+ case ADIVU:
+ case AMOD:
+ case AMODU:
+ if(debug['M'])
+ break;
+ if(p->from.type != D_REG)
+ break;
+ if(p->to.type != D_REG)
+ break;
+ q1 = p;
+
+ /* MOV a,4(SP) */
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ p = q;
+
+ p->as = AMOVW;
+ p->line = q1->line;
+ p->from.type = D_REG;
+ p->from.reg = q1->from.reg;
+ p->to.type = D_OREG;
+ p->to.reg = REGSP;
+ p->to.offset = 4;
+
+ /* MOV b,REGTMP */
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ p = q;
+
+ p->as = AMOVW;
+ p->line = q1->line;
+ p->from.type = D_REG;
+ p->from.reg = q1->reg;
+ if(q1->reg == NREG)
+ p->from.reg = q1->to.reg;
+ p->to.type = D_REG;
+ p->to.reg = REGTMP;
+ p->to.offset = 0;
+
+ /* CALL appropriate */
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ p = q;
+
+ p->as = ABL;
+ p->line = q1->line;
+ p->to.type = D_BRANCH;
+ p->cond = p;
+ switch(o) {
+ case ADIV:
+ p->cond = prog_div;
+ p->to.sym = sym_div;
+ break;
+ case ADIVU:
+ p->cond = prog_divu;
+ p->to.sym = sym_divu;
+ break;
+ case AMOD:
+ p->cond = prog_mod;
+ p->to.sym = sym_mod;
+ break;
+ case AMODU:
+ p->cond = prog_modu;
+ p->to.sym = sym_modu;
+ break;
+ }
+
+ /* MOV REGTMP, b */
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ p = q;
+
+ p->as = AMOVW;
+ p->line = q1->line;
+ p->from.type = D_REG;
+ p->from.reg = REGTMP;
+ p->from.offset = 0;
+ p->to.type = D_REG;
+ p->to.reg = q1->to.reg;
+
+ /* ADD $8,SP */
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ p = q;
+
+ p->as = AADD;
+ p->from.type = D_CONST;
+ p->from.reg = NREG;
+ p->from.offset = 8;
+ p->reg = NREG;
+ p->to.type = D_REG;
+ p->to.reg = REGSP;
+
+ /* SUB $8,SP */
+ q1->as = ASUB;
+ q1->from.type = D_CONST;
+ q1->from.offset = 8;
+ q1->from.reg = NREG;
+ q1->reg = NREG;
+ q1->to.type = D_REG;
+ q1->to.reg = REGSP;
+ break;
+ }
+ }
+}
+
+static void
+sigdiv(char *n)
+{
+ Sym *s;
+
+ s = lookup(n, 0);
+ if(s->type == STEXT){
+ if(s->sig == 0)
+ s->sig = SIGNINTERN;
+ }
+ else if(s->type == 0 || s->type == SXREF)
+ s->type = SUNDEF;
+}
+
+void
+divsig(void)
+{
+ sigdiv("_div");
+ sigdiv("_divu");
+ sigdiv("_mod");
+ sigdiv("_modu");
+}
+
+static void
+sdiv(Sym *s)
+{
+ if(s->type == 0 || s->type == SXREF){
+ /* undefsym(s); */
+ s->type = SXREF;
+ if(s->sig == 0)
+ s->sig = SIGNINTERN;
+ s->subtype = SIMPORT;
+ }
+ else if(s->type != STEXT)
+ diag("undefined: %s", s->name);
+}
+
+void
+initdiv(void)
+{
+ Sym *s2, *s3, *s4, *s5;
+ Prog *p;
+
+ if(prog_div != P)
+ return;
+ sym_div = s2 = lookup("_div", 0);
+ sym_divu = s3 = lookup("_divu", 0);
+ sym_mod = s4 = lookup("_mod", 0);
+ sym_modu = s5 = lookup("_modu", 0);
+ if(dlm) {
+ sdiv(s2); if(s2->type == SXREF) prog_div = UP;
+ sdiv(s3); if(s3->type == SXREF) prog_divu = UP;
+ sdiv(s4); if(s4->type == SXREF) prog_mod = UP;
+ sdiv(s5); if(s5->type == SXREF) prog_modu = UP;
+ }
+ for(p = firstp; p != P; p = p->link)
+ if(p->as == ATEXT) {
+ if(p->from.sym == s2)
+ prog_div = p;
+ if(p->from.sym == s3)
+ prog_divu = p;
+ if(p->from.sym == s4)
+ prog_mod = p;
+ if(p->from.sym == s5)
+ prog_modu = p;
+ }
+ if(prog_div == P) {
+ diag("undefined: %s", s2->name);
+ prog_div = curtext;
+ }
+ if(prog_divu == P) {
+ diag("undefined: %s", s3->name);
+ prog_divu = curtext;
+ }
+ if(prog_mod == P) {
+ diag("undefined: %s", s4->name);
+ prog_mod = curtext;
+ }
+ if(prog_modu == P) {
+ diag("undefined: %s", s5->name);
+ prog_modu = curtext;
+ }
+}
+
+void
+nocache(Prog *p)
+{
+ p->optab = 0;
+ p->from.class = 0;
+ p->to.class = 0;
+}
--- /dev/null
+++ b/utils/5l/obj.c
@@ -1,0 +1,1641 @@
+#define EXTERN
+#include "l.h"
+#include <ar.h>
+
+#ifndef DEFAULT
+#define DEFAULT '9'
+#endif
+
+char *noname = "<none>";
+char symname[] = SYMDEF;
+char thechar = '5';
+char *thestring = "arm";
+
+char** libdir;
+int nlibdir = 0;
+static int maxlibdir = 0;
+
+/*
+ * -H0 no header
+ * -H1 -T0x10005000 -R4 is aif for risc os
+ * -H2 -T4128 -R4096 is plan9 format
+ * -H3 -T0xF0000020 -R4 is NetBSD format
+ * -H4 is IXP1200 (raw)
+ * -H5 -T0xC0008010 -R1024 is ipaq
+ * -H6 -R4096 no header with segments padded to pages
+ * -H7 is elf
+ */
+
+void
+usage(void)
+{
+ diag("usage: %s [-options] objects", argv0);
+ errorexit();
+}
+
+static int
+isobjfile(char *f)
+{
+ int n, v;
+ Biobuf *b;
+ char buf1[5], buf2[SARMAG];
+
+ b = Bopen(f, OREAD);
+ if(b == nil)
+ return 0;
+ n = Bread(b, buf1, 5);
+ if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<'))
+ v = 1; /* good enough for our purposes */
+ else{
+ Bseek(b, 0, 0);
+ n = Bread(b, buf2, SARMAG);
+ v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0;
+ }
+ Bterm(b);
+ return v;
+}
+
+void
+main(int argc, char *argv[])
+{
+ int c;
+ char *a;
+ char name[LIBNAMELEN];
+
+ Binit(&bso, 1, OWRITE);
+ cout = -1;
+ listinit();
+ outfile = 0;
+ nerrors = 0;
+ curtext = P;
+ HEADTYPE = -1;
+ INITTEXT = -1;
+ INITTEXTP = -1;
+ INITDAT = -1;
+ INITRND = -1;
+ INITENTRY = 0;
+
+ ARGBEGIN {
+ default:
+ c = ARGC();
+ if(c >= 0 && c < sizeof(debug))
+ debug[c]++;
+ break;
+ case 'o':
+ outfile = ARGF();
+ break;
+ case 'E':
+ a = ARGF();
+ if(a)
+ INITENTRY = a;
+ break;
+ case 'L':
+ addlibpath(EARGF(usage()));
+ break;
+ case 'T':
+ a = ARGF();
+ if(a)
+ INITTEXT = atolwhex(a);
+ break;
+ case 'P':
+ a = ARGF();
+ if(a)
+ INITTEXTP = atolwhex(a);
+ break;
+ case 'D':
+ a = ARGF();
+ if(a)
+ INITDAT = atolwhex(a);
+ break;
+ case 'R':
+ a = ARGF();
+ if(a)
+ INITRND = atolwhex(a);
+ break;
+ case 'H':
+ a = ARGF();
+ if(a)
+ HEADTYPE = atolwhex(a);
+ /* do something about setting INITTEXT */
+ break;
+ case 'x': /* produce export table */
+ doexp = 1;
+ if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+ readundefs(ARGF(), SEXPORT);
+ break;
+ case 'u': /* produce dynamically loadable module */
+ dlm = 1;
+ if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+ readundefs(ARGF(), SIMPORT);
+ break;
+ } ARGEND
+
+ USED(argc);
+
+ if(*argv == 0)
+ usage();
+ if(!debug['9'] && !debug['U'] && !debug['B'])
+ debug[DEFAULT] = 1;
+ a = getenv("ccroot");
+ if(a != nil && *a != '\0') {
+ if(!fileexists(a)) {
+ diag("nonexistent $ccroot: %s", a);
+ errorexit();
+ }
+ }else
+ a = "";
+ snprint(name, sizeof(name), "%s/%s/lib", a, thestring);
+ addlibpath(name);
+ if(HEADTYPE == -1) {
+ if(debug['U'])
+ HEADTYPE = 0;
+ if(debug['B'])
+ HEADTYPE = 1;
+ if(debug['9'])
+ HEADTYPE = 2;
+ }
+ switch(HEADTYPE) {
+ default:
+ diag("unknown -H option");
+ errorexit();
+ case 0: /* no header */
+ case 6: /* no header, padded segments */
+ HEADR = 0L;
+ if(INITTEXT == -1)
+ INITTEXT = 0;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4;
+ break;
+ case 1: /* aif for risc os */
+ HEADR = 128L;
+ if(INITTEXT == -1)
+ INITTEXT = 0x10005000 + HEADR;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4;
+ break;
+ case 2: /* plan 9 */
+ HEADR = 32L;
+ if(INITTEXT == -1)
+ INITTEXT = 4128;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4096;
+ break;
+ case 3: /* boot for NetBSD */
+ HEADR = 32L;
+ if(INITTEXT == -1)
+ INITTEXT = 0xF0000020L;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4096;
+ break;
+ case 4: /* boot for IXP1200 */
+ HEADR = 0L;
+ if(INITTEXT == -1)
+ INITTEXT = 0x0;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4;
+ break;
+ case 5: /* boot for ipaq */
+ HEADR = 16L;
+ if(INITTEXT == -1)
+ INITTEXT = 0xC0008010;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 1024;
+ break;
+ case 7: /* elf executable */
+ HEADR = rnd(Ehdr32sz+3*Phdr32sz, 16);
+ if(INITTEXT == -1)
+ INITTEXT = 4096+HEADR;
+ if(INITDAT == -1)
+ INITDAT = 0;
+ if(INITRND == -1)
+ INITRND = 4;
+ break;
+ }
+ if (INITTEXTP == -1)
+ INITTEXTP = INITTEXT;
+ if(INITDAT != 0 && INITRND != 0)
+ print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+ INITDAT, INITRND);
+ if(debug['v'])
+ Bprint(&bso, "HEADER = -H0x%d -T0x%lux -D0x%lux -R0x%lux\n",
+ HEADTYPE, INITTEXT, INITDAT, INITRND);
+ Bflush(&bso);
+ zprg.as = AGOK;
+ zprg.scond = 14;
+ zprg.reg = NREG;
+ zprg.from.name = D_NONE;
+ zprg.from.type = D_NONE;
+ zprg.from.reg = NREG;
+ zprg.to = zprg.from;
+ buildop();
+ histgen = 0;
+ textp = P;
+ datap = P;
+ pc = 0;
+ dtype = 4;
+ if(outfile == 0)
+ outfile = "5.out";
+ cout = create(outfile, 1, 0775);
+ if(cout < 0) {
+ diag("cannot create %s: %r", outfile);
+ errorexit();
+ }
+ nuxiinit();
+
+ version = 0;
+ cbp = buf.cbuf;
+ cbc = sizeof(buf.cbuf);
+ firstp = prg();
+ lastp = firstp;
+
+ if(INITENTRY == 0) {
+ INITENTRY = "_main";
+ if(debug['p'])
+ INITENTRY = "_mainp";
+ if(!debug['l'])
+ lookup(INITENTRY, 0)->type = SXREF;
+ } else if(!(*INITENTRY >= '0' && *INITENTRY <= '9'))
+ lookup(INITENTRY, 0)->type = SXREF;
+
+ while(*argv)
+ objfile(*argv++);
+ if(!debug['l'])
+ loadlib();
+ firstp = firstp->link;
+ if(firstp == P)
+ goto out;
+ if(doexp || dlm){
+ EXPTAB = "_exporttab";
+ zerosig(EXPTAB);
+ zerosig("etext");
+ zerosig("edata");
+ zerosig("end");
+ if(dlm){
+ initdiv();
+ import();
+ HEADTYPE = 2;
+ INITTEXT = INITDAT = 0;
+ INITRND = 8;
+ INITENTRY = EXPTAB;
+ }
+ else
+ divsig();
+ export();
+ }
+ patch();
+ if(debug['p'])
+ if(debug['1'])
+ doprof1();
+ else
+ doprof2();
+ dodata();
+ follow();
+ if(firstp == P)
+ goto out;
+ noops();
+ span();
+ asmb();
+ undef();
+
+out:
+ if(debug['v']) {
+ Bprint(&bso, "%5.2f cpu time\n", cputime());
+ Bprint(&bso, "%ld memory used\n", thunk);
+ Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+ Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+ }
+ Bflush(&bso);
+ errorexit();
+}
+
+void
+addlibpath(char *arg)
+{
+ char **p;
+
+ if(nlibdir >= maxlibdir) {
+ if(maxlibdir == 0)
+ maxlibdir = 8;
+ else
+ maxlibdir *= 2;
+ p = malloc(maxlibdir*sizeof(*p));
+ if(p == nil) {
+ diag("out of memory");
+ errorexit();
+ }
+ memmove(p, libdir, nlibdir*sizeof(*p));
+ free(libdir);
+ libdir = p;
+ }
+ libdir[nlibdir++] = strdup(arg);
+}
+
+char*
+findlib(char *file)
+{
+ int i;
+ char name[LIBNAMELEN];
+
+ for(i = 0; i < nlibdir; i++) {
+ snprint(name, sizeof(name), "%s/%s", libdir[i], file);
+ if(fileexists(name))
+ return libdir[i];
+ }
+ return nil;
+}
+
+void
+loadlib(void)
+{
+ int i;
+ long h;
+ Sym *s;
+
+loop:
+ xrefresolv = 0;
+ for(i=0; i<libraryp; i++) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f autolib: %s\n", cputime(), library[i]);
+ objfile(library[i]);
+ }
+ if(xrefresolv)
+ for(h=0; h<nelem(hash); h++)
+ for(s = hash[h]; s != S; s = s->link)
+ if(s->type == SXREF)
+ goto loop;
+}
+
+void
+errorexit(void)
+{
+
+ if(nerrors) {
+ if(cout >= 0)
+ remove(outfile);
+ exits("error");
+ }
+ exits(0);
+}
+
+void
+objfile(char *file)
+{
+ long off, esym, cnt, l;
+ int f, work;
+ Sym *s;
+ char magbuf[SARMAG];
+ char name[LIBNAMELEN], pname[LIBNAMELEN];
+ struct ar_hdr arhdr;
+ char *e, *start, *stop;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+ Bflush(&bso);
+ if(file[0] == '-' && file[1] == 'l') {
+ snprint(pname, sizeof(pname), "lib%s.a", file+2);
+ e = findlib(pname);
+ if(e == nil) {
+ diag("cannot find library: %s", file);
+ errorexit();
+ }
+ snprint(name, sizeof(name), "%s/%s", e, pname);
+ file = name;
+ }
+ f = open(file, 0);
+ if(f < 0) {
+ diag("cannot open %s: %r", file);
+ errorexit();
+ }
+ l = read(f, magbuf, SARMAG);
+ if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+ /* load it as a regular file */
+ l = seek(f, 0L, 2);
+ seek(f, 0L, 0);
+ ldobj(f, l, file);
+ close(f);
+ return;
+ }
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f ldlib: %s\n", cputime(), file);
+ l = read(f, &arhdr, SAR_HDR);
+ if(l != SAR_HDR) {
+ diag("%s: short read on archive file symbol header", file);
+ goto out;
+ }
+ if(strncmp(arhdr.name, symname, strlen(symname))) {
+ diag("%s: first entry not symbol header", file);
+ goto out;
+ }
+
+ esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+ off = SARMAG + SAR_HDR;
+
+ /*
+ * just bang the whole symbol file into memory
+ */
+ seek(f, off, 0);
+ cnt = esym - off;
+ start = malloc(cnt + 10);
+ cnt = read(f, start, cnt);
+ if(cnt <= 0){
+ close(f);
+ return;
+ }
+ stop = &start[cnt];
+ memset(stop, 0, 10);
+
+ work = 1;
+ while(work){
+ if(debug['v'])
+ Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+ Bflush(&bso);
+ work = 0;
+ for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+ s = lookup(e+5, 0);
+ if(s->type != SXREF)
+ continue;
+ sprint(pname, "%s(%s)", file, s->name);
+ if(debug['v'])
+ Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+ Bflush(&bso);
+ l = e[1] & 0xff;
+ l |= (e[2] & 0xff) << 8;
+ l |= (e[3] & 0xff) << 16;
+ l |= (e[4] & 0xff) << 24;
+ seek(f, l, 0);
+ /* need readn to read the dumps (at least) */
+ l = readn(f, &arhdr, SAR_HDR);
+ if(l != SAR_HDR)
+ goto bad;
+ if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+ goto bad;
+ l = atolwhex(arhdr.size);
+ ldobj(f, l, pname);
+ if(s->type == SXREF) {
+ diag("%s: failed to load: %s", file, s->name);
+ errorexit();
+ }
+ work = 1;
+ xrefresolv = 1;
+ }
+ }
+ return;
+
+bad:
+ diag("%s: bad or out of date archive", file);
+out:
+ close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+ int i, c;
+ int l;
+ Sym *s;
+ Auto *u;
+
+ c = p[2];
+ if(c < 0 || c > NSYM){
+ print("sym out of range: %d\n", c);
+ p[0] = ALAST+1;
+ return 0;
+ }
+ a->type = p[0];
+ a->reg = p[1];
+ a->sym = h[c];
+ a->name = p[3];
+ c = 4;
+
+ if(a->reg < 0 || a->reg > NREG) {
+ print("register out of range %d\n", a->reg);
+ p[0] = ALAST+1;
+ return 0; /* force real diagnostic */
+ }
+
+ switch(a->type) {
+ default:
+ print("unknown type %d\n", a->type);
+ p[0] = ALAST+1;
+ return 0; /* force real diagnostic */
+
+ case D_NONE:
+ case D_REG:
+ case D_FREG:
+ case D_PSR:
+ case D_FPCR:
+ break;
+
+ case D_REGREG:
+ a->offset = p[4];
+ c++;
+ break;
+
+ case D_BRANCH:
+ case D_OREG:
+ case D_CONST:
+ case D_OCONST:
+ case D_SHIFT:
+ a->offset = p[4] | (p[5]<<8) |
+ (p[6]<<16) | (p[7]<<24);
+ c += 4;
+ break;
+
+ case D_SCONST:
+ while(nhunk < NSNAME)
+ gethunk();
+ a->sval = (char*)hunk;
+ nhunk -= NSNAME;
+ hunk += NSNAME;
+
+ memmove(a->sval, p+4, NSNAME);
+ c += NSNAME;
+ break;
+
+ case D_FCONST:
+ while(nhunk < sizeof(Ieee))
+ gethunk();
+ a->ieee = (Ieee*)hunk;
+ nhunk -= NSNAME;
+ hunk += NSNAME;
+
+ a->ieee->l = p[4] | (p[5]<<8) |
+ (p[6]<<16) | (p[7]<<24);
+ a->ieee->h = p[8] | (p[9]<<8) |
+ (p[10]<<16) | (p[11]<<24);
+ c += 8;
+ break;
+ }
+ s = a->sym;
+ if(s == S)
+ return c;
+ i = a->name;
+ if(i != D_AUTO && i != D_PARAM)
+ return c;
+
+ l = a->offset;
+ for(u=curauto; u; u=u->link)
+ if(u->asym == s)
+ if(u->type == i) {
+ if(u->aoffset > l)
+ u->aoffset = l;
+ return c;
+ }
+
+ while(nhunk < sizeof(Auto))
+ gethunk();
+ u = (Auto*)hunk;
+ nhunk -= sizeof(Auto);
+ hunk += sizeof(Auto);
+
+ u->link = curauto;
+ curauto = u;
+ u->asym = s;
+ u->aoffset = l;
+ u->type = i;
+ return c;
+}
+
+void
+addlib(char *obj)
+{
+ char fn1[LIBNAMELEN], fn2[LIBNAMELEN], comp[LIBNAMELEN], *p, *name;
+ int i, search;
+
+ if(histfrogp <= 0)
+ return;
+
+ name = fn1;
+ search = 0;
+ if(histfrog[0]->name[1] == '/') {
+ sprint(name, "");
+ i = 1;
+ } else if(histfrog[0]->name[1] == '.') {
+ sprint(name, ".");
+ i = 0;
+ } else {
+ sprint(name, "");
+ i = 0;
+ search = 1;
+ }
+
+ for(; i<histfrogp; i++) {
+ snprint(comp, sizeof comp, histfrog[i]->name+1);
+ for(;;) {
+ p = strstr(comp, "$O");
+ if(p == 0)
+ break;
+ memmove(p+1, p+2, strlen(p+2)+1);
+ p[0] = thechar;
+ }
+ for(;;) {
+ p = strstr(comp, "$M");
+ if(p == 0)
+ break;
+ if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+ diag("library component too long");
+ return;
+ }
+ memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+ memmove(p, thestring, strlen(thestring));
+ }
+ if(strlen(fn1) + strlen(comp) + 3 >= sizeof(fn1)) {
+ diag("library component too long");
+ return;
+ }
+ if(i > 0 || !search)
+ strcat(fn1, "/");
+ strcat(fn1, comp);
+ }
+
+ cleanname(name);
+
+ if(search){
+ p = findlib(name);
+ if(p != nil){
+ snprint(fn2, sizeof(fn2), "%s/%s", p, name);
+ name = fn2;
+ }
+ }
+
+ for(i=0; i<libraryp; i++)
+ if(strcmp(name, library[i]) == 0)
+ return;
+ if(libraryp == nelem(library)){
+ diag("too many autolibs; skipping %s", name);
+ return;
+ }
+
+ p = malloc(strlen(name) + 1);
+ strcpy(p, name);
+ library[libraryp] = p;
+ p = malloc(strlen(obj) + 1);
+ strcpy(p, obj);
+ libraryobj[libraryp] = p;
+ libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+ Auto *u;
+ Sym *s;
+ int i, j, k;
+
+ u = malloc(sizeof(Auto));
+ s = malloc(sizeof(Sym));
+ s->name = malloc(2*(histfrogp+1) + 1);
+
+ u->asym = s;
+ u->type = type;
+ u->aoffset = line;
+ u->link = curhist;
+ curhist = u;
+
+ j = 1;
+ for(i=0; i<histfrogp; i++) {
+ k = histfrog[i]->value;
+ s->name[j+0] = k>>8;
+ s->name[j+1] = k;
+ j += 2;
+ }
+}
+
+void
+histtoauto(void)
+{
+ Auto *l;
+
+ while(l = curhist) {
+ curhist = l->link;
+ l->link = curauto;
+ curauto = l;
+ }
+}
+
+void
+collapsefrog(Sym *s)
+{
+ int i;
+
+ /*
+ * bad encoding of path components only allows
+ * MAXHIST components. if there is an overflow,
+ * first try to collapse xxx/..
+ */
+ for(i=1; i<histfrogp; i++)
+ if(strcmp(histfrog[i]->name+1, "..") == 0) {
+ memmove(histfrog+i-1, histfrog+i+1,
+ (histfrogp-i-1)*sizeof(histfrog[0]));
+ histfrogp--;
+ goto out;
+ }
+
+ /*
+ * next try to collapse .
+ */
+ for(i=0; i<histfrogp; i++)
+ if(strcmp(histfrog[i]->name+1, ".") == 0) {
+ memmove(histfrog+i, histfrog+i+1,
+ (histfrogp-i-1)*sizeof(histfrog[0]));
+ goto out;
+ }
+
+ /*
+ * last chance, just truncate from front
+ */
+ memmove(histfrog+0, histfrog+1,
+ (histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+ histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+ p->as = ANOP;
+ p->from.type = D_NONE;
+ p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+ int n;
+
+ n = stop - good;
+ memmove(buf, good, stop - good);
+ stop = buf + n;
+ n = MAXIO - n;
+ if(n > max)
+ n = max;
+ n = read(f, stop, n);
+ if(n <= 0)
+ return 0;
+ return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+ long ipc;
+ Prog *p, *t;
+ uchar *bloc, *bsize, *stop;
+ Sym *h[NSYM], *s, *di;
+ int v, o, r, skip;
+ ulong sig;
+ static int files;
+ static char **filen;
+ char **nfilen;
+
+ if((files&15) == 0){
+ nfilen = malloc((files+16)*sizeof(char*));
+ memmove(nfilen, filen, files*sizeof(char*));
+ free(filen);
+ filen = nfilen;
+ }
+ filen[files++] = strdup(pn);
+
+ bsize = buf.xbuf;
+ bloc = buf.xbuf;
+ di = S;
+
+newloop:
+ memset(h, 0, sizeof(h));
+ version++;
+ histfrogp = 0;
+ ipc = pc;
+ skip = 0;
+
+loop:
+ if(c <= 0)
+ goto eof;
+ r = bsize - bloc;
+ if(r < 100 && r < c) { /* enough for largest prog */
+ bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+ if(bsize == 0)
+ goto eof;
+ bloc = buf.xbuf;
+ goto loop;
+ }
+ o = bloc[0]; /* as */
+ if(o <= AXXX || o >= ALAST) {
+ diag("%s: line %ld: opcode out of range %d", pn, pc-ipc, o);
+ print(" probably not a .5 file\n");
+ errorexit();
+ }
+ if(o == ANAME || o == ASIGNAME) {
+ sig = 0;
+ if(o == ASIGNAME){
+ sig = bloc[1] | (bloc[2]<<8) | (bloc[3]<<16) | (bloc[4]<<24);
+ bloc += 4;
+ c -= 4;
+ }
+ stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+ if(stop == 0){
+ bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+ if(bsize == 0)
+ goto eof;
+ bloc = buf.xbuf;
+ stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+ if(stop == 0){
+ fprint(2, "%s: name too long\n", pn);
+ errorexit();
+ }
+ }
+ v = bloc[1]; /* type */
+ o = bloc[2]; /* sym */
+ bloc += 3;
+ c -= 3;
+
+ r = 0;
+ if(v == D_STATIC)
+ r = version;
+ s = lookup((char*)bloc, r);
+ c -= &stop[1] - bloc;
+ bloc = stop + 1;
+
+ if(sig != 0){
+ if(s->sig != 0 && s->sig != sig)
+ diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, filen[s->file], sig, pn, s->name);
+ s->sig = sig;
+ s->file = files-1;
+ }
+
+ if(debug['W'])
+ print(" ANAME %s\n", s->name);
+ h[o] = s;
+ if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+ s->type = SXREF;
+ if(v == D_FILE) {
+ if(s->type != SFILE) {
+ histgen++;
+ s->type = SFILE;
+ s->value = histgen;
+ }
+ if(histfrogp < MAXHIST) {
+ histfrog[histfrogp] = s;
+ histfrogp++;
+ } else
+ collapsefrog(s);
+ }
+ goto loop;
+ }
+
+ if(nhunk < sizeof(Prog))
+ gethunk();
+ p = (Prog*)hunk;
+ nhunk -= sizeof(Prog);
+ hunk += sizeof(Prog);
+
+ p->as = o;
+ p->scond = bloc[1];
+ p->reg = bloc[2];
+ p->line = bloc[3] | (bloc[4]<<8) | (bloc[5]<<16) | (bloc[6]<<24);
+
+ r = zaddr(bloc+7, &p->from, h) + 7;
+ r += zaddr(bloc+r, &p->to, h);
+ bloc += r;
+ c -= r;
+
+ if(p->reg > NREG)
+ diag("register out of range %d", p->reg);
+
+ p->link = P;
+ p->cond = P;
+
+ if(debug['W'])
+ print("%P\n", p);
+
+ switch(o) {
+ case AHISTORY:
+ if(p->to.offset == -1) {
+ addlib(pn);
+ histfrogp = 0;
+ goto loop;
+ }
+ addhist(p->line, D_FILE); /* 'z' */
+ if(p->to.offset)
+ addhist(p->to.offset, D_FILE1); /* 'Z' */
+ histfrogp = 0;
+ goto loop;
+
+ case AEND:
+ histtoauto();
+ if(curtext != P)
+ curtext->to.autom = curauto;
+ curauto = 0;
+ curtext = P;
+ if(c)
+ goto newloop;
+ return;
+
+ case AGLOBL:
+ s = p->from.sym;
+ if(s == S) {
+ diag("GLOBL must have a name\n%P", p);
+ errorexit();
+ }
+ if(s->type == 0 || s->type == SXREF) {
+ s->type = SBSS;
+ s->value = 0;
+ }
+ if(s->type != SBSS) {
+ diag("redefinition: %s\n%P", s->name, p);
+ s->type = SBSS;
+ s->value = 0;
+ }
+ if(p->to.offset > s->value)
+ s->value = p->to.offset;
+ break;
+
+ case ADYNT:
+ if(p->to.sym == S) {
+ diag("DYNT without a sym\n%P", p);
+ break;
+ }
+ di = p->to.sym;
+ p->reg = 4;
+ if(di->type == SXREF) {
+ if(debug['z'])
+ Bprint(&bso, "%P set to %d\n", p, dtype);
+ di->type = SCONST;
+ di->value = dtype;
+ dtype += 4;
+ }
+ if(p->from.sym == S)
+ break;
+
+ p->from.offset = di->value;
+ p->from.sym->type = SDATA;
+ if(curtext == P) {
+ diag("DYNT not in text: %P", p);
+ break;
+ }
+ p->to.sym = curtext->from.sym;
+ p->to.type = D_CONST;
+ p->link = datap;
+ datap = p;
+ break;
+
+ case AINIT:
+ if(p->from.sym == S) {
+ diag("INIT without a sym\n%P", p);
+ break;
+ }
+ if(di == S) {
+ diag("INIT without previous DYNT\n%P", p);
+ break;
+ }
+ p->from.offset = di->value;
+ p->from.sym->type = SDATA;
+ p->link = datap;
+ datap = p;
+ break;
+
+ case ADATA:
+ if(p->from.sym == S) {
+ diag("DATA without a sym\n%P", p);
+ break;
+ }
+ p->link = datap;
+ datap = p;
+ break;
+
+ case AGOK:
+ diag("unknown opcode\n%P", p);
+ p->pc = pc;
+ pc++;
+ break;
+
+ case ATEXT:
+ if(curtext != P) {
+ histtoauto();
+ curtext->to.autom = curauto;
+ curauto = 0;
+ }
+ skip = 0;
+ curtext = p;
+ autosize = (p->to.offset+3L) & ~3L;
+ p->to.offset = autosize;
+ autosize += 4;
+ s = p->from.sym;
+ if(s == S) {
+ diag("TEXT must have a name\n%P", p);
+ errorexit();
+ }
+ if(s->type != 0 && s->type != SXREF) {
+ if(p->reg & DUPOK) {
+ skip = 1;
+ goto casedef;
+ }
+ diag("redefinition: %s\n%P", s->name, p);
+ }
+ s->type = STEXT;
+ s->value = pc;
+ lastp->link = p;
+ lastp = p;
+ p->pc = pc;
+ pc++;
+ if(textp == P) {
+ textp = p;
+ etextp = p;
+ goto loop;
+ }
+ etextp->cond = p;
+ etextp = p;
+ break;
+
+ case ASUB:
+ if(p->from.type == D_CONST)
+ if(p->from.name == D_NONE)
+ if(p->from.offset < 0) {
+ p->from.offset = -p->from.offset;
+ p->as = AADD;
+ }
+ goto casedef;
+
+ case AADD:
+ if(p->from.type == D_CONST)
+ if(p->from.name == D_NONE)
+ if(p->from.offset < 0) {
+ p->from.offset = -p->from.offset;
+ p->as = ASUB;
+ }
+ goto casedef;
+
+ case AMOVF:
+ if(skip)
+ goto casedef;
+
+ if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) {
+ /* size sb 9 max */
+ sprint(literal, "$%lux", ieeedtof(p->from.ieee));
+ s = lookup(literal, 0);
+ if(s->type == 0) {
+ s->type = SBSS;
+ s->value = 4;
+ t = prg();
+ t->as = ADATA;
+ t->line = p->line;
+ t->from.type = D_OREG;
+ t->from.sym = s;
+ t->from.name = D_EXTERN;
+ t->reg = 4;
+ t->to = p->from;
+ t->link = datap;
+ datap = t;
+ }
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ p->from.name = D_EXTERN;
+ p->from.offset = 0;
+ }
+ goto casedef;
+
+ case AMOVD:
+ if(skip)
+ goto casedef;
+
+ if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) {
+ /* size sb 18 max */
+ sprint(literal, "$%lux.%lux",
+ p->from.ieee->l, p->from.ieee->h);
+ s = lookup(literal, 0);
+ if(s->type == 0) {
+ s->type = SBSS;
+ s->value = 8;
+ t = prg();
+ t->as = ADATA;
+ t->line = p->line;
+ t->from.type = D_OREG;
+ t->from.sym = s;
+ t->from.name = D_EXTERN;
+ t->reg = 8;
+ t->to = p->from;
+ t->link = datap;
+ datap = t;
+ }
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ p->from.name = D_EXTERN;
+ p->from.offset = 0;
+ }
+ goto casedef;
+
+ default:
+ casedef:
+ if(skip)
+ nopout(p);
+
+ if(p->to.type == D_BRANCH)
+ p->to.offset += ipc;
+ lastp->link = p;
+ lastp = p;
+ p->pc = pc;
+ pc++;
+ break;
+ }
+ goto loop;
+
+eof:
+ diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+ Sym *s;
+ char *p;
+ long h;
+ int c, l;
+
+ h = v;
+ for(p=symb; c = *p; p++)
+ h = h+h+h + c;
+ l = (p - symb) + 1;
+ h &= 0xffffff;
+ h %= NHASH;
+ for(s = hash[h]; s != S; s = s->link)
+ if(s->version == v)
+ if(memcmp(s->name, symb, l) == 0)
+ return s;
+
+ while(nhunk < sizeof(Sym))
+ gethunk();
+ s = (Sym*)hunk;
+ nhunk -= sizeof(Sym);
+ hunk += sizeof(Sym);
+
+ s->name = malloc(l);
+ memmove(s->name, symb, l);
+
+ s->link = hash[h];
+ s->type = 0;
+ s->version = v;
+ s->value = 0;
+ s->sig = 0;
+ hash[h] = s;
+ return s;
+}
+
+Prog*
+prg(void)
+{
+ Prog *p;
+
+ while(nhunk < sizeof(Prog))
+ gethunk();
+ p = (Prog*)hunk;
+ nhunk -= sizeof(Prog);
+ hunk += sizeof(Prog);
+
+ *p = zprg;
+ return p;
+}
+
+void
+gethunk(void)
+{
+ char *h;
+ long nh;
+
+ nh = NHUNK;
+ if(thunk >= 5L*NHUNK) {
+ nh = 5L*NHUNK;
+ if(thunk >= 25L*NHUNK)
+ nh = 25L*NHUNK;
+ }
+ h = mysbrk(nh);
+ if(h == (char*)-1) {
+ diag("out of memory");
+ errorexit();
+ }
+ hunk = h;
+ nhunk = nh;
+ thunk += nh;
+}
+
+void
+doprof1(void)
+{
+ Sym *s;
+ long n;
+ Prog *p, *q;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f profile 1\n", cputime());
+ Bflush(&bso);
+ s = lookup("__mcount", 0);
+ n = 1;
+ for(p = firstp->link; p != P; p = p->link) {
+ if(p->as == ATEXT) {
+ q = prg();
+ q->line = p->line;
+ q->link = datap;
+ datap = q;
+ q->as = ADATA;
+ q->from.type = D_OREG;
+ q->from.name = D_EXTERN;
+ q->from.offset = n*4;
+ q->from.sym = s;
+ q->reg = 4;
+ q->to = p->from;
+ q->to.type = D_CONST;
+
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ p->link = q;
+ p = q;
+ p->as = AMOVW;
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.sym = s;
+ p->from.offset = n*4 + 4;
+ p->to.type = D_REG;
+ p->to.reg = REGTMP;
+
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ p->link = q;
+ p = q;
+ p->as = AADD;
+ p->from.type = D_CONST;
+ p->from.offset = 1;
+ p->to.type = D_REG;
+ p->to.reg = REGTMP;
+
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ p->link = q;
+ p = q;
+ p->as = AMOVW;
+ p->from.type = D_REG;
+ p->from.reg = REGTMP;
+ p->to.type = D_OREG;
+ p->to.name = D_EXTERN;
+ p->to.sym = s;
+ p->to.offset = n*4 + 4;
+
+ n += 2;
+ continue;
+ }
+ }
+ q = prg();
+ q->line = 0;
+ q->link = datap;
+ datap = q;
+
+ q->as = ADATA;
+ q->from.type = D_OREG;
+ q->from.name = D_EXTERN;
+ q->from.sym = s;
+ q->reg = 4;
+ q->to.type = D_CONST;
+ q->to.offset = n;
+
+ s->type = SBSS;
+ s->value = n*4;
+}
+
+static int brcond[] = {ABEQ, ABNE, ABCS, ABCC, ABMI, ABPL, ABVS, ABVC, ABHI, ABLS, ABGE, ABLT, ABGT, ABLE};
+
+void
+doprof2(void)
+{
+ Sym *s2, *s4;
+ Prog *p, *q, *q2, *ps2, *ps4;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f profile 2\n", cputime());
+ Bflush(&bso);
+
+ if(debug['e']){
+ s2 = lookup("_tracein", 0);
+ s4 = lookup("_traceout", 0);
+ }else{
+ s2 = lookup("_profin", 0);
+ s4 = lookup("_profout", 0);
+ }
+ if(s2->type != STEXT || s4->type != STEXT) {
+ if(debug['e'])
+ diag("_tracein/_traceout not defined %d %d", s2->type, s4->type);
+ else
+ diag("_profin/_profout not defined");
+ return;
+ }
+
+ ps2 = P;
+ ps4 = P;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT) {
+ if(p->from.sym == s2) {
+ ps2 = p;
+ p->reg = 1;
+ }
+ if(p->from.sym == s4) {
+ ps4 = p;
+ p->reg = 1;
+ }
+ }
+ }
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT) {
+ if(p->reg & NOPROF) {
+ for(;;) {
+ q = p->link;
+ if(q == P)
+ break;
+ if(q->as == ATEXT)
+ break;
+ p = q;
+ }
+ continue;
+ }
+
+ /*
+ * BL profin
+ */
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ if(debug['e']){ /* embedded tracing */
+ q2 = prg();
+ p->link = q2;
+ q2->link = q;
+
+ q2->line = p->line;
+ q2->pc = p->pc;
+
+ q2->as = AB;
+ q2->to.type = D_BRANCH;
+ q2->to.sym = p->to.sym;
+ q2->cond = q->link;
+ }else
+ p->link = q;
+ p = q;
+ p->as = ABL;
+ p->to.type = D_BRANCH;
+ p->cond = ps2;
+ p->to.sym = s2;
+
+ continue;
+ }
+ if(p->as == ARET) {
+ /*
+ * RET (default)
+ */
+ if(debug['e']){ /* embedded tracing */
+ q = prg();
+ q->line = p->line;
+ q->pc = p->pc;
+ q->link = p->link;
+ p->link = q;
+ p = q;
+ }
+
+ /*
+ * RET
+ */
+ q = prg();
+ q->as = ARET;
+ q->from = p->from;
+ q->to = p->to;
+ q->cond = p->cond;
+ q->link = p->link;
+ q->reg = p->reg;
+ p->link = q;
+
+ if(p->scond != 14) {
+ q = prg();
+ q->as = ABL;
+ q->from = zprg.from;
+ q->to = zprg.to;
+ q->to.type = D_BRANCH;
+ q->cond = ps4;
+ q->to.sym = s4;
+ q->link = p->link;
+ p->link = q;
+
+ p->as = brcond[p->scond^1]; /* complement */
+ p->scond = 14;
+ p->from = zprg.from;
+ p->to = zprg.to;
+ p->to.type = D_BRANCH;
+ p->cond = q->link->link; /* successor of RET */
+ p->to.offset = q->link->link->pc;
+
+ p = q->link->link;
+ } else {
+
+ /*
+ * BL profout
+ */
+ p->as = ABL;
+ p->from = zprg.from;
+ p->to = zprg.to;
+ p->to.type = D_BRANCH;
+ p->cond = ps4;
+ p->to.sym = s4;
+ p->scond = 14;
+
+ p = q;
+ }
+ continue;
+ }
+ }
+}
+
+void
+nuxiinit(void)
+{
+
+ int i, c;
+
+ for(i=0; i<4; i++) {
+ c = find1(0x04030201L, i+1);
+ if(i < 2)
+ inuxi2[i] = c;
+ if(i < 1)
+ inuxi1[i] = c;
+ inuxi4[i] = c;
+ fnuxi4[i] = c;
+ if(debug['d'] == 0){
+ fnuxi8[i] = c;
+ fnuxi8[i+4] = c+4;
+ }
+ else{
+ fnuxi8[i] = c+4; /* ms word first, then ls, even in little endian mode */
+ fnuxi8[i+4] = c;
+ }
+ }
+ if(debug['v']) {
+ Bprint(&bso, "inuxi = ");
+ for(i=0; i<1; i++)
+ Bprint(&bso, "%d", inuxi1[i]);
+ Bprint(&bso, " ");
+ for(i=0; i<2; i++)
+ Bprint(&bso, "%d", inuxi2[i]);
+ Bprint(&bso, " ");
+ for(i=0; i<4; i++)
+ Bprint(&bso, "%d", inuxi4[i]);
+ Bprint(&bso, "\nfnuxi = ");
+ for(i=0; i<4; i++)
+ Bprint(&bso, "%d", fnuxi4[i]);
+ Bprint(&bso, " ");
+ for(i=0; i<8; i++)
+ Bprint(&bso, "%d", fnuxi8[i]);
+ Bprint(&bso, "\n");
+ }
+ Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+ char *p;
+ int i;
+
+ p = (char*)&l;
+ for(i=0; i<4; i++)
+ if(*p++ == c)
+ return i;
+ return 0;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+ int exp;
+ long v;
+
+ if(ieeep->h == 0)
+ return 0;
+ exp = (ieeep->h>>20) & ((1L<<11)-1L);
+ exp -= (1L<<10) - 2L;
+ v = (ieeep->h & 0xfffffL) << 3;
+ v |= (ieeep->l >> 29) & 0x7L;
+ if((ieeep->l >> 28) & 1) {
+ v++;
+ if(v & 0x800000L) {
+ v = (v & 0x7fffffL) >> 1;
+ exp++;
+ }
+ }
+ if(exp <= -126 || exp >= 130)
+ diag("double fp to single fp overflow");
+ v |= ((exp + 126) & 0xffL) << 23;
+ v |= ieeep->h & 0x80000000L;
+ return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+ Ieee e;
+ double fr;
+ int exp;
+
+ if(ieeep->h & (1L<<31)) {
+ e.h = ieeep->h & ~(1L<<31);
+ e.l = ieeep->l;
+ return -ieeedtod(&e);
+ }
+ if(ieeep->l == 0 && ieeep->h == 0)
+ return 0;
+ fr = ieeep->l & ((1L<<16)-1L);
+ fr /= 1L<<16;
+ fr += (ieeep->l>>16) & ((1L<<16)-1L);
+ fr /= 1L<<16;
+ fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+ fr /= 1L<<21;
+ exp = (ieeep->h>>20) & ((1L<<11)-1L);
+ exp -= (1L<<10) - 2L;
+ return ldexp(fr, exp);
+}
+
+void
+undefsym(Sym *s)
+{
+ int n;
+
+ n = imports;
+ if(s->value != 0)
+ diag("value != 0 on SXREF");
+ if(n >= 1<<Rindex)
+ diag("import index %d out of range", n);
+ s->value = n<<Roffset;
+ s->type = SUNDEF;
+ imports++;
+}
+
+void
+zerosig(char *sp)
+{
+ Sym *s;
+
+ s = lookup(sp, 0);
+ s->sig = 0;
+}
+
+void
+readundefs(char *f, int t)
+{
+ int i, n;
+ Sym *s;
+ Biobuf *b;
+ char *l, buf[256], *fields[64];
+
+ if(f == nil)
+ return;
+ b = Bopen(f, OREAD);
+ if(b == nil){
+ diag("could not open %s: %r", f);
+ errorexit();
+ }
+ while((l = Brdline(b, '\n')) != nil){
+ n = Blinelen(b);
+ if(n >= sizeof(buf)){
+ diag("%s: line too long", f);
+ errorexit();
+ }
+ memmove(buf, l, n);
+ buf[n-1] = '\0';
+ n = getfields(buf, fields, nelem(fields), 1, " \t\r\n");
+ if(n == nelem(fields)){
+ diag("%s: bad format", f);
+ errorexit();
+ }
+ for(i = 0; i < n; i++) {
+ s = lookup(fields[i], 0);
+ s->type = SXREF;
+ s->subtype = t;
+ if(t == SIMPORT)
+ nimports++;
+ else
+ nexports++;
+ }
+ }
+ Bterm(b);
+}
--- /dev/null
+++ b/utils/5l/optab.c
@@ -1,0 +1,257 @@
+#include "l.h"
+
+Optab optab[] =
+{
+ { ATEXT, C_LEXT, C_NONE, C_LCON, 0, 0, 0 },
+ { ATEXT, C_LEXT, C_REG, C_LCON, 0, 0, 0 },
+ { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 },
+ { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 },
+
+ { AADD, C_REG, C_REG, C_REG, 1, 4, 0 },
+ { AADD, C_REG, C_NONE, C_REG, 1, 4, 0 },
+ { AMOVW, C_REG, C_NONE, C_REG, 1, 4, 0 },
+ { AMVN, C_REG, C_NONE, C_REG, 1, 4, 0 },
+ { ACMP, C_REG, C_REG, C_NONE, 1, 4, 0 },
+
+ { AADD, C_RCON, C_REG, C_REG, 2, 4, 0 },
+ { AADD, C_RCON, C_NONE, C_REG, 2, 4, 0 },
+ { AMOVW, C_RCON, C_NONE, C_REG, 2, 4, 0 },
+ { AMVN, C_RCON, C_NONE, C_REG, 2, 4, 0 },
+ { ACMP, C_RCON, C_REG, C_NONE, 2, 4, 0 },
+
+ { AADD, C_SHIFT,C_REG, C_REG, 3, 4, 0 },
+ { AADD, C_SHIFT,C_NONE, C_REG, 3, 4, 0 },
+ { AMVN, C_SHIFT,C_NONE, C_REG, 3, 4, 0 },
+ { ACMP, C_SHIFT,C_REG, C_NONE, 3, 4, 0 },
+
+ { AMOVW, C_RECON,C_NONE, C_REG, 4, 4, REGSB },
+ { AMOVW, C_RACON,C_NONE, C_REG, 4, 4, REGSP },
+
+ { AB, C_NONE, C_NONE, C_SBRA, 5, 4, 0, LPOOL },
+ { ABL, C_NONE, C_NONE, C_SBRA, 5, 4, 0 },
+ { ABEQ, C_NONE, C_NONE, C_SBRA, 5, 4, 0 },
+
+ { AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL },
+ { ABL, C_NONE, C_NONE, C_ROREG, 7, 8, 0 },
+
+ { ASLL, C_RCON, C_REG, C_REG, 8, 4, 0 },
+ { ASLL, C_RCON, C_NONE, C_REG, 8, 4, 0 },
+
+ { ASLL, C_REG, C_NONE, C_REG, 9, 4, 0 },
+ { ASLL, C_REG, C_REG, C_REG, 9, 4, 0 },
+
+ { ASWI, C_NONE, C_NONE, C_NONE, 10, 4, 0 },
+ { ASWI, C_NONE, C_NONE, C_LOREG, 10, 4, 0 },
+
+ { AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0 },
+ { AWORD, C_NONE, C_NONE, C_LEXT, 11, 4, 0 },
+ { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 },
+
+ { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 },
+ { AMOVW, C_LCON, C_NONE, C_REG, 12, 4, 0, LFROM },
+
+ { AADD, C_NCON, C_REG, C_REG, 13, 8, 0 },
+ { AADD, C_NCON, C_NONE, C_REG, 13, 8, 0 },
+ { AMVN, C_NCON, C_NONE, C_REG, 13, 8, 0 },
+ { ACMP, C_NCON, C_REG, C_NONE, 13, 8, 0 },
+ { AADD, C_LCON, C_REG, C_REG, 13, 8, 0, LFROM },
+ { AADD, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM },
+ { AMVN, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM },
+ { ACMP, C_LCON, C_REG, C_NONE, 13, 8, 0, LFROM },
+
+ { AMOVB, C_REG, C_NONE, C_REG, 14, 8, 0 },
+ { AMOVBU, C_REG, C_NONE, C_REG, 58, 4, 0 },
+ { AMOVH, C_REG, C_NONE, C_REG, 14, 8, 0 },
+ { AMOVHU, C_REG, C_NONE, C_REG, 14, 8, 0 },
+
+ { AMUL, C_REG, C_REG, C_REG, 15, 4, 0 },
+ { AMUL, C_REG, C_NONE, C_REG, 15, 4, 0 },
+
+ { ADIV, C_REG, C_REG, C_REG, 16, 4, 0 },
+ { ADIV, C_REG, C_NONE, C_REG, 16, 4, 0 },
+
+ { AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0 },
+
+ { AMOVW, C_REG, C_NONE, C_SEXT, 20, 4, REGSB },
+ { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP },
+ { AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 },
+ { AMOVB, C_REG, C_NONE, C_SEXT, 20, 4, REGSB },
+ { AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP },
+ { AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0 },
+ { AMOVBU, C_REG, C_NONE, C_SEXT, 20, 4, REGSB },
+ { AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP },
+ { AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0 },
+
+ { AMOVW, C_SEXT, C_NONE, C_REG, 21, 4, REGSB },
+ { AMOVW, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP },
+ { AMOVW, C_SOREG,C_NONE, C_REG, 21, 4, 0 },
+ { AMOVBU, C_SEXT, C_NONE, C_REG, 21, 4, REGSB },
+ { AMOVBU, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP },
+ { AMOVBU, C_SOREG,C_NONE, C_REG, 21, 4, 0 },
+
+ { AMOVB, C_SEXT, C_NONE, C_REG, 22, 12, REGSB },
+ { AMOVB, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP },
+ { AMOVB, C_SOREG,C_NONE, C_REG, 22, 12, 0 },
+ { AMOVH, C_SEXT, C_NONE, C_REG, 22, 12, REGSB },
+ { AMOVH, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP },
+ { AMOVH, C_SOREG,C_NONE, C_REG, 22, 12, 0 },
+ { AMOVHU, C_SEXT, C_NONE, C_REG, 22, 12, REGSB },
+ { AMOVHU, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP },
+ { AMOVHU, C_SOREG,C_NONE, C_REG, 22, 12, 0 },
+
+ { AMOVH, C_REG, C_NONE, C_SEXT, 23, 12, REGSB },
+ { AMOVH, C_REG, C_NONE, C_SAUTO, 23, 12, REGSP },
+ { AMOVH, C_REG, C_NONE, C_SOREG, 23, 12, 0 },
+ { AMOVHU, C_REG, C_NONE, C_SEXT, 23, 12, REGSB },
+ { AMOVHU, C_REG, C_NONE, C_SAUTO, 23, 12, REGSP },
+ { AMOVHU, C_REG, C_NONE, C_SOREG, 23, 12, 0 },
+
+ { AMOVW, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO },
+ { AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO },
+ { AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO },
+ { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO },
+ { AMOVB, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO },
+ { AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO },
+ { AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO },
+ { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO },
+ { AMOVBU, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO },
+ { AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO },
+ { AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO },
+ { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO },
+
+ { AMOVW, C_LEXT, C_NONE, C_REG, 31, 8, REGSB, LFROM },
+ { AMOVW, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM },
+ { AMOVW, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM },
+ { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM },
+ { AMOVBU, C_LEXT, C_NONE, C_REG, 31, 8, REGSB, LFROM },
+ { AMOVBU, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM },
+ { AMOVBU, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM },
+ { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM },
+
+ { AMOVB, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM },
+ { AMOVB, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM },
+ { AMOVB, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM },
+ { AMOVB, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM },
+ { AMOVH, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM },
+ { AMOVH, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM },
+ { AMOVH, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM },
+ { AMOVH, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM },
+ { AMOVHU, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM },
+ { AMOVHU, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM },
+ { AMOVHU, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM },
+ { AMOVHU, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM },
+
+ { AMOVH, C_REG, C_NONE, C_LEXT, 33, 24, REGSB, LTO },
+ { AMOVH, C_REG, C_NONE, C_LAUTO, 33, 24, REGSP, LTO },
+ { AMOVH, C_REG, C_NONE, C_LOREG, 33, 24, 0, LTO },
+ { AMOVH, C_REG, C_NONE, C_ADDR, 67, 24, 0, LTO },
+ { AMOVHU, C_REG, C_NONE, C_LEXT, 33, 24, REGSB, LTO },
+ { AMOVHU, C_REG, C_NONE, C_LAUTO, 33, 24, REGSP, LTO },
+ { AMOVHU, C_REG, C_NONE, C_LOREG, 33, 24, 0, LTO },
+ { AMOVHU, C_REG, C_NONE, C_ADDR, 67, 24, 0, LTO },
+
+ { AMOVW, C_LECON,C_NONE, C_REG, 34, 8, REGSB, LFROM },
+ { AMOVW, C_LACON,C_NONE, C_REG, 34, 8, REGSP, LFROM },
+
+ { AMOVW, C_PSR, C_NONE, C_REG, 35, 4, 0 },
+ { AMOVW, C_REG, C_NONE, C_PSR, 36, 4, 0 },
+ { AMOVW, C_RCON, C_NONE, C_PSR, 37, 4, 0 },
+
+ { AMOVM, C_LCON, C_NONE, C_SOREG, 38, 4, 0 },
+ { AMOVM, C_SOREG,C_NONE, C_LCON, 39, 4, 0 },
+
+ { ASWPW, C_SOREG,C_REG, C_REG, 40, 4, 0 },
+
+ { ARFE, C_NONE, C_NONE, C_NONE, 41, 4, 0 },
+
+ { AMOVF, C_FREG, C_NONE, C_FEXT, 50, 4, REGSB },
+ { AMOVF, C_FREG, C_NONE, C_FAUTO, 50, 4, REGSP },
+ { AMOVF, C_FREG, C_NONE, C_FOREG, 50, 4, 0 },
+
+ { AMOVF, C_FEXT, C_NONE, C_FREG, 51, 4, REGSB },
+ { AMOVF, C_FAUTO,C_NONE, C_FREG, 51, 4, REGSP },
+ { AMOVF, C_FOREG,C_NONE, C_FREG, 51, 4, 0 },
+
+ { AMOVF, C_FREG, C_NONE, C_LEXT, 52, 12, REGSB, LTO },
+ { AMOVF, C_FREG, C_NONE, C_LAUTO, 52, 12, REGSP, LTO },
+ { AMOVF, C_FREG, C_NONE, C_LOREG, 52, 12, 0, LTO },
+
+ { AMOVF, C_LEXT, C_NONE, C_FREG, 53, 12, REGSB, LFROM },
+ { AMOVF, C_LAUTO,C_NONE, C_FREG, 53, 12, REGSP, LFROM },
+ { AMOVF, C_LOREG,C_NONE, C_FREG, 53, 12, 0, LFROM },
+
+ { AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO },
+ { AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM },
+
+ { AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0 },
+ { AADDF, C_FREG, C_REG, C_FREG, 54, 4, 0 },
+ { AADDF, C_FCON, C_NONE, C_FREG, 54, 4, 0 },
+ { AADDF, C_FCON, C_REG, C_FREG, 54, 4, 0 },
+ { AMOVF, C_FCON, C_NONE, C_FREG, 54, 4, 0 },
+ { AMOVF, C_FREG, C_NONE, C_FREG, 54, 4, 0 },
+
+ { ACMPF, C_FREG, C_REG, C_NONE, 54, 4, 0 },
+ { ACMPF, C_FCON, C_REG, C_NONE, 54, 4, 0 },
+
+ { AMOVFW, C_FREG, C_NONE, C_REG, 55, 4, 0 },
+ { AMOVFW, C_REG, C_NONE, C_FREG, 55, 4, 0 },
+
+ { AMOVW, C_REG, C_NONE, C_FCR, 56, 4, 0 },
+ { AMOVW, C_FCR, C_NONE, C_REG, 57, 4, 0 },
+
+ { AMOVW, C_SHIFT,C_NONE, C_REG, 59, 4, 0 },
+ { AMOVBU, C_SHIFT,C_NONE, C_REG, 59, 4, 0 },
+
+ { AMOVB, C_SHIFT,C_NONE, C_REG, 60, 4, 0 },
+
+ { AMOVW, C_REG, C_NONE, C_SHIFT, 61, 4, 0 },
+ { AMOVB, C_REG, C_NONE, C_SHIFT, 61, 4, 0 },
+ { AMOVBU, C_REG, C_NONE, C_SHIFT, 61, 4, 0 },
+
+ { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0 },
+ { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 },
+
+ { AADDF, C_FREG, C_NONE, C_FREG, 74, 4, 0, VFP },
+ { AADDF, C_FREG, C_REG, C_FREG, 74, 4, 0, VFP },
+ { AMOVF, C_FREG, C_NONE, C_FREG, 74, 4, 0, VFP },
+ { ACMPF, C_FREG, C_REG, C_NONE, 75, 8, 0, VFP },
+ { ACMPF, C_FCON, C_REG, C_NONE, 75, 8, 0, VFP },
+ { AMOVFW, C_FREG, C_NONE, C_REG, 76, 8, 0, VFP },
+ { AMOVFW, C_REG, C_NONE, C_FREG, 76, 8, 0, VFP },
+
+ { AMOVH, C_REG, C_NONE, C_HEXT, 70, 4, REGSB, V4 },
+ { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 },
+ { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 },
+ { AMOVHU, C_REG, C_NONE, C_HEXT, 70, 4, REGSB, V4 },
+ { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 },
+ { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 },
+
+ { AMOVB, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 },
+ { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 },
+ { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 },
+ { AMOVH, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 },
+ { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 },
+ { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 },
+ { AMOVHU, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 },
+ { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 },
+ { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 },
+
+ { AMOVH, C_REG, C_NONE, C_LEXT, 72, 8, REGSB, LTO|V4 },
+ { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 },
+ { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 },
+ { AMOVHU, C_REG, C_NONE, C_LEXT, 72, 8, REGSB, LTO|V4 },
+ { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 },
+ { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 },
+
+ { AMOVB, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 },
+ { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 },
+ { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 },
+ { AMOVH, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 },
+ { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 },
+ { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 },
+ { AMOVHU, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 },
+ { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 },
+ { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 },
+
+ { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 },
+};
--- /dev/null
+++ b/utils/5l/pass.c
@@ -1,0 +1,595 @@
+#include "l.h"
+
+void
+dodata(void)
+{
+ int i, t;
+ Sym *s;
+ Prog *p;
+ long orig, v;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f dodata\n", cputime());
+ Bflush(&bso);
+ for(p = datap; p != P; p = p->link) {
+ s = p->from.sym;
+ if(p->as == ADYNT || p->as == AINIT)
+ s->value = dtype;
+ if(s->type == SBSS)
+ s->type = SDATA;
+ if(s->type != SDATA)
+ diag("initialize non-data (%d): %s\n%P",
+ s->type, s->name, p);
+ v = p->from.offset + p->reg;
+ if(v > s->value)
+ diag("initialize bounds (%ld): %s\n%P",
+ s->value, s->name, p);
+ }
+
+ if(debug['t']) {
+ /*
+ * pull out string constants
+ */
+ for(p = datap; p != P; p = p->link) {
+ s = p->from.sym;
+ if(p->to.type == D_SCONST)
+ s->type = SSTRING;
+ }
+ }
+
+ /*
+ * pass 1
+ * assign 'small' variables to data segment
+ * (rational is that data segment is more easily
+ * addressed through offset on R12)
+ */
+ orig = 0;
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ t = s->type;
+ if(t != SDATA && t != SBSS)
+ continue;
+ v = s->value;
+ if(v == 0) {
+ diag("%s: no size", s->name);
+ v = 1;
+ }
+ while(v & 3)
+ v++;
+ s->value = v;
+ if(v > MINSIZ)
+ continue;
+ s->value = orig;
+ orig += v;
+ s->type = SDATA1;
+ }
+
+ /*
+ * pass 2
+ * assign large 'data' variables to data segment
+ */
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ t = s->type;
+ if(t != SDATA) {
+ if(t == SDATA1)
+ s->type = SDATA;
+ continue;
+ }
+ v = s->value;
+ s->value = orig;
+ orig += v;
+ }
+
+ while(orig & 7)
+ orig++;
+ datsize = orig;
+
+ /*
+ * pass 3
+ * everything else to bss segment
+ */
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ if(s->type != SBSS)
+ continue;
+ v = s->value;
+ s->value = orig;
+ orig += v;
+ }
+ while(orig & 7)
+ orig++;
+ bsssize = orig-datsize;
+
+ xdefine("setR12", SDATA, 0L+BIG);
+ xdefine("bdata", SDATA, 0L);
+ xdefine("edata", SDATA, datsize);
+ xdefine("end", SBSS, datsize+bsssize);
+ xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+ int i;
+ Sym *s;
+
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link)
+ if(s->type == SXREF)
+ diag("%s: not defined", s->name);
+}
+
+Prog*
+brchain(Prog *p)
+{
+ int i;
+
+ for(i=0; i<20; i++) {
+ if(p == P || p->as != AB)
+ return p;
+ p = p->cond;
+ }
+ return P;
+}
+
+int
+relinv(int a)
+{
+ switch(a) {
+ case ABEQ: return ABNE;
+ case ABNE: return ABEQ;
+ case ABCS: return ABCC;
+ case ABHS: return ABLO;
+ case ABCC: return ABCS;
+ case ABLO: return ABHS;
+ case ABMI: return ABPL;
+ case ABPL: return ABMI;
+ case ABVS: return ABVC;
+ case ABVC: return ABVS;
+ case ABHI: return ABLS;
+ case ABLS: return ABHI;
+ case ABGE: return ABLT;
+ case ABLT: return ABGE;
+ case ABGT: return ABLE;
+ case ABLE: return ABGT;
+ }
+ diag("unknown relation: %s", anames[a]);
+ return a;
+}
+
+void
+follow(void)
+{
+ if(debug['v'])
+ Bprint(&bso, "%5.2f follow\n", cputime());
+ Bflush(&bso);
+
+ firstp = prg();
+ lastp = firstp;
+ xfol(textp);
+
+ firstp = firstp->link;
+ lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+ Prog *q, *r;
+ int a, i;
+
+loop:
+ if(p == P)
+ return;
+ a = p->as;
+ if(a == ATEXT)
+ curtext = p;
+ if(a == AB) {
+ q = p->cond;
+ if(q != P) {
+ p->mark |= FOLL;
+ p = q;
+ if(!(p->mark & FOLL))
+ goto loop;
+ }
+ }
+ if(p->mark & FOLL) {
+ for(i=0,q=p; i<4; i++,q=q->link) {
+ if(q == lastp)
+ break;
+ a = q->as;
+ if(a == ANOP) {
+ i--;
+ continue;
+ }
+ if(a == AB || (a == ARET && q->scond == 14) || a == ARFE)
+ goto copy;
+ if(!q->cond || (q->cond->mark&FOLL))
+ continue;
+ if(a != ABEQ && a != ABNE)
+ continue;
+ copy:
+ for(;;) {
+ r = prg();
+ *r = *p;
+ if(!(r->mark&FOLL))
+ print("cant happen 1\n");
+ r->mark |= FOLL;
+ if(p != q) {
+ p = p->link;
+ lastp->link = r;
+ lastp = r;
+ continue;
+ }
+ lastp->link = r;
+ lastp = r;
+ if(a == AB || (a == ARET && q->scond == 14) || a == ARFE)
+ return;
+ r->as = ABNE;
+ if(a == ABNE)
+ r->as = ABEQ;
+ r->cond = p->link;
+ r->link = p->cond;
+ if(!(r->link->mark&FOLL))
+ xfol(r->link);
+ if(!(r->cond->mark&FOLL))
+ print("cant happen 2\n");
+ return;
+ }
+ }
+ a = AB;
+ q = prg();
+ q->as = a;
+ q->line = p->line;
+ q->to.type = D_BRANCH;
+ q->to.offset = p->pc;
+ q->cond = p;
+ p = q;
+ }
+ p->mark |= FOLL;
+ lastp->link = p;
+ lastp = p;
+ if(a == AB || (a == ARET && p->scond == 14) || a == ARFE){
+ return;
+ }
+ if(p->cond != P)
+ if(a != ABL && p->link != P) {
+ q = brchain(p->link);
+ if(a != ATEXT && a != ABCASE)
+ if(q != P && (q->mark&FOLL)) {
+ p->as = relinv(a);
+ p->link = p->cond;
+ p->cond = q;
+ }
+ xfol(p->link);
+ q = brchain(p->cond);
+ if(q == P)
+ q = p->cond;
+ if(q->mark&FOLL) {
+ p->cond = q;
+ return;
+ }
+ p = q;
+ goto loop;
+ }
+ p = p->link;
+ goto loop;
+}
+
+void
+patch(void)
+{
+ long c, vexit;
+ Prog *p, *q;
+ Sym *s;
+ int a;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f patch\n", cputime());
+ Bflush(&bso);
+ mkfwd();
+ s = lookup("exit", 0);
+ vexit = s->value;
+ for(p = firstp; p != P; p = p->link) {
+ a = p->as;
+ if(a == ATEXT)
+ curtext = p;
+ if((a == ABL || a == AB || a == ARET) &&
+ p->to.type != D_BRANCH && p->to.sym != S) {
+ s = p->to.sym;
+ switch(s->type) {
+ default:
+ diag("undefined: %s\n%P", s->name, p);
+ s->type = STEXT;
+ s->value = vexit;
+ break;
+ case STEXT:
+ p->to.offset = s->value;
+ p->to.type = D_BRANCH;
+ break;
+ case SUNDEF:
+ if(p->as != ABL)
+ diag("help: SUNDEF in AB || ARET");
+ p->to.offset = 0;
+ p->to.type = D_BRANCH;
+ p->cond = UP;
+ break;
+ }
+ }
+ if(p->to.type != D_BRANCH || p->cond == UP)
+ continue;
+ c = p->to.offset;
+ for(q = firstp; q != P;) {
+ if(q->forwd != P)
+ if(c >= q->forwd->pc) {
+ q = q->forwd;
+ continue;
+ }
+ if(c == q->pc)
+ break;
+ q = q->link;
+ }
+ if(q == P) {
+ diag("branch out of range %ld\n%P", c, p);
+ p->to.type = D_NONE;
+ }
+ p->cond = q;
+ }
+
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT)
+ curtext = p;
+ if(p->cond != P && p->cond != UP) {
+ p->cond = brloop(p->cond);
+ if(p->cond != P)
+ if(p->to.type == D_BRANCH)
+ p->to.offset = p->cond->pc;
+ }
+ }
+}
+
+#define LOG 5
+void
+mkfwd(void)
+{
+ Prog *p;
+ long dwn[LOG], cnt[LOG], i;
+ Prog *lst[LOG];
+
+ for(i=0; i<LOG; i++) {
+ if(i == 0)
+ cnt[i] = 1; else
+ cnt[i] = LOG * cnt[i-1];
+ dwn[i] = 1;
+ lst[i] = P;
+ }
+ i = 0;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as == ATEXT)
+ curtext = p;
+ i--;
+ if(i < 0)
+ i = LOG-1;
+ p->forwd = P;
+ dwn[i]--;
+ if(dwn[i] <= 0) {
+ dwn[i] = cnt[i];
+ if(lst[i] != P)
+ lst[i]->forwd = p;
+ lst[i] = p;
+ }
+ }
+}
+
+Prog*
+brloop(Prog *p)
+{
+ Prog *q;
+ int c;
+
+ for(c=0; p!=P;) {
+ if(p->as != AB)
+ return p;
+ q = p->cond;
+ if(q <= p) {
+ c++;
+ if(q == p || c > 5000)
+ break;
+ }
+ p = q;
+ }
+ return P;
+}
+
+long
+atolwhex(char *s)
+{
+ long n;
+ int f;
+
+ n = 0;
+ f = 0;
+ while(*s == ' ' || *s == '\t')
+ s++;
+ if(*s == '-' || *s == '+') {
+ if(*s++ == '-')
+ f = 1;
+ while(*s == ' ' || *s == '\t')
+ s++;
+ }
+ if(s[0]=='0' && s[1]){
+ if(s[1]=='x' || s[1]=='X'){
+ s += 2;
+ for(;;){
+ if(*s >= '0' && *s <= '9')
+ n = n*16 + *s++ - '0';
+ else if(*s >= 'a' && *s <= 'f')
+ n = n*16 + *s++ - 'a' + 10;
+ else if(*s >= 'A' && *s <= 'F')
+ n = n*16 + *s++ - 'A' + 10;
+ else
+ break;
+ }
+ } else
+ while(*s >= '0' && *s <= '7')
+ n = n*8 + *s++ - '0';
+ } else
+ while(*s >= '0' && *s <= '9')
+ n = n*10 + *s++ - '0';
+ if(f)
+ n = -n;
+ return n;
+}
+
+long
+rnd(long v, long r)
+{
+ long c;
+
+ if(r <= 0)
+ return v;
+ v += r - 1;
+ c = v % r;
+ if(c < 0)
+ c += r;
+ v -= c;
+ return v;
+}
+
+void
+import(void)
+{
+ int i;
+ Sym *s;
+
+ for(i = 0; i < NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link)
+ if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){
+ undefsym(s);
+ Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value);
+ }
+}
+
+void
+ckoff(Sym *s, long v)
+{
+ if(v < 0 || v >= 1<<Roffset)
+ diag("relocation offset %ld for %s out of range", v, s->name);
+}
+
+static Prog*
+newdata(Sym *s, int o, int w, int t)
+{
+ Prog *p;
+
+ p = prg();
+ p->link = datap;
+ datap = p;
+ p->as = ADATA;
+ p->reg = w;
+ p->from.type = D_OREG;
+ p->from.name = t;
+ p->from.sym = s;
+ p->from.offset = o;
+ p->to.type = D_CONST;
+ p->to.name = D_NONE;
+ return p;
+}
+
+void
+export(void)
+{
+ int i, j, n, off, nb, sv, ne;
+ Sym *s, *et, *str, **esyms;
+ Prog *p;
+ char buf[NSNAME], *t;
+
+ n = 0;
+ for(i = 0; i < NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link)
+ if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+ n++;
+ esyms = malloc(n*sizeof(Sym*));
+ ne = n;
+ n = 0;
+ for(i = 0; i < NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link)
+ if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+ esyms[n++] = s;
+ for(i = 0; i < ne-1; i++)
+ for(j = i+1; j < ne; j++)
+ if(strcmp(esyms[i]->name, esyms[j]->name) > 0){
+ s = esyms[i];
+ esyms[i] = esyms[j];
+ esyms[j] = s;
+ }
+
+ nb = 0;
+ off = 0;
+ et = lookup(EXPTAB, 0);
+ if(et->type != 0 && et->type != SXREF)
+ diag("%s already defined", EXPTAB);
+ et->type = SDATA;
+ str = lookup(".string", 0);
+ if(str->type == 0)
+ str->type = SDATA;
+ sv = str->value;
+ for(i = 0; i < ne; i++){
+ s = esyms[i];
+ Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type);
+
+ /* signature */
+ p = newdata(et, off, sizeof(long), D_EXTERN);
+ off += sizeof(long);
+ p->to.offset = s->sig;
+
+ /* address */
+ p = newdata(et, off, sizeof(long), D_EXTERN);
+ off += sizeof(long);
+ p->to.name = D_EXTERN;
+ p->to.sym = s;
+
+ /* string */
+ t = s->name;
+ n = strlen(t)+1;
+ for(;;){
+ buf[nb++] = *t;
+ sv++;
+ if(nb >= NSNAME){
+ p = newdata(str, sv-NSNAME, NSNAME, D_STATIC);
+ p->to.type = D_SCONST;
+ p->to.sval = malloc(NSNAME);
+ memmove(p->to.sval, buf, NSNAME);
+ nb = 0;
+ }
+ if(*t++ == 0)
+ break;
+ }
+
+ /* name */
+ p = newdata(et, off, sizeof(long), D_EXTERN);
+ off += sizeof(long);
+ p->to.name = D_STATIC;
+ p->to.sym = str;
+ p->to.offset = sv-n;
+ }
+
+ if(nb > 0){
+ p = newdata(str, sv-nb, nb, D_STATIC);
+ p->to.type = D_SCONST;
+ p->to.sval = malloc(NSNAME);
+ memmove(p->to.sval, buf, nb);
+ }
+
+ for(i = 0; i < 3; i++){
+ newdata(et, off, sizeof(long), D_EXTERN);
+ off += sizeof(long);
+ }
+ et->value = off;
+ if(sv == 0)
+ sv = 1;
+ str->value = sv;
+ exports = ne;
+ free(esyms);
+}
--- /dev/null
+++ b/utils/5l/span.c
@@ -1,0 +1,987 @@
+#include "l.h"
+
+static struct {
+ ulong start;
+ ulong size;
+} pool;
+
+void checkpool(Prog*);
+void flushpool(Prog*, int);
+
+void
+span(void)
+{
+ Prog *p;
+ Sym *setext, *s;
+ Optab *o;
+ int m, bflag, i;
+ long c, otxt, v;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f span\n", cputime());
+ Bflush(&bso);
+
+ bflag = 0;
+ c = INITTEXT;
+ otxt = c;
+ for(p = firstp; p != P; p = p->link) {
+ p->pc = c;
+ o = oplook(p);
+ m = o->size;
+ if(m == 0) {
+ if(p->as == ATEXT) {
+ curtext = p;
+ autosize = p->to.offset + 4;
+ if(p->from.sym != S)
+ p->from.sym->value = c;
+ /* need passes to resolve branches */
+ if(c-otxt >= 1L<<17)
+ bflag = 1;
+ otxt = c;
+ continue;
+ }
+ diag("zero-width instruction\n%P", p);
+ continue;
+ }
+ switch(o->flag & (LFROM|LTO|LPOOL)) {
+ case LFROM:
+ addpool(p, &p->from);
+ break;
+ case LTO:
+ addpool(p, &p->to);
+ break;
+ case LPOOL:
+ if ((p->scond&C_SCOND) == 14)
+ flushpool(p, 0);
+ break;
+ }
+ if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14)
+ flushpool(p, 0);
+ c += m;
+ if(blitrl)
+ checkpool(p);
+ }
+
+ /*
+ * if any procedure is large enough to
+ * generate a large SBRA branch, then
+ * generate extra passes putting branches
+ * around jmps to fix. this is rare.
+ */
+ while(bflag) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f span1\n", cputime());
+ bflag = 0;
+ c = INITTEXT;
+ for(p = firstp; p != P; p = p->link) {
+ p->pc = c;
+ o = oplook(p);
+/* very larg branches
+ if(o->type == 6 && p->cond) {
+ otxt = p->cond->pc - c;
+ if(otxt < 0)
+ otxt = -otxt;
+ if(otxt >= (1L<<17) - 10) {
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ q->as = AB;
+ q->to.type = D_BRANCH;
+ q->cond = p->cond;
+ p->cond = q;
+ q = prg();
+ q->link = p->link;
+ p->link = q;
+ q->as = AB;
+ q->to.type = D_BRANCH;
+ q->cond = q->link->link;
+ bflag = 1;
+ }
+ }
+ */
+ m = o->size;
+ if(m == 0) {
+ if(p->as == ATEXT) {
+ curtext = p;
+ autosize = p->to.offset + 4;
+ if(p->from.sym != S)
+ p->from.sym->value = c;
+ continue;
+ }
+ diag("zero-width instruction\n%P", p);
+ continue;
+ }
+ c += m;
+ }
+ }
+
+ if(debug['t']) {
+ /*
+ * add strings to text segment
+ */
+ c = rnd(c, 8);
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ if(s->type != SSTRING)
+ continue;
+ v = s->value;
+ while(v & 3)
+ v++;
+ s->value = c;
+ c += v;
+ }
+ }
+
+ c = rnd(c, 8);
+
+ setext = lookup("etext", 0);
+ if(setext != S) {
+ setext->value = c;
+ textsize = c - INITTEXT;
+ }
+ if(INITRND)
+ INITDAT = rnd(c, INITRND);
+ if(debug['v'])
+ Bprint(&bso, "tsize = %lux\n", textsize);
+ Bflush(&bso);
+}
+
+/*
+ * when the first reference to the literal pool threatens
+ * to go out of range of a 12-bit PC-relative offset,
+ * drop the pool now, and branch round it.
+ * this happens only in extended basic blocks that exceed 4k.
+ */
+void
+checkpool(Prog *p)
+{
+ if(pool.size >= 0xffc || immaddr((p->pc+4)+4+pool.size - pool.start+8) == 0)
+ flushpool(p, 1);
+ else if(p->link == P)
+ flushpool(p, 2);
+}
+
+void
+flushpool(Prog *p, int skip)
+{
+ Prog *q;
+
+ if(blitrl) {
+ if(skip){
+ if(debug['v'] && skip == 1)
+ print("note: flush literal pool at %lux: len=%lud ref=%lux\n", p->pc+4, pool.size, pool.start);
+ q = prg();
+ q->as = AB;
+ q->to.type = D_BRANCH;
+ q->cond = p->link;
+ q->link = blitrl;
+ blitrl = q;
+ }
+ else if(p->pc+pool.size-pool.start < 2048)
+ return;
+ elitrl->link = p->link;
+ p->link = blitrl;
+ blitrl = 0; /* BUG: should refer back to values until out-of-range */
+ elitrl = 0;
+ pool.size = 0;
+ pool.start = 0;
+ }
+}
+
+void
+addpool(Prog *p, Adr *a)
+{
+ Prog *q, t;
+ int c;
+
+ c = aclass(a);
+
+ t = zprg;
+ t.as = AWORD;
+
+ switch(c) {
+ default:
+ t.to = *a;
+ break;
+
+ case C_SROREG:
+ case C_LOREG:
+ case C_ROREG:
+ case C_FOREG:
+ case C_SOREG:
+ case C_FAUTO:
+ case C_SAUTO:
+ case C_LAUTO:
+ case C_LACON:
+ t.to.type = D_CONST;
+ t.to.offset = instoffset;
+ break;
+ }
+
+ for(q = blitrl; q != P; q = q->link) /* could hash on t.t0.offset */
+ if(memcmp(&q->to, &t.to, sizeof(t.to)) == 0) {
+ p->cond = q;
+ return;
+ }
+
+ q = prg();
+ *q = t;
+ q->pc = pool.size;
+
+ if(blitrl == P) {
+ blitrl = q;
+ pool.start = p->pc;
+ } else
+ elitrl->link = q;
+ elitrl = q;
+ pool.size += 4;
+
+ p->cond = q;
+}
+
+void
+xdefine(char *p, int t, long v)
+{
+ Sym *s;
+
+ s = lookup(p, 0);
+ if(s->type == 0 || s->type == SXREF) {
+ s->type = t;
+ s->value = v;
+ }
+}
+
+long
+regoff(Adr *a)
+{
+
+ instoffset = 0;
+ aclass(a);
+ return instoffset;
+}
+
+long
+immrot(ulong v)
+{
+ int i;
+
+ for(i=0; i<16; i++) {
+ if((v & ~0xff) == 0)
+ return (i<<8) | v | (1<<25);
+ v = (v<<2) | (v>>30);
+ }
+ return 0;
+}
+
+long
+immaddr(long v)
+{
+ if(v >= 0 && v <= 0xfff)
+ return (v & 0xfff) |
+ (1<<24) | /* pre indexing */
+ (1<<23); /* pre indexing, up */
+ if(v >= -0xfff && v < 0)
+ return (-v & 0xfff) |
+ (1<<24); /* pre indexing */
+ return 0;
+}
+
+int
+immfloat(long v)
+{
+ return (v & 0xC03) == 0; /* offset will fit in floating-point load/store */
+}
+
+int
+immhalf(long v)
+{
+ if(v >= 0 && v <= 0xff)
+ return v|
+ (1<<24)| /* pre indexing */
+ (1<<23); /* pre indexing, up */
+ if(v >= -0xff && v < 0)
+ return (-v & 0xff)|
+ (1<<24); /* pre indexing */
+ return 0;
+}
+
+int
+aclass(Adr *a)
+{
+ Sym *s;
+ int t;
+
+ switch(a->type) {
+ case D_NONE:
+ return C_NONE;
+
+ case D_REG:
+ return C_REG;
+
+ case D_REGREG:
+ return C_REGREG;
+
+ case D_SHIFT:
+ return C_SHIFT;
+
+ case D_FREG:
+ return C_FREG;
+
+ case D_FPCR:
+ return C_FCR;
+
+ case D_OREG:
+ switch(a->name) {
+ case D_EXTERN:
+ case D_STATIC:
+ if(a->sym == 0 || a->sym->name == 0) {
+ print("null sym external\n");
+ print("%D\n", a);
+ return C_GOK;
+ }
+ s = a->sym;
+ t = s->type;
+ if(t == 0 || t == SXREF) {
+ diag("undefined external: %s in %s",
+ s->name, TNAME);
+ s->type = SDATA;
+ }
+ if(dlm) {
+ switch(t) {
+ default:
+ instoffset = s->value + a->offset + INITDAT;
+ break;
+ case SUNDEF:
+ case STEXT:
+ case SCONST:
+ case SLEAF:
+ case SSTRING:
+ instoffset = s->value + a->offset;
+ break;
+ }
+ return C_ADDR;
+ }
+ instoffset = s->value + a->offset - BIG;
+ t = immaddr(instoffset);
+ if(t) {
+ if(immhalf(instoffset))
+ return immfloat(t) ? C_HFEXT : C_HEXT;
+ if(immfloat(t))
+ return C_FEXT;
+ return C_SEXT;
+ }
+ return C_LEXT;
+ case D_AUTO:
+ instoffset = autosize + a->offset;
+ t = immaddr(instoffset);
+ if(t){
+ if(immhalf(instoffset))
+ return immfloat(t) ? C_HFAUTO : C_HAUTO;
+ if(immfloat(t))
+ return C_FAUTO;
+ return C_SAUTO;
+ }
+ return C_LAUTO;
+
+ case D_PARAM:
+ instoffset = autosize + a->offset + 4L;
+ t = immaddr(instoffset);
+ if(t){
+ if(immhalf(instoffset))
+ return immfloat(t) ? C_HFAUTO : C_HAUTO;
+ if(immfloat(t))
+ return C_FAUTO;
+ return C_SAUTO;
+ }
+ return C_LAUTO;
+ case D_NONE:
+ instoffset = a->offset;
+ t = immaddr(instoffset);
+ if(t) {
+ if(immhalf(instoffset)) /* n.b. that it will also satisfy immrot */
+ return immfloat(t) ? C_HFOREG : C_HOREG;
+ if(immfloat(t))
+ return C_FOREG; /* n.b. that it will also satisfy immrot */
+ t = immrot(instoffset);
+ if(t)
+ return C_SROREG;
+ if(immhalf(instoffset))
+ return C_HOREG;
+ return C_SOREG;
+ }
+ t = immrot(instoffset);
+ if(t)
+ return C_ROREG;
+ return C_LOREG;
+ }
+ return C_GOK;
+
+ case D_PSR:
+ return C_PSR;
+
+ case D_OCONST:
+ switch(a->name) {
+ case D_EXTERN:
+ case D_STATIC:
+ s = a->sym;
+ t = s->type;
+ if(t == 0 || t == SXREF) {
+ diag("undefined external: %s in %s",
+ s->name, TNAME);
+ s->type = SDATA;
+ }
+ instoffset = s->value + a->offset + INITDAT;
+ if(s->type == STEXT || s->type == SLEAF || s->type == SUNDEF)
+ instoffset = s->value + a->offset;
+ return C_LCON;
+ }
+ return C_GOK;
+
+ case D_FCONST:
+ return C_FCON;
+
+ case D_CONST:
+ switch(a->name) {
+
+ case D_NONE:
+ instoffset = a->offset;
+ if(a->reg != NREG)
+ goto aconsize;
+
+ t = immrot(instoffset);
+ if(t)
+ return C_RCON;
+ t = immrot(~instoffset);
+ if(t)
+ return C_NCON;
+ return C_LCON;
+
+ case D_EXTERN:
+ case D_STATIC:
+ s = a->sym;
+ if(s == S)
+ break;
+ t = s->type;
+ switch(t) {
+ case 0:
+ case SXREF:
+ diag("undefined external: %s in %s",
+ s->name, TNAME);
+ s->type = SDATA;
+ break;
+ case SUNDEF:
+ case STEXT:
+ case SSTRING:
+ case SCONST:
+ case SLEAF:
+ instoffset = s->value + a->offset;
+ return C_LCON;
+ }
+ if(!dlm) {
+ instoffset = s->value + a->offset - BIG;
+ t = immrot(instoffset);
+ if(t && instoffset != 0)
+ return C_RECON;
+ }
+ instoffset = s->value + a->offset + INITDAT;
+ return C_LCON;
+
+ case D_AUTO:
+ instoffset = autosize + a->offset;
+ goto aconsize;
+
+ case D_PARAM:
+ instoffset = autosize + a->offset + 4L;
+ aconsize:
+ t = immrot(instoffset);
+ if(t)
+ return C_RACON;
+ return C_LACON;
+ }
+ return C_GOK;
+
+ case D_BRANCH:
+ return C_SBRA;
+ }
+ return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+ int a1, a2, a3, r;
+ char *c1, *c3;
+ Optab *o, *e;
+
+ a1 = p->optab;
+ if(a1)
+ return optab+(a1-1);
+ a1 = p->from.class;
+ if(a1 == 0) {
+ a1 = aclass(&p->from) + 1;
+ p->from.class = a1;
+ }
+ a1--;
+ a3 = p->to.class;
+ if(a3 == 0) {
+ a3 = aclass(&p->to) + 1;
+ p->to.class = a3;
+ }
+ a3--;
+ a2 = C_NONE;
+ if(p->reg != NREG)
+ a2 = C_REG;
+ r = p->as;
+ o = oprange[r].start;
+ if(o == 0) {
+ a1 = opcross[repop[r]][a1][a2][a3];
+ if(a1) {
+ p->optab = a1+1;
+ return optab+a1;
+ }
+ o = oprange[r].stop; /* just generate an error */
+ }
+ if(0) {
+ print("oplook %A %d %d %d\n",
+ (int)p->as, a1, a2, a3);
+ print(" %d %d\n", p->from.type, p->to.type);
+ }
+ e = oprange[r].stop;
+ c1 = xcmp[a1];
+ c3 = xcmp[a3];
+ for(; o<e; o++)
+ if(o->a2 == a2)
+ if(c1[o->a1])
+ if(c3[o->a3]) {
+ p->optab = (o-optab)+1;
+ return o;
+ }
+ diag("illegal combination %A %d %d %d",
+ p->as, a1, a2, a3);
+ prasm(p);
+ if(o == 0)
+ o = optab;
+ return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+ if(a == b)
+ return 1;
+ switch(a) {
+ case C_LCON:
+ if(b == C_RCON || b == C_NCON)
+ return 1;
+ break;
+ case C_LACON:
+ if(b == C_RACON)
+ return 1;
+ break;
+ case C_LECON:
+ if(b == C_RECON)
+ return 1;
+ break;
+
+ case C_HFEXT:
+ return b == C_HEXT || b == C_FEXT;
+ case C_FEXT:
+ case C_HEXT:
+ return b == C_HFEXT;
+ case C_SEXT:
+ return cmp(C_HFEXT, b);
+ case C_LEXT:
+ return cmp(C_SEXT, b);
+
+ case C_HFAUTO:
+ return b == C_HAUTO || b == C_FAUTO;
+ case C_FAUTO:
+ case C_HAUTO:
+ return b == C_HFAUTO;
+ case C_SAUTO:
+ return cmp(C_HFAUTO, b);
+ case C_LAUTO:
+ return cmp(C_SAUTO, b);
+
+ case C_HFOREG:
+ return b == C_HOREG || b == C_FOREG;
+ case C_FOREG:
+ case C_HOREG:
+ return b == C_HFOREG;
+ case C_SROREG:
+ return cmp(C_SOREG, b) || cmp(C_ROREG, b);
+ case C_SOREG:
+ case C_ROREG:
+ return b == C_SROREG || cmp(C_HFOREG, b);
+ case C_LOREG:
+ return cmp(C_SROREG, b);
+
+ case C_LBRA:
+ if(b == C_SBRA)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+int
+ocmp(void *a1, void *a2)
+{
+ Optab *p1, *p2;
+ int n;
+
+ p1 = (Optab*)a1;
+ p2 = (Optab*)a2;
+ n = p1->as - p2->as;
+ if(n)
+ return n;
+ n = (p2->flag&V4) - (p1->flag&V4); /* architecture version */
+ if(n)
+ return n;
+ n = (p2->flag&VFP) - (p1->flag&VFP); /* floating point arch */
+ if(n)
+ return n;
+ n = p1->a1 - p2->a1;
+ if(n)
+ return n;
+ n = p1->a2 - p2->a2;
+ if(n)
+ return n;
+ n = p1->a3 - p2->a3;
+ if(n)
+ return n;
+ return 0;
+}
+
+void
+buildop(void)
+{
+ int i, n, r;
+
+ armv4 = !debug['h'];
+ vfp = debug['f'];
+ for(i=0; i<C_GOK; i++)
+ for(n=0; n<C_GOK; n++)
+ xcmp[i][n] = cmp(n, i);
+ for(n=0; optab[n].as != AXXX; n++) {
+ if((optab[n].flag & VFP) && !vfp)
+ optab[n].as = AXXX;
+ if((optab[n].flag & V4) && !armv4) {
+ optab[n].as = AXXX;
+ break;
+ }
+ }
+ qsort(optab, n, sizeof(optab[0]), ocmp);
+ for(i=0; i<n; i++) {
+ r = optab[i].as;
+ oprange[r].start = optab+i;
+ while(optab[i].as == r)
+ i++;
+ oprange[r].stop = optab+i;
+ i--;
+
+ switch(r)
+ {
+ default:
+ diag("unknown op in build: %A", r);
+ errorexit();
+ case AXXX:
+ break;
+ case AADD:
+ oprange[AAND] = oprange[r];
+ oprange[AEOR] = oprange[r];
+ oprange[ASUB] = oprange[r];
+ oprange[ARSB] = oprange[r];
+ oprange[AADC] = oprange[r];
+ oprange[ASBC] = oprange[r];
+ oprange[ARSC] = oprange[r];
+ oprange[AORR] = oprange[r];
+ oprange[ABIC] = oprange[r];
+ break;
+ case ACMP:
+ oprange[ATST] = oprange[r];
+ oprange[ATEQ] = oprange[r];
+ oprange[ACMN] = oprange[r];
+ break;
+ case AMVN:
+ break;
+ case ABEQ:
+ oprange[ABNE] = oprange[r];
+ oprange[ABCS] = oprange[r];
+ oprange[ABHS] = oprange[r];
+ oprange[ABCC] = oprange[r];
+ oprange[ABLO] = oprange[r];
+ oprange[ABMI] = oprange[r];
+ oprange[ABPL] = oprange[r];
+ oprange[ABVS] = oprange[r];
+ oprange[ABVC] = oprange[r];
+ oprange[ABHI] = oprange[r];
+ oprange[ABLS] = oprange[r];
+ oprange[ABGE] = oprange[r];
+ oprange[ABLT] = oprange[r];
+ oprange[ABGT] = oprange[r];
+ oprange[ABLE] = oprange[r];
+ break;
+ case ASLL:
+ oprange[ASRL] = oprange[r];
+ oprange[ASRA] = oprange[r];
+ break;
+ case AMUL:
+ oprange[AMULU] = oprange[r];
+ break;
+ case ADIV:
+ oprange[AMOD] = oprange[r];
+ oprange[AMODU] = oprange[r];
+ oprange[ADIVU] = oprange[r];
+ break;
+ case AMOVW:
+ case AMOVB:
+ case AMOVBU:
+ case AMOVH:
+ case AMOVHU:
+ break;
+ case ASWPW:
+ oprange[ASWPBU] = oprange[r];
+ break;
+ case AB:
+ case ABL:
+ case ABX:
+ case ABXRET:
+ case ASWI:
+ case AWORD:
+ case AMOVM:
+ case ARFE:
+ case ATEXT:
+ case ACASE:
+ case ABCASE:
+ break;
+ case AADDF:
+ oprange[AADDD] = oprange[r];
+ oprange[ASUBF] = oprange[r];
+ oprange[ASUBD] = oprange[r];
+ oprange[AMULF] = oprange[r];
+ oprange[AMULD] = oprange[r];
+ oprange[ADIVF] = oprange[r];
+ oprange[ADIVD] = oprange[r];
+ oprange[AMOVFD] = oprange[r];
+ oprange[AMOVDF] = oprange[r];
+ break;
+
+ case ACMPF:
+ oprange[ACMPD] = oprange[r];
+ break;
+
+ case AMOVF:
+ oprange[AMOVD] = oprange[r];
+ break;
+
+ case AMOVFW:
+ oprange[AMOVWF] = oprange[r];
+ oprange[AMOVWD] = oprange[r];
+ oprange[AMOVDW] = oprange[r];
+ break;
+
+ case AMULL:
+ oprange[AMULA] = oprange[r];
+ oprange[AMULAL] = oprange[r];
+ oprange[AMULLU] = oprange[r];
+ oprange[AMULALU] = oprange[r];
+ break;
+ }
+ }
+}
+
+/*
+void
+buildrep(int x, int as)
+{
+ Opcross *p;
+ Optab *e, *s, *o;
+ int a1, a2, a3, n;
+
+ if(C_NONE != 0 || C_REG != 1 || C_GOK >= 32 || x >= nelem(opcross)) {
+ diag("assumptions fail in buildrep");
+ errorexit();
+ }
+ repop[as] = x;
+ p = (opcross + x);
+ s = oprange[as].start;
+ e = oprange[as].stop;
+ for(o=e-1; o>=s; o--) {
+ n = o-optab;
+ for(a2=0; a2<2; a2++) {
+ if(a2) {
+ if(o->a2 == C_NONE)
+ continue;
+ } else
+ if(o->a2 != C_NONE)
+ continue;
+ for(a1=0; a1<32; a1++) {
+ if(!xcmp[a1][o->a1])
+ continue;
+ for(a3=0; a3<32; a3++)
+ if(xcmp[a3][o->a3])
+ (*p)[a1][a2][a3] = n;
+ }
+ }
+ }
+ oprange[as].start = 0;
+}
+*/
+
+enum{
+ ABSD = 0,
+ ABSU = 1,
+ RELD = 2,
+ RELU = 3,
+};
+
+int modemap[4] = { 0, 1, -1, 2, };
+
+typedef struct Reloc Reloc;
+
+struct Reloc
+{
+ int n;
+ int t;
+ uchar *m;
+ ulong *a;
+};
+
+Reloc rels;
+
+static void
+grow(Reloc *r)
+{
+ int t;
+ uchar *m, *nm;
+ ulong *a, *na;
+
+ t = r->t;
+ r->t += 64;
+ m = r->m;
+ a = r->a;
+ r->m = nm = malloc(r->t*sizeof(uchar));
+ r->a = na = malloc(r->t*sizeof(ulong));
+ memmove(nm, m, t*sizeof(uchar));
+ memmove(na, a, t*sizeof(ulong));
+ free(m);
+ free(a);
+}
+
+void
+dynreloc(Sym *s, long v, int abs)
+{
+ int i, k, n;
+ uchar *m;
+ ulong *a;
+ Reloc *r;
+
+ if(v&3)
+ diag("bad relocation address");
+ v >>= 2;
+ if(s != S && s->type == SUNDEF)
+ k = abs ? ABSU : RELU;
+ else
+ k = abs ? ABSD : RELD;
+ /* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, a, a, k); */
+ k = modemap[k];
+ r = &rels;
+ n = r->n;
+ if(n >= r->t)
+ grow(r);
+ m = r->m;
+ a = r->a;
+ for(i = n; i > 0; i--){
+ if(v < a[i-1]){ /* happens occasionally for data */
+ m[i] = m[i-1];
+ a[i] = a[i-1];
+ }
+ else
+ break;
+ }
+ m[i] = k;
+ a[i] = v;
+ r->n++;
+}
+
+static int
+sput(char *s)
+{
+ char *p;
+
+ p = s;
+ while(*s)
+ cput(*s++);
+ cput(0);
+ return s-p+1;
+}
+
+void
+asmdyn()
+{
+ int i, n, t, c;
+ Sym *s;
+ ulong la, ra, *a;
+ vlong off;
+ uchar *m;
+ Reloc *r;
+
+ cflush();
+ off = seek(cout, 0, 1);
+ lput(0);
+ t = 0;
+ lput(imports);
+ t += 4;
+ for(i = 0; i < NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link)
+ if(s->type == SUNDEF){
+ lput(s->sig);
+ t += 4;
+ t += sput(s->name);
+ }
+
+ la = 0;
+ r = &rels;
+ n = r->n;
+ m = r->m;
+ a = r->a;
+ lput(n);
+ t += 4;
+ for(i = 0; i < n; i++){
+ ra = *a-la;
+ if(*a < la)
+ diag("bad relocation order");
+ if(ra < 256)
+ c = 0;
+ else if(ra < 65536)
+ c = 1;
+ else
+ c = 2;
+ cput((c<<6)|*m++);
+ t++;
+ if(c == 0){
+ cput(ra);
+ t++;
+ }
+ else if(c == 1){
+ wput(ra);
+ t += 2;
+ }
+ else{
+ lput(ra);
+ t += 4;
+ }
+ la = *a++;
+ }
+
+ cflush();
+ seek(cout, off, 0);
+ lput(t);
+
+ if(debug['v']){
+ Bprint(&bso, "import table entries = %d\n", imports);
+ Bprint(&bso, "export table entries = %d\n", exports);
+ }
+}
--- /dev/null
+++ b/utils/6a/a.h
@@ -1,0 +1,194 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../6c/6.out.h"
+
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+typedef struct Sym Sym;
+typedef struct Ref Ref;
+typedef struct Gen Gen;
+typedef struct Io Io;
+typedef struct Hist Hist;
+typedef struct Gen2 Gen2;
+
+#define MAXALIGN 7
+#define FPCHIP 1
+#define NSYMB 500
+#define BUFSIZ 8192
+#define HISTSZ 20
+#define NINCLUDE 10
+#define NHUNK 10000
+#define EOF (-1)
+#define IGN (-2)
+#define GETC() ((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define NHASH 503
+#define STRINGSZ 200
+#define NMACRO 10
+
+struct Sym
+{
+ Sym* link;
+ Ref* ref;
+ char* macro;
+ vlong value;
+ ushort type;
+ char *name;
+ char sym;
+};
+#define S ((Sym*)0)
+
+struct Ref
+{
+ int class;
+};
+
+EXTERN struct
+{
+ char* p;
+ int c;
+} fi;
+
+struct Io
+{
+ Io* link;
+ char b[BUFSIZ];
+ char* p;
+ short c;
+ short f;
+};
+#define I ((Io*)0)
+
+EXTERN struct
+{
+ Sym* sym;
+ short type;
+} h[NSYM];
+
+struct Gen
+{
+ double dval;
+ char sval[8];
+ vlong offset;
+ Sym* sym;
+ short type;
+ short index;
+ short scale;
+};
+struct Gen2
+{
+ Gen from;
+ Gen to;
+};
+
+struct Hist
+{
+ Hist* link;
+ char* name;
+ long line;
+ vlong offset;
+};
+#define H ((Hist*)0)
+
+enum
+{
+ CLAST,
+ CMACARG,
+ CMACRO,
+ CPREPROC,
+};
+
+
+EXTERN char debug[256];
+EXTERN Sym* hash[NHASH];
+EXTERN char* Dlist[30];
+EXTERN int nDlist;
+EXTERN Hist* ehist;
+EXTERN int newflag;
+EXTERN Hist* hist;
+EXTERN char* hunk;
+EXTERN char* include[NINCLUDE];
+EXTERN Io* iofree;
+EXTERN Io* ionext;
+EXTERN Io* iostack;
+EXTERN long lineno;
+EXTERN int nerrors;
+EXTERN long nhunk;
+EXTERN int ninclude;
+EXTERN Gen nullgen;
+EXTERN char* outfile;
+EXTERN int pass;
+EXTERN char* pathname;
+EXTERN long pc;
+EXTERN int peekc;
+EXTERN int sym;
+EXTERN char symb[NSYMB];
+EXTERN int thechar;
+EXTERN char* thestring;
+EXTERN long thunk;
+EXTERN Biobuf obuf;
+
+void* allocn(void*, long, long);
+void errorexit(void);
+void pushio(void);
+void newio(void);
+void newfile(char*, int);
+Sym* slookup(char*);
+Sym* lookup(void);
+void syminit(Sym*);
+long yylex(void);
+int getc(void);
+int getnsc(void);
+void unget(int);
+int escchar(int);
+void cinit(void);
+void checkscale(int);
+void pinit(char*);
+void cclean(void);
+int isreg(Gen*);
+void outcode(int, Gen2*);
+void outhist(void);
+void zaddr(Gen*, int);
+void zname(char*, int, int);
+void ieeedtod(Ieee*, double);
+int filbuf(void);
+Sym* getsym(void);
+void domacro(void);
+void macund(void);
+void macdef(void);
+void macexpand(Sym*, char*);
+void macinc(void);
+void macprag(void);
+void maclin(void);
+void macif(int);
+void macend(void);
+void dodefine(char*);
+void prfile(long);
+void linehist(char*, int);
+void gethunk(void);
+void yyerror(char*, ...);
+int yyparse(void);
+void setinclude(char*);
+int assemble(char*);
+
+/*
+ * Posix.c/Inferno.c/Nt.c
+ */
+enum /* keep in synch with ../cc/cc.h */
+{
+ Plan9 = 1<<0,
+ Unix = 1<<1,
+ Windows = 1<<2
+};
+int mywait(int*);
+int mycreat(char*, int);
+int systemtype(int);
+int pathchar(void);
+char* mygetwd(char*, int);
+int myexec(char*, char*[]);
+int mydup(int, int);
+int myfork(void);
+int mypipe(int*);
+void* mysbrk(ulong);
--- /dev/null
+++ b/utils/6a/a.y
@@ -1,0 +1,562 @@
+%{
+#include "a.h"
+%}
+%union {
+ Sym *sym;
+ vlong lval;
+ double dval;
+ char sval[8];
+ Gen gen;
+ Gen2 gen2;
+}
+%left '|'
+%left '^'
+%left '&'
+%left '<' '>'
+%left '+' '-'
+%left '*' '/' '%'
+%token <lval> LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4
+%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEXC LTYPEX LTYPERT
+%token <lval> LCONST LFP LPC LSB
+%token <lval> LBREG LLREG LSREG LFREG LMREG LXREG
+%token <dval> LFCONST
+%token <sval> LSCONST LSP
+%token <sym> LNAME LLAB LVAR
+%type <lval> con expr pointer offset
+%type <gen> mem imm reg nam rel rem rim rom omem nmem
+%type <gen2> nonnon nonrel nonrem rimnon rimrem remrim spec10
+%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9
+%%
+prog:
+| prog line
+
+line:
+ LLAB ':'
+ {
+ if($1->value != pc)
+ yyerror("redeclaration of %s", $1->name);
+ $1->value = pc;
+ }
+ line
+| LNAME ':'
+ {
+ $1->type = LLAB;
+ $1->value = pc;
+ }
+ line
+| ';'
+| inst ';'
+| error ';'
+
+inst:
+ LNAME '=' expr
+ {
+ $1->type = LVAR;
+ $1->value = $3;
+ }
+| LVAR '=' expr
+ {
+ if($1->value != $3)
+ yyerror("redeclaration of %s", $1->name);
+ $1->value = $3;
+ }
+| LTYPE0 nonnon { outcode($1, &$2); }
+| LTYPE1 nonrem { outcode($1, &$2); }
+| LTYPE2 rimnon { outcode($1, &$2); }
+| LTYPE3 rimrem { outcode($1, &$2); }
+| LTYPE4 remrim { outcode($1, &$2); }
+| LTYPER nonrel { outcode($1, &$2); }
+| LTYPED spec1 { outcode($1, &$2); }
+| LTYPET spec2 { outcode($1, &$2); }
+| LTYPEC spec3 { outcode($1, &$2); }
+| LTYPEN spec4 { outcode($1, &$2); }
+| LTYPES spec5 { outcode($1, &$2); }
+| LTYPEM spec6 { outcode($1, &$2); }
+| LTYPEI spec7 { outcode($1, &$2); }
+| LTYPEXC spec8 { outcode($1, &$2); }
+| LTYPEX spec9 { outcode($1, &$2); }
+| LTYPERT spec10 { outcode($1, &$2); }
+
+nonnon:
+ {
+ $$.from = nullgen;
+ $$.to = nullgen;
+ }
+| ','
+ {
+ $$.from = nullgen;
+ $$.to = nullgen;
+ }
+
+rimrem:
+ rim ',' rem
+ {
+ $$.from = $1;
+ $$.to = $3;
+ }
+
+remrim:
+ rem ',' rim
+ {
+ $$.from = $1;
+ $$.to = $3;
+ }
+
+rimnon:
+ rim ','
+ {
+ $$.from = $1;
+ $$.to = nullgen;
+ }
+| rim
+ {
+ $$.from = $1;
+ $$.to = nullgen;
+ }
+
+nonrem:
+ ',' rem
+ {
+ $$.from = nullgen;
+ $$.to = $2;
+ }
+| rem
+ {
+ $$.from = nullgen;
+ $$.to = $1;
+ }
+
+nonrel:
+ ',' rel
+ {
+ $$.from = nullgen;
+ $$.to = $2;
+ }
+| rel
+ {
+ $$.from = nullgen;
+ $$.to = $1;
+ }
+
+spec1: /* DATA */
+ nam '/' con ',' imm
+ {
+ $$.from = $1;
+ $$.from.scale = $3;
+ $$.to = $5;
+ }
+
+spec2: /* TEXT */
+ mem ',' imm
+ {
+ $$.from = $1;
+ $$.to = $3;
+ }
+| mem ',' con ',' imm
+ {
+ $$.from = $1;
+ $$.from.scale = $3;
+ $$.to = $5;
+ }
+
+spec3: /* JMP/CALL */
+ ',' rom
+ {
+ $$.from = nullgen;
+ $$.to = $2;
+ }
+| rom
+ {
+ $$.from = nullgen;
+ $$.to = $1;
+ }
+
+spec4: /* NOP */
+ nonnon
+| nonrem
+
+spec5: /* SHL/SHR */
+ rim ',' rem
+ {
+ $$.from = $1;
+ $$.to = $3;
+ }
+| rim ',' rem ':' LLREG
+ {
+ $$.from = $1;
+ $$.to = $3;
+ if($$.from.index != D_NONE)
+ yyerror("dp shift with lhs index");
+ $$.from.index = $5;
+ }
+
+spec6: /* MOVW/MOVL */
+ rim ',' rem
+ {
+ $$.from = $1;
+ $$.to = $3;
+ }
+| rim ',' rem ':' LSREG
+ {
+ $$.from = $1;
+ $$.to = $3;
+ if($$.to.index != D_NONE)
+ yyerror("dp move with lhs index");
+ $$.to.index = $5;
+ }
+
+spec7:
+ rim ','
+ {
+ $$.from = $1;
+ $$.to = nullgen;
+ }
+| rim
+ {
+ $$.from = $1;
+ $$.to = nullgen;
+ }
+| rim ',' rem
+ {
+ $$.from = $1;
+ $$.to = $3;
+ }
+
+spec8: /* CMPPS/CMPPD */
+ reg ',' rem ',' con
+ {
+ $$.from = $1;
+ $$.to = $3;
+ $$.from.offset = $5;
+ }
+
+spec9: /* shufl */
+ imm ',' rem ',' reg
+ {
+ $$.from = $3;
+ $$.to = $5;
+ if($1.type != D_CONST)
+ yyerror("illegal constant");
+ $$.to.offset = $1.offset;
+ }
+
+spec10: /* RET/RETF */
+ {
+ $$.from = nullgen;
+ $$.to = nullgen;
+ }
+| imm
+ {
+ $$.from = $1;
+ $$.to = nullgen;
+ }
+
+rem:
+ reg
+| mem
+
+rom:
+ rel
+| nmem
+| '*' reg
+ {
+ $$ = $2;
+ }
+| '*' omem
+ {
+ $$ = $2;
+ }
+| reg
+| omem
+
+rim:
+ rem
+| imm
+
+rel:
+ con '(' LPC ')'
+ {
+ $$ = nullgen;
+ $$.type = D_BRANCH;
+ $$.offset = $1 + pc;
+ }
+| LNAME offset
+ {
+ $$ = nullgen;
+ if(pass == 2)
+ yyerror("undefined label: %s", $1->name);
+ $$.type = D_BRANCH;
+ $$.sym = $1;
+ $$.offset = $2;
+ }
+| LLAB offset
+ {
+ $$ = nullgen;
+ $$.type = D_BRANCH;
+ $$.sym = $1;
+ $$.offset = $1->value + $2;
+ }
+
+reg:
+ LBREG
+ {
+ $$ = nullgen;
+ $$.type = $1;
+ }
+| LFREG
+ {
+ $$ = nullgen;
+ $$.type = $1;
+ }
+| LLREG
+ {
+ $$ = nullgen;
+ $$.type = $1;
+ }
+| LMREG
+ {
+ $$ = nullgen;
+ $$.type = $1;
+ }
+| LSP
+ {
+ $$ = nullgen;
+ $$.type = D_SP;
+ }
+| LSREG
+ {
+ $$ = nullgen;
+ $$.type = $1;
+ }
+| LXREG
+ {
+ $$ = nullgen;
+ $$.type = $1;
+ }
+
+imm:
+ '$' con
+ {
+ $$ = nullgen;
+ $$.type = D_CONST;
+ $$.offset = $2;
+ }
+| '$' nam
+ {
+ $$ = $2;
+ $$.index = $2.type;
+ $$.type = D_ADDR;
+ /*
+ if($2.type == D_AUTO || $2.type == D_PARAM)
+ yyerror("constant cannot be automatic: %s",
+ $2.sym->name);
+ */
+ }
+| '$' LSCONST
+ {
+ $$ = nullgen;
+ $$.type = D_SCONST;
+ memcpy($$.sval, $2, sizeof($$.sval));
+ }
+| '$' LFCONST
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = $2;
+ }
+| '$' '(' LFCONST ')'
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = $3;
+ }
+| '$' '-' LFCONST
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = -$3;
+ }
+
+mem:
+ omem
+| nmem
+
+omem:
+ con
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+D_NONE;
+ $$.offset = $1;
+ }
+| con '(' LLREG ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+$3;
+ $$.offset = $1;
+ }
+| con '(' LSP ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+D_SP;
+ $$.offset = $1;
+ }
+| con '(' LLREG '*' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+D_NONE;
+ $$.offset = $1;
+ $$.index = $3;
+ $$.scale = $5;
+ checkscale($$.scale);
+ }
+| con '(' LLREG ')' '(' LLREG '*' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+$3;
+ $$.offset = $1;
+ $$.index = $6;
+ $$.scale = $8;
+ checkscale($$.scale);
+ }
+| '(' LLREG ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+$2;
+ }
+| '(' LSP ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+D_SP;
+ }
+| '(' LLREG '*' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+D_NONE;
+ $$.index = $2;
+ $$.scale = $4;
+ checkscale($$.scale);
+ }
+| '(' LLREG ')' '(' LLREG '*' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+$2;
+ $$.index = $5;
+ $$.scale = $7;
+ checkscale($$.scale);
+ }
+
+nmem:
+ nam
+ {
+ $$ = $1;
+ }
+| nam '(' LLREG '*' con ')'
+ {
+ $$ = $1;
+ $$.index = $3;
+ $$.scale = $5;
+ checkscale($$.scale);
+ }
+
+nam:
+ LNAME offset '(' pointer ')'
+ {
+ $$ = nullgen;
+ $$.type = $4;
+ $$.sym = $1;
+ $$.offset = $2;
+ }
+| LNAME '<' '>' offset '(' LSB ')'
+ {
+ $$ = nullgen;
+ $$.type = D_STATIC;
+ $$.sym = $1;
+ $$.offset = $4;
+ }
+
+offset:
+ {
+ $$ = 0;
+ }
+| '+' con
+ {
+ $$ = $2;
+ }
+| '-' con
+ {
+ $$ = -$2;
+ }
+
+pointer:
+ LSB
+| LSP
+ {
+ $$ = D_AUTO;
+ }
+| LFP
+
+con:
+ LCONST
+| LVAR
+ {
+ $$ = $1->value;
+ }
+| '-' con
+ {
+ $$ = -$2;
+ }
+| '+' con
+ {
+ $$ = $2;
+ }
+| '~' con
+ {
+ $$ = ~$2;
+ }
+| '(' expr ')'
+ {
+ $$ = $2;
+ }
+
+expr:
+ con
+| expr '+' expr
+ {
+ $$ = $1 + $3;
+ }
+| expr '-' expr
+ {
+ $$ = $1 - $3;
+ }
+| expr '*' expr
+ {
+ $$ = $1 * $3;
+ }
+| expr '/' expr
+ {
+ $$ = $1 / $3;
+ }
+| expr '%' expr
+ {
+ $$ = $1 % $3;
+ }
+| expr '<' '<' expr
+ {
+ $$ = $1 << $4;
+ }
+| expr '>' '>' expr
+ {
+ $$ = $1 >> $4;
+ }
+| expr '&' expr
+ {
+ $$ = $1 & $3;
+ }
+| expr '^' expr
+ {
+ $$ = $1 ^ $3;
+ }
+| expr '|' expr
+ {
+ $$ = $1 | $3;
+ }
--- /dev/null
+++ b/utils/6a/lex.c
@@ -1,0 +1,1288 @@
+#define EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+ char *p;
+ int nout, nproc, status, i, c;
+
+ thechar = '6';
+ thestring = "amd64";
+ memset(debug, 0, sizeof(debug));
+ cinit();
+ outfile = 0;
+ include[ninclude++] = ".";
+ ARGBEGIN {
+ default:
+ c = ARGC();
+ if(c >= 0 || c < sizeof(debug))
+ debug[c] = 1;
+ break;
+
+ case 'o':
+ outfile = ARGF();
+ break;
+
+ case 'D':
+ p = ARGF();
+ if(p)
+ Dlist[nDlist++] = p;
+ break;
+
+ case 'I':
+ p = ARGF();
+ setinclude(p);
+ break;
+ } ARGEND
+ if(*argv == 0) {
+ print("usage: %ca [-options] file.s\n", thechar);
+ errorexit();
+ }
+ if(argc > 1 && systemtype(Windows)){
+ print("can't assemble multiple files on windows\n");
+ errorexit();
+ }
+ if(argc > 1 && !systemtype(Windows)) {
+ nproc = 1;
+ if(p = getenv("NPROC"))
+ nproc = atol(p); /* */
+ c = 0;
+ nout = 0;
+ for(;;) {
+ while(nout < nproc && argc > 0) {
+ i = myfork();
+ if(i < 0) {
+ i = mywait(&status);
+ if(i < 0)
+ errorexit();
+ if(status)
+ c++;
+ nout--;
+ continue;
+ }
+ if(i == 0) {
+ print("%s:\n", *argv);
+ if(assemble(*argv))
+ errorexit();
+ exits(0);
+ }
+ nout++;
+ argc--;
+ argv++;
+ }
+ i = mywait(&status);
+ if(i < 0) {
+ if(c)
+ errorexit();
+ exits(0);
+ }
+ if(status)
+ c++;
+ nout--;
+ }
+ }
+ if(assemble(argv[0]))
+ errorexit();
+ exits(0);
+}
+
+int
+assemble(char *file)
+{
+ char ofile[100], incfile[20], *p;
+ int i, of;
+
+ strcpy(ofile, file);
+ p = utfrrune(ofile, pathchar());
+ if(p) {
+ include[0] = ofile;
+ *p++ = 0;
+ } else
+ p = ofile;
+ if(outfile == 0) {
+ outfile = p;
+ if(outfile){
+ p = utfrrune(outfile, '.');
+ if(p)
+ if(p[1] == 's' && p[2] == 0)
+ p[0] = 0;
+ p = utfrune(outfile, 0);
+ p[0] = '.';
+ p[1] = thechar;
+ p[2] = 0;
+ } else
+ outfile = "/dev/null";
+ }
+ p = getenv("INCLUDE");
+ if(p) {
+ setinclude(p);
+ } else {
+ if(systemtype(Plan9)) {
+ sprint(incfile,"/%s/include", thestring);
+ setinclude(strdup(incfile));
+ }
+ }
+
+ of = mycreat(outfile, 0664);
+ if(of < 0) {
+ yyerror("%ca: cannot create %s", thechar, outfile);
+ errorexit();
+ }
+ Binit(&obuf, of, OWRITE);
+
+ pass = 1;
+ pinit(file);
+ for(i=0; i<nDlist; i++)
+ dodefine(Dlist[i]);
+ yyparse();
+ if(nerrors) {
+ cclean();
+ return nerrors;
+ }
+
+ pass = 2;
+ outhist();
+ pinit(file);
+ for(i=0; i<nDlist; i++)
+ dodefine(Dlist[i]);
+ yyparse();
+ cclean();
+ return nerrors;
+}
+
+struct
+{
+ char *name;
+ ushort type;
+ ushort value;
+} itab[] =
+{
+ "SP", LSP, D_AUTO,
+ "SB", LSB, D_EXTERN,
+ "FP", LFP, D_PARAM,
+ "PC", LPC, D_BRANCH,
+
+ "AL", LBREG, D_AL,
+ "CL", LBREG, D_CL,
+ "DL", LBREG, D_DL,
+ "BL", LBREG, D_BL,
+/* "SPB", LBREG, D_SPB, */
+ "SIB", LBREG, D_SIB,
+ "DIB", LBREG, D_DIB,
+ "BPB", LBREG, D_BPB,
+ "R8B", LBREG, D_R8B,
+ "R9B", LBREG, D_R9B,
+ "R10B", LBREG, D_R10B,
+ "R11B", LBREG, D_R11B,
+ "R12B", LBREG, D_R12B,
+ "R13B", LBREG, D_R13B,
+ "R14B", LBREG, D_R14B,
+ "R15B", LBREG, D_R15B,
+
+ "AH", LBREG, D_AH,
+ "CH", LBREG, D_CH,
+ "DH", LBREG, D_DH,
+ "BH", LBREG, D_BH,
+
+ "AX", LLREG, D_AX,
+ "CX", LLREG, D_CX,
+ "DX", LLREG, D_DX,
+ "BX", LLREG, D_BX,
+/* "SP", LLREG, D_SP, */
+ "BP", LLREG, D_BP,
+ "SI", LLREG, D_SI,
+ "DI", LLREG, D_DI,
+ "R8", LLREG, D_R8,
+ "R9", LLREG, D_R9,
+ "R10", LLREG, D_R10,
+ "R11", LLREG, D_R11,
+ "R12", LLREG, D_R12,
+ "R13", LLREG, D_R13,
+ "R14", LLREG, D_R14,
+ "R15", LLREG, D_R15,
+
+ "RARG", LLREG, REGARG,
+
+ "F0", LFREG, D_F0+0,
+ "F1", LFREG, D_F0+1,
+ "F2", LFREG, D_F0+2,
+ "F3", LFREG, D_F0+3,
+ "F4", LFREG, D_F0+4,
+ "F5", LFREG, D_F0+5,
+ "F6", LFREG, D_F0+6,
+ "F7", LFREG, D_F0+7,
+
+ "M0", LMREG, D_M0+0,
+ "M1", LMREG, D_M0+1,
+ "M2", LMREG, D_M0+2,
+ "M3", LMREG, D_M0+3,
+ "M4", LMREG, D_M0+4,
+ "M5", LMREG, D_M0+5,
+ "M6", LMREG, D_M0+6,
+ "M7", LMREG, D_M0+7,
+
+ "X0", LXREG, D_X0+0,
+ "X1", LXREG, D_X0+1,
+ "X2", LXREG, D_X0+2,
+ "X3", LXREG, D_X0+3,
+ "X4", LXREG, D_X0+4,
+ "X5", LXREG, D_X0+5,
+ "X6", LXREG, D_X0+6,
+ "X7", LXREG, D_X0+7,
+ "X8", LXREG, D_X0+8,
+ "X9", LXREG, D_X0+9,
+ "X10", LXREG, D_X0+10,
+ "X11", LXREG, D_X0+11,
+ "X12", LXREG, D_X0+12,
+ "X13", LXREG, D_X0+13,
+ "X14", LXREG, D_X0+14,
+ "X15", LXREG, D_X0+15,
+
+ "CS", LSREG, D_CS,
+ "SS", LSREG, D_SS,
+ "DS", LSREG, D_DS,
+ "ES", LSREG, D_ES,
+ "FS", LSREG, D_FS,
+ "GS", LSREG, D_GS,
+
+ "GDTR", LBREG, D_GDTR,
+ "IDTR", LBREG, D_IDTR,
+ "LDTR", LBREG, D_LDTR,
+ "MSW", LBREG, D_MSW,
+ "TASK", LBREG, D_TASK,
+
+ "CR0", LBREG, D_CR+0,
+ "CR1", LBREG, D_CR+1,
+ "CR2", LBREG, D_CR+2,
+ "CR3", LBREG, D_CR+3,
+ "CR4", LBREG, D_CR+4,
+ "CR5", LBREG, D_CR+5,
+ "CR6", LBREG, D_CR+6,
+ "CR7", LBREG, D_CR+7,
+ "CR8", LBREG, D_CR+8,
+ "CR9", LBREG, D_CR+9,
+ "CR10", LBREG, D_CR+10,
+ "CR11", LBREG, D_CR+11,
+ "CR12", LBREG, D_CR+12,
+ "CR13", LBREG, D_CR+13,
+ "CR14", LBREG, D_CR+14,
+ "CR15", LBREG, D_CR+15,
+
+ "DR0", LBREG, D_DR+0,
+ "DR1", LBREG, D_DR+1,
+ "DR2", LBREG, D_DR+2,
+ "DR3", LBREG, D_DR+3,
+ "DR4", LBREG, D_DR+4,
+ "DR5", LBREG, D_DR+5,
+ "DR6", LBREG, D_DR+6,
+ "DR7", LBREG, D_DR+7,
+
+ "TR0", LBREG, D_TR+0,
+ "TR1", LBREG, D_TR+1,
+ "TR2", LBREG, D_TR+2,
+ "TR3", LBREG, D_TR+3,
+ "TR4", LBREG, D_TR+4,
+ "TR5", LBREG, D_TR+5,
+ "TR6", LBREG, D_TR+6,
+ "TR7", LBREG, D_TR+7,
+
+ "AAA", LTYPE0, AAAA,
+ "AAD", LTYPE0, AAAD,
+ "AAM", LTYPE0, AAAM,
+ "AAS", LTYPE0, AAAS,
+ "ADCB", LTYPE3, AADCB,
+ "ADCL", LTYPE3, AADCL,
+ "ADCQ", LTYPE3, AADCQ,
+ "ADCW", LTYPE3, AADCW,
+ "ADDB", LTYPE3, AADDB,
+ "ADDL", LTYPE3, AADDL,
+ "ADDQ", LTYPE3, AADDQ,
+ "ADDW", LTYPE3, AADDW,
+ "ADJSP", LTYPE2, AADJSP,
+ "ANDB", LTYPE3, AANDB,
+ "ANDL", LTYPE3, AANDL,
+ "ANDQ", LTYPE3, AANDQ,
+ "ANDW", LTYPE3, AANDW,
+ "ARPL", LTYPE3, AARPL,
+ "BOUNDL", LTYPE3, ABOUNDL,
+ "BOUNDW", LTYPE3, ABOUNDW,
+ "BSFL", LTYPE3, ABSFL,
+ "BSFQ", LTYPE3, ABSFQ,
+ "BSFW", LTYPE3, ABSFW,
+ "BSRL", LTYPE3, ABSRL,
+ "BSRQ", LTYPE3, ABSRQ,
+ "BSRW", LTYPE3, ABSRW,
+ "BTCL", LTYPE3, ABTCL,
+ "BTCQ", LTYPE3, ABTCQ,
+ "BTCW", LTYPE3, ABTCW,
+ "BTL", LTYPE3, ABTL,
+ "BTQ", LTYPE3, ABTQ,
+ "BTRL", LTYPE3, ABTRL,
+ "BTRQ", LTYPE3, ABTRQ,
+ "BTRW", LTYPE3, ABTRW,
+ "BTSL", LTYPE3, ABTSL,
+ "BTSQ", LTYPE3, ABTSQ,
+ "BTSW", LTYPE3, ABTSW,
+ "BTW", LTYPE3, ABTW,
+ "BYTE", LTYPE2, ABYTE,
+ "CALL", LTYPEC, ACALL,
+ "CLC", LTYPE0, ACLC,
+ "CLD", LTYPE0, ACLD,
+ "CLI", LTYPE0, ACLI,
+ "CLTS", LTYPE0, ACLTS,
+ "CMC", LTYPE0, ACMC,
+ "CMPB", LTYPE4, ACMPB,
+ "CMPL", LTYPE4, ACMPL,
+ "CMPQ", LTYPE4, ACMPQ,
+ "CMPW", LTYPE4, ACMPW,
+ "CMPSB", LTYPE0, ACMPSB,
+ "CMPSL", LTYPE0, ACMPSL,
+ "CMPSQ", LTYPE0, ACMPSQ,
+ "CMPSW", LTYPE0, ACMPSW,
+ "CMPXCHG8B", LTYPE1, ACMPXCHG8B,
+ "CMPXCHGB", LTYPE3, ACMPXCHGB, /* LTYPE3? */
+ "CMPXCHGL", LTYPE3, ACMPXCHGL,
+ "CMPXCHGQ", LTYPE3, ACMPXCHGQ,
+ "CMPXCHGW", LTYPE3, ACMPXCHGW,
+ "CPUID", LTYPE0, ACPUID,
+ "DAA", LTYPE0, ADAA,
+ "DAS", LTYPE0, ADAS,
+ "DATA", LTYPED, ADATA,
+ "DECB", LTYPE1, ADECB,
+ "DECL", LTYPE1, ADECL,
+ "DECQ", LTYPE1, ADECQ,
+ "DECW", LTYPE1, ADECW,
+ "DIVB", LTYPE2, ADIVB,
+ "DIVL", LTYPE2, ADIVL,
+ "DIVQ", LTYPE2, ADIVQ,
+ "DIVW", LTYPE2, ADIVW,
+ "EMMS", LTYPE0, AEMMS,
+ "END", LTYPE0, AEND,
+ "ENTER", LTYPE2, AENTER,
+ "GLOBL", LTYPET, AGLOBL,
+ "HLT", LTYPE0, AHLT,
+ "IDIVB", LTYPE2, AIDIVB,
+ "IDIVL", LTYPE2, AIDIVL,
+ "IDIVQ", LTYPE2, AIDIVQ,
+ "IDIVW", LTYPE2, AIDIVW,
+ "IMULB", LTYPEI, AIMULB,
+ "IMULL", LTYPEI, AIMULL,
+ "IMULQ", LTYPEI, AIMULQ,
+ "IMULW", LTYPEI, AIMULW,
+ "INB", LTYPE0, AINB,
+ "INL", LTYPE0, AINL,
+ "INW", LTYPE0, AINW,
+ "INCB", LTYPE1, AINCB,
+ "INCL", LTYPE1, AINCL,
+ "INCQ", LTYPE1, AINCQ,
+ "INCW", LTYPE1, AINCW,
+ "INSB", LTYPE0, AINSB,
+ "INSL", LTYPE0, AINSL,
+ "INSW", LTYPE0, AINSW,
+ "INT", LTYPE2, AINT,
+ "INTO", LTYPE0, AINTO,
+ "INVD", LTYPE0, AINVD,
+ "INVLPG", LTYPE2, AINVLPG,
+ "IRETL", LTYPE0, AIRETL,
+ "IRETQ", LTYPE0, AIRETQ,
+ "IRETW", LTYPE0, AIRETW,
+
+ "JOS", LTYPER, AJOS,
+ "JO", LTYPER, AJOS, /* alternate */
+ "JOC", LTYPER, AJOC,
+ "JNO", LTYPER, AJOC, /* alternate */
+ "JCS", LTYPER, AJCS,
+ "JB", LTYPER, AJCS, /* alternate */
+ "JC", LTYPER, AJCS, /* alternate */
+ "JNAE", LTYPER, AJCS, /* alternate */
+ "JLO", LTYPER, AJCS, /* alternate */
+ "JCC", LTYPER, AJCC,
+ "JAE", LTYPER, AJCC, /* alternate */
+ "JNB", LTYPER, AJCC, /* alternate */
+ "JNC", LTYPER, AJCC, /* alternate */
+ "JHS", LTYPER, AJCC, /* alternate */
+ "JEQ", LTYPER, AJEQ,
+ "JE", LTYPER, AJEQ, /* alternate */
+ "JZ", LTYPER, AJEQ, /* alternate */
+ "JNE", LTYPER, AJNE,
+ "JNZ", LTYPER, AJNE, /* alternate */
+ "JLS", LTYPER, AJLS,
+ "JBE", LTYPER, AJLS, /* alternate */
+ "JNA", LTYPER, AJLS, /* alternate */
+ "JHI", LTYPER, AJHI,
+ "JA", LTYPER, AJHI, /* alternate */
+ "JNBE", LTYPER, AJHI, /* alternate */
+ "JMI", LTYPER, AJMI,
+ "JS", LTYPER, AJMI, /* alternate */
+ "JPL", LTYPER, AJPL,
+ "JNS", LTYPER, AJPL, /* alternate */
+ "JPS", LTYPER, AJPS,
+ "JP", LTYPER, AJPS, /* alternate */
+ "JPE", LTYPER, AJPS, /* alternate */
+ "JPC", LTYPER, AJPC,
+ "JNP", LTYPER, AJPC, /* alternate */
+ "JPO", LTYPER, AJPC, /* alternate */
+ "JLT", LTYPER, AJLT,
+ "JL", LTYPER, AJLT, /* alternate */
+ "JNGE", LTYPER, AJLT, /* alternate */
+ "JGE", LTYPER, AJGE,
+ "JNL", LTYPER, AJGE, /* alternate */
+ "JLE", LTYPER, AJLE,
+ "JNG", LTYPER, AJLE, /* alternate */
+ "JGT", LTYPER, AJGT,
+ "JG", LTYPER, AJGT, /* alternate */
+ "JNLE", LTYPER, AJGT, /* alternate */
+
+ "JCXZ", LTYPER, AJCXZ,
+ "JMP", LTYPEC, AJMP,
+ "LAHF", LTYPE0, ALAHF,
+ "LARL", LTYPE3, ALARL,
+ "LARW", LTYPE3, ALARW,
+ "LEAL", LTYPE3, ALEAL,
+ "LEAQ", LTYPE3, ALEAQ,
+ "LEAW", LTYPE3, ALEAW,
+ "LEAVEL", LTYPE0, ALEAVEL,
+ "LEAVEQ", LTYPE0, ALEAVEQ,
+ "LEAVEW", LTYPE0, ALEAVEW,
+ "LFENCE", LTYPE0, ALFENCE,
+ "LOCK", LTYPE0, ALOCK,
+ "LODSB", LTYPE0, ALODSB,
+ "LODSL", LTYPE0, ALODSL,
+ "LODSQ", LTYPE0, ALODSQ,
+ "LODSW", LTYPE0, ALODSW,
+ "LONG", LTYPE2, ALONG,
+ "LOOP", LTYPER, ALOOP,
+ "LOOPEQ", LTYPER, ALOOPEQ,
+ "LOOPNE", LTYPER, ALOOPNE,
+ "LSLL", LTYPE3, ALSLL,
+ "LSLW", LTYPE3, ALSLW,
+ "MFENCE", LTYPE0, AMFENCE,
+ "MODE", LTYPE2, AMODE,
+ "MOVB", LTYPE3, AMOVB,
+ "MOVL", LTYPEM, AMOVL,
+ "MOVQ", LTYPEM, AMOVQ,
+ "MOVW", LTYPEM, AMOVW,
+ "MOVBLSX", LTYPE3, AMOVBLSX,
+ "MOVBLZX", LTYPE3, AMOVBLZX,
+ "MOVBQSX", LTYPE3, AMOVBQSX,
+ "MOVBQZX", LTYPE3, AMOVBQZX,
+ "MOVBWSX", LTYPE3, AMOVBWSX,
+ "MOVBWZX", LTYPE3, AMOVBWZX,
+ "MOVLQSX", LTYPE3, AMOVLQSX,
+ "MOVLQZX", LTYPE3, AMOVLQZX,
+ "MOVNTIL", LTYPE3, AMOVNTIL,
+ "MOVNTIQ", LTYPE3, AMOVNTIQ,
+ "MOVWLSX", LTYPE3, AMOVWLSX,
+ "MOVWLZX", LTYPE3, AMOVWLZX,
+ "MOVWQSX", LTYPE3, AMOVWQSX,
+ "MOVWQZX", LTYPE3, AMOVWQZX,
+ "MOVSB", LTYPE0, AMOVSB,
+ "MOVSL", LTYPE0, AMOVSL,
+ "MOVSQ", LTYPE0, AMOVSQ,
+ "MOVSW", LTYPE0, AMOVSW,
+ "MULB", LTYPE2, AMULB,
+ "MULL", LTYPE2, AMULL,
+ "MULQ", LTYPE2, AMULQ,
+ "MULW", LTYPE2, AMULW,
+ "NEGB", LTYPE1, ANEGB,
+ "NEGL", LTYPE1, ANEGL,
+ "NEGQ", LTYPE1, ANEGQ,
+ "NEGW", LTYPE1, ANEGW,
+ "NOP", LTYPEN, ANOP,
+ "NOTB", LTYPE1, ANOTB,
+ "NOTL", LTYPE1, ANOTL,
+ "NOTQ", LTYPE1, ANOTQ,
+ "NOTW", LTYPE1, ANOTW,
+ "ORB", LTYPE3, AORB,
+ "ORL", LTYPE3, AORL,
+ "ORQ", LTYPE3, AORQ,
+ "ORW", LTYPE3, AORW,
+ "OUTB", LTYPE0, AOUTB,
+ "OUTL", LTYPE0, AOUTL,
+ "OUTW", LTYPE0, AOUTW,
+ "OUTSB", LTYPE0, AOUTSB,
+ "OUTSL", LTYPE0, AOUTSL,
+ "OUTSW", LTYPE0, AOUTSW,
+ "POPAL", LTYPE0, APOPAL,
+ "POPAW", LTYPE0, APOPAW,
+ "POPFL", LTYPE0, APOPFL,
+ "POPFQ", LTYPE0, APOPFQ,
+ "POPFW", LTYPE0, APOPFW,
+ "POPL", LTYPE1, APOPL,
+ "POPQ", LTYPE1, APOPQ,
+ "POPW", LTYPE1, APOPW,
+ "PUSHAL", LTYPE0, APUSHAL,
+ "PUSHAW", LTYPE0, APUSHAW,
+ "PUSHFL", LTYPE0, APUSHFL,
+ "PUSHFQ", LTYPE0, APUSHFQ,
+ "PUSHFW", LTYPE0, APUSHFW,
+ "PUSHL", LTYPE2, APUSHL,
+ "PUSHQ", LTYPE2, APUSHQ,
+ "PUSHW", LTYPE2, APUSHW,
+ "RCLB", LTYPE3, ARCLB,
+ "RCLL", LTYPE3, ARCLL,
+ "RCLQ", LTYPE3, ARCLQ,
+ "RCLW", LTYPE3, ARCLW,
+ "RCRB", LTYPE3, ARCRB,
+ "RCRL", LTYPE3, ARCRL,
+ "RCRQ", LTYPE3, ARCRQ,
+ "RCRW", LTYPE3, ARCRW,
+ "RDMSR", LTYPE0, ARDMSR,
+ "RDPMC", LTYPE0, ARDPMC,
+ "RDTSC", LTYPE0, ARDTSC,
+ "REP", LTYPE0, AREP,
+ "REPN", LTYPE0, AREPN,
+ "RET", LTYPE0, ARET,
+ "RETFL", LTYPERT,ARETFL,
+ "RETFW", LTYPERT,ARETFW,
+ "RETFQ", LTYPERT,ARETFQ,
+ "ROLB", LTYPE3, AROLB,
+ "ROLL", LTYPE3, AROLL,
+ "ROLQ", LTYPE3, AROLQ,
+ "ROLW", LTYPE3, AROLW,
+ "RORB", LTYPE3, ARORB,
+ "RORL", LTYPE3, ARORL,
+ "RORQ", LTYPE3, ARORQ,
+ "RORW", LTYPE3, ARORW,
+ "RSM", LTYPE0, ARSM,
+ "SAHF", LTYPE0, ASAHF,
+ "SALB", LTYPE3, ASALB,
+ "SALL", LTYPE3, ASALL,
+ "SALQ", LTYPE3, ASALQ,
+ "SALW", LTYPE3, ASALW,
+ "SARB", LTYPE3, ASARB,
+ "SARL", LTYPE3, ASARL,
+ "SARQ", LTYPE3, ASARQ,
+ "SARW", LTYPE3, ASARW,
+ "SBBB", LTYPE3, ASBBB,
+ "SBBL", LTYPE3, ASBBL,
+ "SBBQ", LTYPE3, ASBBQ,
+ "SBBW", LTYPE3, ASBBW,
+ "SCASB", LTYPE0, ASCASB,
+ "SCASL", LTYPE0, ASCASL,
+ "SCASQ", LTYPE0, ASCASQ,
+ "SCASW", LTYPE0, ASCASW,
+ "SETCC", LTYPE1, ASETCC,
+ "SETCS", LTYPE1, ASETCS,
+ "SETEQ", LTYPE1, ASETEQ,
+ "SETGE", LTYPE1, ASETGE,
+ "SETGT", LTYPE1, ASETGT,
+ "SETHI", LTYPE1, ASETHI,
+ "SETLE", LTYPE1, ASETLE,
+ "SETLS", LTYPE1, ASETLS,
+ "SETLT", LTYPE1, ASETLT,
+ "SETMI", LTYPE1, ASETMI,
+ "SETNE", LTYPE1, ASETNE,
+ "SETOC", LTYPE1, ASETOC,
+ "SETOS", LTYPE1, ASETOS,
+ "SETPC", LTYPE1, ASETPC,
+ "SETPL", LTYPE1, ASETPL,
+ "SETPS", LTYPE1, ASETPS,
+ "SFENCE", LTYPE0, ASFENCE,
+ "CDQ", LTYPE0, ACDQ,
+ "CWD", LTYPE0, ACWD,
+ "CQO", LTYPE0, ACQO,
+ "SHLB", LTYPE3, ASHLB,
+ "SHLL", LTYPES, ASHLL,
+ "SHLQ", LTYPES, ASHLQ,
+ "SHLW", LTYPES, ASHLW,
+ "SHRB", LTYPE3, ASHRB,
+ "SHRL", LTYPES, ASHRL,
+ "SHRQ", LTYPES, ASHRQ,
+ "SHRW", LTYPES, ASHRW,
+ "STC", LTYPE0, ASTC,
+ "STD", LTYPE0, ASTD,
+ "STI", LTYPE0, ASTI,
+ "STOSB", LTYPE0, ASTOSB,
+ "STOSL", LTYPE0, ASTOSL,
+ "STOSQ", LTYPE0, ASTOSQ,
+ "STOSW", LTYPE0, ASTOSW,
+ "SUBB", LTYPE3, ASUBB,
+ "SUBL", LTYPE3, ASUBL,
+ "SUBQ", LTYPE3, ASUBQ,
+ "SUBW", LTYPE3, ASUBW,
+ "SYSCALL", LTYPE0, ASYSCALL,
+ "SYSRET", LTYPE0, ASYSRET,
+ "SWAPGS", LTYPE0, ASWAPGS,
+ "TESTB", LTYPE3, ATESTB,
+ "TESTL", LTYPE3, ATESTL,
+ "TESTQ", LTYPE3, ATESTQ,
+ "TESTW", LTYPE3, ATESTW,
+ "TEXT", LTYPET, ATEXT,
+ "VERR", LTYPE2, AVERR,
+ "VERW", LTYPE2, AVERW,
+ "QUAD", LTYPE2, AQUAD,
+ "WAIT", LTYPE0, AWAIT,
+ "WBINVD", LTYPE0, AWBINVD,
+ "WRMSR", LTYPE0, AWRMSR,
+ "WORD", LTYPE2, AWORD,
+ "XADDB", LTYPE3, AXADDB,
+ "XADDL", LTYPE3, AXADDL,
+ "XADDQ", LTYPE3, AXADDQ,
+ "XADDW", LTYPE3, AXADDW,
+ "XCHGB", LTYPE3, AXCHGB,
+ "XCHGL", LTYPE3, AXCHGL,
+ "XCHGQ", LTYPE3, AXCHGQ,
+ "XCHGW", LTYPE3, AXCHGW,
+ "XLAT", LTYPE2, AXLAT,
+ "XORB", LTYPE3, AXORB,
+ "XORL", LTYPE3, AXORL,
+ "XORQ", LTYPE3, AXORQ,
+ "XORW", LTYPE3, AXORW,
+
+ "CMOVLCC", LTYPE3, ACMOVLCC,
+ "CMOVLCS", LTYPE3, ACMOVLCS,
+ "CMOVLEQ", LTYPE3, ACMOVLEQ,
+ "CMOVLGE", LTYPE3, ACMOVLGE,
+ "CMOVLGT", LTYPE3, ACMOVLGT,
+ "CMOVLHI", LTYPE3, ACMOVLHI,
+ "CMOVLLE", LTYPE3, ACMOVLLE,
+ "CMOVLLS", LTYPE3, ACMOVLLS,
+ "CMOVLLT", LTYPE3, ACMOVLLT,
+ "CMOVLMI", LTYPE3, ACMOVLMI,
+ "CMOVLNE", LTYPE3, ACMOVLNE,
+ "CMOVLOC", LTYPE3, ACMOVLOC,
+ "CMOVLOS", LTYPE3, ACMOVLOS,
+ "CMOVLPC", LTYPE3, ACMOVLPC,
+ "CMOVLPL", LTYPE3, ACMOVLPL,
+ "CMOVLPS", LTYPE3, ACMOVLPS,
+ "CMOVQCC", LTYPE3, ACMOVQCC,
+ "CMOVQCS", LTYPE3, ACMOVQCS,
+ "CMOVQEQ", LTYPE3, ACMOVQEQ,
+ "CMOVQGE", LTYPE3, ACMOVQGE,
+ "CMOVQGT", LTYPE3, ACMOVQGT,
+ "CMOVQHI", LTYPE3, ACMOVQHI,
+ "CMOVQLE", LTYPE3, ACMOVQLE,
+ "CMOVQLS", LTYPE3, ACMOVQLS,
+ "CMOVQLT", LTYPE3, ACMOVQLT,
+ "CMOVQMI", LTYPE3, ACMOVQMI,
+ "CMOVQNE", LTYPE3, ACMOVQNE,
+ "CMOVQOC", LTYPE3, ACMOVQOC,
+ "CMOVQOS", LTYPE3, ACMOVQOS,
+ "CMOVQPC", LTYPE3, ACMOVQPC,
+ "CMOVQPL", LTYPE3, ACMOVQPL,
+ "CMOVQPS", LTYPE3, ACMOVQPS,
+ "CMOVWCC", LTYPE3, ACMOVWCC,
+ "CMOVWCS", LTYPE3, ACMOVWCS,
+ "CMOVWEQ", LTYPE3, ACMOVWEQ,
+ "CMOVWGE", LTYPE3, ACMOVWGE,
+ "CMOVWGT", LTYPE3, ACMOVWGT,
+ "CMOVWHI", LTYPE3, ACMOVWHI,
+ "CMOVWLE", LTYPE3, ACMOVWLE,
+ "CMOVWLS", LTYPE3, ACMOVWLS,
+ "CMOVWLT", LTYPE3, ACMOVWLT,
+ "CMOVWMI", LTYPE3, ACMOVWMI,
+ "CMOVWNE", LTYPE3, ACMOVWNE,
+ "CMOVWOC", LTYPE3, ACMOVWOC,
+ "CMOVWOS", LTYPE3, ACMOVWOS,
+ "CMOVWPC", LTYPE3, ACMOVWPC,
+ "CMOVWPL", LTYPE3, ACMOVWPL,
+ "CMOVWPS", LTYPE3, ACMOVWPS,
+
+ "FMOVB", LTYPE3, AFMOVB,
+ "FMOVBP", LTYPE3, AFMOVBP,
+ "FMOVD", LTYPE3, AFMOVD,
+ "FMOVDP", LTYPE3, AFMOVDP,
+ "FMOVF", LTYPE3, AFMOVF,
+ "FMOVFP", LTYPE3, AFMOVFP,
+ "FMOVL", LTYPE3, AFMOVL,
+ "FMOVLP", LTYPE3, AFMOVLP,
+ "FMOVV", LTYPE3, AFMOVV,
+ "FMOVVP", LTYPE3, AFMOVVP,
+ "FMOVW", LTYPE3, AFMOVW,
+ "FMOVWP", LTYPE3, AFMOVWP,
+ "FMOVX", LTYPE3, AFMOVX,
+ "FMOVXP", LTYPE3, AFMOVXP,
+ "FCOMB", LTYPE3, AFCOMB,
+ "FCOMBP", LTYPE3, AFCOMBP,
+ "FCOMD", LTYPE3, AFCOMD,
+ "FCOMDP", LTYPE3, AFCOMDP,
+ "FCOMDPP", LTYPE3, AFCOMDPP,
+ "FCOMF", LTYPE3, AFCOMF,
+ "FCOMFP", LTYPE3, AFCOMFP,
+ "FCOML", LTYPE3, AFCOML,
+ "FCOMLP", LTYPE3, AFCOMLP,
+ "FCOMW", LTYPE3, AFCOMW,
+ "FCOMWP", LTYPE3, AFCOMWP,
+ "FUCOM", LTYPE3, AFUCOM,
+ "FUCOMP", LTYPE3, AFUCOMP,
+ "FUCOMPP", LTYPE3, AFUCOMPP,
+ "FADDW", LTYPE3, AFADDW,
+ "FADDL", LTYPE3, AFADDL,
+ "FADDF", LTYPE3, AFADDF,
+ "FADDD", LTYPE3, AFADDD,
+ "FADDDP", LTYPE3, AFADDDP,
+ "FSUBDP", LTYPE3, AFSUBDP,
+ "FSUBW", LTYPE3, AFSUBW,
+ "FSUBL", LTYPE3, AFSUBL,
+ "FSUBF", LTYPE3, AFSUBF,
+ "FSUBD", LTYPE3, AFSUBD,
+ "FSUBRDP", LTYPE3, AFSUBRDP,
+ "FSUBRW", LTYPE3, AFSUBRW,
+ "FSUBRL", LTYPE3, AFSUBRL,
+ "FSUBRF", LTYPE3, AFSUBRF,
+ "FSUBRD", LTYPE3, AFSUBRD,
+ "FMULDP", LTYPE3, AFMULDP,
+ "FMULW", LTYPE3, AFMULW,
+ "FMULL", LTYPE3, AFMULL,
+ "FMULF", LTYPE3, AFMULF,
+ "FMULD", LTYPE3, AFMULD,
+ "FDIVDP", LTYPE3, AFDIVDP,
+ "FDIVW", LTYPE3, AFDIVW,
+ "FDIVL", LTYPE3, AFDIVL,
+ "FDIVF", LTYPE3, AFDIVF,
+ "FDIVD", LTYPE3, AFDIVD,
+ "FDIVRDP", LTYPE3, AFDIVRDP,
+ "FDIVRW", LTYPE3, AFDIVRW,
+ "FDIVRL", LTYPE3, AFDIVRL,
+ "FDIVRF", LTYPE3, AFDIVRF,
+ "FDIVRD", LTYPE3, AFDIVRD,
+ "FXCHD", LTYPE3, AFXCHD,
+ "FFREE", LTYPE1, AFFREE,
+ "FLDCW", LTYPE2, AFLDCW,
+ "FLDENV", LTYPE1, AFLDENV,
+ "FRSTOR", LTYPE2, AFRSTOR,
+ "FSAVE", LTYPE1, AFSAVE,
+ "FSTCW", LTYPE1, AFSTCW,
+ "FSTENV", LTYPE1, AFSTENV,
+ "FSTSW", LTYPE1, AFSTSW,
+ "F2XM1", LTYPE0, AF2XM1,
+ "FABS", LTYPE0, AFABS,
+ "FCHS", LTYPE0, AFCHS,
+ "FCLEX", LTYPE0, AFCLEX,
+ "FCOS", LTYPE0, AFCOS,
+ "FDECSTP", LTYPE0, AFDECSTP,
+ "FINCSTP", LTYPE0, AFINCSTP,
+ "FINIT", LTYPE0, AFINIT,
+ "FLD1", LTYPE0, AFLD1,
+ "FLDL2E", LTYPE0, AFLDL2E,
+ "FLDL2T", LTYPE0, AFLDL2T,
+ "FLDLG2", LTYPE0, AFLDLG2,
+ "FLDLN2", LTYPE0, AFLDLN2,
+ "FLDPI", LTYPE0, AFLDPI,
+ "FLDZ", LTYPE0, AFLDZ,
+ "FNOP", LTYPE0, AFNOP,
+ "FPATAN", LTYPE0, AFPATAN,
+ "FPREM", LTYPE0, AFPREM,
+ "FPREM1", LTYPE0, AFPREM1,
+ "FPTAN", LTYPE0, AFPTAN,
+ "FRNDINT", LTYPE0, AFRNDINT,
+ "FSCALE", LTYPE0, AFSCALE,
+ "FSIN", LTYPE0, AFSIN,
+ "FSINCOS", LTYPE0, AFSINCOS,
+ "FSQRT", LTYPE0, AFSQRT,
+ "FTST", LTYPE0, AFTST,
+ "FXAM", LTYPE0, AFXAM,
+ "FXTRACT", LTYPE0, AFXTRACT,
+ "FYL2X", LTYPE0, AFYL2X,
+ "FYL2XP1", LTYPE0, AFYL2XP1,
+
+ "ADDPD", LTYPE3, AADDPD,
+ "ADDPS", LTYPE3, AADDPS,
+ "ADDSD", LTYPE3, AADDSD,
+ "ADDSS", LTYPE3, AADDSS,
+ "ANDNPD", LTYPE3, AANDNPD,
+ "ANDNPS", LTYPE3, AANDNPS,
+ "ANDPD", LTYPE3, AANDPD,
+ "ANDPS", LTYPE3, AANDPS,
+ "CMPPD", LTYPEXC,ACMPPD,
+ "CMPPS", LTYPEXC,ACMPPS,
+ "CMPSD", LTYPEXC,ACMPSD,
+ "CMPSS", LTYPEXC,ACMPSS,
+ "COMISD", LTYPE3, ACOMISD,
+ "COMISS", LTYPE3, ACOMISS,
+ "CVTPL2PD", LTYPE3, ACVTPL2PD,
+ "CVTPL2PS", LTYPE3, ACVTPL2PS,
+ "CVTPD2PL", LTYPE3, ACVTPD2PL,
+ "CVTPD2PS", LTYPE3, ACVTPD2PS,
+ "CVTPS2PL", LTYPE3, ACVTPS2PL,
+ "PF2IW", LTYPE3, APF2IW,
+ "PF2IL", LTYPE3, APF2IL,
+ "PF2ID", LTYPE3, APF2IL, /* syn */
+ "PI2FL", LTYPE3, API2FL,
+ "PI2FD", LTYPE3, API2FL, /* syn */
+ "PI2FW", LTYPE3, API2FW,
+ "CVTPS2PD", LTYPE3, ACVTPS2PD,
+ "CVTSD2SL", LTYPE3, ACVTSD2SL,
+ "CVTSD2SQ", LTYPE3, ACVTSD2SQ,
+ "CVTSD2SS", LTYPE3, ACVTSD2SS,
+ "CVTSL2SD", LTYPE3, ACVTSL2SD,
+ "CVTSQ2SD", LTYPE3, ACVTSQ2SD,
+ "CVTSL2SS", LTYPE3, ACVTSL2SS,
+ "CVTSQ2SS", LTYPE3, ACVTSQ2SS,
+ "CVTSS2SD", LTYPE3, ACVTSS2SD,
+ "CVTSS2SL", LTYPE3, ACVTSS2SL,
+ "CVTSS2SQ", LTYPE3, ACVTSS2SQ,
+ "CVTTPD2PL", LTYPE3, ACVTTPD2PL,
+ "CVTTPS2PL", LTYPE3, ACVTTPS2PL,
+ "CVTTSD2SL", LTYPE3, ACVTTSD2SL,
+ "CVTTSD2SQ", LTYPE3, ACVTTSD2SQ,
+ "CVTTSS2SL", LTYPE3, ACVTTSS2SL,
+ "CVTTSS2SQ", LTYPE3, ACVTTSS2SQ,
+ "DIVPD", LTYPE3, ADIVPD,
+ "DIVPS", LTYPE3, ADIVPS,
+ "DIVSD", LTYPE3, ADIVSD,
+ "DIVSS", LTYPE3, ADIVSS,
+ "FXRSTOR", LTYPE2, AFXRSTOR,
+ "FXRSTOR64", LTYPE2, AFXRSTOR64,
+ "FXSAVE", LTYPE1, AFXSAVE,
+ "FXSAVE64", LTYPE1, AFXSAVE64,
+ "LDMXCSR", LTYPE2, ALDMXCSR,
+ "MASKMOVOU", LTYPE3, AMASKMOVOU,
+ "MASKMOVDQU", LTYPE3, AMASKMOVOU, /* syn */
+ "MASKMOVQ", LTYPE3, AMASKMOVQ,
+ "MAXPD", LTYPE3, AMAXPD,
+ "MAXPS", LTYPE3, AMAXPS,
+ "MAXSD", LTYPE3, AMAXSD,
+ "MAXSS", LTYPE3, AMAXSS,
+ "MINPD", LTYPE3, AMINPD,
+ "MINPS", LTYPE3, AMINPS,
+ "MINSD", LTYPE3, AMINSD,
+ "MINSS", LTYPE3, AMINSS,
+ "MOVAPD", LTYPE3, AMOVAPD,
+ "MOVAPS", LTYPE3, AMOVAPS,
+ "MOVD", LTYPE3, AMOVQ, /* syn */
+ "MOVDQ2Q", LTYPE3, AMOVQ, /* syn */
+ "MOVO", LTYPE3, AMOVO,
+ "MOVOA", LTYPE3, AMOVO, /* syn */
+ "MOVOU", LTYPE3, AMOVOU,
+ "MOVHLPS", LTYPE3, AMOVHLPS,
+ "MOVHPD", LTYPE3, AMOVHPD,
+ "MOVHPS", LTYPE3, AMOVHPS,
+ "MOVLHPS", LTYPE3, AMOVLHPS,
+ "MOVLPD", LTYPE3, AMOVLPD,
+ "MOVLPS", LTYPE3, AMOVLPS,
+ "MOVMSKPD", LTYPE3, AMOVMSKPD,
+ "MOVMSKPS", LTYPE3, AMOVMSKPS,
+ "MOVNTO", LTYPE3, AMOVNTO,
+ "MOVNTDQ", LTYPE3, AMOVNTO, /* syn */
+ "MOVNTPD", LTYPE3, AMOVNTPD,
+ "MOVNTPS", LTYPE3, AMOVNTPS,
+ "MOVNTQ", LTYPE3, AMOVNTQ,
+ "MOVQOZX", LTYPE3, AMOVQOZX,
+ "MOVSD", LTYPE3, AMOVSD,
+ "MOVSS", LTYPE3, AMOVSS,
+ "MOVUPD", LTYPE3, AMOVUPD,
+ "MOVUPS", LTYPE3, AMOVUPS,
+ "MULPD", LTYPE3, AMULPD,
+ "MULPS", LTYPE3, AMULPS,
+ "MULSD", LTYPE3, AMULSD,
+ "MULSS", LTYPE3, AMULSS,
+ "ORPD", LTYPE3, AORPD,
+ "ORPS", LTYPE3, AORPS,
+ "PACKSSLW", LTYPE3, APACKSSLW,
+ "PACKSSWB", LTYPE3, APACKSSWB,
+ "PACKUSWB", LTYPE3, APACKUSWB,
+ "PADDB", LTYPE3, APADDB,
+ "PADDL", LTYPE3, APADDL,
+ "PADDQ", LTYPE3, APADDQ,
+ "PADDSB", LTYPE3, APADDSB,
+ "PADDSW", LTYPE3, APADDSW,
+ "PADDUSB", LTYPE3, APADDUSB,
+ "PADDUSW", LTYPE3, APADDUSW,
+ "PADDW", LTYPE3, APADDW,
+ "PAND", LTYPE3, APAND,
+ "PANDB", LTYPE3, APANDB,
+ "PANDL", LTYPE3, APANDL,
+ "PANDSB", LTYPE3, APANDSB,
+ "PANDSW", LTYPE3, APANDSW,
+ "PANDUSB", LTYPE3, APANDUSB,
+ "PANDUSW", LTYPE3, APANDUSW,
+ "PANDW", LTYPE3, APANDW,
+ "PANDN", LTYPE3, APANDN,
+ "PAVGB", LTYPE3, APAVGB,
+ "PAVGW", LTYPE3, APAVGW,
+ "PCMPEQB", LTYPE3, APCMPEQB,
+ "PCMPEQL", LTYPE3, APCMPEQL,
+ "PCMPEQW", LTYPE3, APCMPEQW,
+ "PCMPGTB", LTYPE3, APCMPGTB,
+ "PCMPGTL", LTYPE3, APCMPGTL,
+ "PCMPGTW", LTYPE3, APCMPGTW,
+ "PEXTRW", LTYPEX, APEXTRW,
+ "PINSRW", LTYPEX, APINSRW,
+ "PMADDWL", LTYPE3, APMADDWL,
+ "PMAXSW", LTYPE3, APMAXSW,
+ "PMAXUB", LTYPE3, APMAXUB,
+ "PMINSW", LTYPE3, APMINSW,
+ "PMINUB", LTYPE3, APMINUB,
+ "PMOVMSKB", LTYPE3, APMOVMSKB,
+ "PMULHRW", LTYPE3, APMULHRW,
+ "PMULHUW", LTYPE3, APMULHUW,
+ "PMULHW", LTYPE3, APMULHW,
+ "PMULLW", LTYPE3, APMULLW,
+ "PMULULQ", LTYPE3, APMULULQ,
+ "POR", LTYPE3, APOR,
+ "PSADBW", LTYPE3, APSADBW,
+ "PSHUFHW", LTYPEX, APSHUFHW,
+ "PSHUFL", LTYPEX, APSHUFL,
+ "PSHUFLW", LTYPEX, APSHUFLW,
+ "PSHUFW", LTYPEX, APSHUFW,
+ "PSLLO", LTYPE3, APSLLO,
+ "PSLLDQ", LTYPE3, APSLLO, /* syn */
+ "PSLLL", LTYPE3, APSLLL,
+ "PSLLQ", LTYPE3, APSLLQ,
+ "PSLLW", LTYPE3, APSLLW,
+ "PSRAL", LTYPE3, APSRAL,
+ "PSRAW", LTYPE3, APSRAW,
+ "PSRLO", LTYPE3, APSRLO,
+ "PSRLDQ", LTYPE3, APSRLO, /* syn */
+ "PSRLL", LTYPE3, APSRLL,
+ "PSRLQ", LTYPE3, APSRLQ,
+ "PSRLW", LTYPE3, APSRLW,
+ "PSUBB", LTYPE3, APSUBB,
+ "PSUBL", LTYPE3, APSUBL,
+ "PSUBQ", LTYPE3, APSUBQ,
+ "PSUBSB", LTYPE3, APSUBSB,
+ "PSUBSW", LTYPE3, APSUBSW,
+ "PSUBUSB", LTYPE3, APSUBUSB,
+ "PSUBUSW", LTYPE3, APSUBUSW,
+ "PSUBW", LTYPE3, APSUBW,
+ "PUNPCKHBW", LTYPE3, APUNPCKHBW,
+ "PUNPCKHLQ", LTYPE3, APUNPCKHLQ,
+ "PUNPCKHQDQ", LTYPE3, APUNPCKHQDQ,
+ "PUNPCKHWL", LTYPE3, APUNPCKHWL,
+ "PUNPCKLBW", LTYPE3, APUNPCKLBW,
+ "PUNPCKLLQ", LTYPE3, APUNPCKLLQ,
+ "PUNPCKLQDQ", LTYPE3, APUNPCKLQDQ,
+ "PUNPCKLWL", LTYPE3, APUNPCKLWL,
+ "PXOR", LTYPE3, APXOR,
+ "RCPPS", LTYPE3, ARCPPS,
+ "RCPSS", LTYPE3, ARCPSS,
+ "RSQRTPS", LTYPE3, ARSQRTPS,
+ "RSQRTSS", LTYPE3, ARSQRTSS,
+ "SHUFPD", LTYPEX, ASHUFPD,
+ "SHUFPS", LTYPEX, ASHUFPS,
+ "SQRTPD", LTYPE3, ASQRTPD,
+ "SQRTPS", LTYPE3, ASQRTPS,
+ "SQRTSD", LTYPE3, ASQRTSD,
+ "SQRTSS", LTYPE3, ASQRTSS,
+ "STMXCSR", LTYPE1, ASTMXCSR,
+ "SUBPD", LTYPE3, ASUBPD,
+ "SUBPS", LTYPE3, ASUBPS,
+ "SUBSD", LTYPE3, ASUBSD,
+ "SUBSS", LTYPE3, ASUBSS,
+ "UCOMISD", LTYPE3, AUCOMISD,
+ "UCOMISS", LTYPE3, AUCOMISS,
+ "UNPCKHPD", LTYPE3, AUNPCKHPD,
+ "UNPCKHPS", LTYPE3, AUNPCKHPS,
+ "UNPCKLPD", LTYPE3, AUNPCKLPD,
+ "UNPCKLPS", LTYPE3, AUNPCKLPS,
+ "XORPD", LTYPE3, AXORPD,
+ "XORPS", LTYPE3, AXORPS,
+
+ 0
+};
+
+void
+cinit(void)
+{
+ Sym *s;
+ int i;
+
+ nullgen.sym = S;
+ nullgen.offset = 0;
+ if(FPCHIP)
+ nullgen.dval = 0;
+ for(i=0; i<sizeof(nullgen.sval); i++)
+ nullgen.sval[i] = 0;
+ nullgen.type = D_NONE;
+ nullgen.index = D_NONE;
+ nullgen.scale = 0;
+
+ nerrors = 0;
+ iostack = I;
+ iofree = I;
+ peekc = IGN;
+ nhunk = 0;
+ for(i=0; i<NHASH; i++)
+ hash[i] = S;
+ for(i=0; itab[i].name; i++) {
+ s = slookup(itab[i].name);
+ if(s->type != LNAME)
+ yyerror("double initialization %s", itab[i].name);
+ s->type = itab[i].type;
+ s->value = itab[i].value;
+ }
+
+ pathname = allocn(pathname, 0, 100);
+ if(mygetwd(pathname, 99) == 0) {
+ pathname = allocn(pathname, 100, 900);
+ if(mygetwd(pathname, 999) == 0)
+ strcpy(pathname, "/?");
+ }
+}
+
+void
+checkscale(int scale)
+{
+
+ switch(scale) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ return;
+ }
+ yyerror("scale must be 1248: %d", scale);
+}
+
+void
+syminit(Sym *s)
+{
+
+ s->type = LNAME;
+ s->value = 0;
+}
+
+void
+cclean(void)
+{
+ Gen2 g2;
+
+ g2.from = nullgen;
+ g2.to = nullgen;
+ outcode(AEND, &g2);
+ Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+ Bputc(&obuf, ANAME); /* as(2) */
+ Bputc(&obuf, ANAME>>8);
+ Bputc(&obuf, t); /* type */
+ Bputc(&obuf, s); /* sym */
+ while(*n) {
+ Bputc(&obuf, *n);
+ n++;
+ }
+ Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+ long l;
+ int i, t;
+ char *n;
+ Ieee e;
+
+ t = 0;
+ if(a->index != D_NONE || a->scale != 0)
+ t |= T_INDEX;
+ if(a->offset != 0) {
+ t |= T_OFFSET;
+ l = a->offset;
+ if((vlong)l != a->offset)
+ t |= T_64;
+ }
+ if(s != 0)
+ t |= T_SYM;
+
+ switch(a->type) {
+ default:
+ t |= T_TYPE;
+ break;
+ case D_FCONST:
+ t |= T_FCONST;
+ break;
+ case D_SCONST:
+ t |= T_SCONST;
+ break;
+ case D_NONE:
+ break;
+ }
+ Bputc(&obuf, t);
+
+ if(t & T_INDEX) { /* implies index, scale */
+ Bputc(&obuf, a->index);
+ Bputc(&obuf, a->scale);
+ }
+ if(t & T_OFFSET) { /* implies offset */
+ l = a->offset;
+ Bputc(&obuf, l);
+ Bputc(&obuf, l>>8);
+ Bputc(&obuf, l>>16);
+ Bputc(&obuf, l>>24);
+ if(t & T_64) {
+ l = a->offset>>32;
+ Bputc(&obuf, l);
+ Bputc(&obuf, l>>8);
+ Bputc(&obuf, l>>16);
+ Bputc(&obuf, l>>24);
+ }
+ }
+ if(t & T_SYM) /* implies sym */
+ Bputc(&obuf, s);
+ if(t & T_FCONST) {
+ ieeedtod(&e, a->dval);
+ l = e.l;
+ Bputc(&obuf, l);
+ Bputc(&obuf, l>>8);
+ Bputc(&obuf, l>>16);
+ Bputc(&obuf, l>>24);
+ l = e.h;
+ Bputc(&obuf, l);
+ Bputc(&obuf, l>>8);
+ Bputc(&obuf, l>>16);
+ Bputc(&obuf, l>>24);
+ return;
+ }
+ if(t & T_SCONST) {
+ n = a->sval;
+ for(i=0; i<NSNAME; i++) {
+ Bputc(&obuf, *n);
+ n++;
+ }
+ return;
+ }
+ if(t & T_TYPE)
+ Bputc(&obuf, a->type);
+}
+
+void
+outcode(int a, Gen2 *g2)
+{
+ int sf, st, t;
+ Sym *s;
+
+ if(pass == 1)
+ goto out;
+
+jackpot:
+ sf = 0;
+ s = g2->from.sym;
+ while(s != S) {
+ sf = s->sym;
+ if(sf < 0 || sf >= NSYM)
+ sf = 0;
+ t = g2->from.type;
+ if(t == D_ADDR)
+ t = g2->from.index;
+ if(h[sf].type == t)
+ if(h[sf].sym == s)
+ break;
+ zname(s->name, t, sym);
+ s->sym = sym;
+ h[sym].sym = s;
+ h[sym].type = t;
+ sf = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ break;
+ }
+ st = 0;
+ s = g2->to.sym;
+ while(s != S) {
+ st = s->sym;
+ if(st < 0 || st >= NSYM)
+ st = 0;
+ t = g2->to.type;
+ if(t == D_ADDR)
+ t = g2->to.index;
+ if(h[st].type == t)
+ if(h[st].sym == s)
+ break;
+ zname(s->name, t, sym);
+ s->sym = sym;
+ h[sym].sym = s;
+ h[sym].type = t;
+ st = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ if(st == sf)
+ goto jackpot;
+ break;
+ }
+ Bputc(&obuf, a);
+ Bputc(&obuf, a>>8);
+ Bputc(&obuf, lineno);
+ Bputc(&obuf, lineno>>8);
+ Bputc(&obuf, lineno>>16);
+ Bputc(&obuf, lineno>>24);
+ zaddr(&g2->from, sf);
+ zaddr(&g2->to, st);
+
+out:
+ if(a != AGLOBL && a != ADATA && a != AMODE)
+ pc++;
+}
+
+void
+outhist(void)
+{
+ Gen g;
+ Hist *h;
+ char *p, *q, *op, c;
+ int n;
+
+ g = nullgen;
+ c = pathchar();
+ for(h = hist; h != H; h = h->link) {
+ p = h->name;
+ op = 0;
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && p && p[1] == ':'){
+ p += 2;
+ c = *p;
+ }
+ if(p && p[0] != c && h->offset == 0 && pathname){
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && pathname[1] == ':') {
+ op = p;
+ p = pathname+2;
+ c = *p;
+ } else if(pathname[0] == c){
+ op = p;
+ p = pathname;
+ }
+ }
+ while(p) {
+ q = strchr(p, c);
+ if(q) {
+ n = q-p;
+ if(n == 0){
+ n = 1; /* leading "/" */
+ *p = '/'; /* don't emit "\" on windows */
+ }
+ q++;
+ } else {
+ n = strlen(p);
+ q = 0;
+ }
+ if(n) {
+ Bputc(&obuf, ANAME);
+ Bputc(&obuf, ANAME>>8);
+ Bputc(&obuf, D_FILE); /* type */
+ Bputc(&obuf, 1); /* sym */
+ Bputc(&obuf, '<');
+ Bwrite(&obuf, p, n);
+ Bputc(&obuf, 0);
+ }
+ p = q;
+ if(p == 0 && op) {
+ p = op;
+ op = 0;
+ }
+ }
+ g.offset = h->offset;
+
+ Bputc(&obuf, AHISTORY);
+ Bputc(&obuf, AHISTORY>>8);
+ Bputc(&obuf, h->line);
+ Bputc(&obuf, h->line>>8);
+ Bputc(&obuf, h->line>>16);
+ Bputc(&obuf, h->line>>24);
+ zaddr(&nullgen, 0);
+ zaddr(&g, 0);
+ }
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/6a/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=6a
+
+OFILES=\
+ y.tab.$O\
+ lex.$O\
+
+HFILES=\
+ ../6c/6.out.h\
+ y.tab.h\
+ a.h\
+
+YFILES=a.y\
+
+LIBS=cc bio 9 # order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS= $CFLAGS -I../include
+
+lex.$O: ../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+ cd ../cc
+ mk $MKFLAGS install
+ mk $MKFLAGS clean
--- /dev/null
+++ b/utils/6c/6.out.h
@@ -1,0 +1,820 @@
+#define NSYM 50
+#define NSNAME 8
+#define NOPROF (1<<0)
+#define DUPOK (1<<1)
+
+/*
+ * amd64
+ */
+
+enum as
+{
+ AXXX,
+ AAAA,
+ AAAD,
+ AAAM,
+ AAAS,
+ AADCB,
+ AADCL,
+ AADCW,
+ AADDB,
+ AADDL,
+ AADDW,
+ AADJSP,
+ AANDB,
+ AANDL,
+ AANDW,
+ AARPL,
+ ABOUNDL,
+ ABOUNDW,
+ ABSFL,
+ ABSFW,
+ ABSRL,
+ ABSRW,
+ ABTL,
+ ABTW,
+ ABTCL,
+ ABTCW,
+ ABTRL,
+ ABTRW,
+ ABTSL,
+ ABTSW,
+ ABYTE,
+ ACALL,
+ ACLC,
+ ACLD,
+ ACLI,
+ ACLTS,
+ ACMC,
+ ACMPB,
+ ACMPL,
+ ACMPW,
+ ACMPSB,
+ ACMPSL,
+ ACMPSW,
+ ADAA,
+ ADAS,
+ ADATA,
+ ADECB,
+ ADECL,
+ ADECQ,
+ ADECW,
+ ADIVB,
+ ADIVL,
+ ADIVW,
+ AENTER,
+ AGLOBL,
+ AGOK,
+ AHISTORY,
+ AHLT,
+ AIDIVB,
+ AIDIVL,
+ AIDIVW,
+ AIMULB,
+ AIMULL,
+ AIMULW,
+ AINB,
+ AINL,
+ AINW,
+ AINCB,
+ AINCL,
+ AINCQ,
+ AINCW,
+ AINSB,
+ AINSL,
+ AINSW,
+ AINT,
+ AINTO,
+ AIRETL,
+ AIRETW,
+ AJCC,
+ AJCS,
+ AJCXZ,
+ AJEQ,
+ AJGE,
+ AJGT,
+ AJHI,
+ AJLE,
+ AJLS,
+ AJLT,
+ AJMI,
+ AJMP,
+ AJNE,
+ AJOC,
+ AJOS,
+ AJPC,
+ AJPL,
+ AJPS,
+ ALAHF,
+ ALARL,
+ ALARW,
+ ALEAL,
+ ALEAW,
+ ALEAVEL,
+ ALEAVEW,
+ ALOCK,
+ ALODSB,
+ ALODSL,
+ ALODSW,
+ ALONG,
+ ALOOP,
+ ALOOPEQ,
+ ALOOPNE,
+ ALSLL,
+ ALSLW,
+ AMOVB,
+ AMOVL,
+ AMOVW,
+ AMOVBLSX,
+ AMOVBLZX,
+ AMOVBQSX,
+ AMOVBQZX,
+ AMOVBWSX,
+ AMOVBWZX,
+ AMOVWLSX,
+ AMOVWLZX,
+ AMOVWQSX,
+ AMOVWQZX,
+ AMOVSB,
+ AMOVSL,
+ AMOVSW,
+ AMULB,
+ AMULL,
+ AMULW,
+ ANAME,
+ ANEGB,
+ ANEGL,
+ ANEGW,
+ ANOP,
+ ANOTB,
+ ANOTL,
+ ANOTW,
+ AORB,
+ AORL,
+ AORW,
+ AOUTB,
+ AOUTL,
+ AOUTW,
+ AOUTSB,
+ AOUTSL,
+ AOUTSW,
+ APOPAL,
+ APOPAW,
+ APOPFL,
+ APOPFW,
+ APOPL,
+ APOPW,
+ APUSHAL,
+ APUSHAW,
+ APUSHFL,
+ APUSHFW,
+ APUSHL,
+ APUSHW,
+ ARCLB,
+ ARCLL,
+ ARCLW,
+ ARCRB,
+ ARCRL,
+ ARCRW,
+ AREP,
+ AREPN,
+ ARET,
+ AROLB,
+ AROLL,
+ AROLW,
+ ARORB,
+ ARORL,
+ ARORW,
+ ASAHF,
+ ASALB,
+ ASALL,
+ ASALW,
+ ASARB,
+ ASARL,
+ ASARW,
+ ASBBB,
+ ASBBL,
+ ASBBW,
+ ASCASB,
+ ASCASL,
+ ASCASW,
+ ASETCC,
+ ASETCS,
+ ASETEQ,
+ ASETGE,
+ ASETGT,
+ ASETHI,
+ ASETLE,
+ ASETLS,
+ ASETLT,
+ ASETMI,
+ ASETNE,
+ ASETOC,
+ ASETOS,
+ ASETPC,
+ ASETPL,
+ ASETPS,
+ ACDQ,
+ ACWD,
+ ASHLB,
+ ASHLL,
+ ASHLW,
+ ASHRB,
+ ASHRL,
+ ASHRW,
+ ASTC,
+ ASTD,
+ ASTI,
+ ASTOSB,
+ ASTOSL,
+ ASTOSW,
+ ASUBB,
+ ASUBL,
+ ASUBW,
+ ASYSCALL,
+ ATESTB,
+ ATESTL,
+ ATESTW,
+ ATEXT,
+ AVERR,
+ AVERW,
+ AWAIT,
+ AWORD,
+ AXCHGB,
+ AXCHGL,
+ AXCHGW,
+ AXLAT,
+ AXORB,
+ AXORL,
+ AXORW,
+
+ AFMOVB,
+ AFMOVBP,
+ AFMOVD,
+ AFMOVDP,
+ AFMOVF,
+ AFMOVFP,
+ AFMOVL,
+ AFMOVLP,
+ AFMOVV,
+ AFMOVVP,
+ AFMOVW,
+ AFMOVWP,
+ AFMOVX,
+ AFMOVXP,
+
+ AFCOMB,
+ AFCOMBP,
+ AFCOMD,
+ AFCOMDP,
+ AFCOMDPP,
+ AFCOMF,
+ AFCOMFP,
+ AFCOML,
+ AFCOMLP,
+ AFCOMW,
+ AFCOMWP,
+ AFUCOM,
+ AFUCOMP,
+ AFUCOMPP,
+
+ AFADDDP,
+ AFADDW,
+ AFADDL,
+ AFADDF,
+ AFADDD,
+
+ AFMULDP,
+ AFMULW,
+ AFMULL,
+ AFMULF,
+ AFMULD,
+
+ AFSUBDP,
+ AFSUBW,
+ AFSUBL,
+ AFSUBF,
+ AFSUBD,
+
+ AFSUBRDP,
+ AFSUBRW,
+ AFSUBRL,
+ AFSUBRF,
+ AFSUBRD,
+
+ AFDIVDP,
+ AFDIVW,
+ AFDIVL,
+ AFDIVF,
+ AFDIVD,
+
+ AFDIVRDP,
+ AFDIVRW,
+ AFDIVRL,
+ AFDIVRF,
+ AFDIVRD,
+
+ AFXCHD,
+ AFFREE,
+
+ AFLDCW,
+ AFLDENV,
+ AFRSTOR,
+ AFSAVE,
+ AFSTCW,
+ AFSTENV,
+ AFSTSW,
+
+ AF2XM1,
+ AFABS,
+ AFCHS,
+ AFCLEX,
+ AFCOS,
+ AFDECSTP,
+ AFINCSTP,
+ AFINIT,
+ AFLD1,
+ AFLDL2E,
+ AFLDL2T,
+ AFLDLG2,
+ AFLDLN2,
+ AFLDPI,
+ AFLDZ,
+ AFNOP,
+ AFPATAN,
+ AFPREM,
+ AFPREM1,
+ AFPTAN,
+ AFRNDINT,
+ AFSCALE,
+ AFSIN,
+ AFSINCOS,
+ AFSQRT,
+ AFTST,
+ AFXAM,
+ AFXTRACT,
+ AFYL2X,
+ AFYL2XP1,
+
+ AEND,
+
+ ADYNT,
+ AINIT,
+
+ ASIGNAME,
+
+ /* extra 32-bit operations */
+ ACMPXCHGB,
+ ACMPXCHGL,
+ ACMPXCHGW,
+ ACMPXCHG8B,
+ ACPUID,
+ AINVD,
+ AINVLPG,
+ ALFENCE,
+ AMFENCE,
+ AMOVNTIL,
+ ARDMSR,
+ ARDPMC,
+ ARDTSC,
+ ARSM,
+ ASFENCE,
+ ASYSRET,
+ AWBINVD,
+ AWRMSR,
+ AXADDB,
+ AXADDL,
+ AXADDW,
+
+ /* conditional move */
+ ACMOVLCC,
+ ACMOVLCS,
+ ACMOVLEQ,
+ ACMOVLGE,
+ ACMOVLGT,
+ ACMOVLHI,
+ ACMOVLLE,
+ ACMOVLLS,
+ ACMOVLLT,
+ ACMOVLMI,
+ ACMOVLNE,
+ ACMOVLOC,
+ ACMOVLOS,
+ ACMOVLPC,
+ ACMOVLPL,
+ ACMOVLPS,
+ ACMOVQCC,
+ ACMOVQCS,
+ ACMOVQEQ,
+ ACMOVQGE,
+ ACMOVQGT,
+ ACMOVQHI,
+ ACMOVQLE,
+ ACMOVQLS,
+ ACMOVQLT,
+ ACMOVQMI,
+ ACMOVQNE,
+ ACMOVQOC,
+ ACMOVQOS,
+ ACMOVQPC,
+ ACMOVQPL,
+ ACMOVQPS,
+ ACMOVWCC,
+ ACMOVWCS,
+ ACMOVWEQ,
+ ACMOVWGE,
+ ACMOVWGT,
+ ACMOVWHI,
+ ACMOVWLE,
+ ACMOVWLS,
+ ACMOVWLT,
+ ACMOVWMI,
+ ACMOVWNE,
+ ACMOVWOC,
+ ACMOVWOS,
+ ACMOVWPC,
+ ACMOVWPL,
+ ACMOVWPS,
+
+ /* 64-bit */
+ AADCQ,
+ AADDQ,
+ AANDQ,
+ ABSFQ,
+ ABSRQ,
+ ABTCQ,
+ ABTQ,
+ ABTRQ,
+ ABTSQ,
+ ACMPQ,
+ ACMPSQ,
+ ACMPXCHGQ,
+ ACQO,
+ ADIVQ,
+ AIDIVQ,
+ AIMULQ,
+ AIRETQ,
+ ALEAQ,
+ ALEAVEQ,
+ ALODSQ,
+ AMOVQ,
+ AMOVLQSX,
+ AMOVLQZX,
+ AMOVNTIQ,
+ AMOVSQ,
+ AMULQ,
+ ANEGQ,
+ ANOTQ,
+ AORQ,
+ APOPFQ,
+ APOPQ,
+ APUSHFQ,
+ APUSHQ,
+ ARCLQ,
+ ARCRQ,
+ AROLQ,
+ ARORQ,
+ AQUAD,
+ ASALQ,
+ ASARQ,
+ ASBBQ,
+ ASCASQ,
+ ASHLQ,
+ ASHRQ,
+ ASTOSQ,
+ ASUBQ,
+ ATESTQ,
+ AXADDQ,
+ AXCHGQ,
+ AXORQ,
+
+ /* media */
+ AADDPD,
+ AADDPS,
+ AADDSD,
+ AADDSS,
+ AANDNPD,
+ AANDNPS,
+ AANDPD,
+ AANDPS,
+ ACMPPD,
+ ACMPPS,
+ ACMPSD,
+ ACMPSS,
+ ACOMISD,
+ ACOMISS,
+ ACVTPD2PL,
+ ACVTPD2PS,
+ ACVTPL2PD,
+ ACVTPL2PS,
+ ACVTPS2PD,
+ ACVTPS2PL,
+ ACVTSD2SL,
+ ACVTSD2SQ,
+ ACVTSD2SS,
+ ACVTSL2SD,
+ ACVTSL2SS,
+ ACVTSQ2SD,
+ ACVTSQ2SS,
+ ACVTSS2SD,
+ ACVTSS2SL,
+ ACVTSS2SQ,
+ ACVTTPD2PL,
+ ACVTTPS2PL,
+ ACVTTSD2SL,
+ ACVTTSD2SQ,
+ ACVTTSS2SL,
+ ACVTTSS2SQ,
+ ADIVPD,
+ ADIVPS,
+ ADIVSD,
+ ADIVSS,
+ AEMMS,
+ AFXRSTOR,
+ AFXRSTOR64,
+ AFXSAVE,
+ AFXSAVE64,
+ ALDMXCSR,
+ AMASKMOVOU,
+ AMASKMOVQ,
+ AMAXPD,
+ AMAXPS,
+ AMAXSD,
+ AMAXSS,
+ AMINPD,
+ AMINPS,
+ AMINSD,
+ AMINSS,
+ AMOVAPD,
+ AMOVAPS,
+ AMOVOU,
+ AMOVHLPS,
+ AMOVHPD,
+ AMOVHPS,
+ AMOVLHPS,
+ AMOVLPD,
+ AMOVLPS,
+ AMOVMSKPD,
+ AMOVMSKPS,
+ AMOVNTO,
+ AMOVNTPD,
+ AMOVNTPS,
+ AMOVNTQ,
+ AMOVO,
+ AMOVQOZX,
+ AMOVSD,
+ AMOVSS,
+ AMOVUPD,
+ AMOVUPS,
+ AMULPD,
+ AMULPS,
+ AMULSD,
+ AMULSS,
+ AORPD,
+ AORPS,
+ APACKSSLW,
+ APACKSSWB,
+ APACKUSWB,
+ APADDB,
+ APADDL,
+ APADDQ,
+ APADDSB,
+ APADDSW,
+ APADDUSB,
+ APADDUSW,
+ APADDW,
+ APANDB,
+ APANDL,
+ APANDSB,
+ APANDSW,
+ APANDUSB,
+ APANDUSW,
+ APANDW,
+ APAND,
+ APANDN,
+ APAVGB,
+ APAVGW,
+ APCMPEQB,
+ APCMPEQL,
+ APCMPEQW,
+ APCMPGTB,
+ APCMPGTL,
+ APCMPGTW,
+ APEXTRW,
+ APFACC,
+ APFADD,
+ APFCMPEQ,
+ APFCMPGE,
+ APFCMPGT,
+ APFMAX,
+ APFMIN,
+ APFMUL,
+ APFNACC,
+ APFPNACC,
+ APFRCP,
+ APFRCPIT1,
+ APFRCPI2T,
+ APFRSQIT1,
+ APFRSQRT,
+ APFSUB,
+ APFSUBR,
+ APINSRW,
+ APMADDWL,
+ APMAXSW,
+ APMAXUB,
+ APMINSW,
+ APMINUB,
+ APMOVMSKB,
+ APMULHRW,
+ APMULHUW,
+ APMULHW,
+ APMULLW,
+ APMULULQ,
+ APOR,
+ APSADBW,
+ APSHUFHW,
+ APSHUFL,
+ APSHUFLW,
+ APSHUFW,
+ APSLLO,
+ APSLLL,
+ APSLLQ,
+ APSLLW,
+ APSRAL,
+ APSRAW,
+ APSRLO,
+ APSRLL,
+ APSRLQ,
+ APSRLW,
+ APSUBB,
+ APSUBL,
+ APSUBQ,
+ APSUBSB,
+ APSUBSW,
+ APSUBUSB,
+ APSUBUSW,
+ APSUBW,
+ APSWAPL,
+ APUNPCKHBW,
+ APUNPCKHLQ,
+ APUNPCKHQDQ,
+ APUNPCKHWL,
+ APUNPCKLBW,
+ APUNPCKLLQ,
+ APUNPCKLQDQ,
+ APUNPCKLWL,
+ APXOR,
+ ARCPPS,
+ ARCPSS,
+ ARSQRTPS,
+ ARSQRTSS,
+ ASHUFPD,
+ ASHUFPS,
+ ASQRTPD,
+ ASQRTPS,
+ ASQRTSD,
+ ASQRTSS,
+ ASTMXCSR,
+ ASUBPD,
+ ASUBPS,
+ ASUBSD,
+ ASUBSS,
+ AUCOMISD,
+ AUCOMISS,
+ AUNPCKHPD,
+ AUNPCKHPS,
+ AUNPCKLPD,
+ AUNPCKLPS,
+ AXORPD,
+ AXORPS,
+
+ APF2IW,
+ APF2IL,
+ API2FW,
+ API2FL,
+ ARETFW,
+ ARETFL,
+ ARETFQ,
+ ASWAPGS,
+
+ AMODE,
+
+ ALAST
+};
+
+enum
+{
+
+ D_AL = 0,
+ D_CL,
+ D_DL,
+ D_BL,
+ D_SPB,
+ D_BPB,
+ D_SIB,
+ D_DIB,
+ D_R8B,
+ D_R9B,
+ D_R10B,
+ D_R11B,
+ D_R12B,
+ D_R13B,
+ D_R14B,
+ D_R15B,
+
+ D_AX = 16,
+ D_CX,
+ D_DX,
+ D_BX,
+ D_SP,
+ D_BP,
+ D_SI,
+ D_DI,
+ D_R8,
+ D_R9,
+ D_R10,
+ D_R11,
+ D_R12,
+ D_R13,
+ D_R14,
+ D_R15,
+
+ D_AH = 32,
+ D_CH,
+ D_DH,
+ D_BH,
+
+ D_F0 = 36,
+
+ D_M0 = 44,
+
+ D_X0 = 52,
+
+ D_CS = 68,
+ D_SS,
+ D_DS,
+ D_ES,
+ D_FS,
+ D_GS,
+
+ D_GDTR, /* global descriptor table register */
+ D_IDTR, /* interrupt descriptor table register */
+ D_LDTR, /* local descriptor table register */
+ D_MSW, /* machine status word */
+ D_TASK, /* task register */
+
+ D_CR = 79,
+ D_DR = 95,
+ D_TR = 103,
+
+ D_NONE = 111,
+
+ D_BRANCH = 112,
+ D_EXTERN = 113,
+ D_STATIC = 114,
+ D_AUTO = 115,
+ D_PARAM = 116,
+ D_CONST = 117,
+ D_FCONST = 118,
+ D_SCONST = 119,
+ D_ADDR = 120,
+
+ D_FILE,
+ D_FILE1,
+
+ D_INDIR, /* additive */
+
+ T_TYPE = 1<<0,
+ T_INDEX = 1<<1,
+ T_OFFSET = 1<<2,
+ T_FCONST = 1<<3,
+ T_SYM = 1<<4,
+ T_SCONST = 1<<5,
+ T_64 = 1<<6,
+
+ REGARG = D_BP, /* MIGHT CHANGE */
+ REGRET = D_AX,
+ FREGRET = D_X0,
+ REGSP = D_SP,
+ REGTMP = D_DI,
+ REGEXT = D_R15, /* compiler allocates external registers R15 down */
+ FREGMIN = D_X0+5, /* first register variable */
+ FREGEXT = D_X0+7 /* first external register */
+};
+
+/*
+ * this is the ranlib header
+ */
+#define SYMDEF "__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef struct ieee Ieee;
+struct ieee
+{
+ long l; /* contains ls-man 0xffffffff */
+ long h; /* contains sign 0x80000000
+ exp 0x7ff00000
+ ms-man 0x000fffff */
+};
--- /dev/null
+++ b/utils/6c/cgen.c
@@ -1,0 +1,1942 @@
+#include "gc.h"
+
+/* ,x/^(print|prtree)\(/i/\/\/ */
+int castup(Type*, Type*);
+
+void
+cgen(Node *n, Node *nn)
+{
+ Node *l, *r, *t;
+ Prog *p1;
+ Node nod, nod1, nod2, nod3, nod4;
+ int o, hardleft;
+ long v, curs;
+ vlong c;
+
+ if(debug['g']) {
+ prtree(nn, "cgen lhs");
+ prtree(n, "cgen");
+ }
+ if(n == Z || n->type == T)
+ return;
+ if(typesu[n->type->etype]) {
+ sugen(n, nn, n->type->width);
+ return;
+ }
+ l = n->left;
+ r = n->right;
+ o = n->op;
+ if(n->addable >= INDEXED) {
+ if(nn == Z) {
+ switch(o) {
+ default:
+ nullwarn(Z, Z);
+ break;
+ case OINDEX:
+ nullwarn(l, r);
+ break;
+ }
+ return;
+ }
+ gmove(n, nn);
+ return;
+ }
+ curs = cursafe;
+
+ if(l->complex >= FNX)
+ if(r != Z && r->complex >= FNX)
+ switch(o) {
+ default:
+ if(cond(o) && typesu[l->type->etype])
+ break;
+
+ regret(&nod, r);
+ cgen(r, &nod);
+
+ regsalloc(&nod1, r);
+ gmove(&nod, &nod1);
+
+ regfree(&nod);
+ nod = *n;
+ nod.right = &nod1;
+
+ cgen(&nod, nn);
+ return;
+
+ case OFUNC:
+ case OCOMMA:
+ case OANDAND:
+ case OOROR:
+ case OCOND:
+ case ODOT:
+ break;
+ }
+
+ hardleft = l->addable < INDEXED || l->complex >= FNX;
+ switch(o) {
+ default:
+ diag(n, "unknown op in cgen: %O", o);
+ break;
+
+ case ONEG:
+ case OCOM:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ gopcode(o, n->type, Z, &nod);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+
+ case OAS:
+ if(l->op == OBIT)
+ goto bitas;
+ if(!hardleft) {
+ if(nn != Z || r->addable < INDEXED || hardconst(r)) {
+ if(r->complex >= FNX && nn == Z)
+ regret(&nod, r);
+ else
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ gmove(&nod, l);
+ if(nn != Z)
+ gmove(&nod, nn);
+ regfree(&nod);
+ } else
+ gmove(r, l);
+ break;
+ }
+ if(l->complex >= r->complex) {
+ if(l->op == OINDEX && immconst(r)) {
+ gmove(r, l);
+ break;
+ }
+ reglcgen(&nod1, l, Z);
+ if(r->addable >= INDEXED && !hardconst(r)) {
+ gmove(r, &nod1);
+ if(nn != Z)
+ gmove(r, nn);
+ regfree(&nod1);
+ break;
+ }
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ reglcgen(&nod1, l, Z);
+ }
+ gmove(&nod, &nod1);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ bitas:
+ n = l->left;
+ regalloc(&nod, r, nn);
+ if(l->complex >= r->complex) {
+ reglcgen(&nod1, n, Z);
+ cgen(r, &nod);
+ } else {
+ cgen(r, &nod);
+ reglcgen(&nod1, n, Z);
+ }
+ regalloc(&nod2, n, Z);
+ gmove(&nod1, &nod2);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+
+ case OBIT:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ bitload(n, &nod, Z, Z, nn);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+
+ case OLSHR:
+ case OASHL:
+ case OASHR:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ if(r->op == OCONST) {
+ if(r->vconst == 0) {
+ cgen(l, nn);
+ break;
+ }
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ if(o == OASHL && r->vconst == 1)
+ gopcode(OADD, n->type, &nod, &nod);
+ else
+ gopcode(o, n->type, r, &nod);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+ }
+
+ /*
+ * get nod to be D_CX
+ */
+ if(nodreg(&nod, nn, D_CX)) {
+ regsalloc(&nod1, n);
+ gmove(&nod, &nod1);
+ cgen(n, &nod); /* probably a bug */
+ gmove(&nod, nn);
+ gmove(&nod1, &nod);
+ break;
+ }
+ reg[D_CX]++;
+ if(nn->op == OREGISTER && nn->reg == D_CX)
+ regalloc(&nod1, l, Z);
+ else
+ regalloc(&nod1, l, nn);
+ if(r->complex >= l->complex) {
+ cgen(r, &nod);
+ cgen(l, &nod1);
+ } else {
+ cgen(l, &nod1);
+ cgen(r, &nod);
+ }
+ gopcode(o, n->type, &nod, &nod1);
+ gmove(&nod1, nn);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ case OADD:
+ case OSUB:
+ case OOR:
+ case OXOR:
+ case OAND:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ if(typefd[n->type->etype])
+ goto fop;
+ if(r->op == OCONST) {
+ if(r->vconst == 0 && o != OAND) {
+ cgen(l, nn);
+ break;
+ }
+ }
+ if(n->op == OADD && l->op == OASHL && l->right->op == OCONST
+ && (r->op != OCONST || r->vconst < -128 || r->vconst > 127)) {
+ c = l->right->vconst;
+ if(c > 0 && c <= 3) {
+ if(l->left->complex >= r->complex) {
+ regalloc(&nod, l->left, nn);
+ cgen(l->left, &nod);
+ if(r->addable < INDEXED) {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ genmuladd(&nod, &nod, 1 << c, &nod1);
+ regfree(&nod1);
+ }
+ else
+ genmuladd(&nod, &nod, 1 << c, r);
+ }
+ else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ regalloc(&nod1, l->left, Z);
+ cgen(l->left, &nod1);
+ genmuladd(&nod, &nod1, 1 << c, &nod);
+ regfree(&nod1);
+ }
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+ }
+ }
+ if(r->addable >= INDEXED && !hardconst(r)) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ gopcode(o, n->type, r, &nod);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+ }
+ if(l->complex >= r->complex) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ gopcode(o, n->type, &nod1, &nod);
+ } else {
+ regalloc(&nod1, r, nn);
+ cgen(r, &nod1);
+ regalloc(&nod, l, Z);
+ cgen(l, &nod);
+ gopcode(o, n->type, &nod1, &nod);
+ }
+ gmove(&nod, nn);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ case OLMOD:
+ case OMOD:
+ case OLMUL:
+ case OLDIV:
+ case OMUL:
+ case ODIV:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ if(typefd[n->type->etype])
+ goto fop;
+ if(r->op == OCONST && typechl[n->type->etype]) { /* TO DO */
+ SET(v);
+ switch(o) {
+ case ODIV:
+ case OMOD:
+ c = r->vconst;
+ if(c < 0)
+ c = -c;
+ v = log2(c);
+ if(v < 0)
+ break;
+ /* fall thru */
+ case OMUL:
+ case OLMUL:
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ switch(o) {
+ case OMUL:
+ case OLMUL:
+ mulgen(n->type, r, &nod);
+ break;
+ case ODIV:
+ sdiv2(r->vconst, v, l, &nod);
+ break;
+ case OMOD:
+ smod2(r->vconst, v, l, &nod);
+ break;
+ }
+ gmove(&nod, nn);
+ regfree(&nod);
+ goto done;
+ case OLDIV:
+ c = r->vconst;
+ if((c & 0x80000000) == 0)
+ break;
+ regalloc(&nod1, l, Z);
+ cgen(l, &nod1);
+ regalloc(&nod, l, nn);
+ zeroregm(&nod);
+ gins(ACMPL, &nod1, nodconst(c));
+ gins(ASBBL, nodconst(-1), &nod);
+ regfree(&nod1);
+ gmove(&nod, nn);
+ regfree(&nod);
+ goto done;
+ }
+ }
+
+ if(o == OMUL) {
+ if(l->addable >= INDEXED) {
+ t = l;
+ l = r;
+ r = t;
+ }
+ /* should favour AX */
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ if(r->addable < INDEXED || hardconst(r)) {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ gopcode(OMUL, n->type, &nod1, &nod);
+ regfree(&nod1);
+ }else
+ gopcode(OMUL, n->type, r, &nod); /* addressible */
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+ }
+
+ /*
+ * get nod to be D_AX
+ * get nod1 to be D_DX
+ */
+ if(nodreg(&nod, nn, D_AX)) {
+ regsalloc(&nod2, n);
+ gmove(&nod, &nod2);
+ v = reg[D_AX];
+ reg[D_AX] = 0;
+
+ if(isreg(l, D_AX)) {
+ nod3 = *n;
+ nod3.left = &nod2;
+ cgen(&nod3, nn);
+ } else
+ if(isreg(r, D_AX)) {
+ nod3 = *n;
+ nod3.right = &nod2;
+ cgen(&nod3, nn);
+ } else
+ cgen(n, nn);
+
+ gmove(&nod2, &nod);
+ reg[D_AX] = v;
+ break;
+ }
+ if(nodreg(&nod1, nn, D_DX)) {
+ regsalloc(&nod2, n);
+ gmove(&nod1, &nod2);
+ v = reg[D_DX];
+ reg[D_DX] = 0;
+
+ if(isreg(l, D_DX)) {
+ nod3 = *n;
+ nod3.left = &nod2;
+ cgen(&nod3, nn);
+ } else
+ if(isreg(r, D_DX)) {
+ nod3 = *n;
+ nod3.right = &nod2;
+ cgen(&nod3, nn);
+ } else
+ cgen(n, nn);
+
+ gmove(&nod2, &nod1);
+ reg[D_DX] = v;
+ break;
+ }
+ reg[D_AX]++;
+
+ if(r->op == OCONST && (o == ODIV || o == OLDIV) && immconst(r) && typechl[r->type->etype]) {
+ reg[D_DX]++;
+ if(l->addable < INDEXED) {
+ regalloc(&nod2, l, Z);
+ cgen(l, &nod2);
+ l = &nod2;
+ }
+ if(o == ODIV)
+ sdivgen(l, r, &nod, &nod1);
+ else
+ udivgen(l, r, &nod, &nod1);
+ gmove(&nod1, nn);
+ if(l == &nod2)
+ regfree(l);
+ goto freeaxdx;
+ }
+
+ if(l->complex >= r->complex) {
+ cgen(l, &nod);
+ reg[D_DX]++;
+ if(o == ODIV || o == OMOD)
+ gins(typechl[l->type->etype]? ACDQ: ACQO, Z, Z);
+ if(o == OLDIV || o == OLMOD)
+ zeroregm(&nod1);
+ if(r->addable < INDEXED || r->op == OCONST) {
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ gopcode(o, n->type, &nod3, Z);
+ regfree(&nod3);
+ } else
+ gopcode(o, n->type, r, Z);
+ } else {
+ regsalloc(&nod3, r);
+ cgen(r, &nod3);
+ cgen(l, &nod);
+ reg[D_DX]++;
+ if(o == ODIV || o == OMOD)
+ gins(typechl[l->type->etype]? ACDQ: ACQO, Z, Z);
+ if(o == OLDIV || o == OLMOD)
+ zeroregm(&nod1);
+ gopcode(o, n->type, &nod3, Z);
+ }
+ if(o == OMOD || o == OLMOD)
+ gmove(&nod1, nn);
+ else
+ gmove(&nod, nn);
+ freeaxdx:
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ case OASLSHR:
+ case OASASHL:
+ case OASASHR:
+ if(r->op == OCONST)
+ goto asand;
+ if(l->op == OBIT)
+ goto asbitop;
+ if(typefd[n->type->etype])
+ goto asand; /* can this happen? */
+
+ /*
+ * get nod to be D_CX
+ */
+ if(nodreg(&nod, nn, D_CX)) {
+ regsalloc(&nod1, n);
+ gmove(&nod, &nod1);
+ cgen(n, &nod);
+ if(nn != Z)
+ gmove(&nod, nn);
+ gmove(&nod1, &nod);
+ break;
+ }
+ reg[D_CX]++;
+
+ if(r->complex >= l->complex) {
+ cgen(r, &nod);
+ if(hardleft)
+ reglcgen(&nod1, l, Z);
+ else
+ nod1 = *l;
+ } else {
+ if(hardleft)
+ reglcgen(&nod1, l, Z);
+ else
+ nod1 = *l;
+ cgen(r, &nod);
+ }
+
+ gopcode(o, l->type, &nod, &nod1);
+ regfree(&nod);
+ if(nn != Z)
+ gmove(&nod1, nn);
+ if(hardleft)
+ regfree(&nod1);
+ break;
+
+ case OASAND:
+ case OASADD:
+ case OASSUB:
+ case OASXOR:
+ case OASOR:
+ asand:
+ if(l->op == OBIT)
+ goto asbitop;
+ if(typefd[l->type->etype] || typefd[r->type->etype])
+ goto asfop;
+ if(l->complex >= r->complex) {
+ if(hardleft)
+ reglcgen(&nod, l, Z);
+ else
+ nod = *l;
+ if(!immconst(r)) {
+ regalloc(&nod1, r, nn);
+ cgen(r, &nod1);
+ gopcode(o, l->type, &nod1, &nod);
+ regfree(&nod1);
+ } else
+ gopcode(o, l->type, r, &nod);
+ } else {
+ regalloc(&nod1, r, nn);
+ cgen(r, &nod1);
+ if(hardleft)
+ reglcgen(&nod, l, Z);
+ else
+ nod = *l;
+ gopcode(o, l->type, &nod1, &nod);
+ regfree(&nod1);
+ }
+ if(nn != Z)
+ gmove(&nod, nn);
+ if(hardleft)
+ regfree(&nod);
+ break;
+
+ asfop:
+ if(l->complex >= r->complex) {
+ if(hardleft)
+ reglcgen(&nod, l, Z);
+ else
+ nod = *l;
+ if(r->addable < INDEXED){
+ regalloc(&nod1, r, nn);
+ cgen(r, &nod1);
+ }else
+ nod1 = *r;
+ regalloc(&nod2, r, Z);
+ gmove(&nod, &nod2);
+ gopcode(o, r->type, &nod1, &nod2);
+ gmove(&nod2, &nod);
+ regfree(&nod2);
+ if(r->addable < INDEXED)
+ regfree(&nod1);
+ } else {
+ regalloc(&nod1, r, nn);
+ cgen(r, &nod1);
+ if(hardleft)
+ reglcgen(&nod, l, Z);
+ else
+ nod = *l;
+ if(o != OASMUL && o != OASADD || !typefd[l->type->etype]) {
+ regalloc(&nod2, r, Z);
+ gmove(&nod, &nod2);
+ gopcode(o, r->type, &nod1, &nod2);
+ regfree(&nod1);
+ gmove(&nod2, &nod);
+ regfree(&nod2);
+ } else {
+ gopcode(o, r->type, &nod, &nod1);
+ gmove(&nod1, &nod);
+ regfree(&nod1);
+ }
+ }
+ if(nn != Z)
+ gmove(&nod, nn);
+ if(hardleft)
+ regfree(&nod);
+ break;
+
+ case OASLMUL:
+ case OASLDIV:
+ case OASLMOD:
+ case OASMUL:
+ case OASDIV:
+ case OASMOD:
+ if(l->op == OBIT)
+ goto asbitop;
+ if(typefd[n->type->etype] || typefd[r->type->etype])
+ goto asfop;
+ if(r->op == OCONST && typechl[n->type->etype]) {
+ SET(v);
+ switch(o) {
+ case OASDIV:
+ case OASMOD:
+ c = r->vconst;
+ if(c < 0)
+ c = -c;
+ v = log2(c);
+ if(v < 0)
+ break;
+ /* fall thru */
+ case OASMUL:
+ case OASLMUL:
+ if(hardleft)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ regalloc(&nod, l, nn);
+ cgen(&nod2, &nod);
+ switch(o) {
+ case OASMUL:
+ case OASLMUL:
+ mulgen(n->type, r, &nod);
+ break;
+ case OASDIV:
+ sdiv2(r->vconst, v, l, &nod);
+ break;
+ case OASMOD:
+ smod2(r->vconst, v, l, &nod);
+ break;
+ }
+ havev:
+ gmove(&nod, &nod2);
+ if(nn != Z)
+ gmove(&nod, nn);
+ if(hardleft)
+ regfree(&nod2);
+ regfree(&nod);
+ goto done;
+ case OASLDIV:
+ c = r->vconst;
+ if((c & 0x80000000) == 0)
+ break;
+ if(hardleft)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ regalloc(&nod1, l, nn);
+ cgen(&nod2, &nod1);
+ regalloc(&nod, l, nn);
+ zeroregm(&nod);
+ gins(ACMPL, &nod1, nodconst(c));
+ gins(ASBBL, nodconst(-1), &nod);
+ regfree(&nod1);
+ goto havev;
+ }
+ }
+
+ if(o == OASMUL) {
+ /* should favour AX */
+ regalloc(&nod, l, nn);
+ if(r->complex >= FNX) {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ r = &nod1;
+ }
+ if(hardleft)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ cgen(&nod2, &nod);
+ if(r->addable < INDEXED || hardconst(r)) {
+ if(r->complex < FNX) {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ }
+ gopcode(OASMUL, n->type, &nod1, &nod);
+ regfree(&nod1);
+ }
+ else
+ gopcode(OASMUL, n->type, r, &nod);
+ if(r == &nod1)
+ regfree(r);
+ gmove(&nod, &nod2);
+ if(nn != Z)
+ gmove(&nod, nn);
+ regfree(&nod);
+ if(hardleft)
+ regfree(&nod2);
+ break;
+ }
+
+ /*
+ * get nod to be D_AX
+ * get nod1 to be D_DX
+ */
+ if(nodreg(&nod, nn, D_AX)) {
+ regsalloc(&nod2, n);
+ gmove(&nod, &nod2);
+ v = reg[D_AX];
+ reg[D_AX] = 0;
+
+ if(isreg(l, D_AX)) {
+ nod3 = *n;
+ nod3.left = &nod2;
+ cgen(&nod3, nn);
+ } else
+ if(isreg(r, D_AX)) {
+ nod3 = *n;
+ nod3.right = &nod2;
+ cgen(&nod3, nn);
+ } else
+ cgen(n, nn);
+
+ gmove(&nod2, &nod);
+ reg[D_AX] = v;
+ break;
+ }
+ if(nodreg(&nod1, nn, D_DX)) {
+ regsalloc(&nod2, n);
+ gmove(&nod1, &nod2);
+ v = reg[D_DX];
+ reg[D_DX] = 0;
+
+ if(isreg(l, D_DX)) {
+ nod3 = *n;
+ nod3.left = &nod2;
+ cgen(&nod3, nn);
+ } else
+ if(isreg(r, D_DX)) {
+ nod3 = *n;
+ nod3.right = &nod2;
+ cgen(&nod3, nn);
+ } else
+ cgen(n, nn);
+
+ gmove(&nod2, &nod1);
+ reg[D_DX] = v;
+ break;
+ }
+ reg[D_AX]++;
+ reg[D_DX]++;
+
+ if(l->complex >= r->complex) {
+ if(hardleft)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ cgen(&nod2, &nod);
+ if(r->op == OCONST && typechl[r->type->etype]) {
+ switch(o) {
+ case OASDIV:
+ sdivgen(&nod2, r, &nod, &nod1);
+ goto divdone;
+ case OASLDIV:
+ udivgen(&nod2, r, &nod, &nod1);
+ divdone:
+ gmove(&nod1, &nod2);
+ if(nn != Z)
+ gmove(&nod1, nn);
+ goto freelxaxdx;
+ }
+ }
+ if(o == OASDIV || o == OASMOD)
+ gins(typechl[l->type->etype]? ACDQ: ACQO, Z, Z);
+ if(o == OASLDIV || o == OASLMOD)
+ zeroregm(&nod1);
+ if(r->addable < INDEXED || r->op == OCONST ||
+ !typeil[r->type->etype]) {
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ gopcode(o, l->type, &nod3, Z);
+ regfree(&nod3);
+ } else
+ gopcode(o, n->type, r, Z);
+ } else {
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ if(hardleft)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ cgen(&nod2, &nod);
+ if(o == OASDIV || o == OASMOD)
+ gins(typechl[l->type->etype]? ACDQ: ACQO, Z, Z);
+ if(o == OASLDIV || o == OASLMOD)
+ zeroregm(&nod1);
+ gopcode(o, l->type, &nod3, Z);
+ regfree(&nod3);
+ }
+ if(o == OASMOD || o == OASLMOD) {
+ gmove(&nod1, &nod2);
+ if(nn != Z)
+ gmove(&nod1, nn);
+ } else {
+ gmove(&nod, &nod2);
+ if(nn != Z)
+ gmove(&nod, nn);
+ }
+ freelxaxdx:
+ if(hardleft)
+ regfree(&nod2);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ fop:
+ if(l->complex >= r->complex) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ if(r->addable < INDEXED) {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ gopcode(o, n->type, &nod1, &nod);
+ regfree(&nod1);
+ } else
+ gopcode(o, n->type, r, &nod);
+ } else {
+ /* TO DO: could do better with r->addable >= INDEXED */
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ gopcode(o, n->type, &nod1, &nod);
+ regfree(&nod1);
+ }
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+
+ asbitop:
+ regalloc(&nod4, n, nn);
+ if(l->complex >= r->complex) {
+ bitload(l, &nod, &nod1, &nod2, &nod4);
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ } else {
+ regalloc(&nod3, r, Z);
+ cgen(r, &nod3);
+ bitload(l, &nod, &nod1, &nod2, &nod4);
+ }
+ gmove(&nod, &nod4);
+
+ { /* TO DO: check floating point source */
+ Node onod;
+
+ /* incredible grot ... */
+ onod = nod3;
+ onod.op = o;
+ onod.complex = 2;
+ onod.addable = 0;
+ onod.type = tfield;
+ onod.left = &nod4;
+ onod.right = &nod3;
+ cgen(&onod, Z);
+ }
+ regfree(&nod3);
+ gmove(&nod4, &nod);
+ regfree(&nod4);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+
+ case OADDR:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ lcgen(l, nn);
+ break;
+
+ case OFUNC:
+ if(l->complex >= FNX) {
+ if(l->op != OIND)
+ diag(n, "bad function call");
+
+ regret(&nod, l->left);
+ cgen(l->left, &nod);
+ regsalloc(&nod1, l->left);
+ gmove(&nod, &nod1);
+ regfree(&nod);
+
+ nod = *n;
+ nod.left = &nod2;
+ nod2 = *l;
+ nod2.left = &nod1;
+ nod2.complex = 1;
+ cgen(&nod, nn);
+
+ return;
+ }
+ o = reg[REGARG];
+ gargs(r, &nod, &nod1);
+ if(l->addable < INDEXED) {
+ reglcgen(&nod, l, nn);
+ nod.op = OREGISTER;
+ gopcode(OFUNC, n->type, Z, &nod);
+ regfree(&nod);
+ } else
+ gopcode(OFUNC, n->type, Z, l);
+ if(REGARG)
+ if(o != reg[REGARG])
+ reg[REGARG]--;
+ if(nn != Z) {
+ regret(&nod, n);
+ gmove(&nod, nn);
+ regfree(&nod);
+ }
+ break;
+
+ case OIND:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ regialloc(&nod, n, nn);
+ r = l;
+ while(r->op == OADD)
+ r = r->right;
+ if(sconst(r)) {
+ v = r->vconst;
+ r->vconst = 0;
+ cgen(l, &nod);
+ nod.xoffset += v;
+ r->vconst = v;
+ } else
+ cgen(l, &nod);
+ regind(&nod, n);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OLO:
+ case OLS:
+ case OHI:
+ case OHS:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ boolgen(n, 1, nn);
+ break;
+
+ case OANDAND:
+ case OOROR:
+ boolgen(n, 1, nn);
+ if(nn == Z)
+ patch(p, pc);
+ break;
+
+ case ONOT:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ boolgen(n, 1, nn);
+ break;
+
+ case OCOMMA:
+ cgen(l, Z);
+ cgen(r, nn);
+ break;
+
+ case OCAST:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ /*
+ * convert from types l->n->nn
+ */
+ if(nocast(l->type, n->type) && nocast(n->type, nn->type)) {
+ /* both null, gen l->nn */
+ cgen(l, nn);
+ break;
+ }
+ if(ewidth[n->type->etype] < ewidth[l->type->etype]){
+ if(l->type->etype == TIND && typechlp[n->type->etype])
+ warn(n, "conversion of pointer to shorter integer");
+ }else if(0){
+ if(nocast(n->type, nn->type) || castup(n->type, nn->type)){
+ if(typefd[l->type->etype] != typefd[nn->type->etype])
+ regalloc(&nod, l, nn);
+ else
+ regalloc(&nod, nn, nn);
+ cgen(l, &nod);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+ }
+ }
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ regalloc(&nod1, n, &nod);
+ gmove(&nod, &nod1);
+ gmove(&nod1, nn);
+ regfree(&nod1);
+ regfree(&nod);
+ break;
+
+ case ODOT:
+ sugen(l, nodrat, l->type->width);
+ if(nn == Z)
+ break;
+ warn(n, "non-interruptable temporary");
+ nod = *nodrat;
+ if(!r || r->op != OCONST) {
+ diag(n, "DOT and no offset");
+ break;
+ }
+ nod.xoffset += (long)r->vconst;
+ nod.type = n->type;
+ cgen(&nod, nn);
+ break;
+
+ case OCOND:
+ bcgen(l, 1);
+ p1 = p;
+ cgen(r->left, nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ cgen(r->right, nn);
+ patch(p1, pc);
+ break;
+
+ case OPOSTINC:
+ case OPOSTDEC:
+ v = 1;
+ if(l->type->etype == TIND)
+ v = l->type->link->width;
+ if(o == OPOSTDEC)
+ v = -v;
+ if(l->op == OBIT)
+ goto bitinc;
+ if(nn == Z)
+ goto pre;
+
+ if(hardleft)
+ reglcgen(&nod, l, Z);
+ else
+ nod = *l;
+
+ gmove(&nod, nn);
+ if(typefd[n->type->etype]) {
+ regalloc(&nod1, l, Z);
+ gmove(&nod, &nod1);
+ if(v < 0)
+ gopcode(OSUB, n->type, nodfconst(-v), &nod1);
+ else
+ gopcode(OADD, n->type, nodfconst(v), &nod1);
+ gmove(&nod1, &nod);
+ regfree(&nod1);
+ } else
+ gopcode(OADD, n->type, nodconst(v), &nod);
+ if(hardleft)
+ regfree(&nod);
+ break;
+
+ case OPREINC:
+ case OPREDEC:
+ v = 1;
+ if(l->type->etype == TIND)
+ v = l->type->link->width;
+ if(o == OPREDEC)
+ v = -v;
+ if(l->op == OBIT)
+ goto bitinc;
+
+ pre:
+ if(hardleft)
+ reglcgen(&nod, l, Z);
+ else
+ nod = *l;
+ if(typefd[n->type->etype]) {
+ regalloc(&nod1, l, Z);
+ gmove(&nod, &nod1);
+ if(v < 0)
+ gopcode(OSUB, n->type, nodfconst(-v), &nod1);
+ else
+ gopcode(OADD, n->type, nodfconst(v), &nod1);
+ gmove(&nod1, &nod);
+ regfree(&nod1);
+ } else
+ gopcode(OADD, n->type, nodconst(v), &nod);
+ if(nn != Z)
+ gmove(&nod, nn);
+ if(hardleft)
+ regfree(&nod);
+ break;
+
+ bitinc:
+ if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+ bitload(l, &nod, &nod1, &nod2, Z);
+ gmove(&nod, nn);
+ gopcode(OADD, tfield, nodconst(v), &nod);
+ bitstore(l, &nod, &nod1, &nod2, Z);
+ break;
+ }
+ bitload(l, &nod, &nod1, &nod2, nn);
+ gopcode(OADD, tfield, nodconst(v), &nod);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+ }
+done:
+ cursafe = curs;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+ Node *r;
+ long v;
+
+ regialloc(t, n, nn);
+ if(n->op == OIND) {
+ r = n->left;
+ while(r->op == OADD)
+ r = r->right;
+ if(sconst(r)) {
+ v = r->vconst;
+ r->vconst = 0;
+ lcgen(n, t);
+ t->xoffset += v;
+ r->vconst = v;
+ regind(t, n);
+ return;
+ }
+ }
+ lcgen(n, t);
+ regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+ Prog *p1;
+ Node nod;
+
+ if(debug['g']) {
+ prtree(nn, "lcgen lhs");
+ prtree(n, "lcgen");
+ }
+ if(n == Z || n->type == T)
+ return;
+ if(nn == Z) {
+ nn = &nod;
+ regalloc(&nod, n, Z);
+ }
+ switch(n->op) {
+ default:
+ if(n->addable < INDEXED) {
+ diag(n, "unknown op in lcgen: %O", n->op);
+ break;
+ }
+ gopcode(OADDR, n->type, n, nn);
+ break;
+
+ case OCOMMA:
+ cgen(n->left, n->left);
+ lcgen(n->right, nn);
+ break;
+
+ case OIND:
+ cgen(n->left, nn);
+ break;
+
+ case OCOND:
+ bcgen(n->left, 1);
+ p1 = p;
+ lcgen(n->right->left, nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ lcgen(n->right->right, nn);
+ patch(p1, pc);
+ break;
+ }
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+ if(n->type == T)
+ gbranch(OGOTO);
+ else
+ boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+ int o;
+ Prog *p1, *p2;
+ Node *l, *r, nod, nod1;
+ long curs;
+
+ if(debug['g']) {
+ prtree(nn, "boolgen lhs");
+ prtree(n, "boolgen");
+ }
+ curs = cursafe;
+ l = n->left;
+ r = n->right;
+ switch(n->op) {
+
+ default:
+ o = ONE;
+ if(true)
+ o = OEQ;
+ /* bad, 13 is address of external that becomes constant */
+ if(n->addable >= INDEXED && n->addable != 13) {
+ if(typefd[n->type->etype]) {
+ regalloc(&nod1, n, Z);
+ gmove(nodfconst(0.0), &nod1); /* TO DO: FREGZERO */
+ gopcode(o, n->type, n, &nod1);
+ regfree(&nod1);
+ } else
+ gopcode(o, n->type, n, nodconst(0));
+ goto com;
+ }
+ regalloc(&nod, n, nn);
+ cgen(n, &nod);
+ if(typefd[n->type->etype]) {
+ regalloc(&nod1, n, Z);
+ gmove(nodfconst(0.0), &nod1); /* TO DO: FREGZERO */
+ gopcode(o, n->type, &nod, &nod1);
+ regfree(&nod1);
+ } else
+ gopcode(o, n->type, &nod, nodconst(0));
+ regfree(&nod);
+ goto com;
+
+ case OCONST:
+ o = vconst(n);
+ if(!true)
+ o = !o;
+ gbranch(OGOTO);
+ if(o) {
+ p1 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ }
+ goto com;
+
+ case OCOMMA:
+ cgen(l, Z);
+ boolgen(r, true, nn);
+ break;
+
+ case ONOT:
+ boolgen(l, !true, nn);
+ break;
+
+ case OCOND:
+ bcgen(l, 1);
+ p1 = p;
+ bcgen(r->left, true);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ bcgen(r->right, !true);
+ patch(p2, pc);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ goto com;
+
+ case OANDAND:
+ if(!true)
+ goto caseor;
+
+ caseand:
+ bcgen(l, true);
+ p1 = p;
+ bcgen(r, !true);
+ p2 = p;
+ patch(p1, pc);
+ gbranch(OGOTO);
+ patch(p2, pc);
+ goto com;
+
+ case OOROR:
+ if(!true)
+ goto caseand;
+
+ caseor:
+ bcgen(l, !true);
+ p1 = p;
+ bcgen(r, !true);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ goto com;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ o = n->op;
+ if(true)
+ o = comrel[relindex(o)];
+ if(l->complex >= FNX && r->complex >= FNX) {
+ regret(&nod, r);
+ cgen(r, &nod);
+ regsalloc(&nod1, r);
+ gmove(&nod, &nod1);
+ regfree(&nod);
+ nod = *n;
+ nod.right = &nod1;
+ boolgen(&nod, true, nn);
+ break;
+ }
+ if(immconst(l)) {
+ o = invrel[relindex(o)];
+ /* bad, 13 is address of external that becomes constant */
+ if(r->addable < INDEXED || r->addable == 13) {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ gopcode(o, l->type, &nod, l);
+ regfree(&nod);
+ } else
+ gopcode(o, l->type, r, l);
+ goto com;
+ }
+ if(typefd[l->type->etype])
+ o = invrel[relindex(logrel[relindex(o)])];
+ if(l->complex >= r->complex) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ if(r->addable < INDEXED || hardconst(r) || typefd[l->type->etype]) {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ gopcode(o, l->type, &nod, &nod1);
+ regfree(&nod1);
+ } else
+ gopcode(o, l->type, &nod, r);
+ regfree(&nod);
+ goto com;
+ }
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ if(l->addable < INDEXED || l->addable == 13 || hardconst(l)) {
+ regalloc(&nod1, l, Z);
+ cgen(l, &nod1);
+ if(typechl[l->type->etype] && ewidth[l->type->etype] <= ewidth[TINT])
+ gopcode(o, types[TINT], &nod1, &nod);
+ else
+ gopcode(o, l->type, &nod1, &nod);
+ regfree(&nod1);
+ } else
+ gopcode(o, l->type, l, &nod);
+ regfree(&nod);
+
+ com:
+ if(nn != Z) {
+ p1 = p;
+ gmove(nodconst(1L), nn);
+ gbranch(OGOTO);
+ p2 = p;
+ patch(p1, pc);
+ gmove(nodconst(0L), nn);
+ patch(p2, pc);
+ }
+ break;
+ }
+ cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+ Prog *p1;
+ Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+ Type *t;
+ int c, mt, mo;
+ vlong o0, o1;
+
+ if(n == Z || n->type == T)
+ return;
+ if(debug['g']) {
+ prtree(nn, "sugen lhs");
+ prtree(n, "sugen");
+ }
+ if(nn == nodrat)
+ if(w > nrathole)
+ nrathole = w;
+ switch(n->op) {
+ case OIND:
+ if(nn == Z) {
+ nullwarn(n->left, Z);
+ break;
+ }
+
+ default:
+ goto copy;
+
+ case OCONST:
+ goto copy;
+
+ case ODOT:
+ l = n->left;
+ sugen(l, nodrat, l->type->width);
+ if(nn == Z)
+ break;
+ warn(n, "non-interruptable temporary");
+ nod1 = *nodrat;
+ r = n->right;
+ if(!r || r->op != OCONST) {
+ diag(n, "DOT and no offset");
+ break;
+ }
+ nod1.xoffset += (long)r->vconst;
+ nod1.type = n->type;
+ sugen(&nod1, nn, w);
+ break;
+
+ case OSTRUCT:
+ /*
+ * rewrite so lhs has no fn call
+ */
+ if(nn != Z && side(nn)) {
+ nod1 = *n;
+ nod1.type = typ(TIND, n->type);
+ regret(&nod2, &nod1);
+ lcgen(nn, &nod2);
+ regsalloc(&nod0, &nod1);
+ cgen(&nod2, &nod0);
+ regfree(&nod2);
+
+ nod1 = *n;
+ nod1.op = OIND;
+ nod1.left = &nod0;
+ nod1.right = Z;
+ nod1.complex = 1;
+
+ sugen(n, &nod1, w);
+ return;
+ }
+
+ r = n->left;
+ for(t = n->type->link; t != T; t = t->down) {
+ l = r;
+ if(r->op == OLIST) {
+ l = r->left;
+ r = r->right;
+ }
+ if(nn == Z) {
+ cgen(l, nn);
+ continue;
+ }
+ /*
+ * hand craft *(&nn + o) = l
+ */
+ nod0 = znode;
+ nod0.op = OAS;
+ nod0.type = t;
+ nod0.left = &nod1;
+ nod0.right = nil;
+
+ nod1 = znode;
+ nod1.op = OIND;
+ nod1.type = t;
+ nod1.left = &nod2;
+
+ nod2 = znode;
+ nod2.op = OADD;
+ nod2.type = typ(TIND, t);
+ nod2.left = &nod3;
+ nod2.right = &nod4;
+
+ nod3 = znode;
+ nod3.op = OADDR;
+ nod3.type = nod2.type;
+ nod3.left = nn;
+
+ nod4 = znode;
+ nod4.op = OCONST;
+ nod4.type = nod2.type;
+ nod4.vconst = t->offset;
+
+ ccom(&nod0);
+ acom(&nod0);
+ xcom(&nod0);
+ nod0.addable = 0;
+ nod0.right = l;
+
+ /* prtree(&nod0, "hand craft"); /* */
+ cgen(&nod0, Z);
+ }
+ break;
+
+ case OAS:
+ if(nn == Z) {
+ if(n->addable < INDEXED)
+ sugen(n->right, n->left, w);
+ break;
+ }
+
+ sugen(n->right, nodrat, w);
+ warn(n, "non-interruptable temporary");
+ sugen(nodrat, n->left, w);
+ sugen(nodrat, nn, w);
+ break;
+
+ case OFUNC:
+ if(nn == Z) {
+ sugen(n, nodrat, w);
+ break;
+ }
+ if(nn->op != OIND) {
+ nn = new1(OADDR, nn, Z);
+ nn->type = types[TIND];
+ nn->addable = 0;
+ } else
+ nn = nn->left;
+ n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+ n->type = types[TVOID];
+ n->left->type = types[TVOID];
+ cgen(n, Z);
+ break;
+
+ case OCOND:
+ bcgen(n->left, 1);
+ p1 = p;
+ sugen(n->right->left, nn, w);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ sugen(n->right->right, nn, w);
+ patch(p1, pc);
+ break;
+
+ case OCOMMA:
+ cgen(n->left, Z);
+ sugen(n->right, nn, w);
+ break;
+ }
+ return;
+
+copy:
+ if(nn == Z) {
+ switch(n->op) {
+ case OASADD:
+ case OASSUB:
+ case OASAND:
+ case OASOR:
+ case OASXOR:
+
+ case OASMUL:
+ case OASLMUL:
+
+
+ case OASASHL:
+ case OASASHR:
+ case OASLSHR:
+ break;
+
+ case OPOSTINC:
+ case OPOSTDEC:
+ case OPREINC:
+ case OPREDEC:
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ if(n->complex >= FNX && nn != nil && nn->complex >= FNX) {
+ t = nn->type;
+ nn->type = types[TLONG];
+ regialloc(&nod1, nn, Z);
+ lcgen(nn, &nod1);
+ regsalloc(&nod2, nn);
+ nn->type = t;
+
+ gins(AMOVQ, &nod1, &nod2);
+ regfree(&nod1);
+
+ nod2.type = typ(TIND, t);
+
+ nod1 = nod2;
+ nod1.op = OIND;
+ nod1.left = &nod2;
+ nod1.right = Z;
+ nod1.complex = 1;
+ nod1.type = t;
+
+ sugen(n, &nod1, w);
+ return;
+ }
+
+ if(w <= 32) {
+ c = cursafe;
+ if(n->left != Z && n->left->complex >= FNX
+ && n->right != Z && n->right->complex >= FNX) {
+ regsalloc(&nod1, n->right);
+ cgen(n->right, &nod1);
+ nod2 = *n;
+ nod2.right = &nod1;
+ cgen(&nod2, nn);
+ cursafe = c;
+ return;
+ }
+ if(w & 7) {
+ mt = TLONG;
+ mo = AMOVL;
+ } else {
+ mt = TVLONG;
+ mo = AMOVQ;
+ }
+ if(n->complex > nn->complex) {
+ t = n->type;
+ n->type = types[mt];
+ regalloc(&nod0, n, Z);
+ if(!vaddr(n, 0)) {
+ reglcgen(&nod1, n, Z);
+ n->type = t;
+ n = &nod1;
+ }
+ else
+ n->type = t;
+
+ t = nn->type;
+ nn->type = types[mt];
+ if(!vaddr(nn, 0)) {
+ reglcgen(&nod2, nn, Z);
+ nn->type = t;
+ nn = &nod2;
+ }
+ else
+ nn->type = t;
+ } else {
+ t = nn->type;
+ nn->type = types[mt];
+ regalloc(&nod0, nn, Z);
+ if(!vaddr(nn, 0)) {
+ reglcgen(&nod2, nn, Z);
+ nn->type = t;
+ nn = &nod2;
+ }
+ else
+ nn->type = t;
+
+ t = n->type;
+ n->type = types[mt];
+ if(!vaddr(n, 0)) {
+ reglcgen(&nod1, n, Z);
+ n->type = t;
+ n = &nod1;
+ }
+ else
+ n->type = t;
+ }
+ o0 = n->xoffset;
+ o1 = nn->xoffset;
+ w /= ewidth[mt];
+ while(--w >= 0) {
+ gins(mo, n, &nod0);
+ gins(mo, &nod0, nn);
+ n->xoffset += ewidth[mt];
+ nn->xoffset += ewidth[mt];
+ }
+ n->xoffset = o0;
+ nn->xoffset = o1;
+ if(nn == &nod2)
+ regfree(&nod2);
+ if(n == &nod1)
+ regfree(&nod1);
+ regfree(&nod0);
+ return;
+ }
+
+ /* botch, need to save in .safe */
+ c = 0;
+ if(n->complex > nn->complex) {
+ t = n->type;
+ n->type = types[TIND];
+ nodreg(&nod1, n, D_SI);
+ if(reg[D_SI]) {
+ gins(APUSHQ, &nod1, Z);
+ c |= 1;
+ reg[D_SI]++;
+ }
+ lcgen(n, &nod1);
+ n->type = t;
+
+ t = nn->type;
+ nn->type = types[TIND];
+ nodreg(&nod2, nn, D_DI);
+ if(reg[D_DI]) {
+warn(Z, "DI botch");
+ gins(APUSHQ, &nod2, Z);
+ c |= 2;
+ reg[D_DI]++;
+ }
+ lcgen(nn, &nod2);
+ nn->type = t;
+ } else {
+ t = nn->type;
+ nn->type = types[TIND];
+ nodreg(&nod2, nn, D_DI);
+ if(reg[D_DI]) {
+warn(Z, "DI botch");
+ gins(APUSHQ, &nod2, Z);
+ c |= 2;
+ reg[D_DI]++;
+ }
+ lcgen(nn, &nod2);
+ nn->type = t;
+
+ t = n->type;
+ n->type = types[TIND];
+ nodreg(&nod1, n, D_SI);
+ if(reg[D_SI]) {
+ gins(APUSHQ, &nod1, Z);
+ c |= 1;
+ reg[D_SI]++;
+ }
+ lcgen(n, &nod1);
+ n->type = t;
+ }
+ nodreg(&nod3, n, D_CX);
+ if(reg[D_CX]) {
+ gins(APUSHQ, &nod3, Z);
+ c |= 4;
+ reg[D_CX]++;
+ }
+ gins(AMOVL, nodconst(w/SZ_INT), &nod3);
+ gins(ACLD, Z, Z);
+ gins(AREP, Z, Z);
+ gins(AMOVSL, Z, Z);
+ if(c & 4) {
+ gins(APOPQ, Z, &nod3);
+ reg[D_CX]--;
+ }
+ if(c & 2) {
+ gins(APOPQ, Z, &nod2);
+ reg[nod2.reg]--;
+ }
+ if(c & 1) {
+ gins(APOPQ, Z, &nod1);
+ reg[nod1.reg]--;
+ }
+}
+
+/*
+ * TO DO
+ */
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+ Node t1, t2;
+
+ while(c > 3) {
+ layout(f, t, 2, 0, Z);
+ c -= 2;
+ }
+
+ regalloc(&t1, &lregnode, Z);
+ regalloc(&t2, &lregnode, Z);
+ if(c > 0) {
+ gmove(f, &t1);
+ f->xoffset += SZ_INT;
+ }
+ if(cn != Z)
+ gmove(nodconst(cv), cn);
+ if(c > 1) {
+ gmove(f, &t2);
+ f->xoffset += SZ_INT;
+ }
+ if(c > 0) {
+ gmove(&t1, t);
+ t->xoffset += SZ_INT;
+ }
+ if(c > 2) {
+ gmove(f, &t1);
+ f->xoffset += SZ_INT;
+ }
+ if(c > 1) {
+ gmove(&t2, t);
+ t->xoffset += SZ_INT;
+ }
+ if(c > 2) {
+ gmove(&t1, t);
+ t->xoffset += SZ_INT;
+ }
+ regfree(&t1);
+ regfree(&t2);
+}
+
+/*
+ * constant is not vlong or fits as 32-bit signed immediate
+ */
+int
+immconst(Node *n)
+{
+ long v;
+
+ if(n->op != OCONST || !typechlpv[n->type->etype])
+ return 0;
+ if(typechl[n->type->etype])
+ return 1;
+ v = n->vconst;
+ return n->vconst == (vlong)v;
+}
+
+/*
+ * if a constant and vlong, doesn't fit as 32-bit signed immediate
+ */
+int
+hardconst(Node *n)
+{
+ return n->op == OCONST && !immconst(n);
+}
+
+/*
+ * casting up to t2 covers an intermediate cast to t1
+ */
+int
+castup(Type *t1, Type *t2)
+{
+ int ft;
+
+ if(!nilcast(t1, t2))
+ return 0;
+ /* known to be small to large */
+ ft = t1->etype;
+ switch(t2->etype){
+ case TINT:
+ case TLONG:
+ return ft == TLONG || ft == TINT || ft == TSHORT || ft == TCHAR;
+ case TUINT:
+ case TULONG:
+ return ft == TULONG || ft == TUINT || ft == TUSHORT || ft == TUCHAR;
+ case TVLONG:
+ return ft == TLONG || ft == TINT || ft == TSHORT;
+ case TUVLONG:
+ return ft == TULONG || ft == TUINT || ft == TUSHORT;
+ }
+ return 0;
+}
+
+void
+zeroregm(Node *n)
+{
+ gins(AMOVL, nodconst(0), n);
+}
+
+/* do we need to load the address of a vlong? */
+int
+vaddr(Node *n, int a)
+{
+ switch(n->op) {
+ case ONAME:
+ if(a)
+ return 1;
+ return !(n->class == CEXTERN || n->class == CGLOBL || n->class == CSTATIC);
+
+ case OCONST:
+ case OREGISTER:
+ case OINDREG:
+ return 1;
+ }
+ return 0;
+}
+
+long
+hi64v(Node *n)
+{
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ return (long)(n->vconst) & ~0L;
+ else
+ return (long)((uvlong)n->vconst>>32) & ~0L;
+}
+
+long
+lo64v(Node *n)
+{
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ return (long)((uvlong)n->vconst>>32) & ~0L;
+ else
+ return (long)(n->vconst) & ~0L;
+}
+
+Node *
+hi64(Node *n)
+{
+ return nodconst(hi64v(n));
+}
+
+Node *
+lo64(Node *n)
+{
+ return nodconst(lo64v(n));
+}
+
+int
+cond(int op)
+{
+ switch(op) {
+ case OANDAND:
+ case OOROR:
+ case ONOT:
+ return 1;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+++ b/utils/6c/div.c
@@ -1,0 +1,206 @@
+#include "gc.h"
+
+/*
+ * Based on: Granlund, T.; Montgomery, P.L.
+ * "Division by Invariant Integers using Multiplication".
+ * SIGPLAN Notices, Vol. 29, June 1994, page 61.
+ */
+
+#define TN(n) ((uvlong)1 << (n))
+#define T31 TN(31)
+#define T32 TN(32)
+
+int
+multiplier(ulong d, int p, uvlong *mp)
+{
+ int l;
+ uvlong mlo, mhi, tlo, thi;
+
+ l = topbit(d - 1) + 1;
+ mlo = (((TN(l) - d) << 32) / d) + T32;
+ if(l + p == 64)
+ mhi = (((TN(l) + 1 - d) << 32) / d) + T32;
+ else
+ mhi = (TN(32 + l) + TN(32 + l - p)) / d;
+ /*assert(mlo < mhi);*/
+ while(l > 0) {
+ tlo = mlo >> 1;
+ thi = mhi >> 1;
+ if(tlo == thi)
+ break;
+ mlo = tlo;
+ mhi = thi;
+ l--;
+ }
+ *mp = mhi;
+ return l;
+}
+
+int
+sdiv(ulong d, ulong *mp, int *sp)
+{
+ int s;
+ uvlong m;
+
+ s = multiplier(d, 32 - 1, &m);
+ *mp = m;
+ *sp = s;
+ if(m >= T31)
+ return 1;
+ else
+ return 0;
+}
+
+int
+udiv(ulong d, ulong *mp, int *sp, int *pp)
+{
+ int p, s;
+ uvlong m;
+
+ s = multiplier(d, 32, &m);
+ p = 0;
+ if(m >= T32) {
+ while((d & 1) == 0) {
+ d >>= 1;
+ p++;
+ }
+ s = multiplier(d, 32 - p, &m);
+ }
+ *mp = m;
+ *pp = p;
+ if(m >= T32) {
+ /*assert(p == 0);*/
+ *sp = s - 1;
+ return 1;
+ }
+ else {
+ *sp = s;
+ return 0;
+ }
+}
+
+void
+sdivgen(Node *l, Node *r, Node *ax, Node *dx)
+{
+ int a, s;
+ ulong m;
+ vlong c;
+
+ c = r->vconst;
+ if(c < 0)
+ c = -c;
+ a = sdiv(c, &m, &s);
+//print("a=%d i=%ld s=%d m=%lux\n", a, (long)r->vconst, s, m);
+ gins(AMOVL, nodconst(m), ax);
+ gins(AIMULL, l, Z);
+ gins(AMOVL, l, ax);
+ if(a)
+ gins(AADDL, ax, dx);
+ gins(ASHRL, nodconst(31), ax);
+ gins(ASARL, nodconst(s), dx);
+ gins(AADDL, ax, dx);
+ if(r->vconst < 0)
+ gins(ANEGL, Z, dx);
+}
+
+void
+udivgen(Node *l, Node *r, Node *ax, Node *dx)
+{
+ int a, s, t;
+ ulong m;
+ Node nod;
+
+ a = udiv(r->vconst, &m, &s, &t);
+//print("a=%ud i=%ld p=%d s=%d m=%lux\n", a, (long)r->vconst, t, s, m);
+ if(t != 0) {
+ gins(AMOVL, l, ax);
+ gins(ASHRL, nodconst(t), ax);
+ gins(AMOVL, nodconst(m), dx);
+ gins(AMULL, dx, Z);
+ }
+ else if(a) {
+ if(l->op != OREGISTER) {
+ regalloc(&nod, l, Z);
+ gins(AMOVL, l, &nod);
+ l = &nod;
+ }
+ gins(AMOVL, nodconst(m), ax);
+ gins(AMULL, l, Z);
+ gins(AADDL, l, dx);
+ gins(ARCRL, nodconst(1), dx);
+ if(l == &nod)
+ regfree(l);
+ }
+ else {
+ gins(AMOVL, nodconst(m), ax);
+ gins(AMULL, l, Z);
+ }
+ if(s != 0)
+ gins(ASHRL, nodconst(s), dx);
+}
+
+void
+sext(Node *d, Node *s, Node *l)
+{
+ if(s->reg == D_AX && !nodreg(d, Z, D_DX)) {
+ reg[D_DX]++;
+ gins(ACDQ, Z, Z);
+ }
+ else {
+ regalloc(d, l, Z);
+ gins(AMOVL, s, d);
+ gins(ASARL, nodconst(31), d);
+ }
+}
+
+void
+sdiv2(long c, int v, Node *l, Node *n)
+{
+ Node nod;
+
+ if(v > 0) {
+ if(v > 1) {
+ sext(&nod, n, l);
+ gins(AANDL, nodconst((1 << v) - 1), &nod);
+ gins(AADDL, &nod, n);
+ regfree(&nod);
+ }
+ else {
+ gins(ACMPL, n, nodconst(0x80000000));
+ gins(ASBBL, nodconst(-1), n);
+ }
+ gins(ASARL, nodconst(v), n);
+ }
+ if(c < 0)
+ gins(ANEGL, Z, n);
+}
+
+void
+smod2(long c, int v, Node *l, Node *n)
+{
+ Node nod;
+
+ if(c == 1) {
+ zeroregm(n);
+ return;
+ }
+
+ sext(&nod, n, l);
+ if(v == 0) {
+ zeroregm(n);
+ gins(AXORL, &nod, n);
+ gins(ASUBL, &nod, n);
+ }
+ else if(v > 1) {
+ gins(AANDL, nodconst((1 << v) - 1), &nod);
+ gins(AADDL, &nod, n);
+ gins(AANDL, nodconst((1 << v) - 1), n);
+ gins(ASUBL, &nod, n);
+ }
+ else {
+ gins(AANDL, nodconst(1), n);
+ gins(AXORL, &nod, n);
+ gins(ASUBL, &nod, n);
+ }
+ regfree(&nod);
+}
--- /dev/null
+++ b/utils/6c/enam.c
@@ -1,0 +1,669 @@
+char* anames[] =
+{
+ "XXX",
+ "AAA",
+ "AAD",
+ "AAM",
+ "AAS",
+ "ADCB",
+ "ADCL",
+ "ADCW",
+ "ADDB",
+ "ADDL",
+ "ADDW",
+ "ADJSP",
+ "ANDB",
+ "ANDL",
+ "ANDW",
+ "ARPL",
+ "BOUNDL",
+ "BOUNDW",
+ "BSFL",
+ "BSFW",
+ "BSRL",
+ "BSRW",
+ "BTL",
+ "BTW",
+ "BTCL",
+ "BTCW",
+ "BTRL",
+ "BTRW",
+ "BTSL",
+ "BTSW",
+ "BYTE",
+ "CALL",
+ "CLC",
+ "CLD",
+ "CLI",
+ "CLTS",
+ "CMC",
+ "CMPB",
+ "CMPL",
+ "CMPW",
+ "CMPSB",
+ "CMPSL",
+ "CMPSW",
+ "DAA",
+ "DAS",
+ "DATA",
+ "DECB",
+ "DECL",
+ "DECQ",
+ "DECW",
+ "DIVB",
+ "DIVL",
+ "DIVW",
+ "ENTER",
+ "GLOBL",
+ "GOK",
+ "HISTORY",
+ "HLT",
+ "IDIVB",
+ "IDIVL",
+ "IDIVW",
+ "IMULB",
+ "IMULL",
+ "IMULW",
+ "INB",
+ "INL",
+ "INW",
+ "INCB",
+ "INCL",
+ "INCQ",
+ "INCW",
+ "INSB",
+ "INSL",
+ "INSW",
+ "INT",
+ "INTO",
+ "IRETL",
+ "IRETW",
+ "JCC",
+ "JCS",
+ "JCXZ",
+ "JEQ",
+ "JGE",
+ "JGT",
+ "JHI",
+ "JLE",
+ "JLS",
+ "JLT",
+ "JMI",
+ "JMP",
+ "JNE",
+ "JOC",
+ "JOS",
+ "JPC",
+ "JPL",
+ "JPS",
+ "LAHF",
+ "LARL",
+ "LARW",
+ "LEAL",
+ "LEAW",
+ "LEAVEL",
+ "LEAVEW",
+ "LOCK",
+ "LODSB",
+ "LODSL",
+ "LODSW",
+ "LONG",
+ "LOOP",
+ "LOOPEQ",
+ "LOOPNE",
+ "LSLL",
+ "LSLW",
+ "MOVB",
+ "MOVL",
+ "MOVW",
+ "MOVBLSX",
+ "MOVBLZX",
+ "MOVBQSX",
+ "MOVBQZX",
+ "MOVBWSX",
+ "MOVBWZX",
+ "MOVWLSX",
+ "MOVWLZX",
+ "MOVWQSX",
+ "MOVWQZX",
+ "MOVSB",
+ "MOVSL",
+ "MOVSW",
+ "MULB",
+ "MULL",
+ "MULW",
+ "NAME",
+ "NEGB",
+ "NEGL",
+ "NEGW",
+ "NOP",
+ "NOTB",
+ "NOTL",
+ "NOTW",
+ "ORB",
+ "ORL",
+ "ORW",
+ "OUTB",
+ "OUTL",
+ "OUTW",
+ "OUTSB",
+ "OUTSL",
+ "OUTSW",
+ "POPAL",
+ "POPAW",
+ "POPFL",
+ "POPFW",
+ "POPL",
+ "POPW",
+ "PUSHAL",
+ "PUSHAW",
+ "PUSHFL",
+ "PUSHFW",
+ "PUSHL",
+ "PUSHW",
+ "RCLB",
+ "RCLL",
+ "RCLW",
+ "RCRB",
+ "RCRL",
+ "RCRW",
+ "REP",
+ "REPN",
+ "RET",
+ "ROLB",
+ "ROLL",
+ "ROLW",
+ "RORB",
+ "RORL",
+ "RORW",
+ "SAHF",
+ "SALB",
+ "SALL",
+ "SALW",
+ "SARB",
+ "SARL",
+ "SARW",
+ "SBBB",
+ "SBBL",
+ "SBBW",
+ "SCASB",
+ "SCASL",
+ "SCASW",
+ "SETCC",
+ "SETCS",
+ "SETEQ",
+ "SETGE",
+ "SETGT",
+ "SETHI",
+ "SETLE",
+ "SETLS",
+ "SETLT",
+ "SETMI",
+ "SETNE",
+ "SETOC",
+ "SETOS",
+ "SETPC",
+ "SETPL",
+ "SETPS",
+ "CDQ",
+ "CWD",
+ "SHLB",
+ "SHLL",
+ "SHLW",
+ "SHRB",
+ "SHRL",
+ "SHRW",
+ "STC",
+ "STD",
+ "STI",
+ "STOSB",
+ "STOSL",
+ "STOSW",
+ "SUBB",
+ "SUBL",
+ "SUBW",
+ "SYSCALL",
+ "TESTB",
+ "TESTL",
+ "TESTW",
+ "TEXT",
+ "VERR",
+ "VERW",
+ "WAIT",
+ "WORD",
+ "XCHGB",
+ "XCHGL",
+ "XCHGW",
+ "XLAT",
+ "XORB",
+ "XORL",
+ "XORW",
+ "FMOVB",
+ "FMOVBP",
+ "FMOVD",
+ "FMOVDP",
+ "FMOVF",
+ "FMOVFP",
+ "FMOVL",
+ "FMOVLP",
+ "FMOVV",
+ "FMOVVP",
+ "FMOVW",
+ "FMOVWP",
+ "FMOVX",
+ "FMOVXP",
+ "FCOMB",
+ "FCOMBP",
+ "FCOMD",
+ "FCOMDP",
+ "FCOMDPP",
+ "FCOMF",
+ "FCOMFP",
+ "FCOML",
+ "FCOMLP",
+ "FCOMW",
+ "FCOMWP",
+ "FUCOM",
+ "FUCOMP",
+ "FUCOMPP",
+ "FADDDP",
+ "FADDW",
+ "FADDL",
+ "FADDF",
+ "FADDD",
+ "FMULDP",
+ "FMULW",
+ "FMULL",
+ "FMULF",
+ "FMULD",
+ "FSUBDP",
+ "FSUBW",
+ "FSUBL",
+ "FSUBF",
+ "FSUBD",
+ "FSUBRDP",
+ "FSUBRW",
+ "FSUBRL",
+ "FSUBRF",
+ "FSUBRD",
+ "FDIVDP",
+ "FDIVW",
+ "FDIVL",
+ "FDIVF",
+ "FDIVD",
+ "FDIVRDP",
+ "FDIVRW",
+ "FDIVRL",
+ "FDIVRF",
+ "FDIVRD",
+ "FXCHD",
+ "FFREE",
+ "FLDCW",
+ "FLDENV",
+ "FRSTOR",
+ "FSAVE",
+ "FSTCW",
+ "FSTENV",
+ "FSTSW",
+ "F2XM1",
+ "FABS",
+ "FCHS",
+ "FCLEX",
+ "FCOS",
+ "FDECSTP",
+ "FINCSTP",
+ "FINIT",
+ "FLD1",
+ "FLDL2E",
+ "FLDL2T",
+ "FLDLG2",
+ "FLDLN2",
+ "FLDPI",
+ "FLDZ",
+ "FNOP",
+ "FPATAN",
+ "FPREM",
+ "FPREM1",
+ "FPTAN",
+ "FRNDINT",
+ "FSCALE",
+ "FSIN",
+ "FSINCOS",
+ "FSQRT",
+ "FTST",
+ "FXAM",
+ "FXTRACT",
+ "FYL2X",
+ "FYL2XP1",
+ "END",
+ "DYNT",
+ "INIT",
+ "SIGNAME",
+ "CMPXCHGB",
+ "CMPXCHGL",
+ "CMPXCHGW",
+ "CMPXCHG8B",
+ "CPUID",
+ "INVD",
+ "INVLPG",
+ "LFENCE",
+ "MFENCE",
+ "MOVNTIL",
+ "RDMSR",
+ "RDPMC",
+ "RDTSC",
+ "RSM",
+ "SFENCE",
+ "SYSRET",
+ "WBINVD",
+ "WRMSR",
+ "XADDB",
+ "XADDL",
+ "XADDW",
+ "CMOVLCC",
+ "CMOVLCS",
+ "CMOVLEQ",
+ "CMOVLGE",
+ "CMOVLGT",
+ "CMOVLHI",
+ "CMOVLLE",
+ "CMOVLLS",
+ "CMOVLLT",
+ "CMOVLMI",
+ "CMOVLNE",
+ "CMOVLOC",
+ "CMOVLOS",
+ "CMOVLPC",
+ "CMOVLPL",
+ "CMOVLPS",
+ "CMOVQCC",
+ "CMOVQCS",
+ "CMOVQEQ",
+ "CMOVQGE",
+ "CMOVQGT",
+ "CMOVQHI",
+ "CMOVQLE",
+ "CMOVQLS",
+ "CMOVQLT",
+ "CMOVQMI",
+ "CMOVQNE",
+ "CMOVQOC",
+ "CMOVQOS",
+ "CMOVQPC",
+ "CMOVQPL",
+ "CMOVQPS",
+ "CMOVWCC",
+ "CMOVWCS",
+ "CMOVWEQ",
+ "CMOVWGE",
+ "CMOVWGT",
+ "CMOVWHI",
+ "CMOVWLE",
+ "CMOVWLS",
+ "CMOVWLT",
+ "CMOVWMI",
+ "CMOVWNE",
+ "CMOVWOC",
+ "CMOVWOS",
+ "CMOVWPC",
+ "CMOVWPL",
+ "CMOVWPS",
+ "ADCQ",
+ "ADDQ",
+ "ANDQ",
+ "BSFQ",
+ "BSRQ",
+ "BTCQ",
+ "BTQ",
+ "BTRQ",
+ "BTSQ",
+ "CMPQ",
+ "CMPSQ",
+ "CMPXCHGQ",
+ "CQO",
+ "DIVQ",
+ "IDIVQ",
+ "IMULQ",
+ "IRETQ",
+ "LEAQ",
+ "LEAVEQ",
+ "LODSQ",
+ "MOVQ",
+ "MOVLQSX",
+ "MOVLQZX",
+ "MOVNTIQ",
+ "MOVSQ",
+ "MULQ",
+ "NEGQ",
+ "NOTQ",
+ "ORQ",
+ "POPFQ",
+ "POPQ",
+ "PUSHFQ",
+ "PUSHQ",
+ "RCLQ",
+ "RCRQ",
+ "ROLQ",
+ "RORQ",
+ "QUAD",
+ "SALQ",
+ "SARQ",
+ "SBBQ",
+ "SCASQ",
+ "SHLQ",
+ "SHRQ",
+ "STOSQ",
+ "SUBQ",
+ "TESTQ",
+ "XADDQ",
+ "XCHGQ",
+ "XORQ",
+ "ADDPD",
+ "ADDPS",
+ "ADDSD",
+ "ADDSS",
+ "ANDNPD",
+ "ANDNPS",
+ "ANDPD",
+ "ANDPS",
+ "CMPPD",
+ "CMPPS",
+ "CMPSD",
+ "CMPSS",
+ "COMISD",
+ "COMISS",
+ "CVTPD2PL",
+ "CVTPD2PS",
+ "CVTPL2PD",
+ "CVTPL2PS",
+ "CVTPS2PD",
+ "CVTPS2PL",
+ "CVTSD2SL",
+ "CVTSD2SQ",
+ "CVTSD2SS",
+ "CVTSL2SD",
+ "CVTSL2SS",
+ "CVTSQ2SD",
+ "CVTSQ2SS",
+ "CVTSS2SD",
+ "CVTSS2SL",
+ "CVTSS2SQ",
+ "CVTTPD2PL",
+ "CVTTPS2PL",
+ "CVTTSD2SL",
+ "CVTTSD2SQ",
+ "CVTTSS2SL",
+ "CVTTSS2SQ",
+ "DIVPD",
+ "DIVPS",
+ "DIVSD",
+ "DIVSS",
+ "EMMS",
+ "FXRSTOR",
+ "FXRSTOR64",
+ "FXSAVE",
+ "FXSAVE64",
+ "LDMXCSR",
+ "MASKMOVOU",
+ "MASKMOVQ",
+ "MAXPD",
+ "MAXPS",
+ "MAXSD",
+ "MAXSS",
+ "MINPD",
+ "MINPS",
+ "MINSD",
+ "MINSS",
+ "MOVAPD",
+ "MOVAPS",
+ "MOVOU",
+ "MOVHLPS",
+ "MOVHPD",
+ "MOVHPS",
+ "MOVLHPS",
+ "MOVLPD",
+ "MOVLPS",
+ "MOVMSKPD",
+ "MOVMSKPS",
+ "MOVNTO",
+ "MOVNTPD",
+ "MOVNTPS",
+ "MOVNTQ",
+ "MOVO",
+ "MOVQOZX",
+ "MOVSD",
+ "MOVSS",
+ "MOVUPD",
+ "MOVUPS",
+ "MULPD",
+ "MULPS",
+ "MULSD",
+ "MULSS",
+ "ORPD",
+ "ORPS",
+ "PACKSSLW",
+ "PACKSSWB",
+ "PACKUSWB",
+ "PADDB",
+ "PADDL",
+ "PADDQ",
+ "PADDSB",
+ "PADDSW",
+ "PADDUSB",
+ "PADDUSW",
+ "PADDW",
+ "PANDB",
+ "PANDL",
+ "PANDSB",
+ "PANDSW",
+ "PANDUSB",
+ "PANDUSW",
+ "PANDW",
+ "PAND",
+ "PANDN",
+ "PAVGB",
+ "PAVGW",
+ "PCMPEQB",
+ "PCMPEQL",
+ "PCMPEQW",
+ "PCMPGTB",
+ "PCMPGTL",
+ "PCMPGTW",
+ "PEXTRW",
+ "PFACC",
+ "PFADD",
+ "PFCMPEQ",
+ "PFCMPGE",
+ "PFCMPGT",
+ "PFMAX",
+ "PFMIN",
+ "PFMUL",
+ "PFNACC",
+ "PFPNACC",
+ "PFRCP",
+ "PFRCPIT1",
+ "PFRCPI2T",
+ "PFRSQIT1",
+ "PFRSQRT",
+ "PFSUB",
+ "PFSUBR",
+ "PINSRW",
+ "PMADDWL",
+ "PMAXSW",
+ "PMAXUB",
+ "PMINSW",
+ "PMINUB",
+ "PMOVMSKB",
+ "PMULHRW",
+ "PMULHUW",
+ "PMULHW",
+ "PMULLW",
+ "PMULULQ",
+ "POR",
+ "PSADBW",
+ "PSHUFHW",
+ "PSHUFL",
+ "PSHUFLW",
+ "PSHUFW",
+ "PSLLO",
+ "PSLLL",
+ "PSLLQ",
+ "PSLLW",
+ "PSRAL",
+ "PSRAW",
+ "PSRLO",
+ "PSRLL",
+ "PSRLQ",
+ "PSRLW",
+ "PSUBB",
+ "PSUBL",
+ "PSUBQ",
+ "PSUBSB",
+ "PSUBSW",
+ "PSUBUSB",
+ "PSUBUSW",
+ "PSUBW",
+ "PSWAPL",
+ "PUNPCKHBW",
+ "PUNPCKHLQ",
+ "PUNPCKHQDQ",
+ "PUNPCKHWL",
+ "PUNPCKLBW",
+ "PUNPCKLLQ",
+ "PUNPCKLQDQ",
+ "PUNPCKLWL",
+ "PXOR",
+ "RCPPS",
+ "RCPSS",
+ "RSQRTPS",
+ "RSQRTSS",
+ "SHUFPD",
+ "SHUFPS",
+ "SQRTPD",
+ "SQRTPS",
+ "SQRTSD",
+ "SQRTSS",
+ "STMXCSR",
+ "SUBPD",
+ "SUBPS",
+ "SUBSD",
+ "SUBSS",
+ "UCOMISD",
+ "UCOMISS",
+ "UNPCKHPD",
+ "UNPCKHPS",
+ "UNPCKLPD",
+ "UNPCKLPS",
+ "XORPD",
+ "XORPS",
+ "PF2IW",
+ "PF2IL",
+ "PI2FW",
+ "PI2FL",
+ "RETFW",
+ "RETFL",
+ "RETFQ",
+ "SWAPGS",
+ "MODE",
+ "LAST",
+};
--- /dev/null
+++ b/utils/6c/gc.h
@@ -1,0 +1,380 @@
+#include "../cc/cc.h"
+#include "../6c/6.out.h"
+
+/*
+ * 6c/amd64
+ * Intel 386 with AMD64 extensions
+ */
+#define SZ_CHAR 1
+#define SZ_SHORT 2
+#define SZ_INT 4
+#define SZ_LONG 4
+#define SZ_IND 8
+#define SZ_FLOAT 4
+#define SZ_VLONG 8
+#define SZ_DOUBLE 8
+#define FNX 100
+
+typedef struct Adr Adr;
+typedef struct Prog Prog;
+typedef struct Case Case;
+typedef struct C1 C1;
+typedef struct Var Var;
+typedef struct Reg Reg;
+typedef struct Rgn Rgn;
+typedef struct Renv Renv;
+
+EXTERN struct
+{
+ Node* regtree;
+ Node* basetree;
+ short scale;
+ short reg;
+ short ptr;
+} idx;
+
+struct Adr
+{
+ vlong offset;
+ double dval;
+ char sval[NSNAME];
+
+ Sym* sym;
+ uchar type;
+ uchar index;
+ uchar etype;
+ uchar scale; /* doubles as width in DATA op */
+};
+#define A ((Adr*)0)
+
+#define INDEXED 9
+struct Prog
+{
+ Adr from;
+ Adr to;
+ Prog* link;
+ long lineno;
+ short as;
+};
+#define P ((Prog*)0)
+
+struct Case
+{
+ Case* link;
+ vlong val;
+ long label;
+ char def;
+ char isv;
+};
+#define C ((Case*)0)
+
+struct C1
+{
+ vlong val;
+ long label;
+};
+
+struct Var
+{
+ vlong offset;
+ Sym* sym;
+ char name;
+ char etype;
+};
+
+struct Reg
+{
+ long pc;
+ long rpo; /* reverse post ordering */
+
+ Bits set;
+ Bits use1;
+ Bits use2;
+
+ Bits refbehind;
+ Bits refahead;
+ Bits calbehind;
+ Bits calahead;
+ Bits regdiff;
+ Bits act;
+
+ long regu;
+ long loop; /* could be shorter */
+
+ Reg* log5;
+ long active;
+
+ Reg* p1;
+ Reg* p2;
+ Reg* p2link;
+ Reg* s1;
+ Reg* s2;
+ Reg* link;
+ Prog* prog;
+};
+#define R ((Reg*)0)
+
+struct Renv
+{
+ int safe;
+ Node base;
+ Node* saved;
+ Node* scope;
+};
+
+#define NRGN 600
+struct Rgn
+{
+ Reg* enter;
+ short cost;
+ short varno;
+ short regno;
+};
+
+EXTERN long breakpc;
+EXTERN long nbreak;
+EXTERN Case* cases;
+EXTERN Node constnode;
+EXTERN Node fconstnode;
+EXTERN Node vconstnode;
+EXTERN long continpc;
+EXTERN long curarg;
+EXTERN long cursafe;
+EXTERN Prog* firstp;
+EXTERN Prog* lastp;
+EXTERN long maxargsafe;
+EXTERN int mnstring;
+EXTERN Node* nodrat;
+EXTERN Node* nodret;
+EXTERN Node* nodsafe;
+EXTERN long nrathole;
+EXTERN long nstring;
+EXTERN Prog* p;
+EXTERN long pc;
+EXTERN Node lregnode;
+EXTERN Node qregnode;
+EXTERN char string[NSNAME];
+EXTERN Sym* symrathole;
+EXTERN Node znode;
+EXTERN Prog zprog;
+EXTERN int reg[D_NONE];
+EXTERN long exregoffset;
+EXTERN long exfregoffset;
+EXTERN uchar typechlpv[NTYPE];
+
+#define BLOAD(r) band(bnot(r->refbehind), r->refahead)
+#define BSTORE(r) band(bnot(r->calbehind), r->calahead)
+#define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z])
+#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z])
+
+#define bset(a,n) ((a).b[(n)/32]&(1L<<(n)%32))
+
+#define CLOAD 5
+#define CREF 5
+#define CINF 1000
+#define LOOP 3
+
+EXTERN Rgn region[NRGN];
+EXTERN Rgn* rgp;
+EXTERN int nregion;
+EXTERN int nvar;
+
+EXTERN Bits externs;
+EXTERN Bits params;
+EXTERN Bits consts;
+EXTERN Bits addrs;
+
+EXTERN long regbits;
+EXTERN long exregbits;
+
+EXTERN int change;
+EXTERN int suppress;
+
+EXTERN Reg* firstr;
+EXTERN Reg* lastr;
+EXTERN Reg zreg;
+EXTERN Reg* freer;
+EXTERN Var var[NVAR];
+EXTERN long* idom;
+EXTERN Reg** rpo2r;
+EXTERN long maxnr;
+
+extern char* anames[];
+
+/*
+ * sgen.c
+ */
+void codgen(Node*, Node*);
+void gen(Node*);
+void noretval(int);
+void usedset(Node*, int);
+void xcom(Node*);
+void indx(Node*);
+int bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void zeroregm(Node*);
+void cgen(Node*, Node*);
+void reglcgen(Node*, Node*, Node*);
+void lcgen(Node*, Node*);
+void bcgen(Node*, int);
+void boolgen(Node*, int, Node*);
+void sugen(Node*, Node*, long);
+int needreg(Node*, int);
+int hardconst(Node*);
+int immconst(Node*);
+
+/*
+ * cgen64.c
+ */
+int vaddr(Node*, int);
+void loadpair(Node*, Node*);
+int cgen64(Node*, Node*);
+void testv(Node*, int);
+
+/*
+ * txt.c
+ */
+void ginit(void);
+void gclean(void);
+void nextpc(void);
+void gargs(Node*, Node*, Node*);
+void garg1(Node*, Node*, Node*, int, Node**);
+Node* nodconst(long);
+Node* nodfconst(double);
+Node* nodgconst(vlong, Type*);
+int nodreg(Node*, Node*, int);
+int isreg(Node*, int);
+void regret(Node*, Node*);
+void regalloc(Node*, Node*, Node*);
+void regfree(Node*);
+void regialloc(Node*, Node*, Node*);
+void regsalloc(Node*, Node*);
+void regaalloc1(Node*, Node*);
+void regaalloc(Node*, Node*);
+void regind(Node*, Node*);
+void gprep(Node*, Node*);
+void naddr(Node*, Adr*);
+void gcmp(int, Node*, vlong);
+void gmove(Node*, Node*);
+void gins(int a, Node*, Node*);
+void gopcode(int, Type*, Node*, Node*);
+int samaddr(Node*, Node*);
+void gbranch(int);
+void patch(Prog*, long);
+int sconst(Node*);
+void gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int swcmp(void*, void*);
+void doswit(Node*);
+void swit1(C1*, int, long, Node*);
+void casf(void);
+void bitload(Node*, Node*, Node*, Node*, Node*);
+void bitstore(Node*, Node*, Node*, Node*, Node*);
+long outstring(char*, long);
+void nullwarn(Node*, Node*);
+void gextern(Sym*, Node*, long, long);
+void outcode(void);
+void ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void listinit(void);
+int Pconv(Fmt*);
+int Aconv(Fmt*);
+int Dconv(Fmt*);
+int Sconv(Fmt*);
+int Rconv(Fmt*);
+int Xconv(Fmt*);
+int Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg* rega(void);
+int rcmp(void*, void*);
+void regopt(Prog*);
+void addmove(Reg*, int, int, int);
+Bits mkvar(Reg*, Adr*);
+void prop(Reg*, Bits, Bits);
+void loopit(Reg*, long);
+void synch(Reg*, Bits);
+ulong allreg(ulong, Rgn*);
+void paint1(Reg*, int);
+ulong paint2(Reg*, int);
+void paint3(Reg*, int, long, int);
+void addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void peep(void);
+void excise(Reg*);
+Reg* uniqp(Reg*);
+Reg* uniqs(Reg*);
+int regtyp(Adr*);
+int anyvar(Adr*);
+int subprop(Reg*);
+int copyprop(Reg*);
+int copy1(Adr*, Adr*, Reg*, int);
+int copyu(Prog*, Adr*, Adr*);
+
+int copyas(Adr*, Adr*);
+int copyau(Adr*, Adr*);
+int copysub(Adr*, Adr*, Adr*, int);
+int copysub1(Prog*, Adr*, Adr*, int);
+
+long RtoB(int);
+long FtoB(int);
+int BtoR(long);
+int BtoF(long);
+
+#define D_HI D_NONE
+#define D_LO D_NONE
+
+#define isregtype(t) ((t)>= D_AX && (t)<=D_R15)
+
+/*
+ * bound
+ */
+void comtarg(void);
+
+/*
+ * com64
+ */
+int cond(int);
+int com64(Node*);
+void com64init(void);
+void bool64(Node*);
+long lo64v(Node*);
+long hi64v(Node*);
+Node* lo64(Node*);
+Node* hi64(Node*);
+
+/*
+ * div/mul
+ */
+void sdivgen(Node*, Node*, Node*, Node*);
+void udivgen(Node*, Node*, Node*, Node*);
+void sdiv2(long, int, Node*, Node*);
+void smod2(long, int, Node*, Node*);
+void mulgen(Type*, Node*, Node*);
+void genmuladd(Node*, Node*, int, Node*);
+void shiftit(Type*, Node*, Node*);
+
+#pragma varargck type "A" int
+#pragma varargck type "B" Bits
+#pragma varargck type "D" Adr*
+#pragma varargck type "P" Prog*
+#pragma varargck type "R" int
+#pragma varargck type "S" char*
+
+#define D_X7 (D_X0+7)
+
+void fgopcode(int, Node*, Node*, int, int);
--- /dev/null
+++ b/utils/6c/list.c
@@ -1,0 +1,337 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+ fmtinstall('A', Aconv);
+ fmtinstall('B', Bconv);
+ fmtinstall('P', Pconv);
+ fmtinstall('S', Sconv);
+ fmtinstall('D', Dconv);
+ fmtinstall('R', Rconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+ char str[STRINGSZ], ss[STRINGSZ], *s;
+ Bits bits;
+ int i;
+
+ str[0] = 0;
+ bits = va_arg(fp->args, Bits);
+ while(bany(&bits)) {
+ i = bnum(bits);
+ if(str[0])
+ strcat(str, " ");
+ if(var[i].sym == S) {
+ sprint(ss, "$%lld", var[i].offset);
+ s = ss;
+ } else
+ s = var[i].sym->name;
+ if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+ break;
+ strcat(str, s);
+ bits.b[i/32] &= ~(1L << (i%32));
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Prog *p;
+
+ p = va_arg(fp->args, Prog*);
+ if(p->as == ADATA)
+ sprint(str, " %A %D/%d,%D",
+ p->as, &p->from, p->from.scale, &p->to);
+ else if(p->as == ATEXT)
+ sprint(str, " %A %D,%d,%D",
+ p->as, &p->from, p->from.scale, &p->to);
+ else
+ sprint(str, " %A %D,%D",
+ p->as, &p->from, &p->to);
+ return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+ int i;
+
+ i = va_arg(fp->args, int);
+ return fmtstrcpy(fp, anames[i]);
+}
+
+int
+Dconv(Fmt *fp)
+{
+ char str[40], s[20];
+ Adr *a;
+ int i;
+
+ a = va_arg(fp->args, Adr*);
+ i = a->type;
+ if(i >= D_INDIR) {
+ if(a->offset)
+ sprint(str, "%lld(%R)", a->offset, i-D_INDIR);
+ else
+ sprint(str, "(%R)", i-D_INDIR);
+ goto brk;
+ }
+ switch(i) {
+
+ default:
+ if(a->offset)
+ sprint(str, "$%lld,%R", a->offset, i);
+ else
+ sprint(str, "%R", i);
+ break;
+
+ case D_NONE:
+ str[0] = 0;
+ break;
+
+ case D_BRANCH:
+ sprint(str, "%lld(PC)", a->offset-pc);
+ break;
+
+ case D_EXTERN:
+ sprint(str, "%s+%lld(SB)", a->sym->name, a->offset);
+ break;
+
+ case D_STATIC:
+ sprint(str, "%s<>+%lld(SB)", a->sym->name,
+ a->offset);
+ break;
+
+ case D_AUTO:
+ sprint(str, "%s+%lld(SP)", a->sym->name, a->offset);
+ break;
+
+ case D_PARAM:
+ if(a->sym)
+ sprint(str, "%s+%lld(FP)", a->sym->name, a->offset);
+ else
+ sprint(str, "%lld(FP)", a->offset);
+ break;
+
+ case D_CONST:
+ sprint(str, "$%lld", a->offset);
+ break;
+
+ case D_FCONST:
+ sprint(str, "$(%.17e)", a->dval);
+ break;
+
+ case D_SCONST:
+ sprint(str, "$\"%S\"", a->sval);
+ break;
+
+ case D_ADDR:
+ a->type = a->index;
+ a->index = D_NONE;
+ sprint(str, "$%D", a);
+ a->index = a->type;
+ a->type = D_ADDR;
+ goto conv;
+ }
+brk:
+ if(a->index != D_NONE) {
+ sprint(s, "(%R*%d)", (int)a->index, (int)a->scale);
+ strcat(str, s);
+ }
+conv:
+ return fmtstrcpy(fp, str);
+}
+
+char* regstr[] =
+{
+ "AL", /* [D_AL] */
+ "CL",
+ "DL",
+ "BL",
+ "SPB",
+ "BPB",
+ "SIB",
+ "DIB",
+ "R8B",
+ "R9B",
+ "R10B",
+ "R11B",
+ "R12B",
+ "R13B",
+ "R14B",
+ "R15B",
+
+ "AX", /* [D_AX] */
+ "CX",
+ "DX",
+ "BX",
+ "SP",
+ "BP",
+ "SI",
+ "DI",
+ "R8",
+ "R9",
+ "R10",
+ "R11",
+ "R12",
+ "R13",
+ "R14",
+ "R15",
+
+ "AH",
+ "CH",
+ "DH",
+ "BH",
+
+ "F0", /* [D_F0] */
+ "F1",
+ "F2",
+ "F3",
+ "F4",
+ "F5",
+ "F6",
+ "F7",
+
+ "M0",
+ "M1",
+ "M2",
+ "M3",
+ "M4",
+ "M5",
+ "M6",
+ "M7",
+
+ "X0",
+ "X1",
+ "X2",
+ "X3",
+ "X4",
+ "X5",
+ "X6",
+ "X7",
+ "X8",
+ "X9",
+ "X10",
+ "X11",
+ "X12",
+ "X13",
+ "X14",
+ "X15",
+
+ "CS", /* [D_CS] */
+ "SS",
+ "DS",
+ "ES",
+ "FS",
+ "GS",
+
+ "GDTR", /* [D_GDTR] */
+ "IDTR", /* [D_IDTR] */
+ "LDTR", /* [D_LDTR] */
+ "MSW", /* [D_MSW] */
+ "TASK", /* [D_TASK] */
+
+ "CR0", /* [D_CR] */
+ "CR1",
+ "CR2",
+ "CR3",
+ "CR4",
+ "CR5",
+ "CR6",
+ "CR7",
+ "CR8",
+ "CR9",
+ "CR10",
+ "CR11",
+ "CR12",
+ "CR13",
+ "CR14",
+ "CR15",
+
+ "DR0", /* [D_DR] */
+ "DR1",
+ "DR2",
+ "DR3",
+ "DR4",
+ "DR5",
+ "DR6",
+ "DR7",
+
+ "TR0", /* [D_TR] */
+ "TR1",
+ "TR2",
+ "TR3",
+ "TR4",
+ "TR5",
+ "TR6",
+ "TR7",
+
+ "NONE", /* [D_NONE] */
+};
+
+int
+Rconv(Fmt *fp)
+{
+ char str[20];
+ int r;
+
+ r = va_arg(fp->args, int);
+ if(r >= D_AL && r <= D_NONE)
+ sprint(str, "%s", regstr[r-D_AL]);
+ else
+ sprint(str, "gok(%d)", r);
+
+ return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+ int i, c;
+ char str[30], *p, *a;
+
+ a = va_arg(fp->args, char*);
+ p = str;
+ for(i=0; i<sizeof(double); i++) {
+ c = a[i] & 0xff;
+ if(c >= 'a' && c <= 'z' ||
+ c >= 'A' && c <= 'Z' ||
+ c >= '0' && c <= '9') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch(c) {
+ default:
+ if(c < 040 || c >= 0177)
+ break; /* not portable */
+ p[-1] = c;
+ continue;
+ case 0:
+ *p++ = 'z';
+ continue;
+ case '\\':
+ case '"':
+ *p++ = c;
+ continue;
+ case '\n':
+ *p++ = 'n';
+ continue;
+ case '\t':
+ *p++ = 't';
+ continue;
+ }
+ *p++ = (c>>6) + '0';
+ *p++ = ((c>>3) & 7) + '0';
+ *p++ = (c & 7) + '0';
+ }
+ *p = 0;
+ return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/6c/machcap.c
@@ -1,0 +1,78 @@
+#include "gc.h"
+
+int
+machcap(Node *n)
+{
+
+ if(n == Z)
+ return 1; /* test */
+
+ switch(n->op) {
+ case OMUL:
+ case OLMUL:
+ case OASMUL:
+ case OASLMUL:
+ if(typechl[n->type->etype])
+ return 1;
+ if(typev[n->type->etype]) {
+ return 1;
+ }
+ break;
+
+ case OCOM:
+ case ONEG:
+ case OADD:
+ case OAND:
+ case OOR:
+ case OSUB:
+ case OXOR:
+ case OASHL:
+ case OLSHR:
+ case OASHR:
+ if(typechlv[n->left->type->etype])
+ return 1;
+ break;
+
+ case OCAST:
+ return 1;
+
+ case OCOND:
+ case OCOMMA:
+ case OLIST:
+ case OANDAND:
+ case OOROR:
+ case ONOT:
+ return 1;
+
+ case OASADD:
+ case OASSUB:
+ case OASAND:
+ case OASOR:
+ case OASXOR:
+ return 1;
+
+ case OASASHL:
+ case OASASHR:
+ case OASLSHR:
+ return 1;
+
+ case OPOSTINC:
+ case OPOSTDEC:
+ case OPREINC:
+ case OPREDEC:
+ return 1;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OGT:
+ case OLT:
+ case OGE:
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+++ b/utils/6c/mkenam
@@ -1,0 +1,15 @@
+ed - ../6c/6.out.h <<'!'
+v/^ A/d
+,s/^ A/ "/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char* anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/6c/mkfile
@@ -1,0 +1,42 @@
+<../../mkconfig
+
+TARG=6c
+
+OFILES=\
+ cgen.$O\
+ enam.$O\
+ list.$O\
+ sgen.$O\
+ swt.$O\
+ txt.$O\
+ reg.$O\
+ peep.$O\
+ pgen.$O\
+ pswt.$O\
+ machcap.$O\
+ div.$O\
+ mul.$O\
+
+HFILES=\
+ gc.h\
+ 6.out.h\
+ ../cc/cc.h\
+
+LIBS=cc bio 9 # order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS= $CFLAGS -I../include
+
+$ROOT/$OBJDIR/lib/libcc.a:
+ cd ../cc
+ mk $MKFLAGS install
+ mk $MKFLAGS clean
+
+%.$O: ../cc/%.c
+ $CC -I. $CFLAGS ../cc/$stem.c
+
+#enam.c: 6.out.h
+# rc mkenam
--- /dev/null
+++ b/utils/6c/mul.c
@@ -1,0 +1,428 @@
+#include "gc.h"
+
+typedef struct Malg Malg;
+typedef struct Mparam Mparam;
+
+struct Malg
+{
+ char vals[10];
+};
+
+struct Mparam
+{
+ ulong value;
+ char alg;
+ char neg;
+ char shift;
+ char arg;
+ char off;
+};
+
+static Mparam multab[32];
+static int mulptr;
+
+static Malg malgs[] =
+{
+ {0, 100},
+ {-1, 1, 100},
+ {-9, -5, -3, 3, 5, 9, 100},
+ {6, 10, 12, 18, 20, 24, 36, 40, 72, 100},
+ {-8, -4, -2, 2, 4, 8, 100},
+};
+
+/*
+ * return position of lowest 1
+ */
+int
+lowbit(ulong v)
+{
+ int s, i;
+ ulong m;
+
+ s = 0;
+ m = 0xFFFFFFFFUL;
+ for(i = 16; i > 0; i >>= 1) {
+ m >>= i;
+ if((v & m) == 0) {
+ v >>= i;
+ s += i;
+ }
+ }
+ return s;
+}
+
+void
+genmuladd(Node *d, Node *s, int m, Node *a)
+{
+ Node nod;
+
+ nod.op = OINDEX;
+ nod.left = a;
+ nod.right = s;
+ nod.scale = m;
+ nod.type = types[TIND];
+ nod.xoffset = 0;
+ xcom(&nod);
+ gopcode(OADDR, d->type, &nod, d);
+}
+
+void
+mulparam(ulong m, Mparam *mp)
+{
+ int c, i, j, n, o, q, s;
+ int bc, bi, bn, bo, bq, bs, bt;
+ char *p;
+ long u;
+ ulong t;
+
+ bc = bq = 10;
+ bi = bn = bo = bs = bt = 0;
+ for(i = 0; i < nelem(malgs); i++) {
+ for(p = malgs[i].vals, j = 0; (o = p[j]) < 100; j++)
+ for(s = 0; s < 2; s++) {
+ c = 10;
+ q = 10;
+ u = m - o;
+ if(u == 0)
+ continue;
+ if(s) {
+ o = -o;
+ if(o > 0)
+ continue;
+ u = -u;
+ }
+ n = lowbit(u);
+ t = (ulong)u >> n;
+ switch(i) {
+ case 0:
+ if(t == 1) {
+ c = s + 1;
+ q = 0;
+ break;
+ }
+ switch(t) {
+ case 3:
+ case 5:
+ case 9:
+ c = s + 1;
+ if(n)
+ c++;
+ q = 0;
+ break;
+ }
+ if(s)
+ break;
+ switch(t) {
+ case 15:
+ case 25:
+ case 27:
+ case 45:
+ case 81:
+ c = 2;
+ if(n)
+ c++;
+ q = 1;
+ break;
+ }
+ break;
+ case 1:
+ if(t == 1) {
+ c = 3;
+ q = 3;
+ break;
+ }
+ switch(t) {
+ case 3:
+ case 5:
+ case 9:
+ c = 3;
+ q = 2;
+ break;
+ }
+ break;
+ case 2:
+ if(t == 1) {
+ c = 3;
+ q = 2;
+ break;
+ }
+ break;
+ case 3:
+ if(s)
+ break;
+ if(t == 1) {
+ c = 3;
+ q = 1;
+ break;
+ }
+ break;
+ case 4:
+ if(t == 1) {
+ c = 3;
+ q = 0;
+ break;
+ }
+ break;
+ }
+ if(c < bc || (c == bc && q > bq)) {
+ bc = c;
+ bi = i;
+ bn = n;
+ bo = o;
+ bq = q;
+ bs = s;
+ bt = t;
+ }
+ }
+ }
+ mp->value = m;
+ if(bc <= 3) {
+ mp->alg = bi;
+ mp->shift = bn;
+ mp->off = bo;
+ mp->neg = bs;
+ mp->arg = bt;
+ }
+ else
+ mp->alg = -1;
+}
+
+int
+m0(int a)
+{
+ switch(a) {
+ case -2:
+ case 2:
+ return 2;
+ case -3:
+ case 3:
+ return 2;
+ case -4:
+ case 4:
+ return 4;
+ case -5:
+ case 5:
+ return 4;
+ case 6:
+ return 2;
+ case -8:
+ case 8:
+ return 8;
+ case -9:
+ case 9:
+ return 8;
+ case 10:
+ return 4;
+ case 12:
+ return 2;
+ case 15:
+ return 2;
+ case 18:
+ return 8;
+ case 20:
+ return 4;
+ case 24:
+ return 2;
+ case 25:
+ return 4;
+ case 27:
+ return 2;
+ case 36:
+ return 8;
+ case 40:
+ return 4;
+ case 45:
+ return 4;
+ case 72:
+ return 8;
+ case 81:
+ return 8;
+ }
+ diag(Z, "bad m0");
+ return 0;
+}
+
+int
+m1(int a)
+{
+ switch(a) {
+ case 15:
+ return 4;
+ case 25:
+ return 4;
+ case 27:
+ return 8;
+ case 45:
+ return 8;
+ case 81:
+ return 8;
+ }
+ diag(Z, "bad m1");
+ return 0;
+}
+
+int
+m2(int a)
+{
+ switch(a) {
+ case 6:
+ return 2;
+ case 10:
+ return 2;
+ case 12:
+ return 4;
+ case 18:
+ return 2;
+ case 20:
+ return 4;
+ case 24:
+ return 8;
+ case 36:
+ return 4;
+ case 40:
+ return 8;
+ case 72:
+ return 8;
+ }
+ diag(Z, "bad m2");
+ return 0;
+}
+
+void
+shiftit(Type *t, Node *s, Node *d)
+{
+ long c;
+
+ c = (long)s->vconst & 31;
+ switch(c) {
+ case 0:
+ break;
+ case 1:
+ gopcode(OADD, t, d, d);
+ break;
+ default:
+ gopcode(OASHL, t, s, d);
+ }
+}
+
+static int
+mulgen1(ulong v, Node *n)
+{
+ int i, o;
+ Mparam *p;
+ Node nod, nods;
+
+ for(i = 0; i < nelem(multab); i++) {
+ p = &multab[i];
+ if(p->value == v)
+ goto found;
+ }
+
+ p = &multab[mulptr];
+ if(++mulptr == nelem(multab))
+ mulptr = 0;
+
+ mulparam(v, p);
+
+found:
+// print("v=%.lx a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off);
+ if(p->alg < 0)
+ return 0;
+
+ nods = *nodconst(p->shift);
+
+ o = OADD;
+ if(p->alg > 0) {
+ regalloc(&nod, n, Z);
+ if(p->off < 0)
+ o = OSUB;
+ }
+
+ switch(p->alg) {
+ case 0:
+ switch(p->arg) {
+ case 1:
+ shiftit(n->type, &nods, n);
+ break;
+ case 15:
+ case 25:
+ case 27:
+ case 45:
+ case 81:
+ genmuladd(n, n, m1(p->arg), n);
+ /* fall thru */
+ case 3:
+ case 5:
+ case 9:
+ genmuladd(n, n, m0(p->arg), n);
+ shiftit(n->type, &nods, n);
+ break;
+ default:
+ goto bad;
+ }
+ if(p->neg == 1)
+ gins(ANEGL, Z, n);
+ break;
+ case 1:
+ switch(p->arg) {
+ case 1:
+ gmove(n, &nod);
+ shiftit(n->type, &nods, &nod);
+ break;
+ case 3:
+ case 5:
+ case 9:
+ genmuladd(&nod, n, m0(p->arg), n);
+ shiftit(n->type, &nods, &nod);
+ break;
+ default:
+ goto bad;
+ }
+ if(p->neg)
+ gopcode(o, n->type, &nod, n);
+ else {
+ gopcode(o, n->type, n, &nod);
+ gmove(&nod, n);
+ }
+ break;
+ case 2:
+ genmuladd(&nod, n, m0(p->off), n);
+ shiftit(n->type, &nods, n);
+ goto comop;
+ case 3:
+ genmuladd(&nod, n, m0(p->off), n);
+ shiftit(n->type, &nods, n);
+ genmuladd(n, &nod, m2(p->off), n);
+ break;
+ case 4:
+ genmuladd(&nod, n, m0(p->off), nodconst(0));
+ shiftit(n->type, &nods, n);
+ goto comop;
+ default:
+ diag(Z, "bad mul alg");
+ break;
+ comop:
+ if(p->neg) {
+ gopcode(o, n->type, n, &nod);
+ gmove(&nod, n);
+ }
+ else
+ gopcode(o, n->type, &nod, n);
+ }
+
+ if(p->alg > 0)
+ regfree(&nod);
+
+ return 1;
+
+bad:
+ diag(Z, "mulgen botch");
+ return 1;
+}
+
+void
+mulgen(Type *t, Node *r, Node *n)
+{
+ if(!mulgen1(r->vconst, n))
+ gopcode(OMUL, t, r, n);
+}
--- /dev/null
+++ b/utils/6c/peep.c
@@ -1,0 +1,846 @@
+#include "gc.h"
+
+static int
+needc(Prog *p)
+{
+ while(p != P) {
+ switch(p->as) {
+ case AADCL:
+ case AADCQ:
+ case ASBBL:
+ case ASBBQ:
+ case ARCRL:
+ case ARCRQ:
+ return 1;
+ case AADDL:
+ case AADDQ:
+ case ASUBL:
+ case ASUBQ:
+ case AJMP:
+ case ARET:
+ case ACALL:
+ return 0;
+ default:
+ if(p->to.type == D_BRANCH)
+ return 0;
+ }
+ p = p->link;
+ }
+ return 0;
+}
+
+static Reg*
+rnops(Reg *r)
+{
+ Prog *p;
+ Reg *r1;
+
+ if(r != R)
+ for(;;){
+ p = r->prog;
+ if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE)
+ break;
+ r1 = uniqs(r);
+ if(r1 == R)
+ break;
+ r = r1;
+ }
+ return r;
+}
+
+void
+peep(void)
+{
+ Reg *r, *r1, *r2;
+ Prog *p, *p1;
+ int t;
+
+ /*
+ * complete R structure
+ */
+ t = 0;
+ for(r=firstr; r!=R; r=r1) {
+ r1 = r->link;
+ if(r1 == R)
+ break;
+ p = r->prog->link;
+ while(p != r1->prog)
+ switch(p->as) {
+ default:
+ r2 = rega();
+ r->link = r2;
+ r2->link = r1;
+
+ r2->prog = p;
+ r2->p1 = r;
+ r->s1 = r2;
+ r2->s1 = r1;
+ r1->p1 = r2;
+
+ r = r2;
+ t++;
+
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ p = p->link;
+ }
+ }
+
+ pc = 0; /* speculating it won't kill */
+
+loop1:
+
+ t = 0;
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ case AMOVL:
+ case AMOVQ:
+ case AMOVSS:
+ case AMOVSD:
+ if(regtyp(&p->to))
+ if(regtyp(&p->from)) {
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ } else
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ break;
+
+ case AMOVBLZX:
+ case AMOVWLZX:
+ case AMOVBLSX:
+ case AMOVWLSX:
+ if(regtyp(&p->to)) {
+ r1 = rnops(uniqs(r));
+ if(r1 != R) {
+ p1 = r1->prog;
+ if(p->as == p1->as && p->to.type == p1->from.type){
+ p1->as = AMOVL;
+ t++;
+ }
+ }
+ }
+ break;
+
+ case AMOVBQSX:
+ case AMOVBQZX:
+ case AMOVWQSX:
+ case AMOVWQZX:
+ case AMOVLQSX:
+ case AMOVLQZX:
+ if(regtyp(&p->to)) {
+ r1 = rnops(uniqs(r));
+ if(r1 != R) {
+ p1 = r1->prog;
+ if(p->as == p1->as && p->to.type == p1->from.type){
+ p1->as = AMOVQ;
+ t++;
+ }
+ }
+ }
+ break;
+
+ case AADDL:
+ case AADDQ:
+ case AADDW:
+ if(p->from.type != D_CONST || needc(p->link))
+ break;
+ if(p->from.offset == -1){
+ if(p->as == AADDQ)
+ p->as = ADECQ;
+ else if(p->as == AADDL)
+ p->as = ADECL;
+ else
+ p->as = ADECW;
+ p->from = zprog.from;
+ }
+ else if(p->from.offset == 1){
+ if(p->as == AADDQ)
+ p->as = AINCQ;
+ else if(p->as == AADDL)
+ p->as = AINCL;
+ else
+ p->as = AINCW;
+ p->from = zprog.from;
+ }
+ break;
+
+ case ASUBL:
+ case ASUBQ:
+ case ASUBW:
+ if(p->from.type != D_CONST || needc(p->link))
+ break;
+ if(p->from.offset == -1) {
+ if(p->as == ASUBQ)
+ p->as = AINCQ;
+ else if(p->as == ASUBL)
+ p->as = AINCL;
+ else
+ p->as = AINCW;
+ p->from = zprog.from;
+ }
+ else if(p->from.offset == 1){
+ if(p->as == ASUBQ)
+ p->as = ADECQ;
+ else if(p->as == ASUBL)
+ p->as = ADECL;
+ else
+ p->as = ADECW;
+ p->from = zprog.from;
+ }
+ break;
+ }
+ }
+ if(t)
+ goto loop1;
+}
+
+void
+excise(Reg *r)
+{
+ Prog *p;
+
+ p = r->prog;
+ p->as = ANOP;
+ p->from = zprog.from;
+ p->to = zprog.to;
+}
+
+Reg*
+uniqp(Reg *r)
+{
+ Reg *r1;
+
+ r1 = r->p1;
+ if(r1 == R) {
+ r1 = r->p2;
+ if(r1 == R || r1->p2link != R)
+ return R;
+ } else
+ if(r->p2 != R)
+ return R;
+ return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+ Reg *r1;
+
+ r1 = r->s1;
+ if(r1 == R) {
+ r1 = r->s2;
+ if(r1 == R)
+ return R;
+ } else
+ if(r->s2 != R)
+ return R;
+ return r1;
+}
+
+int
+regtyp(Adr *a)
+{
+ int t;
+
+ t = a->type;
+ if(t >= D_AX && t <= D_R15)
+ return 1;
+ if(t >= D_X0 && t <= D_X0+15)
+ return 1;
+ return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ * MOV a, R0
+ * ADD b, R0 / no use of R1
+ * MOV R0, R1
+ * would be converted to
+ * MOV a, R1
+ * ADD b, R1
+ * MOV R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+ Prog *p;
+ Adr *v1, *v2;
+ Reg *r;
+ int t;
+
+ p = r0->prog;
+ v1 = &p->from;
+ if(!regtyp(v1))
+ return 0;
+ v2 = &p->to;
+ if(!regtyp(v2))
+ return 0;
+ for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+ if(uniqs(r) == R)
+ break;
+ p = r->prog;
+ switch(p->as) {
+ case ACALL:
+ return 0;
+
+ case AIMULL:
+ case AIMULQ:
+ case AIMULW:
+ if(p->to.type != D_NONE)
+ break;
+
+ case ADIVB:
+ case ADIVL:
+ case ADIVQ:
+ case ADIVW:
+ case AIDIVB:
+ case AIDIVL:
+ case AIDIVQ:
+ case AIDIVW:
+ case AIMULB:
+ case AMULB:
+ case AMULL:
+ case AMULQ:
+ case AMULW:
+
+ case AROLB:
+ case AROLL:
+ case AROLQ:
+ case AROLW:
+ case ARORB:
+ case ARORL:
+ case ARORQ:
+ case ARORW:
+ case ASALB:
+ case ASALL:
+ case ASALQ:
+ case ASALW:
+ case ASARB:
+ case ASARL:
+ case ASARQ:
+ case ASARW:
+ case ASHLB:
+ case ASHLL:
+ case ASHLQ:
+ case ASHLW:
+ case ASHRB:
+ case ASHRL:
+ case ASHRQ:
+ case ASHRW:
+
+ case AREP:
+ case AREPN:
+
+ case ACWD:
+ case ACDQ:
+ case ACQO:
+
+ case AMOVSL:
+ case AMOVSQ:
+ return 0;
+
+ case AMOVL:
+ case AMOVQ:
+ if(p->to.type == v1->type)
+ goto gotit;
+ break;
+ }
+ if(copyau(&p->from, v2) ||
+ copyau(&p->to, v2))
+ break;
+ if(copysub(&p->from, v1, v2, 0) ||
+ copysub(&p->to, v1, v2, 0))
+ break;
+ }
+ return 0;
+
+gotit:
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P']) {
+ print("gotit: %D->%D\n%P", v1, v2, r->prog);
+ if(p->from.type == v2->type)
+ print(" excise");
+ print("\n");
+ }
+ for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+ p = r->prog;
+ copysub(&p->from, v1, v2, 1);
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P'])
+ print("%P\n", r->prog);
+ }
+ t = v1->type;
+ v1->type = v2->type;
+ v2->type = t;
+ if(debug['P'])
+ print("%P last\n", r->prog);
+ return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * use v2 return fail
+ * -----------------
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * set v2 return success
+ */
+int
+copyprop(Reg *r0)
+{
+ Prog *p;
+ Adr *v1, *v2;
+ Reg *r;
+
+ p = r0->prog;
+ v1 = &p->from;
+ v2 = &p->to;
+ if(copyas(v1, v2))
+ return 1;
+ for(r=firstr; r!=R; r=r->link)
+ r->active = 0;
+ return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+ int t;
+ Prog *p;
+
+ if(r->active) {
+ if(debug['P'])
+ print("act set; return 1\n");
+ return 1;
+ }
+ r->active = 1;
+ if(debug['P'])
+ print("copy %D->%D f=%d\n", v1, v2, f);
+ for(; r != R; r = r->s1) {
+ p = r->prog;
+ if(debug['P'])
+ print("%P", p);
+ if(!f && uniqp(r) == R) {
+ f = 1;
+ if(debug['P'])
+ print("; merge; f=%d", f);
+ }
+ t = copyu(p, v2, A);
+ switch(t) {
+ case 2: /* rar, cant split */
+ if(debug['P'])
+ print("; %D rar; return 0\n", v2);
+ return 0;
+
+ case 3: /* set */
+ if(debug['P'])
+ print("; %D set; return 1\n", v2);
+ return 1;
+
+ case 1: /* used, substitute */
+ case 4: /* use and set */
+ if(f) {
+ if(!debug['P'])
+ return 0;
+ if(t == 4)
+ print("; %D used+set and f=%d; return 0\n", v2, f);
+ else
+ print("; %D used and f=%d; return 0\n", v2, f);
+ return 0;
+ }
+ if(copyu(p, v2, v1)) {
+ if(debug['P'])
+ print("; sub fail; return 0\n");
+ return 0;
+ }
+ if(debug['P'])
+ print("; sub %D/%D", v2, v1);
+ if(t == 4) {
+ if(debug['P'])
+ print("; %D used+set; return 1\n", v2);
+ return 1;
+ }
+ break;
+ }
+ if(!f) {
+ t = copyu(p, v1, A);
+ if(!f && (t == 2 || t == 3 || t == 4)) {
+ f = 1;
+ if(debug['P'])
+ print("; %D set and !f; f=%d", v1, f);
+ }
+ }
+ if(debug['P'])
+ print("\n");
+ if(r->s2)
+ if(!copy1(v1, v2, r->s2, f))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+ switch(p->as) {
+
+ default:
+ if(debug['P'])
+ print("unknown op %A\n", p->as);
+ /* SBBL; ADCL; FLD1; SAHF */
+ return 2;
+
+
+ case ANEGB:
+ case ANEGW:
+ case ANEGL:
+ case ANEGQ:
+ case ANOTB:
+ case ANOTW:
+ case ANOTL:
+ case ANOTQ:
+ if(copyas(&p->to, v))
+ return 2;
+ break;
+
+ case ALEAL: /* lhs addr, rhs store */
+ case ALEAQ:
+ if(copyas(&p->from, v))
+ return 2;
+
+
+ case ANOP: /* rhs store */
+ case AMOVL:
+ case AMOVQ:
+ case AMOVBLSX:
+ case AMOVBLZX:
+ case AMOVBQSX:
+ case AMOVBQZX:
+ case AMOVLQSX:
+ case AMOVLQZX:
+ case AMOVWLSX:
+ case AMOVWLZX:
+ case AMOVWQSX:
+ case AMOVWQZX:
+
+ case AMOVSS:
+ case AMOVSD:
+ case ACVTSD2SL:
+ case ACVTSD2SQ:
+ case ACVTSD2SS:
+ case ACVTSL2SD:
+ case ACVTSL2SS:
+ case ACVTSQ2SD:
+ case ACVTSQ2SS:
+ case ACVTSS2SD:
+ case ACVTSS2SL:
+ case ACVTSS2SQ:
+ case ACVTTSD2SL:
+ case ACVTTSD2SQ:
+ case ACVTTSS2SL:
+ case ACVTTSS2SQ:
+ if(copyas(&p->to, v)) {
+ if(s != A)
+ return copysub(&p->from, v, s, 1);
+ if(copyau(&p->from, v))
+ return 4;
+ return 3;
+ }
+ goto caseread;
+
+ case AROLB:
+ case AROLL:
+ case AROLQ:
+ case AROLW:
+ case ARORB:
+ case ARORL:
+ case ARORQ:
+ case ARORW:
+ case ASALB:
+ case ASALL:
+ case ASALQ:
+ case ASALW:
+ case ASARB:
+ case ASARL:
+ case ASARQ:
+ case ASARW:
+ case ASHLB:
+ case ASHLL:
+ case ASHLQ:
+ case ASHLW:
+ case ASHRB:
+ case ASHRL:
+ case ASHRQ:
+ case ASHRW:
+ if(copyas(&p->to, v))
+ return 2;
+ if(copyas(&p->from, v))
+ if(p->from.type == D_CX)
+ return 2;
+ goto caseread;
+
+ case AADDB: /* rhs rar */
+ case AADDL:
+ case AADDQ:
+ case AADDW:
+ case AANDB:
+ case AANDL:
+ case AANDQ:
+ case AANDW:
+ case ADECL:
+ case ADECQ:
+ case ADECW:
+ case AINCL:
+ case AINCQ:
+ case AINCW:
+ case ASUBB:
+ case ASUBL:
+ case ASUBQ:
+ case ASUBW:
+ case AORB:
+ case AORL:
+ case AORQ:
+ case AORW:
+ case AXORB:
+ case AXORL:
+ case AXORQ:
+ case AXORW:
+ case AMOVB:
+ case AMOVW:
+
+ case AADDSD:
+ case AADDSS:
+ case ACMPSD:
+ case ACMPSS:
+ case ADIVSD:
+ case ADIVSS:
+ case AMAXSD:
+ case AMAXSS:
+ case AMINSD:
+ case AMINSS:
+ case AMULSD:
+ case AMULSS:
+ case ARCPSS:
+ case ARSQRTSS:
+ case ASQRTSD:
+ case ASQRTSS:
+ case ASUBSD:
+ case ASUBSS:
+ case AXORPD:
+ if(copyas(&p->to, v))
+ return 2;
+ goto caseread;
+
+ case ACMPL: /* read only */
+ case ACMPW:
+ case ACMPB:
+ case ACMPQ:
+
+ case ACOMISD:
+ case ACOMISS:
+ case AUCOMISD:
+ case AUCOMISS:
+ caseread:
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return copysub(&p->to, v, s, 1);
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ break;
+
+ case AJGE: /* no reference */
+ case AJNE:
+ case AJLE:
+ case AJEQ:
+ case AJHI:
+ case AJLS:
+ case AJMI:
+ case AJPL:
+ case AJGT:
+ case AJLT:
+ case AJCC:
+ case AJCS:
+
+ case AADJSP:
+ case AWAIT:
+ case ACLD:
+ break;
+
+ case AIMULL:
+ case AIMULQ:
+ case AIMULW:
+ if(p->to.type != D_NONE) {
+ if(copyas(&p->to, v))
+ return 2;
+ goto caseread;
+ }
+
+ case ADIVB:
+ case ADIVL:
+ case ADIVQ:
+ case ADIVW:
+ case AIDIVB:
+ case AIDIVL:
+ case AIDIVQ:
+ case AIDIVW:
+ case AIMULB:
+ case AMULB:
+ case AMULL:
+ case AMULQ:
+ case AMULW:
+
+ case ACWD:
+ case ACDQ:
+ case ACQO:
+ if(v->type == D_AX || v->type == D_DX)
+ return 2;
+ goto caseread;
+
+ case AMOVSL:
+ case AMOVSQ:
+ case AREP:
+ case AREPN:
+ if(v->type == D_CX || v->type == D_DI || v->type == D_SI)
+ return 2;
+ goto caseread;
+
+ case AJMP: /* funny */
+ if(s != A) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ARET: /* funny */
+ if(v->type == REGRET || v->type == FREGRET)
+ return 2;
+ if(s != A)
+ return 1;
+ return 3;
+
+ case ACALL: /* funny */
+ if(REGEXT && v->type <= REGEXT && v->type > exregoffset)
+ return 2;
+ if(REGARG && v->type == REGARG)
+ return 2;
+
+ if(s != A) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 4;
+ return 3;
+
+ case ATEXT: /* funny */
+ if(REGARG && v->type == REGARG)
+ return 3;
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+ if(a->type != v->type)
+ return 0;
+ if(regtyp(v))
+ return 1;
+ if(v->type == D_AUTO || v->type == D_PARAM)
+ if(v->offset == a->offset)
+ return 1;
+ return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+ if(copyas(a, v))
+ return 1;
+ if(regtyp(v)) {
+ if(a->type-D_INDIR == v->type)
+ return 1;
+ if(a->index == v->type)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+ int t;
+
+ if(copyas(a, v)) {
+ t = s->type;
+ if(t >= D_AX && t <= D_R15 || t >= D_X0 && t <= D_X0+15) {
+ if(f)
+ a->type = t;
+ }
+ return 0;
+ }
+ if(regtyp(v)) {
+ t = v->type;
+ if(a->type == t+D_INDIR) {
+ if((s->type == D_BP || s->type == D_R13) && a->index != D_NONE)
+ return 1; /* can't use BP-base with index */
+ if(f)
+ a->type = s->type+D_INDIR;
+// return 0;
+ }
+ if(a->index == t) {
+ if(f)
+ a->index = s->type;
+ return 0;
+ }
+ return 0;
+ }
+ return 0;
+}
--- /dev/null
+++ b/utils/6c/reg.c
@@ -1,0 +1,1358 @@
+#include "gc.h"
+
+Reg*
+rega(void)
+{
+ Reg *r;
+
+ r = freer;
+ if(r == R) {
+ r = alloc(sizeof(*r));
+ } else
+ freer = r->link;
+
+ *r = zreg;
+ return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+ Rgn *p1, *p2;
+ int c1, c2;
+
+ p1 = (Rgn*)a1;
+ p2 = (Rgn*)a2;
+ c1 = p2->cost;
+ c2 = p1->cost;
+ if(c1 -= c2)
+ return c1;
+ return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+ Reg *r, *r1, *r2;
+ Prog *p1;
+ int i, z;
+ long initpc, val, npc;
+ ulong vreg;
+ Bits bit;
+ struct
+ {
+ long m;
+ long c;
+ Reg* p;
+ } log5[6], *lp;
+
+ firstr = R;
+ lastr = R;
+ nvar = 0;
+ regbits = RtoB(D_SP) | RtoB(D_AX) | RtoB(D_X0);
+ if(REGEXT)
+ regbits |= RtoB(REGEXT) | RtoB(REGEXT-1);
+ for(z=0; z<BITS; z++) {
+ externs.b[z] = 0;
+ params.b[z] = 0;
+ consts.b[z] = 0;
+ addrs.b[z] = 0;
+ }
+
+ /*
+ * pass 1
+ * build aux data structure
+ * allocate pcs
+ * find use and set of variables
+ */
+ val = 5L * 5L * 5L * 5L * 5L;
+ lp = log5;
+ for(i=0; i<5; i++) {
+ lp->m = val;
+ lp->c = 0;
+ lp->p = R;
+ val /= 5L;
+ lp++;
+ }
+ val = 0;
+ for(; p != P; p = p->link) {
+ switch(p->as) {
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ continue;
+ }
+ r = rega();
+ if(firstr == R) {
+ firstr = r;
+ lastr = r;
+ } else {
+ lastr->link = r;
+ r->p1 = lastr;
+ lastr->s1 = r;
+ lastr = r;
+ }
+ r->prog = p;
+ r->pc = val;
+ val++;
+
+ lp = log5;
+ for(i=0; i<5; i++) {
+ lp->c--;
+ if(lp->c <= 0) {
+ lp->c = lp->m;
+ if(lp->p != R)
+ lp->p->log5 = r;
+ lp->p = r;
+ (lp+1)->c = 0;
+ break;
+ }
+ lp++;
+ }
+
+ r1 = r->p1;
+ if(r1 != R)
+ switch(r1->prog->as) {
+ case ARET:
+ case AJMP:
+ case AIRETL:
+ case AIRETQ:
+ r->p1 = R;
+ r1->s1 = R;
+ }
+
+ bit = mkvar(r, &p->from);
+ if(bany(&bit))
+ switch(p->as) {
+ /*
+ * funny
+ */
+ case ALEAL:
+ case ALEAQ:
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ break;
+
+ /*
+ * left side read
+ */
+ default:
+ for(z=0; z<BITS; z++)
+ r->use1.b[z] |= bit.b[z];
+ break;
+ }
+
+ bit = mkvar(r, &p->to);
+ if(bany(&bit))
+ switch(p->as) {
+ default:
+ diag(Z, "reg: unknown op: %A", p->as);
+ break;
+
+ /*
+ * right side read
+ */
+ case ACMPB:
+ case ACMPL:
+ case ACMPQ:
+ case ACMPW:
+ case ACOMISS:
+ case ACOMISD:
+ case AUCOMISS:
+ case AUCOMISD:
+ for(z=0; z<BITS; z++)
+ r->use2.b[z] |= bit.b[z];
+ break;
+
+ /*
+ * right side write
+ */
+ case ANOP:
+ case AMOVL:
+ case AMOVQ:
+ case AMOVB:
+ case AMOVW:
+ case AMOVBLSX:
+ case AMOVBLZX:
+ case AMOVBQSX:
+ case AMOVBQZX:
+ case AMOVLQSX:
+ case AMOVLQZX:
+ case AMOVWLSX:
+ case AMOVWLZX:
+ case AMOVWQSX:
+ case AMOVWQZX:
+
+ case AMOVSS:
+ case AMOVSD:
+ case ACVTSD2SL:
+ case ACVTSD2SQ:
+ case ACVTSD2SS:
+ case ACVTSL2SD:
+ case ACVTSL2SS:
+ case ACVTSQ2SD:
+ case ACVTSQ2SS:
+ case ACVTSS2SD:
+ case ACVTSS2SL:
+ case ACVTSS2SQ:
+ case ACVTTSD2SL:
+ case ACVTTSD2SQ:
+ case ACVTTSS2SL:
+ case ACVTTSS2SQ:
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
+ break;
+
+ /*
+ * right side read+write
+ */
+ case AADDB:
+ case AADDL:
+ case AADDQ:
+ case AADDW:
+ case AANDB:
+ case AANDL:
+ case AANDQ:
+ case AANDW:
+ case ASUBB:
+ case ASUBL:
+ case ASUBQ:
+ case ASUBW:
+ case AORB:
+ case AORL:
+ case AORQ:
+ case AORW:
+ case AXORB:
+ case AXORL:
+ case AXORQ:
+ case AXORW:
+ case ASALB:
+ case ASALL:
+ case ASALQ:
+ case ASALW:
+ case ASARB:
+ case ASARL:
+ case ASARQ:
+ case ASARW:
+ case AROLB:
+ case AROLL:
+ case AROLQ:
+ case AROLW:
+ case ARORB:
+ case ARORL:
+ case ARORQ:
+ case ARORW:
+ case ASHLB:
+ case ASHLL:
+ case ASHLQ:
+ case ASHLW:
+ case ASHRB:
+ case ASHRL:
+ case ASHRQ:
+ case ASHRW:
+ case AIMULL:
+ case AIMULQ:
+ case AIMULW:
+ case ANEGL:
+ case ANEGQ:
+ case ANOTL:
+ case ANOTQ:
+ case AADCL:
+ case AADCQ:
+ case ASBBL:
+ case ASBBQ:
+
+ case AADDSD:
+ case AADDSS:
+ case ACMPSD:
+ case ACMPSS:
+ case ADIVSD:
+ case ADIVSS:
+ case AMAXSD:
+ case AMAXSS:
+ case AMINSD:
+ case AMINSS:
+ case AMULSD:
+ case AMULSS:
+ case ARCPSS:
+ case ARSQRTSS:
+ case ASQRTSD:
+ case ASQRTSS:
+ case ASUBSD:
+ case ASUBSS:
+ case AXORPD:
+ for(z=0; z<BITS; z++) {
+ r->set.b[z] |= bit.b[z];
+ r->use2.b[z] |= bit.b[z];
+ }
+ break;
+
+ /*
+ * funny
+ */
+ case ACALL:
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ break;
+ }
+
+ switch(p->as) {
+ case AIMULL:
+ case AIMULQ:
+ case AIMULW:
+ if(p->to.type != D_NONE)
+ break;
+
+ case AIDIVB:
+ case AIDIVL:
+ case AIDIVQ:
+ case AIDIVW:
+ case AIMULB:
+ case ADIVB:
+ case ADIVL:
+ case ADIVQ:
+ case ADIVW:
+ case AMULB:
+ case AMULL:
+ case AMULQ:
+ case AMULW:
+
+ case ACWD:
+ case ACDQ:
+ case ACQO:
+ r->regu |= RtoB(D_AX) | RtoB(D_DX);
+ break;
+
+ case AREP:
+ case AREPN:
+ case ALOOP:
+ case ALOOPEQ:
+ case ALOOPNE:
+ r->regu |= RtoB(D_CX);
+ break;
+
+ case AMOVSB:
+ case AMOVSL:
+ case AMOVSQ:
+ case AMOVSW:
+ case ACMPSB:
+ case ACMPSL:
+ case ACMPSQ:
+ case ACMPSW:
+ r->regu |= RtoB(D_SI) | RtoB(D_DI);
+ break;
+
+ case ASTOSB:
+ case ASTOSL:
+ case ASTOSQ:
+ case ASTOSW:
+ case ASCASB:
+ case ASCASL:
+ case ASCASQ:
+ case ASCASW:
+ r->regu |= RtoB(D_AX) | RtoB(D_DI);
+ break;
+
+ case AINSB:
+ case AINSL:
+ case AINSW:
+ case AOUTSB:
+ case AOUTSL:
+ case AOUTSW:
+ r->regu |= RtoB(D_DI) | RtoB(D_DX);
+ break;
+ }
+ }
+ if(firstr == R)
+ return;
+ initpc = pc - val;
+ npc = val;
+
+ /*
+ * pass 2
+ * turn branch references to pointers
+ * build back pointers
+ */
+ for(r = firstr; r != R; r = r->link) {
+ p = r->prog;
+ if(p->to.type == D_BRANCH) {
+ val = p->to.offset - initpc;
+ r1 = firstr;
+ while(r1 != R) {
+ r2 = r1->log5;
+ if(r2 != R && val >= r2->pc) {
+ r1 = r2;
+ continue;
+ }
+ if(r1->pc == val)
+ break;
+ r1 = r1->link;
+ }
+ if(r1 == R) {
+ nearln = p->lineno;
+ diag(Z, "ref not found\n%P", p);
+ continue;
+ }
+ if(r1 == r) {
+ nearln = p->lineno;
+ diag(Z, "ref to self\n%P", p);
+ continue;
+ }
+ r->s2 = r1;
+ r->p2link = r1->p2;
+ r1->p2 = r;
+ }
+ }
+ if(debug['R']) {
+ p = firstr->prog;
+ print("\n%L %D\n", p->lineno, &p->from);
+ }
+
+ /*
+ * pass 2.5
+ * find looping structure
+ */
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ change = 0;
+ loopit(firstr, npc);
+ if(debug['R'] && debug['v']) {
+ print("\nlooping structure:\n");
+ for(r = firstr; r != R; r = r->link) {
+ print("%ld:%P", r->loop, r->prog);
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r->use1.b[z] |
+ r->use2.b[z] |
+ r->set.b[z];
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->use1))
+ print(" u1=%B", r->use1);
+ if(bany(&r->use2))
+ print(" u2=%B", r->use2);
+ if(bany(&r->set))
+ print(" st=%B", r->set);
+ }
+ print("\n");
+ }
+ }
+
+ /*
+ * pass 3
+ * iterate propagating usage
+ * back until flow graph is complete
+ */
+loop1:
+ change = 0;
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ for(r = firstr; r != R; r = r->link)
+ if(r->prog->as == ARET)
+ prop(r, zbits, zbits);
+loop11:
+ /* pick up unreachable code */
+ i = 0;
+ for(r = firstr; r != R; r = r1) {
+ r1 = r->link;
+ if(r1 && r1->active && !r->active) {
+ prop(r, zbits, zbits);
+ i = 1;
+ }
+ }
+ if(i)
+ goto loop11;
+ if(change)
+ goto loop1;
+
+
+ /*
+ * pass 4
+ * iterate propagating register/variable synchrony
+ * forward until graph is complete
+ */
+loop2:
+ change = 0;
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ synch(firstr, zbits);
+ if(change)
+ goto loop2;
+
+
+ /*
+ * pass 5
+ * isolate regions
+ * calculate costs (paint1)
+ */
+ r = firstr;
+ if(r) {
+ for(z=0; z<BITS; z++)
+ bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+ ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+ if(bany(&bit)) {
+ nearln = r->prog->lineno;
+ warn(Z, "used and not set: %B", bit);
+ if(debug['R'] && !debug['w'])
+ print("used and not set: %B\n", bit);
+ }
+ }
+ if(debug['R'] && debug['v'])
+ print("\nprop structure:\n");
+ for(r = firstr; r != R; r = r->link)
+ r->act = zbits;
+ rgp = region;
+ nregion = 0;
+ for(r = firstr; r != R; r = r->link) {
+ if(debug['R'] && debug['v']) {
+ print("%P\t", r->prog);
+ if(bany(&r->set))
+ print("s:%B ", r->set);
+ if(bany(&r->refahead))
+ print("ra:%B ", r->refahead);
+ if(bany(&r->calahead))
+ print("ca:%B ", r->calahead);
+ print("\n");
+ }
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r->set.b[z] &
+ ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+ if(bany(&bit)) {
+ nearln = r->prog->lineno;
+ warn(Z, "set and not used: %B", bit);
+ if(debug['R'])
+ print("set and not used: %B\n", bit);
+ excise(r);
+ }
+ for(z=0; z<BITS; z++)
+ bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+ while(bany(&bit)) {
+ i = bnum(bit);
+ rgp->enter = r;
+ rgp->varno = i;
+ change = 0;
+ if(debug['R'] && debug['v'])
+ print("\n");
+ paint1(r, i);
+ bit.b[i/32] &= ~(1L<<(i%32));
+ if(change <= 0) {
+ if(debug['R'])
+ print("%L$%d: %B\n",
+ r->prog->lineno, change, blsh(i));
+ continue;
+ }
+ rgp->cost = change;
+ nregion++;
+ if(nregion >= NRGN) {
+ warn(Z, "too many regions");
+ goto brk;
+ }
+ rgp++;
+ }
+ }
+brk:
+ qsort(region, nregion, sizeof(region[0]), rcmp);
+
+ /*
+ * pass 6
+ * determine used registers (paint2)
+ * replace code (paint3)
+ */
+ rgp = region;
+ for(i=0; i<nregion; i++) {
+ bit = blsh(rgp->varno);
+ vreg = paint2(rgp->enter, rgp->varno);
+ vreg = allreg(vreg, rgp);
+ if(debug['R']) {
+ print("%L$%d %R: %B\n",
+ rgp->enter->prog->lineno,
+ rgp->cost,
+ rgp->regno,
+ bit);
+ }
+ if(rgp->regno != 0)
+ paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+ rgp++;
+ }
+ /*
+ * pass 7
+ * peep-hole on basic block
+ */
+ if(!debug['R'] || debug['P'])
+ peep();
+
+ /*
+ * pass 8
+ * recalculate pc
+ */
+ val = initpc;
+ for(r = firstr; r != R; r = r1) {
+ r->pc = val;
+ p = r->prog;
+ p1 = P;
+ r1 = r->link;
+ if(r1 != R)
+ p1 = r1->prog;
+ for(; p != p1; p = p->link) {
+ switch(p->as) {
+ default:
+ val++;
+ break;
+
+ case ANOP:
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ break;
+ }
+ }
+ }
+ pc = val;
+
+ /*
+ * fix up branches
+ */
+ if(debug['R'])
+ if(bany(&addrs))
+ print("addrs: %B\n", addrs);
+
+ r1 = 0; /* set */
+ for(r = firstr; r != R; r = r->link) {
+ p = r->prog;
+ if(p->to.type == D_BRANCH)
+ p->to.offset = r->s2->pc;
+ r1 = r;
+ }
+
+ /*
+ * last pass
+ * eliminate nops
+ * free aux structures
+ */
+ for(p = firstr->prog; p != P; p = p->link){
+ while(p->link && p->link->as == ANOP)
+ p->link = p->link->link;
+ }
+ if(r1 != R) {
+ r1->link = freer;
+ freer = firstr;
+ }
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+ Prog *p, *p1;
+ Adr *a;
+ Var *v;
+
+ p1 = alloc(sizeof(*p1));
+ *p1 = zprog;
+ p = r->prog;
+
+ p1->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+
+ v = var + bn;
+
+ a = &p1->to;
+ a->sym = v->sym;
+ a->offset = v->offset;
+ a->etype = v->etype;
+ a->type = v->name;
+
+ p1->as = AMOVL;
+ if(v->etype == TCHAR || v->etype == TUCHAR)
+ p1->as = AMOVB;
+ if(v->etype == TSHORT || v->etype == TUSHORT)
+ p1->as = AMOVW;
+ if(v->etype == TVLONG || v->etype == TUVLONG || v->etype == TIND)
+ p1->as = AMOVQ;
+ if(v->etype == TFLOAT)
+ p1->as = AMOVSS;
+ if(v->etype == TDOUBLE)
+ p1->as = AMOVSD;
+
+ p1->from.type = rn;
+ if(!f) {
+ p1->from = *a;
+ *a = zprog.from;
+ a->type = rn;
+ if(v->etype == TUCHAR)
+ p1->as = AMOVB;
+ if(v->etype == TUSHORT)
+ p1->as = AMOVW;
+ }
+ if(debug['R'])
+ print("%P\t.a%P\n", p, p1);
+}
+
+ulong
+doregbits(int r)
+{
+ ulong b;
+
+ b = 0;
+ if(r >= D_INDIR)
+ r -= D_INDIR;
+ if(r >= D_AX && r <= D_R15)
+ b |= RtoB(r);
+ else
+ if(r >= D_AL && r <= D_R15B)
+ b |= RtoB(r-D_AL+D_AX);
+ else
+ if(r >= D_AH && r <= D_BH)
+ b |= RtoB(r-D_AH+D_AX);
+ else
+ if(r >= D_X0 && r <= D_X0+15)
+ b |= FtoB(r);
+ return b;
+}
+
+Bits
+mkvar(Reg *r, Adr *a)
+{
+ Var *v;
+ int i, t, n, et, z;
+ long o;
+ Bits bit;
+ Sym *s;
+
+ /*
+ * mark registers used
+ */
+ t = a->type;
+ r->regu |= doregbits(t);
+ r->regu |= doregbits(a->index);
+
+ switch(t) {
+ default:
+ goto none;
+ case D_ADDR:
+ a->type = a->index;
+ bit = mkvar(r, a);
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ a->type = t;
+ goto none;
+ case D_EXTERN:
+ case D_STATIC:
+ case D_PARAM:
+ case D_AUTO:
+ n = t;
+ break;
+ }
+ s = a->sym;
+ if(s == S)
+ goto none;
+ if(s->name[0] == '.')
+ goto none;
+ et = a->etype;
+ o = a->offset;
+ v = var;
+ for(i=0; i<nvar; i++) {
+ if(s == v->sym)
+ if(n == v->name)
+ if(o == v->offset)
+ goto out;
+ v++;
+ }
+ if(nvar >= NVAR) {
+ if(debug['w'] > 1 && s)
+ warn(Z, "variable not optimized: %s", s->name);
+ goto none;
+ }
+ i = nvar;
+ nvar++;
+ v = &var[i];
+ v->sym = s;
+ v->offset = o;
+ v->name = n;
+ v->etype = et;
+ if(debug['R'])
+ print("bit=%2d et=%2d %D\n", i, et, a);
+
+out:
+ bit = blsh(i);
+ if(n == D_EXTERN || n == D_STATIC)
+ for(z=0; z<BITS; z++)
+ externs.b[z] |= bit.b[z];
+ if(n == D_PARAM)
+ for(z=0; z<BITS; z++)
+ params.b[z] |= bit.b[z];
+ if(v->etype != et || !(typechlpfd[et] || typev[et])) /* funny punning */
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ return bit;
+
+none:
+ return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+ Reg *r1, *r2;
+ int z;
+
+ for(r1 = r; r1 != R; r1 = r1->p1) {
+ for(z=0; z<BITS; z++) {
+ ref.b[z] |= r1->refahead.b[z];
+ if(ref.b[z] != r1->refahead.b[z]) {
+ r1->refahead.b[z] = ref.b[z];
+ change++;
+ }
+ cal.b[z] |= r1->calahead.b[z];
+ if(cal.b[z] != r1->calahead.b[z]) {
+ r1->calahead.b[z] = cal.b[z];
+ change++;
+ }
+ }
+ switch(r1->prog->as) {
+ case ACALL:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] |= ref.b[z] | externs.b[z];
+ ref.b[z] = 0;
+ }
+ break;
+
+ case ATEXT:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = 0;
+ ref.b[z] = 0;
+ }
+ break;
+
+ case ARET:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = externs.b[z];
+ ref.b[z] = 0;
+ }
+ }
+ for(z=0; z<BITS; z++) {
+ ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+ r1->use1.b[z] | r1->use2.b[z];
+ cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+ r1->refbehind.b[z] = ref.b[z];
+ r1->calbehind.b[z] = cal.b[z];
+ }
+ if(r1->active)
+ break;
+ r1->active = 1;
+ }
+ for(; r != r1; r = r->p1)
+ for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+ prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ * the actual dominators if the flow graph is reducible
+ * otherwise, dominators plus some other non-dominators.
+ * See Matthew S. Hecht and Jeffrey D. Ullman,
+ * "Analysis of a Simple Algorithm for Global Data Flow Problems",
+ * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ * Oct. 1-3, 1973, pp. 207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ * such a node is a loop head.
+ * recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+ Reg *r1;
+
+ r->rpo = 1;
+ r1 = r->s1;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ r1 = r->s2;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ rpo2r[n] = r;
+ n++;
+ return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+ long t;
+
+ if(rpo1 == -1)
+ return rpo2;
+ while(rpo1 != rpo2){
+ if(rpo1 > rpo2){
+ t = rpo2;
+ rpo2 = rpo1;
+ rpo1 = t;
+ }
+ while(rpo1 < rpo2){
+ t = idom[rpo2];
+ if(t >= rpo2)
+ fatal(Z, "bad idom");
+ rpo2 = t;
+ }
+ }
+ return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+ while(s > r)
+ s = idom[s];
+ return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+ long src;
+
+ src = r->rpo;
+ if(r->p1 != R && doms(idom, src, r->p1->rpo))
+ return 1;
+ for(r = r->p2; r != R; r = r->p2link)
+ if(doms(idom, src, r->rpo))
+ return 1;
+ return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+ if(r->rpo < head || r->active == head)
+ return;
+ r->active = head;
+ r->loop += LOOP;
+ if(r->p1 != R)
+ loopmark(rpo2r, head, r->p1);
+ for(r = r->p2; r != R; r = r->p2link)
+ loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+ Reg *r1;
+ long i, d, me;
+
+ if(nr > maxnr) {
+ rpo2r = alloc(nr * sizeof(Reg*));
+ idom = alloc(nr * sizeof(long));
+ maxnr = nr;
+ }
+
+ d = postorder(r, rpo2r, 0);
+ if(d > nr)
+ fatal(Z, "too many reg nodes");
+ nr = d;
+ for(i = 0; i < nr / 2; i++){
+ r1 = rpo2r[i];
+ rpo2r[i] = rpo2r[nr - 1 - i];
+ rpo2r[nr - 1 - i] = r1;
+ }
+ for(i = 0; i < nr; i++)
+ rpo2r[i]->rpo = i;
+
+ idom[0] = 0;
+ for(i = 0; i < nr; i++){
+ r1 = rpo2r[i];
+ me = r1->rpo;
+ d = -1;
+ if(r1->p1 != R && r1->p1->rpo < me)
+ d = r1->p1->rpo;
+ for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+ if(r1->rpo < me)
+ d = rpolca(idom, d, r1->rpo);
+ idom[i] = d;
+ }
+
+ for(i = 0; i < nr; i++){
+ r1 = rpo2r[i];
+ r1->loop++;
+ if(r1->p2 != R && loophead(idom, r1))
+ loopmark(rpo2r, i, r1);
+ }
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+ Reg *r1;
+ int z;
+
+ for(r1 = r; r1 != R; r1 = r1->s1) {
+ for(z=0; z<BITS; z++) {
+ dif.b[z] = (dif.b[z] &
+ ~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+ r1->set.b[z] | r1->regdiff.b[z];
+ if(dif.b[z] != r1->regdiff.b[z]) {
+ r1->regdiff.b[z] = dif.b[z];
+ change++;
+ }
+ }
+ if(r1->active)
+ break;
+ r1->active = 1;
+ for(z=0; z<BITS; z++)
+ dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+ if(r1->s2 != R)
+ synch(r1->s2, dif);
+ }
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+ Var *v;
+ int i;
+
+ v = var + r->varno;
+ r->regno = 0;
+ switch(v->etype) {
+
+ default:
+ diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+ break;
+
+ case TCHAR:
+ case TUCHAR:
+ case TSHORT:
+ case TUSHORT:
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TVLONG:
+ case TUVLONG:
+ case TIND:
+ case TARRAY:
+ i = BtoR(~b);
+ if(i && r->cost > 0) {
+ r->regno = i;
+ return RtoB(i);
+ }
+ break;
+
+ case TDOUBLE:
+ case TFLOAT:
+ i = BtoF(~b);
+ if(i && r->cost > 0) {
+ r->regno = i;
+ return FtoB(i);
+ }
+ break;
+ }
+ return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ ulong bb;
+
+ z = bn/32;
+ bb = 1L<<(bn%32);
+ if(r->act.b[z] & bb)
+ return;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(r1->act.b[z] & bb)
+ break;
+ r = r1;
+ }
+
+ if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tld %B $%d\n", r->loop,
+ r->prog, blsh(bn), change);
+ }
+ for(;;) {
+ r->act.b[z] |= bb;
+ p = r->prog;
+
+ if(r->use1.b[z] & bb) {
+ change += CREF * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tu1 %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ change += CREF * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tu2 %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tst %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ paint1(r1, bn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ paint1(r1, bn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(r->act.b[z] & bb)
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+}
+
+ulong
+regset(Reg *r, ulong bb)
+{
+ ulong b, set;
+ Adr v;
+ int c;
+
+ set = 0;
+ v = zprog.from;
+ while(b = bb & ~(bb-1)) {
+ v.type = b & 0xFFFF? BtoR(b): BtoF(b);
+ if(v.type == 0)
+ diag(Z, "zero v.type for %#lux", b);
+ c = copyu(r->prog, &v, A);
+ if(c == 3)
+ set |= b;
+ bb &= ~b;
+ }
+ return set;
+}
+
+ulong
+reguse(Reg *r, ulong bb)
+{
+ ulong b, set;
+ Adr v;
+ int c;
+
+ set = 0;
+ v = zprog.from;
+ while(b = bb & ~(bb-1)) {
+ v.type = b & 0xFFFF? BtoR(b): BtoF(b);
+ c = copyu(r->prog, &v, A);
+ if(c == 1 || c == 2 || c == 4)
+ set |= b;
+ bb &= ~b;
+ }
+ return set;
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+ Reg *r1;
+ int z;
+ ulong bb, vreg, x;
+
+ z = bn/32;
+ bb = 1L << (bn%32);
+ vreg = regbits;
+ if(!(r->act.b[z] & bb))
+ return vreg;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(!(r1->act.b[z] & bb))
+ break;
+ r = r1;
+ }
+ for(;;) {
+ r->act.b[z] &= ~bb;
+
+ vreg |= r->regu;
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ vreg |= paint2(r1, bn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ vreg |= paint2(r1, bn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(!(r->act.b[z] & bb))
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+
+ bb = vreg;
+ for(; r; r=r->s1) {
+ x = r->regu & ~bb;
+ if(x) {
+ vreg |= reguse(r, x);
+ bb |= regset(r, x);
+ }
+ }
+ return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ ulong bb;
+
+ z = bn/32;
+ bb = 1L << (bn%32);
+ if(r->act.b[z] & bb)
+ return;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(r1->act.b[z] & bb)
+ break;
+ r = r1;
+ }
+
+ if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+ addmove(r, bn, rn, 0);
+ for(;;) {
+ r->act.b[z] |= bb;
+ p = r->prog;
+
+ if(r->use1.b[z] & bb) {
+ if(debug['R'])
+ print("%P", p);
+ addreg(&p->from, rn);
+ if(debug['R'])
+ print("\t.c%P\n", p);
+ }
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ if(debug['R'])
+ print("%P", p);
+ addreg(&p->to, rn);
+ if(debug['R'])
+ print("\t.c%P\n", p);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb)
+ addmove(r, bn, rn, 1);
+ r->regu |= rb;
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ paint3(r1, bn, rb, rn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ paint3(r1, bn, rb, rn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(r->act.b[z] & bb)
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+ a->sym = 0;
+ a->offset = 0;
+ a->type = rn;
+}
+
+long
+RtoB(int r)
+{
+
+ if(r < D_AX || r > D_R15)
+ return 0;
+ return 1L << (r-D_AX);
+}
+
+int
+BtoR(long b)
+{
+
+ b &= 0xffffL;
+ if(b == 0)
+ return 0;
+ return bitno(b) + D_AX;
+}
+
+/*
+ * bit reg
+ * 16 X5
+ * 17 X6
+ * 18 X7
+ */
+long
+FtoB(int f)
+{
+ if(f < FREGMIN || f > FREGEXT)
+ return 0;
+ return 1L << (f - FREGMIN + 16);
+}
+
+int
+BtoF(long b)
+{
+
+ b &= 0x70000L;
+ if(b == 0)
+ return 0;
+ return bitno(b) - 16 + FREGMIN;
+}
--- /dev/null
+++ b/utils/6c/sgen.c
@@ -1,0 +1,435 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+ if(n & 1) {
+ gins(ANOP, Z, Z);
+ p->to.type = REGRET;
+ }
+ if(n & 2) {
+ gins(ANOP, Z, Z);
+ p->to.type = FREGRET;
+ }
+}
+
+/* welcome to commute */
+static void
+commute(Node *n)
+{
+ Node *l, *r;
+
+ l = n->left;
+ r = n->right;
+ if(r->complex > l->complex) {
+ n->left = r;
+ n->right = l;
+ }
+}
+
+void
+indexshift(Node *n)
+{
+ int g;
+
+ if(!typechlpv[n->type->etype])
+ return;
+ simplifyshift(n);
+ if(n->op == OASHL && n->right->op == OCONST){
+ g = vconst(n->right);
+ if(g >= 0 && g <= 3)
+ n->addable = 7;
+ }
+}
+
+/*
+ * calculate addressability as follows
+ * NAME ==> 10/11 name+value(SB/SP)
+ * REGISTER ==> 12 register
+ * CONST ==> 20 $value
+ * *(20) ==> 21 value
+ * &(10) ==> 13 $name+value(SB)
+ * &(11) ==> 1 $name+value(SP)
+ * (13) + (20) ==> 13 fold constants
+ * (1) + (20) ==> 1 fold constants
+ * *(13) ==> 10 back to name
+ * *(1) ==> 11 back to name
+ *
+ * (20) * (X) ==> 7 multiplier in indexing
+ * (X,7) + (13,1) ==> 8 adder in indexing (addresses)
+ * (8) ==> &9(OINDEX) index, almost addressable
+ *
+ * calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+ Node *l, *r;
+ int g;
+
+ if(n == Z)
+ return;
+ l = n->left;
+ r = n->right;
+ n->complex = 0;
+ n->addable = 0;
+ switch(n->op) {
+ case OCONST:
+ n->addable = 20;
+ break;
+
+ case ONAME:
+ n->addable = 10;
+ if(n->class == CPARAM || n->class == CAUTO)
+ n->addable = 11;
+ break;
+
+ case OREGISTER:
+ n->addable = 12;
+ break;
+
+ case OINDREG:
+ n->addable = 12;
+ break;
+
+ case OADDR:
+ xcom(l);
+ if(l->addable == 10)
+ n->addable = 13;
+ else
+ if(l->addable == 11)
+ n->addable = 1;
+ break;
+
+ case OADD:
+ xcom(l);
+ xcom(r);
+ if(n->type->etype != TIND)
+ break;
+
+ switch(r->addable) {
+ case 20:
+ switch(l->addable) {
+ case 1:
+ case 13:
+ commadd:
+ l->type = n->type;
+ *n = *l;
+ l = new(0, Z, Z);
+ *l = *(n->left);
+ l->xoffset += r->vconst;
+ n->left = l;
+ r = n->right;
+ goto brk;
+ }
+ break;
+
+ case 1:
+ case 13:
+ case 10:
+ case 11:
+ /* l is the base, r is the index */
+ if(l->addable != 20)
+ n->addable = 8;
+ break;
+ }
+ switch(l->addable) {
+ case 20:
+ switch(r->addable) {
+ case 13:
+ case 1:
+ r = n->left;
+ l = n->right;
+ n->left = l;
+ n->right = r;
+ goto commadd;
+ }
+ break;
+
+ case 13:
+ case 1:
+ case 10:
+ case 11:
+ /* r is the base, l is the index */
+ if(r->addable != 20)
+ n->addable = 8;
+ break;
+ }
+ if(n->addable == 8 && !side(n)) {
+ indx(n);
+ l = new1(OINDEX, idx.basetree, idx.regtree);
+ l->scale = idx.scale;
+ l->addable = 9;
+ l->complex = l->right->complex;
+ l->type = l->left->type;
+ n->op = OADDR;
+ n->left = l;
+ n->right = Z;
+ n->addable = 8;
+ break;
+ }
+ break;
+
+ case OINDEX:
+ xcom(l);
+ xcom(r);
+ n->addable = 9;
+ break;
+
+ case OIND:
+ xcom(l);
+ if(l->op == OADDR) {
+ l = l->left;
+ l->type = n->type;
+ *n = *l;
+ return;
+ }
+ switch(l->addable) {
+ case 20:
+ n->addable = 21;
+ break;
+ case 1:
+ n->addable = 11;
+ break;
+ case 13:
+ n->addable = 10;
+ break;
+ }
+ break;
+
+ case OASHL:
+ xcom(l);
+ xcom(r);
+ indexshift(n);
+ break;
+
+ case OMUL:
+ case OLMUL:
+ xcom(l);
+ xcom(r);
+ g = vlog(l);
+ if(g >= 0) {
+ n->left = r;
+ n->right = l;
+ l = r;
+ r = n->right;
+ }
+ g = vlog(r);
+ if(g >= 0) {
+ n->op = OASHL;
+ r->vconst = g;
+ r->type = types[TINT];
+ indexshift(n);
+ break;
+ }
+ commute(n);
+ break;
+
+ case OASLDIV:
+ xcom(l);
+ xcom(r);
+ g = vlog(r);
+ if(g >= 0) {
+ n->op = OASLSHR;
+ r->vconst = g;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OLDIV:
+ xcom(l);
+ xcom(r);
+ g = vlog(r);
+ if(g >= 0) {
+ n->op = OLSHR;
+ r->vconst = g;
+ r->type = types[TINT];
+ indexshift(n);
+ break;
+ }
+ break;
+
+ case OASLMOD:
+ xcom(l);
+ xcom(r);
+ g = vlog(r);
+ if(g >= 0) {
+ n->op = OASAND;
+ r->vconst--;
+ }
+ break;
+
+ case OLMOD:
+ xcom(l);
+ xcom(r);
+ g = vlog(r);
+ if(g >= 0) {
+ n->op = OAND;
+ r->vconst--;
+ }
+ break;
+
+ case OASMUL:
+ case OASLMUL:
+ xcom(l);
+ xcom(r);
+ g = vlog(r);
+ if(g >= 0) {
+ n->op = OASASHL;
+ r->vconst = g;
+ }
+ break;
+
+ case OLSHR:
+ case OASHR:
+ xcom(l);
+ xcom(r);
+ indexshift(n);
+ break;
+
+ default:
+ if(l != Z)
+ xcom(l);
+ if(r != Z)
+ xcom(r);
+ break;
+ }
+brk:
+ if(n->addable >= 10)
+ return;
+ if(l != Z)
+ n->complex = l->complex;
+ if(r != Z) {
+ if(r->complex == n->complex)
+ n->complex = r->complex+1;
+ else
+ if(r->complex > n->complex)
+ n->complex = r->complex;
+ }
+ if(n->complex == 0)
+ n->complex++;
+
+ switch(n->op) {
+
+ case OFUNC:
+ n->complex = FNX;
+ break;
+
+ case OCAST:
+ if(l->type->etype == TUVLONG && typefd[n->type->etype])
+ n->complex += 2;
+ break;
+
+ case OLMOD:
+ case OMOD:
+ case OLMUL:
+ case OLDIV:
+ case OMUL:
+ case ODIV:
+ case OASLMUL:
+ case OASLDIV:
+ case OASLMOD:
+ case OASMUL:
+ case OASDIV:
+ case OASMOD:
+ if(r->complex >= l->complex) {
+ n->complex = l->complex + 3;
+ if(r->complex > n->complex)
+ n->complex = r->complex;
+ } else {
+ n->complex = r->complex + 3;
+ if(l->complex > n->complex)
+ n->complex = l->complex;
+ }
+ break;
+
+ case OLSHR:
+ case OASHL:
+ case OASHR:
+ case OASLSHR:
+ case OASASHL:
+ case OASASHR:
+ if(r->complex >= l->complex) {
+ n->complex = l->complex + 2;
+ if(r->complex > n->complex)
+ n->complex = r->complex;
+ } else {
+ n->complex = r->complex + 2;
+ if(l->complex > n->complex)
+ n->complex = l->complex;
+ }
+ break;
+
+ case OADD:
+ case OXOR:
+ case OAND:
+ case OOR:
+ /*
+ * immediate operators, make const on right
+ */
+ if(l->op == OCONST) {
+ n->left = r;
+ n->right = l;
+ }
+ break;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ /*
+ * compare operators, make const on left
+ */
+ if(r->op == OCONST) {
+ n->left = r;
+ n->right = l;
+ n->op = invrel[relindex(n->op)];
+ }
+ break;
+ }
+}
+
+void
+indx(Node *n)
+{
+ Node *l, *r;
+
+ if(debug['x'])
+ prtree(n, "indx");
+
+ l = n->left;
+ r = n->right;
+ if(l->addable == 1 || l->addable == 13 || r->complex > l->complex) {
+ n->right = l;
+ n->left = r;
+ l = r;
+ r = n->right;
+ }
+ if(l->addable != 7) {
+ idx.regtree = l;
+ idx.scale = 1;
+ } else
+ if(l->right->addable == 20) {
+ idx.regtree = l->left;
+ idx.scale = 1 << l->right->vconst;
+ } else
+ if(l->left->addable == 20) {
+ idx.regtree = l->right;
+ idx.scale = 1 << l->left->vconst;
+ } else
+ diag(n, "bad index");
+
+ idx.basetree = r;
+ if(debug['x']) {
+ print("scale = %d\n", idx.scale);
+ prtree(idx.regtree, "index");
+ prtree(idx.basetree, "base");
+ }
+}
--- /dev/null
+++ b/utils/6c/swt.c
@@ -1,0 +1,513 @@
+#include "gc.h"
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+ C1 *r;
+ int i;
+ Prog *sp;
+
+ if(nc < 5) {
+ for(i=0; i<nc; i++) {
+ if(debug['K'])
+ print("case = %.8llux\n", q->val);
+ gcmp(OEQ, n, q->val);
+ patch(p, q->label);
+ q++;
+ }
+ gbranch(OGOTO);
+ patch(p, def);
+ return;
+ }
+ i = nc / 2;
+ r = q+i;
+ if(debug['K'])
+ print("case > %.8llux\n", r->val);
+ gcmp(OGT, n, r->val);
+ sp = p;
+ gbranch(OGOTO);
+ p->as = AJEQ;
+ patch(p, r->label);
+ swit1(q, i, def, n);
+
+ if(debug['K'])
+ print("case < %.8llux\n", r->val);
+ patch(sp, pc);
+ swit1(r+1, nc-i-1, def, n);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+ int sh;
+ long v;
+ Node *l;
+
+ /*
+ * n1 gets adjusted/masked value
+ * n2 gets address of cell
+ * n3 gets contents of cell
+ */
+ l = b->left;
+ if(n2 != Z) {
+ regalloc(n1, l, nn);
+ reglcgen(n2, l, Z);
+ regalloc(n3, l, Z);
+ gmove(n2, n3);
+ gmove(n3, n1);
+ } else {
+ regalloc(n1, l, nn);
+ cgen(l, n1);
+ }
+ if(b->type->shift == 0 && typeu[b->type->etype]) {
+ v = ~0 + (1L << b->type->nbits);
+ gopcode(OAND, tfield, nodconst(v), n1);
+ } else {
+ sh = 32 - b->type->shift - b->type->nbits;
+ if(sh > 0)
+ gopcode(OASHL, tfield, nodconst(sh), n1);
+ sh += b->type->shift;
+ if(sh > 0)
+ if(typeu[b->type->etype])
+ gopcode(OLSHR, tfield, nodconst(sh), n1);
+ else
+ gopcode(OASHR, tfield, nodconst(sh), n1);
+ }
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+ long v;
+ Node nod;
+ int sh;
+
+ regalloc(&nod, b->left, Z);
+ v = ~0 + (1L << b->type->nbits);
+ gopcode(OAND, types[TLONG], nodconst(v), n1);
+ gmove(n1, &nod);
+ if(nn != Z)
+ gmove(n1, nn);
+ sh = b->type->shift;
+ if(sh > 0)
+ gopcode(OASHL, types[TLONG], nodconst(sh), &nod);
+ v <<= sh;
+ gopcode(OAND, types[TLONG], nodconst(~v), n3);
+ gopcode(OOR, types[TLONG], n3, &nod);
+ gmove(&nod, n2);
+
+ regfree(&nod);
+ regfree(n1);
+ regfree(n2);
+ regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+ long r;
+
+ if(suppress)
+ return nstring;
+ r = nstring;