|
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
7 u& k* f6 y2 l v( T
, m7 @& a+ k/ K" S: g9 p[FC][SRAM扩容教程(Mapper 4为例)]
) D9 k6 w# ~& u# \7 A
( F- u& n* B6 ^ d% L2 ~时间:2017.4.28
" r) G) K' j$ c2 i- Q- c作者:FlameCyclone
) ^) p4 h% h9 V9 t% `+ G工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
, S3 g9 \5 \- TROM:双截龙2(J).nes
1 u4 k" S% n9 ~适用:没有使用SRAM的ROM
0 a% ~3 J- c" q* J; A$ F: e6 w
& b% E+ G8 G) s( ]3 S* m+ e首先用Hxd打开ROM:1 C! r. E5 }. ?9 X0 _
7 o# k) B$ X; ]! I; X
然后扩容:
, M: j7 a) f. ^$ L! K2 E! I$ k
% u7 G& I' a+ ^7 Q2 Y r, I. f. Q/ \* p
]9 K' S/ Q7 _( S" P
/ X% A; ]+ v) z6 }+ ?6 ^+ n, P: B
2 M9 L# W& ^6 [' Q& ^
& |, i u3 ~2 @3 S% C
$ g) j9 q$ X) _" T$ x, L/ `" T) R) p8 P) w: S$ B: L
3 c; O8 P+ t$ R) n7 G先看看任天堂产品系统文件对NES文件的说明:
) ?3 K# X; l5 b" y- CNES文件格式6 Z( Z) {/ n/ G' H4 n% V b7 N
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
9 o0 \$ D9 k3 i偏移 字节数 内容
2 K+ G/ q8 _: ?, g$ j' @/ X7 c" Y2 J0-3 4 字符串“NES^Z”用来识别.NES文件 # _% N, `- o* U% w* R" ~
4 1 16kB ROM的数目 # e5 P9 R' ^7 m# o7 B5 B
5 1 8kB VROM的数目
5 x) G) j7 b0 {, m- a, n9 H6 1 D0:1=垂直镜像,0=水平镜像 " a6 Z8 Y% w* a- f7 c% q' r. U# T
D1:1=有电池记忆,SRAM地址$6000-$7FFF
5 ?0 t% _7 w g: N* `- P D D2:1=在$7000-$71FF有一个512字节的trainer 6 R7 V: U1 J; Z; @
D3:1=4屏幕VRAM布局
9 A4 l) G K8 `, L9 e9 e D4-D7:ROM Mapper的低4位 7 w$ @& A# L( l! Q% y
7 1 D0-D3:保留,必须是0(准备作为副Mapper号^_^)
( S, _; B9 P2 M, Y' V' } D4-D7:ROM Mapper的高4位 ) ?4 ~# \/ C* f% e
8-F 8 保留,必须是0 J' c# y3 z4 S& P, k
16- 16KxM ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
7 T) Y1 {! N! R, W-EOF 8KxN VROM段, 升序排列
8 u/ j3 U+ H% [; g# x, l8 `) j, A7 w. a6 }+ }4 s5 t/ q
然后知道这个ROM有0x08个PROM和0x10个VROM
; n+ e( Y0 G0 A* X& x2 Y8 M接下来扩展PROM位0x10个:+ n$ F; n( U& M$ u3 v/ `; \( v5 e
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):& A: ^0 m: p# q, l

1 o( M. p) M. ] y" P& ?由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
4 E4 t) y+ \5 p% F9 C最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
/ ?( e) g2 b1 k% n r于是可以得到双截龙2的是:- z: q9 {$ Y0 e2 F5 N2 x* M
(8-1)x 0x4000 + 0x10 = 1C010。$ S- b$ j$ N) C0 I8 p% r6 ~, A
然后跳转到1C010:3 w* t1 ~4 M5 [/ e7 Y
1 Q) Y9 V( l4 P; J
6 R: f; f& {9 w. m9 C' w. ~

' I# f" [# ?+ C" S7 W然后插入0x20000字节的FF:
5 u. ]4 F8 s& I, ~" x0 J " I6 N6 s* F) S7 X

! d2 G, I6 ~) j
% ` w& D$ V3 _5 s1 e0 P* w然后保存:' F2 x& t; }8 i- `" d5 K, o. G

. }! L3 @" R) @ c. S% |4 I2 l
4 R4 f7 b; J; R! t: G+ g用FCEUX打开正常运行:- Q! [9 l K1 n

$ u; h' v* |" ]7 L/ }2 W查看文件信息:2 S3 Q3 B5 W2 R: ^% u; G$ B
9 {& v8 h9 I* N5 i) Y
2 b* [2 s% F4 f& u* q, R7 p, e- k
接下来切页:
$ q6 y+ [, U8 _5 W$ Z' b先打开十六进制编辑器:
# e; Y7 F% f* `" w- X% A P7 O
- F8 y) h" G- D- i2 H3 m' m拉到滑块最后,看看重启中断
9 Z3 [0 e4 F# F: l, O$ _9 g* L中断地址 中断 优先权
& F$ |" z- j, P' U( R2 G$FFFA NMI 中 ; ]4 _7 U2 x* Z$ q G& z* m
$FFFC RESET 高 3 v: Z! r4 X% ~6 A
$FFFE IRQ/BRK 低
9 L# D6 f. c5 N) l2 K) y( F 8 c; p# |' q% J8 G% Z. f( p
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
6 M: t" F- R& k; t y由此可知双截龙2的RESET中断$FF65。# c2 C3 k2 i7 v8 x1 B
接下来添加$FF65的执行断点:
/ P5 N+ Q+ S9 d4 q7 M打开调试器:% v* K2 {$ f8 z E
1 c6 A5 E3 [& [) }
添加$FF65的执行断点:
; ? C+ Y0 ^& o( d" C" D 7 d2 y* l: M& \% s Q
; g0 m! S; j1 b. l3 @8 h2 e1 R
9 {; \4 y9 m; z; M单击确定:
- U' T$ u% J7 z# K5 G
7 Y6 A4 c2 Z/ m' D: y2 C% m! f: K$ o6 k/ d0 E( F% i
然后重启ROM:+ m* d; I! `! `& _. U" N) c
! G, U1 e8 h3 [: f/ N; R
调试器此时弹出来:9 H5 I% B/ q: V! b* j9 J6 T
3 f8 S# O- G5 k+ @8 A* ?0 @4 J7 e
然后打开Hxd,写一段mapper 4的切换bank程序:# [. t6 p" d1 @: _. h
先看看mapper 4的说明文档:3 y1 K$ w6 u2 n y( a1 }
Mapper 4
1 Q" e- q+ \6 c% Q; C, a
% n) g& |0 a% s4 q; R$8000: 模式号6 t0 k2 E# ^- @, Q* l9 @
位D0-D2: M' q" ] O2 `% L" `
0:选择2KB的VROM存储体映射到PPU的$0000
( |$ G) D! ?9 w( [% p# s1 X0 | n/ F 1:选择2KB的VROM存储体映射到PPU的$0800- Q/ E8 r9 A% B
2:选择1KB的VROM存储体映射到PPU的$10005 b9 I3 H+ A" B5 L; o3 I
3:选择1KB的VROM存储体映射到PPU的$1400, r1 [9 H6 @3 p
4:选择1KB的VROM存储体映射到PPU的$18000 X4 G8 F7 y6 P5 \! X5 x' @
5:选择1KB的VROM存储体映射到PPU的$1C00* i+ q; ^9 {/ q) s2 g0 j$ h
6:选择8KB的ROM存储体映射到$8000
% c& ~& b! M' N" k5 y% q. z6 E$ r 7:选择8KB的ROM存储体映射到$A000
t1 A: I% w( k9 V# E+ v4 @ 位D6:
7 w7 _, Q& L* u! _1 c8 J, G( O 0:允许擦写$8000和$A000/ I- n. |" ~( q6 H3 i* Y
1:允许擦写$A000和$C0002 D! A0 N# a( D- R: S6 ^7 N
位D7:8 B% J$ ]% a, P: x) `
0:模式号D0-D2使用普通地址6 n1 Y& K/ ~$ u% S: `* O' Q7 a6 i' P
1:模式号D0-D2地址异或$1000$ F; O! ]' L0 B5 I- `, o
% \4 A. e* W0 @# ^- j7 k$8001: 模式页面号
' S! h, J, b* b. m- | 写入一个数(00-07),切换存储体到对应地址* D6 i' w# H/ b6 ~; ^+ p
6 Z X$ M) n9 b k% o. w& r
$A000: 镜像选择
7 I' V0 V. P" t) b 0:垂直镜像/ P( O6 B' \! u: ?
1:水平镜像1 Q* \% U! Y8 c( y) E1 S$ j. e7 D
; g- y7 l# S6 J9 w w$A001: SaveRAM 切换/ x1 o( m5 L2 D' I
0:禁用$6000-$7FFF( w+ s/ V! \0 l% o7 a/ V# x# L
1:启用$6000-$7FFF( u, [9 h ]+ n/ }* M
4 q: r+ J0 R a; {3 T9 v) ~$C000: IRQ计数器8 _6 P: m& x1 J, W
IRQ计数器的值存储在此处8 q7 @$ A# Q* t% T8 o
; i6 P/ X6 c1 X9 u" F1 l; O$C001: IRQ暂存器
2 _2 u( p1 { k" x ^ IRQ暂存器的值存储在此处/ z( V. h5 i2 U2 D, B# _
0 Z- m3 g( x" I6 x; z0 I% l$E000: IRQ控制计数器0
6 S3 n* D* }$ J 向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ6 c- ?# a, @1 ^
4 Q, D# |9 ^# q) `
$E001: IRQ控制计数器10 c4 W" r6 N" B! n. q( A3 w: [3 F
向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
3 ^- I, u7 d! u& Z6 B
! R: S( q3 n3 e& ~) S' a那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:: Y* g% V1 X0 _' v6 {' W0 P
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68
/ f; M6 T f7 V# A2 n7 DPHA 累加器A入栈5 M' H: o& F* x
LDA #$06 设置切bank地址为$8000-$9FFF2 ^8 |0 Q9 C3 x2 z
STA $80009 s7 W" y) A# O; Q$ t; @+ K
LDA #$0E 将第0x0E号bank切到$8000-$9FFF
0 K3 C* ~) H2 W7 tSTA $8001
! z7 B8 D9 k9 u: aJSR $8000 跳转到子程序$80000 Y' h, E, q: E
PLA 累加器A出栈
3 }- q5 t$ d p- J- n9 Y
/ V2 S- X8 d' F1 i4 \( ^ G8 h# O为何切换的bank号是0x0E呢?. i4 p( ?7 P" N, b: Y3 z
因为在扩容ROM后,文件PROM结构为:
! \. _/ f5 n8 C7 b1 c6 f4 f: F" }: P( s原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
% Y4 v, i% r% G- c9 P l! Y
( I5 z! V. U+ b; M2 j4 ~Mapper 4切bank一次是切8KB,那么文件结构就是:3 J+ _% p/ l# l4 g7 }1 H1 s
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
4 z! C9 H6 _6 M% r1 v1 P因此选择扩容的第一个空白bank就是0x0E号bank。
+ ^5 o, R' r, \' k
4 h3 f4 q" I0 |3 w, }3 j. y9 h/ W, A( X2 b
然后去调试器找RESET中断中可以放下切页程序的地方:4 p7 ^1 e. C- C! R$ Z
首先长度要小于等于自己写的切页程序。# Z3 d) b, `0 n2 v7 I
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)+ B( c( O: L5 {( b0 b2 Z5 B% n* @
! _' J7 s0 u6 b6 d I
然后跳转到$FF7C:
" C# R" b% c8 A( @( w& {" G 6 h- C9 k+ E- i: P. b1 d9 r
5 N$ y- `! O! z1 v- Q* l! @& S
: |& O. o- r% G4 `2 h2 \
2 {% l" l4 r! N @4 a复制下可以被替换用于写切页的程序:
( U+ _9 D4 Z7 W! A5 W7 O先选中,再复制:
! r1 S, I* ?$ l
6 l _9 T- J0 g: P, ^ . S8 L) E8 s3 _ z
在Hxd中新建一个文件,把复制的数据粘贴上去:
/ ]5 O& |4 ?) ^' J
% a2 Q% h! ]# y) |- |
% T; I# V" I3 B
6 n& n/ R7 U: a( G' R: r4 l+ ]1 J# ?* p
然后回到十六进制模拟器:7 j. V w7 D/ }$ Z; {7 y
转到$FF7C对应的ROM地址:4 G+ v" e% R* Y2 d3 b

; d% t1 }* X1 @6 ~ G ' l B7 B/ W/ P/ l
然后复制Hxd的切页程序,粘贴到这里:
- `% h% P; a. Q8 a# P: y/ Q 8 M; c: ~0 C" Z) T

7 M$ T6 t. C6 y1 ]: K3 L
* D D+ b/ E5 k. H G没有覆盖的用EA覆盖:& o1 ?8 v X5 R% w; W" F: G$ L0 B! N$ G

9 q$ {" L- ?1 E2 H* g0 e% q2 [打开调试器可以看到变化:
/ C- v2 G, o7 n9 [9 R4 k% m+ G/ _ - k9 r i* l+ Q& j# V/ E
然后添加$FF7C执行断点:
. ]5 P: F1 q( t
6 `( z* N) [( v& l& ~ 5 v1 J# q# \ F

# E/ S+ x! [1 U' |8 P8 U: C( }单击运行:
) W& L) {; \7 F! h+ u# H- a然后程序在$FF7C这里停下来了。
" j" u6 f; F* J2 t/ A . r: l. A& H1 Z$ }9 ^9 Q
然后单击单步进入慢慢跟踪,直到跳转到$8000:1 M0 H. ^8 T" J4 z4 p2 x: ^ o

* w C/ H* M8 p9 E* F$ b2 p然后打开6502_Simulator:( \2 v1 f2 L- F0 m; W- T

& b5 Q$ m+ G5 I4 F( [再打开我写的数据搬移程序:/ g$ ]' |5 c' m- q

0 Q, B' n+ z9 v0 p: [ ` % c3 h( T, k6 i i' M' j l
然后修改对应的数据:8 o* F. M1 `# ~, e) @# f$ n
程序开始地址:修改比如$8100就修改为 .ORG $8100
4 C0 B0 ?6 @$ B4 w复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70: Y6 n& t+ [! a! { x% J
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
# ~9 l- F) u: x9 D9 ~1 R想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
) x; s3 z7 F& H+ ]也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。3 b: ~2 U- R* a, q1 ]
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
) N1 X5 L) k- |3 d中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。& d/ I; y- N, H7 P+ [* z- H- Q8 [
后面的不用管。
' Z- V$ Z4 m/ \: Z; s. C+ F1 U) K# j: O2 P8 S1 V3 `; c: \/ f5 H
设置完数据后单击编译:
. i/ u, Y- b1 U+ [ U0 a
" z4 s/ r8 |# w: X+ Q+ p然后保存编译文件:
+ ]& D; {( }1 z3 u3 R9 K. L* S
! f6 H4 k3 M: T6 a选择二进制方式保存:+ U% x6 R: F1 v8 f/ y1 ~7 {1 V

! o5 Z3 Y; m- ?$ t: W7 d$ S 6 b1 x6 H5 [7 i. F, ]! o
用Hxd打开保存的二进制文件:
8 j+ q4 y3 ~7 m
% \7 [+ \- P4 W' l, \5 M; u
1 D( P( @7 k- i2 K$ y/ E跳转到设置的程序开始$8100:/ |; H+ L( {" \" z
, `& L' J6 n* ?8 Y4 _# V+ T
+ F, V, a; s0 P" O( U

0 ^0 z+ O7 m, ?2 z回到FCEUX,转到NES内存的$8100对应的ROM地址:
5 S% V4 F0 z: l+ [. s- F 9 U: C0 ~" ^9 H' o7 O5 w5 z4 \0 D4 `
' ~ Y4 v3 e3 ~7 X% c& v6 S" |
$ v* ]! D$ E; w F- i4 d) B
6 d! k0 L" w, y" Y. Q/ J

" h; i4 N' x& u8 P7 v, |& d然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
, r. t0 ^& k/ [; S: F 0 U0 |1 Y8 O( ?
{ ] Q1 E' G2 |
) o/ v& M) F: |. C% {
然后转到NES内存的$8000对应的ROM地址:
1 i3 k' K- b0 X1 F$ J2 @( J 7 d6 E0 S- | J# K9 u7 @7 L
" f* _5 J, o* p$ L. X) z4 H
' }) P% i6 C3 O- i9 C5 v

, }6 d2 K+ w, ?: s1 @" B. Z写上如下程序:
! m1 n! F) R/ x' J9 l6 SA9 80 8D 01 A0 20 00 81 3 P- ^& [) z: r( W3 e% c+ c, n) B
LDA #$80
2 g" @9 z* q5 Q. s: h; S1 s7 l/ uSTA $A001 可写方式开启SRAM
9 s# u$ D4 M; HJSR $8100 跳转到子程序$8100
% w5 Y/ z) u7 R 1 o1 y2 t9 |! u' {+ n- {
然后把Hxd里被覆盖的程序复制过来粘贴在后面:' c" M4 G y. M
& w7 W0 ?! ^( g8 N/ f3 o/ S3 L

6 ~+ H" p1 X9 x9 G8 c4 O
/ |4 o7 O5 N* |! m末尾补上一个0x60:) K4 b/ t. T. n/ c4 T" \
RTS 子程序返回8 {6 O; @6 M; a# `9 {

. j9 G( g8 o* Y! g1 b. U然后单击运行,ROM音乐响起,正常运行:
1 E) L& e; I( c6 z! y- E1 Q; ^
' R0 K) ~! E# c! E1 B0 @ \ , \. @3 F M) \; X
然后转到NES地址$7000:
, {( u8 @# T; q, L4 W: B* g+ G
$ v$ o$ c; D8 \+ }" V5 s
0 S5 u" f* {4 I6 h* m 5 b1 z7 z' {3 ]/ G5 T! D8 l9 P

. J4 s+ }% k' K2 i8 k) ^
+ K* ?* P; D0 k* d可以看到,$7000-7FFF都被复制了一片数据。
4 @8 R4 Y3 T9 v& ?测试没有问题,然后保存文件:9 G7 q+ k5 Z9 Q( O$ x

2 c& ?& t! k# N/ T/ y) @以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
( u; m) M% H- u- p后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。' V) `) E q, d
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|