Add x86_64-linux-gnu emulator

I wanted a tiny scriptable meltdown proof way to run userspace programs
and visualize how program execution impacts memory. It helps to explain
how things like Actually Portable Executable works. It can show you how
the GCC generated code is going about manipulating matrices and more. I
didn't feel fully comfortable with Qemu and Bochs because I'm not smart
enough to understand them. I wanted something like gVisor but with much
stronger levels of assurances. I wanted a single binary that'll run, on
all major operating systems with an embedded GPL barrier ZIP filesystem
that is tiny enough to transpile to JavaScript and run in browsers too.

https://justine.storage.googleapis.com/emulator625.mp4
This commit is contained in:
Justine Tunney 2020-08-25 04:23:25 -07:00
parent 467504308a
commit f4f4caab0e
1052 changed files with 65667 additions and 7825 deletions

View file

@ -8,6 +8,7 @@
*/
#endif
#include "libc/calls/calls.h"
#include "libc/calls/hefty/copyfile.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/stat.h"
#include "libc/log/check.h"
@ -87,7 +88,7 @@ int main(int argc, char *argv[]) {
CHECK_NE(-1, close(fd));
t1 = dtime(CLOCK_REALTIME);
CHECK_NE(-1, copyfile(core, core2, false));
CHECK_NE(-1, copyfile(core, core2, 0));
t2 = dtime(CLOCK_REALTIME);
printf("%.6Lf\n", t2 - t1);

96
examples/cp.c Normal file
View file

@ -0,0 +1,96 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/calls/hefty/copyfile.h"
#include "libc/conv/conv.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/ok.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
#define USAGE \
" SRC... DST\n\
\n\
SYNOPSIS\n\
\n\
Copies Files\n\
\n\
FLAGS\n\
\n\
-?\n\
-h help\n\
-f force\n\
-n no clobber\n\
-a preserve all\n\
-p preserve owner and timestamps\n\
\n"
int flags;
bool force;
noreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "%s%s%s", "Usage: ", program_invocation_name, USAGE);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?hfnap")) != -1) {
switch (opt) {
case 'f':
force = true;
break;
case 'n':
flags |= COPYFILE_NOCLOBBER;
break;
case 'a':
case 'p':
flags |= COPYFILE_PRESERVE_OWNER;
flags |= COPYFILE_PRESERVE_TIMESTAMPS;
break;
case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
int cp(const char *src, const char *dst) {
if (endswith(dst, "/") || isdirectory(dst)) {
dst = gc(xasprintf("%s/%s", dst, basename));
}
if (!force && access(dst, W_OK) == -1 && errno != ENOENT) goto OnFail;
if (copyfile(src, dst, flags) == -1) goto OnFail;
return 0;
OnFail:
fprintf(stderr, "%s %s %s: %s\n", "error: cp", src, dst, strerror(errno));
return 1;
}
int main(int argc, char *argv[]) {
int i;
GetOpts(argc, argv);
if (argc - optind < 2) PrintUsage(EX_USAGE, stderr);
for (i = optind; i < argc - 1; ++i) {
if (cp(argv[i], argv[argc - 1]) == -1) {
return -1;
}
}
return 0;
}

View file

@ -8,120 +8,135 @@ EXAMPLES_MAINS_S = $(filter %.S,$(EXAMPLES_FILES))
EXAMPLES_MAINS_C = $(filter %.c,$(EXAMPLES_FILES))
EXAMPLES_MAINS_CC = $(filter %.cc,$(EXAMPLES_FILES))
EXAMPLES_SRCS = \
$(EXAMPLES_MAINS_S) \
$(EXAMPLES_MAINS_C) \
EXAMPLES_SRCS = \
$(EXAMPLES_MAINS_S) \
$(EXAMPLES_MAINS_C) \
$(EXAMPLES_MAINS_CC)
EXAMPLES_MAINS = \
$(EXAMPLES_MAINS_S) \
$(EXAMPLES_MAINS_C) \
EXAMPLES_MAINS = \
$(EXAMPLES_MAINS_S) \
$(EXAMPLES_MAINS_C) \
$(EXAMPLES_MAINS_CC)
EXAMPLES_OBJS = \
$(EXAMPLES_SRCS:%=o/$(MODE)/%.zip.o) \
$(EXAMPLES_MAINS_S:%.S=o/$(MODE)/%.o) \
$(EXAMPLES_MAINS_C:%.c=o/$(MODE)/%.o) \
EXAMPLES_OBJS = \
$(EXAMPLES_SRCS:%=o/$(MODE)/%.zip.o) \
$(EXAMPLES_MAINS_S:%.S=o/$(MODE)/%.o) \
$(EXAMPLES_MAINS_C:%.c=o/$(MODE)/%.o) \
$(EXAMPLES_MAINS_CC:%.cc=o/$(MODE)/%.o)
EXAMPLES_COMS = \
$(EXAMPLES_OBJS:%.o=%.com)
EXAMPLES_COMS = \
$(EXAMPLES_MAINS_S:%.S=o/$(MODE)/%.com) \
$(EXAMPLES_MAINS_C:%.c=o/$(MODE)/%.com) \
$(EXAMPLES_MAINS_CC:%.cc=o/$(MODE)/%.com)
EXAMPLES_ELFS = \
EXAMPLES_ELFS = \
$(EXAMPLES_OBJS:%.o=%.elf)
EXAMPLES_BINS = \
$(EXAMPLES_ELFS) \
$(EXAMPLES_COMS) \
EXAMPLES_BINS = \
$(EXAMPLES_ELFS) \
$(EXAMPLES_COMS) \
$(EXAMPLES_COMS:%=%.dbg)
EXAMPLES_DIRECTDEPS = \
APE_LIB \
DSP_CORE \
DSP_SCALE \
DSP_TTY \
LIBC_ALG \
LIBC_BITS \
LIBC_CALLS \
LIBC_CALLS_HEFTY \
LIBC_CONV \
LIBC_FMT \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_KERNELBASE \
LIBC_NT_NTDLL \
LIBC_NT_USER32 \
LIBC_OHMYPLUS \
LIBC_RAND \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
LIBC_TIME \
LIBC_TINYMATH \
LIBC_UNICODE \
LIBC_X \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_DTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_MUSL \
THIRD_PARTY_STB \
THIRD_PARTY_XED \
THIRD_PARTY_ZLIB \
EXAMPLES_DIRECTDEPS = \
APE_LIB \
DSP_CORE \
DSP_SCALE \
DSP_TTY \
LIBC_ALG \
LIBC_BITS \
LIBC_CALLS \
LIBC_CALLS_HEFTY \
LIBC_CONV \
LIBC_FMT \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_KERNELBASE \
LIBC_NT_NTDLL \
LIBC_NT_USER32 \
LIBC_OHMYPLUS \
LIBC_RAND \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
LIBC_TIME \
LIBC_TINYMATH \
LIBC_UNICODE \
LIBC_X \
LIBC_ZIPOS \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_DTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_MUSL \
THIRD_PARTY_STB \
THIRD_PARTY_XED \
THIRD_PARTY_ZLIB \
TOOL_VIZ_LIB
EXAMPLES_DEPS := \
EXAMPLES_DEPS := \
$(call uniq,$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x))))
o/$(MODE)/examples/examples.pkg: \
$(EXAMPLES_OBJS) \
$(THIRD_PARTY_DUKTAPE_A).pkg \
o/$(MODE)/examples/examples.pkg: \
$(EXAMPLES_OBJS) \
$(THIRD_PARTY_DUKTAPE_A).pkg \
$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/examples/unbourne.o: \
OVERRIDE_CPPFLAGS += \
o/$(MODE)/examples/unbourne.o: \
OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
o/$(MODE)/examples/%.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/%.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
o/$(MODE)/examples/%.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/%.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE)
@$(APELINK)
o/$(MODE)/examples/%.elf: \
$(EXAMPLES_DEPS) \
$(THIRD_PARTY_DUKTAPE) \
o/$(MODE)/examples/%.o \
$(CRT) \
o/$(MODE)/examples/%.elf: \
$(EXAMPLES_DEPS) \
$(THIRD_PARTY_DUKTAPE) \
o/$(MODE)/examples/%.o \
$(CRT) \
$(ELF)
@$(ELFLINK)
$(EXAMPLES_OBJS): examples/examples.mk
o/$(MODE)/examples/hellojs.com.dbg: \
$(EXAMPLES_DEPS) \
$(THIRD_PARTY_DUKTAPE) \
o/$(MODE)/examples/hellojs.o \
o/$(MODE)/examples/hello.js.zip.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
o/$(MODE)/examples/hellojs.com.dbg: \
$(EXAMPLES_DEPS) \
$(THIRD_PARTY_DUKTAPE) \
o/$(MODE)/examples/hellojs.o \
o/$(MODE)/examples/hello.js.zip.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE)
@$(APELINK)
o/$(MODE)/examples/ispell.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/ispell.o \
o/$(MODE)/usr/share/dict/words.zip.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
o/$(MODE)/examples/ispell.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/ispell.o \
o/$(MODE)/usr/share/dict/words.zip.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE)
@$(APELINK)
o/$(MODE)/examples/nesemu1.com.dbg: \
$(EXAMPLES_DEPS) \
$(THIRD_PARTY_DUKTAPE) \
o/$(MODE)/examples/nesemu1.o \
o/$(MODE)/usr/share/rom/mario.nes.zip.o \
o/$(MODE)/usr/share/rom/zelda.nes.zip.o \
o/$(MODE)/usr/share/rom/tetris.nes.zip.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE)
@$(APELINK)
@ -129,7 +144,11 @@ o/$(MODE)/usr/share/dict/words: usr/share/dict/words.gz
@$(MKDIR) $(dir $@)
@$(GZ) $(ZFLAGS) <$< >$@
o/$(MODE)/examples/ugh.ok: o/$(MODE)/examples/wut.com
$<
touch $@
.PHONY: o/$(MODE)/examples
o/$(MODE)/examples: \
o/$(MODE)/examples/package \
o/$(MODE)/examples: \
o/$(MODE)/examples/package \
$(EXAMPLES_BINS)

