回数其实就是回合的意思,我为了忠实于英杰传的源代码而这么命名,那么这两者这间有什么关联,并且与其他内容如何联系,这节先简要研究一下。
这两者最大的关系是它们在游戏中的内存空间是在一起的,而且都和DS:CF6A这个地址有关,下面看以下英杰传代码,反汇编代码都是可以写注释,每条语句后面加上; 就是它的注释,也可以放在两条语句中间。
; si在此之前已经=CF6A
seg002:2D16 C6 44 02 00 mov byte ptr [si+2], 0 ;現在回數设为0
seg002:2D1A C6 44 04 01 mov byte ptr [si+4], 1 ;第0回數天气设为1
; di在此之前已经=CF6A
seg002:3D26 8A 45 03 mov al, [di+3] ;限制回數
seg002:3D29 38 45 02 cmp [di+2], al ;比较現在回數与限制回數
seg002:3D2C 72 03 jb short loc_30C51 ; [di+2]<[di+3] 跳转
seg002:3D2E E9 C9 00 jmp loc_30D1A
seg002:3D31 loc_30C51: ; CODE XREF: sub_30C28+24↑j
seg002:3D31 57 push di
seg002:3D32 9A 62 3E F2 2C call sub_30D82 ;更新天气值
seg002:3DFA loc_30D1A: ; CODE XREF: sub_30C28+26↑j
seg002:3DFA 1E push ds
seg002:3DFB 68 28 30 push offset asc_437F8 ; "戰鬥拖長了,所以要撤退.\n"
通过这两段我们可以看到,ds:[CF6A+2]保存的是現在回數,ds:[CF6A+3]保存的是限制回數,ds:[CF6A+4]保存的是天气值,每当回數+1都会更新一次天气值这与我们在游戏中看到的是一样的。
seg002:3D72 57 push di
seg002:3D73 9A D8 3F F2 2C call sub_30EF8 ;天气
seg002:3D78 8A D8 mov bl, al
seg002:3D7A 2A FF sub bh, bh
seg002:3D7C 03 DB add bx, bx
seg002:3D7E FF B7 14 2D push word ptr [bx+2D14h] ; 0"晴", 1"雲", 2"雨"
seg002:3D82 8A 45 02 mov al, [di+2] ;現在回數
seg002:3D85 2A E4 sub ah, ah
seg002:3D87 40 inc ax
seg002:3D88 50 push ax
seg002:3D89 1E push ds
seg002:3D8A 68 42 30 push offset asc_43812 ; "第%w回合 %s"
seg002:3D8D 6A 00 push 0
seg002:3D8F 9A 42 32 F6 1C call sub_201A2
seg002:3D94 83 C4 0A add sp, 0Ah
dseg:2D0A B4 B8 00 asc_434DA text "Big5", '晴',0 ; DATA XREF: dseg:2D14↓o
dseg:2D0D B6 B3 00 asc_434DD text "Big5", '雲',0 ; DATA XREF: dseg:2D16↓o
dseg:2D10 AB 42 00 asc_434E0 text "Big5", '雨',0 ; DATA XREF: dseg:2D18↓o
dseg:2D14 0A 2D dw offset asc_434DA ; "晴"
dseg:2D16 0D 2D dw offset asc_434DD ; "雲"
dseg:2D18 10 2D dw offset asc_434E0 ; "雨"
这就是我们在每个回合开始的时候看到的本回合信息,%w会被替换为(現在回數+1),而%s会被替换为"晴", "雲", "雨",根据bx来确定。
[bx+2D14h]可能看着既熟悉又陌生,这其实是汇编语句中的动态获取内存地址的值的方法。
当sub_30EF8函数返回的是0时,获取的是DS:[0+2D14], 也就是2D0A,指向"晴"这个字符串,返回的是2时,获取的是DS:[2+2+2D14]=DS:[2D18]
;这个函数arg_0值只有CF6A
seg002:35E6 8B 5E 06 mov bx, [bp+arg_0]
seg002:35E9 2A E4 sub ah, ah
seg002:35EB 8A 47 03 mov al, [bx+3] ;限制回數
seg002:35EE 50 push ax
seg002:35EF 8A 47 02 mov al, [bx+2] ;現在回數
seg002:35F2 40 inc ax
seg002:35F3 50 push ax
seg002:35F4 1E push ds
seg002:35F5 68 6D 2F push offset asc_4373D ; "現在回數%2w/%2w"
seg002:35F8 9A 32 32 F6 1C call sub_20192
seg002:35FD 83 C4 08 add sp, 8
seg002:3600 EB 15 jmp short loc_30537
seg002:3602 loc_30522: ; CODE XREF: sub_303DA+12A↑j
seg002:3602 8B 5E 06 mov bx, [bp+arg_0]
seg002:3605 2A E4 sub ah, ah
seg002:3607 8A 47 03 mov al, [bx+3] ;限制回數
seg002:360A 50 push ax
seg002:360B 1E push ds
seg002:360C 68 7E 2F push offset asc_4374E ; "限制回數%2w"
seg002:360F 9A 32 32 F6 1C call sub_20192
seg002:3614 83 C4 06 add sp, 6
seg002:3617 loc_30537: ; CODE XREF: sub_303DA+146↑j
通过上面的分析,这段是什么意思应该不难看出吧。
接下来就是如何更新天气的部分了,龙吟前辈已经给出了其中很详细的更新过程了,这部分就当是朝花夕拾了,想当初我刚开始研究找到这部分的代码,为了把main.exe的内容与之对应上,就费了一会功夫,希望今后其他人的研究就别这么吃力了。
seg002:3E62 sub_30D82 proc far ; CODE XREF: sub_30C28+2A↑P
seg002:3E62
seg002:3E62 var_2 = byte ptr -2
seg002:3E62 var_1 = byte ptr -1
seg002:3E62 arg_0 = word ptr 6 ;这个函数arg_0值只有CF6A
seg002:3E62
seg002:3E62 C8 02 00 00 enter 2, 0
seg002:3E66 56 push si ;在栈中保存si原来的值
seg002:3E67 8B 76 06 mov si, [bp+arg_0]
seg002:3E6A B8 06 00 mov ax, 6
seg002:3E6D 9A E0 3D F6 1C call sub_20D40 ;产生天气随机数
seg002:3E72 88 46 FE mov [bp+var_2], al
seg002:3E75 8A 44 04 mov al, [si+4] ;获取上回合天气代码
seg002:3E78 88 46 FF mov [bp+var_1], al
seg002:3E7B 3A 46 FE cmp al, [bp+var_2] ;比较上回合天气代码与天气随机数
seg002:3E7E 76 05 jbe short loc_30DA5 ;
seg002:3E80 FE 4E FF dec [bp+var_1] ;上回合天气代码大于随机数,更新为代码-1
seg002:3E83 EB 0B jmp short loc_30DB0
seg002:3E85 loc_30DA5: ; CODE XREF: sub_30D82+1C↑j
seg002:3E85 8A 46 FE mov al, [bp+var_2]
seg002:3E88 38 46 FF cmp [bp+var_1], al ;比较上回合天气代码与天气随机数
seg002:3E8B 73 03 jnb short loc_30DB0
seg002:3E8D FE 46 FF inc [bp+var_1] ;上回合天气代码小于随机数,更新为代码+1
seg002:3E90 loc_30DB0: ; CODE XREF: sub_30D82+21↑j sub_30D82+29↑j
seg002:3E90 80 7E FF 00 cmp [bp+var_1], 0 ;比较更新后的天气代码与0
seg002:3E94 75 06 jnz short loc_30DBC
seg002:3E96 C6 44 04 05 mov byte ptr [si+4], 5 ;相等,天气代码由0改为5
seg002:3E9A EB 12 jmp short loc_30DCE
seg002:3E9C loc_30DBC: ; CODE XREF: sub_30D82+32↑j
seg002:3E9C 80 7E FF 05 cmp [bp+var_1], 5 ;比较更新后的天气代码与5
seg002:3EA0 75 06 jnz short loc_30DC8
seg002:3EA2 C6 44 04 00 mov byte ptr [si+4], 0 ;相等,天气代码由5改为0
seg002:3EA6 EB 06 jmp short loc_30DCE
seg002:3EA8 loc_30DC8: ; CODE XREF: sub_30D82+3E↑j
seg002:3EA8 8A 46 FF mov al, [bp+var_1]
seg002:3EAB 88 44 04 mov [si+4], al ;保存本回合天气代码
seg002:3EAE loc_30DCE: ; CODE XREF: sub_30D82+38↑j
seg002:3EAE ; sub_30D82+44↑j
seg002:3EAE 5E pop si ;还原si原来的值
seg002:3EAF C9 leave
seg002:3EB0 CA 02 00 retf 2
seg002:3EB0 sub_30D82 endp
似乎还漏了一点,为什么0, 1, 2代表0"晴",3代表1"雲",4, 5代表2"雨",看了这部分就不会迷惑了吧。
seg002:3FD8 sub_30EF8 proc far
seg002:3FD8
seg002:3FD8 arg_0 = word ptr 6 ;这个函数传入的参数值只有CF6A
seg002:3FD8
seg002:3FD8 55 push bp
seg002:3FD9 8B EC mov bp, sp
seg002:3FDB 8B 5E 06 mov bx, [bp+arg_0]
seg002:3FDE 8A 5F 04 mov bl, [bx+4] ;获取天气代码
seg002:3FE1 2A FF sub bh, bh
seg002:3FE3 8A 87 4E 2E mov al, [bx+2E4Eh]
seg002:3FE7 C9 leave
seg002:3FE8 CA 02 00 retf 2
seg002:3FE8 sub_30EF8 endp
dseg:2E4E 00 00 00 01 02 02 db 0, 0, 0, 1, 2, 2
[ 本帖最后由 漫漫苦短 于 2025-2-10 19:44 编辑 ]