Что такое циклический сдвиг
Перейти к содержимому

Что такое циклический сдвиг

  • автор:

14. Циклический сдвиг элементов массива. (№ 15)

Под циклическим сдвигом понимают перемещение элементов «по цепочке». Т.е. при сдвиге элементов вправо элемент находящийся на последнем месте в массиве становится на место первого с последующим сдвигом всех остальных элементов. И наоборот, при циклическом сдвиге влево — первый элемент переходит на место последнего с соответствующим перемещением всех элементов массива. При реализации таких алгоритмов следует помнить, что обход при перемещении циклическим сдвигом вправо начинают от последнего элемента, а при сдвиге влево с первого элемента.

FasmWorld Программирование на ассемблере FASM для начинающих и не только

Циклический сдвиг отличается от линейного тем, что выдвигаемые с одного конца биты вдвигаются с другой стороны, то есть движутся по кольцу. В процессора x86 существует 2 вида циклического сдвига: простой и через флаг переноса (CF). У всех команд, рассматриваемых в этой части учебного курса, по 2 операнда, таких же, как у команд линейного сдвига. Первый операнд — сдвигаемое значение и место для записи результата. Второй операнд — счётчик сдвигов, который может находится в регистре CL или указываться непосредственно.

Простой циклический сдвиг

Циклический сдвиг вправо выполняется командой ROR, а влево — командой ROL. Схема работы этих команд представлена на рисунке (на примере 8-битного операнда):

Значение последнего выдвигаемого бита копируется в флаг CF. Для сдвигов на 1 бит устанавливается флаг OF, если в результате сдвига изменяется знаковый бит операнда. (Честно говоря, не помню, чтобы я этим когда-то пользовался. Но может вам пригодится �� ) Примеры использования команд:

rol bl,1 ;Циклический сдвиг BL на 1 бит влево ror word[si],5 ;Циклический сдвиг слова по адресу в SI на 5 бит вправо rol ax,cl ;Циклический свдиг AX на CL бит влево

Циклический сдвиг через флаг переноса

Отличие от простого циклического сдвига в том, что флаг CF участвует в сдвиге наравне с битами операнда. При сдвиге на 1 бит выдвигаемый бит помещается в CF, а значение CF вдвигается в операнд с другой стороны. При сдвиге на несколько бит эта операция повторяется многократно. Циклический сдвиг через флаг переноса выполняется командами RCR (вправо) и RCL (влево).

Опять же для сдвигов на 1 бит устанавливается флаг OF, если в результате сдвига изменяется знаковый бит операнда. Примеры использования команд:

rcr dh,3 ;Цикл. сдвиг DH на 3 бита вправо через флаг CF rcl byte[bx],cl ;Цикл. сдвиг байта по адресу в BX на CL бит влево через флаг CF rcl dx,1 ;Цикл. сдвиг DX на 1 бит влево через флаг CF

Пример программы

В качестве примера напишем программу для подсчёта единичных битов в байте. Для анализа битов используется команда ROL в цикле. Цикл выполняется 8 раз — по числу битов в байте. Если очередной бит равен 1, то выполняем инкремент счётчика единичных битов.

use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov al,[x] ;AL = x xor bl,bl ;BL = 0 (Здесь будем считать единичные биты) mov cx,8 ;Инициализация счётчика цикла lp: rol al,1 ;Цилический сдвиг AL на 1 бит влево jnc bit0 ;Переход, если CF=0 inc bl ;Инкремент счетчика единичных битов bit0: loop lp ;Команда цикла mov [n],bl ;Сохраняем результат в n mov ax,4C00h ;\ int 21h ;/ Завершение программы ;———————————————————————- x db 89h ;Байт n db ? ;Количество единичных битов в байте

Эту программу можно немного оптимизировать. Во-первых, использовать вместо условного перехода команду ADC со нулевым вторым операндом. Это позволит прибавить 1, если CF=1 и прибавить 0, если CF=0. Во-вторых, считать единичные биты можно в регистре AH, а обнулить его в начале с помощью команды MOVZX, совместив с загрузкой байта в регистр AL. Ноль для команды ADC можно взять в регистре CH, это делает команду короче и быстрее, чем при использовании непосредственного операнда. CH равен 0 во время выполнения цикла, так как CX изменяется от 8 до 0. Вот что получилось в итоге:

