summaryrefslogtreecommitdiff
path: root/sem2/lab5/DumpTSR.asm
blob: fd3fad9a12b74cb9f2d52bf3ec06728fb078846e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
.MODEL Tiny
.286
.CODE
ORG     100h

LOCALS                                  ; считать метки, начинающиеся с @@ локальными в пределах процедур

TSRKeyCode      =       2000h           ; код комбинации клавиш Alt+D

Start:

                jmp     SetIntVec       ; прыжок на установщик вектора

; Обработчик прерывания 9 (прерывание от клавиатуры)
Handler9:
                pushf                   ; записываем в стек флаги (для вызова старого обработчика прерывания)
                call    dword ptr cs:OldInt9 ; вызываем старый обработчик прерывания 9
                ; Проверка комбинации клавиш и флага занятости
                pusha                   ; сохраняем все регистры общего назначения
                cmp     byte ptr cs:BusyFlag,0  ; проверяем флаг занятости (признак того, что этот код уже работает - это нужно для того, чтобы повторное нажатие Alt+D не вызвало наш код ещё раз)
                jne     @@Exit9         ; если не ноль, прыгаем
                mov     ah,11h          ; функция получения информации о нажатии клавиши (без ожидания)
                int     16h             ; проверяем - нажата ли какая-либо клавиша (и какая)
                jz      @@Exit9         ; прыгаем, если нет
                cmp     ax,TSRKeyCode   ; проверяем код нужной нам комбинации клавиш
                jne     @@Exit9         ; прыгаем, если не наша
                sti                     ; разрешаем прерывания (чтобы во время отображения не происходило зависания... хотя этого обычно и не происходит, лучше всё же это сделать)
                mov     ah,10h
                int     16h             ; удаляем нажатую комбинацию клавиш из буфера клавиатуры
                mov     byte ptr cs:BusyFlag,1  ; устанавливаем флаг занятости
                ; Вывод даты/времени
                push    ds
                push    es              ; сохраняем DS, ES
                call    MainTSRProc     ; вызываем процедуру вывода дампа с сохранением и восстановлением старого текста
                pop     es
                pop     ds              ; восстанавливаем ES, DS
                mov     byte ptr cs:BusyFlag,0  ; сбрасываем флаг занятости в 0 (признак того, что этот код отработал - это нужно для того, чтобы Alt+D снова срабатывало)
        @@Exit9:
                popa                    ; восстанавливаем все регистры общего назначения
                iret                    ; выходим из прерывания

