diff options
Diffstat (limited to 'quine.asm')
| -rw-r--r-- | quine.asm | 703 |
1 files changed, 591 insertions, 112 deletions
diff --git a/quine.asm b/quine.asm index b828b90..d588b1c 100644 --- a/quine.asm +++ b/quine.asm @@ -2000,10 +2000,288 @@ cold_start: ; store, but the only thing early_variable is doing is returning an ; address.) + ; Now we define a bunch of ordinary Forth words. Since we'll only ever be + ; calling these copies from within Forth, we're no longer limited by + ; flatassembler's syntax, so a lot of them have slightly different names + ; than they've had up to now. This will be noted below. + dq litstring, "exit", early_create, early_self_codeword, early_here, fetch dq rsi, pack_popcontrol, pack_next + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "swap", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "drop", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "drop2". + dq litstring, "2drop", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "roll", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; Jonesforth calls this "-roll" and we could do that, but honestly the name + ; unroll sounds nicer and it's only a single character longer. You might say + ; it rolls off the tongue better. + dq litstring, "unroll", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "roll3". + dq litstring, "3roll", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "unroll3". + dq litstring, "3unroll", early_create, early_self_codeword, early_here + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "dup", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "dup2". + dq litstring, "2dup", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "add". + dq litstring, "+", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "sub". + dq litstring, "-", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "mul". + dq litstring, "*", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "divmod". Jonesforth calls it "/mod" but % is widely recognized + ; and has no special Forth significance. + dq litstring, "/%", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "eq". Jonesforth calls it "="; most languages would call it "==". + dq litstring, "=", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "ne". Jonesforth calls it "<>", but even most modern SQL dialects + ; recognize C's legacy and allow "!=" these days. As someone who learned C + ; in childhood, this is not actually a hard call for us. + dq litstring, "!=", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "gt". + dq litstring, ">", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "lt". + dq litstring, "<", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "ge". + dq litstring, ">=", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "le". + dq litstring, "<=", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "and", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "or", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "xor", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "invert", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "lit", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "litstring", early_create, early_self_codeword, early_here + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This is "store", but since we're finally in Forth we're no longer limited + ; by flatassembler's syntax, so we call it by its usual Forth name. + dq litstring, "!", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This is "fetch". + dq litstring, "@", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This is "addstore". + dq litstring, "+!", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This is "substore". + dq litstring, "-!", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "store8". Jonesforth calls it "c!" but we categorically reject + ; the use of letters that are meant to indicate byte sizes. It's unfriendly + ; to newcomers. There's some risk that this name will be confused for + ; meaning "store a value of 8", but that would not be a useful task, so + ; hopefully it'll be okay. + dq litstring, "8!", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "fetch8". + dq litstring, "8@", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "store16". + dq litstring, "16!", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "fetch16". + dq litstring, "16@", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "store32". + dq litstring, "32!", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "fetch32". + dq litstring, "32@", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "memcopy", early_create, early_self_codeword, early_here + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; TODO why does this crash? + ;dq litstring, "stringlen", early_create, early_self_codeword, early_here + ;dq fetch + ; TODO + ;dq lit, 8, packalign, early_here_store + + dq litstring, "branch", early_create, early_self_codeword, early_here, fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; This was "zbranch". + dq litstring, "0branch", early_create, early_self_codeword, early_here + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; TODO why does this crash? + ;dq litstring, "sys_exit", early_create, early_self_codeword, early_here + ;dq fetch + ; TODO + ;dq lit, 8, packalign, early_here_store + + dq litstring, "sys_write", early_create, early_self_codeword, early_here + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "emitstring", early_create, early_self_codeword, early_here + ; TODO this is actually in Forth + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "crash", early_create, early_self_codeword, early_here, fetch + ; TODO dq lit, 8, packalign, early_here_store + ; We could in theory call this 64pack, by analogy to 32! and so on, but it's + ; kept like this to be more similar to reg64, imm64 etc. + ; + ; Also, unlike with ! we specify the size even for the 64-bit version, + ; because the user of pack64 and its variants is always doing something + ; where exact byte sizes are crucial. + dq litstring, "pack64", early_create, early_self_codeword, early_here, fetch + ; TODO this is actually in Forth + dq lit, 8, packalign, early_here_store + + dq litstring, "pack32", early_create, early_self_codeword, early_here, fetch + ; TODO this is actually in Forth + dq lit, 8, packalign, early_here_store + + dq litstring, "pack16", early_create, early_self_codeword, early_here, fetch + ; TODO this is actually in Forth + dq lit, 8, packalign, early_here_store + + dq litstring, "pack8", early_create, early_self_codeword, early_here, fetch + ; TODO this is actually in Forth + dq lit, 8, packalign, early_here_store + + dq litstring, "packstring", early_create, early_self_codeword, early_here + dq fetch + ; TODO this is actually in Forth + dq lit, 8, packalign, early_here_store + + dq litstring, "packalign", early_create, early_self_codeword, early_here + dq fetch + ; TODO this is actually in Forth + dq lit, 8, packalign, early_here_store + + dq litstring, "litpack64", early_create, early_self_codeword, early_here + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "litpack32", early_create, early_self_codeword, early_here + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + dq litstring, "litpack16", early_create, early_self_codeword, early_here + dq fetch + ; TODO + dq lit, 8, packalign, early_here_store + + ; TODO why does this crash? + ;dq litstring, "litpack8", early_create, early_self_codeword, early_here + ;dq fetch + ; TODO + ;dq lit, 8, packalign, early_here_store + ;;; For triage's sake, here's an inventory of everything else in the file. ;;; ;;; Macros: @@ -2020,11 +2298,19 @@ cold_start: ;;; lit, litstring ;;; store, fetch ;;; addstore, substore, store8, fetch8, store16, fetch16, store32, fetch32 - ;;; ccopy, stringlen + ;;; memcopy, stringlen ;;; no dependencies except next for any of these ;;; branch, zbranch ;;; sorta needs a label but might be avoidable ;;; needs next + ;;; sys_exit, sys_write + ;;; nothing special + ;;; Forth: + ;;; emitstring + ;;; stringlen, sys_write, basics + ;;; Assembly: + ;;; crash + ;;; nothing special ;;; Forth: ;;; pack64, pack32, pack16, pack8, packstring, packalign ;;; only the basics above @@ -2034,14 +2320,44 @@ cold_start: ;;; Forth: ;;; rax, rcx, dx, rbx, rsp, rbp, rsi, rdi ;;; r8, r9, r10, r11, r12, r13, r14, r15 + ;;; eax, ecx, edx, ebx, esp, ebp, esi, edi + ;;; ax, cx, dx, bx, sp, bp, si, di + ;;; al, cl, dl, bl, ah, ch, dh, bh + ;;; cc_overflow, cc_no_overflow, cc_below, cc_above_equal, cc_equal, + ;;; cc_not_equal, cc_below_equal, cc_above, cc_sign, cc_not_sign, cc_even, + ;;; cc_odd, cc_less, cc_greater_equal, cc_less_equal, cc_greater ;;; only the basics above - ;;; reg64, extrareg64 + ;;; reg64, extrareg64, reg32, reg16, reg8, scalefield, conditioncode ;;; only the basics plus optional emitstring and sys_exit ;;; (notice that these are forward references!) - ;;; rex_w, rex_wb, opcodereg, modrm + ;;; rex_w, rex_wb, opcodereg, opcodecc, modrm, sib ;;; only the basics above - ;;; cld, mov_reg64_imm64, mov_reg64_reg64, push_reg64, lodsq - ;;; jmp_abs_indirect_reg64, syscall + ;;; addressing_reg64, addressing_indirect_reg64, addressing_disp8_reg64, + ;;; addressing_indexed_reg64, addressing_disp8_indexed_reg64 + ;;; basics plus earlier assembly stuff + ;;; cld, std, mov_reg64_imm64, mov_extrareg64_imm64, mov_reg64_reg64, + ;;; mov_indirect_reg64_reg64, mov_disp8_reg64_reg64, + ;;; mov_reg64_indirect_reg64, mov_reg64_disp8_reg64, + ;;; mov_reg64_indexed_reg64, + ;;; mov_indirect_reg64_reg32, mov_disp8_reg64_reg32, + ;;; mov_reg32_indirect_reg64, mov_reg32_disp8_reg64, + ;;; mov_indirect_reg64_reg16, mov_disp8_reg64_reg16, + ;;; mov_reg16_indirect_reg64, mov_reg16_disp8_reg64, + ;;; mov_indirect_reg64_reg8, mov_disp8_reg64_reg8, + ;;; mov_reg8_indirect_reg64, mov_reg8_disp8_reg64, + ;;; lea_reg64_disp8_reg64, lea_reg64_indexed_reg64, + ;;; lea_reg64_disp8_indexed_reg64, + ;;; push_reg64, pop_reg64, + ;;; lodsq, rep_movs8, rep_mov16, rep_movs32, rep_movs64, repnz_scas8, + ;;; add_reg64_reg64, add_indirect_reg64_reg64, add_reg64_indirect_reg64, + ;;; add_reg64_imm8, + ;;; sub_reg64_reg64, sub_indirect_reg64_reg64, + ;;; mul_reg64_reg64, divmod_reg64, idivmod_reg64, inc_reg64, dec_reg64, + ;;; and_reg64_reg64, or_reg64_reg64, or_reg64_imm8, xox_reg64_reg64, + ;;; not_reg64, + ;;; cmp_reg64_reg64, test_reg64_reg64, + ;;; set_reg8_cc, jmp_cc_rel_imm8, jmp_abs_indirect_reg64, jmp_rel_imm32, + ;;; syscall, hlt ;;; basics plus assembly helpers ;;; Forth, not needed on heap: ;;; early_heap, early_s0, early_r0, early_latest, early_here @@ -2054,20 +2370,12 @@ cold_start: ;;; Forth, subject to reconsideration: ;;; quit ;;; quine, sys_exit (these are forward references) - ;;; Assembly: - ;;; sys_exit, sys_write - ;;; nothing special ;;; Forth: - ;;; emitstring - ;;; stringlen, sys_write, basics ;;; quine ;;; early_here (removable), all_contents (forward) ;;; sys_write, basics ;;; use_label, set_label ;;; only basics - ;;; Assembly: - ;;; hlt - ;;; nothing ;;; Forth: ;;; all_contents, elf_file_header, elf_program_header ;;; output_start_routine @@ -2658,17 +2966,25 @@ defword fetch32, 0 ; logic, see roll and unroll. This always copies at byte granularity, for ease ; of implementation. ; -; Jonesforth calls this CMOVE and has a ccopy which is a single byte. POSIX, +; Jonesforth calls this CMOVE and has a CCOPY which is a single byte. POSIX, ; however, has memcpy() for non-overlapping blocks and memmove() for ; overlapping blocks. We follow the latter convention, because it feels like ; the more important distinction. ; +; Also, the "c" was meant to indicate that it works at one-byte granularity, +; but that isn't, uh... actually an important property here, and as a blanket +; call we're not using letters to denote data sizes. So we call it "memcopy". +; Apologies to C programmers but vowels are good, actually. +; +; Jonesforth also offers C@C! as another name for its CCOPY, but neither +; "@!" nor "mem@mem!" seems particulaly nice. +; ; Stack in: ; destination ; source ; length ; (top) -defword ccopy, 0 +defword memcopy, 0 dq $ + 8 ; We need to save and restore rsi; the other registers we can trample. mov.qreg.qreg rdx, rsi @@ -2724,6 +3040,83 @@ zbranch_after_jmp: next +;;;;;;;;;;;;;;;;;;;; +;;; System calls ;;; +;;;;;;;;;;;;;;;;;;;; +;;; +;;; The kernel preserves every register except rax, rcx, and r11. The system +;;; call number goes in rax, as does the return value. Parameters go in rdi, +;;; rsi, rdx, r10, r8, and r9, in that order. [SysV] A.2.1. +;;; +;;; Notice that rsi is our control stack, so we have to save it (for +;;; syscalls with at least two parameters). We can use the value stack to do +;;; that, since rsp is preserved. We don't save other registers because our +;;; caller should do that, if it cares. +;;; + +;;; +;;; This does the Linux exit() system call, passing it an exit code taken +;;; from the stack. +;;; +defword sys_exit, 0 + dq $ + 8 + + mov.qreg.qimm rax, 60 ; syscall number + pop.qreg rdi ; exit code + syscall + + ; In the event we're still here, let's minimize confusion. + hlt + + +;;; +;;; This does the Linux write() system call, passing it an address from the +;;; top of the stack and a length from the second position on the stack. It +;;; writes to file descriptor 1, which is stdout. +;;; +;;; For our length parameter, we can pop directly from the stack into rdx, +;;; which directly becomes the syscall parameter. For our address parameter, +;;; the syscall wants it in rsi, which we also care about, so we have to do a +;;; little juggling. +;;; +defword sys_write, 0 + dq $ + 8 + pop.qreg rcx ; address from stack + pop.qreg rdx ; length from stack, passed directly + push.qreg rsi ; save rsi + mov.qreg.qimm rax, 1 ; syscall number + mov.qreg.qimm rdi, 1 ; file descriptor + mov.qreg.qreg rsi, rcx ; pass address + syscall + pop.qreg rsi ; restore rsi + next + + +;;;;;;;;;;;;;;;;;;;;;;;; +;;; I/O conveniences ;;; +;;;;;;;;;;;;;;;;;;;;;;;; + + +defword emitstring, 0 + dq docol, dup, stringlen, swap, sys_write, exit + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Development utilities ;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; This peforms the "hlt" instruction (Intel's mnemomic, short for "halt"), +; which will cause the program to exit with a segmentation fault. If you're +; running under a debugger, this is a convenient way to get execution to stop +; at a certain point. +; +; It's called "crash" rather than "hlt" to distinguish it from the word +; which outputs the instruction as machine code. +defword crash, 0 + dq $ + 8 + hlt + + ;;;;;;;;;;;;;;;;;;;;;; ;;; Output helpers ;;; ;;;;;;;;;;;;;;;;;;;;;; @@ -2770,7 +3163,7 @@ defword packstring, 0 ; destination, source, length, length, base/destination dq add, lit, 4, unroll ; new base, destination, source, length - dq ccopy + dq memcopy dq exit ; Stack in: @@ -2859,53 +3252,85 @@ defword r14, 0 defword r15, 0 dq docol, lit, r15, exit defword eax, 0 - dq docol, lit, rax, exit + dq docol, lit, eax, exit defword ecx, 0 - dq docol, lit, rcx, exit + dq docol, lit, ecx, exit defword edx, 0 - dq docol, lit, rdx, exit + dq docol, lit, edx, exit defword ebx, 0 - dq docol, lit, rbx, exit + dq docol, lit, ebx, exit defword esp, 0 - dq docol, lit, rsp, exit + dq docol, lit, esp, exit defword ebp, 0 - dq docol, lit, rbp, exit + dq docol, lit, ebp, exit defword esi, 0 - dq docol, lit, rsi, exit + dq docol, lit, esi, exit defword edi, 0 - dq docol, lit, rdi, exit + dq docol, lit, edi, exit defword ax, 0 - dq docol, lit, rax, exit + dq docol, lit, ax, exit defword cx, 0 - dq docol, lit, rcx, exit + dq docol, lit, cx, exit defword dx, 0 - dq docol, lit, rdx, exit + dq docol, lit, dx, exit defword bx, 0 - dq docol, lit, rbx, exit + dq docol, lit, bx, exit defword sp, 0 - dq docol, lit, rsp, exit + dq docol, lit, sp, exit defword bp, 0 - dq docol, lit, rbp, exit + dq docol, lit, bp, exit defword si, 0 - dq docol, lit, rsi, exit + dq docol, lit, si, exit defword di, 0 - dq docol, lit, rdi, exit + dq docol, lit, di, exit defword al, 0 - dq docol, lit, rax, exit + dq docol, lit, al, exit defword cl, 0 - dq docol, lit, rcx, exit + dq docol, lit, cl, exit defword dl, 0 - dq docol, lit, rdx, exit + dq docol, lit, dl, exit defword bl, 0 - dq docol, lit, rbx, exit + dq docol, lit, bl, exit defword ah, 0 - dq docol, lit, rsp, exit + dq docol, lit, ah, exit defword ch, 0 - dq docol, lit, rbp, exit + dq docol, lit, ch, exit defword dh, 0 - dq docol, lit, rsi, exit + dq docol, lit, dh, exit defword bh, 0 - dq docol, lit, rdi, exit + dq docol, lit, bh, exit +defword cc_overflow, 0 + dq docol, lit, cc_overflow, exit +defword cc_no_overflow, 0 + dq docol, lit, cc_no_overflow, exit +defword cc_below, 0 + dq docol, lit, cc_below, exit +defword cc_above_equal, 0 + dq docol, lit, cc_above_equal, exit +defword cc_equal, 0 + dq docol, lit, cc_equal, exit +defword cc_not_equal, 0 + dq docol, lit, cc_not_equal, exit +defword cc_below_equal, 0 + dq docol, lit, cc_below_equal, exit +defword cc_above, 0 + dq docol, lit, cc_above, exit +defword cc_sign, 0 + dq docol, lit, cc_sign, exit +defword cc_not_sign, 0 + dq docol, lit, cc_not_sign, exit +defword cc_even, 0 + dq docol, lit, cc_even, exit +defword cc_odd, 0 + dq docol, lit, cc_odd, exit +defword cc_less, 0 + dq docol, lit, cc_less, exit +defword cc_greater_equal, 0 + dq docol, lit, cc_greater_equal, exit +defword cc_less_equal, 0 + dq docol, lit, cc_less_equal, exit +defword cc_greater, 0 + dq docol, lit, cc_greater, exit ; Stack in: ; register name @@ -3004,6 +3429,35 @@ defword scalefield, 0 dq litstring, "Parameter to scalefield is not 1, 2, 4, or 8.", emitstring dq lit, 1, sys_exit +; [Intel] volume 2D, appendix B, section B-1.4.7, table B-10. Also see the +; individual opcode pages. +; +; Stack in: +; condition name +; Stack out: +; 4-bit encoded value for condition code in opcodes +defword conditioncode, 0 + dq docol + dq dup, cc_overflow, eq, zbranch, 5*8, drop, lit, 0, exit + dq dup, cc_no_overflow, eq, zbranch, 5*8, drop, lit, 1, exit + dq dup, cc_below, eq, zbranch, 5*8, drop, lit, 2, exit + dq dup, cc_above_equal, eq, zbranch, 5*8, drop, lit, 3, exit + dq dup, cc_equal, eq, zbranch, 5*8, drop, lit, 4, exit + dq dup, cc_not_equal, eq, zbranch, 5*8, drop, lit, 5, exit + dq dup, cc_below_equal, eq, zbranch, 5*8, drop, lit, 6, exit + dq dup, cc_above, eq, zbranch, 5*8, drop, lit, 7, exit + dq dup, cc_sign, eq, zbranch, 5*8, drop, lit, 8, exit + dq dup, cc_not_sign, eq, zbranch, 5*8, drop, lit, 9, exit + dq dup, cc_even, eq, zbranch, 5*8, drop, lit, 10, exit + dq dup, cc_odd, eq, zbranch, 5*8, drop, lit, 11, exit + dq dup, cc_less, eq, zbranch, 5*8, drop, lit, 12, exit + dq dup, cc_greater_equal, eq, zbranch, 5*8, drop, lit, 13, exit + dq dup, cc_less_equal, eq, zbranch, 5*8, drop, lit, 14, exit + dq dup, cc_greater, eq, zbranch, 5*8, drop, lit, 15, exit + dq litstring, "Parameter to conditioncode is not a condition code." + dq emitstring + dq lit, 1, sys_exit + ; Stack: ; output point defword rex_w, 0 @@ -3018,6 +3472,13 @@ defword rex_wb, 0 defword opcodereg, 0 dq docol, or, pack8, exit +; Stack: +; output point +; 4-bit encoded value for condition code +; opcode byte +defword opcodecc, 0 + dq docol, or, pack8, exit + ; The low-level word that outputs a modrm byte given fully-processed, ; numeric values for its fields. Most code will want to call one of the ; higher-level modrm_* words, instead. @@ -3397,11 +3858,43 @@ defword push_reg64, 0 defword pop_reg64, 0 dq docol, reg64, lit, 0x58, opcodereg, exit +; TODO: Contemplate renaming this to lods64. +; ; Stack: ; output point defword lodsq, 0 dq docol, rex_w, lit, 0xAD, pack8, exit +; We break with the Intel mnemonics, which are movsb/movsw/movsd/movsq, +; because this would otherwise be the only place we use the b/w/d/q thing +; instead of 8/16/32/64. Tradition and pronounceability are both nice things, +; but approachability to newcomers is important, too. +; +; Stack: +; output point +defword rep_movs8, 0 + dq docol, lit, 0xF3, pack8, lit, 0xA4, pack8, exit + +; Stack: +; output point +defword rep_movs16, 0 + dq docol, lit, 0xF3, pack8, lit, 0x66, pack8, lit, 0xA5, pack8, exit + +; Stack: +; output point +defword rep_movs32, 0 + dq docol, lit, 0xF3, pack8, lit, 0xA5, pack8, exit + +; Stack: +; output point +defword rep_movs64, 0 + dq docol, lit, 0xF3, pack8, rex_w, lit, 0xA5, pack8, exit + +; Stack: +; output point +defword repnz_scas8, 0 + dq docol, lit, 0xF2, pack8, lit, 0xAE, pack8, exit + ; Stack: ; output point ; source register name @@ -3594,6 +4087,38 @@ defword test_reg64_reg64, 0 ; Stack: ; output point +; target register name +; condition code name +defword set_reg8_cc, 0 + dq docol + dq unroll3, lit, 0x0F, pack8 + dq swap, conditioncode, lit, 0x90, opcodecc + dq swap, reg8, lit, 3, lit, 0, roll3, modrm + dq exit + +; Stack: +; output point +; address offset value +; condition code name +defword jmp_cc_rel_imm8, 0 + dq docol + dq unroll3, swap, conditioncode, lit, 0x70, opcodecc + dq swap, pack8 + dq exit + +; Stack: +; output point +; address offset value +; condition code name +defword jmp_cc_rel_imm32, 0 + dq docol + dq unroll3, lit, 0x0F, pack8 + dq swap, conditioncode, lit, 0x70, opcodecc + dq swap, pack32 + dq exit + +; Stack: +; output point ; register name defword jmp_abs_indirect_reg64, 0 dq docol @@ -3603,15 +4128,23 @@ defword jmp_abs_indirect_reg64, 0 ; Stack: ; output point +; address offset value +defword jmp_rel_imm32, 0 + dq docol + dq swap, lit, 0xE9, pack8 + dq swap, pack32 + dq exit + +; Stack: +; output point defword syscall, 0 dq docol, lit, 0x0F, pack8, lit, 0x05, pack8, exit -; TODO rep movsb -; TODO rep movsq -; TODO repnz scasb -; TODO set_reg8_cc -; (and condition codes) -; TODO jmp_cc_rel_imm8 +; Stack: +; output point +defword hlt, 0 + dq docol, lit, 0xF4, pack8, exit + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Runtime word definition ;;; @@ -3747,6 +4280,17 @@ defword pack_next, 0 dq docol, lodsq, rax, jmp_abs_indirect_reg64, exit ; This is another helper "macro" that we'll use in defining assembly words +; from Forth. As before, see the flatassembler version for more explanation. +; +; Stack in: +; base address +; destination address (absolute) +; Stack out: +; new base address +defword pack_beforenext, 0 + dq docol, rax, swap, mov_reg64_imm64, rax, jmp_abs_indirect_reg64, exit + +; This is another helper "macro" that we'll use in defining assembly words ; from Forth. In particular, this one is used in docol. As before, see the ; flatassembler version for more explanation. ; @@ -3837,66 +4381,6 @@ defword quit, 0 dq lit, 0, sys_exit -;;;;;;;;;;;;;;;;;;;; -;;; System calls ;;; -;;;;;;;;;;;;;;;;;;;; -;;; -;;; The kernel preserves every register except rax, rcx, and r11. The system -;;; call number goes in rax, as does the return value. Parameters go in rdi, -;;; rsi, rdx, r10, r8, and r9, in that order. [SysV] A.2.1. -;;; -;;; Notice that rsi is our control stack, so we have to save it (for -;;; syscalls with at least two parameters). We can use the value stack to do -;;; that, since rsp is preserved. We don't save other registers because our -;;; caller should do that, if it cares. -;;; - -;;; -;;; This does the Linux exit() system call, passing it an exit code taken -;;; from the stack. -;;; -defword sys_exit, 0 - dq $ + 8 - - mov.qreg.qimm rax, 60 ; syscall number - pop.qreg rdi ; exit code - syscall - - ; In the event we're still here, let's minimize confusion. - hlt - - -;;; -;;; This does the Linux write() system call, passing it an address from the -;;; top of the stack and a length from the second position on the stack. It -;;; writes to file descriptor 1, which is stdout. -;;; -;;; For our length parameter, we can pop directly from the stack into rdx, -;;; which directly becomes the syscall parameter. For our address parameter, -;;; the syscall wants it in rsi, which we also care about, so we have to do a -;;; little juggling. -;;; -defword sys_write, 0 - dq $ + 8 - pop.qreg rcx ; address from stack - pop.qreg rdx ; length from stack, passed directly - push.qreg rsi ; save rsi - mov.qreg.qimm rax, 1 ; syscall number - mov.qreg.qimm rdi, 1 ; file descriptor - mov.qreg.qreg rsi, rcx ; pass address - syscall - pop.qreg rsi ; restore rsi - next - - -;;;;;;;;;;;;;;;;;;;;;;;; -;;; I/O conveniences ;;; -;;;;;;;;;;;;;;;;;;;;;;;; - - -defword emitstring, 0 - dq docol, dup, stringlen, swap, sys_write, exit - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; (new) Implementation strategy ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -4033,11 +4517,6 @@ defword set_label, 0 dq exit -defword hlt, 0 - dq $ + 8 - hlt - - ; This takes an address to write to on the stack, writes stuff there, and ; returns the next address after where it stopped Writing. ; @@ -4141,7 +4620,7 @@ defword self_raw, 0 ; destination destination source length dq dup, lit, 4, roll, add, lit, 4, unroll ; result destination source length - dq ccopy + dq memcopy dq exit |