Я пытался писать программы TSR (Terminate-Stay-Resident) (в общем) на Assembly (16- bit) для MS-DOS. Я прочитал страницу в Википедии на TSR, а также страницу, посвященную его использованию специально в DOS (но, похоже, он обучает его на C, а не непосредственно на ассемблере). Я просмотрел сайт с тоннами документации по прерываниям DOS и нашел этот , этот и еще один, наиболее подходящий для программ TSR. Я не могу опубликовать все ссылки, потому что как новый пользователь я могу иметь до 2 гиперссылок в сообщении.
Итак, я попытался написать (казалось бы) очень простую программу TSR в плоской модели реального режима (. Формат файла COM) в NASM. Вот код:
[BITS 16]
[ORG 0x0100]
[SECTION .text]
Start:
; Get current interrupt handler for INT 21h
mov AX,3521h ; DOS function 35h GET INTERRUPT VECTOR for interrupt 21h
int 21h ; Call DOS (Current interrupt handler returned in ES:BX)
mov WORD [v21HandlerSegment],ES ; Store the current INT 21h handler segment
mov WORD [v21HandlerOffset],BX ; Store the current INT 21h handler offset
; Write new interrupt handler for INT 21h
mov AX,2521h ; DOS function 25h SET INTERRUPT VECTOR for interrupt 21h
mov DX,TSRStart ; Load DX with the offset address of the start of this TSR program
; DS already contains the segment address, it is the same as CS in this .COM file
int 21h ; Override the INT 21h handler with this TSR program
; The TSR program will be called even when this portion uses INT 21h to terminate and stay resident
mov AX,3100h ; DOS function TSR, return code 00h
mov DX,00FFh ; I don't know how many paragraphs to keep resident, so keep a bunch
int 21h ; Call our own TSR program first, then call DOS
TSRStart:
push WORD [v21HandlerSegment] ; Push the far address of the original
push WORD [v21HandlerOffset] ; INT 21h handler onto the stack
retf ; Jump to it!
[SECTION .data]
v21HandlerSegment dw 0000h
v21HandlerOffset dw 0000h
Когда я собираю это и выполняю внутри DOS, вместо возврата к приглашению DOS система зависает (никаких действий не происходит, за исключением того, что аппаратный курсор просто мигает под последней подсказкой). Я предполагаю, что мусор памяти может выполняться, но вы уловили суть.
Может ли кто-нибудь помочь выяснить, в чем проблема с этим кодом, и / или дать общий совет по кодированию TSR в DOS? Заранее благодарим за любую помощь!
необходимо использовать cs:
переопределение сегмента для доступа к данным TSR из обработчика прерываний общего назначения, потому что эти ds
значение является регистром произвольного пользователя затем.
Вы не должны продвигать адрес следующего обработчика на стек, затем переходят с retf
. Более просто сделать jmp far [cs:...]
(и это имеет более короткое кодирование). Но Ваш метод хорошо работает также.
можно поместить обработку инициализации (не нужный в установленном обработчике резидентного объекта) в конце изображения программы. Это - тривиальная оптимизация размера TSR.
Для вычисления размера резидентного процесса используйте маркировки NASM. Чтобы позволить сдвиг (или разделиться), операции должны были выяснить длину в абзацах, только использовать дельты маркировок. Дельта (различие) является скалярной величиной к NASM, так может использоваться в вычислениях. Одна только (non-struc) маркировка не является скаляром.
Вот пример с помощью всех них:
cpu 8086
bits 16
org 256
start:
jmp init
align 4
int21old:
dd 0
int21handler:
jmp far [cs:int21old]
end_of_resident:
init:
mov ax, 3521h
int 21h
mov word [int21old + 2], es
mov word [int21old], bx
mov ax, 2521h
mov dx, int21handler
int 21h
mov ax, 3100h
mov dx, (end_of_resident - start + 256 + 15) >> 4
int 21h
вычисление размера вычисляет дельту двух маркировок, добавляет в 256 для PSP процесса (то же как org 256
), добавляет 15, чтобы заставить shift-division окружить, затем смещается вниз в сумму абзацев.