<?xml-stylesheet type="text/xsl" encoding="UTF-8" href="iform.xsl" version="1.0"?>
<!DOCTYPE instructionsection PUBLIC "-//ARM//DTD instructionsection //EN" "iform-p.dtd">
<!-- Copyright (c) 2010-2025 Arm Limited or its affiliates. All rights reserved. -->
<!-- This document is Non-Confidential. This document may only be used and distributed in accordance with the terms of the agreement entered into by Arm and the party that Arm delivered this document to. -->
<instructionsection id="CPYFPN" title="CPYFPN, CPYFMN, CPYFEN -- A64" type="instruction">
  <docvars>
    <docvar key="instr-class" value="general"/>
    <docvar key="isa" value="A64"/>
  </docvars>
  <heading>CPYFPN, CPYFMN, CPYFEN</heading>
  <desc>
    <brief>
      <para>Memory copy forward-only, reads and writes non-temporal</para>
    </brief>
    <authored>
      <para>These instructions copy a requested number of bytes in memory from a source address to a
destination address in a forward direction. The prologue, main, and epilogue instructions
are expected to be run in succession and to appear consecutively in memory:
CPYFPN, then CPYFMN, and then CPYFEN.</para>
      <para>CPYFPN performs some preconditioning of the arguments suitable for using the
CPYFMN instruction, and copies an <arm-defined-word>IMPLEMENTATION DEFINED</arm-defined-word> portion of the requested
number of bytes. CPYFMN copies a further <arm-defined-word>IMPLEMENTATION DEFINED</arm-defined-word> portion of the
remaining bytes. CPYFEN copies any final remaining bytes.</para>
      <note>
        <para>The ability to copy an <arm-defined-word>IMPLEMENTATION DEFINED</arm-defined-word> number of bytes allows an implementation to
optimize how the bytes being copied are divided between the different instructions.</para>
      </note>
      <para>For more information on exceptions specific to memory copy instructions,
see <xref linkend="ARMARM_MDSec.memcpy_and_memset_exceptions">Memory Copy and Memory Set exceptions</xref>.</para>
      <para>The memory copy performed by these instructions is in the forward direction only, so the
instructions are suitable for a memory copy only where there is no overlap between the
source and destination locations, or where the source address is greater than
or equal to the destination address.</para>
      <para>The architecture supports two algorithms for the memory copy: option A and option B.
