|
本帖最后由 yandagui 于 2017-5-19 10:51 编辑 " N# P) N6 p M
+ H( D+ H' }* g6 E
[FC][按键开关][修改教程]
- t5 U7 f9 H) K6 W [7 x, K时间:2017.5.18
# ^* x! h6 t' \- A1 f3 g作者:FlameCyclone: I* k& f: P! o
工具:FCEUX 2.2.3,Hxd 1.7.7
& l% ]2 D( ^9 ]( _) n/ | O$ IROM一个:魂斗罗(J)_Mapper4.nes
/ O" v( }1 P, K% q: i% x; ]5 \& g' u( U+ {% V; q J6 B
, H4 t% ^$ x; B& ` l修改思路:
+ |* C% Q& r7 e1.先在$C000-$FFFF之间找到一段足够写程序的空白空间,通常是一段连续的FF或者00,是在找不到可以切页(切有空间的bank,注意用完切回原bank)或者扩容后再切页。
: ]* Q3 h9 U' I2.再查找一个系统里没有进行读写的内存地址作为开关,一般在$0000-$07FF之间查找。- M& [! M8 h& d; l
3.查找暂停地址,一般暂停和运行时内存中有一个地址会变化。4 y8 ^: k9 _2 `: I
4.查找按键地址,找到单次按键,就是按一次变化一次的按键地址,按键一般有连续按键和单次按键。(如果没有单次可以自己想办法添加)。5 D. r3 e% l: ^; Q# i& a
5.写按键开关程序,程序思路:当暂停时执行开关程序,非暂停时读取开关的状态,开启就干嘛干嘛,关闭就干嘛干嘛。' a* Y; e5 h3 B6 c' ^! `
6.写跳转,将按键程序跳转到按键开关程序。, F$ j/ M9 U( t* e$ V, I: U u- J
) S& W l8 f, b4 s+ J0 D* D3 U r: D) s6 V" Q6 {
程序流程图(假设暂停后,按上键切换无敌锁定的开关):% p6 {$ j7 O" I( U- Z
* e1 B# Q4 d* ?8 P
8 E9 S2 V0 ^$ v" D8 u/ b6 A" | d接下来开始修改:/ i' N2 K6 t/ }4 g5 ?- ]- c
用FCEUX打开rom:
* [7 Z, b) r! R( x2 U/ |4 h3 M Y2 G & u- ^) s. s& l y! C
然后查找修改中需要的地址空间:
4 U( j' V$ E8 \3 T8 Q l' A6 b这里我就不慢慢找了,直接给出:
, u" S! P$ j8 Y' Y8 s空白空间:$FB30-$FBFF
, u+ S ~' y& M
: b3 q7 Q/ ?& X8 M开关地址:$07A0:8 D" U) `! J- r, @0 R
) N0 e4 j; ?$ [ V0 U3 ^! v
暂停地址:$0025;6 D* D L; W: f4 W, Z5 Z3 L
单次按键地址:$00F5,X(X=0是为P1的$00F5,X=1是为P2的$00F6);' u- s) p* Y1 p0 E
按键程序断点:2 z& L, @- \2 B
打开调试器,设置写断点$00F5:6 f: w/ e) u9 d4 O, Q, ~
$ G' u9 x4 D# M3 v! w! t
8 v. y0 b( T% O8 {9 f. T
) l/ n$ c" [. i+ U* s; V
然后程序暂停了:
. L; i6 \/ A% j( e/ c: p. [/ b
) c/ A1 d: w$ d- i! e( v5 L" e9 v! O6 O E
由此知道按键程序断点在$C1EF;% @: } Y6 J- f% d# \
然后开始编写程序: x' k- o/ j3 S% g
推荐使用6502 Simulator编写,由于本篇教程需要的程序不算复杂,我就直接写机器码了:
7 O9 I- O+ \# x打开Hxd,新建一个文件:
# {" G e4 S- f5 C: ^1 ~# c
8 M$ |' ?6 }$ l然后开始写程序:7 d4 O6 Y) a1 q' Y6 N
95 F5 94 F1 48 A5 25 C9 01 D0 1F B5 F5 C9 08 D0 19 BD A0 07 29 08 D0 0A BD A0 07 09 08 9D A0 07 D0 08 BD A0 07 29 F7 9D A0 07 BD A0 07 29 08 F0 04 A9 03 95 B0 68 60
- j, Q1 P: r3 C* Q% _
$ J$ j0 v" V" H c& B6 R" W反编译解释:
/ B, \+ Y5 j- |! P+ M95 F5 STA $F5,X 送入单次按键
( O: ?5 T- n( d$ @94 F1 STY $F1,X 送入连续按键
$ O3 ]% o" |! z, x48 PHA 累加器A入栈
9 @) h% u$ B1 F& ] d- iA5 25 LDA $25 读取暂停状态4 H- ]0 c* {$ y
C9 01 CMP #$01 与暂停值#$01比较
+ d6 t9 x8 p. Y% w1 b0 \. \D0 1F BNE 不同则跳转到 开关读取
$ C8 [& M: W; D) m$ W8 b3 I: u9 Q% F% N
读取按键值:7 U+ Q" R- t: \( C) Z7 j; a
B5 F5 LDA $F5,X 读取单次按键键值
# l) u( {3 y0 BC9 08 CMP #$08 与上键键值比较
5 I( ^/ [# ^7 z: y! K+ w( SD0 19 BNE不同则跳转到 开关读取0 S' y; x5 c6 Y) n4 H$ V) E
D; G! r- Z: h2 m8 ` y
读取开关当前状态:
" P3 W. G9 z, M* \4 o ~( aBD A0 07 LDA $07A0 读取开关数据# a' }* D% F( m% b
29 08 AND #$08 判断D4位是否设置$ y' F$ L# B5 ]2 U0 o5 n" O% ?* E
D0 0A BNE 不同则跳转到 清除开关的D4位
, m6 o' a$ K8 h2 V, L% C4 _
3 {2 f8 b7 N! D' V( o9 Q, v9 I设置开关的D4位:
M: ]4 R; K1 Z6 G' gBD A0 07 LDA $07A0,X 读取开关数据& k; Q6 |, o5 q' L" F: m
09 08 ORA #$08 设置D4位% b c! w) o4 D: l
9D A0 07 STA $07A0,X 送入开关
; y* j" \& H+ h7 qD0 08 BNE 不同则跳转到 开关读取* j1 f' k; q) U$ y0 R9 T
清除开关的D4位:6 M5 C# ^" p6 f( z5 a
BD A0 07 LDA $07A0,X 读取开关数据0 K8 O# w" I6 x! A) E; m; P2 g5 O
29 F7 AND #$F7 清除D4位3 U2 C0 C: ~/ x
9D A0 07 STA $07A0,X 送入开关6 c% U9 X7 J! m, Z9 G
( t6 G- u5 o( n. x3 J' ^9 Z开关读取:
* S# @0 ^) s: ]% P0 q, j+ ~" NBD A0 07 LDA $07A0,X 读取开关数据, ?5 ]' x. o6 I9 e# N
29 08 AND #$08 判断D4位是否设置- ]( [% b1 l d) I7 a# d$ w
F0 04 BEQ 如果未设置则跳转到 出栈并返回
' n- ^. X) M2 x8 p! d( S; {4 u5 Y, s6 j8 r; p4 _ ^' d. j% ]
无敌锁定:6 S O/ C4 s4 ?$ @$ N
A9 03 LDA #$05 取一个立即数#$03( p$ d- C2 \1 c G: O
95 B0 STA $B0,X 送入无敌地址
% j( T+ u$ ?# M$ W) B z7 ?+ \6 O' m6 {! j; @
出栈并返回:
- ~' r9 o4 m d( N9 v2 c* B68 PLA 累加器A出栈1 I$ Z' m& Y- D g% m
60 RTS 子程序返回7 G6 O( e- ?3 j
; k! A5 F5 t4 l5 @7 `. r- y/ ?8 j! e$ `( p* p
然后把程序粘贴到NES内存$FB50对应的ROM地址:" @: Y4 r4 v3 [& x k4 k9 j& q
单击右键:; }$ J2 H# Y( c0 G( T
* i1 D4 a" _( I) m+ \

2 S4 E; |! T& L/ s! D * S) t7 D0 k. A
" _8 Y0 F$ g# R7 w+ a) K) Y
# c% z# v" t9 {9 y
然后跳转到按键程序地址$C1EF对应的ROM地址:7 {' W( w( U& @7 r* r4 s% }/ P

+ H4 E& C- b4 w1 f5 j3 uCtrl+A:
, F; u7 a8 c$ @5 s& v- u
; Z& K3 |, I& Q4 U1 A - ^0 H3 V$ h; F, n) A- ]+ ^/ D
2 O" |. b$ r( p( c
9 R+ |! {" a2 O5 Z* E+ m单击右键:/ K0 N3 p' U1 O1 W9 |
3 D6 F$ J0 }- {

; v: B: K1 d5 H# ?. R把源指令修改为20 50 FB EA
* X. m; ]$ w5 D1 ^ `/ jJSR $FB50 跳转到$FB50
5 r/ ?6 w0 V @1 bNOP 无动作' H# Q0 Q2 K' Y. r7 T4 {
注:
, [ N7 w0 W8 _" v如果不修改最后的F1,那么系统会把F1当做操作码来执行,后果不可预知,所以改为NOP才正确。0 [* E( j' D `! b4 c, J" K5 }

! g1 x* ?5 Z3 L& q8 v. c然后双击断点取消断点,再点击运行按钮继续运行:! G) Y7 b' x' _& F. w# j! F

# @+ w3 g: e9 {% M9 M4 r% w暂停后如下:5 c! V2 n0 g5 V- O& x5 D

* p8 z1 X* r C R7 I5 K; Q8 D按一次上键:5 Q! p; l$ ~, o; ~
无敌马上生效了,取消暂停后也一样保持无敌不会掉:1 j7 k3 w7 m: D1 q

% O. i/ N3 ~8 I; v& F# K暂停后然后再按一次上键) ? _' E2 ~7 x; O5 O
此时无敌还没有消失,因为暂停时无敌时间不会减,所以不会消失.
9 T3 k) X8 w/ ^7 I) W: M( ?$ h* H' j
" o5 |5 a& b5 O4 I) q1 C" V按下开始键取消暂停:
3 q- ]0 s# F" E0 \* [- ^: q+ e马上无敌就失效了:4 F9 y0 I1 K; r/ u( x, N- n
# j9 t* m) N* r( M5 n1 h1 R
0 d+ m5 u8 B2 k1 a/ z测试成功,没有什么BUG,接下来保存文件:
; e9 ~) O' T8 B * W" t5 B8 {! N( y; c5 M+ ^

7 E; e4 b7 e( K* T+ L l到此为止修改结束。. y# I7 o# M7 {+ [/ c
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
评分
-
1
查看全部评分
-
|