summary refs log tree commit diff
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2025-10-29 23:37:50 -0700
committerIrene Knapp <ireneista@irenes.space>2025-10-29 23:37:50 -0700
commit088bc863865717433056dd4d959a014c2db7ca8f (patch)
tree984b74def9f1cf44e2e83be4252347f6b9e29505
parentdf5928294f9154f317d2a2352bae2c1bc46848d9 (diff)
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
-rw-r--r--quine.asm79
1 files changed, 59 insertions, 20 deletions
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 = $ - $$