Which algorithm is used is <arm-defined-word>IMPLEMENTATION DEFINED</arm-defined-word>.</para>
      <note>
        <para>Portable software should not assume that the choice of algorithm is constant.</para>
      </note>
      <para>For CPYFPN:</para>
      <list type="unordered">
        <listitem>
          <content>If Xn[63] == '1', the copy size is saturated to <hexnumber>0x7FFFFFFFFFFFFFFF</hexnumber>.</content>
        </listitem>
      </list>
      <para>On completion of CPYFPN, option A:</para>
      <list type="unordered">
        <listitem>
          <content>Xn holds -1 times the number of bytes in the saturated copy size remaining to be copied.</content>
        </listitem>
        <listitem>
          <content>Xs holds the original Xs + saturated copy size.</content>
        </listitem>
        <listitem>
          <content>Xd holds the original Xd + saturated copy size.</content>
        </listitem>
        <listitem>
          <content>PSTATE.{N,Z,C,V} are set to {0,0,0,0}.</content>
        </listitem>
      </list>
      <para>On completion of CPYFPN, option B:</para>
      <list type="unordered">
        <listitem>
          <content>Xn holds the number of bytes in the saturated copy size remaining to be copied.</content>
        </listitem>
        <listitem>
          <content>Xs holds the lowest address that has not been copied from.</content>
        </listitem>
        <listitem>
          <content>Xd holds the lowest address that has not been copied to.</content>
        </listitem>
        <listitem>
          <content>PSTATE.{N,Z,C,V} are set to {0,0,1,0}.</content>
        </listitem>
      </list>
      <para>For CPYFMN, option A, when PSTATE.C = '0':</para>
      <list type="unordered">
        <listitem>
          <content>Xn holds a signed 64-bit integer.</content>
        </listitem>
        <listitem>
          <content>Xn holds -1 times the number of bytes remaining to be copied.</content>
        </listitem>
        <listitem>
          <content>Xs holds the lowest address to be copied from - Xn.</content>
        </listitem>
        <listitem>
          <content>Xd holds the lowest address to be copied to - Xn.</content>
        </listitem>
        <listitem>
          <content>On completion of the instruction, Xn holds -1 times the number of bytes remaining to
  be copied.</content>
        </listitem>
      </list>
      <para>For CPYFMN, option B, when PSTATE.C = '1':</para>
      <list type="unordered">
        <listitem>
          <content>Xn holds the number of bytes remaining to be copied.</content>
        </listitem>
        <listitem>
          <content>Xs holds the lowest address to be copied from.</content>
        </listitem>
        <listitem>
          <content>Xd holds the lowest address to be copied to.</content>
        </listitem>
        <listitem>
          <content>On completion of the instruction:<list type="unordered">
              <listitem>
                <content>Xn holds the number of bytes remaining to be copied.</content>
              </listitem>
              <listitem>
                <content>Xs holds the lowest address that has not been copied from.</content>
              </listitem>
              <listitem>
                <content>Xd holds the lowest address that has not been copied to.</content>
              </listitem>
            </list>
          </content>
        </listitem>
      </list>
      <para>For CPYFEN, option A, when PSTATE.C = '0':</para>
      <list type="unordered">
        <listitem>
          <content>Xn holds a signed 64-bit integer.</content>
        </listitem>
        <listitem>
          <content>Xn holds -1 times the number of bytes remaining to be copied.</content>
        </listitem>
        <listitem>
          <content>Xs holds the lowest address to be copied from - Xn.</content>
        </listitem>
        <listitem>
          <content>Xd holds the lowest address to be copied to - Xn.</content>
        </listitem>
        <listitem>
          <content>On completion of the instruction, Xn holds 0.</content>
        </listitem>
      </list>
      <para>For CPYFEN, option B, when PSTATE.C = '1':</para>
      <list type="unordered">
        <listitem>
          <content>Xn holds the number of bytes remaining to be copied.</content>
        </listitem>
        <listitem>
          <content>Xs holds the lowest address to be copied from.</content>
        </listitem>
        <listitem>
          <content>Xd holds the lowest address to be copied to.</content>
        </listitem>
        <listitem>
          <content>On completion of the instruction:<list type="unordered">
              <listitem>
                <content>Xn holds 0.</content>
              </listitem>
              <listitem>
                <content>Xs holds the lowest address that has not been copied from.</content>
              </listitem>
              <listitem>
                <content>Xd holds the lowest address that has not been copied to.</content>
              </listitem>
            </list>
          </content>
        </listitem>
      </list>
    </authored>
    <encodingnotes>
      <para>For information about the <arm-defined-word>CONSTRAINED UNPREDICTABLE</arm-defined-word> behavior of this instruction, see <xref linkend="CJAEGDJC">Architectural Constraints on UNPREDICTABLE behaviors</xref>, and particularly <xref linkend="CEGCIDDEI6">Memory Copy and Memory Set CPY*</xref> and <xref linkend="ARMARM_CEGCCGAE">Crossing a page boundary with different memory types or Shareability attributes</xref>.</para>
    </encodingnotes>
  </desc>
  <alias_list howmany="0"/>
  <classes>
    <iclass name="Integer" oneof="1" id="iclass_integer" no_encodings="3" isa="A64">
      <docvars>
        <docvar key="instr-class" value="general"/>
        <docvar key="isa" value="A64"/>
      </docvars>
      <iclassintro count="3"/>
      <arch_variants>
        <arch_variant feature="FEAT_MOPS" name="v8Ap8"/>
      </arch_variants>
      <regdiagram form="32" psname="A64.ldst.memcms.CPYFPN_CPY_memcms" tworows="1">
        <box hibit="31" width="2" name="sz" usename="1">
          <c colspan="2"/>
        </box>
        <box hibit="29" width="3" settings="3">
          <c>0</c>
          <c>1</c>
          <c>1</c>
        </box>
        <box hibit="26" name="o0" usename="1" settings="1" psbits="x">
          <c>0</c>
        </box>
        <box hibit="25" width="2" settings="2">
          <c>0</c>
          <c>1</c>
        </box>
        <box hibit="23" width="2" name="op1" usename="1">
          <c colspan="2"/>
        </box>
        <box hibit="21" width="1" settings="1">
          <c>0</c>
        </box>
        <box hibit="20" width="5" name="Rs" usename="1">
          <c colspan="5"/>
        </box>
        <box hibit="15" width="4" name="op2" usename="1" settings="4" psbits="xxxx">
          <c>1</c>
          <c>1</c>
          <c>0</c>
          <c>0</c>
        </box>
        <box hibit="11" width="2" settings="2">
          <c>0</c>
          <c>1</c>
        </box>
        <box hibit="9" width="5" name="Rn" usename="1">
          <c colspan="5"/>
        </box>
        <box hibit="4" width="5" name="Rd" usename="1">
          <c colspan="5"/>
        </box>
      </regdiagram>
      <encoding name="CPYFPN_CPY_memcms" oneofinclass="3" oneof="3" label="Prologue" bitdiffs="op1 == 00">
        <docvars>
          <docvar key="instr-class" value="general"/>
          <docvar key="isa" value="A64"/>
          <docvar key="pme" value="pme-prologue"/>
          <docvar key="mnemonic" value="CPYFPN"/>
        </docvars>
        <box hibit="23" width="2" name="op1">
          <c>0</c>
          <c>0</c>
        </box>
        <asmtemplate><text>CPYFPN  [</text><a hover="For the &quot;Prologue&quot; variant: is the 64-bit name of the general-purpose register that holds the destination address and is updated by the instruction, encoded in the &quot;Rd&quot; field." link="XdOrXZR">&lt;Xd&gt;</a><text>]!, [</text><a hover="For the &quot;Prologue&quot; variant: is the 64-bit name of the general-purpose register that holds the source address and is updated by the instruction, encoded in the &quot;Rs&quot; field." link="XsOrXZR__5">&lt;Xs&gt;</a><text>]!, </text><a hover="For the &quot;Prologue&quot; variant: is the 64-bit name of the general-purpose register that holds the number of bytes to be transferred and is updated by the instruction to encode the remaining size and destination, encoded in the &quot;Rn&quot; field." link="XnOrXZR__2">&lt;Xn&gt;</a><text>!</text></asmtemplate>
      </encoding>
      <encoding name="CPYFMN_CPY_memcms" oneofinclass="3" oneof="3" label="Main" bitdiffs="op1 == 01">
        <docvars>
          <docvar key="instr-class" value="general"/>
          <docvar key="isa" value="A64"/>
          <docvar key="pme" value="pme-main"/>
          <docvar key="mnemonic" value="CPYFMN"/>
        </docvars>
        <box hibit="23" width="2" name="op1">
          <c>0</c>
          <c>1</c>
        </box>
        <asmtemplate><text>CPYFMN  [</text><a hover="For the &quot;Epilogue&quot; and &quot;Main&quot; variants: is the 64-bit name of the general-purpose register that holds an encoding of the destination address, encoded in the &quot;Rd&quot; field." link="XdOrXZR__2">&lt;Xd&gt;</a><text>]!, [</text><a hover="For the &quot;Epilogue&quot; and &quot;Main&quot; variants: is the 64-bit name of the general-purpose register that holds an encoding of the source address, encoded in the &quot;Rs&quot; field." link="XsOrXZR__6">&lt;Xs&gt;</a><text>]!, </text><a hover="For the &quot;Main&quot; variant: is the 64-bit name of the general-purpose register that holds an encoding of the number of bytes to be transferred, encoded in the &quot;Rn&quot; field." link="XnOrXZR__3">&lt;Xn&gt;</a><text>!</text></asmtemplate>
      </encoding>
      <encoding name="CPYFEN_CPY_memcms" oneofinclass="3" oneof="3" label="Epilogue" bitdiffs="op1 == 10">
        <docvars>
          <docvar key="instr-class" value="general"/>
          <docvar key="isa" value="A64"/>
          <docvar key="pme" value="pme-epilogue"/>
          <docvar key="mnemonic" value="CPYFEN"/>
        </docvars>
        <box hibit="23" width="2" name="op1">
          <c>1</c>
          <c>0</c>
        </box>
        <asmtemplate><text>CPYFEN  [</text><a hover="For the &quot;Epilogue&quot; and &quot;Main&quot; variants: is the 64-bit name of the general-purpose register that holds an encoding of the destination address, encoded in the &quot;Rd&quot; field." link="XdOrXZR__2">&lt;Xd&gt;</a><text>]!, [</text><a hover="For the &quot;Epilogue&quot; and &quot;Main&quot; variants: is the 64-bit name of the general-purpose register that holds an encoding of the source address, encoded in the &quot;Rs&quot; field." link="XsOrXZR__6">&lt;Xs&gt;</a><text>]!, </text><a hover="For the &quot;Epilogue&quot; variant: is the 64-bit name of the general-purpose register that holds an encoding of the number of bytes to be transferred and is set to zero on completion of the instruction, encoded in the &quot;Rn&quot; field." link="XnOrXZR__4">&lt;Xn&gt;</a><text>!</text></asmtemplate>
      </encoding>
      <ps_section howmany="1">
        <ps name="A64.ldst.memcms.CPYFPN_CPY_memcms" sections="1" secttype="noheading">
          <pstext mayhavelinks="1" section="Decode" rep_section="decode">if !IsFeatureImplemented(FEAT_MOPS) || sz != '00' then EndOfDecode(Decode_UNDEF); end;

