EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 1 k0 D/ d! I* A* e& d2 a+ x: {  O

: V! [( t- k' D) O# o[FC][SRAM扩容教程(Mapper 4为例)]
  Q! q1 j# y* D& ^+ `8 q5 D& G
1 N/ ~8 S* D) M" F0 z时间:2017.4.28
. }' i* ]! u# T& s7 m作者:FlameCyclone4 n0 d, K% @7 Q9 T) }- T
工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator8 B+ R( B& P' M/ h/ D7 V
ROM:双截龙2(J).nes
) b4 W$ |% _- F适用:没有使用SRAM的ROM
/ X. s$ f# m" s1 R1 u$ b* h
/ p0 T+ I4 Z: n6 M# B# ?9 W首先用Hxd打开ROM:
7 K; o% j: O% M" O/ N* x- O: Q: L- w4 o
然后扩容:
! c7 O  a) ~% {- Y, w6 E* P( q6 u0 i6 @7 }; W& G5 A. B

4 w5 w; }& r0 y3 q1 m# e& }( Y3 H- s" A2 [
: h2 A9 Z  \2 L6 P5 I6 A

' I& }9 o. ]/ k  i  M9 m- y, c8 l
  H: m# _7 _' |# W0 M
4 F5 a. M1 C. c% \+ ]; I5 O
/ Q3 {- r% l5 R: l  d) Q
2 k, k0 z* |# o! }- W3 l) v' B9 p先看看任天堂产品系统文件对NES文件的说明:
4 c" Z" I4 X% y! S& M& s0 n, WNES文件格式! q2 a/ l( t+ @5 x
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
8 q% D; M& l& E# P! f# l6 s9 R7 A偏移         字节数         内容 # X, s# s( f' Z$ A9 X( }  E# \, a! g
0-3         4         字符串“NES^Z”用来识别.NES文件
+ x9 g$ }4 Q7 h: k( M4         1         16kB ROM的数目
" ?' X: M1 n7 P% d  t3 i5         1         8kB VROM的数目
3 x) ~( b: C' I; {6         1         D0:1=垂直镜像,0=水平镜像 8 s3 v$ b9 H7 S
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF * q) V" ~- \$ a4 c" G
                    D2:1=在$7000-$71FF有一个512字节的trainer
6 c9 C: F9 G2 I% v; X, S; D* H& g                    D3:1=4屏幕VRAM布局 9 B' y; [$ L, I' m0 u& R% Y
                    D4-D7:ROM Mapper的低4位
4 @) I' n3 L: J, Y" F1 D/ [1 Z7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) 5 l  f: n6 {& V, Z7 C0 |/ T
                    D4-D7:ROM Mapper的高4位 2 m& R0 T0 @) e+ C7 W+ l