View file

@ -28,6 +28,7 @@ static const char *magic_;
void OnInterrupt(int sig) {
showprogress_ = true;
}
void ShowProgress(uintmax_t i, uintmax_t j, uintmax_t n) {
fprintf(stderr, "%s%,40jd%,40jd%,40jd\r\n", magic_, i, j, n);
showprogress_ = false;

View file

@ -8,6 +8,7 @@
*/
#endif
#include "libc/errno.h"
#include "libc/log/log.h"
#include "libc/stdio/stdio.h"
int main() {
@ -19,5 +20,5 @@ int main() {
* have that string consist solely of directives.
*/
printf("%s\n", "hello world");
return errno;
return 0;
}

View file

@ -58,7 +58,7 @@ Contact: antirez@gmail.com\"\n\
#define _GNU_SOURCE
#include "libc/alg/alg.h"
#include "libc/alg/arraylist.h"
#include "libc/alg/arraylist2.h"
#include "libc/calls/calls.h"
#include "libc/calls/termios.h"
#include "libc/calls/weirdtypes.h"
@ -879,7 +879,7 @@ struct abuf {
};
static void abAppend(struct abuf *ab, const char *s, int len) {
concat(ab, s, len);
CONCAT(&ab->p, &ab->i, &ab->n, s, len);
}
/* This function writes the whole screen using VT100 escape characters

View file

@ -4,11 +4,13 @@
/* TRADEMARKS ARE OWNED BY THEIR RESPECTIVE OWNERS LAWYERCATS LUV TAUTOLOGIES */
/* https://bisqwit.iki.fi/jutut/kuvat/programming_examples/nesemu1/nesemu1.cc */
#include "dsp/core/core.h"
#include "dsp/core/half.h"
#include "dsp/core/illumination.h"
#include "dsp/scale/scale.h"
#include "dsp/tty/itoa8.h"
#include "dsp/tty/quant.h"
#include "dsp/tty/tty.h"
#include "libc/alg/arraylist2.h"
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
@ -16,6 +18,7 @@
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/winsize.h"
#include "libc/conv/conv.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/inttypes.h"
@ -30,6 +33,8 @@
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.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/o.h"
@ -37,24 +42,77 @@
#include "libc/sysv/consts/sig.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "libc/zip.h"
#include "libc/zipos/zipos.h"
#include "third_party/getopt/getopt.h"
#include "tool/viz/lib/knobs.h"
#define DYN 240
#define DXN 256
#define FPS 60.0988
#define HZ 1789773
#define KEYHZ 20
#define GAMMA 2.2
#define USAGE \
" [ROM] [FMV]\n\
\n\
SYNOPSIS\n\
\n\
Emulates NES Video Games in Terminal\n\
\n\
FLAGS\n\
\n\
-A ansi color mode\n\
-t normal color mode\n\
-x xterm256 color mode\n\
-4 unicode character set\n\
-3 ibm cp437 character set\n\
-1 ntsc crt artifact emulation\n\
-h\n\
-? shows this information\n\
\n\
KEYBOARD\n\
\n\
We support Emacs / Mac OS X control key bindings. We also support\n\
Vim. We support arrow keys. We also support WASD QWERTY & Dvorak.\n\
The 'A' button is mapped to SPACE. The 'B' button is mapped to b.\n\
Lastly TAB is SELECT and ENTER is START.\n\
\n\
Teletypewriters are naturally limited in terms of keyboard input.\n\
They don't exactly have n-key rollover. More like 1-key rollover.\n\
\n\
Try tapping rather than holding keys. You can tune the activation\n\
duration by pressing '8' and '9'. You can also adjust the keyboard\n\
repeat delay in your operating system settings to make it shorter.\n\
\n\
Ideally we should send patches to all the terms that introduces a\n\
new ANSI escape sequences for key down / key up events. It'd also\n\
be great to have inband audio with terminals too.\n\
\n\
GRAPHICS\n\
\n\
The '1' key toggles CRT monitor artifact emulation, which can make\n\
some games like Zelda II look better. The '3' and '4' keys toggle\n\
the selection of UNICODE block characters.\n\
\n\
ZIP\n\
\n\
This executable is also a ZIP archive. If you change the extension\n\
then you can modify its inner structure, to place roms inside it.\n\
\n\
AUTHORS\n\
\n\
Joel Yliluoma <http://iki.fi/bisqwit/>\n\
Justine Tunney <jtunney@gmail.com/>\n\
\n\
\n"
#define DYN 240
#define DXN 256
#define FPS 60.0988
#define HZ 1789773
#define GAMMA 2.2
#define CTRL(C) ((C) ^ 0100)
#define ALT(C) ((033 << 010) | (C))
static const char* inputfn;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef int8_t s8;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
static const struct itimerval kNesFps = {
{0, 1. / FPS * 1e6},
@ -75,9 +133,19 @@ struct Audio {
int16_t p[FRAMESIZE];
};
struct Status {
int wait;
char text[80];
};
struct ZipGames {
size_t i, n;
char** p;
};
static int frame_;
static int drain_;
static int playfd_;
static bool piped_;
static int devnull_;
static int playpid_;
static bool exited_;
@ -87,22 +155,32 @@ static size_t vtsize_;
static bool artifacts_;
static long tyn_, txn_;
static const char* ffplay_;
static struct Audio audio_;
static struct TtyRgb* ttyrgb_;
static struct Frame vf_[2];
static struct Audio audio_;
static const char* inputfn_;
static struct Status status_;
static struct TtyRgb* ttyrgb_;
static unsigned char *R, *G, *B;
static struct ZipGames zipgames_;
static struct Action arrow_, button_;
static struct SamplingSolution* asx_;
static struct SamplingSolution* ssy_;
static struct SamplingSolution* ssx_;
static unsigned char pixels_[3][DYN][DXN];
static unsigned char palette_[3][64][512][3];
static int joy_current_[2] = {0, 0};
static int joy_next_[2] = {0, 0};
static int joypos_[2] = {0, 0};
static int joy_current_[2], joy_next_[2], joypos_[2];
static int Clamp(int v) { return MAX(0, MIN(255, v)); }
static double FixGamma(double x) { return tv2pcgamma(x, GAMMA); }
static int keyframes_ = 20;
static enum TtyBlocksSelection blocks_ = kTtyBlocksUnicode;
static enum TtyQuantizationAlgorithm quant_ = kTtyQuantTrue;
static int Clamp(int v) {
return MAX(0, MIN(255, v));
}
static double FixGamma(double x) {
return tv2pcgamma(x, GAMMA);
}
void InitPalette(void) {
// The input value is a NES color index (with de-emphasis bits).
@ -110,7 +188,7 @@ void InitPalette(void) {
// We need RGB values. To produce a RGB value, we emulate the NTSC circuitry.
double A[3] = {-1.109, -.275, .947};
double B[3] = {1.709, -.636, .624};
double rgbc[3], lightbulb[3][3], rgbd65[3];
double rgbc[3], lightbulb[3][3], rgbd65[3], sc[2];
int o, u, r, c, b, p, y, i, l, q, e, p0, p1, pixel;
signed char volt[] = "\372\273\32\305\35\311I\330D\357\175\13D!}N";
GetChromaticAdaptationMatrix(lightbulb, kIlluminantC, kIlluminantD65);
@ -119,9 +197,7 @@ void InitPalette(void) {
for (p1 = 0; p1 < 64; ++p1) {
for (u = 0; u < 3; ++u) {
// Calculate the luma and chroma by emulating the relevant circuits:
y = 0;
i = 0;
q = 0;
y = i = q = 0;
// 12 samples of NTSC signal constitute a color.
for (p = 0; p < 12; ++p) {
// Sample either the previous or the current pixel.
@ -140,9 +216,10 @@ void InitPalette(void) {
b = 40 + volt[(c > 12 * ((c + 8 + p) % 12 < 6)) +
2 * !(0451326 >> p / 2 * 3 & e) + l];
// Ideal TV NTSC demodulator?
sincos(M_PI * p / 6, &sc[0], &sc[1]);
y += b;
i += b * round(cos(M_PI * p / 6) * 5909);
q += b * round(sin(M_PI * p / 6) * 5909);
i += b * sc[1] * 5909;
q += b * sc[0] * 5909;
}
// Converts YIQ to RGB
// Store color at subpixel precision
@ -161,31 +238,62 @@ static void WriteStringNow(const char* s) {
ttywrite(STDOUT_FILENO, s, strlen(s));
}
void CleanupTerminal(void) {
ttyraw((enum TtyRawFlags)(-1u));
ttyshowcursor(STDOUT_FILENO);
void Exit(int rc) {
WriteStringNow("\r\n\e[0m\e[J");
if (rc && errno) {
fprintf(stderr, "%s%s\r\n", "error: ", strerror(errno));
}
exit(rc);
}
void OnTimer(void) { timeout_ = true; }
void OnResize(void) { resized_ = true; }
void OnCtrlC(void) { exited_ = true; }
void OnSigChld(void) { piped_ = true, playpid_ = 0; }
void Cleanup(void) {
ttyraw((enum TtyRawFlags)(-1u));
ttyshowcursor(STDOUT_FILENO);
if (playpid_) kill(playpid_, SIGTERM), sched_yield();
}
void OnTimer(void) {
timeout_ = true; // also sends EINTR to poll()
}
void OnResize(void) {
resized_ = true;
}
void OnPiped(void) {
exited_ = true;
}
void OnCtrlC(void) {
drain_ = exited_ = true;
}
void OnSigChld(void) {
exited_ = true, playpid_ = 0;
}
void InitFrame(struct Frame* f) {
f->p = f->w = f->mem = (char*)realloc(f->mem, vtsize_);
}
long ChopAxis(long dn, long sn) {
while (HALF(sn) > dn) {
sn = HALF(sn);
}
return sn;
}
void GetTermSize(void) {
struct winsize wsize_;
wsize_.ws_row = 25;
wsize_.ws_col = 80;
getttysize(STDOUT_FILENO, &wsize_);
tyn_ = wsize_.ws_row * 2;
txn_ = wsize_.ws_col * 2;
getttysize(STDIN_FILENO, &wsize_);
FreeSamplingSolution(ssy_);
FreeSamplingSolution(ssx_);
ssy_ = ComputeSamplingSolution(tyn_, DYN, 0, 0, 2);
ssx_ = ComputeSamplingSolution(txn_, DXN, 0, 0, 0);
tyn_ = wsize_.ws_row * 2;
txn_ = wsize_.ws_col * 2;
ssy_ = ComputeSamplingSolution(tyn_, ChopAxis(tyn_, DYN), 0, 0, 2);
ssx_ = ComputeSamplingSolution(txn_, ChopAxis(txn_, DXN), 0, 0, 0);
R = (unsigned char*)realloc(R, tyn_ * txn_);
G = (unsigned char*)realloc(G, tyn_ * txn_);
B = (unsigned char*)realloc(B, tyn_ * txn_);
@ -216,20 +324,23 @@ bool TrySpeaker(const char* prog, char* const* args) {
void IoInit(void) {
GetTermSize();
xsigaction(SIGINT, (void*)OnCtrlC, 0, 0, NULL);
xsigaction(SIGPIPE, (void*)OnPiped, 0, 0, NULL);
xsigaction(SIGWINCH, (void*)OnResize, 0, 0, NULL);
xsigaction(SIGALRM, (void*)OnTimer, 0, 0, NULL);
xsigaction(SIGCHLD, (void*)OnSigChld, 0, 0, NULL);
setitimer(ITIMER_REAL, &kNesFps, NULL);
ttyhidecursor(STDOUT_FILENO);
ttyraw(kTtySigs);
ttyquantinit(kTtyQuantTrue, kTtyQuantRgb, kTtyBlocksUnicode);
atexit(CleanupTerminal);
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
atexit(Cleanup);
}
void SystemFailure(void) {
fputs("error: ", stderr);
fputs(strerror(errno), stderr);
fputc('\n', stderr);
exit(7);
void SetStatus(const char* fmt, ...) {
va_list va;
va_start(va, fmt);
vsnprintf(status_.text, sizeof(status_.text), fmt, va);
va_end(va);
status_.wait = FPS / 2;
}
void ReadKeyboard(void) {
@ -238,6 +349,7 @@ void ReadKeyboard(void) {
ssize_t i, rc;
memset(b, -1, sizeof(b));
if ((rc = read(STDIN_FILENO, b, 16)) != -1) {
if (!rc) exited_ = true;
for (i = 0; i < rc; ++i) {
ch = b[i];
if (b[i] == '\e') {
@ -263,71 +375,105 @@ void ReadKeyboard(void) {
}
}
switch (ch) {
case '1':
artifacts_ = !artifacts_;
InitPalette();
break;
case ' ':
button_.code = 0b00100000; // A
button_.wait = KEYHZ;
button_.wait = keyframes_;
break;
case 'b':
button_.code = 0b00010000; // B
button_.wait = KEYHZ;
button_.wait = keyframes_;
break;
case '\r': // enter
button_.code = 0b10000000; // START
button_.wait = KEYHZ;
button_.wait = keyframes_;
break;
case '\t': // tab
button_.code = 0b01000000; // SELECT
button_.wait = KEYHZ;
button_.wait = keyframes_;
break;
case 'k': // vim
case 'w': // wasd qwerty
case ',': // wasd dvorak
case CTRL('P'): // emacs
arrow_.code = 0b00000100; // UP
arrow_.wait = KEYHZ;
arrow_.wait = keyframes_;
break;
case 'j': // vim
case 's': // wasd qwerty
case 'o': // wasd dvorak
case CTRL('N'): // emacs
arrow_.code = 0b00001000; // DOWN
arrow_.wait = KEYHZ;
arrow_.wait = keyframes_;
break;
case 'h': // vim
case 'a': // wasd qwerty & dvorak
case CTRL('B'): // emacs
arrow_.code = 0b00000010; // LEFT
arrow_.wait = KEYHZ;
arrow_.wait = keyframes_;
break;
case 'l': // vim
case 'd': // wasd qwerty
case 'e': // wasd dvorak
case CTRL('F'): // emacs
arrow_.code = 0b00000001; // RIGHT
arrow_.wait = KEYHZ;
arrow_.wait = keyframes_;
break;
case 'A': // ansi 4-bit color mode
quant_ = kTtyQuantAnsi;
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
SetStatus("ansi color");
break;
case 'x': // xterm256 color mode
ttyquantinit(kTtyQuantXterm256, kTtyQuantRgb, kTtyBlocksUnicode);
quant_ = kTtyQuantXterm256;
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
SetStatus("xterm256 color");
break;
case 't': // ansi 24bit color mode
ttyquantinit(kTtyQuantTrue, kTtyQuantRgb, kTtyBlocksUnicode);
quant_ = kTtyQuantTrue;
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
SetStatus("24-bit color");
break;
case '1':
artifacts_ = !artifacts_;
InitPalette();
SetStatus("artifacts %s", artifacts_ ? "on" : "off");
break;
case '3': // oldskool ibm unicode rasterization
blocks_ = kTtyBlocksCp437;
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
SetStatus("IBM CP437");
break;
case '4': // newskool unicode rasterization
blocks_ = kTtyBlocksUnicode;
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
SetStatus("UNICODE");
break;
case '8':
keyframes_ = MAX(1, keyframes_ - 1);
SetStatus("%d key frames", keyframes_);
break;
case '9':
keyframes_ = keyframes_ + 1;
SetStatus("%d key frames", keyframes_);
break;
default:
break;
}
}
} else {
SystemFailure();
}
}
bool HasVideo(struct Frame* f) { return f->w < f->p; }
bool HasPendingVideo(void) { return HasVideo(&vf_[0]) || HasVideo(&vf_[1]); }
bool HasPendingAudio(void) { return playpid_ && audio_.i; }
bool HasVideo(struct Frame* f) {
return f->w < f->p;
}
bool HasPendingVideo(void) {
return HasVideo(&vf_[0]) || HasVideo(&vf_[1]);
}
bool HasPendingAudio(void) {
return playpid_ && audio_.i;
}
struct Frame* FlipFrameBuffer(void) {
frame_ = !frame_;
@ -341,8 +487,10 @@ void TransmitVideo(void) {
if (!HasVideo(f)) f = FlipFrameBuffer();
if ((rc = write(STDOUT_FILENO, f->w, f->p - f->w)) != -1) {
f->w += rc;
} else {
SystemFailure();
} else if (errno == EPIPE) {
Exit(0);
} else if (errno != EINTR) {
Exit(1);
}
}
@ -353,19 +501,36 @@ void TransmitAudio(void) {
rc /= sizeof(short);
memmove(audio_.p, audio_.p + rc, (audio_.i - rc) * sizeof(short));
audio_.i -= rc;
} else {
SystemFailure();
} else if (errno == EPIPE) {
Exit(0);
} else if (errno != EINTR) {
Exit(1);
}
}
void ScaleVideoFrameToTeletypewriter(void) {
long y, x;
GyaradosUint8(tyn_, txn_, R, DYN, DXN, pixels_[0], tyn_, txn_, DYN, DXN, 0,
255, ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, G, DYN, DXN, pixels_[1], tyn_, txn_, DYN, DXN, 0,
255, ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, B, DYN, DXN, pixels_[2], tyn_, txn_, DYN, DXN, 0,
255, ssy_, ssx_, true);
long y, x, yn, xn;
yn = DYN, xn = DXN;
while (HALF(yn) > tyn_ || HALF(xn) > txn_) {
if (HALF(xn) > txn_) {
Magikarp2xX(DYN, DXN, pixels_[0], yn, xn);
Magikarp2xX(DYN, DXN, pixels_[1], yn, xn);
Magikarp2xX(DYN, DXN, pixels_[2], yn, xn);
xn = HALF(xn);
}
if (HALF(yn) > tyn_) {
Magikarp2xY(DYN, DXN, pixels_[0], yn, xn);
Magikarp2xY(DYN, DXN, pixels_[1], yn, xn);
Magikarp2xY(DYN, DXN, pixels_[2], yn, xn);
yn = HALF(yn);
}
}
GyaradosUint8(tyn_, txn_, R, DYN, DXN, pixels_[0], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, G, DYN, DXN, pixels_[1], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, B, DYN, DXN, pixels_[2], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
for (y = 0; y < tyn_; ++y) {
for (x = 0; x < txn_; ++x) {
ttyrgb_[y * txn_ + x] =
@ -382,15 +547,10 @@ void KeyCountdown(struct Action* a) {
}
}
void DrainAndExit(void) {
while (HasPendingVideo()) TransmitVideo();
WriteStringNow("\r\n\e[0m\e[J");
exit(0);
}
void PollAndSynchronize(void) {
struct pollfd fds[3];
do {
errno = 0;
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
fds[1].fd = HasPendingVideo() ? STDOUT_FILENO : -1;
@ -402,10 +562,15 @@ void PollAndSynchronize(void) {
if (fds[1].revents & (POLLOUT | POLLERR)) TransmitVideo();
if (fds[2].revents & (POLLOUT | POLLERR)) TransmitAudio();
} else if (errno != EINTR) {
SystemFailure();
Exit(1);
}
if (exited_) {
DrainAndExit();
if (drain_) {
while (HasPendingVideo()) {
TransmitVideo();
}
}
Exit(0);
}
if (resized_) {
resized_ = false;
@ -429,6 +594,11 @@ void Raster(void) {
f->p = f->w = f->mem;
f->p = stpcpy(f->p, "\e[0m\e[H");
f->p = ttyraster(f->p, ttyrgb_, tyn_, txn_, bg, fg);
if (status_.wait) {
status_.wait--;
f->p = stpcpy(f->p, "\e[0m\e[H");
f->p = stpcpy(f->p, status_.text);
}
CHECK_LT(f->p - f->mem, vtsize_);
PollAndSynchronize();
}
@ -443,13 +613,8 @@ void FlushScanline(unsigned py) {
}
static void PutPixel(unsigned px, unsigned py, unsigned pixel, int offset) {
static bool once;
static unsigned prev;
unsigned rgb;
if (!once) {
InitPalette();
once = true;
}
static unsigned prev;
pixels_[0][py][px] = palette_[offset][prev % 64][pixel][2];
pixels_[1][py][px] = palette_[offset][prev % 64][pixel][1];
pixels_[2][py][px] = palette_[offset][prev % 64][pixel][0];
@ -491,8 +656,12 @@ struct RegBit {
data = (data & ~(mask << bitno)) | ((nbits > 1 ? v & mask : !!v) << bitno);
return *this;
}
operator unsigned() const { return (data >> bitno) & mask; }
RegBit& operator++() { return *this = *this + 1; }
operator unsigned() const {
return (data >> bitno) & mask;
}
RegBit& operator++() {
return *this = *this + 1;
}
unsigned operator++(int) {
unsigned r = *this;
++*this;
@ -602,8 +771,12 @@ bool intr = false;
template <bool write>
u8 MemAccess(u16 addr, u8 v = 0);
u8 RB(u16 addr) { return MemAccess<0>(addr); }
u8 WB(u16 addr, u8 v) { return MemAccess<1>(addr, v); }
u8 RB(u16 addr) {
return MemAccess<0>(addr);
}
u8 WB(u16 addr, u8 v) {
return MemAccess<1>(addr, v);
}
void Tick();
} // namespace CPU
@ -922,8 +1095,8 @@ void RenderPixel() {
void ReadToolAssistedSpeedrunRobotKeys() {
static FILE* fp;
if (!fp && !isempty(inputfn)) {
fp = fopen(inputfn, "rb");
if (!fp && !isempty(inputfn_)) {
fp = fopen(inputfn_, "rb");
}
if (fp) {
static unsigned ctrlmask = 0;
@ -1039,7 +1212,9 @@ bool ChannelsEnabled[5];
bool PeriodicIRQ;
bool DMC_IRQ;
bool count(int& v, int reset) { return --v < 0 ? (v = reset), true : false; }
bool count(int& v, int reset) {
return --v < 0 ? (v = reset), true : false;
}
struct channel {
int length_counter, linear_counter, address, envelope;
@ -1119,9 +1294,11 @@ struct channel {
// Note: Re-entrant! But not recursive, because even
// the shortest wave length is greater than the read time.
// TODO: proper clock
if (ch.reg.WaveLength > 20)
for (unsigned t = 0; t < 3; ++t)
CPU::RB(u16(ch.address) | 0x8000); // timing
if (ch.reg.WaveLength > 20) {
for (unsigned t = 0; t < 3; ++t) {
CPU::RB(u16(ch.address) | 0x8000); // timing
}
}
ch.hold = CPU::RB(u16(ch.address++) | 0x8000); // Fetch byte
ch.phase = 8;
--ch.length_counter;
@ -1357,13 +1534,19 @@ union { /* Status flags: */
RegBit<7> N; // negative
} P;
u16 wrap(u16 oldaddr, u16 newaddr) { return (oldaddr & 0xFF00) + u8(newaddr); }
u16 wrap(u16 oldaddr, u16 newaddr) {
return (oldaddr & 0xFF00) + u8(newaddr);
}
void Misfire(u16 old, u16 addr) {
u16 q = wrap(old, addr);
if (q != addr) RB(q);
}
u8 Pop() { return RB(0x100 | u8(++S)); }
void Push(u8 v) { WB(0x100 | u8(S--), v); }
u8 Pop() {
return RB(0x100 | u8(++S));
}
void Push(u8 v) {
WB(0x100 | u8(S--), v);
}
template <u16 op> // Execute a single CPU instruction, defined by opcode "op".
void Ins() { // With template magic, the compiler will literally synthesize
@ -1503,47 +1686,59 @@ void Op() {
} // namespace CPU
int main(int argc, char** argv) {
char* GetLine(void) {
static char* line;
static size_t linesize;
if (getline(&line, &linesize, stdin) > 0) {
return chomp(line);
} else {
return NULL;
}
}
int PlayGame(const char* romfile, const char* opt_tasfile) {
FILE* fp;
inputfn_ = opt_tasfile;
if (argc <= 1 || (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-?") == 0 ||
strcmp(argv[1], "--help") == 0)) {
fprintf(stderr, "%s%s%s\n", "Usage: ", argv[0], " ROM [FMV]");
exit(1);
if (!(fp = fopen(romfile, "rb"))) {
fprintf(stderr, "%s%s\n", "failed to open: ", romfile);
return 2;
}
// Open the ROM file specified on commandline
fp = fopen(argv[1], "rb"); /* your .nes file */
inputfn = argc >= 3 ? argv[2] : NULL; /* some tas thing */
if (!fp) {
fprintf(stderr, "%s%s\n", "not a nes rom file: ", argv[1]);
exit(2);
}
if (!(fgetc(fp) == 'N' && fgetc(fp) == 'E' && fgetc(fp) == 'S' &&
fgetc(fp) == CTRL('Z'))) {
fprintf(stderr, "%s%s\n", "not a nes rom file: ", argv[1]);
exit(3);
fprintf(stderr, "%s%s\n", "not a nes rom file: ", romfile);
return 3;
}
InitPalette();
// open speaker
// todo: this needs plenty of work
devnull_ = open("/dev/null", O_WRONLY);
ffplay_ = commandvenv("FFPLAY", "ffplay");
if (devnull_ != -1 && ffplay_) {
if ((ffplay_ = commandvenv("FFPLAY", "ffplay"))) {
const char* args[] = {
"ffplay", "-nodisp", "-loglevel", "quiet", "-fflags", "nobuffer", "-ac",
"1", "-ar", "1789773", "-f", "s16le", "pipe:", NULL,
};
TrySpeaker(ffplay_, (char* const*)args);
} else {
fputs("\nWARNING\n\
\n\
Need `ffplay` command to play audio\n\
Try `sudo apt install ffmpeg` on Linux\n\
You can specify it on `PATH` or in `FFPLAY`\n\
\n\
Press enter to continue without sound: ",
stdout);
fflush(stdout);
GetLine();
}
// Read the ROM file header
u8 rom16count = fgetc(fp);
u8 vrom8count = fgetc(fp);
u8 ctrlbyte = fgetc(fp);
u8 mappernum = fgetc(fp) | (ctrlbyte >> 4);
u8 mappernum = fgetc(fp) | ctrlbyte >> 4;
fgetc(fp);
fgetc(fp);
@ -1578,3 +1773,96 @@ int main(int argc, char** argv) {
// Run the CPU until the program is killed.
for (;;) CPU::Op();
}
noreturn void PrintUsage(int rc, FILE* f) {
fprintf(f, "%s%s%s", "Usage: ", program_invocation_name, USAGE);
exit(rc);
}
void GetOpts(int argc, char* argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?hAxt134")) != -1) {
switch (opt) {
case 'A':
quant_ = kTtyQuantAnsi;
break;
case 'x':
quant_ = kTtyQuantXterm256;
break;
case 't':
quant_ = kTtyQuantTrue;
break;
case '1':
artifacts_ = !artifacts_;
break;
case '3':
blocks_ = kTtyBlocksCp437;
break;
case '4':
blocks_ = kTtyBlocksUnicode;
break;
case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
size_t FindZipGames(void) {
char* name;
struct Zipos* zipos;
size_t i, cf, namesize;
if ((zipos = __zipos_get())) {
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
i < ZIP_CDIR_RECORDS(zipos->cdir);
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
if ((name = strndup(ZIP_CFILE_NAME(zipos->map + cf),
ZIP_CFILE_NAMESIZE(zipos->map + cf))) &&
endswith(name, ".nes")) {
APPEND(&zipgames_.p, &zipgames_.i, &zipgames_.n, &name);
} else {
free(name);
}
}
}
return zipgames_.i;
}
int SelectGameFromZip(void) {
int i, rc;
char *line, *uri;
fputs("\nCOSMOPOLITAN NESEMU1\n\n", stdout);
for (i = 0; i < zipgames_.i; ++i) {
printf(" [%d] %s\n", i, zipgames_.p[i]);
}
fputs("\nPlease choose a game (or CTRL-C to quit) [default 0]: ", stdout);
fflush(stdout);
rc = 0;
if ((line = GetLine())) {
i = MAX(0, MIN(zipgames_.i - 1, atoi(line)));
uri = xasprintf("zip:%s", zipgames_.p[i]);
rc = PlayGame(uri, NULL);
free(uri);
} else {
fputs("\n", stdout);
}
return rc;
}
int main(int argc, char** argv) {
int rc;
GetOpts(argc, argv);
if (optind + 1 < argc) {
rc = PlayGame(argv[optind], argv[optind + 1]);
} else if (optind < argc) {
rc = PlayGame(argv[optind], NULL);
} else {
if (!FindZipGames()) {
PrintUsage(0, stderr);
}
rc = SelectGameFromZip();
}
return rc;
}

View file

@ -50,19 +50,22 @@ const struct AuxiliaryValue {
};
int main(int argc, char *argv[], char **envp) {
long key;
unsigned i;
unsigned long val;
char fmt[64], **env;
printf("\nArguments:\n");
for (unsigned i = 0; i < argc; ++i) {
for (i = 0; i < argc; ++i) {
printf(" ☼ %s\n", argv[i]);
}
printf("\nEnvironment:\n");
for (char **env = envp; *env; ++env) {
for (env = envp; *env; ++env) {
printf(" ☼ %s\n", *env);
}
printf("\nAuxiliary Values:\n");
for (unsigned i = 0; i < ARRAYLEN(kAuxiliaryValues); ++i) {
long key = *kAuxiliaryValues[i].id;
unsigned long val = getauxval(key);
char fmt[64];
for (i = 0; i < ARRAYLEN(kAuxiliaryValues); ++i) {
key = *kAuxiliaryValues[i].id;
val = getauxval(key);
printf(PROGN(stpcpy(stpcpy(stpcpy(fmt, "%16s[%p] = "),
kAuxiliaryValues[i].fmt),
" # %s\n"),

View file

@ -431,40 +431,40 @@
/*
* Evaluate a command.
*/
#define ALIASCMD (builtincmd + 3)
#define BGCMD (builtincmd + 4)
#define BREAKCMD (builtincmd + 5)
#define CDCMD (builtincmd + 6)
#define COMMANDCMD (builtincmd + 8)
#define DOTCMD (builtincmd + 0)
#define ECHOCMD (builtincmd + 10)
#define EVALCMD (builtincmd + 11)
#define EXECCMD (builtincmd + 12)
#define EXITCMD (builtincmd + 13)
#define EXPORTCMD (builtincmd + 14)
#define FALSECMD (builtincmd + 15)
#define FGCMD (builtincmd + 16)
#define GETOPTSCMD (builtincmd + 17)
#define HASHCMD (builtincmd + 18)
#define JOBSCMD (builtincmd + 19)
#define KILLCMD (builtincmd + 20)
#define LOCALCMD (builtincmd + 21)
#define PRINTFCMD (builtincmd + 22)
#define PWDCMD (builtincmd + 23)
#define READCMD (builtincmd + 24)
#define RETURNCMD (builtincmd + 26)
#define SETCMD (builtincmd + 27)
#define SHIFTCMD (builtincmd + 28)
#define TESTCMD (builtincmd + 2)
#define TIMESCMD (builtincmd + 30)
#define TRAPCMD (builtincmd + 31)
#define TRUECMD (builtincmd + 1)
#define TYPECMD (builtincmd + 33)
#define ULIMITCMD (builtincmd + 34)
#define UMASKCMD (builtincmd + 35)
#define UNALIASCMD (builtincmd + 36)
#define UNSETCMD (builtincmd + 37)
#define WAITCMD (builtincmd + 38)
#define ALIASCMD (kBuiltinCmds + 3)
#define BGCMD (kBuiltinCmds + 4)
#define BREAKCMD (kBuiltinCmds + 5)
#define CDCMD (kBuiltinCmds + 6)
#define COMMANDCMD (kBuiltinCmds + 8)
#define DOTCMD (kBuiltinCmds + 0)
#define ECHOCMD (kBuiltinCmds + 10)
#define EVALCMD (kBuiltinCmds + 11)
#define EXECCMD (kBuiltinCmds + 12)
#define EXITCMD (kBuiltinCmds + 13)
#define EXPORTCMD (kBuiltinCmds + 14)
#define FALSECMD (kBuiltinCmds + 15)
#define FGCMD (kBuiltinCmds + 16)
#define GETOPTSCMD (kBuiltinCmds + 17)
#define HASHCMD (kBuiltinCmds + 18)
#define JOBSCMD (kBuiltinCmds + 19)
#define KILLCMD (kBuiltinCmds + 20)
#define LOCALCMD (kBuiltinCmds + 21)
#define PRINTFCMD (kBuiltinCmds + 22)
#define PWDCMD (kBuiltinCmds + 23)
#define READCMD (kBuiltinCmds + 24)
#define RETURNCMD (kBuiltinCmds + 26)
#define SETCMD (kBuiltinCmds + 27)
#define SHIFTCMD (kBuiltinCmds + 28)
#define TESTCMD (kBuiltinCmds + 2)
#define TIMESCMD (kBuiltinCmds + 30)
#define TRAPCMD (kBuiltinCmds + 31)
#define TRUECMD (kBuiltinCmds + 1)
#define TYPECMD (kBuiltinCmds + 33)
#define ULIMITCMD (kBuiltinCmds + 34)
#define UMASKCMD (kBuiltinCmds + 35)
#define UNALIASCMD (kBuiltinCmds + 36)
#define UNSETCMD (kBuiltinCmds + 37)
#define WAITCMD (kBuiltinCmds + 38)
#define BUILTIN_SPECIAL 0x1
#define BUILTIN_REGULAR 0x2
@ -620,12 +620,12 @@
#define octtobin(c) ((c) - '0')
#define scopy(s1, s2) ((void)strcpy(s2, s1))
#define TRACE(param)
/* #define TRACE(param) \ */
/* do { \ */
/* printf("TRACE: "); \ */
/* printf param; \ */
/* } while (0) */
/* #define TRACE(param) */
#define TRACE(param) \
do { \
printf("TRACE: "); \
printf param; \
} while (0)
#define TRACEV(param)
#define digit_val(c) ((c) - '0')
@ -1184,13 +1184,16 @@ static const char dolatstr[] = {CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUO
/* Some macros depend on the order, add new variables to the end. */
static void changepath(const char *);
static void getoptsreset(const char *);
static struct Var varinit[] = {{0, VSTRFIXED | VTEXTFIXED, defifsvar, 0},
{0, VSTRFIXED | VTEXTFIXED, defpathvar, changepath},
{0, VSTRFIXED | VTEXTFIXED, "PS1=$ ", 0},
{0, VSTRFIXED | VTEXTFIXED, "PS2=> ", 0},
{0, VSTRFIXED | VTEXTFIXED, "PS4=+ ", 0},
{0, VSTRFIXED | VTEXTFIXED, defoptindvar, getoptsreset},
{0, VSTRFIXED | VTEXTFIXED, linenovar, 0}};
static struct Var varinit[] = {
{0, VSTRFIXED | VTEXTFIXED, defifsvar, 0},
{0, VSTRFIXED | VTEXTFIXED, defpathvar, changepath},
{0, VSTRFIXED | VTEXTFIXED, "PS1=$ ", 0},
{0, VSTRFIXED | VTEXTFIXED, "PS2=> ", 0},
{0, VSTRFIXED | VTEXTFIXED, "PS4=+ ", 0},
{0, VSTRFIXED | VTEXTFIXED, defoptindvar, getoptsreset},
{0, VSTRFIXED | VTEXTFIXED, linenovar, 0},
};
static const char kPrec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
#define ARITH_PRECEDENCE(OP, PREC) [OP - ARITH_BINOP_MIN] = PREC
@ -1308,7 +1311,8 @@ static const char sqsyntax[] = {
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CCTL, CCTL, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD};
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD,
};
/* syntax table used when in arithmetic */
static const char arisyntax[] = {
@ -1331,7 +1335,8 @@ static const char arisyntax[] = {
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CBACK, CWORD, CWORD, CWORD, CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CENDVAR, CWORD, CWORD};
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CENDVAR, CWORD, CWORD,
};
/* character classification table */
static const char is_type[] /* clang-format off */ = {
@ -1405,20 +1410,47 @@ static int unaliascmd();
static int unsetcmd();
static int waitcmd();
static const struct builtincmd builtincmd[] = {
{".", dotcmd, 3}, {":", truecmd, 3}, {"[", testcmd, 0},
{"alias", aliascmd, 6}, {"bg", bgcmd, 2}, {"break", breakcmd, 3},
{"cd", cdcmd, 2}, {"chdir", cdcmd, 0}, {"command", commandcmd, 2},
{"continue", breakcmd, 3}, {"echo", echocmd, 0}, {"eval", NULL, 3},
{"exec", execcmd, 3}, {"exit", exitcmd, 3}, {"export", exportcmd, 7},
{"false", falsecmd, 2}, {"fg", fgcmd, 2}, {"getopts", getoptscmd, 2},
{"hash", hashcmd, 2}, {"jobs", jobscmd, 2}, {"kill", killcmd, 2},
{"local", localcmd, 7}, {"printf", printfcmd, 0}, {"pwd", pwdcmd, 2},
{"read", readcmd, 2}, {"readonly", exportcmd, 7}, {"return", returncmd, 3},
{"set", setcmd, 3}, {"shift", shiftcmd, 3}, {"test", testcmd, 0},
{"times", timescmd, 3}, {"trap", trapcmd, 3}, {"true", truecmd, 2},
{"type", typecmd, 2}, {"ulimit", ulimitcmd, 2}, {"umask", umaskcmd, 2},
{"unalias", unaliascmd, 2}, {"unset", unsetcmd, 3}, {"wait", waitcmd, 2}};
static const struct builtincmd kBuiltinCmds[] = {
{".", dotcmd, 3}, //
{":", truecmd, 3}, //
{"[", testcmd, 0}, //
{"alias", aliascmd, 6}, //
{"bg", bgcmd, 2}, //
{"break", breakcmd, 3}, //
{"cd", cdcmd, 2}, //
{"chdir", cdcmd, 0}, //
{"command", commandcmd, 2}, //
{"continue", breakcmd, 3}, //
{"echo", echocmd, 0}, //
{"eval", NULL, 3}, //
{"exec", execcmd, 3}, //
{"exit", exitcmd, 3}, //
{"export", exportcmd, 7}, //
{"false", falsecmd, 2}, //
{"fg", fgcmd, 2}, //
{"getopts", getoptscmd, 2}, //
{"hash", hashcmd, 2}, //
{"jobs", jobscmd, 2}, //
{"kill", killcmd, 2}, //
{"local", localcmd, 7}, //
{"printf", printfcmd, 0}, //
{"pwd", pwdcmd, 2}, //
{"read", readcmd, 2}, //
{"readonly", exportcmd, 7}, //
{"return", returncmd, 3}, //
{"set", setcmd, 3}, //
{"shift", shiftcmd, 3}, //
{"test", testcmd, 0}, //
{"times", timescmd, 3}, //
{"trap", trapcmd, 3}, //
{"true", truecmd, 2}, //
{"type", typecmd, 2}, //
{"ulimit", ulimitcmd, 2}, //
{"umask", umaskcmd, 2}, //
{"unalias", unaliascmd, 2}, //
{"unset", unsetcmd, 3}, //
{"wait", waitcmd, 2}, //
};
enum token {
EOI,
@ -2115,7 +2147,7 @@ static int olderf(const char *, const char *);
static int64_t openhere(union node *);
static int openredirect(union node *);
static int options(int);
static int padvance_magic(const char **path, const char *name, int magic);
static int padvance_magic(const char **, const char *, int);
static int patmatch(char *, const char *);
static int peektoken(void);
static int pgetc(void);
@ -2882,7 +2914,7 @@ step6:
goto docd;
err:
sh_error("can't cd to %s", dest);
/* NOTREACHED */
unreachable;
out:
if (flags & CD_PRINT) out1fmt(snlfmt, curdir);
return 0;
@ -3446,7 +3478,7 @@ static void evalbackcmd(union node *n, struct backcmd *result) {
}
ifsfree();
evaltreenr(n, EV_EXIT);
/* NOTREACHED */
unreachable;
}
close(pip[1]);
result->fd = pip[0];
@ -3629,7 +3661,7 @@ static int evalcommand(union node *cmd, int flags) {
break;
}
shellexec(argv, path, cmdentry.u.index);
/* NOTREACHED */
unreachable;
case CMDBUILTIN:
if (evalbltin(cmdentry.u.cmd, argc, argv, flags) &&
!(exception == EXERROR && spclbltin <= 0)) {
@ -3742,9 +3774,11 @@ funcdone:
*/
static void prehash(union node *n) {
struct cmdentry entry;
if (n->type == NCMD && n->ncmd.args)
if (goodname(n->ncmd.args->narg.text))
if (n->type == NCMD && n->ncmd.args) {
if (goodname(n->ncmd.args->narg.text)) {
find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
}
}
}
/*───────────────────────────────────────────────────────────────────────────│─╗
@ -4014,10 +4048,8 @@ static void readcmdfile(char *name) {
* Search the table of builtin commands.
*/
static struct builtincmd *find_builtin(const char *name) {
struct builtincmd *bp;
bp = bsearch(&name, builtincmd, sizeof(builtincmd) / sizeof(builtincmd[0]), sizeof(builtincmd[0]),
pstrcmp);
return bp;
return bsearch(&name, kBuiltinCmds, sizeof(kBuiltinCmds) / sizeof(kBuiltinCmds[0]),
sizeof(kBuiltinCmds[0]), pstrcmp);
}
/*
@ -4025,15 +4057,11 @@ static struct builtincmd *find_builtin(const char *name) {
* change the shellexec routine as well.
*/
static void find_command(char *name, struct cmdentry *entry, int act, const char *path) {
struct tblentry *cmdp;
int idx;
int prev;
char *fullname;
struct stat statb;
int e;
int updatetbl;
struct tblentry *cmdp;
struct builtincmd *bcmd;
int len;
int e, bit, idx, prev, updatetbl, len;
/* If name contains a slash, don't use PATH or hash table */
if (strchr(name, '/') != NULL) {
entry->u.index = -1;
@ -4050,7 +4078,6 @@ static void find_command(char *name, struct cmdentry *entry, int act, const char
if (!updatetbl) act |= DO_ALTPATH;
/* If name is in the table, check answer will be ok */
if ((cmdp = cmdlookup(name, 0)) != NULL) {
int bit;
switch (cmdp->cmdtype) {
default:
case CMDNORMAL:
@ -4067,9 +4094,10 @@ static void find_command(char *name, struct cmdentry *entry, int act, const char
if (act & bit & DO_REGBLTIN) goto fail;
updatetbl = 0;
cmdp = NULL;
} else if (cmdp->rehash == 0)
} else if (cmdp->rehash == 0) {
/* if not invalidated by cd, we're done */
goto success;
}
}
/* If %builtin not in path, check for builtin next */
bcmd = find_builtin(name);
@ -4744,7 +4772,7 @@ static void expbackq(union node *cmd, int flag) {
recordregion(startloc, dest - (char *)stackblock(), 0);
}
TRACE(("evalbackq: size=%d: \"%.*s\"\n", (dest - (char *)stackblock()) - startloc,
(dest - (char *)stackblock()) - startloc, stackblock() + startloc));
(dest - (char *)stackblock()) - startloc, (char *)stackblock() + startloc));
out:
argbackq = argbackq->next;
}
@ -4822,7 +4850,7 @@ static char *subevalvar(char *start, char *str, int strloc, int startloc, int va
goto out;
case VSQUESTION:
varunset(start, str, startp, varflags);
/* NOTREACHED */
unreachable;
}
subtype -= VSTRIMRIGHT;
rmesc = startp;
@ -6450,7 +6478,7 @@ static void forkparent(struct job *jp, union node *n, int mode, int pid) {
TRACE(("Fork failed, errno=%d", errno));
if (jp) freejob(jp);
sh_error("Cannot fork");
/* NOTREACHED */
unreachable;
}
TRACE(("In parent shell: child = %d\n", pid));
if (!jp) return;
@ -6480,10 +6508,11 @@ static int forkshell(struct job *jp, union node *n, int mode) {
int pid;
TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
pid = fork();
if (pid == 0)
if (pid == 0) {
forkchild(jp, n, mode);
else
} else {
forkparent(jp, n, mode, pid);
}
return pid;
}
@ -6498,7 +6527,7 @@ static struct job *vforkexec(union node *n, char **argv, const char *path, int i
forkchild(jp, n, FORK_FG);
sigclearmask();
shellexec(argv, path, idx);
/* NOTREACHED */
unreachable;
}
vforked = 0;
sigclearmask();
@ -7355,7 +7384,7 @@ static void setoption(int flag, int val) {
return;
}
sh_error("Illegal option -%c", flag);
/* NOTREACHED */
unreachable;
}
/*
@ -7799,7 +7828,7 @@ static union node *command(void) {
switch (readtoken()) {
default:
synexpect(-1);
/* NOTREACHED */
unreachable;
case TIF:
n1 = (union node *)stalloc(sizeof(struct nif));
n1->type = NIF;
@ -9998,10 +10027,10 @@ static void initvar(void) {
vp->next = *vpp;
*vpp = vp;
} while (++vp < end);
/*
* PS1 depends on uid
*/
if (!geteuid()) vps1.text = "PS1=# ";
/* PS1 depends on uid */
if (!geteuid()) {
vps1.text = "PS1=# ";
}
}
/*
@ -10749,7 +10778,7 @@ static int exitcmd(int argc, char **argv) {
exraise(EXEXIT);
}
/*
/**
* Main routine. We initialize things, parse the arguments, execute
* profiles if we're a login shell, and then call cmdloop to execute
* commands. The setjmp call sets up the location to jump to when an

View file

@ -39,7 +39,7 @@ int main(int argc, char *argv[argc]) {
errno = err;
break;
}
l = xedd.decoded_length;
l = xedd.length;
if (l <= 0 || l > i) abort();
for (j = 0; j < l; ++j) {
if (fputhex(buf[j], stdout) == -1) {