use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h movzx ax,[x] ;AL = x, AH = 0 mov cx,8 ;Инициализация счётчика цикла lp: rol al,1 ;Цилический сдвиг AL на 1 бит влево adc ah,ch ;Прибавляем флаг CF к AH, так как CH = 0 loop lp ;Команда цикла mov [n],bl ;Сохраняем результат в n mov ax,4C00h ;\ int 21h ;/ Завершение программы ;———————————————————————- x db 89h ;Байт n db ? ;Количество единичных битов в байте

Всего 6 команд для подсчета битов в байте. А попробуйте написать то же самое на языке высокого уровня ��

Упражнение

Объявите в программе строку. Длина строки должна быть больше 8 символов и храниться в байте без знака. Напишите цикл для шифрования строки по алгоритму: первый символ циклически сдвигается вправо на 1 бит, второй символ — на 2 бита, …, 7-й — на 7 битов, 8-й — снова на 1 бит, 9-й на 2 бита и т.д. Затем напишите цикл для расшифровки строки и выведите её на экран.

Комментарии:

;ура, вроде верно
use16
org 100h

start:
movzx cx,[len]
xor di,di
xor bh,bh
xor bl,bl

lp1:
mov bh,cl ;сохраняем значение cl, чтобы не нарушить цикл
inc bl

cmp bl,8 ;если 8-й символ, то снова двигаем на 1 бит
jnz metka1
mov bl,1

metka1:
mov cl,bl

mov al,byte[text+di]
ror al,cl
mov byte[rezult1+di],al
add di,1

mov cl,bh ;останавливаем счетчик цикла

;решил вывести закодированные символы на экран
xor di,di
movzx cx,[len]
mov ah,02h

lp3:
mov dl,byte[rezult1+di]
int 21h
inc di

;———расшифровка
movzx cx,[len]
xor di,di
xor bh,bh
xor bl,bl

lp2:
mov bh,cl ;сохраняем значение cl, чтобы не нарушить цикл
inc bl

cmp bl,8 ;если 8-й символ, то снова двигаем на 1 бит
jnz metka2
mov bl,1

metka2:
mov cl,bl

mov al,byte[rezult1+di]
rol al,cl
mov byte[rezult2+di],al
add di,1

mov cl,bh ;останавливаем счетчик цикла

mov ah,02h
mov dl,13
int 21h
mov dl,10
int 21h

lp4:
mov dl,byte[rezult2+di]
int 21h
inc di

mov ax,4C00h
int 21h

Ух. Ты молодец, упорный ��
Всё правильно.
Можно немного оптимизировать. Например, заменить

xor bh,bh xor bl,bl

И «add di,1» лучше делать как «inc di»

помогите пожалуйста найти ошибку в программе, надо выполнить деление натуральных чисел не используя команды div, idiv

Я тебе помогу. Такое деление можно написать с помощью вычитаний и сдвигов в цикле. Какой размер чисел для деления? Числа со знаком или без? Можешь в комментарий написать свою программу с ошибкой )

Безбожно тупил, но кажется осилил.

lp_kod:
mov al,[string+di]
mov bx,cx
sub cx,cx
mov cl,dl
ror al,cl
mov [string_2+di],al
cmp cl,7
je sbros
inc dl
lp:
inc di
sub ax,ax
mov cx,bx
loop lp_kod
jmp dekod

sbros:
sub dl,6
jmp lp

dekod:
mov cx,12
mov dl,7
sub di,di

lp_dekod:
mov bx,cx
mov al,[string_2+di]
mov cl,dl
ror al,cl
mov [string_3+di],al
cmp cl,1
je nabros
dec dl
lp2:
inc di
mov cx,bx
loop lp_dekod
jmp vivod

nabros:
add dl,6
jmp lp2

vivod:
sub di,di
mov cx,12
mov ah,02h

lp_vivod:
mov dl,[string_2+di]
int 21h
inc di
loop lp_vivod

