/*-*- 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/tty/tty.h" #include "libc/alg/arraylist2.h" #include "libc/assert.h" #include "libc/bits/safemacros.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.h" #include "libc/fmt/fmt.h" #include "libc/log/check.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.h" #include "libc/str/tpencode.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/dtoa/dtoa.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" #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\ -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 DUMPWIDTH 64 #define DISPWIDTH 80 #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 kXmmIntegral 0 #define kXmmDouble 1 #define kXmmFloat 2 #define kXmmDecimal 0 #define kXmmHex 1 #define kXmmChar 2 #define CTRL(C) ((C) ^ 0100) enum Mouse { kMouseLeftDown = 0, kMouseMiddleDown = 1, kMouseRightDown = 2, kMouseLeftUp = 4, kMouseMiddleUp = 5, kMouseRightUp = 6, kMouseLeftDrag = 32, kMouseMiddleDrag = 33, kMouseRightDrag = 34, kMouseWheelUp = 64, kMouseWheelDown = 65, }; 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; }; 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; }; struct Panel p[20]; }; }; 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 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; static int codezoom; static int readzoom; static int writezoom; static int stackzoom; static long rombase; static long codesize; static int64_t opstart; static int64_t codestart; static int64_t readstart; static int64_t mapsstart; static int64_t writestart; static int64_t stackstart; static int64_t framesstart; static int64_t breakpointsstart; static char *codepath; static void *onbusted; static char *statusmessage; static struct Machine *m; static struct MachinePty *pty; static struct Panels pan; static struct MachineState laststate; static struct Breakpoints breakpoints; static struct Elf elf[1]; static struct Dis dis[1]; static uint8_t xmmtype[16]; static uint8_t xmmsize[16]; 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 *b, double x) { return g_fmt(b, x); } 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 IsLongBranch(void) { return m->mode != XED_MODE_LONG && (m->xedd->op.dispatch == 0x0EA || m->xedd->op.dispatch == 0x09A || (m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 3) || (m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 5)); } 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[r]) { case kXmmIntegral: return 16 / xmmsize[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); } } static void UpdateXmmTypes(int regtype, int rmtype) { xmmtype[RexrReg(m->xedd->op.rde)] = regtype; if (IsModrmRegister(m->xedd->op.rde)) { xmmtype[RexbRm(m->xedd->op.rde)] = rmtype; } } static void UpdateXmmSizes(int regsize, int rmsize) { xmmsize[RexrReg(m->xedd->op.rde)] = regsize; if (IsModrmRegister(m->xedd->op.rde)) { xmmsize[RexbRm(m->xedd->op.rde)] = rmsize; } } static void UpdateXmmType(void) { switch (m->xedd->op.dispatch) { case 0x12E: // UCOMIS case 0x12F: // COMIS case 0x151: // SQRT case 0x152: // RSQRT case 0x153: // RCP case 0x158: // ADD case 0x159: // MUL case 0x15C: // SUB case 0x15D: // MIN case 0x15E: // DIV case 0x15F: // MAX case 0x1C2: // CMP if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { UpdateXmmTypes(kXmmDouble, kXmmDouble); } else { UpdateXmmTypes(kXmmFloat, kXmmFloat); } break; case 0x12A: // CVTPI2PS,CVTSI2SS,CVTPI2PD,CVTSI2SD if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { UpdateXmmSizes(8, 4); UpdateXmmTypes(kXmmDouble, kXmmIntegral); } else { UpdateXmmSizes(4, 4); UpdateXmmTypes(kXmmFloat, kXmmIntegral); } break; case 0x15A: // CVT{P,S}{S,D}2{P,S}{S,D} if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { UpdateXmmTypes(kXmmFloat, kXmmDouble); } else { UpdateXmmTypes(kXmmDouble, kXmmFloat); } break; case 0x15B: // CVT{,T}{DQ,PS}2{PS,DQ} UpdateXmmSizes(4, 4); if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 3) { UpdateXmmTypes(kXmmIntegral, kXmmFloat); } else { UpdateXmmTypes(kXmmFloat, kXmmIntegral); } break; case 0x17C: // HADD case 0x17D: // HSUB case 0x1D0: // ADDSUB if (Osz(m->xedd->op.rde)) { UpdateXmmTypes(kXmmDouble, kXmmDouble); } else { UpdateXmmTypes(kXmmFloat, kXmmFloat); } break; case 0x164: // PCMPGTB case 0x174: // PCMPEQB case 0x1D8: // PSUBUSB case 0x1DA: // PMINUB case 0x1DC: // PADDUSB case 0x1DE: // PMAXUB case 0x1E0: // PAVGB case 0x1E8: // PSUBSB case 0x1EC: // PADDSB case 0x1F8: // PSUBB case 0x1FC: // PADDB UpdateXmmSizes(1, 1); UpdateXmmTypes(kXmmIntegral, kXmmIntegral); break; case 0x165: // PCMPGTW case 0x175: // PCMPEQW case 0x171: // PSRLW,PSRAW,PSLLW case 0x1D1: // PSRLW case 0x1D5: // PMULLW case 0x1D9: // PSUBUSW case 0x1DD: // PADDUSW case 0x1E1: // PSRAW case 0x1E3: // PAVGW case 0x1E4: // PMULHUW case 0x1E5: // PMULHW case 0x1E9: // PSUBSW case 0x1EA: // PMINSW case 0x1ED: // PADDSW case 0x1EE: // PMAXSW case 0x1F1: // PSLLW case 0x1F6: // PSADBW case 0x1F9: // PSUBW case 0x1FD: // PADDW UpdateXmmSizes(2, 2); UpdateXmmTypes(kXmmIntegral, kXmmIntegral); break; case 0x166: // PCMPGTD case 0x176: // PCMPEQD case 0x172: // PSRLD,PSRAD,PSLLD case 0x1D2: // PSRLD case 0x1E2: // PSRAD case 0x1F2: // PSLLD case 0x1FA: // PSUBD case 0x1FE: // PADDD UpdateXmmSizes(4, 4); UpdateXmmTypes(kXmmIntegral, kXmmIntegral); break; case 0x173: // PSRLQ,PSRLQ,PSRLDQ,PSLLQ,PSLLDQ case 0x1D3: // PSRLQ case 0x1D4: // PADDQ case 0x1F3: // PSLLQ case 0x1F4: // PMULUDQ case 0x1FB: // PSUBQ UpdateXmmSizes(8, 8); UpdateXmmTypes(kXmmIntegral, kXmmIntegral); break; case 0x16B: // PACKSSDW case 0x1F5: // PMADDWD UpdateXmmSizes(4, 2); UpdateXmmTypes(kXmmIntegral, kXmmIntegral); break; case 0x163: // PACKSSWB case 0x167: // PACKUSWB UpdateXmmSizes(1, 2); UpdateXmmTypes(kXmmIntegral, kXmmIntegral); break; case 0x16F: // MOVDQA Vdq Wdq if (Osz(m->xedd->op.rde) && IsModrmRegister(m->xedd->op.rde)) { xmmtype[RexrReg(m->xedd->op.rde)] = xmmtype[RexbRm(m->xedd->op.rde)]; xmmsize[RexrReg(m->xedd->op.rde)] = xmmsize[RexbRm(m->xedd->op.rde)]; } break; default: return; } } 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(); SetupDraw(); 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 TuiSetup(void) { int y, x; bool report; static bool once; report = false; if (!once) { LOGF("loaded program %s\n%s", codepath, gc(FormatPml4t(m))); LoadSyms(); ResolveBreakpoints(); ioctl(ttyout, TCGETS, &oldterm); xsigaction(SIGALRM, OnSigAlarm, 0, 0, 0); xsigaction(SIGINT, OnSigInt, 0, 0, oldsig + 3); atexit(TtyRestore2); once = true; report = true; } 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) { static bool once; if (!once) { if (breakpoints.i) { LoadSyms(); ResolveBreakpoints(); } once = true; } } 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) { long i, j; for (i = start; i < end; ++i) { for (j = 0; j < 16; ++j) { if (m->xmm[i][j]) { 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 (IsXmmNonZero(8, 16)) { return 16; } else { return 8; } } else { return 0; } } static void SetupDraw(void) { int i, j, n, a, b, cpuy, ssey, dx[2], c2y[3], c3y[5]; for (i = 0; i < ARRAYLEN(pan.p); ++i) { n = pan.p[i].bottom - pan.p[i].top; for (j = 0; j < n; ++j) { free(pan.p[i].lines[j].p); } free(pan.p[i].lines); } 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]; a = 1 / 8. * tyn; b = 3 / 8. * tyn; c2y[0] = a * .7; c2y[1] = a * 2; c2y[2] = a * 2 + b; if (tyn - c2y[2] > 26) { c2y[1] -= tyn - c2y[2] - 26; c2y[2] = tyn - 26; } if (tyn - c2y[2] < 26) { c2y[2] = tyn - 26; } a = (tyn - (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; pan.disassembly.bottom = tyn; pan.disassembly.right = dx[0]; /* 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]; pan.display.bottom = tyn; 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]; pan.stack.bottom = tyn; pan.stack.right = txn; for (i = 0; i < ARRAYLEN(pan.p); ++i) { if (pan.p[i].top > pan.p[i].bottom) pan.p[i].top = pan.p[i].bottom = 0; if (pan.p[i].left > pan.p[i].right) pan.p[i].left = pan.p[i].right = 0; pan.p[i].lines = xcalloc(pan.p[i].bottom - pan.p[i].top, sizeof(struct Buffer)); } MachinePtyResize(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; CHECK_NE(-1, Dis(dis, m, GetIp(), m->ip, lines)); current = DisFind(dis, GetIp()); CHECK_NE(-1, current); return current; } 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"); } void DrawTerminal(struct Panel *p) { long i, y, yn; if (pty->conf & kMachinePtyBell) { if (!alarmed) { alarmed = true; setitimer(ITIMER_REAL, &((struct itimerval){{0, 0}, {0, 800000}}), NULL); } AppendStr(&pan.displayhr.lines[0], "\e[1m"); } AppendStr( &pan.displayhr.lines[0], gc(xasprintf("──────────TELETYPEWRITER──%s──%s──%s──%s", (pty->conf & kMachinePtyLed1) ? "\e[1;31m◎\e[0m" : "○", (pty->conf & kMachinePtyLed2) ? "\e[1;32m◎\e[0m" : "○", (pty->conf & kMachinePtyLed3) ? "\e[1;33m◎\e[0m" : "○", (pty->conf & kMachinePtyLed4) ? "\e[1;34m◎\e[0m" : "○"))); for (i = 36; i < pan.displayhr.right - pan.displayhr.left; ++i) { AppendWide(&pan.displayhr.lines[0], u'─'); } for (yn = MIN(pty->yn, p->bottom - p->top), y = 0; y < yn; ++y) { MachinePtyAppendLine(pty, p->lines + y, y); AppendStr(p->lines + y, "\e[0m"); } } void DrawDisplay(struct Panel *p) { switch (vidya) { case 7: DrawHr(&pan.displayhr, "MONOCHROME DISPLAY ADAPTER"); DrawMda(p, VirtualSend(m, gc(xmalloc(25 * 80 * 2)), 0xb0000, 25 * 80 * 2)); break; case 3: DrawHr(&pan.displayhr, "COLOR GRAPHICS ADAPTER"); DrawCga(p, VirtualSend(m, gc(xmalloc(25 * 80 * 2)), 0xb8000, 25 * 80 * 2)); break; default: 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]; 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[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 < xmmsize[r]; ++k) { itmp = xmm[j * xmmsize[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, xmmsize[r] * 8); } } else { int64toarray_radix10(SignExtend(ival, xmmsize[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; for (i = 0; i < MIN(16, MAX(0, p->bottom - p->top)); ++i) { DrawXmm(p, i, i); } } static void ScrollCode(struct Panel *p) { long i, n; n = p->bottom - p->top; i = GetIp() / DUMPWIDTH; if (!(codestart <= i && i < codestart + n)) { codestart = i; } } static void ScrollReadData(struct Panel *p) { long i, n, addr; n = p->bottom - p->top; i = m->readaddr / (DUMPWIDTH * (1 << readzoom)); if (!(readstart <= i && i < readstart + n)) { readstart = i - n / 3; } } static void ScrollWriteData(struct Panel *p) { long i, n, addr; n = p->bottom - p->top; i = m->writeaddr / DUMPWIDTH; if (!(writestart <= i && i < writestart + n)) { writestart = i - n / 3; } } static void ScrollStack(struct Panel *p) { long i, n; n = p->bottom - p->top; i = GetSp() / DUMPWIDTH; if (!(stackstart <= i && i < stackstart + n)) { stackstart = i; } } static uint8_t Downsample(uint8_t al, uint8_t bl, uint8_t cl, uint8_t dl) { int16_t ax, bx; bx = bl; bx += cl; bx *= 3; ax = al; ax += dl; ax += bx; ax += 4; ax >>= 3; al = ax; return al; } static uint8_t Sharpen(uint8_t al, uint8_t bl, uint8_t cl) { int16_t ax, bx, cx; ax = al; bx = bl; cx = cl; ax *= -1; bx *= +6; cx *= -1; ax += bx; ax += cx; ax += 2; ax >>= 2; return MIN(255, MAX(0, ax)); } static void DrawMemory(struct Panel *p, int zoom, long startline, long histart, long hiend) { char buf[16]; bool high, changed; long i, j, k, c, width; high = false; width = DUMPWIDTH * (1 << zoom); for (i = 0; i < p->bottom - p->top; ++i) { snprintf(buf, sizeof(buf), "%p ", (startline + i) * width); AppendStr(&p->lines[i], buf); for (j = 0; j < width; ++j) { k = (startline + 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 DrawMaps(struct Panel *p) { int i; char *text, *p1, *p2; 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; 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; 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; bp = Read64(r + 0); rp = Read64(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); } static void Redraw(void) { int i, j; 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"); DrawHr(&pan.mapshr, "MAPS"); 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, codezoom, codestart, GetIp(), GetIp() + m->xedd->length); DrawMemory(&pan.readdata, readzoom, readstart, m->readaddr, m->readaddr + m->readsize); DrawMemory(&pan.writedata, writezoom, writestart, m->writeaddr, m->writeaddr + m->writesize); DrawMemory(&pan.stack, stackzoom, stackstart, GetSp(), GetSp() + GetPointerWidth()); if (PrintPanels(ttyout, ARRAYLEN(pan.p), pan.p, tyn, txn) == -1) { LOGF("PrintPanels Interrupted"); CHECK_EQ(EINTR, errno); } if (statusmessage && nowl() < statusexpires) { TtyWriteString(statusmessage); } } static void ReactiveDraw(void) { if (tuimode) { m->ip -= m->xedd->length; SetupDraw(); Redraw(); m->ip += m->xedd->length; tick = speed; } } static void HandleAlarm(void) { alarmed = false; action &= ~ALARM; pty->conf &= ~kMachinePtyBell; 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) { return 0; } 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); pty->conf |= kMachinePtyBlinkcursor; if (tuimode) DisableMouseTracking(); for (;;) { ReactiveDraw(); if ((rc = read(fd, buf, PAGESIZE)) != -1) break; CHECK_EQ(EINTR, errno); HandleAppReadInterrupt(); } if (tuimode) EnableMouseTracking(); pty->conf &= ~kMachinePtyBlinkcursor; if (rc > 0) { MachinePtyWriteInput(pty, buf, rc); ReactiveDraw(); rc = MachinePtyRead(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) { if (!(rc = MachinePtyRead(pty, data, size))) { rc = ReadPtyFdDirect(fd, data, size); } return rc; } else { return 0; } } static void DrawTerminalOnly(void) { struct Buffer b; int i, y, yn, xn, tly, tlx, conf; conf = pty->conf & kMachinePtyNocursor; pty->conf |= kMachinePtyNocursor; memset(&b, 0, sizeof(b)); yn = MIN(tyn, pty->yn); xn = MIN(txn, pty->xn); 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, ' '); } MachinePtyAppendLine(pty, &b, y - tly); } AppendStr(&b, "\e[0m\e[K"); } AppendFmt(&b, "\e[%d;%dH", tly + pty->y + 1, tlx + pty->x + 1); write(ttyout, b.p, b.i); free(b.p); pty->conf |= conf; } static ssize_t OnPtyFdWritev(int fd, const struct iovec *iov, int iovlen) { int i; size_t size; for (size = i = 0; i < iovlen; ++i) { MachinePtyWrite(pty, iov[i].iov_base, iov[i].iov_len); size += iov[i].iov_len; } if (!tuimode) DrawTerminalOnly(); 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)); if (!(pty->conf & kMachinePtyNocanon)) c->c_iflag |= ICANON; if (!(pty->conf & kMachinePtyNoecho)) c->c_iflag |= ECHO; if (!(pty->conf & kMachinePtyNoopost)) c->c_oflag |= OPOST; return 0; } static int OnPtyFdTcsets(int fd, uint64_t request, struct termios *c) { if (c->c_iflag & ICANON) { pty->conf &= ~kMachinePtyNocanon; } else { pty->conf |= kMachinePtyNocanon; } if (c->c_iflag & ECHO) { pty->conf &= ~kMachinePtyNoecho; } else { pty->conf |= kMachinePtyNoecho; } if (c->c_oflag & OPOST) { pty->conf &= ~kMachinePtyNoopost; } else { pty->conf |= kMachinePtyNoopost; } 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) { die(); strcpy(systemfailure, "UNDEFINED INSTRUCTION"); LaunchDebuggerReactively(); } static void OnDecodeError(void) { strcpy(stpcpy(systemfailure, "DECODE: "), 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) { int64_t drive, head, track, sector, offset, size, addr; 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 = m->ax[0] * 512; offset = sector * 512 + track * 512 * 63 + head * 512 * 63 * 1024; if (0 <= sector && offset + size <= elf->mapsize) { addr = Read64(m->es) + Read16(m->bx); if (addr + size <= 0xffff0 + 0xffff + 1) { SetWriteAddr(m, addr, size); VirtualRecv(m, addr, elf->map + offset, size); m->ax[1] = 0x00; SetCarry(false); } else { m->ax[0] = 0x00; m->ax[1] = 0x02; SetCarry(true); } } else { 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) { MachinePtySetY(pty, m->dx[1]); MachinePtySetX(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 m->cx[0] = 7 | !!(pty->conf & kMachinePtyNocursor) << 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--;) { MachinePtyWrite(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); MachinePtyWrite(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; pty->conf |= kMachinePtyBlinkcursor; if (tuimode) DisableMouseTracking(); for (;;) { ReactiveDraw(); if ((rc = read(0, &b, 1)) != -1) break; CHECK_EQ(EINTR, errno); HandleAppReadInterrupt(); } if (tuimode) EnableMouseTracking(); pty->conf &= ~kMachinePtyBlinkcursor; 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]; if (Read32(m->dx) == 0x534D4150 && Read32(m->cx) == 24) { if (!Read32(m->bx)) { Write64(p + 000, 0); Write64(p + 010, BIGPAGESIZE); Write32(p + 014, 1); VirtualRecv(m, Read64(m->es) + Read16(m->di), p, 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 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); s = gc(xvasprintf(fmt, va)); va_end(va); n = strwidth(s); y = tyn - (n / txn + 1); x = txn / 2 - n / 2; free(statusmessage); statusmessage = xasprintf("\e[0m\e[%d;%dH%s", y + 1, x + 1, s); TtyWriteString(statusmessage); setitimer(ITIMER_REAL, &((struct itimerval){{0, 0}, {1, 0}}), NULL); statusexpires = nowl() + 1; } static void OnTurbo(void) { if (speed >= -1) { speed = MIN(0x40000000, MAX(1, speed) << 1); // 1..40mips skip } else { speed >>= 1; } SetStatus("speed %,d", speed); } static void OnSlowmo(void) { if (speed > 0) { speed >>= 1; } else { speed = MAX(-(5 * 1000), MIN(-1, speed) << 1); // 1ms..5s delay } 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[0]); for (i = 0; i < 16; ++i) { xmmtype[i] = t; } } static void SetXmmSize(int bytes) { unsigned i; for (i = 0; i < 16; ++i) { xmmsize[i] = bytes; } } static void SetXmmDisp(int disp) { xmmdisp = disp; } static void OnXmmSize(void) { SetXmmSize(CycleXmmSize(xmmsize[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 OnMouse(char *p) { enum Mouse e; int i, x, y, dy; 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; for (ep = 0, 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)) { ep = &pan.p[i]; break; } } if (ep) { dy = 3; switch (e) { case kMouseWheelUp: if (ep == &pan.disassembly) opstart -= dy; if (ep == &pan.code) codestart -= dy; if (ep == &pan.readdata) readstart -= dy; if (ep == &pan.writedata) writestart -= dy; if (ep == &pan.stack) stackstart -= dy; if (ep == &pan.maps) mapsstart = MAX(0, mapsstart - 1); if (ep == &pan.frames) framesstart = MAX(0, framesstart - 1); if (ep == &pan.breakpoints) { breakpointsstart = MAX(0, breakpointsstart - 1); } break; case kMouseWheelDown: if (ep == &pan.disassembly) opstart += dy; if (ep == &pan.code) codestart += dy; if (ep == &pan.readdata) readstart += dy; if (ep == &pan.writedata) writestart += dy; if (ep == &pan.stack) stackstart += dy; if (ep == &pan.maps) mapsstart += 1; if (ep == &pan.frames) framesstart += 1; if (ep == &pan.breakpoints) breakpointsstart += 1; break; default: break; } } } static void ReadKeyboard(void) { char buf[64], *p = buf; if (readansi(ttyin, buf, sizeof(buf)) == -1) { if (errno == EINTR) 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 noreturn 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) { LOGF("BREAK %p", breakpoints.p[bp].addr); tuimode = true; LoadInstruction(m); ExecuteInstruction(m); CheckFramePointer(); ops++; } else { action &= ~CONTINUE; for (;;) { LoadInstruction(m); ExecuteInstruction(m); KeepGoing: CheckFramePointer(); ops++; if (action || breakpoints.i) { if (action & EXIT) { LOGF("EXEC EXIT"); break; } if (action & INT) { LOGF("EXEC INT"); if (react) { LOGF("REACT"); action &= ~(INT | STEP | FINISH | NEXT); tuimode = true; } break; } if ((bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) { LOGF("BREAK %p", breakpoints.p[bp].addr); tuimode = true; 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); UpdateXmmType(); } 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) { ScrollCode(&pan.code); ScrollStack(&pan.stack); ScrollReadData(&pan.readdata); ScrollWriteData(&pan.writedata); } if (!(action & CONTINUE) || interactive) { tick = 0; GetDisIndex(); SetupDraw(); Redraw(); } if (action & FAILURE) { LOGF("TUI FAILURE"); PrintMessageBox(ttyout, systemfailure, tyn, txn); ReadKeyboard(); } 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()) { op = GetDisIndex(); ScrollOp(&pan.disassembly, op); VERBOSEF("%s", DisGetLine(dis, m, op)); CopyMachineState(&laststate); 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()) { ExecuteInstruction(m); if (!(action & CONTINUE) || interactive) { ScrollCode(&pan.code); ScrollStack(&pan.stack); ScrollReadData(&pan.readdata); ScrollWriteData(&pan.writedata); } if (IsLongBranch()) { Disassemble(); } } else { m->ip += m->xedd->length; action &= ~NEXT; action &= ~FINISH; action &= ~CONTINUE; } KeepGoing: CheckFramePointer(); ops++; if (!(action & CONTINUE)) { ScrollOp(&pan.disassembly, GetDisIndex()); if ((action & FINISH) && IsRet()) action &= ~FINISH; if (((action & NEXT) && IsRet()) || (action & FINISH)) { action &= ~NEXT; } } if ((action & (FINISH | NEXT | CONTINUE)) && (bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) { action &= ~(FINISH | NEXT | CONTINUE); LOGF("BREAK %p", breakpoints.p[bp].addr); break; } } } } 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, "hvtrRsb: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 'h': PrintUsage(EXIT_SUCCESS, stdout); default: PrintUsage(EX_USAGE, stderr); } } g_logfile = fopen(logpath, "a"); setvbuf(g_logfile, xmalloc(PAGESIZE), _IOLBF, PAGESIZE); } int Emulator(int argc, char *argv[]) { void *code; int rc, fd; codepath = argv[optind++]; pty = MachinePtyNew(); m->fds.p = xcalloc((m->fds.n = 8), sizeof(struct MachineFd)); Restart: action = 0; LoadProgram(m, codepath, argv + optind, environ, elf); m->fds.i = 3; m->fds.p[0].fd = STDIN_FILENO; m->fds.p[0].cb = &kMachineFdCbHost; m->fds.p[1].fd = STDOUT_FILENO; m->fds.p[1].cb = &kMachineFdCbHost; m->fds.p[2].fd = STDERR_FILENO; m->fds.p[2].cb = &kMachineFdCbHost; if (tuimode) { ttyin = isatty(0) ? 0 : open("/dev/tty", O_RDWR | O_NOCTTY); ttyout = isatty(1) ? 1 : open("/dev/tty", O_RDWR | O_NOCTTY); } else { ttyin = -1; ttyout = -1; } if (ttyout != -1) { atexit(TtyRestore1); xsigaction(SIGWINCH, OnSigWinch, 0, 0, 0); tyn = 24; txn = 80; GetTtySize(ttyout); if (isatty(0)) { m->fds.p[0].fd = 0; m->fds.p[0].cb = &kMachineFdCbPty; } if (isatty(1)) { m->fds.p[1].fd = 1; m->fds.p[1].cb = &kMachineFdCbPty; } if (isatty(2)) { m->fds.p[2].fd = 2; m->fds.p[2].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); fprintf(stderr, "ops: %,ld\n", ops); } 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[]) { m = NewMachine(); speed = 16; SetXmmSize(2); SetXmmDisp(kXmmHex); if (!NoDebug()) showcrashreports(); /* OnlyRunOnFirstCpu(); */ 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); if (optind == argc) PrintUsage(EX_USAGE, stderr); return Emulator(argc, argv); }