wip: make and xclock tests work

This commit is contained in:
~d6 2023-12-01 12:19:26 -05:00
parent f2dd113134
commit d11768a256
9 changed files with 1829 additions and 298 deletions

2
.gitignore vendored
View File

@ -8,6 +8,8 @@
*.bak *.bak
/*-test /*-test
/screenshot-*.bmp /screenshot-*.bmp
*.rom
*.rom.sym
*snarf *snarf
*theme *theme

View File

@ -27,21 +27,26 @@ represents part of a response to a host-put (0x1f).
the type (0x17) field explains how to interpret calls to the console the type (0x17) field explains how to interpret calls to the console
vector (0x10) and where data can be read from: vector (0x10) and where data can be read from:
- 0x00 - no input (n/a) - 0x00 - no input (-)
- 0x01 - stdin (stdin) - 0x01 - stdin (stdin)
- 0x02 - argument (stdin) - 0x02 - argument (stdin)
- 0x03 - argument spacer (stdin) - 0x03 - argument spacer (-)
- 0x04 - argument end (stdin) - 0x04 - argument end (-)
- 0x05 - host response (host-get) - 0x05 - host response (host-get)
- 0x06 - host response end (host-get) - 0x06 - host response end (-)
- 0x40 - child 0 exited (host-get contains exit code) - 0x07 - stdin end (-)
- 0x41 - child 1 exited (host-get contains exit code) - 0x20 - child 0 sent data (proc-get)
- 0x42 - child 2 exited (host-get contains exit code) - 0x21 - child 1 sent data (proc-get)
- 0x43 - child 3 exited (host-get contains exit code) - 0x22 - child 2 sent data (proc-get)
- 0x80 - child 0 sent data (proc-get) - 0x23 - child 3 sent data (proc-get)
- 0x81 - child 1 sent data (proc-get) - 0x40 - child 0 data end (proc-get)
- 0x82 - child 2 sent data (proc-get) - 0x41 - child 1 data end (proc-get)
- 0x83 - child 3 sent data (proc-get) - 0x42 - child 2 data end (proc-get)
- 0x43 - child 3 data end (proc-get)
- 0x80 - child 0 exited (host-get contains exit code)
- 0x81 - child 1 exited (host-get contains exit code)
- 0x82 - child 2 exited (host-get contains exit code)
- 0x83 - child 3 exited (host-get contains exit code)
writing a byte to the stdout port (0x18) will send one byte of data to writing a byte to the stdout port (0x18) will send one byte of data to
the emulator's stdout. the emulator's stdout.
@ -74,6 +79,7 @@ using the host-put port (0x1f):
+ 0x01 - child 1 + 0x01 - child 1
+ 0x02 - child 2 + 0x02 - child 2
+ 0x03 - child 3 + 0x03 - child 3
- next 2 bits (0x0c) are unused
- upper 4 bits control which pipes to use with execute: - upper 4 bits control which pipes to use with execute:
+ 0x80 - use child's pty (implies 0x70) + 0x80 - use child's pty (implies 0x70)
+ 0x40 - read from child's stderr + 0x40 - read from child's stderr

View File

@ -32,138 +32,254 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
/* static UxnSubprocess children[CONSOLE_MAX_CHILDREN];
CONSOLE DEVICE static char *fork_args[4] = {"/bin/sh", "-c", "", NULL};
static int to_child_fd[2], from_child_fd[2];
static char buf[16];
0x10 vector* 0x18 write byte* (stdout) static UxnSubprocess*
0x11 (vector) 0x19 write byte* (stderr) find_child_by_pid(pid_t pid)
0x12 read byte (stdin) 0x1a --
0x13 exec args 0x1b --
0x14 (exec args) 0x1c --
0x15 exec mode 0x1d --
0x16 exec action* 0x1e --
0x17 type 0x1f --
(* denotes a register that causes an immediate effect)
when vector (0x10) fires, the type (0x17) field explains what kind of
event this (such as data being available to read on stdin) is and how
to handle it.
the read byte (0x12) contains one byte of data to be read. it usually
occurs as the result of data being available on stdin, but may have
other meanings (such as an exit code from a finished process).
exec args (0x13) is usually an address pointing to a string, which is
acted on by exec action (0x16). occasionally it may be used as an 8-bit
or 16-bit number instead (such as with exec actions 0x04-0x05).
the meaning of exec args depends on which action it is used with:
- 0x00 - unused
- 0x01 - address of a command string
- 0x02 - address of an environment variable name
- 0x03 - address of an environment variable name+value (e.g. "FOO=123")
- 0x04 - unsigned 16-bit integer
- 0x05 - unsigned 16-bit integer
- (all strings must be null-terminated)
exec mode (0x015) provides context for exec actions (0x16):
- lower 2 bits control which child process to use:
+ 0x00 - child 0
+ 0x01 - child 1
+ 0x02 - child 2
+ 0x03 - child 3
- (next 2 bits unused)
- upper 4 bits control which pipes to use with execute:
+ 0x80 - use child's pty (implies 0x70)
+ 0x40 - read from child's stderr
+ 0x20 - read from child's stdin
+ 0x10 - write to child's stdin
exec action (0x16) acts on the exec args address (0x13):
- 0x00 - nop, does nothing
- 0x01 - execute, starts a subprocess
- 0x02 - getenv, reads string (NAME) from exec args, overwrites value
- 0x03 - setenv, reads string (NAME=value) from exec, writes into env
- 0x04 - set-pty-height, reads u16 from exec args, updates winsize
- 0x05 - set-pty-width, reads u16 from exec args, updates winsize
reading type (0x17) during vector:
- 0x00 - no input
- 0x01 - stdin
- 0x02 - argument
- 0x03 - argument spacer
- 0x04 - argument end
- 0x05 - action output
- 0x06 - action output end
- 0x40 - child 0 exited
- 0x41 - child 1 exited
- 0x42 - child 2 exited
- 0x43 - child 3 exited
- 0x81 - child 1
- 0x82 - child 2
- 0x83 - child 3
- (child 0 uses 0x01, i.e. "stdin")
for most types, the read byte(0x12) contains one byte of input data.
however, for exit notifications (0x40-0x43) it will intead contain an
unsigned 8-bit exit code.
*/
/* process */
static char *fork_args[32];
static int child_mode;
static int pty_fd;
static int to_child_fd[2];
static int from_child_fd[2];
static int saved_in;
static int saved_out;
static pid_t child_pid;
struct winsize ws = {24, 80, 8, 12};
/* new */
static pid_t child_pids[4];
static int child_modes[4];
static int child_fd_ins[4] = {0, -1, -1, -1};
static int child_fd_outs[4] = {1, -1, -1, -1};
static int child_ptys[4] = {-1, -1, -1, -1};
static void
parse_args(Uxn *u, Uint8 *d)
{ {
Uint8 *port_addr = d + 0x3; for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) {
int addr = PEEK2(port_addr); //fprintf(stderr, "child %d has pid %d [%d] (looking for %d)\n", n, children[n].pid, children[n].running, pid);
char *pos = (char *)&u->ram[addr]; if(children[n].pid == pid)
int i = 0; return &children[n];
do { }
fork_args[i++] = pos; return NULL;
while(*pos != 0) pos++;
pos++;
} while(*pos != '\0');
fork_args[i] = NULL;
} }
/* call after we're sure the process has exited */ #define child_ptys(mode) (mode & CONSOLE_MODE_PTY)
#define child_reads(mode) (mode & CONSOLE_CHILD_READS)
#define child_writes(mode) (mode & CONSOLE_CHILD_WRITES)
#define child_writes_out(mode) (mode & CONSOLE_MODE_OUT)
#define child_writes_err(mode) (mode & CONSOLE_MODE_ERR)
static void static void
clean_after_child(int child) handle_sigchld(int sig)
{ {
int n = child & 0x3; pid_t pid;
int is_pty = child_modes[n] >= 0x80; int n, wstatus;
if(!child_pids[n]) return; while((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
if(is_pty) close(child_ptys[n]); //fprintf(stderr, "waitpid got something: %d\n", pid);
if(is_pty || child_mode & 0x1) close(child_fd_outs[n]); UxnSubprocess *child = find_child_by_pid(pid);
if(is_pty || child_mode & 0x6) close(child_fd_ins[n]); if (child != NULL) {
if(n == 0) { fprintf(stderr, "handle_sigchld: child %d (%d)\n", child->id, pid);
if (is_pty || (child_modes[n] & 0x1)) dup2(saved_out, 1); child->running = WEXITSTATUS(wstatus) - 256;
if (is_pty || (child_modes[n] & 0x6)) dup2(saved_in, 0); if(child_ptys(child->mode))
saved_in = -1; close(child->pty);
saved_out = -1; else {
if(child_reads(child->mode)) close(child->fd_in);
if(child_writes(child->mode)) close(child->fd_out);
}
child->pid = child->mode = 0;
child->fd_out = child->fd_in = child->pty = -1;
return;
}
}
fprintf(stderr, "handle_sigchld: no child found", sig);
}
static void
start_fork_pty(UxnSubprocess *child, int mode)
{
int fd = -1;
pid_t pid = forkpty(&fd, NULL, NULL, NULL);
if(pid < 0) { /* failure */
fprintf(stderr, "parent fork failure\n");
} else if(pid == 0) { /* child */
execvp(fork_args[0], fork_args);
fprintf(stderr, "child exec failure\n");
} else { /*parent*/
do {
child->pid = pid;
child->running = 1;
child->mode = mode;
child->pty = fd;
child->fd_in = fd;
child->fd_out = fd;
} while (child->pid != pid);
}
}
static void
start_fork_pipe(UxnSubprocess *child, int mode)
{
if(child_reads(mode)) {
/* parent writes to child's stdin */
if(pipe(to_child_fd) == -1) {
fprintf(stderr, "pipe error: to child\n");
return;
}
}
if(child_writes(mode)) {
/* parent reads from child's stdout and/or stderr */
if(pipe(from_child_fd) == -1) {
fprintf(stderr, "pipe error: from child\n");
return;
}
}
pid_t pid = fork();
if(pid < 0) { /* failure */
fprintf(stderr, "fork failure\n");
} else if(pid == 0) { /* child */
if(child_reads(mode)) {
dup2(to_child_fd[0], 0);
close(to_child_fd[1]);
}
if(child_writes(mode)) {
if(child_writes_out(mode)) dup2(from_child_fd[1], 1);
if(child_writes_err(mode)) dup2(from_child_fd[1], 2);
close(from_child_fd[0]);
}
execvp(fork_args[0], fork_args);
fprintf(stderr, "child exec failure\n");
exit(1);
} else { /*parent*/
do {
child->pid = pid;
child->mode = mode;
child->running = 1;
child->fd_in = child_reads(mode) ? to_child_fd[1] : -1;
child->fd_out = child_writes(mode) ? from_child_fd[0] : -1;
child->pty = -1;
} while (child->pid != pid);
fprintf(stderr, "child has pid %d\n", child->pid);
if(child_reads(mode)) close(to_child_fd[0]);
if(child_writes(mode)) close(from_child_fd[1]);
}
}
static void
std_put(Uint8 c, FILE *fd)
{
fputc(c, fd);
fflush(fd);
}
static void
proc_put(Uxn *u, Uint8 *d)
{
Uint8 c = d[0x2];
int n = d[0xe] & 0x3;
write(children[n].fd_in, &c, 1);
}
static void
host_execute(Uxn *u, Uint8 *d)
{
int addr = PEEK2(d + 0xc);
int opts = d[0xe];
int mode = opts & 0xf0;
char *cmd = (char *)&u->ram[addr];
UxnSubprocess *child = &children[opts & 0x3];
fork_args[2] = cmd;
fprintf(stderr, "running execute: %s (#%02x)\n", cmd, opts);
if(child->pid) {
fprintf(stderr, " killing previous child: %d\n", child->pid);
kill(child->pid, 9);
}
if(opts >= 0x80)
start_fork_pty(child, mode);
else
start_fork_pipe(child, mode);
}
static void
host_response(Uxn *u, char *s)
{
for(int i = 0;; i++)
console_input(u, s[i], CONSOLE_HOST);
console_input(u, '\0', CONSOLE_HOST_END);
}
static void
host_getpid(Uxn *u, Uint8 *d)
{
int n = d[0xe] & 0x3;
snprintf(buf, 16, "%d", children[n].pid);
host_response(u, buf);
}
static void
host_kill(Uxn *u, Uint8 *d)
{
int n = d[0xe] & 0x3;
kill(children[n].pid, 15);
}
static void
host_getenv(Uxn *u, Uint8 *d)
{
int addr = PEEK2(d + 0x3);
char *name = (char *)&u->ram[addr];
host_response(u, getenv(name));
}
static void
host_setenv(Uxn *u, Uint8 *d)
{
int addr = PEEK2(d + 0x3);
char *name = (char *)&u->ram[addr];
int i = 0;
while (name[i] != '=') i++;
name[i] = '\0';
char *value = &name[i+1];
setenv(name, value, 1);
}
static void
host_put(Uxn *u, Uint8 *d)
{
switch(d[0xf]) {
case 0x0: /* nop */ break;
case 0x1: host_execute(u, d); break;
case 0x2: host_getpid(u, d); break;
case 0x3: host_kill(u, d); break;
case 0x10: host_getenv(u, d); break;
case 0x11: host_setenv(u, d); break;
}
}
void
init_console(void)
{
/* set up signal handler for SIGCHLD */
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = handle_sigchld;
sigaction(SIGCHLD, &sa, NULL);
/* initialize (halted) children */
for(int i = 0; i < CONSOLE_MAX_CHILDREN; i++) {
UxnSubprocess *child = &children[i];
child->id = i;
child->pid = child->mode = child->running = 0;
child->fd_in = child->fd_out = child->pty = -1;
}
}
UxnSubprocess
*get_child(int n)
{
return &children[n & 0x3];
}
void
console_monitor(Uxn *u)
{
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) {
UxnSubprocess *child = &children[n];
if(child->running < 0) {
Uint8 status = child->running + 256;
child->running = 0;
console_input(u, status, CONSOLE_CHILD_EXIT | n);
}
} }
child_pids[n] = 0;
child_modes[n] = 0;
child_ptys[n] = -1;
child_fd_ins[n] = -1;
child_fd_outs[n] = -1;
} }
int int
@ -181,161 +297,28 @@ console_listen(Uxn *u, int i, int argc, char **argv)
for(; i < argc; i++) { for(; i < argc; i++) {
char *p = argv[i]; char *p = argv[i];
while(*p) console_input(u, *p++, CONSOLE_ARG); while(*p) console_input(u, *p++, CONSOLE_ARG);
console_input(u, '\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA); console_input(u, '\n', i == argc - 1 ? CONSOLE_ARG_END : CONSOLE_ARG_SPACER);
} }
} }
static void
start_fork_pty(Uint8 *d)
{
int fd = -1;
pid_t pid = forkpty(&fd, NULL, NULL, NULL);
int n = d[0xa] & 0x3;
if(pid < 0) { /* failure */
d[0x6] = 0xff;
fprintf(stderr, "fork failure\n");
} else if(pid == 0) { /* child */
setenv("TERM", "ansi", 1); /* TODO: configure these */
execvp(fork_args[0], fork_args);
d[0x6] = 0xff;
fprintf(stderr, "exec failure\n");
} else { /*parent*/
child_pids[n] = pid;
child_ptys[n] = fd;
ioctl(fd, TIOCSWINSZ, &ws);
if(n == 0) {
saved_in = dup(0);
saved_out = dup(1);
dup2(fd, 0);
dup2(fd, 1);
} else {
child_fd_ins[n] = fd;
child_fd_outs[n] = fd;
}
}
}
static void
start_fork_pipe(Uint8 *d)
{
int n = d[0xa] & 0x3;
if(child_modes[n] & 0x01) {
/* parent writes to child's stdin */
if(pipe(to_child_fd) == -1) {
d[0x6] = 0xff;
fprintf(stderr, "pipe error: to child\n");
return;
}
}
if(child_modes[n] & 0x06) {
/* parent reads from child's stdout and/or stderr */
if(pipe(from_child_fd) == -1) {
d[0x6] = 0xff;
fprintf(stderr, "pipe error: from child\n");
return;
}
}
pid_t pid = fork();
if(pid < 0) { /* failure */
d[0x6] = 0xff;
fprintf(stderr, "fork failure\n");
} else if(pid == 0) { /* child */
if(child_modes[n] & 0x01) {
dup2(to_child_fd[0], 0);
close(to_child_fd[1]);
}
if(child_modes[n] & 0x06) {
if(child_modes[n] & 0x02) dup2(from_child_fd[1], 1);
if(child_modes[n] & 0x04) dup2(from_child_fd[1], 2);
close(from_child_fd[0]);
}
execvp(fork_args[0], fork_args);
d[0x6] = 0xff;
fprintf(stderr, "exec failure\n");
} else { /*parent*/
child_pids[n] = pid;
if(child_modes[n] & 0x01) {
child_fd_ins[n] = to_child_fd[1];
if(n == 0) {
saved_out = dup(1);
dup2(to_child_fd[1], 1);
}
close(to_child_fd[0]);
}
if(child_modes[n] & 0x06) {
child_fd_outs[n] = from_child_fd[0];
if(n == 0) {
saved_in = dup(0);
dup2(from_child_fd[0], 0);
}
close(from_child_fd[1]);
}
}
}
static void
kill_child(Uint8 *d, int options)
{
int n = d[0xa] & 0x3;
if(child_pids[n]) {
kill(child_pids[n], 9);
int wstatus;
if(waitpid(child_pids[n], &wstatus, options)) {
d[0x6] = 1;
d[0x7] = WEXITSTATUS(wstatus);
clean_after_child(n);
}
}
}
static void
start_fork(Uxn *u, Uint8 *d)
{
int n = d[0xa] & 0x3;
fflush(stderr);
kill_child(d, 0);
child_modes[n] = d[0x5];
parse_args(u, d);
if(child_modes[n] >= 0x80)
start_fork_pty(d);
else
start_fork_pipe(d);
}
Uint8 Uint8
console_dei(Uxn *u, Uint8 addr) console_dei(Uxn *u, Uint8 addr)
{ {
/* for(int n = 0; n < 3; n++) */
/* fprintf(stderr, "child %d has pid %d (%d)\n", n, children[n].pid, children[n].running); */
Uint8 port = addr & 0x0f, *d = &u->dev[addr & 0xf0]; Uint8 port = addr & 0x0f, *d = &u->dev[addr & 0xf0];
switch(port) {
case 0x6:
case 0x7: kill_child(d, WNOHANG);
}
return d[port]; return d[port];
} }
void void
console_deo(Uxn *u, Uint8 *d, Uint8 port) console_deo(Uxn *u, Uint8 *d, Uint8 port)
{ {
FILE *fd = NULL; /* for(int n = 0; n < 3; n++) */
/* fprintf(stderr, "child %d has pid %d (%d)\n", n, children[n].pid, children[n].running); */
switch(port) { switch(port) {
case 0x5: /* Console/dead */ start_fork(u, d); break; case 0x8: std_put(d[port], stdout); break;
case 0x6: /* Console/exit*/ kill_child(d, 0); break; case 0x9: std_put(d[port], stderr); break;
case 0x8: fd = stdout; break; case 0xa: proc_put(u, d); break;
case 0x9: fd = stderr; break; case 0xf: host_put(u, d); break;
}
if(fd) {
fputc(d[port], fd);
fflush(fd);
}
}
void
update_pollfd(struct pollfd *fds)
{
for(int i = 0; i < 16; i++) {
fds[i + 2].fd = child_fd_outs[i];
fds[i + 2].events = POLLIN;
} }
} }

