Compare commits

...

2 Commits

Author SHA1 Message Date
~d6 8ddc1b7676 fix numerous bugs, add tty controls, etc 2023-12-14 23:43:37 -05:00
~d6 05cf2c6ef3 clean up 2023-12-13 12:20:05 -05:00
7 changed files with 126 additions and 123 deletions

View File

@ -63,6 +63,8 @@ for a host-put (0x1f). the meaning values by host-put value:
- 0x01 - (execute) command string (e.g. 'gcc -o "demo" demo.c') - 0x01 - (execute) command string (e.g. 'gcc -o "demo" demo.c')
- 0x02 - (getpid) unused - 0x02 - (getpid) unused
- 0x03 - (kill) unused - 0x03 - (kill) unused
- 0x04 - (raw-tty) unused
- 0x04 - (restore-tty) unused
- 0x10 - (getenv) name string (e.g. "TERM") - 0x10 - (getenv) name string (e.g. "TERM")
- 0x11 - (setenv) assignment string (e.g. "TERM=vt100") - 0x11 - (setenv) assignment string (e.g. "TERM=vt100")
strings must be null-terminated. commands are parsed by /bin/sh -c. strings must be null-terminated. commands are parsed by /bin/sh -c.
@ -86,6 +88,8 @@ the host-put port (0x1f) specifies which host action to take:
- 0x01 - execute: reads command string, starts a subprocess - 0x01 - execute: reads command string, starts a subprocess
- 0x02 - getpid: responds with child process pid (if any) - 0x02 - getpid: responds with child process pid (if any)
- 0x03 - kill: shuts down child process - 0x03 - kill: shuts down child process
- 0x04 - put stdin tty into raw mode
- 0x05 - restore stdin tty to canonical mode
- 0x10 - getenv: looks up a name (e.g. TERM) in env, responds with value - 0x10 - getenv: looks up a name (e.g. TERM) in env, responds with value
- 0x11 - setenv: reads an assignment (e.g. TERM=vt100), updates env - 0x11 - setenv: reads an assignment (e.g. TERM=vt100), updates env

View File

