EMU618社区

 找回密码
 立即注册
搜索
查看: 489|回复: 8

[原创HACK教程] [FC][SRAM扩容教程(Mapper 4为例)]

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 3 D# L$ n: w: V+ P- b! v$ b
; q: p" v& K, L& u2 h
[FC][SRAM扩容教程(Mapper 4为例)]
; t4 L5 x; C8 z( i: O! y1 i3 O( m3 ?+ c+ n2 k8 H4 F0 V
时间:2017.4.288 v0 w* e: q0 f8 E$ s: D
作者:FlameCyclone
4 z, N# S, b) ~$ s7 @工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
+ h, t  C( A: n  F5 z# YROM:双截龙2(J).nes
: G/ Q( F  X7 O0 Q' ~2 e适用:没有使用SRAM的ROM
, l" q) ^2 o6 s; `- n9 `" b/ h0 e1 D8 \9 P! ~1 D2 d  ?+ l/ W2 i
首先用Hxd打开ROM:
0 T. ?& n& e0 W' \- f3 p
8 k+ t- ~1 a) S然后扩容:
; T$ C0 q' G* I8 ~3 |2 w9 S; E* P/ {8 i) F( P0 l

) n' Q0 Y7 N6 t. h% `* w) E0 p4 Z8 ?) C$ N, N! ^. g$ O6 R# g6 Z

4 D" t$ I8 H% y/ w/ K9 j& {6 [4 W- E. M+ M+ l* r
, P+ ~% N, X# i( ?6 b
. U: \4 v' F4 h: |8 I' b3 b
5 a4 f8 J& Z  ~& s; S! z
/ `/ {6 G$ L9 J7 O( m
先看看任天堂产品系统文件对NES文件的说明:
- d6 M9 m: t% M2 i$ mNES文件格式* a( h5 c1 A( e: w3 _2 ]9 S& h
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 7 k% S5 @9 g) }- Y) H: {
偏移         字节数         内容 , V; J1 j, s% e. B& j  y! v  @
0-3         4         字符串“NES^Z”用来识别.NES文件
; w) O/ Z. L7 h( l0 a- f4         1         16kB ROM的数目 1 X/ w3 d8 H# r! o
5         1         8kB VROM的数目
* d9 {1 G1 N; R3 m' N3 m0 m6         1         D0:1=垂直镜像,0=水平镜像
- L' E& K8 g, r9 j1 b! f                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
* r1 X; `6 D. _% @                    D2:1=在$7000-$71FF有一个512字节的trainer
2 ^2 P9 _+ ]( A7 {                    D3:1=4屏幕VRAM布局   {# [8 ]9 X( [* a- M
                    D4-D7:ROM Mapper的低4位
9 C& `6 y+ j1 A. t& n$ T9 m4 i7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) * ]0 U. h+ x/ e
                    D4-D7:ROM Mapper的高4位
! k8 x( p4 g3 V8-F         8         保留,必须是0
$ G( W. a0 k% ~* ], t- U$ B" |16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
. h1 s6 _2 S) _$ I' Q-EOF         8KxN         VROM段, 升序排列 * t* @+ H. P, G0 I' i

! G1 z# J0 q0 y4 Q3 v* W* q然后知道这个ROM有0x08个PROM和0x10个VROM
% A  ]6 l) [$ c$ g0 l接下来扩展PROM位0x10个:* d! c6 M; v' Z6 B6 t& M
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
% O2 ?% h) e$ z4 r" a3 t1 Y) u. Z
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
4 N3 W. z0 e: N7 m, ?) o- M* C最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
+ ?. `4 M) ]) b) X0 E" I于是可以得到双截龙2的是:9 |" W2 E! b. @. S/ V& f
(8-1)x 0x4000 + 0x10 = 1C010。
7 W( ?: x: G7 a; X! g4 ]) p然后跳转到1C010:
9 S9 @9 A! z9 f+ `, L( T5 |, y" e8 G2 d- P' S; \) m: W6 u- t

- ~, y" m* [/ G' N2 t: ~, W$ F* j8 K1 u
然后插入0x20000字节的FF:
  y3 c8 i/ `& s$ C5 m, G" \9 w" G$ h
