zoukankan      html  css  js  c++  java
  • 【原创】NES第四波:如何做一个nes静止画面?(程序运行基本流程)

    如何做一个nes静止画面?
        在这一讲中,我讲最基本的东西,是以后每一步都需要的,但不会重复讲的了。
        前言
        nes文档是由文件头、程序和图形数据组成的。
        nes程序是由6502机器码(8位)一个个拼接组成。6502汇编则是与6502机器码对应的,下面说的nes程序就是说由6502汇编编写成的程序。换言之,也是在说6502汇编代码。
     
        一、nes程序的基本内容
        nes的程序可以分4部分内容。
        1 初始化和机器预热。
        2 主程序循环。
        3 NMI中断。(独立)
        4 IRQ中断。(独立)
        其中“主程序循环”是接在“初始化和机器预热”后面运行的。而两个中断是独立的。它们与主程序之间用(公用)变量沟通。汇编里面一般都是用公用变量的。如没有特别说明,都是指公用变量。
     
        二、什么是初始化和机器预热,具体什么流程?
        初始化是对cpu做一些初定、对内存做统一清理、也对一些主程序和中断里面用到的变量赋值。
        这些变量即使预设是零值也要写上。因为上电和重新复位也是要运行这部分。
        真机的上电,内部中的所有地址上的值都是随机的。
        真机的复位,内部中的所有地址上的值都保持复位前的值,不变。复位不会对内存有任何影响的,它只不过是改变了cpu中的p寄存器。
        模拟器的上电(即打开某个nes),内部地址上的值,我就说不上了。一个个模拟器作者各有不同想法,我就不知道。不过,肯定不会闲着去生成随机数,但是他们会申请一块空间,就不知会是什么情况。
        模拟器的复位,我想应该与真机的情况一样。
     
        为什么我要说上一段这些话?
        一些合集利用复位切换游戏,那只要不对某个变量初始化赋值,反而+1,并比较超限制值则复零,这样只要读此变量就能对应的切换游戏。
        一些正常使用的变量,如果上电或复位肯定是希望按预期运行,那么初始化赋值是必须的。
     
        机器预热是什么回事?
        真机的PPU的启动比CPU慢,所以程序要等待第一个vblank的出现,才能肯定PPU开始工作了,我们才能够写入数据,在下次vblank后显示出来。(关于vblank,我专门写一篇来解释。)
        预热开始就要关屏。并一直保持一边刷关屏一边读vblank。
        预热后,进入PPU的初始化。
        PPU的初始化是设置PPU。然后要对灵精做一些处理,隐藏在背景后面,防止花屏。因为上电时灵精的数据也是随机的。接下来写入背景,用不到的页面也要清空。
        显示静止画面就只有上述的最后一部分,刷静止画面是很常用的,我们写成一个子过程就最好了。
     
        小结:
        对所有变量的赋值是很快的,可以放到PPU设置之前,这样与游戏有关的部分结合在一起,对代码可读性有好处。
        某些变量如果想在复位后保持不变,可以修改内存清理代码。一般将这些保持变量定义在内存的最后几个地址($07FF)。这样改代码也方便。调整和修改程序时影响也不大。因为程序一般不会将内存用尽,那么最后几个地址都不会影响什么的。
        注意,cpu初始化全过程不要用子过程,因为子过程用到栈,而cpu初始化则要清理栈。所以不要作死。在清理栈之后,就可以使用子过程了。
        初始化和机器预热流程如下:
        1 cpu初始化(包括屏蔽十进制指令、清理栈、清理内存)。
        2 机器预热(包括关屏、等待vblank、隐藏灵精、等待vblank结束)。
        3 所有变量赋初始值。
        4 PPU设置和画开始画面。
        5 开屏显示画面。
        上述流程,1和2是通用流程,不管什么游戏都不变的。3和4是与游戏内容相关的。
     
        三、背景都有什么内容,怎样写入(或加载)?
        基本的nes电路是包含2页背景页。我们只要写入其中一页就能够显示了。每个背景页都包含一个命名表和一个属性表。命名表指定图形,属性表指定配色。
        我的这一讲是教“怎么做”,关于原理和细节要看《任...系统文件》。
     
        1 首先,图形是怎么得到的?
        nes的图形文件,我们叫chr。编辑chr就要用到yy-chr或者ch-tlp。
        我用独立的一篇来讲一讲YY-CHR的用法。
        chr图形的单位是Tile,每个Tile是8*8像素。
        我们要注意,chr是不会记录颜色的,它只会记录色盘中的编号。那么色盘是哪些颜色,图形就会显示哪些颜色了。这很灵活,还能换颜色,但是设计的时候要记录(或保存)下设计的颜色。
     
        2 然后,文字怎么显示的?
        文字(包括数字、英文、中文或者什么国家的外文,还有标点符号)也是要做成chr文件,我们称之为字模。再用yy-chr,将字模与图形粘贴成一个4K的chr文件。
        (注意,灵精用图形的方法做成另一个4K的chr文件。)
        这里说的图形和字模,指的是用在背景上的。灵精一般不用于显示文件,因为灵精一行不能超过8个,所以特别需要才用。
        本讲只针对背景上的图形和字模。
        我还是独立用一篇来讲一讲“菜菜字模工具”的用法。
        字模是图形的一个子类,那么结构和颜色记录方面也跟图形是一样的。
        我利用配色的技巧和属性表的配合,可以实现16*16汉字的压缩和显示。这些都在那一篇里说明。也会在接下来画面一篇里面说明的。
     
        3 接下来,画面怎么画?
        图片转成nes画面?这基本是不可能的。只有对nes了如指掌的人、同时对编程也是高手的人才做得到,我自认也办不到。
        先要按手头上的图形对画面做拼凑设计。我们要知道画面的尺寸。我们以格子为单位。每个格子可以放一个chr(8*8)图形。那么画面的尺寸是32*30个格子,是横放的。但是有效显示的30*28个格子。换言之,顶行和底行,最左列和最右列是不显示的。这个很重要。有同学尝试显示一个图形,就定在第一行,那么运行nes的结果就是空白。用方格本设计一个草图,就依着它来画吧。
        设计好草图,就用我的“背景编辑工具”,得到一个命名表,一个属性表和一个调色板。
     
        4 最后画面怎么加载呢?
        命名表、属性表和调色板都是PPU的数据,是需要保存在VRAM中的(具体地,命名表、属性表保存到两个背景页之一,即VRAM中;调色板要保存到PPU里面去)。
        显示那个背景页,要看PPU中的$2005上的设置。下面说的内容,通通可以在《任...系统文件》上读到,我就简明地说一次。
        注意,下文加注“PPU地址”说明是PPU地址线的地址段,没注的说明是CPU地址线的地址段,以示区别。
        加载画面的步骤:
        准备:在前一步,设置PPU($2000的D3D4位)的时候,要指定背景和灵精各用哪一页图案表。不防设为背景用图案表0号页,灵精用图案表1号页。到了用批处理脚本打包NES文档的时候,背景用的chr就放在前,灵精用的chr就放在后。这样才能保证图案对位。
        (1)关于文件头设置
        在文件头设定水平镜像/垂直镜像,这是关于背景页的第二页在哪里。
        当设为水平时,左右两个背景页是同步的。第二页在第一页的下面。
        当设为垂直时,上下两个背景页是同步的。第二页在第一页的右面。
        PPU是假定自己有4页背景,但nes只给了它2页,余下的2页只能错误地成为镜像。
        现在我们只显示一页背景,不用知道太多,只管在文件头便设定一个镜像,反正是用不着的,可以通过模拟器观察镜像情况。
        (2)关于PPU设置
        现在我们加向第一页加载,那么目标就是PPU地址$2000所在的背景页。向$2000的D0D1写入00。表明以PPU地址$2000为第一背景页。
        加载是按字节顺次写入的,所以向$2000的D2写入0。
        (3)关于写入数据
        既然背景页地址在PPU地址$2000,则向$2006写入$20,再向$2006写入$00,就定向到PPU地址$2000了。再向$2007连续写入命名表,然后继续向$2007写入属性表。最后$2006写入$3F,再向$2006写入$00,就定向到PPU地址$3F00了,这是背景调色板的地址。再向$2007连续写入一个调色板。
        (4)设置显示窗口
        我们就在第一背景页上加载和显示,那么显示窗口要定在(0,0)。即当加载完之后向$2005写入0,两次。以后再介绍怎么移动显示窗口。

        加载这就完成了,最后就是开屏显示。如果是动态刷新背景,就不能像上述那样连续加载,要在中断(或vblank)里面分次分批完成。以后的教程再讲。
     
        另外要加载第二页要怎样呢,这里重点是搞定“不同镜像下,第二页的地址在哪,显示位置在哪,怎样移动显示窗口。关于这些,我自己都要做N个试验才能说清楚,书里面真是写得迷糊。
        另外4页联屏是什么情况。我也没有时间确定,但是这都不难。
     
        本范例用不到主程序循环和两个中断。所以简化处理它们。
        相关代码我会慢慢补充。也可以暂时看看我之前发的范例代码。可以看出一二的。所有说了要独立写的篇章,将在后面慢慢补充。有部分看完面的内容也有的,只是重新会更好。
  • 相关阅读:
    Effective Java 的笔记(二)
    设计模式系列 装饰模式
    一道多线程题目的解决方案
    Effective Java 的笔记(一)
    Java 并发编程实践
    【转】微博技术底层架构的实现
    Head First JavaScript 笔记
    JVM 学习笔记 类的加载和执行
    背包问题
    Oracle 序列号通过定时任务重置
  • 原文地址:https://www.cnblogs.com/fogota/p/13624383.html
Copyright © 2011-2022 走看看