EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 6 U3 k0 h; E+ L. ~; t" Z

3 q  X1 z( k& a# s8 o[FC][SRAM扩容教程(Mapper 4为例)]$ w- t7 }% c3 |- I4 f+ a, a
4 E& b: y- c1 _0 e
时间:2017.4.28' o3 r3 N  K1 `
作者:FlameCyclone
" K% r. M1 Q, `. D- _工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
7 E% }: m$ P' R5 V7 k# r4 B/ UROM:双截龙2(J).nes
8 I) W  g# [1 e$ H适用:没有使用SRAM的ROM+ a& \4 I. r) N' N# P
8 V# C& ?6 T) U+ M/ A
首先用Hxd打开ROM:$ X  v2 Z$ y+ ^3 V+ c) t) i5 N2 n; U
6 g4 m0 P6 ?- T; U) x" P2 }3 K- I
然后扩容:
; C+ h0 m8 t9 A/ S/ b8 N* ]# \, Y& G3 W6 b, v+ [4 y. Q  y% x

- h7 w+ g0 ?/ p' ]+ ]7 y2 h8 {: k0 h$ ^0 W5 ~

& w! N/ a9 Z6 e4 c0 s, Q; K: h$ }* ^& b. H" }" h- u$ j

- Z, E: z+ s- Y; }7 u! q. m& G$ P+ j- x4 u2 _9 T) F) D4 l/ X

2 G2 Z8 w5 `/ x  r2 H7 _8 z6 I% m9 N7 G' ]
先看看任天堂产品系统文件对NES文件的说明:
8 E6 [6 Y9 p4 e; s7 V* T% yNES文件格式
- D1 l9 p1 d" Q6 U  s.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
, z  z4 ]( I4 j- V8 q偏移         字节数         内容
9 j$ ?  M7 V0 v& }0-3         4         字符串“NES^Z”用来识别.NES文件
( [4 P2 H7 h# h6 v5 `4 [* ]4         1         16kB ROM的数目
6 D) b6 N+ K. j2 X; Y! r5         1         8kB VROM的数目
( v  h4 o, G9 ]  {- n3 x7 Z; L. q6         1         D0:1=垂直镜像,0=水平镜像 % |! ^. ~0 J5 s4 i: J; Z+ H- Z
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF 6 G) B4 Q* ]" c% @, y
                    D2:1=在$7000-$71FF有一个512字节的trainer
