此次说一下GB的显示系统,先从一幅Gb的内存分布图提及,请看图:缓存
图中红色框框起来的部分就是这篇文章关注的部分,这一部分的内存地址从8000-9Fff,共8KB,这一部分是历来存储背景和游戏“精灵”的数据的。一般咱们认为Gb只能显示黑、白两种颜色,但其实,Gb还能显示明、暗灰度,总共是4种颜色。这样,显示出来的每一个像素点须要占用两个bit的空间。对于Gb的显示系统来讲,屏幕会缓存256*256大小的图片,其中160*144大小的某个区域会显示在液晶屏上,咱们算一下,要缓存的图片的大小为256*256*2=16KB。可是咱们的显存只有从8000-9Fff的8KB,显然是放不下的。因此Gameboy使用了某种机制,使不够16KB的显存也可以显示出16KB大小的内容,这种机制就是再显存中一部分用来存放图片数据,一部分用来存放映射数据。图片被划分为每一个8*8大小的块(tile),每一个块有一个编号,而在映射数据的部分存的只是每一个块的编号,这样子,每一个块就有可能会被重复使用,从而达到在不足16KB显存的状况下显示16KB数据的目的。这就解释了为何在上一篇文中,我直接把任天堂的logo数据加载成图片,显示出来的东西确怎么看都不像是一个logo,其实加载进来的数据只是一个映射数据,而不是真正的图片数据。ide
好,咱们继续。知道了gameboy的显示机制以后再从新看一下显存中的数据分布状况,请看下表:学习
区域spa |
做用3d |
8000-87FF调试 |
图块集1: 图块编号0-127对象 |
8800-8FFFblog |
图块集1: 图块编号128-255 |
9000-97FF游戏 |
图块集0: 图块编号0-127 |
9800-9BFF |
图块集0的映射区域 |
9C00-9FFF |
图块集1的映射区域 |
图块集1能够用来显示背景、窗口和精灵,图块集0用来显示背景和窗口。这里的窗口指的就是160*144大小的在液晶屏上显示的区域。
精灵
Gameboy能够控制40个精灵的显示,每一个精灵的大小为8*8或者8*16的图片块,同时,受硬件能力限制,每一个扫描线(scan line,对这个概念理解得不是很好,但愿有人能帮助解释一下)只能显示10个精灵。每一个精灵的图片数据存放在8000-8Fff的区域中,就是上面说的图块集1所在的区域。而精灵自身的属性数据(x和y坐标等数据)则存放在专门的精灵属性表中(Sprite Attribute Table),也叫对象属性内存OAM(Object Attribute Memory),该区域位于内存FE00-FE9F中,OAM被划分红40个4字节的块,每一个块表明一个精灵。
对于精灵对象,x坐标越小则它的显示优先级越高,对于拥有一样x坐标的精灵对象,则经过它在OAM中的顺序来区分显示优先级(FE00的最高,FE04的次之,而后依次往下)。
因为最多只能有10个精灵在同一行上显示,因此,在一行有超过10个精灵的时候,显示优先级低的将会被隐藏。为了不不使用的精灵影响正常精灵的显示,应该把这些精灵的位置设为y=0 or y>=144+16或者x=0 or x>=160+8.
下面说一下OAM中每一个精灵属性数据的含义:
Byte0: y坐标值
Byte1: x坐标值
Byte2: 精灵的图块编号,对于8*16模式大小的精灵,LSB(最低有效位?)被忽略
Byte3:标志位:
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
值0 |
显示在背景和窗口的顶端 |
从OBJ0PAL取色 |
||||||
值1 |
若是背景和窗口关的颜色为1,2,3则隐藏 |
垂直跳动 |
水平跳动 |
从OBJ1PAL取色 |
颜色表:
值 |
模拟的颜色 |
0 |
[255, 255, 255] |
1 |
[192, 192, 192] |
2 |
[96, 96, 96] |
3 |
[0, 0, 0] |
好了,再了解完gameboy的显示机制以后接下来就要看一下在程序上是怎么来完成从内存到液晶屏上的显示过程了:
下表列出并说明了显示中用到的一些特殊的寄存器,特殊是由于这些寄存器是对内存中特定地址的称呼,而不是在Cpu中的寄存器:
FF40 - LCDC – 液晶屏控制器 (R/W)
Bit 7 – 是否开启液晶屏显示 (0=Off, 1=On) Bit 6 – 窗口图块映射范围选择 (0=9800-9BFF, 1=9C00-9FFF) Bit 5 – 是否开启窗口显示 (0=Off, 1=On) Bit 4 – 背景和窗口图块数据范围选择 (0=8800-97FF, 1=8000-8FFF) Bit 3 – 背景图块映射范围选择 (0=9800-9BFF, 1=9C00-9FFF) Bit 2 – 精灵尺寸选择 (0=8x8, 1=8x16) Bit 1 – 是否容许显示精灵 (0=Off, 1=On) Bit 0 – (对于黑白的GB和SGB)背景显示开关 (0=Off, 1=On) 对于CGB在CGB模式下,0=背景和窗口会失去他们的优先级,精灵们仍是会显示在背景和窗口的顶端。 对于CGB在非CGB模式下,0=背景和窗口变白,只有精灵任能继续显示 |
注:对于Bit7中止LCD操做只能在v-blank中断中进行,不然会毁坏硬件(咱们作模拟器开发的就不须要担忧这个了J)
FF41 - STAT - LCDC Status (R/W)
如下是一些典型的数值切换的过程当中各个模式的值:
Mode 2 2_____2_____2_____2_____2_____2___________________2____
Mode 3 _33____33____33____33____33____33__________________3___
Mode 0 ___000___000___000___000___000___000________________000
Mode 1 ____________________________________11111111111111_____
模式标志每一个周期的切换顺序为0,2,3,一个周期的时间大约为109uS,0大约停留48.6uS,2大约19uS,3大约41uS,这由vblank中断没16.6ms触发一次,模式标志被设为1的持续时间大约为1.08ms.
用时钟周期表示的话模式0大概201-207个周期,模式2大概77-83个周期,模式3大约169-175个周期,一个完整的模式切换大约456个周期,vblank持续4560个周期,一个完整的屏幕刷新大约每70224个周期发生一次
FF42 - SCY – 滚动Y坐标 (R/W)
FF43 - SCX – 滚动X坐标 (R/W)
制定256*256的背景中,哪一点做为液晶屏幕的左上角原点。两坐标的范围为0—255.
the video controller automatically wraps back to the upper (left) position in BG map when drawing exceeds the lower (right) border of the BG map area.
FF44 - LY – LCDC的Y坐标 (R)
LY指示当前那一条水平线上的显示数据被传送给LCD驱动,LY的值可能为0—153,当值为144—153之间时,GB处于vblank中断之中,当写入值时,会重置计数器
FF45 - LYC - LY 比较 (R/W)
GB不停地比较Lyc和Ly寄存器的值,当两个值彻底同样时STAT寄存器的Coincidence Flag位会被设为1,而后若是开启了STAT中断的话,会请求该中断
FF4A - WY – 窗口Y坐标 (R/W)
FF4B - WX – 窗口X坐标 (R/W)
指定左上角的窗口位置,当Wx=0—166,Wy=0—143时,窗口可见
下面经过一张图来讲明Scy,SCX和Wy,Wx之间的关系:
FF47 - BGP – 背景调色板数据 (R/W) – 非CGB模式,即黑白模式
该寄存器用来给背景和窗口的图块的颜色编号赋上灰度值
Bit 7-6 -Color Number 3 Bit 5-4 - Color Number 2 Bit 3-2 - Color Number 1 Bit 1-0 - Color Number 0 |
颜色值参考上面的颜色值表
FF48 - OBP0PAL – 0号对象调色板数据(R/W) -非CGB模式,即黑白模式
FF49 - OBP1PAL - 1号对象调色板数据(R/W) -非CGB模式,即黑白模式
这两个寄存器分别给0号和1号精灵调色板赋上灰度值,每一个位表明的意思和BGP寄存器同样,但最低的两位是不使用的,由于00表明精灵是透明的
好,寄存器的介绍暂告一段落,如今来计算一下。不论是使用图块0仍是图块1,GB的映射空间的大小都是3FFF=1024,而后GB的背景是256*256,同时背景是由8*8的图块拼起来的,因此总共须要32*32个图块。而32*32正好等于1024,由此可知经过解析图块映射区内的全部图块的编号就可以获得要显示的背景的像素数据了。
由今天的分析可知每一个图块的大小是8*8个像素,占用的空间是8*8*2个bit,共16个字节。下面经过一副图来讲明:
由上图,其实每一个像素点的信息表示的是一个索引值,用来索引在调试版的颜色,调色板才是用来实际存放颜色信息的地方。
好了,今天的笔记的就到这吧,原本想先完成模拟Cpu部分的指令的代码再来学习显示的,不过在写代码的时候发现,若是没有显示的话都不知道实际模拟的对不对,因此就只能先学习显示部分的知识而后再去写代码了,有了输出差很少就能够判断本身的代码是否是正确了。
下一回将会对今天的知识用代码来进行实现,就是模拟gameboy的显示系统了,但愿不要太难,呼,老天保佑。