View File

@ -11,13 +11,44 @@ WITH REGARD TO THIS SOFTWARE.
#define CONSOLE_VERSION 1 #define CONSOLE_VERSION 1
#define CONSOLE_STD 0x1 #define CONSOLE_NONE 0x00
#define CONSOLE_ARG 0x2 #define CONSOLE_STDIN 0x01
#define CONSOLE_EOA 0x3 #define CONSOLE_ARG 0x02
#define CONSOLE_END 0x4 #define CONSOLE_ARG_SPACER 0x03
#define CONSOLE_ARG_END 0x04
#define CONSOLE_HOST 0x05
#define CONSOLE_HOST_END 0x06
#define CONSOLE_STDIN_END 0x07
#define CONSOLE_CHILD_DATA 0x20 /* write from child-n: 0x20 | n */
#define CONSOLE_CHILD_END 0x40 /* close from child-n: 0x40 | n */
#define CONSOLE_CHILD_EXIT 0x80 /* exits from child-n: 0x80 | n */
typedef struct UxnSubprocess {
int id; /* identifier: 0-3 */
pid_t pid; /* when running, pid > 0 */
int mode; /* bit flags: CONSOLE_MODE_ */
int running; /* 0=stopped, >0 running, <0 exiting */
int fd_in; /* fd to send to child; -1 is unset */
int fd_out; /* fd to read from child; -1 is unset */
int pty; /* pty for child; -1 is unset */
} UxnSubprocess;
void init_console(void);
UxnSubprocess *get_child(int n);
void console_monitor(Uxn *u);
int console_input(Uxn *u, char c, int type); int console_input(Uxn *u, char c, int type);
void console_listen(Uxn *u, int i, int argc, char **argv); void console_listen(Uxn *u, int i, int argc, char **argv);
Uint8 console_dei(Uxn *u, Uint8 addr); Uint8 console_dei(Uxn *u, Uint8 addr);
void console_deo(Uxn *u, Uint8 *d, Uint8 port); void console_deo(Uxn *u, Uint8 *d, Uint8 port);
void update_pollfd(struct pollfd *fds);
#define CONSOLE_MODE_PTY 0x80
#define CONSOLE_MODE_ERR 0x40
#define CONSOLE_MODE_OUT 0x20
#define CONSOLE_MODE_INP 0x10
#define CONSOLE_CHILD_READS 0x90 /* CONSOLE_MODE_PTY | CONSOLE_MODE_INP */
#define CONSOLE_CHILD_WRITES 0xe0 /* CONSOLE_MODE_PTY | CONSOLE_MODE_OUT | CONSOLE_MODE_ERR */
#define CONSOLE_MAX_CHILDREN 4