@ -7,6 +7,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <termios.h>
#include <unistd.h> #include <unistd.h>
#ifdef __linux #ifdef __linux
@ -32,27 +33,28 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
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];
static UxnSubprocess*
find_child_by_pid(pid_t pid)
{
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) {
if(children[n].pid == pid)
return &children[n];
}
return NULL;
}
#define child_ptys(mode) (mode & CONSOLE_MODE_PTY) #define child_ptys(mode) (mode & CONSOLE_MODE_PTY)
#define child_reads(mode) (mode & CONSOLE_CHILD_READS) #define child_reads(mode) (mode & CONSOLE_CHILD_READS)
#define child_writes(mode) (mode & CONSOLE_CHILD_WRITES) #define child_writes(mode) (mode & CONSOLE_CHILD_WRITES)
#define child_writes_out(mode) (mode & CONSOLE_MODE_OUT) #define child_writes_out(mode) (mode & CONSOLE_MODE_OUT)
#define child_writes_err(mode) (mode & CONSOLE_MODE_ERR) #define child_writes_err(mode) (mode & CONSOLE_MODE_ERR)
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];
static int raw_tty = 0;
struct termios old_termios;
static UxnSubprocess*
find_child_by_pid(pid_t pid)
{
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++)
if(children[n].pid == pid)
return &children[n];
return NULL;
}
static void static void
handle_sigchld(int sig) handle_sigchld(int sig)
{ {
@ -61,29 +63,28 @@ handle_sigchld(int sig)
while((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) { while((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
UxnSubprocess *child = find_child_by_pid(pid); UxnSubprocess *child = find_child_by_pid(pid);
if (child != NULL) { if (child != NULL) {
/* set running to negative when exiting for clean up */
child->running = WEXITSTATUS(wstatus) - 256; child->running = WEXITSTATUS(wstatus) - 256;
if(CONSOLE_DEBUG) /* close relevant file descriptors */
fprintf(stderr, "handle_sigchld: %d -> child %d (%d) -> %d\n", sig, child->id, pid, child->running);
if(child_ptys(child->mode)) if(child_ptys(child->mode))
close(child->pty); close(child->pty);
else { else {
if(child_reads(child->mode)) close(child->fd_in); if(child_reads(child->mode)) close(child->fd_in);
if(child_writes(child->mode)) close(child->fd_out); if(child_writes(child->mode)) close(child->fd_out);
} }
/* reset fields to starting values */
child->pid = child->mode = 0; child->pid = child->mode = 0;
child->fd_out = child->fd_in = child->pty = -1; child->fd_out = child->fd_in = child->pty = -1;
return; return;
} }
} }
if(CONSOLE_DEBUG)
fprintf(stderr, "handle_sigchld: no child found", sig);
} }
static void static void
start_fork_pty(UxnSubprocess *child, int mode) start_fork_pty(UxnSubprocess *child, int mode)
{ {
int fd = -1; int fd = -1;
struct winsize ws = {23, 80, 8, 12}; // rows, cols, xps, ypx struct winsize ws = {23, 80, 8, 12};
pid_t pid = forkpty(&fd, NULL, NULL, &ws); pid_t pid = forkpty(&fd, NULL, NULL, &ws);
if(pid < 0) { /* failure */ if(pid < 0) { /* failure */
fprintf(stderr, "parent fork failure\n"); fprintf(stderr, "parent fork failure\n");
@ -99,13 +100,13 @@ start_fork_pty(UxnSubprocess *child, int mode)
child->fd_in = fd; child->fd_in = fd;
child->fd_out = fd; child->fd_out = fd;
} while (child->pid != pid); } while (child->pid != pid);
/* loop to avoid possible race with clean up */
} }
} }
static void static void
start_fork_pipe(UxnSubprocess *child, int mode) start_fork_pipe(UxnSubprocess *child, int mode)
{ {
//printf("start_fork_pipe(..., %d)\n", mode);
if(child_reads(mode)) { if(child_reads(mode)) {
/* parent writes to child's stdin */ /* parent writes to child's stdin */
if(pipe(to_child_fd) == -1) { if(pipe(to_child_fd) == -1) {
@ -125,6 +126,7 @@ start_fork_pipe(UxnSubprocess *child, int mode)
pid_t pid = fork(); pid_t pid = fork();
if(pid < 0) { /* failure */ if(pid < 0) { /* failure */
fprintf(stderr, "fork failure\n"); fprintf(stderr, "fork failure\n");
return;
} else if(pid == 0) { /* child */ } else if(pid == 0) { /* child */
if(child_reads(mode)) { if(child_reads(mode)) {
dup2(to_child_fd[0], 0); dup2(to_child_fd[0], 0);
@ -147,19 +149,12 @@ start_fork_pipe(UxnSubprocess *child, int mode)
child->fd_in = child_reads(mode) ? to_child_fd[1] : -1; child->fd_in = child_reads(mode) ? to_child_fd[1] : -1;
child->fd_out = child_writes(mode) ? from_child_fd[0] : -1; child->fd_out = child_writes(mode) ? from_child_fd[0] : -1;
} while (child->pid != pid); } while (child->pid != pid);
//printf("child %d: pid=%d mode=%d running=%d in=%d out=%d\n", child->id, child->pid, child->mode, child->running, child->fd_in, child->fd_out); /* loop to avoid possible race with clean up */
if(CONSOLE_DEBUG)
fprintf(stderr, "child has pid %d\n", child->pid); /* close the unused end of pipes we opened */
if(child_reads(mode)) close(to_child_fd[0]); if(child_reads(mode)) close(to_child_fd[0]);
if(child_writes(mode)) close(from_child_fd[1]); if(child_writes(mode)) close(from_child_fd[1]);
} }
/* /\* TODO *\/ */
/* for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) { */
/* UxnSubprocess *child = &children[n]; */
/* printf("child: %d/%d (%d, %d)\n", n, child->id, child->fd_out, child->pid); */
/* } */
} }
static void static void
@ -174,8 +169,6 @@ proc_put(Uxn *u, Uint8 *d)
{ {
Uint8 c = d[0xa]; Uint8 c = d[0xa];
int n = d[0xe] & 0x3; int n = d[0xe] & 0x3;
if (CONSOLE_DEBUG)
fprintf(stderr, " fd %d: write %02x\n", children[n].fd_in, c);
write(children[n].fd_in, &c, 1); write(children[n].fd_in, &c, 1);
fdatasync(children[n].fd_in); fdatasync(children[n].fd_in);
} }
@ -190,13 +183,8 @@ host_execute(Uxn *u, Uint8 *d)
UxnSubprocess *child = &children[opts & 0x3]; UxnSubprocess *child = &children[opts & 0x3];
fork_args[2] = cmd; fork_args[2] = cmd;
if(CONSOLE_DEBUG) if(child->pid > 0)
fprintf(stderr, "running execute: %s (#%02x)\n", cmd, opts);
if(child->pid) {
if(CONSOLE_DEBUG)
fprintf(stderr, " killing previous child: %d\n", child->pid);
kill(child->pid, 9); kill(child->pid, 9);
}
if(opts >= 0x80) if(opts >= 0x80)
start_fork_pty(child, mode); start_fork_pty(child, mode);
@ -208,8 +196,8 @@ static void
host_response(Uxn *u, char *s) host_response(Uxn *u, char *s)
{ {
for(int i = 0;; i++) for(int i = 0;; i++)
console_input(u, 0x5, s[i], CONSOLE_HOST); console_input(u, s[i], CONSOLE_HOST);
console_input(u, 0x5, '\0', CONSOLE_HOST_END); console_input(u, '\0', CONSOLE_HOST_END);
} }
static void static void
@ -224,13 +212,42 @@ static void
host_kill(Uxn *u, Uint8 *d) host_kill(Uxn *u, Uint8 *d)
{ {
int n = d[0xe] & 0x3; int n = d[0xe] & 0x3;
kill(children[n].pid, 15); if(children[n].pid > 0)
kill(children[n].pid, 15);
}
static void
host_raw_tty()
{
if(!raw_tty) {
tcgetattr(STDIN_FILENO, &old_termios);
struct termios term = old_termios;
/* corresponds to `stty raw -echo` */
term.c_cflag |= (CS8);
term.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP);
term.c_iflag &= ~(INLCR | IGNCR | ICRNL | IXON | IXOFF | IUCLC | IXANY | IMAXBEL);
term.c_lflag &= ~(ICANON | ISIG | ECHO);
term.c_oflag &= ~(OPOST);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(0, TCSAFLUSH, &term);
raw_tty = 1;
}
}
static void
host_restore_tty()
{
if(raw_tty) {
tcsetattr(0, TCSAFLUSH, &old_termios);
raw_tty = 0;
}
} }
static void static void
host_getenv(Uxn *u, Uint8 *d) host_getenv(Uxn *u, Uint8 *d)
{ {
int addr = PEEK2(d + 0x3); int addr = PEEK2(d + 0xc);
char *name = (char *)&u->ram[addr]; char *name = (char *)&u->ram[addr];
host_response(u, getenv(name)); host_response(u, getenv(name));
} }
@ -238,23 +255,29 @@ host_getenv(Uxn *u, Uint8 *d)
static void static void
host_setenv(Uxn *u, Uint8 *d) host_setenv(Uxn *u, Uint8 *d)
{ {
int addr = PEEK2(d + 0x3); int addr = PEEK2(d + 0xc);
char *name = (char *)&u->ram[addr]; char *name = (char *)&u->ram[addr];
int i = 0; int i = 0;
while (name[i] != '=') i++; while(name[i] != '=') i++;
name[i] = '\0'; if(name[i] == '=') {
char *value = &name[i+1]; name[i] = '\0';
setenv(name, value, 1); char *value = &name[i+1];
setenv(name, value, 1);
} else {
fprintf(stderr, "mal-formed setenv\n");
}
} }
static void static void
host_put(Uxn *u, Uint8 *d) host_put(Uxn *u, Uint8 *d)
{ {
switch(d[0xf]) { switch(d[0xf]) {
case 0x0: /* nop */ break; case 0x00: /* nop */ break;
case 0x1: host_execute(u, d); break; case 0x01: host_execute(u, d); break;
case 0x2: host_getpid(u, d); break; case 0x02: host_getpid(u, d); break;
case 0x3: host_kill(u, d); break; case 0x03: host_kill(u, d); break;
case 0x04: host_raw_tty(); break;
case 0x05: host_restore_tty(); break;
case 0x10: host_getenv(u, d); break; case 0x10: host_getenv(u, d); break;
case 0x11: host_setenv(u, d); break; case 0x11: host_setenv(u, d); break;
} }
@ -288,23 +311,19 @@ UxnSubprocess
void void
console_monitor(Uxn *u) console_monitor(Uxn *u)
{ {
fflush(stdout); /*fflush(stdout);*/ /* TODO: needed? stderr? children? */
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) { for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) {
UxnSubprocess *child = &children[n]; UxnSubprocess *child = &children[n];
/* printf("child: %d/%d (%d, %d)\n", n, child->id, child->fd_out, child->pid); */
if(child->running < 0) { if(child->running < 0) {
/* printf("child ready for clean up: %d\n", n); */
/* fflush(stdout); */
Uint8 status = child->running + 256; Uint8 status = child->running + 256;
child->running = 0; child->running = 0;
console_input(u, 0x4, status, CONSOLE_CHILD_EXIT | n); console_input(u, status, CONSOLE_CHILD_EXIT | n);
} }
} }
} }
// TODO ignore port
int int
console_input(Uxn *u, int port, char c, int type) console_input(Uxn *u, char c, int type)
{ {
Uint8 *d = &u->dev[0x10]; Uint8 *d = &u->dev[0x10];
d[0x2] = c; d[0x2] = c;
@ -317,8 +336,8 @@ 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, 0x2, *p++, CONSOLE_ARG); while(*p) console_input(u, *p++, CONSOLE_ARG);
console_input(u, 0x2, '\n', i == argc - 1 ? CONSOLE_ARG_END : CONSOLE_ARG_SPACER); console_input(u, '\n', i == argc - 1 ? CONSOLE_ARG_END : CONSOLE_ARG_SPACER);
} }
} }

