设为首页收藏本站

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
& }# h7 W2 n8 X( v" O1 R! I
7 B; _: y* z' D$ ?" d) t[FC][SRAM扩容教程(Mapper 4为例)]  k' `  _! ^# }( O9 K

' e; J( G0 o8 Y1 Z2 L时间:2017.4.28
9 Y7 G( V# L3 t8 C1 v6 s作者:FlameCyclone
1 H/ x& E+ j  l# [$ s工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator( a$ d/ ^/ e$ H) L
ROM:双截龙2(J).nes# \. y1 x  w3 p& y& e* c
适用:没有使用SRAM的ROM! i3 X2 R# Q1 V8 O
0 M* l, F1 ~5 m$ H2 f7 M
首先用Hxd打开ROM:1 C& s% Q5 k3 U5 M2 Z
* G, M- A% K3 R$ p
然后扩容:; i3 _& j7 V# y$ h) k4 c

  G0 A6 c% A0 m# B5 X4 K7 @
& l2 v, a0 {$ _
. n  q9 v6 c$ B& {5 N9 @( q6 i, s8 A
, d" i6 {1 K' w$ N3 Q( g5 T8 T0 u2 i  j* ?/ n" j9 o/ g
9 M) Y3 ?2 _/ H6 p. ~. e6 F
7 i4 O2 u* C3 L8 T% ?+ B
0 R/ R' M1 S( a3 i& h% X

0 l- W/ U9 k' v1 F) e# k先看看任天堂产品系统文件对NES文件的说明:
5 f/ n% i% J* B' W& G* {: f# INES文件格式
- l. q4 t, Y$ [6 S2 v; ~3 Z+ X3 f6 ^.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 9 S8 M3 C8 Z0 s9 x1 u3 Q0 Y" p
偏移         字节数         内容   j$ `$ H% X; j
0-3         4         字符串“NES^Z”用来识别.NES文件   P7 p0 x$ x- }, X# Q6 V) J1 u2 i
4         1         16kB ROM的数目 ) T9 l% E# {$ p) p/ h
5         1         8kB VROM的数目
+ w. q2 o6 _! v5 l4 L0 X! s# n6         1         D0:1=垂直镜像,0=水平镜像 % }; l8 A: d( Y% S$ E# Y2 S
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
0 l8 v/ z( x! G2 e" X/ ?                    D2:1=在$7000-$71FF有一个512字节的trainer
, g1 O5 ~1 o# e9 t: D: p' e* X' O! Z                    D3:1=4屏幕VRAM布局
  o/ K! l9 E' B, \                    D4-D7:ROM Mapper的低4位
# ^0 P6 a2 a: _/ s: v% Y7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) 9 x/ T1 H2 X4 N% [# j7 a! z
                    D4-D7:ROM Mapper的高4位
, z  Y2 r0 p6 w' L2 n. B& \8 `# Y8-F         8         保留,必须是0
; T" H  S  ?& V. @- v. A7 K8 n8 T16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前 2 Z4 i1 L1 k( m( X7 N: `' }
-EOF         8KxN         VROM段, 升序排列 ' j* j. L6 Q4 m& G" j9 m
! ~4 X; T, [7 ~! v, F# M1 I
然后知道这个ROM有0x08个PROM和0x10个VROM
* p! t4 d! @8 p. V( \7 E接下来扩展PROM位0x10个:' ]. h. U& j7 z6 N+ h9 }8 R
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):/ i% S, ]0 S; U2 s" ?8 C
3 `! Q( B( X7 h9 Y. K
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
  x* _7 ]/ `9 P6 |4 E$ L( L最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。0 J" j3 t9 ^, f% F, H9 ~
于是可以得到双截龙2的是:) Y- v1 t  S, f9 E1 L$ ?( `
(8-1)x 0x4000 + 0x10 = 1C010。& S: m( A6 \3 A6 d0 x
然后跳转到1C010:4 C  A  }! `9 G) C' n5 r# M; {

- R; v, I; N5 w/ `3 H5 ]8 x, |7 \2 U- ~9 H: B, U
9 o2 l' ]  a% u% p: J: I. s. Y$ f
然后插入0x20000字节的FF:
4 M* C" _; m* x' t' f+ x# G$ A
0 l4 R9 t, ^( _5 f% M9 @. i- O* ?0 k& X) C3 [+ R, t9 ]. h( T6 d  q" V
' e% T# S1 D* |0 F: Y$ F- [
然后保存:
/ I0 f1 u( O. ^( ~. h8 n* t/ \9 E) i7 ^% L& N' j1 X- J
  |) K, o) K' I7 a, a2 w
用FCEUX打开正常运行:
3 `. j9 N: Q  R7 \/ R
! i# Q9 @" y+ k5 Z: a查看文件信息:
& X+ R/ \+ X7 ]% P3 |
0 U& k, x  h- e# ]' [! P" H3 D" _# P" z1 O" x: |$ n% M( [5 j
接下来切页:9 T3 ]; ~3 M- `0 B; N8 t8 u1 {
先打开十六进制编辑器:
$ B* X% D( F7 ?" V3 k9 N" J+ A  A  q; s- ]
拉到滑块最后,看看重启中断; z6 E# M: j( H/ d6 c4 z' C
中断地址         中断         优先权
' p  D+ }0 A2 W+ y0 Z" C6 i% f$FFFA         NMI         中 0 Y0 \) k+ s" `
$FFFC         RESET         高
9 `# f+ y5 q. ?9 \# D8 h# \$FFFE         IRQ/BRK         低
* ?+ S+ \, c1 A% k. x5 h8 b' L$ i
' U7 y1 ]% X2 L; [RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。+ ]' ]8 @2 A6 V, q+ A) x( F+ M# S4 P
由此可知双截龙2的RESET中断$FF65。
) q2 B* H5 m, B" t& A接下来添加$FF65的执行断点:
" Q3 Q8 V4 Y$ r5 i% `; E1 j打开调试器:
9 ~( u& S7 a. k% m( A' Z. ~6 Q: R$ v; s, T: m: r* o
添加$FF65的执行断点:
1 ]& B# y* K7 o4 H* y3 Z( |# g+ i$ ~5 u4 s0 [+ w
' b+ w5 ?% Y8 R& X( }* T/ I, q+ `

" q. C8 O, @3 w单击确定:
2 T" k7 X: u! n3 w: q* _( |" w& h/ W- p. a

, P4 t! j2 a5 A; J" i+ K然后重启ROM:
/ M6 C. c0 p7 e  ^# d
7 J- S! F: e$ E4 K调试器此时弹出来:
. D6 R1 z' V% e7 ?3 y$ m0 [; q2 i# t' k" |
然后打开Hxd,写一段mapper 4的切换bank程序:$ E& B' t+ {& m+ W
先看看mapper 4的说明文档:
( R% w$ p. V9 k( n, XMapper 4
& f  G) _& o" J: Q- n
# \3 F, j  E1 C! k7 O$8000:  模式号
  n. w& l# X8 W7 O# Y9 H( u! P        位D0-D2:
/ K' @' X' m8 i" W( ~  y        0:选择2KB的VROM存储体映射到PPU的$0000: ~0 ?$ q8 r6 T: y6 b! }' V+ Z/ T
        1:选择2KB的VROM存储体映射到PPU的$0800( x% x7 S. a5 A* Z
        2:选择1KB的VROM存储体映射到PPU的$1000& y( n7 D$ t. A; e2 f+ y0 p. n* u
        3:选择1KB的VROM存储体映射到PPU的$1400
0 k% z# [  T0 Q& c4 D6 O0 f        4:选择1KB的VROM存储体映射到PPU的$1800) L) P% ^* ?# J5 M
        5:选择1KB的VROM存储体映射到PPU的$1C00
+ C7 V* K2 |  t) o% u- R        6:选择8KB的ROM存储体映射到$8000" w3 [0 V' Q# ~2 t' o( U
        7:选择8KB的ROM存储体映射到$A000
* G2 D) ~# F% \        位D6:, u8 B. A, \$ p: [& o3 f# H
        0:允许擦写$8000和$A000
7 Z) Z0 [  Y1 F: @        1:允许擦写$A000和$C0004 @2 H/ u! y8 p
        位D7:( ?- H4 c  U- I) v
        0:模式号D0-D2使用普通地址3 V" |  _' `8 `1 T8 g: s6 {
        1:模式号D0-D2地址异或$1000
9 m' e: w# ?" u2 V, v( m% f. D; j: y3 i! X  Y+ W% P4 t% A
$8001:  模式页面号
5 \. ]! _# G3 E# n% X2 P        写入一个数(00-07),切换存储体到对应地址
* i- y7 x4 Z8 \/ R. S7 K
. t- B& y+ g8 [8 h% W$ f$A000:  镜像选择
  ^: F" H5 \2 y# W7 @) |! L  F( \* m        0:垂直镜像; ]! ]. h1 i" G0 {* M. X/ r, X
        1:水平镜像+ h5 l4 c% f  R

# d) j$ q: y* t7 z) i& t7 ~" n  R$A001:  SaveRAM 切换$ O! M4 w+ }# A( [! A3 {
        0:禁用$6000-$7FFF
2 f6 R. ^( ?( M. u        1:启用$6000-$7FFF* ?! j6 t! e2 Z9 a7 s! g

/ l: b+ G0 Q' k- j  s4 l2 }. m% ?$ N$C000:  IRQ计数器/ Z& O( B; C* |) o" [: ~4 c! }
        IRQ计数器的值存储在此处: m. G& r" s! n- m9 }4 @; `- w  I0 u

# C6 V* S- y& R( Z$ h  t7 n- e: ?$C001:  IRQ暂存器  o* W# K4 T5 l# \7 S; M
        IRQ暂存器的值存储在此处3 H% U/ {8 F' D5 \+ X) u
; a( ^% K& }8 r! ?3 X; Z
$E000:  IRQ控制计数器02 d" p7 Q% v0 I1 i! D' N
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ( @! {" E; z, ~2 n% C8 h. g
1 L1 S6 y4 u/ D  [
$E001:  IRQ控制计数器11 r2 S/ H/ e/ q; Z! S1 h
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
  _* [, o3 L- j; N: U; f# J
" _. G8 y* b( F, H: S那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:! l! U' Y! S& B4 g! X0 |3 o
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 682 X. Z# i- g7 D' j
PHA      累加器A入栈
! V) t' |  s) r! Z! {- GLDA #$06    设置切bank地址为$8000-$9FFF
; e* H# J6 {* S' M. sSTA $80003 B8 N0 u7 ~. c1 F
LDA #$0E    将第0x0E号bank切到$8000-$9FFF% n1 c6 q& M6 y
STA $8001
: t% B  x' f! b( i' AJSR $8000    跳转到子程序$8000
  p) R& h6 z/ i3 \+ V$ H" ^PLA     累加器A出栈2 ]" T# v1 S8 m  s

& I; f+ W5 m, B2 G. N为何切换的bank号是0x0E呢?
& i* ]1 T& K: c因为在扩容ROM后,文件PROM结构为:
3 N! f& J. D) ^  N1 l2 ^, n% r4 A原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
1 V0 W3 B* [3 j& C+ w2 V+ B( W" e4 l2 |. }
Mapper 4切bank一次是切8KB,那么文件结构就是:
3 W" v2 ^, H/ p0 Q  Q原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)3 x* [9 E. Z8 Z8 x
因此选择扩容的第一个空白bank就是0x0E号bank。3 c% z# I+ X  L5 r# B- g8 v
! a2 T, @4 ~: Q

& y* F9 D: W- O5 t/ T- L3 e1 M( ]然后去调试器找RESET中断中可以放下切页程序的地方:, v0 o0 Z" \& n; ^
首先长度要小于等于自己写的切页程序。
' {0 Q& ]0 z9 F1 Z& E可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
* H, p# q3 y/ ?6 o! J" @# a
3 x# k4 Q- E1 ]然后跳转到$FF7C:
' Z7 b6 ^. o7 S$ @# z' M
# l, ~6 z2 H6 l/ n, n/ A/ I4 ]8 [' I' q

; B5 I* ]% o5 o: N$ A+ S( v0 k
4 T- g5 Z0 d- _0 R- k3 N4 O复制下可以被替换用于写切页的程序:
/ H0 X3 r5 [- i% L4 r! r8 Z7 Y/ P先选中,再复制:
: V5 E, Y7 ^7 W* \1 h, u9 K6 [# U& ^, v9 e& }
1 A5 [& _, q6 h$ \6 `
在Hxd中新建一个文件,把复制的数据粘贴上去:
! Y4 x2 \8 @# Y$ J) F  Z2 J8 g7 E5 Y, _

8 K+ @6 c  t: x$ y) E  ?: @, r
. M/ j' ]% |5 S4 `" a  X
2 R4 ~) J; ?, D) r$ t  [6 `然后回到十六进制模拟器:( s0 t% \) Y) {6 W  C6 i  d2 b0 F4 _7 `
转到$FF7C对应的ROM地址:
  q6 [; x) N4 D
1 V+ L3 R6 _0 g% N
  n. }: `% H$ a  @0 W$ g1 t( F然后复制Hxd的切页程序,粘贴到这里:
+ d: g/ v( P: ?4 K; G+ T/ y7 _
+ T7 U9 `7 }  R- A4 k" A+ o  Z4 z; j" V; u* L
3 l/ |5 D9 ]0 q, _, z- u
没有覆盖的用EA覆盖:4 k2 |6 q/ K& C7 U: F
1 W+ @8 U# W4 k3 i4 r
打开调试器可以看到变化:. H$ y$ i* W' q

" {% ?% F1 Q5 a; O9 h然后添加$FF7C执行断点:7 S& [. R; t8 {8 s6 O9 M. V! r8 t

; P4 n! z- \$ |" W
& v) o; F4 q# X- S! \' w% s/ k6 Y* x+ o" z9 H; V$ {
单击运行:# |8 Y6 W& m: u# `8 Z) z. q7 R
然后程序在$FF7C这里停下来了。; Z. r4 Y: u* F" S% Y+ R

4 s: y) Q( \( U3 ]! W然后单击单步进入慢慢跟踪,直到跳转到$8000:
( e0 f7 T7 P- K2 s  w9 x" s$ Z2 G" |" f2 m. E/ v% y% ^
然后打开6502_Simulator:
) |8 a: Z5 F& j  a) G8 \* S9 Y
" l4 a1 I$ u% B/ r9 V再打开我写的数据搬移程序:
8 K# E( X! {2 P5 R/ C6 {
6 C  s7 r: `7 m4 x: k$ O4 A4 G5 u% d* K4 I8 {
然后修改对应的数据:
7 R9 G0 o; M" H9 E. k% y7 e# Q5 v程序开始地址:修改比如$8100就修改为 .ORG $81001 J( w9 i5 e9 M& @) E, e. n
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70/ n7 L. m6 \0 D( c( E9 T4 S- u
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82. H* T* J& w6 e' h1 R! ]7 P6 [. K, |& R
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
# }( o  z* X/ d/ X6 R5 \1 b% ?) g4 X也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。) X$ C1 b# l2 ~  R( K" C6 F
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。  W0 ]- J5 C2 `4 b! n, N; P
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
; ^5 w& f# W) c+ S4 H4 G! u后面的不用管。9 T8 T' }6 T) e: e
8 Z: q  z! v/ Q+ l- e
设置完数据后单击编译:& z! ]0 @/ R' V$ d( m: `  l( e4 s

; J6 [4 \+ ~# U, z9 S; S然后保存编译文件:5 X, k- v( N5 ?
; }1 K  `  s$ D% w
选择二进制方式保存:' |1 |5 _1 O9 s0 J2 z% N2 ?( u

8 z; f  T0 S. U5 R7 {( k/ s4 d) v9 M: t0 Z, a8 W4 I
用Hxd打开保存的二进制文件:$ Q- {' e3 D3 S( [  a' P$ S5 A
: L" E2 k9 M" \' F& h/ Z

& w  {1 f9 g/ [0 Z跳转到设置的程序开始$8100:5 j9 W* x! H( u# u( y( B( a
9 H$ H: y8 \) t3 x
8 [& ^( ^& F9 C1 X

) w* \5 {& b! G0 e* I$ P回到FCEUX,转到NES内存的$8100对应的ROM地址:& a2 ^3 l  k0 ^  h7 i
: ~9 a0 }$ u) K( M8 i8 M
, h- e/ a5 [# c

/ W0 k5 H" @8 x4 W: j/ a
3 _  R' P7 Z, J
! z1 o0 k: [* [3 s  d然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:! k& a  [0 a+ |) R

) q; P) S# S( t! }  O7 u
' @6 v! e% m9 ?7 ^) Q; U& O" l5 n, h( _2 O: {+ r
然后转到NES内存的$8000对应的ROM地址:
1 t3 [* J) B' {9 g6 H
+ E" K: M; F9 V3 D' N. E- c7 H4 C% E$ G5 y- e3 L% M0 h
# c3 W" [+ X+ A" A
. Y2 s8 q7 r0 Q9 q  p# x# V1 L) ~
写上如下程序:. ?6 _4 @9 @7 P( |5 M  l8 u
A9 80 8D 01 A0 20 00 81  
! N8 S$ R' {: c# ?! |& OLDA #$801 l, L1 `; m# a- t* j/ Q3 W
STA $A001 可写方式开启SRAM
; f$ j5 c" j7 p/ KJSR $8100 跳转到子程序$8100
' x1 ?: g& c: F/ m3 C' |
: b4 V* s* [; g% |+ Q9 Q然后把Hxd里被覆盖的程序复制过来粘贴在后面:
9 n$ x. H! ^5 w1 `* q, {& m  K5 {" u8 |6 {9 p6 c! \

, W! x* b  W* A3 o3 `" s$ @! n! ^
( L0 F+ e! }( {0 K' t末尾补上一个0x60:  K0 E9 r+ q6 v& A! f5 E6 x
RTS 子程序返回
3 D. ^; ~8 a9 \2 c: `0 L* D" [: |% K# t5 m6 J6 F
然后单击运行,ROM音乐响起,正常运行:3 }5 x1 A7 H: t, Y% r6 X
" ^0 K) u0 Q6 A# G

3 a) s% I" b) p4 H) R然后转到NES地址$7000:% F9 R0 a* d% c" x& L/ S

6 D, J3 _& S/ v9 w# n: C: ^4 n6 t/ P- {" G9 I/ c. {

  q; q9 ^: s/ t& w
9 ~2 _0 _# Q- W1 Z9 w
9 P$ Y& l. _" ]0 E+ z+ ~' s可以看到,$7000-7FFF都被复制了一片数据。& _" A( i4 a& L7 d1 D
测试没有问题,然后保存文件:( q2 n8 P4 R% C2 w7 v" o4 \) y& G
3 L/ D  Z% e/ o- ^" a* U2 K3 Y% `
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。8 T+ {  k( j" S+ ]6 Y
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
# [! R* X' k  U* K  v
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

×

签到天数: 2060 天

[LV.Master]伴坛终老

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

使用道具 举报

签到天数: 1184 天

[LV.10]以坛为家III

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

使用道具 举报

签到天数: 1555 天

[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, 2026-4-26 19:17

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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