Как правильно подключить прерывание 28h в сборке для DOS и восстановить его? - PullRequest
4 голосов
/ 01 июня 2019

Я пытаюсь установить обработчик прерывания 28h в свою собственную процедуру, восстановить все регистры и флаги, а также восстановить исходный обработчик прерывания. Я использую NASM Assembler, под DOSBox и MS-DOS 6.22 в VirtualBox.

Я думал об отладке, но на программе TSR это невозможно. Я попытался перенести сегмент данных в сегмент кода и сохранить исходный сегмент данных для последующего восстановления, но кажется, что компьютер зависает даже после восстановления сегмента данных.

section .text   ;Code Section
org 100h        ;DOS Executable Start
mov ah,35h      ;Get Interrupt Vector
mov al,28h      ;Of Interrupt 28h
int 21h         ;Call DOS Kernel
push cs         ;Push Code Segment
pop ds          ;Onto Data Segment
mov [oldseg],es ;Save Old Interrupt Vector Segment
mov [oldoff],bx ;Save Old Interrupt Vector Offset
mov ah,25h      ;Set Interrupt Vector
mov dx,resstart ;To Resstart
int 21h         ;Call DOS Kernel
mov dx,resend   ;Set Data Offset to Resend
sub dx,resstart ;Subtract Resstart
shr dx,4h       ;Shift Right 4 Bits for Paragraph
inc dx          ;One Extra Paragraph for PSP
mov ah,31h      ;Terminate and Stay Resident
xor al,al       ;Return Code
int 21h         ;Call DOS Kernel

resstart:       ;Resident Code Start
push ax         ;Save AX
push es         ;Save ES
push di         ;Save DI
push cx         ;Save CX
push ds         ;Save DS
push dx         ;Save DX
mov ah,00h      ;Set Video Mode
mov al,13h      ;To Mode 13h
int 10h         ;Call BIOS Video
mov ax,0A000h   ;VGA Segment
mov es,ax       ;Stored in ES
xor di,di       ;VGA Offset in DI
mov cx,0FA00h   ;Fill Entire Screen
mov al,09h      ;With Light Blue Color
rep stosb       ;Repeat Store AL at ES:DI
mov ah,25h      ;Set Interrupt Vector
mov al,28h      ;Of Interrupt 28h
mov ds,[oldseg] ;Restore Old Interrupt Vector Segment
mov dx,[oldoff] ;Restore Old Interrupt Vector Offset
int 21h         ;Call DOS Kernel
pop dx          ;Restore DX
pop ds          ;Restore DS
pop cx          ;Restore CX
pop di          ;Restore DI
pop es          ;Restore ES
pop ax          ;Restore AX
iret            ;Return and Restore Flags
resend:         ;Resident Code End

section .data
oldseg dw 0     ;Old Interrupt Vector Segment
oldoff dw 0     ;Old Interrupt Vector Offset

После возврата исходного адреса вектора прерывания и установки нового адреса вектора прерывания на «рестарт» программа должна завершиться и остаться резидентной. После этого прерывание 28h будет запускаться автоматически, поскольку DOS больше ничего не делает, что, в свою очередь, запускает мой обработчик прерываний.

Обработчик прерываний устанавливает режим видео на 13h, пытается заполнить весь экран голубым цветом, восстанавливает исходный обработчик прерываний 28h, восстанавливает все задействованные регистры и флаги и возвращает в DOS. Выполнение этой программы не дает результатов, система даже не зависает. При выполнении части установки режима видео 13h и отдельного заполнения всего экрана синим цветом, он работает отлично.

Ответы [ 2 ]

3 голосов
/ 02 июня 2019
mov dx,resend ;Set Data Offset to Resend
sub dx,resstart ;Subtract Resstart
shr dx,4h ;Shift Right 4 Bits for Paragraph
inc dx ;One Extra Paragraph for PSP

В этой программе .COM вы сохраняете и правильно устанавливаете вектор прерывания.Однако вы не можете точно рассчитать количество абзацев, которое нужно сохранить с помощью функции DOS.TerminateAnd StayResident.

inc dx необходимо, чтобы округлить до ближайшего абзаца выше.Конечно, не для учета PSP.Для этого потребуется 16 абзацев, поскольку PSP имеет 256 байтов.

Память, выделенная для этой программы .COM, начинается с PSP, поэтому отсчет DX также должен начинаться там.

mov     dx, resend 
shr     dx, 4
inc     dx
mov     ax, 3100h   ; DOS.TerminateAndStayResident
int     21h

Подсказка Если вы выровняете эту повторную отправку метки к границе абзаца, inc dx больше не требуется.

Если ваш нынешний код работал частично вэмулятор, такой как virtualbox, потому что память, ранее занимаемая вашей программой, еще не была перезаписана, например, программной оболочкой.Эмуляторы, в отличие от DOS, позволяют выполнять интерпретатор команд с большого расстояния.

экран заполняется синим цветом при использовании virtualbox, хотя система зависает

Iбудет зависать, если кто-то выключит свет, пока я что-то пишуЭто то, что делает ваш обработчик, когда он внезапно меняет режим видео ...


Для программы TSR мы обычно перепрыгиваем часть, которая должна оставаться резидентной, поэтому пространство, занимаемое одноразовымсистема может перезапустить установку.

