semiversus / jeg Goto Github PK
View Code? Open in Web Editor NEWNES emulator with focus at hardware abstraction, testability and performance
License: MIT License
NES emulator with focus at hardware abstraction, testability and performance
License: MIT License
With commit 4372a45 I started to rework the interface between emulator and ppu. At the moment the ppu is defined by a big structure containing all the internals states, references to the emulator and references to the actual video output.
When playing around with different ideas for ppu optimizations things started to get complicated so here is a proposal to solve this issue:
void *
reference is here to point to the internal ppu data structure. The emulator don't need to have any knowledge about it.nes.c
which is holding a lot of ppu centric functionality.The separation is work in progress. Missing is clean up of nes.c
and adapting ppu_pixel_based.c
(renamed to ppu_caching.c
). The same separation makes sense for APU and controller input as well.
What do you think? @GorgonMeducer
Here is an example of the definition and using of a function pointer in this project:
typedef uint_fast8_t (*cpu6502_read_func_t) (void *, uint_fast16_t hwAddress);
typedef struct cpu6502_t {
...
cpu6502_read_func_t read;
} cpu6502_t
Add "" to the cpu6502_read_func_t definition makes the definition of read not looking like a pointer. My suggestion is, remove the "" from the definition, so we can use the unified (and simple) syntax for all kinds of pointers:
//! we define a function prototype instead of defining a function pointer type
typedef uint_fast8_t cpu6502_read_func_t(void *, uint_fast16_t hwAddress);
typedef struct cpu6502_t {
...
cpu6502_read_func_t *read; //!< we define a pointer pointing to a prototype
} cpu6502_t
Hence, we also know:
This makes the C language more easy to use and understand.
In cpu6502_opcodes.h, the structure opcode_tbl_entry is defined improperly:
struct opcode_tbl_entry {
char *mnemonic;
operation_enum_t operation;
address_mode_enum_t address_mode;
int bytes;
int cycles;
int page_cross_cycles;
}opcode_tbl_entry ;
As this header file is included by multiple source files, linker ends up throw following errors:
.\Out\msp2_file_io_template.axf: Error: L6200E: Symbol opcode_tbl_entry multiply defined (by cpu6502_debug.o and cpu6502.o).
.\Out\msp2_file_io_template.axf: Error: L6200E: Symbol opcode_tbl_entry multiply defined (by cpu6502_opcodes.o and cpu6502.o).
It's safe to change it to:
struct opcode_tbl_entry {
char *mnemonic;
operation_enum_t operation;
address_mode_enum_t address_mode;
int bytes;
int cycles;
int page_cross_cycles;
};
In function fetch_sprite_pattern(ppu_t *ppu, int i, int row),
for (int j=0; j<8; j++) {
int p1, p2;
if ((attributes&0x40)==0x40) {
p1=(low_tile_byte&0x01);
p2=(high_tile_byte&0x01)<<1;
low_tile_byte>>=1;
high_tile_byte>>=1;
}
else {
p1=(low_tile_byte&0x80)>>7;
p2=(high_tile_byte&0x80)>>6;
low_tile_byte<<=1;
high_tile_byte<<=1;
}
data<<=4;
data|=((attributes&3)<<2)|p1|p2;
}
It's obvious that the result of the if-then clause is unchanged during the looping time, so do it 8 times would be a waster.
This is I suggested:
int p1, p2;
if (attributes&0x40) {
for (int j=0; j<8; j++) {
p1=(low_tile_byte&0x01);
p2=(high_tile_byte&0x01)<<1;
low_tile_byte>>=1;
high_tile_byte>>=1;
data<<=4;
data|=((attributes&3)<<2)|p1|p2;
}
} else {
for (int j=0; j<8; j++) {
p1=(low_tile_byte&0x80)>>7;
p2=(high_tile_byte&0x80)>>6;
low_tile_byte<<=1;
high_tile_byte<<=1;
data<<=4;
data|=((attributes&3)<<2)|p1|p2;
}
}
I have noticed that there are plenty of dummy readings in 6502 emulation? Are they really necessary? ( I guess they might be important if the target address belongs to PPU, as the dummy read might affect the latch).
Could we apply dirty-matrix technique by monitoring nametable, if there is no any x/y scrolling ?
So we can only draw the changed tiles.
I also think maybe we can maintain two nametable images, and using memory copy methods to cope with scrolling.
What do you think?
Hi Günther
I have cloned your re-write branch and ported the EmbeddedNES to this branch using ppu_caching.c and ppu_caching.h. I manage to load a ROM and run it on FVP but there is nothing rendered.
After several hours struggling, I found that ppu->ppumask never got updated. Since there are a lot of structural update, I wonder, did you validated that the ppu_caching was still working?
Cheers,
Gabriel
In order to support dirty-matrix technique, I suggest to use callback to replace the direct frame-buffering access:
Original:
ppu->video_frame_data[ppu->scanline*256+ppu->cycle-1]=ppu->palette[color];
Callback:
typedef void ppu_draw_pixel_t (uint_fast8_t y, uint_fast8_t x, uint_fast8_t color);
ppu->draw_pixel(ppu->scanline, ppu->cycle-1, ppu->palette[color]);
I understand that using callback will reduce the memory access performance due to the unnecessary parameter passing, but comparing the performance gain of eliminating refreshing unchanged tiles, it is worth it.
For PC or other architecture which have rich computing power, the overhead of this callback negligible , but it is vital to enable the dirty-matrix technique which saves a lot of computing power on embedded processors.
I have seen an unnecessary pointer validation in the ppu_update code:
if (ppu->video_frame_data) {
ppu->video_frame_data[ppu->scanline*256+ppu->cycle-1]=ppu->palette[color];
}
This kind of validation should be done in the initialisation phase rather than in such a HOTSPOT area. Especially the ppu->video_frame_data is less likely to be changed dynamically (on-fly).
Please consider to remove it.
Hi
I haven't heard from you for quite a long time.
Have you tried the NES simulator on STM32F746G-Discovery Board?
Cheers,
Gabriel
Hi
I have done some improvements to jeg, but I need permission to create a new branch.
Please help.
Cheers,
Gabriel
It looks like we don’t support Four-screen mirroring uses an additional 2 KB of RAM in the cartridge itself to allow logical name tables to each map to separate physical name tables. Do we have plan?
Hi,
I have encountered an issue which I cannot understand. If the sprite buffer is enabled, the Road Fighter will render incorrect sprites. The sprite buffer works well with Super Mario Bro and City Tank
Please help.
Cheers,
Gabriel
I propose to use an unified configuration style:
typedef struct {
} xxxxx_cfg_t;
bool xxxx_init( xxxx_t *ptObj, xxxx_cfg_t *ptCFG)
{
}
The benefits are:
Currently, the example code calls update_frame on each round:
nes_set_controller(&this.tNESConsole, this.chController[0], this.chController[0]);
nes_iterate_frame(&this.tNESConsole);
update_frame(&this.tFrame);
Actually, we concern that this could lead to some unnecessary frame updates before a complete frame is actually ready. Could we add a method to sync up between ppu_update and update_frame?
My proposal is either adding a call-back or a dedicated flag which can be checked by update_frame.
Hi,
About the sprite test failure, is there any update?
Since my next ppu improvement is mainly based on adding screen buffer for sprite rendering, I hesitate to move forward...
Cheers,
Gabriel
src
for the main source
cpu
ppu
apu
(not part of version 0.1)platforms
for different platforms
windows
linux
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.