Ассемблер 8080/8085/Z80 с компилятором на JS

8-битные микроконтроллеры и микропроцессоры от Intel и их клоны, а также компьютеры на них построенные

Moderator: Shaos

User avatar
vital72
Maniac
Posts: 203
Joined: 17 Jun 2014 04:29
Location: 93.80.157.217

Ассемблер 8080/8085/Z80 с компилятором на JS

Post by vital72 »

написал наконец-то я свой ассемблер, прежде, чем выкладывать на свой сайт, пока для тестов я его выложу на временный адрес -- https://assm.86rk.ru/
желающие могу потестировать/поискать баги, может даже какие то проекты собрать.
основное:
  • поддержка синтаксиса процессоров i8080 и Z80;
  • поддержка расширенного набора команд процессора i8085 и недокументированных команд Z80;
  • поддержка кодовых таблиц КОИ-7 Н0, КОИ-7 Н1, КОИ-7 Н2, КОИ8-Р;
  • полноценная поддержка арифметических выражений любой сложности;
  • директивы условной трансляции;
  • блочные директивы циклов;
  • макросы;
  • локальные метки и переменные;
  • однопроходный компилятор с поствычислением арифметических выражений.
краткая документация доступна по кнопке "ХЁЛП!"

что сейчас не реализовано:
  • редактор. используется кривой-косой браузереный на contenteditable, потому что надо было здесь и сейчас.
  • инструкция EX AF, AF' для Z80 -- именно так, со штрихом, но компилируется без штриха, разработчики Z80 подложили большую свинью, использовав в идентификаторе символ, который не может быть частью идентификатора.
в редакторе отсутствует нумерация строк, поэтому, если словили ошибку -- строку отсчитывайте сами :D
сейчас пишу редактор, в котором строки отображаются, но он ещё очень сырой/глючный, поэтому пока так, если очень надо -- прикручу, вместо браузерного.

что значат кодовые таблицы: в данных можно писать прям русским текстом и на выходе получить нужные коды:

Code: Select all

	db	"РАДИО-86РК", 0
скомпилируется в:

Code: Select all

      .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F
0000: 72 61 64 69 6F 2D 38 36 72 6B 00                |РАДИО-86РК.     |
PS: на странице есть множество примеров, которые демонстрируют возможности компилятора и помогают в освоении.
Last edited by vital72 on 18 Aug 2026 04:59, edited 1 time in total.
https://radio-86rk.ru
кто я такой, чтобы спорить с самим собой
Alekcandr
Devil
Posts: 769
Joined: 01 Oct 2007 10:30
Location: Ukraine

Re: Ассемблер 8080/8085/Z80 на JS

Post by Alekcandr »

vital72 wrote: 04 Jul 2026 12:20 [*] инструкция EX AF, AF' для Z80 -- именно так, со штрихом, но компилируется без штриха, разработчики Z80 подложили большую свинью, использовав в идентификаторе символ, который не может быть частью идентификатора.
Есть такое дело :) Надо отдельно такое парсить в лексере.
Эмулятор OrionEXT:
http://www.orion-ext.narod.ru
User avatar
vital72
Maniac
Posts: 203
Joined: 17 Jun 2014 04:29
Location: 93.80.157.217

Re: Ассемблер 8080/8085/Z80 на JS

Post by vital72 »

да, похоже так и придётся сделать, что то никаких других идей не приходит в голову...
https://radio-86rk.ru
кто я такой, чтобы спорить с самим собой
User avatar
vital72
Maniac
Posts: 203
Joined: 17 Jun 2014 04:29
Location: 93.80.157.217

Re: Ассемблер 8080/8085/Z80 с компилятором на JS

Post by vital72 »

порция обновлений.
  • реализовал-таки для Z80 поддержку AF' (со штрихом). пришлось в лексере прям гвоздями прибить обработку этого случая, получилось некрасиво, но что поделать...
  • добавил компилятор. теперь можно создавать блоки циклов и условий как в языках высокого уровня, где выражения в условиях записываются обычными операторами.
видимо, никто не читает помощь и обратной связи не получено, поэтому составлю краткий список всех возможностей моего транслятора ассемблера.


АССЕМБЛЕР 86РК

ПОДДЕРЖКА СИНТАКСИСА ПРОЦЕССОРОВ 8080, 8085 и Z80
директивы:
  • .8080 -- устанавливает синтаксис команд процессора i8080;
  • .8085 -- устанавливает синтаксис команд процессора i8085;
  • .z80 -- устанавливает синтаксис команд процессора Z80;
  • .z80compat -- устанавливает синтаксис команд процессора Z80, но ограничивает набор командами процессора i8080.
