【原创】NES第八波:怎么写一个HelloWorld

这里我用的编辑工具还是6502Sim。

第一步是确定容量:我写一个极短的汇编代码,16K容量就远远足够了。所以程序从$C000开始存放。图库大小也是最小就可以(即8K)。

第二步是分派背景所在页和精灵所在页。不防定:背景=0页,精灵=1页。

第三步是准备一个4K的背景用chr,和一个精灵用的chr。

因为这次用不着显示精灵,所以精灵chr纯属是打酱油。不过也要用于填充,哪怕是4K的空白文件(字节要精确=4096)。

背景用chr,要我们去制作了。可以从nes游戏里面分离出来(用yychr),也可以用我的字模工具制作。为了方便编程,大小字母和数字按放的位置可以依ascii的号码对应。也可以只做HeloWrd这几个字母。为了简化代码,本例只做8*8的字模。制作的方法,见《NES第五波》。

注:YYCHR默认新建的文件有好几页。如果只想做一页,用我的字模工具,生成一个4K的就可以了。再用YYCHR打开,清空。另外再打开一个YYCHR,打开你想要chr。两个YYCHR可以互相粘贴。右键拉一个选框,就可以复制了。

第四步是新建NES的文件头,用到我的工具NESInfo,见《NES第一波》。

本例:mapper=0,PRG rom=1x16K,CHR rom=1x8K,镜像=水平,S-RAM=no,4屏=no,Trainer=no

mapper:就是卡带的电路板分类号码。0代表最原始的电路板,即没有切页功能,也没有扩展功能。

PRG rom:就是指程序空间的容量,匹配mapper0的容量就是1x16K和2x16K。有一个定律就是程序末尾必然在地址$FFFF。所以1X16K就是程序从$C000开始存放,2x16K就是程序从$8000开始存放。

CHR rom:就是指图库空间的容量,匹配mapper0的容量就是1x8K。对应的地址是ppu$0000到ppu$1FFF

镜像:指PPU的背景页,机内只有2页容量,但有4页地址,那么就会形成镜像。PPU的A10和A11连到卡带,由卡带对这两线的接线方式,决定镜像方式(水平或垂直)。

水平镜像,指0页(ppu$2000)与1页(ppu$2400)镜像,那么可以进行2屏的垂直滚屏(即0页与2页(ppu$2800)都是可编辑的,可以连起来。)。2页与3页镜像,同理。

垂直镜像,指0页与2页镜像,那么可以进行2屏的水平滚屏(即0页与1页(ppu$2400)都是可编辑的,可以连起来。)。1页与3页镜像,同理。

PPU的背景页分布应该是这样的:

【0页ppu$2000】【1页ppu$2400】

【2页ppu$2800】【3页ppu$2B00】

(不是说镜像页就不能编辑,而是没必要编辑,因为无论改哪面都是两面一起变的。)

S-RAM:指卡带上是否增加一块电池维持的内存(地址在$6000到$7FFF)。模拟器会因为它而生成一个nes扩展文件。

4屏:指卡带上是否增加ppu的两个背景页,那就不存要镜像了,前面“镜像”选项就无效。

Trainer:原意教练,这里指卡带上是否增加一块512字节的ROM(地址在$5E00到$5FFF),选这个项的话,连接时要在文件头与PRG rom之间加一块512字节的Bin文件。有点麻烦,还是选no就好了。

NESInfo,可以生成bin文件,也可以对bin文件直接编辑。所以修改还是很方便的。我们用NESInfo,生成一个Head.bin。即nes文件头bin。

第五步写代码。因为代码的编幅太长,讲解代码我放在最后讲。编译工具,我用6502Sim。我选一个汉化版的,有高亮语法显示,有没有写错指令,一眼就知道。

写好代码之后按“仿真”-“汇编F7”。如果没有报错,窗框一闪就关了,说明Pass。可以输出bin。

按菜单“文件”-“保存代码Ctrl+K”;保存类型选"二进制映像.65b";然后一定要点“选项”;在代码地址栏的“开始于”=0xC000,这一项必须确认,这就是第一步选定的地址;确定;编辑文件名,保存。

本例保存到代码的上一层文件夹,与head.bin、xx.chr等放一起,文件名:main.65b

第六步连接。用DOS指令copy /b 将多个文件首尾连成一个新文件。批文件bat是文本文件,可以由记事本打开。学着写就行。

必须注意的是,不能写错文件名。

保存编辑好的批文件。双击批文件,我加了一句暂停指令,你要看清显示的结果,凡是写错文件名的,都没有显示出来,也就没有连接到nes文件里,但是没有报警的。所以必须看清楚了。

 

