我正在尝试编写TSR(DOS 16BIT)代码,其中INT8的每个刻度都会在屏幕周边向前移动一个星号。我有4个子程序,其'*'位置的不同增量对应于相应的屏幕边框。但是,代码挂起,我无法在调试器中同时运行它,因为它取决于中断。请提出解决方案
pos: dw 158,3998,3838,0
routine: dw subrt1,subrt2,subrt3,subrt4
subrt1:
add di,2
cmp di,[pos]
jnz exit
add bx,2
exit: ret
subrt2: add di,160
cmp di,[pos+2]
jnz exit
add bx,2
ret
subrt3: sub di,2
cmp di,[pos+4]
jnz exit
add bx,2
ret
subrt4: sub di,160
cmp di,[pos+6]
jnz exit
mov bx,0
ret
timer: push ax
mov ax,0xb800
mov es,ax
mov word[es:di],0x720
call [routine+bx]
mov word[es:di],0x742
mov al,0x20
out 0x20,al
pop ax
iret
start: xor ax,ax
xor bx,bx
mov es,ax
cli
mov word[es:8*4],timer
mov word[es:8*4+2],cs
sti
mov dx,start
add dx,15
mov cl,4
shr dx,cl
mov ax,0x3100
int 0x21
答案 0 :(得分:3)
这就是你解决问题的方法,主要是正确保存寄存器状态。见评论。
org 0x100 ;; missing?
jmp start ;; missing?
pos: dw 158,3998,3840,0 ;; what is 3T838?
routine: dw subrt1,subrt2,subrt3,subrt4
state dw 0 ;; storage for bx
curpos dw 0 ;; storage for di
oldisr dd 0 ;; address of old timer interrupt ISR
subrt1:
add di,2
cmp di,[pos]
jnz exit
add bx,2
exit: ret
subrt2: add di,160
cmp di,[pos+2]
jnz exit
add bx,2
ret
subrt3: sub di,2
cmp di,[pos+4]
jnz exit
add bx,2
ret
subrt4: sub di,160
cmp di,[pos+6]
jnz exit
mov bx,0
ret
timer: push ax
push bx ;; must preserve bx
push di ;; must preserve di
push ds ;; must preserve ds
push es ;; must preserve es
push cs ;; must load cs into ds to access pos,routine,state,curpos
pop ds
mov ax,0xb800
mov es,ax
mov di, [curpos] ;; must retrieve di from storage
mov bx, [state] ;; must retrieve bx from storage
mov word[es:di],0x720
call [routine+bx]
mov word[es:di],0x72A ;; you need 42 decimal (2A hex), not 42 hex
mov [curpos], di ;; must preserve di between ints
mov [state], bx ;; must preserve bx between ints
;mov al,0x20 ;; remove int acknowledge as the old ISR will do it for us
;out 0x20,al
pop es ;; must restore es
pop ds ;; must restore ds
pop di ;; must restore di
pop bx ;; must restore bx
pop ax
;iret ;; instead of direct iret continue in the old ISR
jmp far [cs:oldisr] ;; to prevent undesired effects (hangs/crashes)
start: xor ax,ax
; xor bx,bx ;; unnecessary
mov es,ax
cli
push word[es:8*4] ;; remember old ISR address
push word[es:8*4+2]
pop word[oldisr+2]
pop word[oldisr]
mov word[es:8*4],timer
mov word[es:8*4+2],cs
sti
mov dx,start
add dx,15
mov cl,4
shr dx,cl
mov ax,0x3100
int 0x21
答案 1 :(得分:1)
我不是TSR的专家,但我确实在这里看到了许多问题。
首先,我同意James Youngman的评论 - 我没有看到你在哪里链接原始的中断处理程序。您在对James的后续评论中引用的代码只是安全替换现有中断处理程序的代码(在替换期间禁用中断,因此没有竞争条件)。这还不够。您需要保存段:该处理程序的地址,并在完成处理程序后调用该处理程序。
其次,正如我在评论中指出的那样,我不理解pos中的3T838值并且不理解汇编程序如何处理它。但我可能会遗漏一些东西。
第三,当您进入处理程序时,您无法预测任何上下文。您基于bx(“call [routine + bx]”)进行分支,但bx未设置为“timer:”标签与该调用指令之间的任何内容。实际上,bx可以是任何东西。如果您希望bx是从中断到中断的运行值,则需要将其值保存在单独的变量中,在处理程序的开头将该变量移动到bx,并在离开之前将其保存回来。这也适用于di寄存器,我没有观察到在代码中任何地方进行初始化。
第四,我对你传递给int 21h / ah = 31h的参数并不完全有信心;我能找到的文档表明dx应该设置为程序使用的段落数。您正在加载“start:”的地址并将其乘以16(实际上是段落的大小)。您可能需要仔细检查这方面的文档;我不确定该怎么回事。
祝你好运。我知道写TSR是一项具有挑战性的壮举,即使在它们普遍存在的时代也是如此。