Giter Club home page Giter Club logo

Comments (5)

qiaobaaa avatar qiaobaaa commented on September 15, 2024

main.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

#include "nes.h"
#include "emulator.h"

int main(void)
{
//注意文件路径格式,不然会出现找不到文件
char game_name[] = "D:\mario.nes";
int tmp;
tmp = nes_loadrom(game_name);
if (tmp != 0) {
printf("NES rom load failed, error code: %d\n", tmp);
exit(tmp);
}

return 0;

}

from response3.

qiaobaaa avatar qiaobaaa commented on September 15, 2024

nes.h
#pragma once
#ifndef NES_H
#define NES_H

#include <stdint.h>

#define FPS 60
#define WIDTH 256
#define HEIGHT 240

#define FILE_NOT_EXIST 1
#define FILE_HEADER_READ_FAILED 2
#define NOT_IS_NES_FORMAT 3
#define MEMORY_ALLOCATE_FAILED 4
#define PRG_ROM_LOAD_FAILED 5
#define CHR_ROM_LOAD_FAILED 6

int nes_loadrom(char* rom);

#endif

from response3.

qiaobaaa avatar qiaobaaa commented on September 15, 2024

nes.c
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include "nes.h"
#include "cartridge.h"

cartridge_t cartridge;

int nes_loadrom(char* rom)
{
FILE* file;

file = fopen(rom, "rb+");
if (file == NULL)
{
	return FILE_NOT_EXIST;
}

if (fread(cartridge.header,sizeof(uint8_t),16,file)!=16)
{
	return FILE_HEADER_READ_FAILED;
}
else if (cartridge.header[0] != 0x4e || cartridge.header[1] != 0x45 || cartridge.header[2] != 0x53 || cartridge.header[3] != 0x1a)
{
	return NOT_IS_NES_FORMAT;
}
else
{

	/* 读取 ROM 信息 */
	cartridge.prg_rom_size = 16 * 1024 * cartridge.header[4];
	cartridge.chr_rom_size = 8 * 1024 * cartridge.header[5];
	cartridge.prg_ram_size = 8 * 1024 * cartridge.header[8];
	if (cartridge.chr_rom_size == 0)
	{
		cartridge.chr_rom_size = 8 * 1024;
	}
	if (cartridge.prg_ram_size == 0)
	{
		cartridge.prg_ram_size = 8 * 1024;
	}
}




return 0;

}

from response3.

qiaobaaa avatar qiaobaaa commented on September 15, 2024

cartridge.h
#pragma once
#include <stdint.h>

