谈edit.com的半自动换行 By 竹闲(于2008-12-15发表)

1.旧弊
  winXP、win2K的edit.com, 长度为6'9886字节, 版本号为 2.0.026,
用命令行"edit [/nnn] filename" (例如 edit /078 test.txt)可以:
打开文本文卷 且每隔nnn个字符 换一次行。
其弊为:1)回车符0d、0a被视为可见字符输出;2)半个汉字。
现拟改动九处,约六十个字节,详细修改如下:

2.新法
;//------------------------------------------------------------
copy edit.com edit.bin

debug edit.bin
-L

-u 8a2e L3
   0AF6:8A2E 803C09     CMP  BYTE PTR [SI],09
-a 8a2e
   0AF6:8A2E:           call 10fc
   0AF6:8A31:

-u 8a51 L3
   0AF6:8A51: 397E04    CMP  [BP+04],DI
-a 8a51
   0AF6:8A51:           call 1118

-u 8a5a L2
   0AF6:8A5A: 7405      JZ   8A61
-a 8a5a
   0AF6:8A5A:           db   90,90
   0AF6:8A5C:

-u 8a6c L5
   0AF6:8A6C: C746FA0000 MOV WORD PTR [BP-06],0000
-a 8a6c
   0AF6:8A6C:           xor  ax,ax
   0AF6:8A6E:           call 116c
   0AF6:8A71:

-u 8a82 L2
   0AF6:8A82: 7425      JZ   8AA9
-a 8a82
   0AF6:8a82:           db   90,90

-d 1fa6:0dcc L2
   1AF6:0DCC:                                     B3 B3                   ..
-a 1fa6:0dcc
   1AF6:0DCC:           db   7c,7c
   1AF6:0DCE:

-d 1fa6:0fb3 Le
   1AF6:0FB0           52 36 30 30 30-0D 0A 2D 20 73 74 61 63      R6000..- stac
   1AF6:0FC0  6B                                                k
-a 1af6:0fb3
   1AF6:0FB3            mov  al,[15c8]
   1AF6:0FB6            not  al
   1AF6:0FB8            and  al,[si]
   1AF6:0FBA            mov  [15c8],al
   1AF6:0FBD            cmp  by[si],9
   1AF6:0FC0            ret
   1AF6:0FC1

-d 1fa6:0fcf Le
   1FA6:0FC0                                               52                  R
   1FA6:0FD0  36 30 30 33 0D 0A 2D 20-69 6E 74 65 67            6003..- integ
-a 1fa6:0fcf
   1FA6:0FCF            cmp  by[15c8],80
   1FA6:0FD4            mov  ax,[bp+4]
   1FA6:0FD7            db   72,01
   1FA6:0FD9            dec  ax
   1FA6:0FDA            cmp  ax,di
   1FA6:0FDC            ret
   1FA6:0FDD

-d 1fa6:1023 L7
   1FA6:1020           72 75 6E 2D 74-69 6D                        run-tim
-a 1fa6:1023
   1FA6:1023            mov  [15c8],al
   1FA6:1026            mov  [bp-6],ax
   1FA6:1029            ret
   1FA6:102A
-w
Writing 110FE bytes
-q

ren edot.bin edit.exe
;//------------------------------------------------------------
以上 小写字符串为 程序员手工键入, 大写字符串为 电脑反馈。
修改後, 用"edit.exe /078 test.txt" 可
浏览"中英文混合、物理行长度达512字节"的测试文卷,
对屏幕每一行临时行, "当其长度 达到 77或78字节" 或 "该行已用
回车符结束" 时都会自动换行, 无"半个汉字"现象。达到自动换行效果,
但由于 不能中途改变行宽、某行插入字符後未能及时自动调整行宽,
所以属于半自动换行。
若不能看到汉字,请多调用几次"graftabl 936"。
  很高兴 您能看到此处。但下面纯粹是详论修改原因,作者写得