sub di,di
mov cx,12
mov ah,02h
mov dl,13
int 21h
mov dl,10
int 21h

lp_vivod_2:
mov dl,[string_3+di]
int 21h
inc di
loop lp_vivod_2

mov ah,08h
int 21h

mov ax,4c00h
int 21h

Как-то даже взрывает мозг �� Особенно сброс и наброс. Что-то здесь не правильно. Отпишусь, когда проверю.

Да, точно, надо кометарии в програму добавить, а то сам забуду чего нагородил))).

Извиняюсь. Долго проверял, но работает всё верно. Хотя конечно можно проще написать тоже самое.
В первом цикле есть код:

sub cx,cx mov cl,dl

Здесь CX можно не обнулять, так как новое значение всё равно затирает старое. Кстати, во втором цикле у тебя нет этой команды.
Чуть ниже:

Тоже обнулять не нужно. Эту команду можно просто убрать.

И гораздо проще можно сделать «сброс» и «наброс». У тебя 3 перехода получается. Можно сделать только один. Например, вот так:

cmp cl,7 jne ne_sbros xor dl,dl ne_sbros: inc dl inc di

Регистры я для себя обнулял, мне так проще в отладчике смотреть какие числа куда идут. А про «сброс», «наброс» — хорошее замечание , спасибо.

Почему в предыдущих примерах резервируется под строковую переменную количество байтов без знака $?

Вот, что у меня получилось:

use16
org 100h
jmp start

arr db ‘Telephone$’
arrz rb 10 ;резервируем под шифровку
arrd rb 10 ;резервируем под дешифровку
l db 10
pak db 13,10,'(de)coding press any key…$’
pak1 db 13,10,’out press any key…$’
start:
mov ah,09h
mov dx,arr
int 21h ;Выводим слово на экран
mov ah,9
mov dx,pak
int 21h
mov ah,8
int 21h ;Предлагаем его (де)шифровать
movzx bx,[l]
mov cx,7
sub bx,cx
mov si,cx
shifr: dec si
mov al,[arr+si]
rol al,cl ;шифруем символы с 7 по 1
mov [arrz+si],al;сохраняем первую часть шифровки
cmp bx,cx
jna men_str ;если в строке меньше 14символов, то начнем шифровать символы >8 попозже
mov ah,[arr+si+7]
rol ah,cl ;шифруем символы с 14 по 8
jmp kon
men_str: jnz con_str ;если это последний символ строки, то записываем $
mov ah,24h
kon: mov [arrz+si+7],ah ;сохраняем вторую часть шифровки

con_str: loop shifr

mov ah,09h
mov dx,arrz
int 21h ;выводим шифровку на экран
mov ah,9
mov dx,pak
int 21h
mov ah,8
int 21h ; предлагаем опять (де)шифровать

mov cx,7
mov si,cx
deshifr: dec si ;дальше тоже самое, но со сдвигом вправо
mov al,[arrz+si]
ror al,cl
mov [arrd+si],al
cmp bx,cx
jna men_str1
mov ah,[arrz+si+7]
ror ah,cl
jmp kon1
men_str1: jnz con_str1
mov ah,24h
kon1: mov [arrd+si+7],ah

con_str1: loop deshifr

mov ah,09h
mov dx,arrd
int 21h ;выводим расшифровку
mov ah,9
mov dx,pak1
int 21h
mov ah,8
int 21h

mov ax,4C00h
int 21h ; happy end

Там строка выводится по одному символу функцией 02h, поэтому конец строки не нужен.

Вообще не понял, как твоя программа работает ��
А что будет, если строка длиннее 14 символов?

Больше 14 символов в строке программа не обработает. Идея была такая — разбить строку по 7символов и обрабатывать символы 1,8;2,9;…. за один цикл, так как они кодируются одинаково. Несложно доработать программу на любую длину строки.

И почему-то русские слова не показываются на экране правильно в моей программе?

Дело в том, что при выводе на консоль используется кодировка cp866 (DOS), а строки в программе в кодировке win1251. Если набрать исходник в каком-нибудь редакторе с поддержкой кодировки дос, то будет выводиться нормально.
Например, можно набрать код в программе FAR.

