448 lines
12 KiB
C
448 lines
12 KiB
C
//////////////////////////////////////////////////////////////////////////////////
|
||
// 本程序移植自网友ye781205的NES模拟器工程
|
||
// ALIENTEK STM32F407开发板
|
||
// NES主函数 代码
|
||
// 正点原子@ALIENTEK
|
||
// 技术论坛:www.openedv.com
|
||
// 创建日期:2014/7/1
|
||
// 版本:V1.0
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
|
||
#include "nes_main.h"
|
||
#include "dac.h"
|
||
#include "main.h"
|
||
#include "mymem.h"
|
||
#include "nes_apu.h"
|
||
#include "nes_mapper.h"
|
||
#include "nes_ppu.h"
|
||
#include "stm32f4xx.h"
|
||
|
||
#include "ff.h"
|
||
#include "string.h"
|
||
|
||
uint8_t nes_frame_cnt; // nes帧计数器
|
||
int MapperNo; // map编号
|
||
int NES_scanline; // nes扫描线
|
||
int VROM_1K_SIZE;
|
||
int VROM_8K_SIZE;
|
||
|
||
uint8_t PADdata; // 手柄1键值 [7:0]右7 左6 下5 上4 Start3 Select2 B1 A0
|
||
uint8_t PADdata1; // 手柄2键值 [7:0]右7 左6 下5 上4 Start3 Select2 B1 A0
|
||
uint8_t *NES_RAM; // 保持1024字节对齐
|
||
uint8_t *NES_SRAM;
|
||
NES_header *RomHeader; // rom文件头
|
||
MAPPER *NES_Mapper;
|
||
MapperCommRes *MAPx;
|
||
|
||
uint8_t *spr_ram; // 精灵RAM,256字节
|
||
ppu_data *ppu; // ppu指针
|
||
uint8_t *VROM_banks;
|
||
uint8_t *VROM_tiles;
|
||
|
||
apu_t *apu; // apu指针
|
||
uint16_t *wave_buffers;
|
||
uint16_t *i2sbuf1; // 音频缓冲帧,占用内存数 367*4 字节@22050Hz
|
||
uint16_t *i2sbuf2; // 音频缓冲帧,占用内存数 367*4 字节@22050Hz
|
||
|
||
uint8_t *romfile; // nes文件指针,指向整个nes文件的起始地址.
|
||
//////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// 记录nes是否需要跳出
|
||
static uint8_t g_nes_break = 0;
|
||
|
||
// 加载ROM
|
||
// 返回值:0,成功
|
||
// 1,内存错误
|
||
// 3,map错误
|
||
uint8_t nes_load_rom(void) {
|
||
uint8_t *p;
|
||
uint8_t i;
|
||
uint8_t res = 0;
|
||
p = (uint8_t *)romfile;
|
||
if (strncmp((char *)p, "NES", 3) == 0) {
|
||
RomHeader->ctrl_z = p[3];
|
||
RomHeader->num_16k_rom_banks = p[4];
|
||
RomHeader->num_8k_vrom_banks = p[5];
|
||
RomHeader->flags_1 = p[6];
|
||
RomHeader->flags_2 = p[7];
|
||
if (RomHeader->flags_1 & 0x04)
|
||
p += 512; // 有512字节的trainer:
|
||
if (RomHeader->num_8k_vrom_banks > 0) // 存在VROM,进行预解码
|
||
{
|
||
VROM_banks = p + 16 + (RomHeader->num_16k_rom_banks * 0x4000);
|
||
#if NES_RAM_SPEED == 1 // 1:内存占用小 0:速度快
|
||
VROM_tiles = VROM_banks;
|
||
#else
|
||
VROM_tiles = mymalloc(RomHeader->num_8k_vrom_banks * 8 *
|
||
1024); // 这里可能申请多达1MB内存!!!
|
||
if (VROM_tiles == 0)
|
||
VROM_tiles =
|
||
VROM_banks; // 内存不够用的情况下,尝试VROM_titles与VROM_banks共用内存
|
||
compile(RomHeader->num_8k_vrom_banks * 8 * 1024 / 16, VROM_banks,
|
||
VROM_tiles);
|
||
#endif
|
||
} else {
|
||
VROM_banks = mymalloc(8 * 1024);
|
||
VROM_tiles = mymalloc(8 * 1024);
|
||
if (!VROM_banks || !VROM_tiles)
|
||
res = 1;
|
||
}
|
||
VROM_1K_SIZE = RomHeader->num_8k_vrom_banks * 8;
|
||
VROM_8K_SIZE = RomHeader->num_8k_vrom_banks;
|
||
MapperNo = (RomHeader->flags_1 >> 4) | (RomHeader->flags_2 & 0xf0);
|
||
if (RomHeader->flags_2 & 0x0E)
|
||
MapperNo = RomHeader->flags_1 >> 4; // 忽略高四位,如果头看起来很糟糕
|
||
// printf("use map:%d\r\n",MapperNo);
|
||
for (i = 0; i < 255; i++) // 查找支持的Mapper号
|
||
{
|
||
if (MapTab[i] == MapperNo)
|
||
break;
|
||
if (MapTab[i] == -1)
|
||
res = 3;
|
||
}
|
||
if (res == 0) {
|
||
switch (MapperNo) {
|
||
case 1:
|
||
MAP1 = mymalloc(sizeof(Mapper1Res));
|
||
if (!MAP1)
|
||
res = 1;
|
||
break;
|
||
case 4:
|
||
case 6:
|
||
case 16:
|
||
case 17:
|
||
case 18:
|
||
case 19:
|
||
case 21:
|
||
case 23:
|
||
case 24:
|
||
case 25:
|
||
case 64:
|
||
case 65:
|
||
case 67:
|
||
case 69:
|
||
case 85:
|
||
case 189:
|
||
MAPx = mymalloc(sizeof(MapperCommRes));
|
||
if (!MAPx)
|
||
res = 1;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return res; // 返回执行结果
|
||
}
|
||
// 变量NES_RAM申请内存时的辅助指针
|
||
void *nes_ram_alignment;
|
||
// 释放内存
|
||
void nes_sram_free(void) {
|
||
// myfree(NES_RAM);
|
||
myfree(nes_ram_alignment);
|
||
myfree(NES_SRAM);
|
||
myfree(RomHeader);
|
||
myfree(NES_Mapper);
|
||
myfree(spr_ram);
|
||
myfree(ppu);
|
||
myfree(apu);
|
||
myfree(wave_buffers);
|
||
myfree(i2sbuf1);
|
||
myfree(i2sbuf2);
|
||
myfree(romfile);
|
||
if ((VROM_tiles != VROM_banks) && VROM_banks &&
|
||
VROM_tiles) // 如果分别为VROM_banks和VROM_tiles申请了内存,则释放
|
||
{
|
||
myfree(VROM_banks);
|
||
myfree(VROM_tiles);
|
||
}
|
||
switch (MapperNo) // 释放map内存
|
||
{
|
||
case 1: // 释放内存
|
||
myfree(MAP1);
|
||
break;
|
||
case 4:
|
||
case 6:
|
||
case 16:
|
||
case 17:
|
||
case 18:
|
||
case 19:
|
||
case 21:
|
||
case 23:
|
||
case 24:
|
||
case 25:
|
||
case 64:
|
||
case 65:
|
||
case 67:
|
||
case 69:
|
||
case 85:
|
||
case 189:
|
||
myfree(MAPx);
|
||
break; // 释放内存
|
||
default:
|
||
break;
|
||
}
|
||
NES_RAM = 0;
|
||
NES_SRAM = 0;
|
||
RomHeader = 0;
|
||
NES_Mapper = 0;
|
||
spr_ram = 0;
|
||
ppu = 0;
|
||
apu = 0;
|
||
wave_buffers = 0;
|
||
i2sbuf1 = 0;
|
||
i2sbuf2 = 0;
|
||
romfile = 0;
|
||
VROM_banks = 0;
|
||
VROM_tiles = 0;
|
||
MAP1 = 0;
|
||
MAPx = 0;
|
||
}
|
||
// 为NES运行申请内存
|
||
// romsize:nes文件大小
|
||
// 返回值:0,申请成功
|
||
// 1,申请失败
|
||
uint8_t nes_sram_malloc(uint32_t romsize) {
|
||
uint16_t i = 0;
|
||
|
||
// NES_RAM需要0x800大小的内存,这里多申请0x400的内存,用于1024字节对齐
|
||
nes_ram_alignment = mymalloc(0x800 + 0x400);
|
||
NES_RAM = (void *)(((uint32_t)nes_ram_alignment + 0x400) & 0xfffffc00);
|
||
|
||
NES_SRAM = mymalloc(0X2000);
|
||
RomHeader = mymalloc(sizeof(NES_header));
|
||
NES_Mapper = mymalloc(sizeof(MAPPER));
|
||
spr_ram = mymalloc(0X100);
|
||
ppu = mymalloc(sizeof(ppu_data));
|
||
apu = mymalloc(sizeof(apu_t)); // sizeof(apu_t)= 12588
|
||
wave_buffers = mymalloc(APU_PCMBUF_SIZE * 2);
|
||
i2sbuf1 = mymalloc(APU_PCMBUF_SIZE * 4 + 10);
|
||
i2sbuf2 = mymalloc(APU_PCMBUF_SIZE * 4 + 10);
|
||
romfile = mymalloc(romsize); // 申请游戏rom空间,等于nes文件大小
|
||
if (i == 64 || !NES_RAM || !NES_SRAM || !RomHeader || !NES_Mapper ||
|
||
!spr_ram || !ppu || !apu || !wave_buffers || !i2sbuf1 || !i2sbuf2 ||
|
||
!romfile) {
|
||
nes_sram_free();
|
||
return 1;
|
||
}
|
||
memset(NES_SRAM, 0, 0X2000); // 清零
|
||
memset(RomHeader, 0, sizeof(NES_header)); // 清零
|
||
memset(NES_Mapper, 0, sizeof(MAPPER)); // 清零
|
||
memset(spr_ram, 0, 0X100); // 清零
|
||
memset(ppu, 0, sizeof(ppu_data)); // 清零
|
||
memset(apu, 0, sizeof(apu_t)); // 清零
|
||
memset(wave_buffers, 0, APU_PCMBUF_SIZE * 2); // 清零
|
||
memset(i2sbuf1, 0, APU_PCMBUF_SIZE * 4 + 10); // 清零
|
||
memset(i2sbuf2, 0, APU_PCMBUF_SIZE * 4 + 10); // 清零
|
||
memset(romfile, 0, romsize); // 清零
|
||
return 0;
|
||
}
|
||
|
||
int nes_xoff = 0; // 显示在x轴方向的偏移量(实际显示宽度=256-2*nes_xoff)
|
||
int nes_yoff = 0;
|
||
|
||
// 开始nes游戏
|
||
// pname:nes游戏路径
|
||
// 返回值:
|
||
// 0,正常退出
|
||
// 1,内存错误
|
||
// 2,文件错误
|
||
// 3,不支持的map
|
||
uint8_t nes_load(uint8_t *pname, void *lcd_addr, int x, int y) {
|
||
nes_xoff = x;
|
||
nes_yoff = y;
|
||
FIL *file;
|
||
FILINFO *file_info;
|
||
UINT br;
|
||
uint8_t res = 0;
|
||
g_nes_break = 0;
|
||
file = mymalloc(sizeof(FIL));
|
||
file_info = mymalloc(sizeof(FILINFO));
|
||
if (file == 0)
|
||
return 1; // 内存申请失败.
|
||
|
||
res = f_stat((char *)pname, file_info);
|
||
if (res != FR_OK) // 打开文件失败
|
||
{
|
||
myfree(file);
|
||
myfree(file_info);
|
||
return 2;
|
||
}
|
||
res = nes_sram_malloc(file_info->fsize); // 申请内存
|
||
if (res == 0) {
|
||
f_open(file, (char *)pname, FA_READ);
|
||
f_read(file, romfile, file_info->fsize, &br); // 读取nes文件
|
||
res = nes_load_rom(); // 加载ROM
|
||
if (res == 0) {
|
||
Mapper_Init(); // map初始化
|
||
cpu6502_init(); // 初始化6502,并复位
|
||
PPU_reset(lcd_addr); // ppu复位
|
||
apu_init(); // apu初始化
|
||
nes_sound_open(0, APU_SAMPLE_RATE); // 初始化播放设备
|
||
nes_emulate_frame(); // 进入NES模拟器主循环
|
||
nes_sound_close(); // 关闭声音输出
|
||
}
|
||
}
|
||
f_close(file);
|
||
myfree(file); // 释放内存
|
||
myfree(file_info);
|
||
nes_sram_free(); // 释放内存
|
||
return res;
|
||
}
|
||
|
||
// 运行已经加载在内存中的游戏
|
||
uint8_t nes_start(uint8_t *file, int fileSize, void *lcd_addr, int x, int y) {
|
||
nes_xoff = x;
|
||
nes_yoff = y;
|
||
uint8_t res = 0;
|
||
g_nes_break = 0;
|
||
res = nes_sram_malloc(fileSize); // 申请内存
|
||
|
||
if (res == 0) {
|
||
mymemcpy(romfile, file, fileSize); // 复制一份在运行空间
|
||
res = nes_load_rom(); // 加载ROM
|
||
if (res == 0) {
|
||
Mapper_Init(); // map初始化
|
||
cpu6502_init(); // 初始化6502,并复位
|
||
PPU_reset(lcd_addr); // ppu复位
|
||
apu_init(); // apu初始化
|
||
nes_sound_open(0, APU_SAMPLE_RATE); // 初始化播放设备
|
||
nes_emulate_frame(); // 进入NES模拟器主循环
|
||
nes_sound_close(); // 关闭声音输出
|
||
}
|
||
}
|
||
nes_sram_free(); // 释放内存
|
||
return res;
|
||
}
|
||
|
||
// 设置游戏显示窗口
|
||
void nes_set_window(void) {}
|
||
|
||
// 读取游戏手柄数据
|
||
void nes_get_gamepadval(void) {
|
||
#include "usart.h"
|
||
PADdata = USART3_GetKey();
|
||
PADdata1 = 0;
|
||
}
|
||
|
||
// nes模拟器主循环
|
||
void nes_emulate_frame(void) {
|
||
uint8_t nes_frame;
|
||
// TIM3_Int_Init(10000-1,8400-1);//启动TIM3 ,1s中断一次
|
||
nes_set_window(); // 设置窗口
|
||
while (1) {
|
||
// LINES 0-239
|
||
PPU_start_frame();
|
||
// LCD_SwitchLayerBuff();//切换帧
|
||
for (NES_scanline = 0; NES_scanline < 240; NES_scanline++) {
|
||
run6502(113 * 256);
|
||
NES_Mapper->HSync(NES_scanline);
|
||
// 扫描一行
|
||
if (nes_frame == 0)
|
||
scanline_draw(NES_scanline);
|
||
else
|
||
do_scanline_and_dont_draw(NES_scanline);
|
||
}
|
||
// LCD_ExitLayerBuff ();
|
||
|
||
NES_scanline = 240;
|
||
run6502(113 * 256); // 运行1线
|
||
NES_Mapper->HSync(NES_scanline);
|
||
start_vblank();
|
||
if (NMI_enabled()) {
|
||
cpunmi = 1;
|
||
run6502(7 * 256); // 运行中断
|
||
}
|
||
NES_Mapper->VSync();
|
||
// LINES 242-261
|
||
for (NES_scanline = 241; NES_scanline < 262; NES_scanline++) {
|
||
run6502(113 * 256);
|
||
NES_Mapper->HSync(NES_scanline);
|
||
}
|
||
end_vblank();
|
||
nes_get_gamepadval(); // 每3帧查询一次USB
|
||
apu_soundoutput(); // 输出游戏声音
|
||
nes_frame_cnt++;
|
||
nes_frame++;
|
||
if (nes_frame > NES_SKIP_FRAME)
|
||
nes_frame = 0; // 跳帧
|
||
uint8_t nesBreak(void);
|
||
if (nesBreak())
|
||
break;
|
||
if (Touch_GetState()->x[1] != 0)
|
||
break;
|
||
}
|
||
// TIM3->CR1&=~(1<<0);//关闭定时器3
|
||
}
|
||
|
||
uint8_t nesBreak(void) {
|
||
uint8_t t = 0;
|
||
if ((PADdata & 0x0c) == 0x0c) {
|
||
g_nes_break = 0x0c;
|
||
}
|
||
if ((g_nes_break) && (t = USART3_GetKeyPressed(), t & 0x0c)) {
|
||
g_nes_break &= ~t;
|
||
if (g_nes_break == 0)
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// 在6502.s里面被调用
|
||
void debug_6502(uint16_t reg0, uint8_t reg1) {
|
||
// printf("6502 error:%x,%d\r\n",reg0,reg1);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
// nes,音频输出支持部分
|
||
|
||
static vu8 g_buff_Invalid = 0; // 数据无效时要重新填充数据
|
||
static vu8 g_buff_useing; // 当前DMA在使用哪个缓冲区
|
||
static DAC_UserStruct g_dac;
|
||
|
||
static void dma_tx_callback(DAC_UserStruct *dac) {
|
||
uint16_t i;
|
||
g_buff_useing = dac->buff_useing;
|
||
g_buff_Invalid = 1;
|
||
}
|
||
|
||
// NES打开音频输出
|
||
int nes_sound_open(int samples_per_sync, int sample_rate) {
|
||
|
||
// 初始化DAC
|
||
g_dac.buff1 = (uint32_t *)i2sbuf1;
|
||
g_dac.buff2 = (uint32_t *)i2sbuf2;
|
||
g_dac.buff_size = APU_PCMBUF_SIZE * 4;
|
||
g_dac.rate = RATE22050;
|
||
g_dac.call_back = dma_tx_callback;
|
||
DAC_NormalInit(&g_dac);
|
||
return 1;
|
||
}
|
||
|
||
// NES关闭音频输出
|
||
void nes_sound_close(void) { DAC_NormalDeInit(&g_dac); }
|
||
|
||
void nes_apu_fill_buffer(int samples, uint16_t *wavebuf) {
|
||
if (DAC_GetDacHander() != &g_dac)
|
||
return;
|
||
uint16_t i;
|
||
uint16_t *p;
|
||
while (g_buff_Invalid == 0) // 等待传输完成
|
||
{
|
||
rt_thread_delay(5);
|
||
};
|
||
if (g_buff_useing == 0) {
|
||
p = (uint16_t *)i2sbuf2;
|
||
} else {
|
||
p = (uint16_t *)i2sbuf1;
|
||
}
|
||
{
|
||
for (i = 0; i < APU_PCMBUF_SIZE; i++) {
|
||
p[2 * i] = ((wavebuf[i] + 0x8000) >> 4);
|
||
p[2 * i + 1] = p[2 * i];
|
||
}
|
||
}
|
||
// 填充了数据之后设置为有效
|
||
g_buff_Invalid = 0;
|
||
}
|