summary refs log tree commit diff
path: root/quine.asm
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-04-13 16:47:24 -0700
committerIrene Knapp <ireneista@irenes.space>2026-04-13 16:58:03 -0700
commitc306bb3a7b3ef4b7e2392fc9dac7480d8f85a4ca (patch)
tree029ad03c25c8519bb766f9f4f795dda0f455eb53 /quine.asm
parentacc9c36a19e34fec1a9b92801319e434e3b61ed2 (diff)
disallow a target of rbp for indirect register moves via flatassembler
this matches the behavior the Forth implementation has, and also the behavior of the other flatassembler moves. those versions were written later; it became obvious over the course of development that transparently remapping one variant of an instruction into another was a bad idea and would cause confusion. this change is happening now because, indeed, it caused confusion.

the offending variant was used exactly once, in the pushcontrol macro. it has been changed to the disp8 version, which is already what was being output. we've carefully verified that this produces no unexpected binary changes either to the ELF or to the heap.

it's kinda cool knowing for sure that the debugging process actually catches this sort of weirdness.

Force-Push: yes
Change-Id: I355fc0070bb95beeb94c7d14d46fa2cc50f3a30d
Diffstat (limited to 'quine.asm')
-rw-r--r--quine.asm19
1 files changed, 11 insertions, 8 deletions
diff --git a/quine.asm b/quine.asm
index 3136a11..157e87b 100644
--- a/quine.asm
+++ b/quine.asm
@@ -471,10 +471,9 @@ end macro
 ; Take a 64-bit source register, store its value into the address pointed to
 ; by a 64-bit target register.
 ;
-; For rbp, the only modes available also have displacement; we use an 8-bit
-; one and set it to zero. The other registers could be encoded without the
-; displacement, but for simplicity's sake we do the same thing for all of
-; them.
+; For a target of rbp, the only modes available also have displacement; we
+; disallow that. Use mov.disp8.qreg.qreg for that case, and set a displacement
+; of 0.
 ;
 ; In understanding this, pay close attention to the Op/En column in the opcode
 ; table. The "MR" variant means the ModRM byte's reg field (the middle one)
@@ -495,17 +494,19 @@ end macro
 ; is for whichever register is specified in the R/M field. Sometimes that's
 ; the source, and sometimes it's the target, depending on the opcode.
 macro mov.indirect.qreg.qreg target, source
+  match =rbp, target
+    assert 0
+  end match
   qwordreg sreg, source
   qwordreg treg, target
   rex.w
   db 0x89
-  modrm 1, sreg, treg
+  modrm 0, sreg, treg
   match =rsp, target
     ; R/M = rsp is the SIB case
     sib 0, 4, treg
       ; no scaling, no indexing, target as base
   end match
-  db 0
 end macro
 
 
@@ -1038,7 +1039,7 @@ end macro
 ;
 ; We need to treat a target of rsp specially because it's the SIB case per
 ; table 2-2.
-macro mov.disp8.qreg.qreg target, offset, source
+macro mov.disp8.qreg.qreg offset, target, source
   qwordreg sreg, source
   qwordreg treg, target
   rex.w
@@ -1723,7 +1724,7 @@ end macro
 ;;;
 macro pushcontrol source
   lea.qreg.disp8.qreg rbp, -8, rbp
-  mov.indirect.qreg.qreg rbp, source
+  mov.disp8.qreg.qreg 0, rbp, source
 end macro
 
 macro popcontrol target
@@ -7229,7 +7230,9 @@ defword addressing_indirect_reg64, 0
   dq dup, rbp, ne, zbranch, 23*8
   ; Check whether the R/M register is rsp; save the test result for later.
   dq dup, rsp, eq, lit, 4, unroll
+  ; (equality result, output point, reg/op value, reg/mem name)
   dq reg64, lit, 0, unroll3, modrm
+  ; (equality result, output point)
   ; If the R/M register was rsp, we need an SIB byte; otherwise, skip it.
   dq swap, zbranch, 8*8, lit, 0, lit, 4, rsp, reg64, sib
   dq exit