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 2020-10-01 01:20:13 -07:00
parent fd22e55b42
commit b6793d42d5
34 changed files with 1056 additions and 358 deletions

View File

@ -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 = \

View File

@ -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

View File

@ -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));
}

View File

@ -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))

View File

@ -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_ */

View File

@ -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 += \

View File

@ -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

View File

@ -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 {

View File

@ -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 äÖ

View File

@ -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

View File

@ -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;

View File

@ -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)))

View File

@ -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;
}

View File

@ -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

View File

@ -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 \

View File

@ -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));
}

View File

@ -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");

View File

@ -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)

View File

@ -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:

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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 *);

View File

@ -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) {

View File

@ -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,

View File

@ -26,9 +26,37 @@
#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) {
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));
if (Eamode(rde) != XED_MODE_REAL) {
if (!SibExists(rde)) {
if (IsRipRelative(rde)) {
if (Mode(rde) == XED_MODE_LONG) {
i += m->ip;
}
} else {
i += Read64(RegRexbRm(m, rde));
if (RexbRm(rde) == 4 || RexbRm(rde) == 5) {
s = m->ss;
}
}
} else {
if (SibHasBase(m->xedd, rde)) {
i += Read64(RegRexbBase(m, rde));
if (RexbBase(m, rde) == 4 || RexbBase(m, rde) == 5) {
s = m->ss;
}
}
if (SibHasIndex(m->xedd)) {
i += Read64(RegRexxIndex(m)) << m->xedd->op.scale;
}
}
if (Eamode(rde) == XED_MODE_LEGACY) {
i &= 0xffffffff;
}
} else {
switch (ModrmRm(rde)) {
case 0:
i += Read16(m->bx);
@ -67,43 +95,14 @@ static relegated noinline int64_t ComputeAddressReal(const struct Machine *m,
unreachable;
}
i &= 0xffff;
return AddSegment(m, rde, i, s);
}
return (struct AddrSeg){i, s};
}
int64_t ComputeAddress(const struct Machine *m, uint32_t rde) {
uint8_t *s = m->ds;
uint64_t i = m->xedd->op.disp;
DCHECK(!IsModrmRegister(rde));
if (Eamode(rde) != XED_MODE_REAL) {
if (!SibExists(rde)) {
if (IsRipRelative(rde)) {
if (Mode(rde) == XED_MODE_LONG) {
i += m->ip;
}
} else {
i += Read64(RegRexbRm(m, rde));
if (RexbRm(rde) == 4 || RexbRm(rde) == 5) {
s = m->ss;
}
}
} else {
if (SibHasBase(m->xedd, rde)) {
i += Read64(RegRexbBase(m, rde));
if (RexbBase(m, rde) == 4 || RexbBase(m, rde) == 5) {
s = m->ss;
}
}
if (SibHasIndex(m->xedd)) {
i += Read64(RegRexxIndex(m)) << m->xedd->op.scale;
}
}
if (Eamode(rde) == XED_MODE_LEGACY) {
i &= 0xffffffff;
}
return AddSegment(m, rde, i, s);
} else {
return ComputeAddressReal(m, rde, s, i);
}
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) {

View File

@ -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);

View File

@ -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 {
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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -256,6 +256,7 @@
"__builtin_lrintf"
"__builtin_lrintl"
"__builtin_memcpy"
"__builtin_memcmp"
"__builtin_memset"
"__builtin_strlen"))

View File

@ -126,6 +126,7 @@
(cosmo
'("__rbx"
"__msabi"
"offsetof"
"microarchitecture"
"targetclones"
"testonly"

View File

@ -141,6 +141,7 @@
"OPEN_MAX"
"ATEXIT_MAX"
"IM_FEELING_NAUGHTY"
"__REAL_MODE__"
"__x86__"
"__i386__"))