Курс программирования на ассемблере для C64(Ghouls/Mechanix). Урок 1

4 новых инструкции:

  • ASL=SHIFT LEFT 1 BIT
  • LSR=SHIFT RIGHT 1 BIT
  • ROL=ROTATE 1 BIT LEFT
  • ROR=ROTATE 1 BIT RIGHT

Для того чтобы лучше понять эти инструкции нам понадобятся числа в двоичном формате:

Аккумулятор теперь содержит значение %00101000 (=40 DECIMAL = $28 HEX).

ВСЕ биты сдвинулись влево, что означает, что значение удвоилось. ASL умножает число на 2. Таким же образом LSR делит число на 2.

Рассмотрим следующее:

Вот тут кое-что интересное:

Бит №7 (самый левый) будет «сохранен» в флаг переноса С, то есть С = 1. Регистр A теперь содержит значение %00110010.  Мы видим, что когда бит №0 (самый правый) будет перемещен на 1 позицию влево, на его место будет выставлен 0. То же самое делает LSR:

Регистр A содержит значение %01001100 и снова будет установлен флаг C, независимо от того  был ли он уже установлен, или нет.

ROL и ROR работают так же как  ASL и LSR. ROL умножает число на 2, ROR делит число на 2, но существует небольшая разница:

Мы рассмотрим следующие примеры:

Регистр A (конечно) содержит значение  %00000000.  Флаг C равен 0, потому что он сброшен.

Регистр A теперь содержит значение %00000001!  И флаг С равен нулю, несмотря на то, что мы его предварительно выставили. Вот разница между ROL и ASL: ROL сохраняет флаг C в регистре A, а затем сбрасывает его(флаг C). Чтобы это понять, посмотрите таблицу:

Команда Флаг C Регистр А
01 CLC C=0
02 LDA #%00000001 C=0 A=%00000001
03 ROL C=0 A=%00000010
04 ROL C=0 A=%00000100
05 ROL C=0 A=%00001000
06 ROL C=0 A=%00010000
07 ROL C=0 A=%00100000
08 ROL C=0 A=%01000000
09 ROL C=0 A=%10000000
10 ROL C=1 A=%00000000
11 ROL C=0 A=%00000001
12 ROL C=0 A=%00000010

Этот пример состоит из 12 шагов. Слева номер шага. Затем идет инструкция машинного кода. Далее значение C-флага и, наконец, содержимое аккумулятора. Шаги 10 и 11 наиболее интересны. В шаге 10 бит № 7 передается  в флаг C. В шаге 11 установленный флаг C сохраняется в бит №0 и сбрасывается. Далее можно посмотреть аналогичный пример, только с «ASL» вместо  «ROL»

Команда Флаг C Регистр А
01 CLC C=0
02 LDA #%00000001 C=0 A=%00000001
03 ASL C=0 A=%00000010
04 ASL C=0 A=%00000100
05 ASL C=0 A=%00001000
06 ASL C=0 A=%00010000
07 ASL C=0 A=%00100000
08 ASL C=0 A=%01000000
09 ASL C=0 A=%10000000
10 ASL C=1 A=%00000000
11 ASL C=0 A=%00000000
12 ASL C=0 A=%00000000

ASL не переносит флаг C в бит №0 хотя работает так же  как ROL. Надеюсь, вы теперь поняли принцип? LSR & ROR влияют на флаг C таким же образом. ROR переносит флаг C  в бит №7, а бит №0 передает  в флаг C.  LSR работает так же  как ASL только в другую сторону.

2 новые команды:

  • PHA = PUSH ACCUMULATOR ON STACK.
  • PLA = PULL ACCUMULATOR FROM STACK.

Стек находится в памяти по адресу $0100-$01FF. Он используется процессором, когда  выполняется команда JSR. При этом в нем сохраняются 2 байта, а  когда позже выполняется RTS, эти 2 байта выгружаются из стека. Вы можете работать со стеком при помощи команд  PHA и PLA. Посмотрим это на следующем примере:

Стек работает примерно как записная книжка. В данном примере $03 сохраняется в стеке.

Так что можно использовать аккумулятор для чего-то другого (например, окрасить бордюр в черный цвет). А затем получить число из стека назад. Конечно, вы можете хранить больше чем 1 значение на стеке:

Сначала на стеке сохраняется A. X перемещается  в А. A сохраняется на стеке. Y перемещается  в А. A снова сохраняется на стеке. После работы программы мы загружаем число в  A из стека. После значение A передается в Y. Снова загружаем  в A из стека и передаем A в X. И последнее значение загружаем в регистр A. Теперь регистры X, Y и A содержат те же значения, что и до сохранения на стеке. Например, это можно использовать так — сделать некоторые расчеты, сохранить эти вычисления на стеке и затем загрузить их позже, когда понадобится.  Обратите внимание, что нельзя сохранить X и Y в стеке напрямую; сначала  надо отправить в A, а затем сохранить A на стеке.

