为方便讲解,代码我直接放到最后一页
对于nes_main.c我们来看u8 nes_main(void)第三行代码
u8 nes_main(void)
{
u16 offset = 0;
u8 res;
NesHeader *neshreader = (NesHeader *) rom_file;
...................................
没错,这行代码指向的就是你的游戏文件数组,即在第一篇讲的nes_rom.h里面的游戏数组,
即你想玩什么游戏,吧该指针指向那里就行了
往下的代码就没什么可以讲的了,主要就是验证你的数组是不是真的是NES文件,
就是这一部分
u16 offset = 0;
u8 res;
NesHeader *neshreader = (NesHeader *) rom_file;
//由于NES文件头类型以0x1A结束,(0x1a是Ctrl+Z,这是模拟文件结束的符号)
//所以使用strcncmp比较前3个字节。
if (strncmp(neshreader->filetype, "NES", 3) != 0)
{
printf("\r\n文件未加载或文件类型错误, NES模拟器退出。");
return 1;
}
else
{
printf("\r\n文件加载完成。");
printf("\r\n 16kB ROM 的数目: %d", neshreader->romnum);
printf("\r\n 8kB VROM 的数目: %d", neshreader->vromnum);
if ((neshreader->romfeature & 0x01) == 0)
{
printf("\r\n 水平镜像");
}
else
{
printf("\r\n 垂直镜像");
}
if ((neshreader->romfeature & 0x02) == 0)
{
printf("\r\n 无记忆SRAM");
}
else
{
printf("\r\n 有记忆SRAM");
}
if ((neshreader->romfeature & 0x04) == 0)
{
printf("\r\n 无512字节的trainer($7000-$71FF)");
}
else
{
offset = 512;
printf("\r\n 有512字节的trainer(ROM格式暂不支持)");
}
if ((neshreader->romfeature & 0x08) == 0)
{
printf("\r\n 2屏幕VRAM布局");
}
else
{
printf("\r\n 4屏幕VRAM布局(暂不支持)");
}
printf("\r\n iNES Mapper Numbers: %d", (neshreader->rommappernum & 0xF0) | (neshreader->romfeature >> 4));
}
验证完之后,看下面这部分代码
printf("\r\n 开始申请内存\n");
res = nes_mem_creat(); //申请内存
if (res == 0) //申请成功了.则运行游戏
{
printf("\r\n 开始申请成功\n");
printf("\r\n 初始化按键\n");
printf("\r\n 初始化屏幕\n");
printf("\r\n 初始化ppu\n");
printf("\r\n 初始化joypad\n");
printf("\r\n 初始化6502\n");
printf("\r\n 无限循环执行游戏\n");
printf("\r\n 按下退出游戏键\n");
}
else printf("\r\n 开始申请失败\n");
nes_mem_delete();//释放内存
return res;
}
验证完之后,就是开始内存申请了,即开辟为游戏运行的内存 ,然后初始化各类设备,这里面的内容第三篇开始讲解
res = nes_mem_creat(); //申请内存
接下来,我们来看与内存开辟的相关代码
//PPU使用
u8 *NameTable; //2K的变量
u16 *Buffer_scanline; //行显示缓存,上下标越界最大为7,显示区 7 ~ 263 0~7 263~270 为防止溢出区
//CPU使用
u8 *ram6502; //RAM 2K字节,由malloc申请
portMUX_TYPE isr_nes_creat = portMUX_INITIALIZER_UNLOCKED;
//开辟nes运行所需的RAM.
//返回值:0,成功;
// 其他,错误代码.
u8 nes_mem_creat(void)
{
portENTER_CRITICAL_ISR(&isr_nes_creat);
ram6502 = (u8 *)malloc(2048); //申请2K内存
if (ram6502 == NULL)return 1; //申请失败
NameTable = (u8 *)malloc(2048); //申请2K内存
if (NameTable == NULL)return 2;
Buffer_scanline = (u16 *)malloc((8 + 256 + 8) * 2);
if (Buffer_scanline == NULL)return 3;
portEXIT_CRITICAL_ISR(&isr_nes_creat);
return 0;
}
可以看到前面几行定义的变量
//PPU使用
u8 *NameTable; //2K的变量
u16 *Buffer_scanline; //行显示缓存,上下标越界最大为7,显示区 7 ~ 263 0~7 263~270 为防止溢出区
//CPU使用
u8 *ram6502; //RAM 2K字节,由malloc申请
这几个变量是测试本篇用的,真正定义的时候并不在这个文件里面而在ppu.c和CPU(即6502.c)文件里面定义。
接下来可能有人会不懂,下面这行代码是啥意思,当你用的时候这段也可以直接删除,对程序没有影响
portMUX_TYPE isr_nes_creat = portMUX_INITIALIZER_UNLOCKED;
这个是定义一个代码临界段变量用的 ,可能你还是不懂,但是看到下面的函数你可能就懂了
//开辟nes运行所需的RAM.
//返回值:0,成功;
// 其他,错误代码.
u8 nes_mem_creat(void)
{
portENTER_CRITICAL_ISR(&isr_nes_creat); //内存申请不可被打断
ram6502 = (u8 *)malloc(2048); //申请2K内存
if (ram6502 == NULL)
return 1; //申请失败
NameTable = (u8 *)malloc(2048); //申请2K内存
if (NameTable == NULL)
return 2;
Buffer_scanline = (u16 *)malloc((8 + 256 + 8) * 2);
if (Buffer_scanline == NULL)
return 3;
portEXIT_CRITICAL_ISR(&isr_nes_creat); //内存申请可以被打断
return 0;
}
通过引用 portENTER_CRITICAL_ISR(&isr_nes_creat); 函数
可以使在申请内存操作时,CPU不会被其他中断操作给打断,当然如果你使用的不是ESP32_Arduino平台开发的话,这下面这两句不写也是可以的。
portENTER_CRITICAL_ISR(&isr_nes_creat); //内存申请不可被打断
portEXIT_CRITICAL_ISR(&isr_nes_creat); //内存申请可以被打断
至此,内存申请测试就此完结,下面贴出nes_main.c,内存申请测试代码,继续学习,请看第三篇
ppu文件的讲解
#include "malloc.h"
#include "nes_main.h"
//返回值:0,执行OK;
// 其他,错误代码
u8 nes_main(void)
{
u16 offset = 0;
u8 res;
NesHeader *neshreader = (NesHeader *) rom_file;
//由于NES文件头类型以0x1A结束,(0x1a是Ctrl+Z,这是模拟文件结束的符号)
//所以使用strcncmp比较前3个字节。
if (strncmp(neshreader->filetype, "NES", 3) != 0)
{
printf("\r\n文件未加载或文件类型错误, NES模拟器退出。");
return 1;
}
else
{
printf("\r\n文件加载完成。");
printf("\r\n 16kB ROM 的数目: %d", neshreader->romnum);
printf("\r\n 8kB VROM 的数目: %d", neshreader->vromnum);
if ((neshreader->romfeature & 0x01) == 0)
{
printf("\r\n 水平镜像");
}
else
{
printf("\r\n 垂直镜像");
}
if ((neshreader->romfeature & 0x02) == 0)
{
printf("\r\n 无记忆SRAM");
}
else
{
printf("\r\n 有记忆SRAM");
}
if ((neshreader->romfeature & 0x04) == 0)
{
printf("\r\n 无512字节的trainer($7000-$71FF)");
}
else
{
offset = 512;
printf("\r\n 有512字节的trainer(ROM格式暂不支持)");
}
if ((neshreader->romfeature & 0x08) == 0)
{
printf("\r\n 2屏幕VRAM布局");
}
else
{
printf("\r\n 4屏幕VRAM布局(暂不支持)");
}
printf("\r\n iNES Mapper Numbers: %d", (neshreader->rommappernum & 0xF0) | (neshreader->romfeature >> 4));
}
printf("\r\n 开始申请内存\n");
res = nes_mem_creat(); //申请内存
if (res == 0) //申请成功了.则运行游戏
{
printf("\r\n 开始申请成功\n");
}
else printf("\r\n 开始申请失败\n");
nes_mem_delete();//释放内存
return res;
}
u8 *ram6502; //RAM 2K字节,由malloc申请
portMUX_TYPE isr_nes_creat = portMUX_INITIALIZER_UNLOCKED;
//开辟nes运行所需的RAM.
//返回值:0,成功;
// 其他,错误代码.
u8 nes_mem_creat(void)
{
portENTER_CRITICAL_ISR(&isr_nes_creat);
ram6502 = (u8 *)malloc(2048); //申请2K内存
if (ram6502 == NULL)return 1; //申请失败
NameTable = (u8 *)malloc(2048); //申请2K内存
if (NameTable == NULL)return 2;
Buffer_scanline = (u16 *)malloc((8 + 256 + 8) * 2);
if (Buffer_scanline == NULL)return 3;
portEXIT_CRITICAL_ISR(&isr_nes_creat);
return 0;
}
//删除nes运行时申请的RAM
void nes_mem_delete(void)
{
free(ram6502);
free(NameTable);
free(Buffer_scanline);
}