EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
! o2 _- m$ P+ z% o% j  k  ^  T! ?8 s2 }5 J! u9 i5 S6 Q
[FC][SRAM扩容教程(Mapper 4为例)]! _' V) J$ J& V8 H
8 s, Q* `6 J9 T  {- @, {
时间:2017.4.28
! @9 [. c$ j  T4 n8 W0 S& J作者:FlameCyclone9 F+ w- G# s7 q$ o! x9 A
工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
8 @7 {2 n; R  D, V0 LROM:双截龙2(J).nes( L: u" @; ~9 z) H( m7 N- L2 p7 Z+ q
适用:没有使用SRAM的ROM6 i& p! d/ p: l6 F3 y

# I3 Q2 Z3 @/ h; V: l( j* X首先用Hxd打开ROM:
; ^4 K+ [* m& u: X& J! f
# {+ Y* \7 F2 k) v1 z. W8 n' e然后扩容:3 b$ T' B0 e  ?- d8 Z. G
6 `6 d" G; w+ o6 }

% O' N+ E6 P8 N; r5 c6 L
# n4 X: c8 l' ^- ?
/ [- n# [  |% a, t  X% a
' C2 z$ p  k$ u7 _
6 g& N: m8 P! K9 X/ s+ p7 \# g- @/ Q2 x6 A  O
7 N2 u! K7 i7 o+ @
6 X5 ?$ N/ D3 T3 L
先看看任天堂产品系统文件对NES文件的说明:/ w/ r# s, a- k" O
NES文件格式
6 W- K9 X7 I; h7 s+ h0 W/ R4 Z.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 . H# R+ T$ B0 @0 G2 Q. s
偏移         字节数         内容
  P) {6 L& @: P4 F  x0 l: I0-3         4         字符串“NES^Z”用来识别.NES文件
, |$ A; y) V# X$ Z  z" M4         1         16kB ROM的数目 8 [6 S: f9 D, @
5         1         8kB VROM的数目 + s) w# a$ q% |* h1 V
6         1         D0:1=垂直镜像,0=水平镜像
4 t  w6 [5 M0 u. ]2 s                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
; i  I6 b* o& r3 z7 i                    D2:1=在$7000-$71FF有一个512字节的trainer 3 [+ N* a4 b' ]; L7 H. p9 U
                    D3:1=4屏幕VRAM布局 ' G2 K3 ~8 O' F7 t: d, k
                    D4-D7:ROM Mapper的低4位
6 i; t6 V6 _5 i7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) " a* W8 ]% ~& u( |- n! G
                    D4-D7:ROM Mapper的高4位
0 C  V7 W+ Q3 N& d  `2 e) |0 K6 G8-F         8         保留,必须是0 + P) e( t' C% b- Q9 {+ u
16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前 6 }+ q+ z% k$ l: \
-EOF         8KxN         VROM段, 升序排列
( {9 C. R- b- H* @& V1 ?
. O9 t; J) H1 r然后知道这个ROM有0x08个PROM和0x10个VROM1 f7 w) P$ R, q( E
接下来扩展PROM位0x10个:& p5 [, w6 I: S( H, [3 k5 y" y6 ?
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
; `  o" `- ?- F+ `/ c! p! q9 x3 \  ^/ [! g+ h8 Q7 T
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:; t3 B7 I4 b* I1 S% n) m) c5 l! l
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
6 E* R; s7 ]5 e! k" P( m  ?于是可以得到双截龙2的是:( E3 H/ Q4 L# V6 Q4 g
(8-1)x 0x4000 + 0x10 = 1C010。6 u7 _+ a/ C8 Y1 r
然后跳转到1C010:) c3 m3 Y1 ]. p. d
  k0 g% V; N. O$ _) t

