| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- ;
- ; queens app — by.avolver'11
- ; distributed under BEERWARE LICENSE
- ;
- ; @todo
- ; - Реализовать многопоточность...
- ; Fork работает, однако общее адресное пространство вносит долю хаоса в картину.
- ; Для начала нужно решить проблему выделения памяти и давать каждому потоку свой массив.
- ; Затем сохранить в регистре каждого потока адрес на его массив и параметры. Вероятно это будет r15. Счётчик же положить в память каждого потока.
- ; - Динамеческое выделение памяти. sys_brk не хочет работать. (или -1 в rcx ничего не значит?)
- ; Файл: /usr/src/linux/arch/x86/kernel/entry_64.S, строка: 422
- ; - Использовать нулевую ячейку массива ферзей, дабы немного сократить количество инструкций.
- ; - Сделать проверку на разрешённый диапазон N [5..256] и введённые в параметры буквы.
- ; - Вывод в JSON
- ; - Добавить функцию DumpDesk
- ; - Вывод времени работы в мс.
- ; - Порт под Windows. Используем команды препроцессора: IS_LINUX = 1; if IS_LINUX ... end if
- ; - Отобразить примерное время завершения.
- format ELF64 executable at 0000000100000000h
- segment readable executable
- appStart:
- entry $
- pop rax ; В стеке лежат параметры командной строки. По восемь байт на адрес параметра. Первый — количество;
- cmp rax, 1
- jbe fShowUsage
- pop rax ; Второй — название программы;
- pop rsi ; Третий — первый аргумент;
- call toDec
- cmp rax, 0
- je fShowUsage
- mov [iTotalLayers], rax
- mov r9, rax
- mov rbx, rax
- mov rax, 8
- mul rbx
- ;mov rcx, rax
- ;mov rax, 12 ; Запрос на расширение памяти. Syscall 12 — sys_brk. В rdi — адрес на желаемую границу сегмента данных.
- ;xor ebx, ebx
- ;syscall
- ;add rax, rcx
- ;mov rdi, rax
- ;xor rdi, rdi
- ;syscall
-
- mov r8, 1 ; Текущий уровень
- mov r15, 0 ; Счётчик совпадений
-
- mainLoop:
- lea rsi, [aQueens]
- mov rbx, r8
- mov rax, 8
- mul rbx
- add rsi, rax
- mov rax, [rsi] ; Получаем значение ферзя для текущего уровня
- cmp rax, r9
- jbe DoCheckPaths ; Если это значение больше максимального уровня
- lea rsi, [aQueens]
- add rsi, 8
- mov rax, [rsi]
- mov rbx, r9
- inc rbx
- cmp rax, rbx
- jne DoCont ; и если позиция ферзя первого уровня не вылезла за грани максимального положения
- jmp DoCheckPaths
- DoCont:
- lea rsi, [aQueens]
- mov rbx, r8
- mov rax, 8
- mul rbx
- add rsi, rax
- mov rax, 1
- mov [rsi], rax ; aQueens[iLevel] = 1 : Сбрасываем позицию текущего уровня в начальное положение
- dec r8 ; iLevel-- : Переходим на уровень ниже.
- sub rsi, 8
- mov rax, [rsi]
- inc rax
- mov [rsi], rax ; aQueens[iLevel]++ : На предыдущем уровне сдвигаемся направо
- jmp mainLoop
- DoCheckPaths:
- call CheckPaths
- cmp rdx, 1 ; Проверяем, свободны ли поля.
- je IncLevel
- inc r8
- cmp r8, r9
- jbe mainLoop
- lea rsi, [aQueens] ; Может это конец?
- add rsi, 8
- mov rax, [rsi]
- cmp rax, r9
- ja breakLoop ; Да? Брякаем цикл.
- inc r15 ; iCount++ : Ура, мы нашли решение!
- call fDumpSolution ; Выводим решение
- dec r8 ; iLevel--
- lea rsi, [aQueens]
- mov rbx, r8
- mov rax, 8
- mul rbx
- add rsi, rax
- mov rax, [rsi]
- inc rax
- mov [rsi], rax ; aQueens[iLevel]++;
- jmp mainLoop
- IncLevel:
- lea rsi, [aQueens]
- mov rbx, r8
- mov rax, 8
- mul rbx
- add rsi, rax
- mov rax, [rsi]
- inc rax
- mov [rsi], rax
- jmp mainLoop
- breakLoop:
- lea rsi, [sStatisticArea]
- mov rax, r15
- call fromDec
- mov rdx, sStatisticArea - sStatistics
- add rdx, rbx
- add rsi, rbx
- mov rcx, 0x0A
- mov [rsi], rcx
- inc rdx
- lea rsi, [sStatistics]
- mov edi, 1
- mov eax, 1
- syscall
-
- xor rax, rax
- xor edi, edi ; Выходим из себя. Syscall 60 — sys_exit
- mov eax, 60
- syscall
-
- ; Функция для проверки свободных путей.
- ; Возвращает: rdx = 1, если на пути встречается ферзь; либо rdx = 0, если дорога пуста.
- ; r10 — iTargetLayer; r11 — iVectorCenter; r12 — iVectorLeft; r13 — iVectorRight; r14 — iEnemyX
- CheckPaths:
- cmp r8, 1
- jne ChPathsP1
- mov rdx, 0
- ret
- ChPathsP1:
- mov r10, r8 ; iTargetLayer = iLevel
- lea rsi, [aQueens]
- mov rbx, r8
- mov rax, 8
- mul rbx
- add rsi, rax
- mov r11, [rsi] ; iVectorCenter, iVectorLeft, iVectorRight = aQueens[iLevel]
- mov r12, r11
- mov r13, r11
- ChPathsLoop:
- dec r10
- cmp r10, 0
- mov rdx, 0
- jbe ChPathsEnd
- lea rsi, [aQueens]
- mov rbx, r10
- mov rax, 8
- mul rbx
- add rsi, rax
- mov r14, [rsi] ; iEnemyX = aQueens[iTargetLevel]
- cmp r11, r14 ; Если iVectorCenter = iEnemyX, то сохраняем в rdx:1 и вываливаемся.
- jne ChPathNoCenter
- mov rdx, 1
- ret
- ChPathNoCenter:
- cmp r12, 0
- jbe ChPathNoLeft
- dec r12 ; Уменьшаем iVectorLeft
- cmp r12, r14 ; Если iVectorLeft = iEnemyX, то сохраняем в rdx:1 и возвращаемся.
- jne ChPathNoLeft
- mov rdx, 1
- ret
- ChPathNoLeft:
- cmp r13, r9
- ja ChPathsLoop
- inc r13 ; Увеличиваем iVectorRight
- cmp r13, r14 ; Если iVectorRight = iEnemyX, то сохраняем в rdx:1 и выпадаем.
- jne ChPathsLoop
- mov rdx, 1
- ret
- ChPathsEnd:
- ret
- ; Функция вывода решения.
- fDumpSolution:
- mov r11, 1
- mov r12, 1
- lea rdx, [aQueens]
- lea rsi, [tBackBuffer]
- mov rax, r15
- call fromDec
- add rsi, rbx
- add r12, rbx
- mov rax, 0x03A
- mov [rsi], rax
- inc rsi
- inc r12
- mov rax, 0x020
- mov [rsi], rax
- inc rsi
- inc r12
- mov rcx, r9
- fDSLoop:
- mov rax, r11
- call fromDec
- add rsi, rbx
- add r12, rbx
- mov rax, 0x03A
- mov [rsi], rax
- inc rsi
- inc r12
- add rdx, 8
- mov rax, [rdx]
- call fromDec
- inc r11
- add rsi, rbx
- add r12, rbx
- mov rax, 0x02C
- mov [rsi], rax
- inc rsi
- inc r12
- mov rax, 0x020
- mov [rsi], rax
- inc rsi
- inc r12
- loop fDSLoop
- dec rsi
- dec rsi
- dec r12
- dec r12
- mov rdx, 0x0A
- mov [rsi], rdx
- mov rdx, r12
- lea rsi, [tBackBuffer]
- mov edi, 1
- mov eax, 1
- syscall
- ret
- ; Отображение сообщения об использовании.
- fShowUsage:
- mov edx, iUsageSize
- lea rsi, [sUsage]
- mov edi, 1
- mov eax, 1 ; Выводим сообщение об использовании. Syscall 1 — sys_write: rsi — адрес на строку, edx — длинна, edi — stdout.
- syscall
- xor edi, edi ; Выходим из программы. Syscall 60 — sys_exit: edi — код возврата.
- mov eax, 60
- syscall
-
- fAllocationError:
- mov edx, iAllProSize
- lea rsi, [sAllocationProblem]
- mov edi, 1
- mov eax, 1
- syscall
- xor edi, edi
- mov eax, 60
- syscall
- ; Функция для перевода ASCII строки в число.
- ; rsi - адрес на строку
- ; rax - число результата
- ; Смотрим первую букву, если она — цифра — прибавляем её к rax. Если следующий байт не 0x0 — умножаем rax на десять и прибавляем следующую цифру.
- toDec:
- xor rax, rax
- mov cl, [rsi]
- toDecEnt:
- sub cl, 48
- jc toDecInc
- cmp cl, 10
- jae toDecInc
- add rax, rcx
- toDecInc:
- inc rsi
- mov cl, [rsi]
- cmp cl, 0
- je toDecEnd
- mov rbx, 10
- mul rbx
- jmp toDecEnt
- toDecEnd:
- ret
- ; Функция для обратного перевода числа в ASCII строку.
- ; rsi - адрес на временную строку
- ; rax - число, которое необходимо преобразовать
- ; rbx - длина строки
- ; Делим исходное число на десять — остаток записываем в ASCII-цифру.
- ; Если исходное число становится меньше десяти, то добавляем его как ASCII-последнюю цифру в поток.
- fromDec:
- push rcx
- push rdx
- push rdi
- xor rdi, rdi
- mov rcx, 10
- push rsi
- push rsi
- lea rsi, [sFromDecTemp]
- add rsi, 10
- fromDecEnt:
- inc rdi
- cmp rax, 10 ; Если rax < 10, то прыгнуть в конец
- jb fromDecEnd
- xor rdx, rdx ; Очищаем регистр для частного
- div rcx ; rcx = rax / rcx => rdx
- or dl, 0x30
- mov [rsi], dl
- dec rsi
- jmp fromDecEnt
- fromDecEnd:
- or al, 0x30
- mov [rsi], al
- mov rbx, rdi ; Копируем строку на первый байт необходимого адреса.
- mov rcx, rdi
- pop rdi
- cld
- rep movsb
- inc rdi
- mov ah, 0 ; Добавляя в конец нулевой стоп-символ
- mov [rdi], ah
- pop rsi
- pop rdi
- pop rdx
- pop rcx
- ret
-
- appEnd:
- segment readable writeable
- dataStart:
- iTotalLayers dq ?
- sTempPlaceForInt db 11 dup('b')
- sFromDecTemp db 11 dup('a')
- sUsage db 'Queens application', 0x0A
- db 'Finds a solution for the "eight queens puzzle"', 0x0A, 0x0A
- db 'Usage: queens <depth level>', 0x0A
- iUsageSize = $ - sUsage
- sStatistics db 'Number of solutions: '
- sStatisticArea db 11 dup(0)
- sAllocationProblem db 'Memory allocation error, sorry...', 0x0A
- iAllProSize = $ - sAllocationProblem
- aQueens:
- times 256 dq 1 ; Массив, собственно, ферзей
- tBackBuffer:
- times 3 db ? ; Дельта размера, дабы забить остатки бинарника до 4096 байт.
- times (4096 - (appEnd - appStart) - (tBackBuffer - dataStart) - 176) / 8 db '6dreams ' ; Great_&_be-e-e-eg backbuffer. (%
|