/*-*- 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 │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ │ above copyright notice and this permission notice appear in all copies. │ │ │ │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ // @fileoverview lisp.c built for real mode with manual tuning // binary footprint is approximately 824 bytes, about 40 bytes // of it is overhead needed to load the second 512-byte sector // so if we can find a way to reduce the code size another 300 // bytes we can bootstrap the metacircular evaluator in an mbr #define NIL 0 #define UNDEFINED 8 #define ATOM_T 30 #define ATOM_QUOTE 34 #define ATOM_ATOM 46 #define ATOM_EQ 56 #define ATOM_COND 62 #define ATOM_CAR 72 #define ATOM_CDR 80 #define ATOM_CONS 88 #define ATOM_LAMBDA 98 #define STR 0x4186 //////////////////////////////////////////////////////////////////////////////// .section .start,"ax",@progbits .globl main .code16 main: mov $q.syntax,%bx mov $32,%al mov %al,32(%bx) mov %al,13(%bx) mov %al,10(%bx) movw $10536,40(%bx) movb $46,46(%bx) mov $STR,%di mov $kSymbols,%si mov $56,%cx rep movsb 0: call GetChar mov %ax,q.look call GetToken call GetObject xchg %ax,%di mov q.globals,%si call Eval xchg %ax,%di call PrintObject mov $kCrlf,%si call PrintString jmp 0b GetChar:xor %ax,%ax # get keystroke int $0x16 # keyboard service xor %ah,%ah # ah is bios scancode push %ax # al is ascii character call PutChar # ax will have result cmp $'\r',%al # don't clobber stuff jne 1f mov $'\n',%al call PutChar 1: pop %ax ret Cadr: and $-2,%di # (object >> 1) * sizeof(word) mov 2(%di),%di # contents of decrement register and $-2,%di # contents of address register mov (%di),%ax ret GetToken: xor %bx,%bx mov $q.syntax,%si mov q.look,%ax mov $q.token,%di 0: mov %al,%bl mov (%bx,%si),%dl mov %dl,%bl cmp $0x20,%dl jne 1f call GetChar jmp 0b 1: test %dl,%dl je 3f stosb call GetChar jmp 4f 2: test %bl,%bl jne 4f stosb call GetChar mov %ax,%bx mov (%bx,%si),%bl 3: test %al,%al jne 2b 4: movb $0,(%di) mov %al,q.look ret Assoc: xchg %si,%bx 0: test %bx,%bx je 2f and $-2,%bx mov (%bx),%si and $-2,%si mov (%si),%ax cmp %di,%ax jne 1f mov (%bx),%si and $-2,%si mov 2(%si),%ax ret 1: mov 2(%bx),%bx jmp 0b 2: xor %ax,%ax ret GetObject: cmpb $40,q.token je GetList mov $q.token,%di // 𝑠𝑙𝑖𝑑𝑒 Intern: mov %di,%bx mov $STR,%si 0: mov %bx,%di push %si lodsb test %al,%al jne 2f pop %di push %di mov %bx,%si 4: lodsb stosb test %al,%al jnz 4b 6: pop %ax sub $STR,%ax shl %ax ret 1: lodsb 2: scasb jne 5f test %al,%al jne 1b jmp 6b 5: pop %di 3: test %al,%al jz 0b lodsb jmp 3b GetList:call GetToken mov q.token,%al cmp $')',%al je 2f cmp $'.',%al je 1f call GetObject push %ax # save call GetList xchg %ax,%si pop %di # restore jmp Cons 1: call GetToken jmp GetObject 2: xor %ax,%ax ret EvalCons: push %dx # save mov 2(%bx),%bx mov %bx,%di call Cadr xchg %ax,%di mov %bp,%si call Eval mov %bp,%si pop %di # restore push %ax # save call Arg1 pop %si # restore xchg %ax,%di pop %bp // jmp Cons // 𝑠𝑙𝑖𝑑𝑒 Cons: mov $q.index,%bx mov (%bx),%ax addw $2,(%bx) shl %ax mov %ax,%bx mov %di,(%bx) mov %si,2(%bx) or $1,%ax ret Bind: test %di,%di je 1f push %bp and $-2,%si and $-2,%di mov %di,%bp push %dx # save no. 1 push %si # save no. 2 mov 2(%si),%si mov 2(%di),%di call Bind pop %si # rest no. 2 mov (%si),%di pop %si # rest no. 1 push %ax # save no. 3 call Eval mov %ds:(%bp),%di xchg %ax,%si call Cons pop %si # rest no. 3 xchg %ax,%di pop %bp jmp Cons 1: xchg %dx,%ax ret PrintString: # nul-terminated in si 0: lodsb # don't clobber bp, bx test %al,%al je 1f call PutChar jmp 0b 1: ret PutChar:push %bx # don't clobber bp,bx,di,si,cx push %bp # original ibm pc scroll up bug mov $7,%bx # normal mda/cga style page zero mov $0x0e,%ah # teletype output al cp437 int $0x10 # vidya service pop %bp # preserves al pop %bx ret //////////////////////////////////////////////////////////////////////////////// .text PrintObject: test $1,%di jnz 1f shr %di lea STR(%di),%si jmp PrintString 1: push %bx mov %di,%bx mov $40,%al call PutChar 2: and $-2,%bx mov (%bx),%di call PrintObject mov 2(%bx),%bx test %bx,%bx jz 4f test $1,%bl jz 3f mov $0x20,%al call PutChar jmp 2b 3: mov $kDot,%si call PrintString mov %bx,%di call PrintObject 4: pop %bx mov $41,%al // jmp PutChar // 𝑠𝑙𝑖𝑑𝑒 Arg1ds: mov %dx,%di mov %bp,%si // 𝑠𝑙𝑖𝑑𝑒 Arg1: call Cadr xchg %ax,%di // jmp Eval // 𝑠𝑙𝑖𝑑𝑒 Eval: push %bp mov %di,%dx mov %si,%bp 0: test $1,%dl jne 1f xchg %bp,%si xchg %dx,%di pop %bp jmp Assoc 1: mov %dx,%bx and $-2,%bx mov (%bx),%ax test $1,%al je 1f mov (%bx),%di and $-2,%di cmpw $ATOM_LAMBDA,(%di) jne EvalUndefined mov 2(%bx),%si mov (%bx),%di push %bx call Cadr xchg %ax,%di mov %bp,%dx call Bind xchg %ax,%bp pop %bx mov (%bx),%bx mov %bx,%di and $-2,%di mov 2(%di),%di jmp EvalCadrLoop 1: mov (%bx),%ax cmp $ATOM_COND,%ax je EvalCond jg 2f cmp $ATOM_ATOM,%ax je EvalAtom jg 1f test %ax,%ax je EvalUndefined cmp $ATOM_QUOTE,%ax jne EvalCall // 𝑠𝑙𝑖𝑑𝑒 EvalQuote: xchg %dx,%di pop %bp jmp Cadr 1: cmp $ATOM_EQ,%ax jne EvalCall // 𝑠𝑙𝑖𝑑𝑒 EvalEq: push %dx mov 2(%bx),%bx mov %bx,%di call Cadr xchg %ax,%di mov %bp,%si call Eval mov %bp,%si pop %di # restore push %ax # save call Arg1 pop %dx # restore cmp %dx,%ax jmp 3f EvalCdr: push $2 jmp EvalCarCdr EvalUndefined: mov $UNDEFINED,%ax 9: pop %bp ret EvalCond: mov 2(%bx),%bx and $-2,%bx mov (%bx),%di and $-2,%di mov (%di),%di mov %bp,%si push %bx # save call Eval pop %bx # restore test %ax,%ax je EvalCond mov (%bx),%di jmp EvalCadrLoop 2: cmp $ATOM_CDR,%ax je EvalCdr cmp $ATOM_CONS,%ax je EvalCons cmp $ATOM_CAR,%ax jne EvalCall // 𝑠𝑙𝑖𝑑𝑒 EvalCar: push $0 // 𝑠𝑙𝑖𝑑𝑒 EvalCarCdr: call Arg1ds and $-2,%ax xchg %ax,%di pop %bx mov (%bx,%di),%ax jmp 9b EvalCall: push 2(%bx) mov (%bx),%di mov %bp,%si call Assoc xchg %ax,%di pop %si call Cons jmp 1f EvalAtom: call Arg1ds test $1,%al 3: mov $ATOM_T,%ax je 9b xor %ax,%ax jmp 9b EvalCadrLoop: call Cadr 1: xchg %ax,%dx jmp 0b //////////////////////////////////////////////////////////////////////////////// .section .rodata,"a",@progbits kDot: .string " . " kCrlf: .string "\r\n" kSymbols: .string "NIL" .string "*UNDEFINED" .string "T" .string "QUOTE" .string "ATOM" .string "EQ" .string "COND" .string "CAR" .string "CDR" .string "CONS" .string "LAMBDA"