var memcpy : CPYParams;
memcpy.d = UInt(Rd);
memcpy.s = UInt(Rs);
memcpy.n = UInt(Rn);
let options : bits(4) = op2;
let rnontemporal : boolean = options[3] == '1';
let wnontemporal : boolean = options[2] == '1';
case op1 of
    when '00' =&gt; memcpy.stage = MOPSStage_Prologue;
    when '01' =&gt; memcpy.stage = MOPSStage_Main;
    when '10' =&gt; memcpy.stage = MOPSStage_Epilogue;
end;</pstext></ps>
      </ps_section>
    </iclass>
  </classes>
  <explanations scope="all">
    <explanation enclist="CPYFPN_CPY_memcms" symboldefcount="1">
      <symbol link="XdOrXZR">&lt;Xd&gt;</symbol>
      <account encodedin="Rd">
        <intro>
          <para>For the "Prologue" variant: is the 64-bit name of the general-purpose register that holds the destination address and is updated by the instruction, encoded in the "Rd" field.</para>
        </intro>
      </account>
    </explanation>
    <explanation enclist="CPYFMN_CPY_memcms, CPYFEN_CPY_memcms" symboldefcount="2">
      <symbol link="XdOrXZR__2">&lt;Xd&gt;</symbol>
      <account encodedin="Rd">
        <intro>
          <para>For the "Epilogue" and "Main" variants: is the 64-bit name of the general-purpose register that holds an encoding of the destination address, encoded in the "Rd" field.</para>
        </intro>
      </account>
    </explanation>
    <explanation enclist="CPYFPN_CPY_memcms" symboldefcount="1">
      <symbol link="XsOrXZR__5">&lt;Xs&gt;</symbol>
      <account encodedin="Rs">
        <intro>
          <para>For the "Prologue" variant: is the 64-bit name of the general-purpose register that holds the source address and is updated by the instruction, encoded in the "Rs" field.</para>
        </intro>
      </account>
    </explanation>
    <explanation enclist="CPYFMN_CPY_memcms, CPYFEN_CPY_memcms" symboldefcount="2">
      <symbol link="XsOrXZR__6">&lt;Xs&gt;</symbol>
      <account encodedin="Rs">
        <intro>
          <para>For the "Epilogue" and "Main" variants: is the 64-bit name of the general-purpose register that holds an encoding of the source address, encoded in the "Rs" field.</para>
        </intro>
      </account>
    </explanation>
    <explanation enclist="CPYFPN_CPY_memcms" symboldefcount="1">
      <symbol link="XnOrXZR__2">&lt;Xn&gt;</symbol>
      <account encodedin="Rn">
        <intro>
          <para>For the "Prologue" variant: is the 64-bit name of the general-purpose register that holds the number of bytes to be transferred and is updated by the instruction to encode the remaining size and destination, encoded in the "Rn" field.</para>
        </intro>
      </account>
    </explanation>
    <explanation enclist="CPYFMN_CPY_memcms" symboldefcount="2">
      <symbol link="XnOrXZR__3">&lt;Xn&gt;</symbol>
      <account encodedin="Rn">
        <intro>
          <para>For the "Main" variant: is the 64-bit name of the general-purpose register that holds an encoding of the number of bytes to be transferred, encoded in the "Rn" field.</para>
        </intro>
      </account>
    </explanation>
    <explanation enclist="CPYFEN_CPY_memcms" symboldefcount="3">
      <symbol link="XnOrXZR__4">&lt;Xn&gt;</symbol>
      <account encodedin="Rn">
        <intro>
          <para>For the "Epilogue" variant: is the 64-bit name of the general-purpose register that holds an encoding of the number of bytes to be transferred and is set to zero on completion of the instruction, encoded in the "Rn" field.</para>
        </intro>
      </account>
    </explanation>
  </explanations>
  <ps_section howmany="1">
    <ps name="A64.ldst.memcms.CPYFPN_CPY_memcms" sections="1" secttype="Operation">
      <pstext mayhavelinks="1" section="Execute" rep_section="execute">CheckMOPSEnabled();

