EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
9 W5 l6 s( H0 j# G8 u# I4 r8 k8 K' r- f
[FC][SRAM扩容教程(Mapper 4为例)]' {6 W7 J& u+ w- G

; i2 `( C( U/ m1 i: ]3 n; I时间:2017.4.28
. r) S/ H* r4 ~; ]0 g( \0 ]! _作者:FlameCyclone
- L9 t  o9 R$ F# r, I4 Y7 C工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
; \7 e$ g9 L, t* e  q* x8 Q! aROM:双截龙2(J).nes+ a8 U6 H" r8 s! a$ T/ K) e% B+ [
适用:没有使用SRAM的ROM
1 ]2 z1 S3 c8 o! |9 w5 E# B
8 a' B( z4 v6 `, H* W( K2 q首先用Hxd打开ROM:& D7 M2 _2 v" N6 B8 Q% T: [

+ Q2 ]& J- G( P+ R& w& o  D然后扩容:
6 y3 F* L0 ?- `6 X9 M9 V/ `4 J; j8 N3 e* P! T- p5 Q

( o2 y1 G6 `+ ~( p! {- x
7 T4 j- c2 G# ^; k8 a, Y& W/ |8 [
1 j2 p$ E* I% M' g8 }$ w8 y6 h( r" ~/ A) G) B; F+ U3 u/ K5 \9 A
& g/ U3 O+ t* \$ {

! }- `  M2 D& X# d# c& ?, L; l. [. k9 \

. J1 a& u' R. B! z# Q先看看任天堂产品系统文件对NES文件的说明:+ B2 a+ o/ c6 J8 p( e" i& F7 d
NES文件格式
0 T0 r" w, u. [, J.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 6 ^4 k6 I3 q$ }& R, u% h
偏移         字节数         内容 0 d4 t7 Y4 m) E( {- y  y% t" p
0-3         4         字符串“NES^Z”用来识别.NES文件
' g  b1 W8 u; O5 }0 {+ l+ e4         1         16kB ROM的数目 ' c$ G- c6 l0 Q; Q1 ]2 b& g
5         1         8kB VROM的数目 9 D. ]* j: h1 z' m, ?# Q
6         1         D0:1=垂直镜像,0=水平镜像
% R& J9 w3 e' ~" W# b% m9 D                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
4 k6 q' ]+ i! K$ M9 V' @                    D2:1=在$7000-$71FF有一个512字节的trainer 5 \1 {0 F$ p+ j
                    D3:1=4屏幕VRAM布局 6 T& S+ a! z0 F" g$ u
                    D4-D7:ROM Mapper的低4位 , l9 N* J9 _5 S- U+ j) T
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
0 h$ k" M  F: O& k- I* O                    D4-D7:ROM Mapper的高4位 5 E- G8 o  q  k( t
8-F         8         保留,必须是0
& u. v4 h* b2 x16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
: E' G6 w5 ^4 N9 M-EOF         8KxN         VROM段, 升序排列 * B2 F) m4 w' ^& H
# I- r0 ~1 S6 f  t
然后知道这个ROM有0x08个PROM和0x10个VROM* |% J$ v" ?6 h, ]2 k9 m
接下来扩展PROM位0x10个:3 Q" y5 Y- u0 o$ f) M$ m1 h
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
9 ]  ?6 W" U2 L4 O" n, X  k% ]) f3 k' Q0 j
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
0 F; [- E6 ?3 [/ X  Y* v+ B/ \% r# \7 B最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。( m" p% q- @) F: C2 b% B9 M
于是可以得到双截龙2的是:7 M& ~, W7 M# i, r1 V  O
(8-1)x 0x4000 + 0x10 = 1C010。
1 H* V7 P/ P9 G) ~然后跳转到1C010:3 }1 U3 p0 m" v: e5 w3 R
1 }, q& i7 S) `2 x/ n: M% |( c

4 q0 v( K; ~3 N8 g% a( U$ {8 S: A  ^& O" D
然后插入0x20000字节的FF:, J* V6 M6 R8 Y1 }5 g8 Z

2 I) M6 U/ n' Q' V3 o! b( a+ ~& ?6 \8 G& L

8 z* M* n2 h& [6 F. L4 `/ e; U然后保存:
1 ]9 ^! L) u% Z  d2 L* I0 W1 r  E" B  X
( W! L; v* p$ r1 a+ o% m5 Q- V1 S
用FCEUX打开正常运行:2 J- j3 j+ ?# @/ E
5 c/ E& |( P6 q8 o) K
查看文件信息:
# }/ L6 c( f2 H1 ?4 j: [4 X" R& y- P4 K6 w8 L( p" [1 g9 q+ v
" w6 x- V7 c  A
接下来切页:
/ z1 \9 F! }' Y; y先打开十六进制编辑器:
$ a: a2 t6 o8 ?1 @5 w" y# d  S# p7 g
拉到滑块最后,看看重启中断
9 b4 T* c6 G; |* L  J2 \中断地址         中断         优先权
8 G) c" `6 _+ u7 r. u5 L$FFFA         NMI         中 6 i7 `5 b4 @0 f2 d+ h& {9 S
$FFFC         RESET         高 - A5 q/ h4 D  }& S
$FFFE         IRQ/BRK         低 4 C) O; Q3 n: j2 L3 y" }
4 r. {, d. J  l: {+ w
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
6 {- l" X1 Y  K2 C/ e由此可知双截龙2的RESET中断$FF65。
8 e1 M. c3 M) a9 K  C接下来添加$FF65的执行断点:& q7 H* j( [2 G& I
打开调试器:$ p7 F3 Q0 e' {; S& |9 v

  r4 A( \- O' T: Z添加$FF65的执行断点:, s+ n4 |* b. a' s$ i
5 Z. D1 u* q7 A- L1 R& y+ j0 C/ V
4 |$ M+ S9 k: N  m( z

- J$ p5 n' ^# ?! T单击确定:
" m& P8 h5 @; @3 `5 V/ k
3 @; V7 w/ u; P1 t+ _
$ S1 K: h9 ?7 p+ B. B: Q然后重启ROM:' `$ O% u# n2 H5 M; b' Q

5 g4 G* p- U' @# m' M* \% ~调试器此时弹出来:- x1 l* {7 a- X& g' S# y$ M/ }
" H% m$ K3 Q* g% S. ?' z3 Y9 d
然后打开Hxd,写一段mapper 4的切换bank程序:
9 ]5 z7 x: g. N) N先看看mapper 4的说明文档:. h& }1 v  D  y  S$ D5 l
Mapper 4
/ Q5 \4 h% U  T( f- r; S
% H1 G+ w! U: M" S- _( R9 d" l$8000:  模式号
; l1 ?- ?6 h, [" x; ~  S" G: E        位D0-D2:
! x% w4 b. O* m* ^* v7 D3 T        0:选择2KB的VROM存储体映射到PPU的$0000
% M. T( a4 G2 i        1:选择2KB的VROM存储体映射到PPU的$0800) ~! [& i; C2 n  Q' z( g
        2:选择1KB的VROM存储体映射到PPU的$10002 m5 O5 o: a3 P8 q2 Q: k
        3:选择1KB的VROM存储体映射到PPU的$1400
& h5 K. o' S& R  ~, A        4:选择1KB的VROM存储体映射到PPU的$18002 D2 H- i% w. ]% T) G$ B
        5:选择1KB的VROM存储体映射到PPU的$1C00
- }1 E+ d+ I( J; s6 `* d( Y9 l        6:选择8KB的ROM存储体映射到$8000
9 Y6 P2 z$ O4 Y3 u        7:选择8KB的ROM存储体映射到$A000
- Z- ]# H7 G' T2 g' O        位D6:
+ E' u0 M8 a; B" v/ H- L2 ~7 \        0:允许擦写$8000和$A000
) z; v' g# j  k& _% t. Y        1:允许擦写$A000和$C000
+ w+ d1 K- N& j' a4 t/ I+ T        位D7:
, Q. K0 g# Q3 o! ]" [        0:模式号D0-D2使用普通地址. D7 U8 p  j  d: ?" b; O
        1:模式号D0-D2地址异或$1000
! q9 s2 o  _3 w' M+ s8 P& H9 A# x( O& V4 r; Q1 F( V
$8001:  模式页面号
3 P  h; D. V/ S9 J" p6 m9 h        写入一个数(00-07),切换存储体到对应地址
6 `9 s/ E  }) Q! v/ q0 C5 E
: f1 h* @; }2 h$A000:  镜像选择
, C# b2 a0 G' k2 Z        0:垂直镜像9 Y" K) o3 s3 w! F
        1:水平镜像
# E+ l* D+ Y2 Z+ Q8 ]8 J" x9 u% z4 X3 h, B5 k/ ]' q" P; b
$A001:  SaveRAM 切换6 L- m7 Q6 @) b  R9 J& x: u2 a
        0:禁用$6000-$7FFF
- ]/ x+ E7 b4 I8 U0 ?) r        1:启用$6000-$7FFF
6 b7 k8 u+ }$ `' w+ w/ u
1 [; e' N# ^4 Q! U3 B$C000:  IRQ计数器# h) ]/ u, s. }" S  C5 M6 \0 }
        IRQ计数器的值存储在此处
- d+ t  K5 o2 ^- r$ B5 P
' y" P# d* r) j( t+ L% x& g$C001:  IRQ暂存器
6 N6 _1 b% A: t$ `        IRQ暂存器的值存储在此处
, L" M* E) `1 I
. `. k5 ]' L- X* t7 j4 }$E000:  IRQ控制计数器0& b6 ?. V2 n% P* }
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
1 u# g* I* K! G" u) c
2 e7 N. _8 D2 B. f, m8 X$E001:  IRQ控制计数器1
, G+ N1 }+ b& f, A        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)5 x1 v" X/ f6 o; L- s
9 o; _& z1 `& m3 V) ]
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:' _. S( }. q& ?. y9 B5 `3 U
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68
. S  ?, K5 m' b! m! lPHA      累加器A入栈' u* G( T2 S/ b' ~* u. L  Y( B9 {6 x
LDA #$06    设置切bank地址为$8000-$9FFF9 C& B5 @3 C; J8 p: Q: J2 z
STA $80001 R8 b0 T0 n! p: D* Y
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
) K; f! s' [% USTA $80015 g& ]2 [4 c! @/ E
JSR $8000    跳转到子程序$80007 ]3 x  v4 D& H. y# ]* D- B! E5 Z( y2 I
PLA     累加器A出栈# q& v: F" {% X8 i& s/ v