я буду первый в подкатегории форума «Ассемблер в DOS» �� нуже промедаретируй мой расказ ��

Вот, написал, можно сказать, «в лоб». Два куска кода совершенно одинаковы,
отличие в одной команде. Так и не смог придумать, как избежать повтора ��

use16
org 100h
mov bx,msg ;в bx — адрес строки
xor si,si ;si указывает номер символа
counter_1:
mov cl,1 ;считает 7 символов
code_msg:
mov al,[bx+si] ;символ — в al
cmp al,0dh ;это конец строки ?
je print_msg_code ;да, напечатать
ror al,cl ;кодируем символ
mov [bx+si],al ;заменим символ на закодированный
inc si
inc cl
cmp cl,7 ;7 символов закодировали ?
jna code_msg ;нет еще, продолжим
jmp counter_1 ;да, кодируем след. группу символов
print_msg_code: ;печать закодированной строки
mov ah,09h
mov dx,bx
int 21h
;Декодирую, все то же самое, за исключением
;направления циклического сдвига:
xor si,si
counter_2:
mov cl,1
decode_msg:
mov al,[bx+si]
cmp al,0dh
je print_msg_decode
rol al,cl
mov [bx+si],al
inc si
inc cl
cmp cl,7
jna decode_msg
jmp counter_2
print_msg_decode: ;печать декодированной строки
int 21h
mov ax,4c00h ;выходим
int 21h
;——————————————————————-
msg db ‘Pochemu,esli ya pishu kirilitzey,’
db ‘vivoditsa kakaya-to abrakadabra ?’,0dh,0ah,’$’

Не стал использовать loop, т. к. счетчик уже занят под роры-ролы.

Не так уж и «в лоб» получилось. Хорошая программа и работает правильно со строкой любой длины.
LOOP использовать не обязательно. В этом упражнении проще цикл сделать командами условных переходов.

Про кириллицу я ответил на предыдущий комментарий. Дело в кодировке. Windows использует win1251 и в редакторе FASM тоже эта кодировка. А на консоль выводится в кодировке cp866. Поэтому и получается абракадабра.
Нужно исходник набрать в 866 кодировке и тогда русские буквы будут отображаться. Например, можно файл набрать в FARe.

Спасибо, тогда буду набирать код в FARе.

не обязательно можно в реестре изменить значение кодировки в консольных приложениях …
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Nls\CodePage->OEMCP поменять с 866 на 1251 и не надо прибегать к фар’у ��

как одобрят скину ссылку на форум ��

Тогда нормальные консольные приложения будут кракозябы печатать.

tak nado nastroykah prilojenia nastroit shrift na Lucida Console i sohranit kak postoianuu nastroyku i vse budet ok �� uje provereno godami )))

Избежать повтора можно, написав процедуру. Один из переданных параметров будет определять направление циклического свига. Вправо — шифрование, влево — расшифровка.

А как можно в качестве параметра передать направление сдвига ?

IgorKing, что-то я не совсем понял твое объяснение, я бы даже сказал, совсем не понял �� В виде кода можешь показать ?

Ну в смысле если значение параметра 1, то будет сдвиг влево, если 2, то вправо.
А в самой процедуре просто сравниваешь параметр с 1, если да сдвиг влево, иначе вправо. Хотя, если честно код не читал поэтому вот просто пример.

cmp [arg],1
jnz Ror
rol al,1
jmp Contine
Ror:
ror al,1
Contine:

А, ну понятно. Но так можно обойтись и без подпрограммы. Просто после шифровки строки пометить какой-нибудь байт в памяти, и потом уже смотреть: байт помечен — ага, ror обходим, используем rol(расшифровываем). Вопрос был немножко в другом, как передать в подпрограмму параметр(направление сдвига), внутри самой же подпрограммы уже не заботиться о нем.

Т.е. без проверки?
Незнаю, идея бредовая, но может быть можно в качестве параметра передавать код операции или что-то вроде…В таких вопросах нам может помочь только наш Сенсей!