имеются алиасы к этим директивам:
  • .cpu 8080 = .8080
  • .cpu 8085 = .8085
  • .cpu z80 = .z80
  • .cpu z80compat = .z80compat
по умолчанию используется трансляция команд i8080.

КОДОВАЯ ТАБЛИЦА
кодовая таблица определяет, как символы юникода будут преобразовываться в числа. это избавляет от необходимости пользоваться сторонними утилитами для преобразования русских текстов в кракозябры, типа таких: "priwet mir!".
директивы:
  • .charset koi7n0 -- устанавливает кодовую таблицу КОИ-7 Н0;
  • .charset koi7n1 -- устанавливает кодовую таблицу КОИ-7 Н1;
  • .charset koi7n2 -- устанавливает кодовую таблицу КОИ-7 Н2;
  • .charset koi8r -- устанавливает кодовую таблицу KOI8-R.
по умолчанию используется кодовая таблица КОИ-7 Н2 (как в "Радио-86РК").

ВЫВОД СООБЩЕНИЯ
директива:
  • .print text -- выводит диагностическое сообщение text, кавычки не требуются.
в сообщении можно выводить значения переменных, меток и параметров макроса, для этого в тексте надо указать их имена, а перед именем поставить символ амперсанда ("&"), все числовые значения выводятся в десятичном формате.

ВЫВОД ОШИБКИ
директива:
  • .error text -- выводит сообщение text как сообщение об ошибке, кавычки не требуются.
директива .error прерывает трансляцию кода.

УСЛОВНАЯ ТРАНСЛЯЦИЯ
директивы:
  • .ifdef var_name -- истинно, если имя var_name определено;
  • .ifndef var_name -- истинно, если имя var_name не определено;
  • .if expr -- истинно, если результат выражения expr отличен от нуля;
  • .elif expr -- альтернативная ветка условия с проверкой истинности выражения expr;
  • .else -- альтернативная ветка, блок кода транслируется, если выражение в условии в .if/.elif было ложно;
  • .ifb arg -- истинно, если строка arg пустая;
  • .ifnb arg -- истинно, если строка arg не пустая;
  • .endif -- завершает блок условной трансляции.

ПОВТОР
директивы:
  • .rept expr[, var_name] -- создаёт блок кода, который повторяется вычисленное в expr количество раз. дополнительно через запятую в директиве можно объявить локальную переменную, используемую как параметр цикла. значение переменной начинается с нуля и увеличивается на 1;
  • .irp arg, { arg_list } -- создаёт блок кода, который повторяется для каждого элемента списка arg_list, значение элемента подставляется в arg;
  • .irpc arg, "string" -- создаёт блок кода, который повторяется для каждого символа строки string, текущий символ подставляется в arg;
  • .endm -- завершает блок повтора.

ЦИКЛ
директивы:
  • .while expr -- создаёт блок кода, который повторяется, пока вычисленное значение выражения expr имеет ненулевое значение;
  • .endm -- завершает блок цикла.

МАКРОС
директивы:
  • .macro name [params] -- создаёт макрос с именем name и необязательными параметрами params;
  • .endm -- завершает блок макроса.
имя макроса может быть указано перед директивой .macro.
дополнительно для каждого параметра можно указать тип или значение по умолчанию. тип указывается после имени параметра через двоеточие, значение по умолчанию -- через символ "равно" ("=").
имеются два типа параметров:
  • req -- обязательный параметр. будет сгенерирована ошибка, если макрос будет вызван без указания этого параметра;
  • rest -- вся оставшаяся часть строки параметров, включая запятые, заносится в этот параметр, этот тип может иметь только последний параметр.
типы параметров были заимствованы из турбо-ассемблера.

вычисление выражений в аргументах макроса.

все аргументы при вызове макроса передаются как набор токенов, выражения не вычисляются автоматически, что может привести к неожиданным результатам в некоторых случаях. пример:

Code: Select all

	.macro calc expr
	.print calc=&expr
	.endm

	calc 2+2
будет выведено "calc=2 + 2".

если необходимо вычислить выражение и результат передать в макрос, то перед выражением нужно поставить символ "=". пример:

Code: Select all

	.macro calc expr
	.print calc=&expr
	.endm

	calc =2+2
будет выведено "calc=4".