. j  Z. e; W' q4 x为何切换的bank号是0x0E呢?% m! t' b$ y, l" U+ {9 b" W
因为在扩容ROM后,文件PROM结构为:
) x% a: V! f/ l原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM4 m5 l  v/ o$ O$ O# m
4 I; G' Q# e. E0 N  `
Mapper 4切bank一次是切8KB,那么文件结构就是:* D$ \+ X* r# K
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)) a$ A" I6 E/ Q" @
因此选择扩容的第一个空白bank就是0x0E号bank。% A! o, I; P, h5 Y
, `: w# L5 d$ [+ {" F' v

) V) a4 u' R2 y然后去调试器找RESET中断中可以放下切页程序的地方:6 L& h4 O9 ^- }
首先长度要小于等于自己写的切页程序。
( E" H6 v2 B; |) f: b; O0 O可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)  }: M& U& e& ]0 U& h  _8 n& r! N
9 D; e, x& u5 \) ^. L  Y9 [; N. O
然后跳转到$FF7C:. g) T  O7 m& q9 m) `
$ z4 t8 I, L# Q  A1 F3 ^
4 N& b/ I7 {( X# e' Z, \

8 C" ^7 ^* \5 M* G
. G" q/ y3 v" v复制下可以被替换用于写切页的程序:6 I" @* D$ T8 x- J9 e" Q
先选中,再复制:
7 [" b7 _% D  R2 p9 D5 i& `
( u* q4 h% s5 e. y, Y/ @+ k: v+ M3 t$ C( d
在Hxd中新建一个文件,把复制的数据粘贴上去:
4 N/ r7 U9 q+ t$ m# L
( E' X& E) Q# L, ?7 O5 K" y6 Q% l; w8 |0 D

) L) P* s2 i8 h; _) o# |
( _# ]2 [' l! w0 J然后回到十六进制模拟器:
; ]3 X- e4 z0 L转到$FF7C对应的ROM地址:' T  D1 u) J- q9 _2 u% Y
. @4 q# Y5 T4 O3 C; s9 f$ Q9 n

8 t  ^9 t+ s2 H1 U2 [+ a: x' y然后复制Hxd的切页程序,粘贴到这里:
3 m9 \$ B/ X, J- T4 H2 P
" R# l( M7 `: S& Y! d8 b# r, L4 o% P; K2 g1 ?8 ^! h

1 i  T9 |8 S2 t1 z( N# {$ R% A没有覆盖的用EA覆盖:
2 A5 ]- P" G$ [' u3 _8 y* _7 N
" G4 x; S! p5 a* O! L打开调试器可以看到变化:
. H6 E' s) y* H4 d. R3 A
# {  r6 K# m  V然后添加$FF7C执行断点:5 I% u4 T' Z/ g& N" t$ ]1 V8 v6 x

( C/ S) X' G& ?( S+ k6 J6 e1 {) D7 T' {6 [/ N. {( N' O& M" p2 k

" u+ r$ Q2 t" S0 S0 H# _/ H单击运行:
9 u# M& R+ M! |% \% G8 c然后程序在$FF7C这里停下来了。
* w/ B+ }! i$ y% V$ |- L9 O
; R, F# @. [" H然后单击单步进入慢慢跟踪,直到跳转到$8000:6 L: s% v4 E# D# H
& D8 h/ t: L1 |# p, v) P
然后打开6502_Simulator:6 d, B+ o; s5 g4 F8 T

4 r! D0 k8 p- h1 F1 x1 f再打开我写的数据搬移程序:
2 ~/ V  \$ r# [9 _: ]  B  P. ]" Z. i4 o

  g3 a0 F! x9 S! }+ J然后修改对应的数据:4 K  p" D2 q. N0 R+ D
程序开始地址:修改比如$8100就修改为 .ORG $8100* x) O9 A5 ]. ?
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
( @0 f: v( i, _8 h8 C9 d, x( b0 ~从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$820 n8 q8 O" i# L) T' @0 D0 O
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$918 B9 y  {: Q2 j0 g$ S+ x/ X& E
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。
+ K6 ]  J% ~6 l; D# f/ y5 A4 a0 s/ ]' a如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
* K2 N4 w: J7 s, n, Q7 l$ C' u中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
- t. H' ?1 d0 g# A2 Q后面的不用管。
8 T+ {* w7 e, _, \+ f) S$ w9 F* h7 B. V* E9 X' k
设置完数据后单击编译:: L" R( I3 c% e& C' J

/ u; @7 S1 V1 z. ^然后保存编译文件:
# y. i+ _9 a9 Y! ~# C* J7 A  G* w7 S+ q  D1 q4 x* t/ O
选择二进制方式保存:7 k* W+ t1 ]1 I( f& _
% h; n- [. X* |" ^" |8 k0 L
' b' ]4 |! N& N# ^
用Hxd打开保存的二进制文件:4 Q2 V; W  g, ^) @* R& \2 X$ W

# z1 z4 S3 Q0 c2 k$ ^
5 b% m7 t! |& |) }" R4 r跳转到设置的程序开始$8100:6 m9 u) r7 n0 w! G2 K

$ L2 j" l4 e% a( g3 ?
6 M  [% d) P" ]1 J& M7 w8 w; J  k8 }: A7 {, j: {0 f) K1 F
回到FCEUX,转到NES内存的$8100对应的ROM地址:
# \# I; r4 \: I4 W! m% m; i, ~' o2 E8 b
: \& E* J9 ~6 z# s( v: F

; j4 P8 |. t) f- @. K+ n8 M' R1 M" `- r
7 M4 ]8 U7 Y) w7 l2 k0 i- T
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:, |% f/ x/ N2 G, h

& ~: z0 |% K) L5 s1 ~" @. \$ e4 s4 U$ j# [4 _. V4 Z0 o; J
2 a& K; s9 \) k% u0 u$ a( Z
然后转到NES内存的$8000对应的ROM地址:
6 w3 G' V# U. x8 b$ c9 w3 c1 I
5 T3 u) R% y) D8 g- p3 u3 v% Y. c& ]2 f5 q
6 H* i; v" ]  ^

1 m! Z6 f' Z/ s" X' b# a7 }8 K$ a写上如下程序:0 w- q* z- w+ `3 P& Q
A9 80 8D 01 A0 20 00 81  
8 \$ y7 e( ^4 C% `4 ^8 O% @6 J$ J% aLDA #$80
6 z( J" c+ g2 a' |9 tSTA $A001 可写方式开启SRAM
. ?/ p" B& G& c1 q" |JSR $8100 跳转到子程序$8100
3 R7 \. F) e& s# I6 V$ X, t( ~) }
% n$ _7 I) I/ ]! A% T# R然后把Hxd里被覆盖的程序复制过来粘贴在后面:7 H, d4 o) k$ R- c
( ]' `' Y, j- T+ ~6 l

% O' V5 w9 Z) v6 }: Q  }( K: W3 J/ X! M
末尾补上一个0x60:
1 v: {- v* A" d) {1 M$ CRTS 子程序返回
+ ?& m4 Y3 {. J/ ?( v: f- ~$ F* `, f& t; I; s6 `( o
然后单击运行,ROM音乐响起,正常运行:
% j1 x! C* a& d8 }5 @4 ~# J: I7 W0 ]  I9 B9 Z0 W- [, w* B

9 R0 b2 \# \4 c3 n4 b( N然后转到NES地址$7000:
6 J5 x" B4 v9 o. R. }- w3 T3 O: ?' b- B9 ~
( {6 \* |3 S  ]5 ~0 S
& J" g" z4 O' H$ t. A, p
! L+ c5 W0 r0 h2 o  Q, U1 a

+ V  m, A5 k0 o$ q" B; ]$ Y% N. b& ~可以看到,$7000-7FFF都被复制了一片数据。. J8 q4 |) Z. J* I9 B: o
测试没有问题,然后保存文件:, y8 O1 p2 {' M0 @  c$ B

% ?$ V+ M) {3 I- [以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
% @: q% |" g6 O9 G! @" W后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。/ ^. `8 B+ ^1 J9 J
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1179 天

[LV.10]以坛为家III

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

签到天数: 1389 天

[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, 2024-11-17 06:27 , Processed in 1.072266 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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