Open dustpg opened 6 years ago
return famicom->prg_banks[address >> 13][address & (uint16_t)0x1fff];
[address & (uint16_t)0x1fff];的作用是什么 ?
return famicom->prg_banks[address >> 13][address & (uint16_t)0x1fff];
[address & (uint16_t)0x1fff];的作用是什么 ?
这里是把内存地址划分为8块,每块大小为0x2000, 也就是8kib. 16位地址中, D0-D12作为块内偏移,D13-D15作为块编号. 所以获取D0-D12用& 0x1fff
, 获取D13-D15用>>13
. 不过我记得后面换成了16块,每块4kib, 你可以现在试试!
感谢作者,确实是这样,忘了那个其实可以看成一个二维的数组哈哈😄
哈哈,很久以前的文章了,但是還是冒昧問一下 const int id2 = famicom->rom_info.count_prgrom16kb & 2; 請問大大,這段是什麼意思呢
哈哈,很久以前的文章了,但是還是冒昧問一下 const int id2 = famicom->rom_info.count_prgrom16kb & 2; 請問大大,這段是什麼意思呢
位操作代替了分支,这里count_prgrom16kb 只有1和2两种情况,将两种情况展开的话可以进一步理解:
if (famicom->rom_info.count_prgrom16kb == 1) {
// ...
}
else {
// ...
}
STEP1: CPU地址空间: 基础读写 + Mapper000
让我们再跨一步吧.
6502汇编使用数字前面美元符号(\$)作为16进制的表示(8086则是在后面加‘h’)
CPU地址空间布局
先谈谈内存布局, 6502理论支持64KB的寻址空间, 但是小霸王服务器只有2kb的内存. 自然得说说内存布局
M: 主内存2KB镜像, 比如读取\$0800实际是读取$0000
R: PPU寄存器, 8字节步进镜像.
Registers: 一堆寄存器, 现在不用管.
Expansion ROM: 扩展ROM, 现在不用管
SRAM, PRG-ROM: 已经说过了
CPU中断
很遗憾, 自己在大学虽然是计算机系的, 但是不是计算机科学之类的学科, 而是靠近多媒体方向(比如计算机图形学, 音频之类的). 这就导致导致计算机硬件知识几乎没有, 只能靠脑补了.
6502有三种中断(按优先度排序, 越后面越优先):
每一种中断都有一个向量. 向量是当中断触发时“转到”的指定位置的16位地址:
也就是说程序一开始执行
$FFFC-FFFD
指向的地址'低地址'在'低地址', '高地址'在'高地址'. 即低8位在\$FFFC, 高8位在\$FFFD.
Mapper000 - NROM
目前当然是实现Mapper000, 实际上也就是没有Mapper的意思:
$8000-$BFFF
: ROM开始的16kb$C000-$FFFF
: ROM最后的16kbMapper接口
就目前而且, 只需要一个重置接口:
BANK
BANK是每个Mapper载入的单位, 在某种意义上也可以称为window. 根据内存布局, 可以把根据地址划分为每8KB一个BANK, 一共8个区块:
0: [$0000, $2000)
系统主内存1: [$2000, $4000)
PPU 寄存器2: [$4000, $6000)
pAPU寄存器以及扩展区域3: [$6000, $8000)
存档用SRAM区也就是:
Mapper000 - Reset
根据所述内容, Mapper000可以实现为:
这里使用到了C99的inline, 和C++的inline略有区别. 还有就是, 利用位操作减少分支判断也是编码技巧之一.
地址空间读写
根据BANK, 就可以非常简单地实现读:
和写:
还没实现的用断言即可. 写入也是同样的. 不过既然是PRG-ROM, 那么写入加个断言好了.
输出各个中断向量
可以看出RESET是跳转$C004.
项目地址Github-StepFC-Step1
作业
REF