View File

@ -11,8 +11,6 @@ WITH REGARD TO THIS SOFTWARE.
#define CONSOLE_VERSION 1 #define CONSOLE_VERSION 1
#define CONSOLE_DEBUG 0
#define CONSOLE_NONE 0x00 #define CONSOLE_NONE 0x00
#define CONSOLE_STDIN 0x01 #define CONSOLE_STDIN 0x01
#define CONSOLE_ARG 0x02 #define CONSOLE_ARG 0x02
@ -27,19 +25,19 @@ WITH REGARD TO THIS SOFTWARE.
#define CONSOLE_CHILD_EXIT 0x80 /* exits from child-n: 0x80 | n */ #define CONSOLE_CHILD_EXIT 0x80 /* exits from child-n: 0x80 | n */
typedef struct UxnSubprocess { typedef struct UxnSubprocess {
int id; /* identifier: 0-3 */ int id; /* identifier: 0-3 */
pid_t pid; /* when running, pid > 0 */ pid_t pid; /* when running, pid > 0 */
int mode; /* bit flags: CONSOLE_MODE_ */ int mode; /* bit flags: CONSOLE_MODE_ */
int running; /* 0=stopped, >0 running, <0 exiting */ int running; /* 0=stopped, >0 running, <0 exiting */
int fd_in; /* fd to send to child; -1 is unset */ int fd_in; /* fd to send to child; -1 is unset */
int fd_out; /* fd to read from child; -1 is unset */ int fd_out; /* fd to read from child; -1 is unset */
int pty; /* pty for child; -1 is unset */ int pty; /* pty for child; -1 is unset */
} UxnSubprocess; } UxnSubprocess;
void init_console(void); void init_console(void);
UxnSubprocess *get_child(int n); UxnSubprocess *get_child(int n);
void console_monitor(Uxn *u); void console_monitor(Uxn *u);
int console_input(Uxn *u, int port, 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);
@ -49,8 +47,11 @@ void console_deo(Uxn *u, Uint8 *d, Uint8 port);
#define CONSOLE_MODE_OUT 0x20 #define CONSOLE_MODE_OUT 0x20
#define CONSOLE_MODE_INP 0x10 #define CONSOLE_MODE_INP 0x10
#define CONSOLE_CHILD_READS 0x90 /* CONSOLE_MODE_PTY | CONSOLE_MODE_INP */ /* CONSOLE_MODE_PTY | CONSOLE_MODE_INP */
#define CONSOLE_CHILD_WRITES 0xe0 /* CONSOLE_MODE_PTY | CONSOLE_MODE_OUT | CONSOLE_MODE_ERR */ #define CONSOLE_CHILD_READS 0x90
/* CONSOLE_MODE_PTY | CONSOLE_MODE_OUT | CONSOLE_MODE_ERR */
#define CONSOLE_CHILD_WRITES 0xe0
#define CONSOLE_MAX_CHILDREN 4 #define CONSOLE_MAX_CHILDREN 4

