wip wip
This commit is contained in:
parent
56a17bd878
commit
813cc5910c
|
@ -0,0 +1,78 @@
|
|||
CONSOLE DEVICE
|
||||
|
||||
0x10 vector* 0x18 out*
|
||||
0x11 (vector) 0x19 err*
|
||||
0x12 read 0x1a --
|
||||
0x13 exec arguments 0x1b --
|
||||
0x14 (exec arguments) 0x1c --
|
||||
0x15 exec mode 0x1d --
|
||||
0x16 exec action* 0x1e --
|
||||
0x17 input type 0x1f --
|
||||
|
||||
(* denotes a register that causes an immediate effect)
|
||||
|
||||
vector (0x10) contains an address to jump to when input is available.
|
||||
when the vector fires one byte can be read from the read port (0x12),
|
||||
and its meaning is defined by the input type (0x17).
|
||||
|
||||
the read port (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).
|
||||
|
||||
the meaning of exec args (0x13) depends on which action it is used with:
|
||||
- 0x00 - unused
|
||||
- 0x01 - address of a command string (e.g. "gcc -o demo demo.c")
|
||||
- 0x02 - address of an env. variable name (e.g. "TERM")
|
||||
- 0x03 - address of an env. variable name=value pair (e.g. "TERM=vt100")
|
||||
- 0x04 - unsigned 16-bit integer (pty height, e.g. 0x0018)
|
||||
- 0x05 - unsigned 16-bit integer (pty width, e.g. 0x0050)
|
||||
- (all strings must be null-terminated)
|
||||
|
||||
exec mode (0x15) 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 stdout
|
||||
+ 0x10 - write to child's stdin
|
||||
|
||||
exec action (0x16) acts on the exec args address (0x13):
|
||||
- 0x00 - nop: does nothing
|
||||
- 0x01 - execute: reads command string, starts a subprocess
|
||||
- 0x02 - getenv: reads string (NAME) from exec args
|
||||
- 0x03 - setenv: reads string (NAME=value) and updates environment
|
||||
- 0x04 - set-pty-height: reads u16 from exec args, updates winsize
|
||||
- 0x05 - set-pty-width: reads u16 from exec args, updates winsize
|
||||
(getenv data returned using console vector with input type 0x05, 0x06)
|
||||
|
||||
the input type (0x17) field explains how to interpret the read port
|
||||
(0x12) when executing the console vector (0x10). these are the input
|
||||
types that are expected:
|
||||
- 0x00 - no input (read port is unused)
|
||||
- 0x01 - stdin
|
||||
- 0x02 - argument
|
||||
- 0x03 - argument spacer (read port is \n)
|
||||
- 0x04 - argument end (read port is \n)
|
||||
- 0x05 - action output
|
||||
- 0x06 - action output end (read port is \n)
|
||||
- 0x40 - child 0 exited (read port is exit code)
|
||||
- 0x41 - child 1 exited (read port is exit code)
|
||||
- 0x42 - child 2 exited (read port is exit code)
|
||||
- 0x43 - child 3 exited (read port is exit code)
|
||||
- 0x81 - child 1
|
||||
- 0x82 - child 2
|
||||
- 0x83 - child 3
|
||||
- (child 0 uses 0x01, i.e. "stdin", instead of 0x80)
|
||||
|
||||
writing a byte to the out port (0x18) will send one byte of data to
|
||||
the standard output. if exec mode (0x15) is non-zero the lower 2 bits
|
||||
will determine which child process to send the output to.
|
||||
|
||||
writing a byte to the err port (0x19) will send one byte of data to
|
||||
stderr. unlike with the out port (0x18), stderr is never redirected to
|
||||
child processes.
|
|
@ -1,6 +1,7 @@
|
|||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -31,6 +32,84 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
WITH REGARD TO THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
CONSOLE DEVICE
|
||||
|
||||
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 --
|
||||
|
||||
(* 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;
|
||||
|
@ -42,6 +121,13 @@ 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)
|
||||
{
|
||||
|
@ -59,26 +145,25 @@ parse_args(Uxn *u, Uint8 *d)
|
|||
|
||||
/* call after we're sure the process has exited */
|
||||
static void
|
||||
clean_after_child(void)
|
||||
clean_after_child(int child)
|
||||
{
|
||||
child_pid = 0;
|
||||
if(child_mode >= 0x80) {
|
||||
close(pty_fd);
|
||||
dup2(saved_in, 0);
|
||||
dup2(saved_out, 1);
|
||||
} else {
|
||||
if(child_mode & 0x01) {
|
||||
close(to_child_fd[1]);
|
||||
dup2(saved_out, 1);
|
||||
}
|
||||
if(child_mode & 0x06) {
|
||||
close(from_child_fd[0]);
|
||||
dup2(saved_in, 0);
|
||||
}
|
||||
}
|
||||
child_mode = 0;
|
||||
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;
|
||||
}
|
||||
child_pids[n] = 0;
|
||||
child_modes[n] = 0;
|
||||
child_ptys[n] = -1;
|
||||
child_fd_ins[n] = -1;
|
||||
child_fd_outs[n] = -1;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -105,29 +190,36 @@ 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);
|
||||
setenv("TERM", "ansi", 1); /* TODO: configure these */
|
||||
execvp(fork_args[0], fork_args);
|
||||
d[0x6] = 0xff;
|
||||
fprintf(stderr, "exec failure\n");
|
||||
} else { /*parent*/
|
||||
child_pid = pid;
|
||||
pty_fd = fd;
|
||||
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)
|
||||
{
|
||||
if(child_mode & 0x01) {
|
||||
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;
|
||||
|
@ -136,7 +228,7 @@ start_fork_pipe(Uint8 *d)
|
|||
}
|
||||
}
|
||||
|
||||
if(child_mode & 0x06) {
|
||||
if(child_modes[n] & 0x06) {
|
||||
/* parent reads from child's stdout and/or stderr */
|
||||
if(pipe(from_child_fd) == -1) {
|
||||
d[0x6] = 0xff;
|
||||
|
@ -150,28 +242,34 @@ start_fork_pipe(Uint8 *d)
|
|||
d[0x6] = 0xff;
|
||||
fprintf(stderr, "fork failure\n");
|
||||
} else if(pid == 0) { /* child */
|
||||
if(child_mode & 0x01) {
|
||||
if(child_modes[n] & 0x01) {
|
||||
dup2(to_child_fd[0], 0);
|
||||
close(to_child_fd[1]);
|
||||
}
|
||||
if(child_mode & 0x06) {
|
||||
if(child_mode & 0x02) dup2(from_child_fd[1], 1);
|
||||
if(child_mode & 0x04) dup2(from_child_fd[1], 2);
|
||||
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_pid = pid;
|
||||
if(child_mode & 0x01) {
|
||||
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_mode & 0x06) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
@ -180,13 +278,14 @@ start_fork_pipe(Uint8 *d)
|
|||
static void
|
||||
kill_child(Uint8 *d, int options)
|
||||
{
|
||||
if(child_pid) {
|
||||
kill(child_pid, 9);
|
||||
int n = d[0xa] & 0x3;
|
||||
if(child_pids[n]) {
|
||||
kill(child_pids[n], 9);
|
||||
int wstatus;
|
||||
if(waitpid(child_pid, &wstatus, options)) {
|
||||
if(waitpid(child_pids[n], &wstatus, options)) {
|
||||
d[0x6] = 1;
|
||||
d[0x7] = WEXITSTATUS(wstatus);
|
||||
clean_after_child();
|
||||
clean_after_child(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,11 +293,12 @@ kill_child(Uint8 *d, int options)
|
|||
static void
|
||||
start_fork(Uxn *u, Uint8 *d)
|
||||
{
|
||||
int n = d[0xa] & 0x3;
|
||||
fflush(stderr);
|
||||
kill_child(d, 0);
|
||||
child_mode = d[0x5];
|
||||
child_modes[n] = d[0x5];
|
||||
parse_args(u, d);
|
||||
if(child_mode >= 0x80)
|
||||
if(child_modes[n] >= 0x80)
|
||||
start_fork_pty(d);
|
||||
else
|
||||
start_fork_pipe(d);
|
||||
|
@ -230,3 +330,12 @@ console_deo(Uxn *u, Uint8 *d, Uint8 port)
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,3 +20,4 @@ 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);
|
||||
|
|
|
@ -220,7 +220,7 @@ emu_run(Uxn *u, char *rom)
|
|||
int i = 1, n, s = uxn_screen.scale;
|
||||
char expirations[8];
|
||||
char coninp[CONINBUFSIZE];
|
||||
struct pollfd fds[3];
|
||||
struct pollfd fds[18];
|
||||
static const struct itimerspec screen_tspec = {{0, 16666666}, {0, 16666666}};
|
||||
loaded_rom = rom;
|
||||
|
||||
|
@ -245,6 +245,8 @@ 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);
|
||||
|
||||
/* main loop */
|
||||
while(!u->dev[0x0f]) {
|
||||
if(poll(fds, 3, 1000) <= 0)
|
||||
|
|
Loading…
Reference in New Issue