View File

@ -1,3 +1,4 @@
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
@ -118,6 +119,7 @@ emu_end(Uxn *u)
XDestroyImage(ximage); XDestroyImage(ximage);
XDestroyWindow(display, window); XDestroyWindow(display, window);
XCloseDisplay(display); XCloseDisplay(display);
kill(0, 15);
exit(0); exit(0);
return u->dev[0x0f] & 0x7f; return u->dev[0x0f] & 0x7f;
} }
@ -146,6 +148,23 @@ toggle_scale(Uxn *u)
screen_resize(uxn_screen.width, uxn_screen.height, s); screen_resize(uxn_screen.width, uxn_screen.height, s);
} }
/* returns true if the fd ended (has been closed), false otherwise */
static int
handle_input(Uxn *u, struct pollfd pollfd, char *coninp, int argdata, int argend)
{
if((pollfd.revents & POLLIN) != 0) {
int n = read(pollfd.fd, coninp, CONINBUFSIZE - 1);
coninp[n] = 0;
if(n == 0)
console_input(u, 0, argend);
else
for(int i = 0; i < n; i++)
console_input(u, coninp[i], argdata);
return n == 0;
} else
return 0;
}
static void static void
emu_event(Uxn *u) emu_event(Uxn *u)
{ {
@ -211,16 +230,18 @@ emu_init(void)
if(!display) if(!display)
return system_error("X11", "Could not open display"); return system_error("X11", "Could not open display");
screen_resize(WIDTH, HEIGHT, 1); screen_resize(WIDTH, HEIGHT, 1);
init_console();
return 1; return 1;
} }
static int static int
emu_run(Uxn *u, char *rom) emu_run(Uxn *u, char *rom)
{ {
int i = 1, n, s = uxn_screen.scale; int i = 1, n, end, s = uxn_screen.scale, t;
int *child_fd_outs;
char expirations[8]; char expirations[8];
char coninp[CONINBUFSIZE]; char coninp[CONINBUFSIZE];
struct pollfd fds[18]; struct pollfd fds[3 + CONSOLE_MAX_CHILDREN];
static const struct itimerspec screen_tspec = {{0, 16666666}, {0, 16666666}}; static const struct itimerspec screen_tspec = {{0, 16666666}, {0, 16666666}};
loaded_rom = rom; loaded_rom = rom;
@ -245,7 +266,10 @@ emu_run(Uxn *u, char *rom)
timerfd_settime(fds[1].fd, 0, &screen_tspec, NULL); timerfd_settime(fds[1].fd, 0, &screen_tspec, NULL);
fds[2].fd = STDIN_FILENO; fds[2].fd = STDIN_FILENO;
fds[0].events = fds[1].events = fds[2].events = POLLIN; fds[0].events = fds[1].events = fds[2].events = POLLIN;
update_pollfd(fds); for(i = 0; i < 4; i++) {
fds[i + 3].fd = get_child(i)->fd_out;
fds[i + 3].events = POLLIN;
}
/* main loop */ /* main loop */
while(!u->dev[0x0f]) { while(!u->dev[0x0f]) {
@ -263,13 +287,18 @@ emu_run(Uxn *u, char *rom)
XPutImage(display, window, DefaultGC(display, 0), ximage, x1, y1, x1 + PAD, y1 + PAD, x2 - x1, y2 - y1); XPutImage(display, window, DefaultGC(display, 0), ximage, x1, y1, x1 + PAD, y1 + PAD, x2 - x1, y2 - y1);
} }
} }
if((fds[2].revents & POLLIN) != 0) {
n = read(fds[2].fd, coninp, CONINBUFSIZE - 1); /* read input from stdin */
coninp[n] = 0; handle_input(u, fds[2], coninp, CONSOLE_STDIN, CONSOLE_STDIN_END);
for(i = 0; i < n; i++)
console_input(u, coninp[i], CONSOLE_STD); /* read input from child processes */
} for(i = 0; i < 4; i++)
handle_input(u, fds[i + 3], coninp, CONSOLE_CHILD_DATA | i, CONSOLE_CHILD_END | i);
/* check to see if any children exited */
console_monitor(u);
} }
kill(0, 15);
return 1; return 1;
} }