8-F         8         保留,必须是0 1 z* u( L5 M! T8 M* e6 w/ V
16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
6 Q# }# |" B% Q, ]; m, \-EOF         8KxN         VROM段, 升序排列
0 _& C0 r$ f2 k/ J/ j, f5 T5 D  x" W, O# s2 U' `, S
然后知道这个ROM有0x08个PROM和0x10个VROM2 L, p: e  |( P
接下来扩展PROM位0x10个:
* `  h/ p# R1 A) h7 ^+ m$ B. m先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):2 P; B5 @% r! g3 [6 b
% ~/ c  `/ L5 b5 f9 G
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:# f8 H. _# D( \
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。1 ]; E: b4 ^7 U: s0 U, N7 j
于是可以得到双截龙2的是:- q5 i, T) [3 D0 }: n+ \5 ^
(8-1)x 0x4000 + 0x10 = 1C010。
( q/ C6 M- s1 b% p$ R* q然后跳转到1C010:
, \4 V. H, o* I6 q8 x) ^6 Y3 k6 O& j8 f8 [- p& q! C$ y
2 h* z+ ?( k; b( q+ \: W

; R2 D- ?! E) f然后插入0x20000字节的FF:
' u5 i3 X3 i# S+ [% V) E: \/ t% S) d
) i" b1 O; j- A* }: D
/ j3 g. F0 D  P- f9 G$ B, h
然后保存:
5 k% z8 D& u6 s# C0 }6 [; f8 `% ?6 G" `' k* q6 J2 x
0 q4 Q$ X+ m& c
用FCEUX打开正常运行:
6 K+ A8 V7 t! Q2 A3 C# Q9 E$ d' |8 R- G! Q4 r$ v9 y" k+ I
查看文件信息:9 Q- `) g+ t) C' W5 e
" h% S9 f# B' z% G, X

' e+ k9 v! d, _& [7 A9 A3 n! S& t- P接下来切页:
6 [! H( l& b. q0 d( K先打开十六进制编辑器:5 C% X3 E$ Q4 x+ j% l) g1 Y' v& t; p

1 |1 I) t7 v+ [+ E拉到滑块最后,看看重启中断
: W( }0 O, ]6 ^. E& O中断地址         中断         优先权 " I" ]' c9 f6 G5 B9 }
$FFFA         NMI         中 5 a0 O' U) ]6 F/ t- @# d0 a& }
$FFFC         RESET         高
! T, @( `( H* u" D4 K1 A' V( b2 J$FFFE         IRQ/BRK         低 ! O. d9 {3 R8 ]2 N& r5 }6 M
6 k& Y0 s8 G* p, e! w
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。& x! ~5 R$ y! t; f; B4 V
由此可知双截龙2的RESET中断$FF65。4 p" M( P+ Y% @8 E( z3 y0 Q8 p( x, X
接下来添加$FF65的执行断点:
% j/ [. [2 i3 H' d1 X4 _0 s打开调试器:
# ^0 ~5 \; \) [; Q
2 N! p6 n& u8 w: K7 b0 }/ l添加$FF65的执行断点:
; y1 k3 E; X$ ~! }
! n/ [# D4 y0 b% r
) [9 U  v# c3 @7 z% r
% B- {; ?8 L, t% G/ j2 G! W单击确定:; k( z- m  z9 F! v$ b! W# O5 R8 J7 _% {

6 x3 Z1 g  ~& M) W* p" j% `6 W- g3 a" I0 A
然后重启ROM:  G. X5 C  o8 z+ n: Z* ^

# |6 Y) c% I  W调试器此时弹出来:
! |' R! m& c9 D8 m2 c: ^5 D. ^$ B) y# v1 F) H3 a+ F5 Q* ~& F- M( K
然后打开Hxd,写一段mapper 4的切换bank程序:) d- J% h2 a& n/ p3 G9 k
先看看mapper 4的说明文档:6 t2 J5 _2 m/ ]+ b% R, S
Mapper 4% }9 D6 q& Q! ~! j' p

4 T2 f% I, G4 P' n0 ]: j! V. y$8000:  模式号  q: Q# D: k& V9 |. U
        位D0-D2:) [! M0 ~4 W" o& f
        0:选择2KB的VROM存储体映射到PPU的$0000
! i4 f0 b0 b  j0 E4 D$ m% w        1:选择2KB的VROM存储体映射到PPU的$0800! p* V1 A0 _& S" S; N3 O- B& @
        2:选择1KB的VROM存储体映射到PPU的$1000
; X5 o2 @1 [# f. Q8 k/ j        3:选择1KB的VROM存储体映射到PPU的$1400. r6 F; m+ t5 j
        4:选择1KB的VROM存储体映射到PPU的$1800; A2 o/ P6 }* ?0 S" Y
        5:选择1KB的VROM存储体映射到PPU的$1C003 a8 I0 u8 C0 ~7 i, O
        6:选择8KB的ROM存储体映射到$8000  {9 x3 f5 G) X) ?7 q$ x9 u. z& c
        7:选择8KB的ROM存储体映射到$A000
& @4 N; z  D) `9 I/ u: {4 j        位D6:2 E) L' u$ A8 c' B; H3 t; |
        0:允许擦写$8000和$A0005 F5 @% P: E; v+ d" A
        1:允许擦写$A000和$C000
' o* u9 k) A. O, Z  e; l        位D7:
2 k9 I" u. p. K        0:模式号D0-D2使用普通地址' ]" o/ P+ K/ j# b5 v9 O* T
        1:模式号D0-D2地址异或$1000  J# m9 O. N% _& s$ O8 j* ^( H7 {

- m: W  S9 c8 q8 W, d4 C$8001:  模式页面号
! w3 z2 K' ~! Z) M0 k        写入一个数(00-07),切换存储体到对应地址% ^: H. ?" v  m& D# U- U2 o

% ~/ M4 L$ ^; }) u4 k" V6 H" A$A000:  镜像选择
; E. r& e8 \5 ^: q2 w( Q$ r        0:垂直镜像1 `3 ?' D/ q, w3 m
        1:水平镜像
