В вашей программе несколько проблем:
Задача 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 21h
(а int 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
, кажется, также не лучшая идея.