Получилось! �� Правда, делал в MASMе (в нем пока поуверенней себя чувствую), вместо подпрограммы использовал макрос.

code_decode macro shft

mov bx,offset msg
xor si,si
counter:
mov cl,1
code_decode:
mov al,[bx+si]
cmp al,0dh
je print_msg
shft al,cl
mov [bx+si],al
inc si
inc cl
cmp cl,7
jna code_decode
jmp counter
print_msg:
mov ah,09h
mov dx,bx
int 21h
endm

start:
code_decode ror
code_decode rol

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

А с процедурой код получился бы меньше ��

На FASMe тоже можно написать аналогичный макрос (см. Часть 28)

Можно сделать самомодифицирующийся код.
Сначала зашифровать, потом пропатчить команду в коде, чтобы изменить направление сдвига, и снова запустить.

Но это уже извращение получается ��

Нет уж, на сегодня с меня хватит извращений, голова уже пухнет.
Спасибо, xrnd и IgorKing за толковые ответы, буду двигаться дальше ��

В общем, правильно. Но я бы сделал 0 или не ноль.
Параметр можно передать через регистр или через стек.
Например, у тебя не используется регистр DI, можно в него поместить значение, которое будет определять направление сдвига.

Как производится циклический арифметический сдвиг?

Не очень понятно, что имеется в виду. Я о таком не слышал.
При арифметическом сдвиге знаковый бит сохраняется, поэтому неясно, как он может быть ещё и циклическим? ��

Может быть, циклически сдвигаются все биты кроме старшего?
Тогда это можно сделать с помощью сдвига и логических операций:

mov al,10001111b ;AL = 10001111b sar al,1 ;Арифметический сдвиг, CF=выдвинутый бит jc set_bit and al,10111111b ;Сбросить 6-й бит jmp continue set_bit: or al,01000000b ;Установить 6-й бит continue: ;Результат AL = 11000111b

Чучуть с циклом перепутал.
Он 1-7
А у меня 1-7 ,+8элемент сдвинуть на 1 , -это как 1 цикл.
Поэтому вышил такой страный
После 9 символа нет действий , так как если цикл , не зная конца строки , просто смешение задать.
А если с конец строки , то там длинно слишком выходит

Замороченно как-то получилось.
Проще всё-таки начинать с первого символа строки. Можно сделать цикл командами перехода, а счетчик держать не в CX.

body:
mov al,byte[wor_d+di]
ror al,cl
mov byte[code_w+di],al
inc di
inc cl
cmp di,8
je cl2one
cmp di,12
je output
jmp body

cl2one:
mov cl,1
jmp body
output:
xor di,di
xor ax,ax

oloop:
mov dl,byte[wor_d+di]
mov ah,02h
int 21h
inc di
cmp di,12
jne oloop

mov dl,10
mov ah,02h
int 21h
mov dl,13
mov ah,02h
int 21h
xor di,di
oloop2:
mov dl,byte[code_w+di]
mov ah,02h
int 21h
inc di
cmp di,12
jne oloop2

mov ah,08h
int 21h
mov ax,4c00h
int 21h

Шифрование написано хорошо, но нет расшифровки строки.

я в бешенстве xrnd помоги пожалуйста почему не работает такой код:
mov [deStr+di], dl .

use16
org 100h
jmp enCode

enStr db «HeL1o asmworld and fasm!», 0
deStr db 32 dup (‘$’)
max db 7

enCode:
mov si, enStr ; адресс строки
xor di, di ; общий счетчик символов
en_loop:
xor cx, cx
mov ch, [max] ; ch = счетчик
_encode:
inc cl ; cl = для сдвига
mov dl, [si]
cmp dl, 0 ; конец строки
je Exit_encode ; подобие break
ror dl, cl
inc si
mov [deStr+di], dl ; ОШИБКА не записывает
inc di ; общий счетчик символов
dec ch
jnz _encode
jmp en_loop

; вывод закодированой строки
mov ah, 2
mov si, deStr-1

pr_deStr:
inc si
mov dl, [si]
cmp dl, ‘$’
jz Exit
int 21h
jmp pr_deStr

Exit:
mov ah, 8
int 21h

