Browse Source

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.
main
Justine Tunney 2 years ago
parent
commit
b6793d42d5
  1. 6
      build/definitions.mk
  2. 37
      build/realify.sed
  3. 2
      libc/log/asan.c
  4. 2
      libc/runtime/runtime.mk
  5. 4
      libc/str/thompike.h
  6. 7
      third_party/stb/stb.mk
  7. 5
      third_party/xed/x86.h
  8. 79
      third_party/xed/x86ild.greg.c
  9. 2
      third_party/xed/x86tab.S
  10. 15
      tool/build/emubin/emubin.mk
  11. 52
      tool/build/emubin/lisp.lds
  12. 12
      tool/build/emubin/lisp.lisp
  13. 632
      tool/build/emubin/lisp.real.c
  14. 52
      tool/build/emubin/lispstart.S
  15. 1
      tool/build/emubin/mdatest.real.c
  16. 8
      tool/build/emubin/real.h
  17. 7
      tool/build/emubin/realstart.inc
  18. 1
      tool/build/emubin/spiral.real.c
  19. 17
      tool/build/emulator.c
  20. 4
      tool/build/lib/address.c
  21. 1
      tool/build/lib/address.h
  22. 10
      tool/build/lib/bcd.c
  23. 23
      tool/build/lib/dis.c
  24. 5
      tool/build/lib/dis.h
  25. 76
      tool/build/lib/disarg.c
  26. 88
      tool/build/lib/machine.c
  27. 93
      tool/build/lib/modrm.c
  28. 8
      tool/build/lib/modrm.h
  29. 16
      tool/build/lib/pty.c
  30. 1
      tool/build/lib/pty.h
  31. 145
      tool/build/lisp.c
  32. 1
      tool/emacs/cosmo-c-builtins.el
  33. 1
      tool/emacs/cosmo-c-keywords.el
  34. 1
      tool/emacs/cosmo-cpp-constants.el

6
build/definitions.mk

@ -321,6 +321,7 @@ OBJECTIFY.real.c = \ @@ -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 = \ @@ -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 = \

37
build/realify.sed

@ -16,11 +16,12 @@ @@ -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/ @@ -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/ @@ -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/ @@ -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/ @@ -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/ @@ -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/ @@ -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/ @@ -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/ @@ -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/ @@ -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/ @@ -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/ @@ -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 @@ -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

2
libc/log/asan.c

@ -238,7 +238,7 @@ static void __asan_deallocate(char *p, int kind) { @@ -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));
}

2
libc/runtime/runtime.mk

@ -69,8 +69,6 @@ o/$(MODE)/libc/runtime/winmain.greg.o: \ @@ -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))

4
libc/str/thompike.h

@ -3,6 +3,8 @@ @@ -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_ */

7
third_party/stb/stb.mk vendored

@ -63,10 +63,11 @@ $(THIRD_PARTY_STB_A_OBJS): \ @@ -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 += \

5
third_party/xed/x86.h vendored

@ -361,8 +361,8 @@ struct XedOperands { /* @@ -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 { /* @@ -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

79
third_party/xed/x86ild.greg.c vendored

@ -130,8 +130,8 @@ static const struct XedDenseMagnums { @@ -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) { @@ -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) { @@ -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( @@ -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( @@ -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( @@ -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) { @@ -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 {

2
third_party/xed/x86tab.S vendored

@ -258,7 +258,7 @@ xed_imm_bits_2d.rodata: @@ -258,7 +258,7 @@ xed_imm_bits_2d.rodata:
.byte 1,0x07 # 6969 i
.byte 2,0x05 # 6a6b jk
.byte 20,0x01 # 6c7f l
.byte 1,0x05 # 8080 Ç
.byte 1,0x05 # 8080 Ç
.byte 1,0x07 # 8181 ü
.byte 2,0x05 # 8283 éâ
.byte 22,0x01 # 8499 äÖ

15
tool/build/emubin/emubin.mk

@ -49,6 +49,21 @@ o/$(MODE)/tool/build/emubin/%.bin.dbg: \ @@ -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

52
tool/build/emubin/lisp.lds

@ -0,0 +1,52 @@ @@ -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;

12
tool/build/emubin/lisp.lisp

@ -0,0 +1,12 @@ @@ -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)))

632
tool/build/emubin/lisp.real.c

@ -0,0 +1,632 @@ @@ -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;
}

52
tool/build/emubin/lispstart.S

@ -0,0 +1,52 @@ @@ -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

1
tool/build/emubin/mdatest.real.c

@ -19,6 +19,7 @@ @@ -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 \

8
tool/build/emubin/real.h

@ -3,14 +3,6 @@ @@ -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));
}

7
tool/build/emubin/realstart.inc

@ -0,0 +1,7 @@ @@ -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");

1
tool/build/emubin/spiral.real.c

@ -19,6 +19,7 @@ @@ -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)

17
tool/build/emulator.c

@ -239,6 +239,13 @@ static bool IsCall(void) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -1512,6 +1520,7 @@ static void OnInt15h(void) {
}
static bool OnHalt(int interrupt) {
ReactiveDraw();
switch (interrupt) {
case 1:
case 3:

4
tool/build/lib/address.c

@ -23,6 +23,10 @@ @@ -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:

1
tool/build/lib/address.h

@ -6,6 +6,7 @@ @@ -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);

10
tool/build/lib/bcd.c

@ -23,7 +23,7 @@ @@ -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) { @@ -39,7 +39,7 @@ void OpDas(struct Machine *m, uint32_t rde) {