EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 ( G% P% }& }+ n# b& ~2 N7 `2 I

! E3 J1 E3 s1 t' W' ?[FC][SRAM扩容教程(Mapper 4为例)]7 K: ^, A7 J! H$ z/ |8 H) `

: I, J- M0 V' B# z+ o+ m( }4 B时间:2017.4.28' i+ c( X6 t% r: b" s  l7 j
作者:FlameCyclone
& y& r( S; A! X! Y工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator" H+ F& Y5 ~0 b# I+ w1 c5 |' b. U
ROM:双截龙2(J).nes
( O! V$ R0 i) U* B/ E1 V: u7 D8 B- s适用:没有使用SRAM的ROM
6 ?' ]& P( k5 m3 b' }( c; V# m: m$ h' n+ r' [5 ^, e
首先用Hxd打开ROM:
/ l% _6 ~5 B7 p: `& Y
) H, H0 w! e, `4 O. K! J' Q然后扩容:
( \% r4 b/ n( O" h0 z) k' g0 }4 {" x0 F7 g0 \5 E4 N; n

# y( S7 A# f0 N  Y( Q
( E+ A* m, [- O" j* h2 Z+ V! y' i% i
, m/ y9 b+ B& u. t9 ^5 @' h, B& F6 s8 M1 V$ k1 n

1 B: r6 d! V1 Q
; ]1 v: |4 P8 M' O6 G6 a$ C( Y+ l9 V( B$ s$ e

+ g# k3 B) s; W/ S# r. F( s先看看任天堂产品系统文件对NES文件的说明:
! L# {$ ?. M8 v( S; w# ~8 sNES文件格式
* J  z, Z" E; H.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 % H% i8 J$ [; P+ m4 S) c3 F! ~
偏移         字节数         内容 7 X- B+ i) m6 K2 B+ v
0-3         4         字符串“NES^Z”用来识别.NES文件 ) g  H# @. S1 E* o9 @9 {) ~
4         1         16kB ROM的数目 $ B2 q& L& D6 f0 k' h3 D
5         1         8kB VROM的数目
" O5 ~2 [9 z) W( m6         1         D0:1=垂直镜像,0=水平镜像
4 m* J8 t8 A6 Z' o5 x: |0 V                    D1:1=有电池记忆,SRAM地址$6000-$7FFF 0 z8 ?3 U  v8 R% [, \( A6 J: Y
                    D2:1=在$7000-$71FF有一个512字节的trainer # @7 U$ n; \: i$ t$ z0 Y
                    D3:1=4屏幕VRAM布局
- r5 d0 Y' M* A. a2 W- u) y                    D4-D7:ROM Mapper的低4位 $ I2 k' I. K4 i9 l  F
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) 7 b2 g& y. \. l; ~* C8 h! \# O; b
                    D4-D7:ROM Mapper的高4位
; ~7 c! _8 A" p8-F         8         保留,必须是0
  O6 ^6 `0 ]% k% W( x$ p16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前 0 G+ ~/ L; `" a+ }0 q
-EOF         8KxN         VROM段, 升序排列 5 n3 J/ g; B9 w
# Z6 Z9 l$ A% P$ N  }0 _( J# F
然后知道这个ROM有0x08个PROM和0x10个VROM
0 m3 y: c, J) A9 N  g* k; u接下来扩展PROM位0x10个:8 R! s& |& I. v1 _  [" p4 d$ R
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
- X, D5 X& E6 m* \* e6 C- }- e/ z
4 P/ F" W# z  ?由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
: O5 i$ ^& `+ g: k& Y最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。: o, P3 n3 C9 e  b! _
于是可以得到双截龙2的是:
* M7 C3 ^8 `$ W) L8 b$ I(8-1)x 0x4000 + 0x10 = 1C010。
5 U% t' j' n9 ]+ |- F, ]1 z# f然后跳转到1C010:) W2 h2 i8 g* ^1 r, M! C

7 r3 h5 g% e, ^: G* x/ l6 {: t' G% E; O/ T& t; t- `

7 y. u- [8 S/ v然后插入0x20000字节的FF:( S2 l1 k- l: C0 A

3 z6 |- h9 t! i  E5 i, T! O4 U* L* ^/ o" V4 M- F0 {

% o2 |* Y" ~% Q, u1 [  C然后保存:
0 n. k0 ?+ i6 J# f* |: |
! |( X0 `! t+ C# i1 N8 \, S6 E: D: [  T* L; @- Z
用FCEUX打开正常运行:
2 n/ ]$ e( U4 H7 F( |# O9 K2 y! y' r( E
查看文件信息:" w; w' G2 W6 i
" P, x' |8 p+ B8 X
4 R% B$ `2 u3 x; \4 c$ I
接下来切页:
: p- b. H- q4 i: j先打开十六进制编辑器:
" o/ G, Y/ j/ k+ [! A% u" K$ V
/ r* ]; {) ^% p9 T. R8 V& `) s6 c拉到滑块最后,看看重启中断
( Y" j& I1 P4 [中断地址         中断         优先权 5 D0 v' H! E& E& i5 B1 R( G
$FFFA         NMI         中 * k8 N5 A6 s; a+ g( Y& s
$FFFC         RESET         高 / g& F4 q% I( D# j, A5 w
$FFFE         IRQ/BRK         低 8 t' L. k7 R8 b5 G( o
+ s  i" v3 f5 O3 |$ \7 V( G
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。; R2 U7 m6 B3 D  |* t% g
由此可知双截龙2的RESET中断$FF65。1 e3 V+ \5 {- p; e: G) I
接下来添加$FF65的执行断点:
/ w5 g+ k9 l4 |6 ~) M- U7 @* Z打开调试器:* F8 L# f% J. H' J8 g4 ]

! F1 B; @7 Y! `) l. b5 v4 n添加$FF65的执行断点:
$ f/ V& C6 v6 l
% ~0 b! N/ j6 v/ j9 }) _4 Y5 M' i& B, j- J% f
: B" A. q/ m9 P  B. r& a
单击确定:8 ~' O3 D9 O8 {6 y) F