ВЫХОД ИЗ МАКРОСА
директива:
  • .exitm -- завершает трансляцию текущего макроса и прерывает циклы; применима к: .rept, .while, .irp, .irpc, .macro.

УСТАНОВКА АДРЕСА ТРАНСЛЯЦИИ
директива:
  • .org expr -- устанавливает текущий адрес трансляции равным значению выражения expr.

ВЫРАВНИВАНИЕ
директива:
  • .align expr[, fill] -- выравнивает текущий адрес трансляции по границе, заданной значением expr, результат выражения expr должен быть степенью двойки. необязательный параметр fill указывает, каким значением требуется заполнять пустоты. по умолчанию для заполнения используется нулевое значение.

ОБЪЯВЛЕНИЕ ИМЁН
директивы:
  • name .equ expr -- задаёт имени name постоянное значение, вычисленное из expr, значение не может быть изменено;
  • name .set expr -- задаёт имени name значение, вычисленное из expr, значение может быть изменено последующим .set.
для директивы .set имеется два синонима: .defl и запись через символ "равно" ("="), пример:

Code: Select all

var2 = 123

ОБЪЯВЛЕНИЕ ДАННЫХ
директивы:
  • .db expr[, expr, ...] -- размещает в выходном файле последовательность байтов, значения выражений должны находиться в диапазоне -128..255, строковые литералы автоматически разворачиваются в последовательность байтов;
  • .dw expr[, expr, ...] -- размещает в выходном файле последовательность 16-разрядных слов, значения выражений должны находиться в диапазоне -32768..65535;
  • .ds expr -- резервирует область адресного пространства, значение выражения expr определяет, сколько байт необходимо зарезервировать.
имеются алиасы к этим директивам:
  • .defb = .db
  • .defw = .dw
  • .defs = .ds

ПОДКЛЮЧЕНИЕ ФАЙЛОВ
директива:
  • .include file -- подключает файл file исходного кода, имя файла можно указывать без кавычек.
транслятор не умеет работать с файлами напрямую, транслятор обращается к файлам через интерфейс, который предоставляет окружение, из которого транслятор запускается, интерфейс передаётся в конструктор при создании транслятора. в браузере -- это виртуальная файловая система, хранение файлов в которой производится в localStorage.

КОНЕЦ
директива:
  • .end -- завершает трансляцию исходного файла.
директиву .end использовать необязательно.

ДИРЕКТИВЫ БЕЗ ТОЧКИ
большинство директив можно записывать без лидирующей точки; к директивам, для которых точка является обязательной, относятся:
  • .8080, .8085, .z80, .z80compat, .cpu, .charset, .print, .error

ОПРЕДЕЛЕНИЕ МЕТОК
метка представляет собой именованный адрес программы.
синтаксис:

Code: Select all

	label:
где label -- идентификатор, который не был определён ранее в тексте программы. метки можно размещать на одной строке вместе с инструкцией, директивой или другим содержимым строки, в этом случае символ двоеточия (":") после имени метки необязателен. если же в строке определена только метка, то символ двоеточия обязателен. имена меток должны быть уникальными, переопределение меток не допускается. в качестве имени метки не могут быть использованы имена инструкций и директив.
символ "$" является предопределённым идентификатором, который обозначает текущий адрес. имена меток и символ "$" могут использоваться в командах jmp, call, а также в арифметических выражениях.

ЛОКАЛЬНЫЕ МЕТКИ И ПЕРЕМЕННЫЕ
локальные метки и переменные -- это метки и переменные, которые ограничены локальной областью действия. для определения локальной метки или переменной к её имени необходимо добавить префикс "точка" ("."). локальная область действия создаётся автоматически в трёх случаях:
  • при определении глобальной метки;
  • при установке нового адреса трансляции директивой .org;
  • внутри макроса, объявленного директивой .macro.

ВЫРАЖЕНИЯ, АРИФМЕТИЧЕСКИЕ И ЛОГИЧЕСКИЕ ОПЕРАТОРЫ
транслятор вычисляет выражения любой сложности с неограниченной вложенностью скобок, поддерживаются все стандартные арифметические и логические операторы, для некоторых операторов имеются символьные синонимы:
  • MOD -- взятие остатка от деления;
  • AND -- побитовая "И";
  • OR -- побитовая "ИЛИ";
  • XOR -- побитовая "исключающее ИЛИ";
  • SHR -- сдвиг вправо;
  • SHL -- сдвиг влево;
  • LT -- "меньше";
  • GT -- "больше";
  • LE -- "меньше или равно";
  • GE -- "больше или равно";
  • EQ -- "равно";
  • NE -- "не равно";
  • NOT -- унарный оператор логического отрицания;
  • LOW -- унарный оператор взятия младшего байта 16-разрядного числа;
  • HIGH -- унарный оператор взятия старшего байта 16-разрядного числа;
  • ABS -- унарный оператор взятия абсолютного значения.