typedef struct Cartridge
{
/**
* NES ROM 的 header
* 一共有 16 字节, 每个字节定义如下:
* 0-3 字节: 固定值, $4E $45 $53 $1A (前三字节即为 "NES")
* 4: PRG ROM 大小, 单位为 16KB 的倍数
* 5: CHR ROM 大小, 单位为 8KB 的倍数, 0 代表 8KB
* 6: Flag 6
* 7: Flag 7
* 8: PRG RAM 大小, 单位为 8KB 的倍数, 0 代表 8KB
* 9: Flag 9
* 10: Flag 10
* 11-15: 统一用 0 填充
* 对于任天堂第一方游戏, 无需考虑 Flag 6, Flag 7, Flag 9, Flag 10
*
* 参考资料: http://ewind.us/2015/nes-emu-3-rom-assembly/
* http://wiki.nesdev.com/w/index.php/INES
/
//unsigned char uint8_t
uint8_t header[16];
int prg_rom_size; // PRG ROM 大小 (Byte)
int chr_rom_size; // CHR ROM 大小 (Byte)
int prg_ram_size; // PRG RAM 大小 (Byte)
uint8_t
prg_rom;
uint8_t* chr_rom;
}cartridge_t;

from response3.

qiaobaaa avatar qiaobaaa commented on September 15, 2024

//CPU复位
void cpu_resrt() {
cpu.reg_sp -= 3;
cpu.reg_p |= FLAG_INTERRUPT;
memory_write_byte(0x4015, 0);
cpu.reg_pc = memory_read_word(0xfffc);
}

//检查并设置zero flag与negative flag
void cpu_checknz(uint8_t n) {
if ((n >> 7) & 1)
{
cpu.reg_p |= FLAG_NEGATIVE;
}
else
{
cpu.reg_p &= ~FLAG_NEGATIVE;
}
if (n == 0)
{
cpu.reg_p = FLAG_ZERO;
}
else
{
cpu.reg_p &= ~FLAG_ZERO;
}
}

//修改flag
void cpu_modify_flag(uint8_t flag, int value) {
if (value)
{
cpu.reg_p |= flag;
}
else
{
cpu.reg_p &= ~flag;
}
}

//栈操作
void cpu_stack_push_byte(uint8_t data) {
memory_write_byte(0x100 + cpu.reg_sp, data);
cpu.reg_sp -= 1;
}
void cpu_stack_push_word(uint16_t data) {
memory_write_word(0x0ff + cpu.reg_sp, data);
cpu.reg_sp -= 2;
}
uint8_t cpu_stack_pop_byte() {
cpu.reg_sp += 1;
return memory_read_byte(0x100 + cpu.reg_sp);
}
uint16_t cpu_stack_pop_word() {
cpu.reg_sp += 2;
return memory_read_word(0x0ff + cpu.reg_sp);
}

/* 存储 CPU 经过寻址后得到的地址和该地址对应的值 */
uint16_t op_address;
uint8_t op_value;
uint8_t additional_cycles; // 对于某些寻址方式, 如果跨页访问, 需要多使用一个 CPU Cycle

/* implied (1 字节)

  • 隐含寻址. 与累加器寻址类似, 不过指令所需的操作数不在 A 中, 而在其他寄存器中
    */

void cpu_addressing_implied() {
additional_cycles = 0;
}

/* accumulator (1 字节)

  • 缩写: A
  • 累加器寻址. 指令所需操作数在累加器 A 中, 无需操作数
    */
    void cpu_addressing_accumulator() {
    additional_cycles = 0;
    }

/* immediate (2 字节)

  • 缩写: #v
  • 立即数寻址. 后面跟一个 8 位的立即数
    */
    void cpu_addressing_immediate() {
    op_value = memory_read_byte(cpu.reg_pc);
    cpu.reg_pc++;
    additional_cycles = 0;
    }

/* zeropage (2 字节)

  • 缩写: d
  • 零页寻址. 地址 00 ~ FF 为零页地址
    */
    void cpu_addressing_zerppage() {
    op_address = memory_read_byte(cpu.reg_pc);
    op_value = memory_read_byte(op_address);
    cpu.reg_pc++;
    additional_cycles = 0;
    }

/* zeropage, X-indexed (2 字节)

  • 缩写: d,x
  • 使用寄存器 X 的零页寻址. 在零页寻址的基础上, 地址与 X 中的值相加
    */
    void cpu_addressing_zeropage_x() {
    op_address = (memory_read_byte(cpu.reg_pc) + cpu.reg_x) & 0xff;
    op_value = memory_read_byte(op_address);
    cpu.reg_pc++;
    additional_cycles = 0;
    }

/* zeropage, Y-indexed (2 字节)

  • 缩写: d,y
  • 使用寄存器 Y 的零页寻址. 在零页寻址的基础上, 地址与 Y 中的值相加
    */
    void cpu_addressing_zeropage_y() {
    op_address = (memory_read_byte(cpu.reg_pc) + cpu.reg_y) & 0xff;
    op_value = memory_read_byte(op_address);
    cpu.reg_pc++;
    additional_cycles = 0;
    }

/* absolute (3 字节)

  • 缩写: a
  • 直接寻址. 操作数即为内存地址, 低位在前, 高位在后
    */
    void cpu_addressing_absolute() {
    op_address = memory_read_word(cpu.reg_pc);
    op_value = memory_read_byte(op_address);
    cpu.reg_pc += 2;
    additional_cycles = 0;
    }

/* absolute, X-indexed (3 字节)

  • 缩写: a,x
  • 使用寄存器 X 的直接变址寻址. 16 位地址做为基地址, 与寄存器 X 的内容相加
    */
    void cpu_addressing_absolute_x() {
    op_address = memory_read_word(cpu.reg_pc) + cpu.reg_x;
    op_value = memory_read_byte(op_address);
    cpu.reg_pc += 2;
    if ((op_address >> 8) != (cpu.reg_pc >> 8))
    {
    additional_cycles = 1;
    }
    else
    {
    additional_cycles = 0;
    }
    }

/* absolute, Y-indexed (3 字节)

  • 缩写: a,y
  • 使用寄存器 Y 的直接变址寻址. 16 位地址做为基地址, 与寄存器 Y 的内容相加
    */
    void cpu_addressing_absolute_y() {
    op_address = (memory_read_word(cpu.reg_pc) + cpu.reg_y) & 0xffff;
    op_value = memory_read_byte(op_address);
    cpu.reg_pc += 2;
    if ((op_address >> 8) != (cpu.reg_pc >> 8))
    {
    additional_cycles = 1;
    }
    else
    {
    additional_cycles = 0;
    }
    }

/* relative (2 字节)

  • 缩写: label
  • 相对寻址. 用于条件转移指令. 指令第二字节为偏移量, 可正可负.
    */
    void cpu_addressing_relative() {
    op_address = memory_read_byte(cpu.reg_pc);
    cpu.reg_pc++;
    if (op_address & 0x80)
    {
    op_address -= 0x100;
    }
    op_address += cpu.reg_pc;
    if ((op_address >> 8) != (cpu.reg_pc >> 8))
    {
    additional_cycles = 1;
    }
    else
    {
    additional_cycles = 0;
    }
    }

/* indirect (3 字节)

  • 缩写: (a)

  • 间接寻址. 对应地址内存单元中的数做为地址.
    */
    void cpu_addressing_indirect() {
    uint16_t arg_addr = memory_read_word(cpu.reg_pc);

    /* 据说这是 6502 的 Bug */
    if ((arg_addr & 0xff) == 0xff) {
    // 有 Bug 的情况下
    op_address = (memory_read_byte(arg_addr & 0xff00) << 8) + memory_read_byte(arg_addr);
    }
    else {
    // 正常情况下
    op_address = memory_read_word(arg_addr);
    }
    cpu.reg_pc += 2;
    additional_cycles = 0;
    }

/* indirect, X-indexed (2 字节)

  • 缩写: (d,x)
  • 先变址 X 后间接寻址. 以 X 做为变址, 与基地址相加, 然后间接寻址
    */
    void cpu_addressing_indirect_x() {
    uint8_t arg_addr = memory_read_byte(cpu.reg_pc);
    op_address = (memory_read_byte((arg_addr + cpu.reg_x + 1) & 0xff) << 8) | memory_read_byte((arg_addr + cpu.reg_x) & 0xff);
    op_value = memory_read_byte(op_address);
    cpu.reg_pc++;
    additional_cycles = 0;
    }

/* indirect, Y-indexed (2 字节)

  • 缩写: (d),y
  • 后变址 Y 间接寻址. 对操作数中的零页地址先做一次间接寻址, 得到 16 位地址, 再与 Y 相加, 对相加后得到的地址进行直接寻址.
    */
    void cpu_addressing_indirect_y() {
    uint8_t arg_addr = memory_read_byte(cpu.reg_pc);
    op_address = (((memory_read_byte((arg_addr + 1) & 0xff) << 8) | memory_read_byte(arg_addr)) + cpu.reg_y) & 0xffff;
    op_value = memory_read_byte(op_address);
    cpu.reg_pc++;
    if ((op_address >> 8) != (cpu.reg_pc >> 8))
    {
    additional_cycles = 1;
    }
    else
    {
    additional_cycles = 0;
    }
    }

//56种常规指令集
//ALU: 算术逻辑单元

void cpu_ora() {
cpu.reg_a |= op_value;
cpu_checknz(cpu.reg_a);
}

from response3.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.