Еще один прием, который вы можете использовать, - записать смещение и сегмент старого вектора прерывания непосредственно в инструкциях, которые восстановят вектор.Больше нет проблем с сегментными регистрами в обработчике.

Это моя перезапись вашей программы:

    org     100h
Start:
    jmp     Setup

MyInt28:
    push    ax
    push    es
    push    di
    push    cx
    push    ds
    push    dx
    mov     ax, 0013h   ; BIOS.SetVideoMode
    int     10h
    mov     ax, 0A000h
    mov     es, ax
    xor     di, di
    mov     cx, 64000/2
    mov     ax, 0909h
    cld
    rep stosw
PatchA:
    mov     ax, 0       ; Don't change this to 'xor ax,ax'
    mov     ds, ax
PatchB:
    mov     dx, 0       ; Don't change this to 'xor dx,dx'
    mov     ax, 2528h   ; DOS.SetInterruptVector
    int     21h
    pop     dx
    pop     ds
    pop     cx
    pop     di
    pop     es
    pop     ax 
    iret

Setup:                  ; Resident part ends here.
    mov     ax, 3528h   ; DOS.GetInterruptVector
    int     21h         ; -> ES:BX
    mov     [PatchA + 1], es
    mov     [PatchB + 1], bx
    mov     dx, MyInt28
    mov     ah, 25h     ; DOS.SetInterruptVector
    int     21h
    mov     dx, (Setup-Start+15)/16
    mov     ax, 3100h   ; DOS.TerminateAndStayResident
    int     21h
1 голос
/ 01 июня 2019

В вашей программе несколько проблем:

Задача 1

push cs ;Push Code Segment
pop ds ;Onto Data Segment
mov [oldseg],es ;Save Old Interrupt Vector Segment
mov [oldoff],bx ;Save Old Interrupt Vector Offset
...
mov ds,[oldseg] ;Restore Old Interrupt Vector Segment
mov dx,[oldoff] ;Restore Old Interrupt Vector Offset

Четыре инструкции mov предполагают, что регистр ds указывает на секцию .data.

Однако в случае первых двух mov инструкций ds будет указывать на секцию .text, а не на секцию .data из-за последовательности push cs - pop ds.

В случае .COM файла * секции 1024 * и .data обычно одинаковы; однако в .EXE файлах они обычно не совпадают.

В случае третьей инструкции mov очень маловероятно, что ds указывает на любой раздел, связанный с вашей программой. А в случае четвертого это почти невозможно, потому что третья инструкция mov изменила регистр ds.

Решением будет использование сегмента .text для хранения данных. Это возможно в операционных системах «реального режима» (таких как MS-DOS), но не в операционных системах «защищенного режима» (таких как Windows):

Поместите две строки dw 0 (например, oldseg dw 0) перед строкой section .data. Теперь четыре байта хранилища данных расположены в том же разделе, что и ваш код. Тогда вы можете получить доступ к данным следующим образом:

 push cs
 pop ds
 mov [oldseg],es ;We know that ds=cs, so no "cs:" is required here
 ...
 mov ds,cs:[oldseg] ;Restore Old Interrupt Vector Segment
 mov dx,cs:[oldoff] ;Restore Old Interrupt Vector Offset

«cs:» сообщит ЦПУ, что данные, к которым вы обращаетесь, находятся в разделе, на который указывает cs; и cs всегда указывает на раздел, содержащий код, выполняемый в данный момент. И это .text раздел.

Обратите внимание, что правильный синтаксис (расположение букв "cs:" в строке) отличается от ассемблера к ассемблеру:

 mov dx,cs:[oldoff]
 cs:mov dx,[oldoff]
 mov dx,[cs:oldoff]

Возможно, ваш ассемблер использует другой синтаксис.

Задача 2

mov ah,25h ;Set Interrupt Vector
mov al,28h ;Of Interrupt 28h
mov ds,[oldseg] ;Restore Old Interrupt Vector Segment
mov dx,[oldoff] ;Restore Old Interrupt Vector Offset
int 21h ;Call DOS Kernel

Вызов int 21h изнутри int 21hint 28h изнутри int 21h) тоже не очень хорошая идея.

Однако функция 25h ничего не сделает, кроме записи 4 байтов данных в таблицу векторов прерываний (в то время как прерывания отключаются с помощью cli):

Вы можете сделать это напрямую, просто сохранив смещение по адресу 0:0A0h и сегмент по адресу 0:0A2h:

mov ax,0      ;You might also use "xor ax,ax" or "sub ax,ax"
mov ds,ax     ;Now ds=0
mov ax,cs:[oldseg]
mov dx,cs:[oldoff]
cli           ;Disable the interrupts
mov [0A0h],dx ;Write dx to ds:0A0h which is 0:0A0h
mov [0A2h],ax ;Write ax to ds:0A2h which is 0:0A2h

* * * * * * * * * * * * * * * cli * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1078* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1078* * * * * * * * * * * * * * * * * * * * * * * *1078* * * * * * * * *1078* * * * * * * * *1078* * * *] * * * * * * * * * * *1078* * * * * * * * * * * * * * * * * *1078*.

Если вы можете гарантировать, что int 28h не вызывается из-за аппаратного прерывания, вам не нужно это делать.

Инструкция iret автоматически восстановит старое состояние прерываний (включено или отключено).

Задача 3

Вызов сложных функций (таких как int 10h) из прерывания int 28h, кажется, также не лучшая идея.

...