; Процедура вывода дампа с сохранением и восстановлением старого текста
MainTSRProc     proc
                ; Получение информации о видеорежиме
                mov     ah,0Fh
                int     10h             ; получаем в AL текущий видеорежим, в BH номер видеостраницы
                cmp     al,3            ; сравниваем с 3 (стандартный текстовый режим 80х25)
                je      @@OkVideoMode   ; прыгаем, если равно
                ; Действие в случае неверного видеорежима
                mov     al,7
                int     29h             ; издаём звуковой сигнал (в случае неверного видеорежима)
                ret                     ; выходим из процедуры
                ; Сохранение информации о курсоре, видеостранице, адреса дампа
        @@OkVideoMode:
                mov     cs:[VideoPage],bh ; сохраняем номер активной видеостраницы
                mov     ah,3
                int     10h             ; получаем размер курсора в CX
                mov     cs:[OldCurSize],cx ; сохраняем размер
                mov     bp,sp
                mov     ax,[bp+2+4+16]  ; смещение адреса дампа (2 - адрес возврата, 4 - сохранённые ds,es, 16 - pusha)
                mov     cs:[DumpAddr],ax
                mov     ax,[bp+2+4+16+2] ; сегмент адреса дампа
                mov     cs:[DumpAddr+2],ax
                ; Чтение старого изображения
                xor     ax,ax
                mov     ds,ax           ; DS = сегмент 0
                mov     si,ds:[44Eh]    ; SI = смещение начала активной видеостраницы, он же адрес для чтения данных (содержимого экрана)
                mov     cs:[VideoOffset],si ; сохраняем его
                mov     ax,0B800h
                mov     ds,ax           ; DS = сегмент видеопамяти
                push    cs
                pop     es              ; ES = сегмент кода и данных нашей программы
                lea     di,ScreenBuffer ; DI = адрес для записи данных (наш буфер)
                mov     cx,80*25
                cld                     ; прямой порядок копирования данных
                rep movsw               ; копируем содержимое экрана в наш буфер (CX слов из DS:SI в ES:DI)
                ; Прячем курсор
                mov     ah,1
                mov     ch,20h          ; спрятать курсор
                int     10h             ; прячем курсор
                ; Рисование рамки
                push    ds
                pop     es              ; сегмент видеопамяти
                push    cs
                pop     ds              ; DS = сегмент кода и данных нашей программы
                mov     di,[VideoOffset] ; DI = смещение начала активной видеостраницы
                mov     ax,1BC9h        ; белый цвет на синем фоне (AH) и код символа левого верхнего угла
                stosw                   ; записываем его (AX) в видеопамять (ES:DI, DI=DI+2)
                mov     al,205          ; символ горизонтальной линии
                mov     cx,78
                rep stosw               ; записываем его 78 раз
                mov     al,187          ; символ правого верхнего угла
                stosw
                mov     cx,23
        @@Vert: mov     al,186          ; символ ветикальной линии
                cmp     cl,3
                jne     @@1
                mov     al,199          ; вертикальная линия с ответвлением для 3-й строки с конца
        @@1:    stosw
                mov     al,' '          ; пробел
                cmp     cl,3
                jne     @@2
                mov     al,196          ; горизонтальная линия для 3-й строки с конца
        @@2:    push    cx
                mov     cx,78
                rep stosw
                pop     cx
                mov     al,186          ; символ ветикальной линии
                cmp     cl,3
                jne     @@3
                mov     al,182          ; вертикальная линия с ответвлением для 3-й строки с конца
        @@3:    stosw
                loop    @@Vert
                mov     al,200          ; символ левого нижнего угла
                stosw
                mov     al,205          ; символ горизонтальной линии
                mov     cx,78
                rep stosw               ; записываем его 78 раз
                mov     al,188          ; символ правого нижнего угла
                stosw
                ; Вывод текста помощи
                mov     ah,1Eh          ; жёлтый текст на синем фоне
                lea     si,HelpText1
                mov     di,(22*80 + 6)*2
                call    ShowText        ; выводим текст помощи
                mov     di,(23*80 + 3)*2
                call    ShowText        ; выводим текст помощи (SI уже на HelpText2, т.к. он идёт сразу за HelpText1)
                ; Вывод дампа и обработка клавиш
        @@MainLoop:
                call    ShowDump        ; вывод дампа
                mov     ah,10h
                int     16h             ; ожидание нажатия на клавишу
                cmp     ax,4BE0h        ; проверка клавиши влево
                jne     @@R
                dec     [DumpAddr]      ; уменьшение смещения на 1
        @@R:    cmp     ax,4DE0h        ; проверка клавиши вправо
                jne     @@CL
                inc     [DumpAddr]      ; увеличение смещения на 1
        @@CL:   cmp     ax,73E0h        ; проверка клавиши Ctrl+влево
                jne     @@CR
                sub     [DumpAddr],100h ; уменьшение смещения на 100h
        @@CR:   cmp     ax,74E0h        ; проверка клавиши Ctrl+вправо
                jne     @@U
                add     [DumpAddr],100h ; увеличение смещения на 100h
        @@U:    cmp     ax,48E0h        ; проверка клавиши вверх
                jne     @@D
                dec     [DumpAddr+2]    ; уменьшение сегмента на 1
        @@D:    cmp     ax,50E0h        ; проверка клавиши вниз
                jne     @@PU
                inc     [DumpAddr+2]    ; увеличение сегмента на 1
        @@PU:   cmp     ax,49E0h        ; проверка клавиши Page Up
                jne     @@PD
                sub     [DumpAddr+2],100h ; уменьшение сегмента на 100h
        @@PD:   cmp     ax,51E0h        ; проверка клавиши Page Down
                jne     @@E
                add     [DumpAddr+2],100h ; увеличение сегмента на 100h
        @@E:    cmp     ax,11Bh         ; проверка нажатия на Esc
                jne     @@MainLoop
                ; Восстановление старого изображения
                lea     si,ScreenBuffer ; SI = смещение сохранённой области памяти
                mov     di,[VideoOffset] ; DI = смещение начала активной видеостраницы
                mov     cx,80*25
                rep movsw               ; восстанавливаем изображение: копируем из буфера в область видеопамяти (CX слов из DS:SI в ES:DI)
                ; Восстановление курсора
                mov     ah,1
                mov     bh,[VideoPage]  ; видеостраница
                mov     cx,[OldCurSize] ; размер курсора
                int     10h             ; восстанавливаем размер курсора
                ret
MainTSRProc     endp

; Вывод текста по адресу DS:SI для нулевого символа цветом AH в позицию ES:DI
ShowText        proc
        @@Char: lodsb                   ; читаем символ
                test    al,al           ; тестируем на ноль
                jz      @@Ret           ; выходим, если ноль
                stosw                   ; выводим его вместе с цветом
                jmp     @@Char          ; повторяем
        @@Ret:  ret
ShowText        endp