冗长拖沓,对 反汇编代码 与 具体技术范畴不感兴趣的 各位朋友 请关闭
本窗口吧, 可以免按 许多下 pageDown了。:)


3.因由
;//------------------------------------------------------------ 
debug edit.com
-L
-g 33
-t
-g 103
-t
-u 6bed
 6BED:3976F8    CMP  [BP-08],SI
 6BF0:756C      JNZ  6C5E
 ....
 6C5E:803C09    CMP  BYTE PTR [SI],09;//应改为 call f32c
 6C61:7517      JNZ  6C7A
 6C7A:47        INC  DI;//x++
 ....
 6C81:397E04    CMP  [BP+04],DI;//即"cmp width,x" //应改为 call f348
 6C84:7216      JB   6C9C
 6C86:837EF400  CMP  WO[BP-0C],0;//即"cmp open_mode,‘按binary打开’"
 6C8A:7405      JZ   6C91;//应改为 nop nop
 6C8C:803C0A    CMP  BY[SI],0A;//即"if(*pSrc == '\n')"
 6C8F:740B      JZ   6C9C;//即"goto label_换行"
 6C91:AC        LODSB;//即"c=*(pSrc++)"
 ....
 6C98:8807      MOV  [BX],AL;//即"*pDest=c"
 6C9A:EB64      JMP  6D00

label_换行 :
 6C9C:C746FA0000 MOV WO[BP-06],0;//应改为 xor ax,ax call f39c
 ...
 6CAE:837EF400  CMP  WO[BP-0C],0;//即"cmp open_mode,‘按binary打开’"
 6CB2:7425      JZ   6CD9;//应改为 nop nop
 ....
 6CB9:803C0A    CMP  BY[SI],0A;//即"if(*pSrc == '\n')"
 6CBC:7503      JNZ  6CC1
 6CBE:46        INC  SI;//即"pSrc++"
 6CBF:EB05      JMP  6CC6
 ....
 6CFE:33FF      XOR  DI,DI;//即"x=0"
 6D00:837EE600  CMP  WO[BP-1A],0
 6D04:7403      JZ   $+5
 6D06:E9E4FE    JMP  6BED;//即"do{}while"

-d cs:f32c
 F320:                                     52 36 30 30               R600
 F330: 30 0D 0A 2D 20 73 74 61-63 6B 20 6F 76 65 72 66   0..- stack overf
 F340: 6C 6F 77 0D 0A 00 03 00-52 36 30 30 33 0D 0A 2D   low.....R6003..-
 F350: 20 69 6E 74 65 67 65 72-20 64 69 76 69 64 65 20    integer divide
 F360: 62 79 20 30 0D 0A 00 09-00 52 36 30 30 39 0D 0A   by 0.....R6009..
 F370: 2D 20 6E 6F 74 20 65 6E-6F 75 67 68 20 73 70 61   - not enough spa
 F380: 63 65 20 66 6F 72 20 65-6E 76 69 72 6F 6E 6D 65   ce for environme
 F390: 6E 74 0D 0A 00 FC 00 0D-0A 00 FF 00 72 75 6E 2D   nt..........run-
 F3A0: 74 69 6D 65 20 65 72 72-6F 72 20 00               time error .

-q
;//-----------------------------------------------------------------------

  自cs:6bed到cs:6d09, 为函数 uint16 建立行表(uint16 u1,
uint16 *p,uint16 u2,uint16 width) 的一部分, 属于do-while循环。
当open_mode==rb 时,把"\r\n"视为可见字符 来 输出。现将6c8a与6cb2
两处改nop,已可让程序读到0d0a时 改作回车换行处理, 因而已能正常地
浏览纯英文的长文档(每一个自然段 都上百个字符 才有一个换行符的
那种文档), 达到 半自动换行 效果。
  以 by[15c8]的最高位, 作为 "pSrc是否指向汉字的首字节"的 flag,
则有f348、f39c与f32c三处程序。
即将原程序6c81处的 cmp width,x 改为 call f348,
 而 f348处内容为 return(cmp width-(flag?1:0),x);