View File

@ -152,16 +152,16 @@ toggle_scale(Uxn *u)
/* returns true if the fd ended (has been closed), false otherwise */ /* returns true if the fd ended (has been closed), false otherwise */
static int static int
handle_input(Uxn *u, struct pollfd pollfd, int port, char *coninp, int argdata, int argend) handle_input(Uxn *u, struct pollfd pollfd, char *coninp, int argdata, int argend)
{ {
if((pollfd.revents & POLLIN) != 0) { if((pollfd.revents & POLLIN) != 0) {
int n = read(pollfd.fd, coninp, CONINBUFSIZE - 1); int n = read(pollfd.fd, coninp, CONINBUFSIZE - 1);
coninp[n] = 0; coninp[n] = 0;
if(n == 0) if(n == 0)
console_input(u, port, 0, argend); console_input(u, 0, argend);
else else
for(int i = 0; i < n; i++) for(int i = 0; i < n; i++)
console_input(u, port, coninp[i], argdata); console_input(u, coninp[i], argdata);
return n == 0; return n == 0;
} else } else
return 0; return 0;
@ -268,17 +268,14 @@ 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;
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]) {
for(i = 0; i < 4; i++) { for(i = 0; i < CONSOLE_MAX_CHILDREN; i++) {
fds[i + 3].fd = get_child(i)->fd_out; fds[i + 3].fd = get_child(i)->fd_out;
fds[i + 3].events = POLLIN;
} }
if(poll(fds, 7, 1000) <= 0) if(poll(fds, 3 + CONSOLE_MAX_CHILDREN, 1000) <= 0)
continue; continue;
while(XPending(display)) while(XPending(display))
emu_event(u); emu_event(u);
@ -294,11 +291,11 @@ emu_run(Uxn *u, char *rom)
} }
/* read input from stdin */ /* read input from stdin */
handle_input(u, fds[2], 0x2, coninp, CONSOLE_STDIN, CONSOLE_STDIN_END); handle_input(u, fds[2], coninp, CONSOLE_STDIN, CONSOLE_STDIN_END);
/* read input from child processes */ /* read input from child processes */
for(i = 0; i < 4; i++) for(i = 0; i < CONSOLE_MAX_CHILDREN; i++)
handle_input(u, fds[i + 3], 0x4, coninp, CONSOLE_CHILD_DATA | i, CONSOLE_CHILD_END | i); handle_input(u, fds[i + 3], coninp, CONSOLE_CHILD_DATA | i, CONSOLE_CHILD_END | i);
/* check to see if any children exited */ /* check to see if any children exited */
console_monitor(u); console_monitor(u);