View File

@ -1,5 +1,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include "uxn.h" #include "uxn.h"
#include "devices/system.h" #include "devices/system.h"
@ -42,16 +45,55 @@ emu_deo(Uxn *u, Uint8 addr, Uint8 value)
} }
} }
static void
handle_input(Uxn *u, int fd, int argdata, int argend)
{
char buf[32];
int n = read(fd, &buf, 32);
if(n == 0)
console_input(u, 0x00, argend);
else
for(int i = 0; i < n; i++)
console_input(u, buf[i], argdata);
}
static int
init_rfds(fd_set *rfds)
{
int i, nfds;
FD_ZERO(rfds);
FD_SET(0, rfds);
nfds = 1;
/* for(i = 0; i < 4; i++) { */
/* int fd = get_child(i)->fd_out; */
/* FD_SET(fd, rfds); */
/* if (fd >= nfds) nfds = fd + 1; */
/* } */
return nfds;
}
static void static void
emu_run(Uxn *u) emu_run(Uxn *u)
{ {
int i;
struct timeval tv;
fd_set rfds;
init_console();
while(!u->dev[0x0f]) { while(!u->dev[0x0f]) {
int c = fgetc(stdin); int nfds = init_rfds(&rfds);
if(c == EOF) { tv.tv_sec = 0;
console_input(u, 0x00, CONSOLE_END); tv.tv_usec = 1000;
break; int retval = select(nfds, &rfds, NULL, NULL, &tv);
if(retval <= 0)
continue;
if (FD_ISSET(0, &rfds))
handle_input(u, 0, CONSOLE_STDIN, CONSOLE_STDIN_END);
for(i = 0; i < 4; i++) {
int fd = get_child(i)->fd_out;
if (fd > 2 && FD_ISSET(fd, &rfds))
handle_input(u, fd, CONSOLE_CHILD_DATA | i, CONSOLE_CHILD_END | i);
} }
console_input(u, (Uint8)c, CONSOLE_STD); console_monitor(u);
} }
} }

