Написание цикла while в сборке
Я пытаюсь написать цикл while в сборке с процессором 6502, и я не могу понять, как написать шестнадцатеричный код. Я видел примеры, написанные с использованием сокращения, где есть метка для того, где цикл должен начинаться и заканчиваться, но я ничего не вижу для фактического шестнадцатеричного кода.
Два кода, которые я считаю полезными:
- Сравнение байта в памяти с X reg (сокращение: CPX, hex: EC). Это устанавливает Z флаг на ноль, если равен и
- Байты ветви X, если Z flag = 0 (сокращение: BNE, hex: D0)
4 ответа
Вот место для начала: http://www.obelisk.demon.co.uk/6502/. На странице представлен кросс-ассемблер, который вы можете запустить на своем ПК. Это может быть хорошей платформой для вас.
Прежде чем что-то делать, вы должны понять теорию работы 6502. Затем вы должны понять процесс разработки программного обеспечения, который включает в себя:
- подготовка так называемых "исходных файлов" символических инструкций, которые вы называете "стенография"
- используя ассемблер, переводя этот исходный файл в машинные инструкции, которые понимает 6502
- загрузка перевода в 6502
- сказать 6502 выполнить переведенные машинные инструкции
Ваша программа-пример пытается скопировать LEN
байты памяти из SRC
в DST
,
Вы форматируете это так:
LDX #0 ; Start with the first byte
_LOOP LDA SRC,X ; load a byte from SRC into the A register
STA DST,X ; store that byte into DST
INX ; bump the index register to point to the next SRC and DST locations
CPX #LEN ; have we moved LEN characters?
BNE _LOOP ; if not, go move the next one
После того, как вы добавили больше строк выписки (например, END
, например); и после того, как вы определили SRC
, DST
, а также LEN
, вы сохраняете все это в файле, скажем, cploop.txt
,
Затем вы говорите ассемблеру перевести его. Ассемблер выпускает файл двоичного машинного кода 6502, который можно представить в виде шестнадцатеричных байтов, о которых вы говорите.
Вы передаете этот файл машинного кода в имитированный 6502. Затем вы как-то предписываете 6502 выполнить операции, которые воплощает машинный код.
Вот пример, показывающий соответствие между сборкой (то, что вы называете "сокращением") и машинным кодом. Во-первых, вот код ассемблера для алгоритма с некоторыми абстрагированными параметрами:
* = 4000 ; This is the address where our program will be stored
LDX #len
loop LDA src,X
STA dest,X
DEX
BNE loop
Конечно, вы не можете превратить это непосредственно в машинный код. Вам также необходимо заполнить значения len
, src
а также dest
:
src = $1234
dest = $5678
len = 10
Что нужно знать о loop
имя это так же, как src
присваивается значение $1234
, loop
после этого будет назначен адрес инструкции. Так что в этом случае, так как LDX #len
занимает 2 байта (как я покажу вам в ближайшее время), loop
установлен в $4000 + 2 = $4002
, Это делается автоматически ассемблером, но, конечно, вы можете сделать все это и на бумаге.
Итак, каков машинный код 6502 для вышеуказанной программы сборки?
A2 0A
BD 34 12
9D 78 56
CA
D0 F7
Откуда я это знаю? Ну, я только что вставил вышеупомянутую программу в онлайн-ассемблер 6502 по адресу http://www.masswerk.at/6502/assembler.html. Он даже показывает подробное отображение между сборкой и машинным кодом:
4000 LDX #LEN A2 0A
4002 LOOP LDA SRC,X BD 34 12
4005 STA DEST,X 9D 78 56
4008 DEX CA
4009 BNE LOOP D0 F7
400B
Обратите внимание, как фактическая стоимость LOOP
даже не используется для вычисления машинного кода для BNE LOOP
только его относительный адрес по сравнению с BNE
сама инструкция: F7
равно -9, а разница между $400B
а также $4002
это -9!
Таким образом, если бы вы делали это вручную, вы бы просто перевели все остальное в машинный код, а затем, когда вы переходите к прыжку, вы вычисляете разницу между начальным адресом следующей инструкции и адресом назначения перехода. Он должен быть отрицательным для прыжков назад и положительным для прыжков вперед.
Команды ветвления принимают однобайтовый операнд относительного адреса со знаком, который добавляется к адресу следующей инструкции для получения цели ветвления. Поскольку инструкция ветвления всегда занимает 2 байта, целевой адрес является адресом инструкции ветвления плюс операнд (с расширенным знаком) минус 2.
Примеры:$D0 $00
: no-op: ветка переходит к следующей инструкции независимо от условия$D0 $FE
: Branch указывает на себя, создавая бесконечный цикл, если Z=0
,
while
утверждение действительно означает:
- проверить условие
- если условие ложно, перейдите к 5
- сделай что-нибудь
- вернуться к 1 (простой JMP или ветка)
- остальная часть программы
С 6502 все это будет чрезвычайно просто, если вы не сделаете много предположений. Если условие, которое вы тестируете, всегда будет регистром, инструкции сравнения (cmp, cpx, cpy) и ветвления, очевидно, то, что вам нужно для 1.
Если это будет один байт, хранящийся в памяти, вам нужно загрузить этот байт, а затем сравнить его.
Если это 16-битное значение, хранящееся в двух байтах, вам необходимо загрузить и протестировать каждое значение байта.
Имеете дело с поплавками? Если вы написали или имеете доступ к пакету с плавающей запятой (например, подпрограммам с плавающей запятой Commodore 64 ROM BASIC), вам нужно будет их использовать.
Вы можете понять, почему языки высокого уровня имеют типы данных.
На самом деле, это зависит от типа данных, с которыми вы имеете дело, но любая реализация while
в 6502 следует в значительной степени следовать вышеизложенному.
Конкретный случай, который вы определили в своем вопросе, - это нормально, если вы знаете, что сравниваемые данные всегда будут в X и что ваш пункт назначения всегда будет на расстоянии +127/-128 байт (предел диапазона команд Bxx).