EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
7 u& k* f6 y2 l  v( T
, m7 @& a+ k/ K" S: g9 p[FC][SRAM扩容教程(Mapper 4为例)]
) D9 k6 w# ~& u# \7 A
( F- u& n* B6 ^  d% L2 ~时间:2017.4.28
" r) G) K' j$ c2 i- Q- c作者:FlameCyclone
) ^) p4 h% h9 V9 t% `+ G工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
, S3 g9 \5 \- TROM:双截龙2(J).nes
1 u4 k" S% n9 ~适用:没有使用SRAM的ROM
0 a% ~3 J- c" q* J; A$ F: e6 w
& b% E+ G8 G) s( ]3 S* m+ e首先用Hxd打开ROM:1 C! r. E5 }. ?9 X0 _
7 o# k) B$ X; ]! I; X
然后扩容:
, M: j7 a) f. ^$ L! K2 E! I$ k
% u7 G& I' a+ ^7 Q2 Y  r, I. f. Q/ \* p
  ]9 K' S/ Q7 _( S" P
/ X% A; ]+ v) z6 }+ ?6 ^+ n, P: B
2 M9 L# W& ^6 [' Q& ^
& |, i  u3 ~2 @3 S% C

$ g) j9 q$ X) _" T$ x, L/ `" T) R) p8 P) w: S$ B: L

