Ahm*_*tt_ 0 x86 assembly emu8086
我有一个程序可以在8086中清楚地完成一些任务,我想为我的程序添加一个时间限制.如果我的程序在120秒内没有完成所有任务,那么必须暂停程序.我该怎么做?
我的程序生成一个随机数,并对其进行一些操作并给出一些输出.
org 100h
;CALL TIMER_DISPLAY
GO:
MOV AL,0
MOV ROW,AL
CALL COORDINATE
CALL CLEAR_SCREEN
CALL RANDOM_NUMBER
MOV AL,R
AND AL,5
MOV KEY,AL
MUL R
ADD AL,5
MOV DL,5
DIV DL
MOV KEY1,AH
CMP KEY1,0
JNE GO
LEA BX,M1
CALL DISPLAY_MESSAGE
MOV AL,KEY
OR AL,R
MOV KEY2,AL
CMP KEY2,0
JE GO
CALL COORDINATE
LEA BX,M2
CALL DISPLAY_MESSAGE
MOV AL,KEY
ADD AL,R
SAR AL,2
MOV KEY3,AL
CMP KEY3,0
JE GO
CALL COORDINATE
LEA BX,M3
CALL DISPLAY_MESSAGE
MOV AL,KEY
XOR AL,R
MOV KEY4,AL
CMP KEY4,0
JE GO
CALL COORDINATE
LEA BX,M4
CALL DISPLAY_MESSAGE
MOV AL,KEY
MOV DL,R
MUL DL
MOV KEY5,AL
CMP KEY5,0
JE GO
CALL COORDINATE
LEA BX,M5
CALL DISPLAY_MESSAGE
ret
M1 DB 'LOCK 1 WAS OPENED', '$'
M2 DB 'LOCK 2 WAS OPENED', '$'
M3 DB 'LOCK 3 WAS OPENED', '$'
M4 DB 'LOCK 4 WAS OPENED', '$'
M5 DB 'LOCK 5 WAS OPENED AND I AM OUT ', 01, '$'
R DB ?
KEY DB ?
KEY1 DB ?
KEY2 DB ?
KEY3 DB ?
KEY4 DB ?
KEY5 DB ?
ROW DB 0
Run Code Online (Sandbox Code Playgroud)
这里有一个生成随机数的过程
; -- RANDOM NUMBER GENERATION PROCEDURE --
RANDOM_NUMBER PROC
MOV AH,00H ; INTERRUPTS TO GET SYSTEM TIME
INT 1AH ; CX:DX NOW HOLD NUMBER OF CLOCK TICKS
MOV AX,DX
XOR DX,DX
MOV CX,10
DIV CX
MOV R,DL
MOV AX,0
RET
RANDOM_NUMBER ENDP
Run Code Online (Sandbox Code Playgroud)
设置坐标,显示消息和清除屏幕的程序.
; -- DISPLAY MESSAGE --
DISPLAY_MESSAGE PROC
MOV AL,0
MOV AH,09H
MOV DX,BX
INT 21H
RET
DISPLAY_MESSAGE ENDP
; -- SET COORDINATE --
COORDINATE PROC
MOV AH,2H
MOV BH,0
MOV DH,ROW
MOV DL,0
INT 10H
INC ROW
RET
COORDINATE ENDP
; -- CLEAR SCREEN --
CLEAR_SCREEN PROC
MOV AH,7
MOV AL,0
MOV CX,0
MOV DX,184FH
MOV BH,7
INT 10H
RET
CLEAR_SCREEN ENDP
Run Code Online (Sandbox Code Playgroud)
调用定时器显示的程序并使其与实时同步.
; -- TIMER DISPLAY --
TIMER_DISPLAY PROC
#START=LED_DISPLAY.EXE#
;#MAKE_BIN#
NAME "TIMER"
MOV AX,120
OUT 199,AX
X1:
CALL SYNCHRONIZE_TIMER_DISPLAY
DEC AX
OUT 199,AX
CMP AX,0
JG X1
HLT
RET
TIMER_DISPLAY ENDP
; -- SYNCHRONIZE TIMER DISPLAY --
SYNCHRONIZE_TIMER_DISPLAY PROC
PUSH AX
MOV CX, 0FH
MOV DX, 4240H
MOV AH, 86H
INT 15H
POP AX
RET
SYNCHRONIZE_TIMER_DISPLAY ENDP
Run Code Online (Sandbox Code Playgroud)
因为你使用int 1Ah,我认为"8086"意味着"PC兼容"(有几台计算机使用8086 CPU不兼容PC).
理论背景
在这种情况下,您可能会在CPU端挂起硬件中断IRQ 0,即SW中断#8:
在PC兼容系统上,定时器将在某个定时器间隔内触发定时器中断(IRQ 0)一次.这就像是int 8软件中的指令.
默认情况下,定时器配置为18.2 Hz的频率,因此int 8将在10秒内调用182次或在120秒内调用2184次.
中断指令(int)将在堆栈上推送6个字节(标志,CS和IP),然后跳转到存储在该地址的远地址(段:偏移)0:(4*n).
示例:如果0x1234存储在地址0:0x20并0x5678存储在地址中0:0x22,则int 8指令将跳转到地址0x5678:0x1234.
当进入中断例程时,堆栈(ss:sp)的前4个字节包含中断后要执行的下一条指令的远地址; 这个地址可以修改.
障碍
请注意,当cli指令禁止中断时,硬件不能执行任何中断.在这种情况下,您没有机会限制程序的时间.
另一个问题可能是DOS和BIOS调用:
如果在BIOS调用(例如int 10h)或甚至DOS调用(int 21h)中达到120秒并且您中断了程序,则可能会导致整个操作系统崩溃!
示例代码
还请注意,我通常使用GNU汇编程序,它具有不同的语法.因此,我的示例中的某些行可能需要进行一些修改,因为汇编程序会识别语法错误.
install:
; Remember the location of the original stack
mov origStack, sp
mov origStack+2, ss
; First we copy the original address of the "int 8" interrupt
; to the variable "origAddress"
mov ax, 0
mov es, ax
mov ax, [es:20h]
mov origAddress, ax
mov ax, [es:22h]
mov origAddress+2, ax
; We disable hardware interrupt generation
; This ensures that "int 8" cannot be generated between the
; next two instructions when [0:20h] already contains the
; new value but [0:22h] still contains the old one (so the
; combination of [0:20h] and [0:22h] is invalid)
cli
; We write the address of our handler to 0:0x20
mov word ptr [es:20h], offset hookHandler
mov [es:22h], cs
; Now we can enable interrupt generation again
sti
; Actually perform the program
...
; Uninstall the interrupt hook
cli
call uninstallHook
; The program finishes normally
...
; If the program took longer than 120s, we get here!
timeoutCode:
cli
; Ensure DS contains the correct value
push cs
pop ds
; Restore the original stack
lss sp, origStack
; Uninstall the hook
call uninstallHook
; The program finishes due to a timeout
...
uninstallHook:
; Uninstall the hook; note: "cli" has already been called!
mov ax, 0
mov es, ax
mov ax, origAddress
mov [es:20h], ax
mov ax, origAddress+2
mov [es:22h], ax
sti
ret
; The actual handler is called 182 times in 10 seconds
; Note that a handler must not modify any registers but
; it must "push" all registers that it modifies and
; restore the original values using "pop"
hookHandler:
; Decrement the variable "timeout"
dec word ptr [cs:timeout]
; Is it zero?
jnz notZero
; It is zero; replace the address of the instruction
; that is executed after the interrupt by "timeoutCode"
push bp
mov bp,sp
mov word ptr [ss:bp+2], timeoutCode
mov word ptr [ss:bp+4], cs ; (segment of timeoutCode)
pop bp
notZero:
; Jump to the original handler of the BIOS which will
; do the rest (e.g. handle the interrupt controller)
jmp dword ptr [cs:origAddress]
origAddress DW 0,0
origStack DW 0,0
timeout DW 2184
Run Code Online (Sandbox Code Playgroud)