CheckCPYConstrainedUnpredictable(memcpy.n, memcpy.d, memcpy.s);

memcpy.nzcv        = PSTATE.[N,Z,C,V];
memcpy.toaddress   = X{64}(memcpy.d);
memcpy.fromaddress = X{64}(memcpy.s);
memcpy.cpysize     = SInt(X{64}(memcpy.n));
memcpy.implements_option_a = CPYFOptionA();

let rprivileged : boolean = (if options[1] == '1' then AArch64_IsUnprivAccessPriv()
                                else PSTATE.EL != EL0);
let wprivileged : boolean = (if options[0] == '1' then AArch64_IsUnprivAccessPriv()
                                else PSTATE.EL != EL0);

let raccdesc : AccessDescriptor = CreateAccDescMOPS(MemOp_LOAD,  rprivileged, rnontemporal);
let waccdesc : AccessDescriptor = CreateAccDescMOPS(MemOp_STORE, wprivileged, wnontemporal);

if memcpy.stage == MOPSStage_Prologue then
    if memcpy.cpysize[63] == '1' then memcpy.cpysize = ArchMaxMOPSBlockSize; end;

    if memcpy.implements_option_a then
        memcpy.nzcv = '0000';
        // Copy in the forward direction offsets the arguments.
        memcpy.toaddress   = memcpy.toaddress   + memcpy.cpysize;
        memcpy.fromaddress = memcpy.fromaddress + memcpy.cpysize;
        memcpy.cpysize     = 0 - memcpy.cpysize;
    else
        memcpy.nzcv = '0010';
    end;