" [# {9 C, A$ w, \$ X$ H
* v; m1 K/ ^: |5 k4 k然后插入0x20000字节的FF:
( L' L6 i3 e+ y+ d
7 b  i1 v3 y0 K0 t* [! l4 b2 ?9 h7 b
- I# t# O+ Z: e' L
然后保存:
  Y1 O* Z' y' y* ?- K
& ~( {- |/ t: u, T: R# r4 p  [7 D  X6 S7 @( i6 l
用FCEUX打开正常运行:
) G0 n8 q! o: X; H- d
+ Z( D4 Y; T. h! ^' z, n9 x- ^查看文件信息:% T' F! z0 k& \- K7 n6 f9 I

; z1 C+ U5 @, u( E+ c3 g
; v9 O/ |" l& y+ n) o6 r. e) I4 J接下来切页:
6 J8 J5 h; K& l0 f4 B" y  {先打开十六进制编辑器:
- R) i0 f) f: r2 |" `- p7 ~. t# l. D( E  j" P9 l
拉到滑块最后,看看重启中断
  M$ g. F8 x+ b; L3 D中断地址         中断         优先权
6 A) Y1 R2 V7 O3 G/ A$ ]$FFFA         NMI         中
9 ~8 W5 e2 E5 e  [$ |  t$FFFC         RESET         高
) o- H+ F5 G/ v4 d4 N$FFFE         IRQ/BRK         低
! S8 G# C% y, b6 B
/ e4 V& h8 @3 `! QRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。8 q( @* R# k* s3 c5 s+ u6 v
由此可知双截龙2的RESET中断$FF65。
# c! f! n# y  d* M) x8 D) Q' l: i" I接下来添加$FF65的执行断点:
( }, V! v3 \5 r) S% ?) h打开调试器:' H2 ]- M) Q. J
' K# }% ^, {# B; v6 i& V
添加$FF65的执行断点:
( v% M' S2 A& q2 v' j/ k# j' C: X/ v/ n. R1 R; a- W( p# P

# E4 K& i+ p2 v- Q8 t) F8 }& c, w3 {
单击确定:+ ^# i/ J1 @' ]% s& P0 _

7 N( S  G% L# C' D+ v8 O: q
" z8 e5 d+ O: e然后重启ROM:
9 k) L/ o! b6 [3 h+ U* u1 m
6 z/ H' K9 |3 e7 S" \5 Q+ R调试器此时弹出来:4 I' i" m5 n0 B7 _* z) x: S
; }" a- K5 {2 Q9 y
然后打开Hxd,写一段mapper 4的切换bank程序:- ~+ ?% v3 f' F. x7 b
先看看mapper 4的说明文档:  W2 S% P0 \5 K5 Z8 A1 {% p/ N( Z1 q
Mapper 41 D7 `- e  ?, k

4 Y# G( y6 s- A$ D$8000:  模式号6 D/ z% u; G* f% _3 `: d) }+ `
        位D0-D2:8 _7 f, N7 R3 }' i" Y- Z7 Z
        0:选择2KB的VROM存储体映射到PPU的$0000, F5 O- B) S. N6 ]1 A4 Q+ [
        1:选择2KB的VROM存储体映射到PPU的$0800
7 G/ G2 Q1 P" T: J- W        2:选择1KB的VROM存储体映射到PPU的$1000
2 W+ e6 k2 D8 [" a" T2 e        3:选择1KB的VROM存储体映射到PPU的$14008 l5 m+ N7 |# L& D4 N9 P! H
        4:选择1KB的VROM存储体映射到PPU的$1800
$ t8 r" g; v: q$ n: x1 P8 N& h        5:选择1KB的VROM存储体映射到PPU的$1C00
& q0 h3 p9 O, s  V. T6 V        6:选择8KB的ROM存储体映射到$8000
9 ]/ P% e4 {5 @) q0 v8 c        7:选择8KB的ROM存储体映射到$A000
! X1 D5 X( `; i* \        位D6:
, v  ^8 o  p$ R( I0 `        0:允许擦写$8000和$A0007 W1 d' j8 T2 _4 }
        1:允许擦写$A000和$C000
8 j+ V6 W4 J9 x        位D7:
( r9 W! l5 c- p& @6 o0 j        0:模式号D0-D2使用普通地址
0 |9 L  ~8 V9 O        1:模式号D0-D2地址异或$1000
0 E" i+ p9 B/ u
; X4 G" a6 `: I- Q$ F' Z$8001:  模式页面号% j" z. R; W9 {- p/ y' D
        写入一个数(00-07),切换存储体到对应地址+ j. k/ E* L! h0 ^9 I# ], v$ X0 n

. y1 T6 `( J& R4 O9 l: I7 t$A000:  镜像选择$ a# M8 y3 o6 H8 I: l! @' Z
        0:垂直镜像
3 ^3 T# k9 K) Q. n  J4 l: L; R        1:水平镜像
, N2 j: z; k  w9 l, x. H, {1 h+ K4 @
$A001:  SaveRAM 切换
. j# O0 R  u' z( J: ]  Y        0:禁用$6000-$7FFF. A& v3 a4 p, K$ \& n7 {' R# s
        1:启用$6000-$7FFF9 u/ \  B  R  _8 {* p

) z% L9 L. ]" Y1 V8 Q1 ?8 d$C000:  IRQ计数器1 M/ ^& q' E3 G; @' \7 w0 l
        IRQ计数器的值存储在此处
9 K1 y6 F. A; x2 @( d4 l" x8 l  u/ V/ w9 Q/ d. V
$C001:  IRQ暂存器
9 D  x/ M% J  g/ y        IRQ暂存器的值存储在此处2 `; e/ z0 l3 |; a. O. I

3 F4 T+ X+ Q4 a5 p. I0 d* o$E000:  IRQ控制计数器01 P! x, k, `+ m" h( ~
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ) Z. z6 X5 ^3 D  S

: X: K9 g* Q7 M9 r" T$ }- }$E001:  IRQ控制计数器1
, `! H: A" z. t5 B% d  T0 R        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
" J- N) a: B/ S1 O) [% x7 |. B/ j9 Y; ^9 T) ]5 F' h
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:0 d( m- m  v$ J1 G/ N/ J
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68$ N9 O4 R  [9 A& S% s) E
PHA      累加器A入栈5 v" P- ]5 ?) R. d, ?9 H/ ?8 L
LDA #$06    设置切bank地址为$8000-$9FFF
; v8 Z! P: T9 u# vSTA $8000
6 G9 k  H: |. o" ELDA #$0E    将第0x0E号bank切到$8000-$9FFF0 W/ ?) r( `* e1 T" G' W
STA $8001) j. Q8 `# ^& }, o, Z/ o
JSR $8000    跳转到子程序$8000
$ O, E5 C  Q  jPLA     累加器A出栈/ B5 G% h# c! G& ~4 M3 M
( m' w/ j" c" }* {, Z7 u
为何切换的bank号是0x0E呢?
3 N$ e0 a7 S6 B4 h因为在扩容ROM后,文件PROM结构为:) j. \2 r: O+ X: B0 E# r
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
) T" E& o3 W' ^$ k
* B4 \3 R' t( F9 O1 V+ sMapper 4切bank一次是切8KB,那么文件结构就是:
0 C" H! D: N! i! ~' k# j原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
* t1 T; x3 V* V+ W" t因此选择扩容的第一个空白bank就是0x0E号bank。" k9 ?" C: H. o9 u$ |, {) k

2 m/ r: H, ?3 X1 N" |' e6 J& X/ c, B2 w% ?
然后去调试器找RESET中断中可以放下切页程序的地方:
1 s3 x! t3 G: R0 O首先长度要小于等于自己写的切页程序。7 Y8 d7 x# e6 R6 T3 T! c+ h
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
0 m! V) u1 d1 j- D# v
5 Q: Z, \$ |1 m5 h6 u2 i) i然后跳转到$FF7C:) W+ E1 K. v6 f' _+ L

9 |" [1 f; T' j: Z7 N9 O) ]& c- T* ?: A& {" }- p' B+ \* s

* T% Y: k' f6 |! G
! f8 ~- S9 m: m4 I& b$ r2 w复制下可以被替换用于写切页的程序:
3 V& o* v0 p. W5 k+ F先选中,再复制:
$ f, N, d$ ?, O7 ~2 L2 `6 Y7 k
6 {  J# f1 v3 B9 g7 s
在Hxd中新建一个文件,把复制的数据粘贴上去:
$ K" D( I% n2 Y: v) _$ a3 t# l+ Q" f! U6 u. E' H/ Y
( ?% c  ^: y% l# O- m/ E

6 ^* w% U8 Y6 l/ K" J, v- O/ h- s4 Y0 r( X
然后回到十六进制模拟器:. v) q  f, I8 ^
转到$FF7C对应的ROM地址:
- w& N. G9 s3 N2 s% n2 E3 l
/ X' e. \, b3 q7 x: B9 \, Q( y; O3 E  ?$ Y3 _/ |( I
然后复制Hxd的切页程序,粘贴到这里:
, Q; B5 {- b) u0 _& v9 x; T6 v- ~* F" E6 R! I$ _3 k

- M* K: ?3 x% R
! {; I6 }$ B# G没有覆盖的用EA覆盖:
6 p/ r% p$ ]' N; k+ I% f9 [
4 D0 O% I. k/ M" T打开调试器可以看到变化:
# ?% x" c+ w; S7 \5 D0 i' k3 W" j' a' a, ?; u: k1 t
然后添加$FF7C执行断点:
$ A' L' G% F% C, x
2 m9 z( Y, ^( s  \! ^% e; h( d2 R$ E  Q1 F
4 n2 u( m' l& w3 g* b2 @
单击运行:1 C4 o1 z7 A# t/ y; |
然后程序在$FF7C这里停下来了。
' n. Y) I8 h5 M- z
7 m6 N* _7 q, J5 ?. ^然后单击单步进入慢慢跟踪,直到跳转到$8000:
" m! a, J, a$ q5 D8 q3 g! F; T4 r3 F  D) Y4 o0 b. q3 O5 m. D
然后打开6502_Simulator:0 s& F0 G8 j& o0 e
( v- L; Z6 V4 J+ p1 [& B1 U) q( c
再打开我写的数据搬移程序:
- D& s, I& R+ S! j& \( {9 J1 s% @4 P3 _, Q9 n5 V7 C/ A) V
+ s. L* p) y/ i# q0 W$ X
然后修改对应的数据:
( x4 w9 w6 T7 D) h9 y- f+ ?9 O程序开始地址:修改比如$8100就修改为 .ORG $8100
2 W) U" w- u  ~复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$701 i' L4 O9 J& m0 T( @9 Q  N
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
1 F: G  P0 }8 D& K# V& L想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$918 Y6 D% k4 b8 J
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。" g) W$ t9 Z+ y! V( ?
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。/ Z& ^7 Q. R0 F8 H. X2 Y9 ^- n
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。0 x6 B1 ~5 z6 `( z* }& c5 P9 Q
后面的不用管。
, ~" b" B& c+ t! Q5 e+ U2 Q% ^  w) \: M) N) d
设置完数据后单击编译:
: [, {6 c% b( W* ~: P
0 u4 m7 ^, E( ]- Q+ T然后保存编译文件:
( _" ?9 W: C6 G) f# v3 e9 i8 k4 F- A" @7 m/ N. e6 @/ i* a: p
选择二进制方式保存:( ~9 O+ C( v5 P% }4 e% X* i

* y, t# O' I4 O2 A& l' _; B
+ X! d2 F6 e6 |" B8 o0 G用Hxd打开保存的二进制文件:
% u* R. f2 {8 m; f$ v+ G7 K  E4 [
# z8 d* c+ `6 v7 x1 r0 u  A
* ]- ~/ Y% l2 x- J; g跳转到设置的程序开始$8100:1 A6 l% D1 R- K1 u% k2 _: n

" x& C, T6 l8 r' K9 ?3 `
: v1 X( e4 N/ X0 ?
/ T9 y  e- P  s+ W: @$ ^! Y- T回到FCEUX,转到NES内存的$8100对应的ROM地址:
! q( ?! B% t/ z& H7 ]" b
% N$ w0 c3 @! G( M6 R- P0 [, A& N

, {$ q& o8 }- L
/ I9 X' d7 V" n" `  T" p3 K& s' U8 V. ]
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:* o; a7 N2 e! N# u- V

* x( S2 ~* \) b2 W6 v. N$ |' T1 h6 i: g0 R2 ?* {0 z

. z7 Y. g2 n) M; E: o& {然后转到NES内存的$8000对应的ROM地址:9 K: M( m; Y& b; c- @! b5 h9 w( K! |" T

% {/ A  M3 a: Z6 ]* M( j- m  B; E4 w7 V3 B9 a
  D+ {- `0 Z8 R+ [

& b, X3 n0 O$ c( J8 ~5 X, r写上如下程序:- [% R- S  A3 `" f( f6 B9 d
A9 80 8D 01 A0 20 00 81  2 @2 u7 R: U; T; F; w* U, h
LDA #$80
; M6 U7 H) y% A& M* R/ \STA $A001 可写方式开启SRAM9 I( C) C$ e& r3 s  R( z, |
JSR $8100 跳转到子程序$8100
% U  \" y8 _# x
: [4 ^( T3 f5 d. c, y2 t然后把Hxd里被覆盖的程序复制过来粘贴在后面:
4 r/ z( W" v$ U" P) m3 N
- N9 t1 k7 x; A: N0 G- f. z/ ?9 x7 r5 e& R1 F; J
; a& J$ A" ]! o! R4 g* {
末尾补上一个0x60:
1 F: d4 g- g2 \- t7 Z* x' Q- NRTS 子程序返回# G( [& d" [3 n$ U; D5 m

/ S. _: z' D2 w9 Z& l然后单击运行,ROM音乐响起,正常运行:% E  ^+ J, i" C/ |

" V9 T% {6 k; L% r( @* s* |7 E' @9 d4 R" w% E) ?* o8 L. u6 Z
然后转到NES地址$7000:( N' Q  @$ n+ m+ Q

! Y; P0 a& D$ f2 h* G7 t4 o( b: l4 ~) P  M2 y0 L: q

1 m. `1 I" g5 d# `0 w8 h
- D6 r/ K6 o2 A
6 U2 W6 w, i. ^9 M" v6 P可以看到,$7000-7FFF都被复制了一片数据。3 e+ T. U5 }$ _; q; q+ R! M4 R
测试没有问题,然后保存文件:9 @# n" O/ o( ^

6 ~& G4 R- R) |8 S% d以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
; T4 ?! M9 H7 X9 {, v后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。8 d5 G) v& m4 t  @- s) {
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1183 天

[LV.10]以坛为家III

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

签到天数: 1589 天

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

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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