Дорогие друзья, эта статья посвящена всем кто помнит что такое Dos, видео режим 80×25 и прочие «замечательные мелочи» олд скульного разработчика приложений. А так же кому интересно с этим познакомиться.
Далее следует предыстория этого рассказа. В начале двухтысячных годов, в мою бытность студентом, появился у меня замечательный девайс (карманный персональный компьютер): Compaq iPAQ H3800 с установленной операционной системой Microsoft Windows Pocket PC 2002. А так же имелись в наличии длинные, темные вечера зимних каникул в деревне, не имея при себе ни интернета, ни персонального компьютера в данном месте. А только сей девайс и большое желание студента размять мозговые клетки не банальными кросс/сканвордами а чем то поинтереснее. Признаюсь, перед отправлением в деревню на это устройство мною был установлен эмулятор операционной системы Dos под названием PocketDOS, а так же взята с собой в те времена являющаяся моей «настольной книгой», это замечательное произведение автора Зубкова С.В., под названием: «Assembler для Dos, Windows и UNIX». Плюс мне пригодились: стилус и стандартный блокнот операционной системы Windwos Pocket PC.
(На данный момент конечно же будет уместней использовать TASM в эмуляторе DosBox для настольных PC).
Впрочем, результаты недавнего опроса «На каком языке вы бы написали Матрицу?» подтверждают мои догадки о том, что визуализировать матрицу лучше на том же языке программирования что и её писать.
Итак, начнем же разгадывать кроссворд «The Matrix» на языке Assembler, под процессор i80186, в операционной системе Dos с помощью компилятор и линковщика Turbo Assembler (далее TASM) от компании Borland, на устройстве iPAQ H3800.
Как известно процессор i80186 является 16 битным, соответственно регистры данного процессора имеют размер 16 бит (например регистр аккумулятор AX) и делятся на две части по 8 бит (AH — старшая часть и AL — младшая). Так же нам понадобятся прерывания BIOS: 10h и 16h, которые будут управлять видеорежимом и клавиатурой соответственно. И прерывание Dos под номером 21h, необходимое нам в данном случае для вывода строки на дисплей и корректного завершения программы.
Чтобы отображать матрицу нам достаточно небольшого объема оперативной памяти, поэтому мы будем использовать формат исполняемого файла – .com и модель памяти tiny. Как написано у классиков ассемблера в данном случае установим размер стека равным 100h.
.model tiny ; модель памяти крохотного размера .186 ;для процессора i80186 .code ; начало единственной секции, секции кода org 100h ; размер стека
Далее следует память в которой мы объявляем переменные и константы для нашего кода, и сразу перепрыгиваем эту часть памяти чтобы не выполнять её:
main: jmp start ; описание некоторых констант говорящих о размере экрана вывода матрицы W equ 1Fh H equ 18h OFFS equ 04h cells db W + OFFS dup(0) c_n db W + OFFS dup(0) bsymb db 0 _sleep dw 1fffh ; задержка вывода каждого символа матрицы на экране the_matrix db "T H E M A T R I X" ; да это она, надпись матрица the_matrix_len equ $ - the_matrix ; длина текста , символ $ говорит о текущем положении строки кода msg_end db 0ah, 0dh, "Follow the white rabbit.", 0ah, 0dh, "$" timer dw 04ffh ; время отображения названия программы
Итак, мы добрались до момента когда нужно указать видео режим дисплея нашей ЭВМ в котором мы будем выводить символы матрицы. Но сначала сохраним текущий режим в стек чтобы в конце к нему вернуться.
mov ax, 0f00h ; считать текущий видеорежим int 10h push ax ; результат сохраним в стек
Выставляем видеорежим: текст CGA,EGA 80×25:
mov ax, 0003h int 10h
Далее идет просто огромный кусок кода в котором происходят невероятные магические преобразования:
- Определение позиции символа на экране который необходимо затереть
- Процедура перемещения курсора и затирание символа
- Определение позиции вывода символа на экран
- Генерация символа из таблицы ASCII
- Вывод его с определенным цветом
- Цикл с задержкой между выводом следующего символа
- Определение истекшего значения таймера вывода надписи «T H E M A T R I X»
- Вывод надписи «T H E M A T R I X»
main_loop: imul dx, 4E35h inc dx push dx and dh, W add dh, OFFS shr dx, 08h mov bx, dx lea di, cells add byte ptr [di+ bx], 1 mov dh, byte ptr [di+ bx] cmp dh, H jne next1 mov byte ptr [di+ bx], 0 next1: cmp dh, 0 je draw1 dec dh ; номер строки mov bh, 00h mov ah, 02h ; установить позицию курсора int 10h mov ah, 09h ; писать символ/атрибут в текущей позиции курсора mov al, bsymb ; записываемый символ mov bx, 0002h ; номер видео страницы, цвет зеленый mov cx, 01h ;счетчик (сколько экземпляров символа записать) int 10h draw1: inc dh mov bh, 00h mov ah, 02h int 10h mov ah, 09h ; писать символ/атрибут в текущей позиции курсора pop dx imul dx, 4E35h inc dx push dx mov bsymb, dh ; сгенерированный символ mov al, bsymb mov bx, 000Ah mov cx, 01h int 10h ;... pop dx imul dx, 4E35h inc dx push dx and dh, W add dh, OFFS shr dx, 08h mov bx, dx lea di, c_n add byte ptr [di+ bx], 01h mov dh, byte ptr [di+ bx] cmp dh, H + 01h jne next2 mov byte ptr [di+ bx], 00h next2: dec dh mov bh, 00h mov ah, 2 int 10h mov ah, 09h ; писать символ/атрибут в текущей позиции курсора mov al, 08h ; номер символа mov bh, 00h ; номер страницы mov bl, 00h ; видео атрибут (текст) или цвет (графика) mov cx, 0001h ; один символ int 10h ; sleep cycle mov cx, _sleep loop2: nop loop loop2 ;draw message cmp timer, 0 je no_msg dec timer mov ax, 1300h ; писать строку mov ch, 00h mov cl, the_matrix_len mov bh, 00h ; номер страницы mov bl, 20h ; цвет mov dl, 0Bh ; позиция (колонка) mov dh, 0Bh ; позиция (строка) lea bp, the_matrix ; выводимая строка int 10h inc dh
И, наконец, нам необходимо получить сигнал о том что мы больше не хотим смотреть на матрицу и уже можем проследовать за белым кроликом. Для этого мы используем проверку нажатия клавиши клавиатуры и если символ нажат не был, то продолжаем вывод символов матрицы:
mov ah, 01h int 16h pop dx jz loop1 jmp end_prog loop1: jmp main_loop
Символ нажат и мы закрываем программу, но сначала восстановим предыдущий видеорежим:
end_prog: pop ax ; восстановить видеорежим из стека mov ah, 00h mov bh, 00h int 10h
Следуем за белым кроликом:
mov ah, 09h ; выдать строку на дисплей lea dx, msg_end ; адрес строки, заканчивающейся символом '$' (ASCII 24H) int 21h
Заканчиваем программу корректно:
mov ah, 4ch mov al, 00h ; код выхода int 21h end main
Если слепить все эти кусочки кода вместе, то скомпилировать и слинковать можно с помощью команд:
tasm /t /z matrix tlink /t matrix
В итоге вот что мы имеем:
- Вывод вертикально скатывающихся символов целеным цветом
- Вывод надписи «The Matrix» по центру с исчезновением через некоторое время
- Ожидание символа с клавиатуры для прерывания вывода и завершения ропграммы
- Скомпилированную версию приложения размером 357 байт
ссылка на оригинал статьи http://habrahabr.ru/post/198106/
Добавить комментарий