\section{Host backing memory spaces и loader} Host allocation не является частью PTR64. Активные architectural memory spaces имеют независимый anonymous mmap() backing; guest pointer содержит selector+offset и никогда не содержит host virtual address. \subsection{Anonymous mmap only} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] mmap(NULL, capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) \end{lstlisting} PROGRAM, DATA, STACK, CONTROL, INPUT, OUTPUT, TRACE и HEAP получают отдельные mappings. Loader читает image bytes обычным read() в anonymous memory. PROGRAM после загрузки переводится host-side в read-only mapping; guest access policy дополнительно проверяется instruction resolver. CONTROL/INPUT сохраняются host-writable, потому что runtime должен иметь возможность менять configuration/input между фазами исполнения, но guest STOREP в них запрещён. \subsection{Architectural size, capacity и HEAP limit} \begin{center} \begin{tabularx}{0.92\textwidth}{@{}L{34mm}Y@{}} \toprule \textbf{Величина} & \textbf{Смысл}\\ \midrule \code{segment.size} & точное число байтов, доступных guest-программе\\ \code{segment.capacity} & округлённый по страницам размер host mapping\\ \code{heap.size} & текущий guest-visible размер HEAP\\ \code{heap.capacity} & текущая вместимость host backing HEAP\\ \code{heap.limit} & настроенный неизменяемый максимальный размер HEAP\\ \bottomrule \end{tabularx} \end{center} Все CPU bounds checks используют architectural size, а не host capacity. HEAP additionally reserves offsets 0..15; первый ordinary heap byte имеет offset CPU\_HEAP\_BEGIN = 16. Relocatable host backing. PTR64(HEAP,offset) не зависит от host base. Поэтому будущая реализация heap growth может создать larger anonymous mapping, скопировать bytes и заменить host base, не меняя ни одного guest pointer. \subsection{MELF loader и окончательный process image} Начиная с MELF ABI, development sidecar images не являются форматом запуска. \code{mcpu-ld} создаёт ET_EXEC/ET_DYN с segment metadata, а \code{mcpu-run} валидирует target description, program-model metadata, program headers и relocation records до передачи управления guest. Для ABI v2 все preload-модули сначала полностью читаются и валидируются, затем назначаются независимые PROGRAM/DATA bases, после чего применяются retained relocations. PROGRAM после relocation phase переводится в read-only host mapping. \section{Fault, atomicity и invariants} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] • REG24 проверяет width, natural alignment и границы register file. • JMPP/CALLP требуют PROGRAM PTR64: NULL, другой SEG или offset вне program.size дают POINTER_FAULT до изменения PC/SP/STACK. • CALLP сначала валидирует target и stack space; return PC записывается только после успешной полной проверки. • All-zero PTR64 является NULL и даёт POINTER_FAULT при indirect dereference. • Assembler резервирует DATA[0..15]; ordinary DATA labels начинаются не раньше 0x10. • CODE64 target должен ссылаться внутрь PROGRAM. • STACK operations сначала проверяют bounds, затем меняют SP/FP/memory. • PUSHFP проверяет место и корректность текущего FP до записи. • POPFP сначала читает saved value и проверяет saved_fp <= stack_size; при fault ни FP, ни SP не меняются. • SETFP требует allocated STACK и корректный SP. • RESTSP требует FP <= stack_size. • LDFP/STFP/LEAFP используют signed OFF64 относительно FP и выполняют bounds checking. • SETSP принимает только STACK PTR64; wrong segment -> pointer fault, out-of-range offset -> stack fault. • Ни одна FP/SP management instruction не меняет PSW. • CALL/CALLP/RET сами PSW-neutral; normal-function ABI считает PSW[15:0] volatile, TF/IF/ MODE persistent control state, HF отдельным execution state. • PADD/PADDI/PSUB/PSUBI проверяют 60-bit PTR64 offset, но не object lifetime. • BMOV/BMOVI сначала проверяют complete source и destination ranges; при fault destination memory не изменяется. Overlap разрешён и имеет memmove semantics. • CPU не обеспечивает ownership текущего frame: правило SP <= FP внутри FP-frame и stack-scope discipline обеспечиваются compiler/ABI. \end{lstlisting} Нельзя хранить долгоживущий указатель на automatic object после release scope/frame. PTR64 остаётся битово корректным, но high-level program нарушает lifetime. Это семантическая ошибка compiler/program, а не PTR64 encoding fault. \section{Byte order и architectural object representation} LibMCPU намеренно разделяет два независимых byte-order domain. Это один из фундаментальных ABI contracts: PROGRAM является переносимым сериализованным instruction stream, а register file и memory segments содержат runtime objects в target representation, совместимом с LibMPU. \begin{figure}[ht] \centering \begin{tikzpicture}[font=\sffamily\small,>=Latex,node distance=12mm and 15mm] \node[draw=McpuBlue,very thick,rounded corners,fill=McpuBlueBg,minimum width=72mm,minimum height=22mm,align=center] (prog) {\textbf{PROGRAM / machine fields}\\[-1mm]\scriptsize OPCODE16, REG24, IMM64, ADDR64, CODE64, OFF64\\\textbf{канонический MSB-first}}; \node[draw=McpuGreen,very thick,rounded corners,fill=McpuGreenBg,right=of prog,minimum width=72mm,minimum height=22mm,align=center] (data) {\textbf{Архитектурные объекты данных}\\[-1mm]\scriptsize register file, DATA, STACK, CONTROL, INPUT, OUTPUT, TRACE, HEAP\\\textbf{target representation LibMPU}}; \draw[->,thick,McpuBlue2] (prog) -- node[above]{decode / execute} (data); \node[below=11mm of prog,align=center,font=\ttfamily\small] (pb) {0x1122334455667788\\11 22 33 44 55 66 77 88}; \node[below=11mm of data,align=center,font=\ttfamily\small] (db) {тот же u64 на текущем LE target\\88 77 66 55 44 33 22 11}; \draw[->,McpuBlue] (prog.south) -- (pb.north); \draw[->,McpuGreen] (data.south) -- (db.north); \end{tikzpicture} \caption{Сериализация PROGRAM и целевое представление объектов данных являются разными byte-order domains.} \end{figure} \subsection{PROGRAM: canonical MSB-first} Все multi-byte machine fields внутри PROGRAM кодируются старшим байтом вперёд. Это правило не зависит от host CPU, LibMPU byte order или будущего object-file container. value = 0x1122334455667788 IMM64 / CODE64 / ADDR64 / OFF64 bytes: 11 22 33 44 55 66 77 88 REG24 также является canonical machine field: его три байта формируются из WIDTH и ADDRESS по уже определённому битовому layout. Decoder не делает C-cast на packed host structure. \subsection{Data-object domain} Register file, DATA, STACK, CONTROL, INPUT, OUTPUT, TRACE и HEAP используют одно target object representation. Для integer-like objects byte order берётся из target profile LibMCPU, который совпадает с configured LibMPU: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] CPU_DATA_BYTE_ORDER = MPU_BYTE_ORDER CPU_DATA_WORD_ORDER = MPU_WORD_ORDER CPU_DATA_BYTE_ORDER_* = corresponding LibMPU settings CPU_DATA_WORD_ORDER_* = corresponding LibMPU settings \end{lstlisting} На текущей little-endian конфигурации: u64 value 0x1122334455667788 address+0 88 address+1 77 address+2 66 address+3 55 address+4 44 address+5 33 address+6 22 address+7 11 Общее representation. Если один object скопирован между register file или любыми data-object spaces без числового преобразования, его bytes не переставляются. Endian conversion не является скрытым side effect memory instructions. \subsection{Явные helper families в реализации} Чтобы source code executor/assembler визуально не смешивал два domain, используются две группы accessors: \_\_cpu\_code\_get\_u16 / \_\_cpu\_code\_put\_u16 \_\_cpu\_code\_get\_u32 / \_\_cpu\_code\_put\_u32 \_\_cpu\_code\_get\_u64 / \_\_cpu\_code\_put\_u64 \_\_cpu\_data\_get\_u16 / \_\_cpu\_data\_put\_u16 \_\_cpu\_data\_get\_u32 / \_\_cpu\_data\_put\_u32 \_\_cpu\_data\_get\_u64 / \_\_cpu\_data\_put\_u64 code-helpers явно собирают/разбирают MSB-first fields. data-helpers работают с target object representation через byte-safe copy semantics. Слово data здесь означает architectural data-object domain, а не только PTR64 segment DATA=0x0; эти helpers применимы также к register-file и STACK bytes. Именно поэтому выбрано имя \_\_cpu\_data\_get\_u*, а не \_\_cpu\_object\_get\_u*: в исходниках сразу виден контраст code vs data. Во избежание путаницы с segment DATA это значение термина закреплено в ABI documentation. \subsection{Integer, PTR64, return PC и overlapping registers} Signed integer использует two's-complement; unsigned - binary modulo 2\textasciicircum{}N. Их физический byte/word order является target data profile. PTR64 логически остаётся 4-bit SEG + 60-bit OFFSET, но когда он находится в r64, DATA field, STACK argument или structure member, это обычный 64-bit data object. PTR64(PROGRAM, 0x1234) = 0x2000000000001234 current LE target bytes: 34 12 00 00 00 00 00 20 Return PC, который CALL/CALLP помещают в STACK, тоже является 64-bit STACK data object. В самой CALL instruction CODE64 target encoded MSB-first; pushed return PC хранится в target data representation. RET читает его как data object и устанавливает логический PC. Overlapping register views физически наблюдают одни и те же bytes. Поэтому endian-sensitive assembler code, который разбирает широкое integer value через меньшие overlapping views, является target-specific low-level code. Это допустимо и сознательно является частью ABI. \subsection{Real и Complex} LibMCPU не вводит отдельный формат floating/complex objects поверх LibMPU. Для real применяется external object representation соответствующего LibMPU real type при текущем target profile. Для complex используется LibMPU complex representation двух real components половинной ширины. Complex physical half order не фиксируется независимым правилом LibMCPU. Он следует configured LibMPU word-order profile. Нельзя считать универсальной истиной, что первые bytes complex object всегда являются real component, а вторые - imaginary component. Complex arithmetic должен использовать C-opcodes/LibMPU representation либо явно target-specific byte code. \subsection{Assembler data directives} Directive inside .data создаёт target data object либо явно заданную raw byte sequence, а не canonical machine field: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .data .align 8 \end{lstlisting} x: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .integer 64, 0x1122334455667788 \end{lstlisting} text16: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .ucs2 "Андрей" \end{lstlisting} text8: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .utf8 "Андрей" \end{lstlisting} raw: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .byte 0x11, 0x22, 0x33 .align 8 \end{lstlisting} jump: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .cptr target .text \end{lstlisting} target: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] HALT \end{lstlisting} На текущем LE target bytes объекта x идут как 88 77 66 55 44 33 22 11. Но literal того же значения в LDI находится в PROGRAM как canonical 11 22 33 44 55 66 77 88. .integer/.int, .real и .complex генерируют target/LibMPU object representation. .ucs2 пишет target-representation 16-bit code units, тогда как .utf8 и .byte являются raw byte sequences без endian conversion. .cptr генерирует target-representation PTR64(PROGRAM,label/ expression). .cptr является каноническим mnemonic directive. Исторический .codeptr принимается assembler как compatibility alias. .label не используется: запись name: уже определяет label, тогда как .cptr создаёт объект-указатель. Полные правила строк, .equ и expressions приведены в разделе 34. \subsection{Byte-preserving memory operations} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] LOAD / STORE LOADP / STOREP PUSH / POP LDSP / STSP LDFP / STFP BMOV / BMOVI \end{lstlisting} Эти операции не знают endian semantics конкретного объекта. Они переносят physical bytes. В частности: DATA source --LOAD--> register A DATA source --BMOV--> DATA/STACK destination --LOAD--> register B bytes(register A) == bytes(register B) Точно так же structure/array/packed object может быть скопирован BMOV без type metadata и без byte swap. Padding bytes, если они являются частью complete object representation, тоже сохраняются. \subsection{Raw bytes, packed I/O и external byte order} char, byte arrays, UTF-8 и arbitrary binary blobs являются последовательностями байтов и сами по себе endian не имеют. packed означает отсутствие layout padding, но не означает network/ big-endian или little-endian protocol representation. Если внешний файл/протокол требует конкретный byte order, assembler/program должен выполнить явное преобразование. Автоматическая endian conversion при LOAD/STORE запрещена этой ABI-моделью. Для явного разворота физического порядка байтов LibMCPU предоставляет универсальную инструкцию BSWAP, подробно определённую в разделе 32. Она не подменяет понятие external byte order: решение о необходимости swap принимает программа или инструмент. \subsection{Target metadata в MELF} MELF уже содержит нормативную target metadata: byte order/container encoding, machine id и MCPU target description. Loader не пытается эвристически переконвертировать DATA/STACK-compatible objects при несовместимом profile. Образ либо совместим с текущей LibMPU target representation, либо отвергается до исполнения. PROGRAM machine fields при этом сохраняют отдельное canonical MSB-first кодирование. \subsection{Host alignment и unaligned accesses} Byte order не меняет ранее принятые alignment rules. Guest memory остаётся byte-addressable; unaligned accesses разрешены ISA. Реализация не должна читать произвольную guest memory через alignment-sensitive или strict-aliasing-unsafe casts. \_\_cpu\_data\_get\_u*/put используют byte-safe copy semantics, а большие arbitrary objects перемещаются memcpy/memmove или LibMPU routines. \section{Pointer comparison, red zone, active/reserved} PTR64 и BSWAP Этот раздел фиксирует четыре низкоуровневых соглашения CPU/assembler ABI, необходимые до появления object/executable format: сравнение PTR64, отсутствие stack red zone, поведение активных/зарезервированных segment selectors и универсальное преобразование порядка физических байтов. \subsection{Equality и ordering для PTR64} PTR64 остаётся обычным 64-битным архитектурным значением. Для равенства/неравенства сравниваются все 64 бита: selector и 60-bit offset. Поэтому существующего integer compare достаточно: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] ICMP r64@p, r64@q JZ equal JNZ not_equal \end{lstlisting} Это правило корректно и для разных selector: DATA:0x100 и STACK:0x100 не равны, поскольку их полные PTR64 различаются. Обычный ICMP может также выдавать signed integer flags для двух pointer bit patterns, однако ABI не придаёт этому результату смысла pointer ordering. Pointer ordering определяется только внутри одного segment selector и выполняется через PDIFF. Если selectors различны, PDIFF возвращает CPU\_EXEC\_POINTER\_FAULT и destination остаётся неизменным. \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] PDIFF r64@delta, r64@p, r64@q ; signed byte difference ITEST r64@delta, r64@delta JS p_less_than_q JZ p_equal_q \end{lstlisting} Разность двух 60-bit offsets всегда помещается в signed 64-bit диапазон. CPU не знает границ high-level объектов/массивов: same-segment ordering является низкоуровневой адресной операцией, а object-domain restrictions будущего языка остаются задачей compiler/program. \subsection{Stack red zone отсутствует} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] CPU_ABI_STACK_RED_ZONE_SIZE = 0 \end{lstlisting} STACK растёт вниз. Если текущий SP = X, байты ниже X не считаются принадлежащими функции как скрытая scratch-area. Функция должна сначала явно изменить SP через ADJSP или валидированный SETSP, а уже затем использовать выделенную область. Важно: ISA может физически позволить SP-relative address с отрицательным OFF64, если он попадает в allocated STACK. Это не превращает область ниже текущего SP в ABI red zone. Использование невыделенных bytes является ABI violation, а не обязательным аппаратным fault. \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] ; неправильно как normal ABI scratch STSP -8, r64@tmp ; правильно ADJSP -16 STSP 0, r64@tmp ... ADJSP 16 \end{lstlisting} Отсутствие red zone упрощает будущие traps/interrupts: обработчик может строить собственный frame ниже текущего SP, не уничтожая скрытые temporaries leaf-функции. \subsection{Active и reserved PTR64 selectors} Selectors 0x0..0x7 имеют назначенную address-space semantics. Selectors 0x8..0xF остаются структурно допустимыми PTR64 values, но не имеют memory semantics. Это сохраняет возможность будущего расширения без изменения PTR64 encoding. \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] Операция над reserved PTR64 Правило \end{lstlisting} (0x8..0xF) \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] MOV / SWAP / byte storage разрешено: bit pattern можно копировать и хранить ICMP equality / inequality разрешено как full 64-bit comparison PADD/PADDI/PSUB/PSUBI разрешено; selector сохраняется, arithmetic только по 60-bit offset PDIFF разрешено только при одинаковом selector LOADP / STOREP / BMOV / BMOVI CPU_EXEC_POINTER_FAULT JMPP / CALLP CPU_EXEC_POINTER_FAULT SETSP CPU_EXEC_POINTER_FAULT \end{lstlisting} HEAP теперь является активным selector 0x7. Его low guard 0..15 недоступен для dereference, но это не делает HEAP:0 значением NULL. All-zero DATA:0 остаётся единственным NULL PTR64. \subsection{BSWAP: raw byte reversal} BSWAP имеет opcode 0x0103 и 8-byte machine form: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] BSWAP r@dst, r@src \end{lstlisting} OPCODE16 + REG24(dst) + REG24(src) \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] 2 bytes + 3 bytes + 3 bytes = 8 bytes \end{lstlisting} Оба operands обязаны иметь одинаковую физическую ширину. Ассемблер использует именно r syntax, подчёркивая raw/non-complex interpretation. Допустимы все существующие widths от r8 до r65536. \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] source: b0 b1 b2 ... b(N-2) b(N-1) \end{lstlisting} destination: b(N-1) b(N-2) ... b1 b0 r8 является identity. Для r65536 разворачиваются все 8192 bytes. BSWAP не знает, является ли содержимое integer, PTR64, real, complex или частью packed record: операция определена только над физическими байтами. PSW invariant. BSWAP не изменяет ни volatile status flags, ни TF/IF/MODE/HF. Width mismatch возвращает CPU\_EXEC\_WIDTH\_FAULT до изменения destination. \subsection{In-place и snapshot semantics} Архитектурно BSWAP определяется как чтение полного source snapshot и только затем запись reversed bytes в destination. Поэтому in-place форма полностью определена: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] BSWAP r64@0x00100, r64@0x00100 \end{lstlisting} При текущем natural alignment equal-width register views не могут частично перекрываться иначе, чем полностью совпасть. Тем не менее snapshot formulation является нормативной и не зависит от внутренних оптимизаций исполнителя. \subsection{External binary formats} BSWAP не означает «to little endian» или «to big endian». Она лишь разворачивает bytes. Решение о swap принимает программа/tool, сравнивая external format с текущим CPU\_DATA\_BYTE\_ORDER. \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] ; native target-order value LDI r64@0x00000, 42 ; физически reversed representation (например wire order, если он противоположен target) BSWAP r64@0x00008, r64@0x00000 ; обратное преобразование BSWAP r64@0x00010, r64@0x00008 \end{lstlisting} Для packed file/protocol records это даёт явное и предсказуемое преобразование. Packed layout и byte order остаются независимыми понятиями: packed убирает padding, BSWAP меняет порядок bytes конкретного field.