Достаточно о стеке, теперь рассмотрим совсем другое: Логические операторы.

  • AND ; «AND» MEMORY WITH ACCUMULATOR.
  • ORA  ;»OR» MEMORY WITH A
  • EOR ; «EXCLUSIVE-OR» MEMORY WITH ACCUMULATOR.

Пример «AND»:

LDA    #%00101110

AND   #%10001010

RESULTAT :   %00001010

Мы видим, что верно следующее:

  • 0 AND 0 = 0
  • 0 AND 1 = 0
  • 1 AND 0 = 0
  • 1 AND 1 = 1

Сравните эту таблицу  с примером выше. Бит №X в результате может только быть 1, если:

Бит №X в «LDA» = 1       и если

Бит №X в “AND” = 1

Упражнение 7

LDA #%00001111

AND #%00111100

Результат:   #%????????

Найдите результат.

Скрытый текст

Вы можете использовать  «AND» для «управления» результатом вычислений, например:

Мы получаем значение из $3000, затем мы делаем  «AND»  с числом 7. Затем сохраняем результат в $D020. Независимо от содержимого $3000, наибольшее число, которое будет сохранено в $D020 — 7.  Мы предполагаем, что $3000 содержит 255, которое аналогично %11111111  в двоичной системе.

%11111111  ; содержимое $3000

AND %00000111

=%00000111

«AND %00000111»  выключает биты  №№  3, 4, 5, 6 и 7. Значение $D020 не может превышать 7.

И наоборот с «ORA»: 

  • 0 OR 0 = 0
  • 0 OR 1 = 1
  • 1 OR 0 = 1
  • 1 OR 1 = 1

«AND» может использоваться для управления наибольшим значением числа.

«ORA» может использоваться для проверки минимального значения числа.

ПРИМЕР:

Здесь значение в $D020 не будет меньше чем 7, потому что «ORA» должен обеспечить, чтобы биты №№ 0,1 и 2 были включены при любых обстоятельствах.

Упражнение 8

LDA #%00100110

ORA #%01101011

Результат:   #%????????

Скрытый текст

Последняя инструкция «EOR» отличается от двух предыдущих. ПРИМЕР:

LDA #%01100101

EOR #%11001011

Результат:   #%10101110

Так мы можем определить следующую схему:

  • 0 EOR 0 = 0
  • 0 EOR 1 = 1
  • 1 EOR 0 = 1
  • 1 EOR 1 = 0

С  «EOR»  мы можем сделать программу, которая будет работать как переключатель. К примеру, когда она используется в первый раз, выполняется одно действие. Когда она вызывается второй раз, выполняется что-то другое. В третий раз выполняется снова первое действие, и т.д. Ниже приведен пример такой (полезной) программы.

Во-первых, загружаем  значение $3000 в А. Затем делаем EOR #$01 и сохраняем в $3000.  Если теперь мы предположим, что в $3000 содержится значение $01 с самого начала, то  результат будет следующим: $01 EOR $01 = $00. То есть, Z-флаг установлен, потому что А  содержит ноль. Далее  выполняется BEQ ZERO (и бордюр станет белым). Если в $3000 содержится значение $00, то будет так: $00 EOR $01 = $01.  Флаг Z не будет установлен, т.е. Z-флаг=0. Поэтому BEQ ZERO не будет выполнена. Компьютер продолжает работу, и бордюр будет черного цвета. Содержимое $3000 переключается между $00 и $01. Обратите внимание, что это работает, только если начальное значение в $3000 или $00 или $01.  Если начальное значение будет другим, «EOR $01» не даст 0 или 1 и программа не будет работать как надо.

Урок 1 подходит к концу, но я хотел бы кратко объяснить еще несколько инструкций.

  • NOP = NO OPERATION

Эта инструкция не влияет ни  на флаги, ни на регистры. Короче говоря, он ничего не делает! С другой стороны она часто используется, когда вы отрисовываете экран или «открываете» бордюр, но вы познакомитесь с этими приемами на других уроках…!

  • BMI = BRANCH ON RESULT MINUS (если N флаг установлен)
  • BPL = BRANCH ON RESULT PLUS (если N флаг не установлен)

NEGATIV-флаг используется довольно редко, так как отрицательные числа практически не имеют никакого применения, когда нужно сделать демо и т.д. Флаг N будет установлен, если значение в регистре (A, X или Y), в который были внесены изменения, находится в диапазоне  $00-$7F. Он будет выключен, если значение находится в диапазоне $80-$FF.

  • BRK = FORCE BREAK.

Эта инструкция устанавливает флаг прерывания (B-флаг). Если вы программируете в машинных кодах в мониторе, можно  использовать BRK для остановки программы. Вы можете BRK использовать вместо RTS, но если вы программируете в Турбо-Ассемблере, нельзя заканчивать программу  BRK! Это может привести к зависанию. В общем, BRK используется очень редко…

Добавить комментарий