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
        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 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 ax,4A10h ;flush smartdrv/pccache buffers
        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
下一篇>>>