приоритеты операторов в основном совпадают с приоритетами операторов из СИ за исключением побитовых операторов "И", "ИЛИ" и "исключающее ИЛИ" -- их приоритеты выше операторов сравнения. это сделано намеренно, чтобы в выражениях минимизировать использование круглых скобок. например, можно записывать так:

Code: Select all

	.if varA & 0x80 == varB & 0x80
	...
	.endif
в языке СИ, чтобы условие корректно сработало, необходимо левую и правую часть выражения заключить в скобки.

имеются три дополнительных оператора, отсутствующие в языке СИ:
  • ** -- возведение в степень, имеет приоритет выше умножения и деления;
  • <? -- оператор нахождения минимума;
  • >? -- оператор нахождения максимума.
в выражениях можно указывать только целые числа, однако, внутренние вычисления выполняются с использованием чисел с плавающей точкой.

КОНСТАНТЫ В ВЫРАЖЕНИЯХ
транслятор поддерживает несколько стилей записи констант в выражениях.
СИ-стиль:
  • 0b01010101 -- двоичное число;
  • 0o377 или 0q377 -- восьмеричное число;
  • 0xAA55 -- шестнадцатеричное число.
Intel-стиль для ассемблеров:
  • 01010101b -- двоичное число;
  • 377o или 377q -- восьмеричное число;
  • 0AA55h -- шестнадцатеричное число.
Мотороловский стиль только для шестнадцатеричных чисел:
  • $AA55.

ПРЕДОПРЕДЕЛЁННЫЕ КОНСТАНТЫ
в выражениях можно использовать следующие предопределённые константы:
  • M_E -- значение числа e;
  • M_PI -- значение числа π.

ФУНКЦИИ
в выражениях можно использовать следующие математические функции:
  • SIN, COS, TAN, SQRT, LN, LG, LOG2, ROUND, FLOOR, CEIL

