玖叶教程网

前端编程开发入门

nes编程系列翻译 ——ASM篇(主要手册-颜色着色精灵和第二个应用)

本周:现在你能制作和运行一个程序,定时放一些东西在屏幕上。

着色板

在把一些图像放到屏幕上之前,你首先需要设置颜色着色板。这里有两个着色板,每个16字节。一个着色板被当作背景使用,另外一个当作精灵使用。在着色板的字节对应到在NES中能显示的64个颜色之一。 $0D是一个坏的颜色,不应该被使用。这些颜色不是太精确,在模拟器和TV上将找到不同颜色。

着色板开始在PPU的地址$3F00和$3F10.去设置这个地址,PPU地址端口$2006将被使用。这个端口必须被写两次,一次为高字节,然后为低字节:

LDA $2002 ; 读PPU状态 去设置高/低门闩到高

LDA #$3F

STA $2006 ; 写$3F10高字节

LDA #$10

STA $2006 ; 写$3F10低字节

这些代码告诉PPU去设置它的地址到$3F10.现在对于$2007的PPU数据端口准备去接受数据。首先写你想要设置的地址($3F10),然后PPU将自动增加地址($3F11,$3F12,$3F13),在每次读或者写之后。你可以保持写数据,然后地址会自动增加。这里设置着色板的一开始的4个颜色。

LDA #$32 ; 作为亮蓝色代码

STA $2007 ; 写到PPU的$3F10

LDA #$14 ; 作为桃色代码

STA $2007 ; 写到PPU的$3F11

LDA #$2A ; 作为绿色代码

STA $2007 ; 写到PPU的$3F12

LDA #$16 ; 作为红色代码

STA $2007 ; 写到PPU的$3F13

你应该继续写来填充着色板剩下部分。幸运的是,这里有个小方式写所有代码。首先你能使用“.db"命名去存储数据字节:

着色板数据:

.db $0F,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3A,$3B,$0F,$3D,$3E,$0F ; 背景着色数据

.db $0F,$1c,$15,$14,$0F,$02,$38,$3C,$0F,$1C,$15,$14,$0F,$02,$38,$3C ; 精灵着色数据

然后一个循环被使用,去拷贝这些字节到PPU的着色板。X寄存器被使用作为一个着色板的索引,使用去计数多少次循环被重复。你想去拷贝两个着色板,一次32个字节,所以,循环开始从0计数到32.

LDX #$00 ; 开始于0

LoadPaletesLoop:

LDA PaletteData, x ; 从地址加载数据(PaletteData + x的值)

; 第一次循环将加载PaletteData + 0

; 第二次循环将加载PaletteData + 1

; 第三次循环将加载PaletteData + 2

; 等等

STA $2007 ; 写到PPU

INX ; x = x + 1

CPX #$20 ; 比较X和十六进制20,十进制32

BNE LoadPalettersLoop ; 分支到LoadPalettesLoop 如果比较不相等于0

; 如果比较等于32,保持继续向下


一旦这些代码完成,这整个颜色着色板已经准备。在你程序在运行的时候,一个字节或者整个事情能被改变。


精灵


一些事情从背景分离移动,将会由精灵做成的。一个精灵仅仅是一个8*8像素纹理,PPU可以在屏幕的任何地方渲染。一般情况,这些对象是由多个精灵彼此连接。例子是马里奥和一些敌人,想Goombas和Browser.PPU有足够的内部内存为64个精灵。这些内存从其他视频内存分开,不能被扩展。


精灵DMA

最快和最容易方式是使用DMA转换你的精灵到精灵内存.这仅意味着一块内存被从CPU内存拷贝到PPU精灵内存。这个主板内容空间从$0200到$02FF,这个通常作为目的内存使用。去开始转换,有两个字节需要使用PPU端口来被写:

LDA #$00

STA $2003 ; 设置RAM地址的低字节

LDA #$02

STA $4014 ; 设置RAM地址的高字节,开始转换


一旦第二次写完成,这个DMA转换将会自动操作。所有来自64个精灵的数据将会被拷贝。像所有的图形更新,这些需要在VBlank开始时期被完成,所以,它将在你的代码的NMI段


精灵数据

每个精灵需要4字节的数据作为它的位置和纹理,按照这个顺序:

1 - Y 位置 精灵在屏幕上的垂直位置。$00 是在屏幕的顶部。一些$EF上面是屏幕的关闭按钮。

2 - 纹理数 - 从模式作为图形被代入的纹理数目(0到256)

3 - 属性 - 这个字节拥有颜色和显示信息:

7 6 5 4 3 2 1 0

| | | | |

| | | + + - 精灵颜色着色板. 从16个颜色中选择哪4个字节被使用

| | |

| | + - - - - - - 优先级(0:在背景前面; 1:在背景后面)

| + - - - - - - - 水平翻转精灵

+ - - - - - - - --垂直翻转精灵

4 - X 位置 - 在屏幕上的水平位置。 $00是在左边,其余些在$F9上面的都在屏幕外面


这些4个字节被重复64次(一次一个精灵)去填充精灵内存的256个字节。如果你想要编辑精灵0,你能改变字节$2000-0203.精灵1是$0204-0207,精灵2是$0208-020B,等等


开启NMI/精灵

PPU的端口$2001是被再次使用去开启精灵。设置位4为1让他们出现。NMI也需要被开启,所以精灵DMA将会运行,精灵将会在每个帧被拷贝。这个在PPU端口$2000被完成。这个模式表0也是被查询去选中精灵表。当这些被增加后,背景将来自模式表1。

PPUCTRL ($2000)

7 6 5 4 3 2 1 0

| | | | | | |

| | | | | + + - 名称表的基地址

| | | | | (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)

| | | | + - - - 每次CPU读/写PPUDATA,增加VRAM地址

| | | | (0:每个加1,穿过; 1:每次增加32,下降)

| | | + - - - - 作为8*8精灵的精灵模式表地址(0:$0000; 1:$1000)

| | + - - - - - 背景模式表地址(0:$0000; 1:$1000)

| + - - - - - - 精灵大小(0:8*8; 1:8*16)

+ - - - - - - - - 生成一个NMI在垂直空白内部的vlank的开始(0:关闭,1:打开)


这些新代码被设置到精灵数据:

LDA #$80

STA $0200 ; 把精灵0放在屏幕垂直的中间位置

STA $0203 ; 把精灵0放在屏幕水平的中间位置

LDA #$00

STA $0201 ; 纹理号 = 0

STA $0202 ; 颜色着色板 = 0, 没有翻转


LDA #%10000000 ;开启NMI, 精灵来自模式表0

STA $2000


LDA #%00010000 ; 没有增强(白色背景),开启精灵

STA $2001


把这些放一起

下载并解压sprites.zip样例文件。上面所有的代码都在sprites.asm文件里,确保制作sprites.asm,mario.chr,和sprites.bat 所有这些文件在相同的文件夹NESASM3中,然后双击sprites.bat.然后运行NESASM3,将产出一个sprites.nes文件。在FCEUXD SP中运行NES文件去看你的精灵。纹理号0是Mario的头和帽子的背部,你可以看见么?编辑sprites.asm去改变sprite的位置(0到255),或者去改变精灵(0到3)的颜色着色表。你可以在FCEUXD SP种查看PPU的视图去查看两个模式板和两个着色板。


-------------解读和学习建议-------------------------------------------------------------------------------------------

本周主要讲解颜色着色板和精灵相关数据的设置和操作,学习和实验过程中,不要急于完全读懂,可以尝试做这样的步骤来学习。

首先,先下载案例代码,在模拟器中运行,并且使用模拟器自带的工具,查看两个模式板和两个着色板的信息。这样有个直观感受。

然后,看本教程的示例代码,比如下面这段:

LDA $2002

LDA #$3F

STA $2006

LDA #$10

STA $2006

在这段代码,作用在参考教程后,比较容易得到设置PPU的数据读写地址为$3F10,具体为什么这么写,目前这个阶段就忽略,直接模仿就行了(可以作为思考点)。

然后,可以做点思考,比如,为什么要先要读取$2002?如果没有这个操作会怎样?

在参考教程的解释,通过读PPU状态寄存器来重置到高,可以追溯到高和低的区别。

其次可以改的地方是$3F10的值,可以作哪些改变,最大最小范围是哪些?可不可以设置到$FFFF?

上面是比较直接的初级思考和改造性学习,想要高一层次理解,可以思考为什么要先写高位,再写低地位,这个问题需要前面一个思考点为基础,就是为什么需要花两次写。这样高低为写后了之后,其内部怎样去实现这样机制的,要是我们来设计,我们会怎么做,把这些设置思想记下来,在下来课程来验证,你的思路是否可以继续兼容或者要修正。来体验下原始设置这些系统的工作人员的思考和设计尝试。


其他相关代码,可以按照初级方式去实践,去修改,不清楚地方找教程相关部分再去细读。如果还是不清楚可以查其他相关资料来阅读。

当然,阅读和学习实践过程如果有疑难之处,可以联系本作者(w:jinjiacun123)一起探讨,共同提高进步。

-------------英文原文-------------------------------------------------------------------------------------------

This Week: now that you can make and run a program, time to put something on screen!


Palettes


Before putting any graphics on screen, you first need to set the color palette. There are two separate palettes, each 16 bytes. One palette is used for the background, and the other for sprites. The byte in the palette corresponds to one of the 64 base colors the NES can display. $0D is a bad color and should not be used. These colors are not exact and will look different on emulators and TVs.

The palettes start at PPU address $3F00 and $3F10. To set this address, PPU address port $2006 is used. This port must be written twice, once for the high byte then for the low byte:


LDA $2002 ; read PPU status to reset the high/low latch to high

LDA #$3F

STA $2006 ; write the high byte of $3F10 address

LDA #$10

STA $2006 ; write the low byte of $3F10 address


That code tells the PPU to set its address to $3F10. Now the PPU data port at $2007 is ready to accept data. The first write will go to the address you set ($3F10), then the PPU will automatically increment the address ($3F11, $3F12, $3F13) after each read or write. You can keep writing data and it will keep incrementing. This sets the first 4 colors in the palette:


LDA #$32 ;code for light blueish

STA $2007 ;write to PPU $3F10

LDA #$14 ;code for pinkish

STA $2007 ;write to PPU $3F11

LDA #$2A ;code for greenish

STA $2007 ;write to PPU $3F12

LDA #$16 ;code for redish

STA $2007 ;write to PPU $3F13


You would continue to do writes to fill out the rest of the palette. Fortunately there is a smaller way to write all that code. First you can use the .db directive to store data bytes:


PaletteData:

.db $0F,$31,$32,$33,$0F,$35,$36,$37,$0F,$39,$3A,$3B,$0F,$3D,$3E,$0F ;background palette data

.db $0F,$1C,$15,$14,$0F,$02,$38,$3C,$0F,$1C,$15,$14,$0F,$02,$38,$3C ;sprite palette data


Then a loop is used to copy those bytes to the palette in the PPU. The X register is used as an index into the palette, and used to count how many times the loop has repeated. You want to copy both palettes at once which is 32 bytes, so the loop starts at 0 and counts up to 32.


LDX #$00 ; start out at 0

LoadPalettesLoop:

LDA PaletteData, x ; load data from address (PaletteData + the value in x)

; 1st time through loop it will load PaletteData+0

; 2nd time through loop it will load PaletteData+1

; 3rd time through loop it will load PaletteData+2

; etc

STA $2007 ; write to PPU

INX ; X = X + 1

CPX #$20 ; Compare X to hex $20, decimal 32

BNE LoadPalettesLoop ; Branch to LoadPalettesLoop if compare was Not Equal to zero

; if compare was equal to 32, keep going down





Once that code finishes, the full color palette is ready. One byte or the whole thing can be changed while your program is running.



Sprites


Anything that moves separately from the background will be made of sprites. A sprite is just an 8x8 pixel tile that the PPU renders anywhere on the screen. Generally objects are made from multiple sprites next to each other. Examples would be Mario and any of the enemies like Goombas and Bowser. The PPU has enough internal memory for 64 sprites. This memory is separate from all other video memory and cannot be expanded.


Sprite DMA

The fastest and easiest way to transfer your sprites to the sprite memory is using DMA (direct memory access). This just means a block of RAM is copied from CPU memory to the PPU sprite memory. The on board RAM space from $0200-02FF is usually used for this purpose. To start the transfer, two bytes need to be written to the PPU ports:


LDA #$00

STA $2003 ; set the low byte (00) of the RAM address

LDA #$02

STA $4014 ; set the high byte (02) of the RAM address, start the transfer


Once the second write is done the DMA transfer will start automatically. All data for the 64 sprites will be copied. Like all graphics updates, this needs to be done at the beginning of the VBlank period, so it will go in the NMI section of your code.


Sprite Data

Each sprite needs 4 bytes of data for its position and tile information in this order:


1 - Y Position - vertical position of the sprite on screen. $00 is the top of the screen. Anything above $EF is off the bottom of the screen.


2 - Tile Number - this is the tile number (0 to 256) for the graphic to be taken from a Pattern Table.


3 - Attributes - this byte holds color and displaying information:


76543210

||| ||

||| ++- Color Palette of sprite. Choose which set of 4 from the 16 colors to use

|||

||+------ Priority (0: in front of background; 1: behind background)

|+------- Flip sprite horizontally

+-------- Flip sprite vertically


4 - X Position - horizontal position on the screen. $00 is the left side, anything above $F9 is off screen.


Those 4 bytes repeat 64 times (one set per sprite) to fill the 256 bytes of sprite memory. If you want to edit sprite 0, you change bytes $0200-0203. Sprite 1 is $0204-0207, sprite 2 is $0208-020B, etc


Turning NMI/Sprites On

The PPU port $2001 is used again to enable sprites. Setting bit 4 to 1 will make them appear. NMI also needs to be turned on, so the Sprite DMA will run and the sprites will be copied every frame. This is done with the PPU port $2000. The Pattern Table 0 is also selected to choose sprites from. Background will come from Pattern Table 1 when that is added later.


PPUCTRL ($2000)

76543210

| ||||||

| ||||++- Base nametable address

| |||| (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)

| |||+--- VRAM address increment per CPU read/write of PPUDATA

| ||| (0: increment by 1, going across; 1: increment by 32, going down)

| ||+---- Sprite pattern table address for 8x8 sprites (0: $0000; 1: $1000)

| |+----- Background pattern table address (0: $0000; 1: $1000)

| +------ Sprite size (0: 8x8; 1: 8x16)

|

+-------- Generate an NMI at the start of the

vertical blanking interval vblank (0: off; 1: on)


And the new code to set up the sprite data:


LDA #$80

STA $0200 ;put sprite 0 in center ($80) of screen vertically

STA $0203 ;put sprite 0 in center ($80) of screen horizontally

LDA #$00

STA $0201 ;tile number = 0

STA $0202 ;color palette = 0, no flipping


LDA #%10000000 ; enable NMI, sprites from Pattern Table 0

STA $2000


LDA #%00010000 ; no intensify (black background), enable sprites

STA $2001


Putting It All Together

Download and unzip the sprites.zip sample files. All the code above is in the sprites.asm file. Make sure sprites.asm, mario.chr, and sprites.bat are all in the same folder as NESASM3, then double click sprites.bat. That will run NESASM3 and should produce the sprites.nes file. Run that NES file in FCEUXD SP to see your sprite! Tile number 0 is the back of Mario's head and hat, can you see it? Edit sprites.asm to change the sprite position (0 to 255), or to change the color palette for the sprite (0 to 3). You can choose the PPU viewer in FCEUXD SP to see both Pattern Tables, and both Palettes.

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言