# w* l! K2 x3 R) ~                    D3:1=4屏幕VRAM布局
) S' Y+ z6 W7 y8 H                    D4-D7:ROM Mapper的低4位 + l3 H) e) R+ u0 F3 w7 \, s/ ~
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
& \' ~! r: F) a. Y                    D4-D7:ROM Mapper的高4位 2 t; A" m% ]3 u6 m: B+ r
8-F         8         保留,必须是0
# k4 b  k# t( U. x$ U4 Y16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
, P$ x) K! r- [" T- n2 x-EOF         8KxN         VROM段, 升序排列 , ~8 g$ O7 b6 j/ x
2 o6 h& T* V/ w* k+ {
然后知道这个ROM有0x08个PROM和0x10个VROM
0 i( C9 S- b. o5 e( ~% f& o: j# a! N接下来扩展PROM位0x10个:' M& o3 L; p5 F, ]4 z; b+ G( _: ?! Z* q
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
- f# y( R) @. ~9 d3 ?8 G
) }" i3 N6 P( o" Q. g9 e由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:& G3 N$ |6 ~, n. b" K
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。- _' @: t0 O3 n% ?2 a; l5 M
于是可以得到双截龙2的是:+ C& u( c& i% ^. o- r. P
(8-1)x 0x4000 + 0x10 = 1C010。7 |3 v, t9 W& y3 F+ \6 i
然后跳转到1C010:5 k1 q5 u2 b9 w6 _1 ~$ B

% z5 q& r& X# z' u9 O! K) i5 R! r) u7 q9 Q  k
9 l5 w7 {/ N* f; j6 d
然后插入0x20000字节的FF:
0 X+ o/ s: G& u6 Q* M$ y9 c9 j1 A5 x
8 _% `' Y* _" a$ S% ~4 Y7 t/ Q$ |& f. R
  U; k3 j* {! m( ~
然后保存:- S  T& l3 w7 \. l2 t- B- k

: q! o$ r9 o/ U8 I  A  l% X) Y9 @! l4 i' R& a8 J( l0 f
用FCEUX打开正常运行:- h. w. y  F/ w

2 j2 O0 [1 I; C3 R  h: n6 K查看文件信息:: L9 v; r) Y6 E8 t
- O' U6 O* g$ R+ x; p
' {+ T) }  |/ S0 }& g! }
接下来切页:# E6 E7 D; ~8 m. a
先打开十六进制编辑器:1 G3 y  m- B& Q. T$ i, R% U4 c
6 v9 c7 S$ ]" }) m* b! Y+ J
拉到滑块最后,看看重启中断
' c1 w' E# O6 l( M( M' k中断地址         中断         优先权 5 y/ m5 x2 K0 K& E" p0 ~: g
$FFFA         NMI         中 + }% N  ~% A- Z0 i3 ?" F# `
$FFFC         RESET         高 + h  m8 D. q4 W8 f* m
$FFFE         IRQ/BRK         低
9 i& U3 }9 h' l* {0 b1 F0 G
# Y3 w5 H0 t7 j, x4 oRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
3 i! k; m% v5 @  f* W+ Y5 m由此可知双截龙2的RESET中断$FF65。. s7 [& |% I/ c" x9 ~+ Q0 m0 t
接下来添加$FF65的执行断点:
  z* }1 y) e/ M& o3 W) U打开调试器:
0 H3 o" i/ I$ |; p  P  E/ w5 C. ~- M& x# H' D
添加$FF65的执行断点:
( k+ m* A( e! [- h: M: Q+ \9 V* p
9 R9 `+ r2 C" A7 c- o' z$ q# f7 n3 `4 N, A: U1 U

. d4 L/ l, K9 X7 P$ ~2 C单击确定:
9 N- E/ \; q' x- K0 w8 z  r6 o2 I' r5 K; N% u: Q( s% b1 t: E

1 ~0 }  K/ m6 j: h6 ?然后重启ROM:! n3 Y! v% s+ Y8 g6 E4 {

5 K7 X* n' P6 h* r: A( `调试器此时弹出来:
0 n+ ?9 ?0 K7 m, s# W  T% c! Y- e
6 k+ ~* B0 q7 `* u5 k2 R# Y然后打开Hxd,写一段mapper 4的切换bank程序:- T6 n, f$ d: |( J: Z7 ?; v
先看看mapper 4的说明文档:: z5 y. }+ n: B6 Z. G* f. ^$ x4 W: ~: S
Mapper 4( Z2 d2 V' z  l: ~( A

& D3 B: [8 ]$ O- G4 n' W$8000:  模式号: R% v: ^0 P. Z
        位D0-D2:$ }; o1 z& d1 v
        0:选择2KB的VROM存储体映射到PPU的$0000
! |% ^  ~* W; s( u6 }# s, y' W: o; K        1:选择2KB的VROM存储体映射到PPU的$0800# \! `$ m5 L. C' r6 F
        2:选择1KB的VROM存储体映射到PPU的$1000
4 \% ?6 V$ T+ N; o1 F# a        3:选择1KB的VROM存储体映射到PPU的$1400
0 W1 s( g& n; @& e: H. z# I8 _        4:选择1KB的VROM存储体映射到PPU的$1800
, E# l+ J) t% ?3 r, y  W1 j1 Q; a        5:选择1KB的VROM存储体映射到PPU的$1C005 d, r8 u: I/ \' V  i3 G1 X6 o
        6:选择8KB的ROM存储体映射到$8000
3 m: L- j' T* N) W5 X1 f* J; {, Q        7:选择8KB的ROM存储体映射到$A000
% B# E$ P* [/ o/ n        位D6:
& L( F0 t( t$ _3 ^6 I        0:允许擦写$8000和$A000
5 b- m+ f' h0 c5 }4 c  S        1:允许擦写$A000和$C000  ?) ?* n. G/ ~- n& X  F
        位D7:3 P' C# H3 O' @0 u. h- s3 G" ~  z2 i6 R; i
        0:模式号D0-D2使用普通地址3 o1 m1 e+ M* g' G8 E* u7 L
        1:模式号D0-D2地址异或$10002 I7 M2 v3 U5 U- `

5 Z/ L& q  T4 H5 n! J; n+ o% W$8001:  模式页面号4 u2 k+ w3 c/ |& S6 G; P$ l+ ^# O. x
        写入一个数(00-07),切换存储体到对应地址
! R" v2 z' ^3 A/ [+ [: @  u+ t1 t
$A000:  镜像选择! ?& }( n" }) ?: ^! R) [7 M/ }4 h* p
        0:垂直镜像
, N  [' A( L- }+ g, Y        1:水平镜像4 y* U9 E% X0 z  G
* y7 k# ^( N% x. m% u: }
$A001:  SaveRAM 切换5 Q& I4 ~, P# V6 j7 L% s4 z
        0:禁用$6000-$7FFF
: t3 G& z* Z9 c$ z3 u0 X4 a        1:启用$6000-$7FFF
( A1 b; O- X: R& e
9 g) n+ ]! o: G  g! \; x$C000:  IRQ计数器- t2 C- X* r; U8 p
        IRQ计数器的值存储在此处
3 |% I+ e  {+ C2 O+ P% O5 N  b- u3 V! s
) X$ x) A: g* U$ }$C001:  IRQ暂存器6 D$ H, P7 Y- K- ?" n3 ^7 p: E2 B
        IRQ暂存器的值存储在此处
% a6 r7 N% s  n) g& E' ]" t- Q: T& U6 z
( U7 G/ @* R; {, p! W$E000:  IRQ控制计数器0
' c0 r; M0 f) Y6 r% Q5 S; T3 a        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
/ k1 A3 \# a  W" ~5 ]- S6 ?  ]# E2 [7 e2 o- N2 P
$E001:  IRQ控制计数器1
. c: U# `! z# e1 {1 ]- t' N+ g8 X        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
8 T! p) L6 F* A+ V5 |# ?$ Z5 p3 T
/ \, X6 {4 D8 F那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
7 Q: l6 |) g5 ~9 A' ^2 w48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 683 n, A: F/ w) J& |' h
PHA      累加器A入栈
: c4 I2 R  s. e4 M' z4 R/ lLDA #$06    设置切bank地址为$8000-$9FFF
# H' V% Y% G# x  y  [3 \* YSTA $8000# w6 H$ R$ L/ ?: U: O
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
9 N% j& _# a7 J4 H; jSTA $80011 `: v$ A2 j, Y  y/ u
JSR $8000    跳转到子程序$80008 d8 p4 Z! C$ j, p# a
PLA     累加器A出栈
* k/ _+ T5 H9 {( }8 h9 ^1 e# Q
2 _) ?# e6 ~% r/ f+ {7 \为何切换的bank号是0x0E呢?" L9 B4 l+ R( r( N$ g. k7 u
因为在扩容ROM后,文件PROM结构为:
  \0 }0 l6 T, Q. U4 _3 @原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM9 P, ]5 N7 H* [. g

; x% h( `+ F% A& \8 aMapper 4切bank一次是切8KB,那么文件结构就是:
, K% A" K5 j! Y9 Q; _原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)6 E; A* L  i3 z$ r; D; ?
因此选择扩容的第一个空白bank就是0x0E号bank。
; h, A2 t! y1 P% I4 ~$ g8 ]4 _* F) g8 [# ?7 E
" s# o1 s  O( x3 E
然后去调试器找RESET中断中可以放下切页程序的地方:, N8 O" P4 i. b: o) g0 F7 Z
首先长度要小于等于自己写的切页程序。
/ ^$ j* v5 V+ c% ^7 d$ K0 c* M# b9 v可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)5 S) Q1 }* g# ]9 q, T
5 W* h9 H( g# O; U5 r
然后跳转到$FF7C:' M4 S+ i, G8 r

5 d8 \$ e9 w1 i" J5 g! c) X, i( K) r2 r& M  `8 a; r

. [' ?) R8 S( M7 H1 C9 J" G( D4 f7 @: {+ ?: C# {* v/ Y
复制下可以被替换用于写切页的程序:  c1 V2 \- `# [
先选中,再复制:; e' f' G' P8 Q$ [' P2 _

0 U; `: ~$ c3 o+ `+ R/ p' N' u! R
在Hxd中新建一个文件,把复制的数据粘贴上去:5 N4 A2 b4 `1 f  L; [8 l

  p4 \2 q5 H5 U; S$ w' a* G/ `; k1 w0 M; E$ Y9 m1 w( K
. a; X" [$ @3 R

3 [/ c8 u( T- L% u然后回到十六进制模拟器:
: f9 j2 o3 I' H8 Z' G& g转到$FF7C对应的ROM地址:
- i% U) Z6 B7 Y. Y0 g1 E, k0 L8 w9 O# s8 b" x9 [/ ?8 _
8 J9 Z/ I  d- u  M
然后复制Hxd的切页程序,粘贴到这里:8 j$ O2 v' g& l, S$ n

( s; \0 C& }2 m& _; G
# ~* ~5 d+ z1 ^5 z2 \! p: z% \1 f' p8 h+ G9 F7 m# K
没有覆盖的用EA覆盖:
: z) ^1 w2 k7 x3 W& X0 B+ h4 a) Q  [' [! Y% _
打开调试器可以看到变化:/ z( h8 p9 |! r- T1 T4 O) i
# H$ W/ \/ T/ q( h% e: ?
然后添加$FF7C执行断点:" y4 W( J- D( ~$ ]6 ~

: \( R& O3 r0 D  }. S6 L9 {
2 d1 |6 U; i) A4 B, b/ `6 G; v
% ^2 I9 @" l+ d单击运行:# K  a) i0 ]4 L1 K7 }# L
然后程序在$FF7C这里停下来了。
# A- i5 I% v3 A$ j3 B1 S  Y" h* m- B/ O
然后单击单步进入慢慢跟踪,直到跳转到$8000:
7 t3 l) G6 \  b0 a7 y( r1 R7 }  z1 j6 i# ~" J4 D- v: L
然后打开6502_Simulator:
# k' t( }# M) i" I3 a, ]0 T
6 e3 E; u5 u$ \1 l% [再打开我写的数据搬移程序:
2 @/ w+ P( g5 K, @4 G
$ h6 S1 U8 i& w9 t3 e6 v
; m/ x1 ~6 I& L# H然后修改对应的数据:
/ h+ Q1 x3 `, F+ R+ F- t% p程序开始地址:修改比如$8100就修改为 .ORG $8100. u+ g3 T9 D4 Z6 o- S; Y& @7 k
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
' P# e! w9 V/ r6 z% x/ {从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82# m* Y  y7 S8 L" u- ]
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91( n# y/ ~! ^% c* C' {. k" Q
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。! v0 e0 u/ _3 L! P
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
+ ?5 c- Y$ S3 R& L中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
( N# C2 `" z6 Y. D4 L0 h- n后面的不用管。: C7 j7 x4 S! N, `. ~+ I

' u; Q: }/ w% k! {' t. `. K设置完数据后单击编译:. p9 H7 n) {2 b6 s% P$ p+ x

) i  ~' x7 R) M! e然后保存编译文件:1 h' P9 r* v' h1 F4 g3 T3 l

& U1 H/ T6 X, y$ Z1 C0 [选择二进制方式保存:3 W" H* G7 X# U% w1 E5 B

+ |8 M+ z3 Y" Q' A2 F
3 u4 l8 Y6 ?. {. Y: p# U用Hxd打开保存的二进制文件:5 V2 n& ^% _5 J. G
$ M1 |" V2 J% g; ^* }" M2 L
) @# f2 L: u# b0 K9 p  ^
跳转到设置的程序开始$8100:- g3 m; ?  p6 M5 x% T; p/ N8 ?# W
& Z- Y9 ^  _/ Q) ?0 |" E# E

( ]" b. p' [4 p3 S2 n
3 Z- |# J) N# Z3 e- P  @4 f回到FCEUX,转到NES内存的$8100对应的ROM地址:
" W% p: C7 u& ?% C: r# A" p" e% }" l3 p- R' D
# d+ @0 w1 I2 t8 p6 x$ a
% w3 x; K) A2 B& z9 P

) k( T6 S# B( f
3 R' ?# a3 C# n6 e2 o& b! W& r; T然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
4 S0 [1 W3 U4 o$ \% m' I! R+ B- K* X6 y

  s7 \/ S6 c' A9 T- ?3 G( X9 m9 _& \. a) D% y) r, S
然后转到NES内存的$8000对应的ROM地址:* k) \  y* Z. Q5 Y) @7 V
, T4 G; ?9 [3 D9 A+ a) i9 s! a

# g- m. c6 J: N# ^, X& X* C" |6 o8 Y/ p' u5 e# w4 t9 X

! [7 t; |) T3 y写上如下程序:
4 A& Z1 l: Y( g4 d( ?9 G% e9 UA9 80 8D 01 A0 20 00 81  4 p6 h/ v& K9 H  |# Y
LDA #$804 R9 X2 `1 {- C' w7 O1 \! B' d2 f& q
STA $A001 可写方式开启SRAM
+ V7 w& B' N/ t& i6 UJSR $8100 跳转到子程序$8100# g# D& I4 m5 v

: [3 j+ `( s4 s5 a+ c然后把Hxd里被覆盖的程序复制过来粘贴在后面:
* j* c$ N, R& t' C3 T) ~8 J* {2 i  c: w

+ O* N, ~& U% j+ h5 b
2 h4 w) P7 |% _+ |末尾补上一个0x60:6 P% K$ A( ]% Z* E& E
RTS 子程序返回, l; b- t; v- L: Y- F$ h* w
; @" T$ r. [$ C
然后单击运行,ROM音乐响起,正常运行:
3 l" D/ s, M! N/ I/ ?3 f
' G$ I6 g) ~$ E1 q0 o4 C8 t/ X9 w9 V: H' f- u( B- s: x6 |
然后转到NES地址$7000:
% g2 b* F" `! T1 j/ p& e4 \. ]/ Y' L1 i0 V1 M7 p% |; b8 h

4 ^0 o1 T/ {1 [0 g, U' u
! t' [& l7 z7 @, l+ V! F. E' e" ]5 ~+ e
  c4 s9 ?( t1 l& x* a
可以看到,$7000-7FFF都被复制了一片数据。
4 _& Y/ J+ v+ P& D1 i- B测试没有问题,然后保存文件:5 R, R" {: h; b" {9 C/ c
7 I8 R# G8 ?: y2 |- Z% |
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。1 l/ z$ ~" \8 }# u+ a) \4 D
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
& v4 q/ a1 M* C! S$ K( ~2 [- L
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1184 天

[LV.10]以坛为家III

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

签到天数: 1800 天

[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-1-9 12:47 , Processed in 1.090820 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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