end;

memcpy.stagecpysize = MemCpyStageSize(memcpy);

if memcpy.stage != MOPSStage_Prologue then
    CheckMemCpyParams(memcpy, options);
end;

var copied : integer;
var iswrite : boolean;
var memaddrdesc : AddressDescriptor;
var memstatus : PhysMemRetStatus;
memcpy.forward = TRUE;
var fault : boolean  = FALSE;
var B : <a link="MOPSBlockSize" file="shared_pseudocode.xml" hover="type MOPSBlockSize">MOPSBlockSize</a> = 0;

if memcpy.implements_option_a then
    while memcpy.stagecpysize != 0 &amp;&amp; !fault looplimit ArchMaxMOPSBlockSize do
        // IMP DEF selection of the block size that is worked on. While many
        // implementations might make this constant, that is not assumed.
        B = CPYSizeChoice(memcpy);
        assert B &lt;= -1 * memcpy.stagecpysize;

        (copied, iswrite, memaddrdesc, memstatus) = MemCpyBytes(memcpy.toaddress   + memcpy.cpysize,
                                                                memcpy.fromaddress + memcpy.cpysize,
                                                                memcpy.forward, B,
                                                                raccdesc, waccdesc);
        if copied != B then
            fault = TRUE;
        else
            memcpy.cpysize      = memcpy.cpysize + B;
            memcpy.stagecpysize = memcpy.stagecpysize + B;
        end;
    end;
else
    while memcpy.stagecpysize &gt; 0 &amp;&amp; !fault looplimit ArchMaxMOPSBlockSize do
        // IMP DEF selection of the block size that is worked on. While many
        // implementations might make this constant, that is not assumed.
        B = CPYSizeChoice(memcpy);
        assert B &lt;= memcpy.stagecpysize;

        (copied, iswrite, memaddrdesc, memstatus) = MemCpyBytes(memcpy.toaddress,
                                                                memcpy.fromaddress,
                                                                memcpy.forward, B,
                                                                raccdesc, waccdesc);
        if copied != B then
            fault = TRUE;
        else
            memcpy.fromaddress  = memcpy.fromaddress + B;
            memcpy.toaddress    = memcpy.toaddress   + B;
            memcpy.cpysize      = memcpy.cpysize     - B;
            memcpy.stagecpysize = memcpy.stagecpysize - B;
        end;
    end;
end;

UpdateCpyRegisters(memcpy, fault, copied);

if fault then
    if IsFault(memaddrdesc) then
        AArch64_Abort(memaddrdesc.fault);
    end;

    if IsFault(memstatus) then
        let accdesc : AccessDescriptor = if iswrite then waccdesc else raccdesc;
        HandleExternalAbort(memstatus, iswrite, memaddrdesc, B, accdesc);
    end;
end;

if memcpy.stage == MOPSStage_Prologue then
    PSTATE.[N,Z,C,V] = memcpy.nzcv;
end;</pstext></ps>
  </ps_section>
  <timestamp>2026-03-12 12:23:09</timestamp>
  <commit_id>2025-09_rel_asl1</commit_id>
</instructionsection>