View File

@ -50,17 +50,11 @@ handle_input(Uxn *u, int fd, int port, int argdata, int argend)
{ {
char buf[32]; char buf[32];
int n = read(fd, &buf, 32); int n = read(fd, &buf, 32);
if(n == 0) { if(n == 0)
if(CONSOLE_DEBUG) console_input(u, 0x00, argend);
fprintf(stderr, " fd %d: closed\n", fd); else
console_input(u, port, 0x00, argend); for(int i = 0; i < n; i++)
} else { console_input(u, buf[i], argdata);
for(int i = 0; i < n; i++) {
if(CONSOLE_DEBUG)
fprintf(stderr, " fd %d: read %02x\n", fd, buf[i]);
console_input(u, port, buf[i], argdata);
}
}
} }
static int static int

View File

@ -171,16 +171,12 @@
#01 .Console/host-put DEO JMP2r #01 .Console/host-put DEO JMP2r
( restore the terminal to "normal" settings ) ( restore the terminal to "normal" settings )
@restore-terminal @restore-terminal ( -> )
;stty-sane !run-cmd #05 .Console/host-put DEO JMP2r
( put the terminal in raw mode ) ( put the terminal in raw mode )
@init-terminal ( -> ) @init-terminal ( -> )
;stty-raw !run-cmd #04 .Console/host-put DEO JMP2r
( command strings to use )
@stty-sane "stty 20 "sane 00
@stty-raw "stty 20 "raw 20 "-echo 00
( import uxn regex library ) ( import uxn regex library )
~regex.tal ~regex.tal
@ -487,11 +483,7 @@
( ) ( )
( this definition is needed so the address can be used by JCN2. ) ( this definition is needed so the address can be used by JCN2. )
@quit-now @quit-now
;finish-quit .Console/vector DEO2 restore-terminal #010f DEO BRK
restore-terminal BRK
@finish-quit
.Console/type DEI #81 NEQ ?{ #80 .System/halt DEO } BRK
( label that calls BRK ) ( label that calls BRK )
( ) ( )

