Седов Иван Алексеевич
Рязанский политехнический колледж
ASSEMBLER 8-bit SIMULATOR
Занятие #8: создание процедуры
сортировка массива по возрастанию
https://e1m7.github.io/work/
Последнее занятие по ассемблеру будет посвящено сложной и интересной процедуре сортировки одномерного массива по возрастанию. Для решения этой задачи мы напишем несколько вспомогательных процедур, которые позволят решить эту задачу за пару шагов
Для простоты мы договоримся, что массив это набор байт, каждый из которых является числом от 0 до 9. Можно задавать полные байты (0-255), но выводить массив на печать будет не удобно. (Однако, в оперативной памяти массив упорядочится верно.)
Суть программы будет такова:
1) Получаем адрес массива (байты 48-57), на конце '#' (код 35)
2) Находим длину массива (нужна для сортировки)
3) Выводим массив на экран (экран: 2 1 3)
4) Сортируем по возрастанию (магия)
5) Выводим массив на экран (экран: 1 2 3)
array: DB 2
DB 1
DB 3
DB '#'
; 2 1 3 (до сортировки)
; 1 2 3 (после сортировки)array_print:
; input
; C = array of number, '#'
PUSH A
PUSH C
PUSH D
MOV D, 232 ; D=232 (настройка на байты вывода)
.loop100:
MOV A, [C] ; A=байт, адрес которого в C
ADD A, '0' ; A=A+'0'=значение байта + 48
CMP A, 83 ; Сравнение: A=83? 83=35(#)+48(0)
JE .exit101 ; (да) переход на .exit101
MOV [D], A ; (нет) байт, адрес которого в D=A
INC D ; D=D+1
INC C ; C=C+1
JMP .loop100
.exit101:
POP D
POP C
POP A
RETВопрос: а зачем нам нужна процедура array_length? Да, она найдет нам длину массива (допустим, он состоит из 6 элементов). Но давайте разберемся...
Если мы будем брать текущий индекс элемента и сравнивать значение текущего элемента и значение следующего элемента, то какой индекс будет для нас "остановочным"?
Если на 6-ом месте (под индексом 5) стоит минимальное число (в данном случае 1), то сколько проходов-сравнений надо будет сделать, чтобы она встала на 1-ое место (под индексом 0)?
arr: DB 6, 5, 4, 3, 2, 1, #
; 0 1 2 3 4 5 6array_length:
; input
; C = array of number, '#'
; output
; A = length array
PUSH B
PUSH C
XOR A, A ; A=0 (XOR 11=>0, 10=>1, 01=>1, 00=>0)
.loop110:
MOV B, [C] ; B=байт, адрес которого в С
CMP B, 35 ; Сравнение: B=35? (это решетка?)
JE .exit111 ; (да) перейти на выход
INC A ; (нет) A=A+1 (накопить счетчик)
INC C ; С=С+1 (перейти на следующий элемент)
JMP .loop110
.exit111:
DEC A ; A=A-1 (нам нужно именно это число)
POP C
POP B
RETПроцедура array_sort оказалась длинной (вся программа 173 байта, 130 строк кода), записывать ее лучше сразу с основной программой (надо будет добавить две ранее записанные процедуры array_print и array_length в самый низ).
JMP start
arr: DB 6
DB 5
DB 4
DB 3
DB 2
DB 1
DB '#'
len: DB 0start:
MOV C, arr
CALL array_length
MOV [len], A
CALL array_print ; Вывод массива
CALL array_sort ; Сортировка
CALL array_print ; Вывод массива
HLT
array_sort:
; input
; C = array of number, '#'
; len = length - 1
PUSH A
PUSH B
PUSH C
PUSH D ; Внешний цикл
MOV C, 0
.loop600:
CMP C, [len]
JE .exit601
; Внутренний цикл
MOV D, 0
.loop602:
CMP D, [len]
JE .exit602
; Извлечение чисел
MOV A, arr ; A=arr (адрес)
ADD A, D ; A=A+D (адрес + смещение)
MOV B, [A] ; B=по адресу A (1-ое число)
PUSH B ; 1 число => в стек
MOV B, [A+1] ; B=по адресу A+1 (2-ое число)
POP A ; 1 число <= из стека ; A=1-ое число (текущее)
; B=2-ое число (текущее+1)
; Сравнение чисел
CMP A, B ; Сравнить: A>B?
JA .change666 ; (да) переход .change666
JMP .exit666 ; (нет) переход .exit666
; Обмен чисел
.change666:
PUSH A ; 1 => стек
PUSH B ; 2 => стек (2,1)
MOV A, arr ; A=arr (адрес)
ADD A, D ; A=A+D (адрес + смещение)
POP B ; B=2-ое число
MOV [A], B ; Адрес 1 = 2 число
POP B ; B=1-ое число
MOV [A+1], B ; Адрес 2 = 1 число .exit666:
INC D
JMP .loop602
; Внутренний цикл
.exit602:
INC C
JMP .loop600
; Внешний цикл
.exit601:
POP D
POP C
POP B
POP A
RETprint_number
print_char
remdiv10
В нашем распоряжении есть процедуры: