wip: make and xclock tests work
This commit is contained in:
parent
f2dd113134
commit
d11768a256
|
@ -8,6 +8,8 @@
|
||||||
*.bak
|
*.bak
|
||||||
/*-test
|
/*-test
|
||||||
/screenshot-*.bmp
|
/screenshot-*.bmp
|
||||||
|
*.rom
|
||||||
|
*.rom.sym
|
||||||
|
|
||||||
*snarf
|
*snarf
|
||||||
*theme
|
*theme
|
||||||
|
|
30
console.txt
30
console.txt
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
47
src/uxn11.c
47
src/uxn11.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
52
src/uxncli.c
52
src/uxncli.c
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 ¶m $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
|
File diff suppressed because it is too large
Load Diff
|
@ -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 ¶m $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
|
Loading…
Reference in New Issue