\section{Ассемблер: UTF-8 source, строки, .equ и arithmetic expressions} Низкоуровневый mcpu-as обязан уметь описывать binary images data-like sections, labels и relocation-like address expressions без обязательного текстового препроцессора. Текстовая метапрограмма (\#define, \#include, conditional compilation, parameterized macros) остаётся отдельным будущим слоем. В assembler входят только механизмы, непосредственно связанные с machine/data representation и разрешением символов. \subsection{Кодировка assembler source} Исходный файл mcpu-as всегда UTF-8. Результат сборки не зависит от LANG, LC\_CTYPE или другой locale host-системы. Перед разбором assembler проверяет source как well-formed UTF-8 через LibMPUIO. Это особенно важно для строковых директив: одинаковый .s обязан создавать одинаковые image bytes на любых build hosts при одинаковом LibMCPU target profile. \subsection{Командная строка mcpu-as} Ассемблер всегда создаёт один перемещаемый объект MELF ET\_REL, поэтому отдельный переключатель режима сборки не нужен. Выходной файл задаётся обычной UNIX-опцией \code{-o}, а единственный исходный файл является позиционным аргументом: \begin{lstlisting}[language={}] mcpu-as -o file.o file.s mcpu-as file.s -o file.o \end{lstlisting} Обе формы полностью эквивалентны: порядок \code{-o} и входного файла свободный. Ровно один входной файл и ровно один выходной файл обязательны. Опции \code{-h} и \code{--help} печатают справку. Историческая форма \code{mcpu-as -c input.s output.o} удалена: \code{-c} больше не является опцией, поскольку другого режима работы у assembler нет. \subsection{.utf8 - NUL-terminated UTF-8 byte string} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .data \end{lstlisting} name\_utf8: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .utf8 "Андрей" \end{lstlisting} .utf8 записывает UTF-8 payload буквально как bytes и добавляет один завершающий 0x00. Никакого target endian преобразования и никакой locale conversion нет. "Андрей" -> D0 90 D0 BD D0 B4 D1 80 D0 B5 D0 B9 00 Поддерживаются escapes \textbackslash{}\textbackslash{}, \textbackslash{}", \textbackslash{}n, \textbackslash{}r, \textbackslash{}t, \textbackslash{}b, \textbackslash{}f, \textbackslash{}v, \textbackslash{}a, byte escape \textbackslash{}xHH, а также Unicode scalar escapes \textbackslash{}uXXXX и \textbackslash{}UXXXXXXXX. Результирующая последовательность снова проверяется как UTF-8. Встроенный NUL внутри .utf8/.ucs2 намеренно не поддерживается: эти директивы определяют C-style NUL-terminated strings. Для произвольного binary layout с внутренними нулями используется .byte или .integer/.int. \subsection{.ucs2 - UTF-8 source literal в strict UCS-2 target objects} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .data \end{lstlisting} name\_ucs2: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .ucs2 "Андрей" \end{lstlisting} Assembler сначала получает UTF-8 literal, затем использует strict LibMPUIO UTF-8 -> UCS-2 conversion. Каждый UCS-2 code unit записывается как отдельный 16-bit architectural data object с помощью target data byte order; в конце добавляется U+0000. U+0410 U+043D U+0434 U+0440 U+0435 U+0439 U+0000 current little-endian target bytes: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] 10 04 3D 04 34 04 40 04 35 04 39 04 00 00 \end{lstlisting} Strict UCS-2 допускает scalar values U+0000..U+FFFF, кроме surrogate range U+D800..U+DFFF. UTF-8 characters выше U+FFFF (например emoji) не раскладываются на UTF-16 surrogate pairs, а дают assembler error. Таким образом .ucs2 точно соответствует принятой в LibMCPU/LibMPUIO модели UCS-2, а не UTF-16. \subsection{.byte, .integer и .int} .byte создаёт raw bytes без implicit terminator, alignment или endian conversion: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .byte 0x4c, 0x43, 0x50, 0x55 .byte 1, 2+3, BLOCK_SIZE/512 \end{lstlisting} Каждый элемент должен находиться в диапазоне 0..255. .integer сохраняет прежний смысл typed integer object, а .int является полным синонимом. Обе директивы принимают список значений одной ширины: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .integer 16, 0x0410, 0x043d, 0x0000 .int 8, 65, 66, 0 \end{lstlisting} Каждое значение формирует отдельный object указанной width непосредственно в текущей data-like section position; hidden padding отсутствует. \subsection{.equ - абсолютные assembler symbols} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .equ table_ptr, 0x03000 .equ block_size, 4 * 512 .equ next_reg, table_ptr + 8 \end{lstlisting} .equ section-independent и не создаёт bytes. Имена symbols case-sensitive. Все .equ definitions собираются до emission, поэтому разрешены forward references между .equ symbols. \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .equ является absolute constant symbol. В текущем low-level assembler выражение \end{lstlisting} справа может ссылаться на integer literals и другие .equ, но не на DATA/CODE labels. Это сохраняет различие между absolute assembler symbols и section-relative labels/relocations MELF. Имя label не может совпадать с именем .equ. \subsection{Integer expression grammar и precedence} primary := integer | symbol | '(' expression ')' \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] unary := ['+' | '-'] unary | primary \end{lstlisting} product := unary \{ ('*' | '/' | '\%') unary \} expression := product \{ ('+' | '-') product \} Операции имеют обычный arithmetic precedence: unary, затем multiply/divide/remainder, затем add/subtract. Division by zero, overflow internal expression domain и значение вне требуемой width дают assembler error. Expressions используются для absolute numeric operands: register byte address, LDI, stack/pointer signed offsets, BMOVI count, .align, .byte, integer widths/values и других соответствующих immediate fields. \subsection{Label expressions и fixups} Address expressions дополнительно получают labels тех sections, которые допустимы для конкретного operand class. Поэтому assembler непосредственно поддерживает: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] LEA r64@table_ptr, input_vector+8 ; selector follows label section LOAD r64@next_reg, data_object+16 ; direct LOAD remains DATA-relative JMP loop+0 ; PROGRAM CODE64 CALL function .cptr handler+0 \end{lstlisting} LEA принимает labels из DATA/CONTROL/INPUT/OUTPUT/TRACE и code sections и создаёт полный PTR64 selector+offset. Direct LOAD/STORE остаются в namespace DATA labels + .equ. CODE64 operands и .cptr используют PROGRAM labels + .equ. Forward labels работают через fixups. CPU по-прежнему не хранит instruction-boundary metadata: arithmetic CODE expression обязано использоваться программистом осмысленно. \subsection{Symbolic register addresses} REG24 byte address может быть integer expression из absolute .equ symbols: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .equ table_ptr, 0x03000 .equ temp_ptr, table_ptr + 8 LEA r64@table_ptr, table LDI r64@temp_ptr, 42 LDI r64@(table_ptr + 16), 7 \end{lstlisting} После вычисления expression assembler выполняет обычную проверку register width, natural alignment и границ 512 KiB register file. DATA/CODE labels в register-address expression не участвуют: label address и register-file byte address являются разными namespaces. \subsection{Граница mcpu-as и будущего препроцессора} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] Механизм Уровень Причина .init/.text/.fini/.data/.control/.input/.output/.trace, mcpu-as определяют layout labels, .align executable/data images .integer/.int/.real/.complex/.byte/.utf8/.ucs2/.cptr mcpu-as создают data-like objects/bytes .equ, arithmetic expressions, symbol fixups mcpu-as необходимы для адресов и будущих relocations #define/#undef/#include/#if/#ifdef отдельный текстовая preprocessor metaprogramming не является machine encoding parameterized macros / repetition отдельный source transformation preprocessor до mcpu-as \end{lstlisting} Таким образом обычный symbolic assembler остаётся самодостаточным, а будущая схема .S -> cpu-cpp -> mcpu-as может добавляться независимо и не усложняет binary semantics cpu-as. \subsection{Полный пример} \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .equ table_ptr, 0x03000 .equ next_reg, table_ptr + 8 .equ block_size, 4 * 512 .data \end{lstlisting} name16: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .ucs2 "Андрей" \end{lstlisting} name8: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .utf8 "Андрей" \end{lstlisting} header: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .byte 0x4c, 0x43, 0x50, 0x55, block_size/512 \end{lstlisting} letters: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .int 16, 0x0410, 0x043d, 0x0434, 0 .align 16 \end{lstlisting} answer: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] .integer 64, 42 .text \end{lstlisting} start: \begin{lstlisting}[language={},basicstyle=\ttfamily\scriptsize] LEA r64@table_ptr, name16 LOAD r64@next_reg, answer HALT \end{lstlisting} Здесь ни один identifier не требует textual macro substitution: table\_ptr/next\_reg - absolute assembler symbols, name16/answer - DATA labels, а arithmetic expression вычисляется самим assembler. \subsection{Маркеры строк препроцессора и логические координаты diagnostics} После появления \code{mcpu-cpp} assembler не использует собственную директиву \code{\#line} для управления source coordinates. Вместо неё \code{mcpu-as} распознаёт маркеры строк, оставляемые препроцессором в выходном потоке: \begin{lstlisting}[language={}] # digit-sequence # digit-sequence "filename" # digit-sequence "filename" flags... \end{lstlisting} \code{digit-sequence} содержит как минимум одну decimal digit и всегда разбирается в основании 10, даже при leading zeroes. Допустимый диапазон - 0..2147483647. Значение задаёт logical line number для physical source line, непосредственно следующей за marker. Filename optional; при его отсутствии сохраняется предыдущий logical filename. Всё после закрывающей кавычки filename считается полем flags препроцессора и полностью игнорируется assembler. Marker не создаёт bytes, symbols, \code{__LINE__} или \code{__FILE__}, не участвует в expression evaluation и влияет только на source coordinates последующих diagnostics. Empty и comment-only ordinary lines увеличивают logical counter обычным образом. Строки \code{\#pragma} и \code{\#lang} предназначены для других участников toolchain: \code{mcpu-as} полностью игнорирует их и не увеличивает logical line counter. Любая другая строка, начинающаяся с \code{\#}, также удаляется из assembler token stream, но учитывается как одна ordinary logical source line. \begin{normative} \textbf{Историческая форма \#line.} Запись \code{\#line N "file"} больше не является source-location directive \code{mcpu-as}. Она обрабатывается как обычная неизвестная строка \code{\#...}: игнорируется как assembler syntax и учитывается при продвижении logical counter. Координаты source задаются только preprocessing line marker формой \code{\# N "file" flags...}. Это diagnostics contract и не часть MELF ABI. \end{normative}