cosmopolitan/tool/build/blinkenlights.c

2795 lines
69 KiB
C
Raw Normal View History

/*-*- 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 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/scale/cdecimate2xuint8x8.h"
#include "dsp/tty/tty.h"
#include "libc/alg/arraylist2.internal.h"
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/termios.h"
#include "libc/conv/conv.h"
#include "libc/conv/itoa.h"
#include "libc/errno.h"
#include "libc/fmt/bing.internal.h"
#include "libc/fmt/fmt.h"
2020-10-27 10:39:46 +00:00
#include "libc/intrin/pcmpeqb.h"
#include "libc/intrin/pmovmskb.h"
#include "libc/log/check.h"
#include "libc/log/color.internal.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/thompike.h"
#include "libc/str/tpdecode.internal.h"
#include "libc/str/tpencode.internal.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
#include "libc/unicode/unicode.h"
#include "libc/x/x.h"
#include "third_party/gdtoa/gdtoa.h"
#include "third_party/getopt/getopt.h"
#include "tool/build/lib/address.h"
#include "tool/build/lib/breakpoint.h"
#include "tool/build/lib/case.h"
#include "tool/build/lib/cga.h"
#include "tool/build/lib/demangle.h"
#include "tool/build/lib/dis.h"
#include "tool/build/lib/endian.h"
#include "tool/build/lib/fds.h"
#include "tool/build/lib/flags.h"
#include "tool/build/lib/fpu.h"
#include "tool/build/lib/high.h"
#include "tool/build/lib/loader.h"
#include "tool/build/lib/machine.h"
#include "tool/build/lib/mda.h"
#include "tool/build/lib/memory.h"
#include "tool/build/lib/modrm.h"
#include "tool/build/lib/panel.h"
#include "tool/build/lib/pml4t.h"
#include "tool/build/lib/pty.h"
#include "tool/build/lib/stats.h"
#include "tool/build/lib/syscall.h"
#include "tool/build/lib/throw.h"
#include "tool/build/lib/xmmtype.h"
#define USAGE \
" [-?HhrRstv] [ROM] [ARGS...]\n\
\n\
DESCRIPTION\n\
\n\
Emulates x86 Linux Programs w/ Dense Machine State Visualization\n\
Please keep still and only watchen astaunished das blinkenlights\n\
\n\
FLAGS\n\
\n\
-h help\n\
-z zoom\n\
-v verbosity\n\
-r real mode\n\
-s statistics\n\
-H disable highlight\n\
-t tui debugger mode\n\
-R reactive tui mode\n\
-b ADDR push a breakpoint\n\
-L PATH log file location\n\
\n\
ARGUMENTS\n\
\n\
ROM files can be ELF or a flat αcτµαlly pδrταblε εxεcµταblε.\n\
It should use x86_64 in accordance with the System Five ABI.\n\
The SYSCALL ABI is defined as it is written in Linux Kernel.\n\
\n\
FEATURES\n\
\n\
8086, 8087, i386, x86_64, SSE3, SSSE3, POPCNT, MDA, CGA, TTY\n\
\n"
#define MAXZOOM 16
#define DUMPWIDTH 64
#define DISPWIDTH 80
#define WHEELDELTA 1
#define RESTART 0x001
#define REDRAW 0x002
#define CONTINUE 0x004
#define STEP 0x008
#define NEXT 0x010
#define FINISH 0x020
#define FAILURE 0x040
#define WINCHED 0x080
#define INT 0x100
#define QUIT 0x200
#define EXIT 0x400
#define ALARM 0x800
#define kXmmDecimal 0
#define kXmmHex 1
#define kXmmChar 2
#define kMouseLeftDown 0
#define kMouseMiddleDown 1
#define kMouseRightDown 2
#define kMouseLeftUp 4
#define kMouseMiddleUp 5
#define kMouseRightUp 6
#define kMouseLeftDrag 32
#define kMouseMiddleDrag 33
#define kMouseRightDrag 34
#define kMouseWheelUp 64
#define kMouseWheelDown 65
#define kMouseCtrlWheelUp 80
#define kMouseCtrlWheelDown 81
2020-10-27 10:39:46 +00:00
#define CTRL(C) ((C) ^ 0100)
struct MemoryView {
int64_t start;
int zoom;
};
struct MachineState {
uint64_t ip;
uint8_t cs[8];
uint8_t ss[8];
uint8_t es[8];
uint8_t ds[8];
uint8_t fs[8];
uint8_t gs[8];
uint8_t reg[16][8];
uint8_t xmm[16][16];
struct MachineFpu fpu;
struct MachineSse sse;
2020-10-27 10:39:46 +00:00
struct MachineMemstat memstat;
};
struct Panels {
union {
struct {
struct Panel disassembly;
struct Panel breakpointshr;
struct Panel breakpoints;
struct Panel mapshr;
struct Panel maps;
struct Panel frameshr;
struct Panel frames;
struct Panel displayhr;
struct Panel display;
struct Panel registers;
struct Panel ssehr;
struct Panel sse;
struct Panel codehr;
struct Panel code;
struct Panel readhr;
struct Panel readdata;
struct Panel writehr;
struct Panel writedata;
struct Panel stackhr;
struct Panel stack;
2020-10-27 10:39:46 +00:00
struct Panel status;
};
2020-10-27 10:39:46 +00:00
struct Panel p[21];
};
};
static const signed char kThePerfectKernel[8] = {-1, -3, 3, 17, 17, 3, -3, -1};
static const char kRegisterNames[16][4] = {
"RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI",
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
};
static bool react;
static bool tuimode;
static bool alarmed;
static bool colorize;
static bool mousemode;
static bool printstats;
static bool showhighsse;
static int tyn;
static int txn;
static int tick;
static int speed;
static int vidya;
static int ttyin;
static int focus;
static int ttyout;
static int opline;
static int action;
static int xmmdisp;
static int exitcode;
2020-10-27 10:39:46 +00:00
static long ips;
static long rombase;
static long codesize;
static int64_t opstart;
static int64_t mapsstart;
static int64_t framesstart;
static int64_t breakpointsstart;
2020-10-27 10:39:46 +00:00
static uint64_t last_opcount;
static char *codepath;
static void *onbusted;
static char *statusmessage;
2020-10-27 10:39:46 +00:00
static struct Pty *pty;
static struct Machine *m;
static struct Panels pan;
static struct MemoryView codeview;
static struct MemoryView readview;
static struct MemoryView writeview;
static struct MemoryView stackview;
static struct MachineState laststate;
static struct Breakpoints breakpoints;
2020-10-27 10:39:46 +00:00
static struct MachineMemstat lastmemstat;
static struct XmmType xmmtype;
static struct Elf elf[1];
static struct Dis dis[1];
static long double last_seconds;
static long double statusexpires;
static struct termios oldterm;
static char logpath[PATH_MAX];
static char systemfailure[128];
static struct sigaction oldsig[4];
static void SetupDraw(void);
static void Redraw(void);
static char *FormatDouble(char buf[32], long double x) {
g_xfmt_p(buf, &x, 15, 32, 0);
return buf;
}
static int64_t SignExtend(uint64_t x, char b) {
char k;
assert(1 <= b && b <= 64);
k = 64 - b;
return (int64_t)(x << k) >> k;
}
static void SetCarry(bool cf) {
m->flags = SetFlag(m->flags, FLAGS_CF, cf);
}
static bool IsCall(void) {
return (m->xedd->op.dispatch == 0x0E8 ||
(m->xedd->op.dispatch == 0x0FF && m->xedd->op.reg == 2));
}
static bool IsDebugBreak(void) {
return m->xedd->op.map == XED_ILD_MAP0 && m->xedd->op.opcode == 0xCC;
}
static bool IsRet(void) {
switch (m->xedd->op.dispatch) {
case 0x0C2:
case 0x0C3:
case 0x0CA:
case 0x0CB:
case 0x0CF:
return true;
default:
return false;
}
}
static int GetXmmTypeCellCount(int r) {
switch (xmmtype.type[r]) {
case kXmmIntegral:
return 16 / xmmtype.size[r];
case kXmmFloat:
return 4;
case kXmmDouble:
return 2;
default:
unreachable;
}
}
static uint8_t CycleXmmType(uint8_t t) {
switch (t) {
default:
case kXmmIntegral:
return kXmmFloat;
case kXmmFloat:
return kXmmDouble;
case kXmmDouble:
return kXmmIntegral;
}
}
static uint8_t CycleXmmDisp(uint8_t t) {
switch (t) {
default:
case kXmmDecimal:
return kXmmHex;
case kXmmHex:
return kXmmChar;
case kXmmChar:
return kXmmDecimal;
}
}
static uint8_t CycleXmmSize(uint8_t w) {
switch (w) {
default:
case 1:
return 2;
case 2:
return 4;
case 4:
return 8;
case 8:
return 1;
}
}
static int GetPointerWidth(void) {
return 2 << (m->mode & 3);
}
static int64_t GetIp(void) {
switch (GetPointerWidth()) {
default:
case 8:
return m->ip;
case 4:
return Read64(m->cs) + (m->ip & 0xffff);
case 2:
return Read64(m->cs) + (m->ip & 0xffff);
}
}
static int64_t GetSp(void) {
switch (GetPointerWidth()) {
default:
case 8:
return Read64(m->sp);
case 4:
return Read64(m->ss) + Read32(m->sp);
case 2:
return Read64(m->ss) + Read16(m->sp);
}
}
2020-10-27 10:39:46 +00:00
static int64_t ReadWord(uint8_t *p) {
switch (GetPointerWidth()) {
default:
case 8:
return Read64(p);
case 4:
return Read32(p);
case 2:
return Read16(p);
}
}
static void CopyMachineState(struct MachineState *ms) {
ms->ip = m->ip;
memcpy(ms->cs, m->cs, sizeof(m->cs));
memcpy(ms->ss, m->ss, sizeof(m->ss));
memcpy(ms->es, m->es, sizeof(m->es));
memcpy(ms->ds, m->ds, sizeof(m->ds));
memcpy(ms->fs, m->fs, sizeof(m->fs));
memcpy(ms->gs, m->gs, sizeof(m->gs));
memcpy(ms->reg, m->reg, sizeof(m->reg));
memcpy(ms->xmm, m->xmm, sizeof(m->xmm));
memcpy(&ms->fpu, &m->fpu, sizeof(m->fpu));
memcpy(&ms->sse, &m->sse, sizeof(m->sse));
}
static void OnSigBusted(void) {
CHECK(onbusted);
longjmp(onbusted, 1);
}
static int VirtualBing(int64_t v) {
int rc;
uint8_t *p;
jmp_buf busted;
onbusted = busted;
if ((p = FindReal(m, v))) {
if (!setjmp(busted)) {
rc = bing(p[0], 0);
} else {
rc = u'';
}
} else {
rc = u'';
}
onbusted = NULL;
return rc;
}
static void ScrollOp(struct Panel *p, long op) {
long n;
opline = op;
if ((n = p->bottom - p->top) > 1) {
if (!(opstart + 1 <= op && op < opstart + n)) {
opstart = MIN(MAX(0, op - n / 8), MAX(0, dis->ops.i - n));
}
}
}
static int TtyWriteString(const char *s) {
return write(ttyout, s, strlen(s));
}
static void OnFeed(void) {
TtyWriteString("\e[K\e[2J");
}
static void HideCursor(void) {
TtyWriteString("\e[?25l");
}
static void ShowCursor(void) {
TtyWriteString("\e[?25h");
}
static void EnableMouseTracking(void) {
mousemode = true;
TtyWriteString("\e[?1000;1002;1015;1006h");
}
static void DisableMouseTracking(void) {
mousemode = false;
TtyWriteString("\e[?1000;1002;1015;1006l");
}
static void ToggleMouseTracking(void) {
if (mousemode) {
DisableMouseTracking();
} else {
EnableMouseTracking();
}
}
static void LeaveScreen(void) {
TtyWriteString(gc(xasprintf("\e[%d;%dH\e[S\r\n", tyn, txn)));
}
static void GetTtySize(int fd) {
struct winsize wsize;
wsize.ws_row = tyn;
wsize.ws_col = txn;
getttysize(fd, &wsize);
tyn = wsize.ws_row;
txn = wsize.ws_col;
}
static void TuiRejuvinate(void) {
struct termios term;
DEBUGF("TuiRejuvinate");
GetTtySize(ttyout);
HideCursor();
memcpy(&term, &oldterm, sizeof(term));
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 1;
term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON);
term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL);
term.c_cflag &= ~(CSIZE | PARENB);
term.c_cflag |= CS8;
term.c_iflag |= IUTF8;
CHECK_NE(-1, ioctl(ttyout, TCSETS, &term));
xsigaction(SIGBUS, OnSigBusted, SA_NODEFER, 0, NULL);
EnableMouseTracking();
}
static void OnQ(void) {
LOGF("OnQ");
if (action & FAILURE) exit(1);
action |= INT;
breakpoints.i = 0;
}
static void OnV(void) {
vidya = !vidya;
}
static void OnSigWinch(void) {
action |= WINCHED;
}
static void OnSigInt(void) {
if (tuimode) {
action |= INT;
} else {
action |= EXIT;
}
}
static void OnSigAlarm(void) {
action |= ALARM;
}
static void OnSigCont(void) {
TuiRejuvinate();
Redraw();
}
static void TtyRestore1(void) {
DEBUGF("TtyRestore1");
ShowCursor();
TtyWriteString("\e[0m");
}
static void TtyRestore2(void) {
DEBUGF("TtyRestore2");
ioctl(ttyout, TCSETS, &oldterm);
DisableMouseTracking();
}
static void TuiCleanup(void) {
sigaction(SIGCONT, oldsig + 2, NULL);
TtyRestore1();
DisableMouseTracking();
tuimode = false;
}
static void ResolveBreakpoints(void) {
long i, sym;
for (i = 0; i < breakpoints.i; ++i) {
if (breakpoints.p[i].symbol && !breakpoints.p[i].addr) {
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);
exit(1);
}
}
}
}
static void BreakAtNextInstruction(void) {
struct Breakpoint b;
memset(&b, 0, sizeof(b));
b.addr = GetIp() + m->xedd->length;
b.oneshot = true;
PushBreakpoint(&breakpoints, &b);
}
static void LoadSyms(void) {
LoadDebugSymbols(elf);
DisLoadElf(dis, elf);
}
static int DrainInput(int fd) {
char buf[32];
struct pollfd fds[1];
for (;;) {
fds[0].fd = fd;
fds[0].events = POLLIN;
if (poll(fds, ARRAYLEN(fds), 0) == -1) return -1;
if (!(fds[0].revents & POLLIN)) break;
if (read(fd, buf, sizeof(buf)) == -1) return -1;
}
return 0;
}
static int ReadCursorPosition(int *out_y, int *out_x) {
int y, x;
char *p, buf[32];
if (readansi(ttyin, buf, sizeof(buf)) == 1) return -1;
p = buf;
if (*p == '\e') ++p;
if (*p == '[') ++p;
y = strtol(p, &p, 10);
if (*p == ';') ++p;
x = strtol(p, &p, 10);
if (*p != 'R') return ebadmsg();
if (out_y) *out_y = MAX(1, y) - 1;
if (out_x) *out_x = MAX(1, x) - 1;
return 0;
}
static int GetCursorPosition(int *out_y, int *out_x) {
TtyWriteString("\e[6n");
return ReadCursorPosition(out_y, out_x);
}
static int GetTerminalDimensions(int *out_y, int *out_x) {
TtyWriteString("\e7\e[9979;9979H\e[6n\e8");
return ReadCursorPosition(out_y, out_x);
}
void CommonSetup(void) {
static bool once;
if (!once) {
if (tuimode || breakpoints.i) {
LoadSyms();
ResolveBreakpoints();
}
once = true;
}
}
void TuiSetup(void) {
int y, x;
bool report;
static bool once;
report = false;
if (!once) {
LOGF("loaded program %s\n%s", codepath, gc(FormatPml4t(m)));
CommonSetup();
ioctl(ttyout, TCGETS, &oldterm);
xsigaction(SIGINT, OnSigInt, 0, 0, oldsig + 3);
atexit(TtyRestore2);
once = true;
report = true;
}
2020-10-27 10:39:46 +00:00
setitimer(ITIMER_REAL, &((struct itimerval){0}), NULL);
xsigaction(SIGCONT, OnSigCont, SA_RESTART | SA_NODEFER, 0, oldsig + 2);
CopyMachineState(&laststate);
TuiRejuvinate();
if (report) {
DrainInput(ttyin);
y = 0;
if (GetCursorPosition(&y, NULL) != -1) {
TtyWriteString(gc(xasprintf("\e[%dS", y)));
}
}
}
static void ExecSetup(void) {
CommonSetup();
2020-10-27 10:39:46 +00:00
setitimer(ITIMER_REAL,
&((struct itimerval){{0, 1. / 60 * 1e6}, {0, 1. / 60 * 1e6}}),
NULL);
}
static void AppendPanel(struct Panel *p, long line, const char *s) {
if (0 <= line && line < p->bottom - p->top) {
AppendStr(&p->lines[line], s);
}
}
static bool IsXmmNonZero(long start, long end) {
2020-10-27 10:39:46 +00:00
long i;
uint8_t v1[16], vz[16];
for (i = start; i < end; ++i) {
2020-10-27 10:39:46 +00:00
memset(vz, 0, 16);
memcpy(v1, m->xmm[i], 16);
pcmpeqb(v1, v1, vz);
if (pmovmskb(v1) != 0xffff) {
return true;
}
}
return false;
}
static bool IsSegNonZero(void) {
unsigned i;
for (i = 0; i < 6; ++i) {
if (Read64(GetSegment(m, 0, i))) {
return true;
}
}
return false;
}
static int PickNumberOfXmmRegistersToShow(void) {
if (IsXmmNonZero(0, 8) || IsXmmNonZero(8, 16)) {
if (showhighsse || IsXmmNonZero(8, 16)) {
showhighsse = true;
return 16;
} else {
return 8;
}
} else {
showhighsse = false;
return 0;
}
}
2020-10-27 10:39:46 +00:00
void SetupDraw(void) {
int i, j, n, a, b, yn, cpuy, ssey, dx[2], c2y[3], c3y[5];
cpuy = 9;
if (IsSegNonZero()) cpuy += 2;
ssey = PickNumberOfXmmRegistersToShow();
if (ssey) ++ssey;
a = 12 + 1 + DUMPWIDTH;
b = DISPWIDTH + 1;
dx[1] = txn >= a + b ? txn - a : txn;
dx[0] = txn >= a + b + b ? txn - a - b : dx[1];
2020-10-27 10:39:46 +00:00
yn = tyn - 1;
a = 1 / 8. * yn;
b = 3 / 8. * yn;
2020-09-03 12:44:37 +00:00
c2y[0] = a * .7;
c2y[1] = a * 2;
c2y[2] = a * 2 + b;
2020-10-27 10:39:46 +00:00
if (yn - c2y[2] > 26) {
c2y[1] -= yn - c2y[2] - 26;
c2y[2] = yn - 26;
}
2020-10-27 10:39:46 +00:00
if (yn - c2y[2] < 26) {
c2y[2] = yn - 26;
}
2020-10-27 10:39:46 +00:00
a = (yn - (cpuy + ssey) - 3) / 4;
c3y[0] = cpuy;
c3y[1] = cpuy + ssey;
c3y[2] = cpuy + ssey + 1 + 1 + a * 1;
c3y[3] = cpuy + ssey + 1 + 1 + a * 2 + 1;
c3y[4] = cpuy + ssey + 1 + 1 + 1 + a * 3 + 1;
/* COLUMN #1: DISASSEMBLY */
pan.disassembly.top = 0;
pan.disassembly.left = 0;
2020-10-27 10:39:46 +00:00
pan.disassembly.bottom = yn;
pan.disassembly.right = dx[0] - 1;
/* COLUMN #2: BREAKPOINTS, MEMORY MAPS, BACKTRACE, DISPLAY */
pan.breakpointshr.top = 0;
pan.breakpointshr.left = dx[0];
pan.breakpointshr.bottom = 1;
pan.breakpointshr.right = dx[1] - 1;
pan.breakpoints.top = 1;
pan.breakpoints.left = dx[0];
pan.breakpoints.bottom = c2y[0];
pan.breakpoints.right = dx[1] - 1;
pan.mapshr.top = c2y[0];
pan.mapshr.left = dx[0];
pan.mapshr.bottom = c2y[0] + 1;
pan.mapshr.right = dx[1] - 1;
pan.maps.top = c2y[0] + 1;
pan.maps.left = dx[0];
pan.maps.bottom = c2y[1];
pan.maps.right = dx[1] - 1;
pan.frameshr.top = c2y[1];
pan.frameshr.left = dx[0];
pan.frameshr.bottom = c2y[1] + 1;
pan.frameshr.right = dx[1] - 1;
pan.frames.top = c2y[1] + 1;
pan.frames.left = dx[0];
pan.frames.bottom = c2y[2];
pan.frames.right = dx[1] - 1;
pan.displayhr.top = c2y[2];
pan.displayhr.left = dx[0];
pan.displayhr.bottom = c2y[2] + 1;
pan.displayhr.right = dx[1] - 1;
pan.display.top = c2y[2] + 1;
pan.display.left = dx[0];
2020-10-27 10:39:46 +00:00
pan.display.bottom = yn;
pan.display.right = dx[1] - 1;
/* COLUMN #3: REGISTERS, VECTORS, CODE, MEMORY READS, MEMORY WRITES, STACK */
pan.registers.top = 0;
pan.registers.left = dx[1];
pan.registers.bottom = c3y[0];
pan.registers.right = txn;
pan.ssehr.top = c3y[0];
pan.ssehr.left = dx[1];
pan.ssehr.bottom = c3y[0] + (ssey ? 1 : 0);
pan.ssehr.right = txn;
pan.sse.top = c3y[0] + (ssey ? 1 : 0);
pan.sse.left = dx[1];
pan.sse.bottom = c3y[1];
pan.sse.right = txn;
pan.codehr.top = c3y[1];
pan.codehr.left = dx[1];
pan.codehr.bottom = c3y[1] + 1;
pan.codehr.right = txn;
pan.code.top = c3y[1] + 1;
pan.code.left = dx[1];
pan.code.bottom = c3y[2];
pan.code.right = txn;
pan.readhr.top = c3y[2];
pan.readhr.left = dx[1];
pan.readhr.bottom = c3y[2] + 1;
pan.readhr.right = txn;
pan.readdata.top = c3y[2] + 1;
pan.readdata.left = dx[1];
pan.readdata.bottom = c3y[3];
pan.readdata.right = txn;
pan.writehr.top = c3y[3];
pan.writehr.left = dx[1];
pan.writehr.bottom = c3y[3] + 1;
pan.writehr.right = txn;
pan.writedata.top = c3y[3] + 1;
pan.writedata.left = dx[1];
pan.writedata.bottom = c3y[4];
pan.writedata.right = txn;
pan.stackhr.top = c3y[4];
pan.stackhr.left = dx[1];
pan.stackhr.bottom = c3y[4] + 1;
pan.stackhr.right = txn;
pan.stack.top = c3y[4] + 1;
pan.stack.left = dx[1];
2020-10-27 10:39:46 +00:00
pan.stack.bottom = yn;
pan.stack.right = txn;
2020-10-27 10:39:46 +00:00
pan.status.top = yn;
pan.status.left = 0;
pan.status.bottom = yn + 1;
pan.status.right = txn;
for (i = 0; i < ARRAYLEN(pan.p); ++i) {
2020-10-27 10:39:46 +00:00
if (pan.p[i].left > pan.p[i].right) {
pan.p[i].left = pan.p[i].right = 0;
}
if (pan.p[i].top > pan.p[i].bottom) {
pan.p[i].top = pan.p[i].bottom = 0;
}
n = pan.p[i].bottom - pan.p[i].top;
if (n == pan.p[i].n) {
for (j = 0; j < n; ++j) {
pan.p[i].lines[j].i = 0;
}
} else {
for (j = 0; j < pan.p[i].n; ++j) {
free(pan.p[i].lines[j].p);
}
free(pan.p[i].lines);
pan.p[i].lines = xcalloc(n, sizeof(struct Buffer));
pan.p[i].n = n;
}
}
2020-10-27 10:39:46 +00:00
PtyResize(pty, pan.display.bottom - pan.display.top,
pan.display.right - pan.display.left);
}
static long Disassemble(void) {
long lines, current;
lines = pan.disassembly.bottom - pan.disassembly.top * 2;
if (Dis(dis, m, GetIp(), m->ip, lines) != -1) {
return DisFind(dis, GetIp());
} else {
return -1;
}
}
static long GetDisIndex(void) {
long i;
if ((i = DisFind(dis, GetIp())) == -1) {
i = Disassemble();
}
while (i + 1 < dis->ops.i && !dis->ops.p[i].size) ++i;
return i;
}
static void DrawDisassembly(struct Panel *p) {
long i, j;
for (i = 0; i < p->bottom - p->top; ++i) {
j = opstart + i;
if (0 <= j && j < dis->ops.i) {
if (j == opline) AppendPanel(p, i, "\e[7m");
AppendPanel(p, i, DisGetLine(dis, m, j));
if (j == opline) AppendPanel(p, i, "\e[27m");
}
}
}
static void DrawHr(struct Panel *p, const char *s) {
long i, wp, ws, wl, wr;
if (p->bottom - p->top < 1) return;
wp = p->right - p->left;
ws = strwidth(s);
wl = wp / 4 - ws / 2;
wr = wp - (wl + ws);
for (i = 0; i < wl; ++i) AppendWide(&p->lines[0], u'');
AppendStr(&p->lines[0], s);
for (i = 0; i < wr; ++i) AppendWide(&p->lines[0], u'');
AppendStr(&p->lines[0], "\e[0m");
}
static void DrawTerminalHr(struct Panel *p) {
long i;
if (p->bottom == p->top) return;
2020-10-27 10:39:46 +00:00
if (pty->conf & kPtyBell) {
if (!alarmed) {
alarmed = true;
setitimer(ITIMER_REAL, &((struct itimerval){{0, 0}, {0, 800000}}), NULL);
}
AppendStr(&p->lines[0], "\e[1m");
}
AppendFmt(&p->lines[0], "──────────TELETYPEWRITER──%s──%s──%s──%s",
(pty->conf & kPtyLed1) ? "\e[1;31m◎\e[0m" : "",
(pty->conf & kPtyLed2) ? "\e[1;32m◎\e[0m" : "",
(pty->conf & kPtyLed3) ? "\e[1;33m◎\e[0m" : "",
(pty->conf & kPtyLed4) ? "\e[1;34m◎\e[0m" : "");
for (i = 36; i < p->right - p->left; ++i) {
AppendWide(&p->lines[0], u'');
}
}
static void DrawTerminal(struct Panel *p) {
long y, yn;
if (p->top == p->bottom) return;
for (yn = MIN(pty->yn, p->bottom - p->top), y = 0; y < yn; ++y) {
2020-10-27 10:39:46 +00:00
PtyAppendLine(pty, p->lines + y, y);
AppendStr(p->lines + y, "\e[0m");
}
}
static void DrawDisplay(struct Panel *p) {
switch (vidya) {
case 7:
DrawHr(&pan.displayhr, "MONOCHROME DISPLAY ADAPTER");
if (0xb0000 + 25 * 80 * 2 > m->real.n) return;
DrawMda(p, (void *)(m->real.p + 0xb0000));
break;
case 3:
DrawHr(&pan.displayhr, "COLOR GRAPHICS ADAPTER");
if (0xb8000 + 25 * 80 * 2 > m->real.n) return;
DrawCga(p, (void *)(m->real.p + 0xb8000));
break;
default:
DrawTerminalHr(&pan.displayhr);
DrawTerminal(p);
break;
}
}
static void DrawFlag(struct Panel *p, long i, char name, bool value) {
char str[3] = " ";
if (value) str[1] = name;
AppendPanel(p, i, str);
}
static void DrawRegister(struct Panel *p, long i, long r) {
char buf[32];
uint64_t value, previous;
value = Read64(m->reg[r]);
previous = Read64(laststate.reg[r]);
if (value != previous) AppendPanel(p, i, "\e[7m");
snprintf(buf, sizeof(buf), "%-3s", kRegisterNames[r]);
AppendPanel(p, i, buf);
AppendPanel(p, i, " ");
snprintf(buf, sizeof(buf), "0x%016lx", value);
AppendPanel(p, i, buf);
if (value != previous) AppendPanel(p, i, "\e[27m");
AppendPanel(p, i, " ");
}
static void DrawSegment(struct Panel *p, long i, const uint8_t seg[8],
const uint8_t last[8], const char *name) {
char buf[32];
uint64_t value, previous;
value = Read64(seg);
previous = Read64(last);
if (value != previous) AppendPanel(p, i, "\e[7m");
snprintf(buf, sizeof(buf), "%-3s", name);
AppendPanel(p, i, buf);
AppendPanel(p, i, " ");
snprintf(buf, sizeof(buf), "0x%016lx", value);
AppendPanel(p, i, buf);
if (value != previous) AppendPanel(p, i, "\e[27m");
AppendPanel(p, i, " ");
}
static void DrawSt(struct Panel *p, long i, long r) {
char buf[32];
long double value;
bool isempty, changed;
isempty = FpuGetTag(m, r) == kFpuTagEmpty;
if (isempty) AppendPanel(p, i, "\e[38;5;241m");
value = m->fpu.st[(r + m->fpu.sp) & 0b111];
changed = value != laststate.fpu.st[(r + m->fpu.sp) & 0b111];
if (!isempty && changed) AppendPanel(p, i, "\e[7m");
snprintf(buf, sizeof(buf), "ST%d ", r);
AppendPanel(p, i, buf);
AppendPanel(p, i, FormatDouble(buf, value));
if (changed) AppendPanel(p, i, "\e[27m");
AppendPanel(p, i, " ");
if (isempty) AppendPanel(p, i, "\e[39m");
}
static void DrawCpu(struct Panel *p) {
char buf[48];
2020-10-27 10:39:46 +00:00
if (p->top == p->bottom) return;
DrawRegister(p, 0, 7), DrawRegister(p, 0, 0), DrawSt(p, 0, 0);
DrawRegister(p, 1, 6), DrawRegister(p, 1, 3), DrawSt(p, 1, 1);
DrawRegister(p, 2, 2), DrawRegister(p, 2, 5), DrawSt(p, 2, 2);
DrawRegister(p, 3, 1), DrawRegister(p, 3, 4), DrawSt(p, 3, 3);
DrawRegister(p, 4, 8), DrawRegister(p, 4, 12), DrawSt(p, 4, 4);
DrawRegister(p, 5, 9), DrawRegister(p, 5, 13), DrawSt(p, 5, 5);
DrawRegister(p, 6, 10), DrawRegister(p, 6, 14), DrawSt(p, 6, 6);
DrawRegister(p, 7, 11), DrawRegister(p, 7, 15), DrawSt(p, 7, 7);
snprintf(buf, sizeof(buf), "RIP 0x%016x FLG", m->ip);
AppendPanel(p, 8, buf);
DrawFlag(p, 8, 'C', GetFlag(m->flags, FLAGS_CF));
DrawFlag(p, 8, 'P', GetFlag(m->flags, FLAGS_PF));
DrawFlag(p, 8, 'A', GetFlag(m->flags, FLAGS_AF));
DrawFlag(p, 8, 'Z', GetFlag(m->flags, FLAGS_ZF));
DrawFlag(p, 8, 'S', GetFlag(m->flags, FLAGS_SF));
DrawFlag(p, 8, 'I', GetFlag(m->flags, FLAGS_IF));
DrawFlag(p, 8, 'D', GetFlag(m->flags, FLAGS_DF));
DrawFlag(p, 8, 'O', GetFlag(m->flags, FLAGS_OF));
AppendPanel(p, 8, " ");
if (m->fpu.ie) AppendPanel(p, 8, " IE");
if (m->fpu.de) AppendPanel(p, 8, " DE");
if (m->fpu.ze) AppendPanel(p, 8, " ZE");
if (m->fpu.oe) AppendPanel(p, 8, " OE");
if (m->fpu.ue) AppendPanel(p, 8, " UE");
if (m->fpu.pe) AppendPanel(p, 8, " PE");
if (m->fpu.sf) AppendPanel(p, 8, " SF");
if (m->fpu.es) AppendPanel(p, 8, " ES");
if (m->fpu.c0) AppendPanel(p, 8, " C0");
if (m->fpu.c1) AppendPanel(p, 8, " C1");
if (m->fpu.c2) AppendPanel(p, 8, " C2");
if (m->fpu.bf) AppendPanel(p, 8, " BF");
DrawSegment(p, 9, m->fs, laststate.fs, "FS");
DrawSegment(p, 9, m->ds, laststate.ds, "DS");
DrawSegment(p, 9, m->cs, laststate.cs, "CS");
DrawSegment(p, 10, m->gs, laststate.gs, "GS");
DrawSegment(p, 10, m->es, laststate.es, "ES");
DrawSegment(p, 10, m->ss, laststate.ss, "SS");
}
static void DrawXmm(struct Panel *p, long i, long r) {
float f;
double d;
long j, k, n;
bool changed;
char buf[32];
uint8_t xmm[16];
uint64_t ival, itmp;
int cells, left, cellwidth, panwidth;
memcpy(xmm, m->xmm[r], sizeof(xmm));
changed = memcmp(xmm, laststate.xmm[r], sizeof(xmm)) != 0;
if (changed) AppendPanel(p, i, "\e[7m");
left = sprintf(buf, "XMM%-2d", r);
AppendPanel(p, i, buf);
cells = GetXmmTypeCellCount(r);
panwidth = p->right - p->left;
cellwidth = MIN(MAX(0, (panwidth - left) / cells - 1), sizeof(buf) - 1);
for (j = 0; j < cells; ++j) {
AppendPanel(p, i, " ");
switch (xmmtype.type[r]) {
case kXmmFloat:
memcpy(&f, xmm + j * sizeof(f), sizeof(f));
FormatDouble(buf, f);
break;
case kXmmDouble:
memcpy(&d, xmm + j * sizeof(d), sizeof(d));
FormatDouble(buf, d);
break;
case kXmmIntegral:
ival = 0;
for (k = 0; k < xmmtype.size[r]; ++k) {
itmp = xmm[j * xmmtype.size[r] + k] & 0xff;
itmp <<= k * 8;
ival |= itmp;
}
if (xmmdisp == kXmmHex || xmmdisp == kXmmChar) {
if (xmmdisp == kXmmChar && iswalnum(ival)) {
sprintf(buf, "%lc", ival);
} else {
uint64toarray_fixed16(ival, buf, xmmtype.size[r] * 8);
}
} else {
int64toarray_radix10(SignExtend(ival, xmmtype.size[r] * 8), buf);
}
break;
default:
unreachable;
}
buf[cellwidth] = '\0';
AppendPanel(p, i, buf);
n = cellwidth - strlen(buf);
for (k = 0; k < n; ++k) {
AppendPanel(p, i, " ");
}
}
if (changed) AppendPanel(p, i, "\e[27m");
}
static void DrawSse(struct Panel *p) {
long i, n;
n = p->bottom - p->top;
if (n > 0) {
for (i = 0; i < MIN(16, n); ++i) {
DrawXmm(p, i, i);
}
}
}
static void ScrollMemoryView(struct Panel *p, struct MemoryView *v, int64_t a) {
long i, n;
n = p->bottom - p->top;
i = a / (DUMPWIDTH * (1ull << v->zoom));
if (!(v->start <= i && i < v->start + n)) {
v->start = i;
}
}
static void ZoomMemoryView(struct MemoryView *v, long y, long x, int dy) {
long a, b, i, s;
s = v->start;
a = v->zoom;
b = MIN(MAXZOOM, MAX(0, a + dy));
i = y * DUMPWIDTH - x;
s *= DUMPWIDTH * (1L << a);
s += i * (1L << a) - i * (1L << b);
s /= DUMPWIDTH * (1L << b);
v->zoom = b;
v->start = s;
}
static void ScrollMemoryViews(void) {
ScrollMemoryView(&pan.code, &codeview, GetIp());
ScrollMemoryView(&pan.readdata, &readview, m->readaddr);
ScrollMemoryView(&pan.writedata, &writeview, m->writeaddr);
ScrollMemoryView(&pan.stack, &stackview, GetSp());
}
static void ZoomMemoryViews(struct Panel *p, int y, int x, int dy) {
if (p == &pan.code) {
ZoomMemoryView(&codeview, y, x, dy);
} else if (p == &pan.readdata) {
ZoomMemoryView(&readview, y, x, dy);
} else if (p == &pan.writedata) {
ZoomMemoryView(&writeview, y, x, dy);
} else if (p == &pan.stack) {
ZoomMemoryView(&stackview, y, x, dy);
}
}
static void DrawMemoryZoomed(struct Panel *p, struct MemoryView *view,
long histart, long hiend) {
bool high, changed;
uint8_t *canvas, *chunk, *invalid;
int64_t a, b, c, d, n, i, j, k, size;
struct ContiguousMemoryRanges ranges;
a = view->start * DUMPWIDTH * (1ull << view->zoom);
b = (view->start + (p->bottom - p->top)) * DUMPWIDTH * (1ull << view->zoom);
size = (p->bottom - p->top) * DUMPWIDTH;
canvas = xcalloc(1, size);
invalid = xcalloc(1, size);
memset(&ranges, 0, sizeof(ranges));
FindContiguousMemoryRanges(m, &ranges);
for (k = i = 0; i < ranges.i; ++i) {
if ((a >= ranges.p[i].a && a < ranges.p[i].b) ||
(b >= ranges.p[i].a && b < ranges.p[i].b) ||
(a < ranges.p[i].a && b >= ranges.p[i].b)) {
c = MAX(a, ranges.p[i].a);
d = MIN(b, ranges.p[i].b);
n = ROUNDUP(ROUNDUP(d - c, 16), 1ull << view->zoom);
chunk = xmalloc(n);
VirtualSend(m, chunk, c, d - c);
memset(chunk + (d - c), 0, n - (d - c));
for (j = 0; j < view->zoom; ++j) {
cDecimate2xUint8x8(ROUNDUP(n, 16), chunk, kThePerfectKernel);
n >>= 1;
}
j = (c - a) / (1ull << view->zoom);
memset(invalid + k, -1, j - k);
memcpy(canvas + j, chunk, MIN(n, size - j));
k = j + MIN(n, size - j);
free(chunk);
}
}
memset(invalid + k, -1, size - k);
free(ranges.p);
high = false;
for (c = i = 0; i < p->bottom - p->top; ++i) {
AppendFmt(&p->lines[i], "%p ",
(view->start + i) * DUMPWIDTH * (1ull << view->zoom));
for (j = 0; j < DUMPWIDTH; ++j, ++c) {
a = ((view->start + i) * DUMPWIDTH + j + 0) * (1ull << view->zoom);
b = ((view->start + i) * DUMPWIDTH + j + 1) * (1ull << view->zoom);
changed = ((histart >= a && hiend < b) ||
(histart && hiend && histart >= a && hiend < b));
if (changed && !high) {
high = true;
AppendStr(&p->lines[i], "\e[7m");
} else if (!changed && high) {
AppendStr(&p->lines[i], "\e[27m");
high = false;
}
if (invalid[c]) {
AppendWide(&p->lines[i], u'');
} else {
AppendWide(&p->lines[i], kCp437[canvas[c]]);
}
}
if (high) {
AppendStr(&p->lines[i], "\e[27m");
high = false;
}
}
free(invalid);
free(canvas);
}
static void DrawMemoryUnzoomed(struct Panel *p, struct MemoryView *view,
long histart, long hiend) {
long i, j, k, c;
bool high, changed;
high = false;
for (i = 0; i < p->bottom - p->top; ++i) {
AppendFmt(&p->lines[i], "%p ", (view->start + i) * DUMPWIDTH);
for (j = 0; j < DUMPWIDTH; ++j) {
k = (view->start + i) * DUMPWIDTH + j;
c = VirtualBing(k);
changed = histart <= k && k < hiend;
if (changed && !high) {
high = true;
AppendStr(&p->lines[i], "\e[7m");
} else if (!changed && high) {
AppendStr(&p->lines[i], "\e[27m");
high = false;
}
AppendWide(&p->lines[i], c);
}
if (high) {
AppendStr(&p->lines[i], "\e[27m");
high = false;
}
}
}
static void DrawMemory(struct Panel *p, struct MemoryView *view, long histart,
long hiend) {
if (p->top == p->bottom) return;
if (view->zoom) {
DrawMemoryZoomed(p, view, histart, hiend);
} else {
DrawMemoryUnzoomed(p, view, histart, hiend);
}
}
static void DrawMaps(struct Panel *p) {
int i;
char *text, *p1, *p2;
2020-10-27 10:39:46 +00:00
if (p->top == p->bottom) return;
p1 = text = FormatPml4t(m);
for (i = 0; p1; ++i, p1 = p2) {
if ((p2 = strchr(p1, '\n'))) *p2++ = '\0';
if (i >= mapsstart) {
AppendPanel(p, i - mapsstart, p1);
}
}
free(text);
}
static void DrawBreakpoints(struct Panel *p) {
int64_t addr;
const char *name;
char *s, buf[256];
long i, line, sym;
2020-10-27 10:39:46 +00:00
if (p->top == p->bottom) return;
for (line = 0, i = breakpoints.i; i--;) {
if (breakpoints.p[i].disable) continue;
if (line >= breakpointsstart) {
addr = breakpoints.p[i].addr;
sym = DisFindSym(dis, addr);
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
s = buf;
s += sprintf(s, "%p ", addr);
CHECK_LT(Demangle(s, name, DIS_MAX_SYMBOL_LENGTH), buf + ARRAYLEN(buf));
AppendPanel(p, line - breakpointsstart, buf);
if (sym != -1 && addr != dis->syms.p[sym].addr) {
snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr);
AppendPanel(p, line, buf);
}
}
++line;
}
}
static int GetPreferredStackAlignmentMask(void) {
switch (m->mode & 3) {
case XED_MODE_LONG:
return 15;
case XED_MODE_LEGACY:
return 3;
case XED_MODE_REAL:
return 3;
default:
unreachable;
}
}
static void DrawFrames(struct Panel *p) {
int i, n;
long sym;
uint8_t *r;
const char *name;
char *s, line[256];
int64_t sp, bp, rp;
2020-10-27 10:39:46 +00:00
if (p->top == p->bottom) return;
rp = m->ip;
bp = Read64(m->bp);
sp = Read64(m->sp);
for (i = 0; i < p->bottom - p->top;) {
sym = DisFindSym(dis, rp);
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
s = line;
s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp);
s = Demangle(s, name, DIS_MAX_SYMBOL_LENGTH);
AppendPanel(p, i - framesstart, line);
if (sym != -1 && rp != dis->syms.p[sym].addr) {
snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr);
AppendPanel(p, i - framesstart, line);
}
if (!bp) break;
if (bp < sp) {
AppendPanel(p, i - framesstart, " [STRAY]");
} else if (bp - sp <= 0x1000) {
snprintf(line, sizeof(line), " %,ld bytes", bp - sp);
AppendPanel(p, i - framesstart, line);
}
if (bp & GetPreferredStackAlignmentMask() && i) {
AppendPanel(p, i - framesstart, " [MISALIGN]");
}
++i;
if (((Read64(m->ss) + bp) & 0xfff) > 0xff0) break;
if (!(r = FindReal(m, Read64(m->ss) + bp))) {
AppendPanel(p, i - framesstart, "CORRUPT FRAME POINTER");
break;
}
sp = bp;
2020-10-27 10:39:46 +00:00
bp = ReadWord(r + 0);
rp = ReadWord(r + 8);
}
}
static void CheckFramePointerImpl(void) {
uint8_t *r;
int64_t bp, sp, rp;
static int64_t lastbp;
bp = Read64(m->bp);
if (bp && bp == lastbp) return;
lastbp = bp;
rp = m->ip;
sp = Read64(m->sp);
while (bp) {
if (!(r = FindReal(m, Read64(m->ss) + bp))) {
LOGF("corrupt frame: %p", bp);
ThrowProtectionFault(m);
}
sp = bp;
bp = Read64(r + 0) - 0;
rp = Read64(r + 8) - 1;
if (!bp && !(m->bofram[0] <= rp && rp <= m->bofram[1])) {
LOGF("bad frame !(%p <= %p <= %p)", m->bofram[0], rp, m->bofram[1]);
ThrowProtectionFault(m);
}
}
}
forceinline void CheckFramePointer(void) {
if (m->bofram[0]) {
CheckFramePointerImpl();
}
}
static bool IsExecuting(void) {
return (action & (CONTINUE | STEP | NEXT | FINISH)) && !(action & FAILURE);
}
2020-10-27 10:39:46 +00:00
static int AppendStat(struct Buffer *b, const char *name, int64_t value,
bool changed) {
int width;
AppendChar(b, ' ');
if (changed) AppendStr(b, "\e[31m");
width = AppendFmt(b, "%,8ld %s", value, name);
if (changed) AppendStr(b, "\e[39m");
return 1 + width;
}
static void DrawStatus(struct Panel *p) {
2020-10-27 10:39:46 +00:00
int yn, xn, rw;
struct Buffer *s;
2020-10-27 10:39:46 +00:00
struct MachineMemstat *a, *b;
yn = p->top - p->bottom;
xn = p->right - p->left;
if (!yn || !xn) return;
rw = 0;
a = &m->memstat;
b = &lastmemstat;
s = xmalloc(sizeof(struct Buffer));
memset(s, 0, sizeof(*s));
if (ips > 0) rw += AppendStat(s, "ips", ips, false);
rw += AppendStat(s, "kb", m->real.n / 1024, false);
rw += AppendStat(s, "reserve", a->reserved, a->reserved != b->reserved);
rw += AppendStat(s, "commit", a->committed, a->committed != b->committed);
rw += AppendStat(s, "freed", a->freed, a->freed != b->freed);
rw += AppendStat(s, "tables", a->pagetables, a->pagetables != b->pagetables);
rw += AppendStat(s, "fds", m->fds.i, false);
2020-10-27 10:39:46 +00:00
AppendFmt(&p->lines[0], "\e[7m%-*s%s\e[0m", xn - rw,
statusmessage && nowl() < statusexpires ? statusmessage
: "das blinkenlights",
s->p);
free(s->p);
free(s);
2020-10-27 10:39:46 +00:00
memcpy(b, a, sizeof(*a));
}
static void PreventBufferbloat(void) {
long double now, rate;
static long double last;
now = nowl();
rate = 1. / 60;
if (now - last < rate) {
dsleep(rate - (now - last));
}
last = now;
}
static void Redraw(void) {
int i, j;
2020-10-27 10:39:46 +00:00
ScrollOp(&pan.disassembly, GetDisIndex());
if (last_opcount) {
ips = unsignedsubtract(opcount, last_opcount) / (nowl() - last_seconds);
}
SetupDraw();
for (i = 0; i < ARRAYLEN(pan.p); ++i) {
for (j = 0; j < pan.p[i].bottom - pan.p[i].top; ++j) {
pan.p[i].lines[j].i = 0;
}
}
DrawDisassembly(&pan.disassembly);
DrawDisplay(&pan.display);
DrawCpu(&pan.registers);
DrawSse(&pan.sse);
DrawHr(&pan.breakpointshr, "BREAKPOINTS");
2020-10-27 10:39:46 +00:00
DrawHr(&pan.mapshr, "PML4T");
DrawHr(&pan.frameshr, m->bofram[0] ? "PROTECTED FRAMES" : "FRAMES");
DrawHr(&pan.ssehr, "SSE");
DrawHr(&pan.codehr, "CODE");
DrawHr(&pan.readhr, "READ");
DrawHr(&pan.writehr, "WRITE");
DrawHr(&pan.stackhr, "STACK");
DrawMaps(&pan.maps);
DrawFrames(&pan.frames);
DrawBreakpoints(&pan.breakpoints);
DrawMemory(&pan.code, &codeview, GetIp(), GetIp() + m->xedd->length);
DrawMemory(&pan.readdata, &readview, m->readaddr, m->readaddr + m->readsize);
DrawMemory(&pan.writedata, &writeview, m->writeaddr,
m->writeaddr + m->writesize);
DrawMemory(&pan.stack, &stackview, GetSp(), GetSp() + GetPointerWidth());
2020-10-27 10:39:46 +00:00
DrawStatus(&pan.status);
PreventBufferbloat();
if (PrintPanels(ttyout, ARRAYLEN(pan.p), pan.p, tyn, txn) == -1) {
LOGF("PrintPanels Interrupted");
CHECK_EQ(EINTR, errno);
}
2020-10-27 10:39:46 +00:00
last_opcount = opcount;
last_seconds = nowl();
CopyMachineState(&laststate);
}
static void ReactiveDraw(void) {
if (tuimode) {
m->ip -= m->xedd->length;
Redraw();
m->ip += m->xedd->length;
tick = speed;
}
}
static void HandleAlarm(void) {
alarmed = false;
action &= ~ALARM;
2020-10-27 10:39:46 +00:00
pty->conf &= ~kPtyBell;
free(statusmessage);
statusmessage = NULL;
}
static void HandleAppReadInterrupt(void) {
DEBUGF("HandleAppReadInterrupt");
if (action & ALARM) {
HandleAlarm();
}
if (action & WINCHED) {
GetTtySize(ttyout);
action &= ~WINCHED;
}
if (action & INT) {
action &= ~INT;
if (action & CONTINUE) {
action &= ~CONTINUE;
} else {
if (tuimode) {
LeaveScreen();
TuiCleanup();
}
exit(0);
}
}
}
static int OnPtyFdClose(int fd) {
2020-10-27 10:39:46 +00:00
return close(fd);
}
static bool HasPendingInput(int fd) {
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
fds[0].revents = 0;
poll(fds, ARRAYLEN(fds), 0);
return fds[0].revents & (POLLIN | POLLERR);
}
static ssize_t ReadPtyFdDirect(int fd, void *data, size_t size) {
char *buf;
ssize_t rc;
DEBUGF("ReadPtyFdDirect");
buf = malloc(PAGESIZE);
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyBlinkcursor;
if (tuimode) DisableMouseTracking();
for (;;) {
ReactiveDraw();
if ((rc = read(fd, buf, PAGESIZE)) != -1) break;
CHECK_EQ(EINTR, errno);
HandleAppReadInterrupt();
}
if (tuimode) EnableMouseTracking();
2020-10-27 10:39:46 +00:00
pty->conf &= ~kPtyBlinkcursor;
if (rc > 0) {
2020-10-27 10:39:46 +00:00
PtyWriteInput(pty, buf, rc);
ReactiveDraw();
2020-10-27 10:39:46 +00:00
rc = PtyRead(pty, data, size);
}
free(buf);
return rc;
}
static ssize_t OnPtyFdReadv(int fd, const struct iovec *iov, int iovlen) {
int i;
ssize_t rc;
void *data;
size_t size;
for (size = i = 0; i < iovlen; ++i) {
if (iov[i].iov_len) {
data = iov[i].iov_base;
size = iov[i].iov_len;
break;
}
}
if (size) {
2020-10-27 10:39:46 +00:00
if (!(rc = PtyRead(pty, data, size))) {
rc = ReadPtyFdDirect(fd, data, size);
}
return rc;
} else {
return 0;
}
}
2020-10-27 10:39:46 +00:00
static void DrawDisplayOnly(struct Panel *p) {
struct Buffer b;
int i, y, yn, xn, tly, tlx, conf;
2020-10-27 10:39:46 +00:00
yn = MIN(tyn, p->bottom - p->top);
xn = MIN(txn, p->right - p->left);
for (i = 0; i < yn; ++i) {
p->lines[i].i = 0;
}
DrawDisplay(p);
memset(&b, 0, sizeof(b));
tly = tyn / 2 - yn / 2;
tlx = txn / 2 - xn / 2;
AppendStr(&b, "\e[0m\e[H");
for (y = 0; y < tyn; ++y) {
if (y) AppendStr(&b, "\r\n");
if (tly <= y && y <= tly + yn) {
for (i = 0; i < tlx; ++i) {
AppendChar(&b, ' ');
}
2020-10-27 10:39:46 +00:00
AppendData(&b, p->lines[y - tly].p, p->lines[y - tly].i);
}
AppendStr(&b, "\e[0m\e[K");
}
write(ttyout, b.p, b.i);
free(b.p);
}
static ssize_t OnPtyFdWritev(int fd, const struct iovec *iov, int iovlen) {
int i;
size_t size;
for (size = i = 0; i < iovlen; ++i) {
2020-10-27 10:39:46 +00:00
PtyWrite(pty, iov[i].iov_base, iov[i].iov_len);
size += iov[i].iov_len;
}
return size;
}
static int OnPtyFdTiocgwinsz(int fd, struct winsize *ws) {
ws->ws_row = pty->yn;
ws->ws_col = pty->xn;
return 0;
}
static int OnPtyFdTcgets(int fd, struct termios *c) {
memset(c, 0, sizeof(*c));
2020-10-27 10:39:46 +00:00
if (!(pty->conf & kPtyNocanon)) c->c_iflag |= ICANON;
if (!(pty->conf & kPtyNoecho)) c->c_iflag |= ECHO;
if (!(pty->conf & kPtyNoopost)) c->c_oflag |= OPOST;
return 0;
}
static int OnPtyFdTcsets(int fd, uint64_t request, struct termios *c) {
if (c->c_iflag & ICANON) {
2020-10-27 10:39:46 +00:00
pty->conf &= ~kPtyNocanon;
} else {
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyNocanon;
}
if (c->c_iflag & ECHO) {
2020-10-27 10:39:46 +00:00
pty->conf &= ~kPtyNoecho;
} else {
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyNoecho;
}
if (c->c_oflag & OPOST) {
2020-10-27 10:39:46 +00:00
pty->conf &= ~kPtyNoopost;
} else {
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyNoopost;
}
return 0;
}
static int OnPtyFdIoctl(int fd, uint64_t request, void *memory) {
if (request == TIOCGWINSZ) {
return OnPtyFdTiocgwinsz(fd, memory);
} else if (request == TCGETS) {
return OnPtyFdTcgets(fd, memory);
} else if (request == TCSETS || request == TCSETSW || request == TCSETSF) {
return OnPtyFdTcsets(fd, request, memory);
} else {
return einval();
}
}
static const struct MachineFdCb kMachineFdCbPty = {
.close = OnPtyFdClose,
.readv = OnPtyFdReadv,
.writev = OnPtyFdWritev,
.ioctl = OnPtyFdIoctl,
};
static void LaunchDebuggerReactively(void) {
LOGF("%s", systemfailure);
if (tuimode) {
action |= FAILURE;
} else {
if (react) {
tuimode = true;
action |= FAILURE;
} else {
fprintf(stderr, "ERROR: %s\n", systemfailure);
exit(1);
}
}
}
static void OnDebug(void) {
strcpy(systemfailure, "IT'S A TRAP");
LaunchDebuggerReactively();
}
static void OnSegmentationFault(void) {
snprintf(systemfailure, sizeof(systemfailure), "SEGMENTATION FAULT %p",
m->faultaddr);
LaunchDebuggerReactively();
}
static void OnProtectionFault(void) {
strcpy(systemfailure, "PROTECTION FAULT");
LaunchDebuggerReactively();
}
static void OnSimdException(void) {
strcpy(systemfailure, "SIMD FAULT");
LaunchDebuggerReactively();
}
static void OnUndefinedInstruction(void) {
strcpy(systemfailure, "UNDEFINED INSTRUCTION");
LaunchDebuggerReactively();
}
static void OnDecodeError(void) {
strcpy(stpcpy(systemfailure, "DECODE: "),
2020-10-27 10:39:46 +00:00
IndexDoubleNulString(kXedErrorNames, m->xedd->op.error));
LaunchDebuggerReactively();
}
static void OnDivideError(void) {
strcpy(systemfailure, "DIVIDE BY ZERO OR BANE/-1");
LaunchDebuggerReactively();
}
static void OnFpuException(void) {
strcpy(systemfailure, "FPU EXCEPTION");
LaunchDebuggerReactively();
}
static void OnExit(int rc) {
exitcode = rc;
action |= EXIT;
}
static size_t GetLastIndex(size_t size, unsigned unit, int i, unsigned limit) {
unsigned q, r;
if (!size) return 0;
q = size / unit;
r = size % unit;
if (!r) --q;
q += i;
if (q > limit) q = limit;
return q;
}
static void OnDiskServiceReset(void) {
m->ax[1] = 0x00;
SetCarry(false);
}
static void OnDiskServiceBadCommand(void) {
m->ax[1] = 0x01;
SetCarry(true);
}
static void OnDiskServiceGetParams(void) {
size_t lastsector, lasttrack, lasthead;
lasthead = GetLastIndex(elf->mapsize, 512 * 63 * 1024, 0, 255);
lasttrack = GetLastIndex(elf->mapsize, 512 * 63, 0, 1023);
lastsector = GetLastIndex(elf->mapsize, 512, 1, 63);
m->dx[0] = 1;
m->dx[1] = lasthead;
m->cx[0] = lasttrack >> 8 << 6 | lastsector;
m->cx[1] = lasttrack;
m->ax[1] = 0;
Write64(m->es, 0);
Write16(m->di, 0);
SetCarry(false);
}
static void OnDiskServiceReadSectors(void) {
static int x;
uint64_t addr, size;
int64_t sectors, drive, head, track, sector, offset;
sectors = m->ax[0];
drive = m->dx[0];
head = m->dx[1];
track = (m->cx[0] & 0b11000000) << 2 | m->cx[1];
sector = (m->cx[0] & 0b00111111) - 1;
offset = head * track * sector * 512;
size = sectors * 512;
offset = sector * 512 + track * 512 * 63 + head * 512 * 63 * 1024;
VERBOSEF("bios read sectors %d "
"@ sector %ld track %ld head %ld drive %ld offset %#lx",
sectors, sector, track, head, drive, offset);
if (0 <= sector && offset + size <= elf->mapsize) {
addr = Read64(m->es) + Read16(m->bx);
if (addr + size <= m->real.n) {
SetWriteAddr(m, addr, size);
memcpy(m->real.p + addr, elf->map + offset, size);
m->ax[1] = 0x00;
SetCarry(false);
} else {
m->ax[0] = 0x00;
m->ax[1] = 0x02;
SetCarry(true);
}
} else {
WARNF("bios read sector failed 0 <= %d && %lx + %lx <= %lx", sector, offset,
size, elf->mapsize);
m->ax[0] = 0x00;
m->ax[1] = 0x0d;
SetCarry(true);
}
}
static void OnDiskService(void) {
switch (m->ax[1]) {
case 0x00:
OnDiskServiceReset();
break;
case 0x02:
OnDiskServiceReadSectors();
break;
case 0x08:
OnDiskServiceGetParams();
break;
default:
OnDiskServiceBadCommand();
break;
}
}
static void OnVidyaServiceSetMode(void) {
if (FindReal(m, 0xB0000)) {
vidya = m->ax[0];
} else {
WARNF("maybe you forgot -r flag");
}
}
static void OnVidyaServiceGetMode(void) {
m->ax[0] = vidya;
m->ax[1] = 80; // columns
m->bx[1] = 0; // page
}
static void OnVidyaServiceSetCursorPosition(void) {
2020-10-27 10:39:46 +00:00
PtySetY(pty, m->dx[1]);
PtySetX(pty, m->dx[0]);
}
static void OnVidyaServiceGetCursorPosition(void) {
m->dx[1] = pty->y;
m->dx[0] = pty->x;
m->cx[1] = 5; // cursor ▂ scan lines 5..7 of 0..7
2020-10-27 10:39:46 +00:00
m->cx[0] = 7 | !!(pty->conf & kPtyNocursor) << 5;
}
static int GetVidyaByte(unsigned char b) {
if (0x20 <= b && b <= 0x7F) return b;
if (b == 0xFF) b = 0x00;
return kCp437[b];
}
static void OnVidyaServiceWriteCharacter(void) {
int i, n, y, x;
char *p, buf[32];
p = buf;
p += FormatCga(m->bx[0], p);
p = stpcpy(p, "\e7");
p += tpencode(p, 8, GetVidyaByte(m->ax[0]), false);
p = stpcpy(p, "\e8");
for (i = Read16(m->cx); i--;) {
2020-10-27 10:39:46 +00:00
PtyWrite(pty, buf, p - buf);
}
}
static char16_t VidyaServiceXlatTeletype(uint8_t c) {
switch (c) {
case '\a':
case '\b':
case '\r':
case '\n':
case 0177:
return c;
default:
return GetVidyaByte(c);
}
}
static void OnVidyaServiceTeletypeOutput(void) {
int n;
char buf[12];
n = FormatCga(m->bx[0], buf);
n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false);
2020-10-27 10:39:46 +00:00
PtyWrite(pty, buf, n);
}
static void OnVidyaService(void) {
switch (m->ax[1]) {
case 0x00:
OnVidyaServiceSetMode();
break;
case 0x02:
OnVidyaServiceSetCursorPosition();
break;
case 0x03:
OnVidyaServiceGetCursorPosition();
break;
case 0x09:
OnVidyaServiceWriteCharacter();
break;
case 0x0E:
OnVidyaServiceTeletypeOutput();
break;
case 0x0F:
OnVidyaServiceGetMode();
break;
default:
break;
}
}
static void OnKeyboardServiceReadKeyPress(void) {
uint8_t b;
ssize_t rc;
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyBlinkcursor;
if (tuimode) DisableMouseTracking();
for (;;) {
ReactiveDraw();
if ((rc = read(0, &b, 1)) != -1) break;
CHECK_EQ(EINTR, errno);
HandleAppReadInterrupt();
}
if (tuimode) EnableMouseTracking();
2020-10-27 10:39:46 +00:00
pty->conf &= ~kPtyBlinkcursor;
ReactiveDraw();
if (b == 0x7F) b = '\b';
m->ax[0] = b;
m->ax[1] = 0;
}
static void OnKeyboardService(void) {
switch (m->ax[1]) {
case 0x00:
OnKeyboardServiceReadKeyPress();
break;
default:
break;
}
}
static void OnApmService(void) {
if (Read16(m->ax) == 0x5300 && Read16(m->bx) == 0x0000) {
Write16(m->bx, 'P' << 8 | 'M');
SetCarry(false);
} else if (Read16(m->ax) == 0x5301 && Read16(m->bx) == 0x0000) {
SetCarry(false);
} else if (Read16(m->ax) == 0x5307 && m->bx[0] == 1 && m->cx[0] == 3) {
LOGF("APM SHUTDOWN");
exit(0);
} else {
SetCarry(true);
}
}
static void OnE820(void) {
uint8_t p[20];
uint64_t addr;
addr = Read64(m->es) + Read16(m->di);
if (Read32(m->dx) == 0x534D4150 && Read32(m->cx) == 24 &&
addr + sizeof(p) <= m->real.n) {
if (!Read32(m->bx)) {
Write64(p + 000, 0);
Write64(p + 010, m->real.n);
Write32(p + 014, 1);
memcpy(m->real.p + addr, p, sizeof(p));
SetWriteAddr(m, addr, sizeof(p));
Write32(m->cx, sizeof(p));
Write32(m->bx, 1);
} else {
Write32(m->bx, 0);
Write32(m->cx, 0);
}
Write32(m->ax, 0x534D4150);
SetCarry(false);
} else {
SetCarry(true);
}
}
static void OnInt15h(void) {
if (Read32(m->ax) == 0xE820) {
OnE820();
} else if (m->ax[1] == 0x53) {
OnApmService();
} else {
SetCarry(true);
}
}
static bool OnHalt(int interrupt) {
ReactiveDraw();
switch (interrupt) {
case 1:
case 3:
OnDebug();
return false;
case 0x13:
OnDiskService();
return true;
case 0x10:
OnVidyaService();
return true;
case 0x15:
OnInt15h();
return true;
case 0x16:
OnKeyboardService();
return true;
case kMachineSegmentationFault:
OnSegmentationFault();
return false;
case kMachineProtectionFault:
OnProtectionFault();
return false;
case kMachineSimdException:
OnSimdException();
return false;
case kMachineUndefinedInstruction:
OnUndefinedInstruction();
return false;
case kMachineDecodeError:
OnDecodeError();
return false;
case kMachineDivideError:
OnDivideError();
return false;
case kMachineFpuException:
OnFpuException();
return false;
case kMachineExit:
case kMachineHalt:
default:
OnExit(interrupt);
return false;
}
}
static void OnBinbase(struct Machine *m) {
unsigned i;
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;
}
Disassemble();
}
static void OnLongBranch(struct Machine *m) {
if (tuimode) {
Disassemble();
}
}
static void OnPageUp(void) {
opstart -= tyn / 2;
}
static void OnPageDown(void) {
opstart += tyn / 2;
}
static void SetStatus(const char *fmt, ...) {
char *s;
va_list va;
int y, x, n;
va_start(va, fmt);
2020-10-27 10:39:46 +00:00
s = xvasprintf(fmt, va);
va_end(va);
free(statusmessage);
2020-10-27 10:39:46 +00:00
statusmessage = s;
statusexpires = nowl() + 1;
2020-10-27 10:39:46 +00:00
setitimer(ITIMER_REAL, &((struct itimerval){{0, 0}, {1, 0}}), NULL);
}
static int ClampSpeed(int s) {
return MAX(-0x1000, MIN(0x40000000, s));
}
static void OnTurbo(void) {
if (!speed || speed == -1) {
speed = 1;
} else if (speed > 0) {
speed = ClampSpeed(speed << 1);
} else {
speed = ClampSpeed(speed >> 1);
}
SetStatus("speed %,d", speed);
}
static void OnSlowmo(void) {
if (!speed || speed == 1) {
speed = -1;
} else if (speed > 0) {
speed = ClampSpeed(speed >> 1);
} else {
speed = ClampSpeed(speed << 1);
}
SetStatus("speed %,d", speed);
}
static void OnUpArrow(void) {
--opstart;
}
static void OnDownArrow(void) {
++opstart;
}
static void OnHome(void) {
opstart = 0;
}
static void OnEnd(void) {
opstart = dis->ops.i - (pan.disassembly.bottom - pan.disassembly.top);
}
static void OnEnter(void) {
action &= ~FAILURE;
}
static void OnTab(void) {
focus = (focus + 1) % ARRAYLEN(pan.p);
}
static void OnUp(void) {
}
static void OnDown(void) {
}
static void OnStep(void) {
if (action & FAILURE) return;
action |= STEP;
action &= ~NEXT;
action &= ~CONTINUE;
}
static void OnNext(void) {
if (action & FAILURE) return;
action ^= NEXT;
action &= ~STEP;
action &= ~FINISH;
action &= ~CONTINUE;
}
static void OnFinish(void) {
if (action & FAILURE) return;
action ^= FINISH;
action &= ~NEXT;
action &= ~FAILURE;
action &= ~CONTINUE;
}
static void OnContinue(void) {
action ^= CONTINUE;
action &= ~STEP;
action &= ~NEXT;
action &= ~FINISH;
action &= ~FAILURE;
}
static void OnQuit(void) {
action |= QUIT;
}
static void OnRestart(void) {
action |= RESTART;
}
static void OnXmmType(void) {
uint8_t t;
unsigned i;
t = CycleXmmType(xmmtype.type[0]);
for (i = 0; i < 16; ++i) {
xmmtype.type[i] = t;
}
}
static void SetXmmSize(int bytes) {
unsigned i;
for (i = 0; i < 16; ++i) {
xmmtype.size[i] = bytes;
}
}
static void SetXmmDisp(int disp) {
xmmdisp = disp;
}
static void OnXmmSize(void) {
SetXmmSize(CycleXmmSize(xmmtype.size[0]));
}
static void OnXmmDisp(void) {
SetXmmDisp(CycleXmmDisp(xmmdisp));
}
static bool HasPendingKeyboard(void) {
return HasPendingInput(ttyin);
}
static void Sleep(int ms) {
poll((struct pollfd[]){{ttyin, POLLIN}}, 1, ms);
}
static void OnMouseWheelUp(struct Panel *p, int y, int x) {
if (p == &pan.disassembly) {
opstart -= WHEELDELTA;
} else if (p == &pan.code) {
codeview.start -= WHEELDELTA;
} else if (p == &pan.readdata) {
readview.start -= WHEELDELTA;
} else if (p == &pan.writedata) {
writeview.start -= WHEELDELTA;
} else if (p == &pan.stack) {
stackview.start -= WHEELDELTA;
} else if (p == &pan.maps) {
mapsstart = MAX(0, mapsstart - 1);
} else if (p == &pan.frames) {
framesstart = MAX(0, framesstart - 1);
} else if (p == &pan.breakpoints) {
breakpointsstart = MAX(0, breakpointsstart - 1);
}
}
static void OnMouseWheelDown(struct Panel *p, int y, int x) {
if (p == &pan.disassembly) {
opstart += WHEELDELTA;
} else if (p == &pan.code) {
codeview.start += WHEELDELTA;
} else if (p == &pan.readdata) {
readview.start += WHEELDELTA;
} else if (p == &pan.writedata) {
writeview.start += WHEELDELTA;
} else if (p == &pan.stack) {
stackview.start += WHEELDELTA;
} else if (p == &pan.maps) {
mapsstart += 1;
} else if (p == &pan.frames) {
framesstart += 1;
} else if (p == &pan.breakpoints) {
breakpointsstart += 1;
}
}
static void OnMouseCtrlWheelUp(struct Panel *p, int y, int x) {
ZoomMemoryViews(p, y, x, -1);
}
static void OnMouseCtrlWheelDown(struct Panel *p, int y, int x) {
ZoomMemoryViews(p, y, x, +1);
}
static struct Panel *LocatePanel(int y, int x) {
int i;
for (i = 0; i < ARRAYLEN(pan.p); ++i) {
if ((pan.p[i].left <= x && x < pan.p[i].right) &&
(pan.p[i].top <= y && y < pan.p[i].bottom)) {
return &pan.p[i];
}
}
return NULL;
}
static void OnMouse(char *p) {
int e, x, y;
struct Panel *ep;
e = strtol(p, &p, 10);
if (*p == ';') ++p;
x = min(txn, max(1, strtol(p, &p, 10))) - 1;
if (*p == ';') ++p;
y = min(tyn, max(1, strtol(p, &p, 10))) - 1;
e |= (*p == 'm') << 2;
if ((ep = LocatePanel(y, x))) {
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;
default:
break;
}
}
}
static void ReadKeyboard(void) {
char buf[64], *p = buf;
memset(buf, 0, sizeof(buf));
if (readansi(ttyin, buf, sizeof(buf)) == -1) {
2020-10-27 10:39:46 +00:00
if (errno == EINTR) {
LOGF("readkeyboard interrupted");
return;
}
FATALF("readkeyboard failed: %s", strerror(errno));
}
switch (*p++) {
CASE('q', OnQ());
CASE('v', OnV());
CASE('s', OnStep());
CASE('n', OnNext());
CASE('f', OnFinish());
CASE('c', OnContinue());
CASE('R', OnRestart());
CASE('x', OnXmmDisp());
CASE('t', OnXmmType());
CASE('T', OnXmmSize());
CASE('u', OnUp());
CASE('d', OnDown());
CASE('B', PopBreakpoint(&breakpoints));
CASE('M', ToggleMouseTracking());
CASE('\t', OnTab());
CASE('\r', OnEnter());
CASE('\n', OnEnter());
CASE(CTRL('C'), OnSigInt());
CASE(CTRL('D'), OnSigInt());
CASE(CTRL('\\'), OnQuit());
CASE(CTRL('Z'), raise(SIGSTOP));
CASE(CTRL('L'), OnFeed());
CASE(CTRL('P'), OnUpArrow());
CASE(CTRL('N'), OnDownArrow());
CASE(CTRL('V'), OnPageDown());
CASE(CTRL('T'), OnTurbo());
case '\e':
switch (*p++) {
CASE('v', OnPageUp());
CASE('t', OnSlowmo());
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;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
}
static int64_t ParseHexValue(const char *s) {
char *ep;
int64_t x;
x = strtoll(s, &ep, 16);
if (*ep) {
fputs("ERROR: bad hexadecimal: ", stderr);
fputs(s, stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
return x;
}
static void HandleBreakpointFlag(const char *s) {
struct Breakpoint b;
memset(&b, 0, sizeof(b));
if (isdigit(*s)) {
b.addr = ParseHexValue(s);
} else {
b.symbol = optarg;
}
PushBreakpoint(&breakpoints, &b);
}
static wontreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "SYNOPSIS\n\n %s%s", program_invocation_name, USAGE);
exit(rc);
}
static void Exec(void) {
long op;
ssize_t bp;
int interrupt;
ExecSetup();
if (!(interrupt = setjmp(m->onhalt))) {
if (!(action & CONTINUE) &&
(bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) {
2020-10-27 10:39:46 +00:00
LOGF("BREAK1 %p", breakpoints.p[bp].addr);
tuimode = true;
LoadInstruction(m);
ExecuteInstruction(m);
2020-10-27 10:39:46 +00:00
++opcount;
CheckFramePointer();
} else {
action &= ~CONTINUE;
for (;;) {
LoadInstruction(m);
2020-10-27 10:39:46 +00:00
if ((bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) {
LOGF("BREAK2 %p", breakpoints.p[bp].addr);
action &= ~(FINISH | NEXT | CONTINUE);
tuimode = true;
break;
}
ExecuteInstruction(m);
2020-10-27 10:39:46 +00:00
++opcount;
KeepGoing:
CheckFramePointer();
2020-10-27 10:39:46 +00:00
if (action & ALARM) {
DrawDisplayOnly(&pan.display);
action &= ~ALARM;
}
if (action & EXIT) {
LOGF("EXEC EXIT");
break;
}
if (action & INT) {
LOGF("EXEC INT");
if (react) {
LOGF("REACT");
action &= ~(INT | STEP | FINISH | NEXT);
tuimode = true;
}
2020-10-27 10:39:46 +00:00
break;
}
}
}
} else {
if (OnHalt(interrupt)) {
goto KeepGoing;
}
}
}
static void Tui(void) {
long op;
ssize_t bp;
int interrupt;
bool interactive;
LOGF("TUI");
TuiSetup();
SetupDraw();
ScrollOp(&pan.disassembly, GetDisIndex());
if (!(interrupt = setjmp(m->onhalt))) {
for (;;) {
if (!(action & FAILURE)) {
LoadInstruction(m);
2020-10-27 10:39:46 +00:00
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];
m->xedd->length = 1;
m->xedd->bytes[0] = 0xCC;
m->xedd->op.opcode = 0xCC;
}
if (action & WINCHED) {
GetTtySize(ttyout);
action &= ~WINCHED;
}
interactive = ++tick > speed;
if (interactive && speed < 0) {
Sleep(-speed);
}
if (action & ALARM) {
HandleAlarm();
}
if (action & FAILURE) {
ScrollMemoryViews();
}
if (!(action & CONTINUE) || interactive) {
tick = 0;
Redraw();
}
if (action & FAILURE) {
LOGF("TUI FAILURE");
PrintMessageBox(ttyout, systemfailure, tyn, txn);
ReadKeyboard();
2020-10-27 10:39:46 +00:00
if (action & INT) {
LOGF("TUI INT");
LeaveScreen();
exit(1);
}
} else if (!IsExecuting() || (!(action & CONTINUE) && !(action & INT) &&
HasPendingKeyboard())) {
ReadKeyboard();
}
if (action & INT) {
LOGF("TUI INT");
action &= ~INT;
if (action & (CONTINUE | NEXT | FINISH)) {
action &= ~(CONTINUE | NEXT | FINISH);
} else {
tuimode = false;
action |= CONTINUE;
break;
}
}
if (action & EXIT) {
LeaveScreen();
LOGF("TUI EXIT");
break;
}
if (action & QUIT) {
LOGF("TUI QUIT");
action &= ~QUIT;
raise(SIGQUIT);
continue;
}
if (action & RESTART) {
LOGF("TUI RESTART");
break;
}
if (IsExecuting()) {
if (!(action & CONTINUE)) {
action &= ~STEP;
if (action & NEXT) {
action &= ~NEXT;
if (IsCall()) {
BreakAtNextInstruction();
break;
}
}
if (action & FINISH) {
if (IsCall()) {
BreakAtNextInstruction();
break;
} else if (IsRet()) {
action &= ~FINISH;
}
}
}
if (!IsDebugBreak()) {
UpdateXmmType(m, &xmmtype);
ExecuteInstruction(m);
2020-10-27 10:39:46 +00:00
++opcount;
if (!(action & CONTINUE) || interactive) {
ScrollMemoryViews();
}
} else {
m->ip += m->xedd->length;
action &= ~NEXT;
action &= ~FINISH;
action &= ~CONTINUE;
}
KeepGoing:
CheckFramePointer();
if (!(action & CONTINUE)) {
ScrollOp(&pan.disassembly, GetDisIndex());
if ((action & FINISH) && IsRet()) action &= ~FINISH;
if (((action & NEXT) && IsRet()) || (action & FINISH)) {
action &= ~NEXT;
}
}
}
}
} else {
if (OnHalt(interrupt)) {
goto KeepGoing;
}
ScrollOp(&pan.disassembly, GetDisIndex());
}
TuiCleanup();
}
static void GetOpts(int argc, char *argv[]) {
int opt;
stpcpy(stpcpy(stpcpy(logpath, kTmpPath), basename(argv[0])), ".log");
while ((opt = getopt(argc, argv, "hvtrzRsb:HL:")) != -1) {
switch (opt) {
case 't':
tuimode = true;
break;
case 'R':
react = true;
break;
case 'r':
m->mode = XED_MACHINE_MODE_REAL;
g_disisprog_disable = true;
break;
case 's':
printstats = true;
break;
case 'b':
HandleBreakpointFlag(optarg);
break;
case 'H':
memset(&g_high, 0, sizeof(g_high));
break;
case 'v':
++g_loglevel;
break;
case 'L':
strcpy(logpath, optarg);
break;
case 'z':
++codeview.zoom;
++readview.zoom;
++writeview.zoom;
++stackview.zoom;
break;
case 'h':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
g_logfile = fopen(logpath, "a");
setvbuf(g_logfile, xmalloc(PAGESIZE), _IOLBF, PAGESIZE);
}
2020-10-27 10:39:46 +00:00
static int OpenDevTty(void) {
return open("/dev/tty", O_RDWR | O_NOCTTY);
}
static void AddHostFd(int fd) {
int i = m->fds.i++;
CHECK_NE(-1, (m->fds.p[i].fd = fd));
m->fds.p[i].cb = &kMachineFdCbHost;
}
int Emulator(int argc, char *argv[]) {
void *code;
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);
2020-10-27 10:39:46 +00:00
AddHostFd(STDIN_FILENO);
AddHostFd(STDOUT_FILENO);
AddHostFd(STDERR_FILENO);
if (tuimode) {
2020-10-27 10:39:46 +00:00
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);
2020-10-27 10:39:46 +00:00
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();
} else {
Tui();
}
if (action & RESTART) {
goto Restart;
}
}
if (tuimode) {
LeaveScreen();
}
if (printstats) {
fprintf(stderr, "taken: %,ld\n", taken);
fprintf(stderr, "ntaken: %,ld\n", ntaken);
2020-10-27 10:39:46 +00:00
fprintf(stderr, "ops: %,ld\n", opcount);
}
munmap(elf->ehdr, elf->size);
DisFree(dis);
return exitcode;
}
static void OnlyRunOnFirstCpu(void) {
uint64_t bs = 1;
sched_setaffinity(0, sizeof(bs), &bs);
}
int main(int argc, char *argv[]) {
2020-10-27 10:39:46 +00:00
if (!NoDebug()) showcrashreports();
pty = NewPty();
m = NewMachine();
2020-10-27 10:39:46 +00:00
m->mode = XED_MACHINE_MODE_LONG_64;
m->onbinbase = OnBinbase;
m->onlongbranch = OnLongBranch;
speed = 16;
SetXmmSize(2);
SetXmmDisp(kXmmHex);
if ((colorize = cancolor())) {
g_high.keyword = 155;
g_high.reg = 215;
g_high.literal = 182;
g_high.label = 221;
g_high.comment = 112;
g_high.quote = 215;
}
GetOpts(argc, argv);
2020-10-27 10:39:46 +00:00
xsigaction(SIGALRM, OnSigAlarm, 0, 0, 0);
if (optind == argc) PrintUsage(EX_USAGE, stderr);
return Emulator(argc, argv);
}