38
test/make.tal Normal file
View File

@ -0,0 +1,38 @@
|10 @Console [
&vector $2 &stdin $1 &pad1 $1 &proc-get $1 &host-get $1 &pad2 $1 &type $1
&stdout $1 &stderr $1 &proc-put $1 &pad3 $1 &param $2 &opts $1 &host-put $1
]
|0100 ( -- BRK )
;on-console .Console/vector DEO2 ( ; set up console vector callback )
BRK ( ; do other initialization )
@on-console ( -- BRK )
LIT "? .Console/stdout DEO
.Console/type DEI #81 EQU ?on-exit ( ; 0x41 signals child 1's exit )
.Console/type DEI #01 EQU ?on-stdin ( ; handle stdin )
BRK ( ; handle other console input )
@on-stdin
.Console/stdin DEI #0a NEQ
?{ run-make } BRK
@on-exit ( -- BRK )
.Console/host-get DEI ( ; read child 1's exit code )
?{ display-success-msg BRK } ( ; zero exit code means success )
display-failure-msg BRK ( ; non-zero exit code means failure )
@run-make ( -- )
;make-cmd .Console/param DEO2 ( ; set up make to run )
#01 .Console/opts DEO ( ; use child without pipelines )
#01 .Console/host-put DEO JMP2r ( ; run the command now and return )
@make-cmd "pwd 00 ( ; buffer containing cmd to run )
@display-success-msg
LIT ". .Console/stdout DEO
#0a .Console/stdout DEO JMP2r
@display-failure-msg
LIT "! .Console/stdout DEO
#0a .Console/stdout DEO JMP2r

1375
test/term.tal Normal file

File diff suppressed because it is too large Load Diff

25
test/xclock.tal Normal file
View File

@ -0,0 +1,25 @@
( xclock.tal )
( )
( runs 4 xclock processes. )
( )
( when one is killed it immediately restarts. )
( when this program exits all xclocks also exit. )
|10 @Console [
&vect $2 &stdin $1 &pad1 $1 &proc-get $1 &host-get $1 &pad2 $1 &type $1
&stdout $1 &stderr $1 &proc-put $1 &pad3 $1 &param $2 &opts $1 &host-put $1
]
|0100
#00 &loop DUP run-xclock INC DUP #04 LTH ?&loop POP
;on-console .Console/vect DEO2 BRK
@on-console ( -> BRK )
.Console/type DEI DUP #80 AND ?{ POP BRK } #0f AND run-xclock BRK
@run-xclock ( child^ -> )
.Console/opts DEO
;xclock .Console/param DEO2
#01 .Console/host-put DEO JMP2r
@xclock "xclock 00