Giter VIP home page Giter VIP logo

nes_ebook's Introduction

Hey there! I've a got a bit of time-off recently and wrote a mini ebook on how to create NES emulator in Rust.

Check it out:

nes_ebook's People

Contributors

andreram avatar bugzmanov avatar fsmiamoto avatar fuzzylitchi avatar jhutchins avatar kevinalbs avatar ndouglas avatar nmarley avatar sburris0 avatar tatupesonen avatar wngtk avatar yangruihan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

nes_ebook's Issues

Chapter 3.4 conversion to i8 for Branch

In cpu.rs

fn branch(&mut self, condition: bool) {
        if condition {
            let jump: i8 = self.mem_read(self.program_counter) as i8;
            let jump_addr = self
                .program_counter
                .wrapping_add(1)
                .wrapping_add(jump as u16);

If I change jump to u16 I get an extremely incorrect result immediately resulting in a BRK command run. The negative flag isn't set and it is immediately changed back to u16 later in the wrapping add. Any idea about this? Is there something in the documentation I am missing where this should a signed and then unsigned add?

test case can not pass in /code/ch3.3/cpu.rs

seems like we need comment 3 line to pass the test.

pub fn reset(&mut self){
       //self.register_a = 0;
       //self.register_x = 0;
       self.register_y = 0;
       self.stack_pointer = STACK_RESET;
       self.status = CpuFlags::from_bits_truncate(0b100100);
       //self.memory = [0; 0xFFFF];

       self.program_counter = self.mem_read_u16(0xFFFC);
    }

Bug in get_operand_address -> Indirect_X ?

Hi, first of all thank you for this tutorial! I've really been struggling trying to wrap my head around writing an NES emulator, and this has been a great resource.

I'm thinking this might be a bug or just me misunderstanding something about rust, but this line looks like it is doing a wrapping_add of the u8 pointer, which is not how the emulator from https://skilldrick.github.io/easy6502/ is working. Given this code:

LDX #$00
LDA #$05
STA $ff
LDA #$07
STA $0100
LDA #$08
STA $00
LDY #$0d
STY $0705
LDY #$0b
STY $0805
LDA ($ff,X)

(you can plug it in here)

If the u8 pointer were wrapped, I'd expect it to load A from $0805, but it is loading from $0705, since we end up with A having a value of $0b.

I think this code should instead be:

let lo = self.mem_read(ptr as u16);
let hi = self.mem_read((ptr as u16).wrapping_add(1));

Typo in chapter 6.4?

Isn't upper and lower flipped in the render function?

pub fn render(ppu: &NesPPU, frame: &mut Frame) {
   let bank = ppu.ctrl.bknd_pattern_addr();

   for i in 0..0x03c0 { // just for now, lets use the first nametable
       let tile = ppu.vram[i] as u16;
       let tile_x = i % 32;
       let tile_y = i / 32;
       let tile = &ppu.chr_rom[(bank + tile * 16) as usize..=(bank + tile * 16 + 15) as usize];

       for y in 0..=7 {
           let mut upper = tile[y];           //<--- Shouldn't this be lower?
           let mut lower = tile[y + 8];     //<---  Shouldn't this be upper?

I did the same in my emulator, and had wrong colors because bits from upper and lower end up in the wrong positions.

Hint for using wrapping_add() ?

At the end of 3.1, maybe you could give a hint to use wrapping_add(). Because rust doesn't allow overflow in debug mode:

fn inx(&mut self) {                                                         
    self.rx = self.rx.wrapping_add(1) as u8;                                
}

instead of:

fn inx(&mut self) {                                                         
    self.rx += 1;                                
}

to solve:

#[test]
fn test_inx_overflow() {
    let mut cpu = CPU::new();
    cpu.register_x = 0xff;
    cpu.interpret(vec![0xe8, 0xe8, 0x00]);

    assert_eq!(cpu.register_x, 1)
}

Test case 2 problem. Reset function overwrites certain data.

in the function reset() before register_a successfully transfer the data to register_x, the reset function overwrites the register_a with 0 causing the register_x to be 0 also instead of the intended data.

Hello thanks for your work by the way I have learned a lot in learning rust language because of this project, I really appreciate your work :).

SDL2 link error and fix. (Os win10)

To compile SDL2, seems like we need more configutration on windows. for me , I use windows 10,
at command line I will type
image
it means I need download msvc version of sdl2 at here http://www.libsdl.org/release/SDL2-2.0.14-win32-x64.zip
unzip the file

copy ./SDL2-2.014/lib/x64/SDL2.lib .. SDL2main.lib .. SDL2test.lib to
C:\Users\Your_user_name\ .rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\x86_64-pc-windows-msvc\lib\

copy ./SDL2-2.014/lib/x64/SDL2.dll to your project folder next to your cargo.toml.

as ref pls read https://github.com/Rust-SDL2/rust-sdl2 readme

Wrong status flags in LDA example?

Hi, I was just reading through your NES EMU book, but I was confused pretty quickly.

In Chapter 3.1, you start by implementing LDA as the very first instruction.
At the end of the instruction implementation, you adjust the status flags:

if result == 0 {
    self.status = self.status | 0b0000_0001;
} else {
    self.status = self.status & 0b1111_1110;
}

This seems to be modifying the Carry flag. However, the 6502 instruction reference you linked clearly indicates that it is the Zero and Negative flag that should be updated.

. flag effect
C Carry Flag Not affected
Z Zero Flag Set if A = 0
I Interrupt Disable Not affected
D Decimal Mode Flag Not affected
B Break Command Not affected
V Overflow Flag Not affected
N Negative Flag Set if bit 7 of A is set

Considering that the status register layout is

NVxx DIZC

should the code not be as follows?

if result == 0 {
    self.status = self.status | 0b0000_0010;
} else {
    self.status = self.status & 0b1111_1101;
}

Ppu address mirroring before accessing nametables

Hello, your tutorial is amazing first of all. I was just wondering why we still mirror before accessing nametables as in here:
pub fn mirror_vram_addr(&self, addr: u16) -> u16 { let mirrored_vram = addr & 0b10111111111111; // mirror down 0x3000-0x3eff to 0x2000 - 0x2eff

when we never reach that range as in:

0x2000..=0x2fff => { let result = self.internal_data_buf; self.internal_data_buf = self.vram[self.mirror_vram_addr(addr) as usize]; result } 0x3000..=0x3eff => unimplemented!("addr {} shouldn't be used in reallity", addr),

or is this an error where we need to change the range from 0x2fff to 0x3eff and get rid of the second match
? Thank you

about 3.2 Memory addressing modes

Why use wrapping_ add() method?
For example:
pos is 200,self.register_x is 111
The result of adding them is 311,but using wrapping_add(),the result of the add method is 000000010011011, i.e. 55
The addressing range of NES is 0-65535, so why? Isn't that an error?
thx

Chapter 3.4 doesn't mention that you should change the load function

I just got out of a 2 hour debugging session. After days of implementing processor instructions, I just wanted to see something run, but to my dismay, the SDL window opened and quickly closed, without showing anything. To discard any possible problems in my OS or libraries or whatever, I ran the version in this repo, and it worked.

After scratching my head for a while, I realized that the load function quietly changed to load the contents of the program to another memory location. As you can see, in the previous chapter it's different.

Please, state this clearly in the book. This program expects its code to be in a different memory location.

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.