mov ax, 4c00h
int 21h

программа еще не закончена … ;((

;))) оказывается я написал баг программу )) ‘H’ ror 1 == ‘$’ )))
поэтому на выводе строки — первый же символ заканчивал мою программу )) , блин придется менять

Ура почти все работает. �� не могу тока понять почему не выводится строка
0ah, 0dh, ‘Decoded: $’ а так все работает как швейцарские часы, можно даже зашифрованые сообщения в пентагон посылать )))

use16
org 100h
jmp enCode

source db «HeL1o asmworld and fasm!», 0
enStr db 32 dup (0)
deStr db 32 dup (0)
max db 7
str1 db «Encoded: $»
str2 db 0dh, 0ah, «Decoded: $»

mov si, source ; адресс строки
xor di, di ; общий счетчик символов
en_loop:
xor cx, cx
mov ch, [max] ; ch = счетчик
_encode:
inc cl ; cl = для сдвига
mov dl, [si]
cmp dl, 0 ; конец строки
je enCode_print ; подобие break
ror dl, cl
inc si
mov [enStr+di], dl
inc di ; общий счетчик символов
dec ch
jnz _encode
jmp en_loop

mov si, enStr ; адресс закодированой строки
xor di, di ; общий счетчик символов
de_loop:
xor cx, cx
mov ch, [max] ; ch = счетчик
_decode:
inc cl
mov dl, [si]
cmp dl, 0
jz deCode_print ; вывод декодированой строки
rol dl, cl
mov [deStr+di], dl
inc si
inc di
dec ch
jnz _decode
jmp de_loop

;—[вывод закодированой строки]——
enCode_print: ;

mov ah, 9
mov dx, str1
int 21h
mov ah, 2
mov si, enStr-1

lp_enStr:
inc si
mov dl, [si]
cmp dl, 0
jz deCode ; декодируем и выводим оригинал
int 21h
jmp lp_enStr

mov ah, 9
mov dx, str2
int 21
mov ah, 2
mov si, deStr-1

lp_deStr:
inc si
mov dl, [si]
cmp dl, 0
jz Exit
int 21h
jmp lp_deStr

Exit:
mov ah, 8
int 21h
mov ax, 4c00h
int 21h

mov dx, str2 int 21

Должно быть 21h, иначе это другое прерывание.

;шифровка строки по алгоритму циклического сдвига каждого байта
;строки на количество битов равное индексу кодируемого байта
;с основанием 7, т.е. 1-7,1-7.
use16
org 100h
jmp main
;=================================;
strDecode db ‘Пример зашифровываемой строки’,0
strBR db 10,13,0
;=================================;

;=================================;
main:
mov bx,strDecode ;
mov ax,bx ;
call proc_strOut ;
mov bx,strBR ;
call proc_strOut ;выводим начальную строку
mov bx,ax
xor ax,ax ;
call proc_strDE ;шифруем строку, выводим
mov ah,1 ;
call proc_strDE ;дешифруем строку, выводим
;———————————;
exit:
mov ax,4c00h
int 21h
;=================================;

;=================================;
;кодируем/декодируем строку
;bx — указатель на строку
;ah — флаг работы подпрограммы (0 — шифруем, 1 — дешифруем)
proc_strDE:
xor cx,cx
mov si,cx
get_char:
inc cl
js slide_more7 ;если счетчик сдвига равен 8
slide:
mov al,[bx+si] ;получаем текущий символ
cmp al,0
je display_string ;если конец строки, выводим на экран
cmp ah,0 ;проверяем флаг работы подпрограммы
jne decode
ror al,cl ;шифруем, если ah=0
jmp short end_de_char
decode:
rol al,cl ;дешифруем, если ah0
end_de_char:
mov [bx+si],al
inc si
jmp short get_char
slide_more7:
mov cx,1 ;начинаем счетчик сдвига сначала
jmp short slide
display_string:
mov ax,bx ;
call proc_strOut ;
mov bx,strBR ;
call proc_strOut ;отображаем строку
mov bx,ax
proc_strDE_end:
ret
;=================================;