( V+ K* V1 j2 I6 O# h/ U* l! K* F  z' J% x+ l3 o4 M4 h
然后重启ROM:8 u/ g' j  `, C( J. C0 W  ^

- t" g2 y2 Z1 y! K调试器此时弹出来:+ p9 J) Z6 i5 F5 @
3 Q4 w8 `2 {# ^3 C" I
然后打开Hxd,写一段mapper 4的切换bank程序:0 U! ~  C" F! d" w8 Z8 P1 |: _# C
先看看mapper 4的说明文档:; O4 f0 U# }8 [% E/ v( C0 b
Mapper 49 Q/ O9 D  m' k! o. Z0 B% g

1 v: F1 Z2 [' J7 w$8000:  模式号
/ ~( v$ |4 N+ m& V, _        位D0-D2:( ]9 h: I9 V, P" n  q
        0:选择2KB的VROM存储体映射到PPU的$0000
+ t3 U" i* X! ]  W" }+ S        1:选择2KB的VROM存储体映射到PPU的$0800; `4 {* E, S1 `2 J: ]
        2:选择1KB的VROM存储体映射到PPU的$1000: f# N) D3 F% n2 L/ C  P
        3:选择1KB的VROM存储体映射到PPU的$1400
* _8 x, A3 j6 a6 {6 O  J/ w6 d        4:选择1KB的VROM存储体映射到PPU的$1800
" p3 j( L9 H- j1 G, O4 Q        5:选择1KB的VROM存储体映射到PPU的$1C00  U' n' H! S8 Y" c5 I
        6:选择8KB的ROM存储体映射到$8000) [$ I5 A4 e) E  N6 F: c' i; K
        7:选择8KB的ROM存储体映射到$A000- y0 _9 D! W6 a& {# k
        位D6:) e: p; J: Q# j# {% m- H# X+ i
        0:允许擦写$8000和$A000
9 M6 }0 L) ^% y" R5 R        1:允许擦写$A000和$C000
: a3 @7 j9 T( U! |2 M        位D7:
3 k& r6 s  P0 j/ B9 u0 Q' i  q        0:模式号D0-D2使用普通地址" e' t( P% i$ |9 K. j( k4 M. p, o
        1:模式号D0-D2地址异或$1000+ M3 T0 O- i  A3 C2 j

/ h6 b% c- M% k- ?$8001:  模式页面号! l4 ^4 G! W  O
        写入一个数(00-07),切换存储体到对应地址8 h  l5 i( F5 y1 a! k( R- C# b: e
  w+ ?% p$ O  p9 C
$A000:  镜像选择
- ?2 V! \1 X6 k7 q+ D        0:垂直镜像
0 G; r# U1 L- Q1 p        1:水平镜像
# ^! B$ t9 d" V1 D; M- g
1 n1 a0 t2 ?1 h( R( g( E, y( k1 m2 p$A001:  SaveRAM 切换/ T+ M, k1 d4 {6 N  y  }
        0:禁用$6000-$7FFF
# H, x# i! \6 D: I6 y# G        1:启用$6000-$7FFF
; g7 l1 l8 b6 U0 P+ T: _8 {8 t2 \! E& p8 o% h- h# b' q! n
$C000:  IRQ计数器: q/ K6 [4 o6 V7 C
        IRQ计数器的值存储在此处, D1 H! x9 Z$ O$ j

2 R3 ?0 N( @- E2 B5 K, v$C001:  IRQ暂存器5 B8 z. x8 I$ S) {& t
        IRQ暂存器的值存储在此处, t5 F* ?+ }! S1 Y( N* }3 m4 Q2 k

" d9 Y4 R: O- `# O% ~/ x. b$E000:  IRQ控制计数器06 G0 E% v* o  l: Q5 n5 h* @) A" P
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ+ b  N7 P' X% e' {
: J0 f& [+ N2 }# m; A" G0 z7 Y! n
$E001:  IRQ控制计数器1
$ K1 e) ^/ h: t3 r" t- j9 A        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
" A2 g+ F2 m5 ?. Y  r; r
. F8 ?% z9 y1 Q& v4 Y* d  M7 R那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:6 B! ~5 u8 L; A# i0 d' V9 p
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 680 L' k' Q' _5 X( W
PHA      累加器A入栈7 z, L- [' k% |; k8 x
LDA #$06    设置切bank地址为$8000-$9FFF
! W, j$ q. ]0 fSTA $8000
- q' z$ a+ i/ Z+ _# [LDA #$0E    将第0x0E号bank切到$8000-$9FFF/ I% r& V1 A# c
STA $8001  W/ m0 Z  |5 e
JSR $8000    跳转到子程序$8000+ I  w6 `" z9 R* a6 g  y
PLA     累加器A出栈
# S. H! |% b, o5 k9 @3 T
; f5 f2 H: h* H9 T为何切换的bank号是0x0E呢?3 p0 C* J* T) k5 }$ a
因为在扩容ROM后,文件PROM结构为:0 q+ t3 _  H) a$ O5 S9 l
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
& L6 s8 L( P$ w: j# K  w& p, ^4 H. g4 t7 N/ x) s" B
Mapper 4切bank一次是切8KB,那么文件结构就是:
( Z! V4 H% U: L8 w原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)/ U- w5 b2 D0 R! c. e7 U1 i& ^& j
因此选择扩容的第一个空白bank就是0x0E号bank。
. Y0 o% r6 j# e- g& P- d5 }. r
$ G1 o# _' R; r+ q0 y1 X
* @1 a' @, \$ ^1 I# z! U然后去调试器找RESET中断中可以放下切页程序的地方:1 }5 {( a) a. `: n
首先长度要小于等于自己写的切页程序。
3 ]7 m- K+ Z, v' w% m# D+ h* r可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方), V9 G1 p! `  t, }$ }

9 L1 f. `8 _0 x( ?2 r9 B然后跳转到$FF7C:( f: Q1 V- q) \! y- @, Z& h
. R* q9 _4 k; c; X) E# x

% t/ V$ {2 B# G6 S9 ~6 _3 z; O. K4 j8 B( ~1 N& K  E
& y, q* f; G5 C4 T& h2 p1 \
复制下可以被替换用于写切页的程序:% m9 R. n0 J. [- s* `! o! j
先选中,再复制:
# C5 m+ d& ?1 v0 S! e
9 a! Q6 ]# K* u
* ^" w% v0 Z' U4 Q' A! B" ^在Hxd中新建一个文件,把复制的数据粘贴上去:
& k$ K8 a+ O! `% ^' d
0 w: M6 |& \9 @( ]: {; C+ _( B$ m
  D; R  A/ {0 B/ q

1 }, C3 A: W! d" d+ R8 `5 Q然后回到十六进制模拟器:  F1 F+ J5 E+ X1 w& B
转到$FF7C对应的ROM地址:
$ w7 I/ _4 Y. o0 c. R
: u) G4 g7 G! v$ e. i4 O8 l* R. ]
5 F! j2 w( h, n然后复制Hxd的切页程序,粘贴到这里:+ V% J  H. v# \2 W- F
9 Z5 f) B: a2 }: B  s

