EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 3 k% h& ]& @3 p$ C" {

0 V* T+ y. n- b: e$ A8 O, T[FC][SRAM扩容教程(Mapper 4为例)]' O. G* t- u( S/ K% t6 h

" p. Y2 C/ Q4 M8 M& _- L, z时间:2017.4.28% H- s$ u, I/ y8 I- V
作者:FlameCyclone
% n5 e. K) J4 ^: i! X% P- |+ y工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
. H! `+ ]& a% MROM:双截龙2(J).nes% D# S- W* h' |- g% V
适用:没有使用SRAM的ROM
8 R: P  q# x! ~- w1 ]4 S
" K# R8 w9 t  R6 {0 U5 D% T首先用Hxd打开ROM:/ }" e$ a1 l5 S4 R9 @
& k) p0 d1 b6 ?; H
然后扩容:
; v0 X6 T" U, Y( u* |+ a% w* [2 G6 o) k3 U4 ^: Y( D
# x8 R! U+ w. z* t" }

6 K% D' P, m5 n. h- Y4 `6 a7 |7 u9 d! j. U! ^1 i
3 U8 Q" _6 H* P0 f/ L
$ W& K& `: l0 [- B, n) N& }

0 ]9 O, w6 m% A1 b8 a
7 a6 ^* H0 o: j: J4 z/ J
" S6 _) A  l$ D5 t/ G  S2 T1 H* H先看看任天堂产品系统文件对NES文件的说明:
7 Q! k9 A% y' C* {  n/ Q. ^- qNES文件格式& l8 D( z: J6 A& Q+ |( k+ e
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 ' U3 |- t* q+ a! i) p; T
偏移         字节数         内容 5 @1 U' r3 J6 W6 G& L& }
0-3         4         字符串“NES^Z”用来识别.NES文件 $ G! n& P% u1 t) i2 v2 ^) J5 K0 T! M& H* c
4         1         16kB ROM的数目 * y' h1 i: y: i6 o$ P, l
5         1         8kB VROM的数目 ! j- X7 m8 ^! T4 N4 |
6         1         D0:1=垂直镜像,0=水平镜像 - G) N8 _' E4 }$ @" Z) o
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
) ^9 }3 i4 O  w; }# Z8 _. T                    D2:1=在$7000-$71FF有一个512字节的trainer * p  E, N5 T) {3 v& e8 _
                    D3:1=4屏幕VRAM布局 + R  B) @+ d6 I, K% A) Z! H7 ?
                    D4-D7:ROM Mapper的低4位 * @( I' o+ H2 {. P2 q
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
" l  F/ s' b$ N' u& y! e                    D4-D7:ROM Mapper的高4位
9 G- I4 S2 X/ B0 @2 D, |# v8-F         8         保留,必须是0
6 u/ |7 G+ s, ?) E4 u' p16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
: [1 m. D0 e& F) A-EOF         8KxN         VROM段, 升序排列 9 S  F' N7 z, n! P7 H; h7 C3 Y

7 l( @1 b% y0 @1 D8 w5 ~; t" B然后知道这个ROM有0x08个PROM和0x10个VROM1 m5 X  t) P1 ]6 ^
接下来扩展PROM位0x10个:' s& Y" Y& I- U1 m9 q5 G( |9 L; b
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
1 O8 f% o! t0 O* ^: {* n% Q( n" P! X! i0 q. n) Y
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
% C9 X( `/ B- T5 o- L最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
3 K. A  h( g; [. b于是可以得到双截龙2的是:0 b: q* {3 Z9 n; n/ N- B5 q
(8-1)x 0x4000 + 0x10 = 1C010。$ c8 o9 ~: K: `
然后跳转到1C010:, j' i3 }  u! K: z5 w) F
* x& x2 E- H/ L5 r0 g5 w; J4 T

- [8 T# h# }5 P( K1 k( [! O
. N1 q& m/ ?! B7 |9 ?然后插入0x20000字节的FF:8 w+ |9 l$ {9 p* O) k; X

+ Q! z* k, E7 u+ t# X( u3 I! K* m9 S& g$ P5 u! _
' K+ a, F' r! ]# _$ g( Q
然后保存:% d! o. r; v( {- {
, X7 G8 j0 x: R; C9 q, i; S# V; p

9 v# t" n# x! H: ^7 R5 L用FCEUX打开正常运行:
- a5 E# \& H" k- [. _- {8 U4 y1 o4 y9 Z5 h
查看文件信息:
% D$ c# q/ c% J! T+ ?& x- u( D* x) M  ~3 D

( O/ Y" G) ?+ }4 K& G接下来切页:
9 B" D0 {* ?& z5 y, I; f, A先打开十六进制编辑器:& z2 K$ y/ z: z7 v) T

/ ]1 d% t$ z" H* [' r拉到滑块最后,看看重启中断
5 A" n* L' B3 e中断地址         中断         优先权
- t: W! H" k: o0 Y$FFFA         NMI         中
  F) i! l, o& c. x1 S) s! q' P5 W$FFFC         RESET         高
, J* {# W" G3 n' L1 {; [$FFFE         IRQ/BRK         低
  p$ j7 G4 d, ]* n& z. i  s
. O4 n0 ?5 v) {( lRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。- X0 P" {" X+ _' _- ]: U7 L" ]$ b& v
由此可知双截龙2的RESET中断$FF65。4 [% O8 I% F( c7 m1 |( I/ [7 R6 ]/ c
接下来添加$FF65的执行断点:. z* J0 w/ c* y4 Q- j$ W
打开调试器:
4 h; t$ L: G, l7 B, b2 D0 ~; L: t+ x( {! c# K: a5 `
添加$FF65的执行断点:
  {1 o7 n8 n" G3 O/ D1 Z
6 Y( [. p8 P( S- Z; A
+ b2 ^6 D# Z* t% L) v& F9 X  j' c+ h8 x& `/ B" h+ p' n- Z
单击确定:# n0 f$ x+ q: Y
0 [1 Y" U  E! `/ w4 A2 M& S* ?

; ?" @6 o* X* K- B) W然后重启ROM:$ u7 d" }& M8 z& ?
$ Y# d/ O3 u3 [( Z
调试器此时弹出来:0 H: X) T! c0 R9 l) T7 j8 Y* L: P
# @+ Z9 J* x+ R; F) k+ N
然后打开Hxd,写一段mapper 4的切换bank程序:5 L- \+ r4 s; U0 L* b8 D. W
先看看mapper 4的说明文档:: A9 x4 m: |& A+ u8 h' ~/ P1 w
Mapper 4
) Y" X  d7 _/ e7 d" X  t1 K
7 P' x$ ?/ q2 _! V' N0 H& @( V0 r; ]$8000:  模式号
9 S7 w: a; H# Q7 B0 R, `, m        位D0-D2:
* L1 g9 d) ~# X        0:选择2KB的VROM存储体映射到PPU的$0000' U  r- W$ H0 c! Z
        1:选择2KB的VROM存储体映射到PPU的$08002 P+ Q/ w/ m7 a9 \- M
        2:选择1KB的VROM存储体映射到PPU的$1000
# B$ S! D. c! r& i        3:选择1KB的VROM存储体映射到PPU的$1400' h0 K8 `8 x- j* k9 s; W
        4:选择1KB的VROM存储体映射到PPU的$1800
6 n- X0 O7 ]. T9 L0 L/ `        5:选择1KB的VROM存储体映射到PPU的$1C005 `0 O. C' }+ C; D; A
        6:选择8KB的ROM存储体映射到$8000
" A/ J" X8 t9 S& r/ F1 G! |  {& L3 l        7:选择8KB的ROM存储体映射到$A000
: q2 L3 q* l, _" h7 I' m; D& O1 `. r        位D6:9 J/ ?/ H- [' g6 S
        0:允许擦写$8000和$A0009 C, w/ E7 e, O  I0 J: H" Q
        1:允许擦写$A000和$C000# I7 \5 m3 H5 N$ m7 F9 r- d. V: P' J% V
        位D7:
$ n! x; v. H4 x; H/ @% w        0:模式号D0-D2使用普通地址
( p3 M7 g# M+ R7 p2 k        1:模式号D0-D2地址异或$1000# L4 U" \' {1 K/ G
$ {! s# Y" L$ K
$8001:  模式页面号! C. R/ [8 L" G! r* c& H
        写入一个数(00-07),切换存储体到对应地址
9 r# |; J8 i  Y. P# e# a
) v' Y% n" M4 v. W$A000:  镜像选择
' o1 h5 d0 S/ S$ B3 D$ p2 T# P' v, O        0:垂直镜像
, h5 h/ h% s0 d        1:水平镜像  Z9 v; w$ X; R$ h
* `  e; C" V6 X
$A001:  SaveRAM 切换& O+ s8 B7 `2 u$ S% f  u
        0:禁用$6000-$7FFF
' ?, n" s4 k& k& K        1:启用$6000-$7FFF3 s" X9 P; p8 A) S3 t
! t$ ~; p! X/ S% W
$C000:  IRQ计数器" p( q4 i$ K1 s7 ?! n
        IRQ计数器的值存储在此处
8 B4 e: {+ q" E; w3 c) d! n8 g' n/ Y7 M9 y
$C001:  IRQ暂存器
% S4 [+ ?5 ~" c6 ]        IRQ暂存器的值存储在此处# D9 A9 r8 {3 r1 b6 m% _
' a2 @$ B3 p* W& ^  j
$E000:  IRQ控制计数器0: r# i; [7 }$ B
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ4 T- l" m2 C6 L5 Y8 B9 Z! I$ h5 Z

, g+ g; ]) o: O3 n; \$E001:  IRQ控制计数器1( y: |; C2 n8 q$ z: e
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
4 U7 I; K+ o- A5 e. U8 W$ c# t6 _' P# ~! e0 f8 ]' m
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
% }/ `: E9 p8 V: ]. C  F9 [48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 688 b: |0 `$ _4 j$ Q) \5 k% j
PHA      累加器A入栈
7 S1 x4 A" b# R+ uLDA #$06    设置切bank地址为$8000-$9FFF% B& J0 W4 z9 v% a! @( \0 O3 U
STA $80006 s9 S! j" U7 x& q, a
LDA #$0E    将第0x0E号bank切到$8000-$9FFF4 Y  p, d/ \* u
STA $8001. q8 p, u0 b, c7 b( y# Z
JSR $8000    跳转到子程序$8000
1 K  ~  g# u) ^5 T( EPLA     累加器A出栈( A5 ]6 F0 K2 p& q4 I7 O
" l( l, R( h7 \
为何切换的bank号是0x0E呢?
5 z" Z' ?5 j8 ]4 N; T因为在扩容ROM后,文件PROM结构为:
6 N4 c+ i! P" U3 b) t原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM7 n  _+ ]+ Z% E3 j6 R- H  ~

) Y1 [7 _% d) f: jMapper 4切bank一次是切8KB,那么文件结构就是:
: N  Y  `: z% A5 \) Z; O原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)& d) @( D' b% e# d4 |$ c0 [
因此选择扩容的第一个空白bank就是0x0E号bank。+ X; x7 C4 S2 y' e8 R% ~

. [4 }; [6 q% w# R+ X6 ]# s5 m6 J( P5 M5 e7 Z
然后去调试器找RESET中断中可以放下切页程序的地方:
6 a& ^* z& }, `. W! s8 e2 `9 a) G首先长度要小于等于自己写的切页程序。
! Z7 h0 s; [" Q. @, E可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
* ]! L4 H  x* q4 k( n4 ^, r% G: P
然后跳转到$FF7C:" ^  I0 F- q% S( V0 ~' g. {
# {* V* P! @: `3 V& w7 z

7 S+ h1 @: A$ g0 K( h$ t) E' \( g
4 t1 S+ F* ]& O5 \
复制下可以被替换用于写切页的程序:2 w/ h* U/ R0 n7 Q
先选中,再复制:" |  _5 E( x" D8 Q  \% y+ L

1 i# f6 {4 F9 |3 i$ h$ G4 y6 A- O: Y8 d+ [) E0 B) \
在Hxd中新建一个文件,把复制的数据粘贴上去:: `7 j  s/ R7 A" _' h8 l

" T) H  R& X! A7 l
/ ^' n4 F8 C; S  V/ _" F1 C9 E& f) p% X3 L7 x0 u, ]

9 w, O( ^2 ~2 y  B- h* h然后回到十六进制模拟器:
/ S1 r: d2 I* \+ Z3 d转到$FF7C对应的ROM地址:
' `! G7 V" b3 t* U; @- X7 I1 W( u7 Q* z. R
1 ~, l% z# w+ {9 }- r0 [7 g* D
然后复制Hxd的切页程序,粘贴到这里:5 a) d3 U2 T# z/ L( ?8 R; ~* Y
6 |8 f* |* O, H
$ S1 ^9 `* j  a; l
- c, j' S- Q  F* c7 j+ a6 h5 X) S
没有覆盖的用EA覆盖:/ _0 {) Z7 X8 [0 R7 R6 M8 Z

8 z! r1 z8 e- x2 L5 J  b打开调试器可以看到变化:' ?# e  h! y7 K% p! `. E
- e/ G4 {+ n( t  f9 t! Y
然后添加$FF7C执行断点:
4 K2 ?: |* [6 Y9 B+ ^0 R) ^6 d2 D3 R/ m. |; u
2 z4 l- J" m, y: `$ l& Q

5 a1 u- D) s3 U) ?) [7 Z8 F: R+ p单击运行:
6 w6 Y3 i& l9 Q: v* Q1 A) j- b然后程序在$FF7C这里停下来了。: X3 A* y- |% x+ X+ j) c" I
. k5 o+ U8 W/ g" S6 _# V7 I& D2 H; }
然后单击单步进入慢慢跟踪,直到跳转到$8000:
/ m9 e; M, B: ^" V* {) ~& f6 p, d; o9 ?) R  F. }* U9 S
然后打开6502_Simulator:
+ {6 w6 Z3 p8 O0 j" ~% c$ g5 `8 i' N! G" p) F8 m/ F5 {
再打开我写的数据搬移程序:2 |5 w- b  e1 o. t- d8 t
) r2 V, N; a; c( r' x

3 A- m5 z+ O: Y# m! \& }然后修改对应的数据:
" Y% F% a; M" J7 g7 _; D程序开始地址:修改比如$8100就修改为 .ORG $8100; ~8 N2 J, _+ o7 x
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70: r; Q0 _/ f0 h+ N6 I9 }/ x
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82  Z2 J; D2 x  A  R; C# c
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$916 e) N, u& C" G* c# J: [- Z
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。
) K' K2 C( T" U) f6 v  s如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
: z: `! P3 x8 S7 d  F中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。3 n' C3 e' b( s0 C
后面的不用管。' ^# F) }  h1 v' ~( ~& v
4 k0 }  W/ R) W7 Q  M2 T
设置完数据后单击编译:- k# f: C& G2 R$ b( K

3 Q' ]- c) P2 b  l5 p! b然后保存编译文件:
: z& L9 R9 X8 n) ~9 t
& t# k% J# E+ P; i; |! I选择二进制方式保存:
5 K0 j- c; g1 W5 \/ K0 z% N6 z0 V8 J" H8 X4 e3 v
+ J& E4 ^3 J% s. _
用Hxd打开保存的二进制文件:
6 ~: x# o- j. c4 G& z  M# P, ?1 n" w; h7 v
4 e$ Z! S2 E0 L( ^$ g  q
跳转到设置的程序开始$8100:" w; ]1 i3 S" ?. V% Q

  s( _' W! T, K1 W+ x  u$ x0 x) w' [$ ]( f5 `* c