. z2 `- e) p$ s3 D7 r" ~% g% d- E: L
+ J, c5 e2 `! j# x7 l$ a
然后保存:9 L- N, ~  y* `! m" X+ s! T& _
- q. X- {' P( C# R
* b% b: c3 b' Q
用FCEUX打开正常运行:% y8 G6 Y* s" G7 g9 B. {1 _

, u& A- e& e6 d( X! c$ T/ m查看文件信息:
" a+ G" q1 A4 o; H/ B7 W# B0 u5 t- e7 G( w- [2 W2 H  E. P0 B: B# c
$ E' _: T; y) S+ V/ K5 }0 D( o2 q, l
接下来切页:; [6 E4 m1 N0 F, ?- Q( Z
先打开十六进制编辑器:
+ T$ Z& q0 i% U* C8 Y
4 M# ]' }% @( v8 m拉到滑块最后,看看重启中断" p* ^3 [  a9 m  J# c
中断地址         中断         优先权
5 h7 c0 L2 c2 t) ?+ m4 x) q0 c$FFFA         NMI         中
' T) D2 }5 g: ~( q0 o  @$FFFC         RESET         高 7 f! c5 p- W$ _2 n3 X
$FFFE         IRQ/BRK         低 4 @/ n) E! l7 i- a

8 Z- L- x  V: _- yRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
7 B4 }, [) A. p' @2 ^由此可知双截龙2的RESET中断$FF65。
6 H1 M* Q- k. z. B" r. o接下来添加$FF65的执行断点:' u/ q, a4 _" K  b% G8 _
打开调试器:
6 |+ Y% g( l( K- E9 D/ \
4 c% t# a0 A3 q7 Y添加$FF65的执行断点:- h# c3 K  z0 u% \7 a3 @+ ]

; Q. [  L$ V0 `5 O' Q0 e
" j7 Z- ~+ c3 }' }
+ z% R% m4 V8 ]7 W( |) R& V( P3 h单击确定:' N" X3 `/ ^' ^& V# Z
: h' o( O3 M( G; u7 v
: |% U4 p& z' `# d7 r$ L
然后重启ROM:
8 Q5 _8 P) K2 p& ]- p
: h1 d+ l# i2 q调试器此时弹出来:
# j# n7 A# @! H* ?2 s7 ?' h8 ~% L2 E  ^- T9 B, V% M6 s
然后打开Hxd,写一段mapper 4的切换bank程序:
; c/ ^: s5 J% N: ^! L; c先看看mapper 4的说明文档:
$ a* Z9 m$ b; f$ _; oMapper 4
$ J: I4 f4 J; z$ P5 `, \# ^. B
- Z% H8 I8 a9 Z' a$8000:  模式号+ @9 g# s( J5 X; p$ Y* m
        位D0-D2:. Z1 g# _! o0 z" W
        0:选择2KB的VROM存储体映射到PPU的$0000' y7 z* m# d$ N. S7 K
        1:选择2KB的VROM存储体映射到PPU的$08002 Q/ q* R6 V/ w2 `6 y5 s2 x  J+ b) v
        2:选择1KB的VROM存储体映射到PPU的$1000% G% `; I9 ]0 ^. N. Z0 F
        3:选择1KB的VROM存储体映射到PPU的$1400+ [& v1 |; t* M7 X
        4:选择1KB的VROM存储体映射到PPU的$1800
* Z/ a# r2 v3 T+ e7 N        5:选择1KB的VROM存储体映射到PPU的$1C00
6 d! J7 v. N( v7 Y1 \. H. _% N  n6 v        6:选择8KB的ROM存储体映射到$8000
; \+ ~: X! w( c& V        7:选择8KB的ROM存储体映射到$A000) z! d3 g' b, i2 @
        位D6:
$ b  C4 d  a' T1 x        0:允许擦写$8000和$A000( z) c2 k) r/ ~$ O( R: Q+ B
        1:允许擦写$A000和$C000
* T5 v& P3 b$ ^# ]& j" n        位D7:( ~; V, [- F6 ~( t6 ?/ f$ j, D* ?
        0:模式号D0-D2使用普通地址$ N; |0 s6 L( }
        1:模式号D0-D2地址异或$1000  ~8 Y  D* F) m# x. h

  n" ^* c+ q# {8 V3 J' N+ S) y0 V) r( a$8001:  模式页面号
0 E7 H# n+ Y$ m% b  e) \* c+ b        写入一个数(00-07),切换存储体到对应地址
9 I% K+ \# k7 k- a) Q  o/ s. ?; H% k; F; R. R
$A000:  镜像选择$ i+ T0 @7 W7 r, {; D
        0:垂直镜像
  }* [( v! P: n        1:水平镜像
2 U3 Y2 X2 n5 j; B6 a$ L/ Y, B. O
$A001:  SaveRAM 切换7 L' b  }" s6 c+ z
        0:禁用$6000-$7FFF
" [- R- D0 }4 l9 T        1:启用$6000-$7FFF2 P% j. {; P3 p- J6 f- i3 n" _( A, E
$ @6 b7 ~3 P% A. w
$C000:  IRQ计数器
0 @) H3 @; }5 \% h+ u4 k7 p/ V        IRQ计数器的值存储在此处- z4 y2 M3 n. y7 K5 c
. {# m- }2 n: d* ^" n- w
$C001:  IRQ暂存器1 ~5 U- v7 Y3 ]/ B/ |7 i6 h3 A
        IRQ暂存器的值存储在此处$ P0 ~# q) J4 m
3 I* v  r% p! Z  x# E5 e+ K
$E000:  IRQ控制计数器0
0 U% r4 Y: t! x% e/ e. G2 h  X        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
2 N4 i  r1 L$ C. y8 u- T& t  ]& F+ s% |2 [% c
$E001:  IRQ控制计数器1
5 Y: l( U" _1 z, Y! d+ E0 k3 T! `        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来). J5 b4 a9 Z+ v* p
& p6 Q  i, p% v$ c+ f6 G. h( T
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
8 T+ A7 c$ H: S# ]48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 681 P0 W" W+ j! k! a) t
PHA      累加器A入栈8 f/ c. s3 o5 G. y8 D5 t$ _
LDA #$06    设置切bank地址为$8000-$9FFF8 B* d3 _% E- c& K* S
STA $8000
! P# w) n% M4 w# pLDA #$0E    将第0x0E号bank切到$8000-$9FFF4 ?. M& T/ E, A0 [5 M. n
STA $8001
8 V: R" O) Q( FJSR $8000    跳转到子程序$8000" s  i+ D4 F* p$ j5 B; U
PLA     累加器A出栈
; B% K3 Z, l  d( g$ g+ a
# l! j1 |& W  r) _为何切换的bank号是0x0E呢?
! ?' A' [4 D* `; v' y/ Q+ E因为在扩容ROM后,文件PROM结构为:0 X( F0 P, [0 N3 i9 @" v" E
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM6 [; Q5 Z% m! A( {

% E- l( K1 u( EMapper 4切bank一次是切8KB,那么文件结构就是:0 |! s5 o5 s& d  @. o
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
' `" C  Q, `6 m% J因此选择扩容的第一个空白bank就是0x0E号bank。3 [* _4 G$ a' W/ C! ~; H" x/ {

- E5 F) }, l$ d- L4 {) R) |0 h1 r" n. l7 X
然后去调试器找RESET中断中可以放下切页程序的地方:* {7 W7 R8 x; W* g2 Z( k. C, S
首先长度要小于等于自己写的切页程序。1 K9 J0 k9 n! U9 W% C1 D7 _
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
5 `0 r3 P! a; K- u( Q  P+ W2 R+ C/ M+ l
然后跳转到$FF7C:. I; v6 G/ l) a; Q
# ^& o% l' ~. n- z1 U- @( W- S
2 B' d( h  d# {7 i! H4 w! h7 ?

2 W- U" h% s) S
+ b2 `$ A1 Q$ z3 z. h% f1 d3 d0 q复制下可以被替换用于写切页的程序:! s/ y0 U) R  j( c  [
先选中,再复制:% v6 B7 S; S5 ^1 M
9 E- `/ }' S; r! H3 o' T6 C
4 I0 M* Q" k4 N
在Hxd中新建一个文件,把复制的数据粘贴上去:
8 O5 ~, _! U% R. u( Y
/ ^+ s" H4 f; j9 N( f# `% U* m/ C; u( e3 y: M7 D
4 K  v) b1 d3 M* R, k5 }

+ T* F! d5 Y$ Q然后回到十六进制模拟器:
" Z6 m. C. v: P. z; L5 L转到$FF7C对应的ROM地址:6 a) e5 ?7 |$ f, D) `( D
3 Y, N3 O* A4 m6 t
3 Q! F2 ]$ l8 h
然后复制Hxd的切页程序,粘贴到这里:
9 w& `! ^9 A! a4 g) U& ~! f
* h4 b' M* s! Q& Y; W6 X  Z0 P+ |3 N; Z

5 i4 t/ y& {0 Y! W3 g" d没有覆盖的用EA覆盖:1 x3 n- m) _0 j! i2 z0 I9 v2 |3 X

4 i& Y: h# g7 H打开调试器可以看到变化:+ O3 m) o1 l6 R6 o+ u8 o& P
. r/ j. K! E8 v# v
然后添加$FF7C执行断点:
' z9 b& r4 L3 a3 W% _9 Q1 h  v' M1 z  R% W
5 f- _) b/ Q1 [" N" l( {% M* X
" O* a, N5 M3 z# [$ a5 h
单击运行:
  ~. w; F1 f; F: `5 m) y1 q然后程序在$FF7C这里停下来了。. }  h/ u. x+ {- r* d/ \

5 {# q8 f' v* V1 W4 c% @然后单击单步进入慢慢跟踪,直到跳转到$8000:
2 B+ G: a' b2 \( _6 o
! z; |) U2 y* `; _" j然后打开6502_Simulator:
1 \+ i/ b  A0 v" u  g1 |8 f6 K7 A6 l% l
再打开我写的数据搬移程序:
% G2 N% t7 s$ q" q! P' b) G- h7 \: p# F8 I# j* x
2 j. `; [- c4 a
然后修改对应的数据:% F6 F2 u3 u% ^7 ^3 S) A5 G
程序开始地址:修改比如$8100就修改为 .ORG $81000 u- I3 @" R8 F7 b" Q
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$708 y  |7 Y; q+ P* V7 }% B4 G; f
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82; W/ z; Y8 y1 W1 X; |" h: w
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91! E0 u6 b$ B( C3 M0 V3 U
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。. T- \. i4 ?8 C# `, I
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
4 ~9 ^) K, P" N' \7 L6 Z( g1 h4 C2 T& r中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。6 n% b  ~6 V, j- o1 i. L: }7 ?' K
后面的不用管。
% R! e4 Q0 ]3 P+ c2 e, R) Z" E/ |* @8 M' V0 N8 d9 @
设置完数据后单击编译:1 L  L5 X5 _: p8 I+ S, S+ _
7 i5 g  C5 X$ k8 {
然后保存编译文件:
; i, H& A- j0 v% l
! q# o% d: D$ o" W" M; `选择二进制方式保存:' W7 ?& l& x& x

  |' W$ m# h. {) j8 U0 N& O* S/ P' p+ E3 W+ q
用Hxd打开保存的二进制文件:  t, @: P: Q# x  P3 Q
' }) _1 F2 W7 m/ A5 F

# Z- L! v2 E  g. {# }# V跳转到设置的程序开始$8100:: b) b+ J* |" n5 D
: @  X% C2 E$ Y& R& b6 ?

: a) r# j1 }( Q0 Q0 F7 G* k8 O" c4 t: S9 a( u8 e( V& ]
回到FCEUX,转到NES内存的$8100对应的ROM地址:
- Z" L1 Y" d: j7 q+ I; r. ]/ W
9 L4 `. I( I) X0 Y
" l( `. o5 [' w5 n& [. m9 r& |4 k& x3 d. c7 t) {* y
/ }6 j+ Y2 _1 i1 k& c: d# p$ B3 v
3 I& g! @1 ^' Q: Q
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
' ~: D$ |; [) C# l6 }: e& x8 y2 l& }! V4 ]+ x; U! c
% A; ?9 l, ]9 x) ^! G- V$ t

