?? dos下實現重新啟動或者關機的小程序 .txt
字號:
DOS下實現重新啟動或者關機的小程序
作者:阿豆腐 于2008-6-16上傳
--------------------------------------------------------------------------------
近日,在DOS下測試某個硬件設備,需要執行DOS下的重新啟動。上網搜索,大多是Windows下面的那個shutdown。
經過苦苦探尋,終于找到DOS下的這樣工具。其中附帶了源程序,下面我就對他的程序做一點簡單的分析。原程序名為
shut12.zip,其中提供了源程序。不過他的源程序是 MAGIC ASSEMBLER 編譯器編譯的,和Masm的語法有很大差別,
我動手將它改造為Masm的格式:
;*****************************************************************************
;*DOS下重新啟動/關機的代碼。也是一個簡單的處理輸入參數的例子 SOLD.ASM *
;*****************************************************************************
.model Tiny
.data
WinErr db 'This program cannot be run under Windows.',0ah,0dh,'$'
Txt db 'ShutDown v1.2 www.blacklight.wxs.org',0ah,0dh,'$'
SyntaxTxt db 'Syntax: SHUTDOWN [S(hutdown)|R(estart)]',0ah,0dh,'$'
Question db 'S(hutdown), R(estart), or C(ancel)? $'
NoATX db 'Could not shutdown! No ATX maybe?$',0ah,0dh
KeyOff db 'S'
KeyRes db 'R'
KeyCan db 'C'
KeyEsc db 27
CrLf db 0ah,0d,'$'
FlushMsg1 db 'Flushing SMARTDRV buffers...$'
FlushMsg2 db 'done',0ah,0dh,'$'
.code
Syntax: mov ah,9h
mov dx,offset SyntaxTxt
int 21h
jmp Exit
NoPars: mov ah,9h ;Show question
mov dx,offset(Question)
int 21h
DoAsk: xor ah,ah ;Ask for key
int 16h
cmp al,KeyEsc ;Check if 'Esc'-key pressed
je DoCan
and al,0DFh ;convert AL to uppercase
cmp al,KeyOff ;Check if 'S'-key pressed
je DoOff
cmp al,KeyRes ;Check if 'R'-key pressed
je DoRes
cmp al,KeyCan ;Check if 'C'-key pressed
je DoCan
jmp DoAsk ;Invalid key pressed, ask again...
ShowKey: mov ah,2h ;Show the pressed key
mov dl,al
int 21h
mov ah,9h ;Show CrLf
mov dx,offset(CrLf)
int 21h
ret ;return
.startup
mov ax,160ah ;check for Windows
int 2fh
cmp ax,0000
jne NoWin
mov dx,offset(WinErr) ;print error message
mov ah,9h
jmp Exit
NoWin:
mov ah,9h ;Show program name
mov dx,offset(Txt)
int 21h
mov si,81h
mov al,[si]
cmp al,0Dh ;Check if any parameters given
jz NoPars
mov si,81h ;get parameters
ParLoop: lodsb
cmp al,0d ;if end reached with no result
je Syntax ; show syntax
and al,0DFh ;convert AL to uppercase
cmp al,KeyOff ;check for S parameter
je DoOffW
cmp al,KeyRes ;check for R parameter
je DoResW
jmp ParLoop ;not recognized,goto next char.
DoRes: call ShowKey
DoResW: call FlushSD
db 0EAh
dw 00000
dw 0FFFFh ; jmpf ffff:0000 this instruction will reboot the computer
DoOff: call ShowKey
DoOffW: jmp ATXOff
DoCan: call ShowKey
Exit:
.exit ;exit to DOS
ATXOff: call FlushSD ;flush smartdrive cache
mov ax,5301h ;Function 5301h: APM ?Connect real-mode interface
xor bx,bx ;Device ID: 0000h (=system BIOS)
int 15h ;Call interrupt: 15h
mov ax,530eh ;Function 530Eh: APM ?Driver version
mov cx,0102h ;Driver version: APM v1.2
int 15h ;Call interrupt: 15h
mov ax,5307h ;Function 5307h: APM ?Set system power state
mov bl,01h ;Device ID: 0001h (=All devices)
mov cx,0003h ;Power State: 0003h (=Off)
int 15h ;Call interrupt: 15h
;if the program is still running here, there was an error...
mov ah,9h
mov dx,offset(NoATX)
int 21h
jmp Exit
FlushSD:
mov ah,9h
mov dx,offset(FlushMsg1)
int 21h
mov ax,4A10h ;flush smartdrv/pccache buffers
mov bx,1h
int 1Ah
mov ah,9h
mov dx,offset(FlushMsg2)
int 21h
ret
end
編譯也很順利,唯獨運行就出錯。仔細研究靜態代碼無果,轉用Turbo Debugger調試,
發現 jz NoPars 這條指令對應的機器碼很奇怪,反編譯的結果竟然是
jne 0121 和 jmp 000A 兩條指令,莫非是TD的bug。再換用最老實的debug,結果仍然如此。
這時候就開始懷疑是編譯器本身的問題了,使用 ml /Fl 查看生成的.lst文件:
011A BE 0081 mov si,81h
011D 8A 04 mov al,[si]
011F 3C 0D cmp al,0Dh ;Check if any parameters given
0121 75 03 E9 FEE4 jz NoPars
真的是生成了這樣的代碼,難道真的是Masm的bug么?
先冷靜下來,繼續跟蹤代碼,這個跳轉會導致程序運行到一片“亂七八糟”的地方。
而那個位置實際上應該是我們.code下面的代碼,再仔細查看生成的.com文件中,竟然
沒有.code下面代碼對應的機器碼,就是說編譯過程中,那一段程序沒有生成機器碼。
再進一步分析,com文件是沒有文件頭的,運行期前面256個是dos生成的... ...莫非是
程序結構上的問題,我們上面的程序入口并非在代碼段開始處。于是,修改程序如下,
修改處用紅色標記
;*****************************************************************************
;*DOS下重新啟動/關機的代碼。也是一個簡單的處理輸入參數的例子 SNEW.ASM *
;*****************************************************************************
.model Tiny
.data
WinErr db 'This program cannot be run under Windows.',0ah,0dh,'$'
Txt db 'ShutDown v1.2 www.blacklight.wxs.org',0ah,0dh,'$'
SyntaxTxt db 'Syntax: SHUTDOWN [S(hutdown)|R(estart)]',0ah,0dh,'$'
Question db 'S(hutdown), R(estart), or C(ancel)? $'
NoATX db 'Could not shutdown! No ATX maybe?$',0ah,0dh
KeyOff db 'S'
KeyRes db 'R'
KeyCan db 'C'
KeyEsc db 27
CrLf db 0ah,0d,'$'
FlushMsg1 db 'Flushing SMARTDRV buffers...$'
FlushMsg2 db 'done',0ah,0dh,'$'
.code
.startup
jmp realstart
Syntax: mov ah,9h
mov dx,offset SyntaxTxt
int 21h
jmp Exit
NoPars: mov ah,9h ;Show question
mov dx,offset(Question)
int 21h
DoAsk: xor ah,ah ;Ask for key
int 16h
cmp al,KeyEsc ;Check if 'Esc'-key pressed
je DoCan
and al,0DFh ;convert AL to uppercase
cmp al,KeyOff ;Check if 'S'-key pressed
je DoOff
cmp al,KeyRes ;Check if 'R'-key pressed
je DoRes
cmp al,KeyCan ;Check if 'C'-key pressed
je DoCan
jmp DoAsk ;Invalid key pressed, ask again...
ShowKey: mov ah,2h ;Show the pressed key
mov dl,al
int 21h
mov ah,9h ;Show CrLf
mov dx,offset(CrLf)
int 21h
ret ;return
realstart:
mov ax,160ah ;check for Windows
int 2fh
cmp ax,0000
jne NoWin
mov dx,offset(WinErr) ;print error message
mov ah,9h
int 21h
jmp Exit
NoWin:
mov ah,9h ;Show program name
mov dx,offset(Txt)
int 21h
mov si,81h
mov al,[si]
cmp al,0Dh ;Check if any parameters given
jz NoPars
mov si,81h ;get parameters
ParLoop: lodsb
cmp al,0d ;if end reached with no result
je Syntax ; show syntax
and al,0DFh ;convert AL to uppercase
cmp al,KeyOff ;check for S parameter
je DoOffW
cmp al,KeyRes ;check for R parameter
je DoResW
jmp ParLoop ;not recognized,goto next char.
DoRes: call ShowKey
DoResW: call FlushSD
db 0EAh
dw 00000
dw 0FFFFh ; jmpf ffff:0000 this instruction will reboot the computer
DoOff: call ShowKey
DoOffW: jmp ATXOff
DoCan: call ShowKey
Exit:
.exit ;exit to DOS
ATXOff: call FlushSD ;flush smartdrive cache
mov ax,5301h ;Function 5301h: APM ?Connect real-mode interface
xor bx,bx ;Device ID: 0000h (=system BIOS)
int 15h ;Call interrupt: 15h
mov ax,530eh ;Function 530Eh: APM ?Driver version
mov cx,0102h ;Driver version: APM v1.2
int 15h ;Call interrupt: 15h
mov ax,5307h ;Function 5307h: APM ?Set system power state
mov bl,01h ;Device ID: 0001h (=All devices)
mov cx,0003h ;Power State: 0003h (=Off)
int 15h ;Call interrupt: 15h
;if the program is still running here, there was an error...
mov ah,9h
mov dx,offset(NoATX)
int 21h
jmp Exit
FlushSD:
mov ah,9h
mov dx,offset(FlushMsg1)
int 21h
mov bx,1h
int 1Ah
mov ah,9h
mov dx,offset(FlushMsg2)
int 21h
ret
end
可以看到,我們的修改只是將程序的入口放到了代碼段的起始。編譯運行,沒有報錯,感覺很好。
我們再來比較生成com文件的大小:snew.com 449 字節 vs sold.com 389 字節 。明顯長了一截~
不見了的代碼又重新出現... ...查看jz NoPars 的機器代碼是 74 A8 正確,因此,整個問題得以解決。
總結一下:對于com文件,它沒有文件頭,因此不可以將代碼的入口設置在程序中,否則編譯過程中
會丟失入口之前的代碼,并且程序會將一些跳轉誤認為是段間的跳轉,從而導致許許多多稀奇古怪的問題。
另外,程序提到了smartdrv,這個是dos下的一個硬盤緩沖驅動,加上它之后硬盤的讀寫會先緩沖到內存中,
從而達到加速的目的,也因為如此,在shutdown/reset的時候要記得“請它先”完成讀寫動作。另外,APM
是dos下的電源管理方面的服務,是BIOS提供的。具體的解釋可以在BIOS中斷大全中找到。
--------------------------------------------------------------------------------
<<<上一篇 歡迎訪問AoGo匯編小站:http://www.aogosoft.com 下一篇>>>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -