下文摘录自于春的《电脑游戏机硬件与编程特技》p.267-277,侵删。436快充网络
5.4.4 优化查表法436快充网络
《魂斗罗》游戏已为广大朋友所熟悉、所喜爱。该游戏的编程中有许多优化的方法可供我们学习和借鉴。兹特列两例供参考。436快充网络
①武器选项画面的绘制436快充网络
压缩版的《魂斗罗》游戏开始有一个武器选项画面(见图5-9)。在该画面,游戏者可通过选择键(SELECT)在四种武器中任选其一。该画面的绘制程序见 No.5-25。436快充网络
436快充网络
No.5-25《魂斗罗》武器选项画面的绘制程序436快充网络
F80D A94E LDA #$4E F80F 8500 STA $00 F811 A9F8 LDA #$F8 F813 8501 STA $01 ;置绘图数据区首址于 $00、$01 单元 F815 A000 LDY #$00 F817 B100 LDA ($00),Y F819 8D0620 STA $2006 ;置 PPU 地址高位 F81C C8 INY F81D B100 LDA ($00),Y F81F 8D0620 STA $2006 ;置 PPU 地址低位 F822 C8 INY F823 B100 LDA ($00),Y F825 AA TAX F826 3011 BMI $F839 ;判断送数指南,若大于 H7F 则 F828 C8 INY ;转 $F839 F829 B100 LDA ($00),Y ;送数指南小于 H80 的送数处理, F82B 8D0720 STA $2007 ;将其后的 N 个数送入 PPU。 F82E CA DEX F82F D0F7 BNE $F828 F831 C8 INY F832 B100 LDA ($00),Y F834 C980 CMP #$80 F836 D0DF BNE $F817 ;不是 H80 则改变 PPU 地址,继续送数 F838 60 RTS ;如是 H80 则结束送数 F839 8A TXA F83A 297F AND #$7F F83C AA TAX ;以该数的 D6~D0 位为计数器,将其后 F83D C8 INY ;的一数连送 N 次。 F83E B100 LDA ($00),Y F840 8D0720 STA $2007 F843 CA DEX F844 D0FA BNE $F840 F846 F0E9 BEQ $F831
数据区如下:436快充网络
F84E 3F 00 PPU 配 F850 08 0F 20 20 20 0F 27 27 27 3F 10 04 0F 05 0F 17 色,送 $3F00~$3F07。 送 PPU $3F10~$3F13 F860 23 d0 88 50 20 E4 17 20 20 20 20 20 20 20 20 20 背景零页配色,$23D0~$23D7 F870 20 20 20 20 20 20 20 20 20 20 20 20 20 20 21 2A F880 0B 47 41 4D 45 00 53 45 4C 45 43 54 21 66 93 3F G A M E S E L E C T F890 21 AA 0B 41 40 00 43 4F 4E 54 52 41 00 31 21 EA A . C O N T R A 1 F8A0 0B 42 40 00 43 4F 4E 54 52 41 00 32 22 2A 0B 43 B . C O N T R A 2 C F8B0 40 00 43 4F 4E 54 52 41 00 33 22 6A 0B 44 40 00 . C O N T R A 3 D . F8C0 43 4F 4E 54 52 41 00 34 00 00 C O N T R A 4
436快充网络
图 5-9 的数据区仅仅使用了 124 个单元就完成了 PPU 配色、背景页配色和画面显示,虽然四行都是相同的内容(CONTRA),但数据区中已留出了相应的空间,即使送显其它内容,数据区也不会增加。436快充网络
程序 No.5-25 的结构比较简单,读者可自己完成程序的转换,不多赘述。436快充网络
②《魂斗罗》标题画面的绘制436快充网络
我们知道,《魂斗罗》的标题画面是由画写的“CONTRA”和两个主人翁的上身组成的。画面既简单又富有力的感召,是正义和美的有机结合与升华。《魂斗罗》仅凭一幅标题画面就勾起了人们的无限遐思与向往,这怎能不令人钦佩和惊叹呢!现在让我们看看这一标题画面是如何绘制的。436快充网络
标题画面的绘制程序在 $C9CF~$CA60,其数据区在第二体的 $9097~$9251,现抄录如下,见程序 No.5-26436快充网络
N0.5-26《魂斗罗》标题画面绘制程序436快充网络
C9CF A001 LDY #$01 ;该子程序调用前已向 $00、$01 单元 C9D1 B100 LDA ($00),Y ;置入数据区首址 $9097 C9D3 8D0620 STA $2006 ;置 PPU 地址高位 C9D6 88 DEY C9D7 B100 LDA ($00),Y C9D9 8D0620 STA $2006 ;置 PPU 地址低位 C9DC A902 LDA #$02 C9DE A604 LDX $04 C9E0 1001 BPL $C9E3 C9E2 0A ASL ;若 $04>H7F 则 A=4 C9E3 A200 LDX #$00 C9E5 2092C8 JSR $C892 ;调整取数地址。 C9E8 A000 LDY #$00 C9EA B100 LDA ($00),Y C9EC C9FF CMP #$FF ;若 A==HFF 则结束送数 C9EE F06B BEQ $CA5B C9F0 C97F CMP #$7F ;若 A==H7F 则改变 PPU 地址 C9F2 F05D BEQ $CA51 C9F4 A8 TAY C9F5 1026 BPL $CA1D C9F7 297F AND #$7F C9F9 8502 STA $02 ;若取数指南小于 H7F 则以 $02 为计数器, C9FB A001 LDY #$01 ;送后面的 N 个数。 C9FD B100 LDA ($00),Y C9FF A604 LDX $04 CA01 1003 BPL $CA06 CA03 2036CA JSR $CA36 ;若 $04>H7F 则延时送数 CA06 8D0720 STA $2007 CA09 C402 CPY $02 CA0B F003 BEQ $CA10 ;送到送数数量则转 $CA10 调整地址 CA0D C8 INY CA0E D0ED BNE $C9FD ;未达到送数数量则转 $C9FD 继续送数 CA10 A901 LDA #$01 CA12 18 CLC CA13 6502 ADC $02 CA15 A200 LDX #$00 CA17 2092C8 JSR $C892 ;调整取数地址 CA1A 4CE8C9 JMP $C9E8 CA1D A001 LDY #$01 ;当取数指南 >H7F 时,把后面的一个数据 CA1F 8502 STA $02 ;连送 N 次的处理。 CA21 B100 LDA ($00),Y CA23 A402 LDY $02 CA25 A604 LDX $04 CA27 1003 BPL $CA2C CA29 2036CA JSR $CA36 CA2C 8D0720 STA $2007 CA2F 88 DEY CA30 D0FA BNE $CA2C CA32 A902 LDA #$02 CA34 DODF BNE $CA15 ;一次送数结束后,调整取数地址 CA36 8503 STA $03 ;以下是延时子程序 CA38 0603 ASL $03 CA3A 6A ROR CA3B 0603 ASL $03 CA3D 6A ROR CA3E 0603 ASL $03 CA40 6A ROR CA41 0603 ASL $03 CA43 6A ROR CA44 0603 ASL $03 CA46 6A ROR CA47 0603 ASL $03 CA49 6A ROR CA4A 0603 ASL $03 CA4C 6A ROR CA4D 0603 ASL $03 CA4F 6A ROR CA50 60 RTS CA51 A901 LDA #$01 CA53 A200 LDX #$00 CA55 2092C8 JSR $C892 ;调整取数地址 CA58 4CCFC9 JMP $C9CF CA5B 60 RTS ;结束送数
调整取数地址子程序436快充网络
C892 18 CLC C893 7500 ADC $00,X C895 9500 STA $00,X C897 9002 BCC $C89B C899 F601 INC $01,X C89B 60 RTS
绘图数据
9097 00 20 4B 00 8B 00 00 00 00 PPU $2000 先送 H4B 个 00、又送 H0B 个 00 90A0 00 00 00 00 00 00 00 14 00 8C 00 00 00 00 00 00 送 H14 个 00,送 H0C 个 00(下同,不再注释) 90B0 00 00 00 00 00 00 14 00 82 00 00 3A 00 86 02 03 90C0 04 05 06 07 19 00 89 11 12 13 14 15 16 17 18 19 90D0 16 00 8A 20 21 22 23 24 25 26 27 28 29 16 00 83 90E0 60 61 62 04 00 82 08 09 17 00 96 70 71 72 00 00 90F0 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 0A 0B 0C 9100 0D 0A 00 97 80 81 82 00 00 73 74 75 76 77 78 79 9110 7A 7B 7C 7D 7E 7F 1A 1B 1C 1D 1E 09 00 98 90 91 9120 92 00 00 83 84 85 86 87 88 89 8A 00 8C 8D 8E 8F 9130 2A 2B 2C 2D 2E 2F 08 00 98 A0 A1 A2 A3 00 93 94 9140 95 96 97 98 99 9A 00 9C 9D 9E 9F 3A 3B 3C 3D 3E 9150 3F 09 00 8B B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB 17 9160 00 86 A4 A5 A6 A7 A8 A9 27 00 85 C4 00 D0 D1 D2 9170 1A 00 87 D3 D4 D5 E0 E1 E2 E3 0B 00 8B 50 4C 41 P L A 9180 59 00 53 45 4C 45 43 54 04 00 85 E4 E5 F0 F1 F2 Y S E L E C T 9190 1A 00 88 F3 F4 F5 C5 C6 C7 C8 C9 0C 00 88 31 00 91A0 50 4C 41 59 45 52 03 00 85 CA CB CC CD CE 03 00 P L A Y E R 91B0 82 EF D6 15 00 86 D9 DA DB DC DD DE 04 00 81 E6 91C0 0B 00 90 32 00 50 4C 41 59 45 52 53 00 E9 EA EB 2 P L A Y E R S 91D0 EC ED EE 01 00 81 F6 17 00 84 D7 D8 E7 E8 13 00 91EO 8D 00 00 00 00 00 00 00 00 00 00 00 00 00 0E 00 91F0 97 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 9200 00 00 00 00 00 00 00 00 10 00 8B 00 00 00 00 00 9210 00 00 00 00 00 00 0E 00 97 00 00 00 00 00 00 00 9220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (以下为配色数据) 9230 4D 00 83 A0 A0 20 06 00 82 58 52 03 50 83 00 00 9240 20 05 05 05 00 83 CC FF F3 05 00 83 FF FF CC 11 9250 00 FF
图5-10《魂斗罗》标题画面的图形代码坐标图436快充网络
注——数据区中,凡下面划双线的数据为取数指南【注:输入的时候省略掉了】。436快充网络
程序 No.5-26 使用了 442 个数据完成了标题画面的绘制。实际上单对标题画面而言,数据量还可以压缩。如在清屏以后绘图时,数据区中的 $9097~$90BC 和 $91E0~$922F 这 118 个置零(清屏)的数据可以删除【注:这 118 个 00 其实是厂商 LOGO 显示的数据,被盗版厂商直接置零不显示】,那么数据区将减少到 326 个单元。可见,程序 No.5-26 是较优化的绘图程序。436快充网络
标题画面的图形代码排列见图 5-10。436快充网络
【注:省略一段于春修改的程序代码】436快充网络
5.4.5 间接分段绘图法(《魂斗罗》游戏画面的基本绘制方法)436快充网络
《魂斗罗》中的游戏画面都是采取间接分段绘图法绘制的。这种绘图方法的特点是:整幅画面分散绘制,一般一次中断或隔几次中断绘制一列或一行,横向卷动的画面每次绘一列,纵向滚动的画面每次绘一行;以七页为间接绘图数据缓冲区,由当前中断把绘图数据送入七页,在下一次中断再把七页的数据送入 PPU。这种绘图方法可保证多幅画面连续绘制,产生连绵不绝的效果。如第一关开始时的游戏画面犹如幕布从左向右拉开,而第二关又如幕布由下向上提起。随着游戏的进程,画面一幕幕的位移,使人丝毫感觉不到画面的间断,整关游戏画面好像是一幅长卷图画,左卷右放,尽头方休。下面仅列出源绘图程序并加以简要说明,以供学习、参考。源程序见 No.5-28。436快充网络
程序 No.5-28 共分两段:第一段为 $CB60~$CBC5,完成把 PPU 地址、绘图数据从数据区读出,按照一定的格式送入七页;第二段为 $CC3F~$CC7E,完成把七页的绘图数据送入 PPU,绘制游戏画面。436快充网络
N0.5-28《魂斗罗》游戏画面绘制源程序436快充网络
从数据区取数送入七页子程序436快充网络
CB60 48 PHA ;保存调该程序时的 A 值, CB61 A902 LDA #$02 ;该值决定绘图的种类 CB63 8503 STA $03 CB65 A901 LDA #$01 CB67 20BDCB JSR $CBBD ;令 $700=1、$21=1 CB6A 68 PLA ;弹出绘图类型数据 CB6B 8502 STA $02 CB6D 0A ASL CB6E AA TAX ;以绘图类型数据为地址偏移量 CB6F BD62B2 LDA $B262,X CB72 8500 STA $00 CB74 BD63B2 LDA $B263,X CB77 8501 STA $01 ;读出绘图数据区首址存入 CB79 A621 LDX $21 ;$00、$01 单元 CB7B A000 LDY #$00 CB7D B100 LDA ($00),Y ;读第一个数据 CB7F C8 INY CB80 C9FF CMP #$FF CB82 F03F BEQ $CBC3 ;若 A==HFF 则结束读数 CB84 C9FE CMP #$FE CB86 F019 BEQ $CBA1 ;若 A==HFE 则令 $700+X==HFF、 CB88 C9FD CMP #$FD ;x+1→$21,结束取数 CB8A F019 BEQ $CBA5 ;若 A=HFD 则令 $700+X=HFF, CB8C 9D0007 STA $0700,X ;进行第二次送数,把数据送入七页 CB8F A502 LDA $02 CB91 100B BPL $CB9E CB93 A503 LDA $03 ;七页的数据格式为: CB95 D005 BNE $CB9C ;$700 表示送数方式; CB97 9D0007 STA $0700,X ;$701 表示一组送入 PPU 的数据个数 CB9A F002 BEQ $CB9E ;$702 表示送入 PPU 数据的组数 CB9C C603 DEC $03 ;$703 表示 PPU 地址高位 CB9E E8 INX ;$704 表示 PPU 地址低位 CB9F D0DC BNE $CB7D ;$705……为绘图数据 CBA1 A9FF LDA #$FF ;当一次送多组数据时,每一组数据 CBA3 D01A BNE $CBBF ;传送完毕后的下两个数据,即为下一组 CBA5 A9FF LDA #$FF ;绘图数据的 PPU 地址。 CBA7 20BFCB JSR $CBBF CBAA A902 LDA #$02 CBAC 8503 STA $03 CBAE A901 LDA #$01 CBB0 20BFCB JSR $CBBF CBB3 D0C8 BNE $CB7D CBB5 A9FF LDA #$FF CBB7 D004 BNE $CBBD CBB9 A900 LDA #$00 CBBB F000 BEQ $CBBD CBBD A621 LDX $21 CBBF 9D0007 STA $0700,X CBC2 E8 INX CBC3 8621 STX $21 CBC5 60 RTS
取七页数据送入 PPU 子程序436快充网络
CC3F A200 LDX #$00 CC41 A5FF LDA $FF CC43 2918 AND #$18 CC45 8502 STA $02 CC47 BC0007 LDY $0700,X CC4A F0D0 BEQ $CC1C ;$700==0 不送数 CC4C A502 LDA $02 CC4E 19C5CB ORA $CBC5,Y CC51 8D0020 STA $2000 ;设置送数方式 CC54 E8 INX CC55 BD0007 LDA $0700,X CC58 8500 STA $00 ;以 $00 为一组送数的个数计数器 CC5A E8 INX CC5B BD0007 LDA $0700,X CC5E 8501 STA $01 ;以 $01 为本次送数的组数计数器 CC60 A400 LDY $00 CC62 E8 INX CC63 BD0007 LDA $0700,X CC66 8D0620 STA $2006 ;置 PPU 地址高位 CC69 E8 INX CC6A BD0007 LDA $0700,X CC6D 8D0620 STA $2006 ;置 PPU 地址低位 CC70 E8 INX CC71 BD0007 LDA $0700,X CC74 8D0720 STA $2007 ;取数送入 PPU CC77 88 DEY CC78 D0F6 BNE $CC70 ;若本组未送完则转 $CC70 继续 CC7A C601 DEC $01 ;若本组已送完则令组数计数器减一 CC7C D0E2 BNE $CC60 ;若还有数据组则转 $CC60 继续送数 CC7E 60 RTS ;若数据组计数器为零则结束送数
程序 No.5-28 通俗易懂,留给读者练习编写用于电脑游戏机的实用程序。