ПРОДОЛЖЕНИЕ СТРОКИ
при записи выражения или в иных случаях может возникнуть ситуация, когда текст программы не помещается целиком в видимую область экрана. чтобы целиком разместить длинный текст на экране, можно использовать символ продолжения строки — обратный слэш ("\"). применять этот символ необходимо в конце строки, т.к. все символы после него будут проигнорированы.
пример:

Code: Select all

a = 123
b = 456
c = 789

result = \
	((((a + b) * c) XOR (a SHL 4)) \
	+ (sqrt (a * b) * 100) \
	- (log2 (c) * 50)) \
	AND 65535

РАСШИРЕННЫЕ ИНСТРУКЦИИ ДЛЯ Z80
для Z80 имеются несколько фиктивных расширенных инструкций -- это инструкции, которых нет в реальной системе команд, но транслятор умеет разворачивать их в последовательность реальных инструкций.
неполный перечень фиктивных инструкций и команд, в которые они транслируются:

Code: Select all

  ld    bc, de          ;  ld    b, d  :  ld   c, e
  ld    bc, hl          ;  ld    b, h  :  ld   c, l
  ld    de, bc          ;  ld    d, b  :  ld   e, c
  ld    de, hl          ;  ld    d, h  :  ld   e, l
  ld    hl, bc          ;  ld    h, b  :  ld   l, c
  ld    hl, de          ;  ld    h, d  :  ld   l, e
  ld    hl, sp          ;  ld    h, 0  :  add  hl, sp

  ex    af', af         ;  ex    af, af'
  ex    hl, de          ;  ex    de, hl
  ex    hl, (sp)        ;  ex    (sp), hl
  ex    ix, (sp)        ;  ex    (sp), ix
  ex    iy, (sp)        ;  ex    (sp), iy

  add   de, bc          ;  ex    de, hl  :  add   hl, bc  :  ex    de, hl
  add   de, de          ;  ex    de, hl  :  add   hl, hl  :  ex    de, hl
  add   de, hl          ;  ex    de, hl  :  add   hl, de  :  ex    de, hl
  add   de, sp          ;  ex    de, hl  :  add   hl, sp  :  ex    de, hl
  adc   de, bc          ;  ex    de, hl  :  adc   hl, bc  :  ex    de, hl
  adc   de, de          ;  ex    de, hl  :  adc   hl, hl  :  ex    de, hl
  adc   de, hl          ;  ex    de, hl  :  adc   hl, de  :  ex    de, hl
  adc   de, sp          ;  ex    de, hl  :  adc   hl, sp  :  ex    de, hl
  sbc   de, bc          ;  ex    de, hl  :  sbc   hl, bc  :  ex    de, hl
  sbc   de, de          ;  ex    de, hl  :  sbc   hl, hl  :  ex    de, hl
  sbc   de, hl          ;  ex    de, hl  :  sbc   hl, de  :  ex    de, hl
  sbc   de, sp          ;  ex    de, hl  :  sbc   hl, sp  :  ex    de, hl

  rl    bc              ;  rl    c  :  rl  b
  rl    de              ;  rl    e  :  rl  d
  rl    hl              ;  rl    l  :  rl  h
  rr    bc              ;  rr    b  :  rr  c
  rr    de              ;  rr    d  :  rr  e
  rr    hl              ;  rr    h  :  rr  l
  sla   bc              ;  sla   c  :  rl  b
  sla   de              ;  sla   e  :  rl  d
  sla   hl              ;  add   hl, hl
  sll   bc              ;  sli   c  :  rl  b
  sll   de              ;  sli   e  :  rl  d
  sll   hl              ;  sli   l  :  rl  h
  sli   bc              ;  sli   c  :  rl  b
  sli   de              ;  sli   e  :  rl  d
  sli   hl              ;  sli   l  :  rl  h
  sra   bc              ;  sra   b  :  rr  c
  sra   de              ;  sra   d  :  rr  e
  sra   hl              ;  sra   h  :  rr  l
  srl   bc              ;  srl   b  :  rr  c
  srl   de              ;  srl   d  :  rr  e
  srl   hl              ;  srl   h  :  rr  l
отдельно нужно рассмотреть фиктивные расширенные инструкции, в которых используются 16-битные регистры для косвенной или индексной адресации (регистровые пары BC, DE, HL или индексные регистры IX и IY). во всех подобных инструкциях можно использовать оператор инкремента (++) или декремента (--) для пары или индексного регистра. поддерживаются как префиксные, так и постифинсные инкремент и декремент. примеры:

Code: Select all

  ld    a, (++bc)         ;  inc   bc          :  ld    a, (bc)
  ld    (--de), a         ;  dec   de          :  ld    (de), a
  ld    (hl++), imm8      ;  ld    (hl), imm8  :  inc   hl
  ld    (++iy + d), imm8  ;  inc   iy          :  ld    (iy + d), imm8
  and   (--ix)            ;  dec   ix          :  and   (ix + 0)
  bit   3, (hl--)         ;  bit   3, (hl)     :  dec   hl
в командах условных переходов, условных вызовов подпрограмм и условных возвратов можно инвертировать условие с помощью оператора отрицания (!).
перечень команд:

Code: Select all

  jr    !nz, label        ;  jr    z, label
  jr    !nc, label        ;  jr    c, label
  jr    !z, label         ;  jr    nz, label
  jr    !c, label         ;  jr    nc, label

  jp    !nz, label        ;  jp    z, label
  jp    !nc, label        ;  jp    c, label
  jp    !po, label        ;  jp    pe, label
  jp    !p, label         ;  jp    m, label
  jp    !z, label         ;  jp    nz, label
  jp    !c, label         ;  jp    nc, label
  jp    !pe, label        ;  jp    po, label
  jp    !m, label         ;  jp    p, label

  call  !nz, label        ;  call  z, label
  call  !nc, label        ;  call  c, label
  call  !po, label        ;  call  pe, label
  call  !p, label         ;  call  m, label
  call  !z, label         ;  call  nz, label
  call  !c, label         ;  call  nc, label
  call  !pe, label        ;  call  po, label
  call  !m, label         ;  call  p, label

  ret   !nz               ;  ret   z
  ret   !nc               ;  ret   c
  ret   !po               ;  ret   pe
  ret   !p                ;  ret   m
  ret   !z                ;  ret   nz
  ret   !c                ;  ret   nc
  ret   !pe               ;  ret   po
  ret   !m                ;  ret   p
все эти фиктивные команды были навеяны ассемблером Sjasm от Sjoerd Mastijn.
в отличие от Sjasm, мой транслятор позволяет произвольную запись инкремента и декремента для регистровых пар и индексных регистров (допускаются пробелы между оператором и именем регистра), кроме того, в выражениях с индексной адресацией позволяется записывать индексный регистр в середине выражения, главное, чтобы он не был с отрицательным знаком. всё это достигается благодаря тому, что выражение в скобках рассматривается и парсится именно как выражение, а не как отдельная специальная конструкция команды, а выражению всё равно, в каком порядке записаны его операнды. вот такая запись транслируется без ошибок:

Code: Select all

	ld    (123 + iy ++ - offset), c

КОМПИЛЯТОР
транслятор умеет компилировать условные выражения, записанные обычной инфиксной записью, как в языках высокого уровня. это позволяет упростить исходный код программ и делать его более понятным. таким образом, условия и циклы можно выделить в отдельные блоки для которых имеются следующие директивы:
  • @if/@elif/@else/@endif -- условия;
  • @while/@endw -- циклы с предусловием;
  • @repeat/@until -- циклы с постусловием;
  • @break -- безусловный выход из цикла;
  • @break_if -- условный выход из цикла;
  • @continue -- безусловный переход на условие цикла;
  • @continue_if -- условный переход на условие цикла.
выражения могут быть любой сложности, условия могут быть объединены логическим "И" (&&) или "ИЛИ" (||). условия и циклы можно вкладывать друг в друга без ограничений. операнды в условиях сравниваются как беззнаковые, кроме случая, когда сравнение производится непосредственно с нулём: "A < 0", "A <= 0", "A > 0", "A >= 0".
допустимо также применять к регистрам операторы инкремента и декремента, но есть ряд ограничений, которые всегда надо иметь в виду:
  • в выражениях можно использовать только регистры, регистровые пары, числа, именованные константы и метки, переменные в памяти использовать в выражениях нельзя;
  • смешивать 8-разрядные и 16-разрядные регистры в выражения нельзя;
  • операторы умножения, деления и взятия остатка не могут быть применены к регистрам;
  • если выражение требует вычислений или проверка условия выполняется не с аккумулятором, то содержимое аккумулятора будет потеряно, для 16-разрядных вычислений в роли аккумулятора используется пара HL.
примеры.
ветвистое условие:

Code: Select all

	@if a < 0x10
		lxi	h, 0x1111
	@elif a < 0x20
		lxi	h, 0x2222
	@elif a < 0x30
		lxi	h, 0x3333
	@elif a < 0x40
		lxi	h, 0x4444
	@else
		lxi	h, 0x5555
	@endif
компилируется в код:

Code: Select all

				ORG	0
0000:	FE 10			CPI	10h
0002:	D2 0B 00		JNC	LBL1
0005:	21 11 11		LXI	H, 1111h
0008:	C3 2F 00		JMP	LBL5
000B:	FE 20		LBL1:	CPI	20h
000D:	D2 16 00		JNC	LBL2
0010:	21 22 22		LXI	H, 2222h
0013:	C3 2F 00		JMP	LBL5
0016:	FE 30		LBL2:	CPI	30h
0018:	D2 21 00		JNC	LBL3
001B:	21 33 33		LXI	H, 3333h
001E:	C3 2F 00		JMP	LBL5
0021:	FE 40		LBL3:	CPI	40h
0023:	D2 2C 00		JNC	LBL4
0026:	21 44 44		LXI	H, 4444h
0029:	C3 2F 00		JMP	LBL5
002C:	21 55 55	LBL4:	LXI	H, 5555h
002F:			LBL5:
цикл с предусловием:

Code: Select all

	mvi	a, 123
	@while --a
		call 0x1111
	@endw
компилируется в код:

Code: Select all

				ORG	0
0000:	3E 7B			MVI	A, 7Bh
0002:	C3 08 00		JMP	LBL2
0005:	CD 11 11	LBL1:	CALL	1111h
0008:	3D		LBL2:	DCR	A
0009:	C2 05 00		JNZ	LBL1
цикл с постусловием:

Code: Select all

	mvi	a, 123
	@repeat
		call 0x1111
	@until !--a
компилируется в код:

Code: Select all

				ORG	0
0000:	3E 7B			MVI	A, 7Bh
0002:	CD 11 11	LBL1:	CALL	1111h
0005:	3D			DCR	A
0006:	C2 02 00		JNZ	LBL1
https://radio-86rk.ru
кто я такой, чтобы спорить с самим собой