将原程序6c9c处的wo[bp-6]=0 改为 call f39c,
 而 f39c处内容为{flag=0;wo[bp-6]=0;return};
此外,为在每一次fgetch时 都刷新flag,须在do-while的首部 嵌入刷新代码。
不敢改动6bed处(它涉及fread),就拿6c5e来开刀了。即将原程序6c5e处的
 cmp *pSrc, '\t' 改为 call f32c, 而 f32c处内容为
{flag=flag? FALSE: ((0x80 & *pSrc)?TRUE:FALSE);
 return(cmp *pSrc, '\t')};
  至于那两个B3B3改7C7C,是ascii竖条改为'|'而已。
  到此,改动完毕,请存盘测试。

4.余波
  win98的edit.com, 长度为7'2174字节, 版本号同为 2.0.026,
程序与上类似,为:
;//------------------------------------------------------------ 
debug edit.com
-L
-g 33
-t
-g 103
-t
-u 70ed
 70ED:3976F8    CMP  [BP-08],SI
 70F0:756C      JNZ  715E
 ....
 715E:803C09    CMP  BYTE PTR [SI],09;//应改为 call fc02
 7161:7517      JNZ  717A
 717A:47        INC  DI;//x++
 ....
 7181:397E04    CMP  [BP+04],DI;//即"cmp width,x" //应改为 call fc1e
 7184:7216      JB   719C
 7186:837EF400  CMP  WO[BP-0C],0;//即"cmp open_mode,‘按binary打开’"
 718A:7405      JZ   7191;//应改为 nop nop
 718C:803C0A    CMP  BY[SI],0A;//即"if(*pSrc == '\n')"
 718F:740B      JZ   719C;//即"goto label_换行"
 7191:AC        LODSB;//即"c=*(pSrc++)"
 ....
 7198:8807      MOV  [BX],AL;//即"*pDest=c"
 719A:EB64      JMP  7200

label_换行 :
 719C:C746FA0000 MOV WO[BP-06],0;//应改为 xor ax,ax call fc72
 ...
 71AE:837EF400  CMP  WO[BP-0C],0;//即"cmp open_mode,‘按binary打开’"
 71B2:7425      JZ   71D9;//应改为 nop nop
 ....
 71B9:803C0A    CMP  BY[SI],0A;//即"if(*pSrc == '\n')"
 71BC:7503      JNZ  71C1
 71BE:46        INC  SI;//即"pSrc++"
 71BF:EB05      JMP  71C6
 ....
 71FE:33FF      XOR  DI,DI;//即"x=0"
 7200:837EE600  CMP  WO[BP-1A],0
 7204:7403      JZ   $+5
 7206:E9E4FE    JMP  70ED;//即"do{}while"

-d cs:fc01
 FC01:    52 36 30 30 30 0D 0A-2D 20 73 74 61 63 6B 20   R6000..- stack 
 FC10: 6F 76 65 72 66 6C 6F 77-0D 0A 00 03 00 52 36 30  overflow.....R60
 FC20: 30 33 0D 0A 2D 20 69 6E-74 65 67 65 72 20 64 69  03..- integer di
 FC30: 76 69 64 65 20 62 79 20-30 0D 0A 00 09 00 52 36  vide by 0.....R6
 FC40: 30 30 39 0D 0A 2D 20 6E-6F 74 20 65 6E 6F 75 67  009..- not enoug
 FC50: 68 20 73 70 61 63 65 20-66 6F 72 20 65 6E 76 69  h space for envi
 FC60: 72 6F 6E 6D 65 6E 74 0D-0A 00 FC 00 0D 0A 00 FF  ronment.........
 FC70: 00 72 75 6E 2D 74 69 6D-65 20 65 72 72 6F 72 20  .run-time error 
 FC80: 00                                               .
;//------------------------------------------------------------ 
修改方法 亦与上类似。

并不是所有的贴子都是原创,此时作者均指发表的人而不是文章的作者,作者会说明是否是转贴