( W% }# t" h" |$ p4 [, C; Y回到FCEUX,转到NES内存的$8100对应的ROM地址:
' H7 B0 N9 y" n& o, Q
: R; p# \, c; U  A1 p/ H2 c! i0 N0 e* {1 I7 g
0 A' e1 p# j. E6 ^" M. j6 g

2 o, A6 l8 j8 ^! m3 Q& w1 h
" E: t% P5 Q) D+ x5 \8 }然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
9 Z  n( d, \) R2 K& s
6 a- O$ T0 g4 y
' g9 b% Q8 {# M& K0 a( g
: G9 {6 e) k. A; V& ]# y然后转到NES内存的$8000对应的ROM地址:
# N3 U9 j9 X# ]8 k0 r% [- p; G2 d9 @) s  A$ C4 W

# h2 [( D& @4 V
$ b& ~. C4 [0 k2 X- n! }$ ]% n! j6 N8 o# h* V  ~; M' I
写上如下程序:9 b5 \5 h* c# A# P+ y
A9 80 8D 01 A0 20 00 81  
# v5 U8 ?% l8 OLDA #$80
( V1 _4 o8 N4 LSTA $A001 可写方式开启SRAM& P  K8 M, ]/ \" k
JSR $8100 跳转到子程序$8100) `0 O( J2 R* D7 o
2 h$ Y& J/ Q9 T2 |7 ~
然后把Hxd里被覆盖的程序复制过来粘贴在后面:
: {* x" q) f: O5 Q, `1 p# b( k6 z3 z' w" m1 a8 T
9 e* p3 I, W9 ]2 r
. L8 K. H1 S" w8 ^
末尾补上一个0x60:
  q5 l5 s3 u) ^. c6 LRTS 子程序返回
4 N7 ~0 ^& g1 ~4 `# B
+ y; E$ V' V' [6 W& U% f  }然后单击运行,ROM音乐响起,正常运行:  U4 Z" r9 r6 R9 Z/ D7 R
9 f+ n, k% T" z3 t5 u- Z+ j+ X

4 i8 O$ @. p" h然后转到NES地址$7000:4 o; O3 |- ~; v  g! y" \0 ]
$ w8 U7 [8 O5 S7 s

6 N/ r' z! X6 f/ z% c
& N: j0 U( d! o) t' T5 r% l# b8 X" _
: C% i! o& t' P( q: T
可以看到,$7000-7FFF都被复制了一片数据。% Z6 m/ t0 S  l2 X
测试没有问题,然后保存文件:
, \4 Q# I% S9 D" b+ p& j- Y  ^- q7 v. q; |( r$ f7 g6 K
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
/ O( W  q' V  Z- m后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
/ |# k. p. X5 K% }" Z6 @
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1183 天

[LV.10]以坛为家III

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

签到天数: 1526 天

[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, 2025-4-4 10:47 , Processed in 1.157226 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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