3 c; O8 P+ t$ R) n7 G先看看任天堂产品系统文件对NES文件的说明:
) ?3 K# X; l5 b" y- CNES文件格式6 Z( Z) {/ n/ G' H4 n% V  b7 N
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
9 o0 \$ D9 k3 i偏移         字节数         内容
2 K+ G/ q8 _: ?, g$ j' @/ X7 c" Y2 J0-3         4         字符串“NES^Z”用来识别.NES文件 # _% N, `- o* U% w* R" ~
4         1         16kB ROM的数目 # e5 P9 R' ^7 m# o7 B5 B
5         1         8kB VROM的数目
5 x) G) j7 b0 {, m- a, n9 H6         1         D0:1=垂直镜像,0=水平镜像 " a6 Z8 Y% w* a- f7 c% q' r. U# T
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
5 ?0 t% _7 w  g: N* `- P  D                    D2:1=在$7000-$71FF有一个512字节的trainer 6 R7 V: U1 J; Z; @
                    D3:1=4屏幕VRAM布局
9 A4 l) G  K8 `, L9 e9 e                    D4-D7:ROM Mapper的低4位 7 w$ @& A# L( l! Q% y
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
( S, _; B9 P2 M, Y' V' }                    D4-D7:ROM Mapper的高4位 ) ?4 ~# \/ C* f% e
8-F         8         保留,必须是0   J' c# y3 z4 S& P, k
16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
7 T) Y1 {! N! R, W-EOF         8KxN         VROM段, 升序排列
8 u/ j3 U+ H% [; g# x, l8 `) j, A7 w. a6 }+ }4 s5 t/ q
然后知道这个ROM有0x08个PROM和0x10个VROM
; n+ e( Y0 G0 A* X& x2 Y8 M接下来扩展PROM位0x10个:+ n$ F; n( U& M$ u3 v/ `; \( v5 e
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):& A: ^0 m: p# q, l

1 o( M. p) M. ]  y" P& ?由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
4 E4 t) y+ \5 p% F9 C最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
/ ?( e) g2 b1 k% n  r于是可以得到双截龙2的是:- z: q9 {$ Y0 e2 F5 N2 x* M
(8-1)x 0x4000 + 0x10 = 1C010。$ S- b$ j$ N) C0 I8 p% r6 ~, A
然后跳转到1C010:3 w* t1 ~4 M5 [/ e7 Y
1 Q) Y9 V( l4 P; J
6 R: f; f& {9 w. m9 C' w. ~

' I# f" [# ?+ C" S7 W然后插入0x20000字节的FF:
5 u. ]4 F8 s& I, ~" x0 J" I6 N6 s* F) S7 X

! d2 G, I6 ~) j
% `  w& D$ V3 _5 s1 e0 P* w然后保存:' F2 x& t; }8 i- `" d5 K, o. G

. }! L3 @" R) @  c. S% |4 I2 l
4 R4 f7 b; J; R! t: G+ g用FCEUX打开正常运行:- Q! [9 l  K1 n

$ u; h' v* |" ]7 L/ }2 W查看文件信息:2 S3 Q3 B5 W2 R: ^% u; G$ B
9 {& v8 h9 I* N5 i) Y
2 b* [2 s% F4 f& u* q, R7 p, e- k
接下来切页:
$ q6 y+ [, U8 _5 W$ Z' b先打开十六进制编辑器:
# e; Y7 F% f* `" w- X% A  P7 O
- F8 y) h" G- D- i2 H3 m' m拉到滑块最后,看看重启中断
9 Z3 [0 e4 F# F: l, O$ _9 g* L中断地址         中断         优先权
& F$ |" z- j, P' U( R2 G$FFFA         NMI         中 ; ]4 _7 U2 x* Z$ q  G& z* m
$FFFC         RESET         高 3 v: Z! r4 X% ~6 A
$FFFE         IRQ/BRK         低
9 L# D6 f. c5 N) l2 K) y( F8 c; p# |' q% J8 G% Z. f( p
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
6 M: t" F- R& k; t  y由此可知双截龙2的RESET中断$FF65。# c2 C3 k2 i7 v8 x1 B
接下来添加$FF65的执行断点:
/ P5 N+ Q+ S9 d4 q7 M打开调试器:% v* K2 {$ f8 z  E
1 c6 A5 E3 [& [) }
添加$FF65的执行断点:
; ?  C+ Y0 ^& o( d" C" D7 d2 y* l: M& \% s  Q
; g0 m! S; j1 b. l3 @8 h2 e1 R

9 {; \4 y9 m; z; M单击确定:
- U' T$ u% J7 z# K5 G
7 Y6 A4 c2 Z/ m' D: y2 C% m! f: K$ o6 k/ d0 E( F% i
然后重启ROM:+ m* d; I! `! `& _. U" N) c
! G, U1 e8 h3 [: f/ N; R
调试器此时弹出来:9 H5 I% B/ q: V! b* j9 J6 T
3 f8 S# O- G5 k+ @8 A* ?0 @4 J7 e
然后打开Hxd,写一段mapper 4的切换bank程序:# [. t6 p" d1 @: _. h
先看看mapper 4的说明文档:3 y1 K$ w6 u2 n  y( a1 }
Mapper 4
1 Q" e- q+ \6 c% Q; C, a
% n) g& |0 a% s4 q; R$8000:  模式号6 t0 k2 E# ^- @, Q* l9 @
        位D0-D2:  M' q" ]  O2 `% L" `
        0:选择2KB的VROM存储体映射到PPU的$0000
( |$ G) D! ?9 w( [% p# s1 X0 |  n/ F        1:选择2KB的VROM存储体映射到PPU的$0800- Q/ E8 r9 A% B
        2:选择1KB的VROM存储体映射到PPU的$10005 b9 I3 H+ A" B5 L; o3 I
        3:选择1KB的VROM存储体映射到PPU的$1400, r1 [9 H6 @3 p
        4:选择1KB的VROM存储体映射到PPU的$18000 X4 G8 F7 y6 P5 \! X5 x' @
        5:选择1KB的VROM存储体映射到PPU的$1C00* i+ q; ^9 {/ q) s2 g0 j$ h
        6:选择8KB的ROM存储体映射到$8000
% c& ~& b! M' N" k5 y% q. z6 E$ r        7:选择8KB的ROM存储体映射到$A000
  t1 A: I% w( k9 V# E+ v4 @        位D6:
7 w7 _, Q& L* u! _1 c8 J, G( O        0:允许擦写$8000和$A000/ I- n. |" ~( q6 H3 i* Y
        1:允许擦写$A000和$C0002 D! A0 N# a( D- R: S6 ^7 N
        位D7:8 B% J$ ]% a, P: x) `
        0:模式号D0-D2使用普通地址6 n1 Y& K/ ~$ u% S: `* O' Q7 a6 i' P
        1:模式号D0-D2地址异或$1000$ F; O! ]' L0 B5 I- `, o

% \4 A. e* W0 @# ^- j7 k$8001:  模式页面号
' S! h, J, b* b. m- |        写入一个数(00-07),切换存储体到对应地址* D6 i' w# H/ b6 ~; ^+ p
6 Z  X$ M) n9 b  k% o. w& r
$A000:  镜像选择
7 I' V0 V. P" t) b        0:垂直镜像/ P( O6 B' \! u: ?
        1:水平镜像1 Q* \% U! Y8 c( y) E1 S$ j. e7 D

; g- y7 l# S6 J9 w  w$A001:  SaveRAM 切换/ x1 o( m5 L2 D' I
        0:禁用$6000-$7FFF( w+ s/ V! \0 l% o7 a/ V# x# L
        1:启用$6000-$7FFF( u, [9 h  ]+ n/ }* M

4 q: r+ J0 R  a; {3 T9 v) ~$C000:  IRQ计数器8 _6 P: m& x1 J, W
        IRQ计数器的值存储在此处8 q7 @$ A# Q* t% T8 o

; i6 P/ X6 c1 X9 u" F1 l; O$C001:  IRQ暂存器
2 _2 u( p1 {  k" x  ^        IRQ暂存器的值存储在此处/ z( V. h5 i2 U2 D, B# _

0 Z- m3 g( x" I6 x; z0 I% l$E000:  IRQ控制计数器0
6 S3 n* D* }$ J        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ6 c- ?# a, @1 ^
4 Q, D# |9 ^# q) `
$E001:  IRQ控制计数器10 c4 W" r6 N" B! n. q( A3 w: [3 F
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
3 ^- I, u7 d! u& Z6 B
! R: S( q3 n3 e& ~) S' a那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:: Y* g% V1 X0 _' v6 {' W0 P
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68
/ f; M6 T  f7 V# A2 n7 DPHA      累加器A入栈5 M' H: o& F* x
LDA #$06    设置切bank地址为$8000-$9FFF2 ^8 |0 Q9 C3 x2 z
STA $80009 s7 W" y) A# O; Q$ t; @+ K
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
0 K3 C* ~) H2 W7 tSTA $8001
! z7 B8 D9 k9 u: aJSR $8000    跳转到子程序$80000 Y' h, E, q: E
PLA     累加器A出栈
3 }- q5 t$ d  p- J- n9 Y
/ V2 S- X8 d' F1 i4 \( ^  G8 h# O为何切换的bank号是0x0E呢?. i4 p( ?7 P" N, b: Y3 z
因为在扩容ROM后,文件PROM结构为:
! \. _/ f5 n8 C7 b1 c6 f4 f: F" }: P( s原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
% Y4 v, i% r% G- c9 P  l! Y
( I5 z! V. U+ b; M2 j4 ~Mapper 4切bank一次是切8KB,那么文件结构就是:3 J+ _% p/ l# l4 g7 }1 H1 s
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
4 z! C9 H6 _6 M% r1 v1 P因此选择扩容的第一个空白bank就是0x0E号bank。
+ ^5 o, R' r, \' k
4 h3 f4 q" I0 |3 w, }3 j. y9 h/ W, A( X2 b
然后去调试器找RESET中断中可以放下切页程序的地方:4 p7 ^1 e. C- C! R$ Z
首先长度要小于等于自己写的切页程序。# Z3 d) b, `0 n2 v7 I
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)+ B( c( O: L5 {( b0 b2 Z5 B% n* @
! _' J7 s0 u6 b6 d  I
然后跳转到$FF7C:
" C# R" b% c8 A( @( w& {" G6 h- C9 k+ E- i: P. b1 d9 r
5 N$ y- `! O! z1 v- Q* l! @& S
: |& O. o- r% G4 `2 h2 \

2 {% l" l4 r! N  @4 a复制下可以被替换用于写切页的程序:
( U+ _9 D4 Z7 W! A5 W7 O先选中,再复制:
! r1 S, I* ?$ l
6 l  _9 T- J0 g: P, ^. S8 L) E8 s3 _  z
在Hxd中新建一个文件,把复制的数据粘贴上去:
/ ]5 O& |4 ?) ^' J
% a2 Q% h! ]# y) |- |
% T; I# V" I3 B
6 n& n/ R7 U: a( G' R: r4 l+ ]1 J# ?* p
然后回到十六进制模拟器:7 j. V  w7 D/ }$ Z; {7 y
转到$FF7C对应的ROM地址:4 G+ v" e% R* Y2 d3 b

; d% t1 }* X1 @6 ~  G' l  B7 B/ W/ P/ l
然后复制Hxd的切页程序,粘贴到这里:
- `% h% P; a. Q8 a# P: y/ Q8 M; c: ~0 C" Z) T

7 M$ T6 t. C6 y1 ]: K3 L
* D  D+ b/ E5 k. H  G没有覆盖的用EA覆盖:& o1 ?8 v  X5 R% w; W" F: G$ L0 B! N$ G

9 q$ {" L- ?1 E2 H* g0 e% q2 [打开调试器可以看到变化:
/ C- v2 G, o7 n9 [9 R4 k% m+ G/ _- k9 r  i* l+ Q& j# V/ E
然后添加$FF7C执行断点:
. ]5 P: F1 q( t
6 `( z* N) [( v& l& ~5 v1 J# q# \  F

# E/ S+ x! [1 U' |8 P8 U: C( }单击运行:
) W& L) {; \7 F! h+ u# H- a然后程序在$FF7C这里停下来了。
" j" u6 f; F* J2 t/ A. r: l. A& H1 Z$ }9 ^9 Q
然后单击单步进入慢慢跟踪,直到跳转到$8000:1 M0 H. ^8 T" J4 z4 p2 x: ^  o

* w  C/ H* M8 p9 E* F$ b2 p然后打开6502_Simulator:( \2 v1 f2 L- F0 m; W- T

& b5 Q$ m+ G5 I4 F( [再打开我写的数据搬移程序:/ g$ ]' |5 c' m- q

0 Q, B' n+ z9 v0 p: [  `% c3 h( T, k6 i  i' M' j  l
然后修改对应的数据:8 o* F. M1 `# ~, e) @# f$ n
程序开始地址:修改比如$8100就修改为 .ORG $8100
4 C0 B0 ?6 @$ B4 w复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70: Y6 n& t+ [! a! {  x% J
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
# ~9 l- F) u: x9 D9 ~1 R想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
) x; s3 z7 F& H+ ]也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。3 b: ~2 U- R* a, q1 ]
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
) N1 X5 L) k- |3 d中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。& d/ I; y- N, H7 P+ [* z- H- Q8 [
后面的不用管。
' Z- V$ Z4 m/ \: Z; s. C+ F1 U) K# j: O2 P8 S1 V3 `; c: \/ f5 H
设置完数据后单击编译:
. i/ u, Y- b1 U+ [  U0 a
" z4 s/ r8 |# w: X+ Q+ p然后保存编译文件:
+ ]& D; {( }1 z3 u3 R9 K. L* S
! f6 H4 k3 M: T6 a选择二进制方式保存:+ U% x6 R: F1 v8 f/ y1 ~7 {1 V

! o5 Z3 Y; m- ?$ t: W7 d$ S6 b1 x6 H5 [7 i. F, ]! o
用Hxd打开保存的二进制文件:
8 j+ q4 y3 ~7 m
% \7 [+ \- P4 W' l, \5 M; u
1 D( P( @7 k- i2 K$ y/ E跳转到设置的程序开始$8100:/ |; H+ L( {" \" z
, `& L' J6 n* ?8 Y4 _# V+ T
+ F, V, a; s0 P" O( U

0 ^0 z+ O7 m, ?2 z回到FCEUX,转到NES内存的$8100对应的ROM地址:
5 S% V4 F0 z: l+ [. s- F9 U: C0 ~" ^9 H' o7 O5 w5 z4 \0 D4 `
' ~  Y4 v3 e3 ~7 X% c& v6 S" |
$ v* ]! D$ E; w  F- i4 d) B
6 d! k0 L" w, y" Y. Q/ J

" h; i4 N' x& u8 P7 v, |& d然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
, r. t0 ^& k/ [; S: F0 U0 |1 Y8 O( ?
  {  ]  Q1 E' G2 |
) o/ v& M) F: |. C% {
然后转到NES内存的$8000对应的ROM地址:
1 i3 k' K- b0 X1 F$ J2 @( J7 d6 E0 S- |  J# K9 u7 @7 L
" f* _5 J, o* p$ L. X) z4 H
' }) P% i6 C3 O- i9 C5 v

, }6 d2 K+ w, ?: s1 @" B. Z写上如下程序:
! m1 n! F) R/ x' J9 l6 SA9 80 8D 01 A0 20 00 81  3 P- ^& [) z: r( W3 e% c+ c, n) B
LDA #$80
2 g" @9 z* q5 Q. s: h; S1 s7 l/ uSTA $A001 可写方式开启SRAM
9 s# u$ D4 M; HJSR $8100 跳转到子程序$8100
% w5 Y/ z) u7 R1 o1 y2 t9 |! u' {+ n- {
然后把Hxd里被覆盖的程序复制过来粘贴在后面:' c" M4 G  y. M
& w7 W0 ?! ^( g8 N/ f3 o/ S3 L

6 ~+ H" p1 X9 x9 G8 c4 O
/ |4 o7 O5 N* |! m末尾补上一个0x60:) K4 b/ t. T. n/ c4 T" \
RTS 子程序返回8 {6 O; @6 M; a# `9 {

. j9 G( g8 o* Y! g1 b. U然后单击运行,ROM音乐响起,正常运行:
1 E) L& e; I( c6 z! y- E1 Q; ^
' R0 K) ~! E# c! E1 B0 @  \, \. @3 F  M) \; X
然后转到NES地址$7000:
, {( u8 @# T; q, L4 W: B* g+ G
$ v$ o$ c; D8 \+ }" V5 s
0 S5 u" f* {4 I6 h* m5 b1 z7 z' {3 ]/ G5 T! D8 l9 P

. J4 s+ }% k' K2 i8 k) ^
+ K* ?* P; D0 k* d可以看到,$7000-7FFF都被复制了一片数据。
4 @8 R4 Y3 T9 v& ?测试没有问题,然后保存文件:9 G7 q+ k5 Z9 Q( O$ x

2 c& ?& t! k# N/ T/ y) @以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
( u; m) M% H- u- p后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。' V) `) E  q, d
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1182 天

[LV.10]以坛为家III

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

签到天数: 1487 天

[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 | 显示全部楼层
好东西,看看
回复 支持 反对

使用道具 举报

签到天数: 208 天

[LV.7]常住居民III

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-2-24 04:23 , Processed in 1.063477 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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