;=================================;
;посимвольный вывод кириллической строки на экран (0 — завершающий символ)
;bx — указатель на строку
proc_strOut:
push ax
push dx
push si
mov si,-1
proc_strOut_getChar:
inc si mov al,[bx+si]
cmp al,0xf0
jb proc_strOut_charLessF0
and al,0xef
jmp short proc_strOut_displayChar
proc_strOut_charLessF0:
cmp al,0xef
ja proc_strOut_displayChar
cmp al,0xc0
jnb proc_strOut_charC0EF
cmp al,0
je proc_strOut_end
jmp short proc_strOut_displayChar
proc_strOut_charC0EF:
and al,0xbf
proc_strOut_displayChar:
mov ah,02h
mov dl,al
int 21h
jmp short proc_strOut_getChar
proc_strOut_end:
pop si
pop dx
pop ax
ret
;=================================;

В принципе всё хорошо.
Вроде есть одна ошибка:

inc cl js slide_more7 ;если счетчик сдвига равен 8

Переход по флагу SF будет выполнен, если старший бит равен 1, то есть CL=128 и больше.

Очень интересная процедура proc_strOut. Я так понял, она преобразует кодировку?
Есть более простой способ — сделать табличку из 256 байт и заменять байты по этой таблице (существует даже специальная команда XLAT)

Да, надо вместо js вставить cmp cl,8 и jb.
Опять тетраду с байтом перепутал ��
а xlat моя пока не знать ��

17. Циклический сдвиг в ассемблере

Статья основана на материале xrnd с сайта asmworld (из учебного курса по программированию на ассемблер 16-битного процессора 8086 под DOS).

Циклический сдвиг отличается от линейного тем, что выдвигаемые с одного конца биты вдвигаются с другой стороны, то есть движутся по кольцу. В процессора x86 существует 2 вида циклического сдвига: простой и через флаг переноса ( CF ). У всех команд, рассматриваемых в этой части учебного курса, по 2 операнда, таких же, как у команд линейного сдвига. Первый операнд — сдвигаемое значение и место для записи результата. Второй операнд — счётчик сдвигов, который может находится в регистре CL или указываться непосредственно.

Простой циклический сдвиг

Циклический сдвиг вправо выполняется командой ROR , а влево — командой ROL . Схема работы этих команд представлена на рисунке (на примере 8-битного операнда):

Значение последнего выдвигаемого бита копируется в флаг CF . Для сдвигов на 1 бит устанавливается флаг OF , если в результате сдвига изменяется знаковый бит операнда. (Честно говоря, не помню, чтобы я этим когда-то пользовался. Но может вам пригодится ? ) Примеры использования команд:

Циклический сдвиг через флаг переноса

Отличие от простого циклического сдвига в том, что флаг CF участвует в сдвиге наравне с битами операнда. При сдвиге на 1 бит выдвигаемый бит помещается в CF , а значение CF вдвигается в операнд с другой стороны. При сдвиге на несколько бит эта операция повторяется многократно. Циклический сдвиг через флаг переноса выполняется командами RCR (вправо) и RCL (влево).

Опять же для сдвигов на 1 бит устанавливается флаг OF , если в результате сдвига изменяется знаковый бит операнда. Примеры использования команд:

Пример программы

В качестве примера напишем программу для подсчёта единичных битов в байте. Для анализа битов используется команда ROL в цикле. Цикл выполняется 8 раз — по числу битов в байте. Если очередной бит равен 1, то выполняем инкремент счётчика единичных битов.

Эту программу можно немного оптимизировать. Во-первых, использовать вместо условного перехода команду ADC со нулевым вторым операндом. Это позволит прибавить 1, если CF=1 и прибавить 0, если CF=0 . Во-вторых, считать единичные биты можно в регистре AH , а обнулить его в начале с помощью команды MOVZX , совместив с загрузкой байта в регистр AL . Ноль для команды ADC можно взять в регистре CH , это делает команду короче и быстрее, чем при использовании непосредственного операнда. CH равен 0 во время выполнения цикла, так как CX изменяется от 8 до 0. Вот что получилось в итоге:

Всего 6 команд для подсчета битов в байте. А попробуйте написать то же самое на языке высокого уровня ?

