Files
player/Project/Src/NES/nes_main.c
2025-07-10 11:30:57 +08:00

448 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//////////////////////////////////////////////////////////////////////////////////
// 本程序移植自网友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;
}