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
|