, n5 Q% Y2 R- `2 o/ g% c, a! V$ b; z然后转到NES内存的$8000对应的ROM地址:9 Q3 h* O+ G: j8 r
( f/ a0 l% o' O% P4 ~8 R8 \: N
% k) ]" O& ^, Y& }+ c5 M3 _

. x1 i" ~# ^. s2 Z& N1 X  F0 Y% q/ ^/ U+ [5 r, q
写上如下程序:
9 U# T9 n- r/ q8 H" cA9 80 8D 01 A0 20 00 81  4 I( C1 q* G3 J6 h+ G* w$ ~6 V
LDA #$806 u7 F; j) K* f
STA $A001 可写方式开启SRAM
7 `  J. X9 P8 Q2 U8 a% QJSR $8100 跳转到子程序$8100. [! r5 E) j4 |, l; t# W" W

0 w  n4 I; I1 \9 O然后把Hxd里被覆盖的程序复制过来粘贴在后面:1 C. o  x4 [! K& x" e: \

8 g6 x) t1 ?1 C) b) @( E" K3 @0 q: E8 y4 \1 i2 t) `
1 ^! _1 I7 x2 ?0 O9 P6 ^
末尾补上一个0x60:
6 s, y. U! k( E" URTS 子程序返回
: M4 c: ]9 E# S
* t8 t8 M2 n% z  C, v. E5 E然后单击运行,ROM音乐响起,正常运行:# ~: O2 R6 v) i

/ g; Y- d2 w  I- h7 a' T/ O' r* [+ H% V( x/ I
然后转到NES地址$7000:8 ]! f6 _8 G. l
" L# |  e$ L3 }6 v4 c
: t: \, [( r, L* ?: A! A
' m0 i7 l3 ^5 b, g9 y

2 Y! J- v1 R$ w- f5 |( r0 L7 [
& _) f( s, Y" s9 U; _9 \可以看到,$7000-7FFF都被复制了一片数据。  K2 [/ F! Y8 l5 \0 ^. ^8 n
测试没有问题,然后保存文件:
% b! J% F  l/ j9 |7 x
% t! n, D' L% k6 a! x$ k; {以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。8 S4 G1 E) `8 p) K" m
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。; c% [& a1 ^1 s1 f3 I$ X
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

签到天数: 2060 天

[LV.Master]伴坛终老

发表于 2017-4-29 01:07:47 | 显示全部楼层
好多内容没有心情看了、、、、、
[发帖际遇]: liujunbtx为灾区捐款 6 个 柠檬. 幸运榜 / 衰神榜

签到天数: 1159 天

[LV.10]以坛为家III

发表于 2017-4-29 21:00:16 | 显示全部楼层
火纹外传扩容就可以汉化?

签到天数: 1223 天

[LV.10]以坛为家III

发表于 2017-4-29 22:24:54 | 显示全部楼层
非常好的教程!👍

该用户从未签到

发表于 2017-7-25 15:03:46 | 显示全部楼层
{:4_104:}来学习学习感谢分享
回复 支持 反对

使用道具 举报

签到天数: 30 天

[LV.5]常住居民I

发表于 2017-7-27 21:47:15 | 显示全部楼层
看一下楼主的教程,非常好。
回复 支持 反对

使用道具 举报

头像被屏蔽

签到天数: 5 天

[LV.2]偶尔看看I

发表于 2018-2-8 21:36:34 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

签到天数: 57 天

[LV.5]常住居民I

发表于 2018-10-23 13:32:34 | 显示全部楼层
好东西,看看
回复 支持 反对

使用道具 举报

签到天数: 166 天

[LV.7]常住居民III

发表于 2024-3-22 21:01:24 | 显示全部楼层
进来学习。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|国治模拟精品屋 ( 沪ICP备15012945号-1 )

GMT+8, 2024-5-31 00:13 , Processed in 1.093750 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表