From 088bc863865717433056dd4d959a014c2db7ca8f Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Wed, 29 Oct 2025 23:37:50 -0700 Subject: get it down to a single call to write() this also adds a block-copy routine, further breaking up the handful of remaining monolithic things Force-Push: yes Change-Id: Ida960d83bab6176d016168cdf25e0763aa6050ef --- quine.asm | 79 +++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 20 deletions(-) (limited to 'quine.asm') diff --git a/quine.asm b/quine.asm index 2d9f6eb..35c5920 100644 --- a/quine.asm +++ b/quine.asm @@ -1178,7 +1178,17 @@ end macro ; where does this get its parameters from?" Writing them in a way that makes ; them appear simpler than they are would be confusing. macro rep operation - match =movsq, operation + match =movsb, operation + db 0xF3 ; rep prefix + db 0xA4 ; opcode + else match =movsw, operation + db 0xF3 ; rep prefix + db 0x66 ; operand-size prefix + db 0xA5 ; opcode + else match =movsd, operation + db 0xF3 ; rep prefix + db 0xA5 ; opcode + else match =movsq, operation ; The "rep" instruction can also be thought of as a prefix to other ; instructions, though only a few specific ones are allowed. Anyway, it ; comes before the REX byte. @@ -2394,6 +2404,33 @@ defword FETCH32, 0 mov.dreg.indirect.qreg eax, rbx NEXT +; Copy one non-overlapping block of memory over another. For the overlapping +; 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, +; 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. +; +; Stack in: +; destination +; source +; length +; (top) +defword CCOPY, 0 + dq $ + 0x8 ; codeword + ; We need to save and restore rsi; the other registers we can trample. + mov.qreg.qreg rdx, rsi + pop.qreg rcx + pop.qreg rsi + pop.qreg rdi + ; We start from the low end, since that's easier arithmetic. So, we get to + ; leave the DF flag alone. + rep movsb + mov.qreg.qreg rsi, rdx + NEXT + ;;;;;;;;;;;;;;;;; ;;; Branching ;;; ;;;;;;;;;;;;;;;;; @@ -2582,13 +2619,12 @@ defword QUINE, 0 ; This takes an address to write to on the stack and adds an ELF file header ; to it, leaving the adjusted address with the size of the header added. ; Then it does the same thing with an ELF program header. - dq ELF_FILE_HEADER, ELF_PROGRAM_HEADER + dq ELF_FILE_HEADER, ELF_PROGRAM_HEADER, SELF_RAW ; The two-pass magick. - dq LIT, file_size - 0x78, ADD dq SET_LABEL dq DROP, LIT, 4, ROLL, DUP, LIT, 5, UNROLL - dq ELF_FILE_HEADER, ELF_PROGRAM_HEADER + dq ELF_FILE_HEADER, ELF_PROGRAM_HEADER, SELF_RAW ; Drop the copy of the buffer's address. dq DROP @@ -2601,16 +2637,12 @@ defword QUINE, 0 ; hardcoded), and writes that offset into an appopriate place in the middle ; of those headers. It then returns the length of the used portion of the ; buffer. - dq LIT, 0x78, SWAP + ;dq LIT, 0x78, SWAP + dq LIT, file_size, SWAP ; write() from stack-allocated buffer dq SYS_WRITE - ; write() the machine code by using self-reference - ; TODO do this in a "real" quine way - dq WRITE_SELF_RAW_H - dq SYS_WRITE - dq EXIT ; Stack in: @@ -2632,10 +2664,10 @@ defword USE_LABEL, 0 ; Fetch the status dq SWAP ; Check the bit that indicates it's been set. - dq DUP, LIT, 2, AND, ZBRANCH, 12*8 + dq DUP, LIT, 2, AND, ZBRANCH, 10*8 ; If we're here, it has been set already, so just put the status back... - dq LIT, 2, UNROLL + dq SWAP ; Fetch the actual value... dq LIT, 4, ROLL, DUP, LIT, 5, UNROLL ; ... and exit @@ -2686,14 +2718,6 @@ defword HLT, 0 dq $ + 0x8 ; codeword hlt -defword WRITE_SELF_RAW_H, 0 - dq $ + 0x8 ; codeword - mov.qreg.qimm rax, file_size - 0x78 - push.qreg rax - mov.qreg.qimm rax, elf_header + 0x78 - push.qreg rax - NEXT - ;;; ;;; ELF header @@ -2760,6 +2784,21 @@ defword ELF_PROGRAM_HEADER, 0 dq EXIT + +; write() the machine code by using self-reference +; TODO do this in a "real" quine way +defword SELF_RAW, 0 + dq DOCOL ; codeword + dq DUP + dq LIT, elf_header + 0x78 ; source + dq LIT, file_size - 0x78 ; length + ; destination destination source length + dq DUP, LIT, 4, ROLL, ADD, LIT, 4, UNROLL + ; result destination source length + dq CCOPY + dq EXIT + + code_size = $ - code_start file_size = $ - $$ -- cgit 1.4.1