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
parent
fd22e55b42
commit
b6793d42d5
|
@ -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 = \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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 += \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 ä─Ö
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
|
@ -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)))
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 \
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -256,6 +256,7 @@
|
|||
"__builtin_lrintf"
|
||||
"__builtin_lrintl"
|
||||
"__builtin_memcpy"
|
||||
"__builtin_memcmp"
|
||||
"__builtin_memset"
|
||||
"__builtin_strlen"))
|
||||
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
(cosmo
|
||||
'("__rbx"
|
||||
"__msabi"
|
||||
"offsetof"
|
||||
"microarchitecture"
|
||||
"targetclones"
|
||||
"testonly"
|
||||
|
|
|
@ -141,6 +141,7 @@
|
|||
"OPEN_MAX"
|
||||
"ATEXIT_MAX"
|
||||
"IM_FEELING_NAUGHTY"
|
||||
"__REAL_MODE__"
|
||||
"__x86__"
|
||||
"__i386__"))
|
||||
|
||||
|
|
Loading…
Reference in New Issue