.model tiny locals .286 ; смещение различных полей в MCB MCB_SEQ equ 0 MCB_TYPE equ 1 MCB_SIZE equ 3 MCB_PROGNAME equ 8 .code org 100h start: jmp install ;------ DATA -------- int9h dd 0 ; старое значение вектора window_flag db 0 ; флажок, показывающий есть ли у нас окошко на экране temps dw 0 tempo dw 0 save_ss dw 0 save_sp dw 0 tsr_entry: mov cs:[save_ss], ss mov cs:[save_sp], sp mov cs:[temps], cs mov ss, cs:[temps] mov sp, offset mystack push ax bx cx dx di si ds es pushf cli in al, 60h cmp cs:window_flag, 1 ; есть чего чистить? jne no_flag test al, 80h ; "any key". надо отсечь break-коды jz cls jmp exit cls: call shadow2screen mov cs:window_flag, 0 jmp ack_iret no_flag: cmp al, 29h ; а hot ли это, простите, key? jne exit ; проверка нажатия ctrl: бит 2 в 0040:0017 mov ax, 40h mov es, ax test byte ptr es:[17h], 4 ; а ctrl нажат? jnz zzz exit: popf pop es ds si di dx cx bx ax mov ss, cs:[save_ss] mov sp, cs:[save_sp] jmp cs:int9h zzz: ; из глубины стеков извлечём адрес возврата, ; то бишь адрес где-то внутри той программы, ; где произошло прерывание. её мы и будем называть "текущей". ; соображение сомнительное, но как вариант... mov es, cs:[save_ss] mov bx, cs:[save_sp] mov dx, es:[bx+2] mov bx, es:[bx] mov cs:totl_p, dx mov cs:prog_p, bx ; нарисуем адрес, где произошло прерывание mov ax, cs mov es, ax mov ax, dx xchg ah, al mov di, offset tretadr call printhex inc di inc di xchg ah, al call printhex add di, 3 mov ax, bx xchg ah, al call printhex inc di inc di xchg ah, al call printhex ; call far ptr cs:[here] ;here: pop bx ; pop dx ; mov ax, 0B800h ; mov es, ax ; mov word ptr es:[0], 0430h call find_parent ; stc mov cs:temps, es mov cs:tempo, 8 jnc pstat mov cs:temps, cs mov cs:tempo, offset nah mov dx, 0 jmp mstat pstat: mov dx, es:[1] ; mov es, dx ; call getprogname mstat: call memstat jnc stat_ok jmp exit stat_ok: ; mov ax, 0B800h ; mov es, ax ; mov word ptr es:[0], 0432h call fill_window call screen2shadow call window_out mov cs:window_flag, 1 jmp ack_iret ack_iret: ; перед выходом нужно передёрнуть 7 бит в 61 порту in al, 61h or al, 80h out 61h, al and al, 7Fh out 61h, al ; и уведомить PIC о завершении обработки прерывания mov al, 20h out 20h, al popf pop es ds si di dx cx bx ax mov ss, cs:[save_ss] mov sp, cs:[save_sp] iret fill_window proc mov ax, cs mov ds, ax ; переносим имя программы в оконце ; имя программы либо все 8 символов, либо asciiz mov di, offset tprogn mov cx, 8 mov es, cs:[temps] mov si, cs:[tempo] movename: mov al, es:[si] or al, al jz pad_name mov ds:[di], al inc si inc di loop movename pad_name: jcxz end_of_name ; добить пробелами остаток до 8 mov al, ' ' mov ds:[di], al inc di loop pad_name end_of_name: ; теперь цифирьки mov ax, cs mov es, ax mov ax, totl_p mov di, offset tntotl call para2kb2ascii mov ax, prog_p mov di, offset tnused call para2kb2ascii mov ax, free_p mov di, offset tnfree call para2kb2ascii ret fill_window endp ; прогаммы часто освобождают env, так что непонятно когда ; эту функцию можно использовать... оставленя для полноты картины getprogname proc push ax bx cx dx si di ds es ; (dd1) ; найдём своё имя: оно находится после environment'а ; загружаем сегментный адрес environment'а из PSP cmp word ptr es:[2Ch], 0 jz @@leave_now mov es, es:[2Ch] ; сканируем с начала xor di, di mov al, 0 mov cx, 0FFFFh cld @@nextvar: repne scasb ; переменные разделены NULL'ями, а весь ; environment заканчивается двумя NULL cmp byte ptr es:[di], 0 jne @@nextvar ; пропустим к тому ещё 2 байта - число item'ов или типа того add di, 3 ; теперь es:di указывает на полный путь к файлу ; дойдём до конца и откатимся до последнего \ repne scasb mov al, '\' std repne scasb inc di inc di cld ; теперь es:di указывает на имя файла программы mov cs:[temps], es mov cs:[tempo], di @@leave_now: pop es ds di si dx cx bx ax ret getprogname endp ; дебаг! (printhex и dump_mcb) printhex proc .386 push eax ebx ecx edx di pushf movzx eax, ax mov cx, 2 ; а мне больше и не надо! ;) add di, 1 std ; справа налево mov ebx, 10h @@loopout: xor edx, edx div ebx cmp dl, 10 jae @@hex add dl, '0' jmp @@dec @@hex: add dl, 'A'-10 @@dec: xchg al, dl stosb xchg al, dl loop @@loopout popf pop di edx ecx ebx eax .286 ret printhex endp dump_mcb proc push ax bx cx dx si di ds es mov ax, es mov cs:[mcb_seg], ax mov bx, cs mov es, bx mov di, offset dumpline shr ax, 8 call printhex inc di inc di mov ax, cs:[mcb_seg] call printhex inc di inc di mov byte ptr es:[di], ':' inc di inc di mov ds, cs:[mcb_seg] xor si, si mov cx, 16 @@mcb_out: cld lodsb call printhex add di, 3 loop @@mcb_out mov cx, 10h xor si, si rep movsb mov ax, 0B800h mov es, ax mov ax, cs mov ds, ax mov si, offset dumpline mov di, cs:[dumpxy] cld mov cx, 70 @@dumpout: lodsb mov ah, 4 stosw loop @@dumpout add di, 20 mov cs:[dumpxy], di pop es ds di si dx cx bx ax ret dump_mcb endp mcb_seg dw 0 dumpxy dw 0 dumpline db 80 dup(' ') ; конец дебага db 200h dup('S') mystack db 'S' ; найти, кому принадлежит регион памяти, ; содержащий определённый адрес (передаваемый в dx:bx) ; возвращает es -> искомый MCB или cf=1 find_parent proc push ax bx dx mov cs:[dumpxy], 0 ; переведём смещение (bx) в параграфы и добавим к dx shr bx, 4 add dx, bx ; а, бабах, переполнение... кранты, ловить нечего jo @@fail ; начнём с... начала mov es, cs:[mcbhead] @@next_mcb: call dump_mcb ; удостоверимся, что это действительно MCB: ; первый байт должен быть M или Z mov al, es:[MCB_SEQ] cmp al, 'M' je @@is_mcb cmp al, 'Z' jne @@fail @@is_mcb: ; теперь сличим то, что нам дали с тем, ; что данный MCB описывает: попадёт ли данный нам адрес ; в область, описываемую MCB (es <= dx <= es+es:[3]) mov ax, es cmp dx, ax jb @@next add ax, es:[MCB_SIZE] jo @@fail ; что-то пошло не так... cmp dx, ax ja @@next1 @@check_last: ; вот он! ; но минуточку...может быть это свободный блок? cmp word ptr es:[MCB_TYPE], 0000h je @@fail ; или доисторический блок без имени? cmp word ptr es:[MCB_TYPE], 0008h je @@fail ; ok, это нормальный блок - выходим с cf=0 pop dx bx ax clc ret @@next: add ax, es:[MCB_SIZE] jo @@fail ; что-то пошло не так... @@next1: ; может, это был последний MCB? cmp byte ptr es:[MCB_SEQ], 'Z' je @@check_last ; число параграфов не включает сам MCB. ; при сравнении, с учётом округления, это прокатывает, ; но здесь мы-таки должны добавить 1 параграф inc ax mov es, ax jmp @@next_mcb @@fail: pop dx bx ax stc ret find_parent endp ; сбор статистики total/used_by_prog/free ; на входе dx - PSP программы, для которой считаем used memstat proc push ax bx es xor ax, ax mov cs:[dumpxy], ax ; обнуляем счётчики mov cs:[totl_p], ax mov cs:[prog_p], ax mov cs:[free_p], ax ; начинаем обход MCB с первого mov es, cs:[mcbhead] @@next_mcb: ;call dump_mcb ; удостоверимся, что это действительно MCB: ; первый байт должен быть M или Z mov al, es:[MCB_SEQ] cmp al, 'M' je @@is_mcb cmp al, 'Z' jne @@fail @@is_mcb: ; ok, это MCB. берём размер блока в ax mov ax, es:[MCB_SIZE] add cs:[totl_p], ax ; total - по-любому ; смотрим тип блока mov bx, es:[MCB_TYPE] ; свободный? cmp bx, 0 jne @@not_free add cs:[free_p], ax @@not_free: ; память нашей программы? cmp bx, dx jne @@not_prog add cs:[prog_p], ax @@not_prog: ; это был последний MCB? cmp byte ptr es:[MCB_SEQ], 'Z' je @@done ; идём дальше mov ax, es add ax, es:[MCB_SIZE] jo @@fail ; что-то пошло не так... inc ax mov es, ax jmp @@next_mcb @@done: pop es bx ax clc ret @@fail: pop es bx ax stc ret endp ; перевод параграфов в байты, потом в ascii и в буфер ; ax - число (параграфов) ; es:di - указатель на буфер. 7 знаков. para2kb2ascii proc ; уж простите, но мне просто лень делать по-другому ;) .386 push eax ebx ecx edx pushf movzx eax, ax shl eax, 4 mov cx, 8 ; а мне больше и не надо! ;) add di, 7 std ; справа налево mov ebx, 10 @@loopout: xor edx, edx div ebx add dl, '0' xchg al, dl stosb xchg al, dl loop @@loopout popf pop edx ecx ebx eax .286 ret para2kb2ascii endp ; сохраняем область экрана в shadow ; никаких параметров, всё высечено в камне ;) screen2shadow proc push ax cx si di ds es pushf mov ax, 0B800h mov ds, ax mov si, (WY*80 + WX) * 2 mov ax, cs mov es, ax mov di, offset shadow mov ax, WH cld @@moveline: mov cx, WW rep movsw add si, 80*2 - WW*2 dec ax jnz @@moveline popf pop es ds di si cx ax ret screen2shadow endp ; восстанавливаем экран из shadow shadow2screen proc push ax cx si di ds es pushf mov ax, 0B800h mov es, ax mov di, (WY*80 + WX) * 2 mov ax, cs mov ds, ax mov si, offset shadow mov ax, WH cld @@moveline: mov cx, WW rep movsw add di, 80*2 - WW*2 dec ax jnz @@moveline popf pop es ds di si cx ax ret shadow2screen endp ; выводим ляпоту window_out proc push ax bx cx si di ds es pushf mov ax, 0B800h mov es, ax mov di, (WY*80 + WX) * 2 mov ax, cs mov ds, ax mov si, offset window mov bx, WH cld ; придумаем нашей ляпоте цвет... ; да, это будет салатовый!! ммм... на... на синем фоне ;) mov ah, 1Ah @@outline: mov cx, WW @@outchar: lodsb ; из одной кучки взяли... stosw ; ...в другую положили dec cx jnz @@outchar add di, 80*2 - WW*2 dec bx jnz @@outline popf pop es ds di si cx bx ax ret window_out endp ; первый MCB mcbhead dw 0 ; сегментный адрес PSP "родителя" ppsp dw 0 ; счётчики totl_p dw 0 ; общее число параграфов prog_p dw 0 ; число параграфов, занятых родителем free_p dw 0 ; число свободных параграфов ; ляпота! window db '+--- ' tretadr db '0000:0000' db ' ---+' db '| total: ' tntotl db '00000000 |' db '| prog : ' tprogn db '________ |' db '| used : ' tnused db '00000000 |' db '| free : ' tnfree db '00000000 |' db '+- press any key -+' ; координаты ляпоты WX equ 30 WY equ 10 WW equ 19 ; ширина ляпоты WH equ 6 ; высота ляпоты nah db 'n/a', 0 ; место хранения областы экрана временно накрытой ляпотой shadow dw ($-window) dup('░') install: mov ax, 3 int 10h ; получаем адрес первого MCB (на будущее) mov ah, 52h int 21h mov ax, es:[bx-2] mov cs:[mcbhead], ax ; call far ptr cs:[here] ;here: pop bx ; pop dx ; mov dx, 0fff0h ; mov bx, 0 ; call find_parent ; call getprogname ; ret ; call far ptr cs:[tsr_entry] MOV AX,0500H INT 10H MOV AX,3509H ;Get address of INT 09H INT 21H MOV WORD PTR int9h,BX ; and save it MOV WORD PTR int9h+2,ES MOV AX,2509H MOV DX,OFFSET tsr_entry ; set new INT 09 to TEST INT 21H mov ah, 9 mov dx, offset msg int 21h MOV AX,3100H ;Request stay resident MOV DX, (install - tsr_entry + 15) / 16 + 100h INT 21H msg db 'Installed.', 10, 13, '$' END start