; s7 C- ]/ u+ \  Y) L4 o% ]# y1 Z% H+ l* n& i! r# }1 |
$A001:  SaveRAM 切换! Z  S7 F* z3 w& j& [" {
        0:禁用$6000-$7FFF% {* U! V: R, d
        1:启用$6000-$7FFF! v& {  R, f1 r) u$ _/ g
! U9 l2 u/ p% l7 Y8 ~
$C000:  IRQ计数器; O( R) |, O( @$ y. }
        IRQ计数器的值存储在此处
* T( r+ s, C- E# X9 C+ a5 ?5 Q9 H9 `
0 _" L) ]* R( @) w1 ^* A$C001:  IRQ暂存器% Y+ c' B. ?' x5 L$ \# C. l
        IRQ暂存器的值存储在此处
9 j6 S& M( L, ]+ B+ ?; V2 V  S+ u# g) V1 K/ W% @# [3 Z
$E000:  IRQ控制计数器0
+ |1 B; o1 v5 y3 J8 A" M        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ  l, n1 I! ~8 p: Z

0 B6 F/ b1 |8 g! o$E001:  IRQ控制计数器1
0 m: P' A8 [" R1 x! c        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
0 A  h# Z% ^' R' E
2 K5 W$ j  K& _那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:% d5 k( s+ C* A) e. j; {7 n
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 686 a$ @* I3 `0 i3 |8 c7 U/ O' F
PHA      累加器A入栈
/ o" O$ v/ }4 ~LDA #$06    设置切bank地址为$8000-$9FFF
4 n; k6 y2 m/ FSTA $8000- ~9 w4 P. O% y  z7 d* O
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
; c0 e% f6 ?  G1 p+ A/ wSTA $8001' D# T, L* P( X* G: X$ R/ z# _
JSR $8000    跳转到子程序$8000
4 y+ e! R0 \5 Y+ N( P  |6 B# [0 K) o* [PLA     累加器A出栈+ e( h) b+ F( n% f: c% y
% d. f/ p9 C5 r( i, T& m& u4 e6 B
为何切换的bank号是0x0E呢?
! M" R0 `. ?- T/ m因为在扩容ROM后,文件PROM结构为:3 r# _- ?2 Y1 \9 @
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
# |* A4 V+ Y" I5 b* {) P1 p" m$ N! I7 r$ L9 Z9 G
Mapper 4切bank一次是切8KB,那么文件结构就是:
8 l; x* B% d# g. m原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)/ p! L8 X4 w! M% x( R) A9 x
因此选择扩容的第一个空白bank就是0x0E号bank。
: T. Z; z/ |+ w
# ^  q3 S  h0 J5 j* H! r/ a5 E7 h& \/ x6 x8 n# H
然后去调试器找RESET中断中可以放下切页程序的地方:
8 V1 z% O5 X% _6 ?  N8 k( \首先长度要小于等于自己写的切页程序。% |2 w* l, M& A3 y! t& D! y
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
5 D9 ^% _* k' c) I5 J- e- T9 ~/ U, `( \" j0 `1 w& O) B
然后跳转到$FF7C:: p: M. A; m& o: w

' Y* o5 A( d& U6 ?; c$ e1 p  u! k0 ~, |, E: I

( ?9 K4 z1 J( H$ B  k8 P  C! k4 j5 G( Z" \+ R2 d, v" W& C- b
复制下可以被替换用于写切页的程序:
( ]  x4 E" j5 C' y% B先选中,再复制:
9 L. ]5 [$ R; _, q
4 j% f/ f# F, g! }& w& }! n" Z  t! w. F: K
在Hxd中新建一个文件,把复制的数据粘贴上去:
/ v- d1 t# t, Z# R7 b" `' D" e- h; \

5 Y. \3 X' G6 v, h
# |' a8 ^' ^# ]% x
7 K9 ?- H, I7 q; r! L然后回到十六进制模拟器:
9 u: ?: ^+ ^5 H- K% a# W7 a1 {+ g转到$FF7C对应的ROM地址:
) s, y; S5 E7 b) @! I) E; p, j( p. T( }  O& n3 D

4 D! [- p, ^5 f6 `$ M然后复制Hxd的切页程序,粘贴到这里:
! k. o) O$ g$ r7 u
- a0 C# T% n9 [. u, e
4 n/ [2 a0 E. r2 T1 g
" s$ a+ D4 c0 t. W没有覆盖的用EA覆盖:) N9 }  L8 ?) l1 e" F7 ^

% \9 A+ d( o' J8 I打开调试器可以看到变化:- Q  f+ h+ ]& w

6 U7 g7 }$ m; n" n然后添加$FF7C执行断点:
! `+ K7 {. V2 `( _- @/ A* F! K
! J3 d& _- P' V: C
! Q8 ~! \' M% F0 v, [7 r6 p( c( A3 y7 |. h
单击运行:# P( h: F- j4 v: m9 i
然后程序在$FF7C这里停下来了。& {* {1 V: E/ V7 ^0 `5 F2 i# N4 B

! M  n8 v, I, ~" T7 N0 K0 ^2 m* z然后单击单步进入慢慢跟踪,直到跳转到$8000:, J1 b! R8 l3 a$ Z
) y5 Q$ N; w) n# n( Y! r0 m
然后打开6502_Simulator:
, g6 s8 ^2 y$ g0 x- @+ [  v2 g3 v1 j+ `! U
再打开我写的数据搬移程序:8 S, P8 f) j( l( e) f" }) D

5 |, `3 ?) R6 e; y+ v% j5 K2 n1 ^+ r) [( _
然后修改对应的数据:
5 Y# z) ^0 J+ E1 S7 `  B+ M3 f程序开始地址:修改比如$8100就修改为 .ORG $8100
, O7 j5 M  @  u复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$703 R, Q; C( `) M7 v6 p9 O
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$825 o5 b( o7 C# o  Q, y) ]- ?$ A. ^7 a
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91. u3 C( c/ x' j5 ~8 s
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。
7 v8 a/ A8 ?: Q/ v7 w/ x如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。; t9 b8 k! a2 @. M, W7 Y
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
0 |" Y' {0 L4 b% W5 P$ D  D' V" Y后面的不用管。
( p6 h0 o5 m! b7 r/ G& ?# H, s/ e$ W5 H2 `$ D6 X6 Y
设置完数据后单击编译:
  Z& P  r5 t( ^& V
8 f$ {" U- Z/ k- v3 k然后保存编译文件:
5 j/ S9 z8 w9 x0 o
. s* T# ^, W+ p( H3 [4 S选择二进制方式保存:
3 s1 H9 S" Y* _: J( ?* ?8 y! F- D! A6 t* c8 d9 v7 m4 o: v

: S4 w# Q1 Z' N4 {6 O用Hxd打开保存的二进制文件:* e, B, i: S1 v: u& d
$ Q! l2 h9 a$ o5 S7 b( H+ F

& Y- |5 e' N2 P) j$ n跳转到设置的程序开始$8100:! `6 Q. ~+ Y3 J5 \
3 m! |9 t" h. ?" F: y; b5 i
; H+ ^6 |$ m7 }- b$ s' y
9 `5 r1 q3 I3 Y# [* y! J
回到FCEUX,转到NES内存的$8100对应的ROM地址:, ]3 d7 z: v6 O5 \5 {

+ K0 [" S& q* D, m" J4 V7 }% `
( _8 B/ f: s% d8 K# C5 A% D+ C9 `0 Z) n$ Q" {3 w+ {
5 X0 Y  \. f0 Z# U: p& M' R) P

; {/ L; j' Q! g( S* b: o3 n: w; r6 M然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:  U5 B7 g) Y; B; T2 V( L* s
! p: T! A& S/ k
6 g6 K6 _& O" V: J/ V5 R

" y5 I9 |3 R! ^8 d+ n- ]然后转到NES内存的$8000对应的ROM地址:
# A7 V8 G+ a3 X) T; W1 b2 D
" z2 I4 q# C( Z* _& y% E9 ?' V' Z+ a9 `  D

/ ]6 j4 p3 Q% a% u6 D
2 X( x( t; z2 S' `, A* n写上如下程序:
; w2 J2 l/ @& x) x2 t" kA9 80 8D 01 A0 20 00 81  
+ J4 K/ _( m* G+ a% ^LDA #$806 X* d, h' C( W8 V7 o4 o5 L% K
STA $A001 可写方式开启SRAM* u" x% ^0 z8 _0 ]% Z7 A& p: d
JSR $8100 跳转到子程序$8100  g6 H" J3 o' P& c" T

3 r4 |& l. `0 E* U% h# C* y然后把Hxd里被覆盖的程序复制过来粘贴在后面:
3 m; X7 P& m# I- o2 V# Y* g
& b- }0 d$ `8 L/ @# q' F" U5 i/ u) U5 E+ P/ l

! |2 B& h8 @7 o7 V- f( b  u# i0 ^末尾补上一个0x60:7 t, r% @7 v6 U. m
RTS 子程序返回
7 @9 u% y4 p( o& [. B. L( m; V4 N# K1 }8 W9 c8 a1 P
然后单击运行,ROM音乐响起,正常运行:/ P2 L$ K3 _- |8 g* v2 y% p9 @7 O, h

+ w; g1 {- n0 I
) D( H, ]8 c2 b5 |! j; ^( C, c然后转到NES地址$7000:+ `% N$ u  ?0 q# Z% z& H- m1 s, f( n* ?
) l- K7 Q" M- U4 Q. d6 y
2 e4 ?2 e% L- w" z( Z, v: s3 p
2 J# K6 J1 U3 `5 D  d* x9 I

. Q2 G; H  m/ d0 |  E- Q; C2 c2 s! a; |: W3 `0 K1 c; v
可以看到,$7000-7FFF都被复制了一片数据。
1 L6 W; ?+ i8 a: Z  ^0 N: t测试没有问题,然后保存文件:/ W1 h) r( e, n9 D' e

6 O* x; Y) `: Z5 H4 \以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。5 n  Z, y0 P4 g! M% r
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。% P6 H" g$ T! ?1 ]4 B
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1184 天

[LV.10]以坛为家III

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

签到天数: 1787 天

[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-12-26 16:09 , Processed in 1.092773 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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