View File

@ -409,7 +409,6 @@
( send ESC [ $c ) ( send ESC [ $c )
@arrow ( c^ -> ) @arrow ( c^ -> )
( .Console/w STH )
.Console/proc-put STH .Console/proc-put STH
#1b STHkr DEO LIT "[ STHkr DEO STHr DEO #1b STHkr DEO LIT "[ STHkr DEO STHr DEO
JMP2r JMP2r
@ -417,12 +416,10 @@
@paste-from-buf ( size* -> ) @paste-from-buf ( size* -> )
;paste-buf SWP2 OVR2 ADD2 SWP2 ( limit* start* ) ;paste-buf SWP2 OVR2 ADD2 SWP2 ( limit* start* )
&loop ( limit* pos* ) &loop ( limit* pos* )
( LDAk .Console/w DEO INC2 ( limit* pos+1* ) )
LDAk .Console/proc-put DEO INC2 ( limit* pos+1* ) LDAk .Console/proc-put DEO INC2 ( limit* pos+1* )
GTH2k ?&loop POP2 POP2 JMP2r GTH2k ?&loop POP2 POP2 JMP2r
@bracket-paste ( c^ -> ) @bracket-paste ( c^ -> )
( .Console/w STH )
.Console/proc-put STH .Console/proc-put STH
#1b STHkr DEO #1b STHkr DEO
LIT "[ STHkr DEO LIT "[ STHkr DEO
@ -638,7 +635,6 @@
.Controller/key DEI .Controller/key DEI
DUP #08 NEQ ?&done DUP #08 NEQ ?&done
POP #7f ( send DEL instead of BS ) POP #7f ( send DEL instead of BS )
( &done .Console/w DEO BRK )
&done .Console/proc-put DEO BRK &done .Console/proc-put DEO BRK
@ctrl ( -> is-down? ) .Controller/button DEI #01 AND JMP2r @ctrl ( -> is-down? ) .Controller/button DEI #01 AND JMP2r
@ -646,10 +642,8 @@
( alt-XYZ emits ESC and then emits XYZ ) ( alt-XYZ emits ESC and then emits XYZ )
@on-alt-key ( -> BRK ) @on-alt-key ( -> BRK )
( #1b .Console/w DEO )
#1b .Console/proc-put DEO #1b .Console/proc-put DEO
ctrl ?on-ctrl-key ctrl ?on-ctrl-key
( .Controller/key DEI .Console/w DEO BRK )
.Controller/key DEI .Console/proc-put DEO BRK .Controller/key DEI .Console/proc-put DEO BRK
( control seqs: ) ( control seqs: )
@ -685,7 +679,6 @@
&rs #1e !&done &rs #1e !&done
&us #1f !&done &us #1f !&done
&c1 LIT "@ SUB &c1 LIT "@ SUB
( &done .Console/w DEO BRK )
&done .Console/proc-put DEO BRK &done .Console/proc-put DEO BRK
@on-read-priv ( -> BRK ) @on-read-priv ( -> BRK )
@ -911,15 +904,11 @@
@dsr ( n* -> ) @dsr ( n* -> )
#0006 NEQ2 ?&done #0006 NEQ2 ?&done
( #1b .Console/w DEO )
#1b .Console/proc-put DEO #1b .Console/proc-put DEO
( LIT "[ .Console/w DEO )
LIT "[ .Console/proc-put DEO LIT "[ .Console/proc-put DEO
.cur-y LDZ2 INC2 emit-dec2 .cur-y LDZ2 INC2 emit-dec2
( LIT "; .Console/w DEO )
LIT "; .Console/proc-put DEO LIT "; .Console/proc-put DEO
.cur-x LDZ2 INC2 emit-dec2 .cur-x LDZ2 INC2 emit-dec2
( LIT "R .Console/w DEO )
LIT "R .Console/proc-put DEO LIT "R .Console/proc-put DEO
&done BRK &done BRK
@ -1300,14 +1289,21 @@
( emit a signed short as a decimal ) ( emit a signed short as a decimal )
@emit-sdec2 ( n* -> ) @emit-sdec2 ( n* -> )
DUP2k #1f SFT2 EQUk ?&s LIT2 "- 18 DEO DUP2k #1f SFT2 EQUk ?&s LIT "- .Console/proc-put DEO
&s MUL2 SUB2 ( fall-through to emit-dec2 ) &s MUL2 SUB2 ( fall-through to emit-dec2 )
( emit an unsigned short as a decimal ) ( emit an unsigned short as a decimal )
@emit-dec2 ( n* -> ) @emit-dec2 ( n* -> )
LITr ff00 &read #000a DIV2k STH2k MUL2 SUB2 STH2r INCr ORAk ?&read LIT2r ff00 ( n* [ff^ 0^] )
POP2 &write NIP #30 ADD #18 DEO OVRr ADDr STHkr ?&write &read ( ... x* )
POP2r JMP2r #000a DIV2k STH2k ( x* 10* x/10* [ff^ i^ x/10*] )
MUL2 SUB2 STH2r ( x%10* x/10* [ff^ i^] )
INCr ORAk ?&read ( x%10* x/10* [ff^ i+1^] )
POP2 ( x0* ... xn* [ff^ i+1^] )
&write
NIP #30 ADD .Console/proc-put DEO ( x0* ... xn-1* [ff^ j^] )
OVRr ADDr STHkr ?&write ( x* ... xn-1* [ff^ j-1^] )
POP2r JMP2r ( )
@debug-log "debug_term.log 00 @debug-log "debug_term.log 00
@scratch $40 &pos $2 @scratch $40 &pos $2