Here's some screenshots of an emulator tui program that was compiled on Linux, then scp'd it to Windows, Mac, and FreeBSD. https://justine.storage.googleapis.com/blinkenlights-cmdexe.png https://justine.storage.googleapis.com/blinkenlights-imac.png https://justine.storage.googleapis.com/blinkenlights-freebsd.png https://justine.storage.googleapis.com/blinkenlights-lisp.png How is this even possible that we have a nontrivial ui binary that just works on Mac, Windows, Linux, and BSD? Surely a first ever achievement. Fixed many bugs. Bootstrapped John McCarthy's metacircular evaluator on bare metal in half the size of Altair BASIC (about 2.5kb) and ran it in emulator for fun and profit.
183 lines
8.5 KiB
C
183 lines
8.5 KiB
C
/*───────────────────────────────────────────────────────────────────────────│─╗
|
|
│ The LISP Challenge § Hardware Integration w/ x86_64 Linux & 8086 PC BIOS ─╬─│┼
|
|
╚────────────────────────────────────────────────────────────────────────────│*/
|
|
|
|
#define CompilerBarrier() asm volatile("" ::: "memory");
|
|
|
|
#define TYPE(x) /* a.k.a. x&1 */ \
|
|
({ \
|
|
char IsAtom; \
|
|
asm("test%z1\t$1,%1" : "=@ccnz"(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 PEEK_(REG, BASE, INDEX, DISP) \
|
|
({ \
|
|
__typeof(*(BASE)) Reg; \
|
|
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
|
|
asm("mov\t%c2(%1),%0" \
|
|
: REG(Reg) \
|
|
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \
|
|
"m"(BASE[(INDEX) + (DISP)])); \
|
|
} else { \
|
|
asm("mov\t%c3(%1,%2),%0" \
|
|
: REG(Reg) \
|
|
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
|
|
"i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \
|
|
} \
|
|
Reg; \
|
|
})
|
|
|
|
#define PEEK(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
|
|
(sizeof(*(BASE)) == 1 ? PEEK_("=Q", BASE, INDEX, DISP) \
|
|
: PEEK_("=r", BASE, INDEX, DISP))
|
|
|
|
#define PEEK_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP) \
|
|
({ \
|
|
__typeof(*(OBJECT->MEMBER)) Reg; \
|
|
if (!(OBJECT)) { \
|
|
asm("mov\t%c2(%1),%0" \
|
|
: REG(Reg) \
|
|
: "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
|
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
|
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
|
|
"m"(OBJECT->MEMBER)); \
|
|
} else { \
|
|
asm("mov\t%c3(%1,%2),%0" \
|
|
: REG(Reg) \
|
|
: "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
|
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
|
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
|
|
"m"(OBJECT->MEMBER)); \
|
|
} \
|
|
Reg; \
|
|
})
|
|
|
|
#define PEEK_ARRAY(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
|
|
(sizeof(*(OBJECT->MEMBER)) == 1 \
|
|
? PEEK_ARRAY_("=Q", OBJECT, MEMBER, INDEX, DISP) \
|
|
: PEEK_ARRAY_("=r", OBJECT, MEMBER, INDEX, DISP))
|
|
|
|
#define POKE_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \
|
|
do { \
|
|
if (!(OBJECT)) { \
|
|
asm("mov\t%1,%c3(%2)" \
|
|
: "=m"(OBJECT->MEMBER) \
|
|
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
|
|
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
|
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
|
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
|
} else { \
|
|
asm("mov\t%1,%c4(%2,%3)" \
|
|
: "=m"(OBJECT->MEMBER) \
|
|
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
|
|
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
|
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
|
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define POKE_ARRAY(OBJECT, MEMBER, INDEX, DISP, VALUE) /* o->m[i]=v */ \
|
|
do { \
|
|
__typeof(*(OBJECT->MEMBER)) Reg; \
|
|
switch (sizeof(*(OBJECT->MEMBER))) { \
|
|
case 1: \
|
|
POKE_ARRAY_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \
|
|
break; \
|
|
default: \
|
|
POKE_ARRAY_("r", OBJECT, MEMBER, INDEX, DISP, VALUE); \
|
|
break; \
|
|
} \
|
|
} while (0)
|
|
|
|
int setjmp(void *) __attribute__((__returns_twice__));
|
|
int longjmp(void *, int) __attribute__((__noreturn__));
|
|
|
|
static inline 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 inline 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 void PrintChar(long c) {
|
|
#ifdef __REAL_MODE__
|
|
asm volatile("mov\t$0x0E,%%ah\n\t"
|
|
"int\t$0x10"
|
|
: /* no outputs */
|
|
: "a"(c), "b"(7)
|
|
: "memory");
|
|
#else
|
|
static short buf;
|
|
int rc;
|
|
buf = c;
|
|
asm volatile("syscall"
|
|
: "=a"(rc)
|
|
: "0"(1), "D"(1), "S"(&buf), "d"(1)
|
|
: "rcx", "r11", "memory");
|
|
#endif
|
|
}
|
|
|
|
static 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 c;
|
|
}
|