十八、大字的输出程序
这一部分将要分享使用4个Tile显示一个汉字的方法。
主程序WriteLarge包括3个部分:WordLocation用于计算出所要显示的字符在字库中的位置;VRAMWrite将这个字符对应的4个Tile写到VRAM中;MAPWrite将这些Tile写到MAP上从而可以在屏幕上显示对应的汉字。
10:4100 WriteLarge: push hl push de push bc push af call WordLocation call VRAMWrite pop af pop bc pop de pop hl call MAPWrite ret |
程序WordLocation(10:4200)用于根据字符编码换算出该字符在Bank中的地址
10:4200 WordLocation: ld c,a ld a,0 ld b,a sla c rl b sla c rl b sla c rl b sla c rl b ld hl,4000 add hl,bc add hl,bc add hl,bc add hl,bc ret |
首先通过位运算把a值扩大10倍放到bc中,字库的起始地址是4000,累加4次bc得到最终的地址,并把这个地址保存在hl中。需要注意的是对于不同的字库类型,计算字符地址时会有差别,这里是按照GB 2bpp的字体来写的程序。
接下来我们就需要把这些Tile从Rom中读出来并写到VRAM中,通过下面这程序VRAMWrite来实现
10:4230 VRAMwrite: push hl call VRAMaddr pop hl ld b,40 ReadNext: ld a,(DA01) sub a,20 call ReadTile push af judgeVRAM: ld a,(FF00+41) and a,03 cp a,00 jr nz,judgeVRAM pop af ld (de),a inc de dec b jr nz,ReadNext ld a,d ld (DA04),a ld a,e ld (DA05),a ret |
首先调用程序VRAMaddr计算出VRAM中可写入的地址保存在de。接下来要读出字符的Tile的一个字节放到a,字符的Bank号根据双字节编码的第一个字节计算出来,这个值已经被保存在DA01中,取出这个值,减去20得到实际的Bank号。调用程序ReadTile读出Tile信息。由于VRAM不是随时可以写入的,需要先判断VRAM是否是可以写入的状态,具体原理我也不太明白,可以直接套用这里的程序。一旦判断出可以写入就执行写入操作,然后将de值增加1,再接着读出下一个字节来写入,重复40次,把一个字对应的4个Tile都写入到VRAM中。写完之后把当前de中的地址保存在DA04、DA05中,后面的字可以跟在这个地址之后去写入。
VRAMaddr程序是用来生成VRAM中可以写入的地址
10:4280 VRAMaddr: ld hl,DA04 ldi a,(hl) cp a,0 jr nz,VRAMaddread VRAMreset: ld a,A0 ldd (hl),a ld a,90 ldi (hl),a VRAMaddread: cp a,97 jr z,VRAMreset ld d,a ld e,(hl) call VRAMTile ret |
之前我们已经分析出来,《圣剑传说》这个游戏的VRAM中可以用来写入字体Tile的地方还是挺大的,从90A0到9760这些原来存放日文Tile的位置都可以写入。VRAM中可写入的地址信息我们是保存在DA04、DA05中的。程序先判断DA04是否写有地址,没有的话写初始地址90A0,再判断这个地址是否是97开头,也就是VRAM最下面一行,是的话也跳回开头设置为初始的地址90A0。
需要注意的是,对于对话这样一直在动态变化的文本, VRAM中的地址循环利用不会有大问题。但是对于菜单栏、状态栏等一些文本,大部分字是不会变的,只有少部分字在变,这样把所有VRAM拿来循环利用就会出问题。这时候就需要考虑给每一部分文字设置单独的VRAM区域,这个后面遇到再展开说明。
在计算完VRAM地址后,又调用程序VRAMTile,它的作用是根据de中VRAM地址,计算出对应的Tile编号信息并保存到DA07,供后面写到MAP上时调用。
10:4260 VRAMTile: ld a,d swap a and a,F0 ld b,a ld a,e swap a and a,0F add b ld (DA07),a ret |
这个Tile编号就是将来要写到MAP上用来显示文字的,比如当VRAM地址是90A0时,这个地址对应的Tile编号就是0A,也就说接下来0A开始连续的4个Tile就是用来显示这一个汉字的。
做完这些准备工作就是要把字符的Tile从字库中读出来,这里用到了ReadTile这个程序,其实它就是前面我们用来读取文本的程序的一部分,只不过开头指定Bank号那里不一样,我们先计算出字库的Bank号,然后从程序的第二句开始开始调用
0:3F8B ReadTile: push hl call 2A21 pop hl ldi a,(hl) push af push hl call 2A30 pop hl pop af ret |
这一系列程序运行完后,我们就可以看到第一个字对应的4个Tile已经被写到了VRAM中。

接下来就是要让这个字在屏幕上显示,按照之前分析的,我们直接借用原来的程序,利用e的值来控制位置。程序MAPwrite(10:4400)负责把一个字符的4个Tile写到MAP中。0:3897用于写上半部分,0:389D用于写下半部分,e加1后写字的后半部分。文字的第一个Tile编号保存在DA07,开始导入字库时选择的是垂直重组,所以一个字的四个Tile是按照左上、左下、右上、右下存放在VRAM里的,写到MAP时也按照这个顺序来写。
10:4400 MAPwrite: ld a,(DA07) push af call 3897 pop af inc a push af call 389D inc e pop af inc a push af call 3897 pop af inc a call 389D dec b ret |
因为我们这里相当于一次写了原来的两个字,所以b要多减1,e在运行中已经加了1,所以结尾就不用再加1了。
这样我们就实现了对话部分的文本修改和大字输出。

上一篇:
GB/GBC汉化经验分享之十七--新的文本读取程序下一篇:
【求助】海淘的wiiu到货了,问题不少