设为首页收藏本站

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
% K* c! _9 v8 y
% l8 c# ?& g0 ?9 I[FC][SRAM扩容教程(Mapper 4为例)]
& D0 i: W' P: R# l
! t( H3 U! _" {* U/ k6 {' B时间:2017.4.28& H4 N5 R4 A# O
作者:FlameCyclone1 M, r/ ]3 u; d& {
工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
" y3 X3 h1 k8 D0 K0 {ROM:双截龙2(J).nes! t* \3 p% [1 d6 |8 V
适用:没有使用SRAM的ROM
& Z8 R$ \* d. r6 Z/ Y6 v* L
4 ?5 ?- W3 Y  j9 o$ g) ]# {首先用Hxd打开ROM:% g& P. o2 h" x* z( i- Z& e

& d3 o2 [7 t6 t, U; ]( E3 V然后扩容:3 e; j1 X5 s1 W7 j( `* J
2 l/ I: N; G! a
. d! ]1 R0 A$ I8 Q

2 v7 a. e1 X* D7 T+ L8 b1 z) h3 S' `" o! y1 v

- Z3 f" O3 ~, W& q  O5 O1 u7 [( V7 J/ @' ^
( h. J9 f2 w) h3 I! k* S5 J- d

4 q) b. X8 W; I+ y9 [% n+ |4 A1 |/ s& W6 W: k  K/ V
先看看任天堂产品系统文件对NES文件的说明:
. v4 Y1 P% g0 _8 t) s# [" uNES文件格式
/ S3 y( L  [6 z8 b2 G0 {7 H.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
& s, z4 v; A, u# v+ X3 u5 O# j! B& n偏移         字节数         内容
+ k! N4 y. w4 t. m% g, z0-3         4         字符串“NES^Z”用来识别.NES文件
# U8 U7 }7 p, z) o/ T* x8 j4         1         16kB ROM的数目
- f3 w( ]9 Q5 Q9 O0 k5         1         8kB VROM的数目
+ @1 x4 R' u9 z: [2 z) A1 J3 {6         1         D0:1=垂直镜像,0=水平镜像
6 s5 j+ v" z: i* m                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
% ?3 N; F0 Y$ d1 e, ~  B                    D2:1=在$7000-$71FF有一个512字节的trainer
3 N! r( j, @. D; ~5 N                    D3:1=4屏幕VRAM布局 8 I3 s, }3 e  f$ {, M$ Y9 C$ {
                    D4-D7:ROM Mapper的低4位 8 E8 C& Z. y( o3 @9 m4 V, c
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
: u+ Y9 G9 }! B0 \  w6 H" V                    D4-D7:ROM Mapper的高4位
5 w! i. i+ |6 R. T* ]8-F         8         保留,必须是0
6 `- K* y# G# W4 n16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
1 H+ X& I( X# y+ y-EOF         8KxN         VROM段, 升序排列
- A  b5 m* L! Z$ K6 g& t  N" Y+ ]  d7 \; Q! t
然后知道这个ROM有0x08个PROM和0x10个VROM
% Q, U& }  K& }/ J+ f接下来扩展PROM位0x10个:
+ D$ G1 J+ ~9 a) p  J" E先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
0 ?. u0 O* |" M( @3 `
/ R/ [9 J6 z" ]$ I: n由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
. @* M  j9 h3 V! O. l! s& l最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。: }& V1 l* c. [9 V5 e
于是可以得到双截龙2的是:+ d6 Z8 A6 U! q% \
(8-1)x 0x4000 + 0x10 = 1C010。* G/ x+ q: x4 ]- v$ `; n
然后跳转到1C010:
8 Q6 r) I# r8 u0 g! |# C, y/ {3 p" q0 }
, Z) b: W* C! A7 B* |. k( ?- G7 ^
4 v6 G3 u- A' t# @' [
) o0 B3 ~; X! P  H) A$ N然后插入0x20000字节的FF:
& e3 K. x  r  A" U. k& i" K& P# Q+ }4 ?& O9 L0 x

* I  d2 k. _4 L% p
  I5 G  @; _5 s# E1 {, @然后保存:! N$ W2 i8 E* [3 C( ^

2 N" |- s/ y) S: u+ F; I2 n
+ x" ~" I( _0 J用FCEUX打开正常运行:, E. t5 M+ i% k$ G$ r# Y, V
8 ]* h. V" l" |( S( G( b
查看文件信息:% g9 p/ ?9 c- ~! f" w) R* X
; I8 d% o! t4 T; q) K' i7 w4 e$ L' N7 L

; t8 ~8 ?& \" A6 S% F: s' ]; X接下来切页:: J; q$ I* |4 q0 i9 a
先打开十六进制编辑器:
- G5 a- ?# f1 `! h/ ^1 C
: I1 j' \0 [( C; E4 b: i3 Q+ s0 L3 s, P拉到滑块最后,看看重启中断
3 R+ U% T  {* W% A4 s中断地址         中断         优先权
! `" H. D5 ?% D$ K5 I0 G' L$FFFA         NMI         中
' f" X4 ^8 ?7 V- z* @/ Z$FFFC         RESET         高
' b# z& c# b8 z8 Y$FFFE         IRQ/BRK         低
' B" o) o) `* X. x4 B, \/ S; g! l% _7 T4 e, o+ P5 {& f
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。( z/ o. m# |  x! T: i3 l9 A
由此可知双截龙2的RESET中断$FF65。
6 y: w! k8 E9 K0 y- ]; l8 s: F接下来添加$FF65的执行断点:
: J1 F9 q1 }  S& M打开调试器:2 W2 @) O* k" |

# q4 F/ F4 D0 x8 R# O- Q) ?: p添加$FF65的执行断点:$ s% h& b/ ]; v3 p( |/ \

4 d, q# \8 e' e4 `7 K' D6 F+ Z  D7 U! Y
, W; r" K2 g7 m# k/ o. f! o# j& j- E  h& s" L, X. E
单击确定:
2 ?' d5 Z3 e: m; D0 R1 G. u* a" b+ L, ^4 m+ ~

& j  R% t1 L* q2 w( c5 I$ p然后重启ROM:
5 v9 ~! B# V# g) P. S9 i4 X' s6 }9 M/ ]; h" k/ T
调试器此时弹出来:8 i6 C9 o& M3 P+ [$ W+ L* l) \
' x5 Z' V) D( K1 J0 z/ O, e, N+ T
然后打开Hxd,写一段mapper 4的切换bank程序:
- k% C+ _! G- v4 w先看看mapper 4的说明文档:
% ~4 I, v8 K8 J4 {0 D7 v4 @4 k/ V5 ^Mapper 4
1 q( T! S! V$ [- Q0 _# H0 D0 U: m. x. u, l. u. {$ p
$8000:  模式号
4 `9 ?* G0 i( u! I        位D0-D2:
7 C; ^! i4 L' ^2 t" x' R! z- }) a0 I        0:选择2KB的VROM存储体映射到PPU的$00000 O8 d8 \3 t6 f, t
        1:选择2KB的VROM存储体映射到PPU的$0800" J; O# ]+ f7 c% y, t
        2:选择1KB的VROM存储体映射到PPU的$1000
1 A( E2 B) j- a; ?# W9 r, o        3:选择1KB的VROM存储体映射到PPU的$1400. c0 U8 Q9 ^3 b
        4:选择1KB的VROM存储体映射到PPU的$18009 X* T. P% U' N
        5:选择1KB的VROM存储体映射到PPU的$1C007 i$ [# x* ]7 A$ U/ g2 G1 x
        6:选择8KB的ROM存储体映射到$8000
6 e& W9 S" G/ B  T        7:选择8KB的ROM存储体映射到$A000' q7 K5 f; Z) o" m
        位D6:* |! b( ^) u- ~- g1 M; P
        0:允许擦写$8000和$A000
- B$ F4 V. u/ Q5 K  A; e$ h/ ]        1:允许擦写$A000和$C000. h/ Z9 p7 z1 k
        位D7:
" U# @- c! b" }7 m' Y2 B        0:模式号D0-D2使用普通地址4 H0 F# a0 D% I; u. b& n+ ~
        1:模式号D0-D2地址异或$1000. U2 [. i8 T  P: g6 O" T, _

3 g& r+ y! \7 n$8001:  模式页面号
6 Y9 v/ y& |2 R- T) w4 S        写入一个数(00-07),切换存储体到对应地址
. Z# |- {" c) j" ^; \: R. C# Z# e
8 X, K2 s, f' G0 v' ?1 Q7 I, d$A000:  镜像选择5 S: a1 T3 `/ n/ ]6 J- {
        0:垂直镜像$ h8 E% m+ o$ I: `
        1:水平镜像
4 d9 F% }# _; ?
. z" n/ j% \$ \* b- N1 n. {$A001:  SaveRAM 切换
1 v- ~# X4 J% V6 F+ Z, i        0:禁用$6000-$7FFF
6 N; y$ @+ ^9 y8 J6 ?        1:启用$6000-$7FFF' K( Z+ }  d. o, ~" Q2 d" m/ w
4 y2 [6 z2 p) L
$C000:  IRQ计数器( w: j& _% p6 t3 W) o
        IRQ计数器的值存储在此处
* ?" F  M& r+ a1 N* Y7 {& y$ u* W6 }* Z3 _. J
$C001:  IRQ暂存器3 C* k' j6 U; m/ Y- {4 Z
        IRQ暂存器的值存储在此处
8 a6 X; R0 C9 L7 U" o9 T; ?% D& ~
5 K. n# h- \. _) m) w1 C5 P$E000:  IRQ控制计数器08 G) }; u  ]. ?4 w* {+ g" {/ I
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ/ b/ c) I' J, m1 ^% n+ ^

4 G- x- V4 A- d1 O# l- g$E001:  IRQ控制计数器13 m# e) L' `" t
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来); v1 x& n: z2 o
7 G& L& p* h3 x6 m
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:. j9 M. z8 Q5 g% ]4 Q
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68. y  Q- N; P% z  R
PHA      累加器A入栈; D( }1 f  W6 L: O& G: n/ P3 p/ w
LDA #$06    设置切bank地址为$8000-$9FFF
) H, V( `3 u" k  {6 R& DSTA $8000
6 x7 [* m8 l1 ]  w- c# qLDA #$0E    将第0x0E号bank切到$8000-$9FFF
1 v+ d: o; X5 h: Z2 A( y% K: @STA $8001: w0 Z: l) W: ~* r; W
JSR $8000    跳转到子程序$80004 o" d+ _  f. r8 h; Z1 x/ S
PLA     累加器A出栈$ _6 z& o' j+ N% o* O0 q6 V6 Z
$ ], h: o% p: N  M2 ]
为何切换的bank号是0x0E呢?2 a; j) O1 e1 r2 Z8 l/ W1 B9 G
因为在扩容ROM后,文件PROM结构为:
/ Y  {! t* U  a8 u原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
2 K! r3 S1 E1 ]2 v* a  }" a  `0 H* ~) E5 `7 C
Mapper 4切bank一次是切8KB,那么文件结构就是:
  c! m3 a. Q4 A8 Z原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)$ ?$ l# ]6 y) [! u" c( S7 k& {8 j
因此选择扩容的第一个空白bank就是0x0E号bank。/ @. d6 P+ T! V$ e

/ M1 R- z2 C5 |2 @3 i
" i) T& _* k- }: W- d5 ^然后去调试器找RESET中断中可以放下切页程序的地方:8 Z) Q' o- t8 @8 g3 u& W8 E
首先长度要小于等于自己写的切页程序。
/ @! E  s( x: B) H, |: N可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)/ I) h8 x3 D# T# l$ f
, y+ L$ N3 y$ L2 N* p7 h+ W
然后跳转到$FF7C:
, Q& ^4 V8 a( M  C% s
. y+ O/ W: q7 q7 {$ z: O/ T+ r
' L) O0 o3 v' W6 u8 K6 t  f0 }
( h3 M  \) C4 t! ~6 j. `7 _
. O- k/ E1 M: J; Z复制下可以被替换用于写切页的程序:
) U. y  C9 e* t9 l* f先选中,再复制:8 L8 z& {) c/ a
- |8 u0 r2 S. V. p1 I/ m& Y& d2 Y
7 |2 c5 f/ B, f7 c- T; I
在Hxd中新建一个文件,把复制的数据粘贴上去:5 O# n4 P$ w! o$ u

% B8 i; N" q; N6 }6 l" Q/ s
8 p6 @- R: X# U- w! v9 k+ C$ f
! ]2 }' m( z. f& i6 O8 k' P! T3 I& a' z# _/ k
然后回到十六进制模拟器:$ Q. X6 ^0 H7 x8 u/ p
转到$FF7C对应的ROM地址:
( R" I# i8 @! k$ \- s  b; Y/ s' |; W4 i& y1 A0 j. |6 w
# {* o4 F" W5 [3 _7 y# B% x5 r
然后复制Hxd的切页程序,粘贴到这里:
4 f* M: H& F5 y8 G4 m. _( X9 b
  ^  z$ v2 r; L1 B" L: \, [4 ~% q

4 A9 S. q+ \( j8 ~% ?& H7 G没有覆盖的用EA覆盖:
- [  N- g- u3 m9 w# Z7 O' U, K# }  v$ `/ W; K7 M8 U0 W
打开调试器可以看到变化:
, x, m. c8 Y$ Q" r' w$ Y+ M/ W/ F* h$ m8 p& u2 L* r- ?
然后添加$FF7C执行断点:
9 M6 w* Y1 E; L' Q) r5 n1 Z! e$ B' l/ E' J8 f
6 L4 B, A3 Y% _
+ {; H7 I) S" J# h( A
单击运行:
/ U* L8 y: {& K# M) I1 A然后程序在$FF7C这里停下来了。
% X  ~( ?2 H! N9 s/ |
; i/ h7 P5 x  Y% \) x* G然后单击单步进入慢慢跟踪,直到跳转到$8000:+ w( z. c. E. ~* [' `

4 z- I# n0 n/ z- o* i$ Y0 T然后打开6502_Simulator:
2 I, \7 K8 Q& ~! o! j0 d9 B. k5 }9 B
! f. ]+ i( d( m( G# L$ M4 _+ `, b再打开我写的数据搬移程序:2 v+ a  j$ i# N+ }5 {

- U; E4 o! H& g: N! l
. U7 Y1 |5 f5 ~  }然后修改对应的数据:/ Q! `' L/ r  c# _/ Y# E
程序开始地址:修改比如$8100就修改为 .ORG $8100
$ L) @6 z' m8 C4 ~复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
+ n* \1 g& v3 j% i! m# @3 @从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
: A; ]4 o# x6 O5 H+ l想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91( Z" U3 t, ]- G; }4 d
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。6 p! D1 X* s  c, o* G
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。' J8 r! u2 e3 P) W
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。# b. I/ N6 E, W% T% J
后面的不用管。
4 }- [; N* l6 {* A+ ~
) F& k7 F( {, v2 y设置完数据后单击编译:4 G- Y$ i. b" @. e9 y8 J! U

! ]& C* t( s. {0 i1 F/ L然后保存编译文件:
  e0 i, Q/ T/ P) Q6 v4 {1 P! e+ s; s. R) E6 c
选择二进制方式保存:: C/ d: C4 j7 K7 F" k* m

9 X4 ~. b& D! N, p! H
6 _- r8 R; e% ?8 U用Hxd打开保存的二进制文件:8 o$ @8 a" X, f* e/ j
* t" [8 [2 Y4 T0 N8 Y# [

. g  Y. A( |6 z; D2 ?! r9 Y跳转到设置的程序开始$8100:
4 f7 u4 {# i; }
  \) Z; x' n& _- n7 J
$ F% G9 Q$ x3 c) Q6 a% a
, c$ n0 Z6 j4 b6 X$ n回到FCEUX,转到NES内存的$8100对应的ROM地址:
% Y0 `  c8 p* n0 k& g4 V6 z  i. b1 P; H4 |0 z

2 i! {& `2 w7 R! b: _4 i
! ^8 e: M; e! s$ Q  b! E) o$ o) f, F  H+ A" T5 O4 i

" \, i- d& ^/ V8 v; X2 G: R然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
$ P4 R6 @+ Y, }. g0 B8 N+ o1 }" J  @4 |* b- v
! i- A3 }" ?" c' w3 o; ]( z

1 S5 a' B5 c; Y$ V3 L+ ^- V然后转到NES内存的$8000对应的ROM地址:# R4 k6 l4 Q7 _7 i- _9 e. u
) i- x7 g1 L7 I1 a+ [: M% ?
& }$ b" E2 w' |" ?& p6 m0 E

8 J) y. K9 p/ @, m; ?8 C: [  a- ~5 N$ l9 h' B; X) ^, N8 E
写上如下程序:3 Q; Y0 ]2 k+ M# M
A9 80 8D 01 A0 20 00 81  ! _. w$ p0 C/ k
LDA #$80
* [2 i0 e8 C* ]1 @/ y+ ASTA $A001 可写方式开启SRAM
! h) m: V( ~+ D9 C& i3 u/ hJSR $8100 跳转到子程序$81004 Y% O  R8 |" X% }0 h6 k0 P, o  H% @
) E! V/ H: A) r' d
然后把Hxd里被覆盖的程序复制过来粘贴在后面:( Y1 E4 \5 b  b- \! o; k, U5 t

. N: _6 I2 x9 O4 G7 ]  w$ [
6 T9 A7 \7 N, l; r& d7 ?
+ ?2 L1 W$ b2 {7 k" _. U- L末尾补上一个0x60:
4 ^: @0 v" N( K4 MRTS 子程序返回) N- C2 h8 y2 L" y7 F

/ L1 L7 @5 X9 D# c) \$ Y4 _6 Q然后单击运行,ROM音乐响起,正常运行:
" p; a/ H3 T, Z6 |4 L0 ~0 K  s0 i$ n0 c6 I: S' D* j1 h" x" @

7 f6 C# Y; x( g' m# z& D6 h然后转到NES地址$7000:
7 I' q  D0 P+ C. ~& K8 I) t4 \
& u. P3 [2 x$ ?" B; E- e$ {* a) L6 W
$ W" Q6 V8 B! l  V9 Q
5 T8 j; ^* p! i! [$ P$ u6 N
, V) D5 |" s4 q' n) y* }6 }8 i! u: D( k9 \' H) U
可以看到,$7000-7FFF都被复制了一片数据。
0 C4 u$ }2 s0 c测试没有问题,然后保存文件:
& R9 D3 _# w  `' \# A
3 U3 m% V& t6 y+ u# R+ \& y以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
/ o: Q* m: `8 N: d, ~后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
; W! ^! {4 |: l6 R; m
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

×

签到天数: 2060 天

[LV.Master]伴坛终老

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

使用道具 举报

签到天数: 1184 天

[LV.10]以坛为家III

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

使用道具 举报

签到天数: 1633 天

[LV.Master]伴坛终老

发表于 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 | 显示全部楼层
好东西,看看
回复

使用道具 举报

签到天数: 208 天

[LV.7]常住居民III

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-7-16 02:30

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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