; Вывод значения AL цветом AH в 16-ричном виде по адресу ES:DI
ShowByte        proc
                push    ax
                shr     al,4            ; выделяем старшую часть
                cmp     al,9
                jbe     @@1
                add     al,'A'-('9'+1)
        @@1:    add     al,'0'          ; преобразуем в 16-ричную цифру
                stosw                   ; выводим
                pop     ax
                and     al,0Fh          ; выделяем младшую часть
                cmp     al,9
                jbe     @@2
                add     al,'A'-('9'+1)
        @@2:    add     al,'0'          ; преобразуем в 16-ричную цифру
                stosw                   ; выводим
                ret
ShowByte        endp

; Вывод значения DX цветом AH в 16-ричном виде по адресу ES:DI
ShowWord        proc
                mov     al,dh
                call    ShowByte
                mov     al,dl
                call    ShowByte
                ret
ShowWord        endp

ShowDump        proc
                push    ds
                lds     si,dword ptr [DumpAddr] ; DS:SI = адрес дампа
                mov     di, (1*80 + 2)*2 ; стартовая позиция

                mov     cx,20           ; 20 строк дампа
        @@1:    push    cx
                mov     ah,1Bh          ; светло-голубой цвет на синем фоне
                mov     dx,ds
                call    ShowWord        ; вывести сегмент
                mov     al,':'
                stosw                   ; вывести двоеточие
                mov     dx,si
                call    ShowWord        ; вывести смещение

                mov     cx,16           ; 16 элементов
                mov     ax,1320h        ; тёмно-голубой цвет на синем фоне и пробел
                stosw                   ; выводим пробел
        @@2:    mov     al,' '
                stosw                   ; вывести пробел
                lodsb                   ; прочитать символ из памяти DS:SI (SI=SI+1)
                call    ShowByte        ; вывести 16-ричное значение байта из памяти
                loop    @@2

                mov     al,' '
                stosw
                stosw                   ; вывести 2 пробела
                mov     cx,16           ; 16 символов
                sub     si,cx           ; назад на 16 символов
                mov     ah,12h          ; тёмно-зелёный цвет на синем фоне
        @@3:    lodsb                   ; прочитать символ из памяти DS:SI (SI=SI+1)
                stosw                   ; вывести символ из памяти
                loop    @@3

                pop     cx
                add     di,160-(9+1+3*16+2+16)*2 ; переходим на новую строку
                loop    @@1
                pop     ds
                ret
ShowDump        endp

BusyFlag        db      0               ; флаг занятости процедуры отображения дампа (0 - не занята, 1 - занята)
HelpText1       db      27,'/',26,' - one byte offset scroll, Ctrl',27,'/Ctrl',26,' - 100h bytes offset scroll',0
HelpText2       db      24,'/',25,' - one segment scroll, PageUp/PageDn - 100h segments scroll, Esc - exit',0

TSRCodeEnd      =       $               ; конец кода резидентной части (дальше идут буферы)

OldInt9         dw      ?,?             ; адрес старого обработчика прерывания 9
DumpAddr        dw      ?,?             ; адрес дампа
OldCurSize      dw      ?               ; старый размер курсора
VideoOffset     dw      ?               ; смещение начала области текущей видеопамяти (для активной видеостраницы)
VideoPage       db      ?               ; номер активной видеостраницы
ScreenBuffer    dw      80*25 dup (?)   ; буфер для хранения старого изображения с экрана

TSREnd          =       $               ; конец резидентной части программы
ORG     TSRCodeEnd                      ; для экономии места на диске следующий код будет начинаться здесь (на месте начала буфера, всё равно этот буфер не инициализирован)

; Установка векторов прерывания
SetIntVec:
                mov     ax,3509h        ; функция получения вектора прерывания 9 (прерывание от клавиатуры)
                int     21h             ; получаем вектор
                mov     OldInt9[0],bx   ; сохраняем смещение
                mov     OldInt9[2],es   ; сохраняем сегмент
                mov     ah,25h          ; функция установки обработчика прерывания - записи вектора прерывания (AL=9 не менялся после вызова предыдущей функции 35h)
                lea     dx,Handler9     ; DS:DX = адрес нашего обработчика
                int     21h             ; устанавливаем наш обработчик

                mov     ah,49h          ; функция освобождения памяти
                mov     es,ds:[2Ch]     ; сегмент переменных окружения
                int     21h             ; освобождаем сегмент с переменными окружения (чтобы программа занимала меньше памяти)

                mov     ah,9            ; функция вывода сообщения (до '$')
                lea     dx,MsgInstalled ; адрес сообщения
                int     21h             ; выводим сообщение

                lea     dx,TSREnd       ; конец обработчика прерывания
                int     27h             ; выходим, оставив программу резидентной

MsgInstalled    db      'DumpTSR is successfully installed!',13,10
                db      'Press <Alt>+<D> to activate.',13,10,'$'

END             Start