Add help dialog to Blinkenlights emulator

This commit is contained in:
Justine Tunney
2021-02-19 16:56:06 -08:00
parent 02b4a5c824
commit 5f1415af3a
9 changed files with 248 additions and 147 deletions

View File

@ -1511,17 +1511,20 @@ long: push $GDT_LONG_DATA
xor %ebp,%ebp
mov $REAL_STACK_FRAME+FRAMESIZE,%esp
call __map_image
ezlea metal,ax
ezlea metal.thunk,ax
jmp *%rax
.endfn long
// Long mode in virtual address space.
// @noreturn
metal:
metal.thunk:
#if USE_SYMBOL_HACK
.byte 0x0f,0x1f,0207 # nop rdi binbase
.long (IMAGE_BASE_VIRTUAL-IMAGE_BASE_REAL)/512
#endif
/ 𝑠𝑙𝑖𝑑𝑒
.endfn metal.thunk
metal:
xor %eax,%eax # clear bss
mov $ape_bss_vaddr,%edi
mov $ape_bss_memsz,%ecx

View File

@ -16,8 +16,6 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/safemacros.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
@ -37,26 +35,24 @@
* @see gc()
*/
char *(xstrcat)(const char *s, ...) {
char *p, b[2];
va_list va;
size_t i, n, n2, l;
size_t n, m;
char *p, b[2];
p = NULL;
i = n = 0;
n = 0;
va_start(va, s);
do {
if ((intptr_t)s > 0 && (intptr_t)s <= 255) {
b[0] = (unsigned char)(intptr_t)s;
b[1] = '\0';
s = b;
l = 1;
m = 1;
} else {
l = strlen(s);
m = strlen(s);
}
if ((n2 = i + l + 16) >= n) {
p = xrealloc(p, (n = n2 + (n2 >> 1)));
}
memcpy(p + i, s, l + 1);
i += l;
p = xrealloc(p, n + m + 1);
memcpy(p + n, s, m + 1);
n += m;
} while ((s = va_arg(va, const char *)));
va_end(va);
return p;

View File

@ -126,6 +126,27 @@ FEATURES\n\
8086, 8087, i386, x86_64, SSE3, SSSE3, POPCNT, MDA, CGA, TTY\n\
\n"
#define HELP \
"\e[1mBLINKENLIGHTS v1.o\e[22m\
https://justine.lol/blinkenlights/\n\
\n\
KEYBOARD SHORTCUTS CLI FLAGS\n\
\n\
ctrl-c interrupt -t tui mode\n\
s step -r real mode\n\
n next -s statistics\n\
c continue -b ADDR push breakpoint\n\
q quit -L PATH log file location\n\
f finish -R reactive tui mode\n\
R restart -H disable highlighting\n\
x hex -v increase verbosity\n\
? help -? help\n\
t sse type\n\
w sse width\n\
B pop breakpoint\n\
ctrl-t turbo\n\
alt-t slowmo"
#define MAXZOOM 16
#define DUMPWIDTH 64
#define DISPWIDTH 80
@ -255,17 +276,18 @@ static int64_t breakpointsstart;
static uint64_t last_opcount;
static char *codepath;
static void *onbusted;
static char *dialog;
static char *statusmessage;
static struct Pty *pty;
static struct Machine *m;
static struct Panels pan;
static struct Breakpoints breakpoints;
static struct MemoryView codeview;
static struct MemoryView readview;
static struct MemoryView writeview;
static struct MemoryView stackview;
static struct MachineState laststate;
static struct Breakpoints breakpoints;
static struct MachineMemstat lastmemstat;
static struct XmmType xmmtype;
static struct Elf elf[1];
@ -424,15 +446,24 @@ static void CopyMachineState(struct MachineState *ms) {
memcpy(&ms->sse, &m->sse, sizeof(m->sse));
}
/**
* Handles file mapped page faults in valid page but past eof.
*/
static void OnSigBusted(void) {
CHECK(onbusted);
longjmp(onbusted, 1);
}
/**
* Returns true if 𝑣 is a shadow memory virtual address.
*/
static bool IsShadow(int64_t v) {
return 0x7fff8000 <= v && v < 0x100080000000;
}
/**
* Returns glyph representing byte at virtual address 𝑣.
*/
static int VirtualBing(int64_t v) {
int rc;
uint8_t *p;
@ -451,6 +482,9 @@ static int VirtualBing(int64_t v) {
return rc;
}
/**
* Returns ASAN shadow uint8 concomitant to address 𝑣 or -1.
*/
static int VirtualShadow(int64_t v) {
int rc;
char *p;
@ -547,9 +581,7 @@ static void TuiRejuvinate(void) {
static void OnQ(void) {
LOGF("OnQ");
if (action & FAILURE) exit(1);
action |= INT;
breakpoints.i = 0;
action |= EXIT;
}
static void OnV(void) {
@ -594,6 +626,7 @@ static void TuiCleanup(void) {
TtyRestore1();
DisableMouseTracking();
tuimode = false;
LeaveScreen();
}
static void ResolveBreakpoints(void) {
@ -603,8 +636,10 @@ static void ResolveBreakpoints(void) {
if ((sym = DisFindSymByName(dis, breakpoints.p[i].symbol)) != -1) {
breakpoints.p[i].addr = dis->syms.p[sym].addr;
} else {
fprintf(stderr, "error: breakpoint not found: %s\n",
breakpoints.p[i].symbol);
fprintf(
stderr,
"error: breakpoint not found: %s (out of %,ld loaded symbols)\n",
breakpoints.p[i].symbol, dis->syms.i);
exit(1);
}
}
@ -2155,12 +2190,9 @@ static void OnBinbase(struct Machine *m) {
int64_t skew;
skew = m->xedd->op.disp * 512;
LOGF("skew binbase %,ld @ %p", skew, GetIp());
for (i = 0; i < dis->syms.i; ++i) {
dis->syms.p[i].addr += skew;
}
for (i = 0; i < dis->loads.i; ++i) {
dis->loads.p[i].addr += skew;
}
for (i = 0; i < dis->syms.i; ++i) dis->syms.p[i].addr += skew;
for (i = 0; i < dis->loads.i; ++i) dis->loads.p[i].addr += skew;
for (i = 0; i < breakpoints.i; ++i) breakpoints.p[i].addr += skew;
Disassemble();
}
@ -2270,7 +2302,7 @@ static void OnFinish(void) {
action &= ~CONTINUE;
}
static void OnContinue(void) {
static void OnContinueTui(void) {
action ^= CONTINUE;
action &= ~STEP;
action &= ~NEXT;
@ -2278,6 +2310,15 @@ static void OnContinue(void) {
action &= ~FAILURE;
}
static void OnContinueExec(void) {
tuimode = false;
action |= CONTINUE;
action &= ~STEP;
action &= ~NEXT;
action &= ~FINISH;
action &= ~FAILURE;
}
static void OnQuit(void) {
action |= QUIT;
}
@ -2394,41 +2435,41 @@ static void OnMouse(char *p) {
y -= ep->top;
x -= ep->left;
switch (e) {
case kMouseWheelUp:
OnMouseWheelUp(ep, y, x);
break;
case kMouseWheelDown:
OnMouseWheelDown(ep, y, x);
break;
case kMouseCtrlWheelUp:
OnMouseCtrlWheelUp(ep, y, x);
break;
case kMouseCtrlWheelDown:
OnMouseCtrlWheelDown(ep, y, x);
break;
CASE(kMouseWheelUp, OnMouseWheelUp(ep, y, x));
CASE(kMouseWheelDown, OnMouseWheelDown(ep, y, x));
CASE(kMouseCtrlWheelUp, OnMouseCtrlWheelUp(ep, y, x));
CASE(kMouseCtrlWheelDown, OnMouseCtrlWheelDown(ep, y, x));
default:
break;
}
}
}
static void OnHelp(void) {
DEBUGF("setting dialog");
dialog = HELP;
}
static void ReadKeyboard(void) {
char buf[64], *p = buf;
memset(buf, 0, sizeof(buf));
dialog = NULL;
if (readansi(ttyin, buf, sizeof(buf)) == -1) {
if (errno == EINTR) {
LOGF("readkeyboard interrupted");
LOGF("ReadKeyboard interrupted");
return;
}
FATALF("readkeyboard failed: %s", strerror(errno));
FATALF("ReadKeyboard failed: %s", strerror(errno));
}
switch (*p++) {
CASE('q', OnQ());
CASE('v', OnV());
CASE('?', OnHelp());
CASE('s', OnStep());
CASE('n', OnNext());
CASE('f', OnFinish());
CASE('c', OnContinue());
CASE('c', OnContinueTui());
CASE('C', OnContinueExec());
CASE('R', OnRestart());
CASE('x', OnXmmDisp());
CASE('t', OnXmmType());
@ -2451,57 +2492,26 @@ static void ReadKeyboard(void) {
CASE(CTRL('T'), OnTurbo());
case '\e':
switch (*p++) {
CASE('v', OnPageUp());
CASE('t', OnSlowmo());
CASE('v', OnPageUp()); /* alt+v */
CASE('t', OnSlowmo()); /* alt+t */
case 'O':
switch (*p++) {
CASE('P', OnHelp()); /* \eOP is F1 */
default:
break;
}
break;
case '[':
switch (*p++) {
CASE('<', OnMouse(p));
CASE('A', OnUpArrow());
CASE('B', OnDownArrow());
CASE('F', OnEnd());
CASE('H', OnHome());
case '1':
switch (*p++) {
CASE('~', OnHome());
default:
break;
}
break;
case '4':
switch (*p++) {
CASE('~', OnEnd());
default:
break;
}
break;
case '5':
switch (*p++) {
CASE('~', OnPageUp());
default:
break;
}
break;
case '6':
switch (*p++) {
CASE('~', OnPageDown());
default:
break;
}
break;
case '7':
switch (*p++) {
CASE('~', OnHome());
default:
break;
}
break;
case '8':
switch (*p++) {
CASE('~', OnEnd());
default:
break;
}
break;
CASE('A', OnUpArrow()); /* \e[A is up */
CASE('B', OnDownArrow()); /* \e[B is down */
CASE('F', OnEnd()); /* \e[F is end */
CASE('H', OnHome()); /* \e[H is home */
CASE('1', OnHome()); /* \e[1~ is home */
CASE('4', OnEnd()); /* \e[1~ is end */
CASE('5', OnPageUp()); /* \e[1~ is pgup */
CASE('6', OnPageDown()); /* \e[1~ is pgdn */
default:
break;
}
@ -2609,14 +2619,13 @@ static void Tui(void) {
SetupDraw();
ScrollOp(&pan.disassembly, GetDisIndex());
if (!(interrupt = setjmp(m->onhalt))) {
for (;;) {
do {
if (!(action & FAILURE)) {
LoadInstruction(m);
if ((action & (FINISH | NEXT | CONTINUE)) &&
(bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) {
action &= ~(FINISH | NEXT | CONTINUE);
LOGF("BREAK %p", breakpoints.p[bp].addr);
break;
}
} else {
m->xedd = (struct XedDecodedInst *)m->icache[0];
@ -2642,6 +2651,9 @@ static void Tui(void) {
tick = 0;
Redraw();
}
if (dialog) {
PrintMessageBox(ttyout, dialog, tyn, txn);
}
if (action & FAILURE) {
LOGF("TUI FAILURE");
PrintMessageBox(ttyout, systemfailure, tyn, txn);
@ -2651,8 +2663,9 @@ static void Tui(void) {
LeaveScreen();
exit(1);
}
} else if (!IsExecuting() || (!(action & CONTINUE) && !(action & INT) &&
HasPendingKeyboard())) {
} else if (dialog || !IsExecuting() ||
(!(action & CONTINUE) && !(action & INT) &&
HasPendingKeyboard())) {
ReadKeyboard();
}
if (action & INT) {
@ -2661,13 +2674,11 @@ static void Tui(void) {
if (action & (CONTINUE | NEXT | FINISH)) {
action &= ~(CONTINUE | NEXT | FINISH);
} else {
tuimode = false;
action |= CONTINUE;
action |= EXIT;
break;
}
}
if (action & EXIT) {
LeaveScreen();
LOGF("TUI EXIT");
break;
}
@ -2723,7 +2734,7 @@ static void Tui(void) {
}
}
}
}
} while (tuimode);
} else {
if (OnHalt(interrupt)) {
goto KeepGoing;
@ -2794,42 +2805,37 @@ int Emulator(int argc, char *argv[]) {
int rc, fd;
codepath = argv[optind++];
m->fds.p = xcalloc((m->fds.n = 8), sizeof(struct MachineFd));
Restart:
action = 0;
LoadProgram(m, codepath, argv + optind, environ, elf);
AddHostFd(STDIN_FILENO);
AddHostFd(STDOUT_FILENO);
AddHostFd(STDERR_FILENO);
if (tuimode) {
ttyin = isatty(STDIN_FILENO) ? STDIN_FILENO : OpenDevTty();
ttyout = isatty(STDOUT_FILENO) ? STDOUT_FILENO : OpenDevTty();
} else {
ttyin = -1;
ttyout = -1;
}
if (ttyout != -1) {
atexit(TtyRestore1);
xsigaction(SIGWINCH, OnSigWinch, 0, 0, 0);
tyn = 24;
txn = 80;
GetTtySize(ttyout);
if (isatty(STDIN_FILENO)) m->fds.p[STDIN_FILENO].cb = &kMachineFdCbPty;
if (isatty(STDOUT_FILENO)) m->fds.p[STDOUT_FILENO].cb = &kMachineFdCbPty;
if (isatty(STDERR_FILENO)) m->fds.p[STDERR_FILENO].cb = &kMachineFdCbPty;
}
while (!(action & EXIT)) {
if (!tuimode) {
Exec();
do {
action = 0;
LoadProgram(m, codepath, argv + optind, environ, elf);
AddHostFd(0);
AddHostFd(1);
AddHostFd(2);
if (tuimode) {
ttyin = isatty(0) ? 0 : OpenDevTty();
ttyout = isatty(1) ? 1 : OpenDevTty();
} else {
Tui();
ttyin = -1;
ttyout = -1;
}
if (action & RESTART) {
goto Restart;
if (ttyout != -1) {
atexit(TtyRestore1);
xsigaction(SIGWINCH, OnSigWinch, 0, 0, 0);
tyn = 24;
txn = 80;
GetTtySize(ttyout);
if (isatty(0)) m->fds.p[0].cb = &kMachineFdCbPty;
if (isatty(1)) m->fds.p[1].cb = &kMachineFdCbPty;
if (isatty(2)) m->fds.p[2].cb = &kMachineFdCbPty;
}
}
if (tuimode) {
LeaveScreen();
}
do {
if (!tuimode) {
Exec();
} else {
Tui();
}
} while (!(action & (RESTART | EXIT)));
} while (action & RESTART);
if (printstats) {
fprintf(stderr, "taken: %,ld\n", taken);
fprintf(stderr, "ntaken: %,ld\n", ntaken);

View File

@ -18,6 +18,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/elf/elf.h"
#include "libc/log/check.h"
#include "libc/runtime/gc.h"
#include "libc/sysv/consts/map.h"
@ -28,13 +29,15 @@
void LoadDebugSymbols(struct Elf *elf) {
int fd;
size_t n;
void *elfmap;
struct stat st;
const char *path;
if (elf->ehdr) return;
if (elf->ehdr && GetElfSymbolTable(elf->ehdr, elf->size, &n) && n) return;
DCHECK_NOTNULL(elf->prog);
fprintf(stderr, "HI %s\n", elf->prog);
if ((fd = open(gc(xstrcat(elf->prog, ".dbg")), O_RDONLY)) != -1 ||
(fd = open(elf->prog, O_RDONLY))) {
(fd = open(elf->prog, O_RDONLY)) != -1) {
if (fstat(fd, &st) != -1 &&
(elfmap = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) !=
MAP_FAILED) {

View File

@ -203,5 +203,5 @@ void DisLoadElf(struct Dis *d, struct Elf *elf) {
DisLoadElfLoads(d, elf);
DisLoadElfSyms(d, elf);
DisSortSyms(d);
DisCanonizeSyms(d);
/* DisCanonizeSyms(d); */
}

54
tool/build/lib/lines.c Normal file
View File

@ -0,0 +1,54 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2021 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or 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 INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "tool/build/lib/lines.h"
struct Lines *NewLines(void) {
return calloc(1, sizeof(struct Lines));
}
void FreeLines(struct Lines *lines) {
size_t i;
for (i = 0; i < lines->n; ++i) {
free(lines->p[i]);
}
free(lines);
}
void AppendLine(struct Lines *lines, const char *s, size_t n) {
lines->p = realloc(lines->p, ++lines->n * sizeof(*lines->p));
lines->p[lines->n - 1] = strndup(s, n);
}
void AppendLines(struct Lines *lines, const char *s) {
const char *p;
for (;;) {
p = strchr(s, '\n');
if (p) {
AppendLine(lines, s, p - s);
s = p + 1;
} else {
if (*s) {
AppendLine(lines, s, -1);
}
break;
}
}
}

18
tool/build/lib/lines.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_LINES_H_
#define COSMOPOLITAN_TOOL_BUILD_LIB_LINES_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct Lines {
size_t n;
char **p;
};
struct Lines *NewLines(void);
void FreeLines(struct Lines *);
void AppendLine(struct Lines *, const char *, size_t);
void AppendLines(struct Lines *, const char *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_LINES_H_ */

View File

@ -140,7 +140,7 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars,
size_t i, mappedsize;
DCHECK_NOTNULL(prog);
elf->prog = prog;
if ((fd = open(prog, O_RDWR)) == -1 ||
if ((fd = open(prog, O_RDONLY)) == -1 ||
(fstat(fd, &st) == -1 || !st.st_size)) {
fputs(prog, stderr);
fputs(": not found\n", stderr);

View File

@ -17,30 +17,51 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/unicode/unicode.h"
#include "tool/build/lib/buffer.h"
#include "tool/build/lib/lines.h"
#include "tool/build/lib/panel.h"
static int GetWidthOfLongestLine(struct Lines *lines) {
int i, w, m;
for (m = i = 0; i < lines->n; ++i) {
w = strwidth(lines->p[i], 0);
m = MAX(m, w);
}
return m;
}
void PrintMessageBox(int fd, const char *msg, long tyn, long txn) {
char buf[1];
struct Buffer b;
int i, w, h, x, y;
h = 2 + 1 + 2;
w = 3 + strlen(msg) + 3;
struct Lines *lines;
lines = NewLines();
AppendLines(lines, msg);
h = 3 + lines->n + 3;
w = 4 + GetWidthOfLongestLine(lines) + 4;
x = lrint(txn / 2. - w / 2.);
y = lrint(tyn / 2. - h / 2.);
memset(&b, 0, sizeof(b));
AppendFmt(&b, "\e[%d;%dH", y++, x);
for (i = 0; i < w - 2; ++i) AppendStr(&b, "");
AppendStr(&b, "");
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, "");
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, msg);
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, "");
AppendFmt(&b, "\e[%d;%dH╚", y++, x);
for (i = 0; i < w - 2; ++i) AppendStr(&b, "");
AppendStr(&b, "");
AppendFmt(&b, "\e[%d;%dH", y++, x);
for (i = 0; i < w; ++i) AppendStr(&b, " ");
AppendFmt(&b, "\e[%d;%dH ╔", y++, x);
for (i = 0; i < w - 4; ++i) AppendStr(&b, "");
AppendStr(&b, "");
AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, "");
for (i = 0; i < lines->n; ++i) {
AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, lines->p[i]);
}
FreeLines(lines);
AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, "");
AppendFmt(&b, "\e[%d;%dH ╚", y++, x);
for (i = 0; i < w - 4; ++i) AppendStr(&b, "");
AppendStr(&b, "");
AppendFmt(&b, "\e[%d;%dH", y++, x);
for (i = 0; i < w; ++i) AppendStr(&b, " ");
CHECK_NE(-1, WriteBuffer(&b, fd));
free(b.p);
}