From b6793d42d5ed6b4f78ffdf61bf68931c8ca37b44 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 1 Oct 2020 01:20:13 -0700 Subject: [PATCH] Add The LISP Challenge This change introduces a 2.5kb program that's comes pretty close so far to bootstrapping John McCarthy's metacircular evaluator on bare metal. --- build/definitions.mk | 6 + build/realify.sed | 37 +- libc/log/asan.c | 2 +- libc/runtime/runtime.mk | 2 - libc/str/thompike.h | 4 +- third_party/stb/stb.mk | 7 +- third_party/xed/x86.h | 5 +- third_party/xed/x86ild.greg.c | 79 ++-- third_party/xed/x86tab.S | 2 +- tool/build/emubin/emubin.mk | 15 + tool/build/emubin/lisp.lds | 52 +++ tool/build/emubin/lisp.lisp | 12 + tool/build/emubin/lisp.real.c | 632 ++++++++++++++++++++++++++++++ tool/build/emubin/lispstart.S | 52 +++ tool/build/emubin/mdatest.real.c | 1 + tool/build/emubin/real.h | 8 - tool/build/emubin/realstart.inc | 7 + tool/build/emubin/spiral.real.c | 1 + tool/build/emulator.c | 17 +- tool/build/lib/address.c | 4 + tool/build/lib/address.h | 1 + tool/build/lib/bcd.c | 10 +- tool/build/lib/dis.c | 23 +- tool/build/lib/dis.h | 5 +- tool/build/lib/disarg.c | 76 +++- tool/build/lib/machine.c | 88 +++-- tool/build/lib/modrm.c | 93 +++-- tool/build/lib/modrm.h | 8 +- tool/build/lib/pty.c | 16 +- tool/build/lib/pty.h | 1 + tool/build/lisp.c | 145 ------- tool/emacs/cosmo-c-builtins.el | 1 + tool/emacs/cosmo-c-keywords.el | 1 + tool/emacs/cosmo-cpp-constants.el | 1 + 34 files changed, 1056 insertions(+), 358 deletions(-) create mode 100644 tool/build/emubin/lisp.lds create mode 100644 tool/build/emubin/lisp.lisp create mode 100644 tool/build/emubin/lisp.real.c create mode 100644 tool/build/emubin/lispstart.S create mode 100644 tool/build/emubin/realstart.inc delete mode 100644 tool/build/lisp.c diff --git a/build/definitions.mk b/build/definitions.mk index b56478fff..b033c7564 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -321,6 +321,7 @@ OBJECTIFY.real.c = \ $(GCC) \ $(OBJECTIFY.c.flags) \ -wrapper build/realify.sh \ + -D__REAL_MODE__ \ -ffixed-r8 \ -ffixed-r9 \ -ffixed-r10 \ @@ -329,6 +330,11 @@ OBJECTIFY.real.c = \ -ffixed-r13 \ -ffixed-r14 \ -ffixed-r15 \ + -fcall-used-rbx \ + -fno-omit-frame-pointer \ + -momit-leaf-frame-pointer \ + -mpreferred-stack-boundary=3 \ + -fno-delete-null-pointer-checks \ -c OBJECTIFY.ncabi.c = \ diff --git a/build/realify.sed b/build/realify.sed index b6e6ccfbc..97893c79a 100644 --- a/build/realify.sed +++ b/build/realify.sed @@ -16,11 +16,12 @@ s/[ \t][ \t]*#.*// # preserve hardcoded stack offsets -s/leave\(q\|\)/leavew ; add $6,%sp/ -s/call\(q\|\)\t/sub $6,%sp ; callw / +# bloats code size 13% compared to recomputing stack solution +s/leave\(q\|\)/leavew\n\tadd\t$6,%sp/ +s/call\(q\|\)\t/sub\t$6,%sp\n\tcallw\t/ s/ret\(q\|\)/retw\t$6/ -s/pushq\t\(.*\)/sub $6,%sp ; push \1/ -s/popq\t\(.*\)/pop \1 ; add $6,%sp/ +s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/ +s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/ # can be used instead if # 1. functions have 6 args or fewer @@ -30,8 +31,10 @@ s/popq\t\(.*\)/pop \1 ; add $6,%sp/ #s/ret\(q\|\)/retw/ #s/popq\t%rbp/pop\t%bp/ #s/pushq\t%rbp/push\t%bp/ -#s/pushq\t\(.*\)/sub $6,%sp ; push \1/ -#s/popq\t\(.*\)/pop \1 ; add $6,%sp/ +#s/pushq\t\(.*\)/sub $6,%sp\n\tpush \1/ +#s/popq\t\(.*\)/pop \1\n\tadd $6,%sp/ + +s/, /,/g # 32-bitify s/rax/eax/g @@ -48,11 +51,13 @@ s/movswl/mov/ s/movzwl/mov/ s/movslq/mov/ s/movzlq/mov/ +s/movsbl/movsbw/ # unsuffix s/^\(\t\(fild\|fist\|fistp\|fiadd\|fisub\|fisubr\|fimul\|fidiv\|fidivr\|ficom\)\)q\t/\1\t/ s/^\(\t\(mov\|add\|adc\|cmp\|test\|lea\|sbb\|mul\|imul\|div\|idiv\|in\|out\|xor\|sub\|and\|or\|rol\|ror\|rcl\|rcr\|shl\|shr\|sal\|sar\|inc\|dec\|not\|neg\)\)l\t/\1w\t/ s/^\(\t[a-z]*\)q\t/\1w\t/ +s/movsww/mov/ # remove fluff s/mov\t%eax,%eax// @@ -85,7 +90,6 @@ s/(%eax,%ecx/(%EAX,%ECX/ s/(%eax,%edi/(%EAX,%EDI/ s/(%eax,%edx/(%EAX,%EDX/ s/(%eax,%esi/(%EAX,%ESI/ -s/(%eax,%esp/(%EAX,%ESP/ s/(%ebp,%eax/(%EBP,%EAX/ s/(%ebp,%ebp/(%EBP,%EBP/ s/(%ebp,%ebx/(%EBP,%EBX/ @@ -93,7 +97,6 @@ s/(%ebp,%ecx/(%EBP,%ECX/ s/(%ebp,%edi/(%EBP,%EDI/ s/(%ebp,%edx/(%EBP,%EDX/ s/(%ebp,%esi/(%EBP,%ESI/ -s/(%ebp,%esp/(%EBP,%ESP/ s/(%ebx,%eax/(%EBX,%EAX/ s/(%ebx,%ebp/(%EBX,%EBP/ s/(%ebx,%ebx/(%EBX,%EBX/ @@ -101,7 +104,6 @@ s/(%ebx,%ecx/(%EBX,%ECX/ s/(%ebx,%edi/(%EBX,%EDI/ s/(%ebx,%edx/(%EBX,%EDX/ s/(%ebx,%esi/(%EBX,%ESI/ -s/(%ebx,%esp/(%EBX,%ESP/ s/(%ecx,%eax/(%ECX,%EAX/ s/(%ecx,%ebp/(%ECX,%EBP/ s/(%ecx,%ebx/(%ECX,%EBX/ @@ -109,7 +111,6 @@ s/(%ecx,%ecx/(%ECX,%ECX/ s/(%ecx,%edi/(%ECX,%EDI/ s/(%ecx,%edx/(%ECX,%EDX/ s/(%ecx,%esi/(%ECX,%ESI/ -s/(%ecx,%esp/(%ECX,%ESP/ s/(%edi,%eax/(%EDI,%EAX/ s/(%edi,%ebp/(%EDI,%EBP/ s/(%edi,%ebx/(%EDI,%EBX/ @@ -117,7 +118,6 @@ s/(%edi,%ecx/(%EDI,%ECX/ s/(%edi,%edi/(%EDI,%EDI/ s/(%edi,%edx/(%EDI,%EDX/ s/(%edi,%esi/(%EDI,%ESI/ -s/(%edi,%esp/(%EDI,%ESP/ s/(%edx,%eax/(%EDX,%EAX/ s/(%edx,%ebp/(%EDX,%EBP/ s/(%edx,%ebx/(%EDX,%EBX/ @@ -125,7 +125,6 @@ s/(%edx,%ecx/(%EDX,%ECX/ s/(%edx,%edi/(%EDX,%EDI/ s/(%edx,%edx/(%EDX,%EDX/ s/(%edx,%esi/(%EDX,%ESI/ -s/(%edx,%esp/(%EDX,%ESP/ s/(%esi,%eax/(%ESI,%EAX/ s/(%esi,%ebp/(%ESI,%EBP/ s/(%esi,%ebx/(%ESI,%EBX/ @@ -133,7 +132,6 @@ s/(%esi,%ecx/(%ESI,%ECX/ s/(%esi,%edi/(%ESI,%EDI/ s/(%esi,%edx/(%ESI,%EDX/ s/(%esi,%esi/(%ESI,%ESI/ -s/(%esi,%esp/(%ESI,%ESP/ s/(%esp,%eax/(%ESP,%EAX/ s/(%esp,%ebp/(%ESP,%EBP/ s/(%esp,%ebx/(%ESP,%EBX/ @@ -141,7 +139,6 @@ s/(%esp,%ecx/(%ESP,%ECX/ s/(%esp,%edi/(%ESP,%EDI/ s/(%esp,%edx/(%ESP,%EDX/ s/(%esp,%esi/(%ESP,%ESI/ -s/(%esp,%esp/(%ESP,%ESP/ s/(,%eax/(,%EAX/ s/(,%ebx/(,%EBX/ s/(,%ecx/(,%ECX/ @@ -149,7 +146,6 @@ s/(,%edx/(,%EDX/ s/(,%esi/(,%ESI/ s/(,%edi/(,%EDI/ s/(,%ebp/(,%EBP/ -s/(,%esp/(,%ESP/ s/(%eax)/(%EAX)/ s/(%ecx)/(%ECX)/ s/(%edx)/(%EDX)/ @@ -166,8 +162,15 @@ s/esi/si/g s/esp/sp/g # sigh :\ -# impossible to avoid rex byte access with naive substitution -# best workaround is avoid uint8_t* and try using uint16_t* more +# gcc needs a flag for not using rex byte regs. workaround: +# - %dil can be avoided through copious use of STOS() macro +# - %sil can be avoided through copious use of LODS() macro +# - %bpl shouldn't be allocated due to -fno-omit-frame-pointer +# - %spl shouldn't be allocated like ever +# beyond that there's only a few cases where %dil and %sil +# need some handcoded asm() macros to workaround, for example +# if ARG1 is long and you say (ARG1 & 1) gcc will use %dil +# so just kludge it using asm("and\t$1,%0" : "+Q"(ARG1)) #s/dil/bl/g #s/sil/bh/g #s/spl/bl/g diff --git a/libc/log/asan.c b/libc/log/asan.c index 08b8758d0..9ac455532 100644 --- a/libc/log/asan.c +++ b/libc/log/asan.c @@ -238,7 +238,7 @@ static void __asan_deallocate(char *p, int kind) { if ((*s < 0 && *s != kAsanHeapOverrun) || *s >= 8) { __asan_report_deallocate_fault(p, *s); } - for (; *s >= 0; ++s) *s = kind; + memset(s, kind, dlmalloc_usable_size(p) >> 3); dlfree(__asan_morgue_add(p)); } diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index bf08225ad..0625f4b0d 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -69,8 +69,6 @@ o/$(MODE)/libc/runtime/winmain.greg.o: \ DEFAULT_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED -o/$(MODE)/libc/runtime/main-gcc.asm: OVERRIDE_CFLAGS += -ffixed-r12 -ffixed-r13 -ffixed-r14 -ffixed-r15 - LIBC_RUNTIME_LIBS = $(foreach x,$(LIBC_RUNTIME_ARTIFACTS),$($(x))) LIBC_RUNTIME_SRCS = $(foreach x,$(LIBC_RUNTIME_ARTIFACTS),$($(x)_SRCS)) LIBC_RUNTIME_HDRS = $(foreach x,$(LIBC_RUNTIME_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/str/thompike.h b/libc/str/thompike.h index 4736a57e5..8319827c6 100644 --- a/libc/str/thompike.h +++ b/libc/str/thompike.h @@ -3,6 +3,8 @@ #include "libc/nexgen32e/bsr.h" #define ThomPikeCont(x) ((x & 0b11000000) == 0b10000000) -#define ThomPikeByte(x) (x & (((1 << (x < 252 ? bsr(~x & 0xff) : 1)) - 1) | 3)) +#define ThomPikeByte(x) (x & (((1 << ThomPikeMsb(x)) - 1) | 3)) +#define ThomPikeLen(x) (7 - ThomPikeMsb(x)) +#define ThomPikeMsb(x) (x < 252 ? bsr(~x & 0xff) : 1) #endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */ diff --git a/third_party/stb/stb.mk b/third_party/stb/stb.mk index ba245bc28..87bb34d64 100644 --- a/third_party/stb/stb.mk +++ b/third_party/stb/stb.mk @@ -63,10 +63,11 @@ $(THIRD_PARTY_STB_A_OBJS): \ -ffunction-sections \ -fdata-sections -o/$(MODE)/third_party/stb/stb_image_write.o \ -o/$(MODE)/third_party/stb/stb_image.o: \ +o//third_party/stb/stb_image_write.o \ +o//third_party/stb/stb_image.o: \ OVERRIDE_CFLAGS += \ - -ftrapv + -ftrapv \ + -fsanitize=address $(THIRD_PARTY_STB_A_OBJS): \ OVERRIDE_CPPFLAGS += \ diff --git a/third_party/xed/x86.h b/third_party/xed/x86.h index dd547e8fc..8e1835d91 100644 --- a/third_party/xed/x86.h +++ b/third_party/xed/x86.h @@ -361,8 +361,8 @@ struct XedOperands { /* bool rexx : 1; // rex.x or rex.wx or etc. see sib table bool rexrr : 1; // evex bool asz : 1; // address size override - int64_t disp; // displacement(%xxx) sign-extended - uint64_t uimm0; // $immediate sign-extended + int64_t disp; // displacement(%xxx) mostly sign-extended + uint64_t uimm0; // $immediate mostly sign-extended bool out_of_bytes : 1; bool is_intel_specific : 1; bool ild_f2 : 1; @@ -383,6 +383,7 @@ struct XedOperands { /* uint8_t rep : 2; // 0, 2 (0xf2 repnz), 3 (0xf3 rep/repe) uint8_t has_modrm : 2; bool imm_signed : 1; // internal + bool disp_unsigned : 1; // internal uint8_t seg_ovd : 3; // XED_SEG_xx uint8_t error : 5; // enum XedError uint8_t mode : 2; // real,legacy,long diff --git a/third_party/xed/x86ild.greg.c b/third_party/xed/x86ild.greg.c index cdf605f35..831548683 100644 --- a/third_party/xed/x86ild.greg.c +++ b/third_party/xed/x86ild.greg.c @@ -130,8 +130,8 @@ static const struct XedDenseMagnums { xed_bits_t OSZ_NONTERM_REFINING66_EOSZ[2][2][3]; } kXed = { .vex_prefix_recoding = {0, 1, 3, 2}, - .BRDISPz_BRDISP_WIDTH = {0x00, 0x10, 0x20, 0x20}, - .MEMDISPv_DISP_WIDTH = {0x00, 0x10, 0x20, 0x40}, + .BRDISPz_BRDISP_WIDTH = {0, 16, 32, 32}, + .MEMDISPv_DISP_WIDTH = {0, 16, 32, 64}, .SIMMz_IMM_WIDTH = {0x00, 0x10, 0x20, 0x20}, .UIMMv_IMM_WIDTH = {0x00, 0x10, 0x20, 0x40}, .ASZ_NONTERM_EASZ = @@ -786,6 +786,28 @@ privileged static void xed_evex_scanner(struct XedDecodedInst *d) { } } +privileged static uint64_t xed_read_number(uint8_t *p, size_t n, bool s) { + switch (s << 2 | bsr(n)) { + case 0b000: + return *p; + case 0b100: + return (int8_t)*p; + case 0b001: + return READ16LE(p); + case 0b101: + return (int16_t)READ16LE(p); + case 0b010: + return READ32LE(p); + case 0b110: + return (int32_t)READ32LE(p); + case 0b011: + case 0b111: + return READ64LE(p); + default: + unreachable; + } +} + privileged static void xed_evex_imm_scanner(struct XedDecodedInst *d) { uint64_t uimm0; uint8_t *itext, *imm_ptr; @@ -829,34 +851,9 @@ privileged static void xed_evex_imm_scanner(struct XedDecodedInst *d) { return; } } - imm_ptr = itext + d->op.pos_imm; if (imm_bytes) { - switch (d->op.imm_signed << 2 | bsr(imm_bytes)) { - case 0b000: - d->op.uimm0 = *imm_ptr; - break; - case 0b100: - d->op.uimm0 = (int8_t)*imm_ptr; - break; - case 0b001: - d->op.uimm0 = READ16LE(imm_ptr); - break; - case 0b101: - d->op.uimm0 = (int16_t)READ16LE(imm_ptr); - break; - case 0b010: - d->op.uimm0 = READ32LE(imm_ptr); - break; - case 0b110: - d->op.uimm0 = (int32_t)READ32LE(imm_ptr); - break; - case 0b011: - case 0b111: - d->op.uimm0 = READ64LE(imm_ptr); - break; - default: - break; - } + d->op.uimm0 = + xed_read_number(itext + d->op.pos_imm, imm_bytes, d->op.imm_signed); } } @@ -1026,6 +1023,7 @@ privileged static void XED_LF_BRDISPz_BRDISP_WIDTH_OSZ_NONTERM_EOSZ_l2( x->op.disp_width = kXed.BRDISPz_BRDISP_WIDTH[kXed.OSZ_NONTERM_EOSZ[x->op.rexw][x->op.osz] [x->op.mode]]; + x->op.disp_unsigned = true; } privileged static void XED_LF_RESOLVE_BYREG_DISP_map0x0_op0xc7_l1( @@ -1046,6 +1044,7 @@ privileged static void XED_LF_MEMDISPv_DISP_WIDTH_ASZ_NONTERM_EASZ_l2( struct XedDecodedInst *x) { x->op.disp_width = kXed.MEMDISPv_DISP_WIDTH[kXed.ASZ_NONTERM_EASZ[x->op.asz][x->op.mode]]; + x->op.disp_unsigned = true; } privileged static void XED_LF_BRDISP32_BRDISP_WIDTH_CONST_l2( @@ -1056,15 +1055,14 @@ privileged static void XED_LF_BRDISP32_BRDISP_WIDTH_CONST_l2( privileged static void XED_LF_DISP_BUCKET_0_l1(struct XedDecodedInst *x) { if (x->op.mode <= XED_MODE_LEGACY) { XED_LF_BRDISPz_BRDISP_WIDTH_OSZ_NONTERM_EOSZ_l2(x); + x->op.disp_unsigned = false; } else if (x->op.mode == XED_MODE_LONG) { XED_LF_BRDISP32_BRDISP_WIDTH_CONST_l2(x); } } privileged static void xed_disp_scanner(struct XedDecodedInst *d) { - uint8_t *disp_ptr; xed_bits_t length, disp_width, disp_bytes, max_bytes; - const xed_bits_t ilog2[] = {99, 0, 1, 99, 2, 99, 99, 99, 3}; length = d->length; if (d->op.map < XED_ILD_MAP2) { switch (xed_disp_bits_2d[d->op.map][d->op.opcode]) { @@ -1095,23 +1093,8 @@ privileged static void xed_disp_scanner(struct XedDecodedInst *d) { if (disp_bytes) { max_bytes = d->op.max_bytes; if (length + disp_bytes <= max_bytes) { - disp_ptr = d->bytes + length; - switch (ilog2[disp_bytes]) { - case 0: - d->op.disp = *(int8_t *)disp_ptr; - break; - case 1: - d->op.disp = (int16_t)READ16LE(disp_ptr); - break; - case 2: - d->op.disp = (int32_t)READ32LE(disp_ptr); - break; - case 3: - d->op.disp = (int64_t)READ64LE(disp_ptr); - break; - default: - break; - } + d->op.disp = + xed_read_number(d->bytes + length, disp_bytes, !d->op.disp_unsigned); d->op.pos_disp = length; d->length = length + disp_bytes; } else { diff --git a/third_party/xed/x86tab.S b/third_party/xed/x86tab.S index 7a92ca130..7c45f5f8f 100644 --- a/third_party/xed/x86tab.S +++ b/third_party/xed/x86tab.S @@ -258,7 +258,7 @@ xed_imm_bits_2d.rodata: .byte 1,0x07 # 69─69 i .byte 2,0x05 # 6a─6b j─k .byte 20,0x01 # 6c─7f l─⌂ -.byte 1,0x05 # 80─80 Ç + .byte 1,0x05 # 80─80 Ç .byte 1,0x07 # 81─81 ü .byte 2,0x05 # 82─83 é─â .byte 22,0x01 # 84─99 ä─Ö diff --git a/tool/build/emubin/emubin.mk b/tool/build/emubin/emubin.mk index f1baaeb42..5684d4a36 100644 --- a/tool/build/emubin/emubin.mk +++ b/tool/build/emubin/emubin.mk @@ -49,6 +49,21 @@ o/$(MODE)/tool/build/emubin/%.bin.dbg: \ $(TOOL_BUILD_EMUBIN_A).pkg @$(ELFLINK) -e emucrt -z max-page-size=0x10 +o/dbg/tool/build/emubin/lisp.real.com.dbg: \ + $(TOOL_BUILD_EMUBIN_DEPS) \ + $(TOOL_BUILD_EMUBIN_A) \ + o/dbg/tool/build/emubin/lisp.real.o \ + $(CRT) \ + $(APE) + -@$(APELINK) + +o/tiny/tool/build/emubin/lisp.bin.dbg: \ + $(TOOL_BUILD_EMUBIN_DEPS) \ + o/tiny/tool/build/emubin/lisp.real.o \ + o/tiny/tool/build/emubin/lispstart.o \ + tool/build/emubin/lisp.lds + @$(ELFLINK) -z max-page-size=0x10 + o/tiny/tool/build/emubin/spiral.bin.dbg: \ $(TOOL_BUILD_EMUBIN_DEPS) \ o/tiny/tool/build/emubin/spiral.real.o diff --git a/tool/build/emubin/lisp.lds b/tool/build/emubin/lisp.lds new file mode 100644 index 000000000..8b0b7f300 --- /dev/null +++ b/tool/build/emubin/lisp.lds @@ -0,0 +1,52 @@ +/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│ +│vi: set et sts=2 tw=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 │ +╚─────────────────────────────────────────────────────────────────────────────*/ + +ENTRY(_start) + +SECTIONS { + + .text 0x7c00 - 0x600 : { + *(.start) + rodata = .; + *(.rodata .rodata.*) + . = 0x1fe; + SHORT(0xaa55); + *(.text .text.*) + . = ALIGN(512); + } + + .bss : { + bss = .; + *(.bss .bss.*) + *(COMMON) + } + + /DISCARD/ : { + *(.*) + } +} + +syntax = 0x600+2048*2; +look = 0x600+2048*2+256; +token = 0x600+2048*2+256+1; +globals = 0x600+2048*2+256+1+16; +index = 0x600+2048*2+256+1+16+2; +str = 0x600+2048*2+256+1+16+2+4; +v_sectors = SIZEOF(.text) / 512; diff --git a/tool/build/emubin/lisp.lisp b/tool/build/emubin/lisp.lisp new file mode 100644 index 000000000..dea2498c3 --- /dev/null +++ b/tool/build/emubin/lisp.lisp @@ -0,0 +1,12 @@ +(DEFUN FF (X) + (COND ((ATOM X) X) + ((QUOTE T) (FF (CAR X))))) +(FF '(A B C)) + +((LABEL FF + (LAMBDA (X) + (COND ((ATOM X) + X) + ((QUOTE T) + (FF (CAR X)))))) + (QUOTE ((A B) C))) diff --git a/tool/build/emubin/lisp.real.c b/tool/build/emubin/lisp.real.c new file mode 100644 index 000000000..202c31c09 --- /dev/null +++ b/tool/build/emubin/lisp.real.c @@ -0,0 +1,632 @@ +/*-*- 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 │ +╚─────────────────────────────────────────────────────────────────────────────*/ + +#define TRACE 0 +#define ERRORS 1 +#define LONG long +#define WORD short +#define WORDS 2048 + +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ The LISP Challenge § 8086 PC BIOS / x86_64 Linux System Integration ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +#define ATOM(x) /* a.k.a. !(x&1) */ \ + ({ \ + char IsAtom; \ + asm("test%z1\t$1,%1" : "=@ccz"(IsAtom) : "Qm"((char)x)); \ + IsAtom; \ + }) + +#define OBJECT(t, v) /* a.k.a. v<<1|t */ \ + ({ \ + __typeof(v) Val = (v); \ + asm("shl\t%0" : "+r"(Val)); \ + Val | (t); \ + }) + +#define SUB(x, y) /* a.k.a. x-y */ \ + ({ \ + __typeof(x) Reg = (x); \ + asm("sub\t%1,%0" : "+rm"(Reg) : "g"(y)); \ + Reg; \ + }) + +#define STOS(di, c) asm("stos%z1" : "+D"(di), "=m"(*(di)) : "a"(c)) +#define LODS(si) \ + ({ \ + typeof(*(si)) c; \ + asm("lods%z2" : "+S"(si), "=a"(c) : "m"(*(si))); \ + c; \ + }) + +#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \ + ({ \ + __typeof(*(BASE)) Reg; \ + if (__builtin_constant_p(INDEX) && !(INDEX)) { \ + asm("mov\t%c2(%1),%0" \ + : "=Q"(Reg) \ + : "bDS"(BASE), "i"((DISP) * sizeof(*(BASE)))); \ + } else { \ + asm("mov\t%c3(%1,%2),%0" \ + : "=Q"(Reg) \ + : "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \ + "i"((DISP) * sizeof(*(BASE)))); \ + } \ + Reg; \ + }) + +#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \ + ({ \ + __typeof(*(OBJECT->MEMBER)) Reg; \ + if (!(OBJECT)) { \ + asm("mov\t%c2(%1),%0" \ + : "=Q"(Reg) \ + : "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP))); \ + } else { \ + asm("mov\t%c3(%1,%2),%0" \ + : "=Q"(Reg) \ + : "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP))); \ + } \ + Reg; \ + }) + +#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \ + do { \ + __typeof(*(OBJECT->MEMBER)) Reg; \ + if (!(OBJECT)) { \ + asm volatile("mov\t%0,%c2(%1)" \ + : /* manual output */ \ + : "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), \ + "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP)) \ + : "memory"); \ + } else { \ + asm volatile("mov\t%0,%c3(%1,%2)" \ + : /* manual output */ \ + : "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \ + "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP)) \ + : "memory"); \ + } \ + } while (0) + +static void *SetMemory(void *di, int al, unsigned long cx) { + asm("rep stosb" + : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) + : "0"(di), "1"(cx), "a"(al)); + return di; +} + +static void *CopyMemory(void *di, void *si, unsigned long cx) { + asm("rep movsb" + : "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di) + : "0"(di), "1"(si), "2"(cx)); + return di; +} + +static void RawMode(void) { +#ifndef __REAL_MODE__ + int rc; + int c[14]; + asm volatile("syscall" + : "=a"(rc) + : "0"(0x10), "D"(0), "S"(0x5401), "d"(c) + : "rcx", "r11", "memory"); + c[0] &= ~0b0000010111111000; // INPCK|ISTRIP|PARMRK|INLCR|IGNCR|ICRNL|IXON + c[2] &= ~0b0000000100110000; // CSIZE|PARENB + c[2] |= 0b00000000000110000; // CS8 + c[3] &= ~0b1000000001011010; // ECHONL|ECHO|ECHOE|IEXTEN|ICANON + asm volatile("syscall" + : "=a"(rc) + : "0"(0x10), "D"(0), "S"(0x5402), "d"(c) + : "rcx", "r11", "memory"); +#endif +} + +__attribute__((__noinline__)) static int PrintChar(LONG c) { +#ifdef __REAL_MODE__ + asm volatile("mov\t$0x0E,%%ah\n\t" + "int\t$0x10" + : /* no outputs */ + : "a"(c), "b"(7) + : "memory"); + return 0; +#else + static short buf; + int rc; + buf = c; + asm volatile("syscall" + : "=a"(rc) + : "0"(1), "D"(1), "S"(&buf), "d"(1) + : "rcx", "r11", "memory"); + return rc; +#endif +} + +static void PrintString(char *s) { + char c; + for (;;) { + if (!(c = REAL_READ(s, 0, 0))) break; + PrintChar(c); + ++s; + } +} + +static int XlatChar(LONG c) { + if (c >= 'a') { + asm volatile("" ::: "memory"); + if (c <= 'z') c -= 'a' - 'A'; + } + return c; +} + +static int EchoChar(LONG c) { + if (c == '\b' || c == 0x7F) { + PrintString("\b \b"); + return '\b'; + } else { + PrintChar(c); + if (c == '\r') { + PrintChar('\n'); + } + return c; + } +} + +static noinline int ReadChar(void) { + int c; +#ifdef __REAL_MODE__ + asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory"); +#else + static int buf; + asm volatile("syscall" + : "=a"(c) + : "0"(0), "D"(0), "S"(&buf), "d"(1) + : "rcx", "r11", "memory"); + c = buf; +#endif + return EchoChar(XlatChar(c)); +} + +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ The LISP Challenge § LISP Machine ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +#define TYPE_ATOM 0 +#define TYPE_CONS 1 + +#define ATOM_NIL 0 +#define ATOM_T 8 +#define ATOM_QUOTE 12 +#define ATOM_ATOM 24 +#define ATOM_EQ 34 +#define ATOM_COND 40 +#define ATOM_CAR 50 +#define ATOM_CDR 58 +#define ATOM_CONS 66 +#define ATOM_LABEL 76 +#define ATOM_LAMBDA 88 +#define ATOM_SET 102 +#define ATOM_DEFUN 110 + +#define Quote(x) List(ATOM_QUOTE, x) +#define List(x, y) Cons(x, Cons(y, ATOM_NIL)) +#define Caar(x) Car(Car(x)) // ((A B C D) (E F G) H I) → A +#define Cdar(x) Cdr(Car(x)) // ((A B C D) (E F G) H I) → (B C D) +#define Cadar(x) Cadr(Car(x)) // ((A B C D) (E F G) H I) → B +#define Caddar(x) Caddr(Car(x)) // ((A B C D) (E F G) H I) → C +#define Cadr(x) Car(Cdr(x)) // ((A B C D) (E F G) H I) → (E F G) +#define Caddr(x) Cadr(Cdr(x)) // ((A B C D) (E F G) H I) → H + +#define BOOL(x) ((x) ? ATOM_T : ATOM_NIL) +#define VALUE(x) ((x) >> 1) + +struct Lisp { + WORD memory[WORDS]; + unsigned char syntax[256]; + unsigned char look; + char token[16]; + WORD globals; + int index; + char str[WORDS]; +}; + +const char kSymbols[] aligned(1) = "\ +NIL\0T\0QUOTE\0ATOM\0EQ\0COND\0CAR\0CDR\0CONS\0LABEL\0LAMBDA\0SET\0DEFUN\0"; + +#ifdef __REAL_MODE__ +static struct Lisp *const q; +#else +static struct Lisp q[1]; +#endif + +static void Print(LONG); +static WORD GetList(void); +static WORD GetObject(void); +static void PrintObject(LONG); +static WORD Eval(LONG, LONG); + +static void SetupSyntax(void) { + q->syntax[' '] = ' '; + q->syntax['\t'] = ' '; + q->syntax['\r'] = ' '; + q->syntax['\n'] = ' '; + q->syntax['('] = '('; + q->syntax[')'] = ')'; + q->syntax['.'] = '.'; + q->syntax['\''] = '\''; +} + +forceinline WORD Car(LONG x) { + return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 0); +} + +forceinline WORD Cdr(LONG x) { + return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 1); +} + +static WORD Cons(WORD car, WORD cdr) { + int i, c; + i = q->index; + REAL_WRITE_ARRAY_FIELD(q, memory, i, 0, car); + REAL_WRITE_ARRAY_FIELD(q, memory, i, 1, cdr); + q->index += 2; + c = OBJECT(TYPE_CONS, i); + return c; +} + +static void SetupBuiltins(void) { + CopyMemory(q->str, kSymbols, sizeof(kSymbols)); + q->globals = + Cons(Cons(ATOM_NIL, ATOM_NIL), Cons(Cons(ATOM_T, ATOM_T), ATOM_NIL)); +} + +static char *StpCpy(char *d, char *s) { + char c; + do { + c = LODS(s); /* a.k.a. c = *s++; */ + STOS(d, c); /* a.k.a. *d++ = c; */ + } while (c); + return d; +} + +static WORD Intern(char *s) { + int j, cx; + char c, *z, *t; + z = q->str; + c = LODS(z); + while (c) { + for (j = 0;; ++j) { + if (c != REAL_READ(s, j, 0)) { + break; + } + if (!c) { + return OBJECT(TYPE_ATOM, z - q->str - j - 1); + } + c = LODS(z); + } + while (c) c = LODS(z); + c = LODS(z); + } + --z; + StpCpy(z, s); + return OBJECT(TYPE_ATOM, SUB((long)z, q->str)); +} + +forceinline unsigned char XlatSyntax(unsigned char b) { + return REAL_READ_ARRAY_FIELD(q, syntax, b, 0); /* a.k.a. q->syntax[b] */ +} + +static void GetToken(void) { + char *t; + unsigned char b; + b = q->look; + t = q->token; + while (XlatSyntax(b) == ' ') { + b = ReadChar(); + } + if (XlatSyntax(b)) { + STOS(t, b); + b = ReadChar(); + } else { + while (b && !XlatSyntax(b)) { + if (b != '\b') { + STOS(t, b); + } else if (t > q->token) { + --t; + } + b = ReadChar(); + } + } + STOS(t, 0); + q->look = b; +} + +static WORD ConsumeObject(void) { + GetToken(); + return GetObject(); +} + +static WORD GetQuote(void) { + return Quote(ConsumeObject()); +} + +static WORD AddList(WORD x) { + return Cons(x, GetList()); +} + +static WORD GetList(void) { + GetToken(); + switch (*q->token & 0xFF) { + default: + return AddList(GetObject()); + case '\'': + return AddList(GetQuote()); + case ')': + return ATOM_NIL; + case '.': + return ConsumeObject(); + } +} + +static WORD GetObject(void) { + switch (*q->token & 0xFF) { + default: + return Intern(q->token); + case '\'': + return GetQuote(); + case '(': + return GetList(); + } +} + +static WORD ReadObject(void) { + q->look = ReadChar(); + GetToken(); + return GetObject(); +} + +static WORD Read(void) { + return ReadObject(); +} + +static void PrintAtom(LONG x) { + PrintString(q->str + VALUE(x)); +} + +static void PrintList(LONG x) { + PrintChar('('); + PrintObject(Car(x)); + while ((x = Cdr(x))) { + if (!ATOM(x)) { + PrintChar(' '); + PrintObject(Car(x)); + } else { + PrintString(" . "); + PrintObject(x); + } + } + PrintChar(')'); +} + +static void PrintObject(LONG x) { + if (ATOM(x)) { + PrintAtom(x); + } else { + PrintList(x); + } +} + +static void Print(LONG i) { + PrintObject(i); + PrintString("\r\n"); +} + +__attribute__((__noreturn__)) static void Reset(void) { + asm volatile("jmp\tRepl"); + __builtin_unreachable(); +} + +__attribute__((__noreturn__)) static void OnUndefined(LONG x) { + PrintString("UNDEF! "); + Print(x); + Reset(); +} + +__attribute__((__noreturn__)) static void OnArity(void) { + PrintString("ARITY!\n"); + Reset(); +} + +#if !ERRORS +#define OnUndefined(x) __builtin_unreachable() +#define OnArity() __builtin_unreachable() +#endif + +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +static WORD Atom(LONG x) { + return BOOL(ATOM(x)); +} + +static WORD Null(LONG x) { + return BOOL(!x); +} + +static WORD Eq(LONG x, LONG y) { + return BOOL(x == y); /* undefiled if !ATOM(x)||!ATOM(y) */ +} + +static WORD Assoc(LONG x, LONG y) { + for (;;) { + if (!y) OnUndefined(x); + if (Eq(Caar(y), x)) break; + y = Cdr(y); + } + return Cdar(y); +} + +static WORD Append(LONG x, LONG y) { + if (x) { + return Cons(Car(x), Append(Cdr(x), y)); + } else { + return y; + } +} + +/** + * Gives list of pairs of corresponding elements of the lists x and y. + * E.g. pair[(A,B,C);(X,(Y,Z),U)] = ((A.X),(B.(Y,Z)),(C.U)) + * @note recoded to make lists in dot notation + * @note it's zip() basically + */ +static WORD Pair(LONG x, LONG y) { + if (!x && !y) { + return ATOM_NIL; + } else if (!ATOM(x) && !ATOM(y)) { + return Cons(Cons(Car(x), Car(y)), Pair(Cdr(x), Cdr(y))); + } else { + OnArity(); + } +} + +static WORD Appq(long m) { + if (m) { + return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m))); + } else { + return ATOM_NIL; + } +} + +static WORD Apply(long f, long a) { + return Eval(Cons(f, Appq(a)), ATOM_NIL); +} + +static WORD Evcon(LONG c, LONG a) { + if (Eval(Caar(c), a)) { + return Eval(Cadar(c), a); + } else { + return Evcon(Cdr(c), a); + } +} + +static WORD Evlis(LONG m, LONG a) { + if (m) { + return Cons(Eval(Car(m), a), Evlis(Cdr(m), a)); + } else { + return ATOM_NIL; + } +} + +static WORD Set(LONG e) { + WORD name, value; + name = Car(e); + value = Cadr(e); + q->globals = Cons(Cons(name, value), q->globals); + return value; +} + +static WORD Defun(LONG e) { + WORD name, args, body, lamb; + name = Car(e); + args = Cadr(e); + body = Caddr(e); + lamb = Cons(ATOM_LAMBDA, List(args, body)); + q->globals = Cons(Cons(name, lamb), q->globals); + return name; +} + +static WORD Evaluate(LONG e, LONG a) { + if (ATOM(e)) { + return Assoc(e, a); + } else if (ATOM(Car(e))) { + switch (Car(e)) { + case ATOM_QUOTE: + return Cadr(e); + case ATOM_ATOM: + return Atom(Eval(Cadr(e), a)); + case ATOM_EQ: + return Eq(Eval(Cadr(e), a), Eval(Caddr(e), a)); + case ATOM_COND: + return Evcon(Cdr(e), a); + case ATOM_CAR: + return Car(Eval(Cadr(e), a)); + case ATOM_CDR: + return Cdr(Eval(Cadr(e), a)); + case ATOM_CONS: + return Cons(Eval(Cadr(e), a), Eval(Caddr(e), a)); + case ATOM_DEFUN: + return Defun(Cdr(e)); + case ATOM_SET: + return Set(Cdr(e)); + default: + return Eval(Cons(Assoc(Car(e), a), Evlis(Cdr(e), a)), a); + } + } else if (Eq(Caar(e), ATOM_LABEL)) { + return Eval(Cons(Caddar(e), Cdr(e)), Cons(Cons(Cadar(e), Car(e)), a)); + } else if (Eq(Caar(e), ATOM_LAMBDA)) { + return Eval(Caddar(e), Append(Pair(Cadar(e), Evlis(Cdr(e), a)), a)); + } else { + OnUndefined(Caar(e)); + } +} + +static WORD Eval(LONG e, LONG a) { +#if TRACE + PrintString("->"); + Print(e); +#endif + e = Evaluate(e, a); +#if TRACE + PrintString("<-"); + Print(e); +#endif + return e; +} + +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ The LISP Challenge § User Interface ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +void Repl(void) { + for (;;) { + PrintString("* "); + Print(Eval(Read(), q->globals)); + } +} + +int main(int argc, char *argv[]) { + RawMode(); + SetupSyntax(); + SetupBuiltins(); + PrintString("THE LISP CHALLENGE V1\r\n" + "VISIT GITHUB.COM/JART\r\n"); + Repl(); + return 0; +} diff --git a/tool/build/emubin/lispstart.S b/tool/build/emubin/lispstart.S new file mode 100644 index 000000000..2d9e88a96 --- /dev/null +++ b/tool/build/emubin/lispstart.S @@ -0,0 +1,52 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 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 │ +╚─────────────────────────────────────────────────────────────────────────────*/ + + .code16 + .section .start,"ax",@progbits +_start: jmp 1f +1: ljmp $0x600>>4,$2f +2: push %cs + pop %ds + push %cs + pop %es + mov $0x70000>>4,%ax + cli + mov %ax,%ss + xor %sp,%sp + sti + cld + xor %ax,%ax + xor %di,%di + mov $0x7c00-0x600,%cx + rep stosb + xchg %di,%bx + inc %cx + xor %dh,%dh + mov $v_sectors+0x0200,%ax + int $0x13 + xor %bp,%bp + sub $6,%sp + call main + nop + .type _start,@function + .size _start,.-_start + .globl _start + .globl v_sectors + .globl main diff --git a/tool/build/emubin/mdatest.real.c b/tool/build/emubin/mdatest.real.c index 86f53a057..4c08bcd46 100644 --- a/tool/build/emubin/mdatest.real.c +++ b/tool/build/emubin/mdatest.real.c @@ -19,6 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "tool/build/emubin/poke.h" #include "tool/build/emubin/real.h" +#include "tool/build/emubin/realstart.inc" /* m=tiny; make -j12 MODE=$m o/$m/tool/build/{tinyemu,emulator}.com \ diff --git a/tool/build/emubin/real.h b/tool/build/emubin/real.h index 85a7d2313..1223f1dc3 100644 --- a/tool/build/emubin/real.h +++ b/tool/build/emubin/real.h @@ -3,14 +3,6 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -asm(".pushsection .start,\"ax\",@progbits\n\t" - ".globl\t_start\n" - "_start:\n\t" - "jmp\t1f\n1:\t" - "call\tmain\n\t" - "nop\n\t" - ".popsection"); - forceinline void SetEs(int base) { asm volatile("mov%z0\t%0,%%es" : /* no outputs */ : "r"(base)); } diff --git a/tool/build/emubin/realstart.inc b/tool/build/emubin/realstart.inc new file mode 100644 index 000000000..2864495a2 --- /dev/null +++ b/tool/build/emubin/realstart.inc @@ -0,0 +1,7 @@ +asm(".pushsection .start,\"ax\",@progbits\n\t" + ".globl\t_start\n" + "_start:\n\t" + "jmp\t1f\n1:\t" + "call\tmain\n\t" + "nop\n\t" + ".popsection"); diff --git a/tool/build/emubin/spiral.real.c b/tool/build/emubin/spiral.real.c index e44e716dc..8f64db2a8 100644 --- a/tool/build/emubin/spiral.real.c +++ b/tool/build/emubin/spiral.real.c @@ -19,6 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "tool/build/emubin/poke.h" #include "tool/build/emubin/real.h" +#include "tool/build/emubin/realstart.inc" #define signbit(x) __builtin_signbit(x) diff --git a/tool/build/emulator.c b/tool/build/emulator.c index 62c392047..4572150b7 100644 --- a/tool/build/emulator.c +++ b/tool/build/emulator.c @@ -239,6 +239,13 @@ static bool IsCall(void) { (m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 2)); } +static bool IsLongBranch(void) { + return m->xedd && m->xedd->op.map == XED_ILD_MAP0 && + (m->xedd->op.opcode == 0xEA || m->xedd->op.opcode == 0x9A || + (m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 3) || + (m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 5)); +} + static bool IsDebugBreak(void) { return m->xedd->op.map == XED_ILD_MAP0 && m->xedd->op.opcode == 0xCC; } @@ -434,7 +441,7 @@ void TuiSetup(void) { LOGF("loaded program %s\n%s", codepath, gc(FormatPml4t(m->cr3))); LoadSyms(); ResolveBreakpoints(); - Dis(dis, m, elf->base, 100); + Dis(dis, m, elf->base, elf->base, 100); once = true; } CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR))); @@ -655,8 +662,9 @@ static void SetupDraw(void) { static long GetDisIndex(void) { long i; - if ((i = DisFind(dis, GetIp())) == -1) { - Dis(dis, m, GetIp(), pan.disassembly.bottom - pan.disassembly.top * 2); + if ((i = DisFind(dis, GetIp())) == -1 || IsLongBranch()) { + Dis(dis, m, GetIp(), m->ip, + pan.disassembly.bottom - pan.disassembly.top * 2); CHECK_NE(-1, (i = DisFind(dis, GetIp()))); } while (i + 1 < dis->ops.i && !dis->ops.p[i].size) ++i; @@ -1003,7 +1011,6 @@ static void DrawTrace(struct Panel *p) { bp = Read64(m->bp); sp = Read64(m->sp); for (i = 0; i < p->bottom - p->top;) { - rp += Read64(m->cs); sym = DisFindSym(dis, rp); name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; s = line; @@ -1410,6 +1417,7 @@ static void OnVidyaServiceTeletypeOutput(void) { char buf[12]; n = FormatCga(m->bx[0], buf); n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false); + LOGF("teletype output %`'.*s", n, buf); MachinePtyWrite(pty, buf, n); } @@ -1512,6 +1520,7 @@ static void OnInt15h(void) { } static bool OnHalt(int interrupt) { + ReactiveDraw(); switch (interrupt) { case 1: case 3: diff --git a/tool/build/lib/address.c b/tool/build/lib/address.c index c2f626ab4..07c7271fc 100644 --- a/tool/build/lib/address.c +++ b/tool/build/lib/address.c @@ -23,6 +23,10 @@ #include "tool/build/lib/modrm.h" #include "tool/build/lib/throw.h" +uint64_t AddressOb(struct Machine *m, uint32_t rde) { + return AddSegment(m, rde, m->xedd->op.disp, m->ds); +} + uint8_t *GetSegment(struct Machine *m, uint32_t rde, int s) { switch (s & 7) { case 0: diff --git a/tool/build/lib/address.h b/tool/build/lib/address.h index b85ffc7ba..1f3afcbe9 100644 --- a/tool/build/lib/address.h +++ b/tool/build/lib/address.h @@ -6,6 +6,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +uint64_t AddressOb(struct Machine *, uint32_t); uint64_t AddressDi(struct Machine *, uint32_t); uint64_t AddressSi(struct Machine *, uint32_t); uint64_t DataSegment(struct Machine *, uint32_t, uint64_t); diff --git a/tool/build/lib/bcd.c b/tool/build/lib/bcd.c index 5dab82a08..bd560ce1e 100644 --- a/tool/build/lib/bcd.c +++ b/tool/build/lib/bcd.c @@ -23,7 +23,7 @@ #include "tool/build/lib/flags.h" #include "tool/build/lib/throw.h" -void OpDas(struct Machine *m, uint32_t rde) { +relegated void OpDas(struct Machine *m, uint32_t rde) { uint8_t al, af, cf; af = cf = 0; al = m->ax[0]; @@ -39,7 +39,7 @@ void OpDas(struct Machine *m, uint32_t rde) { AluFlags8(m->ax[0], af, &m->flags, 0, cf); } -void OpAaa(struct Machine *m, uint32_t rde) { +relegated void OpAaa(struct Machine *m, uint32_t rde) { uint8_t af, cf; af = cf = 0; if ((m->ax[0] & 0x0f) > 9 || GetFlag(m->flags, FLAGS_AF)) { @@ -51,7 +51,7 @@ void OpAaa(struct Machine *m, uint32_t rde) { AluFlags8(m->ax[0], af, &m->flags, 0, cf); } -void OpAas(struct Machine *m, uint32_t rde) { +relegated void OpAas(struct Machine *m, uint32_t rde) { uint8_t af, cf; af = cf = 0; if ((m->ax[0] & 0x0f) > 9 || GetFlag(m->flags, FLAGS_AF)) { @@ -63,7 +63,7 @@ void OpAas(struct Machine *m, uint32_t rde) { AluFlags8(m->ax[0], af, &m->flags, 0, cf); } -void OpAam(struct Machine *m, uint32_t rde) { +relegated void OpAam(struct Machine *m, uint32_t rde) { uint8_t i = m->xedd->op.uimm0; if (!i) ThrowDivideError(m); m->ax[1] = m->ax[0] / i; @@ -71,7 +71,7 @@ void OpAam(struct Machine *m, uint32_t rde) { AluFlags8(m->ax[0], 0, &m->flags, 0, 0); } -void OpAad(struct Machine *m, uint32_t rde) { +relegated void OpAad(struct Machine *m, uint32_t rde) { uint8_t i = m->xedd->op.uimm0; Write16(m->ax, (m->ax[1] * i + m->ax[0]) & 0xff); AluFlags8(m->ax[0], 0, &m->flags, 0, 0); diff --git a/tool/build/lib/dis.c b/tool/build/lib/dis.c index 1e7405be9..e8cb8d683 100644 --- a/tool/build/lib/dis.c +++ b/tool/build/lib/dis.c @@ -35,6 +35,7 @@ #include "tool/build/lib/case.h" #include "tool/build/lib/demangle.h" #include "tool/build/lib/dis.h" +#include "tool/build/lib/endian.h" #include "tool/build/lib/high.h" #include "tool/build/lib/memory.h" #include "tool/build/lib/modrm.h" @@ -173,19 +174,21 @@ long DisFind(struct Dis *d, int64_t addr) { return -1; } -static long DisOne(struct Dis *d, struct Machine *m, int64_t addr) { +static long DisAppendOpLines(struct Dis *d, struct Machine *m, int64_t addr) { void *r; + int64_t ip; unsigned k; uint8_t b[15]; struct DisOp op; long i, n, symbol; n = 15; - if ((symbol = DisFindSym(d, addr)) != -1) { - if (d->syms.p[symbol].addr <= addr && - addr < d->syms.p[symbol].addr + d->syms.p[symbol].size) { - n = d->syms.p[symbol].size - (addr - d->syms.p[symbol].addr); + ip = addr - Read64(m->cs); + if ((symbol = DisFindSym(d, ip)) != -1) { + if (d->syms.p[symbol].addr <= ip && + ip < d->syms.p[symbol].addr + d->syms.p[symbol].size) { + n = d->syms.p[symbol].size - (ip - d->syms.p[symbol].addr); } - if (addr == d->syms.p[symbol].addr && d->syms.p[symbol].name) { + if (ip == d->syms.p[symbol].addr && d->syms.p[symbol].name) { op.addr = addr; op.size = 0; op.active = true; @@ -219,18 +222,19 @@ static long DisOne(struct Dis *d, struct Machine *m, int64_t addr) { return n; } -long Dis(struct Dis *d, struct Machine *m, int64_t addr, int lines) { +long Dis(struct Dis *d, struct Machine *m, uint64_t addr, uint64_t ip, + int lines) { int64_t i, j, symbol; DisFreeOps(&d->ops); if ((symbol = DisFindSym(d, addr)) != -1 && (d->syms.p[symbol].addr < addr && addr < d->syms.p[symbol].addr + d->syms.p[symbol].size)) { for (i = d->syms.p[symbol].addr; i < addr; i += j) { - if ((j = DisOne(d, m, i)) == -1) return -1; + if ((j = DisAppendOpLines(d, m, i)) == -1) return -1; } } for (i = 0; i < lines; ++i, addr += j) { - if ((j = DisOne(d, m, addr)) == -1) return -1; + if ((j = DisAppendOpLines(d, m, addr)) == -1) return -1; } return 0; } @@ -247,6 +251,7 @@ const char *DisGetLine(struct Dis *d, struct Machine *m, size_t i) { d->xedd, AccessRam(m, d->ops.p[i].addr, d->ops.p[i].size, r, b, true), d->ops.p[i].size); d->addr = d->ops.p[i].addr; + d->m = m; p = DisLineCode(d, d->buf); CHECK_LT(p - d->buf, sizeof(d->buf)); return d->buf; diff --git a/tool/build/lib/dis.h b/tool/build/lib/dis.h index 65f05e7ab..116a5cb55 100644 --- a/tool/build/lib/dis.h +++ b/tool/build/lib/dis.h @@ -45,11 +45,12 @@ struct Dis { } * p; } edges; struct XedDecodedInst xedd[1]; - uint64_t addr; + struct Machine *m; /* for the segment registers */ + uint64_t addr; /* current effective address */ char buf[512]; }; -long Dis(struct Dis *, struct Machine *, int64_t, int); +long Dis(struct Dis *, struct Machine *, uint64_t, uint64_t, int); long DisFind(struct Dis *, int64_t); void DisFree(struct Dis *); void DisFreeOp(struct DisOp *); diff --git a/tool/build/lib/disarg.c b/tool/build/lib/disarg.c index 0ba591148..e0867606a 100644 --- a/tool/build/lib/disarg.c +++ b/tool/build/lib/disarg.c @@ -59,6 +59,43 @@ static int64_t RipRelative(struct Dis *d, int64_t i) { return d->addr + d->xedd->length + i; } +static int64_t ZeroExtend(uint32_t rde, int64_t i) { + switch (Mode(rde)) { + case XED_MODE_REAL: + return i & 0xffff; + case XED_MODE_LEGACY: + return i & 0xffffffff; + default: + return i; + } +} + +static int64_t Unrelative(uint32_t rde, int64_t i) { + switch (Eamode(rde)) { + case XED_MODE_REAL: + return i & 0xffff; + case XED_MODE_LEGACY: + return i & 0xffffffff; + default: + return i; + } +} + +static int64_t GetSeg(struct Dis *d, uint32_t rde, unsigned char *s) { + switch (Sego(rde) ? Sego(rde) : d->xedd->op.hint) { + default: + return Read64(s); + case 1: + return Read64(d->m->es); + case 2: + return Read64(d->m->cs); + case 3: + return Read64(d->m->ss); + case 4: + return Read64(d->m->ds); + } +} + static const char *GetAddrReg(struct Dis *d, uint32_t rde, uint8_t x, uint8_t r) { return kGreg[Eamode(rde) == XED_MODE_REAL][Eamode(rde) == XED_MODE_LONG] @@ -106,12 +143,12 @@ static char *DisInt(char *p, int64_t x) { return p; } -static char *DisSym(struct Dis *d, char *p, int64_t addr) { +static char *DisSym(struct Dis *d, char *p, int64_t addr, int64_t ip) { long sym; int64_t addend; const char *name; - if ((sym = DisFindSym(d, addr)) != -1 && d->syms.p[sym].name) { - addend = addr - d->syms.p[sym].addr; + if ((sym = DisFindSym(d, ip)) != -1 && d->syms.p[sym].name) { + addend = ip - d->syms.p[sym].addr; name = d->syms.stab + d->syms.p[sym].name; p = Demangle(p, name); if (addend) { @@ -124,10 +161,11 @@ static char *DisSym(struct Dis *d, char *p, int64_t addr) { } } -static char *DisSymLiteral(struct Dis *d, char *p, uint64_t x) { +static char *DisSymLiteral(struct Dis *d, uint32_t rde, char *p, uint64_t addr, + uint64_t ip) { *p++ = '$'; p = HighStart(p, g_high.literal); - p = DisSym(d, p, x); + p = DisSym(d, p, addr, ip); p = HighEnd(p); return p; } @@ -154,18 +192,30 @@ static char *DisSego(struct Dis *d, uint32_t rde, char *p) { return p; } +static bool IsRealModrmAbsolute(uint32_t rde) { + return Eamode(rde) == XED_MODE_REAL && ModrmRm(rde) == 6 && !ModrmMod(rde); +} + static char *DisM(struct Dis *d, uint32_t rde, char *p) { int64_t disp; const char *base, *index, *scale; p = DisSego(d, rde, p); base = index = scale = NULL; if (ModrmMod(rde) == 0b01 || ModrmMod(rde) == 0b10 || IsRipRelative(rde) || - (Eamode(rde) == XED_MODE_REAL && ModrmRm(rde) == 6 && !ModrmMod(rde)) || + IsRealModrmAbsolute(rde) || (ModrmMod(rde) == 0b00 && ModrmRm(rde) == 0b100 && SibBase(d->xedd) == 0b101)) { disp = d->xedd->op.disp; - if (IsRipRelative(rde)) disp = RipRelative(d, disp); - p = DisSym(d, p, disp); + if (IsRipRelative(rde)) { + if (Mode(rde) == XED_MODE_LONG) { + disp = RipRelative(d, disp); + } else { + disp = Unrelative(rde, disp); + } + } else if (IsRealModrmAbsolute(rde)) { + disp = Unrelative(rde, disp); + } + p = DisSym(d, p, disp, disp); } if (Eamode(rde) != XED_MODE_REAL) { if (!SibExists(rde)) { @@ -355,11 +405,12 @@ static char *DisHd(struct Dis *d, uint32_t rde, char *p) { } static char *DisImm(struct Dis *d, uint32_t rde, char *p) { - return DisSymLiteral(d, p, d->xedd->op.uimm0); + return DisSymLiteral(d, rde, p, d->xedd->op.uimm0, + ZeroExtend(rde, d->xedd->op.uimm0)); } static char *DisRvds(struct Dis *d, uint32_t rde, char *p) { - return DisSymLiteral(d, p, d->xedd->op.disp); + return DisSymLiteral(d, rde, p, d->xedd->op.disp, d->xedd->op.disp); } static char *DisKpvds(struct Dis *d, uint32_t rde, char *p, uint64_t x) { @@ -400,11 +451,12 @@ static char *DisJb(struct Dis *d, uint32_t rde, char *p) { } static char *DisJvds(struct Dis *d, uint32_t rde, char *p) { - return DisSym(d, p, RipRelative(d, d->xedd->op.disp)); + return DisSym(d, p, RipRelative(d, d->xedd->op.disp), + RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs)); } static char *DisAbs(struct Dis *d, uint32_t rde, char *p) { - return DisSym(d, p, d->xedd->op.disp); + return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp); } static char *DisSw(struct Dis *d, uint32_t rde, char *p) { diff --git a/tool/build/lib/machine.c b/tool/build/lib/machine.c index be86f9191..5f3416ab0 100644 --- a/tool/build/lib/machine.c +++ b/tool/build/lib/machine.c @@ -188,7 +188,7 @@ static void OpSahf(struct Machine *m, uint32_t rde) { } static void OpLeaGvqpM(struct Machine *m, uint32_t rde) { - WriteRegister(rde, RegRexrReg(m, rde), ComputeAddress(m, rde)); + WriteRegister(rde, RegRexrReg(m, rde), LoadEffectiveAddress(m, rde).addr); } static relegated void OpPushSeg(struct Machine *m, uint32_t rde) { @@ -216,6 +216,14 @@ static relegated void OpJmpf(struct Machine *m, uint32_t rde) { m->ip = m->xedd->op.disp; } +static relegated void OpXlatAlBbb(struct Machine *m, uint32_t rde) { + uint64_t v; + v = MaskAddress(Eamode(rde), Read64(m->bx) + Read8(m->ax)); + v = DataSegment(m, rde, v); + SetReadAddr(m, v, 1); + Write8(m->ax, Read8(ResolveAddress(m, v))); +} + static void WriteEaxAx(struct Machine *m, uint32_t rde, uint32_t x) { if (!Osz(rde)) { Write64(m->ax, x); @@ -264,14 +272,6 @@ static void OpOutDxAx(struct Machine *m, uint32_t rde) { OpOut(m, Read16(m->dx), ReadEaxAx(m, rde)); } -static void OpXlatAlBbb(struct Machine *m, uint32_t rde) { - uint64_t v; - v = MaskAddress(Eamode(rde), Read64(m->bx) + Read8(m->ax)); - v = DataSegment(m, rde, v); - SetReadAddr(m, v, 1); - Write8(m->ax, Read8(ResolveAddress(m, v))); -} - static void AluEb(struct Machine *m, uint32_t rde, aluop_f op) { uint8_t *p; p = GetModrmRegisterBytePointerWrite(m, rde); @@ -515,13 +515,17 @@ static void OpMovEbIb(struct Machine *m, uint32_t rde) { } static void OpMovAlOb(struct Machine *m, uint32_t rde) { - SetReadAddr(m, m->xedd->op.disp, 1); - Write8(ResolveAddress(m, m->xedd->op.disp), Read8(m->ax)); + int64_t addr; + addr = AddressOb(m, rde); + SetWriteAddr(m, addr, 1); + Write8(m->ax, Read8(ResolveAddress(m, addr))); } static void OpMovObAl(struct Machine *m, uint32_t rde) { - SetWriteAddr(m, m->xedd->op.disp, 1); - Write8(m->ax, Read8(ResolveAddress(m, m->xedd->op.disp))); + int64_t addr; + addr = AddressOb(m, rde); + SetReadAddr(m, addr, 1); + Write8(ResolveAddress(m, addr), Read8(m->ax)); } static void OpMovRaxOvqp(struct Machine *m, uint32_t rde) { @@ -1199,35 +1203,6 @@ static void OpJcxz(struct Machine *m, uint32_t rde) { } } -static void Loop(struct Machine *m, uint32_t rde, bool cond) { - uint64_t cx; - cx = Read64(m->cx) - 1; - if (Eamode(rde) != XED_MODE_REAL) { - if (Eamode(rde) == XED_MODE_LEGACY) { - cx &= 0xffffffff; - } - Write64(m->cx, cx); - } else { - cx &= 0xffff; - Write16(m->cx, cx); - } - if (cx && cond) { - OpJmp(m, rde); - } -} - -static void OpLoope(struct Machine *m, uint32_t rde) { - Loop(m, rde, GetFlag(m->flags, FLAGS_ZF)); -} - -static void OpLoopne(struct Machine *m, uint32_t rde) { - Loop(m, rde, !GetFlag(m->flags, FLAGS_ZF)); -} - -static void OpLoop1(struct Machine *m, uint32_t rde) { - Loop(m, rde, true); -} - static void Bitscan(struct Machine *m, uint32_t rde, bitscan_f op) { WriteRegister( rde, RegRexrReg(m, rde), @@ -1259,6 +1234,35 @@ static void OpNegEb(struct Machine *m, uint32_t rde) { AluEb(m, rde, Neg8); } +static relegated void Loop(struct Machine *m, uint32_t rde, bool cond) { + uint64_t cx; + cx = Read64(m->cx) - 1; + if (Eamode(rde) != XED_MODE_REAL) { + if (Eamode(rde) == XED_MODE_LEGACY) { + cx &= 0xffffffff; + } + Write64(m->cx, cx); + } else { + cx &= 0xffff; + Write16(m->cx, cx); + } + if (cx && cond) { + OpJmp(m, rde); + } +} + +static relegated void OpLoope(struct Machine *m, uint32_t rde) { + Loop(m, rde, GetFlag(m->flags, FLAGS_ZF)); +} + +static relegated void OpLoopne(struct Machine *m, uint32_t rde) { + Loop(m, rde, !GetFlag(m->flags, FLAGS_ZF)); +} + +static relegated void OpLoop1(struct Machine *m, uint32_t rde) { + Loop(m, rde, true); +} + static const nexgen32e_f kOp0f6[] = { OpAlubiTest, OpAlubiTest, diff --git a/tool/build/lib/modrm.c b/tool/build/lib/modrm.c index d20f2fe97..601620e00 100644 --- a/tool/build/lib/modrm.c +++ b/tool/build/lib/modrm.c @@ -26,51 +26,7 @@ #include "tool/build/lib/modrm.h" #include "tool/build/lib/throw.h" -static relegated noinline int64_t ComputeAddressReal(const struct Machine *m, - uint32_t rde, uint8_t *s, - uint64_t i) { - switch (ModrmRm(rde)) { - case 0: - i += Read16(m->bx); - i += Read16(m->si); - break; - case 1: - i += Read16(m->bx); - i += Read16(m->di); - break; - case 2: - s = m->ss; - i += Read16(m->bp); - i += Read16(m->si); - break; - case 3: - s = m->ss; - i += Read16(m->bp); - i += Read16(m->di); - break; - case 4: - i += Read16(m->si); - break; - case 5: - i += Read16(m->di); - break; - case 6: - if (ModrmMod(rde)) { - s = m->ss; - i += Read16(m->bp); - } - break; - case 7: - i += Read16(m->bx); - break; - default: - unreachable; - } - i &= 0xffff; - return AddSegment(m, rde, i, s); -} - -int64_t ComputeAddress(const struct Machine *m, uint32_t rde) { +struct AddrSeg LoadEffectiveAddress(const struct Machine *m, uint32_t rde) { uint8_t *s = m->ds; uint64_t i = m->xedd->op.disp; DCHECK(!IsModrmRegister(rde)); @@ -100,10 +56,53 @@ int64_t ComputeAddress(const struct Machine *m, uint32_t rde) { if (Eamode(rde) == XED_MODE_LEGACY) { i &= 0xffffffff; } - return AddSegment(m, rde, i, s); } else { - return ComputeAddressReal(m, rde, s, i); + switch (ModrmRm(rde)) { + case 0: + i += Read16(m->bx); + i += Read16(m->si); + break; + case 1: + i += Read16(m->bx); + i += Read16(m->di); + break; + case 2: + s = m->ss; + i += Read16(m->bp); + i += Read16(m->si); + break; + case 3: + s = m->ss; + i += Read16(m->bp); + i += Read16(m->di); + break; + case 4: + i += Read16(m->si); + break; + case 5: + i += Read16(m->di); + break; + case 6: + if (ModrmMod(rde)) { + s = m->ss; + i += Read16(m->bp); + } + break; + case 7: + i += Read16(m->bx); + break; + default: + unreachable; + } + i &= 0xffff; } + return (struct AddrSeg){i, s}; +} + +int64_t ComputeAddress(const struct Machine *m, uint32_t rde) { + struct AddrSeg ea; + ea = LoadEffectiveAddress(m, rde); + return AddSegment(m, rde, ea.addr, ea.seg); } void *ComputeReserveAddressRead(struct Machine *m, uint32_t rde, size_t n) { diff --git a/tool/build/lib/modrm.h b/tool/build/lib/modrm.h index afa2cf74b..43410e4ce 100644 --- a/tool/build/lib/modrm.h +++ b/tool/build/lib/modrm.h @@ -50,9 +50,15 @@ COSMOPOLITAN_C_START_ #define SibIsAbsolute(x, r) (!SibHasBase(x, r) && !SibHasIndex(x)) #define IsRipRelative(x) (ModrmRm(x) == 5 && !ModrmMod(x)) +struct AddrSeg { + int64_t addr; + uint8_t *seg; +}; + extern const uint8_t kByteReg[32]; -int64_t ComputeAddress(const struct Machine *, uint32_t) nosideeffect; +int64_t ComputeAddress(const struct Machine *, uint32_t); +struct AddrSeg LoadEffectiveAddress(const struct Machine *, uint32_t); void *ComputeReserveAddressRead(struct Machine *, uint32_t, size_t); void *ComputeReserveAddressRead1(struct Machine *, uint32_t); diff --git a/tool/build/lib/pty.c b/tool/build/lib/pty.c index c1bdc58a1..7ef15182a 100644 --- a/tool/build/lib/pty.c +++ b/tool/build/lib/pty.c @@ -469,18 +469,21 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { } else if (!ThomPikeCont(p[i])) { pty->state = kMachinePtyUtf8; pty->u8 = ThomPikeByte(p[i]); + pty->n8 = ThomPikeLen(p[i]) - 1; } break; case kMachinePtyUtf8: if (ThomPikeCont(p[i])) { pty->u8 <<= 6; pty->u8 |= p[i] & 0b00111111; - } else { - SetMachinePtyCell(pty, pty->u8); - pty->state = kMachinePtyAscii; - pty->u8 = 0; - --i; + if (--pty->n8) { + break; + } } + SetMachinePtyCell(pty, pty->u8); + pty->state = kMachinePtyAscii; + pty->u8 = 0; + --i; break; case kMachinePtyEsc: if (p[i] == '[') { @@ -531,9 +534,6 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { abort(); } } - if (pty->u8) { - SetMachinePtyCell(pty, pty->u8); - } return n; } diff --git a/tool/build/lib/pty.h b/tool/build/lib/pty.h index e5a9ec093..4e91f22aa 100644 --- a/tool/build/lib/pty.h +++ b/tool/build/lib/pty.h @@ -28,6 +28,7 @@ struct MachinePty { uint32_t fg; uint32_t bg; uint32_t u8; + uint32_t n8; uint32_t *wcs; uint32_t *fgs; uint32_t *bgs; diff --git a/tool/build/lisp.c b/tool/build/lisp.c deleted file mode 100644 index 469864887..000000000 --- a/tool/build/lisp.c +++ /dev/null @@ -1,145 +0,0 @@ -/*-*- 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 "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" - -#define T short - -#define CONS 0 -#define ATOM 1 -#define INTEGER 2 - -#define NIL OBJECT(CONS, 0) -#define TYPE(x) ((x)&3) -#define VALUE(x) ((x) >> 2) -#define OBJECT(t, v) ((v) << 2 | (t)) - -static T Mi, Si; -static T M[2048]; -static char S[2048]; - -static noreturn void Die(void) { - abort(); -} - -static int Parse(const char *s) { - int x, j; - for (;;) { - switch (*s & 0xff) { - case ' ': - case '\t': - case '\r': - case '\n': - case '\v': - ++s; - break; - case 'A' ... 'Z': - x = Si; - do { - S[Si++] = *s++; - } while ('A' <= *s && *s <= 'Z'); - S[Si++] = 0; - M[++Mi] = OBJECT(ATOM, x); - return Mi; - case '0' ... '9': - x = 0; - do { - x *= 10; - x += *s++ - '0'; - } while ('0' <= *s && *s <= '9'); - M[++Mi] = OBJECT(INTEGER, x); - return Mi; - default: - Die(); - } - } -} - -static int PrintChar(int c) { - return putc(c, stdout); -} - -static void PrintString(const char *s) { - while (*s) PrintChar(*s++); -} - -static void PrintInteger(int x) { - int q, r; - q = x / 10; - r = x % 10; - if (q) PrintInteger(q); - PrintChar('0' + r); -} - -static void PrintObject(int i) { - int j, x; - switch (TYPE(M[i])) { - case CONS: - if ((i = VALUE(M[i]))) { - PrintChar('('); - PrintObject(i); - for (;;) { - if (TYPE(M[i + 1]) == CONS) { - if (!(i = VALUE(M[i + 1]))) break; - PrintChar(' '); - PrintObject(i); - } else { - PrintString(" . "); - PrintObject(i + 1); - break; - } - } - PrintChar(')'); - } else { - PrintString("NIL"); - } - break; - case ATOM: - for (j = VALUE(M[i]); S[j]; ++j) { - PrintChar(S[j]); - } - break; - case INTEGER: - PrintInteger(VALUE(M[i])); - break; - default: - unreachable; - } -} - -static void Print(int i) { - PrintObject(i); - PrintChar('\n'); -} - -int main(int argc, char *argv[]) { - int i; - - M[1] = OBJECT(CONS, 2); - M[2] = OBJECT(INTEGER, 123); - M[3] = OBJECT(CONS, 4); - M[4] = OBJECT(INTEGER, 456); - M[5] = OBJECT(CONS, 6); - M[6] = OBJECT(INTEGER, 789); - M[7] = NIL; - Print(1); - - return 0; -} diff --git a/tool/emacs/cosmo-c-builtins.el b/tool/emacs/cosmo-c-builtins.el index d50174c0c..f86769393 100644 --- a/tool/emacs/cosmo-c-builtins.el +++ b/tool/emacs/cosmo-c-builtins.el @@ -256,6 +256,7 @@ "__builtin_lrintf" "__builtin_lrintl" "__builtin_memcpy" + "__builtin_memcmp" "__builtin_memset" "__builtin_strlen")) diff --git a/tool/emacs/cosmo-c-keywords.el b/tool/emacs/cosmo-c-keywords.el index 4186d9e96..6d7193bfa 100644 --- a/tool/emacs/cosmo-c-keywords.el +++ b/tool/emacs/cosmo-c-keywords.el @@ -126,6 +126,7 @@ (cosmo '("__rbx" "__msabi" + "offsetof" "microarchitecture" "targetclones" "testonly" diff --git a/tool/emacs/cosmo-cpp-constants.el b/tool/emacs/cosmo-cpp-constants.el index be86fde75..20ebdaee1 100644 --- a/tool/emacs/cosmo-cpp-constants.el +++ b/tool/emacs/cosmo-cpp-constants.el @@ -141,6 +141,7 @@ "OPEN_MAX" "ATEXIT_MAX" "IM_FEELING_NAUGHTY" + "__REAL_MODE__" "__x86__" "__i386__"))