1) Вывод в текущее положение курсора символ #.
2) Этот же символ выводит в позицию выше, ниже, левее или правее текущего символа, в зависимости от нажатия клавиш “8”, “2”, “4”, “6” на цифровой клавиатуре.
3) Задержка между выводом каждого символа определяется нажатием цифровой клавиши, следующим способом: введённую цифру умножить на 2^9, это и будет число повторений цикла задержки. Для анализа нажатия клавиши использовать вектор 1Ch.
data segment
DIRECT db 1 ; направление перемещения
EXIT db 0 ; выход
SYM db "#" ; символ, выводимый на экран
ATRIBUT1 db 14 ; атрибут символа (жёлтый)
ATRIBUT2 db 10 ; атрибут символа (зелёный)
POS dw 0000 ; позиция начального вывода символа
OLD_CS dw ? ; адрес сегмента старого вектора 1Сh
OLD_IP dw ? ; адрес смещения старого вектора 1Сh
data ends
code segment
assume cs:code, ds:data
;наше новое прерывание 1CH,которое запишется в обработчик:-)
NEW_1C proc far
push ax ; сохранить
push bx ;все
push cx ;регистры
push dx ;не юзаем pusha на процессор не поддерживает эту инструкцию Т_Т
push ds ;ибо по словам транслятора,
push es ;наш процессор не поддерживает эту инструкцию Т_Т
mov ax, DATA ; установить ds на сегмент данных
mov ds, ax ; основной программы
mov ax, 40h ; установить es на
mov es, ax ; сегмент данных bios
mov ax, es:[1ch] ;пишем в регистр голову буфера клавы
mov bx, es:[1ah] ;в другой пишем хвост буфера клавы
cmp bx , ax ;если они равны,буфер пуст
jne m5 ;а если не равны...на м5
jmp back ;если равны,то на бэк..
m5: mov al, es:[bx] ;заливаем в al символ находящийся в голове
mov es:[1ch], bx ;заливаем в хвост голову
cmp al, 30h ;нажатая клавиша 0?
jnz m1 ;если не ноль,то на м1
mov EXIT, 1 ;эт ноль,передаем параметру выхода идиничку
jmp back ;на бэк...
m1: cmp al, 35h ;если цифра 5,то меняем цвета
jne m6 ;если не 5,то...какая?:-)
mov dl, ATRIBUT1;через
mov dh, ATRIBUT2;регистр dx
mov ATRIBUT1, dh;делаем
mov ATRIBUT2, dl;замену
jmp back ;идем на бэк....
m6: cmp al, 38h ; стрелка вверх
jz m2 ;переходим к метке
cmp al, 32h ; стрелка вниз
jz m3 ;переходим к метке
cmp al, 34h ; стрелка влево
jz m4 ;переходим к метке
cmp al, 36h ; стрелка вправо
jz m7 ;переходим к метке
m2: mov DIRECT, 1 ;тут
jmp back
m3: mov DIRECT, 4 ;везде
jmp back
m4: mov DIRECT, 2 ;все
jmp back
m7: mov DIRECT, 3 ;однотипно,а вобще мы тут смотрим какая клавиша нажата и изменяем переменную направления
back: pop es ;возвращаем
pop ds ;все
pop dx ;регистры
pop cx ;на
pop bx ;место
pop ax ; Ы :-)
iret ;выход из пп
NEW_1C endp
; Подпрограмма очистки экрана
;эт вобще интересная штука,так как обычно мы воводим символ на экран прерыванием int 10h или подобным
;но!
;мы пойдем другим путем и будем сразу писать в буффер дисплея!эт упрощает нам работу
;его сегмент находится по адресу 0B8000h и имеет объем 16Кбайт для цветного,для монохромного там все печально и убого:-(
;в чем суть этой подпрограммы
;сейвим регистры,некоторые очищаем от как и сеем на просторах видеобуффера пустоту :-)
;таким образом начинаея с сегмента 0B8000h в памяти bios и до смещения 0B0FA0 мы забиваем нулями и атрибутами фона
CLS proc near
push cx ;сохраняем
push ax ;изменяемые
push si ;регистры
xor si, si ;ксорим си,мало ли какая кака в нем лежала...
mov ah, 7 ;цвет фона
;mov dl, ' '
mov cx, 2000;80*25 этстолбцы и строки цветного видеобуффера,которые будут заполнены через цикл...
CL1: mov es:[si], ax ;...нулями,собственно вот этой инструкцией
inc si ;no
inc si ;coment
loop CL1 ;повторяем все это дело аж 2000 раз,ибо это число байт видеобуфера
pop si ;востанавливаем
pop ax ;изменяемые
pop cx ;регистры
ret
CLS endp
; Подпрограмма задержки
;нам же надо по заданию выводить каждый новый символ с определенной задержкой
;вот эта процедура этим и занимается
;она на определенное время занимает процессор
;потом она будет вставленна в системный таймер(прерывание 1CH) и будет выполняться 18,2 раз всекунду
DELAY proc near
push cx ;сохраняем регистр,так как его старое значение еще понадобится
;ниже будем смотреть какая кнопка нажата и в соответствии с этим выставлять время задержки
cmp al, 38h ;стрелка вверх
mov cx,1000h ;задержка будет равна 1000h или 4096 повторениям цикла
jz d11 ;прыгаем на метку,тут больше выбирать нечего
cmp al, 32h ; стрелка вниз
mov cx,400h ;ТУТ
jz d11
cmp al, 34h ; стрелка влево
mov cx,800h ;ВСЕ
jz d11
cmp al, 36h ;стрелка вправо
mov cx,0c00h ;АНАЛОГИЧНО
jz d11
d12: push cx ;сохраняем нужное нам время задержки в стек,после однократного выполения оно изменится
xor cx,cx ;очищаем регистр где лежит наше время задержки
d11: nop ;nop эт такая инструкция,которая заставляет процессор ничего не делать - просто пропостоять
loop d11 ;повторяем nop 1 раз
pop cx ;возвращаем когдато выбранное нами значение задержки и потом успешно отксореное
loop d12 ;уменьшаем эт значение на 1 и идем на метку
pop cx ;когда задержка выполнилась,возвращаем значения регистра до выполнения задержки обратно
ret
DELAY endp
; Подпрограмма вывода символа с заданным атрибутом
;тут все просто,так же как мы очищали дисплейный буффер,мы туда символы писать будем
OUT_SYMBOL proc near
push ax ; сохраняем
push bx ;регистры
mov al, SYM ;атрибут символа
mov ah, ATRIBUT1 ;атрибут цвета
mov bx, POS ;атрибут позиции
call DELAY ;вызываем задержку
mov es:[bx], ax ;заносим символ в дисплейный буфер,после чего он сразу же появляется на экране
pop bx ;востанавливаем
pop ax ;регистры
ret
OUT_SYMBOL endp
; Основная программа
START: mov ax, DATA
mov ds, ax
; чтение вектора прерывания
mov ah, 35h ;ну я думаю тут для тебя все понятно....
mov al, 1Ch
int 21h
mov OLD_IP, bx
mov OLD_CS, es
; установка вектора прерывания
push ds
mov dx, offset NEW_1C
mov ax, seg NEW_1C
mov ds, ax
mov ah, 25h
mov al, 1Ch
int 21h ;...вот примерно ля этого момента
pop ds ;сохраняем наш сегмент данных в стек
mov ax, 0B800h ;адрес сегмента буфера дисплея
mov es, ax
call CLS ;чистим экран
call DELAY ;делаем задержку
;и дальше начинаем проверять какая клавиша нажата
;так как у нас была выше процедура,которая по нажатию клавиши записывала в переменную DIRECT определенное число
;то теперь нам достаточно посмотреть по этой переменной в какую сторону необходимо дорисовать символ
;ну и собственно дорисовать его :-)
;как происходит масштабирование символов
;символ в верхнем левом углу имеет адрес 0000
;если нам надо нарисовать символ левее или правее,то мы должны отнять или прибавить 2 соответственно
;если нам надо нарисовать выше или ниже то мы должны от адреса отнять или прибавить 160 соответственно
p1: cmp EXIT, 0 ;если мы не нажимаем клавишу 0,то переменная EXIT не меняется и остается 0
jne quit ;ну а если нажимаем,то она станет 1 и мы перейдем на выход из программы
cmp DIRECT, 1 ;клавиша вверх?
jz p2
cmp DIRECT, 2 ;клавиша влево?
jz p3
cmp DIRECT, 3 ;клавиша вправо?
jz p4
cmp DIRECT, 4 ;клавиша вниз?
jz p5
p2: mov ax, POS ;записываем в реистр текущую позицию последнего выведенного символа
sub ax, 160 ;отнимаем от нее число,так как нам нужно нарисовать символ выше
jl p1 ;я хз зачем это,но без него не работает :-)
mov POS, ax ;а потом заносим новые координаты обратно
call OUT_SYMBOL ;вызываем процедуру вывода символа на экран
jmp p1 ;идем по ссылке выше
;собственно все что ниже это выбор координат по нажатию клавиши...
p3: mov ax, POS
sub ax, 2
jl p1
mov POS, ax
call OUT_SYMBOL
jmp p1
p4: mov ax, POS
add ax, 2
mov POS, ax
call OUT_SYMBOL
jmp p1
p5: mov ax, POS
add ax,160
cmp ax, 3999
jg p1
mov POS, ax
call OUT_SYMBOL
jmp p1
;вот до этого момента,дальше уже выход из программы
quit: call CLS ;очищаем экран
mov dx, OLD_IP ;и теперь нам надо вернуть старый вектор перываний
mov ax, OLD_CS ;адрес котороо мы успешно сохранили в самом начале
mov ds, ax ;возвращаем наш сегмент данных,а то мы были в сегменте bios
mov ah, 25h ;ну дальше думаю тебе тоже все понятно
mov al, 1Ch
int 21h
mov ax, 4c00h
int 21h
CODE ends
end START
|