5 I; u' m. E+ B5 D% W
7 @6 i! m9 J! h没有覆盖的用EA覆盖:; f" ~# d  c( ^# U. R
  p! `+ w6 l) O5 p0 }
打开调试器可以看到变化:
1 Q, q# }0 E' Q% s1 r, G2 y. P' P+ e3 e  F6 \6 n% W, c3 I% D
然后添加$FF7C执行断点:5 R; ^5 M+ ?& m1 e8 \
$ g0 h- N: i( x/ I: c2 `6 ^
5 I' }7 \6 l; ]0 E
# m3 i8 g0 S$ M# F9 i, Y6 T
单击运行:
! C5 s4 V$ T2 m5 U5 C然后程序在$FF7C这里停下来了。0 E$ Y2 ^0 j) r0 K" S5 C
1 {% H$ f5 J( l+ H! W3 l" e; G
然后单击单步进入慢慢跟踪,直到跳转到$8000:
0 A2 a  {* s) |/ Y) @
4 L4 Z# e3 }' q% ]然后打开6502_Simulator:8 E& z# _8 D) t2 Z( B

& u; x9 G2 b. b3 F! r再打开我写的数据搬移程序:
! d' D: T7 A2 Q5 M9 _! X
: Z2 f3 @* c1 Z- p
( |1 T) K! j' b" ?: y; v2 E9 V) v$ R然后修改对应的数据:
. t. \, E. Y: E9 H; S: }5 @程序开始地址:修改比如$8100就修改为 .ORG $8100$ E1 I4 t. z+ f1 u4 w" |
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$706 ^7 ]% @* I1 J  f6 f% t
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$820 Q. l0 |4 m3 B; Z2 @8 [2 J
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
$ [# Z. I& S) [# C* n9 v7 @也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。1 L& @" D9 y( [5 x( f
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
8 [, z& `" e" v9 L$ `中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
$ H, _+ C8 @4 m  v1 p后面的不用管。! f/ X+ n5 \3 h: W
8 p) _! u4 b$ C- b
设置完数据后单击编译:
! ]! @- |8 [( E) o- W9 ?, c7 Z3 ~. i7 H+ [$ X' \  I
然后保存编译文件:
2 K7 }' s# ?2 Q( g& u$ J& L. V( U
选择二进制方式保存:* j' [: ^$ j& L3 q  N4 Y3 x

) `  ^5 t) v+ z- U4 ~; }4 S$ D. h, E2 g
用Hxd打开保存的二进制文件:+ n' r! P1 ?9 U) p7 J
4 V% `; L9 E& `# Y. [
( n, u, X8 e6 D/ S2 P/ r8 w
跳转到设置的程序开始$8100:
, x4 a, p. t8 c4 g9 f
0 w" ?; r' K" m5 Z3 K1 j2 h
( c$ L9 t5 m0 s% m: M
) r4 l8 X4 L+ e8 u  H5 \* v回到FCEUX,转到NES内存的$8100对应的ROM地址:
. f: X' S( P; t
) C/ h' a) k2 V' |9 z$ W1 r- P
- s" w1 d- ?! @5 k+ M7 ^# I+ b6 `3 \

: ^7 p$ I& Q& X0 a
9 B7 Y8 Y8 _7 v: f然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
" _. E9 Y4 x' y& A% U% W9 p  v9 u3 T2 R6 q2 E
9 p; W$ P. @# G# B+ Q* `
0 B6 k0 O1 U9 A% T
然后转到NES内存的$8000对应的ROM地址:+ ~0 D; a  [8 l  w# Z3 L+ e

* i' F" \3 ?& d4 @) w- d
' q% M! F- u# q  g! P/ X
, D( z6 o3 _/ t: ~. G
4 c/ F5 v5 w: F( D写上如下程序:9 Z5 e% H# o* v. p1 ~
A9 80 8D 01 A0 20 00 81  # G& k! C0 X/ y  e8 J
LDA #$80' {# E  I& q% R3 w" t
STA $A001 可写方式开启SRAM' @. O( p$ X' m  r7 D4 A
JSR $8100 跳转到子程序$8100
, S0 e7 x' k6 r$ h5 g2 @
, E6 t2 p' |4 Q4 k( |然后把Hxd里被覆盖的程序复制过来粘贴在后面:
9 E, W+ L( F1 M8 c5 J7 W& h8 c8 U6 ~8 o3 C
" Y8 _  h/ E: ?' V& X

. [9 T0 V! N" U6 z$ ?  x; [末尾补上一个0x60:( \' s4 ?+ L7 s2 @' x. Z
RTS 子程序返回  B, }5 J0 \7 f& f6 q5 N$ t

$ n" s/ _4 s# Y! X, t然后单击运行,ROM音乐响起,正常运行:
( `5 ~* S5 ~7 `( R- U; L5 C* k, h$ y/ T0 U# R( a
, p4 X/ z" L) }% _( @9 G
然后转到NES地址$7000:
; h7 n1 I: k9 v6 l% C$ P) }  u! z
* |  w* d2 T" s+ X1 q( {" e4 G, |! l) T

$ V( i! m' B, [) R" V' D& Z0 s; m' O3 R  C
/ O( |: `3 P( Z+ L
可以看到,$7000-7FFF都被复制了一片数据。9 m0 I. Z# p- [# G- i
测试没有问题,然后保存文件:7 {2 U' b7 s3 \5 Y1 p6 a
& K. J0 s1 l0 v# E: y
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。" c' f3 k( z5 b+ T& k& b8 ]
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。' {2 ~' s" B; Y# [( R. c
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1183 天

[LV.10]以坛为家III

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

签到天数: 1512 天

[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-2-24 18:19

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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