1. 重定向
以下a.asm,用含't'及回车13的txt,仿command.com,依masm命令行的arg符号值N,(如masm /Darg=0 a;),实现5个DOS打开专用文件(STD_)的6种io重定向:
N=0:是STDIN句柄,先bin方式读1符,再从txt输't'到char
N=1:是STDOUT句柄,输出char的'1'到txt
N=2:是STDERR句柄,输出char的'2'到txt
N=30,31:
3是STDAUX句柄,N=30,从txt输't'到char;N=31,输出char的'3'到txt
N=41:
4是STDPRN句柄,输出char的'4'到txt
asm调用如下功能:
N=0时,44,读/写设备info,敲键符值入100h,其ascii/scan对,入106h,扩展键时,首对,入104h,次值,入101h.
如1,得(31,31,2),ctl_c,得(3,3,2e),F1,得(0,0,3bh),(3bh,0,3bh)
3d,依读写方式,打开txt,保存句柄到si
45,保存STD_句柄的复制值到di
46,将来访问(cx)句柄,变为访问(bx)=(si)句柄,开始重定向
3f/40,用STD_输入/出,字符数1,偏置char
46,将来访问(cx)句柄,变为访问(bx)=(di)句柄,结束重定向
3e,关闭txt
DEBUG行完a.com,对输入,见103h的char,变't',对输出,type txt,见1~4.
ACT=2
IFDEF arg
IF arg GT 2
IF arg EQ 30
ACT=30
ENDIF
IF arg EQ 31
ACT=31
ENDIF
IF arg EQ 41
ACT=41
ENDIF
STD_=arg/10
ELSE
IF arg GE 0
ACT=1
STD_=arg
IF arg EQ 0
ACT=0
ENDIF
ENDIF
ENDIF
ENDIF
IF ACT EQ 2 无效
%OUT /Darg=0|1|2|30|31|41
.ERR
ENDIF
just macro
local j
mov di,es:[26] 敲键,i9写低asc,高scan到[1Ch尾引字++],i16读[1Ah首引字++]到3f功能的ax
sub di,2
cmp di,1ch 未重划键区,现在回头
ja j
mov di,3ch 刚才入尾
j: mov ax,es:[di]
endm
c segment
assume cs:c,ds:c
org 100h
@: jmp @1
char db '0'+STD_
txt db 'txt',0
@1: mov ah,3dh open
mov al,ACT and 1
lea dx,txt
int 21h
mov si,ax han
mov bx,ACT stdin
test bx,63 >41
jnz @3
mov ax,4400h I/O,从/到fil\nul定向,dx=42h\80c4,否则80d3
int 21h
mov ax,4401h
or dl,32 bin
xor dh,dh
push dx
int 21h
mov ax,64 bios数据区seg,1E~3D是键区
mov es,ax
mov ah,3fh 不等回车读
mov cx,1 1符
lea dx,@ 入256
int 21h
test byte ptr cs:[256],255 测扩展
jnz @2
just
mov word ptr txt,ax
mov ah,3fh 取真
inc dx
int 21h
@2: just
mov word ptr txt[2],ax
mov ax,4401h 复原
pop dx
and dx,not 32
int 21h
@3: mov ah,45h duplica
mov bx,STD_
int 21h
mov di,ax han
mov ah,46h force han
mov bx,si
mov cx,STD_
int 21h
mov ah,3fh+ACT r/w=3f/40
mov bx,STD_
mov cx,1
lea dx,char
int 21h
mov ah,46h
mov bx,di
mov cx,STD_
int 21h
mov ah,3eh close
mov bx,si
int 21h
ret
c ends
end @
2. 管道
反汇编DOS_3.31_more,得[100,1e9]是代码区,[1ea,219]是数据区,初值为18/50的1ea/leb,记录屏幕配置行/列数,初值1/1的1ec/1ed,记录已处理行/列数.
流程:
(2.1) 100处,用功能30,取DOS主/次版本号al/ah,若非3/1f,就显1fc处的"MORE: Incorrect DOS version",用110处的int 20,退出
(2.2) 置1ea=19,用int 10,ah=0f,得屏幕列数到1eb,如50
(2.3) 用功能45,复制stdin(bx=0)句柄到bp,如5,用功能3e,关闭stdin句柄
(2.4) 用功能45,复制stderr(bx=2)句柄(也可复制被@1的si句柄),必得刚关闭的句柄0,
为看完满屏而读键盘一符时,从stderr或si读,而不移管道stdin指针.
(2.5) 139处,用功能3f,打开bp句柄,字节数cx=1000,目的地21a,读stdin
用|接入管道时,more不创建新进程.
未接入时,将读键盘行,如读"135",此串及回车0d,换行0a,被存目的地,被存长度5返到ax,键入符数多于cx时,前面cx个字符被存目的地.对^Z单行,ax返回0.
若ax=0,就用14a处的int 20退出,否则
(2.6) 150处,读目的地每个字符到al
读到^Z,则转14a退出
读到0d,置列数1ed=1,转1ac
读到0a,行数1ec加1,转1ac
读到回退符08,当1ed=1,转1ac,否则列数减1后,转1ac
读到制表符09,则1ed置为下个制表位置,转1ac
读到响铃符07,则不占输出列,转1ac
读到其它符,1ed加1,比较屏幕配置列数1eb,不大于则转1ac,否则
1ec加1,再置1ed=1
(2.7) 1ac处,
用功能2,显出al到stdout,以再接管道
若1ec小于屏幕配置行数1ea,则转1e1处,否则用功能09,显出1f0处的"-- More --",用功能0c,且al=08,清键盘输入后,内调功能08,无回显,从stderr读键盘一符
输出2空行,再置1ed=1,1ec=1
(2.8) 1e1处,
cx减1,结果为0,转139,否则转150
成文:
BLKSZ=1000h
c segment
assume cs:c;ds:c,es:c,ss:c
org 100h
@: jmp init
cfg_r db 19h
cfg_c db 50h
cur_r db 1
cur_c db 1
buf db BLKSZ dup (0)
msg db '-more-$'
cr db 13,10,36
init: xor bx,bx
mov ah,45h
int 21h
mov bp,ax
mov ah,3eh
int 21h
mov bx,2
mov ah,45h
int 21h
read: lea dx,buf
mov cx,BLKSZ
mov bx,bp
mov ah,3fh
int 21h
or ax,ax
jnz read1
quit: int 20h
read1: mov cx,ax
mov si,dx
nxtch: lodsb
cmp al,1ah
jz quit
cmp al,13
jnz ?newlin
mov cur_c,1
jmp show
?newlin: cmp al,10
jnz ?back
inc cur_r
jmp show
?back: cmp al,8
jnz ?tab
cmp cur_c,1
jz show
dec cur_c
jmp show
?tab: cmp al,9
jnz ?bell
mov ah,cur_c
add ah,7
and ah,0f8h
inc ah
mov cur_c,ah
jmp show
?bell: cmp al,7
jz show
inc cur_c
mov ah,cur_c
cmp ah,cfg_c
jbe show
inc cur_r
mov cur_c,1
show: mov dl,al
mov ah,2
int 21h
mov ah,cur_r
cmp ah,cfg_r
jb ?endblk
lea dx,msg
mov ah,9
int 21h
mov ax,0c08h
int 21h
lea dx,cr
mov ah,9 1 cr
int 21h
mov cur_c,1
mov cur_r,1
?endblk: dec cx
jz read@
jmp nxtch
read@: jmp read
c ends
end @
