wip: make and xclock tests work
This commit is contained in:
parent
f2dd113134
commit
d11768a256
|
@ -8,6 +8,8 @@
|
|||
*.bak
|
||||
/*-test
|
||||
/screenshot-*.bmp
|
||||
*.rom
|
||||
*.rom.sym
|
||||
|
||||
*snarf
|
||||
*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
|
||||
vector (0x10) and where data can be read from:
|
||||
- 0x00 - no input (n/a)
|
||||
- 0x00 - no input (-)
|
||||
- 0x01 - stdin (stdin)
|
||||
- 0x02 - argument (stdin)
|
||||
- 0x03 - argument spacer (stdin)
|
||||
- 0x04 - argument end (stdin)
|
||||
- 0x03 - argument spacer (-)
|
||||
- 0x04 - argument end (-)
|
||||
- 0x05 - host response (host-get)
|
||||
- 0x06 - host response end (host-get)
|
||||
- 0x40 - child 0 exited (host-get contains exit code)
|
||||
- 0x41 - child 1 exited (host-get contains exit code)
|
||||
- 0x42 - child 2 exited (host-get contains exit code)
|
||||
- 0x43 - child 3 exited (host-get contains exit code)
|
||||
- 0x80 - child 0 sent data (proc-get)
|
||||
- 0x81 - child 1 sent data (proc-get)
|
||||
- 0x82 - child 2 sent data (proc-get)
|
||||
- 0x83 - child 3 sent data (proc-get)
|
||||
- 0x06 - host response end (-)
|
||||
- 0x07 - stdin end (-)
|
||||
- 0x20 - child 0 sent data (proc-get)
|
||||
- 0x21 - child 1 sent data (proc-get)
|
||||
- 0x22 - child 2 sent data (proc-get)
|
||||
- 0x23 - child 3 sent data (proc-get)
|
||||
- 0x40 - child 0 data end (proc-get)
|
||||
- 0x41 - child 1 data end (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
|
||||
the emulator's stdout.
|
||||
|
@ -74,6 +79,7 @@ using the host-put port (0x1f):
|
|||
+ 0x01 - child 1
|
||||
+ 0x02 - child 2
|
||||
+ 0x03 - child 3
|
||||
- next 2 bits (0x0c) are unused
|
||||
- upper 4 bits control which pipes to use with execute:
|
||||
+ 0x80 - use child's pty (implies 0x70)
|
||||
+ 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
CONSOLE DEVICE
|
||||
static UxnSubprocess children[CONSOLE_MAX_CHILDREN];
|
||||
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)
|
||||
0x11 (vector) 0x19 write byte* (stderr)
|
||||
0x12 read byte (stdin) 0x1a --
|
||||
0x13 exec args 0x1b --
|
||||
0x14 (exec args) 0x1c --
|
||||
0x15 exec mode 0x1d --
|
||||
0x16 exec action* 0x1e --
|
||||
0x17 type 0x1f --
|
||||
static UxnSubprocess*
|
||||
find_child_by_pid(pid_t pid)
|
||||
{
|
||||
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) {
|
||||
//fprintf(stderr, "child %d has pid %d [%d] (looking for %d)\n", n, children[n].pid, children[n].running, pid);
|
||||
if(children[n].pid == pid)
|
||||
return &children[n];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(* 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};
|
||||
#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
|
||||
parse_args(Uxn *u, Uint8 *d)
|
||||
handle_sigchld(int sig)
|
||||
{
|
||||
Uint8 *port_addr = d + 0x3;
|
||||
int addr = PEEK2(port_addr);
|
||||
char *pos = (char *)&u->ram[addr];
|
||||
int i = 0;
|
||||
pid_t pid;
|
||||
int n, wstatus;
|
||||
while((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
|
||||
//fprintf(stderr, "waitpid got something: %d\n", pid);
|
||||
UxnSubprocess *child = find_child_by_pid(pid);
|
||||
if (child != NULL) {
|
||||
fprintf(stderr, "handle_sigchld: child %d (%d)\n", child->id, pid);
|
||||
child->running = WEXITSTATUS(wstatus) - 256;
|
||||
if(child_ptys(child->mode))
|
||||
close(child->pty);
|
||||
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 {
|
||||
fork_args[i++] = pos;
|
||||
while(*pos != 0) pos++;
|
||||
pos++;
|
||||
} while(*pos != '\0');
|
||||
fork_args[i] = NULL;
|
||||
child->pid = pid;
|
||||
child->running = 1;
|
||||
child->mode = mode;
|
||||
child->pty = fd;
|
||||
child->fd_in = fd;
|
||||
child->fd_out = fd;
|
||||
} while (child->pid != pid);
|
||||
}
|
||||
}
|
||||
|
||||
/* call after we're sure the process has exited */
|
||||
static void
|
||||
clean_after_child(int child)
|
||||
start_fork_pipe(UxnSubprocess *child, int mode)
|
||||
{
|
||||
int n = child & 0x3;
|
||||
int is_pty = child_modes[n] >= 0x80;
|
||||
if(!child_pids[n]) return;
|
||||
if(is_pty) close(child_ptys[n]);
|
||||
if(is_pty || child_mode & 0x1) close(child_fd_outs[n]);
|
||||
if(is_pty || child_mode & 0x6) close(child_fd_ins[n]);
|
||||
if(n == 0) {
|
||||
if (is_pty || (child_modes[n] & 0x1)) dup2(saved_out, 1);
|
||||
if (is_pty || (child_modes[n] & 0x6)) dup2(saved_in, 0);
|
||||
saved_in = -1;
|
||||
saved_out = -1;
|
||||
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
|
||||
|
@ -181,161 +297,28 @@ console_listen(Uxn *u, int i, int argc, char **argv)
|
|||
for(; i < argc; i++) {
|
||||
char *p = argv[i];
|
||||
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
|
||||
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];
|
||||
switch(port) {
|
||||
case 0x6:
|
||||
case 0x7: kill_child(d, WNOHANG);
|
||||
}
|
||||
return d[port];
|
||||
}
|
||||
|
||||
void
|
||||
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) {
|
||||
case 0x5: /* Console/dead */ start_fork(u, d); break;
|
||||
case 0x6: /* Console/exit*/ kill_child(d, 0); break;
|
||||
case 0x8: fd = stdout; break;
|
||||
case 0x9: fd = stderr; 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;
|
||||
case 0x8: std_put(d[port], stdout); break;
|
||||
case 0x9: std_put(d[port], stderr); break;
|
||||
case 0xa: proc_put(u, d); break;
|
||||
case 0xf: host_put(u, d); break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,44 @@ WITH REGARD TO THIS SOFTWARE.
|
|||
|
||||
#define CONSOLE_VERSION 1
|
||||
|
||||
#define CONSOLE_STD 0x1
|
||||
#define CONSOLE_ARG 0x2
|
||||
#define CONSOLE_EOA 0x3
|
||||
#define CONSOLE_END 0x4
|
||||
#define CONSOLE_NONE 0x00
|
||||
#define CONSOLE_STDIN 0x01
|
||||
#define CONSOLE_ARG 0x02
|
||||
#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);
|
||||
void console_listen(Uxn *u, int i, int argc, char **argv);
|
||||
Uint8 console_dei(Uxn *u, Uint8 addr);
|
||||
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 <stdlib.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
@ -118,6 +119,7 @@ emu_end(Uxn *u)
|
|||
XDestroyImage(ximage);
|
||||
XDestroyWindow(display, window);
|
||||
XCloseDisplay(display);
|
||||
kill(0, 15);
|
||||
exit(0);
|
||||
return u->dev[0x0f] & 0x7f;
|
||||
}
|
||||
|
@ -146,6 +148,23 @@ toggle_scale(Uxn *u)
|
|||
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
|
||||
emu_event(Uxn *u)
|
||||
{
|
||||
|
@ -211,16 +230,18 @@ emu_init(void)
|
|||
if(!display)
|
||||
return system_error("X11", "Could not open display");
|
||||
screen_resize(WIDTH, HEIGHT, 1);
|
||||
init_console();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
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 coninp[CONINBUFSIZE];
|
||||
struct pollfd fds[18];
|
||||
struct pollfd fds[3 + CONSOLE_MAX_CHILDREN];
|
||||
static const struct itimerspec screen_tspec = {{0, 16666666}, {0, 16666666}};
|
||||
loaded_rom = rom;
|
||||
|
||||
|
@ -245,7 +266,10 @@ emu_run(Uxn *u, char *rom)
|
|||
timerfd_settime(fds[1].fd, 0, &screen_tspec, NULL);
|
||||
fds[2].fd = STDIN_FILENO;
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
}
|
||||
if((fds[2].revents & POLLIN) != 0) {
|
||||
n = read(fds[2].fd, coninp, CONINBUFSIZE - 1);
|
||||
coninp[n] = 0;
|
||||
for(i = 0; i < n; i++)
|
||||
console_input(u, coninp[i], CONSOLE_STD);
|
||||
}
|
||||
|
||||
/* read input from stdin */
|
||||
handle_input(u, fds[2], coninp, CONSOLE_STDIN, CONSOLE_STDIN_END);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
|
52
src/uxncli.c
52
src/uxncli.c
|
@ -1,5 +1,8 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "uxn.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
|
||||
emu_run(Uxn *u)
|
||||
{
|
||||
int i;
|
||||
struct timeval tv;
|
||||
fd_set rfds;
|
||||
init_console();
|
||||
while(!u->dev[0x0f]) {
|
||||
int c = fgetc(stdin);
|
||||
if(c == EOF) {
|
||||
console_input(u, 0x00, CONSOLE_END);
|
||||
break;
|
||||
int nfds = init_rfds(&rfds);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 1000;
|
||||
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