6502源代码:

  1 ;开始地址:$c000
  2 ;结束地址:$ffff
  3 ;文件长度:$4000
  4 ; prg rom = 16K
  5 
  6         ;.start  reset
  7         .ORG    $C000
  8 reset:
  9     ; 默认启动代码
 10         SEI             ; 禁用中断
 11         CLD               
 12         LDX     #$ff    ; 初始化栈顶指针到$FF
 13         TXS
 14 
 15         ; 初始化内存
 16         INX        ; x=0了
 17 _loop_1:        ; 清理全部内存
 18     STA $00,x
 19     STA $0100,x
 20     STA $0200,x
 21     STA $0300,x
 22     STA $0400,x
 23     STA $0500,x
 24     STA $0600,x
 25     STA $0700,x
 26     INX
 27     BNE _loop_1
 28     
 29     ; PPU热机,大约要2帧的时间
 30 _vb1:            ; 1帧
 31         BIT $2002
 32         BPL _vb1
 33 _vb2:            ; 2帧
 34         BIT $2002
 35         BPL _vb2
 36             ; 以下可以对PPU操作了。
 37 
 38     ; 初始化PPU
 39         LDA #$00    ; 关屏
 40         STA $2001
 41 
 42     LDA #$3F    ; 写入配色盘(从0号盘开始)
 43     STA $2006
 44     LDA #$00
 45     STA $2006
 46     LDA #$0F    ;0#=黑色
 47     STA $2007
 48     LDA #$30    ;1#=白色
 49     STA $2007
 50     LDA #$2B    ;2#=浅蓝色
 51     STA $2007
 52     LDA #$15    ;3#=红色
 53     STA $2007
 54             ;...可以继续写入1号2号和3号盘。各4个字节。
 55             
 56     LDA #$20    ; 清除背景2000-23FF即0页背景。
 57     STA $2006
 58     LDA #$00
 59     STA $2006
 60     LDY #$04
 61 _loop_ppu_1:
 62     LDX #$00
 63     LDA #$00
 64 _loop_ppu_2:
 65     STA $2007
 66     DEX
 67     BNE _loop_ppu_2
 68     DEY
 69     BNE _loop_ppu_1
 70     
 71 _vb3:            ; 凑足够一帧
 72     BIT $2002
 73     BPL _vb3
 74 
 75         ; 设置PPU的工作方式  
 76         LDA #$08    ; (D7=0)禁nmi中断,
 77                 ; (D5=0)精灵=8*8,(D6=x)
 78                 ; (D4=0)图库:背景用0页,
 79                 ; (D3=1)图库:精灵用1页,
 80                 ; (D2=0)PPU写入自动+1,
 81                 ; (D1D0=00)命名表=2000
 82         STA $2000
 83             ; 开屏,也是正式设置PPU的显示方式
 84         LDA #$08    ; (D7D6D5=000)底色=黑
 85                 ; (D4=0)不显示精灵
 86                 ; (D3=1)显示背景(开屏)
 87                 ; (D2=0)左8列像素不显示精灵,可以将精灵藏在其中
 88                 ; (D1=0)左8列像素不显示背景,可用来做滚屏
 89                 ; (D0=0)显示模式=彩色
 90         STA $2001
 91 
 92 main:
 93     ; 黑屏,这样写入才不会花屏。如果动态写入要用中断。
 94     LDA #$00
 95     STA $2001
 96     
 97     ; 确定位置在$2021(即第2行的第2列);注,从$2000开始,每行32个图块
 98     LDA #$20
 99     STA $2006
100     LDA #$21
101     STA $2006
102     
103     ; 写入显示的内容
104     ; 本例chr的字母是ascii的序号排的,所以这小字与ascii相符。
105     LDA #$48    ; H
106     STA $2007
107     LDA #$65    ; e
108     STA $2007
109     LDA #$6c    ; l
110     STA $2007
111     LDA #$6c    ; l
112     STA $2007    
113     LDA #$6F    ; o
114     STA $2007
115     LDA #$57    ; W
116     STA $2007
117     LDA #$6F    ; o
118     STA $2007
119     LDA #$72    ; r
120     sta $2007    
121     LDA #$6c    ; l
122     STA $2007
123     LDA #$64    ; d
124     STA $2007
125     
126     LDA #$00    ; 复位PPU的显示位置(对应0页($2000)背景就是(0,0))
127     STA $2005
128     STA $2005
129     
130     LDA #$08
131     STA $2001
132 end:
133         JMP end
134         
135 
136 nmi:
137         RTI
138 
139 irq:
140         RTI
141 
142 
143         .ORG $fffa
144         .DW nmi,reset,irq

 

工程到我的网盘找。维京的梦 (ys168.com)

续。。找个时间再续写代码的解析。

posted on 2021-08-02 00:19  大魔司教教主  阅读(49)  评论(0编辑  收藏  举报

导航