350 lines
8.4 KiB
Diff
350 lines
8.4 KiB
Diff
|
diff --git a/build.sh b/build.sh
|
||
|
index ae2e905..740bf6f 100755
|
||
|
--- a/build.sh
|
||
|
+++ b/build.sh
|
||
|
@@ -60,6 +60,8 @@ then
|
||
|
clang-format -i src/devices/controller.c
|
||
|
clang-format -i src/devices/datetime.h
|
||
|
clang-format -i src/devices/datetime.c
|
||
|
+ clang-format -i src/devices/console.h
|
||
|
+ clang-format -i src/devices/console.c
|
||
|
clang-format -i src/uxnasm.c
|
||
|
clang-format -i src/uxnemu.c
|
||
|
clang-format -i src/uxncli.c
|
||
|
@@ -83,7 +85,7 @@ Darwin) # macOS
|
||
|
UXNEMU_LDFLAGS="$(brew --prefix)/lib/libSDL2.a $(sdl2-config --cflags --static-libs | sed -e 's/-lSDL2 //')"
|
||
|
;;
|
||
|
Linux|*)
|
||
|
- UXNEMU_LDFLAGS="-L/usr/local/lib $(sdl2-config --cflags --libs)"
|
||
|
+ UXNEMU_LDFLAGS="-L/usr/local/lib $(sdl2-config --cflags --libs) -lutil"
|
||
|
;;
|
||
|
esac
|
||
|
|
||
|
@@ -99,7 +101,7 @@ fi
|
||
|
|
||
|
echo "Building.."
|
||
|
${CC} ${CFLAGS} src/uxnasm.c -o bin/uxnasm
|
||
|
-${CC} ${CFLAGS} ${CORE} src/devices/system.c src/devices/file.c src/devices/datetime.c src/devices/mouse.c src/devices/controller.c src/devices/screen.c src/devices/audio.c src/uxnemu.c ${UXNEMU_LDFLAGS} ${FILE_LDFLAGS} -o bin/uxnemu
|
||
|
+${CC} ${CFLAGS} ${CORE} src/devices/system.c src/devices/file.c src/devices/datetime.c src/devices/mouse.c src/devices/controller.c src/devices/screen.c src/devices/audio.c src/devices/console.c src/uxnemu.c ${UXNEMU_LDFLAGS} ${FILE_LDFLAGS} -o bin/uxnemu
|
||
|
${CC} ${CFLAGS} ${CORE} src/devices/system.c src/devices/file.c src/devices/datetime.c src/uxncli.c ${FILE_LDFLAGS} -o bin/uxncli
|
||
|
|
||
|
if [ $install = 1 ]
|
||
|
diff --git a/src/devices/console.c b/src/devices/console.c
|
||
|
new file mode 100644
|
||
|
index 0000000..eda58ea
|
||
|
--- /dev/null
|
||
|
+++ b/src/devices/console.c
|
||
|
@@ -0,0 +1,212 @@
|
||
|
+/*
|
||
|
+Copyright (c) 2023 d_m
|
||
|
+
|
||
|
+Permission to use, copy, modify, and distribute this software for any
|
||
|
+purpose with or without fee is hereby granted, provided that the above
|
||
|
+copyright notice and this permission notice appear in all copies.
|
||
|
+
|
||
|
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
+WITH REGARD TO THIS SOFTWARE.
|
||
|
+*/
|
||
|
+
|
||
|
+#undef _POSIX_C_SOURCE
|
||
|
+#define _POSIX_C_SOURCE 200112L
|
||
|
+
|
||
|
+#include "../uxn.h"
|
||
|
+
|
||
|
+#include <stdio.h>
|
||
|
+#include <unistd.h>
|
||
|
+
|
||
|
+#include <pty.h>
|
||
|
+#include <stdlib.h>
|
||
|
+#include <signal.h>
|
||
|
+#include <sys/select.h>
|
||
|
+#include <sys/wait.h>
|
||
|
+
|
||
|
+/* process */
|
||
|
+static char *fork_args[32];
|
||
|
+static pid_t child_pid;
|
||
|
+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 void
|
||
|
+parse_args(Uint8 *d, Uxn *u)
|
||
|
+{
|
||
|
+ int addr = (d[0x3] << 8) | d[0x4];
|
||
|
+ char *pos = (char *)&u->ram[addr];
|
||
|
+ int i = 0;
|
||
|
+ do {
|
||
|
+ fork_args[i++] = pos;
|
||
|
+ while (*pos != 0) pos++;
|
||
|
+ pos++;
|
||
|
+ } while (*pos != '\0');
|
||
|
+ fork_args[i] = NULL;
|
||
|
+}
|
||
|
+
|
||
|
+/* call after we're sure the process has exited */
|
||
|
+static void
|
||
|
+clean_after_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;
|
||
|
+ saved_in = -1;
|
||
|
+ saved_out = -1;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+kill_child(Uint8 *d)
|
||
|
+{
|
||
|
+ if (child_pid) {
|
||
|
+ kill(child_pid, 9);
|
||
|
+ int wstatus;
|
||
|
+ if (waitpid(child_pid, &wstatus, 0)) {
|
||
|
+ d[0x6] = 1;
|
||
|
+ d[0x7] = WEXITSTATUS(wstatus);
|
||
|
+ clean_after_child();
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+start_fork_pty(Uint8 *d, Uxn *u)
|
||
|
+{
|
||
|
+ int fd = -1;
|
||
|
+ pid_t pid = forkpty(&fd, NULL, NULL, NULL);
|
||
|
+ if (pid < 0) { /* failure */
|
||
|
+ d[0x6] = 0xff;
|
||
|
+ fprintf(stderr, "fork failure");
|
||
|
+ } else if (pid == 0) { /* child */
|
||
|
+ setenv("TERM", "ansi", 1);
|
||
|
+ execvp(fork_args[0], fork_args);
|
||
|
+ d[0x6] = 0xff;
|
||
|
+ fprintf(stderr, "exec failure");
|
||
|
+ } else { /*parent*/
|
||
|
+ child_pid = pid;
|
||
|
+ pty_fd = fd;
|
||
|
+ struct winsize ws = {24, 80, 8, 12};
|
||
|
+ ioctl(fd, TIOCSWINSZ, &ws);
|
||
|
+ saved_in = dup(0);
|
||
|
+ saved_out = dup(1);
|
||
|
+ dup2(fd, 0);
|
||
|
+ dup2(fd, 1);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+start_fork_pipe(Uint8 *d, Uxn *u)
|
||
|
+{
|
||
|
+ if (child_mode & 0x01) {
|
||
|
+ /* parent writes to child's stdin */
|
||
|
+ if (pipe(to_child_fd) == -1) {
|
||
|
+ d[0x6] = 0xff;
|
||
|
+ fprintf(stderr, "pipe error: to child");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (child_mode & 0x06) {
|
||
|
+ /* parent reads from child's stdout and/or stderr */
|
||
|
+ if (pipe(from_child_fd) == -1) {
|
||
|
+ d[0x6] = 0xff;
|
||
|
+ fprintf(stderr, "pipe error: to child");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ pid_t pid = fork();
|
||
|
+ if (pid < 0) { /* failure */
|
||
|
+ d[0x6] = 0xff;
|
||
|
+ fprintf(stderr, "fork failure");
|
||
|
+ } else if (pid == 0) { /* child */
|
||
|
+ if (child_mode & 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);
|
||
|
+ close(from_child_fd[0]);
|
||
|
+ }
|
||
|
+ execvp(fork_args[0], fork_args);
|
||
|
+ d[0x6] = 0xff;
|
||
|
+ fprintf(stderr, "exec failure");
|
||
|
+ } else { /*parent*/
|
||
|
+ child_pid = pid;
|
||
|
+ if (child_mode & 0x01) {
|
||
|
+ saved_out = dup(1);
|
||
|
+ dup2(to_child_fd[1], 1);
|
||
|
+ close(to_child_fd[0]);
|
||
|
+ }
|
||
|
+ if (child_mode & 0x06) {
|
||
|
+ saved_in = dup(0);
|
||
|
+ dup2(from_child_fd[0], 0);
|
||
|
+ close(from_child_fd[1]);
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+start_fork(Uint8 *d, Uxn *u)
|
||
|
+{
|
||
|
+ kill_child(d);
|
||
|
+ child_mode = d[0x5];
|
||
|
+ parse_args(d, u);
|
||
|
+ if (child_mode >= 0x80) {
|
||
|
+ start_fork_pty(d, u);
|
||
|
+ } else {
|
||
|
+ start_fork_pipe(d, u);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+Uint8
|
||
|
+console_dei(Uint8 *d, Uint8 port)
|
||
|
+{
|
||
|
+ switch(port) {
|
||
|
+ case 0x6:
|
||
|
+ case 0x7:
|
||
|
+ if (child_pid) {
|
||
|
+ int wstatus;
|
||
|
+ if (waitpid(child_pid, &wstatus, WNOHANG)) {
|
||
|
+ d[0x6] = 1;
|
||
|
+ d[0x7] = WEXITSTATUS(wstatus);
|
||
|
+ clean_after_child();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return d[port];
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+console_deo(Uint8 *d, Uint8 port, Uxn *u)
|
||
|
+{
|
||
|
+ FILE *fd = NULL;
|
||
|
+ switch(port) {
|
||
|
+ case 0x5: start_fork(d, u); break;
|
||
|
+ case 0x6: kill_child(d); break;
|
||
|
+ case 0x8: fd = stdout; break;
|
||
|
+ case 0x9: fd = stderr; break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if(fd) {
|
||
|
+ fputc(d[port], fd);
|
||
|
+ fflush(fd);
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/src/devices/console.h b/src/devices/console.h
|
||
|
new file mode 100644
|
||
|
index 0000000..3eb8e19
|
||
|
--- /dev/null
|
||
|
+++ b/src/devices/console.h
|
||
|
@@ -0,0 +1,13 @@
|
||
|
+/*
|
||
|
+Copyright (c) 2023 d_m
|
||
|
+
|
||
|
+Permission to use, copy, modify, and distribute this software for any
|
||
|
+purpose with or without fee is hereby granted, provided that the above
|
||
|
+copyright notice and this permission notice appear in all copies.
|
||
|
+
|
||
|
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
+WITH REGARD TO THIS SOFTWARE.
|
||
|
+*/
|
||
|
+
|
||
|
+Uint8 console_dei(Uint8 *d, Uint8 port);
|
||
|
+void console_deo(Uint8 *d, Uint8 port, Uxn *u);
|
||
|
diff --git a/src/uxnemu.c b/src/uxnemu.c
|
||
|
index 39c4300..7291d76 100644
|
||
|
--- a/src/uxnemu.c
|
||
|
+++ b/src/uxnemu.c
|
||
|
@@ -16,12 +16,15 @@
|
||
|
#include "devices/controller.h"
|
||
|
#include "devices/mouse.h"
|
||
|
#include "devices/datetime.h"
|
||
|
+#include "devices/console.h"
|
||
|
#ifdef _WIN32
|
||
|
#include <processthreadsapi.h>
|
||
|
#endif
|
||
|
#pragma GCC diagnostic pop
|
||
|
#pragma clang diagnostic pop
|
||
|
|
||
|
+#include <sys/select.h>
|
||
|
+
|
||
|
/*
|
||
|
Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick
|
||
|
|
||
|
@@ -70,17 +73,6 @@ console_input(Uxn *u, char c)
|
||
|
return uxn_eval(u, GETVEC(d));
|
||
|
}
|
||
|
|
||
|
-static void
|
||
|
-console_deo(Uint8 *d, Uint8 port)
|
||
|
-{
|
||
|
- FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr
|
||
|
- : 0;
|
||
|
- if(fd) {
|
||
|
- fputc(d[port], fd);
|
||
|
- fflush(fd);
|
||
|
- }
|
||
|
-}
|
||
|
-
|
||
|
static Uint8
|
||
|
audio_dei(int instance, Uint8 *d, Uint8 port)
|
||
|
{
|
||
|
@@ -109,6 +101,7 @@ emu_dei(Uxn *u, Uint8 addr)
|
||
|
{
|
||
|
Uint8 p = addr & 0x0f, d = addr & 0xf0;
|
||
|
switch(d) {
|
||
|
+ case 0x10: return console_dei(&u->dev[d], p);
|
||
|
case 0x20: return screen_dei(&u->dev[d], p);
|
||
|
case 0x30: return audio_dei(0, &u->dev[d], p);
|
||
|
case 0x40: return audio_dei(1, &u->dev[d], p);
|
||
|
@@ -133,7 +126,7 @@ emu_deo(Uxn *u, Uint8 addr, Uint8 v)
|
||
|
if(p > 0x7 && p < 0xe)
|
||
|
screen_palette(&uxn_screen, &u->dev[0x8]);
|
||
|
break;
|
||
|
- case 0x10: console_deo(&u->dev[d], p); break;
|
||
|
+ case 0x10: console_deo(&u->dev[d], p, u); break;
|
||
|
case 0x20: screen_deo(u->ram, &u->dev[d], p); break;
|
||
|
case 0x30: audio_deo(0, &u->dev[d], p, u); break;
|
||
|
case 0x40: audio_deo(1, &u->dev[d], p, u); break;
|
||
|
@@ -171,9 +164,23 @@ static int
|
||
|
stdin_handler(void *p)
|
||
|
{
|
||
|
SDL_Event event;
|
||
|
+ fd_set rds;
|
||
|
+ struct timeval tv;
|
||
|
+ int retval;
|
||
|
+ int ok = 1;
|
||
|
event.type = stdin_event;
|
||
|
- while(read(0, &event.cbutton.button, 1) > 0 && SDL_PushEvent(&event) >= 0)
|
||
|
- ;
|
||
|
+ while(ok) {
|
||
|
+ FD_ZERO(&rds);
|
||
|
+ FD_SET(0, &rds); /* read from stdin */
|
||
|
+ tv.tv_sec = 0;
|
||
|
+ tv.tv_usec = 100000; /* wait 100ms */
|
||
|
+ retval = select(1, &rds, NULL, NULL, &tv);
|
||
|
+ if (retval > 0) {
|
||
|
+ ok = read(0, &event.cbutton.button, 1) > 0 && SDL_PushEvent(&event) >= 0;
|
||
|
+ } else if (retval < 0) {
|
||
|
+ ok = 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
return 0;
|
||
|
(void)p;
|
||
|
}
|