Упражнение

Объявите в программе строку. Длина строки должна быть больше 8 символов и храниться в байте без знака. Напишите цикл для шифрования строки по алгоритму: первый символ циклически сдвигается вправо на 1 бит, второй символ — на 2 бита, …, 7-й — на 7 битов, 8-й — снова на 1 бит, 9-й на 2 бита и т.д. Затем напишите цикл для расшифровки строки и выведите её на экран.

Name already in use

Команды сдвига позволяют выполнять действия над отдельными битами операндов. Все команды сдвига перемещают биты в поле операндов влево или вправо. При выполнении команд сдвига флаг CF всегда содержит значение последнего выдвинутого бита. Рассмотрим следующие команды сдвига:

shr операнд, счетчик_сдвигов ; логический (беззнаковый) сдвиг вправо

shl операнд, счетчик_сдвигов ; логический (беззнаковый) сдвиг влево

В следующем примере команда shr сдвигает содержимое регистра al вправо на 1 бит. Выдвинутый в результате один бит попадает в флаг CF , а самый левый бит регистра al заполняется нулем.

При сдвигах влево правые биты заполняются нулями.

Сдвиг влево на один двоичный разряд часто используется для удваивания чисел, а сдвиг на один разряд вправо — для деления на 2. Эти операции осуществляются значительно быстрее, чем команды умножения или деления.

Количество позиций, на которые должен быть выполнен сдвиг, задаются вторым аргументом инструкции сдвига, и может быть либо задано константой, либо регистром cl : например, shl ax, 12 или shl eax, cl .

Команды циклического сдвига

Циклический сдвиг представляет собой операцию сдвига, при которой бит, выдвинутый с одного «конца» числа, занимает освободившийся разряд с другой стороны этого же числа. Существуют следующие команды циклического сдвига:

ror ; Циклический сдвиг вправо

rol ; Циклический сдвиг влево

rcr ; Циклический сдвиг вправо с переносом

rcl ; Циклический сдвиг влево с переносом

В командах rcr и rcl в сдвиге участвует флаг CF . Выдвигаемый из регистра бит заносится в флаг CF , а прежнее значение CF при этом поступает в освободившуюся позицию в регистре.

Команды работы с битами операндов

Команды and , or , xor и test являются командами логических операций. Эти команды имеют два операнда и используются для сброса, установки и проверки бит. Размерность операндов должна быть одинакова. Например, если размерность операндов равна слову (16 бит), то логическая операция выполняется сначала над нулевыми битами операндов, и далее над всеми битами с первого по пятнадцатый. Результат записывается на место первого операнда. Исключение составляет команда test . Команда test действует аналогично команде and , но результат не записывает на место первого операнда, а устанавливает только флаги во флаговом регистре. Это дает возможность анализировать отдельные биты не изменяя операнд.

Таблица истинности для and or xor
Значение операнда 1 0101 0101 0101
Значение операнда 2 0011 0011 0011
Результат операции 0001 0111 0110

Команда or : Если хотя бы один из сравниваемых битов равен 1, то результат равен 1; если сравниваемые биты равны 0, то результат — 0. Команда and : Если оба из сравниваемых битов равны 1, то результат равен 1; во всех остальных случаях результат — 0. Команда xor : Если один из сравниваемых битов равен 0,а другой 1, то результат равен 1; если сравниваемые биты одинаковы (оба — 0 или оба — 1) то результат — 0.

Будем рассматривать байтную переменную flags как восемь независимых флагов, которые необходимо сбрасывать, устанавливать и анализировать в какой-либо программе. Для этого в правый операнд заносим «маску» в двоичной системе счисления. Рассмотрим пример установки второго бита с использованием команды or :

Сбросить второй бит можно командой and , но маска должна быть инверсная:

Для того чтобы проверить установлен ли нужный бит, применяется команда test :

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

Еще одна логическая команда not устанавливает обратное значение бит в байте или в слове, в регистре или в памяти: нули становятся единицами, а единицы — нулями. Если, например, регистр al содержит 1100 0101, то команда not al изменяет это значение на 0011 1010.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *