EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 8 C) `, R  X' Y
' V& F: i+ A: F, W5 ^% u3 R9 ]
[FC][SRAM扩容教程(Mapper 4为例)]3 Y( n9 r' j1 [6 E$ I

# K- f$ A* i6 r) @# Z: _( ?时间:2017.4.28: Y# G1 ]" `0 s, [& b, P
作者:FlameCyclone
! @2 h4 I6 W' ]' g, ?工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator% J: p4 t, T! k$ P; Z/ z8 V! a+ \
ROM:双截龙2(J).nes
* o$ I7 @& T- F9 Y适用:没有使用SRAM的ROM0 d+ p. I! x  f$ U" w* T! Z  V$ Q
  p! y. a) A3 M$ s5 T
首先用Hxd打开ROM:
# Q* {( Q$ z4 N; F3 h7 S+ Q) @" t5 A# m" F* C* x% I- Q; i+ \5 [) a+ Z
然后扩容:
6 L1 Y& I" U% b2 W* M+ K. b
/ y: K0 a0 |4 e& l& l5 Q9 L& E8 I% \4 c& `; s5 M, }$ h; c

. E; @2 C; H0 T- K7 \' k  I" Y5 G7 s( g8 [& y5 w6 g$ P

3 A, j9 |1 W8 U) m. X
: s, q4 |' {) p7 \5 a% Y% u0 k+ `8 y# ~* P

3 d6 b" b5 y! y: Z
# d- I8 l3 O: U% W: u" w6 O先看看任天堂产品系统文件对NES文件的说明:! K- N* W' J1 |) {  L( \5 Y7 ^1 T- ^
NES文件格式
( ^$ |, Z, [2 w$ G" y% b4 Z.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 3 V+ D3 T. h3 D7 ]2 K+ W( n
偏移         字节数         内容 & K9 Y+ c& n8 P7 \' W" T
0-3         4         字符串“NES^Z”用来识别.NES文件
$ D' k/ W' z$ m4         1         16kB ROM的数目
2 k' m* c# a2 c2 ^- F9 R# j9 g5         1         8kB VROM的数目
  Q! O# c: }9 w# M7 T/ H6         1         D0:1=垂直镜像,0=水平镜像 " N0 u& B* B7 u1 U; S
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
8 Q3 {) }, f! _, p                    D2:1=在$7000-$71FF有一个512字节的trainer
5 s, x! L; ?* J                    D3:1=4屏幕VRAM布局
% U. R8 l& z: f: r# A                    D4-D7:ROM Mapper的低4位 ! e5 J# T: P- b# {
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) 7 z1 ~$ q4 m" z* s9 B
                    D4-D7:ROM Mapper的高4位
; S, j- }1 ]/ ~8 i8-F         8         保留,必须是0
4 G$ |9 p" J+ C' G) G" q* Z16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
  B% {! K! m& W. T+ N* ?3 i-EOF         8KxN         VROM段, 升序排列
7 e( s" m1 @* E" c: d1 `- B4 W/ k1 x* A  @  n  F
然后知道这个ROM有0x08个PROM和0x10个VROM
" z- v& _4 V1 x; u接下来扩展PROM位0x10个:
/ y: K' J' t0 e/ O先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
' t) O' I- f" t& K+ c# T/ L
+ R+ K0 ?3 |! j" X由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
: B( O% ?- {5 |) J% N, `最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。3 q5 K. @- \3 f8 E9 Z
于是可以得到双截龙2的是:4 T. r+ m+ c! i  ]0 ^3 s
(8-1)x 0x4000 + 0x10 = 1C010。. ]7 ~9 M8 o% z* ?$ y) N7 v
然后跳转到1C010:+ }; f1 n  g& d% O) I6 f3 V

0 A$ j1 y7 I5 k& X* G+ \0 _- p+ s; T- t  S7 D
3 y8 {  f3 r5 b. b  S3 e# p7 h
然后插入0x20000字节的FF:3 M0 i8 ]2 h6 I  x! P5 t3 u

5 m) j% A4 |# B" K$ @' w4 R0 O3 ~: h  W' q, U
4 C0 Z0 P* C+ |/ |7 A
然后保存:
- Y# ?* e  u. l/ h1 i" o' @9 O  o0 l' t/ p: N# q: _6 Q& \

- C/ h+ M  T8 i  {$ f" N! M) O用FCEUX打开正常运行:
* ~& Q9 e: D( Y; C, ^" ]# T8 a; q1 ?; @
查看文件信息:4 \3 e% _! t, n$ R
' `: V' @0 f6 f! J9 Q# R) T7 k# `

* ~: U) b5 r8 y; m0 F接下来切页:
0 K% X# e1 c. I6 i先打开十六进制编辑器:
  L/ k" \( c0 W0 ?9 v) w$ s
+ Z/ x; g* p& ^( n2 x$ V: W  J拉到滑块最后,看看重启中断
" I9 |) Z  [# ?, D6 }中断地址         中断         优先权 ' h" H  }( T* }" [0 n+ K. C# t, ?
$FFFA         NMI         中 % U0 M0 t$ r2 C" T2 F) S# O+ ]
$FFFC         RESET         高 2 y9 b* S% F. Z
$FFFE         IRQ/BRK         低
2 c0 k7 \7 @8 }& S  n
7 H) t) K6 L0 M, B8 HRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
8 g6 X9 |0 r2 H) n+ r由此可知双截龙2的RESET中断$FF65。
. P4 G% A$ y) R. }6 G* ^6 d' y接下来添加$FF65的执行断点:) D* X% Y3 I( I' m; X" v: x5 {- S- y
打开调试器:
" g- {" D2 k' @6 H  M0 x/ X
5 ]4 T) l) g/ G$ P; W5 j; K添加$FF65的执行断点:
+ v2 k9 N) t1 l! ^/ X% d$ H! K- J& N
- N0 a  i; S6 Y5 t, b
9 U$ v! O2 f4 D# v9 N5 E% _( W: P; z; g( A" Z
单击确定:
; P$ [- `7 \# C0 [& y* g
* {+ q4 r0 O. K: c/ m$ A5 ~9 M. P6 f2 k3 {& J2 W& \
然后重启ROM:& U8 F6 I: W/ `9 m, ]% ]2 n3 B
- Q3 I' H: F7 r% A% R
调试器此时弹出来:
; @# p8 O& J- ^. ]+ }  _: U& D2 V1 ~3 c" z& ^) a' M
然后打开Hxd,写一段mapper 4的切换bank程序:& p6 Q4 m2 ^* }8 k$ v# m
先看看mapper 4的说明文档:' o1 L$ Q3 R# M5 o+ F) `# z: j
Mapper 4/ r- h: \9 j. s' p" [$ c) q
$ g: Y& Q, Y& y  [: @: ~# z
$8000:  模式号. T. k9 l  S& O* _1 L9 ~1 B
        位D0-D2:- ]- j  j. s2 b2 F0 y" P# G
        0:选择2KB的VROM存储体映射到PPU的$0000
9 v& ?1 ~6 _& ^. p        1:选择2KB的VROM存储体映射到PPU的$0800
: S) l7 k! i; E        2:选择1KB的VROM存储体映射到PPU的$1000- Z* E1 j' `, m3 w" ^! l
        3:选择1KB的VROM存储体映射到PPU的$1400) }$ F# D$ ?2 M. v1 [. M
        4:选择1KB的VROM存储体映射到PPU的$1800; ^! R7 ?2 h4 d5 I8 ^' j
        5:选择1KB的VROM存储体映射到PPU的$1C00  u/ P% c8 Q! J4 }
        6:选择8KB的ROM存储体映射到$8000# Z6 ~! G5 y# c* O7 w( |6 m
        7:选择8KB的ROM存储体映射到$A000
8 I4 R  G( ^; P; `  n2 J+ L0 _5 I        位D6:. V# S* ~/ Z1 T( e
        0:允许擦写$8000和$A0008 K9 r% x7 Q2 a3 ~/ X
        1:允许擦写$A000和$C000; i( {4 y- @6 R7 s7 `
        位D7:
' q  V9 V# S( D% r8 W0 x$ W        0:模式号D0-D2使用普通地址) T2 k* q; b) A( Q* r4 ^
        1:模式号D0-D2地址异或$1000
& S) b: A' u# Y# A, z# E. Y, o7 J* ?5 C2 s  I% F! n
$8001:  模式页面号7 W7 u7 F5 q  b- d/ m" V4 P, T  s$ ?
        写入一个数(00-07),切换存储体到对应地址
3 N# _" B) e- G& d* T
/ R2 u- a$ u) d) o+ @$A000:  镜像选择5 |/ ?$ N! r9 w+ {  X5 M
        0:垂直镜像$ m4 c9 m; l5 |' v. ~/ ?; `8 ]. n
        1:水平镜像6 {& h: L4 D, A( {4 H

5 _! j+ K0 w0 g6 H6 N& T! F5 s, Z1 ^6 L$A001:  SaveRAM 切换
# _9 C. z9 w+ S, e; C        0:禁用$6000-$7FFF; O, m$ p4 P* s; w
        1:启用$6000-$7FFF3 [: P1 k, T( N* X; Z- @
. C( \* @& ?/ b( `/ V
$C000:  IRQ计数器: q$ f  x* x4 N/ {- K
        IRQ计数器的值存储在此处' n( H, F" ~- C* K% o$ e1 X) j
3 r" P, ~, e+ Q$ K. T
$C001:  IRQ暂存器
* j1 {2 W% [0 e  a$ C1 U  U2 D        IRQ暂存器的值存储在此处. n' ?1 f0 \4 C9 }

) E6 y% `+ b  p) o# M) X$E000:  IRQ控制计数器0* M0 O% d! b; M! y
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ$ u# q7 o* ^! X! A5 ]7 ^* N
7 F* F" ~8 S( d6 Y  q. z8 _
$E001:  IRQ控制计数器1/ f0 o' A% X9 b, x$ d$ H% o8 Q. B2 a
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)! t1 s  G  R* `" j; D0 N% W
* V! P0 F; h: g" @
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:& H9 U) N3 D1 T2 }6 V
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 687 P9 \: W5 ^% M( C. Q" f
PHA      累加器A入栈: K, i0 `& p1 |4 d$ B* K0 A) k- ]" P# S0 u6 f
LDA #$06    设置切bank地址为$8000-$9FFF4 T5 M; b, N' @  p
STA $8000: I9 ~1 R/ v4 H( D4 q5 Y) h
LDA #$0E    将第0x0E号bank切到$8000-$9FFF# B. K0 Z7 m) h, b: ^* G/ h
STA $8001
! Z" Y! Y8 h# L% S$ IJSR $8000    跳转到子程序$8000" M# R9 B- \3 F
PLA     累加器A出栈1 g: J8 r  t3 `. ?# ^; B" a
  @. `1 `8 L5 E% q; b
为何切换的bank号是0x0E呢?
7 i  T2 o8 D1 f" s6 _# m因为在扩容ROM后,文件PROM结构为:
" |5 f9 i& \% [; I( \  Q" ^1 e) `原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
8 m' w" R3 _  Z* y/ I2 O. |9 H5 _, H; b# I8 c7 ~$ o
Mapper 4切bank一次是切8KB,那么文件结构就是:
) V/ [2 L  n, Y6 k原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
5 @9 o9 _4 c( \0 |. H/ C因此选择扩容的第一个空白bank就是0x0E号bank。
/ ^9 |  P# \- t3 a7 u; H
4 n3 g! X" z5 c( F/ _; p; u* ^: ]
然后去调试器找RESET中断中可以放下切页程序的地方:
4 S' n4 z! [' ~6 A2 @首先长度要小于等于自己写的切页程序。
# N2 S8 Z2 O5 i( D- z( ?3 P可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
( f7 w. ^2 j" _8 z1 f7 L$ L; A( I: w2 c4 W
然后跳转到$FF7C:
+ q5 I% o  Q! G; {1 f" P# g" B" T/ B! l; m9 f

2 \4 |' s) r( @0 R
% F5 j; K6 L0 ~6 n3 y' C0 c
$ p5 _: v* k$ r& K, I复制下可以被替换用于写切页的程序:
* Z( ?+ f1 ~2 q% I; ?6 g/ P4 P2 r" u先选中,再复制:3 j. F6 Y( l6 U( i1 u% O

0 Z& }" x+ z4 ]9 x# W3 B" x3 F) k7 J% Y  I
在Hxd中新建一个文件,把复制的数据粘贴上去:4 ?/ \* r1 v9 W/ s$ }- z$ r

* W, ~. C& c- v
: h' x; N, i7 F$ x" L4 K" b) I0 W4 ?8 T
8 C1 B' b/ o* M. ?1 ?" U& X) W. `9 i, ~1 j# w
然后回到十六进制模拟器:, U6 {4 u+ E5 W7 k% S0 @
转到$FF7C对应的ROM地址:! m( a6 \' G: P, O1 `9 ^
& P2 C" F! P- ]1 y8 [/ p

: X2 V! [9 \1 J+ d* ^8 a% P+ p; }; V然后复制Hxd的切页程序,粘贴到这里:, S: h- o+ q  k- b
- i: p3 ^+ K. b" h' m' f* o
* H) O# g- {/ e' m

( f: N; _3 P" [1 r7 h没有覆盖的用EA覆盖:* u3 w/ T8 Z, x" W
* \1 b/ X9 t6 [3 H- H7 A
打开调试器可以看到变化:
: z7 j- R9 h) b
, A, v7 G% N0 B$ ]7 C然后添加$FF7C执行断点:
0 p; G$ L6 g: |- g  b) g2 F9 m
  J; x1 Y" K: C( N/ L  V
* p, a2 @( F9 K( _  A7 ]( w# p  X. h7 H& ^, t1 I1 j5 y  D
单击运行:* H2 m- W0 X2 a: }2 d' t( t& x$ h( \
然后程序在$FF7C这里停下来了。0 H+ \: h7 e; W3 @7 j  c0 I
; J0 J) `) l8 ^- [3 ?& c+ _
然后单击单步进入慢慢跟踪,直到跳转到$8000:# ?& u" K( ]  I! I- S% j
& T4 c3 o+ n! ^9 k/ ?
然后打开6502_Simulator:
2 l3 f7 M- C6 }" N3 \& \5 b+ q2 }
- Z4 s( R, R$ m5 P3 E, E3 g0 Q再打开我写的数据搬移程序:% W5 w. T6 Z( v% j6 J( I$ _

% e! x! g5 t% t+ O3 ]* `0 D, y
% F1 h# z# Q7 U! X0 x* N然后修改对应的数据:, w, M1 f) |7 ~  }+ y, H5 B
程序开始地址:修改比如$8100就修改为 .ORG $8100
7 Y9 ^/ m9 R( @9 l9 O: \复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
7 \' I. s1 o2 I. k+ _; N从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
1 a6 s; F8 M0 u- a5 M& |1 G想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
" ?* D- m7 o. m  H" G也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。$ ?* m. A8 w7 w
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
& d2 k4 y" Z( m- |  u$ X中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
; l+ k& ~* M+ A# @, F: a后面的不用管。; U- V& I9 ~& ]: q
7 K2 R; F$ |" ^3 B# j( N
设置完数据后单击编译:; n9 l. c' D  [4 P! M  k
" @' y; `+ h! {- r, ]! `
然后保存编译文件:' k$ z. b/ c8 Y) B3 J
" R% _& Y* O* Z. n! s
选择二进制方式保存:
+ y1 u3 J& Q6 X% {" K
/ \( h6 w6 P' Y* j4 ~) _$ C- W+ ~" J, w
用Hxd打开保存的二进制文件:
, X" p& M+ o# M8 w' u; @( P
( w8 i3 v) Z  {6 W' S# [. ]+ [& Q1 E) m) ~* H: Q) Q9 j+ W
跳转到设置的程序开始$8100:# q/ W8 @$ ^! L6 U/ d" M

9 b2 S" C& s& h9 s$ S0 \
6 e( k1 I$ p: K* `
5 ]) V% U" o8 E" M" ~回到FCEUX,转到NES内存的$8100对应的ROM地址:/ t( ]6 v( R7 |4 _- T

# o, d9 I6 m( q- g+ e% b/ T& Y% a: Z. P9 v
& h; A; S( N  e9 b
  i; K4 j+ S; x: @  d5 n$ W
' Y& e: X7 O. @( T- B) j
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:- j  P8 b( [% T/ S5 `6 L
) i1 A% M% ?& s

9 y7 ?0 P' s4 G" U" e# W- W2 J4 x. {/ A" T: p
然后转到NES内存的$8000对应的ROM地址:" d) p( D9 J/ ?" W
1 r1 @% F1 u6 K  R; f' J) K1 p
9 K: D) Q% b, w! g. W4 ^
* n/ \: W0 n; @

2 S3 W3 i+ K( M3 y# _写上如下程序:
4 X* Y/ D5 u; z  JA9 80 8D 01 A0 20 00 81  
' D( s+ ^4 o' {LDA #$80
# Y$ @; k. G9 H; b# mSTA $A001 可写方式开启SRAM
4 y& o; I+ y- D* sJSR $8100 跳转到子程序$8100
3 ~, q8 f5 g" h2 z8 E0 b
, \  k& B+ j0 D% Z2 H, X然后把Hxd里被覆盖的程序复制过来粘贴在后面:2 x) K% Z- F6 d9 p" H. r4 G9 ]

( [; L% V1 e" |' ]  |  H5 r4 @
' o* s. G- H# j  S6 y5 v* a+ D$ d1 O$ a: i! @: l% I
末尾补上一个0x60:% J, ~8 P1 k' R$ y7 d
RTS 子程序返回
- U5 H8 o# s, x6 Q( E; {8 G6 S2 s- ?+ F7 f3 y- ~, Y: n
然后单击运行,ROM音乐响起,正常运行:
% S& a' _$ X1 ~: l) J
# R3 X4 `9 c+ l- n7 I4 l8 P! P6 O8 w. s2 m, z7 m; J( B
然后转到NES地址$7000:8 M3 l" A- Y0 w# b% i: @
" F$ v, F$ m  u: w, D

3 ], k$ e/ O$ K) _9 v: w
3 p4 ^8 k7 V+ X$ u
% s1 B) m. w# s7 P* z0 J8 g# [& v' f: g* J; i" P
可以看到,$7000-7FFF都被复制了一片数据。% S1 |" O+ [$ @9 I' d
测试没有问题,然后保存文件:
9 Y9 }, v, W6 v; ]- ~1 ]' d( k. y* @4 K
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。# t4 [, H6 o$ c; w
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。( D% ]4 [' U. W. t3 P
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1180 天

[LV.10]以坛为家III

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

签到天数: 1420 天

[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-12-19 06:50 , Processed in 1.093750 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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